1
1

Compare commits

...

17 Commits

Author SHA1 Message Date
074dc9b675 EEVEE: Lightcache: Change cubemap roughness mapping
This changes the roughness mapping to better utilize the mip chain resolution.
This improves glossy reflections with small roughness.

Lightcache version bumped because old data does not have the same roughness
mapping and cannot be used.
2021-02-10 20:40:42 +01:00
a9ac8d8871 EEVEE: Better fit of BTDF LUT
This simplify the BTDF retreival removing the manual clean cut at
low roughness.

This maximize the precision of the LUT by scalling the sides by the
critical angle.

Also touched the ior > 1.0 approximation to be smoother.
2021-02-10 17:02:06 +01:00
9c2f459816 EEVEE: Improve / fix GGX LUTs
This modifies the principled BSDF and the Glass BSDF which now
have better fit to multiscatter GGX.

Code to generate the LUT have been updated and can run at runtime.

The refraction LUT has been changed to have the critical angle always
centered around one pixel so that interpolation can be mitigated.

Offline LUT data will be updated in another commit.
2021-02-10 05:09:12 +01:00
b1840d1bc5 EEVEE: Cleanup: Remove setup_noise from bsdf_sampling
Not needed anymore and makes code harder to read.
2021-02-09 21:41:54 +01:00
8ba1acf826 EEVEE: Cleanup: Remove sample count from bsdf_sampling
Makes uniform more local to where they are needed.
2021-02-09 21:37:45 +01:00
b1d43d0f60 EEVEE: Fix incorrect fresnel function
This caused discrepency between cycles and EEVEE.
2021-02-09 19:37:55 +01:00
251ad8bf60 EEVEE: Fix issue with planar SSR 2021-02-09 02:02:18 +01:00
7ca2427ce2 EEVEE: Fix most regression with ambient occlusion
Occlusion had change because of the way it was applied.
Get back to almost the same thing we had previously. The only thing
that changed is the occlusion of Normal mapped surface.

It is a bit less noticeable due to a new approximation we make.
2021-02-09 01:36:20 +01:00
744427b729 EEVEE: Cleanup: Remove use of macros for the loops 2021-02-08 17:11:05 +01:00
6eaeb6272e EEVEE: Fix occlusion applied on SSR 2021-02-08 16:45:38 +01:00
1d56589f14 EEVEE: Add back support for user occlusion on eevee_specular BSDF 2021-02-08 15:43:24 +01:00
f6cc14f86e EEVEE: Split closure_lit_lib.glsl 2021-02-08 01:25:21 +01:00
1df2f7a713 EEVEE: Fix missing shadowing factor 2021-02-07 20:46:37 +01:00
bb05f6d335 EEVEE: Replace constant booleans by const float
This makes principled optimization easier and less verbose. Tests shows
no differences in performance.
2021-02-07 20:46:37 +01:00
36b066ee98 EEVEE: Fix lightgrid accumulation not using diffuse_accum
Also add safe normalize to interpolated normals.
2021-02-07 20:46:37 +01:00
60b2e410a7 EEVEE: Fix typo in macro leading to undefined behavior
Only the 1st closure radiance accumulator was initialized correctly.
2021-02-07 20:13:09 +01:00
fe00859202 EEVEE: Rewrite closure_lit_lib to reduce complexity
This rewrite improves:
- Code clarity: Less duplicated code and removes a few hacks.
- Compile time performance: Shader code was divided by 3 in average.
  I did not profile the compilation time but it is faster.
- Shading Perf: Noticed a 25% performance improvement on
  the shading pass with default dielectric principled bsdf.
- Fix Principled Tint being white if color is black
- It seems to have fixed issues on some drivers giving some incorect
  results.
- Changes Principled BSDF support to be less hacky.
2021-02-07 18:03:58 +01:00
44 changed files with 1599 additions and 1555 deletions

View File

@@ -90,6 +90,7 @@ set(SRC
engines/eevee/eevee_lights.c
engines/eevee/eevee_lookdev.c
engines/eevee/eevee_lut.c
engines/eevee/eevee_lut_gen.c
engines/eevee/eevee_materials.c
engines/eevee/eevee_mist.c
engines/eevee/eevee_motion_blur.c
@@ -197,7 +198,6 @@ set(LIB
data_to_c_simple(engines/eevee/shaders/ambient_occlusion_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/background_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/closure_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/common_uniforms_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/common_utiltex_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lights_lib.glsl SRC)
@@ -215,7 +215,12 @@ data_to_c_simple(engines/eevee/shaders/lightprobe_grid_fill_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_planar_display_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_planar_display_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lookdev_world_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/closure_lit_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/closure_eval_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/closure_eval_diffuse_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/closure_eval_glossy_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/closure_eval_refraction_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/closure_eval_translucent_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/closure_type_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_bloom_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_dof_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_dof_frag.glsl SRC)

View File

@@ -1040,8 +1040,10 @@ void EEVEE_lightbake_filter_glossy(EEVEE_ViewLayerData *sldata,
pinfo->padding_size *= pinfo->texel_size;
pinfo->layer = probe_idx * 6;
pinfo->roughness = i / (float)maxlevel;
pinfo->roughness *= pinfo->roughness; /* Disney Roughness */
pinfo->roughness *= pinfo->roughness; /* Distribute Roughness accros lod more evenly */
/* Disney Roughness */
pinfo->roughness = square_f(pinfo->roughness);
/* Distribute Roughness across lod more evenly */
pinfo->roughness = square_f(square_f(pinfo->roughness));
CLAMP(pinfo->roughness, 1e-8f, 0.99999f); /* Avoid artifacts */
#if 1 /* Variable Sample count and bias (fast) */

View File

@@ -33,139 +33,82 @@
#include "eevee_private.h"
static struct GPUTexture *create_ggx_lut_texture(int UNUSED(w), int UNUSED(h))
float *EEVEE_lut_update_ggx_brdf(int lut_size)
{
struct GPUTexture *tex;
struct GPUFrameBuffer *fb = NULL;
static float samples_len = 8192.0f;
static float inv_samples_len = 1.0f / 8192.0f;
DRWPass *pass = DRW_pass_create("LightProbe Filtering", DRW_STATE_WRITE_COLOR);
DRWPass *pass = DRW_pass_create(__func__, DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_ggx_lut_sh_get(), pass);
DRW_shgroup_uniform_float(grp, "sampleCount", &samples_len, 1);
DRW_shgroup_uniform_float(grp, "invSampleCount", &inv_samples_len, 1);
DRW_shgroup_uniform_texture(grp, "texHammersley", e_data.hammersley);
DRW_shgroup_uniform_texture(grp, "texJitter", e_data.jitter);
struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
DRW_shgroup_call(grp, geom, NULL);
float *texels = MEM_mallocN(sizeof(float[2]) * w * h, "lut");
tex = DRW_texture_create_2d(w, h, GPU_RG16F, DRW_TEX_FILTER, (float *)texels);
DRWFboTexture tex_filter = {&tex, GPU_RG16F, DRW_TEX_FILTER};
GPU_framebuffer_init(&fb, &draw_engine_eevee_type, w, h, &tex_filter, 1);
DRW_shgroup_uniform_float_copy(grp, "sampleCount", 64.0f); /* Actual sample count is squared. */
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
GPUTexture *tex = DRW_texture_create_2d(lut_size, lut_size, GPU_RG16F, 0, NULL);
GPUFrameBuffer *fb = NULL;
GPU_framebuffer_ensure_config(&fb,
{
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(tex),
});
GPU_framebuffer_bind(fb);
DRW_draw_pass(pass);
GPU_FRAMEBUFFER_FREE_SAFE(fb);
float *data = MEM_mallocN(sizeof(float[3]) * w * h, "lut");
GPU_framebuffer_read_color(fb, 0, 0, w, h, 3, 0, GPU_DATA_FLOAT, data);
printf("{");
for (int i = 0; i < w * h * 3; i += 3) {
printf("%ff, %ff, ", data[i], data[i + 1]);
i += 3;
printf("%ff, %ff, ", data[i], data[i + 1]);
i += 3;
printf("%ff, %ff, ", data[i], data[i + 1]);
i += 3;
printf("%ff, %ff, \n", data[i], data[i + 1]);
float *data = GPU_texture_read(tex, GPU_DATA_FLOAT, 0);
GPU_texture_free(tex);
#if 0
FILE *f = BLI_fopen("brdf_split_sum_ggx.h", "w");
fprintf(f, "static float brdf_split_sum_ggx[%d * %d * 2] = {\n", lut_size, lut_size);
fprintf(f, "{");
for (int i = 0; i < lut_size * lut_size * 2;) {
for (int j = 0; j < 4; j++, i += 2) {
fprintf(f, "%ff, %ff, ", data[i], data[i + 1]);
}
fprintf(f, "\n ");
}
printf("}");
MEM_freeN(texels);
MEM_freeN(data);
return tex;
}
static struct GPUTexture *create_ggx_refraction_lut_texture(int w, int h)
{
struct GPUTexture *tex;
struct GPUTexture *hammersley = create_hammersley_sample_texture(8192);
struct GPUFrameBuffer *fb = NULL;
static float samples_len = 8192.0f;
static float a2 = 0.0f;
static float inv_samples_len = 1.0f / 8192.0f;
DRWPass *pass = DRW_pass_create("LightProbe Filtering", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_ggx_refraction_lut_sh_get(), pass);
DRW_shgroup_uniform_float(grp, "a2", &a2, 1);
DRW_shgroup_uniform_float(grp, "sampleCount", &samples_len, 1);
DRW_shgroup_uniform_float(grp, "invSampleCount", &inv_samples_len, 1);
DRW_shgroup_uniform_texture(grp, "texHammersley", hammersley);
DRW_shgroup_uniform_texture(grp, "utilTex", e_data.util_tex);
struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
DRW_shgroup_call(grp, geom, NULL);
float *texels = MEM_mallocN(sizeof(float[2]) * w * h, "lut");
tex = DRW_texture_create_2d(w, h, GPU_R16F, DRW_TEX_FILTER, (float *)texels);
DRWFboTexture tex_filter = {&tex, GPU_R16F, DRW_TEX_FILTER};
GPU_framebuffer_init(&fb, &draw_engine_eevee_type, w, h, &tex_filter, 1);
GPU_framebuffer_bind(fb);
float *data = MEM_mallocN(sizeof(float[3]) * w * h, "lut");
float inc = 1.0f / 31.0f;
float roughness = 1e-8f - inc;
FILE *f = BLI_fopen("btdf_split_sum_ggx.h", "w");
fprintf(f, "static float btdf_split_sum_ggx[32][64 * 64] = {\n");
do {
roughness += inc;
CLAMP(roughness, 1e-4f, 1.0f);
a2 = powf(roughness, 4.0f);
DRW_draw_pass(pass);
GPU_framebuffer_read_data(0, 0, w, h, 3, 0, data);
#if 1
fprintf(f, "\t{\n\t\t");
for (int i = 0; i < w * h * 3; i += 3) {
fprintf(f, "%ff,", data[i]);
if (((i / 3) + 1) % 12 == 0) {
fprintf(f, "\n\t\t");
}
else {
fprintf(f, " ");
}
}
fprintf(f, "\n\t},\n");
#else
for (int i = 0; i < w * h * 3; i += 3) {
if (data[i] < 0.01) {
printf(" ");
}
else if (data[i] < 0.3) {
printf(".");
}
else if (data[i] < 0.6) {
printf("+");
}
else if (data[i] < 0.9) {
printf("%%");
}
else {
printf("#");
}
if ((i / 3 + 1) % 64 == 0) {
printf("\n");
}
}
fprintf(f, "}");
fclose(f);
#endif
} while (roughness < 1.0f);
fprintf(f, "\n};\n");
fclose(f);
MEM_freeN(texels);
MEM_freeN(data);
return tex;
return data;
}
float *EEVEE_lut_update_ggx_btdf(int lut_size, int lut_depth)
{
float roughness;
DRWPass *pass = DRW_pass_create(__func__, DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_ggx_refraction_lut_sh_get(), pass);
DRW_shgroup_uniform_float_copy(grp, "sampleCount", 64.0f); /* Actual sample count is squared. */
DRW_shgroup_uniform_float(grp, "z", &roughness, 1);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
GPUTexture *tex = DRW_texture_create_2d_array(lut_size, lut_size, lut_depth, GPU_RG16F, 0, NULL);
GPUFrameBuffer *fb = NULL;
for (int i = 0; i < lut_depth; i++) {
GPU_framebuffer_ensure_config(&fb,
{
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE_LAYER(tex, i),
});
GPU_framebuffer_bind(fb);
roughness = i / (lut_depth - 1.0f);
DRW_draw_pass(pass);
}
GPU_FRAMEBUFFER_FREE_SAFE(fb);
float *data = GPU_texture_read(tex, GPU_DATA_FLOAT, 0);
GPU_texture_free(tex);
#if 0
FILE *f = BLI_fopen("brdf_split_sum_ggx.h", "w");
fprintf(f, "static float brdf_split_sum_ggx[%d * %d * 2] = {\n", lut_size, lut_size);
fprintf(f, "{");
for (int i = 0; i < lut_size * lut_size * 2;) {
for (int j = 0; j < 4; j++, i += 2) {
fprintf(f, "%ff, %ff, ", data[i], data[i + 1]);
}
fprintf(f, "\n ");
}
fprintf(f, "}");
fclose(f);
#endif
return data;
}

View File

@@ -142,11 +142,20 @@ static void eevee_init_noise_texture(void)
e_data.noise_tex = DRW_texture_create_2d(64, 64, GPU_RGBA16F, 0, (float *)blue_noise);
}
#define RUNTIME_LUT_CREATION 1
static void eevee_init_util_texture(void)
{
const int layers = 4 + 16;
float(*texels)[4] = MEM_mallocN(sizeof(float[4]) * 64 * 64 * layers, "utils texels");
float(*texels_layer)[4] = texels;
#if RUNTIME_LUT_CREATION
float *bsdf_ggx_lut = EEVEE_lut_update_ggx_brdf(64);
float(*btdf_ggx_lut)[64 * 64 * 2] = (float(*)[64 * 64 * 2]) EEVEE_lut_update_ggx_btdf(64, 16);
#else
const float *bsdf_ggx_lut = bsdf_split_sum_ggx;
const float(*btdf_ggx_lut)[64 * 64 * 2] = btdf_split_sum_ggx;
#endif
/* Copy ltc_mat_ggx into 1st layer */
memcpy(texels_layer, ltc_mat_ggx, sizeof(float[4]) * 64 * 64);
@@ -155,8 +164,8 @@ static void eevee_init_util_texture(void)
/* Copy bsdf_split_sum_ggx into 2nd layer red and green channels.
* Copy ltc_mag_ggx into 2nd layer blue and alpha channel. */
for (int i = 0; i < 64 * 64; i++) {
texels_layer[i][0] = bsdf_split_sum_ggx[i * 2 + 0];
texels_layer[i][1] = bsdf_split_sum_ggx[i * 2 + 1];
texels_layer[i][0] = bsdf_ggx_lut[i * 2 + 0];
texels_layer[i][1] = bsdf_ggx_lut[i * 2 + 1];
texels_layer[i][2] = ltc_mag_ggx[i * 2 + 0];
texels_layer[i][3] = ltc_mag_ggx[i * 2 + 1];
}
@@ -183,8 +192,8 @@ static void eevee_init_util_texture(void)
/* Copy Refraction GGX LUT in layer 5 - 21 */
for (int j = 0; j < 16; j++) {
for (int i = 0; i < 64 * 64; i++) {
texels_layer[i][0] = btdf_split_sum_ggx[j * 2][i];
texels_layer[i][1] = 0.0; /* UNUSED */
texels_layer[i][0] = btdf_ggx_lut[j][i * 2 + 0];
texels_layer[i][1] = btdf_ggx_lut[j][i * 2 + 1];
texels_layer[i][2] = 0.0; /* UNUSED */
texels_layer[i][3] = 0.0; /* UNUSED */
}
@@ -195,6 +204,10 @@ static void eevee_init_util_texture(void)
64, 64, layers, GPU_RGBA16F, DRW_TEX_FILTER | DRW_TEX_WRAP, (float *)texels);
MEM_freeN(texels);
#if RUNTIME_LUT_CREATION
MEM_freeN(bsdf_ggx_lut);
MEM_freeN(btdf_ggx_lut);
#endif
}
void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const double offsets[3])

View File

@@ -1422,6 +1422,10 @@ void EEVEE_lookdev_draw(EEVEE_Data *vedata);
/** eevee_engine.c */
void EEVEE_cache_populate(void *vedata, Object *ob);
/** eevee_lut_gen.c */
float *EEVEE_lut_update_ggx_brdf(int lut_size);
float *EEVEE_lut_update_ggx_btdf(int lut_size, int lut_depth);
/* Shadow Matrix */
static const float texcomat[4][4] = {
/* From NDC to TexCo */

View File

@@ -191,6 +191,12 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
DRW_shgroup_uniform_texture_ref(grp, "hitBuffer", &effects->ssr_hit_output);
DRW_shgroup_uniform_texture_ref(grp, "pdfBuffer", &effects->ssr_pdf_output);
DRW_shgroup_uniform_texture_ref(grp, "prevColorBuffer", &txl->color_double_buffer);
DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool);
DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool);
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
@@ -198,7 +204,6 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_uniform_int(grp, "neighborOffset", &effects->ssr_neighbor_ofs, 1);
if ((effects->enabled_effects & EFFECT_GTAO) != 0) {
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons);
}

View File

@@ -142,7 +142,6 @@ static struct {
struct GPUShader *volumetric_accum_sh;
/* Shader strings */
char *closure_lit_lib;
char *surface_lit_frag;
char *surface_prepass_frag;
char *surface_geom_barycentric;
@@ -184,7 +183,7 @@ extern char datatoc_bsdf_common_lib_glsl[];
extern char datatoc_bsdf_lut_frag_glsl[];
extern char datatoc_bsdf_sampling_lib_glsl[];
extern char datatoc_btdf_lut_frag_glsl[];
extern char datatoc_closure_lib_glsl[];
extern char datatoc_closure_type_lib_glsl[];
extern char datatoc_common_uniforms_lib_glsl[];
extern char datatoc_common_utiltex_lib_glsl[];
extern char datatoc_cryptomatte_frag_glsl[];
@@ -224,7 +223,11 @@ extern char datatoc_lightprobe_planar_downsample_geom_glsl[];
extern char datatoc_lightprobe_planar_downsample_vert_glsl[];
extern char datatoc_lightprobe_vert_glsl[];
extern char datatoc_lights_lib_glsl[];
extern char datatoc_closure_lit_lib_glsl[];
extern char datatoc_closure_eval_lib_glsl[];
extern char datatoc_closure_eval_diffuse_lib_glsl[];
extern char datatoc_closure_eval_glossy_lib_glsl[];
extern char datatoc_closure_eval_refraction_lib_glsl[];
extern char datatoc_closure_eval_translucent_lib_glsl[];
extern char datatoc_ltc_lib_glsl[];
extern char datatoc_object_motion_frag_glsl[];
extern char datatoc_object_motion_vert_glsl[];
@@ -279,23 +282,13 @@ static void eevee_shader_library_ensure(void)
DRW_SHADER_LIB_ADD(e_data.lib, lights_lib);
DRW_SHADER_LIB_ADD(e_data.lib, surface_lib);
DRW_SHADER_LIB_ADD(e_data.lib, volumetric_lib);
DRW_SHADER_LIB_ADD(e_data.lib, closure_lib);
DRW_SHADER_LIB_ADD(e_data.lib, ssr_lib);
/* Add one for each Closure */
e_data.closure_lit_lib = BLI_string_joinN(datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl,
datatoc_closure_lit_lib_glsl);
DRW_shader_library_add_file(e_data.lib, e_data.closure_lit_lib, "closure_lit_lib.glsl");
DRW_SHADER_LIB_ADD(e_data.lib, closure_type_lib);
DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_lib);
DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_diffuse_lib);
DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_glossy_lib);
DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_translucent_lib);
DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_refraction_lib);
e_data.surface_lit_frag = DRW_shader_library_create_shader_string(e_data.lib,
datatoc_surface_frag_glsl);
@@ -556,11 +549,8 @@ GPUShader *EEVEE_shaders_effect_maxz_copydepth_sh_get(void)
GPUShader *EEVEE_shaders_ggx_lut_sh_get(void)
{
if (e_data.ggx_lut_sh == NULL) {
e_data.ggx_lut_sh = DRW_shader_create_with_shaderlib(datatoc_lightprobe_vert_glsl,
datatoc_lightprobe_geom_glsl,
datatoc_bsdf_lut_frag_glsl,
e_data.lib,
"#define HAMMERSLEY_SIZE 8192\n");
e_data.ggx_lut_sh = DRW_shader_create_fullscreen_with_shaderlib(
datatoc_bsdf_lut_frag_glsl, e_data.lib, NULL);
}
return e_data.ggx_lut_sh;
}
@@ -1403,7 +1393,6 @@ struct GPUMaterial *EEVEE_material_get(
void EEVEE_shaders_free(void)
{
MEM_SAFE_FREE(e_data.closure_lit_lib);
MEM_SAFE_FREE(e_data.surface_prepass_frag);
MEM_SAFE_FREE(e_data.surface_lit_frag);
MEM_SAFE_FREE(e_data.surface_geom_barycentric);

View File

@@ -191,15 +191,15 @@ void gtao_deferred(
dirs.xy = get_ao_dir(noise.x * 0.5);
dirs.zw = get_ao_dir(noise.x * 0.5 + 0.5);
bent_normal = normal * 1e-8;
visibility = 1e-8;
bent_normal = vec3(0.0);
visibility = 0.0;
horizons = unpack_horizons(horizons);
integrate_slice(normal, dirs.xy, horizons.xy, visibility, bent_normal);
integrate_slice(normal, dirs.zw, horizons.zw, visibility, bent_normal);
bent_normal = normalize(bent_normal / visibility);
bent_normal = safe_normalize(bent_normal);
visibility *= 0.5; /* We integrated 2 slices. */
}
@@ -240,13 +240,24 @@ float gtao_multibounce(float visibility, vec3 albedo)
return max(x, ((x * a + b) * x + c) * x);
}
float diffuse_occlusion(vec3 N, vec3 vis_cone_dir, float vis_cone_aperture_cos, vec3 albedo)
{
if ((int(aoSettings) & USE_AO) == 0) {
return 1.0;
}
/* If the shading normal is orthogonal to the geometric normal, it should be half lit. */
float horizon_fac = saturate(dot(N, vis_cone_dir) * 0.5 + 0.5);
float ao = vis_cone_aperture_cos * horizon_fac;
return gtao_multibounce(ao, albedo);
}
float specular_occlusion(float NV, float AO, float roughness)
{
return saturate(pow(NV + AO, roughness) - 1.0 + AO);
}
/* Use the right occlusion */
float occlusion_compute(vec3 N, vec3 vpos, float user_occlusion, vec4 rand, out vec3 bent_normal)
float occlusion_compute(vec3 N, vec3 vpos, vec4 rand, out vec3 bent_normal)
{
#ifndef USE_REFRACTION
if ((int(aoSettings) & USE_AO) != 0) {
@@ -263,10 +274,6 @@ float occlusion_compute(vec3 N, vec3 vpos, float user_occlusion, vec4 rand, out
visibility = max(1e-3, visibility);
if ((int(aoSettings) & USE_BENT_NORMAL) != 0) {
/* The bent normal will show the facet look of the mesh. Try to minimize this. */
float mix_fac = visibility * visibility * visibility;
bent_normal = normalize(mix(bent_normal, vnor, mix_fac));
bent_normal = transform_direction(ViewMatrixInverse, bent_normal);
}
else {
@@ -276,10 +283,10 @@ float occlusion_compute(vec3 N, vec3 vpos, float user_occlusion, vec4 rand, out
/* Scale by user factor */
visibility = pow(visibility, aoFactor);
return min(visibility, user_occlusion);
return visibility;
}
#endif
bent_normal = N;
return user_occlusion;
return 1.0;
}

View File

@@ -1,6 +1,15 @@
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
vec3 diffuse_dominant_dir(vec3 N, vec3 vis_cone_dir, float vis_cone_aperture_cos)
{
/* TODO(fclem) revisit this. bent too much towards vis_cone_dir. */
vis_cone_aperture_cos *= sqr(vis_cone_aperture_cos);
N = mix(vis_cone_dir, N, vis_cone_aperture_cos);
return normalize(N);
}
vec3 specular_dominant_dir(vec3 N, vec3 V, float roughness)
{
vec3 R = -reflect(V, N);
@@ -15,6 +24,7 @@ float ior_from_f0(float f0)
return (-f - 1.0) / (f - 1.0);
}
/* Simplified form of F_eta(eta, 1.0). */
float f0_from_ior(float eta)
{
float A = (eta - 1.0) / (eta + 1.0);
@@ -47,30 +57,21 @@ float F_eta(float eta, float cos_theta)
* the refracted direction */
float c = abs(cos_theta);
float g = eta * eta - 1.0 + c * c;
float result;
if (g > 0.0) {
g = sqrt(g);
vec2 g_c = vec2(g) + vec2(c, -c);
float A = g_c.y / g_c.x;
A *= A;
g_c *= c;
float B = (g_c.y - 1.0) / (g_c.x + 1.0);
B *= B;
result = 0.5 * A * (1.0 + B);
float A = (g - c) / (g + c);
float B = (c * (g + c) - 1.0) / (c * (g - c) + 1.0);
return 0.5 * A * A * (1.0 + B * B);
}
else {
result = 1.0; /* TIR (no refracted component) */
}
return result;
/* Total internal reflections. */
return 1.0;
}
/* Fresnel color blend base on fresnel factor */
vec3 F_color_blend(float eta, float fresnel, vec3 f0_color)
{
float f0 = F_eta(eta, 1.0);
float fac = saturate((fresnel - f0) / max(1e-8, 1.0 - f0));
float f0 = f0_from_ior(eta);
float fac = saturate((fresnel - f0) / (1.0 - f0));
return mix(f0_color, vec3(1.0), fac);
}
@@ -79,7 +80,7 @@ vec3 F_brdf_single_scatter(vec3 f0, vec3 f90, vec2 lut)
{
/* Unreal specular matching : if specular color is below 2% intensity,
* treat as shadowning */
return saturate(50.0 * dot(f0, vec3(0.3, 0.6, 0.1))) * lut.y * abs(f90) + lut.x * f0;
return lut.y * f90 + lut.x * f0;
}
/* Multi-scattering brdf approximation from :
@@ -87,11 +88,7 @@ vec3 F_brdf_single_scatter(vec3 f0, vec3 f90, vec2 lut)
* by Carmelo J. Fdez-Agüera. */
vec3 F_brdf_multi_scatter(vec3 f0, vec3 f90, vec2 lut)
{
vec3 FssEss = F_brdf_single_scatter(f0, f90, lut);
/* Hack to avoid many more shader variations. */
if (f90.g < 0.0) {
return FssEss;
}
vec3 FssEss = lut.y * f90 + lut.x * f0;
float Ess = lut.x + lut.y;
float Ems = 1.0 - Ess;
@@ -102,8 +99,6 @@ vec3 F_brdf_multi_scatter(vec3 f0, vec3 f90, vec2 lut)
return FssEss + Fms * Ems;
}
#define F_brdf(f0, f90, lut) F_brdf_multi_scatter(f0, f90, lut)
/* GGX */
float D_ggx_opti(float NH, float a2)
{

View File

@@ -1,48 +1,57 @@
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
out vec4 FragColor;
uniform float sampleCount;
out vec2 FragColor;
void main()
{
vec3 N, T, B, V;
/* Make sure coordinates are covering the whole [0..1] range at texel center. */
float y = floor(gl_FragCoord.y) / (LUT_SIZE - 1);
float x = floor(gl_FragCoord.x) / (LUT_SIZE - 1);
float NV = (1.0 - (clamp(gl_FragCoord.y / LUT_SIZE, 1e-4, 0.9999)));
float sqrtRoughness = clamp(gl_FragCoord.x / LUT_SIZE, 1e-4, 0.9999);
float a = sqrtRoughness * sqrtRoughness;
float a2 = a * a;
float NV = clamp(1.0 - y * y, 1e-4, 0.9999);
float a = x * x;
float a2 = clamp(a * a, 1e-4, 0.9999);
N = vec3(0.0, 0.0, 1.0);
T = vec3(1.0, 0.0, 0.0);
B = vec3(0.0, 1.0, 0.0);
V = vec3(sqrt(1.0 - NV * NV), 0.0, NV);
setup_noise();
vec3 V = vec3(sqrt(1.0 - NV * NV), 0.0, NV);
/* Integrating BRDF */
float brdf_accum = 0.0;
float fresnel_accum = 0.0;
for (float i = 0; i < sampleCount; i++) {
vec3 H = sample_ggx(i, a2, N, T, B); /* Microfacet normal */
vec3 L = -reflect(V, H);
float NL = L.z;
for (float j = 0.0; j < sampleCount; j++) {
for (float i = 0.0; i < sampleCount; i++) {
vec3 Xi = (vec3(i, j, 0.0) + 0.5) / sampleCount;
Xi.yz = vec2(cos(Xi.y * M_2PI), sin(Xi.y * M_2PI));
if (NL > 0.0) {
float NH = max(H.z, 0.0);
float VH = max(dot(V, H), 0.0);
vec3 H = sample_ggx(Xi, a2); /* Microfacet normal */
vec3 L = -reflect(V, H);
float NL = L.z;
float G1_v = G1_Smith_GGX(NV, a2);
float G1_l = G1_Smith_GGX(NL, a2);
float G_smith = 4.0 * NV * NL / (G1_v * G1_l); /* See G1_Smith_GGX for explanations. */
if (NL > 0.0) {
float NH = max(H.z, 0.0);
float VH = max(dot(V, H), 0.0);
float brdf = (G_smith * VH) / (NH * NV);
float Fc = pow(1.0 - VH, 5.0);
float G1_v = G1_Smith_GGX(NV, a2);
float G1_l = G1_Smith_GGX(NL, a2);
float G_smith = 4.0 * NV * NL / (G1_v * G1_l); /* See G1_Smith_GGX for explanations. */
brdf_accum += (1.0 - Fc) * brdf;
fresnel_accum += Fc * brdf;
float brdf = (G_smith * VH) / (NH * NV);
/* Follow maximum specular value for principled bsdf. */
const float specular = 1.0;
const float eta = (2.0 / (1.0 - sqrt(0.08 * specular))) - 1.0;
float fresnel = F_eta(eta, VH);
float Fc = F_color_blend(eta, fresnel, vec3(0)).r;
brdf_accum += (1.0 - Fc) * brdf;
fresnel_accum += Fc * brdf;
}
}
}
brdf_accum /= sampleCount;
fresnel_accum /= sampleCount;
brdf_accum /= sampleCount * sampleCount;
fresnel_accum /= sampleCount * sampleCount;
FragColor = vec4(brdf_accum, fresnel_accum, 0.0, 1.0);
FragColor = vec2(brdf_accum, fresnel_accum);
}

View File

@@ -1,21 +1,7 @@
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl)
uniform sampler1D texHammersley;
uniform float sampleCount;
uniform float invSampleCount;
vec2 jitternoise = vec2(0.0);
#ifndef UTIL_TEX
# define UTIL_TEX
#endif /* UTIL_TEX */
void setup_noise(void)
{
jitternoise = texelfetch_noise_tex(gl_FragCoord.xy).rg; /* Global variable */
}
vec3 tangent_to_world(vec3 vector, vec3 N, vec3 T, vec3 B)
{
@@ -27,20 +13,11 @@ vec3 hammersley_3d(float i, float invsamplenbr)
{
vec3 Xi; /* Theta, cos(Phi), sin(Phi) */
Xi.x = i * invsamplenbr; /* i/samples */
Xi.x = fract(Xi.x + jitternoise.x);
int u = int(mod(i + jitternoise.y * HAMMERSLEY_SIZE, HAMMERSLEY_SIZE));
Xi.yz = texelFetch(texHammersley, u, 0).rg;
Xi.x = i * invsamplenbr;
Xi.yz = texelFetch(texHammersley, int(i), 0).rg;
return Xi;
}
vec3 hammersley_3d(float i)
{
return hammersley_3d(i, invSampleCount);
}
#endif
/* -------------- BSDFS -------------- */
@@ -75,16 +52,16 @@ vec3 sample_ggx(vec3 rand, float a2, vec3 N, vec3 T, vec3 B, out float NH)
}
#ifdef HAMMERSLEY_SIZE
vec3 sample_ggx(float nsample, float a2, vec3 N, vec3 T, vec3 B)
vec3 sample_ggx(float nsample, float inv_sample_count, float a2, vec3 N, vec3 T, vec3 B)
{
vec3 Xi = hammersley_3d(nsample);
vec3 Xi = hammersley_3d(nsample, inv_sample_count);
vec3 Ht = sample_ggx(Xi, a2);
return tangent_to_world(Ht, N, T, B);
}
vec3 sample_hemisphere(float nsample, vec3 N, vec3 T, vec3 B)
vec3 sample_hemisphere(float nsample, float inv_sample_count, vec3 N, vec3 T, vec3 B)
{
vec3 Xi = hammersley_3d(nsample);
vec3 Xi = hammersley_3d(nsample, inv_sample_count);
float z = Xi.x; /* cos theta */
float r = sqrt(max(0.0, 1.0f - z * z)); /* sin theta */
@@ -96,9 +73,9 @@ vec3 sample_hemisphere(float nsample, vec3 N, vec3 T, vec3 B)
return tangent_to_world(Ht, N, T, B);
}
vec3 sample_cone(float nsample, float angle, vec3 N, vec3 T, vec3 B)
vec3 sample_cone(float nsample, float inv_sample_count, float angle, vec3 N, vec3 T, vec3 B)
{
vec3 Xi = hammersley_3d(nsample);
vec3 Xi = hammersley_3d(nsample, inv_sample_count);
float z = cos(angle * Xi.x); /* cos theta */
float r = sqrt(max(0.0, 1.0f - z * z)); /* sin theta */

View File

@@ -1,62 +1,89 @@
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
uniform float a2;
uniform float sampleCount;
uniform float z;
out vec4 FragColor;
void main()
{
vec3 N, T, B, V;
float x = floor(gl_FragCoord.x) / (LUT_SIZE - 1.0);
float y = floor(gl_FragCoord.y) / (LUT_SIZE - 1.0);
float x = gl_FragCoord.x / LUT_SIZE;
float y = gl_FragCoord.y / LUT_SIZE;
/* There is little variation if ior > 1.0 so we
* maximize LUT precision for ior < 1.0 */
x = x * 1.1;
float ior = (x > 1.0) ? ior_from_f0((x - 1.0) * 10.0) : sqrt(x);
float NV = (1.0 - (clamp(y, 1e-4, 0.9999)));
float ior = clamp(sqrt(x), 0.05, 0.999);
/* ior is sin of critical angle. */
float critical_cos = sqrt(1.0 - saturate(ior * ior));
N = vec3(0.0, 0.0, 1.0);
T = vec3(1.0, 0.0, 0.0);
B = vec3(0.0, 1.0, 0.0);
V = vec3(sqrt(1.0 - NV * NV), 0.0, NV);
y = y * 2.0 - 1.0;
/* Maximize texture usage on both sides of the critical angle. */
y *= (y > 0.0) ? (1.0 - critical_cos) : critical_cos;
/* Center LUT around critical angle to avoid strange interpolation issues when the critical
* angle is changing. */
y += critical_cos;
float NV = clamp(y, 1e-4, 0.9999);
setup_noise();
float a = z * z;
float a2 = clamp(a * a, 1e-8, 0.9999);
vec3 V = vec3(sqrt(1.0 - NV * NV), 0.0, NV);
/* Integrating BTDF */
float btdf_accum = 0.0;
for (float i = 0.0; i < sampleCount; i++) {
vec3 H = sample_ggx(i, a2, N, T, B); /* Microfacet normal */
float fresnel_accum = 0.0;
for (float j = 0.0; j < sampleCount; j++) {
for (float i = 0.0; i < sampleCount; i++) {
vec3 Xi = (vec3(i, j, 0.0) + 0.5) / sampleCount;
Xi.yz = vec2(cos(Xi.y * M_2PI), sin(Xi.y * M_2PI));
float VH = dot(V, H);
/* Microfacet normal. */
vec3 H = sample_ggx(Xi, a2);
/* Check if there is total internal reflections. */
float c = abs(VH);
float g = ior * ior - 1.0 + c * c;
float VH = dot(V, H);
float eta = 1.0 / ior;
if (dot(H, V) < 0.0) {
H = -H;
eta = ior;
}
/* Check if there is total internal reflections. */
float fresnel = F_eta(ior, VH);
vec3 L = refract(-V, H, eta);
float NL = -dot(N, L);
fresnel_accum += fresnel;
if ((NL > 0.0) && (g > 0.0)) {
float LH = dot(L, H);
float eta = 1.0 / ior;
if (dot(H, V) < 0.0) {
H = -H;
eta = ior;
}
float G1_l = NL * 2.0 /
G1_Smith_GGX(NL, a2); /* Balancing the adjustments made in G1_Smith */
vec3 L = refract(-V, H, eta);
float NL = -L.z;
/* btdf = abs(VH*LH) * (ior*ior) * D * G(V) * G(L) / (Ht2 * NV)
* pdf = (VH * abs(LH)) * (ior*ior) * D * G(V) / (Ht2 * NV) */
float btdf = G1_l * abs(VH * LH) / (VH * abs(LH));
if ((NL > 0.0) && (fresnel < 0.999)) {
float LH = dot(L, H);
btdf_accum += btdf;
/* Balancing the adjustments made in G1_Smith. */
float G1_l = NL * 2.0 / G1_Smith_GGX(NL, a2);
/* btdf = abs(VH*LH) * (ior*ior) * D * G(V) * G(L) / (Ht2 * NV)
* pdf = (VH * abs(LH)) * (ior*ior) * D * G(V) / (Ht2 * NV) */
float btdf = G1_l * abs(VH * LH) / (VH * abs(LH));
btdf_accum += btdf;
}
}
}
btdf_accum /= sampleCount;
btdf_accum /= sampleCount * sampleCount;
fresnel_accum /= sampleCount * sampleCount;
FragColor = vec4(btdf_accum, 0.0, 0.0, 1.0);
if (z == 0.0) {
/* Perfect mirror. Increased precision because the roughness is clamped. */
fresnel_accum = F_eta(ior, NV);
}
if (x == 0.0) {
/* Special case. */
fresnel_accum = 1.0;
btdf_accum = 0.0;
}
/* There is place to put multiscater result (which is a little bit different still)
* and / or lobe fitting for better sampling of */
FragColor = vec4(btdf_accum, fresnel_accum, 0.0, 1.0);
}

View File

@@ -0,0 +1,85 @@
#pragma BLENDER_REQUIRE(lights_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
struct ClosureInputDiffuse {
vec3 N; /** Shading normal. */
vec3 albedo; /** Used for multibounce GTAO approximation. Not applied to final radiance. */
};
#define CLOSURE_INPUT_Diffuse_DEFAULT ClosureInputDiffuse(vec3(0.0), vec3(0.0))
struct ClosureEvalDiffuse {
vec3 probe_sampling_dir; /** Direction to sample probes from. */
float ambient_occlusion; /** Final occlusion for distant lighting. */
};
/* Stubs. */
#define ClosureOutputDiffuse ClosureOutput
#define closure_Diffuse_planar_eval(cl_in, cl_eval, cl_common, data, cl_out)
#define closure_Diffuse_cubemap_eval(cl_in, cl_eval, cl_common, data, cl_out)
ClosureEvalDiffuse closure_Diffuse_eval_init(inout ClosureInputDiffuse cl_in,
ClosureEvalCommon cl_common,
out ClosureOutputDiffuse cl_out)
{
cl_in.N = safe_normalize(cl_in.N);
cl_out.radiance = vec3(0.0);
ClosureEvalDiffuse cl_eval;
cl_eval.ambient_occlusion = diffuse_occlusion(
cl_in.N, cl_common.bent_normal, cl_common.occlusion, cl_in.albedo);
cl_eval.probe_sampling_dir = diffuse_dominant_dir(
cl_in.N, cl_common.bent_normal, cl_common.occlusion);
return cl_eval;
}
void closure_Diffuse_light_eval(ClosureInputDiffuse cl_in,
ClosureEvalDiffuse cl_eval,
ClosureEvalCommon cl_common,
ClosureLightData light,
inout ClosureOutputDiffuse cl_out)
{
float radiance = light_diffuse(light.data, cl_in.N, cl_common.V, light.L);
/* TODO(fclem) We could try to shadow lights that are shadowless with the ambient_occlusion
* factor here. */
cl_out.radiance += light.data.l_color * (light.vis * light.contact_shadow * radiance);
}
void closure_Diffuse_grid_eval(ClosureInputDiffuse cl_in,
ClosureEvalDiffuse cl_eval,
ClosureEvalCommon cl_common,
ClosureGridData grid,
inout ClosureOutputDiffuse cl_out)
{
vec3 probe_radiance = probe_evaluate_grid(
grid.data, cl_common.P, cl_eval.probe_sampling_dir, grid.local_pos);
cl_out.radiance += grid.attenuation * probe_radiance;
}
void closure_Diffuse_indirect_end(ClosureInputDiffuse cl_in,
ClosureEvalDiffuse cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputDiffuse cl_out)
{
/* If not enough light has been accumulated from probes, use the world specular cubemap
* to fill the remaining energy needed. */
if (cl_common.diffuse_accum > 0.0) {
vec3 probe_radiance = probe_evaluate_world_diff(cl_eval.probe_sampling_dir);
cl_out.radiance += cl_common.diffuse_accum * probe_radiance;
}
/* Apply occlusion on radiance before the light loop. */
cl_out.radiance *= cl_eval.ambient_occlusion;
}
void closure_Diffuse_eval_end(ClosureInputDiffuse cl_in,
ClosureEvalDiffuse cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputDiffuse cl_out)
{
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
cl_out.radiance = vec3(0.0);
return;
#endif
}

View File

@@ -0,0 +1,136 @@
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(lights_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
struct ClosureInputGlossy {
vec3 N; /** Shading normal. */
float roughness; /** Input roughness, not squared. */
};
#define CLOSURE_INPUT_Glossy_DEFAULT ClosureInputGlossy(vec3(0.0), 0.0)
struct ClosureEvalGlossy {
vec4 ltc_mat; /** LTC matrix values. */
float ltc_brdf_scale; /** LTC BRDF scaling. */
vec3 probe_sampling_dir; /** Direction to sample probes from. */
float spec_occlusion; /** Specular Occlusion. */
vec3 raytrace_radiance; /** Raytrace reflection to be accumulated after occlusion. */
};
/* Stubs. */
#define ClosureOutputGlossy ClosureOutput
#define closure_Glossy_grid_eval(cl_in, cl_eval, cl_common, data, cl_out)
#ifdef STEP_RESOLVE /* SSR */
/* Prototype. */
void raytrace_resolve(ClosureInputGlossy cl_in,
inout ClosureEvalGlossy cl_eval,
inout ClosureEvalCommon cl_common,
inout ClosureOutputGlossy cl_out);
#endif
ClosureEvalGlossy closure_Glossy_eval_init(inout ClosureInputGlossy cl_in,
inout ClosureEvalCommon cl_common,
out ClosureOutputGlossy cl_out)
{
cl_in.N = safe_normalize(cl_in.N);
cl_in.roughness = clamp(cl_in.roughness, 1e-8, 0.9999);
cl_out.radiance = vec3(0.0);
float NV = dot(cl_in.N, cl_common.V);
vec2 lut_uv = lut_coords(NV, cl_in.roughness);
ClosureEvalGlossy cl_eval;
cl_eval.ltc_mat = texture(utilTex, vec3(lut_uv, LTC_MAT_LAYER));
cl_eval.probe_sampling_dir = specular_dominant_dir(cl_in.N, cl_common.V, sqr(cl_in.roughness));
cl_eval.spec_occlusion = specular_occlusion(NV, cl_common.occlusion, cl_in.roughness);
cl_eval.raytrace_radiance = vec3(0.0);
#ifdef STEP_RESOLVE /* SSR */
raytrace_resolve(cl_in, cl_eval, cl_common, cl_out);
#endif
/* The brdf split sum LUT is applied after the radiance accumulation.
* Correct the LTC so that its energy is constant. */
/* TODO(fclem) Optimize this so that only one scale factor is stored. */
vec4 ltc_brdf = texture(utilTex, vec3(lut_uv, LTC_BRDF_LAYER)).barg;
vec2 split_sum_brdf = ltc_brdf.zw;
cl_eval.ltc_brdf_scale = (ltc_brdf.x + ltc_brdf.y) / (split_sum_brdf.x + split_sum_brdf.y);
return cl_eval;
}
void closure_Glossy_light_eval(ClosureInputGlossy cl_in,
ClosureEvalGlossy cl_eval,
ClosureEvalCommon cl_common,
ClosureLightData light,
inout ClosureOutputGlossy cl_out)
{
float radiance = light_specular(light.data, cl_eval.ltc_mat, cl_in.N, cl_common.V, light.L);
radiance *= cl_eval.ltc_brdf_scale;
cl_out.radiance += light.data.l_color *
(light.data.l_spec * light.vis * light.contact_shadow * radiance);
}
void closure_Glossy_planar_eval(ClosureInputGlossy cl_in,
ClosureEvalGlossy cl_eval,
inout ClosureEvalCommon cl_common,
ClosurePlanarData planar,
inout ClosureOutputGlossy cl_out)
{
#ifndef STEP_RESOLVE /* SSR already evaluates planar reflections. */
vec3 probe_radiance = probe_evaluate_planar(
planar.id, planar.data, cl_common.P, cl_in.N, cl_common.V, cl_in.roughness);
cl_out.radiance += planar.attenuation * probe_radiance;
#else
/* HACK: Fix an issue with planar reflections still being counted inside the specular
* accumulator. This only works because we only use one Glossy closure in the resolve pass. */
cl_common.specular_accum += planar.attenuation;
#endif
}
void closure_Glossy_cubemap_eval(ClosureInputGlossy cl_in,
ClosureEvalGlossy cl_eval,
ClosureEvalCommon cl_common,
ClosureCubemapData cube,
inout ClosureOutputGlossy cl_out)
{
vec3 probe_radiance = probe_evaluate_cube(
cube.id, cl_common.P, cl_eval.probe_sampling_dir, cl_in.roughness);
cl_out.radiance += cube.attenuation * probe_radiance;
}
void closure_Glossy_indirect_end(ClosureInputGlossy cl_in,
ClosureEvalGlossy cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputGlossy cl_out)
{
/* If not enough light has been accumulated from probes, use the world specular cubemap
* to fill the remaining energy needed. */
if (specToggle && cl_common.specular_accum > 0.0) {
vec3 probe_radiance = probe_evaluate_world_spec(cl_eval.probe_sampling_dir, cl_in.roughness);
cl_out.radiance += cl_common.specular_accum * probe_radiance;
}
/* Apply occlusion on distant lighting. */
cl_out.radiance *= cl_eval.spec_occlusion;
/* Apply Raytrace reflections after occlusion since they are direct, local reflections. */
cl_out.radiance += cl_eval.raytrace_radiance;
}
void closure_Glossy_eval_end(ClosureInputGlossy cl_in,
ClosureEvalGlossy cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputGlossy cl_out)
{
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
cl_out.radiance = vec3(0.0);
return;
#endif
if (!specToggle) {
cl_out.radiance = vec3(0.0);
}
}

View File

@@ -0,0 +1,316 @@
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(lights_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
/**
* Extensive use of Macros to be able to change the maximum amount of evaluated closure easily.
* NOTE: GLSL does not support variadic macros.
*
* Example
* // Declare the cl_eval function
* CLOSURE_EVAL_FUNCTION_DECLARE_3(name, Diffuse, Glossy, Refraction);
* // Declare the inputs & outputs
* CLOSURE_VARS_DECLARE(Diffuse, Glossy, Refraction);
* // Specify inputs
* in_Diffuse_0.N = N;
* ...
* // Call the cl_eval function
* CLOSURE_EVAL_FUNCTION_3(name, Diffuse, Glossy, Refraction);
* // Get the cl_out
* closure.radiance = out_Diffuse_0.radiance + out_Glossy_1.radiance + out_Refraction_2.radiance;
**/
#define CLOSURE_VARS_DECLARE(t0, t1, t2, t3) \
ClosureInputCommon in_common = CLOSURE_INPUT_COMMON_DEFAULT; \
ClosureInput##t0 in_##t0##_0 = CLOSURE_INPUT_##t0##_DEFAULT; \
ClosureInput##t1 in_##t1##_1 = CLOSURE_INPUT_##t1##_DEFAULT; \
ClosureInput##t2 in_##t2##_2 = CLOSURE_INPUT_##t2##_DEFAULT; \
ClosureInput##t3 in_##t3##_3 = CLOSURE_INPUT_##t3##_DEFAULT; \
ClosureOutput##t0 out_##t0##_0; \
ClosureOutput##t1 out_##t1##_1; \
ClosureOutput##t2 out_##t2##_2; \
ClosureOutput##t3 out_##t3##_3;
#define CLOSURE_EVAL_DECLARE(t0, t1, t2, t3) \
ClosureEvalCommon cl_common = closure_Common_eval_init(in_common); \
ClosureEval##t0 eval_##t0##_0 = closure_##t0##_eval_init(in_##t0##_0, cl_common, out_##t0##_0); \
ClosureEval##t1 eval_##t1##_1 = closure_##t1##_eval_init(in_##t1##_1, cl_common, out_##t1##_1); \
ClosureEval##t2 eval_##t2##_2 = closure_##t2##_eval_init(in_##t2##_2, cl_common, out_##t2##_2); \
ClosureEval##t3 eval_##t3##_3 = closure_##t3##_eval_init(in_##t3##_3, cl_common, out_##t3##_3);
#define CLOSURE_META_SUBROUTINE(subroutine, t0, t1, t2, t3) \
closure_##t0##_##subroutine(in_##t0##_0, eval_##t0##_0, cl_common, out_##t0##_0); \
closure_##t1##_##subroutine(in_##t1##_1, eval_##t1##_1, cl_common, out_##t1##_1); \
closure_##t2##_##subroutine(in_##t2##_2, eval_##t2##_2, cl_common, out_##t2##_2); \
closure_##t3##_##subroutine(in_##t3##_3, eval_##t3##_3, cl_common, out_##t3##_3);
#define CLOSURE_META_SUBROUTINE_DATA(subroutine, sub_data, t0, t1, t2, t3) \
closure_##t0##_##subroutine(in_##t0##_0, eval_##t0##_0, cl_common, sub_data, out_##t0##_0); \
closure_##t1##_##subroutine(in_##t1##_1, eval_##t1##_1, cl_common, sub_data, out_##t1##_1); \
closure_##t2##_##subroutine(in_##t2##_2, eval_##t2##_2, cl_common, sub_data, out_##t2##_2); \
closure_##t3##_##subroutine(in_##t3##_3, eval_##t3##_3, cl_common, sub_data, out_##t3##_3);
/* Inputs are inout so that callers can get the final inputs used for evaluation. */
#define CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3) \
void closure_##name##_eval(ClosureInputCommon in_common, \
inout ClosureInput##t0 in_##t0##_0, \
inout ClosureInput##t1 in_##t1##_1, \
inout ClosureInput##t2 in_##t2##_2, \
inout ClosureInput##t3 in_##t3##_3, \
out ClosureOutput##t0 out_##t0##_0, \
out ClosureOutput##t1 out_##t1##_1, \
out ClosureOutput##t2 out_##t2##_2, \
out ClosureOutput##t3 out_##t3##_3) \
{ \
CLOSURE_EVAL_DECLARE(t0, t1, t2, t3); \
\
for (int i = 0; cl_common.specular_accum > 0.0 && i < prbNumPlanar && i < MAX_PLANAR; i++) { \
ClosurePlanarData planar = closure_planar_eval_init(i, cl_common); \
if (planar.attenuation > 1e-8) { \
CLOSURE_META_SUBROUTINE_DATA(planar_eval, planar, t0, t1, t2, t3); \
} \
} \
\
/* Starts at 1 because 0 is world cubemap. */ \
for (int i = 1; cl_common.specular_accum > 0.0 && i < prbNumRenderCube && i < MAX_PROBE; \
i++) { \
ClosureCubemapData cube = closure_cubemap_eval_init(i, cl_common); \
if (cube.attenuation > 1e-8) { \
CLOSURE_META_SUBROUTINE_DATA(cubemap_eval, cube, t0, t1, t2, t3); \
} \
} \
\
/* Starts at 1 because 0 is world irradiance. */ \
for (int i = 1; cl_common.diffuse_accum > 0.0 && i < prbNumRenderGrid && i < MAX_GRID; i++) { \
ClosureGridData grid = closure_grid_eval_init(i, cl_common); \
if (grid.attenuation > 1e-8) { \
CLOSURE_META_SUBROUTINE_DATA(grid_eval, grid, t0, t1, t2, t3); \
} \
} \
\
CLOSURE_META_SUBROUTINE(indirect_end, t0, t1, t2, t3); \
\
for (int i = 0; i < laNumLight && i < MAX_LIGHT; i++) { \
ClosureLightData light = closure_light_eval_init(cl_common, i); \
if (light.vis > 1e-8) { \
CLOSURE_META_SUBROUTINE_DATA(light_eval, light, t0, t1, t2, t3); \
} \
} \
\
CLOSURE_META_SUBROUTINE(eval_end, t0, t1, t2, t3); \
}
#define CLOSURE_EVAL_FUNCTION(name, t0, t1, t2, t3) \
closure_##name##_eval(in_common, \
in_##t0##_0, \
in_##t1##_1, \
in_##t2##_2, \
in_##t3##_3, \
out_##t0##_0, \
out_##t1##_1, \
out_##t2##_2, \
out_##t3##_3)
#define CLOSURE_EVAL_FUNCTION_DECLARE_1(name, t0) \
CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, Dummy, Dummy, Dummy)
#define CLOSURE_EVAL_FUNCTION_DECLARE_2(name, t0, t1) \
CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, Dummy, Dummy)
#define CLOSURE_EVAL_FUNCTION_DECLARE_3(name, t0, t1, t2) \
CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, Dummy)
#define CLOSURE_EVAL_FUNCTION_DECLARE_4(name, t0, t1, t2, t3) \
CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3)
#define CLOSURE_VARS_DECLARE_1(t0) CLOSURE_VARS_DECLARE(t0, Dummy, Dummy, Dummy)
#define CLOSURE_VARS_DECLARE_2(t0, t1) CLOSURE_VARS_DECLARE(t0, t1, Dummy, Dummy)
#define CLOSURE_VARS_DECLARE_3(t0, t1, t2) CLOSURE_VARS_DECLARE(t0, t1, t2, Dummy)
#define CLOSURE_VARS_DECLARE_4(t0, t1, t2, t3) CLOSURE_VARS_DECLARE(t0, t1, t2, t3)
#define CLOSURE_EVAL_FUNCTION_1(name, t0) CLOSURE_EVAL_FUNCTION(name, t0, Dummy, Dummy, Dummy)
#define CLOSURE_EVAL_FUNCTION_2(name, t0, t1) CLOSURE_EVAL_FUNCTION(name, t0, t1, Dummy, Dummy)
#define CLOSURE_EVAL_FUNCTION_3(name, t0, t1, t2) CLOSURE_EVAL_FUNCTION(name, t0, t1, t2, Dummy)
#define CLOSURE_EVAL_FUNCTION_4(name, t0, t1, t2, t3) CLOSURE_EVAL_FUNCTION(name, t0, t1, t2, t3)
/* -------------------------------------------------------------------- */
/** \name Dummy Closure
*
* Dummy closure type that will be optimized out by the compiler.
* \{ */
#define ClosureInputDummy ClosureOutput
#define ClosureOutputDummy ClosureOutput
#define ClosureEvalDummy ClosureOutput
#define CLOSURE_EVAL_DUMMY ClosureOutput(vec3(0))
#define CLOSURE_INPUT_Dummy_DEFAULT CLOSURE_EVAL_DUMMY
#define closure_Dummy_eval_init(cl_in, cl_common, cl_out) CLOSURE_EVAL_DUMMY
#define closure_Dummy_planar_eval(cl_in, cl_eval, cl_common, data, cl_out)
#define closure_Dummy_cubemap_eval(cl_in, cl_eval, cl_common, data, cl_out)
#define closure_Dummy_grid_eval(cl_in, cl_eval, cl_common, data, cl_out)
#define closure_Dummy_indirect_end(cl_in, cl_eval, cl_common, cl_out)
#define closure_Dummy_light_eval(cl_in, cl_eval, cl_common, data, cl_out)
#define closure_Dummy_eval_end(cl_in, cl_eval, cl_common, cl_out)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Common cl_eval data
*
* Eval data not dependant on input parameters. All might not be used but unused ones
* will be optimized out.
* \{ */
struct ClosureInputCommon {
/** Custom occlusion value set by the user. */
float occlusion;
};
#define CLOSURE_INPUT_COMMON_DEFAULT ClosureInputCommon(1.0)
struct ClosureEvalCommon {
/** View vector. */
vec3 V;
/** Surface position. */
vec3 P;
/** Normal vector, always facing camera. */
vec3 N;
/** Normal vector, always facing camera. (viewspace) */
vec3 vN;
/** Surface position. (viewspace) */
vec3 vP;
/** Geometric normal, always facing camera. (viewspace) */
vec3 vNg;
/** Random numbers. 3 random sequences. zw is a random point on a circle. */
vec4 rand;
/** Final occlusion factor. Mix of the user occlusion and SSAO. */
float occlusion;
/** Least occluded direction in the hemisphere. */
vec3 bent_normal;
/** Specular probe accumulator. Shared between planar and cubemap probe. */
float specular_accum;
/** Diffuse probe accumulator. */
float diffuse_accum;
/** Viewspace depth to start raytracing from. */
float tracing_depth;
};
/* Common cl_out struct used by most closures. */
struct ClosureOutput {
vec3 radiance;
};
ClosureEvalCommon closure_Common_eval_init(ClosureInputCommon cl_in)
{
ClosureEvalCommon cl_eval;
cl_eval.rand = texelfetch_noise_tex(gl_FragCoord.xy);
cl_eval.V = cameraVec;
cl_eval.P = worldPosition;
cl_eval.N = safe_normalize(gl_FrontFacing ? worldNormal : -worldNormal);
cl_eval.vN = safe_normalize(gl_FrontFacing ? viewNormal : -viewNormal);
cl_eval.vP = viewPosition;
cl_eval.vNg = safe_normalize(cross(dFdx(viewPosition), dFdy(viewPosition)));
/* TODO(fclem) See if we can avoid this complicated setup. */
cl_eval.tracing_depth = gl_FragCoord.z;
/* Constant bias (due to depth buffer precision) */
/* Magic numbers for 24bits of precision.
* From http://terathon.com/gdc07_lengyel.pdf (slide 26) */
cl_eval.tracing_depth -= mix(2.4e-7, 4.8e-7, gl_FragCoord.z);
/* Convert to view Z. */
cl_eval.tracing_depth = get_view_z_from_depth(cl_eval.tracing_depth);
/* TODO(fclem) Do occlusion evaluation per Closure using shading normal. */
cl_eval.occlusion = min(
cl_in.occlusion,
occlusion_compute(cl_eval.N, cl_eval.vP, cl_eval.rand, cl_eval.bent_normal));
cl_eval.specular_accum = 1.0;
cl_eval.diffuse_accum = 1.0;
return cl_eval;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Loop data
*
* Loop datas are conveniently packed into struct to make it future proof.
* \{ */
struct ClosureLightData {
LightData data; /** Light Data. */
vec4 L; /** Non-Normalized Light Vector (surface to light) with length in W component. */
float vis; /** Light visibility. */
float contact_shadow; /** Result of contact shadow tracing. */
};
ClosureLightData closure_light_eval_init(ClosureEvalCommon cl_common, int light_id)
{
ClosureLightData light;
light.data = lights_data[light_id];
light.L.xyz = light.data.l_position - cl_common.P;
light.L.w = length(light.L.xyz);
light.vis = light_visibility(light.data, cl_common.P, light.L);
light.contact_shadow = light_contact_shadows(light.data,
cl_common.P,
cl_common.vP,
cl_common.tracing_depth,
cl_common.vNg,
cl_common.rand.x,
light.vis);
return light;
}
struct ClosureCubemapData {
int id; /** Probe id. */
float attenuation; /** Attenuation. */
};
ClosureCubemapData closure_cubemap_eval_init(int cube_id, inout ClosureEvalCommon cl_common)
{
ClosureCubemapData cube;
cube.id = cube_id;
cube.attenuation = probe_attenuation_cube(cube_id, cl_common.P);
cube.attenuation = min(cube.attenuation, cl_common.specular_accum);
cl_common.specular_accum -= cube.attenuation;
return cube;
}
struct ClosurePlanarData {
int id; /** Probe id. */
PlanarData data; /** planars_data[id]. */
float attenuation; /** Attenuation. */
};
ClosurePlanarData closure_planar_eval_init(int planar_id, inout ClosureEvalCommon cl_common)
{
ClosurePlanarData planar;
planar.id = planar_id;
planar.data = planars_data[planar_id];
planar.attenuation = probe_attenuation_planar(planar.data, cl_common.P, cl_common.N, 0.0);
planar.attenuation = min(planar.attenuation, cl_common.specular_accum);
cl_common.specular_accum -= planar.attenuation;
return planar;
}
struct ClosureGridData {
int id; /** Grid id. */
GridData data; /** grids_data[id] */
float attenuation; /** Attenuation. */
vec3 local_pos; /** Local position inside the grid. */
};
ClosureGridData closure_grid_eval_init(int id, inout ClosureEvalCommon cl_common)
{
ClosureGridData grid;
grid.id = id;
grid.data = grids_data[id];
grid.attenuation = probe_attenuation_grid(grid.data, cl_common.P, grid.local_pos);
grid.attenuation = min(grid.attenuation, cl_common.diffuse_accum);
cl_common.diffuse_accum -= grid.attenuation;
return grid;
}
/** \} */

View File

@@ -0,0 +1,128 @@
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(lights_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
#pragma BLENDER_REQUIRE(ssr_lib.glsl)
struct ClosureInputRefraction {
vec3 N; /** Shading normal. */
float roughness; /** Input roughness, not squared. */
float ior; /** Index of refraction ratio. */
};
#define CLOSURE_INPUT_Refraction_DEFAULT ClosureInputRefraction(vec3(0.0), 0.0, 0.0)
struct ClosureEvalRefraction {
vec3 P; /** LTC matrix values. */
vec3 ltc_brdf; /** LTC BRDF values. */
vec3 probe_sampling_dir; /** Direction to sample probes from. */
float probes_weight; /** Factor to apply to probe radiance. */
};
/* Stubs. */
#define ClosureOutputRefraction ClosureOutput
#define closure_Refraction_grid_eval(cl_in, cl_eval, cl_common, data, cl_out)
ClosureEvalRefraction closure_Refraction_eval_init(inout ClosureInputRefraction cl_in,
ClosureEvalCommon cl_common,
out ClosureOutputRefraction cl_out)
{
cl_in.N = safe_normalize(cl_in.N);
cl_in.roughness = clamp(cl_in.roughness, 1e-8, 0.9999);
cl_in.ior = max(cl_in.ior, 1e-5);
cl_out.radiance = vec3(0.0);
ClosureEvalRefraction cl_eval;
vec3 cl_V;
float eval_ior;
/* Refract the view vector using the depth heuristic.
* Then later Refract a second time the already refracted
* ray using the inverse ior. */
if (refractionDepth > 0.0) {
eval_ior = 1.0 / cl_in.ior;
cl_V = -refract(-cl_common.V, cl_in.N, eval_ior);
vec3 plane_pos = cl_common.P - cl_in.N * refractionDepth;
cl_eval.P = line_plane_intersect(cl_common.P, cl_V, plane_pos, cl_in.N);
}
else {
eval_ior = cl_in.ior;
cl_V = cl_common.V;
cl_eval.P = cl_common.P;
}
cl_eval.probe_sampling_dir = refraction_dominant_dir(cl_in.N, cl_V, cl_in.roughness, eval_ior);
cl_eval.probes_weight = 1.0;
#ifdef USE_REFRACTION
if (ssrefractToggle && cl_in.roughness < ssrMaxRoughness + 0.2) {
/* Find approximated position of the 2nd refraction event. */
vec3 vP = (refractionDepth > 0.0) ? transform_point(ViewMatrix, cl_eval.P) : cl_common.vP;
vec4 ssr_output = screen_space_refraction(
vP, cl_in.N, cl_V, eval_ior, sqr(cl_in.roughness), cl_common.rand);
ssr_output.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, cl_in.roughness);
cl_out.radiance += ssr_output.rgb * ssr_output.a;
cl_eval.probes_weight -= ssr_output.a;
}
#endif
return cl_eval;
}
void closure_Refraction_light_eval(ClosureInputRefraction cl_in,
ClosureEvalRefraction cl_eval,
ClosureEvalCommon cl_common,
ClosureLightData light,
inout ClosureOutputRefraction cl_out)
{
/* Not implemented yet. */
}
void closure_Refraction_planar_eval(ClosureInputRefraction cl_in,
ClosureEvalRefraction cl_eval,
ClosureEvalCommon cl_common,
ClosurePlanarData planar,
inout ClosureOutputRefraction cl_out)
{
/* Not implemented yet. */
}
void closure_Refraction_cubemap_eval(ClosureInputRefraction cl_in,
ClosureEvalRefraction cl_eval,
ClosureEvalCommon cl_common,
ClosureCubemapData cube,
inout ClosureOutputRefraction cl_out)
{
vec3 probe_radiance = probe_evaluate_cube(
cube.id, cl_eval.P, cl_eval.probe_sampling_dir, sqr(cl_in.roughness));
cl_out.radiance += (cube.attenuation * cl_eval.probes_weight) * probe_radiance;
}
void closure_Refraction_indirect_end(ClosureInputRefraction cl_in,
ClosureEvalRefraction cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputRefraction cl_out)
{
/* If not enough light has been accumulated from probes, use the world specular cubemap
* to fill the remaining energy needed. */
if (specToggle && cl_common.specular_accum > 0.0) {
vec3 probe_radiance = probe_evaluate_world_spec(cl_eval.probe_sampling_dir,
sqr(cl_in.roughness));
cl_out.radiance += (cl_common.specular_accum * cl_eval.probes_weight) * probe_radiance;
}
}
void closure_Refraction_eval_end(ClosureInputRefraction cl_in,
ClosureEvalRefraction cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputRefraction cl_out)
{
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
cl_out.radiance = vec3(0.0);
return;
#endif
if (!specToggle) {
cl_out.radiance = vec3(0.0);
}
}

View File

@@ -0,0 +1,71 @@
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(lights_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
struct ClosureInputTranslucent {
vec3 N; /** Shading normal. */
};
#define CLOSURE_INPUT_Translucent_DEFAULT ClosureInputTranslucent(vec3(0.0))
/* Stubs. */
#define ClosureEvalTranslucent ClosureEvalDummy
#define ClosureOutputTranslucent ClosureOutput
#define closure_Translucent_planar_eval(cl_in, cl_eval, cl_common, data, cl_out)
#define closure_Translucent_cubemap_eval(cl_in, cl_eval, cl_common, data, cl_out)
ClosureEvalTranslucent closure_Translucent_eval_init(inout ClosureInputTranslucent cl_in,
ClosureEvalCommon cl_common,
out ClosureOutputTranslucent cl_out)
{
cl_in.N = safe_normalize(cl_in.N);
cl_out.radiance = vec3(0.0);
return CLOSURE_EVAL_DUMMY;
}
void closure_Translucent_light_eval(ClosureInputTranslucent cl_in,
ClosureEvalTranslucent cl_eval,
ClosureEvalCommon cl_common,
ClosureLightData light,
inout ClosureOutputTranslucent cl_out)
{
float radiance = light_diffuse(light.data, cl_in.N, cl_common.V, light.L);
cl_out.radiance += light.data.l_color * (light.vis * radiance);
}
void closure_Translucent_grid_eval(ClosureInputTranslucent cl_in,
ClosureEvalTranslucent cl_eval,
ClosureEvalCommon cl_common,
ClosureGridData grid,
inout ClosureOutputTranslucent cl_out)
{
vec3 probe_radiance = probe_evaluate_grid(grid.data, cl_common.P, cl_in.N, grid.local_pos);
cl_out.radiance += grid.attenuation * probe_radiance;
}
void closure_Translucent_indirect_end(ClosureInputTranslucent cl_in,
ClosureEvalTranslucent cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputTranslucent cl_out)
{
/* If not enough light has been accumulated from probes, use the world specular cubemap
* to fill the remaining energy needed. */
if (cl_common.diffuse_accum > 0.0) {
vec3 probe_radiance = probe_evaluate_world_diff(cl_in.N);
cl_out.radiance += cl_common.diffuse_accum * probe_radiance;
}
}
void closure_Translucent_eval_end(ClosureInputTranslucent cl_in,
ClosureEvalTranslucent cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputTranslucent cl_out)
{
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
cl_out.radiance = vec3(0.0);
return;
#endif
}

View File

@@ -1,545 +0,0 @@
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
#pragma BLENDER_REQUIRE(ssr_lib.glsl)
/**
* AUTO CONFIG
* We include the file multiple times each time with a different configuration.
* This leads to a lot of deadcode. Better idea would be to only generate the one needed.
*/
#if !defined(SURFACE_DEFAULT)
# define SURFACE_DEFAULT
# define CLOSURE_NAME eevee_closure_default
# define CLOSURE_DIFFUSE
# define CLOSURE_GLOSSY
#endif /* SURFACE_DEFAULT */
#if !defined(SURFACE_DEFAULT_CLEARCOAT) && !defined(CLOSURE_NAME)
# define SURFACE_DEFAULT_CLEARCOAT
# define CLOSURE_NAME eevee_closure_default_clearcoat
# define CLOSURE_DIFFUSE
# define CLOSURE_GLOSSY
# define CLOSURE_CLEARCOAT
#endif /* SURFACE_DEFAULT_CLEARCOAT */
#if !defined(SURFACE_PRINCIPLED) && !defined(CLOSURE_NAME)
# define SURFACE_PRINCIPLED
# define CLOSURE_NAME eevee_closure_principled
# define CLOSURE_DIFFUSE
# define CLOSURE_GLOSSY
# define CLOSURE_CLEARCOAT
# define CLOSURE_REFRACTION
# define CLOSURE_SUBSURFACE
#endif /* SURFACE_PRINCIPLED */
#if !defined(SURFACE_CLEARCOAT) && !defined(CLOSURE_NAME)
# define SURFACE_CLEARCOAT
# define CLOSURE_NAME eevee_closure_clearcoat
# define CLOSURE_GLOSSY
# define CLOSURE_CLEARCOAT
#endif /* SURFACE_CLEARCOAT */
#if !defined(SURFACE_DIFFUSE) && !defined(CLOSURE_NAME)
# define SURFACE_DIFFUSE
# define CLOSURE_NAME eevee_closure_diffuse
# define CLOSURE_DIFFUSE
#endif /* SURFACE_DIFFUSE */
#if !defined(SURFACE_SUBSURFACE) && !defined(CLOSURE_NAME)
# define SURFACE_SUBSURFACE
# define CLOSURE_NAME eevee_closure_subsurface
# define CLOSURE_DIFFUSE
# define CLOSURE_SUBSURFACE
#endif /* SURFACE_SUBSURFACE */
#if !defined(SURFACE_SKIN) && !defined(CLOSURE_NAME)
# define SURFACE_SKIN
# define CLOSURE_NAME eevee_closure_skin
# define CLOSURE_DIFFUSE
# define CLOSURE_SUBSURFACE
# define CLOSURE_GLOSSY
#endif /* SURFACE_SKIN */
#if !defined(SURFACE_GLOSSY) && !defined(CLOSURE_NAME)
# define SURFACE_GLOSSY
# define CLOSURE_NAME eevee_closure_glossy
# define CLOSURE_GLOSSY
#endif /* SURFACE_GLOSSY */
#if !defined(SURFACE_REFRACT) && !defined(CLOSURE_NAME)
# define SURFACE_REFRACT
# define CLOSURE_NAME eevee_closure_refraction
# define CLOSURE_REFRACTION
#endif /* SURFACE_REFRACT */
#if !defined(SURFACE_GLASS) && !defined(CLOSURE_NAME)
# define SURFACE_GLASS
# define CLOSURE_NAME eevee_closure_glass
# define CLOSURE_GLOSSY
# define CLOSURE_REFRACTION
#endif /* SURFACE_GLASS */
/* Safety : CLOSURE_CLEARCOAT implies CLOSURE_GLOSSY */
#ifdef CLOSURE_CLEARCOAT
# ifndef CLOSURE_GLOSSY
# define CLOSURE_GLOSSY
# endif
#endif /* CLOSURE_CLEARCOAT */
void CLOSURE_NAME(vec3 N
#ifdef CLOSURE_DIFFUSE
,
vec3 albedo
#endif
#ifdef CLOSURE_GLOSSY
,
vec3 f0,
vec3 f90,
int ssr_id
#endif
#if defined(CLOSURE_GLOSSY) || defined(CLOSURE_REFRACTION)
,
float roughness
#endif
#ifdef CLOSURE_CLEARCOAT
,
vec3 C_N,
float C_intensity,
float C_roughness
#endif
#if defined(CLOSURE_GLOSSY) || defined(CLOSURE_DIFFUSE)
,
float ao
#endif
#ifdef CLOSURE_SUBSURFACE
,
float sss_scale
#endif
#ifdef CLOSURE_REFRACTION
,
float ior
#endif
,
const bool use_contact_shadows
#ifdef CLOSURE_DIFFUSE
,
out vec3 out_diff
#endif
#ifdef CLOSURE_GLOSSY
,
out vec3 out_spec
#endif
#ifdef CLOSURE_REFRACTION
,
out vec3 out_refr
#endif
#ifdef CLOSURE_GLOSSY
,
out vec3 ssr_spec
#endif
)
{
#ifdef CLOSURE_DIFFUSE
out_diff = vec3(0.0);
#endif
#ifdef CLOSURE_GLOSSY
out_spec = vec3(0.0);
#endif
#ifdef CLOSURE_REFRACTION
out_refr = vec3(0.0);
#endif
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
return;
#else
/* Zero length vectors cause issues, see: T51979. */
float len = length(N);
if (isnan(len)) {
return;
}
N /= len;
# ifdef CLOSURE_CLEARCOAT
len = length(C_N);
if (isnan(len)) {
return;
}
C_N /= len;
# endif
# if defined(CLOSURE_GLOSSY) || defined(CLOSURE_REFRACTION)
roughness = clamp(roughness, 1e-8, 0.9999);
float roughnessSquared = roughness * roughness;
# endif
# ifdef CLOSURE_CLEARCOAT
C_roughness = clamp(C_roughness, 1e-8, 0.9999);
float C_roughnessSquared = C_roughness * C_roughness;
# endif
vec3 V = cameraVec;
vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
/* ---------------------------------------------------------------- */
/* -------------------- SCENE LIGHTS LIGHTING --------------------- */
/* ---------------------------------------------------------------- */
# ifdef CLOSURE_GLOSSY
vec2 lut_uv = lut_coords_ltc(dot(N, V), roughness);
vec4 ltc_mat = texture(utilTex, vec3(lut_uv, 0.0)).rgba;
# endif
# ifdef CLOSURE_CLEARCOAT
vec2 lut_uv_clear = lut_coords_ltc(dot(C_N, V), C_roughness);
vec4 ltc_mat_clear = texture(utilTex, vec3(lut_uv_clear, 0.0)).rgba;
vec3 out_spec_clear = vec3(0.0);
# endif
float tracing_depth = gl_FragCoord.z;
/* Constant bias (due to depth buffer precision) */
/* Magic numbers for 24bits of precision.
* From http://terathon.com/gdc07_lengyel.pdf (slide 26) */
tracing_depth -= mix(2.4e-7, 4.8e-7, gl_FragCoord.z);
/* Convert to view Z. */
tracing_depth = get_view_z_from_depth(tracing_depth);
vec3 true_normal = normalize(cross(dFdx(viewPosition), dFdy(viewPosition)));
for (int i = 0; i < MAX_LIGHT && i < laNumLight; i++) {
LightData ld = lights_data[i];
vec4 l_vector; /* Non-Normalized Light Vector with length in last component. */
l_vector.xyz = ld.l_position - worldPosition;
l_vector.w = length(l_vector.xyz);
float l_vis = light_visibility(ld,
worldPosition,
viewPosition,
tracing_depth,
true_normal,
rand.x,
use_contact_shadows,
l_vector);
if (l_vis < 1e-8) {
continue;
}
vec3 l_color_vis = ld.l_color * l_vis;
# ifdef CLOSURE_DIFFUSE
out_diff += l_color_vis * light_diffuse(ld, N, V, l_vector);
# endif
# ifdef CLOSURE_GLOSSY
out_spec += l_color_vis * light_specular(ld, ltc_mat, N, V, l_vector) * ld.l_spec;
# endif
# ifdef CLOSURE_CLEARCOAT
out_spec_clear += l_color_vis * light_specular(ld, ltc_mat_clear, C_N, V, l_vector) *
ld.l_spec;
# endif
}
# ifdef CLOSURE_GLOSSY
vec2 brdf_lut_lights = texture(utilTex, vec3(lut_uv, 1.0)).ba;
out_spec *= F_brdf(f0, f90, brdf_lut_lights.xy);
# endif
# ifdef CLOSURE_CLEARCOAT
vec2 brdf_lut_lights_clear = texture(utilTex, vec3(lut_uv_clear, 1.0)).ba;
out_spec_clear *= F_brdf(vec3(0.04), vec3(1.0), brdf_lut_lights_clear.xy);
out_spec += out_spec_clear * C_intensity;
# endif
/* ---------------------------------------------------------------- */
/* ---------------- SPECULAR ENVIRONMENT LIGHTING ----------------- */
/* ---------------------------------------------------------------- */
/* Accumulate incoming light from all sources until accumulator is full. Then apply Occlusion and
* BRDF. */
# ifdef CLOSURE_GLOSSY
vec4 spec_accum = vec4(0.0);
# endif
# ifdef CLOSURE_CLEARCOAT
vec4 C_spec_accum = vec4(0.0);
# endif
# ifdef CLOSURE_REFRACTION
vec4 refr_accum = vec4(0.0);
# endif
# ifdef CLOSURE_GLOSSY
/* ---------------------------- */
/* Planar Reflections */
/* ---------------------------- */
for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar && spec_accum.a < 0.999; i++) {
PlanarData pd = planars_data[i];
/* Fade on geometric normal. */
float fade = probe_attenuation_planar(
pd, worldPosition, (gl_FrontFacing) ? worldNormal : -worldNormal, roughness);
if (fade > 0.0) {
if (!(ssrToggle && ssr_id == outputSsrId)) {
vec3 spec = probe_evaluate_planar(float(i), pd, worldPosition, N, V, roughness, fade);
accumulate_light(spec, fade, spec_accum);
}
# ifdef CLOSURE_CLEARCOAT
vec3 C_spec = probe_evaluate_planar(float(i), pd, worldPosition, C_N, V, C_roughness, fade);
accumulate_light(C_spec, fade, C_spec_accum);
# endif
}
}
# endif
# ifdef CLOSURE_GLOSSY
vec3 spec_dir = specular_dominant_dir(N, V, roughnessSquared);
# endif
# ifdef CLOSURE_CLEARCOAT
vec3 C_spec_dir = specular_dominant_dir(C_N, V, C_roughnessSquared);
# endif
# ifdef CLOSURE_REFRACTION
/* Refract the view vector using the depth heuristic.
* Then later Refract a second time the already refracted
* ray using the inverse ior. */
float final_ior = (refractionDepth > 0.0) ? 1.0 / ior : ior;
vec3 refr_V = (refractionDepth > 0.0) ? -refract(-V, N, final_ior) : V;
vec3 refr_pos = (refractionDepth > 0.0) ?
line_plane_intersect(
worldPosition, refr_V, worldPosition - N * refractionDepth, N) :
worldPosition;
vec3 refr_dir = refraction_dominant_dir(N, refr_V, roughness, final_ior);
# endif
# ifdef CLOSURE_REFRACTION
/* ---------------------------- */
/* Screen Space Refraction */
/* ---------------------------- */
# ifdef USE_REFRACTION
if (ssrefractToggle && roughness < ssrMaxRoughness + 0.2) {
/* Find approximated position of the 2nd refraction event. */
vec3 refr_vpos = (refractionDepth > 0.0) ? transform_point(ViewMatrix, refr_pos) :
viewPosition;
vec4 trans = screen_space_refraction(refr_vpos, N, refr_V, final_ior, roughnessSquared, rand);
trans.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, roughness);
accumulate_light(trans.rgb, trans.a, refr_accum);
}
# endif
# endif
/* ---------------------------- */
/* Specular probes */
/* ---------------------------- */
# if defined(CLOSURE_GLOSSY) || defined(CLOSURE_REFRACTION)
# if defined(CLOSURE_GLOSSY) && defined(CLOSURE_REFRACTION)
# define GLASS_ACCUM 1
# define ACCUM min(refr_accum.a, spec_accum.a)
# elif defined(CLOSURE_REFRACTION)
# define GLASS_ACCUM 0
# define ACCUM refr_accum.a
# else
# define GLASS_ACCUM 0
# define ACCUM spec_accum.a
# endif
/* Starts at 1 because 0 is world probe */
for (int i = 1; ACCUM < 0.999 && i < prbNumRenderCube && i < MAX_PROBE; i++) {
float fade = probe_attenuation_cube(i, worldPosition);
if (fade > 0.0) {
# if GLASS_ACCUM
if (spec_accum.a < 0.999) {
# endif
# ifdef CLOSURE_GLOSSY
if (!(ssrToggle && ssr_id == outputSsrId)) {
vec3 spec = probe_evaluate_cube(i, worldPosition, spec_dir, roughness);
accumulate_light(spec, fade, spec_accum);
}
# endif
# ifdef CLOSURE_CLEARCOAT
vec3 C_spec = probe_evaluate_cube(i, worldPosition, C_spec_dir, C_roughness);
accumulate_light(C_spec, fade, C_spec_accum);
# endif
# if GLASS_ACCUM
}
# endif
# if GLASS_ACCUM
if (refr_accum.a < 0.999) {
# endif
# ifdef CLOSURE_REFRACTION
vec3 trans = probe_evaluate_cube(i, refr_pos, refr_dir, roughnessSquared);
accumulate_light(trans, fade, refr_accum);
# endif
# if GLASS_ACCUM
}
# endif
}
}
# undef GLASS_ACCUM
# undef ACCUM
/* ---------------------------- */
/* World Probe */
/* ---------------------------- */
# ifdef CLOSURE_GLOSSY
if (spec_accum.a < 0.999) {
if (!(ssrToggle && ssr_id == outputSsrId)) {
vec3 spec = probe_evaluate_world_spec(spec_dir, roughness);
accumulate_light(spec, 1.0, spec_accum);
}
# ifdef CLOSURE_CLEARCOAT
vec3 C_spec = probe_evaluate_world_spec(C_spec_dir, C_roughness);
accumulate_light(C_spec, 1.0, C_spec_accum);
# endif
}
# endif
# ifdef CLOSURE_REFRACTION
if (refr_accum.a < 0.999) {
vec3 trans = probe_evaluate_world_spec(refr_dir, roughnessSquared);
accumulate_light(trans, 1.0, refr_accum);
}
# endif
# endif /* Specular probes */
/* ---------------------------- */
/* Ambient Occlusion */
/* ---------------------------- */
# if defined(CLOSURE_GLOSSY) || defined(CLOSURE_DIFFUSE)
if (!use_contact_shadows) {
/* HACK: Fix for translucent BSDF. (see T65631) */
N = -N;
}
vec3 bent_normal;
float final_ao = occlusion_compute(N, viewPosition, ao, rand, bent_normal);
if (!use_contact_shadows) {
N = -N;
/* Bypass bent normal. */
bent_normal = N;
}
# endif
/* ---------------------------- */
/* Specular Output */
/* ---------------------------- */
float NV = dot(N, V);
# ifdef CLOSURE_GLOSSY
vec2 uv = lut_coords(NV, roughness);
vec2 brdf_lut = texture(utilTex, vec3(uv, 1.0)).rg;
/* This factor is outputted to be used by SSR in order
* to match the intensity of the regular reflections. */
ssr_spec = F_brdf(f0, f90, brdf_lut);
float spec_occlu = specular_occlusion(NV, final_ao, roughness);
/* The SSR pass recompute the occlusion to not apply it to the SSR */
if (ssrToggle && ssr_id == outputSsrId) {
spec_occlu = 1.0;
}
out_spec += spec_accum.rgb * ssr_spec * spec_occlu;
# endif
# ifdef CLOSURE_REFRACTION
float btdf = get_btdf_lut(NV, roughness, ior);
out_refr += refr_accum.rgb * btdf;
/* Global toggle for lightprobe baking. */
out_refr *= float(specToggle);
# endif
# ifdef CLOSURE_CLEARCOAT
NV = dot(C_N, V);
vec2 C_uv = lut_coords(NV, C_roughness);
vec2 C_brdf_lut = texture(utilTex, vec3(C_uv, 1.0)).rg;
vec3 C_fresnel = F_brdf(vec3(0.04), vec3(1.0), C_brdf_lut) *
specular_occlusion(NV, final_ao, C_roughness);
out_spec += C_spec_accum.rgb * C_fresnel * C_intensity;
# endif
# ifdef CLOSURE_GLOSSY
/* Global toggle for lightprobe baking. */
out_spec *= float(specToggle);
# endif
/* ---------------------------------------------------------------- */
/* ---------------- DIFFUSE ENVIRONMENT LIGHTING ------------------ */
/* ---------------------------------------------------------------- */
/* Accumulate light from all sources until accumulator is full. Then apply Occlusion and BRDF. */
# ifdef CLOSURE_DIFFUSE
vec4 diff_accum = vec4(0.0);
/* ---------------------------- */
/* Irradiance Grids */
/* ---------------------------- */
/* Start at 1 because 0 is world irradiance */
for (int i = 1; i < MAX_GRID && i < prbNumRenderGrid && diff_accum.a < 0.999; i++) {
GridData gd = grids_data[i];
vec3 localpos;
float fade = probe_attenuation_grid(gd, grids_data[i].localmat, worldPosition, localpos);
if (fade > 0.0) {
vec3 diff = probe_evaluate_grid(gd, worldPosition, bent_normal, localpos);
accumulate_light(diff, fade, diff_accum);
}
}
/* ---------------------------- */
/* World Diffuse */
/* ---------------------------- */
if (diff_accum.a < 0.999 && prbNumRenderGrid > 0) {
vec3 diff = probe_evaluate_world_diff(bent_normal);
accumulate_light(diff, 1.0, diff_accum);
}
out_diff += diff_accum.rgb * gtao_multibounce(final_ao, albedo);
# endif
#endif
}
/* Cleanup for next configuration */
#undef CLOSURE_NAME
#ifdef CLOSURE_DIFFUSE
# undef CLOSURE_DIFFUSE
#endif
#ifdef CLOSURE_GLOSSY
# undef CLOSURE_GLOSSY
#endif
#ifdef CLOSURE_CLEARCOAT
# undef CLOSURE_CLEARCOAT
#endif
#ifdef CLOSURE_REFRACTION
# undef CLOSURE_REFRACTION
#endif
#ifdef CLOSURE_SUBSURFACE
# undef CLOSURE_SUBSURFACE
#endif

View File

@@ -147,17 +147,27 @@ Closure closure_emission(vec3 rgb)
#ifndef VOLUMETRICS
/* Let radiance passthrough or replace it to get the BRDF and color
* to applied to the SSR result. */
vec3 closure_mask_ssr_radiance(vec3 radiance, float ssr_id)
{
return (ssrToggle && int(ssr_id) == outputSsrId) ? vec3(1.0) : radiance;
}
void closure_load_ssr_data(
vec3 ssr_spec, float roughness, vec3 N, vec3 viewVec, int ssr_id, inout Closure cl)
vec3 ssr_radiance, float roughness, vec3 N, float ssr_id, inout Closure cl)
{
/* Still encode to avoid artifacts in the SSR pass. */
vec3 vN = normalize(mat3(ViewMatrix) * N);
cl.ssr_normal = normal_encode(vN, viewVec);
cl.ssr_normal = normal_encode(vN, viewCameraVec);
if (ssr_id == outputSsrId) {
cl.ssr_data = vec4(ssr_spec, roughness);
if (ssrToggle && int(ssr_id) == outputSsrId) {
cl.ssr_data = vec4(ssr_radiance, roughness);
cl.flag |= CLOSURE_SSR_FLAG;
}
else {
cl.radiance += ssr_radiance;
}
}
void closure_load_sss_data(
@@ -169,13 +179,11 @@ void closure_load_sss_data(
cl.sss_radius = radius;
cl.sss_albedo = sss_albedo;
cl.flag |= CLOSURE_SSS_FLAG;
cl.radiance += render_pass_diffuse_mask(sss_albedo, vec3(0));
/* Irradiance will be convolved by SSSS pass. Do not add to radiance. */
sss_irradiance = vec3(0);
}
else
# endif
{
cl.radiance += render_pass_diffuse_mask(sss_albedo, sss_irradiance * sss_albedo);
}
cl.radiance += render_pass_diffuse_mask(vec3(1), sss_irradiance) * sss_albedo;
}
#endif

View File

@@ -12,52 +12,91 @@ uniform sampler2DArray utilTex;
#define LUT_SIZE 64
#define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0)
#define LTC_MAT_LAYER 0
#define LTC_BRDF_LAYER 1
#define BRDF_LUT_LAYER 1
#define NOISE_LAYER 2
#define LTC_DISK_LAYER 3 /* UNUSED */
/* Return texture coordinates to sample Surface LUT */
vec2 lut_coords(float cosTheta, float roughness)
/* Layers 4 to 20 are for BTDF Lut. */
const float lut_btdf_layer_first = 4.0;
const float lut_btdf_layer_count = 16.0;
#define texelfetch_noise_tex(coord) \
texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, NOISE_LAYER), 0)
/* Return texture coordinates to sample Surface LUT. */
vec2 lut_coords(float cos_theta, float roughness)
{
float theta = acos(cosTheta);
vec2 coords = vec2(roughness, theta / M_PI_2);
vec2 coords = vec2(roughness, sqrt(1.0 - cos_theta));
/* scale and bias coordinates, for correct filtered lookup */
return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE;
}
vec2 lut_coords_ltc(float cosTheta, float roughness)
/* Returns the GGX split-sum precomputed in LUT. */
vec2 brdf_lut(float cos_theta, float roughness)
{
vec2 coords = vec2(roughness, sqrt(1.0 - cosTheta));
/* scale and bias coordinates, for correct filtered lookup */
return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE;
return textureLod(utilTex, vec3(lut_coords(cos_theta, roughness), BRDF_LUT_LAYER), 0.0).rg;
}
float get_btdf_lut(float NV, float roughness, float ior)
/* Return texture coordinates to sample Surface LUT. */
vec3 lut_coords_btdf(float cos_theta, float roughness, float ior)
{
const vec3 lut_scale_bias_texel_size = vec3((LUT_SIZE - 1.0), 0.5, 1.5) / LUT_SIZE;
/* ior is sin of critical angle. */
float critical_cos = sqrt(1.0 - ior * ior);
vec3 coords;
/* Try to compensate for the low resolution and interpolation error. */
coords.x = (ior > 1.0) ? (0.9 + lut_scale_bias_texel_size.z) +
(0.1 - lut_scale_bias_texel_size.z) * f0_from_ior(ior) :
(0.9 + lut_scale_bias_texel_size.z) * ior * ior;
coords.y = 1.0 - saturate(NV);
coords.xy *= lut_scale_bias_texel_size.x;
coords.xy += lut_scale_bias_texel_size.y;
coords.x = sqr(ior);
coords.y = cos_theta;
coords.y -= critical_cos;
coords.y /= (coords.y > 0.0) ? (1.0 - critical_cos) : critical_cos;
coords.y = coords.y * 0.5 + 0.5;
coords.z = roughness;
const float lut_lvl_ofs = 4.0; /* First texture lvl of roughness. */
const float lut_lvl_scale = 16.0; /* How many lvl of roughness in the lut. */
coords = saturate(coords);
float mip = roughness * lut_lvl_scale;
float mip_floor = floor(mip);
/* scale and bias coordinates, for correct filtered lookup */
coords.xy = coords.xy * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE;
coords.z = lut_lvl_ofs + mip_floor + 1.0;
float btdf_high = textureLod(utilTex, coords, 0.0).r;
return coords;
}
coords.z -= 1.0;
float btdf_low = textureLod(utilTex, coords, 0.0).r;
/* Returns GGX BTDF in first component and fresnel in second. */
vec2 btdf_lut(float cos_theta, float roughness, float ior)
{
if (ior <= 1e-5) {
return vec2(0.0);
}
float btdf = (ior == 1.0) ? 1.0 : mix(btdf_low, btdf_high, mip - coords.z);
if (ior >= 1.0) {
vec2 split_sum = brdf_lut(cos_theta, roughness);
float f0 = f0_from_ior(ior);
/* Baked IOR for GGX BRDF. */
const float specular = 1.0;
const float eta_brdf = (2.0 / (1.0 - sqrt(0.08 * specular))) - 1.0;
/* Avoid harsh transition comming from ior == 1. */
float f90 = fast_sqrt(saturate(f0 / (f0_from_ior(eta_brdf) * 0.25)));
float fresnel = F_brdf_single_scatter(vec3(f0), vec3(f90), split_sum).r;
/* Setting the BTDF to one is not really important since it is only used for multiscatter
* and it's already quite close to ground truth. */
float btdf = 1.0;
return vec2(btdf, fresnel);
}
vec3 coords = lut_coords_btdf(cos_theta, roughness, ior);
float layer = coords.z * lut_btdf_layer_count;
float layer_floored = floor(layer);
coords.z = lut_btdf_layer_first + layer_floored;
vec2 btdf_low = textureLod(utilTex, coords, 0.0).rg;
coords.z += 1.0;
vec2 btdf_high = textureLod(utilTex, coords, 0.0).rg;
/* Manual trilinear interpolation. */
vec2 btdf = mix(btdf_low, btdf_high, layer - layer_floored);
return btdf;
}

View File

@@ -2,6 +2,8 @@
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
#pragma BLENDER_REQUIRE(raytrace_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ssr_lib.glsl)
@@ -170,7 +172,7 @@ void main()
/* Importance sampling bias */
rand.x = mix(rand.x, 0.0, ssrBrdfBias);
vec3 worldPosition = transform_point(ViewMatrixInverse, viewPosition);
vec3 W = transform_point(ViewMatrixInverse, viewPosition);
vec3 wN = transform_direction(ViewMatrixInverse, N);
vec3 T, B;
@@ -180,12 +182,12 @@ void main()
for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) {
PlanarData pd = planars_data[i];
float fade = probe_attenuation_planar(pd, worldPosition, wN, 0.0);
float fade = probe_attenuation_planar(pd, W, wN, 0.0);
if (fade > 0.5) {
/* Find view vector / reflection plane intersection. */
/* TODO optimize, use view space for all. */
vec3 tracePosition = line_plane_intersect(worldPosition, cameraVec, pd.pl_plane_eq);
vec3 tracePosition = line_plane_intersect(W, cameraVec, pd.pl_plane_eq);
tracePosition = transform_point(ViewMatrix, tracePosition);
vec3 planeNormal = transform_direction(ViewMatrix, pd.pl_normal);
@@ -213,6 +215,8 @@ uniform sampler2D pdfBuffer;
uniform int neighborOffset;
in vec4 uvcoordsvar;
const ivec2 neighbors[32] = ivec2[32](ivec2(0, 0),
ivec2(1, 1),
ivec2(-2, 0),
@@ -298,7 +302,7 @@ float get_sample_depth(vec2 hit_co, bool is_planar, float planar_index)
vec3 get_hit_vector(vec3 hit_pos,
PlanarData pd,
vec3 worldPosition,
vec3 P,
vec3 N,
vec3 V,
bool is_planar,
@@ -309,7 +313,7 @@ vec3 get_hit_vector(vec3 hit_pos,
if (is_planar) {
/* Reflect back the hit position to have it in non-reflected world space */
vec3 trace_pos = line_plane_intersect(worldPosition, V, pd.pl_plane_eq);
vec3 trace_pos = line_plane_intersect(P, V, pd.pl_plane_eq);
hit_vec = hit_pos - trace_pos;
hit_vec = reflect(hit_vec, pd.pl_normal);
/* Modify here so mip texel alignment is correct. */
@@ -317,8 +321,8 @@ vec3 get_hit_vector(vec3 hit_pos,
}
else {
/* Find hit position in previous frame. */
hit_co = get_reprojected_reflection(hit_pos, worldPosition, N);
hit_vec = hit_pos - worldPosition;
hit_co = get_reprojected_reflection(hit_pos, P, N);
hit_vec = hit_pos - P;
}
mask = screen_border_mask(hit_co);
@@ -339,7 +343,7 @@ vec4 get_ssr_samples(vec4 hit_pdf,
ivec4 hit_data[2],
PlanarData pd,
float planar_index,
vec3 worldPosition,
vec3 P,
vec3 N,
vec3 V,
float roughnessSquared,
@@ -379,14 +383,10 @@ vec4 get_ssr_samples(vec4 hit_pdf,
/* Get actual hit vector and hit coordinate (from last frame). */
vec4 mask = vec4(1.0);
hit_pos[0] = get_hit_vector(
hit_pos[0], pd, worldPosition, N, V, is_planar.x, hit_co[0].xy, mask.x);
hit_pos[1] = get_hit_vector(
hit_pos[1], pd, worldPosition, N, V, is_planar.y, hit_co[0].zw, mask.y);
hit_pos[2] = get_hit_vector(
hit_pos[2], pd, worldPosition, N, V, is_planar.z, hit_co[1].xy, mask.z);
hit_pos[3] = get_hit_vector(
hit_pos[3], pd, worldPosition, N, V, is_planar.w, hit_co[1].zw, mask.w);
hit_pos[0] = get_hit_vector(hit_pos[0], pd, P, N, V, is_planar.x, hit_co[0].xy, mask.x);
hit_pos[1] = get_hit_vector(hit_pos[1], pd, P, N, V, is_planar.y, hit_co[0].zw, mask.y);
hit_pos[2] = get_hit_vector(hit_pos[2], pd, P, N, V, is_planar.z, hit_co[1].xy, mask.z);
hit_pos[3] = get_hit_vector(hit_pos[3], pd, P, N, V, is_planar.w, hit_co[1].zw, mask.w);
vec4 hit_dist;
hit_dist.x = length(hit_pos[0]);
@@ -476,47 +476,30 @@ vec4 get_ssr_samples(vec4 hit_pdf,
return accum;
}
void main()
void raytrace_resolve(ClosureInputGlossy cl_in,
inout ClosureEvalGlossy cl_eval,
inout ClosureEvalCommon cl_common,
inout ClosureOutputGlossy cl_out)
{
ivec2 fullres_texel = ivec2(gl_FragCoord.xy);
# ifdef FULLRES
ivec2 halfres_texel = fullres_texel;
ivec2 texel = ivec2(gl_FragCoord.xy);
# else
ivec2 halfres_texel = ivec2(gl_FragCoord.xy / 2.0);
ivec2 texel = ivec2(gl_FragCoord.xy / 2.0);
# endif
vec2 uvs = gl_FragCoord.xy / vec2(textureSize(depthBuffer, 0));
float depth = textureLod(depthBuffer, uvs, 0.0).r;
/* Early out */
if (depth == 1.0) {
discard;
}
/* Using world space */
vec3 viewPosition = get_view_space_from_depth(uvs, depth); /* Needed for viewCameraVec */
vec3 worldPosition = transform_point(ViewMatrixInverse, viewPosition);
vec3 V = cameraVec;
vec3 vN = normal_decode(texelFetch(normalBuffer, fullres_texel, 0).rg, viewCameraVec);
vec3 N = transform_direction(ViewMatrixInverse, vN);
vec4 speccol_roughness = texelFetch(specroughBuffer, fullres_texel, 0).rgba;
vec3 V = cl_common.V;
vec3 N = cl_in.N;
vec3 P = cl_common.P;
/* Early out */
if (dot(speccol_roughness.rgb, vec3(1.0)) == 0.0) {
discard;
}
float roughness = speccol_roughness.a;
float roughnessSquared = max(1e-3, roughness * roughness);
vec4 spec_accum = vec4(0.0);
float roughness = cl_in.roughness;
float roughnessSquared = max(1e-3, sqr(roughness));
/* Resolve SSR */
float cone_cos = cone_cosine(roughnessSquared);
float cone_tan = sqrt(1 - cone_cos * cone_cos) / cone_cos;
cone_tan *= mix(saturate(dot(N, -V) * 2.0), 1.0, roughness); /* Elongation fit */
vec2 source_uvs = project_point(pastViewProjectionMatrix, worldPosition).xy * 0.5 + 0.5;
vec2 source_uvs = project_point(pastViewProjectionMatrix, P).xy * 0.5 + 0.5;
vec4 ssr_accum = vec4(0.0);
float weight_acc = 0.0;
@@ -525,16 +508,16 @@ void main()
/* TODO optimize with textureGather */
/* Doing these fetches early to hide latency. */
vec4 hit_pdf;
hit_pdf.x = texelFetch(pdfBuffer, halfres_texel + neighbors[0 + neighborOffset], 0).r;
hit_pdf.y = texelFetch(pdfBuffer, halfres_texel + neighbors[1 + neighborOffset], 0).r;
hit_pdf.z = texelFetch(pdfBuffer, halfres_texel + neighbors[2 + neighborOffset], 0).r;
hit_pdf.w = texelFetch(pdfBuffer, halfres_texel + neighbors[3 + neighborOffset], 0).r;
hit_pdf.x = texelFetch(pdfBuffer, texel + neighbors[0 + neighborOffset], 0).r;
hit_pdf.y = texelFetch(pdfBuffer, texel + neighbors[1 + neighborOffset], 0).r;
hit_pdf.z = texelFetch(pdfBuffer, texel + neighbors[2 + neighborOffset], 0).r;
hit_pdf.w = texelFetch(pdfBuffer, texel + neighbors[3 + neighborOffset], 0).r;
ivec4 hit_data[2];
hit_data[0].xy = texelFetch(hitBuffer, halfres_texel + neighbors[0 + neighborOffset], 0).rg;
hit_data[0].zw = texelFetch(hitBuffer, halfres_texel + neighbors[1 + neighborOffset], 0).rg;
hit_data[1].xy = texelFetch(hitBuffer, halfres_texel + neighbors[2 + neighborOffset], 0).rg;
hit_data[1].zw = texelFetch(hitBuffer, halfres_texel + neighbors[3 + neighborOffset], 0).rg;
hit_data[0].xy = texelFetch(hitBuffer, texel + neighbors[0 + neighborOffset], 0).rg;
hit_data[0].zw = texelFetch(hitBuffer, texel + neighbors[1 + neighborOffset], 0).rg;
hit_data[1].xy = texelFetch(hitBuffer, texel + neighbors[2 + neighborOffset], 0).rg;
hit_data[1].zw = texelFetch(hitBuffer, texel + neighbors[3 + neighborOffset], 0).rg;
/* Find Planar Reflections affecting this pixel */
PlanarData pd;
@@ -542,7 +525,7 @@ void main()
for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) {
pd = planars_data[i];
float fade = probe_attenuation_planar(pd, worldPosition, N, 0.0);
float fade = probe_attenuation_planar(pd, P, N, 0.0);
if (fade > 0.5) {
planar_index = float(i);
@@ -554,7 +537,7 @@ void main()
hit_data,
pd,
planar_index,
worldPosition,
P,
N,
V,
roughnessSquared,
@@ -564,19 +547,51 @@ void main()
}
/* Compute SSR contribution */
if (weight_acc > 0.0) {
ssr_accum /= weight_acc;
/* fade between 0.5 and 1.0 roughness */
ssr_accum.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, roughness);
accumulate_light(ssr_accum.rgb, ssr_accum.a, spec_accum);
ssr_accum *= (weight_acc == 0.0) ? 0.0 : (1.0 / weight_acc);
/* fade between 0.5 and 1.0 roughness */
ssr_accum.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, roughness);
cl_eval.raytrace_radiance = ssr_accum.rgb * ssr_accum.a;
cl_common.specular_accum -= ssr_accum.a;
}
CLOSURE_EVAL_FUNCTION_DECLARE_1(ssr_resolve, Glossy)
void main()
{
ivec2 texel = ivec2(gl_FragCoord.xy);
float depth = texelFetch(depthBuffer, texel, 0).r;
if (depth == 1.0) {
discard;
}
/* If SSR contribution is not 1.0, blend with cubemaps */
if (spec_accum.a < 1.0) {
fallback_cubemap(N, V, worldPosition, viewPosition, roughness, roughnessSquared, spec_accum);
vec4 speccol_roughness = texelFetch(specroughBuffer, texel, 0).rgba;
vec3 brdf = speccol_roughness.rgb;
float roughness = speccol_roughness.a;
if (max_v3(brdf) <= 0.0) {
discard;
}
fragColor = vec4(spec_accum.rgb * speccol_roughness.rgb, 1.0);
viewPosition = get_view_space_from_depth(uvcoordsvar.xy, depth);
worldPosition = transform_point(ViewMatrixInverse, viewPosition);
vec2 normal_encoded = texelFetch(normalBuffer, texel, 0).rg;
viewNormal = normal_decode(normal_encoded, viewCameraVec);
worldNormal = transform_direction(ViewMatrixInverse, viewNormal);
CLOSURE_VARS_DECLARE_1(Glossy);
in_Glossy_0.N = worldNormal;
in_Glossy_0.roughness = roughness;
/* Do a full deferred evaluation of the glossy BSDF. The only difference is that we inject the
* SSR resolve before the cubemap iter. BRDF term is already computed during main pass and is
* passed as specular color. */
CLOSURE_EVAL_FUNCTION_1(ssr_resolve, Glossy);
fragColor = vec4(out_Glossy_0.radiance * brdf, 1.0);
}
#endif

View File

@@ -9,6 +9,9 @@ uniform float lodFactor;
uniform float lodMax;
uniform float intensityFac;
uniform float sampleCount;
uniform float invSampleCount;
in vec3 worldPosition;
out vec4 FragColor;
@@ -144,7 +147,7 @@ void main()
float weight = 0.0;
vec3 out_radiance = vec3(0.0);
for (float i = 0; i < sampleCount; i++) {
vec3 L = sample_hemisphere(i, N, T, B); /* Microfacet normal */
vec3 L = sample_hemisphere(i, invSampleCount, N, T, B); /* Microfacet normal */
float NL = dot(N, L);
if (NL > 0.0) {

View File

@@ -11,6 +11,9 @@ uniform float paddingSize;
uniform float intensityFac;
uniform float fireflyFactor;
uniform float sampleCount;
uniform float invSampleCount;
in vec3 worldPosition;
out vec4 FragColor;
@@ -45,15 +48,11 @@ void main()
make_orthonormal_basis(N, T, B); /* Generate tangent space */
/* Noise to dither the samples */
/* Note : ghosting is better looking than noise. */
// setup_noise();
/* Integrating Envmap */
float weight = 0.0;
vec3 out_radiance = vec3(0.0);
for (float i = 0; i < sampleCount; i++) {
vec3 H = sample_ggx(i, roughnessSquared, N, T, B); /* Microfacet normal */
vec3 H = sample_ggx(i, invSampleCount, roughnessSquared, N, T, B); /* Microfacet normal */
vec3 L = -reflect(V, H);
float NL = dot(N, L);

View File

@@ -13,6 +13,9 @@ uniform float farClip;
uniform float visibilityRange;
uniform float visibilityBlur;
uniform float sampleCount;
uniform float invSampleCount;
out vec4 FragColor;
vec3 octahedral_to_cubemap_proj(vec2 co)
@@ -77,7 +80,7 @@ void main()
vec2 accum = vec2(0.0);
for (float i = 0; i < sampleCount; i++) {
vec3 sample = sample_cone(i, M_PI_2 * visibilityBlur, cos, T, B);
vec3 sample = sample_cone(i, invSampleCount, M_PI_2 * visibilityBlur, cos, T, B);
float depth = texture(probeDepth, sample).r;
depth = get_world_distance(depth, sample);
accum += vec2(depth, depth * depth);

View File

@@ -137,9 +137,9 @@ float probe_attenuation_planar(PlanarData pd, vec3 W, vec3 N, float roughness)
return fac;
}
float probe_attenuation_grid(GridData gd, mat4 localmat, vec3 W, out vec3 localpos)
float probe_attenuation_grid(GridData gd, vec3 W, out vec3 localpos)
{
localpos = transform_point(localmat, W);
localpos = transform_point(gd.localmat, W);
vec3 pos_to_edge = max(vec3(0.0), abs(localpos) - 1.0);
float fade = length(pos_to_edge);
return saturate(-fade * gd.g_atten_scale + gd.g_atten_bias);
@@ -167,7 +167,7 @@ vec3 probe_evaluate_cube(int pd_id, vec3 W, vec3 R, float roughness)
* http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf
*/
float original_roughness = roughness;
float linear_roughness = sqrt(roughness);
float linear_roughness = fast_sqrt(roughness);
float distance_roughness = saturate(dist * linear_roughness / length(intersection));
linear_roughness = mix(distance_roughness, linear_roughness, linear_roughness);
roughness = linear_roughness * linear_roughness;
@@ -175,16 +175,17 @@ vec3 probe_evaluate_cube(int pd_id, vec3 W, vec3 R, float roughness)
float fac = saturate(original_roughness * 2.0 - 1.0);
R = mix(intersection, R, fac * fac);
return textureLod_cubemapArray(probeCubes, vec4(R, float(pd_id)), roughness * prbLodCubeMax).rgb;
float lod = linear_roughness * prbLodCubeMax;
return textureLod_cubemapArray(probeCubes, vec4(R, float(pd_id)), lod).rgb;
}
vec3 probe_evaluate_world_spec(vec3 R, float roughness)
{
return textureLod_cubemapArray(probeCubes, vec4(R, 0.0), roughness * prbLodCubeMax).rgb;
float lod = fast_sqrt(roughness) * prbLodCubeMax;
return textureLod_cubemapArray(probeCubes, vec4(R, 0.0), lod).rgb;
}
vec3 probe_evaluate_planar(
float id, PlanarData pd, vec3 W, vec3 N, vec3 V, float roughness, inout float fade)
vec3 probe_evaluate_planar(int id, PlanarData pd, vec3 W, vec3 N, vec3 V, float roughness)
{
/* Find view vector / reflection plane intersection. */
vec3 point_on_plane = line_plane_intersect(W, V, pd.pl_plane_eq);
@@ -226,7 +227,7 @@ void fallback_cubemap(vec3 N,
#ifdef SSR_AO
vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
vec3 bent_normal;
float final_ao = occlusion_compute(N, viewPosition, 1.0, rand, bent_normal);
float final_ao = occlusion_compute(N, viewPosition, rand, bent_normal);
final_ao = specular_occlusion(dot(N, V), final_ao, roughness);
#else
const float final_ao = 1.0;

View File

@@ -252,32 +252,29 @@ float light_attenuation(LightData ld, vec4 l_vector)
return vis;
}
float light_shadowing(LightData ld,
vec3 W,
#ifndef VOLUMETRICS
vec3 viewPosition,
float tracing_depth,
vec3 true_normal,
float rand_x,
const bool use_contact_shadows,
#endif
float vis)
float light_shadowing(LightData ld, vec3 W, float vis)
{
#if !defined(VOLUMETRICS) || defined(VOLUME_SHADOW)
/* shadowing */
if (ld.l_shadowid >= 0.0 && vis > 0.001) {
if (ld.l_type == SUN) {
vis *= sample_cascade_shadow(int(ld.l_shadowid), W);
}
else {
vis *= sample_cube_shadow(int(ld.l_shadowid), W);
}
}
#endif
return vis;
}
# ifndef VOLUMETRICS
#ifndef VOLUMETRICS
float light_contact_shadows(
LightData ld, vec3 P, vec3 vP, float tracing_depth, vec3 vNg, float rand_x, float vis)
{
if (ld.l_shadowid >= 0.0 && vis > 0.001) {
ShadowData sd = shadows_data[int(ld.l_shadowid)];
/* Only compute if not already in shadow. */
if (use_contact_shadows && sd.sh_contact_dist > 0.0 && vis > 1e-8) {
if (sd.sh_contact_dist > 0.0) {
/* Contact Shadows. */
vec3 ray_ori, ray_dir;
float trace_distance;
@@ -287,54 +284,34 @@ float light_shadowing(LightData ld,
ray_dir = shadows_cascade_data[int(sd.sh_data_index)].sh_shadow_vec * trace_distance;
}
else {
ray_dir = shadows_cube_data[int(sd.sh_data_index)].position.xyz - W;
ray_dir = shadows_cube_data[int(sd.sh_data_index)].position.xyz - P;
float len = length(ray_dir);
trace_distance = min(sd.sh_contact_dist, len);
ray_dir *= trace_distance / len;
}
ray_dir = transform_direction(ViewMatrix, ray_dir);
ray_ori = vec3(viewPosition.xy, tracing_depth) + true_normal * sd.sh_contact_offset;
ray_ori = vec3(vP.xy, tracing_depth) + vNg * sd.sh_contact_offset;
vec3 hit_pos = raycast(
-1, ray_ori, ray_dir, sd.sh_contact_thickness, rand_x, 0.1, 0.001, false);
if (hit_pos.z > 0.0) {
hit_pos = get_view_space_from_depth(hit_pos.xy, hit_pos.z);
float hit_dist = distance(viewPosition, hit_pos);
float hit_dist = distance(vP, hit_pos);
float dist_ratio = hit_dist / trace_distance;
return vis * saturate(dist_ratio * 3.0 - 2.0);
return saturate(dist_ratio * 3.0 - 2.0);
}
}
# endif /* VOLUMETRICS */
}
#endif
return vis;
return 1.0;
}
#endif /* VOLUMETRICS */
float light_visibility(LightData ld,
vec3 W,
#ifndef VOLUMETRICS
vec3 viewPosition,
float tracing_depth,
vec3 true_normal,
float rand_x,
const bool use_contact_shadows,
#endif
vec4 l_vector)
float light_visibility(LightData ld, vec3 W, vec4 l_vector)
{
float l_atten = light_attenuation(ld, l_vector);
return light_shadowing(ld,
W,
#ifndef VOLUMETRICS
viewPosition,
tracing_depth,
true_normal,
rand_x,
use_contact_shadows,
#endif
l_atten);
return light_shadowing(ld, W, l_atten);
}
float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector)

View File

@@ -5,8 +5,12 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl)
#pragma BLENDER_REQUIRE(closure_lib.glsl)
#pragma BLENDER_REQUIRE(closure_lit_lib.glsl)
#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_diffuse_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_translucent_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_refraction_lib.glsl)
#pragma BLENDER_REQUIRE(surface_lib.glsl)
#ifdef USE_ALPHA_HASH

View File

@@ -39,7 +39,7 @@ void main()
vec3 viewPosition = get_view_space_from_depth(uvs, depth);
vec3 worldPosition = transform_point(ViewMatrixInverse, viewPosition);
vec3 true_normal = normalize(cross(dFdx(viewPosition), dFdy(viewPosition)));
vec3 true_normal = safe_normalize(cross(dFdx(viewPosition), dFdy(viewPosition)));
for (int i = 0; i < MAX_LIGHT && i < laNumLight; i++) {
LightData ld = lights_data[i];
@@ -48,8 +48,10 @@ void main()
l_vector.xyz = ld.l_position - worldPosition;
l_vector.w = length(l_vector.xyz);
float l_vis = light_shadowing(
ld, worldPosition, viewPosition, tracing_depth, true_normal, rand.x, true, 1.0);
float l_vis = light_shadowing(ld, worldPosition, 1.0);
l_vis *= light_contact_shadows(
ld, worldPosition, viewPosition, tracing_depth, true_normal, rand.x, 1.0);
accum_light += l_vis;
}

View File

@@ -3,8 +3,13 @@
#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(closure_lib.glsl)
#pragma BLENDER_REQUIRE(closure_lit_lib.glsl)
#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_diffuse_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_translucent_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_refraction_lib.glsl)
#pragma BLENDER_REQUIRE(surface_lib.glsl)
#pragma BLENDER_REQUIRE(volumetric_lib.glsl)

View File

@@ -13,7 +13,15 @@ uniform float refractionDepth;
vec3 worldNormal; \
vec3 viewNormal;
#ifdef GPU_GEOMETRY_SHADER
#if defined(STEP_RESOLVE) || defined(STEP_RAYTRACE)
/* SSR will set these global variables itself.
* Also make false positive compiler warnings disapear by setting values. */
vec3 worldPosition = vec3(0);
vec3 viewPosition = vec3(0);
vec3 worldNormal = vec3(0);
vec3 viewNormal = vec3(0);
#elif defined(GPU_GEOMETRY_SHADER)
in ShaderStageInterface{SURFACE_INTERFACE} dataIn[];
out ShaderStageInterface{SURFACE_INTERFACE} dataOut;
@@ -24,7 +32,7 @@ out ShaderStageInterface{SURFACE_INTERFACE} dataOut;
dataOut.worldNormal = dataIn[vert].worldNormal; \
dataOut.viewNormal = dataIn[vert].viewNormal;
#else
#else /* GPU_VERTEX_SHADER || GPU_FRAGMENT_SHADER*/
IN_OUT ShaderStageInterface{SURFACE_INTERFACE};

View File

@@ -1,6 +1,6 @@
#pragma BLENDER_REQUIRE(volumetric_lib.glsl)
#pragma BLENDER_REQUIRE(closure_lib.glsl)
#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
/* Based on Frosbite Unified Volumetric.
* https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */

View File

@@ -72,6 +72,9 @@ float sum(vec4 v) { return dot(vec4(1.0), v); }
float avg(vec2 v) { return dot(vec2(1.0 / 2.0), v); }
float avg(vec3 v) { return dot(vec3(1.0 / 3.0), v); }
float avg(vec4 v) { return dot(vec4(1.0 / 4.0), v); }
float sqr(float v) { return v * v; }
/* clang-format on */
#define saturate(a) clamp(a, 0.0, 1.0)
@@ -93,6 +96,15 @@ float len_squared(vec3 a)
return dot(a, a);
}
vec3 safe_normalize(vec3 v)
{
float len = length(v);
if (isnan(len) || len == 0.0) {
return vec3(1.0, 0.0, 0.0);
}
return v / len;
}
/** \} */
/* ---------------------------------------------------------------------- */

View File

@@ -755,6 +755,10 @@ static void gpu_parse_material_library(GHash *hash, GPUMaterialLibrary *library)
/* get parameters */
while (*code && *code != ')') {
if (BLI_str_startswith(code, "const ")) {
code = gpu_str_skip_token(code, NULL, 0);
}
/* test if it's an input or output */
qual = FUNCTION_QUAL_IN;
if (BLI_str_startswith(code, "out ")) {

View File

@@ -4,7 +4,7 @@ void node_ambient_occlusion(
{
vec3 bent_normal;
vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
result_ao = occlusion_compute(normalize(normal), viewPosition, 1.0, rand, bent_normal);
result_ao = occlusion_compute(normalize(normal), viewPosition, rand, bent_normal);
result_color = result_ao * color;
}
#else

View File

@@ -1,12 +1,27 @@
#ifndef VOLUMETRICS
CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_diffuse, Diffuse)
void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out Closure result)
{
N = normalize(N);
CLOSURE_VARS_DECLARE_1(Diffuse);
in_Diffuse_0.N = N; /* Normalized during eval. */
in_Diffuse_0.albedo = color.rgb;
CLOSURE_EVAL_FUNCTION_1(node_bsdf_diffuse, Diffuse);
result = CLOSURE_DEFAULT;
eevee_closure_diffuse(N, color.rgb, 1.0, true, result.radiance);
result.radiance = render_pass_diffuse_mask(color.rgb, result.radiance * color.rgb);
closure_load_ssr_data(vec3(0.0), 0.0, N, viewCameraVec, -1, result);
out_Diffuse_0.radiance = render_pass_diffuse_mask(vec3(1.0), out_Diffuse_0.radiance);
out_Diffuse_0.radiance *= color.rgb;
result.radiance = out_Diffuse_0.radiance;
/* TODO(fclem) Try to not use this. */
closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1, result);
}
#else
/* Stub diffuse because it is not compatible with volumetrics. */
# define node_bsdf_diffuse(a, b, c, d) (d = CLOSURE_DEFAULT)

View File

@@ -1,4 +1,7 @@
#ifndef VOLUMETRICS
CLOSURE_EVAL_FUNCTION_DECLARE_3(node_eevee_specular, Diffuse, Glossy, Glossy)
void node_eevee_specular(vec4 diffuse,
vec4 specular,
float roughness,
@@ -12,34 +15,63 @@ void node_eevee_specular(vec4 diffuse,
float ssr_id,
out Closure result)
{
normal = normalize(normal);
CLOSURE_VARS_DECLARE_3(Diffuse, Glossy, Glossy);
vec3 out_diff, out_spec, ssr_spec;
eevee_closure_default_clearcoat(normal,
diffuse.rgb,
specular.rgb,
vec3(1.0),
int(ssr_id),
roughness,
clearcoat_normal,
clearcoat * 0.25,
clearcoat_roughness,
occlusion,
true,
out_diff,
out_spec,
ssr_spec);
in_common.occlusion = occlusion;
in_Diffuse_0.N = normal; /* Normalized during eval. */
in_Diffuse_0.albedo = diffuse.rgb;
in_Glossy_1.N = normal; /* Normalized during eval. */
in_Glossy_1.roughness = roughness;
in_Glossy_2.N = clearcoat_normal; /* Normalized during eval. */
in_Glossy_2.roughness = clearcoat_roughness;
CLOSURE_EVAL_FUNCTION_3(node_eevee_specular, Diffuse, Glossy, Glossy);
float alpha = 1.0 - transp;
result = CLOSURE_DEFAULT;
result.radiance = render_pass_diffuse_mask(diffuse.rgb, out_diff * diffuse.rgb);
result.radiance += render_pass_glossy_mask(vec3(1.0), out_spec);
result.radiance += render_pass_emission_mask(emissive.rgb);
result.radiance *= alpha;
result.transmittance = vec3(transp);
closure_load_ssr_data(ssr_spec * alpha, roughness, normal, viewCameraVec, int(ssr_id), result);
{
/* Diffuse. */
out_Diffuse_0.radiance = render_pass_diffuse_mask(vec3(1), out_Diffuse_0.radiance);
out_Diffuse_0.radiance *= in_Diffuse_0.albedo;
result += out_Diffuse_0.radiance;
}
{
/* Glossy. */
float NV = dot(in_Glossy_1.N, cameraVec);
vec2 split_sum = brdf_lut(NV, in_Glossy_1.roughness);
vec3 brdf = F_brdf_single_scatter(specular.rgb, vec3(1.0), split_sum);
out_Glossy_1.radiance = closure_mask_ssr_radiance(out_Glossy_1.radiance, ssr_id);
out_Glossy_1.radiance *= brdf;
out_Glossy_1.radiance = render_pass_glossy_mask(spec_color, out_Glossy_1.radiance);
closure_load_ssr_data(
out_Glossy_1.radiance, in_Glossy_1.roughness, in_Glossy_1.N, ssr_id, result);
}
{
/* Clearcoat. */
float NV = dot(in_Glossy_2.N, cameraVec);
vec2 split_sum = brdf_lut(NV, in_Glossy_2.roughness);
vec3 brdf = F_brdf_single_scatter(vec3(0.04), vec3(1.0), split_sum);
out_Glossy_2.radiance *= brdf * clearcoat * 0.25;
out_Glossy_2.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_2.radiance);
result.radiance += out_Glossy_2.radiance;
}
{
/* Emission. */
vec3 out_emission_radiance = render_pass_emission_mask(emission.rgb);
result.radiance += out_emission_radiance;
}
float trans = 1.0 - trans;
result.transmittance = vec3(trans);
result.radiance *= alpha;
result.ssr_data.rgb *= alpha;
}
#else
/* Stub specular because it is not compatible with volumetrics. */
# define node_eevee_specular(a, b, c, d, e, f, g, h, i, j, k, result) (result = CLOSURE_DEFAULT)

View File

@@ -1,38 +1,55 @@
#ifndef VOLUMETRICS
CLOSURE_EVAL_FUNCTION_DECLARE_2(node_bsdf_glass, Glossy, Refraction)
void node_bsdf_glass(vec4 color,
float roughness,
float ior,
vec3 N,
float use_multiscatter,
float ssr_id,
const float do_multiscatter,
const float ssr_id,
out Closure result)
{
N = normalize(N);
vec3 out_spec, out_refr, ssr_spec;
vec3 refr_color = (refractionDepth > 0.0) ? color.rgb * color.rgb :
color.rgb; /* Simulate 2 transmission event */
eevee_closure_glass(N,
vec3(1.0),
/* HACK: Pass the multiscatter flag as the sign to not add closure
* variations or increase register usage. */
(use_multiscatter != 0.0) ? vec3(1.0) : -vec3(1.0),
int(ssr_id),
roughness,
1.0,
ior,
true,
out_spec,
out_refr,
ssr_spec);
float fresnel = F_eta(ior, dot(N, cameraVec));
vec3 vN = mat3(ViewMatrix) * N;
result = CLOSURE_DEFAULT;
result.radiance = render_pass_glossy_mask(refr_color, out_refr * refr_color) * (1.0 - fresnel);
result.radiance += render_pass_glossy_mask(color.rgb, out_spec * color.rgb) * fresnel;
CLOSURE_VARS_DECLARE_2(Glossy, Refraction);
in_Glossy_0.N = N; /* Normalized during eval. */
in_Glossy_0.roughness = roughness;
in_Refraction_1.N = N; /* Normalized during eval. */
in_Refraction_1.roughness = roughness;
in_Refraction_1.ior = ior;
CLOSURE_EVAL_FUNCTION_2(node_bsdf_glass, Glossy, Refraction);
result = CLOSURE_DEFAULT;
float NV = dot(in_Refraction_1.N, cameraVec);
float fresnel = (do_multiscatter != 0.0) ?
btdf_lut(NV, in_Refraction_1.roughness, in_Refraction_1.ior).y :
F_eta(in_Refraction_1.ior, NV);
vec2 split_sum = brdf_lut(NV, in_Glossy_0.roughness);
vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(vec3(1.0), vec3(1.0), split_sum) :
F_brdf_single_scatter(vec3(1.0), vec3(1.0), split_sum);
out_Glossy_0.radiance = closure_mask_ssr_radiance(out_Glossy_0.radiance, ssr_id);
out_Glossy_0.radiance *= brdf;
out_Glossy_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Glossy_0.radiance);
out_Glossy_0.radiance *= color.rgb * fresnel;
closure_load_ssr_data(
ssr_spec * color.rgb * fresnel, roughness, N, viewCameraVec, int(ssr_id), result);
out_Glossy_0.radiance, in_Glossy_0.roughness, in_Glossy_0.N, ssr_id, result);
float btdf = (do_multiscatter != 0.0) ?
1.0 :
btdf_lut(NV, in_Refraction_1.roughness, in_Refraction_1.ior).x;
out_Refraction_1.radiance *= btdf;
out_Refraction_1.radiance = render_pass_glossy_mask(vec3(1.0), out_Refraction_1.radiance);
out_Refraction_1.radiance *= color.rgb * (1.0 - fresnel);
/* Simulate 2nd absorption event. */
out_Refraction_1.radiance *= (refractionDepth > 0.0) ? color.rgb : vec3(1.0);
result.radiance += out_Refraction_1.radiance;
}
#else
/* Stub glass because it is not compatible with volumetrics. */
# define node_bsdf_glass(a, b, c, d, e, f, result) (result = CLOSURE_DEFAULT)

View File

@@ -1,23 +1,32 @@
#ifndef VOLUMETRICS
CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_glossy, Glossy)
void node_bsdf_glossy(
vec4 color, float roughness, vec3 N, float use_multiscatter, float ssr_id, out Closure result)
{
N = normalize(N);
vec3 out_spec, ssr_spec;
eevee_closure_glossy(N,
vec3(1.0),
use_multiscatter != 0.0 ? vec3(1.0) : vec3(-1.0), /* HACK */
int(ssr_id),
roughness,
1.0,
true,
out_spec,
ssr_spec);
vec3 vN = mat3(ViewMatrix) * N;
bool do_ssr = (ssrToggle && int(ssr_id) == outputSsrId);
CLOSURE_VARS_DECLARE_1(Glossy);
in_Glossy_0.N = N; /* Normalized during eval. */
in_Glossy_0.roughness = roughness;
CLOSURE_EVAL_FUNCTION_1(node_bsdf_glossy, Glossy);
result = CLOSURE_DEFAULT;
result.radiance = render_pass_glossy_mask(vec3(1.0), out_spec) * color.rgb;
closure_load_ssr_data(ssr_spec * color.rgb, roughness, N, viewCameraVec, int(ssr_id), result);
vec2 split_sum = brdf_lut(dot(in_Glossy_0.N, cameraVec), in_Glossy_0.roughness);
vec3 brdf = (use_multiscatter != 0.0) ? F_brdf_multi_scatter(vec3(1.0), vec3(1.0), split_sum) :
F_brdf_single_scatter(vec3(1.0), vec3(1.0), split_sum);
out_Glossy_0.radiance = closure_mask_ssr_radiance(out_Glossy_0.radiance, ssr_id);
out_Glossy_0.radiance *= brdf;
out_Glossy_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Glossy_0.radiance);
out_Glossy_0.radiance *= color.rgb;
closure_load_ssr_data(
out_Glossy_0.radiance, in_Glossy_0.roughness, in_Glossy_0.N, ssr_id, result);
}
#else
/* Stub glossy because it is not compatible with volumetrics. */
# define node_bsdf_glossy(a, b, c, d, e, result) (result = CLOSURE_DEFAULT)

View File

@@ -1,41 +1,20 @@
#ifndef VOLUMETRICS
vec3 tint_from_color(vec3 color)
{
float lum = dot(color, vec3(0.3, 0.6, 0.1)); /* luminance approx. */
return (lum > 0) ? color / lum : vec3(1.0); /* normalize lum. to isolate hue+sat */
float lum = dot(color, vec3(0.3, 0.6, 0.1)); /* luminance approx. */
return (lum > 0.0) ? color / lum : vec3(0.0); /* normalize lum. to isolate hue+sat */
}
void convert_metallic_to_specular_tinted(vec3 basecol,
vec3 basecol_tint,
float metallic,
float specular_fac,
float specular_tint,
out vec3 diffuse,
out vec3 f0)
{
vec3 tmp_col = mix(vec3(1.0), basecol_tint, specular_tint);
f0 = mix((0.08 * specular_fac) * tmp_col, basecol, metallic);
diffuse = basecol * (1.0 - metallic);
}
/* Output sheen is to be multiplied by sheen_color. */
void principled_sheen(float NV,
vec3 basecol_tint,
float sheen,
float sheen_tint,
out float out_sheen,
out vec3 sheen_color)
float principled_sheen(float NV)
{
float f = 1.0 - NV;
/* Temporary fix for T59784. Normal map seems to contain NaNs for tangent space normal maps,
* therefore we need to clamp value. */
f = clamp(f, 0.0, 1.0);
/* Empirical approximation (manual curve fitting). Can be refined. */
out_sheen = f * f * f * 0.077 + f * 0.01 + 0.00026;
sheen_color = sheen * mix(vec3(1.0), basecol_tint, sheen_tint);
float sheen = f * f * f * 0.077 + f * 0.01 + 0.00026;
return sheen;
}
CLOSURE_EVAL_FUNCTION_DECLARE_4(node_bsdf_principled, Diffuse, Glossy, Glossy, Refraction)
void node_bsdf_principled(vec4 base_color,
float subsurface,
vec3 subsurface_radius,
@@ -59,434 +38,166 @@ void node_bsdf_principled(vec4 base_color,
vec3 N,
vec3 CN,
vec3 T,
vec3 I,
float use_multiscatter,
const float do_diffuse,
const float do_clearcoat,
const float do_refraction,
const float do_multiscatter,
float ssr_id,
float sss_id,
vec3 sss_scale,
out Closure result)
{
N = normalize(N);
ior = max(ior, 1e-5);
/* Match cycles. */
metallic = saturate(metallic);
transmission = saturate(transmission);
float m_transmission = 1.0 - transmission;
float diffuse_weight = (1.0 - transmission) * (1.0 - metallic);
transmission *= (1.0 - metallic);
float specular_weight = (1.0 - transmission);
clearcoat = max(clearcoat, 0.0);
transmission_roughness = 1.0 - (1.0 - roughness) * (1.0 - transmission_roughness);
float dielectric = 1.0 - metallic;
transmission *= dielectric;
sheen *= dielectric;
subsurface_color *= dielectric;
CLOSURE_VARS_DECLARE_4(Diffuse, Glossy, Glossy, Refraction);
vec3 diffuse, f0, out_diff, out_spec, out_refr, ssr_spec, sheen_color;
float out_sheen;
vec3 ctint = tint_from_color(base_color.rgb);
convert_metallic_to_specular_tinted(
base_color.rgb, ctint, metallic, specular, specular_tint, diffuse, f0);
in_Diffuse_0.N = N; /* Normalized during eval. */
in_Diffuse_0.albedo = mix(base_color.rgb, subsurface_color.rgb, subsurface);
float NV = dot(N, cameraVec);
principled_sheen(NV, ctint, sheen, sheen_tint, out_sheen, sheen_color);
in_Glossy_1.N = N; /* Normalized during eval. */
in_Glossy_1.roughness = roughness;
vec3 f90 = mix(vec3(1.0), f0, (1.0 - specular) * metallic);
in_Glossy_2.N = CN; /* Normalized during eval. */
in_Glossy_2.roughness = clearcoat_roughness;
/* Far from being accurate, but 2 glossy evaluation is too expensive.
* Most noticeable difference is at grazing angles since the bsdf lut
* f0 color interpolation is done on top of this interpolation. */
vec3 f0_glass = mix(vec3(1.0), base_color.rgb, specular_tint);
float fresnel = F_eta(ior, NV);
vec3 spec_col = F_color_blend(ior, fresnel, f0_glass) * fresnel;
f0 = mix(f0, spec_col, transmission);
f90 = mix(f90, spec_col, transmission);
in_Refraction_3.N = N; /* Normalized during eval. */
in_Refraction_3.roughness = do_multiscatter != 0.0 ? roughness : transmission_roughness;
in_Refraction_3.ior = ior;
/* Really poor approximation but needed to workaround issues with renderpasses. */
spec_col = mix(vec3(1.0), spec_col, transmission);
/* Match cycles. */
spec_col += float(clearcoat > 1e-5);
vec3 mixed_ss_base_color = mix(diffuse, subsurface_color.rgb, subsurface);
float sss_scalef = avg(sss_scale) * subsurface;
eevee_closure_principled(N,
mixed_ss_base_color,
f0,
/* HACK: Pass the multiscatter flag as the sign to not add closure
* variations or increase register usage. */
(use_multiscatter != 0.0) ? f90 : -f90,
int(ssr_id),
roughness,
CN,
clearcoat * 0.25,
clearcoat_roughness,
1.0,
sss_scalef,
ior,
true,
out_diff,
out_spec,
out_refr,
ssr_spec);
vec3 refr_color = base_color.rgb;
refr_color *= (refractionDepth > 0.0) ? refr_color :
vec3(1.0); /* Simulate 2 transmission event */
refr_color *= saturate(1.0 - fresnel) * transmission;
sheen_color *= m_transmission;
mixed_ss_base_color *= m_transmission;
CLOSURE_EVAL_FUNCTION_4(node_bsdf_principled, Diffuse, Glossy, Glossy, Refraction);
result = CLOSURE_DEFAULT;
result.radiance = render_pass_glossy_mask(refr_color, out_refr * refr_color);
result.radiance += render_pass_glossy_mask(spec_col, out_spec);
/* Coarse approx. */
result.radiance += render_pass_diffuse_mask(sheen_color, out_diff * out_sheen * sheen_color);
result.radiance += render_pass_emission_mask(emission.rgb * emission_strength);
result.radiance *= alpha;
closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result);
mixed_ss_base_color *= alpha;
closure_load_sss_data(sss_scalef, out_diff, mixed_ss_base_color, int(sss_id), result);
result.transmittance = vec3(1.0 - alpha);
}
/* This will tag the whole eval for optimisation. */
if (do_diffuse == 0.0) {
out_Diffuse_0.radiance = vec3(0);
}
if (do_clearcoat == 0.0) {
out_Glossy_2.radiance = vec3(0);
}
if (do_refraction == 0.0) {
out_Refraction_3.radiance = vec3(0);
}
void node_bsdf_principled_dielectric(vec4 base_color,
float subsurface,
vec3 subsurface_radius,
vec4 subsurface_color,
float metallic,
float specular,
float specular_tint,
float roughness,
float anisotropic,
float anisotropic_rotation,
float sheen,
float sheen_tint,
float clearcoat,
float clearcoat_roughness,
float ior,
float transmission,
float transmission_roughness,
vec4 emission,
float emission_strength,
float alpha,
vec3 N,
vec3 CN,
vec3 T,
vec3 I,
float use_multiscatter,
float ssr_id,
float sss_id,
vec3 sss_scale,
out Closure result)
{
N = normalize(N);
metallic = saturate(metallic);
float dielectric = 1.0 - metallic;
/* Glossy_1 will always be evaluated. */
float NV = dot(in_Glossy_1.N, cameraVec);
vec3 diffuse, f0, out_diff, out_spec, ssr_spec, sheen_color;
float out_sheen;
vec3 ctint = tint_from_color(base_color.rgb);
convert_metallic_to_specular_tinted(
base_color.rgb, ctint, metallic, specular, specular_tint, diffuse, f0);
vec3 base_color_tint = tint_from_color(base_color.rgb);
vec3 f90 = mix(vec3(1.0), f0, (1.0 - specular) * metallic);
float fresnel = (do_multiscatter != 0.0) ?
btdf_lut(NV, in_Glossy_1.roughness, in_Refraction_3.ior).y :
F_eta(in_Refraction_3.ior, NV);
float NV = dot(N, cameraVec);
principled_sheen(NV, ctint, sheen, sheen_tint, out_sheen, sheen_color);
{
/* Glossy reflections.
* Separate Glass reflections and main specular reflections to match Cycles renderpasses. */
out_Glossy_1.radiance = closure_mask_ssr_radiance(out_Glossy_1.radiance, ssr_id);
eevee_closure_default(N,
diffuse,
f0,
/* HACK: Pass the multiscatter flag as the sign to not add closure
* variations or increase register usage. */
(use_multiscatter != 0.0) ? f90 : -f90,
int(ssr_id),
roughness,
1.0,
true,
out_diff,
out_spec,
ssr_spec);
vec2 split_sum = brdf_lut(NV, roughness);
result = CLOSURE_DEFAULT;
result.radiance = render_pass_glossy_mask(vec3(1.0), out_spec);
result.radiance += render_pass_diffuse_mask(sheen_color, out_diff * out_sheen * sheen_color);
result.radiance += render_pass_diffuse_mask(diffuse, out_diff * diffuse);
result.radiance += render_pass_emission_mask(emission.rgb * emission_strength);
result.radiance *= alpha;
closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result);
vec3 glossy_radiance_final = vec3(0.0);
if (transmission > 1e-5) {
/* Glass Reflection: Reuse radiance from Glossy1. */
vec3 out_glass_refl_radiance = out_Glossy_1.radiance;
/* Poor approximation since we baked the LUT using a fixed IOR. */
vec3 f0 = mix(vec3(1.0), base_color.rgb, specular_tint);
vec3 f90 = vec3(1);
vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) :
F_brdf_single_scatter(f0, f90, split_sum);
out_glass_refl_radiance *= brdf;
out_glass_refl_radiance = render_pass_glossy_mask(vec3(1), out_glass_refl_radiance);
out_glass_refl_radiance *= fresnel * transmission;
glossy_radiance_final += out_glass_refl_radiance;
}
if (specular_weight > 1e-5) {
vec3 dielectric_f0_color = mix(vec3(1.0), base_color_tint, specular_tint);
vec3 metallic_f0_color = base_color.rgb;
vec3 f0 = mix((0.08 * specular) * dielectric_f0_color, metallic_f0_color, metallic);
/* Cycles does this blending using the microfacet fresnel factor. However, our fresnel
* is already baked inside the split sum LUT. We approximate using by modifying the
* changing the f90 color directly in a non linear fashion. */
vec3 f90 = mix(f0, vec3(1), fast_sqrt(specular));
vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) :
F_brdf_single_scatter(f0, f90, split_sum);
out_Glossy_1.radiance *= brdf;
out_Glossy_1.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_1.radiance);
out_Glossy_1.radiance *= specular_weight;
glossy_radiance_final += out_Glossy_1.radiance;
}
closure_load_ssr_data(
glossy_radiance_final, in_Glossy_1.roughness, in_Glossy_1.N, ssr_id, result);
}
if (diffuse_weight > 1e-5) {
/* Mask over all diffuse radiance. */
out_Diffuse_0.radiance *= diffuse_weight;
/* Sheen Coarse approximation: We reuse the diffuse radiance and just scale it. */
vec3 sheen_color = mix(vec3(1), base_color_tint, sheen_tint);
vec3 out_sheen_radiance = out_Diffuse_0.radiance * principled_sheen(NV);
out_sheen_radiance = render_pass_diffuse_mask(vec3(1), out_sheen_radiance);
out_sheen_radiance *= sheen * sheen_color;
result.radiance += out_sheen_radiance;
/* Diffuse / Subsurface. */
float scale = avg(sss_scale) * subsurface;
closure_load_sss_data(scale, out_Diffuse_0.radiance, in_Diffuse_0.albedo, int(sss_id), result);
}
if (transmission > 1e-5) {
float btdf = (do_multiscatter != 0.0) ?
1.0 :
btdf_lut(NV, in_Refraction_3.roughness, in_Refraction_3.ior).x;
/* TODO(fclem) This could be going to a transmission render pass instead. */
out_Refraction_3.radiance *= btdf;
out_Refraction_3.radiance = render_pass_glossy_mask(vec3(1), out_Refraction_3.radiance);
out_Refraction_3.radiance *= base_color.rgb;
/* Simulate 2nd transmission event. */
out_Refraction_3.radiance *= (refractionDepth > 0.0) ? base_color.rgb : vec3(1);
out_Refraction_3.radiance *= (1.0 - fresnel) * transmission;
result.radiance += out_Refraction_3.radiance;
}
if (clearcoat > 1e-5) {
float NV = dot(in_Glossy_2.N, cameraVec);
vec2 split_sum = brdf_lut(NV, in_Glossy_2.roughness);
vec3 brdf = F_brdf_single_scatter(vec3(0.04), vec3(1.0), split_sum);
out_Glossy_2.radiance *= brdf * clearcoat * 0.25;
out_Glossy_2.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_2.radiance);
result.radiance += out_Glossy_2.radiance;
}
{
vec3 out_emission_radiance = render_pass_emission_mask(emission.rgb);
out_emission_radiance *= emission_strength;
result.radiance += out_emission_radiance;
}
result.transmittance = vec3(1.0 - alpha);
}
void node_bsdf_principled_metallic(vec4 base_color,
float subsurface,
vec3 subsurface_radius,
vec4 subsurface_color,
float metallic,
float specular,
float specular_tint,
float roughness,
float anisotropic,
float anisotropic_rotation,
float sheen,
float sheen_tint,
float clearcoat,
float clearcoat_roughness,
float ior,
float transmission,
float transmission_roughness,
vec4 emission,
float emission_strength,
float alpha,
vec3 N,
vec3 CN,
vec3 T,
vec3 I,
float use_multiscatter,
float ssr_id,
float sss_id,
vec3 sss_scale,
out Closure result)
{
N = normalize(N);
vec3 out_spec, ssr_spec;
vec3 f90 = mix(vec3(1.0), base_color.rgb, (1.0 - specular) * metallic);
eevee_closure_glossy(N,
base_color.rgb,
/* HACK: Pass the multiscatter flag as the sign to not add closure
* variations or increase register usage. */
(use_multiscatter != 0.0) ? f90 : -f90,
int(ssr_id),
roughness,
1.0,
true,
out_spec,
ssr_spec);
result = CLOSURE_DEFAULT;
result.radiance = render_pass_glossy_mask(vec3(1.0), out_spec);
result.radiance += render_pass_emission_mask(emission.rgb * emission_strength);
result.radiance *= alpha;
closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result);
result.transmittance = vec3(1.0 - alpha);
result.ssr_data.rgb *= alpha;
# ifdef USE_SSS
result.sss_irradiance *= alpha;
# endif
}
void node_bsdf_principled_clearcoat(vec4 base_color,
float subsurface,
vec3 subsurface_radius,
vec4 subsurface_color,
float metallic,
float specular,
float specular_tint,
float roughness,
float anisotropic,
float anisotropic_rotation,
float sheen,
float sheen_tint,
float clearcoat,
float clearcoat_roughness,
float ior,
float transmission,
float transmission_roughness,
vec4 emission,
float emission_strength,
float alpha,
vec3 N,
vec3 CN,
vec3 T,
vec3 I,
float use_multiscatter,
float ssr_id,
float sss_id,
vec3 sss_scale,
out Closure result)
{
vec3 out_spec, ssr_spec;
N = normalize(N);
vec3 f90 = mix(vec3(1.0), base_color.rgb, (1.0 - specular) * metallic);
eevee_closure_clearcoat(N,
base_color.rgb,
/* HACK: Pass the multiscatter flag as the sign to not add closure
* variations or increase register usage. */
(use_multiscatter != 0.0) ? f90 : -f90,
int(ssr_id),
roughness,
CN,
clearcoat * 0.25,
clearcoat_roughness,
1.0,
true,
out_spec,
ssr_spec);
/* Match cycles. */
float spec_col = 1.0 + float(clearcoat > 1e-5);
result = CLOSURE_DEFAULT;
result.radiance = render_pass_glossy_mask(vec3(spec_col), out_spec);
result.radiance += render_pass_emission_mask(emission.rgb * emission_strength);
result.radiance *= alpha;
closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result);
result.transmittance = vec3(1.0 - alpha);
}
void node_bsdf_principled_subsurface(vec4 base_color,
float subsurface,
vec3 subsurface_radius,
vec4 subsurface_color,
float metallic,
float specular,
float specular_tint,
float roughness,
float anisotropic,
float anisotropic_rotation,
float sheen,
float sheen_tint,
float clearcoat,
float clearcoat_roughness,
float ior,
float transmission,
float transmission_roughness,
vec4 emission,
float emission_strength,
float alpha,
vec3 N,
vec3 CN,
vec3 T,
vec3 I,
float use_multiscatter,
float ssr_id,
float sss_id,
vec3 sss_scale,
out Closure result)
{
metallic = saturate(metallic);
N = normalize(N);
vec3 diffuse, f0, out_diff, out_spec, ssr_spec, sheen_color;
float out_sheen;
vec3 ctint = tint_from_color(base_color.rgb);
convert_metallic_to_specular_tinted(
base_color.rgb, ctint, metallic, specular, specular_tint, diffuse, f0);
subsurface_color = subsurface_color * (1.0 - metallic);
vec3 mixed_ss_base_color = mix(diffuse, subsurface_color.rgb, subsurface);
float sss_scalef = avg(sss_scale) * subsurface;
float NV = dot(N, cameraVec);
principled_sheen(NV, ctint, sheen, sheen_tint, out_sheen, sheen_color);
vec3 f90 = mix(vec3(1.0), base_color.rgb, (1.0 - specular) * metallic);
eevee_closure_skin(N,
mixed_ss_base_color,
f0,
/* HACK: Pass the multiscatter flag as the sign to not add closure variations
* or increase register usage. */
(use_multiscatter != 0.0) ? f90 : -f90,
int(ssr_id),
roughness,
1.0,
sss_scalef,
true,
out_diff,
out_spec,
ssr_spec);
result = CLOSURE_DEFAULT;
result.radiance = render_pass_glossy_mask(vec3(1.0), out_spec);
result.radiance += render_pass_diffuse_mask(sheen_color, out_diff * out_sheen * sheen_color);
result.radiance += render_pass_emission_mask(emission.rgb * emission_strength);
result.radiance *= alpha;
closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result);
mixed_ss_base_color *= alpha;
closure_load_sss_data(sss_scalef, out_diff, mixed_ss_base_color, int(sss_id), result);
result.transmittance = vec3(1.0 - alpha);
}
void node_bsdf_principled_glass(vec4 base_color,
float subsurface,
vec3 subsurface_radius,
vec4 subsurface_color,
float metallic,
float specular,
float specular_tint,
float roughness,
float anisotropic,
float anisotropic_rotation,
float sheen,
float sheen_tint,
float clearcoat,
float clearcoat_roughness,
float ior,
float transmission,
float transmission_roughness,
vec4 emission,
float emission_strength,
float alpha,
vec3 N,
vec3 CN,
vec3 T,
vec3 I,
float use_multiscatter,
float ssr_id,
float sss_id,
vec3 sss_scale,
out Closure result)
{
ior = max(ior, 1e-5);
N = normalize(N);
vec3 f0, out_spec, out_refr, ssr_spec;
f0 = mix(vec3(1.0), base_color.rgb, specular_tint);
eevee_closure_glass(N,
vec3(1.0),
vec3((use_multiscatter != 0.0) ? 1.0 : -1.0),
int(ssr_id),
roughness,
1.0,
ior,
true,
out_spec,
out_refr,
ssr_spec);
vec3 refr_color = base_color.rgb;
refr_color *= (refractionDepth > 0.0) ? refr_color :
vec3(1.0); /* Simulate 2 transmission events */
float fresnel = F_eta(ior, dot(N, cameraVec));
vec3 spec_col = F_color_blend(ior, fresnel, f0);
spec_col *= fresnel;
refr_color *= (1.0 - fresnel);
ssr_spec *= spec_col;
result = CLOSURE_DEFAULT;
result.radiance = render_pass_glossy_mask(refr_color, out_refr * refr_color);
result.radiance += render_pass_glossy_mask(spec_col, out_spec * spec_col);
result.radiance += render_pass_emission_mask(emission.rgb * emission_strength);
result.radiance *= alpha;
closure_load_ssr_data(ssr_spec * alpha, roughness, N, viewCameraVec, int(ssr_id), result);
result.transmittance = vec3(1.0 - alpha);
}
#else
/* clang-format off */
/* Stub principled because it is not compatible with volumetrics. */
# define node_bsdf_principled(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, result) (result = CLOSURE_DEFAULT)
# define node_bsdf_principled_dielectric(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, result) (result = CLOSURE_DEFAULT)
# define node_bsdf_principled_metallic(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, result) (result = CLOSURE_DEFAULT)
# define node_bsdf_principled_clearcoat(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, result) (result = CLOSURE_DEFAULT)
# define node_bsdf_principled_subsurface(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, result) (result = CLOSURE_DEFAULT)
# define node_bsdf_principled_glass(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, result) (result = CLOSURE_DEFAULT)
# define node_bsdf_principled(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, cc, dd, result) (result = CLOSURE_DEFAULT)
/* clang-format on */
#endif

View File

@@ -1,15 +1,30 @@
#ifndef VOLUMETRICS
CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_refraction, Refraction)
void node_bsdf_refraction(vec4 color, float roughness, float ior, vec3 N, out Closure result)
{
N = normalize(N);
vec3 out_refr;
color.rgb *= (refractionDepth > 0.0) ? color.rgb : vec3(1.0); /* Simulate 2 absorption event. */
eevee_closure_refraction(N, roughness, ior, true, out_refr);
vec3 vN = mat3(ViewMatrix) * N;
CLOSURE_VARS_DECLARE_1(Refraction);
in_Refraction_0.N = N; /* Normalized during eval. */
in_Refraction_0.roughness = roughness;
in_Refraction_0.ior = ior;
CLOSURE_EVAL_FUNCTION_1(node_bsdf_refraction, Refraction);
result = CLOSURE_DEFAULT;
result.ssr_normal = normal_encode(vN, viewCameraVec);
result.radiance = render_pass_glossy_mask(color.rgb, out_refr * color.rgb);
out_Refraction_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Refraction_0.radiance);
out_Refraction_0.radiance *= color.rgb;
/* Simulate 2nd absorption event. */
out_Refraction_0.radiance *= (refractionDepth > 0.0) ? color.rgb : vec3(1.0);
result.radiance = out_Refraction_0.radiance;
/* TODO(fclem) Try to not use this. */
result.ssr_normal = normal_encode(mat3(ViewMatrix) * in_Refraction_0.N, viewCameraVec);
}
#else
/* Stub refraction because it is not compatible with volumetrics. */
# define node_bsdf_refraction(a, b, c, d, e) (e = CLOSURE_DEFAULT)

View File

@@ -1,4 +1,7 @@
#ifndef VOLUMETRICS
CLOSURE_EVAL_FUNCTION_DECLARE_1(node_subsurface_scattering, Diffuse)
void node_subsurface_scattering(vec4 color,
float scale,
vec3 radius,
@@ -8,20 +11,29 @@ void node_subsurface_scattering(vec4 color,
float sss_id,
out Closure result)
{
N = normalize(N);
vec3 out_diff;
vec3 vN = mat3(ViewMatrix) * N;
CLOSURE_VARS_DECLARE_1(Diffuse);
in_Diffuse_0.N = N; /* Normalized during eval. */
in_Diffuse_0.albedo = color.rgb;
CLOSURE_EVAL_FUNCTION_1(node_subsurface_scattering, Diffuse);
result = CLOSURE_DEFAULT;
closure_load_ssr_data(vec3(0.0), 0.0, N, viewCameraVec, -1, result);
eevee_closure_subsurface(N, color.rgb, 1.0, scale, true, out_diff);
/* Not perfect for texture_blur values between 0.0 and 1.0.
* Interpolate between separated color and color applied on irradiance. */
float one_minus_texture_blur = 1.0 - texture_blur;
vec3 sss_albedo = color.rgb * texture_blur + one_minus_texture_blur;
vec3 radiance_tint = color.rgb * one_minus_texture_blur + texture_blur;
/* Consider output radiance as irradiance. */
out_Diffuse_0.radiance *= radiance_tint;
/* Not perfect for texture_blur not exactly equal to 0.0 or 1.0. */
vec3 sss_albedo = mix(color.rgb, vec3(1.0), texture_blur);
out_diff *= mix(vec3(1.0), color.rgb, texture_blur);
result.radiance = render_pass_sss_mask(sss_albedo);
closure_load_sss_data(scale, out_diff, sss_albedo, int(sss_id), result);
closure_load_sss_data(scale, out_Diffuse_0.radiance, sss_albedo, int(sss_id), result);
/* TODO(fclem) Try to not use this. */
closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1, result);
}
#else
/* Stub subsurface scattering because it is not compatible with volumetrics. */
# define node_subsurface_scattering(a, b, c, d, e, f, g, h) (h = CLOSURE_DEFAULT)

View File

@@ -1,12 +1,20 @@
#ifndef VOLUMETRICS
CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_translucent, Translucent)
void node_bsdf_translucent(vec4 color, vec3 N, out Closure result)
{
N = normalize(N);
CLOSURE_VARS_DECLARE_1(Translucent);
in_Translucent_0.N = -N; /* Normalized during eval. */
CLOSURE_EVAL_FUNCTION_1(node_bsdf_translucent, Translucent);
result = CLOSURE_DEFAULT;
eevee_closure_diffuse(-N, color.rgb, 1.0, false, result.radiance);
closure_load_ssr_data(vec3(0.0), 0.0, N, viewCameraVec, -1, result);
result.radiance = render_pass_diffuse_mask(color.rgb, result.radiance * color.rgb);
closure_load_ssr_data(vec3(0.0), 0.0, -in_Translucent_0.N, -1, result);
result.radiance = render_pass_diffuse_mask(color.rgb, out_Translucent_0.radiance * color.rgb);
}
#else
/* Stub translucent because it is not compatible with volumetrics. */
# define node_bsdf_translucent(a, b, c) (c = CLOSURE_DEFAULT)

View File

@@ -185,7 +185,7 @@ typedef struct LightCache {
} LightCache;
/* Bump the version number for lightcache data structure changes. */
#define LIGHTCACHE_STATIC_VERSION 1
#define LIGHTCACHE_STATIC_VERSION 2
/* LightCache->type */
enum {

View File

@@ -134,54 +134,32 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat,
GPU_link(mat, "set_rgb_one", &sss_scale);
}
/* Due to the manual effort done per config, we only optimize the most common permutations. */
char *node_name;
uint flag = 0;
if (!use_subsurf && use_diffuse && !use_refract && !use_clear) {
static char name[] = "node_bsdf_principled_dielectric";
node_name = name;
flag = GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_GLOSSY;
uint flag = GPU_MATFLAG_GLOSSY;
if (use_diffuse) {
flag |= GPU_MATFLAG_DIFFUSE;
}
else if (!use_subsurf && !use_diffuse && !use_refract && !use_clear) {
static char name[] = "node_bsdf_principled_metallic";
node_name = name;
flag = GPU_MATFLAG_GLOSSY;
if (use_refract) {
flag |= GPU_MATFLAG_REFRACT;
}
else if (!use_subsurf && !use_diffuse && !use_refract && use_clear) {
static char name[] = "node_bsdf_principled_clearcoat";
node_name = name;
flag = GPU_MATFLAG_GLOSSY;
}
else if (use_subsurf && use_diffuse && !use_refract && !use_clear) {
static char name[] = "node_bsdf_principled_subsurface";
node_name = name;
flag = GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_GLOSSY;
}
else if (!use_subsurf && !use_diffuse && use_refract && !use_clear && !socket_not_zero(4)) {
static char name[] = "node_bsdf_principled_glass";
node_name = name;
flag = GPU_MATFLAG_GLOSSY | GPU_MATFLAG_REFRACT;
}
else {
static char name[] = "node_bsdf_principled";
node_name = name;
flag = GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_GLOSSY | GPU_MATFLAG_REFRACT;
}
if (use_subsurf) {
flag |= GPU_MATFLAG_SSS;
}
float f_use_diffuse = use_diffuse ? 1.0f : 0.0f;
float f_use_clearcoat = use_clear ? 1.0f : 0.0f;
float f_use_refraction = use_refract ? 1.0f : 0.0f;
float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f;
GPU_material_flag_set(mat, flag);
return GPU_stack_link(mat,
node,
node_name,
"node_bsdf_principled",
in,
out,
GPU_builtin(GPU_VIEW_POSITION),
GPU_constant(&f_use_diffuse),
GPU_constant(&f_use_clearcoat),
GPU_constant(&f_use_refraction),
GPU_constant(&use_multi_scatter),
GPU_constant(&node->ssr_id),
GPU_constant(&node->sss_id),