1
1

Compare commits

...

162 Commits

Author SHA1 Message Date
b53fd38ff3 Cleanup Pep8 2021-10-07 16:07:27 +02:00
b31a265ada Check in Panels if using Scene Render 2021-10-07 15:52:08 +02:00
d238b77bdb Small tweaks to panels 2021-10-06 18:24:17 +02:00
b6ee53fb55 Hide unsupported features 2021-10-06 17:39:33 +02:00
4bbfdce004 Move GPStyle pass_index to Material
This was duplicated
2021-10-06 17:17:29 +02:00
4500e100bb Changes in Material panels 2021-10-06 17:07:51 +02:00
20abefa18e Revert different types of materials creation 2021-10-06 16:05:51 +02:00
889d1f4e85 Merge branch 'eevee-rewrite' into temp-eevee-gpencil-rewrite 2021-10-06 12:09:46 +02:00
bcd5bd6cd5 EEVEE: Fix double Stroke color in Panel
The stroke color was displayed in the side panel and this must be used only in 3D View.
2021-10-06 12:08:59 +02:00
2f35045fd1 Revert python changes in material panel 2021-10-06 10:35:40 +02:00
95210568b7 Merge branch 'temp-eevee-gpencil-rewrite' of ssh://git.blender.org/blender into temp-eevee-gpencil-rewrite 2021-10-05 16:51:50 +02:00
fe58ba2ade New property to define render engine for grease pencil 2021-10-05 16:25:25 +02:00
c2c2f0b284 Create material depending of type 2021-10-05 16:08:29 +02:00
9fdd21d153 Basic show material panel 2021-10-05 14:13:30 +02:00
a74350af58 More python changes 2021-10-05 12:25:23 +02:00
efa55c157a New property to define render engine for grease pencil 2021-10-05 12:25:23 +02:00
6f773e289b EEVEE: Fix shader compilation caused by latest merge 2021-09-29 18:10:06 +02:00
d5f91a68c0 Merge branch 'draw-viewport-data' into eevee-rewrite 2021-09-29 18:06:17 +02:00
4984cba10d DRW: Fix implicit convertion warning on MSVC 2021-09-29 18:05:49 +02:00
e28ae32461 Merge branch 'draw-viewport-data' into eevee-rewrite 2021-09-29 17:26:03 +02:00
59a0099b9f Merge branch 'master' into draw-viewport-data 2021-09-29 17:25:16 +02:00
225c1b0708 Merge branch 'draw-viewport-data' into eevee-rewrite
# Conflicts:
#	source/blender/draw/engines/eevee/eevee_cryptomatte.c
#	source/blender/draw/engines/eevee/eevee_effects.c
#	source/blender/draw/engines/eevee/eevee_engine.c
#	source/blender/draw/engines/eevee/eevee_lookdev.c
#	source/blender/draw/engines/eevee/eevee_materials.c
#	source/blender/draw/engines/eevee/eevee_motion_blur.c
#	source/blender/draw/engines/eevee/eevee_private.h
#	source/blender/draw/engines/eevee/eevee_render.c
#	source/blender/draw/engines/eevee/eevee_subsurface.c
#	source/blender/draw/engines/eevee/eevee_volumes.c
#	source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
#	source/blender/draw/engines/eevee/shaders/effect_downsample_frag.glsl
#	source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl
#	source/blender/draw/intern/DRW_render.h
#	source/blender/draw/intern/draw_cache.h
#	source/blender/gpu/GPU_material.h
#	source/blender/gpu/intern/gpu_codegen.c
#	source/blender/gpu/intern/gpu_material.c
#	source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
#	source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
#	source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
#	source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
#	source/blender/makesdna/DNA_gpencil_types.h
#	source/blender/makesdna/DNA_node_types.h
#	source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c
#	source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c
2021-09-29 17:24:30 +02:00
f8cfd7e288 Merge branch 'master' into draw-viewport-data
# Conflicts:
#	source/blender/draw/DRW_engine.h
#	source/blender/draw/intern/draw_manager.c
#	source/blender/draw/intern/draw_manager.h
2021-09-29 11:31:39 +02:00
dc0c074ac4 Cleanup: Remove compiler warning and fix some comments 2021-09-29 11:24:25 +02:00
2994b6dd98 EEVEE: Fix crash when destroying the Instance (in debug build)
This was caused by the StructArrayBuffer wrapper not being tagged as NonMovable.
The UBO was in fact being freed at creation time in debug build, but the
pointer was kept as valid in the copied wrapper.

Changing the higher level structure to not use the copy constructor to avoid this.
2021-09-29 11:14:34 +02:00
ab6a6ffed4 Cleanup: Remove compiler warnings 2021-09-29 11:14:34 +02:00
d3a825678a EEVEE: Film: Make smooth transition not rely on dtxl->color persistence
This is a needed change for the viewport compositor. The compositor
needs to draw to `dtxl->color` to have correct overlay / background
composition.

The solution here is to have a separate buffer that keeps the first
sample we blend from. This increases VRAM usage but it is the most
elegant option.
2021-09-15 17:15:40 +02:00
41c84bb08b EEVEE: More Windows 64bits changes
Missing in previous commit
2021-09-13 17:14:54 +02:00
9711cddbe0 EEVEE: Fix Windows 64bits error
Windows is different of Linux
2021-09-13 17:02:04 +02:00
85b6e6da4a EEVEE: Fix compiler errors in Windows 2021-09-12 19:37:59 +02:00
c51604e308 EEVEE: Use special depth shader to resolve the depth buffer
This was the cause of a bug on Intel Integrated GPU and might as well
impact other platforms.
2021-09-07 08:41:57 +02:00
9207920c1e EEVEE: LightGrid: Fix divide by zero leading to wrong world lighting
This integer divide by zero was evaluated to 0 on all platform but apple,
where it yields 1. The world lighting would then sample the 1 sample of the
first grid instead of its own sample.
2021-08-25 15:38:26 +02:00
f79788f767 EEVEE: Film: Fix undefined viewport color values after resizing
This was caused by the blend mode that was used even with full opacity.
This caused issues with the viewport was resized and the color of the
framebuffer becomes undefined, leading to undefined values in the blend
equation.

Another fix would be to clear the viewport color on resize inside the
GPUViewport.
2021-08-19 10:01:25 +02:00
c4287db151 EEVEE: Fix compilation on OSX platform
OSX driver does not support loose semicolon in code
2021-07-20 16:48:38 +02:00
62aa72dac8 Merge branch 'draw-viewport-data' into eevee-rewrite
# Conflicts:
#	source/blender/blenlib/BLI_assert.h
#	source/blender/blenloader/intern/versioning_290.c
#	source/blender/draw/engines/eevee/eevee_cryptomatte.c
#	source/blender/draw/engines/eevee/eevee_depth_of_field.c
#	source/blender/draw/engines/eevee/eevee_effects.c
#	source/blender/draw/engines/eevee/eevee_engine.c
#	source/blender/draw/engines/eevee/eevee_lightcache.c
#	source/blender/draw/engines/eevee/eevee_lightprobes.c
#	source/blender/draw/engines/eevee/eevee_lights.c
#	source/blender/draw/engines/eevee/eevee_lookdev.c
#	source/blender/draw/engines/eevee/eevee_materials.c
#	source/blender/draw/engines/eevee/eevee_mist.c
#	source/blender/draw/engines/eevee/eevee_motion_blur.c
#	source/blender/draw/engines/eevee/eevee_occlusion.c
#	source/blender/draw/engines/eevee/eevee_private.h
#	source/blender/draw/engines/eevee/eevee_render.c
#	source/blender/draw/engines/eevee/eevee_renderpasses.c
#	source/blender/draw/engines/eevee/eevee_screen_raytrace.c
#	source/blender/draw/engines/eevee/eevee_shaders.c
#	source/blender/draw/engines/eevee/eevee_shadows.c
#	source/blender/draw/engines/eevee/eevee_shadows_cascade.c
#	source/blender/draw/engines/eevee/eevee_subsurface.c
#	source/blender/draw/engines/eevee/eevee_temporal_sampling.c
#	source/blender/draw/engines/eevee/eevee_volumes.c
#	source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
#	source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
#	source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl
#	source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
#	source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl
#	source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl
#	source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl
#	source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_tiles_flatten_frag.glsl
#	source/blender/draw/engines/eevee/shaders/effect_dof_bokeh_frag.glsl
#	source/blender/draw/engines/eevee/shaders/effect_dof_dilate_tiles_frag.glsl
#	source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl
#	source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
#	source/blender/draw/engines/eevee/shaders/effect_dof_reduce_frag.glsl
#	source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl
#	source/blender/draw/engines/eevee/shaders/effect_dof_setup_frag.glsl
#	source/blender/draw/engines/eevee/shaders/effect_reflection_resolve_frag.glsl
#	source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl
#	source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl
#	source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl
#	source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
#	source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl
#	source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl
#	source/blender/draw/engines/eevee/shaders/lights_lib.glsl
#	source/blender/draw/engines/eevee/shaders/random_lib.glsl
#	source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl
#	source/blender/draw/engines/eevee/shaders/ssr_lib.glsl
#	source/blender/draw/engines/eevee/shaders/surface_lib.glsl
#	source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl
#	source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl
#	source/blender/draw/intern/draw_manager_shader.c
#	source/blender/draw/intern/shaders/common_math_lib.glsl
#	source/blender/gpu/CMakeLists.txt
#	source/blender/gpu/intern/gpu_codegen.c
#	source/blender/gpu/intern/gpu_shader.cc
#	source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl
#	source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
#	source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
#	source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl
#	source/blender/nodes/shader/nodes/node_shader_output_aov.c
2021-07-19 23:04:20 +02:00
0053d2fc81 DRW: Move buffer & temp textures & framebuffer management to DrawManager
This is a necessary step for EEVEE's new arch. This moves more data
to the draw manager. This makes it easier to have the render or draw
engines manage their own data.

This makes more sense and cleans-up what the GPUViewport holds

Also rewrites the Texture pool manager to be in C++.

This also move the DefaultFramebuffer/TextureList and the engine related
data to a new `DRWViewData` struct. This struct manages the per view
(as in stereo view) engine data.

There is a bit of cleanup in the way the draw manager is setup.
We now use a temporary DRWData instead of creating a dummy viewport.

Differential Revision: https://developer.blender.org/D11966
2021-07-19 19:47:55 +02:00
6206a30519 EEVEE: GBuffer: Change layout
This change the gbuffer layout to use more of the hardware to converting
data back and forth. Normals are encoded as two 16 bits components and
colors as R11G11B10F format.

This was motivated by the need of better quality normals. The issue is
that this increase the GBuffer size consequently. In order to balance
this we chose to merge the refraction and Diffuse/SSS data to use the
same buffer. This means we need to stochastically chose one of these
layers (so noise appear). Given that Glass BSDFs are rarely mixed
with Diffuse BSDFs, we think this is a good tradeoff.
2021-07-19 19:01:09 +02:00
e3ff83a4a8 GPUFramebuffer: Bump maximum color attachement to 8
This is needed for EEVEE's new deferred render pipeline.
2021-07-15 17:26:03 +02:00
80b92467f0 GPU: Shader: Add debug context line count
This gives more detail, when the error line is misleading.
2021-07-03 14:45:26 +02:00
169a2a54d7 EEVEE: Fix crash caused by transparent material without prepass 2021-07-03 14:44:23 +02:00
d8ec228a76 GPUMaterial: Fix issue with displacement tree and partial derivatives 2021-07-01 00:02:42 +02:00
27adad0b0d GPUNodeTree: Fix issue with weight tree inversion and displacement
Displacement tree was also being tagged for copy and caused issue.
2021-06-30 23:56:03 +02:00
c4a3ba6f83 EEVEE: Hair: Add back shaded hair support
Same implementation as before but it is less intrusive towards the
shading Node glsl code.

Hair shaders also now supports displacement.
2021-06-30 23:55:37 +02:00
e962002db2 DRW: Fix crash in deferred compilation 2021-06-23 15:39:55 +02:00
3ad7832a8d EEVEE: Add back refraction support for lightprobes
Screen Space Raytracing support is still to come.
Both forward and deferred pipelines are supported.
2021-06-20 18:45:38 +02:00
209ab8c424 EEVEE: Cleanup: Replace lighting evaluation macro by functions
The functions need to be declared before main as prototypes.
The appended libs will use the resources (textures, UBOs) defined at
global scope.

This removes a bit of code duplication and some long macros.
2021-06-20 01:28:30 +02:00
94f813db70 DRW: ShaderLib: Add support for requesting lib to be appended to shader
Instead of appending using `BLENDER_REQUIRE`, shaders can now ask for
libs to be added after the shader's `main()` by using the
`BLENDER_REQUIRE_POST` pragma.
2021-06-19 18:01:26 +02:00
c844497aee EEVEE: Film: Fix accumulator precision when zoomed out
Use viewspace instead of world space to compute pixel projection.
This fix issues when camera is far from origin and float precision would
produce artifacts.
2021-06-19 18:00:53 +02:00
a6ae942540 EEVEE: Port back barycentric coordinates.
Nothing changed appart from the style of the integration which is
now fully on EEVEE's side.
2021-06-15 22:35:54 +02:00
1f262a461c GPUCodegen: Fix crash when there is no output node 2021-06-15 22:31:40 +02:00
4c816924e7 EEVEE: GPencil: Fix missing strokes
And comment velocity not implemented yet
2021-06-08 22:54:12 +02:00
a1459e1fcf EEVEE: Shadows: Modify view matrix instead of projection for each face
This is the same reason we changed back for lightprobes rendering:
To much area assume -Z is camera direction.
2021-06-08 22:30:36 +02:00
4260823e1e EEVEE: GPencil: Finish geometry support
This port the facing "flat" normal trick used by the gpencil engine
to EEVEE as well as the thickness mode.

The objects parameters are passed via the objectInfos UBO to avoid
much boiler plate code. However if this UBO grows too much we might have
to split it.

The normal trick for planar surfaces is quite simple to port to the
vertex shader even if it is less efficient.
However to compute it we need the objects bounds. This is passed as a
scale only through the orco factors. This will needs a bit of cleaning
at some points, with boundbox computed at object level.
2021-06-08 22:09:57 +02:00
89a002c4e3 EEVEE: Material: Add back support for backfacing and transparency
Nothing much different compared to the previous implementation.

The transparent BSDF and principled BSDF now detects when the material
is potentially transparent to select the best way to render it.
2021-06-07 19:49:00 +02:00
6c1e7868c7 EEVEE: LookDev: Move rendering to view render.
This makes is possible to have AA and correct blending of the
forward rendered spheres.

However, to avoid distorded spheres we need to not support Lookdev
in panoramic projection mode.

Also remove support for LookDev when using render border for now.
2021-06-06 16:36:02 +02:00
9b153666e7 EEVEE: FowardPass: Fix closure sampling, add emission & fix transparency 2021-06-06 16:36:02 +02:00
93881a2a8e EEVEE: LookDev: Add back overlay support
This differs a bit from old implementation.
- Instead of manually adjusting the viewport we correctly place the
  sphere in the vertex shader.
- Rendering happens after TAA accumulation: This is because we now
  support panoramic cameras and TAA would distort the spheres.
2021-06-06 16:36:02 +02:00
04f053c6a4 EEVEE: ForwardPass: Add lightprobe support & fix pipeline selection 2021-06-06 00:37:55 +02:00
d66b98e9c8 EEVEE: Patch lightprobe and light modules to handle zero lights/probes
This expose the capability of having no light and no probe (except the
world one) for specific views / code path.

The caller just need to pass 0 as extent to the `set_view()` function.

This is usefull for lookdev.
2021-06-05 23:46:27 +02:00
ae529ed93e BLI: int2: add more float operator to avoid incorrect implicit cast 2021-06-05 23:45:13 +02:00
581cb48c9e BLI: float2: add more operator and fix a typo 2021-06-05 23:44:44 +02:00
dc64186d75 EEVEE: Nodes: Fix environment texture node default mapping and ...
... empty image behavior
2021-06-05 16:00:44 +02:00
308d42d072 Merge branch 'draw-viewport-data' into eevee-rewrite 2021-06-05 15:38:33 +02:00
e6d94b83ba DRW: Fix memory leak of GPUTextures
The textures needs to be released by iterating. Not by using the
free callback.
2021-06-05 15:36:47 +02:00
3caf7ba32d EEVEE: Lookdev: Add back lighting support
This does not include reference spheres rendering.

The approach is a bit different than before.
Now we use a `bNodeTree` to control the rendering of lookdev. This
generates a `GPUMaterial` that is stored per `Instance`. This way
rendering lookdev is just updating the temp light cache using this
material as world material. Removing the use of custom shader.

This introduces a small hack in order to bind the studiolight hdri after
the nodetree glsl parsing.

The background display however is still using a custom shader in order
to sample the world cubemap with different roughness.

The view space option of the studiolight is now faster by using a
transform before shading instead of rebaking the lightprobe constantly.
This should not have any particular impact on render time.
2021-06-05 15:29:00 +02:00
b3084d23bf EEVEE: Light: Fix culling in orthographic view 2021-06-04 21:05:32 +02:00
7f5d787952 EEVEE: LightCache: Fix broken visibility sampling 2021-06-04 20:29:48 +02:00
79a5322fa4 EEVEE: LightCache: Fix broken light bounce 2021-06-04 19:52:03 +02:00
92aedc5eda EEVEE: Fix world probe rendering objects
Was leftover of a test.
2021-06-04 18:22:37 +02:00
33ff463ea1 EEVEE: GBuffer: Fix undefined behavior
When evaluating surfaces, the deferred passes needs to sample the
depth buffer. But it also test against the stancil buffer.

Moreover the sampler needs to be a 2D sampler which is not the case
for cubemaps and texture2Darrays.

To overcome this we simply copy the gbuffer depth to another
temp texture using framebuffer blitting.
2021-06-04 02:34:32 +02:00
7d3f65a044 EEVEE: Remove light specular during baking & fix bounce light 2021-06-04 02:33:44 +02:00
6d3c7a8281 EEVEE: LightProbe: Fix grid display and deduplicate code 2021-06-03 16:53:38 +02:00
8e6deba985 EEVEE: LightProbe: Fix light cache display visibility without overlays 2021-06-03 16:35:55 +02:00
d31e74d3f8 EEVEE: LightProbe: Fix wrong shading on other cube faces
Use modified viewmat instead of modified winmat. Too much code
assume conventional winmat.
2021-06-03 16:34:48 +02:00
bdcf0ccead EEVEE: LightProbe: Fix level bias and visibility filtering 2021-06-03 16:34:16 +02:00
c8b40c5fd6 EEVEE: Fix irradiance baking sample order 2021-06-03 16:33:48 +02:00
2dc9db65d7 EEVEE: Fix irradiance smoothing wrong 2021-06-03 15:17:44 +02:00
ff00c1d6eb EEVEE: Fix background transparent
Alpha of main output is transparency and not opacity.
2021-06-03 15:15:06 +02:00
1f5c3c9d74 EEVEE: LightCache: Port back cache Display
Nothing significantly different appart from codestyle.
2021-06-03 15:15:06 +02:00
d87161e574 BLI: float3: add division operator between 2 float3 2021-06-03 15:15:06 +02:00
14df74ea8b EEVEE: LightCache: Port baking
Some things differs from old implementation.
- Object visibility is filtered correctly without using a visibility
  callback (which is to be removed).

The implementation is also more high level using less low level tricks.

A dedicated LightProbeView is created for each lightprobe cubeface to
render using all pipeline (deferred and forward).

There is still a few things not working.
2021-06-03 15:15:06 +02:00
060c462f3a EEVEE: LightProbe: Finish loading and rendering of lightcache
Starting to port lightcache.c to c++.
2021-05-31 14:25:03 +02:00
25dd16a8cd EEVEE: Fix crash cause by world material without attributes 2021-05-30 22:48:40 +02:00
0fb1621594 EEVEE: Lightprobe: Add back lightprobe rendering support for world.
Only world probe is supported for now.

The new implementation diverge from the original by randomly
selecting one lightprobe instead of sampling them all.
This speeds-up rendering a bit.
2021-05-30 22:48:40 +02:00
5fb1b27d17 EEVEE: Lightprobe: Port back lightprobe filtering
No much change appart from code organization and structure.
2021-05-30 22:48:40 +02:00
6376b575d9 DRW: Add DRW_view_frustum_bsphere_get 2021-05-30 22:48:40 +02:00
31963c8d86 DRW: Increase shader library max supported lib count 2021-05-29 16:59:15 +02:00
4495060185 DRW: Add DRW_view_get_active 2021-05-29 16:44:00 +02:00
f4dbdd7b52 EEVEE: Lightprobe: Add simple world probe rendering and downsampling 2021-05-28 01:07:12 +02:00
e19c028cc4 EEVEE: Add back world nodetree support
Only for background for now.

Support is now not using defines and just use the correct globals and
uniforms to keep the same values as before.
2021-05-28 00:51:04 +02:00
f04011dd87 World: Add static default world
This is a small convenience. This let the render engine use this
default world if scene has no world.

World is black to keep the same behavior as before.
2021-05-27 04:52:21 +02:00
1bc0a70d94 EEVEE: Fix ModelMatrix in nodetree and wrong bump map output 2021-05-26 02:23:32 +02:00
2b6c70a780 EEVEE: Rework multi material handling
Shading groups are now created by the material_array_get functions
instead of passing a reference to be filled later. This avoids having
to wait later to maybe create a sub shading group.
This also simplifies different geomety type handling.
2021-05-26 02:02:46 +02:00
113c16d7a9 EEVEE/GPU: Nodes: Fix some BSDF nodes
Fix glass, principled and eevee specular shaders.
2021-05-24 20:45:58 +02:00
06ca1818d0 EEVEE: GPencil: Add basic support for gpencil geometry
This adds support for rendering gpencil objects.

There is a lot of features to implement specially the ones requiring
per object uniforms.
2021-05-24 20:43:25 +02:00
6c0d8c4b75 EEVEE: Material: Fix shader uuid to material type conversion 2021-05-24 18:13:55 +02:00
44bb4be66c DRW: Shader: Fix deferred compilation abortion
The case where a shader was queued but then requested as non deferred
was not handled correctly.
2021-05-24 18:12:24 +02:00
f7f1ee9e99 EEVEE: Add back split sum BSDF approximation
This improves the surface appearance of most bsdf.
2021-05-23 02:42:46 +02:00
e91df656f5 EEVEE: Material: Use weight to random sample closure per types
This adds a new closure selection method.

- In a first pass, weights are accumulated per output type (diffuse,
  reflection, refraction).
- A random threshold is then generated before evaluating the BSDF nodes
  again.
- During the evaluation pass the random threshold is decremented until
  it reaches 0. At this moment the current BSDF is sampled.

For this to work, I splited the evaluation and the weighting in two
functions for all BSDF. The `*_eval` nodes are generated as dangling
nodes from the graph and only serialized after the rest of the graph.
2021-05-23 00:39:56 +02:00
10cf16270a GPUMaterial: Add recalc flag workaround
Recalc flag on Material ID being unavailable to render engine, this
adds a simple way to detect material update by detecting shader creation
or update.
2021-05-22 03:05:24 +02:00
7d36a00d14 GPUNodeTree: Add weight tree inversion
This constructs a "mirror" nodetree that feeds the closure "shader"
nodes with their respective final weight.

The tree is mirrored using simple math nodes. This is quite messy but
this is the only way to proceed without introducing special nodes.
The other issue with this method is that inputs are all uniforms even
for unplugged socket on temporary math nodes with add bloat to the
shader uniform buffer structure.

Only the part relevant to the weighting is duplicated. Other connexions
with the shading tree are reuse.

All shader nodes are updated to receive a `Weight` hidden parameter.

The original shader mixing tree is preserve to let the choice of using
either way to weight the output.

For now this is only done for the output nodes. This will need to be
extended to Closure to RGBA sub-tree.
2021-05-22 02:55:41 +02:00
9a857d83a6 GPUNodeTree: Move closure socket implicit cast to ntreeGPUMaterialNodes
This reduces complexity of weighting closure in further dev.
2021-05-21 17:28:18 +02:00
25806227e8 GPUNode: Remove ssr_id and sss_id
These are not useful anymore.
2021-05-21 17:25:55 +02:00
6dc49ec992 GPU/EEVEE: Refactor codegen and nodetree support
This is the first step towards the new evaluation scheme of EEVEE
closures.

This commit contains:
- Removal of GPU_SOURCE_BUILTIN type, prefering global instead. This
  avoid many boilerplate code since most of the old builtins are now
  datas that are always present (i.e: view matrices, normals).
- Rewritting of codegen in C++ to use `std::stringstream`.
- Added a callback to let engine decide what to do with codegen code.
  This remove a lot of needs for defines because of code order
  dependency. The engine can insert the nodetree code in custom ways
  to create advance effects (i.e: add displacement or vertex lighting).
  Engine now returns final shader strings.
- Closure nodes evaluation replacment is a placeholder for now.
2021-05-20 23:51:52 +02:00
0c71240f14 EEVEE: Material: Add basic material logic
This is a port of the old material grouping. This is a bit more
clean as we use containers for each passes and other structures.

Nodetree is generated without major error for simple materials but
it is not yet used as closures are not outputed.
2021-05-18 15:39:40 +02:00
f46661428b DRWShaderLib: Add better debug output from missing lib 2021-05-03 16:37:17 +02:00
4500a90cdc EEVEE: Implementation of volume rendering
This adds the transparency and volume handling in the deferred
render pipeline.

Implementation is still unfinished.

To have better naming convention, I renamed object shader to surface.
2021-05-03 16:35:36 +02:00
99a5d49a38 EEVEE: Initial implementation of deferred shading
This introduce a fat Gbuffer layout that groups closure data in groups
of similar BSDF. The goal is to have at least one sample for each
group to avoid too much code complexity and expected worse performance.

There is a lot of room for buffer reuse to reduce memory usage but it is
 not considered a priority for now.
2021-04-30 15:57:38 +02:00
c59156dac7 DRWTexture: Add missing integer render-targets format support. 2021-04-30 15:56:15 +02:00
c7fb0d5c7b EEVEE: Add specular layer to temporary default material 2021-04-27 13:45:20 +02:00
f1a5c5f6cb EEVEE: Film/Sampling: Add smooth transition
Add a smooth transition to avoid flickering of stochastic effects such
as soft shadows.

This use a simple blend method to progressively reveal the render
after some low sample count to avoid most of the flickering.

Parameters are hardcoded for now.
2021-04-27 13:42:43 +02:00
556478c20e EEVEE: Shadow: Add back soft shadows support
We use a new RNG to avoid correlation artifacts between Anti-Aliasing
and Shadow samples (see T68594).

The new sequence is a leap halton sequence. This makes it good with
low number of samples and yield less correlation issues.

Another change is that we directly jitter the projection matrix instead
of rotating the view matrix. This is improving convergence time and
avoid passing a second matrix to the shader.

However this case lead to discontinuity artifacts at face boders.
We might want to revert to the old rotation method for this
reason even if convergence is slower.
2021-04-27 03:15:15 +02:00
5df8d5af8a EEVEE: Shadow: Add back shadow caster tracking
Compared to previous implementation this does track dupli objects.

There is a few optimizations left like using bitfield instead of bool
arrays.
2021-04-26 15:17:59 +02:00
1d3de154e2 EEVEE: Shadow: Simplify the shadow module
Now the shadows are linked to a `Light` object. The `Light` object is
linked to an `ObjectKey` to ensure persistence and deletion tracking.

The Uniform data are packed so that there is 1 `ShadowPunctualData`
per light in a `LightBatch`. This means there is only a shadowmap
limit to the number of `Shadow` in a scene.
2021-04-26 01:44:52 +02:00
4090bac8c8 EEVEE: Use C++ vector types 2021-04-24 23:03:21 +02:00
0932d508c8 BLI: Add more operator to float2/3 and int2/3 2021-04-24 23:02:50 +02:00
0fdd8a64b4 BLI: add int2 and int3 C++ support
Simple addition based on float2/3.
2021-04-24 21:58:03 +02:00
9dddfe2ef6 DRW: Debug: Add DRW_debug_view 2021-04-23 17:32:12 +02:00
e808500ba1 EEVEE: Shadow: Add Point light shadows support back
Difference with previous implementation:
- Better texture space usage of cone and area light shadow.
- Shadows are packed in an atlas. Reducing requirements for future
  features.
- Sampling is simpler because shadow matrix does everything.
2021-04-23 17:27:02 +02:00
2fd359684d EEVEE: Fix use after free crash 2021-04-21 22:35:00 +02:00
da91f87764 EEVEE: Check updates using recalc flag only an object maps.
This avoids having to reset accumulation if nothing affecting
eevee changes.
2021-04-21 22:06:52 +02:00
610294205f EEVEE: Light: Add back LTC area lights and lighting function
I did a small optimization pass to avoid some division and
redundant computation.

Also cleans-up the Light vector usage.
2021-04-19 14:29:38 +02:00
5697f96608 EEVEE: Light: 2.5D Culling: Initial implementation
This follows closely the implementation of 2.5D tiled light
culling described in the presentation:
"Improved Culling for Tiled and Clustered Rendering"
from Michal Drobot
http://advances.realtimerendering.com/s2017/2017_Sig_Improved_Culling_final.pdf

I chose the tile + Z binning approach for its high depth range support
and low CPU overhead & low memory consumption compared to the cluster
based culling. The cons is that the culling is a bit less precise in
some aspect but it is quite balanced.

The culling is done by the `Culling` object which is templated to easily
be reused for light probes cullg.

The Z-binning process is described starting from slide 20 in the
reference pdf.

I also implemented a debug pass to visualize false negative (light
culled when they shouldn't) and light evaluation density.
This is useful to detect failure case and hotspot. This could be exposed
as a developper only render pass in the future.

Some optimization of the reference implementation requires extensions
not yet added to GPU module and will be added later.
2021-04-18 17:30:36 +02:00
93b774a661 GPUTexture: Fix missing cases for integer textures 2021-04-18 01:15:19 +02:00
bf0ca28494 DRW: Fix const correctness 2021-04-18 01:14:48 +02:00
89af2b0485 EEVE: Lights: Implement simple culling scheme.
This has the basis of clustered light culling but does not yet do
it. The lights are only culled by frustum.

Its the same as if there was only one Cell for the entire Viewport.
2021-04-16 02:16:15 +02:00
ddc1be5556 EEVEE: Split shading passes to eevee_shading.cc 2021-04-15 01:05:42 +02:00
23584ee52f EEVEE: Refactor: Split implementation to .cc file
This also wrap GPUFrameBuffer & GPUTexture inside eevee:Framebuffer
and eevee:Texture to improve managment.

Another cleanup was to put all members of `Instance` public to
avoid much complexity in accessing the data with modules
dependencies.

Also split velocity View related data to `class Velocity` and
rename previous `Velocity` to `VelocityModule`
2021-04-15 00:49:32 +02:00
520962b3d6 EEVEE: Lights: Support infinite light count
Support infinite light count by dividing rendering into chucks of
LIGHT_MAX. Forward passes are just rendered again and deferred passes
(not implemented yet) will just have to have multiple light evaluation
passes.
2021-04-14 16:46:48 +02:00
017e9d852e EEVEE: Lights: Initial Work in progress implementation
Only supports simple point lights for now
2021-04-14 13:21:49 +02:00
ab55a1b67b DRW: Make draw_debug.h C++ compatible 2021-04-14 13:20:25 +02:00
9bf6fa974d Cleanup: EEVEE: RenderPasses: Use iterator 2021-04-14 12:08:44 +02:00
431a662f4f EEVEE: Add back object update and view update detection. 2021-04-14 00:24:05 +02:00
9c74f9322d EEVEE: Motion Blur: Fix cdf inversion and a typo 2021-04-12 23:06:55 +02:00
a4ae2b91c9 EEVEE: Motion Blur: Add back post process motion blur
This is almost the same thing as old implementation.
Differences:
- We clamp the motion vectors to their maximum when sampling the velocity buffer.
- Velocity rendering (and data manager) is separated from motion blur. This allows
  outputing the motion vector render pass and in the future use motion vectors to
  reproject older frames.
- Vector render pass support (only if motion blur is disabled, just like cycles).
- Velocity tiles are computed in one pass (simpler code, less CPU overhead, less
  VRAM usage, maybe a bit slower but imperceivable (< 0.3ms)).
- Two velocity passes are outputed, one for motion blur fx (applied per shading view)
  and one for the vector pass. This could be optimized further in the future.
- No current support for deformation & hair (to come).
2021-04-12 21:34:08 +02:00
0cd4896037 EEVEE: Add wrapper to simplify ubo managment 2021-04-10 01:40:19 +02:00
e540c9c47a EEVEE: Motion Blur: Add back accumulation logic
Bonus addition, support for shutter curve.

Compared to the old implementation, the per time step sync function
is lighter and localized. Also it does not require a full engine
"reboot" in order to work.

Also modifies camera setup to be compatible with future camera motion
blur.
2021-04-09 21:42:56 +02:00
f9b15edbde EEVEE: Motion Blur: Add back accumulation logic
Bonus addition, support for shutter curve.

Compared to the old implementation, the per time step sync function
is lighter and localized. Also it does not require a full engine
"reboot" in order to work.
2021-04-09 21:42:49 +02:00
1857fa8bc9 EEVEE: Depth Of Field: Fix ortho factor
When will it end? Is there some UB somewhere in cycles?
2021-04-08 23:13:10 +02:00
ac6e2b0083 EEVEE: Sampling: Add UBO containing LDS random numbers
We have many dimensions to avoid correlation between effects.
2021-04-08 23:11:51 +02:00
d18e74d822 DRW: Fix debug print of error in shader lib dependency. 2021-04-08 16:22:57 +02:00
8c753a2c80 GPUTexture: Fix max FBO attachement in panoramic view + dof + eevee 2021-04-08 16:22:14 +02:00
2a7d9d4515 EEVEE: Depth Of Field: Add back post process depth of field.
Pretty much identical to the previous implementation. With the exception
of a temporary noise function and some simplification of the CoC
computation. This also fixes issues with the Ortho depth of field.

Most of the files were modified to comply to new shader codestyle.

This also adds partial support of panoramic cameras (bokeh and
anamorphic is still buggy).
2021-04-08 16:17:04 +02:00
89f2d3427e EEVEE: Depth Of Field: Port back jittered dof
This cleansup a lot of confusion / complexity in the setup code.

Setup is closer to what cycles does now.

Also duplicates some buggy behavior of Cycles for now until this
is fixed.
2021-04-05 17:38:41 +02:00
309b90c955 Cleanup: BLI: Fix some const conrectness 2021-04-05 17:34:33 +02:00
a766ee6d5d EEVEE: Shader: Add preprocessor to have better shared enum definition
This removes the use of very ugly macros. Now we preprocess
the .hh string at startup to change enum definitions to a more
GLSL friendly variant.
2021-04-04 21:56:55 +02:00
ebf455daba Cleanup: DRW: Const correctness 2021-04-04 19:17:50 +02:00
fe9ea5d5cb Cleanup: EEVEE: Use class instead of struct and remove typedefs 2021-04-04 00:12:25 +02:00
8a2f400cf3 EEVEE: Reorganize ShadingView initialisation
This move view resolution handling to the `Camera` class that will
in the future clip and trim each view in panoramic projection.

There is a new `CameraView` that contains the `DRWView` and subview.
This way each `ShadingView` is associated to a unique `CameraView`.

ShadingView` & `CameraView` are all allocated & defined at creation time
but only the one activated by `Camera` will be rendered.
2021-04-03 23:43:23 +02:00
448d10a31a Cleanup: DRW: More const correctness 2021-04-03 23:40:48 +02:00
e0e1dd73bb EEVEE: Film: Add option to use log encoding
This option will make accumulation happen in a pre exposed logarithm
color space. This reduces the importance of bright pixels in the pixel
filter which will result in less aliasing in theses areas.

There is a few cases where one might want to disable this option to
match cycles better.
2021-04-03 23:40:26 +02:00
fa88f5af4c EEVEE: Render: Add back render mode
Render mode is really close to what the viewport render does.

Film output is done by resolving the data to the next (double buffered)
framebuffer and read back.

This also includes a bit of cleaning about naming of init() and sync()
functions.
2021-04-03 00:51:37 +02:00
fcb85633ea DRWView: Add getter for camtexco 2021-04-03 00:50:49 +02:00
935da83e2c Cleanup: RenderPipeline: Fix const correctness 2021-04-03 00:50:02 +02:00
ef174ffcb3 Cleanup: EEVEE: Renaming
- Add eevee_ prefix to shaders to avoid name clashing.
- remove plural of eevee_shaders.
- rename eevee_shared.hh to eevee_shader_shared.hh.
2021-04-02 14:44:44 +02:00
ad23570fa2 EEVEE: Film: Filtered accumulation with panoramic projection support
This commit adds the Film class that handles accumulation of color and
non-color data using arbitrary projection and filter size.

A weighted accumulation (sum) is done into a data buffer with an
additional weight buffer. The sum being per pixel, it allows the input
textures that are not aligned with the output pixel grid.

Panoramic projection works by rendering a cubemap (6 views) of the scene
at the camera position. The Film filter pass then gather the pixels
using the correct Panoramic projection ensuring correct Anti-Aliasing.

For Non-color data (depth, normals) we only keep the closest value to
the target pixel center (simulating a filter size of 0).

Color data is accumulated in a log space to improve AntiAliasing output.
This is hardcoded for now.

Larger filters have poor performance but are very fast to converge.

Code Wise: This commit rename some modules to avoid possible confusion
and have better meaning. Use namespace instead of prefixes.

Added a new eevee_shared.hh file to share structure and enum definitions
between GLSL and C++.
2021-04-01 22:37:50 +02:00
8777497c4b DNA: Camera: Add panoramic types and parameters
These are direct copy of Cycles parameters. Cycles parameters
unification will be done separately.
2021-04-01 21:38:53 +02:00
25af8b801d EEVEE: New basic drawing
TODO describe overall implementation here
2021-03-28 16:53:31 +02:00
829e2e4a24 EEVEE: Add new engine managed per viewport data
This was engine(s) can have arbitrarily persistent resources and
are responsible for freeing it properly.
2021-03-25 14:30:47 +01:00
09e1f276ff EEVEE: Remove EEVEE
Starting clean!
2021-03-25 13:15:35 +01:00
81632de706 DRW: Move mempool datas to a DRW managed struct.
Same idea as previous commit. This cleans-up the interface and put all
viewport related data inside the `DRWData` struct.

The draw manager is responsible for freeing it. That is the main point
of this all. In the future, we can have custom freeing method for each
engine.

This also move the DefaultFramebuffer/TextureList and the engine related
data to a new `DRWViewData` struct. This struct manages the per view
(as in stereo view) engine data.

There is a bit of cleanup in the way the draw manager is setup.
We now use a temporary DRWData instead of creating a dummy viewport.
2021-03-25 01:03:10 +01:00
f7cb19956f DRW: Move GPUViewport texture pool to DRW module
This makes more sense and cleans-up what the GPUViewport holds.

Also rewrite it to be in C++.
2021-03-22 23:53:42 +01:00
422 changed files with 24317 additions and 29747 deletions

View File

@@ -180,6 +180,7 @@ ForEachMacros:
- CTX_DATA_BEGIN_WITH_ID
- DEG_OBJECT_ITER_BEGIN
- DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN
- DRW_ENABLED_ENGINE_ITER
- DRIVER_TARGETS_LOOPER_BEGIN
- DRIVER_TARGETS_USED_LOOPER_BEGIN
- FOREACH_BASE_IN_EDIT_MODE_BEGIN
@@ -263,6 +264,8 @@ ForEachMacros:
- SET_SLOT_PROBING_BEGIN
- MAP_SLOT_PROBING_BEGIN
- VECTOR_SET_SLOT_PROBING_BEGIN
- ITEM_FOREACH_BEGIN
- ITEM_FOREACH_BEGIN_NO_CULL
StatementMacros:
- PyObject_HEAD

View File

@@ -110,7 +110,21 @@ class DATA_PT_lens(CameraButtonsPanel, Panel):
sub = col.column(align=True)
sub.prop(ccam, "longitude_min", text="Longitude Min")
sub.prop(ccam, "longitude_max", text="Max")
elif engine in {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}:
elif engine == 'BLENDER_EEVEE':
col.prop(cam, "panorama_type")
if cam.panorama_type == 'FISHEYE_EQUIDISTANT':
col.prop(cam, "fisheye_fov")
elif cam.panorama_type == 'FISHEYE_EQUISOLID':
col.prop(cam, "fisheye_lens", text="Lens")
col.prop(cam, "fisheye_fov")
elif cam.panorama_type == 'EQUIRECTANGULAR':
sub = col.column(align=True)
sub.prop(cam, "latitude_min", text="Latitude Min")
sub.prop(cam, "latitude_max", text="Max")
sub = col.column(align=True)
sub.prop(cam, "longitude_min", text="Longitude Min")
sub.prop(cam, "longitude_max", text="Max")
elif engine in {'BLENDER_RENDER', 'BLENDER_WORKBENCH'}:
if cam.lens_unit == 'MILLIMETERS':
col.prop(cam, "lens")
elif cam.lens_unit == 'FOV':

View File

@@ -591,7 +591,7 @@ class GreasePencilMaterialsPanel:
if len(ob.material_slots) > 0 and ob.active_material_index >= 0:
ma = ob.material_slots[ob.active_material_index].material
if ma is not None and ma.grease_pencil is not None:
if is_view3d and ma is not None and ma.grease_pencil is not None:
gpcolor = ma.grease_pencil
if gpcolor.stroke_style == 'SOLID':
row = layout.row()

View File

@@ -63,8 +63,12 @@ class MaterialButtonsPanel:
@classmethod
def poll(cls, context):
ob = context.active_object
is_scene_render = ob and ob.use_grease_pencil_scene_engine
mat = context.material
return mat and (context.engine in cls.COMPAT_ENGINES) and not mat.grease_pencil
return mat and (((context.engine in cls.COMPAT_ENGINES) and
not mat.grease_pencil) or (mat.grease_pencil and
mat.use_nodes and is_scene_render))
class MATERIAL_PT_preview(MaterialButtonsPanel, Panel):
@@ -72,6 +76,15 @@ class MATERIAL_PT_preview(MaterialButtonsPanel, Panel):
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_EEVEE'}
@classmethod
def poll(cls, context):
ob = context.active_object
is_scene_render = ob and ob.use_grease_pencil_scene_engine
mat = context.material
return mat and (((context.engine in cls.COMPAT_ENGINES) and
not mat.grease_pencil) or (mat.grease_pencil and
mat.use_nodes and is_scene_render))
def draw(self, context):
self.layout.template_preview(context.material)

View File

@@ -120,12 +120,23 @@ class MATERIAL_PT_gpencil_slots(GreasePencilMaterialsPanel, Panel):
class MATERIAL_PT_gpencil_surface(GPMaterialButtonsPanel, Panel):
bl_label = "Surface"
@classmethod
def poll(cls, context):
mat = context.material
ob = context.active_object
is_scene_render = ob and ob.use_grease_pencil_scene_engine
return (mat and (mat.use_nodes is False or is_scene_render is False))
def draw_header_preset(self, _context):
MATERIAL_PT_gpencil_material_presets.draw_panel_header(self.layout)
def draw(self, _context):
def draw(self, context):
layout = self.layout
layout.use_property_split = True
ob = context.active_object
is_scene_render = ob and ob.use_grease_pencil_scene_engine
mat = context.material
if is_scene_render:
layout.prop(mat, "use_nodes", icon='NODETREE')
class MATERIAL_PT_gpencil_strokecolor(GPMaterialButtonsPanel, Panel):
@@ -142,7 +153,8 @@ class MATERIAL_PT_gpencil_strokecolor(GPMaterialButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
layout.use_property_split = True
ob = context.active_object
is_scene_render = ob and ob.use_grease_pencil_scene_engine
ma = context.material
if ma is not None and ma.grease_pencil is not None:
gpcolor = ma.grease_pencil
@@ -155,7 +167,8 @@ class MATERIAL_PT_gpencil_strokecolor(GPMaterialButtonsPanel, Panel):
col.prop(gpcolor, "stroke_style", text="Style")
col.prop(gpcolor, "color", text="Base Color")
col.prop(gpcolor, "use_stroke_holdout")
if not is_scene_render:
col.prop(gpcolor, "use_stroke_holdout")
if gpcolor.stroke_style == 'TEXTURE':
row = col.row()
@@ -173,7 +186,7 @@ class MATERIAL_PT_gpencil_strokecolor(GPMaterialButtonsPanel, Panel):
col.prop(gpcolor, "alignment_mode")
col.prop(gpcolor, "alignment_rotation")
if gpcolor.mode == 'LINE':
if gpcolor.mode == 'LINE' and not is_scene_render:
col.prop(gpcolor, "use_overlap_strokes")
@@ -191,6 +204,8 @@ class MATERIAL_PT_gpencil_fillcolor(GPMaterialButtonsPanel, Panel):
layout = self.layout
layout.use_property_split = True
ob = context.active_object
is_scene_render = ob and ob.use_grease_pencil_scene_engine
ma = context.material
gpcolor = ma.grease_pencil
@@ -201,14 +216,16 @@ class MATERIAL_PT_gpencil_fillcolor(GPMaterialButtonsPanel, Panel):
if gpcolor.fill_style == 'SOLID':
col.prop(gpcolor, "fill_color", text="Base Color")
col.prop(gpcolor, "use_fill_holdout")
if not is_scene_render:
col.prop(gpcolor, "use_fill_holdout")
elif gpcolor.fill_style == 'GRADIENT':
col.prop(gpcolor, "gradient_type")
col.prop(gpcolor, "fill_color", text="Base Color")
col.prop(gpcolor, "mix_color", text="Secondary Color")
col.prop(gpcolor, "use_fill_holdout")
if not is_scene_render:
col.prop(gpcolor, "use_fill_holdout")
col.prop(gpcolor, "mix_factor", text="Blend", slider=True)
col.prop(gpcolor, "flip", text="Flip Colors")
@@ -222,7 +239,8 @@ class MATERIAL_PT_gpencil_fillcolor(GPMaterialButtonsPanel, Panel):
elif gpcolor.fill_style == 'TEXTURE':
col.prop(gpcolor, "fill_color", text="Base Color")
col.prop(gpcolor, "use_fill_holdout")
if not is_scene_render:
col.prop(gpcolor, "use_fill_holdout")
col.template_ID(gpcolor, "fill_image", open="image.open")
@@ -239,6 +257,14 @@ class MATERIAL_PT_gpencil_preview(GPMaterialButtonsPanel, Panel):
COMPAT_ENGINES = {'BLENDER_EEVEE'}
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
mat = context.material
ob = context.active_object
is_scene_render = ob and ob.use_grease_pencil_scene_engine
return (mat and mat.grease_pencil and
(mat.use_nodes is False or is_scene_render is False))
def draw(self, context):
ma = context.material
self.layout.label(text=ma.name)
@@ -250,18 +276,56 @@ class MATERIAL_PT_gpencil_custom_props(GPMaterialButtonsPanel, PropertyPanel, Pa
_context_path = "object.active_material"
_property_type = bpy.types.Material
@classmethod
def poll(cls, context):
mat = context.material
ob = context.active_object
is_scene_render = ob and ob.use_grease_pencil_scene_engine
return (mat and mat.grease_pencil and
(mat.use_nodes is False or is_scene_render is False))
def draw_material_settings(self, context):
ob = context.active_object
is_scene_render = ob and ob.use_grease_pencil_scene_engine
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
mat = context.material
if is_scene_render:
layout.prop(mat, "use_backface_culling")
layout.prop(mat, "blend_method")
layout.prop(mat, "shadow_method")
row = layout.row()
row.active = ((mat.blend_method == 'CLIP') or (mat.shadow_method == 'CLIP'))
row.prop(mat, "alpha_threshold")
if mat.blend_method not in {'OPAQUE', 'CLIP', 'HASHED'}:
layout.prop(mat, "show_transparent_back")
layout.prop(mat, "use_screen_refraction")
layout.prop(mat, "refraction_depth")
layout.prop(mat, "use_sss_translucency")
layout.prop(mat, "pass_index")
class MATERIAL_PT_gpencil_settings(GPMaterialButtonsPanel, Panel):
bl_label = "Settings"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
@classmethod
def poll(cls, context):
mat = context.material
ob = context.active_object
is_scene_render = ob and ob.use_grease_pencil_scene_engine
return (mat and mat.grease_pencil and
(mat.use_nodes is False or is_scene_render is False))
ma = context.material
gpcolor = ma.grease_pencil
layout.prop(gpcolor, "pass_index")
def draw(self, context):
draw_material_settings(self, context)
class MATERIAL_PT_gpencil_material_presets(PresetPanel, Panel):

View File

@@ -405,8 +405,13 @@ class OBJECT_PT_visibility(ObjectButtonsPanel, Panel):
if context.object.type == 'GPENCIL':
col = layout.column(heading="Grease Pencil")
col.prop(ob, "use_grease_pencil_lights", toggle=False)
subcol = layout.column()
subcol.prop(ob, "use_grease_pencil_lights", toggle=False)
subcol.active = not ob.use_grease_pencil_scene_engine
subcol = layout.column()
subcol.prop(ob, "use_grease_pencil_scene_engine", toggle=False)
subcol.active = context.scene.render.engine == 'BLENDER_EEVEE'
layout.separator()
col = layout.column(heading="Mask")
col.prop(ob, "is_holdout")

View File

@@ -180,6 +180,37 @@ class RENDER_PT_eevee_motion_blur(RenderButtonsPanel, Panel):
col.prop(props, "motion_blur_max")
col.prop(props, "motion_blur_steps", text="Steps")
class RENDER_PT_eevee_motion_blur_curve(RenderButtonsPanel, Panel):
bl_label = "Shutter Curve"
bl_parent_id = "RENDER_PT_eevee_motion_blur"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_EEVEE'}
@classmethod
def poll(cls, context):
return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
scene = context.scene
rd = scene.render
props = scene.eevee
layout.active = props.use_motion_blur
col = layout.column()
col.template_curve_mapping(rd, "motion_blur_shutter_curve")
col = layout.column(align=True)
row = col.row(align=True)
row.operator("render.shutter_curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH'
row.operator("render.shutter_curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND'
row.operator("render.shutter_curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT'
row.operator("render.shutter_curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP'
row.operator("render.shutter_curve_preset", icon='LINCURVE', text="").shape = 'LINE'
row.operator("render.shutter_curve_preset", icon='NOCURVE', text="").shape = 'MAX'
class RENDER_PT_eevee_depth_of_field(RenderButtonsPanel, Panel):
bl_label = "Depth of Field"
@@ -491,6 +522,7 @@ class RENDER_PT_eevee_film(RenderButtonsPanel, Panel):
col = layout.column()
col.prop(rd, "filter_size")
col.prop(rd, "film_transparent", text="Transparent")
col.prop(props, "use_log_space")
col = layout.column(align=False, heading="Overscan")
row = col.row(align=True)
@@ -705,6 +737,7 @@ classes = (
RENDER_PT_eevee_subsurface_scattering,
RENDER_PT_eevee_screen_space_reflections,
RENDER_PT_eevee_motion_blur,
RENDER_PT_eevee_motion_blur_curve,
RENDER_PT_eevee_volumetric,
RENDER_PT_eevee_volumetric_lighting,
RENDER_PT_eevee_volumetric_shadows,

View File

@@ -76,6 +76,8 @@ class VIEWLAYER_PT_eevee_layer_passes_data(ViewLayerButtonsPanel, Panel):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
scene = context.scene
scene_eevee = scene.eevee
view_layer = context.view_layer
@@ -84,6 +86,9 @@ class VIEWLAYER_PT_eevee_layer_passes_data(ViewLayerButtonsPanel, Panel):
col.prop(view_layer, "use_pass_z")
col.prop(view_layer, "use_pass_mist")
col.prop(view_layer, "use_pass_normal")
sub = col.column()
sub.active = not scene_eevee.use_motion_blur
sub.prop(view_layer, "use_pass_vector")
class VIEWLAYER_PT_eevee_layer_passes_light(ViewLayerButtonsPanel, Panel):

View File

@@ -43,7 +43,7 @@ void *BKE_camera_add(struct Main *bmain, const char *name);
/* Camera Usage */
float BKE_camera_object_dof_distance(struct Object *ob);
float BKE_camera_object_dof_distance(const struct Object *ob);
int BKE_camera_sensor_fit(int sensor_fit, float sizex, float sizey);
float BKE_camera_sensor_size(int sensor_fit, float sensor_x, float sensor_y);

View File

@@ -1114,10 +1114,7 @@ struct bNodeTreeExec *ntreeShaderBeginExecTree(struct bNodeTree *ntree);
void ntreeShaderEndExecTree(struct bNodeTreeExec *exec);
struct bNode *ntreeShaderOutputNode(struct bNodeTree *ntree, int target);
void ntreeGPUMaterialNodes(struct bNodeTree *localtree,
struct GPUMaterial *mat,
bool *has_surface_output,
bool *has_volume_output);
void ntreeGPUMaterialNodes(struct bNodeTree *localtree, struct GPUMaterial *mat);
/** \} */

View File

@@ -33,6 +33,15 @@ struct World;
struct World *BKE_world_add(struct Main *bmain, const char *name);
void BKE_world_eval(struct Depsgraph *depsgraph, struct World *world);
struct World *BKE_world_default(void);
void BKE_world_defaults_free_gpu(void);
/* Module */
void BKE_worlds_init(void);
void BKE_worlds_exit(void);
#ifdef __cplusplus
}
#endif

View File

@@ -218,9 +218,9 @@ void *BKE_camera_add(Main *bmain, const char *name)
}
/* get the camera's dof value, takes the dof object into account */
float BKE_camera_object_dof_distance(Object *ob)
float BKE_camera_object_dof_distance(const Object *ob)
{
Camera *cam = (Camera *)ob->data;
const Camera *cam = (const Camera *)ob->data;
if (ob->type != OB_CAMERA) {
return 0.0f;
}

View File

@@ -49,6 +49,8 @@
#include "BLT_translation.h"
#include "NOD_shader.h"
#include "DRW_engine.h"
#include "DEG_depsgraph.h"
@@ -224,3 +226,57 @@ void BKE_world_eval(struct Depsgraph *depsgraph, World *world)
DEG_debug_print_eval(depsgraph, __func__, world->id.name, world);
GPU_material_free(&world->gpumaterial);
}
/* Default World
*
* Used for rendering when a scene have no world assigned. */
static World default_world;
static void world_default_init(World *ma)
{
bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
ma->nodetree = ntree;
ma->use_nodes = true;
bNode *background = nodeAddStaticNode(NULL, ntree, SH_NODE_BACKGROUND);
bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_WORLD);
bNodeSocket *background_out = nodeFindSocket(background, SOCK_OUT, "Background");
bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface");
nodeAddLink(ntree, background, background_out, output, output_in);
nodeSetActive(ntree, output);
bNodeSocketValueRGBA *color_socket_ =
(bNodeSocketValueRGBA *)nodeFindSocket(background, SOCK_IN, "Color")->default_value;
color_socket_->value[0] = 0.0f;
color_socket_->value[1] = 0.0f;
color_socket_->value[2] = 0.0f;
color_socket_->value[3] = 1.0f;
}
World *BKE_world_default(void)
{
return &default_world;
}
void BKE_world_defaults_free_gpu(void)
{
if (default_world.gpumaterial.first) {
GPU_material_free(&default_world.gpumaterial);
}
}
/* Module functions called on startup and exit. */
void BKE_worlds_init(void)
{
world_init_data(&default_world.id);
world_default_init(&default_world);
}
void BKE_worlds_exit(void)
{
world_free_data(&default_world.id);
}

View File

@@ -99,6 +99,9 @@ void _BLI_assert_unreachable_print(const char *file, const int line, const char
#define BLI_STATIC_ASSERT_ALIGN(st, align) \
BLI_STATIC_ASSERT((sizeof(st) % (align) == 0), "Structure must be strictly aligned")
#define BLI_STATIC_ASSERT_SIZE(st, max_size) \
BLI_STATIC_ASSERT(sizeof(CullingData) <= max_size, "Structure is too big")
/**
* Indicates that this line of code should never be executed. If it is reached, it will abort in
* debug builds and print an error in release builds.

View File

@@ -84,6 +84,20 @@ struct float2 {
return *this;
}
float2 &operator*=(const float2 &other)
{
x *= other.x;
y *= other.y;
return *this;
}
float2 &operator/=(const float2 &other)
{
x *= other.x;
y *= other.y;
return *this;
}
float2 &operator*=(float factor)
{
x *= factor;
@@ -115,6 +129,27 @@ struct float2 {
return {a.x - b.x, a.y - b.y};
}
friend float2 operator*(const float2 &a, const float2 &b)
{
return {a.x * b.x, a.y * b.y};
}
friend float2 operator/(const float2 &a, const float2 &b)
{
BLI_assert(b.x != 0.0f && b.y != 0.0f);
return {a.x / b.x, a.y / b.y};
}
friend float2 operator+(const float2 &a, float b)
{
return {a.x + b, a.y + b};
}
friend float2 operator-(const float2 &a, float b)
{
return {a.x - b, a.y - b};
}
friend float2 operator*(const float2 &a, float b)
{
return {a.x * b, a.y * b};
@@ -126,11 +161,27 @@ struct float2 {
return {a.x / b, a.y / b};
}
friend float2 operator+(float a, const float2 &b)
{
return {a + b.x, a + b.y};
}
friend float2 operator-(float a, const float2 &b)
{
return {a - b.x, a - b.y};
}
friend float2 operator*(float a, const float2 &b)
{
return b * a;
}
friend float2 operator/(float a, const float2 &b)
{
BLI_assert(b.x != 0.0f && b.y != 0.0f);
return {a / b.x, a / b.y};
}
friend std::ostream &operator<<(std::ostream &stream, const float2 &v)
{
stream << "(" << v.x << ", " << v.y << ")";

View File

@@ -125,6 +125,12 @@ struct float3 {
return {a.x / b, a.y / b, a.z / b};
}
friend float3 operator/(const float3 &a, const float3 &b)
{
BLI_assert(b.x != 0.0f && b.y != 0.0f && b.z != 0.0f);
return {a.x / b.x, a.y / b.y, a.z / b.z};
}
friend std::ostream &operator<<(std::ostream &stream, const float3 &v)
{
stream << "(" << v.x << ", " << v.y << ", " << v.z << ")";

View File

@@ -0,0 +1,170 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "BLI_float2.hh"
#include "BLI_int3.hh"
namespace blender {
struct int2 {
int32_t x, y;
int2() = default;
int2(const int32_t *ptr) : x{ptr[0]}, y{ptr[1]}
{
}
explicit int2(int32_t value) : x(value), y(value)
{
}
int2(int32_t x, int32_t y) : x(x), y(y)
{
}
explicit int2(const float2 &other) : x(other.x), y(other.y)
{
}
int2(const int3 &other) : x(other.x), y(other.y)
{
}
operator int32_t *()
{
return &x;
}
operator float2() const
{
return float2(x, y);
}
operator const int32_t *() const
{
return &x;
}
bool is_zero() const
{
return this->x == 0 && this->y == 0;
}
int2 &operator+=(const int2 &other)
{
x += other.x;
y += other.y;
return *this;
}
int2 &operator-=(const int2 &other)
{
x -= other.x;
y -= other.y;
return *this;
}
int2 &operator*=(int32_t factor)
{
x *= factor;
y *= factor;
return *this;
}
int2 &operator/=(int32_t divisor)
{
x /= divisor;
y /= divisor;
return *this;
}
friend int2 operator+(const int2 &a, const int2 &b)
{
return {a.x + b.x, a.y + b.y};
}
friend int2 operator-(const int2 &a, const int2 &b)
{
return {a.x - b.x, a.y - b.y};
}
friend int2 operator*(const int2 &a, int32_t b)
{
return {a.x * b, a.y * b};
}
friend int2 operator/(const int2 &a, int32_t b)
{
BLI_assert(b != 0);
return {a.x / b, a.y / b};
}
friend int2 operator*(int32_t a, const int2 &b)
{
return b * a;
}
friend float2 operator*(const int2 &a, float b)
{
return b * float2(a.x, a.y);
}
friend float2 operator/(const int2 &a, float b)
{
return float2(a.x, a.y) / b;
}
friend float2 operator*(float a, const int2 &b)
{
return a * float2(b.x, b.y);
}
friend float2 operator/(float a, const int2 &b)
{
return a / float2(b.x, b.y);
}
friend std::ostream &operator<<(std::ostream &stream, const int2 &v)
{
stream << "(" << v.x << ", " << v.y << ")";
return stream;
}
static int2 clamp(const int2 &a, const int2 &min, const int2 &max)
{
return int2(clamp_i(a.x, min.x, max.x), clamp_i(a.y, min.y, max.y));
}
static int2 clamp(const int2 &a, const int32_t &min, const int32_t &max)
{
return int2(clamp_i(a.x, min, max), clamp_i(a.y, min, max));
}
friend bool operator==(const int2 &a, const int2 &b)
{
return a.x == b.x && a.y == b.y;
}
friend bool operator!=(const int2 &a, const int2 &b)
{
return !(a == b);
}
};
} // namespace blender

View File

@@ -0,0 +1,156 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include <iostream>
#include "BLI_float3.hh"
namespace blender {
struct int3 {
int32_t x, y, z;
int3() = default;
int3(const int *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]}
{
}
explicit int3(int value) : x(value), y(value), z(value)
{
}
int3(int x, int y, int z) : x(x), y(y), z(z)
{
}
int3(const int3 &other) : x(other.x), y(other.y), z(other.z)
{
}
explicit int3(const float3 &other) : x(other.x), y(other.y), z(other.z)
{
}
operator int *()
{
return &x;
}
operator float3() const
{
return float3(x, y, z);
}
operator const int *() const
{
return &x;
}
bool is_zero() const
{
return this->x == 0 && this->y == 0 && this->z == 0;
}
int3 &operator+=(const int3 &other)
{
x += other.x;
y += other.y;
z += other.z;
return *this;
}
int3 &operator-=(const int3 &other)
{
x -= other.x;
y -= other.y;
z -= other.z;
return *this;
}
int3 &operator*=(int factor)
{
x *= factor;
y *= factor;
z *= factor;
return *this;
}
int3 &operator/=(int divisor)
{
x /= divisor;
y /= divisor;
z /= divisor;
return *this;
}
friend int3 operator+(const int3 &a, const int3 &b)
{
return {a.x + b.x, a.y + b.y, a.z + b.z};
}
friend int3 operator-(const int3 &a, const int3 &b)
{
return {a.x - b.x, a.y - b.y, a.z - b.z};
}
friend int3 operator*(const int3 &a, int b)
{
return {a.x * b, a.y * b, a.z * b};
}
friend int3 operator/(const int3 &a, int b)
{
BLI_assert(b != 0);
return {a.x / b, a.y / b, a.z / b};
}
friend int3 operator*(int a, const int3 &b)
{
return b * a;
}
friend std::ostream &operator<<(std::ostream &stream, const int3 &v)
{
stream << "(" << v.x << ", " << v.y << ", " << v.z << ")";
return stream;
}
static int3 clamp(const int3 &a, const int3 &min, const int3 &max)
{
return int3(
clamp_i(a.x, min.x, max.x), clamp_i(a.y, min.y, max.y), clamp_i(a.z, min.z, max.z));
}
static int3 clamp(const int3 &a, const int32_t &min, const int32_t &max)
{
return int3(clamp_i(a.x, min, max), clamp_i(a.y, min, max), clamp_i(a.z, min, max));
}
friend bool operator==(const int3 &a, const int3 &b)
{
return a.x == b.x && a.y == b.y;
}
friend bool operator!=(const int3 &a, const int3 &b)
{
return !(a == b);
}
};
} // namespace blender

View File

@@ -672,7 +672,10 @@ void orthographic_m4(float mat[4][4],
const float top,
const float nearClip,
const float farClip);
void window_translate_m4(float winmat[4][4], float perspmat[4][4], const float x, const float y);
void window_translate_m4(float winmat[4][4],
const float perspmat[4][4],
const float x,
const float y);
void planes_from_projmat(const float mat[4][4],
float left[4],

View File

@@ -4875,7 +4875,7 @@ void perspective_m4_fov(float mat[4][4],
/* translate a matrix created by orthographic_m4 or perspective_m4 in XY coords
* (used to jitter the view) */
void window_translate_m4(float winmat[4][4], float perspmat[4][4], const float x, const float y)
void window_translate_m4(float winmat[4][4], const float perspmat[4][4], const float x, const float y)
{
if (winmat[2][3] == -1.0f) {
/* in the case of a win-matrix, this means perspective always */

View File

@@ -702,6 +702,12 @@ MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
r[2] += a[2] * f;
}
MINLINE void madd_v2_v2v2(float r[2], const float a[2], const float b[2])
{
r[0] += a[0] * b[0];
r[1] += a[1] * b[1];
}
MINLINE void madd_v3_v3v3(float r[3], const float a[3], const float b[3])
{
r[0] += a[0] * b[0];

View File

@@ -30,6 +30,7 @@
#include "DNA_armature_types.h"
#include "DNA_brush_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_camera_types.h"
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
#include "DNA_fluid_types.h"
@@ -2061,5 +2062,22 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
*/
{
/* Keep this block, even when empty. */
if (!DNA_struct_elem_find(fd->filesdna, "Camera", "float", "fisheye_fov")) {
/* EEVEE Panoramic Camera support. */
LISTBASE_FOREACH (Camera *, camera, &bmain->cameras) {
camera->panorama_type = CAM_PANO_FISHEYE_EQUISOLID;
camera->fisheye_fov = DEG2RADF(180.0f);
camera->fisheye_lens = 10.5f;
camera->latitude_min = DEG2RADF(-90.0f);
camera->latitude_max = DEG2RADF(90.0f);
camera->longitude_min = DEG2RADF(-180.0f);
camera->longitude_max = DEG2RADF(180.0f);
}
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
scene->eevee.flag |= SCE_EEVEE_FILM_LOG_ENCODING;
}
}
}
}

View File

@@ -1682,5 +1682,14 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
*/
{
/* Keep this block, even when empty. */
/* Fix grease pencil material index. */
{
LISTBASE_FOREACH (Material *, mat, &bmain->materials) {
if (mat->gp_style != NULL) {
mat->index = mat->gp_style->index;
}
}
}
}
}

View File

@@ -94,6 +94,7 @@ set(SRC
intern/draw_color_management.cc
intern/draw_common.c
intern/draw_debug.c
intern/draw_view_data.cc
intern/draw_fluid.c
intern/draw_hair.c
intern/draw_instance_data.c
@@ -105,38 +106,37 @@ set(SRC
intern/draw_manager_text.c
intern/draw_manager_texture.c
intern/draw_select_buffer.c
intern/draw_texture_pool.cc
intern/draw_shader.c
intern/draw_view.c
engines/basic/basic_engine.c
engines/image/image_engine.c
engines/image/image_shader.c
engines/eevee/eevee_bloom.c
engines/eevee/eevee_cryptomatte.c
engines/eevee/eevee_data.c
engines/eevee/eevee_depth_of_field.c
engines/eevee/eevee_effects.c
engines/eevee/eevee_camera.cc
engines/eevee/eevee_culling.cc
engines/eevee/eevee_depth_of_field.cc
engines/eevee/eevee_engine.c
engines/eevee/eevee_lightcache.c
engines/eevee/eevee_lightprobes.c
engines/eevee/eevee_lights.c
engines/eevee/eevee_lookdev.c
engines/eevee/eevee_engine.cc
engines/eevee/eevee_film.cc
engines/eevee/eevee_gpencil.cc
engines/eevee/eevee_hair.cc
engines/eevee/eevee_id_map.cc
engines/eevee/eevee_instance.cc
engines/eevee/eevee_light.cc
engines/eevee/eevee_lightcache.cc
engines/eevee/eevee_lightprobe.cc
engines/eevee/eevee_lookdev.cc
engines/eevee/eevee_lut.c
engines/eevee/eevee_lut_gen.c
engines/eevee/eevee_materials.c
engines/eevee/eevee_mist.c
engines/eevee/eevee_motion_blur.c
engines/eevee/eevee_occlusion.c
engines/eevee/eevee_render.c
engines/eevee/eevee_renderpasses.c
engines/eevee/eevee_sampling.c
engines/eevee/eevee_screen_raytrace.c
engines/eevee/eevee_shaders.c
engines/eevee/eevee_shadows.c
engines/eevee/eevee_shadows_cascade.c
engines/eevee/eevee_shadows_cube.c
engines/eevee/eevee_subsurface.c
engines/eevee/eevee_temporal_sampling.c
engines/eevee/eevee_volumes.c
engines/eevee/eevee_material.cc
engines/eevee/eevee_mesh.cc
engines/eevee/eevee_motion_blur.cc
engines/eevee/eevee_renderpasses.cc
engines/eevee/eevee_shader.cc
engines/eevee/eevee_shading.cc
engines/eevee/eevee_shadow.cc
engines/eevee/eevee_velocity.cc
engines/eevee/eevee_view.cc
engines/eevee/eevee_world.cc
engines/workbench/workbench_data.c
engines/workbench/workbench_effect_antialiasing.c
engines/workbench/workbench_effect_cavity.c
@@ -188,7 +188,6 @@ set(SRC
engines/overlay/overlay_wireframe.c
DRW_engine.h
DRW_engine_types.h
DRW_select_buffer.h
intern/DRW_render.h
intern/draw_cache.h
@@ -198,12 +197,14 @@ set(SRC
intern/draw_color_management.h
intern/draw_common.h
intern/draw_debug.h
intern/draw_view_data.h
intern/draw_hair_private.h
intern/draw_instance_data.h
intern/draw_manager.h
intern/draw_manager_profiling.h
intern/draw_manager_testing.h
intern/draw_manager_text.h
intern/draw_texture_pool.h
intern/draw_shader.h
intern/draw_view.h
intern/mesh_extractors/extract_mesh.h
@@ -211,6 +212,10 @@ set(SRC
engines/basic/basic_engine.h
engines/eevee/eevee_engine.h
engines/eevee/eevee_lightcache.h
engines/eevee/eevee_camera.hh
engines/eevee/eevee_instance.hh
engines/eevee/eevee_light.hh
engines/eevee/eevee_depth_of_field.hh
engines/eevee/eevee_lut.h
engines/eevee/eevee_private.h
engines/external/external_engine.h
@@ -230,95 +235,92 @@ set(LIB
bf_windowmanager
)
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/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)
data_to_c_simple(engines/eevee/shaders/lightprobe_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_geom.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_cube_display_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_cube_display_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_grid_display_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_grid_display_vert.glsl SRC)
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/effect_bloom_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_reflection_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_reflection_resolve_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_reflection_trace_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)
data_to_c_simple(engines/eevee/shaders/effect_velocity_resolve_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_velocity_tile_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_minmaxz_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_mist_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_motion_blur_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_subsurface_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_translucency_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/effect_temporal_aa.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_planar_downsample_geom.glsl SRC)
data_to_c_simple(engines/eevee/shaders/lightprobe_planar_downsample_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/object_motion_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/object_motion_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/prepass_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/prepass_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/shadow_accum_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/shadow_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/shadow_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/bsdf_lut_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/btdf_lut_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/bsdf_common_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/irradiance_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/octahedron_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/cubemap_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/bsdf_sampling_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/random_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/raytrace_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/renderpass_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/renderpass_postprocess_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/cryptomatte_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/ltc_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/ssr_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/surface_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/surface_geom.glsl SRC)
data_to_c_simple(engines/eevee/shaders/surface_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/surface_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/update_noise_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/volumetric_accum_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/volumetric_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/volumetric_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/volumetric_geom.glsl SRC)
data_to_c_simple(engines/eevee/shaders/volumetric_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/volumetric_resolve_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/volumetric_scatter_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/volumetric_integration_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_bsdf_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_bsdf_microfacet_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_bsdf_sampling_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_bsdf_stubs_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_camera_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_camera_velocity_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_closure_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_cubemap_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_culling_debug_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_culling_iter_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_culling_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_culling_light_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_deferred_direct_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_deferred_holdout_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_deferred_transparent_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_deferred_volume_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_depth_clear_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_depth_of_field_accumulator_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_depth_of_field_bokeh_lut_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_depth_of_field_filter_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_depth_of_field_gather_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_depth_of_field_gather_holefill_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_depth_of_field_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_depth_of_field_reduce_copy_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_depth_of_field_reduce_downsample_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_depth_of_field_reduce_recursive_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_depth_of_field_resolve_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_depth_of_field_scatter_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_depth_of_field_scatter_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_depth_of_field_scatter_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_depth_of_field_setup_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_depth_of_field_tiles_dilate_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_depth_of_field_tiles_flatten_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_film_filter_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_film_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_film_resolve_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_film_resolve_depth_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_gbuffer_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_irradiance_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_light_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_light_eval_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_lightprobe_display_cubemap_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_lightprobe_display_cubemap_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_lightprobe_display_grid_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_lightprobe_display_grid_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_lightprobe_display_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_lightprobe_eval_cubemap_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_lightprobe_eval_grid_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_lightprobe_filter_diffuse_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_lightprobe_filter_downsample_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_lightprobe_filter_geom.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_lightprobe_filter_glossy_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_lightprobe_filter_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_lightprobe_filter_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_lightprobe_filter_visibility_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_lookdev_background_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_ltc_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_motion_blur_gather_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_motion_blur_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_motion_blur_tiles_dilate_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_motion_blur_tiles_flatten_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_nodetree_eval_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_sampling_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_shadow_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_surface_background_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_surface_deferred_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_surface_depth_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_surface_depth_simple_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_surface_forward_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_surface_gpencil_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_surface_hair_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_surface_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_surface_lookdev_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_surface_mesh_geom.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_surface_mesh_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_surface_velocity_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_surface_velocity_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_surface_velocity_mesh_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_surface_world_vert.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_velocity_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_volume_deferred_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_volume_eval_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_volume_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/eevee_volume_vert.glsl SRC)
data_to_c_simple(engines/eevee/eevee_shader_shared.hh SRC)
data_to_c_simple(engines/workbench/shaders/workbench_cavity_lib.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_common_lib.glsl SRC)
@@ -350,14 +352,18 @@ data_to_c_simple(engines/workbench/shaders/workbench_volume_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_volume_vert.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_world_light_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_attribute_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_colormanagement_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_globals_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_gpencil_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_pointcloud_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_hair_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_hair_refine_vert.glsl SRC)
data_to_c_simple(intern/shaders/common_hair_refine_comp.glsl SRC)
data_to_c_simple(intern/shaders/common_math_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_math_geom_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_obinfos_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_uniform_attribute_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_view_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_fxaa_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_smaa_lib.glsl SRC)

View File

@@ -26,13 +26,12 @@
#include "DNA_object_enums.h"
#include "DRW_engine_types.h"
#ifdef __cplusplus
extern "C" {
#endif
struct ARegion;
struct DRWData;
struct DRWInstanceDataList;
struct Depsgraph;
struct DrawEngineType;
@@ -57,8 +56,6 @@ void DRW_engines_free(void);
bool DRW_engine_render_support(struct DrawEngineType *draw_engine_type);
void DRW_engine_register(struct DrawEngineType *draw_engine_type);
void DRW_engine_viewport_data_size_get(
const void *engine_type, int *r_fbl_len, int *r_txl_len, int *r_psl_len, int *r_stl_len);
typedef struct DRWUpdateContext {
struct Main *bmain;
@@ -176,6 +173,9 @@ void DRW_deferred_shader_remove(struct GPUMaterial *mat);
struct DrawDataList *DRW_drawdatalist_from_id(struct ID *id);
void DRW_drawdata_free(struct ID *id);
struct DRWData *DRW_viewport_data_create(void);
void DRW_viewport_data_free(struct DRWData *drw_data);
bool DRW_opengl_context_release(void);
void DRW_opengl_context_activate(bool drw_state);

View File

@@ -296,6 +296,7 @@ DrawEngineType draw_engine_basic_type = {
&basic_data_size,
&basic_engine_init,
&basic_engine_free,
NULL,
&basic_cache_init,
&basic_cache_populate,
&basic_cache_finish,

View File

@@ -1,344 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Eevee's bloom shader.
*/
#include "DRW_render.h"
#include "GPU_texture.h"
#include "DEG_depsgraph_query.h"
#include "eevee_private.h"
static const bool use_highres = true;
int EEVEE_bloom_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_EffectsInfo *effects = stl->effects;
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
if (scene_eval->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) {
const float *viewport_size = DRW_viewport_size_get();
/* Bloom */
int blitsize[2], texsize[2];
/* Blit Buffer */
effects->source_texel_size[0] = 1.0f / viewport_size[0];
effects->source_texel_size[1] = 1.0f / viewport_size[1];
blitsize[0] = (int)viewport_size[0];
blitsize[1] = (int)viewport_size[1];
effects->blit_texel_size[0] = 1.0f / (float)blitsize[0];
effects->blit_texel_size[1] = 1.0f / (float)blitsize[1];
effects->bloom_blit = DRW_texture_pool_query_2d(
blitsize[0], blitsize[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type);
GPU_framebuffer_ensure_config(
&fbl->bloom_blit_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->bloom_blit)});
/* Parameters */
const float threshold = scene_eval->eevee.bloom_threshold;
const float knee = scene_eval->eevee.bloom_knee;
const float intensity = scene_eval->eevee.bloom_intensity;
const float *color = scene_eval->eevee.bloom_color;
const float radius = scene_eval->eevee.bloom_radius;
effects->bloom_clamp = scene_eval->eevee.bloom_clamp;
/* determine the iteration count */
const float minDim = (float)MIN2(blitsize[0], blitsize[1]);
const float maxIter = (radius - 8.0f) + log(minDim) / log(2);
const int maxIterInt = effects->bloom_iteration_len = (int)maxIter;
CLAMP(effects->bloom_iteration_len, 1, MAX_BLOOM_STEP);
effects->bloom_sample_scale = 0.5f + maxIter - (float)maxIterInt;
effects->bloom_curve_threshold[0] = threshold - knee;
effects->bloom_curve_threshold[1] = knee * 2.0f;
effects->bloom_curve_threshold[2] = 0.25f / max_ff(1e-5f, knee);
effects->bloom_curve_threshold[3] = threshold;
mul_v3_v3fl(effects->bloom_color, color, intensity);
/* Downsample buffers */
copy_v2_v2_int(texsize, blitsize);
for (int i = 0; i < effects->bloom_iteration_len; i++) {
texsize[0] /= 2;
texsize[1] /= 2;
texsize[0] = MAX2(texsize[0], 2);
texsize[1] = MAX2(texsize[1], 2);
effects->downsamp_texel_size[i][0] = 1.0f / (float)texsize[0];
effects->downsamp_texel_size[i][1] = 1.0f / (float)texsize[1];
effects->bloom_downsample[i] = DRW_texture_pool_query_2d(
texsize[0], texsize[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type);
GPU_framebuffer_ensure_config(
&fbl->bloom_down_fb[i],
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->bloom_downsample[i])});
}
/* Upsample buffers */
copy_v2_v2_int(texsize, blitsize);
for (int i = 0; i < effects->bloom_iteration_len - 1; i++) {
texsize[0] /= 2;
texsize[1] /= 2;
texsize[0] = MAX2(texsize[0], 2);
texsize[1] = MAX2(texsize[1], 2);
effects->bloom_upsample[i] = DRW_texture_pool_query_2d(
texsize[0], texsize[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type);
GPU_framebuffer_ensure_config(
&fbl->bloom_accum_fb[i],
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->bloom_upsample[i])});
}
return EFFECT_BLOOM | EFFECT_POST_BUFFER;
}
/* Cleanup to release memory */
GPU_FRAMEBUFFER_FREE_SAFE(fbl->bloom_blit_fb);
for (int i = 0; i < MAX_BLOOM_STEP - 1; i++) {
GPU_FRAMEBUFFER_FREE_SAFE(fbl->bloom_down_fb[i]);
GPU_FRAMEBUFFER_FREE_SAFE(fbl->bloom_accum_fb[i]);
}
return 0;
}
static DRWShadingGroup *eevee_create_bloom_pass(const char *name,
EEVEE_EffectsInfo *effects,
struct GPUShader *sh,
DRWPass **pass,
bool upsample,
bool resolve)
{
struct GPUBatch *quad = DRW_cache_fullscreen_quad_get();
*pass = DRW_pass_create(name, DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(sh, *pass);
DRW_shgroup_call(grp, quad, NULL);
DRW_shgroup_uniform_texture_ref(grp, "sourceBuffer", &effects->unf_source_buffer);
DRW_shgroup_uniform_vec2(grp, "sourceBufferTexelSize", effects->unf_source_texel_size, 1);
if (upsample) {
DRW_shgroup_uniform_texture_ref(grp, "baseBuffer", &effects->unf_base_buffer);
DRW_shgroup_uniform_float(grp, "sampleScale", &effects->bloom_sample_scale, 1);
}
if (resolve) {
DRW_shgroup_uniform_vec3(grp, "bloomColor", effects->bloom_color, 1);
DRW_shgroup_uniform_bool_copy(grp, "bloomAddBase", true);
}
return grp;
}
void EEVEE_bloom_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
psl->bloom_accum_ps = NULL;
if ((effects->enabled_effects & EFFECT_BLOOM) != 0) {
/**
* Bloom Algorithm
*
* Overview:
* - Down-sample the color buffer doing a small blur during each step.
* - Accumulate bloom color using previously down-sampled color buffers
* and do an up-sample blur for each new accumulated layer.
* - Finally add accumulation buffer onto the source color buffer.
*
* [1/1] is original copy resolution (can be half or quarter res for performance)
* <pre>
* [DOWNSAMPLE CHAIN] [UPSAMPLE CHAIN]
*
* Source Color ─ [Blit] ─> Bright Color Extract [1/1] Final Color
* | Λ
* [Downsample First] Source Color ─> + [Resolve]
* v |
* Color Downsampled [1/2] ────────────> + Accumulation Buffer [1/2]
* | Λ
* ─── ───
* Repeat Repeat
* ─── ───
* v |
* Color Downsampled [1/N-1] ──────────> + Accumulation Buffer [1/N-1]
* | Λ
* [Downsample] [Upsample]
* v |
* Color Downsampled [1/N] ─────────────────────────┘
* </pre>
*/
DRWShadingGroup *grp;
const bool use_antiflicker = true;
eevee_create_bloom_pass("Bloom Downsample First",
effects,
EEVEE_shaders_bloom_downsample_get(use_antiflicker),
&psl->bloom_downsample_first,
false,
false);
eevee_create_bloom_pass("Bloom Downsample",
effects,
EEVEE_shaders_bloom_downsample_get(false),
&psl->bloom_downsample,
false,
false);
eevee_create_bloom_pass("Bloom Upsample",
effects,
EEVEE_shaders_bloom_upsample_get(use_highres),
&psl->bloom_upsample,
true,
false);
grp = eevee_create_bloom_pass("Bloom Blit",
effects,
EEVEE_shaders_bloom_blit_get(use_antiflicker),
&psl->bloom_blit,
false,
false);
DRW_shgroup_uniform_vec4(grp, "curveThreshold", effects->bloom_curve_threshold, 1);
DRW_shgroup_uniform_float(grp, "clampIntensity", &effects->bloom_clamp, 1);
grp = eevee_create_bloom_pass("Bloom Resolve",
effects,
EEVEE_shaders_bloom_resolve_get(use_highres),
&psl->bloom_resolve,
true,
true);
}
}
void EEVEE_bloom_draw(EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
/* Bloom */
if ((effects->enabled_effects & EFFECT_BLOOM) != 0) {
struct GPUTexture *last;
/* Extract bright pixels */
copy_v2_v2(effects->unf_source_texel_size, effects->source_texel_size);
effects->unf_source_buffer = effects->source_buffer;
GPU_framebuffer_bind(fbl->bloom_blit_fb);
DRW_draw_pass(psl->bloom_blit);
/* Downsample */
copy_v2_v2(effects->unf_source_texel_size, effects->blit_texel_size);
effects->unf_source_buffer = effects->bloom_blit;
GPU_framebuffer_bind(fbl->bloom_down_fb[0]);
DRW_draw_pass(psl->bloom_downsample_first);
last = effects->bloom_downsample[0];
for (int i = 1; i < effects->bloom_iteration_len; i++) {
copy_v2_v2(effects->unf_source_texel_size, effects->downsamp_texel_size[i - 1]);
effects->unf_source_buffer = last;
GPU_framebuffer_bind(fbl->bloom_down_fb[i]);
DRW_draw_pass(psl->bloom_downsample);
/* Used in next loop */
last = effects->bloom_downsample[i];
}
/* Upsample and accumulate */
for (int i = effects->bloom_iteration_len - 2; i >= 0; i--) {
copy_v2_v2(effects->unf_source_texel_size, effects->downsamp_texel_size[i]);
effects->unf_source_buffer = last;
effects->unf_base_buffer = effects->bloom_downsample[i];
GPU_framebuffer_bind(fbl->bloom_accum_fb[i]);
DRW_draw_pass(psl->bloom_upsample);
last = effects->bloom_upsample[i];
}
/* Resolve */
copy_v2_v2(effects->unf_source_texel_size, effects->downsamp_texel_size[0]);
effects->unf_source_buffer = last;
effects->unf_base_buffer = effects->source_buffer;
GPU_framebuffer_bind(effects->target_buffer);
DRW_draw_pass(psl->bloom_resolve);
SWAP_BUFFERS();
}
}
void EEVEE_bloom_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
EEVEE_Data *vedata,
uint UNUSED(tot_samples))
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
/* Create FrameBuffer. */
DRW_texture_ensure_fullscreen_2d(&txl->bloom_accum, GPU_R11F_G11F_B10F, 0);
GPU_framebuffer_ensure_config(&fbl->bloom_pass_accum_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->bloom_accum)});
/* Create Pass and shgroup. */
DRWShadingGroup *grp = eevee_create_bloom_pass("Bloom Accumulate",
effects,
EEVEE_shaders_bloom_resolve_get(use_highres),
&psl->bloom_accum_ps,
true,
true);
DRW_shgroup_uniform_bool_copy(grp, "bloomAddBase", false);
}
void EEVEE_bloom_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
if (stl->g_data->render_passes & EEVEE_RENDER_PASS_BLOOM) {
GPU_framebuffer_bind(fbl->bloom_pass_accum_fb);
DRW_draw_pass(psl->bloom_accum_ps);
/* Restore */
GPU_framebuffer_bind(fbl->main_fb);
}
}

View File

@@ -0,0 +1,167 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*/
#include <array>
#include "DRW_render.h"
#include "DNA_camera_types.h"
#include "DNA_view3d_types.h"
#include "BKE_camera.h"
#include "DEG_depsgraph_query.h"
#include "RE_pipeline.h"
#include "eevee_camera.hh"
#include "eevee_instance.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name eCameraType
* \{ */
static eCameraType from_camera(const ::Camera *camera)
{
switch (camera->type) {
default:
case CAM_PERSP:
return CAMERA_PERSP;
case CAM_ORTHO:
return CAMERA_ORTHO;
case CAM_PANO:
switch (camera->panorama_type) {
default:
case CAM_PANO_EQUIRECTANGULAR:
return CAMERA_PANO_EQUIRECT;
case CAM_PANO_FISHEYE_EQUIDISTANT:
return CAMERA_PANO_EQUIDISTANT;
case CAM_PANO_FISHEYE_EQUISOLID:
return CAMERA_PANO_EQUISOLID;
case CAM_PANO_MIRRORBALL:
return CAMERA_PANO_MIRROR;
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Camera
* \{ */
void Camera::init(void)
{
const Object *camera_eval = inst_.camera_eval_object;
synced_ = false;
/* Swap! */
data_id_ = !data_id_;
CameraData &data = data_[data_id_];
if (camera_eval) {
const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data);
data.type = from_camera(cam);
}
else if (inst_.drw_view) {
data.type = DRW_view_is_persp_get(inst_.drw_view) ? CAMERA_PERSP : CAMERA_ORTHO;
}
else {
/* Lightprobe baking. */
data.type = CAMERA_PERSP;
}
}
void Camera::sync(void)
{
const Object *camera_eval = inst_.camera_eval_object;
CameraData &data = data_[data_id_];
data.filter_size = inst_.scene->r.gauss;
if (inst_.drw_view) {
DRW_view_viewmat_get(inst_.drw_view, data.viewmat, false);
DRW_view_viewmat_get(inst_.drw_view, data.viewinv, true);
DRW_view_winmat_get(inst_.drw_view, data.winmat, false);
DRW_view_winmat_get(inst_.drw_view, data.wininv, true);
DRW_view_persmat_get(inst_.drw_view, data.persmat, false);
DRW_view_persmat_get(inst_.drw_view, data.persinv, true);
DRW_view_camtexco_get(inst_.drw_view, data.uv_scale);
}
else if (inst_.render) {
/* TODO(fclem) Overscan */
// RE_GetCameraWindowWithOverscan(inst_.render->re, g_data->overscan, data.winmat);
RE_GetCameraWindow(inst_.render->re, camera_eval, data.winmat);
RE_GetCameraModelMatrix(inst_.render->re, camera_eval, data.viewinv);
invert_m4_m4(data.viewmat, data.viewinv);
invert_m4_m4(data.wininv, data.winmat);
mul_m4_m4m4(data.persmat, data.winmat, data.viewmat);
invert_m4_m4(data.persinv, data.persmat);
data.uv_scale = vec2(1.0f);
data.uv_bias = vec2(0.0f);
}
else {
unit_m4(data.viewmat);
unit_m4(data.viewinv);
perspective_m4(data.winmat, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 1.0f);
invert_m4_m4(data.wininv, data.winmat);
mul_m4_m4m4(data.persmat, data.winmat, data.viewmat);
invert_m4_m4(data.persinv, data.persmat);
}
if (camera_eval) {
const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data);
data.clip_near = cam->clip_start;
data.clip_far = cam->clip_end;
data.fisheye_fov = cam->fisheye_fov;
data.fisheye_lens = cam->fisheye_lens;
data.equirect_bias.x = -cam->longitude_min + M_PI_2;
data.equirect_bias.y = -cam->latitude_min + M_PI_2;
data.equirect_scale.x = cam->longitude_min - cam->longitude_max;
data.equirect_scale.y = cam->latitude_min - cam->latitude_max;
/* Combine with uv_scale/bias to avoid doing extra computation. */
data.equirect_bias += data.uv_bias * data.equirect_scale;
data.equirect_scale *= data.uv_scale;
data.equirect_scale_inv = 1.0f / data.equirect_scale;
}
else if (inst_.drw_view) {
data.clip_near = DRW_view_near_distance_get(inst_.drw_view);
data.clip_far = DRW_view_far_distance_get(inst_.drw_view);
data.fisheye_fov = data.fisheye_lens = -1.0f;
data.equirect_bias = vec2(0.0f);
data.equirect_scale = vec2(0.0f);
}
data_[data_id_].push_update();
synced_ = true;
/* Detect changes in parameters. */
if (data_[data_id_] != data_[!data_id_]) {
inst_.sampling.reset();
}
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,135 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*/
#pragma once
#include <array>
#include "DNA_object_types.h"
#include "eevee_sampling.hh"
#include "eevee_shader_shared.hh"
namespace blender::eevee {
class Instance;
/* TODO(fclem) Might want to move to eevee_shader_shared.hh. */
static const float cubeface_mat[6][4][4] = {
/* Pos X */
{{0.0f, 0.0f, -1.0f, 0.0f},
{0.0f, -1.0f, 0.0f, 0.0f},
{-1.0f, 0.0f, 0.0f, 0.0f},
{0.0f, 0.0f, 0.0f, 1.0f}},
/* Neg X */
{{0.0f, 0.0f, 1.0f, 0.0f},
{0.0f, -1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f, 0.0f},
{0.0f, 0.0f, 0.0f, 1.0f}},
/* Pos Y */
{{1.0f, 0.0f, 0.0f, 0.0f},
{0.0f, 0.0f, -1.0f, 0.0f},
{0.0f, 1.0f, 0.0f, 0.0f},
{0.0f, 0.0f, 0.0f, 1.0f}},
/* Neg Y */
{{1.0f, 0.0f, 0.0f, 0.0f},
{0.0f, 0.0f, 1.0f, 0.0f},
{0.0f, -1.0f, 0.0f, 0.0f},
{0.0f, 0.0f, 0.0f, 1.0f}},
/* Pos Z */
{{1.0f, 0.0f, 0.0f, 0.0f},
{0.0f, -1.0f, 0.0f, 0.0f},
{0.0f, 0.0f, -1.0f, 0.0f},
{0.0f, 0.0f, 0.0f, 1.0f}},
/* Neg Z */
{{-1.0f, 0.0f, 0.0f, 0.0f},
{0.0f, -1.0f, 0.0f, 0.0f},
{0.0f, 0.0f, 1.0f, 0.0f},
{0.0f, 0.0f, 0.0f, 1.0f}},
};
/* -------------------------------------------------------------------- */
/** \name CameraData operators
* \{ */
inline bool operator==(const CameraData &a, const CameraData &b)
{
return compare_m4m4(a.persmat, b.persmat, FLT_MIN) && (a.uv_scale == b.uv_scale) &&
(a.uv_bias == b.uv_bias) && (a.equirect_scale == b.equirect_scale) &&
(a.equirect_bias == b.equirect_bias) && (a.fisheye_fov == b.fisheye_fov) &&
(a.fisheye_lens == b.fisheye_lens) && (a.filter_size == b.filter_size) &&
(a.type == b.type);
}
inline bool operator!=(const CameraData &a, const CameraData &b)
{
return !(a == b);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Camera
* \{ */
/**
* Point of view in the scene. Can be init from viewport or camera object.
*/
class Camera {
private:
Instance &inst_;
/** Double buffered to detect changes and have history for re-projection. */
CameraDataBuf data_[2];
/** Active data index in data_. */
int data_id_ = 0;
/** Detects wrong usage. */
bool synced_ = false;
public:
Camera(Instance &inst) : inst_(inst){};
~Camera(){};
void init(void);
void sync(void);
/**
* Getters
**/
const CameraData &data_get(void) const
{
BLI_assert(synced_);
return data_[data_id_];
}
const GPUUniformBuf *ubo_get(void) const
{
return data_[data_id_].ubo_get();
}
bool is_panoramic(void) const
{
return eevee::is_panoramic(data_[data_id_].type);
}
};
/** \} */
} // namespace blender::eevee

View File

@@ -1,720 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2020, Blender Foundation.
*/
/** \file
* \ingroup EEVEE
*
* This file implements Cryptomatte for EEVEE. Cryptomatte is used to extract mattes using
* information already available at render time. See
* https://raw.githubusercontent.com/Psyop/Cryptomatte/master/specification/IDmattes_poster.pdf
* for reference to the cryptomatte specification.
*
* The challenge with cryptomatte in EEVEE is the merging and sorting of the samples.
* User can enable up to 3 cryptomatte layers (Object, Material and Asset).
*
* Process
*
* - Cryptomatte sample: Rendering of a cryptomatte sample is stored in a GPUBuffer. The buffer
* holds a single float per pixel per number of active cryptomatte layers. The float is the
* cryptomatte hash of each layer. After drawing the cryptomatte sample the intermediate result is
* downloaded to a CPU buffer (`cryptomatte_download_buffer`).
*
* Accurate mode
*
* There are two accuracy modes. The difference between the two is the number of render samples
* they take into account to create the render passes. When accurate mode is off the number of
* levels is used as the number of cryptomatte samples to take. When accuracy mode is on the number
* of render samples is used.
*
*/
#include "DRW_engine.h"
#include "DRW_render.h"
#include "BKE_cryptomatte.h"
#include "GPU_batch.h"
#include "RE_pipeline.h"
#include "BLI_alloca.h"
#include "BLI_math_bits.h"
#include "BLI_rect.h"
#include "DNA_hair_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_particle_types.h"
#include "eevee_private.h"
/* -------------------------------------------------------------------- */
/** \name Data Management cryptomatte accum buffer
* \{ */
BLI_INLINE eViewLayerCryptomatteFlags eevee_cryptomatte_active_layers(const ViewLayer *view_layer)
{
const eViewLayerCryptomatteFlags cryptomatte_layers = view_layer->cryptomatte_flag &
VIEW_LAYER_CRYPTOMATTE_ALL;
return cryptomatte_layers;
}
/* The number of cryptomatte layers that are enabled */
BLI_INLINE int eevee_cryptomatte_layers_count(const ViewLayer *view_layer)
{
const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers(
view_layer);
return count_bits_i(cryptomatte_layers);
}
/* The number of render result passes are needed to store a single cryptomatte layer. Per
* renderpass 2 cryptomatte samples can be stored. */
BLI_INLINE int eevee_cryptomatte_passes_per_layer(const ViewLayer *view_layer)
{
const int num_cryptomatte_levels = view_layer->cryptomatte_levels;
const int num_cryptomatte_passes = (num_cryptomatte_levels + 1) / 2;
return num_cryptomatte_passes;
}
BLI_INLINE int eevee_cryptomatte_layer_stride(const ViewLayer *view_layer)
{
return view_layer->cryptomatte_levels;
}
BLI_INLINE int eevee_cryptomatte_layer_offset(const ViewLayer *view_layer, const int layer)
{
return view_layer->cryptomatte_levels * layer;
}
BLI_INLINE int eevee_cryptomatte_pixel_stride(const ViewLayer *view_layer)
{
return eevee_cryptomatte_layer_stride(view_layer) * eevee_cryptomatte_layers_count(view_layer);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Init Renderpasses
* \{ */
void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PrivateData *g_data = stl->g_data;
const DRWContextState *draw_ctx = DRW_context_state_get();
ViewLayer *view_layer = draw_ctx->view_layer;
/* Cryptomatte is only rendered for final image renders */
if (!DRW_state_is_scene_render()) {
return;
}
const eViewLayerCryptomatteFlags active_layers = eevee_cryptomatte_active_layers(view_layer);
if (active_layers) {
struct CryptomatteSession *session = BKE_cryptomatte_init();
if ((active_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
BKE_cryptomatte_add_layer(session, "CryptoObject");
}
if ((active_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
BKE_cryptomatte_add_layer(session, "CryptoMaterial");
}
if ((active_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
BKE_cryptomatte_add_layer(session, "CryptoAsset");
}
g_data->cryptomatte_session = session;
g_data->render_passes |= EEVEE_RENDER_PASS_CRYPTOMATTE | EEVEE_RENDER_PASS_VOLUME_LIGHT;
}
}
void EEVEE_cryptomatte_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
EEVEE_Data *vedata,
int UNUSED(tot_samples))
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PrivateData *g_data = stl->g_data;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
const DRWContextState *draw_ctx = DRW_context_state_get();
const ViewLayer *view_layer = draw_ctx->view_layer;
const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer);
eGPUTextureFormat format = (num_cryptomatte_layers == 1) ? GPU_R32F :
(num_cryptomatte_layers == 2) ? GPU_RG32F :
GPU_RGBA32F;
const float *viewport_size = DRW_viewport_size_get();
const int buffer_size = viewport_size[0] * viewport_size[1];
if (g_data->cryptomatte_accum_buffer == NULL) {
g_data->cryptomatte_accum_buffer = MEM_calloc_arrayN(
sizeof(EEVEE_CryptomatteSample),
buffer_size * eevee_cryptomatte_pixel_stride(view_layer),
__func__);
/* Download buffer should store a float per active cryptomatte layer. */
g_data->cryptomatte_download_buffer = MEM_malloc_arrayN(
sizeof(float), buffer_size * num_cryptomatte_layers, __func__);
}
else {
/* During multiview rendering the `cryptomatte_accum_buffer` is deallocated after all views
* have been rendered. Clear it here to be reused by the next view. */
memset(g_data->cryptomatte_accum_buffer,
0,
buffer_size * eevee_cryptomatte_pixel_stride(view_layer) *
sizeof(EEVEE_CryptomatteSample));
}
DRW_texture_ensure_fullscreen_2d(&txl->cryptomatte, format, 0);
GPU_framebuffer_ensure_config(&fbl->cryptomatte_fb,
{
GPU_ATTACHMENT_TEXTURE(dtxl->depth),
GPU_ATTACHMENT_TEXTURE(txl->cryptomatte),
});
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Populate Cache
* \{ */
void EEVEE_cryptomatte_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) {
DRW_PASS_CREATE(psl->cryptomatte_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
}
}
static DRWShadingGroup *eevee_cryptomatte_shading_group_create(EEVEE_Data *vedata,
EEVEE_ViewLayerData *UNUSED(sldata),
Object *ob,
Material *material,
bool is_hair)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
const ViewLayer *view_layer = draw_ctx->view_layer;
const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers(
view_layer);
EEVEE_PrivateData *g_data = vedata->stl->g_data;
float cryptohash[4] = {0.0f};
EEVEE_PassList *psl = vedata->psl;
int layer_offset = 0;
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
uint32_t cryptomatte_hash = BKE_cryptomatte_object_hash(
g_data->cryptomatte_session, "CryptoObject", ob);
float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash);
cryptohash[layer_offset] = cryptomatte_color_value;
layer_offset++;
}
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
uint32_t cryptomatte_hash = BKE_cryptomatte_material_hash(
g_data->cryptomatte_session, "CryptoMaterial", material);
float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash);
cryptohash[layer_offset] = cryptomatte_color_value;
layer_offset++;
}
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
uint32_t cryptomatte_hash = BKE_cryptomatte_asset_hash(
g_data->cryptomatte_session, "CryptoAsset", ob);
float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash);
cryptohash[layer_offset] = cryptomatte_color_value;
layer_offset++;
}
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_cryptomatte_sh_get(is_hair),
psl->cryptomatte_ps);
DRW_shgroup_uniform_vec4_copy(grp, "cryptohash", cryptohash);
return grp;
}
static void eevee_cryptomatte_hair_cache_populate(EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
Object *ob,
ParticleSystem *psys,
ModifierData *md,
Material *material)
{
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
vedata, sldata, ob, material, true);
DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL);
}
void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
Object *ob)
{
BLI_assert(ob->type == OB_HAIR);
Material *material = BKE_object_material_get_eval(ob, HAIR_MATERIAL_NR);
eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, NULL, NULL, material);
}
void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
Object *ob)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
if (ob->type == OB_MESH) {
if (ob != draw_ctx->object_edit) {
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type != eModifierType_ParticleSystem) {
continue;
}
ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) {
continue;
}
ParticleSettings *part = psys->part;
const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
if (draw_as != PART_DRAW_PATH) {
continue;
}
Material *material = BKE_object_material_get_eval(ob, part->omat);
eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, psys, md, material);
}
}
}
}
void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
const ViewLayer *view_layer = draw_ctx->view_layer;
const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers(
view_layer);
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
const int materials_len = DRW_cache_object_material_count_get(ob);
struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len);
memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len);
struct GPUBatch **geoms = DRW_cache_object_surface_material_get(
ob, gpumat_array, materials_len);
if (geoms) {
for (int i = 0; i < materials_len; i++) {
struct GPUBatch *geom = geoms[i];
if (geom == NULL) {
continue;
}
Material *material = BKE_object_material_get_eval(ob, i + 1);
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
vedata, sldata, ob, material, false);
DRW_shgroup_call(grp, geom, ob);
}
}
}
else {
GPUBatch *geom = DRW_cache_object_surface_get(ob);
if (geom) {
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
vedata, sldata, ob, NULL, false);
DRW_shgroup_call(grp, geom, ob);
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Accumulate Samples
* \{ */
/* Downloads cryptomatte sample buffer from the GPU and integrate the samples with the accumulated
* cryptomatte samples. */
static void eevee_cryptomatte_download_buffer(EEVEE_Data *vedata, GPUFrameBuffer *framebuffer)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PrivateData *g_data = stl->g_data;
const DRWContextState *draw_ctx = DRW_context_state_get();
const ViewLayer *view_layer = draw_ctx->view_layer;
const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer);
const int num_levels = view_layer->cryptomatte_levels;
const float *viewport_size = DRW_viewport_size_get();
const int buffer_size = viewport_size[0] * viewport_size[1];
EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer;
float *download_buffer = g_data->cryptomatte_download_buffer;
BLI_assert(accum_buffer);
BLI_assert(download_buffer);
GPU_framebuffer_read_color(framebuffer,
0,
0,
viewport_size[0],
viewport_size[1],
num_cryptomatte_layers,
0,
GPU_DATA_FLOAT,
download_buffer);
/* Integrate download buffer into the accum buffer.
* The download buffer contains up to 3 floats per pixel (one float per cryptomatte layer.
*
* NOTE: here we deviate from the cryptomatte standard. During integration the standard always
* sort the samples by its weight to make sure that samples with the lowest weight
* are discarded first. In our case the weight of each sample is always 1 as we don't have
* subsamples and apply the coverage during the post processing. When there is no room for new
* samples the new samples has a weight of 1 and will always be discarded. */
int download_pixel_index = 0;
int accum_pixel_index = 0;
int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer);
for (int pixel_index = 0; pixel_index < buffer_size; pixel_index++) {
for (int layer = 0; layer < num_cryptomatte_layers; layer++) {
const int layer_offset = eevee_cryptomatte_layer_offset(view_layer, layer);
float download_hash = download_buffer[download_pixel_index++];
for (int level = 0; level < num_levels; level++) {
EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level];
if (sample->hash == download_hash) {
sample->weight += 1.0f;
break;
}
/* We test against weight as hash 0.0f is used for samples hitting the world background. */
if (sample->weight == 0.0f) {
sample->hash = download_hash;
sample->weight = 1.0f;
break;
}
}
}
accum_pixel_index += accum_pixel_stride;
}
}
void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_PassList *psl = vedata->psl;
const DRWContextState *draw_ctx = DRW_context_state_get();
const ViewLayer *view_layer = draw_ctx->view_layer;
const int cryptomatte_levels = view_layer->cryptomatte_levels;
const int current_sample = effects->taa_current_sample;
/* Render samples used by cryptomatte are limited to the number of cryptomatte levels. This will
* reduce the overhead of downloading the GPU buffer and integrating it into the accum buffer. */
if (current_sample < cryptomatte_levels) {
static float clear_color[4] = {0.0};
GPU_framebuffer_bind(fbl->cryptomatte_fb);
GPU_framebuffer_clear_color(fbl->cryptomatte_fb, clear_color);
DRW_draw_pass(psl->cryptomatte_ps);
eevee_cryptomatte_download_buffer(vedata, fbl->cryptomatte_fb);
/* Restore */
GPU_framebuffer_bind(fbl->main_fb);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Update Render Passes
* \{ */
/* Register the render passes needed for cryptomatte
* normally this is done in `EEVEE_render_update_passes`, but it has been placed here to keep
* related code side-by-side for clarity. */
void EEVEE_cryptomatte_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
{
char cryptomatte_pass_name[MAX_NAME];
const short num_passes = eevee_cryptomatte_passes_per_layer(view_layer);
if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
for (short pass = 0; pass < num_passes; pass++) {
BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoObject%02d", pass);
RE_engine_register_pass(
engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
}
}
if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
for (short pass = 0; pass < num_passes; pass++) {
BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoMaterial%02d", pass);
RE_engine_register_pass(
engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
}
}
if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
for (short pass = 0; pass < num_passes; pass++) {
BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoAsset%02d", pass);
RE_engine_register_pass(
engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Construct Render Result
* \{ */
/* Compare function for cryptomatte samples. Samples with the highest weight will be at the
* beginning of the list. */
static int eevee_cryptomatte_sample_cmp_reverse(const void *a_, const void *b_)
{
const EEVEE_CryptomatteSample *a = a_;
const EEVEE_CryptomatteSample *b = b_;
if (a->weight < b->weight) {
return 1;
}
if (a->weight > b->weight) {
return -1;
}
return 0;
}
/* Post process the weights. The accumulated weights buffer adds one to each weight per sample.
* During post processing ensure that the total of weights per sample is between 0 and 1. */
static void eevee_cryptomatte_postprocess_weights(EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PrivateData *g_data = stl->g_data;
EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_TextureList *txl = vedata->txl;
const DRWContextState *draw_ctx = DRW_context_state_get();
const ViewLayer *view_layer = draw_ctx->view_layer;
const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer);
const int num_levels = view_layer->cryptomatte_levels;
const float *viewport_size = DRW_viewport_size_get();
const int buffer_size = viewport_size[0] * viewport_size[1];
EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer;
BLI_assert(accum_buffer);
float *volumetric_transmittance_buffer = NULL;
if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
volumetric_transmittance_buffer = GPU_texture_read(
txl->volume_transmittance_accum, GPU_DATA_FLOAT, 0);
}
const int num_samples = effects->taa_current_sample - 1;
int accum_pixel_index = 0;
int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer);
for (int pixel_index = 0; pixel_index < buffer_size;
pixel_index++, accum_pixel_index += accum_pixel_stride) {
float coverage = 1.0f;
if (volumetric_transmittance_buffer != NULL) {
coverage = (volumetric_transmittance_buffer[pixel_index * 4] +
volumetric_transmittance_buffer[pixel_index * 4 + 1] +
volumetric_transmittance_buffer[pixel_index * 4 + 2]) /
(3.0f * num_samples);
}
for (int layer = 0; layer < num_cryptomatte_layers; layer++) {
const int layer_offset = eevee_cryptomatte_layer_offset(view_layer, layer);
/* Calculate the total weight of the sample. */
float total_weight = 0.0f;
for (int level = 0; level < num_levels; level++) {
EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level];
total_weight += sample->weight;
}
BLI_assert(total_weight > 0.0f);
float total_weight_inv = coverage / total_weight;
if (total_weight_inv > 0.0f) {
for (int level = 0; level < num_levels; level++) {
EEVEE_CryptomatteSample *sample =
&accum_buffer[accum_pixel_index + layer_offset + level];
/* Remove background samples. These samples were used to determine the correct weight
* but won't be part of the final result. */
if (sample->hash == 0.0f) {
sample->weight = 0.0f;
}
sample->weight *= total_weight_inv;
}
/* Sort accum buffer by coverage of each sample. */
qsort(&accum_buffer[accum_pixel_index + layer_offset],
num_levels,
sizeof(EEVEE_CryptomatteSample),
eevee_cryptomatte_sample_cmp_reverse);
}
else {
/* This pixel doesn't have any weight, so clear it fully. */
for (int level = 0; level < num_levels; level++) {
EEVEE_CryptomatteSample *sample =
&accum_buffer[accum_pixel_index + layer_offset + level];
sample->weight = 0.0f;
sample->hash = 0.0f;
}
}
}
}
if (volumetric_transmittance_buffer) {
MEM_freeN(volumetric_transmittance_buffer);
}
}
/* Extract cryptomatte layer from the cryptomatte_accum_buffer to render passes. */
static void eevee_cryptomatte_extract_render_passes(
RenderLayer *rl,
const char *viewname,
const char *render_pass_name_format,
EEVEE_CryptomatteSample *accum_buffer,
/* number of render passes per cryptomatte layer. */
const int num_cryptomatte_passes,
const int num_cryptomatte_levels,
const int accum_pixel_stride,
const int layer_stride,
const int layer_index,
const int rect_width,
const int rect_height,
const int rect_offset_x,
const int rect_offset_y,
const int viewport_width)
{
char cryptomatte_pass_name[MAX_NAME];
/* A pass can store 2 levels. Technically the last pass can have a single level if the number of
* levels is an odd number. This parameter counts the number of levels it has processed. */
int levels_done = 0;
for (int pass = 0; pass < num_cryptomatte_passes; pass++) {
/* Each pass holds 2 cryptomatte samples. */
const int pass_offset = pass * 2;
BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, render_pass_name_format, pass);
RenderPass *rp_object = RE_pass_find_by_name(rl, cryptomatte_pass_name, viewname);
for (int y = 0; y < rect_height; y++) {
for (int x = 0; x < rect_width; x++) {
const int accum_buffer_offset = (rect_offset_x + x +
(rect_offset_y + y) * viewport_width) *
accum_pixel_stride +
layer_index * layer_stride + pass_offset;
const int render_pass_offset = (y * rect_width + x) * 4;
rp_object->rect[render_pass_offset] = accum_buffer[accum_buffer_offset].hash;
rp_object->rect[render_pass_offset + 1] = accum_buffer[accum_buffer_offset].weight;
if (levels_done + 1 < num_cryptomatte_levels) {
rp_object->rect[render_pass_offset + 2] = accum_buffer[accum_buffer_offset + 1].hash;
rp_object->rect[render_pass_offset + 3] = accum_buffer[accum_buffer_offset + 1].weight;
}
else {
rp_object->rect[render_pass_offset + 2] = 0.0f;
rp_object->rect[render_pass_offset + 3] = 0.0f;
}
}
}
levels_done++;
}
}
void EEVEE_cryptomatte_render_result(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *UNUSED(sldata))
{
EEVEE_PrivateData *g_data = vedata->stl->g_data;
const DRWContextState *draw_ctx = DRW_context_state_get();
const ViewLayer *view_layer = draw_ctx->view_layer;
const eViewLayerCryptomatteFlags cryptomatte_layers = view_layer->cryptomatte_flag &
VIEW_LAYER_CRYPTOMATTE_ALL;
eevee_cryptomatte_postprocess_weights(vedata);
const int rect_width = BLI_rcti_size_x(rect);
const int rect_height = BLI_rcti_size_y(rect);
const int rect_offset_x = vedata->stl->g_data->overscan_pixels + rect->xmin;
const int rect_offset_y = vedata->stl->g_data->overscan_pixels + rect->ymin;
const float *viewport_size = DRW_viewport_size_get();
const int viewport_width = viewport_size[0];
EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer;
BLI_assert(accum_buffer);
const int num_cryptomatte_levels = view_layer->cryptomatte_levels;
const int num_cryptomatte_passes = eevee_cryptomatte_passes_per_layer(view_layer);
const int layer_stride = eevee_cryptomatte_layer_stride(view_layer);
const int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer);
int layer_index = 0;
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
eevee_cryptomatte_extract_render_passes(rl,
viewname,
"CryptoObject%02d",
accum_buffer,
num_cryptomatte_passes,
num_cryptomatte_levels,
accum_pixel_stride,
layer_stride,
layer_index,
rect_width,
rect_height,
rect_offset_x,
rect_offset_y,
viewport_width);
layer_index++;
}
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
eevee_cryptomatte_extract_render_passes(rl,
viewname,
"CryptoMaterial%02d",
accum_buffer,
num_cryptomatte_passes,
num_cryptomatte_levels,
accum_pixel_stride,
layer_stride,
layer_index,
rect_width,
rect_height,
rect_offset_x,
rect_offset_y,
viewport_width);
layer_index++;
}
if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
eevee_cryptomatte_extract_render_passes(rl,
viewname,
"CryptoAsset%02d",
accum_buffer,
num_cryptomatte_passes,
num_cryptomatte_levels,
accum_pixel_stride,
layer_stride,
layer_index,
rect_width,
rect_height,
rect_offset_x,
rect_offset_y,
viewport_width);
layer_index++;
}
}
void EEVEE_cryptomatte_store_metadata(EEVEE_Data *vedata, RenderResult *render_result)
{
EEVEE_PrivateData *g_data = vedata->stl->g_data;
const DRWContextState *draw_ctx = DRW_context_state_get();
const ViewLayer *view_layer = draw_ctx->view_layer;
BLI_assert(g_data->cryptomatte_session);
BKE_cryptomatte_store_metadata(g_data->cryptomatte_session, render_result, view_layer);
}
/** \} */
void EEVEE_cryptomatte_free(EEVEE_Data *vedata)
{
EEVEE_PrivateData *g_data = vedata->stl->g_data;
MEM_SAFE_FREE(g_data->cryptomatte_accum_buffer);
MEM_SAFE_FREE(g_data->cryptomatte_download_buffer);
if (g_data->cryptomatte_session) {
BKE_cryptomatte_free(g_data->cryptomatte_session);
g_data->cryptomatte_session = NULL;
}
}

View File

@@ -0,0 +1,68 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* A culling object is a data structure that contains fine grained culling
* of entities against in the whole view frustum. The Culling structure contains the
* final entity list since it has to have a special order.
*
* Follows the principles of Tiled Culling + Z binning from:
* "Improved Culling for Tiled and Clustered Rendering"
* by Michal Drobot
* http://advances.realtimerendering.com/s2017/2017_Sig_Improved_Culling_final.pdf
*/
#include "eevee_instance.hh"
#include "eevee_culling.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name CullingDebugPass
* \{ */
void CullingDebugPass::sync(void)
{
LightModule &lights = inst_.lights;
debug_ps_ = DRW_pass_create("CullingDebug", DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(CULLING_DEBUG);
DRWShadingGroup *grp = DRW_shgroup_create(sh, debug_ps_);
DRW_shgroup_uniform_block_ref(grp, "lights_block", lights.lights_ubo_ref_get());
DRW_shgroup_uniform_block_ref(grp, "lights_culling_block", lights.culling_ubo_ref_get());
DRW_shgroup_uniform_texture_ref(grp, "lights_culling_tx", lights.culling_tx_ref_get());
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
void CullingDebugPass::render(GPUTexture *input_depth_tx)
{
input_depth_tx_ = input_depth_tx;
inst_.lights.bind_batch(0);
DRW_draw_pass(debug_ps_);
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,334 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* A culling object is a data structure that contains fine grained culling
* of entities against in the whole view frustum. The Culling structure contains the
* final entity list since it has to have a special order.
*
* Follows the principles of Tiled Culling + Z binning from:
* "Improved Culling for Tiled and Clustered Rendering"
* by Michal Drobot
* http://advances.realtimerendering.com/s2017/2017_Sig_Improved_Culling_final.pdf
*/
#pragma once
#include "DRW_render.h"
#include "BLI_vector.hh"
#include "eevee_shader_shared.hh"
namespace blender::eevee {
class Instance;
/* -------------------------------------------------------------------- */
/** \name CullingBatch
* \{ */
/**
* Do not use directly. Use Culling object instead.
*/
template<
/* Type of data contained per culling batch. */
typename Tdata>
class CullingBatch {
public:
/** Z ordered items. */
Tdata item_data;
private:
/* Items to order in Z. */
struct ItemHandle {
/** Index inside item_source_. */
uint32_t source_index;
/** Signed Z distance along camera Z axis. */
float z_dist;
/** Item radius. */
float radius;
};
/** Compact handle list to order without moving source. */
Vector<ItemHandle, CULLING_ITEM_BATCH> item_handles_;
/** Z bins. */
CullingDataBuf culling_data_;
/** Tile texture and framebuffer handling the 2D culling. */
eevee::Texture tiles_tx_ = Texture("culling_tx_");
eevee::Framebuffer tiles_fb_;
public:
CullingBatch(){};
~CullingBatch(){};
void init(const ivec2 &extent)
{
item_handles_.clear();
uint tile_size = 8;
uint res[2] = {divide_ceil_u(extent.x, tile_size), divide_ceil_u(extent.y, tile_size)};
tiles_tx_.ensure(UNPACK2(res), 1, GPU_RGBA32UI);
culling_data_.tile_size = tile_size;
for (int i = 0; i < 2; i++) {
culling_data_.tile_to_uv_fac[i] = tile_size / (float)extent[i];
}
// tiles_tx_.ensure(1, 1, 1, GPU_RGBA32UI);
// uvec4 no_2D_culling = {UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX};
// GPU_texture_update(tiles_tx_, GPU_DATA_UINT, no_2D_culling);
tiles_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(tiles_tx_));
}
void set_empty(void)
{
init_min_max();
culling_data_.push_update();
}
void insert(int32_t index, float z_dist, float radius)
{
ItemHandle handle = {(uint32_t)index, z_dist, radius};
item_handles_.append(handle);
}
template<typename DataAppendF, typename CullingF>
void finalize(float near_z,
float far_z,
const DataAppendF &data_append,
const CullingF &draw_culling)
{
culling_data_.zbin_scale = -CULLING_ZBIN_COUNT / fabsf(far_z - near_z);
culling_data_.zbin_bias = -near_z * culling_data_.zbin_scale;
/* Order items by Z distance to the camera. */
auto sort = [](const ItemHandle &a, const ItemHandle &b) { return a.z_dist > b.z_dist; };
std::sort(item_handles_.begin(), item_handles_.end(), sort);
init_min_max();
/* Fill the GPU data buffer. */
for (auto item_idx : item_handles_.index_range()) {
ItemHandle &handle = item_handles_[item_idx];
data_append(item_data, item_idx, handle.source_index);
/* Register to Z bins. */
int z_min = max_ii(culling_z_to_zbin(culling_data_, handle.z_dist + handle.radius), 0);
int z_max = min_ii(culling_z_to_zbin(culling_data_, handle.z_dist - handle.radius),
CULLING_ZBIN_COUNT - 1);
for (auto z : IndexRange(z_min, z_max - z_min + 1)) {
BLI_assert(z >= 0 && z < CULLING_ZBIN_COUNT);
uint16_t(&zbin_minmax)[2] = ((uint16_t(*)[2])culling_data_.zbins)[z];
if (item_idx < zbin_minmax[0]) {
zbin_minmax[0] = (uint16_t)item_idx;
}
if (item_idx > zbin_minmax[1]) {
zbin_minmax[1] = (uint16_t)item_idx;
}
}
}
/* Set item count for no-cull iterator. */
culling_data_.items_count = item_handles_.size();
/* Upload data to GPU. */
culling_data_.push_update();
GPU_framebuffer_bind(tiles_fb_);
draw_culling(item_data, culling_data_);
}
/**
* Getters
**/
bool is_full(void)
{
return item_handles_.size() == CULLING_ITEM_BATCH;
}
const GPUUniformBuf *culling_ubo_get(void)
{
return culling_data_.ubo_get();
}
GPUTexture *culling_texture_get(void)
{
return tiles_tx_;
}
private:
void init_min_max(void)
{
/* Init min-max for each bin. */
for (auto i : IndexRange(CULLING_ZBIN_COUNT)) {
uint16_t *zbin_minmax = (uint16_t *)culling_data_.zbins;
zbin_minmax[i * 2 + 0] = CULLING_ITEM_BATCH - 1;
zbin_minmax[i * 2 + 1] = 0;
}
culling_data_.items_count = 0;
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Culling
* \{ */
template</* Type of data contained per culling batch. */
typename Tdata,
/* True if items can be added in multiple batches. */
bool is_extendable = false>
class Culling {
private:
using CullingBatchType = CullingBatch<Tdata>;
/** Multiple culling batches containing at most CULLING_ITEM_BATCH items worth of data. */
Vector<CullingBatchType *> batches_;
/** Number of active batches. Allocated count may be higher. */
int used_batch_count_;
/** Pointer to the active batch being filled. */
CullingBatchType *active_batch_;
/** Used to get Z distance. */
vec3 camera_z_axis_;
float camera_z_offset_;
/** View for which the culling is computed. */
const DRWView *view_;
/** View resolution. */
ivec2 extent_ = ivec2(0);
public:
Culling(){};
~Culling()
{
for (CullingBatchType *batch : batches_) {
delete batch;
}
}
void set_view(const DRWView *view, const ivec2 extent)
{
view_ = view;
extent_ = extent;
float viewinv[4][4];
DRW_view_viewmat_get(view, viewinv, true);
camera_z_axis_ = viewinv[2];
camera_z_offset_ = -vec3::dot(camera_z_axis_, viewinv[3]);
if (batches_.size() == 0) {
batches_.append(new CullingBatchType());
}
used_batch_count_ = 1;
active_batch_ = batches_[0];
active_batch_->init(extent_);
}
/* Cull every items. Do not reset the batches to avoid freeing the vectors' memory. */
void set_empty(void)
{
if (extent_.x == 0) {
extent_ = ivec2(1);
}
if (batches_.size() == 0) {
batches_.append(new CullingBatchType());
active_batch_ = batches_[0];
active_batch_->init(extent_);
}
active_batch_ = batches_[0];
active_batch_->set_empty();
}
/* Returns true if we cannot add any more items.
* In this case, the caller is expected to not try to insert another item. */
bool insert(int32_t index, BoundSphere &bsphere)
{
if (!DRW_culling_sphere_test(view_, &bsphere)) {
return false;
}
if (active_batch_->is_full()) {
BLI_assert(is_extendable);
/* TODO(fclem) degrow vector of batches. */
if (batches_.size() < (used_batch_count_ + 1)) {
batches_.append(new CullingBatchType());
}
active_batch_ = batches_[used_batch_count_];
active_batch_->init(extent_);
used_batch_count_++;
}
float z_dist = vec3::dot(bsphere.center, camera_z_axis_) + camera_z_offset_;
active_batch_->insert(index, z_dist, bsphere.radius);
return active_batch_->is_full();
}
template<typename DataAppendF, typename CullingF>
void finalize(const DataAppendF &data_append, const CullingF &draw_culling)
{
float near_z = DRW_view_near_distance_get(view_);
float far_z = DRW_view_far_distance_get(view_);
for (auto i : IndexRange(used_batch_count_)) {
batches_[i]->finalize(near_z, far_z, data_append, draw_culling);
}
}
/**
* Getters
**/
CullingBatchType *operator[](int64_t index)
{
return batches_[index];
}
IndexRange index_range(void) const
{
return IndexRange(used_batch_count_);
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name CullingDebugPass
* \{ */
class CullingDebugPass {
private:
Instance &inst_;
GPUTexture *input_depth_tx_ = nullptr;
DRWPass *debug_ps_ = nullptr;
public:
CullingDebugPass(Instance &inst) : inst_(inst){};
void sync(void);
void render(GPUTexture *input_depth_tx);
};
/** \} */
} // namespace blender::eevee

View File

@@ -1,389 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* All specific data handler for Objects, Lights, ViewLayers, ...
*/
#include "DRW_render.h"
#include "BLI_ghash.h"
#include "BLI_memblock.h"
#include "BKE_duplilist.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "DEG_depsgraph_query.h"
#include "GPU_vertex_buffer.h"
#include "eevee_lightcache.h"
#include "eevee_private.h"
/* Motion Blur data. */
static void eevee_motion_blur_mesh_data_free(void *val)
{
EEVEE_GeometryMotionData *geom_mb = (EEVEE_GeometryMotionData *)val;
EEVEE_HairMotionData *hair_mb = (EEVEE_HairMotionData *)val;
switch (geom_mb->type) {
case EEVEE_MOTION_DATA_HAIR:
for (int j = 0; j < hair_mb->psys_len; j++) {
for (int i = 0; i < ARRAY_SIZE(hair_mb->psys[0].hair_pos); i++) {
GPU_VERTBUF_DISCARD_SAFE(hair_mb->psys[j].hair_pos[i]);
}
for (int i = 0; i < ARRAY_SIZE(hair_mb->psys[0].hair_pos); i++) {
DRW_TEXTURE_FREE_SAFE(hair_mb->psys[j].hair_pos_tx[i]);
}
}
break;
case EEVEE_MOTION_DATA_MESH:
for (int i = 0; i < ARRAY_SIZE(geom_mb->vbo); i++) {
GPU_VERTBUF_DISCARD_SAFE(geom_mb->vbo[i]);
}
break;
}
MEM_freeN(val);
}
static uint eevee_object_key_hash(const void *key)
{
EEVEE_ObjectKey *ob_key = (EEVEE_ObjectKey *)key;
uint hash = BLI_ghashutil_ptrhash(ob_key->ob);
hash = BLI_ghashutil_combine_hash(hash, BLI_ghashutil_ptrhash(ob_key->parent));
for (int i = 0; i < MAX_DUPLI_RECUR; i++) {
if (ob_key->id[i] != 0) {
hash = BLI_ghashutil_combine_hash(hash, BLI_ghashutil_inthash(ob_key->id[i]));
}
else {
break;
}
}
return hash;
}
/* Return false if equal. */
static bool eevee_object_key_cmp(const void *a, const void *b)
{
EEVEE_ObjectKey *key_a = (EEVEE_ObjectKey *)a;
EEVEE_ObjectKey *key_b = (EEVEE_ObjectKey *)b;
if (key_a->ob != key_b->ob) {
return true;
}
if (key_a->parent != key_b->parent) {
return true;
}
if (memcmp(key_a->id, key_b->id, sizeof(key_a->id)) != 0) {
return true;
}
return false;
}
void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb)
{
if (mb->object == NULL) {
mb->object = BLI_ghash_new(eevee_object_key_hash, eevee_object_key_cmp, "EEVEE Object Motion");
}
if (mb->geom == NULL) {
mb->geom = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE Mesh Motion");
}
}
void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb)
{
if (mb->object) {
BLI_ghash_free(mb->object, MEM_freeN, MEM_freeN);
mb->object = NULL;
}
if (mb->geom) {
BLI_ghash_free(mb->geom, NULL, eevee_motion_blur_mesh_data_free);
mb->geom = NULL;
}
}
EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb,
Object *ob,
bool hair)
{
if (mb->object == NULL) {
return NULL;
}
EEVEE_ObjectKey key, *key_p;
/* Small hack to avoid another comparison. */
key.ob = (Object *)((char *)ob + hair);
DupliObject *dup = DRW_object_get_dupli(ob);
if (dup) {
key.parent = DRW_object_get_dupli_parent(ob);
memcpy(key.id, dup->persistent_id, sizeof(key.id));
}
else {
key.parent = key.ob;
memset(key.id, 0, sizeof(key.id));
}
EEVEE_ObjectMotionData *ob_step = BLI_ghash_lookup(mb->object, &key);
if (ob_step == NULL) {
key_p = MEM_mallocN(sizeof(*key_p), __func__);
memcpy(key_p, &key, sizeof(*key_p));
ob_step = MEM_callocN(sizeof(EEVEE_ObjectMotionData), __func__);
BLI_ghash_insert(mb->object, key_p, ob_step);
}
return ob_step;
}
static void *motion_blur_deform_data_get(EEVEE_MotionBlurData *mb, Object *ob, bool hair)
{
if (mb->geom == NULL) {
return NULL;
}
DupliObject *dup = DRW_object_get_dupli(ob);
void *key;
if (dup) {
key = dup->ob;
}
else {
key = ob;
}
/* Only use data for object that have no modifiers. */
if (!BKE_object_is_modified(DRW_context_state_get()->scene, ob)) {
key = ob->data;
}
key = (char *)key + (int)hair;
EEVEE_GeometryMotionData *geom_step = BLI_ghash_lookup(mb->geom, key);
if (geom_step == NULL) {
if (hair) {
EEVEE_HairMotionData *hair_step;
/* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */
int psys_len = (ob->type != OB_HAIR) ? BLI_listbase_count(&ob->modifiers) : 1;
hair_step = MEM_callocN(sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len,
__func__);
hair_step->psys_len = psys_len;
geom_step = (EEVEE_GeometryMotionData *)hair_step;
geom_step->type = EEVEE_MOTION_DATA_HAIR;
}
else {
geom_step = MEM_callocN(sizeof(EEVEE_GeometryMotionData), __func__);
geom_step->type = EEVEE_MOTION_DATA_MESH;
}
BLI_ghash_insert(mb->geom, key, geom_step);
}
return geom_step;
}
EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb, Object *ob)
{
return motion_blur_deform_data_get(mb, ob, false);
}
EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_MotionBlurData *mb, Object *ob)
{
return motion_blur_deform_data_get(mb, ob, true);
}
/* View Layer data. */
void EEVEE_view_layer_data_free(void *storage)
{
EEVEE_ViewLayerData *sldata = (EEVEE_ViewLayerData *)storage;
/* Lights */
MEM_SAFE_FREE(sldata->lights);
DRW_UBO_FREE_SAFE(sldata->light_ubo);
DRW_UBO_FREE_SAFE(sldata->shadow_ubo);
GPU_FRAMEBUFFER_FREE_SAFE(sldata->shadow_fb);
DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_pool);
DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_pool);
for (int i = 0; i < 2; i++) {
MEM_SAFE_FREE(sldata->shcasters_buffers[i].bbox);
MEM_SAFE_FREE(sldata->shcasters_buffers[i].update);
}
if (sldata->fallback_lightcache) {
EEVEE_lightcache_free(sldata->fallback_lightcache);
sldata->fallback_lightcache = NULL;
}
/* Probes */
MEM_SAFE_FREE(sldata->probes);
DRW_UBO_FREE_SAFE(sldata->probe_ubo);
DRW_UBO_FREE_SAFE(sldata->grid_ubo);
DRW_UBO_FREE_SAFE(sldata->planar_ubo);
DRW_UBO_FREE_SAFE(sldata->common_ubo);
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.combined);
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.diff_color);
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.diff_light);
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.spec_color);
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.spec_light);
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.emit);
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.environment);
for (int aov_index = 0; aov_index < MAX_AOVS; aov_index++) {
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.aovs[aov_index]);
}
if (sldata->material_cache) {
BLI_memblock_destroy(sldata->material_cache, NULL);
sldata->material_cache = NULL;
}
}
EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void)
{
return (EEVEE_ViewLayerData *)DRW_view_layer_engine_data_get(&draw_engine_eevee_type);
}
static void eevee_view_layer_init(EEVEE_ViewLayerData *sldata)
{
sldata->common_ubo = GPU_uniformbuf_create(sizeof(sldata->common_data));
}
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer)
{
EEVEE_ViewLayerData **sldata = (EEVEE_ViewLayerData **)DRW_view_layer_engine_data_ensure_ex(
view_layer, &draw_engine_eevee_type, &EEVEE_view_layer_data_free);
if (*sldata == NULL) {
*sldata = MEM_callocN(sizeof(**sldata), "EEVEE_ViewLayerData");
eevee_view_layer_init(*sldata);
}
return *sldata;
}
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure(void)
{
EEVEE_ViewLayerData **sldata = (EEVEE_ViewLayerData **)DRW_view_layer_engine_data_ensure(
&draw_engine_eevee_type, &EEVEE_view_layer_data_free);
if (*sldata == NULL) {
*sldata = MEM_callocN(sizeof(**sldata), "EEVEE_ViewLayerData");
eevee_view_layer_init(*sldata);
}
return *sldata;
}
/* Object data. */
static void eevee_object_data_init(DrawData *dd)
{
EEVEE_ObjectEngineData *eevee_data = (EEVEE_ObjectEngineData *)dd;
eevee_data->shadow_caster_id = -1;
eevee_data->need_update = false;
eevee_data->geom_update = false;
}
EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob)
{
if (ELEM(ob->type, OB_LIGHTPROBE, OB_LAMP)) {
return NULL;
}
return (EEVEE_ObjectEngineData *)DRW_drawdata_get(&ob->id, &draw_engine_eevee_type);
}
EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob)
{
BLI_assert(!ELEM(ob->type, OB_LIGHTPROBE, OB_LAMP));
return (EEVEE_ObjectEngineData *)DRW_drawdata_ensure(&ob->id,
&draw_engine_eevee_type,
sizeof(EEVEE_ObjectEngineData),
eevee_object_data_init,
NULL);
}
/* Light probe data. */
static void eevee_lightprobe_data_init(DrawData *dd)
{
EEVEE_LightProbeEngineData *ped = (EEVEE_LightProbeEngineData *)dd;
ped->need_update = false;
}
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob)
{
if (ob->type != OB_LIGHTPROBE) {
return NULL;
}
return (EEVEE_LightProbeEngineData *)DRW_drawdata_get(&ob->id, &draw_engine_eevee_type);
}
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_ensure(Object *ob)
{
BLI_assert(ob->type == OB_LIGHTPROBE);
return (EEVEE_LightProbeEngineData *)DRW_drawdata_ensure(&ob->id,
&draw_engine_eevee_type,
sizeof(EEVEE_LightProbeEngineData),
eevee_lightprobe_data_init,
NULL);
}
/* Light data. */
static void eevee_light_data_init(DrawData *dd)
{
EEVEE_LightEngineData *led = (EEVEE_LightEngineData *)dd;
led->need_update = true;
}
EEVEE_LightEngineData *EEVEE_light_data_get(Object *ob)
{
if (ob->type != OB_LAMP) {
return NULL;
}
return (EEVEE_LightEngineData *)DRW_drawdata_get(&ob->id, &draw_engine_eevee_type);
}
EEVEE_LightEngineData *EEVEE_light_data_ensure(Object *ob)
{
BLI_assert(ob->type == OB_LAMP);
return (EEVEE_LightEngineData *)DRW_drawdata_ensure(&ob->id,
&draw_engine_eevee_type,
sizeof(EEVEE_LightEngineData),
eevee_light_data_init,
NULL);
}
/* World data. */
static void eevee_world_data_init(DrawData *dd)
{
EEVEE_WorldEngineData *wed = (EEVEE_WorldEngineData *)dd;
wed->dd.recalc |= 1;
}
EEVEE_WorldEngineData *EEVEE_world_data_get(World *wo)
{
return (EEVEE_WorldEngineData *)DRW_drawdata_get(&wo->id, &draw_engine_eevee_type);
}
EEVEE_WorldEngineData *EEVEE_world_data_ensure(World *wo)
{
return (EEVEE_WorldEngineData *)DRW_drawdata_ensure(&wo->id,
&draw_engine_eevee_type,
sizeof(EEVEE_WorldEngineData),
eevee_world_data_init,
NULL);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,730 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* Depth of field post process effect.
*
* There are 2 methods to achieve this effect.
* - The first uses projection matrix offsetting and sample accumulation to give
* reference quality depth of field. But this needs many samples to hide the
* under-sampling.
* - The second one is a post-processing based one. It follows the
* implementation described in the presentation "Life of a Bokeh - Siggraph
* 2018" from Guillaume Abadie. There are some difference with our actual
* implementation that prioritize quality.
*/
#include "DRW_render.h"
#include "BKE_camera.h"
#include "DNA_camera_types.h"
#include "GPU_texture.h"
#include "GPU_uniform_buffer.h"
#include "eevee_camera.hh"
#include "eevee_instance.hh"
#include "eevee_sampling.hh"
#include "eevee_shader.hh"
#include "eevee_shader_shared.hh"
#include "eevee_depth_of_field.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name Depth of field
* \{ */
void DepthOfField::init(void)
{
const SceneEEVEE &sce_eevee = inst_.scene->eevee;
do_hq_slight_focus_ = (sce_eevee.flag & SCE_EEVEE_DOF_HQ_SLIGHT_FOCUS) != 0;
do_jitter_ = (sce_eevee.flag & SCE_EEVEE_DOF_JITTER) != 0;
user_overblur_ = sce_eevee.bokeh_overblur / 100.0f;
fx_max_coc_ = sce_eevee.bokeh_max_size;
data_.scatter_color_threshold = sce_eevee.bokeh_threshold;
data_.scatter_neighbor_max_color = sce_eevee.bokeh_neighbor_max;
data_.denoise_factor = sce_eevee.bokeh_denoise_fac;
/* Default to no depth of field. */
fx_radius_ = 0.0f;
jitter_radius_ = 0.0f;
}
void DepthOfField::sync(const mat4 winmat, ivec2 input_extent)
{
const Object *camera_object_eval = inst_.camera_eval_object;
const ::Camera *cam = (camera_object_eval) ?
reinterpret_cast<const ::Camera *>(camera_object_eval->data) :
nullptr;
if (cam == nullptr || (cam->dof.flag & CAM_DOF_ENABLED) == 0) {
fx_radius_ = 0.0f;
jitter_radius_ = 0.0f;
return;
}
extent_ = input_extent;
data_.bokeh_blades = cam->dof.aperture_blades;
data_.bokeh_rotation = cam->dof.aperture_rotation;
data_.bokeh_anisotropic_scale.x = clamp_f(1.0f / cam->dof.aperture_ratio, 0.00001f, 1.0f);
data_.bokeh_anisotropic_scale.y = clamp_f(cam->dof.aperture_ratio, 0.00001f, 1.0f);
data_.bokeh_anisotropic_scale_inv = 1.0f / data_.bokeh_anisotropic_scale;
focus_distance_ = BKE_camera_object_dof_distance(camera_object_eval);
float fstop = max_ff(cam->dof.aperture_fstop, 1e-5f);
float aperture = 1.0f / (2.0f * fstop);
if (cam->type == CAM_PERSP) {
aperture *= cam->lens * 1e-3f;
}
if (cam->type == CAM_ORTHO) {
/* FIXME: Why is this needed? Some kind of implicit unit conversion? */
aperture *= 0.04f;
/* Really strange behavior from Cycles but replicating. */
focus_distance_ += cam->clip_start;
}
if (cam->type == CAM_PANO) {
/* FIXME: Eyeballed. */
aperture *= 0.185f;
}
if (cam->dof.aperture_ratio < 1.0) {
/* If ratio is scaling the bokeh outwards, we scale the aperture so that
* the gather kernel size will encompass the maximum axis. */
aperture /= max_ff(cam->dof.aperture_ratio, 1e-5f);
}
/* Balance blur radius between fx dof and jitter dof. */
if (do_jitter_ && (inst_.sampling.dof_ring_count_get() > 0) && (cam->type != CAM_PANO)) {
/* Compute a minimal overblur radius to fill the gaps between the samples.
* This is just the simplified form of dividing the area of the bokeh by
* the number of samples. */
float minimal_overblur = 1.0f / sqrtf(inst_.sampling.dof_sample_count_get());
fx_radius_ = (minimal_overblur + user_overblur_) * aperture;
/* Avoid dilating the shape. Over-blur only soften. */
jitter_radius_ = max_ff(0.0f, aperture - fx_radius_);
}
else {
jitter_radius_ = 0.0f;
fx_radius_ = aperture;
}
if (fx_max_coc_ > 0.0f && fx_radius_ > 0.0f) {
data_.camera_type = inst_.camera.data_get().type;
/* OPTI(fclem) Could be optimized. */
float jitter[3] = {fx_radius_, 0.0f, -focus_distance_};
float center[3] = {0.0f, 0.0f, -focus_distance_};
mul_project_m4_v3(winmat, jitter);
mul_project_m4_v3(winmat, center);
/* Simplify CoC calculation to a simple MADD. */
if (data_.camera_type != CAMERA_ORTHO) {
data_.coc_bias = -(center[0] - jitter[0]) * 0.5f * extent_[0];
data_.coc_mul = focus_distance_ * data_.coc_bias;
}
else {
data_.coc_mul = (center[0] - jitter[0]) * 0.5f * extent_[0];
data_.coc_bias = focus_distance_ * data_.coc_mul;
}
float min_fg_coc = coc_radius_from_camera_depth(data_, -cam->clip_start);
float max_bg_coc = coc_radius_from_camera_depth(data_, -cam->clip_end);
if (data_.camera_type != CAMERA_ORTHO) {
/* Background is at infinity so maximum CoC is the limit of the function at -inf. */
/* NOTE: we only do this for perspective camera since orthographic coc limit is inf. */
max_bg_coc = data_.coc_bias;
}
/* Clamp with user defined max. */
data_.coc_abs_max = min_ff(max_ff(fabsf(min_fg_coc), fabsf(max_bg_coc)), fx_max_coc_);
bokeh_lut_pass_sync();
setup_pass_sync();
tiles_prepare_pass_sync();
reduce_pass_sync();
convolve_pass_sync();
resolve_pass_sync();
data_.push_update();
}
}
void DepthOfField::jitter_apply(mat4 winmat, mat4 viewmat)
{
if (jitter_radius_ == 0.0f) {
return;
}
float radius, theta;
inst_.sampling.dof_disk_sample_get(&radius, &theta);
if (data_.bokeh_blades >= 3.0f) {
theta = circle_to_polygon_angle(data_.bokeh_blades, theta);
radius *= circle_to_polygon_radius(data_.bokeh_blades, theta);
}
radius *= jitter_radius_;
theta += data_.bokeh_rotation;
/* Sample in View Space. */
vec2 sample = vec2(radius * cosf(theta), radius * sinf(theta));
sample *= data_.bokeh_anisotropic_scale;
/* Convert to NDC Space. */
vec3 jitter = vec3(UNPACK2(sample), -focus_distance_);
vec3 center = vec3(0.0f, 0.0f, -focus_distance_);
mul_project_m4_v3(winmat, jitter);
mul_project_m4_v3(winmat, center);
const bool is_ortho = (winmat[2][3] != -1.0f);
if (is_ortho) {
sample *= focus_distance_;
}
/* Translate origin. */
sub_v2_v2(viewmat[3], sample);
/* Skew winmat Z axis. */
add_v2_v2(winmat[2], center - jitter);
}
/** Will swap input and output texture if rendering happens. The actual output of this function
* is in intput_tx. */
void DepthOfField::render(GPUTexture *depth_tx, GPUTexture **input_tx, GPUTexture **output_tx)
{
if (fx_radius_ == 0.0f || fx_max_coc_ < 0.5f) {
return;
}
input_color_tx_ = *input_tx;
input_depth_tx_ = depth_tx;
DRW_stats_group_start("Depth of Field");
bokeh_lut_pass_render();
setup_pass_render();
tiles_prepare_pass_render();
reduce_pass_render();
convolve_pass_render();
resolve_pass_render(*output_tx);
DRW_stats_group_end();
/* Swap buffers so that next effect has the right input. */
*input_tx = *output_tx;
*output_tx = input_color_tx_;
}
/**
* Creates bokeh texture.
**/
void DepthOfField::bokeh_lut_pass_sync(void)
{
const bool has_anisotropy = data_.bokeh_anisotropic_scale != vec2(1.0f);
if (has_anisotropy && (data_.bokeh_blades == 0.0)) {
bokeh_gather_lut_tx_ = nullptr;
bokeh_scatter_lut_tx_ = nullptr;
bokeh_resolve_lut_tx_ = nullptr;
bokeh_lut_ps_ = nullptr;
return;
}
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
int res[2] = {DOF_BOKEH_LUT_SIZE, DOF_BOKEH_LUT_SIZE};
DRW_PASS_CREATE(bokeh_lut_ps_, DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(DOF_BOKEH_LUT);
DRWShadingGroup *grp = DRW_shgroup_create(sh, bokeh_lut_ps_);
DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
bokeh_gather_lut_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RG16F, owner);
bokeh_scatter_lut_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
bokeh_resolve_lut_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
bokeh_lut_fb_.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(bokeh_gather_lut_tx_),
GPU_ATTACHMENT_TEXTURE(bokeh_scatter_lut_tx_),
GPU_ATTACHMENT_TEXTURE(bokeh_resolve_lut_tx_));
}
void DepthOfField::bokeh_lut_pass_render(void)
{
if (bokeh_lut_ps_) {
GPU_framebuffer_bind(bokeh_lut_fb_);
DRW_draw_pass(bokeh_lut_ps_);
}
}
/**
* Outputs halfResColorBuffer and halfResCocBuffer.
**/
void DepthOfField::setup_pass_sync(void)
{
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
uint res[2] = {divide_ceil_u(extent_[0], 2), divide_ceil_u(extent_[1], 2)};
DRW_PASS_CREATE(setup_ps_, DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(DOF_SETUP);
DRWShadingGroup *grp = DRW_shgroup_create(sh, setup_ps_);
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &input_color_tx_, no_filter);
DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &input_depth_tx_, no_filter);
DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
setup_color_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
setup_coc_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RG16F, owner);
setup_fb_.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(setup_color_tx_),
GPU_ATTACHMENT_TEXTURE(setup_coc_tx_));
}
void DepthOfField::setup_pass_render(void)
{
GPU_framebuffer_bind(setup_fb_);
DRW_draw_pass(setup_ps_);
}
/**
* Outputs min & max COC in each 8x8 half res pixel tiles (so 1/16th of full resolution).
* Then dilates the min & max CoCs to cover maximum COC values.
**/
void DepthOfField::tiles_prepare_pass_sync(void)
{
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
uint res[2] = {divide_ceil_u(extent_[0], DOF_TILE_DIVISOR),
divide_ceil_u(extent_[1], DOF_TILE_DIVISOR)};
/* WARNING: If you change this, make sure dof_tile_* GLSL constants can be properly encoded. */
eGPUTextureFormat fg_tile_format = GPU_RGBA16F;
eGPUTextureFormat bg_tile_format = GPU_R11F_G11F_B10F;
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
{
DRW_PASS_CREATE(tiles_flatten_ps_, DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(DOF_TILES_FLATTEN);
DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_flatten_ps_);
DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
tiles_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), fg_tile_format, owner);
tiles_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), bg_tile_format, owner);
tiles_flatten_fb_.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(tiles_fg_tx_),
GPU_ATTACHMENT_TEXTURE(tiles_bg_tx_));
}
{
DRW_PASS_CREATE(tiles_dilate_minmax_ps_, DRW_STATE_WRITE_COLOR);
DRW_PASS_CREATE(tiles_dilate_minabs_ps_, DRW_STATE_WRITE_COLOR);
for (int pass = 0; pass < 2; pass++) {
DRWPass *drw_pass = (pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_;
GPUShader *sh = inst_.shaders.static_shader_get((pass == 0) ? DOF_TILES_DILATE_MINMAX :
DOF_TILES_DILATE_MINABS);
DRWShadingGroup *grp = DRW_shgroup_create(sh, drw_pass);
DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_fg_tx", &tiles_fg_tx_, no_filter);
DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_bg_tx", &tiles_bg_tx_, no_filter);
DRW_shgroup_uniform_bool(grp, "dilate_slight_focus", &tiles_dilate_slight_focus_, 1);
DRW_shgroup_uniform_int(grp, "ring_count", &tiles_dilate_ring_count_, 1);
DRW_shgroup_uniform_int(
grp, "ring_width_multiplier", &tiles_dilate_ring_width_multiplier_, 1);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
tiles_dilated_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), fg_tile_format, owner);
tiles_dilated_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), bg_tile_format, owner);
tiles_dilate_fb_.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(tiles_dilated_fg_tx_),
GPU_ATTACHMENT_TEXTURE(tiles_dilated_bg_tx_));
}
}
void DepthOfField::tiles_prepare_pass_render(void)
{
GPU_framebuffer_bind(tiles_flatten_fb_);
DRW_draw_pass(tiles_flatten_ps_);
/* Run dilation twice. One for minmax and one for minabs. */
for (int pass = 0; pass < 2; pass++) {
/* Error introduced by gather center jittering. */
const float error_multiplier = 1.0f + 1.0f / (DOF_GATHER_RING_COUNT + 0.5f);
int dilation_end_radius = ceilf((fx_max_coc_ * error_multiplier) / DOF_TILE_DIVISOR);
/* This algorithm produce the exact dilation radius by dividing it in multiple passes. */
int dilation_radius = 0;
while (dilation_radius < dilation_end_radius) {
/* Dilate slight focus only on first iteration. */
tiles_dilate_slight_focus_ = (dilation_radius == 0) ? 1 : 0;
int remainder = dilation_end_radius - dilation_radius;
/* Do not step over any unvisited tile. */
int max_multiplier = dilation_radius + 1;
int ring_count = min_ii(DOF_DILATE_RING_COUNT, ceilf(remainder / (float)max_multiplier));
int multiplier = min_ii(max_multiplier, floor(remainder / (float)ring_count));
dilation_radius += ring_count * multiplier;
tiles_dilate_ring_count_ = ring_count;
tiles_dilate_ring_width_multiplier_ = multiplier;
GPU_framebuffer_bind(tiles_dilate_fb_);
DRW_draw_pass((pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_);
SWAP(eevee::Framebuffer, tiles_dilate_fb_, tiles_flatten_fb_);
SWAP(GPUTexture *, tiles_dilated_bg_tx_, tiles_bg_tx_);
SWAP(GPUTexture *, tiles_dilated_fg_tx_, tiles_fg_tx_);
}
}
/* Swap again so that final textures are tiles_dilated_*_tx_. */
SWAP(eevee::Framebuffer, tiles_dilate_fb_, tiles_flatten_fb_);
SWAP(GPUTexture *, tiles_dilated_bg_tx_, tiles_bg_tx_);
SWAP(GPUTexture *, tiles_dilated_fg_tx_, tiles_fg_tx_);
}
/**
* Create mipmapped color & COC textures for gather passes.
**/
void DepthOfField::reduce_pass_sync(void)
{
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
/* Divide by 2 because dof_fx_max_coc is in fullres CoC radius and the reduce
* texture begins at half resolution. */
float max_space_between_sample = fx_max_coc_ * 0.5f / DOF_GATHER_RING_COUNT;
int mip_count = max_ii(1, log2_ceil_u(max_space_between_sample));
reduce_steps_ = mip_count - 1;
/* This ensure the mipmaps are aligned for the needed 4 mip levels.
* Starts at 2 because already at half resolution. */
int multiple = 2 << (mip_count - 1);
uint res[2] = {(multiple * divide_ceil_u(extent_[0], multiple)) / 2,
(multiple * divide_ceil_u(extent_[1], multiple)) / 2};
uint quater_res[2] = {divide_ceil_u(extent_[0], 4), divide_ceil_u(extent_[1], 4)};
/* TODO(fclem): Make this dependent of the quality of the gather pass. */
data_.scatter_coc_threshold = 4.0f;
/* Color needs to be signed format here. See note in shader for explanation. */
/* Do not use texture pool because of needs mipmaps. */
reduced_color_tx_.ensure(UNPACK2(res), mip_count, GPU_RGBA16F);
reduced_coc_tx_.ensure(UNPACK2(res), mip_count, GPU_R16F);
{
DRW_PASS_CREATE(reduce_downsample_ps_, DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(DOF_REDUCE_DOWNSAMPLE);
DRWShadingGroup *grp = DRW_shgroup_create(sh, reduce_downsample_ps_);
DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &setup_color_tx_, no_filter);
DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
reduce_downsample_tx_ = DRW_texture_pool_query_2d(UNPACK2(quater_res), GPU_RGBA16F, owner);
reduce_downsample_fb_.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(reduce_downsample_tx_));
}
{
DRW_PASS_CREATE(reduce_copy_ps_, DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(DOF_REDUCE_COPY);
DRWShadingGroup *grp = DRW_shgroup_create(sh, reduce_copy_ps_);
DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &setup_color_tx_, no_filter);
DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter);
DRW_shgroup_uniform_texture_ex(grp, "downsampled_tx", reduce_downsample_tx_, no_filter);
DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
scatter_src_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R11F_G11F_B10F, owner);
}
{
DRW_PASS_CREATE(reduce_recursive_ps_, DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(DOF_REDUCE_RECURSIVE);
DRWShadingGroup *grp = DRW_shgroup_create(sh, reduce_recursive_ps_);
DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter);
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
reduce_fb_.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(reduced_color_tx_),
GPU_ATTACHMENT_TEXTURE(reduced_coc_tx_));
reduce_copy_fb_.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(reduced_color_tx_),
GPU_ATTACHMENT_TEXTURE(reduced_coc_tx_),
GPU_ATTACHMENT_TEXTURE(scatter_src_tx_));
}
void DepthOfField::reduce_recusive(void *thunk, int UNUSED(level))
{
DepthOfField *dof = reinterpret_cast<DepthOfField *>(thunk);
DRW_draw_pass(dof->reduce_recursive_ps_);
}
void DepthOfField::reduce_pass_render(void)
{
GPU_framebuffer_bind(reduce_downsample_fb_);
DRW_draw_pass(reduce_downsample_ps_);
/* First step is just a copy. */
GPU_framebuffer_bind(reduce_copy_fb_);
DRW_draw_pass(reduce_copy_ps_);
GPU_framebuffer_recursive_downsample(reduce_fb_, reduce_steps_, &reduce_recusive, this);
}
/**
* Do the gather & scatter convolution. For each pixels we gather multiple pixels in its
* neighborhood depending on the min & max CoC tiles. We apply a median filter on the output.
* We also scatter a sprite for very bright pixels for high quality bokeh.
**/
void DepthOfField::convolve_pass_sync(void)
{
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
eGPUSamplerState with_filter = (GPU_SAMPLER_MIPMAP | GPU_SAMPLER_FILTER);
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
uint res[2] = {divide_ceil_u(extent_[0], 2), divide_ceil_u(extent_[1], 2)};
int input_size[2];
GPU_texture_get_mipmap_size(reduced_color_tx_, 0, input_size);
for (int i = 0; i < 2; i++) {
data_.gather_uv_fac[i] = 1.0f / (float)input_size[i];
data_.texel_size[i] = 1.0f / res[i];
}
/* Reuse textures from the setup pass. */
/* NOTE: We could use the texture pool do that for us but it does not track
* usage and it might backfire (it does in practice). */
/* Since it is only used for scatter, and foreground is processed before background, we can
* reuse the occlusion_tx for both field. */
occlusion_tx_ = setup_coc_tx_;
{
DRW_PASS_CREATE(gather_holefill_ps_, DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(DOF_GATHER_HOLEFILL);
DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_holefill_ps_);
DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, with_filter);
DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter);
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_);
DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_);
DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
/* Reuse textures from the setup pass. */
/* NOTE: We could use the texture pool do that for us but it does not track
* usage and it might backfire (it does in practice). */
color_holefill_tx_ = setup_color_tx_;
weight_holefill_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
gather_holefill_fb_.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(color_holefill_tx_),
GPU_ATTACHMENT_TEXTURE(weight_holefill_tx_));
}
{
DRW_PASS_CREATE(gather_fg_ps_, DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(
bokeh_gather_lut_tx_ ? DOF_GATHER_FOREGROUND_LUT : DOF_GATHER_FOREGROUND);
DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_fg_ps_);
DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, with_filter);
DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter);
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_);
DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_);
if (bokeh_gather_lut_tx_) {
DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_gather_lut_tx_);
}
DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
color_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
weight_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
}
{
DRW_PASS_CREATE(gather_bg_ps_, DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(
bokeh_gather_lut_tx_ ? DOF_GATHER_BACKGROUND_LUT : DOF_GATHER_BACKGROUND);
DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_bg_ps_);
DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, with_filter);
DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter);
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_);
DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_);
if (bokeh_gather_lut_tx_) {
DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_gather_lut_tx_);
}
DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
color_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
weight_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
}
/* NOTE: First target is holefill texture so we can use the median filter on it and save some
* texture memory. Both field use the same framebuffer. */
gather_fb_.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(color_holefill_tx_),
GPU_ATTACHMENT_TEXTURE(weight_holefill_tx_),
GPU_ATTACHMENT_TEXTURE(occlusion_tx_));
{
/**
* Filter an input buffer using a median filter to reduce noise.
* NOTE: We use the holefill texture as our input to reduce memory usage.
* Thus, the holefill pass cannot be filtered.
**/
DRW_PASS_CREATE(filter_ps_, DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(DOF_FILTER);
DRWShadingGroup *grp = DRW_shgroup_create(sh, filter_ps_);
DRW_shgroup_uniform_texture_ex(grp, "color_tx", color_holefill_tx_, no_filter);
DRW_shgroup_uniform_texture_ex(grp, "weight_tx", weight_holefill_tx_, no_filter);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
filter_fg_fb_.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(color_fg_tx_),
GPU_ATTACHMENT_TEXTURE(weight_fg_tx_));
filter_bg_fb_.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(color_bg_tx_),
GPU_ATTACHMENT_TEXTURE(weight_bg_tx_));
}
/**
* Do the Scatter convolution. A sprite is emitted for every 4 pixels but is only expanded
* if the pixels are bright enough to be scattered.
**/
data_.scatter_sprite_per_row = input_size[0] / 2;
int sprite_count = data_.scatter_sprite_per_row * (input_size[1] / 2);
{
DRW_PASS_CREATE(scatter_fg_ps_, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
GPUShader *sh = inst_.shaders.static_shader_get(
bokeh_gather_lut_tx_ ? DOF_SCATTER_FOREGROUND_LUT : DOF_SCATTER_FOREGROUND);
DRWShadingGroup *grp = DRW_shgroup_create(sh, scatter_fg_ps_);
DRW_shgroup_uniform_texture_ex(grp, "color_tx", scatter_src_tx_, no_filter);
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
DRW_shgroup_uniform_texture(grp, "occlusion_tx", occlusion_tx_);
if (bokeh_scatter_lut_tx_) {
DRW_shgroup_uniform_texture(grp, "bokehLut", bokeh_scatter_lut_tx_);
}
DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
DRW_shgroup_call_procedural_triangles(grp, NULL, sprite_count);
scatter_fg_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(color_fg_tx_));
}
{
DRW_PASS_CREATE(scatter_bg_ps_, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
GPUShader *sh = inst_.shaders.static_shader_get(
bokeh_gather_lut_tx_ ? DOF_SCATTER_BACKGROUND_LUT : DOF_SCATTER_BACKGROUND);
DRWShadingGroup *grp = DRW_shgroup_create(sh, scatter_bg_ps_);
DRW_shgroup_uniform_texture_ex(grp, "color_tx", scatter_src_tx_, no_filter);
DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
DRW_shgroup_uniform_texture(grp, "occlusion_tx", occlusion_tx_);
if (bokeh_scatter_lut_tx_) {
DRW_shgroup_uniform_texture(grp, "bokehLut", bokeh_scatter_lut_tx_);
}
DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
DRW_shgroup_call_procedural_triangles(grp, NULL, sprite_count);
scatter_bg_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(color_bg_tx_));
}
}
void DepthOfField::convolve_pass_render(void)
{
DRW_stats_group_start("Foreground convolution");
GPU_framebuffer_bind(gather_fb_);
DRW_draw_pass(gather_fg_ps_);
GPU_framebuffer_bind(filter_fg_fb_);
DRW_draw_pass(filter_ps_);
GPU_framebuffer_bind(scatter_fg_fb_);
DRW_draw_pass(scatter_fg_ps_);
DRW_stats_group_end();
DRW_stats_group_start("Background convolution");
GPU_framebuffer_bind(gather_fb_);
DRW_draw_pass(gather_bg_ps_);
GPU_framebuffer_bind(filter_bg_fb_);
DRW_draw_pass(filter_ps_);
GPU_framebuffer_bind(scatter_bg_fb_);
DRW_draw_pass(scatter_bg_ps_);
DRW_stats_group_end();
DRW_stats_group_start("Background convolution");
/* Hole-fill convolution. */
GPU_framebuffer_bind(gather_holefill_fb_);
DRW_draw_pass(gather_holefill_ps_);
/* NOTE: We do not filter the hole-fill pass as we use it as out filter input
* buffer. Also effect is likely to not be noticeable. */
DRW_stats_group_end();
}
/**
* Recombine the result of the foreground and background processing.
* Also perform a slight out of focus gather to improve geometric continuity.
**/
void DepthOfField::resolve_pass_sync(void)
{
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
eGPUSamplerState with_filter = GPU_SAMPLER_FILTER;
eShaderType sh_type = (bokeh_resolve_lut_tx_) ?
(do_hq_slight_focus_ ? DOF_RESOLVE_LUT_HQ : DOF_RESOLVE_LUT) :
(do_hq_slight_focus_ ? DOF_RESOLVE_HQ : DOF_RESOLVE);
DRW_PASS_CREATE(resolve_ps_, DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(sh_type);
DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_);
DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &input_depth_tx_, no_filter);
DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &input_color_tx_, no_filter);
DRW_shgroup_uniform_texture_ex(grp, "color_bg_tx", color_bg_tx_, with_filter);
DRW_shgroup_uniform_texture_ex(grp, "color_fg_tx", color_fg_tx_, with_filter);
DRW_shgroup_uniform_texture_ex(grp, "color_holefill_tx", color_holefill_tx_, with_filter);
DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_);
DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_);
DRW_shgroup_uniform_texture_ex(grp, "weight_bg_tx", weight_bg_tx_, with_filter);
DRW_shgroup_uniform_texture_ex(grp, "weight_fg_tx", weight_fg_tx_, with_filter);
DRW_shgroup_uniform_texture_ex(grp, "weight_holefill_tx", weight_holefill_tx_, with_filter);
DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
if (bokeh_resolve_lut_tx_) {
DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_resolve_lut_tx_);
}
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
void DepthOfField::resolve_pass_render(GPUTexture *output_tx)
{
resolve_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(output_tx));
GPU_framebuffer_bind(resolve_fb_);
DRW_draw_pass(resolve_ps_);
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,176 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* Depth of field post process effect.
*
* There are 2 methods to achieve this effect.
* - The first uses projection matrix offsetting and sample accumulation to give
* reference quality depth of field. But this needs many samples to hide the
* under-sampling.
* - The second one is a post-processing based one. It follows the
* implementation described in the presentation "Life of a Bokeh - Siggraph
* 2018" from Guillaume Abadie. There are some difference with our actual
* implementation that prioritize quality.
*/
#pragma once
#include "eevee_shader_shared.hh"
namespace blender::eevee {
class Instance;
/* -------------------------------------------------------------------- */
/** \name Depth of field
* \{ */
class DepthOfField {
private:
class Instance &inst_;
DepthOfFieldDataBuf data_;
/** Textures from pool. Not owned. */
GPUTexture *bokeh_gather_lut_tx_ = nullptr;
GPUTexture *bokeh_resolve_lut_tx_ = nullptr;
GPUTexture *bokeh_scatter_lut_tx_ = nullptr;
GPUTexture *color_bg_tx_ = nullptr;
GPUTexture *color_fg_tx_ = nullptr;
GPUTexture *color_holefill_tx_ = nullptr;
GPUTexture *occlusion_tx_ = nullptr;
GPUTexture *reduce_downsample_tx_ = nullptr;
GPUTexture *scatter_src_tx_ = nullptr;
GPUTexture *setup_coc_tx_ = nullptr;
GPUTexture *setup_color_tx_ = nullptr;
GPUTexture *tiles_bg_tx_ = nullptr;
GPUTexture *tiles_fg_tx_ = nullptr;
GPUTexture *tiles_dilated_bg_tx_ = nullptr;
GPUTexture *tiles_dilated_fg_tx_ = nullptr;
GPUTexture *weight_bg_tx_ = nullptr;
GPUTexture *weight_fg_tx_ = nullptr;
GPUTexture *weight_holefill_tx_ = nullptr;
/** Allocated textures. Owned. */
eevee::Texture reduced_coc_tx_ = Texture("dof_reduced_coc");
eevee::Texture reduced_color_tx_ = Texture("dof_reduced_color");
/** Input texture. Not owned. */
GPUTexture *input_color_tx_;
GPUTexture *input_depth_tx_;
/** Passes. Not owned. */
DRWPass *bokeh_lut_ps_ = nullptr;
DRWPass *gather_bg_ps_ = nullptr;
DRWPass *gather_fg_ps_ = nullptr;
DRWPass *filter_ps_ = nullptr;
DRWPass *gather_holefill_ps_ = nullptr;
DRWPass *reduce_copy_ps_ = nullptr;
DRWPass *reduce_downsample_ps_ = nullptr;
DRWPass *reduce_recursive_ps_ = nullptr;
DRWPass *resolve_ps_ = nullptr;
DRWPass *scatter_bg_ps_ = nullptr;
DRWPass *scatter_fg_ps_ = nullptr;
DRWPass *setup_ps_ = nullptr;
DRWPass *tiles_dilate_minabs_ps_ = nullptr;
DRWPass *tiles_dilate_minmax_ps_ = nullptr;
DRWPass *tiles_flatten_ps_ = nullptr;
/** Framebuffers. Owned. */
eevee::Framebuffer bokeh_lut_fb_ = Framebuffer("bokeh_lut_fb_");
eevee::Framebuffer filter_bg_fb_ = Framebuffer("filter_bg_fb_");
eevee::Framebuffer filter_fg_fb_ = Framebuffer("filter_fg_fb_");
eevee::Framebuffer gather_fb_ = Framebuffer("gather_fb_");
eevee::Framebuffer gather_filter_bg_fb_ = Framebuffer("gather_filter_bg_fb_");
eevee::Framebuffer gather_holefill_fb_ = Framebuffer("gather_holefill_fb_");
eevee::Framebuffer reduce_copy_fb_ = Framebuffer("reduce_copy_fb_");
eevee::Framebuffer reduce_downsample_fb_ = Framebuffer("reduce_downsample_fb_");
eevee::Framebuffer reduce_fb_ = Framebuffer("reduce_fb_");
eevee::Framebuffer resolve_fb_ = Framebuffer("resolve_fb_");
eevee::Framebuffer scatter_bg_fb_ = Framebuffer("scatter_bg_fb_");
eevee::Framebuffer scatter_fg_fb_ = Framebuffer("scatter_fg_fb_");
eevee::Framebuffer setup_fb_ = Framebuffer("setup_fb_");
eevee::Framebuffer tiles_dilate_fb_ = Framebuffer("tiles_dilate_fb_");
eevee::Framebuffer tiles_flatten_fb_ = Framebuffer("tiles_flatten_fb_");
/** Scene settings that are immutable. */
float user_overblur_;
float fx_max_coc_;
/** Use Hiqh Quality (expensive) in-focus gather pass. */
bool do_hq_slight_focus_;
/** Use jittered depth of field where we randomize camera location. */
bool do_jitter_;
/** Circle of Confusion radius for FX DoF passes. Is in view X direction in [0..1] range. */
float fx_radius_;
/** Circle of Confusion radius for jittered DoF. Is in view X direction in [0..1] range. */
float jitter_radius_;
/** Focus distance in view space. */
float focus_distance_;
/** Extent of the input buffer. */
ivec2 extent_;
/** Tile dilation uniforms. */
int tiles_dilate_slight_focus_;
int tiles_dilate_ring_count_;
int tiles_dilate_ring_width_multiplier_;
/** Reduce pass info. */
int reduce_steps_;
/** Static srting pointer. Used as debug name and as UUID for texture pool. */
StringRefNull view_name_;
public:
DepthOfField(Instance &inst, StringRefNull view_name) : inst_(inst), view_name_(view_name){};
~DepthOfField(){};
void init();
void sync(const mat4 winmat, ivec2 input_extent);
/** Apply Depth Of Field jittering to the view and projection matrices.. */
void jitter_apply(mat4 winmat, mat4 viewmat);
/** Will swap input and output texture if rendering happens. The actual output of this function
* is in intput_tx. */
void render(GPUTexture *depth_tx, GPUTexture **input_tx, GPUTexture **output_tx);
private:
void bokeh_lut_pass_sync(void);
void bokeh_lut_pass_render(void);
void setup_pass_sync(void);
void setup_pass_render(void);
void tiles_prepare_pass_sync(void);
void tiles_prepare_pass_render(void);
static void reduce_recusive(void *thunk, int level);
void reduce_pass_sync(void);
void reduce_pass_render(void);
void convolve_pass_sync(void);
void convolve_pass_render(void);
void resolve_pass_sync(void);
void resolve_pass_render(GPUTexture *output_tx);
};
/** \} */
} // namespace blender::eevee

View File

@@ -1,529 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Gather all screen space effects technique such as Bloom, Motion Blur, DoF, SSAO, SSR, ...
*/
#include "DRW_render.h"
#include "BKE_global.h" /* for G.debug_value */
#include "GPU_capabilities.h"
#include "GPU_platform.h"
#include "GPU_state.h"
#include "GPU_texture.h"
#include "eevee_private.h"
static struct {
/* These are just references, not actually allocated */
struct GPUTexture *depth_src;
struct GPUTexture *color_src;
int depth_src_layer;
/* Size can be vec3. But we only use 2 components in the shader. */
float texel_size[2];
} e_data = {NULL}; /* Engine data */
#define SETUP_BUFFER(tex, fb, fb_color) \
{ \
eGPUTextureFormat format = (DRW_state_is_scene_render()) ? GPU_RGBA32F : GPU_RGBA16F; \
DRW_texture_ensure_fullscreen_2d(&tex, format, DRW_TEX_FILTER); \
GPU_framebuffer_ensure_config(&fb, \
{ \
GPU_ATTACHMENT_TEXTURE(dtxl->depth), \
GPU_ATTACHMENT_TEXTURE(tex), \
}); \
GPU_framebuffer_ensure_config(&fb_color, \
{ \
GPU_ATTACHMENT_NONE, \
GPU_ATTACHMENT_TEXTURE(tex), \
}); \
} \
((void)0)
#define CLEANUP_BUFFER(tex, fb, fb_color) \
{ \
/* Cleanup to release memory */ \
DRW_TEXTURE_FREE_SAFE(tex); \
GPU_FRAMEBUFFER_FREE_SAFE(fb); \
GPU_FRAMEBUFFER_FREE_SAFE(fb_color); \
} \
((void)0)
void EEVEE_effects_init(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
Object *camera,
const bool minimal)
{
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_EffectsInfo *effects;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
const float *viewport_size = DRW_viewport_size_get();
const int size_fs[2] = {(int)viewport_size[0], (int)viewport_size[1]};
if (!stl->effects) {
stl->effects = MEM_callocN(sizeof(EEVEE_EffectsInfo), "EEVEE_EffectsInfo");
stl->effects->taa_render_sample = 1;
}
/* WORKAROUND: EEVEE_lookdev_init can reset TAA and needs a stl->effect.
* So putting this before EEVEE_temporal_sampling_init for now. */
EEVEE_lookdev_init(vedata);
effects = stl->effects;
effects->enabled_effects = 0;
effects->enabled_effects |= (G.debug_value == 9) ? EFFECT_VELOCITY_BUFFER : 0;
effects->enabled_effects |= EEVEE_motion_blur_init(sldata, vedata);
effects->enabled_effects |= EEVEE_bloom_init(sldata, vedata);
effects->enabled_effects |= EEVEE_depth_of_field_init(sldata, vedata, camera);
effects->enabled_effects |= EEVEE_temporal_sampling_init(sldata, vedata);
effects->enabled_effects |= EEVEE_occlusion_init(sldata, vedata);
effects->enabled_effects |= EEVEE_screen_raytrace_init(sldata, 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);
/* Force normal buffer creation. */
if (!minimal && (stl->g_data->render_passes & EEVEE_RENDER_PASS_NORMAL) != 0) {
effects->enabled_effects |= EFFECT_NORMAL_BUFFER;
}
/**
* MinMax Pyramid
*/
int div = 1 << MAX_SCREEN_BUFFERS_LOD_LEVEL;
effects->hiz_size[0] = divide_ceil_u(size_fs[0], div) * div;
effects->hiz_size[1] = divide_ceil_u(size_fs[1], div) * div;
if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
/* Intel gpu seems to have problem rendering to only depth hiz_format */
DRW_texture_ensure_2d(&txl->maxzbuffer, UNPACK2(effects->hiz_size), GPU_R32F, DRW_TEX_MIPMAP);
GPU_framebuffer_ensure_config(&fbl->maxzbuffer_fb,
{
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(txl->maxzbuffer),
});
}
else {
DRW_texture_ensure_2d(
&txl->maxzbuffer, UNPACK2(effects->hiz_size), GPU_DEPTH_COMPONENT24, DRW_TEX_MIPMAP);
GPU_framebuffer_ensure_config(&fbl->maxzbuffer_fb,
{
GPU_ATTACHMENT_TEXTURE(txl->maxzbuffer),
GPU_ATTACHMENT_NONE,
});
}
if (fbl->downsample_fb == NULL) {
fbl->downsample_fb = GPU_framebuffer_create("downsample_fb");
}
/**
* Compute hiZ texel alignment.
*/
common_data->hiz_uv_scale[0] = viewport_size[0] / effects->hiz_size[0];
common_data->hiz_uv_scale[1] = viewport_size[1] / effects->hiz_size[1];
/* Compute pixel size. Size is multiplied by 2 because it is applied in NDC [-1..1] range. */
sldata->common_data.ssr_pixelsize[0] = 2.0f / size_fs[0];
sldata->common_data.ssr_pixelsize[1] = 2.0f / size_fs[1];
/**
* Color buffer with correct down-sampling alignment.
* Used for SSReflections & SSRefractions.
*/
if ((effects->enabled_effects & EFFECT_RADIANCE_BUFFER) != 0) {
DRW_texture_ensure_2d(&txl->filtered_radiance,
UNPACK2(effects->hiz_size),
GPU_R11F_G11F_B10F,
DRW_TEX_FILTER | DRW_TEX_MIPMAP);
GPU_framebuffer_ensure_config(&fbl->radiance_filtered_fb,
{
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(txl->filtered_radiance),
});
}
else {
DRW_TEXTURE_FREE_SAFE(txl->filtered_radiance);
GPU_FRAMEBUFFER_FREE_SAFE(fbl->radiance_filtered_fb);
}
/**
* Normal buffer for deferred passes.
*/
if ((effects->enabled_effects & EFFECT_NORMAL_BUFFER) != 0) {
effects->ssr_normal_input = DRW_texture_pool_query_2d(
size_fs[0], size_fs[1], GPU_RG16, &draw_engine_eevee_type);
GPU_framebuffer_texture_attach(fbl->main_fb, effects->ssr_normal_input, 1, 0);
}
else {
effects->ssr_normal_input = NULL;
}
/**
* Motion vector buffer for correct TAA / motion blur.
*/
if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
effects->velocity_tx = DRW_texture_pool_query_2d(
size_fs[0], size_fs[1], GPU_RGBA16, &draw_engine_eevee_type);
GPU_framebuffer_ensure_config(&fbl->velocity_fb,
{
GPU_ATTACHMENT_TEXTURE(dtxl->depth),
GPU_ATTACHMENT_TEXTURE(effects->velocity_tx),
});
GPU_framebuffer_ensure_config(
&fbl->velocity_resolve_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->velocity_tx)});
}
else {
effects->velocity_tx = NULL;
}
/**
* Setup depth double buffer.
*/
if ((effects->enabled_effects & EFFECT_DEPTH_DOUBLE_BUFFER) != 0) {
DRW_texture_ensure_fullscreen_2d(&txl->depth_double_buffer, GPU_DEPTH24_STENCIL8, 0);
GPU_framebuffer_ensure_config(&fbl->double_buffer_depth_fb,
{GPU_ATTACHMENT_TEXTURE(txl->depth_double_buffer)});
}
else {
/* Cleanup to release memory */
DRW_TEXTURE_FREE_SAFE(txl->depth_double_buffer);
GPU_FRAMEBUFFER_FREE_SAFE(fbl->double_buffer_depth_fb);
}
if ((effects->enabled_effects & (EFFECT_TAA | EFFECT_TAA_REPROJECT)) != 0) {
SETUP_BUFFER(txl->taa_history, fbl->taa_history_fb, fbl->taa_history_color_fb);
}
else {
CLEANUP_BUFFER(txl->taa_history, fbl->taa_history_fb, fbl->taa_history_color_fb);
}
}
void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
DRWState downsample_write = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS;
DRWShadingGroup *grp;
/* Intel gpu seems to have problem rendering to only depth format.
* Use color texture instead. */
if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
downsample_write = DRW_STATE_WRITE_COLOR;
}
struct GPUBatch *quad = DRW_cache_fullscreen_quad_get();
if (effects->enabled_effects & EFFECT_RADIANCE_BUFFER) {
DRW_PASS_CREATE(psl->color_copy_ps, DRW_STATE_WRITE_COLOR);
grp = DRW_shgroup_create(EEVEE_shaders_effect_color_copy_sh_get(), psl->color_copy_ps);
DRW_shgroup_uniform_texture_ref_ex(grp, "source", &e_data.color_src, GPU_SAMPLER_DEFAULT);
DRW_shgroup_uniform_float(grp, "fireflyFactor", &sldata->common_data.ssr_firefly_fac, 1);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
DRW_PASS_CREATE(psl->color_downsample_ps, DRW_STATE_WRITE_COLOR);
grp = DRW_shgroup_create(EEVEE_shaders_effect_downsample_sh_get(), psl->color_downsample_ps);
DRW_shgroup_uniform_texture_ex(grp, "source", txl->filtered_radiance, GPU_SAMPLER_FILTER);
DRW_shgroup_uniform_vec2(grp, "texelSize", e_data.texel_size, 1);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
{
DRW_PASS_CREATE(psl->color_downsample_cube_ps, DRW_STATE_WRITE_COLOR);
grp = DRW_shgroup_create(EEVEE_shaders_effect_downsample_cube_sh_get(),
psl->color_downsample_cube_ps);
DRW_shgroup_uniform_texture_ref(grp, "source", &e_data.color_src);
DRW_shgroup_uniform_float(grp, "texelSize", e_data.texel_size, 1);
DRW_shgroup_uniform_int_copy(grp, "Layer", 0);
DRW_shgroup_call_instances(grp, NULL, quad, 6);
}
{
/* Perform min/max down-sample. */
DRW_PASS_CREATE(psl->maxz_downlevel_ps, downsample_write);
grp = DRW_shgroup_create(EEVEE_shaders_effect_maxz_downlevel_sh_get(), psl->maxz_downlevel_ps);
DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &txl->maxzbuffer, GPU_SAMPLER_DEFAULT);
DRW_shgroup_uniform_vec2(grp, "texelSize", e_data.texel_size, 1);
DRW_shgroup_call(grp, quad, NULL);
/* Copy depth buffer to top level of HiZ */
DRW_PASS_CREATE(psl->maxz_copydepth_ps, downsample_write);
grp = DRW_shgroup_create(EEVEE_shaders_effect_maxz_copydepth_sh_get(), psl->maxz_copydepth_ps);
DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &e_data.depth_src, GPU_SAMPLER_DEFAULT);
DRW_shgroup_call(grp, quad, NULL);
DRW_PASS_CREATE(psl->maxz_copydepth_layer_ps, downsample_write);
grp = DRW_shgroup_create(EEVEE_shaders_effect_maxz_copydepth_layer_sh_get(),
psl->maxz_copydepth_layer_ps);
DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &e_data.depth_src, GPU_SAMPLER_DEFAULT);
DRW_shgroup_uniform_int(grp, "depthLayer", &e_data.depth_src_layer, 1);
DRW_shgroup_call(grp, quad, NULL);
}
if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
EEVEE_MotionBlurData *mb_data = &effects->motion_blur;
/* This pass compute camera motions to the non moving objects. */
DRW_PASS_CREATE(psl->velocity_resolve, DRW_STATE_WRITE_COLOR);
grp = DRW_shgroup_create(EEVEE_shaders_velocity_resolve_sh_get(), psl->velocity_resolve);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_uniform_mat4(grp, "prevViewProjMatrix", mb_data->camera[MB_PREV].persmat);
DRW_shgroup_uniform_mat4(grp, "currViewProjMatrixInv", mb_data->camera[MB_CURR].persinv);
DRW_shgroup_uniform_mat4(grp, "nextViewProjMatrix", mb_data->camera[MB_NEXT].persmat);
DRW_shgroup_call(grp, quad, NULL);
}
}
void EEVEE_effects_draw_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_EffectsInfo *effects = vedata->stl->effects;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
/**
* Setup double buffer so we can access last frame as it was before post processes.
*/
if ((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0) {
SETUP_BUFFER(txl->color_double_buffer, fbl->double_buffer_fb, fbl->double_buffer_color_fb);
}
else {
CLEANUP_BUFFER(txl->color_double_buffer, fbl->double_buffer_fb, fbl->double_buffer_color_fb);
}
/**
* Ping Pong buffer
*/
if ((effects->enabled_effects & EFFECT_POST_BUFFER) != 0) {
SETUP_BUFFER(txl->color_post, fbl->effect_fb, fbl->effect_color_fb);
}
else {
CLEANUP_BUFFER(txl->color_post, fbl->effect_fb, fbl->effect_color_fb);
}
}
#if 0 /* Not required for now */
static void min_downsample_cb(void *vedata, int UNUSED(level))
{
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
DRW_draw_pass(psl->minz_downlevel_ps);
}
#endif
static void max_downsample_cb(void *vedata, int level)
{
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
EEVEE_TextureList *txl = ((EEVEE_Data *)vedata)->txl;
int texture_size[3];
GPU_texture_get_mipmap_size(txl->maxzbuffer, level - 1, texture_size);
e_data.texel_size[0] = 1.0f / texture_size[0];
e_data.texel_size[1] = 1.0f / texture_size[1];
DRW_draw_pass(psl->maxz_downlevel_ps);
}
static void simple_downsample_cube_cb(void *vedata, int level)
{
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
e_data.texel_size[0] = (float)(1 << level) / (float)GPU_texture_width(e_data.color_src);
e_data.texel_size[1] = e_data.texel_size[0];
DRW_draw_pass(psl->color_downsample_cube_ps);
}
void EEVEE_create_minmax_buffer(EEVEE_Data *vedata, GPUTexture *depth_src, int layer)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_FramebufferList *fbl = vedata->fbl;
e_data.depth_src = depth_src;
e_data.depth_src_layer = layer;
DRW_stats_group_start("Max buffer");
/* Copy depth buffer to max texture top level */
GPU_framebuffer_bind(fbl->maxzbuffer_fb);
if (layer >= 0) {
DRW_draw_pass(psl->maxz_copydepth_layer_ps);
}
else {
DRW_draw_pass(psl->maxz_copydepth_ps);
}
/* Create lower levels */
GPU_framebuffer_recursive_downsample(
fbl->maxzbuffer_fb, MAX_SCREEN_BUFFERS_LOD_LEVEL, &max_downsample_cb, vedata);
DRW_stats_group_end();
/* Restore */
GPU_framebuffer_bind(fbl->main_fb);
if (GPU_mip_render_workaround() ||
GPU_type_matches(GPU_DEVICE_INTEL_UHD, GPU_OS_WIN, GPU_DRIVER_ANY)) {
/* Fix dot corruption on intel HD5XX/HD6XX series. */
GPU_flush();
}
}
static void downsample_radiance_cb(void *vedata, int level)
{
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
EEVEE_TextureList *txl = ((EEVEE_Data *)vedata)->txl;
int texture_size[3];
GPU_texture_get_mipmap_size(txl->filtered_radiance, level - 1, texture_size);
e_data.texel_size[0] = 1.0f / texture_size[0];
e_data.texel_size[1] = 1.0f / texture_size[1];
DRW_draw_pass(psl->color_downsample_ps);
}
/**
* Simple down-sampling algorithm. Reconstruct mip chain up to mip level.
*/
void EEVEE_effects_downsample_radiance_buffer(EEVEE_Data *vedata, GPUTexture *texture_src)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_FramebufferList *fbl = vedata->fbl;
e_data.color_src = texture_src;
DRW_stats_group_start("Downsample Radiance");
GPU_framebuffer_bind(fbl->radiance_filtered_fb);
DRW_draw_pass(psl->color_copy_ps);
GPU_framebuffer_recursive_downsample(
fbl->radiance_filtered_fb, MAX_SCREEN_BUFFERS_LOD_LEVEL, &downsample_radiance_cb, vedata);
DRW_stats_group_end();
}
/**
* Simple down-sampling algorithm for cube-map. Reconstruct mip chain up to mip level.
*/
void EEVEE_downsample_cube_buffer(EEVEE_Data *vedata, GPUTexture *texture_src, int level)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
e_data.color_src = texture_src;
/* Create lower levels */
DRW_stats_group_start("Downsample Cube buffer");
GPU_framebuffer_texture_attach(fbl->downsample_fb, texture_src, 0, 0);
GPU_framebuffer_recursive_downsample(
fbl->downsample_fb, level, &simple_downsample_cube_cb, vedata);
GPU_framebuffer_texture_detach(fbl->downsample_fb, texture_src);
DRW_stats_group_end();
}
static void EEVEE_velocity_resolve(EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
e_data.depth_src = dtxl->depth;
GPU_framebuffer_bind(fbl->velocity_resolve_fb);
DRW_draw_pass(psl->velocity_resolve);
if (psl->velocity_object) {
GPU_framebuffer_bind(fbl->velocity_fb);
DRW_draw_pass(psl->velocity_object);
}
}
}
void EEVEE_draw_effects(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_TextureList *txl = vedata->txl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
/* only once per frame after the first post process */
effects->swap_double_buffer = ((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0);
/* Init pointers */
effects->source_buffer = txl->color; /* latest updated texture */
effects->target_buffer = fbl->effect_color_fb; /* next target to render to */
/* Post process stack (order matters) */
EEVEE_velocity_resolve(vedata);
EEVEE_motion_blur_draw(vedata);
EEVEE_depth_of_field_draw(vedata);
/* NOTE: Lookdev drawing happens before TAA but after
* motion blur and dof to avoid distortions.
* Velocity resolve use a hack to exclude lookdev
* spheres from creating shimmering re-projection vectors. */
EEVEE_lookdev_draw(vedata);
EEVEE_temporal_sampling_draw(vedata);
EEVEE_bloom_draw(vedata);
/* Post effect render passes are done here just after the drawing of the effects and just before
* the swapping of the buffers. */
EEVEE_renderpasses_output_accumulate(sldata, vedata, true);
/* Save the final texture and framebuffer for final transformation or read. */
effects->final_tx = effects->source_buffer;
effects->final_fb = (effects->target_buffer != fbl->main_color_fb) ? fbl->main_fb :
fbl->effect_fb;
if ((effects->enabled_effects & EFFECT_TAA) && (effects->source_buffer == txl->taa_history)) {
effects->final_fb = fbl->taa_history_fb;
}
/* If no post processes is enabled, buffers are still not swapped, do it now. */
SWAP_DOUBLE_BUFFERS();
if (!stl->g_data->valid_double_buffer &&
((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0) &&
(DRW_state_is_image_render() == false)) {
/* If history buffer is not valid request another frame.
* This fix black reflections on area resize. */
DRW_viewport_request_redraw();
}
/* Record perspective matrix for the next frame. */
DRW_view_persmat_get(effects->taa_view, effects->prev_persmat, false);
/* Update double buffer status if render mode. */
if (DRW_state_is_image_render()) {
stl->g_data->valid_double_buffer = (txl->color_double_buffer != NULL);
stl->g_data->valid_taa_history = (txl->taa_history != NULL);
}
}

View File

@@ -22,599 +22,123 @@
#include "DRW_render.h"
#include "draw_color_management.h" /* TODO: remove dependency. */
#include "BLI_rand.h"
#include "BKE_object.h"
#include "DEG_depsgraph_query.h"
#include "DNA_world_types.h"
#include "IMB_imbuf.h"
#include "DRW_engine.h"
#include "GPU_framebuffer.h"
#include "eevee_engine.h"
#include "eevee_private.h"
#include "eevee_engine.h" /* own include */
typedef struct EEVEE_Data {
DrawEngineType *engine_type;
DRWViewportEmptyList *fbl;
DRWViewportEmptyList *txl;
DRWViewportEmptyList *psl;
DRWViewportEmptyList *stl;
struct EEVEE_Instance *instance_data;
} EEVEE_Data;
#define EEVEE_ENGINE "BLENDER_EEVEE"
/* *********** FUNCTIONS *********** */
static void eevee_engine_init(void *ved)
static void eevee_engine_init(void *vedata)
{
EEVEE_Data *vedata = (EEVEE_Data *)ved;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
EEVEE_Data *ved = (EEVEE_Data *)vedata;
const DRWContextState *draw_ctx = DRW_context_state_get();
View3D *v3d = draw_ctx->v3d;
RegionView3D *rv3d = draw_ctx->rv3d;
Object *camera = (rv3d->persp == RV3D_CAMOB) ? v3d->camera : NULL;
if (!stl->g_data) {
/* Alloc transient pointers */
stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__);
if (ved->instance_data == NULL) {
ved->instance_data = EEVEE_instance_alloc();
}
stl->g_data->use_color_render_settings = USE_SCENE_LIGHT(v3d) ||
!LOOK_DEV_STUDIO_LIGHT_ENABLED(v3d);
stl->g_data->background_alpha = DRW_state_draw_background() ? 1.0f : 0.0f;
stl->g_data->valid_double_buffer = (txl->color_double_buffer != NULL);
stl->g_data->valid_taa_history = (txl->taa_history != NULL);
stl->g_data->queued_shaders_count = 0;
stl->g_data->render_timesteps = 1;
/* Main Buffer */
DRW_texture_ensure_fullscreen_2d(&txl->color, GPU_RGBA16F, DRW_TEX_FILTER);
EEVEE_instance_init(ved->instance_data);
}
GPU_framebuffer_ensure_config(&fbl->main_fb,
{GPU_ATTACHMENT_TEXTURE(dtxl->depth),
GPU_ATTACHMENT_TEXTURE(txl->color),
GPU_ATTACHMENT_LEAVE,
GPU_ATTACHMENT_LEAVE,
GPU_ATTACHMENT_LEAVE,
GPU_ATTACHMENT_LEAVE});
GPU_framebuffer_ensure_config(&fbl->main_color_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->color)});
/* `EEVEE_renderpasses_init` will set the active render passes used by `EEVEE_effects_init`.
* `EEVEE_effects_init` needs to go second for TAA. */
EEVEE_renderpasses_init(vedata);
EEVEE_effects_init(sldata, vedata, camera, false);
EEVEE_materials_init(sldata, vedata, stl, fbl);
EEVEE_shadows_init(sldata);
EEVEE_lightprobes_init(sldata, vedata);
static void eevee_draw_scene(void *vedata)
{
EEVEE_instance_draw_viewport(((EEVEE_Data *)vedata)->instance_data);
}
static void eevee_cache_init(void *vedata)
{
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
EEVEE_bloom_cache_init(sldata, vedata);
EEVEE_depth_of_field_cache_init(sldata, vedata);
EEVEE_effects_cache_init(sldata, vedata);
EEVEE_lightprobes_cache_init(sldata, vedata);
EEVEE_lights_cache_init(sldata, vedata);
EEVEE_materials_cache_init(sldata, vedata);
EEVEE_motion_blur_cache_init(sldata, vedata);
EEVEE_occlusion_cache_init(sldata, vedata);
EEVEE_screen_raytrace_cache_init(sldata, vedata);
EEVEE_subsurface_cache_init(sldata, vedata);
EEVEE_temporal_sampling_cache_init(sldata, vedata);
EEVEE_volumes_cache_init(sldata, vedata);
EEVEE_instance_cache_init(((EEVEE_Data *)vedata)->instance_data);
}
void EEVEE_cache_populate(void *vedata, Object *ob)
static void eevee_cache_populate(void *vedata, Object *object)
{
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
const DRWContextState *draw_ctx = DRW_context_state_get();
const int ob_visibility = DRW_object_visibility_in_active_context(ob);
bool cast_shadow = false;
if (ob_visibility & OB_VISIBLE_PARTICLES) {
EEVEE_particle_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
}
if (DRW_object_is_renderable(ob) && (ob_visibility & OB_VISIBLE_SELF)) {
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) {
EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow);
}
else if (ob->type == OB_HAIR) {
EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
}
else if (ob->type == OB_VOLUME) {
EEVEE_volumes_cache_object_add(sldata, vedata, draw_ctx->scene, ob);
}
else if (!USE_SCENE_LIGHT(draw_ctx->v3d)) {
/* do not add any scene light sources to the cache */
}
else if (ob->type == OB_LIGHTPROBE) {
if ((ob->base_flag & BASE_FROM_DUPLI) != 0) {
/* TODO: Special case for dupli objects because we cannot save the object pointer. */
}
else {
EEVEE_lightprobes_cache_add(sldata, vedata, ob);
}
}
else if (ob->type == OB_LAMP) {
EEVEE_lights_cache_add(sldata, ob);
}
}
if (cast_shadow) {
EEVEE_shadows_caster_register(sldata, ob);
}
EEVEE_instance_cache_populate(((EEVEE_Data *)vedata)->instance_data, object);
}
static void eevee_cache_finish(void *vedata)
{
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
EEVEE_PrivateData *g_data = stl->g_data;
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
EEVEE_volumes_cache_finish(sldata, vedata);
EEVEE_materials_cache_finish(sldata, vedata);
EEVEE_lights_cache_finish(sldata, vedata);
EEVEE_lightprobes_cache_finish(sldata, vedata);
EEVEE_renderpasses_cache_finish(sldata, vedata);
EEVEE_subsurface_draw_init(sldata, vedata);
EEVEE_effects_draw_init(sldata, vedata);
EEVEE_volumes_draw_init(sldata, vedata);
uint tot_samples = scene_eval->eevee.taa_render_samples;
if (tot_samples == 0) {
/* Use a high number of samples so the outputs accumulation buffers
* will have the highest possible precision. */
tot_samples = 1024;
}
EEVEE_renderpasses_output_init(sldata, vedata, tot_samples);
/* Restart TAA if a shader has finish compiling. */
/* HACK: We should use notification of some sort from the compilation job instead. */
if (g_data->queued_shaders_count != g_data->queued_shaders_count_prev) {
g_data->queued_shaders_count_prev = g_data->queued_shaders_count;
EEVEE_temporal_sampling_reset(vedata);
}
}
/* As renders in an HDR off-screen buffer, we need draw everything once
* during the background pass. This way the other drawing callback between
* the background and the scene pass are visible.
* NOTE: we could break it up in two passes using some depth test
* to reduce the fill-rate. */
static void eevee_draw_scene(void *vedata)
{
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
EEVEE_FramebufferList *fbl = ((EEVEE_Data *)vedata)->fbl;
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
/* Default framebuffer and texture */
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
/* Sort transparents before the loop. */
DRW_pass_sort_shgroup_z(psl->transparent_pass);
/* Number of iteration: Use viewport taa_samples when using viewport rendering */
int loop_len = 1;
if (DRW_state_is_image_render()) {
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene = draw_ctx->scene;
loop_len = MAX2(1, scene->eevee.taa_samples);
}
if (stl->effects->bypass_drawing) {
loop_len = 0;
}
while (loop_len--) {
const float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float clear_depth = 1.0f;
uint clear_stencil = 0x0;
const uint primes[3] = {2, 3, 7};
double offset[3] = {0.0, 0.0, 0.0};
double r[3];
bool taa_use_reprojection = (stl->effects->enabled_effects & EFFECT_TAA_REPROJECT) != 0;
if (DRW_state_is_image_render() || taa_use_reprojection ||
((stl->effects->enabled_effects & EFFECT_TAA) != 0)) {
int samp = taa_use_reprojection ? stl->effects->taa_reproject_sample + 1 :
stl->effects->taa_current_sample;
BLI_halton_3d(primes, offset, samp, r);
EEVEE_update_noise(psl, fbl, r);
EEVEE_volumes_set_jitter(sldata, samp - 1);
EEVEE_materials_init(sldata, vedata, stl, fbl);
}
/* Copy previous persmat to UBO data */
copy_m4_m4(sldata->common_data.prev_persmat, stl->effects->prev_persmat);
/* Refresh Probes
* Shadows needs to be updated for correct probes */
DRW_stats_group_start("Probes Refresh");
EEVEE_shadows_update(sldata, vedata);
EEVEE_lightprobes_refresh(sldata, vedata);
EEVEE_lightprobes_refresh_planar(sldata, vedata);
DRW_stats_group_end();
/* Refresh shadows */
DRW_stats_group_start("Shadows");
EEVEE_shadows_draw(sldata, vedata, stl->effects->taa_view);
DRW_stats_group_end();
if (((stl->effects->enabled_effects & EFFECT_TAA) != 0) &&
(stl->effects->taa_current_sample > 1) && !DRW_state_is_image_render() &&
!taa_use_reprojection) {
DRW_view_set_active(stl->effects->taa_view);
}
/* when doing viewport rendering the overrides needs to be recalculated for
* every loop as this normally happens once inside
* `EEVEE_temporal_sampling_init` */
else if (((stl->effects->enabled_effects & EFFECT_TAA) != 0) &&
(stl->effects->taa_current_sample > 1) && DRW_state_is_image_render()) {
EEVEE_temporal_sampling_update_matrices(vedata);
}
/* Set ray type. */
sldata->common_data.ray_type = EEVEE_RAY_CAMERA;
sldata->common_data.ray_depth = 0.0f;
GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
GPU_framebuffer_bind(fbl->main_fb);
eGPUFrameBufferBits clear_bits = GPU_DEPTH_BIT;
SET_FLAG_FROM_TEST(clear_bits, !DRW_state_draw_background(), GPU_COLOR_BIT);
SET_FLAG_FROM_TEST(clear_bits, (stl->effects->enabled_effects & EFFECT_SSS), GPU_STENCIL_BIT);
GPU_framebuffer_clear(fbl->main_fb, clear_bits, clear_col, clear_depth, clear_stencil);
/* Depth prepass */
DRW_stats_group_start("Prepass");
DRW_draw_pass(psl->depth_ps);
DRW_stats_group_end();
/* Create minmax texture */
DRW_stats_group_start("Main MinMax buffer");
EEVEE_create_minmax_buffer(vedata, dtxl->depth, -1);
DRW_stats_group_end();
EEVEE_occlusion_compute(sldata, vedata);
EEVEE_volumes_compute(sldata, vedata);
/* Shading pass */
DRW_stats_group_start("Shading");
if (DRW_state_draw_background()) {
DRW_draw_pass(psl->background_ps);
}
DRW_draw_pass(psl->material_ps);
EEVEE_subsurface_data_render(sldata, vedata);
DRW_stats_group_end();
/* Effects pre-transparency */
EEVEE_subsurface_compute(sldata, vedata);
EEVEE_reflection_compute(sldata, vedata);
EEVEE_occlusion_draw_debug(sldata, vedata);
if (psl->probe_display) {
DRW_draw_pass(psl->probe_display);
}
EEVEE_refraction_compute(sldata, vedata);
/* Opaque refraction */
DRW_stats_group_start("Opaque Refraction");
DRW_draw_pass(psl->depth_refract_ps);
DRW_draw_pass(psl->material_refract_ps);
DRW_stats_group_end();
/* Volumetrics Resolve Opaque */
EEVEE_volumes_resolve(sldata, vedata);
/* Renderpasses */
EEVEE_renderpasses_output_accumulate(sldata, vedata, false);
/* Transparent */
/* TODO(fclem): should be its own Frame-buffer.
* This is needed because dualsource blending only works with 1 color buffer. */
GPU_framebuffer_texture_attach(fbl->main_color_fb, dtxl->depth, 0, 0);
GPU_framebuffer_bind(fbl->main_color_fb);
DRW_draw_pass(psl->transparent_pass);
GPU_framebuffer_bind(fbl->main_fb);
GPU_framebuffer_texture_detach(fbl->main_color_fb, dtxl->depth);
/* Post Process */
DRW_stats_group_start("Post FX");
EEVEE_draw_effects(sldata, vedata);
DRW_stats_group_end();
DRW_view_set_active(NULL);
if (DRW_state_is_image_render() && (stl->effects->enabled_effects & EFFECT_SSR) &&
!stl->effects->ssr_was_valid_double_buffer) {
/* SSR needs one iteration to start properly. */
loop_len++;
/* Reset sampling (and accumulation) after the first sample to avoid
* washed out first bounce for SSR. */
EEVEE_temporal_sampling_reset(vedata);
stl->effects->ssr_was_valid_double_buffer = stl->g_data->valid_double_buffer;
}
}
if ((stl->g_data->render_passes & EEVEE_RENDER_PASS_COMBINED) != 0) {
/* Transfer result to default framebuffer. */
GPU_framebuffer_bind(dfbl->default_fb);
DRW_transform_none(stl->effects->final_tx);
}
else {
EEVEE_renderpasses_draw(sldata, vedata);
}
if (stl->effects->bypass_drawing) {
/* Restore the depth from sample 1. */
GPU_framebuffer_blit(fbl->double_buffer_depth_fb, 0, dfbl->default_fb, 0, GPU_DEPTH_BIT);
}
EEVEE_renderpasses_draw_debug(vedata);
EEVEE_volumes_free_smoke_textures();
stl->g_data->view_updated = false;
DRW_view_set_active(NULL);
}
static void eevee_view_update(void *vedata)
{
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
if (stl->g_data) {
stl->g_data->view_updated = true;
}
}
static void eevee_id_object_update(void *UNUSED(vedata), Object *object)
{
EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(object);
if (ped != NULL && ped->dd.recalc != 0) {
ped->need_update = (ped->dd.recalc & ID_RECALC_TRANSFORM) != 0;
ped->dd.recalc = 0;
}
EEVEE_LightEngineData *led = EEVEE_light_data_get(object);
if (led != NULL && led->dd.recalc != 0) {
led->need_update = true;
led->dd.recalc = 0;
}
EEVEE_ObjectEngineData *oedata = EEVEE_object_data_get(object);
if (oedata != NULL && oedata->dd.recalc != 0) {
oedata->need_update = true;
oedata->geom_update = (oedata->dd.recalc & (ID_RECALC_GEOMETRY)) != 0;
oedata->dd.recalc = 0;
}
}
static void eevee_id_world_update(void *vedata, World *wo)
{
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
LightCache *lcache = stl->g_data->light_cache;
if (ELEM(lcache, NULL, stl->lookdev_lightcache)) {
/* Avoid Lookdev viewport clearing the update flag (see T67741). */
return;
}
EEVEE_WorldEngineData *wedata = EEVEE_world_data_ensure(wo);
if (wedata != NULL && wedata->dd.recalc != 0) {
if ((lcache->flag & LIGHTCACHE_BAKING) == 0) {
lcache->flag |= LIGHTCACHE_UPDATE_WORLD;
}
wedata->dd.recalc = 0;
}
}
void eevee_id_update(void *vedata, ID *id)
{
/* Handle updates based on ID type. */
switch (GS(id->name)) {
case ID_WO:
eevee_id_world_update(vedata, (World *)id);
break;
case ID_OB:
eevee_id_object_update(vedata, (Object *)id);
break;
default:
/* pass */
break;
}
}
static void eevee_render_reset_passes(EEVEE_Data *vedata)
{
/* Reset passlist. This is safe as they are stored into managed memory chunks. */
memset(vedata->psl, 0, sizeof(*vedata->psl));
}
static void eevee_render_to_image(void *vedata,
RenderEngine *engine,
struct RenderLayer *render_layer,
const rcti *rect)
{
EEVEE_Data *ved = (EEVEE_Data *)vedata;
const DRWContextState *draw_ctx = DRW_context_state_get();
Depsgraph *depsgraph = draw_ctx->depsgraph;
Scene *scene = DEG_get_evaluated_scene(depsgraph);
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
const bool do_motion_blur = (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0;
const bool do_motion_blur_fx = do_motion_blur && (scene->eevee.motion_blur_max > 0);
if (!EEVEE_render_init(vedata, engine, depsgraph)) {
return;
}
EEVEE_PrivateData *g_data = ved->stl->g_data;
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_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) {
case SCE_EEVEE_MB_START:
/* No offset. */
break;
case SCE_EEVEE_MB_CENTER:
time -= shuttertime * 0.5f;
break;
case SCE_EEVEE_MB_END:
time -= shuttertime;
break;
default:
BLI_assert_msg(0, "Invalid motion blur position enum!");
break;
}
float time_step = shuttertime / time_steps_tot;
for (int i = 0; i < time_steps_tot && !RE_engine_test_break(engine); i++) {
float time_prev = time;
float time_curr = time + time_step * 0.5f;
float time_next = time + time_step;
time += time_step;
/* Previous motion step. */
if (do_motion_blur_fx) {
if (i == 0) {
EEVEE_motion_blur_step_set(ved, MB_PREV);
DRW_render_set_time(engine, depsgraph, floorf(time_prev), fractf(time_prev));
EEVEE_render_modules_init(vedata, engine, depsgraph);
sldata = EEVEE_view_layer_data_ensure();
EEVEE_render_cache_init(sldata, vedata);
DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
EEVEE_motion_blur_cache_finish(vedata);
EEVEE_materials_cache_finish(sldata, vedata);
eevee_render_reset_passes(vedata);
}
}
/* Next motion step. */
if (do_motion_blur_fx) {
EEVEE_motion_blur_step_set(ved, MB_NEXT);
DRW_render_set_time(engine, depsgraph, floorf(time_next), fractf(time_next));
EEVEE_render_modules_init(vedata, engine, depsgraph);
sldata = EEVEE_view_layer_data_ensure();
EEVEE_render_cache_init(sldata, vedata);
DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
EEVEE_motion_blur_cache_finish(vedata);
EEVEE_materials_cache_finish(sldata, vedata);
eevee_render_reset_passes(vedata);
}
/* Current motion step. */
{
if (do_motion_blur) {
EEVEE_motion_blur_step_set(ved, MB_CURR);
DRW_render_set_time(engine, depsgraph, floorf(time_curr), fractf(time_curr));
EEVEE_render_modules_init(vedata, engine, depsgraph);
sldata = EEVEE_view_layer_data_ensure();
}
EEVEE_render_cache_init(sldata, vedata);
DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
EEVEE_motion_blur_cache_finish(vedata);
EEVEE_volumes_cache_finish(sldata, vedata);
EEVEE_materials_cache_finish(sldata, vedata);
EEVEE_lights_cache_finish(sldata, vedata);
EEVEE_lightprobes_cache_finish(sldata, vedata);
EEVEE_renderpasses_cache_finish(sldata, vedata);
EEVEE_subsurface_draw_init(sldata, vedata);
EEVEE_effects_draw_init(sldata, vedata);
EEVEE_volumes_draw_init(sldata, vedata);
}
/* Actual drawing. */
{
EEVEE_renderpasses_output_init(
sldata, vedata, g_data->render_sample_count_per_timestep * time_steps_tot);
if (scene->world) {
/* Update world in case of animated world material. */
eevee_id_world_update(vedata, scene->world);
}
EEVEE_temporal_sampling_create_view(vedata);
EEVEE_render_draw(vedata, engine, render_layer, rect);
if (i < time_steps_tot - 1) {
/* Don't reset after the last loop. Since EEVEE_render_read_result
* might need some DRWPasses. */
DRW_cache_restart();
}
}
if (do_motion_blur_fx) {
/* The previous step of next iteration N is exactly the next step of this iteration N - 1.
* So we just swap the resources to avoid too much re-evaluation.
* Note that this also clears the VBO references from the GPUBatches of deformed
* geometries. */
EEVEE_motion_blur_swap_data(vedata);
}
}
EEVEE_volumes_free_smoke_textures();
EEVEE_motion_blur_data_free(&ved->stl->effects->motion_blur);
if (RE_engine_test_break(engine)) {
return;
}
EEVEE_render_read_result(vedata, engine, render_layer, rect);
/* Restore original viewport size. */
DRW_render_viewport_size_set((int[2]){g_data->size_orig[0], g_data->size_orig[1]});
if (CFRA != initial_frame || SUBFRA != initial_subframe) {
/* Restore original frame number. This is because the render pipeline expects it. */
RE_engine_frame_set(engine, initial_frame, initial_subframe);
}
}
static void eevee_store_metadata(void *vedata, struct RenderResult *render_result)
{
EEVEE_Data *ved = (EEVEE_Data *)vedata;
EEVEE_PrivateData *g_data = ved->stl->g_data;
if (g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) {
EEVEE_cryptomatte_store_metadata(ved, render_result);
EEVEE_cryptomatte_free(ved);
}
EEVEE_instance_cache_finish(((EEVEE_Data *)vedata)->instance_data);
}
static void eevee_engine_free(void)
{
EEVEE_shaders_free();
EEVEE_lightprobes_free();
EEVEE_materials_free();
EEVEE_occlusion_free();
EEVEE_volumes_free();
EEVEE_shared_data_free();
}
static void eevee_instance_free(void *instance_data)
{
EEVEE_instance_free((struct EEVEE_Instance *)instance_data);
}
static void eevee_render_to_image(void *UNUSED(vedata),
struct RenderEngine *engine,
struct RenderLayer *layer,
const struct rcti *UNUSED(rect))
{
struct EEVEE_Instance *instance = EEVEE_instance_alloc();
EEVEE_instance_render_frame(instance, engine, layer);
EEVEE_instance_free(instance);
}
static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
{
RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA);
#define CHECK_PASS_LEGACY(name, type, channels, chanid) \
if (view_layer->passflag & (SCE_PASS_##name)) { \
RE_engine_register_pass( \
engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
} \
((void)0)
#define CHECK_PASS_EEVEE(name, type, channels, chanid) \
if (view_layer->eevee.render_passes & (EEVEE_RENDER_PASS_##name)) { \
RE_engine_register_pass( \
engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
} \
((void)0)
CHECK_PASS_LEGACY(Z, SOCK_FLOAT, 1, "Z");
CHECK_PASS_LEGACY(MIST, SOCK_FLOAT, 1, "Z");
CHECK_PASS_LEGACY(NORMAL, SOCK_VECTOR, 3, "XYZ");
CHECK_PASS_LEGACY(VECTOR, SOCK_RGBA, 4, "RGBA");
CHECK_PASS_LEGACY(DIFFUSE_DIRECT, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(DIFFUSE_COLOR, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(GLOSSY_COLOR, SOCK_RGBA, 3, "RGB");
CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(SHADOW, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(AO, SOCK_RGBA, 3, "RGB");
CHECK_PASS_EEVEE(BLOOM, SOCK_RGBA, 3, "RGB");
LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
if ((aov->flag & AOV_CONFLICT) != 0) {
continue;
}
switch (aov->type) {
case AOV_TYPE_COLOR:
RE_engine_register_pass(engine, scene, view_layer, aov->name, 4, "RGBA", SOCK_RGBA);
break;
case AOV_TYPE_VALUE:
RE_engine_register_pass(engine, scene, view_layer, aov->name, 1, "X", SOCK_FLOAT);
break;
default:
break;
}
}
// EEVEE_cryptomatte_update_passes(engine, scene, view_layer);
#undef CHECK_PASS_LEGACY
#undef CHECK_PASS_EEVEE
}
static const DrawEngineDataSize eevee_data_size = DRW_VIEWPORT_DATA_SIZE(EEVEE_Data);
@@ -626,16 +150,19 @@ DrawEngineType draw_engine_eevee_type = {
&eevee_data_size,
&eevee_engine_init,
&eevee_engine_free,
&eevee_instance_free,
&eevee_cache_init,
&EEVEE_cache_populate,
&eevee_cache_populate,
&eevee_cache_finish,
&eevee_draw_scene,
&eevee_view_update,
&eevee_id_update,
NULL,
NULL,
&eevee_render_to_image,
&eevee_store_metadata,
NULL,
};
#define EEVEE_ENGINE "BLENDER_EEVEE"
RenderEngineType DRW_engine_viewport_eevee_type = {
NULL,
NULL,
@@ -650,7 +177,7 @@ RenderEngineType DRW_engine_viewport_eevee_type = {
NULL,
NULL,
NULL,
&EEVEE_render_update_passes,
&eevee_render_update_passes,
&draw_engine_eevee_type,
{NULL, NULL, NULL},
};

View File

@@ -0,0 +1,167 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
#include "BKE_global.h"
#include "BLI_rect.h"
#include "GPU_framebuffer.h"
#include "ED_view3d.h"
#include "DRW_render.h"
#include "eevee_private.h"
#include "eevee_instance.hh"
using namespace blender::eevee;
static ShaderModule *g_shader_module = nullptr;
/* -------------------------------------------------------------------- */
/** \name EEVEE Instance C interface
* \{ */
EEVEE_Instance *EEVEE_instance_alloc(void)
{
if (g_shader_module == nullptr) {
/* TODO(fclem) threadsafety. */
g_shader_module = new ShaderModule();
}
return reinterpret_cast<EEVEE_Instance *>(new Instance(*g_shader_module));
}
void EEVEE_instance_free(EEVEE_Instance *instance_)
{
Instance *instance = reinterpret_cast<Instance *>(instance_);
delete instance;
}
void EEVEE_instance_init(EEVEE_Instance *instance_)
{
Instance *instance = reinterpret_cast<Instance *>(instance_);
const DRWContextState *ctx_state = DRW_context_state_get();
Depsgraph *depsgraph = ctx_state->depsgraph;
Scene *scene = ctx_state->scene;
View3D *v3d = ctx_state->v3d;
const ARegion *region = ctx_state->region;
RegionView3D *rv3d = ctx_state->rv3d;
/* Scaling output to better see what happens with accumulation. */
int resolution_divider = (ELEM(G.debug_value, 1, 2)) ? 16 : 1;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
int size[2];
size[0] = divide_ceil_u(GPU_texture_width(dtxl->color), resolution_divider);
size[1] = divide_ceil_u(GPU_texture_height(dtxl->color), resolution_divider);
const DRWView *default_view = DRW_view_default_get();
Object *camera = nullptr;
/* Get render borders. */
rcti rect;
BLI_rcti_init(&rect, 0, size[0], 0, size[1]);
if (v3d) {
if (rv3d && (rv3d->persp == RV3D_CAMOB)) {
camera = v3d->camera;
}
if (v3d->flag2 & V3D_RENDER_BORDER) {
if (camera) {
rctf viewborder;
/* TODO(fclem) Might be better to get it from DRW. */
ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &viewborder, false);
float viewborder_sizex = BLI_rctf_size_x(&viewborder);
float viewborder_sizey = BLI_rctf_size_y(&viewborder);
rect.xmin = floorf(viewborder.xmin + (scene->r.border.xmin * viewborder_sizex));
rect.ymin = floorf(viewborder.ymin + (scene->r.border.ymin * viewborder_sizey));
rect.xmax = floorf(viewborder.xmin + (scene->r.border.xmax * viewborder_sizex));
rect.ymax = floorf(viewborder.ymin + (scene->r.border.ymax * viewborder_sizey));
}
else {
rect.xmin = v3d->render_border.xmin * size[0];
rect.ymin = v3d->render_border.ymin * size[1];
rect.xmax = v3d->render_border.xmax * size[0];
rect.ymax = v3d->render_border.ymax * size[1];
}
}
}
instance->init(
size, &rect, nullptr, depsgraph, nullptr, camera, nullptr, default_view, v3d, rv3d);
}
void EEVEE_instance_cache_init(EEVEE_Instance *instance_)
{
Instance *instance = reinterpret_cast<Instance *>(instance_);
instance->begin_sync();
}
void EEVEE_instance_cache_populate(EEVEE_Instance *instance_, Object *object)
{
Instance *instance = reinterpret_cast<Instance *>(instance_);
instance->object_sync(object);
}
void EEVEE_instance_cache_finish(EEVEE_Instance *instance_)
{
Instance *instance = reinterpret_cast<Instance *>(instance_);
instance->end_sync();
}
void EEVEE_instance_draw_viewport(EEVEE_Instance *instance_)
{
Instance *instance = reinterpret_cast<Instance *>(instance_);
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
instance->draw_viewport(dfbl);
}
void EEVEE_instance_render_frame(EEVEE_Instance *instance_,
struct RenderEngine *engine,
struct RenderLayer *render_layer)
{
Instance *instance = reinterpret_cast<Instance *>(instance_);
Render *render = engine->re;
Depsgraph *depsgraph = DRW_context_state_get()->depsgraph;
Object *camera_original_ob = RE_GetCamera(engine->re);
const char *viewname = RE_GetActiveRenderView(engine->re);
int size[2] = {engine->resolution_x, engine->resolution_y};
rctf view_rect;
rcti rect;
RE_GetViewPlane(render, &view_rect, &rect);
instance->init(size, &rect, engine, depsgraph, nullptr, camera_original_ob, render_layer);
instance->render_frame(render_layer, viewname);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name EEVEE Shaders C interface
* \{ */
void EEVEE_shared_data_free(void)
{
delete g_shader_module;
g_shader_module = nullptr;
}
/** \} */

View File

@@ -22,4 +22,12 @@
#pragma once
extern RenderEngineType DRW_engine_viewport_eevee_type;
#ifdef __cplusplus
extern "C" {
#endif
extern RenderEngineType DRW_engine_viewport_eevee_type;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,255 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* A film is a fullscreen buffer (usually at output extent)
* that will be able to accumulate sample in any distorted camera_type
* using a pixel filter.
*
* Input needs to be jittered so that the filter converges to the right result.
*/
#include "BLI_rect.h"
#include "GPU_framebuffer.h"
#include "GPU_texture.h"
#include "DRW_render.h"
#include "eevee_film.hh"
#include "eevee_instance.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name FilmData
* \{ */
static eGPUTextureFormat to_gpu_texture_format(eFilmDataType film_type)
{
switch (film_type) {
default:
case FILM_DATA_COLOR_LOG:
case FILM_DATA_COLOR:
case FILM_DATA_MOTION:
case FILM_DATA_VEC4:
return GPU_RGBA16F;
case FILM_DATA_FLOAT:
return GPU_R16F;
case FILM_DATA_VEC2:
return GPU_RG16F;
case FILM_DATA_NORMAL:
return GPU_RGB10_A2;
case FILM_DATA_DEPTH:
return GPU_R32F;
}
}
inline bool operator==(const FilmData &a, const FilmData &b)
{
return (a.extent == b.extent) && (a.offset == b.offset);
}
inline bool operator!=(const FilmData &a, const FilmData &b)
{
return !(a == b);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Film
* \{ */
void Film::init(const ivec2 &full_extent, const rcti *output_rect)
{
FilmData data = data_;
data.extent = ivec2(BLI_rcti_size_x(output_rect), BLI_rcti_size_y(output_rect));
data.offset = ivec2(output_rect->xmin, output_rect->ymin);
has_changed_ = data_ != data;
if (has_changed_) {
data_ = data;
inst_.sampling.reset();
}
data_.opacity = 1.0f;
data_.uv_scale = 1.0f / vec2(full_extent);
data_.uv_scale_inv = full_extent;
data_.uv_bias = data_.offset / vec2(full_extent);
}
void Film::sync(void)
{
char full_name[32];
for (int i = 0; i < 2; i++) {
if (data_tx_[i] == nullptr) {
eGPUTextureFormat tex_format = to_gpu_texture_format(data_.data_type);
SNPRINTF(full_name, "Film.%s.data", name_.c_str());
data_tx_[i].ensure(full_name, UNPACK2(data_.extent), 1, tex_format);
/* TODO(fclem) The weight texture could be shared between all similar accumulators. */
SNPRINTF(full_name, "Film.%s.weight", name_.c_str());
weight_tx_[i].ensure(full_name, UNPACK2(data_.extent), 1, GPU_R16F);
accumulation_fb_[i].ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(data_tx_[i]),
GPU_ATTACHMENT_TEXTURE(weight_tx_[i]));
}
}
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
{
SNPRINTF(full_name, "Film.%s.Accumulate", name_.c_str());
accumulate_ps_ = DRW_pass_create(full_name, DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(FILM_FILTER);
DRWShadingGroup *grp = DRW_shgroup_create(sh, accumulate_ps_);
DRW_shgroup_uniform_block(grp, "film_block", data_.ubo_get());
DRW_shgroup_uniform_block(grp, "camera_block", inst_.camera.ubo_get());
DRW_shgroup_uniform_texture_ref_ex(grp, "input_tx", &input_tx_, no_filter);
DRW_shgroup_uniform_texture_ref_ex(grp, "data_tx", &data_tx_[0], no_filter);
DRW_shgroup_uniform_texture_ref_ex(grp, "weight_tx", &weight_tx_[0], no_filter);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
{
SNPRINTF(full_name, "Film.%s.Resolve", name_.c_str());
DRWState state = DRW_STATE_WRITE_COLOR;
eShaderType sh_type = FILM_RESOLVE;
if (data_.data_type == FILM_DATA_DEPTH) {
state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS;
sh_type = FILM_RESOLVE_DEPTH;
}
resolve_ps_ = DRW_pass_create(full_name, state);
GPUShader *sh = inst_.shaders.static_shader_get(sh_type);
DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_);
DRW_shgroup_uniform_block(grp, "film_block", data_.ubo_get());
DRW_shgroup_uniform_texture_ref_ex(grp, "first_sample_tx", &first_sample_ref_, no_filter);
DRW_shgroup_uniform_texture_ref_ex(grp, "data_tx", &data_tx_[0], no_filter);
DRW_shgroup_uniform_texture_ref_ex(grp, "weight_tx", &weight_tx_[0], no_filter);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
}
void Film::end_sync()
{
/* TODO reprojection. */
if (inst_.sampling.is_reset()) {
data_.use_history = 0;
}
if (inst_.is_viewport()) {
data_.opacity = inst_.sampling.viewport_smoothing_opacity_factor_get();
}
if (data_.use_history == 0 || inst_.is_viewport()) {
data_.push_update();
}
const bool is_first_sample = (inst_.sampling.sample_get() == 1);
if (do_smooth_viewport_smooth_transition() && (data_.opacity < 1.0f || is_first_sample)) {
char full_name[32];
SNPRINTF(full_name, "Film.%s.first_sample", name_.c_str());
eGPUTextureFormat tex_format = GPU_texture_format(DRW_viewport_texture_list_get()->color);
first_sample_tx_.ensure(full_name, UNPACK2(data_.extent), 1, tex_format);
first_sample_ref_ = first_sample_tx_;
}
else {
/* Reuse the data_tx since there is no need to blend. */
first_sample_tx_.release();
first_sample_ref_ = data_tx_[0];
}
}
void Film::accumulate(GPUTexture *input, const DRWView *view)
{
input_tx_ = input;
DRW_view_set_active(view);
GPU_framebuffer_bind(accumulation_fb_[1]);
DRW_draw_pass(accumulate_ps_);
SWAP(Framebuffer, accumulation_fb_[0], accumulation_fb_[1]);
SWAP(Texture, data_tx_[0], data_tx_[1]);
SWAP(Texture, weight_tx_[0], weight_tx_[1]);
/* Use history after first sample. */
if (data_.use_history == 0) {
data_.use_history = 1;
data_.push_update();
}
}
void Film::resolve_viewport(GPUFrameBuffer *target)
{
int viewport[4];
GPU_framebuffer_bind(target);
GPU_framebuffer_viewport_get(target, viewport);
const bool use_render_border = (data_.offset[0] > 0) || (data_.offset[1] > 0) ||
(data_.extent[0] < viewport[2]) ||
(data_.extent[1] < viewport[3]);
if (use_render_border) {
if (has_changed_) {
/* Film is cropped and does not fill the view completely. Clear the background. */
if (data_.data_type == FILM_DATA_DEPTH) {
GPU_framebuffer_clear_depth(target, 1.0f);
}
else {
float color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_framebuffer_clear_color(target, color);
}
}
GPU_framebuffer_viewport_set(target, UNPACK2(data_.offset), UNPACK2(data_.extent));
}
DRW_draw_pass(resolve_ps_);
/* Minus one because we already incremented it in step() which is the first
* thing to happen in the sample loop. */
const bool is_first_sample = (inst_.sampling.sample_get() - 1 == 1);
const bool is_only_one_sample = is_first_sample && inst_.sampling.finished();
if (is_first_sample && !is_only_one_sample && do_smooth_viewport_smooth_transition()) {
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
GPU_texture_copy(first_sample_tx_, dtxl->color);
}
if (use_render_border) {
GPU_framebuffer_viewport_reset(target);
}
}
void Film::read_result(float *data)
{
/* Resolve onto the next data texture. */
read_result_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(data_tx_[1]));
GPU_framebuffer_bind(read_result_fb_);
DRW_draw_pass(resolve_ps_);
eGPUTextureFormat format = to_gpu_texture_format(data_.data_type);
int channel_count = GPU_texture_component_len(format);
GPU_framebuffer_read_color(
read_result_fb_, 0, 0, UNPACK2(data_.extent), channel_count, 0, GPU_DATA_FLOAT, data);
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,112 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* A film is a fullscreen buffer (usually at output extent)
* that will be able to accumulate sample in any distorted camera_type
* using a pixel filter.
*
* Input needs to be jittered so that the filter converges to the right result.
*/
#pragma once
#include "DRW_render.h"
#include "eevee_camera.hh"
#include "eevee_sampling.hh"
#include "eevee_shader.hh"
#include "eevee_wrapper.hh"
namespace blender::eevee {
class Instance;
/* -------------------------------------------------------------------- */
/** \name Film
* \{ */
class Film {
private:
Instance &inst_;
/** Owned resources. */
eevee::Framebuffer read_result_fb_;
eevee::Framebuffer accumulation_fb_[2];
eevee::Texture data_tx_[2];
eevee::Texture weight_tx_[2];
/** First sample in case we need to blend using it or just reuse it. */
eevee::Texture first_sample_tx_;
/** Reference to first_sample_tx_ or data_tx_ depending on the context. */
GPUTexture *first_sample_ref_;
// DRWPass *clear_ps_ = nullptr;
DRWPass *accumulate_ps_ = nullptr;
DRWPass *resolve_ps_ = nullptr;
/** Shader parameter, not allocated. */
GPUTexture *input_tx_;
/** ViewProjection matrix used to render the input. */
// float src_persmat_[4][4];
/** ViewProjection matrix Inverse used to render the input. */
// float src_persinv_[4][4];
StructBuffer<FilmData> data_;
/** True if offset or size changed. */
bool has_changed_ = true;
/** Debug static name. */
StringRefNull name_;
public:
/* NOTE: name needs to be static. */
Film(Instance &inst, eFilmDataType data_type, const char *name) : inst_(inst), name_(name)
{
data_.extent[0] = data_.extent[1] = -1;
data_.data_type = data_type;
data_.use_history = 0;
}
~Film(){};
void init(const ivec2 &full_extent, const rcti *output_rect);
void sync(void);
void end_sync(void);
void accumulate(GPUTexture *input, const DRWView *view);
void resolve_viewport(GPUFrameBuffer *target);
void read_result(float *data);
private:
bool do_smooth_viewport_smooth_transition(void)
{
return ELEM(data_.data_type, FILM_DATA_COLOR, FILM_DATA_COLOR_LOG) &&
!DRW_state_is_image_render();
}
};
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,153 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*/
#include "BKE_gpencil.h"
#include "BKE_object.h"
#include "DEG_depsgraph_query.h"
#include "DNA_gpencil_types.h"
#include "eevee_instance.hh"
namespace blender::eevee {
#define DO_BATCHING true
struct gpIterData {
Instance &inst;
Object *ob;
MaterialArray &material_array;
int cfra;
/* Drawcall batching. */
GPUBatch *geom = nullptr;
Material *material = nullptr;
int vfirst = 0;
int vcount = 0;
bool instancing = false;
gpIterData(Instance &inst_, Object *ob_)
: inst(inst_), ob(ob_), material_array(inst_.materials.material_array_get(ob_))
{
cfra = DEG_get_ctime(inst.depsgraph);
};
};
static void gpencil_drawcall_flush(gpIterData &iter)
{
if (iter.geom != NULL) {
shgroup_geometry_call(iter.material->shading.shgrp,
iter.ob,
iter.geom,
iter.vfirst,
iter.vcount,
iter.instancing);
shgroup_geometry_call(iter.material->prepass.shgrp,
iter.ob,
iter.geom,
iter.vfirst,
iter.vcount,
iter.instancing);
shgroup_geometry_call(iter.material->shadow.shgrp,
iter.ob,
iter.geom,
iter.vfirst,
iter.vcount,
iter.instancing);
}
iter.geom = NULL;
iter.vfirst = -1;
iter.vcount = 0;
}
/* Group draw-calls that are consecutive and with the same type. Reduces GPU driver overhead. */
static void gpencil_drawcall_add(gpIterData &iter,
GPUBatch *geom,
Material *material,
int v_first,
int v_count,
bool instancing)
{
int last = iter.vfirst + iter.vcount;
/* Interrupt draw-call grouping if the sequence is not consecutive. */
if (!DO_BATCHING || (geom != iter.geom) || (material != iter.material) || (v_first - last > 3)) {
gpencil_drawcall_flush(iter);
}
iter.geom = geom;
iter.material = material;
iter.instancing = instancing;
if (iter.vfirst == -1) {
iter.vfirst = v_first;
}
iter.vcount = v_first + v_count - iter.vfirst;
}
static void gpencil_stroke_sync(bGPDlayer *UNUSED(gpl),
bGPDframe *UNUSED(gpf),
bGPDstroke *gps,
void *thunk)
{
gpIterData &iter = *(gpIterData *)thunk;
Material *material = iter.material_array.materials[gps->mat_nr];
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter.ob, gps->mat_nr + 1);
bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
bool show_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0) ||
(!DRW_state_is_image_render() && ((gps->flag & GP_STROKE_NOFILL) != 0));
bool show_fill = (gps->tot_triangles > 0) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0);
if (hide_material) {
return;
}
if (show_fill) {
GPUBatch *geom = DRW_cache_gpencil_fills_get(iter.ob, iter.cfra);
int vfirst = gps->runtime.fill_start * 3;
int vcount = gps->tot_triangles * 3;
gpencil_drawcall_add(iter, geom, material, vfirst, vcount, false);
}
if (show_stroke) {
GPUBatch *geom = DRW_cache_gpencil_strokes_get(iter.ob, iter.cfra);
/* Start one vert before to have gl_InstanceID > 0 (see shader). */
int vfirst = gps->runtime.stroke_start - 1;
/* Include "potential" cyclic vertex and start adj vertex (see shader). */
int vcount = gps->totpoints + 1 + 1;
gpencil_drawcall_add(iter, geom, material, vfirst, vcount, true);
}
}
void Instance::gpencil_sync(Object *ob, ObjectHandle &ob_handle)
{
gpIterData iter(*this, ob);
BKE_gpencil_visible_stroke_iter((bGPdata *)ob->data, nullptr, gpencil_stroke_sync, &iter);
gpencil_drawcall_flush(iter);
/* TODO(fclem) Gpencil velocity. */
// shading_passes.velocity.gpencil_add(ob, ob_handle);
shadows.sync_caster(ob, ob_handle);
}
} // namespace blender::eevee

View File

@@ -0,0 +1,74 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*/
#include "DNA_hair_types.h"
#include "DNA_modifier_types.h"
#include "DNA_particle_types.h"
#include "eevee_instance.hh"
namespace blender::eevee {
static void shgroup_hair_call(MaterialPass &matpass,
Object *ob,
ParticleSystem *part_sys = nullptr,
ModifierData *modifier_data = nullptr)
{
if (matpass.shgrp == nullptr) {
return;
}
DRW_shgroup_hair_create_sub(ob, part_sys, modifier_data, matpass.shgrp, matpass.gpumat);
}
void Instance::hair_sync(Object *ob, ObjectHandle &ob_handle, ModifierData *modifier_data)
{
int mat_nr = HAIR_MATERIAL_NR;
ParticleSystem *part_sys = nullptr;
if (modifier_data != nullptr) {
part_sys = reinterpret_cast<ParticleSystemModifierData *>(modifier_data)->psys;
if (!DRW_object_is_visible_psys_in_active_context(ob, part_sys)) {
return;
}
ParticleSettings *part_settings = part_sys->part;
const int draw_as = (part_settings->draw_as == PART_DRAW_REND) ? part_settings->ren_as :
part_settings->draw_as;
if (draw_as != PART_DRAW_PATH) {
return;
}
mat_nr = part_settings->omat;
}
Material &material = materials.material_get(ob, mat_nr - 1, MAT_GEOM_HAIR);
shgroup_hair_call(material.shading, ob, part_sys, modifier_data);
shgroup_hair_call(material.prepass, ob, part_sys, modifier_data);
shgroup_hair_call(material.shadow, ob, part_sys, modifier_data);
/* TODO(fclem) Hair velocity. */
// shading_passes.velocity.gpencil_add(ob, ob_handle);
if (material.shadow.shgrp) {
shadows.sync_caster(ob, ob_handle);
}
}
} // namespace blender::eevee

View File

@@ -0,0 +1,76 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* Structures to identify unique data blocks. The keys are unique so we are able to
* match ids across frame updates.
*/
#include "eevee_instance.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name Draw Data
*
* \{ */
static void draw_data_init_cb(struct DrawData *dd)
{
/* Object has just been created or was never evaluated by the engine. */
dd->recalc = ID_RECALC_ALL;
}
ObjectHandle &SyncModule::sync_object(Object *ob)
{
DrawEngineType *owner = (DrawEngineType *)&DRW_engine_viewport_eevee_type;
struct DrawData *dd = DRW_drawdata_ensure(
(ID *)ob, owner, sizeof(eevee::ObjectHandle), draw_data_init_cb, nullptr);
ObjectHandle &eevee_dd = *reinterpret_cast<ObjectHandle *>(dd);
if (eevee_dd.object_key.ob == nullptr) {
eevee_dd.object_key = ObjectKey(ob);
}
const int recalc_flags = ID_RECALC_COPY_ON_WRITE | ID_RECALC_TRANSFORM | ID_RECALC_SHADING |
ID_RECALC_GEOMETRY;
if ((eevee_dd.recalc & recalc_flags) != 0) {
inst_.sampling.reset();
}
return eevee_dd;
}
WorldHandle &SyncModule::sync_world(::World *world)
{
DrawEngineType *owner = (DrawEngineType *)&DRW_engine_viewport_eevee_type;
struct DrawData *dd = DRW_drawdata_ensure(
(ID *)world, owner, sizeof(eevee::WorldHandle), draw_data_init_cb, nullptr);
WorldHandle &eevee_dd = *reinterpret_cast<WorldHandle *>(dd);
const int recalc_flags = ID_RECALC_ALL;
if ((eevee_dd.recalc & recalc_flags) != 0) {
inst_.sampling.reset();
}
return eevee_dd;
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,285 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* Structures to identify unique data blocks. The keys are unique so we are able to
* match ids across frame updates.
*/
#pragma once
#include "BKE_duplilist.h"
#include "BLI_ghash.h"
#include "BLI_map.hh"
#include "DNA_object_types.h"
#include "GPU_material.h"
#include "eevee_engine.h"
namespace blender::eevee {
class Instance;
/* -------------------------------------------------------------------- */
/** \name ObjectKey
*
* \{ */
/** Unique key to identify each object in the hashmap. */
struct ObjectKey {
/** Hash value of the key. */
uint64_t hash_value;
/** Original Object or source object for duplis. */
Object *ob;
/** Original Parent object for duplis. */
Object *parent;
/** Dupli objects recursive unique identifier */
int id[MAX_DUPLI_RECUR];
/** If object uses particle system hair. */
bool use_particle_hair;
#ifdef DEBUG
char name[64];
#endif
ObjectKey(Object *ob_, Object *parent_, int id_[MAX_DUPLI_RECUR], bool use_particle_hair_)
: ob(ob_), parent(parent_), use_particle_hair(use_particle_hair_)
{
if (id_) {
memcpy(id, id_, sizeof(id));
}
else {
memset(id, 0, sizeof(id));
}
/* Compute hash on creation so we avoid the cost of it for every sync. */
hash_value = BLI_ghashutil_ptrhash(ob);
hash_value = BLI_ghashutil_combine_hash(hash_value, BLI_ghashutil_ptrhash(parent));
for (int i = 0; i < MAX_DUPLI_RECUR; i++) {
if (id[i] != 0) {
hash_value = BLI_ghashutil_combine_hash(hash_value, BLI_ghashutil_inthash(id[i]));
}
else {
break;
}
}
#ifdef DEBUG
STRNCPY(name, ob->id.name);
#endif
}
ObjectKey(Object *ob, DupliObject *dupli, Object *parent)
: ObjectKey(ob, parent, dupli ? dupli->persistent_id : nullptr, false){};
ObjectKey(Object *ob)
: ObjectKey(ob, DRW_object_get_dupli(ob), DRW_object_get_dupli_parent(ob)){};
uint64_t hash(void) const
{
return hash_value;
}
bool operator<(const ObjectKey &k) const
{
if (ob != k.ob) {
return (ob < k.ob);
}
if (parent != k.parent) {
return (parent < k.parent);
}
if (use_particle_hair != k.use_particle_hair) {
return (use_particle_hair < k.use_particle_hair);
}
return memcmp(id, k.id, sizeof(id)) < 0;
}
bool operator==(const ObjectKey &k) const
{
if (ob != k.ob) {
return false;
}
if (parent != k.parent) {
return false;
}
if (use_particle_hair != k.use_particle_hair) {
return false;
}
return memcmp(id, k.id, sizeof(id)) == 0;
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Draw Data
*
* \{ */
struct ObjectHandle : public DrawData {
ObjectKey object_key;
void reset_recalc_flag(void)
{
if (recalc != 0) {
recalc = 0;
}
}
};
struct WorldHandle : public DrawData {
void reset_recalc_flag(void)
{
if (recalc != 0) {
recalc = 0;
}
}
};
class SyncModule {
private:
Instance &inst_;
public:
SyncModule(Instance &inst) : inst_(inst){};
~SyncModule(){};
ObjectHandle &sync_object(Object *ob);
WorldHandle &sync_world(::World *world);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name MaterialKey
*
* \{ */
enum eMaterialPipeline {
MAT_PIPE_DEFERRED = 0,
MAT_PIPE_FORWARD = 1,
MAT_PIPE_DEFERRED_PREPASS = 2,
MAT_PIPE_FORWARD_PREPASS = 3,
MAT_PIPE_VOLUME = 4,
MAT_PIPE_SHADOW = 5,
};
enum eMaterialGeometry {
MAT_GEOM_MESH = 0,
MAT_GEOM_HAIR = 1,
MAT_GEOM_GPENCIL = 2,
MAT_GEOM_VOLUME = 3,
MAT_GEOM_WORLD = 4,
MAT_GEOM_LOOKDEV = 5,
};
static inline void material_type_from_shader_uuid(uint64_t shader_uuid,
eMaterialPipeline &pipeline_type,
eMaterialGeometry &geometry_type)
{
const uint64_t geometry_mask = ((1u << 3u) - 1u);
const uint64_t pipeline_mask = ((1u << 3u) - 1u);
geometry_type = static_cast<eMaterialGeometry>(shader_uuid & geometry_mask);
pipeline_type = static_cast<eMaterialPipeline>((shader_uuid >> 3u) & pipeline_mask);
}
static inline uint64_t shader_uuid_from_material_type(eMaterialPipeline pipeline_type,
eMaterialGeometry geometry_type)
{
return geometry_type | (pipeline_type << 3);
}
static inline eMaterialGeometry to_material_geometry(const Object *ob)
{
switch (ob->type) {
case OB_HAIR:
return MAT_GEOM_HAIR;
case OB_VOLUME:
return MAT_GEOM_VOLUME;
case OB_GPENCIL:
return MAT_GEOM_GPENCIL;
default:
return MAT_GEOM_MESH;
}
}
/** Unique key to identify each material in the hashmap. */
struct MaterialKey {
Material *mat;
uint64_t options;
MaterialKey(::Material *mat_, eMaterialGeometry geometry, eMaterialPipeline surface_pipeline)
: mat(mat_)
{
options = shader_uuid_from_material_type(surface_pipeline, geometry);
}
uint64_t hash(void) const
{
BLI_assert(options < sizeof(*mat));
return (uint64_t)mat + options;
}
bool operator<(const MaterialKey &k) const
{
return (mat < k.mat) || (options < k.options);
}
bool operator==(const MaterialKey &k) const
{
return (mat == k.mat) && (options == k.options);
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name ShaderKey
*
* \{ */
struct ShaderKey {
GPUShader *shader;
uint64_t options;
ShaderKey(GPUMaterial *gpumat,
eMaterialGeometry geometry,
eMaterialPipeline pipeline)
{
shader = GPU_material_get_shader(gpumat);
options = shader_uuid_from_material_type(pipeline, geometry);
}
uint64_t hash(void) const
{
return (uint64_t)shader + options;
}
bool operator<(const ShaderKey &k) const
{
return (shader < k.shader) || (options < k.options);
}
bool operator==(const ShaderKey &k) const
{
return (shader == k.shader) && (options == k.options);
}
};
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,309 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* An instance contains all structures needed to do a complete render.
*/
#include "BKE_object.h"
#include "BLI_rect.h"
#include "DEG_depsgraph_query.h"
#include "DNA_ID.h"
#include "DNA_lightprobe_types.h"
#include "DNA_modifier_types.h"
#include "eevee_instance.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name Init
*
* Init funcions need to be called once at the start of a frame.
* Active camera, render extent and enabled render passes are immutable until next init.
* This takes care of resizing output buffers and view in case a parameter changed.
* IMPORTANT: xxx.init() functions are NOT meant to acquire and allocate DRW resources.
* Any attempt to do so will likely produce use after free situations.
* \{ */
void Instance::init(const ivec2 &output_res,
const rcti *output_rect,
RenderEngine *render_,
Depsgraph *depsgraph_,
const struct LightProbe *light_probe_,
Object *camera_object_,
const RenderLayer *render_layer_,
const DRWView *drw_view_,
const View3D *v3d_,
const RegionView3D *rv3d_)
{
render = render_;
depsgraph = depsgraph_;
render_layer = render_layer_;
camera_orig_object = camera_object_;
drw_view = drw_view_;
v3d = v3d_;
rv3d = rv3d_;
baking_probe = light_probe_;
update_eval_members();
rcti render_border = output_crop(output_res, output_rect);
/* Needs to be first. */
sampling.init(scene);
camera.init();
motion_blur.init();
render_passes.init(output_res, &render_border);
main_view.init(output_res);
velocity.init();
shadows.init();
lightprobes.init();
lookdev.init(output_res, &render_border);
}
rcti Instance::output_crop(const int res[2], const rcti *crop)
{
rcti rect;
BLI_rcti_init(&rect, 0, res[0], 0, res[1]);
/* Clip the render border to region bounds. */
BLI_rcti_isect(crop, &rect, &rect);
if (BLI_rcti_is_empty(&rect)) {
BLI_rcti_init(&rect, 0, res[0], 0, res[1]);
}
return rect;
}
void Instance::set_time(float time)
{
BLI_assert(render);
DRW_render_set_time(render, depsgraph, floorf(time), fractf(time));
update_eval_members();
}
void Instance::update_eval_members(void)
{
scene = DEG_get_evaluated_scene(depsgraph);
view_layer = DEG_get_evaluated_view_layer(depsgraph);
camera_eval_object = (camera_orig_object) ?
DEG_get_evaluated_object(depsgraph, camera_orig_object) :
nullptr;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Sync
*
* Sync will gather data from the scene that can change over a time step (i.e: motion steps).
* IMPORTANT: xxx.sync() functions area responsible for creating DRW resources (i.e: DRWView) as
* well as querying temp texture pool. All DRWPasses should be ready by the end end_sync().
* \{ */
void Instance::begin_sync()
{
camera.sync();
render_passes.sync();
shading_passes.sync();
main_view.sync();
world.sync();
lookdev.sync_background();
lookdev.sync_overlay();
materials.begin_sync();
velocity.begin_sync();
lights.begin_sync();
lightprobes.begin_sync();
}
void Instance::object_sync(Object *ob)
{
const bool is_renderable_type = ELEM(
ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_LAMP, OB_VOLUME, OB_GPENCIL);
const int ob_visibility = DRW_object_visibility_in_active_context(ob);
const bool partsys_is_visible = (ob_visibility & OB_VISIBLE_PARTICLES) != 0 &&
(ob->type == OB_MESH);
const bool object_is_visible = DRW_object_is_renderable(ob) &&
(ob_visibility & OB_VISIBLE_SELF) != 0;
if (!is_renderable_type || (!partsys_is_visible && !object_is_visible)) {
return;
}
ObjectHandle &ob_handle = sync.sync_object(ob);
if (partsys_is_visible && ob != DRW_context_state_get()->object_edit) {
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type == eModifierType_ParticleSystem) {
hair_sync(ob, ob_handle, md);
}
}
}
if (object_is_visible) {
switch (ob->type) {
case OB_LAMP:
lights.sync_light(ob, ob_handle);
break;
case OB_MESH:
case OB_CURVE:
case OB_SURF:
case OB_FONT:
case OB_MBALL: {
mesh_sync(ob, ob_handle);
break;
}
case OB_VOLUME:
shading_passes.deferred.volume_add(ob);
break;
case OB_HAIR:
hair_sync(ob, ob_handle);
break;
case OB_GPENCIL:
gpencil_sync(ob, ob_handle);
break;
default:
break;
}
}
ob_handle.reset_recalc_flag();
}
/* Wrapper to use with DRW_render_object_iter. */
void Instance::object_sync_render(void *instance_,
Object *ob,
RenderEngine *engine,
Depsgraph *depsgraph)
{
UNUSED_VARS(engine, depsgraph);
Instance &inst = *reinterpret_cast<Instance *>(instance_);
if (inst.baking_probe != nullptr) {
if (inst.baking_probe->visibility_grp != nullptr) {
bool test = BKE_collection_has_object_recursive(inst.baking_probe->visibility_grp, ob);
test = (inst.baking_probe->flag & LIGHTPROBE_FLAG_INVERT_GROUP) ? !test : test;
if (!test) {
return;
}
}
/* Exclude planar lightprobes. */
if (ob->type == OB_LIGHTPROBE) {
LightProbe *prb = (LightProbe *)ob->data;
if (prb->type == LIGHTPROBE_TYPE_PLANAR) {
return;
}
}
}
inst.object_sync(ob);
}
void Instance::end_sync(void)
{
velocity.end_sync();
lights.end_sync();
sampling.end_sync();
render_passes.end_sync();
lightprobes.end_sync();
}
void Instance::render_sync(void)
{
DRW_cache_restart();
this->begin_sync();
DRW_render_object_iter(this, render, depsgraph, object_sync_render);
this->end_sync();
DRW_render_instance_buffer_finish();
/* Also we weed to have a correct fbo bound for DRW_hair_update */
// GPU_framebuffer_bind();
// DRW_hair_update();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Rendering
* \{ */
/**
* Conceptually renders one sample per pixel.
* Everything based on random sampling should be done here (i.e: DRWViews jitter)
**/
void Instance::render_sample(void)
{
if (sampling.finished()) {
return;
}
/* Motion blur may need to do re-sync after a certain number of sample. */
if (sampling.do_render_sync()) {
this->render_sync();
}
sampling.step();
/* TODO update shadowmaps, planars, etc... */
// shadow_view_.render();
main_view.render();
motion_blur.step();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Interface
* \{ */
void Instance::render_frame(RenderLayer *render_layer, const char *view_name)
{
while (!sampling.finished()) {
this->render_sample();
/* TODO(fclem) print progression. */
}
render_passes.read_result(render_layer, view_name);
}
void Instance::draw_viewport(DefaultFramebufferList *dfbl)
{
this->render_sample();
render_passes.resolve_viewport(dfbl);
if (!sampling.finished_viewport()) {
DRW_viewport_request_redraw();
}
}
bool Instance::finished(void) const
{
return sampling.finished();
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,179 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* An instance contains all structures needed to do a complete render.
*/
#pragma once
#include "BKE_object.h"
#include "DEG_depsgraph.h"
#include "DRW_render.h"
#include "eevee_film.hh"
#include "eevee_id_map.hh"
#include "eevee_light.hh"
#include "eevee_lightprobe.hh"
#include "eevee_lookdev.hh"
#include "eevee_material.hh"
#include "eevee_motion_blur.hh"
#include "eevee_renderpasses.hh"
#include "eevee_sampling.hh"
#include "eevee_shader.hh"
#include "eevee_shading.hh"
#include "eevee_shadow.hh"
#include "eevee_view.hh"
#include "eevee_world.hh"
#include "eevee_engine.h"
namespace blender::eevee {
/**
* \class Instance
* \brief A running instance of the engine.
*/
class Instance {
friend MotionBlur;
friend MotionBlurModule;
friend VelocityModule;
public:
ShaderModule &shaders;
Sampling sampling;
RenderPasses render_passes;
ShadingPasses shading_passes;
MainView main_view;
Camera camera;
World world;
VelocityModule velocity;
MotionBlurModule motion_blur;
LightModule lights;
LightProbeModule lightprobes;
/* TODO(fclem) Move it to scene layer data. */
ShadowModule shadows;
SyncModule sync;
MaterialModule materials;
/** Lookdev own lightweight instance. May not be allocated. */
LookDev lookdev;
/** Input data. */
Depsgraph *depsgraph;
/** Evaluated IDs. */
Scene *scene;
ViewLayer *view_layer;
Object *camera_eval_object;
Object *camera_orig_object;
/** Only available when rendering for final render. */
const RenderLayer *render_layer;
RenderEngine *render;
/** Only available when rendering for viewport. */
const DRWView *drw_view;
const View3D *v3d;
const RegionView3D *rv3d;
/** Can be null. Used to exclude objects during baking. */
const struct LightProbe *baking_probe = nullptr;
/* Info string displayed at the top of the render / viewport. */
char info[64];
public:
Instance(ShaderModule &shared_shaders)
: shaders(shared_shaders),
render_passes(*this),
shading_passes(*this),
main_view(*this),
camera(*this),
world(*this),
velocity(*this),
motion_blur(*this),
lights(*this),
lightprobes(*this),
shadows(*this),
sync(*this),
materials(*this),
lookdev(*this){};
~Instance(){};
void init(const ivec2 &output_res,
const rcti *output_rect,
RenderEngine *render,
Depsgraph *depsgraph,
const struct LightProbe *light_probe_ = nullptr,
Object *camera_object = nullptr,
const RenderLayer *render_layer = nullptr,
const DRWView *drw_view = nullptr,
const View3D *v3d = nullptr,
const RegionView3D *rv3d = nullptr);
void begin_sync(void);
void object_sync(Object *ob);
void end_sync(void);
void render_sync(void);
void render_frame(RenderLayer *render_layer, const char *view_name);
void draw_viewport(DefaultFramebufferList *dfbl);
bool finished(void) const;
bool is_viewport(void)
{
return !DRW_state_is_scene_render();
}
bool use_scene_light(void) const
{
return (!v3d) ||
((v3d->shading.type == OB_MATERIAL) &&
(v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS)) ||
((v3d->shading.type == OB_RENDER) &&
(v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER));
}
/* Do we light the scene using the HDRI setup in the viewport settings. */
bool use_studio_light(void) const
{
return (v3d) && (((v3d->shading.type == OB_MATERIAL) &&
((v3d->shading.flag & V3D_SHADING_SCENE_WORLD) == 0)) ||
((v3d->shading.type == OB_RENDER) &&
((v3d->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER) == 0)));
}
private:
void render_sample(void);
static void object_sync_render(void *instance_,
Object *ob,
RenderEngine *engine,
Depsgraph *depsgraph);
void mesh_sync(Object *ob, ObjectHandle &ob_handle);
void gpencil_sync(Object *ob, ObjectHandle &ob_handle);
void hair_sync(Object *ob, ObjectHandle &ob_handle, ModifierData *modifier_data = nullptr);
rcti output_crop(const int output_res[2], const rcti *crop);
void set_time(float time);
void update_eval_members(void);
};
} // namespace blender::eevee

View File

@@ -0,0 +1,405 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* The light module manages light data buffers and light culling system.
*/
#include "eevee_instance.hh"
#include "eevee_light.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name LightData
* \{ */
static eLightType to_light_type(short blender_light_type, short blender_area_type)
{
switch (blender_light_type) {
default:
case LA_LOCAL:
return LIGHT_POINT;
case LA_SUN:
return LIGHT_SUN;
case LA_SPOT:
return LIGHT_SPOT;
case LA_AREA:
return ELEM(blender_area_type, LA_AREA_DISK, LA_AREA_ELLIPSE) ? LIGHT_ELLIPSE : LIGHT_RECT;
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Light Object
* \{ */
void Light::sync(ShadowModule &shadows, const Object *ob, float threshold)
{
const ::Light *la = (const ::Light *)ob->data;
float scale[3];
float max_power = max_fff(la->r, la->g, la->b) * fabsf(la->energy / 100.0f);
float surface_max_power = max_ff(la->diff_fac, la->spec_fac) * max_power;
float volume_max_power = la->volume_fac * max_power;
float influence_radius_surface = attenuation_radius_get(la, threshold, surface_max_power);
float influence_radius_volume = attenuation_radius_get(la, threshold, volume_max_power);
this->influence_radius_max = max_ff(influence_radius_surface, influence_radius_volume);
this->influence_radius_invsqr_surface = (influence_radius_surface > 1e-8f) ?
(1.0f / square_f(influence_radius_surface)) :
0.0f;
this->influence_radius_invsqr_volume = (influence_radius_volume > 1e-8f) ?
(1.0f / square_f(influence_radius_volume)) :
0.0f;
this->color = vec3(&la->r) * la->energy;
normalize_m4_m4_ex(this->object_mat, ob->obmat, scale);
/* Make sure we have consistent handedness (in case of negatively scaled Z axis). */
vec3 cross = vec3::cross(this->_right, this->_up);
if (vec3::dot(cross, this->_back) < 0.0f) {
negate_v3(this->_up);
}
shape_parameters_set(la, scale);
float shape_power = shape_power_get(la);
this->diffuse_power = la->diff_fac * shape_power;
this->specular_power = la->spec_fac * shape_power;
this->volume_power = la->volume_fac * shape_power_volume_get(la);
eLightType new_type = to_light_type(la->type, la->area_shape);
if (this->type != new_type) {
shadow_discard_safe(shadows);
this->type = new_type;
}
if (la->mode & LA_SHADOW) {
if (la->type == LA_SUN) {
/* TODO */
// shadows.sync_directional_shadow()
}
else {
float cone_aperture = DEG2RAD(360.0);
if (la->type == LA_SPOT) {
cone_aperture = min_ff(DEG2RAD(179.9), la->spotsize);
}
else if (la->type == LA_AREA) {
cone_aperture = DEG2RAD(179.9);
}
if (this->shadow_id == LIGHT_NO_SHADOW) {
this->shadow_id = shadows.punctual_new();
}
ShadowPunctual &shadow = shadows.punctual_get(this->shadow_id);
shadow.sync(this->type,
this->object_mat,
cone_aperture,
la->clipsta,
this->influence_radius_max,
la->bias * 0.05f);
}
}
else {
shadow_discard_safe(shadows);
}
this->initialized = true;
}
void Light::shadow_discard_safe(ShadowModule &shadows)
{
if (shadow_id != LIGHT_NO_SHADOW) {
if (this->type != LIGHT_SUN) {
shadows.punctual_discard(shadow_id);
}
shadow_id = LIGHT_NO_SHADOW;
}
}
/* Returns attenuation radius inversed & squared for easy bound checking inside the shader. */
float Light::attenuation_radius_get(const ::Light *la, float light_threshold, float light_power)
{
if (la->type == LA_SUN) {
return (light_power > 1e-5f) ? 1e16f : 0.0f;
}
if (la->mode & LA_CUSTOM_ATTENUATION) {
return la->att_dist;
}
/* Compute the distance (using the inverse square law)
* at which the light power reaches the light_threshold. */
/* TODO take area light scale into account. */
return sqrtf(light_power / light_threshold);
}
void Light::shape_parameters_set(const ::Light *la, const float scale[3])
{
if (la->type == LA_AREA) {
float area_size_y = (ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE)) ? la->area_sizey :
la->area_size;
_area_size_x = max_ff(0.003f, la->area_size * scale[0] * 0.5f);
_area_size_y = max_ff(0.003f, area_size_y * scale[1] * 0.5f);
/* For volume point lighting. */
radius_squared = max_ff(0.001f, hypotf(_area_size_x, _area_size_y) * 0.5f);
radius_squared = square_f(radius_squared);
}
else {
if (la->type == LA_SPOT) {
/* Spot size & blend */
spot_size_inv[0] = scale[2] / scale[0];
spot_size_inv[1] = scale[2] / scale[1];
float spot_size = cosf(la->spotsize * 0.5f);
float spot_blend = (1.0f - spot_size) * la->spotblend;
_spot_mul = 1.0f / max_ff(1e-8f, spot_blend);
_spot_bias = -spot_size * _spot_mul;
}
if (la->type == LA_SUN) {
_area_size_x = max_ff(0.001f, tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f));
_area_size_y = _area_size_x;
}
else {
_area_size_x = _area_size_y = max_ff(0.001f, la->area_size);
}
radius_squared = square_f(_area_size_x);
}
}
float Light::shape_power_get(const ::Light *la)
{
float power;
/* Make illumination power constant */
if (la->type == LA_AREA) {
float area = _area_size_x * _area_size_y;
power = 1.0f / (area * 4.0f * float(M_PI));
/* FIXME : Empirical, Fit cycles power */
power *= 0.8f;
if (ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) {
/* Scale power to account for the lower area of the ellipse compared to the surrounding
* rectangle. */
power *= 4.0f / M_PI;
}
}
else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) {
power = 1.0f / (4.0f * square_f(_radius) * float(M_PI * M_PI));
}
else { /* LA_SUN */
power = 1.0f / (square_f(_radius) * float(M_PI));
/* Make illumination power closer to cycles for bigger radii. Cycles uses a cos^3 term that
* we cannot reproduce so we account for that by scaling the light power. This function is
* the result of a rough manual fitting. */
/* Simplification of:
* power *= 1 + r²/2 */
power += 1.0f / (2.0f * M_PI);
}
return power;
}
float Light::shape_power_volume_get(const ::Light *la)
{
/* Volume light is evaluated as point lights. Remove the shape power. */
if (la->type == LA_AREA) {
/* Match cycles. Empirical fit... must correspond to some constant. */
float power = 0.0792f * M_PI;
/* This corrects for area light most representative point trick. The fit was found by
* reducing the average error compared to cycles. */
float area = _area_size_x * _area_size_y;
float tmp = M_PI_2 / (M_PI_2 + sqrtf(area));
/* Lerp between 1.0 and the limit (1 / pi). */
power *= tmp + (1.0f - tmp) * M_1_PI;
return power;
}
else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) {
/* Match cycles. Empirical fit... must correspond to some constant. */
return 0.0792f;
}
else { /* LA_SUN */
return 1.0f;
}
}
void Light::debug_draw(void)
{
const float color[4] = {0.8, 0.3, 0, 1};
DRW_debug_sphere(_position, influence_radius_max, color);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name LightModule
* \{ */
void LightModule::begin_sync(void)
{
/* In begin_sync so it can be aninated. */
float light_threshold = max_ff(1e-16f, inst_.scene->eevee.light_threshold);
if (light_threshold != light_threshold_) {
light_threshold_ = light_threshold;
inst_.sampling.reset();
}
}
void LightModule::sync_light(const Object *ob, ObjectHandle &handle)
{
Light &light = lights_.lookup_or_add_default(handle.object_key);
light.used = true;
if (handle.recalc != 0 || !light.initialized) {
light.sync(inst_.shadows, ob, light_threshold_);
}
}
void LightModule::end_sync(void)
{
lights_refs_.clear();
Vector<ObjectKey, 0> deleted_keys;
/* Detect light deletion. */
for (auto item : lights_.items()) {
Light &light = item.value;
if (!light.used) {
deleted_keys.append(item.key);
light.shadow_discard_safe(inst_.shadows);
}
else {
light.used = false;
lights_refs_.append(&light);
}
}
if (deleted_keys.size() > 0) {
inst_.sampling.reset();
}
for (auto key : deleted_keys) {
lights_.remove(key);
}
/* Call shadows.end_sync after light pruning to avoid packing deleted shadows. */
inst_.shadows.end_sync();
}
/* Compute acceleration structure for the given view. If extent is 0, bind no lights. */
void LightModule::set_view(const DRWView *view, const ivec2 extent, bool enable_specular)
{
if (extent.x == 0) {
culling_.set_empty();
return;
}
culling_.set_view(view, extent);
for (auto light_id : lights_refs_.index_range()) {
Light &light = *lights_refs_[light_id];
BoundSphere bsphere;
if (light.type == LIGHT_SUN) {
/* Make sun lights cover the whole frustum. */
float viewinv[4][4];
DRW_view_viewmat_get(view, viewinv, true);
copy_v3_v3(bsphere.center, viewinv[3]);
bsphere.radius = fabsf(DRW_view_far_distance_get(view));
}
else {
/* TODO(fclem) fit cones better. */
copy_v3_v3(bsphere.center, light._position);
bsphere.radius = light.influence_radius_max;
}
culling_.insert(light_id, bsphere);
}
DRW_view_set_active(view);
/* This is only called if the light is visible under this view. */
auto data_copy = [&](LightBatch &light_batch, uint32_t dst_index, uint32_t src_index) {
Light &light = *this->lights_refs_[src_index];
LightData &dst = light_batch.lights_data[dst_index];
dst = light;
if (!enable_specular) {
dst.specular_power = 0.0f;
}
if (light.shadow_id != LIGHT_NO_SHADOW) {
ShadowPunctual &shadow = this->inst_.shadows.punctual_get(light.shadow_id);
shadow.is_visible = true;
shadow.random_position_on_shape_set(this->inst_);
light_batch.shadows_data[dst_index] = shadow;
}
};
/* Called for each batch. Do 2D gpu culling. */
auto culling_func = [&](LightBatch &light_batch, CullingDataBuf &culling_data) {
LightDataBuf &lights_data = light_batch.lights_data;
ShadowPunctualDataBuf &shadow_data = light_batch.shadows_data;
lights_data.push_update();
shadow_data.push_update();
this->inst_.shading_passes.light_culling.render(lights_data.ubo_get(), culling_data.ubo_get());
};
culling_.finalize(data_copy, culling_func);
inst_.shadows.update_visible(view);
}
void LightModule::bind_batch(int range_id)
{
active_lights_ubo_ = culling_[range_id]->item_data.lights_data.ubo_get();
active_shadows_ubo_ = culling_[range_id]->item_data.shadows_data.ubo_get();
active_culling_ubo_ = culling_[range_id]->culling_ubo_get();
active_culling_tx_ = culling_[range_id]->culling_texture_get();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name CullingPass
* \{ */
void CullingLightPass::sync(void)
{
culling_ps_ = DRW_pass_create("CullingLight", DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(CULLING_LIGHT);
DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_ps_);
DRW_shgroup_uniform_block_ref(grp, "lights_block", &lights_ubo_);
DRW_shgroup_uniform_block_ref(grp, "lights_culling_block", &culling_ubo_);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
void CullingLightPass::render(const GPUUniformBuf *lights_ubo, const GPUUniformBuf *culling_ubo)
{
lights_ubo_ = lights_ubo;
culling_ubo_ = culling_ubo;
DRW_draw_pass(culling_ps_);
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,167 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* The light module manages light data buffers and light culling system.
*/
#pragma once
#include "BLI_bitmap.h"
#include "BLI_vector.hh"
#include "DNA_light_types.h"
#include "eevee_camera.hh"
#include "eevee_culling.hh"
#include "eevee_id_map.hh"
#include "eevee_sampling.hh"
#include "eevee_shader.hh"
#include "eevee_shader_shared.hh"
#include "eevee_shadow.hh"
#include "eevee_wrapper.hh"
namespace blender::eevee {
class Instance;
/* -------------------------------------------------------------------- */
/** \name Light Object
* \{ */
struct Light : public LightData {
public:
bool initialized = false;
bool used = false;
public:
Light()
{
shadow_id = LIGHT_NO_SHADOW;
}
void sync(ShadowModule &shadows, const Object *ob, float threshold);
void shadow_discard_safe(ShadowModule &shadows);
void debug_draw(void);
private:
float attenuation_radius_get(const ::Light *la, float light_threshold, float light_power);
void shape_parameters_set(const ::Light *la, const float scale[3]);
float shape_power_get(const ::Light *la);
float shape_power_volume_get(const ::Light *la);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name CullingPass
* \{ */
class CullingLightPass {
private:
Instance &inst_;
DRWPass *culling_ps_ = nullptr;
const GPUUniformBuf *lights_ubo_ = nullptr;
const GPUUniformBuf *culling_ubo_ = nullptr;
public:
CullingLightPass(Instance &inst) : inst_(inst){};
void sync(void);
void render(const GPUUniformBuf *lights_ubo, const GPUUniformBuf *culling_ubo);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name LightModule
* \{ */
/**
* The light module manages light data buffers and light culling system.
*/
class LightModule {
private:
Instance &inst_;
/** Map of light objects. This is used to track light deletion. */
Map<ObjectKey, Light> lights_;
/** References to data in lights_ for easy indexing. */
Vector<Light *> lights_refs_;
/** Batches of lights alongside their culling data. */
struct LightBatch {
LightDataBuf lights_data;
ShadowPunctualDataBuf shadows_data;
};
Culling<LightBatch, true> culling_;
/** Active data pointers used for rendering. */
const GPUUniformBuf *active_lights_ubo_;
const GPUUniformBuf *active_shadows_ubo_;
const GPUUniformBuf *active_culling_ubo_;
GPUTexture *active_culling_tx_;
// uint64_t active_batch_count_;
float light_threshold_;
public:
LightModule(Instance &inst) : inst_(inst), culling_(){};
~LightModule(){};
void begin_sync(void);
void sync_light(const Object *ob, ObjectHandle &handle);
void end_sync(void);
void set_view(const DRWView *view, const ivec2 extent, bool enable_specular = true);
void bind_batch(int range_id);
/**
* Getters
**/
const GPUUniformBuf **lights_ubo_ref_get(void)
{
return &active_lights_ubo_;
}
const GPUUniformBuf **shadows_ubo_ref_get(void)
{
return &active_shadows_ubo_;
}
const GPUUniformBuf **culling_ubo_ref_get(void)
{
return &active_culling_ubo_;
}
GPUTexture **culling_tx_ref_get(void)
{
return &active_culling_tx_;
}
/* Return a range iterator to loop over all lights.
* In practice, we render with light in waves of LIGHT_MAX lights at a time. */
IndexRange index_range(void) const
{
return culling_.index_range();
}
};
/** \} */
} // namespace blender::eevee

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -18,12 +18,18 @@
/** \file
* \ingroup eevee
*
* Contains functions used outside of EEVEE for lightcache baking.
*/
#pragma once
#include "BLI_sys_types.h" /* for bool */
#ifdef __cplusplus
extern "C" {
#endif
struct BlendDataReader;
struct BlendWriter;
struct EEVEE_Data;
@@ -50,19 +56,13 @@ void EEVEE_lightbake_job_data_free(void *custom_data);
void EEVEE_lightbake_update(void *custom_data);
void EEVEE_lightbake_job(void *custom_data, short *stop, short *do_update, float *progress);
void EEVEE_lightbake_update_world_quick(struct EEVEE_ViewLayerData *sldata,
struct EEVEE_Data *vedata,
const Scene *scene);
/* Light Cache */
struct LightCache *EEVEE_lightcache_create(const int grid_len,
const int cube_len,
const int cube_size,
const int vis_size,
const int irr_size[3]);
void EEVEE_lightcache_free(struct LightCache *lcache);
bool EEVEE_lightcache_load(struct LightCache *lcache);
void EEVEE_lightcache_info_update(struct SceneEEVEE *eevee);
void EEVEE_lightcache_blend_write(struct BlendWriter *writer, struct LightCache *cache);
void EEVEE_lightcache_blend_read_data(struct BlendDataReader *reader, struct LightCache *cache);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,92 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2018, Blender Foundation.
*/
/** \file
* \ingroup eevee
*/
#pragma once
#include "MEM_guardedalloc.h"
#include "DNA_lightprobe_types.h"
#include "GPU_capabilities.h"
namespace blender::eevee {
/**
* Wrapper to blender lightcache structure.
* Used to define methods for the light cache.
**/
struct LightCache : public ::LightCache {
private:
constexpr static int min_cube_lod_level = 3;
/* Rounded to nearest PowerOfTwo */
constexpr static int irradiance_sample_size_x = 4; /* 3 in reality */
constexpr static int irradiance_sample_size_y = 2;
/* Manually encoded as RGBM. Also encodes visibility. */
constexpr static eGPUTextureFormat irradiance_format = GPU_RGBA8;
/* OpenGL 3.3 core requirement, can be extended but it's already very big */
constexpr static int irradiance_max_pool_layer = 256;
constexpr static int irradiance_max_pool_size = 1024;
constexpr static int max_irradiance_samples =
(irradiance_max_pool_size / irradiance_sample_size_x) *
(irradiance_max_pool_size / irradiance_sample_size_y);
constexpr static eGPUTextureFormat reflection_format = GPU_R11F_G11F_B10F;
public:
LightCache(const int cube_len,
const int grid_len,
const int cube_size,
const int vis_size,
const int irr_size[3]);
~LightCache();
static void irradiance_cache_size_get(int visibility_size, int total_samples, int r_size[3]);
static void update_info(SceneEEVEE *eevee);
int irradiance_cells_per_row_get(void) const;
bool load(void);
bool validate(const int cube_len,
const int cube_res,
const int grid_len,
const int irr_size[3]) const;
uint memsize_get(void) const;
int64_t irradiance_sample_count(void) const;
void readback_irradiance(void);
void readback_reflections(void);
private:
bool version_check(void) const;
bool can_be_saved(void) const;
bool load_static(void);
bool create_reflection_texture(void);
bool create_irradiance_texture(void);
MEM_CXX_CLASS_ALLOC_FUNCS("EEVEE:LightCache")
};
} // namespace blender::eevee

View File

@@ -0,0 +1,583 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2018, Blender Foundation.
*/
/** \file
* \ingroup eevee
*/
#include "BLI_rect.h"
#include "BLI_span.hh"
#include "DNA_defaults.h"
#include "DNA_lightprobe_types.h"
#include "eevee_instance.hh"
namespace blender::eevee {
void LightProbeModule::init()
{
SceneEEVEE &sce_eevee = inst_.scene->eevee;
lightcache_ = static_cast<LightCache *>(sce_eevee.light_cache_data);
bool use_lookdev = inst_.use_studio_light();
if (!use_lookdev && lightcache_ && lightcache_->load()) {
OBJECT_GUARDED_SAFE_DELETE(lightcache_lookdev_, LightCache);
}
else {
if (lightcache_ && (lightcache_->flag & LIGHTCACHE_NOT_USABLE)) {
BLI_snprintf(
inst_.info, sizeof(inst_.info), "Error: LightCache cannot be loaded on this GPU");
}
if (lightcache_lookdev_ == nullptr) {
int cube_len = 1;
int grid_len = 1;
int irr_samples_len = 1;
ivec3 irr_size;
LightCache::irradiance_cache_size_get(
sce_eevee.gi_visibility_resolution, irr_samples_len, irr_size);
lightcache_lookdev_ = new LightCache(cube_len,
grid_len,
sce_eevee.gi_cubemap_resolution,
sce_eevee.gi_visibility_resolution,
irr_size);
}
lightcache_ = lightcache_lookdev_;
}
for (DRWView *&view : face_view_) {
view = nullptr;
}
if (info_data_.cubes.display_size != sce_eevee.gi_cubemap_draw_size ||
info_data_.grids.display_size != sce_eevee.gi_irradiance_draw_size ||
info_data_.grids.irradiance_smooth != square_f(sce_eevee.gi_irradiance_smoothing)) {
/* TODO(fclem) reset on scene update instead. */
inst_.sampling.reset();
}
info_data_.cubes.display_size = sce_eevee.gi_cubemap_draw_size;
info_data_.grids.display_size = sce_eevee.gi_irradiance_draw_size;
info_data_.grids.irradiance_smooth = square_f(sce_eevee.gi_irradiance_smoothing);
info_data_.grids.irradiance_cells_per_row = lightcache_->irradiance_cells_per_row_get();
info_data_.grids.visibility_size = lightcache_->vis_res;
info_data_.grids.visibility_cells_per_row = lightcache_->grid_tx.tex_size[0] /
info_data_.grids.visibility_size;
info_data_.grids.visibility_cells_per_layer = (lightcache_->grid_tx.tex_size[1] /
info_data_.grids.visibility_size) *
info_data_.grids.visibility_cells_per_row;
glossy_clamp_ = sce_eevee.gi_glossy_clamp;
filter_quality_ = clamp_f(sce_eevee.gi_filter_quality, 1.0f, 8.0f);
}
void LightProbeModule::begin_sync()
{
{
cube_downsample_ps_ = DRW_pass_create("Downsample.Cube", DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_FILTER_DOWNSAMPLE_CUBE);
DRWShadingGroup *grp = DRW_shgroup_create(sh, cube_downsample_ps_);
DRW_shgroup_uniform_texture_ref(grp, "input_tx", &cube_downsample_input_tx_);
DRW_shgroup_uniform_block(grp, "filter_block", filter_data_.ubo_get());
DRW_shgroup_call_procedural_triangles(grp, nullptr, 6);
}
{
filter_glossy_ps_ = DRW_pass_create("Filter.GlossyMip", DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_FILTER_GLOSSY);
DRWShadingGroup *grp = DRW_shgroup_create(sh, filter_glossy_ps_);
DRW_shgroup_uniform_texture_ref(grp, "radiance_tx", &cube_downsample_input_tx_);
DRW_shgroup_uniform_block(grp, "filter_block", filter_data_.ubo_get());
DRW_shgroup_call_procedural_triangles(grp, nullptr, 6);
}
{
filter_diffuse_ps_ = DRW_pass_create("Filter.Diffuse", DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_FILTER_DIFFUSE);
DRWShadingGroup *grp = DRW_shgroup_create(sh, filter_diffuse_ps_);
DRW_shgroup_uniform_texture_ref(grp, "radiance_tx", &cube_downsample_input_tx_);
DRW_shgroup_uniform_block(grp, "filter_block", filter_data_.ubo_get());
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
{
filter_visibility_ps_ = DRW_pass_create("Filter.Visibility", DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_FILTER_VISIBILITY);
DRWShadingGroup *grp = DRW_shgroup_create(sh, filter_visibility_ps_);
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &cube_downsample_input_tx_);
DRW_shgroup_uniform_block(grp, "filter_block", filter_data_.ubo_get());
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
display_ps_ = nullptr;
if ((inst_.v3d != nullptr) && ((inst_.v3d->flag2 & V3D_HIDE_OVERLAYS) == 0)) {
if (inst_.scene->eevee.flag & (SCE_EEVEE_SHOW_CUBEMAPS | SCE_EEVEE_SHOW_IRRADIANCE)) {
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
display_ps_ = DRW_pass_create("LightProbe.Display", state);
}
if (inst_.scene->eevee.flag & SCE_EEVEE_SHOW_CUBEMAPS) {
if (lightcache_->cube_len > 1) {
GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_DISPLAY_CUBEMAP);
DRWShadingGroup *grp = DRW_shgroup_create(sh, display_ps_);
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", cube_tx_ref_get());
DRW_shgroup_uniform_block(grp, "cubes_block", cube_ubo_get());
DRW_shgroup_uniform_block(grp, "lightprobes_info_block", info_ubo_get());
uint cubemap_count = 0;
/* Skip world. */
for (auto cube_id : IndexRange(1, lightcache_->cube_len - 1)) {
const LightProbeCache &cube = lightcache_->cube_data[cube_id];
/* Note: only works because probes are rendered in sequential order. */
if (cube.is_ready) {
cubemap_count++;
}
}
if (cubemap_count > 0) {
DRW_shgroup_call_procedural_triangles(grp, nullptr, cubemap_count * 2);
}
}
}
if (inst_.scene->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE) {
if (lightcache_->grid_len > 1) {
GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_DISPLAY_IRRADIANCE);
DRWShadingGroup *grp = DRW_shgroup_create(sh, display_ps_);
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", grid_tx_ref_get());
DRW_shgroup_uniform_block(grp, "grids_block", grid_ubo_get());
DRW_shgroup_uniform_block(grp, "lightprobes_info_block", info_ubo_get());
/* Skip world. */
for (auto grid_id : IndexRange(1, lightcache_->grid_len - 1)) {
const LightGridCache &grid = lightcache_->grid_data[grid_id];
if (grid.is_ready) {
DRWShadingGroup *grp_sub = DRW_shgroup_create_sub(grp);
DRW_shgroup_uniform_int_copy(grp_sub, "grid_id", grid_id);
uint sample_count = grid.resolution[0] * grid.resolution[1] * grid.resolution[2];
DRW_shgroup_call_procedural_triangles(grp_sub, nullptr, sample_count * 2);
}
}
}
}
}
}
void LightProbeModule::end_sync()
{
if (lightcache_->flag & LIGHTCACHE_UPDATE_WORLD) {
cubemap_prepare(vec3(0.0f), 0.01f, 1.0f, true);
}
}
void LightProbeModule::cubeface_winmat_get(mat4 &winmat, float near, float far)
{
/* Simple 90° FOV projection. */
perspective_m4(winmat, -near, near, -near, near, near, far);
}
void LightProbeModule::cubemap_prepare(vec3 position, float near, float far, bool background_only)
{
SceneEEVEE &sce_eevee = inst_.scene->eevee;
int cube_res = sce_eevee.gi_cubemap_resolution;
int cube_mip_count = (int)log2_ceil_u(cube_res);
mat4 viewmat;
unit_m4(viewmat);
negate_v3_v3(viewmat[3], position);
/* TODO(fclem) We might want to have theses as temporary textures. */
cube_depth_tx_.ensure_cubemap("CubemapDepth", cube_res, cube_mip_count, GPU_DEPTH24_STENCIL8);
cube_color_tx_.ensure_cubemap("CubemapColor", cube_res, cube_mip_count, GPU_RGBA16F);
GPU_texture_mipmap_mode(cube_color_tx_, true, true);
cube_downsample_fb_.ensure(GPU_ATTACHMENT_TEXTURE(cube_depth_tx_),
GPU_ATTACHMENT_TEXTURE(cube_color_tx_));
filter_cube_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(lightcache_->cube_tx.tex));
filter_grid_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(lightcache_->grid_tx.tex));
mat4 winmat;
cubeface_winmat_get(winmat, near, far);
for (auto i : IndexRange(ARRAY_SIZE(probe_views_))) {
probe_views_[i].sync(cube_color_tx_, cube_depth_tx_, winmat, viewmat, background_only);
}
}
void LightProbeModule::cubemap_render(void)
{
DRW_stats_group_start("Cubemap Render");
for (auto i : IndexRange(ARRAY_SIZE(probe_views_))) {
probe_views_[i].render();
}
DRW_stats_group_end();
/* Update mipchain. */
filter_data_.target_layer = 0;
filter_data_.push_update();
cube_downsample_input_tx_ = cube_color_tx_;
DRW_stats_group_start("Cubemap Downsample");
GPU_framebuffer_recursive_downsample(cube_downsample_fb_, 7, cube_downsample_cb, this);
DRW_stats_group_end();
}
void LightProbeModule::filter_glossy(int cube_index, float intensity)
{
DRW_stats_group_start("Filter.Glossy");
filter_data_.instensity_fac = intensity;
filter_data_.target_layer = cube_index * 6;
int level_max = lightcache_->mips_len;
for (int level = 0; level <= level_max; level++) {
filter_data_.luma_max = (glossy_clamp_ > 0.0f) ? glossy_clamp_ : 1e16f;
/* Disney Roughness. */
filter_data_.roughness = square_f(level / (float)level_max);
/* Distribute Roughness across lod more evenly. */
filter_data_.roughness = square_f(filter_data_.roughness);
/* Avoid artifacts. */
filter_data_.roughness = clamp_f(filter_data_.roughness, 1e-4f, 0.9999f);
/* Variable sample count and bias to make first levels faster. */
switch (level) {
case 0:
filter_data_.sample_count = 1.0f;
filter_data_.lod_bias = -1.0f;
break;
case 1:
filter_data_.sample_count = filter_quality_ * 32.0f;
filter_data_.lod_bias = 1.0f;
break;
case 2:
filter_data_.sample_count = filter_quality_ * 40.0f;
filter_data_.lod_bias = 2.0f;
break;
case 3:
filter_data_.sample_count = filter_quality_ * 64.0f;
filter_data_.lod_bias = 2.0f;
break;
default:
filter_data_.sample_count = filter_quality_ * 128.0f;
filter_data_.lod_bias = 2.0f;
break;
}
/* Add automatic LOD bias (based on target size). */
filter_data_.lod_bias += lod_bias_from_cubemap();
filter_data_.push_update();
filter_cube_fb_.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE_MIP(lightcache_->cube_tx.tex, level));
GPU_framebuffer_bind(filter_cube_fb_);
DRW_draw_pass(filter_glossy_ps_);
}
DRW_stats_group_end();
}
void LightProbeModule::filter_diffuse(int sample_index, float intensity)
{
filter_data_.instensity_fac = intensity;
filter_data_.target_layer = 0;
filter_data_.luma_max = 1e16f;
filter_data_.sample_count = 1024.0f;
filter_data_.lod_bias = lod_bias_from_cubemap();
filter_data_.push_update();
ivec2 extent = ivec2(3, 2);
ivec2 offset = extent;
offset.x *= sample_index % info_data_.grids.irradiance_cells_per_row;
offset.y *= sample_index / info_data_.grids.irradiance_cells_per_row;
GPU_framebuffer_bind(filter_grid_fb_);
GPU_framebuffer_viewport_set(filter_grid_fb_, UNPACK2(offset), UNPACK2(extent));
DRW_draw_pass(filter_diffuse_ps_);
GPU_framebuffer_viewport_reset(filter_grid_fb_);
}
void LightProbeModule::filter_visibility(int sample_index,
float visibility_blur,
float visibility_range)
{
ivec2 extent = ivec2(info_data_.grids.visibility_size);
ivec2 offset = extent;
offset.x *= sample_index % info_data_.grids.visibility_cells_per_row;
offset.y *= (sample_index / info_data_.grids.visibility_cells_per_row) %
info_data_.grids.visibility_cells_per_layer;
filter_data_.target_layer = 1 + sample_index / info_data_.grids.visibility_cells_per_layer;
filter_data_.sample_count = 512.0f; /* TODO refine */
filter_data_.visibility_blur = visibility_blur;
filter_data_.visibility_range = visibility_range;
filter_data_.push_update();
GPU_framebuffer_bind(filter_grid_fb_);
GPU_framebuffer_viewport_set(filter_grid_fb_, UNPACK2(offset), UNPACK2(extent));
DRW_draw_pass(filter_visibility_ps_);
GPU_framebuffer_viewport_reset(filter_grid_fb_);
}
void LightProbeModule::update_world_cache()
{
DRW_stats_group_start("LightProbe.world");
const DRWView *view_active = DRW_view_get_active();
cubemap_render();
filter_diffuse(0, 1.0f);
if ((lightcache_->flag & LIGHTCACHE_NO_REFLECTION) == 0) {
/* TODO(fclem) Change ray type. */
/* OPTI(fclem) Only re-render if there is a light path node in the world material. */
// cubemap_render();
filter_glossy(0, 1.0f);
}
if (view_active != nullptr) {
DRW_view_set_active(view_active);
}
DRW_stats_group_end();
}
/* Ensure a temporary cache the same size at the target lightcache exists. */
LightCache *LightProbeModule::baking_cache_get(void)
{
if (lightcache_baking_ == nullptr) {
lightcache_baking_ = new LightCache(lightcache_->cube_len,
lightcache_->grid_len,
lightcache_->cube_tx.tex_size[0],
lightcache_->vis_res,
lightcache_->grid_tx.tex_size);
if (lightcache_baking_->flag != LIGHTCACHE_INVALID) {
LightCache &lcache_src = *lightcache_;
LightCache &lcache = *lightcache_baking_;
/* Copy cache structure. */
memcpy(lcache.cube_data, lcache_src.cube_data, lcache.cube_len * sizeof(*lcache.cube_data));
memcpy(lcache.grid_data, lcache_src.grid_data, lcache.grid_len * sizeof(*lcache.grid_data));
/* Make grids renderable. */
for (LightGridCache &grid : MutableSpan(lcache.grid_data, lcache.grid_len)) {
grid.is_ready = 1;
}
/* Avoid sampling further than mip 0. Mips > 0 being undefined. */
lcache.mips_len = 0;
lcache.flag |= LIGHTCACHE_NO_REFLECTION;
/* Init to black. */
uint data_cube = 0;
uchar data_grid[4] = {0, 0, 0, 0};
GPU_texture_clear(lcache.cube_tx.tex, GPU_DATA_10_11_11_REV, &data_cube);
GPU_texture_clear(lcache.grid_tx.tex, GPU_DATA_UBYTE, &data_grid);
}
}
return lightcache_baking_;
}
void LightProbeModule::bake(Depsgraph *depsgraph,
int type,
int index,
int bounce,
const float position[3],
const LightProbe *probe,
float visibility_range)
{
rcti rect;
BLI_rcti_init(&rect, 0, 0, 1, 1);
/* Disable screenspace effects. */
SceneEEVEE &sce_eevee = DEG_get_evaluated_scene(depsgraph)->eevee;
sce_eevee.flag &= ~(SCE_EEVEE_GTAO_ENABLED | SCE_EEVEE_SSR_ENABLED);
inst_.init(ivec2(1), &rect, nullptr, depsgraph, probe);
inst_.sampling.reset();
inst_.render_sync();
inst_.sampling.step();
float near = (probe) ? probe->clipsta : 0.1f;
float far = (probe) ? probe->clipend : 1.0f;
float intensity = (probe) ? probe->intensity : 1.0f;
bool background_only = (probe == nullptr);
cubemap_prepare(position, near, far, background_only);
if (type == LIGHTPROBE_TYPE_CUBE && probe != nullptr) {
/* Reflections cubemaps are rendered after all irradiance bounces.
* Swap to get the final irradiance in lightcache_baking_. */
swap_irradiance_cache();
}
/* Render using the previous bounce to light the scene. */
lightcache_ = baking_cache_get();
cubemap_render();
/* Filter on the original cache. */
lightcache_ = reinterpret_cast<LightCache *>(sce_eevee.light_cache_data);
if (type == LIGHTPROBE_TYPE_CUBE) {
filter_glossy(index, intensity);
/* Swap back final irradiance to lightcache_. */
if (probe != nullptr) {
swap_irradiance_cache();
}
}
else {
filter_diffuse(index, intensity);
if (probe && bounce < 2) {
/* No need to filter visibility after 2nd bounce since both lightcache_ and
* lightcache_baking_ will have correct visibility grid. */
filter_visibility(index, probe->vis_blur, visibility_range);
}
}
}
/* Push world probe to first grid and cubemap slots. */
void LightProbeModule::sync_world(const DRWView *view)
{
BoundSphere view_bounds = DRW_view_frustum_bsphere_get(view);
/* Playing safe. The fake grid needs to be bigger than the frustum. */
view_bounds.radius = clamp_f(view_bounds.radius * 2.0, 0.0f, FLT_MAX);
CubemapData &cube = cube_data_[0];
GridData &grid = grid_data_[0];
scale_m4_fl(grid.local_mat, view_bounds.radius);
negate_v3_v3(grid.local_mat[3], view_bounds.center);
copy_m4_m4(cube.influence_mat, grid.local_mat);
copy_m4_m4(cube.parallax_mat, cube.influence_mat);
grid.resolution = ivec3(1);
grid.offset = 0;
grid.level_skip = 1;
grid.attenuation_bias = 0.001f;
grid.attenuation_scale = 1.0f;
grid.visibility_range = 1.0f;
grid.visibility_bleed = 0.001f;
grid.visibility_bias = 0.0f;
grid.increment_x = vec3(0.0f);
grid.increment_y = vec3(0.0f);
grid.increment_z = vec3(0.0f);
grid.corner = vec3(0.0f);
cube._parallax_type = CUBEMAP_SHAPE_SPHERE;
cube._layer = 0.0;
}
void LightProbeModule::sync_grid(const DRWView *UNUSED(view),
const LightGridCache &grid_cache,
int grid_index)
{
/* Skip the world probe. */
if (grid_index == 0 || grid_cache.is_ready != 1) {
return;
}
GridData &grid = grid_data_[info_data_.grids.grid_count];
copy_m4_m4(grid.local_mat, grid_cache.mat);
grid.resolution = ivec3(grid_cache.resolution);
grid.offset = grid_cache.offset;
grid.level_skip = grid_cache.level_bias;
grid.attenuation_bias = grid_cache.attenuation_bias;
grid.attenuation_scale = grid_cache.attenuation_scale;
grid.visibility_range = grid_cache.visibility_range;
grid.visibility_bleed = grid_cache.visibility_bleed;
grid.visibility_bias = grid_cache.visibility_bias;
grid.increment_x = vec3(grid_cache.increment_x);
grid.increment_y = vec3(grid_cache.increment_y);
grid.increment_z = vec3(grid_cache.increment_z);
grid.corner = vec3(grid_cache.corner);
info_data_.grids.grid_count++;
}
void LightProbeModule::sync_cubemap(const DRWView *UNUSED(view),
const LightProbeCache &cube_cache,
int cube_index)
{
/* Skip the world probe. */
if (cube_index == 0 || cube_cache.is_ready != 1) {
return;
}
CubemapData &cube = cube_data_[info_data_.cubes.cube_count];
copy_m4_m4(cube.parallax_mat, cube_cache.parallaxmat);
copy_m4_m4(cube.influence_mat, cube_cache.attenuationmat);
cube._attenuation_factor = cube_cache.attenuation_fac;
cube._attenuation_type = cube_cache.attenuation_type;
cube._parallax_type = cube_cache.parallax_type;
cube._layer = cube_index;
cube._world_position_x = cube_cache.position[0];
cube._world_position_y = cube_cache.position[1];
cube._world_position_z = cube_cache.position[2];
info_data_.cubes.cube_count++;
}
/* Only enables world light probe if extent is invalid (no culling possible). */
void LightProbeModule::set_view(const DRWView *view, const ivec2 extent)
{
if (lightcache_->flag & LIGHTCACHE_UPDATE_WORLD) {
/* Set before update to avoid infinite recursion. */
lightcache_->flag &= ~LIGHTCACHE_UPDATE_WORLD;
update_world_cache();
}
/* Only sync when setting the view. This way we can cull probes not in frustum. */
/* TODO(fclem) implement culling. But needs to fix display when not all probes are present. */
info_data_.grids.grid_count = 1;
info_data_.cubes.cube_count = 1;
sync_world(view);
/* Only world if extent is 0. */
if (extent.x > 0) {
for (auto i : IndexRange(lightcache_->grid_len)) {
sync_grid(view, lightcache_->grid_data[i], i);
}
for (auto i : IndexRange(lightcache_->cube_len)) {
sync_cubemap(view, lightcache_->cube_data[i], i);
}
}
info_data_.cubes.roughness_max_lod = lightcache_->mips_len;
inst_.lookdev.rotation_get(info_data_.cubes.lookdev_rotation);
inst_.lookdev.rotation_get(info_data_.grids.lookdev_rotation);
active_grid_tx_ = lightcache_->grid_tx.tex;
active_cube_tx_ = lightcache_->cube_tx.tex;
info_data_.push_update();
grid_data_.push_update();
cube_data_.push_update();
}
void LightProbeModule::draw_cache_display(void)
{
/* Only draws something if enabled. */
DRW_draw_pass(display_ps_);
}
} // namespace blender::eevee

View File

@@ -0,0 +1,177 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2018, Blender Foundation.
*/
/** \file
* \ingroup eevee
*/
#pragma once
#include "eevee_lightcache.hh"
#include "eevee_view.hh"
#include "eevee_wrapper.hh"
namespace blender::eevee {
class Instance;
class LightProbeModule {
private:
Instance &inst_;
LightProbeFilterDataBuf filter_data_;
LightProbeInfoDataBuf info_data_;
GridDataBuf grid_data_;
CubemapDataBuf cube_data_;
/* Either scene lightcache or lookdev lightcache */
LightCache *lightcache_ = nullptr;
/* Own lightcache used for lookdev lighting or as fallback. */
LightCache *lightcache_lookdev_ = nullptr;
/* Temporary cache used for baking. */
LightCache *lightcache_baking_ = nullptr;
/* Used for rendering probes. */
/* OPTI(fclem) Share for the whole scene? Only allocate temporary? */
Texture cube_depth_tx_ = Texture("CubemapDepth");
Texture cube_color_tx_ = Texture("CubemapColor");
LightProbeView probe_views_[6];
Framebuffer cube_downsample_fb_ = Framebuffer("cube_downsample");
Framebuffer filter_cube_fb_ = Framebuffer("filter_cube");
Framebuffer filter_grid_fb_ = Framebuffer("filter_grid");
std::array<DRWView *, 6> face_view_ = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
DRWPass *cube_downsample_ps_ = nullptr;
DRWPass *filter_glossy_ps_ = nullptr;
DRWPass *filter_diffuse_ps_ = nullptr;
DRWPass *filter_visibility_ps_ = nullptr;
DRWPass *display_ps_ = nullptr;
/** Input texture to downsample cube pass. */
GPUTexture *cube_downsample_input_tx_ = nullptr;
/** Copy of actual textures from the lightcache_. */
GPUTexture *active_grid_tx_ = nullptr;
GPUTexture *active_cube_tx_ = nullptr;
/** Constant values during baking. */
float glossy_clamp_ = 0.0;
float filter_quality_ = 0.0;
public:
LightProbeModule(Instance &inst)
: inst_(inst),
probe_views_{{inst, "posX_view", cubeface_mat[0], 0},
{inst, "negX_view", cubeface_mat[1], 1},
{inst, "posY_view", cubeface_mat[2], 2},
{inst, "negY_view", cubeface_mat[3], 3},
{inst, "posZ_view", cubeface_mat[4], 4},
{inst, "negZ_view", cubeface_mat[5], 5}}
{
}
~LightProbeModule()
{
OBJECT_GUARDED_SAFE_DELETE(lightcache_lookdev_, LightCache);
OBJECT_GUARDED_SAFE_DELETE(lightcache_baking_, LightCache);
}
void init();
void begin_sync();
void end_sync();
void set_view(const DRWView *view, const ivec2 extent);
void set_world_dirty(void)
{
lightcache_->flag |= LIGHTCACHE_UPDATE_WORLD;
}
void swap_irradiance_cache(void)
{
if (lightcache_baking_ && lightcache_) {
SWAP(GPUTexture *, lightcache_baking_->grid_tx.tex, lightcache_->grid_tx.tex);
}
}
const GPUUniformBuf *grid_ubo_get() const
{
return grid_data_.ubo_get();
}
const GPUUniformBuf *cube_ubo_get() const
{
return cube_data_.ubo_get();
}
const GPUUniformBuf *info_ubo_get() const
{
return info_data_.ubo_get();
}
GPUTexture **grid_tx_ref_get()
{
return &active_grid_tx_;
}
GPUTexture **cube_tx_ref_get()
{
return &active_cube_tx_;
}
void bake(Depsgraph *depsgraph,
int type,
int index,
int bounce,
const float position[3],
const LightProbe *probe = nullptr,
float visibility_range = 0.0f);
void draw_cache_display(void);
private:
void update_world_cache();
void sync_world(const DRWView *view);
void sync_grid(const DRWView *view, const struct LightGridCache &grid_cache, int grid_index);
void sync_cubemap(const DRWView *view, const struct LightProbeCache &cube_cache, int cube_index);
void cubeface_winmat_get(mat4 &winmat, float near, float far);
LightCache *baking_cache_get(void);
void cubemap_prepare(vec3 position, float near, float far, bool background_only);
void filter_glossy(int cube_index, float intensity);
void filter_diffuse(int sample_index, float intensity);
void filter_visibility(int sample_index, float visibility_blur, float visibility_range);
float lod_bias_from_cubemap(void)
{
float target_size_sq = square_f(GPU_texture_width(cube_color_tx_));
return 0.5f * logf(target_size_sq / filter_data_.sample_count) / logf(2);
}
static void cube_downsample_cb(void *thunk, int UNUSED(level))
{
DRW_draw_pass(reinterpret_cast<LightProbeModule *>(thunk)->cube_downsample_ps_);
}
void cubemap_render(void);
};
} // namespace blender::eevee

File diff suppressed because it is too large Load Diff

View File

@@ -1,269 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup DNA
*/
#include "BLI_sys_types.h" /* bool */
#include "BKE_object.h"
#include "DEG_depsgraph_query.h"
#include "eevee_private.h"
/* Reconstruct local obmat from EEVEE_light. (normalized) */
void eevee_light_matrix_get(const EEVEE_Light *evli, float r_mat[4][4])
{
copy_v3_v3(r_mat[0], evli->rightvec);
copy_v3_v3(r_mat[1], evli->upvec);
negate_v3_v3(r_mat[2], evli->forwardvec);
copy_v3_v3(r_mat[3], evli->position);
r_mat[0][3] = 0.0f;
r_mat[1][3] = 0.0f;
r_mat[2][3] = 0.0f;
r_mat[3][3] = 1.0f;
}
static float light_attenuation_radius_get(const Light *la,
float light_threshold,
float light_power)
{
if (la->mode & LA_CUSTOM_ATTENUATION) {
return la->att_dist;
}
/* Compute the distance (using the inverse square law)
* at which the light power reaches the light_threshold. */
return sqrtf(max_ff(1e-16, light_power / max_ff(1e-16, light_threshold)));
}
static void light_shape_parameters_set(EEVEE_Light *evli, const Light *la, const float scale[3])
{
if (la->type == LA_SPOT) {
/* Spot size & blend */
evli->sizex = scale[0] / scale[2];
evli->sizey = scale[1] / scale[2];
evli->spotsize = cosf(la->spotsize * 0.5f);
evli->spotblend = (1.0f - evli->spotsize) * la->spotblend;
evli->radius = max_ff(0.001f, la->area_size);
}
else if (la->type == LA_AREA) {
evli->sizex = max_ff(0.003f, la->area_size * scale[0] * 0.5f);
if (ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE)) {
evli->sizey = max_ff(0.003f, la->area_sizey * scale[1] * 0.5f);
}
else {
evli->sizey = max_ff(0.003f, la->area_size * scale[1] * 0.5f);
}
/* For volume point lighting. */
evli->radius = max_ff(0.001f, hypotf(evli->sizex, evli->sizey) * 0.5f);
}
else if (la->type == LA_SUN) {
evli->radius = max_ff(0.001f, tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f));
}
else {
evli->radius = max_ff(0.001f, la->area_size);
}
}
static float light_shape_power_get(const Light *la, const EEVEE_Light *evli)
{
float power;
/* Make illumination power constant */
if (la->type == LA_AREA) {
power = 1.0f / (evli->sizex * evli->sizey * 4.0f * M_PI) * /* 1/(w*h*Pi) */
0.8f; /* XXX: Empirical, Fit cycles power. */
if (ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) {
/* Scale power to account for the lower area of the ellipse compared to the surrounding
* rectangle. */
power *= 4.0f / M_PI;
}
}
else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) {
power = 1.0f / (4.0f * evli->radius * evli->radius * M_PI * M_PI); /* `1/(4*(r^2)*(Pi^2))` */
/* for point lights (a.k.a radius == 0.0) */
// power = M_PI * M_PI * 0.78; /* XXX: Empirical, Fit cycles power. */
}
else { /* LA_SUN */
power = 1.0f / (evli->radius * evli->radius * M_PI);
/* Make illumination power closer to cycles for bigger radii. Cycles uses a cos^3 term that we
* cannot reproduce so we account for that by scaling the light power. This function is the
* result of a rough manual fitting. */
power += 1.0f / (2.0f * M_PI); /* `power *= 1 + (r^2)/2` */
}
return power;
}
static float light_shape_power_volume_get(const Light *la,
const EEVEE_Light *evli,
float area_power)
{
/* Volume light is evaluated as point lights. Remove the shape power. */
float power = 1.0f / area_power;
if (la->type == LA_AREA) {
/* Match cycles. Empirical fit... must correspond to some constant. */
power *= 0.0792f * M_PI;
/* This corrects for area light most representative point trick. The fit was found by
* reducing the average error compared to cycles. */
float area = evli->sizex * evli->sizey;
float tmp = M_PI_2 / (M_PI_2 + sqrtf(area));
/* Lerp between 1.0 and the limit (1 / pi). */
power *= tmp + (1.0f - tmp) * M_1_PI;
}
else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) {
/* Match cycles. Empirical fit... must correspond to some constant. */
power *= 0.0792f;
}
else { /* LA_SUN */
/* Nothing to do. */
}
return power;
}
/* Update buffer with light data */
static void eevee_light_setup(Object *ob, EEVEE_Light *evli)
{
const Light *la = (Light *)ob->data;
float mat[4][4], scale[3];
const DRWContextState *draw_ctx = DRW_context_state_get();
const float light_threshold = draw_ctx->scene->eevee.light_threshold;
/* Position */
copy_v3_v3(evli->position, ob->obmat[3]);
/* Color */
copy_v3_v3(evli->color, &la->r);
evli->diff = la->diff_fac;
evli->spec = la->spec_fac;
evli->volume = la->volume_fac;
float max_power = max_fff(la->r, la->g, la->b) * fabsf(la->energy / 100.0f);
float surface_max_power = max_ff(evli->diff, evli->spec) * max_power;
float volume_max_power = evli->volume * max_power;
/* Influence Radii. */
float att_radius = light_attenuation_radius_get(la, light_threshold, surface_max_power);
float att_radius_volume = light_attenuation_radius_get(la, light_threshold, volume_max_power);
/* Take the inverse square of this distance. */
evli->invsqrdist = 1.0f / max_ff(1e-4f, square_f(att_radius));
evli->invsqrdist_volume = 1.0f / max_ff(1e-4f, square_f(att_radius_volume));
/* Vectors */
normalize_m4_m4_ex(mat, ob->obmat, scale);
copy_v3_v3(evli->forwardvec, mat[2]);
normalize_v3(evli->forwardvec);
negate_v3(evli->forwardvec);
copy_v3_v3(evli->rightvec, mat[0]);
normalize_v3(evli->rightvec);
copy_v3_v3(evli->upvec, mat[1]);
normalize_v3(evli->upvec);
/* Make sure we have a consistent Right Hand coord frame.
* (in case of negatively scaled Z axis) */
float cross[3];
cross_v3_v3v3(cross, evli->rightvec, evli->forwardvec);
if (dot_v3v3(cross, evli->upvec) < 0.0) {
negate_v3(evli->upvec);
}
light_shape_parameters_set(evli, la, scale);
/* Light Type */
evli->light_type = (float)la->type;
if ((la->type == LA_AREA) && ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) {
evli->light_type = LAMPTYPE_AREA_ELLIPSE;
}
float shape_power = light_shape_power_get(la, evli);
mul_v3_fl(evli->color, shape_power * la->energy);
evli->volume *= light_shape_power_volume_get(la, evli, shape_power);
/* No shadow by default */
evli->shadow_id = -1.0f;
}
void EEVEE_lights_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_LightsInfo *linfo = sldata->lights;
linfo->num_light = 0;
EEVEE_shadows_cache_init(sldata, vedata);
}
void EEVEE_lights_cache_add(EEVEE_ViewLayerData *sldata, Object *ob)
{
EEVEE_LightsInfo *linfo = sldata->lights;
const Light *la = (Light *)ob->data;
if (linfo->num_light >= MAX_LIGHT) {
printf("Too many lights in the scene !!!\n");
return;
}
/* Early out if light has no power. */
if (la->energy == 0.0f || is_zero_v3(&la->r)) {
return;
}
EEVEE_Light *evli = linfo->light_data + linfo->num_light;
eevee_light_setup(ob, evli);
if (la->mode & LA_SHADOW) {
if (la->type == LA_SUN) {
EEVEE_shadows_cascade_add(linfo, evli, ob);
}
else if (ELEM(la->type, LA_SPOT, LA_LOCAL, LA_AREA)) {
EEVEE_shadows_cube_add(linfo, evli, ob);
}
}
linfo->num_light++;
}
void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_LightsInfo *linfo = sldata->lights;
sldata->common_data.la_num_light = linfo->num_light;
/* Clamp volume lights power. */
float upper_bound = vedata->stl->effects->volume_light_clamp;
for (int i = 0; i < linfo->num_light; i++) {
EEVEE_Light *evli = linfo->light_data + i;
float power = max_fff(UNPACK3(evli->color)) * evli->volume;
if (power > 0.0f && evli->light_type != LA_SUN) {
/* The limit of the power attenuation function when the distance to the light goes to 0 is
* `2 / r^2` where r is the light radius. We need to find the right radius that emits at most
* the volume light upper bound. Inverting the function we get: */
float min_radius = 1.0f / sqrtf(0.5f * upper_bound / power);
/* Square it here to avoid a multiplication inside the shader. */
evli->volume_radius = square_f(max_ff(min_radius, evli->radius));
}
}
GPU_uniformbuf_update(sldata->light_ubo, &linfo->light_data);
}

View File

@@ -1,383 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*/
#include "DRW_render.h"
#include "BKE_camera.h"
#include "BKE_studiolight.h"
#include "BLI_rand.h"
#include "BLI_rect.h"
#include "DNA_screen_types.h"
#include "DNA_world_types.h"
#include "DEG_depsgraph_query.h"
#include "ED_screen.h"
#include "GPU_material.h"
#include "UI_resources.h"
#include "eevee_lightcache.h"
#include "eevee_private.h"
#include "draw_common.h"
static void eevee_lookdev_lightcache_delete(EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PrivateData *g_data = stl->g_data;
EEVEE_TextureList *txl = vedata->txl;
MEM_SAFE_FREE(stl->lookdev_lightcache);
MEM_SAFE_FREE(stl->lookdev_grid_data);
MEM_SAFE_FREE(stl->lookdev_cube_data);
DRW_TEXTURE_FREE_SAFE(txl->lookdev_grid_tx);
DRW_TEXTURE_FREE_SAFE(txl->lookdev_cube_tx);
g_data->studiolight_index = -1;
g_data->studiolight_rot_z = 0.0f;
}
static void eevee_lookdev_hdri_preview_init(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata)
{
EEVEE_PassList *psl = vedata->psl;
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
DRWShadingGroup *grp;
const EEVEE_EffectsInfo *effects = vedata->stl->effects;
struct GPUBatch *sphere = DRW_cache_sphere_get(effects->sphere_lod);
int mat_options = VAR_MAT_MESH | VAR_MAT_LOOKDEV;
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS |
DRW_STATE_CULL_BACK;
{
Material *ma = EEVEE_material_default_diffuse_get();
GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options);
struct GPUShader *sh = GPU_material_get_shader(gpumat);
DRW_PASS_CREATE(psl->lookdev_diffuse_pass, state);
grp = DRW_shgroup_create(sh, psl->lookdev_diffuse_pass);
EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false);
DRW_shgroup_add_material_resources(grp, gpumat);
DRW_shgroup_call(grp, sphere, NULL);
}
{
Material *ma = EEVEE_material_default_glossy_get();
GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options);
struct GPUShader *sh = GPU_material_get_shader(gpumat);
DRW_PASS_CREATE(psl->lookdev_glossy_pass, state);
grp = DRW_shgroup_create(sh, psl->lookdev_glossy_pass);
EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false);
DRW_shgroup_add_material_resources(grp, gpumat);
DRW_shgroup_call(grp, sphere, NULL);
}
}
void EEVEE_lookdev_init(EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
const DRWContextState *draw_ctx = DRW_context_state_get();
/* The view will be NULL when rendering previews. */
const View3D *v3d = draw_ctx->v3d;
if (eevee_hdri_preview_overlay_enabled(v3d)) {
/* Viewport / Spheres size. */
const rcti *rect;
rcti fallback_rect;
if (DRW_state_is_opengl_render()) {
const float *vp_size = DRW_viewport_size_get();
fallback_rect.xmax = vp_size[0];
fallback_rect.ymax = vp_size[1];
fallback_rect.xmin = fallback_rect.ymin = 0;
rect = &fallback_rect;
}
else {
rect = ED_region_visible_rect(draw_ctx->region);
}
/* Make the viewport width scale the lookdev spheres a bit.
* Scale between 1000px and 2000px. */
const float viewport_scale = clamp_f(
BLI_rcti_size_x(rect) / (2000.0f * U.dpi_fac), 0.5f, 1.0f);
const int sphere_size = U.lookdev_sphere_size * U.dpi_fac * viewport_scale;
if (sphere_size != effects->sphere_size || rect->xmax != effects->anchor[0] ||
rect->ymin != effects->anchor[1]) {
/* Make sphere resolution adaptive to viewport_scale, dpi and lookdev_sphere_size */
float res_scale = clamp_f(
(U.lookdev_sphere_size / 400.0f) * viewport_scale * U.dpi_fac, 0.1f, 1.0f);
if (res_scale > 0.7f) {
effects->sphere_lod = DRW_LOD_HIGH;
}
else if (res_scale > 0.25f) {
effects->sphere_lod = DRW_LOD_MEDIUM;
}
else {
effects->sphere_lod = DRW_LOD_LOW;
}
/* If sphere size or anchor point moves, reset TAA to avoid ghosting issue.
* This needs to happen early because we are changing taa_current_sample. */
effects->sphere_size = sphere_size;
effects->anchor[0] = rect->xmax;
effects->anchor[1] = rect->ymin;
stl->g_data->valid_double_buffer = false;
EEVEE_temporal_sampling_reset(vedata);
}
}
}
void EEVEE_lookdev_cache_init(EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
DRWPass *pass,
EEVEE_LightProbesInfo *pinfo,
DRWShadingGroup **r_shgrp)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_PrivateData *g_data = stl->g_data;
const DRWContextState *draw_ctx = DRW_context_state_get();
/* The view will be NULL when rendering previews. */
const View3D *v3d = draw_ctx->v3d;
const Scene *scene = draw_ctx->scene;
const bool probe_render = pinfo != NULL;
effects->lookdev_view = NULL;
if (eevee_hdri_preview_overlay_enabled(v3d)) {
eevee_lookdev_hdri_preview_init(vedata, sldata);
}
if (LOOK_DEV_STUDIO_LIGHT_ENABLED(v3d)) {
const View3DShading *shading = &v3d->shading;
StudioLight *sl = BKE_studiolight_find(shading->lookdev_light,
STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE);
if (sl == NULL || (sl->flag & STUDIOLIGHT_TYPE_WORLD) == 0) {
return;
}
GPUShader *shader = probe_render ? EEVEE_shaders_studiolight_probe_sh_get() :
EEVEE_shaders_studiolight_background_sh_get();
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
int cube_res = scene_eval->eevee.gi_cubemap_resolution;
/* If one of the component is missing we start from scratch. */
if ((stl->lookdev_grid_data == NULL) || (stl->lookdev_cube_data == NULL) ||
(txl->lookdev_grid_tx == NULL) || (txl->lookdev_cube_tx == NULL) ||
(g_data->light_cache && g_data->light_cache->ref_res != cube_res)) {
eevee_lookdev_lightcache_delete(vedata);
}
if (stl->lookdev_lightcache == NULL) {
#if defined(IRRADIANCE_SH_L2)
int grid_res = 4;
#elif defined(IRRADIANCE_HL2)
int grid_res = 4;
#endif
stl->lookdev_lightcache = EEVEE_lightcache_create(
1, 1, cube_res, 8, (int[3]){grid_res, grid_res, 1});
/* XXX: Fix memleak. TODO: find out why. */
MEM_SAFE_FREE(stl->lookdev_cube_mips);
/* We do this to use a special light cache for lookdev.
* This light-cache needs to be per viewport. But we need to
* have correct freeing when the viewport is closed. So we
* need to reference all textures to the txl and the memblocks
* to the stl. */
stl->lookdev_grid_data = stl->lookdev_lightcache->grid_data;
stl->lookdev_cube_data = stl->lookdev_lightcache->cube_data;
stl->lookdev_cube_mips = stl->lookdev_lightcache->cube_mips;
txl->lookdev_grid_tx = stl->lookdev_lightcache->grid_tx.tex;
txl->lookdev_cube_tx = stl->lookdev_lightcache->cube_tx.tex;
}
g_data->light_cache = stl->lookdev_lightcache;
DRWShadingGroup *grp = DRW_shgroup_create(shader, pass);
axis_angle_to_mat3_single(g_data->studiolight_matrix, 'Z', shading->studiolight_rot_z);
float studiolight_matrix[3][3] = {{0.0f}};
if (shading->flag & V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION) {
float view_matrix[4][4];
float view_rot_matrix[3][3];
float x_rot_matrix[3][3];
DRW_view_viewmat_get(NULL, view_matrix, false);
copy_m3_m4(view_rot_matrix, view_matrix);
axis_angle_to_mat3_single(x_rot_matrix, 'X', M_PI / 2.0f);
mul_m3_m3m3(view_rot_matrix, x_rot_matrix, view_rot_matrix);
mul_m3_m3m3(view_rot_matrix, g_data->studiolight_matrix, view_rot_matrix);
copy_m3_m3(studiolight_matrix, view_rot_matrix);
}
DRW_shgroup_uniform_mat3(grp, "StudioLightMatrix", g_data->studiolight_matrix);
if (probe_render) {
/* Avoid artifact with equirectangular mapping. */
eGPUSamplerState state = (GPU_SAMPLER_FILTER | GPU_SAMPLER_REPEAT_S);
DRW_shgroup_uniform_float_copy(grp, "studioLightIntensity", shading->studiolight_intensity);
BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE);
DRW_shgroup_uniform_texture_ex(grp, "studioLight", sl->equirect_radiance_gputexture, state);
/* Do not fade-out when doing probe rendering, only when drawing the background. */
DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f);
}
else {
float background_alpha = g_data->background_alpha * shading->studiolight_background;
float studiolight_blur = powf(shading->studiolight_blur, 2.5f);
DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", background_alpha);
DRW_shgroup_uniform_float_copy(grp, "studioLightBlur", studiolight_blur);
DRW_shgroup_uniform_texture(grp, "probeCubes", txl->lookdev_cube_tx);
}
/* Common UBOs are setup latter. */
*r_shgrp = grp;
/* Do we need to recalc the lightprobes? */
if (g_data->studiolight_index != sl->index ||
(shading->flag & V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION &&
!equals_m3m3(g_data->studiolight_matrix, studiolight_matrix)) ||
g_data->studiolight_rot_z != shading->studiolight_rot_z ||
g_data->studiolight_intensity != shading->studiolight_intensity ||
g_data->studiolight_cubemap_res != scene->eevee.gi_cubemap_resolution ||
g_data->studiolight_glossy_clamp != scene->eevee.gi_glossy_clamp ||
g_data->studiolight_filter_quality != scene->eevee.gi_filter_quality) {
stl->lookdev_lightcache->flag |= LIGHTCACHE_UPDATE_WORLD;
g_data->studiolight_index = sl->index;
copy_m3_m3(g_data->studiolight_matrix, studiolight_matrix);
g_data->studiolight_rot_z = shading->studiolight_rot_z;
g_data->studiolight_intensity = shading->studiolight_intensity;
g_data->studiolight_cubemap_res = scene->eevee.gi_cubemap_resolution;
g_data->studiolight_glossy_clamp = scene->eevee.gi_glossy_clamp;
g_data->studiolight_filter_quality = scene->eevee.gi_filter_quality;
}
}
}
static void eevee_lookdev_apply_taa(const EEVEE_EffectsInfo *effects,
int sphere_size,
float winmat[4][4])
{
if (DRW_state_is_image_render() || ((effects->enabled_effects & EFFECT_TAA) != 0)) {
double ht_point[2];
double ht_offset[2] = {0.0, 0.0};
const uint ht_primes[2] = {2, 3};
float ofs[2];
BLI_halton_2d(ht_primes, ht_offset, effects->taa_current_sample, ht_point);
EEVEE_temporal_sampling_offset_calc(ht_point, 1.5f, ofs);
winmat[3][0] += ofs[0] / sphere_size;
winmat[3][1] += ofs[1] / sphere_size;
}
}
void EEVEE_lookdev_draw(EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
const DRWContextState *draw_ctx = DRW_context_state_get();
if (psl->lookdev_diffuse_pass && eevee_hdri_preview_overlay_enabled(draw_ctx->v3d)) {
/* Config renderer. */
EEVEE_CommonUniformBuffer *common = &sldata->common_data;
common->la_num_light = 0;
common->prb_num_planar = 0;
common->prb_num_render_cube = 1;
common->prb_num_render_grid = 1;
common->ao_dist = 0.0f;
common->ao_factor = 0.0f;
common->ao_settings = 0.0f;
GPU_uniformbuf_update(sldata->common_ubo, common);
/* override matrices */
float winmat[4][4], viewmat[4][4];
unit_m4(winmat);
/* Look through the negative Z. */
negate_v3(winmat[2]);
eevee_lookdev_apply_taa(effects, effects->sphere_size, winmat);
/* "Remove" view matrix location. Leaving only rotation. */
DRW_view_viewmat_get(NULL, viewmat, false);
zero_v3(viewmat[3]);
if (effects->lookdev_view) {
/* When rendering just update the view. This avoids recomputing the culling. */
DRW_view_update_sub(effects->lookdev_view, viewmat, winmat);
}
else {
/* Using default view bypasses the culling. */
const DRWView *default_view = DRW_view_default_get();
effects->lookdev_view = DRW_view_create_sub(default_view, viewmat, winmat);
}
DRW_view_set_active(effects->lookdev_view);
/* Find the right frame-buffers to render to. */
GPUFrameBuffer *fb = (effects->target_buffer == fbl->effect_color_fb) ? fbl->main_fb :
fbl->effect_fb;
DRW_stats_group_start("Look Dev");
GPU_framebuffer_bind(fb);
const int sphere_margin = effects->sphere_size / 6.0f;
float offset[2] = {0.0f, sphere_margin};
offset[0] = effects->sphere_size + sphere_margin;
GPU_framebuffer_viewport_set(fb,
effects->anchor[0] - offset[0],
effects->anchor[1] + offset[1],
effects->sphere_size,
effects->sphere_size);
DRW_draw_pass(psl->lookdev_diffuse_pass);
offset[0] = (effects->sphere_size + sphere_margin) +
(sphere_margin + effects->sphere_size + sphere_margin);
GPU_framebuffer_viewport_set(fb,
effects->anchor[0] - offset[0],
effects->anchor[1] + offset[1],
effects->sphere_size,
effects->sphere_size);
DRW_draw_pass(psl->lookdev_glossy_pass);
GPU_framebuffer_viewport_reset(fb);
DRW_stats_group_end();
DRW_view_set_active(NULL);
}
}

View File

@@ -0,0 +1,366 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2018, Blender Foundation.
*/
/** \file
* \ingroup eevee
*/
#include "BKE_image.h"
#include "BKE_lib_id.h"
#include "BKE_node.h"
#include "BKE_studiolight.h"
#include "BKE_world.h"
#include "BLI_math_matrix.h"
#include "BLI_rect.h"
#include "DNA_userdef_types.h"
#include "ED_screen.h"
#include "NOD_shader.h"
#include "eevee_instance.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name Lookdev Nodetree
*
* \{ */
LookDevWorldNodeTree::LookDevWorldNodeTree()
{
bNodeTree *ntree = ntreeAddTree(NULL, "Lookdev Nodetree", ntreeType_Shader->idname);
bNode *background = nodeAddStaticNode(NULL, ntree, SH_NODE_BACKGROUND);
bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_WORLD);
bNodeSocket *background_out = nodeFindSocket(background, SOCK_OUT, "Background");
bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface");
nodeAddLink(ntree, background, background_out, output, output_in);
nodeSetActive(ntree, output);
/* Note that we do not populate the environment texture input.
* We plug the GPUTexture directly using the sampler binding name ("samp1"). */
bNode *environment = nodeAddStaticNode(NULL, ntree, SH_NODE_TEX_ENVIRONMENT);
bNodeSocket *background_in = nodeFindSocket(background, SOCK_IN, "Color");
bNodeSocket *environment_out = nodeFindSocket(environment, SOCK_OUT, "Color");
nodeAddLink(ntree, environment, environment_out, background, background_in);
strength_socket_ =
(bNodeSocketValueFloat *)nodeFindSocket(background, SOCK_IN, "Strength")->default_value;
ntree_ = ntree;
}
LookDevWorldNodeTree::~LookDevWorldNodeTree()
{
ntreeFreeEmbeddedTree(ntree_);
MEM_SAFE_FREE(ntree_);
}
/* Configure a default nodetree with the given parameters. */
bNodeTree *LookDevWorldNodeTree::nodetree_get(float strength)
{
/* WARNING: This function is not threadsafe. Which is not a problem for the moment. */
strength_socket_->value = strength;
return ntree_;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name LookDev Studiolight
*
* Light the scene using the studiolight hdri. Overrides the lightcache (if any) and
* use custom shader to draw the background.
* \{ */
void LookDev::init(const ivec2 &output_res, const rcti *render_border)
{
StudioLight *studiolight = nullptr;
if (inst_.v3d) {
studiolight = BKE_studiolight_find(inst_.v3d->shading.lookdev_light,
STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE);
}
if (inst_.use_studio_light() && studiolight && (studiolight->flag & STUDIOLIGHT_TYPE_WORLD)) {
const View3DShading &shading = inst_.v3d->shading;
studiolight_ = studiolight;
/* Detect update. */
if ((opacity_ != shading.studiolight_background) || (rotation_ != shading.studiolight_rot_z) ||
(instensity_ != shading.studiolight_intensity) || (blur_ != shading.studiolight_blur) ||
(view_rotation_ != ((shading.flag & V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION) != 0)) ||
(studiolight_index_ != studiolight_->index)) {
opacity_ = shading.studiolight_background;
instensity_ = shading.studiolight_intensity;
blur_ = shading.studiolight_blur;
rotation_ = shading.studiolight_rot_z;
studiolight_index_ = studiolight_->index;
view_rotation_ = (shading.flag & V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION) != 0;
inst_.sampling.reset();
inst_.lightprobes.set_world_dirty();
/* Update the material. */
GPU_material_free(&material);
}
}
else {
if (studiolight_ != nullptr) {
inst_.sampling.reset();
inst_.lightprobes.set_world_dirty();
}
studiolight_ = nullptr;
studiolight_index_ = -1;
GPU_material_free(&material);
}
if (do_overlay(output_res, render_border)) {
rcti rect;
if (DRW_state_is_opengl_render()) {
BLI_rcti_init(&rect, 0, output_res.x, 0, output_res.y);
}
else {
const DRWContextState *draw_ctx = DRW_context_state_get();
rect = *ED_region_visible_rect(draw_ctx->region);
}
/* Make the viewport width scale the lookdev spheres a bit.
* Scale between 1000px and 2000px. */
float viewport_scale = clamp_f(BLI_rcti_size_x(&rect) / (2000.0f * U.dpi_fac), 0.5f, 1.0f);
int sphere_size = U.lookdev_sphere_size * U.dpi_fac * viewport_scale;
ivec2 anchor = ivec2(rect.xmax, rect.ymin);
if (sphere_size != sphere_size_ || anchor != anchor_) {
/* Make sphere resolution adaptive to viewport_scale, dpi and lookdev_sphere_size */
float res_scale = (U.lookdev_sphere_size / 400.0f) * viewport_scale * U.dpi_fac;
if (res_scale > 0.7f) {
sphere_lod_ = DRW_LOD_HIGH;
}
else if (res_scale > 0.25f) {
sphere_lod_ = DRW_LOD_MEDIUM;
}
else {
sphere_lod_ = DRW_LOD_LOW;
}
sphere_size_ = sphere_size;
anchor_ = anchor;
inst_.sampling.reset();
}
}
else if (sphere_size_ != 0) {
sphere_size_ = 0;
inst_.sampling.reset();
}
}
bool LookDev::do_overlay(const ivec2 &output_res, const rcti *render_border)
{
const View3D *v3d = inst_.v3d;
/* Only show the HDRI Preview in Shading Preview in the Viewport. */
if (v3d == nullptr || v3d->shading.type != OB_MATERIAL) {
return false;
}
/* Only show the HDRI Preview when viewing the Combined render pass */
if (v3d->shading.render_pass != SCE_PASS_COMBINED) {
return false;
}
if (v3d->flag2 & V3D_HIDE_OVERLAYS) {
return false;
}
if ((v3d->overlay.flag & V3D_OVERLAY_LOOK_DEV) == 0) {
return false;
}
if (inst_.camera.is_panoramic()) {
return false;
}
if (output_res != ivec2(BLI_rcti_size_x(render_border), BLI_rcti_size_y(render_border))) {
/* TODO(fclem) support this case. */
return false;
}
return true;
}
bool LookDev::sync_world(void)
{
if (studiolight_ == nullptr) {
return false;
}
/* World light probes render. */
bNodeTree *nodetree = world_tree.nodetree_get(instensity_);
GPUMaterial *gpumat = inst_.shaders.material_shader_get(
"LookDev", material, nodetree, MAT_PIPE_FORWARD, MAT_GEOM_WORLD, true);
BKE_studiolight_ensure_flag(studiolight_, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE);
GPUTexture *gputex = studiolight_->equirect_radiance_gputexture;
if (gputex == nullptr) {
return false;
}
inst_.shading_passes.background.sync(gpumat, gputex);
return true;
}
void LookDev::rotation_get(mat4 r_mat)
{
if (studiolight_ == nullptr) {
unit_m4(r_mat);
}
else {
axis_angle_to_mat4_single(r_mat, 'Z', rotation_);
}
if (view_rotation_) {
float x_rot_matrix[4][4];
const CameraData &cam = inst_.camera.data_get();
axis_angle_to_mat4_single(x_rot_matrix, 'X', M_PI / 2.0f);
mul_m4_m4m4(x_rot_matrix, x_rot_matrix, cam.viewmat);
mul_m4_m4m4(r_mat, r_mat, x_rot_matrix);
}
}
void LookDev::sync_background(void)
{
if (studiolight_ == nullptr) {
return;
}
/* Viewport display. */
background_ps_ = DRW_pass_create("LookDev.Background", DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(LOOKDEV_BACKGROUND);
DRWShadingGroup *grp = DRW_shgroup_create(sh, background_ps_);
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", inst_.lightprobes.cube_tx_ref_get());
DRW_shgroup_uniform_block(grp, "lightprobes_info_block", inst_.lightprobes.info_ubo_get());
DRW_shgroup_uniform_float_copy(grp, "blur", clamp_f(blur_, 0.0f, 0.99999f));
DRW_shgroup_uniform_float_copy(grp, "opacity", opacity_);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
/* Renders background using lightcache. */
bool LookDev::render_background(void)
{
if (studiolight_ == nullptr) {
return false;
}
DRW_draw_pass(background_ps_);
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name LookDev Reference spheres
*
* Render reference spheres into a separate framebuffer to not distrub the main rendering.
* The final texture is composited onto the render.
* \{ */
void LookDev::sync_overlay(void)
{
if (sphere_size_ == 0) {
return;
}
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS |
DRW_STATE_CULL_BACK;
overlay_ps_ = DRW_pass_create("LookDev.Overlay", state);
GPUBatch *sphere = DRW_cache_sphere_get(sphere_lod_);
const CameraData &cam = inst_.camera.data_get();
LightModule &lights = inst_.lights;
LightProbeModule &lightprobes = inst_.lightprobes;
/* Jitter for AA. */
vec2 jitter = -0.5f + vec2(inst_.sampling.rng_get(SAMPLING_FILTER_U),
inst_.sampling.rng_get(SAMPLING_FILTER_V));
/* Matrix used to position the spheres in viewport space. */
mat4 sphere_mat;
copy_m4_m4(sphere_mat, cam.viewmat);
const float *viewport_size = DRW_viewport_size_get();
const int sphere_margin = sphere_size_ / 6;
vec2 offset = vec2(0, sphere_margin);
std::array<::Material *, 2> materials = {inst_.materials.diffuse_mat_,
inst_.materials.glossy_mat_};
for (::Material *mat : materials) {
GPUMaterial *gpumat = inst_.shaders.material_shader_get(
mat, mat->nodetree, MAT_PIPE_FORWARD, MAT_GEOM_LOOKDEV, false);
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, overlay_ps_);
DRW_shgroup_uniform_block_ref(grp, "lights_block", lights.lights_ubo_ref_get());
DRW_shgroup_uniform_block_ref(grp, "shadows_punctual_block", lights.shadows_ubo_ref_get());
DRW_shgroup_uniform_block_ref(grp, "lights_culling_block", lights.culling_ubo_ref_get());
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
DRW_shgroup_uniform_block(grp, "grids_block", lightprobes.grid_ubo_get());
DRW_shgroup_uniform_block(grp, "cubes_block", lightprobes.cube_ubo_get());
DRW_shgroup_uniform_block(grp, "lightprobes_info_block", lightprobes.info_ubo_get());
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get());
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
DRW_shgroup_uniform_texture_ref(grp, "lights_culling_tx", lights.culling_tx_ref_get());
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
DRW_shgroup_uniform_texture_ref(grp, "shadow_atlas_tx", inst_.shadows.atlas_ref_get());
offset.x -= sphere_size_ + sphere_margin;
/* Pass 2D scale and bias factor in the last column. */
vec2 scale = sphere_size_ / vec2(viewport_size);
vec2 bias = -1.0f + scale + 2.0f * (anchor_ + offset + jitter) / vec2(viewport_size);
copy_v4_fl4(sphere_mat[3], UNPACK2(scale), UNPACK2(bias));
DRW_shgroup_call_obmat(grp, sphere, sphere_mat);
offset.x -= sphere_margin;
}
view_ = nullptr;
}
/* Renders the reference spheres. */
void LookDev::render_overlay(GPUFrameBuffer *fb)
{
if (sphere_size_ == 0) {
return;
}
const DRWView *active_view = DRW_view_get_active();
inst_.lightprobes.set_view(active_view, ivec2(0));
inst_.lights.set_view(active_view, ivec2(0));
inst_.lights.bind_batch(0);
/* Create subview for correct shading. Sub because we don not care about culling. */
const CameraData &cam = inst_.camera.data_get();
mat4 winmat;
orthographic_m4(winmat, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
if (view_) {
DRW_view_update_sub(view_, cam.viewmat, winmat);
}
else {
view_ = DRW_view_create_sub(active_view, cam.viewmat, winmat);
}
DRW_view_set_active(view_);
GPU_framebuffer_bind(fb);
DRW_draw_pass(overlay_ps_);
DRW_view_set_active(active_view);
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,112 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2018, Blender Foundation.
*/
/** \file
* \ingroup eevee
*/
#pragma once
#include "BKE_studiolight.h"
#include "DNA_world_types.h"
#include "DRW_render.h"
namespace blender::eevee {
class Instance;
/* -------------------------------------------------------------------- */
/** \name Lookdev World Nodetree
*
* \{ */
class LookDevWorldNodeTree {
private:
bNodeTree *ntree_;
bNodeSocketValueFloat *strength_socket_;
public:
LookDevWorldNodeTree();
~LookDevWorldNodeTree();
bNodeTree *nodetree_get(float strength);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Look Dev
*
* \{ */
class LookDev {
private:
Instance &inst_;
/** Nodetree used to render the world reflection cubemap and irradiance. */
LookDevWorldNodeTree world_tree;
/** Compiled gpu material for the nodetree. Owned. */
ListBase material = {nullptr, nullptr};
/** Choosen studio light. */
StudioLight *studiolight_ = nullptr;
int studiolight_index_ = -1;
/** Draw pass to draw the viewport background. */
DRWPass *background_ps_ = nullptr;
/** Parameters. */
float instensity_ = -1.0f;
float blur_ = -1.0f;
float opacity_ = -1.0f;
float rotation_ = -9999.0f;
bool view_rotation_ = false;
/** Overlay (reference spheres). */
DRWPass *overlay_ps_ = nullptr;
/** View based on main view with orthographic projection. Without this, shading is incorrect. */
DRWView *view_ = nullptr;
/** Selected LOD of the sphere mesh. */
eDRWLevelOfDetail sphere_lod_;
/** Screen space radius in pixels. */
int sphere_size_ = 0;
/** Lower right corner of the area where we can start drawing. */
ivec2 anchor_;
public:
LookDev(Instance &inst) : inst_(inst){};
~LookDev()
{
GPU_material_free(&material);
};
void init(const ivec2 &output_res, const rcti *render_border);
void sync_background(void);
bool sync_world(void);
void sync_overlay(void);
bool render_background(void);
void render_overlay(GPUFrameBuffer *view_fb);
void rotation_get(mat4 r_mat);
private:
bool do_overlay(const ivec2 &output_res, const rcti *render_border);
};
/** \} */
} // namespace blender::eevee

View File

@@ -23,9 +23,17 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
extern const float ltc_mat_ggx[64 * 64 * 4];
extern const float ltc_mag_ggx[64 * 64 * 2];
extern const float bsdf_split_sum_ggx[64 * 64 * 2];
extern const float ltc_disk_integral[64 * 64];
extern const float btdf_split_sum_ggx[16][64 * 64 * 2];
extern const float blue_noise[64 * 64][4];
#ifdef __cplusplus
}
#endif

View File

@@ -1,123 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2020, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* EEVEE LUT generation:
*
* Routine to generate the LUT used by eevee stored in eevee_lut.h
* These functions are not to be used in the final executable.
*/
#include "DRW_render.h"
#include "BLI_fileops.h"
#include "BLI_rand.h"
#include "BLI_string_utils.h"
#include "eevee_private.h"
#define DO_FILE_OUTPUT 0
float *EEVEE_lut_update_ggx_brdf(int lut_size)
{
DRWPass *pass = DRW_pass_create(__func__, DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_ggx_lut_sh_get(), pass);
DRW_shgroup_uniform_float_copy(grp, "sampleCount", 64.0f); /* Actual sample count is squared. */
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
GPUTexture *tex = DRW_texture_create_2d(lut_size, lut_size, GPU_RG16F, 0, NULL);
GPUFrameBuffer *fb = NULL;
GPU_framebuffer_ensure_config(&fb,
{
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(tex),
});
GPU_framebuffer_bind(fb);
DRW_draw_pass(pass);
GPU_FRAMEBUFFER_FREE_SAFE(fb);
float *data = GPU_texture_read(tex, GPU_DATA_FLOAT, 0);
GPU_texture_free(tex);
#if DO_FILE_OUTPUT
/* Content is to be put inside eevee_lut.c */
FILE *f = BLI_fopen("bsdf_split_sum_ggx.h", "w");
fprintf(f, "const float bsdf_split_sum_ggx[%d * %d * 2] = {", lut_size, lut_size);
for (int i = 0; i < lut_size * lut_size * 2;) {
fprintf(f, "\n ");
for (int j = 0; j < 4; j++, i += 2) {
fprintf(f, "%ff, %ff, ", data[i], data[i + 1]);
}
}
fprintf(f, "\n};\n");
fclose(f);
#endif
return data;
}
float *EEVEE_lut_update_ggx_btdf(int lut_size, int lut_depth)
{
float roughness;
DRWPass *pass = DRW_pass_create(__func__, DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_ggx_refraction_lut_sh_get(), pass);
DRW_shgroup_uniform_float_copy(grp, "sampleCount", 64.0f); /* Actual sample count is squared. */
DRW_shgroup_uniform_float(grp, "z", &roughness, 1);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
GPUTexture *tex = DRW_texture_create_2d_array(lut_size, lut_size, lut_depth, GPU_RG16F, 0, NULL);
GPUFrameBuffer *fb = NULL;
for (int i = 0; i < lut_depth; i++) {
GPU_framebuffer_ensure_config(&fb,
{
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE_LAYER(tex, i),
});
GPU_framebuffer_bind(fb);
roughness = i / (lut_depth - 1.0f);
DRW_draw_pass(pass);
}
GPU_FRAMEBUFFER_FREE_SAFE(fb);
float *data = GPU_texture_read(tex, GPU_DATA_FLOAT, 0);
GPU_texture_free(tex);
#if DO_FILE_OUTPUT
/* Content is to be put inside eevee_lut.c. Don't forget to format the output. */
FILE *f = BLI_fopen("btdf_split_sum_ggx.h", "w");
fprintf(f, "const float btdf_split_sum_ggx[%d][%d * %d * 2] = {", lut_depth, lut_size, lut_size);
fprintf(f, "\n ");
int ofs = 0;
for (int d = 0; d < lut_depth; d++) {
fprintf(f, "{\n");
for (int i = 0; i < lut_size * lut_size * 2;) {
for (int j = 0; j < 4; j++, i += 2, ofs += 2) {
fprintf(f, "%ff, %ff, ", data[ofs], data[ofs + 1]);
}
fprintf(f, "\n ");
}
fprintf(f, "},\n");
}
fprintf(f, "};\n");
fclose(f);
#endif
return data;
}

View File

@@ -0,0 +1,329 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*/
#include "DNA_material_types.h"
#include "BKE_lib_id.h"
#include "BKE_material.h"
#include "BKE_node.h"
#include "NOD_shader.h"
#include "eevee_instance.hh"
#include "eevee_material.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name Default Material
*
* \{ */
DefaultSurfaceNodeTree::DefaultSurfaceNodeTree()
{
bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_PRINCIPLED);
bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_MATERIAL);
bNodeSocket *bsdf_out = nodeFindSocket(bsdf, SOCK_OUT, "BSDF");
bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface");
nodeAddLink(ntree, bsdf, bsdf_out, output, output_in);
nodeSetActive(ntree, output);
color_socket_ =
(bNodeSocketValueRGBA *)nodeFindSocket(bsdf, SOCK_IN, "Base Color")->default_value;
metallic_socket_ =
(bNodeSocketValueFloat *)nodeFindSocket(bsdf, SOCK_IN, "Metallic")->default_value;
roughness_socket_ =
(bNodeSocketValueFloat *)nodeFindSocket(bsdf, SOCK_IN, "Roughness")->default_value;
specular_socket_ =
(bNodeSocketValueFloat *)nodeFindSocket(bsdf, SOCK_IN, "Specular")->default_value;
ntree_ = ntree;
}
DefaultSurfaceNodeTree::~DefaultSurfaceNodeTree()
{
ntreeFreeEmbeddedTree(ntree_);
MEM_SAFE_FREE(ntree_);
}
/* Configure a default nodetree with the given material. */
bNodeTree *DefaultSurfaceNodeTree::nodetree_get(::Material *ma)
{
/* WARNING: This function is not threadsafe. Which is not a problem for the moment. */
copy_v3_fl3(color_socket_->value, ma->r, ma->g, ma->b);
metallic_socket_->value = ma->metallic;
roughness_socket_->value = ma->roughness;
specular_socket_->value = ma->spec;
return ntree_;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Material
*
* \{ */
MaterialModule::MaterialModule(Instance &inst) : inst_(inst)
{
{
bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
diffuse_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default diffuse");
diffuse_mat_->nodetree = ntree;
diffuse_mat_->use_nodes = true;
/* To use the forward pipeline. */
diffuse_mat_->blend_method = MA_BM_BLEND;
bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_DIFFUSE);
bNodeSocket *base_color = nodeFindSocket(bsdf, SOCK_IN, "Color");
copy_v3_fl(((bNodeSocketValueRGBA *)base_color->default_value)->value, 0.8f);
bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_MATERIAL);
nodeAddLink(ntree,
bsdf,
nodeFindSocket(bsdf, SOCK_OUT, "BSDF"),
output,
nodeFindSocket(output, SOCK_IN, "Surface"));
nodeSetActive(ntree, output);
}
{
bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
glossy_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default metal");
glossy_mat_->nodetree = ntree;
glossy_mat_->use_nodes = true;
/* To use the forward pipeline. */
glossy_mat_->blend_method = MA_BM_BLEND;
bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_GLOSSY);
bNodeSocket *base_color = nodeFindSocket(bsdf, SOCK_IN, "Color");
copy_v3_fl(((bNodeSocketValueRGBA *)base_color->default_value)->value, 1.0f);
bNodeSocket *roughness = nodeFindSocket(bsdf, SOCK_IN, "Roughness");
((bNodeSocketValueFloat *)roughness->default_value)->value = 0.0f;
bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_MATERIAL);
nodeAddLink(ntree,
bsdf,
nodeFindSocket(bsdf, SOCK_OUT, "BSDF"),
output,
nodeFindSocket(output, SOCK_IN, "Surface"));
nodeSetActive(ntree, output);
}
{
bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
error_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default error");
error_mat_->nodetree = ntree;
error_mat_->use_nodes = true;
/* Use emission and output material to be compatible with both World and Material. */
bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_EMISSION);
bNodeSocket *color = nodeFindSocket(bsdf, SOCK_IN, "Color");
copy_v3_fl3(((bNodeSocketValueRGBA *)color->default_value)->value, 1.0f, 0.0f, 1.0f);
bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_MATERIAL);
nodeAddLink(ntree,
bsdf,
nodeFindSocket(bsdf, SOCK_OUT, "Emission"),
output,
nodeFindSocket(output, SOCK_IN, "Surface"));
nodeSetActive(ntree, output);
}
}
MaterialModule::~MaterialModule()
{
for (Material *mat : material_map_.values()) {
delete mat;
mat = nullptr;
}
for (DRWShadingGroup **shgroup : shader_map_.values()) {
delete shgroup;
shgroup = nullptr;
}
BKE_id_free(NULL, glossy_mat_);
BKE_id_free(NULL, diffuse_mat_);
BKE_id_free(NULL, error_mat_);
}
void MaterialModule::begin_sync(void)
{
queued_shaders_count_ = 0;
for (Material *mat : material_map_.values()) {
mat->init = false;
}
for (DRWShadingGroup **shgroup : shader_map_.values()) {
*shgroup = nullptr;
}
}
MaterialPass MaterialModule::material_pass_get(::Material *blender_mat,
eMaterialPipeline pipeline_type,
eMaterialGeometry geometry_type)
{
bNodeTree *ntree = (blender_mat->use_nodes && blender_mat->nodetree != nullptr) ?
blender_mat->nodetree :
default_surface_ntree_.nodetree_get(blender_mat);
MaterialPass matpass;
matpass.gpumat = inst_.shaders.material_shader_get(
blender_mat, ntree, pipeline_type, geometry_type, true);
switch (GPU_material_status(matpass.gpumat)) {
case GPU_MAT_SUCCESS:
break;
case GPU_MAT_QUEUED:
queued_shaders_count_++;
blender_mat = (geometry_type == MAT_GEOM_VOLUME) ? BKE_material_default_volume() :
BKE_material_default_surface();
matpass.gpumat = inst_.shaders.material_shader_get(
blender_mat, blender_mat->nodetree, pipeline_type, geometry_type, false);
break;
case GPU_MAT_FAILED:
default:
matpass.gpumat = inst_.shaders.material_shader_get(
error_mat_, error_mat_->nodetree, pipeline_type, geometry_type, false);
break;
}
/* Returned material should be ready to be drawn. */
BLI_assert(GPU_material_status(matpass.gpumat) == GPU_MAT_SUCCESS);
if (GPU_material_recalc_flag_get(matpass.gpumat)) {
inst_.sampling.reset();
}
if ((pipeline_type == MAT_PIPE_DEFERRED) &&
GPU_material_flag_get(matpass.gpumat, GPU_MATFLAG_SHADER_TO_RGBA)) {
pipeline_type = MAT_PIPE_FORWARD;
}
if ((pipeline_type == MAT_PIPE_FORWARD) &&
GPU_material_flag_get(matpass.gpumat, GPU_MATFLAG_TRANSPARENT)) {
/* Transparent needs to use one shgroup per object to support reordering. */
matpass.shgrp = inst_.shading_passes.material_add(blender_mat, matpass.gpumat, pipeline_type);
}
else {
ShaderKey shader_key(matpass.gpumat, geometry_type, pipeline_type);
/* TODO(fclem) allocate in blocks to avoid memory fragmentation. */
auto add_cb = [&]() { return new DRWShadingGroup *(); };
DRWShadingGroup *&grp = *shader_map_.lookup_or_add_cb(shader_key, add_cb);
if (grp == nullptr) {
/* First time encountering this shader. Create a shading group. */
grp = inst_.shading_passes.material_add(blender_mat, matpass.gpumat, pipeline_type);
}
if (grp != nullptr) {
/* Shading group for this shader already exists. Create a sub one for this material. */
/* IMPORTANT: We always create a subgroup so that all subgroups are inserted after the
* first "empty" shgroup. This avoids messing the order of subgroups when there is more
* nested subgroup (i.e: hair drawing). */
/* TODO(fclem) Remove material resource binding from the first group creation. */
matpass.shgrp = DRW_shgroup_create_sub(grp);
DRW_shgroup_add_material_resources(matpass.shgrp, matpass.gpumat);
}
}
return matpass;
}
Material &MaterialModule::material_sync(::Material *blender_mat, eMaterialGeometry geometry_type)
{
eMaterialPipeline surface_pipe = (blender_mat->blend_method == MA_BM_BLEND) ? MAT_PIPE_FORWARD :
MAT_PIPE_DEFERRED;
eMaterialPipeline prepass_pipe = (blender_mat->blend_method == MA_BM_BLEND) ?
MAT_PIPE_FORWARD_PREPASS :
MAT_PIPE_DEFERRED_PREPASS;
MaterialKey material_key(blender_mat, geometry_type, surface_pipe);
/* TODO allocate in blocks to avoid memory fragmentation. */
auto add_cb = [&]() { return new Material(); };
Material &mat = *material_map_.lookup_or_add_cb(material_key, add_cb);
/* Forward pipeline needs to use one shgroup per object. */
if (mat.init == false || (surface_pipe == MAT_PIPE_FORWARD)) {
mat.init = true;
/* Order is important for transparent. */
mat.prepass = material_pass_get(blender_mat, prepass_pipe, geometry_type);
mat.shading = material_pass_get(blender_mat, surface_pipe, geometry_type);
mat.shadow = material_pass_get(blender_mat, MAT_PIPE_SHADOW, geometry_type);
}
return mat;
}
/* Return correct material or empty default material if slot is empty. */
::Material *MaterialModule::material_from_slot(Object *ob, int slot)
{
if (ob->base_flag & BASE_HOLDOUT) {
return BKE_material_default_holdout();
}
::Material *ma = BKE_object_material_get(ob, slot + 1);
if (ma == nullptr) {
if (ob->type == OB_VOLUME) {
return BKE_material_default_volume();
}
else {
return BKE_material_default_surface();
}
}
return ma;
}
/* Return Material references are valid until the next call to this function or
* material_get(). */
MaterialArray &MaterialModule::material_array_get(Object *ob)
{
material_array_.materials.clear();
material_array_.gpu_materials.clear();
for (auto i : IndexRange(max_ii(1, ob->totcol))) {
::Material *blender_mat = material_from_slot(ob, i);
Material &mat = material_sync(blender_mat, to_material_geometry(ob));
material_array_.materials.append(&mat);
material_array_.gpu_materials.append(mat.shading.gpumat);
}
return material_array_;
}
/* Return Material references are valid until the next call to this function or
* material_array_get(). */
Material &MaterialModule::material_get(Object *ob, int mat_nr, eMaterialGeometry geometry_type)
{
::Material *blender_mat = material_from_slot(ob, mat_nr);
Material &mat = material_sync(blender_mat, geometry_type);
return mat;
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,122 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*/
#pragma once
#include "DRW_render.h"
#include "BLI_map.hh"
#include "BLI_vector.hh"
#include "GPU_material.h"
#include "eevee_id_map.hh"
namespace blender::eevee {
class Instance;
/* -------------------------------------------------------------------- */
/** \name Default Material Nodetree
*
* In order to support materials without nodetree we reuse and configure a standalone nodetree that
* we pass for shader generation. The GPUMaterial is still stored inside the Material even if
* it does not use a nodetree.
*
* \{ */
class DefaultSurfaceNodeTree {
private:
bNodeTree *ntree_;
bNodeSocketValueRGBA *color_socket_;
bNodeSocketValueFloat *metallic_socket_;
bNodeSocketValueFloat *roughness_socket_;
bNodeSocketValueFloat *specular_socket_;
public:
DefaultSurfaceNodeTree();
~DefaultSurfaceNodeTree();
bNodeTree *nodetree_get(::Material *ma);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Material
*
* \{ */
struct MaterialPass {
GPUMaterial *gpumat = nullptr;
DRWShadingGroup *shgrp = nullptr;
};
struct Material {
bool init = false;
MaterialPass shadow, shading, prepass;
};
struct MaterialArray {
Vector<Material *> materials;
Vector<GPUMaterial *> gpu_materials;
};
class MaterialModule {
public:
::Material *diffuse_mat_;
::Material *glossy_mat_;
private:
Instance &inst_;
Map<MaterialKey, Material *> material_map_;
Map<ShaderKey, DRWShadingGroup **> shader_map_;
MaterialArray material_array_;
DefaultSurfaceNodeTree default_surface_ntree_;
::Material *error_mat_;
int64_t queued_shaders_count_ = 0;
public:
MaterialModule(Instance &inst);
~MaterialModule();
void begin_sync(void);
MaterialArray &material_array_get(Object *ob);
Material &material_get(Object *ob, int mat_nr, eMaterialGeometry geometry_type);
private:
Material &material_sync(::Material *blender_mat, eMaterialGeometry geometry_type);
::Material *material_from_slot(Object *ob, int slot);
MaterialPass material_pass_get(::Material *blender_mat,
eMaterialPipeline pipeline_type,
eMaterialGeometry geometry_type);
};
/** \} */
} // namespace blender::eevee

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,49 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*/
#include "eevee_instance.hh"
namespace blender::eevee {
void Instance::mesh_sync(Object *ob, ObjectHandle &ob_handle)
{
MaterialArray &material_array = materials.material_array_get(ob);
GPUBatch **mat_geom = DRW_cache_object_surface_material_get(
ob, material_array.gpu_materials.data(), material_array.gpu_materials.size());
for (auto i : material_array.gpu_materials.index_range()) {
GPUBatch *geom = mat_geom[i];
if (geom == nullptr) {
continue;
}
Material *material = material_array.materials[i];
shgroup_geometry_call(material->shading.shgrp, ob, geom);
shgroup_geometry_call(material->prepass.shgrp, ob, geom);
shgroup_geometry_call(material->shadow.shgrp, ob, geom);
}
shading_passes.velocity.mesh_add(ob, ob_handle);
shadows.sync_caster(ob, ob_handle);
}
} // namespace blender::eevee

View File

@@ -1,113 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Implementation of Blender Mist pass.
* IMPORTANT: This is a "post process" of the Z depth so it will lack any transparent objects.
*/
#include "DRW_engine.h"
#include "DRW_render.h"
#include "DNA_world_types.h"
#include "BLI_string_utils.h"
#include "eevee_private.h"
void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
EEVEE_FramebufferList *fbl = vedata->fbl;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
EEVEE_TextureList *txl = vedata->txl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PassList *psl = vedata->psl;
EEVEE_PrivateData *g_data = stl->g_data;
Scene *scene = draw_ctx->scene;
/* Create FrameBuffer. */
/* Should be enough precision for many samples. */
DRW_texture_ensure_fullscreen_2d(&txl->mist_accum, GPU_R32F, 0);
GPU_framebuffer_ensure_config(&fbl->mist_accum_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->mist_accum)});
/* Mist settings. */
if (scene && scene->world) {
g_data->mist_start = scene->world->miststa;
g_data->mist_inv_dist = (scene->world->mistdist > 0.0f) ? 1.0f / scene->world->mistdist : 0.0f;
switch (scene->world->mistype) {
case WO_MIST_QUADRATIC:
g_data->mist_falloff = 2.0f;
break;
case WO_MIST_LINEAR:
g_data->mist_falloff = 1.0f;
break;
case WO_MIST_INVERSE_QUADRATIC:
g_data->mist_falloff = 0.5f;
break;
}
}
else {
float near = DRW_view_near_distance_get(NULL);
float far = DRW_view_far_distance_get(NULL);
/* Fallback */
g_data->mist_start = near;
g_data->mist_inv_dist = 1.0f / fabsf(far - near);
g_data->mist_falloff = 1.0f;
}
/* XXX ??!! WHY? If not it does not match cycles. */
g_data->mist_falloff *= 0.5f;
/* Create Pass and shgroup. */
DRW_PASS_CREATE(psl->mist_accum_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD);
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_effect_mist_sh_get(),
psl->mist_accum_ps);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_uniform_vec3(grp, "mistSettings", &g_data->mist_start, 1);
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
}
void EEVEE_mist_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_PassList *psl = vedata->psl;
EEVEE_EffectsInfo *effects = vedata->stl->effects;
if (fbl->mist_accum_fb != NULL) {
GPU_framebuffer_bind(fbl->mist_accum_fb);
/* Clear texture. */
if (effects->taa_current_sample == 1) {
const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_framebuffer_clear_color(fbl->mist_accum_fb, clear);
}
DRW_draw_pass(psl->mist_accum_ps);
/* Restore */
GPU_framebuffer_bind(fbl->main_fb);
}
}

View File

@@ -1,604 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Gather all screen space effects technique such as Bloom, Motion Blur, DoF, SSAO, SSR, ...
*/
#include "DRW_render.h"
#include "BLI_rand.h"
#include "BLI_string_utils.h"
#include "BKE_animsys.h"
#include "BKE_camera.h"
#include "BKE_object.h"
#include "BKE_screen.h"
#include "DNA_anim_types.h"
#include "DNA_camera_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_particle_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_screen_types.h"
#include "ED_screen.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "GPU_batch.h"
#include "GPU_texture.h"
#include "eevee_private.h"
int EEVEE_motion_blur_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_EffectsInfo *effects = stl->effects;
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
/* Viewport not supported for now. */
if (!DRW_state_is_scene_render()) {
return 0;
}
effects->motion_blur_max = max_ii(0, scene->eevee.motion_blur_max);
if ((effects->motion_blur_max > 0) && (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED)) {
if (DRW_state_is_scene_render()) {
int mb_step = effects->motion_blur_step;
DRW_view_viewmat_get(NULL, effects->motion_blur.camera[mb_step].viewmat, false);
DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persmat, false);
DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persinv, true);
}
const float *fs_size = DRW_viewport_size_get();
const int tx_size[2] = {
1 + ((int)fs_size[0] / EEVEE_VELOCITY_TILE_SIZE),
1 + ((int)fs_size[1] / EEVEE_VELOCITY_TILE_SIZE),
};
effects->velocity_tiles_x_tx = DRW_texture_pool_query_2d(
tx_size[0], fs_size[1], GPU_RGBA16, &draw_engine_eevee_type);
GPU_framebuffer_ensure_config(&fbl->velocity_tiles_fb[0],
{
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(effects->velocity_tiles_x_tx),
});
effects->velocity_tiles_tx = DRW_texture_pool_query_2d(
tx_size[0], tx_size[1], GPU_RGBA16, &draw_engine_eevee_type);
GPU_framebuffer_ensure_config(&fbl->velocity_tiles_fb[1],
{
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(effects->velocity_tiles_tx),
});
return EFFECT_MOTION_BLUR | EFFECT_POST_BUFFER | EFFECT_VELOCITY_BUFFER;
}
return 0;
}
void EEVEE_motion_blur_step_set(EEVEE_Data *vedata, int step)
{
BLI_assert(step < 3);
vedata->stl->effects->motion_blur_step = step;
}
static void eevee_motion_blur_sync_camera(EEVEE_Data *vedata)
{
EEVEE_EffectsInfo *effects = vedata->stl->effects;
if (DRW_state_is_scene_render()) {
int mb_step = effects->motion_blur_step;
DRW_view_viewmat_get(NULL, effects->motion_blur.camera[mb_step].viewmat, false);
DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persmat, false);
DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persinv, true);
}
effects->motion_blur_near_far[0] = fabsf(DRW_view_near_distance_get(NULL));
effects->motion_blur_near_far[1] = fabsf(DRW_view_far_distance_get(NULL));
}
void EEVEE_motion_blur_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_MotionBlurData *mb_data = &effects->motion_blur;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
if ((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0) {
const float *fs_size = DRW_viewport_size_get();
const int tx_size[2] = {
GPU_texture_width(effects->velocity_tiles_tx),
GPU_texture_height(effects->velocity_tiles_tx),
};
eevee_motion_blur_sync_camera(vedata);
DRWShadingGroup *grp;
{
DRW_PASS_CREATE(psl->velocity_tiles_x, DRW_STATE_WRITE_COLOR);
DRW_PASS_CREATE(psl->velocity_tiles, DRW_STATE_WRITE_COLOR);
/* Create max velocity tiles in 2 passes. One for X and one for Y */
GPUShader *sh = EEVEE_shaders_effect_motion_blur_velocity_tiles_sh_get();
grp = DRW_shgroup_create(sh, psl->velocity_tiles_x);
DRW_shgroup_uniform_texture(grp, "velocityBuffer", effects->velocity_tx);
DRW_shgroup_uniform_ivec2_copy(grp, "velocityBufferSize", (int[2]){fs_size[0], fs_size[1]});
DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1);
DRW_shgroup_uniform_vec2(grp, "viewportSizeInv", DRW_viewport_invert_size_get(), 1);
DRW_shgroup_uniform_ivec2_copy(grp, "gatherStep", (int[2]){1, 0});
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
grp = DRW_shgroup_create(sh, psl->velocity_tiles);
DRW_shgroup_uniform_texture(grp, "velocityBuffer", effects->velocity_tiles_x_tx);
DRW_shgroup_uniform_ivec2_copy(grp, "velocityBufferSize", (int[2]){tx_size[0], fs_size[1]});
DRW_shgroup_uniform_ivec2_copy(grp, "gatherStep", (int[2]){0, 1});
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
/* Expand max tiles by keeping the max tile in each tile neighborhood. */
DRW_PASS_CREATE(psl->velocity_tiles_expand[0], DRW_STATE_WRITE_COLOR);
DRW_PASS_CREATE(psl->velocity_tiles_expand[1], DRW_STATE_WRITE_COLOR);
for (int i = 0; i < 2; i++) {
GPUTexture *tile_tx = (i == 0) ? effects->velocity_tiles_tx : effects->velocity_tiles_x_tx;
GPUShader *sh_expand = EEVEE_shaders_effect_motion_blur_velocity_tiles_expand_sh_get();
grp = DRW_shgroup_create(sh_expand, psl->velocity_tiles_expand[i]);
DRW_shgroup_uniform_ivec2_copy(grp, "velocityBufferSize", tx_size);
DRW_shgroup_uniform_texture(grp, "velocityBuffer", tile_tx);
DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1);
DRW_shgroup_uniform_vec2(grp, "viewportSizeInv", DRW_viewport_invert_size_get(), 1);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
}
{
DRW_PASS_CREATE(psl->motion_blur, DRW_STATE_WRITE_COLOR);
eGPUSamplerState state = 0;
int expand_steps = 1 + (max_ii(0, effects->motion_blur_max - 1) / EEVEE_VELOCITY_TILE_SIZE);
GPUTexture *tile_tx = (expand_steps & 1) ? effects->velocity_tiles_x_tx :
effects->velocity_tiles_tx;
grp = DRW_shgroup_create(EEVEE_shaders_effect_motion_blur_sh_get(), psl->motion_blur);
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
DRW_shgroup_uniform_texture_ref_ex(grp, "colorBuffer", &effects->source_buffer, state);
DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &dtxl->depth, state);
DRW_shgroup_uniform_texture_ref_ex(grp, "velocityBuffer", &effects->velocity_tx, state);
DRW_shgroup_uniform_texture(grp, "tileMaxBuffer", tile_tx);
DRW_shgroup_uniform_float_copy(grp, "depthScale", scene->eevee.motion_blur_depth_scale);
DRW_shgroup_uniform_vec2(grp, "nearFar", effects->motion_blur_near_far, 1);
DRW_shgroup_uniform_bool_copy(grp, "isPerspective", DRW_view_is_persp_get(NULL));
DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1);
DRW_shgroup_uniform_vec2(grp, "viewportSizeInv", DRW_viewport_invert_size_get(), 1);
DRW_shgroup_uniform_ivec2_copy(grp, "tileBufferSize", tx_size);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
{
DRW_PASS_CREATE(psl->velocity_object, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
grp = DRW_shgroup_create(EEVEE_shaders_effect_motion_blur_object_sh_get(),
psl->velocity_object);
DRW_shgroup_uniform_mat4(grp, "prevViewProjMatrix", mb_data->camera[MB_PREV].persmat);
DRW_shgroup_uniform_mat4(grp, "currViewProjMatrix", mb_data->camera[MB_CURR].persmat);
DRW_shgroup_uniform_mat4(grp, "nextViewProjMatrix", mb_data->camera[MB_NEXT].persmat);
DRW_PASS_CREATE(psl->velocity_hair, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
mb_data->hair_grp = grp = DRW_shgroup_create(EEVEE_shaders_effect_motion_blur_hair_sh_get(),
psl->velocity_hair);
DRW_shgroup_uniform_mat4(grp, "prevViewProjMatrix", mb_data->camera[MB_PREV].persmat);
DRW_shgroup_uniform_mat4(grp, "currViewProjMatrix", mb_data->camera[MB_CURR].persmat);
DRW_shgroup_uniform_mat4(grp, "nextViewProjMatrix", mb_data->camera[MB_NEXT].persmat);
DRW_pass_link(psl->velocity_object, psl->velocity_hair);
}
EEVEE_motion_blur_data_init(mb_data);
}
else {
psl->motion_blur = NULL;
psl->velocity_object = NULL;
psl->velocity_hair = NULL;
}
}
void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
EEVEE_Data *vedata,
Object *ob,
ParticleSystem *psys,
ModifierData *md)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
DRWShadingGroup *grp = NULL;
if (!DRW_state_is_scene_render() || psl->velocity_hair == NULL) {
return;
}
/* For now we assume hair objects are always moving. */
EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(
&effects->motion_blur, ob, true);
if (mb_data) {
int mb_step = effects->motion_blur_step;
/* Store transform. */
DRW_hair_duplimat_get(ob, psys, md, mb_data->obmat[mb_step]);
EEVEE_HairMotionData *mb_hair = EEVEE_motion_blur_hair_data_get(&effects->motion_blur, ob);
int psys_id = (md != NULL) ? BLI_findindex(&ob->modifiers, md) : 0;
if (psys_id >= mb_hair->psys_len) {
/* This should never happen. It means the modifier list was changed by frame evaluation. */
BLI_assert(0);
return;
}
if (mb_step == MB_CURR) {
/* Fill missing matrices if the object was hidden in previous or next frame. */
if (is_zero_m4(mb_data->obmat[MB_PREV])) {
copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]);
}
if (is_zero_m4(mb_data->obmat[MB_NEXT])) {
copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]);
}
GPUTexture *tex_prev = mb_hair->psys[psys_id].hair_pos_tx[MB_PREV];
GPUTexture *tex_next = mb_hair->psys[psys_id].hair_pos_tx[MB_NEXT];
grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp, NULL);
DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]);
DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]);
DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]);
DRW_shgroup_uniform_texture(grp, "prvBuffer", tex_prev);
DRW_shgroup_uniform_texture(grp, "nxtBuffer", tex_next);
DRW_shgroup_uniform_bool(grp, "useDeform", &mb_hair->use_deform, 1);
}
else {
/* Store vertex position buffer. */
mb_hair->psys[psys_id].hair_pos[mb_step] = DRW_hair_pos_buffer_get(ob, psys, md);
mb_hair->use_deform = true;
}
}
}
void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
EEVEE_Data *vedata,
Object *ob)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
DRWShadingGroup *grp = NULL;
if (!DRW_state_is_scene_render() || psl->velocity_object == NULL) {
return;
}
RigidBodyOb *rbo = ob->rigidbody_object;
/* active rigidbody objects only, as only those are affected by sim. */
const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE));
#if 0
/* For now we assume dupli objects are moving. */
const bool is_dupli = (ob->base_flag & BASE_FROM_DUPLI) != 0;
const bool object_moves = is_dupli || has_rigidbody || BKE_object_moves_in_time(ob, true);
#else
/* BKE_object_moves_in_time does not work in some cases.
* Better detect non moving object after evaluation. */
const bool object_moves = true;
#endif
const bool is_deform = BKE_object_is_deform_modified(DRW_context_state_get()->scene, ob) ||
(has_rigidbody && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0);
if (!(object_moves || is_deform)) {
return;
}
EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(
&effects->motion_blur, ob, false);
if (mb_data) {
int mb_step = effects->motion_blur_step;
/* Store transform. */
copy_m4_m4(mb_data->obmat[mb_step], ob->obmat);
EEVEE_GeometryMotionData *mb_geom = EEVEE_motion_blur_geometry_data_get(&effects->motion_blur,
ob);
if (mb_step == MB_CURR) {
GPUBatch *batch = DRW_cache_object_surface_get(ob);
if (batch == NULL) {
return;
}
/* Fill missing matrices if the object was hidden in previous or next frame. */
if (is_zero_m4(mb_data->obmat[MB_PREV])) {
copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]);
}
if (is_zero_m4(mb_data->obmat[MB_NEXT])) {
copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]);
}
if (mb_geom->use_deform) {
/* Keep to modify later (after init). */
mb_geom->batch = batch;
}
/* Avoid drawing object that has no motions since object_moves is always true. */
if (!mb_geom->use_deform && /* Object deformation can happen without transform. */
equals_m4m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]) &&
equals_m4m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR])) {
return;
}
grp = DRW_shgroup_create(EEVEE_shaders_effect_motion_blur_object_sh_get(),
psl->velocity_object);
DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]);
DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]);
DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]);
DRW_shgroup_uniform_bool(grp, "useDeform", &mb_geom->use_deform, 1);
DRW_shgroup_call(grp, batch, ob);
}
else if (is_deform) {
/* Store vertex position buffer. */
mb_geom->vbo[mb_step] = DRW_cache_object_pos_vertbuf_get(ob);
mb_geom->use_deform = (mb_geom->vbo[mb_step] != NULL);
}
else {
mb_geom->vbo[mb_step] = NULL;
mb_geom->use_deform = false;
}
}
}
static void motion_blur_remove_vbo_reference_from_batch(GPUBatch *batch,
GPUVertBuf *vbo1,
GPUVertBuf *vbo2)
{
for (int i = 0; i < GPU_BATCH_VBO_MAX_LEN; i++) {
if (ELEM(batch->verts[i], vbo1, vbo2)) {
/* Avoid double reference of the VBOs. */
batch->verts[i] = NULL;
}
}
}
void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
GHashIterator ghi;
if ((effects->enabled_effects & EFFECT_MOTION_BLUR) == 0) {
return;
}
int mb_step = effects->motion_blur_step;
if (mb_step != MB_CURR) {
/* Push instances attributes to the GPU. */
DRW_render_instance_buffer_finish();
/* Need to be called after DRW_render_instance_buffer_finish() */
/* Also we weed to have a correct fbo bound for DRW_hair_update */
GPU_framebuffer_bind(vedata->fbl->main_fb);
DRW_hair_update();
DRW_cache_restart();
}
for (BLI_ghashIterator_init(&ghi, effects->motion_blur.geom);
BLI_ghashIterator_done(&ghi) == false;
BLI_ghashIterator_step(&ghi)) {
EEVEE_GeometryMotionData *mb_geom = BLI_ghashIterator_getValue(&ghi);
EEVEE_HairMotionData *mb_hair = (EEVEE_HairMotionData *)mb_geom;
if (!mb_geom->use_deform) {
continue;
}
switch (mb_geom->type) {
case EEVEE_MOTION_DATA_HAIR:
if (mb_step == MB_CURR) {
/* TODO(fclem): Check if vertex count mismatch. */
mb_hair->use_deform = true;
}
else {
for (int i = 0; i < mb_hair->psys_len; i++) {
if (mb_hair->psys[i].hair_pos[mb_step] == NULL) {
continue;
}
mb_hair->psys[i].hair_pos[mb_step] = GPU_vertbuf_duplicate(
mb_hair->psys[i].hair_pos[mb_step]);
/* Create vbo immediately to bind to texture buffer. */
GPU_vertbuf_use(mb_hair->psys[i].hair_pos[mb_step]);
mb_hair->psys[i].hair_pos_tx[mb_step] = GPU_texture_create_from_vertbuf(
"hair_pos_motion_blur", mb_hair->psys[i].hair_pos[mb_step]);
}
}
break;
case EEVEE_MOTION_DATA_MESH:
if (mb_step == MB_CURR) {
/* Modify batch to have data from adjacent frames. */
GPUBatch *batch = mb_geom->batch;
for (int i = 0; i < MB_CURR; i++) {
GPUVertBuf *vbo = mb_geom->vbo[i];
if (vbo && batch) {
if (GPU_vertbuf_get_vertex_len(vbo) != GPU_vertbuf_get_vertex_len(batch->verts[0])) {
/* Vertex count mismatch, disable deform motion blur. */
mb_geom->use_deform = false;
}
if (mb_geom->use_deform == false) {
motion_blur_remove_vbo_reference_from_batch(
batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]);
GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]);
GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_NEXT]);
break;
}
GPU_batch_vertbuf_add_ex(batch, vbo, false);
}
}
}
else {
GPUVertBuf *vbo = mb_geom->vbo[mb_step];
if (vbo) {
/* Use the vbo to perform the copy on the GPU. */
GPU_vertbuf_use(vbo);
/* Perform a copy to avoid losing it after RE_engine_frame_set(). */
mb_geom->vbo[mb_step] = vbo = GPU_vertbuf_duplicate(vbo);
/* Find and replace "pos" attrib name. */
GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo);
int attrib_id = GPU_vertformat_attr_id_get(format, "pos");
GPU_vertformat_attr_rename(format, attrib_id, (mb_step == MB_PREV) ? "prv" : "nxt");
}
else {
/* This might happen if the object visibility has been animated. */
mb_geom->use_deform = false;
}
}
break;
default:
BLI_assert(0);
break;
}
}
}
void EEVEE_motion_blur_swap_data(EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
GHashIterator ghi;
BLI_assert((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0);
/* Camera Data. */
effects->motion_blur.camera[MB_PREV] = effects->motion_blur.camera[MB_NEXT];
/* Object Data. */
for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object);
BLI_ghashIterator_done(&ghi) == false;
BLI_ghashIterator_step(&ghi)) {
EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi);
copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_NEXT]);
}
/* Deformation Data. */
for (BLI_ghashIterator_init(&ghi, effects->motion_blur.geom);
BLI_ghashIterator_done(&ghi) == false;
BLI_ghashIterator_step(&ghi)) {
EEVEE_GeometryMotionData *mb_geom = BLI_ghashIterator_getValue(&ghi);
EEVEE_HairMotionData *mb_hair = (EEVEE_HairMotionData *)mb_geom;
switch (mb_geom->type) {
case EEVEE_MOTION_DATA_HAIR:
for (int i = 0; i < mb_hair->psys_len; i++) {
GPU_VERTBUF_DISCARD_SAFE(mb_hair->psys[i].hair_pos[MB_PREV]);
DRW_TEXTURE_FREE_SAFE(mb_hair->psys[i].hair_pos_tx[MB_PREV]);
mb_hair->psys[i].hair_pos[MB_PREV] = mb_hair->psys[i].hair_pos[MB_NEXT];
mb_hair->psys[i].hair_pos_tx[MB_PREV] = mb_hair->psys[i].hair_pos_tx[MB_NEXT];
mb_hair->psys[i].hair_pos[MB_NEXT] = NULL;
mb_hair->psys[i].hair_pos_tx[MB_NEXT] = NULL;
}
break;
case EEVEE_MOTION_DATA_MESH:
if (mb_geom->batch != NULL) {
motion_blur_remove_vbo_reference_from_batch(
mb_geom->batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]);
}
GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]);
mb_geom->vbo[MB_PREV] = mb_geom->vbo[MB_NEXT];
mb_geom->vbo[MB_NEXT] = NULL;
if (mb_geom->vbo[MB_PREV]) {
GPUVertBuf *vbo = mb_geom->vbo[MB_PREV];
GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo);
int attrib_id = GPU_vertformat_attr_id_get(format, "nxt");
GPU_vertformat_attr_rename(format, attrib_id, "prv");
}
break;
default:
BLI_assert(0);
break;
}
}
}
void EEVEE_motion_blur_draw(EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
/* Motion Blur */
if ((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0) {
/* Create velocity max tiles in 2 passes. One for each dimension. */
GPU_framebuffer_bind(fbl->velocity_tiles_fb[0]);
DRW_draw_pass(psl->velocity_tiles_x);
GPU_framebuffer_bind(fbl->velocity_tiles_fb[1]);
DRW_draw_pass(psl->velocity_tiles);
/* Expand the tiles by reading the neighborhood. Do as many passes as required. */
int buf = 0;
for (int i = effects->motion_blur_max; i > 0; i -= EEVEE_VELOCITY_TILE_SIZE) {
GPU_framebuffer_bind(fbl->velocity_tiles_fb[buf]);
/* Change viewport to avoid invoking more pixel shaders than necessary since in one of the
* buffer the texture is way bigger in height. This avoid creating another texture and
* reduce VRAM usage. */
int w = GPU_texture_width(effects->velocity_tiles_tx);
int h = GPU_texture_height(effects->velocity_tiles_tx);
GPU_framebuffer_viewport_set(fbl->velocity_tiles_fb[buf], 0, 0, w, h);
DRW_draw_pass(psl->velocity_tiles_expand[buf]);
GPU_framebuffer_viewport_reset(fbl->velocity_tiles_fb[buf]);
buf = buf ? 0 : 1;
}
GPU_framebuffer_bind(effects->target_buffer);
DRW_draw_pass(psl->motion_blur);
SWAP_BUFFERS();
}
}

View File

@@ -0,0 +1,248 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*/
#include "BLI_map.hh"
#include "DEG_depsgraph_query.h"
#include "eevee_instance.hh"
#include "eevee_sampling.hh"
#include "eevee_shader_shared.hh"
#include "eevee_velocity.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name MotionBlurModule
*
* \{ */
void MotionBlurModule::init(void)
{
const Scene *scene = inst_.scene;
enabled_ = (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0;
/* Viewport not supported for now. */
if (!DRW_state_is_scene_render()) {
enabled_ = false;
}
if (!enabled_) {
motion_blur_fx_enabled_ = false;
return;
}
/* Take into account the steps needed for fx motion blur. */
int steps_count = max_ii(1, scene->eevee.motion_blur_steps) * 2 + 1;
time_steps_.resize(steps_count);
initial_frame_ = CFRA;
initial_subframe_ = SUBFRA;
frame_time_ = initial_frame_ + initial_subframe_;
motion_blur_position_ = scene->eevee.motion_blur_position;
motion_blur_shutter_ = scene->eevee.motion_blur_shutter;
/* Without this there is the possibility of the curve table not being allocated. */
BKE_curvemapping_changed((struct CurveMapping *)&scene->r.mblur_shutter_curve, false);
Vector<float> cdf(CM_TABLE);
Sampling::cdf_from_curvemapping(scene->r.mblur_shutter_curve, cdf);
Sampling::cdf_invert(cdf, time_steps_);
for (float &time : time_steps_) {
time = this->shutter_time_to_scene_time(time);
}
motion_blur_fx_enabled_ = scene->eevee.motion_blur_max > 0.5f;
step_id_ = 1;
if (motion_blur_fx_enabled_) {
/* A bit weird but we have to sync the first 2 steps here because the step()
* function is only called after rendering a sample. */
inst_.velocity.step_sync(VelocityModule::STEP_PREVIOUS, time_steps_[0]);
inst_.velocity.step_sync(VelocityModule::STEP_NEXT, time_steps_[2]);
}
inst_.set_time(time_steps_[1]);
}
/* Runs after rendering a sample. */
void MotionBlurModule::step(void)
{
if (!enabled_) {
return;
}
else if (inst_.sampling.finished()) {
/* Restore original frame number. This is because the render pipeline expects it. */
RE_engine_frame_set(inst_.render, initial_frame_, initial_subframe_);
}
else if (inst_.sampling.do_render_sync()) {
/* Time to change motion step. */
BLI_assert(time_steps_.size() > step_id_ + 2);
step_id_ += 2;
if (motion_blur_fx_enabled_) {
inst_.velocity.step_swap();
inst_.velocity.step_sync(VelocityModule::STEP_NEXT, time_steps_[step_id_ + 1]);
}
inst_.set_time(time_steps_[step_id_]);
}
}
float MotionBlurModule::shutter_time_to_scene_time(float time)
{
switch (motion_blur_position_) {
case SCE_EEVEE_MB_START:
/* No offset. */
break;
case SCE_EEVEE_MB_CENTER:
time -= 0.5f;
break;
case SCE_EEVEE_MB_END:
time -= 1.0;
break;
default:
BLI_assert(!"Invalid motion blur position enum!");
break;
}
time *= motion_blur_shutter_;
time += frame_time_;
return time;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name MotionBlur
*
* \{ */
void MotionBlur::init()
{
const Scene *scene = inst_.scene;
data_.blur_max = scene->eevee.motion_blur_max;
data_.depth_scale = scene->eevee.motion_blur_depth_scale;
enabled_ = ((scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0) && (data_.blur_max > 0.5f);
}
void MotionBlur::sync(int extent[2])
{
if (!enabled_) {
return;
}
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
uint res[2] = {divide_ceil_u(extent[0], MB_TILE_DIVISOR),
divide_ceil_u(extent[1], MB_TILE_DIVISOR)};
{
/* Create max velocity tiles in 2 passes. One for X and one for Y */
DRW_PASS_CREATE(tiles_flatten_ps_, DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_TILE_FLATTEN);
DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_flatten_ps_);
DRW_shgroup_uniform_texture_ref_ex(grp, "velocity_tx", &input_velocity_tx_, no_filter);
DRW_shgroup_uniform_block(grp, "motion_blur_block", data_.ubo_get());
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
tiles_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
tiles_flatten_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(tiles_tx_));
}
{
/* Expand max tiles by keeping the max tile in each tile neighborhood. */
DRW_PASS_CREATE(tiles_dilate_ps_, DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_TILE_DILATE);
DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_dilate_ps_);
DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_tx", &tiles_tx_, no_filter);
DRW_shgroup_uniform_block(grp, "motion_blur_block", data_.ubo_get());
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
tiles_dilated_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
tiles_dilate_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(tiles_dilated_tx_));
}
{
data_.target_size_inv[0] = 1.0f / extent[0];
data_.target_size_inv[1] = 1.0f / extent[1];
/* Do the motion blur gather algorithm. */
DRW_PASS_CREATE(gather_ps_, DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_GATHER);
DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_ps_);
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
DRW_shgroup_uniform_block(grp, "motion_blur_block", data_.ubo_get());
DRW_shgroup_uniform_texture_ref(grp, "color_tx", &input_color_tx_);
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
DRW_shgroup_uniform_texture_ref_ex(grp, "velocity_tx", &input_velocity_tx_, no_filter);
DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_tx", &tiles_dilated_tx_, no_filter);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
data_.is_viewport = !DRW_state_is_image_render();
data_.push_update();
}
void MotionBlur::render(GPUTexture *depth_tx,
GPUTexture *velocity_tx,
GPUTexture **input_tx,
GPUTexture **output_tx)
{
if (!enabled_) {
return;
}
input_color_tx_ = *input_tx;
input_depth_tx_ = depth_tx;
input_velocity_tx_ = velocity_tx;
DRW_stats_group_start("Motion Blur");
GPU_framebuffer_bind(tiles_flatten_fb_);
DRW_draw_pass(tiles_flatten_ps_);
for (int max_blur = data_.blur_max; max_blur > 0; max_blur -= MB_TILE_DIVISOR) {
GPU_framebuffer_bind(tiles_dilate_fb_);
DRW_draw_pass(tiles_dilate_ps_);
SWAP(GPUTexture *, tiles_tx_, tiles_dilated_tx_);
SWAP(Framebuffer, tiles_flatten_fb_, tiles_dilate_fb_);
}
/* Swap again so result is in tiles_dilated_tx_. */
SWAP(GPUTexture *, tiles_tx_, tiles_dilated_tx_);
SWAP(Framebuffer, tiles_flatten_fb_, tiles_dilate_fb_);
gather_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(*output_tx));
GPU_framebuffer_bind(gather_fb_);
DRW_draw_pass(gather_ps_);
DRW_stats_group_end();
/* Swap buffers so that next effect has the right input. */
*input_tx = *output_tx;
*output_tx = input_color_tx_;
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,159 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* Motion blur is done by accumulating scene samples over shutter time.
* Since the number of step is discrete, quite low, and not per pixel randomized,
* we couple this with a post processing motion blur.
*
* The post-fx motion blur is done in two directions, from the previous step and to the next.
*
* For a scene with 3 motion steps, a flat shutter curve and shutter time of 2 frame
* centered on frame we have:
*
* |--------------------|--------------------|
* -1 0 1 Frames
*
* |-------------|-------------|-------------|
* 1 2 3 Motion steps
*
* |------|------|------|------|------|------|
* 0 1 2 4 5 6 7 Time Steps
*
* |-------------| One motion step blurs this range.
* -1 | +1 Objects and geometry steps are recorded here.
* 0 Scene is rendered here.
*
* Since motion step N and N+1 share one time step we reuse it to avoid an extra scene evaluation.
*
* Note that we have to evaluate -1 and +1 time steps before rendering so eval order is -1, +1, 0.
* This is because all GPUBatches from the DRWCache are being free when changing a frame.
*/
#pragma once
#include "BLI_map.hh"
#include "DEG_depsgraph_query.h"
#include "eevee_sampling.hh"
#include "eevee_shader_shared.hh"
#include "eevee_velocity.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name MotionBlur
*
* \{ */
/**
* Manages timesteps evaluations and accumulation Motion blur.
* Post process motion blur is handled by the MotionBlur class.
*/
class MotionBlurModule {
private:
Instance &inst_;
/**
* Array containing all steps (in scene time) we need to evaluate (not render).
* Only odd steps are rendered. The even ones are evaluated for fx motion blur.
*/
Vector<float> time_steps_;
/** Copy of input frame an subframe to restore after render. */
int initial_frame_;
float initial_subframe_;
/** Time of the frame we are rendering. */
float frame_time_;
/** Copy of scene settings. */
int motion_blur_position_;
float motion_blur_shutter_;
bool enabled_ = false;
float motion_blur_fx_enabled_ = false;
int step_id_ = 0;
public:
MotionBlurModule(Instance &inst) : inst_(inst){};
~MotionBlurModule(){};
void init(void);
void step(void);
private:
float shutter_time_to_scene_time(float time);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name MotionBlur
*
* \{ */
/**
* Per view fx module. Perform a motion blur using the result of the velocity pass.
*/
class MotionBlur {
private:
Instance &inst_;
StringRefNull view_name_;
/** Textures from pool. Not owned. */
GPUTexture *tiles_tx_ = nullptr;
GPUTexture *tiles_dilated_tx_ = nullptr;
/** Input texture. Not owned. */
GPUTexture *input_velocity_tx_ = nullptr;
GPUTexture *input_color_tx_ = nullptr;
GPUTexture *input_depth_tx_ = nullptr;
/** Passes. Not owned. */
DRWPass *tiles_flatten_ps_ = nullptr;
DRWPass *tiles_dilate_ps_ = nullptr;
DRWPass *gather_ps_ = nullptr;
/** Framebuffers. Owned. */
eevee::Framebuffer tiles_flatten_fb_;
eevee::Framebuffer tiles_dilate_fb_;
eevee::Framebuffer gather_fb_;
StructBuffer<MotionBlurData> data_;
bool enabled_;
public:
MotionBlur(Instance &inst, StringRefNull view_name) : inst_(inst), view_name_(view_name){};
~MotionBlur(){};
void init(void);
void sync(int extent[2]);
void render(GPUTexture *depth_tx,
GPUTexture *velocity_tx,
GPUTexture **input_tx,
GPUTexture **output_tx);
};
/** \} */
} // namespace blender::eevee

View File

@@ -1,269 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Implementation of the screen space Ground Truth Ambient Occlusion.
*/
#include "DRW_render.h"
#include "BLI_string_utils.h"
#include "DEG_depsgraph_query.h"
#include "BKE_global.h" /* for G.debug_value */
#include "eevee_private.h"
#include "GPU_capabilities.h"
#include "GPU_platform.h"
#include "GPU_state.h"
static struct {
struct GPUTexture *dummy_horizon_tx;
} e_data = {NULL}; /* Engine data */
int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
if (!e_data.dummy_horizon_tx) {
const float pixel[4] = {0.0f, 0.0f, 0.0f, 0.0f};
e_data.dummy_horizon_tx = DRW_texture_create_2d(1, 1, GPU_RGBA8, DRW_TEX_WRAP, pixel);
}
if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED ||
stl->g_data->render_passes & EEVEE_RENDER_PASS_AO) {
const float *viewport_size = DRW_viewport_size_get();
const int fs_size[2] = {(int)viewport_size[0], (int)viewport_size[1]};
common_data->ao_dist = scene_eval->eevee.gtao_distance;
common_data->ao_factor = max_ff(1e-4f, scene_eval->eevee.gtao_factor);
common_data->ao_quality = scene_eval->eevee.gtao_quality;
if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) {
common_data->ao_settings = 1.0f; /* USE_AO */
}
if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_BENT_NORMALS) {
common_data->ao_settings += 2.0f; /* USE_BENT_NORMAL */
}
if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_BOUNCE) {
common_data->ao_settings += 4.0f; /* USE_DENOISE */
}
common_data->ao_bounce_fac = (scene_eval->eevee.flag & SCE_EEVEE_GTAO_BOUNCE) ? 1.0f : 0.0f;
effects->gtao_horizons_renderpass = DRW_texture_pool_query_2d(
fs_size[0], fs_size[1], GPU_RGBA8, &draw_engine_eevee_type);
GPU_framebuffer_ensure_config(
&fbl->gtao_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons_renderpass)});
if (G.debug_value == 6) {
effects->gtao_horizons_debug = DRW_texture_pool_query_2d(
fs_size[0], fs_size[1], GPU_RGBA8, &draw_engine_eevee_type);
GPU_framebuffer_ensure_config(
&fbl->gtao_debug_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons_debug)});
}
else {
effects->gtao_horizons_debug = NULL;
}
effects->gtao_horizons = (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) ?
effects->gtao_horizons_renderpass :
e_data.dummy_horizon_tx;
return EFFECT_GTAO | EFFECT_NORMAL_BUFFER;
}
/* Cleanup */
effects->gtao_horizons_renderpass = e_data.dummy_horizon_tx;
effects->gtao_horizons = e_data.dummy_horizon_tx;
GPU_FRAMEBUFFER_FREE_SAFE(fbl->gtao_fb);
common_data->ao_settings = 0.0f;
return 0;
}
void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PassList *psl = vedata->psl;
EEVEE_EffectsInfo *effects = stl->effects;
const eGPUTextureFormat texture_format = (tot_samples > 128) ? GPU_R32F : GPU_R16F;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
/* Should be enough precision for many samples. */
DRW_texture_ensure_fullscreen_2d(&txl->ao_accum, texture_format, 0);
GPU_framebuffer_ensure_config(&fbl->ao_accum_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ao_accum)});
/* Accumulation pass */
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD;
DRW_PASS_CREATE(psl->ao_accum_ps, state);
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_effect_ambient_occlusion_debug_sh_get(),
psl->ao_accum_ps);
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input);
DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons_renderpass);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
}
void EEVEE_occlusion_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_EffectsInfo *effects = stl->effects;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
if ((effects->enabled_effects & EFFECT_GTAO) != 0) {
/**
* Occlusion Algorithm Overview:
*
* We separate the computation into 2 steps.
*
* - First we scan the neighborhood pixels to find the maximum horizon angle.
* We save this angle in a RG8 array texture.
*
* - Then we use this angle to compute occlusion with the shading normal at
* the shading stage. This let us do correct shadowing for each diffuse / specular
* lobe present in the shader using the correct normal.
*/
DRW_PASS_CREATE(psl->ao_horizon_search, DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_effect_ambient_occlusion_sh_get(),
psl->ao_horizon_search);
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
if (G.debug_value == 6) {
DRW_PASS_CREATE(psl->ao_horizon_debug, DRW_STATE_WRITE_COLOR);
grp = DRW_shgroup_create(EEVEE_shaders_effect_ambient_occlusion_debug_sh_get(),
psl->ao_horizon_debug);
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input);
DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons_renderpass);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
}
}
void EEVEE_occlusion_compute(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
if ((effects->enabled_effects & EFFECT_GTAO) != 0) {
DRW_stats_group_start("GTAO Horizon Scan");
GPU_framebuffer_bind(fbl->gtao_fb);
DRW_draw_pass(psl->ao_horizon_search);
if (GPU_mip_render_workaround() ||
GPU_type_matches(GPU_DEVICE_INTEL_UHD, GPU_OS_WIN, GPU_DRIVER_ANY)) {
/* Fix dot corruption on intel HD5XX/HD6XX series. */
GPU_flush();
}
/* Restore */
GPU_framebuffer_bind(fbl->main_fb);
DRW_stats_group_end();
}
}
void EEVEE_occlusion_draw_debug(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
if (((effects->enabled_effects & EFFECT_GTAO) != 0) && (G.debug_value == 6)) {
DRW_stats_group_start("GTAO Debug");
GPU_framebuffer_bind(fbl->gtao_debug_fb);
DRW_draw_pass(psl->ao_horizon_debug);
/* Restore */
GPU_framebuffer_bind(fbl->main_fb);
DRW_stats_group_end();
}
}
void EEVEE_occlusion_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_PassList *psl = vedata->psl;
EEVEE_EffectsInfo *effects = vedata->stl->effects;
if (fbl->ao_accum_fb != NULL) {
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
/* Update the min_max/horizon buffers so the refraction materials appear in it. */
EEVEE_create_minmax_buffer(vedata, dtxl->depth, -1);
EEVEE_occlusion_compute(sldata, vedata);
GPU_framebuffer_bind(fbl->ao_accum_fb);
/* Clear texture. */
if (effects->taa_current_sample == 1) {
const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_framebuffer_clear_color(fbl->ao_accum_fb, clear);
}
DRW_draw_pass(psl->ao_accum_ps);
/* Restore */
GPU_framebuffer_bind(fbl->main_fb);
}
}
void EEVEE_occlusion_free(void)
{
DRW_TEXTURE_FREE_SAFE(e_data.dummy_horizon_tx);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,745 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*/
/**
* Render functions for final render outputs.
*/
#include "DRW_engine.h"
#include "DRW_render.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "BKE_global.h"
#include "BKE_object.h"
#include "BLI_rand.h"
#include "BLI_rect.h"
#include "DEG_depsgraph_query.h"
#include "GPU_capabilities.h"
#include "GPU_framebuffer.h"
#include "GPU_state.h"
#include "RE_pipeline.h"
#include "eevee_private.h"
/* Return true if init properly. */
bool EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph *depsgraph)
{
EEVEE_Data *vedata = (EEVEE_Data *)ved;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_FramebufferList *fbl = vedata->fbl;
Scene *scene = DEG_get_evaluated_scene(depsgraph);
const float *size_orig = DRW_viewport_size_get();
float size_final[2];
/* Init default FB and render targets:
* In render mode the default framebuffer is not generated
* because there is no viewport. So we need to manually create it or
* not use it. For code clarity we just allocate it make use of it. */
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
/* Alloc transient data. */
if (!stl->g_data) {
stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__);
}
EEVEE_PrivateData *g_data = stl->g_data;
g_data->background_alpha = DRW_state_draw_background() ? 1.0f : 0.0f;
g_data->valid_double_buffer = 0;
copy_v2_v2(g_data->size_orig, size_orig);
float *camtexcofac = g_data->camtexcofac;
if (scene->eevee.flag & SCE_EEVEE_OVERSCAN) {
g_data->overscan = scene->eevee.overscan / 100.0f;
g_data->overscan_pixels = roundf(max_ff(size_orig[0], size_orig[1]) * g_data->overscan);
madd_v2_v2v2fl(size_final, size_orig, (float[2]){2.0f, 2.0f}, g_data->overscan_pixels);
camtexcofac[0] = size_final[0] / size_orig[0];
camtexcofac[1] = size_final[1] / size_orig[1];
camtexcofac[2] = -camtexcofac[0] * g_data->overscan_pixels / size_final[0];
camtexcofac[3] = -camtexcofac[1] * g_data->overscan_pixels / size_final[1];
}
else {
copy_v2_v2(size_final, size_orig);
g_data->overscan = 0.0f;
g_data->overscan_pixels = 0.0f;
copy_v4_fl4(camtexcofac, 1.0f, 1.0f, 0.0f, 0.0f);
}
const int final_res[2] = {
size_orig[0] + g_data->overscan_pixels * 2.0f,
size_orig[1] + g_data->overscan_pixels * 2.0f,
};
int max_dim = max_ii(final_res[0], final_res[1]);
if (max_dim > GPU_max_texture_size()) {
char error_msg[128];
BLI_snprintf(error_msg,
sizeof(error_msg),
"Error: Reported texture size limit (%dpx) is lower than output size (%dpx).",
GPU_max_texture_size(),
max_dim);
RE_engine_set_error_message(engine, error_msg);
G.is_break = true;
return false;
}
/* XXX overriding viewport size. Simplify things but is not really 100% safe. */
DRW_render_viewport_size_set(final_res);
/* TODO: 32 bit depth. */
DRW_texture_ensure_fullscreen_2d(&dtxl->depth, GPU_DEPTH24_STENCIL8, 0);
DRW_texture_ensure_fullscreen_2d(&txl->color, GPU_RGBA32F, DRW_TEX_FILTER);
GPU_framebuffer_ensure_config(
&dfbl->default_fb,
{GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(txl->color)});
GPU_framebuffer_ensure_config(
&fbl->main_fb, {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(txl->color)});
GPU_framebuffer_ensure_config(&fbl->main_color_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->color)});
/* Camera could change because of Motion blur. */
g_data->cam_original_ob = RE_GetCamera(engine->re);
return true;
}
void EEVEE_render_modules_init(EEVEE_Data *vedata,
RenderEngine *engine,
struct Depsgraph *depsgraph)
{
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PrivateData *g_data = vedata->stl->g_data;
EEVEE_FramebufferList *fbl = vedata->fbl;
/* TODO(sergey): Shall render hold pointer to an evaluated camera instead? */
struct Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, g_data->cam_original_ob);
EEVEE_render_view_sync(vedata, engine, depsgraph);
/* `EEVEE_renderpasses_init` will set the active render passes used by `EEVEE_effects_init`.
* `EEVEE_effects_init` needs to go second for TAA. */
EEVEE_renderpasses_init(vedata);
EEVEE_effects_init(sldata, vedata, ob_camera_eval, false);
EEVEE_materials_init(sldata, vedata, stl, fbl);
EEVEE_shadows_init(sldata);
EEVEE_lightprobes_init(sldata, vedata);
}
void EEVEE_render_view_sync(EEVEE_Data *vedata, RenderEngine *engine, struct Depsgraph *depsgraph)
{
EEVEE_PrivateData *g_data = vedata->stl->g_data;
/* Set the perspective & view matrix. */
float winmat[4][4], viewmat[4][4], viewinv[4][4];
/* TODO(sergey): Shall render hold pointer to an evaluated camera instead? */
struct Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, g_data->cam_original_ob);
RE_GetCameraWindow(engine->re, ob_camera_eval, winmat);
RE_GetCameraWindowWithOverscan(engine->re, g_data->overscan, winmat);
RE_GetCameraModelMatrix(engine->re, ob_camera_eval, viewinv);
invert_m4_m4(viewmat, viewinv);
DRWView *view = DRW_view_create(viewmat, winmat, NULL, NULL, NULL);
DRW_view_reset();
DRW_view_default_set(view);
DRW_view_set_active(view);
DRW_view_camtexco_set(view, g_data->camtexcofac);
}
void EEVEE_render_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_view_layer_data_ensure();
EEVEE_bloom_cache_init(sldata, vedata);
EEVEE_depth_of_field_cache_init(sldata, vedata);
EEVEE_effects_cache_init(sldata, vedata);
EEVEE_lightprobes_cache_init(sldata, vedata);
EEVEE_lights_cache_init(sldata, vedata);
EEVEE_materials_cache_init(sldata, vedata);
EEVEE_motion_blur_cache_init(sldata, vedata);
EEVEE_occlusion_cache_init(sldata, vedata);
EEVEE_screen_raytrace_cache_init(sldata, vedata);
EEVEE_subsurface_cache_init(sldata, vedata);
EEVEE_temporal_sampling_cache_init(sldata, vedata);
EEVEE_volumes_cache_init(sldata, vedata);
EEVEE_cryptomatte_cache_init(sldata, vedata);
}
/* Used by light cache. in this case engine is NULL. */
void EEVEE_render_cache(void *vedata,
struct Object *ob,
struct RenderEngine *engine,
struct Depsgraph *depsgraph)
{
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
EEVEE_Data *data = vedata;
EEVEE_StorageList *stl = data->stl;
EEVEE_PrivateData *g_data = stl->g_data;
EEVEE_LightProbesInfo *pinfo = sldata->probes;
bool cast_shadow = false;
const bool do_cryptomatte = (engine != NULL) &&
((g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0);
eevee_id_update(vedata, &ob->id);
if (pinfo->vis_data.collection) {
/* Used for rendering probe with visibility groups. */
bool ob_vis = BKE_collection_has_object_recursive(pinfo->vis_data.collection, ob);
ob_vis = (pinfo->vis_data.invert) ? !ob_vis : ob_vis;
if (!ob_vis) {
return;
}
}
/* Don't print dupli objects as this can be very verbose and
* increase the render time on Windows because of slow windows term.
* (see T59649) */
if (engine && (ob->base_flag & BASE_FROM_DUPLI) == 0) {
char info[42];
BLI_snprintf(info, sizeof(info), "Syncing %s", ob->id.name + 2);
RE_engine_update_stats(engine, NULL, info);
}
const int ob_visibility = DRW_object_visibility_in_active_context(ob);
if (ob_visibility & OB_VISIBLE_PARTICLES) {
EEVEE_particle_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
if (do_cryptomatte) {
EEVEE_cryptomatte_particle_hair_cache_populate(data, sldata, ob);
}
}
if (ob_visibility & OB_VISIBLE_SELF) {
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) {
EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow);
if (do_cryptomatte) {
EEVEE_cryptomatte_cache_populate(data, sldata, ob);
}
}
else if (ob->type == OB_HAIR) {
EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
if (do_cryptomatte) {
EEVEE_cryptomatte_object_hair_cache_populate(data, sldata, ob);
}
}
else if (ob->type == OB_VOLUME) {
Scene *scene = DEG_get_evaluated_scene(depsgraph);
EEVEE_volumes_cache_object_add(sldata, vedata, scene, ob);
}
else if (ob->type == OB_LIGHTPROBE) {
EEVEE_lightprobes_cache_add(sldata, vedata, ob);
}
else if (ob->type == OB_LAMP) {
EEVEE_lights_cache_add(sldata, ob);
}
}
if (cast_shadow) {
EEVEE_shadows_caster_register(sldata, ob);
}
}
static void eevee_render_color_result(RenderLayer *rl,
const char *viewname,
const rcti *rect,
const char *render_pass_name,
int num_channels,
GPUFrameBuffer *framebuffer,
EEVEE_Data *vedata)
{
RenderPass *rp = RE_pass_find_by_name(rl, render_pass_name, viewname);
if (rp == NULL) {
return;
}
GPU_framebuffer_bind(framebuffer);
GPU_framebuffer_read_color(framebuffer,
vedata->stl->g_data->overscan_pixels + rect->xmin,
vedata->stl->g_data->overscan_pixels + rect->ymin,
BLI_rcti_size_x(rect),
BLI_rcti_size_y(rect),
num_channels,
0,
GPU_DATA_FLOAT,
rp->rect);
}
static void eevee_render_result_combined(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *UNUSED(sldata))
{
eevee_render_color_result(
rl, viewname, rect, RE_PASSNAME_COMBINED, 4, vedata->stl->effects->final_fb, vedata);
}
static void eevee_render_result_normal(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata)
{
const int current_sample = vedata->stl->effects->taa_current_sample;
/* Only read the center texel. */
if (current_sample > 1) {
return;
}
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_NORMAL) != 0) {
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_NORMAL, 0);
eevee_render_color_result(
rl, viewname, rect, RE_PASSNAME_NORMAL, 3, vedata->fbl->renderpass_fb, vedata);
}
}
static void eevee_render_result_z(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata)
{
const int current_sample = vedata->stl->effects->taa_current_sample;
/* Only read the center texel. */
if (current_sample > 1) {
return;
}
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_Z) != 0) {
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_Z, 0);
eevee_render_color_result(
rl, viewname, rect, RE_PASSNAME_Z, 1, vedata->fbl->renderpass_fb, vedata);
}
}
static void eevee_render_result_mist(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata)
{
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_MIST) != 0) {
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_MIST, 0);
eevee_render_color_result(
rl, viewname, rect, RE_PASSNAME_MIST, 1, vedata->fbl->renderpass_fb, vedata);
}
}
static void eevee_render_result_shadow(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata)
{
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_SHADOW) != 0) {
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_SHADOW, 0);
eevee_render_color_result(
rl, viewname, rect, RE_PASSNAME_SHADOW, 3, vedata->fbl->renderpass_fb, vedata);
}
}
static void eevee_render_result_occlusion(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata)
{
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_AO) != 0) {
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_AO, 0);
eevee_render_color_result(
rl, viewname, rect, RE_PASSNAME_AO, 3, vedata->fbl->renderpass_fb, vedata);
}
}
static void eevee_render_result_bloom(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata)
{
if ((vedata->stl->effects->enabled_effects & EFFECT_BLOOM) == 0) {
/* Bloom is not enabled. */
return;
}
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_BLOOM) != 0) {
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_BLOOM, 0);
eevee_render_color_result(
rl, viewname, rect, RE_PASSNAME_BLOOM, 3, vedata->fbl->renderpass_fb, vedata);
}
}
#define EEVEE_RENDER_RESULT_MATERIAL_PASS(pass_name, eevee_pass_type) \
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_##eevee_pass_type) != 0) { \
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_##eevee_pass_type, 0); \
eevee_render_color_result( \
rl, viewname, rect, RE_PASSNAME_##pass_name, 3, vedata->fbl->renderpass_fb, vedata); \
}
static void eevee_render_result_diffuse_color(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata)
{
EEVEE_RENDER_RESULT_MATERIAL_PASS(DIFFUSE_COLOR, DIFFUSE_COLOR)
}
static void eevee_render_result_diffuse_direct(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata)
{
EEVEE_RENDER_RESULT_MATERIAL_PASS(DIFFUSE_DIRECT, DIFFUSE_LIGHT)
}
static void eevee_render_result_specular_color(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata)
{
EEVEE_RENDER_RESULT_MATERIAL_PASS(GLOSSY_COLOR, SPECULAR_COLOR)
}
static void eevee_render_result_specular_direct(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata)
{
EEVEE_RENDER_RESULT_MATERIAL_PASS(GLOSSY_DIRECT, SPECULAR_LIGHT)
}
static void eevee_render_result_emission(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata)
{
EEVEE_RENDER_RESULT_MATERIAL_PASS(EMIT, EMIT)
}
static void eevee_render_result_environment(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata)
{
EEVEE_RENDER_RESULT_MATERIAL_PASS(ENVIRONMENT, ENVIRONMENT)
}
static void eevee_render_result_volume_light(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata)
{
EEVEE_RENDER_RESULT_MATERIAL_PASS(VOLUME_LIGHT, VOLUME_LIGHT)
}
static void eevee_render_result_aovs(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata)
{
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_AOV) != 0) {
const DRWContextState *draw_ctx = DRW_context_state_get();
ViewLayer *view_layer = draw_ctx->view_layer;
int aov_index = 0;
LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
if ((aov->flag & AOV_CONFLICT) != 0) {
continue;
}
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_AOV, aov_index);
switch (aov->type) {
case AOV_TYPE_COLOR:
eevee_render_color_result(
rl, viewname, rect, aov->name, 4, vedata->fbl->renderpass_fb, vedata);
break;
case AOV_TYPE_VALUE:
eevee_render_color_result(
rl, viewname, rect, aov->name, 1, vedata->fbl->renderpass_fb, vedata);
}
aov_index++;
}
}
}
#undef EEVEE_RENDER_RESULT_MATERIAL_PASS
static void eevee_render_result_cryptomatte(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata)
{
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) {
EEVEE_cryptomatte_render_result(rl, viewname, rect, vedata, sldata);
}
}
static void eevee_render_draw_background(EEVEE_Data *vedata)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PassList *psl = vedata->psl;
/* Prevent background to write to data buffers.
* NOTE : This also make sure the textures are bound
* to the right double buffer. */
GPU_framebuffer_ensure_config(&fbl->main_fb,
{GPU_ATTACHMENT_LEAVE,
GPU_ATTACHMENT_LEAVE,
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_NONE});
GPU_framebuffer_bind(fbl->main_fb);
DRW_draw_pass(psl->background_ps);
GPU_framebuffer_ensure_config(&fbl->main_fb,
{GPU_ATTACHMENT_LEAVE,
GPU_ATTACHMENT_LEAVE,
GPU_ATTACHMENT_TEXTURE(stl->effects->ssr_normal_input),
GPU_ATTACHMENT_TEXTURE(stl->effects->ssr_specrough_input),
GPU_ATTACHMENT_TEXTURE(stl->effects->sss_irradiance),
GPU_ATTACHMENT_TEXTURE(stl->effects->sss_radius),
GPU_ATTACHMENT_TEXTURE(stl->effects->sss_albedo)});
GPU_framebuffer_bind(fbl->main_fb);
}
void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl, const rcti *rect)
{
const char *viewname = RE_GetActiveRenderView(engine->re);
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_FramebufferList *fbl = vedata->fbl;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
/* Push instances attributes to the GPU. */
DRW_render_instance_buffer_finish();
/* Need to be called after DRW_render_instance_buffer_finish() */
/* Also we weed to have a correct FBO bound for DRW_hair_update */
GPU_framebuffer_bind(fbl->main_fb);
DRW_hair_update();
/* Sort transparents before the loop. */
DRW_pass_sort_shgroup_z(psl->transparent_pass);
uint tot_sample = stl->g_data->render_sample_count_per_timestep;
uint render_samples = 0;
/* SSR needs one iteration to start properly. */
if ((stl->effects->enabled_effects & EFFECT_SSR) && !stl->effects->ssr_was_valid_double_buffer) {
tot_sample += 1;
}
while (render_samples < tot_sample && !RE_engine_test_break(engine)) {
const float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float clear_depth = 1.0f;
uint clear_stencil = 0x00;
const uint primes[3] = {2, 3, 7};
double offset[3] = {0.0, 0.0, 0.0};
double r[3];
if ((stl->effects->enabled_effects & EFFECT_SSR) && (render_samples == 1) &&
!stl->effects->ssr_was_valid_double_buffer) {
/* SSR needs one iteration to start properly.
* This iteration was done, reset to the original target sample count. */
render_samples--;
tot_sample--;
/* Reset sampling (and accumulation) after the first sample to avoid
* washed out first bounce for SSR. */
EEVEE_temporal_sampling_reset(vedata);
stl->effects->ssr_was_valid_double_buffer = stl->g_data->valid_double_buffer;
}
/* Don't print every samples as it can lead to bad performance. (see T59649) */
else if ((render_samples % 25) == 0 || (render_samples + 1) == tot_sample) {
char info[42];
BLI_snprintf(
info, sizeof(info), "Rendering %u / %u samples", render_samples + 1, tot_sample);
RE_engine_update_stats(engine, NULL, info);
}
/* Copy previous persmat to UBO data */
copy_m4_m4(sldata->common_data.prev_persmat, stl->effects->prev_persmat);
BLI_halton_3d(primes, offset, stl->effects->taa_current_sample, r);
EEVEE_update_noise(psl, fbl, r);
EEVEE_temporal_sampling_matrices_calc(stl->effects, r);
EEVEE_volumes_set_jitter(sldata, stl->effects->taa_current_sample - 1);
EEVEE_materials_init(sldata, vedata, stl, fbl);
/* Refresh Probes
* Shadows needs to be updated for correct probes */
EEVEE_shadows_update(sldata, vedata);
EEVEE_lightprobes_refresh(sldata, vedata);
EEVEE_lightprobes_refresh_planar(sldata, vedata);
/* Refresh Shadows */
EEVEE_shadows_draw(sldata, vedata, stl->effects->taa_view);
/* Set matrices. */
DRW_view_set_active(stl->effects->taa_view);
/* Set ray type. */
sldata->common_data.ray_type = EEVEE_RAY_CAMERA;
sldata->common_data.ray_depth = 0.0f;
GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
GPU_framebuffer_bind(fbl->main_fb);
GPU_framebuffer_clear_color_depth_stencil(fbl->main_fb, clear_col, clear_depth, clear_stencil);
/* Depth prepass */
DRW_draw_pass(psl->depth_ps);
/* Create minmax texture */
EEVEE_create_minmax_buffer(vedata, dtxl->depth, -1);
EEVEE_occlusion_compute(sldata, vedata);
EEVEE_volumes_compute(sldata, vedata);
/* Shading pass */
eevee_render_draw_background(vedata);
GPU_framebuffer_bind(fbl->main_fb);
DRW_draw_pass(psl->material_ps);
EEVEE_subsurface_data_render(sldata, vedata);
/* Effects pre-transparency */
EEVEE_subsurface_compute(sldata, vedata);
EEVEE_reflection_compute(sldata, vedata);
EEVEE_refraction_compute(sldata, vedata);
/* Opaque refraction */
DRW_draw_pass(psl->depth_refract_ps);
DRW_draw_pass(psl->material_refract_ps);
/* Result NORMAL */
eevee_render_result_normal(rl, viewname, rect, vedata, sldata);
/* Volumetrics Resolve Opaque */
EEVEE_volumes_resolve(sldata, vedata);
/* Subsurface output, Occlusion output, Mist output */
EEVEE_renderpasses_output_accumulate(sldata, vedata, false);
/* Transparent */
GPU_framebuffer_texture_attach(fbl->main_color_fb, dtxl->depth, 0, 0);
GPU_framebuffer_bind(fbl->main_color_fb);
DRW_draw_pass(psl->transparent_pass);
GPU_framebuffer_bind(fbl->main_fb);
GPU_framebuffer_texture_detach(fbl->main_color_fb, dtxl->depth);
/* Result Z */
eevee_render_result_z(rl, viewname, rect, vedata, sldata);
/* Post Process */
EEVEE_draw_effects(sldata, vedata);
/* XXX Seems to fix TDR issue with NVidia drivers on linux. */
GPU_finish();
RE_engine_update_progress(engine, (float)(render_samples++) / (float)tot_sample);
}
}
void EEVEE_render_read_result(EEVEE_Data *vedata,
RenderEngine *engine,
RenderLayer *rl,
const rcti *rect)
{
const char *viewname = RE_GetActiveRenderView(engine->re);
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
eevee_render_result_combined(rl, viewname, rect, vedata, sldata);
eevee_render_result_mist(rl, viewname, rect, vedata, sldata);
eevee_render_result_occlusion(rl, viewname, rect, vedata, sldata);
eevee_render_result_shadow(rl, viewname, rect, vedata, sldata);
eevee_render_result_diffuse_color(rl, viewname, rect, vedata, sldata);
eevee_render_result_diffuse_direct(rl, viewname, rect, vedata, sldata);
eevee_render_result_specular_color(rl, viewname, rect, vedata, sldata);
eevee_render_result_specular_direct(rl, viewname, rect, vedata, sldata);
eevee_render_result_emission(rl, viewname, rect, vedata, sldata);
eevee_render_result_environment(rl, viewname, rect, vedata, sldata);
eevee_render_result_bloom(rl, viewname, rect, vedata, sldata);
eevee_render_result_volume_light(rl, viewname, rect, vedata, sldata);
eevee_render_result_aovs(rl, viewname, rect, vedata, sldata);
eevee_render_result_cryptomatte(rl, viewname, rect, vedata, sldata);
}
void EEVEE_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
{
RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA);
#define CHECK_PASS_LEGACY(name, type, channels, chanid) \
if (view_layer->passflag & (SCE_PASS_##name)) { \
RE_engine_register_pass( \
engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
} \
((void)0)
#define CHECK_PASS_EEVEE(name, type, channels, chanid) \
if (view_layer->eevee.render_passes & (EEVEE_RENDER_PASS_##name)) { \
RE_engine_register_pass( \
engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
} \
((void)0)
CHECK_PASS_LEGACY(Z, SOCK_FLOAT, 1, "Z");
CHECK_PASS_LEGACY(MIST, SOCK_FLOAT, 1, "Z");
CHECK_PASS_LEGACY(NORMAL, SOCK_VECTOR, 3, "XYZ");
CHECK_PASS_LEGACY(DIFFUSE_DIRECT, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(DIFFUSE_COLOR, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(GLOSSY_COLOR, SOCK_RGBA, 3, "RGB");
CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(SHADOW, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(AO, SOCK_RGBA, 3, "RGB");
CHECK_PASS_EEVEE(BLOOM, SOCK_RGBA, 3, "RGB");
LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
if ((aov->flag & AOV_CONFLICT) != 0) {
continue;
}
switch (aov->type) {
case AOV_TYPE_COLOR:
RE_engine_register_pass(engine, scene, view_layer, aov->name, 4, "RGBA", SOCK_RGBA);
break;
case AOV_TYPE_VALUE:
RE_engine_register_pass(engine, scene, view_layer, aov->name, 1, "X", SOCK_FLOAT);
break;
default:
break;
}
}
EEVEE_cryptomatte_update_passes(engine, scene, view_layer);
#undef CHECK_PASS_LEGACY
#undef CHECK_PASS_EEVEE
}

View File

@@ -1,529 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2019, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*/
#include "DRW_engine.h"
#include "DRW_render.h"
#include "draw_color_management.h" /* TODO: remove dependency. */
#include "BKE_global.h" /* for G.debug_value */
#include "BLI_hash.h"
#include "BLI_string_utils.h"
#include "DEG_depsgraph_query.h"
#include "eevee_private.h"
typedef enum eRenderPassPostProcessType {
PASS_POST_UNDEFINED = 0,
PASS_POST_ACCUMULATED_COLOR = 1,
PASS_POST_ACCUMULATED_COLOR_ALPHA = 2,
PASS_POST_ACCUMULATED_LIGHT = 3,
PASS_POST_ACCUMULATED_VALUE = 4,
PASS_POST_DEPTH = 5,
PASS_POST_AO = 6,
PASS_POST_NORMAL = 7,
PASS_POST_TWO_LIGHT_BUFFERS = 8,
PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR = 9,
} eRenderPassPostProcessType;
/* bitmask containing all renderpasses that need post-processing */
#define EEVEE_RENDERPASSES_WITH_POST_PROCESSING \
(EEVEE_RENDER_PASS_Z | EEVEE_RENDER_PASS_MIST | EEVEE_RENDER_PASS_NORMAL | \
EEVEE_RENDER_PASS_AO | EEVEE_RENDER_PASS_BLOOM | EEVEE_RENDER_PASS_VOLUME_LIGHT | \
EEVEE_RENDER_PASS_SHADOW | EEVEE_RENDERPASSES_MATERIAL)
#define EEVEE_RENDERPASSES_ALL \
(EEVEE_RENDERPASSES_WITH_POST_PROCESSING | EEVEE_RENDER_PASS_COMBINED)
#define EEVEE_RENDERPASSES_POST_PROCESS_ON_FIRST_SAMPLE \
(EEVEE_RENDER_PASS_Z | EEVEE_RENDER_PASS_NORMAL)
#define EEVEE_RENDERPASSES_COLOR_PASS \
(EEVEE_RENDER_PASS_DIFFUSE_COLOR | EEVEE_RENDER_PASS_SPECULAR_COLOR | EEVEE_RENDER_PASS_EMIT | \
EEVEE_RENDER_PASS_BLOOM)
#define EEVEE_RENDERPASSES_LIGHT_PASS \
(EEVEE_RENDER_PASS_DIFFUSE_LIGHT | EEVEE_RENDER_PASS_SPECULAR_LIGHT)
/* Render passes that uses volume transmittance when available */
#define EEVEE_RENDERPASSES_USES_TRANSMITTANCE \
(EEVEE_RENDER_PASS_DIFFUSE_COLOR | EEVEE_RENDER_PASS_SPECULAR_COLOR | EEVEE_RENDER_PASS_EMIT | \
EEVEE_RENDER_PASS_ENVIRONMENT)
bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PrivateData *g_data = stl->g_data;
return (g_data->render_passes & ~EEVEE_RENDERPASSES_POST_PROCESS_ON_FIRST_SAMPLE) == 0;
}
/* Calculate the hash for an AOV. The least significant bit is used to store the AOV
* type the rest of the bits are used for the name hash. */
int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov)
{
int hash = BLI_hash_string(aov->name) << 1;
SET_FLAG_FROM_TEST(hash, aov->type == AOV_TYPE_COLOR, EEVEE_AOV_HASH_COLOR_TYPE_MASK);
return hash;
}
void EEVEE_renderpasses_init(EEVEE_Data *vedata)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PrivateData *g_data = stl->g_data;
ViewLayer *view_layer = draw_ctx->view_layer;
View3D *v3d = draw_ctx->v3d;
if (v3d) {
const Scene *scene = draw_ctx->scene;
eViewLayerEEVEEPassType render_pass = v3d->shading.render_pass;
g_data->aov_hash = 0;
if (render_pass == EEVEE_RENDER_PASS_BLOOM &&
((scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) == 0)) {
render_pass = EEVEE_RENDER_PASS_COMBINED;
}
if (render_pass == EEVEE_RENDER_PASS_AOV) {
ViewLayerAOV *aov = BLI_findstring(
&view_layer->aovs, v3d->shading.aov_name, offsetof(ViewLayerAOV, name));
if (aov != NULL) {
g_data->aov_hash = EEVEE_renderpasses_aov_hash(aov);
}
else {
/* AOV not found in view layer. */
render_pass = EEVEE_RENDER_PASS_COMBINED;
}
}
g_data->render_passes = render_pass;
}
else {
eViewLayerEEVEEPassType enabled_render_passes = view_layer->eevee.render_passes;
#define ENABLE_FROM_LEGACY(name_legacy, name_eevee) \
SET_FLAG_FROM_TEST(enabled_render_passes, \
(view_layer->passflag & SCE_PASS_##name_legacy) != 0, \
EEVEE_RENDER_PASS_##name_eevee);
ENABLE_FROM_LEGACY(Z, Z)
ENABLE_FROM_LEGACY(MIST, MIST)
ENABLE_FROM_LEGACY(NORMAL, NORMAL)
ENABLE_FROM_LEGACY(SHADOW, SHADOW)
ENABLE_FROM_LEGACY(AO, AO)
ENABLE_FROM_LEGACY(EMIT, EMIT)
ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT)
ENABLE_FROM_LEGACY(DIFFUSE_COLOR, DIFFUSE_COLOR)
ENABLE_FROM_LEGACY(GLOSSY_COLOR, SPECULAR_COLOR)
ENABLE_FROM_LEGACY(DIFFUSE_DIRECT, DIFFUSE_LIGHT)
ENABLE_FROM_LEGACY(GLOSSY_DIRECT, SPECULAR_LIGHT)
ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT)
#undef ENABLE_FROM_LEGACY
if (DRW_state_is_image_render() && !BLI_listbase_is_empty(&view_layer->aovs)) {
enabled_render_passes |= EEVEE_RENDER_PASS_AOV;
g_data->aov_hash = EEVEE_AOV_HASH_ALL;
}
g_data->render_passes = (enabled_render_passes & EEVEE_RENDERPASSES_ALL) |
EEVEE_RENDER_PASS_COMBINED;
}
EEVEE_material_renderpasses_init(vedata);
EEVEE_cryptomatte_renderpasses_init(vedata);
}
BLI_INLINE bool eevee_renderpasses_volumetric_active(const EEVEE_EffectsInfo *effects,
const EEVEE_PrivateData *g_data)
{
if (effects->enabled_effects & EFFECT_VOLUMETRIC) {
if (g_data->render_passes &
(EEVEE_RENDER_PASS_VOLUME_LIGHT | EEVEE_RENDERPASSES_USES_TRANSMITTANCE)) {
return true;
}
}
return false;
}
void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
uint tot_samples)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_PrivateData *g_data = stl->g_data;
const bool needs_post_processing = (g_data->render_passes &
EEVEE_RENDERPASSES_WITH_POST_PROCESSING) > 0;
if (needs_post_processing) {
/* Create FrameBuffer. */
/* Should be enough to store the data needs for a single pass.
* Some passes will use less, but it is only relevant for final renderings and
* when renderpasses other than `EEVEE_RENDER_PASS_COMBINED` are requested */
DRW_texture_ensure_fullscreen_2d(&txl->renderpass, GPU_RGBA16F, 0);
GPU_framebuffer_ensure_config(&fbl->renderpass_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->renderpass)});
if ((g_data->render_passes & EEVEE_RENDERPASSES_MATERIAL) != 0) {
EEVEE_material_output_init(sldata, vedata, tot_samples);
}
if ((g_data->render_passes & EEVEE_RENDER_PASS_MIST) != 0) {
EEVEE_mist_output_init(sldata, vedata);
}
if ((g_data->render_passes & EEVEE_RENDER_PASS_SHADOW) != 0) {
EEVEE_shadow_output_init(sldata, vedata, tot_samples);
}
if ((g_data->render_passes & EEVEE_RENDER_PASS_AO) != 0) {
EEVEE_occlusion_output_init(sldata, vedata, tot_samples);
}
if ((g_data->render_passes & EEVEE_RENDER_PASS_BLOOM) != 0 &&
(effects->enabled_effects & EFFECT_BLOOM) != 0) {
EEVEE_bloom_output_init(sldata, vedata, tot_samples);
}
if (eevee_renderpasses_volumetric_active(effects, g_data)) {
EEVEE_volumes_output_init(sldata, vedata, tot_samples);
}
/* We set a default texture as not all post processes uses the inputBuffer. */
g_data->renderpass_input = txl->color;
g_data->renderpass_col_input = txl->color;
g_data->renderpass_light_input = txl->color;
g_data->renderpass_transmittance_input = txl->color;
}
else {
/* Free unneeded memory */
DRW_TEXTURE_FREE_SAFE(txl->renderpass);
GPU_FRAMEBUFFER_FREE_SAFE(fbl->renderpass_fb);
}
/* Cryptomatte doesn't use the GPU shader for post processing */
if ((g_data->render_passes & (EEVEE_RENDER_PASS_CRYPTOMATTE)) != 0) {
EEVEE_cryptomatte_output_init(sldata, vedata, tot_samples);
}
}
void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_PrivateData *g_data = vedata->stl->g_data;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
const bool needs_post_processing = (g_data->render_passes &
EEVEE_RENDERPASSES_WITH_POST_PROCESSING) > 0;
if (needs_post_processing) {
DRW_PASS_CREATE(psl->renderpass_pass, DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_renderpasses_post_process_sh_get(),
psl->renderpass_pass);
DRW_shgroup_uniform_texture_ref(grp, "inputBuffer", &g_data->renderpass_input);
DRW_shgroup_uniform_texture_ref(grp, "inputColorBuffer", &g_data->renderpass_col_input);
DRW_shgroup_uniform_texture_ref(
grp, "inputSecondLightBuffer", &g_data->renderpass_light_input);
DRW_shgroup_uniform_texture_ref(
grp, "inputTransmittanceBuffer", &g_data->renderpass_transmittance_input);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
DRW_shgroup_uniform_block_ref(grp, "common_block", &sldata->common_ubo);
DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &sldata->renderpass_ubo.combined);
DRW_shgroup_uniform_int(grp, "currentSample", &g_data->renderpass_current_sample, 1);
DRW_shgroup_uniform_int(grp, "renderpassType", &g_data->renderpass_type, 1);
DRW_shgroup_uniform_int(grp, "postProcessType", &g_data->renderpass_postprocess, 1);
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
}
else {
psl->renderpass_pass = NULL;
}
}
/* Post-process data to construct a specific render-pass
*
* This method will create a shading group to perform the post-processing for the given
* `renderpass_type`. The post-processing will be done and the result will be stored in the
* `vedata->txl->renderpass` texture.
*
* Only invoke this function for passes that need post-processing.
*
* After invoking this function the active frame-buffer is set to `vedata->fbl->renderpass_fb`. */
void EEVEE_renderpasses_postprocess(EEVEE_ViewLayerData *UNUSED(sldata),
EEVEE_Data *vedata,
eViewLayerEEVEEPassType renderpass_type,
int aov_index)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PrivateData *g_data = stl->g_data;
EEVEE_EffectsInfo *effects = stl->effects;
/* Compensate for taa_current_sample being incremented after last drawing in
* EEVEE_temporal_sampling_draw when DRW_state_is_image_render(). */
const int current_sample = DRW_state_is_image_render() ? effects->taa_current_sample - 1 :
effects->taa_current_sample;
g_data->renderpass_current_sample = current_sample;
g_data->renderpass_type = renderpass_type;
g_data->renderpass_postprocess = PASS_POST_UNDEFINED;
const bool volumetric_active = eevee_renderpasses_volumetric_active(effects, g_data);
eRenderPassPostProcessType default_color_pass_type =
volumetric_active ? PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR : PASS_POST_ACCUMULATED_COLOR;
g_data->renderpass_transmittance_input = volumetric_active ? txl->volume_transmittance_accum :
txl->color;
if (!volumetric_active && renderpass_type == EEVEE_RENDER_PASS_VOLUME_LIGHT) {
/* Early exit: Volumetric effect is off, but the volume light pass was requested. */
static float clear_col[4] = {0.0f};
GPU_framebuffer_bind(fbl->renderpass_fb);
GPU_framebuffer_clear_color(fbl->renderpass_fb, clear_col);
return;
}
switch (renderpass_type) {
case EEVEE_RENDER_PASS_Z: {
g_data->renderpass_postprocess = PASS_POST_DEPTH;
break;
}
case EEVEE_RENDER_PASS_AO: {
g_data->renderpass_postprocess = PASS_POST_AO;
g_data->renderpass_input = txl->ao_accum;
break;
}
case EEVEE_RENDER_PASS_NORMAL: {
g_data->renderpass_postprocess = PASS_POST_NORMAL;
g_data->renderpass_input = effects->ssr_normal_input;
break;
}
case EEVEE_RENDER_PASS_MIST: {
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_VALUE;
g_data->renderpass_input = txl->mist_accum;
break;
}
case EEVEE_RENDER_PASS_VOLUME_LIGHT: {
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR;
g_data->renderpass_input = txl->volume_scatter_accum;
break;
}
case EEVEE_RENDER_PASS_SHADOW: {
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_VALUE;
g_data->renderpass_input = txl->shadow_accum;
break;
}
case EEVEE_RENDER_PASS_DIFFUSE_COLOR: {
g_data->renderpass_postprocess = default_color_pass_type;
g_data->renderpass_input = txl->diff_color_accum;
break;
}
case EEVEE_RENDER_PASS_SPECULAR_COLOR: {
g_data->renderpass_postprocess = default_color_pass_type;
g_data->renderpass_input = txl->spec_color_accum;
break;
}
case EEVEE_RENDER_PASS_ENVIRONMENT: {
g_data->renderpass_postprocess = default_color_pass_type;
g_data->renderpass_input = txl->env_accum;
break;
}
case EEVEE_RENDER_PASS_EMIT: {
g_data->renderpass_postprocess = default_color_pass_type;
g_data->renderpass_input = txl->emit_accum;
break;
}
case EEVEE_RENDER_PASS_SPECULAR_LIGHT: {
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_LIGHT;
g_data->renderpass_input = txl->spec_light_accum;
g_data->renderpass_col_input = txl->spec_color_accum;
if ((stl->effects->enabled_effects & EFFECT_SSR) != 0) {
g_data->renderpass_postprocess = PASS_POST_TWO_LIGHT_BUFFERS;
g_data->renderpass_light_input = txl->ssr_accum;
}
else {
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_LIGHT;
}
break;
}
case EEVEE_RENDER_PASS_DIFFUSE_LIGHT: {
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_LIGHT;
g_data->renderpass_input = txl->diff_light_accum;
g_data->renderpass_col_input = txl->diff_color_accum;
if ((stl->effects->enabled_effects & EFFECT_SSS) != 0) {
g_data->renderpass_postprocess = PASS_POST_TWO_LIGHT_BUFFERS;
g_data->renderpass_light_input = txl->sss_accum;
}
else {
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_LIGHT;
}
break;
}
case EEVEE_RENDER_PASS_AOV: {
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR_ALPHA;
g_data->renderpass_input = txl->aov_surface_accum[aov_index];
break;
}
case EEVEE_RENDER_PASS_BLOOM: {
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR;
g_data->renderpass_input = txl->bloom_accum;
g_data->renderpass_current_sample = 1;
break;
}
default: {
break;
}
}
GPU_framebuffer_bind(fbl->renderpass_fb);
DRW_draw_pass(psl->renderpass_pass);
}
void EEVEE_renderpasses_output_accumulate(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
bool post_effect)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_PrivateData *g_data = stl->g_data;
eViewLayerEEVEEPassType render_pass = g_data->render_passes;
if (!post_effect) {
if ((render_pass & EEVEE_RENDER_PASS_MIST) != 0) {
EEVEE_mist_output_accumulate(sldata, vedata);
}
if ((render_pass & EEVEE_RENDER_PASS_AO) != 0) {
EEVEE_occlusion_output_accumulate(sldata, vedata);
}
if ((render_pass & EEVEE_RENDER_PASS_SHADOW) != 0) {
EEVEE_shadow_output_accumulate(sldata, vedata);
}
if ((render_pass & EEVEE_RENDERPASSES_MATERIAL) != 0) {
EEVEE_material_output_accumulate(sldata, vedata);
}
if (eevee_renderpasses_volumetric_active(effects, g_data)) {
EEVEE_volumes_output_accumulate(sldata, vedata);
}
if ((render_pass & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) {
EEVEE_cryptomatte_output_accumulate(sldata, vedata);
}
}
else {
if ((render_pass & EEVEE_RENDER_PASS_BLOOM) != 0 &&
(effects->enabled_effects & EFFECT_BLOOM) != 0) {
EEVEE_bloom_output_accumulate(sldata, vedata);
}
}
}
void EEVEE_renderpasses_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
/* We can only draw a single render-pass. Light-passes also select their color pass
* (a second pass). We mask the light pass when a light pass is selected. */
const eViewLayerEEVEEPassType render_pass =
((stl->g_data->render_passes & EEVEE_RENDERPASSES_LIGHT_PASS) != 0) ?
(stl->g_data->render_passes & EEVEE_RENDERPASSES_LIGHT_PASS) :
stl->g_data->render_passes;
bool is_valid = (render_pass & EEVEE_RENDERPASSES_ALL) != 0;
bool needs_color_transfer = (render_pass & EEVEE_RENDERPASSES_COLOR_PASS) != 0 &&
DRW_state_is_opengl_render();
UNUSED_VARS(needs_color_transfer);
if ((render_pass & EEVEE_RENDER_PASS_BLOOM) != 0 &&
(effects->enabled_effects & EFFECT_BLOOM) == 0) {
is_valid = false;
}
const int current_sample = stl->effects->taa_current_sample;
const int total_samples = stl->effects->taa_total_sample;
if ((render_pass & EEVEE_RENDERPASSES_POST_PROCESS_ON_FIRST_SAMPLE) &&
(current_sample > 1 && total_samples != 1)) {
return;
}
if (is_valid) {
EEVEE_renderpasses_postprocess(sldata, vedata, render_pass, 0);
GPU_framebuffer_bind(dfbl->default_fb);
DRW_transform_none(txl->renderpass);
}
else {
/* Draw state is not valid for this pass, clear the buffer */
static float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
GPU_framebuffer_bind(dfbl->default_fb);
GPU_framebuffer_clear_color(dfbl->default_fb, clear_color);
}
GPU_framebuffer_bind(fbl->main_fb);
}
void EEVEE_renderpasses_draw_debug(EEVEE_Data *vedata)
{
EEVEE_TextureList *txl = vedata->txl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
GPUTexture *tx = NULL;
/* Debug : Output buffer to view. */
switch (G.debug_value) {
case 1:
tx = txl->maxzbuffer;
break;
case 2:
/* UNUSED */
break;
case 3:
tx = effects->ssr_normal_input;
break;
case 4:
tx = effects->ssr_specrough_input;
break;
case 5:
tx = txl->color_double_buffer;
break;
case 6:
tx = effects->gtao_horizons_renderpass;
break;
case 7:
tx = effects->gtao_horizons_renderpass;
break;
case 8:
tx = effects->sss_irradiance;
break;
case 9:
tx = effects->sss_radius;
break;
case 10:
tx = effects->sss_albedo;
break;
case 11:
tx = effects->velocity_tx;
break;
default:
break;
}
if (tx) {
DRW_transform_none(tx);
}
}

View File

@@ -0,0 +1,92 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
#include "BLI_hash_tables.hh"
#include "BLI_rect.h"
#include "BLI_vector.hh"
#include "RE_pipeline.h"
#include "eevee_instance.hh"
#include "eevee_renderpasses.hh"
namespace blender::eevee {
class Instance;
/* -------------------------------------------------------------------- */
/** \name RenderPasses
* \{ */
void RenderPasses::init(const int extent[2], const rcti *output_rect)
{
const Scene *scene = inst_.scene;
eRenderPassBit enabled_passes;
if (inst_.render_layer) {
enabled_passes = to_render_passes_bits(inst_.render_layer->passflag);
/* Cannot output motion vectors when using motion blur. */
if (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) {
enabled_passes &= ~RENDERPASS_VECTOR;
}
}
else if (inst_.v3d) {
enabled_passes = to_render_passes_bits(inst_.v3d->shading.render_pass);
/* We need the depth pass for compositing overlays or GPencil. */
if (!DRW_state_is_scene_render()) {
enabled_passes |= RENDERPASS_DEPTH;
}
}
else {
enabled_passes = RENDERPASS_COMBINED;
}
const bool use_log_encoding = scene->eevee.flag & SCE_EEVEE_FILM_LOG_ENCODING;
rcti fallback_rect;
if (BLI_rcti_is_empty(output_rect)) {
BLI_rcti_init(&fallback_rect, 0, extent[0], 0, extent[1]);
output_rect = &fallback_rect;
}
/* HACK to iterate over all passes. */
enabled_passes_ = RENDERPASS_ALL;
for (RenderPassItem rpi : *this) {
bool enable = (enabled_passes & rpi.pass_bit) != 0;
if (enable && rpi.film == nullptr) {
rpi.film = new Film(inst_,
to_render_passes_data_type(rpi.pass_bit, use_log_encoding),
to_render_passes_name(rpi.pass_bit));
}
else if (!enable && rpi.film != nullptr) {
/* Delete unused passes. */
delete rpi.film;
rpi.film = nullptr;
}
if (rpi.film) {
rpi.film->init(extent, output_rect);
}
}
enabled_passes_ = enabled_passes;
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,249 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
#pragma once
#include "DRW_render.h"
#include "BLI_hash_tables.hh"
#include "BLI_vector.hh"
#include "RE_pipeline.h"
#include "eevee_film.hh"
namespace blender::eevee {
class Instance;
/* -------------------------------------------------------------------- */
/** \name eRenderPassBit
*
* This enum might seems redundant but there is an opportunity to use it for internal debug passes.
* \{ */
enum eRenderPassBit {
RENDERPASS_NONE = 0,
RENDERPASS_COMBINED = (1 << 0),
RENDERPASS_DEPTH = (1 << 1),
RENDERPASS_NORMAL = (1 << 2),
RENDERPASS_VECTOR = (1 << 3),
/** Used for iterator. */
RENDERPASS_MAX,
RENDERPASS_ALL = ((RENDERPASS_MAX - 1) << 1) - 1,
};
ENUM_OPERATORS(eRenderPassBit, RENDERPASS_NORMAL)
static inline eRenderPassBit to_render_passes_bits(int i_rpasses)
{
eRenderPassBit rpasses = RENDERPASS_NONE;
SET_FLAG_FROM_TEST(rpasses, i_rpasses & SCE_PASS_COMBINED, RENDERPASS_COMBINED);
SET_FLAG_FROM_TEST(rpasses, i_rpasses & SCE_PASS_Z, RENDERPASS_DEPTH);
SET_FLAG_FROM_TEST(rpasses, i_rpasses & SCE_PASS_NORMAL, RENDERPASS_NORMAL);
SET_FLAG_FROM_TEST(rpasses, i_rpasses & SCE_PASS_VECTOR, RENDERPASS_VECTOR);
return rpasses;
}
static inline const char *to_render_passes_name(eRenderPassBit rpass)
{
switch (rpass) {
case RENDERPASS_COMBINED:
return RE_PASSNAME_COMBINED;
case RENDERPASS_DEPTH:
return RE_PASSNAME_Z;
case RENDERPASS_NORMAL:
return RE_PASSNAME_NORMAL;
case RENDERPASS_VECTOR:
return RE_PASSNAME_VECTOR;
default:
BLI_assert(0);
return "";
}
}
static inline eFilmDataType to_render_passes_data_type(eRenderPassBit rpass,
const bool use_log_encoding)
{
switch (rpass) {
case RENDERPASS_COMBINED:
return (use_log_encoding) ? FILM_DATA_COLOR_LOG : FILM_DATA_COLOR;
case RENDERPASS_DEPTH:
return FILM_DATA_DEPTH;
case RENDERPASS_NORMAL:
return FILM_DATA_NORMAL;
case RENDERPASS_VECTOR:
return FILM_DATA_MOTION;
default:
BLI_assert(0);
return FILM_DATA_COLOR;
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name RenderPasses
* \{ */
/**
* Contains and manages each \c Film output for each render pass output.
*/
class RenderPasses {
public:
/** Film for each render pass. A nullptr means the pass is not needed. */
Film *combined = nullptr;
Film *depth = nullptr;
Film *normal = nullptr;
Film *vector = nullptr;
Vector<Film *> aovs;
private:
Instance &inst_;
eRenderPassBit enabled_passes_ = RENDERPASS_NONE;
public:
RenderPasses(Instance &inst) : inst_(inst){};
~RenderPasses()
{
delete combined;
delete depth;
delete normal;
delete vector;
}
void init(const int extent[2], const rcti *output_rect);
void sync(void)
{
for (RenderPassItem rpi : *this) {
rpi.film->sync();
}
}
void end_sync(void)
{
for (RenderPassItem rpi : *this) {
rpi.film->end_sync();
}
}
void resolve_viewport(DefaultFramebufferList *dfbl)
{
for (RenderPassItem rpi : *this) {
if (rpi.pass_bit == RENDERPASS_DEPTH) {
rpi.film->resolve_viewport(dfbl->depth_only_fb);
}
else {
/* Ensures only one color render pass is enabled. */
BLI_assert((enabled_passes_ & ~RENDERPASS_DEPTH) == rpi.pass_bit);
rpi.film->resolve_viewport(dfbl->color_only_fb);
}
}
}
void read_result(RenderLayer *render_layer, const char *view_name)
{
for (RenderPassItem rpi : *this) {
const char *pass_name = to_render_passes_name(rpi.pass_bit);
RenderPass *rp = RE_pass_find_by_name(render_layer, pass_name, view_name);
if (rp) {
rpi.film->read_result(rp->rect);
}
}
}
private:
constexpr Film *&render_pass_bit_to_film_p(eRenderPassBit rpass)
{
switch (rpass) {
case RENDERPASS_COMBINED:
return combined;
case RENDERPASS_DEPTH:
return depth;
case RENDERPASS_NORMAL:
return normal;
case RENDERPASS_VECTOR:
return vector;
default:
BLI_assert(0);
return combined;
}
}
/**
* Iterator
**/
struct RenderPassItem {
Film *&film;
eRenderPassBit pass_bit;
constexpr explicit RenderPassItem(Film *&film_, eRenderPassBit pass_bit_)
: film(film_), pass_bit(pass_bit_){};
};
class Iterator {
private:
RenderPasses &render_passes_;
int64_t current_;
public:
constexpr explicit Iterator(RenderPasses &rpasses, int64_t current)
: render_passes_(rpasses), current_(current){};
constexpr Iterator &operator++()
{
while (current_ < RENDERPASS_MAX) {
current_ <<= 1;
if (current_ & render_passes_.enabled_passes_) {
break;
}
}
return *this;
}
constexpr friend bool operator!=(const Iterator &a, const Iterator &b)
{
return a.current_ != b.current_;
}
constexpr RenderPassItem operator*()
{
eRenderPassBit pass_bit = static_cast<eRenderPassBit>(current_);
return RenderPassItem(render_passes_.render_pass_bit_to_film_p(pass_bit), pass_bit);
}
};
/* Iterator over all enabled passes. */
constexpr Iterator begin()
{
return Iterator(*this, 1);
}
constexpr Iterator end()
{
return Iterator(*this, power_of_2_max_constexpr(RENDERPASS_MAX));
}
};
/** \} */
} // namespace blender::eevee

View File

@@ -1,129 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2019, Blender Foundation.
*/
/** \file
* \ingroup EEVEE
*/
#include "eevee_private.h"
#include "BLI_rand.h"
/**
* Special ball distribution:
* Point are distributed in a way that when they are orthogonally
* projected into any plane, the resulting distribution is (close to)
* a uniform disc distribution.
*/
void EEVEE_sample_ball(int sample_ofs, float radius, float rsample[3])
{
double ht_point[3];
double ht_offset[3] = {0.0, 0.0, 0.0};
const uint ht_primes[3] = {2, 3, 7};
BLI_halton_3d(ht_primes, ht_offset, sample_ofs, ht_point);
/* De-correlate AA and shadow samples. (see T68594) */
ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0);
ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0);
ht_point[2] = fmod(ht_point[2] * 1151.0, 1.0);
float omega = ht_point[1] * 2.0f * M_PI;
rsample[2] = ht_point[0] * 2.0f - 1.0f; /* cos theta */
float r = sqrtf(fmaxf(0.0f, 1.0f - rsample[2] * rsample[2])); /* sin theta */
rsample[0] = r * cosf(omega);
rsample[1] = r * sinf(omega);
radius *= sqrt(sqrt(ht_point[2]));
mul_v3_fl(rsample, radius);
}
void EEVEE_sample_rectangle(int sample_ofs,
const float x_axis[3],
const float y_axis[3],
float size_x,
float size_y,
float rsample[3])
{
double ht_point[2];
double ht_offset[2] = {0.0, 0.0};
const uint ht_primes[2] = {2, 3};
BLI_halton_2d(ht_primes, ht_offset, sample_ofs, ht_point);
/* De-correlate AA and shadow samples. (see T68594) */
ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0);
ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0);
/* Change distribution center to be 0,0 */
ht_point[0] = (ht_point[0] > 0.5f) ? ht_point[0] - 1.0f : ht_point[0];
ht_point[1] = (ht_point[1] > 0.5f) ? ht_point[1] - 1.0f : ht_point[1];
zero_v3(rsample);
madd_v3_v3fl(rsample, x_axis, (ht_point[0] * 2.0f) * size_x);
madd_v3_v3fl(rsample, y_axis, (ht_point[1] * 2.0f) * size_y);
}
void EEVEE_sample_ellipse(int sample_ofs,
const float x_axis[3],
const float y_axis[3],
float size_x,
float size_y,
float rsample[3])
{
double ht_point[2];
double ht_offset[2] = {0.0, 0.0};
const uint ht_primes[2] = {2, 3};
BLI_halton_2d(ht_primes, ht_offset, sample_ofs, ht_point);
/* Decorelate AA and shadow samples. (see T68594) */
ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0);
ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0);
/* Uniform disc sampling. */
float omega = ht_point[1] * 2.0f * M_PI;
float r = sqrtf(ht_point[0]);
ht_point[0] = r * cosf(omega) * size_x;
ht_point[1] = r * sinf(omega) * size_y;
zero_v3(rsample);
madd_v3_v3fl(rsample, x_axis, ht_point[0]);
madd_v3_v3fl(rsample, y_axis, ht_point[1]);
}
void EEVEE_random_rotation_m4(int sample_ofs, float scale, float r_mat[4][4])
{
double ht_point[3];
double ht_offset[3] = {0.0, 0.0, 0.0};
const uint ht_primes[3] = {2, 3, 5};
BLI_halton_3d(ht_primes, ht_offset, sample_ofs, ht_point);
/* Decorelate AA and shadow samples. (see T68594) */
ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0);
ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0);
ht_point[2] = fmod(ht_point[2] * 1151.0, 1.0);
rotate_m4(r_mat, 'X', ht_point[0] * scale);
rotate_m4(r_mat, 'Y', ht_point[1] * scale);
rotate_m4(r_mat, 'Z', ht_point[2] * scale);
}

View File

@@ -0,0 +1,350 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* Random Number Generator
*/
#pragma once
#include "BKE_colortools.h"
#include "BLI_rand.h"
#include "BLI_vector.hh"
#include "DNA_scene_types.h"
#include "DRW_render.h"
#include "GPU_framebuffer.h"
#include "GPU_texture.h"
#include "eevee_shader_shared.hh"
#include "eevee_wrapper.hh"
namespace blender::eevee {
/**
* Random number generator, contains persistent state and sample count logic.
*/
class Sampling {
private:
/* Number of samples in the first ring of jittered depth of field. */
constexpr static uint64_t dof_web_density_ = 6;
/* High number of sample for viewport infinite rendering. */
constexpr static uint64_t infinite_sample_count_ = 0xFFFFFFu;
/** 1 based current sample. */
uint64_t sample_ = 1;
/** Target sample count. */
uint64_t sample_count_ = 64;
/** Number of ring in the web pattern of the jittered Depth of Field. */
uint64_t dof_ring_count_ = 0;
/** Number of samples in the web pattern of the jittered Depth of Field. */
uint64_t dof_sample_count_ = 1;
/** Motion blur steps. */
uint64_t motion_blur_steps_ = 1;
/** Used for viewport smooth transition. */
int64_t sample_viewport_ = 1;
int64_t viewport_smoothing_start = 0;
int64_t viewport_smoothing_duration = 0;
/** Tag to reset sampling for the next sample. */
bool reset_ = false;
StructBuffer<SamplingData> data_;
public:
Sampling(){};
~Sampling(){};
void init(const Scene *scene)
{
sample_count_ = DRW_state_is_image_render() ? scene->eevee.taa_render_samples :
scene->eevee.taa_samples;
if (sample_count_ == 0) {
BLI_assert(!DRW_state_is_image_render());
sample_count_ = infinite_sample_count_;
}
motion_blur_steps_ = DRW_state_is_image_render() ? scene->eevee.motion_blur_steps : 1;
sample_count_ = divide_ceil_u(sample_count_, motion_blur_steps_);
if (scene->eevee.flag & SCE_EEVEE_DOF_JITTER) {
if (sample_count_ == infinite_sample_count_) {
/* Special case for viewport continuous rendering. We clamp to a max sample
* to avoid the jittered dof never converging. */
dof_ring_count_ = 6;
}
else {
dof_ring_count_ = web_ring_count_get(dof_web_density_, sample_count_);
}
dof_sample_count_ = web_sample_count_get(dof_web_density_, dof_ring_count_);
/* Change total sample count to fill the web pattern entirely. */
sample_count_ = divide_ceil_u(sample_count_, dof_sample_count_) * dof_sample_count_;
}
else {
dof_ring_count_ = 0;
dof_sample_count_ = 1;
}
/* Only multiply after to have full the full DoF web pattern for each time steps. */
sample_count_ *= motion_blur_steps_;
if (!DRW_state_is_image_render()) {
/* TODO(fclem) UI. */
viewport_smoothing_start = 16;
viewport_smoothing_duration = 32;
/* At minima start when rendering has finished. */
viewport_smoothing_start = min_ii(viewport_smoothing_start, sample_count_);
/* Basically counts the number of redraw. */
sample_viewport_ += 1;
}
else {
viewport_smoothing_start = 0;
viewport_smoothing_duration = 0;
}
}
void end_sync(void)
{
if (reset_) {
sample_ = 1;
sample_viewport_ = 1;
}
}
void step(void)
{
{
/* TODO(fclem) we could use some persistent states to speedup the computation. */
double r[2], offset[2] = {0, 0};
/* Using 2,3 primes as per UE4 Temporal AA presentation.
* advances.realtimerendering.com/s2014/epic/TemporalAA.pptx (slide 14) */
uint32_t primes[2] = {2, 3};
BLI_halton_2d(primes, offset, sample_, r);
data_.dimensions[SAMPLING_FILTER_U][0] = r[0];
data_.dimensions[SAMPLING_FILTER_V][0] = r[1];
/* TODO decorelate. */
data_.dimensions[SAMPLING_TIME][0] = r[0];
data_.dimensions[SAMPLING_CLOSURE][0] = r[1];
}
{
double r[2], offset[2] = {0, 0};
uint32_t primes[2] = {5, 7};
BLI_halton_2d(primes, offset, sample_, r);
data_.dimensions[SAMPLING_LENS_U][0] = r[0];
data_.dimensions[SAMPLING_LENS_V][0] = r[1];
/* TODO decorelate. */
data_.dimensions[SAMPLING_LIGHTPROBE][0] = r[0];
data_.dimensions[SAMPLING_TRANSPARENCY][0] = r[1];
}
{
/* Using leaped halton sequence so we can reused the same primes as lens. */
double r[3], offset[3] = {0, 0, 0};
uint64_t leap = 11;
uint32_t primes[3] = {5, 4, 7};
BLI_halton_3d(primes, offset, (sample_ - 1) * leap, r);
data_.dimensions[SAMPLING_SHADOW_U][0] = r[0];
data_.dimensions[SAMPLING_SHADOW_V][0] = r[1];
data_.dimensions[SAMPLING_SHADOW_W][0] = r[2];
}
{
/* Using leaped halton sequence so we can reused the same primes. */
double r[2], offset[2] = {0, 0};
uint64_t leap = 5;
uint32_t primes[2] = {2, 3};
BLI_halton_2d(primes, offset, (sample_ - 1) * leap, r);
data_.dimensions[SAMPLING_SHADOW_X][0] = r[0];
data_.dimensions[SAMPLING_SHADOW_Y][0] = r[1];
}
data_.push_update();
sample_++;
reset_ = false;
}
/**
* Getters
**/
/* Returns current, 1 based, scene sample index. */
uint64_t sample_get(void) const
{
return sample_;
}
/* Returns blend factor to apply to film to have a smooth transition instead of flickering
* for the first samples of random shadows. */
float viewport_smoothing_opacity_factor_get(void) const
{
return (sample_ == 1 || viewport_smoothing_duration == 0) ?
1.0f :
square_f(clamp_f((sample_viewport_ - viewport_smoothing_start) /
float(viewport_smoothing_duration),
0.0f,
1.0f));
}
/* Returns sample count inside the jittered depth of field web pattern. */
uint64_t dof_ring_count_get(void) const
{
return dof_ring_count_;
}
/* Returns sample count inside the jittered depth of field web pattern. */
uint64_t dof_sample_count_get(void) const
{
return dof_sample_count_;
}
const GPUUniformBuf *ubo_get(void) const
{
return data_.ubo_get();
}
/* Returns a pseudo random number in [0..1] range. Each dimension are uncorrelated. */
float rng_get(eSamplingDimension dimension) const
{
return data_.dimensions[dimension][0];
}
/* Returns true if rendering has finished. */
bool finished(void) const
{
return (sample_ > sample_count_);
}
/* Returns true if viewport smoothing and sampling has finished. */
bool finished_viewport(void) const
{
return finished() &&
(sample_viewport_ > (viewport_smoothing_start + viewport_smoothing_duration));
}
/* Viewport Only: Function to call to notify something in the scene changed.
* This will reset accumulation. Do not call after end_sync() or during sample rendering. */
void reset(void)
{
reset_ = true;
}
/* Viewport Only: true if an update happened in the scene and accumulation needs reset. */
bool is_reset(void) const
{
return reset_;
}
/* Return true if we are starting a new motion blur step. We need to run sync agains since
* depsgraph was updated by MotionBlur::step(). */
bool do_render_sync(void) const
{
return DRW_state_is_image_render() &&
(((sample_ - 1) % (sample_count_ / motion_blur_steps_)) == 0);
}
void dof_disk_sample_get(float *r_radius, float *r_theta)
{
if (dof_ring_count_ == 0) {
*r_radius = *r_theta = 0.0f;
return;
}
int s = sample_ - 1;
int ring = 0;
int ring_sample_count = 1;
int ring_sample = 1;
s = s * (dof_web_density_ - 1);
s = s % dof_sample_count_;
/* Choosing sample to we get faster convergence.
* The issue here is that we cannot map a low descripency sequence to this sampling pattern
* because the same sample could be choosen twice in relatively short intervals. */
/* For now just use an ascending sequence with an offset. This gives us relatively quick
* initial coverage and relatively high distance between samples. */
/* TODO(fclem) We can try to order samples based on a LDS into a table to avoid duplicates.
* The drawback would be some memory consumption and init time. */
int samples_passed = 1;
while (s >= samples_passed) {
ring++;
ring_sample_count = ring * dof_web_density_;
ring_sample = s - samples_passed;
ring_sample = (ring_sample + 1) % ring_sample_count;
samples_passed += ring_sample_count;
}
*r_radius = ring / (float)dof_ring_count_;
*r_theta = 2.0f * M_PI * ring_sample / (float)ring_sample_count;
}
/* Creates a discrete cumulative distribution function table from a given curvemapping.
* Output cdf vector is expected to already be sized according to the wanted resolution. */
static void cdf_from_curvemapping(const CurveMapping &curve, Vector<float> &cdf)
{
BLI_assert(cdf.size() > 1);
cdf[0] = 0.0f;
/* Actual CDF evaluation. */
for (int u : cdf.index_range()) {
float x = (float)(u + 1) / (float)(cdf.size() - 1);
cdf[u + 1] = cdf[u] + BKE_curvemapping_evaluateF(&curve, 0, x);
}
/* Normalize the CDF. */
for (int u : cdf.index_range()) {
cdf[u] /= cdf.last();
}
/* Just to make sure. */
cdf.last() = 1.0f;
}
/* Inverts a cumulative distribution function.
* Output vector is expected to already be sized according to the wanted resolution. */
static void cdf_invert(Vector<float> &cdf, Vector<float> &inverted_cdf)
{
for (int u : inverted_cdf.index_range()) {
float x = (float)u / (float)(inverted_cdf.size() - 1);
for (int i : cdf.index_range()) {
if (i == cdf.size() - 1) {
inverted_cdf[u] = 1.0f;
}
else if (cdf[i] >= x) {
float t = (x - cdf[i]) / (cdf[i + 1] - cdf[i]);
inverted_cdf[u] = ((float)i + t) / (float)(cdf.size() - 1);
break;
}
}
}
}
/**
* Special ball distribution:
* Point are distributed in a way that when they are orthogonally
* projected into any plane, the resulting distribution is (close to)
* a uniform disc distribution.
*/
vec3 sample_ball(const float rand[3])
{
vec3 sample;
sample.z = rand[0] * 2.0f - 1.0f; /* cos theta */
float r = sqrtf(fmaxf(0.0f, 1.0f - square_f(sample.z))); /* sin theta */
float omega = rand[1] * 2.0f * M_PI;
sample.x = r * cosf(omega);
sample.y = r * sinf(omega);
sample *= sqrtf(sqrtf(rand[2]));
return sample;
}
vec2 sample_disk(const float rand[2])
{
float omega = rand[1] * 2.0f * M_PI;
return sqrtf(rand[0]) * vec2(cosf(omega), sinf(omega));
}
};
} // namespace blender::eevee

View File

@@ -1,264 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Screen space reflections and refractions techniques.
*/
#include "DRW_render.h"
#include "BLI_dynstr.h"
#include "BLI_string_utils.h"
#include "DEG_depsgraph_query.h"
#include "GPU_texture.h"
#include "eevee_private.h"
int EEVEE_screen_raytrace_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_EffectsInfo *effects = stl->effects;
const float *viewport_size = DRW_viewport_size_get();
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
if (scene_eval->eevee.flag & SCE_EEVEE_SSR_ENABLED) {
const bool use_refraction = (scene_eval->eevee.flag & SCE_EEVEE_SSR_REFRACTION) != 0;
const bool is_persp = DRW_view_is_persp_get(NULL);
if (effects->ssr_was_persp != is_persp) {
effects->ssr_was_persp = is_persp;
DRW_viewport_request_redraw();
EEVEE_temporal_sampling_reset(vedata);
stl->g_data->valid_double_buffer = false;
}
if (!effects->ssr_was_valid_double_buffer) {
DRW_viewport_request_redraw();
EEVEE_temporal_sampling_reset(vedata);
}
effects->ssr_was_valid_double_buffer = stl->g_data->valid_double_buffer;
effects->reflection_trace_full = (scene_eval->eevee.flag & SCE_EEVEE_SSR_HALF_RESOLUTION) == 0;
common_data->ssr_thickness = scene_eval->eevee.ssr_thickness;
common_data->ssr_border_fac = scene_eval->eevee.ssr_border_fade;
common_data->ssr_firefly_fac = scene_eval->eevee.ssr_firefly_fac;
common_data->ssr_max_roughness = scene_eval->eevee.ssr_max_roughness;
common_data->ssr_quality = 1.0f - 0.95f * scene_eval->eevee.ssr_quality;
common_data->ssr_brdf_bias = 0.1f + common_data->ssr_quality * 0.6f; /* Range [0.1, 0.7]. */
if (common_data->ssr_firefly_fac < 1e-8f) {
common_data->ssr_firefly_fac = FLT_MAX;
}
void *owner = (void *)EEVEE_screen_raytrace_init;
const int divisor = (effects->reflection_trace_full) ? 1 : 2;
int tracing_res[2] = {(int)viewport_size[0] / divisor, (int)viewport_size[1] / divisor};
const int size_fs[2] = {(int)viewport_size[0], (int)viewport_size[1]};
const bool high_qual_input = true; /* TODO: dither low quality input. */
const eGPUTextureFormat format = (high_qual_input) ? GPU_RGBA16F : GPU_RGBA8;
tracing_res[0] = max_ii(1, tracing_res[0]);
tracing_res[1] = max_ii(1, tracing_res[1]);
common_data->ssr_uv_scale[0] = size_fs[0] / ((float)tracing_res[0] * divisor);
common_data->ssr_uv_scale[1] = size_fs[1] / ((float)tracing_res[1] * divisor);
/* MRT for the shading pass in order to output needed data for the SSR pass. */
effects->ssr_specrough_input = DRW_texture_pool_query_2d(UNPACK2(size_fs), format, owner);
GPU_framebuffer_texture_attach(fbl->main_fb, effects->ssr_specrough_input, 2, 0);
/* Ray-tracing output. */
effects->ssr_hit_output = DRW_texture_pool_query_2d(UNPACK2(tracing_res), GPU_RGBA16F, owner);
effects->ssr_hit_depth = DRW_texture_pool_query_2d(UNPACK2(tracing_res), GPU_R16F, owner);
GPU_framebuffer_ensure_config(&fbl->screen_tracing_fb,
{
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(effects->ssr_hit_output),
GPU_ATTACHMENT_TEXTURE(effects->ssr_hit_depth),
});
return EFFECT_SSR | EFFECT_NORMAL_BUFFER | EFFECT_RADIANCE_BUFFER | EFFECT_DOUBLE_BUFFER |
((use_refraction) ? EFFECT_REFRACT : 0);
}
/* Cleanup to release memory */
GPU_FRAMEBUFFER_FREE_SAFE(fbl->screen_tracing_fb);
effects->ssr_specrough_input = NULL;
effects->ssr_hit_output = NULL;
return 0;
}
void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_EffectsInfo *effects = stl->effects;
LightCache *lcache = stl->g_data->light_cache;
if ((effects->enabled_effects & EFFECT_SSR) != 0) {
struct GPUShader *trace_shader = EEVEE_shaders_effect_reflection_trace_sh_get();
struct GPUShader *resolve_shader = EEVEE_shaders_effect_reflection_resolve_sh_get();
int hitbuf_size[3];
GPU_texture_get_mipmap_size(effects->ssr_hit_output, 0, hitbuf_size);
/** Screen space raytracing overview
*
* Following Frostbite stochastic SSR.
*
* - First pass Trace rays across the depth buffer. The hit position and PDF are
* recorded in a RGBA16F render target for each ray (sample).
*
* - We down-sample the previous frame color buffer.
*
* - For each final pixel, we gather neighbors rays and choose a color buffer
* mipmap for each ray using its PDF. (filtered importance sampling)
* We then evaluate the lighting from the probes and mix the results together.
*/
DRW_PASS_CREATE(psl->ssr_raytrace, DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(trace_shader, psl->ssr_raytrace);
DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input);
DRW_shgroup_uniform_texture_ref(grp, "specroughBuffer", &effects->ssr_specrough_input);
DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer);
DRW_shgroup_uniform_texture_ref(grp, "planarDepth", &vedata->txl->planar_depth);
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
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);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_uniform_vec2_copy(grp, "targetSize", (float[2]){hitbuf_size[0], hitbuf_size[1]});
DRW_shgroup_uniform_float_copy(
grp, "randomScale", effects->reflection_trace_full ? 0.0f : 0.5f);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
DRW_PASS_CREATE(psl->ssr_resolve, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD);
grp = DRW_shgroup_create(resolve_shader, psl->ssr_resolve);
DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input);
DRW_shgroup_uniform_texture_ref(grp, "specroughBuffer", &effects->ssr_specrough_input);
DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &lcache->cube_tx.tex);
DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &vedata->txl->planar_pool);
DRW_shgroup_uniform_texture_ref(grp, "planarDepth", &vedata->txl->planar_depth);
DRW_shgroup_uniform_texture_ref_ex(grp, "hitBuffer", &effects->ssr_hit_output, no_filter);
DRW_shgroup_uniform_texture_ref_ex(grp, "hitDepth", &effects->ssr_hit_depth, no_filter);
DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &txl->filtered_radiance);
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);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_uniform_int(grp, "samplePoolOffset", &effects->taa_current_sample, 1);
DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
}
void EEVEE_refraction_compute(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
if ((effects->enabled_effects & EFFECT_REFRACT) != 0) {
EEVEE_effects_downsample_radiance_buffer(vedata, txl->color);
/* Restore */
GPU_framebuffer_bind(fbl->main_fb);
}
}
void EEVEE_reflection_compute(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_EffectsInfo *effects = stl->effects;
if (((effects->enabled_effects & EFFECT_SSR) != 0) && stl->g_data->valid_double_buffer) {
DRW_stats_group_start("SSR");
/* Raytrace. */
GPU_framebuffer_bind(fbl->screen_tracing_fb);
DRW_draw_pass(psl->ssr_raytrace);
EEVEE_effects_downsample_radiance_buffer(vedata, txl->color_double_buffer);
GPU_framebuffer_bind(fbl->main_color_fb);
DRW_draw_pass(psl->ssr_resolve);
/* Restore */
GPU_framebuffer_bind(fbl->main_fb);
DRW_stats_group_end();
}
}
void EEVEE_reflection_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
EEVEE_Data *vedata,
uint tot_samples)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
/* Create FrameBuffer. */
const eGPUTextureFormat texture_format = (tot_samples > 256) ? GPU_RGBA32F : GPU_RGBA16F;
DRW_texture_ensure_fullscreen_2d(&txl->ssr_accum, texture_format, 0);
GPU_framebuffer_ensure_config(&fbl->ssr_accum_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ssr_accum)});
}
void EEVEE_reflection_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = vedata->stl->effects;
if (stl->g_data->valid_double_buffer) {
GPU_framebuffer_bind(fbl->ssr_accum_fb);
/* Clear texture. */
if (effects->taa_current_sample == 1) {
const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_framebuffer_clear_color(fbl->ssr_accum_fb, clear);
}
DRW_draw_pass(psl->ssr_resolve);
}
}

View File

@@ -0,0 +1,819 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* Shader module that manage shader libraries, deferred compilation,
* and static shader usage.
*/
#include "eevee_shader.hh"
#include "eevee_material.hh"
extern "C" {
extern char datatoc_common_attribute_lib_glsl[];
extern char datatoc_common_fullscreen_vert_glsl[];
extern char datatoc_common_gpencil_lib_glsl[];
extern char datatoc_common_hair_lib_glsl[];
extern char datatoc_common_math_geom_lib_glsl[];
extern char datatoc_common_math_lib_glsl[];
extern char datatoc_common_obinfos_lib_glsl[];
extern char datatoc_common_uniform_attribute_lib_glsl[];
extern char datatoc_common_view_lib_glsl[];
extern char datatoc_eevee_bsdf_lib_glsl[];
extern char datatoc_eevee_bsdf_microfacet_lib_glsl[];
extern char datatoc_eevee_bsdf_sampling_lib_glsl[];
extern char datatoc_eevee_bsdf_stubs_lib_glsl[];
extern char datatoc_eevee_camera_lib_glsl[];
extern char datatoc_eevee_camera_velocity_frag_glsl[];
extern char datatoc_eevee_closure_lib_glsl[];
extern char datatoc_eevee_cubemap_lib_glsl[];
extern char datatoc_eevee_culling_debug_frag_glsl[];
extern char datatoc_eevee_culling_iter_lib_glsl[];
extern char datatoc_eevee_culling_lib_glsl[];
extern char datatoc_eevee_culling_light_frag_glsl[];
extern char datatoc_eevee_deferred_direct_frag_glsl[];
extern char datatoc_eevee_deferred_holdout_frag_glsl[];
extern char datatoc_eevee_deferred_transparent_frag_glsl[];
extern char datatoc_eevee_deferred_volume_frag_glsl[];
extern char datatoc_eevee_depth_clear_frag_glsl[];
extern char datatoc_eevee_depth_of_field_accumulator_lib_glsl[];
extern char datatoc_eevee_depth_of_field_bokeh_lut_frag_glsl[];
extern char datatoc_eevee_depth_of_field_downsample_frag_glsl[];
extern char datatoc_eevee_depth_of_field_filter_frag_glsl[];
extern char datatoc_eevee_depth_of_field_gather_frag_glsl[];
extern char datatoc_eevee_depth_of_field_gather_holefill_frag_glsl[];
extern char datatoc_eevee_depth_of_field_lib_glsl[];
extern char datatoc_eevee_depth_of_field_reduce_copy_frag_glsl[];
extern char datatoc_eevee_depth_of_field_reduce_downsample_frag_glsl[];
extern char datatoc_eevee_depth_of_field_reduce_recursive_frag_glsl[];
extern char datatoc_eevee_depth_of_field_resolve_frag_glsl[];
extern char datatoc_eevee_depth_of_field_scatter_frag_glsl[];
extern char datatoc_eevee_depth_of_field_scatter_lib_glsl[];
extern char datatoc_eevee_depth_of_field_scatter_vert_glsl[];
extern char datatoc_eevee_depth_of_field_setup_frag_glsl[];
extern char datatoc_eevee_depth_of_field_tiles_dilate_frag_glsl[];
extern char datatoc_eevee_depth_of_field_tiles_flatten_frag_glsl[];
extern char datatoc_eevee_film_filter_frag_glsl[];
extern char datatoc_eevee_film_lib_glsl[];
extern char datatoc_eevee_film_resolve_frag_glsl[];
extern char datatoc_eevee_film_resolve_depth_frag_glsl[];
extern char datatoc_eevee_gbuffer_lib_glsl[];
extern char datatoc_eevee_irradiance_lib_glsl[];
extern char datatoc_eevee_light_eval_lib_glsl[];
extern char datatoc_eevee_light_lib_glsl[];
extern char datatoc_eevee_lightprobe_display_cubemap_frag_glsl[];
extern char datatoc_eevee_lightprobe_display_cubemap_vert_glsl[];
extern char datatoc_eevee_lightprobe_display_grid_frag_glsl[];
extern char datatoc_eevee_lightprobe_display_grid_vert_glsl[];
extern char datatoc_eevee_lightprobe_display_lib_glsl[];
extern char datatoc_eevee_lightprobe_eval_cubemap_lib_glsl[];
extern char datatoc_eevee_lightprobe_eval_grid_lib_glsl[];
extern char datatoc_eevee_lightprobe_filter_diffuse_frag_glsl[];
extern char datatoc_eevee_lightprobe_filter_downsample_frag_glsl[];
extern char datatoc_eevee_lightprobe_filter_geom_glsl[];
extern char datatoc_eevee_lightprobe_filter_glossy_frag_glsl[];
extern char datatoc_eevee_lightprobe_filter_lib_glsl[];
extern char datatoc_eevee_lightprobe_filter_vert_glsl[];
extern char datatoc_eevee_lightprobe_filter_visibility_frag_glsl[];
extern char datatoc_eevee_lookdev_background_frag_glsl[];
extern char datatoc_eevee_ltc_lib_glsl[];
extern char datatoc_eevee_motion_blur_gather_frag_glsl[];
extern char datatoc_eevee_motion_blur_lib_glsl[];
extern char datatoc_eevee_motion_blur_tiles_dilate_frag_glsl[];
extern char datatoc_eevee_motion_blur_tiles_flatten_frag_glsl[];
extern char datatoc_eevee_nodetree_eval_lib_glsl[];
extern char datatoc_eevee_sampling_lib_glsl[];
extern char datatoc_eevee_shadow_lib_glsl[];
extern char datatoc_eevee_surface_background_frag_glsl[];
extern char datatoc_eevee_surface_deferred_frag_glsl[];
extern char datatoc_eevee_surface_depth_frag_glsl[];
extern char datatoc_eevee_surface_depth_simple_frag_glsl[];
extern char datatoc_eevee_surface_forward_frag_glsl[];
extern char datatoc_eevee_surface_gpencil_vert_glsl[];
extern char datatoc_eevee_surface_hair_vert_glsl[];
extern char datatoc_eevee_surface_lib_glsl[];
extern char datatoc_eevee_surface_lookdev_vert_glsl[];
extern char datatoc_eevee_surface_mesh_geom_glsl[];
extern char datatoc_eevee_surface_mesh_vert_glsl[];
extern char datatoc_eevee_surface_velocity_frag_glsl[];
extern char datatoc_eevee_surface_velocity_lib_glsl[];
extern char datatoc_eevee_surface_velocity_mesh_vert_glsl[];
extern char datatoc_eevee_surface_world_vert_glsl[];
extern char datatoc_eevee_velocity_lib_glsl[];
extern char datatoc_eevee_volume_deferred_frag_glsl[];
extern char datatoc_eevee_volume_eval_lib_glsl[];
extern char datatoc_eevee_volume_lib_glsl[];
extern char datatoc_eevee_volume_vert_glsl[];
extern char datatoc_eevee_shader_shared_hh[];
extern char datatoc_gpu_shader_codegen_lib_glsl[];
}
namespace blender::eevee {
/** \} */
/* -------------------------------------------------------------------- */
/** \name Static shaders
*
* \{ */
ShaderModule::ShaderModule()
{
for (GPUShader *&shader : shaders_) {
shader = nullptr;
}
shared_lib_ = enum_preprocess(datatoc_eevee_shader_shared_hh);
shader_lib_ = DRW_shader_library_create();
/* NOTE: These need to be ordered by dependencies. */
DRW_SHADER_LIB_ADD(shader_lib_, common_math_lib);
DRW_shader_library_add_file(shader_lib_, shared_lib_.c_str(), "eevee_shader_shared.hh");
DRW_SHADER_LIB_ADD(shader_lib_, common_math_geom_lib);
DRW_SHADER_LIB_ADD(shader_lib_, common_hair_lib);
DRW_SHADER_LIB_ADD(shader_lib_, common_view_lib);
DRW_SHADER_LIB_ADD(shader_lib_, common_attribute_lib);
DRW_SHADER_LIB_ADD(shader_lib_, common_obinfos_lib);
DRW_SHADER_LIB_ADD(shader_lib_, common_gpencil_lib);
DRW_SHADER_LIB_ADD(shader_lib_, gpu_shader_codegen_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_bsdf_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_bsdf_microfacet_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_bsdf_sampling_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_bsdf_stubs_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_irradiance_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_closure_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_cubemap_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_gbuffer_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_nodetree_eval_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_sampling_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_ltc_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_shadow_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_camera_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_culling_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_culling_iter_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_light_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_light_eval_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_lightprobe_filter_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_lightprobe_display_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_lightprobe_eval_cubemap_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_lightprobe_eval_grid_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_volume_eval_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_volume_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_velocity_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_depth_of_field_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_depth_of_field_accumulator_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_depth_of_field_scatter_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_film_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_motion_blur_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_surface_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_surface_velocity_lib);
/* Meh ¯\_(ツ)_/¯. */
char *datatoc_nullptr_glsl = nullptr;
#define SHADER(enum_, vert_, geom_, frag_, defs_) \
shader_descriptions_[enum_].name = STRINGIFY(enum_); \
shader_descriptions_[enum_].vertex_shader_code = datatoc_##vert_##_glsl; \
shader_descriptions_[enum_].geometry_shader_code = datatoc_##geom_##_glsl; \
shader_descriptions_[enum_].fragment_shader_code = datatoc_##frag_##_glsl; \
shader_descriptions_[enum_].defines_shader_code = defs_;
#define SHADER_FULLSCREEN_DEFINES(enum_, frag_, defs_) \
SHADER(enum_, common_fullscreen_vert, nullptr, frag_, defs_)
#define SHADER_FULLSCREEN(enum_, frag_) SHADER_FULLSCREEN_DEFINES(enum_, frag_, nullptr)
SHADER_FULLSCREEN(CULLING_DEBUG, eevee_culling_debug_frag);
SHADER_FULLSCREEN(CULLING_LIGHT, eevee_culling_light_frag);
SHADER_FULLSCREEN(FILM_FILTER, eevee_film_filter_frag);
SHADER_FULLSCREEN(FILM_RESOLVE, eevee_film_resolve_frag);
SHADER_FULLSCREEN(FILM_RESOLVE_DEPTH, eevee_film_resolve_depth_frag);
SHADER_FULLSCREEN(DEFERRED_EVAL_DIRECT, eevee_deferred_direct_frag);
SHADER_FULLSCREEN(DEFERRED_EVAL_HOLDOUT, eevee_deferred_holdout_frag);
SHADER_FULLSCREEN(DEFERRED_EVAL_TRANSPARENT, eevee_deferred_transparent_frag);
SHADER_FULLSCREEN(DEFERRED_EVAL_VOLUME, eevee_deferred_volume_frag);
SHADER(DEFERRED_MESH, eevee_surface_mesh_vert, nullptr, eevee_surface_deferred_frag, nullptr);
SHADER(DEFERRED_VOLUME, eevee_volume_vert, nullptr, eevee_volume_deferred_frag, nullptr);
SHADER_FULLSCREEN(DOF_BOKEH_LUT, eevee_depth_of_field_bokeh_lut_frag);
SHADER_FULLSCREEN(DOF_FILTER, eevee_depth_of_field_filter_frag);
SHADER_FULLSCREEN_DEFINES(DOF_GATHER_BACKGROUND_LUT,
eevee_depth_of_field_gather_frag,
"#define DOF_FOREGROUND_PASS false\n"
"#define DOF_BOKEH_TEXTURE true\n");
SHADER_FULLSCREEN_DEFINES(DOF_GATHER_BACKGROUND,
eevee_depth_of_field_gather_frag,
"#define DOF_FOREGROUND_PASS false\n"
"#define DOF_BOKEH_TEXTURE false\n");
SHADER_FULLSCREEN_DEFINES(DOF_GATHER_FOREGROUND_LUT,
eevee_depth_of_field_gather_frag,
"#define DOF_FOREGROUND_PASS true\n"
"#define DOF_BOKEH_TEXTURE true\n");
SHADER_FULLSCREEN_DEFINES(DOF_GATHER_FOREGROUND,
eevee_depth_of_field_gather_frag,
"#define DOF_FOREGROUND_PASS true\n"
"#define DOF_BOKEH_TEXTURE false\n");
SHADER_FULLSCREEN_DEFINES(DOF_GATHER_HOLEFILL,
eevee_depth_of_field_gather_holefill_frag,
"#define DOF_HOLEFILL_PASS true\n"
"#define DOF_FOREGROUND_PASS false\n"
"#define DOF_BOKEH_TEXTURE false\n");
SHADER_FULLSCREEN(DOF_REDUCE_COPY, eevee_depth_of_field_reduce_copy_frag);
SHADER_FULLSCREEN(DOF_REDUCE_DOWNSAMPLE, eevee_depth_of_field_reduce_downsample_frag);
SHADER_FULLSCREEN(DOF_REDUCE_RECURSIVE, eevee_depth_of_field_reduce_recursive_frag);
SHADER_FULLSCREEN_DEFINES(DOF_RESOLVE_LUT,
eevee_depth_of_field_resolve_frag,
"#define DOF_RESOLVE_PASS true\n"
"#define DOF_BOKEH_TEXTURE true\n"
"#define DOF_SLIGHT_FOCUS_DENSITY 2\n");
SHADER_FULLSCREEN_DEFINES(DOF_RESOLVE_LUT_HQ,
eevee_depth_of_field_resolve_frag,
"#define DOF_RESOLVE_PASS true\n"
"#define DOF_BOKEH_TEXTURE true\n"
"#define DOF_SLIGHT_FOCUS_DENSITY 4\n");
SHADER_FULLSCREEN_DEFINES(DOF_RESOLVE,
eevee_depth_of_field_resolve_frag,
"#define DOF_RESOLVE_PASS true\n"
"#define DOF_BOKEH_TEXTURE false\n"
"#define DOF_SLIGHT_FOCUS_DENSITY 2\n");
SHADER_FULLSCREEN_DEFINES(DOF_RESOLVE_HQ,
eevee_depth_of_field_resolve_frag,
"#define DOF_RESOLVE_PASS true\n"
"#define DOF_BOKEH_TEXTURE false\n"
"#define DOF_SLIGHT_FOCUS_DENSITY 4\n");
SHADER(DOF_SCATTER_BACKGROUND_LUT,
eevee_depth_of_field_scatter_vert,
nullptr,
eevee_depth_of_field_scatter_frag,
"#define DOF_FOREGROUND_PASS false\n"
"#define DOF_BOKEH_TEXTURE true\n");
SHADER(DOF_SCATTER_BACKGROUND,
eevee_depth_of_field_scatter_vert,
nullptr,
eevee_depth_of_field_scatter_frag,
"#define DOF_FOREGROUND_PASS false\n"
"#define DOF_BOKEH_TEXTURE false\n");
SHADER(DOF_SCATTER_FOREGROUND_LUT,
eevee_depth_of_field_scatter_vert,
nullptr,
eevee_depth_of_field_scatter_frag,
"#define DOF_FOREGROUND_PASS true\n"
"#define DOF_BOKEH_TEXTURE true\n");
SHADER(DOF_SCATTER_FOREGROUND,
eevee_depth_of_field_scatter_vert,
nullptr,
eevee_depth_of_field_scatter_frag,
"#define DOF_FOREGROUND_PASS true\n"
"#define DOF_BOKEH_TEXTURE false\n");
SHADER_FULLSCREEN(DOF_SETUP, eevee_depth_of_field_setup_frag);
SHADER_FULLSCREEN_DEFINES(DOF_TILES_DILATE_MINABS,
eevee_depth_of_field_tiles_dilate_frag,
"#define DILATE_MODE_MIN_MAX false\n");
SHADER_FULLSCREEN_DEFINES(DOF_TILES_DILATE_MINMAX,
eevee_depth_of_field_tiles_dilate_frag,
"#define DILATE_MODE_MIN_MAX true\n");
SHADER_FULLSCREEN(DOF_TILES_FLATTEN, eevee_depth_of_field_tiles_flatten_frag);
SHADER(LIGHTPROBE_DISPLAY_CUBEMAP,
eevee_lightprobe_display_cubemap_vert,
nullptr,
eevee_lightprobe_display_cubemap_frag,
nullptr);
SHADER(LIGHTPROBE_DISPLAY_IRRADIANCE,
eevee_lightprobe_display_grid_vert,
nullptr,
eevee_lightprobe_display_grid_frag,
nullptr);
SHADER(LIGHTPROBE_FILTER_DOWNSAMPLE_CUBE,
eevee_lightprobe_filter_vert,
eevee_lightprobe_filter_geom,
eevee_lightprobe_filter_downsample_frag,
"#define CUBEMAP\n");
SHADER(LIGHTPROBE_FILTER_GLOSSY,
eevee_lightprobe_filter_vert,
eevee_lightprobe_filter_geom,
eevee_lightprobe_filter_glossy_frag,
"#define CUBEMAP\n");
SHADER(LIGHTPROBE_FILTER_DIFFUSE,
eevee_lightprobe_filter_vert,
eevee_lightprobe_filter_geom,
eevee_lightprobe_filter_diffuse_frag,
nullptr);
SHADER(LIGHTPROBE_FILTER_VISIBILITY,
eevee_lightprobe_filter_vert,
eevee_lightprobe_filter_geom,
eevee_lightprobe_filter_visibility_frag,
nullptr);
SHADER_FULLSCREEN(LOOKDEV_BACKGROUND, eevee_lookdev_background_frag);
SHADER_FULLSCREEN(MOTION_BLUR_GATHER, eevee_motion_blur_gather_frag);
SHADER_FULLSCREEN(MOTION_BLUR_TILE_DILATE, eevee_motion_blur_tiles_dilate_frag);
SHADER_FULLSCREEN(MOTION_BLUR_TILE_FLATTEN, eevee_motion_blur_tiles_flatten_frag);
SHADER_FULLSCREEN(SHADOW_CLEAR, eevee_depth_clear_frag);
SHADER(VELOCITY_MESH,
eevee_surface_velocity_mesh_vert,
nullptr,
eevee_surface_velocity_frag,
nullptr);
SHADER_FULLSCREEN(VELOCITY_CAMERA, eevee_camera_velocity_frag);
#undef SHADER
#undef SHADER_FULLSCREEN
#ifdef DEBUG
/* Ensure all shader are described. */
for (ShaderDescription &desc : shader_descriptions_) {
BLI_assert(desc.name != nullptr);
BLI_assert(desc.vertex_shader_code != nullptr);
BLI_assert(desc.fragment_shader_code != nullptr);
}
#endif
}
ShaderModule::~ShaderModule()
{
for (GPUShader *&shader : shaders_) {
DRW_SHADER_FREE_SAFE(shader);
}
DRW_SHADER_LIB_FREE_SAFE(shader_lib_);
}
GPUShader *ShaderModule::static_shader_get(eShaderType shader_type)
{
if (shaders_[shader_type] == nullptr) {
ShaderDescription &desc = shader_descriptions_[shader_type];
shaders_[shader_type] = DRW_shader_create_with_shaderlib_ex(desc.vertex_shader_code,
desc.geometry_shader_code,
desc.fragment_shader_code,
shader_lib_,
desc.defines_shader_code,
desc.name);
if (shaders_[shader_type] == nullptr) {
fprintf(stderr, "EEVEE: error: Could not compile static shader \"%s\"\n", desc.name);
}
BLI_assert(shaders_[shader_type] != nullptr);
}
return shaders_[shader_type];
}
/* Run some custom preprocessor shader rewrite and returns a new string. */
std::string ShaderModule::enum_preprocess(const char *input)
{
std::string output = "";
/* Not failure safe but this only runs on static data. */
const char *cursor = input;
while ((cursor = strstr(cursor, "enum "))) {
output += StringRef(input, cursor - input);
/* Skip "enum" keyword. */
cursor = strstr(cursor, " ");
const char *enum_name = cursor;
cursor = strstr(cursor, " :");
output += "#define " + StringRef(enum_name, cursor - enum_name) + " uint\n";
output += "const uint ";
const char *enum_values = strstr(cursor, "{") + 1;
cursor = strstr(cursor, "}");
output += StringRef(enum_values, cursor - enum_values);
if (cursor != nullptr) {
/* Skip the curly bracket but not the semicolon. */
input = cursor + 1;
}
else {
input = nullptr;
}
}
if (input != nullptr) {
output += input;
}
return output;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name GPU Materials
*
* \{ */
char *ShaderModule::material_shader_code_defs_get(eMaterialGeometry geometry_type)
{
std::string output = "";
switch (geometry_type) {
case MAT_GEOM_HAIR:
output += "#define MAT_GEOM_HAIR\n";
break;
case MAT_GEOM_GPENCIL:
output += "#define MAT_GEOM_GPENCIL\n";
output += "#define UNIFORM_RESOURCE_ID\n";
break;
default:
break;
}
return BLI_strdup(output.c_str());
}
char *ShaderModule::material_shader_code_vert_get(const GPUCodegenOutput *codegen,
GPUMaterial *mat,
eMaterialGeometry geometry_type)
{
std::string output = "\n\n";
/* Might be needed by attr_load_orco. */
if (GPU_material_flag_get(mat, GPU_MATFLAG_OBJECT_INFO)) {
output += "#pragma BLENDER_REQUIRE(common_obinfos_lib.glsl)\n";
}
if (codegen->attribs_interface) {
/* Declare inputs. */
std::string delimiter = ";\n";
std::string sub(codegen->attribs_declare);
size_t start, pos = 0;
while ((pos = sub.find(delimiter)) != std::string::npos) {
switch (geometry_type) {
case MAT_GEOM_MESH:
/* Example print:
* in vec2 u015684; */
output += "in ";
output += sub.substr(0, pos + delimiter.length());
break;
case MAT_GEOM_HAIR:
/* Example print:
* uniform samplerBuffer u015684; */
output += "uniform samplerBuffer ";
start = sub.find(" ") + 1;
output += sub.substr(start, pos + delimiter.length() - start);
break;
case MAT_GEOM_GPENCIL:
/* Example print:
* vec2 u015684;
* These are not used and just here to make the attribs_load functions call valids.
* Only one uv and one color attribute layer is supported by gpencil objects. */
output += sub.substr(0, pos + delimiter.length());
break;
case MAT_GEOM_WORLD:
case MAT_GEOM_VOLUME:
case MAT_GEOM_LOOKDEV:
/* Not supported. */
break;
}
sub.erase(0, pos + delimiter.length());
}
output += "\n";
if (geometry_type != MAT_GEOM_WORLD) {
output += "IN_OUT AttributesInterface\n";
output += "{\n";
output += codegen->attribs_interface;
output += "};\n\n";
}
}
output += "void attrib_load(void)\n";
output += "{\n";
if (codegen->attribs_load && geometry_type != MAT_GEOM_WORLD) {
output += codegen->attribs_load;
}
output += "}\n\n";
if (ELEM(geometry_type, MAT_GEOM_MESH, MAT_GEOM_HAIR)) {
if (codegen->displacement) {
if (GPU_material_flag_get(mat, GPU_MATFLAG_UNIFORMS_ATTRIB)) {
output += datatoc_common_uniform_attribute_lib_glsl;
}
output += codegen->uniforms;
output += "\n";
output += codegen->library;
output += "\n";
}
output += "vec3 nodetree_displacement(void)\n";
output += "{\n";
if (codegen->displacement) {
output += codegen->displacement;
}
else {
output += "return vec3(0);\n";
}
output += "}\n\n";
}
switch (geometry_type) {
case MAT_GEOM_WORLD:
output += datatoc_eevee_surface_world_vert_glsl;
break;
case MAT_GEOM_VOLUME:
output += datatoc_eevee_volume_vert_glsl;
break;
case MAT_GEOM_GPENCIL:
output += datatoc_eevee_surface_gpencil_vert_glsl;
break;
case MAT_GEOM_LOOKDEV:
output += datatoc_eevee_surface_lookdev_vert_glsl;
break;
case MAT_GEOM_HAIR:
output += datatoc_eevee_surface_hair_vert_glsl;
break;
case MAT_GEOM_MESH:
default:
output += datatoc_eevee_surface_mesh_vert_glsl;
break;
}
return DRW_shader_library_create_shader_string(shader_lib_, output.c_str());
}
char *ShaderModule::material_shader_code_geom_get(const GPUCodegenOutput *codegen,
GPUMaterial *mat,
eMaterialGeometry geometry_type)
{
/* Force geometry usage if GPU_BARYCENTRIC_DIST is used. */
if (!GPU_material_flag_get(mat, GPU_MATFLAG_BARYCENTRIC) ||
!ELEM(geometry_type, MAT_GEOM_GPENCIL, MAT_GEOM_MESH)) {
return nullptr;
}
StringRefNull interp_lib(datatoc_eevee_surface_lib_glsl);
int64_t start = interp_lib.find("SurfaceInterface");
int64_t end = interp_lib.find("interp");
StringRef interp_lib_stripped = interp_lib.substr(start, end - start);
std::string output = "\n\n";
if (codegen->attribs_interface) {
output += "in AttributesInterface\n";
output += "{\n";
output += codegen->attribs_interface;
output += "} attr_in[];\n\n";
output += "out AttributesInterface\n";
output += "{\n";
output += codegen->attribs_interface;
output += "} attr_out;\n\n";
}
output += "in ";
output += interp_lib_stripped;
output += "interp_in[];\n\n";
output += "out ";
output += interp_lib_stripped;
output += "interp_out;\n\n";
output += datatoc_eevee_surface_mesh_geom_glsl;
output += "void main(void)\n";
output += "{\n";
output += "interp_out.barycentric_dists = calc_barycentric_distances(";
output += " interp_in[0].P, interp_in[1].P, interp_in[2].P);\n ";
for (int i = 0; i < 3; i++) {
output += "{\n";
output += "const int vert_id = " + std::to_string(i) + ";\n";
output += "interp_out.barycentric_coords = calc_barycentric_co(vert_id);";
output += "gl_Position = gl_in[vert_id].gl_Position;";
if (codegen->attribs_passthrough) {
output += codegen->attribs_passthrough;
}
output += "EmitVertex();";
output += "}\n";
}
output += "}\n";
return DRW_shader_library_create_shader_string(shader_lib_, output.c_str());
}
char *ShaderModule::material_shader_code_frag_get(const GPUCodegenOutput *codegen,
GPUMaterial *gpumat,
eMaterialGeometry geometry_type,
eMaterialPipeline pipeline_type)
{
std::string output = "\n\n";
/* World material loads attribs in fragment shader (only used for orco). */
if (geometry_type == MAT_GEOM_WORLD) {
if (codegen->attribs_interface) {
/* Declare inputs. */
std::string delimiter = ";\n";
std::string sub(codegen->attribs_declare);
size_t pos = 0;
while ((pos = sub.find(delimiter)) != std::string::npos) {
/* Example print:
* vec2 u015684;
* These are not used and just here to make the attribs_load functions call valids.
* Only orco layer is supported by world. */
output += sub.substr(0, pos + delimiter.length());
sub.erase(0, pos + delimiter.length());
}
output += "\n";
output += codegen->attribs_interface;
output += "\n";
}
output += "void attrib_load(void)\n";
output += "{\n";
if (codegen->attribs_interface) {
output += codegen->attribs_load;
}
output += "}\n\n";
}
else {
if (codegen->attribs_interface) {
output += "IN_OUT AttributesInterface\n";
output += "{\n";
output += codegen->attribs_interface;
output += "};\n\n";
}
}
if (codegen->surface || codegen->volume) {
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_UNIFORMS_ATTRIB)) {
output += datatoc_common_uniform_attribute_lib_glsl;
}
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_OBJECT_INFO)) {
output += "#pragma BLENDER_REQUIRE(common_obinfos_lib.glsl)\n";
}
output += codegen->uniforms;
output += "\n";
output += codegen->library;
output += "\n";
}
output += "Closure nodetree_surface(void)\n";
output += "{\n";
if (codegen->surface) {
output += codegen->surface;
}
else {
output += "return CLOSURE_DEFAULT;\n";
}
output += "}\n\n";
output += "Closure nodetree_volume(void)\n";
output += "{\n";
if (codegen->volume) {
output += codegen->volume;
}
else {
output += "return CLOSURE_DEFAULT;\n";
}
output += "}\n\n";
switch (geometry_type) {
case MAT_GEOM_WORLD:
output += datatoc_eevee_surface_background_frag_glsl;
break;
case MAT_GEOM_VOLUME:
switch (pipeline_type) {
case MAT_PIPE_DEFERRED:
output += datatoc_eevee_volume_deferred_frag_glsl;
break;
default:
BLI_assert(0);
break;
}
break;
default:
switch (pipeline_type) {
case MAT_PIPE_FORWARD_PREPASS:
output += datatoc_eevee_surface_depth_simple_frag_glsl;
break;
case MAT_PIPE_DEFERRED_PREPASS:
case MAT_PIPE_SHADOW:
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
output += datatoc_eevee_surface_depth_frag_glsl;
}
else {
output += datatoc_eevee_surface_depth_simple_frag_glsl;
}
break;
case MAT_PIPE_DEFERRED:
output += datatoc_eevee_surface_deferred_frag_glsl;
break;
case MAT_PIPE_FORWARD:
output += datatoc_eevee_surface_forward_frag_glsl;
break;
default:
BLI_assert(0);
break;
}
break;
}
return DRW_shader_library_create_shader_string(shader_lib_, output.c_str());
}
/* WATCH: This can be called from another thread! Needs to not touch the shader module in any
* thread unsafe manner. */
GPUShaderSource ShaderModule::material_shader_code_generate(GPUMaterial *mat,
const GPUCodegenOutput *codegen)
{
uint64_t shader_uuid = GPU_material_uuid_get(mat);
eMaterialPipeline pipeline_type;
eMaterialGeometry geometry_type;
material_type_from_shader_uuid(shader_uuid, pipeline_type, geometry_type);
GPUShaderSource source;
source.vertex = material_shader_code_vert_get(codegen, mat, geometry_type);
source.fragment = material_shader_code_frag_get(codegen, mat, geometry_type, pipeline_type);
source.geometry = material_shader_code_geom_get(codegen, mat, geometry_type);
source.defines = material_shader_code_defs_get(geometry_type);
return source;
}
static GPUShaderSource codegen_callback(void *thunk,
GPUMaterial *mat,
const GPUCodegenOutput *codegen)
{
return ((ShaderModule *)thunk)->material_shader_code_generate(mat, codegen);
}
GPUMaterial *ShaderModule::material_shader_get(::Material *blender_mat,
struct bNodeTree *nodetree,
eMaterialPipeline pipeline_type,
eMaterialGeometry geometry_type,
bool deferred_compilation)
{
uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type);
bool is_volume = (pipeline_type == MAT_PIPE_VOLUME);
return DRW_shader_from_material(
blender_mat, nodetree, shader_uuid, is_volume, deferred_compilation, codegen_callback, this);
}
GPUMaterial *ShaderModule::world_shader_get(::World *blender_world, struct bNodeTree *nodetree)
{
eMaterialPipeline pipeline_type = MAT_PIPE_DEFERRED; /* Unused. */
eMaterialGeometry geometry_type = MAT_GEOM_WORLD;
uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type);
bool is_volume = (pipeline_type == MAT_PIPE_VOLUME);
bool deferred_compilation = false;
return DRW_shader_from_world(blender_world,
nodetree,
shader_uuid,
is_volume,
deferred_compilation,
codegen_callback,
this);
}
/* Variation to compile a material only with a nodetree. Caller needs to maintain the list of
* materials and call GPU_material_free on it to update the material. */
GPUMaterial *ShaderModule::material_shader_get(const char *name,
ListBase &materials,
struct bNodeTree *nodetree,
eMaterialPipeline pipeline_type,
eMaterialGeometry geometry_type,
bool is_lookdev)
{
uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type);
bool is_volume = (pipeline_type == MAT_PIPE_VOLUME);
GPUMaterial *gpumat = GPU_material_from_nodetree(nullptr,
nullptr,
nodetree,
&materials,
name,
shader_uuid,
is_volume,
is_lookdev,
codegen_callback,
this);
GPU_material_status_set(gpumat, GPU_MAT_QUEUED);
GPU_material_compile(gpumat);
return gpumat;
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,157 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* Shader module that manage shader libraries, deferred compilation,
* and static shader usage.
*/
#pragma once
#include <array>
#include <string>
#include "BLI_string_ref.hh"
#include "DRW_render.h"
#include "GPU_material.h"
#include "GPU_shader.h"
#include "eevee_id_map.hh"
namespace blender::eevee {
/* Keep alphabetical order and clean prefix. */
enum eShaderType {
CULLING_DEBUG = 0,
CULLING_LIGHT,
DEFERRED_EVAL_DIRECT,
DEFERRED_EVAL_HOLDOUT,
DEFERRED_EVAL_TRANSPARENT,
DEFERRED_EVAL_VOLUME,
DEFERRED_MESH,
DEFERRED_VOLUME,
DOF_BOKEH_LUT,
DOF_GATHER_BACKGROUND_LUT,
DOF_GATHER_BACKGROUND,
DOF_FILTER,
DOF_GATHER_FOREGROUND_LUT,
DOF_GATHER_FOREGROUND,
DOF_GATHER_HOLEFILL,
DOF_REDUCE_COPY,
DOF_REDUCE_DOWNSAMPLE,
DOF_REDUCE_RECURSIVE,
DOF_RESOLVE,
DOF_RESOLVE_HQ,
DOF_RESOLVE_LUT,
DOF_RESOLVE_LUT_HQ,
DOF_SCATTER_BACKGROUND_LUT,
DOF_SCATTER_BACKGROUND,
DOF_SCATTER_FOREGROUND_LUT,
DOF_SCATTER_FOREGROUND,
DOF_SETUP,
DOF_TILES_DILATE_MINABS,
DOF_TILES_DILATE_MINMAX,
DOF_TILES_FLATTEN,
FILM_FILTER,
FILM_RESOLVE,
FILM_RESOLVE_DEPTH,
LIGHTPROBE_DISPLAY_CUBEMAP,
LIGHTPROBE_DISPLAY_IRRADIANCE,
LIGHTPROBE_FILTER_DOWNSAMPLE_CUBE,
LIGHTPROBE_FILTER_GLOSSY,
LIGHTPROBE_FILTER_DIFFUSE,
LIGHTPROBE_FILTER_VISIBILITY,
LOOKDEV_BACKGROUND,
MOTION_BLUR_GATHER,
MOTION_BLUR_TILE_DILATE,
MOTION_BLUR_TILE_FLATTEN,
SHADOW_CLEAR,
VELOCITY_CAMERA,
VELOCITY_MESH,
MAX_SHADER_TYPE,
};
/**
* Shader module. shared between instances.
*/
class ShaderModule {
private:
struct ShaderDescription {
const char *name;
const char *vertex_shader_code;
const char *geometry_shader_code;
const char *fragment_shader_code;
const char *defines_shader_code;
};
DRWShaderLibrary *shader_lib_ = nullptr;
std::array<GPUShader *, MAX_SHADER_TYPE> shaders_;
std::array<ShaderDescription, MAX_SHADER_TYPE> shader_descriptions_;
std::string shared_lib_;
public:
ShaderModule();
~ShaderModule();
GPUShader *static_shader_get(eShaderType shader_type);
GPUMaterial *material_shader_get(::Material *blender_mat,
struct bNodeTree *nodetree,
eMaterialPipeline pipeline_type,
eMaterialGeometry geometry_type,
bool deferred_compilation);
GPUMaterial *world_shader_get(::World *blender_world, struct bNodeTree *nodetree);
GPUMaterial *material_shader_get(const char *name,
ListBase &materials,
struct bNodeTree *nodetree,
eMaterialPipeline pipeline_type,
eMaterialGeometry geometry_type,
bool is_lookdev);
GPUShaderSource material_shader_code_generate(GPUMaterial *mat, const GPUCodegenOutput *codegen);
private:
/* Run some custom preprocessor shader rewrite and returns a new string. */
std::string enum_preprocess(const char *input);
char *material_shader_code_defs_get(eMaterialGeometry geometry_type);
char *material_shader_code_vert_get(const GPUCodegenOutput *codegen,
GPUMaterial *mat,
eMaterialGeometry geometry_type);
char *material_shader_code_geom_get(const GPUCodegenOutput *codegen,
GPUMaterial *mat,
eMaterialGeometry geometry_type);
char *material_shader_code_frag_get(const GPUCodegenOutput *codegen,
GPUMaterial *mat,
eMaterialGeometry geometry_type,
eMaterialPipeline pipeline_type);
};
} // namespace blender::eevee

View File

@@ -0,0 +1,679 @@
/**
* Shared structures, enums & defines between C++ and GLSL.
* Can also include some math functions but they need to be simple enough to be valid in both
* language.
*/
/**
* NOTE: Enum support is not part of GLSL. It is handled by our own pre-processor pass in
* EEVEE's shader module.
*
* IMPORTANT:
* - Don't add trailing comma at the end of the enum. Our custom pre-processor will noy trim it
* for GLSL.
* - Always use `u` suffix for values. GLSL do not support implicit cast.
* - Define all values. This is in order to simplify custom pre-processor code.
* - Always use uint32_t as underlying type.
* - Use float suffix by default for float literals to avoid double promotion in C++.
* - Pack one float or int after a vec3/ivec3 to fullfil alligment rules.
*
* NOTE: Due to alignment restriction and buggy drivers, do not try to use mat3 inside structs.
* Do not use arrays of float. They are padded to arrays of vec4 and are not worth it.
*
* IMPORTANT: Don't forget to align mat4 and vec4 to 16 bytes.
**/
#ifndef __cplusplus /* GLSL */
# pragma BLENDER_REQUIRE(common_math_lib.glsl)
# define BLI_STATIC_ASSERT_ALIGN(type_, align_)
# define BLI_STATIC_ASSERT_SIZE(type_, size_)
# define static
# define inline
# define cosf cos
# define sinf sin
# define tanf tan
# define acosf acos
# define asinf asin
# define atanf atan
# define floorf floor
# define ceilf ceil
# define sqrtf sqrt
#else /* C++ */
# pragma once
# include "BLI_float2.hh"
# include "BLI_float3.hh"
# include "BLI_float4x4.hh"
# include "BLI_int2.hh"
# include "BLI_int3.hh"
typedef float mat4[4][4];
typedef float vec4[4];
using vec3 = blender::float3;
using vec2 = blender::float2;
using ivec3 = blender::int3;
using ivec2 = blender::int2;
typedef uint uvec4[4];
typedef uint uvec3[3];
typedef uint uvec2[2];
typedef int bvec4[4];
typedef int bvec2[2];
/* Ugly but it does the job! */
# define bool int
# include "eevee_wrapper.hh"
namespace blender::eevee {
#endif
#define UBO_MIN_MAX_SUPPORTED_SIZE 1 << 14
/* -------------------------------------------------------------------- */
/** \name Sampling
* \{ */
enum eSamplingDimension : uint32_t {
SAMPLING_FILTER_U = 0u,
SAMPLING_FILTER_V = 1u,
SAMPLING_LENS_U = 2u,
SAMPLING_LENS_V = 3u,
SAMPLING_TIME = 4u,
SAMPLING_SHADOW_U = 5u,
SAMPLING_SHADOW_V = 6u,
SAMPLING_SHADOW_W = 7u,
SAMPLING_SHADOW_X = 8u,
SAMPLING_SHADOW_Y = 9u,
SAMPLING_CLOSURE = 10u,
SAMPLING_LIGHTPROBE = 11u,
SAMPLING_TRANSPARENCY = 12u
};
/** IMPORTANT: Make sure the array can contain all sampling dimensions. */
#define SAMPLING_DIMENSION_COUNT 13
struct SamplingData {
/** Array containing random values from Low Discrepency Sequence in [0..1) range. */
/** HACK: float arrays are padded to vec4 in GLSL. Using vec4 for now to get the same alignment
* but this is wasteful. */
vec4 dimensions[SAMPLING_DIMENSION_COUNT];
};
BLI_STATIC_ASSERT_ALIGN(SamplingData, 16)
/* Returns total sample count in a web pattern of the given size. */
static inline int web_sample_count_get(int web_density, int ring_count)
{
return ((ring_count * ring_count + ring_count) / 2) * web_density + 1;
}
/* Returns lowest possible ring count that contains at least sample_count samples. */
static inline int web_ring_count_get(int web_density, int sample_count)
{
/* Inversion of web_sample_count_get(). */
float x = 2.0f * (float(sample_count) - 1.0f) / float(web_density);
/* Solving polynomial. We only search positive solution. */
float discriminant = 1.0f + 4.0f * x;
return int(ceilf(0.5f * (sqrtf(discriminant) - 1.0f)));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Camera
* \{ */
enum eCameraType : uint32_t {
CAMERA_PERSP = 0u,
CAMERA_ORTHO = 1u,
CAMERA_PANO_EQUIRECT = 2u,
CAMERA_PANO_EQUISOLID = 3u,
CAMERA_PANO_EQUIDISTANT = 4u,
CAMERA_PANO_MIRROR = 5u
};
static inline bool is_panoramic(eCameraType type)
{
return type > CAMERA_ORTHO;
}
struct CameraData {
/* View Matrices of the camera, not from any view! */
mat4 persmat;
mat4 persinv;
mat4 viewmat;
mat4 viewinv;
mat4 winmat;
mat4 wininv;
/** Camera UV scale and bias. Also known as viewcamtexcofac. */
vec2 uv_scale;
vec2 uv_bias;
/** Panorama parameters. */
vec2 equirect_scale;
vec2 equirect_scale_inv;
vec2 equirect_bias;
float fisheye_fov;
float fisheye_lens;
/** Clipping distances. */
float clip_near;
float clip_far;
/** Film pixel filter radius. */
float filter_size;
eCameraType type;
};
BLI_STATIC_ASSERT_ALIGN(CameraData, 16)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Film
* \{ */
enum eFilmDataType : uint32_t {
/** Color is accumulated using the pixel filter. No negative values. */
FILM_DATA_COLOR = 0u,
/** Variant where we accumulate using pre-exposed values and log space. */
FILM_DATA_COLOR_LOG = 1u,
/** Non-Color will be accumulated using nearest filter. All values are allowed. */
FILM_DATA_FLOAT = 2u,
FILM_DATA_VEC2 = 3u,
/** No VEC3 because GPU_RGB16F is not a renderable format. */
FILM_DATA_VEC4 = 4u,
FILM_DATA_NORMAL = 5u,
FILM_DATA_DEPTH = 6u,
FILM_DATA_MOTION = 7u
};
struct FilmData {
/** Size of the render target in pixels. */
ivec2 extent;
/** Offset of the render target in the full-res frame, in pixels. */
ivec2 offset;
/** Scale and bias to filter only a region of the render (aka. render_border). */
vec2 uv_bias;
vec2 uv_scale;
vec2 uv_scale_inv;
/** Data type stored by this film. */
eFilmDataType data_type;
/** Is true if history is valid and can be sampled. Bypassing history to resets accumulation. */
bool use_history;
/** Used for fade-in effect. */
float opacity;
/** Padding to sizeof(vec4). */
int _pad0, _pad1, _pad2;
};
BLI_STATIC_ASSERT_ALIGN(FilmData, 16)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Depth of field
* \{ */
/* 5% error threshold. */
#define DOF_FAST_GATHER_COC_ERROR 0.05
#define DOF_GATHER_RING_COUNT 5
#define DOF_DILATE_RING_COUNT 3
#define DOF_TILE_DIVISOR 16
#define DOF_BOKEH_LUT_SIZE 32
struct DepthOfFieldData {
/** Size of the render targets for gather & scatter passes. */
ivec2 extent;
/** Size of a pixel in uv space (1.0 / extent). */
vec2 texel_size;
/** Bokeh Scale factor. */
vec2 bokeh_anisotropic_scale;
vec2 bokeh_anisotropic_scale_inv;
/* Correction factor to align main target pixels with the filtered mipmap chain texture. */
vec2 gather_uv_fac;
/** Scatter parameters. */
float scatter_coc_threshold;
float scatter_color_threshold;
float scatter_neighbor_max_color;
int scatter_sprite_per_row;
/** Downsampling paramters. */
float denoise_factor;
/** Bokeh Shape parameters. */
float bokeh_blades;
float bokeh_rotation;
float bokeh_aperture_ratio;
/** Circle of confusion (CoC) parameters. */
float coc_mul;
float coc_bias;
float coc_abs_max;
/** Copy of camera type. */
eCameraType camera_type;
int _pad0, _pad1;
};
BLI_STATIC_ASSERT_ALIGN(DepthOfFieldData, 16)
static inline float coc_radius_from_camera_depth(DepthOfFieldData dof, float depth)
{
depth = (dof.camera_type != CAMERA_ORTHO) ? 1.0f / depth : depth;
return dof.coc_mul * depth + dof.coc_bias;
}
static inline float regular_polygon_side_length(float sides_count)
{
return 2.0f * sinf(M_PI / sides_count);
}
/* Returns intersection ratio between the radius edge at theta and the regular polygon edge.
* Start first corners at theta == 0. */
static inline 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 = (2.0f * M_PI) / sides_count;
return cosf(side_angle * 0.5f) /
cosf(theta - side_angle * floorf((sides_count * theta + M_PI) / (2.0f * M_PI)));
}
/* Remap input angle to have homogenous spacing of points along a polygon edge.
* Expects theta to be in [0..2pi] range. */
static inline float circle_to_polygon_angle(float sides_count, float theta)
{
float side_angle = (2.0f * M_PI) / sides_count;
float halfside_angle = side_angle * 0.5f;
float side = floorf(theta / side_angle);
/* Length of segment from center to the middle of polygon side. */
float adjacent = circle_to_polygon_radius(sides_count, 0.0f);
/* 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 = regular_polygon_side_length(sides_count) * 0.5f;
float opposite = ratio * halfside_len;
/* NOTE: atan(y_over_x) has output range [-M_PI_2..M_PI_2]. */
float final_local_theta = atanf(opposite / adjacent);
return side * side_angle + final_local_theta;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name VelocityModule
* \{ */
struct VelocityObjectData {
mat4 next_object_mat;
mat4 prev_object_mat;
};
BLI_STATIC_ASSERT_ALIGN(VelocityObjectData, 16)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Motion Blur
* \{ */
#define MB_TILE_DIVISOR 32
struct MotionBlurData {
/** Motion vector lengths are clamped to this maximum. A value of 0 means effect is bypassed. */
float blur_max;
/** Depth scaling factor. Avoid bluring background behind moving objects. */
float depth_scale;
/** As the name suggests. Used to avoid a division in the sampling. */
vec2 target_size_inv;
/** Viewport motion blur only blurs using previous frame vectors. */
bool is_viewport;
int _pad0, _pad1, _pad2;
};
BLI_STATIC_ASSERT_ALIGN(MotionBlurData, 16)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Cullings
* \{ */
/* Number of items in a culling batch. Needs to be Power of 2. */
#define CULLING_ITEM_BATCH 128
/* Maximum number of 32 bit uint stored per tile. */
#define CULLING_MAX_WORD ((CULLING_ITEM_BATCH + 1) / 32)
/* TODO(fclem) Support more than 4 words using layered texture for culling result. */
#if CULLING_MAX_WORD > 4
# error "CULLING_MAX_WORD is greater than supported maximum."
#endif
/* Fine grained subdivision in the Z direction. */
#define CULLING_ZBIN_COUNT 4088
struct CullingData {
/* Linearly distributed z-bins with encoded uint16_t min and max index. */
/* NOTE: due to alignment restrictions of uint arrays, use uvec4. */
uvec4 zbins[CULLING_ZBIN_COUNT / 4];
/* Extent of one square tile in pixels. */
int tile_size;
/* Valid item count in the data array. */
uint items_count;
/* Scale and bias applied to linear Z to get zbin. */
float zbin_scale;
float zbin_bias;
/* Scale applied to tile pixel coordinates to get target UV coordinate. */
vec2 tile_to_uv_fac;
vec2 _pad0;
};
BLI_STATIC_ASSERT_ALIGN(CullingData, 16)
BLI_STATIC_ASSERT_SIZE(CullingData, UBO_MIN_MAX_SUPPORTED_SIZE)
static inline int culling_z_to_zbin(CullingData data, float z)
{
return int(z * data.zbin_scale + data.zbin_bias);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Lights
* \{ */
#define LIGHT_NO_SHADOW -1
enum eLightType : uint32_t {
LIGHT_SUN = 0u,
LIGHT_POINT = 1u,
LIGHT_SPOT = 2u,
LIGHT_RECT = 3u,
LIGHT_ELLIPSE = 4u
};
static inline bool is_area_light(eLightType type)
{
return type >= LIGHT_RECT;
}
struct LightData {
/** Normalized obmat. Last column contains data accessible using the following macros. */
mat4 object_mat;
/** Packed data in the last column of the object_mat. */
#define _area_size_x object_mat[0][3]
#define _area_size_y object_mat[1][3]
#define _radius _area_size_x
#define _spot_mul object_mat[2][3]
#define _spot_bias object_mat[3][3]
/** Aliases for axes. */
#ifdef __cplusplus
# define _right object_mat[0]
# define _up object_mat[1]
# define _back object_mat[2]
# define _position object_mat[3]
#else
# define _right object_mat[0].xyz
# define _up object_mat[1].xyz
# define _back object_mat[2].xyz
# define _position object_mat[3].xyz
#endif
/** Influence radius (inversed and squared) adjusted for Surface / Volume power. */
float influence_radius_invsqr_surface;
float influence_radius_invsqr_volume;
/** Maximum influence radius. Used for culling. */
float influence_radius_max;
/** Offset in the shadow struct table. -1 means no shadow. */
int shadow_id;
/** NOTE: It is ok to use vec3 here. A float is declared right after it.
* vec3 is also aligned to 16 bytes. */
vec3 color;
/** Power depending on shader type. */
float diffuse_power;
float specular_power;
float volume_power;
/** Special radius factor for point lighting. */
float radius_squared;
/** Light Type. */
eLightType type;
/** Spot size. Aligned to size of vec2. */
vec2 spot_size_inv;
/** Padding to sizeof(vec4). */
float _pad0;
float _pad1;
};
BLI_STATIC_ASSERT_ALIGN(LightData, 16)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Shadows
* \{ */
/**
* A point light shadow is composed of 1, 5 or 6 shadow regions.
* Regions are sorted in this order -Z, +X, -X, +Y, -Y, +Z.
* Face index is computed from light's object space coordinates.
*/
struct ShadowPunctualData {
/** Shadow matrix to convert Local face coordinates to UV space [0..1]. */
mat4 shadow_mat;
/** NOTE: It is ok to use vec3 here. A float is declared right after it.
* vec3 is also aligned to 16 bytes. */
/** Shadow offset caused by jittering projection origin (for soft shadows). */
vec3 shadow_offset;
/** Shadow bias in world space. */
float shadow_bias;
/** Offset from the first region to the second one. All regions are stored vertically. */
float region_offset;
/** True if shadow is omnidirectional and there is 6 fullsized shadow regions. */
bool is_omni;
/** Padding to sizeof(vec4). */
int _pad0;
int _pad1;
};
BLI_STATIC_ASSERT_ALIGN(ShadowPunctualData, 16)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Light Probes
* \{ */
/**
* Data used when filtering the cubemap.
* NOTE(fclem): We might want to promote some of theses to push constants as they are changed
* very frequently (Vulkan).
*/
struct LightProbeFilterData {
/** For glossy filter. */
float roughness;
/** Higher bias lowers the noise but increases blur and reduces quality. */
float lod_bias;
/** Final intensity multiplicator. */
float instensity_fac;
/** Luma maximum value. */
float luma_max;
/** Sample count to take from the input cubemap. */
float sample_count;
/** Visibility blur ratio [0..1]. Converted to angle in [0..PI/2] range. */
float visibility_blur;
/** Depth range to encode in the resulting visibility map. */
float visibility_range;
/** Target layer to render the fullscreen triangle to. */
int target_layer;
};
BLI_STATIC_ASSERT_ALIGN(LightProbeFilterData, 16)
/**
* Common data to all irradiance grid.
*/
struct GridInfoData {
mat4 lookdev_rotation;
/** Total of visibility cells per row and layer. */
int visibility_cells_per_row;
int visibility_cells_per_layer;
/** Size of visibility cell. */
int visibility_size;
/** Number of irradiance cells per row. */
int irradiance_cells_per_row;
/** Smooth irradiance sample interpolation but increases light leaks. */
float irradiance_smooth;
/** Total number of active irradiance grid including world. */
int grid_count;
/** Display size of sample spheres. */
float display_size;
float _pad0;
};
BLI_STATIC_ASSERT_ALIGN(GridInfoData, 16)
/**
* Data for a single irradiance grid.
*/
struct GridData {
/** Object matrix inverse (World -> Local). */
mat4 local_mat;
/** Resolution of the light grid. */
ivec3 resolution;
/** Offset of the first cell of this grid in the grid texture. */
int offset;
/** World space vector between 2 adjacent cells. */
vec3 increment_x;
/** Attenuation Bias. */
float attenuation_bias;
/** World space vector between 2 adjacent cells. */
vec3 increment_y;
/** Attenuation scaling. */
float attenuation_scale;
/** World space vector between 2 adjacent cells. */
vec3 increment_z;
/** Number of grid levels not ready for display during baking. */
int level_skip;
/** World space corner position. */
vec3 corner;
/** Visibility test parameters. */
float visibility_range;
float visibility_bleed;
float visibility_bias;
float _pad0;
float _pad1;
};
BLI_STATIC_ASSERT_ALIGN(GridData, 16)
static inline ivec3 grid_cell_index_to_coordinate(int cell_id, ivec3 resolution)
{
ivec3 cell_coord;
cell_coord.z = cell_id % resolution.z;
cell_coord.y = (cell_id / resolution.z) % resolution.y;
cell_coord.x = cell_id / (resolution.z * resolution.y);
return cell_coord;
}
/**
* Common data to all cubemaps.
*/
struct CubemapInfoData {
mat4 lookdev_rotation;
/** LOD containing data for roughness of 1. */
float roughness_max_lod;
/** Total number of active cubemaps including world. */
int cube_count;
/** Display size of sample spheres. */
float display_size;
float _pad2;
};
BLI_STATIC_ASSERT_ALIGN(CubemapInfoData, 16)
#define CUBEMAP_SHAPE_SPHERE 0.0
#define CUBEMAP_SHAPE_BOX 1.0
/**
* Data for a single reflection cubemap probe.
*/
struct CubemapData {
/** Influence shape matrix (World -> Local). */
mat4 influence_mat;
/** Packed data in the last column of the influence_mat. */
#define _attenuation_factor influence_mat[0][3]
#define _attenuation_type influence_mat[1][3]
#define _parallax_type influence_mat[2][3]
/** Layer of the cube array to sample. */
#define _layer influence_mat[3][3]
/** Parallax shape matrix (World -> Local). */
mat4 parallax_mat;
/** Packed data in the last column of the parallax_mat. */
#define _world_position_x parallax_mat[0][3]
#define _world_position_y parallax_mat[1][3]
#define _world_position_z parallax_mat[2][3]
};
BLI_STATIC_ASSERT_ALIGN(CubemapData, 16)
struct LightProbeInfoData {
GridInfoData grids;
CubemapInfoData cubes;
};
BLI_STATIC_ASSERT_ALIGN(LightProbeInfoData, 16)
#define GRID_MAX 64
/** \} */
/* -------------------------------------------------------------------- */
/** \name Utility Texture
* \{ */
#define UTIL_TEX_SIZE 64
#define UTIL_BTDF_LAYER_COUNT 16
/* Scale and bias to avoid interpolation of the border pixel.
* Remap UVs to the border pixels centers. */
#define UTIL_TEX_UV_SCALE ((UTIL_TEX_SIZE - 1.0f) / UTIL_TEX_SIZE)
#define UTIL_TEX_UV_BIAS (0.5f / UTIL_TEX_SIZE)
#define UTIL_BLUE_NOISE_LAYER 0
#define UTIL_LTC_MAT_LAYER 1
#define UTIL_LTC_MAG_LAYER 2
#define UTIL_BSDF_LAYER 2
#define UTIL_BTDF_LAYER 3
#define UTIL_DISK_INTEGRAL_LAYER 3
#define UTIL_DISK_INTEGRAL_COMP 2
#ifndef __cplusplus
/* For codestyle reasons, we do not declare samplers in lib files. Use a prototype instead. */
vec4 utility_tx_fetch(vec2 texel, float layer);
vec4 utility_tx_sample(vec2 uv, float layer);
/* Fetch texel. Wrapping if above range. */
# define utility_tx_fetch_define(utility_tx_) \
vec4 utility_tx_fetch(vec2 texel, float layer) \
{ \
return texelFetch(utility_tx_, ivec3(ivec2(texel) % UTIL_TEX_SIZE, layer), 0); \
}
/* Sample at uv position. Filtered & Wrapping enabled. */
# define utility_tx_sample_define(utility_tx_) \
vec4 utility_tx_sample(vec2 uv, float layer) \
{ \
return textureLod(utility_tx_, vec3(uv, layer), 0.0); \
}
/* Stubs declarations if not using it. */
# define utility_tx_fetch_define_stub(utility_tx_) \
vec4 utility_tx_fetch(vec2 texel, float layer) \
{ \
return vec4(0); \
}
# define utility_tx_sample_define_stub(utility_tx_) \
vec4 utility_tx_sample(vec2 uv, float layer) \
{ \
return vec4(0); \
}
#endif
/** \} */
#ifdef __cplusplus
using CameraDataBuf = StructBuffer<CameraData>;
using CubemapDataBuf = StructArrayBuffer<CubemapData, CULLING_ITEM_BATCH>;
using CullingDataBuf = StructBuffer<CullingData>;
using DepthOfFieldDataBuf = StructBuffer<DepthOfFieldData>;
using GridDataBuf = StructArrayBuffer<GridData, GRID_MAX>;
using LightDataBuf = StructArrayBuffer<LightData, CULLING_ITEM_BATCH>;
using LightProbeFilterDataBuf = StructBuffer<LightProbeFilterData>;
using LightProbeInfoDataBuf = StructBuffer<LightProbeInfoData>;
using ShadowPunctualDataBuf = StructArrayBuffer<ShadowPunctualData, CULLING_ITEM_BATCH>;
using VelocityObjectBuf = StructBuffer<VelocityObjectData>;
# undef bool
} // namespace blender::eevee
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,453 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* Shading passes contain drawcalls specific to shading pipelines.
* They are to be shared across views.
* This file is only for shading passes. Other passes are declared in their own module.
*/
#include "eevee_instance.hh"
#include "eevee_shading.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name Background Pass
*
* \{ */
void BackgroundPass::sync(GPUMaterial *gpumat, GPUTexture *lookdev_tx)
{
DRWState state = DRW_STATE_WRITE_COLOR;
background_ps_ = DRW_pass_create("Background", state);
/* Push a matrix at the same location as the camera. */
mat4 camera_mat;
unit_m4(camera_mat);
copy_v3_v3(camera_mat[3], inst_.camera.data_get().viewinv[3]);
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, background_ps_);
if (lookdev_tx != nullptr) {
/* HACK(fclem) This particular texture has been left without resource to be set here. */
DRW_shgroup_uniform_texture(grp, "samp0", lookdev_tx);
}
DRW_shgroup_call_obmat(grp, DRW_cache_fullscreen_quad_get(), camera_mat);
}
void BackgroundPass::render(void)
{
DRW_draw_pass(background_ps_);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Forward Pass
*
* NPR materials (using Closure to RGBA) or material using ALPHA_BLEND.
* \{ */
void ForwardPass::sync(void)
{
{
DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
prepass_ps_ = DRW_pass_create("Forward.Opaque.Prepass", state);
state |= DRW_STATE_CULL_BACK;
prepass_culled_ps_ = DRW_pass_create("Forward.Opaque.Prepass.Culled", state);
DRW_pass_link(prepass_ps_, prepass_culled_ps_);
}
{
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL;
opaque_ps_ = DRW_pass_create("Forward.Opaque", state);
state |= DRW_STATE_CULL_BACK;
opaque_culled_ps_ = DRW_pass_create("Forward.Opaque.Culled", state);
DRW_pass_link(opaque_ps_, opaque_culled_ps_);
}
{
DRWState state = DRW_STATE_DEPTH_LESS_EQUAL;
transparent_ps_ = DRW_pass_create("Forward.Transparent", state);
}
}
DRWShadingGroup *ForwardPass::material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat)
{
DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? opaque_culled_ps_ : opaque_ps_;
LightModule &lights = inst_.lights;
LightProbeModule &lightprobes = inst_.lightprobes;
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
DRW_shgroup_uniform_block_ref(grp, "lights_block", lights.lights_ubo_ref_get());
DRW_shgroup_uniform_block_ref(grp, "shadows_punctual_block", lights.shadows_ubo_ref_get());
DRW_shgroup_uniform_block_ref(grp, "lights_culling_block", lights.culling_ubo_ref_get());
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
DRW_shgroup_uniform_block(grp, "grids_block", lightprobes.grid_ubo_get());
DRW_shgroup_uniform_block(grp, "cubes_block", lightprobes.cube_ubo_get());
DRW_shgroup_uniform_block(grp, "lightprobes_info_block", lightprobes.info_ubo_get());
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get());
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
DRW_shgroup_uniform_texture_ref(grp, "lights_culling_tx", lights.culling_tx_ref_get());
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
DRW_shgroup_uniform_texture_ref(grp, "shadow_atlas_tx", inst_.shadows.atlas_ref_get());
return grp;
}
DRWShadingGroup *ForwardPass::prepass_opaque_add(::Material *blender_mat, GPUMaterial *gpumat)
{
DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? prepass_culled_ps_ :
prepass_ps_;
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
return grp;
}
DRWShadingGroup *ForwardPass::material_transparent_add(::Material *blender_mat,
GPUMaterial *gpumat)
{
LightModule &lights = inst_.lights;
LightProbeModule &lightprobes = inst_.lightprobes;
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, transparent_ps_);
DRW_shgroup_uniform_block_ref(grp, "lights_block", lights.lights_ubo_ref_get());
DRW_shgroup_uniform_block_ref(grp, "shadows_punctual_block", lights.shadows_ubo_ref_get());
DRW_shgroup_uniform_block_ref(grp, "lights_culling_block", lights.culling_ubo_ref_get());
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
DRW_shgroup_uniform_block(grp, "grids_block", lightprobes.grid_ubo_get());
DRW_shgroup_uniform_block(grp, "cubes_block", lightprobes.cube_ubo_get());
DRW_shgroup_uniform_block(grp, "lightprobes_info_block", lightprobes.info_ubo_get());
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get());
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
DRW_shgroup_uniform_texture_ref(grp, "lights_culling_tx", lights.culling_tx_ref_get());
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
DRW_shgroup_uniform_texture_ref(grp, "shadow_atlas_tx", inst_.shadows.atlas_ref_get());
DRWState state_disable = DRW_STATE_WRITE_DEPTH;
DRWState state_enable = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM;
if (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) {
state_enable |= DRW_STATE_CULL_BACK;
}
DRW_shgroup_state_disable(grp, state_disable);
DRW_shgroup_state_enable(grp, state_enable);
return grp;
}
DRWShadingGroup *ForwardPass::prepass_transparent_add(::Material *blender_mat, GPUMaterial *gpumat)
{
if ((blender_mat->blend_flag & MA_BL_HIDE_BACKFACE) == 0) {
return nullptr;
}
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, transparent_ps_);
DRWState state_disable = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM;
DRWState state_enable = DRW_STATE_WRITE_DEPTH;
if (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) {
state_enable |= DRW_STATE_CULL_BACK;
}
DRW_shgroup_state_disable(grp, state_disable);
DRW_shgroup_state_enable(grp, state_enable);
return grp;
}
void ForwardPass::render(void)
{
/* Only one batch of light is supported. */
inst_.lights.bind_batch(0);
DRW_draw_pass(prepass_ps_);
DRW_draw_pass(opaque_ps_);
/* TODO(fclem) This is suboptimal. We could sort during sync. */
/* FIXME(fclem) This wont work for panoramic, where we need
* to sort by distance to camera, not by z. */
DRW_pass_sort_shgroup_z(transparent_ps_);
DRW_draw_pass(transparent_ps_);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name DeferredLayer
* \{ */
void DeferredLayer::sync(void)
{
{
DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
prepass_ps_ = DRW_pass_create("Gbuffer.Prepass", state);
state |= DRW_STATE_CULL_BACK;
prepass_culled_ps_ = DRW_pass_create("Gbuffer.Prepass.Culled", state);
DRW_pass_link(prepass_ps_, prepass_culled_ps_);
}
{
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_STENCIL_ALWAYS |
DRW_STATE_WRITE_STENCIL;
gbuffer_ps_ = DRW_pass_create("Gbuffer", state);
state |= DRW_STATE_CULL_BACK;
gbuffer_culled_ps_ = DRW_pass_create("Gbuffer.Culled", state);
DRW_pass_link(gbuffer_ps_, gbuffer_culled_ps_);
}
{
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL |
DRW_STATE_CULL_BACK | DRW_STATE_STENCIL_ALWAYS | DRW_STATE_WRITE_STENCIL;
volume_ps_ = DRW_pass_create("VolumesHeterogeneous", state);
}
}
DRWShadingGroup *DeferredLayer::material_add(::Material *blender_mat, GPUMaterial *gpumat)
{
uint stencil_mask = CLOSURE_DIFFUSE | CLOSURE_REFLECTION | CLOSURE_TRANSPARENCY |
CLOSURE_EMISSION;
DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? gbuffer_culled_ps_ :
gbuffer_ps_;
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
DRW_shgroup_stencil_set(grp, stencil_mask, 0xFF, 0xFF);
return grp;
}
DRWShadingGroup *DeferredLayer::prepass_add(::Material *blender_mat, GPUMaterial *gpumat)
{
DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? prepass_culled_ps_ :
prepass_ps_;
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
return grp;
}
void DeferredLayer::volume_add(Object *ob)
{
LightModule &lights = inst_.lights;
DeferredPass &deferred_pass = inst_.shading_passes.deferred;
GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_VOLUME);
DRWShadingGroup *grp = DRW_shgroup_create(sh, volume_ps_);
DRW_shgroup_uniform_block_ref(grp, "lights_block", lights.lights_ubo_ref_get());
DRW_shgroup_uniform_block_ref(grp, "shadows_punctual_block", lights.shadows_ubo_ref_get());
DRW_shgroup_uniform_block_ref(grp, "lights_culling_block", lights.culling_ubo_ref_get());
DRW_shgroup_uniform_texture_ref(grp, "lights_culling_tx", lights.culling_tx_ref_get());
DRW_shgroup_uniform_texture_ref(grp, "depth_max_tx", &deferred_pass.input_depth_behind_tx_);
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
DRW_shgroup_stencil_set(grp, CLOSURE_VOLUME | CLOSURE_TRANSPARENCY, 0xFF, 0xFF);
DRW_shgroup_call(grp, DRW_cache_cube_get(), ob);
}
void DeferredLayer::render(GBuffer &gbuffer, GPUFrameBuffer *view_fb)
{
const bool no_surfaces = DRW_pass_is_empty(gbuffer_ps_);
const bool no_volumes = DRW_pass_is_empty(volume_ps_);
if (no_surfaces && no_volumes) {
return;
}
gbuffer.bind((eClosureBits)0xFFFFFFFF);
if (!no_surfaces) {
DRW_draw_pass(prepass_ps_);
DRW_draw_pass(gbuffer_ps_);
}
DeferredPass &deferred_pass = inst_.shading_passes.deferred;
deferred_pass.input_combined_tx = gbuffer.combined_tx;
deferred_pass.input_emission_data_tx_ = gbuffer.emission_tx;
deferred_pass.input_transmit_color_tx_ = gbuffer.transmit_color_tx;
deferred_pass.input_transmit_normal_tx_ = gbuffer.transmit_normal_tx;
deferred_pass.input_transmit_data_tx_ = gbuffer.transmit_data_tx;
deferred_pass.input_reflect_color_tx_ = gbuffer.reflect_color_tx;
deferred_pass.input_reflect_normal_tx_ = gbuffer.reflect_normal_tx;
deferred_pass.input_transparency_data_tx_ = gbuffer.transparency_tx;
deferred_pass.input_volume_data_tx_ = gbuffer.volume_tx;
deferred_pass.input_depth_tx_ = gbuffer.depth_copy_tx;
if (!no_volumes) {
gbuffer.copy_depth_behind();
deferred_pass.input_depth_behind_tx_ = gbuffer.depth_behind_tx;
}
if (!no_volumes) {
for (auto index : inst_.lights.index_range()) {
inst_.lights.bind_batch(index);
gbuffer.bind_volume();
DRW_draw_pass(volume_ps_);
}
}
gbuffer.copy_depth();
if (true) {
gbuffer.bind_holdout();
DRW_draw_pass(deferred_pass.eval_holdout_ps_);
}
GPU_framebuffer_bind(view_fb);
if (true) {
DRW_draw_pass(deferred_pass.eval_transparency_ps_);
}
for (auto index : inst_.lights.index_range()) {
inst_.lights.bind_batch(index);
if (!no_volumes) {
DRW_draw_pass(deferred_pass.eval_volume_homogeneous_ps_);
}
if (!no_surfaces) {
DRW_draw_pass(deferred_pass.eval_diffuse_ps_);
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name DeferredLayer
* \{ */
void DeferredPass::sync(void)
{
opaque_layer_.sync();
refraction_layer_.sync();
volumetric_layer_.sync();
LightModule &lights = inst_.lights;
LightProbeModule &lightprobes = inst_.lightprobes;
eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
{
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_ADD_FULL;
eval_diffuse_ps_ = DRW_pass_create("DeferredDirect", state);
GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_EVAL_DIRECT);
DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_diffuse_ps_);
DRW_shgroup_uniform_block_ref(grp, "lights_block", lights.lights_ubo_ref_get());
DRW_shgroup_uniform_block_ref(grp, "shadows_punctual_block", lights.shadows_ubo_ref_get());
DRW_shgroup_uniform_block_ref(grp, "lights_culling_block", lights.culling_ubo_ref_get());
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
DRW_shgroup_uniform_block(grp, "grids_block", lightprobes.grid_ubo_get());
DRW_shgroup_uniform_block(grp, "cubes_block", lightprobes.cube_ubo_get());
DRW_shgroup_uniform_block(grp, "lightprobes_info_block", lightprobes.info_ubo_get());
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get());
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
DRW_shgroup_uniform_texture_ref(grp, "lights_culling_tx", lights.culling_tx_ref_get());
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
DRW_shgroup_uniform_texture_ref(grp, "shadow_atlas_tx", inst_.shadows.atlas_ref_get());
DRW_shgroup_uniform_texture_ref_ex(
grp, "emission_data_tx", &input_emission_data_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(
grp, "transmit_color_tx", &input_transmit_color_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(
grp, "transmit_normal_tx", &input_transmit_normal_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(
grp, "transmit_data_tx", &input_transmit_data_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(
grp, "reflect_color_tx", &input_reflect_color_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(
grp, "reflect_normal_tx", &input_reflect_normal_tx_, no_interp);
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
DRW_shgroup_stencil_set(
grp, 0x0, 0x0, CLOSURE_DIFFUSE | CLOSURE_REFLECTION | CLOSURE_EMISSION);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
{
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_ADD_FULL;
eval_volume_homogeneous_ps_ = DRW_pass_create("DeferredVolume", state);
GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_EVAL_VOLUME);
DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_volume_homogeneous_ps_);
DRW_shgroup_uniform_block_ref(grp, "lights_block", lights.lights_ubo_ref_get());
DRW_shgroup_uniform_block_ref(grp, "shadows_punctual_block", lights.shadows_ubo_ref_get());
DRW_shgroup_uniform_block_ref(grp, "lights_culling_block", lights.culling_ubo_ref_get());
DRW_shgroup_uniform_texture_ref(grp, "lights_culling_tx", lights.culling_tx_ref_get());
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
DRW_shgroup_uniform_texture_ref(grp, "shadow_atlas_tx", inst_.shadows.atlas_ref_get());
DRW_shgroup_uniform_texture_ref_ex(
grp, "transparency_data_tx", &input_transparency_data_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(grp, "volume_data_tx", &input_volume_data_tx_, no_interp);
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
DRW_shgroup_stencil_set(grp, 0x0, 0x0, CLOSURE_VOLUME);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
{
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_MUL;
eval_transparency_ps_ = DRW_pass_create("DeferredTransparency", state);
GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_EVAL_TRANSPARENT);
DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_transparency_ps_);
DRW_shgroup_uniform_texture_ref(grp, "transparency_data_tx", &input_transparency_data_tx_);
DRW_shgroup_uniform_texture_ref_ex(grp, "volume_data_tx", &input_volume_data_tx_, no_interp);
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
DRW_shgroup_stencil_set(grp, 0x0, 0x0, CLOSURE_TRANSPARENCY);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
{
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL;
eval_holdout_ps_ = DRW_pass_create("DeferredHoldout", state);
GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_EVAL_HOLDOUT);
DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_volume_homogeneous_ps_);
DRW_shgroup_uniform_texture_ref(grp, "combined_tx", &input_combined_tx);
DRW_shgroup_uniform_texture_ref(grp, "transparency_data_tx", &input_transparency_data_tx_);
DRW_shgroup_stencil_set(grp, 0x0, 0x0, CLOSURE_TRANSPARENCY);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
}
DRWShadingGroup *DeferredPass::material_add(::Material *material, GPUMaterial *gpumat)
{
if (material->blend_flag & MA_BL_SS_REFRACTION) {
return refraction_layer_.material_add(material, gpumat);
}
else {
return opaque_layer_.material_add(material, gpumat);
}
}
DRWShadingGroup *DeferredPass::prepass_add(::Material *material, GPUMaterial *gpumat)
{
if (material->blend_flag & MA_BL_SS_REFRACTION) {
return refraction_layer_.prepass_add(material, gpumat);
}
else {
return opaque_layer_.prepass_add(material, gpumat);
}
}
void DeferredPass::volume_add(Object *ob)
{
volumetric_layer_.volume_add(ob);
}
void DeferredPass::render(GBuffer &gbuffer, GPUFrameBuffer *view_fb)
{
opaque_layer_.render(gbuffer, view_fb);
refraction_layer_.render(gbuffer, view_fb);
volumetric_layer_.render(gbuffer, view_fb);
gbuffer.render_end();
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,480 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* Shading passes contain drawcalls specific to shading pipelines.
* They are to be shared across views.
* This file is only for shading passes. Other passes are declared in their own module.
*/
#pragma once
#include "DRW_render.h"
#include "eevee_lut.h"
#include "eevee_culling.hh"
#include "eevee_shadow.hh"
#include "eevee_velocity.hh"
namespace blender::eevee {
class Instance;
/* -------------------------------------------------------------------- */
/** \name Background Pass
*
* Render world values.
* \{ */
class BackgroundPass {
private:
Instance &inst_;
DRWPass *background_ps_ = nullptr;
public:
BackgroundPass(Instance &inst) : inst_(inst){};
void sync(GPUMaterial *gpumat, GPUTexture *loodev_tx = nullptr);
void render(void);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Forward Pass
*
* Handles alpha blended surfaces and NPR materials (using Closure to RGBA).
* \{ */
class ForwardPass {
private:
Instance &inst_;
DRWPass *prepass_ps_ = nullptr;
DRWPass *prepass_culled_ps_ = nullptr;
DRWPass *opaque_ps_ = nullptr;
DRWPass *opaque_culled_ps_ = nullptr;
DRWPass *transparent_ps_ = nullptr;
public:
ForwardPass(Instance &inst) : inst_(inst){};
void sync(void);
DRWShadingGroup *material_add(::Material *blender_mat, GPUMaterial *gpumat)
{
return (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) ?
material_transparent_add(blender_mat, gpumat) :
material_opaque_add(blender_mat, gpumat);
}
DRWShadingGroup *prepass_add(::Material *blender_mat, GPUMaterial *gpumat)
{
return (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) ?
prepass_transparent_add(blender_mat, gpumat) :
prepass_opaque_add(blender_mat, gpumat);
}
DRWShadingGroup *material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat);
DRWShadingGroup *prepass_opaque_add(::Material *blender_mat, GPUMaterial *gpumat);
DRWShadingGroup *material_transparent_add(::Material *blender_mat, GPUMaterial *gpumat);
DRWShadingGroup *prepass_transparent_add(::Material *blender_mat, GPUMaterial *gpumat);
void render(void);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Deferred lighting.
* \{ */
enum eClosureBits {
CLOSURE_DIFFUSE = 1 << 0,
CLOSURE_SSS = 1 << 1,
CLOSURE_REFLECTION = 1 << 2,
CLOSURE_REFRACTION = 1 << 3,
CLOSURE_VOLUME = 1 << 4,
CLOSURE_EMISSION = 1 << 5,
CLOSURE_TRANSPARENCY = 1 << 6,
};
struct GBuffer {
Texture transmit_color_tx = Texture("GbufferTransmitColor");
Texture transmit_normal_tx = Texture("GbufferTransmitNormal");
Texture transmit_data_tx = Texture("GbufferTransmitData");
Texture reflect_color_tx = Texture("GbufferReflectionColor");
Texture reflect_normal_tx = Texture("GbufferReflectionNormal");
Texture volume_tx = Texture("GbufferVolume");
Texture emission_tx = Texture("GbufferEmission");
Texture transparency_tx = Texture("GbufferTransparency");
Framebuffer gbuffer_fb = Framebuffer("Gbuffer");
Framebuffer volume_fb = Framebuffer("VolumeHeterogeneous");
Texture holdout_tx = Texture("HoldoutRadiance");
Framebuffer holdout_fb = Framebuffer("Holdout");
Texture depth_behind_tx = Texture("DepthBehind");
Texture depth_copy_tx = Texture("DepthCopy");
Framebuffer depth_behind_fb = Framebuffer("DepthCopy");
Framebuffer depth_copy_fb = Framebuffer("DepthCopy");
/* Owner of this GBuffer. Used to query temp textures. */
void *owner;
/* Pointer to the view's buffers. */
GPUTexture *depth_tx = nullptr;
GPUTexture *combined_tx = nullptr;
int layer = -1;
void sync(GPUTexture *depth_tx_, GPUTexture *combined_tx_, void *owner_, int layer_ = -1)
{
owner = owner_;
depth_tx = depth_tx_;
combined_tx = combined_tx_;
layer = layer_;
transmit_color_tx.sync_tmp();
transmit_normal_tx.sync_tmp();
transmit_data_tx.sync_tmp();
reflect_color_tx.sync_tmp();
reflect_normal_tx.sync_tmp();
volume_tx.sync_tmp();
emission_tx.sync_tmp();
transparency_tx.sync_tmp();
holdout_tx.sync_tmp();
depth_behind_tx.sync_tmp();
depth_copy_tx.sync_tmp();
}
void bind(eClosureBits closures_used)
{
ivec2 extent = {GPU_texture_width(depth_tx), GPU_texture_height(depth_tx)};
/* TODO Reuse for different config. */
if (closures_used & (CLOSURE_DIFFUSE | CLOSURE_SSS | CLOSURE_REFRACTION)) {
transmit_color_tx.acquire_tmp(UNPACK2(extent), GPU_R11F_G11F_B10F, owner);
}
if (closures_used & (CLOSURE_SSS | CLOSURE_REFRACTION)) {
transmit_normal_tx.acquire_tmp(UNPACK2(extent), GPU_RGBA16F, owner);
transmit_data_tx.acquire_tmp(UNPACK2(extent), GPU_R11F_G11F_B10F, owner);
}
else if (closures_used & CLOSURE_DIFFUSE) {
transmit_normal_tx.acquire_tmp(UNPACK2(extent), GPU_RG16F, owner);
}
if (closures_used & CLOSURE_DIFFUSE) {
reflect_color_tx.acquire_tmp(UNPACK2(extent), GPU_R11F_G11F_B10F, owner);
reflect_normal_tx.acquire_tmp(UNPACK2(extent), GPU_RGBA16, owner);
}
if (closures_used & CLOSURE_VOLUME) {
/* TODO(fclem): This is killing performance.
* Idea: use interleaved data pattern to fill only a 32bpp buffer. */
volume_tx.acquire_tmp(UNPACK2(extent), GPU_RGBA32UI, owner);
}
if (closures_used & CLOSURE_EMISSION) {
emission_tx.acquire_tmp(UNPACK2(extent), GPU_R11F_G11F_B10F, owner);
}
if (closures_used & CLOSURE_TRANSPARENCY) {
/* TODO(fclem): Speedup by using Dithered holdout and GPU_RGB10_A2. */
transparency_tx.acquire_tmp(UNPACK2(extent), GPU_RGBA16, owner);
}
holdout_tx.acquire_tmp(UNPACK2(extent), GPU_R11F_G11F_B10F, owner);
depth_behind_tx.acquire_tmp(UNPACK2(extent), GPU_DEPTH24_STENCIL8, owner);
depth_copy_tx.acquire_tmp(UNPACK2(extent), GPU_DEPTH24_STENCIL8, owner);
/* Layer attachement also works with cubemap. */
gbuffer_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer),
GPU_ATTACHMENT_TEXTURE(transmit_color_tx),
GPU_ATTACHMENT_TEXTURE(transmit_normal_tx),
GPU_ATTACHMENT_TEXTURE(transmit_data_tx),
GPU_ATTACHMENT_TEXTURE(reflect_color_tx),
GPU_ATTACHMENT_TEXTURE(reflect_normal_tx),
GPU_ATTACHMENT_TEXTURE(volume_tx),
GPU_ATTACHMENT_TEXTURE(emission_tx),
GPU_ATTACHMENT_TEXTURE(transparency_tx));
GPU_framebuffer_bind(gbuffer_fb);
GPU_framebuffer_clear_stencil(gbuffer_fb, 0x0);
}
void bind_volume(void)
{
volume_fb.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx),
GPU_ATTACHMENT_TEXTURE(volume_tx),
GPU_ATTACHMENT_TEXTURE(transparency_tx));
GPU_framebuffer_bind(volume_fb);
}
void bind_holdout(void)
{
holdout_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(holdout_tx));
GPU_framebuffer_bind(holdout_fb);
}
void copy_depth_behind(void)
{
depth_behind_fb.ensure(GPU_ATTACHMENT_TEXTURE(depth_behind_tx));
GPU_framebuffer_bind(depth_behind_fb);
GPU_framebuffer_blit(gbuffer_fb, 0, depth_behind_fb, 0, GPU_DEPTH_BIT);
}
void copy_depth(void)
{
depth_copy_fb.ensure(GPU_ATTACHMENT_TEXTURE(depth_copy_tx));
GPU_framebuffer_bind(depth_copy_fb);
GPU_framebuffer_blit(gbuffer_fb, 0, depth_copy_fb, 0, GPU_DEPTH_BIT);
}
void render_end(void)
{
transmit_color_tx.release_tmp();
transmit_normal_tx.release_tmp();
transmit_data_tx.release_tmp();
reflect_color_tx.release_tmp();
reflect_normal_tx.release_tmp();
volume_tx.release_tmp();
emission_tx.release_tmp();
transparency_tx.release_tmp();
holdout_tx.release_tmp();
depth_behind_tx.release_tmp();
depth_copy_tx.release_tmp();
}
};
class DeferredLayer {
private:
Instance &inst_;
/* TODO */
// GPUTexture *input_emission_data_tx_ = nullptr;
// GPUTexture *input_diffuse_data_tx_ = nullptr;
// GPUTexture *input_depth_tx_ = nullptr;
DRWPass *prepass_ps_ = nullptr;
DRWPass *prepass_culled_ps_ = nullptr;
DRWPass *gbuffer_ps_ = nullptr;
DRWPass *gbuffer_culled_ps_ = nullptr;
DRWPass *volume_ps_ = nullptr;
public:
DeferredLayer(Instance &inst) : inst_(inst){};
void sync(void);
DRWShadingGroup *material_add(::Material *blender_mat, GPUMaterial *gpumat);
DRWShadingGroup *prepass_add(::Material *blender_mat, GPUMaterial *gpumat);
void volume_add(Object *ob);
void render(GBuffer &gbuffer, GPUFrameBuffer *view_fb);
};
class DeferredPass {
friend DeferredLayer;
private:
Instance &inst_;
/* Gbuffer filling passes. We could have an arbitrary number of them but for now we just have
* a harcoded number of them. */
DeferredLayer opaque_layer_;
DeferredLayer refraction_layer_;
DeferredLayer volumetric_layer_;
DRWPass *eval_diffuse_ps_ = nullptr;
DRWPass *eval_transparency_ps_ = nullptr;
DRWPass *eval_holdout_ps_ = nullptr;
// DRWPass *eval_volume_heterogeneous_ps_ = nullptr;
DRWPass *eval_volume_homogeneous_ps_ = nullptr;
/* References only. */
GPUTexture *input_combined_tx = nullptr;
GPUTexture *input_depth_behind_tx_ = nullptr;
GPUTexture *input_depth_tx_ = nullptr;
GPUTexture *input_emission_data_tx_ = nullptr;
GPUTexture *input_transmit_color_tx_ = nullptr;
GPUTexture *input_transmit_normal_tx_ = nullptr;
GPUTexture *input_transmit_data_tx_ = nullptr;
GPUTexture *input_reflect_color_tx_ = nullptr;
GPUTexture *input_reflect_normal_tx_ = nullptr;
GPUTexture *input_transparency_data_tx_ = nullptr;
GPUTexture *input_volume_data_tx_ = nullptr;
// GPUTexture *input_volume_radiance_tx_ = nullptr;
// GPUTexture *input_volume_transmittance_tx_ = nullptr;
public:
DeferredPass(Instance &inst)
: inst_(inst), opaque_layer_(inst), refraction_layer_(inst), volumetric_layer_(inst){};
void sync(void);
DRWShadingGroup *material_add(::Material *material, GPUMaterial *gpumat);
DRWShadingGroup *prepass_add(::Material *material, GPUMaterial *gpumat);
void volume_add(Object *ob);
void render(GBuffer &gbuffer, GPUFrameBuffer *view_fb);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Utility texture
*
* 64x64 2D array texture containing LUT tables and blue noises.
* \{ */
class UtilityTexture : public Texture {
struct Layer {
float data[UTIL_TEX_SIZE * UTIL_TEX_SIZE][4];
};
static constexpr int lut_size = UTIL_TEX_SIZE;
static constexpr int lut_size_sqr = lut_size * lut_size;
static constexpr int layer_count = 4 + UTIL_BTDF_LAYER_COUNT;
public:
UtilityTexture()
: Texture("UtilityTx", lut_size, lut_size, layer_count, 1, GPU_RGBA16F, nullptr, true)
{
#ifdef RUNTIME_LUT_CREATION
float *bsdf_ggx_lut = EEVEE_lut_update_ggx_brdf(lut_size);
float(*btdf_ggx_lut)[lut_size_sqr * 2] = (float(*)[lut_size_sqr * 2])
EEVEE_lut_update_ggx_btdf(lut_size, UTIL_BTDF_LAYER_COUNT);
#else
const float *bsdf_ggx_lut = bsdf_split_sum_ggx;
const float(*btdf_ggx_lut)[lut_size_sqr * 2] = btdf_split_sum_ggx;
#endif
Vector<Layer> data(layer_count);
{
Layer &layer = data[UTIL_BLUE_NOISE_LAYER];
memcpy(layer.data, blue_noise, sizeof(layer));
}
{
Layer &layer = data[UTIL_LTC_MAT_LAYER];
memcpy(layer.data, ltc_mat_ggx, sizeof(layer));
}
{
Layer &layer = data[UTIL_LTC_MAG_LAYER];
for (auto i : IndexRange(lut_size_sqr)) {
layer.data[i][0] = bsdf_ggx_lut[i * 2 + 0];
layer.data[i][1] = bsdf_ggx_lut[i * 2 + 1];
layer.data[i][2] = ltc_mag_ggx[i * 2 + 0];
layer.data[i][3] = ltc_mag_ggx[i * 2 + 1];
}
BLI_assert(UTIL_LTC_MAG_LAYER == UTIL_BSDF_LAYER);
}
{
Layer &layer = data[UTIL_DISK_INTEGRAL_LAYER];
for (auto i : IndexRange(lut_size_sqr)) {
layer.data[i][UTIL_DISK_INTEGRAL_COMP] = ltc_disk_integral[i];
}
}
{
for (auto layer_id : IndexRange(16)) {
Layer &layer = data[3 + layer_id];
for (auto i : IndexRange(lut_size_sqr)) {
layer.data[i][0] = btdf_ggx_lut[layer_id][i * 2 + 0];
layer.data[i][1] = btdf_ggx_lut[layer_id][i * 2 + 1];
}
}
}
GPU_texture_update_mipmap(*this, 0, GPU_DATA_FLOAT, data.data());
}
~UtilityTexture(){};
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name ShadingPasses
*
* \{ */
/**
* Shading passes. Shared between views. Objects will subscribe to one of them.
*/
class ShadingPasses {
public:
CullingLightPass light_culling;
BackgroundPass background;
DeferredPass deferred;
ForwardPass forward;
ShadowPass shadow;
VelocityPass velocity;
CullingDebugPass debug_culling;
UtilityTexture utility_tx;
public:
ShadingPasses(Instance &inst)
: light_culling(inst),
background(inst),
deferred(inst),
forward(inst),
shadow(inst),
velocity(inst),
debug_culling(inst){};
void sync()
{
light_culling.sync();
deferred.sync();
forward.sync();
shadow.sync();
velocity.sync();
debug_culling.sync();
}
DRWShadingGroup *material_add(::Material *blender_mat,
GPUMaterial *gpumat,
eMaterialPipeline pipeline_type)
{
switch (pipeline_type) {
case MAT_PIPE_DEFERRED_PREPASS:
return deferred.prepass_add(blender_mat, gpumat);
case MAT_PIPE_FORWARD_PREPASS:
return forward.prepass_add(blender_mat, gpumat);
case MAT_PIPE_DEFERRED:
return deferred.material_add(blender_mat, gpumat);
case MAT_PIPE_FORWARD:
return forward.material_add(blender_mat, gpumat);
case MAT_PIPE_VOLUME:
/* TODO(fclem) volume pass. */
return nullptr;
case MAT_PIPE_SHADOW:
return shadow.material_add(blender_mat, gpumat);
}
return nullptr;
}
};
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,531 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* The shadow module manages shadow update tagging & shadow rendering.
*/
#include "eevee_instance.hh"
#include <iostream>
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name Shadow Punctual
*
* \{ */
void ShadowPunctual::sync(eLightType light_type,
const mat4 &object_mat,
float cone_aperture,
float near_clip,
float far_clip,
float bias)
{
bool is_wide_cone = cone_aperture > DEG2RAD(90);
bool is_omni = cone_aperture > DEG2RAD(180);
if ((is_wide_cone_ != is_wide_cone) || (is_omni_ != is_omni)) {
is_wide_cone_ = is_wide_cone;
is_omni_ = is_omni;
shadows_.packing_changed_ = true;
}
far_ = max_ff(far_clip, 3e-4f);
near_ = min_ff(near_clip, far_clip - 1e-4f);
bias_ = bias;
update_extent(cube_res_);
light_type_ = light_type;
/* Keep custom data. */
size_x_ = _area_size_x;
size_y_ = _area_size_y;
copy_m4_m4(object_mat_.values, object_mat);
/* Clear custom data. */
object_mat_.values[0][3] = object_mat_.values[1][3] = object_mat_.values[2][3] = 0.0f;
object_mat_.values[3][3] = 1.0f;
/* TODO(fclem) Tighter bounds for cones. */
copy_v3_v3(bsphere.center, object_mat_.translation());
bsphere.radius = far_clip;
do_update_tag = true;
}
void ShadowPunctual::update_extent(int cube_res)
{
do_update_persist = true;
is_visible = false;
cube_res_ = cube_res;
if (is_omni_) {
extent[0] = cube_res_;
extent[1] = cube_res_ * 6;
}
else if (is_wide_cone_) {
extent[0] = cube_res_;
extent[1] = cube_res_ * 3;
}
else {
extent[0] = cube_res_;
extent[1] = cube_res_;
}
}
void ShadowPunctual::render(Instance &inst, MutableSpan<DRWView *> views)
{
mat4 viewmat;
copy_m4_m4(viewmat, object_mat_.values);
if (inst.shadows.soft_shadows_enabled_) {
*reinterpret_cast<vec3 *>(viewmat[3]) += object_mat_.ref_3x3() * random_offset_;
}
/* The viewmat is garanteed to be normalized by Light::Light(), so transpose is also the
* inverse. Transpose is equivalent to inverse only if matrix is symmetrical. */
/* TODO(fclem) */
// transpose_m4(r_viewmat);
/* Apply translation. */
// translate_m4(r_viewmat, );
invert_m4(viewmat);
/* Create/Update all views before rendering. This is in order to perform culling in batch. */
mat4 winmat;
cubeface_winmat_get(winmat, false);
view_update(views[Z_NEG], viewmat, winmat, Z_NEG);
if (is_wide_cone_) {
if (!is_omni_) {
cubeface_winmat_get(winmat, true);
}
view_update(views[X_POS], viewmat, winmat, X_POS);
view_update(views[X_NEG], viewmat, winmat, X_NEG);
view_update(views[Y_POS], viewmat, winmat, Y_POS);
view_update(views[Y_NEG], viewmat, winmat, Y_NEG);
}
if (is_omni_) {
view_update(views[Z_POS], viewmat, winmat, Z_POS);
}
/* TODO(fclem): Optimization: only update faces that are intersecting the active view. */
view_render(inst, views[Z_NEG], Z_NEG);
if (is_wide_cone_) {
view_render(inst, views[X_POS], X_POS);
view_render(inst, views[X_NEG], X_NEG);
view_render(inst, views[Y_POS], Y_POS);
view_render(inst, views[Y_NEG], Y_NEG);
}
if (is_omni_) {
view_render(inst, views[Z_POS], Z_POS);
}
do_update_persist = false;
is_visible = false;
}
/* Sets random offset for soft shadows. This needs to be called before rendering and before
* converting the data to ShadowPunctualData, whichever comes first. If it is not the case,
* rendering and shadow map sampling won't match. */
void ShadowPunctual::random_position_on_shape_set(Instance &inst)
{
if (inst.shadows.soft_shadows_enabled_ == false) {
random_offset_ = vec3(0.0f);
aa_offset_ = vec2(0.0f);
return;
}
float random[3];
random[0] = inst.sampling.rng_get(SAMPLING_SHADOW_U);
random[1] = inst.sampling.rng_get(SAMPLING_SHADOW_V);
random[2] = inst.sampling.rng_get(SAMPLING_SHADOW_W);
aa_offset_.x = inst.sampling.rng_get(SAMPLING_SHADOW_X);
aa_offset_.y = inst.sampling.rng_get(SAMPLING_SHADOW_Y);
/* TODO(fclem) Decorellate per shadow. */
switch (light_type_) {
case LIGHT_RECT: {
random_offset_ = vec3((random[0] > 0.5f ? random[0] - 1.0f : random[0]) * 2.0f * size_x_,
(random[1] > 0.5f ? random[1] - 1.0f : random[1]) * 2.0f * size_y_,
0.0f);
break;
}
case LIGHT_ELLIPSE: {
vec2 disk = inst.sampling.sample_disk(random);
random_offset_ = vec3(disk.x * size_x_, disk.y * size_y_, 0.0f);
break;
}
default: {
random_offset_ = inst.sampling.sample_ball(random) * size_x_;
break;
}
}
}
void ShadowPunctual::cubeface_winmat_get(mat4 &winmat, bool half_opened)
{
/**
* o < Shadow source
* / \
* / \
* / \ < Face projection border
* / \
* / \
* / \
* | x | x | x | x | x | x |
*
* Here x's denote the samples location and cube_res_ is 6.
* To match samples at the border of each projector, we need a half pixel offset on each side of
* the projection. We add 2 more pixels to allow for AA jittering. Note that jittering this
* matrix directly exhibit the seam at face borders if the samples don't line up with the
* face projection border.
**/
float padding = 1.5f * 2.0f;
float side = near_ * (cube_res_ + padding) / cube_res_;
vec2 aa_jitter = aa_offset_ * (side * 2.0f / cube_res_);
perspective_m4(winmat,
-side + aa_jitter.x,
side + aa_jitter.x,
-side + aa_jitter.y,
((half_opened) ? 0.0f : side) + aa_jitter.y,
near_,
far_);
}
void ShadowPunctual::view_update(DRWView *&view,
const mat4 &viewmat,
const mat4 &winmat,
eShadowCubeFace face)
{
mat4 facemat;
mul_m4_m4m4(facemat, shadow_face_mat[face], viewmat);
if (view == nullptr) {
view = DRW_view_create(facemat, winmat, nullptr, nullptr, nullptr);
}
else {
DRW_view_update(view, facemat, winmat, nullptr, nullptr);
}
}
void ShadowPunctual::view_render(Instance &inst, DRWView *view, eShadowCubeFace face)
{
AtlasRegion sub_region = *this;
sub_region.extent[0] = sub_region.extent[1] = cube_res_;
if (face > 0 && !is_omni_) {
sub_region.extent[1] /= 2;
sub_region.offset[1] += sub_region.extent[1] * (face + 1);
}
else {
sub_region.offset[1] += sub_region.extent[1] * face;
}
DRW_view_set_active(view);
GPU_viewport(UNPACK2(sub_region.offset), UNPACK2(sub_region.extent));
inst.shading_passes.shadow.render();
}
ShadowPunctual::operator ShadowPunctualData()
{
ShadowPunctualData data;
cubeface_winmat_get(data.shadow_mat, false);
/**
* Conversion from NDC to atlas coordinate.
* GLSL pseudo code:
* region_uv = ((ndc * 0.5 + 0.5) * cube_res + offset) / atlas_extent;
* region_uv = ndc * (0.5 * cube_res) / atlas_extent
* + (0.5 * cube_res + offset) / atlas_extent;
* Also remove half a pixel from each side to avoid interpolation issues.
* The projection matrix should take this into account.
**/
mat4 coord_mat;
zero_m4(coord_mat);
coord_mat[0][0] = (0.5f * cube_res_) / shadows_.atlas_extent_[0];
coord_mat[1][1] = (0.5f * cube_res_) / shadows_.atlas_extent_[1];
coord_mat[2][2] = 0.5f;
coord_mat[3][0] = (0.5f * cube_res_ + offset[0]) / shadows_.atlas_extent_[0];
coord_mat[3][1] = (0.5f * cube_res_ + offset[1]) / shadows_.atlas_extent_[1];
coord_mat[3][2] = 0.5f;
coord_mat[3][3] = 1.0f;
mul_m4_m4m4(data.shadow_mat, coord_mat, data.shadow_mat);
data.shadow_offset = random_offset_;
data.is_omni = is_omni_;
data.shadow_bias = bias_;
data.region_offset = (is_omni_ ? cube_res_ : (cube_res_ / 2)) / float(shadows_.atlas_extent_.y);
return data;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Shadow Module
*
* \{ */
void ShadowModule::init(void)
{
if (cube_shadow_res_ != inst_.scene->eevee.shadow_cube_size) {
cube_shadow_res_ = inst_.scene->eevee.shadow_cube_size;
for (ShadowPunctual &shadow : punctuals_) {
shadow.update_extent(cube_shadow_res_);
}
packing_changed_ = true;
inst_.sampling.reset();
}
eGPUTextureFormat shadow_format = (inst_.scene->eevee.flag & SCE_EEVEE_SHADOW_HIGH_BITDEPTH) ?
GPU_DEPTH_COMPONENT32F :
GPU_DEPTH_COMPONENT16;
if (shadow_format_ != shadow_format) {
shadow_format_ = shadow_format;
format_changed_ = true;
inst_.sampling.reset();
}
const bool soft_shadow_enabled = (inst_.scene->eevee.flag & SCE_EEVEE_SHADOW_SOFT) != 0;
if (soft_shadows_enabled_ != soft_shadow_enabled) {
soft_shadows_enabled_ = soft_shadow_enabled;
for (ShadowPunctual &shadow : punctuals_) {
shadow.do_update_persist = true;
}
inst_.sampling.reset();
}
memset(views_, 0, sizeof(views_));
}
void ShadowModule::sync_caster(Object *ob, const ObjectHandle &handle)
{
ShadowCaster &caster = casters_.lookup_or_add_default(handle.object_key);
caster.used = true;
if (handle.recalc != 0 || !caster.initialized) {
caster.sync(ob);
}
}
/* Packs all shadow regions into a shadow atlas and update shadow casters linking / updates. */
void ShadowModule::end_sync(void)
{
const bool do_global_update = packing_changed_ || format_changed_;
Vector<ObjectKey, 0> deleted_keys;
/* Collect updated shadows bits. */
punctuals_used_bits_.resize(punctuals_.size());
punctuals_updated_bits_.resize(punctuals_.size());
for (auto shadow_index : punctuals_.index_range()) {
ShadowPunctual &shpoint = punctuals_[shadow_index];
punctuals_used_bits_.set_bit(shadow_index, shpoint.used);
punctuals_updated_bits_.set_bit(shadow_index, shpoint.do_update_tag);
shpoint.do_update_tag = false;
}
/* Search for deleted shadow casters or if shadow caster WAS in shadow radius. */
for (auto item : casters_.items()) {
ShadowCaster &caster = item.value;
if (!caster.used) {
deleted_keys.append(item.key);
caster.updated = true;
}
if (!do_global_update && caster.updated) {
/* If the shadow-caster has been deleted or updated, update previously intersect shadows. */
/* TODO(fclem): Bitmap iterator. */
for (auto shadow_index : caster.intersected_shadows_bits.index_range()) {
if (caster.intersected_shadows_bits[shadow_index] == false) {
continue;
}
punctuals_[shadow_index].do_update_persist = true;
}
}
if (caster.used) {
/* If the shadow-caster has been updated, update all shadow intersection bits and
* tag the shadows for update. If the shadow-caster has NOT been updated, only update shadow
* intersection bits for updated shadows. */
ShadowBitmap &shadow_bits = (caster.updated) ? punctuals_used_bits_ :
punctuals_updated_bits_;
caster.intersected_shadows_bits.resize(punctuals_.size());
/* If the shadow-caster has been updated, update intersected shadows and their bits. */
/* TODO(fclem): This part can be slow (max O(N²), min O(N)), optimize it with an acceleration
* structure. */
/* TODO(fclem): Bitmap iterator. */
for (auto shadow_index : shadow_bits.index_range()) {
if (shadow_bits[shadow_index] == false) {
continue;
}
ShadowPunctual &shadow = punctuals_[shadow_index];
const bool isect = ShadowCaster::intersect(caster, shadow);
caster.intersected_shadows_bits.set_bit(shadow_index, isect);
if (isect) {
punctuals_[shadow_index].do_update_persist = true;
}
}
/* TODO(fclem) Gather global bounds. */
}
caster.updated = false;
caster.used = false;
}
if (deleted_keys.size() > 0) {
inst_.sampling.reset();
}
for (auto key : deleted_keys) {
casters_.remove(key);
}
if (!do_global_update) {
return;
}
/* Pruned unused shadows at the end of the vector. */
while (punctuals_.size() > 0 && punctuals_.last().used == false) {
punctuals_.remove_last();
punctual_unused_count_--;
}
if (punctual_unused_first_ >= punctuals_.size()) {
/* First unused has been pruned. */
punctual_unused_first_ = INT_MAX;
}
Vector<AtlasRegion *> regions;
/* Collect regions. */
for (auto i : punctuals_.index_range()) {
ShadowPunctual &shpoint = punctuals_[i];
if (shpoint.used) {
/* TODO(fclem): Ideally, we could de-fragment the atlas using compute passes to move regions.
* For now, we update all shadows inside the atlas. */
shpoint.do_update_persist = true;
shpoint.is_visible = false;
regions.append(&shpoint);
}
}
/* Sort regions by size. */
/* TODO */
/* Pack regions. */
/* TODO(fclem) better packing. */
int row_height = cube_shadow_res_ * 6;
const int max_width = 8192;
ivec2 offset = {0, 0};
for (AtlasRegion *region : regions) {
if (offset[0] + region->extent[0] > max_width) {
offset[1] += row_height;
offset[0] = 0;
}
region->offset = offset;
offset[0] += region->extent[0];
}
ivec2 extent = {(offset.y > 0) ? max_width : offset.x,
(offset.y > 0) ? (offset.y + row_height) : row_height};
/* TODO(fclem) Make allocation safe. */
atlas_tx_.ensure(UNPACK2(extent), 1, shadow_format_);
atlas_fb_.ensure(GPU_ATTACHMENT_TEXTURE(atlas_tx_));
atlas_tx_ptr_ = atlas_tx_;
GPU_texture_compare_mode(atlas_tx_, true);
GPU_texture_filter_mode(atlas_tx_, true);
atlas_extent_ = {atlas_tx_.width(), atlas_tx_.height()};
format_changed_ = false;
packing_changed_ = false;
}
/* Update all shadow regions visible inside the view. */
void ShadowModule::update_visible(const DRWView *UNUSED(view))
{
bool force_update = false;
if (soft_shadows_enabled_ && (inst_.sampling.sample_get() != last_sample_)) {
force_update = true;
last_sample_ = inst_.sampling.sample_get();
}
else {
last_sample_ = 0;
}
DRW_stats_group_start("ShadowUpdate");
GPU_framebuffer_bind(atlas_fb_);
for (ShadowPunctual &shpoint : punctuals_) {
if (force_update) {
shpoint.do_update_persist = true;
}
if (shpoint.used && shpoint.do_update_persist && shpoint.is_visible) {
shpoint.render(inst_, MutableSpan(views_, 6));
}
}
DRW_stats_group_end();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Shadow Pass
*
* \{ */
void ShadowPass::sync(void)
{
{
/* Clear using a fullscreen quad.
* This is to avoid using scissors & to make it Metal compatible.
* Other possible way to do it:
* - Compute shader.
* - Blit blank region.
* - Render to tmp buffer. Copy to atlas later.
* - Dedicated Image View & framebuffer for each AtlasRegion.
*/
DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS;
clear_ps_ = DRW_pass_create("ShadowClear", state);
GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_CLEAR);
DRWShadingGroup *grp = DRW_shgroup_create(sh, clear_ps_);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
{
DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_SHADOW_OFFSET;
surface_ps_ = DRW_pass_create("ShadowSurface", state);
}
}
DRWShadingGroup *ShadowPass::material_add(::Material *UNUSED(material), GPUMaterial *gpumat)
{
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, surface_ps_);
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
return grp;
}
void ShadowPass::render(void)
{
DRW_draw_pass(clear_ps_);
DRW_draw_pass(surface_ps_);
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,357 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* The shadow module manages shadow update tagging & shadow rendering.
*/
#pragma once
#include "BLI_vector.hh"
#include "eevee_id_map.hh"
#include "eevee_material.hh"
#include "eevee_shader.hh"
#include "eevee_shader_shared.hh"
namespace blender::eevee {
class Instance;
class ShadowModule;
struct AtlasRegion {
ivec2 offset;
ivec2 extent;
};
/* -------------------------------------------------------------------- */
/** \name Shadow
*
* \{ */
struct ShadowPunctual : public AtlasRegion {
public:
/** Bounds to update during sync. Used to detect updates from shadow caster updates. */
BoundSphere bsphere;
/**
* Update tag to signal the shadow has changed. This is only the result of light changes.
* This flag is clear after sync.
**/
bool do_update_tag = true;
/** Persistent update tag until shadow map have been updated. */
bool do_update_persist = true;
/** The light module tag each shadow intersecting the current view. */
bool is_visible = false;
/** False if the object is only allocated, waiting to be reused. */
bool used = true;
private:
/** A wide cone has an aperture larger than 90° and covers more than 1 cubeface. */
bool is_wide_cone_ = false;
/** The shadow covers the whole sphere and all faces needs to be rendered. */
bool is_omni_ = false;
/** Clip distances. */
float near_, far_;
/** Area light size. */
float size_x_, size_y_;
/** Shape type. */
eLightType light_type_;
/** Random position on the light. In world space. */
vec3 random_offset_;
/** Random offset on the cube face for AA. In pixel. */
vec2 aa_offset_;
/** View space offset to apply to the shadow. */
float bias_;
/** Shadow size in pixels. */
int cube_res_;
/** Copy of normalized object matrix. Used to create DRWView. */
float4x4 object_mat_;
/** Full atlas size. Only updated after end_sync(). */
ShadowModule &shadows_;
enum eShadowCubeFace {
/* Ordering by culling order. If cone aperture is shallow, we cull the later view. */
Z_NEG = 0,
X_POS,
X_NEG,
Y_POS,
Y_NEG,
Z_POS,
};
/* Since we don't render to cubemap textures, order or orientation is not important
* as long as winding is left unchanged.
* So we minimize the complexity of `half_cubeface_projmat_get()` by rotating the cropped edge
* to always be on the top side. */
constexpr static const float shadow_face_mat[6][4][4] = {
{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}, /* Z_NEG */
{{0, 0, -1, 0}, {-1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* X_POS */
{{0, 0, 1, 0}, {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* X_NEG */
{{1, 0, 0, 0}, {0, 0, -1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* Y_POS */
{{-1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* Y_NEG */
{{1, 0, 0, 0}, {0, -1, 0, 0}, {0, 0, -1, 0}, {0, 0, 0, 1}}, /* Z_POS */
};
public:
ShadowPunctual(ShadowModule &shadows, int cube_res) : cube_res_(cube_res), shadows_(shadows){};
void sync(eLightType light_type,
const mat4 &object_mat,
float cone_aperture,
float near_clip,
float far_clip,
float bias);
void render(Instance &inst, MutableSpan<DRWView *> views);
void update_extent(int cube_res);
void random_position_on_shape_set(Instance &inst);
operator ShadowPunctualData();
private:
void cubeface_winmat_get(mat4 &winmat, bool half_opened);
void view_update(DRWView *&view, const mat4 &viewmat, const mat4 &winmat, eShadowCubeFace face);
void view_render(Instance &inst, DRWView *views, eShadowCubeFace face);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name ShadowCaster
*
* \{ */
/* TODO(fclem) Replace with a bitmap structure when we have one. */
class ShadowBitmap : public Vector<bool> {
public:
void set_bit(int64_t index, bool set)
{
(*this)[index] = set;
}
};
struct AABB {
vec3 center, halfdim;
void debug_draw(void)
{
BoundBox bb;
copy_v3_v3(bb.vec[0], center + halfdim * vec3(1, 1, 1));
copy_v3_v3(bb.vec[1], center + halfdim * vec3(-1, 1, 1));
copy_v3_v3(bb.vec[2], center + halfdim * vec3(-1, -1, 1));
copy_v3_v3(bb.vec[3], center + halfdim * vec3(1, -1, 1));
copy_v3_v3(bb.vec[4], center + halfdim * vec3(1, 1, -1));
copy_v3_v3(bb.vec[5], center + halfdim * vec3(-1, 1, -1));
copy_v3_v3(bb.vec[6], center + halfdim * vec3(-1, -1, -1));
copy_v3_v3(bb.vec[7], center + halfdim * vec3(1, -1, -1));
vec4 color = {1, 0, 0, 1};
DRW_debug_bbox(&bb, color);
}
};
struct ShadowCaster {
/** Bitmap of all shadows intersecting with this caster. Used to make update tagging faster. */
ShadowBitmap intersected_shadows_bits;
/** World space axis aligned bounding box. */
AABB aabb;
bool initialized = false;
bool used;
bool updated;
void sync(Object *ob)
{
BoundBox *bb = BKE_object_boundbox_get(ob);
vec3 min, max;
INIT_MINMAX(min, max);
for (int i = 0; i < 8; i++) {
float vec[3];
copy_v3_v3(vec, bb->vec[i]);
mul_m4_v3(ob->obmat, vec);
minmax_v3v3_v3(min, max, vec);
}
aabb.center = (min + max) * 0.5;
aabb.halfdim = vec3::abs(aabb.center - max);
initialized = true;
updated = true;
}
static bool intersect(ShadowCaster &caster, ShadowPunctual &shadow)
{
/* We are testing using a rougher AABB vs AABB test instead of full AABB vs Sphere. */
/* TODO test speed with AABB vs Sphere. */
for (int i = 0; i < 3; i++) {
if (fabsf(caster.aabb.center[i] - shadow.bsphere.center[i]) >
(caster.aabb.halfdim[i] + shadow.bsphere.radius)) {
return false;
}
}
return true;
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name ShadowModule
*
* Manages shadow atlas and shadow region datas.
* \{ */
class ShadowModule {
friend ShadowPunctual;
private:
Instance &inst_;
/** Map of shadow casters to track deletion & update of intersected shadows. */
Map<ObjectKey, ShadowCaster> casters_;
/** Global bounds that contains all shadow casters. */
// AABB casters_global_bounds_;
ShadowBitmap punctuals_used_bits_;
ShadowBitmap punctuals_updated_bits_;
Vector<ShadowPunctual> punctuals_;
/** First unused item in the vector for fast reallocating. */
int punctual_unused_first_ = INT_MAX;
/** Unused item count in the vector for fast reallocating. */
int punctual_unused_count_ = 0;
/** True if a shadow was deleted or allocated and we need to repack the data. */
bool packing_changed_ = true;
/** True if a shadow type was changed and all shadows need update. */
bool format_changed_ = true;
/** Full atlas size. Only updated after end_sync(). */
ivec2 atlas_extent_;
/** Used to detect sample change for soft shadows. */
uint64_t last_sample_ = 0;
/**
* TODO(fclem) These should be stored inside the Shadow objects instead.
* The issues is that only 32 DRWView can have effective culling data with the current
* implementation. So we try to reduce the number of DRWView allocated to avoid the slow path.
**/
DRWView *views_[6] = {nullptr};
/**
* For now we store every shadow in one atlas for simplicity.
* We could split by light batch later to improve vram usage.
*/
eevee::Texture atlas_tx_ = Texture("shadow_atlas_tx");
eevee::Framebuffer atlas_fb_ = Framebuffer("shadow_fb");
/** Scene immutable parameter. */
int cube_shadow_res_ = 64;
bool soft_shadows_enabled_ = false;
/** Default to invalid texture type. */
eGPUTextureFormat shadow_format_ = GPU_RGBA8;
GPUTexture *atlas_tx_ptr_;
public:
ShadowModule(Instance &inst) : inst_(inst){};
~ShadowModule(){};
void init(void);
void sync_caster(Object *ob, const ObjectHandle &handle);
void end_sync(void);
void update_visible(const DRWView *view);
int punctual_new(void)
{
packing_changed_ = true;
if (punctual_unused_count_ > 0) {
/* Reallocate unused item. */
int index = punctual_unused_first_;
punctual_unused_count_ -= 1;
punctual_unused_first_ = INT_MAX;
if (punctual_unused_count_ > 0) {
/* Find next first unused. */
for (auto i : IndexRange(index + 1, punctuals_.size() - index - 1)) {
if (punctuals_[i].used == false) {
punctual_unused_first_ = i;
break;
}
}
}
punctuals_[index].used = true;
return index;
}
punctuals_.append(ShadowPunctual(*this, cube_shadow_res_));
return punctuals_.size() - 1;
}
void punctual_discard(int index)
{
packing_changed_ = true;
punctual_unused_count_ += 1;
punctual_unused_first_ = min_ii(index, punctual_unused_first_);
punctuals_[index].used = false;
}
ShadowPunctual &punctual_get(int index)
{
return punctuals_[index];
}
GPUTexture **atlas_ref_get(void)
{
return &atlas_tx_ptr_;
}
private:
void remove_unused(void);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name ShadowPass
*
* A simple depth pass to which all shadow casters subscribe.
* \{ */
class ShadowPass {
private:
Instance &inst_;
DRWPass *clear_ps_ = nullptr;
DRWPass *surface_ps_ = nullptr;
public:
ShadowPass(Instance &inst) : inst_(inst){};
void sync(void);
DRWShadingGroup *material_add(::Material *blender_mat, GPUMaterial *gpumat);
void render(void);
};
/** \} */
} // namespace blender::eevee

View File

@@ -1,415 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2019, Blender Foundation.
*/
/** \file
* \ingroup EEVEE
*/
#include "BLI_string_utils.h"
#include "BLI_sys_types.h" /* bool */
#include "BKE_object.h"
#include "DEG_depsgraph_query.h"
#include "eevee_private.h"
#define SH_CASTER_ALLOC_CHUNK 32
void eevee_contact_shadow_setup(const Light *la, EEVEE_Shadow *evsh)
{
evsh->contact_dist = (la->mode & LA_SHAD_CONTACT) ? la->contact_dist : 0.0f;
evsh->contact_bias = 0.05f * la->contact_bias;
evsh->contact_thickness = la->contact_thickness;
}
void EEVEE_shadows_init(EEVEE_ViewLayerData *sldata)
{
const uint shadow_ubo_size = sizeof(EEVEE_Shadow) * MAX_SHADOW +
sizeof(EEVEE_ShadowCube) * MAX_SHADOW_CUBE +
sizeof(EEVEE_ShadowCascade) * MAX_SHADOW_CASCADE;
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
if (!sldata->lights) {
sldata->lights = MEM_callocN(sizeof(EEVEE_LightsInfo), "EEVEE_LightsInfo");
sldata->light_ubo = GPU_uniformbuf_create_ex(sizeof(EEVEE_Light) * MAX_LIGHT, NULL, "evLight");
sldata->shadow_ubo = GPU_uniformbuf_create_ex(shadow_ubo_size, NULL, "evShadow");
for (int i = 0; i < 2; i++) {
sldata->shcasters_buffers[i].bbox = MEM_mallocN(
sizeof(EEVEE_BoundBox) * SH_CASTER_ALLOC_CHUNK, __func__);
sldata->shcasters_buffers[i].update = BLI_BITMAP_NEW(SH_CASTER_ALLOC_CHUNK, __func__);
sldata->shcasters_buffers[i].alloc_count = SH_CASTER_ALLOC_CHUNK;
sldata->shcasters_buffers[i].count = 0;
}
sldata->lights->shcaster_frontbuffer = &sldata->shcasters_buffers[0];
sldata->lights->shcaster_backbuffer = &sldata->shcasters_buffers[1];
}
/* Flip buffers */
SWAP(EEVEE_ShadowCasterBuffer *,
sldata->lights->shcaster_frontbuffer,
sldata->lights->shcaster_backbuffer);
int sh_cube_size = scene_eval->eevee.shadow_cube_size;
int sh_cascade_size = scene_eval->eevee.shadow_cascade_size;
const bool sh_high_bitdepth = (scene_eval->eevee.flag & SCE_EEVEE_SHADOW_HIGH_BITDEPTH) != 0;
sldata->lights->soft_shadows = (scene_eval->eevee.flag & SCE_EEVEE_SHADOW_SOFT) != 0;
EEVEE_LightsInfo *linfo = sldata->lights;
if ((linfo->shadow_cube_size != sh_cube_size) ||
(linfo->shadow_high_bitdepth != sh_high_bitdepth)) {
BLI_assert((sh_cube_size > 0) && (sh_cube_size <= 4096));
DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_pool);
CLAMP(sh_cube_size, 1, 4096);
}
if ((linfo->shadow_cascade_size != sh_cascade_size) ||
(linfo->shadow_high_bitdepth != sh_high_bitdepth)) {
BLI_assert((sh_cascade_size > 0) && (sh_cascade_size <= 4096));
DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_pool);
CLAMP(sh_cascade_size, 1, 4096);
}
linfo->shadow_high_bitdepth = sh_high_bitdepth;
linfo->shadow_cube_size = sh_cube_size;
linfo->shadow_cascade_size = sh_cascade_size;
}
void EEVEE_shadows_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_LightsInfo *linfo = sldata->lights;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PassList *psl = vedata->psl;
EEVEE_ShadowCasterBuffer *backbuffer = linfo->shcaster_backbuffer;
EEVEE_ShadowCasterBuffer *frontbuffer = linfo->shcaster_frontbuffer;
frontbuffer->count = 0;
linfo->num_cube_layer = 0;
linfo->num_cascade_layer = 0;
linfo->cube_len = linfo->cascade_len = linfo->shadow_len = 0;
/* Shadow Casters: Reset flags. */
BLI_bitmap_set_all(backbuffer->update, true, backbuffer->alloc_count);
/* Is this one needed? */
BLI_bitmap_set_all(frontbuffer->update, false, frontbuffer->alloc_count);
INIT_MINMAX(linfo->shcaster_aabb.min, linfo->shcaster_aabb.max);
{
DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_SHADOW_OFFSET;
DRW_PASS_CREATE(psl->shadow_pass, state);
stl->g_data->shadow_shgrp = DRW_shgroup_create(EEVEE_shaders_shadow_sh_get(),
psl->shadow_pass);
}
}
/* Make that object update shadow casting lights inside its influence bounding box. */
void EEVEE_shadows_caster_register(EEVEE_ViewLayerData *sldata, Object *ob)
{
EEVEE_LightsInfo *linfo = sldata->lights;
EEVEE_ShadowCasterBuffer *backbuffer = linfo->shcaster_backbuffer;
EEVEE_ShadowCasterBuffer *frontbuffer = linfo->shcaster_frontbuffer;
bool update = true;
int id = frontbuffer->count;
/* Make sure shadow_casters is big enough. */
if (id >= frontbuffer->alloc_count) {
/* Double capacity to prevent exponential slowdown. */
frontbuffer->alloc_count *= 2;
frontbuffer->bbox = MEM_reallocN(frontbuffer->bbox,
sizeof(EEVEE_BoundBox) * frontbuffer->alloc_count);
BLI_BITMAP_RESIZE(frontbuffer->update, frontbuffer->alloc_count);
}
if (ob->base_flag & BASE_FROM_DUPLI) {
/* Duplis will always refresh the shadow-maps as if they were deleted each frame. */
/* TODO(fclem): fix this. */
update = true;
}
else {
EEVEE_ObjectEngineData *oedata = EEVEE_object_data_ensure(ob);
int past_id = oedata->shadow_caster_id;
oedata->shadow_caster_id = id;
/* Update flags in backbuffer. */
if (past_id > -1 && past_id < backbuffer->count) {
BLI_BITMAP_SET(backbuffer->update, past_id, oedata->need_update);
}
update = oedata->need_update;
oedata->need_update = false;
}
if (update) {
BLI_BITMAP_ENABLE(frontbuffer->update, id);
}
/* Update World AABB in frontbuffer. */
BoundBox *bb = BKE_object_boundbox_get(ob);
float min[3], max[3];
INIT_MINMAX(min, max);
for (int i = 0; i < 8; i++) {
float vec[3];
copy_v3_v3(vec, bb->vec[i]);
mul_m4_v3(ob->obmat, vec);
minmax_v3v3_v3(min, max, vec);
}
EEVEE_BoundBox *aabb = &frontbuffer->bbox[id];
/* Note that `*aabb` has not been initialized yet. */
add_v3_v3v3(aabb->center, min, max);
mul_v3_fl(aabb->center, 0.5f);
sub_v3_v3v3(aabb->halfdim, aabb->center, max);
aabb->halfdim[0] = fabsf(aabb->halfdim[0]);
aabb->halfdim[1] = fabsf(aabb->halfdim[1]);
aabb->halfdim[2] = fabsf(aabb->halfdim[2]);
minmax_v3v3_v3(linfo->shcaster_aabb.min, linfo->shcaster_aabb.max, min);
minmax_v3v3_v3(linfo->shcaster_aabb.min, linfo->shcaster_aabb.max, max);
frontbuffer->count++;
}
/* Used for checking if object is inside the shadow volume. */
static bool sphere_bbox_intersect(const BoundSphere *bs, const EEVEE_BoundBox *bb)
{
/* We are testing using a rougher AABB vs AABB test instead of full AABB vs Sphere. */
/* TODO: test speed with AABB vs Sphere. */
bool x = fabsf(bb->center[0] - bs->center[0]) <= (bb->halfdim[0] + bs->radius);
bool y = fabsf(bb->center[1] - bs->center[1]) <= (bb->halfdim[1] + bs->radius);
bool z = fabsf(bb->center[2] - bs->center[2]) <= (bb->halfdim[2] + bs->radius);
return x && y && z;
}
void EEVEE_shadows_update(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_LightsInfo *linfo = sldata->lights;
EEVEE_ShadowCasterBuffer *backbuffer = linfo->shcaster_backbuffer;
EEVEE_ShadowCasterBuffer *frontbuffer = linfo->shcaster_frontbuffer;
eGPUTextureFormat shadow_pool_format = (linfo->shadow_high_bitdepth) ? GPU_DEPTH_COMPONENT24 :
GPU_DEPTH_COMPONENT16;
/* Setup enough layers. */
/* Free textures if number mismatch. */
if (linfo->num_cube_layer != linfo->cache_num_cube_layer) {
DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_pool);
linfo->cache_num_cube_layer = linfo->num_cube_layer;
/* Update all lights. */
BLI_bitmap_set_all(&linfo->sh_cube_update[0], true, MAX_LIGHT);
}
if (linfo->num_cascade_layer != linfo->cache_num_cascade_layer) {
DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_pool);
linfo->cache_num_cascade_layer = linfo->num_cascade_layer;
}
if (!sldata->shadow_cube_pool) {
sldata->shadow_cube_pool = DRW_texture_create_2d_array(linfo->shadow_cube_size,
linfo->shadow_cube_size,
max_ii(1, linfo->num_cube_layer * 6),
shadow_pool_format,
DRW_TEX_FILTER | DRW_TEX_COMPARE,
NULL);
}
if (!sldata->shadow_cascade_pool) {
sldata->shadow_cascade_pool = DRW_texture_create_2d_array(linfo->shadow_cascade_size,
linfo->shadow_cascade_size,
max_ii(1, linfo->num_cascade_layer),
shadow_pool_format,
DRW_TEX_FILTER | DRW_TEX_COMPARE,
NULL);
}
if (sldata->shadow_fb == NULL) {
sldata->shadow_fb = GPU_framebuffer_create("shadow_fb");
}
/* Gather all light own update bits. to avoid costly intersection check. */
for (int j = 0; j < linfo->cube_len; j++) {
const EEVEE_Light *evli = linfo->light_data + linfo->shadow_cube_light_indices[j];
/* Setup shadow cube in UBO and tag for update if necessary. */
if (EEVEE_shadows_cube_setup(linfo, evli, effects->taa_current_sample - 1)) {
BLI_BITMAP_ENABLE(&linfo->sh_cube_update[0], j);
}
}
/* TODO(fclem): This part can be slow, optimize it. */
EEVEE_BoundBox *bbox = backbuffer->bbox;
BoundSphere *bsphere = linfo->shadow_bounds;
/* Search for deleted shadow casters or if shcaster WAS in shadow radius. */
for (int i = 0; i < backbuffer->count; i++) {
/* If the shadow-caster has been deleted or updated. */
if (BLI_BITMAP_TEST(backbuffer->update, i)) {
for (int j = 0; j < linfo->cube_len; j++) {
if (!BLI_BITMAP_TEST(&linfo->sh_cube_update[0], j)) {
if (sphere_bbox_intersect(&bsphere[j], &bbox[i])) {
BLI_BITMAP_ENABLE(&linfo->sh_cube_update[0], j);
}
}
}
}
}
/* Search for updates in current shadow casters. */
bbox = frontbuffer->bbox;
for (int i = 0; i < frontbuffer->count; i++) {
/* If the shadow-caster has been updated. */
if (BLI_BITMAP_TEST(frontbuffer->update, i)) {
for (int j = 0; j < linfo->cube_len; j++) {
if (!BLI_BITMAP_TEST(&linfo->sh_cube_update[0], j)) {
if (sphere_bbox_intersect(&bsphere[j], &bbox[i])) {
BLI_BITMAP_ENABLE(&linfo->sh_cube_update[0], j);
}
}
}
}
}
/* Resize shcasters buffers if too big. */
if (frontbuffer->alloc_count - frontbuffer->count > SH_CASTER_ALLOC_CHUNK) {
frontbuffer->alloc_count = (frontbuffer->count / SH_CASTER_ALLOC_CHUNK) *
SH_CASTER_ALLOC_CHUNK;
frontbuffer->alloc_count += (frontbuffer->count % SH_CASTER_ALLOC_CHUNK != 0) ?
SH_CASTER_ALLOC_CHUNK :
0;
frontbuffer->bbox = MEM_reallocN(frontbuffer->bbox,
sizeof(EEVEE_BoundBox) * frontbuffer->alloc_count);
BLI_BITMAP_RESIZE(frontbuffer->update, frontbuffer->alloc_count);
}
}
/* this refresh lights shadow buffers */
void EEVEE_shadows_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, DRWView *view)
{
EEVEE_LightsInfo *linfo = sldata->lights;
int saved_ray_type = sldata->common_data.ray_type;
/* Precompute all shadow/view test before rendering and trashing the culling cache. */
BLI_bitmap *cube_visible = BLI_BITMAP_NEW_ALLOCA(MAX_SHADOW_CUBE);
bool any_visible = linfo->cascade_len > 0;
for (int cube = 0; cube < linfo->cube_len; cube++) {
if (DRW_culling_sphere_test(view, linfo->shadow_bounds + cube)) {
BLI_BITMAP_ENABLE(cube_visible, cube);
any_visible = true;
}
}
if (any_visible) {
sldata->common_data.ray_type = EEVEE_RAY_SHADOW;
GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
}
DRW_stats_group_start("Cube Shadow Maps");
{
for (int cube = 0; cube < linfo->cube_len; cube++) {
if (BLI_BITMAP_TEST(cube_visible, cube) && BLI_BITMAP_TEST(linfo->sh_cube_update, cube)) {
EEVEE_shadows_draw_cubemap(sldata, vedata, cube);
}
}
}
DRW_stats_group_end();
DRW_stats_group_start("Cascaded Shadow Maps");
{
for (int cascade = 0; cascade < linfo->cascade_len; cascade++) {
EEVEE_shadows_draw_cascades(sldata, vedata, view, cascade);
}
}
DRW_stats_group_end();
DRW_view_set_active(view);
GPU_uniformbuf_update(sldata->shadow_ubo, &linfo->shadow_data); /* Update all data at once */
if (any_visible) {
sldata->common_data.ray_type = saved_ray_type;
GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
}
}
/* -------------------------------------------------------------------- */
/** \name Render Passes
* \{ */
void EEVEE_shadow_output_init(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
uint UNUSED(tot_samples))
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_PassList *psl = vedata->psl;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
/* Create FrameBuffer. */
const eGPUTextureFormat texture_format = GPU_R32F;
DRW_texture_ensure_fullscreen_2d(&txl->shadow_accum, texture_format, 0);
GPU_framebuffer_ensure_config(&fbl->shadow_accum_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->shadow_accum)});
/* Create Pass and shgroup. */
DRW_PASS_CREATE(psl->shadow_accum_pass,
DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ADD_FULL);
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_shadow_accum_sh_get(),
psl->shadow_accum_pass);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
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, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool);
DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool);
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
}
void EEVEE_shadow_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_PassList *psl = vedata->psl;
EEVEE_EffectsInfo *effects = vedata->stl->effects;
if (fbl->shadow_accum_fb != NULL) {
GPU_framebuffer_bind(fbl->shadow_accum_fb);
/* Clear texture. */
if (effects->taa_current_sample == 1) {
const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_framebuffer_clear_color(fbl->shadow_accum_fb, clear);
}
DRW_draw_pass(psl->shadow_accum_pass);
/* Restore */
GPU_framebuffer_bind(fbl->main_fb);
}
}
/** \} */

View File

@@ -1,437 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2019, Blender Foundation.
*/
/** \file
* \ingroup EEVEE
*/
#include "BLI_rect.h"
#include "BLI_sys_types.h" /* bool */
#include "BKE_object.h"
#include "eevee_private.h"
#include "BLI_rand.h" /* needs to be after for some reason. */
void EEVEE_shadows_cascade_add(EEVEE_LightsInfo *linfo, EEVEE_Light *evli, Object *ob)
{
if (linfo->cascade_len >= MAX_SHADOW_CASCADE) {
return;
}
const Light *la = (Light *)ob->data;
EEVEE_Shadow *sh_data = linfo->shadow_data + linfo->shadow_len;
EEVEE_ShadowCascade *csm_data = linfo->shadow_cascade_data + linfo->cascade_len;
EEVEE_ShadowCascadeRender *csm_render = linfo->shadow_cascade_render + linfo->cascade_len;
eevee_contact_shadow_setup(la, sh_data);
linfo->shadow_cascade_light_indices[linfo->cascade_len] = linfo->num_light;
evli->shadow_id = linfo->shadow_len++;
sh_data->type_data_id = linfo->cascade_len++;
csm_data->tex_id = linfo->num_cascade_layer;
csm_render->cascade_fade = la->cascade_fade;
csm_render->cascade_count = la->cascade_count;
csm_render->cascade_exponent = la->cascade_exponent;
csm_render->cascade_max_dist = la->cascade_max_dist;
csm_render->original_bias = max_ff(la->bias, 0.0f);
linfo->num_cascade_layer += la->cascade_count;
}
static void shadow_cascade_random_matrix_set(float mat[4][4], float radius, int sample_ofs)
{
float jitter[3];
#ifndef DEBUG_SHADOW_DISTRIBUTION
EEVEE_sample_ellipse(sample_ofs, mat[0], mat[1], radius, radius, jitter);
#else
for (int i = 0; i <= sample_ofs; i++) {
EEVEE_sample_ellipse(i, mat[0], mat[1], radius, radius, jitter);
float p[3];
add_v3_v3v3(p, jitter, mat[2]);
DRW_debug_sphere(p, 0.01f, (float[4]){1.0f, (sample_ofs == i) ? 1.0f : 0.0f, 0.0f, 1.0f});
}
#endif
add_v3_v3(mat[2], jitter);
orthogonalize_m4(mat, 2);
}
static double round_to_digits(double value, int digits)
{
double factor = pow(10.0, digits - ceil(log10(fabs(value))));
return round(value * factor) / factor;
}
static void frustum_min_bounding_sphere(const float corners[8][3],
float r_center[3],
float *r_radius)
{
#if 0 /* Simple solution but waste too much space. */
float minvec[3], maxvec[3];
/* compute the bounding box */
INIT_MINMAX(minvec, maxvec);
for (int i = 0; i < 8; i++) {
minmax_v3v3_v3(minvec, maxvec, corners[i]);
}
/* compute the bounding sphere of this box */
r_radius = len_v3v3(minvec, maxvec) * 0.5f;
add_v3_v3v3(r_center, minvec, maxvec);
mul_v3_fl(r_center, 0.5f);
#else
/* Find averaged center. */
zero_v3(r_center);
for (int i = 0; i < 8; i++) {
add_v3_v3(r_center, corners[i]);
}
mul_v3_fl(r_center, 1.0f / 8.0f);
/* Search the largest distance from the sphere center. */
*r_radius = 0.0f;
for (int i = 0; i < 8; i++) {
float rad = len_squared_v3v3(corners[i], r_center);
if (rad > *r_radius) {
*r_radius = rad;
}
}
/* TODO: try to reduce the radius further by moving the center.
* Remember we need a __stable__ solution! */
/* Try to reduce float imprecision leading to shimmering. */
*r_radius = (float)round_to_digits(sqrtf(*r_radius), 3);
#endif
}
static void eevee_shadow_cascade_setup(EEVEE_LightsInfo *linfo,
EEVEE_Light *evli,
DRWView *view,
float view_near,
float view_far,
int sample_ofs)
{
EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id;
EEVEE_ShadowCascade *csm_data = linfo->shadow_cascade_data + (int)shdw_data->type_data_id;
EEVEE_ShadowCascadeRender *csm_render = linfo->shadow_cascade_render +
(int)shdw_data->type_data_id;
int cascade_nbr = csm_render->cascade_count;
float cascade_fade = csm_render->cascade_fade;
float cascade_max_dist = csm_render->cascade_max_dist;
float cascade_exponent = csm_render->cascade_exponent;
float jitter_ofs[2];
double ht_point[2];
double ht_offset[2] = {0.0, 0.0};
const uint ht_primes[2] = {2, 3};
BLI_halton_2d(ht_primes, ht_offset, sample_ofs, ht_point);
/* Not really sure why we need 4.0 factor here. */
jitter_ofs[0] = (ht_point[0] * 2.0 - 1.0) * 4.0 / linfo->shadow_cascade_size;
jitter_ofs[1] = (ht_point[1] * 2.0 - 1.0) * 4.0 / linfo->shadow_cascade_size;
/* Camera Matrices */
float persinv[4][4], vp_projmat[4][4];
DRW_view_persmat_get(view, persinv, true);
DRW_view_winmat_get(view, vp_projmat, false);
bool is_persp = DRW_view_is_persp_get(view);
/* obmat = Object Space > World Space */
/* viewmat = World Space > View Space */
float(*viewmat)[4] = csm_render->viewmat;
eevee_light_matrix_get(evli, viewmat);
/* At this point, viewmat == normalize_m4(obmat) */
if (linfo->soft_shadows) {
shadow_cascade_random_matrix_set(viewmat, evli->radius, sample_ofs);
}
copy_m4_m4(csm_render->viewinv, viewmat);
invert_m4(viewmat);
copy_v3_v3(csm_data->shadow_vec, csm_render->viewinv[2]);
/* Compute near and far value based on all shadow casters cumulated AABBs. */
float sh_near = -1.0e30f, sh_far = 1.0e30f;
BoundBox shcaster_bounds;
BKE_boundbox_init_from_minmax(
&shcaster_bounds, linfo->shcaster_aabb.min, linfo->shcaster_aabb.max);
#ifdef DEBUG_CSM
float dbg_col1[4] = {1.0f, 0.5f, 0.6f, 1.0f};
DRW_debug_bbox(&shcaster_bounds, dbg_col1);
#endif
for (int i = 0; i < 8; i++) {
mul_m4_v3(viewmat, shcaster_bounds.vec[i]);
sh_near = max_ff(sh_near, shcaster_bounds.vec[i][2]);
sh_far = min_ff(sh_far, shcaster_bounds.vec[i][2]);
}
#ifdef DEBUG_CSM
float dbg_col2[4] = {0.5f, 1.0f, 0.6f, 1.0f};
float pts[2][3] = {{0.0, 0.0, sh_near}, {0.0, 0.0, sh_far}};
mul_m4_v3(csm_render->viewinv, pts[0]);
mul_m4_v3(csm_render->viewinv, pts[1]);
DRW_debug_sphere(pts[0], 1.0f, dbg_col1);
DRW_debug_sphere(pts[1], 1.0f, dbg_col2);
#endif
/* The rest of the function is assuming inverted Z. */
/* Add a little bias to avoid invalid matrices. */
sh_far = -(sh_far - 1e-3);
sh_near = -sh_near;
/* The technique consists into splitting
* the view frustum into several sub-frustum
* that are individually receiving one shadow map */
float csm_start, csm_end;
if (is_persp) {
csm_start = view_near;
csm_end = max_ff(view_far, -cascade_max_dist);
/* Avoid artifacts */
csm_end = min_ff(view_near, csm_end);
}
else {
csm_start = -view_far;
csm_end = view_far;
}
/* init near/far */
for (int c = 0; c < MAX_CASCADE_NUM; c++) {
csm_data->split_start[c] = csm_end;
csm_data->split_end[c] = csm_end;
}
/* Compute split planes */
float splits_start_ndc[MAX_CASCADE_NUM];
float splits_end_ndc[MAX_CASCADE_NUM];
{
/* Nearest plane */
float p[4] = {1.0f, 1.0f, csm_start, 1.0f};
/* TODO: we don't need full m4 multiply here */
mul_m4_v4(vp_projmat, p);
splits_start_ndc[0] = p[2];
if (is_persp) {
splits_start_ndc[0] /= p[3];
}
}
{
/* Farthest plane */
float p[4] = {1.0f, 1.0f, csm_end, 1.0f};
/* TODO: we don't need full m4 multiply here */
mul_m4_v4(vp_projmat, p);
splits_end_ndc[cascade_nbr - 1] = p[2];
if (is_persp) {
splits_end_ndc[cascade_nbr - 1] /= p[3];
}
}
csm_data->split_start[0] = csm_start;
csm_data->split_end[cascade_nbr - 1] = csm_end;
for (int c = 1; c < cascade_nbr; c++) {
/* View Space */
float linear_split = interpf(csm_end, csm_start, c / (float)cascade_nbr);
float exp_split = csm_start * powf(csm_end / csm_start, c / (float)cascade_nbr);
if (is_persp) {
csm_data->split_start[c] = interpf(exp_split, linear_split, cascade_exponent);
}
else {
csm_data->split_start[c] = linear_split;
}
csm_data->split_end[c - 1] = csm_data->split_start[c];
/* Add some overlap for smooth transition */
csm_data->split_start[c] = interpf((c > 1) ? csm_data->split_end[c - 2] :
csm_data->split_start[0],
csm_data->split_end[c - 1],
cascade_fade);
/* NDC Space */
{
float p[4] = {1.0f, 1.0f, csm_data->split_start[c], 1.0f};
/* TODO: we don't need full m4 multiply here */
mul_m4_v4(vp_projmat, p);
splits_start_ndc[c] = p[2];
if (is_persp) {
splits_start_ndc[c] /= p[3];
}
}
{
float p[4] = {1.0f, 1.0f, csm_data->split_end[c - 1], 1.0f};
/* TODO: we don't need full m4 multiply here */
mul_m4_v4(vp_projmat, p);
splits_end_ndc[c - 1] = p[2];
if (is_persp) {
splits_end_ndc[c - 1] /= p[3];
}
}
}
/* Set last cascade split fade distance into the first split_start. */
float prev_split = (cascade_nbr > 1) ? csm_data->split_end[cascade_nbr - 2] :
csm_data->split_start[0];
csm_data->split_start[0] = interpf(
prev_split, csm_data->split_end[cascade_nbr - 1], cascade_fade);
/* For each cascade */
for (int c = 0; c < cascade_nbr; c++) {
float(*projmat)[4] = csm_render->projmat[c];
/* Given 8 frustum corners */
float corners[8][3] = {
/* Near Cap */
{1.0f, -1.0f, splits_start_ndc[c]},
{-1.0f, -1.0f, splits_start_ndc[c]},
{-1.0f, 1.0f, splits_start_ndc[c]},
{1.0f, 1.0f, splits_start_ndc[c]},
/* Far Cap */
{1.0f, -1.0f, splits_end_ndc[c]},
{-1.0f, -1.0f, splits_end_ndc[c]},
{-1.0f, 1.0f, splits_end_ndc[c]},
{1.0f, 1.0f, splits_end_ndc[c]},
};
/* Transform them into world space */
for (int i = 0; i < 8; i++) {
mul_project_m4_v3(persinv, corners[i]);
}
float center[3];
frustum_min_bounding_sphere(corners, center, &(csm_render->radius[c]));
#ifdef DEBUG_CSM
float dbg_col[4] = {0.0f, 0.0f, 0.0f, 1.0f};
if (c < 3) {
dbg_col[c] = 1.0f;
}
DRW_debug_bbox((BoundBox *)&corners, dbg_col);
DRW_debug_sphere(center, csm_render->radius[c], dbg_col);
#endif
/* Project into light-space. */
mul_m4_v3(viewmat, center);
/* Snap projection center to nearest texel to cancel shimmering. */
float shadow_origin[2], shadow_texco[2];
/* Light to texture space. */
mul_v2_v2fl(
shadow_origin, center, linfo->shadow_cascade_size / (2.0f * csm_render->radius[c]));
/* Find the nearest texel. */
shadow_texco[0] = roundf(shadow_origin[0]);
shadow_texco[1] = roundf(shadow_origin[1]);
/* Compute offset. */
sub_v2_v2(shadow_texco, shadow_origin);
/* Texture to light space. */
mul_v2_fl(shadow_texco, (2.0f * csm_render->radius[c]) / linfo->shadow_cascade_size);
/* Apply offset. */
add_v2_v2(center, shadow_texco);
/* Expand the projection to cover frustum range */
rctf rect_cascade;
BLI_rctf_init_pt_radius(&rect_cascade, center, csm_render->radius[c]);
orthographic_m4(projmat,
rect_cascade.xmin,
rect_cascade.xmax,
rect_cascade.ymin,
rect_cascade.ymax,
sh_near,
sh_far);
/* Anti-Aliasing */
if (linfo->soft_shadows) {
add_v2_v2(projmat[3], jitter_ofs);
}
float viewprojmat[4][4];
mul_m4_m4m4(viewprojmat, projmat, viewmat);
mul_m4_m4m4(csm_data->shadowmat[c], texcomat, viewprojmat);
#ifdef DEBUG_CSM
DRW_debug_m4_as_bbox(viewprojmat, dbg_col, true);
#endif
}
/* Bias is in clip-space, divide by range. */
shdw_data->bias = csm_render->original_bias * 0.05f / fabsf(sh_far - sh_near);
shdw_data->near = sh_near;
shdw_data->far = sh_far;
}
static void eevee_ensure_cascade_views(EEVEE_ShadowCascadeRender *csm_render,
DRWView *view[MAX_CASCADE_NUM])
{
for (int i = 0; i < csm_render->cascade_count; i++) {
if (view[i] == NULL) {
view[i] = DRW_view_create(csm_render->viewmat, csm_render->projmat[i], NULL, NULL, NULL);
}
else {
DRW_view_update(view[i], csm_render->viewmat, csm_render->projmat[i], NULL, NULL);
}
}
}
void EEVEE_shadows_draw_cascades(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
DRWView *view,
int cascade_index)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_PrivateData *g_data = stl->g_data;
EEVEE_LightsInfo *linfo = sldata->lights;
EEVEE_Light *evli = linfo->light_data + linfo->shadow_cascade_light_indices[cascade_index];
EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id;
EEVEE_ShadowCascade *csm_data = linfo->shadow_cascade_data + (int)shdw_data->type_data_id;
EEVEE_ShadowCascadeRender *csm_render = linfo->shadow_cascade_render +
(int)shdw_data->type_data_id;
float near = DRW_view_near_distance_get(view);
float far = DRW_view_far_distance_get(view);
eevee_shadow_cascade_setup(linfo, evli, view, near, far, effects->taa_current_sample - 1);
/* Meh, Reusing the cube views. */
BLI_assert(MAX_CASCADE_NUM <= 6);
eevee_ensure_cascade_views(csm_render, g_data->cube_views);
/* Render shadow cascades */
/* Render cascade separately: seems to be faster for the general case.
* The only time it's more beneficial is when the CPU culling overhead
* outweigh the instancing overhead. which is rarely the case. */
for (int j = 0; j < csm_render->cascade_count; j++) {
DRW_view_set_active(g_data->cube_views[j]);
int layer = csm_data->tex_id + j;
GPU_framebuffer_texture_layer_attach(
sldata->shadow_fb, sldata->shadow_cascade_pool, 0, layer, 0);
GPU_framebuffer_bind(sldata->shadow_fb);
GPU_framebuffer_clear_depth(sldata->shadow_fb, 1.0f);
DRW_draw_pass(psl->shadow_pass);
}
}

View File

@@ -1,225 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2019, Blender Foundation.
*/
/** \file
* \ingroup EEVEE
*/
#include "eevee_private.h"
void EEVEE_shadows_cube_add(EEVEE_LightsInfo *linfo, EEVEE_Light *evli, Object *ob)
{
if (linfo->cube_len >= MAX_SHADOW_CUBE) {
return;
}
const Light *la = (Light *)ob->data;
EEVEE_Shadow *sh_data = linfo->shadow_data + linfo->shadow_len;
/* Always update dupli lights as EEVEE_LightEngineData is not saved.
* Same issue with dupli shadow casters. */
bool update = (ob->base_flag & BASE_FROM_DUPLI) != 0;
if (!update) {
EEVEE_LightEngineData *led = EEVEE_light_data_ensure(ob);
if (led->need_update) {
update = true;
led->need_update = false;
}
}
if (update) {
BLI_BITMAP_ENABLE(&linfo->sh_cube_update[0], linfo->cube_len);
}
sh_data->near = max_ff(la->clipsta, 1e-8f);
sh_data->bias = max_ff(la->bias * 0.05f, 0.0f);
eevee_contact_shadow_setup(la, sh_data);
/* Saving light bounds for later. */
BoundSphere *cube_bound = linfo->shadow_bounds + linfo->cube_len;
copy_v3_v3(cube_bound->center, evli->position);
cube_bound->radius = sqrt(1.0f / evli->invsqrdist);
linfo->shadow_cube_light_indices[linfo->cube_len] = linfo->num_light;
evli->shadow_id = linfo->shadow_len++;
sh_data->type_data_id = linfo->cube_len++;
/* Same as linfo->cube_len, no need to save. */
linfo->num_cube_layer++;
}
static void shadow_cube_random_position_set(const EEVEE_Light *evli,
int sample_ofs,
float ws_sample_pos[3])
{
float jitter[3];
#ifdef DEBUG_SHADOW_DISTRIBUTION
int i = 0;
start:
#else
int i = sample_ofs;
#endif
switch ((int)evli->light_type) {
case LA_AREA:
EEVEE_sample_rectangle(i, evli->rightvec, evli->upvec, evli->sizex, evli->sizey, jitter);
break;
case (int)LAMPTYPE_AREA_ELLIPSE:
EEVEE_sample_ellipse(i, evli->rightvec, evli->upvec, evli->sizex, evli->sizey, jitter);
break;
default:
EEVEE_sample_ball(i, evli->radius, jitter);
}
#ifdef DEBUG_SHADOW_DISTRIBUTION
float p[3];
add_v3_v3v3(p, jitter, ws_sample_pos);
DRW_debug_sphere(p, 0.01f, (float[4]){1.0f, (sample_ofs == i) ? 1.0f : 0.0f, 0.0f, 1.0f});
if (i++ < sample_ofs) {
goto start;
}
#endif
add_v3_v3(ws_sample_pos, jitter);
}
/* Return true if sample has changed and light needs to be updated. */
bool EEVEE_shadows_cube_setup(EEVEE_LightsInfo *linfo, const EEVEE_Light *evli, int sample_ofs)
{
EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id;
EEVEE_ShadowCube *cube_data = linfo->shadow_cube_data + (int)shdw_data->type_data_id;
eevee_light_matrix_get(evli, cube_data->shadowmat);
shdw_data->far = max_ff(sqrt(1.0f / evli->invsqrdist), 3e-4);
shdw_data->near = min_ff(shdw_data->near, shdw_data->far - 1e-4);
bool update = false;
if (linfo->soft_shadows) {
shadow_cube_random_position_set(evli, sample_ofs, cube_data->shadowmat[3]);
/* Update if position changes (avoid infinite update if soft shadows does not move).
* Other changes are caught by depsgraph tagging. This one is for update between samples. */
update = !compare_v3v3(cube_data->shadowmat[3], cube_data->position, 1e-10f);
/**
* Anti-Aliasing jitter: Add random rotation.
*
* The 2.0 factor is because texel angular size is not even across the cube-map,
* so we make the rotation range a bit bigger.
* This will not blur the shadow even if the spread is too big since we are just
* rotating the shadow cube-map.
* Note that this may be a rough approximation an may not converge to a perfectly
* smooth shadow (because sample distribution is quite non-uniform) but is enough
* in practice.
*/
/* NOTE: this has implication for spotlight rendering optimization
* (see EEVEE_shadows_draw_cubemap). */
float angular_texel_size = 2.0f * DEG2RADF(90) / (float)linfo->shadow_cube_size;
EEVEE_random_rotation_m4(sample_ofs, angular_texel_size, cube_data->shadowmat);
}
copy_v3_v3(cube_data->position, cube_data->shadowmat[3]);
invert_m4(cube_data->shadowmat);
return update;
}
static void eevee_ensure_cube_views(
float near, float far, int cube_res, const float viewmat[4][4], DRWView *view[6])
{
float winmat[4][4];
float side = near;
/* TODO: shadow-cube array. */
if (true) {
/* This half texel offset is used to ensure correct filtering between faces. */
/* FIXME: This exhibit float precision issue with lower cube_res.
* But it seems to be caused by the perspective_m4. */
side *= ((float)cube_res + 1.0f) / (float)(cube_res);
}
perspective_m4(winmat, -side, side, -side, side, near, far);
for (int i = 0; i < 6; i++) {
float tmp[4][4];
mul_m4_m4m4(tmp, cubefacemat[i], viewmat);
if (view[i] == NULL) {
view[i] = DRW_view_create(tmp, winmat, NULL, NULL, NULL);
}
else {
DRW_view_update(view[i], tmp, winmat, NULL, NULL);
}
}
}
/* Does a spot angle fits a single cubeface. */
static bool spot_angle_fit_single_face(const EEVEE_Light *evli)
{
/* alpha = spot/cone half angle. */
/* beta = scaled spot/cone half angle. */
float cos_alpha = evli->spotsize;
float sin_alpha = sqrtf(max_ff(0.0f, 1.0f - cos_alpha * cos_alpha));
float cos_beta = min_ff(cos_alpha / hypotf(cos_alpha, sin_alpha * evli->sizex),
cos_alpha / hypotf(cos_alpha, sin_alpha * evli->sizey));
/* Don't use 45 degrees because AA jitter can offset the face. */
return cos_beta > cosf(DEG2RADF(42.0f));
}
void EEVEE_shadows_draw_cubemap(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, int cube_index)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PrivateData *g_data = stl->g_data;
EEVEE_LightsInfo *linfo = sldata->lights;
EEVEE_Light *evli = linfo->light_data + linfo->shadow_cube_light_indices[cube_index];
EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id;
EEVEE_ShadowCube *cube_data = linfo->shadow_cube_data + (int)shdw_data->type_data_id;
eevee_ensure_cube_views(shdw_data->near,
shdw_data->far,
linfo->shadow_cube_size,
cube_data->shadowmat,
g_data->cube_views);
/* Render shadow cube */
/* Render 6 faces separately: seems to be faster for the general case.
* The only time it's more beneficial is when the CPU culling overhead
* outweigh the instancing overhead. which is rarely the case. */
for (int j = 0; j < 6; j++) {
/* Optimization: Only render the needed faces. */
/* Skip all but -Z face. */
if (evli->light_type == LA_SPOT && j != 5 && spot_angle_fit_single_face(evli)) {
continue;
}
/* Skip +Z face. */
if (evli->light_type != LA_LOCAL && j == 4) {
continue;
}
/* TODO(fclem): some cube sides can be invisible in the main views. Cull them. */
// if (frustum_intersect(g_data->cube_views[j], main_view))
// continue;
DRW_view_set_active(g_data->cube_views[j]);
int layer = cube_index * 6 + j;
GPU_framebuffer_texture_layer_attach(sldata->shadow_fb, sldata->shadow_cube_pool, 0, layer, 0);
GPU_framebuffer_bind(sldata->shadow_fb);
GPU_framebuffer_clear_depth(sldata->shadow_fb, 1.0f);
DRW_draw_pass(psl->shadow_pass);
}
BLI_BITMAP_SET(&linfo->sh_cube_update[0], cube_index, false);
}

View File

@@ -1,363 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Screen space subsurface scattering technique.
*/
#include "DRW_render.h"
#include "BLI_string_utils.h"
#include "DEG_depsgraph_query.h"
#include "GPU_capabilities.h"
#include "GPU_material.h"
#include "GPU_texture.h"
#include "eevee_private.h"
void EEVEE_subsurface_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *UNUSED(vedata))
{
}
void EEVEE_subsurface_draw_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_EffectsInfo *effects = vedata->stl->effects;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
const float *viewport_size = DRW_viewport_size_get();
const int fs_size[2] = {(int)viewport_size[0], (int)viewport_size[1]};
if (effects->enabled_effects & EFFECT_SSS) {
/* NOTE : we need another stencil because the stencil buffer is on the same texture
* as the depth buffer we are sampling from. This could be avoided if the stencil is
* a separate texture but that needs OpenGL 4.4 or ARB_texture_stencil8.
* OR OpenGL 4.3 / ARB_ES3_compatibility if using a render-buffer instead. */
effects->sss_stencil = DRW_texture_pool_query_2d(
fs_size[0], fs_size[1], GPU_DEPTH24_STENCIL8, &draw_engine_eevee_type);
effects->sss_blur = DRW_texture_pool_query_2d(
fs_size[0], fs_size[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type);
effects->sss_irradiance = DRW_texture_pool_query_2d(
fs_size[0], fs_size[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type);
effects->sss_radius = DRW_texture_pool_query_2d(
fs_size[0], fs_size[1], GPU_R16F, &draw_engine_eevee_type);
effects->sss_albedo = DRW_texture_pool_query_2d(
fs_size[0], fs_size[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type);
GPUTexture *stencil_tex = effects->sss_stencil;
if (GPU_depth_blitting_workaround()) {
/* Blitting stencil buffer does not work on macOS + Radeon Pro.
* Blit depth instead and use sss_stencil's depth as depth texture,
* and dtxl->depth as stencil mask. */
GPU_framebuffer_ensure_config(
&fbl->sss_blit_fb, {GPU_ATTACHMENT_TEXTURE(effects->sss_stencil), GPU_ATTACHMENT_NONE});
stencil_tex = dtxl->depth;
}
GPU_framebuffer_ensure_config(
&fbl->sss_blur_fb,
{GPU_ATTACHMENT_TEXTURE(stencil_tex), GPU_ATTACHMENT_TEXTURE(effects->sss_blur)});
GPU_framebuffer_ensure_config(
&fbl->sss_resolve_fb,
{GPU_ATTACHMENT_TEXTURE(stencil_tex), GPU_ATTACHMENT_TEXTURE(txl->color)});
GPU_framebuffer_ensure_config(
&fbl->sss_translucency_fb,
{GPU_ATTACHMENT_TEXTURE(stencil_tex), GPU_ATTACHMENT_TEXTURE(effects->sss_irradiance)});
GPU_framebuffer_ensure_config(&fbl->sss_clear_fb,
{GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(effects->sss_irradiance),
GPU_ATTACHMENT_TEXTURE(effects->sss_radius)});
if ((stl->g_data->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) != 0) {
EEVEE_subsurface_output_init(sldata, vedata, 0);
}
else {
GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_accum_fb);
DRW_TEXTURE_FREE_SAFE(txl->sss_accum);
}
}
else {
/* Cleanup to release memory */
GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_blur_fb);
GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_resolve_fb);
GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_clear_fb);
GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_accum_fb);
DRW_TEXTURE_FREE_SAFE(txl->sss_accum);
effects->sss_stencil = NULL;
effects->sss_blur = NULL;
effects->sss_irradiance = NULL;
effects->sss_radius = NULL;
}
}
void EEVEE_subsurface_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
EEVEE_Data *vedata,
uint UNUSED(tot_samples))
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
const eGPUTextureFormat texture_format_light = GPU_RGBA32F;
const bool texture_created = txl->sss_accum == NULL;
DRW_texture_ensure_fullscreen_2d(&txl->sss_accum, texture_format_light, 0);
GPUTexture *stencil_tex = effects->sss_stencil;
if (GPU_depth_blitting_workaround()) {
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
/* Blitting stencil buffer does not work on macOS + Radeon Pro.
* Blit depth instead and use sss_stencil's depth as depth texture,
* and dtxl->depth as stencil mask. */
stencil_tex = dtxl->depth;
}
GPU_framebuffer_ensure_config(
&fbl->sss_accum_fb,
{GPU_ATTACHMENT_TEXTURE(stencil_tex), GPU_ATTACHMENT_TEXTURE(txl->sss_accum)});
/* Clear texture.
* Due to the late initialization of the SSS it can happen that the `taa_current_sample` is
* already higher than one. This is noticeable when loading a file that has the diffuse light
* pass in look-dev mode active. `texture_created` will make sure that newly created textures
* are cleared. */
if (effects->taa_current_sample == 1 || texture_created) {
const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_framebuffer_bind(fbl->sss_accum_fb);
GPU_framebuffer_clear_color(fbl->sss_accum_fb, clear);
}
}
void EEVEE_subsurface_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
EEVEE_EffectsInfo *effects = vedata->stl->effects;
EEVEE_PassList *psl = vedata->psl;
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
effects->sss_sample_count = 1 + scene_eval->eevee.sss_samples * 2;
effects->sss_surface_count = 0;
common_data->sss_jitter_threshold = scene_eval->eevee.sss_jitter_threshold;
/** Screen Space SubSurface Scattering overview
* TODO
*/
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL;
DRW_PASS_CREATE(psl->sss_blur_ps, state);
DRW_PASS_CREATE(psl->sss_resolve_ps, state | DRW_STATE_BLEND_ADD);
DRW_PASS_CREATE(psl->sss_translucency_ps, state | DRW_STATE_BLEND_ADD);
}
void EEVEE_subsurface_add_pass(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
Material *ma,
DRWShadingGroup *shgrp,
struct GPUMaterial *gpumat)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
GPUTexture **depth_src = GPU_depth_blitting_workaround() ? &effects->sss_stencil : &dtxl->depth;
struct GPUTexture *sss_tex_profile = NULL;
struct GPUUniformBuf *sss_profile = GPU_material_sss_profile_get(
gpumat, stl->effects->sss_sample_count, &sss_tex_profile);
if (!sss_profile) {
BLI_assert_msg(0, "SSS pass requested but no SSS data was found");
return;
}
/* Limit of 8 bit stencil buffer. ID 255 is refraction. */
if (effects->sss_surface_count >= 254) {
/* TODO: display message. */
printf("Error: Too many different Subsurface shader in the scene.\n");
return;
}
int sss_id = ++(effects->sss_surface_count);
/* Make main pass output stencil mask. */
DRW_shgroup_stencil_mask(shgrp, sss_id);
{
eGPUSamplerState state = GPU_SAMPLER_DEFAULT;
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_subsurface_first_pass_sh_get(),
psl->sss_blur_ps);
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", depth_src);
DRW_shgroup_uniform_texture_ref_ex(grp, "sssIrradiance", &effects->sss_irradiance, state);
DRW_shgroup_uniform_texture_ref_ex(grp, "sssRadius", &effects->sss_radius, state);
DRW_shgroup_uniform_block(grp, "sssProfile", sss_profile);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_stencil_mask(grp, sss_id);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
grp = DRW_shgroup_create(EEVEE_shaders_subsurface_second_pass_sh_get(), psl->sss_resolve_ps);
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", depth_src);
DRW_shgroup_uniform_texture_ref_ex(grp, "sssIrradiance", &effects->sss_blur, state);
DRW_shgroup_uniform_texture_ref_ex(grp, "sssAlbedo", &effects->sss_albedo, state);
DRW_shgroup_uniform_texture_ref_ex(grp, "sssRadius", &effects->sss_radius, state);
DRW_shgroup_uniform_block(grp, "sssProfile", sss_profile);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_stencil_mask(grp, sss_id);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
if (ma->blend_flag & MA_BL_TRANSLUCENCY) {
DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_subsurface_translucency_sh_get(),
psl->sss_translucency_ps);
DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
DRW_shgroup_uniform_texture(grp, "sssTexProfile", sss_tex_profile);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", depth_src);
DRW_shgroup_uniform_texture_ref(grp, "sssRadius", &effects->sss_radius);
DRW_shgroup_uniform_texture_ref(grp, "sssShadowCubes", &sldata->shadow_cube_pool);
DRW_shgroup_uniform_texture_ref(grp, "sssShadowCascades", &sldata->shadow_cascade_pool);
DRW_shgroup_uniform_block(grp, "sssProfile", sss_profile);
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, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_stencil_mask(grp, sss_id);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
}
void EEVEE_subsurface_data_render(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
if ((effects->enabled_effects & EFFECT_SSS) != 0) {
const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
/* Clear sss_data texture only... can this be done in a more clever way? */
GPU_framebuffer_bind(fbl->sss_clear_fb);
GPU_framebuffer_clear_color(fbl->sss_clear_fb, clear);
GPU_framebuffer_ensure_config(&fbl->main_fb,
{GPU_ATTACHMENT_LEAVE,
GPU_ATTACHMENT_LEAVE,
GPU_ATTACHMENT_LEAVE,
GPU_ATTACHMENT_LEAVE,
GPU_ATTACHMENT_TEXTURE(effects->sss_irradiance),
GPU_ATTACHMENT_TEXTURE(effects->sss_radius),
GPU_ATTACHMENT_TEXTURE(effects->sss_albedo)});
GPU_framebuffer_bind(fbl->main_fb);
DRW_draw_pass(psl->material_sss_ps);
/* Restore */
GPU_framebuffer_ensure_config(&fbl->main_fb,
{GPU_ATTACHMENT_LEAVE,
GPU_ATTACHMENT_LEAVE,
GPU_ATTACHMENT_LEAVE,
GPU_ATTACHMENT_LEAVE,
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_NONE});
}
}
void EEVEE_subsurface_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_EffectsInfo *effects = stl->effects;
if ((effects->enabled_effects & EFFECT_SSS) != 0) {
const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
DRW_stats_group_start("SSS");
if (GPU_depth_blitting_workaround()) {
/* Copy depth channel */
GPU_framebuffer_blit(fbl->main_fb, 0, fbl->sss_blit_fb, 0, GPU_DEPTH_BIT);
}
else {
/* Copy stencil channel, could be avoided (see EEVEE_subsurface_init) */
GPU_framebuffer_blit(fbl->main_fb, 0, fbl->sss_blur_fb, 0, GPU_STENCIL_BIT);
}
if (!DRW_pass_is_empty(psl->sss_translucency_ps)) {
/* We sample the shadow-maps using normal sampler. We need to disable Comparison mode.
* TODO(fclem): avoid this by using sampler objects. */
GPU_texture_compare_mode(sldata->shadow_cube_pool, false);
GPU_texture_compare_mode(sldata->shadow_cascade_pool, false);
GPU_framebuffer_bind(fbl->sss_translucency_fb);
DRW_draw_pass(psl->sss_translucency_ps);
/* Reset original state. */
GPU_texture_compare_mode(sldata->shadow_cube_pool, true);
GPU_texture_compare_mode(sldata->shadow_cascade_pool, true);
}
/* 1. horizontal pass */
GPU_framebuffer_bind(fbl->sss_blur_fb);
GPU_framebuffer_clear_color(fbl->sss_blur_fb, clear);
DRW_draw_pass(psl->sss_blur_ps);
/* 2. vertical pass + Resolve */
GPU_framebuffer_texture_attach(fbl->sss_resolve_fb, txl->color, 0, 0);
GPU_framebuffer_bind(fbl->sss_resolve_fb);
DRW_draw_pass(psl->sss_resolve_ps);
GPU_framebuffer_bind(fbl->main_fb);
DRW_stats_group_end();
}
}
void EEVEE_subsurface_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
if (((effects->enabled_effects & EFFECT_SSS) != 0) && (fbl->sss_accum_fb != NULL)) {
/* Copy stencil channel, could be avoided (see EEVEE_subsurface_init) */
GPU_framebuffer_blit(fbl->main_fb, 0, fbl->sss_accum_fb, 0, GPU_STENCIL_BIT);
/* Only do vertical pass + Resolve */
GPU_framebuffer_bind(fbl->sss_accum_fb);
DRW_draw_pass(psl->sss_resolve_ps);
/* Restore */
GPU_framebuffer_bind(fbl->main_fb);
}
}

View File

@@ -1,440 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Temporal super sampling technique
*/
#include "DRW_render.h"
#include "ED_screen.h"
#include "BLI_rand.h"
#include "DEG_depsgraph_query.h"
#include "GPU_texture.h"
#include "eevee_private.h"
#define FILTER_CDF_TABLE_SIZE 512
static struct {
/* Pixel filter table: Only blackman-harris for now. */
bool inited;
float inverted_cdf[FILTER_CDF_TABLE_SIZE];
} e_data = {false}; /* Engine data */
static float UNUSED_FUNCTION(filter_box)(float UNUSED(x))
{
return 1.0f;
}
static float filter_blackman_harris(float x)
{
/* Hardcoded 1px footprint [-0.5..0.5]. We resize later. */
const float width = 1.0f;
x = 2.0f * M_PI * (x / width + 0.5f);
return 0.35875f - 0.48829f * cosf(x) + 0.14128f * cosf(2.0f * x) - 0.01168f * cosf(3.0f * x);
}
/* Compute cumulative distribution function of a discrete function. */
static void compute_cdf(float (*func)(float x), float cdf[FILTER_CDF_TABLE_SIZE])
{
cdf[0] = 0.0f;
/* Actual CDF evaluation. */
for (int u = 0; u < FILTER_CDF_TABLE_SIZE - 1; u++) {
float x = (float)(u + 1) / (float)(FILTER_CDF_TABLE_SIZE - 1);
cdf[u + 1] = cdf[u] + func(x - 0.5f); /* [-0.5..0.5]. We resize later. */
}
/* Normalize the CDF. */
for (int u = 0; u < FILTER_CDF_TABLE_SIZE - 1; u++) {
cdf[u] /= cdf[FILTER_CDF_TABLE_SIZE - 1];
}
/* Just to make sure. */
cdf[FILTER_CDF_TABLE_SIZE - 1] = 1.0f;
}
static void invert_cdf(const float cdf[FILTER_CDF_TABLE_SIZE],
float invert_cdf[FILTER_CDF_TABLE_SIZE])
{
for (int u = 0; u < FILTER_CDF_TABLE_SIZE; u++) {
float x = (float)u / (float)(FILTER_CDF_TABLE_SIZE - 1);
for (int i = 0; i < FILTER_CDF_TABLE_SIZE; i++) {
if (cdf[i] >= x) {
if (i == FILTER_CDF_TABLE_SIZE - 1) {
invert_cdf[u] = 1.0f;
}
else {
float t = (x - cdf[i]) / (cdf[i + 1] - cdf[i]);
invert_cdf[u] = ((float)i + t) / (float)(FILTER_CDF_TABLE_SIZE - 1);
}
break;
}
}
}
}
/* Evaluate a discrete function table with linear interpolation. */
static float eval_table(const float *table, float x)
{
CLAMP(x, 0.0f, 1.0f);
x = x * (FILTER_CDF_TABLE_SIZE - 1);
int index = min_ii((int)(x), FILTER_CDF_TABLE_SIZE - 1);
int nindex = min_ii(index + 1, FILTER_CDF_TABLE_SIZE - 1);
float t = x - index;
return (1.0f - t) * table[index] + t * table[nindex];
}
static void eevee_create_cdf_table_temporal_sampling(void)
{
float *cdf_table = MEM_mallocN(sizeof(float) * FILTER_CDF_TABLE_SIZE, "Eevee Filter CDF table");
float filter_width = 2.0f; /* Use a 2 pixel footprint by default. */
{
/* Use blackman-harris filter. */
filter_width *= 2.0f;
compute_cdf(filter_blackman_harris, cdf_table);
}
invert_cdf(cdf_table, e_data.inverted_cdf);
/* Scale and offset table. */
for (int i = 0; i < FILTER_CDF_TABLE_SIZE; i++) {
e_data.inverted_cdf[i] = (e_data.inverted_cdf[i] - 0.5f) * filter_width;
}
MEM_freeN(cdf_table);
e_data.inited = true;
}
void EEVEE_temporal_sampling_offset_calc(const double ht_point[2],
const float filter_size,
float r_offset[2])
{
r_offset[0] = eval_table(e_data.inverted_cdf, (float)(ht_point[0])) * filter_size;
r_offset[1] = eval_table(e_data.inverted_cdf, (float)(ht_point[1])) * filter_size;
}
void EEVEE_temporal_sampling_matrices_calc(EEVEE_EffectsInfo *effects, const double ht_point[2])
{
const float *viewport_size = DRW_viewport_size_get();
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
RenderData *rd = &scene->r;
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);
if (effects->taa_current_sample > 1) {
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 view-space. */
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 blurring 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. */
DRW_view_update_sub(effects->taa_view, viewmat, winmat);
}
/* Update the matrices based on the current sample.
* NOTE: `DRW_MAT_PERS` and `DRW_MAT_VIEW` needs to read the original matrices. */
void EEVEE_temporal_sampling_update_matrices(EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
EEVEE_EffectsInfo *effects = stl->effects;
double ht_point[2];
double ht_offset[2] = {0.0, 0.0};
const uint ht_primes[2] = {2, 3};
BLI_halton_2d(ht_primes, ht_offset, effects->taa_current_sample - 1, ht_point);
EEVEE_temporal_sampling_matrices_calc(effects, ht_point);
DRW_view_set_active(effects->taa_view);
}
void EEVEE_temporal_sampling_reset(EEVEE_Data *vedata)
{
vedata->stl->effects->taa_render_sample = 1;
vedata->stl->effects->taa_current_sample = 1;
}
void EEVEE_temporal_sampling_create_view(EEVEE_Data *vedata)
{
EEVEE_EffectsInfo *effects = vedata->stl->effects;
/* Create a sub view to disable clipping planes (if any). */
const DRWView *default_view = DRW_view_default_get();
float viewmat[4][4], winmat[4][4];
DRW_view_viewmat_get(default_view, viewmat, false);
DRW_view_winmat_get(default_view, winmat, false);
effects->taa_view = DRW_view_create_sub(default_view, viewmat, winmat);
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;
EEVEE_EffectsInfo *effects = stl->effects;
int repro_flag = 0;
if (!e_data.inited) {
eevee_create_cdf_table_temporal_sampling();
}
/**
* Reset for each "redraw". When rendering using ogl render,
* we accumulate the redraw inside the drawing loop in eevee_draw_scene().
*/
if (DRW_state_is_opengl_render()) {
effects->taa_render_sample = 1;
}
effects->bypass_drawing = false;
EEVEE_temporal_sampling_create_view(vedata);
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
if ((scene_eval->eevee.taa_samples != 1) || DRW_state_is_image_render()) {
float persmat[4][4];
if (!DRW_state_is_image_render() && (scene_eval->eevee.flag & SCE_EEVEE_TAA_REPROJECTION)) {
repro_flag = EFFECT_TAA_REPROJECT | EFFECT_VELOCITY_BUFFER | EFFECT_DEPTH_DOUBLE_BUFFER |
EFFECT_DOUBLE_BUFFER | EFFECT_POST_BUFFER;
effects->taa_reproject_sample = ((effects->taa_reproject_sample + 1) % 16);
}
/* Until we support reprojection, we need to make sure
* that the history buffer contains correct information. */
bool view_is_valid = stl->g_data->valid_double_buffer;
view_is_valid = view_is_valid && (stl->g_data->view_updated == false);
if (draw_ctx->evil_C != NULL) {
struct wmWindowManager *wm = CTX_wm_manager(draw_ctx->evil_C);
view_is_valid = view_is_valid && (ED_screen_animation_no_scrub(wm) == NULL);
}
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()) {
DRW_view_persmat_get(NULL, persmat, false);
view_is_valid = view_is_valid && compare_m4m4(persmat, effects->prev_drw_persmat, FLT_MIN);
}
/* Prevent ghosting from probe data. */
view_is_valid = view_is_valid && (effects->prev_drw_support == DRW_state_draw_support()) &&
(effects->prev_is_navigating == DRW_state_is_navigating());
effects->prev_drw_support = DRW_state_draw_support();
effects->prev_is_navigating = DRW_state_is_navigating();
if (((effects->taa_total_sample == 0) ||
(effects->taa_current_sample < effects->taa_total_sample)) ||
(!view_is_valid) || DRW_state_is_image_render()) {
if (view_is_valid) {
/* Viewport rendering updates the matrices in `eevee_draw_scene` */
if (!DRW_state_is_image_render()) {
effects->taa_current_sample += 1;
repro_flag = 0;
}
}
else {
effects->taa_current_sample = 1;
}
}
else {
const bool all_shaders_compiled = stl->g_data->queued_shaders_count_prev == 0;
/* Fix Texture painting (see T79370) and shader compilation (see T78520). */
if (DRW_state_is_navigating() || !all_shaders_compiled) {
effects->taa_current_sample = 1;
}
else {
effects->bypass_drawing = true;
}
}
return repro_flag | EFFECT_TAA | EFFECT_DOUBLE_BUFFER | EFFECT_DEPTH_DOUBLE_BUFFER |
EFFECT_POST_BUFFER;
}
effects->taa_current_sample = 1;
return repro_flag;
}
void EEVEE_temporal_sampling_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_EffectsInfo *effects = stl->effects;
if (effects->enabled_effects & EFFECT_TAA) {
struct GPUShader *sh = EEVEE_shaders_taa_resolve_sh_get(effects->enabled_effects);
DRW_PASS_CREATE(psl->taa_resolve, DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->taa_resolve);
DRW_shgroup_uniform_texture_ref(grp, "colorHistoryBuffer", &txl->taa_history);
DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &effects->source_buffer);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
if (effects->enabled_effects & EFFECT_TAA_REPROJECT) {
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
DRW_shgroup_uniform_mat4(grp, "prevViewProjectionMatrix", effects->prev_drw_persmat);
}
else {
DRW_shgroup_uniform_float(grp, "alpha", &effects->taa_alpha, 1);
}
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
}
}
void EEVEE_temporal_sampling_draw(EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
if ((effects->enabled_effects & (EFFECT_TAA | EFFECT_TAA_REPROJECT)) != 0) {
if ((effects->enabled_effects & EFFECT_TAA) != 0 && effects->taa_current_sample != 1) {
if (DRW_state_is_image_render()) {
/* See EEVEE_temporal_sampling_init() for more details. */
effects->taa_alpha = 1.0f / (float)(effects->taa_render_sample);
}
else {
effects->taa_alpha = 1.0f / (float)(effects->taa_current_sample);
}
GPU_framebuffer_bind(effects->target_buffer);
DRW_draw_pass(psl->taa_resolve);
/* Restore the depth from sample 1. */
GPU_framebuffer_blit(fbl->double_buffer_depth_fb, 0, fbl->main_fb, 0, GPU_DEPTH_BIT);
SWAP_BUFFERS_TAA();
}
else {
/* Save the depth buffer for the next frame.
* This saves us from doing anything special
* in the other mode engines. */
GPU_framebuffer_blit(fbl->main_fb, 0, fbl->double_buffer_depth_fb, 0, GPU_DEPTH_BIT);
/* Do reprojection for noise reduction */
/* TODO: do AA jitter if in only render view. */
if (!DRW_state_is_image_render() && (effects->enabled_effects & EFFECT_TAA_REPROJECT) != 0 &&
stl->g_data->valid_taa_history) {
GPU_framebuffer_bind(effects->target_buffer);
DRW_draw_pass(psl->taa_resolve);
SWAP_BUFFERS_TAA();
}
else {
struct GPUFrameBuffer *source_fb = (effects->target_buffer == fbl->main_color_fb) ?
fbl->effect_color_fb :
fbl->main_color_fb;
GPU_framebuffer_blit(source_fb, 0, fbl->taa_history_color_fb, 0, GPU_COLOR_BIT);
}
}
/* Make each loop count when doing a render. */
if (DRW_state_is_image_render()) {
effects->taa_render_sample += 1;
effects->taa_current_sample += 1;
}
else {
if (!DRW_state_is_playback() &&
((effects->taa_total_sample == 0) ||
(effects->taa_current_sample < effects->taa_total_sample))) {
DRW_viewport_request_redraw();
}
}
DRW_view_persmat_get(NULL, effects->prev_drw_persmat, false);
}
}

View File

@@ -0,0 +1,356 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* The velocity pass outputs motion vectors to use for either
* temporal re-projection or motion blur.
*
* It is the module that tracks the objects between frames updates.
*/
#include "BKE_duplilist.h"
#include "BKE_object.h"
#include "BLI_map.hh"
#include "DEG_depsgraph_query.h"
#include "DNA_rigidbody_types.h"
#include "eevee_instance.hh"
#include "eevee_renderpasses.hh"
#include "eevee_shader.hh"
#include "eevee_shader_shared.hh"
#include "eevee_velocity.hh"
#include "eevee_wrapper.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name VelocityModule
*
* \{ */
void VelocityModule::init(void)
{
if (inst_.is_viewport()) {
/* For viewport we sync when object is evaluated and we swap at init time.
* Use next step to store the current position. This one will become the previous step after
* next swapping. */
step_ = STEP_NEXT;
step_swap();
}
if (inst_.render && (inst_.render_passes.vector != nullptr)) {
/* No motion blur and the vector pass was requested. Do the step sync here. */
const Scene *scene = inst_.scene;
float initial_time = scene->r.cfra + scene->r.subframe;
step_sync(STEP_PREVIOUS, initial_time - 1.0f);
step_sync(STEP_NEXT, initial_time + 1.0f);
inst_.set_time(initial_time);
}
}
static void step_object_sync_render(void *velocity,
Object *ob,
RenderEngine *UNUSED(engine),
Depsgraph *UNUSED(depsgraph))
{
ObjectKey object_key(ob);
reinterpret_cast<VelocityModule *>(velocity)->step_object_sync(ob, object_key);
}
void VelocityModule::step_sync(eStep step, float time)
{
inst_.set_time(time);
step_ = step;
step_camera_sync();
DRW_render_object_iter(this, inst_.render, inst_.depsgraph, step_object_sync_render);
}
void VelocityModule::step_camera_sync()
{
if (!inst_.is_viewport()) {
inst_.camera.sync();
}
if (step_ == STEP_NEXT) {
camera_step.next = inst_.camera.data_get();
}
else if (step_ == STEP_PREVIOUS) {
camera_step.prev = inst_.camera.data_get();
}
}
/* Gather motion data from all objects in the scene. */
void VelocityModule::step_object_sync(Object *ob, ObjectKey &object_key)
{
if (!object_has_velocity(ob) && !object_is_deform(ob)) {
return;
}
auto add_cb = [&]() {
inst_.sampling.reset();
return new VelocityObjectBuf();
};
auto data = objects_steps.lookup_or_add_cb(object_key, add_cb);
if (step_ == STEP_NEXT) {
copy_m4_m4(data->next_object_mat, ob->obmat);
}
else if (step_ == STEP_PREVIOUS) {
copy_m4_m4(data->prev_object_mat, ob->obmat);
}
}
/* Moves next frame data to previous frame data. Nullify next frame data. */
void VelocityModule::step_swap(void)
{
for (VelocityObjectBuf *data : objects_steps.values()) {
copy_m4_m4(data->prev_object_mat, data->next_object_mat);
/* Important: This let us known if object is missing from the next time step. */
zero_m4(data->next_object_mat);
}
camera_step.prev = static_cast<CameraData>(camera_step.next);
}
void VelocityModule::begin_sync(void)
{
if (inst_.is_viewport()) {
step_camera_sync();
}
}
/* This is the end of the current frame sync. Not the step_sync. */
void VelocityModule::end_sync(void)
{
Vector<ObjectKey, 1> deleted_keys;
for (auto item : objects_steps.items()) {
/* Detect object deletion. Only do this on viewport as STEP_NEXT means current step. */
if (inst_.is_viewport() && is_zero_m4(item.value->next_object_mat)) {
deleted_keys.append(item.key);
delete item.value;
}
else {
item.value->push_update();
}
}
if (deleted_keys.size() > 0) {
inst_.sampling.reset();
}
for (auto key : deleted_keys) {
objects_steps.remove(key);
}
camera_step.prev.push_update();
camera_step.next.push_update();
}
bool VelocityModule::object_has_velocity(const Object *ob)
{
#if 0
RigidBodyOb *rbo = ob->rigidbody_object;
/* Active rigidbody objects only, as only those are affected by sim. */
const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE));
/* For now we assume dupli objects are moving. */
const bool is_dupli = (ob->base_flag & BASE_FROM_DUPLI) != 0;
const bool object_moves = is_dupli || has_rigidbody || BKE_object_moves_in_time(ob, true);
#else
UNUSED_VARS(ob);
/* BKE_object_moves_in_time does not work in some cases.
* Better detect non moving object after evaluation. */
const bool object_moves = true;
#endif
return object_moves;
}
bool VelocityModule::object_is_deform(const Object *ob)
{
RigidBodyOb *rbo = ob->rigidbody_object;
/* Active rigidbody objects only, as only those are affected by sim. */
const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE));
const bool is_deform = BKE_object_is_deform_modified(inst_.scene, (Object *)ob) ||
(has_rigidbody && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0);
return is_deform;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name VelocityPass
*
* Draws velocity data from VelocityModule module to a framebuffer / texture.
* \{ */
void VelocityPass::sync(void)
{
VelocityModule &velocity = inst_.velocity;
{
/* Outputs camera motion vector. */
/* TODO(fclem) Ideally, we should run this only where the motion vectors were not written.
* But without imageLoadStore, we cannot do that without another buffer. */
DRWState state = DRW_STATE_WRITE_COLOR;
DRW_PASS_CREATE(camera_ps_, state);
GPUShader *sh = inst_.shaders.static_shader_get(VELOCITY_CAMERA);
DRWShadingGroup *grp = DRW_shgroup_create(sh, camera_ps_);
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
DRW_shgroup_uniform_block(grp, "camera_prev_block", velocity.camera_step.prev.ubo_get());
DRW_shgroup_uniform_block(grp, "camera_next_block", velocity.camera_step.next.ubo_get());
DRW_shgroup_uniform_block(grp, "camera_curr_block", inst_.camera.ubo_get());
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
{
/* Animated objects are rendered and output the correct motion vector. */
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL;
DRW_PASS_CREATE(object_ps_, state);
{
GPUShader *sh = inst_.shaders.static_shader_get(VELOCITY_MESH);
DRWShadingGroup *grp = mesh_grp_ = DRW_shgroup_create(sh, object_ps_);
DRW_shgroup_uniform_block(grp, "camera_prev_block", velocity.camera_step.prev.ubo_get());
DRW_shgroup_uniform_block(grp, "camera_next_block", velocity.camera_step.next.ubo_get());
DRW_shgroup_uniform_block(grp, "camera_curr_block", inst_.camera.ubo_get());
}
}
}
void VelocityPass::mesh_add(Object *ob, ObjectHandle &handle)
{
if (inst_.is_viewport()) {
/* FIXME(fclem) As we are using original objects pointers, there is a chance the previous
* object key matches a totally different object if the scene was changed by user or python
* callback. In this case, we cannot correctly match objects between updates.
* What this means is that there will be incorrect motion vectors for these objects.
* We live with that until we have a correct way of identifying new objects. */
if (handle.recalc & ID_RECALC_TRANSFORM) {
inst_.velocity.step_object_sync(ob, handle.object_key);
}
}
VelocityObjectBuf **data_ptr = inst_.velocity.objects_steps.lookup_ptr(handle.object_key);
if (data_ptr == nullptr) {
return;
}
VelocityObjectBuf *data = *data_ptr;
GPUBatch *geom = DRW_cache_object_surface_get(ob);
if (geom == NULL) {
return;
}
if (!inst_.is_viewport()) {
/* Fill missing matrices if the object was hidden in previous or next frame. */
if (is_zero_m4(data->prev_object_mat)) {
copy_m4_m4(data->prev_object_mat, ob->obmat);
}
/* Avoid drawing object that has no motions since object_moves is always true. */
if (/* !mb_geom->use_deform && */ /* Object deformation can happen without transform. */
equals_m4m4(data->prev_object_mat, ob->obmat)) {
return;
}
}
else {
/* Fill missing matrices if the object was hidden in previous or next frame. */
if (is_zero_m4(data->prev_object_mat)) {
copy_m4_m4(data->prev_object_mat, ob->obmat);
}
if (is_zero_m4(data->next_object_mat)) {
copy_m4_m4(data->next_object_mat, ob->obmat);
}
// if (mb_geom->use_deform) {
// /* Keep to modify later (after init). */
// mb_geom->batch = geom;
// }
/* Avoid drawing object that has no motions since object_moves is always true. */
if (/* !mb_geom->use_deform && */ /* Object deformation can happen without transform. */
equals_m4m4(data->prev_object_mat, ob->obmat) &&
equals_m4m4(data->next_object_mat, ob->obmat)) {
return;
}
}
/* TODO(fclem) Use the same layout as modelBlock from draw so we can reuse the same offset
* and avoid the overhead of 1 shading group and one UBO per object. */
DRWShadingGroup *grp = DRW_shgroup_create_sub(mesh_grp_);
DRW_shgroup_uniform_block(grp, "object_block", data->ubo_get());
DRW_shgroup_call(grp, geom, ob);
}
void VelocityPass::render_objects(void)
{
DRW_draw_pass(object_ps_);
}
void VelocityPass::resolve_camera_motion(GPUTexture *depth_tx)
{
input_depth_tx_ = depth_tx;
DRW_draw_pass(camera_ps_);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name VelocityPass
*
* Draws velocity data from VelocityModule module to a framebuffer / texture.
* \{ */
void Velocity::sync(int extent[2])
{
/* HACK: View name should be unique and static.
* With this, we can reuse the same texture across views. */
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
/* TODO(fclem) Only allocate if needed. RG16F when only doing reprojection. */
velocity_camera_tx_ = DRW_texture_pool_query_2d(UNPACK2(extent), GPU_RGBA16F, owner);
/* TODO(fclem) Only allocate if needed. RG16F when only doing motion blur post fx in
* panoramic camera. */
velocity_view_tx_ = DRW_texture_pool_query_2d(UNPACK2(extent), GPU_RGBA16F, owner);
velocity_only_fb_.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(velocity_camera_tx_),
GPU_ATTACHMENT_TEXTURE(velocity_view_tx_));
}
void Velocity::render(GPUTexture *depth_tx)
{
DRW_stats_group_start("Velocity");
GPU_framebuffer_bind(velocity_only_fb_);
inst_.shading_passes.velocity.resolve_camera_motion(depth_tx);
velocity_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx),
GPU_ATTACHMENT_TEXTURE(velocity_camera_tx_),
GPU_ATTACHMENT_TEXTURE(velocity_view_tx_));
GPU_framebuffer_bind(velocity_fb_);
inst_.shading_passes.velocity.render_objects();
DRW_stats_group_end();
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,171 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* The velocity pass outputs motion vectors to use for either
* temporal re-projection or motion blur.
*
* It is the module that tracks the objects between frames updates.
*/
#pragma once
#include "BLI_map.hh"
#include "eevee_id_map.hh"
#include "eevee_renderpasses.hh"
#include "eevee_shader_shared.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name VelocityModule
*
* \{ */
/** Container for scene velocity data. */
class VelocityModule {
public:
enum eStep {
STEP_PREVIOUS = 0,
STEP_NEXT = 1,
STEP_CURRENT = 2,
};
/** Map an object key to a velocity data. */
Map<ObjectKey, VelocityObjectBuf *> objects_steps;
struct {
/** Copies of camera data. One for previous and one for next time step. */
StructBuffer<CameraData> prev, next;
} camera_step;
private:
Instance &inst_;
eStep step_;
public:
VelocityModule(Instance &inst) : inst_(inst){};
~VelocityModule()
{
for (VelocityObjectBuf *data : objects_steps.values()) {
delete data;
}
}
void init(void);
void step_camera_sync(void);
void step_sync(eStep step, float time);
/* Gather motion data from all objects in the scene. */
void step_object_sync(Object *ob, ObjectKey &ob_key);
/* Moves next frame data to previous frame data. Nullify next frame data. */
void step_swap(void);
void begin_sync(void);
void end_sync(void);
private:
bool object_has_velocity(const Object *ob);
bool object_is_deform(const Object *ob);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name VelocityPass
*
* Draws velocity data from VelocityModule module to a framebuffer / texture.
* \{ */
class VelocityPass {
private:
Instance &inst_;
DRWPass *object_ps_ = nullptr;
DRWPass *camera_ps_ = nullptr;
/** Shading groups from object_ps_ */
DRWShadingGroup *mesh_grp_;
/** Reference only. Not owned. */
GPUTexture *input_depth_tx_;
public:
VelocityPass(Instance &inst) : inst_(inst){};
void sync(void);
void mesh_add(Object *ob, ObjectHandle &handle);
void render_objects(void);
void resolve_camera_motion(GPUTexture *depth_tx);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Velocity
*
* \{ */
/**
* Per view module.
*/
class Velocity {
private:
Instance &inst_;
StringRefNull view_name_;
/** Owned resources. */
eevee::Framebuffer velocity_fb_;
eevee::Framebuffer velocity_only_fb_;
/** Draw resources. Not owned. */
GPUTexture *velocity_camera_tx_ = nullptr;
GPUTexture *velocity_view_tx_ = nullptr;
public:
Velocity(Instance &inst, const char *name) : inst_(inst), view_name_(name){};
~Velocity(){};
void sync(int extent[2]);
void render(GPUTexture *depth_tx);
/**
* Getters
**/
GPUTexture *view_vectors_get(void) const
{
return (velocity_view_tx_ != nullptr) ? velocity_view_tx_ : velocity_camera_tx_;
}
GPUTexture *camera_vectors_get(void) const
{
return velocity_camera_tx_;
}
};
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,259 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* A view is either:
* - The entire main view.
* - A fragment of the main view (for panoramic projections).
* - A shadow map view.
* - A lightprobe view (either planar, cubemap, irradiance grid).
*
* A pass is a container for scene data. It is view agnostic but has specific logic depending on
* its type. Passes are shared between views.
*/
#include "BKE_global.h"
#include "DRW_render.h"
#include "eevee_instance.hh"
#include "eevee_view.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name ShadingView
* \{ */
void ShadingView::init()
{
dof_.init();
mb_.init();
}
void ShadingView::sync(ivec2 render_extent_)
{
if (inst_.camera.is_panoramic()) {
int64_t render_pixel_count = render_extent_.x * (int64_t)render_extent_.y;
/* Divide pixel count between the 6 views. Rendering to a square target. */
extent_[0] = extent_[1] = ceilf(sqrtf(1 + (render_pixel_count / 6)));
/* TODO(fclem) Clip unused views heres. */
is_enabled_ = true;
}
else {
extent_ = render_extent_;
/* Only enable -Z view. */
is_enabled_ = (StringRefNull(name_) == "negZ_view");
}
if (!is_enabled_) {
return;
}
/* Create views. */
const CameraData &data = inst_.camera.data_get();
float viewmat[4][4], winmat[4][4];
const float(*viewmat_p)[4] = viewmat, (*winmat_p)[4] = winmat;
if (inst_.camera.is_panoramic()) {
/* TODO(fclem) Overscans. */
/* For now a mandatory 5% overscan for DoF. */
float side = data.clip_near * 1.05f;
float near = data.clip_near;
float far = data.clip_far;
perspective_m4(winmat, -side, side, -side, side, near, far);
mul_m4_m4m4(viewmat, face_matrix_, data.viewmat);
}
else {
viewmat_p = data.viewmat;
winmat_p = data.winmat;
}
main_view_ = DRW_view_create(viewmat_p, winmat_p, nullptr, nullptr, nullptr);
sub_view_ = DRW_view_create_sub(main_view_, viewmat_p, winmat_p);
render_view_ = DRW_view_create_sub(main_view_, viewmat_p, winmat_p);
dof_.sync(winmat_p, extent_);
mb_.sync(extent_);
velocity_.sync(extent_);
{
/* Query temp textures and create framebuffers. */
/* HACK: View name should be unique and static.
* With this, we can reuse the same texture across views. */
DrawEngineType *owner = (DrawEngineType *)name_;
depth_tx_ = DRW_texture_pool_query_2d(UNPACK2(extent_), GPU_DEPTH24_STENCIL8, owner);
combined_tx_ = DRW_texture_pool_query_2d(UNPACK2(extent_), GPU_RGBA16F, owner);
/* TODO(fclem) Only allocate if needed. */
postfx_tx_ = DRW_texture_pool_query_2d(UNPACK2(extent_), GPU_RGBA16F, owner);
view_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx_), GPU_ATTACHMENT_TEXTURE(combined_tx_));
/* Reuse postfx_tx_. */
debug_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(postfx_tx_));
gbuffer_.sync(depth_tx_, combined_tx_, owner);
}
}
void ShadingView::render(void)
{
if (!is_enabled_) {
return;
}
float color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
update_view();
DRW_stats_group_start(name_);
DRW_view_set_active(render_view_);
GPU_framebuffer_bind(view_fb_);
GPU_framebuffer_clear_color_depth(view_fb_, color, 1.0f);
if (inst_.lookdev.render_background() == false) {
inst_.shading_passes.background.render();
}
inst_.shading_passes.deferred.render(gbuffer_, view_fb_);
inst_.lightprobes.draw_cache_display();
inst_.lookdev.render_overlay(view_fb_);
inst_.shading_passes.forward.render();
velocity_.render(depth_tx_);
if (inst_.render_passes.vector) {
inst_.render_passes.vector->accumulate(velocity_.camera_vectors_get(), sub_view_);
}
GPUTexture *final_radiance_tx = render_post(combined_tx_);
/* TODO(fclem) Have a special renderpass for this. */
if (G.debug_value == 3) {
GPU_framebuffer_bind(debug_fb_);
inst_.shading_passes.debug_culling.render(depth_tx_);
// inst_.render_passes.debug_culling->accumulate(debug_tx_, sub_view_);
inst_.render_passes.combined->accumulate(postfx_tx_, sub_view_);
}
else if (inst_.render_passes.combined) {
inst_.render_passes.combined->accumulate(final_radiance_tx, sub_view_);
}
if (inst_.render_passes.depth) {
inst_.render_passes.depth->accumulate(depth_tx_, sub_view_);
}
DRW_stats_group_end();
}
GPUTexture *ShadingView::render_post(GPUTexture *input_tx)
{
GPUTexture *velocity_tx = velocity_.view_vectors_get();
GPUTexture *output_tx = postfx_tx_;
/* Swapping is done internally. Actual output is set to the next input. */
dof_.render(depth_tx_, &input_tx, &output_tx);
mb_.render(depth_tx_, velocity_tx, &input_tx, &output_tx);
return input_tx;
}
void ShadingView::update_view(void)
{
float viewmat[4][4], winmat[4][4];
DRW_view_viewmat_get(main_view_, viewmat, false);
DRW_view_winmat_get(main_view_, winmat, false);
/* Anti-Aliasing / Super-Sampling jitter. */
float jitter_u = 2.0f * (inst_.sampling.rng_get(SAMPLING_FILTER_U) - 0.5f) / extent_[0];
float jitter_v = 2.0f * (inst_.sampling.rng_get(SAMPLING_FILTER_V) - 0.5f) / extent_[1];
window_translate_m4(winmat, winmat, jitter_u, jitter_v);
DRW_view_update_sub(sub_view_, viewmat, winmat);
/* FIXME(fclem): The offset may be is noticeably large and the culling might make object pop
* out of the blurring radius. To fix this, use custom enlarged culling matrix. */
dof_.jitter_apply(winmat, viewmat);
DRW_view_update_sub(render_view_, viewmat, winmat);
inst_.lightprobes.set_view(render_view_, extent_);
inst_.lights.set_view(render_view_, extent_);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name LightProbeView
* \{ */
void LightProbeView::sync(Texture &color_tx,
Texture &depth_tx,
const mat4 winmat,
const mat4 viewmat,
bool is_only_background)
{
mat4 facemat;
mul_m4_m4m4(facemat, face_matrix_, viewmat);
is_only_background_ = is_only_background;
extent_ = ivec2(color_tx.width());
view_ = DRW_view_create(facemat, winmat, nullptr, nullptr, nullptr);
view_fb_.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer_),
GPU_ATTACHMENT_TEXTURE_LAYER(color_tx, layer_));
if (!is_only_background_) {
/* Query temp textures and create framebuffers. */
/* HACK: View name should be unique and static.
* With this, we can reuse the same texture across views. */
DrawEngineType *owner = (DrawEngineType *)name_;
gbuffer_.sync(depth_tx, color_tx, owner, layer_);
}
}
void LightProbeView::render(void)
{
if (!is_only_background_) {
inst_.lightprobes.set_view(view_, extent_);
inst_.lights.set_view(view_, extent_, false);
}
DRW_stats_group_start(name_);
DRW_view_set_active(view_);
GPU_framebuffer_bind(view_fb_);
inst_.shading_passes.background.render();
if (!is_only_background_) {
GPU_framebuffer_clear_depth(view_fb_, 1.0f);
inst_.shading_passes.deferred.render(gbuffer_, view_fb_);
inst_.shading_passes.forward.render();
}
DRW_stats_group_end();
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,216 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* A view is either:
* - The entire main view.
* - A fragment of the main view (for panoramic projections).
* - A shadow map view.
* - A lightprobe view (either planar, cubemap, irradiance grid).
*
* A pass is a container for scene data. It is view agnostic but has specific logic depending on
* its type. Passes are shared between views.
*/
#pragma once
#include "GPU_framebuffer.h"
#include "GPU_texture.h"
#include "DRW_render.h"
#include "eevee_depth_of_field.hh"
#include "eevee_motion_blur.hh"
#include "eevee_renderpasses.hh"
#include "eevee_shader.hh"
#include "eevee_shading.hh"
#include "eevee_velocity.hh"
namespace blender::eevee {
class Instance;
/* -------------------------------------------------------------------- */
/** \name ShadingView
*
* Render the scene and fill all render passes data.
* \{ */
class ShadingView {
private:
Instance &inst_;
/** Static srting pointer. Used as debug name and as UUID for texture pool. */
const char *name_;
/** Matrix to apply to the viewmat. */
const float (*face_matrix_)[4];
/** Post-fx modules. */
DepthOfField dof_;
MotionBlur mb_;
Velocity velocity_;
GBuffer gbuffer_;
/** Owned resources. */
eevee::Framebuffer view_fb_;
eevee::Framebuffer debug_fb_;
/** Draw resources. Not owned. */
GPUTexture *combined_tx_ = nullptr;
GPUTexture *depth_tx_ = nullptr;
GPUTexture *postfx_tx_ = nullptr;
/** Main views is created from the camera (or is from the viewport). It is not jittered. */
DRWView *main_view_ = nullptr;
/** Sub views is jittered versions or the main views. This allows jitter updates without trashing
* the visibility culling cache. */
DRWView *sub_view_ = nullptr;
/** Same as sub_view_ but has Depth Of Field jitter applied. */
DRWView *render_view_ = nullptr;
/** Render size of the view. Can change between scene sample eval. */
ivec2 extent_ = {-1, -1};
bool is_enabled_ = false;
public:
ShadingView(Instance &inst, const char *name, const float (*face_matrix)[4])
: inst_(inst),
name_(name),
face_matrix_(face_matrix),
dof_(inst, name),
mb_(inst, name),
velocity_(inst, name){};
~ShadingView(){};
void init(void);
void sync(ivec2 render_extent_);
void render(void);
GPUTexture *render_post(GPUTexture *input_tx);
private:
void update_view(void);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name LightProbeView
*
* Render the scene to a lightprobe target cubemap face.
* \{ */
class LightProbeView {
private:
Instance &inst_;
/** Static string pointer. Used as debug name and as UUID for texture pool. */
const char *name_;
/** Matrix to apply to the viewmat. */
const float (*face_matrix_)[4];
/** GBuffer for deferred passes. */
GBuffer gbuffer_;
/** Owned resources. */
Framebuffer view_fb_;
/** DRWView of this face. */
DRWView *view_ = nullptr;
/** Render size of the view. */
ivec2 extent_ = {-1, -1};
int layer_ = 0;
bool is_only_background_ = false;
public:
LightProbeView(Instance &inst, const char *name, const float (*face_matrix)[4], int layer_)
: inst_(inst), name_(name), face_matrix_(face_matrix), layer_(layer_){};
~LightProbeView(){};
void sync(Texture &color_tx,
Texture &depth_tx,
const mat4 winmat,
const mat4 viewmat,
bool is_only_background);
void render(void);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Main View
*
* \{ */
/**
* Container for all views needed to render the final image.
* We might need up to 6 views for panoramic cameras.
*/
class MainView {
private:
ShadingView shading_views_[6];
/** Internal render size. */
int render_extent_[2];
public:
MainView(Instance &inst)
: shading_views_{{inst, "posX_view", cubeface_mat[0]},
{inst, "negX_view", cubeface_mat[1]},
{inst, "posY_view", cubeface_mat[2]},
{inst, "negY_view", cubeface_mat[3]},
{inst, "posZ_view", cubeface_mat[4]},
{inst, "negZ_view", cubeface_mat[5]}}
{
}
void init(const ivec2 full_extent_)
{
/* TODO(fclem) parameter hidden in experimental. We need to figure out mipmap bias to preserve
* texture crispiness. */
float resolution_scale = 1.0f;
for (int i = 0; i < 2; i++) {
render_extent_[i] = max_ii(1, roundf(full_extent_[i] * resolution_scale));
}
for (auto i : IndexRange(ARRAY_SIZE(shading_views_))) {
shading_views_[i].init();
}
}
void sync(void)
{
for (auto i : IndexRange(ARRAY_SIZE(shading_views_))) {
shading_views_[i].sync(render_extent_);
}
}
void render(void)
{
for (auto i : IndexRange(ARRAY_SIZE(shading_views_))) {
shading_views_[i].render();
}
}
};
/** \} */
} // namespace blender::eevee

View File

@@ -1,856 +0,0 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Volumetric effects rendering using frostbite approach.
*/
#include "DRW_render.h"
#include "BLI_listbase.h"
#include "BLI_rand.h"
#include "BLI_string_utils.h"
#include "DNA_fluid_types.h"
#include "DNA_object_force_types.h"
#include "DNA_volume_types.h"
#include "DNA_world_types.h"
#include "BKE_fluid.h"
#include "BKE_global.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_volume.h"
#include "BKE_volume_render.h"
#include "ED_screen.h"
#include "DEG_depsgraph_query.h"
#include "GPU_capabilities.h"
#include "GPU_material.h"
#include "GPU_texture.h"
#include "eevee_private.h"
static struct {
GPUTexture *depth_src;
GPUTexture *dummy_zero;
GPUTexture *dummy_one;
GPUTexture *dummy_flame;
GPUTexture *dummy_scatter;
GPUTexture *dummy_transmit;
/* List of all fluid simulation / smoke domains rendered within this frame. */
ListBase smoke_domains;
} e_data = {NULL}; /* Engine data */
static void eevee_create_textures_volumes(void)
{
const float zero[4] = {0.0f, 0.0f, 0.0f, 0.0f};
e_data.dummy_zero = DRW_texture_create_3d(1, 1, 1, GPU_RGBA8, DRW_TEX_WRAP, zero);
const float one[4] = {1.0f, 1.0f, 1.0f, 1.0f};
e_data.dummy_one = DRW_texture_create_3d(1, 1, 1, GPU_RGBA8, DRW_TEX_WRAP, one);
const float flame = 0.0f;
e_data.dummy_flame = DRW_texture_create_3d(1, 1, 1, GPU_R8, DRW_TEX_WRAP, &flame);
}
static GPUTexture *eevee_volume_default_texture(eGPUVolumeDefaultValue default_value)
{
switch (default_value) {
case GPU_VOLUME_DEFAULT_0:
return e_data.dummy_zero;
case GPU_VOLUME_DEFAULT_1:
return e_data.dummy_one;
}
return e_data.dummy_zero;
}
void EEVEE_volumes_set_jitter(EEVEE_ViewLayerData *sldata, uint current_sample)
{
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
double ht_point[3];
double ht_offset[3] = {0.0, 0.0};
const uint ht_primes[3] = {3, 7, 2};
BLI_halton_3d(ht_primes, ht_offset, current_sample, ht_point);
common_data->vol_jitter[0] = (float)ht_point[0];
common_data->vol_jitter[1] = (float)ht_point[1];
common_data->vol_jitter[2] = (float)ht_point[2];
}
void EEVEE_volumes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
const float *viewport_size = DRW_viewport_size_get();
const int tile_size = scene_eval->eevee.volumetric_tile_size;
/* Find Froxel Texture resolution. */
int tex_size[3];
tex_size[0] = (int)ceilf(fmaxf(1.0f, viewport_size[0] / (float)tile_size));
tex_size[1] = (int)ceilf(fmaxf(1.0f, viewport_size[1] / (float)tile_size));
tex_size[2] = max_ii(scene_eval->eevee.volumetric_samples, 1);
common_data->vol_coord_scale[0] = viewport_size[0] / (float)(tile_size * tex_size[0]);
common_data->vol_coord_scale[1] = viewport_size[1] / (float)(tile_size * tex_size[1]);
common_data->vol_coord_scale[2] = 1.0f / viewport_size[0];
common_data->vol_coord_scale[3] = 1.0f / viewport_size[1];
/* TODO: compute snap to maxZBuffer for clustered rendering. */
if ((common_data->vol_tex_size[0] != tex_size[0]) ||
(common_data->vol_tex_size[1] != tex_size[1]) ||
(common_data->vol_tex_size[2] != tex_size[2])) {
DRW_TEXTURE_FREE_SAFE(txl->volume_prop_scattering);
DRW_TEXTURE_FREE_SAFE(txl->volume_prop_extinction);
DRW_TEXTURE_FREE_SAFE(txl->volume_prop_emission);
DRW_TEXTURE_FREE_SAFE(txl->volume_prop_phase);
DRW_TEXTURE_FREE_SAFE(txl->volume_scatter);
DRW_TEXTURE_FREE_SAFE(txl->volume_transmit);
DRW_TEXTURE_FREE_SAFE(txl->volume_scatter_history);
DRW_TEXTURE_FREE_SAFE(txl->volume_transmit_history);
GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_fb);
GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_scat_fb);
GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_integ_fb);
copy_v3_v3_int(common_data->vol_tex_size, tex_size);
common_data->vol_inv_tex_size[0] = 1.0f / (float)(tex_size[0]);
common_data->vol_inv_tex_size[1] = 1.0f / (float)(tex_size[1]);
common_data->vol_inv_tex_size[2] = 1.0f / (float)(tex_size[2]);
}
/* Like frostbite's paper, 5% blend of the new frame. */
common_data->vol_history_alpha = (txl->volume_prop_scattering == NULL) ? 0.0f : 0.95f;
/* Temporal Super sampling jitter */
uint ht_primes[3] = {3, 7, 2};
uint current_sample = 0;
/* If TAA is in use do not use the history buffer. */
bool do_taa = ((effects->enabled_effects & EFFECT_TAA) != 0);
if (draw_ctx->evil_C != NULL) {
struct wmWindowManager *wm = CTX_wm_manager(draw_ctx->evil_C);
do_taa = do_taa && (ED_screen_animation_no_scrub(wm) == NULL);
}
if (do_taa) {
common_data->vol_history_alpha = 0.0f;
current_sample = effects->taa_current_sample - 1;
effects->volume_current_sample = -1;
}
else if (DRW_state_is_image_render()) {
const uint max_sample = (ht_primes[0] * ht_primes[1] * ht_primes[2]);
current_sample = effects->volume_current_sample = (effects->volume_current_sample + 1) %
max_sample;
if (current_sample != max_sample - 1) {
DRW_viewport_request_redraw();
}
}
EEVEE_volumes_set_jitter(sldata, current_sample);
float integration_start = scene_eval->eevee.volumetric_start;
float integration_end = scene_eval->eevee.volumetric_end;
effects->volume_light_clamp = scene_eval->eevee.volumetric_light_clamp;
common_data->vol_shadow_steps = (float)scene_eval->eevee.volumetric_shadow_samples;
if ((scene_eval->eevee.flag & SCE_EEVEE_VOLUMETRIC_SHADOWS) == 0) {
common_data->vol_shadow_steps = 0;
}
if (DRW_view_is_persp_get(NULL)) {
float sample_distribution = scene_eval->eevee.volumetric_sample_distribution;
sample_distribution = 4.0f * (max_ff(1.0f - sample_distribution, 1e-2f));
const float clip_start = DRW_view_near_distance_get(NULL);
/* Negate */
float near = integration_start = min_ff(-integration_start, clip_start - 1e-4f);
float far = integration_end = min_ff(-integration_end, near - 1e-4f);
common_data->vol_depth_param[0] = (far - near * exp2(1.0f / sample_distribution)) /
(far - near);
common_data->vol_depth_param[1] = (1.0f - common_data->vol_depth_param[0]) / near;
common_data->vol_depth_param[2] = sample_distribution;
}
else {
const float clip_start = DRW_view_near_distance_get(NULL);
const float clip_end = DRW_view_far_distance_get(NULL);
integration_start = min_ff(integration_end, clip_start);
integration_end = max_ff(-integration_end, clip_end);
common_data->vol_depth_param[0] = integration_start;
common_data->vol_depth_param[1] = integration_end;
common_data->vol_depth_param[2] = 1.0f / (integration_end - integration_start);
}
/* Disable clamp if equal to 0. */
if (effects->volume_light_clamp == 0.0) {
effects->volume_light_clamp = FLT_MAX;
}
common_data->vol_use_lights = (scene_eval->eevee.flag & SCE_EEVEE_VOLUMETRIC_LIGHTS) != 0;
common_data->vol_use_soft_shadows = (scene_eval->eevee.flag & SCE_EEVEE_SHADOW_SOFT) != 0;
if (!e_data.dummy_scatter) {
const float scatter[4] = {0.0f, 0.0f, 0.0f, 0.0f};
const float transmit[4] = {1.0f, 1.0f, 1.0f, 1.0f};
e_data.dummy_scatter = DRW_texture_create_3d(1, 1, 1, GPU_RGBA8, DRW_TEX_WRAP, scatter);
e_data.dummy_transmit = DRW_texture_create_3d(1, 1, 1, GPU_RGBA8, DRW_TEX_WRAP, transmit);
}
}
void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
DRWShadingGroup *grp = NULL;
/* Textures */
if (!e_data.dummy_zero) {
eevee_create_textures_volumes();
}
/* Quick breakdown of the Volumetric rendering:
*
* The rendering is separated in 4 stages:
*
* - Material Parameters : we collect volume properties of
* all participating media in the scene and store them in
* a 3D texture aligned with the 3D frustum.
* This is done in 2 passes, one that clear the texture
* and/or evaluate the world volumes, and the 2nd one that
* additively render object volumes.
*
* - Light Scattering : the volume properties then are sampled
* and light scattering is evaluated for each cell of the
* volume texture. Temporal super-sampling (if enabled) occurs here.
*
* - Volume Integration : the scattered light and extinction is
* integrated (accumulated) along the view-rays. The result is stored
* for every cell in another texture.
*
* - Full-screen Resolve : From the previous stage, we get two
* 3D textures that contains integrated scattered light and extinction
* for "every" positions in the frustum. We only need to sample
* them and blend the scene color with those factors. This also
* work for alpha blended materials.
*/
/* World pass is not additive as it also clear the buffer. */
DRW_PASS_CREATE(psl->volumetric_world_ps, DRW_STATE_WRITE_COLOR);
DRW_PASS_CREATE(psl->volumetric_objects_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD);
/* World Volumetric */
struct World *wo = scene->world;
if (wo != NULL && wo->use_nodes && wo->nodetree &&
!LOOK_DEV_STUDIO_LIGHT_ENABLED(draw_ctx->v3d)) {
struct GPUMaterial *mat = EEVEE_material_get(vedata, scene, NULL, wo, VAR_MAT_VOLUME);
if (GPU_material_has_volume_output(mat)) {
grp = DRW_shgroup_material_create(mat, psl->volumetric_world_ps);
}
if (grp) {
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
/* TODO(fclem): remove those (need to clean the GLSL files). */
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);
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, "renderpass_block", sldata->renderpass_ubo.combined);
/* Fix principle volumetric not working with world materials. */
ListBase gpu_grids = GPU_material_volume_grids(mat);
LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, &gpu_grids) {
DRW_shgroup_uniform_texture(
grp, gpu_grid->sampler_name, eevee_volume_default_texture(gpu_grid->default_value));
}
DRW_shgroup_call_procedural_triangles(grp, NULL, common_data->vol_tex_size[2]);
effects->enabled_effects |= (EFFECT_VOLUMETRIC | EFFECT_POST_BUFFER);
}
}
if (grp == NULL) {
/* If no world or volume material is present just clear the buffer with this drawcall */
grp = DRW_shgroup_create(EEVEE_shaders_volumes_clear_sh_get(), psl->volumetric_world_ps);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_call_procedural_triangles(grp, NULL, common_data->vol_tex_size[2]);
}
}
static bool eevee_volume_object_grids_init(Object *ob, ListBase *gpu_grids, DRWShadingGroup *grp)
{
Volume *volume = ob->data;
BKE_volume_load(volume, G.main);
/* Test if we need to use multiple transforms. */
DRWVolumeGrid *first_drw_grid = NULL;
bool multiple_transforms = true;
LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) {
const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, gpu_grid->name);
DRWVolumeGrid *drw_grid = (volume_grid) ?
DRW_volume_batch_cache_get_grid(volume, volume_grid) :
NULL;
if (drw_grid) {
if (first_drw_grid == NULL) {
first_drw_grid = drw_grid;
}
else if (drw_grid &&
!equals_m4m4(drw_grid->object_to_texture, first_drw_grid->object_to_texture)) {
multiple_transforms = true;
break;
}
}
}
/* Bail out of no grids to render. */
if (first_drw_grid == NULL) {
return false;
}
/* Set transform matrix for the volume as a whole. This one is also used for
* clipping so must map the entire bounding box to 0..1. */
float bounds_to_object[4][4];
if (multiple_transforms) {
/* For multiple grids with different transform, we first transform from object space
* to bounds, then for each individual grid from bounds to texture. */
BoundBox *bb = BKE_volume_boundbox_get(ob);
float bb_size[3];
sub_v3_v3v3(bb_size, bb->vec[6], bb->vec[0]);
size_to_mat4(bounds_to_object, bb_size);
copy_v3_v3(bounds_to_object[3], bb->vec[0]);
invert_m4_m4(first_drw_grid->object_to_bounds, bounds_to_object);
DRW_shgroup_uniform_mat4(grp, "volumeObjectToTexture", first_drw_grid->object_to_bounds);
}
else {
/* All grid transforms are equal, we can transform to texture space immediately. */
DRW_shgroup_uniform_mat4(grp, "volumeObjectToTexture", first_drw_grid->object_to_texture);
}
/* Don't use orco transform here, only matrix. */
DRW_shgroup_uniform_vec3_copy(grp, "volumeOrcoLoc", (float[3]){0.5f, 0.5f, 0.5f});
DRW_shgroup_uniform_vec3_copy(grp, "volumeOrcoSize", (float[3]){0.5f, 0.5f, 0.5f});
/* Set density scale. */
const float density_scale = BKE_volume_density_scale(volume, ob->obmat);
DRW_shgroup_uniform_float_copy(grp, "volumeDensityScale", density_scale);
/* Bind volume grid textures. */
LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) {
const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, gpu_grid->name);
DRWVolumeGrid *drw_grid = (volume_grid) ?
DRW_volume_batch_cache_get_grid(volume, volume_grid) :
NULL;
/* Handle 3 cases here:
* - Grid exists and texture was loaded -> use texture.
* - Grid exists but has zero size or failed to load -> use zero.
* - Grid does not exist -> use default value. */
GPUTexture *grid_tex = (drw_grid) ? drw_grid->texture :
(volume_grid) ? e_data.dummy_zero :
eevee_volume_default_texture(gpu_grid->default_value);
DRW_shgroup_uniform_texture(grp, gpu_grid->sampler_name, grid_tex);
if (drw_grid && multiple_transforms) {
/* Specify per-volume transform matrix that is applied after the
* transform from object to bounds. */
mul_m4_m4m4(drw_grid->bounds_to_texture, drw_grid->object_to_texture, bounds_to_object);
DRW_shgroup_uniform_mat4(grp, gpu_grid->transform_name, drw_grid->bounds_to_texture);
}
}
return true;
}
static bool eevee_volume_object_mesh_init(Scene *scene,
Object *ob,
ListBase *gpu_grids,
DRWShadingGroup *grp)
{
static const float white[3] = {1.0f, 1.0f, 1.0f};
ModifierData *md = NULL;
/* Smoke Simulation */
if ((md = BKE_modifiers_findby_type(ob, eModifierType_Fluid)) &&
(BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) &&
((FluidModifierData *)md)->domain != NULL) {
FluidModifierData *fmd = (FluidModifierData *)md;
FluidDomainSettings *fds = fmd->domain;
/* Don't try to show liquid domains here. */
if (!fds->fluid || !(fds->type == FLUID_DOMAIN_TYPE_GAS)) {
return false;
}
/* Don't show smoke before simulation starts, this could be made an option in the future. */
/* (sebbas): Always show smoke for manta */
#if 0
const DRWContextState *draw_ctx = DRW_context_state_get();
const bool show_smoke = ((int)DEG_get_ctime(draw_ctx->depsgraph) >=
*fds->point_cache[0]->startframe);
#endif
if (fds->fluid && (fds->type == FLUID_DOMAIN_TYPE_GAS) /* && show_smoke */) {
DRW_smoke_ensure(fmd, fds->flags & FLUID_DOMAIN_USE_NOISE);
BLI_addtail(&e_data.smoke_domains, BLI_genericNodeN(fmd));
}
LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) {
if (STREQ(gpu_grid->name, "density")) {
DRW_shgroup_uniform_texture_ref(
grp, gpu_grid->sampler_name, fds->tex_density ? &fds->tex_density : &e_data.dummy_one);
}
else if (STREQ(gpu_grid->name, "color")) {
DRW_shgroup_uniform_texture_ref(
grp, gpu_grid->sampler_name, fds->tex_color ? &fds->tex_color : &e_data.dummy_one);
}
else if (STR_ELEM(gpu_grid->name, "flame", "temperature")) {
DRW_shgroup_uniform_texture_ref(
grp, gpu_grid->sampler_name, fds->tex_flame ? &fds->tex_flame : &e_data.dummy_flame);
}
else {
DRW_shgroup_uniform_texture(
grp, gpu_grid->sampler_name, eevee_volume_default_texture(gpu_grid->default_value));
}
}
/* Constant Volume color. */
bool use_constant_color = ((fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) == 0 &&
(fds->active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET) != 0);
DRW_shgroup_uniform_vec3(
grp, "volumeColor", (use_constant_color) ? fds->active_color : white, 1);
/* Output is such that 0..1 maps to 0..1000K */
DRW_shgroup_uniform_vec2(grp, "volumeTemperature", &fds->flame_ignition, 1);
}
else {
LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) {
DRW_shgroup_uniform_texture(
grp, gpu_grid->sampler_name, eevee_volume_default_texture(gpu_grid->default_value));
}
}
/* Transform for mesh volumes. */
static const float unit_mat[4][4] = {{1.0f, 0.0f, 0.0f, 0.0f},
{0.0f, 1.0f, 0.0f, 0.0f},
{0.0f, 0.0f, 1.0f, 0.0f},
{0.0f, 0.0f, 0.0f, 1.0f}};
float *texco_loc, *texco_size;
BKE_mesh_texspace_get_reference((struct Mesh *)ob->data, NULL, &texco_loc, &texco_size);
DRW_shgroup_uniform_mat4(grp, "volumeObjectToTexture", unit_mat);
DRW_shgroup_uniform_vec3(grp, "volumeOrcoLoc", texco_loc, 1);
DRW_shgroup_uniform_vec3(grp, "volumeOrcoSize", texco_size, 1);
return true;
}
void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
Scene *scene,
Object *ob)
{
Material *ma = BKE_object_material_get_eval(ob, 1);
if (ma == NULL) {
if (ob->type == OB_VOLUME) {
ma = BKE_material_default_volume();
}
else {
return;
}
}
float size[3];
mat4_to_size(size, ob->obmat);
/* Check if any of the axes have 0 length. (see T69070) */
const float epsilon = 1e-8f;
if ((size[0] < epsilon) || (size[1] < epsilon) || (size[2] < epsilon)) {
return;
}
int mat_options = VAR_MAT_VOLUME | VAR_MAT_MESH;
struct GPUMaterial *mat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options);
eGPUMaterialStatus status = GPU_material_status(mat);
/* If shader failed to compile or is currently compiling. */
if (status != GPU_MAT_SUCCESS) {
return;
}
DRWShadingGroup *grp = DRW_shgroup_material_create(mat, vedata->psl->volumetric_objects_ps);
/* TODO(fclem): remove those "unnecessary" UBOs */
DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
ListBase gpu_grids = GPU_material_volume_grids(mat);
if (ob->type == OB_VOLUME) {
if (!eevee_volume_object_grids_init(ob, &gpu_grids, grp)) {
return;
}
}
else {
if (!eevee_volume_object_mesh_init(scene, ob, &gpu_grids, grp)) {
return;
}
}
/* TODO: Reduce to number of slices intersecting. */
/* TODO: Preemptive culling. */
DRW_shgroup_call_procedural_triangles(grp, ob, sldata->common_data.vol_tex_size[2]);
vedata->stl->effects->enabled_effects |= (EFFECT_VOLUMETRIC | EFFECT_POST_BUFFER);
}
void EEVEE_volumes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_EffectsInfo *effects = vedata->stl->effects;
LightCache *lcache = vedata->stl->g_data->light_cache;
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
DRWShadingGroup *grp;
struct GPUShader *sh;
DRW_PASS_CREATE(psl->volumetric_scatter_ps, DRW_STATE_WRITE_COLOR);
sh = (common_data->vol_use_lights) ? EEVEE_shaders_volumes_scatter_with_lights_sh_get() :
EEVEE_shaders_volumes_scatter_sh_get();
grp = DRW_shgroup_create(sh, psl->volumetric_scatter_ps);
DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &lcache->grid_tx.tex);
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_ref(grp, "volumeScattering", &txl->volume_prop_scattering);
DRW_shgroup_uniform_texture_ref(grp, "volumeExtinction", &txl->volume_prop_extinction);
DRW_shgroup_uniform_texture_ref(grp, "volumeEmission", &txl->volume_prop_emission);
DRW_shgroup_uniform_texture_ref(grp, "volumePhase", &txl->volume_prop_phase);
DRW_shgroup_uniform_texture_ref(grp, "historyScattering", &txl->volume_scatter_history);
DRW_shgroup_uniform_texture_ref(grp, "historyTransmittance", &txl->volume_transmit_history);
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, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_call_procedural_triangles(grp, NULL, common_data->vol_tex_size[2]);
DRW_PASS_CREATE(psl->volumetric_integration_ps, DRW_STATE_WRITE_COLOR);
grp = DRW_shgroup_create(EEVEE_shaders_volumes_integration_sh_get(),
psl->volumetric_integration_ps);
DRW_shgroup_uniform_texture_ref(grp, "volumeScattering", &txl->volume_scatter);
DRW_shgroup_uniform_texture_ref(grp, "volumeExtinction", &txl->volume_transmit);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
if (USE_VOLUME_OPTI) {
DRW_shgroup_uniform_image_ref(grp, "finalScattering_img", &txl->volume_scatter_history);
DRW_shgroup_uniform_image_ref(grp, "finalTransmittance_img", &txl->volume_transmit_history);
}
DRW_shgroup_call_procedural_triangles(
grp, NULL, USE_VOLUME_OPTI ? 1 : common_data->vol_tex_size[2]);
DRW_PASS_CREATE(psl->volumetric_resolve_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
grp = DRW_shgroup_create(EEVEE_shaders_volumes_resolve_sh_get(false),
psl->volumetric_resolve_ps);
DRW_shgroup_uniform_texture_ref(grp, "inScattering", &txl->volume_scatter);
DRW_shgroup_uniform_texture_ref(grp, "inTransmittance", &txl->volume_transmit);
DRW_shgroup_uniform_texture_ref(grp, "inSceneDepth", &e_data.depth_src);
DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
}
void EEVEE_volumes_draw_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_EffectsInfo *effects = vedata->stl->effects;
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
int *tex_size = common_data->vol_tex_size;
if (txl->volume_prop_scattering == NULL) {
/* Volume properties: We evaluate all volumetric objects
* and store their final properties into each froxel */
txl->volume_prop_scattering = DRW_texture_create_3d(
tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL);
txl->volume_prop_extinction = DRW_texture_create_3d(
tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL);
txl->volume_prop_emission = DRW_texture_create_3d(
tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL);
txl->volume_prop_phase = DRW_texture_create_3d(
tex_size[0], tex_size[1], tex_size[2], GPU_RG16F, DRW_TEX_FILTER, NULL);
/* Volume scattering: We compute for each froxel the
* Scattered light towards the view. We also resolve temporal
* super sampling during this stage. */
txl->volume_scatter = DRW_texture_create_3d(
tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL);
txl->volume_transmit = DRW_texture_create_3d(
tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL);
/* Final integration: We compute for each froxel the
* amount of scattered light and extinction coef at this
* given depth. We use these textures as double buffer
* for the volumetric history. */
txl->volume_scatter_history = DRW_texture_create_3d(
tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL);
txl->volume_transmit_history = DRW_texture_create_3d(
tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL);
}
GPU_framebuffer_ensure_config(&fbl->volumetric_fb,
{GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(txl->volume_prop_scattering),
GPU_ATTACHMENT_TEXTURE(txl->volume_prop_extinction),
GPU_ATTACHMENT_TEXTURE(txl->volume_prop_emission),
GPU_ATTACHMENT_TEXTURE(txl->volume_prop_phase)});
GPU_framebuffer_ensure_config(&fbl->volumetric_scat_fb,
{GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(txl->volume_scatter),
GPU_ATTACHMENT_TEXTURE(txl->volume_transmit)});
GPU_framebuffer_ensure_config(&fbl->volumetric_integ_fb,
{GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(txl->volume_scatter_history),
GPU_ATTACHMENT_TEXTURE(txl->volume_transmit_history)});
}
else {
DRW_TEXTURE_FREE_SAFE(txl->volume_prop_scattering);
DRW_TEXTURE_FREE_SAFE(txl->volume_prop_extinction);
DRW_TEXTURE_FREE_SAFE(txl->volume_prop_emission);
DRW_TEXTURE_FREE_SAFE(txl->volume_prop_phase);
DRW_TEXTURE_FREE_SAFE(txl->volume_scatter);
DRW_TEXTURE_FREE_SAFE(txl->volume_transmit);
DRW_TEXTURE_FREE_SAFE(txl->volume_scatter_history);
DRW_TEXTURE_FREE_SAFE(txl->volume_transmit_history);
GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_fb);
GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_scat_fb);
GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_integ_fb);
}
effects->volume_scatter = e_data.dummy_scatter;
effects->volume_transmit = e_data.dummy_transmit;
}
void EEVEE_volumes_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
DRW_stats_group_start("Volumetrics");
/* We sample the shadow-maps using shadow sampler. We need to enable Comparison mode.
* TODO(fclem): avoid this by using sampler objects. */
GPU_texture_compare_mode(sldata->shadow_cube_pool, true);
GPU_texture_compare_mode(sldata->shadow_cascade_pool, true);
GPU_framebuffer_bind(fbl->volumetric_fb);
DRW_draw_pass(psl->volumetric_world_ps);
DRW_draw_pass(psl->volumetric_objects_ps);
GPU_framebuffer_bind(fbl->volumetric_scat_fb);
DRW_draw_pass(psl->volumetric_scatter_ps);
if (USE_VOLUME_OPTI) {
/* Avoid feedback loop assert. */
GPU_framebuffer_bind(fbl->volumetric_fb);
}
else {
GPU_framebuffer_bind(fbl->volumetric_integ_fb);
}
DRW_draw_pass(psl->volumetric_integration_ps);
SWAP(struct GPUFrameBuffer *, fbl->volumetric_scat_fb, fbl->volumetric_integ_fb);
SWAP(GPUTexture *, txl->volume_scatter, txl->volume_scatter_history);
SWAP(GPUTexture *, txl->volume_transmit, txl->volume_transmit_history);
effects->volume_scatter = txl->volume_scatter;
effects->volume_transmit = txl->volume_transmit;
/* Restore */
GPU_framebuffer_bind(fbl->main_fb);
DRW_stats_group_end();
}
}
void EEVEE_volumes_resolve(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
e_data.depth_src = dtxl->depth;
if (USE_VOLUME_OPTI) {
GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
}
/* Apply for opaque geometry. */
GPU_framebuffer_bind(fbl->main_color_fb);
DRW_draw_pass(psl->volumetric_resolve_ps);
/* Restore. */
GPU_framebuffer_bind(fbl->main_fb);
}
}
void EEVEE_volumes_free_smoke_textures(void)
{
/* Free Smoke Textures after rendering */
LISTBASE_FOREACH (LinkData *, link, &e_data.smoke_domains) {
FluidModifierData *fmd = (FluidModifierData *)link->data;
DRW_smoke_free(fmd);
}
BLI_freelistN(&e_data.smoke_domains);
}
void EEVEE_volumes_free(void)
{
DRW_TEXTURE_FREE_SAFE(e_data.dummy_scatter);
DRW_TEXTURE_FREE_SAFE(e_data.dummy_transmit);
DRW_TEXTURE_FREE_SAFE(e_data.dummy_zero);
DRW_TEXTURE_FREE_SAFE(e_data.dummy_one);
DRW_TEXTURE_FREE_SAFE(e_data.dummy_flame);
}
/* -------------------------------------------------------------------- */
/** \name Render Passes
* \{ */
void EEVEE_volumes_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PassList *psl = vedata->psl;
EEVEE_EffectsInfo *effects = stl->effects;
/* Create FrameBuffer. */
/* Should be enough precision for many samples. */
const eGPUTextureFormat texture_format_accum = (tot_samples > 128) ? GPU_RGBA32F : GPU_RGBA16F;
DRW_texture_ensure_fullscreen_2d(&txl->volume_scatter_accum, texture_format_accum, 0);
DRW_texture_ensure_fullscreen_2d(&txl->volume_transmittance_accum, texture_format_accum, 0);
GPU_framebuffer_ensure_config(&fbl->volumetric_accum_fb,
{GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(txl->volume_scatter_accum),
GPU_ATTACHMENT_TEXTURE(txl->volume_transmittance_accum)});
/* Create Pass and shgroup. */
DRW_PASS_CREATE(psl->volumetric_accum_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
DRWShadingGroup *grp = NULL;
if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
grp = DRW_shgroup_create(EEVEE_shaders_volumes_resolve_sh_get(true), psl->volumetric_accum_ps);
DRW_shgroup_uniform_texture_ref(grp, "inScattering", &txl->volume_scatter);
DRW_shgroup_uniform_texture_ref(grp, "inTransmittance", &txl->volume_transmit);
DRW_shgroup_uniform_texture_ref(grp, "inSceneDepth", &e_data.depth_src);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
}
else {
/* There is no volumetrics in the scene. Use a shader to fill the accum textures with a default
* value. */
grp = DRW_shgroup_create(EEVEE_shaders_volumes_accum_sh_get(), psl->volumetric_accum_ps);
}
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
}
void EEVEE_volumes_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_PassList *psl = vedata->psl;
EEVEE_EffectsInfo *effects = vedata->stl->effects;
if (fbl->volumetric_accum_fb != NULL) {
/* Accum pass */
GPU_framebuffer_bind(fbl->volumetric_accum_fb);
/* Clear texture. */
if (effects->taa_current_sample == 1) {
const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_framebuffer_clear_color(fbl->volumetric_accum_fb, clear);
}
DRW_draw_pass(psl->volumetric_accum_ps);
/* Restore */
GPU_framebuffer_bind(fbl->main_fb);
}
}
/** \} */

View File

@@ -0,0 +1,111 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*/
#include "NOD_shader.h"
#include "BKE_lib_id.h"
#include "BKE_node.h"
#include "BKE_world.h"
#include "eevee_instance.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name Default Material
*
* \{ */
DefaultWorldNodeTree::DefaultWorldNodeTree()
{
bNodeTree *ntree = ntreeAddTree(NULL, "World Nodetree", ntreeType_Shader->idname);
bNode *background = nodeAddStaticNode(NULL, ntree, SH_NODE_BACKGROUND);
bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_WORLD);
bNodeSocket *background_out = nodeFindSocket(background, SOCK_OUT, "Background");
bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface");
nodeAddLink(ntree, background, background_out, output, output_in);
nodeSetActive(ntree, output);
color_socket_ =
(bNodeSocketValueRGBA *)nodeFindSocket(background, SOCK_IN, "Color")->default_value;
ntree_ = ntree;
}
DefaultWorldNodeTree::~DefaultWorldNodeTree()
{
ntreeFreeEmbeddedTree(ntree_);
MEM_SAFE_FREE(ntree_);
}
/* Configure a default nodetree with the given world. */
bNodeTree *DefaultWorldNodeTree::nodetree_get(::World *wo)
{
/* WARNING: This function is not threadsafe. Which is not a problem for the moment. */
copy_v3_fl3(color_socket_->value, wo->horr, wo->horg, wo->horb);
return ntree_;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name World
*
* \{ */
void World::sync()
{
if (inst_.lookdev.sync_world()) {
return;
}
::World *bl_world = inst_.scene->world;
if (bl_world == nullptr) {
bl_world = BKE_world_default();
}
else {
WorldHandle &wo_handle = inst_.sync.sync_world(bl_world);
if (wo_handle.recalc != 0) {
inst_.lightprobes.set_world_dirty();
}
wo_handle.reset_recalc_flag();
}
/* TODO(fclem) This should be detected to scene level. */
::World *orig_world = (::World *)DEG_get_original_id(&bl_world->id);
if (prev_original_world != orig_world) {
prev_original_world = orig_world;
inst_.sampling.reset();
}
bNodeTree *ntree = (bl_world->nodetree && bl_world->use_nodes) ?
bl_world->nodetree :
default_tree.nodetree_get(bl_world);
GPUMaterial *gpumat = inst_.shaders.world_shader_get(bl_world, ntree);
inst_.shading_passes.background.sync(gpumat);
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,78 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
/** \file
* \ingroup eevee
*
* World rendering with material handling. Also take care of lookdev
* HDRI and default material.
*/
#pragma once
#include "DNA_world_types.h"
namespace blender::eevee {
class Instance;
/* -------------------------------------------------------------------- */
/** \name Default World Nodetree
*
* In order to support worlds without nodetree we reuse and configure a standalone nodetree that
* we pass for shader generation. The GPUMaterial is still stored inside the World even if
* it does not use a nodetree.
* \{ */
class DefaultWorldNodeTree {
private:
bNodeTree *ntree_;
bNodeSocketValueRGBA *color_socket_;
public:
DefaultWorldNodeTree();
~DefaultWorldNodeTree();
bNodeTree *nodetree_get(::World *world);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name World
*
* \{ */
class World {
private:
Instance &inst_;
DefaultWorldNodeTree default_tree;
/* Used to detect if world change. */
::World *prev_original_world = nullptr;
public:
World(Instance &inst) : inst_(inst){};
void sync(void);
};
/** \} */
} // namespace blender::eevee

Some files were not shown because too many files have changed in this diff Show More