Compare commits
98 Commits
eevee-clos
...
eevee-dof-
Author | SHA1 | Date | |
---|---|---|---|
7769b89bab | |||
cccf7387a8 | |||
c5873c7828 | |||
d02e644060 | |||
54d6e071ab | |||
7bc49f68a9 | |||
8a6e07e42c | |||
aeaa549948 | |||
c4c1b2addc | |||
d447a5eab3 | |||
376bffae03 | |||
c96b1e4cda | |||
f12338f3ce | |||
e6d0a5be0e | |||
7d6f58271b | |||
5db9a008b9 | |||
099788c3e8 | |||
e58ef7db5b | |||
9c4dbb592b | |||
57fc9f6082 | |||
fc6b04d4e3 | |||
4b04818d79 | |||
c8b324fe90 | |||
3c84851142 | |||
1f14c0d20c | |||
24f09b8f55 | |||
e36f3da31c | |||
3bb970f75a | |||
ff74136da9 | |||
81235000d4 | |||
61e5c665b7 | |||
e1f2db669c | |||
a6ca6f5af7 | |||
a8e3293b81 | |||
cfa97fd7f7 | |||
f48adcca64 | |||
9457a0faf5 | |||
eedf6c6295 | |||
6e32018499 | |||
89d9f5983c | |||
06e080c712 | |||
b036baa60a | |||
2c948ea405 | |||
874665d5e6 | |||
556381c84a | |||
64612d57e2 | |||
18ea15cf2d | |||
9cd4618390 | |||
c9c79aad9e | |||
6fff85b2e4 | |||
fea585ded4 | |||
e7e4b3c6af | |||
5841c1d819 | |||
f91aeaedb1 | |||
d7365bfdd5 | |||
9135370633 | |||
53ab14ffed | |||
0b55a9f886 | |||
55936c060b | |||
6c6431809d | |||
3f0213d929 | |||
bf3875d6ac | |||
e137695436 | |||
8d78831fbd | |||
3203340168 | |||
a9e12e50ae | |||
e623e62d3e | |||
928aee934f | |||
f38479f9b4 | |||
440c348323 | |||
6650310844 | |||
6a655eb871 | |||
c8067f798f | |||
371312cf2c | |||
b37fac5ff4 | |||
ec909e5d0f | |||
fee0c90127 | |||
42732568d5 | |||
b94f1a63f1 | |||
8f112af609 | |||
0c732382ae | |||
643420186c | |||
4f3fb67c43 | |||
e267afebf4 | |||
e69f8483ed | |||
9f658822f9 | |||
4f6ce899d4 | |||
63e898a0ff | |||
239229c390 | |||
14e6f2f4db | |||
34de342257 | |||
f071e41d63 | |||
dacce1d07f | |||
2b2e65855a | |||
ef2ed3d737 | |||
3a62ab73a3 | |||
69b667d14e | |||
144d9b901e |
@@ -198,8 +198,15 @@ class RENDER_PT_eevee_depth_of_field(RenderButtonsPanel, Panel):
|
||||
|
||||
col = layout.column()
|
||||
col.prop(props, "bokeh_max_size")
|
||||
# Not supported yet
|
||||
# col.prop(props, "bokeh_threshold")
|
||||
col.prop(props, "bokeh_threshold")
|
||||
col.prop(props, "bokeh_neighbor_max")
|
||||
col.prop(props, "bokeh_denoise_fac")
|
||||
col.prop(props, "use_bokeh_high_quality_slight_defocus")
|
||||
col.prop(props, "use_bokeh_jittered")
|
||||
|
||||
col = layout.column()
|
||||
col.active = props.use_bokeh_jittered
|
||||
col.prop(props, "bokeh_overblur")
|
||||
|
||||
|
||||
class RENDER_PT_eevee_bloom(RenderButtonsPanel, Panel):
|
||||
|
@@ -1670,6 +1670,14 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
||||
* \note Keep this message at the bottom of the function.
|
||||
*/
|
||||
{
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "float", "bokeh_overblur")) {
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
scene->eevee.bokeh_neighbor_max = 10.0f;
|
||||
scene->eevee.bokeh_denoise_fac = 0.75f;
|
||||
scene->eevee.bokeh_overblur = 5.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep this block, even when empty. */
|
||||
}
|
||||
}
|
||||
|
@@ -197,6 +197,7 @@ 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)
|
||||
@@ -214,15 +215,20 @@ 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_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/closure_lit_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)
|
||||
data_to_c_simple(engines/eevee/shaders/effect_dof_bokeh_frag.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/effect_dof_dilate_tiles_frag.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/effect_dof_downsample_frag.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/effect_dof_filter_frag.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/effect_dof_flatten_tiles_frag.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/effect_dof_gather_frag.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/effect_dof_lib.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/effect_dof_reduce_frag.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/effect_dof_resolve_frag.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/effect_dof_scatter_frag.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/effect_dof_scatter_vert.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/effect_dof_setup_frag.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/effect_downsample_frag.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/effect_downsample_cube_frag.glsl SRC)
|
||||
data_to_c_simple(engines/eevee/shaders/effect_gtao_frag.glsl SRC)
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -102,11 +102,9 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata,
|
||||
effects->enabled_effects |= EEVEE_occlusion_init(sldata, vedata);
|
||||
effects->enabled_effects |= EEVEE_screen_raytrace_init(sldata, vedata);
|
||||
|
||||
if ((effects->enabled_effects & EFFECT_TAA) && effects->taa_current_sample > 1) {
|
||||
/* Update matrices here because EEVEE_screen_raytrace_init can have reset the
|
||||
* taa_current_sample. (See T66811) */
|
||||
EEVEE_temporal_sampling_update_matrices(vedata);
|
||||
}
|
||||
|
||||
EEVEE_volumes_init(sldata, vedata);
|
||||
EEVEE_subsurface_init(sldata, vedata);
|
||||
|
@@ -456,13 +456,17 @@ static void eevee_render_to_image(void *vedata,
|
||||
}
|
||||
EEVEE_PrivateData *g_data = ved->stl->g_data;
|
||||
|
||||
EEVEE_render_modules_init(vedata, engine, depsgraph);
|
||||
|
||||
int initial_frame = CFRA;
|
||||
float initial_subframe = SUBFRA;
|
||||
float shuttertime = (do_motion_blur) ? scene->eevee.motion_blur_shutter : 0.0f;
|
||||
int time_steps_tot = (do_motion_blur) ? max_ii(1, scene->eevee.motion_blur_steps) : 1;
|
||||
g_data->render_tot_samples = divide_ceil_u(scene->eevee.taa_render_samples, time_steps_tot);
|
||||
g_data->render_timesteps = time_steps_tot;
|
||||
|
||||
EEVEE_render_modules_init(vedata, engine, depsgraph);
|
||||
|
||||
g_data->render_sample_count_per_timestep = EEVEE_temporal_sampling_sample_count_get(scene,
|
||||
ved->stl);
|
||||
|
||||
/* Compute start time. The motion blur will cover `[time ...time + shuttertime]`. */
|
||||
float time = initial_frame + initial_subframe;
|
||||
switch (scene->eevee.motion_blur_position) {
|
||||
@@ -553,7 +557,8 @@ static void eevee_render_to_image(void *vedata,
|
||||
|
||||
/* Actual drawing. */
|
||||
{
|
||||
EEVEE_renderpasses_output_init(sldata, vedata, g_data->render_tot_samples * time_steps_tot);
|
||||
EEVEE_renderpasses_output_init(
|
||||
sldata, vedata, g_data->render_sample_count_per_timestep * time_steps_tot);
|
||||
|
||||
EEVEE_temporal_sampling_create_view(vedata);
|
||||
EEVEE_render_draw(vedata, engine, render_layer, rect);
|
||||
|
@@ -55,6 +55,9 @@ extern struct DrawEngineType draw_engine_eevee_type;
|
||||
#define MAX_BLOOM_STEP 16
|
||||
#define MAX_AOVS 64
|
||||
|
||||
/* Special value chosen to not be altered by depth of field sample count. */
|
||||
#define TAA_MAX_SAMPLE 10000926
|
||||
|
||||
// #define DEBUG_SHADOW_DISTRIBUTION
|
||||
|
||||
/* Only define one of these. */
|
||||
@@ -206,6 +209,28 @@ typedef enum EEVEE_SSRShaderOptions {
|
||||
SSR_MAX_SHADER = (1 << 4),
|
||||
} EEVEE_SSRShaderOptions;
|
||||
|
||||
/* DOF Gather pass shader variations */
|
||||
typedef enum EEVEE_DofGatherPass {
|
||||
DOF_GATHER_FOREGROUND = 0,
|
||||
DOF_GATHER_BACKGROUND = 1,
|
||||
DOF_GATHER_HOLEFILL = 2,
|
||||
|
||||
DOF_GATHER_MAX_PASS,
|
||||
} EEVEE_DofGatherPass;
|
||||
|
||||
#define DOF_TILE_DIVISOR 16
|
||||
#define DOF_BOKEH_LUT_SIZE 32
|
||||
#define DOF_GATHER_RING_COUNT 5
|
||||
#define DOF_DILATE_RING_COUNT 3
|
||||
#define DOF_FAST_GATHER_COC_ERROR 0.05
|
||||
|
||||
#define DOF_SHADER_DEFINES \
|
||||
"#define DOF_TILE_DIVISOR " STRINGIFY(DOF_TILE_DIVISOR) "\n" \
|
||||
"#define DOF_BOKEH_LUT_SIZE " STRINGIFY(DOF_BOKEH_LUT_SIZE) "\n" \
|
||||
"#define DOF_GATHER_RING_COUNT " STRINGIFY(DOF_GATHER_RING_COUNT) "\n" \
|
||||
"#define DOF_DILATE_RING_COUNT " STRINGIFY(DOF_DILATE_RING_COUNT) "\n" \
|
||||
"#define DOF_FAST_GATHER_COC_ERROR " STRINGIFY(DOF_FAST_GATHER_COC_ERROR) "\n"
|
||||
|
||||
/* ************ PROBE UBO ************* */
|
||||
|
||||
/* They are the same struct as their Cache siblings.
|
||||
@@ -258,8 +283,20 @@ typedef struct EEVEE_PassList {
|
||||
struct DRWPass *bloom_upsample;
|
||||
struct DRWPass *bloom_resolve;
|
||||
struct DRWPass *bloom_accum_ps;
|
||||
struct DRWPass *dof_down;
|
||||
struct DRWPass *dof_scatter;
|
||||
struct DRWPass *dof_setup;
|
||||
struct DRWPass *dof_flatten_tiles;
|
||||
struct DRWPass *dof_dilate_tiles_minmax;
|
||||
struct DRWPass *dof_dilate_tiles_minabs;
|
||||
struct DRWPass *dof_reduce_copy;
|
||||
struct DRWPass *dof_downsample;
|
||||
struct DRWPass *dof_reduce;
|
||||
struct DRWPass *dof_bokeh;
|
||||
struct DRWPass *dof_gather_fg;
|
||||
struct DRWPass *dof_gather_fg_holefill;
|
||||
struct DRWPass *dof_gather_bg;
|
||||
struct DRWPass *dof_scatter_fg;
|
||||
struct DRWPass *dof_scatter_bg;
|
||||
struct DRWPass *dof_filter;
|
||||
struct DRWPass *dof_resolve;
|
||||
struct DRWPass *volumetric_world_ps;
|
||||
struct DRWPass *volumetric_objects_ps;
|
||||
@@ -339,8 +376,20 @@ typedef struct EEVEE_FramebufferList {
|
||||
struct GPUFrameBuffer *sss_clear_fb;
|
||||
struct GPUFrameBuffer *sss_translucency_fb;
|
||||
struct GPUFrameBuffer *sss_accum_fb;
|
||||
struct GPUFrameBuffer *dof_down_fb;
|
||||
struct GPUFrameBuffer *dof_scatter_fb;
|
||||
struct GPUFrameBuffer *dof_setup_fb;
|
||||
struct GPUFrameBuffer *dof_flatten_tiles_fb;
|
||||
struct GPUFrameBuffer *dof_dilate_tiles_fb;
|
||||
struct GPUFrameBuffer *dof_downsample_fb;
|
||||
struct GPUFrameBuffer *dof_reduce_fb;
|
||||
struct GPUFrameBuffer *dof_reduce_copy_fb;
|
||||
struct GPUFrameBuffer *dof_bokeh_fb;
|
||||
struct GPUFrameBuffer *dof_gather_fg_fb;
|
||||
struct GPUFrameBuffer *dof_filter_fg_fb;
|
||||
struct GPUFrameBuffer *dof_gather_fg_holefill_fb;
|
||||
struct GPUFrameBuffer *dof_gather_bg_fb;
|
||||
struct GPUFrameBuffer *dof_filter_bg_fb;
|
||||
struct GPUFrameBuffer *dof_scatter_fg_fb;
|
||||
struct GPUFrameBuffer *dof_scatter_bg_fb;
|
||||
struct GPUFrameBuffer *volumetric_fb;
|
||||
struct GPUFrameBuffer *volumetric_scat_fb;
|
||||
struct GPUFrameBuffer *volumetric_integ_fb;
|
||||
@@ -390,6 +439,9 @@ typedef struct EEVEE_TextureList {
|
||||
struct GPUTexture *cryptomatte;
|
||||
struct GPUTexture *refract_color;
|
||||
struct GPUTexture *taa_history;
|
||||
/* Could not be pool texture because of mipmapping. */
|
||||
struct GPUTexture *dof_reduced_color;
|
||||
struct GPUTexture *dof_reduced_coc;
|
||||
|
||||
struct GPUTexture *volume_prop_scattering;
|
||||
struct GPUTexture *volume_prop_extinction;
|
||||
@@ -727,16 +779,45 @@ typedef struct EEVEE_EffectsInfo {
|
||||
struct GPUTexture *velocity_tiles_x_tx;
|
||||
struct GPUTexture *velocity_tiles_tx;
|
||||
/* Depth Of Field */
|
||||
float dof_near_far[2];
|
||||
float dof_params[2];
|
||||
float dof_bokeh[4];
|
||||
float dof_bokeh_sides[4];
|
||||
int dof_target_size[2];
|
||||
struct GPUTexture *dof_down_near; /* Textures from pool */
|
||||
struct GPUTexture *dof_down_far;
|
||||
struct GPUTexture *dof_coc;
|
||||
struct GPUTexture *dof_blur;
|
||||
struct GPUTexture *dof_blur_alpha;
|
||||
float dof_jitter_radius;
|
||||
float dof_jitter_blades;
|
||||
float dof_jitter_focus;
|
||||
int dof_jitter_ring_count;
|
||||
float dof_coc_params[2], dof_coc_near_dist, dof_coc_far_dist;
|
||||
float dof_bokeh_blades, dof_bokeh_rotation, dof_bokeh_aniso[2], dof_bokeh_max_size;
|
||||
float dof_bokeh_aniso_inv[2];
|
||||
float dof_scatter_color_threshold;
|
||||
float dof_scatter_coc_threshold;
|
||||
float dof_scatter_neighbor_max_color;
|
||||
float dof_fx_max_coc;
|
||||
float dof_denoise_factor;
|
||||
int dof_dilate_slight_focus;
|
||||
int dof_dilate_ring_count;
|
||||
int dof_dilate_ring_width_multiplier;
|
||||
int dof_reduce_steps;
|
||||
bool dof_hq_slight_focus;
|
||||
eGPUTextureFormat dof_color_format;
|
||||
struct GPUTexture *dof_bg_color_tx; /* All textures from pool... */
|
||||
struct GPUTexture *dof_bg_occlusion_tx;
|
||||
struct GPUTexture *dof_bg_weight_tx;
|
||||
struct GPUTexture *dof_bokeh_gather_lut_tx;
|
||||
struct GPUTexture *dof_bokeh_scatter_lut_tx;
|
||||
struct GPUTexture *dof_bokeh_resolve_lut_tx;
|
||||
struct GPUTexture *dof_coc_dilated_tiles_bg_tx;
|
||||
struct GPUTexture *dof_coc_dilated_tiles_fg_tx;
|
||||
struct GPUTexture *dof_coc_tiles_bg_tx;
|
||||
struct GPUTexture *dof_coc_tiles_fg_tx;
|
||||
struct GPUTexture *dof_downsample_tx;
|
||||
struct GPUTexture *dof_fg_color_tx;
|
||||
struct GPUTexture *dof_fg_occlusion_tx;
|
||||
struct GPUTexture *dof_fg_weight_tx;
|
||||
struct GPUTexture *dof_fg_holefill_color_tx;
|
||||
struct GPUTexture *dof_fg_holefill_weight_tx;
|
||||
struct GPUTexture *dof_half_res_coc_tx;
|
||||
struct GPUTexture *dof_half_res_color_tx;
|
||||
struct GPUTexture *dof_scatter_src_tx;
|
||||
struct GPUTexture *dof_reduce_input_coc_tx; /* Just references to actual textures. */
|
||||
struct GPUTexture *dof_reduce_input_color_tx;
|
||||
/* Alpha Checker */
|
||||
float color_checker_dark[4];
|
||||
float color_checker_light[4];
|
||||
@@ -1002,7 +1083,8 @@ typedef struct EEVEE_PrivateData {
|
||||
/** For rendering planar reflections. */
|
||||
struct DRWView *planar_views[MAX_PLANAR];
|
||||
|
||||
int render_tot_samples;
|
||||
int render_timesteps;
|
||||
int render_sample_count_per_timestep;
|
||||
} EEVEE_PrivateData; /* Transient data */
|
||||
|
||||
/* eevee_data.c */
|
||||
@@ -1110,9 +1192,16 @@ struct GPUShader *EEVEE_shaders_bloom_blit_get(bool high_quality);
|
||||
struct GPUShader *EEVEE_shaders_bloom_downsample_get(bool high_quality);
|
||||
struct GPUShader *EEVEE_shaders_bloom_upsample_get(bool high_quality);
|
||||
struct GPUShader *EEVEE_shaders_bloom_resolve_get(bool high_quality);
|
||||
struct GPUShader *EEVEE_shaders_depth_of_field_downsample_get(bool use_alpha);
|
||||
struct GPUShader *EEVEE_shaders_depth_of_field_scatter_get(bool use_alpha);
|
||||
struct GPUShader *EEVEE_shaders_depth_of_field_resolve_get(bool use_alpha);
|
||||
struct GPUShader *EEVEE_shaders_depth_of_field_bokeh_get(void);
|
||||
struct GPUShader *EEVEE_shaders_depth_of_field_setup_get(void);
|
||||
struct GPUShader *EEVEE_shaders_depth_of_field_flatten_tiles_get(void);
|
||||
struct GPUShader *EEVEE_shaders_depth_of_field_dilate_tiles_get(bool pass);
|
||||
struct GPUShader *EEVEE_shaders_depth_of_field_downsample_get(void);
|
||||
struct GPUShader *EEVEE_shaders_depth_of_field_reduce_get(bool is_copy_pass);
|
||||
struct GPUShader *EEVEE_shaders_depth_of_field_gather_get(EEVEE_DofGatherPass pass, bool bokeh_tx);
|
||||
struct GPUShader *EEVEE_shaders_depth_of_field_filter_get(void);
|
||||
struct GPUShader *EEVEE_shaders_depth_of_field_scatter_get(bool is_foreground, bool bokeh_tx);
|
||||
struct GPUShader *EEVEE_shaders_depth_of_field_resolve_get(bool use_bokeh_tx, bool use_hq_gather);
|
||||
struct GPUShader *EEVEE_shaders_effect_downsample_sh_get(void);
|
||||
struct GPUShader *EEVEE_shaders_effect_downsample_cube_sh_get(void);
|
||||
struct GPUShader *EEVEE_shaders_effect_minz_downlevel_sh_get(void);
|
||||
@@ -1232,6 +1321,12 @@ void EEVEE_lightprobes_planar_data_from_object(Object *ob,
|
||||
int EEVEE_depth_of_field_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *camera);
|
||||
void EEVEE_depth_of_field_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
|
||||
void EEVEE_depth_of_field_draw(EEVEE_Data *vedata);
|
||||
bool EEVEE_depth_of_field_jitter_get(EEVEE_EffectsInfo *effects,
|
||||
float r_jitter[2],
|
||||
float *r_focus_distance);
|
||||
int EEVEE_depth_of_field_sample_count_get(EEVEE_EffectsInfo *effects,
|
||||
int sample_count,
|
||||
int *r_ring_count);
|
||||
|
||||
/* eevee_bloom.c */
|
||||
int EEVEE_bloom_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
|
||||
@@ -1345,6 +1440,7 @@ int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov);
|
||||
/* eevee_temporal_sampling.c */
|
||||
void EEVEE_temporal_sampling_reset(EEVEE_Data *vedata);
|
||||
void EEVEE_temporal_sampling_create_view(EEVEE_Data *vedata);
|
||||
int EEVEE_temporal_sampling_sample_count_get(const Scene *scene, const EEVEE_StorageList *stl);
|
||||
int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
|
||||
void EEVEE_temporal_sampling_offset_calc(const double ht_point[2],
|
||||
const float filter_size,
|
||||
|
@@ -563,7 +563,7 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl
|
||||
/* Sort transparents before the loop. */
|
||||
DRW_pass_sort_shgroup_z(psl->transparent_pass);
|
||||
|
||||
uint tot_sample = stl->g_data->render_tot_samples;
|
||||
uint tot_sample = stl->g_data->render_sample_count_per_timestep;
|
||||
uint render_samples = 0;
|
||||
|
||||
/* SSR needs one iteration to start properly. */
|
||||
|
@@ -191,12 +191,6 @@ 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);
|
||||
@@ -204,6 +198,7 @@ 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);
|
||||
}
|
||||
|
||||
|
@@ -77,9 +77,16 @@ static struct {
|
||||
struct GPUShader *bloom_resolve_sh[2];
|
||||
|
||||
/* Depth Of Field */
|
||||
struct GPUShader *dof_downsample_sh[2];
|
||||
struct GPUShader *dof_scatter_sh[2];
|
||||
struct GPUShader *dof_resolve_sh[2];
|
||||
struct GPUShader *dof_bokeh_sh;
|
||||
struct GPUShader *dof_setup_sh;
|
||||
struct GPUShader *dof_flatten_tiles_sh;
|
||||
struct GPUShader *dof_dilate_tiles_sh[2];
|
||||
struct GPUShader *dof_downsample_sh;
|
||||
struct GPUShader *dof_reduce_sh[2];
|
||||
struct GPUShader *dof_gather_sh[DOF_GATHER_MAX_PASS][2];
|
||||
struct GPUShader *dof_filter_sh;
|
||||
struct GPUShader *dof_scatter_sh[2][2];
|
||||
struct GPUShader *dof_resolve_sh[2][2];
|
||||
|
||||
/* General purpose Shaders. */
|
||||
struct GPUShader *lookdev_background;
|
||||
@@ -142,6 +149,7 @@ 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;
|
||||
@@ -183,7 +191,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_type_lib_glsl[];
|
||||
extern char datatoc_closure_lib_glsl[];
|
||||
extern char datatoc_common_uniforms_lib_glsl[];
|
||||
extern char datatoc_common_utiltex_lib_glsl[];
|
||||
extern char datatoc_cryptomatte_frag_glsl[];
|
||||
@@ -191,8 +199,18 @@ extern char datatoc_cubemap_lib_glsl[];
|
||||
extern char datatoc_default_frag_glsl[];
|
||||
extern char datatoc_lookdev_world_frag_glsl[];
|
||||
extern char datatoc_effect_bloom_frag_glsl[];
|
||||
extern char datatoc_effect_dof_frag_glsl[];
|
||||
extern char datatoc_effect_dof_vert_glsl[];
|
||||
extern char datatoc_effect_dof_bokeh_frag_glsl[];
|
||||
extern char datatoc_effect_dof_dilate_tiles_frag_glsl[];
|
||||
extern char datatoc_effect_dof_downsample_frag_glsl[];
|
||||
extern char datatoc_effect_dof_filter_frag_glsl[];
|
||||
extern char datatoc_effect_dof_flatten_tiles_frag_glsl[];
|
||||
extern char datatoc_effect_dof_gather_frag_glsl[];
|
||||
extern char datatoc_effect_dof_lib_glsl[];
|
||||
extern char datatoc_effect_dof_reduce_frag_glsl[];
|
||||
extern char datatoc_effect_dof_resolve_frag_glsl[];
|
||||
extern char datatoc_effect_dof_scatter_frag_glsl[];
|
||||
extern char datatoc_effect_dof_scatter_vert_glsl[];
|
||||
extern char datatoc_effect_dof_setup_frag_glsl[];
|
||||
extern char datatoc_effect_downsample_cube_frag_glsl[];
|
||||
extern char datatoc_effect_downsample_frag_glsl[];
|
||||
extern char datatoc_effect_gtao_frag_glsl[];
|
||||
@@ -223,11 +241,7 @@ 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_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_closure_lit_lib_glsl[];
|
||||
extern char datatoc_ltc_lib_glsl[];
|
||||
extern char datatoc_object_motion_frag_glsl[];
|
||||
extern char datatoc_object_motion_vert_glsl[];
|
||||
@@ -282,13 +296,24 @@ 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);
|
||||
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);
|
||||
DRW_SHADER_LIB_ADD(e_data.lib, effect_dof_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");
|
||||
|
||||
e_data.surface_lit_frag = DRW_shader_library_create_shader_string(e_data.lib,
|
||||
datatoc_surface_frag_glsl);
|
||||
@@ -1003,48 +1028,172 @@ GPUShader *EEVEE_shaders_bloom_resolve_get(bool high_quality)
|
||||
/** \name Depth of field
|
||||
* \{ */
|
||||
|
||||
GPUShader *EEVEE_shaders_depth_of_field_downsample_get(bool use_alpha)
|
||||
GPUShader *EEVEE_shaders_depth_of_field_bokeh_get(void)
|
||||
{
|
||||
int index = use_alpha ? 1 : 0;
|
||||
if (e_data.dof_downsample_sh[index] == NULL) {
|
||||
e_data.dof_downsample_sh[index] = DRW_shader_create_fullscreen_with_shaderlib(
|
||||
datatoc_effect_dof_frag_glsl,
|
||||
e_data.lib,
|
||||
use_alpha ? "#define USE_ALPHA_DOF\n"
|
||||
"#define STEP_DOWNSAMPLE\n" :
|
||||
"#define STEP_DOWNSAMPLE\n");
|
||||
if (e_data.dof_bokeh_sh == NULL) {
|
||||
e_data.dof_bokeh_sh = DRW_shader_create_fullscreen_with_shaderlib(
|
||||
datatoc_effect_dof_bokeh_frag_glsl, e_data.lib, DOF_SHADER_DEFINES);
|
||||
}
|
||||
return e_data.dof_downsample_sh[index];
|
||||
return e_data.dof_bokeh_sh;
|
||||
}
|
||||
|
||||
GPUShader *EEVEE_shaders_depth_of_field_scatter_get(bool use_alpha)
|
||||
GPUShader *EEVEE_shaders_depth_of_field_setup_get(void)
|
||||
{
|
||||
int index = use_alpha ? 1 : 0;
|
||||
if (e_data.dof_scatter_sh[index] == NULL) {
|
||||
e_data.dof_scatter_sh[index] = DRW_shader_create_with_shaderlib(datatoc_effect_dof_vert_glsl,
|
||||
if (e_data.dof_setup_sh == NULL) {
|
||||
e_data.dof_setup_sh = DRW_shader_create_fullscreen_with_shaderlib(
|
||||
datatoc_effect_dof_setup_frag_glsl, e_data.lib, DOF_SHADER_DEFINES);
|
||||
}
|
||||
return e_data.dof_setup_sh;
|
||||
}
|
||||
|
||||
GPUShader *EEVEE_shaders_depth_of_field_flatten_tiles_get(void)
|
||||
{
|
||||
if (e_data.dof_flatten_tiles_sh == NULL) {
|
||||
e_data.dof_flatten_tiles_sh = DRW_shader_create_fullscreen_with_shaderlib(
|
||||
datatoc_effect_dof_flatten_tiles_frag_glsl, e_data.lib, DOF_SHADER_DEFINES);
|
||||
}
|
||||
return e_data.dof_flatten_tiles_sh;
|
||||
}
|
||||
|
||||
GPUShader *EEVEE_shaders_depth_of_field_dilate_tiles_get(bool b_pass)
|
||||
{
|
||||
int pass = b_pass;
|
||||
if (e_data.dof_dilate_tiles_sh[pass] == NULL) {
|
||||
e_data.dof_dilate_tiles_sh[pass] = DRW_shader_create_fullscreen_with_shaderlib(
|
||||
datatoc_effect_dof_dilate_tiles_frag_glsl,
|
||||
e_data.lib,
|
||||
(pass == 0) ? DOF_SHADER_DEFINES "#define DILATE_MODE_MIN_MAX\n" :
|
||||
DOF_SHADER_DEFINES "#define DILATE_MODE_MIN_ABS\n");
|
||||
}
|
||||
return e_data.dof_dilate_tiles_sh[pass];
|
||||
}
|
||||
|
||||
GPUShader *EEVEE_shaders_depth_of_field_downsample_get(void)
|
||||
{
|
||||
if (e_data.dof_downsample_sh == NULL) {
|
||||
e_data.dof_downsample_sh = DRW_shader_create_fullscreen_with_shaderlib(
|
||||
datatoc_effect_dof_downsample_frag_glsl, e_data.lib, DOF_SHADER_DEFINES);
|
||||
}
|
||||
return e_data.dof_downsample_sh;
|
||||
}
|
||||
|
||||
GPUShader *EEVEE_shaders_depth_of_field_reduce_get(bool b_is_copy_pass)
|
||||
{
|
||||
int is_copy_pass = b_is_copy_pass;
|
||||
if (e_data.dof_reduce_sh[is_copy_pass] == NULL) {
|
||||
e_data.dof_reduce_sh[is_copy_pass] = DRW_shader_create_fullscreen_with_shaderlib(
|
||||
datatoc_effect_dof_reduce_frag_glsl,
|
||||
e_data.lib,
|
||||
(is_copy_pass) ? DOF_SHADER_DEFINES "#define COPY_PASS\n" :
|
||||
DOF_SHADER_DEFINES "#define REDUCE_PASS\n");
|
||||
}
|
||||
return e_data.dof_reduce_sh[is_copy_pass];
|
||||
}
|
||||
|
||||
GPUShader *EEVEE_shaders_depth_of_field_gather_get(EEVEE_DofGatherPass pass, bool b_use_bokeh_tx)
|
||||
{
|
||||
int use_bokeh_tx = b_use_bokeh_tx;
|
||||
if (e_data.dof_gather_sh[pass][use_bokeh_tx] == NULL) {
|
||||
DynStr *ds = BLI_dynstr_new();
|
||||
|
||||
BLI_dynstr_append(ds, DOF_SHADER_DEFINES);
|
||||
|
||||
switch (pass) {
|
||||
case DOF_GATHER_FOREGROUND:
|
||||
BLI_dynstr_append(ds, "#define DOF_FOREGROUND_PASS\n");
|
||||
break;
|
||||
case DOF_GATHER_BACKGROUND:
|
||||
BLI_dynstr_append(ds, "#define DOF_BACKGROUND_PASS\n");
|
||||
break;
|
||||
case DOF_GATHER_HOLEFILL:
|
||||
BLI_dynstr_append(ds,
|
||||
"#define DOF_BACKGROUND_PASS\n"
|
||||
"#define DOF_HOLEFILL_PASS\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (use_bokeh_tx) {
|
||||
BLI_dynstr_append(ds, "#define DOF_BOKEH_TEXTURE\n");
|
||||
}
|
||||
|
||||
char *define = BLI_dynstr_get_cstring(ds);
|
||||
BLI_dynstr_free(ds);
|
||||
|
||||
e_data.dof_gather_sh[pass][use_bokeh_tx] = DRW_shader_create_fullscreen_with_shaderlib(
|
||||
datatoc_effect_dof_gather_frag_glsl, e_data.lib, define);
|
||||
|
||||
MEM_freeN(define);
|
||||
}
|
||||
return e_data.dof_gather_sh[pass][use_bokeh_tx];
|
||||
}
|
||||
|
||||
GPUShader *EEVEE_shaders_depth_of_field_filter_get(void)
|
||||
{
|
||||
if (e_data.dof_filter_sh == NULL) {
|
||||
e_data.dof_filter_sh = DRW_shader_create_fullscreen_with_shaderlib(
|
||||
datatoc_effect_dof_filter_frag_glsl, e_data.lib, DOF_SHADER_DEFINES);
|
||||
}
|
||||
return e_data.dof_filter_sh;
|
||||
}
|
||||
|
||||
GPUShader *EEVEE_shaders_depth_of_field_scatter_get(bool b_is_foreground, bool b_use_bokeh_tx)
|
||||
{
|
||||
int is_foreground = b_is_foreground;
|
||||
int use_bokeh_tx = b_use_bokeh_tx;
|
||||
if (e_data.dof_scatter_sh[is_foreground][use_bokeh_tx] == NULL) {
|
||||
DynStr *ds = BLI_dynstr_new();
|
||||
|
||||
BLI_dynstr_append(ds, DOF_SHADER_DEFINES);
|
||||
BLI_dynstr_append(
|
||||
ds, (is_foreground) ? "#define DOF_FOREGROUND_PASS\n" : "#define DOF_BACKGROUND_PASS\n");
|
||||
|
||||
if (use_bokeh_tx) {
|
||||
BLI_dynstr_append(ds, "#define DOF_BOKEH_TEXTURE\n");
|
||||
}
|
||||
|
||||
char *define = BLI_dynstr_get_cstring(ds);
|
||||
BLI_dynstr_free(ds);
|
||||
|
||||
e_data.dof_scatter_sh[is_foreground][use_bokeh_tx] = DRW_shader_create_with_shaderlib(
|
||||
datatoc_effect_dof_scatter_vert_glsl,
|
||||
NULL,
|
||||
datatoc_effect_dof_frag_glsl,
|
||||
datatoc_effect_dof_scatter_frag_glsl,
|
||||
e_data.lib,
|
||||
use_alpha ?
|
||||
"#define USE_ALPHA_DOF\n"
|
||||
"#define STEP_SCATTER\n" :
|
||||
"#define STEP_SCATTER\n");
|
||||
define);
|
||||
|
||||
MEM_freeN(define);
|
||||
}
|
||||
return e_data.dof_scatter_sh[index];
|
||||
return e_data.dof_scatter_sh[is_foreground][use_bokeh_tx];
|
||||
}
|
||||
|
||||
GPUShader *EEVEE_shaders_depth_of_field_resolve_get(bool use_alpha)
|
||||
GPUShader *EEVEE_shaders_depth_of_field_resolve_get(bool b_use_bokeh_tx, bool b_use_hq_gather)
|
||||
{
|
||||
int index = use_alpha ? 1 : 0;
|
||||
if (e_data.dof_resolve_sh[index] == NULL) {
|
||||
e_data.dof_resolve_sh[index] = DRW_shader_create_fullscreen_with_shaderlib(
|
||||
datatoc_effect_dof_frag_glsl,
|
||||
e_data.lib,
|
||||
use_alpha ? "#define USE_ALPHA_DOF\n"
|
||||
"#define STEP_RESOLVE\n" :
|
||||
"#define STEP_RESOLVE\n");
|
||||
int use_hq_gather = b_use_hq_gather;
|
||||
int use_bokeh_tx = b_use_bokeh_tx;
|
||||
if (e_data.dof_resolve_sh[use_bokeh_tx][use_hq_gather] == NULL) {
|
||||
DynStr *ds = BLI_dynstr_new();
|
||||
|
||||
BLI_dynstr_append(ds, DOF_SHADER_DEFINES);
|
||||
BLI_dynstr_append(ds, "#define DOF_RESOLVE_PASS\n");
|
||||
|
||||
if (use_bokeh_tx) {
|
||||
BLI_dynstr_append(ds, "#define DOF_BOKEH_TEXTURE\n");
|
||||
}
|
||||
return e_data.dof_resolve_sh[index];
|
||||
|
||||
BLI_dynstr_appendf(ds, "#define DOF_SLIGHT_FOCUS_DENSITY %d\n", use_hq_gather ? 4 : 2);
|
||||
|
||||
char *define = BLI_dynstr_get_cstring(ds);
|
||||
BLI_dynstr_free(ds);
|
||||
|
||||
e_data.dof_resolve_sh[use_bokeh_tx][use_hq_gather] =
|
||||
DRW_shader_create_fullscreen_with_shaderlib(
|
||||
datatoc_effect_dof_resolve_frag_glsl, e_data.lib, define);
|
||||
|
||||
MEM_freeN(define);
|
||||
}
|
||||
return e_data.dof_resolve_sh[use_bokeh_tx][use_hq_gather];
|
||||
}
|
||||
|
||||
/* \} */
|
||||
@@ -1396,6 +1545,7 @@ 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);
|
||||
@@ -1450,6 +1600,27 @@ void EEVEE_shaders_free(void)
|
||||
DRW_SHADER_FREE_SAFE(e_data.velocity_resolve_sh);
|
||||
DRW_SHADER_FREE_SAFE(e_data.taa_resolve_sh);
|
||||
DRW_SHADER_FREE_SAFE(e_data.taa_resolve_reproject_sh);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_bokeh_sh);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_setup_sh);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_flatten_tiles_sh);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_dilate_tiles_sh[0]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_dilate_tiles_sh[1]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_downsample_sh);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_reduce_sh[0]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_reduce_sh[1]);
|
||||
for (int i = 0; i < DOF_GATHER_MAX_PASS; i++) {
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_gather_sh[i][0]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_gather_sh[i][1]);
|
||||
}
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_filter_sh);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_scatter_sh[0][0]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_scatter_sh[0][1]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_scatter_sh[1][0]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_scatter_sh[1][1]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh[0][0]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh[0][1]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh[1][0]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh[1][1]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.cryptomatte_sh[0]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.cryptomatte_sh[1]);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
@@ -1457,9 +1628,6 @@ void EEVEE_shaders_free(void)
|
||||
DRW_SHADER_FREE_SAFE(e_data.bloom_downsample_sh[i]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.bloom_upsample_sh[i]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.bloom_resolve_sh[i]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_downsample_sh[i]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_scatter_sh[i]);
|
||||
DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh[i]);
|
||||
}
|
||||
for (int i = 0; i < SSR_MAX_SHADER; i++) {
|
||||
DRW_SHADER_FREE_SAFE(e_data.ssr_sh[i]);
|
||||
|
@@ -142,16 +142,52 @@ void EEVEE_temporal_sampling_matrices_calc(EEVEE_EffectsInfo *effects, const dou
|
||||
Scene *scene = draw_ctx->scene;
|
||||
RenderData *rd = &scene->r;
|
||||
|
||||
float persmat[4][4], viewmat[4][4], winmat[4][4];
|
||||
float persmat[4][4], viewmat[4][4], winmat[4][4], wininv[4][4];
|
||||
DRW_view_persmat_get(NULL, persmat, false);
|
||||
DRW_view_viewmat_get(NULL, viewmat, false);
|
||||
DRW_view_winmat_get(NULL, winmat, false);
|
||||
DRW_view_winmat_get(NULL, wininv, true);
|
||||
|
||||
float ofs[2];
|
||||
EEVEE_temporal_sampling_offset_calc(ht_point, rd->gauss, ofs);
|
||||
|
||||
window_translate_m4(winmat, persmat, ofs[0] / viewport_size[0], ofs[1] / viewport_size[1]);
|
||||
|
||||
/* Jitter is in pixel space. Focus distance in world space units. */
|
||||
float dof_jitter[2], focus_distance;
|
||||
if (EEVEE_depth_of_field_jitter_get(effects, dof_jitter, &focus_distance)) {
|
||||
/* Convert to NDC space [-1..1]. */
|
||||
dof_jitter[0] /= viewport_size[0] * 0.5f;
|
||||
dof_jitter[1] /= viewport_size[1] * 0.5f;
|
||||
|
||||
/* Skew the projection matrix in the ray direction and offset it to ray origin.
|
||||
* Make it focus at focus_distance. */
|
||||
if (winmat[2][3] != -1.0f) {
|
||||
/* Orthographic */
|
||||
add_v2_v2(winmat[2], dof_jitter);
|
||||
|
||||
window_translate_m4(
|
||||
winmat, persmat, dof_jitter[0] * focus_distance, dof_jitter[1] * focus_distance);
|
||||
}
|
||||
else {
|
||||
/* Get focus distance in NDC. */
|
||||
float focus_pt[3] = {0.0f, 0.0f, -focus_distance};
|
||||
mul_project_m4_v3(winmat, focus_pt);
|
||||
/* Get pixel footprint in viewspace. */
|
||||
float jitter_scaled[3] = {dof_jitter[0], dof_jitter[1], focus_pt[2]};
|
||||
float center[3] = {0.0f, 0.0f, focus_pt[2]};
|
||||
mul_project_m4_v3(wininv, jitter_scaled);
|
||||
mul_project_m4_v3(wininv, center);
|
||||
|
||||
/* FIXME(fclem) The offset is noticeably large and the culling might make object pop out
|
||||
* of the bluring radius. To fix this, use custom enlarged culling matrix. */
|
||||
sub_v2_v2v2(jitter_scaled, jitter_scaled, center);
|
||||
add_v2_v2(viewmat[3], jitter_scaled);
|
||||
|
||||
window_translate_m4(winmat, persmat, dof_jitter[0], dof_jitter[1]);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert(effects->taa_view != NULL);
|
||||
|
||||
/* When rendering just update the view. This avoids recomputing the culling. */
|
||||
@@ -194,6 +230,21 @@ void EEVEE_temporal_sampling_create_view(EEVEE_Data *vedata)
|
||||
DRW_view_clip_planes_set(effects->taa_view, NULL, 0);
|
||||
}
|
||||
|
||||
int EEVEE_temporal_sampling_sample_count_get(const Scene *scene, const EEVEE_StorageList *stl)
|
||||
{
|
||||
const bool is_render = DRW_state_is_image_render();
|
||||
int sample_count = is_render ? scene->eevee.taa_render_samples : scene->eevee.taa_samples;
|
||||
int timesteps = is_render ? stl->g_data->render_timesteps : 1;
|
||||
|
||||
sample_count = max_ii(0, sample_count);
|
||||
sample_count = (sample_count == 0) ? TAA_MAX_SAMPLE : sample_count;
|
||||
sample_count = divide_ceil_u(sample_count, timesteps);
|
||||
|
||||
int dof_sample_count = EEVEE_depth_of_field_sample_count_get(stl->effects, sample_count, NULL);
|
||||
sample_count = dof_sample_count * divide_ceil_u(sample_count, dof_sample_count);
|
||||
return sample_count;
|
||||
}
|
||||
|
||||
int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
|
||||
{
|
||||
EEVEE_StorageList *stl = vedata->stl;
|
||||
@@ -238,10 +289,12 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data
|
||||
view_is_valid = view_is_valid && (ED_screen_animation_no_scrub(wm) == NULL);
|
||||
}
|
||||
|
||||
const bool first_sample_only = EEVEE_renderpasses_only_first_sample_pass_active(vedata);
|
||||
view_is_valid = view_is_valid && !first_sample_only;
|
||||
effects->taa_total_sample = first_sample_only ? 1 : scene_eval->eevee.taa_samples;
|
||||
MAX2(effects->taa_total_sample, 0);
|
||||
effects->taa_total_sample = EEVEE_temporal_sampling_sample_count_get(scene_eval, stl);
|
||||
|
||||
if (EEVEE_renderpasses_only_first_sample_pass_active(vedata)) {
|
||||
view_is_valid = false;
|
||||
effects->taa_total_sample = 1;
|
||||
}
|
||||
|
||||
/* Motion blur steps could reset the sampling when camera is animated (see T79970). */
|
||||
if (!DRW_state_is_scene_render()) {
|
||||
|
@@ -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 = vec3(0.0);
|
||||
visibility = 0.0;
|
||||
bent_normal = normal * 1e-8;
|
||||
visibility = 1e-8;
|
||||
|
||||
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 = safe_normalize(bent_normal);
|
||||
bent_normal = normalize(bent_normal / visibility);
|
||||
|
||||
visibility *= 0.5; /* We integrated 2 slices. */
|
||||
}
|
||||
@@ -240,24 +240,13 @@ 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, vec4 rand, out vec3 bent_normal)
|
||||
float occlusion_compute(vec3 N, vec3 vpos, float user_occlusion, vec4 rand, out vec3 bent_normal)
|
||||
{
|
||||
#ifndef USE_REFRACTION
|
||||
if ((int(aoSettings) & USE_AO) != 0) {
|
||||
@@ -274,6 +263,10 @@ float occlusion_compute(vec3 N, vec3 vpos, vec4 rand, out vec3 bent_normal)
|
||||
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 {
|
||||
@@ -283,10 +276,10 @@ float occlusion_compute(vec3 N, vec3 vpos, vec4 rand, out vec3 bent_normal)
|
||||
/* Scale by user factor */
|
||||
visibility = pow(visibility, aoFactor);
|
||||
|
||||
return visibility;
|
||||
return min(visibility, user_occlusion);
|
||||
}
|
||||
#endif
|
||||
|
||||
bent_normal = N;
|
||||
return 1.0;
|
||||
return user_occlusion;
|
||||
}
|
||||
|
@@ -1,15 +1,6 @@
|
||||
|
||||
#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);
|
||||
@@ -88,7 +79,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 lut.y * f90 + lut.x * f0;
|
||||
return saturate(50.0 * dot(f0, vec3(0.3, 0.6, 0.1))) * lut.y * abs(f90) + lut.x * f0;
|
||||
}
|
||||
|
||||
/* Multi-scattering brdf approximation from :
|
||||
@@ -96,7 +87,11 @@ 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 = lut.y * f90 + lut.x * f0;
|
||||
vec3 FssEss = F_brdf_single_scatter(f0, f90, lut);
|
||||
/* Hack to avoid many more shader variations. */
|
||||
if (f90.g < 0.0) {
|
||||
return FssEss;
|
||||
}
|
||||
|
||||
float Ess = lut.x + lut.y;
|
||||
float Ems = 1.0 - Ess;
|
||||
@@ -107,6 +102,8 @@ 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)
|
||||
{
|
||||
|
@@ -1,85 +0,0 @@
|
||||
#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
|
||||
}
|
@@ -1,136 +0,0 @@
|
||||
|
||||
#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_ltc(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);
|
||||
}
|
||||
}
|
@@ -1,316 +0,0 @@
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
/** \} */
|
@@ -1,128 +0,0 @@
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
@@ -1,71 +0,0 @@
|
||||
|
||||
#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
|
||||
}
|
@@ -147,27 +147,17 @@ 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_radiance, float roughness, vec3 N, float ssr_id, inout Closure cl)
|
||||
vec3 ssr_spec, float roughness, vec3 N, vec3 viewVec, int 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, viewCameraVec);
|
||||
cl.ssr_normal = normal_encode(vN, viewVec);
|
||||
|
||||
if (ssrToggle && int(ssr_id) == outputSsrId) {
|
||||
cl.ssr_data = vec4(ssr_radiance, roughness);
|
||||
if (ssr_id == outputSsrId) {
|
||||
cl.ssr_data = vec4(ssr_spec, roughness);
|
||||
cl.flag |= CLOSURE_SSR_FLAG;
|
||||
}
|
||||
else {
|
||||
cl.radiance += ssr_radiance;
|
||||
}
|
||||
}
|
||||
|
||||
void closure_load_sss_data(
|
||||
@@ -179,11 +169,13 @@ void closure_load_sss_data(
|
||||
cl.sss_radius = radius;
|
||||
cl.sss_albedo = sss_albedo;
|
||||
cl.flag |= CLOSURE_SSS_FLAG;
|
||||
/* Irradiance will be convolved by SSSS pass. Do not add to radiance. */
|
||||
sss_irradiance = vec3(0);
|
||||
cl.radiance += render_pass_diffuse_mask(sss_albedo, vec3(0));
|
||||
}
|
||||
else
|
||||
# endif
|
||||
cl.radiance += render_pass_diffuse_mask(vec3(1), sss_irradiance) * sss_albedo;
|
||||
{
|
||||
cl.radiance += render_pass_diffuse_mask(sss_albedo, sss_irradiance * sss_albedo);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
545
source/blender/draw/engines/eevee/shaders/closure_lit_lib.glsl
Normal file
545
source/blender/draw/engines/eevee/shaders/closure_lit_lib.glsl
Normal file
@@ -0,0 +1,545 @@
|
||||
|
||||
#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
|
@@ -12,20 +12,17 @@ uniform sampler2DArray utilTex;
|
||||
|
||||
#define LUT_SIZE 64
|
||||
|
||||
#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 */
|
||||
/* Layers 4 to 20 are for BTDF Lut. */
|
||||
|
||||
#define texelfetch_noise_tex(coord) \
|
||||
texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, NOISE_LAYER), 0)
|
||||
/**
|
||||
* Reminder: The 4 noise values are based of 3 uncorrelated blue noises:
|
||||
* x : Uniformly distributed value [0..1] (noise 1).
|
||||
* y : Uniformly distributed value [0..1] (noise 2).
|
||||
* z,w : Uniformly distributed point on the unit circle [-1..1] (noise 3).
|
||||
**/
|
||||
#define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0)
|
||||
|
||||
/* Return texture coordinates to sample Surface LUT */
|
||||
vec2 lut_coords(float cosTheta, float roughness)
|
||||
{
|
||||
/* TODO(fclem) Ugly Acos here. Get rid ot this. Should use same mapping as lut_coords_ltc. */
|
||||
float theta = acos(cosTheta);
|
||||
vec2 coords = vec2(roughness, theta / M_PI_2);
|
||||
|
||||
@@ -41,11 +38,6 @@ vec2 lut_coords_ltc(float cosTheta, float roughness)
|
||||
return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE;
|
||||
}
|
||||
|
||||
vec2 brdf_lut(float cosTheta, float roughness)
|
||||
{
|
||||
return textureLod(utilTex, vec3(lut_coords(cosTheta, roughness), BRDF_LUT_LAYER), 0.0).rg;
|
||||
}
|
||||
|
||||
float get_btdf_lut(float NV, float roughness, float ior)
|
||||
{
|
||||
const vec3 lut_scale_bias_texel_size = vec3((LUT_SIZE - 1.0), 0.5, 1.5) / LUT_SIZE;
|
||||
|
@@ -0,0 +1,101 @@
|
||||
|
||||
/**
|
||||
* Bokeh Look Up Table: This outputs a radius multiplier to shape the sampling in gather pass or
|
||||
* the scatter sprite appearance. This is only used if bokeh shape is either anamorphic or is not
|
||||
* a perfect circle.
|
||||
* We correct samples spacing for polygonal bokeh shapes. However, we do not for anamorphic bokeh
|
||||
* as it is way more complex and expensive to do.
|
||||
**/
|
||||
|
||||
#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
|
||||
|
||||
uniform float bokehSides;
|
||||
uniform float bokehRotation;
|
||||
uniform vec2 bokehAnisotropyInv;
|
||||
|
||||
in vec4 uvcoordsvar;
|
||||
|
||||
layout(location = 0) out vec2 outGatherLut;
|
||||
layout(location = 1) out float outScatterLut;
|
||||
layout(location = 2) out float outResolveLut;
|
||||
|
||||
float polygon_sides_length(float sides_count)
|
||||
{
|
||||
return 2.0 * sin(M_PI / sides_count);
|
||||
}
|
||||
|
||||
/* Returns intersection ratio between the radius edge at theta and the polygon edge.
|
||||
* Start first corners at theta == 0. */
|
||||
float circle_to_polygon_radius(float sides_count, float theta)
|
||||
{
|
||||
/* From Graphics Gems from CryENGINE 3 (Siggraph 2013) by Tiago Sousa (slide 36). */
|
||||
float side_angle = M_2PI / sides_count;
|
||||
float halfside_angle = side_angle * 0.5;
|
||||
return cos(side_angle * 0.5) /
|
||||
cos(theta - side_angle * floor((sides_count * theta + M_PI) / M_2PI));
|
||||
}
|
||||
|
||||
/* Remap input angle to have homogenous spacing of points along a polygon edge.
|
||||
* Expect theta to be in [0..2pi] range. */
|
||||
float circle_to_polygon_angle(float sides_count, float theta)
|
||||
{
|
||||
float side_angle = M_2PI / sides_count;
|
||||
float halfside_angle = side_angle * 0.5;
|
||||
float side = floor(theta / side_angle);
|
||||
/* Length of segment from center to the middle of polygon side. */
|
||||
float adjacent = circle_to_polygon_radius(sides_count, 0.0);
|
||||
|
||||
/* This is the relative position of the sample on the polygon half side. */
|
||||
float local_theta = theta - side * side_angle;
|
||||
float ratio = (local_theta - halfside_angle) / halfside_angle;
|
||||
|
||||
float halfside_len = polygon_sides_length(sides_count) * 0.5;
|
||||
float oposite = ratio * halfside_len;
|
||||
|
||||
/* NOTE: atan(y_over_x) has output range [-M_PI_2..M_PI_2]. */
|
||||
float final_local_theta = atan(oposite / adjacent);
|
||||
|
||||
return side * side_angle + final_local_theta;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
/* Center uv in range [-1..1]. */
|
||||
vec2 uv = uvcoordsvar.xy * 2.0 - 1.0;
|
||||
|
||||
float radius = length(uv);
|
||||
|
||||
vec2 texel = floor(gl_FragCoord.xy) - float(DOF_MAX_SLIGHT_FOCUS_RADIUS);
|
||||
|
||||
if (bokehSides > 0.0) {
|
||||
/* NOTE: atan(y,x) has output range [-M_PI..M_PI], so add 2pi to avoid negative angles. */
|
||||
float theta = atan(uv.y, uv.x) + M_2PI;
|
||||
float r = length(uv);
|
||||
|
||||
radius /= circle_to_polygon_radius(bokehSides, theta - bokehRotation);
|
||||
|
||||
float theta_new = circle_to_polygon_angle(bokehSides, theta);
|
||||
float r_new = circle_to_polygon_radius(bokehSides, theta_new);
|
||||
|
||||
theta_new -= bokehRotation;
|
||||
|
||||
uv = r_new * vec2(-cos(theta_new), sin(theta_new));
|
||||
|
||||
{
|
||||
/* Slight focus distance */
|
||||
texel *= bokehAnisotropyInv;
|
||||
float theta = atan(texel.y, -texel.x) + M_2PI;
|
||||
texel /= circle_to_polygon_radius(bokehSides, theta + bokehRotation);
|
||||
}
|
||||
}
|
||||
else {
|
||||
uv *= safe_rcp(length(uv));
|
||||
}
|
||||
|
||||
/* For gather store the normalized UV. */
|
||||
outGatherLut = uv;
|
||||
/* For scatter store distance. */
|
||||
outScatterLut = radius;
|
||||
/* For slight focus gather store pixel perfect distance. */
|
||||
outResolveLut = length(texel);
|
||||
}
|
@@ -0,0 +1,117 @@
|
||||
|
||||
/**
|
||||
* Tile dilate pass: Takes the 8x8 Tiles buffer and converts dilates the tiles with large CoC to
|
||||
* their neighboorhod. This pass is repeated multiple time until the maximum CoC can be covered.
|
||||
**/
|
||||
|
||||
#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
|
||||
|
||||
/* 1/16th of fullres. */
|
||||
uniform sampler2D cocTilesFgBuffer;
|
||||
uniform sampler2D cocTilesBgBuffer;
|
||||
|
||||
uniform int ringCount;
|
||||
uniform int ringWidthMultiplier;
|
||||
uniform bool dilateSlightFocus;
|
||||
|
||||
/* 1/16th of fullres. Same format as input. */
|
||||
layout(location = 0) out vec4 outFgCoc;
|
||||
layout(location = 1) out vec3 outBgCoc;
|
||||
|
||||
const float tile_to_fullres_factor = float(DOF_TILE_DIVISOR);
|
||||
|
||||
/* Error introduced by the random offset of the gathering kernel's center. */
|
||||
const float bluring_radius_error = 1.0 + 1.0 / (gather_ring_count + 0.5);
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 center_tile_pos = ivec2(gl_FragCoord.xy);
|
||||
|
||||
CocTile ring_buckets[DOF_DILATE_RING_COUNT];
|
||||
|
||||
for (int ring = 0; ring < ringCount && ring < DOF_DILATE_RING_COUNT; ring++) {
|
||||
ring_buckets[ring] = dof_coc_tile_init();
|
||||
|
||||
int ring_distance = ring + 1;
|
||||
for (int sample_id = 0; sample_id < 4 * ring_distance; sample_id++) {
|
||||
ivec2 offset = dof_square_ring_sample_offset(ring_distance, sample_id);
|
||||
|
||||
offset *= ringWidthMultiplier;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
ivec2 adj_tile_pos = center_tile_pos + ((i == 0) ? offset : -offset);
|
||||
|
||||
CocTile adj_tile = dof_coc_tile_load(cocTilesFgBuffer, cocTilesBgBuffer, adj_tile_pos);
|
||||
|
||||
#ifdef DILATE_MODE_MIN_MAX
|
||||
/* Actually gather the "absolute" biggest coc but keeping the sign. */
|
||||
ring_buckets[ring].fg_min_coc = min(ring_buckets[ring].fg_min_coc, adj_tile.fg_min_coc);
|
||||
ring_buckets[ring].bg_max_coc = max(ring_buckets[ring].bg_max_coc, adj_tile.bg_max_coc);
|
||||
|
||||
if (dilateSlightFocus) {
|
||||
ring_buckets[ring].fg_slight_focus_max_coc = dof_coc_max_slight_focus(
|
||||
ring_buckets[ring].fg_slight_focus_max_coc, adj_tile.fg_slight_focus_max_coc);
|
||||
}
|
||||
|
||||
#else /* DILATE_MODE_MIN_ABS */
|
||||
ring_buckets[ring].fg_max_coc = max(ring_buckets[ring].fg_max_coc, adj_tile.fg_max_coc);
|
||||
ring_buckets[ring].bg_min_coc = min(ring_buckets[ring].bg_min_coc, adj_tile.bg_min_coc);
|
||||
|
||||
/* Should be tight as possible to reduce gather overhead (see slide 61). */
|
||||
float closest_neighbor_distance = length(max(abs(vec2(offset)) - 1.0, 0.0)) *
|
||||
tile_to_fullres_factor;
|
||||
|
||||
ring_buckets[ring].fg_max_intersectable_coc = max(
|
||||
ring_buckets[ring].fg_max_intersectable_coc,
|
||||
adj_tile.fg_max_intersectable_coc + closest_neighbor_distance);
|
||||
ring_buckets[ring].bg_min_intersectable_coc = min(
|
||||
ring_buckets[ring].bg_min_intersectable_coc,
|
||||
adj_tile.bg_min_intersectable_coc + closest_neighbor_distance);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Load center tile. */
|
||||
CocTile out_tile = dof_coc_tile_load(cocTilesFgBuffer, cocTilesBgBuffer, center_tile_pos);
|
||||
|
||||
/* Dilate once. */
|
||||
if (dilateSlightFocus) {
|
||||
out_tile.fg_slight_focus_max_coc = dof_coc_max_slight_focus(
|
||||
out_tile.fg_slight_focus_max_coc, ring_buckets[0].fg_slight_focus_max_coc);
|
||||
}
|
||||
|
||||
for (int ring = 0; ring < ringCount && ring < DOF_DILATE_RING_COUNT; ring++) {
|
||||
float ring_distance = float(ring + 1);
|
||||
|
||||
ring_distance = (ring_distance * ringWidthMultiplier - 1) * tile_to_fullres_factor;
|
||||
|
||||
/* NOTE(fclem): Unsure if both sides of the inequalities have the same unit. */
|
||||
#ifdef DILATE_MODE_MIN_MAX
|
||||
if (-ring_buckets[ring].fg_min_coc * bluring_radius_error > ring_distance) {
|
||||
out_tile.fg_min_coc = min(out_tile.fg_min_coc, ring_buckets[ring].fg_min_coc);
|
||||
}
|
||||
|
||||
if (ring_buckets[ring].bg_max_coc * bluring_radius_error > ring_distance) {
|
||||
out_tile.bg_max_coc = max(out_tile.bg_max_coc, ring_buckets[ring].bg_max_coc);
|
||||
}
|
||||
|
||||
#else /* DILATE_MODE_MIN_ABS */
|
||||
/* Find minimum absolute CoC radii that will be intersected for the previously
|
||||
* computed maximum CoC values. */
|
||||
if (-out_tile.fg_min_coc * bluring_radius_error > ring_distance) {
|
||||
out_tile.fg_max_coc = max(out_tile.fg_max_coc, ring_buckets[ring].fg_max_coc);
|
||||
out_tile.fg_max_intersectable_coc = max(out_tile.fg_max_intersectable_coc,
|
||||
ring_buckets[ring].fg_max_intersectable_coc);
|
||||
}
|
||||
|
||||
if (out_tile.bg_max_coc * bluring_radius_error > ring_distance) {
|
||||
out_tile.bg_min_coc = min(out_tile.bg_min_coc, ring_buckets[ring].bg_min_coc);
|
||||
out_tile.bg_min_intersectable_coc = min(out_tile.bg_min_intersectable_coc,
|
||||
ring_buckets[ring].bg_min_intersectable_coc);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
dof_coc_tile_store(out_tile, outFgCoc, outBgCoc);
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
|
||||
/**
|
||||
* Downsample pass: CoC aware downsample to quarter resolution.
|
||||
*
|
||||
* Pretty much identical to the setup pass but get CoC from buffer. Also does not
|
||||
* weight luma for the bilateral weights.
|
||||
**/
|
||||
|
||||
#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
|
||||
|
||||
/* Half resolution. */
|
||||
uniform sampler2D colorBuffer;
|
||||
uniform sampler2D cocBuffer;
|
||||
|
||||
/* Quarter resolution. */
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 halfres_texel_size = 1.0 / vec2(textureSize(colorBuffer, 0).xy);
|
||||
/* Center uv around the 4 halfres pixels. */
|
||||
vec2 quad_center = (floor(gl_FragCoord.xy) * 2.0 + 1.0) * halfres_texel_size;
|
||||
|
||||
vec4 colors[4];
|
||||
vec4 cocs;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
vec2 sample_uv = quad_center + quad_offsets[i] * halfres_texel_size;
|
||||
colors[i] = textureLod(colorBuffer, sample_uv, 0.0);
|
||||
cocs[i] = textureLod(cocBuffer, sample_uv, 0.0).r;
|
||||
}
|
||||
|
||||
vec4 weights = dof_downsample_bilateral_coc_weights(cocs);
|
||||
/* Normalize so that the sum is 1. */
|
||||
weights *= safe_rcp(sum(weights));
|
||||
|
||||
outColor = weighted_sum_array(colors, weights);
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
|
||||
/**
|
||||
* Gather Filter pass: Filter the gather pass result to reduce noise.
|
||||
*
|
||||
* This is a simple 3x3 median filter to avoid dilating highlights with a 3x3 max filter even if
|
||||
* cheaper.
|
||||
**/
|
||||
|
||||
#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
|
||||
|
||||
uniform sampler2D colorBuffer;
|
||||
uniform sampler2D weightBuffer;
|
||||
|
||||
in vec4 uvcoordsvar;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
layout(location = 1) out float outWeight;
|
||||
|
||||
/* From:
|
||||
* Implementing Median Filters in XC4000E FPGAs
|
||||
* JOHN L. SMITH, Univision Technologies Inc., Billerica, MA
|
||||
* http://users.utcluj.ro/~baruch/resources/Image/xl23_16.pdf
|
||||
* Figure 1 */
|
||||
|
||||
/* Outputs low median and high value of a triple. */
|
||||
void lmh(vec4 s1, vec4 s2, vec4 s3, out vec4 l, out vec4 m, out vec4 h)
|
||||
{
|
||||
/* From diagram, with nodes numbered from top to bottom. */
|
||||
vec4 h1 = max(s2, s3);
|
||||
vec4 l1 = min(s2, s3);
|
||||
|
||||
vec4 h2 = max(s1, l1);
|
||||
vec4 l2 = min(s1, l1);
|
||||
|
||||
vec4 h3 = max(h2, h1);
|
||||
vec4 l3 = min(h2, h1);
|
||||
|
||||
l = l2;
|
||||
m = l3;
|
||||
h = h3;
|
||||
}
|
||||
|
||||
vec4 median_filter(sampler2D tex, vec2 uv)
|
||||
{
|
||||
vec2 texel_size = 1.0 / vec2(textureSize(tex, 0).xy);
|
||||
vec4 samples[9];
|
||||
int s = 0;
|
||||
|
||||
const vec2 ofs[9] = vec2[9](vec2(-1, -1),
|
||||
vec2(0, -1),
|
||||
vec2(1, -1),
|
||||
vec2(-1, 0),
|
||||
vec2(0, 0),
|
||||
vec2(1, 0),
|
||||
vec2(-1, 1),
|
||||
vec2(0, 1),
|
||||
vec2(1, 1));
|
||||
|
||||
for (int s = 0; s < 9; s++) {
|
||||
samples[s] = textureLod(tex, uv + ofs[s] * texel_size, 0.0);
|
||||
}
|
||||
|
||||
if (no_gather_filtering) {
|
||||
return samples[4];
|
||||
}
|
||||
|
||||
for (int s = 0; s < 9; s += 3) {
|
||||
lmh(samples[s], samples[s + 1], samples[s + 2], samples[s], samples[s + 1], samples[s + 2]);
|
||||
}
|
||||
/* Some aliases to better understand what's happening. */
|
||||
vec4 L123 = samples[0 + 0], L456 = samples[3 + 0], L789 = samples[6 + 0];
|
||||
vec4 M123 = samples[0 + 1], M456 = samples[3 + 1], M789 = samples[6 + 1];
|
||||
vec4 H123 = samples[0 + 2], H456 = samples[3 + 2], H789 = samples[6 + 2];
|
||||
vec4 dummy, l, m, h;
|
||||
/* Left nodes. */
|
||||
h = max(max(L123, L456), L789);
|
||||
/* Right nodes. */
|
||||
l = min(min(H123, H456), H789);
|
||||
/* Center nodes. */
|
||||
lmh(M123, M456, M789, dummy, m, dummy);
|
||||
/* Last bottom nodes. */
|
||||
lmh(l, m, h, dummy, m, dummy);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
/* OPTI(fclem) Could early return on some tiles. */
|
||||
|
||||
outColor = median_filter(colorBuffer, uvcoordsvar.xy);
|
||||
outWeight = median_filter(weightBuffer, uvcoordsvar.xy).r;
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
|
||||
/**
|
||||
* Tile flatten pass: Takes the halfres CoC buffer and converts it to 8x8 tiles.
|
||||
*
|
||||
* Output min and max values for each tile and for both foreground & background.
|
||||
* Also outputs min intersectable CoC for the background, which is the minimum CoC
|
||||
* that comes from the background pixels.
|
||||
**/
|
||||
|
||||
#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
|
||||
|
||||
/* Half resolution. */
|
||||
uniform sampler2D halfResCocBuffer;
|
||||
|
||||
/* 1/8th of halfResCocBuffer resolution. So 1/16th of fullres. */
|
||||
layout(location = 0) out vec4 outFgCoc;
|
||||
layout(location = 1) out vec3 outBgCoc;
|
||||
|
||||
const int halfres_tile_divisor = DOF_TILE_DIVISOR / 2;
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 halfres_bounds = textureSize(halfResCocBuffer, 0).xy - 1;
|
||||
ivec2 tile_co = ivec2(gl_FragCoord.xy);
|
||||
|
||||
CocTile tile = dof_coc_tile_init();
|
||||
|
||||
for (int x = 0; x < halfres_tile_divisor; x++) {
|
||||
/* OPTI: Could be done in separate passes. */
|
||||
for (int y = 0; y < halfres_tile_divisor; y++) {
|
||||
ivec2 sample_texel = tile_co * halfres_tile_divisor + ivec2(x, y);
|
||||
vec2 sample_data = texelFetch(halfResCocBuffer, min(sample_texel, halfres_bounds), 0).rg;
|
||||
float sample_coc = sample_data.x;
|
||||
float sample_slight_focus_coc = sample_data.y;
|
||||
|
||||
float fg_coc = min(sample_coc, 0.0);
|
||||
tile.fg_min_coc = min(tile.fg_min_coc, fg_coc);
|
||||
tile.fg_max_coc = max(tile.fg_max_coc, fg_coc);
|
||||
|
||||
float bg_coc = max(sample_coc, 0.0);
|
||||
tile.bg_min_coc = min(tile.bg_min_coc, bg_coc);
|
||||
tile.bg_max_coc = max(tile.bg_max_coc, bg_coc);
|
||||
|
||||
if (sample_coc > 0.0) {
|
||||
tile.bg_min_intersectable_coc = min(tile.bg_min_intersectable_coc, bg_coc);
|
||||
}
|
||||
if (sample_coc < 0.0) {
|
||||
tile.fg_max_intersectable_coc = max(tile.fg_max_intersectable_coc, fg_coc);
|
||||
}
|
||||
|
||||
tile.fg_slight_focus_max_coc = dof_coc_max_slight_focus(tile.fg_slight_focus_max_coc,
|
||||
sample_slight_focus_coc);
|
||||
}
|
||||
}
|
||||
|
||||
dof_coc_tile_store(tile, outFgCoc, outBgCoc);
|
||||
}
|
@@ -1,254 +0,0 @@
|
||||
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
|
||||
|
||||
uniform sampler2D colorBuffer;
|
||||
uniform sampler2D depthBuffer;
|
||||
|
||||
uniform vec2 dofParams;
|
||||
uniform bool unpremult;
|
||||
|
||||
#define dof_mul dofParams.x /* distance * aperturesize * invsensorsize */
|
||||
#define dof_bias dofParams.y /* aperturesize * invsensorsize */
|
||||
|
||||
uniform vec4 bokehParams[2];
|
||||
|
||||
#define bokeh_rotation bokehParams[0].x
|
||||
#define bokeh_ratio bokehParams[0].y
|
||||
#define bokeh_maxsize bokehParams[0].z
|
||||
#define bokeh_sides \
|
||||
bokehParams[1] /* Polygon Bokeh shape number of sides (with precomputed vars) */
|
||||
|
||||
uniform vec2 nearFar; /* Near & far view depths values */
|
||||
|
||||
/* -------------- Utils ------------- */
|
||||
|
||||
/* divide by sensor size to get the normalized size */
|
||||
#define calculate_coc(zdepth) (dof_mul / zdepth - dof_bias)
|
||||
|
||||
#define linear_depth(z) \
|
||||
((ProjectionMatrix[3][3] == 0.0) ? \
|
||||
(nearFar.x * nearFar.y) / (z * (nearFar.x - nearFar.y) + nearFar.y) : \
|
||||
z * (nearFar.y - nearFar.x) + nearFar.x) /* Only true for camera view! */
|
||||
|
||||
#define weighted_sum(a, b, c, d, e) \
|
||||
(a * e.x + b * e.y + c * e.z + d * e.w) / max(1e-6, dot(e, vec4(1.0)));
|
||||
|
||||
vec4 safe_color(vec4 c)
|
||||
{
|
||||
/* Clamp to avoid black square artifacts if a pixel goes NaN. */
|
||||
return clamp(c, vec4(0.0), vec4(1e20)); /* 1e20 arbitrary. */
|
||||
}
|
||||
|
||||
#define THRESHOLD 1.0
|
||||
|
||||
#ifdef STEP_DOWNSAMPLE
|
||||
|
||||
layout(location = 0) out vec4 nearColor;
|
||||
layout(location = 1) out vec4 farColor;
|
||||
layout(location = 2) out vec2 cocData;
|
||||
|
||||
/* Downsample the color buffer to half resolution.
|
||||
* Weight color samples by
|
||||
* Compute maximum CoC for near and far blur. */
|
||||
void main(void)
|
||||
{
|
||||
ivec4 uvs = ivec4(gl_FragCoord.xyxy) * 2 + ivec4(0, 0, 1, 1);
|
||||
|
||||
/* custom downsampling */
|
||||
vec4 color1 = safe_color(texelFetch(colorBuffer, uvs.xy, 0));
|
||||
vec4 color2 = safe_color(texelFetch(colorBuffer, uvs.zw, 0));
|
||||
vec4 color3 = safe_color(texelFetch(colorBuffer, uvs.zy, 0));
|
||||
vec4 color4 = safe_color(texelFetch(colorBuffer, uvs.xw, 0));
|
||||
|
||||
/* Leverage SIMD by combining 4 depth samples into a vec4 */
|
||||
vec4 depth;
|
||||
depth.r = texelFetch(depthBuffer, uvs.xy, 0).r;
|
||||
depth.g = texelFetch(depthBuffer, uvs.zw, 0).r;
|
||||
depth.b = texelFetch(depthBuffer, uvs.zy, 0).r;
|
||||
depth.a = texelFetch(depthBuffer, uvs.xw, 0).r;
|
||||
|
||||
vec4 zdepth = linear_depth(depth);
|
||||
|
||||
/* Compute signed CoC for each depth samples */
|
||||
vec4 coc_near = calculate_coc(zdepth);
|
||||
vec4 coc_far = -coc_near;
|
||||
|
||||
cocData.x = max(max_v4(coc_near), 0.0);
|
||||
cocData.y = max(max_v4(coc_far), 0.0);
|
||||
|
||||
/* now we need to write the near-far fields premultiplied by the coc
|
||||
* also use bilateral weighting by each coc values to avoid bleeding. */
|
||||
vec4 near_weights = step(THRESHOLD, coc_near) * clamp(1.0 - abs(cocData.x - coc_near), 0.0, 1.0);
|
||||
vec4 far_weights = step(THRESHOLD, coc_far) * clamp(1.0 - abs(cocData.y - coc_far), 0.0, 1.0);
|
||||
|
||||
# ifdef USE_ALPHA_DOF
|
||||
/* Premult */
|
||||
color1.rgb *= color1.a;
|
||||
color2.rgb *= color2.a;
|
||||
color3.rgb *= color3.a;
|
||||
color4.rgb *= color4.a;
|
||||
# endif
|
||||
|
||||
/* now write output to weighted buffers. */
|
||||
nearColor = weighted_sum(color1, color2, color3, color4, near_weights);
|
||||
farColor = weighted_sum(color1, color2, color3, color4, far_weights);
|
||||
}
|
||||
|
||||
#elif defined(STEP_SCATTER)
|
||||
|
||||
flat in vec4 color;
|
||||
flat in float weight;
|
||||
flat in float smoothFac;
|
||||
flat in ivec2 edge;
|
||||
/* coordinate used for calculating radius */
|
||||
in vec2 particlecoord;
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
# ifdef USE_ALPHA_DOF
|
||||
layout(location = 1) out float fragAlpha;
|
||||
# endif
|
||||
|
||||
/* accumulate color in the near/far blur buffers */
|
||||
void main(void)
|
||||
{
|
||||
/* Discard to avoid bleeding onto the next layer */
|
||||
if (int(gl_FragCoord.x) * edge.x + edge.y > 0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
/* Circle Dof */
|
||||
float dist = length(particlecoord);
|
||||
|
||||
/* Outside of bokeh shape */
|
||||
if (dist > 1.0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
/* Regular Polygon Dof */
|
||||
if (bokeh_sides.x > 0.0) {
|
||||
/* Circle parametrization */
|
||||
float theta = atan(particlecoord.y, particlecoord.x) + bokeh_rotation;
|
||||
|
||||
/* Optimized version of :
|
||||
* float denom = theta - (M_2PI / bokeh_sides) * floor((bokeh_sides * theta + M_PI) / M_2PI);
|
||||
* float r = cos(M_PI / bokeh_sides) / cos(denom); */
|
||||
float denom = theta - bokeh_sides.y * floor(bokeh_sides.z * theta + 0.5);
|
||||
float r = bokeh_sides.w / cos(denom);
|
||||
|
||||
/* Divide circle radial coord by the shape radius for angle theta.
|
||||
* Giving us the new linear radius to the shape edge. */
|
||||
dist /= r;
|
||||
|
||||
/* Outside of bokeh shape */
|
||||
if (dist > 1.0) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
|
||||
fragColor = color;
|
||||
|
||||
/* Smooth the edges a bit. This effectively reduce the bokeh shape
|
||||
* but does fade out the undersampling artifacts. */
|
||||
float shape = smoothstep(1.0, min(0.999, smoothFac), dist);
|
||||
|
||||
fragColor *= shape;
|
||||
|
||||
# ifdef USE_ALPHA_DOF
|
||||
fragAlpha = fragColor.a;
|
||||
fragColor.a = weight * shape;
|
||||
# endif
|
||||
}
|
||||
|
||||
#elif defined(STEP_RESOLVE)
|
||||
|
||||
# define MERGE_THRESHOLD 4.0
|
||||
|
||||
uniform sampler2D scatterBuffer;
|
||||
uniform sampler2D scatterAlphaBuffer;
|
||||
|
||||
in vec4 uvcoordsvar;
|
||||
out vec4 fragColor;
|
||||
|
||||
vec4 upsample_filter(sampler2D tex, vec2 uv, vec2 texelSize)
|
||||
{
|
||||
/* TODO FIXME: Clamp the sample position
|
||||
* depending on the layer to avoid bleeding.
|
||||
* This is not really noticeable so leaving it as is for now. */
|
||||
|
||||
# if 1 /* 9-tap bilinear upsampler (tent filter) */
|
||||
vec4 d = texelSize.xyxy * vec4(1, 1, -1, 0);
|
||||
|
||||
vec4 s;
|
||||
s = textureLod(tex, uv - d.xy, 0.0);
|
||||
s += textureLod(tex, uv - d.wy, 0.0) * 2;
|
||||
s += textureLod(tex, uv - d.zy, 0.0);
|
||||
|
||||
s += textureLod(tex, uv + d.zw, 0.0) * 2;
|
||||
s += textureLod(tex, uv, 0.0) * 4;
|
||||
s += textureLod(tex, uv + d.xw, 0.0) * 2;
|
||||
|
||||
s += textureLod(tex, uv + d.zy, 0.0);
|
||||
s += textureLod(tex, uv + d.wy, 0.0) * 2;
|
||||
s += textureLod(tex, uv + d.xy, 0.0);
|
||||
|
||||
return s * (1.0 / 16.0);
|
||||
# else
|
||||
/* 4-tap bilinear upsampler */
|
||||
vec4 d = texelSize.xyxy * vec4(-1, -1, +1, +1) * 0.5;
|
||||
|
||||
vec4 s;
|
||||
s = textureLod(tex, uv + d.xy, 0.0);
|
||||
s += textureLod(tex, uv + d.zy, 0.0);
|
||||
s += textureLod(tex, uv + d.xw, 0.0);
|
||||
s += textureLod(tex, uv + d.zw, 0.0);
|
||||
|
||||
return s * (1.0 / 4.0);
|
||||
# endif
|
||||
}
|
||||
|
||||
/* Combine the Far and Near color buffers */
|
||||
void main(void)
|
||||
{
|
||||
vec2 uv = uvcoordsvar.xy;
|
||||
/* Recompute Near / Far CoC per pixel */
|
||||
float depth = textureLod(depthBuffer, uv, 0.0).r;
|
||||
float zdepth = linear_depth(depth);
|
||||
float coc_signed = calculate_coc(zdepth);
|
||||
float coc_far = max(-coc_signed, 0.0);
|
||||
float coc_near = max(coc_signed, 0.0);
|
||||
|
||||
vec4 focus_col = textureLod(colorBuffer, uv, 0.0);
|
||||
|
||||
vec2 texelSize = vec2(0.5, 1.0) / vec2(textureSize(scatterBuffer, 0));
|
||||
vec2 near_uv = uv * vec2(0.5, 1.0);
|
||||
vec2 far_uv = near_uv + vec2(0.5, 0.0);
|
||||
vec4 near_col = upsample_filter(scatterBuffer, near_uv, texelSize);
|
||||
vec4 far_col = upsample_filter(scatterBuffer, far_uv, texelSize);
|
||||
|
||||
float far_w = far_col.a;
|
||||
float near_w = near_col.a;
|
||||
float focus_w = 1.0 - smoothstep(1.0, MERGE_THRESHOLD, abs(coc_signed));
|
||||
float inv_weight_sum = 1.0 / (near_w + focus_w + far_w);
|
||||
|
||||
focus_col *= focus_w; /* Premul */
|
||||
|
||||
# ifdef USE_ALPHA_DOF
|
||||
near_col.a = upsample_filter(scatterAlphaBuffer, near_uv, texelSize).r;
|
||||
far_col.a = upsample_filter(scatterAlphaBuffer, far_uv, texelSize).r;
|
||||
# endif
|
||||
|
||||
fragColor = (far_col + near_col + focus_col) * inv_weight_sum;
|
||||
|
||||
# ifdef USE_ALPHA_DOF
|
||||
/* Sigh... viewport expect premult output but
|
||||
* the final render output needs to be with
|
||||
* associated alpha. */
|
||||
if (unpremult) {
|
||||
fragColor.rgb /= (fragColor.a > 0.0) ? fragColor.a : 1.0;
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,293 @@
|
||||
|
||||
/**
|
||||
* Gather pass: Convolve foreground and background parts in separate passes.
|
||||
*
|
||||
* Using the min&max CoC tile buffer, we select the best apropriate method to blur the scene color.
|
||||
* A fast gather path is taken if there is not many CoC variation inside the tile.
|
||||
*
|
||||
* We sample using an octaweb sampling pattern. We randomize the kernel center and each ring
|
||||
* rotation to ensure maximum coverage.
|
||||
**/
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
|
||||
|
||||
/* Mipmapped input buffers, halfres but with padding to ensure mipmap alignement. */
|
||||
uniform sampler2D colorBuffer;
|
||||
uniform sampler2D cocBuffer;
|
||||
|
||||
/* Same input buffer but with a bilinear sampler object. */
|
||||
uniform sampler2D colorBufferBilinear;
|
||||
|
||||
/* CoC Min&Max tile buffer at 1/16th of fullres. */
|
||||
uniform sampler2D cocTilesFgBuffer;
|
||||
uniform sampler2D cocTilesBgBuffer;
|
||||
|
||||
uniform sampler2D bokehLut;
|
||||
|
||||
/* Used to correct the padding in the color and CoC buffers. */
|
||||
uniform vec2 gatherInputUvCorrection;
|
||||
|
||||
uniform vec2 gatherOutputTexelSize;
|
||||
|
||||
uniform vec2 bokehAnisotropy;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
layout(location = 1) out float outWeight;
|
||||
#ifndef DOF_HOLEFILL_PASS
|
||||
layout(location = 2) out vec2 outOcclusion;
|
||||
#else
|
||||
|
||||
/* Dirty global variable that isn't used. So it should get optimized out. */
|
||||
vec2 outOcclusion;
|
||||
#endif
|
||||
|
||||
#ifdef DOF_FOREGROUND_PASS
|
||||
const bool is_foreground = true;
|
||||
#else /* DOF_BACKGROUND_PASS */
|
||||
const bool is_foreground = false;
|
||||
#endif
|
||||
|
||||
const float unit_ring_radius = 1.0 / float(gather_ring_count);
|
||||
const float unit_sample_radius = 1.0 / float(gather_ring_count + 0.5);
|
||||
const float large_kernel_radius = 0.5 + float(gather_ring_count);
|
||||
const float smaller_kernel_radius = 0.5 + float(gather_ring_count - gather_density_change_ring);
|
||||
/* NOTE(fclem) the bias is reducing issues with density change visible transition. */
|
||||
const float radius_downscale_factor = smaller_kernel_radius / large_kernel_radius;
|
||||
const int change_density_at_ring = (gather_ring_count - gather_density_change_ring + 1);
|
||||
const float coc_radius_error = 2.0;
|
||||
|
||||
/* Radii needs to be halfres CoC sizes. */
|
||||
bool dof_do_density_change(float base_radius, float min_intersectable_radius)
|
||||
{
|
||||
/* Reduce artifact for very large blur. */
|
||||
min_intersectable_radius *= 0.1;
|
||||
|
||||
bool need_new_density = (base_radius * unit_ring_radius > min_intersectable_radius);
|
||||
bool larger_than_min_density = (base_radius * radius_downscale_factor >
|
||||
float(gather_ring_count));
|
||||
|
||||
return need_new_density && larger_than_min_density;
|
||||
}
|
||||
|
||||
void dof_gather_init(float base_radius,
|
||||
vec4 noise,
|
||||
out vec2 center_co,
|
||||
out float lod,
|
||||
out float intersection_multiplier)
|
||||
{
|
||||
/* Jitter center half a ring to reduce undersampling. */
|
||||
vec2 jitter_ofs = 0.499 * noise.zw * sqrt(noise.x);
|
||||
#ifdef DOF_BOKEH_TEXTURE
|
||||
jitter_ofs *= bokehAnisotropy;
|
||||
#endif
|
||||
center_co = gl_FragCoord.xy + jitter_ofs * base_radius * unit_sample_radius;
|
||||
|
||||
/* TODO(fclem) Seems like the default lod selection is too big. Bias to avoid blocky moving
|
||||
* out of focus shapes. */
|
||||
const float lod_bias = -2.0;
|
||||
lod = max(floor(log2(base_radius * unit_sample_radius) + 0.5) + lod_bias, 0.0);
|
||||
|
||||
if (no_gather_mipmaps) {
|
||||
lod = 0.0;
|
||||
}
|
||||
/* (Slide 64). */
|
||||
intersection_multiplier = pow(0.5, lod);
|
||||
}
|
||||
|
||||
void dof_gather_accumulator(float base_radius,
|
||||
float min_intersectable_radius,
|
||||
const bool do_fast_gather,
|
||||
const bool do_density_change)
|
||||
{
|
||||
vec4 noise = no_gather_random ? vec4(0.0, 0.0, 0.0, 1.0) : texelfetch_noise_tex(gl_FragCoord.xy);
|
||||
|
||||
if (!do_fast_gather) {
|
||||
/* Jitter the radius to reduce noticeable density changes. */
|
||||
base_radius += noise.x * unit_ring_radius * base_radius;
|
||||
}
|
||||
else {
|
||||
/* Jittering the radius more than we need means we are going to feather the bokeh shape half
|
||||
* a ring. So we need to compensate for fast gather that does not check CoC intersection. */
|
||||
base_radius += (0.5 - noise.x) * 1.5 * unit_ring_radius * base_radius;
|
||||
}
|
||||
/* TODO(fclem) another seed? For now Cranly-Partterson rotation with golden ratio. */
|
||||
noise.x = fract(noise.x + 0.61803398875);
|
||||
|
||||
float lod, isect_mul;
|
||||
vec2 center_co;
|
||||
dof_gather_init(base_radius, noise, center_co, lod, isect_mul);
|
||||
|
||||
bool first_ring = true;
|
||||
|
||||
DofGatherData accum_data = GATHER_DATA_INIT;
|
||||
|
||||
int density_change = 0;
|
||||
for (int ring = gather_ring_count; ring > 0; ring--) {
|
||||
int sample_pair_count = gather_ring_density * ring;
|
||||
|
||||
float step_rot = M_PI / float(sample_pair_count);
|
||||
mat2 step_rot_mat = rot2_from_angle(step_rot);
|
||||
|
||||
float angle_offset = noise.y * step_rot;
|
||||
vec2 offset = vec2(cos(angle_offset), sin(angle_offset));
|
||||
|
||||
float ring_radius = float(ring) * unit_sample_radius * base_radius;
|
||||
|
||||
/* Slide 38. */
|
||||
float bordering_radius = ring_radius +
|
||||
(0.5 + coc_radius_error) * base_radius * unit_sample_radius;
|
||||
DofGatherData ring_data = GATHER_DATA_INIT;
|
||||
for (int sample_pair = 0; sample_pair < sample_pair_count; sample_pair++) {
|
||||
offset = step_rot_mat * offset;
|
||||
|
||||
DofGatherData pair_data[2];
|
||||
for (int i = 0; i < 2; i++) {
|
||||
vec2 offset_co = ((i == 0) ? offset : -offset);
|
||||
#ifdef DOF_BOKEH_TEXTURE
|
||||
/* Scaling to 0.25 for speed. Improves texture cache hit. */
|
||||
offset_co = texture(bokehLut, offset_co * 0.25 + 0.5).rg;
|
||||
offset_co *= bokehAnisotropy;
|
||||
#endif
|
||||
vec2 sample_co = center_co + offset_co * ring_radius;
|
||||
vec2 sample_uv = sample_co * gatherOutputTexelSize * gatherInputUvCorrection;
|
||||
if (do_fast_gather) {
|
||||
pair_data[i].color = dof_load_gather_color(colorBufferBilinear, sample_uv, lod);
|
||||
}
|
||||
else {
|
||||
pair_data[i].color = dof_load_gather_color(colorBuffer, sample_uv, lod);
|
||||
}
|
||||
pair_data[i].coc = dof_load_gather_coc(cocBuffer, sample_uv, lod);
|
||||
pair_data[i].dist = ring_radius;
|
||||
}
|
||||
|
||||
dof_gather_accumulate_sample_pair(pair_data,
|
||||
bordering_radius,
|
||||
isect_mul,
|
||||
first_ring,
|
||||
do_fast_gather,
|
||||
is_foreground,
|
||||
ring_data,
|
||||
accum_data);
|
||||
}
|
||||
|
||||
#ifdef DOF_FOREGROUND_PASS /* Reduce issue with closer foreground over distant foreground. */
|
||||
/* TODO(fclem) this seems to not be completely correct as the issue remains. */
|
||||
float ring_area = (sqr(float(ring) + 0.5 + coc_radius_error) -
|
||||
sqr(float(ring) - 0.5 + coc_radius_error)) *
|
||||
sqr(base_radius * unit_sample_radius);
|
||||
dof_gather_ammend_weight(ring_data, ring_area);
|
||||
#endif
|
||||
|
||||
dof_gather_accumulate_sample_ring(
|
||||
ring_data, sample_pair_count * 2, first_ring, do_fast_gather, is_foreground, accum_data);
|
||||
|
||||
first_ring = false;
|
||||
|
||||
if (do_density_change && (ring == change_density_at_ring) &&
|
||||
(density_change < gather_max_density_change)) {
|
||||
if (dof_do_density_change(base_radius, min_intersectable_radius)) {
|
||||
base_radius *= radius_downscale_factor;
|
||||
ring += gather_density_change_ring;
|
||||
/* We need to account for the density change in the weights (slide 62).
|
||||
* For that multiply old kernel data by its area divided by the new kernel area. */
|
||||
const float outer_rings_weight = 1.0 / (radius_downscale_factor * radius_downscale_factor);
|
||||
#ifndef DOF_FOREGROUND_PASS /* Samples are already weighted per ring in foreground pass. */
|
||||
dof_gather_ammend_weight(accum_data, outer_rings_weight);
|
||||
#endif
|
||||
/* Re-init kernel position & sampling parameters. */
|
||||
dof_gather_init(base_radius, noise, center_co, lod, isect_mul);
|
||||
density_change++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/* Center sample. */
|
||||
vec2 sample_uv = center_co * gatherOutputTexelSize * gatherInputUvCorrection;
|
||||
DofGatherData center_data;
|
||||
if (do_fast_gather) {
|
||||
center_data.color = dof_load_gather_color(colorBufferBilinear, sample_uv, lod);
|
||||
}
|
||||
else {
|
||||
center_data.color = dof_load_gather_color(colorBuffer, sample_uv, lod);
|
||||
}
|
||||
center_data.coc = dof_load_gather_coc(cocBuffer, sample_uv, lod);
|
||||
center_data.dist = 0.0;
|
||||
|
||||
/* Slide 38. */
|
||||
float bordering_radius = (0.5 + coc_radius_error) * base_radius * unit_sample_radius;
|
||||
|
||||
dof_gather_accumulate_center_sample(
|
||||
center_data, bordering_radius, do_fast_gather, is_foreground, accum_data);
|
||||
}
|
||||
|
||||
int total_sample_count = dof_gather_total_sample_count_with_density_change(
|
||||
gather_ring_count, gather_ring_density, density_change);
|
||||
dof_gather_accumulate_resolve(total_sample_count, accum_data, outColor, outWeight, outOcclusion);
|
||||
|
||||
#if defined(DOF_DEBUG_GATHER_PERF)
|
||||
if (density_change > 0) {
|
||||
float fac = saturate(float(density_change) / float(10.0));
|
||||
outColor.rgb = avg(outColor.rgb) * neon_gradient(fac);
|
||||
}
|
||||
if (do_fast_gather) {
|
||||
outColor.rgb = avg(outColor.rgb) * vec3(0.0, 1.0, 0.0);
|
||||
}
|
||||
#elif defined(DOF_DEBUG_SCATTER_PERF)
|
||||
outColor.rgb = avg(outColor.rgb) * vec3(0.0, 1.0, 0.0);
|
||||
#endif
|
||||
|
||||
/* Output premultiplied color so we can use bilinear sampler in resolve pass. */
|
||||
outColor *= outWeight;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 tile_co = ivec2(gl_FragCoord.xy / float(DOF_TILE_DIVISOR / 2));
|
||||
CocTile coc_tile = dof_coc_tile_load(cocTilesFgBuffer, cocTilesBgBuffer, tile_co);
|
||||
CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile);
|
||||
|
||||
#if defined(DOF_FOREGROUND_PASS)
|
||||
float base_radius = -coc_tile.fg_min_coc;
|
||||
float min_radius = -coc_tile.fg_max_coc;
|
||||
float min_intersectable_radius = -coc_tile.fg_max_intersectable_coc;
|
||||
bool can_early_out = !prediction.do_foreground;
|
||||
|
||||
#elif defined(DOF_HOLEFILL_PASS)
|
||||
float base_radius = -coc_tile.fg_min_coc;
|
||||
float min_radius = -coc_tile.fg_max_coc;
|
||||
float min_intersectable_radius = DOF_TILE_LARGE_COC;
|
||||
bool can_early_out = !prediction.do_holefill;
|
||||
|
||||
#else /* DOF_BACKGROUND_PASS */
|
||||
float base_radius = coc_tile.bg_max_coc;
|
||||
float min_radius = coc_tile.bg_min_coc;
|
||||
float min_intersectable_radius = coc_tile.bg_min_intersectable_coc;
|
||||
bool can_early_out = !prediction.do_background;
|
||||
#endif
|
||||
|
||||
bool do_fast_gather = dof_do_fast_gather(base_radius, min_radius, is_foreground);
|
||||
|
||||
/* Gather at half resolution. Divide CoC by 2. */
|
||||
base_radius *= 0.5;
|
||||
min_intersectable_radius *= 0.5;
|
||||
|
||||
bool do_density_change = dof_do_density_change(base_radius, min_intersectable_radius);
|
||||
|
||||
if (can_early_out) {
|
||||
/* Early out. */
|
||||
outColor = vec4(0.0);
|
||||
outWeight = 0.0;
|
||||
outOcclusion = vec2(0.0, 0.0);
|
||||
}
|
||||
else if (do_fast_gather) {
|
||||
dof_gather_accumulator(base_radius, min_intersectable_radius, true, false);
|
||||
}
|
||||
else if (do_density_change) {
|
||||
dof_gather_accumulator(base_radius, min_intersectable_radius, false, true);
|
||||
}
|
||||
else {
|
||||
dof_gather_accumulator(base_radius, min_intersectable_radius, false, false);
|
||||
}
|
||||
}
|
631
source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
Normal file
631
source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
Normal file
@@ -0,0 +1,631 @@
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
|
||||
|
||||
uniform vec4 cocParams;
|
||||
|
||||
#define cocMul cocParams[0] /* distance * aperturesize * invsensorsize */
|
||||
#define cocBias cocParams[1] /* aperturesize * invsensorsize */
|
||||
#define cocNear cocParams[2] /* Near view depths value. */
|
||||
#define cocFar cocParams[3] /* Far view depths value. */
|
||||
|
||||
/* -------------- Debug Defines ------------- */
|
||||
|
||||
// #define DOF_DEBUG_GATHER_PERF
|
||||
// #define DOF_DEBUG_SCATTER_PERF
|
||||
|
||||
const bool no_smooth_intersection = false;
|
||||
const bool no_gather_occlusion = false;
|
||||
const bool no_gather_mipmaps = false;
|
||||
const bool no_gather_random = false;
|
||||
const bool no_gather_filtering = false;
|
||||
const bool no_scatter_occlusion = false;
|
||||
const bool no_scatter_pass = false;
|
||||
const bool no_foreground_pass = false;
|
||||
const bool no_background_pass = false;
|
||||
const bool no_slight_focus_pass = false;
|
||||
const bool no_focus_pass = false;
|
||||
const bool no_holefill_pass = false;
|
||||
|
||||
/* -------------- Quality Defines ------------- */
|
||||
|
||||
#ifdef DOF_HOLEFILL_PASS
|
||||
/* No need for very high density for holefill. */
|
||||
const int gather_ring_count = 3;
|
||||
const int gather_ring_density = 3;
|
||||
const int gather_max_density_change = 0;
|
||||
const int gather_density_change_ring = 1;
|
||||
#else
|
||||
const int gather_ring_count = DOF_GATHER_RING_COUNT;
|
||||
const int gather_ring_density = 3;
|
||||
const int gather_max_density_change = 50; /* Dictates the maximum good quality blur. */
|
||||
const int gather_density_change_ring = 1;
|
||||
#endif
|
||||
|
||||
/* -------------- Utils ------------- */
|
||||
|
||||
const vec2 quad_offsets[4] = vec2[4](
|
||||
vec2(-0.5, 0.5), vec2(0.5, 0.5), vec2(0.5, -0.5), vec2(-0.5, -0.5));
|
||||
|
||||
/* Divide by sensor size to get the normalized size. */
|
||||
#define calculate_coc_persp(zdepth) (cocMul / zdepth - cocBias)
|
||||
#define calculate_coc_ortho(zdepth) ((zdepth + cocMul / cocBias) * cocMul)
|
||||
#define calculate_coc(z) \
|
||||
(ProjectionMatrix[3][3] == 0.0) ? calculate_coc_persp(z) : calculate_coc_ortho(z)
|
||||
|
||||
/* Ortho conversion is only true for camera view! */
|
||||
#define linear_depth_persp(d) ((cocNear * cocFar) / (d * (cocNear - cocFar) + cocFar))
|
||||
#define linear_depth_ortho(d) (d * (cocNear - cocFar) + cocNear)
|
||||
|
||||
#define linear_depth(d) \
|
||||
((ProjectionMatrix[3][3] == 0.0) ? linear_depth_persp(d) : linear_depth_ortho(d))
|
||||
|
||||
#define dof_coc_from_zdepth(d) calculate_coc(linear_depth(d))
|
||||
|
||||
vec4 safe_color(vec4 c)
|
||||
{
|
||||
/* Clamp to avoid black square artifacts if a pixel goes NaN. */
|
||||
return clamp(c, vec4(0.0), vec4(1e20)); /* 1e20 arbitrary. */
|
||||
}
|
||||
|
||||
float dof_hdr_color_weight(vec4 color)
|
||||
{
|
||||
/* From UE4. Very fast "luma" weighting. */
|
||||
float luma = (color.g * 2.0) + (color.r + color.b);
|
||||
/* TODO(fclem) Pass correct exposure. */
|
||||
const float exposure = 1.0;
|
||||
return 1.0 / (luma * exposure + 4.0);
|
||||
}
|
||||
|
||||
float dof_coc_select(vec4 cocs)
|
||||
{
|
||||
/* Select biggest coc. */
|
||||
float selected_coc = cocs.x;
|
||||
if (abs(cocs.y) > abs(selected_coc)) {
|
||||
selected_coc = cocs.y;
|
||||
}
|
||||
if (abs(cocs.z) > abs(selected_coc)) {
|
||||
selected_coc = cocs.z;
|
||||
}
|
||||
if (abs(cocs.w) > abs(selected_coc)) {
|
||||
selected_coc = cocs.w;
|
||||
}
|
||||
return selected_coc;
|
||||
}
|
||||
|
||||
/* NOTE: Do not forget to normalize weights afterwards. */
|
||||
vec4 dof_downsample_bilateral_coc_weights(vec4 cocs)
|
||||
{
|
||||
float chosen_coc = dof_coc_select(cocs);
|
||||
|
||||
const float scale = 4.0; /* TODO(fclem) revisit. */
|
||||
/* NOTE: The difference between the cocs should be inside a abs() function,
|
||||
* but we follow UE4 implementation to improve how dithered transparency looks (see slide 19). */
|
||||
return saturate(1.0 - (chosen_coc - cocs) * scale);
|
||||
}
|
||||
|
||||
/* NOTE: Do not forget to normalize weights afterwards. */
|
||||
vec4 dof_downsample_bilateral_color_weights(vec4 colors[4])
|
||||
{
|
||||
vec4 weights;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
weights[i] = dof_hdr_color_weight(colors[i]);
|
||||
}
|
||||
return weights;
|
||||
}
|
||||
|
||||
/* Makes sure the load functions distribute the energy correctly
|
||||
* to both scatter and gather passes. */
|
||||
vec4 dof_load_gather_color(sampler2D gather_input_color_buffer, vec2 uv, float lod)
|
||||
{
|
||||
vec4 color = textureLod(gather_input_color_buffer, uv, lod);
|
||||
return color;
|
||||
}
|
||||
|
||||
vec4 dof_load_scatter_color(sampler2D scatter_input_color_buffer, vec2 uv, float lod)
|
||||
{
|
||||
vec4 color = textureLod(scatter_input_color_buffer, uv, lod);
|
||||
return color;
|
||||
}
|
||||
|
||||
float dof_load_gather_coc(sampler2D gather_input_coc_buffer, vec2 uv, float lod)
|
||||
{
|
||||
float coc = textureLod(gather_input_coc_buffer, uv, lod).r;
|
||||
/* We gather at halfres. CoC must be divided by 2 to be compared against radii. */
|
||||
return coc * 0.5;
|
||||
}
|
||||
|
||||
/* Distribute weights between near/slightfocus/far fields (slide 117). */
|
||||
const float layer_threshold = 4.0;
|
||||
/* Make sure it overlaps. */
|
||||
const float layer_offset_fg = 0.5 + 1.0;
|
||||
/* Extra offset for convolution layers to avoid light leaking from background. */
|
||||
const float layer_offset = 0.5 + 0.5;
|
||||
|
||||
#define DOF_MAX_SLIGHT_FOCUS_RADIUS 5
|
||||
|
||||
float dof_layer_weight(float coc, const bool is_foreground)
|
||||
{
|
||||
/* NOTE: These are fullres pixel CoC value. */
|
||||
#ifdef DOF_RESOLVE_PASS
|
||||
return saturate(-abs(coc) + layer_threshold + layer_offset) *
|
||||
float(is_foreground ? (coc <= 0.5) : (coc > -0.5));
|
||||
#else
|
||||
coc *= 2.0; /* Account for half pixel gather. */
|
||||
float threshold = layer_threshold - ((is_foreground) ? layer_offset_fg : layer_offset);
|
||||
return saturate(((is_foreground) ? -coc : coc) - threshold);
|
||||
#endif
|
||||
}
|
||||
vec4 dof_layer_weight(vec4 coc)
|
||||
{
|
||||
/* NOTE: Used for scatter pass which already flipped the sign correctly. */
|
||||
coc *= 2.0; /* Account for half pixel gather. */
|
||||
return saturate(coc - layer_threshold + layer_offset);
|
||||
}
|
||||
|
||||
/* NOTE: This is halfres CoC radius. */
|
||||
float dof_sample_weight(float coc)
|
||||
{
|
||||
/* Full intensity if CoC radius is below the pixel footprint. */
|
||||
const float min_coc = 1.0;
|
||||
coc = max(min_coc, abs(coc));
|
||||
return (M_PI * min_coc * min_coc) / (M_PI * coc * coc);
|
||||
}
|
||||
vec4 dof_sample_weight(vec4 coc)
|
||||
{
|
||||
/* Full intensity if CoC radius is below the pixel footprint. */
|
||||
const float min_coc = 1.0;
|
||||
coc = max(vec4(min_coc), abs(coc));
|
||||
return (M_PI * min_coc * min_coc) / (M_PI * coc * coc);
|
||||
}
|
||||
|
||||
/* Intersection with the center of the kernel. */
|
||||
float dof_intersection_weight(float coc, float distance_from_center, float intersection_multiplier)
|
||||
{
|
||||
if (no_smooth_intersection) {
|
||||
return step(0.0, (abs(coc) - distance_from_center));
|
||||
}
|
||||
else {
|
||||
/* (Slide 64). */
|
||||
return saturate((abs(coc) - distance_from_center) * intersection_multiplier + 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns weight of the sample for the outer bucket (containing previous rings). */
|
||||
float dof_gather_accum_weight(float coc, float bordering_radius, bool first_ring)
|
||||
{
|
||||
/* First ring has nothing to be mixed against. */
|
||||
if (first_ring) {
|
||||
return 0.0;
|
||||
}
|
||||
return saturate(coc - bordering_radius);
|
||||
}
|
||||
|
||||
bool dof_do_fast_gather(float max_absolute_coc, float min_absolute_coc, const bool is_foreground)
|
||||
{
|
||||
float min_weight = dof_layer_weight((is_foreground) ? -min_absolute_coc : min_absolute_coc,
|
||||
is_foreground);
|
||||
if (min_weight < 1.0) {
|
||||
return false;
|
||||
}
|
||||
/* FIXME(fclem): This is a workaround to fast gather triggering too early.
|
||||
* Since we use custom opacity mask, the opacity is not given to be 100% even for
|
||||
* after normal threshold. */
|
||||
if (is_foreground && min_absolute_coc < layer_threshold) {
|
||||
return false;
|
||||
}
|
||||
return (max_absolute_coc - min_absolute_coc) < (DOF_FAST_GATHER_COC_ERROR * max_absolute_coc);
|
||||
}
|
||||
|
||||
/* ------------------- COC TILES UTILS ------------------- */
|
||||
|
||||
struct CocTile {
|
||||
float fg_min_coc;
|
||||
float fg_max_coc;
|
||||
float fg_max_intersectable_coc;
|
||||
float fg_slight_focus_max_coc;
|
||||
float bg_min_coc;
|
||||
float bg_max_coc;
|
||||
float bg_min_intersectable_coc;
|
||||
};
|
||||
|
||||
struct CocTilePrediction {
|
||||
bool do_foreground;
|
||||
bool do_slight_focus;
|
||||
bool do_focus;
|
||||
bool do_background;
|
||||
bool do_holefill;
|
||||
};
|
||||
|
||||
/* WATCH: Might have to change depending on the texture format. */
|
||||
#define DOF_TILE_DEFOCUS 0.25
|
||||
#define DOF_TILE_FOCUS 0.0
|
||||
#define DOF_TILE_MIXED 0.75
|
||||
#define DOF_TILE_LARGE_COC 1024.0
|
||||
|
||||
/* Init a CoC tile for reduction algorithms. */
|
||||
CocTile dof_coc_tile_init(void)
|
||||
{
|
||||
CocTile tile;
|
||||
tile.fg_min_coc = 0.0;
|
||||
tile.fg_max_coc = -DOF_TILE_LARGE_COC;
|
||||
tile.fg_max_intersectable_coc = DOF_TILE_LARGE_COC;
|
||||
tile.fg_slight_focus_max_coc = -1.0;
|
||||
tile.bg_min_coc = DOF_TILE_LARGE_COC;
|
||||
tile.bg_max_coc = 0.0;
|
||||
tile.bg_min_intersectable_coc = DOF_TILE_LARGE_COC;
|
||||
return tile;
|
||||
}
|
||||
|
||||
CocTile dof_coc_tile_load(sampler2D fg_buffer, sampler2D bg_buffer, ivec2 tile_co)
|
||||
{
|
||||
ivec2 tex_size = textureSize(fg_buffer, 0).xy;
|
||||
tile_co = clamp(tile_co, ivec2(0), tex_size - 1);
|
||||
|
||||
vec4 fg = texelFetch(fg_buffer, tile_co, 0);
|
||||
vec3 bg = texelFetch(bg_buffer, tile_co, 0).xyz;
|
||||
|
||||
CocTile tile;
|
||||
tile.fg_min_coc = -fg.x;
|
||||
tile.fg_max_coc = -fg.y;
|
||||
tile.fg_max_intersectable_coc = -fg.z;
|
||||
tile.fg_slight_focus_max_coc = fg.w;
|
||||
tile.bg_min_coc = bg.x;
|
||||
tile.bg_max_coc = bg.y;
|
||||
tile.bg_min_intersectable_coc = bg.z;
|
||||
return tile;
|
||||
}
|
||||
|
||||
void dof_coc_tile_store(CocTile tile, out vec4 out_fg, out vec3 out_bg)
|
||||
{
|
||||
out_fg.x = -tile.fg_min_coc;
|
||||
out_fg.y = -tile.fg_max_coc;
|
||||
out_fg.z = -tile.fg_max_intersectable_coc;
|
||||
out_fg.w = tile.fg_slight_focus_max_coc;
|
||||
out_bg.x = tile.bg_min_coc;
|
||||
out_bg.y = tile.bg_max_coc;
|
||||
out_bg.z = tile.bg_min_intersectable_coc;
|
||||
}
|
||||
|
||||
CocTilePrediction dof_coc_tile_prediction_get(CocTile tile)
|
||||
{
|
||||
/* Based on tile value, predict what pass we need to load. */
|
||||
CocTilePrediction predict;
|
||||
|
||||
predict.do_foreground = (-tile.fg_min_coc > layer_threshold - layer_offset_fg);
|
||||
bool fg_fully_opaque = predict.do_foreground &&
|
||||
dof_do_fast_gather(-tile.fg_min_coc, -tile.fg_max_coc, true);
|
||||
|
||||
predict.do_slight_focus = !fg_fully_opaque && (tile.fg_slight_focus_max_coc >= 0.5);
|
||||
predict.do_focus = !fg_fully_opaque && (tile.fg_slight_focus_max_coc == DOF_TILE_FOCUS);
|
||||
|
||||
predict.do_background = !predict.do_focus && !fg_fully_opaque &&
|
||||
(tile.bg_max_coc > layer_threshold - layer_offset);
|
||||
bool bg_fully_opaque = predict.do_background &&
|
||||
dof_do_fast_gather(-tile.bg_max_coc, tile.bg_min_coc, false);
|
||||
predict.do_holefill = !predict.do_focus && !fg_fully_opaque && -tile.fg_max_coc > 0.0;
|
||||
|
||||
#if 0 /* Debug */
|
||||
predict.do_foreground = predict.do_background = predict.do_holefill = true;
|
||||
#endif
|
||||
return predict;
|
||||
}
|
||||
|
||||
/* Special function to return the correct max value of 2 slight focus coc. */
|
||||
float dof_coc_max_slight_focus(float coc1, float coc2)
|
||||
{
|
||||
/* Do not consider values below 0.5 for expansion as they are "encoded".
|
||||
* See setup pass shader for more infos. */
|
||||
if ((coc1 == DOF_TILE_DEFOCUS && coc2 == DOF_TILE_FOCUS) ||
|
||||
(coc1 == DOF_TILE_FOCUS && coc2 == DOF_TILE_DEFOCUS)) {
|
||||
/* Tile where completely out of focus and in focus are both present.
|
||||
* Consider as very slightly out of focus. */
|
||||
return DOF_TILE_MIXED;
|
||||
}
|
||||
return max(coc1, coc2);
|
||||
}
|
||||
|
||||
/* ------------------- GATHER UTILS ------------------- */
|
||||
|
||||
struct DofGatherData {
|
||||
vec4 color;
|
||||
float weight;
|
||||
float dist; /* TODO remove */
|
||||
/* For scatter occlusion. */
|
||||
float coc;
|
||||
float coc_sqr;
|
||||
/* For ring bucket merging. */
|
||||
float transparency;
|
||||
|
||||
float layer_opacity;
|
||||
};
|
||||
|
||||
#define GATHER_DATA_INIT DofGatherData(vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
|
||||
|
||||
void dof_gather_ammend_weight(inout DofGatherData sample_data, float weight)
|
||||
{
|
||||
sample_data.color *= weight;
|
||||
sample_data.coc *= weight;
|
||||
sample_data.coc_sqr *= weight;
|
||||
sample_data.weight *= weight;
|
||||
}
|
||||
|
||||
void dof_gather_accumulate_sample(DofGatherData sample_data,
|
||||
float weight,
|
||||
inout DofGatherData accum_data)
|
||||
{
|
||||
accum_data.color += sample_data.color * weight;
|
||||
accum_data.coc += sample_data.coc * weight;
|
||||
accum_data.coc_sqr += sample_data.coc * (sample_data.coc * weight);
|
||||
accum_data.weight += weight;
|
||||
}
|
||||
|
||||
void dof_gather_accumulate_sample_pair(DofGatherData pair_data[2],
|
||||
float bordering_radius,
|
||||
float intersection_multiplier,
|
||||
bool first_ring,
|
||||
const bool do_fast_gather,
|
||||
const bool is_foreground,
|
||||
inout DofGatherData ring_data,
|
||||
inout DofGatherData accum_data)
|
||||
{
|
||||
if (do_fast_gather) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
dof_gather_accumulate_sample(pair_data[i], 1.0, accum_data);
|
||||
accum_data.layer_opacity += 1.0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
const float mirroring_threshold = -layer_threshold - layer_offset;
|
||||
/* TODO(fclem) Promote to parameter? dither with Noise? */
|
||||
const float mirroring_min_distance = 15.0;
|
||||
if (pair_data[0].coc < mirroring_threshold &&
|
||||
(pair_data[1].coc - mirroring_min_distance) > pair_data[0].coc) {
|
||||
pair_data[1].coc = pair_data[0].coc;
|
||||
}
|
||||
else if (pair_data[1].coc < mirroring_threshold &&
|
||||
(pair_data[0].coc - mirroring_min_distance) > pair_data[1].coc) {
|
||||
pair_data[0].coc = pair_data[1].coc;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
float sample_weight = dof_sample_weight(pair_data[i].coc);
|
||||
float layer_weight = dof_layer_weight(pair_data[i].coc, is_foreground);
|
||||
float inter_weight = dof_intersection_weight(
|
||||
pair_data[i].coc, pair_data[i].dist, intersection_multiplier);
|
||||
float weight = inter_weight * layer_weight * sample_weight;
|
||||
|
||||
/**
|
||||
* If a CoC is larger than bordering radius we accumulate it to the general accumulator.
|
||||
* If not, we accumulate to the ring bucket. This is to have more consistent sample occlusion.
|
||||
**/
|
||||
float accum_weight = dof_gather_accum_weight(pair_data[i].coc, bordering_radius, first_ring);
|
||||
dof_gather_accumulate_sample(pair_data[i], weight * accum_weight, accum_data);
|
||||
dof_gather_accumulate_sample(pair_data[i], weight * (1.0 - accum_weight), ring_data);
|
||||
|
||||
accum_data.layer_opacity += layer_weight;
|
||||
|
||||
if (is_foreground) {
|
||||
ring_data.transparency += 1.0 - inter_weight * layer_weight;
|
||||
}
|
||||
else {
|
||||
float coc = is_foreground ? -pair_data[i].coc : pair_data[i].coc;
|
||||
ring_data.transparency += saturate(coc - bordering_radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dof_gather_accumulate_sample_ring(DofGatherData ring_data,
|
||||
int sample_count,
|
||||
bool first_ring,
|
||||
const bool do_fast_gather,
|
||||
/* accum_data occludes the ring_data if true. */
|
||||
const bool reversed_occlusion,
|
||||
inout DofGatherData accum_data)
|
||||
{
|
||||
if (do_fast_gather) {
|
||||
/* Do nothing as ring_data contains nothing. All samples are already in accum_data. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (first_ring) {
|
||||
/* Layer opacity is directly accumulated into accum_data data. */
|
||||
accum_data.color = ring_data.color;
|
||||
accum_data.coc = ring_data.coc;
|
||||
accum_data.coc_sqr = ring_data.coc_sqr;
|
||||
accum_data.weight = ring_data.weight;
|
||||
|
||||
accum_data.transparency = ring_data.transparency / float(sample_count);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ring_data.weight == 0.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
float ring_avg_coc = ring_data.coc / ring_data.weight;
|
||||
float accum_avg_coc = accum_data.coc / accum_data.weight;
|
||||
|
||||
/* Smooth test to set opacity to see if the ring average coc occludes the accumulation.
|
||||
* Test is reversed to be multiplied against opacity. */
|
||||
float ring_occlu = saturate(accum_avg_coc - ring_avg_coc);
|
||||
/* The bias here is arbitrary. Seems to avoid weird looking foreground in most cases.
|
||||
* We might need to make it a parameter or find a relative bias. */
|
||||
float accum_occlu = saturate((ring_avg_coc - accum_avg_coc) * 0.1 - 1.0);
|
||||
|
||||
#ifdef DOF_RESOLVE_PASS
|
||||
ring_occlu = accum_occlu = 0.0;
|
||||
#endif
|
||||
|
||||
if (no_gather_occlusion) {
|
||||
ring_occlu = 0.0;
|
||||
accum_occlu = 0.0;
|
||||
}
|
||||
|
||||
/* (Slide 40) */
|
||||
float ring_opacity = saturate(1.0 - ring_data.transparency / float(sample_count));
|
||||
float accum_opacity = 1.0 - accum_data.transparency;
|
||||
|
||||
if (reversed_occlusion) {
|
||||
/* Accum_data occludes the ring. */
|
||||
float alpha = (accum_data.weight == 0.0) ? 0.0 : accum_opacity * accum_occlu;
|
||||
float one_minus_alpha = 1.0 - alpha;
|
||||
|
||||
accum_data.color += ring_data.color * one_minus_alpha;
|
||||
accum_data.coc += ring_data.coc * one_minus_alpha;
|
||||
accum_data.coc_sqr += ring_data.coc_sqr * one_minus_alpha;
|
||||
accum_data.weight += ring_data.weight * one_minus_alpha;
|
||||
|
||||
accum_data.transparency *= 1.0 - ring_opacity;
|
||||
}
|
||||
else {
|
||||
/* Ring occludes the accum_data (Same as reference). */
|
||||
float alpha = (accum_data.weight == 0.0) ? 1.0 : (ring_opacity * ring_occlu);
|
||||
float one_minus_alpha = 1.0 - alpha;
|
||||
|
||||
accum_data.color = accum_data.color * one_minus_alpha + ring_data.color;
|
||||
accum_data.coc = accum_data.coc * one_minus_alpha + ring_data.coc;
|
||||
accum_data.coc_sqr = accum_data.coc_sqr * one_minus_alpha + ring_data.coc_sqr;
|
||||
accum_data.weight = accum_data.weight * one_minus_alpha + ring_data.weight;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME(fclem) Seems to be wrong since it needs ringcount+1 as input for slightfocus gather. */
|
||||
int dof_gather_total_sample_count(const int ring_count, const int ring_density)
|
||||
{
|
||||
return (ring_count * ring_count - ring_count) * ring_density + 1;
|
||||
}
|
||||
|
||||
void dof_gather_accumulate_center_sample(DofGatherData center_data,
|
||||
float bordering_radius,
|
||||
#ifdef DOF_RESOLVE_PASS
|
||||
int i_radius,
|
||||
#endif
|
||||
const bool do_fast_gather,
|
||||
const bool is_foreground,
|
||||
inout DofGatherData accum_data)
|
||||
{
|
||||
float layer_weight = dof_layer_weight(center_data.coc, is_foreground);
|
||||
float sample_weight = dof_sample_weight(center_data.coc);
|
||||
float weight = layer_weight * sample_weight;
|
||||
float accum_weight = dof_gather_accum_weight(center_data.coc, bordering_radius, false);
|
||||
|
||||
if (do_fast_gather) {
|
||||
/* Hope for the compiler to optimize the above. */
|
||||
layer_weight = 1.0;
|
||||
sample_weight = 1.0;
|
||||
accum_weight = 1.0;
|
||||
weight = 1.0;
|
||||
}
|
||||
|
||||
center_data.transparency = 1.0 - weight;
|
||||
|
||||
dof_gather_accumulate_sample(center_data, weight * accum_weight, accum_data);
|
||||
|
||||
if (!do_fast_gather) {
|
||||
#ifdef DOF_RESOLVE_PASS
|
||||
/* NOTE(fclem): Hack to smooth transition to full in-focus opacity. */
|
||||
int total_sample_count = dof_gather_total_sample_count(i_radius + 1, DOF_SLIGHT_FOCUS_DENSITY);
|
||||
float fac = saturate(1.0 - abs(center_data.coc) / float(layer_threshold));
|
||||
accum_data.layer_opacity += float(total_sample_count) * fac * fac;
|
||||
#endif
|
||||
accum_data.layer_opacity += layer_weight;
|
||||
|
||||
/* Logic of dof_gather_accumulate_sample(). */
|
||||
weight *= (1.0 - accum_weight);
|
||||
center_data.coc_sqr = center_data.coc * (center_data.coc * weight);
|
||||
center_data.color *= weight;
|
||||
center_data.coc *= weight;
|
||||
center_data.weight = weight;
|
||||
|
||||
#ifdef DOF_FOREGROUND_PASS /* Reduce issue with closer foreground over distant foreground. */
|
||||
float ring_area = sqr(bordering_radius);
|
||||
dof_gather_ammend_weight(center_data, ring_area);
|
||||
#endif
|
||||
|
||||
/* Accumulate center as its own ring. */
|
||||
dof_gather_accumulate_sample_ring(
|
||||
center_data, 1, false, do_fast_gather, is_foreground, accum_data);
|
||||
}
|
||||
}
|
||||
|
||||
int dof_gather_total_sample_count_with_density_change(const int ring_count,
|
||||
const int ring_density,
|
||||
int density_change)
|
||||
{
|
||||
int sample_count_per_density_change = dof_gather_total_sample_count(ring_count, ring_density) -
|
||||
dof_gather_total_sample_count(
|
||||
ring_count - gather_density_change_ring, ring_density);
|
||||
|
||||
return dof_gather_total_sample_count(ring_count, ring_density) +
|
||||
sample_count_per_density_change * density_change;
|
||||
}
|
||||
|
||||
void dof_gather_accumulate_resolve(int total_sample_count,
|
||||
DofGatherData accum_data,
|
||||
out vec4 out_col,
|
||||
out float out_weight,
|
||||
out vec2 out_occlusion)
|
||||
{
|
||||
float weight_inv = safe_rcp(accum_data.weight);
|
||||
out_col = accum_data.color * weight_inv;
|
||||
out_occlusion = vec2(abs(accum_data.coc), accum_data.coc_sqr) * weight_inv;
|
||||
|
||||
#ifdef DOF_FOREGROUND_PASS
|
||||
out_weight = 1.0 - accum_data.transparency;
|
||||
#else
|
||||
if (accum_data.weight > 0.0) {
|
||||
out_weight = accum_data.layer_opacity / float(total_sample_count);
|
||||
}
|
||||
else {
|
||||
out_weight = 0.0;
|
||||
}
|
||||
#endif
|
||||
/* Gathering may not accumulate to 1.0 alpha because of float precision. */
|
||||
if (out_weight > 0.99) {
|
||||
out_weight = 1.0;
|
||||
}
|
||||
else if (out_weight < 0.01) {
|
||||
out_weight = 0.0;
|
||||
}
|
||||
/* Same thing for alpha channel. */
|
||||
if (out_col.a > 0.99) {
|
||||
out_col.a = 1.0;
|
||||
}
|
||||
else if (out_col.a < 0.01) {
|
||||
out_col.a = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
ivec2 dof_square_ring_sample_offset(int ring_distance, int sample_id)
|
||||
{
|
||||
/**
|
||||
* Generate samples in a square pattern with the ring radius. X is the center tile.
|
||||
*
|
||||
* Dist1 Dist2
|
||||
* 6 5 4 3 2
|
||||
* 3 2 1 7 1
|
||||
* . X 0 . X 0
|
||||
* . . . . .
|
||||
* . . . . .
|
||||
*
|
||||
* Samples are expected to be mirrored to complete the pattern.
|
||||
**/
|
||||
ivec2 offset;
|
||||
if (sample_id < ring_distance) {
|
||||
offset.x = ring_distance;
|
||||
offset.y = sample_id;
|
||||
}
|
||||
else if (sample_id < ring_distance * 3) {
|
||||
offset.x = ring_distance - sample_id + ring_distance;
|
||||
offset.y = ring_distance;
|
||||
}
|
||||
else {
|
||||
offset.x = -ring_distance;
|
||||
offset.y = ring_distance - sample_id + 3 * ring_distance;
|
||||
}
|
||||
return offset;
|
||||
}
|
@@ -0,0 +1,179 @@
|
||||
|
||||
/**
|
||||
* Reduce pass: Downsample the color buffer to generate mipmaps.
|
||||
* Also decide if a pixel is to be convolved by scattering or gathering during the first pass.
|
||||
**/
|
||||
|
||||
#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
|
||||
|
||||
/** Inputs:
|
||||
* COPY_PASS: Is output of setup pass (halfres) and downsample pass (quarter res).
|
||||
* REDUCE_PASS: Is previous Gather input miplvl (halfres >> miplvl).
|
||||
**/
|
||||
uniform sampler2D colorBuffer;
|
||||
uniform sampler2D cocBuffer;
|
||||
uniform sampler2D downsampledBuffer;
|
||||
|
||||
uniform vec2 bokehAnisotropy;
|
||||
uniform float scatterColorThreshold;
|
||||
uniform float scatterCocThreshold;
|
||||
uniform float scatterColorNeighborMax;
|
||||
uniform float colorNeighborClamping;
|
||||
|
||||
/** Outputs:
|
||||
* COPY_PASS: Gather input mip0.
|
||||
* REDUCE_PASS: Is next Gather input miplvl (halfres >> miplvl).
|
||||
**/
|
||||
layout(location = 0) out vec4 outColor;
|
||||
layout(location = 1) out float outCoc;
|
||||
|
||||
#ifdef COPY_PASS
|
||||
|
||||
layout(location = 2) out vec3 outScatterColor;
|
||||
|
||||
/* NOTE: Do not compare alpha as it is not scattered by the scatter pass. */
|
||||
float dof_scatter_neighborhood_rejection(vec3 color)
|
||||
{
|
||||
color = min(vec3(scatterColorNeighborMax), color);
|
||||
|
||||
float validity = 0.0;
|
||||
|
||||
/* Centered in the middle of 4 quarter res texel. */
|
||||
vec2 texel_size = 1.0 / vec2(textureSize(downsampledBuffer, 0).xy);
|
||||
vec2 uv = (gl_FragCoord.xy * 0.5) * texel_size;
|
||||
|
||||
vec3 max_diff = vec3(0.0);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
vec2 sample_uv = uv + quad_offsets[i] * texel_size;
|
||||
vec3 ref = textureLod(downsampledBuffer, sample_uv, 0.0).rgb;
|
||||
|
||||
ref = min(vec3(scatterColorNeighborMax), ref);
|
||||
float diff = max_v3(max(vec3(0.0), abs(ref - color)));
|
||||
|
||||
const float rejection_threshold = 0.7;
|
||||
diff = saturate(diff / rejection_threshold - 1.0);
|
||||
validity = max(validity, diff);
|
||||
}
|
||||
|
||||
return validity;
|
||||
}
|
||||
|
||||
/* This avoids sprite popping in and out at the screen border and
|
||||
* drawing sprites larger than the screen. */
|
||||
float dof_scatter_screen_border_rejection(float coc, vec2 uv, vec2 screen_size)
|
||||
{
|
||||
vec2 screen_pos = uv * screen_size;
|
||||
float min_screen_border_distance = min_v2(min(screen_pos, screen_size - screen_pos));
|
||||
/* Fullres to halfres CoC. */
|
||||
coc *= 0.5;
|
||||
/* Allow 10px transition. */
|
||||
const float rejection_hardeness = 1.0 / 10.0;
|
||||
return saturate((min_screen_border_distance - abs(coc)) * rejection_hardeness + 1.0);
|
||||
}
|
||||
|
||||
float dof_scatter_luminosity_rejection(vec3 color)
|
||||
{
|
||||
const float rejection_hardness = 1.0;
|
||||
return saturate(max_v3(color - scatterColorThreshold) * rejection_hardness);
|
||||
}
|
||||
|
||||
float dof_scatter_coc_radius_rejection(float coc)
|
||||
{
|
||||
const float rejection_hardness = 0.3;
|
||||
return saturate((abs(coc) - scatterCocThreshold) * rejection_hardness);
|
||||
}
|
||||
|
||||
float fast_luma(vec3 color)
|
||||
{
|
||||
return (2.0 * color.g) + color.r + color.b;
|
||||
}
|
||||
|
||||
/* Lightweight version of neighborhood clamping found in TAA. */
|
||||
vec3 dof_neighborhood_clamping(vec3 color)
|
||||
{
|
||||
vec2 texel_size = 1.0 / vec2(textureSize(colorBuffer, 0));
|
||||
vec2 uv = gl_FragCoord.xy * texel_size;
|
||||
vec4 ofs = vec4(-1, 1, -1, 1) * texel_size.xxyy;
|
||||
|
||||
/* Luma clamping. 3x3 square neighborhood. */
|
||||
float c00 = fast_luma(textureLod(colorBuffer, uv + ofs.xz, 0.0).rgb);
|
||||
float c01 = fast_luma(textureLod(colorBuffer, uv + ofs.xz * vec2(1.0, 0.0), 0.0).rgb);
|
||||
float c02 = fast_luma(textureLod(colorBuffer, uv + ofs.xw, 0.0).rgb);
|
||||
|
||||
float c10 = fast_luma(textureLod(colorBuffer, uv + ofs.xz * vec2(0.0, 1.0), 0.0).rgb);
|
||||
float c11 = fast_luma(color);
|
||||
float c12 = fast_luma(textureLod(colorBuffer, uv + ofs.xw * vec2(0.0, 1.0), 0.0).rgb);
|
||||
|
||||
float c20 = fast_luma(textureLod(colorBuffer, uv + ofs.yz, 0.0).rgb);
|
||||
float c21 = fast_luma(textureLod(colorBuffer, uv + ofs.yz * vec2(1.0, 0.0), 0.0).rgb);
|
||||
float c22 = fast_luma(textureLod(colorBuffer, uv + ofs.yw, 0.0).rgb);
|
||||
|
||||
float avg_luma = avg8(c00, c01, c02, c10, c12, c20, c21, c22);
|
||||
float max_luma = max8(c00, c01, c02, c10, c12, c20, c21, c22);
|
||||
|
||||
float upper_bound = mix(max_luma, avg_luma, colorNeighborClamping);
|
||||
upper_bound = mix(c11, upper_bound, colorNeighborClamping);
|
||||
|
||||
float clamped_luma = min(upper_bound, c11);
|
||||
|
||||
return color * clamped_luma * safe_rcp(c11);
|
||||
}
|
||||
|
||||
/* Simple copy pass where we select what pixels to scatter. Also the resolution might change.
|
||||
* NOTE: The texture can end up being too big because of the mipmap padding. We correct for
|
||||
* that during the convolution phase. */
|
||||
void main()
|
||||
{
|
||||
vec2 halfres = vec2(textureSize(colorBuffer, 0).xy);
|
||||
vec2 uv = gl_FragCoord.xy / halfres;
|
||||
|
||||
outColor = textureLod(colorBuffer, uv, 0.0);
|
||||
outCoc = textureLod(cocBuffer, uv, 0.0).r;
|
||||
|
||||
outColor.rgb = dof_neighborhood_clamping(outColor.rgb);
|
||||
|
||||
/* Only scatter if luminous enough. */
|
||||
float do_scatter = dof_scatter_luminosity_rejection(outColor.rgb);
|
||||
/* Only scatter if CoC is big enough. */
|
||||
do_scatter *= dof_scatter_coc_radius_rejection(outCoc);
|
||||
/* Only scatter if CoC is not too big to avoid performance issues. */
|
||||
do_scatter *= dof_scatter_screen_border_rejection(outCoc, uv, halfres);
|
||||
/* Only scatter if neighborhood is different enough. */
|
||||
do_scatter *= dof_scatter_neighborhood_rejection(outColor.rgb);
|
||||
/* For debuging. */
|
||||
do_scatter *= float(!no_scatter_pass);
|
||||
|
||||
outScatterColor = mix(vec3(0.0), outColor.rgb, do_scatter);
|
||||
outColor.rgb = mix(outColor.rgb, vec3(0.0), do_scatter);
|
||||
|
||||
/* Apply energy conservation to anamorphic scattered bokeh. */
|
||||
outScatterColor /= min_v2(bokehAnisotropy);
|
||||
}
|
||||
|
||||
#else /* REDUCE_PASS */
|
||||
|
||||
/* Downsample pass done for each mip starting from mip1. */
|
||||
void main()
|
||||
{
|
||||
vec2 input_texel_size = 1.0 / vec2(textureSize(colorBuffer, 0).xy);
|
||||
/* Center uv around the 4 pixels of the previous mip. */
|
||||
vec2 quad_center = (floor(gl_FragCoord.xy) * 2.0 + 1.0) * input_texel_size;
|
||||
|
||||
vec4 colors[4];
|
||||
vec4 cocs;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
vec2 sample_uv = quad_center + quad_offsets[i] * input_texel_size;
|
||||
colors[i] = dof_load_gather_color(colorBuffer, sample_uv, 0.0);
|
||||
cocs[i] = textureLod(cocBuffer, sample_uv, 0.0).r;
|
||||
}
|
||||
|
||||
vec4 weights = dof_downsample_bilateral_coc_weights(cocs);
|
||||
weights *= dof_downsample_bilateral_color_weights(colors);
|
||||
/* Normalize so that the sum is 1. */
|
||||
weights *= safe_rcp(sum(weights));
|
||||
|
||||
outColor = weighted_sum_array(colors, weights);
|
||||
outCoc = dot(cocs, weights);
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,212 @@
|
||||
|
||||
/**
|
||||
* Recombine Pass: Load separate convolution layer and composite with self slight defocus
|
||||
* convolution and in-focus fields.
|
||||
*
|
||||
* The halfres gather methods are fast but lack precision for small CoC areas. To fix this we
|
||||
* do a bruteforce gather to have a smooth transition between in-focus and defocus regions.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
|
||||
|
||||
uniform sampler2D fullResColorBuffer;
|
||||
uniform sampler2D fullResDepthBuffer;
|
||||
|
||||
uniform sampler2D bgColorBuffer;
|
||||
uniform sampler2D bgWeightBuffer;
|
||||
uniform sampler2D bgTileBuffer;
|
||||
|
||||
uniform sampler2D fgColorBuffer;
|
||||
uniform sampler2D fgWeightBuffer;
|
||||
uniform sampler2D fgTileBuffer;
|
||||
|
||||
uniform sampler2D holefillColorBuffer;
|
||||
uniform sampler2D holefillWeightBuffer;
|
||||
|
||||
uniform sampler2D bokehLut;
|
||||
|
||||
uniform float bokehMaxSize;
|
||||
|
||||
in vec4 uvcoordsvar;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void dof_slight_focus_gather(float radius, out vec4 out_color, out float out_weight)
|
||||
{
|
||||
/* offset coord to avoid correlation with sampling pattern. */
|
||||
vec4 noise = texelfetch_noise_tex(gl_FragCoord.xy + 7.0);
|
||||
|
||||
DofGatherData fg_accum = GATHER_DATA_INIT;
|
||||
DofGatherData bg_accum = GATHER_DATA_INIT;
|
||||
|
||||
int i_radius = clamp(int(radius), 0, int(layer_threshold));
|
||||
const int resolve_ring_density = DOF_SLIGHT_FOCUS_DENSITY;
|
||||
ivec2 texel = ivec2(gl_FragCoord.xy);
|
||||
|
||||
bool first_ring = true;
|
||||
|
||||
for (int ring = i_radius; ring > 0; ring--) {
|
||||
DofGatherData fg_ring = GATHER_DATA_INIT;
|
||||
DofGatherData bg_ring = GATHER_DATA_INIT;
|
||||
|
||||
int ring_distance = ring;
|
||||
int ring_sample_count = resolve_ring_density * ring_distance;
|
||||
for (int sample_id = 0; sample_id < ring_sample_count; sample_id++) {
|
||||
int s = sample_id * (4 / resolve_ring_density) +
|
||||
int(noise.y * float((4 - resolve_ring_density) * ring_distance));
|
||||
|
||||
ivec2 offset = dof_square_ring_sample_offset(ring_distance, s);
|
||||
float ring_dist = length(vec2(offset));
|
||||
|
||||
DofGatherData pair_data[2];
|
||||
for (int i = 0; i < 2; i++) {
|
||||
ivec2 sample_offset = ((i == 0) ? offset : -offset);
|
||||
ivec2 sample_texel = texel + sample_offset;
|
||||
/* OPTI: could precompute the factor. */
|
||||
vec2 sample_uv = (vec2(sample_texel) + 0.5) / vec2(textureSize(fullResDepthBuffer, 0));
|
||||
float depth = textureLod(fullResDepthBuffer, sample_uv, 0.0).r;
|
||||
pair_data[i].color = safe_color(textureLod(fullResColorBuffer, sample_uv, 0.0));
|
||||
pair_data[i].coc = dof_coc_from_zdepth(depth);
|
||||
pair_data[i].dist = ring_dist;
|
||||
#ifdef DOF_BOKEH_TEXTURE
|
||||
/* Contains subpixel distance to bokeh shape. */
|
||||
pair_data[i].dist = texelFetch(bokehLut, sample_offset + DOF_MAX_SLIGHT_FOCUS_RADIUS, 0).r;
|
||||
#endif
|
||||
pair_data[i].coc = clamp(pair_data[i].coc, -bokehMaxSize, bokehMaxSize);
|
||||
}
|
||||
|
||||
float bordering_radius = ring_dist + 0.5;
|
||||
const float isect_mul = 1.0;
|
||||
dof_gather_accumulate_sample_pair(
|
||||
pair_data, bordering_radius, isect_mul, first_ring, false, false, bg_ring, bg_accum);
|
||||
|
||||
#ifdef DOF_BOKEH_TEXTURE
|
||||
/* Swap distances in order to flip bokeh shape for foreground. */
|
||||
float tmp = pair_data[0].dist;
|
||||
pair_data[0].dist = pair_data[1].dist;
|
||||
pair_data[1].dist = tmp;
|
||||
#endif
|
||||
dof_gather_accumulate_sample_pair(
|
||||
pair_data, bordering_radius, isect_mul, first_ring, false, true, fg_ring, fg_accum);
|
||||
}
|
||||
|
||||
dof_gather_accumulate_sample_ring(
|
||||
bg_ring, ring_sample_count * 2, first_ring, false, false, bg_accum);
|
||||
dof_gather_accumulate_sample_ring(
|
||||
fg_ring, ring_sample_count * 2, first_ring, false, true, fg_accum);
|
||||
|
||||
first_ring = false;
|
||||
}
|
||||
|
||||
/* Center sample. */
|
||||
vec2 sample_uv = uvcoordsvar.xy;
|
||||
float depth = textureLod(fullResDepthBuffer, sample_uv, 0.0).r;
|
||||
DofGatherData center_data;
|
||||
center_data.color = safe_color(textureLod(fullResColorBuffer, sample_uv, 0.0));
|
||||
center_data.coc = dof_coc_from_zdepth(depth);
|
||||
center_data.coc = clamp(center_data.coc, -bokehMaxSize, bokehMaxSize);
|
||||
center_data.dist = 0.0;
|
||||
|
||||
/* Slide 38. */
|
||||
float bordering_radius = 0.5;
|
||||
|
||||
dof_gather_accumulate_center_sample(
|
||||
center_data, bordering_radius, i_radius, false, true, fg_accum);
|
||||
dof_gather_accumulate_center_sample(
|
||||
center_data, bordering_radius, i_radius, false, false, bg_accum);
|
||||
|
||||
vec4 bg_col, fg_col;
|
||||
float bg_weight, fg_weight;
|
||||
vec2 unused_occlusion;
|
||||
|
||||
int total_sample_count = dof_gather_total_sample_count(i_radius + 1, resolve_ring_density);
|
||||
dof_gather_accumulate_resolve(total_sample_count, bg_accum, bg_col, bg_weight, unused_occlusion);
|
||||
dof_gather_accumulate_resolve(total_sample_count, fg_accum, fg_col, fg_weight, unused_occlusion);
|
||||
|
||||
/* Fix weighting issues on perfectly focus > slight focus transitionning areas. */
|
||||
if (abs(center_data.coc) < 0.5) {
|
||||
bg_col = center_data.color;
|
||||
bg_weight = 1.0;
|
||||
}
|
||||
|
||||
/* Alpha Over */
|
||||
float alpha = 1.0 - fg_weight;
|
||||
out_weight = bg_weight * alpha + fg_weight;
|
||||
out_color = bg_col * bg_weight * alpha + fg_col * fg_weight;
|
||||
}
|
||||
|
||||
void dof_resolve_load_layer(sampler2D color_tex,
|
||||
sampler2D weight_tex,
|
||||
out vec4 out_color,
|
||||
out float out_weight)
|
||||
{
|
||||
vec2 pixel_co = gl_FragCoord.xy / 2.0;
|
||||
vec2 uv = pixel_co / textureSize(color_tex, 0).xy;
|
||||
out_color = textureLod(color_tex, uv, 0.0);
|
||||
out_weight = textureLod(weight_tex, uv, 0.0).r;
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
ivec2 tile_co = ivec2(gl_FragCoord.xy / float(DOF_TILE_DIVISOR));
|
||||
CocTile coc_tile = dof_coc_tile_load(fgTileBuffer, bgTileBuffer, tile_co);
|
||||
CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile);
|
||||
|
||||
fragColor = vec4(0.0);
|
||||
float weight = 0.0;
|
||||
|
||||
vec4 layer_color;
|
||||
float layer_weight;
|
||||
|
||||
if (!no_holefill_pass && prediction.do_holefill) {
|
||||
dof_resolve_load_layer(holefillColorBuffer, holefillWeightBuffer, layer_color, layer_weight);
|
||||
fragColor = layer_color * safe_rcp(layer_weight);
|
||||
weight = float(layer_weight > 0.0);
|
||||
}
|
||||
|
||||
if (!no_background_pass && prediction.do_background) {
|
||||
dof_resolve_load_layer(bgColorBuffer, bgWeightBuffer, layer_color, layer_weight);
|
||||
/* Always prefer background to holefill pass. */
|
||||
layer_color *= safe_rcp(layer_weight);
|
||||
layer_weight = float(layer_weight > 0.0);
|
||||
/* Composite background. */
|
||||
fragColor = fragColor * (1.0 - layer_weight) + layer_color;
|
||||
weight = weight * (1.0 - layer_weight) + layer_weight;
|
||||
/* Fill holes with the composited background. */
|
||||
fragColor *= safe_rcp(weight);
|
||||
weight = float(weight > 0.0);
|
||||
}
|
||||
|
||||
if (!no_slight_focus_pass && prediction.do_slight_focus) {
|
||||
dof_slight_focus_gather(coc_tile.fg_slight_focus_max_coc, layer_color, layer_weight);
|
||||
/* Composite slight defocus. */
|
||||
fragColor = fragColor * (1.0 - layer_weight) + layer_color;
|
||||
weight = weight * (1.0 - layer_weight) + layer_weight;
|
||||
}
|
||||
|
||||
if (!no_focus_pass && prediction.do_focus) {
|
||||
layer_color = safe_color(textureLod(fullResColorBuffer, uvcoordsvar.xy, 0.0));
|
||||
layer_weight = 1.0;
|
||||
/* Composite in focus. */
|
||||
fragColor = fragColor * (1.0 - layer_weight) + layer_color;
|
||||
weight = weight * (1.0 - layer_weight) + layer_weight;
|
||||
}
|
||||
|
||||
if (!no_foreground_pass && prediction.do_foreground) {
|
||||
dof_resolve_load_layer(fgColorBuffer, fgWeightBuffer, layer_color, layer_weight);
|
||||
/* Composite foreground. */
|
||||
fragColor = fragColor * (1.0 - layer_weight) + layer_color;
|
||||
}
|
||||
|
||||
/* Fix float precision issue in alpha compositing. */
|
||||
if (fragColor.a > 0.99) {
|
||||
fragColor.a = 1.0;
|
||||
}
|
||||
|
||||
#if 0 /* Debug */
|
||||
if (coc_tile.fg_slight_focus_max_coc >= 0.5) {
|
||||
fragColor.rgb *= vec3(1.0, 0.1, 0.1);
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
|
||||
/**
|
||||
* Scatter pass: Use sprites to scatter the color of very bright pixel to have higher quality blur.
|
||||
*
|
||||
* We only scatter one triangle per sprite and one sprite per 4 pixels to reduce vertex shader
|
||||
* invocations and overdraw.
|
||||
**/
|
||||
|
||||
#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
|
||||
|
||||
uniform sampler2D occlusionBuffer;
|
||||
uniform sampler2D bokehLut;
|
||||
|
||||
uniform vec2 bokehAnisotropyInv;
|
||||
|
||||
flat in vec4 color1;
|
||||
flat in vec4 color2;
|
||||
flat in vec4 color3;
|
||||
flat in vec4 color4;
|
||||
flat in vec4 weights;
|
||||
flat in vec4 cocs;
|
||||
flat in vec2 spritepos;
|
||||
flat in float spritesize; /* MaxCoC */
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
float bokeh_shape(vec2 center)
|
||||
{
|
||||
vec2 co = gl_FragCoord.xy - center;
|
||||
|
||||
#ifdef DOF_BOKEH_TEXTURE
|
||||
co *= bokehAnisotropyInv;
|
||||
float texture_size = float(textureSize(bokehLut, 0).x);
|
||||
/* Bias scale to avoid sampling at the texture's border. */
|
||||
float scale_fac = spritesize * (float(DOF_BOKEH_LUT_SIZE) / float(DOF_BOKEH_LUT_SIZE - 1));
|
||||
float dist = scale_fac * textureLod(bokehLut, (co / scale_fac) * 0.5 + 0.5, 0.0).r;
|
||||
#else
|
||||
float dist = length(co);
|
||||
#endif
|
||||
|
||||
return dist;
|
||||
}
|
||||
|
||||
#define linearstep(p0, p1, v) (clamp(((v) - (p0)) / abs((p1) - (p0)), 0.0, 1.0))
|
||||
|
||||
void main(void)
|
||||
{
|
||||
vec4 shapes;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
shapes[i] = bokeh_shape(spritepos + quad_offsets[i]);
|
||||
}
|
||||
/* Becomes signed distance field in pixel units. */
|
||||
shapes -= cocs;
|
||||
/* Smooth the edges a bit to fade out the undersampling artifacts. */
|
||||
shapes = 1.0 - linearstep(-0.8, 0.8, shapes);
|
||||
/* Outside of bokeh shape. Try to avoid overloading ROPs. */
|
||||
if (max_v4(shapes) == 0.0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
if (!no_scatter_occlusion) {
|
||||
/* Works because target is the same size as occlusionBuffer. */
|
||||
vec2 uv = gl_FragCoord.xy / vec2(textureSize(occlusionBuffer, 0).xy);
|
||||
vec2 occlusion_data = texture(occlusionBuffer, uv).rg;
|
||||
/* Fix tilling artifacts. (Slide 90) */
|
||||
const float correction_fac = 1.0 - DOF_FAST_GATHER_COC_ERROR;
|
||||
/* Occlude the sprite with geometry from the same field
|
||||
* using a VSM like chebychev test (slide 85). */
|
||||
float mean = occlusion_data.x;
|
||||
float variance = occlusion_data.x;
|
||||
shapes *= variance * safe_rcp(variance + sqr(max(cocs * correction_fac - mean, 0.0)));
|
||||
}
|
||||
|
||||
fragColor = color1 * shapes.x;
|
||||
fragColor += color2 * shapes.y;
|
||||
fragColor += color3 * shapes.z;
|
||||
fragColor += color4 * shapes.w;
|
||||
|
||||
/* Do not accumulate alpha. This has already been accumulated by the gather pass. */
|
||||
fragColor.a = 0.0;
|
||||
|
||||
#ifdef DOF_DEBUG_SCATTER_PERF
|
||||
fragColor.rgb = avg(fragColor.rgb) * vec3(1.0, 0.0, 0.0);
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,138 @@
|
||||
|
||||
#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
|
||||
|
||||
uniform vec2 targetTexelSize;
|
||||
uniform int spritePerRow;
|
||||
uniform vec2 bokehAnisotropy;
|
||||
|
||||
uniform sampler2D colorBuffer;
|
||||
uniform sampler2D cocBuffer;
|
||||
|
||||
/* Scatter pass, calculate a triangle covering the CoC.
|
||||
* We render to a half resolution target with double width so we can
|
||||
* separate near and far fields. We also generate only one triangle per group of 4 pixels
|
||||
* to limit overdraw. */
|
||||
|
||||
flat out vec4 color1;
|
||||
flat out vec4 color2;
|
||||
flat out vec4 color3;
|
||||
flat out vec4 color4;
|
||||
flat out vec4 weights;
|
||||
flat out vec4 cocs;
|
||||
flat out vec2 spritepos;
|
||||
flat out float spritesize;
|
||||
|
||||
/* Load 4 Circle of confusion values. texel_co is centered around the 4 taps. */
|
||||
vec4 fetch_cocs(vec2 texel_co)
|
||||
{
|
||||
/* TODO(fclem) The textureGather(sampler, co, comp) variant isn't here on some implementations.*/
|
||||
#if 0 // GPU_ARB_texture_gather
|
||||
vec2 uvs = texel_co / vec2(textureSize(cocBuffer, 0));
|
||||
/* Reminder: Samples order is CW starting from top left. */
|
||||
cocs = textureGather(cocBuffer, uvs, isForegroundPass ? 0 : 1);
|
||||
#else
|
||||
ivec2 texel = ivec2(texel_co - 0.5);
|
||||
vec4 cocs;
|
||||
cocs.x = texelFetchOffset(cocBuffer, texel, 0, ivec2(0, 1)).r;
|
||||
cocs.y = texelFetchOffset(cocBuffer, texel, 0, ivec2(1, 1)).r;
|
||||
cocs.z = texelFetchOffset(cocBuffer, texel, 0, ivec2(1, 0)).r;
|
||||
cocs.w = texelFetchOffset(cocBuffer, texel, 0, ivec2(0, 0)).r;
|
||||
#endif
|
||||
|
||||
#ifdef DOF_FOREGROUND_PASS
|
||||
cocs *= -1.0;
|
||||
#endif
|
||||
|
||||
cocs = max(vec4(0.0), cocs);
|
||||
/* We are scattering at half resolution, so divide CoC by 2. */
|
||||
return cocs * 0.5;
|
||||
}
|
||||
|
||||
void vertex_discard()
|
||||
{
|
||||
/* Don't produce any fragments */
|
||||
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 tex_size = textureSize(cocBuffer, 0);
|
||||
|
||||
int t_id = gl_VertexID / 3; /* Triangle Id */
|
||||
|
||||
/* Some math to get the target pixel. */
|
||||
ivec2 texelco = ivec2(t_id % spritePerRow, t_id / spritePerRow) * 2;
|
||||
|
||||
/* Center sprite around the 4 texture taps. */
|
||||
spritepos = vec2(texelco) + 1.0;
|
||||
|
||||
cocs = fetch_cocs(spritepos);
|
||||
|
||||
/* Early out from local CoC radius. */
|
||||
if (all(lessThan(cocs, vec4(0.5)))) {
|
||||
vertex_discard();
|
||||
return;
|
||||
}
|
||||
|
||||
vec2 input_texel_size = 1.0 / vec2(tex_size);
|
||||
vec2 quad_center = spritepos * input_texel_size;
|
||||
vec4 colors[4];
|
||||
bool no_color = true;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
vec2 sample_uv = quad_center + quad_offsets[i] * input_texel_size;
|
||||
|
||||
colors[i] = dof_load_scatter_color(colorBuffer, sample_uv, 0.0);
|
||||
no_color = no_color && all(equal(colors[i].rgb, vec3(0.0)));
|
||||
}
|
||||
|
||||
/* Early out from no color to scatter. */
|
||||
if (no_color) {
|
||||
vertex_discard();
|
||||
return;
|
||||
}
|
||||
|
||||
weights = dof_layer_weight(cocs) * dof_sample_weight(cocs);
|
||||
/* Filter NaNs. */
|
||||
weights = mix(weights, vec4(0.0), equal(cocs, vec4(0.0)));
|
||||
|
||||
color1 = colors[0] * weights[0];
|
||||
color2 = colors[1] * weights[1];
|
||||
color3 = colors[2] * weights[2];
|
||||
color4 = colors[3] * weights[3];
|
||||
|
||||
/* Extend to cover at least the unit circle */
|
||||
const float extend = (cos(M_PI / 4.0) + 1.0) * 2.0;
|
||||
/* Crappy diagram
|
||||
* ex 1
|
||||
* | \
|
||||
* | \
|
||||
* 1 | \
|
||||
* | \
|
||||
* | \
|
||||
* 0 | x \
|
||||
* | Circle \
|
||||
* | Origin \
|
||||
* -1 0 --------------- 2
|
||||
* -1 0 1 ex
|
||||
*/
|
||||
|
||||
/* Generate Triangle : less memory fetches from a VBO */
|
||||
int v_id = gl_VertexID % 3; /* Vertex Id */
|
||||
gl_Position.x = float(v_id / 2) * extend - 1.0; /* int divisor round down */
|
||||
gl_Position.y = float(v_id % 2) * extend - 1.0;
|
||||
gl_Position.z = 0.0;
|
||||
gl_Position.w = 1.0;
|
||||
|
||||
spritesize = max_v4(cocs);
|
||||
|
||||
/* Add 2.5 to max_coc because the max_coc may not be centered on the sprite origin
|
||||
* and because we smooth the bokeh shape a bit in the pixel shader. */
|
||||
gl_Position.xy *= spritesize * bokehAnisotropy + 2.5;
|
||||
/* Position the sprite. */
|
||||
gl_Position.xy += spritepos;
|
||||
/* NDC range [-1..1]. */
|
||||
gl_Position.xy = gl_Position.xy * targetTexelSize * 2.0 - 1.0;
|
||||
|
||||
/* Add 2.5 for the same reason but without the ratio. */
|
||||
spritesize += 2.5;
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
|
||||
/**
|
||||
* Setup pass: CoC and luma aware downsample to half resolution of the input scene color buffer.
|
||||
*
|
||||
* An addition to the downsample CoC, we output the maximum slight out of focus CoC to be
|
||||
* sure we don't miss a pixel.
|
||||
**/
|
||||
|
||||
#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
|
||||
|
||||
/* Full resolution. */
|
||||
uniform sampler2D colorBuffer;
|
||||
uniform sampler2D depthBuffer;
|
||||
|
||||
uniform float bokehMaxSize;
|
||||
|
||||
/* Half resolution. */
|
||||
layout(location = 0) out vec4 outColor;
|
||||
layout(location = 1) out vec2 outCoc; /* x: Downsample CoC, y: Max slight focus abs CoC */
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 fullres_texel_size = 1.0 / vec2(textureSize(colorBuffer, 0).xy);
|
||||
/* Center uv around the 4 fullres pixels. */
|
||||
vec2 quad_center = (floor(gl_FragCoord.xy) * 2.0 + 1.0) * fullres_texel_size;
|
||||
|
||||
vec4 colors[4];
|
||||
vec4 depths;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
vec2 sample_uv = quad_center + quad_offsets[i] * fullres_texel_size;
|
||||
colors[i] = safe_color(textureLod(colorBuffer, sample_uv, 0.0));
|
||||
depths[i] = textureLod(depthBuffer, sample_uv, 0.0).r;
|
||||
}
|
||||
|
||||
vec4 cocs = dof_coc_from_zdepth(depths);
|
||||
|
||||
cocs = clamp(cocs, -bokehMaxSize, bokehMaxSize);
|
||||
|
||||
vec4 weights = dof_downsample_bilateral_coc_weights(cocs);
|
||||
weights *= dof_downsample_bilateral_color_weights(colors);
|
||||
/* Normalize so that the sum is 1. */
|
||||
weights *= safe_rcp(sum(weights));
|
||||
|
||||
outColor = weighted_sum_array(colors, weights);
|
||||
outCoc.x = dot(cocs, weights);
|
||||
|
||||
/* Max slight focus abs CoC. */
|
||||
|
||||
/* Clamp to 0.5 if full in defocus to differentiate full focus tiles with coc == 0.0.
|
||||
* This enables an optimization in the resolve pass. */
|
||||
const vec4 threshold = vec4(layer_threshold + layer_offset);
|
||||
cocs = abs(cocs);
|
||||
bvec4 defocus = greaterThan(cocs, threshold);
|
||||
bvec4 focus = lessThanEqual(cocs, vec4(0.5));
|
||||
if (any(defocus) && any(focus)) {
|
||||
/* For the same reason as in the flatten pass. This is a case we cannot optimize for. */
|
||||
cocs = mix(cocs, vec4(DOF_TILE_MIXED), focus);
|
||||
cocs = mix(cocs, vec4(DOF_TILE_MIXED), defocus);
|
||||
}
|
||||
else {
|
||||
cocs = mix(cocs, vec4(DOF_TILE_FOCUS), focus);
|
||||
cocs = mix(cocs, vec4(DOF_TILE_DEFOCUS), defocus);
|
||||
}
|
||||
outCoc.y = max_v4(cocs);
|
||||
}
|
@@ -1,109 +0,0 @@
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
|
||||
|
||||
uniform vec4 bokehParams[2];
|
||||
|
||||
#define bokeh_rotation bokehParams[0].x
|
||||
#define bokeh_ratio bokehParams[0].y
|
||||
#define bokeh_maxsize bokehParams[0].z
|
||||
|
||||
uniform sampler2D nearBuffer;
|
||||
uniform sampler2D farBuffer;
|
||||
uniform sampler2D cocBuffer;
|
||||
|
||||
flat out vec4 color;
|
||||
flat out float weight;
|
||||
flat out float smoothFac;
|
||||
flat out ivec2 edge;
|
||||
out vec2 particlecoord;
|
||||
|
||||
/* Scatter pass, calculate a triangle covering the CoC. */
|
||||
void main()
|
||||
{
|
||||
ivec2 tex_size = textureSize(cocBuffer, 0);
|
||||
/* We render to a double width texture so compute
|
||||
* the target texel size accordingly */
|
||||
vec2 texel_size = vec2(0.5, 1.0) / vec2(tex_size);
|
||||
|
||||
int t_id = gl_VertexID / 3; /* Triangle Id */
|
||||
|
||||
ivec2 texelco = ivec2(0);
|
||||
/* some math to get the target pixel */
|
||||
texelco.x = t_id % tex_size.x;
|
||||
texelco.y = t_id / tex_size.x;
|
||||
|
||||
vec2 cocs = texelFetch(cocBuffer, texelco, 0).rg;
|
||||
|
||||
bool is_near = (cocs.x > cocs.y);
|
||||
float coc = (is_near) ? cocs.x : cocs.y;
|
||||
|
||||
/* Clamp to max size for performance */
|
||||
coc = min(coc, bokeh_maxsize);
|
||||
|
||||
if (coc >= 1.0) {
|
||||
if (is_near) {
|
||||
color = texelFetch(nearBuffer, texelco, 0);
|
||||
}
|
||||
else {
|
||||
color = texelFetch(farBuffer, texelco, 0);
|
||||
}
|
||||
/* find the area the pixel will cover and divide the color by it */
|
||||
/* HACK: 4.0 out of nowhere (I suppose it's 4 pixels footprint for coc 0?)
|
||||
* Makes near in focus more closer to 1.0 alpha. */
|
||||
weight = 4.0 / (coc * coc * M_PI);
|
||||
color *= weight;
|
||||
|
||||
/* Compute edge to discard fragment that does not belong to the other layer. */
|
||||
edge.x = (is_near) ? 1 : -1;
|
||||
edge.y = (is_near) ? -tex_size.x + 1 : tex_size.x;
|
||||
}
|
||||
else {
|
||||
/* Don't produce any fragments */
|
||||
color = vec4(0.0);
|
||||
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Generate Triangle : less memory fetches from a VBO */
|
||||
int v_id = gl_VertexID % 3; /* Vertex Id */
|
||||
|
||||
/* Extend to cover at least the unit circle */
|
||||
const float extend = (cos(M_PI / 4.0) + 1.0) * 2.0;
|
||||
/* Crappy diagram
|
||||
* ex 1
|
||||
* | \
|
||||
* | \
|
||||
* 1 | \
|
||||
* | \
|
||||
* | \
|
||||
* 0 | x \
|
||||
* | Circle \
|
||||
* | Origin \
|
||||
* -1 0 --------------- 2
|
||||
* -1 0 1 ex
|
||||
*/
|
||||
gl_Position.x = float(v_id / 2) * extend - 1.0; /* int divisor round down */
|
||||
gl_Position.y = float(v_id % 2) * extend - 1.0;
|
||||
gl_Position.z = 0.0;
|
||||
gl_Position.w = 1.0;
|
||||
|
||||
/* Generate Triangle */
|
||||
particlecoord = gl_Position.xy;
|
||||
|
||||
gl_Position.xy *= coc * texel_size * vec2(bokeh_ratio, 1.0);
|
||||
gl_Position.xy -= 1.0 - 0.5 * texel_size; /* NDC Bottom left */
|
||||
gl_Position.xy += (0.5 + vec2(texelco) * 2.0) * texel_size;
|
||||
|
||||
/* Push far plane to left side. */
|
||||
if (!is_near) {
|
||||
gl_Position.x += 2.0 / 2.0;
|
||||
}
|
||||
|
||||
/* don't do smoothing for small sprites */
|
||||
if (coc > 3.0) {
|
||||
smoothFac = 1.0 - 1.5 / coc;
|
||||
}
|
||||
else {
|
||||
smoothFac = 1.0;
|
||||
}
|
||||
}
|
@@ -2,8 +2,6 @@
|
||||
#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)
|
||||
@@ -172,7 +170,7 @@ void main()
|
||||
/* Importance sampling bias */
|
||||
rand.x = mix(rand.x, 0.0, ssrBrdfBias);
|
||||
|
||||
vec3 W = transform_point(ViewMatrixInverse, viewPosition);
|
||||
vec3 worldPosition = transform_point(ViewMatrixInverse, viewPosition);
|
||||
vec3 wN = transform_direction(ViewMatrixInverse, N);
|
||||
|
||||
vec3 T, B;
|
||||
@@ -182,12 +180,12 @@ void main()
|
||||
for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) {
|
||||
PlanarData pd = planars_data[i];
|
||||
|
||||
float fade = probe_attenuation_planar(pd, W, wN, 0.0);
|
||||
float fade = probe_attenuation_planar(pd, worldPosition, 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(W, cameraVec, pd.pl_plane_eq);
|
||||
vec3 tracePosition = line_plane_intersect(worldPosition, cameraVec, pd.pl_plane_eq);
|
||||
tracePosition = transform_point(ViewMatrix, tracePosition);
|
||||
vec3 planeNormal = transform_direction(ViewMatrix, pd.pl_normal);
|
||||
|
||||
@@ -215,8 +213,6 @@ uniform sampler2D pdfBuffer;
|
||||
|
||||
uniform int neighborOffset;
|
||||
|
||||
in vec4 uvcoordsvar;
|
||||
|
||||
const ivec2 neighbors[32] = ivec2[32](ivec2(0, 0),
|
||||
ivec2(1, 1),
|
||||
ivec2(-2, 0),
|
||||
@@ -302,7 +298,7 @@ float get_sample_depth(vec2 hit_co, bool is_planar, float planar_index)
|
||||
|
||||
vec3 get_hit_vector(vec3 hit_pos,
|
||||
PlanarData pd,
|
||||
vec3 P,
|
||||
vec3 worldPosition,
|
||||
vec3 N,
|
||||
vec3 V,
|
||||
bool is_planar,
|
||||
@@ -313,7 +309,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(P, V, pd.pl_plane_eq);
|
||||
vec3 trace_pos = line_plane_intersect(worldPosition, 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. */
|
||||
@@ -321,8 +317,8 @@ vec3 get_hit_vector(vec3 hit_pos,
|
||||
}
|
||||
else {
|
||||
/* Find hit position in previous frame. */
|
||||
hit_co = get_reprojected_reflection(hit_pos, P, N);
|
||||
hit_vec = hit_pos - P;
|
||||
hit_co = get_reprojected_reflection(hit_pos, worldPosition, N);
|
||||
hit_vec = hit_pos - worldPosition;
|
||||
}
|
||||
|
||||
mask = screen_border_mask(hit_co);
|
||||
@@ -343,7 +339,7 @@ vec4 get_ssr_samples(vec4 hit_pdf,
|
||||
ivec4 hit_data[2],
|
||||
PlanarData pd,
|
||||
float planar_index,
|
||||
vec3 P,
|
||||
vec3 worldPosition,
|
||||
vec3 N,
|
||||
vec3 V,
|
||||
float roughnessSquared,
|
||||
@@ -383,10 +379,14 @@ 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, 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);
|
||||
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);
|
||||
|
||||
vec4 hit_dist;
|
||||
hit_dist.x = length(hit_pos[0]);
|
||||
@@ -476,30 +476,47 @@ vec4 get_ssr_samples(vec4 hit_pdf,
|
||||
return accum;
|
||||
}
|
||||
|
||||
void raytrace_resolve(ClosureInputGlossy cl_in,
|
||||
inout ClosureEvalGlossy cl_eval,
|
||||
inout ClosureEvalCommon cl_common,
|
||||
inout ClosureOutputGlossy cl_out)
|
||||
void main()
|
||||
{
|
||||
ivec2 fullres_texel = ivec2(gl_FragCoord.xy);
|
||||
# ifdef FULLRES
|
||||
ivec2 texel = ivec2(gl_FragCoord.xy);
|
||||
ivec2 halfres_texel = fullres_texel;
|
||||
# else
|
||||
ivec2 texel = ivec2(gl_FragCoord.xy / 2.0);
|
||||
ivec2 halfres_texel = ivec2(gl_FragCoord.xy / 2.0);
|
||||
# endif
|
||||
/* Using world space */
|
||||
vec3 V = cl_common.V;
|
||||
vec3 N = cl_in.N;
|
||||
vec3 P = cl_common.P;
|
||||
vec2 uvs = gl_FragCoord.xy / vec2(textureSize(depthBuffer, 0));
|
||||
|
||||
float roughness = cl_in.roughness;
|
||||
float roughnessSquared = max(1e-3, sqr(roughness));
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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, P).xy * 0.5 + 0.5;
|
||||
vec2 source_uvs = project_point(pastViewProjectionMatrix, worldPosition).xy * 0.5 + 0.5;
|
||||
|
||||
vec4 ssr_accum = vec4(0.0);
|
||||
float weight_acc = 0.0;
|
||||
@@ -508,16 +525,16 @@ void raytrace_resolve(ClosureInputGlossy cl_in,
|
||||
/* TODO optimize with textureGather */
|
||||
/* Doing these fetches early to hide latency. */
|
||||
vec4 hit_pdf;
|
||||
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;
|
||||
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;
|
||||
|
||||
ivec4 hit_data[2];
|
||||
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;
|
||||
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;
|
||||
|
||||
/* Find Planar Reflections affecting this pixel */
|
||||
PlanarData pd;
|
||||
@@ -525,7 +542,7 @@ void raytrace_resolve(ClosureInputGlossy cl_in,
|
||||
for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) {
|
||||
pd = planars_data[i];
|
||||
|
||||
float fade = probe_attenuation_planar(pd, P, N, 0.0);
|
||||
float fade = probe_attenuation_planar(pd, worldPosition, N, 0.0);
|
||||
|
||||
if (fade > 0.5) {
|
||||
planar_index = float(i);
|
||||
@@ -537,7 +554,7 @@ void raytrace_resolve(ClosureInputGlossy cl_in,
|
||||
hit_data,
|
||||
pd,
|
||||
planar_index,
|
||||
P,
|
||||
worldPosition,
|
||||
N,
|
||||
V,
|
||||
roughnessSquared,
|
||||
@@ -547,51 +564,19 @@ void raytrace_resolve(ClosureInputGlossy cl_in,
|
||||
}
|
||||
|
||||
/* Compute SSR contribution */
|
||||
ssr_accum *= (weight_acc == 0.0) ? 0.0 : (1.0 / weight_acc);
|
||||
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);
|
||||
|
||||
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;
|
||||
accumulate_light(ssr_accum.rgb, ssr_accum.a, 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;
|
||||
/* 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);
|
||||
}
|
||||
|
||||
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);
|
||||
fragColor = vec4(spec_accum.rgb * speccol_roughness.rgb, 1.0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -137,9 +137,9 @@ float probe_attenuation_planar(PlanarData pd, vec3 W, vec3 N, float roughness)
|
||||
return fac;
|
||||
}
|
||||
|
||||
float probe_attenuation_grid(GridData gd, vec3 W, out vec3 localpos)
|
||||
float probe_attenuation_grid(GridData gd, mat4 localmat, vec3 W, out vec3 localpos)
|
||||
{
|
||||
localpos = transform_point(gd.localmat, W);
|
||||
localpos = transform_point(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);
|
||||
@@ -183,7 +183,8 @@ vec3 probe_evaluate_world_spec(vec3 R, float roughness)
|
||||
return textureLod_cubemapArray(probeCubes, vec4(R, 0.0), roughness * prbLodCubeMax).rgb;
|
||||
}
|
||||
|
||||
vec3 probe_evaluate_planar(int id, PlanarData pd, vec3 W, vec3 N, vec3 V, float roughness)
|
||||
vec3 probe_evaluate_planar(
|
||||
float id, PlanarData pd, vec3 W, vec3 N, vec3 V, float roughness, inout float fade)
|
||||
{
|
||||
/* Find view vector / reflection plane intersection. */
|
||||
vec3 point_on_plane = line_plane_intersect(W, V, pd.pl_plane_eq);
|
||||
@@ -225,7 +226,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, rand, bent_normal);
|
||||
float final_ao = occlusion_compute(N, viewPosition, 1.0, rand, bent_normal);
|
||||
final_ao = specular_occlusion(dot(N, V), final_ao, roughness);
|
||||
#else
|
||||
const float final_ao = 1.0;
|
||||
|
@@ -252,29 +252,32 @@ float light_attenuation(LightData ld, vec4 l_vector)
|
||||
return vis;
|
||||
}
|
||||
|
||||
float light_shadowing(LightData ld, vec3 W, float 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)
|
||||
{
|
||||
#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
|
||||
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) {
|
||||
# ifndef VOLUMETRICS
|
||||
ShadowData sd = shadows_data[int(ld.l_shadowid)];
|
||||
/* Only compute if not already in shadow. */
|
||||
if (sd.sh_contact_dist > 0.0) {
|
||||
if (use_contact_shadows && sd.sh_contact_dist > 0.0 && vis > 1e-8) {
|
||||
/* Contact Shadows. */
|
||||
vec3 ray_ori, ray_dir;
|
||||
float trace_distance;
|
||||
@@ -284,34 +287,54 @@ float light_contact_shadows(
|
||||
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 - P;
|
||||
ray_dir = shadows_cube_data[int(sd.sh_data_index)].position.xyz - W;
|
||||
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(vP.xy, tracing_depth) + vNg * sd.sh_contact_offset;
|
||||
ray_ori = vec3(viewPosition.xy, tracing_depth) + true_normal * 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(vP, hit_pos);
|
||||
float hit_dist = distance(viewPosition, hit_pos);
|
||||
float dist_ratio = hit_dist / trace_distance;
|
||||
return saturate(dist_ratio * 3.0 - 2.0);
|
||||
return vis * saturate(dist_ratio * 3.0 - 2.0);
|
||||
}
|
||||
}
|
||||
# endif /* VOLUMETRICS */
|
||||
}
|
||||
return 1.0;
|
||||
}
|
||||
#endif /* VOLUMETRICS */
|
||||
#endif
|
||||
|
||||
float light_visibility(LightData ld, vec3 W, vec4 l_vector)
|
||||
return vis;
|
||||
}
|
||||
|
||||
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 l_atten = light_attenuation(ld, l_vector);
|
||||
return light_shadowing(ld, W, l_atten);
|
||||
return light_shadowing(ld,
|
||||
W,
|
||||
#ifndef VOLUMETRICS
|
||||
viewPosition,
|
||||
tracing_depth,
|
||||
true_normal,
|
||||
rand_x,
|
||||
use_contact_shadows,
|
||||
#endif
|
||||
l_atten);
|
||||
}
|
||||
|
||||
float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector)
|
||||
|
@@ -5,12 +5,8 @@
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_uniforms_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(closure_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(closure_lit_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(surface_lib.glsl)
|
||||
|
||||
#ifdef USE_ALPHA_HASH
|
||||
|
@@ -39,7 +39,7 @@ void main()
|
||||
vec3 viewPosition = get_view_space_from_depth(uvs, depth);
|
||||
vec3 worldPosition = transform_point(ViewMatrixInverse, viewPosition);
|
||||
|
||||
vec3 true_normal = safe_normalize(cross(dFdx(viewPosition), dFdy(viewPosition)));
|
||||
vec3 true_normal = normalize(cross(dFdx(viewPosition), dFdy(viewPosition)));
|
||||
|
||||
for (int i = 0; i < MAX_LIGHT && i < laNumLight; i++) {
|
||||
LightData ld = lights_data[i];
|
||||
@@ -48,10 +48,8 @@ void main()
|
||||
l_vector.xyz = ld.l_position - worldPosition;
|
||||
l_vector.w = length(l_vector.xyz);
|
||||
|
||||
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);
|
||||
float l_vis = light_shadowing(
|
||||
ld, worldPosition, viewPosition, tracing_depth, true_normal, rand.x, true, 1.0);
|
||||
|
||||
accum_light += l_vis;
|
||||
}
|
||||
|
@@ -3,13 +3,8 @@
|
||||
#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_utiltex_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(closure_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(closure_lit_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(surface_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(volumetric_lib.glsl)
|
||||
|
||||
|
@@ -13,15 +13,7 @@ uniform float refractionDepth;
|
||||
vec3 worldNormal; \
|
||||
vec3 viewNormal;
|
||||
|
||||
#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)
|
||||
#ifdef GPU_GEOMETRY_SHADER
|
||||
in ShaderStageInterface{SURFACE_INTERFACE} dataIn[];
|
||||
|
||||
out ShaderStageInterface{SURFACE_INTERFACE} dataOut;
|
||||
@@ -32,7 +24,7 @@ out ShaderStageInterface{SURFACE_INTERFACE} dataOut;
|
||||
dataOut.worldNormal = dataIn[vert].worldNormal; \
|
||||
dataOut.viewNormal = dataIn[vert].viewNormal;
|
||||
|
||||
#else /* GPU_VERTEX_SHADER || GPU_FRAGMENT_SHADER*/
|
||||
#else
|
||||
|
||||
IN_OUT ShaderStageInterface{SURFACE_INTERFACE};
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
|
||||
#pragma BLENDER_REQUIRE(volumetric_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(closure_lib.glsl)
|
||||
|
||||
/* Based on Frosbite Unified Volumetric.
|
||||
* https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
|
||||
|
@@ -24,9 +24,6 @@ uniform sampler2D noiseTex;
|
||||
#define dof_distance dofParams.y
|
||||
#define dof_invsensorsize dofParams.z
|
||||
|
||||
#define weighted_sum(a, b, c, d, e, e_sum) \
|
||||
((a)*e.x + (b)*e.y + (c)*e.z + (d)*e.w) / max(1e-6, e_sum);
|
||||
|
||||
/* divide by sensor size to get the normalized size */
|
||||
#define calculate_coc(zdepth) \
|
||||
(dof_aperturesize * (dof_distance / zdepth - 1.0) * dof_invsensorsize)
|
||||
@@ -89,8 +86,7 @@ void main()
|
||||
/* now write output to weighted buffers. */
|
||||
/* Take far plane pixels in priority. */
|
||||
vec4 w = any(notEqual(far_weights, vec4(0.0))) ? far_weights : near_weights;
|
||||
float tot_weight = dot(w, vec4(1.0));
|
||||
halfResColor = weighted_sum(color1, color2, color3, color4, w, tot_weight);
|
||||
halfResColor = weighted_sum(color1, color2, color3, color4, w);
|
||||
halfResColor = clamp(halfResColor, 0.0, 3.0);
|
||||
|
||||
normalizedCoc = encode_coc(coc_near, coc_far);
|
||||
@@ -138,8 +134,7 @@ void main()
|
||||
|
||||
/* now write output to weighted buffers. */
|
||||
vec4 w = any(notEqual(far_weights, vec4(0.0))) ? far_weights : near_weights;
|
||||
float tot_weight = dot(w, vec4(1.0));
|
||||
outColor = weighted_sum(color1, color2, color3, color4, w, tot_weight);
|
||||
outColor = weighted_sum(color1, color2, color3, color4, w);
|
||||
|
||||
outCocs = encode_coc(coc_near, coc_far);
|
||||
}
|
||||
|
@@ -9,6 +9,8 @@
|
||||
#define M_1_PI 0.318309886183790671538 /* 1/pi */
|
||||
#define M_1_2PI 0.159154943091895335768 /* 1/(2*pi) */
|
||||
#define M_1_PI2 0.101321183642337771443 /* 1/(pi^2) */
|
||||
#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */
|
||||
#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */
|
||||
#define FLT_MAX 3.402823e+38
|
||||
|
||||
vec3 mul(mat3 m, vec3 v)
|
||||
@@ -33,6 +35,13 @@ vec3 project_point(mat4 m, vec3 v)
|
||||
return tmp.xyz / tmp.w;
|
||||
}
|
||||
|
||||
mat2 rot2_from_angle(float a)
|
||||
{
|
||||
float c = cos(a);
|
||||
float s = sin(a);
|
||||
return mat2(c, -s, s, c);
|
||||
}
|
||||
|
||||
#define min3(a, b, c) min(a, min(b, c))
|
||||
#define min4(a, b, c, d) min(a, min3(b, c, d))
|
||||
#define min5(a, b, c, d, e) min(a, min4(b, c, d, e))
|
||||
@@ -73,8 +82,20 @@ 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; }
|
||||
float safe_rcp(float a) { return (a != 0.0) ? (1.0 / a) : 0.0; }
|
||||
vec2 safe_rcp(vec2 a) { return mix(vec2(0.0), (1.0 / a), notEqual(a, vec2(0.0))); }
|
||||
vec4 safe_rcp(vec4 a) { return mix(vec4(0.0), (1.0 / a), notEqual(a, vec4(0.0))); }
|
||||
|
||||
float sqr(float a) { return a * a; }
|
||||
vec2 sqr(vec2 a) { return a * a; }
|
||||
vec3 sqr(vec3 a) { return a * a; }
|
||||
vec4 sqr(vec4 a) { return a * a; }
|
||||
|
||||
float len_squared(vec3 a) { return dot(a, a); }
|
||||
float len_squared(vec2 a) { return dot(a, a); }
|
||||
|
||||
#define weighted_sum(val0, val1, val2, val3, weights) ((val0 * weights[0] + val1 * weights[1] + val2 * weights[2] + val3 * weights[3]) * safe_rcp(sum(weights)));
|
||||
#define weighted_sum_array(val, weights) ((val[0] * weights[0] + val[1] * weights[1] + val[2] * weights[2] + val[3] * weights[3]) * safe_rcp(sum(weights)));
|
||||
/* clang-format on */
|
||||
|
||||
#define saturate(a) clamp(a, 0.0, 1.0)
|
||||
@@ -91,20 +112,6 @@ float distance_squared(vec3 a, vec3 b)
|
||||
return dot(a, a);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
@@ -140,3 +147,14 @@ vec2 fast_acos(vec2 v)
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/*
|
||||
* For debugging purpose mainly.
|
||||
* From https://www.shadertoy.com/view/4dsSzr
|
||||
* By Morgan McGuire @morgan3d, http://graphicscodex.com
|
||||
* Reuse permitted under the BSD license.
|
||||
*/
|
||||
vec3 neon_gradient(float t)
|
||||
{
|
||||
return clamp(vec3(t * 1.3 + 0.1, sqr(abs(0.43 - t) * 1.7), (1.0 - t) * 1.7), 0.0, 1.0);
|
||||
}
|
||||
|
@@ -299,12 +299,29 @@ TEST_F(DrawTest, eevee_glsl_shaders_static)
|
||||
EXPECT_NE(EEVEE_shaders_bloom_upsample_get(true), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_bloom_resolve_get(false), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_bloom_resolve_get(true), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_downsample_get(false), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_downsample_get(true), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_scatter_get(false), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_scatter_get(true), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_resolve_get(false), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_resolve_get(true), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_bokeh_get(), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_setup_get(), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_flatten_tiles_get(), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_dilate_tiles_get(false), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_dilate_tiles_get(true), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_downsample_get(), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_reduce_get(true), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_reduce_get(false), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_gather_get(DOF_GATHER_FOREGROUND, false), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_gather_get(DOF_GATHER_FOREGROUND, true), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_gather_get(DOF_GATHER_BACKGROUND, false), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_gather_get(DOF_GATHER_BACKGROUND, true), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_gather_get(DOF_GATHER_HOLEFILL, false), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_gather_get(DOF_GATHER_HOLEFILL, true), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_filter_get(), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_scatter_get(false, false), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_scatter_get(false, true), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_scatter_get(true, false), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_scatter_get(true, true), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_resolve_get(false, true), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_resolve_get(false, false), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_resolve_get(true, true), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_depth_of_field_resolve_get(true, false), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_effect_downsample_sh_get(), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_effect_downsample_cube_sh_get(), nullptr);
|
||||
EXPECT_NE(EEVEE_shaders_effect_minz_downlevel_sh_get(), nullptr);
|
||||
|
@@ -755,10 +755,6 @@ 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 ")) {
|
||||
|
@@ -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, rand, bent_normal);
|
||||
result_ao = occlusion_compute(normalize(normal), viewPosition, 1.0, rand, bent_normal);
|
||||
result_color = result_ao * color;
|
||||
}
|
||||
#else
|
||||
|
@@ -1,27 +1,12 @@
|
||||
#ifndef VOLUMETRICS
|
||||
|
||||
CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_diffuse, Diffuse)
|
||||
|
||||
void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out Closure result)
|
||||
{
|
||||
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);
|
||||
|
||||
N = normalize(N);
|
||||
result = CLOSURE_DEFAULT;
|
||||
|
||||
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.0, result);
|
||||
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);
|
||||
}
|
||||
|
||||
#else
|
||||
/* Stub diffuse because it is not compatible with volumetrics. */
|
||||
# define node_bsdf_diffuse(a, b, c, d) (d = CLOSURE_DEFAULT)
|
||||
|
@@ -1,7 +1,4 @@
|
||||
#ifndef VOLUMETRICS
|
||||
|
||||
CLOSURE_EVAL_FUNCTION_DECLARE_3(node_eevee_specular, Diffuse, Glossy, Glossy)
|
||||
|
||||
void node_eevee_specular(vec4 diffuse,
|
||||
vec4 specular,
|
||||
float roughness,
|
||||
@@ -15,63 +12,34 @@ void node_eevee_specular(vec4 diffuse,
|
||||
float ssr_id,
|
||||
out Closure result)
|
||||
{
|
||||
CLOSURE_VARS_DECLARE_3(Diffuse, Glossy, Glossy);
|
||||
normal = normalize(normal);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
float alpha = 1.0 - transp;
|
||||
result = CLOSURE_DEFAULT;
|
||||
|
||||
{
|
||||
/* 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 = 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.ssr_data.rgb *= alpha;
|
||||
}
|
||||
result.transmittance = vec3(transp);
|
||||
|
||||
closure_load_ssr_data(ssr_spec * alpha, roughness, normal, viewCameraVec, int(ssr_id), result);
|
||||
}
|
||||
#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)
|
||||
|
@@ -1,7 +1,4 @@
|
||||
#ifndef VOLUMETRICS
|
||||
|
||||
CLOSURE_EVAL_FUNCTION_DECLARE_2(node_bsdf_glass, Glossy, Refraction)
|
||||
|
||||
void node_bsdf_glass(vec4 color,
|
||||
float roughness,
|
||||
float ior,
|
||||
@@ -10,38 +7,32 @@ void node_bsdf_glass(vec4 color,
|
||||
float ssr_id,
|
||||
out Closure result)
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
float fresnel = F_eta(in_Refraction_1.ior, dot(in_Glossy_0.N, cameraVec));
|
||||
|
||||
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 * fresnel;
|
||||
closure_load_ssr_data(
|
||||
out_Glossy_0.radiance, in_Glossy_0.roughness, in_Glossy_0.N, ssr_id, result);
|
||||
|
||||
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;
|
||||
ssr_spec * color.rgb * fresnel, roughness, N, viewCameraVec, int(ssr_id), result);
|
||||
}
|
||||
|
||||
#else
|
||||
/* Stub glass because it is not compatible with volumetrics. */
|
||||
# define node_bsdf_glass(a, b, c, d, e, f, result) (result = CLOSURE_DEFAULT)
|
||||
|
@@ -1,32 +1,23 @@
|
||||
#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)
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
result = CLOSURE_DEFAULT;
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
#else
|
||||
/* Stub glossy because it is not compatible with volumetrics. */
|
||||
# define node_bsdf_glossy(a, b, c, d, e, result) (result = CLOSURE_DEFAULT)
|
||||
|
@@ -2,18 +2,39 @@
|
||||
vec3 tint_from_color(vec3 color)
|
||||
{
|
||||
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 */
|
||||
return (lum > 0) ? color / lum : vec3(1.0); /* normalize lum. to isolate hue+sat */
|
||||
}
|
||||
|
||||
float principled_sheen(float NV)
|
||||
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 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. */
|
||||
float sheen = f * f * f * 0.077 + f * 0.01 + 0.00026;
|
||||
return sheen;
|
||||
}
|
||||
out_sheen = f * f * f * 0.077 + f * 0.01 + 0.00026;
|
||||
|
||||
CLOSURE_EVAL_FUNCTION_DECLARE_4(node_bsdf_principled, Diffuse, Glossy, Glossy, Refraction)
|
||||
sheen_color = sheen * mix(vec3(1.0), basecol_tint, sheen_tint);
|
||||
}
|
||||
|
||||
void node_bsdf_principled(vec4 base_color,
|
||||
float subsurface,
|
||||
@@ -38,163 +59,434 @@ void node_bsdf_principled(vec4 base_color,
|
||||
vec3 N,
|
||||
vec3 CN,
|
||||
vec3 T,
|
||||
const float do_diffuse,
|
||||
const float do_clearcoat,
|
||||
const float do_refraction,
|
||||
const float do_multiscatter,
|
||||
vec3 I,
|
||||
float use_multiscatter,
|
||||
float ssr_id,
|
||||
float sss_id,
|
||||
vec3 sss_scale,
|
||||
out Closure result)
|
||||
{
|
||||
/* Match cycles. */
|
||||
N = normalize(N);
|
||||
ior = max(ior, 1e-5);
|
||||
metallic = saturate(metallic);
|
||||
transmission = saturate(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 m_transmission = 1.0 - transmission;
|
||||
|
||||
CLOSURE_VARS_DECLARE_4(Diffuse, Glossy, Glossy, Refraction);
|
||||
float dielectric = 1.0 - metallic;
|
||||
transmission *= dielectric;
|
||||
sheen *= dielectric;
|
||||
subsurface_color *= dielectric;
|
||||
|
||||
in_Diffuse_0.N = N; /* Normalized during eval. */
|
||||
in_Diffuse_0.albedo = mix(base_color.rgb, subsurface_color.rgb, subsurface);
|
||||
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_Glossy_1.N = N; /* Normalized during eval. */
|
||||
in_Glossy_1.roughness = roughness;
|
||||
float NV = dot(N, cameraVec);
|
||||
principled_sheen(NV, ctint, sheen, sheen_tint, out_sheen, sheen_color);
|
||||
|
||||
in_Glossy_2.N = CN; /* Normalized during eval. */
|
||||
in_Glossy_2.roughness = clearcoat_roughness;
|
||||
vec3 f90 = mix(vec3(1.0), f0, (1.0 - specular) * metallic);
|
||||
|
||||
in_Refraction_3.N = N; /* Normalized during eval. */
|
||||
in_Refraction_3.roughness = do_multiscatter != 0.0 ? roughness : transmission_roughness;
|
||||
in_Refraction_3.ior = ior;
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
CLOSURE_EVAL_FUNCTION_4(node_bsdf_principled, Diffuse, Glossy, Glossy, Refraction);
|
||||
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;
|
||||
|
||||
result = CLOSURE_DEFAULT;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* Glossy_1 will always be evaluated. */
|
||||
float NV = dot(in_Glossy_1.N, cameraVec);
|
||||
|
||||
vec3 base_color_tint = tint_from_color(base_color.rgb);
|
||||
|
||||
/* TODO(fclem) This isn't good for rough glass using multiscatter (since the fresnel is applied
|
||||
* on each microfacet in cycles). */
|
||||
float fresnel = F_eta(in_Refraction_3.ior, NV);
|
||||
|
||||
{
|
||||
/* 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);
|
||||
|
||||
vec2 split_sum = brdf_lut(NV, roughness);
|
||||
|
||||
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) {
|
||||
/* TODO(fclem) This could be going to a transmission render pass instead. */
|
||||
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);
|
||||
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;
|
||||
result.ssr_data.rgb *= alpha;
|
||||
# ifdef USE_SSS
|
||||
result.sss_irradiance *= alpha;
|
||||
# endif
|
||||
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_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;
|
||||
|
||||
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 f90 = mix(vec3(1.0), f0, (1.0 - specular) * metallic);
|
||||
|
||||
float NV = dot(N, cameraVec);
|
||||
principled_sheen(NV, ctint, sheen, sheen_tint, out_sheen, sheen_color);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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, cc, dd, 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, 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)
|
||||
/* clang-format on */
|
||||
#endif
|
||||
|
@@ -1,30 +1,15 @@
|
||||
#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)
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
result = CLOSURE_DEFAULT;
|
||||
|
||||
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);
|
||||
result.ssr_normal = normal_encode(vN, viewCameraVec);
|
||||
result.radiance = render_pass_glossy_mask(color.rgb, out_refr * color.rgb);
|
||||
}
|
||||
|
||||
#else
|
||||
/* Stub refraction because it is not compatible with volumetrics. */
|
||||
# define node_bsdf_refraction(a, b, c, d, e) (e = CLOSURE_DEFAULT)
|
||||
|
@@ -1,7 +1,4 @@
|
||||
#ifndef VOLUMETRICS
|
||||
|
||||
CLOSURE_EVAL_FUNCTION_DECLARE_1(node_subsurface_scattering, Diffuse)
|
||||
|
||||
void node_subsurface_scattering(vec4 color,
|
||||
float scale,
|
||||
vec3 radius,
|
||||
@@ -11,29 +8,20 @@ void node_subsurface_scattering(vec4 color,
|
||||
float sss_id,
|
||||
out Closure result)
|
||||
{
|
||||
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);
|
||||
|
||||
N = normalize(N);
|
||||
vec3 out_diff;
|
||||
vec3 vN = mat3(ViewMatrix) * N;
|
||||
result = CLOSURE_DEFAULT;
|
||||
closure_load_ssr_data(vec3(0.0), 0.0, N, viewCameraVec, -1, result);
|
||||
|
||||
/* 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;
|
||||
eevee_closure_subsurface(N, color.rgb, 1.0, scale, true, out_diff);
|
||||
|
||||
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.0, result);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
#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)
|
||||
|
@@ -1,20 +1,12 @@
|
||||
#ifndef VOLUMETRICS
|
||||
|
||||
CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_translucent, Translucent)
|
||||
|
||||
void node_bsdf_translucent(vec4 color, vec3 N, out Closure result)
|
||||
{
|
||||
CLOSURE_VARS_DECLARE_1(Translucent);
|
||||
|
||||
in_Translucent_0.N = -N; /* Normalized during eval. */
|
||||
|
||||
CLOSURE_EVAL_FUNCTION_1(node_bsdf_translucent, Translucent);
|
||||
|
||||
N = normalize(N);
|
||||
result = CLOSURE_DEFAULT;
|
||||
closure_load_ssr_data(vec3(0.0), 0.0, -in_Translucent_0.N, -1.0, result);
|
||||
result.radiance = render_pass_diffuse_mask(color.rgb, out_Translucent_0.radiance * color.rgb);
|
||||
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);
|
||||
}
|
||||
|
||||
#else
|
||||
/* Stub translucent because it is not compatible with volumetrics. */
|
||||
# define node_bsdf_translucent(a, b, c) (c = CLOSURE_DEFAULT)
|
||||
|
@@ -208,8 +208,11 @@
|
||||
.gtao_factor = 1.0f, \
|
||||
.gtao_quality = 0.25f, \
|
||||
\
|
||||
.bokeh_overblur = 5.0f, \
|
||||
.bokeh_max_size = 100.0f, \
|
||||
.bokeh_threshold = 1.0f, \
|
||||
.bokeh_neighbor_max = 10.0f, \
|
||||
.bokeh_denoise_fac = 0.75f, \
|
||||
\
|
||||
.bloom_color = {1.0f, 1.0f, 1.0f}, \
|
||||
.bloom_threshold = 0.8f, \
|
||||
|
@@ -1642,8 +1642,11 @@ typedef struct SceneEEVEE {
|
||||
float gtao_factor;
|
||||
float gtao_quality;
|
||||
|
||||
float bokeh_overblur;
|
||||
float bokeh_max_size;
|
||||
float bokeh_threshold;
|
||||
float bokeh_neighbor_max;
|
||||
float bokeh_denoise_fac;
|
||||
|
||||
float bloom_color[3];
|
||||
float bloom_threshold;
|
||||
@@ -1658,7 +1661,6 @@ typedef struct SceneEEVEE {
|
||||
int motion_blur_position;
|
||||
float motion_blur_shutter;
|
||||
float motion_blur_depth_scale;
|
||||
char _pad0[4];
|
||||
|
||||
int shadow_method DNA_DEPRECATED;
|
||||
int shadow_cube_size;
|
||||
@@ -2420,6 +2422,8 @@ enum {
|
||||
SCE_EEVEE_GI_AUTOBAKE = (1 << 19),
|
||||
SCE_EEVEE_SHADOW_SOFT = (1 << 20),
|
||||
SCE_EEVEE_OVERSCAN = (1 << 21),
|
||||
SCE_EEVEE_DOF_HQ_SLIGHT_FOCUS = (1 << 22),
|
||||
SCE_EEVEE_DOF_JITTER = (1 << 23),
|
||||
};
|
||||
|
||||
/* SceneEEVEE->shadow_method */
|
||||
|
@@ -7349,21 +7349,66 @@ static void rna_def_scene_eevee(BlenderRNA *brna)
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
|
||||
|
||||
/* Depth of Field */
|
||||
|
||||
prop = RNA_def_property(srna, "bokeh_max_size", PROP_FLOAT, PROP_PIXEL);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Max Size", "Max size of the bokeh shape for the depth of field (lower is faster)");
|
||||
RNA_def_property_range(prop, 0.0f, 2000.0f);
|
||||
RNA_def_property_ui_range(prop, 2.0f, 200.0f, 1, 3);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 200.0f, 100.0f, 1);
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
|
||||
prop = RNA_def_property(srna, "bokeh_threshold", PROP_FLOAT, PROP_FACTOR);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Sprite Threshold", "Brightness threshold for using sprite base depth of field");
|
||||
RNA_def_property_range(prop, 0.0f, 100000.0f);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 10.0f, 1, 3);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 10.0f, 10, 2);
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "bokeh_neighbor_max", PROP_FLOAT, PROP_FACTOR);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Neighbor Rejection",
|
||||
"Maximum brightness to consider when rejecting bokeh sprites "
|
||||
"based on neighboorhod (lower is faster)");
|
||||
RNA_def_property_range(prop, 0.0f, 100000.0f);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 40.0f, 10, 2);
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "bokeh_denoise_fac", PROP_FLOAT, PROP_FACTOR);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Denoise Amount", "Amount of flicker removal applied to bokeh highlights");
|
||||
RNA_def_property_range(prop, 0.0f, 1.0f);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 10, 2);
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "use_bokeh_high_quality_slight_defocus", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_DOF_HQ_SLIGHT_FOCUS);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"High Quality Slight Defocus",
|
||||
"Sample all pixels in almost in-focus regions to eliminate noise");
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "use_bokeh_jittered", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_DOF_JITTER);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Jitter Camera",
|
||||
"Jitter camera position to create accurate bluring "
|
||||
"using render samples");
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "bokeh_overblur", PROP_FLOAT, PROP_PERCENTAGE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Overblur",
|
||||
"Apply blur to each jittered sample to reduce "
|
||||
"undersampling artifacts");
|
||||
RNA_def_property_range(prop, 0, 100);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 20.0f, 1, 1);
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
|
||||
/* Bloom */
|
||||
prop = RNA_def_property(srna, "use_bloom", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_BLOOM_ENABLED);
|
||||
|
@@ -134,32 +134,54 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat,
|
||||
GPU_link(mat, "set_rgb_one", &sss_scale);
|
||||
}
|
||||
|
||||
uint flag = GPU_MATFLAG_GLOSSY;
|
||||
if (use_diffuse) {
|
||||
flag |= GPU_MATFLAG_DIFFUSE;
|
||||
/* 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;
|
||||
}
|
||||
if (use_refract) {
|
||||
flag |= GPU_MATFLAG_REFRACT;
|
||||
else if (!use_subsurf && !use_diffuse && !use_refract && !use_clear) {
|
||||
static char name[] = "node_bsdf_principled_metallic";
|
||||
node_name = name;
|
||||
flag = GPU_MATFLAG_GLOSSY;
|
||||
}
|
||||
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_bsdf_principled",
|
||||
node_name,
|
||||
in,
|
||||
out,
|
||||
GPU_constant(&f_use_diffuse),
|
||||
GPU_constant(&f_use_clearcoat),
|
||||
GPU_constant(&f_use_refraction),
|
||||
GPU_builtin(GPU_VIEW_POSITION),
|
||||
GPU_constant(&use_multi_scatter),
|
||||
GPU_constant(&node->ssr_id),
|
||||
GPU_constant(&node->sss_id),
|
||||
|
Reference in New Issue
Block a user