1
1

Compare commits

...

210 Commits

Author SHA1 Message Date
9cc6d1dc32 Fix MainView constructor error on GCC < 9.4 2022-01-27 22:19:38 +01:00
8e7b535ce6 Fix NaN compilation error on MSVC 2022-01-27 21:39:42 +01:00
9b35b77716 Merge branch 'master' into eevee-rewrite 2022-01-27 21:38:05 +01:00
ea577c499f EEVEE: Make use of float4x4 instead of float[4][4] and rename vectors
This follows the type defined in `gpu_shader_shared_utils.h`.
2022-01-27 19:33:09 +01:00
0ea5f3fb5d Merge branch 'master' into eevee-rewrite 2022-01-27 18:46:38 +01:00
37cfceb7eb EEVEE: Make use of new DRW_gpu_wrapper.hh instead of eevee_wrapper.hh 2022-01-27 18:41:53 +01:00
c75f7143b6 Merge branch 'master' into eevee-rewrite 2022-01-27 17:05:11 +01:00
ea214f128e EEVEE: Fix compilation issues 2022-01-27 17:01:17 +01:00
848d35c57e EEVEE: Make use of DRW_gpu_wrappers 2022-01-27 17:00:58 +01:00
a3c416f864 Merge branch 'master' into eevee-rewrite 2022-01-27 16:21:02 +01:00
d0e86c1b78 Merge branch 'master' into eevee-rewrite
# Conflicts:
#	source/blender/draw/intern/draw_manager_exec.c
2022-01-27 15:53:38 +01:00
338c0e5f90 Merge branch 'master' into eevee-rewrite 2022-01-27 14:59:50 +01:00
1f2b3cdd73 Comment eevee_raytrace_resolve_lib out to avoid compilation errors for now 2022-01-27 14:56:10 +01:00
032baf06f3 Merge branch 'draw-viewport-data' into eevee-rewrite 2022-01-27 12:34:42 +01:00
0bdf574ea2 Merge branch 'master' into draw-viewport-data 2022-01-26 22:05:55 +01:00
4226c484bd Merge branch 'draw-viewport-data' into eevee-rewrite
# Conflicts:
#	release/scripts/startup/bl_ui/properties_data_camera.py
#	source/blender/blenkernel/BKE_camera.h
#	source/blender/blenkernel/BKE_node.h
#	source/blender/blenkernel/intern/camera.c
#	source/blender/blenlib/BLI_float2.hh
#	source/blender/blenlib/BLI_float3.hh
#	source/blender/blenlib/BLI_float4.hh
#	source/blender/blenlib/BLI_math_geom.h
#	source/blender/blenlib/intern/math_geom.c
#	source/blender/draw/CMakeLists.txt
#	source/blender/draw/engines/basic/basic_engine.c
#	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_lightcache.c
#	source/blender/draw/engines/eevee/eevee_lightcache.h
#	source/blender/draw/engines/eevee/eevee_lightprobes.c
#	source/blender/draw/engines/eevee/eevee_lights.c
#	source/blender/draw/engines/eevee/eevee_materials.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_sampling.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_cube.c
#	source/blender/draw/engines/eevee/eevee_temporal_sampling.c
#	source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
#	source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
#	source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl
#	source/blender/draw/engines/eevee/shaders/effect_dof_bokeh_frag.glsl
#	source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl
#	source/blender/draw/engines/eevee/shaders/effect_dof_reduce_frag.glsl
#	source/blender/draw/engines/eevee/shaders/effect_reflection_resolve_frag.glsl
#	source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl
#	source/blender/draw/engines/eevee/shaders/random_lib.glsl
#	source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
#	source/blender/draw/engines/eevee/shaders/surface_lib.glsl
#	source/blender/draw/engines/eevee/shaders/surface_vert.glsl
#	source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl
#	source/blender/draw/engines/external/external_engine.c
#	source/blender/draw/engines/gpencil/gpencil_engine.c
#	source/blender/draw/engines/image/image_engine.c
#	source/blender/draw/engines/overlay/overlay_engine.c
#	source/blender/draw/engines/select/select_engine.c
#	source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl
#	source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl
#	source/blender/draw/engines/workbench/workbench_engine.c
#	source/blender/draw/engines/workbench/workbench_shader.c
#	source/blender/draw/intern/DRW_render.h
#	source/blender/draw/intern/draw_debug.h
#	source/blender/draw/intern/draw_manager_data.c
#	source/blender/draw/intern/draw_manager_exec.c
#	source/blender/draw/intern/draw_view_data.h
#	source/blender/gpu/CMakeLists.txt
#	source/blender/gpu/GPU_material.h
#	source/blender/gpu/GPU_shader.h
#	source/blender/gpu/GPU_state.h
#	source/blender/gpu/GPU_vertex_buffer.h
#	source/blender/gpu/intern/gpu_codegen.c
#	source/blender/gpu/intern/gpu_material.c
#	source/blender/gpu/intern/gpu_material_library.h
#	source/blender/gpu/intern/gpu_node_graph.c
#	source/blender/gpu/intern/gpu_texture_private.hh
#	source/blender/gpu/intern/gpu_vertex_buffer.cc
#	source/blender/gpu/opengl/gl_shader.cc
#	source/blender/gpu/shaders/gpu_shader_common_obinfos_lib.glsl
#	source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl
#	source/blender/nodes/shader/node_shader_tree.cc
#	source/blender/nodes/shader/nodes/node_shader_background.cc
#	source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc
#	source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc
#	source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc
#	source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc
#	source/blender/nodes/shader/nodes/node_shader_bsdf_hair.cc
#	source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.c
#	source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c
#	source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc
#	source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc
#	source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc
#	source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc
#	source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc
#	source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc
#	source/blender/nodes/shader/nodes/node_shader_emission.cc
#	source/blender/nodes/shader/nodes/node_shader_holdout.cc
#	source/blender/nodes/shader/nodes/node_shader_output_material.cc
#	source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c
#	source/blender/nodes/shader/nodes/node_shader_tex_coord.cc
#	source/blender/nodes/shader/nodes/node_shader_vector_transform.cc
#	source/blender/nodes/shader/nodes/node_shader_volume_absorption.cc
#	source/blender/nodes/shader/nodes/node_shader_volume_principled.cc
#	source/blender/nodes/shader/nodes/node_shader_volume_scatter.cc
#	source/blender/render/RE_pipeline.h
#	source/blender/render/intern/initrender.c
2022-01-26 22:03:58 +01:00
af87b6d8cb Merge branch 'master' into draw-viewport-data
# Conflicts:
#	source/blender/draw/DRW_engine_types.h
#	source/blender/draw/intern/draw_manager.c
#	source/blender/draw/intern/draw_manager.h
#	source/blender/draw/intern/draw_manager_profiling.c
#	source/blender/draw/intern/draw_manager_text.h
#	source/blender/draw/intern/draw_texture_pool.cc
#	source/blender/draw/intern/draw_texture_pool.h
#	source/blender/draw/intern/draw_view_data.cc
#	source/blender/draw/intern/draw_view_data.h
#	source/blender/editors/space_view3d/view3d_draw.c
#	source/blender/gpu/GPU_texture.h
#	source/blender/gpu/GPU_viewport.h
#	source/blender/gpu/intern/gpu_shader_create_info_private.hh
#	source/blender/gpu/intern/gpu_viewport.c
2022-01-26 20:27:16 +01:00
55a6a8900a EEVEE: Shadow: Avoid double rendering
The update flag was not being cleared after the opaque pass, making the
updates of the forward pass re-render the same tiles.
2021-12-10 18:19:47 +01:00
36bec765e2 EEVEE: Shadow: Add defrag shader and page debug buffer generator
The defrag shader make sure the free heap is free of holes. Making
the allocation more straightforward.

Since we now only reference the pages using the tiles, we introduce
a debug shader that produces an image with page data in a visual way.
This replaces the debug 8 option.

This also fixes some bug that were still present in the pipeline.
2021-12-10 17:25:37 +01:00
0a7c4afd21 EEVEE: Shadow: Split LOD masking to its own shader
This fixes an issue with tile reuse causing corruption.
2021-12-09 16:27:48 +01:00
021bf5b171 EEVEE: Light: Separate directional lights iteration
This separate the handling of directional lights (sun) into their
own loops. This will help reduce register pressure and remove some
pollution of the local light culling.

All sun lights are packed at the start of the light array.
2021-12-08 21:51:13 +01:00
181bc60214 EEVEE: Shadow: Add depth scan for tile tagging
We now scan the depth buffer after the prepass to tag the needed
shadow tiles.

This is much more precise than the bound box tagging which is now
reserved for transparent objects.

This also:
- fix pixel radius size.
- add a dedicated info buffer to avoid having one unused tile.
2021-12-08 13:02:28 +01:00
b23d9519d0 EEVEE: Shadow: Make punctual LOD selection based on pixel density
Until now the LOD selection was based on distance from camera.

Now it is based on receiver distance ratio. We compute the world
size of one view pixel along with the world size of one shadow texel.
By knowing one point distance to the light or to the view, we can
compute the pixel density ratio and deduce the corresponding LOD.

We use this to compute the min LOD during the visibility selection phase
and the "mean" LOD for usage tagging by BBoxes.

The tagging LOD is a crude approximation as it only uses the BBox
center.
2021-12-05 23:08:46 +01:00
7303a453aa EEVEE: Shadow: Add LOD system for punctual shadows
This makes every shadow setup pass aware of the LOD chain of the tilemap
for each cubemap face.

In the free phase, we mask any LOD page that is completely covered by
higher LOD. This avoir commiting memory twice or more per area.

In the allocation phase, we check for the last valid LOD and set it
in the LOD 0 meta data. We also store the actual page location in LOD0 but
do not mark it as allocated as the LOD tile has the ownership of the page.
2021-12-05 01:28:36 +01:00
26335dfc57 EEVEE: Light: Merge ShadowData into LightData
This is because the light indices can now change because of the
culling. So the shadow data needs to follow.
2021-12-03 22:41:37 +01:00
1b00ca3575 EEVEE: Light: Port light culling to GPU
This removes the light count limit for the forward shaded object. This
also provides a more efficient way of computing the culling directly on
the GPU. Moreover, this avoids doing multiple lighting passes for high
light counts in the deferred pipeline, improving performance.
2021-12-03 22:41:37 +01:00
68b0195bf3 GL: Add support for all image resource types 2021-11-23 21:19:34 +01:00
68602f3d87 DRW: Add DRW_shgroup_vertex_buffer_ref 2021-11-23 21:19:11 +01:00
a8a4f5f805 EEVEE: Shadow: New virtual shadowmap sampling
This adds clipmap level selection and final page coordinate computation.

This also fixes some issues with tilemaps tagging.
2021-11-18 23:19:54 +01:00
9bd070fbc2 GPU: Add more barrier types 2021-11-16 21:39:26 +01:00
7664e1dd79 EEVEE: Shadows: Virtual shadowmap part 2
This continue the effort to implement virtual shadow mapping.

This includes:
- Spot cone culling of tile.
- Tile vs. view frustum tagging.
- Shadowmap Page allocation / freeing.
- Rendering to 4K buffer only tiles that needs it.
- Copying to shadow atlas.
2021-11-14 19:35:41 +01:00
55af3361bf DRWView: Add frustum infos to uniform buffer
This way it is possible to do some culling on GPU.
2021-11-14 14:43:38 +01:00
fbfbc9f15d DRW: Add debug line buffer
This debug buffer is automatically bound if a shader is including
`common_debug_lib.glsl`. One buffer is created for each shading group
using such a shader.

The shader can then use the functions from that file to draw debug
lines. There is a hardcoded limit of line one buffer can contain. Make
sure to only output lines for a few threads at most.

Under the hood this uses a vertex buffer bound as SSBO that contains
the number of verts and all the positions and colors packed into 1 vec4.
We render by just rendering the whole buffer.
All unused vertices are initialized with NaN positions and will not be
drawn.
2021-11-14 14:42:54 +01:00
bf42246984 GPUTexture: Fix support / validation for integer textures
Some types were missing in the switch cases in order to convert them to
correct values.
2021-11-14 14:29:53 +01:00
761ba97601 BLI: Add more operator to float4
Straightforward.
2021-11-14 14:28:54 +01:00
ebb2b1120c EEVEE: Shadow: Add Tilemap lod debug and fix clipmap end distance 2021-11-09 23:20:50 +01:00
2b61ca8f34 GPUVertexBuf: Mark dirty when using GPU_vertbuf_get_data
This way buffers with dynamic usage can also use this function
to update their content.
2021-11-09 20:13:17 +01:00
9db0734a1e GPU: Bump shader to version 430 and support other image uniforms
This is required for new EEVEE features
2021-11-09 14:47:35 +01:00
9c4cf35414 EEVEE: Shadow: Implement Virtual Shadowmapping
This is a total refactor of how shadows are handled.

We use Virtual shadow maps with different Level of details to
ensure a somewhat evenly distributed precision.

The shadow test is a really crude shadow test that will be
improved in further commit.

There is a pool of 4096 Tilemaps that are distributed between
shadowed ligths. These tilemaps are 16x16 each and reference
shadow map pages that are allocated in an atlas. Pages are only
allocated if needed (i.e: visible for rendering an object).

Page management is done on GPU using compute shaders to reduce
CPU task.

On CPU only one draw pass per updated tilemaps is issued.

This reduces the memory requirement of shadowmapping large scenes
with many lights.
2021-11-09 14:47:23 +01:00
c92b2d8bc4 EEVEE: Camera: fix fallback camera projection matrix 2021-11-05 16:55:06 +01:00
2a6a06fab7 EEVEE: Update / remove obsolete properties in the UI. 2021-11-05 16:55:06 +01:00
fc6a430d8e EEVEE: Raytracing: Add denoise and move raytrace passes to raytracing module.
Denoising make use of more memory to store and reproject the result of
previous frame to reduce noise. This only works for viewport.

There is a final bilateral filter for cleaning up noise even more.
2021-11-05 16:55:06 +01:00
c746eeae93 DRW: Shading Group: Add compute dispatch with size reference
This way we can reuse the same DRWPass using dispatch for multiple views.
2021-11-05 16:55:06 +01:00
e5fadd8c84 EEVEE: Texture: Make ensure function return true if allocation occured 2021-11-05 16:55:06 +01:00
682e1bea7e EEVEE: Forward: Support raytracing
Screen space Raytracing is supported by alpha blended surfaces.
However only opaque surfaces will be visible to the rays. This means
Alpha blended surfaces cannot reflect or refract themselves.

Denoising is not possible on alpha blended surfaces. Many samples
are needed for noise free results.

Since the cost of tracing can be very high, raytracing will only be
enabled on demand, on a per-material basis.
2021-11-05 16:55:06 +01:00
75db7522c6 EEVEE: Gbuffer: Split file 2021-11-05 16:55:06 +01:00
96d354a84d EEVEE: Subsurface: Small optimisation & fix comparison 2021-11-05 16:55:06 +01:00
5905b11c07 EEVEE: Raytrace: Add Diffuse surface raytracing
This simply reuse the reflection raytracing pipeline but with another
ray distribution. Only direct lighting, distant lighting and emissive
light are visible to diffuse rays.

Subsurface effect is not visible but transmittance effect is visible
to diffuse rays.

Indirect diffuse light is processed by the SSS filter.
2021-11-05 16:55:06 +01:00
a97234574d EEVEE: Deffered: Change normal packing for better negative normals
The spheremap encoding has a singularity issue for inverted normals
which are used by the translucent BSDF. This fixes the issue.
2021-11-05 16:55:06 +01:00
282a0c90d1 EEVEE: Subsurface: Fix issue cause by HiZ mipmaps
The mip level needs to be 0 to avoid sampling coarser level.
2021-11-05 16:55:06 +01:00
b8ab3f2f52 EEVEE: Principled BSDF: Fix refraction roughness when GGX single-scatter 2021-11-05 16:55:06 +01:00
92a92fc60f EEVEE: Cleanup: Rename uvcoordsvar to uv and explicit output location 2021-11-05 16:55:06 +01:00
52a81b8175 EEVEE: Raytracing: Add back screen space raytracing
The new pipeline is now cleaner and allows for deferred refraction.

The refractions are more accurate but are not denoised for now. More
research needs to be done in this area.

There is no feedback buffer for now, so reflections of metallic surfaces
will appear black.

The same restriction on refractive materials still holds true. They will
not appear in screen space tracing of other non refractive surfaces.

However, refractive surfaces (non-blended) can now reflect themselves
and the other surfaces with screen space reflections.

Half res tracing is not implemented back yet.
2021-11-05 16:55:06 +01:00
55a85af05d EEVEE: Add eevee_build: precomputation and codegen at compile time
This is to automate the generation of reuse sample tables and maybe more
in the future. This is not designed to make compilation way longer than
expected.
2021-10-24 16:15:38 +02:00
3db3006d4c EEVEE: Port back HiZbuffer
Pretty much identical.

Texture format is now always `GPU_R32F` to remove some workarounds.
2021-10-24 16:13:04 +02:00
008fb3eed9 EEVEE: Fix Crash with some geometry type 2021-10-08 17:58:45 +02:00
b304514bd5 EEVEE: Transmittance: Add back light transmittance
Same as SSS this has been rewritten to support varying SSS radius.

Instead of relying on shadowmap hack to improve the transmittance
artifact (previously called translucency) we exposed a min thickness
output that will reduce the maximum of light bleeding that can happen
at the shading point. This is far from perfect but at least it is
tweakable.

The effect is now cheaper and the option to enable it is now gone.
It can always be artificially disabled by making the thickness bigger
than the sss radius.

The effect is always enabled for all SSS surfaces and will even be
applied on forward shaded object (alpha blend mode).
2021-10-08 17:58:45 +02:00
b0da401292 EEVEE: Material: Add thickness output
This only adds the output but the output is not yet used.

This thickness output is meant to control the aspect of subsurface,
refraction, absorption and volume shaders.

The value expected is the mean thickness inside the object at the
shading point. The source can be a vertex color or a texture map baked
from a raytracer.
2021-10-08 17:58:45 +02:00
ee7deb09cf EEVEE: Subsurface Scattering: New implementation
This new implementation follows the technique described in
"Efficient screen space subsurface scattering Siggraph 2018".

Compared to the old implementation it fixes a lot of issues at
the cost of it being slower. This fixes:
- Light leaking between different objects.
- Light leaking between different surfaces with different depths.
- SSS radii are now "texturable" per pixel. No SSS surfaces limits.
- Noise should be lower.
- Precomputation is only done once for all SSS surfaces which lowers the
  per material storage and precomputation time.

Implementation is also simpler as it is only a one pass processing.

We differ from the reference presentation by not precomputing the
RGB weights per samples. We actually compute them on the fly in order
to support varying SSS radii.

Notes:
- SSS IOR and SSS anisotropy are not supported.
- Object level light leak prevention might not work for high number of
  objects in the scene (> 1024). In this case light leak might occur.
  Adding or deleting (hidding) objects in the scene might change which
  objects can leak.
2021-10-08 17:58:45 +02:00
9f85107fef EEVEE: Fix smooth transition when using render borders
The first sample buffer is fullscreen and needs to have its uvs
corrected to match the render border viewport.
2021-10-08 17:58:42 +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
42d2c96d4c Wrapped related global vars into struct. 2021-10-04 14:45:30 +02:00
941fdefdb3 Move index from DrawEngineType to DRWRegisteredDrawEngine. 2021-10-04 14:36:47 +02:00
a0df3c4d51 Fix incorrect merge: missing function DRW_view_get_active. 2021-10-04 11:21:38 +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
417 changed files with 29579 additions and 28828 deletions

View File

@@ -264,6 +264,9 @@ ForEachMacros:
- SET_SLOT_PROBING_BEGIN
- MAP_SLOT_PROBING_BEGIN
- VECTOR_SET_SLOT_PROBING_BEGIN
- LIGHT_FOREACH_BEGIN_DIRECTIONAL
- LIGHT_FOREACH_BEGIN_LOCAL
- LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL
StatementMacros:
- PyObject_HEAD

View File

@@ -117,8 +117,21 @@ class DATA_PT_lens(CameraButtonsPanel, Panel):
col.prop(ccam, "fisheye_polynomial_k2", text="K2")
col.prop(ccam, "fisheye_polynomial_k3", text="K3")
col.prop(ccam, "fisheye_polynomial_k4", text="K4")
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

@@ -227,8 +227,6 @@ def draw_material_settings(self, context):
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")

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"
@@ -329,8 +360,8 @@ class RENDER_PT_eevee_subsurface_scattering(RenderButtonsPanel, Panel):
col.prop(props, "sss_jitter_threshold")
class RENDER_PT_eevee_screen_space_reflections(RenderButtonsPanel, Panel):
bl_label = "Screen Space Reflections"
class RENDER_PT_eevee_raytracing(RenderButtonsPanel, Panel):
bl_label = "Raytracing"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_EEVEE'}
@@ -352,12 +383,9 @@ class RENDER_PT_eevee_screen_space_reflections(RenderButtonsPanel, Panel):
col = layout.column()
col.active = props.use_ssr
col.prop(props, "use_ssr_refraction", text="Refraction")
col.prop(props, "use_ssr_halfres")
col.prop(props, "ssr_quality")
col.prop(props, "ssr_max_roughness")
col.prop(props, "ssr_thickness")
col.prop(props, "ssr_border_fade")
col.prop(props, "ssr_firefly_fac")
@@ -491,6 +519,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)
@@ -703,8 +732,9 @@ classes = (
RENDER_PT_eevee_bloom,
RENDER_PT_eevee_depth_of_field,
RENDER_PT_eevee_subsurface_scattering,
RENDER_PT_eevee_screen_space_reflections,
RENDER_PT_eevee_raytracing,
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

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

@@ -49,6 +49,8 @@
#include "BLT_translation.h"
#include "NOD_shader.h"
#include "DRW_engine.h"
#include "DEG_depsgraph.h"
@@ -227,3 +229,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

@@ -100,6 +100,9 @@ void _BLI_assert_unreachable_print(const char *file, int line, const char *funct
#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

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

@@ -681,6 +681,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

@@ -2273,8 +2273,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
scene->eevee.shadow_cascade_size = 1024;
scene->eevee.flag = SCE_EEVEE_VOLUMETRIC_LIGHTS | SCE_EEVEE_GTAO_BENT_NORMALS |
SCE_EEVEE_GTAO_BOUNCE | SCE_EEVEE_TAA_REPROJECTION |
SCE_EEVEE_SSR_HALF_RESOLUTION;
SCE_EEVEE_GTAO_BOUNCE | SCE_EEVEE_TAA_REPROJECTION;
/* If the file is pre-2.80 move on. */
if (scene->layer_properties == NULL) {
@@ -2342,9 +2341,9 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
EEVEE_GET_BOOL(props, taa_reprojection, SCE_EEVEE_TAA_REPROJECTION);
// EEVEE_GET_BOOL(props, sss_enable, SCE_EEVEE_SSS_ENABLED);
// EEVEE_GET_BOOL(props, sss_separate_albedo, SCE_EEVEE_SSS_SEPARATE_ALBEDO);
EEVEE_GET_BOOL(props, ssr_enable, SCE_EEVEE_SSR_ENABLED);
EEVEE_GET_BOOL(props, ssr_refraction, SCE_EEVEE_SSR_REFRACTION);
EEVEE_GET_BOOL(props, ssr_halfres, SCE_EEVEE_SSR_HALF_RESOLUTION);
EEVEE_GET_BOOL(props, ssr_enable, SCE_EEVEE_RAYTRACING_ENABLED);
// EEVEE_GET_BOOL(props, ssr_refraction, SCE_EEVEE_SSR_REFRACTION);
// EEVEE_GET_BOOL(props, ssr_halfres, SCE_EEVEE_SSR_HALF_RESOLUTION);
EEVEE_GET_INT(props, gi_diffuse_bounces);
EEVEE_GET_INT(props, gi_diffuse_bounces);

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"
@@ -2036,5 +2037,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

@@ -51,6 +51,25 @@ set(INC
${OPENSUBDIV_INCLUDE_DIRS}
)
set(EEVEE_BUILD_DIR
${CMAKE_CURRENT_BINARY_DIR}/engines/eevee
)
set(EEVEE_BUILD_SRC
engines/eevee/eevee_build.cc
engines/eevee/eevee_lut.c
)
add_executable(eevee_build ${EEVEE_BUILD_SRC})
target_include_directories(eevee_build PRIVATE ${INC})
add_custom_command(
OUTPUT
${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_lib.glsl
COMMAND
"$<TARGET_FILE:eevee_build>" --resolve_sample_table ${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_lib.glsl
DEPENDS eevee_build
)
set(SRC
intern/draw_cache.c
intern/draw_cache_extract_mesh.cc
@@ -118,33 +137,33 @@ set(SRC
engines/basic/basic_shader.c
engines/image/image_engine.cc
engines/image/image_shader.cc
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_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_hizbuffer.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_raytracing.cc
engines/eevee/eevee_renderpasses.cc
engines/eevee/eevee_shader.cc
engines/eevee/eevee_shading.cc
engines/eevee/eevee_shadow.cc
engines/eevee/eevee_subsurface.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
@@ -224,6 +243,10 @@ set(SRC
engines/basic/basic_private.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
@@ -248,95 +271,117 @@ set(LIB
)
set(GLSL_SRC
engines/eevee/shaders/ambient_occlusion_lib.glsl
engines/eevee/shaders/background_vert.glsl
engines/eevee/shaders/common_uniforms_lib.glsl
engines/eevee/shaders/common_utiltex_lib.glsl
engines/eevee/shaders/lights_lib.glsl
engines/eevee/shaders/lightprobe_lib.glsl
engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl
engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl
engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl
engines/eevee/shaders/lightprobe_geom.glsl
engines/eevee/shaders/lightprobe_vert.glsl
engines/eevee/shaders/lightprobe_cube_display_frag.glsl
engines/eevee/shaders/lightprobe_cube_display_vert.glsl
engines/eevee/shaders/lightprobe_grid_display_frag.glsl
engines/eevee/shaders/lightprobe_grid_display_vert.glsl
engines/eevee/shaders/lightprobe_grid_fill_frag.glsl
engines/eevee/shaders/lightprobe_planar_display_frag.glsl
engines/eevee/shaders/lightprobe_planar_display_vert.glsl
engines/eevee/shaders/lookdev_world_frag.glsl
engines/eevee/shaders/closure_eval_lib.glsl
engines/eevee/shaders/closure_eval_diffuse_lib.glsl
engines/eevee/shaders/closure_eval_glossy_lib.glsl
engines/eevee/shaders/closure_eval_refraction_lib.glsl
engines/eevee/shaders/closure_eval_translucent_lib.glsl
engines/eevee/shaders/closure_type_lib.glsl
engines/eevee/shaders/effect_bloom_frag.glsl
engines/eevee/shaders/effect_dof_bokeh_frag.glsl
engines/eevee/shaders/effect_dof_dilate_tiles_frag.glsl
engines/eevee/shaders/effect_dof_downsample_frag.glsl
engines/eevee/shaders/effect_dof_filter_frag.glsl
engines/eevee/shaders/effect_dof_flatten_tiles_frag.glsl
engines/eevee/shaders/effect_dof_gather_frag.glsl
engines/eevee/shaders/effect_dof_lib.glsl
engines/eevee/shaders/effect_dof_reduce_frag.glsl
engines/eevee/shaders/effect_dof_resolve_frag.glsl
engines/eevee/shaders/effect_dof_scatter_frag.glsl
engines/eevee/shaders/effect_dof_scatter_vert.glsl
engines/eevee/shaders/effect_dof_setup_frag.glsl
engines/eevee/shaders/effect_reflection_lib.glsl
engines/eevee/shaders/effect_reflection_resolve_frag.glsl
engines/eevee/shaders/effect_reflection_trace_frag.glsl
engines/eevee/shaders/effect_downsample_frag.glsl
engines/eevee/shaders/effect_downsample_cube_frag.glsl
engines/eevee/shaders/effect_gtao_frag.glsl
engines/eevee/shaders/effect_velocity_resolve_frag.glsl
engines/eevee/shaders/effect_velocity_tile_frag.glsl
engines/eevee/shaders/effect_minmaxz_frag.glsl
engines/eevee/shaders/effect_mist_frag.glsl
engines/eevee/shaders/effect_motion_blur_frag.glsl
engines/eevee/shaders/effect_subsurface_frag.glsl
engines/eevee/shaders/effect_translucency_frag.glsl
engines/eevee/shaders/effect_temporal_aa.glsl
engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl
engines/eevee/shaders/lightprobe_planar_downsample_geom.glsl
engines/eevee/shaders/lightprobe_planar_downsample_vert.glsl
engines/eevee/shaders/object_motion_frag.glsl
engines/eevee/shaders/object_motion_vert.glsl
engines/eevee/shaders/prepass_frag.glsl
engines/eevee/shaders/prepass_vert.glsl
engines/eevee/shaders/shadow_accum_frag.glsl
engines/eevee/shaders/shadow_frag.glsl
engines/eevee/shaders/shadow_vert.glsl
engines/eevee/shaders/bsdf_lut_frag.glsl
engines/eevee/shaders/btdf_lut_frag.glsl
engines/eevee/shaders/bsdf_common_lib.glsl
engines/eevee/shaders/irradiance_lib.glsl
engines/eevee/shaders/octahedron_lib.glsl
engines/eevee/shaders/cubemap_lib.glsl
engines/eevee/shaders/bsdf_sampling_lib.glsl
engines/eevee/shaders/random_lib.glsl
engines/eevee/shaders/raytrace_lib.glsl
engines/eevee/shaders/renderpass_lib.glsl
engines/eevee/shaders/renderpass_postprocess_frag.glsl
engines/eevee/shaders/cryptomatte_frag.glsl
engines/eevee/shaders/ltc_lib.glsl
engines/eevee/shaders/ssr_lib.glsl
engines/eevee/shaders/surface_frag.glsl
engines/eevee/shaders/surface_geom.glsl
engines/eevee/shaders/surface_lib.glsl
engines/eevee/shaders/surface_vert.glsl
engines/eevee/shaders/update_noise_frag.glsl
engines/eevee/shaders/volumetric_accum_frag.glsl
engines/eevee/shaders/volumetric_lib.glsl
engines/eevee/shaders/volumetric_frag.glsl
engines/eevee/shaders/volumetric_geom.glsl
engines/eevee/shaders/volumetric_vert.glsl
engines/eevee/shaders/volumetric_resolve_frag.glsl
engines/eevee/shaders/volumetric_scatter_frag.glsl
engines/eevee/shaders/volumetric_integration_frag.glsl
engines/eevee/shaders/eevee_bsdf_lib.glsl
engines/eevee/shaders/eevee_bsdf_microfacet_lib.glsl
engines/eevee/shaders/eevee_bsdf_sampling_lib.glsl
engines/eevee/shaders/eevee_bsdf_stubs_lib.glsl
engines/eevee/shaders/eevee_camera_lib.glsl
engines/eevee/shaders/eevee_camera_velocity_frag.glsl
engines/eevee/shaders/eevee_closure_lib.glsl
engines/eevee/shaders/eevee_cubemap_lib.glsl
engines/eevee/shaders/eevee_culling_debug_frag.glsl
engines/eevee/shaders/eevee_culling_iter_lib.glsl
engines/eevee/shaders/eevee_culling_lib.glsl
engines/eevee/shaders/eevee_culling_select_comp.glsl
engines/eevee/shaders/eevee_culling_sort_comp.glsl
engines/eevee/shaders/eevee_culling_tile_comp.glsl
engines/eevee/shaders/eevee_deferred_direct_frag.glsl
engines/eevee/shaders/eevee_deferred_holdout_frag.glsl
engines/eevee/shaders/eevee_deferred_transparent_frag.glsl
engines/eevee/shaders/eevee_deferred_volume_frag.glsl
engines/eevee/shaders/eevee_depth_clear_frag.glsl
engines/eevee/shaders/eevee_hiz_copy_frag.glsl
engines/eevee/shaders/eevee_hiz_downsample_frag.glsl
engines/eevee/shaders/eevee_depth_of_field_accumulator_lib.glsl
engines/eevee/shaders/eevee_depth_of_field_bokeh_lut_frag.glsl
engines/eevee/shaders/eevee_depth_of_field_filter_frag.glsl
engines/eevee/shaders/eevee_depth_of_field_gather_frag.glsl
engines/eevee/shaders/eevee_depth_of_field_gather_holefill_frag.glsl
engines/eevee/shaders/eevee_depth_of_field_lib.glsl
engines/eevee/shaders/eevee_depth_of_field_reduce_copy_frag.glsl
engines/eevee/shaders/eevee_depth_of_field_reduce_downsample_frag.glsl
engines/eevee/shaders/eevee_depth_of_field_reduce_recursive_frag.glsl
engines/eevee/shaders/eevee_depth_of_field_resolve_frag.glsl
engines/eevee/shaders/eevee_depth_of_field_scatter_frag.glsl
engines/eevee/shaders/eevee_depth_of_field_scatter_lib.glsl
engines/eevee/shaders/eevee_depth_of_field_scatter_vert.glsl
engines/eevee/shaders/eevee_depth_of_field_setup_frag.glsl
engines/eevee/shaders/eevee_depth_of_field_tiles_dilate_frag.glsl
engines/eevee/shaders/eevee_depth_of_field_tiles_flatten_frag.glsl
engines/eevee/shaders/eevee_film_filter_frag.glsl
engines/eevee/shaders/eevee_film_lib.glsl
engines/eevee/shaders/eevee_film_resolve_frag.glsl
engines/eevee/shaders/eevee_film_resolve_depth_frag.glsl
engines/eevee/shaders/eevee_gbuffer_lib.glsl
engines/eevee/shaders/eevee_irradiance_lib.glsl
engines/eevee/shaders/eevee_light_lib.glsl
engines/eevee/shaders/eevee_light_eval_lib.glsl
engines/eevee/shaders/eevee_lightprobe_display_cubemap_frag.glsl
engines/eevee/shaders/eevee_lightprobe_display_cubemap_vert.glsl
engines/eevee/shaders/eevee_lightprobe_display_grid_frag.glsl
engines/eevee/shaders/eevee_lightprobe_display_grid_vert.glsl
engines/eevee/shaders/eevee_lightprobe_display_lib.glsl
engines/eevee/shaders/eevee_lightprobe_eval_cubemap_lib.glsl
engines/eevee/shaders/eevee_lightprobe_eval_grid_lib.glsl
engines/eevee/shaders/eevee_lightprobe_filter_diffuse_frag.glsl
engines/eevee/shaders/eevee_lightprobe_filter_downsample_frag.glsl
engines/eevee/shaders/eevee_lightprobe_filter_geom.glsl
engines/eevee/shaders/eevee_lightprobe_filter_glossy_frag.glsl
engines/eevee/shaders/eevee_lightprobe_filter_lib.glsl
engines/eevee/shaders/eevee_lightprobe_filter_vert.glsl
engines/eevee/shaders/eevee_lightprobe_filter_visibility_frag.glsl
engines/eevee/shaders/eevee_lookdev_background_frag.glsl
engines/eevee/shaders/eevee_ltc_lib.glsl
engines/eevee/shaders/eevee_motion_blur_gather_frag.glsl
engines/eevee/shaders/eevee_motion_blur_lib.glsl
engines/eevee/shaders/eevee_motion_blur_tiles_dilate_frag.glsl
engines/eevee/shaders/eevee_motion_blur_tiles_flatten_frag.glsl
engines/eevee/shaders/eevee_nodetree_eval_lib.glsl
engines/eevee/shaders/eevee_raytrace_denoise_comp.glsl
engines/eevee/shaders/eevee_raytrace_raygen_frag.glsl
engines/eevee/shaders/eevee_raytrace_raygen_lib.glsl
engines/eevee/shaders/eevee_raytrace_resolve_frag.glsl
engines/eevee/shaders/eevee_raytrace_trace_lib.glsl
engines/eevee/shaders/eevee_sampling_lib.glsl
engines/eevee/shaders/eevee_shadow_debug_frag.glsl
engines/eevee/shaders/eevee_shadow_lib.glsl
engines/eevee/shaders/eevee_shadow_page_alloc_comp.glsl
engines/eevee/shaders/eevee_shadow_page_copy_comp.glsl
engines/eevee/shaders/eevee_shadow_page_debug_comp.glsl
engines/eevee/shaders/eevee_shadow_page_defrag_comp.glsl
engines/eevee/shaders/eevee_shadow_page_free_comp.glsl
engines/eevee/shaders/eevee_shadow_page_init_comp.glsl
engines/eevee/shaders/eevee_shadow_page_lib.glsl
engines/eevee/shaders/eevee_shadow_page_mark_vert.glsl
engines/eevee/shaders/eevee_shadow_tilemap_depth_scan_comp.glsl
engines/eevee/shaders/eevee_shadow_tilemap_lod_mask_comp.glsl
engines/eevee/shaders/eevee_shadow_tilemap_lib.glsl
engines/eevee/shaders/eevee_shadow_tilemap_setup_comp.glsl
engines/eevee/shaders/eevee_shadow_tilemap_tag_comp.glsl
engines/eevee/shaders/eevee_shadow_tilemap_visibility_comp.glsl
engines/eevee/shaders/eevee_subsurface_eval_frag.glsl
engines/eevee/shaders/eevee_surface_background_frag.glsl
engines/eevee/shaders/eevee_surface_deferred_frag.glsl
engines/eevee/shaders/eevee_surface_depth_frag.glsl
engines/eevee/shaders/eevee_surface_depth_simple_frag.glsl
engines/eevee/shaders/eevee_surface_forward_frag.glsl
engines/eevee/shaders/eevee_surface_gpencil_vert.glsl
engines/eevee/shaders/eevee_surface_hair_vert.glsl
engines/eevee/shaders/eevee_surface_lib.glsl
engines/eevee/shaders/eevee_surface_lookdev_vert.glsl
engines/eevee/shaders/eevee_surface_mesh_geom.glsl
engines/eevee/shaders/eevee_surface_mesh_vert.glsl
engines/eevee/shaders/eevee_surface_velocity_frag.glsl
engines/eevee/shaders/eevee_surface_velocity_lib.glsl
engines/eevee/shaders/eevee_surface_velocity_mesh_vert.glsl
engines/eevee/shaders/eevee_surface_world_vert.glsl
engines/eevee/shaders/eevee_velocity_lib.glsl
engines/eevee/shaders/eevee_volume_deferred_frag.glsl
engines/eevee/shaders/eevee_volume_eval_lib.glsl
engines/eevee/shaders/eevee_volume_lib.glsl
engines/eevee/shaders/eevee_volume_vert.glsl
engines/eevee/eevee_shader_shared.hh
engines/workbench/shaders/workbench_cavity_lib.glsl
engines/workbench/shaders/workbench_common_lib.glsl
@@ -368,19 +413,24 @@ set(GLSL_SRC
engines/workbench/workbench_shader_shared.h
intern/shaders/common_attribute_lib.glsl
intern/shaders/common_colormanagement_lib.glsl
intern/shaders/common_globals_lib.glsl
intern/shaders/common_pointcloud_lib.glsl
intern/shaders/common_hair_lib.glsl
intern/shaders/common_hair_refine_vert.glsl
intern/shaders/common_hair_refine_comp.glsl
intern/shaders/common_math_lib.glsl
intern/shaders/common_math_geom_lib.glsl
intern/shaders/common_view_clipping_lib.glsl
intern/shaders/common_view_lib.glsl
intern/shaders/common_fxaa_lib.glsl
intern/shaders/common_smaa_lib.glsl
intern/shaders/common_debug_lib.glsl
intern/shaders/common_fullscreen_vert.glsl
intern/shaders/common_fxaa_lib.glsl
intern/shaders/common_globals_lib.glsl
intern/shaders/common_gpencil_lib.glsl
intern/shaders/common_hair_lib.glsl
intern/shaders/common_hair_refine_comp.glsl
intern/shaders/common_hair_refine_vert.glsl
intern/shaders/common_intersection_lib.glsl
intern/shaders/common_math_geom_lib.glsl
intern/shaders/common_math_lib.glsl
intern/shaders/common_obinfos_lib.glsl
intern/shaders/common_pointcloud_lib.glsl
intern/shaders/common_smaa_lib.glsl
intern/shaders/common_uniform_attribute_lib.glsl
intern/shaders/common_view_lib.glsl
intern/shaders/common_subdiv_custom_data_interp_comp.glsl
intern/shaders/common_subdiv_ibo_lines_comp.glsl

View File

@@ -0,0 +1,219 @@
/*
* 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
*
* Allocators that may be moved to BLI at some point.
*/
#pragma once
#include "BLI_math_bits.h"
#include "BLI_vector.hh"
namespace blender::eevee {
/**
* Allow allocation and deletion of elements without reordering.
* Useful to keed valid indices to items inside this allocator.
* Type T need to implement the free_resources() method.
*/
template<typename T> class IndexedAllocator {
private:
Vector<T> items_;
/** Bitmap of used items. Make search of unused slot faster. */
Vector<uint32_t> unused_;
/** First unused batch of items in the vector for fast reallocating. */
int64_t unused_first_ = LONG_MAX;
/** Unused item count in the vector for fast reallocating. */
int64_t unused_count_ = 0;
public:
int64_t alloc(T &&value)
{
if (unused_count_ > 0) {
/* Reclaim unused slot. */
int64_t index = unused_first_;
items_[index] = std::move(value);
set_slot_used(index);
unused_count_ -= 1;
unused_first_ = first_unused_slot_get();
return index;
}
/* Not enough place, grow the vector. */
int64_t index = items_.append_and_get_index(value);
int64_t size_needed = (index + 32) / 32;
int64_t size_old = unused_.size();
unused_.resize(size_needed);
if (size_old < size_needed) {
for (auto i : IndexRange(size_old, size_needed - size_old)) {
/* Used by default. */
unused_[i] = 0;
}
}
set_slot_used(index);
return index;
}
void free(int64_t index)
{
unused_count_ += 1;
if (index < unused_first_) {
unused_first_ = index;
}
set_slot_unused(index);
is_slot_unused(index);
items_[index].free_resources();
}
/* Pruned unused shadows at the end of the vector. */
void resize()
{
while (items_.size() > 0 && is_slot_unused(items_.size() - 1)) {
set_slot_used(items_.size() - 1);
items_.remove_last();
unused_count_--;
}
if (unused_first_ >= items_.size()) {
/* First unused has been pruned. */
unused_first_ = first_unused_slot_get();
}
}
int64_t size() const
{
return items_.size() - unused_count_;
}
class Iterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = T;
using pointer = const T *;
using reference = const T &;
using difference_type = std::ptrdiff_t;
private:
IndexedAllocator &allocator_;
int64_t current_;
public:
constexpr explicit Iterator(IndexedAllocator &allocator, int64_t current)
: allocator_(allocator), current_(current)
{
}
constexpr Iterator &operator++()
{
current_++;
while ((current_ < allocator_.items_.size()) && allocator_.is_slot_unused(current_)) {
current_++;
}
return *this;
}
constexpr Iterator operator++(int) const
{
Iterator iterator = *this;
++*this;
return iterator;
}
constexpr friend bool operator!=(const Iterator &a, const Iterator &b)
{
return a.current_ != b.current_;
}
T &operator*()
{
BLI_assert(allocator_.is_slot_unused(current_) == false);
return allocator_[current_];
}
};
constexpr Iterator begin()
{
int64_t first_used = first_used_slot_get();
if (first_used == LONG_MAX) {
/* Will produce no iteration. */
first_used = items_.size();
}
return Iterator(*this, first_used);
}
constexpr Iterator end()
{
return Iterator(*this, items_.size());
}
const T &operator[](int64_t index) const
{
BLI_assert(is_slot_unused(index) == false);
return items_[index];
}
T &operator[](int64_t index)
{
BLI_assert(is_slot_unused(index) == false);
return items_[index];
}
private:
int64_t first_unused_slot_get(void) const
{
if (unused_count_ > 0) {
for (auto i : IndexRange(unused_.size())) {
if (unused_[i] != 0) {
return i * 32 + bitscan_forward_uint(unused_[i]);
}
}
}
return LONG_MAX;
}
int64_t first_used_slot_get(void) const
{
if (unused_count_ < items_.size()) {
for (auto i : IndexRange(unused_.size())) {
if (~unused_[i] != 0) {
return i * 32 + bitscan_forward_uint(~unused_[i]);
}
}
}
return LONG_MAX;
}
bool is_slot_unused(int64_t index) const
{
return (unused_[index / 32] & (1u << uint32_t(index % 32))) != 0;
}
void set_slot_unused(int64_t index)
{
SET_FLAG_FROM_TEST(unused_[index / 32], true, (1u << uint32_t(index % 32)));
}
void set_slot_used(int64_t index)
{
SET_FLAG_FROM_TEST(unused_[index / 32], false, (1u << uint32_t(index % 32)));
}
};
} // namespace blender::eevee

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,155 @@
/*
* 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
*
* Compile time computation and code generation.
*/
#include <algorithm>
#include <array>
#include <fstream>
#include <iostream>
#include <math.h>
#include <string>
#include <vector>
/* For blue_noise. */
#include "eevee_lut.h"
using namespace std;
typedef unsigned char uchar;
typedef float float3[3];
const int samples_per_pool = 32;
static void raytrace_sample_reuse_table(string &output_name, bool debug)
{
/** Following "Stochastic All The Things: Raytracing in Hybrid Real-Time Rendering"
* by Tomasz Stachowiak
* https://www.ea.com/seed/news/seed-dd18-presentation-slides-raytracing
*/
struct sample {
int x, y;
sample(int _x, int _y) : x(_x), y(_y){};
};
array<vector<sample>, 4> pools;
array<float3, 4> pools_color = {1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0};
vector<float3> debug_image(64 * 64);
ofstream ppm;
auto ppm_file_out = [&](const char *name, vector<float3> &debug_image) {
ppm.open(name);
ppm << "P3\n64 64\n255\n";
for (auto &vec : debug_image) {
uchar ucol[3] = {(uchar)(vec[0] * 255), (uchar)(vec[1] * 255), (uchar)(vec[2] * 255)};
ppm << (int)ucol[0] << " " << (int)ucol[1] << " " << (int)ucol[2] << "\n";
/* Clear the image. */
vec[0] = vec[1] = vec[2] = 0.0f;
}
ppm.close();
};
/* Using sample_reuse_1.ppm, set a center position where there is 4 different pool (color). */
int center[4][2] = {{49, 47}, {48, 46}, {49, 46}, {48, 47}};
/* Remapping of pool order since the center samples may not match the pool order from shader.
* Order is (0,0), (1,0), (0,1), (1,1).
* IMPORTANT: the actual PPM picture has Y flipped compared to OpenGL. */
int poolmap[4] = {3, 0, 1, 2};
for (int x = 0; x < 64; x++) {
for (int y = 0; y < 64; y++) {
int px = y * 64 + x;
float noise_value = blue_noise[px][0];
int pool_id = floorf(noise_value * 4.0f);
sample ofs(x - center[pool_id][0], y - center[pool_id][1]);
pools[pool_id].push_back(ofs);
for (int i = 0; i < 3; i++) {
debug_image[px][i] = pools_color[pool_id][i];
}
}
}
if (debug) {
ppm_file_out("sample_reuse_1.ppm", debug_image);
}
for (int pool_id = 0; pool_id < 4; pool_id++) {
auto &pool = pools[pool_id];
auto sort = [](const sample &a, const sample &b) {
/* Manhattan distance. */
return abs(a.x) + abs(a.y) < abs(b.x) + abs(b.y);
};
std::sort(pool.begin(), pool.end(), sort);
for (int j = 0; j < samples_per_pool; j++) {
int pos[2] = {pool[j].x + center[pool_id][0], pool[j].y + center[pool_id][1]};
int px = pos[1] * 64 + pos[0];
for (int i = 0; i < 3; i++) {
debug_image[px][i] = pools_color[pool_id][i];
}
}
}
if (debug) {
ppm_file_out("sample_reuse_2.ppm", debug_image);
}
/* TODO(fclem): Order the samples to have better spatial coherence. */
ofstream table_out;
int total = samples_per_pool * 4;
table_out.open(output_name);
table_out << "\n/* Sample table generated at build time. */\n";
table_out << "const int resolve_sample_max = " << samples_per_pool << ";\n";
table_out << "const float2 resolve_sample_offsets[" << total << "] = float2[" << total << "](\n";
for (int pool_id = 0; pool_id < 4; pool_id++) {
auto &pool = pools[poolmap[pool_id]];
for (int i = 0; i < samples_per_pool; i++) {
table_out << " float2(" << pool[i].x << ", " << pool[i].y << ")";
if (i < samples_per_pool - 1) {
table_out << ",\n";
}
}
if (pool_id < 3) {
table_out << ",\n";
}
}
table_out << ");\n\n";
table_out.close();
}
int main(int argc, char **argv)
{
if (argc != 3) {
fprintf(stderr, "usage: eevee_build [--resolve_sample_table] output_file\n");
return -1;
}
if (string(argv[1]) == "--resolve_sample_table") {
string output_name(argv[2]);
raytrace_sample_reuse_table(output_name, false);
}
return 0;
}

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.ptr(), false);
DRW_view_viewmat_get(inst_.drw_view, data.viewinv.ptr(), true);
DRW_view_winmat_get(inst_.drw_view, data.winmat.ptr(), false);
DRW_view_winmat_get(inst_.drw_view, data.wininv.ptr(), true);
DRW_view_persmat_get(inst_.drw_view, data.persmat.ptr(), false);
DRW_view_persmat_get(inst_.drw_view, data.persinv.ptr(), 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.ptr());
RE_GetCameraModelMatrix(inst_.render->re, camera_eval, data.viewinv.ptr());
invert_m4_m4(data.viewmat.ptr(), data.viewinv.ptr());
invert_m4_m4(data.wininv.ptr(), data.winmat.ptr());
mul_m4_m4m4(data.persmat.ptr(), data.winmat.ptr(), data.viewmat.ptr());
invert_m4_m4(data.persinv.ptr(), data.persmat.ptr());
data.uv_scale = float2(1.0f);
data.uv_bias = float2(0.0f);
}
else {
data.viewmat.identity();
data.viewinv.identity();
perspective_m4(data.winmat.ptr(), -0.1f, 0.1f, -0.1f, 0.1f, 0.1f, 1.0f);
data.wininv = data.winmat.inverted();
data.persmat = data.winmat * data.viewmat;
data.persinv = data.persmat.inverted();
}
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 = float2(0.0f);
data.equirect_scale = float2(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,149 @@
/*
* 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}},
};
inline void cubeface_winmat_get(float4x4 &winmat, float near, float far)
{
/* Simple 90° FOV projection. */
perspective_m4(winmat.ptr(), -near, near, -near, near, near, far);
}
/* -------------------------------------------------------------------- */
/** \name CameraData operators
* \{ */
inline bool operator==(const CameraData &a, const CameraData &b)
{
return compare_m4m4(a.persmat.ptr(), b.persmat.ptr(), 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_];
}
bool is_panoramic(void) const
{
return eevee::is_panoramic(data_[data_id_].type);
}
bool is_orthographic(void) const
{
return data_[data_id_].type == CAMERA_ORTHO;
}
float3 position(void) const
{
return float3(data_[data_id_].viewinv[3]);
}
};
/** \} */
} // namespace blender::eevee

View File

@@ -1,721 +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;
g_data->cryptomatte_accurate_mode = (view_layer->cryptomatte_flag &
VIEW_LAYER_CRYPTOMATTE_ACCURATE) != 0;
}
}
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(
buffer_size * eevee_cryptomatte_pixel_stride(view_layer),
sizeof(EEVEE_CryptomatteSample),
__func__);
/* Download buffer should store a float per active cryptomatte layer. */
g_data->cryptomatte_download_buffer = MEM_malloc_arrayN(
buffer_size * num_cryptomatte_layers, sizeof(float), __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_PrivateData *g_data = stl->g_data;
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;
/* In accurate mode all render samples are evaluated. In inaccurate mode this is 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 (g_data->cryptomatte_accurate_mode || 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
* \{ */
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

@@ -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,732 @@
/*
* 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 Instance &inst = inst_;
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 float4x4 winmat, int2 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.ptr(), jitter);
mul_project_m4_v3(winmat.ptr(), 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(float4x4 winmat, float4x4 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. */
float2 sample = float2(radius * cosf(theta), radius * sinf(theta));
sample *= data_.bokeh_anisotropic_scale;
/* Convert to NDC Space. */
float3 jitter = float3(UNPACK2(sample), -focus_distance_);
float3 center = float3(0.0f, 0.0f, -focus_distance_);
mul_project_m4_v3(winmat.ptr(), jitter);
mul_project_m4_v3(winmat.ptr(), 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 != float2(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_);
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_);
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_);
Framebuffer::swap(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_. */
Framebuffer::swap(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);
int2 res = (int2(divide_ceil_u(extent_[0], multiple), divide_ceil_u(extent_[1], multiple)) *
multiple) /
2;
int2 quater_res = int2(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_2d(GPU_RGBA16F, res, nullptr, mip_count);
reduced_coc_tx_.ensure_2d(GPU_R16F, res, nullptr, mip_count);
{
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_);
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_);
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_);
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_);
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_);
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_);
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_);
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. */
Texture reduced_coc_tx_ = {"dof_reduced_coc"};
Texture reduced_color_tx_ = {"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. */
Framebuffer bokeh_lut_fb_ = {"bokeh_lut_fb_"};
Framebuffer filter_bg_fb_ = {"filter_bg_fb_"};
Framebuffer filter_fg_fb_ = {"filter_fg_fb_"};
Framebuffer gather_fb_ = {"gather_fb_"};
Framebuffer gather_filter_bg_fb_ = {"gather_filter_bg_fb_"};
Framebuffer gather_holefill_fb_ = {"gather_holefill_fb_"};
Framebuffer reduce_copy_fb_ = {"reduce_copy_fb_"};
Framebuffer reduce_downsample_fb_ = {"reduce_downsample_fb_"};
Framebuffer reduce_fb_ = {"reduce_fb_"};
Framebuffer resolve_fb_ = {"resolve_fb_"};
Framebuffer scatter_bg_fb_ = {"scatter_bg_fb_"};
Framebuffer scatter_fg_fb_ = {"scatter_fg_fb_"};
Framebuffer setup_fb_ = {"setup_fb_"};
Framebuffer tiles_dilate_fb_ = {"tiles_dilate_fb_"};
Framebuffer tiles_flatten_fb_ = {"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. */
int2 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 string 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 float4x4 winmat, int2 input_extent);
/** Apply Depth Of Field jittering to the view and projection matrices.. */
void jitter_apply(float4x4 winmat, float4x4 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,524 +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;
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;
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
*/
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);
}
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();
}
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 frame-buffer 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,602 +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_SURF, 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);
/* Reset in case the same engine is used on multiple views. */
EEVEE_temporal_sampling_reset(vedata);
/* 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);
@@ -629,17 +150,19 @@ DrawEngineType draw_engine_eevee_type = {
&eevee_data_size,
&eevee_engine_init,
&eevee_engine_free,
NULL, /* instance_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,
@@ -654,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,253 @@
/*
* 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 int2 &full_extent, const rcti *output_rect)
{
FilmData data = data_;
data.extent = int2(BLI_rcti_size_x(output_rect), BLI_rcti_size_y(output_rect));
data.offset = int2(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_inv = float2(full_extent);
data_.uv_scale = 1.0f / data_.uv_scale_inv;
data_.uv_bias = float2(data_.offset) * data_.uv_scale;
}
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);
data_tx_[i].ensure_2d(tex_format, data_.extent);
/* TODO(fclem) The weight texture could be shared between all similar accumulators. */
weight_tx_[i].ensure_2d(GPU_R16F, data_.extent);
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_);
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_);
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)) {
GPUTexture *dtxl_color = DRW_viewport_texture_list_get()->color;
eGPUTextureFormat tex_format = GPU_texture_format(dtxl_color);
int extent[2] = {GPU_texture_width(dtxl_color), GPU_texture_height(dtxl_color)};
first_sample_tx_.ensure_2d(tex_format, extent);
first_sample_ref_ = first_sample_tx_;
}
else {
/* Reuse the data_tx since there is no need to blend. */
first_sample_tx_.free();
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_);
Framebuffer::swap(accumulation_fb_[0], accumulation_fb_[1]);
Texture::swap(data_tx_[0], data_tx_[1]);
Texture::swap(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,113 @@
/*
* 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. */
Framebuffer read_result_fb_;
Framebuffer accumulation_fb_[2];
Texture data_tx_[2];
Texture weight_tx_[2];
/** First sample in case we need to blend using it or just reuse it. */
Texture first_sample_tx_ = {"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];
draw::UniformBuffer<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), data_tx_{{name}, {name}}, weight_tx_{{name}, {name}}, name_(name)
{
data_.extent[0] = data_.extent[1] = -1;
data_.data_type = data_type;
data_.use_history = 0;
}
~Film(){};
void init(const int2 &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,258 @@
/*
* 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
*
* Gbuffer layout used for deferred shading pipeline.
*/
#pragma once
#include "DRW_render.h"
#include "eevee_wrapper.hh"
namespace blender::eevee {
class Instance;
/* -------------------------------------------------------------------- */
/** \name Gbuffer
*
* Fullscreen textures containing geometric, surface and volume data.
* Used by deferred shading layers. Only one gbuffer is allocated per view
* and is reused for each deferred layer. This is why there can only be temporary
* texture inside it.
* \{ */
/** NOTE: Theses are used as stencil bits. So we are limited to 8bits. */
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 {
TextureFromPool transmit_color_tx = {"GbufferTransmitColor"};
TextureFromPool transmit_normal_tx = {"GbufferTransmitNormal"};
TextureFromPool transmit_data_tx = {"GbufferTransmitData"};
TextureFromPool reflect_color_tx = {"GbufferReflectionColor"};
TextureFromPool reflect_normal_tx = {"GbufferReflectionNormal"};
TextureFromPool volume_tx = {"GbufferVolume"};
TextureFromPool emission_tx = {"GbufferEmission"};
TextureFromPool transparency_tx = {"GbufferTransparency"};
Framebuffer gbuffer_fb = {"Gbuffer"};
Framebuffer volume_fb = {"VolumeHeterogeneous"};
TextureFromPool holdout_tx = {"HoldoutRadiance"};
TextureFromPool diffuse_tx = {"DiffuseRadiance"};
Framebuffer radiance_fb = {"Radiance"};
Framebuffer radiance_clear_fb = {"RadianceClear"};
Framebuffer holdout_fb = {"Holdout"};
TextureFromPool depth_behind_tx = {"DepthBehind"};
Framebuffer depth_behind_fb = {"DepthCopy"};
/** Raytracing. */
TextureFromPool ray_data_tx = {"RayData"};
TextureFromPool ray_radiance_tx = {"RayRadiance"};
TextureFromPool ray_variance_tx = {"RayVariance"};
Framebuffer ray_data_fb = {"RayData"};
Framebuffer ray_denoise_fb = {"RayDenoise"};
/* 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();
transmit_normal_tx.sync();
transmit_data_tx.sync();
reflect_color_tx.sync();
reflect_normal_tx.sync();
volume_tx.sync();
emission_tx.sync();
transparency_tx.sync();
holdout_tx.sync();
diffuse_tx.sync();
depth_behind_tx.sync();
ray_data_tx.sync();
ray_radiance_tx.sync();
ray_variance_tx.sync();
}
void prepare(eClosureBits closures_used)
{
int2 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(extent, GPU_R11F_G11F_B10F, owner);
}
if (closures_used & (CLOSURE_SSS | CLOSURE_REFRACTION)) {
transmit_normal_tx.acquire(extent, GPU_RGBA16F, owner);
transmit_data_tx.acquire(extent, GPU_R11F_G11F_B10F, owner);
}
else if (closures_used & CLOSURE_DIFFUSE) {
transmit_normal_tx.acquire(extent, GPU_RG16F, owner);
}
if (closures_used & CLOSURE_SSS) {
diffuse_tx.acquire(extent, GPU_RGBA16F, owner);
}
if (closures_used & CLOSURE_REFLECTION) {
reflect_color_tx.acquire(extent, GPU_R11F_G11F_B10F, owner);
reflect_normal_tx.acquire(extent, GPU_RGBA16F, 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(extent, GPU_RGBA32UI, owner);
}
if (closures_used & CLOSURE_EMISSION) {
emission_tx.acquire(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(extent, GPU_RGBA16, owner);
}
if (closures_used & (CLOSURE_DIFFUSE | CLOSURE_REFLECTION | CLOSURE_REFRACTION)) {
ray_data_tx.acquire(extent, GPU_RGBA16F, owner);
ray_radiance_tx.acquire(extent, GPU_RGBA16F, owner);
ray_variance_tx.acquire(extent, GPU_R8, owner);
}
holdout_tx.acquire(extent, GPU_R11F_G11F_B10F, owner);
depth_behind_tx.acquire(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));
}
void bind(void)
{
GPU_framebuffer_bind(gbuffer_fb);
GPU_framebuffer_clear_stencil(gbuffer_fb, 0x0);
}
void bind_radiance(void)
{
/* Layer attachement also works with cubemap. */
radiance_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer),
GPU_ATTACHMENT_TEXTURE(combined_tx),
GPU_ATTACHMENT_TEXTURE(diffuse_tx));
GPU_framebuffer_bind(radiance_fb);
}
void bind_volume(void)
{
/* Layer attachement also works with cubemap. */
volume_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer),
GPU_ATTACHMENT_TEXTURE(volume_tx),
GPU_ATTACHMENT_TEXTURE(transparency_tx));
GPU_framebuffer_bind(volume_fb);
}
void bind_tracing(void)
{
/* Layer attachement also works with cubemap. */
/* Attach depth_stencil buffer to only trace the surfaces that need it. */
ray_data_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer),
GPU_ATTACHMENT_TEXTURE(ray_data_tx),
GPU_ATTACHMENT_TEXTURE(ray_radiance_tx));
GPU_framebuffer_bind(ray_data_fb);
float color[4] = {0.0f};
GPU_framebuffer_clear_color(ray_data_fb, color);
}
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 clear_radiance(void)
{
radiance_clear_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(diffuse_tx));
GPU_framebuffer_bind(radiance_clear_fb);
float color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_framebuffer_clear_color(radiance_clear_fb, color);
}
void render_end(void)
{
transmit_color_tx.release();
transmit_normal_tx.release();
transmit_data_tx.release();
reflect_color_tx.release();
reflect_normal_tx.release();
volume_tx.release();
emission_tx.release();
transparency_tx.release();
holdout_tx.release();
diffuse_tx.release();
depth_behind_tx.release();
ray_data_tx.release();
ray_radiance_tx.release();
ray_variance_tx.release();
}
};
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,155 @@
/*
* 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);
bool is_caster = true; /* TODO material.shadow.shgrp. */
bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */
shadows.sync_object(ob, ob_handle, is_caster, is_alpha_blend);
}
} // 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);
bool is_caster = material.shadow.shgrp != nullptr;
bool is_alpha_blend = material.is_alpha_blend_transparent;
shadows.sync_object(ob, ob_handle, is_caster, is_alpha_blend);
}
} // namespace blender::eevee

View File

@@ -0,0 +1,102 @@
/*
* 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 Hierarchical-Z buffer is texture containing a copy of the depth buffer with mipmaps.
* Each mip contains the maximum depth of each 4 pixels on the upper level.
* The size of the texture is padded to avoid messing with the mipmap pixels alignments.
*/
#include "eevee_instance.hh"
#include "eevee_hizbuffer.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name Hierarchical-Z buffer
*
* \{ */
void HiZBufferModule::sync(void)
{
{
hiz_copy_ps_ = DRW_pass_create("HizCopy", DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(HIZ_COPY);
DRWShadingGroup *grp = DRW_shgroup_create(sh, hiz_copy_ps_);
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
{
hiz_downsample_ps_ = DRW_pass_create("HizDownsample", DRW_STATE_WRITE_COLOR);
GPUShader *sh = inst_.shaders.static_shader_get(HIZ_DOWNSAMPLE);
DRWShadingGroup *grp = DRW_shgroup_create(sh, hiz_downsample_ps_);
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
DRW_shgroup_uniform_vec2(grp, "texel_size", texel_size_, 1);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
}
void HiZBuffer::prepare(GPUTexture *depth_src_tx)
{
int div = 1 << mip_count_;
float2 extent_src(GPU_texture_width(depth_src_tx), GPU_texture_height(depth_src_tx));
int2 extent_hiz(divide_ceil_u(extent_src.x, div) * div, divide_ceil_u(extent_src.y, div) * div);
inst_.hiz.data_.pixel_to_ndc = 2.0f / extent_src;
inst_.hiz.texel_size_ = 1.0f / float2(extent_hiz);
inst_.hiz.data_.uv_scale = extent_src / float2(extent_hiz);
inst_.hiz.data_.push_update();
/* TODO/OPTI(fclem): Share it between similar views.
* Not possible right now because request_tmp does not support mipmaps. */
hiz_tx_.ensure_2d(GPU_R32F, extent_hiz, nullptr, mip_count_);
hiz_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(hiz_tx_));
GPU_texture_mipmap_mode(hiz_tx_, true, false);
}
void HiZBuffer::recursive_downsample(void *thunk, int UNUSED(lvl))
{
HiZBufferModule &hiz = *reinterpret_cast<HiZBufferModule *>(thunk);
hiz.texel_size_ *= 2.0f;
DRW_draw_pass(hiz.hiz_downsample_ps_);
}
void HiZBuffer::update(GPUTexture *depth_src_tx)
{
DRW_stats_group_start("Hiz");
inst_.hiz.texel_size_ = 1.0f / float2(GPU_texture_width(hiz_tx_), GPU_texture_height(hiz_tx_));
inst_.hiz.input_depth_tx_ = depth_src_tx;
GPU_framebuffer_bind(hiz_fb_);
DRW_draw_pass(inst_.hiz.hiz_copy_ps_);
inst_.hiz.input_depth_tx_ = hiz_tx_;
GPU_framebuffer_recursive_downsample(hiz_fb_, mip_count_, &recursive_downsample, &inst_.hiz);
DRW_stats_group_end();
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,103 @@
/*
* 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 Hierarchical-Z buffer is texture containing a copy of the depth buffer with mipmaps.
* Each mip contains the maximum depth of each 4 pixels on the upper level.
* The size of the texture is padded to avoid messing with the mipmap pixels alignments.
*/
#pragma once
#include "DRW_render.h"
#include "eevee_shader_shared.hh"
namespace blender::eevee {
class Instance;
/* -------------------------------------------------------------------- */
/** \name Hierarchical-Z buffer
* \{ */
class HiZBuffer {
private:
Instance &inst_;
/** Framebuffer use for recursive downsampling. */
/* TODO(fclem) Remove this and use a compute shader instead. */
Framebuffer hiz_fb_ = draw::Framebuffer("DepthHiz");
/** Max mip to downsample to. We ensure the hiz has enough padding to never
* have to compensate the mipmap alignments. */
constexpr static int mip_count_ = 6;
/** TODO/OPTI(fclem): Share it between similar views. */
Texture hiz_tx_ = {"hiz_tx_"};
public:
HiZBuffer(Instance &inst) : inst_(inst){};
void prepare(GPUTexture *depth_src);
void update(GPUTexture *depth_src);
GPUTexture *texture_get(void) const
{
return hiz_tx_;
}
private:
static void recursive_downsample(void *thunk, int lvl);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Hierarchical-Z buffer Module
* \{ */
class HiZBufferModule {
friend HiZBuffer;
private:
Instance &inst_;
HiZDataBuf data_;
/** Copy input depth to hiz-buffer with border padding. */
DRWPass *hiz_copy_ps_;
/** Downsample one mipmap level. */
DRWPass *hiz_downsample_ps_;
/** References only. */
GPUTexture *input_depth_tx_ = nullptr;
/** Pixel size of the render target during hiz downsampling. */
float2 texel_size_;
public:
HiZBufferModule(Instance &inst) : inst_(inst){};
void sync(void);
const GPUUniformBuf *ubo_get(void) const
{
return data_;
}
};
/** \} */
} // 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,284 @@
/*
* 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() : ob(nullptr), parent(nullptr){};
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,316 @@
/*
* 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_global.h"
#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 int2 &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_;
debug_mode = (eDebugMode)G.debug_value;
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();
raytracing.sync();
hiz.sync();
lookdev.sync_background();
lookdev.sync_overlay();
materials.begin_sync();
velocity.begin_sync();
lights.begin_sync();
shadows.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();
subsurface.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,190 @@
/*
* 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_hizbuffer.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_raytracing.hh"
#include "eevee_renderpasses.hh"
#include "eevee_sampling.hh"
#include "eevee_shader.hh"
#include "eevee_shading.hh"
#include "eevee_shadow.hh"
#include "eevee_subsurface.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;
RaytracingModule raytracing;
HiZBufferModule hiz;
/* TODO(fclem) Move it to scene layer data. */
ShadowModule shadows;
SubsurfaceModule subsurface;
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;
eDebugMode debug_mode = SHADOW_DEBUG_NONE;
/* 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),
raytracing(*this),
hiz(*this),
shadows(*this),
subsurface(*this),
sync(*this),
materials(*this),
lookdev(*this){};
~Instance(){};
void init(const int2 &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,482 @@
/*
* 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 = float3(&la->r) * la->energy;
normalize_m4_m4_ex(this->object_mat.ptr(), ob->obmat, scale);
/* Make sure we have consistent handedness (in case of negatively scaled Z axis). */
float3 cross = math::cross(float3(this->_right), float3(this->_up));
if (math::dot(cross, float3(this->_back)) < 0.0f) {
negate_v3(this->_up);
}
shape_parameters_set(la, scale);
float shape_power = shape_power_get(la);
float point_power = point_power_get(la);
this->diffuse_power = la->diff_fac * shape_power;
this->transmit_power = la->diff_fac * point_power;
this->specular_power = la->spec_fac * shape_power;
this->volume_power = la->volume_fac * point_power;
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) {
if (this->shadow_id == LIGHT_NO_SHADOW) {
this->shadow_id = shadows.directionals.alloc();
}
ShadowDirectional &shadow = shadows.directionals[this->shadow_id];
shadow.sync(this->object_mat, la->bias * 0.05f, 1.0f);
}
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.punctuals.alloc();
}
ShadowPunctual &shadow = shadows.punctuals[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.punctuals.free(shadow_id);
}
else {
shadows.directionals.free(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::point_power_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)
{
Vector<ObjectKey, 0> deleted_keys;
light_refs_.clear();
/* Detect light deletion. */
culling_data.items_no_cull_count = 0;
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;
light_refs_.append(&light);
if (light.type == LIGHT_SUN) {
culling_data.items_no_cull_count++;
}
}
}
if (deleted_keys.size() > 0) {
inst_.sampling.reset();
}
for (auto key : deleted_keys) {
lights_.remove(key);
}
if (light_refs_.size() > CULLING_MAX_ITEM) {
/* TODO(fclem) Print error to user. */
light_refs_.resize(CULLING_MAX_ITEM);
}
batch_len_ = divide_ceil_u(max_ii(light_refs_.size(), 1), CULLING_BATCH_SIZE);
lights_data.resize(batch_len_ * CULLING_BATCH_SIZE);
culling_key_buf.resize(batch_len_ * CULLING_BATCH_SIZE);
culling_light_buf.resize(batch_len_ * CULLING_BATCH_SIZE);
culling_zbin_buf.resize(batch_len_ * CULLING_ZBIN_COUNT);
culling_data.items_count = light_refs_.size();
culling_data.tile_word_len = divide_ceil_u(max_ii(culling_data.items_count, 1), 32);
/* Call shadows.end_sync after light pruning to avoid packing deleted shadows. */
inst_.shadows.end_sync();
int direc_idx = 0;
int punct_idx = culling_data.items_no_cull_count;
for (auto l_idx : light_refs_.index_range()) {
Light &light = *light_refs_[l_idx];
int dst_idx = (light.type == LIGHT_SUN) ? direc_idx++ : punct_idx++;
lights_data[dst_idx] = light;
if (light.shadow_id != LIGHT_NO_SHADOW) {
if (light.type == LIGHT_SUN) {
lights_data[dst_idx].shadow_data = this->inst_.shadows.directionals[light.shadow_id];
}
else {
lights_data[dst_idx].shadow_data = this->inst_.shadows.punctuals[light.shadow_id];
}
}
}
lights_data.push_update();
{
culling_ps_ = DRW_pass_create("CullingLight", (DRWState)0);
uint lights_len = light_refs_.size();
uint batch_len = divide_ceil_u(lights_len, CULLING_BATCH_SIZE);
if (batch_len > 0) {
/* NOTE: We reference the buffers that may be resized or updated later. */
{
GPUShader *sh = inst_.shaders.static_shader_get(CULLING_SELECT);
DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_ps_);
DRW_shgroup_vertex_buffer(grp, "lights_buf", lights_data);
DRW_shgroup_vertex_buffer_ref(grp, "culling_buf", &culling_data);
DRW_shgroup_vertex_buffer(grp, "key_buf", culling_key_buf);
DRW_shgroup_call_compute(grp, batch_len, 1, 1);
DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE);
}
{
GPUShader *sh = inst_.shaders.static_shader_get(CULLING_SORT);
DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_ps_);
DRW_shgroup_vertex_buffer(grp, "lights_buf", lights_data);
DRW_shgroup_vertex_buffer_ref(grp, "culling_buf", &culling_data);
DRW_shgroup_vertex_buffer(grp, "key_buf", culling_key_buf);
DRW_shgroup_vertex_buffer_ref(grp, "out_zbins_buf", &culling_zbin_buf);
DRW_shgroup_vertex_buffer_ref(grp, "out_items_buf", &culling_light_buf);
DRW_shgroup_call_compute(grp, batch_len, 1, 1);
DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE);
}
{
GPUShader *sh = inst_.shaders.static_shader_get(CULLING_TILE);
DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_ps_);
DRW_shgroup_vertex_buffer(grp, "lights_buf", culling_light_buf);
DRW_shgroup_vertex_buffer_ref(grp, "culling_buf", &culling_data);
DRW_shgroup_vertex_buffer_ref(grp, "culling_tile_buf", &culling_tile_buf);
DRW_shgroup_call_compute_ref(grp, culling_tile_dispatch_size_);
DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH);
}
}
}
debug_end_sync();
}
void LightModule::debug_end_sync(void)
{
if (inst_.debug_mode != eDebugMode::DEBUG_LIGHT_CULLING) {
debug_draw_ps_ = nullptr;
return;
}
debug_draw_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_draw_ps_);
DRW_shgroup_vertex_buffer_ref(grp, "lights_buf", &culling_light_buf);
DRW_shgroup_vertex_buffer_ref(grp, "lights_culling_buf", &culling_data);
DRW_shgroup_vertex_buffer_ref(grp, "lights_zbins_buf", &culling_zbin_buf);
DRW_shgroup_vertex_buffer_ref(grp, "lights_tile_buf", &culling_tile_buf);
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
/* Compute acceleration structure for the given view. If extent is 0, bind no lights. */
void LightModule::set_view(const DRWView *view, const int2 extent, bool enable_specular)
{
const bool no_lights = (extent.x == 0);
/* Target 1bit per pixel. */
uint tile_size = 1u << log2_ceil_u(ceil(sqrtf(culling_data.tile_word_len * 32)));
int3 tiles_extent;
tiles_extent.x = divide_ceil_u(extent.x, tile_size);
tiles_extent.y = divide_ceil_u(extent.y, tile_size);
tiles_extent.z = batch_len_;
float far_z = DRW_view_far_distance_get(view);
float near_z = DRW_view_near_distance_get(view);
culling_data.zbin_scale = -CULLING_ZBIN_COUNT / fabsf(far_z - near_z);
culling_data.zbin_bias = -near_z * culling_data.zbin_scale;
culling_data.tile_size = tile_size;
culling_data.tile_x_len = tiles_extent.x;
culling_data.tile_y_len = tiles_extent.y;
culling_data.tile_to_uv_fac = tile_size / float2(UNPACK2(extent));
culling_data.enable_specular = enable_specular;
culling_data.items_count = no_lights ? 0 : light_refs_.size();
culling_data.visible_count = 0;
culling_data.push_update();
if (no_lights) {
return;
}
uint word_count = tiles_extent.x * tiles_extent.y * tiles_extent.z * culling_data.tile_word_len;
/* TODO(fclem) Only resize once per redraw. */
culling_tile_buf.resize(word_count);
culling_tile_dispatch_size_.x = divide_ceil_u(word_count, 1024);
culling_tile_dispatch_size_.y = 1;
culling_tile_dispatch_size_.z = 1;
DRW_view_set_active(view);
DRW_draw_pass(culling_ps_);
}
void LightModule::debug_draw(GPUFrameBuffer *view_fb, HiZBuffer &hiz)
{
if (debug_draw_ps_ == nullptr) {
return;
}
input_depth_tx_ = hiz.texture_get();
GPU_framebuffer_bind(view_fb);
DRW_draw_pass(debug_draw_ps_);
}
void LightModule::shgroup_resources(DRWShadingGroup *grp)
{
DRW_shgroup_vertex_buffer_ref(grp, "lights_buf", &culling_light_buf);
DRW_shgroup_vertex_buffer_ref(grp, "lights_culling_buf", &culling_data);
DRW_shgroup_vertex_buffer_ref(grp, "lights_zbins_buf", &culling_zbin_buf);
DRW_shgroup_vertex_buffer_ref(grp, "lights_tile_buf", &culling_tile_buf);
DRW_shgroup_uniform_texture(grp, "shadow_atlas_tx", inst_.shadows.atlas_tx_get());
DRW_shgroup_uniform_texture(grp, "shadow_tilemaps_tx", inst_.shadows.tilemap_tx_get());
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,140 @@
/*
* 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_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 point_power_get(const ::Light *la);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name LightModule
* \{ */
/**
* The light module manages light data buffers and light culling system.
*/
class LightModule {
friend ShadowModule;
public:
/** Scene lights data. */
LightDataBuf lights_data;
/** Shadow data. TODO(fclem): merge with lights_data. */
ShadowDataBuf shadows_data;
/** Culling infos. */
CullingDataBuf culling_data;
/** Key buffer containing only visible lights indices. */
CullingKeyBuf culling_key_buf;
/** LightData buffer used for rendering. Ordered by the culling phase. */
CullingLightBuf culling_light_buf;
/** Zbins containing min and max light index for each Z bin. */
CullingZbinBuf culling_zbin_buf;
/** Bitmap of lights touching each tiles. Using one layer for each culling batch. */
CullingTileBuf culling_tile_buf;
private:
Instance &inst_;
/** Map of light objects. This is used to track light deletion. */
Map<ObjectKey, Light> lights_;
Vector<Light *> light_refs_;
/** 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 */
DRWPass *culling_ps_ = nullptr;
int3 culling_tile_dispatch_size_ = int3(1);
/* Number of batches of lights that are separately processed. */
int batch_len_ = 1;
float light_threshold_;
/** Debug Culling visualization. */
DRWPass *debug_draw_ps_ = nullptr;
GPUTexture *input_depth_tx_ = nullptr;
public:
LightModule(Instance &inst) : inst_(inst){};
~LightModule(){};
void begin_sync(void);
void sync_light(const Object *ob, ObjectHandle &handle);
void end_sync(void);
void set_view(const DRWView *view, const int2 extent, bool enable_specular = true);
void shgroup_resources(DRWShadingGroup *grp);
void debug_end_sync(void);
void debug_draw(GPUFrameBuffer *view_fb, HiZBuffer &hiz);
};
/** \} */
} // 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

@@ -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,580 @@
/*
* 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_camera.hh"
#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()) {
MEM_delete(lightcache_lookdev_);
}
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;
int3 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_);
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_);
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_);
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_);
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(float3(0.0f), 0.01f, 1.0f, true);
}
}
void LightProbeModule::cubemap_prepare(float3 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);
float4x4 viewmat;
viewmat.identity();
negate_v3_v3(viewmat[3], position);
/* TODO(fclem) We might want to have theses as temporary textures. */
cube_depth_tx_.ensure_cube(GPU_DEPTH24_STENCIL8, cube_res, nullptr, cube_mip_count);
cube_color_tx_.ensure_cube(GPU_RGBA16F, cube_res, nullptr, cube_mip_count);
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));
float4x4 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();
int2 extent = int2(3, 2);
int2 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)
{
int2 extent = int2(info_data_.grids.visibility_size);
int2 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_RAYTRACING_ENABLED);
inst_.init(int2(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.ptr(), view_bounds.radius);
negate_v3_v3(grid.local_mat[3], view_bounds.center);
cube.parallax_mat = cube.influence_mat = grid.local_mat;
grid.resolution = int3(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 = float3(0.0f);
grid.increment_y = float3(0.0f);
grid.increment_z = float3(0.0f);
grid.corner = float3(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.ptr(), grid_cache.mat);
grid.resolution = int3(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 = float3(grid_cache.increment_x);
grid.increment_y = float3(grid_cache.increment_y);
grid.increment_z = float3(grid_cache.increment_z);
grid.corner = float3(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.ptr(), cube_cache.parallaxmat);
copy_m4_m4(cube.influence_mat.ptr(), 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 int2 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,175 @@
/*
* 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_ = {"CubemapDepth"};
Texture cube_color_tx_ = {"CubemapColor"};
LightProbeView probe_views_[6];
Framebuffer cube_downsample_fb_ = {"cube_downsample"};
Framebuffer filter_cube_fb_ = {"filter_cube"};
Framebuffer filter_grid_fb_ = {"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()
{
MEM_delete(lightcache_lookdev_);
MEM_delete(lightcache_baking_);
}
void init();
void begin_sync();
void end_sync();
void set_view(const DRWView *view, const int2 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_;
}
const GPUUniformBuf *cube_ubo_get() const
{
return cube_data_;
}
const GPUUniformBuf *info_ubo_get() const
{
return info_data_;
}
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);
LightCache *baking_cache_get(void);
void cubemap_prepare(float3 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,268 +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"
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,359 @@
/*
* 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 int2 &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;
int2 anchor = int2(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 int2 &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 != int2(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(float4x4 r_mat)
{
if (studiolight_ == nullptr) {
r_mat.identity();
}
else {
axis_angle_to_mat4_single(r_mat.ptr(), 'Z', rotation_);
}
if (view_rotation_) {
float4x4 x_rot_matrix;
const CameraData &cam = inst_.camera.data_get();
axis_angle_to_mat4_single(x_rot_matrix.ptr(), 'X', M_PI / 2.0f);
r_mat = r_mat * (x_rot_matrix * cam.viewmat);
}
}
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. */
float2 jitter = -0.5f + float2(inst_.sampling.rng_get(SAMPLING_FILTER_U),
inst_.sampling.rng_get(SAMPLING_FILTER_V));
/* Matrix used to position the spheres in viewport space. */
float4x4 sphere_mat = cam.viewmat;
const float *viewport_size = DRW_viewport_size_get();
const int sphere_margin = sphere_size_ / 6;
float2 offset = float2(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_);
lights.shgroup_resources(grp);
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());
offset.x -= sphere_size_ + sphere_margin;
/* Pass 2D scale and bias factor in the last column. */
float2 scale = sphere_size_ / float2(viewport_size);
float2 bias = -1.0f + scale +
2.0f * (float2(anchor_) + offset + jitter) / float2(viewport_size);
copy_v4_fl4(sphere_mat[3], UNPACK2(scale), UNPACK2(bias));
DRW_shgroup_call_obmat(grp, sphere, sphere_mat.ptr());
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, int2(0));
inst_.lights.set_view(active_view, int2(0));
/* Create subview for correct shading. Sub because we don not care about culling. */
const CameraData &cam = inst_.camera.data_get();
float4x4 winmat;
orthographic_m4(winmat.ptr(), -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
if (view_) {
DRW_view_update_sub(view_, cam.viewmat.ptr(), winmat.ptr());
}
else {
view_ = DRW_view_create_sub(active_view, cam.viewmat.ptr(), winmat.ptr());
}
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. */
int2 anchor_;
public:
LookDev(Instance &inst) : inst_(inst){};
~LookDev()
{
GPU_material_free(&material);
};
void init(const int2 &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(float4x4 r_mat);
private:
bool do_overlay(const int2 &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,335 @@
/*
* 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);
mat.is_alpha_blend_transparent = (blender_mat->blend_method == MA_BM_BLEND) &&
GPU_material_flag_get(mat.prepass.gpumat,
GPU_MATFLAG_TRANSPARENT);
}
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();
const int materials_len = DRW_cache_object_material_count_get(ob);
for (auto i : IndexRange(materials_len)) {
::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,123 @@
/*
* 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;
bool is_alpha_blend_transparent;
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,58 @@
/*
* 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());
if (mat_geom == nullptr) {
return;
}
bool is_shadow_caster = false;
bool is_alpha_blend = false;
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);
is_shadow_caster = is_shadow_caster || material->shadow.shgrp != nullptr;
is_alpha_blend = is_alpha_blend || material->is_alpha_blend_transparent;
}
shading_passes.velocity.mesh_add(ob, ob_handle);
shadows.sync_object(ob, ob_handle, is_shadow_caster, is_alpha_blend);
}
} // 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,613 +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_duplilist.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;
}
const DupliObject *dup = DRW_object_get_dupli(ob);
if (dup != NULL && dup->ob->data != dup->ob_data) {
/* Geometry instances do not support motion blur correctly yet. The #key used in
* #motion_blur_deform_data_get has to take ids of instances (#DupliObject.persistent_id) into
* account. Otherwise it can't find matching geometry instances at different points in time. */
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_);
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_);
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_);
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_);
Framebuffer::swap(tiles_flatten_fb_, tiles_dilate_fb_);
}
/* Swap again so result is in tiles_dilated_tx_. */
SWAP(GPUTexture *, tiles_tx_, tiles_dilated_tx_);
Framebuffer::swap(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. */
Framebuffer tiles_flatten_fb_;
Framebuffer tiles_dilate_fb_;
Framebuffer gather_fb_;
draw::UniformBuffer<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,281 +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(
UNPACK2(effects->hiz_size), 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(
UNPACK2(fs_size), 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 *sldata, EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
if ((effects->enabled_effects & EFFECT_GTAO) != 0) {
DRW_stats_group_start("GTAO Horizon Scan");
GPU_framebuffer_bind(fbl->gtao_fb);
/** NOTE(fclem): Kind of fragile. We need this to make sure everything lines up
* nicely during planar reflection. */
if (common_data->ray_type != EEVEE_RAY_GLOSSY) {
const float *viewport_size = DRW_viewport_size_get();
GPU_framebuffer_viewport_set(fbl->gtao_fb, 0, 0, UNPACK2(viewport_size));
}
DRW_draw_pass(psl->ao_horizon_search);
if (common_data->ray_type != EEVEE_RAY_GLOSSY) {
GPU_framebuffer_viewport_reset(fbl->gtao_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();
}
/* 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

@@ -0,0 +1,303 @@
/*
* 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
*
* Module containing passes and parameters used for raytracing.
* NOTE: For now only screen space raytracing is supported.
*/
#include <fstream>
#include <iostream>
#include "eevee_instance.hh"
#include "eevee_raytracing.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name Raytracing
*
* \{ */
void RaytracingModule::sync(void)
{
SceneEEVEE &sce_eevee = inst_.scene->eevee;
reflection_data_.thickness = sce_eevee.ssr_thickness;
reflection_data_.brightness_clamp = (sce_eevee.ssr_firefly_fac < 1e-8f) ?
FLT_MAX :
sce_eevee.ssr_firefly_fac;
reflection_data_.max_roughness = sce_eevee.ssr_max_roughness + 0.01f;
reflection_data_.quality = 1.0f - 0.95f * sce_eevee.ssr_quality;
reflection_data_.bias = 0.8f + sce_eevee.ssr_quality * 0.15f;
reflection_data_.pool_offset = inst_.sampling.sample_get() / 5;
refraction_data_ = static_cast<RaytraceData>(reflection_data_);
// refraction_data_.thickness = 1e16;
/* TODO(fclem): Clamp option for refraction. */
/* TODO(fclem): bias option for refraction. */
/* TODO(fclem): bias option for refraction. */
diffuse_data_ = static_cast<RaytraceData>(reflection_data_);
diffuse_data_.max_roughness = 1.01f;
reflection_data_.push_update();
refraction_data_.push_update();
diffuse_data_.push_update();
enabled_ = (sce_eevee.flag & SCE_EEVEE_RAYTRACING_ENABLED) != 0;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Raytracing Buffers
*
* \{ */
void RaytraceBuffer::sync(int2 extent)
{
extent_ = extent;
dispatch_size_.x = divide_ceil_u(extent.x, 8);
dispatch_size_.y = divide_ceil_u(extent.y, 8);
dispatch_size_.z = 1;
/* Make sure the history matrix is up to date. */
data_.push_update();
LightProbeModule &lightprobes = inst_.lightprobes;
eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
/* The raytracing buffer contains the draw passes since it is stored per view and we need to
* dispatch compute shaders with the right workgroup size. */
{
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL;
std::array<DRWShadingGroup *, 3> grps;
bool do_rt = inst_.raytracing.enabled();
{
trace_reflection_ps_ = DRW_pass_create("TraceReflection", state);
GPUShader *sh = inst_.shaders.static_shader_get(do_rt ? RAYTRACE_REFLECTION :
RAYTRACE_REFLECTION_FALLBACK);
grps[0] = DRW_shgroup_create(sh, trace_reflection_ps_);
DRW_shgroup_uniform_block(grps[0], "raytrace_block", inst_.raytracing.reflection_ubo_get());
DRW_shgroup_stencil_set(grps[0], 0x0, 0x0, CLOSURE_REFLECTION);
}
{
trace_refraction_ps_ = DRW_pass_create("TraceRefraction", state);
GPUShader *sh = inst_.shaders.static_shader_get(do_rt ? RAYTRACE_REFRACTION :
RAYTRACE_REFRACTION_FALLBACK);
grps[1] = DRW_shgroup_create(sh, trace_refraction_ps_);
DRW_shgroup_uniform_block(grps[1], "raytrace_block", inst_.raytracing.refraction_ubo_get());
DRW_shgroup_stencil_set(grps[1], 0x0, 0x0, CLOSURE_REFRACTION);
}
{
trace_diffuse_ps_ = DRW_pass_create("TraceDiffuse", state);
GPUShader *sh = inst_.shaders.static_shader_get(do_rt ? RAYTRACE_DIFFUSE :
RAYTRACE_DIFFUSE_FALLBACK);
grps[2] = DRW_shgroup_create(sh, trace_diffuse_ps_);
DRW_shgroup_uniform_block(grps[2], "raytrace_block", inst_.raytracing.diffuse_ubo_get());
DRW_shgroup_stencil_set(grps[2], 0x0, 0x0, CLOSURE_DIFFUSE);
}
for (DRWShadingGroup *grp : grps) {
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.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, "hiz_tx", &input_hiz_tx_);
DRW_shgroup_uniform_texture_ref(grp, "hiz_front_tx", &input_hiz_front_tx_);
DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_radiance_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(grp, "combined_tx", &input_combined_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_color_tx", &input_cl_color_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_normal_tx", &input_cl_normal_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_data_tx", &input_cl_data_tx_, no_interp);
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
}
{
/* Compute stage. No state needed. */
DRWState state = (DRWState)0;
std::array<DRWShadingGroup *, 3> grps;
{
denoise_reflection_ps_ = DRW_pass_create("DenoiseReflection", state);
GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_DENOISE_REFLECTION);
grps[0] = DRW_shgroup_create(sh, denoise_reflection_ps_);
}
{
denoise_refraction_ps_ = DRW_pass_create("DenoiseRefraction", state);
GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_DENOISE_REFRACTION);
grps[1] = DRW_shgroup_create(sh, denoise_refraction_ps_);
}
{
denoise_diffuse_ps_ = DRW_pass_create("DenoiseDiffuse", state);
GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_DENOISE_DIFFUSE);
grps[2] = DRW_shgroup_create(sh, denoise_diffuse_ps_);
}
for (DRWShadingGroup *grp : grps) {
/* Does not matter which raytrace_block we use. */
DRW_shgroup_uniform_block(grp, "raytrace_block", inst_.raytracing.diffuse_ubo_get());
DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.ubo_get());
DRW_shgroup_uniform_block(grp, "rtbuffer_block", data_);
DRW_shgroup_uniform_texture_ref_ex(grp, "ray_data_tx", &input_ray_data_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(grp, "ray_radiance_tx", &input_ray_color_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(grp, "hiz_tx", &input_hiz_front_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_color_tx", &input_cl_color_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_normal_tx", &input_cl_normal_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_data_tx", &input_cl_data_tx_, no_interp);
DRW_shgroup_uniform_texture_ref(grp, "ray_history_tx", &input_history_tx_);
DRW_shgroup_uniform_texture_ref(grp, "ray_variance_tx", &input_variance_tx_);
DRW_shgroup_uniform_image_ref(grp, "out_history_img", &output_history_tx_);
DRW_shgroup_uniform_image_ref(grp, "out_variance_img", &output_variance_tx_);
DRW_shgroup_call_compute_ref(grp, dispatch_size_);
DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS);
}
}
{
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_ADD_FULL;
std::array<DRWShadingGroup *, 3> grps;
{
resolve_reflection_ps_ = DRW_pass_create("ResolveReflection", state);
GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_RESOLVE_REFLECTION);
grps[0] = DRW_shgroup_create(sh, resolve_reflection_ps_);
DRW_shgroup_stencil_set(grps[0], 0x0, 0x0, CLOSURE_REFLECTION);
}
{
resolve_refraction_ps_ = DRW_pass_create("ResolveRefraction", state);
GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_RESOLVE_REFRACTION);
grps[1] = DRW_shgroup_create(sh, resolve_refraction_ps_);
DRW_shgroup_stencil_set(grps[1], 0x0, 0x0, CLOSURE_REFRACTION);
}
{
resolve_diffuse_ps_ = DRW_pass_create("ResolveDiffuse", state);
GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_RESOLVE_DIFFUSE);
grps[2] = DRW_shgroup_create(sh, resolve_diffuse_ps_);
DRW_shgroup_stencil_set(grps[2], 0x0, 0x0, CLOSURE_DIFFUSE);
}
for (DRWShadingGroup *grp : grps) {
DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.ubo_get());
DRW_shgroup_uniform_texture_ref_ex(grp, "ray_radiance_tx", &output_history_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(grp, "ray_variance_tx", &output_variance_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_color_tx", &input_cl_color_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_normal_tx", &input_cl_normal_tx_, no_interp);
DRW_shgroup_uniform_texture_ref_ex(grp, "cl_data_tx", &input_cl_data_tx_, no_interp);
// DRW_shgroup_call_compute_ref(grp, dispatch_size_);
// DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
}
}
void RaytraceBuffer::trace(eClosureBits closure_type,
GBuffer &gbuffer,
HiZBuffer &hiz,
HiZBuffer &hiz_front)
{
gbuffer.bind_tracing();
input_hiz_tx_ = hiz.texture_get();
input_hiz_front_tx_ = hiz_front.texture_get();
if (closure_type == CLOSURE_REFLECTION) {
input_cl_color_tx_ = gbuffer.reflect_color_tx;
input_cl_normal_tx_ = gbuffer.reflect_normal_tx;
input_cl_data_tx_ = gbuffer.reflect_normal_tx;
}
else {
input_cl_color_tx_ = gbuffer.transmit_color_tx;
input_cl_normal_tx_ = gbuffer.transmit_normal_tx;
input_cl_data_tx_ = gbuffer.transmit_data_tx;
}
switch (closure_type) {
default:
case CLOSURE_REFLECTION:
input_radiance_tx_ = gbuffer.combined_tx;
DRW_draw_pass(trace_reflection_ps_);
break;
case CLOSURE_REFRACTION:
input_radiance_tx_ = gbuffer.combined_tx;
DRW_draw_pass(trace_refraction_ps_);
break;
case CLOSURE_DIFFUSE:
input_radiance_tx_ = gbuffer.diffuse_tx;
input_combined_tx_ = gbuffer.combined_tx;
DRW_draw_pass(trace_diffuse_ps_);
break;
}
input_ray_data_tx_ = gbuffer.ray_data_tx;
input_ray_color_tx_ = gbuffer.ray_radiance_tx;
}
void RaytraceBuffer::denoise(eClosureBits closure_type)
{
switch (closure_type) {
default:
case CLOSURE_REFLECTION:
input_history_tx_ = reflection_radiance_history_get();
input_variance_tx_ = reflection_variance_history_get();
output_history_tx_ = reflection_radiance_get();
output_variance_tx_ = reflection_variance_get();
DRW_draw_pass(denoise_reflection_ps_);
break;
case CLOSURE_REFRACTION:
input_history_tx_ = refraction_radiance_history_get();
input_variance_tx_ = refraction_variance_history_get();
output_history_tx_ = refraction_radiance_get();
output_variance_tx_ = refraction_variance_get();
DRW_draw_pass(denoise_refraction_ps_);
break;
case CLOSURE_DIFFUSE:
input_history_tx_ = diffuse_radiance_history_get();
input_variance_tx_ = diffuse_variance_history_get();
output_history_tx_ = diffuse_radiance_get();
output_variance_tx_ = diffuse_variance_get();
DRW_draw_pass(denoise_diffuse_ps_);
break;
}
}
void RaytraceBuffer::resolve(eClosureBits closure_type, GBuffer &gbuffer)
{
gbuffer.bind_radiance();
switch (closure_type) {
default:
case CLOSURE_REFLECTION:
DRW_draw_pass(resolve_reflection_ps_);
break;
case CLOSURE_REFRACTION:
DRW_draw_pass(resolve_refraction_ps_);
break;
case CLOSURE_DIFFUSE:
DRW_draw_pass(resolve_diffuse_ps_);
break;
}
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,229 @@
/*
* 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 "eevee_gbuffer.hh"
#include "eevee_shader_shared.hh"
namespace blender::eevee {
class Instance;
/* -------------------------------------------------------------------- */
/** \name Raytracing
* \{ */
class RaytracingModule {
private:
Instance &inst_;
RaytraceDataBuf reflection_data_;
RaytraceDataBuf refraction_data_;
RaytraceDataBuf diffuse_data_;
bool enabled_ = false;
public:
RaytracingModule(Instance &inst) : inst_(inst){};
void sync(void);
const GPUUniformBuf *reflection_ubo_get(void) const
{
return reflection_data_;
}
const GPUUniformBuf *refraction_ubo_get(void) const
{
return refraction_data_;
}
const GPUUniformBuf *diffuse_ubo_get(void) const
{
return diffuse_data_;
}
bool enabled(void) const
{
return enabled_;
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Raytracing Buffers
*
* Contain persistent data used for temporal denoising. Similar to \class GBuffer but only contains
* persistent data.
* \{ */
struct RaytraceBuffer {
public:
DRWPass *denoise_diffuse_ps_ = nullptr;
DRWPass *denoise_reflection_ps_ = nullptr;
DRWPass *denoise_refraction_ps_ = nullptr;
DRWPass *resolve_diffuse_ps_ = nullptr;
DRWPass *resolve_reflection_ps_ = nullptr;
DRWPass *resolve_refraction_ps_ = nullptr;
DRWPass *trace_diffuse_ps_ = nullptr;
DRWPass *trace_reflection_ps_ = nullptr;
DRWPass *trace_refraction_ps_ = nullptr;
private:
Instance &inst_;
/* Only allocated if used. */
Texture diffuse_radiance_tx_ = {"DiffuseHistory_A"};
Texture diffuse_radiance_history_tx_ = {"DiffuseHistory_B"};
Texture diffuse_variance_tx_ = {"DiffuseVariance_A"};
Texture diffuse_variance_history_tx_ = {"DiffuseVariance_B"};
Texture reflection_radiance_tx_ = {"ReflectionHistory_A"};
Texture reflection_radiance_history_tx_ = {"ReflectionHistory_B"};
Texture reflection_variance_tx_ = {"ReflectionVariance_A"};
Texture reflection_variance_history_tx_ = {"ReflectionVariance_B"};
Texture refraction_radiance_tx_ = {"RefractionHistory_A"};
Texture refraction_radiance_history_tx_ = {"RefractionHistory_B"};
Texture refraction_variance_tx_ = {"RefractionVariance_A"};
Texture refraction_variance_history_tx_ = {"RefractionVariance_B"};
/* Reference only. */
GPUTexture *input_radiance_tx_;
GPUTexture *input_combined_tx_;
GPUTexture *input_ray_data_tx_;
GPUTexture *input_ray_color_tx_;
GPUTexture *input_hiz_tx_;
GPUTexture *input_hiz_front_tx_;
GPUTexture *input_cl_color_tx_;
GPUTexture *input_cl_normal_tx_;
GPUTexture *input_cl_data_tx_;
GPUTexture *input_history_tx_;
GPUTexture *input_variance_tx_;
GPUTexture *output_history_tx_;
GPUTexture *output_variance_tx_;
RaytraceBufferDataBuf data_;
int2 extent_ = int2(0);
int3 dispatch_size_ = int3(1);
public:
RaytraceBuffer(Instance &inst) : inst_(inst){};
~RaytraceBuffer(){};
void sync(int2 extent);
void trace(eClosureBits closure_type, GBuffer &gbuffer, HiZBuffer &hiz, HiZBuffer &hiz_front);
void denoise(eClosureBits closure_type);
void resolve(eClosureBits closure_type, GBuffer &gbuffer);
GPUTexture *diffuse_radiance_history_get(void)
{
ensure_buffer(diffuse_radiance_history_tx_, data_.valid_history_diffuse, GPU_RGBA16F);
return diffuse_radiance_history_tx_;
}
GPUTexture *reflection_radiance_history_get(void)
{
ensure_buffer(reflection_radiance_history_tx_, data_.valid_history_reflection, GPU_RGBA16F);
return reflection_radiance_history_tx_;
}
GPUTexture *refraction_radiance_history_get(void)
{
ensure_buffer(refraction_radiance_history_tx_, data_.valid_history_refraction, GPU_RGBA16F);
return refraction_radiance_history_tx_;
}
GPUTexture *diffuse_variance_history_get(void)
{
ensure_buffer(diffuse_variance_history_tx_, data_.valid_history_diffuse, GPU_R8);
return diffuse_variance_history_tx_;
}
GPUTexture *reflection_variance_history_get(void)
{
ensure_buffer(reflection_variance_history_tx_, data_.valid_history_reflection, GPU_R8);
return reflection_variance_history_tx_;
}
GPUTexture *refraction_variance_history_get(void)
{
ensure_buffer(refraction_variance_history_tx_, data_.valid_history_refraction, GPU_R8);
return refraction_variance_history_tx_;
}
GPUTexture *diffuse_radiance_get(void)
{
ensure_buffer(diffuse_radiance_tx_, data_.valid_history_diffuse, GPU_RGBA16F);
return diffuse_radiance_tx_;
}
GPUTexture *reflection_radiance_get(void)
{
ensure_buffer(reflection_radiance_tx_, data_.valid_history_reflection, GPU_RGBA16F);
return reflection_radiance_tx_;
}
GPUTexture *refraction_radiance_get(void)
{
ensure_buffer(refraction_radiance_tx_, data_.valid_history_refraction, GPU_RGBA16F);
return refraction_radiance_tx_;
}
GPUTexture *diffuse_variance_get(void)
{
ensure_buffer(diffuse_variance_tx_, data_.valid_history_diffuse, GPU_R8);
return diffuse_variance_tx_;
}
GPUTexture *reflection_variance_get(void)
{
ensure_buffer(reflection_variance_tx_, data_.valid_history_reflection, GPU_R8);
return reflection_variance_tx_;
}
GPUTexture *refraction_variance_get(void)
{
ensure_buffer(refraction_variance_tx_, data_.valid_history_refraction, GPU_R8);
return refraction_variance_tx_;
}
void render_end(const DRWView *view)
{
using draw::Texture;
DRW_view_persmat_get(view, data_.history_persmat.ptr(), false);
Texture::swap(diffuse_radiance_tx_, diffuse_radiance_history_tx_);
Texture::swap(diffuse_variance_tx_, diffuse_variance_history_tx_);
Texture::swap(reflection_radiance_tx_, reflection_radiance_history_tx_);
Texture::swap(reflection_variance_tx_, reflection_variance_history_tx_);
Texture::swap(refraction_radiance_tx_, refraction_radiance_history_tx_);
Texture::swap(refraction_variance_tx_, refraction_variance_history_tx_);
}
private:
void ensure_buffer(Texture &texture, int &valid_history, eGPUTextureFormat format)
{
bool was_allocated = texture.ensure_2d(format, extent_);
if (was_allocated && valid_history) {
valid_history = false;
data_.push_update();
}
else if (!was_allocated && !valid_history) {
valid_history = true;
data_.push_update();
}
}
};
/** \} */
} // namespace blender::eevee

View File

@@ -1,743 +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"
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);
}
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_SURF, 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,518 +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;
}
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;
}
}
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,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 2019, Blender Foundation.
*/
/** \file
* \ingroup EEVEE
*/
#include "eevee_private.h"
#include "BLI_rand.h"
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,358 @@
/*
* 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;
draw::UniformBuffer<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];
data_.dimensions[SAMPLING_RAYTRACE_X][0] = r[0];
}
{
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];
/* TODO decorelate. */
data_.dimensions[SAMPLING_RAYTRACE_U][0] = r[0];
data_.dimensions[SAMPLING_RAYTRACE_V][0] = r[1];
data_.dimensions[SAMPLING_RAYTRACE_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];
/* TODO decorelate. */
data_.dimensions[SAMPLING_SSS_U][0] = r[0];
data_.dimensions[SAMPLING_SSS_V][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_;
}
/* 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.
*/
float3 sample_ball(const float rand[3])
{
float3 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;
}
float2 sample_disk(const float rand[2])
{
float omega = rand[1] * 2.0f * M_PI;
return sqrtf(rand[0]) * float2(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 ray-tracing 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,930 @@
/*
* 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_debug_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_intersection_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_select_comp_glsl[];
extern char datatoc_eevee_culling_sort_comp_glsl[];
extern char datatoc_eevee_culling_tile_comp_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_depth_frag_glsl[];
extern char datatoc_eevee_film_resolve_frag_glsl[];
extern char datatoc_eevee_gbuffer_lib_glsl[];
extern char datatoc_eevee_hiz_copy_frag_glsl[];
extern char datatoc_eevee_hiz_downsample_frag_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_raytrace_denoise_comp_glsl[];
extern char datatoc_eevee_raytrace_raygen_frag_glsl[];
extern char datatoc_eevee_raytrace_raygen_lib_glsl[];
extern char datatoc_eevee_raytrace_resolve_frag_glsl[];
// extern char datatoc_eevee_raytrace_resolve_lib_glsl[];
extern char datatoc_eevee_raytrace_trace_lib_glsl[];
extern char datatoc_eevee_sampling_lib_glsl[];
extern char datatoc_eevee_shadow_debug_frag_glsl[];
extern char datatoc_eevee_shadow_lib_glsl[];
extern char datatoc_eevee_shadow_page_alloc_comp_glsl[];
extern char datatoc_eevee_shadow_page_copy_comp_glsl[];
extern char datatoc_eevee_shadow_page_debug_comp_glsl[];
extern char datatoc_eevee_shadow_page_defrag_comp_glsl[];
extern char datatoc_eevee_shadow_page_free_comp_glsl[];
extern char datatoc_eevee_shadow_page_init_comp_glsl[];
extern char datatoc_eevee_shadow_page_lib_glsl[];
extern char datatoc_eevee_shadow_page_mark_vert_glsl[];
extern char datatoc_eevee_shadow_tilemap_depth_scan_comp_glsl[];
extern char datatoc_eevee_shadow_tilemap_lod_mask_comp_glsl[];
extern char datatoc_eevee_shadow_tilemap_lib_glsl[];
extern char datatoc_eevee_shadow_tilemap_setup_comp_glsl[];
extern char datatoc_eevee_shadow_tilemap_tag_comp_glsl[];
extern char datatoc_eevee_shadow_tilemap_visibility_comp_glsl[];
extern char datatoc_eevee_subsurface_eval_frag_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_debug_lib);
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_intersection_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_page_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_shadow_tilemap_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_raytrace_trace_lib);
DRW_SHADER_LIB_ADD(shader_lib_, eevee_raytrace_raygen_lib);
// DRW_SHADER_LIB_ADD(shader_lib_, eevee_raytrace_resolve_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_COMPUTE(enum_, comp_, defs_) \
shader_descriptions_[enum_].name = STRINGIFY(enum_); \
shader_descriptions_[enum_].compute_shader_code = datatoc_##comp_##_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_COMPUTE(CULLING_SELECT, eevee_culling_select_comp, nullptr);
SHADER_COMPUTE(CULLING_SORT, eevee_culling_sort_comp, nullptr);
SHADER_COMPUTE(CULLING_TILE, eevee_culling_tile_comp, nullptr);
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(HIZ_COPY, eevee_hiz_copy_frag);
SHADER_FULLSCREEN(HIZ_DOWNSAMPLE, eevee_hiz_downsample_frag);
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_DEFINES(RAYTRACE_DIFFUSE, eevee_raytrace_raygen_frag, "#define DIFFUSE\n");
SHADER_FULLSCREEN_DEFINES(RAYTRACE_DIFFUSE_FALLBACK,
eevee_raytrace_raygen_frag,
"#define DIFFUSE\n"
"#define SKIP_TRACE\n");
SHADER_FULLSCREEN_DEFINES(
RAYTRACE_REFLECTION, eevee_raytrace_raygen_frag, "#define REFLECTION\n");
SHADER_FULLSCREEN_DEFINES(RAYTRACE_REFLECTION_FALLBACK,
eevee_raytrace_raygen_frag,
"#define REFLECTION\n"
"#define SKIP_TRACE\n");
SHADER_FULLSCREEN_DEFINES(
RAYTRACE_REFRACTION, eevee_raytrace_raygen_frag, "#define REFRACTION\n");
SHADER_FULLSCREEN_DEFINES(RAYTRACE_REFRACTION_FALLBACK,
eevee_raytrace_raygen_frag,
"#define REFRACTION\n"
"#define SKIP_TRACE\n");
SHADER_COMPUTE(RAYTRACE_DENOISE_DIFFUSE, eevee_raytrace_denoise_comp, "#define DIFFUSE\n");
SHADER_COMPUTE(RAYTRACE_DENOISE_REFLECTION, eevee_raytrace_denoise_comp, "#define REFLECTION\n");
SHADER_COMPUTE(RAYTRACE_DENOISE_REFRACTION, eevee_raytrace_denoise_comp, "#define REFRACTION\n");
SHADER_FULLSCREEN_DEFINES(
RAYTRACE_RESOLVE_DIFFUSE, eevee_raytrace_resolve_frag, "#define DIFFUSE\n");
SHADER_FULLSCREEN_DEFINES(
RAYTRACE_RESOLVE_REFLECTION, eevee_raytrace_resolve_frag, "#define REFLECTION\n");
SHADER_FULLSCREEN_DEFINES(
RAYTRACE_RESOLVE_REFRACTION, eevee_raytrace_resolve_frag, "#define REFRACTION\n");
SHADER_FULLSCREEN(SHADOW_DEBUG, eevee_shadow_debug_frag);
SHADER_COMPUTE(SHADOW_PAGE_ALLOC, eevee_shadow_page_alloc_comp, nullptr);
SHADER_COMPUTE(SHADOW_PAGE_COPY, eevee_shadow_page_copy_comp, nullptr);
SHADER_COMPUTE(SHADOW_PAGE_DEBUG, eevee_shadow_page_debug_comp, nullptr);
SHADER_COMPUTE(SHADOW_PAGE_DEFRAG, eevee_shadow_page_defrag_comp, nullptr);
SHADER_COMPUTE(SHADOW_PAGE_FREE, eevee_shadow_page_free_comp, nullptr);
SHADER_COMPUTE(SHADOW_PAGE_INIT, eevee_shadow_page_init_comp, nullptr);
SHADER(SHADOW_PAGE_MARK, eevee_shadow_page_mark_vert, nullptr, eevee_depth_clear_frag, nullptr);
SHADER_COMPUTE(SHADOW_TILE_DEPTH_SCAN, eevee_shadow_tilemap_depth_scan_comp, nullptr);
SHADER_COMPUTE(SHADOW_TILE_LOD_MASK, eevee_shadow_tilemap_lod_mask_comp, nullptr);
SHADER_COMPUTE(SHADOW_TILE_SETUP, eevee_shadow_tilemap_setup_comp, nullptr);
SHADER_COMPUTE(SHADOW_TILE_TAG_UPDATE, eevee_shadow_tilemap_tag_comp, "#define TAG_UPDATE\n");
SHADER_COMPUTE(SHADOW_TILE_TAG_USAGE, eevee_shadow_tilemap_tag_comp, "#define TAG_USAGE\n");
SHADER_COMPUTE(SHADOW_TILE_TAG_VISIBILITY, eevee_shadow_tilemap_visibility_comp, nullptr);
SHADER_FULLSCREEN(SUBSURFACE_EVAL, eevee_subsurface_eval_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
#undef SHADER_FULLSCREEN_DEFINES
#undef SHADER_COMPUTE
#ifdef DEBUG
/* Ensure all shader are described. */
for (ShaderDescription &desc : shader_descriptions_) {
BLI_assert_msg(desc.name != nullptr, "EEVEE: Mising shader definition.");
if (desc.compute_shader_code == 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];
if (desc.compute_shader_code != nullptr) {
char *comp_with_lib = DRW_shader_library_create_shader_string(shader_lib_,
desc.compute_shader_code);
shaders_[shader_type] = GPU_shader_create_compute(
comp_with_lib, nullptr, desc.defines_shader_code, desc.name);
MEM_SAFE_FREE(comp_with_lib);
}
else {
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 float2 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:
* float2 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 += "float3 nodetree_displacement(void)\n";
output += "{\n";
if (codegen->displacement) {
output += codegen->displacement;
}
else {
output += "return float3(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:
* float2 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";
output += "float nodetree_thickness(void)\n";
output += "{\n";
if (codegen->thickness) {
output += codegen->thickness;
}
else {
/* TODO(fclem): Better default. */
output += "return 0.1;\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,191 @@
/*
* 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_SELECT,
CULLING_SORT,
CULLING_TILE,
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,
HIZ_COPY,
HIZ_DOWNSAMPLE,
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,
RAYTRACE_DIFFUSE,
RAYTRACE_DIFFUSE_FALLBACK,
RAYTRACE_REFLECTION,
RAYTRACE_REFLECTION_FALLBACK,
RAYTRACE_REFRACTION,
RAYTRACE_REFRACTION_FALLBACK,
RAYTRACE_DENOISE_DIFFUSE,
RAYTRACE_DENOISE_REFLECTION,
RAYTRACE_DENOISE_REFRACTION,
RAYTRACE_RESOLVE_DIFFUSE,
RAYTRACE_RESOLVE_REFLECTION,
RAYTRACE_RESOLVE_REFRACTION,
SHADOW_DEBUG,
SHADOW_PAGE_ALLOC,
SHADOW_PAGE_COPY,
SHADOW_PAGE_DEBUG,
SHADOW_PAGE_DEFRAG,
SHADOW_PAGE_FREE,
SHADOW_PAGE_INIT,
SHADOW_PAGE_MARK,
SHADOW_TILE_DEPTH_SCAN,
SHADOW_TILE_LOD_MASK,
SHADOW_TILE_SETUP,
SHADOW_TILE_TAG_UPDATE,
SHADOW_TILE_TAG_USAGE,
SHADOW_TILE_TAG_VISIBILITY,
SUBSURFACE_EVAL,
VELOCITY_CAMERA,
VELOCITY_MESH,
MAX_SHADER_TYPE,
};
/**
* Shader module. shared between instances.
*/
class ShaderModule {
private:
struct ShaderDescription {
const char *name = nullptr;
const char *vertex_shader_code = nullptr;
const char *geometry_shader_code = nullptr;
const char *fragment_shader_code = nullptr;
const char *compute_shader_code = nullptr;
const char *defines_shader_code = nullptr;
};
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,883 @@
/**
* 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.
*/
#ifndef USE_GPU_SHADER_CREATE_INFO
# pragma once
# include "eevee_wrapper.hh"
# include "GPU_shader_shared.h"
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,
SAMPLING_SSS_U = 13u,
SAMPLING_SSS_V = 14u,
SAMPLING_RAYTRACE_U = 15u,
SAMPLING_RAYTRACE_V = 16u,
SAMPLING_RAYTRACE_W = 17u,
SAMPLING_RAYTRACE_X = 18u
};
/** IMPORTANT: Make sure the array can contain all sampling dimensions. */
#define SAMPLING_DIMENSION_COUNT 19
struct SamplingData {
/** Array containing random values from Low Discrepency Sequence in [0..1) range. */
/** HACK: float arrays are padded to float4 in GLSL. Using float4 for now to get the same
* alignment but this is wasteful. */
float4 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! */
float4x4 persmat;
float4x4 persinv;
float4x4 viewmat;
float4x4 viewinv;
float4x4 winmat;
float4x4 wininv;
/** Camera UV scale and bias. Also known as viewcamtexcofac. */
float2 uv_scale;
float2 uv_bias;
/** Panorama parameters. */
float2 equirect_scale;
float2 equirect_scale_inv;
float2 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 eDebugMode : uint32_t {
/* TODO(fclem) Rename shadow cases. */
SHADOW_DEBUG_NONE = 0u,
/**
* Gradient showing light evaluation hotspots.
*/
DEBUG_LIGHT_CULLING = 4u,
/**
* Tilemaps to screen. Is also present in other modes.
* - Black pixels, no pages allocated.
* - Green pixels, pages cached.
* - Red pixels, pages allocated.
*/
SHADOW_DEBUG_TILEMAPS = 5u,
/**
* Random color per pages. Validates page density allocation and sampling.
*/
SHADOW_DEBUG_PAGES = 6u,
/**
* Outputs random color per tilemap (or tilemap level). Validates tilemaps coverage.
* Black means not covered by any tilemaps LOD of the shadow.
*/
SHADOW_DEBUG_LOD = 7u,
/**
* Outputs white pixels for pages allocated and black pixels for unused pages.
* This needs SHADOW_DEBUG_PAGE_ALLOCATION_ENABLED defined in order to work.
*/
SHADOW_DEBUG_PAGE_ALLOCATION = 8u,
/**
* Outputs the tilemap atlas. Default tilemap is too big for the usual screen resolution.
* Try lowering SHADOW_TILEMAP_PER_ROW and SHADOW_MAX_TILEMAP before using this option.
*/
SHADOW_DEBUG_TILE_ALLOCATION = 9u,
/**
* Visualize linear depth stored in the atlas regions of the active light.
* This way, one can check if the rendering, the copying and the shadow sampling functions works.
*/
SHADOW_DEBUG_SHADOW_DEPTH = 10u
};
/** \} */
/* -------------------------------------------------------------------- */
/** \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. */
int2 extent;
/** Offset of the render target in the full-res frame, in pixels. */
int2 offset;
/** Scale and bias to filter only a region of the render (aka. render_border). */
float2 uv_bias;
float2 uv_scale;
float2 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. */
bool1 use_history;
/** Used for fade-in effect. */
float opacity;
/** Padding to sizeof(float4). */
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. */
int2 extent;
/** Size of a pixel in uv space (1.0 / extent). */
float2 texel_size;
/** Bokeh Scale factor. */
float2 bokeh_anisotropic_scale;
float2 bokeh_anisotropic_scale_inv;
/* Correction factor to align main target pixels with the filtered mipmap chain texture. */
float2 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 {
float4x4 next_object_mat;
float4x4 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. */
float2 target_size_inv;
/** Viewport motion blur only blurs using previous frame vectors. */
bool1 is_viewport;
int _pad0, _pad1, _pad2;
};
BLI_STATIC_ASSERT_ALIGN(MotionBlurData, 16)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Cullings
* \{ */
/* TODO(fclem) Rename this. Only used by probes now. */
#define CULLING_ITEM_BATCH 128
/* Number of items we can cull. Limited by how we store CullingZBin. */
#define CULLING_MAX_ITEM 65536
/* Number of items in a culling batch. Needs to be Power of 2. Must be <= to 65536. */
/* Current limiting factor is the sorting phase which is single pass and only sort within a
* threadgroup which maximum size is 1024. */
#define CULLING_BATCH_SIZE 1024
/* Maximum number of 32 bit uint stored per tile. */
#define CULLING_MAX_WORD (CULLING_BATCH_SIZE / 32)
/* Fine grained subdivision in the Z direction (Must be multiple of CULLING_BATCH_SIZE). */
#define CULLING_ZBIN_COUNT 4096
struct CullingData {
/** Scale applied to tile pixel coordinates to get target UV coordinate. */
float2 tile_to_uv_fac;
/** Scale and bias applied to linear Z to get zbin. */
float zbin_scale;
float zbin_bias;
/** Valid item count in the source data array. */
uint items_count;
/** Items to skip that are not processed by the 2.5D culling. */
uint items_no_cull_count;
/** Number of items that passes the first culling test. */
uint visible_count;
/** Will disable specular during light data copy.. */
bool1 enable_specular;
/** Extent of one square tile in pixels. */
uint tile_size;
/** Number of tiles on the X/Y axis. */
uint tile_x_len;
uint tile_y_len;
/** Number of word per tile. Depends on the maximum number of lights. */
uint tile_word_len;
};
BLI_STATIC_ASSERT_ALIGN(CullingData, 16)
#define CullingZBin uint
#define CullingWord uint
static inline int culling_z_to_zbin(CullingData data, float z)
{
return int(z * data.zbin_scale + data.zbin_bias);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Shadows
* \{ */
/**
* Shadow data for either a directional shadow or a punctual shadow.
*
* A punctual 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.
*
* A directional light shadow is composed of multiple clipmaps with each level
* covering twice as much area as the previous one.
*/
struct ShadowData {
/**
* Point : Unused.
* Directional : Rotation matrix to local light coordinate.
* The scale is uniform for the Z axis.
* For the X & Y axes, it is scaled to be the size of a tile.
* Origin is the one of the largest clipmap.
* So after transformation, you are in the tilemap space [0..SHADOW_TILEMAP_RES]
* of the largest clipmap.
*/
float4x4 mat;
/** NOTE: It is ok to use float3 here. A float is declared right after it.
* float3 is also aligned to 16 bytes. */
/** Shadow offset caused by jittering projection origin (for soft shadows). */
float3 offset;
/** Shadow bias in world space. */
float bias;
/** Near and far clipping distance to convert shadowmap to world space distances. */
float clip_near;
float clip_far;
/** Index of the first tilemap. */
int tilemap_index;
/** Index of the last tilemap. */
int tilemap_last;
/** Directional : Clipmap lod range to avoid sampling outside of valid range. */
int clipmap_lod_min, clipmap_lod_max;
/** Directional : Offset of the lod min in base units. */
int2 base_offset;
};
BLI_STATIC_ASSERT_ALIGN(ShadowData, 16)
/**
* IMPORTANT: Some data packing are tweaked for these values.
* Be sure to update them accordingly.
* SHADOW_TILEMAP_RES max is 32 because of the shared bitmaps used for LOD tagging.
* It is also limited by the maximum thread group size (1024).
*/
#define SHADOW_TILEMAP_RES 16
#define SHADOW_TILEMAP_LOD 4 /* LOG2(SHADOW_TILEMAP_RES) */
#define SHADOW_TILEMAP_PER_ROW 64
#define SHADOW_PAGE_COPY_GROUP_SIZE 32
#define SHADOW_DEPTH_SCAN_GROUP_SIZE 32
#define SHADOW_AABB_TAG_GROUP_SIZE 64
#define SHADOW_MAX_TILEMAP 4096
#define SHADOW_MAX_PAGE 4096
#define SHADOW_PAGE_PER_ROW 64
#define SHADOW_DEBUG_PAGE_ALLOCATION_ENABLED
#define SHADOW_DEBUG_TILE_ALLOCATION_ENABLED
/** Debug shadow tile allocation. */
// #define SHADOW_DEBUG_NO_CACHING
/* Debug: Comment to only use BBox tagging instead of depth scanning. */
// #define SHADOW_DEBUG_NO_DEPTH_SCAN
/* Debug: Will freeze the camera used for shadow tagging if G.debug_value is >= 4. */
// #define SHADOW_DEBUG_FREEZE_CAMERA
#if defined(SHADOW_DEBUG_FREEZE_CAMERA) && defined(SHADOW_DEBUG_NO_DEPTH_SCAN)
# error Freeze camera debug option is incompatible with depth scanning.
#endif
/* Given an input tile coordinate [0..SHADOW_TILEMAP_RES] returns the coordinate in NDC [-1..1]. */
static inline float2 shadow_tile_coord_to_ndc(int2 tile)
{
float2 co = float2(tile.x, tile.y) / float(SHADOW_TILEMAP_RES);
return co * 2.0f - 1.0f;
}
/**
* Small descriptor used for the tile update phase.
*/
struct ShadowTileMapData {
/** View Projection matrix used to tag tiles (World > UV Tile [0..SHADOW_TILEMAP_RES]). */
float4x4 tilemat;
/** Corners of the frustum. */
float4 corners[4];
/** NDC depths to clip usage bbox. */
#define _max_usage_depth corners[0].w
#define _min_usage_depth corners[1].w
#define _punctual_distance corners[2].w
/** Shift to apply to the tile grid in the setup phase. */
int2 grid_shift;
/** True for punctual lights. */
bool1 is_cubeface;
/** Index inside the tilemap allocator. */
int index;
/** Cone direction for punctual shadows. */
float3 cone_direction;
/** Cosine of the max angle. Offset to take into acount the max tile angle. */
float cone_angle_cos;
};
BLI_STATIC_ASSERT_ALIGN(ShadowTileMapData, 16)
struct ShadowPagesInfoData {
/** Index of the next free pages in the free page heap. */
int page_free_next;
/** Last number of free pages at the begining of the redraw. */
int page_free_next_prev;
/** Number of pages that needs to be rendered. */
int page_updated_count;
int _pad0;
};
BLI_STATIC_ASSERT_ALIGN(ShadowPagesInfoData, 16)
/** \} */
/* -------------------------------------------------------------------- */
/** \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. */
float4x4 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;
/** Index of the shadow struct on CPU. -1 means no shadow. */
int shadow_id;
/** NOTE: It is ok to use float3 here. A float is declared right after it.
* float3 is also aligned to 16 bytes. */
float3 color;
/** Power depending on shader type. */
float diffuse_power;
float specular_power;
float volume_power;
float transmit_power;
/** Special radius factor for point lighting. */
float radius_squared;
/** Light Type. */
eLightType type;
/** Padding to sizeof(float2). */
float _pad1;
/** Spot size. Aligned to size of float2. */
float2 spot_size_inv;
/** Associated shadow data. Only valid if shadow_id is not LIGHT_NO_SHADOW. */
ShadowData shadow_data;
};
BLI_STATIC_ASSERT_ALIGN(LightData, 16)
/**
* Shadow data for debugging the active light shadow.
*/
struct ShadowDebugData {
LightData light;
ShadowData shadow;
float3 camera_position;
eDebugMode type;
int tilemap_data_index;
int _pad1;
int _pad2;
int _pad3;
};
BLI_STATIC_ASSERT_ALIGN(ShadowDebugData, 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 {
float4x4 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). */
float4x4 local_mat;
/** Resolution of the light grid. */
int3 resolution;
/** Offset of the first cell of this grid in the grid texture. */
int offset;
/** World space vector between 2 adjacent cells. */
float3 increment_x;
/** Attenuation Bias. */
float attenuation_bias;
/** World space vector between 2 adjacent cells. */
float3 increment_y;
/** Attenuation scaling. */
float attenuation_scale;
/** World space vector between 2 adjacent cells. */
float3 increment_z;
/** Number of grid levels not ready for display during baking. */
int level_skip;
/** World space corner position. */
float3 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 int3 grid_cell_index_to_coordinate(int cell_id, int3 resolution)
{
int3 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 {
float4x4 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). */
float4x4 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). */
float4x4 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 Hierarchical-Z Buffer
* \{ */
struct HiZData {
/** Scale factor to remove HiZBuffer padding. */
float2 uv_scale;
/** Scale factor to convert from pixel space to Normalized Device Coordinates [-1..1]. */
float2 pixel_to_ndc;
};
BLI_STATIC_ASSERT_ALIGN(HiZData, 16)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Raytracing
* \{ */
struct RaytraceData {
/** View space thickness the objects. */
float thickness;
/** Determine how fast the sample steps are getting bigger. */
float quality;
/** Importance sample bias. Lower values will make the render less noisy. */
float bias;
/** Maximum brightness during lighting evaluation. */
float brightness_clamp;
/** Maximum roughness for which we will trace a ray. */
float max_roughness;
/** Resolve sample pool offset, based on scene current sample. */
int pool_offset;
int _pad0;
int _pad1;
};
BLI_STATIC_ASSERT_ALIGN(RaytraceData, 16)
struct RaytraceBufferData {
/** ViewProjection matrix used to render the previous frame. */
float4x4 history_persmat;
/** False if the history buffer was just allocated and contains uninitialized data. */
bool1 valid_history_diffuse;
bool1 valid_history_reflection;
bool1 valid_history_refraction;
int _pad0;
};
BLI_STATIC_ASSERT_ALIGN(RaytraceData, 16)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Subsurface
* \{ */
#define SSS_SAMPLE_MAX 64
#define SSS_BURLEY_TRUNCATE 16.0
#define SSS_BURLEY_TRUNCATE_CDF 0.9963790093708328
#define SSS_TRANSMIT_LUT_SIZE 64.0
#define SSS_TRANSMIT_LUT_RADIUS 1.218
#define SSS_TRANSMIT_LUT_SCALE ((SSS_TRANSMIT_LUT_SIZE - 1.0) / float(SSS_TRANSMIT_LUT_SIZE))
#define SSS_TRANSMIT_LUT_BIAS (0.5 / float(SSS_TRANSMIT_LUT_SIZE))
#define SSS_TRANSMIT_LUT_STEP_RES 64.0
struct SubsurfaceData {
/** xy: 2D sample position [-1..1], zw: sample_bounds. */
/* NOTE(fclem) Using float4 for alignment. */
float4 samples[SSS_SAMPLE_MAX];
/** Sample index after which samples are not randomly rotated anymore. */
int jitter_threshold;
/** Number of samples precomputed in the set. */
int sample_len;
int _pad0;
int _pad1;
};
BLI_STATIC_ASSERT_ALIGN(SubsurfaceData, 16)
/** \} */
/* -------------------------------------------------------------------- */
/** \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. */
float4 utility_tx_fetch(float2 texel, float layer);
float4 utility_tx_sample(float2 uv, float layer);
/* Fetch texel. Wrapping if above range. */
# define utility_tx_fetch_define(utility_tx_) \
float4 utility_tx_fetch(float2 texel, float layer) \
{ \
return texelFetch(utility_tx_, int3(int2(texel) % UTIL_TEX_SIZE, layer), 0); \
}
/* Sample at uv position. Filtered & Wrapping enabled. */
# define utility_tx_sample_define(utility_tx_) \
float4 utility_tx_sample(float2 uv, float layer) \
{ \
return textureLod(utility_tx_, float3(uv, layer), 0.0); \
}
/* Stubs declarations if not using it. */
# define utility_tx_fetch_define_stub(utility_tx_) \
float4 utility_tx_fetch(float2 texel, float layer) \
{ \
return float4(0); \
}
# define utility_tx_sample_define_stub(utility_tx_) \
float4 utility_tx_sample(float2 uv, float layer) \
{ \
return float4(0); \
}
#endif
/** \} */
#ifdef __cplusplus
using CameraDataBuf = draw::UniformBuffer<CameraData>;
using CubemapDataBuf = draw::UniformArrayBuffer<CubemapData, CULLING_ITEM_BATCH>;
using CullingDataBuf = draw::StorageBuffer<CullingData>;
using CullingKeyBuf = draw::StorageArrayBuffer<uint, CULLING_BATCH_SIZE, true>;
using CullingLightBuf = draw::StorageArrayBuffer<LightData, CULLING_BATCH_SIZE, true>;
using CullingTileBuf = draw::StorageArrayBuffer<uint, 16 * 16 * CULLING_MAX_WORD, true>;
using CullingZbinBuf = draw::StorageArrayBuffer<uint, CULLING_ZBIN_COUNT, true>;
using DepthOfFieldDataBuf = draw::UniformBuffer<DepthOfFieldData>;
using GridDataBuf = draw::UniformArrayBuffer<GridData, GRID_MAX>;
using HiZDataBuf = draw::UniformBuffer<HiZData>;
using LightDataBuf = draw::StorageArrayBuffer<LightData, CULLING_BATCH_SIZE>;
using LightProbeFilterDataBuf = draw::UniformBuffer<LightProbeFilterData>;
using LightProbeInfoDataBuf = draw::UniformBuffer<LightProbeInfoData>;
using RaytraceBufferDataBuf = draw::UniformBuffer<RaytraceBufferData>;
using RaytraceDataBuf = draw::UniformBuffer<RaytraceData>;
using ShadowDataBuf = draw::StorageArrayBuffer<ShadowData, CULLING_BATCH_SIZE>;
using ShadowDebugDataBuf = draw::UniformBuffer<ShadowDebugData>;
using ShadowPagesInfoDataBuf = draw::StorageBuffer<ShadowPagesInfoData, true>;
using ShadowPageHeapBuf = draw::StorageArrayBuffer<uint, SHADOW_MAX_PAGE, true>;
using ShadowTileMapDataBuf = draw::StorageArrayBuffer<ShadowTileMapData, SHADOW_MAX_TILEMAP>;
using SubsurfaceDataBuf = draw::UniformBuffer<SubsurfaceData>;
using VelocityObjectBuf = draw::UniformBuffer<VelocityObjectData>;
} // namespace blender::eevee
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,599 @@
/*
* 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. */
float4x4 camera_mat;
camera_mat.identity();
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.ptr());
}
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;
eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
lights.shgroup_resources(grp);
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(grp, "utility_tx", inst_.shading_passes.utility_tx);
/* TODO(fclem): Make this only needed if material uses it ... somehow. */
if (true) {
DRW_shgroup_uniform_texture_ref(
grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get());
}
if (true) {
DRW_shgroup_uniform_block(grp, "rt_diffuse_block", inst_.raytracing.diffuse_ubo_get());
DRW_shgroup_uniform_block(grp, "rt_reflection_block", inst_.raytracing.reflection_ubo_get());
DRW_shgroup_uniform_block(grp, "rt_refraction_block", inst_.raytracing.refraction_ubo_get());
DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_radiance_tx_, no_interp);
}
if (true) {
DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.ubo_get());
DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_tx_);
}
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;
eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, transparent_ps_);
lights.shgroup_resources(grp);
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(grp, "utility_tx", inst_.shading_passes.utility_tx);
/* TODO(fclem): Make this only needed if material uses it ... somehow. */
if (true) {
DRW_shgroup_uniform_texture_ref(
grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get());
}
if (true) {
DRW_shgroup_uniform_block(grp, "rt_diffuse_block", inst_.raytracing.diffuse_ubo_get());
DRW_shgroup_uniform_block(grp, "rt_reflection_block", inst_.raytracing.reflection_ubo_get());
DRW_shgroup_uniform_block(grp, "rt_refraction_block", inst_.raytracing.refraction_ubo_get());
DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_radiance_tx_, no_interp);
}
if (true) {
DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.ubo_get());
DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_tx_);
}
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(const DRWView *view,
GBuffer &gbuffer,
HiZBuffer &hiz,
GPUFrameBuffer *view_fb)
{
if (inst_.raytracing.enabled()) {
int2 extent = {GPU_texture_width(gbuffer.depth_tx), GPU_texture_height(gbuffer.depth_tx)};
/* Reuse texture. */
gbuffer.ray_radiance_tx.acquire(extent, GPU_RGBA16F, gbuffer.owner);
/* Copy combined buffer so we can sample from it. */
GPU_texture_copy(gbuffer.ray_radiance_tx, gbuffer.combined_tx);
input_radiance_tx_ = gbuffer.ray_radiance_tx;
hiz.prepare(gbuffer.depth_tx);
/* TODO(fclem): Avoid this if possible. */
hiz.update(gbuffer.depth_tx);
input_hiz_tx_ = hiz.texture_get();
GPU_framebuffer_bind(view_fb);
}
DRW_stats_group_start("ForwardOpaque");
DRW_draw_pass(prepass_ps_);
inst_.shadows.set_view(view, gbuffer.depth_tx);
DRW_draw_pass(opaque_ps_);
DRW_stats_group_end();
DRW_stats_group_start("ForwardTransparent");
/* 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_);
DRW_stats_group_end();
if (inst_.raytracing.enabled()) {
gbuffer.ray_radiance_tx.release();
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \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)
{
/* TODO/OPTI(fclem) Set the right mask for each effect based on gpumat flags. */
uint stencil_mask = CLOSURE_DIFFUSE | CLOSURE_SSS | CLOSURE_REFLECTION | CLOSURE_TRANSPARENCY |
CLOSURE_EMISSION | CLOSURE_REFRACTION;
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_);
lights.shgroup_resources(grp);
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(const DRWView *view,
GBuffer &gbuffer,
HiZBuffer &hiz_front,
HiZBuffer &hiz_back,
RaytraceBuffer &rt_buffer,
GPUFrameBuffer *view_fb)
{
DeferredPass &deferred_pass = inst_.shading_passes.deferred;
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;
}
/* TODO(fclem): detect these cases. */
const bool use_diffuse = true;
const bool use_subsurface = true;
const bool use_transparency = true;
const bool use_holdout = true;
const bool use_refraction = true;
const bool use_glossy = true;
const bool use_ao = false;
gbuffer.prepare((eClosureBits)0xFFFFFFFFu);
if (use_ao || use_glossy || use_diffuse) {
hiz_front.prepare(gbuffer.depth_tx);
}
if (use_refraction) {
hiz_back.prepare(gbuffer.depth_tx);
}
update_pass_inputs(gbuffer, hiz_front, hiz_back);
if (use_refraction) {
/* TODO(fclem) Only update if needed.
* i.e: No need when SSR from previous layer has already updated hiz. */
hiz_back.update(gbuffer.depth_tx);
}
gbuffer.bind();
if (!no_surfaces) {
DRW_draw_pass(prepass_ps_);
/* TODO(fclem): Ambient Occlusion texture node. */
if (use_ao) {
hiz_front.update(gbuffer.depth_tx);
gbuffer.bind();
}
DRW_draw_pass(gbuffer_ps_);
}
inst_.shadows.set_view(view, gbuffer.depth_tx);
if (!no_volumes) {
// gbuffer.copy_depth_behind();
// deferred_pass.input_depth_behind_tx_ = gbuffer.depth_behind_tx;
gbuffer.bind_volume();
DRW_draw_pass(volume_ps_);
}
if (use_holdout) {
gbuffer.bind_holdout();
DRW_draw_pass(deferred_pass.eval_holdout_ps_);
}
/* TODO(fclem) We could bypass update if ao already updated it and if there is no volume. */
hiz_front.update(gbuffer.depth_tx);
if (!no_surfaces && use_refraction) {
/* Launch and shade refraction rays before transparency changes the combined pass. */
rt_buffer.trace(CLOSURE_REFRACTION, gbuffer, hiz_back, hiz_front);
}
GPU_framebuffer_bind(view_fb);
if (use_transparency) {
DRW_draw_pass(deferred_pass.eval_transparency_ps_);
}
gbuffer.clear_radiance();
if (!no_surfaces && use_refraction) {
rt_buffer.denoise(CLOSURE_REFRACTION);
rt_buffer.resolve(CLOSURE_REFRACTION, gbuffer);
}
if (!no_volumes) {
/* TODO(fclem) volume fb. */
GPU_framebuffer_bind(view_fb);
DRW_draw_pass(deferred_pass.eval_volume_homogeneous_ps_);
}
if (!no_surfaces) {
gbuffer.bind_radiance();
DRW_draw_pass(deferred_pass.eval_direct_ps_);
if (use_diffuse) {
rt_buffer.trace(CLOSURE_DIFFUSE, gbuffer, hiz_front, hiz_front);
rt_buffer.denoise(CLOSURE_DIFFUSE);
rt_buffer.resolve(CLOSURE_DIFFUSE, gbuffer);
}
if (use_subsurface) {
GPU_framebuffer_bind(view_fb);
DRW_draw_pass(deferred_pass.eval_subsurface_ps_);
}
if (use_glossy) {
rt_buffer.trace(CLOSURE_REFLECTION, gbuffer, hiz_front, hiz_front);
rt_buffer.denoise(CLOSURE_REFLECTION);
rt_buffer.resolve(CLOSURE_REFLECTION, gbuffer);
}
}
}
void DeferredLayer::update_pass_inputs(GBuffer &gbuffer, HiZBuffer &hiz_front, HiZBuffer &hiz_back)
{
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_diffuse_tx_ = gbuffer.diffuse_tx;
deferred_pass.input_transparency_data_tx_ = gbuffer.transparency_tx;
deferred_pass.input_volume_data_tx_ = gbuffer.volume_tx;
deferred_pass.input_hiz_front_tx_ = hiz_front.texture_get();
deferred_pass.input_hiz_back_tx_ = hiz_back.texture_get();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \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_direct_ps_ = DRW_pass_create("DeferredDirect", state);
GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_EVAL_DIRECT);
DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_direct_ps_);
lights.shgroup_resources(grp);
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(grp, "utility_tx", inst_.shading_passes.utility_tx);
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, "hiz_tx", &input_hiz_front_tx_);
DRW_shgroup_uniform_texture_ref(
grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get());
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_EQUAL | DRW_STATE_BLEND_ADD_FULL;
eval_subsurface_ps_ = DRW_pass_create("DeferredSubsurface", state);
GPUShader *sh = inst_.shaders.static_shader_get(SUBSURFACE_EVAL);
DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_subsurface_ps_);
DRW_shgroup_uniform_block(grp, "subsurface_block", inst_.subsurface.ubo_get());
DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.ubo_get());
DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_front_tx_);
DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_diffuse_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_stencil_set(grp, 0x0, 0xFF, CLOSURE_SSS);
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_);
lights.shgroup_resources(grp);
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
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, "hiz_tx", &input_hiz_front_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_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(const DRWView *drw_view,
GBuffer &gbuffer,
HiZBuffer &hiz_front,
HiZBuffer &hiz_back,
RaytraceBuffer &rt_buffer_opaque_,
RaytraceBuffer &rt_buffer_refract_,
GPUFrameBuffer *view_fb)
{
DRW_stats_group_start("OpaqueLayer");
opaque_layer_.render(drw_view, gbuffer, hiz_front, hiz_back, rt_buffer_opaque_, view_fb);
DRW_stats_group_end();
DRW_stats_group_start("RefractionLayer");
refraction_layer_.render(drw_view, gbuffer, hiz_front, hiz_back, rt_buffer_refract_, view_fb);
DRW_stats_group_end();
/* NOTE(fclem): Reuse the same rtbuffer as refraction but should not use it. */
DRW_stats_group_start("VolumetricLayer");
volumetric_layer_.render(drw_view, gbuffer, hiz_front, hiz_back, rt_buffer_refract_, view_fb);
DRW_stats_group_end();
gbuffer.render_end();
rt_buffer_opaque_.render_end(drw_view);
rt_buffer_refract_.render_end(drw_view);
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,327 @@
/*
* 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_gbuffer.hh"
#include "eevee_raytracing.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;
GPUTexture *input_hiz_tx_ = nullptr;
GPUTexture *input_radiance_tx_ = 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(const DRWView *view, GBuffer &gbuffer, HiZBuffer &hiz, GPUFrameBuffer *view_fb);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Deferred lighting.
* \{ */
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(const DRWView *view,
GBuffer &gbuffer,
HiZBuffer &hiz_front,
HiZBuffer &hiz_back,
RaytraceBuffer &rtbuffer,
GPUFrameBuffer *view_fb);
private:
void update_pass_inputs(GBuffer &gbuffer, HiZBuffer &hiz_front, HiZBuffer &hiz_back);
};
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 hardcoded number of them. */
DeferredLayer opaque_layer_;
DeferredLayer refraction_layer_;
DeferredLayer volumetric_layer_;
DRWPass *eval_direct_ps_ = nullptr;
DRWPass *eval_subsurface_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_diffuse_tx_ = nullptr;
GPUTexture *input_emission_data_tx_ = nullptr;
GPUTexture *input_hiz_front_tx_ = nullptr;
GPUTexture *input_hiz_back_tx_ = nullptr;
GPUTexture *input_reflect_color_tx_ = nullptr;
GPUTexture *input_reflect_normal_tx_ = nullptr;
GPUTexture *input_transmit_color_tx_ = nullptr;
GPUTexture *input_transmit_data_tx_ = nullptr;
GPUTexture *input_transmit_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(const DRWView *drw_view,
GBuffer &gbuffer,
HiZBuffer &hiz_front,
HiZBuffer &hiz_back,
RaytraceBuffer &rtbuffer_opaque,
RaytraceBuffer &rtbuffer_refract,
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", GPU_RGBA16F, int2(lut_size), layer_count, nullptr)
{
#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:
BackgroundPass background;
DeferredPass deferred;
ForwardPass forward;
ShadowPass shadow;
VelocityPass velocity;
UtilityTexture utility_tx;
public:
ShadingPasses(Instance &inst)
: background(inst), deferred(inst), forward(inst), shadow(inst), velocity(inst){};
void sync()
{
deferred.sync();
forward.sync();
shadow.sync();
velocity.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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,611 @@
/*
* 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 "GPU_batch.h"
#include "eevee_allocator.hh"
#include "eevee_id_map.hh"
#include "eevee_material.hh"
#include "eevee_shader.hh"
#include "eevee_shader_shared.hh"
namespace blender::eevee {
/**
* TODO(fclem): Future plans
* The start of the implementation was done on CPU with the constraints of UBO limits and no
* compute capabilities in mind.
* But after removing this limit this left the door open for a full GPU driven pipeline of
* shadow and light management where the CPU would only push Objects updates and manage buffer
* grow/shrink behaviors. The GPU would then do what ShadowTileAllocator, ShadowPunctual and
* ShadowDirectional classes are doing.
* We still need to find a way to issue the shadow render passes at once and cull objects per view
* on GPU.
*/
class Instance;
class ShadowModule;
/** World space axis aligned bounding box. */
struct AABB {
/**
* TODO(fclem) There is padding to match the std430 layout requirement inside shaders storage.
* The goal would be to send the Oriented Bound Box for better culling.
*/
float3 min;
float _pad0;
float3 max;
float _pad1;
AABB() = default;
AABB(float val) : min(-val), max(val){};
AABB(Object *ob)
{
init_min_max();
BoundBox *bb = BKE_object_boundbox_get(ob);
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);
}
}
void debug_draw(void)
{
BoundBox bb = *this;
float4 color = {1, 0, 0, 1};
DRW_debug_bbox(&bb, color);
}
float3 center(void) const
{
return (min + max) * 0.5;
}
void init_min_max(void)
{
INIT_MINMAX(min, max);
}
void merge(const AABB &a)
{
DO_MIN(a.min, min);
DO_MAX(a.max, max);
}
void merge(const float3 &a)
{
DO_MIN(a, min);
DO_MAX(a, max);
}
float radius(void) const
{
return math::length(max - min) / 2.0f;
}
operator BoundBox() const
{
float3 middle = center();
float3 halfdim = max - middle;
BoundBox bb;
*reinterpret_cast<float3 *>(bb.vec[0]) = middle + halfdim * float3(1, 1, 1);
*reinterpret_cast<float3 *>(bb.vec[1]) = middle + halfdim * float3(-1, 1, 1);
*reinterpret_cast<float3 *>(bb.vec[2]) = middle + halfdim * float3(-1, -1, 1);
*reinterpret_cast<float3 *>(bb.vec[3]) = middle + halfdim * float3(1, -1, 1);
*reinterpret_cast<float3 *>(bb.vec[4]) = middle + halfdim * float3(1, 1, -1);
*reinterpret_cast<float3 *>(bb.vec[5]) = middle + halfdim * float3(-1, 1, -1);
*reinterpret_cast<float3 *>(bb.vec[6]) = middle + halfdim * float3(-1, -1, -1);
*reinterpret_cast<float3 *>(bb.vec[7]) = middle + halfdim * float3(1, -1, -1);
return bb;
}
};
/* -------------------------------------------------------------------- */
/** \name Shadow
*
* \{ */
/* To be applied after viewmatrix. */
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 */
};
/* Converts to [-SHADOW_TILEMAP_RES / 2..SHADOW_TILEMAP_RES / 2] for XY and [0..1] for Z. */
constexpr static const float shadow_clipmap_scale_mat[4][4] = {{SHADOW_TILEMAP_RES / 2, 0, 0, 0},
{0, SHADOW_TILEMAP_RES / 2, 0, 0},
{0, 0, 0.5, 0},
{0, 0, 0.5, 1}};
constexpr static const float tilemat_scale_bias_mat[4][4] = {
{SHADOW_TILEMAP_RES / 2, 0, 0, 0},
{0, SHADOW_TILEMAP_RES / 2, 0, 0},
{0, 0, 1, 0},
{SHADOW_TILEMAP_RES / 2, SHADOW_TILEMAP_RES / 2, 0, 1}};
enum eCubeFace {
/* 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,
};
/**
* Stores indirection table and states of each tile of a virtual shadowmap clipmap level.
* One tilemap has the effective resolution of `pagesize * tile_map_resolution` .
* Each tilemap overhead is quite small if they do not have any pages allocated.
*/
struct ShadowTileMap : public ShadowTileMapData {
static constexpr int64_t tile_map_resolution = SHADOW_TILEMAP_RES;
static constexpr int64_t tiles_count = tile_map_resolution * tile_map_resolution;
/**
* Maximum "bounding" angle of a tile inside a cubemap.
* Half the diagonal of tile since we test using the tile center.
*/
static float tile_cone_half_angle;
/** Level of detail for clipmap. */
int level = INT_MAX;
/** Integer offset of the center of the 16x16 tiles from the origin of the tile space. */
int2 grid_offset = int2(16);
/** Cube face index. */
eCubeFace cubeface = Z_NEG;
/** Cached, used for rendering. */
float4x4 viewmat, winmat;
/** Cached, used for detecting updates. */
float4x4 object_mat;
/** Near and far clip distances. For clipmap they are updated after sync. */
float near, far;
public:
ShadowTileMap(int64_t _index)
{
index = _index;
};
void sync_clipmap(const float3 &camera_position,
const float4x4 &object_mat_,
float near_,
float far_,
int2 origin_offset,
int clipmap_level);
void sync_cubeface(
const float4x4 &object_mat, float near, float far, float cone_aperture, eCubeFace face);
float tilemap_coverage_get(void) const
{
/* This function should be kept in sync with shadow_directional_clipmap_level(). */
/* NOTE(fclem): If we would to introduce a global scaling option it would be here. */
BLI_assert(!is_cubeface);
return powf(2.0f, level);
}
float tile_size_get(void) const
{
return tilemap_coverage_get() / tile_map_resolution;
}
float4x4 winmat_get(const rcti *tile_minmax) const;
void setup_view(const rcti &rect, DRWView *&view) const;
void debug_draw(void) const;
/* For external callers. Use this in order to not miss an update. */
void set_level(int clipmap_level)
{
if (level != clipmap_level) {
level = clipmap_level;
set_dirty();
}
}
void set_is_cubemap(bool is_cubemap_)
{
if (is_cubeface != is_cubemap_) {
is_cubeface = is_cubemap_;
set_dirty();
}
}
void set_dirty()
{
grid_shift = int2(SHADOW_TILEMAP_RES);
}
void set_updated()
{
grid_shift = int2(0);
}
};
struct ShadowCommon {
/** Tilemap for each cubeface needed (in eCubeFace order) or for each clipmap level. */
Vector<ShadowTileMap *> tilemaps;
/** To have access to the tilemap allocator. */
ShadowModule *shadows_;
ShadowCommon(ShadowModule *shadows) : shadows_(shadows){};
void free_resources();
};
class ShadowPunctual : public ShadowCommon {
private:
/** Area light size. */
float size_x_, size_y_;
/** Shape type. */
eLightType light_type_;
/** Random position on the light. In world space. */
float3 random_offset_;
/** Light position. */
float3 position_;
/** Near and far clip distances. */
float far_, near_;
/** View space offset to apply to the shadow. */
float bias_;
public:
ShadowPunctual(ShadowModule *shadows) : ShadowCommon(shadows){};
void sync(eLightType light_type,
const float4x4 &object_mat,
float cone_aperture,
float near_clip,
float far_clip,
float bias);
operator ShadowData();
};
class ShadowDirectional : public ShadowCommon {
private:
/** User minimum resolution. */
float min_resolution_;
/** View space offset to apply to the shadow. */
float bias_;
/** Near and far clip distances. For clipmap, when they are updated after sync. */
float near_, far_;
/** Offset of the lowest clipmap relative to the highest one. */
int2 base_offset_;
/** Copy of object matrix. */
float4x4 object_mat_;
public:
ShadowDirectional(ShadowModule *shadows) : ShadowCommon(shadows){};
void sync(const float4x4 &object_mat, float bias, float min_resolution);
void end_sync(int min_level,
int max_level,
const float3 &camera_position,
const AABB &casters_bounds);
operator ShadowData();
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Shadow Casters & Receivers
*
* \{ */
/* Can be either a shadow caster or a shadow receiver. */
struct ShadowObject {
AABB aabb;
bool initialized = false;
bool used;
bool updated;
void sync(Object *ob)
{
aabb = AABB(ob);
initialized = true;
updated = true;
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name ShadowModule
*
* Manages shadow atlas and shadow region datas.
* \{ */
/**
* Manages the tilemaps and allocates continuous regions to a shadow object.
* This way indexing is simple and fast inside the shaders.
* The tilemap atlas has a fixed 64x64 size. So it can contain 4096 tilemap or 16x16 pixels each.
* We allocate for many tilemaps because we don't want to reallocate the buffer as it would mean
* trashing the whole cache which it.
* In the future we could resize and copy old tilemap infos. But for now we KISS.
*/
struct ShadowTileAllocator {
static constexpr int64_t size = SHADOW_MAX_TILEMAP;
/** Limit the with of the texture. */
static constexpr int64_t maps_per_row = SHADOW_TILEMAP_PER_ROW;
/* TODO(fclem): Do it for real... Use real bitmap. */
Vector<bool> usage_bitmap_ = Vector<bool>(size);
/** Circular buffer allocation scheme. This is the last allocated index. */
int64_t next_index = 0;
/** Vector containning the actual maps. Unordered. */
Vector<ShadowTileMap *> maps;
/** Deleted maps go here to be freed after the next sync. */
Vector<ShadowTileMap *> maps_deleted;
/**
* Tilemap atlas containing mapping to shadow pages inside the atlas texture.
* All shadow tilemaps are packed into one texture.
* Contains every clipmaps level of all directional light and each cubeface with mipmap.
*/
Texture tilemap_tx = {"tilemap_tx"};
/** Very small texture containing the result of the update pass. */
/** FIXME(fclem): It would be nice to avoid GPU > CPU readback. */
Texture tilemap_rects_tx = {"tilemap_rects_tx"};
/** UBO containing the description for every allocated tilemap. */
ShadowTileMapDataBuf tilemaps_data;
/** Number of maps inside the tilemaps_data. */
int64_t active_maps_len = 0;
/** Number of maps at the end of tilemaps_data that are being deleted and need clear. */
int64_t deleted_maps_len = 0;
ShadowTileAllocator();
~ShadowTileAllocator();
/** Returns empty span on failure. */
Span<ShadowTileMap *> alloc(int64_t count);
void free(Vector<ShadowTileMap *> &free_list);
void end_sync();
};
/**
* Simple struct here to group all things page related.
*/
struct ShadowVirtualPageManager {
ShadowVirtualPageManager();
~ShadowVirtualPageManager();
void end_sync();
};
class ShadowModule {
friend ShadowPunctual;
friend ShadowDirectional;
template<typename T> class ShadowAllocator : public IndexedAllocator<T> {
private:
ShadowModule &shadows_;
public:
ShadowAllocator(ShadowModule &shadows) : shadows_(shadows){};
int64_t alloc(void)
{
return IndexedAllocator<T>::alloc(T(&shadows_));
}
};
public:
/** Need to be first because of destructor order. */
ShadowTileAllocator tilemap_allocator;
ShadowAllocator<ShadowPunctual> punctuals;
ShadowAllocator<ShadowDirectional> directionals;
private:
Instance &inst_;
/** Map of shadow casters to track deletion & update of intersected shadows. */
Map<ObjectKey, ShadowObject> objects_;
/** 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};
/**
* Separate render buffer. This is meant to be replace by directly rendering inside the atlas.
*/
Texture render_tx_ = {"shadow_target_tx_"};
Framebuffer render_fb_ = {"shadow_fb"};
/* -------------------------------------------------------------------- */
/** \name Tilemap Management
* \{ */
/**
* Clear the visibility, usage and request bits.
* Also shifts the whole tilemap for directional shadow clipmaps.
*/
DRWPass *tilemap_setup_ps_;
/** Update passes that will mark all shadow pages from a light to update or as unused. */
DRWPass *tilemap_visibility_ps_;
/** Update passes that will mark all shadow pages touching an updated shadow caster. */
DRWPass *tilemap_update_tag_ps_;
/** Tag each tile intersecting with a shadow receiver. */
/* NOTE(fclem): Until we implement depth buffer scanning, we rely solely on this to tag
* needed tiles. */
DRWPass *tilemap_usage_tag_ps_;
/** Use depth buffer to tag needed shadow pages. */
DRWPass *tilemap_depth_scan_ps_;
/** Discard pages that are redundant in the mipmap chain. */
DRWPass *tilemap_lod_mask_ps_;
/** List of AABBs for tagging passes. */
DRWCallBuffer *casters_updated_;
DRWCallBuffer *receivers_non_opaque_;
int do_tilemap_setup_ = true;
const DRWView *last_processed_view = nullptr;
float tilemap_pixel_radius_;
float screen_pixel_radius_inv_;
/** \} */
/* -------------------------------------------------------------------- */
/** \name Page Management
* \{ */
Texture atlas_tx_ = {"shadow_atlas_tx_"};
/** Pool of unallocated pages waiting to be assigned to specific tiles in the tilemap atlas. */
ShadowPageHeapBuf pages_free_data_;
/** Infos for book keeping and debug. */
ShadowPagesInfoDataBuf pages_infos_data_;
/** Page buffer clear. This is only done if shadow atlas is reallocated. */
DRWPass *page_init_ps_;
/** Defragment the page free array. */
DRWPass *page_defrag_ps_;
/** Free pages of deleted tiles. You can think of a garbage collection. */
DRWPass *page_free_ps_;
/** Allocate pages for new tiles. */
DRWPass *page_alloc_ps_;
/** Clear depth of tiles to render to 1.0 and 0.0 for others. */
DRWPass *page_mark_ps_;
/** Copy pages in the copy list. */
DRWPass *page_copy_ps_;
bool do_page_init_ = true;
int3 copy_dispatch_size_;
int3 scan_dispatch_size_;
int rendering_tilemap_;
int rendering_lod_;
/** \} */
/* -------------------------------------------------------------------- */
/** \name Debugging
* \{ */
/** Display informations about the virtual shadows. */
DRWPass *debug_draw_ps_;
/** Depth input for debug drawing. Reference only. */
GPUTexture *input_depth_tx_;
/** Object key used to retreive last active light. The debug info shown are from this light. */
ObjectKey debug_light_key;
/** View used for the whole virtual shadow mapping setup. Used to debug culling. */
DRWView *debug_view_;
/** Debug data sent to GPU. */
ShadowDebugDataBuf debug_data_;
/** Debug texture to check page status. */
Texture debug_page_tx_ = {"debug_page_tx_"};
/** \} */
/** Scene immutable parameter. */
int shadow_page_size_ = 256;
bool soft_shadows_enabled_ = false;
/** Default to invalid texture type. */
eGPUTextureFormat shadow_format_ = GPU_RGBA8;
/** Used for caster & receiver AABB lists. */
GPUVertFormat aabb_format_;
/** Global bounds that contains all shadow casters. Used by directionnal for best fit. */
AABB casters_bounds_;
public:
ShadowModule(Instance &inst) : punctuals(*this), directionals(*this), inst_(inst)
{
GPU_vertformat_clear(&aabb_format_);
/* Must match the C++ AABB layout. */
BLI_assert(sizeof(AABB) == sizeof(float) * 8);
GPU_vertformat_attr_add(&aabb_format_, "aabb_min", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
GPU_vertformat_attr_add(&aabb_format_, "aabb_max", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
}
~ShadowModule(){};
void init(void);
void begin_sync(void);
void sync_object(Object *ob,
const ObjectHandle &handle,
bool is_shadow_caster,
bool is_alpha_blend);
void end_sync(void);
void set_view(const DRWView *view, GPUTexture *depth_tx);
void debug_end_sync(void);
void debug_draw(GPUFrameBuffer *view_fb, HiZBuffer &hiz);
GPUTexture *atlas_tx_get(void)
{
return atlas_tx_;
}
GPUTexture *tilemap_tx_get(void)
{
return tilemap_allocator.tilemap_tx;
}
private:
void remove_unused(void);
void debug_page_map_call(DRWPass *pass);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name ShadowPass
*
* A simple depth pass to which all shadow casters subscribe.
* \{ */
class ShadowPass {
private:
Instance &inst_;
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,413 +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);
}
}
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);
}
}
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,224 +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);
}
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

@@ -0,0 +1,205 @@
/*
* 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_vector.hh"
#include "eevee_instance.hh"
#include "eevee_subsurface.hh"
#include <iostream>
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name Subsurface
*
* \{ */
/* TODO(fclem) Only enable this module if there is any SSS object in the scene. */
void SubsurfaceModule::end_sync()
{
data_.jitter_threshold = inst_.scene->eevee.sss_jitter_threshold;
if (data_.sample_len != inst_.scene->eevee.sss_samples) {
/* Convert sample count from old implementation which was using a separable filter. */
/* TODO(fclem) better remapping. */
// data_.sample_len = square_f(1 + 2 * inst_.scene->eevee.sss_samples);
data_.sample_len = 55;
}
if (transmittance_tx == nullptr) {
precompute_transmittance_profile();
}
precompute_samples_location();
data_.push_update();
}
void SubsurfaceModule::precompute_samples_location()
{
/* Precompute sample position with white albedo. */
float d = burley_setup(1.0f, 1.0f);
float rand_u = inst_.sampling.rng_get(SAMPLING_SSS_U);
float rand_v = inst_.sampling.rng_get(SAMPLING_SSS_V);
double golden_angle = M_PI * (3.0 - sqrt(5.0));
for (auto i : IndexRange(data_.sample_len)) {
float theta = golden_angle * i + M_PI * 2.0f * rand_u;
/* Scale using rand_v in order to keep first sample always at center. */
float x = (1.0f + (rand_v / data_.sample_len)) * (i / (float)data_.sample_len);
float r = burley_sample(d, x);
data_.samples[i].x = cosf(theta) * r;
data_.samples[i].y = sinf(theta) * r;
data_.samples[i].z = 1.0f / burley_pdf(d, r);
}
}
void SubsurfaceModule::precompute_transmittance_profile()
{
Vector<float> profile(SSS_TRANSMIT_LUT_SIZE);
/* Precompute sample position with white albedo. */
float radius = 1.0f;
float d = burley_setup(radius, 1.0f);
/* For each distance d we compute the radiance incoming from an hypothetical parallel plane. */
for (auto i : IndexRange(SSS_TRANSMIT_LUT_SIZE)) {
/* Distance from the lit surface plane.
* Compute to a larger maximum distance to have a smoother falloff for all channels. */
float lut_radius = SSS_TRANSMIT_LUT_RADIUS * radius;
float distance = lut_radius * (i + 1e-5f) / profile.size();
/* Compute radius of the footprint on the hypothetical plane. */
float r_fp = sqrtf(square_f(lut_radius) - square_f(distance));
profile[i] = 0.0f;
float area_accum = 0.0f;
for (auto j : IndexRange(SSS_TRANSMIT_LUT_STEP_RES)) {
/* Compute distance to the "shading" point through the medium. */
float r = (r_fp * (j + 0.5f)) / SSS_TRANSMIT_LUT_STEP_RES;
float r_prev = (r_fp * (j + 0.0f)) / SSS_TRANSMIT_LUT_STEP_RES;
float r_next = (r_fp * (j + 1.0f)) / SSS_TRANSMIT_LUT_STEP_RES;
r = hypotf(r, distance);
float R = burley_eval(d, r);
/* Since the profile and configuration are radially symmetrical we
* can just evaluate it once and weight it accordingly */
float disk_area = square_f(r_next) - square_f(r_prev);
profile[i] += R * disk_area;
area_accum += disk_area;
}
/* Normalize over the disk. */
profile[i] /= area_accum;
}
/* Make a smooth gradient from 1 to 0. */
float range = profile.first() - profile.last();
float offset = profile.last();
for (float &value : profile) {
value = (value - offset) / range;
}
profile.first() = 1;
profile.last() = 0;
transmittance_tx = GPU_texture_create_1d(
"SSSTransmittanceProfile", profile.size(), 1, GPU_R16F, profile.data());
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Christensen-Burley SSS model
*
* Based on: "Approximate Reflectance Profiles for Efficient Subsurface Scattering"
* by Per Christensen
* https://graphics.pixar.com/library/ApproxBSSRDF/approxbssrdfslides.pdf
* \{ */
float SubsurfaceModule::burley_setup(float radius, float albedo)
{
float A = albedo;
/* Diffuse surface transmission, equation (6). */
float s = 1.9f - A + 3.5f * square_f(A - 0.8f);
/* Mean free path length adapted to fit ancient Cubic and Gaussian models. */
float l = 0.25 * M_1_PI * radius;
return l / s;
}
float SubsurfaceModule::burley_sample(float d, float x_rand)
{
x_rand *= SSS_BURLEY_TRUNCATE_CDF;
const float tolerance = 1e-6;
const int max_iteration_count = 10;
/* Do initial guess based on manual curve fitting, this allows us to reduce
* number of iterations to maximum 4 across the [0..1] range. We keep maximum
* number of iteration higher just to be sure we didn't miss root in some
* corner case.
*/
float r;
if (x_rand <= 0.9) {
r = exp(x_rand * x_rand * 2.4) - 1.0;
}
else {
/* TODO(sergey): Some nicer curve fit is possible here. */
r = 15.0;
}
/* Solve against scaled radius. */
for (int i = 0; i < max_iteration_count; i++) {
float exp_r_3 = exp(-r / 3.0);
float exp_r = exp_r_3 * exp_r_3 * exp_r_3;
float f = 1.0 - 0.25 * exp_r - 0.75 * exp_r_3 - x_rand;
float f_ = 0.25 * exp_r + 0.25 * exp_r_3;
if (abs(f) < tolerance || f_ == 0.0) {
break;
}
r = r - f / f_;
if (r < 0.0) {
r = 0.0;
}
}
return r * d;
}
float SubsurfaceModule::burley_eval(float d, float r)
{
if (r >= SSS_BURLEY_TRUNCATE * d) {
return 0.0;
}
/* Slide 33. */
float exp_r_3_d = expf(-r / (3.0f * d));
float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d;
return (exp_r_d + exp_r_3_d) / (8.0f * (float)M_PI * d);
}
float SubsurfaceModule::burley_pdf(float d, float r)
{
return burley_eval(d, r) / SSS_BURLEY_TRUNCATE_CDF;
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,84 @@
/*
* 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 "eevee_shader.hh"
#include "eevee_shader_shared.hh"
#include "eevee_wrapper.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name Subsurface
*
* \{ */
class Instance;
struct SubsurfaceModule {
private:
Instance &inst_;
/** Contains samples locations. */
SubsurfaceDataBuf data_;
/** Contains translucence profile for a single color channel. */
GPUTexture *transmittance_tx = nullptr;
public:
SubsurfaceModule(Instance &inst) : inst_(inst)
{
/* Force first update. */
data_.sample_len = -1;
};
~SubsurfaceModule()
{
GPU_TEXTURE_FREE_SAFE(transmittance_tx);
};
void end_sync();
const GPUUniformBuf *ubo_get(void) const
{
return data_;
}
GPUTexture **transmittance_ref_get(void)
{
return &transmittance_tx;
}
private:
void precompute_samples_location();
void precompute_transmittance_profile();
/** Christensen-Burley implementation. */
static float burley_setup(float radius, float albedo);
static float burley_sample(float d, float x_rand);
static float burley_eval(float d, float r);
static float burley_pdf(float d, float r);
};
/** \} */
} // namespace blender::eevee

View File

@@ -1,438 +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);
}
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) {
data->next_object_mat = ob->obmat;
}
else if (step_ == STEP_PREVIOUS) {
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()) {
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.ptr());
}
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.ptr())) {
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);
DRW_shgroup_uniform_block(grp, "camera_next_block", velocity.camera_step.next);
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);
DRW_shgroup_uniform_block(grp, "camera_next_block", velocity.camera_step.next);
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.ptr())) {
copy_m4_m4(data->prev_object_mat.ptr(), 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.ptr(), 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.ptr())) {
data->prev_object_mat = ob->obmat;
}
if (is_zero_m4(data->next_object_mat.ptr())) {
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.ptr(), ob->obmat) &&
equals_m4m4(data->next_object_mat.ptr(), 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);
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. */
draw::UniformBuffer<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. */
Framebuffer velocity_fb_;
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,261 @@
/*
* 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(int2 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();
float4x4 viewmat, winmat;
const float(*viewmat_p)[4] = viewmat.ptr(), (*winmat_p)[4] = winmat.ptr();
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.ptr(), -side, side, -side, side, near, far);
viewmat = face_matrix_ * data.viewmat;
}
else {
viewmat_p = data.viewmat.ptr();
winmat_p = data.winmat.ptr();
}
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_);
rt_buffer_opaque_.sync(extent_);
rt_buffer_refract_.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_));
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(render_view_,
gbuffer_,
hiz_front_,
hiz_back_,
rt_buffer_opaque_,
rt_buffer_refract_,
view_fb_);
inst_.lightprobes.draw_cache_display();
inst_.lookdev.render_overlay(view_fb_);
inst_.shading_passes.forward.render(render_view_, gbuffer_, hiz_front_, view_fb_);
inst_.lights.debug_draw(view_fb_, hiz_front_);
inst_.shadows.debug_draw(view_fb_, hiz_front_);
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_);
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 float4x4 winmat,
const float4x4 viewmat,
bool is_only_background)
{
float4x4 facemat = face_matrix_ * viewmat;
is_only_background_ = is_only_background;
extent_ = int2(color_tx.width());
view_ = DRW_view_create(facemat.ptr(), winmat.ptr(), 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_);
rt_buffer_opaque_.sync(extent_);
rt_buffer_refract_.sync(extent_);
}
}
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(
view_, gbuffer_, hiz_front_, hiz_back_, rt_buffer_opaque_, rt_buffer_refract_, view_fb_);
inst_.shading_passes.forward.render(view_, gbuffer_, hiz_front_, view_fb_);
}
DRW_stats_group_end();
}
/** \} */
} // namespace blender::eevee

View File

@@ -0,0 +1,250 @@
/*
* 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_hizbuffer.hh"
#include "eevee_motion_blur.hh"
#include "eevee_raytracing.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 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];
/** Post-fx modules. */
DepthOfField dof_;
MotionBlur mb_;
Velocity velocity_;
/** GBuffer for deferred passes. */
GBuffer gbuffer_;
/** HiZBuffer for screenspace effects. */
HiZBuffer hiz_front_, hiz_back_;
/** Raytracing persistent buffers. Only opaque and refraction can have surface tracing. */
RaytraceBuffer rt_buffer_opaque_;
RaytraceBuffer rt_buffer_refract_;
/** Owned resources. */
Framebuffer view_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. */
int2 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),
hiz_front_(inst),
hiz_back_(inst),
rt_buffer_opaque_(inst),
rt_buffer_refract_(inst){};
~ShadingView(){};
void init(void);
void sync(int2 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_;
/** HiZBuffer for screenspace effects. */
HiZBuffer hiz_front_, hiz_back_;
/** Raytracing persistent buffers. Only opaque and refraction can have surface tracing. */
RaytraceBuffer rt_buffer_opaque_;
RaytraceBuffer rt_buffer_refract_;
/** Owned resources. */
Framebuffer view_fb_;
/** DRWView of this face. */
DRWView *view_ = nullptr;
/** Render size of the view. */
int2 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),
hiz_front_(inst),
hiz_back_(inst),
rt_buffer_opaque_(inst),
rt_buffer_refract_(inst),
layer_(layer_){};
~LightProbeView(){};
void sync(Texture &color_tx,
Texture &depth_tx,
const float4x4 winmat,
const float4x4 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:
/* WORKAROUND: Defining this as an array does not seems to work on GCC < 9.4.
* It tries to use the copy constructor and fails because ShadingView is non-copyable and
* non-movable. */
ShadingView shading_views_0;
ShadingView shading_views_1;
ShadingView shading_views_2;
ShadingView shading_views_3;
ShadingView shading_views_4;
ShadingView shading_views_5;
#define shading_views_ (&shading_views_0)
/** Internal render size. */
int render_extent_[2];
public:
MainView(Instance &inst)
: shading_views_0(inst, "posX_view", cubeface_mat[0]),
shading_views_1(inst, "negX_view", cubeface_mat[1]),
shading_views_2(inst, "posY_view", cubeface_mat[2]),
shading_views_3(inst, "negY_view", cubeface_mat[3]),
shading_views_4(inst, "posZ_view", cubeface_mat[4]),
shading_views_5(inst, "negZ_view", cubeface_mat[5])
{
}
void init(const int2 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(6)) {
shading_views_[i].init();
}
}
void sync(void)
{
for (auto i : IndexRange(6)) {
shading_views_[i].sync(render_extent_);
}
}
void render(void)
{
for (auto i : IndexRange(6)) {
shading_views_[i].render();
}
}
#undef shading_views_
};
/** \} */
} // 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

View File

@@ -0,0 +1,57 @@
/*
* 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
*
* Templated wrappers to make it easier to use GPU objects in C++.
*/
#pragma once
#include "DRW_gpu_wrapper.hh"
namespace blender::eevee {
using draw::Framebuffer;
using draw::Texture;
using draw::TextureFromPool;
static inline void shgroup_geometry_call(DRWShadingGroup *grp,
Object *ob,
GPUBatch *geom,
int v_first = -1,
int v_count = -1,
bool use_instancing = false)
{
if (grp == nullptr) {
return;
}
if (v_first == -1) {
DRW_shgroup_call(grp, geom, ob);
}
else if (use_instancing) {
DRW_shgroup_call_instance_range(grp, ob, geom, v_first, v_count);
}
else {
DRW_shgroup_call_range(grp, ob, geom, v_first, v_count);
}
}
} // namespace blender::eevee

View File

@@ -1,417 +0,0 @@
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
#pragma BLENDER_REQUIRE(raytrace_lib.glsl)
/* Based on Practical Realtime Strategies for Accurate Indirect Occlusion
* http://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf
* http://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pptx
*/
#if defined(MESH_SHADER)
# if !defined(USE_ALPHA_HASH)
# if !defined(DEPTH_SHADER)
# if !defined(USE_ALPHA_BLEND)
# if !defined(USE_REFRACTION)
# define ENABLE_DEFERED_AO
# endif
# endif
# endif
# endif
#endif
#ifndef ENABLE_DEFERED_AO
# if defined(STEP_RESOLVE)
# define ENABLE_DEFERED_AO
# endif
#endif
uniform sampler2D horizonBuffer;
/* aoSettings flags */
#define USE_AO 1
#define USE_BENT_NORMAL 2
#define USE_DENOISE 4
#define NO_OCCLUSION_DATA OcclusionData(vec4(M_PI, -M_PI, M_PI, -M_PI), 1.0)
struct OcclusionData {
/* 4 horizons angles, one in each direction around the view vector to form a cross pattern. */
vec4 horizons;
/* Custom large scale occlusion. */
float custom_occlusion;
};
vec4 pack_occlusion_data(OcclusionData data)
{
return vec4(1.0 - data.horizons * vec4(1, -1, 1, -1) * M_1_PI);
}
OcclusionData unpack_occlusion_data(vec4 v)
{
return OcclusionData((1.0 - v) * vec4(1, -1, 1, -1) * M_PI, 0.0);
}
vec2 get_ao_noise(void)
{
vec2 noise = texelfetch_noise_tex(gl_FragCoord.xy).xy;
/* Decorrelate noise from AA. */
/* TODO(fclem) we should use a more general approach for more random number dimensions. */
noise = fract(noise * 6.1803402007);
return noise;
}
vec2 get_ao_dir(float jitter)
{
/* Only a quarter of a turn because we integrate using 2 slices.
* We use this instead of using utiltex circle noise to improve cache hits
* since all tracing direction will be in the same quadrant. */
jitter *= M_PI_2;
return vec2(cos(jitter), sin(jitter));
}
/* Return horizon angle cosine. */
float search_horizon(vec3 vI,
vec3 vP,
float noise,
ScreenSpaceRay ssray,
sampler2D depth_tx,
const float inverted,
float radius,
const float sample_count)
{
/* Init at cos(M_PI). */
float h = (inverted != 0.0) ? 1.0 : -1.0;
ssray.max_time -= 1.0;
if (ssray.max_time <= 2.0) {
/* Produces self shadowing under this threshold. */
return fast_acos(h);
}
float prev_time, time = 0.0;
for (float iter = 0.0; time < ssray.max_time && iter < sample_count; iter++) {
prev_time = time;
/* Gives us good precision at center and ensure we cross at least one pixel per iteration. */
time = 1.0 + iter + sqr((iter + noise) / sample_count) * ssray.max_time;
float stride = time - prev_time;
float lod = (log2(stride) - noise) / (1.0 + aoQuality);
vec2 uv = ssray.origin.xy + ssray.direction.xy * time;
float depth = textureLod(depth_tx, uv * hizUvScale.xy, floor(lod)).r;
if (depth == 1.0 && inverted == 0.0) {
/* Skip background. Avoids making shadow on the geometry near the far plane. */
continue;
}
/* Bias depth a bit to avoid self shadowing issues. */
const float bias = 2.0 * 2.4e-7;
depth += (inverted != 0.0) ? -bias : bias;
vec3 s = get_view_space_from_depth(uv, depth);
vec3 omega_s = s - vP;
float len = length(omega_s);
/* Sample's horizon angle cosine. */
float s_h = dot(vI, omega_s / len);
/* Blend weight to fade artifacts. */
float dist_ratio = abs(len) / radius;
/* Sphere falloff. */
float dist_fac = sqr(saturate(dist_ratio));
/* Unbiased, gives too much hard cut behind objects */
// float dist_fac = step(0.999, dist_ratio);
if (inverted != 0.0) {
h = min(h, s_h);
}
else {
h = mix(max(h, s_h), h, dist_fac);
}
}
return fast_acos(h);
}
OcclusionData occlusion_search(
vec3 vP, sampler2D depth_tx, float radius, const float inverted, const float dir_sample_count)
{
if ((int(aoSettings) & USE_AO) == 0) {
return NO_OCCLUSION_DATA;
}
vec2 noise = get_ao_noise();
vec2 dir = get_ao_dir(noise.x);
vec2 uv = get_uvs_from_view(vP);
vec3 vI = ((ProjectionMatrix[3][3] == 0.0) ? normalize(-vP) : vec3(0.0, 0.0, 1.0));
vec3 avg_dir = vec3(0.0);
float avg_apperture = 0.0;
OcclusionData data = (inverted != 0.0) ? OcclusionData(vec4(0, 0, 0, 0), 1.0) :
NO_OCCLUSION_DATA;
for (int i = 0; i < 2; i++) {
Ray ray;
ray.origin = vP;
ray.direction = vec3(dir * radius, 0.0);
ScreenSpaceRay ssray;
ssray = raytrace_screenspace_ray_create(ray);
data.horizons[0 + i * 2] = search_horizon(
vI, vP, noise.y, ssray, depth_tx, inverted, radius, dir_sample_count);
ray.direction = -ray.direction;
ssray = raytrace_screenspace_ray_create(ray);
data.horizons[1 + i * 2] = -search_horizon(
vI, vP, noise.y, ssray, depth_tx, inverted, radius, dir_sample_count);
/* Rotate 90 degrees. */
dir = vec2(-dir.y, dir.x);
}
return data;
}
vec2 clamp_horizons_to_hemisphere(vec2 horizons, float angle_N, const float inverted)
{
/* Add a little bias to fight self shadowing. */
const float max_angle = M_PI_2 - 0.05;
if (inverted != 0.0) {
horizons.x = max(horizons.x, angle_N + max_angle);
horizons.y = min(horizons.y, angle_N - max_angle);
}
else {
horizons.x = min(horizons.x, angle_N + max_angle);
horizons.y = max(horizons.y, angle_N - max_angle);
}
return horizons;
}
void occlusion_eval(OcclusionData data,
vec3 V,
vec3 N,
vec3 Ng,
const float inverted,
out float visibility,
out float visibility_error,
out vec3 bent_normal)
{
/* No error by default. */
visibility_error = 1.0;
if ((int(aoSettings) & USE_AO) == 0) {
visibility = data.custom_occlusion;
bent_normal = N;
return;
}
bool early_out = (inverted != 0.0) ? (max_v4(abs(data.horizons)) == 0.0) :
(min_v4(abs(data.horizons)) == M_PI);
if (early_out) {
visibility = saturate(dot(N, Ng) * 0.5 + 0.5);
visibility = min(visibility, data.custom_occlusion);
if ((int(aoSettings) & USE_BENT_NORMAL) == 0) {
bent_normal = N;
}
else {
bent_normal = safe_normalize(N + Ng);
}
return;
}
vec2 noise = get_ao_noise();
vec2 dir = get_ao_dir(noise.x);
visibility_error = 0.0;
visibility = 0.0;
bent_normal = N * 0.001;
for (int i = 0; i < 2; i++) {
vec3 T = transform_direction(ViewMatrixInverse, vec3(dir, 0.0));
/* Setup integration domain around V. */
vec3 B = normalize(cross(V, T));
T = normalize(cross(B, V));
float proj_N_len;
vec3 proj_N = normalize_len(N - B * dot(N, B), proj_N_len);
vec3 proj_Ng = normalize(Ng - B * dot(Ng, B));
vec2 h = (i == 0) ? data.horizons.xy : data.horizons.zw;
float N_sin = dot(proj_N, T);
float Ng_sin = dot(proj_Ng, T);
float N_cos = saturate(dot(proj_N, V));
float Ng_cos = saturate(dot(proj_Ng, V));
/* Gamma, angle between normalized projected normal and view vector. */
float angle_Ng = sign(Ng_sin) * fast_acos(Ng_cos);
float angle_N = sign(N_sin) * fast_acos(N_cos);
/* Clamp horizons to hemisphere around shading normal. */
h = clamp_horizons_to_hemisphere(h, angle_N, inverted);
float bent_angle = (h.x + h.y) * 0.5;
/* NOTE: here we multiply z by 0.5 as it shows less difference with the geometric normal.
* Also modulate by projected normal length to reduce issues with slanted surfaces.
* All of this is ad-hoc and not really grounded. */
bent_normal += proj_N_len * (T * sin(bent_angle) + V * 0.5 * cos(bent_angle));
/* Clamp to geometric normal only for integral to keep smooth bent normal. */
/* This is done to match Cycles ground truth but adds some computation. */
h = clamp_horizons_to_hemisphere(h, angle_Ng, inverted);
/* Inner integral (Eq. 7). */
float a = dot(-cos(2.0 * h - angle_N) + N_cos + 2.0 * h * N_sin, vec2(0.25));
/* Correct normal not on plane (Eq. 8). */
visibility += proj_N_len * a;
/* Using a very low number of slices (2) leads to over-darkening of surfaces orthogonal to
* the view. This is particularly annoying for sharp reflections occlusion. So we compute how
* much the error is and correct the visibility later. */
visibility_error += proj_N_len;
/* Rotate 90 degrees. */
dir = vec2(-dir.y, dir.x);
}
/* We integrated 2 directions. */
visibility *= 0.5;
visibility_error *= 0.5;
visibility = min(visibility, data.custom_occlusion);
if ((int(aoSettings) & USE_BENT_NORMAL) == 0) {
bent_normal = N;
}
else {
/* NOTE: using pow(visibility, 6.0) produces NaN (see T87369). */
float tmp = saturate(pow6(visibility));
bent_normal = normalize(mix(bent_normal, N, tmp));
}
}
/* Multibounce approximation base on surface albedo.
* Page 78 in the .pdf version. */
float gtao_multibounce(float visibility, vec3 albedo)
{
if (aoBounceFac == 0.0) {
return visibility;
}
/* Median luminance. Because Colored multibounce looks bad. */
float lum = dot(albedo, vec3(0.3333));
float a = 2.0404 * lum - 0.3324;
float b = -4.7951 * lum + 0.6417;
float c = 2.7552 * lum + 0.6903;
float x = visibility;
return max(x, ((x * a + b) * x + c) * x);
}
float diffuse_occlusion(OcclusionData data, vec3 V, vec3 N, vec3 Ng)
{
vec3 unused;
float unused_error;
float visibility;
occlusion_eval(data, V, N, Ng, 0.0, visibility, unused_error, unused);
/* Scale by user factor */
visibility = pow(saturate(visibility), aoFactor);
return visibility;
}
float diffuse_occlusion(
OcclusionData data, vec3 V, vec3 N, vec3 Ng, vec3 albedo, out vec3 bent_normal)
{
float visibility;
float unused_error;
occlusion_eval(data, V, N, Ng, 0.0, visibility, unused_error, bent_normal);
visibility = gtao_multibounce(visibility, albedo);
/* Scale by user factor */
visibility = pow(saturate(visibility), aoFactor);
return visibility;
}
/**
* Approximate the area of intersection of two spherical caps
* radius1 : First caps radius (arc length in radians)
* radius2 : Second caps radius (in radians)
* dist : Distance between caps (radians between centers of caps)
* NOTE: Result is divided by pi to save one multiply.
*/
float spherical_cap_intersection(float radius1, float radius2, float dist)
{
/* From "Ambient Aperture Lighting" by Chris Oat
* Slide 15. */
float max_radius = max(radius1, radius2);
float min_radius = min(radius1, radius2);
float sum_radius = radius1 + radius2;
float area;
if (dist <= max_radius - min_radius) {
/* One cap in completely inside the other */
area = 1.0 - cos(min_radius);
}
else if (dist >= sum_radius) {
/* No intersection exists */
area = 0;
}
else {
float diff = max_radius - min_radius;
area = smoothstep(0.0, 1.0, 1.0 - saturate((dist - diff) / (sum_radius - diff)));
area *= 1.0 - cos(min_radius);
}
return area;
}
float specular_occlusion(
OcclusionData data, vec3 V, vec3 N, float roughness, inout vec3 specular_dir)
{
vec3 visibility_dir;
float visibility_error;
float visibility;
occlusion_eval(data, V, N, N, 0.0, visibility, visibility_error, visibility_dir);
/* Correct visibility error for very sharp surfaces. */
visibility *= mix(safe_rcp(visibility_error), 1.0, roughness);
specular_dir = normalize(mix(specular_dir, visibility_dir, roughness * (1.0 - visibility)));
/* Visibility to cone angle (eq. 18). */
float vis_angle = fast_acos(sqrt(1 - visibility));
/* Roughness to cone angle (eq. 26). */
float spec_angle = max(0.00990998744964599609375, fast_acos(cone_cosine(roughness)));
/* Angle between cone axes. */
float cone_cone_dist = fast_acos(saturate(dot(visibility_dir, specular_dir)));
float cone_nor_dist = fast_acos(saturate(dot(N, specular_dir)));
float isect_solid_angle = spherical_cap_intersection(vis_angle, spec_angle, cone_cone_dist);
float specular_solid_angle = spherical_cap_intersection(M_PI_2, spec_angle, cone_nor_dist);
float specular_occlusion = isect_solid_angle / specular_solid_angle;
/* Mix because it is unstable in unoccluded areas. */
float tmp = saturate(pow8(visibility));
visibility = mix(specular_occlusion, 1.0, tmp);
/* Scale by user factor */
visibility = pow(saturate(visibility), aoFactor);
return visibility;
}
/* Use the right occlusion. */
OcclusionData occlusion_load(vec3 vP, float custom_occlusion)
{
/* Default to fully opened cone. */
OcclusionData data = NO_OCCLUSION_DATA;
#ifdef ENABLE_DEFERED_AO
if ((int(aoSettings) & USE_AO) != 0) {
data = unpack_occlusion_data(texelFetch(horizonBuffer, ivec2(gl_FragCoord.xy), 0));
}
#else
/* For blended surfaces. */
data = occlusion_search(vP, maxzBuffer, aoDistance, 0.0, 8.0);
#endif
data.custom_occlusion = custom_occlusion;
return data;
}

View File

@@ -1,27 +0,0 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(surface_lib.glsl)
in vec2 pos;
RESOURCE_ID_VARYING
void main()
{
GPU_INTEL_VERTEX_SHADER_WORKAROUND
PASS_RESOURCE_ID
gl_Position = vec4(pos, 1.0, 1.0);
viewPosition = vec3(pos, -1.0);
#ifndef VOLUMETRICS
/* Not used in practice but needed to avoid compilation errors. */
worldPosition = viewPosition;
worldNormal = viewNormal = normalize(-viewPosition);
#endif
#ifdef USE_ATTR
pass_attr(viewPosition, NormalMatrix, ModelMatrixInverse);
#endif
}

View File

@@ -1,206 +0,0 @@
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
vec3 diffuse_dominant_dir(vec3 bent_normal)
{
return bent_normal;
}
vec3 specular_dominant_dir(vec3 N, vec3 V, float roughness)
{
vec3 R = -reflect(V, N);
float smoothness = 1.0 - roughness;
float fac = smoothness * (sqrt(smoothness) + roughness);
return normalize(mix(N, R, fac));
}
float ior_from_f0(float f0)
{
float f = sqrt(f0);
return (-f - 1.0) / (f - 1.0);
}
/* Simplified form of F_eta(eta, 1.0). */
float f0_from_ior(float eta)
{
float A = (eta - 1.0) / (eta + 1.0);
return A * A;
}
vec3 refraction_dominant_dir(vec3 N, vec3 V, float roughness, float ior)
{
/* TODO: This a bad approximation. Better approximation should fit
* the refracted vector and roughness into the best prefiltered reflection
* lobe. */
/* Correct the IOR for ior < 1.0 to not see the abrupt delimitation or the TIR */
ior = (ior < 1.0) ? mix(ior, 1.0, roughness) : ior;
float eta = 1.0 / ior;
float NV = dot(N, -V);
/* Custom Refraction. */
float k = 1.0 - eta * eta * (1.0 - NV * NV);
k = max(0.0, k); /* Only this changes. */
vec3 R = eta * -V - (eta * NV + sqrt(k)) * N;
return R;
}
/* Fresnel monochromatic, perfect mirror */
float F_eta(float eta, float cos_theta)
{
/* compute fresnel reflectance without explicitly computing
* the refracted direction */
float c = abs(cos_theta);
float g = eta * eta - 1.0 + c * c;
if (g > 0.0) {
g = sqrt(g);
float A = (g - c) / (g + c);
float B = (c * (g + c) - 1.0) / (c * (g - c) + 1.0);
return 0.5 * A * A * (1.0 + B * B);
}
/* Total internal reflections. */
return 1.0;
}
/* Fresnel color blend base on fresnel factor */
vec3 F_color_blend(float eta, float fresnel, vec3 f0_color)
{
float f0 = f0_from_ior(eta);
float fac = saturate((fresnel - f0) / (1.0 - f0));
return mix(f0_color, vec3(1.0), fac);
}
/* Fresnel split-sum approximation. */
vec3 F_brdf_single_scatter(vec3 f0, vec3 f90, vec2 lut)
{
/* Unreal specular matching : if specular color is below 2% intensity,
* treat as shadowning */
return lut.y * f90 + lut.x * f0;
}
/* Multi-scattering brdf approximation from :
* "A Multiple-Scattering Microfacet Model for Real-Time Image-based Lighting"
* by Carmelo J. Fdez-Agüera. */
vec3 F_brdf_multi_scatter(vec3 f0, vec3 f90, vec2 lut)
{
vec3 FssEss = lut.y * f90 + lut.x * f0;
float Ess = lut.x + lut.y;
float Ems = 1.0 - Ess;
vec3 Favg = f0 + (1.0 - f0) / 21.0;
vec3 Fms = FssEss * Favg / (1.0 - (1.0 - Ess) * Favg);
/* We don't do anything special for diffuse surfaces because the principle bsdf
* does not care about energy conservation of the specular layer for dielectrics. */
return FssEss + Fms * Ems;
}
/* GGX */
float D_ggx_opti(float NH, float a2)
{
float tmp = (NH * a2 - NH) * NH + 1.0;
return M_PI * tmp * tmp; /* Doing RCP and mul a2 at the end */
}
float G1_Smith_GGX_opti(float NX, float a2)
{
/* Using Brian Karis approach and refactoring by NX/NX
* this way the (2*NL)*(2*NV) in G = G1(V) * G1(L) gets canceled by the brdf denominator 4*NL*NV
* Rcp is done on the whole G later
* Note that this is not convenient for the transmission formula */
return NX + sqrt(NX * (NX - NX * a2) + a2);
/* return 2 / (1 + sqrt(1 + a2 * (1 - NX*NX) / (NX*NX) ) ); /* Reference function */
}
float bsdf_ggx(vec3 N, vec3 L, vec3 V, float roughness)
{
float a = roughness;
float a2 = a * a;
vec3 H = normalize(L + V);
float NH = max(dot(N, H), 1e-8);
float NL = max(dot(N, L), 1e-8);
float NV = max(dot(N, V), 1e-8);
float G = G1_Smith_GGX_opti(NV, a2) * G1_Smith_GGX_opti(NL, a2); /* Doing RCP at the end */
float D = D_ggx_opti(NH, a2);
/* Denominator is canceled by G1_Smith */
/* bsdf = D * G / (4.0 * NL * NV); /* Reference function */
return NL * a2 / (D * G); /* NL to Fit cycles Equation : line. 345 in bsdf_microfacet.h */
}
void accumulate_light(vec3 light, float fac, inout vec4 accum)
{
accum += vec4(light, 1.0) * min(fac, (1.0 - accum.a));
}
/* Same thing as Cycles without the comments to make it shorter. */
vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N)
{
vec3 R = -reflect(I, N);
/* Reflection rays may always be at least as shallow as the incoming ray. */
float threshold = min(0.9 * dot(Ng, I), 0.025);
if (dot(Ng, R) >= threshold) {
return N;
}
float NdotNg = dot(N, Ng);
vec3 X = normalize(N - NdotNg * Ng);
float Ix = dot(I, X), Iz = dot(I, Ng);
float Ix2 = sqr(Ix), Iz2 = sqr(Iz);
float a = Ix2 + Iz2;
float b = sqrt(Ix2 * (a - sqr(threshold)));
float c = Iz * threshold + a;
float fac = 0.5 / a;
float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c);
bool valid1 = (N1_z2 > 1e-5) && (N1_z2 <= (1.0 + 1e-5));
bool valid2 = (N2_z2 > 1e-5) && (N2_z2 <= (1.0 + 1e-5));
vec2 N_new;
if (valid1 && valid2) {
/* If both are possible, do the expensive reflection-based check. */
vec2 N1 = vec2(safe_sqrt(1.0 - N1_z2), safe_sqrt(N1_z2));
vec2 N2 = vec2(safe_sqrt(1.0 - N2_z2), safe_sqrt(N2_z2));
float R1 = 2.0 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz;
float R2 = 2.0 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz;
valid1 = (R1 >= 1e-5);
valid2 = (R2 >= 1e-5);
if (valid1 && valid2) {
N_new = (R1 < R2) ? N1 : N2;
}
else {
N_new = (R1 > R2) ? N1 : N2;
}
}
else if (valid1 || valid2) {
float Nz2 = valid1 ? N1_z2 : N2_z2;
N_new = vec2(safe_sqrt(1.0 - Nz2), safe_sqrt(Nz2));
}
else {
return Ng;
}
return N_new.x * X + N_new.y * Ng;
}
/* ----------- Cone angle Approximation --------- */
/* Return a fitted cone angle given the input roughness */
float cone_cosine(float r)
{
/* Using phong gloss
* roughness = sqrt(2/(gloss+2)) */
float gloss = -2 + 2 / (r * r);
/* Drobot 2014 in GPUPro5 */
// return cos(2.0 * sqrt(2.0 / (gloss + 2)));
/* Uludag 2014 in GPUPro5 */
// return pow(0.244, 1 / (gloss + 1));
/* Jimenez 2016 in Practical Realtime Strategies for Accurate Indirect Occlusion. */
return exp2(-3.32193 * r * r);
}

View File

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

View File

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

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