EEVEE-Next: Make closure evaluation fully type agnostic

The goal of this task is to remove noise in the most common material
layering configuration.

Subsequently, this also split the evaluation of different closure to
their own buffer to avoid discontinuity when denoising them.

This commit does a few things:
- [x] Removes use of global for closure random number.
- [x] Refactor the forward evaluation to be closure type agnostic.
- [x] Refactor the gbuffer lib to be closure type agnostic.
- [x] Reduces the number of picked closure to 3 maximum or less.
- [x] Use GPU_MATFLAG_COAT to tag the use of multiple usage of glossy BSDF.
- [x] Use two closure bin for Glossy when more than one.
- [x] Set closure bin per type for best noise level for most materials.
- [x] Change the gbuffer header to put the closure at their bin index.
- [x] Add a method to get a closure from the gbuffer from a specific bin.
- [x] Split lighting passes per Closure.

Pull Request: https://projects.blender.org/blender/blender/pulls/118079
This commit is contained in:
Clément Foucault
2024-02-24 00:00:11 +01:00
committed by Clément Foucault
parent eaea3b846f
commit 06d3627c43
30 changed files with 743 additions and 526 deletions

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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";

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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));
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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);
}
/** \} */

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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];

View File

@@ -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 */

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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.

View File

@@ -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);

View File

@@ -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))

View File

@@ -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) ||

View File

@@ -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 ----- */

View File

@@ -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);
}

View File

@@ -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. */

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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),

View File

@@ -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;
}