EEVEE-Next: Remove Light eval hard coded closures

Related to #115966

Allows for multiple evaluation of the same closure
(i.e: metal + clearcoat).

Pull Request: https://projects.blender.org/blender/blender/pulls/116478
This commit is contained in:
Clément Foucault
2023-12-23 07:58:10 +01:00
committed by Clément Foucault
parent df91fb329e
commit dc155e2ae4
5 changed files with 146 additions and 112 deletions

View File

@@ -11,48 +11,77 @@
#pragma BLENDER_REQUIRE(eevee_renderpass_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl)
vec3 load_radiance_direct(ivec2 texel, int i)
{
/* TODO(fclem): Layered texture. */
switch (i) {
case 0:
return imageLoad(direct_radiance_1_img, texel).rgb;
case 1:
return imageLoad(direct_radiance_2_img, texel).rgb;
case 2:
return imageLoad(direct_radiance_3_img, texel).rgb;
default:
return vec3(0);
}
return vec3(0);
}
vec3 load_radiance_indirect(ivec2 texel, ClosureType closure_type)
{
/* TODO(fclem): Layered texture. */
switch (closure_type) {
case CLOSURE_BSSRDF_BURLEY_ID:
case CLOSURE_BSDF_DIFFUSE_ID:
return imageLoad(indirect_diffuse_img, texel).rgb;
case CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID:
return imageLoad(indirect_reflect_img, texel).rgb;
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
return imageLoad(indirect_refract_img, texel).rgb;
default:
return vec3(0);
}
return vec3(0);
}
void main()
{
ivec2 texel = ivec2(gl_FragCoord.xy);
GBufferReader gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel);
vec3 glossy_reflect_light = vec3(0.0);
vec3 glossy_refract_light = vec3(0.0);
vec3 diffuse_reflect_light = vec3(0.0);
vec3 diffuse_refract_light = vec3(0.0);
out_combined = vec4(0.0, 0.0, 0.0, 0.0);
vec3 out_diffuse = vec3(0.0);
vec3 out_specular = vec3(0.0);
if (gbuf.has_diffuse) {
diffuse_reflect_light = imageLoad(direct_radiance_1_img, texel).rgb +
imageLoad(indirect_diffuse_img, texel).rgb;
}
if (gbuf.has_reflection) {
glossy_reflect_light = imageLoad(direct_radiance_2_img, texel).rgb +
imageLoad(indirect_reflect_img, texel).rgb;
}
if (gbuf.has_translucent) {
/* Indirect radiance not implemented yet. */
diffuse_refract_light = imageLoad(direct_radiance_3_img, texel).rgb;
}
if (gbuf.has_refraction) {
/* Direct radiance not implemented yet. */
glossy_refract_light = imageLoad(indirect_refract_img, texel).rgb;
for (int i = 0; i < GBUFFER_LAYER_MAX && i < gbuf.closure_count; i++) {
vec3 closure_light = load_radiance_direct(texel, i) +
load_radiance_indirect(texel, gbuf.closures[i].type);
closure_light *= gbuf.closures[i].color;
out_combined.rgb += closure_light;
switch (gbuf.closures[i].type) {
case CLOSURE_BSDF_TRANSLUCENT_ID:
case CLOSURE_BSSRDF_BURLEY_ID:
case CLOSURE_BSDF_DIFFUSE_ID:
out_diffuse += closure_light;
break;
case CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID:
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
out_specular += closure_light;
break;
case CLOSURE_NONE_ID:
/* TODO(fclem): Assert. */
break;
}
}
#if 1 /* TODO(fclem): Only if needed. */
/* Light passes. */
vec3 diffuse_light = diffuse_reflect_light + diffuse_refract_light;
vec3 specular_light = glossy_reflect_light + glossy_refract_light;
output_renderpass_color(uniform_buf.render_pass.diffuse_light_id, vec4(diffuse_light, 1.0));
output_renderpass_color(uniform_buf.render_pass.specular_light_id, vec4(specular_light, 1.0));
/* Combine. */
out_combined = vec4(0.0);
out_combined.xyz += diffuse_reflect_light * gbuf.data.diffuse.color;
out_combined.xyz += diffuse_refract_light * gbuf.data.translucent.color;
out_combined.xyz += glossy_reflect_light * gbuf.data.reflection.color;
out_combined.xyz += glossy_refract_light * gbuf.data.refraction.color;
output_renderpass_color(uniform_buf.render_pass.diffuse_light_id, vec4(out_diffuse, 1.0));
output_renderpass_color(uniform_buf.render_pass.specular_light_id, vec4(out_specular, 1.0));
#endif
if (any(isnan(out_combined))) {
out_combined = vec4(1.0, 0.0, 1.0, 0.0);

View File

@@ -13,6 +13,33 @@
#pragma BLENDER_REQUIRE(eevee_thickness_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_subsurface_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;
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.type = LIGHT_SPECULAR;
break;
case CLOSURE_NONE_ID:
/* TODO(fclem): Assert. */
break;
}
return cl_light;
}
void main()
{
ivec2 texel = ivec2(gl_FragCoord.xy);
@@ -25,46 +52,16 @@ void main()
}
vec3 P = drw_point_screen_to_world(vec3(uvcoordsvar.xy, depth));
/* Assume reflection closure normal is always somewhat representative of the geometric normal.
* Ng is only used for shadow biases and subsurface check in this case. */
vec3 Ng = gbuf.data.surface_N;
vec3 V = drw_world_incident_vector(P);
float vPz = dot(drw_view_forward(), P) - dot(drw_view_forward(), drw_view_position());
ClosureLight cl_diff;
cl_diff.N = gbuf.data.diffuse.N;
cl_diff.ltc_mat = LTC_LAMBERT_MAT;
cl_diff.type = LIGHT_DIFFUSE;
ClosureLight cl_refl;
cl_refl.N = gbuf.data.reflection.N;
cl_refl.ltc_mat = LTC_GGX_MAT(dot(gbuf.data.reflection.N, V), gbuf.data.reflection.roughness);
cl_refl.type = LIGHT_SPECULAR;
ClosureLight cl_sss;
cl_sss.N = -gbuf.data.diffuse.N;
cl_sss.ltc_mat = LTC_LAMBERT_MAT;
cl_sss.type = LIGHT_DIFFUSE;
ClosureLight cl_translucent;
cl_translucent.N = -gbuf.data.translucent.N;
cl_translucent.ltc_mat = LTC_LAMBERT_MAT;
cl_translucent.type = LIGHT_DIFFUSE;
ClosureLightStack stack;
for (int i = 0; i < LIGHT_CLOSURE_EVAL_COUNT && i < gbuf.closure_count; i++) {
stack.cl[i] = closure_light_new(gbuf.closures[i], V);
}
/* TODO(fclem): This is waiting for fully flexible evaluation pipeline. We need to refactor the
* raytracing pipeline first. */
stack.cl[0] = (gbuf.has_diffuse) ? cl_diff : cl_refl;
#if LIGHT_CLOSURE_EVAL_COUNT > 1
stack.cl[1] = cl_refl;
#endif
#if LIGHT_CLOSURE_EVAL_COUNT > 2
stack.cl[2] = (gbuf.has_translucent) ? cl_translucent : cl_sss;
#endif
/* TODO(fclem): Split thickness computation. */
float thickness = (gbuf.has_translucent) ? gbuf.data.thickness : 0.0;
#ifdef MAT_SUBSURFACE
if (gbuf.has_sss) {
@@ -72,57 +69,57 @@ void main()
thickness = (shadow_thickness != THICKNESS_NO_VALUE) ?
max(shadow_thickness, gbuf.data.thickness) :
gbuf.data.thickness;
/* Add one translucent closure for all SSS closure. Reuse the same lighting. */
ClosureLight cl_light;
cl_light.N = -Ng;
cl_light.ltc_mat = LTC_LAMBERT_MAT;
cl_light.type = LIGHT_DIFFUSE;
stack.cl[gbuf.closure_count] = cl_light;
}
#endif
light_eval(stack, P, Ng, V, vPz, thickness);
vec3 radiance_shadowed = stack.cl[0].light_shadowed;
vec3 radiance_unshadowed = stack.cl[0].light_unshadowed;
#if LIGHT_CLOSURE_EVAL_COUNT > 1
radiance_shadowed += stack.cl[1].light_shadowed;
radiance_unshadowed += stack.cl[1].light_unshadowed;
#endif
#if LIGHT_CLOSURE_EVAL_COUNT > 2
radiance_shadowed += stack.cl[2].light_shadowed;
radiance_unshadowed += stack.cl[2].light_unshadowed;
#endif
#ifdef MAT_SUBSURFACE
if (gbuf.has_sss) {
vec3 sss_profile = subsurface_transmission(gbuf.data.diffuse.sss_radius, thickness);
stack.cl[2].light_shadowed *= sss_profile;
stack.cl[2].light_unshadowed *= sss_profile;
/* Add to diffuse light for processing inside the Screen Space SSS pass. */
stack.cl[0].light_shadowed += stack.cl[2].light_shadowed;
stack.cl[0].light_unshadowed += stack.cl[2].light_unshadowed;
for (int i = 0; i < LIGHT_CLOSURE_EVAL_COUNT && i < gbuf.closure_count; i++) {
/* Add to diffuse light for processing inside the Screen Space SSS pass.
* The tranlucent light is not outputed as a separate quantity because
* it is over the closure_count. */
if (gbuf.closures[i].type == CLOSURE_BSSRDF_BURLEY_ID) {
/* Apply absorption per closure. */
vec3 sss_profile = subsurface_transmission(gbuf.closures[i].data.rgb, thickness);
stack.cl[i].light_shadowed += stack.cl[gbuf.closure_count].light_shadowed * sss_profile;
stack.cl[i].light_unshadowed += stack.cl[gbuf.closure_count].light_unshadowed *
sss_profile;
}
}
}
#endif
#if 1 /* TODO(fclem): Limit to when shadow pass is needed. */
vec3 radiance_shadowed = vec3(0);
vec3 radiance_unshadowed = vec3(0);
for (int i = 0; i < LIGHT_CLOSURE_EVAL_COUNT && i < gbuf.closure_count; i++) {
radiance_shadowed += stack.cl[i].light_shadowed;
radiance_unshadowed += stack.cl[i].light_unshadowed;
}
/* TODO(fclem): Change shadow pass to be colored. */
vec3 shadows = radiance_shadowed * safe_rcp(radiance_unshadowed);
output_renderpass_value(uniform_buf.render_pass.shadow_id, average(shadows));
if (gbuf.closure_count > 0) {
/* TODO(fclem): This is waiting for fully flexible evaluation pipeline. We need to refactor the
* raytracing pipeline first. */
if (gbuf.has_diffuse) {
imageStore(direct_radiance_1_img, texel, vec4(stack.cl[0].light_shadowed, 1.0));
}
else {
imageStore(direct_radiance_2_img, texel, vec4(stack.cl[0].light_shadowed, 1.0));
}
}
#if LIGHT_CLOSURE_EVAL_COUNT > 1
if (gbuf.closure_count > 1) {
imageStore(direct_radiance_2_img, texel, vec4(stack.cl[1].light_shadowed, 1.0));
}
#endif
#if LIGHT_CLOSURE_EVAL_COUNT > 2
if (gbuf.closure_count > 2 || gbuf.has_translucent) {
imageStore(direct_radiance_3_img, texel, vec4(stack.cl[2].light_shadowed, 1.0));
for (int i = 0; i < LIGHT_CLOSURE_EVAL_COUNT && i < gbuf.closure_count; i++) {
/* TODO(fclem): Layered texture. */
if (i == 0) {
imageStore(direct_radiance_1_img, texel, vec4(stack.cl[i].light_shadowed, 1.0));
}
else if (i == 1) {
imageStore(direct_radiance_2_img, texel, vec4(stack.cl[i].light_shadowed, 1.0));
}
else if (i == 2) {
imageStore(direct_radiance_3_img, texel, vec4(stack.cl[i].light_shadowed, 1.0));
}
}
#endif
}

View File

@@ -17,8 +17,9 @@
*
* \{ */
#define GBUFFER_LAYER_MAX 8
#define GBUFFER_NORMAL_MAX 4
#define GBUFFER_LAYER_MAX 4
#define GBUFFER_NORMAL_MAX GBUFFER_LAYER_MAX
#define GBUFFER_DATA_MAX (GBUFFER_LAYER_MAX * 2)
/* Structure used as input and output of the packing & read functions. */
struct GBufferData {
@@ -52,7 +53,7 @@ struct GBufferDataUndetermined {
struct GBufferWriter {
uint header;
/* TODO(fclem): Better packing. */
vec4 data[GBUFFER_LAYER_MAX];
vec4 data[GBUFFER_DATA_MAX];
vec2 N[GBUFFER_NORMAL_MAX];
/* Only used for book-keeping. Not actually written. Can be derived from header. */
@@ -65,6 +66,8 @@ struct GBufferWriter {
struct GBufferReader {
GBufferData data;
ClosureUndetermined closures[GBUFFER_LAYER_MAX];
bool has_diffuse;
bool has_translucent;
bool has_reflection;
@@ -72,7 +75,7 @@ struct GBufferReader {
bool has_sss;
bool has_any_surface;
uint header;
uint closure_count;
int closure_count;
/* Only used for book-keeping when reading. */
int layer_data;
int layer_normal;
@@ -297,6 +300,8 @@ void gbuffer_append_closure(inout GBufferWriter gbuf, GBufferMode closure_type)
}
void gbuffer_register_closure(inout GBufferReader gbuf, ClosureUndetermined cl)
{
gbuf.closures[gbuf.closure_count] = cl;
gbuf.closure_count++;
switch (cl.type) {
case CLOSURE_NONE_ID:
/* TODO(fclem): Assert. */
@@ -333,7 +338,6 @@ void gbuffer_register_closure(inout GBufferReader gbuf, ClosureUndetermined cl)
gbuf.has_refraction = true;
break;
}
gbuf.closure_count++;
}
void gbuffer_append_data(inout GBufferWriter gbuf, vec4 data)
@@ -666,7 +670,7 @@ GBufferReader gbuffer_read_header(uint header)
gbuf.has_refraction = false;
gbuf.has_translucent = false;
gbuf.has_sss = false;
gbuf.closure_count = 0u;
gbuf.closure_count = 0;
for (int layer = 0; layer < 4; layer++) {
GBufferMode mode = gbuffer_header_unpack(gbuf.header, layer);
@@ -744,7 +748,11 @@ GBufferReader gbuffer_read(samplerGBufferHeader header_tx,
gbuf.data.thickness = 0.0;
for (int layer = 0; layer < 4; layer++) {
for (int layer = 0; layer < GBUFFER_LAYER_MAX; layer++) {
gbuf.closures[layer].type = CLOSURE_NONE_ID;
}
for (int layer = 0; layer < GBUFFER_LAYER_MAX; layer++) {
GBufferMode mode = gbuffer_header_unpack(gbuf.header, layer);
switch (mode) {
case GBUF_NONE:

View File

@@ -101,7 +101,7 @@ 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_LAYER_MAX && layer < gbuf.layer_data; layer++) {
for (int layer = 2; layer < GBUFFER_DATA_MAX && layer < gbuf.layer_data; 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`. */

View File

@@ -104,7 +104,7 @@ 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_LAYER_MAX && layer < gbuf.layer_data; layer++) {
for (int layer = 2; layer < GBUFFER_DATA_MAX && layer < gbuf.layer_data; 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`. */