1
1

Compare commits

..

98 Commits

Author SHA1 Message Date
7769b89bab EEVEE: Depth of Field: Improves jittered camera sampling pattern
Jittered camera now uses an hexaweb sampling pattern that covers the
bokeh area better and faster than previous pseudo random solution.

The downside is that we need to change the sample count based on the
number of rings to fill. We increase the number of samples just to fill
the largest ring.
2021-02-11 04:13:25 +01:00
cccf7387a8 EEVEE: Depth of Field: Improves denoise filter
This improve spatial stability by only clamping down pixels that
are too bright (fixing the issue of bright pixels inflating).

Also use the full 3x3 region to avoid cross shape patterns.
2021-02-10 21:53:22 +01:00
c5873c7828 Cleanup: Fix formating 2021-02-03 17:55:40 +01:00
d02e644060 Merge branch 'master' into eevee-dof-refactor 2021-02-03 17:14:04 +01:00
54d6e071ab EEVEE: Depth of field: Cleanup eevee_shaders interface 2021-02-03 17:08:00 +01:00
7bc49f68a9 EEVEE: Depth of field: Remove studiolight related changes 2021-02-03 17:02:43 +01:00
8a6e07e42c EEVEE: Depth of field: Fix typo and wrong ownership 2021-02-03 16:56:41 +01:00
aeaa549948 EEVEE: Depth of field: Move versioning code to versioning_290.c 2021-02-03 16:56:09 +01:00
c4c1b2addc EEVEE: Depth of field: Add slight defocus bokeh shape foreground flip 2021-02-03 16:49:03 +01:00
d447a5eab3 EEVEE: Depth of field: Fix bokeh shape rotation inverted
This was comming from that the rotation was based on foreground
shape which is now the one we invert.
2021-02-03 16:47:06 +01:00
376bffae03 EEVEE: Depth of field: Fix bokeh shape not inverted for scatter
The wrong vector was negated.
2021-02-03 16:46:11 +01:00
c96b1e4cda EEVEE: Depth of field: Fix incorrect prediction for holefill tiles
Holefill is only needed if having foreground elements in the tile.
2021-01-30 00:14:45 +01:00
f12338f3ce EEVEE: Depth of field: Improve slight defocus opacity estimation
This correct the opacity estimate (for some reason the number of samples
returned by dof_gather_total_sample_count is wrong and ring_count needs
to be incremented).

Also adds a bias towards the focused area to improve the transition to
fully opaque.
2021-01-29 23:46:05 +01:00
e6d0a5be0e EEVEE: Depth of field: Bleed more background over in-focus area
Improves water-tightness.
2021-01-29 23:34:05 +01:00
7d6f58271b EEVEE: Depth of field: Fix tile prediction and fast gather threshold
Tile prediction wasn't using the correct threshold and fast gathering
wasn't taking layer weight into consideration.

Both were making tilling artifacts.
2021-01-29 15:29:54 +01:00
5db9a008b9 EEVEE: Depth Of Field: Fix jittered dof breaking screenspace effects
This was the case in perspective view. Seems like changing the projmat
origin was the issue. Instead we rely on window_translate_m4 to skew
the matrix and jitter the viewmat position to offset the origin.
2021-01-28 19:32:03 +01:00
099788c3e8 EEVEE: Depth Of Field: Flip bokeh shape on foreground.
This is to mimic optical phenomenon and also match jittered DOF.
2021-01-28 19:29:12 +01:00
e58ef7db5b Merge branch 'master' into eevee-dof-refactor 2021-01-28 15:16:15 +01:00
9c4dbb592b EEVEE: Depth Of Field: UI: Change Jitter option name & tooltip 2021-01-28 15:14:55 +01:00
57fc9f6082 EEVEE: Depth Of Field: Add minimal overblur radius
This makes jittered Dof much easier to use as it scales itself
with the number of samples.
2021-01-28 15:06:35 +01:00
fc6b04d4e3 EEVEE: Depth Of Field: Add new jittered Dof
This is a new method to increase quality of the Depth of field.

This works by jittering the camera for every sample. This needs a lot of
samples to converge to something smooth. The new overblur parameter is
introduced to blur each sample to make it noisefree faster at the cost
of accuracy.
2021-01-28 14:32:02 +01:00
4b04818d79 EEVEE: Depth Of Field: Reintroduce the extra offset for convolution
This is really needed for watertighteness and avoid background leaking.
2021-01-28 14:26:40 +01:00
c8b324fe90 EEVEE: Depth Of Field: Fix orthographic view Circle of confusion
Orthographic view depth of field was broken since day one it seems.
2021-01-28 14:22:58 +01:00
3c84851142 EEVEE: Depth Of Field: Gather: Fix tiling artifact on foreground
Was caused by wrong tile prediction.
2021-01-27 19:28:04 +01:00
1f14c0d20c EEVEE: Depth Of Field: Gather: Fix polygonal dof radius being too big
The polygonal bokeh was outputing normalized UV but not the normal one.
2021-01-27 19:05:33 +01:00
24f09b8f55 EEVEE: Depth Of Field: Lower minimal CoC radius for scattering
Makes possible to have sharper bright spots.
2021-01-27 18:56:04 +01:00
e36f3da31c EEVEE: Depth Of Field: Cleanup: Add resolve pass description 2021-01-27 18:49:13 +01:00
3bb970f75a EEVEE: Depth Of Field: Cleanup: Share more define 2021-01-27 18:49:13 +01:00
ff74136da9 EEVEE: Depth Of Field: Tests: update shader tests 2021-01-27 18:31:11 +01:00
81235000d4 EEVEE: Depth Of Field: Cleanup: Share defines between C and GLSL 2021-01-27 18:30:51 +01:00
61e5c665b7 EEVEE: Depth Of Field: Fix fast gather tiles bleeding color
Seems like the dilation is too small for fast gathering to not bleed.
Introduce an arbitrary factor to fix this issue.
2021-01-27 17:11:02 +01:00
e1f2db669c EEVEE: Depth Of Field: Add high quality slight defocus option
This option is needed to avoid bumping the scene samples really high to
mitigate the noise and flickering introduced by slight defocus areas.
2021-01-27 17:09:35 +01:00
a6ca6f5af7 EEVEE: Depth Of Field: Cleanup: Change precision of dof parameter 2021-01-27 16:32:40 +01:00
a8e3293b81 EEVEE: Depth Of Field: Fix jump in appearance when using bokeh texture
This was caused by a missing factor of 2.
Also fix issues with scatter bokeh size.
Remove fast_sqrt because it does not give the correct bokeh size and
cause noticeable jump between ratio = 1 and ratio > 1.
2021-01-27 16:23:04 +01:00
cfa97fd7f7 EEVEE: Depth Of Field: Fix apparent tiles on foreground limits 2021-01-27 16:05:56 +01:00
f48adcca64 EEVEE: Depth Of Field: Add denoising pass to stabilize bokeh highlights
Alike in the reference implementation, we stabilize the convolution
input. However, instead of a fullblown TAA pass, we just do neighborhood
clamping to avoid very bright pixels. We filter only spatially and not
temporaly.
2021-01-27 15:46:33 +01:00
9457a0faf5 EEVEE: Depth Of Field: Resolve: Fix slight focus gather radius too big
This improves performance a bit.
2021-01-27 14:27:05 +01:00
eedf6c6295 EEVEE: Depth Of Field: Scatter Add UI options
The neighbor max tooltip could be less vague.
2021-01-27 14:17:59 +01:00
6e32018499 EEVEE: Depth Of Field: Revert fix scatter power difference
Scatter only seems to have an intensity difference because gather is
only underestimating the highlights. But the scatter bokeh are correct.
2021-01-27 14:15:22 +01:00
89d9f5983c EEVEE: Depth Of Field: Fix neighborhood rejection
The samples were offseted and the difference wasn't absolute.
2021-01-27 14:15:08 +01:00
06e080c712 EEVEE: Depth Of Field: Always use RGBA16F format
Lower precision format have issues when there is too much scattered
bokeh.
2021-01-27 14:04:56 +01:00
b036baa60a EEVEE: Depth Of Field: Improve bokeh shape support
- Separate the LUT texture for faster access.
- Add dedicated shader for resolve with bokeh LUT.
- Add per-pixel distance to shape for slight focus gather.
2021-01-27 02:36:39 +01:00
2c948ea405 EEVEE: Depth Of Field: Improve anamorphic bokeh support for gather
Directly morph the sampling pattern instead of using the LUT for that.
This avoids the weird energy spike problem.

Also now supports ratio less than 1.0.
2021-01-27 00:55:59 +01:00
874665d5e6 EEVEE: Depth Of Field: Support anamorphic bokeh on slight focus gather
Support is kindof weak though as it makes discontinuities appear at
layers transitions.
2021-01-26 19:15:25 +01:00
556381c84a EEVEE: Depth Of Field: Convolution output premultiplied color
Use premultiplied color so we can easily bilinearly sample the
result in resolve pass. This fixes issue with the custom resolve
sampling that was dilating / aliasing the foreground.
2021-01-26 19:10:46 +01:00
64612d57e2 EEVEE: Depth Of Field: Scatter: Speedup: Reduce pixel shader complexity 2021-01-26 18:28:56 +01:00
18ea15cf2d EEVEE: Depth Of Field: Scatter: Speedup: Reduce pixel shader complexity 2021-01-26 18:26:28 +01:00
9cd4618390 EEVEE: Depth Of Field: Cleanup: Use same tile prediction for gather
This use the same code to early out of gathering for a tile. This
make sure we don't do any expensive work for nothing.
2021-01-26 18:18:08 +01:00
c9c79aad9e EEVEE: Depth Of Field: Resolve: Do not load layers that will not be used
Based on coc tiles we can know in advance which layers to load.
This gives a 10% boost compared to loading every layers.
2021-01-26 17:49:30 +01:00
6fff85b2e4 EEVEE: Depth Of Field: Resolve: Fix some issues
- Half res buffers were offseted by half a pixel.
- fullres slight focus gather was giving black borders.
- Fix foreground pass having full weight when CoC is almost equal to
  layer_threshold.
2021-01-26 17:13:24 +01:00
fea585ded4 EEVEE: Depth Of Field: Scatter: Improve neighborhood rejection
This is now based on a user parameter.
2021-01-26 16:27:23 +01:00
e7e4b3c6af EEVEE: Depth Of Field: Fix issue with texture reuse
Also reuse the scatter occlusion texture from foreground to
background.
2021-01-26 15:20:58 +01:00
5841c1d819 EEVEE: Depth Of Field: Use bilinear filtering during resolve
Instead of using stochastic resampling, use bilinear filtering to reduce
noise amount and not waste the spatial coherency we have from the
convolution passes.
2021-01-26 02:14:25 +01:00
f91aeaedb1 EEVEE: Depth Of Field: Filter weight buffer
This allows us to filter foreground buffer and have better layer masks.

This doubles the filter time but the pass is already quite inexpensive.
Also we could add an early out condition using the tile buffers.
2021-01-26 01:23:03 +01:00
d7365bfdd5 EEVEE: Depth Of Field: Tweak: Reduce fast gather random radius a bit
Seems to fit better the normal gather radius.
2021-01-26 01:07:09 +01:00
9135370633 EEVEE: Depth Of Field: Use bilinear filtering for fast gather
This reduces the noise amount a bit.
2021-01-26 01:05:49 +01:00
53ab14ffed EEVEE: Depth Of Field: Use RGB only format when alpha isn't required
This reduces the amount of VRAM needed for the effect.
2021-01-26 00:23:00 +01:00
0b55a9f886 EEVEE: Depth Of Field: Improve foreground gather pass performance
By tracking the max (or min absolute) intersectable Circle of confusion
radius instead of the simply dilated maximum, we can reduce the overhead
of density changing gathering.
2021-01-26 00:00:33 +01:00
55936c060b EEVEE: Depth Of Field: Fix NaNs in going through resolve pass 2021-01-25 20:02:42 +01:00
6c6431809d EEVEE: Depth Of Field: Improve scatter rejection masks
This update the rejection with smooth tests which fades the
pixels between scattering and gathering convolutions.
2021-01-25 19:41:45 +01:00
3f0213d929 EEVEE: Depth Of Field: Fix compositing issues 2021-01-25 19:00:12 +01:00
bf3875d6ac EEVEE: Depth Of Field: Cleanup: Remove scatter threshold uniform...
... from scatter pass and bump minimum scatter radius.
2021-01-25 18:42:48 +01:00
e137695436 EEVEE: Depth Of Field: Make max mipmap based on maximum CoC
This make sure all levels that are going to be sampled are
filled with data. Leading to more performance.
2021-01-25 18:41:01 +01:00
8d78831fbd EEVEE: Depth Of Field: Improve slight focus gather patern
This double the sample count and change the randomization pattern.
2021-01-25 17:00:23 +01:00
3203340168 EEVEE: Depth Of Field: Fix scatter anamorphic bokeh energy conservation
Everything is in the title.
2021-01-25 15:40:50 +01:00
a9e12e50ae EEVEE: Depth Of Field: Improve Max size slider UI feel
The prop is in pixels. No need for this much accuracy.
2021-01-25 14:30:38 +01:00
e623e62d3e EEVEE: Depth Of Field: Separate scatter buffer
This adds a new texture buffer to store the scatter color.
This improve code quality and in the future will allow smoother
transition between scatter and gather.
2021-01-25 14:28:59 +01:00
928aee934f EEVEE: Depth Of Field: Resolve pass: Fix some bugs
- Fix missing tiles when max size is under layer threshold.
- Fix infocus sampling being jittered
- Reduce intersection multiplier for smoother slight focus transition.
2021-01-25 13:32:15 +01:00
f38479f9b4 EEVEE: Depth Of Field: Add performance debugging defines 2021-01-25 13:00:26 +01:00
440c348323 EEVEE: Depth Of Field: Tweak layer threshold for more watertighteness 2021-01-25 12:31:16 +01:00
6650310844 EEVEE: Depth Of Field: Add back the resolve sample jitter
This time using a tweaked radius and a noise rotation (offset).
2021-01-25 12:28:41 +01:00
6a655eb871 EEVEE: Depth Of Field: Fix output weight of the gather passes 2021-01-25 12:28:02 +01:00
c8067f798f EEVEE: Depth Of Field: Change foreground occlusion threshold
This makes it a bit more relaxed to avoid too harsh transitions.

Also disable occlusion in resolve pass.
2021-01-25 12:26:34 +01:00
371312cf2c EEVEE: Depth Of Field: Correct foreground occlusion weighting
This is not perfect but at least it has less noticeable transition
issues.
2021-01-25 12:07:20 +01:00
b37fac5ff4 EEVEE: Depth Of Field: Cleanup remove transparency_weight 2021-01-24 19:19:01 +01:00
ec909e5d0f EEVEE: Depth Of Field: Fix sample weighting for foreground 2021-01-24 19:12:34 +01:00
fee0c90127 EEVEE: Depth Of Field: Various improvement
- Add compile time options for debugging.
- Reduce number of samples for slight focus gather.
- Increase/fix quality of background and foreground density change.
2021-01-24 14:20:55 +01:00
42732568d5 EEVEE: Depth Of Field: Fix slight out of focus tile dilation
It wasn't working as expected.
2021-01-24 14:19:19 +01:00
b94f1a63f1 EEVEE: Depth Of Field: Speedup: Use linearstep in scatter pass
This reduce the perf bottleneck of the pixel shader.
2021-01-24 14:15:38 +01:00
8f112af609 EEVEE: Depth Of Field: Fix issue with slight focus filtering perf.
The issue was caused by wrong tile values.
2021-01-22 15:33:44 +01:00
0c732382ae EEVEE: Depth Of Field: Fix issue with neighborhood comparison
The filter was not using the correct UVs.
2021-01-22 15:32:31 +01:00
643420186c EEVEE: Depth Of Field: Add gather density change
To fix closer background undersampling, we change gathering ring
density in the middle of the convolution. We change the weighting
of the already accumulated samples accordingly.
2021-01-21 14:53:14 +01:00
4f3fb67c43 EEVEE: Depth Of Field: Fix missing slight focus convolution
This was due to recent change in the accumulate_sample_pair
function.
2021-01-20 18:24:56 +01:00
e267afebf4 EEVEE: Depth Of Field: Fix early out on half res CoC
`layer_threshold` is based on fullres CoC radius. Better compare
apple to apple.
2021-01-20 18:04:27 +01:00
e69f8483ed EEVEE: Depth Of Field: Fix slight out of focus missing tile
Slight out of focus tiles needs to be expanded too at least once.
2021-01-20 17:49:38 +01:00
9f658822f9 EEVEE: Depth Of Field: Fix ring opacity issues
Accumulate transparency instead of occlusion to avoid precision
error an make closer objects more water tight.

Also fixed reversed occlusion ring accumulation.
2021-01-20 14:50:09 +01:00
4f6ce899d4 EEVEE: Depth Of Field: Disable filtering for foreground
This is to avoid darkening of the edges. This might be fixed if
we also filter the weight buffer (TODO).
2021-01-20 14:32:32 +01:00
63e898a0ff EEVEE: Depth Of Field: Add Bokeh shape texture precomputation
To support geometric bokeh shapes, we now use a texture to guide
the gathered samples and modify the scattered pixels shapes.

This is done by a very fast precomputation pass that outputs the
bokeh shape to a texture that is then sampled by the convolution
pass.
2021-01-20 01:43:42 +01:00
239229c390 EEVEE: Depth Of Field: Dilate CoC Tiles based on max CoC radius
This means we now supports arbitrary large CoC.
This makes sure to always covers the maximum tile radius.
2021-01-18 18:36:03 +01:00
14e6f2f4db EEVEE: Depth Of Field: Gather median filter
Filter the noisy output of the gather pass. This improves temporal
stability.
2021-01-18 12:32:21 +01:00
34de342257 EEVEE: Depth Of Field: Scatter optimization
Use neighborhood comparison to reject pixels from scattering pass if
they are not different enough from surrounding pixels.

Logic is here, but this needs refinement. This is mainly to
reject lots of scattering sprites on large out of focus regions
that are really bright.
2021-01-17 18:57:07 +01:00
f071e41d63 EEVEE: Depth Of Field: Fix alpha resolve 2021-01-17 17:38:56 +01:00
dacce1d07f EEVEE: Depth Of Field: Scatter Occlusion
Use VSM like occlusion texture to avoid light leaking through closer
background and foreground.
2021-01-17 17:36:35 +01:00
2b2e65855a EEVEE: Depth Of Field: Fix Weighting
Fix accumulation of center samples and the total sample count function.
2021-01-16 19:45:14 +01:00
ef2ed3d737 EEVEE: Depth Of Field: Improvement part 3
Add Hole Fill pass to fill missing background.
2021-01-16 19:43:34 +01:00
3a62ab73a3 EEVEE: Depth Of Field: Improvement part 2
Add In Focus max CoC tiles to speedup the resolve pass
2021-01-15 19:45:13 +01:00
69b667d14e EEVEE: Depth Of Field: Improvement part 1
Add new gather algorithm.

This involve multiple passes which are well described in each shader.

Shaders have been split into multiple file for clarity sake.
2021-01-15 17:46:00 +01:00
144d9b901e EEVEE: Depth of field: Improve scatter pass
This makes each sprite scatter 4 pixels instead of one.

This roughly divides the render time of the effect by two and
improve the color precision.

The single pass method was creating issues on near/far CoC
discontinuities, so we reverted to a 2 pass method even if a bit
more costly (but still is 2x faster).
2021-01-06 14:12:38 +01:00
60 changed files with 4748 additions and 1892 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,
NULL,
datatoc_effect_dof_frag_glsl,
e_data.lib,
use_alpha ?
"#define USE_ALPHA_DOF\n"
"#define STEP_SCATTER\n" :
"#define STEP_SCATTER\n");
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_scatter_sh[index];
return e_data.dof_setup_sh;
}
GPUShader *EEVEE_shaders_depth_of_field_resolve_get(bool use_alpha)
GPUShader *EEVEE_shaders_depth_of_field_flatten_tiles_get(void)
{
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");
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_resolve_sh[index];
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_scatter_frag_glsl,
e_data.lib,
define);
MEM_freeN(define);
}
return e_data.dof_scatter_sh[is_foreground][use_bokeh_tx];
}
GPUShader *EEVEE_shaders_depth_of_field_resolve_get(bool b_use_bokeh_tx, bool b_use_hq_gather)
{
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");
}
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]);

View File

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

View File

@@ -191,15 +191,15 @@ void gtao_deferred(
dirs.xy = get_ao_dir(noise.x * 0.5);
dirs.zw = get_ao_dir(noise.x * 0.5 + 0.5);
bent_normal = 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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
/* fade between 0.5 and 1.0 roughness */
ssr_accum.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, roughness);
cl_eval.raytrace_radiance = ssr_accum.rgb * ssr_accum.a;
cl_common.specular_accum -= ssr_accum.a;
}
CLOSURE_EVAL_FUNCTION_DECLARE_1(ssr_resolve, Glossy)
void main()
{
ivec2 texel = ivec2(gl_FragCoord.xy);
float depth = texelFetch(depthBuffer, texel, 0).r;
if (depth == 1.0) {
discard;
if (weight_acc > 0.0) {
ssr_accum /= weight_acc;
/* fade between 0.5 and 1.0 roughness */
ssr_accum.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, roughness);
accumulate_light(ssr_accum.rgb, ssr_accum.a, spec_accum);
}
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

View File

@@ -137,9 +137,9 @@ float probe_attenuation_planar(PlanarData pd, vec3 W, vec3 N, float roughness)
return fac;
}
float probe_attenuation_grid(GridData gd, 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;

View File

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

View File

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

View File

@@ -39,7 +39,7 @@ void main()
vec3 viewPosition = get_view_space_from_depth(uvs, depth);
vec3 worldPosition = transform_point(ViewMatrixInverse, viewPosition);
vec3 true_normal = 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;
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,12 +3,14 @@
/** \name Common Math Utilities
* \{ */
#define M_PI 3.14159265358979323846 /* pi */
#define M_2PI 6.28318530717958647692 /* 2*pi */
#define M_PI_2 1.57079632679489661923 /* pi/2 */
#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_PI 3.14159265358979323846 /* pi */
#define M_2PI 6.28318530717958647692 /* 2*pi */
#define M_PI_2 1.57079632679489661923 /* pi/2 */
#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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,19 +1,40 @@
#ifndef VOLUMETRICS
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 */
float lum = dot(color, vec3(0.3, 0.6, 0.1)); /* luminance approx. */
return (lum > 0) ? color / lum : vec3(1.0); /* normalize lum. to isolate hue+sat */
}
float 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);
CLOSURE_EVAL_FUNCTION_4(node_bsdf_principled, Diffuse, Glossy, Glossy, Refraction);
/* Really poor approximation but needed to workaround issues with renderpasses. */
spec_col = mix(vec3(1.0), spec_col, transmission);
/* Match cycles. */
spec_col += float(clearcoat > 1e-5);
vec3 mixed_ss_base_color = mix(diffuse, subsurface_color.rgb, subsurface);
float sss_scalef = avg(sss_scale) * subsurface;
eevee_closure_principled(N,
mixed_ss_base_color,
f0,
/* HACK: Pass the multiscatter flag as the sign to not add closure
* variations or increase register usage. */
(use_multiscatter != 0.0) ? f90 : -f90,
int(ssr_id),
roughness,
CN,
clearcoat * 0.25,
clearcoat_roughness,
1.0,
sss_scalef,
ior,
true,
out_diff,
out_spec,
out_refr,
ssr_spec);
vec3 refr_color = base_color.rgb;
refr_color *= (refractionDepth > 0.0) ? refr_color :
vec3(1.0); /* Simulate 2 transmission event */
refr_color *= saturate(1.0 - fresnel) * transmission;
sheen_color *= m_transmission;
mixed_ss_base_color *= m_transmission;
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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