1
1

Compare commits

...

353 Commits

Author SHA1 Message Date
b7d224b543 Viewport Compositor: Add missing shader builder stub
This patch adds a stub that was missed during the previous merge.
2022-02-18 11:59:11 +02:00
b3a11b6584 Merge branch 'eevee-rewrite' into viewport-compositor 2022-02-18 09:37:49 +02:00
311f8ba794 DRW: Add null resource check instead of crashing 2022-02-09 11:34:54 +01:00
af82d0fec7 GPUCodegen: Fix attribute having GPU_NONE gputype 2022-02-09 11:34:54 +01:00
dc9e8a4075 EEVEE: Split directional light path
This should reduce VGPR now that the iterations are separated
2022-02-09 11:34:54 +01:00
da0a8e384e EEVEE: Fix shader not static 2022-02-09 11:34:54 +01:00
d73152b985 EEVEE: Disable raytracing 2022-02-09 11:34:54 +01:00
a9b0728bd4 EEVEE: Remove integer division 2022-02-09 11:34:54 +01:00
4bcc62a257 EEVEE: Fix light culling light count and padd culling tile buffer 2022-02-09 11:34:54 +01:00
89bef5adb2 GPUCodegen: Fix missing mat4 in function arguments 2022-02-09 11:34:54 +01:00
a02eee7e98 DRW: Add new draw_debug_print_lib.glsl
This lib allows any shader to use `print()` like functions for
logging and debugging shaders.

Usage is described in the comment at the top of the file.
2022-02-09 11:34:54 +01:00
4205629ea2 GLShader: Fix two small issues with compute shader generated code 2022-02-09 11:34:54 +01:00
dd6ee49d05 DRW: Add support for GPUStorageBuf in wrappers 2022-02-09 11:34:54 +01:00
9c36da1c34 DRW: Add support for GPUStorageBuf 2022-02-09 11:34:54 +01:00
4477ac7c8a GPU/GL: Add StorageBuf implementation
Almost 1:1 identical to UniformBuf implementation.
2022-02-08 23:17:31 +01:00
8a9f18d72c GPUShader: Fix crash when compilation error is a linking error 2022-02-08 23:15:52 +01:00
9d53e0cb07 EEVEE: Fix more float4x4 issues 2022-02-06 17:04:11 +01:00
e654975b1e EEVEE: Fix assert caused by division by 0 2022-02-06 17:02:10 +01:00
74a0a85181 GLDebug: Wrap glDispatch function into our debug wrappers 2022-02-06 17:01:10 +01:00
d02a3b4aeb EEVEE: Fix uninitialized texture issue.
This was happending on amdgpu-pro driver. `mix(a,b,1)` does not
garantee `b`.
2022-02-06 16:39:30 +01:00
de6d0db833 Merge branch 'master' into eevee-rewrite 2022-02-06 13:51:44 +01:00
6ebe1652d6 Merge branch 'master' into eevee-rewrite 2022-02-06 01:41:18 +01:00
9d44753ab8 EEVEE: Fix missing gpu_Layer in probe filter 2022-02-06 01:23:37 +01:00
03be5c4140 EEVEE: Fix dependencies to draw_view 2022-02-06 01:23:12 +01:00
f7cbd80bd9 EEVEE: Fix frustum_planes/corners references 2022-02-06 01:22:39 +01:00
3b812973fa GPUShaderCreateInfo: Add duplication error checking of dependency merging
As of now we do not allow additional infos duplication. We could in the future
but for now assert that this is not the case.
2022-02-06 01:21:07 +01:00
112c4345a3 DRW: Add preprocessor error if including common_view_lib.glsl without draw_view
This avoid making include mistake and potentially detect areas that do
not really need common_view_lib.glsl.
2022-02-06 01:18:56 +01:00
a3f79499b2 EEVEE: Fix float4x4 usage 2022-02-06 01:16:32 +01:00
26f413d1f8 BLI_float4x4: Add << operator and add ATTR_WARN_UNUSED_RESULT to identity() 2022-02-06 01:16:10 +01:00
ae024ca446 Merge branch 'master' into eevee-rewrite 2022-02-05 23:09:49 +01:00
6f531aba94 GPU: Debug: Avoid double printing of compilation issues
To avoid that, we simply filter using a debug group.
2022-02-05 23:07:53 +01:00
5c9ce9d066 DRW: Make use of shader shared header 2022-02-05 22:58:32 +01:00
29824c6ed3 Merge branch 'master' into eevee-rewrite
# Conflicts:
#	source/blender/gpu/CMakeLists.txt
2022-02-05 22:46:04 +01:00
e3027906d7 Merge branch 'master' into eevee-rewrite 2022-02-05 22:19:07 +01:00
ee6fafc615 GPUTexture: Fix missing cases in to_data_format() 2022-02-05 19:26:07 +01:00
94b35f9e9d GPU: Enable CLOG for gpu when --debug-gpu option is set
This is because all of the debug printing is done through CLog now. Without
it the is little point in this option.
2022-02-05 19:25:35 +01:00
ac9dd50384 GLShaderInterface: Fix missing in builtin SSBO support 2022-02-05 19:23:06 +01:00
a9fa50b6fb GL: Fix compute shader label error 2022-02-05 18:51:39 +01:00
b253532fa7 EEVEE: Fix missing resource binding 2022-02-05 18:51:11 +01:00
e58b397a2a EEVEE: Fix compilation on amdgpu-pro
Implementation only supports global scope shared variables and does not
like having the restrict qualifier on function inputs.
2022-02-05 18:48:28 +01:00
7dff6a074b Merge branch 'master' into eevee-rewrite 2022-02-05 14:00:04 +01:00
af49f0022a Codegen: Fix memory leak 2022-02-05 13:58:57 +01:00
9a31f13298 Merge branch 'master' into eevee-rewrite 2022-02-05 13:30:05 +01:00
b7e2e075c4 Cleanup: EEVEE: Remove unecessary internal C interface. 2022-02-05 13:27:53 +01:00
2f9a7d536d GLShaderInterface: Fix SSBO using the ubo mask
This might head lead to a crash when a shader uses both ubo and ssbo.
2022-02-05 13:22:30 +01:00
4717151495 EEVEE: Fix some broken renaming 2022-02-05 00:06:08 +01:00
031181a6b5 Merge branch 'master' into eevee-rewrite
# Conflicts:
#	source/blender/gpu/intern/gpu_shader_dependency.cc
2022-02-04 23:48:23 +01:00
df349a2214 Merge branch 'master' into eevee-rewrite
# Conflicts:
#	source/blender/draw/intern/DRW_render.h
#	source/blender/draw/intern/draw_manager_data.c
2022-02-04 19:41:36 +01:00
792e3fc6ed GPU: Rewrite the function library system
Instead of using a manual list of dependency, the new implementation
scans all shader files beginning by `gpu_shader_material_` and extract
all function declarations.

This way we can deduce the internal dependencies between theses files.

This new implementation is merged with the manual pragma dependency system
uses by other shader files. This way it is compatible with the shader
logging system and does not require any string duplication during shader
building.
2022-02-04 15:47:03 +01:00
86ef6da834 GPUCodegen: Remove use of gpu_data_type_to_string 2022-02-04 15:20:26 +01:00
314f5b20d5 EEVEE: Fix more shader dependencie issues 2022-02-04 15:18:16 +01:00
b2b1c0102c GPUShader: Add optionnal dependency display in
This introduce `DEBUG_DEPENDENCIES` (not a cmake flag but a local define)
 which when set to 1, will list all the original files included in this
shader while omitting the generated / non original code.
2022-02-04 15:16:10 +01:00
6f16ca39a2 GPUShader: Remove some non useful error lines from log 2022-02-04 15:02:30 +01:00
31133d22b6 GPUShader: Fix issue when error row is 0
It would display the whole file instead of only the line.
2022-02-04 15:01:50 +01:00
b2d3960dc1 GLShader: Fix warning about ARB_conservative_depth even if the GL version supports it 2022-02-04 15:00:43 +01:00
bcd635e549 GPUShader: Improve error logging to support source:row layout
This is the layout used by the amdgpu-pro GL implementation.
This also add some sanitizing of the parse output because the bespoke
implementation has bogus error when it comes to compute shaders.
2022-02-04 14:59:31 +01:00
8af5e2d246 EEVEE: Fix more shader issues 2022-02-03 18:29:55 +01:00
c785a8e166 GPUShader: Avoid double typedef source inclusion 2022-02-03 18:28:45 +01:00
f6d1ba8235 EEVEE: Fix missing weight socket on multiple nodes 2022-02-03 18:18:28 +01:00
78e9d829de EEVEE: Fix shader compilations issues, resource bindings & naming 2022-02-03 13:45:41 +01:00
45885eaea3 GPUShader: Add better error message in case of incorrect dependency name 2022-02-03 12:55:58 +01:00
249717f394 GPUShaderLog: Add filename before error to improve debugging
This displays the error source such as IDE can find identify them
as path and let the programmer follow the direct link instead of
manual string search.

This only works for shaders compiling from unaltered sources as
it uses the source `char*` as key for filename search.
2022-02-03 12:44:32 +01:00
5b02f1c031 Cleanup: GPUShader: Avoid casting 2022-02-03 12:42:13 +01:00
e3f475c17c GPUTexture: Fix missing cases in validate_data_format 2022-02-03 12:30:03 +01:00
bc2e5845de Cleanup: GPUShader: Remove shader source duplication 2022-02-03 00:58:00 +01:00
e5dcd91755 DRW: Fix heatmap_gradient function messing error line number
For some reason on some GL implementation (amdgpu) this particular
syntaxes shift the error lines.

Remove the context lines by default as they are not useful anymore.
2022-02-03 00:24:30 +01:00
0ef24bb0e2 Cleanup: EEVEE: Naming 2022-02-02 23:47:38 +01:00
1b2eeb9c64 Cleanup: GPUShader: Reduce copy paste 2022-02-02 23:33:50 +01:00
0f964142b9 GPUShaderCreateInfo: Add basic debug print support 2022-02-02 22:14:44 +01:00
a0c8c932fb Cleanup: common_hair_lib.glsl: deduplicate functions definitions 2022-02-02 22:12:45 +01:00
e167c1cb89 EEVEE: Add material shader tests in the form of unused createInfos
This adds basic variations check at compile time in debug mode.
2022-02-02 22:11:44 +01:00
e9fe318e8e EEVEE: Fix more shader compilation issues 2022-02-02 22:11:26 +01:00
8a20e3f229 GPUShaderCreateInfo: Add automatic resource location
Turning this option on will recreate the slot number making sure
no slot overlaps.
2022-02-02 22:08:02 +01:00
664d31d40a Cleanup: Remove void argument to functions 2022-02-02 22:07:01 +01:00
088fc410d9 EEVEE: Finish implementing GPUShaderCreateInfo and fix compilation 2022-02-02 19:01:37 +01:00
6f3a119464 DRW: Add draw_gpencil info for gpencil geom type 2022-02-02 18:40:40 +01:00
7073959d48 GLShader: Add array macro
This is to support different array syntax
2022-02-02 18:38:16 +01:00
76a4fb223e GPUShaderCreateInfo: Fix strange crash when using std::cerr 2022-02-02 18:37:35 +01:00
a9a045fd2d GLShader: Fix image declaration 2022-02-02 18:37:03 +01:00
f39acfc874 GPUCodegen: Port to use shader create info
We now use ShaderCreateInfo as a way to setup the custom material
implementation.
This is more versatile and flexible while not require parsing of
snippets of code.
2022-02-02 18:36:50 +01:00
484c97707f Fix clang-tidy 2022-02-02 18:32:19 +01:00
81502a99b5 EEVEE: Add back support of raytrace_resolve_lib.glsl 2022-02-02 17:53:08 +01:00
9c6ceb4503 fix merge 2022-02-01 19:49:51 +01:00
08439aebf1 EEVEE: Use GPUShaderCreateInfo 2022-02-01 19:49:45 +01:00
4fd26cdd37 Merge branch 'master' into eevee-rewrite
# Conflicts:
#	source/blender/draw/engines/eevee/eevee_depth_of_field.c
#	source/blender/draw/engines/eevee/eevee_lightprobes.c
#	source/blender/draw/engines/eevee/eevee_render.c
#	source/blender/draw/engines/eevee/eevee_subsurface.c
#	source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
#	source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl
#	source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.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_gtao_frag.glsl
#	source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl
#	source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
#	source/blender/draw/intern/draw_manager_shader.c
#	source/blender/gpu/intern/gpu_codegen.c
#	source/blender/gpu/intern/gpu_shader_log.cc
#	source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl
#	source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
#	source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl
#	source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
2022-02-01 19:38:54 +01:00
97c482ca42 Merge branch 'eevee-rewrite' into viewport-compositor 2022-01-31 20:06:04 +02:00
f7b03a7906 Merge branch 'master' into eevee-rewrite 2022-01-27 22:57:26 +01:00
39c7e19d43 Fix missing glsl file in cmake file 2022-01-27 22:55:15 +01:00
1f503d2ef5 Fix linker error on gpu_shader_builder.cc 2022-01-27 22:43:58 +01:00
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
610b67e13b Viewport Compositor: Average RGB for link conversion
This patch changes the link conversion operation for the compositor
shader to be an RGB average to match the Blender compositor. Compositor
shaders are now marked with a COMPOSITOR_SHADER definition.
2022-01-06 18:19:50 +02:00
65272b80e4 Viewport Compositor: Add a UBO for the compositor
This patch adds a new dedicated UBO for compositor shaders. The
FrameNumber UBO member was moved to the new UBO. Additionally, the OCIO
luminance coefficients were added to the UBO for utilization by various
compositor operations.
2022-01-06 18:19:12 +02:00
25b95e277f Viewport Compositor: Port Time node
This patch ports the Time node to the viewport compositor. The current
frame number was added to the View UBO in order to be accessed by the
shader.
2021-12-28 20:29:53 +02:00
422a556eac Viewport Compositor: Port Luminance Key node
This patch ports the Luminance Key node to the viewport compositor. The
shader is a straightforward port of the compositor code.
2021-12-28 20:29:44 +02:00
9c3b42a188 Viewport Compositor: Port Distance Key node
This patch ports the Distance Key node to the viewport compositor. The
shader is a straightforward port of the compositor code.
2021-12-28 20:26:38 +02:00
06fd4c3617 Viewport Compositor: Port Difference Key node
This patch ports the Difference Key node to the viewport compositor. The
shader is a straightforward port of the compositor code.
2021-12-28 20:26:33 +02:00
f18c18ae52 Viewport Compositor: Port Color Spill node
This patch ports the Color Spill node to the viewport compositor. The
shader is a straightforward port of the compositor code.
2021-12-28 20:24:31 +02:00
4516e86668 Viewport Compositor: Port Color Key node
This patch ports the Color Key node to the viewport compositor. The
shader is a straightforward port of the compositor code.
2021-12-28 20:24:31 +02:00
782cc8a795 Viewport Compositor: Port Chroma Key node
This patch ports the Chroma Key node to the viewport compositor. The
shader is a straightforward port of the compositor code.
2021-12-28 20:24:25 +02:00
c38aebbafb Viewport Compositor: Port Channel Key node
This patch ports the Channel Key node to the viewport compositor. The
shader is a straightforward port of the compositor code.
2021-12-28 19:49:18 +02:00
df192657da Viewport Compositor: Port Ellipse Mask node
This patch ports the Ellipse Mask node to the viewport compositor.
2021-12-28 19:49:18 +02:00
a3c16b6279 Viewport Compositor: Port Box Mask node
This patch ports the Box Mask node to the viewport compositor.
2021-12-28 19:49:18 +02:00
fea726180d Viewport Compositor: Port Split Viewer node
This patch ports the Split Viewer node to the viewport compositor.
2021-12-28 19:49:18 +02:00
1d4230e92a Viewport Compositor: Port Value node
This patch ports the Value node to the viewport compositor.
2021-12-28 19:49:18 +02:00
72ea3f954c Viewport Compositor: Port RGB node
This patch ports the RGB node to the viewport compositor.
2021-12-28 19:49:18 +02:00
21b83a7e8c Viewport Compositor: Port Vector Curves node
This patch ports the Vector Curves node to the viewport compositor. A
variant of the existing vector_curves was added to work without mixing
and the inputs were reduced to vec3 because vec4 are superfluous.
2021-12-28 19:49:17 +02:00
4d688e7393 Viewport Compositor: Port Normal node
This patch ports the Normal node to the viewport compositor. The shader
is a straightforward port of the compositor code.
2021-12-28 19:47:52 +02:00
289852dfb5 Viewport Compositor: Port Map Value node
This patch ports the Map Value node to the viewport compositor. The
shader is a straightforward port of the compositor code.
2021-12-28 19:47:52 +02:00
6f119700ff Viewport Compositor: Port Map Range node
This patch ports the Map Range node to the viewport compositor. The
shader is a straightforward port of the compositor code.
2021-12-28 19:47:52 +02:00
03775f2b92 Viewport Compositor: Port Set Alpha node
This patch ports the Set Alpha node to the viewport compositor. The
shader is a straightforward port of the compositor code.
2021-12-28 19:47:52 +02:00
79fe3e14ac Viewport Compositor: Port RGB To BW node
This patch ports the RGB To BW node to the viewport compositor. The
shader is a straightforward port of the compositor code.
2021-12-28 19:47:52 +02:00
ae7833d9da Viewport Compositor: Port Math node
This patch ports the Math node to the viewport compositor. The shading
math shader was moved into a common directory to be used by both
materials and the compositor.
2021-12-28 19:47:52 +02:00
5ca6124207 Viewport Compositor: Port Separate/Combine nodes
This patch ports the Separate/Combine nodes to the viewport compositor.
The shader is a straightforward port of the compositor code.
2021-12-28 19:47:52 +02:00
5ed9bb563e Viewport Compositor: Port Color Ramp node
This patch ports the Color Ramp node to the viewport compositor. The
shading color ramp shader was moved into a common directory to be used
by both materials and the compositor.
2021-12-28 19:47:52 +02:00
4d3e44108e Viewport Compositor: Port Convert Alpha node
This patch ports the Convert Alpha node to the viewport compositor. The
shader is a straightforward port of the compositor code.
2021-12-28 19:47:52 +02:00
a3be79303f Viewport Compositor: Complete Alpha Over node
This patch adds the missing premultiplication handling for the Alpha
Over node. The shader is a straightforward port of the compositor code.
2021-12-28 19:47:52 +02:00
61a3fad7aa Viewport Compositor: Port RGB Curves node
This patch ports the RGB Curves node to the viewport compositor. The
curves code was mostly rewritten in a common directory to be used by
both the material and compositor nodes. The new code avoids code
duplication by moving common code into BKE curve mapping functions. It
also avoids ambiguous data embedding into gradient vectors and avoids
redundancies. Finally, a film-like implementation was added.
2021-12-28 19:47:46 +02:00
37593cdf6d Viewport Compositor: Port Posterize node
This patch ports the Posterize node to the viewport compositor. The
shader is a straightforward port of the compositor code.
2021-12-28 19:21:26 +02:00
88494adecb Viewport Compositor: Port Mix RGB node
This patch ports the Mix RGB node to the viewport compositor. The
material Mix RGB node code was moved into a common directory to be
utilized by both the material and the compositor nodes. Additionally,
some of the operations were adapted to work with the compositor, in
particular, the linear and soft light operations now write the alpha to
the result, this has no effect on materials but is consistent with the
compositor.
2021-12-28 19:14:13 +02:00
9d0700196b Viewport Compositor: Port Hue Saturation Value node
This patch ports the Hue Saturation Value node to the viewport
compositor. The shader is a straightforward port of the compositor code.
2021-12-28 19:11:57 +02:00
88907357c2 Viewport Compositor: Port Hue Correct node
This patch ports the Hue Correct node to the viewport compositor. The
shader is a straightforward port of the compositor code.
2021-12-28 19:11:48 +02:00
8af0e52921 Viewport Compositor: Port Gamma node
This patch ports the Gamma node to the viewport compositor. The shader
is a straightforward port of the compositor code.
2021-12-28 18:32:46 +02:00
8a4f1f99ad Viewport Compositor: Port Exposure node
This patch ports the Exposure node to the viewport compositor. The
shader is a straightforward port of the compositor code.
2021-12-28 18:32:46 +02:00
506ad72c66 Viewport Compositor: Port Color Correction node
This patch ports the Color Correction node to the viewport compositor.
The shader is a straightforward port of the compositor code. A function
to return the luminance coefficients from the color management
configuration was added to pass the coefficients to the shader.
2021-12-28 18:32:39 +02:00
5ddddee07b Viewport Compositor: Port Color Balance node
This patch ports the Color Balance node to the viewport compositor. The
shader is a straightforward port of the compositor code. A few utilities
were added to ease implementation.
2021-12-28 18:27:58 +02:00
c85eb8fc7c Viewport Compositor: Port Bright And Contrast node
This patch ports the Bright And Contrast node to the viewport
compositor. The shader is a straightforward port of the compositor code.
The (un)premultiply_alpha functions were adjusted to retain the original
alpha for compatibility with the compositor. This has no effect on
materials because alpha is implicitly discarded.
2021-12-28 18:27:34 +02: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
090432e416 GPU: Move shader utils into a common directory
This patches moves the math, color, and hash utils shader libraries into
a common directory to be used by both the compositor and materials.
2021-11-06 22:25:05 +02: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
85dc7288e4 Viewport Compositor: Fix compositing multiple viewlayers from one scene
All layers were not correctly rendered or using the right index.
2021-09-30 20:40:38 +02:00
4affe3215b Viewport Compositor: Improve update mechanism
Now viewport updates fully through DRWRenderScene even if compositor
is disabled.

External RenderEngine now have the correct depsgraph during update.
2021-09-30 19:21:47 +02:00
2ac637eef0 Viewport Compositor: Move rv3d->render_engine to DRWRenderScene
This allows multiple instances of external render engines per viewport.
Allowing them to be combined by the compositor.

Many things needed to be ported to the draw manager since it is the only
one that can know what is inside the `DRWRenderScene` and can iterate
over all running engines.
2021-09-30 12:05:53 +02:00
84bba36429 Viewport Compositor: Fix memory leak after rendering multi-scenes
This was caused by the per view `draw_view` not being freed correctly.

Fixing this also caused issue because the `draw_view` would keep
ownership of the renderbuffer and would free it a second time.

Moving all renderbuffers ownership to `draw_view` for now.
2021-09-29 19:53:28 +02:00
56b601e3a0 Merge branch 'eevee-rewrite' into viewport-compositor 2021-09-29 18:11:20 +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
e7c2e19096 Merge branch 'eevee-rewrite' into viewport-compositor
# Conflicts:
#	source/blender/blenkernel/BKE_scene.h
#	source/blender/blenkernel/intern/scene.c
#	source/blender/draw/intern/draw_manager.c
#	source/blender/draw/intern/draw_manager.h
#	source/blender/nodes/composite/nodes/node_composite_composite.cc
2021-09-29 17:58:31 +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
f466a0f1df Viewport Compositor: Add per view_data per DRWRenderView
This way we have correct handling of engine data. This fix issue
with infinite redraw and AA never converging.
2021-09-28 22:29:46 +02:00
1025ee77ed Viewport Compositor: Fix render layer sampler name colision and leak 2021-09-28 22:28:58 +02:00
ad19f7d558 Viewport Compositor: Separate the compositor checkbox to its own tab
This is to make is visible under all engines.
2021-09-28 15:03:22 +02:00
9412113834 Viewport Compositor: Support multiple render layers
This adds the new DRWRenderScene structure (and its sub structures) that
contains all the needed render passes for each scene present in the
compositor nodetree.

The scenes are rendered using a special option to avoid rendering overlays.

The render layer input to the GPUMaterial are now a separate structure and
a separate list of input handled by the compositor engine.

Rendering all scenes is the first thing done to avoir much trouble with

There are still issues like continuous rendering of TAA because the same
DRWData is used for all scenes.
2021-09-28 15:03:22 +02:00
405f8edf02 Viewport Compositor: Support alpha over factor 2021-09-27 17:20:32 +02:00
a0826667f1 Viewport Compositor: Add DEG eval callback and redraw listener
This is pretty basic but it gets the job done. This may change in
the future.

The `NodeType` (SHADING) for the `OperationKey` is not ideal.

Also it seems the tagging comming from the nodetree tag everything
as COPY_ON_WRITE update. Which is slow. To investigate.
2021-09-16 11:42:27 +02:00
58e9a40d5f Viewport Compositor: Add basic alphaOver node support
Not all feature are supported. For now expect both inputs to be
"premultiplied".
2021-09-16 00:14:14 +02:00
dfb14c1e9f Viewport Compositor: Small fixes
Fix memory leak, a crash when resizing and wrong texture coordinate
in camera mode.

Change render layer sampler name to allow other texture to be bound.
2021-09-16 00:13:30 +02:00
dd51d5acd4 Merge branch 'eevee-rewrite' into viewport-compositor 2021-09-15 17:16:33 +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
ae22642690 Viewport Compositor: Initial commit
This introduce a new compositor engine. It applies the compositor
nodetree onto the render result in the viewport using GLSL shader.

For now it only very few nodes are supported and only the combined
pass is passed to the evaluation pass.

This reuse almost the same pipeline as `GPUMaterial`.
2021-09-15 13:51:00 +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
edfeca758c DRW: Add barebone realtime compositor engine
This is just the initial commit. Compositor just clears the framebuffer to
red.
2021-09-12 20:41:58 +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
574 changed files with 34897 additions and 31162 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

@@ -5989,6 +5989,26 @@ class VIEW3D_PT_shading_render_pass(Panel):
layout.prop(shading, "render_pass", text="")
class VIEW3D_PT_shading_compositor(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
bl_label = "Compositor"
bl_parent_id = 'VIEW3D_PT_shading'
@classmethod
def poll(cls, context):
return (
(context.space_data.shading.type == 'MATERIAL') or
(context.space_data.shading.type == 'RENDERED')
)
def draw(self, context):
shading = context.space_data.shading
layout = self.layout
layout.prop(shading, "use_compositor")
class VIEW3D_PT_gizmo_display(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
@@ -7715,6 +7735,7 @@ classes = (
VIEW3D_PT_shading_options_shadow,
VIEW3D_PT_shading_options_ssao,
VIEW3D_PT_shading_render_pass,
VIEW3D_PT_shading_compositor,
VIEW3D_PT_gizmo_display,
VIEW3D_PT_overlay,
VIEW3D_PT_overlay_guides,

View File

@@ -144,6 +144,14 @@ void BKE_curvemapping_evaluate_premulRGBF(const struct CurveMapping *cumap,
bool BKE_curvemapping_RGBA_does_something(const struct CurveMapping *cumap);
void BKE_curvemapping_table_F(const struct CurveMapping *cumap, float **array, int *size);
void BKE_curvemapping_table_RGBA(const struct CurveMapping *cumap, float **array, int *size);
void BKE_curvemapping_get_range_minimums(const struct CurveMapping *curve_mapping,
float minimums[4]);
void BKE_curvemapping_compute_range_dividers(const struct CurveMapping *curve_mapping,
float dividers[4]);
void BKE_curvemapping_compute_slopes(const struct CurveMapping *curve_mapping,
float start_slopes[4],
float end_slopes[4]);
bool BKE_curvemapping_is_map_identity(const struct CurveMapping *curve_mapping, int index);
/**
* Call when you do images etc, needs restore too. also verifies tables.

View File

@@ -368,6 +368,10 @@ void BKE_scene_cursor_from_mat4(struct View3DCursor *cursor,
const float mat[4][4],
bool use_compat);
/* Dependency graph evaluation. */
void BKE_scene_eval_compositor_nodetree(struct Depsgraph *depsgraph, struct Scene *scene);
#ifdef __cplusplus
}
#endif

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

@@ -1174,6 +1174,98 @@ bool BKE_curvemapping_RGBA_does_something(const CurveMapping *cumap)
return false;
}
/* Get the minimum x value of each curve map table. */
void BKE_curvemapping_get_range_minimums(const CurveMapping *curve_mapping, float minimums[CM_TOT])
{
for (int i = 0; i < CM_TOT; i++) {
minimums[i] = curve_mapping->cm[i].mintable;
}
}
/* Get the reciprocal of the difference between the maximum and the minimum x value of each curve
* map table. Evaluation parameters can be multiplied by this value to be normalized. If the
* difference is zero, 1^8 is returned. */
void BKE_curvemapping_compute_range_dividers(const CurveMapping *curve_mapping,
float dividers[CM_TOT])
{
for (int i = 0; i < CM_TOT; i++) {
const CurveMap *curve_map = &curve_mapping->cm[i];
dividers[i] = 1.0f / max_ff(1e-8f, curve_map->maxtable - curve_map->mintable);
}
}
/* Compute the slopes at the start and end points of each curve map. The slopes are multiplied by
* the range of the curve map to compensate for parameter normalization. If the slope is vertical,
* 1^8 is returned. */
void BKE_curvemapping_compute_slopes(const CurveMapping *curve_mapping,
float start_slopes[CM_TOT],
float end_slopes[CM_TOT])
{
float range_dividers[CM_TOT];
BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
for (int i = 0; i < CM_TOT; i++) {
const CurveMap *curve_map = &curve_mapping->cm[i];
/* If extrapolation is not enabled, the slopes are horizontal. */
if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) {
start_slopes[i] = 0.0f;
end_slopes[i] = 0.0f;
continue;
}
if (curve_map->ext_in[0] != 0.0f) {
start_slopes[i] = curve_map->ext_in[1] / (curve_map->ext_in[0] * range_dividers[i]);
}
else {
start_slopes[i] = 1e8f;
}
if (curve_map->ext_out[0] != 0.0f) {
end_slopes[i] = curve_map->ext_out[1] / (curve_map->ext_out[0] * range_dividers[i]);
}
else {
end_slopes[i] = 1e8f;
}
}
}
/* Check if the curve map at the index is identity, that is, does nothing. A curve map is said to
* be identity if:
* - The curve mapping uses extrapolation.
* - Its range is 1.
* - The slope at its start point is 1.
* - The slope at its end point is 1.
* - The number of points is 2.
* - The start point is at (0, 0).
* - The end point is at (1, 1).
* Note that this could return false even if the curve map is identity, this happens in the case
* when more than 2 points exist in the curve map but all points are collinear. */
bool BKE_curvemapping_is_map_identity(const CurveMapping *curve_mapping, int index)
{
if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) {
return false;
}
const CurveMap *curve_map = &curve_mapping->cm[index];
if (curve_map->maxtable - curve_map->mintable != 1.0f) {
return false;
}
if (curve_map->ext_in[0] != curve_map->ext_in[1]) {
return false;
}
if (curve_map->ext_out[0] != curve_map->ext_out[1]) {
return false;
}
if (curve_map->totpoint != 2) {
return false;
}
if (curve_map->curve[0].x != 0 || curve_map->curve[0].y != 0) {
return false;
}
if (curve_map->curve[1].x != 0 || curve_map->curve[1].y != 0) {
return false;
}
return true;
}
void BKE_curvemapping_init(CurveMapping *cumap)
{
int a;

View File

@@ -127,6 +127,8 @@
#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
#include "GPU_material.h"
#include "bmesh.h"
static void scene_init_data(ID *id)
@@ -364,6 +366,9 @@ static void scene_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
scene_dst->preview = NULL;
}
BLI_listbase_clear(&scene_dst->gpumaterial);
BLI_listbase_clear(&scene_dst->compositor_depsgraphs);
BKE_scene_copy_data_eevee(scene_dst, scene_src);
}
@@ -388,6 +393,9 @@ static void scene_free_data(ID *id)
BKE_keyingsets_free(&scene->keyingsets);
/* Free gpu material before the ntree */
GPU_material_free(&scene->gpumaterial);
/* is no lib link block, but scene extension */
if (scene->nodetree) {
ntreeFreeEmbeddedTree(scene->nodetree);
@@ -462,6 +470,11 @@ static void scene_free_data(ID *id)
scene->display.shading.prop = NULL;
}
LISTBASE_FOREACH_MUTABLE (LinkData *, link, &scene->compositor_depsgraphs) {
DEG_graph_free((Depsgraph *)link->data);
}
BLI_freelistN(&scene->compositor_depsgraphs);
/* These are freed on doversion. */
BLI_assert(scene->layer_properties == NULL);
}
@@ -937,6 +950,8 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres
if (BLO_write_is_undo(writer)) {
/* Clean up, important in undo case to reduce false detection of changed data-blocks. */
BLI_listbase_clear(&sce->gpumaterial);
BLI_listbase_clear(&sce->compositor_depsgraphs);
/* XXX This UI data should not be stored in Scene at all... */
memset(&sce->cursor, 0, sizeof(sce->cursor));
}
@@ -1367,6 +1382,9 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
BKE_screen_view3d_shading_blend_read_data(reader, &sce->display.shading);
BLI_listbase_clear(&sce->gpumaterial);
BLI_listbase_clear(&sce->compositor_depsgraphs);
BLO_read_data_address(reader, &sce->layer_properties);
IDP_BlendDataRead(reader, &sce->layer_properties);
}
@@ -3666,3 +3684,14 @@ void BKE_scene_cursor_from_mat4(View3DCursor *cursor, const float mat[4][4], boo
}
/** \} */
void BKE_scene_eval_compositor_nodetree(struct Depsgraph *depsgraph, Scene *scene)
{
DEG_debug_print_eval(depsgraph, __func__, scene->id.name, scene);
GPU_material_free(&scene->gpumaterial);
LISTBASE_FOREACH_MUTABLE (LinkData *, link, &scene->compositor_depsgraphs) {
DEG_graph_free((Depsgraph *)link->data);
}
BLI_freelistN(&scene->compositor_depsgraphs);
}

View File

@@ -1417,7 +1417,6 @@ static void direct_link_region(BlendDataReader *reader, ARegion *region, int spa
BLO_read_data_address(reader, &rv3d->localvd);
BLO_read_data_address(reader, &rv3d->clipbb);
rv3d->render_engine = NULL;
rv3d->sms = NULL;
rv3d->smooth_timer = NULL;

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

@@ -90,7 +90,7 @@ struct float4x4 {
return matrix;
}
static float4x4 identity()
static float4x4 identity() ATTR_WARN_UNUSED_RESULT
{
float4x4 mat;
unit_m4(mat.values);
@@ -259,6 +259,16 @@ struct float4x4 {
}
return h;
}
friend std::ostream &operator<<(std::ostream &stream, const float4x4 &m)
{
stream << "(\n";
for (int i = 0; i < 4; i++) {
stream << " " << *reinterpret_cast<const float4 *>(m.values[i]) << std::endl;
}
stream << ")";
return stream;
}
};
} // namespace blender

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

@@ -388,6 +388,14 @@ MINLINE uint divide_ceil_u(uint a, uint b)
return (a + b - 1) / b;
}
/**
* Return the next multiple of `b` after `a`.
*/
MINLINE uint ceil_multiple_u(uint a, uint b)
{
return ((a + b - 1) / b) * b;
}
MINLINE int mod_i(int i, int n)
{
return (i % n + n) % n;

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

@@ -25,6 +25,8 @@
#include "DNA_scene_types.h"
#include "BKE_scene.h"
namespace blender::deg {
void DepsgraphNodeBuilder::build_scene_render(Scene *scene, ViewLayer *view_layer)
@@ -84,6 +86,14 @@ void DepsgraphNodeBuilder::build_scene_compositor(Scene *scene)
if (scene->nodetree == nullptr) {
return;
}
Scene *scene_cow = get_cow_datablock(scene);
add_operation_node(&scene->id,
NodeType::SHADING,
OperationCode::COMPOSITOR_EVAL,
[scene_cow](::Depsgraph *depsgraph) {
BKE_scene_eval_compositor_nodetree(depsgraph, scene_cow);
});
build_nodetree(scene->nodetree);
}

View File

@@ -23,6 +23,7 @@
#include "intern/builder/deg_builder_relations.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
namespace blender::deg {
@@ -72,7 +73,15 @@ void DepsgraphRelationBuilder::build_scene_compositor(Scene *scene)
if (scene->nodetree == nullptr) {
return;
}
build_nodetree(scene->nodetree);
OperationKey scene_key(&scene->id, NodeType::SHADING, OperationCode::COMPOSITOR_EVAL);
OperationKey ntree_key(
&scene->nodetree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT);
add_relation(ntree_key, scene_key, "Compositor's NTree");
build_nested_nodetree(&scene->id, scene->nodetree);
}
} // namespace blender::deg

View File

@@ -202,6 +202,9 @@ const char *operationCodeAsString(OperationCode opcode)
/* Sequencer. */
case OperationCode::SEQUENCES_EVAL:
return "SEQUENCES_EVAL";
/* Sequencer. */
case OperationCode::COMPOSITOR_EVAL:
return "COMPOSITOR_EVAL";
/* instancing/duplication. */
case OperationCode::DUPLI:
return "DUPLI";

View File

@@ -201,9 +201,11 @@ enum class OperationCode {
GENERIC_DATABLOCK_UPDATE,
/* Sequencer. ----------------------------------------------------------- */
SEQUENCES_EVAL,
/* Sequencer. ----------------------------------------------------------- */
COMPOSITOR_EVAL,
/* Duplication/instancing system. --------------------------------------- */
DUPLI,

View File

@@ -52,6 +52,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
@@ -117,35 +136,35 @@ set(SRC
intern/smaa_textures.c
engines/basic/basic_engine.c
engines/basic/basic_shader.c
engines/compositor/compositor_engine.cc
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_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_camera.cc
engines/eevee/eevee_depth_of_field.cc
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
@@ -223,10 +242,14 @@ set(SRC
intern/smaa_textures.h
engines/basic/basic_engine.h
engines/basic/basic_private.h
engines/compositor/compositor_engine.h
engines/eevee/eevee_engine.h
engines/eevee/eevee_lightcache.h
engines/eevee/eevee_camera.hh
engines/eevee/eevee_instance.hh
engines/eevee/eevee_light.hh
engines/eevee/eevee_depth_of_field.hh
engines/eevee/eevee_lut.h
engines/eevee/eevee_private.h
engines/external/external_engine.h
engines/image/image_batches.hh
engines/image/image_drawing_mode.hh
@@ -255,95 +278,115 @@ 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/compositor/shaders/compositor_frag.glsl
engines/compositor/shaders/compositor_lib.glsl
engines/compositor/shaders/compositor_nodetree_eval_lib.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_closure_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_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_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_glossy_frag.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_world_vert.glsl
engines/eevee/shaders/eevee_velocity_lib.glsl
engines/eevee/shaders/eevee_velocity_camera_frag.glsl
engines/eevee/shaders/eevee_velocity_surface_mesh_vert.glsl
engines/eevee/shaders/eevee_velocity_surface_frag.glsl
engines/eevee/shaders/eevee_volume_deferred_frag.glsl
engines/eevee/shaders/eevee_volume_eval_lib.glsl
engines/eevee/shaders/eevee_volume_vert.glsl
engines/eevee/eevee_defines.hh
engines/eevee/eevee_shader_shared.hh
engines/workbench/shaders/workbench_cavity_lib.glsl
engines/workbench/shaders/workbench_common_lib.glsl
@@ -375,19 +418,25 @@ 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_debug_print_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_view_lib.glsl
intern/shaders/common_view_clipping_lib.glsl
intern/shaders/common_subdiv_custom_data_interp_comp.glsl
intern/shaders/common_subdiv_ibo_lines_comp.glsl
@@ -402,6 +451,9 @@ set(GLSL_SRC
intern/shaders/common_subdiv_vbo_lnor_comp.glsl
intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl
intern/shaders/draw_debug_print_display_vert.glsl
intern/shaders/draw_debug_print_display_frag.glsl
intern/draw_shader_shared.h
engines/gpencil/shaders/gpencil_frag.glsl
@@ -530,6 +582,14 @@ foreach(GLSL_FILE ${GLSL_SRC})
data_to_c_simple(${GLSL_FILE} GLSL_C)
endforeach()
# Generated shader files
# TODO(@fclem): Clean that by wrapping it into a function.
data_to_c(${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_lib.glsl ${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_lib.glsl.c GLSL_C)
list(APPEND GLSL_SRC
${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_lib.glsl
)
blender_add_lib(bf_draw_shaders "${GLSL_C}" "" "" "")
list(APPEND LIB

View File

@@ -46,8 +46,10 @@ struct Render;
struct RenderEngine;
struct RenderEngineType;
struct Scene;
struct ScrArea;
struct View3D;
struct ViewLayer;
struct wmWindow;
struct bContext;
struct rcti;
@@ -62,11 +64,13 @@ typedef struct DRWUpdateContext {
struct Depsgraph *depsgraph;
struct Scene *scene;
struct ViewLayer *view_layer;
struct ScrArea *area;
struct ARegion *region;
struct View3D *v3d;
struct RenderEngineType *engine_type;
struct wmWindow *window;
} DRWUpdateContext;
void DRW_notify_view_update(const DRWUpdateContext *update_ctx);
void DRW_notify_view_update(const DRWUpdateContext *update_ctx, bool do_update);
typedef enum eDRWSelectStage {
DRW_SELECT_PASS_PRE = 1,
@@ -222,6 +226,10 @@ void DRW_drawdata_free(struct ID *id);
struct DRWData *DRW_viewport_data_create(void);
void DRW_viewport_data_free(struct DRWData *drw_data);
bool DRW_viewport_has_external_engine(struct GPUViewport *viewport);
void DRW_viewport_free_external_engines(struct GPUViewport *viewport);
bool DRW_viewport_engines_stereo_support(struct GPUViewport *viewport);
void DRW_viewport_tag_redraw(struct GPUViewport *viewport, struct ARegion *region);
bool DRW_opengl_context_release(void);
void DRW_opengl_context_activate(bool drw_state);

View File

@@ -0,0 +1,44 @@
/*
* 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 "BLT_translation.h"
#include "DRW_render.h"
extern "C" {
static const DrawEngineDataSize compositor_data_size = {};
DrawEngineType draw_engine_compositor_type = {
nullptr,
nullptr,
N_("Compositor"),
&compositor_data_size,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
};
}

View File

@@ -0,0 +1,33 @@
/*
* 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 draw_engine
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
extern DrawEngineType draw_engine_compositor_type;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,16 @@
#pragma BLENDER_REQUIRE(compositor_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(compositor_nodetree_eval_lib.glsl)
in vec4 uvcoordsvar;
layout(location = 0) out vec4 out_color;
void main()
{
g_data.uv_render_layer = uvcoordsvar.xy;
g_data.uv_texco = uvcoordsvar.xy * CameraTexCoFactors.xy + CameraTexCoFactors.zw;
out_color = nodetree_composite();
}

View File

@@ -0,0 +1,10 @@
/* Keep in sync with CompositorData in compositor_engine.cc. */
struct CompositorData {
vec3 luminance_coefficients;
float frame_number;
};
layout(std140) uniform compositor_block
{
CompositorData compositor_data;
};

View File

@@ -0,0 +1,20 @@
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
struct GlobalData {
/** UV to sample render layer. Cover the whole viewport. */
vec2 uv_render_layer;
/** UV of the compositing area. Also known as Camera Texture coordinates in material shaders. */
vec2 uv_texco;
};
GlobalData g_data;
void ntree_eval_init()
{
}
void ntree_eval_weights()
{
}

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 = float4x4::identity();
data.viewinv = float4x4::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);
}

View File

@@ -0,0 +1,47 @@
/*
* 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
*
* List of defines that are shared with the GPUShaderCreateInfos. We do this to avoid
* dragging larger headers into the createInfo pipeline which would cause problems.
*/
#pragma once
/* 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
/**
* 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

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_buf", 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_buf", 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_buf", 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_buf", data_);
DRW_shgroup_uniform_block(grp, "sampling_buf", 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_buf", data_);
DRW_shgroup_uniform_block(grp, "sampling_buf", 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_buf", data_);
DRW_shgroup_uniform_block(grp, "sampling_buf", 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_buf", 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_buf", 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_buf", data_);
DRW_shgroup_uniform_block(grp, "sampling_buf", 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

@@ -1,662 +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 "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 "eevee_private.h"
#include "eevee_engine.h" /* own include */
#define EEVEE_ENGINE "BLENDER_EEVEE"
/* *********** FUNCTIONS *********** */
static void eevee_engine_init(void *ved)
{
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();
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__);
}
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);
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_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);
}
void EEVEE_cache_populate(void *vedata, Object *ob)
{
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);
}
}
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);
}
}
static void eevee_engine_free(void)
{
EEVEE_shaders_free();
EEVEE_lightprobes_free();
EEVEE_materials_free();
EEVEE_occlusion_free();
EEVEE_volumes_free();
}
static const DrawEngineDataSize eevee_data_size = DRW_VIEWPORT_DATA_SIZE(EEVEE_Data);
DrawEngineType draw_engine_eevee_type = {
NULL,
NULL,
N_("Eevee"),
&eevee_data_size,
&eevee_engine_init,
&eevee_engine_free,
NULL, /* instance_free */
&eevee_cache_init,
&EEVEE_cache_populate,
&eevee_cache_finish,
&eevee_draw_scene,
&eevee_view_update,
&eevee_id_update,
&eevee_render_to_image,
&eevee_store_metadata,
};
RenderEngineType DRW_engine_viewport_eevee_type = {
NULL,
NULL,
EEVEE_ENGINE,
N_("Eevee"),
RE_INTERNAL | RE_USE_PREVIEW | RE_USE_STEREO_VIEWPORT | RE_USE_GPU_CONTEXT,
NULL,
&DRW_render_to_image,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&EEVEE_render_update_passes,
&draw_engine_eevee_type,
{NULL, NULL, NULL},
};
#undef EEVEE_ENGINE

View File

@@ -0,0 +1,247 @@
/*
* 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_instance.hh"
using namespace blender::eevee;
typedef struct EEVEE_Data {
DrawEngineType *engine_type;
DRWViewportEmptyList *fbl;
DRWViewportEmptyList *txl;
DRWViewportEmptyList *psl;
DRWViewportEmptyList *stl;
Instance *instance;
} EEVEE_Data;
static void eevee_engine_init(void *vedata)
{
EEVEE_Data *ved = (EEVEE_Data *)vedata;
if (ved->instance == NULL) {
ved->instance = new 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];
}
}
}
ved->instance->init(
size, &rect, nullptr, depsgraph, nullptr, camera, nullptr, default_view, v3d, rv3d);
}
static void eevee_draw_scene(void *vedata)
{
((EEVEE_Data *)vedata)->instance->draw_viewport(DRW_viewport_framebuffer_list_get());
}
static void eevee_cache_init(void *vedata)
{
((EEVEE_Data *)vedata)->instance->begin_sync();
}
static void eevee_cache_populate(void *vedata, Object *object)
{
((EEVEE_Data *)vedata)->instance->object_sync(object);
}
static void eevee_cache_finish(void *vedata)
{
((EEVEE_Data *)vedata)->instance->end_sync();
}
static void eevee_engine_free(void)
{
ShaderModule::module_free();
}
static void eevee_instance_free(void *instance)
{
delete reinterpret_cast<Instance *>(instance);
}
static void eevee_render_to_image(void *UNUSED(vedata),
struct RenderEngine *engine,
struct RenderLayer *layer,
const struct rcti *UNUSED(rect))
{
Instance *instance = new 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, layer);
instance->render_frame(layer, viewname);
delete 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);
DrawEngineType draw_engine_eevee_type = {
NULL,
NULL,
N_("Eevee"),
&eevee_data_size,
&eevee_engine_init,
&eevee_engine_free,
&eevee_instance_free,
&eevee_cache_init,
&eevee_cache_populate,
&eevee_cache_finish,
&eevee_draw_scene,
NULL,
NULL,
&eevee_render_to_image,
NULL,
};
#define EEVEE_ENGINE "BLENDER_EEVEE"
RenderEngineType DRW_engine_viewport_eevee_type = {
NULL,
NULL,
EEVEE_ENGINE,
N_("Eevee"),
RE_INTERNAL | RE_USE_PREVIEW | RE_USE_STEREO_VIEWPORT | RE_USE_GPU_CONTEXT,
NULL,
&DRW_render_to_image,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&eevee_render_update_passes,
&draw_engine_eevee_type,
{NULL, NULL, NULL},
};
#undef EEVEE_ENGINE

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].is_valid() == false) {
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", data_);
DRW_shgroup_uniform_block(grp, "camera", 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", 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()
: shaders(*ShaderModule::module_get()),
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,483 @@
/*
* 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_storage_block(grp, "lights_buf", lights_data);
DRW_shgroup_storage_block_ref(grp, "lights_cull_buf", &culling_data);
DRW_shgroup_storage_block(grp, "keys_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_storage_block(grp, "lights_buf", lights_data);
DRW_shgroup_storage_block_ref(grp, "lights_cull_buf", &culling_data);
DRW_shgroup_storage_block(grp, "keys_buf", culling_key_buf);
DRW_shgroup_storage_block_ref(grp, "lights_zbin_buf", &culling_zbin_buf);
DRW_shgroup_storage_block_ref(grp, "out_lights_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_storage_block(grp, "lights_buf", culling_light_buf);
DRW_shgroup_storage_block_ref(grp, "lights_cull_buf", &culling_data);
DRW_shgroup_storage_block_ref(grp, "lights_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_storage_block_ref(grp, "lights_buf", &culling_light_buf);
DRW_shgroup_storage_block_ref(grp, "lights_cull_buf", &culling_data);
DRW_shgroup_storage_block_ref(grp, "lights_zbin_buf", &culling_zbin_buf);
DRW_shgroup_storage_block_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 = (!no_lights) ? tile_size / float2(extent) : float2(0.0f);
culling_data.enable_specular = enable_specular;
culling_data.items_count = no_lights ? 0 : light_refs_.size();
culling_data.items_no_cull_count = no_lights ? 0 : culling_data.items_no_cull_count;
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(ceil_multiple_u(word_count, 4u));
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_storage_block_ref(grp, "lights_buf", &culling_light_buf);
DRW_shgroup_storage_block_ref(grp, "lights_cull_buf", &culling_data);
DRW_shgroup_storage_block_ref(grp, "lights_zbin_buf", &culling_zbin_buf);
DRW_shgroup_storage_block_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,579 @@
/*
* 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_info.display_size != sce_eevee.gi_cubemap_draw_size ||
info_data_.grids_info.display_size != sce_eevee.gi_irradiance_draw_size ||
info_data_.grids_info.irradiance_smooth != square_f(sce_eevee.gi_irradiance_smoothing)) {
/* TODO(fclem) reset on scene update instead. */
inst_.sampling.reset();
}
info_data_.cubes_info.display_size = sce_eevee.gi_cubemap_draw_size;
info_data_.grids_info.display_size = sce_eevee.gi_irradiance_draw_size;
info_data_.grids_info.irradiance_smooth = square_f(sce_eevee.gi_irradiance_smoothing);
info_data_.grids_info.irradiance_cells_per_row = lightcache_->irradiance_cells_per_row_get();
info_data_.grids_info.visibility_size = lightcache_->vis_res;
info_data_.grids_info.visibility_cells_per_row = lightcache_->grid_tx.tex_size[0] /
info_data_.grids_info.visibility_size;
info_data_.grids_info.visibility_cells_per_layer =
(lightcache_->grid_tx.tex_size[1] / info_data_.grids_info.visibility_size) *
info_data_.grids_info.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_buf", 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_buf", 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_buf", 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_buf", 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_buf", cube_ubo_get());
DRW_shgroup_uniform_block(grp, "probes_buf", 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_buf", grid_ubo_get());
DRW_shgroup_uniform_block(grp, "probes_buf", 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 = float4x4::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_info.irradiance_cells_per_row;
offset.y *= sample_index / info_data_.grids_info.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_info.visibility_size);
int2 offset = extent;
offset.x *= sample_index % info_data_.grids_info.visibility_cells_per_row;
offset.y *= (sample_index / info_data_.grids_info.visibility_cells_per_row) %
info_data_.grids_info.visibility_cells_per_layer;
filter_data_.target_layer = 1 + sample_index / info_data_.grids_info.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_info.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_info.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_info.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_info.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_info.grid_count = 1;
info_data_.cubes_info.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_info.roughness_max_lod = lightcache_->mips_len;
inst_.lookdev.rotation_get(info_data_.cubes_info.lookdev_rotation);
inst_.lookdev.rotation_get(info_data_.grids_info.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 = float4x4::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, "probes_buf", 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_buf", inst_.sampling.ubo_get());
DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get());
DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get());
DRW_shgroup_uniform_block(grp, "probes_buf", 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_buf", 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_buf", 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_buf", inst_.sampling.ubo_get());
DRW_shgroup_uniform_block(grp, "motion_blur_buf", 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_buf", 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_buf", 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_buf", 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_buf", inst_.sampling.ubo_get());
DRW_shgroup_uniform_block(grp, "hiz_buf", inst_.hiz.ubo_get());
DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get());
DRW_shgroup_uniform_block(grp, "probes_buf", 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_buf", inst_.raytracing.diffuse_ubo_get());
DRW_shgroup_uniform_block(grp, "hiz_buf", inst_.hiz.ubo_get());
DRW_shgroup_uniform_block(grp, "rtbuf_buf", 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_buf", 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,742 +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,538 @@
/*
* 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"
#include "../../gpu/intern/gpu_shader_create_info.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
/** \name Module
*
* \{ */
ShaderModule *ShaderModule::g_shader_module = nullptr;
ShaderModule *ShaderModule::module_get()
{
if (g_shader_module == nullptr) {
/* TODO(fclem) threadsafety. */
g_shader_module = new ShaderModule();
}
return g_shader_module;
}
void ShaderModule::module_free()
{
if (g_shader_module != nullptr) {
/* TODO(fclem) threadsafety. */
delete g_shader_module;
g_shader_module = nullptr;
}
}
ShaderModule::ShaderModule()
{
for (GPUShader *&shader : shaders_) {
shader = nullptr;
}
#ifdef DEBUG
/* Ensure all shader are described. */
for (auto i : IndexRange(MAX_SHADER_TYPE)) {
const char *name = static_shader_create_info_name_get(eShaderType(i));
if (name == nullptr) {
std::cerr << "EEVEE: Missing case for eShaderType(" << i
<< ") in static_shader_create_info_name_get().";
BLI_assert(0);
}
const GPUShaderCreateInfo *create_info = GPU_shader_create_info_get(name);
BLI_assert_msg(create_info != nullptr, "EEVEE: Missing create info for static shader.");
}
#endif
}
ShaderModule::~ShaderModule()
{
for (GPUShader *&shader : shaders_) {
DRW_SHADER_FREE_SAFE(shader);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Static shaders
*
* \{ */
const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_type)
{
switch (shader_type) {
case CULLING_DEBUG:
return "eevee_culling_debug";
case CULLING_SELECT:
return "eevee_culling_select";
case CULLING_SORT:
return "eevee_culling_sort";
case CULLING_TILE:
return "eevee_culling_tile";
case FILM_FILTER:
return "eevee_film_filter";
case FILM_RESOLVE:
return "eevee_film_resolve";
case FILM_RESOLVE_DEPTH:
return "eevee_film_resolve_depth";
case DEFERRED_EVAL_DIRECT:
return "eevee_deferred_direct";
case DEFERRED_EVAL_HOLDOUT:
return "eevee_deferred_holdout";
case DEFERRED_EVAL_TRANSPARENT:
return "eevee_deferred_transparent";
case DEFERRED_EVAL_VOLUME:
return "eevee_deferred_volume";
case DEFERRED_VOLUME:
return "eevee_volume_deferred";
case HIZ_COPY:
return "eevee_hiz_copy";
case HIZ_DOWNSAMPLE:
return "eevee_hiz_downsample";
case DOF_BOKEH_LUT:
return "eevee_depth_of_field_bokeh_lut";
case DOF_FILTER:
return "eevee_depth_of_field_filter";
case DOF_GATHER_BACKGROUND_LUT:
return "eevee_depth_of_field_gather_background_lut";
case DOF_GATHER_BACKGROUND:
return "eevee_depth_of_field_gather_background";
case DOF_GATHER_FOREGROUND_LUT:
return "eevee_depth_of_field_gather_foreground_lut";
case DOF_GATHER_FOREGROUND:
return "eevee_depth_of_field_gather_foreground";
case DOF_GATHER_HOLEFILL:
return "eevee_depth_of_field_gather_holefill";
case DOF_REDUCE_COPY:
return "eevee_depth_of_field_reduce_copy";
case DOF_REDUCE_DOWNSAMPLE:
return "eevee_depth_of_field_reduce_downsample";
case DOF_REDUCE_RECURSIVE:
return "eevee_depth_of_field_reduce_recursive";
case DOF_RESOLVE_LUT:
return "eevee_depth_of_field_resolve_lq_lut";
case DOF_RESOLVE_LUT_HQ:
return "eevee_depth_of_field_resolve_hq_lut";
case DOF_RESOLVE:
return "eevee_depth_of_field_resolve_lq";
case DOF_RESOLVE_HQ:
return "eevee_depth_of_field_resolve_hq";
case DOF_SCATTER_BACKGROUND_LUT:
return "eevee_depth_of_field_scatter_background_lut";
case DOF_SCATTER_BACKGROUND:
return "eevee_depth_of_field_scatter_background";
case DOF_SCATTER_FOREGROUND_LUT:
return "eevee_depth_of_field_scatter_foreground_lut";
case DOF_SCATTER_FOREGROUND:
return "eevee_depth_of_field_scatter_foreground";
case DOF_SETUP:
return "eevee_depth_of_field_setup";
case DOF_TILES_DILATE_MINABS:
return "eevee_depth_of_field_tiles_dilate_minabs";
case DOF_TILES_DILATE_MINMAX:
return "eevee_depth_of_field_tiles_dilate_minmax";
case DOF_TILES_FLATTEN:
return "eevee_depth_of_field_tiles_flatten";
case LIGHTPROBE_DISPLAY_CUBEMAP:
return "eevee_lightprobe_display_cubemap";
case LIGHTPROBE_DISPLAY_IRRADIANCE:
return "eevee_lightprobe_display_grid";
case LIGHTPROBE_FILTER_DOWNSAMPLE_CUBE:
return "eevee_lightprobe_filter_downsample";
case LIGHTPROBE_FILTER_GLOSSY:
return "eevee_lightprobe_filter_glossy";
case LIGHTPROBE_FILTER_DIFFUSE:
return "eevee_lightprobe_filter_diffuse";
case LIGHTPROBE_FILTER_VISIBILITY:
return "eevee_lightprobe_filter_visibility";
case LOOKDEV_BACKGROUND:
return "eevee_background_lookdev";
case MOTION_BLUR_GATHER:
return "eevee_motion_blur_gather";
case MOTION_BLUR_TILE_DILATE:
return "eevee_motion_blur_tiles_dilate";
case MOTION_BLUR_TILE_FLATTEN:
return "eevee_motion_blur_tiles_flatten";
case RAYTRACE_DIFFUSE:
return "eevee_raytrace_raygen_diffuse";
case RAYTRACE_REFLECTION:
return "eevee_raytrace_raygen_reflection";
case RAYTRACE_REFRACTION:
return "eevee_raytrace_raygen_refraction";
case RAYTRACE_DIFFUSE_FALLBACK:
return "eevee_raytrace_raygen_fallback_diffuse";
case RAYTRACE_REFLECTION_FALLBACK:
return "eevee_raytrace_raygen_fallback_reflection";
case RAYTRACE_REFRACTION_FALLBACK:
return "eevee_raytrace_raygen_fallback_refraction";
case RAYTRACE_DENOISE_DIFFUSE:
return "eevee_raytrace_denoise_diffuse";
case RAYTRACE_DENOISE_REFLECTION:
return "eevee_raytrace_denoise_reflection";
case RAYTRACE_DENOISE_REFRACTION:
return "eevee_raytrace_denoise_refraction";
case RAYTRACE_RESOLVE_DIFFUSE:
return "eevee_raytrace_resolve_diffuse";
case RAYTRACE_RESOLVE_REFLECTION:
return "eevee_raytrace_resolve_reflection";
case RAYTRACE_RESOLVE_REFRACTION:
return "eevee_raytrace_resolve_refraction";
case SHADOW_DEBUG:
return "eevee_shadow_debug";
case SHADOW_PAGE_ALLOC:
return "eevee_shadow_page_alloc";
case SHADOW_PAGE_COPY:
return "eevee_shadow_page_copy";
case SHADOW_PAGE_DEBUG:
return "eevee_shadow_page_debug";
case SHADOW_PAGE_DEFRAG:
return "eevee_shadow_page_defrag";
case SHADOW_PAGE_FREE:
return "eevee_shadow_page_free";
case SHADOW_PAGE_INIT:
return "eevee_shadow_page_init";
case SHADOW_PAGE_MARK:
return "eevee_shadow_page_mark";
case SHADOW_TILE_DEPTH_SCAN:
return "eevee_shadow_tilemap_depth_scan";
case SHADOW_TILE_LOD_MASK:
return "eevee_shadow_tilemap_lod_mask";
case SHADOW_TILE_SETUP:
return "eevee_shadow_tilemap_setup";
case SHADOW_TILE_TAG_UPDATE:
return "eevee_shadow_tilemap_tag_update";
case SHADOW_TILE_TAG_USAGE:
return "eevee_shadow_tilemap_tag_usage";
case SHADOW_TILE_TAG_VISIBILITY:
return "eevee_shadow_tilemap_visibility";
case SUBSURFACE_EVAL:
return "eevee_subsurface_eval";
case VELOCITY_MESH:
return "eevee_velocity_surface_mesh";
case VELOCITY_CAMERA:
return "eevee_velocity_camera";
/* To avoid compiler warning about missing case. */
case MAX_SHADER_TYPE:
return "";
}
return "";
}
GPUShader *ShaderModule::static_shader_get(eShaderType shader_type)
{
if (shaders_[shader_type] == nullptr) {
const char *shader_name = static_shader_create_info_name_get(shader_type);
shaders_[shader_type] = GPU_shader_create_from_info_name(shader_name);
if (shaders_[shader_type] == nullptr) {
fprintf(stderr, "EEVEE: error: Could not compile static shader \"%s\"\n", shader_name);
}
BLI_assert(shaders_[shader_type] != nullptr);
}
return shaders_[shader_type];
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name GPU Materials
*
* \{ */
void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOutput *codegen_)
{
using namespace blender::gpu::shader;
uint64_t shader_uuid = GPU_material_uuid_get(gpumat);
eMaterialPipeline pipeline_type;
eMaterialGeometry geometry_type;
material_type_from_shader_uuid(shader_uuid, pipeline_type, geometry_type);
GPUCodegenOutput &codegen = *codegen_;
ShaderCreateInfo &info = *reinterpret_cast<ShaderCreateInfo *>(codegen.create_info);
info.auto_resource_location(true);
std::stringstream global_vars;
switch (geometry_type) {
case MAT_GEOM_MESH:
/** Noop. */
break;
case MAT_GEOM_HAIR:
/** Hair attributes comme from sampler buffer. Transfer attributes to sampler. */
for (auto &input : info.vertex_inputs_) {
info.sampler(0, ImageType::FLOAT_BUFFER, input.name, Frequency::BATCH);
}
info.vertex_inputs_.clear();
break;
case MAT_GEOM_WORLD:
/**
* Only orco layer is supported by world and it is procedurally generated. These are here to
* make the attribs_load function calls valids.
*/
ATTR_FALLTHROUGH;
case MAT_GEOM_GPENCIL:
/**
* Only one uv and one color attribute layer are supported by gpencil objects and they are
* already declared in another createInfo. These are here to make the attribs_load
* function calls valids.
*/
for (auto &input : info.vertex_inputs_) {
global_vars << input.type << " " << input.name << ";\n";
}
info.vertex_inputs_.clear();
break;
case MAT_GEOM_VOLUME:
case MAT_GEOM_LOOKDEV:
/** No attributes supported. */
info.vertex_inputs_.clear();
break;
}
const bool do_fragment_attrib_load = (geometry_type == MAT_GEOM_WORLD);
if (do_fragment_attrib_load && !info.vertex_out_interfaces_.is_empty()) {
/* Codegen outputs only one interface. */
const StageInterfaceInfo &iface = *info.vertex_out_interfaces_.first();
/* Globals the attrib_load() can write to when it is in the fragment shader. */
global_vars << "struct " << iface.name << " {\n";
for (auto &inout : iface.inouts) {
global_vars << " " << inout.type << " " << inout.name << ";\n";
}
global_vars << "};\n";
global_vars << iface.name << " " << iface.instance_name << ";\n";
info.vertex_out_interfaces_.clear();
}
std::stringstream attr_load;
attr_load << "void attrib_load()\n";
attr_load << "{\n";
attr_load << ((codegen.attr_load) ? codegen.attr_load : "");
attr_load << "}\n\n";
std::stringstream vert_gen, frag_gen;
if (do_fragment_attrib_load) {
frag_gen << global_vars.str() << attr_load.str();
}
else {
vert_gen << global_vars.str() << attr_load.str();
}
{
/* Only mesh and hair support displacement for now. */
if (ELEM(geometry_type, MAT_GEOM_MESH, MAT_GEOM_HAIR)) {
vert_gen << "vec3 nodetree_displacement()\n";
vert_gen << "{\n";
vert_gen << ((codegen.displacement) ? codegen.displacement : "return vec3(0);\n");
vert_gen << "}\n\n";
}
info.vertex_source_generated = vert_gen.str();
}
{
frag_gen << "Closure nodetree_surface()\n";
frag_gen << "{\n";
frag_gen << ((codegen.surface) ? codegen.surface : "return CLOSURE_DEFAULT;\n");
frag_gen << "}\n\n";
frag_gen << "Closure nodetree_volume()\n";
frag_gen << "{\n";
frag_gen << ((codegen.volume) ? codegen.volume : "return CLOSURE_DEFAULT;\n");
frag_gen << "}\n\n";
frag_gen << "float nodetree_thickness()\n";
frag_gen << "{\n";
/* TODO(fclem): Better default. */
frag_gen << ((codegen.thickness) ? codegen.thickness : "return 0.1;\n");
frag_gen << "}\n\n";
info.fragment_source_generated = frag_gen.str();
}
/* Geometry Info. */
switch (geometry_type) {
case MAT_GEOM_WORLD:
info.additional_info("eevee_surface_world");
break;
case MAT_GEOM_VOLUME:
info.additional_info("eevee_volume");
break;
case MAT_GEOM_GPENCIL:
info.additional_info("eevee_surface_gpencil");
break;
case MAT_GEOM_LOOKDEV:
info.additional_info("eevee_surface_lookdev");
break;
case MAT_GEOM_HAIR:
info.additional_info("eevee_surface_hair");
break;
case MAT_GEOM_MESH:
default:
info.additional_info("eevee_surface_mesh");
break;
}
/* Pipeline Info. */
switch (geometry_type) {
case MAT_GEOM_WORLD:
info.additional_info("eevee_surface_background");
break;
case MAT_GEOM_VOLUME:
switch (pipeline_type) {
case MAT_PIPE_DEFERRED:
info.additional_info("eevee_volume_deferred");
break;
default:
BLI_assert(0);
break;
}
break;
default:
switch (pipeline_type) {
case MAT_PIPE_FORWARD_PREPASS:
info.additional_info("eevee_surface_depth_simple");
break;
case MAT_PIPE_DEFERRED_PREPASS:
case MAT_PIPE_SHADOW:
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
info.additional_info("eevee_surface_depth");
}
else {
info.additional_info("eevee_surface_depth_simple");
info.fragment_source_generated = "";
}
break;
case MAT_PIPE_DEFERRED:
info.additional_info("eevee_surface_deferred");
break;
case MAT_PIPE_FORWARD:
info.additional_info("eevee_surface_forward");
break;
default:
BLI_assert(0);
break;
}
break;
}
}
/* WATCH: This can be called from another thread! Needs to not touch the shader module in any
* thread unsafe manner. */
static void codegen_callback(void *thunk, GPUMaterial *mat, GPUCodegenOutput *codegen)
{
reinterpret_cast<ShaderModule *>(thunk)->material_create_info_ammend(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,172 @@
/*
* 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_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:
std::array<GPUShader *, MAX_SHADER_TYPE> shaders_;
/** Shared shader module accross all engine instances. */
static ShaderModule *g_shader_module;
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);
void material_create_info_ammend(GPUMaterial *mat, GPUCodegenOutput *codegen);
/** Only to be used by Instance constructor. */
static ShaderModule *module_get();
static void module_free();
private:
const char *static_shader_create_info_name_get(eShaderType shader_type);
};
} // namespace blender::eevee

View File

@@ -0,0 +1,856 @@
/**
* 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_defines.hh"
# 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)
/** WORKAROUND(@fclem): This is because this file is included before common_math_lib.glsl. */
#ifndef M_PI
# define EEVEE_PI
# define M_PI 3.14159265358979323846 /* pi */
#endif
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;
}
#ifdef EEVEE_PI
# undef M_PI
#endif
/** \} */
/* -------------------------------------------------------------------- */
/** \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
/* 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. */
float 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)
#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_info;
CubemapInfoData cubes_info;
};
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
/* Fetch texel. Wrapping if above range. */
float4 utility_tx_fetch(sampler2DArray util_tx, float2 texel, float layer)
{
return texelFetch(util_tx, int3(int2(texel) % UTIL_TEX_SIZE, layer), 0);
}
/* Sample at uv position. Filtered & Wrapping enabled. */
float4 utility_tx_sample(sampler2DArray util_tx, float2 uv, float layer)
{
return textureLod(util_tx, float3(uv, layer), 0.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 = float4x4::identity();
copy_v3_v3(camera_mat[3], inst_.camera.data_get().viewinv[3]);
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, background_ps_);
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
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;
RaytracingModule &raytracing = inst_.raytracing;
eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
lights.shgroup_resources(grp);
DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get());
DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get());
DRW_shgroup_uniform_block(grp, "probes_buf", 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 (raytracing.enabled()) {
DRW_shgroup_uniform_block(grp, "rt_diffuse_buf", raytracing.diffuse_ubo_get());
DRW_shgroup_uniform_block(grp, "rt_reflection_buf", raytracing.reflection_ubo_get());
DRW_shgroup_uniform_block(grp, "rt_refraction_buf", raytracing.refraction_ubo_get());
DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_radiance_tx_, no_interp);
}
if (raytracing.enabled()) {
DRW_shgroup_uniform_block(grp, "hiz_buf", 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;
RaytracingModule &raytracing = inst_.raytracing;
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_buf", inst_.sampling.ubo_get());
DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get());
DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get());
DRW_shgroup_uniform_block(grp, "probes_buf", 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 (raytracing.enabled()) {
DRW_shgroup_uniform_block(grp, "rt_diffuse_buf", raytracing.diffuse_ubo_get());
DRW_shgroup_uniform_block(grp, "rt_reflection_buf", raytracing.reflection_ubo_get());
DRW_shgroup_uniform_block(grp, "rt_refraction_buf", raytracing.refraction_ubo_get());
DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_radiance_tx_, no_interp);
}
if (raytracing.enabled()) {
DRW_shgroup_uniform_block(grp, "hiz_buf", 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_buf", 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);
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_buf", inst_.sampling.ubo_get());
DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get());
DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get());
DRW_shgroup_uniform_block(grp, "probes_buf", 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, "sss_buf", inst_.subsurface.ubo_get());
DRW_shgroup_uniform_block(grp, "hiz_buf", 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 rtbuf_buf 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 &rt_buffer,
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);
}
}
/** \} */

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