diff --git a/source/blender/draw/engines/eevee_next/eevee_material.hh b/source/blender/draw/engines/eevee_next/eevee_material.hh index 3e8ad8675d5..ed4ded64cf9 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.hh +++ b/source/blender/draw/engines/eevee_next/eevee_material.hh @@ -134,6 +134,9 @@ static inline eClosureBits shader_closure_bits_from_flag(const GPUMaterial *gpum if (GPU_material_flag_get(gpumat, GPU_MATFLAG_GLOSSY)) { closure_bits |= CLOSURE_REFLECTION; } + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_COAT)) { + closure_bits |= CLOSURE_CLEARCOAT; + } if (GPU_material_flag_get(gpumat, GPU_MATFLAG_SUBSURFACE)) { closure_bits |= CLOSURE_SSS; } diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index 966d281f1b0..709e3aa9991 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -200,7 +200,8 @@ struct DeferredLayerBase { /* SSS require an additional layer compared to diffuse. */ count += count_bits_i(closure_bits_ & CLOSURE_SSS); /* Reflection and refraction can have at most two layers. */ - count += 2 * count_bits_i(closure_bits_ & (CLOSURE_REFRACTION | CLOSURE_REFLECTION)); + count += 2 * count_bits_i(closure_bits_ & + (CLOSURE_REFRACTION | CLOSURE_REFLECTION | CLOSURE_CLEARCOAT)); return count; } @@ -210,8 +211,9 @@ struct DeferredLayerBase { /* TODO(fclem): We could count the number of different tangent frame in the shader and use * min(tangent_frame_count, closure_count) once we have the normal reuse optimization. * For now, allocate a split normal layer for each Closure. */ - int count = count_bits_i(closure_bits_ & (CLOSURE_REFRACTION | CLOSURE_REFLECTION | - CLOSURE_DIFFUSE | CLOSURE_TRANSLUCENT)); + int count = count_bits_i(closure_bits_ & + (CLOSURE_REFRACTION | CLOSURE_REFLECTION | CLOSURE_CLEARCOAT | + CLOSURE_DIFFUSE | CLOSURE_TRANSLUCENT)); /* Count the additional infos layer needed by some closures. */ count += count_bits_i(closure_bits_ & (CLOSURE_SSS | CLOSURE_TRANSLUCENT)); return count; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 317f2bd87be..b89e83e463b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -18,6 +18,7 @@ #include "eevee_shadow.hh" #include "BLI_assert.h" +#include "BLI_math_bits.h" namespace blender::eevee { @@ -389,35 +390,78 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu info.additional_info("eevee_cryptomatte_out"); } - int lit_closure_count = 0; + int32_t closure_data_slots = 0; + int32_t closure_extra_eval = 0; if (GPU_material_flag_get(gpumat, GPU_MATFLAG_DIFFUSE)) { info.define("MAT_DIFFUSE"); - lit_closure_count++; - } - if (GPU_material_flag_get(gpumat, GPU_MATFLAG_GLOSSY)) { - info.define("MAT_REFLECTION"); - lit_closure_count++; - } - if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSLUCENT)) { - info.define("MAT_TRANSLUCENT"); - lit_closure_count++; + closure_data_slots |= (1 << 0); } if (GPU_material_flag_get(gpumat, GPU_MATFLAG_SUBSURFACE)) { info.define("MAT_SUBSURFACE"); - lit_closure_count++; + closure_data_slots |= (1 << 0); } if (GPU_material_flag_get(gpumat, GPU_MATFLAG_REFRACT)) { info.define("MAT_REFRACTION"); - /* TODO(fclem): Support refracted lights. */ + closure_data_slots |= (1 << 0); + } + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSLUCENT)) { + info.define("MAT_TRANSLUCENT"); + closure_data_slots |= (1 << 1); + } + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_GLOSSY)) { + info.define("MAT_REFLECTION"); + closure_data_slots |= (1 << 2); + } + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_COAT)) { + info.define("MAT_CLEARCOAT"); + closure_data_slots |= (1 << 3); } if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSLUCENT | GPU_MATFLAG_SUBSURFACE)) { info.define("SHADOW_SUBSURFACE"); } + int32_t CLOSURE_BIN_COUNT = count_bits_i(closure_data_slots); + switch (CLOSURE_BIN_COUNT) { + /* These need to be separated since the strings need to be static. */ + case 0: + case 1: + info.define("CLOSURE_BIN_COUNT", "1"); + break; + case 2: + info.define("CLOSURE_BIN_COUNT", "2"); + break; + case 3: + info.define("CLOSURE_BIN_COUNT", "3"); + break; + default: + BLI_assert_unreachable(); + break; + } + + if (pipeline_type == MAT_PIPE_DEFERRED) { + switch (CLOSURE_BIN_COUNT) { + /* These need to be separated since the strings need to be static. */ + case 0: + case 1: + info.define("GBUFFER_LAYER_MAX", "1"); + break; + case 2: + info.define("GBUFFER_LAYER_MAX", "2"); + break; + case 3: + info.define("GBUFFER_LAYER_MAX", "3"); + break; + default: + BLI_assert_unreachable(); + break; + } + } + if ((pipeline_type == MAT_PIPE_FORWARD) || GPU_material_flag_get(gpumat, GPU_MATFLAG_SHADER_TO_RGBA)) { + int32_t lit_closure_count = CLOSURE_BIN_COUNT + closure_extra_eval; switch (lit_closure_count) { case 0: /* Define nothing. This will in turn define SKIP_LIGHT_EVAL. */ @@ -432,9 +476,6 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu case 3: info.define("LIGHT_CLOSURE_EVAL_COUNT", "3"); break; - case 4: - info.define("LIGHT_CLOSURE_EVAL_COUNT", "4"); - break; default: BLI_assert_unreachable(); break; @@ -575,9 +616,9 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu frag_gen << "}\n\n"; } - frag_gen << "Closure nodetree_surface()\n"; + frag_gen << "Closure nodetree_surface(float closure_rand)\n"; frag_gen << "{\n"; - frag_gen << " closure_weights_reset();\n"; + frag_gen << " closure_weights_reset(closure_rand);\n"; frag_gen << ((!codegen.surface.empty()) ? codegen.surface : "return Closure(0);\n"); frag_gen << "}\n\n"; @@ -595,7 +636,7 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu comp_gen << "Closure nodetree_volume()\n"; comp_gen << "{\n"; - comp_gen << " closure_weights_reset();\n"; + comp_gen << " closure_weights_reset(0.0);\n"; comp_gen << ((!codegen.volume.empty()) ? codegen.volume : "return Closure(0);\n"); comp_gen << "}\n\n"; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 6267a7453d4..4f3404579e3 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -1276,6 +1276,7 @@ enum eClosureBits : uint32_t { CLOSURE_VOLUME = (1u << 11u), CLOSURE_AMBIENT_OCCLUSION = (1u << 12u), CLOSURE_SHADER_TO_RGBA = (1u << 13u), + CLOSURE_CLEARCOAT = (1u << 14u), }; enum GBufferMode : uint32_t { diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_combine_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_combine_frag.glsl index 57cff98670b..a4699b9c4fc 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_combine_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_combine_frag.glsl @@ -55,11 +55,15 @@ void main() out_combined = vec4(0.0, 0.0, 0.0, 0.0); for (int i = 0; i < GBUFFER_LAYER_MAX && i < gbuf.closure_count; i++) { - vec3 closure_light = load_radiance_direct(texel, i); ClosureUndetermined cl = gbuffer_closure_get(gbuf, i); + if (cl.type == CLOSURE_NONE_ID) { + continue; + } + int layer_index = gbuffer_closure_get_bin_index(gbuf, i); + vec3 closure_light = load_radiance_direct(texel, layer_index); if (!use_combined_lightprobe_eval) { - vec3 closure_indirect = load_radiance_indirect(texel, i); + vec3 closure_indirect = load_radiance_indirect(texel, layer_index); if (cl.type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID) { /* TODO(fclem): Add instead of replacing when we support correct refracted light. */ closure_light = closure_indirect; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl index 20272a3d7fc..01865edee67 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl @@ -15,35 +15,6 @@ #pragma BLENDER_REQUIRE(eevee_subsurface_lib.glsl) #pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl) -ClosureLight closure_light_new(ClosureUndetermined cl, vec3 V) -{ - ClosureLight cl_light; - cl_light.N = cl.N; - cl_light.ltc_mat = LTC_LAMBERT_MAT; - cl_light.type = LIGHT_DIFFUSE; - cl_light.light_shadowed = vec3(0.0); - switch (cl.type) { - case CLOSURE_BSDF_TRANSLUCENT_ID: - cl_light.N = -cl.N; - break; - case CLOSURE_BSSRDF_BURLEY_ID: - case CLOSURE_BSDF_DIFFUSE_ID: - break; - case CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID: - cl_light.ltc_mat = LTC_GGX_MAT(dot(cl.N, V), cl.data.x); - cl_light.type = LIGHT_SPECULAR; - break; - case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID: - cl_light.N = -cl.N; - cl_light.type = LIGHT_SPECULAR; - break; - case CLOSURE_NONE_ID: - /* TODO(fclem): Assert. */ - break; - } - return cl_light; -} - void main() { ivec2 texel = ivec2(gl_FragCoord.xy); @@ -110,39 +81,20 @@ void main() for (int i = 0; i < LIGHT_CLOSURE_EVAL_COUNT && i < gbuf.closure_count; i++) { ClosureUndetermined cl = gbuffer_closure_get(gbuf, i); - switch (cl.type) { - case CLOSURE_BSDF_TRANSLUCENT_ID: - /* TODO: Support in ray tracing first. Otherwise we have a discrepancy. */ - stack.cl[i].light_shadowed += lightprobe_eval(samp, to_closure_translucent(cl), P, V); - break; - case CLOSURE_BSSRDF_BURLEY_ID: - /* TODO: Support translucency in ray tracing first. Otherwise we have a discrepancy. */ - case CLOSURE_BSDF_DIFFUSE_ID: - stack.cl[i].light_shadowed += lightprobe_eval(samp, to_closure_diffuse(cl), P, V); - break; - case CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID: - stack.cl[i].light_shadowed += lightprobe_eval(samp, to_closure_reflection(cl), P, V); - break; - case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID: - /* TODO(fclem): Add instead of replacing when we support correct refracted light. */ - stack.cl[i].light_shadowed = lightprobe_eval(samp, to_closure_refraction(cl), P, V); - break; - case CLOSURE_NONE_ID: - /* TODO(fclem): Assert. */ - break; - } + lightprobe_eval(samp, cl, g_data.P, V, stack.cl[i].light_shadowed); } } for (int i = 0; i < LIGHT_CLOSURE_EVAL_COUNT && i < gbuf.closure_count; i++) { + int layer_index = gbuffer_closure_get_bin_index(gbuf, i); /* TODO(fclem): Layered texture. */ - if (i == 0) { + if (layer_index == 0) { imageStore(direct_radiance_1_img, texel, vec4(stack.cl[i].light_shadowed, 1.0)); } - else if (i == 1) { + else if (layer_index == 1) { imageStore(direct_radiance_2_img, texel, vec4(stack.cl[i].light_shadowed, 1.0)); } - else if (i == 2) { + else if (layer_index == 2) { imageStore(direct_radiance_3_img, texel, vec4(stack.cl[i].light_shadowed, 1.0)); } } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_forward_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_forward_lib.glsl index 71f34554e13..3de8852752a 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_forward_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_forward_lib.glsl @@ -13,111 +13,34 @@ #pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl) #pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl) +#if CLOSURE_BIN_COUNT != LIGHT_CLOSURE_EVAL_COUNT +# error Closure data count and eval count must match +#endif + void forward_lighting_eval(float thickness, out vec3 radiance, out vec3 transmittance) { float vPz = dot(drw_view_forward(), g_data.P) - dot(drw_view_forward(), drw_view_position()); vec3 V = drw_world_incident_vector(g_data.P); ClosureLightStack stack; - - ClosureLight cl_diffuse; - cl_diffuse.N = g_diffuse_data.N; - cl_diffuse.ltc_mat = LTC_LAMBERT_MAT; - cl_diffuse.type = LIGHT_DIFFUSE; - - ClosureLight cl_subsurface; - cl_subsurface.N = -g_diffuse_data.N; - cl_subsurface.ltc_mat = LTC_LAMBERT_MAT; - cl_subsurface.type = LIGHT_DIFFUSE; - - ClosureLight cl_translucent; - cl_translucent.N = -g_translucent_data.N; - cl_translucent.ltc_mat = LTC_LAMBERT_MAT; - cl_translucent.type = LIGHT_DIFFUSE; - - ClosureLight cl_reflection; - cl_reflection.N = g_reflection_data.N; - cl_reflection.ltc_mat = LTC_GGX_MAT(dot(g_reflection_data.N, V), g_reflection_data.data.x); - cl_reflection.type = LIGHT_SPECULAR; - - int cl_layer = 0; - -#ifdef MAT_DIFFUSE - const int cl_diffuse_id = cl_layer++; - stack.cl[cl_diffuse_id] = cl_diffuse; -#endif - -#ifdef MAT_SUBSURFACE - const int cl_subsurface_id = cl_layer++; - stack.cl[cl_subsurface_id] = cl_subsurface; -#endif - -#ifdef MAT_TRANSLUCENT - const int cl_translucent_id = cl_layer++; - stack.cl[cl_translucent_id] = cl_translucent; -#endif - -#ifdef MAT_REFLECTION - const int cl_reflection_id = cl_layer++; - stack.cl[cl_reflection_id] = cl_reflection; -#endif + for (int i = 0; i < LIGHT_CLOSURE_EVAL_COUNT; i++) { + stack.cl[i] = closure_light_new(g_closure_get(i), V); + } #ifndef SKIP_LIGHT_EVAL light_eval(stack, g_data.P, g_data.Ng, V, vPz, thickness); #endif -#ifdef MAT_SUBSURFACE - vec3 sss_profile = subsurface_transmission(to_closure_subsurface(g_diffuse_data).sss_radius, - thickness); - stack.cl[cl_subsurface_id].light_shadowed *= sss_profile; - stack.cl[cl_subsurface_id].light_unshadowed *= sss_profile; - /* Fuse back the SSS transmittance with the diffuse lighting. */ - stack.cl[cl_diffuse_id].light_shadowed += stack.cl[cl_subsurface_id].light_shadowed; - stack.cl[cl_diffuse_id].light_unshadowed += stack.cl[cl_subsurface_id].light_unshadowed; -#endif - - vec3 diffuse_light = vec3(0.0); - vec3 translucent_light = vec3(0.0); - vec3 reflection_light = vec3(0.0); - vec3 refraction_light = vec3(0.0); - LightProbeSample samp = lightprobe_load(g_data.P, g_data.Ng, V); -#ifdef MAT_DIFFUSE - diffuse_light = stack.cl[cl_diffuse_id].light_shadowed; - diffuse_light += lightprobe_eval(samp, to_closure_diffuse(g_diffuse_data), g_data.P, V); -#endif -#ifdef MAT_TRANSLUCENT - translucent_light = stack.cl[cl_translucent_id].light_shadowed; - translucent_light += lightprobe_eval( - samp, to_closure_translucent(g_translucent_data), g_data.P, V); -#endif -#ifdef MAT_REFLECTION - reflection_light = stack.cl[cl_reflection_id].light_shadowed; - reflection_light += lightprobe_eval(samp, to_closure_reflection(g_reflection_data), g_data.P, V); -#endif -#ifdef MAT_REFRACTION - /* TODO(fclem): Refraction from light. */ - refraction_light += lightprobe_eval(samp, to_closure_refraction(g_refraction_data), g_data.P, V); -#endif - - /* Apply weight. */ - g_diffuse_data.color *= g_diffuse_data.weight; - g_translucent_data.color *= g_translucent_data.weight; - g_reflection_data.color *= g_reflection_data.weight; - g_refraction_data.color *= g_refraction_data.weight; - /* Mask invalid lighting from undefined closure. */ - diffuse_light = (g_diffuse_data.weight > 1e-5) ? diffuse_light : vec3(0.0); - translucent_light = (g_translucent_data.weight > 1e-5) ? translucent_light : vec3(0.0); - reflection_light = (g_reflection_data.weight > 1e-5) ? reflection_light : vec3(0.0); - refraction_light = (g_refraction_data.weight > 1e-5) ? refraction_light : vec3(0.0); - /* Combine all radiance. */ radiance = g_emission; - radiance += g_diffuse_data.color * diffuse_light; - radiance += g_reflection_data.color * reflection_light; - radiance += g_refraction_data.color * refraction_light; - radiance += g_translucent_data.color * translucent_light; - + for (int i = 0; i < LIGHT_CLOSURE_EVAL_COUNT; i++) { + ClosureUndetermined cl = g_closure_get(i); + lightprobe_eval(samp, cl, g_data.P, V, stack.cl[i].light_shadowed); + if (cl.weight > 1e-5) { + radiance += stack.cl[i].light_shadowed * cl.color * cl.weight; + } + } transmittance = g_transmittance; } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_closure_test.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_closure_test.glsl index 7ef8bd1f582..bafcc1122aa 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_closure_test.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_closure_test.glsl @@ -15,10 +15,9 @@ GBufferData gbuffer_new() { GBufferData data; - data.diffuse.weight = 0.0; - data.translucent.weight = 0.0; - data.reflection.weight = 0.0; - data.refraction.weight = 0.0; + data.closure[0].weight = 0.0; + data.closure[1].weight = 0.0; + data.closure[2].weight = 0.0; data.thickness = 0.2; data.object_id = 0xF220u; data.surface_N = normalize(vec3(0.1, 0.2, 0.3)); @@ -36,119 +35,120 @@ void main() TEST(eevee_gbuffer, ClosureDiffuse) { data_in = gbuffer_new(); - data_in.diffuse.type = CLOSURE_BSDF_DIFFUSE_ID; - data_in.diffuse.weight = 1.0; - data_in.diffuse.color = vec3(0.1, 0.2, 0.3); - data_in.diffuse.N = normalize(vec3(0.2, 0.1, 0.3)); + data_in.closure[0].type = CLOSURE_BSDF_DIFFUSE_ID; + data_in.closure[0].weight = 1.0; + data_in.closure[0].color = vec3(0.1, 0.2, 0.3); + data_in.closure[0].N = normalize(vec3(0.2, 0.1, 0.3)); g_data_packed = gbuffer_pack(data_in); data_out = gbuffer_read(header_tx, closure_tx, normal_tx, ivec2(0)); - EXPECT_EQ(g_data_packed.layer_data, 1); + EXPECT_EQ(g_data_packed.data_len, 1); EXPECT_EQ(data_out.closure_count, 1); ClosureUndetermined out_diffuse = gbuffer_closure_get(data_out, 0); EXPECT_EQ(out_diffuse.type, CLOSURE_BSDF_DIFFUSE_ID); - EXPECT_EQ(data_in.diffuse.type, CLOSURE_BSDF_DIFFUSE_ID); - EXPECT_NEAR(data_in.diffuse.color, out_diffuse.color, 1e-5); - EXPECT_NEAR(data_in.diffuse.N, out_diffuse.N, 1e-5); + EXPECT_EQ(data_in.closure[0].type, CLOSURE_BSDF_DIFFUSE_ID); + EXPECT_NEAR(data_in.closure[0].color, out_diffuse.color, 1e-5); + EXPECT_NEAR(data_in.closure[0].N, out_diffuse.N, 1e-5); } TEST(eevee_gbuffer, ClosureSubsurface) { data_in = gbuffer_new(); - data_in.diffuse.type = CLOSURE_BSSRDF_BURLEY_ID; - data_in.diffuse.weight = 1.0; - data_in.diffuse.color = vec3(0.1, 0.2, 0.3); - data_in.diffuse.data.rgb = vec3(0.2, 0.3, 0.4); - data_in.diffuse.N = normalize(vec3(0.2, 0.1, 0.3)); + data_in.closure[0].type = CLOSURE_BSSRDF_BURLEY_ID; + data_in.closure[0].weight = 1.0; + data_in.closure[0].color = vec3(0.1, 0.2, 0.3); + data_in.closure[0].data.rgb = vec3(0.2, 0.3, 0.4); + data_in.closure[0].N = normalize(vec3(0.2, 0.1, 0.3)); g_data_packed = gbuffer_pack(data_in); data_out = gbuffer_read(header_tx, closure_tx, normal_tx, ivec2(0)); - EXPECT_EQ(g_data_packed.layer_data, 2); + EXPECT_EQ(g_data_packed.data_len, 2); EXPECT_EQ(data_out.closure_count, 1); ClosureUndetermined out_sss_burley = gbuffer_closure_get(data_out, 0); EXPECT_EQ(out_sss_burley.type, CLOSURE_BSSRDF_BURLEY_ID); - EXPECT_EQ(data_in.diffuse.type, CLOSURE_BSSRDF_BURLEY_ID); - EXPECT_NEAR(data_in.diffuse.color, out_sss_burley.color, 1e-5); - EXPECT_NEAR(data_in.diffuse.N, out_sss_burley.N, 1e-5); - EXPECT_NEAR(data_in.diffuse.data.rgb, to_closure_subsurface(out_sss_burley).sss_radius, 1e-5); + EXPECT_EQ(data_in.closure[0].type, CLOSURE_BSSRDF_BURLEY_ID); + EXPECT_NEAR(data_in.closure[0].color, out_sss_burley.color, 1e-5); + EXPECT_NEAR(data_in.closure[0].N, out_sss_burley.N, 1e-5); + EXPECT_NEAR( + data_in.closure[0].data.rgb, to_closure_subsurface(out_sss_burley).sss_radius, 1e-5); } TEST(eevee_gbuffer, ClosureTranslucent) { data_in = gbuffer_new(); - data_in.translucent.type = CLOSURE_BSDF_TRANSLUCENT_ID; - data_in.translucent.weight = 1.0; - data_in.translucent.color = vec3(0.1, 0.2, 0.3); - data_in.translucent.N = normalize(vec3(0.2, 0.1, 0.3)); + data_in.closure[0].type = CLOSURE_BSDF_TRANSLUCENT_ID; + data_in.closure[0].weight = 1.0; + data_in.closure[0].color = vec3(0.1, 0.2, 0.3); + data_in.closure[0].N = normalize(vec3(0.2, 0.1, 0.3)); g_data_packed = gbuffer_pack(data_in); data_out = gbuffer_read(header_tx, closure_tx, normal_tx, ivec2(0)); - EXPECT_EQ(g_data_packed.layer_data, 1); + EXPECT_EQ(g_data_packed.data_len, 1); EXPECT_EQ(data_out.closure_count, 1); ClosureUndetermined out_translucent = gbuffer_closure_get(data_out, 0); EXPECT_EQ(out_translucent.type, CLOSURE_BSDF_TRANSLUCENT_ID); - EXPECT_EQ(data_in.translucent.type, CLOSURE_BSDF_TRANSLUCENT_ID); - EXPECT_NEAR(data_in.translucent.color, out_translucent.color, 1e-5); - EXPECT_NEAR(data_in.translucent.N, out_translucent.N, 1e-5); + EXPECT_EQ(data_in.closure[0].type, CLOSURE_BSDF_TRANSLUCENT_ID); + EXPECT_NEAR(data_in.closure[0].color, out_translucent.color, 1e-5); + EXPECT_NEAR(data_in.closure[0].N, out_translucent.N, 1e-5); } TEST(eevee_gbuffer, ClosureReflection) { data_in = gbuffer_new(); - data_in.reflection.type = CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID; - data_in.reflection.weight = 1.0; - data_in.reflection.color = vec3(0.1, 0.2, 0.3); - data_in.reflection.data.x = 0.4; - data_in.reflection.N = normalize(vec3(0.2, 0.1, 0.3)); + data_in.closure[0].type = CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID; + data_in.closure[0].weight = 1.0; + data_in.closure[0].color = vec3(0.1, 0.2, 0.3); + data_in.closure[0].data.x = 0.4; + data_in.closure[0].N = normalize(vec3(0.2, 0.1, 0.3)); g_data_packed = gbuffer_pack(data_in); data_out = gbuffer_read(header_tx, closure_tx, normal_tx, ivec2(0)); - EXPECT_EQ(g_data_packed.layer_data, 2); + EXPECT_EQ(g_data_packed.data_len, 2); EXPECT_EQ(data_out.closure_count, 1); ClosureUndetermined out_reflection = gbuffer_closure_get(data_out, 0); EXPECT_EQ(out_reflection.type, CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID); - EXPECT_EQ(data_in.reflection.type, CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID); - EXPECT_NEAR(data_in.reflection.color, out_reflection.color, 1e-5); - EXPECT_NEAR(data_in.reflection.N, out_reflection.N, 1e-5); - EXPECT_NEAR(data_in.reflection.data.r, out_reflection.data.r, 1e-5); + EXPECT_EQ(data_in.closure[0].type, CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID); + EXPECT_NEAR(data_in.closure[0].color, out_reflection.color, 1e-5); + EXPECT_NEAR(data_in.closure[0].N, out_reflection.N, 1e-5); + EXPECT_NEAR(data_in.closure[0].data.r, out_reflection.data.r, 1e-5); } TEST(eevee_gbuffer, ClosureRefraction) { data_in = gbuffer_new(); - data_in.refraction.type = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; - data_in.refraction.weight = 1.0; - data_in.refraction.color = vec3(0.1, 0.2, 0.3); - data_in.refraction.data.x = 0.4; - data_in.refraction.data.y = 0.5; - data_in.refraction.N = normalize(vec3(0.2, 0.1, 0.3)); + data_in.closure[0].type = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; + data_in.closure[0].weight = 1.0; + data_in.closure[0].color = vec3(0.1, 0.2, 0.3); + data_in.closure[0].data.x = 0.4; + data_in.closure[0].data.y = 0.5; + data_in.closure[0].N = normalize(vec3(0.2, 0.1, 0.3)); g_data_packed = gbuffer_pack(data_in); data_out = gbuffer_read(header_tx, closure_tx, normal_tx, ivec2(0)); - EXPECT_EQ(g_data_packed.layer_data, 2); + EXPECT_EQ(g_data_packed.data_len, 2); EXPECT_EQ(data_out.closure_count, 1); ClosureUndetermined out_refraction = gbuffer_closure_get(data_out, 0); EXPECT_EQ(out_refraction.type, CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID); - EXPECT_EQ(data_in.refraction.type, CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID); - EXPECT_NEAR(data_in.refraction.color, out_refraction.color, 1e-5); - EXPECT_NEAR(data_in.refraction.N, out_refraction.N, 1e-5); - EXPECT_NEAR(data_in.refraction.data.r, out_refraction.data.r, 1e-5); - EXPECT_NEAR(data_in.refraction.data.g, out_refraction.data.g, 1e-5); + EXPECT_EQ(data_in.closure[0].type, CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID); + EXPECT_NEAR(data_in.closure[0].color, out_refraction.color, 1e-5); + EXPECT_NEAR(data_in.closure[0].N, out_refraction.N, 1e-5); + EXPECT_NEAR(data_in.closure[0].data.r, out_refraction.data.r, 1e-5); + EXPECT_NEAR(data_in.closure[0].data.g, out_refraction.data.g, 1e-5); } TEST(eevee_gbuffer, ClosureCombination) @@ -167,12 +167,12 @@ void main() in_cl1.N = normalize(vec3(0.2, 0.3, 0.4)); data_in = gbuffer_new(); - data_in.refraction = in_cl0; - data_in.reflection = in_cl1; + data_in.closure[0] = in_cl0; + data_in.closure[1] = in_cl1; g_data_packed = gbuffer_pack(data_in); - EXPECT_EQ(g_data_packed.layer_data, 4); + EXPECT_EQ(g_data_packed.data_len, 4); data_out = gbuffer_read(header_tx, closure_tx, normal_tx, ivec2(0)); @@ -198,22 +198,22 @@ void main() TEST(eevee_gbuffer, ClosureColorless) { data_in = gbuffer_new(); - data_in.refraction.type = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; - data_in.refraction.weight = 1.0; - data_in.refraction.color = vec3(0.1, 0.1, 0.1); - data_in.refraction.data.x = 0.4; - data_in.refraction.data.y = 0.5; - data_in.refraction.N = normalize(vec3(0.2, 0.1, 0.3)); + data_in.closure[0].type = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; + data_in.closure[0].weight = 1.0; + data_in.closure[0].color = vec3(0.1, 0.1, 0.1); + data_in.closure[0].data.x = 0.4; + data_in.closure[0].data.y = 0.5; + data_in.closure[0].N = normalize(vec3(0.2, 0.1, 0.3)); - data_in.reflection.type = CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID; - data_in.reflection.weight = 1.0; - data_in.reflection.color = vec3(0.1, 0.1, 0.1); - data_in.reflection.data.x = 0.4; - data_in.reflection.N = normalize(vec3(0.2, 0.3, 0.4)); + data_in.closure[1].type = CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID; + data_in.closure[1].weight = 1.0; + data_in.closure[1].color = vec3(0.1, 0.1, 0.1); + data_in.closure[1].data.x = 0.4; + data_in.closure[1].N = normalize(vec3(0.2, 0.3, 0.4)); g_data_packed = gbuffer_pack(data_in); - EXPECT_EQ(g_data_packed.layer_data, 2); + EXPECT_EQ(g_data_packed.data_len, 2); data_out = gbuffer_read(header_tx, closure_tx, normal_tx, ivec2(0)); @@ -223,16 +223,16 @@ void main() ClosureUndetermined out_refraction = gbuffer_closure_get(data_out, 0); EXPECT_EQ(out_refraction.type, CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID); - EXPECT_EQ(data_in.refraction.type, CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID); - EXPECT_NEAR(data_in.refraction.color, out_refraction.color, 1e-5); - EXPECT_NEAR(data_in.refraction.N, out_refraction.N, 1e-5); - EXPECT_NEAR(data_in.refraction.data.r, out_refraction.data.r, 1e-5); - EXPECT_NEAR(data_in.refraction.data.g, out_refraction.data.g, 1e-5); + EXPECT_EQ(data_in.closure[0].type, CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID); + EXPECT_NEAR(data_in.closure[0].color, out_refraction.color, 1e-5); + EXPECT_NEAR(data_in.closure[0].N, out_refraction.N, 1e-5); + EXPECT_NEAR(data_in.closure[0].data.r, out_refraction.data.r, 1e-5); + EXPECT_NEAR(data_in.closure[0].data.g, out_refraction.data.g, 1e-5); EXPECT_EQ(out_reflection.type, CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID); - EXPECT_EQ(data_in.reflection.type, CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID); - EXPECT_NEAR(data_in.reflection.color, out_reflection.color, 1e-5); - EXPECT_NEAR(data_in.reflection.N, out_reflection.N, 1e-5); - EXPECT_NEAR(data_in.reflection.data.r, out_reflection.data.r, 1e-5); + EXPECT_EQ(data_in.closure[1].type, CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID); + EXPECT_NEAR(data_in.closure[1].color, out_reflection.color, 1e-5); + EXPECT_NEAR(data_in.closure[1].N, out_reflection.N, 1e-5); + EXPECT_NEAR(data_in.closure[1].data.r, out_reflection.data.r, 1e-5); } } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl index 1b403956574..83d8a3caea6 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl @@ -6,6 +6,16 @@ * G-buffer: Packing and unpacking of G-buffer data. * * See #GBuffer for a breakdown of the G-buffer layout. + * + * There is two way of indexing closure data from the GBuffer: + * - per "bin": same closure indices as during the material evaluation pass. + * Can have none-closures. + * - per "layer": gbuffer internal storage order. Tightly packed, will only have none-closures at + * the end of the array. + * + * Indexing per bin is better to avoid parameter discontinuity for a given closure + * (i.e: for denoising), whereas indexing per layer is better for iterating through the closure + * without dealing with none-closures. */ #pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl) @@ -17,17 +27,18 @@ * * \{ */ -#define GBUFFER_LAYER_MAX 3 -#define GBUFFER_NORMAL_MAX GBUFFER_LAYER_MAX +/* Note: Only specialized for the gbuffer pass. */ +#ifndef GBUFFER_LAYER_MAX +# define GBUFFER_LAYER_MAX 3 +#endif +#define GBUFFER_NORMAL_MAX (GBUFFER_LAYER_MAX + /* Additional data */ 1) #define GBUFFER_DATA_MAX (GBUFFER_LAYER_MAX * 2) +#define GBUFFER_HEADER_BITS_PER_LAYER 4 /* Note: Reserve the last 4 bits for the normal layers ids. */ #define GBUFFER_NORMAL_BITS_SHIFT 12 struct GBufferData { - ClosureUndetermined diffuse; - ClosureUndetermined translucent; - ClosureUndetermined reflection; - ClosureUndetermined refraction; + ClosureUndetermined closure[GBUFFER_LAYER_MAX]; /* Additional object information if any closure needs it. */ float thickness; uint object_id; @@ -37,18 +48,19 @@ struct GBufferData { /* Result of Packing the GBuffer. */ struct GBufferWriter { - /* TODO(fclem): Better packing. */ + /* Packed GBuffer data in layer indexing. */ vec4 data[GBUFFER_DATA_MAX]; + /* Packed normal data. Redundant normals are omited. */ vec2 N[GBUFFER_NORMAL_MAX]; - + /* Header containing which closures are encoded and which normals are used. */ uint header; /** Only used for book-keeping. Not actually written. Can be derived from header. */ - /* Number of layers written in the header. */ - int layer_gbuf; + /* Number of bins written in the header. Counts empty bins. */ + int bins_len; /* Number of data written in the data array. */ - int layer_data; + int data_len; /* Number of normal written in the normal array. */ - int layer_normal; + int normal_len; }; /* Result of loading the GBuffer. */ @@ -64,12 +76,33 @@ struct GBufferReader { /* Number of valid closure encoded in the gbuffer. */ int closure_count; /* Only used for book-keeping when reading. */ - int layer_data; - int layer_normal; + int data_len; + /* Only used for debugging and testing. */ + int normal_len; /* Texel of the gbuffer being read. */ ivec2 texel; }; +ClosureType gbuffer_mode_to_closure_type(uint mode) +{ + switch (mode) { + case GBUF_DIFFUSE: + return ClosureType(CLOSURE_BSDF_DIFFUSE_ID); + case GBUF_TRANSLUCENT: + return ClosureType(CLOSURE_BSDF_TRANSLUCENT_ID); + case GBUF_SUBSURFACE: + return ClosureType(CLOSURE_BSSRDF_BURLEY_ID); + case GBUF_REFLECTION_COLORLESS: + case GBUF_REFLECTION: + return ClosureType(CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID); + case GBUF_REFRACTION_COLORLESS: + case GBUF_REFRACTION: + return ClosureType(CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID); + default: + return ClosureType(CLOSURE_NONE_ID); + } +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -269,20 +302,20 @@ bool gbuffer_is_refraction(vec4 gbuffer) return gbuffer.w < 1.0; } -uint gbuffer_header_pack(GBufferMode mode, uint layer) +uint gbuffer_header_pack(GBufferMode mode, uint bin) { - return (mode << (4u * layer)); + return (mode << (4u * bin)); } -GBufferMode gbuffer_header_unpack(uint data, uint layer) +GBufferMode gbuffer_header_unpack(uint data, uint bin) { - return GBufferMode((data >> (4u * layer)) & 15u); + return GBufferMode((data >> (4u * bin)) & 15u); } void gbuffer_append_closure(inout GBufferWriter gbuf, GBufferMode closure_type) { - gbuf.header |= gbuffer_header_pack(closure_type, gbuf.layer_gbuf); - gbuf.layer_gbuf++; + gbuf.header |= gbuffer_header_pack(closure_type, gbuf.bins_len); + gbuf.bins_len++; } void gbuffer_register_closure(inout GBufferReader gbuf, ClosureUndetermined cl, int slot) { @@ -304,6 +337,10 @@ void gbuffer_register_closure(inout GBufferReader gbuf, ClosureUndetermined cl, #endif } } +void gbuffer_skip_closure(inout GBufferReader gbuf) +{ + gbuf.closure_count++; +} ClosureUndetermined gbuffer_closure_get(GBufferReader gbuf, int i) { @@ -327,7 +364,7 @@ ClosureUndetermined gbuffer_closure_get(GBufferReader gbuf, int i) void gbuffer_append_data(inout GBufferWriter gbuf, vec4 data) { - switch (gbuf.layer_data) { + switch (gbuf.data_len) { #if GBUFFER_DATA_MAX > 0 case 0: gbuf.data[0] = data; @@ -359,14 +396,18 @@ void gbuffer_append_data(inout GBufferWriter gbuf, vec4 data) break; #endif } - gbuf.layer_data++; + gbuf.data_len++; } vec4 gbuffer_pop_first_data(inout GBufferReader gbuf, samplerGBufferClosure closure_tx) { - vec4 data = fetchGBuffer(closure_tx, gbuf.texel, gbuf.layer_data); - gbuf.layer_data++; + vec4 data = fetchGBuffer(closure_tx, gbuf.texel, gbuf.data_len); + gbuf.data_len++; return data; } +void gbuffer_skip_data(inout GBufferReader gbuf) +{ + gbuf.data_len++; +} /** * Set the dedicated normal bit for the last added closure. @@ -395,30 +436,30 @@ int gbuffer_header_normal_layer_id_get(uint header, int layer_id) void gbuffer_append_normal(inout GBufferWriter gbuf, vec3 normal) { vec2 packed_N = gbuffer_normal_pack(normal); - int layer_id = gbuf.layer_gbuf - 1; + int layer_id = gbuf.bins_len - 1; /* Try to reuse previous normals. */ #if GBUFFER_NORMAL_MAX > 1 - if (gbuf.layer_normal > 0 && all(equal(gbuf.N[0], packed_N))) { + if (gbuf.normal_len > 0 && all(equal(gbuf.N[0], packed_N))) { gbuffer_header_normal_layer_id_set(gbuf.header, layer_id, 0u); return; } #endif #if GBUFFER_NORMAL_MAX > 2 - if (gbuf.layer_normal > 1 && all(equal(gbuf.N[1], packed_N))) { + if (gbuf.normal_len > 1 && all(equal(gbuf.N[1], packed_N))) { gbuffer_header_normal_layer_id_set(gbuf.header, layer_id, 1u); return; } #endif #if GBUFFER_NORMAL_MAX > 3 - if (gbuf.layer_normal > 2 && all(equal(gbuf.N[2], packed_N))) { + if (gbuf.normal_len > 2 && all(equal(gbuf.N[2], packed_N))) { gbuffer_header_normal_layer_id_set(gbuf.header, layer_id, 2u); return; } #endif /* Could not reuse. Add another normal. */ - gbuffer_header_normal_layer_id_set(gbuf.header, layer_id, uint(gbuf.layer_normal)); + gbuffer_header_normal_layer_id_set(gbuf.header, layer_id, uint(gbuf.normal_len)); - switch (gbuf.layer_normal) { + switch (gbuf.normal_len) { #if GBUFFER_NORMAL_MAX > 0 case 0: gbuf.N[0] = packed_N; @@ -435,27 +476,31 @@ void gbuffer_append_normal(inout GBufferWriter gbuf, vec3 normal) break; #endif } - gbuf.layer_normal++; + gbuf.normal_len++; } vec3 gbuffer_normal_get(inout GBufferReader gbuf, int layer_id, samplerGBufferNormal normal_tx) { int normal_layer_id = gbuffer_header_normal_layer_id_get(gbuf.header, layer_id); vec2 normal_packed = fetchGBuffer(normal_tx, gbuf.texel, normal_layer_id).rg; - gbuf.layer_normal = max(gbuf.layer_normal, normal_layer_id + 1); + gbuf.normal_len = max(gbuf.normal_len, normal_layer_id + 1); return gbuffer_normal_unpack(normal_packed); } +void gbuffer_skip_normal(inout GBufferReader gbuf) +{ + /* Nothing to do. Normals are indexed. */ +} /* Pack geometry additional infos onto the normal stack. Needs to be run last. */ void gbuffer_additional_info_pack(inout GBufferWriter gbuf, float thickness, uint object_id) { - gbuf.N[gbuf.layer_normal] = vec2(gbuffer_thickness_pack(thickness), - gbuffer_object_id_unorm16_pack(object_id)); - gbuf.layer_normal++; + gbuf.N[gbuf.normal_len] = vec2(gbuffer_thickness_pack(thickness), + gbuffer_object_id_unorm16_pack(object_id)); + gbuf.normal_len++; } void gbuffer_additional_info_load(inout GBufferReader gbuf, samplerGBufferNormal normal_tx) { - vec2 data_packed = fetchGBuffer(normal_tx, gbuf.texel, gbuf.layer_normal).rg; - gbuf.layer_normal++; + vec2 data_packed = fetchGBuffer(normal_tx, gbuf.texel, gbuf.normal_len).rg; + gbuf.normal_len++; gbuf.thickness = gbuffer_thickness_unpack(data_packed.x); gbuf.object_id = gbuffer_object_id_unorm16_unpack(data_packed.y); } @@ -481,7 +526,12 @@ void gbuffer_closure_diffuse_pack(inout GBufferWriter gbuf, ClosureUndetermined gbuffer_append_data(gbuf, gbuffer_closure_color_pack(cl.color)); gbuffer_append_normal(gbuf, cl.N); } - +void gbuffer_closure_diffuse_skip(inout GBufferReader gbuf) +{ + gbuffer_skip_closure(gbuf); + gbuffer_skip_data(gbuf); + gbuffer_skip_normal(gbuf); +} void gbuffer_closure_diffuse_load(inout GBufferReader gbuf, int layer, samplerGBufferClosure closure_tx, @@ -502,7 +552,12 @@ void gbuffer_closure_translucent_pack(inout GBufferWriter gbuf, ClosureUndetermi gbuffer_append_data(gbuf, gbuffer_closure_color_pack(cl.color)); gbuffer_append_normal(gbuf, cl.N); } - +void gbuffer_closure_translucent_skip(inout GBufferReader gbuf) +{ + gbuffer_skip_closure(gbuf); + gbuffer_skip_data(gbuf); + gbuffer_skip_normal(gbuf); +} void gbuffer_closure_translucent_load(inout GBufferReader gbuf, int layer, samplerGBufferClosure closure_tx, @@ -524,7 +579,13 @@ void gbuffer_closure_subsurface_pack(inout GBufferWriter gbuf, ClosureUndetermin gbuffer_append_data(gbuf, gbuffer_sss_radii_pack(cl.data.xyz)); gbuffer_append_normal(gbuf, cl.N); } - +void gbuffer_closure_subsurface_skip(inout GBufferReader gbuf) +{ + gbuffer_skip_closure(gbuf); + gbuffer_skip_data(gbuf); + gbuffer_skip_data(gbuf); + gbuffer_skip_normal(gbuf); +} void gbuffer_closure_subsurface_load(inout GBufferReader gbuf, int layer, samplerGBufferClosure closure_tx, @@ -548,7 +609,13 @@ void gbuffer_closure_reflection_pack(inout GBufferWriter gbuf, ClosureUndetermin gbuffer_append_data(gbuf, vec4(cl.data.x, 0.0, 0.0, 0.0)); gbuffer_append_normal(gbuf, cl.N); } - +void gbuffer_closure_reflection_skip(inout GBufferReader gbuf) +{ + gbuffer_skip_closure(gbuf); + gbuffer_skip_data(gbuf); + gbuffer_skip_data(gbuf); + gbuffer_skip_normal(gbuf); +} void gbuffer_closure_reflection_load(inout GBufferReader gbuf, int layer, samplerGBufferClosure closure_tx, @@ -572,7 +639,13 @@ void gbuffer_closure_refraction_pack(inout GBufferWriter gbuf, ClosureUndetermin gbuffer_append_data(gbuf, vec4(cl.data.x, gbuffer_ior_pack(cl.data.y), 0.0, 0.0)); gbuffer_append_normal(gbuf, cl.N); } - +void gbuffer_closure_refraction_skip(inout GBufferReader gbuf) +{ + gbuffer_skip_closure(gbuf); + gbuffer_skip_data(gbuf); + gbuffer_skip_data(gbuf); + gbuffer_skip_normal(gbuf); +} void gbuffer_closure_refraction_load(inout GBufferReader gbuf, int layer, samplerGBufferClosure closure_tx, @@ -605,7 +678,12 @@ void gbuffer_closure_reflection_colorless_pack(inout GBufferWriter gbuf, Closure gbuffer_append_data(gbuf, vec4(cl.data.x, 0.0, intensity_packed)); gbuffer_append_normal(gbuf, cl.N); } - +void gbuffer_closure_reflection_colorless_skip(inout GBufferReader gbuf) +{ + gbuffer_skip_closure(gbuf); + gbuffer_skip_data(gbuf); + gbuffer_skip_normal(gbuf); +} void gbuffer_closure_reflection_colorless_load(inout GBufferReader gbuf, int layer, samplerGBufferClosure closure_tx, @@ -629,7 +707,12 @@ void gbuffer_closure_refraction_colorless_pack(inout GBufferWriter gbuf, Closure gbuffer_append_data(gbuf, vec4(cl.data.x, gbuffer_ior_pack(cl.data.y), intensity_packed)); gbuffer_append_normal(gbuf, cl.N); } - +void gbuffer_closure_refraction_colorless_skip(inout GBufferReader gbuf) +{ + gbuffer_skip_closure(gbuf); + gbuffer_skip_data(gbuf); + gbuffer_skip_normal(gbuf); +} void gbuffer_closure_refraction_colorless_load(inout GBufferReader gbuf, int layer, samplerGBufferClosure closure_tx, @@ -655,6 +738,10 @@ void gbuffer_closure_refraction_colorless_load(inout GBufferReader gbuf, * Special cases where we can save some space by packing multiple closures data together. * \{ */ +/* Still unused. Have to finalize support. + * Might be difficult to make it work with #gbuffer_read_bin(). */ +#if 0 + void gbuffer_closure_metal_clear_coat_pack(inout GBufferWriter gbuf, ClosureUndetermined cl_bottom, ClosureUndetermined cl_coat) @@ -662,7 +749,7 @@ void gbuffer_closure_metal_clear_coat_pack(inout GBufferWriter gbuf, vec2 intensity_packed = gbuffer_closure_intensity_pack(cl_coat.color.r); gbuffer_append_closure(gbuf, GBUF_METAL_CLEARCOAT); gbuffer_append_data(gbuf, gbuffer_closure_color_pack(cl_bottom.color)); - gbuffer_append_data(gbuf, vec4(cl_bottom.data.x, cl_coat.data.y, intensity_packed)); + gbuffer_append_data(gbuf, vec4(cl_bottom.data.x, cl_coat.data.x, intensity_packed)); gbuffer_append_normal(gbuf, cl_bottom.N); } @@ -687,6 +774,8 @@ void gbuffer_closure_metal_clear_coat_load(inout GBufferReader gbuf, gbuffer_register_closure(gbuf, coat, 1); } +#endif + /** \} */ /* -------------------------------------------------------------------- */ @@ -698,32 +787,18 @@ GBufferWriter gbuffer_pack(GBufferData data_in) { GBufferWriter gbuf; gbuf.header = 0u; - gbuf.layer_gbuf = 0; - gbuf.layer_data = 0; - gbuf.layer_normal = 0; + gbuf.bins_len = 0; + gbuf.data_len = 0; + gbuf.normal_len = 0; /* Check special configurations first. */ bool has_additional_data = false; - for (int i = 0; i < 4; i++) { - ClosureUndetermined cl; - /* TODO(fclem): Rename inside GBufferData. */ - switch (i) { - case 0: - cl = data_in.diffuse; - break; - case 1: - cl = data_in.refraction; - break; - case 2: - cl = data_in.reflection; - break; - case 3: - cl = data_in.translucent; - break; - } + for (int i = 0; i < GBUFFER_LAYER_MAX; i++) { + ClosureUndetermined cl = data_in.closure[i]; if (cl.weight <= 1e-5) { + gbuf.bins_len++; continue; } @@ -759,7 +834,7 @@ GBufferWriter gbuffer_pack(GBufferData data_in) } } - if (gbuf.layer_normal == 0) { + if (gbuf.normal_len == 0) { gbuffer_closure_unlit_pack(gbuf, data_in.surface_N); } @@ -770,11 +845,12 @@ GBufferWriter gbuffer_pack(GBufferData data_in) return gbuf; } -/* Return the number of closure as encoded in the give header value. */ +/* Return the number of closure as encoded in the given header value. */ int gbuffer_closure_count(uint header) { /* Note: Need to be adjusted for different global GBUFFER_LAYER_MAX. */ - uvec3 closure_types = (uvec3(header) >> uvec3(0u, 4u, 8u)) & ((1u << 4) - 1); + const int bits_per_layer = GBUFFER_HEADER_BITS_PER_LAYER; + uvec3 closure_types = (uvec3(header) >> uvec3(0u, 4u, 8u)) & ((1u << bits_per_layer) - 1); if (closure_types.x == GBUF_METAL_CLEARCOAT) { return 2; @@ -782,39 +858,66 @@ int gbuffer_closure_count(uint header) return reduce_add(ivec3(not(equal(closure_types, uvec3(0u))))); } +/* Return the type of a closure using its bin index. */ +ClosureType gbuffer_closure_type_get_by_bin(uint header, int bin_index) +{ + /* TODO(fclem): Doesn't take GBUF_METAL_CLEARCOAT into account or other mode that could merge two + * bins into one layer. */ + const int bits_per_layer = GBUFFER_HEADER_BITS_PER_LAYER; + uint mode = (header >> (bin_index * bits_per_layer)) & ((1u << bits_per_layer) - 1); + return gbuffer_mode_to_closure_type(mode); +} + +/* Only read closure types out of the header. The rest of GBufferReader is undefined. */ GBufferReader gbuffer_read_header_closure_types(uint header) { GBufferReader gbuf; - for (int layer = 0; layer < GBUFFER_LAYER_MAX; layer++) { - GBufferMode mode = gbuffer_header_unpack(header, layer); - ClosureType closure_type = CLOSURE_NONE_ID; - switch (mode) { - case GBUF_DIFFUSE: - closure_type = CLOSURE_BSDF_DIFFUSE_ID; - break; - case GBUF_TRANSLUCENT: - closure_type = CLOSURE_BSDF_TRANSLUCENT_ID; - break; - case GBUF_SUBSURFACE: - closure_type = CLOSURE_BSSRDF_BURLEY_ID; - break; - case GBUF_REFLECTION_COLORLESS: - case GBUF_REFLECTION: - closure_type = CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID; - break; - case GBUF_REFRACTION_COLORLESS: - case GBUF_REFRACTION: - closure_type = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; - break; - default: - break; - } - gbuffer_register_closure(gbuf, closure_new(closure_type), layer); + for (int bin = 0; bin < GBUFFER_LAYER_MAX; bin++) { + GBufferMode mode = gbuffer_header_unpack(header, bin); + ClosureType closure_type = gbuffer_mode_to_closure_type(mode); + gbuffer_register_closure(gbuf, closure_new(closure_type), bin); } return gbuf; } +/* Return the bin index of a closure using its layer index. */ +int gbuffer_closure_get_bin_index(GBufferReader gbuf, int layer_index) +{ + int layer = 0; + for (int bin = 0; bin < GBUFFER_LAYER_MAX; bin++) { + GBufferMode mode = gbuffer_header_unpack(gbuf.header, bin); + /* Gbuffer header can have holes. Skip GBUF_NONE. */ + if (mode != GBUF_NONE) { + if (layer == layer_index) { + return bin; + } + layer++; + } + } + /* Should never happen. But avoid out of bound access. */ + return 0; +} + +ClosureUndetermined gbuffer_closure_get_by_bin(GBufferReader gbuf, int bin_index) +{ + int layer_index = 0; + for (int bin = 0; bin < GBUFFER_LAYER_MAX; bin++) { + GBufferMode mode = gbuffer_header_unpack(gbuf.header, bin); + if (bin == bin_index) { + return gbuffer_closure_get(gbuf, layer_index); + } + else { + if (mode != GBUF_NONE) { + layer_index++; + } + } + } + /* Should never happen. */ + return closure_new(CLOSURE_NONE_ID); +} + +/* Read the entirety of the GBuffer. */ GBufferReader gbuffer_read(samplerGBufferHeader header_tx, samplerGBufferClosure closure_tx, samplerGBufferNormal normal_tx, @@ -825,15 +928,16 @@ GBufferReader gbuffer_read(samplerGBufferHeader header_tx, gbuf.thickness = 0.0; gbuf.closure_count = 0; gbuf.object_id = 0u; - gbuf.layer_data = 0; - gbuf.layer_normal = 0; + gbuf.data_len = 0; + gbuf.normal_len = 0; gbuf.surface_N = vec3(0.0); + for (int bin = 0; bin < GBUFFER_LAYER_MAX; bin++) { + gbuffer_register_closure(gbuf, closure_new(CLOSURE_NONE_ID), bin); + } gbuf.header = fetchGBuffer(header_tx, texel); + if (gbuf.header == 0u) { - for (int layer = 0; layer < GBUFFER_LAYER_MAX; layer++) { - gbuffer_register_closure(gbuf, closure_new(CLOSURE_NONE_ID), layer); - } return gbuf; } @@ -841,42 +945,41 @@ GBufferReader gbuffer_read(samplerGBufferHeader header_tx, gbuf.surface_N = gbuffer_normal_unpack(fetchGBuffer(normal_tx, texel, 0).xy); bool has_additional_data = false; - for (int layer = 0; layer < GBUFFER_LAYER_MAX; layer++) { - GBufferMode mode = gbuffer_header_unpack(gbuf.header, layer); + for (int bin = 0; bin < GBUFFER_LAYER_MAX; bin++) { + GBufferMode mode = gbuffer_header_unpack(gbuf.header, bin); switch (mode) { default: case GBUF_NONE: - gbuffer_register_closure(gbuf, closure_new(CLOSURE_NONE_ID), layer); break; case GBUF_DIFFUSE: - gbuffer_closure_diffuse_load(gbuf, layer, closure_tx, normal_tx); + gbuffer_closure_diffuse_load(gbuf, gbuf.closure_count, closure_tx, normal_tx); gbuf.closure_count++; break; case GBUF_TRANSLUCENT: - gbuffer_closure_translucent_load(gbuf, layer, closure_tx, normal_tx); + gbuffer_closure_translucent_load(gbuf, gbuf.closure_count, closure_tx, normal_tx); gbuf.closure_count++; has_additional_data = true; break; case GBUF_SUBSURFACE: - gbuffer_closure_subsurface_load(gbuf, layer, closure_tx, normal_tx); + gbuffer_closure_subsurface_load(gbuf, gbuf.closure_count, closure_tx, normal_tx); gbuf.closure_count++; has_additional_data = true; break; case GBUF_REFLECTION: - gbuffer_closure_reflection_load(gbuf, layer, closure_tx, normal_tx); + gbuffer_closure_reflection_load(gbuf, gbuf.closure_count, closure_tx, normal_tx); gbuf.closure_count++; break; case GBUF_REFRACTION: - gbuffer_closure_refraction_load(gbuf, layer, closure_tx, normal_tx); + gbuffer_closure_refraction_load(gbuf, gbuf.closure_count, closure_tx, normal_tx); gbuf.closure_count++; has_additional_data = true; break; case GBUF_REFLECTION_COLORLESS: - gbuffer_closure_reflection_colorless_load(gbuf, layer, closure_tx, normal_tx); + gbuffer_closure_reflection_colorless_load(gbuf, gbuf.closure_count, closure_tx, normal_tx); gbuf.closure_count++; break; case GBUF_REFRACTION_COLORLESS: - gbuffer_closure_refraction_colorless_load(gbuf, layer, closure_tx, normal_tx); + gbuffer_closure_refraction_colorless_load(gbuf, gbuf.closure_count, closure_tx, normal_tx); gbuf.closure_count++; has_additional_data = true; break; @@ -890,4 +993,90 @@ GBufferReader gbuffer_read(samplerGBufferHeader header_tx, return gbuf; } +/* Read only one bin from the GBuffer. */ +ClosureUndetermined gbuffer_read_bin(samplerGBufferHeader header_tx, + samplerGBufferClosure closure_tx, + samplerGBufferNormal normal_tx, + ivec2 texel, + int bin_index) +{ + GBufferReader gbuf; + gbuf.texel = texel; + gbuf.closure_count = 0; + gbuf.data_len = 0; + gbuf.normal_len = 0; + gbuf.header = fetchGBuffer(header_tx, texel); + + if (gbuf.header == 0u) { + return closure_new(CLOSURE_NONE_ID); + } + + GBufferMode mode; + for (int bin = 0; bin < GBUFFER_LAYER_MAX; bin++) { + mode = gbuffer_header_unpack(gbuf.header, bin); + + if (mode != GBUF_NONE && bin >= bin_index) { + break; + } + + switch (mode) { + default: + case GBUF_NONE: + break; + case GBUF_DIFFUSE: + gbuffer_closure_diffuse_skip(gbuf); + break; + case GBUF_TRANSLUCENT: + gbuffer_closure_translucent_skip(gbuf); + break; + case GBUF_SUBSURFACE: + gbuffer_closure_subsurface_skip(gbuf); + break; + case GBUF_REFLECTION: + gbuffer_closure_reflection_skip(gbuf); + break; + case GBUF_REFRACTION: + gbuffer_closure_refraction_skip(gbuf); + break; + case GBUF_REFLECTION_COLORLESS: + gbuffer_closure_reflection_colorless_skip(gbuf); + break; + case GBUF_REFRACTION_COLORLESS: + gbuffer_closure_refraction_colorless_skip(gbuf); + break; + } + } + + bool has_additional_data = false; + switch (mode) { + default: + case GBUF_NONE: + gbuffer_register_closure(gbuf, closure_new(CLOSURE_NONE_ID), gbuf.closure_count); + break; + case GBUF_DIFFUSE: + gbuffer_closure_diffuse_load(gbuf, gbuf.closure_count, closure_tx, normal_tx); + break; + case GBUF_TRANSLUCENT: + gbuffer_closure_translucent_load(gbuf, gbuf.closure_count, closure_tx, normal_tx); + break; + case GBUF_SUBSURFACE: + gbuffer_closure_subsurface_load(gbuf, gbuf.closure_count, closure_tx, normal_tx); + break; + case GBUF_REFLECTION: + gbuffer_closure_reflection_load(gbuf, gbuf.closure_count, closure_tx, normal_tx); + break; + case GBUF_REFRACTION: + gbuffer_closure_refraction_load(gbuf, gbuf.closure_count, closure_tx, normal_tx); + break; + case GBUF_REFLECTION_COLORLESS: + gbuffer_closure_reflection_colorless_load(gbuf, gbuf.closure_count, closure_tx, normal_tx); + break; + case GBUF_REFRACTION_COLORLESS: + gbuffer_closure_refraction_colorless_load(gbuf, gbuf.closure_count, closure_tx, normal_tx); + break; + } + + return gbuffer_closure_get(gbuf, gbuf.closure_count); +} + /** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_normal_test.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_normal_test.glsl index ea6c740eb09..65e9282af51 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_normal_test.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_normal_test.glsl @@ -15,10 +15,9 @@ GBufferData gbuffer_new() { GBufferData data; - data.diffuse.weight = 0.0; - data.translucent.weight = 0.0; - data.reflection.weight = 0.0; - data.refraction.weight = 0.0; + data.closure[0].weight = 0.0; + data.closure[1].weight = 0.0; + data.closure[2].weight = 0.0; data.thickness = 0.2; data.object_id = 0xF220u; data.surface_N = normalize(vec3(0.1, 0.2, 0.3)); @@ -31,9 +30,9 @@ void main() { GBufferWriter gbuf; gbuf.header = 0u; - gbuf.layer_gbuf = 0; - gbuf.layer_data = 0; - gbuf.layer_normal = 0; + gbuf.bins_len = 0; + gbuf.data_len = 0; + gbuf.normal_len = 0; vec3 N0 = normalize(vec3(0.2, 0.1, 0.3)); vec3 N1 = normalize(vec3(0.1, 0.2, 0.3)); @@ -42,22 +41,22 @@ void main() gbuffer_append_closure(gbuf, GBUF_DIFFUSE); gbuffer_append_normal(gbuf, N0); - EXPECT_EQ(gbuf.layer_gbuf, 1); - EXPECT_EQ(gbuf.layer_normal, 1); + EXPECT_EQ(gbuf.bins_len, 1); + EXPECT_EQ(gbuf.normal_len, 1); EXPECT_EQ(gbuf.N[0], gbuffer_normal_pack(N0)); gbuffer_append_closure(gbuf, GBUF_DIFFUSE); gbuffer_append_normal(gbuf, N1); - EXPECT_EQ(gbuf.layer_gbuf, 2); - EXPECT_EQ(gbuf.layer_normal, 2); + EXPECT_EQ(gbuf.bins_len, 2); + EXPECT_EQ(gbuf.normal_len, 2); EXPECT_EQ(gbuf.N[1], gbuffer_normal_pack(N1)); gbuffer_append_closure(gbuf, GBUF_DIFFUSE); gbuffer_append_normal(gbuf, N2); - EXPECT_EQ(gbuf.layer_gbuf, 3); - EXPECT_EQ(gbuf.layer_normal, 3); + EXPECT_EQ(gbuf.bins_len, 3); + EXPECT_EQ(gbuf.normal_len, 3); EXPECT_EQ(gbuf.N[2], gbuffer_normal_pack(N2)); } @@ -65,30 +64,30 @@ void main() { GBufferWriter gbuf; gbuf.header = 0u; - gbuf.layer_gbuf = 0; - gbuf.layer_data = 0; - gbuf.layer_normal = 0; + gbuf.bins_len = 0; + gbuf.data_len = 0; + gbuf.normal_len = 0; vec3 N0 = normalize(vec3(0.2, 0.1, 0.3)); gbuffer_append_closure(gbuf, GBUF_DIFFUSE); gbuffer_append_normal(gbuf, N0); - EXPECT_EQ(gbuf.layer_gbuf, 1); - EXPECT_EQ(gbuf.layer_normal, 1); + EXPECT_EQ(gbuf.bins_len, 1); + EXPECT_EQ(gbuf.normal_len, 1); EXPECT_EQ(gbuf.N[0], gbuffer_normal_pack(N0)); gbuffer_append_closure(gbuf, GBUF_DIFFUSE); gbuffer_append_normal(gbuf, N0); - EXPECT_EQ(gbuf.layer_gbuf, 2); - EXPECT_EQ(gbuf.layer_normal, 1); + EXPECT_EQ(gbuf.bins_len, 2); + EXPECT_EQ(gbuf.normal_len, 1); gbuffer_append_closure(gbuf, GBUF_DIFFUSE); gbuffer_append_normal(gbuf, N0); - EXPECT_EQ(gbuf.layer_gbuf, 3); - EXPECT_EQ(gbuf.layer_normal, 1); + EXPECT_EQ(gbuf.bins_len, 3); + EXPECT_EQ(gbuf.normal_len, 1); } GBufferData data_in; @@ -115,18 +114,18 @@ void main() TEST(eevee_gbuffer, NormalReuseDoubleFirst) { data_in = gbuffer_new(); - data_in.refraction = cl1; - data_in.reflection = cl1; + data_in.closure[0] = cl1; + data_in.closure[1] = cl1; g_data_packed = gbuffer_pack(data_in); - EXPECT_EQ(g_data_packed.layer_gbuf, 2); - EXPECT_EQ(g_data_packed.layer_normal, 1); + EXPECT_EQ(g_data_packed.data_len, 2); + EXPECT_EQ(g_data_packed.normal_len, 1); data_out = gbuffer_read(header_tx, closure_tx, normal_tx, ivec2(0)); EXPECT_EQ(data_out.closure_count, 2); - EXPECT_EQ(data_out.layer_normal, 1); + EXPECT_EQ(data_out.normal_len, 1); EXPECT_NEAR(cl1.N, gbuffer_closure_get(data_out, 0).N, 1e-5); EXPECT_NEAR(cl1.N, gbuffer_closure_get(data_out, 1).N, 1e-5); } @@ -134,18 +133,18 @@ void main() TEST(eevee_gbuffer, NormalReuseDoubleNone) { data_in = gbuffer_new(); - data_in.refraction = cl1; - data_in.reflection = cl2; + data_in.closure[0] = cl1; + data_in.closure[1] = cl2; g_data_packed = gbuffer_pack(data_in); - EXPECT_EQ(g_data_packed.layer_gbuf, 2); - EXPECT_EQ(g_data_packed.layer_normal, 2); + EXPECT_EQ(g_data_packed.data_len, 2); + EXPECT_EQ(g_data_packed.normal_len, 2); data_out = gbuffer_read(header_tx, closure_tx, normal_tx, ivec2(0)); EXPECT_EQ(data_out.closure_count, 2); - EXPECT_EQ(data_out.layer_normal, 2); + EXPECT_EQ(data_out.normal_len, 2); EXPECT_NEAR(cl1.N, gbuffer_closure_get(data_out, 0).N, 1e-5); EXPECT_NEAR(cl2.N, gbuffer_closure_get(data_out, 1).N, 1e-5); } @@ -153,18 +152,18 @@ void main() TEST(eevee_gbuffer, NormalReuseTripleFirst) { data_in = gbuffer_new(); - data_in.diffuse = cl1; - data_in.refraction = cl2; - data_in.reflection = cl2; + data_in.closure[0] = cl1; + data_in.closure[1] = cl2; + data_in.closure[2] = cl2; g_data_packed = gbuffer_pack(data_in); - EXPECT_EQ(g_data_packed.layer_normal, 2); + EXPECT_EQ(g_data_packed.normal_len, 2); data_out = gbuffer_read(header_tx, closure_tx, normal_tx, ivec2(0)); EXPECT_EQ(data_out.closure_count, 3); - EXPECT_EQ(data_out.layer_normal, 2); + EXPECT_EQ(data_out.normal_len, 2); EXPECT_NEAR(cl1.N, gbuffer_closure_get(data_out, 0).N, 1e-5); EXPECT_NEAR(cl2.N, gbuffer_closure_get(data_out, 1).N, 1e-5); EXPECT_NEAR(cl2.N, gbuffer_closure_get(data_out, 2).N, 1e-5); @@ -173,18 +172,18 @@ void main() TEST(eevee_gbuffer, NormalReuseTripleSecond) { data_in = gbuffer_new(); - data_in.diffuse = cl2; - data_in.refraction = cl1; - data_in.reflection = cl2; + data_in.closure[0] = cl2; + data_in.closure[1] = cl1; + data_in.closure[2] = cl2; g_data_packed = gbuffer_pack(data_in); - EXPECT_EQ(g_data_packed.layer_normal, 2); + EXPECT_EQ(g_data_packed.normal_len, 2); data_out = gbuffer_read(header_tx, closure_tx, normal_tx, ivec2(0)); EXPECT_EQ(data_out.closure_count, 3); - EXPECT_EQ(data_out.layer_normal, 2); + EXPECT_EQ(data_out.normal_len, 2); EXPECT_NEAR(cl2.N, gbuffer_closure_get(data_out, 0).N, 1e-5); EXPECT_NEAR(cl1.N, gbuffer_closure_get(data_out, 1).N, 1e-5); EXPECT_NEAR(cl2.N, gbuffer_closure_get(data_out, 2).N, 1e-5); @@ -193,18 +192,18 @@ void main() TEST(eevee_gbuffer, NormalReuseTripleThird) { data_in = gbuffer_new(); - data_in.diffuse = cl2; - data_in.refraction = cl2; - data_in.reflection = cl1; + data_in.closure[0] = cl2; + data_in.closure[1] = cl2; + data_in.closure[2] = cl1; g_data_packed = gbuffer_pack(data_in); - EXPECT_EQ(g_data_packed.layer_normal, 2); + EXPECT_EQ(g_data_packed.normal_len, 2); data_out = gbuffer_read(header_tx, closure_tx, normal_tx, ivec2(0)); EXPECT_EQ(data_out.closure_count, 3); - EXPECT_EQ(data_out.layer_normal, 2); + EXPECT_EQ(data_out.normal_len, 2); EXPECT_NEAR(cl2.N, gbuffer_closure_get(data_out, 0).N, 1e-5); EXPECT_NEAR(cl2.N, gbuffer_closure_get(data_out, 1).N, 1e-5); EXPECT_NEAR(cl1.N, gbuffer_closure_get(data_out, 2).N, 1e-5); @@ -213,18 +212,18 @@ void main() TEST(eevee_gbuffer, NormalReuseTripleNone) { data_in = gbuffer_new(); - data_in.diffuse = cl1; - data_in.refraction = cl2; - data_in.reflection = cl3; + data_in.closure[0] = cl1; + data_in.closure[1] = cl2; + data_in.closure[2] = cl3; g_data_packed = gbuffer_pack(data_in); - EXPECT_EQ(g_data_packed.layer_normal, 3); + EXPECT_EQ(g_data_packed.normal_len, 3); data_out = gbuffer_read(header_tx, closure_tx, normal_tx, ivec2(0)); EXPECT_EQ(data_out.closure_count, 3); - EXPECT_EQ(data_out.layer_normal, 3); + EXPECT_EQ(data_out.normal_len, 3); EXPECT_NEAR(cl1.N, gbuffer_closure_get(data_out, 0).N, 1e-5); EXPECT_NEAR(cl2.N, gbuffer_closure_get(data_out, 1).N, 1e-5); EXPECT_NEAR(cl3.N, gbuffer_closure_get(data_out, 2).N, 1e-5); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_denoise_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_denoise_comp.glsl index 8acd2e13fa3..80db3b8e832 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_denoise_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_denoise_comp.glsl @@ -81,16 +81,13 @@ void main() return; } - GBufferReader gbuf = gbuffer_read( - gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres); + ClosureUndetermined closure_center = gbuffer_read_bin( + gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres, closure_index); - bool has_valid_closure = closure_index < gbuf.closure_count; - if (!has_valid_closure) { + if (closure_center.type == CLOSURE_NONE_ID) { return; } - ClosureUndetermined closure_center = gbuffer_closure_get(gbuf, closure_index); - vec3 center_N = closure_center.N; float roughness = closure_apparent_roughness_get(closure_center); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl index d861c2772dc..a4c2dc2a0b7 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_scan_comp.glsl @@ -32,19 +32,16 @@ void main() return; } - GBufferReader gbuf = gbuffer_read( - gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres); + HorizonScanContext ctx; + ctx.closure = gbuffer_read_bin( + gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres, closure_index); + ctx.closure.N = drw_normal_world_to_view(ctx.closure.N); - bool has_valid_closure = closure_index < gbuf.closure_count; - if (!has_valid_closure) { + if (ctx.closure.type == CLOSURE_NONE_ID) { imageStore(horizon_radiance_img, texel, vec4(FLT_11_11_10_MAX, 0.0)); return; } - HorizonScanContext ctx; - ctx.closure = gbuffer_closure_get(gbuf, closure_index); - ctx.closure.N = drw_normal_world_to_view(ctx.closure.N); - vec3 vP = drw_point_screen_to_view(vec3(uv, depth)); vec2 noise = utility_tx_fetch(utility_tx, vec2(texel), UTIL_BLUE_NOISE_LAYER).rg; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl index 6a63c5ab2d5..f9c17068066 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl @@ -104,6 +104,35 @@ struct ClosureLight { vec3 light_unshadowed; }; +ClosureLight closure_light_new(ClosureUndetermined cl, vec3 V) +{ + ClosureLight cl_light; + cl_light.N = cl.N; + cl_light.ltc_mat = LTC_LAMBERT_MAT; + cl_light.type = LIGHT_DIFFUSE; + cl_light.light_shadowed = vec3(0.0); + switch (cl.type) { + case CLOSURE_BSDF_TRANSLUCENT_ID: + cl_light.N = -cl.N; + break; + case CLOSURE_BSSRDF_BURLEY_ID: + case CLOSURE_BSDF_DIFFUSE_ID: + break; + case CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID: + cl_light.ltc_mat = LTC_GGX_MAT(dot(cl.N, V), cl.data.x); + cl_light.type = LIGHT_SPECULAR; + break; + case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID: + cl_light.N = -cl.N; + cl_light.type = LIGHT_SPECULAR; + break; + case CLOSURE_NONE_ID: + /* TODO(fclem): Assert. */ + break; + } + return cl_light; +} + struct ClosureLightStack { /* NOTE: This is wrapped into a struct to avoid array shenanigans on MSL. */ ClosureLight cl[LIGHT_CLOSURE_EVAL_COUNT]; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl index c97c1579e44..324eb5c648a 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl @@ -163,4 +163,30 @@ vec3 lightprobe_eval(LightProbeSample samp, ClosureRefraction cl, vec3 P, vec3 V return mix(radiance_cube, radiance_sh, fac); } +void lightprobe_eval( + LightProbeSample samp, ClosureUndetermined cl, vec3 P, vec3 V, inout vec3 radiance) +{ + switch (cl.type) { + case CLOSURE_BSDF_TRANSLUCENT_ID: + /* TODO: Support in ray tracing first. Otherwise we have a discrepancy. */ + radiance += lightprobe_eval(samp, to_closure_translucent(cl), P, V); + break; + case CLOSURE_BSSRDF_BURLEY_ID: + /* TODO: Support translucency in ray tracing first. Otherwise we have a discrepancy. */ + case CLOSURE_BSDF_DIFFUSE_ID: + radiance += lightprobe_eval(samp, to_closure_diffuse(cl), P, V); + break; + case CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID: + radiance += lightprobe_eval(samp, to_closure_reflection(cl), P, V); + break; + case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID: + /* TODO(fclem): Add instead of replacing when we support correct refracted light. */ + radiance = lightprobe_eval(samp, to_closure_refraction(cl), P, V); + break; + case CLOSURE_NONE_ID: + /* TODO(fclem): Assert. */ + break; + } +} + #endif /* SPHERE_PROBE */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl index bef2fe3bfae..fb806874435 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl @@ -20,16 +20,38 @@ vec3 g_volume_absorption; #define Closure float #define CLOSURE_DEFAULT 0.0 +/* Maximum number of picked closure. */ +#ifndef CLOSURE_BIN_COUNT +# define CLOSURE_BIN_COUNT 1 +#endif /* Sampled closure parameters. */ -ClosureUndetermined g_diffuse_data; -ClosureUndetermined g_translucent_data; -ClosureUndetermined g_reflection_data; -ClosureUndetermined g_refraction_data; +ClosureUndetermined g_closure_bins[CLOSURE_BIN_COUNT]; /* Random number per sampled closure type. */ -float g_diffuse_rand; -float g_translucent_rand; -float g_reflection_rand; -float g_refraction_rand; +float g_closure_rand[CLOSURE_BIN_COUNT]; + +ClosureUndetermined g_closure_get(int i) +{ + switch (i) { + default: + case 0: + return g_closure_bins[0]; +#if CLOSURE_BIN_COUNT > 1 + case 1: + return g_closure_bins[1]; +#endif +#if CLOSURE_BIN_COUNT > 2 + case 2: + return g_closure_bins[2]; +#endif + } +} + +ClosureUndetermined g_closure_get_resolved(int i, float weight_fac) +{ + ClosureUndetermined cl = g_closure_get(i); + cl.color *= cl.weight * weight_fac; + return cl; +} ClosureType closure_type_get(ClosureDiffuse cl) { @@ -77,7 +99,7 @@ bool closure_select_check(float weight, inout float total_weight, inout float r) * Assign `candidate` to `destination` based on a random value and the respective weights. */ void closure_select(inout ClosureUndetermined destination, - float random, + inout float random, ClosureUndetermined candidate) { if (closure_select_check(candidate.weight, destination.weight, random)) { @@ -87,28 +109,23 @@ void closure_select(inout ClosureUndetermined destination, } } -float g_closure_rand; - -void closure_weights_reset() +void closure_weights_reset(float closure_rand) { - g_diffuse_data.weight = 0.0; - g_translucent_data.weight = 0.0; - g_reflection_data.weight = 0.0; - g_refraction_data.weight = 0.0; + g_closure_rand[0] = closure_rand; + g_closure_bins[0].weight = 0.0; +#if CLOSURE_BIN_COUNT > 1 + g_closure_rand[1] = closure_rand; + g_closure_bins[1].weight = 0.0; +#endif +#if CLOSURE_BIN_COUNT > 2 + g_closure_rand[2] = closure_rand; + g_closure_bins[2].weight = 0.0; +#endif g_volume_scattering = vec3(0.0); g_volume_anisotropy = 0.0; g_volume_absorption = vec3(0.0); -#if defined(GPU_FRAGMENT_SHADER) - g_diffuse_rand = g_translucent_rand = g_reflection_rand = g_refraction_rand = g_closure_rand; -#else - g_diffuse_rand = 0.0; - g_translucent_rand = 0.0; - g_reflection_rand = 0.0; - g_refraction_rand = 0.0; -#endif - g_emission = vec3(0.0); g_transmittance = vec3(0.0); g_volume_scattering = vec3(0.0); @@ -127,7 +144,8 @@ Closure closure_eval(ClosureDiffuse diffuse) { ClosureUndetermined cl; closure_base_copy(cl, diffuse); - closure_select(g_diffuse_data, g_diffuse_rand, cl); + /* Diffuse & SSS always use the first closure. */ + closure_select(g_closure_bins[0], g_closure_rand[0], cl); return Closure(0); } @@ -136,7 +154,8 @@ Closure closure_eval(ClosureSubsurface diffuse) ClosureUndetermined cl; closure_base_copy(cl, diffuse); cl.data.rgb = diffuse.sss_radius; - closure_select(g_diffuse_data, g_diffuse_rand, cl); + /* Diffuse & SSS always use the first closure. */ + closure_select(g_closure_bins[0], g_closure_rand[0], cl); return Closure(0); } @@ -144,16 +163,53 @@ Closure closure_eval(ClosureTranslucent translucent) { ClosureUndetermined cl; closure_base_copy(cl, translucent); - closure_select(g_translucent_data, g_translucent_rand, cl); +#if CLOSURE_BIN_COUNT == 1 + /* Only one closure type is present in the whole tree. */ + closure_select(g_closure_bins[0], g_closure_rand[0], cl); +#else + /* Use second slot so we can have diffuse + translucent without noise. */ + closure_select(g_closure_bins[1], g_closure_rand[1], cl); +#endif return Closure(0); } +/* Alternate between two bins on a per closure basis. + * Allow clearcoat layer without noise. + * Choosing the bin with the least weight can choose a + * different bin for the same closure and + * produce issue with raytracing denoiser. + * Alway start with the second bin, this one doesn't + * overlap with other closure. */ +bool g_closure_reflection_bin = true; +#define CHOOSE_MIN_WEIGHT_CLOSURE_BIN(a, b) \ + if (g_closure_reflection_bin) { \ + closure_select(g_closure_bins[b], g_closure_rand[b], cl); \ + } \ + else { \ + closure_select(g_closure_bins[a], g_closure_rand[a], cl); \ + } \ + g_closure_reflection_bin = !g_closure_reflection_bin; + Closure closure_eval(ClosureReflection reflection) { ClosureUndetermined cl; closure_base_copy(cl, reflection); cl.data.r = reflection.roughness; - closure_select(g_reflection_data, g_reflection_rand, cl); + +#if CLOSURE_BIN_COUNT == 1 + /* Only one reflection closure is present in the whole tree. */ + closure_select(g_closure_bins[0], g_closure_rand[0], cl); +#elif CLOSURE_BIN_COUNT == 2 + /* Case with either only one reflection and one other closure + * or only multiple reflection closures. */ + CHOOSE_MIN_WEIGHT_CLOSURE_BIN(0, 1); +#elif CLOSURE_BIN_COUNT == 3 + /* Case with multiple reflection closures and one other closure. */ + CHOOSE_MIN_WEIGHT_CLOSURE_BIN(1, 2); +#endif + +#undef CHOOSE_MIN_WEIGHT_CLOSURE_BIN + return Closure(0); } @@ -163,7 +219,9 @@ Closure closure_eval(ClosureRefraction refraction) closure_base_copy(cl, refraction); cl.data.r = refraction.roughness; cl.data.g = refraction.ior; - closure_select(g_refraction_data, g_refraction_rand, cl); + /* Use same slot as diffuse as mixed diffuse/refraction are not common. + * Allow glass material with clearcoat without noise. */ + closure_select(g_closure_bins[0], g_closure_rand[0], cl); return Closure(0); } @@ -327,7 +385,7 @@ float ambient_occlusion_eval(vec3 normal, #ifndef GPU_METAL void attrib_load(); -Closure nodetree_surface(); +Closure nodetree_surface(float closure_rand); /* Closure nodetree_volume(); */ vec3 nodetree_displacement(); float nodetree_thickness(); @@ -513,7 +571,7 @@ vec2 bsdf_lut(float cos_theta, float roughness, float ior, bool do_multiscatter) #ifdef EEVEE_MATERIAL_STUBS # define attrib_load() # define nodetree_displacement() vec3(0.0) -# define nodetree_surface() Closure(0) +# define nodetree_surface(closure_rand) Closure(0) # define nodetree_volume() Closure(0) # define nodetree_thickness() 0.1 #endif diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_bilateral_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_bilateral_comp.glsl index d5b831904cb..5800af70c82 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_bilateral_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_bilateral_comp.glsl @@ -71,17 +71,14 @@ void main() float center_depth = texelFetch(depth_tx, texel_fullres, 0).r; vec3 center_P = drw_point_screen_to_world(vec3(center_uv, center_depth)); - GBufferReader gbuf = gbuffer_read( - gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres); + ClosureUndetermined center_closure = gbuffer_read_bin( + gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres, closure_index); - bool has_valid_closure = closure_index < gbuf.closure_count; - if (!has_valid_closure) { + if (center_closure.type == CLOSURE_NONE_ID) { /* Output nothing. This shouldn't even be loaded. */ return; } - ClosureUndetermined center_closure = gbuffer_closure_get(gbuf, closure_index); - float roughness = closure_apparent_roughness_get(center_closure); float variance = imageLoad(in_variance_img, texel_fullres).r; vec3 in_radiance = imageLoad(in_radiance_img, texel_fullres).rgb; @@ -137,15 +134,13 @@ void main() continue; } - GBufferReader sample_gbuf = gbuffer_read( - gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, sample_texel); + ClosureUndetermined sample_closure = gbuffer_read_bin( + gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, sample_texel, closure_index); - if (closure_index >= sample_gbuf.closure_count) { + if (sample_closure.type == CLOSURE_NONE_ID) { continue; } - ClosureUndetermined sample_closure = gbuffer_closure_get(sample_gbuf, closure_index); - float depth_weight = bilateral_depth_weight(center_closure.N, center_P, sample_P); float spatial_weight = bilateral_spatial_weight(filter_size, vec2(offset)); float normal_weight = bilateral_normal_weight(center_closure.N, sample_closure.N); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_spatial_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_spatial_comp.glsl index 35880d2e311..549ac4c8873 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_spatial_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_spatial_comp.glsl @@ -109,11 +109,10 @@ void main() return; } - GBufferReader gbuf = gbuffer_read( - gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres); + ClosureUndetermined closure = gbuffer_read_bin( + gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres, closure_index); - bool has_valid_closure = closure_index < gbuf.closure_count; - if (!has_valid_closure) { + if (closure.type == CLOSURE_NONE_ID) { invalid_pixel_write(texel_fullres); return; } @@ -122,8 +121,6 @@ void main() vec3 P = drw_point_screen_to_world(vec3(uv, 0.5)); vec3 V = drw_world_incident_vector(P); - ClosureUndetermined closure = gbuffer_closure_get(gbuf, closure_index); - /* Compute filter size and needed sample count */ float apparent_roughness = closure_apparent_roughness_get(closure); float filter_size_factor = saturate(apparent_roughness * 8.0); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_generate_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_generate_comp.glsl index f7c87236c85..83fdc1ac5ef 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_generate_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_generate_comp.glsl @@ -21,11 +21,10 @@ void main() ivec2 texel_fullres = texel * uniform_buf.raytrace.resolution_scale + uniform_buf.raytrace.resolution_bias; - GBufferReader gbuf = gbuffer_read( - gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres); + ClosureUndetermined closure = gbuffer_read_bin( + gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel_fullres, closure_index); - bool valid_pixel = closure_index < gbuf.closure_count; - if (!valid_pixel) { + if (closure.type == CLOSURE_NONE_ID) { imageStore(out_ray_data_img, texel, vec4(0.0)); return; } @@ -36,7 +35,7 @@ void main() vec2 noise = utility_tx_fetch(utility_tx, vec2(texel), UTIL_BLUE_NOISE_LAYER).rg; noise = fract(noise + sampling_rng_2D_get(SAMPLING_RAYTRACE_U)); - BsdfSample samp = ray_generate_direction(noise.xy, gbuffer_closure_get(gbuf, closure_index), V); + BsdfSample samp = ray_generate_direction(noise.xy, closure, V); /* Store inverse pdf to speedup denoising. * Limit to the smallest non-0 value that the format can encode. diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_tile_classify_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_tile_classify_comp.glsl index f8a9db7cb23..c35b7570dcd 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_tile_classify_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_tile_classify_comp.glsl @@ -42,9 +42,9 @@ void main() GBufferReader gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel); for (int i = 0; i < GBUFFER_LAYER_MAX; i++) { - ClosureUndetermined cl = gbuffer_closure_get(gbuf, i); + ClosureUndetermined cl = gbuffer_closure_get_by_bin(gbuf, i); if (cl.type == CLOSURE_NONE_ID) { - break; + continue; } float roughness = closure_apparent_roughness_get(cl); float ray_roughness_fac = ray_roughness_factor(uniform_buf.raytrace, roughness); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_planar_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_planar_comp.glsl index 4699e8038a8..0255af2f70a 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_planar_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_planar_comp.glsl @@ -37,8 +37,7 @@ void main() uniform_buf.raytrace.resolution_bias; uint gbuf_header = texelFetch(gbuf_header_tx, texel_fullres, 0).r; - GBufferReader gbuf = gbuffer_read_header_closure_types(gbuf_header); - ClosureType closure_type = gbuffer_closure_get(gbuf, closure_index).type; + ClosureType closure_type = gbuffer_closure_type_get_by_bin(gbuf_header, closure_index); if ((closure_type == CLOSURE_BSDF_TRANSLUCENT_ID) || (closure_type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID)) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_screen_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_screen_comp.glsl index af5a4ece603..c9143d7e3ef 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_screen_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_trace_screen_comp.glsl @@ -39,8 +39,7 @@ void main() uniform_buf.raytrace.resolution_bias; uint gbuf_header = texelFetch(gbuf_header_tx, texel_fullres, 0).r; - GBufferReader gbuf = gbuffer_read_header_closure_types(gbuf_header); - uint closure_type = gbuffer_closure_get(gbuf, closure_index).type; + ClosureType closure_type = gbuffer_closure_type_get_by_bin(gbuf_header, closure_index); bool is_reflection = true; if ((closure_type == CLOSURE_BSDF_TRANSLUCENT_ID) || diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl index dc5713bf578..897d7c5bc25 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_capture_frag.glsl @@ -27,15 +27,23 @@ void main() init_globals(); /* TODO(fclem): Remove random sampling for capture and accumulate color. */ - g_closure_rand = 0.5; + float closure_rand = 0.5; - nodetree_surface(); + nodetree_surface(closure_rand); - g_diffuse_data.color *= g_diffuse_data.weight; - g_reflection_data.color *= g_reflection_data.weight; - g_refraction_data.color *= g_refraction_data.weight; + vec3 albedo = vec3(0.0); - vec3 albedo = g_diffuse_data.color + g_reflection_data.color; + for (int i = 0; i < CLOSURE_BIN_COUNT; i++) { + ClosureUndetermined cl = g_closure_get_resolved(i, 1.0); + if (cl.weight <= 1e-5) { + continue; + } + if (cl.type != CLOSURE_BSDF_TRANSLUCENT_ID && + cl.type != CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID) + { + albedo += cl.color; + } + } /* ----- Surfel output ----- */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl index 4c21d0b398a..88ba50d9a29 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl @@ -24,7 +24,9 @@ vec4 closure_to_rgba(Closure cl) out_color.a = saturate(1.0 - average(g_transmittance)); /* Reset for the next closure tree. */ - closure_weights_reset(); + float noise = utility_tx_fetch(utility_tx, gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r; + float closure_rand = fract(noise + sampling_rng_1D_get(SAMPLING_CLOSURE)); + closure_weights_reset(closure_rand); return out_color; } @@ -37,11 +39,11 @@ void main() init_globals(); float noise = utility_tx_fetch(utility_tx, gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r; - g_closure_rand = fract(noise + sampling_rng_1D_get(SAMPLING_CLOSURE)); + float closure_rand = fract(noise + sampling_rng_1D_get(SAMPLING_CLOSURE)); fragment_displacement(); - nodetree_surface(); + nodetree_surface(closure_rand); g_holdout = saturate(g_holdout); @@ -51,15 +53,6 @@ void main() float transparency = 1.0 - average(g_transmittance); float transparency_rcp = safe_rcp(transparency); g_emission *= transparency_rcp; - g_diffuse_data.weight *= transparency_rcp; - g_translucent_data.weight *= transparency_rcp; - g_reflection_data.weight *= transparency_rcp; - g_refraction_data.weight *= transparency_rcp; - - g_diffuse_data.color *= g_diffuse_data.weight; - g_translucent_data.color *= g_translucent_data.weight; - g_reflection_data.color *= g_reflection_data.weight; - g_refraction_data.color *= g_refraction_data.weight; ivec2 out_texel = ivec2(gl_FragCoord.xy); @@ -79,10 +72,13 @@ void main() /* ----- GBuffer output ----- */ GBufferData gbuf_data; - gbuf_data.diffuse = g_diffuse_data; - gbuf_data.translucent = g_translucent_data; - gbuf_data.reflection = g_reflection_data; - gbuf_data.refraction = g_refraction_data; + gbuf_data.closure[0] = g_closure_get_resolved(0, transparency_rcp); +#if CLOSURE_BIN_COUNT > 1 + gbuf_data.closure[1] = g_closure_get_resolved(1, transparency_rcp); +#endif +#if CLOSURE_BIN_COUNT > 2 + gbuf_data.closure[2] = g_closure_get_resolved(2, transparency_rcp); +#endif gbuf_data.surface_N = g_data.N; gbuf_data.thickness = thickness; gbuf_data.object_id = resource_id; @@ -97,11 +93,11 @@ void main() /* Output remaining closures using image store. */ /* NOTE: The image view start at layer 2 so all destination layer is `layer - 2`. */ - for (int layer = 2; layer < GBUFFER_DATA_MAX && layer < gbuf.layer_data; layer++) { + for (int layer = 2; layer < GBUFFER_DATA_MAX && layer < gbuf.data_len; layer++) { imageStore(out_gbuf_closure_img, ivec3(out_texel, layer - 2), gbuf.data[layer]); } /* NOTE: The image view start at layer 1 so all destination layer is `layer - 1`. */ - for (int layer = 1; layer < GBUFFER_NORMAL_MAX && layer < gbuf.layer_normal; layer++) { + for (int layer = 1; layer < GBUFFER_NORMAL_MAX && layer < gbuf.normal_len; layer++) { imageStore(out_gbuf_normal_img, ivec3(out_texel, layer - 1), gbuf.N[layer].xyyy); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl index b5d55177d45..f62ffe16fb2 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl @@ -21,7 +21,7 @@ vec4 closure_to_rgba(Closure cl) out_color.a = saturate(1.0 - average(g_transmittance)); /* Reset for the next closure tree. */ - closure_weights_reset(); + closure_weights_reset(0.0); return out_color; } @@ -31,7 +31,7 @@ void main() #ifdef MAT_TRANSPARENT init_globals(); - nodetree_surface(); + nodetree_surface(0.0); # ifdef MAT_FORWARD /* Pre-pass only allows fully opaque areas to cut through all transparent layers. */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl index c200d780c4f..cb5477724fd 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl @@ -26,7 +26,9 @@ vec4 closure_to_rgba(Closure cl_unused) forward_lighting_eval(g_thickness, radiance, transmittance); /* Reset for the next closure tree. */ - closure_weights_reset(); + float noise = utility_tx_fetch(utility_tx, gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r; + float closure_rand = fract(noise + sampling_rng_1D_get(SAMPLING_CLOSURE)); + closure_weights_reset(closure_rand); return vec4(radiance, saturate(1.0 - average(transmittance))); } @@ -39,13 +41,13 @@ void main() init_globals(); float noise = utility_tx_fetch(utility_tx, gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r; - g_closure_rand = fract(noise + sampling_rng_1D_get(SAMPLING_CLOSURE)); + float closure_rand = fract(noise + sampling_rng_1D_get(SAMPLING_CLOSURE)); fragment_displacement(); g_thickness = max(0.0, nodetree_thickness()); - nodetree_surface(); + nodetree_surface(closure_rand); vec3 radiance, transmittance; forward_lighting_eval(g_thickness, radiance, transmittance); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_hybrid_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_hybrid_frag.glsl index fa0a76307a6..91796d721e8 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_hybrid_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_hybrid_frag.glsl @@ -27,7 +27,9 @@ vec4 closure_to_rgba(Closure cl_unused) forward_lighting_eval(g_thickness, radiance, transmittance); /* Reset for the next closure tree. */ - closure_weights_reset(); + float noise = utility_tx_fetch(utility_tx, gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r; + float closure_rand = fract(noise + sampling_rng_1D_get(SAMPLING_CLOSURE)); + closure_weights_reset(closure_rand); return vec4(radiance, saturate(1.0 - average(transmittance))); } @@ -40,11 +42,11 @@ void main() init_globals(); float noise = utility_tx_fetch(utility_tx, gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r; - g_closure_rand = fract(noise + sampling_rng_1D_get(SAMPLING_CLOSURE)); + float closure_rand = fract(noise + sampling_rng_1D_get(SAMPLING_CLOSURE)); fragment_displacement(); - nodetree_surface(); + nodetree_surface(closure_rand); g_holdout = saturate(g_holdout); @@ -54,15 +56,6 @@ void main() float transparency = 1.0 - average(g_transmittance); float transparency_rcp = safe_rcp(transparency); g_emission *= transparency_rcp; - g_diffuse_data.weight *= transparency_rcp; - g_translucent_data.weight *= transparency_rcp; - g_reflection_data.weight *= transparency_rcp; - g_refraction_data.weight *= transparency_rcp; - - g_diffuse_data.color *= g_diffuse_data.weight; - g_translucent_data.color *= g_translucent_data.weight; - g_reflection_data.color *= g_reflection_data.weight; - g_refraction_data.color *= g_refraction_data.weight; ivec2 out_texel = ivec2(gl_FragCoord.xy); @@ -82,10 +75,13 @@ void main() /* ----- GBuffer output ----- */ GBufferData gbuf_data; - gbuf_data.diffuse = g_diffuse_data; - gbuf_data.translucent = g_translucent_data; - gbuf_data.reflection = g_reflection_data; - gbuf_data.refraction = g_refraction_data; + gbuf_data.closure[0] = g_closure_get_resolved(0, transparency_rcp); +#if CLOSURE_BIN_COUNT > 1 + gbuf_data.closure[1] = g_closure_get_resolved(1, transparency_rcp); +#endif +#if CLOSURE_BIN_COUNT > 2 + gbuf_data.closure[2] = g_closure_get_resolved(2, transparency_rcp); +#endif gbuf_data.surface_N = g_data.N; gbuf_data.thickness = g_thickness; gbuf_data.object_id = resource_id; @@ -100,11 +96,11 @@ void main() /* Output remaining closures using image store. */ /* NOTE: The image view start at layer 2 so all destination layer is `layer - 2`. */ - for (int layer = 2; layer < GBUFFER_DATA_MAX && layer < gbuf.layer_data; layer++) { + for (int layer = 2; layer < GBUFFER_DATA_MAX && layer < gbuf.data_len; layer++) { imageStore(out_gbuf_closure_img, ivec3(out_texel, layer - 2), gbuf.data[layer]); } /* NOTE: The image view start at layer 1 so all destination layer is `layer - 1`. */ - for (int layer = 1; layer < GBUFFER_NORMAL_MAX && layer < gbuf.layer_normal; layer++) { + for (int layer = 1; layer < GBUFFER_NORMAL_MAX && layer < gbuf.normal_len; layer++) { imageStore(out_gbuf_normal_img, ivec3(out_texel, layer - 1), gbuf.N[layer].xyyy); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl index 4bf88f64e14..e47ecd27f74 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl @@ -28,7 +28,7 @@ void main() #ifdef MAT_TRANSPARENT init_globals(); - nodetree_surface(); + nodetree_surface(0.0); float noise_offset = sampling_rng_1D_get(SAMPLING_TRANSPARENCY); float random_threshold = transparency_hashed_alpha_threshold(1.0, noise_offset, g_data.P); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl index f8b5e7cd9bf..f63504fde6b 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl @@ -31,7 +31,7 @@ void main() g_data.P = -g_data.N; attrib_load(); - nodetree_surface(); + nodetree_surface(0.0); g_holdout = saturate(g_holdout); diff --git a/source/blender/gpu/GPU_material.hh b/source/blender/gpu/GPU_material.hh index c4451cef48b..e6f9ad01bab 100644 --- a/source/blender/gpu/GPU_material.hh +++ b/source/blender/gpu/GPU_material.hh @@ -73,6 +73,7 @@ enum eGPUMaterialFlag { GPU_MATFLAG_HOLDOUT = (1 << 6), GPU_MATFLAG_SHADER_TO_RGBA = (1 << 7), GPU_MATFLAG_AO = (1 << 8), + /* Signals the presence of multiple reflection closures. */ GPU_MATFLAG_COAT = (1 << 9), GPU_MATFLAG_TRANSLUCENT = (1 << 10), diff --git a/source/blender/gpu/intern/gpu_material.cc b/source/blender/gpu/intern/gpu_material.cc index db68f7c20c1..0d0f4f58bc2 100644 --- a/source/blender/gpu/intern/gpu_material.cc +++ b/source/blender/gpu/intern/gpu_material.cc @@ -790,6 +790,10 @@ bool GPU_material_has_displacement_output(GPUMaterial *mat) void GPU_material_flag_set(GPUMaterial *mat, eGPUMaterialFlag flag) { + if ((flag & GPU_MATFLAG_GLOSSY) && (mat->flag & GPU_MATFLAG_GLOSSY)) { + /* Tag material using multiple glossy BSDF as using clear coat. */ + mat->flag |= GPU_MATFLAG_COAT; + } mat->flag |= flag; }