Eevee: SSR: Refactor multiple rays. Plus other changes...
-Allow a maximum of 4 rays per trace pixel. -Removes parameter Normalize: use normalization all the time now. -Add firefly clamp slider.
This commit is contained in:
parent
7585c82722
commit
27dd82a951
|
@ -710,8 +710,8 @@ class RENDER_PT_eevee_volumetric(RenderButtonsPanel, Panel):
|
|||
col.prop(props, "volumetric_colored_transmittance")
|
||||
|
||||
|
||||
class RENDER_PT_eevee_shading(RenderButtonsPanel, Panel):
|
||||
bl_label = "Shading"
|
||||
class RENDER_PT_eevee_screen_space_reflections(RenderButtonsPanel, Panel):
|
||||
bl_label = "Screen Space Reflections"
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
||||
|
||||
@classmethod
|
||||
|
@ -719,19 +719,23 @@ class RENDER_PT_eevee_shading(RenderButtonsPanel, Panel):
|
|||
scene = context.scene
|
||||
return scene and (scene.render.engine in cls.COMPAT_ENGINES)
|
||||
|
||||
def draw_header(self, context):
|
||||
scene = context.scene
|
||||
props = scene.layer_properties['BLENDER_EEVEE']
|
||||
self.layout.prop(props, "ssr_enable", text="")
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scene = context.scene
|
||||
props = scene.layer_properties['BLENDER_EEVEE']
|
||||
|
||||
col = layout.column()
|
||||
col.prop(props, "ssr_enable")
|
||||
col.prop(props, "ssr_halfres")
|
||||
col.prop(props, "ssr_two_rays")
|
||||
col.prop(props, "ssr_normalize_weight")
|
||||
col.prop(props, "ssr_ray_count")
|
||||
col.prop(props, "ssr_stride")
|
||||
col.prop(props, "ssr_thickness")
|
||||
col.prop(props, "ssr_border_fade")
|
||||
col.prop(props, "ssr_firefly_fac")
|
||||
|
||||
|
||||
classes = (
|
||||
|
@ -751,10 +755,10 @@ classes = (
|
|||
RENDER_PT_bake,
|
||||
RENDER_PT_clay_layer_settings,
|
||||
RENDER_PT_clay_collection_settings,
|
||||
RENDER_PT_eevee_volumetric,
|
||||
RENDER_PT_eevee_screen_space_reflections,
|
||||
RENDER_PT_eevee_poststack_settings,
|
||||
RENDER_PT_eevee_postprocess_settings,
|
||||
RENDER_PT_eevee_volumetric,
|
||||
RENDER_PT_eevee_shading,
|
||||
)
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
|
|
|
@ -250,8 +250,8 @@ class RENDERLAYER_PT_eevee_volumetric(RenderLayerButtonsPanel, Panel):
|
|||
col.template_override_property(layer_props, scene_props, "volumetric_colored_transmittance")
|
||||
|
||||
|
||||
class RENDERLAYER_PT_eevee_shading(RenderLayerButtonsPanel, Panel):
|
||||
bl_label = "Shading"
|
||||
class RENDERLAYER_PT_eevee_screen_space_reflections(RenderLayerButtonsPanel, Panel):
|
||||
bl_label = "Screen Space Reflections"
|
||||
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
||||
|
||||
@classmethod
|
||||
|
@ -259,6 +259,14 @@ class RENDERLAYER_PT_eevee_shading(RenderLayerButtonsPanel, Panel):
|
|||
scene = context.scene
|
||||
return scene and (scene.render.engine in cls.COMPAT_ENGINES)
|
||||
|
||||
def draw_header(self, context):
|
||||
scene = context.scene
|
||||
scene_props = scene.layer_properties['BLENDER_EEVEE']
|
||||
layer = bpy.context.render_layer
|
||||
layer_props = layer.engine_overrides['BLENDER_EEVEE']
|
||||
|
||||
self.layout.template_override_property(layer_props, scene_props, "ssr_enable", text="")
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scene = context.scene
|
||||
|
@ -267,13 +275,12 @@ class RENDERLAYER_PT_eevee_shading(RenderLayerButtonsPanel, Panel):
|
|||
layer_props = layer.engine_overrides['BLENDER_EEVEE']
|
||||
|
||||
col = layout.column()
|
||||
col.template_override_property(layer_props, scene_props, "ssr_enable")
|
||||
col.template_override_property(layer_props, scene_props, "ssr_halfres")
|
||||
col.template_override_property(layer_props, scene_props, "ssr_two_rays")
|
||||
col.template_override_property(layer_props, scene_props, "ssr_normalize_weight")
|
||||
col.template_override_property(layer_props, scene_props, "ssr_ray_count")
|
||||
col.template_override_property(layer_props, scene_props, "ssr_stride")
|
||||
col.template_override_property(layer_props, scene_props, "ssr_thickness")
|
||||
col.template_override_property(layer_props, scene_props, "ssr_border_fade")
|
||||
col.template_override_property(layer_props, scene_props, "ssr_firefly_fac")
|
||||
|
||||
|
||||
classes = (
|
||||
|
@ -284,7 +291,7 @@ classes = (
|
|||
RENDERLAYER_PT_clay_settings,
|
||||
RENDERLAYER_PT_eevee_poststack_settings,
|
||||
RENDERLAYER_PT_eevee_postprocess_settings,
|
||||
RENDERLAYER_PT_eevee_shading,
|
||||
RENDERLAYER_PT_eevee_screen_space_reflections,
|
||||
RENDERLAYER_PT_eevee_volumetric,
|
||||
)
|
||||
|
||||
|
|
|
@ -61,10 +61,8 @@ typedef struct EEVEE_LightProbeData {
|
|||
/* SSR shader variations */
|
||||
enum {
|
||||
SSR_RESOLVE = (1 << 0),
|
||||
SSR_TWO_HIT = (1 << 1),
|
||||
SSR_FULL_TRACE = (1 << 2),
|
||||
SSR_NORMALIZE = (1 << 3),
|
||||
SSR_MAX_SHADER = (1 << 4),
|
||||
SSR_FULL_TRACE = (1 << 1),
|
||||
SSR_MAX_SHADER = (1 << 2),
|
||||
};
|
||||
|
||||
static struct {
|
||||
|
@ -199,15 +197,9 @@ static struct GPUShader *eevee_effects_ssr_shader_get(int options)
|
|||
else {
|
||||
BLI_dynstr_appendf(ds_defines, "#define STEP_RAYTRACE\n");
|
||||
}
|
||||
if (options & SSR_TWO_HIT) {
|
||||
BLI_dynstr_appendf(ds_defines, "#define TWO_HIT\n");
|
||||
}
|
||||
if (options & SSR_FULL_TRACE) {
|
||||
BLI_dynstr_appendf(ds_defines, "#define FULLRES\n");
|
||||
}
|
||||
if (options & SSR_NORMALIZE) {
|
||||
BLI_dynstr_appendf(ds_defines, "#define USE_NORMALIZATION\n");
|
||||
}
|
||||
char *ssr_define_str = BLI_dynstr_get_cstring(ds_defines);
|
||||
BLI_dynstr_free(ds_defines);
|
||||
|
||||
|
@ -586,12 +578,16 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
|
|||
/* Enable double buffering to be able to read previous frame color */
|
||||
effects->enabled_effects |= EFFECT_DOUBLE_BUFFER;
|
||||
|
||||
effects->ssr_use_two_hit = BKE_collection_engine_property_value_get_bool(props, "ssr_two_rays");
|
||||
effects->ssr_ray_count = BKE_collection_engine_property_value_get_int(props, "ssr_ray_count");
|
||||
effects->reflection_trace_full = !BKE_collection_engine_property_value_get_bool(props, "ssr_halfres");
|
||||
effects->ssr_use_normalization = BKE_collection_engine_property_value_get_bool(props, "ssr_normalize_weight");
|
||||
effects->ssr_stride = (float)BKE_collection_engine_property_value_get_int(props, "ssr_stride");
|
||||
effects->ssr_thickness = BKE_collection_engine_property_value_get_float(props, "ssr_thickness");
|
||||
effects->ssr_border_fac = BKE_collection_engine_property_value_get_float(props, "ssr_border_fade");
|
||||
effects->ssr_firefly_fac = BKE_collection_engine_property_value_get_float(props, "ssr_firefly_fac");
|
||||
|
||||
/* Important, can lead to breakage otherwise. */
|
||||
CLAMP(effects->ssr_ray_count, 1, 4);
|
||||
|
||||
const int divisor = (effects->reflection_trace_full) ? 1 : 2;
|
||||
int tracing_res[2] = {(int)viewport_size[0] / divisor, (int)viewport_size[1] / divisor};
|
||||
|
@ -618,10 +614,12 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
|
|||
|
||||
/* Raytracing output */
|
||||
/* TODO try integer format for hit coord to increase precision */
|
||||
DRWFboTexture tex_output[2] = {{&stl->g_data->ssr_hit_output, (effects->ssr_use_two_hit) ? DRW_TEX_RGBA_16 : DRW_TEX_RG_16, DRW_TEX_TEMP},
|
||||
{&stl->g_data->ssr_pdf_output, (effects->ssr_use_two_hit) ? DRW_TEX_RG_16 : DRW_TEX_R_16, DRW_TEX_TEMP}};
|
||||
DRWFboTexture tex_output[4] = {{&stl->g_data->ssr_hit_output[0], DRW_TEX_RGBA_16, DRW_TEX_TEMP},
|
||||
{&stl->g_data->ssr_hit_output[1], DRW_TEX_RGBA_16, DRW_TEX_TEMP},
|
||||
{&stl->g_data->ssr_hit_output[2], DRW_TEX_RGBA_16, DRW_TEX_TEMP},
|
||||
{&stl->g_data->ssr_hit_output[3], DRW_TEX_RGBA_16, DRW_TEX_TEMP}};
|
||||
|
||||
DRW_framebuffer_init(&fbl->screen_tracing_fb, &draw_engine_eevee_type, tracing_res[0], tracing_res[1], tex_output, 2);
|
||||
DRW_framebuffer_init(&fbl->screen_tracing_fb, &draw_engine_eevee_type, tracing_res[0], tracing_res[1], tex_output, effects->ssr_ray_count);
|
||||
|
||||
/* Compute pixel projection matrix */
|
||||
{
|
||||
|
@ -646,8 +644,9 @@ void EEVEE_effects_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
|
|||
DRW_TEXTURE_FREE_SAFE(txl->ssr_normal_input);
|
||||
DRW_TEXTURE_FREE_SAFE(txl->ssr_specrough_input);
|
||||
DRW_FRAMEBUFFER_FREE_SAFE(fbl->screen_tracing_fb);
|
||||
stl->g_data->ssr_hit_output = NULL;
|
||||
stl->g_data->ssr_pdf_output = NULL;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
stl->g_data->ssr_hit_output[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup double buffer so we can access last frame as it was before post processes */
|
||||
|
@ -752,10 +751,7 @@ void EEVEE_effects_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
|
|||
}
|
||||
|
||||
if ((effects->enabled_effects & EFFECT_SSR) != 0) {
|
||||
int options = 0;
|
||||
options |= (effects->ssr_use_two_hit) ? SSR_TWO_HIT : 0;
|
||||
options |= (effects->reflection_trace_full) ? SSR_FULL_TRACE : 0;
|
||||
options |= (effects->ssr_use_normalization) ? SSR_NORMALIZE : 0;
|
||||
int options = (effects->reflection_trace_full) ? SSR_FULL_TRACE : 0;
|
||||
|
||||
struct GPUShader *trace_shader = eevee_effects_ssr_shader_get(options);
|
||||
struct GPUShader *resolve_shader = eevee_effects_ssr_shader_get(SSR_RESOLVE | options);
|
||||
|
@ -769,6 +765,7 @@ void EEVEE_effects_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
|
|||
DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)stl->g_data->viewvecs, 2);
|
||||
DRW_shgroup_uniform_vec2(grp, "ssrParameters", &effects->ssr_stride, 1);
|
||||
DRW_shgroup_uniform_mat4(grp, "PixelProjMatrix", (float *)&e_data.pixelprojmat);
|
||||
DRW_shgroup_uniform_int(grp, "rayCount", &effects->ssr_ray_count, 1);
|
||||
DRW_shgroup_call_add(grp, quad, NULL);
|
||||
|
||||
psl->ssr_resolve = DRW_pass_create("SSR Resolve", DRW_STATE_WRITE_COLOR | DRW_STATE_ADDITIVE);
|
||||
|
@ -778,18 +775,22 @@ void EEVEE_effects_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
|
|||
DRW_shgroup_uniform_buffer(grp, "specroughBuffer", &txl->ssr_specrough_input);
|
||||
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
|
||||
DRW_shgroup_uniform_buffer(grp, "colorBuffer", &txl->color_double_buffer);
|
||||
DRW_shgroup_uniform_buffer(grp, "hitBuffer", &stl->g_data->ssr_hit_output);
|
||||
DRW_shgroup_uniform_buffer(grp, "pdfBuffer", &stl->g_data->ssr_pdf_output);
|
||||
DRW_shgroup_uniform_mat4(grp, "PastViewProjectionMatrix", (float *)stl->g_data->prev_persmat);
|
||||
DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)stl->g_data->viewvecs, 2);
|
||||
DRW_shgroup_uniform_int(grp, "probe_count", &sldata->probes->num_render_cube, 1);
|
||||
DRW_shgroup_uniform_float(grp, "borderFadeFactor", &effects->ssr_border_fac, 1);
|
||||
DRW_shgroup_uniform_float(grp, "lodCubeMax", &sldata->probes->lod_cube_max, 1);
|
||||
DRW_shgroup_uniform_float(grp, "lodPlanarMax", &sldata->probes->lod_planar_max, 1);
|
||||
DRW_shgroup_uniform_float(grp, "fireflyFactor", &effects->ssr_firefly_fac, 1);
|
||||
DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
|
||||
DRW_shgroup_uniform_buffer(grp, "probeCubes", &sldata->probe_pool);
|
||||
DRW_shgroup_uniform_buffer(grp, "probePlanars", &vedata->txl->planar_pool);
|
||||
DRW_shgroup_uniform_buffer(grp, "hitBuffer0", &stl->g_data->ssr_hit_output[0]);
|
||||
DRW_shgroup_uniform_buffer(grp, "hitBuffer1", (effects->ssr_ray_count < 2) ? &stl->g_data->ssr_hit_output[0] : &stl->g_data->ssr_hit_output[1]);
|
||||
DRW_shgroup_uniform_buffer(grp, "hitBuffer2", (effects->ssr_ray_count < 3) ? &stl->g_data->ssr_hit_output[0] : &stl->g_data->ssr_hit_output[2]);
|
||||
DRW_shgroup_uniform_buffer(grp, "hitBuffer3", (effects->ssr_ray_count < 4) ? &stl->g_data->ssr_hit_output[0] : &stl->g_data->ssr_hit_output[3]);
|
||||
DRW_shgroup_uniform_int(grp, "rayCount", &effects->ssr_ray_count, 1);
|
||||
DRW_shgroup_call_add(grp, quad, NULL);
|
||||
}
|
||||
|
||||
|
@ -1068,8 +1069,9 @@ void EEVEE_effects_do_ssr(EEVEE_SceneLayerData *UNUSED(sldata), EEVEE_Data *veda
|
|||
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
|
||||
e_data.depth_src = dtxl->depth;
|
||||
|
||||
DRW_framebuffer_texture_attach(fbl->screen_tracing_fb, stl->g_data->ssr_hit_output, 0, 0);
|
||||
DRW_framebuffer_texture_attach(fbl->screen_tracing_fb, stl->g_data->ssr_pdf_output, 1, 0);
|
||||
for (int i = 0; i < effects->ssr_ray_count; ++i) {
|
||||
DRW_framebuffer_texture_attach(fbl->screen_tracing_fb, stl->g_data->ssr_hit_output[i], i, 0);
|
||||
}
|
||||
DRW_framebuffer_bind(fbl->screen_tracing_fb);
|
||||
|
||||
if (stl->g_data->valid_double_buffer) {
|
||||
|
@ -1077,12 +1079,13 @@ void EEVEE_effects_do_ssr(EEVEE_SceneLayerData *UNUSED(sldata), EEVEE_Data *veda
|
|||
DRW_draw_pass(psl->ssr_raytrace);
|
||||
}
|
||||
else {
|
||||
float clear_col[4] = {-1.0f, -1.0f, -1.0f, -1.0f};
|
||||
float clear_col[4] = {0.0f, 0.0f, -1.0f, 0.001f};
|
||||
DRW_framebuffer_clear(true, false, false, clear_col, 0.0f);
|
||||
}
|
||||
|
||||
DRW_framebuffer_texture_detach(stl->g_data->ssr_hit_output);
|
||||
DRW_framebuffer_texture_detach(stl->g_data->ssr_pdf_output);
|
||||
for (int i = 0; i < effects->ssr_ray_count; ++i) {
|
||||
DRW_framebuffer_texture_detach(stl->g_data->ssr_hit_output[i]);
|
||||
}
|
||||
|
||||
EEVEE_downsample_buffer(vedata, fbl->downsample_fb, txl->color_double_buffer, 9);
|
||||
|
||||
|
@ -1244,24 +1247,21 @@ void EEVEE_draw_effects(EEVEE_Data *vedata)
|
|||
DRW_transform_to_display(effects->source_buffer);
|
||||
|
||||
/* Debug : Ouput buffer to view. */
|
||||
if ((G.debug_value > 0) && (G.debug_value <= 6)) {
|
||||
if ((G.debug_value > 0) && (G.debug_value <= 5)) {
|
||||
switch (G.debug_value) {
|
||||
case 1:
|
||||
if (stl->g_data->minzbuffer) DRW_transform_to_display(stl->g_data->minzbuffer);
|
||||
break;
|
||||
case 2:
|
||||
if (stl->g_data->ssr_hit_output) DRW_transform_to_display(stl->g_data->ssr_hit_output);
|
||||
if (stl->g_data->ssr_hit_output[0]) DRW_transform_to_display(stl->g_data->ssr_hit_output[0]);
|
||||
break;
|
||||
case 3:
|
||||
if (stl->g_data->ssr_pdf_output) DRW_transform_to_display(stl->g_data->ssr_pdf_output);
|
||||
break;
|
||||
case 4:
|
||||
if (txl->ssr_normal_input) DRW_transform_to_display(txl->ssr_normal_input);
|
||||
break;
|
||||
case 5:
|
||||
case 4:
|
||||
if (txl->ssr_specrough_input) DRW_transform_to_display(txl->ssr_specrough_input);
|
||||
break;
|
||||
case 6:
|
||||
case 5:
|
||||
if (txl->color_double_buffer) DRW_transform_to_display(txl->color_double_buffer);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -218,12 +218,12 @@ static void EEVEE_scene_layer_settings_create(RenderEngine *UNUSED(engine), IDPr
|
|||
props->subtype == IDP_GROUP_SUB_ENGINE_RENDER);
|
||||
|
||||
BKE_collection_engine_property_add_bool(props, "ssr_enable", false);
|
||||
BKE_collection_engine_property_add_bool(props, "ssr_two_rays", false);
|
||||
BKE_collection_engine_property_add_bool(props, "ssr_normalize_weight", false);
|
||||
BKE_collection_engine_property_add_bool(props, "ssr_halfres", true);
|
||||
BKE_collection_engine_property_add_int(props, "ssr_ray_count", 1);
|
||||
BKE_collection_engine_property_add_int(props, "ssr_stride", 16);
|
||||
BKE_collection_engine_property_add_float(props, "ssr_thickness", 0.2f);
|
||||
BKE_collection_engine_property_add_float(props, "ssr_border_fade", 0.075f);
|
||||
BKE_collection_engine_property_add_float(props, "ssr_firefly_fac", 0.5f);
|
||||
|
||||
BKE_collection_engine_property_add_bool(props, "volumetric_enable", false);
|
||||
BKE_collection_engine_property_add_float(props, "volumetric_start", 0.1f);
|
||||
|
|
|
@ -1052,7 +1052,6 @@ static void render_scene_to_planar(
|
|||
EEVEE_FramebufferList *fbl = vedata->fbl;
|
||||
EEVEE_TextureList *txl = vedata->txl;
|
||||
EEVEE_PassList *psl = vedata->psl;
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
|
||||
float viewinv[4][4];
|
||||
float persinv[4][4];
|
||||
|
|
|
@ -322,8 +322,9 @@ typedef struct EEVEE_EffectsInfo {
|
|||
/* SSR */
|
||||
bool use_ssr;
|
||||
bool reflection_trace_full;
|
||||
bool ssr_use_two_hit;
|
||||
bool ssr_use_normalization;
|
||||
int ssr_ray_count;
|
||||
float ssr_firefly_fac;
|
||||
float ssr_border_fac;
|
||||
float ssr_stride;
|
||||
float ssr_thickness;
|
||||
|
@ -459,8 +460,7 @@ typedef struct EEVEE_PrivateData {
|
|||
struct GHash *material_hash;
|
||||
struct GHash *hair_material_hash;
|
||||
struct GPUTexture *minzbuffer;
|
||||
struct GPUTexture *ssr_hit_output;
|
||||
struct GPUTexture *ssr_pdf_output;
|
||||
struct GPUTexture *ssr_hit_output[4];
|
||||
struct GPUTexture *volumetric;
|
||||
struct GPUTexture *volumetric_transmit;
|
||||
float background_alpha; /* TODO find a better place for this. */
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
|
||||
/* Based on Stochastic Screen Space Reflections
|
||||
* https://www.ea.com/frostbite/news/stochastic-screen-space-reflections */
|
||||
|
||||
#ifndef UTIL_TEX
|
||||
#define UTIL_TEX
|
||||
uniform sampler2DArray utilTex;
|
||||
#endif /* UTIL_TEX */
|
||||
|
||||
uniform int rayCount;
|
||||
|
||||
#define BRDF_BIAS 0.7
|
||||
|
||||
vec3 generate_ray(vec3 V, vec3 N, float a2, vec3 rand, out float pdf)
|
||||
|
@ -15,6 +20,7 @@ vec3 generate_ray(vec3 V, vec3 N, float a2, vec3 rand, out float pdf)
|
|||
/* Importance sampling bias */
|
||||
rand.x = mix(rand.x, 0.0, BRDF_BIAS);
|
||||
|
||||
/* TODO distribution of visible normals */
|
||||
vec3 H = sample_ggx(rand, a2, N, T, B, NH); /* Microfacet normal */
|
||||
pdf = min(1024e32, pdf_ggx_reflect(NH, a2)); /* Theoretical limit of 16bit float */
|
||||
return reflect(-V, H);
|
||||
|
@ -28,8 +34,34 @@ uniform sampler2D depthBuffer;
|
|||
uniform sampler2D normalBuffer;
|
||||
uniform sampler2D specroughBuffer;
|
||||
|
||||
layout(location = 0) out vec4 hitData;
|
||||
layout(location = 1) out vec4 pdfData;
|
||||
uniform mat4 ViewProjectionMatrix;
|
||||
|
||||
layout(location = 0) out vec4 hitData0;
|
||||
layout(location = 1) out vec4 hitData1;
|
||||
layout(location = 2) out vec4 hitData2;
|
||||
layout(location = 3) out vec4 hitData3;
|
||||
|
||||
bool has_hit_backface(vec3 hit_pos, vec3 R, vec3 V)
|
||||
{
|
||||
vec2 hit_co = project_point(ProjectionMatrix, hit_pos).xy * 0.5 + 0.5;
|
||||
vec3 hit_N = normal_decode(textureLod(normalBuffer, hit_co, 0.0).rg, V);
|
||||
return (dot(-R, hit_N) < 0.0);
|
||||
}
|
||||
|
||||
vec4 do_ssr(sampler2D depthBuffer, vec3 V, vec3 N, vec3 viewPosition, float a2, vec3 rand)
|
||||
{
|
||||
float pdf;
|
||||
vec3 R = generate_ray(V, N, a2, rand, pdf);
|
||||
|
||||
float hit_dist = raycast(depthBuffer, viewPosition, R, rand.x);
|
||||
vec3 hit_pos = viewPosition + R * abs(hit_dist);
|
||||
|
||||
if (has_hit_backface(hit_pos, R, V) || (hit_dist <= 0.0)) {
|
||||
hit_pos.z *= -1.0;
|
||||
}
|
||||
|
||||
return vec4(hit_pos, pdf);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
|
@ -68,81 +100,13 @@ void main()
|
|||
float roughnessSquared = max(1e-3, roughness * roughness);
|
||||
float a2 = roughnessSquared * roughnessSquared;
|
||||
|
||||
/* Generate Ray */
|
||||
float pdf;
|
||||
vec3 rand = texelFetch(utilTex, ivec3(halfres_texel % LUT_SIZE, 2), 0).rba;
|
||||
vec3 R = generate_ray(V, N, a2, rand, pdf);
|
||||
#ifdef TWO_HIT
|
||||
float pdf2;
|
||||
vec3 R2 = generate_ray(V, N, a2, rand * vec3(1.0, -1.0, -1.0), pdf2);
|
||||
#endif
|
||||
|
||||
/* Search for the planar reflection affecting this pixel */
|
||||
/* If no planar is found, fallback to screen space */
|
||||
|
||||
/* Raycast over planar reflection */
|
||||
/* Potentially lots of time waisted here for pixels
|
||||
* that does not have planar reflections. TODO Profile it. */
|
||||
/* TODO: Idea, rasterize boxes around planar
|
||||
* reflection volumes (frontface culling to avoid overdraw)
|
||||
* and do the raycasting, discard pixel that are not in influence.
|
||||
* Add stencil test to discard the main SSR.
|
||||
* Cons: - Potentially raytrace multiple times
|
||||
* if Planar Influence overlaps. */
|
||||
//float hit_dist = raycast(depthBuffer, W, R);
|
||||
|
||||
/* Raycast over screen */
|
||||
float hit_dist = -1.0;
|
||||
#ifdef TWO_HIT
|
||||
float hit_dist2 = -1.0;
|
||||
#endif
|
||||
/* Only raytrace if ray is above the surface normal */
|
||||
/* Note : this still fails in some cases like with normal map.
|
||||
* We should check against the geometric normal but we don't have it at this stage. */
|
||||
if (dot(R, N) > 0.0001) {
|
||||
hit_dist = raycast(depthBuffer, viewPosition, R, rand.x);
|
||||
}
|
||||
#ifdef TWO_HIT
|
||||
/* TODO do double raytrace at the same time */
|
||||
if (dot(R2, N) > 0.0001) {
|
||||
hit_dist2 = raycast(depthBuffer, viewPosition, R2, rand.x);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* TODO Do reprojection here */
|
||||
vec2 hit_co = project_point(ProjectionMatrix, viewPosition + R * hit_dist).xy * 0.5 + 0.5;
|
||||
#ifdef TWO_HIT
|
||||
vec2 hit_co2 = project_point(ProjectionMatrix, viewPosition + R2 * hit_dist2).xy * 0.5 + 0.5;
|
||||
#endif
|
||||
|
||||
/* Check if has hit a backface */
|
||||
vec3 hit_N = normal_decode(textureLod(normalBuffer, hit_co, 0.0).rg, V);
|
||||
hit_dist *= step(0.0, dot(-R, hit_N));
|
||||
#ifdef TWO_HIT
|
||||
hit_N = normal_decode(textureLod(normalBuffer, hit_co2, 0.0).rg, V);
|
||||
hit_dist2 *= step(0.0, dot(-R2, hit_N));
|
||||
#endif
|
||||
|
||||
if (hit_dist > 0.0) {
|
||||
hitData = hit_co.xyxy;
|
||||
}
|
||||
else {
|
||||
hitData = vec4(-1.0);
|
||||
}
|
||||
#ifdef TWO_HIT
|
||||
if (hit_dist2 > 0.0) {
|
||||
hitData.zw = hit_co2;
|
||||
}
|
||||
else {
|
||||
hitData.zw = vec2(-1.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TWO_HIT
|
||||
pdfData = vec4(pdf, pdf2, 0.0, 0.0);
|
||||
#else
|
||||
pdfData = vec4(pdf);
|
||||
#endif
|
||||
/* TODO : Raytrace together if textureGather is supported. */
|
||||
hitData0 = do_ssr(depthBuffer, V, N, viewPosition, a2, rand);
|
||||
if (rayCount > 1) hitData1 = do_ssr(depthBuffer, V, N, viewPosition, a2, rand.xyz * vec3(1.0, -1.0, -1.0));
|
||||
if (rayCount > 2) hitData2 = do_ssr(depthBuffer, V, N, viewPosition, a2, rand.xzy * vec3(1.0, 1.0, -1.0));
|
||||
if (rayCount > 3) hitData3 = do_ssr(depthBuffer, V, N, viewPosition, a2, rand.xzy * vec3(1.0, -1.0, 1.0));
|
||||
}
|
||||
|
||||
#else /* STEP_RESOLVE */
|
||||
|
@ -152,12 +116,15 @@ uniform sampler2D depthBuffer;
|
|||
uniform sampler2D normalBuffer;
|
||||
uniform sampler2D specroughBuffer;
|
||||
|
||||
uniform sampler2D hitBuffer;
|
||||
uniform sampler2D pdfBuffer;
|
||||
uniform sampler2D hitBuffer0;
|
||||
uniform sampler2D hitBuffer1;
|
||||
uniform sampler2D hitBuffer2;
|
||||
uniform sampler2D hitBuffer3;
|
||||
|
||||
uniform int probe_count;
|
||||
|
||||
uniform float borderFadeFactor;
|
||||
uniform float fireflyFactor;
|
||||
|
||||
uniform mat4 ViewProjectionMatrix;
|
||||
uniform mat4 PastViewProjectionMatrix;
|
||||
|
@ -218,19 +185,6 @@ float brightness(vec3 c)
|
|||
return max(max(c.r, c.g), c.b);
|
||||
}
|
||||
|
||||
vec2 get_reprojected_reflection(vec3 hit, vec3 pos, vec3 N)
|
||||
{
|
||||
/* TODO real motion vectors */
|
||||
/* Transform to viewspace */
|
||||
// vec4(get_view_space_from_depth(uvcoords, depth), 1.0);
|
||||
// vec4(get_view_space_from_depth(uvcoords, depth), 1.0);
|
||||
|
||||
/* Reproject */
|
||||
// vec3 hit_reprojected = find_reflection_incident_point(cameraPos, hit, pos, N);
|
||||
|
||||
return project_point(PastViewProjectionMatrix, hit).xy * 0.5 + 0.5;
|
||||
}
|
||||
|
||||
float screen_border_mask(vec2 past_hit_co, vec3 hit)
|
||||
{
|
||||
/* Fade on current and past screen edges */
|
||||
|
@ -254,44 +208,51 @@ float view_facing_mask(vec3 V, vec3 R)
|
|||
return smoothstep(0.95, 0.80, dot(V, R));
|
||||
}
|
||||
|
||||
vec4 get_ssr_sample(
|
||||
vec2 hit_co, vec3 worldPosition, vec3 N, vec3 V, float roughnessSquared,
|
||||
float cone_tan, vec2 source_uvs, vec2 texture_size, ivec2 target_texel,
|
||||
out float weight)
|
||||
vec2 get_reprojected_reflection(vec3 hit, vec3 pos, vec3 N)
|
||||
{
|
||||
/* Reconstruct ray */
|
||||
float hit_depth = textureLod(depthBuffer, hit_co, 0.0).r;
|
||||
vec3 hit_pos = get_world_space_from_depth(hit_co, hit_depth);
|
||||
/* TODO real reprojection with motion vectors, etc... */
|
||||
return project_point(PastViewProjectionMatrix, hit).xy * 0.5 + 0.5;
|
||||
}
|
||||
|
||||
/* Find hit position in previous frame */
|
||||
vec4 get_ssr_sample(
|
||||
sampler2D hitBuffer, vec3 worldPosition, vec3 N, vec3 V, float roughnessSquared,
|
||||
float cone_tan, vec2 source_uvs, vec2 texture_size, ivec2 target_texel,
|
||||
inout float weight_acc)
|
||||
{
|
||||
vec4 hit_co_pdf = texelFetch(hitBuffer, target_texel, 0).rgba;
|
||||
bool has_hit = (hit_co_pdf.z < 0.0);
|
||||
hit_co_pdf.z = -abs(hit_co_pdf.z);
|
||||
|
||||
/* Hit position in world space. */
|
||||
vec3 hit_pos = (ViewMatrixInverse * vec4(hit_co_pdf.xyz, 1.0)).xyz;
|
||||
|
||||
/* Find hit position in previous frame. */
|
||||
vec2 ref_uvs = get_reprojected_reflection(hit_pos, worldPosition, N);
|
||||
|
||||
/* Estimate a cone footprint to sample a corresponding mipmap level */
|
||||
/* compute cone footprint Using UV distance because we are using screen space filtering */
|
||||
/* Estimate a cone footprint to sample a corresponding mipmap level. */
|
||||
/* Compute cone footprint Using UV distance because we are using screen space filtering. */
|
||||
float cone_footprint = 1.5 * cone_tan * distance(ref_uvs, source_uvs);
|
||||
float mip = BRDF_BIAS * clamp(log2(cone_footprint * max(texture_size.x, texture_size.y)), 0.0, MAX_MIP);
|
||||
|
||||
/* Slide 54 */
|
||||
vec3 L = normalize(hit_pos - worldPosition);
|
||||
#ifdef USE_NORMALIZATION
|
||||
/* Evaluate BSDF */
|
||||
float bsdf = bsdf_ggx(N, L, V, roughnessSquared);
|
||||
float pdf = texelFetch(pdfBuffer, target_texel, 0).r;
|
||||
float weight = step(0.001, hit_co_pdf.w) * bsdf / hit_co_pdf.w;
|
||||
weight_acc += weight;
|
||||
|
||||
weight = step(0.001, pdf) * bsdf / pdf;
|
||||
#else
|
||||
weight = 1.0;
|
||||
#endif
|
||||
vec3 sample = textureLod(colorBuffer, ref_uvs, mip).rgb;
|
||||
|
||||
vec3 sample = textureLod(colorBuffer, ref_uvs, mip).rgb ;
|
||||
/* Do not add light if ray has failed. */
|
||||
sample *= float(has_hit);
|
||||
|
||||
/* Firefly removal */
|
||||
sample /= 1.0 + brightness(sample);
|
||||
sample /= 1.0 + fireflyFactor * brightness(sample);
|
||||
|
||||
float mask = screen_border_mask(ref_uvs, hit_pos);
|
||||
mask *= view_facing_mask(V, N);
|
||||
mask *= float(has_hit);
|
||||
|
||||
/* Check if there was a hit */
|
||||
return vec4(sample, mask) * weight * step(0.0, hit_co.x);
|
||||
return vec4(sample, mask) * weight;
|
||||
}
|
||||
|
||||
#define NUM_NEIGHBORS 9
|
||||
|
@ -341,34 +302,40 @@ void main()
|
|||
float weight_acc = 0.0;
|
||||
const ivec2 neighbors[9] = ivec2[9](
|
||||
ivec2(0, 0),
|
||||
ivec2(-1, 1), ivec2(0, 1), ivec2(1, 1),
|
||||
ivec2(-1, 0), ivec2(1, 0),
|
||||
ivec2(-1, -1), ivec2(0, -1), ivec2(1, -1)
|
||||
|
||||
ivec2(0, 1),
|
||||
ivec2(-1, -1), ivec2(1, -1),
|
||||
|
||||
ivec2(-1, 1), ivec2(1, 1),
|
||||
ivec2(0, -1),
|
||||
|
||||
ivec2(-1, 0), ivec2(1, 0)
|
||||
);
|
||||
ivec2 invert_neighbor;
|
||||
invert_neighbor.x = ((fullres_texel.x & 0x1) == 0) ? 1 : -1;
|
||||
invert_neighbor.y = ((fullres_texel.y & 0x1) == 0) ? 1 : -1;
|
||||
|
||||
for (int i = 0; i < NUM_NEIGHBORS; i++) {
|
||||
ivec2 target_texel = halfres_texel + neighbors[i] * invert_neighbor;
|
||||
|
||||
#ifdef TWO_HIT
|
||||
vec4 hit_co = texelFetch(hitBuffer, target_texel, 0).rgba;
|
||||
#else
|
||||
vec2 hit_co = texelFetch(hitBuffer, target_texel, 0).rg;
|
||||
#endif
|
||||
|
||||
float weight;
|
||||
ssr_accum += get_ssr_sample(hit_co.xy, worldPosition, N, V,
|
||||
ssr_accum += get_ssr_sample(hitBuffer0, worldPosition, N, V,
|
||||
roughnessSquared, cone_tan, source_uvs,
|
||||
texture_size, target_texel, weight);
|
||||
weight_acc += weight;
|
||||
|
||||
#ifdef TWO_HIT
|
||||
ssr_accum += get_ssr_sample(hit_co.zw, worldPosition, N, V,
|
||||
roughnessSquared, cone_tan, source_uvs,
|
||||
texture_size, target_texel, weight);
|
||||
weight_acc += weight;
|
||||
#endif
|
||||
texture_size, target_texel, weight_acc);
|
||||
if (rayCount > 1) {
|
||||
ssr_accum += get_ssr_sample(hitBuffer1, worldPosition, N, V,
|
||||
roughnessSquared, cone_tan, source_uvs,
|
||||
texture_size, target_texel, weight_acc);
|
||||
}
|
||||
if (rayCount > 2) {
|
||||
ssr_accum += get_ssr_sample(hitBuffer2, worldPosition, N, V,
|
||||
roughnessSquared, cone_tan, source_uvs,
|
||||
texture_size, target_texel, weight_acc);
|
||||
}
|
||||
if (rayCount > 3) {
|
||||
ssr_accum += get_ssr_sample(hitBuffer3, worldPosition, N, V,
|
||||
roughnessSquared, cone_tan, source_uvs,
|
||||
texture_size, target_texel, weight_acc);
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute SSR contribution */
|
||||
|
@ -385,8 +352,6 @@ void main()
|
|||
}
|
||||
|
||||
fragColor = vec4(spec_accum.rgb * speccol_roughness.rgb, 1.0);
|
||||
// vec2 _uvs = project_point(PastViewProjectionMatrix, worldPosition).xy * 0.5 + 0.5;
|
||||
// fragColor = vec4(textureLod(colorBuffer, _uvs, roughness * MAX_MIP).rgb, 1.0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,15 +21,20 @@ void swapIfBigger(inout float a, inout float b)
|
|||
}
|
||||
}
|
||||
|
||||
/* Return the length of the ray if there is a hit, and -1.0 if not hit occured */
|
||||
/* Return the length of the ray if there is a hit, and negate it if not hit occured */
|
||||
float raycast(sampler2D depth_texture, vec3 ray_origin, vec3 ray_dir, float ray_jitter)
|
||||
{
|
||||
float near = get_view_z_from_depth(0.0); /* TODO optimize */
|
||||
float far = get_view_z_from_depth(1.0); /* TODO optimize */
|
||||
|
||||
/* Clip ray to a near plane in 3D */
|
||||
/* Clip ray to a near/far plane in 3D */
|
||||
float ray_length = 1e16;
|
||||
if ((ray_origin.z + ray_dir.z * ray_length) > near)
|
||||
if ((ray_origin.z + ray_dir.z * ray_length) > near) {
|
||||
ray_length = (near - ray_origin.z) / ray_dir.z;
|
||||
}
|
||||
else {
|
||||
ray_length = (ray_origin.z - far) / -ray_dir.z;
|
||||
}
|
||||
|
||||
vec3 ray_end = ray_dir * ray_length + ray_origin;
|
||||
|
||||
|
@ -136,7 +141,7 @@ float raycast(sampler2D depth_texture, vec3 ray_origin, vec3 ray_dir, float ray_
|
|||
|
||||
prev_zmax = (dPQK.z * -0.5 + pqk.z) / (dPQK.w * -0.5 + pqk.w);
|
||||
|
||||
for (float refinestep = 0.0; refinestep < ssrStride * 2.0 && refinestep < MAX_REFINE_STEP * 2.0; refinestep++) {
|
||||
for (float refinestep = 0.0; refinestep < (ssrStride * 2.0) && refinestep < (MAX_REFINE_STEP * 2.0); refinestep++) {
|
||||
/* step through current cell */
|
||||
pqk += dPQK;
|
||||
|
||||
|
@ -159,9 +164,14 @@ float raycast(sampler2D depth_texture, vec3 ray_origin, vec3 ray_dir, float ray_
|
|||
}
|
||||
}
|
||||
|
||||
/* Background case. */
|
||||
hit = hit && (raw_depth != 1.0);
|
||||
/* If we did hit the background, get exact ray. */
|
||||
if (raw_depth == 1.0) {
|
||||
zmax = get_view_z_from_depth(1.0); /* TODO optimize */
|
||||
}
|
||||
|
||||
hit = hit && (raw_depth != 0.0);
|
||||
|
||||
/* Return length */
|
||||
return (hit) ? (zmax - ray_origin.z) / ray_dir.z : -1.0;
|
||||
float result = (zmax - ray_origin.z) / ray_dir.z;
|
||||
return (hit) ? result : -result;
|
||||
}
|
||||
|
|
|
@ -2623,12 +2623,12 @@ RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(volumetric_shadows)
|
|||
RNA_LAYER_ENGINE_EEVEE_GET_SET_INT(volumetric_shadow_samples)
|
||||
RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(volumetric_colored_transmittance)
|
||||
RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(ssr_enable)
|
||||
RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(ssr_two_rays)
|
||||
RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(ssr_halfres)
|
||||
RNA_LAYER_ENGINE_EEVEE_GET_SET_BOOL(ssr_normalize_weight)
|
||||
RNA_LAYER_ENGINE_EEVEE_GET_SET_INT(ssr_ray_count)
|
||||
RNA_LAYER_ENGINE_EEVEE_GET_SET_INT(ssr_stride)
|
||||
RNA_LAYER_ENGINE_EEVEE_GET_SET_FLOAT(ssr_thickness)
|
||||
RNA_LAYER_ENGINE_EEVEE_GET_SET_FLOAT(ssr_border_fade)
|
||||
RNA_LAYER_ENGINE_EEVEE_GET_SET_FLOAT(ssr_firefly_fac)
|
||||
|
||||
/* object engine */
|
||||
RNA_LAYER_MODE_OBJECT_GET_SET_BOOL(show_wire)
|
||||
|
@ -6198,20 +6198,6 @@ static void rna_def_scene_layer_engine_settings_eevee(BlenderRNA *brna)
|
|||
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update");
|
||||
|
||||
prop = RNA_def_property(srna, "ssr_two_rays", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_funcs(prop, "rna_LayerEngineSettings_Eevee_ssr_two_rays_get",
|
||||
"rna_LayerEngineSettings_Eevee_ssr_two_rays_set");
|
||||
RNA_def_property_ui_text(prop, "Double Trace", "Raytrace two rays instead of just one");
|
||||
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update");
|
||||
|
||||
prop = RNA_def_property(srna, "ssr_normalize_weight", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_funcs(prop, "rna_LayerEngineSettings_Eevee_ssr_normalize_weight_get",
|
||||
"rna_LayerEngineSettings_Eevee_ssr_normalize_weight_set");
|
||||
RNA_def_property_ui_text(prop, "Weight Normalize", "Fills low resolution in reflection but exhibit harsh transition");
|
||||
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update");
|
||||
|
||||
prop = RNA_def_property(srna, "ssr_stride", PROP_INT, PROP_PIXEL);
|
||||
RNA_def_property_int_funcs(prop, "rna_LayerEngineSettings_Eevee_ssr_stride_get",
|
||||
"rna_LayerEngineSettings_Eevee_ssr_stride_set", NULL);
|
||||
|
@ -6220,6 +6206,14 @@ static void rna_def_scene_layer_engine_settings_eevee(BlenderRNA *brna)
|
|||
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update");
|
||||
|
||||
prop = RNA_def_property(srna, "ssr_ray_count", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_int_funcs(prop, "rna_LayerEngineSettings_Eevee_ssr_ray_count_get",
|
||||
"rna_LayerEngineSettings_Eevee_ssr_ray_count_set", NULL);
|
||||
RNA_def_property_ui_text(prop, "Samples", "Number of rays to trace per pixels");
|
||||
RNA_def_property_range(prop, 1, 4);
|
||||
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update");
|
||||
|
||||
prop = RNA_def_property(srna, "ssr_thickness", PROP_FLOAT, PROP_DISTANCE);
|
||||
RNA_def_property_float_funcs(prop, "rna_LayerEngineSettings_Eevee_ssr_thickness_get",
|
||||
"rna_LayerEngineSettings_Eevee_ssr_thickness_set", NULL);
|
||||
|
@ -6237,6 +6231,14 @@ static void rna_def_scene_layer_engine_settings_eevee(BlenderRNA *brna)
|
|||
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update");
|
||||
|
||||
prop = RNA_def_property(srna, "ssr_firefly_fac", PROP_FLOAT, PROP_FACTOR);
|
||||
RNA_def_property_float_funcs(prop, "rna_LayerEngineSettings_Eevee_ssr_firefly_fac_get",
|
||||
"rna_LayerEngineSettings_Eevee_ssr_firefly_fac_set", NULL);
|
||||
RNA_def_property_ui_text(prop, "Clamp", "Smoothly clamp pixel intensity to remove noise");
|
||||
RNA_def_property_range(prop, 0.0f, 1.0f);
|
||||
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_SceneLayerEngineSettings_update");
|
||||
|
||||
/* Volumetrics */
|
||||
prop = RNA_def_property(srna, "volumetric_enable", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_funcs(prop, "rna_LayerEngineSettings_Eevee_volumetric_enable_get",
|
||||
|
|
Loading…
Reference in New Issue