Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format3
-rw-r--r--release/scripts/startup/bl_ui/properties_data_camera.py17
-rw-r--r--release/scripts/startup/bl_ui/properties_material.py2
-rw-r--r--release/scripts/startup/bl_ui/properties_render.py42
-rw-r--r--release/scripts/startup/bl_ui/properties_view_layer.py5
-rw-r--r--source/blender/blenkernel/BKE_world.h9
-rw-r--r--source/blender/blenkernel/intern/world.c56
-rw-r--r--source/blender/blenlib/BLI_assert.h3
-rw-r--r--source/blender/blenlib/BLI_int2.hh170
-rw-r--r--source/blender/blenlib/BLI_int3.hh156
-rw-r--r--source/blender/blenlib/intern/math_vector_inline.c6
-rw-r--r--source/blender/blenloader/intern/versioning_280.c9
-rw-r--r--source/blender/blenloader/intern/versioning_290.c18
-rw-r--r--source/blender/draw/CMakeLists.txt294
-rw-r--r--source/blender/draw/engines/eevee/eevee_allocator.hh219
-rw-r--r--source/blender/draw/engines/eevee/eevee_bloom.c344
-rw-r--r--source/blender/draw/engines/eevee/eevee_build.cc155
-rw-r--r--source/blender/draw/engines/eevee/eevee_camera.cc167
-rw-r--r--source/blender/draw/engines/eevee/eevee_camera.hh149
-rw-r--r--source/blender/draw/engines/eevee/eevee_cryptomatte.c721
-rw-r--r--source/blender/draw/engines/eevee/eevee_data.c389
-rw-r--r--source/blender/draw/engines/eevee/eevee_depth_of_field.c1048
-rw-r--r--source/blender/draw/engines/eevee/eevee_depth_of_field.cc731
-rw-r--r--source/blender/draw/engines/eevee/eevee_depth_of_field.hh176
-rw-r--r--source/blender/draw/engines/eevee/eevee_effects.c524
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c661
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.cc167
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.h10
-rw-r--r--source/blender/draw/engines/eevee/eevee_film.cc257
-rw-r--r--source/blender/draw/engines/eevee/eevee_film.hh112
-rw-r--r--source/blender/draw/engines/eevee/eevee_gbuffer.hh258
-rw-r--r--source/blender/draw/engines/eevee/eevee_gpencil.cc155
-rw-r--r--source/blender/draw/engines/eevee/eevee_hair.cc74
-rw-r--r--source/blender/draw/engines/eevee/eevee_hizbuffer.cc102
-rw-r--r--source/blender/draw/engines/eevee/eevee_hizbuffer.hh103
-rw-r--r--source/blender/draw/engines/eevee/eevee_id_map.cc76
-rw-r--r--source/blender/draw/engines/eevee/eevee_id_map.hh284
-rw-r--r--source/blender/draw/engines/eevee/eevee_instance.cc316
-rw-r--r--source/blender/draw/engines/eevee/eevee_instance.hh190
-rw-r--r--source/blender/draw/engines/eevee/eevee_light.cc482
-rw-r--r--source/blender/draw/engines/eevee/eevee_light.hh140
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightcache.c1533
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightcache.cc1198
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightcache.hh92
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightprobe.cc578
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightprobe.hh175
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightprobes.c1265
-rw-r--r--source/blender/draw/engines/eevee/eevee_lights.c268
-rw-r--r--source/blender/draw/engines/eevee/eevee_lookdev.c383
-rw-r--r--source/blender/draw/engines/eevee/eevee_lookdev.cc360
-rw-r--r--source/blender/draw/engines/eevee/eevee_lookdev.hh112
-rw-r--r--source/blender/draw/engines/eevee/eevee_lut.h8
-rw-r--r--source/blender/draw/engines/eevee/eevee_lut_gen.c123
-rw-r--r--source/blender/draw/engines/eevee/eevee_material.cc335
-rw-r--r--source/blender/draw/engines/eevee/eevee_material.hh123
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c1142
-rw-r--r--source/blender/draw/engines/eevee/eevee_mesh.cc58
-rw-r--r--source/blender/draw/engines/eevee/eevee_mist.c113
-rw-r--r--source/blender/draw/engines/eevee/eevee_motion_blur.c613
-rw-r--r--source/blender/draw/engines/eevee/eevee_motion_blur.cc248
-rw-r--r--source/blender/draw/engines/eevee/eevee_motion_blur.hh159
-rw-r--r--source/blender/draw/engines/eevee/eevee_occlusion.c281
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h1625
-rw-r--r--source/blender/draw/engines/eevee/eevee_raytracing.cc303
-rw-r--r--source/blender/draw/engines/eevee/eevee_raytracing.hh228
-rw-r--r--source/blender/draw/engines/eevee/eevee_render.c743
-rw-r--r--source/blender/draw/engines/eevee/eevee_renderpasses.c518
-rw-r--r--source/blender/draw/engines/eevee/eevee_renderpasses.cc92
-rw-r--r--source/blender/draw/engines/eevee/eevee_renderpasses.hh249
-rw-r--r--source/blender/draw/engines/eevee/eevee_sampling.c123
-rw-r--r--source/blender/draw/engines/eevee/eevee_sampling.hh358
-rw-r--r--source/blender/draw/engines/eevee/eevee_screen_raytrace.c264
-rw-r--r--source/blender/draw/engines/eevee/eevee_shader.cc930
-rw-r--r--source/blender/draw/engines/eevee/eevee_shader.hh191
-rw-r--r--source/blender/draw/engines/eevee/eevee_shader_shared.hh934
-rw-r--r--source/blender/draw/engines/eevee/eevee_shaders.c1652
-rw-r--r--source/blender/draw/engines/eevee/eevee_shading.cc599
-rw-r--r--source/blender/draw/engines/eevee/eevee_shading.hh328
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadow.cc1201
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadow.hh611
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadows.c413
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadows_cascade.c437
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadows_cube.c224
-rw-r--r--source/blender/draw/engines/eevee/eevee_subsurface.c363
-rw-r--r--source/blender/draw/engines/eevee/eevee_subsurface.cc205
-rw-r--r--source/blender/draw/engines/eevee/eevee_subsurface.hh84
-rw-r--r--source/blender/draw/engines/eevee/eevee_temporal_sampling.c438
-rw-r--r--source/blender/draw/engines/eevee/eevee_velocity.cc356
-rw-r--r--source/blender/draw/engines/eevee/eevee_velocity.hh171
-rw-r--r--source/blender/draw/engines/eevee/eevee_view.cc262
-rw-r--r--source/blender/draw/engines/eevee/eevee_view.hh239
-rw-r--r--source/blender/draw/engines/eevee/eevee_volumes.c856
-rw-r--r--source/blender/draw/engines/eevee/eevee_world.cc111
-rw-r--r--source/blender/draw/engines/eevee/eevee_world.hh78
-rw-r--r--source/blender/draw/engines/eevee/eevee_wrapper.hh682
-rw-r--r--source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl417
-rw-r--r--source/blender/draw/engines/eevee/shaders/background_vert.glsl27
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl206
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl59
-rw-r--r--source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl89
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl88
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl145
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl324
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl128
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl71
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl189
-rw-r--r--source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl73
-rw-r--r--source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl108
-rw-r--r--source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl7
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_bsdf_lib.glsl242
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_bsdf_microfacet_lib.glsl59
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_bsdf_sampling_lib.glsl (renamed from source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl)92
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_bsdf_stubs_lib.glsl41
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_camera_lib.glsl167
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_camera_velocity_frag.glsl59
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_closure_lib.glsl104
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_cubemap_lib.glsl (renamed from source/blender/draw/engines/eevee/shaders/cubemap_lib.glsl)54
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_culling_debug_frag.glsl72
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_culling_iter_lib.glsl73
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_culling_lib.glsl175
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_culling_select_comp.glsl60
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_culling_sort_comp.glsl148
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_culling_tile_comp.glsl70
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_deferred_direct_frag.glsl136
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_deferred_holdout_frag.glsl25
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_deferred_transparent_frag.glsl59
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_deferred_volume_frag.glsl80
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_depth_clear_frag.glsl6
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_accumulator_lib.glsl685
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_bokeh_lut_frag.glsl63
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_filter_frag.glsl (renamed from source/blender/draw/engines/eevee/shaders/effect_dof_filter_frag.glsl)14
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_gather_frag.glsl118
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_gather_holefill_frag.glsl89
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_lib.glsl328
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_copy_frag.glsl144
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_downsample_frag.glsl (renamed from source/blender/draw/engines/eevee/shaders/effect_dof_downsample_frag.glsl)20
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_recursive_frag.glsl36
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_resolve_frag.glsl111
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_scatter_frag.glsl (renamed from source/blender/draw/engines/eevee/shaders/effect_dof_scatter_frag.glsl)58
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_scatter_lib.glsl17
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_scatter_vert.glsl (renamed from source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl)60
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_setup_frag.glsl73
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_tiles_dilate_frag.glsl117
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_tiles_flatten_frag.glsl (renamed from source/blender/draw/engines/eevee/shaders/effect_dof_flatten_tiles_frag.glsl)21
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_film_filter_frag.glsl114
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_film_lib.glsl149
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_film_resolve_depth_frag.glsl22
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_film_resolve_frag.glsl31
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_gbuffer_lib.glsl272
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_hiz_copy_frag.glsl17
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_hiz_downsample_frag.glsl40
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_irradiance_lib.glsl139
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_light_eval_lib.glsl103
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_light_lib.glsl197
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_cubemap_frag.glsl26
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_cubemap_vert.glsl41
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_grid_frag.glsl29
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_grid_vert.glsl53
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_lib.glsl8
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_lightprobe_eval_cubemap_lib.glsl89
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_lightprobe_eval_grid_lib.glsl105
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_diffuse_frag.glsl73
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_downsample_frag.glsl20
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_geom.glsl42
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_glossy_frag.glsl67
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_lib.glsl7
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_vert.glsl49
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_visibility_frag.glsl78
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_lookdev_background_frag.glsl38
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_ltc_lib.glsl (renamed from source/blender/draw/engines/eevee/shaders/ltc_lib.glsl)45
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_motion_blur_gather_frag.glsl (renamed from source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl)120
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_motion_blur_lib.glsl18
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_motion_blur_tiles_dilate_frag.glsl (renamed from source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl)80
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_motion_blur_tiles_flatten_frag.glsl51
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_nodetree_eval_lib.glsl97
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_raytrace_denoise_comp.glsl260
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_raytrace_raygen_frag.glsl183
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_raytrace_raygen_lib.glsl83
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_raytrace_resolve_frag.glsl124
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_raytrace_trace_lib.glsl182
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_sampling_lib.glsl96
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_debug_frag.glsl279
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_lib.glsl128
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_page_alloc_comp.glsl132
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_page_copy_comp.glsl45
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_page_debug_comp.glsl113
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_page_defrag_comp.glsl59
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_page_free_comp.glsl73
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_page_init_comp.glsl49
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_page_lib.glsl17
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_page_mark_vert.glsl40
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_depth_scan_comp.glsl134
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lib.glsl216
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lod_mask_comp.glsl70
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_setup_comp.glsl90
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_tag_comp.glsl221
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_visibility_comp.glsl149
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_subsurface_eval_frag.glsl155
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_surface_background_frag.glsl57
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_surface_deferred_frag.glsl96
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_surface_depth_frag.glsl (renamed from source/blender/draw/engines/eevee/shaders/prepass_frag.glsl)61
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_surface_depth_simple_frag.glsl25
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_surface_forward_frag.glsl237
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_surface_gpencil_vert.glsl86
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_surface_hair_vert.glsl80
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_surface_lib.glsl63
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_surface_lookdev_vert.glsl34
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_surface_mesh_geom.glsl33
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_surface_mesh_vert.glsl72
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_frag.glsl43
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_lib.glsl8
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_mesh_vert.glsl40
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_surface_world_vert.glsl26
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_velocity_lib.glsl38
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_volume_deferred_frag.glsl66
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_volume_eval_lib.glsl90
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_volume_lib.glsl16
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_volume_vert.glsl34
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_bloom_frag.glsl218
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_bokeh_frag.glsl101
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_dilate_tiles_frag.glsl117
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl293
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl625
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_reduce_frag.glsl179
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl212
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_setup_frag.glsl65
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_downsample_cube_frag.glsl42
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_downsample_frag.glsl42
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl115
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl76
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl36
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_reflection_lib.glsl101
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_reflection_resolve_frag.glsl220
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl149
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl78
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl117
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl272
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl32
-rw-r--r--source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl212
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl22
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl44
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl170
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl79
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl97
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl47
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl22
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl47
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_grid_fill_frag.glsl18
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl311
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl18
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl17
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl37
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_geom.glsl25
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_vert.glsl12
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_vert.glsl11
-rw-r--r--source/blender/draw/engines/eevee/shaders/lights_lib.glsl387
-rw-r--r--source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl52
-rw-r--r--source/blender/draw/engines/eevee/shaders/object_motion_frag.glsl27
-rw-r--r--source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl59
-rw-r--r--source/blender/draw/engines/eevee/shaders/octahedron_lib.glsl20
-rw-r--r--source/blender/draw/engines/eevee/shaders/prepass_vert.glsl35
-rw-r--r--source/blender/draw/engines/eevee/shaders/random_lib.glsl38
-rw-r--r--source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl228
-rw-r--r--source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl56
-rw-r--r--source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl126
-rw-r--r--source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl52
-rw-r--r--source/blender/draw/engines/eevee/shaders/shadow_frag.glsl5
-rw-r--r--source/blender/draw/engines/eevee/shaders/shadow_vert.glsl54
-rw-r--r--source/blender/draw/engines/eevee/shaders/ssr_lib.glsl91
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_frag.glsl94
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_geom.glsl46
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_lib.glsl54
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_vert.glsl63
-rw-r--r--source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl18
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_accum_frag.glsl11
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl74
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl80
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl85
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl198
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl34
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl86
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl37
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.h3
-rw-r--r--source/blender/draw/engines/overlay/overlay_shader.c16
-rw-r--r--source/blender/draw/intern/DRW_render.h75
-rw-r--r--source/blender/draw/intern/draw_common.h8
-rw-r--r--source/blender/draw/intern/draw_debug.c77
-rw-r--r--source/blender/draw/intern/draw_debug.h12
-rw-r--r--source/blender/draw/intern/draw_manager.h32
-rw-r--r--source/blender/draw/intern/draw_manager_data.c125
-rw-r--r--source/blender/draw/intern/draw_manager_exec.c31
-rw-r--r--source/blender/draw/intern/draw_manager_profiling.h8
-rw-r--r--source/blender/draw/intern/draw_manager_shader.c209
-rw-r--r--source/blender/draw/intern/draw_manager_texture.c7
-rw-r--r--source/blender/draw/intern/shaders/common_attribute_lib.glsl19
-rw-r--r--source/blender/draw/intern/shaders/common_debug_lib.glsl131
-rw-r--r--source/blender/draw/intern/shaders/common_gpencil_lib.glsl309
-rw-r--r--source/blender/draw/intern/shaders/common_hair_lib.glsl13
-rw-r--r--source/blender/draw/intern/shaders/common_intersection_lib.glsl209
-rw-r--r--source/blender/draw/intern/shaders/common_math_geom_lib.glsl119
-rw-r--r--source/blender/draw/intern/shaders/common_math_lib.glsl72
-rw-r--r--source/blender/draw/intern/shaders/common_obinfos_lib.glsl25
-rw-r--r--source/blender/draw/intern/shaders/common_uniform_attribute_lib.glsl12
-rw-r--r--source/blender/draw/intern/shaders/common_view_lib.glsl11
-rw-r--r--source/blender/gpu/CMakeLists.txt4
-rw-r--r--source/blender/gpu/GPU_material.h124
-rw-r--r--source/blender/gpu/GPU_shader.h15
-rw-r--r--source/blender/gpu/GPU_state.h13
-rw-r--r--source/blender/gpu/intern/gpu_codegen.c1138
-rw-r--r--source/blender/gpu/intern/gpu_codegen.cc824
-rw-r--r--source/blender/gpu/intern/gpu_codegen.h22
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer_private.hh5
-rw-r--r--source/blender/gpu/intern/gpu_material.c482
-rw-r--r--source/blender/gpu/intern/gpu_material_library.c8
-rw-r--r--source/blender/gpu/intern/gpu_material_library.h10
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.c120
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.h46
-rw-r--r--source/blender/gpu/intern/gpu_shader.cc6
-rw-r--r--source/blender/gpu/intern/gpu_shader_builder_stubs.cc5
-rw-r--r--source/blender/gpu/intern/gpu_shader_interface.hh18
-rw-r--r--source/blender/gpu/intern/gpu_shader_log.cc26
-rw-r--r--source/blender/gpu/opengl/gl_framebuffer.hh2
-rw-r--r--source/blender/gpu/opengl/gl_shader_interface.cc29
-rw-r--r--source/blender/gpu/opengl/gl_state.hh13
-rw-r--r--source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl121
-rw-r--r--source/blender/gpu/shaders/gpu_shader_common_obinfos_lib.glsl23
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl9
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl19
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_background.glsl11
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl15
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl8
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl33
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl7
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl99
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl10
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl7
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl30
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl90
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl40
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl24
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl8
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl8
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl16
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl69
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl14
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl13
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl36
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl18
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl23
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl269
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl41
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl25
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl39
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl6
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl16
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl73
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl18
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl26
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl11
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl25
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl17
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl7
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl1
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl8
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl23
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl24
-rw-r--r--source/blender/makesdna/DNA_camera_defaults.h7
-rw-r--r--source/blender/makesdna/DNA_camera_types.h18
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h3
-rw-r--r--source/blender/makesdna/DNA_lightprobe_types.h12
-rw-r--r--source/blender/makesdna/DNA_material_types.h4
-rw-r--r--source/blender/makesdna/DNA_node_types.h11
-rw-r--r--source/blender/makesdna/DNA_scene_defaults.h3
-rw-r--r--source/blender/makesdna/DNA_scene_types.h10
-rw-r--r--source/blender/makesrna/intern/rna_camera.c60
-rw-r--r--source/blender/makesrna/intern/rna_material.c15
-rw-r--r--source/blender/makesrna/intern/rna_scene.c29
-rw-r--r--source/blender/nodes/NOD_shader.h5
-rw-r--r--source/blender/nodes/shader/node_shader_tree.cc382
-rw-r--r--source/blender/nodes/shader/node_shader_util.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bevel.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc14
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc30
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc11
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc81
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc3
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bump.cc3
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_camera.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_displacement.cc9
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_emission.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_fresnel.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_geometry.cc16
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_layer_weight.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_normal_map.cc9
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_object_info.cc11
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_output_aov.cc7
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_output_material.cc39
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_output_world.cc18
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_particle_info.cc13
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc17
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tangent.cc9
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_coord.cc18
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_environment.cc12
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_image.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc29
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_transform.cc39
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_wireframe.cc14
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c2
-rw-r--r--source/creator/creator.c2
-rw-r--r--tests/python/eevee_render_tests.py6
417 files changed, 30214 insertions, 28796 deletions
diff --git a/.clang-format b/.clang-format
index 41f828787b2..cd8d4c66674 100644
--- a/.clang-format
+++ b/.clang-format
@@ -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
diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py
index edd0623d8fe..20bee833847 100644
--- a/release/scripts/startup/bl_ui/properties_data_camera.py
+++ b/release/scripts/startup/bl_ui/properties_data_camera.py
@@ -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':
diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py
index 1c7f3639f0a..5226a0efea7 100644
--- a/release/scripts/startup/bl_ui/properties_material.py
+++ b/release/scripts/startup/bl_ui/properties_material.py
@@ -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")
diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py
index 3abaa490d02..02b37eec10f 100644
--- a/release/scripts/startup/bl_ui/properties_render.py
+++ b/release/scripts/startup/bl_ui/properties_render.py
@@ -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,
diff --git a/release/scripts/startup/bl_ui/properties_view_layer.py b/release/scripts/startup/bl_ui/properties_view_layer.py
index 3ced6a31db5..1387b1e7450 100644
--- a/release/scripts/startup/bl_ui/properties_view_layer.py
+++ b/release/scripts/startup/bl_ui/properties_view_layer.py
@@ -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):
diff --git a/source/blender/blenkernel/BKE_world.h b/source/blender/blenkernel/BKE_world.h
index bbab6fa2712..fe67a0388dc 100644
--- a/source/blender/blenkernel/BKE_world.h
+++ b/source/blender/blenkernel/BKE_world.h
@@ -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
diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c
index b2e90b7ba12..d4f0e016697 100644
--- a/source/blender/blenkernel/intern/world.c
+++ b/source/blender/blenkernel/intern/world.c
@@ -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);
+}
diff --git a/source/blender/blenlib/BLI_assert.h b/source/blender/blenlib/BLI_assert.h
index ad5e2b29766..71b00b77998 100644
--- a/source/blender/blenlib/BLI_assert.h
+++ b/source/blender/blenlib/BLI_assert.h
@@ -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.
diff --git a/source/blender/blenlib/BLI_int2.hh b/source/blender/blenlib/BLI_int2.hh
new file mode 100644
index 00000000000..b710105be17
--- /dev/null
+++ b/source/blender/blenlib/BLI_int2.hh
@@ -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
diff --git a/source/blender/blenlib/BLI_int3.hh b/source/blender/blenlib/BLI_int3.hh
new file mode 100644
index 00000000000..4891584d505
--- /dev/null
+++ b/source/blender/blenlib/BLI_int3.hh
@@ -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
diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c
index 3022dbbe4ff..5893f909183 100644
--- a/source/blender/blenlib/intern/math_vector_inline.c
+++ b/source/blender/blenlib/intern/math_vector_inline.c
@@ -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];
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index ceddc451a46..8d478b1e1c1 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -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);
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index 888bd244007..e158838008b 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -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;
+ }
+ }
}
}
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 0c5b158ac80..f8741ea48c4 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -51,6 +51,25 @@ set(INC
${OPENSUBDIV_INCLUDE_DIRS}
)
+set(EEVEE_BUILD_DIR
+ ${CMAKE_CURRENT_BINARY_DIR}/engines/eevee
+)
+
+set(EEVEE_BUILD_SRC
+ engines/eevee/eevee_build.cc
+ engines/eevee/eevee_lut.c
+)
+add_executable(eevee_build ${EEVEE_BUILD_SRC})
+target_include_directories(eevee_build PRIVATE ${INC})
+
+add_custom_command(
+ OUTPUT
+ ${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_lib.glsl
+ COMMAND
+ "$<TARGET_FILE:eevee_build>" --resolve_sample_table ${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_lib.glsl
+ DEPENDS eevee_build
+)
+
set(SRC
intern/draw_cache.c
intern/draw_cache_extract_mesh.cc
@@ -118,33 +137,33 @@ set(SRC
engines/basic/basic_shader.c
engines/image/image_engine.cc
engines/image/image_shader.cc
- engines/eevee/eevee_bloom.c
- engines/eevee/eevee_cryptomatte.c
- engines/eevee/eevee_data.c
- engines/eevee/eevee_depth_of_field.c
- engines/eevee/eevee_effects.c
+ engines/eevee/eevee_camera.cc
+ engines/eevee/eevee_depth_of_field.cc
engines/eevee/eevee_engine.c
- engines/eevee/eevee_lightcache.c
- engines/eevee/eevee_lightprobes.c
- engines/eevee/eevee_lights.c
- engines/eevee/eevee_lookdev.c
+ engines/eevee/eevee_engine.cc
+ engines/eevee/eevee_film.cc
+ engines/eevee/eevee_gpencil.cc
+ engines/eevee/eevee_hair.cc
+ engines/eevee/eevee_hizbuffer.cc
+ engines/eevee/eevee_id_map.cc
+ engines/eevee/eevee_instance.cc
+ engines/eevee/eevee_light.cc
+ engines/eevee/eevee_lightcache.cc
+ engines/eevee/eevee_lightprobe.cc
+ engines/eevee/eevee_lookdev.cc
engines/eevee/eevee_lut.c
- engines/eevee/eevee_lut_gen.c
- engines/eevee/eevee_materials.c
- engines/eevee/eevee_mist.c
- engines/eevee/eevee_motion_blur.c
- engines/eevee/eevee_occlusion.c
- engines/eevee/eevee_render.c
- engines/eevee/eevee_renderpasses.c
- engines/eevee/eevee_sampling.c
- engines/eevee/eevee_screen_raytrace.c
- engines/eevee/eevee_shaders.c
- engines/eevee/eevee_shadows.c
- engines/eevee/eevee_shadows_cascade.c
- engines/eevee/eevee_shadows_cube.c
- engines/eevee/eevee_subsurface.c
- engines/eevee/eevee_temporal_sampling.c
- engines/eevee/eevee_volumes.c
+ engines/eevee/eevee_material.cc
+ engines/eevee/eevee_mesh.cc
+ engines/eevee/eevee_motion_blur.cc
+ engines/eevee/eevee_raytracing.cc
+ engines/eevee/eevee_renderpasses.cc
+ engines/eevee/eevee_shader.cc
+ engines/eevee/eevee_shading.cc
+ engines/eevee/eevee_shadow.cc
+ engines/eevee/eevee_subsurface.cc
+ engines/eevee/eevee_velocity.cc
+ engines/eevee/eevee_view.cc
+ engines/eevee/eevee_world.cc
engines/workbench/workbench_data.c
engines/workbench/workbench_effect_antialiasing.c
engines/workbench/workbench_effect_cavity.c
@@ -224,6 +243,10 @@ set(SRC
engines/basic/basic_private.h
engines/eevee/eevee_engine.h
engines/eevee/eevee_lightcache.h
+ engines/eevee/eevee_camera.hh
+ engines/eevee/eevee_instance.hh
+ engines/eevee/eevee_light.hh
+ engines/eevee/eevee_depth_of_field.hh
engines/eevee/eevee_lut.h
engines/eevee/eevee_private.h
engines/external/external_engine.h
@@ -248,95 +271,119 @@ 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
+ ${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_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_camera_velocity_frag.glsl
+ engines/eevee/shaders/eevee_closure_lib.glsl
+ engines/eevee/shaders/eevee_cubemap_lib.glsl
+ engines/eevee/shaders/eevee_culling_debug_frag.glsl
+ engines/eevee/shaders/eevee_culling_iter_lib.glsl
+ engines/eevee/shaders/eevee_culling_lib.glsl
+ engines/eevee/shaders/eevee_culling_select_comp.glsl
+ engines/eevee/shaders/eevee_culling_sort_comp.glsl
+ engines/eevee/shaders/eevee_culling_tile_comp.glsl
+ engines/eevee/shaders/eevee_deferred_direct_frag.glsl
+ engines/eevee/shaders/eevee_deferred_holdout_frag.glsl
+ engines/eevee/shaders/eevee_deferred_transparent_frag.glsl
+ engines/eevee/shaders/eevee_deferred_volume_frag.glsl
+ engines/eevee/shaders/eevee_depth_clear_frag.glsl
+ engines/eevee/shaders/eevee_hiz_copy_frag.glsl
+ engines/eevee/shaders/eevee_hiz_downsample_frag.glsl
+ engines/eevee/shaders/eevee_depth_of_field_accumulator_lib.glsl
+ engines/eevee/shaders/eevee_depth_of_field_bokeh_lut_frag.glsl
+ engines/eevee/shaders/eevee_depth_of_field_filter_frag.glsl
+ engines/eevee/shaders/eevee_depth_of_field_gather_frag.glsl
+ engines/eevee/shaders/eevee_depth_of_field_gather_holefill_frag.glsl
+ engines/eevee/shaders/eevee_depth_of_field_lib.glsl
+ engines/eevee/shaders/eevee_depth_of_field_reduce_copy_frag.glsl
+ engines/eevee/shaders/eevee_depth_of_field_reduce_downsample_frag.glsl
+ engines/eevee/shaders/eevee_depth_of_field_reduce_recursive_frag.glsl
+ engines/eevee/shaders/eevee_depth_of_field_resolve_frag.glsl
+ engines/eevee/shaders/eevee_depth_of_field_scatter_frag.glsl
+ engines/eevee/shaders/eevee_depth_of_field_scatter_lib.glsl
+ engines/eevee/shaders/eevee_depth_of_field_scatter_vert.glsl
+ engines/eevee/shaders/eevee_depth_of_field_setup_frag.glsl
+ engines/eevee/shaders/eevee_depth_of_field_tiles_dilate_frag.glsl
+ engines/eevee/shaders/eevee_depth_of_field_tiles_flatten_frag.glsl
+ engines/eevee/shaders/eevee_film_filter_frag.glsl
+ engines/eevee/shaders/eevee_film_lib.glsl
+ engines/eevee/shaders/eevee_film_resolve_frag.glsl
+ engines/eevee/shaders/eevee_film_resolve_depth_frag.glsl
+ engines/eevee/shaders/eevee_gbuffer_lib.glsl
+ engines/eevee/shaders/eevee_irradiance_lib.glsl
+ engines/eevee/shaders/eevee_light_lib.glsl
+ engines/eevee/shaders/eevee_light_eval_lib.glsl
+ engines/eevee/shaders/eevee_lightprobe_display_cubemap_frag.glsl
+ engines/eevee/shaders/eevee_lightprobe_display_cubemap_vert.glsl
+ engines/eevee/shaders/eevee_lightprobe_display_grid_frag.glsl
+ engines/eevee/shaders/eevee_lightprobe_display_grid_vert.glsl
+ engines/eevee/shaders/eevee_lightprobe_display_lib.glsl
+ engines/eevee/shaders/eevee_lightprobe_eval_cubemap_lib.glsl
+ engines/eevee/shaders/eevee_lightprobe_eval_grid_lib.glsl
+ engines/eevee/shaders/eevee_lightprobe_filter_diffuse_frag.glsl
+ engines/eevee/shaders/eevee_lightprobe_filter_downsample_frag.glsl
+ engines/eevee/shaders/eevee_lightprobe_filter_geom.glsl
+ engines/eevee/shaders/eevee_lightprobe_filter_glossy_frag.glsl
+ engines/eevee/shaders/eevee_lightprobe_filter_lib.glsl
+ engines/eevee/shaders/eevee_lightprobe_filter_vert.glsl
+ engines/eevee/shaders/eevee_lightprobe_filter_visibility_frag.glsl
+ engines/eevee/shaders/eevee_lookdev_background_frag.glsl
+ engines/eevee/shaders/eevee_ltc_lib.glsl
+ engines/eevee/shaders/eevee_motion_blur_gather_frag.glsl
+ engines/eevee/shaders/eevee_motion_blur_lib.glsl
+ engines/eevee/shaders/eevee_motion_blur_tiles_dilate_frag.glsl
+ engines/eevee/shaders/eevee_motion_blur_tiles_flatten_frag.glsl
+ engines/eevee/shaders/eevee_nodetree_eval_lib.glsl
+ engines/eevee/shaders/eevee_raytrace_denoise_comp.glsl
+ engines/eevee/shaders/eevee_raytrace_raygen_frag.glsl
+ engines/eevee/shaders/eevee_raytrace_raygen_lib.glsl
+ engines/eevee/shaders/eevee_raytrace_resolve_frag.glsl
+ engines/eevee/shaders/eevee_raytrace_trace_lib.glsl
+ engines/eevee/shaders/eevee_sampling_lib.glsl
+ engines/eevee/shaders/eevee_shadow_debug_frag.glsl
+ engines/eevee/shaders/eevee_shadow_lib.glsl
+ engines/eevee/shaders/eevee_shadow_page_alloc_comp.glsl
+ engines/eevee/shaders/eevee_shadow_page_copy_comp.glsl
+ engines/eevee/shaders/eevee_shadow_page_debug_comp.glsl
+ engines/eevee/shaders/eevee_shadow_page_defrag_comp.glsl
+ engines/eevee/shaders/eevee_shadow_page_free_comp.glsl
+ engines/eevee/shaders/eevee_shadow_page_init_comp.glsl
+ engines/eevee/shaders/eevee_shadow_page_lib.glsl
+ engines/eevee/shaders/eevee_shadow_page_mark_vert.glsl
+ engines/eevee/shaders/eevee_shadow_tilemap_depth_scan_comp.glsl
+ engines/eevee/shaders/eevee_shadow_tilemap_lod_mask_comp.glsl
+ engines/eevee/shaders/eevee_shadow_tilemap_lib.glsl
+ engines/eevee/shaders/eevee_shadow_tilemap_setup_comp.glsl
+ engines/eevee/shaders/eevee_shadow_tilemap_tag_comp.glsl
+ engines/eevee/shaders/eevee_shadow_tilemap_visibility_comp.glsl
+ engines/eevee/shaders/eevee_subsurface_eval_frag.glsl
+ engines/eevee/shaders/eevee_surface_background_frag.glsl
+ engines/eevee/shaders/eevee_surface_deferred_frag.glsl
+ engines/eevee/shaders/eevee_surface_depth_frag.glsl
+ engines/eevee/shaders/eevee_surface_depth_simple_frag.glsl
+ engines/eevee/shaders/eevee_surface_forward_frag.glsl
+ engines/eevee/shaders/eevee_surface_gpencil_vert.glsl
+ engines/eevee/shaders/eevee_surface_hair_vert.glsl
+ engines/eevee/shaders/eevee_surface_lib.glsl
+ engines/eevee/shaders/eevee_surface_lookdev_vert.glsl
+ engines/eevee/shaders/eevee_surface_mesh_geom.glsl
+ engines/eevee/shaders/eevee_surface_mesh_vert.glsl
+ engines/eevee/shaders/eevee_surface_velocity_frag.glsl
+ engines/eevee/shaders/eevee_surface_velocity_lib.glsl
+ engines/eevee/shaders/eevee_surface_velocity_mesh_vert.glsl
+ engines/eevee/shaders/eevee_surface_world_vert.glsl
+ engines/eevee/shaders/eevee_velocity_lib.glsl
+ engines/eevee/shaders/eevee_volume_deferred_frag.glsl
+ engines/eevee/shaders/eevee_volume_eval_lib.glsl
+ engines/eevee/shaders/eevee_volume_lib.glsl
+ engines/eevee/shaders/eevee_volume_vert.glsl
+
+ engines/eevee/eevee_shader_shared.hh
engines/workbench/shaders/workbench_cavity_lib.glsl
engines/workbench/shaders/workbench_common_lib.glsl
@@ -368,19 +415,24 @@ set(GLSL_SRC
engines/workbench/workbench_shader_shared.h
+ intern/shaders/common_attribute_lib.glsl
intern/shaders/common_colormanagement_lib.glsl
+ intern/shaders/common_debug_lib.glsl
+ intern/shaders/common_fullscreen_vert.glsl
+ intern/shaders/common_fxaa_lib.glsl
intern/shaders/common_globals_lib.glsl
- intern/shaders/common_pointcloud_lib.glsl
+ intern/shaders/common_gpencil_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_hair_refine_vert.glsl
+ intern/shaders/common_intersection_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_math_lib.glsl
+ intern/shaders/common_obinfos_lib.glsl
+ intern/shaders/common_pointcloud_lib.glsl
intern/shaders/common_smaa_lib.glsl
- intern/shaders/common_fullscreen_vert.glsl
+ intern/shaders/common_uniform_attribute_lib.glsl
+ intern/shaders/common_view_lib.glsl
intern/shaders/common_subdiv_custom_data_interp_comp.glsl
intern/shaders/common_subdiv_ibo_lines_comp.glsl
diff --git a/source/blender/draw/engines/eevee/eevee_allocator.hh b/source/blender/draw/engines/eevee/eevee_allocator.hh
new file mode 100644
index 00000000000..5aa4bf2569c
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_allocator.hh
@@ -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
diff --git a/source/blender/draw/engines/eevee/eevee_bloom.c b/source/blender/draw/engines/eevee/eevee_bloom.c
deleted file mode 100644
index cc6e75859c8..00000000000
--- a/source/blender/draw/engines/eevee/eevee_bloom.c
+++ /dev/null
@@ -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);
- }
-}
diff --git a/source/blender/draw/engines/eevee/eevee_build.cc b/source/blender/draw/engines/eevee/eevee_build.cc
new file mode 100644
index 00000000000..59be843fcbb
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_build.cc
@@ -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 vec3[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<vec3, 4> pools_color = {1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0};
+ vector<vec3> debug_image(64 * 64);
+
+ ofstream ppm;
+ auto ppm_file_out = [&](const char *name, vector<vec3> &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 vec2 resolve_sample_offsets[" << total << "] = vec2[" << 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 << " vec2(" << 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;
+}
diff --git a/source/blender/draw/engines/eevee/eevee_camera.cc b/source/blender/draw/engines/eevee/eevee_camera.cc
new file mode 100644
index 00000000000..3af221e8689
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_camera.cc
@@ -0,0 +1,167 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ */
+
+#include <array>
+
+#include "DRW_render.h"
+
+#include "DNA_camera_types.h"
+#include "DNA_view3d_types.h"
+
+#include "BKE_camera.h"
+#include "DEG_depsgraph_query.h"
+#include "RE_pipeline.h"
+
+#include "eevee_camera.hh"
+#include "eevee_instance.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name eCameraType
+ * \{ */
+
+static eCameraType from_camera(const ::Camera *camera)
+{
+ switch (camera->type) {
+ default:
+ case CAM_PERSP:
+ return CAMERA_PERSP;
+ case CAM_ORTHO:
+ return CAMERA_ORTHO;
+ case CAM_PANO:
+ switch (camera->panorama_type) {
+ default:
+ case CAM_PANO_EQUIRECTANGULAR:
+ return CAMERA_PANO_EQUIRECT;
+ case CAM_PANO_FISHEYE_EQUIDISTANT:
+ return CAMERA_PANO_EQUIDISTANT;
+ case CAM_PANO_FISHEYE_EQUISOLID:
+ return CAMERA_PANO_EQUISOLID;
+ case CAM_PANO_MIRRORBALL:
+ return CAMERA_PANO_MIRROR;
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Camera
+ * \{ */
+
+void Camera::init(void)
+{
+ const Object *camera_eval = inst_.camera_eval_object;
+ synced_ = false;
+ /* Swap! */
+ data_id_ = !data_id_;
+
+ CameraData &data = data_[data_id_];
+
+ if (camera_eval) {
+ const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data);
+ data.type = from_camera(cam);
+ }
+ else if (inst_.drw_view) {
+ data.type = DRW_view_is_persp_get(inst_.drw_view) ? CAMERA_PERSP : CAMERA_ORTHO;
+ }
+ else {
+ /* Lightprobe baking. */
+ data.type = CAMERA_PERSP;
+ }
+}
+
+void Camera::sync(void)
+{
+ const Object *camera_eval = inst_.camera_eval_object;
+ CameraData &data = data_[data_id_];
+
+ data.filter_size = inst_.scene->r.gauss;
+
+ if (inst_.drw_view) {
+ DRW_view_viewmat_get(inst_.drw_view, data.viewmat, false);
+ DRW_view_viewmat_get(inst_.drw_view, data.viewinv, true);
+ DRW_view_winmat_get(inst_.drw_view, data.winmat, false);
+ DRW_view_winmat_get(inst_.drw_view, data.wininv, true);
+ DRW_view_persmat_get(inst_.drw_view, data.persmat, false);
+ DRW_view_persmat_get(inst_.drw_view, data.persinv, true);
+ DRW_view_camtexco_get(inst_.drw_view, data.uv_scale);
+ }
+ else if (inst_.render) {
+ /* TODO(fclem) Overscan */
+ // RE_GetCameraWindowWithOverscan(inst_.render->re, g_data->overscan, data.winmat);
+ RE_GetCameraWindow(inst_.render->re, camera_eval, data.winmat);
+ RE_GetCameraModelMatrix(inst_.render->re, camera_eval, data.viewinv);
+ invert_m4_m4(data.viewmat, data.viewinv);
+ invert_m4_m4(data.wininv, data.winmat);
+ mul_m4_m4m4(data.persmat, data.winmat, data.viewmat);
+ invert_m4_m4(data.persinv, data.persmat);
+ data.uv_scale = vec2(1.0f);
+ data.uv_bias = vec2(0.0f);
+ }
+ else {
+ unit_m4(data.viewmat);
+ unit_m4(data.viewinv);
+ perspective_m4(data.winmat, -0.1f, 0.1f, -0.1f, 0.1f, 0.1f, 1.0f);
+ invert_m4_m4(data.wininv, data.winmat);
+ mul_m4_m4m4(data.persmat, data.winmat, data.viewmat);
+ invert_m4_m4(data.persinv, data.persmat);
+ }
+
+ if (camera_eval) {
+ const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data);
+ data.clip_near = cam->clip_start;
+ data.clip_far = cam->clip_end;
+ data.fisheye_fov = cam->fisheye_fov;
+ data.fisheye_lens = cam->fisheye_lens;
+ data.equirect_bias.x = -cam->longitude_min + M_PI_2;
+ data.equirect_bias.y = -cam->latitude_min + M_PI_2;
+ data.equirect_scale.x = cam->longitude_min - cam->longitude_max;
+ data.equirect_scale.y = cam->latitude_min - cam->latitude_max;
+ /* Combine with uv_scale/bias to avoid doing extra computation. */
+ data.equirect_bias += data.uv_bias * data.equirect_scale;
+ data.equirect_scale *= data.uv_scale;
+
+ data.equirect_scale_inv = 1.0f / data.equirect_scale;
+ }
+ else if (inst_.drw_view) {
+ data.clip_near = DRW_view_near_distance_get(inst_.drw_view);
+ data.clip_far = DRW_view_far_distance_get(inst_.drw_view);
+ data.fisheye_fov = data.fisheye_lens = -1.0f;
+ data.equirect_bias = vec2(0.0f);
+ data.equirect_scale = vec2(0.0f);
+ }
+
+ data_[data_id_].push_update();
+
+ synced_ = true;
+
+ /* Detect changes in parameters. */
+ if (data_[data_id_] != data_[!data_id_]) {
+ inst_.sampling.reset();
+ }
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_camera.hh b/source/blender/draw/engines/eevee/eevee_camera.hh
new file mode 100644
index 00000000000..702c73866f8
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_camera.hh
@@ -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(mat4 &winmat, float near, float far)
+{
+ /* Simple 90° FOV projection. */
+ perspective_m4(winmat, -near, near, -near, near, near, far);
+}
+
+/* -------------------------------------------------------------------- */
+/** \name CameraData operators
+ * \{ */
+
+inline bool operator==(const CameraData &a, const CameraData &b)
+{
+ return compare_m4m4(a.persmat, b.persmat, FLT_MIN) && (a.uv_scale == b.uv_scale) &&
+ (a.uv_bias == b.uv_bias) && (a.equirect_scale == b.equirect_scale) &&
+ (a.equirect_bias == b.equirect_bias) && (a.fisheye_fov == b.fisheye_fov) &&
+ (a.fisheye_lens == b.fisheye_lens) && (a.filter_size == b.filter_size) &&
+ (a.type == b.type);
+}
+
+inline bool operator!=(const CameraData &a, const CameraData &b)
+{
+ return !(a == b);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Camera
+ * \{ */
+
+/**
+ * Point of view in the scene. Can be init from viewport or camera object.
+ */
+class Camera {
+ private:
+ Instance &inst_;
+
+ /** Double buffered to detect changes and have history for re-projection. */
+ CameraDataBuf data_[2];
+ /** Active data index in data_. */
+ int data_id_ = 0;
+ /** Detects wrong usage. */
+ bool synced_ = false;
+
+ public:
+ Camera(Instance &inst) : inst_(inst){};
+ ~Camera(){};
+
+ void init(void);
+ void sync(void);
+
+ /**
+ * Getters
+ **/
+ const CameraData &data_get(void) const
+ {
+ BLI_assert(synced_);
+ return data_[data_id_];
+ }
+ const GPUUniformBuf *ubo_get(void) const
+ {
+ return data_[data_id_].ubo_get();
+ }
+ bool is_panoramic(void) const
+ {
+ return eevee::is_panoramic(data_[data_id_].type);
+ }
+ bool is_orthographic(void) const
+ {
+ return data_[data_id_].type == CAMERA_ORTHO;
+ }
+ vec3 position(void) const
+ {
+ return vec3(data_[data_id_].viewinv[3]);
+ }
+};
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
deleted file mode 100644
index 80207523a65..00000000000
--- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c
+++ /dev/null
@@ -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;
- }
-}
diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c
deleted file mode 100644
index b453df284ed..00000000000
--- a/source/blender/draw/engines/eevee/eevee_data.c
+++ /dev/null
@@ -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);
-}
diff --git a/source/blender/draw/engines/eevee/eevee_depth_of_field.c b/source/blender/draw/engines/eevee/eevee_depth_of_field.c
deleted file mode 100644
index 4748323d6a7..00000000000
--- a/source/blender/draw/engines/eevee/eevee_depth_of_field.c
+++ /dev/null
@@ -1,1048 +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
- *
- * 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 "DNA_camera_types.h"
-#include "DNA_screen_types.h"
-#include "DNA_view3d_types.h"
-#include "DNA_world_types.h"
-
-#include "BKE_camera.h"
-
-#include "BLI_string_utils.h"
-
-#include "DEG_depsgraph.h"
-#include "DEG_depsgraph_query.h"
-
-#include "GPU_framebuffer.h"
-#include "GPU_texture.h"
-#include "eevee_private.h"
-
-#define CAMERA_JITTER_RING_DENSITY 6
-
-static float coc_radius_from_camera_depth(bool is_ortho, EEVEE_EffectsInfo *fx, float camera_depth)
-{
- float multiplier = fx->dof_coc_params[0];
- float bias = fx->dof_coc_params[1];
- if (multiplier == 0.0f || bias == 0.0f) {
- return 0.0f;
- }
- if (is_ortho) {
- return (camera_depth + multiplier / bias) * multiplier;
- }
- return multiplier / camera_depth - bias;
-}
-
-static float polygon_sides_length(float sides_count)
-{
- return 2.0 * sin(M_PI / sides_count);
-}
-
-/* Returns intersection ratio between the radius edge at theta and the polygon edge.
- * Start first corners at theta == 0. */
-static 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.
- * Expect theta to be in [0..2pi] range. */
-static 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 = polygon_sides_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;
-}
-
-static int dof_jitter_total_sample_count(int ring_density, int ring_count)
-{
- return ((ring_count * ring_count + ring_count) / 2) * ring_density + 1;
-}
-
-bool EEVEE_depth_of_field_jitter_get(EEVEE_EffectsInfo *fx,
- float r_jitter[2],
- float *r_focus_distance)
-{
- if (fx->dof_jitter_radius == 0.0f) {
- return false;
- }
-
- int ring_density = CAMERA_JITTER_RING_DENSITY;
- int ring_count = fx->dof_jitter_ring_count;
- int sample_count = dof_jitter_total_sample_count(ring_density, ring_count);
-
- int s = fx->taa_current_sample - 1;
-
- int ring = 0;
- int ring_sample_count = 1;
- int ring_sample = 1;
-
- s = s * (ring_density - 1);
- s = s % sample_count;
-
- int samples_passed = 1;
- while (s >= samples_passed) {
- ring++;
- ring_sample_count = ring * ring_density;
- ring_sample = s - samples_passed;
- ring_sample = (ring_sample + 1) % ring_sample_count;
- samples_passed += ring_sample_count;
- }
-
- r_jitter[0] = (float)ring / ring_count;
- r_jitter[1] = (float)ring_sample / ring_sample_count;
-
- {
- /* Bokeh shape parameterization. */
- float r = r_jitter[0];
- float T = r_jitter[1] * 2.0f * M_PI;
-
- if (fx->dof_jitter_blades >= 3.0f) {
- T = circle_to_polygon_angle(fx->dof_jitter_blades, T);
- r *= circle_to_polygon_radius(fx->dof_jitter_blades, T);
- }
-
- T += fx->dof_bokeh_rotation;
-
- r_jitter[0] = r * cosf(T);
- r_jitter[1] = r * sinf(T);
-
- mul_v2_v2(r_jitter, fx->dof_bokeh_aniso);
- }
-
- mul_v2_fl(r_jitter, fx->dof_jitter_radius);
-
- *r_focus_distance = fx->dof_jitter_focus;
- return true;
-}
-
-int EEVEE_depth_of_field_sample_count_get(EEVEE_EffectsInfo *effects,
- int sample_count,
- int *r_ring_count)
-{
- if (effects->dof_jitter_radius == 0.0f) {
- if (r_ring_count != NULL) {
- *r_ring_count = 0;
- }
- return 1;
- }
-
- if (sample_count == TAA_MAX_SAMPLE) {
- /* Special case for viewport continuous rendering. We clamp to a max sample to avoid the
- * jittered dof never converging. */
- sample_count = 1024;
- }
- /* Inversion of dof_jitter_total_sample_count. */
- float x = 2.0f * (sample_count - 1.0f) / CAMERA_JITTER_RING_DENSITY;
- /* Solving polynomial. We only search positive solution. */
- float discriminant = 1.0f + 4.0f * x;
- int ring_count = ceilf(0.5f * (sqrt(discriminant) - 1.0f));
-
- sample_count = dof_jitter_total_sample_count(CAMERA_JITTER_RING_DENSITY, ring_count);
-
- if (r_ring_count != NULL) {
- *r_ring_count = ring_count;
- }
- return sample_count;
-}
-
-int EEVEE_depth_of_field_init(EEVEE_ViewLayerData *UNUSED(sldata),
- EEVEE_Data *vedata,
- Object *camera)
-{
- EEVEE_TextureList *txl = vedata->txl;
- 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);
-
- Camera *cam = (camera != NULL) ? camera->data : NULL;
-
- if (cam && (cam->dof.flag & CAM_DOF_ENABLED)) {
- RegionView3D *rv3d = draw_ctx->rv3d;
- const float *viewport_size = DRW_viewport_size_get();
-
- effects->dof_hq_slight_focus = (scene_eval->eevee.flag & SCE_EEVEE_DOF_HQ_SLIGHT_FOCUS) != 0;
-
- /* Retrieve Near and Far distance */
- effects->dof_coc_near_dist = -cam->clip_start;
- effects->dof_coc_far_dist = -cam->clip_end;
-
- /* Parameters */
- bool is_ortho = cam->type == CAM_ORTHO;
- float fstop = cam->dof.aperture_fstop;
- float blades = cam->dof.aperture_blades;
- float rotation = cam->dof.aperture_rotation;
- float ratio = 1.0f / max_ff(cam->dof.aperture_ratio, 0.00001f);
- float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y);
- float focus_dist = BKE_camera_object_dof_distance(camera);
- float focal_len = cam->lens;
-
- if (is_ortho) {
- /* (fclem) A bit of black magic here. I don't know if this is correct. */
- fstop *= 1.3f;
- focal_len = 1.0f;
- sensor = cam->ortho_scale;
- }
-
- const float scale_camera = (is_ortho) ? 1.0 : 0.001f;
- /* We want radius here for the aperture number. */
- float aperture = 0.5f * scale_camera * focal_len / fstop;
- float focal_len_scaled = scale_camera * focal_len;
- float sensor_scaled = scale_camera * sensor;
-
- if (rv3d != NULL) {
- sensor_scaled *= rv3d->viewcamtexcofac[0];
- }
-
- if (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 *= ratio;
- }
-
- effects->dof_coc_params[1] = -aperture *
- fabsf(focal_len_scaled / (focus_dist - focal_len_scaled));
- /* FIXME(fclem) This is broken for vertically fit sensor. */
- effects->dof_coc_params[1] *= viewport_size[0] / sensor_scaled;
-
- if ((scene_eval->eevee.flag & SCE_EEVEE_DOF_JITTER) != 0) {
- effects->dof_jitter_radius = effects->dof_coc_params[1];
- effects->dof_jitter_focus = focus_dist;
- effects->dof_jitter_blades = blades;
-
- int sample_count = EEVEE_temporal_sampling_sample_count_get(scene_eval, stl);
- sample_count = EEVEE_depth_of_field_sample_count_get(
- effects, sample_count, &effects->dof_jitter_ring_count);
-
- if (effects->dof_jitter_ring_count == 0) {
- effects->dof_jitter_radius = 0.0f;
- }
- else {
- /* 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(sample_count);
- float user_overblur = scene_eval->eevee.bokeh_overblur / 100.0f;
-
- minimal_overblur *= effects->dof_coc_params[1];
- user_overblur *= effects->dof_coc_params[1];
-
- effects->dof_coc_params[1] = minimal_overblur + user_overblur;
- /* Avoid dilating the shape. Over-blur only soften. */
- effects->dof_jitter_radius -= minimal_overblur + user_overblur * 0.5f;
- }
- }
- else {
- effects->dof_jitter_radius = 0.0f;
- }
-
- if (is_ortho) {
- /* (fclem) A bit of black magic here. Needed to match cycles. */
- effects->dof_coc_params[1] *= 0.225;
- }
-
- effects->dof_coc_params[0] = -focus_dist * effects->dof_coc_params[1];
-
- effects->dof_bokeh_blades = blades;
- effects->dof_bokeh_rotation = rotation;
- effects->dof_bokeh_aniso[0] = min_ff(ratio, 1.0f);
- effects->dof_bokeh_aniso[1] = min_ff(1.0f / ratio, 1.0f);
- effects->dof_bokeh_max_size = scene_eval->eevee.bokeh_max_size;
-
- copy_v2_v2(effects->dof_bokeh_aniso_inv, effects->dof_bokeh_aniso);
- invert_v2(effects->dof_bokeh_aniso_inv);
-
- effects->dof_scatter_color_threshold = scene_eval->eevee.bokeh_threshold;
- effects->dof_scatter_neighbor_max_color = scene_eval->eevee.bokeh_neighbor_max;
- effects->dof_denoise_factor = clamp_f(scene_eval->eevee.bokeh_denoise_fac, 0.0f, 1.0f);
-
- float max_abs_fg_coc, max_abs_bg_coc;
- if (is_ortho) {
- max_abs_fg_coc = fabsf(coc_radius_from_camera_depth(true, effects, -cam->clip_start));
- max_abs_bg_coc = fabsf(coc_radius_from_camera_depth(true, effects, -cam->clip_end));
- }
- else {
- max_abs_fg_coc = fabsf(coc_radius_from_camera_depth(false, effects, -cam->clip_start));
- /* Background is at infinity so maximum CoC is the limit of the function at -inf. */
- max_abs_bg_coc = fabsf(effects->dof_coc_params[1]);
- }
-
- float max_coc = max_ff(max_abs_bg_coc, max_abs_fg_coc);
- /* Clamp with user defined max. */
- effects->dof_fx_max_coc = min_ff(scene_eval->eevee.bokeh_max_size, max_coc);
-
- if (effects->dof_fx_max_coc < 0.5f) {
- return 0;
- }
-
- return EFFECT_DOF | EFFECT_POST_BUFFER;
- }
-
- effects->dof_jitter_radius = 0.0f;
-
- /* Cleanup to release memory */
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_setup_fb);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_flatten_tiles_fb);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_dilate_tiles_fb);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_reduce_fb);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_reduce_copy_fb);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_gather_fg_fb);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_gather_bg_fb);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_scatter_bg_fb);
- DRW_TEXTURE_FREE_SAFE(txl->dof_reduced_color);
- DRW_TEXTURE_FREE_SAFE(txl->dof_reduced_coc);
-
- return 0;
-}
-
-#define WITH_FILTERING (GPU_SAMPLER_MIPMAP | GPU_SAMPLER_FILTER)
-#define NO_FILTERING GPU_SAMPLER_MIPMAP
-#define COLOR_FORMAT fx->dof_color_format
-#define FG_TILE_FORMAT GPU_RGBA16F
-#define BG_TILE_FORMAT GPU_R11F_G11F_B10F
-
-/**
- * Create bokeh texture.
- */
-static void dof_bokeh_pass_init(EEVEE_FramebufferList *fbl,
- EEVEE_PassList *psl,
- EEVEE_EffectsInfo *fx)
-{
- if ((fx->dof_bokeh_aniso[0] == 1.0f) && (fx->dof_bokeh_aniso[1] == 1.0f) &&
- (fx->dof_bokeh_blades == 0.0)) {
- fx->dof_bokeh_gather_lut_tx = NULL;
- fx->dof_bokeh_scatter_lut_tx = NULL;
- fx->dof_bokeh_resolve_lut_tx = NULL;
- return;
- }
-
- void *owner = (void *)&EEVEE_depth_of_field_init;
- int res[2] = {DOF_BOKEH_LUT_SIZE, DOF_BOKEH_LUT_SIZE};
-
- DRW_PASS_CREATE(psl->dof_bokeh, DRW_STATE_WRITE_COLOR);
-
- GPUShader *sh = EEVEE_shaders_depth_of_field_bokeh_get();
- DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_bokeh);
- DRW_shgroup_uniform_float_copy(grp, "bokehSides", fx->dof_bokeh_blades);
- DRW_shgroup_uniform_float_copy(grp, "bokehRotation", fx->dof_bokeh_rotation);
- DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropyInv", fx->dof_bokeh_aniso_inv);
- DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
-
- fx->dof_bokeh_gather_lut_tx = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RG16F, owner);
- fx->dof_bokeh_scatter_lut_tx = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
- fx->dof_bokeh_resolve_lut_tx = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
-
- GPU_framebuffer_ensure_config(&fbl->dof_bokeh_fb,
- {
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(fx->dof_bokeh_gather_lut_tx),
- GPU_ATTACHMENT_TEXTURE(fx->dof_bokeh_scatter_lut_tx),
- GPU_ATTACHMENT_TEXTURE(fx->dof_bokeh_resolve_lut_tx),
- });
-}
-
-/**
- * Outputs halfResColorBuffer and halfResCocBuffer.
- */
-static void dof_setup_pass_init(EEVEE_FramebufferList *fbl,
- EEVEE_PassList *psl,
- EEVEE_EffectsInfo *fx)
-{
- DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
-
- void *owner = (void *)&EEVEE_depth_of_field_init;
- const float *fullres = DRW_viewport_size_get();
- int res[2] = {divide_ceil_u(fullres[0], 2), divide_ceil_u(fullres[1], 2)};
-
- DRW_PASS_CREATE(psl->dof_setup, DRW_STATE_WRITE_COLOR);
-
- GPUShader *sh = EEVEE_shaders_depth_of_field_setup_get();
- DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_setup);
- DRW_shgroup_uniform_texture_ref_ex(grp, "colorBuffer", &fx->source_buffer, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &dtxl->depth, NO_FILTERING);
- DRW_shgroup_uniform_vec4_copy(grp, "cocParams", fx->dof_coc_params);
- DRW_shgroup_uniform_float_copy(grp, "bokehMaxSize", fx->dof_bokeh_max_size);
- DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
-
- fx->dof_half_res_color_tx = DRW_texture_pool_query_2d(UNPACK2(res), COLOR_FORMAT, owner);
- fx->dof_half_res_coc_tx = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RG16F, owner);
-
- GPU_framebuffer_ensure_config(&fbl->dof_setup_fb,
- {
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(fx->dof_half_res_color_tx),
- GPU_ATTACHMENT_TEXTURE(fx->dof_half_res_coc_tx),
- });
-}
-
-/**
- * Outputs min & max COC in each 8x8 half res pixel tiles (so 1/16th of full resolution).
- */
-static void dof_flatten_tiles_pass_init(EEVEE_FramebufferList *fbl,
- EEVEE_PassList *psl,
- EEVEE_EffectsInfo *fx)
-{
- void *owner = (void *)&EEVEE_depth_of_field_init;
- const float *fullres = DRW_viewport_size_get();
- int res[2] = {divide_ceil_u(fullres[0], DOF_TILE_DIVISOR),
- divide_ceil_u(fullres[1], DOF_TILE_DIVISOR)};
-
- DRW_PASS_CREATE(psl->dof_flatten_tiles, DRW_STATE_WRITE_COLOR);
-
- GPUShader *sh = EEVEE_shaders_depth_of_field_flatten_tiles_get();
- DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_flatten_tiles);
- DRW_shgroup_uniform_texture_ref_ex(
- grp, "halfResCocBuffer", &fx->dof_half_res_coc_tx, NO_FILTERING);
- DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
-
- fx->dof_coc_tiles_fg_tx = DRW_texture_pool_query_2d(UNPACK2(res), FG_TILE_FORMAT, owner);
- fx->dof_coc_tiles_bg_tx = DRW_texture_pool_query_2d(UNPACK2(res), BG_TILE_FORMAT, owner);
-
- GPU_framebuffer_ensure_config(&fbl->dof_flatten_tiles_fb,
- {
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(fx->dof_coc_tiles_fg_tx),
- GPU_ATTACHMENT_TEXTURE(fx->dof_coc_tiles_bg_tx),
- });
-}
-
-/**
- * Dilates the min & max COCS to cover maximum COC values.
- * Output format/dimensions should be the same as coc_flatten_pass as they are swapped for
- * doing multiple dilation passes.
- */
-static void dof_dilate_tiles_pass_init(EEVEE_FramebufferList *fbl,
- EEVEE_PassList *psl,
- EEVEE_EffectsInfo *fx)
-{
- void *owner = (void *)&EEVEE_depth_of_field_init;
- const float *fullres = DRW_viewport_size_get();
- int res[2] = {divide_ceil_u(fullres[0], DOF_TILE_DIVISOR),
- divide_ceil_u(fullres[1], DOF_TILE_DIVISOR)};
-
- DRW_PASS_CREATE(psl->dof_dilate_tiles_minmax, DRW_STATE_WRITE_COLOR);
- DRW_PASS_CREATE(psl->dof_dilate_tiles_minabs, DRW_STATE_WRITE_COLOR);
-
- for (int pass = 0; pass < 2; pass++) {
- DRWPass *drw_pass = (pass == 0) ? psl->dof_dilate_tiles_minmax : psl->dof_dilate_tiles_minabs;
- GPUShader *sh = EEVEE_shaders_depth_of_field_dilate_tiles_get(pass);
- DRWShadingGroup *grp = DRW_shgroup_create(sh, drw_pass);
- DRW_shgroup_uniform_texture_ref(grp, "cocTilesFgBuffer", &fx->dof_coc_tiles_fg_tx);
- DRW_shgroup_uniform_texture_ref(grp, "cocTilesBgBuffer", &fx->dof_coc_tiles_bg_tx);
- DRW_shgroup_uniform_bool(grp, "dilateSlightFocus", &fx->dof_dilate_slight_focus, 1);
- DRW_shgroup_uniform_int(grp, "ringCount", &fx->dof_dilate_ring_count, 1);
- DRW_shgroup_uniform_int(grp, "ringWidthMultiplier", &fx->dof_dilate_ring_width_multiplier, 1);
- DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
- }
-
- fx->dof_coc_dilated_tiles_fg_tx = DRW_texture_pool_query_2d(UNPACK2(res), FG_TILE_FORMAT, owner);
- fx->dof_coc_dilated_tiles_bg_tx = DRW_texture_pool_query_2d(UNPACK2(res), BG_TILE_FORMAT, owner);
-
- GPU_framebuffer_ensure_config(&fbl->dof_dilate_tiles_fb,
- {
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(fx->dof_coc_dilated_tiles_fg_tx),
- GPU_ATTACHMENT_TEXTURE(fx->dof_coc_dilated_tiles_bg_tx),
- });
-}
-
-static void dof_dilate_tiles_pass_draw(EEVEE_FramebufferList *fbl,
- EEVEE_PassList *psl,
- EEVEE_EffectsInfo *fx)
-{
- for (int pass = 0; pass < 2; pass++) {
- DRWPass *drw_pass = (pass == 0) ? psl->dof_dilate_tiles_minmax : psl->dof_dilate_tiles_minabs;
-
- /* 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->dof_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. */
- fx->dof_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;
-
- fx->dof_dilate_ring_count = ring_count;
- fx->dof_dilate_ring_width_multiplier = multiplier;
-
- GPU_framebuffer_bind(fbl->dof_dilate_tiles_fb);
- DRW_draw_pass(drw_pass);
-
- SWAP(GPUFrameBuffer *, fbl->dof_dilate_tiles_fb, fbl->dof_flatten_tiles_fb);
- SWAP(GPUTexture *, fx->dof_coc_dilated_tiles_bg_tx, fx->dof_coc_tiles_bg_tx);
- SWAP(GPUTexture *, fx->dof_coc_dilated_tiles_fg_tx, fx->dof_coc_tiles_fg_tx);
- }
- }
- /* Swap again so that final textures are dof_coc_dilated_tiles_*_tx. */
- SWAP(GPUFrameBuffer *, fbl->dof_dilate_tiles_fb, fbl->dof_flatten_tiles_fb);
- SWAP(GPUTexture *, fx->dof_coc_dilated_tiles_bg_tx, fx->dof_coc_tiles_bg_tx);
- SWAP(GPUTexture *, fx->dof_coc_dilated_tiles_fg_tx, fx->dof_coc_tiles_fg_tx);
-}
-
-/**
- * Create mipmapped color & COC textures for gather passes.
- */
-static void dof_reduce_pass_init(EEVEE_FramebufferList *fbl,
- EEVEE_PassList *psl,
- EEVEE_TextureList *txl,
- EEVEE_EffectsInfo *fx)
-{
- const float *fullres = DRW_viewport_size_get();
-
- /* 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->dof_fx_max_coc * 0.5f / DOF_GATHER_RING_COUNT;
-
- int mip_count = max_ii(1, log2_ceil_u(max_space_between_sample));
-
- fx->dof_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);
- int res[2] = {(multiple * divide_ceil_u(fullres[0], multiple)) / 2,
- (multiple * divide_ceil_u(fullres[1], multiple)) / 2};
-
- int quater_res[2] = {divide_ceil_u(fullres[0], 4), divide_ceil_u(fullres[1], 4)};
-
- /* TODO(fclem): Make this dependent of the quality of the gather pass. */
- fx->dof_scatter_coc_threshold = 4.0f;
-
- {
- DRW_PASS_CREATE(psl->dof_downsample, DRW_STATE_WRITE_COLOR);
-
- GPUShader *sh = EEVEE_shaders_depth_of_field_downsample_get();
- DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_downsample);
- DRW_shgroup_uniform_texture_ref_ex(
- grp, "colorBuffer", &fx->dof_reduce_input_color_tx, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref_ex(
- grp, "cocBuffer", &fx->dof_reduce_input_coc_tx, NO_FILTERING);
- DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
-
- void *owner = (void *)&EEVEE_depth_of_field_init;
- fx->dof_downsample_tx = DRW_texture_pool_query_2d(UNPACK2(quater_res), COLOR_FORMAT, owner);
-
- GPU_framebuffer_ensure_config(&fbl->dof_downsample_fb,
- {
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(fx->dof_downsample_tx),
- });
- }
-
- {
- DRW_PASS_CREATE(psl->dof_reduce_copy, DRW_STATE_WRITE_COLOR);
-
- const bool is_copy_pass = true;
- GPUShader *sh = EEVEE_shaders_depth_of_field_reduce_get(is_copy_pass);
- DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_reduce_copy);
- DRW_shgroup_uniform_texture_ref_ex(
- grp, "colorBuffer", &fx->dof_reduce_input_color_tx, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref_ex(
- grp, "cocBuffer", &fx->dof_reduce_input_coc_tx, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref_ex(
- grp, "downsampledBuffer", &fx->dof_downsample_tx, NO_FILTERING);
- DRW_shgroup_uniform_float_copy(grp, "scatterColorThreshold", fx->dof_scatter_color_threshold);
- DRW_shgroup_uniform_float_copy(
- grp, "scatterColorNeighborMax", fx->dof_scatter_neighbor_max_color);
- DRW_shgroup_uniform_float_copy(grp, "scatterCocThreshold", fx->dof_scatter_coc_threshold);
- DRW_shgroup_uniform_float_copy(grp, "colorNeighborClamping", fx->dof_denoise_factor);
- DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropy", fx->dof_bokeh_aniso);
- DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
-
- void *owner = (void *)&EEVEE_depth_of_field_init;
- fx->dof_scatter_src_tx = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R11F_G11F_B10F, owner);
- }
-
- {
- DRW_PASS_CREATE(psl->dof_reduce, DRW_STATE_WRITE_COLOR);
-
- const bool is_copy_pass = false;
- GPUShader *sh = EEVEE_shaders_depth_of_field_reduce_get(is_copy_pass);
- DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_reduce);
- DRW_shgroup_uniform_texture_ref_ex(
- grp, "colorBuffer", &fx->dof_reduce_input_color_tx, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref_ex(
- grp, "cocBuffer", &fx->dof_reduce_input_coc_tx, NO_FILTERING);
- DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
- }
-
- if (txl->dof_reduced_color) {
- /* TODO(fclem) In the future, we need to check if mip_count did not change.
- * For now it's ok as we always define all mip level. */
- if (res[0] != GPU_texture_width(txl->dof_reduced_color) ||
- res[1] != GPU_texture_width(txl->dof_reduced_color)) {
- DRW_TEXTURE_FREE_SAFE(txl->dof_reduced_color);
- DRW_TEXTURE_FREE_SAFE(txl->dof_reduced_coc);
- }
- }
-
- if (txl->dof_reduced_color == NULL) {
- /* Color needs to be signed format here. See note in shader for explanation. */
- /* Do not use texture pool because of needs mipmaps. */
- txl->dof_reduced_color = GPU_texture_create_2d(
- "dof_reduced_color", UNPACK2(res), mip_count, GPU_RGBA16F, NULL);
- txl->dof_reduced_coc = GPU_texture_create_2d(
- "dof_reduced_coc", UNPACK2(res), mip_count, GPU_R16F, NULL);
-
- /* TODO(fclem) Remove once we have immutable storage or when mips are generated on creation. */
- GPU_texture_generate_mipmap(txl->dof_reduced_color);
- GPU_texture_generate_mipmap(txl->dof_reduced_coc);
- }
-
- GPU_framebuffer_ensure_config(&fbl->dof_reduce_fb,
- {
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(txl->dof_reduced_color),
- GPU_ATTACHMENT_TEXTURE(txl->dof_reduced_coc),
- });
-
- GPU_framebuffer_ensure_config(&fbl->dof_reduce_copy_fb,
- {
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(txl->dof_reduced_color),
- GPU_ATTACHMENT_TEXTURE(txl->dof_reduced_coc),
- GPU_ATTACHMENT_TEXTURE(fx->dof_scatter_src_tx),
- });
-}
-
-/**
- * Do the gather convolution. For each pixels we gather multiple pixels in its neighborhood
- * depending on the min & max CoC tiles.
- */
-static void dof_gather_pass_init(EEVEE_FramebufferList *fbl,
- EEVEE_PassList *psl,
- EEVEE_TextureList *txl,
- EEVEE_EffectsInfo *fx)
-{
- void *owner = (void *)&EEVEE_depth_of_field_init;
- const float *fullres = DRW_viewport_size_get();
- int res[2] = {divide_ceil_u(fullres[0], 2), divide_ceil_u(fullres[1], 2)};
- int input_size[2];
- GPU_texture_get_mipmap_size(txl->dof_reduced_color, 0, input_size);
- float uv_correction_fac[2] = {res[0] / (float)input_size[0], res[1] / (float)input_size[1]};
- float output_texel_size[2] = {1.0f / res[0], 1.0f / res[1]};
- const bool use_bokeh_tx = (fx->dof_bokeh_gather_lut_tx != NULL);
-
- {
- DRW_PASS_CREATE(psl->dof_gather_fg_holefill, DRW_STATE_WRITE_COLOR);
-
- GPUShader *sh = EEVEE_shaders_depth_of_field_gather_get(DOF_GATHER_HOLEFILL, false);
- DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_gather_fg_holefill);
- DRW_shgroup_uniform_texture_ref_ex(
- grp, "colorBufferBilinear", &txl->dof_reduced_color, WITH_FILTERING);
- DRW_shgroup_uniform_texture_ref_ex(grp, "colorBuffer", &txl->dof_reduced_color, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref_ex(grp, "cocBuffer", &txl->dof_reduced_coc, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref(grp, "cocTilesFgBuffer", &fx->dof_coc_dilated_tiles_fg_tx);
- DRW_shgroup_uniform_texture_ref(grp, "cocTilesBgBuffer", &fx->dof_coc_dilated_tiles_bg_tx);
- DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
- DRW_shgroup_uniform_vec2_copy(grp, "gatherInputUvCorrection", uv_correction_fac);
- DRW_shgroup_uniform_vec2_copy(grp, "gatherOutputTexelSize", output_texel_size);
- 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). */
- fx->dof_fg_holefill_color_tx = fx->dof_half_res_color_tx;
- fx->dof_fg_holefill_weight_tx = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
-
- GPU_framebuffer_ensure_config(&fbl->dof_gather_fg_holefill_fb,
- {
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(fx->dof_fg_holefill_color_tx),
- GPU_ATTACHMENT_TEXTURE(fx->dof_fg_holefill_weight_tx),
- });
- }
- {
- DRW_PASS_CREATE(psl->dof_gather_fg, DRW_STATE_WRITE_COLOR);
-
- GPUShader *sh = EEVEE_shaders_depth_of_field_gather_get(DOF_GATHER_FOREGROUND, use_bokeh_tx);
- DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_gather_fg);
- DRW_shgroup_uniform_texture_ref_ex(
- grp, "colorBufferBilinear", &txl->dof_reduced_color, WITH_FILTERING);
- DRW_shgroup_uniform_texture_ref_ex(grp, "colorBuffer", &txl->dof_reduced_color, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref_ex(grp, "cocBuffer", &txl->dof_reduced_coc, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref(grp, "cocTilesFgBuffer", &fx->dof_coc_dilated_tiles_fg_tx);
- DRW_shgroup_uniform_texture_ref(grp, "cocTilesBgBuffer", &fx->dof_coc_dilated_tiles_bg_tx);
- DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
- DRW_shgroup_uniform_vec2_copy(grp, "gatherInputUvCorrection", uv_correction_fac);
- DRW_shgroup_uniform_vec2_copy(grp, "gatherOutputTexelSize", output_texel_size);
- if (use_bokeh_tx) {
- /* Negate to flip bokeh shape. Mimics optical phenomenon. */
- negate_v2(fx->dof_bokeh_aniso);
- DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropy", fx->dof_bokeh_aniso);
- DRW_shgroup_uniform_texture_ref(grp, "bokehLut", &fx->dof_bokeh_gather_lut_tx);
- /* Restore. */
- negate_v2(fx->dof_bokeh_aniso);
- }
- DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
-
- fx->dof_fg_color_tx = DRW_texture_pool_query_2d(UNPACK2(res), COLOR_FORMAT, owner);
- fx->dof_fg_weight_tx = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
- /* 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). */
- fx->dof_fg_occlusion_tx = fx->dof_half_res_coc_tx;
-
- /* NOTE: First target is holefill texture so we can use the median filter on it.
- * See the filter function. */
- GPU_framebuffer_ensure_config(&fbl->dof_gather_fg_fb,
- {
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(fx->dof_fg_holefill_color_tx),
- GPU_ATTACHMENT_TEXTURE(fx->dof_fg_holefill_weight_tx),
- GPU_ATTACHMENT_TEXTURE(fx->dof_fg_occlusion_tx),
- });
- }
- {
- DRW_PASS_CREATE(psl->dof_gather_bg, DRW_STATE_WRITE_COLOR);
-
- GPUShader *sh = EEVEE_shaders_depth_of_field_gather_get(DOF_GATHER_BACKGROUND, use_bokeh_tx);
- DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_gather_bg);
- DRW_shgroup_uniform_texture_ref_ex(
- grp, "colorBufferBilinear", &txl->dof_reduced_color, WITH_FILTERING);
- DRW_shgroup_uniform_texture_ref_ex(grp, "colorBuffer", &txl->dof_reduced_color, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref_ex(grp, "cocBuffer", &txl->dof_reduced_coc, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref(grp, "cocTilesFgBuffer", &fx->dof_coc_dilated_tiles_fg_tx);
- DRW_shgroup_uniform_texture_ref(grp, "cocTilesBgBuffer", &fx->dof_coc_dilated_tiles_bg_tx);
- DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
- DRW_shgroup_uniform_vec2_copy(grp, "gatherInputUvCorrection", uv_correction_fac);
- DRW_shgroup_uniform_vec2_copy(grp, "gatherOutputTexelSize", output_texel_size);
- if (use_bokeh_tx) {
- DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropy", fx->dof_bokeh_aniso);
- DRW_shgroup_uniform_texture_ref(grp, "bokehLut", &fx->dof_bokeh_gather_lut_tx);
- }
- DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
-
- fx->dof_bg_color_tx = DRW_texture_pool_query_2d(UNPACK2(res), COLOR_FORMAT, owner);
- fx->dof_bg_weight_tx = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
- /* Reuse, since only used for scatter. Foreground is processed before background. */
- fx->dof_bg_occlusion_tx = fx->dof_fg_occlusion_tx;
-
- /* NOTE: First target is holefill texture so we can use the median filter on it.
- * See the filter function. */
- GPU_framebuffer_ensure_config(&fbl->dof_gather_bg_fb,
- {
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(fx->dof_fg_holefill_color_tx),
- GPU_ATTACHMENT_TEXTURE(fx->dof_fg_holefill_weight_tx),
- GPU_ATTACHMENT_TEXTURE(fx->dof_bg_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.
- */
-static void dof_filter_pass_init(EEVEE_FramebufferList *fbl,
- EEVEE_PassList *psl,
- EEVEE_EffectsInfo *fx)
-{
- DRW_PASS_CREATE(psl->dof_filter, DRW_STATE_WRITE_COLOR);
-
- GPUShader *sh = EEVEE_shaders_depth_of_field_filter_get();
- DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_filter);
- DRW_shgroup_uniform_texture_ref_ex(
- grp, "colorBuffer", &fx->dof_fg_holefill_color_tx, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref_ex(
- grp, "weightBuffer", &fx->dof_fg_holefill_weight_tx, NO_FILTERING);
- DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
-
- GPU_framebuffer_ensure_config(&fbl->dof_filter_fg_fb,
- {
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(fx->dof_fg_color_tx),
- GPU_ATTACHMENT_TEXTURE(fx->dof_fg_weight_tx),
- });
-
- GPU_framebuffer_ensure_config(&fbl->dof_filter_bg_fb,
- {
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(fx->dof_bg_color_tx),
- GPU_ATTACHMENT_TEXTURE(fx->dof_bg_weight_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.
- */
-static void dof_scatter_pass_init(EEVEE_FramebufferList *fbl,
- EEVEE_PassList *psl,
- EEVEE_TextureList *txl,
- EEVEE_EffectsInfo *fx)
-{
- int input_size[2], target_size[2];
- GPU_texture_get_mipmap_size(fx->dof_half_res_color_tx, 0, input_size);
- GPU_texture_get_mipmap_size(fx->dof_bg_color_tx, 0, target_size);
- /* Draw a sprite for every four half-res pixels. */
- int sprite_count = (input_size[0] / 2) * (input_size[1] / 2);
- float target_texel_size[2] = {1.0f / target_size[0], 1.0f / target_size[1]};
- const bool use_bokeh_tx = (fx->dof_bokeh_gather_lut_tx != NULL);
-
- {
- DRW_PASS_CREATE(psl->dof_scatter_fg, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
-
- const bool is_foreground = true;
- GPUShader *sh = EEVEE_shaders_depth_of_field_scatter_get(is_foreground, use_bokeh_tx);
- DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_scatter_fg);
- DRW_shgroup_uniform_texture_ref_ex(grp, "colorBuffer", &fx->dof_scatter_src_tx, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref_ex(grp, "cocBuffer", &txl->dof_reduced_coc, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref(grp, "occlusionBuffer", &fx->dof_fg_occlusion_tx);
- DRW_shgroup_uniform_vec2_copy(grp, "targetTexelSize", target_texel_size);
- DRW_shgroup_uniform_int_copy(grp, "spritePerRow", input_size[0] / 2);
- DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropy", fx->dof_bokeh_aniso);
- if (use_bokeh_tx) {
- /* Negate to flip bokeh shape. Mimics optical phenomenon. */
- negate_v2(fx->dof_bokeh_aniso_inv);
- DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropyInv", fx->dof_bokeh_aniso_inv);
- DRW_shgroup_uniform_texture_ref(grp, "bokehLut", &fx->dof_bokeh_scatter_lut_tx);
- /* Restore. */
- negate_v2(fx->dof_bokeh_aniso_inv);
- }
- DRW_shgroup_call_procedural_triangles(grp, NULL, sprite_count);
-
- GPU_framebuffer_ensure_config(&fbl->dof_scatter_fg_fb,
- {
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(fx->dof_fg_color_tx),
- });
- }
- {
- DRW_PASS_CREATE(psl->dof_scatter_bg, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
-
- const bool is_foreground = false;
- GPUShader *sh = EEVEE_shaders_depth_of_field_scatter_get(is_foreground, use_bokeh_tx);
- DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_scatter_bg);
- DRW_shgroup_uniform_texture_ref_ex(grp, "colorBuffer", &fx->dof_scatter_src_tx, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref_ex(grp, "cocBuffer", &txl->dof_reduced_coc, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref(grp, "occlusionBuffer", &fx->dof_bg_occlusion_tx);
- DRW_shgroup_uniform_vec2_copy(grp, "targetTexelSize", target_texel_size);
- DRW_shgroup_uniform_int_copy(grp, "spritePerRow", input_size[0] / 2);
- DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropy", fx->dof_bokeh_aniso);
- if (use_bokeh_tx) {
- DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropyInv", fx->dof_bokeh_aniso_inv);
- DRW_shgroup_uniform_texture_ref(grp, "bokehLut", &fx->dof_bokeh_scatter_lut_tx);
- }
- DRW_shgroup_call_procedural_triangles(grp, NULL, sprite_count);
-
- GPU_framebuffer_ensure_config(&fbl->dof_scatter_bg_fb,
- {
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(fx->dof_bg_color_tx),
- });
- }
-}
-
-/**
- * Recombine the result of the foreground and background processing. Also perform a slight out of
- * focus blur to improve geometric continuity.
- */
-static void dof_recombine_pass_init(EEVEE_FramebufferList *UNUSED(fbl),
- EEVEE_PassList *psl,
- EEVEE_EffectsInfo *fx)
-{
- DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- const bool use_bokeh_tx = (fx->dof_bokeh_gather_lut_tx != NULL);
-
- DRW_PASS_CREATE(psl->dof_resolve, DRW_STATE_WRITE_COLOR);
-
- GPUShader *sh = EEVEE_shaders_depth_of_field_resolve_get(use_bokeh_tx, fx->dof_hq_slight_focus);
- DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_resolve);
- DRW_shgroup_uniform_texture_ref_ex(grp, "fullResColorBuffer", &fx->source_buffer, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref_ex(grp, "fullResDepthBuffer", &dtxl->depth, NO_FILTERING);
- DRW_shgroup_uniform_texture_ref(grp, "bgColorBuffer", &fx->dof_bg_color_tx);
- DRW_shgroup_uniform_texture_ref(grp, "bgWeightBuffer", &fx->dof_bg_weight_tx);
- DRW_shgroup_uniform_texture_ref(grp, "bgTileBuffer", &fx->dof_coc_dilated_tiles_bg_tx);
- DRW_shgroup_uniform_texture_ref(grp, "fgColorBuffer", &fx->dof_fg_color_tx);
- DRW_shgroup_uniform_texture_ref(grp, "fgWeightBuffer", &fx->dof_fg_weight_tx);
- DRW_shgroup_uniform_texture_ref(grp, "holefillColorBuffer", &fx->dof_fg_holefill_color_tx);
- DRW_shgroup_uniform_texture_ref(grp, "holefillWeightBuffer", &fx->dof_fg_holefill_weight_tx);
- DRW_shgroup_uniform_texture_ref(grp, "fgTileBuffer", &fx->dof_coc_dilated_tiles_fg_tx);
- DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
- DRW_shgroup_uniform_vec4_copy(grp, "cocParams", fx->dof_coc_params);
- DRW_shgroup_uniform_float_copy(grp, "bokehMaxSize", fx->dof_bokeh_max_size);
- if (use_bokeh_tx) {
- DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropyInv", fx->dof_bokeh_aniso_inv);
- DRW_shgroup_uniform_texture_ref(grp, "bokehLut", &fx->dof_bokeh_resolve_lut_tx);
- }
- DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
-}
-
-void EEVEE_depth_of_field_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
-{
- EEVEE_TextureList *txl = vedata->txl;
- EEVEE_FramebufferList *fbl = vedata->fbl;
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *fx = stl->effects;
-
- if ((fx->enabled_effects & EFFECT_DOF) != 0) {
- /* GPU_RGBA16F is sufficient now that all scattered bokeh are premultiplied.
- * GPU_R11F_G11F_B10F is not enough when lots of scattered sprites are big and offers
- * relatively small benefits. */
- fx->dof_color_format = GPU_RGBA16F;
-
- dof_bokeh_pass_init(fbl, psl, fx);
- dof_setup_pass_init(fbl, psl, fx);
- dof_flatten_tiles_pass_init(fbl, psl, fx);
- dof_dilate_tiles_pass_init(fbl, psl, fx);
- dof_reduce_pass_init(fbl, psl, txl, fx);
- dof_gather_pass_init(fbl, psl, txl, fx);
- dof_filter_pass_init(fbl, psl, fx);
- dof_scatter_pass_init(fbl, psl, txl, fx);
- dof_recombine_pass_init(fbl, psl, fx);
- }
-}
-
-static void dof_recursive_reduce(void *vedata, int UNUSED(level))
-{
- EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
- EEVEE_TextureList *txl = ((EEVEE_Data *)vedata)->txl;
- EEVEE_EffectsInfo *fx = ((EEVEE_Data *)vedata)->stl->effects;
-
- fx->dof_reduce_input_color_tx = txl->dof_reduced_color;
- fx->dof_reduce_input_coc_tx = txl->dof_reduced_coc;
-
- DRW_draw_pass(psl->dof_reduce);
-}
-
-void EEVEE_depth_of_field_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; /* TODO(fclem): Because of silly SWAP_BUFFERS. */
- EEVEE_EffectsInfo *fx = effects;
-
- /* Depth Of Field */
- if ((effects->enabled_effects & EFFECT_DOF) != 0) {
- DRW_stats_group_start("Depth of Field");
-
- if (fx->dof_bokeh_gather_lut_tx != NULL) {
- GPU_framebuffer_bind(fbl->dof_bokeh_fb);
- DRW_draw_pass(psl->dof_bokeh);
- }
-
- GPU_framebuffer_bind(fbl->dof_setup_fb);
- DRW_draw_pass(psl->dof_setup);
-
- GPU_framebuffer_bind(fbl->dof_flatten_tiles_fb);
- DRW_draw_pass(psl->dof_flatten_tiles);
-
- dof_dilate_tiles_pass_draw(fbl, psl, fx);
-
- fx->dof_reduce_input_color_tx = fx->dof_half_res_color_tx;
- fx->dof_reduce_input_coc_tx = fx->dof_half_res_coc_tx;
-
- /* First step is just a copy. */
- GPU_framebuffer_bind(fbl->dof_downsample_fb);
- DRW_draw_pass(psl->dof_downsample);
-
- /* First step is just a copy. */
- GPU_framebuffer_bind(fbl->dof_reduce_copy_fb);
- DRW_draw_pass(psl->dof_reduce_copy);
-
- GPU_framebuffer_recursive_downsample(
- fbl->dof_reduce_fb, fx->dof_reduce_steps, &dof_recursive_reduce, vedata);
-
- {
- /* Foreground convolution. */
- GPU_framebuffer_bind(fbl->dof_gather_fg_fb);
- DRW_draw_pass(psl->dof_gather_fg);
-
- GPU_framebuffer_bind(fbl->dof_filter_fg_fb);
- DRW_draw_pass(psl->dof_filter);
-
- GPU_framebuffer_bind(fbl->dof_scatter_fg_fb);
- DRW_draw_pass(psl->dof_scatter_fg);
- }
-
- {
- /* Background convolution. */
- GPU_framebuffer_bind(fbl->dof_gather_bg_fb);
- DRW_draw_pass(psl->dof_gather_bg);
-
- GPU_framebuffer_bind(fbl->dof_filter_bg_fb);
- DRW_draw_pass(psl->dof_filter);
-
- GPU_framebuffer_bind(fbl->dof_scatter_bg_fb);
- DRW_draw_pass(psl->dof_scatter_bg);
- }
-
- {
- /* Hole-fill convolution. */
- GPU_framebuffer_bind(fbl->dof_gather_fg_holefill_fb);
- DRW_draw_pass(psl->dof_gather_fg_holefill);
-
- /* NOTE: do not filter the hole-fill pass as we use it as out filter input buffer. */
- }
-
- GPU_framebuffer_bind(fx->target_buffer);
- DRW_draw_pass(psl->dof_resolve);
-
- SWAP_BUFFERS();
-
- DRW_stats_group_end();
- }
-}
diff --git a/source/blender/draw/engines/eevee/eevee_depth_of_field.cc b/source/blender/draw/engines/eevee/eevee_depth_of_field.cc
new file mode 100644
index 00000000000..131b80fbd83
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_depth_of_field.cc
@@ -0,0 +1,731 @@
+/*
+ * 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 mat4 winmat, ivec2 input_extent)
+{
+ const Object *camera_object_eval = inst_.camera_eval_object;
+ const ::Camera *cam = (camera_object_eval) ?
+ reinterpret_cast<const ::Camera *>(camera_object_eval->data) :
+ nullptr;
+
+ if (cam == nullptr || (cam->dof.flag & CAM_DOF_ENABLED) == 0) {
+ fx_radius_ = 0.0f;
+ jitter_radius_ = 0.0f;
+ return;
+ }
+
+ extent_ = input_extent;
+
+ data_.bokeh_blades = cam->dof.aperture_blades;
+ data_.bokeh_rotation = cam->dof.aperture_rotation;
+ data_.bokeh_anisotropic_scale.x = clamp_f(1.0f / cam->dof.aperture_ratio, 0.00001f, 1.0f);
+ data_.bokeh_anisotropic_scale.y = clamp_f(cam->dof.aperture_ratio, 0.00001f, 1.0f);
+ data_.bokeh_anisotropic_scale_inv = 1.0f / data_.bokeh_anisotropic_scale;
+
+ focus_distance_ = BKE_camera_object_dof_distance(camera_object_eval);
+ float fstop = max_ff(cam->dof.aperture_fstop, 1e-5f);
+ float aperture = 1.0f / (2.0f * fstop);
+ if (cam->type == CAM_PERSP) {
+ aperture *= cam->lens * 1e-3f;
+ }
+
+ if (cam->type == CAM_ORTHO) {
+ /* FIXME: Why is this needed? Some kind of implicit unit conversion? */
+ aperture *= 0.04f;
+ /* Really strange behavior from Cycles but replicating. */
+ focus_distance_ += cam->clip_start;
+ }
+
+ if (cam->type == CAM_PANO) {
+ /* FIXME: Eyeballed. */
+ aperture *= 0.185f;
+ }
+
+ if (cam->dof.aperture_ratio < 1.0) {
+ /* If ratio is scaling the bokeh outwards, we scale the aperture so that
+ * the gather kernel size will encompass the maximum axis. */
+ aperture /= max_ff(cam->dof.aperture_ratio, 1e-5f);
+ }
+
+ /* Balance blur radius between fx dof and jitter dof. */
+ if (do_jitter_ && (inst_.sampling.dof_ring_count_get() > 0) && (cam->type != CAM_PANO)) {
+ /* Compute a minimal overblur radius to fill the gaps between the samples.
+ * This is just the simplified form of dividing the area of the bokeh by
+ * the number of samples. */
+ float minimal_overblur = 1.0f / sqrtf(inst_.sampling.dof_sample_count_get());
+
+ fx_radius_ = (minimal_overblur + user_overblur_) * aperture;
+ /* Avoid dilating the shape. Over-blur only soften. */
+ jitter_radius_ = max_ff(0.0f, aperture - fx_radius_);
+ }
+ else {
+ jitter_radius_ = 0.0f;
+ fx_radius_ = aperture;
+ }
+
+ if (fx_max_coc_ > 0.0f && fx_radius_ > 0.0f) {
+ data_.camera_type = inst_.camera.data_get().type;
+ /* OPTI(fclem) Could be optimized. */
+ float jitter[3] = {fx_radius_, 0.0f, -focus_distance_};
+ float center[3] = {0.0f, 0.0f, -focus_distance_};
+ mul_project_m4_v3(winmat, jitter);
+ mul_project_m4_v3(winmat, center);
+ /* Simplify CoC calculation to a simple MADD. */
+ if (data_.camera_type != CAMERA_ORTHO) {
+ data_.coc_bias = -(center[0] - jitter[0]) * 0.5f * extent_[0];
+ data_.coc_mul = focus_distance_ * data_.coc_bias;
+ }
+ else {
+ data_.coc_mul = (center[0] - jitter[0]) * 0.5f * extent_[0];
+ data_.coc_bias = focus_distance_ * data_.coc_mul;
+ }
+
+ float min_fg_coc = coc_radius_from_camera_depth(data_, -cam->clip_start);
+ float max_bg_coc = coc_radius_from_camera_depth(data_, -cam->clip_end);
+ if (data_.camera_type != CAMERA_ORTHO) {
+ /* Background is at infinity so maximum CoC is the limit of the function at -inf. */
+ /* NOTE: we only do this for perspective camera since orthographic coc limit is inf. */
+ max_bg_coc = data_.coc_bias;
+ }
+ /* Clamp with user defined max. */
+ data_.coc_abs_max = min_ff(max_ff(fabsf(min_fg_coc), fabsf(max_bg_coc)), fx_max_coc_);
+
+ bokeh_lut_pass_sync();
+ setup_pass_sync();
+ tiles_prepare_pass_sync();
+ reduce_pass_sync();
+ convolve_pass_sync();
+ resolve_pass_sync();
+
+ data_.push_update();
+ }
+}
+
+void DepthOfField::jitter_apply(mat4 winmat, mat4 viewmat)
+{
+ if (jitter_radius_ == 0.0f) {
+ return;
+ }
+ float radius, theta;
+ inst_.sampling.dof_disk_sample_get(&radius, &theta);
+
+ if (data_.bokeh_blades >= 3.0f) {
+ theta = circle_to_polygon_angle(data_.bokeh_blades, theta);
+ radius *= circle_to_polygon_radius(data_.bokeh_blades, theta);
+ }
+ radius *= jitter_radius_;
+ theta += data_.bokeh_rotation;
+
+ /* Sample in View Space. */
+ vec2 sample = vec2(radius * cosf(theta), radius * sinf(theta));
+ sample *= data_.bokeh_anisotropic_scale;
+ /* Convert to NDC Space. */
+ vec3 jitter = vec3(UNPACK2(sample), -focus_distance_);
+ vec3 center = vec3(0.0f, 0.0f, -focus_distance_);
+ mul_project_m4_v3(winmat, jitter);
+ mul_project_m4_v3(winmat, center);
+
+ const bool is_ortho = (winmat[2][3] != -1.0f);
+ if (is_ortho) {
+ sample *= focus_distance_;
+ }
+ /* Translate origin. */
+ sub_v2_v2(viewmat[3], sample);
+ /* Skew winmat Z axis. */
+ add_v2_v2(winmat[2], center - jitter);
+}
+
+/** Will swap input and output texture if rendering happens. The actual output of this function
+ * is in intput_tx. */
+void DepthOfField::render(GPUTexture *depth_tx, GPUTexture **input_tx, GPUTexture **output_tx)
+{
+ if (fx_radius_ == 0.0f || fx_max_coc_ < 0.5f) {
+ return;
+ }
+
+ input_color_tx_ = *input_tx;
+ input_depth_tx_ = depth_tx;
+
+ DRW_stats_group_start("Depth of Field");
+
+ bokeh_lut_pass_render();
+
+ setup_pass_render();
+ tiles_prepare_pass_render();
+ reduce_pass_render();
+ convolve_pass_render();
+ resolve_pass_render(*output_tx);
+
+ DRW_stats_group_end();
+
+ /* Swap buffers so that next effect has the right input. */
+ *input_tx = *output_tx;
+ *output_tx = input_color_tx_;
+}
+
+/**
+ * Creates bokeh texture.
+ **/
+void DepthOfField::bokeh_lut_pass_sync(void)
+{
+ const bool has_anisotropy = data_.bokeh_anisotropic_scale != vec2(1.0f);
+ if (has_anisotropy && (data_.bokeh_blades == 0.0)) {
+ bokeh_gather_lut_tx_ = nullptr;
+ bokeh_scatter_lut_tx_ = nullptr;
+ bokeh_resolve_lut_tx_ = nullptr;
+ bokeh_lut_ps_ = nullptr;
+ return;
+ }
+
+ DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
+ int res[2] = {DOF_BOKEH_LUT_SIZE, DOF_BOKEH_LUT_SIZE};
+
+ DRW_PASS_CREATE(bokeh_lut_ps_, DRW_STATE_WRITE_COLOR);
+ GPUShader *sh = inst_.shaders.static_shader_get(DOF_BOKEH_LUT);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, bokeh_lut_ps_);
+ DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+
+ bokeh_gather_lut_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RG16F, owner);
+ bokeh_scatter_lut_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
+ bokeh_resolve_lut_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
+
+ bokeh_lut_fb_.ensure(GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(bokeh_gather_lut_tx_),
+ GPU_ATTACHMENT_TEXTURE(bokeh_scatter_lut_tx_),
+ GPU_ATTACHMENT_TEXTURE(bokeh_resolve_lut_tx_));
+}
+
+void DepthOfField::bokeh_lut_pass_render(void)
+{
+ if (bokeh_lut_ps_) {
+ GPU_framebuffer_bind(bokeh_lut_fb_);
+ DRW_draw_pass(bokeh_lut_ps_);
+ }
+}
+
+/**
+ * Outputs halfResColorBuffer and halfResCocBuffer.
+ **/
+void DepthOfField::setup_pass_sync(void)
+{
+ DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
+ uint res[2] = {divide_ceil_u(extent_[0], 2), divide_ceil_u(extent_[1], 2)};
+
+ DRW_PASS_CREATE(setup_ps_, DRW_STATE_WRITE_COLOR);
+ GPUShader *sh = inst_.shaders.static_shader_get(DOF_SETUP);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, setup_ps_);
+
+ eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
+ DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &input_color_tx_, no_filter);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &input_depth_tx_, no_filter);
+ DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+
+ setup_color_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
+ setup_coc_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RG16F, owner);
+
+ setup_fb_.ensure(GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(setup_color_tx_),
+ GPU_ATTACHMENT_TEXTURE(setup_coc_tx_));
+}
+
+void DepthOfField::setup_pass_render(void)
+{
+ GPU_framebuffer_bind(setup_fb_);
+ DRW_draw_pass(setup_ps_);
+}
+
+/**
+ * Outputs min & max COC in each 8x8 half res pixel tiles (so 1/16th of full resolution).
+ * Then dilates the min & max CoCs to cover maximum COC values.
+ **/
+void DepthOfField::tiles_prepare_pass_sync(void)
+{
+ DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
+ uint res[2] = {divide_ceil_u(extent_[0], DOF_TILE_DIVISOR),
+ divide_ceil_u(extent_[1], DOF_TILE_DIVISOR)};
+ /* WARNING: If you change this, make sure dof_tile_* GLSL constants can be properly encoded. */
+ eGPUTextureFormat fg_tile_format = GPU_RGBA16F;
+ eGPUTextureFormat bg_tile_format = GPU_R11F_G11F_B10F;
+
+ eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
+ {
+ DRW_PASS_CREATE(tiles_flatten_ps_, DRW_STATE_WRITE_COLOR);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(DOF_TILES_FLATTEN);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_flatten_ps_);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter);
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+
+ tiles_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), fg_tile_format, owner);
+ tiles_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), bg_tile_format, owner);
+
+ tiles_flatten_fb_.ensure(GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(tiles_fg_tx_),
+ GPU_ATTACHMENT_TEXTURE(tiles_bg_tx_));
+ }
+ {
+ DRW_PASS_CREATE(tiles_dilate_minmax_ps_, DRW_STATE_WRITE_COLOR);
+ DRW_PASS_CREATE(tiles_dilate_minabs_ps_, DRW_STATE_WRITE_COLOR);
+
+ for (int pass = 0; pass < 2; pass++) {
+ DRWPass *drw_pass = (pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_;
+ GPUShader *sh = inst_.shaders.static_shader_get((pass == 0) ? DOF_TILES_DILATE_MINMAX :
+ DOF_TILES_DILATE_MINABS);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, drw_pass);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_fg_tx", &tiles_fg_tx_, no_filter);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_bg_tx", &tiles_bg_tx_, no_filter);
+ DRW_shgroup_uniform_bool(grp, "dilate_slight_focus", &tiles_dilate_slight_focus_, 1);
+ DRW_shgroup_uniform_int(grp, "ring_count", &tiles_dilate_ring_count_, 1);
+ DRW_shgroup_uniform_int(
+ grp, "ring_width_multiplier", &tiles_dilate_ring_width_multiplier_, 1);
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+ }
+
+ tiles_dilated_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), fg_tile_format, owner);
+ tiles_dilated_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), bg_tile_format, owner);
+
+ tiles_dilate_fb_.ensure(GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(tiles_dilated_fg_tx_),
+ GPU_ATTACHMENT_TEXTURE(tiles_dilated_bg_tx_));
+ }
+}
+
+void DepthOfField::tiles_prepare_pass_render(void)
+{
+ GPU_framebuffer_bind(tiles_flatten_fb_);
+ DRW_draw_pass(tiles_flatten_ps_);
+
+ /* Run dilation twice. One for minmax and one for minabs. */
+ for (int pass = 0; pass < 2; pass++) {
+ /* Error introduced by gather center jittering. */
+ const float error_multiplier = 1.0f + 1.0f / (DOF_GATHER_RING_COUNT + 0.5f);
+ int dilation_end_radius = ceilf((fx_max_coc_ * error_multiplier) / DOF_TILE_DIVISOR);
+
+ /* This algorithm produce the exact dilation radius by dividing it in multiple passes. */
+ int dilation_radius = 0;
+ while (dilation_radius < dilation_end_radius) {
+ /* Dilate slight focus only on first iteration. */
+ tiles_dilate_slight_focus_ = (dilation_radius == 0) ? 1 : 0;
+
+ int remainder = dilation_end_radius - dilation_radius;
+ /* Do not step over any unvisited tile. */
+ int max_multiplier = dilation_radius + 1;
+
+ int ring_count = min_ii(DOF_DILATE_RING_COUNT, ceilf(remainder / (float)max_multiplier));
+ int multiplier = min_ii(max_multiplier, floor(remainder / (float)ring_count));
+
+ dilation_radius += ring_count * multiplier;
+
+ tiles_dilate_ring_count_ = ring_count;
+ tiles_dilate_ring_width_multiplier_ = multiplier;
+
+ GPU_framebuffer_bind(tiles_dilate_fb_);
+ DRW_draw_pass((pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_);
+
+ SWAP(eevee::Framebuffer, tiles_dilate_fb_, tiles_flatten_fb_);
+ SWAP(GPUTexture *, tiles_dilated_bg_tx_, tiles_bg_tx_);
+ SWAP(GPUTexture *, tiles_dilated_fg_tx_, tiles_fg_tx_);
+ }
+ }
+ /* Swap again so that final textures are tiles_dilated_*_tx_. */
+ SWAP(eevee::Framebuffer, tiles_dilate_fb_, tiles_flatten_fb_);
+ SWAP(GPUTexture *, tiles_dilated_bg_tx_, tiles_bg_tx_);
+ SWAP(GPUTexture *, tiles_dilated_fg_tx_, tiles_fg_tx_);
+}
+
+/**
+ * Create mipmapped color & COC textures for gather passes.
+ **/
+void DepthOfField::reduce_pass_sync(void)
+{
+ DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
+ eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
+ /* Divide by 2 because dof_fx_max_coc is in fullres CoC radius and the reduce
+ * texture begins at half resolution. */
+ float max_space_between_sample = fx_max_coc_ * 0.5f / DOF_GATHER_RING_COUNT;
+
+ int mip_count = max_ii(1, log2_ceil_u(max_space_between_sample));
+
+ reduce_steps_ = mip_count - 1;
+ /* This ensure the mipmaps are aligned for the needed 4 mip levels.
+ * Starts at 2 because already at half resolution. */
+ int multiple = 2 << (mip_count - 1);
+ uint res[2] = {(multiple * divide_ceil_u(extent_[0], multiple)) / 2,
+ (multiple * divide_ceil_u(extent_[1], multiple)) / 2};
+
+ uint quater_res[2] = {divide_ceil_u(extent_[0], 4), divide_ceil_u(extent_[1], 4)};
+
+ /* TODO(fclem): Make this dependent of the quality of the gather pass. */
+ data_.scatter_coc_threshold = 4.0f;
+
+ /* Color needs to be signed format here. See note in shader for explanation. */
+ /* Do not use texture pool because of needs mipmaps. */
+ reduced_color_tx_.ensure(UNPACK2(res), mip_count, GPU_RGBA16F);
+ reduced_coc_tx_.ensure(UNPACK2(res), mip_count, GPU_R16F);
+
+ {
+ DRW_PASS_CREATE(reduce_downsample_ps_, DRW_STATE_WRITE_COLOR);
+ GPUShader *sh = inst_.shaders.static_shader_get(DOF_REDUCE_DOWNSAMPLE);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, reduce_downsample_ps_);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &setup_color_tx_, no_filter);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter);
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+
+ reduce_downsample_tx_ = DRW_texture_pool_query_2d(UNPACK2(quater_res), GPU_RGBA16F, owner);
+
+ reduce_downsample_fb_.ensure(GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(reduce_downsample_tx_));
+ }
+ {
+ DRW_PASS_CREATE(reduce_copy_ps_, DRW_STATE_WRITE_COLOR);
+ GPUShader *sh = inst_.shaders.static_shader_get(DOF_REDUCE_COPY);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, reduce_copy_ps_);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &setup_color_tx_, no_filter);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter);
+ DRW_shgroup_uniform_texture_ex(grp, "downsampled_tx", reduce_downsample_tx_, no_filter);
+ DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+
+ scatter_src_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R11F_G11F_B10F, owner);
+ }
+ {
+ DRW_PASS_CREATE(reduce_recursive_ps_, DRW_STATE_WRITE_COLOR);
+ GPUShader *sh = inst_.shaders.static_shader_get(DOF_REDUCE_RECURSIVE);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, reduce_recursive_ps_);
+ DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter);
+ DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+ }
+
+ reduce_fb_.ensure(GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(reduced_color_tx_),
+ GPU_ATTACHMENT_TEXTURE(reduced_coc_tx_));
+
+ reduce_copy_fb_.ensure(GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(reduced_color_tx_),
+ GPU_ATTACHMENT_TEXTURE(reduced_coc_tx_),
+ GPU_ATTACHMENT_TEXTURE(scatter_src_tx_));
+}
+
+void DepthOfField::reduce_recusive(void *thunk, int UNUSED(level))
+{
+ DepthOfField *dof = reinterpret_cast<DepthOfField *>(thunk);
+ DRW_draw_pass(dof->reduce_recursive_ps_);
+}
+
+void DepthOfField::reduce_pass_render(void)
+{
+ GPU_framebuffer_bind(reduce_downsample_fb_);
+ DRW_draw_pass(reduce_downsample_ps_);
+
+ /* First step is just a copy. */
+ GPU_framebuffer_bind(reduce_copy_fb_);
+ DRW_draw_pass(reduce_copy_ps_);
+
+ GPU_framebuffer_recursive_downsample(reduce_fb_, reduce_steps_, &reduce_recusive, this);
+}
+
+/**
+ * Do the gather & scatter convolution. For each pixels we gather multiple pixels in its
+ * neighborhood depending on the min & max CoC tiles. We apply a median filter on the output.
+ * We also scatter a sprite for very bright pixels for high quality bokeh.
+ **/
+void DepthOfField::convolve_pass_sync(void)
+{
+ eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
+ eGPUSamplerState with_filter = (GPU_SAMPLER_MIPMAP | GPU_SAMPLER_FILTER);
+ DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
+ uint res[2] = {divide_ceil_u(extent_[0], 2), divide_ceil_u(extent_[1], 2)};
+ int input_size[2];
+ GPU_texture_get_mipmap_size(reduced_color_tx_, 0, input_size);
+ for (int i = 0; i < 2; i++) {
+ data_.gather_uv_fac[i] = 1.0f / (float)input_size[i];
+ data_.texel_size[i] = 1.0f / res[i];
+ }
+
+ /* Reuse textures from the setup pass. */
+ /* NOTE: We could use the texture pool do that for us but it does not track
+ * usage and it might backfire (it does in practice). */
+ /* Since it is only used for scatter, and foreground is processed before background, we can
+ * reuse the occlusion_tx for both field. */
+ occlusion_tx_ = setup_coc_tx_;
+
+ {
+ DRW_PASS_CREATE(gather_holefill_ps_, DRW_STATE_WRITE_COLOR);
+ GPUShader *sh = inst_.shaders.static_shader_get(DOF_GATHER_HOLEFILL);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_holefill_ps_);
+ DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, with_filter);
+ DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter);
+ DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
+ DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_);
+ DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_);
+ DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
+ DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
+ DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
+
+ /* Reuse textures from the setup pass. */
+ /* NOTE: We could use the texture pool do that for us but it does not track
+ * usage and it might backfire (it does in practice). */
+ color_holefill_tx_ = setup_color_tx_;
+ weight_holefill_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
+
+ gather_holefill_fb_.ensure(GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(color_holefill_tx_),
+ GPU_ATTACHMENT_TEXTURE(weight_holefill_tx_));
+ }
+ {
+ DRW_PASS_CREATE(gather_fg_ps_, DRW_STATE_WRITE_COLOR);
+ GPUShader *sh = inst_.shaders.static_shader_get(
+ bokeh_gather_lut_tx_ ? DOF_GATHER_FOREGROUND_LUT : DOF_GATHER_FOREGROUND);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_fg_ps_);
+ DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, with_filter);
+ DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter);
+ DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
+ DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_);
+ DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_);
+ if (bokeh_gather_lut_tx_) {
+ DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_gather_lut_tx_);
+ }
+ DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
+ DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
+ DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
+
+ color_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
+ weight_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
+ }
+ {
+ DRW_PASS_CREATE(gather_bg_ps_, DRW_STATE_WRITE_COLOR);
+ GPUShader *sh = inst_.shaders.static_shader_get(
+ bokeh_gather_lut_tx_ ? DOF_GATHER_BACKGROUND_LUT : DOF_GATHER_BACKGROUND);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_bg_ps_);
+ DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, with_filter);
+ DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter);
+ DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
+ DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_);
+ DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_);
+ if (bokeh_gather_lut_tx_) {
+ DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_gather_lut_tx_);
+ }
+ DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
+ DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
+ DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
+
+ color_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
+ weight_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner);
+ }
+
+ /* NOTE: First target is holefill texture so we can use the median filter on it and save some
+ * texture memory. Both field use the same framebuffer. */
+ gather_fb_.ensure(GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(color_holefill_tx_),
+ GPU_ATTACHMENT_TEXTURE(weight_holefill_tx_),
+ GPU_ATTACHMENT_TEXTURE(occlusion_tx_));
+ {
+ /**
+ * Filter an input buffer using a median filter to reduce noise.
+ * NOTE: We use the holefill texture as our input to reduce memory usage.
+ * Thus, the holefill pass cannot be filtered.
+ **/
+ DRW_PASS_CREATE(filter_ps_, DRW_STATE_WRITE_COLOR);
+ GPUShader *sh = inst_.shaders.static_shader_get(DOF_FILTER);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, filter_ps_);
+ DRW_shgroup_uniform_texture_ex(grp, "color_tx", color_holefill_tx_, no_filter);
+ DRW_shgroup_uniform_texture_ex(grp, "weight_tx", weight_holefill_tx_, no_filter);
+ DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
+
+ filter_fg_fb_.ensure(GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(color_fg_tx_),
+ GPU_ATTACHMENT_TEXTURE(weight_fg_tx_));
+
+ filter_bg_fb_.ensure(GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(color_bg_tx_),
+ GPU_ATTACHMENT_TEXTURE(weight_bg_tx_));
+ }
+
+ /**
+ * Do the Scatter convolution. A sprite is emitted for every 4 pixels but is only expanded
+ * if the pixels are bright enough to be scattered.
+ **/
+ data_.scatter_sprite_per_row = input_size[0] / 2;
+ int sprite_count = data_.scatter_sprite_per_row * (input_size[1] / 2);
+ {
+ DRW_PASS_CREATE(scatter_fg_ps_, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
+ GPUShader *sh = inst_.shaders.static_shader_get(
+ bokeh_gather_lut_tx_ ? DOF_SCATTER_FOREGROUND_LUT : DOF_SCATTER_FOREGROUND);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, scatter_fg_ps_);
+ DRW_shgroup_uniform_texture_ex(grp, "color_tx", scatter_src_tx_, no_filter);
+ DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
+ DRW_shgroup_uniform_texture(grp, "occlusion_tx", occlusion_tx_);
+ if (bokeh_scatter_lut_tx_) {
+ DRW_shgroup_uniform_texture(grp, "bokehLut", bokeh_scatter_lut_tx_);
+ }
+ DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
+ DRW_shgroup_call_procedural_triangles(grp, NULL, sprite_count);
+
+ scatter_fg_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(color_fg_tx_));
+ }
+ {
+ DRW_PASS_CREATE(scatter_bg_ps_, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
+ GPUShader *sh = inst_.shaders.static_shader_get(
+ bokeh_gather_lut_tx_ ? DOF_SCATTER_BACKGROUND_LUT : DOF_SCATTER_BACKGROUND);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, scatter_bg_ps_);
+ DRW_shgroup_uniform_texture_ex(grp, "color_tx", scatter_src_tx_, no_filter);
+ DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter);
+ DRW_shgroup_uniform_texture(grp, "occlusion_tx", occlusion_tx_);
+ if (bokeh_scatter_lut_tx_) {
+ DRW_shgroup_uniform_texture(grp, "bokehLut", bokeh_scatter_lut_tx_);
+ }
+ DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
+ DRW_shgroup_call_procedural_triangles(grp, NULL, sprite_count);
+
+ scatter_bg_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(color_bg_tx_));
+ }
+}
+
+void DepthOfField::convolve_pass_render(void)
+{
+ DRW_stats_group_start("Foreground convolution");
+ GPU_framebuffer_bind(gather_fb_);
+ DRW_draw_pass(gather_fg_ps_);
+
+ GPU_framebuffer_bind(filter_fg_fb_);
+ DRW_draw_pass(filter_ps_);
+
+ GPU_framebuffer_bind(scatter_fg_fb_);
+ DRW_draw_pass(scatter_fg_ps_);
+ DRW_stats_group_end();
+
+ DRW_stats_group_start("Background convolution");
+ GPU_framebuffer_bind(gather_fb_);
+ DRW_draw_pass(gather_bg_ps_);
+
+ GPU_framebuffer_bind(filter_bg_fb_);
+ DRW_draw_pass(filter_ps_);
+
+ GPU_framebuffer_bind(scatter_bg_fb_);
+ DRW_draw_pass(scatter_bg_ps_);
+ DRW_stats_group_end();
+
+ DRW_stats_group_start("Background convolution");
+ /* Hole-fill convolution. */
+ GPU_framebuffer_bind(gather_holefill_fb_);
+ DRW_draw_pass(gather_holefill_ps_);
+ /* NOTE: We do not filter the hole-fill pass as we use it as out filter input
+ * buffer. Also effect is likely to not be noticeable. */
+ DRW_stats_group_end();
+}
+
+/**
+ * Recombine the result of the foreground and background processing.
+ * Also perform a slight out of focus gather to improve geometric continuity.
+ **/
+void DepthOfField::resolve_pass_sync(void)
+{
+ eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
+ eGPUSamplerState with_filter = GPU_SAMPLER_FILTER;
+ eShaderType sh_type = (bokeh_resolve_lut_tx_) ?
+ (do_hq_slight_focus_ ? DOF_RESOLVE_LUT_HQ : DOF_RESOLVE_LUT) :
+ (do_hq_slight_focus_ ? DOF_RESOLVE_HQ : DOF_RESOLVE);
+
+ DRW_PASS_CREATE(resolve_ps_, DRW_STATE_WRITE_COLOR);
+ GPUShader *sh = inst_.shaders.static_shader_get(sh_type);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &input_depth_tx_, no_filter);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &input_color_tx_, no_filter);
+ DRW_shgroup_uniform_texture_ex(grp, "color_bg_tx", color_bg_tx_, with_filter);
+ DRW_shgroup_uniform_texture_ex(grp, "color_fg_tx", color_fg_tx_, with_filter);
+ DRW_shgroup_uniform_texture_ex(grp, "color_holefill_tx", color_holefill_tx_, with_filter);
+ DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_);
+ DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_);
+ DRW_shgroup_uniform_texture_ex(grp, "weight_bg_tx", weight_bg_tx_, with_filter);
+ DRW_shgroup_uniform_texture_ex(grp, "weight_fg_tx", weight_fg_tx_, with_filter);
+ DRW_shgroup_uniform_texture_ex(grp, "weight_holefill_tx", weight_holefill_tx_, with_filter);
+ DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get());
+ DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
+ if (bokeh_resolve_lut_tx_) {
+ DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_resolve_lut_tx_);
+ }
+ DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
+}
+
+void DepthOfField::resolve_pass_render(GPUTexture *output_tx)
+{
+ resolve_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(output_tx));
+
+ GPU_framebuffer_bind(resolve_fb_);
+ DRW_draw_pass(resolve_ps_);
+}
+
+/** \} */
+
+} // namespace blender::eevee \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/eevee_depth_of_field.hh b/source/blender/draw/engines/eevee/eevee_depth_of_field.hh
new file mode 100644
index 00000000000..657a22b990a
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_depth_of_field.hh
@@ -0,0 +1,176 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Depth of field post process effect.
+ *
+ * There are 2 methods to achieve this effect.
+ * - The first uses projection matrix offsetting and sample accumulation to give
+ * reference quality depth of field. But this needs many samples to hide the
+ * under-sampling.
+ * - The second one is a post-processing based one. It follows the
+ * implementation described in the presentation "Life of a Bokeh - Siggraph
+ * 2018" from Guillaume Abadie. There are some difference with our actual
+ * implementation that prioritize quality.
+ */
+
+#pragma once
+
+#include "eevee_shader_shared.hh"
+
+namespace blender::eevee {
+
+class Instance;
+
+/* -------------------------------------------------------------------- */
+/** \name Depth of field
+ * \{ */
+
+class DepthOfField {
+ private:
+ class Instance &inst_;
+
+ DepthOfFieldDataBuf data_;
+
+ /** Textures from pool. Not owned. */
+ GPUTexture *bokeh_gather_lut_tx_ = nullptr;
+ GPUTexture *bokeh_resolve_lut_tx_ = nullptr;
+ GPUTexture *bokeh_scatter_lut_tx_ = nullptr;
+ GPUTexture *color_bg_tx_ = nullptr;
+ GPUTexture *color_fg_tx_ = nullptr;
+ GPUTexture *color_holefill_tx_ = nullptr;
+ GPUTexture *occlusion_tx_ = nullptr;
+ GPUTexture *reduce_downsample_tx_ = nullptr;
+ GPUTexture *scatter_src_tx_ = nullptr;
+ GPUTexture *setup_coc_tx_ = nullptr;
+ GPUTexture *setup_color_tx_ = nullptr;
+ GPUTexture *tiles_bg_tx_ = nullptr;
+ GPUTexture *tiles_fg_tx_ = nullptr;
+ GPUTexture *tiles_dilated_bg_tx_ = nullptr;
+ GPUTexture *tiles_dilated_fg_tx_ = nullptr;
+ GPUTexture *weight_bg_tx_ = nullptr;
+ GPUTexture *weight_fg_tx_ = nullptr;
+ GPUTexture *weight_holefill_tx_ = nullptr;
+ /** Allocated textures. Owned. */
+ eevee::Texture reduced_coc_tx_ = Texture("dof_reduced_coc");
+ eevee::Texture reduced_color_tx_ = Texture("dof_reduced_color");
+ /** Input texture. Not owned. */
+ GPUTexture *input_color_tx_;
+ GPUTexture *input_depth_tx_;
+ /** Passes. Not owned. */
+ DRWPass *bokeh_lut_ps_ = nullptr;
+ DRWPass *gather_bg_ps_ = nullptr;
+ DRWPass *gather_fg_ps_ = nullptr;
+ DRWPass *filter_ps_ = nullptr;
+ DRWPass *gather_holefill_ps_ = nullptr;
+ DRWPass *reduce_copy_ps_ = nullptr;
+ DRWPass *reduce_downsample_ps_ = nullptr;
+ DRWPass *reduce_recursive_ps_ = nullptr;
+ DRWPass *resolve_ps_ = nullptr;
+ DRWPass *scatter_bg_ps_ = nullptr;
+ DRWPass *scatter_fg_ps_ = nullptr;
+ DRWPass *setup_ps_ = nullptr;
+ DRWPass *tiles_dilate_minabs_ps_ = nullptr;
+ DRWPass *tiles_dilate_minmax_ps_ = nullptr;
+ DRWPass *tiles_flatten_ps_ = nullptr;
+ /** Framebuffers. Owned. */
+ eevee::Framebuffer bokeh_lut_fb_ = Framebuffer("bokeh_lut_fb_");
+ eevee::Framebuffer filter_bg_fb_ = Framebuffer("filter_bg_fb_");
+ eevee::Framebuffer filter_fg_fb_ = Framebuffer("filter_fg_fb_");
+ eevee::Framebuffer gather_fb_ = Framebuffer("gather_fb_");
+ eevee::Framebuffer gather_filter_bg_fb_ = Framebuffer("gather_filter_bg_fb_");
+ eevee::Framebuffer gather_holefill_fb_ = Framebuffer("gather_holefill_fb_");
+ eevee::Framebuffer reduce_copy_fb_ = Framebuffer("reduce_copy_fb_");
+ eevee::Framebuffer reduce_downsample_fb_ = Framebuffer("reduce_downsample_fb_");
+ eevee::Framebuffer reduce_fb_ = Framebuffer("reduce_fb_");
+ eevee::Framebuffer resolve_fb_ = Framebuffer("resolve_fb_");
+ eevee::Framebuffer scatter_bg_fb_ = Framebuffer("scatter_bg_fb_");
+ eevee::Framebuffer scatter_fg_fb_ = Framebuffer("scatter_fg_fb_");
+ eevee::Framebuffer setup_fb_ = Framebuffer("setup_fb_");
+ eevee::Framebuffer tiles_dilate_fb_ = Framebuffer("tiles_dilate_fb_");
+ eevee::Framebuffer tiles_flatten_fb_ = Framebuffer("tiles_flatten_fb_");
+
+ /** Scene settings that are immutable. */
+ float user_overblur_;
+ float fx_max_coc_;
+ /** Use Hiqh Quality (expensive) in-focus gather pass. */
+ bool do_hq_slight_focus_;
+ /** Use jittered depth of field where we randomize camera location. */
+ bool do_jitter_;
+
+ /** Circle of Confusion radius for FX DoF passes. Is in view X direction in [0..1] range. */
+ float fx_radius_;
+ /** Circle of Confusion radius for jittered DoF. Is in view X direction in [0..1] range. */
+ float jitter_radius_;
+ /** Focus distance in view space. */
+ float focus_distance_;
+ /** Extent of the input buffer. */
+ ivec2 extent_;
+
+ /** Tile dilation uniforms. */
+ int tiles_dilate_slight_focus_;
+ int tiles_dilate_ring_count_;
+ int tiles_dilate_ring_width_multiplier_;
+
+ /** Reduce pass info. */
+ int reduce_steps_;
+
+ /** Static 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 mat4 winmat, ivec2 input_extent);
+
+ /** Apply Depth Of Field jittering to the view and projection matrices.. */
+ void jitter_apply(mat4 winmat, mat4 viewmat);
+
+ /** Will swap input and output texture if rendering happens. The actual output of this function
+ * is in intput_tx. */
+ void render(GPUTexture *depth_tx, GPUTexture **input_tx, GPUTexture **output_tx);
+
+ private:
+ void bokeh_lut_pass_sync(void);
+ void bokeh_lut_pass_render(void);
+
+ void setup_pass_sync(void);
+ void setup_pass_render(void);
+
+ void tiles_prepare_pass_sync(void);
+ void tiles_prepare_pass_render(void);
+
+ static void reduce_recusive(void *thunk, int level);
+ void reduce_pass_sync(void);
+ void reduce_pass_render(void);
+
+ void convolve_pass_sync(void);
+ void convolve_pass_render(void);
+
+ void resolve_pass_sync(void);
+ void resolve_pass_render(GPUTexture *output_tx);
+};
+
+/** \} */
+
+} // namespace blender::eevee \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c
deleted file mode 100644
index 3d32b4acd82..00000000000
--- a/source/blender/draw/engines/eevee/eevee_effects.c
+++ /dev/null
@@ -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);
- }
-}
diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c
index fc9b8b0cde4..a744d1f8cd5 100644
--- a/source/blender/draw/engines/eevee/eevee_engine.c
+++ b/source/blender/draw/engines/eevee/eevee_engine.c
@@ -22,602 +22,123 @@
#include "DRW_render.h"
-#include "draw_color_management.h" /* TODO: remove dependency. */
-
-#include "BLI_rand.h"
-
-#include "BKE_object.h"
-
-#include "DEG_depsgraph_query.h"
-
-#include "DNA_world_types.h"
-
-#include "IMB_imbuf.h"
+#include "DRW_engine.h"
+#include "GPU_framebuffer.h"
+#include "eevee_engine.h"
#include "eevee_private.h"
-#include "eevee_engine.h" /* own include */
-
-#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);
- }
-}
+typedef struct EEVEE_Data {
+ DrawEngineType *engine_type;
+ DRWViewportEmptyList *fbl;
+ DRWViewportEmptyList *txl;
+ DRWViewportEmptyList *psl;
+ DRWViewportEmptyList *stl;
+ struct EEVEE_Instance *instance_data;
+} EEVEE_Data;
-static void eevee_cache_finish(void *vedata)
+static void eevee_engine_init(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);
+ EEVEE_Data *ved = (EEVEE_Data *)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;
+ if (ved->instance_data == NULL) {
+ ved->instance_data = EEVEE_instance_alloc();
}
- 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);
- }
+ EEVEE_instance_init(ved->instance_data);
}
-/* 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;
- }
+ EEVEE_instance_draw_viewport(((EEVEE_Data *)vedata)->instance_data);
}
-static void eevee_id_object_update(void *UNUSED(vedata), Object *object)
+static void eevee_cache_init(void *vedata)
{
- 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;
- }
+ EEVEE_instance_cache_init(((EEVEE_Data *)vedata)->instance_data);
}
-static void eevee_id_world_update(void *vedata, World *wo)
+static void eevee_cache_populate(void *vedata, Object *object)
{
- 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;
- }
+ EEVEE_instance_cache_populate(((EEVEE_Data *)vedata)->instance_data, object);
}
-void eevee_id_update(void *vedata, ID *id)
+static void eevee_cache_finish(void *vedata)
{
- /* 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;
- }
+ EEVEE_instance_cache_finish(((EEVEE_Data *)vedata)->instance_data);
}
-static void eevee_render_reset_passes(EEVEE_Data *vedata)
+static void eevee_engine_free(void)
{
- /* Reset passlist. This is safe as they are stored into managed memory chunks. */
- memset(vedata->psl, 0, sizeof(*vedata->psl));
+ EEVEE_shared_data_free();
}
-static void eevee_render_to_image(void *vedata,
- RenderEngine *engine,
- struct RenderLayer *render_layer,
- const rcti *rect)
+static void eevee_instance_free(void *instance_data)
{
- 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);
- }
+ EEVEE_instance_free((struct EEVEE_Instance *)instance_data);
}
-static void eevee_store_metadata(void *vedata, struct RenderResult *render_result)
+static void eevee_render_to_image(void *UNUSED(vedata),
+ struct RenderEngine *engine,
+ struct RenderLayer *layer,
+ const struct rcti *UNUSED(rect))
{
- 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);
- }
+ struct EEVEE_Instance *instance = EEVEE_instance_alloc();
+ EEVEE_instance_render_frame(instance, engine, layer);
+ EEVEE_instance_free(instance);
}
-static void eevee_engine_free(void)
+static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
{
- EEVEE_shaders_free();
- EEVEE_lightprobes_free();
- EEVEE_materials_free();
- EEVEE_occlusion_free();
- EEVEE_volumes_free();
+ RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA);
+
+#define CHECK_PASS_LEGACY(name, type, channels, chanid) \
+ if (view_layer->passflag & (SCE_PASS_##name)) { \
+ RE_engine_register_pass( \
+ engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
+ } \
+ ((void)0)
+#define CHECK_PASS_EEVEE(name, type, channels, chanid) \
+ if (view_layer->eevee.render_passes & (EEVEE_RENDER_PASS_##name)) { \
+ RE_engine_register_pass( \
+ engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
+ } \
+ ((void)0)
+
+ CHECK_PASS_LEGACY(Z, SOCK_FLOAT, 1, "Z");
+ CHECK_PASS_LEGACY(MIST, SOCK_FLOAT, 1, "Z");
+ CHECK_PASS_LEGACY(NORMAL, SOCK_VECTOR, 3, "XYZ");
+ CHECK_PASS_LEGACY(VECTOR, SOCK_RGBA, 4, "RGBA");
+ CHECK_PASS_LEGACY(DIFFUSE_DIRECT, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(DIFFUSE_COLOR, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(GLOSSY_COLOR, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(SHADOW, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(AO, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_EEVEE(BLOOM, SOCK_RGBA, 3, "RGB");
+
+ LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
+ if ((aov->flag & AOV_CONFLICT) != 0) {
+ continue;
+ }
+ switch (aov->type) {
+ case AOV_TYPE_COLOR:
+ RE_engine_register_pass(engine, scene, view_layer, aov->name, 4, "RGBA", SOCK_RGBA);
+ break;
+ case AOV_TYPE_VALUE:
+ RE_engine_register_pass(engine, scene, view_layer, aov->name, 1, "X", SOCK_FLOAT);
+ break;
+ default:
+ break;
+ }
+ }
+ // EEVEE_cryptomatte_update_passes(engine, scene, view_layer);
+
+#undef CHECK_PASS_LEGACY
+#undef CHECK_PASS_EEVEE
}
static const DrawEngineDataSize eevee_data_size = DRW_VIEWPORT_DATA_SIZE(EEVEE_Data);
@@ -629,17 +150,19 @@ DrawEngineType draw_engine_eevee_type = {
&eevee_data_size,
&eevee_engine_init,
&eevee_engine_free,
- NULL, /* instance_free */
+ &eevee_instance_free,
&eevee_cache_init,
- &EEVEE_cache_populate,
+ &eevee_cache_populate,
&eevee_cache_finish,
&eevee_draw_scene,
- &eevee_view_update,
- &eevee_id_update,
+ NULL,
+ NULL,
&eevee_render_to_image,
- &eevee_store_metadata,
+ NULL,
};
+#define EEVEE_ENGINE "BLENDER_EEVEE"
+
RenderEngineType DRW_engine_viewport_eevee_type = {
NULL,
NULL,
@@ -654,7 +177,7 @@ RenderEngineType DRW_engine_viewport_eevee_type = {
NULL,
NULL,
NULL,
- &EEVEE_render_update_passes,
+ &eevee_render_update_passes,
&draw_engine_eevee_type,
{NULL, NULL, NULL},
};
diff --git a/source/blender/draw/engines/eevee/eevee_engine.cc b/source/blender/draw/engines/eevee/eevee_engine.cc
new file mode 100644
index 00000000000..a25c31f4690
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_engine.cc
@@ -0,0 +1,167 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "BKE_global.h"
+#include "BLI_rect.h"
+
+#include "GPU_framebuffer.h"
+
+#include "ED_view3d.h"
+
+#include "DRW_render.h"
+
+#include "eevee_private.h"
+
+#include "eevee_instance.hh"
+
+using namespace blender::eevee;
+
+static ShaderModule *g_shader_module = nullptr;
+
+/* -------------------------------------------------------------------- */
+/** \name EEVEE Instance C interface
+ * \{ */
+
+EEVEE_Instance *EEVEE_instance_alloc(void)
+{
+ if (g_shader_module == nullptr) {
+ /* TODO(fclem) threadsafety. */
+ g_shader_module = new ShaderModule();
+ }
+ return reinterpret_cast<EEVEE_Instance *>(new Instance(*g_shader_module));
+}
+
+void EEVEE_instance_free(EEVEE_Instance *instance_)
+{
+ Instance *instance = reinterpret_cast<Instance *>(instance_);
+ delete instance;
+}
+
+void EEVEE_instance_init(EEVEE_Instance *instance_)
+{
+ Instance *instance = reinterpret_cast<Instance *>(instance_);
+
+ const DRWContextState *ctx_state = DRW_context_state_get();
+ Depsgraph *depsgraph = ctx_state->depsgraph;
+ Scene *scene = ctx_state->scene;
+ View3D *v3d = ctx_state->v3d;
+ const ARegion *region = ctx_state->region;
+ RegionView3D *rv3d = ctx_state->rv3d;
+
+ /* Scaling output to better see what happens with accumulation. */
+ int resolution_divider = (ELEM(G.debug_value, 1, 2)) ? 16 : 1;
+
+ DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
+ int size[2];
+ size[0] = divide_ceil_u(GPU_texture_width(dtxl->color), resolution_divider);
+ size[1] = divide_ceil_u(GPU_texture_height(dtxl->color), resolution_divider);
+
+ const DRWView *default_view = DRW_view_default_get();
+
+ Object *camera = nullptr;
+ /* Get render borders. */
+ rcti rect;
+ BLI_rcti_init(&rect, 0, size[0], 0, size[1]);
+ if (v3d) {
+ if (rv3d && (rv3d->persp == RV3D_CAMOB)) {
+ camera = v3d->camera;
+ }
+
+ if (v3d->flag2 & V3D_RENDER_BORDER) {
+ if (camera) {
+ rctf viewborder;
+ /* TODO(fclem) Might be better to get it from DRW. */
+ ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &viewborder, false);
+ float viewborder_sizex = BLI_rctf_size_x(&viewborder);
+ float viewborder_sizey = BLI_rctf_size_y(&viewborder);
+ rect.xmin = floorf(viewborder.xmin + (scene->r.border.xmin * viewborder_sizex));
+ rect.ymin = floorf(viewborder.ymin + (scene->r.border.ymin * viewborder_sizey));
+ rect.xmax = floorf(viewborder.xmin + (scene->r.border.xmax * viewborder_sizex));
+ rect.ymax = floorf(viewborder.ymin + (scene->r.border.ymax * viewborder_sizey));
+ }
+ else {
+ rect.xmin = v3d->render_border.xmin * size[0];
+ rect.ymin = v3d->render_border.ymin * size[1];
+ rect.xmax = v3d->render_border.xmax * size[0];
+ rect.ymax = v3d->render_border.ymax * size[1];
+ }
+ }
+ }
+
+ instance->init(
+ size, &rect, nullptr, depsgraph, nullptr, camera, nullptr, default_view, v3d, rv3d);
+}
+
+void EEVEE_instance_cache_init(EEVEE_Instance *instance_)
+{
+ Instance *instance = reinterpret_cast<Instance *>(instance_);
+ instance->begin_sync();
+}
+
+void EEVEE_instance_cache_populate(EEVEE_Instance *instance_, Object *object)
+{
+ Instance *instance = reinterpret_cast<Instance *>(instance_);
+ instance->object_sync(object);
+}
+
+void EEVEE_instance_cache_finish(EEVEE_Instance *instance_)
+{
+ Instance *instance = reinterpret_cast<Instance *>(instance_);
+ instance->end_sync();
+}
+
+void EEVEE_instance_draw_viewport(EEVEE_Instance *instance_)
+{
+ Instance *instance = reinterpret_cast<Instance *>(instance_);
+ DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
+
+ instance->draw_viewport(dfbl);
+}
+
+void EEVEE_instance_render_frame(EEVEE_Instance *instance_,
+ struct RenderEngine *engine,
+ struct RenderLayer *render_layer)
+{
+ Instance *instance = reinterpret_cast<Instance *>(instance_);
+ Render *render = engine->re;
+ Depsgraph *depsgraph = DRW_context_state_get()->depsgraph;
+ Object *camera_original_ob = RE_GetCamera(engine->re);
+ const char *viewname = RE_GetActiveRenderView(engine->re);
+ int size[2] = {engine->resolution_x, engine->resolution_y};
+
+ rctf view_rect;
+ rcti rect;
+ RE_GetViewPlane(render, &view_rect, &rect);
+
+ instance->init(size, &rect, engine, depsgraph, nullptr, camera_original_ob, render_layer);
+ instance->render_frame(render_layer, viewname);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name EEVEE Shaders C interface
+ * \{ */
+
+void EEVEE_shared_data_free(void)
+{
+ delete g_shader_module;
+ g_shader_module = nullptr;
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/eevee_engine.h b/source/blender/draw/engines/eevee/eevee_engine.h
index 40784e2980b..94d2e541b88 100644
--- a/source/blender/draw/engines/eevee/eevee_engine.h
+++ b/source/blender/draw/engines/eevee/eevee_engine.h
@@ -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
diff --git a/source/blender/draw/engines/eevee/eevee_film.cc b/source/blender/draw/engines/eevee/eevee_film.cc
new file mode 100644
index 00000000000..2542e39aed0
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_film.cc
@@ -0,0 +1,257 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * A film is a fullscreen buffer (usually at output extent)
+ * that will be able to accumulate sample in any distorted camera_type
+ * using a pixel filter.
+ *
+ * Input needs to be jittered so that the filter converges to the right result.
+ */
+
+#include "BLI_rect.h"
+
+#include "GPU_framebuffer.h"
+#include "GPU_texture.h"
+
+#include "DRW_render.h"
+
+#include "eevee_film.hh"
+#include "eevee_instance.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name FilmData
+ * \{ */
+
+static eGPUTextureFormat to_gpu_texture_format(eFilmDataType film_type)
+{
+ switch (film_type) {
+ default:
+ case FILM_DATA_COLOR_LOG:
+ case FILM_DATA_COLOR:
+ case FILM_DATA_MOTION:
+ case FILM_DATA_VEC4:
+ return GPU_RGBA16F;
+ case FILM_DATA_FLOAT:
+ return GPU_R16F;
+ case FILM_DATA_VEC2:
+ return GPU_RG16F;
+ case FILM_DATA_NORMAL:
+ return GPU_RGB10_A2;
+ case FILM_DATA_DEPTH:
+ return GPU_R32F;
+ }
+}
+
+inline bool operator==(const FilmData &a, const FilmData &b)
+{
+ return (a.extent == b.extent) && (a.offset == b.offset);
+}
+
+inline bool operator!=(const FilmData &a, const FilmData &b)
+{
+ return !(a == b);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Film
+ * \{ */
+
+void Film::init(const ivec2 &full_extent, const rcti *output_rect)
+{
+ FilmData data = data_;
+ data.extent = ivec2(BLI_rcti_size_x(output_rect), BLI_rcti_size_y(output_rect));
+ data.offset = ivec2(output_rect->xmin, output_rect->ymin);
+
+ has_changed_ = data_ != data;
+
+ if (has_changed_) {
+ data_ = data;
+ inst_.sampling.reset();
+ }
+
+ data_.opacity = 1.0f;
+ data_.uv_scale_inv = vec2(full_extent);
+ data_.uv_scale = 1.0f / data_.uv_scale_inv;
+ data_.uv_bias = vec2(data_.offset) * data_.uv_scale;
+}
+
+void Film::sync(void)
+{
+ char full_name[32];
+ for (int i = 0; i < 2; i++) {
+ if (data_tx_[i] == nullptr) {
+ eGPUTextureFormat tex_format = to_gpu_texture_format(data_.data_type);
+ SNPRINTF(full_name, "Film.%s.data", name_.c_str());
+ data_tx_[i].ensure(full_name, UNPACK2(data_.extent), 1, tex_format);
+ /* TODO(fclem) The weight texture could be shared between all similar accumulators. */
+ SNPRINTF(full_name, "Film.%s.weight", name_.c_str());
+ weight_tx_[i].ensure(full_name, UNPACK2(data_.extent), 1, GPU_R16F);
+
+ accumulation_fb_[i].ensure(GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(data_tx_[i]),
+ GPU_ATTACHMENT_TEXTURE(weight_tx_[i]));
+ }
+ }
+
+ eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
+ {
+ SNPRINTF(full_name, "Film.%s.Accumulate", name_.c_str());
+ accumulate_ps_ = DRW_pass_create(full_name, DRW_STATE_WRITE_COLOR);
+ GPUShader *sh = inst_.shaders.static_shader_get(FILM_FILTER);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, accumulate_ps_);
+ DRW_shgroup_uniform_block(grp, "film_block", data_.ubo_get());
+ DRW_shgroup_uniform_block(grp, "camera_block", inst_.camera.ubo_get());
+ DRW_shgroup_uniform_texture_ref_ex(grp, "input_tx", &input_tx_, no_filter);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "data_tx", &data_tx_[0], no_filter);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "weight_tx", &weight_tx_[0], no_filter);
+ DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
+ }
+ {
+ SNPRINTF(full_name, "Film.%s.Resolve", name_.c_str());
+ DRWState state = DRW_STATE_WRITE_COLOR;
+ eShaderType sh_type = FILM_RESOLVE;
+ if (data_.data_type == FILM_DATA_DEPTH) {
+ state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS;
+ sh_type = FILM_RESOLVE_DEPTH;
+ }
+ resolve_ps_ = DRW_pass_create(full_name, state);
+ GPUShader *sh = inst_.shaders.static_shader_get(sh_type);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_);
+ DRW_shgroup_uniform_block(grp, "film_block", data_.ubo_get());
+ DRW_shgroup_uniform_texture_ref_ex(grp, "first_sample_tx", &first_sample_ref_, no_filter);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "data_tx", &data_tx_[0], no_filter);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "weight_tx", &weight_tx_[0], no_filter);
+ DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
+ }
+}
+
+void Film::end_sync()
+{
+ /* TODO reprojection. */
+ if (inst_.sampling.is_reset()) {
+ data_.use_history = 0;
+ }
+
+ if (inst_.is_viewport()) {
+ data_.opacity = inst_.sampling.viewport_smoothing_opacity_factor_get();
+ }
+
+ if (data_.use_history == 0 || inst_.is_viewport()) {
+ data_.push_update();
+ }
+
+ const bool is_first_sample = (inst_.sampling.sample_get() == 1);
+ if (do_smooth_viewport_smooth_transition() && (data_.opacity < 1.0f || is_first_sample)) {
+ char full_name[32];
+ SNPRINTF(full_name, "Film.%s.first_sample", name_.c_str());
+ 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(full_name, UNPACK2(extent), 1, tex_format);
+ first_sample_ref_ = first_sample_tx_;
+ }
+ else {
+ /* Reuse the data_tx since there is no need to blend. */
+ first_sample_tx_.release();
+ first_sample_ref_ = data_tx_[0];
+ }
+}
+
+void Film::accumulate(GPUTexture *input, const DRWView *view)
+{
+ input_tx_ = input;
+
+ DRW_view_set_active(view);
+
+ GPU_framebuffer_bind(accumulation_fb_[1]);
+ DRW_draw_pass(accumulate_ps_);
+
+ SWAP(Framebuffer, accumulation_fb_[0], accumulation_fb_[1]);
+ SWAP(Texture, data_tx_[0], data_tx_[1]);
+ SWAP(Texture, weight_tx_[0], weight_tx_[1]);
+
+ /* Use history after first sample. */
+ if (data_.use_history == 0) {
+ data_.use_history = 1;
+ data_.push_update();
+ }
+}
+
+void Film::resolve_viewport(GPUFrameBuffer *target)
+{
+ int viewport[4];
+
+ GPU_framebuffer_bind(target);
+ GPU_framebuffer_viewport_get(target, viewport);
+
+ const bool use_render_border = (data_.offset[0] > 0) || (data_.offset[1] > 0) ||
+ (data_.extent[0] < viewport[2]) ||
+ (data_.extent[1] < viewport[3]);
+ if (use_render_border) {
+ if (has_changed_) {
+ /* Film is cropped and does not fill the view completely. Clear the background. */
+ if (data_.data_type == FILM_DATA_DEPTH) {
+ GPU_framebuffer_clear_depth(target, 1.0f);
+ }
+ else {
+ float color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ GPU_framebuffer_clear_color(target, color);
+ }
+ }
+ GPU_framebuffer_viewport_set(target, UNPACK2(data_.offset), UNPACK2(data_.extent));
+ }
+
+ DRW_draw_pass(resolve_ps_);
+
+ /* Minus one because we already incremented it in step() which is the first
+ * thing to happen in the sample loop. */
+ const bool is_first_sample = (inst_.sampling.sample_get() - 1 == 1);
+ const bool is_only_one_sample = is_first_sample && inst_.sampling.finished();
+ if (is_first_sample && !is_only_one_sample && do_smooth_viewport_smooth_transition()) {
+ DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
+ GPU_texture_copy(first_sample_tx_, dtxl->color);
+ }
+
+ if (use_render_border) {
+ GPU_framebuffer_viewport_reset(target);
+ }
+}
+
+void Film::read_result(float *data)
+{
+ /* Resolve onto the next data texture. */
+ read_result_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(data_tx_[1]));
+ GPU_framebuffer_bind(read_result_fb_);
+ DRW_draw_pass(resolve_ps_);
+
+ eGPUTextureFormat format = to_gpu_texture_format(data_.data_type);
+ int channel_count = GPU_texture_component_len(format);
+ GPU_framebuffer_read_color(
+ read_result_fb_, 0, 0, UNPACK2(data_.extent), channel_count, 0, GPU_DATA_FLOAT, data);
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_film.hh b/source/blender/draw/engines/eevee/eevee_film.hh
new file mode 100644
index 00000000000..06e019e5a3a
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_film.hh
@@ -0,0 +1,112 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * A film is a fullscreen buffer (usually at output extent)
+ * that will be able to accumulate sample in any distorted camera_type
+ * using a pixel filter.
+ *
+ * Input needs to be jittered so that the filter converges to the right result.
+ */
+
+#pragma once
+
+#include "DRW_render.h"
+
+#include "eevee_camera.hh"
+#include "eevee_sampling.hh"
+#include "eevee_shader.hh"
+#include "eevee_wrapper.hh"
+
+namespace blender::eevee {
+
+class Instance;
+
+/* -------------------------------------------------------------------- */
+/** \name Film
+ * \{ */
+
+class Film {
+ private:
+ Instance &inst_;
+
+ /** Owned resources. */
+ eevee::Framebuffer read_result_fb_;
+ eevee::Framebuffer accumulation_fb_[2];
+ eevee::Texture data_tx_[2];
+ eevee::Texture weight_tx_[2];
+ /** First sample in case we need to blend using it or just reuse it. */
+ eevee::Texture first_sample_tx_;
+
+ /** Reference to first_sample_tx_ or data_tx_ depending on the context. */
+ GPUTexture *first_sample_ref_;
+
+ // DRWPass *clear_ps_ = nullptr;
+ DRWPass *accumulate_ps_ = nullptr;
+ DRWPass *resolve_ps_ = nullptr;
+
+ /** Shader parameter, not allocated. */
+ GPUTexture *input_tx_;
+ /** ViewProjection matrix used to render the input. */
+ // float src_persmat_[4][4];
+ /** ViewProjection matrix Inverse used to render the input. */
+ // float src_persinv_[4][4];
+
+ StructBuffer<FilmData> data_;
+
+ /** True if offset or size changed. */
+ bool has_changed_ = true;
+
+ /** Debug static name. */
+ StringRefNull name_;
+
+ public:
+ /* NOTE: name needs to be static. */
+ Film(Instance &inst, eFilmDataType data_type, const char *name) : inst_(inst), name_(name)
+ {
+ data_.extent[0] = data_.extent[1] = -1;
+ data_.data_type = data_type;
+ data_.use_history = 0;
+ }
+
+ ~Film(){};
+
+ void init(const ivec2 &full_extent, const rcti *output_rect);
+
+ void sync(void);
+ void end_sync(void);
+
+ void accumulate(GPUTexture *input, const DRWView *view);
+
+ void resolve_viewport(GPUFrameBuffer *target);
+
+ void read_result(float *data);
+
+ private:
+ bool do_smooth_viewport_smooth_transition(void)
+ {
+ return ELEM(data_.data_type, FILM_DATA_COLOR, FILM_DATA_COLOR_LOG) &&
+ !DRW_state_is_image_render();
+ }
+};
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_gbuffer.hh b/source/blender/draw/engines/eevee/eevee_gbuffer.hh
new file mode 100644
index 00000000000..452aca584d0
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_gbuffer.hh
@@ -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 {
+ Texture transmit_color_tx = Texture("GbufferTransmitColor");
+ Texture transmit_normal_tx = Texture("GbufferTransmitNormal");
+ Texture transmit_data_tx = Texture("GbufferTransmitData");
+ Texture reflect_color_tx = Texture("GbufferReflectionColor");
+ Texture reflect_normal_tx = Texture("GbufferReflectionNormal");
+ Texture volume_tx = Texture("GbufferVolume");
+ Texture emission_tx = Texture("GbufferEmission");
+ Texture transparency_tx = Texture("GbufferTransparency");
+
+ Framebuffer gbuffer_fb = Framebuffer("Gbuffer");
+ Framebuffer volume_fb = Framebuffer("VolumeHeterogeneous");
+
+ Texture holdout_tx = Texture("HoldoutRadiance");
+ Texture diffuse_tx = Texture("DiffuseRadiance");
+
+ Framebuffer radiance_fb = Framebuffer("Radiance");
+ Framebuffer radiance_clear_fb = Framebuffer("RadianceClear");
+
+ Framebuffer holdout_fb = Framebuffer("Holdout");
+
+ Texture depth_behind_tx = Texture("DepthBehind");
+
+ Framebuffer depth_behind_fb = Framebuffer("DepthCopy");
+
+ /** Raytracing. */
+ Texture ray_data_tx = Texture("RayData");
+ Texture ray_radiance_tx = Texture("RayRadiance");
+ Texture ray_variance_tx = Texture("RayVariance");
+ Framebuffer ray_data_fb = Framebuffer("RayData");
+ Framebuffer ray_denoise_fb = Framebuffer("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_tmp();
+ transmit_normal_tx.sync_tmp();
+ transmit_data_tx.sync_tmp();
+ reflect_color_tx.sync_tmp();
+ reflect_normal_tx.sync_tmp();
+ volume_tx.sync_tmp();
+ emission_tx.sync_tmp();
+ transparency_tx.sync_tmp();
+ holdout_tx.sync_tmp();
+ diffuse_tx.sync_tmp();
+ depth_behind_tx.sync_tmp();
+ ray_data_tx.sync_tmp();
+ ray_radiance_tx.sync_tmp();
+ ray_variance_tx.sync_tmp();
+ }
+
+ void prepare(eClosureBits closures_used)
+ {
+ ivec2 extent = {GPU_texture_width(depth_tx), GPU_texture_height(depth_tx)};
+
+ /* TODO Reuse for different config. */
+ if (closures_used & (CLOSURE_DIFFUSE | CLOSURE_SSS | CLOSURE_REFRACTION)) {
+ transmit_color_tx.acquire_tmp(UNPACK2(extent), GPU_R11F_G11F_B10F, owner);
+ }
+ if (closures_used & (CLOSURE_SSS | CLOSURE_REFRACTION)) {
+ transmit_normal_tx.acquire_tmp(UNPACK2(extent), GPU_RGBA16F, owner);
+ transmit_data_tx.acquire_tmp(UNPACK2(extent), GPU_R11F_G11F_B10F, owner);
+ }
+ else if (closures_used & CLOSURE_DIFFUSE) {
+ transmit_normal_tx.acquire_tmp(UNPACK2(extent), GPU_RG16F, owner);
+ }
+ if (closures_used & CLOSURE_SSS) {
+ diffuse_tx.acquire_tmp(UNPACK2(extent), GPU_RGBA16F, owner);
+ }
+
+ if (closures_used & CLOSURE_REFLECTION) {
+ reflect_color_tx.acquire_tmp(UNPACK2(extent), GPU_R11F_G11F_B10F, owner);
+ reflect_normal_tx.acquire_tmp(UNPACK2(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_tmp(UNPACK2(extent), GPU_RGBA32UI, owner);
+ }
+
+ if (closures_used & CLOSURE_EMISSION) {
+ emission_tx.acquire_tmp(UNPACK2(extent), GPU_R11F_G11F_B10F, owner);
+ }
+
+ if (closures_used & CLOSURE_TRANSPARENCY) {
+ /* TODO(fclem): Speedup by using Dithered holdout and GPU_RGB10_A2. */
+ transparency_tx.acquire_tmp(UNPACK2(extent), GPU_RGBA16, owner);
+ }
+
+ if (closures_used & (CLOSURE_DIFFUSE | CLOSURE_REFLECTION | CLOSURE_REFRACTION)) {
+ ray_data_tx.acquire_tmp(UNPACK2(extent), GPU_RGBA16F, owner);
+ ray_radiance_tx.acquire_tmp(UNPACK2(extent), GPU_RGBA16F, owner);
+ ray_variance_tx.acquire_tmp(UNPACK2(extent), GPU_R8, owner);
+ }
+
+ holdout_tx.acquire_tmp(UNPACK2(extent), GPU_R11F_G11F_B10F, owner);
+ depth_behind_tx.acquire_tmp(UNPACK2(extent), GPU_DEPTH24_STENCIL8, owner);
+
+ /* Layer attachement also works with cubemap. */
+ gbuffer_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer),
+ GPU_ATTACHMENT_TEXTURE(transmit_color_tx),
+ GPU_ATTACHMENT_TEXTURE(transmit_normal_tx),
+ GPU_ATTACHMENT_TEXTURE(transmit_data_tx),
+ GPU_ATTACHMENT_TEXTURE(reflect_color_tx),
+ GPU_ATTACHMENT_TEXTURE(reflect_normal_tx),
+ GPU_ATTACHMENT_TEXTURE(volume_tx),
+ GPU_ATTACHMENT_TEXTURE(emission_tx),
+ GPU_ATTACHMENT_TEXTURE(transparency_tx));
+ }
+
+ 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_tmp();
+ transmit_normal_tx.release_tmp();
+ transmit_data_tx.release_tmp();
+ reflect_color_tx.release_tmp();
+ reflect_normal_tx.release_tmp();
+ volume_tx.release_tmp();
+ emission_tx.release_tmp();
+ transparency_tx.release_tmp();
+ holdout_tx.release_tmp();
+ diffuse_tx.release_tmp();
+ depth_behind_tx.release_tmp();
+ ray_data_tx.release_tmp();
+ ray_radiance_tx.release_tmp();
+ ray_variance_tx.release_tmp();
+ }
+};
+
+/** \} */
+
+} // namespace blender::eevee \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/eevee_gpencil.cc b/source/blender/draw/engines/eevee/eevee_gpencil.cc
new file mode 100644
index 00000000000..d5d34040c4f
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_gpencil.cc
@@ -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
diff --git a/source/blender/draw/engines/eevee/eevee_hair.cc b/source/blender/draw/engines/eevee/eevee_hair.cc
new file mode 100644
index 00000000000..519be551fbb
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_hair.cc
@@ -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
diff --git a/source/blender/draw/engines/eevee/eevee_hizbuffer.cc b/source/blender/draw/engines/eevee/eevee_hizbuffer.cc
new file mode 100644
index 00000000000..038773aecab
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_hizbuffer.cc
@@ -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_;
+ vec2 extent_src(GPU_texture_width(depth_src_tx), GPU_texture_height(depth_src_tx));
+ vec2 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 / extent_hiz;
+ inst_.hiz.data_.uv_scale = extent_src / 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("HiZ", UNPACK2(extent_hiz), mip_count_, GPU_R32F);
+ 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 / vec2(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
diff --git a/source/blender/draw/engines/eevee/eevee_hizbuffer.hh b/source/blender/draw/engines/eevee/eevee_hizbuffer.hh
new file mode 100644
index 00000000000..80bd7be912c
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_hizbuffer.hh
@@ -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_ = 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_;
+
+ 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. */
+ vec2 texel_size_;
+
+ public:
+ HiZBufferModule(Instance &inst) : inst_(inst){};
+
+ void sync(void);
+
+ const GPUUniformBuf *ubo_get(void) const
+ {
+ return data_.ubo_get();
+ }
+};
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_id_map.cc b/source/blender/draw/engines/eevee/eevee_id_map.cc
new file mode 100644
index 00000000000..ab701e382a1
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_id_map.cc
@@ -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
diff --git a/source/blender/draw/engines/eevee/eevee_id_map.hh b/source/blender/draw/engines/eevee/eevee_id_map.hh
new file mode 100644
index 00000000000..b8d1246c6bb
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_id_map.hh
@@ -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
diff --git a/source/blender/draw/engines/eevee/eevee_instance.cc b/source/blender/draw/engines/eevee/eevee_instance.cc
new file mode 100644
index 00000000000..f614d4b4a97
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_instance.cc
@@ -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 ivec2 &output_res,
+ const rcti *output_rect,
+ RenderEngine *render_,
+ Depsgraph *depsgraph_,
+ const struct LightProbe *light_probe_,
+ Object *camera_object_,
+ const RenderLayer *render_layer_,
+ const DRWView *drw_view_,
+ const View3D *v3d_,
+ const RegionView3D *rv3d_)
+{
+ render = render_;
+ depsgraph = depsgraph_;
+ render_layer = render_layer_;
+ camera_orig_object = camera_object_;
+ drw_view = drw_view_;
+ v3d = v3d_;
+ rv3d = rv3d_;
+ baking_probe = light_probe_;
+
+ 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
diff --git a/source/blender/draw/engines/eevee/eevee_instance.hh b/source/blender/draw/engines/eevee/eevee_instance.hh
new file mode 100644
index 00000000000..3079f931231
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_instance.hh
@@ -0,0 +1,190 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * An instance contains all structures needed to do a complete render.
+ */
+
+#pragma once
+
+#include "BKE_object.h"
+#include "DEG_depsgraph.h"
+#include "DRW_render.h"
+
+#include "eevee_film.hh"
+#include "eevee_hizbuffer.hh"
+#include "eevee_id_map.hh"
+#include "eevee_light.hh"
+#include "eevee_lightprobe.hh"
+#include "eevee_lookdev.hh"
+#include "eevee_material.hh"
+#include "eevee_motion_blur.hh"
+#include "eevee_raytracing.hh"
+#include "eevee_renderpasses.hh"
+#include "eevee_sampling.hh"
+#include "eevee_shader.hh"
+#include "eevee_shading.hh"
+#include "eevee_shadow.hh"
+#include "eevee_subsurface.hh"
+#include "eevee_view.hh"
+#include "eevee_world.hh"
+
+#include "eevee_engine.h"
+
+namespace blender::eevee {
+
+/**
+ * \class Instance
+ * \brief A running instance of the engine.
+ */
+class Instance {
+ friend MotionBlur;
+ friend MotionBlurModule;
+ friend VelocityModule;
+
+ public:
+ ShaderModule &shaders;
+ Sampling sampling;
+ RenderPasses render_passes;
+ ShadingPasses shading_passes;
+ MainView main_view;
+ Camera camera;
+ World world;
+ VelocityModule velocity;
+ MotionBlurModule motion_blur;
+ LightModule lights;
+ LightProbeModule lightprobes;
+ RaytracingModule raytracing;
+ HiZBufferModule hiz;
+ /* TODO(fclem) Move it to scene layer data. */
+ ShadowModule shadows;
+ SubsurfaceModule subsurface;
+ SyncModule sync;
+ MaterialModule materials;
+ /** Lookdev own lightweight instance. May not be allocated. */
+ LookDev lookdev;
+
+ /** Input data. */
+ Depsgraph *depsgraph;
+ /** Evaluated IDs. */
+ Scene *scene;
+ ViewLayer *view_layer;
+ Object *camera_eval_object;
+ Object *camera_orig_object;
+ /** Only available when rendering for final render. */
+ const RenderLayer *render_layer;
+ RenderEngine *render;
+ /** Only available when rendering for viewport. */
+ const DRWView *drw_view;
+ const View3D *v3d;
+ const RegionView3D *rv3d;
+
+ /** Can be null. Used to exclude objects during baking. */
+ const struct LightProbe *baking_probe = nullptr;
+
+ eDebugMode debug_mode = SHADOW_DEBUG_NONE;
+
+ /* Info string displayed at the top of the render / viewport. */
+ char info[64];
+
+ public:
+ Instance(ShaderModule &shared_shaders)
+ : shaders(shared_shaders),
+ render_passes(*this),
+ shading_passes(*this),
+ main_view(*this),
+ camera(*this),
+ world(*this),
+ velocity(*this),
+ motion_blur(*this),
+ lights(*this),
+ lightprobes(*this),
+ raytracing(*this),
+ hiz(*this),
+ shadows(*this),
+ subsurface(*this),
+ sync(*this),
+ materials(*this),
+ lookdev(*this){};
+ ~Instance(){};
+
+ void init(const ivec2 &output_res,
+ const rcti *output_rect,
+ RenderEngine *render,
+ Depsgraph *depsgraph,
+ const struct LightProbe *light_probe_ = nullptr,
+ Object *camera_object = nullptr,
+ const RenderLayer *render_layer = nullptr,
+ const DRWView *drw_view = nullptr,
+ const View3D *v3d = nullptr,
+ const RegionView3D *rv3d = nullptr);
+
+ void begin_sync(void);
+ void object_sync(Object *ob);
+ void end_sync(void);
+
+ void render_sync(void);
+ void render_frame(RenderLayer *render_layer, const char *view_name);
+
+ void draw_viewport(DefaultFramebufferList *dfbl);
+
+ bool finished(void) const;
+
+ bool is_viewport(void)
+ {
+ return !DRW_state_is_scene_render();
+ }
+
+ bool use_scene_light(void) const
+ {
+ return (!v3d) ||
+ ((v3d->shading.type == OB_MATERIAL) &&
+ (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS)) ||
+ ((v3d->shading.type == OB_RENDER) &&
+ (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER));
+ }
+
+ /* Do we light the scene using the HDRI setup in the viewport settings. */
+ bool use_studio_light(void) const
+ {
+ return (v3d) && (((v3d->shading.type == OB_MATERIAL) &&
+ ((v3d->shading.flag & V3D_SHADING_SCENE_WORLD) == 0)) ||
+ ((v3d->shading.type == OB_RENDER) &&
+ ((v3d->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER) == 0)));
+ }
+
+ private:
+ void render_sample(void);
+ static void object_sync_render(void *instance_,
+ Object *ob,
+ RenderEngine *engine,
+ Depsgraph *depsgraph);
+
+ void mesh_sync(Object *ob, ObjectHandle &ob_handle);
+ void gpencil_sync(Object *ob, ObjectHandle &ob_handle);
+ void hair_sync(Object *ob, ObjectHandle &ob_handle, ModifierData *modifier_data = nullptr);
+
+ rcti output_crop(const int output_res[2], const rcti *crop);
+
+ void set_time(float time);
+ void update_eval_members(void);
+};
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_light.cc b/source/blender/draw/engines/eevee/eevee_light.cc
new file mode 100644
index 00000000000..bcc68d773f5
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_light.cc
@@ -0,0 +1,482 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * The light module manages light data buffers and light culling system.
+ */
+
+#include "eevee_instance.hh"
+
+#include "eevee_light.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name LightData
+ * \{ */
+
+static eLightType to_light_type(short blender_light_type, short blender_area_type)
+{
+ switch (blender_light_type) {
+ default:
+ case LA_LOCAL:
+ return LIGHT_POINT;
+ case LA_SUN:
+ return LIGHT_SUN;
+ case LA_SPOT:
+ return LIGHT_SPOT;
+ case LA_AREA:
+ return ELEM(blender_area_type, LA_AREA_DISK, LA_AREA_ELLIPSE) ? LIGHT_ELLIPSE : LIGHT_RECT;
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Light Object
+ * \{ */
+
+void Light::sync(ShadowModule &shadows, const Object *ob, float threshold)
+{
+ const ::Light *la = (const ::Light *)ob->data;
+ float scale[3];
+
+ float max_power = max_fff(la->r, la->g, la->b) * fabsf(la->energy / 100.0f);
+ float surface_max_power = max_ff(la->diff_fac, la->spec_fac) * max_power;
+ float volume_max_power = la->volume_fac * max_power;
+
+ float influence_radius_surface = attenuation_radius_get(la, threshold, surface_max_power);
+ float influence_radius_volume = attenuation_radius_get(la, threshold, volume_max_power);
+
+ this->influence_radius_max = max_ff(influence_radius_surface, influence_radius_volume);
+ this->influence_radius_invsqr_surface = (influence_radius_surface > 1e-8f) ?
+ (1.0f / square_f(influence_radius_surface)) :
+ 0.0f;
+ this->influence_radius_invsqr_volume = (influence_radius_volume > 1e-8f) ?
+ (1.0f / square_f(influence_radius_volume)) :
+ 0.0f;
+
+ this->color = vec3(&la->r) * la->energy;
+ normalize_m4_m4_ex(this->object_mat, ob->obmat, scale);
+ /* Make sure we have consistent handedness (in case of negatively scaled Z axis). */
+ vec3 cross = math::cross(float3(this->_right), float3(this->_up));
+ if (math::dot(cross, float3(this->_back)) < 0.0f) {
+ negate_v3(this->_up);
+ }
+
+ shape_parameters_set(la, scale);
+
+ float shape_power = shape_power_get(la);
+ float point_power = point_power_get(la);
+ this->diffuse_power = la->diff_fac * shape_power;
+ this->transmit_power = la->diff_fac * point_power;
+ this->specular_power = la->spec_fac * shape_power;
+ this->volume_power = la->volume_fac * point_power;
+
+ eLightType new_type = to_light_type(la->type, la->area_shape);
+ if (this->type != new_type) {
+ shadow_discard_safe(shadows);
+ this->type = new_type;
+ }
+
+ if (la->mode & LA_SHADOW) {
+ if (la->type == LA_SUN) {
+ if (this->shadow_id == LIGHT_NO_SHADOW) {
+ this->shadow_id = shadows.directionals.alloc();
+ }
+
+ ShadowDirectional &shadow = shadows.directionals[this->shadow_id];
+ shadow.sync(this->object_mat, la->bias * 0.05f, 1.0f);
+ }
+ else {
+ float cone_aperture = DEG2RAD(360.0);
+ if (la->type == LA_SPOT) {
+ cone_aperture = min_ff(DEG2RAD(179.9), la->spotsize);
+ }
+ else if (la->type == LA_AREA) {
+ cone_aperture = DEG2RAD(179.9);
+ }
+
+ if (this->shadow_id == LIGHT_NO_SHADOW) {
+ this->shadow_id = shadows.punctuals.alloc();
+ }
+
+ ShadowPunctual &shadow = shadows.punctuals[this->shadow_id];
+ shadow.sync(this->type,
+ this->object_mat,
+ cone_aperture,
+ la->clipsta,
+ this->influence_radius_max,
+ la->bias * 0.05f);
+ }
+ }
+ else {
+ shadow_discard_safe(shadows);
+ }
+
+ this->initialized = true;
+}
+
+void Light::shadow_discard_safe(ShadowModule &shadows)
+{
+ if (shadow_id != LIGHT_NO_SHADOW) {
+ if (this->type != LIGHT_SUN) {
+ shadows.punctuals.free(shadow_id);
+ }
+ else {
+ shadows.directionals.free(shadow_id);
+ }
+ shadow_id = LIGHT_NO_SHADOW;
+ }
+}
+
+/* Returns attenuation radius inversed & squared for easy bound checking inside the shader. */
+float Light::attenuation_radius_get(const ::Light *la, float light_threshold, float light_power)
+{
+ if (la->type == LA_SUN) {
+ return (light_power > 1e-5f) ? 1e16f : 0.0f;
+ }
+
+ if (la->mode & LA_CUSTOM_ATTENUATION) {
+ return la->att_dist;
+ }
+ /* Compute the distance (using the inverse square law)
+ * at which the light power reaches the light_threshold. */
+ /* TODO take area light scale into account. */
+ return sqrtf(light_power / light_threshold);
+}
+
+void Light::shape_parameters_set(const ::Light *la, const float scale[3])
+{
+ if (la->type == LA_AREA) {
+ float area_size_y = (ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE)) ? la->area_sizey :
+ la->area_size;
+ _area_size_x = max_ff(0.003f, la->area_size * scale[0] * 0.5f);
+ _area_size_y = max_ff(0.003f, area_size_y * scale[1] * 0.5f);
+ /* For volume point lighting. */
+ radius_squared = max_ff(0.001f, hypotf(_area_size_x, _area_size_y) * 0.5f);
+ radius_squared = square_f(radius_squared);
+ }
+ else {
+ if (la->type == LA_SPOT) {
+ /* Spot size & blend */
+ spot_size_inv[0] = scale[2] / scale[0];
+ spot_size_inv[1] = scale[2] / scale[1];
+ float spot_size = cosf(la->spotsize * 0.5f);
+ float spot_blend = (1.0f - spot_size) * la->spotblend;
+ _spot_mul = 1.0f / max_ff(1e-8f, spot_blend);
+ _spot_bias = -spot_size * _spot_mul;
+ }
+
+ if (la->type == LA_SUN) {
+ _area_size_x = max_ff(0.001f, tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f));
+ _area_size_y = _area_size_x;
+ }
+ else {
+ _area_size_x = _area_size_y = max_ff(0.001f, la->area_size);
+ }
+ radius_squared = square_f(_area_size_x);
+ }
+}
+
+float Light::shape_power_get(const ::Light *la)
+{
+ float power;
+ /* Make illumination power constant */
+ if (la->type == LA_AREA) {
+ float area = _area_size_x * _area_size_y;
+ power = 1.0f / (area * 4.0f * float(M_PI));
+ /* FIXME : Empirical, Fit cycles power */
+ power *= 0.8f;
+ if (ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) {
+ /* Scale power to account for the lower area of the ellipse compared to the surrounding
+ * rectangle. */
+ power *= 4.0f / M_PI;
+ }
+ }
+ else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) {
+ power = 1.0f / (4.0f * square_f(_radius) * float(M_PI * M_PI));
+ }
+ else { /* LA_SUN */
+ power = 1.0f / (square_f(_radius) * float(M_PI));
+ /* Make illumination power closer to cycles for bigger radii. Cycles uses a cos^3 term that
+ * we cannot reproduce so we account for that by scaling the light power. This function is
+ * the result of a rough manual fitting. */
+ /* Simplification of:
+ * power *= 1 + r²/2 */
+ power += 1.0f / (2.0f * M_PI);
+ }
+ return power;
+}
+
+float Light::point_power_get(const ::Light *la)
+{
+ /* Volume light is evaluated as point lights. Remove the shape power. */
+ if (la->type == LA_AREA) {
+ /* Match cycles. Empirical fit... must correspond to some constant. */
+ float power = 0.0792f * M_PI;
+
+ /* This corrects for area light most representative point trick. The fit was found by
+ * reducing the average error compared to cycles. */
+ float area = _area_size_x * _area_size_y;
+ float tmp = M_PI_2 / (M_PI_2 + sqrtf(area));
+ /* Lerp between 1.0 and the limit (1 / pi). */
+ power *= tmp + (1.0f - tmp) * M_1_PI;
+
+ return power;
+ }
+ else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) {
+ /* Match cycles. Empirical fit... must correspond to some constant. */
+ return 0.0792f;
+ }
+ else { /* LA_SUN */
+ return 1.0f;
+ }
+}
+
+void Light::debug_draw(void)
+{
+ const float color[4] = {0.8, 0.3, 0, 1};
+ DRW_debug_sphere(_position, influence_radius_max, color);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name LightModule
+ * \{ */
+
+void LightModule::begin_sync(void)
+{
+ /* In begin_sync so it can be aninated. */
+ float light_threshold = max_ff(1e-16f, inst_.scene->eevee.light_threshold);
+ if (light_threshold != light_threshold_) {
+ light_threshold_ = light_threshold;
+ inst_.sampling.reset();
+ }
+}
+
+void LightModule::sync_light(const Object *ob, ObjectHandle &handle)
+{
+ Light &light = lights_.lookup_or_add_default(handle.object_key);
+ light.used = true;
+ if (handle.recalc != 0 || !light.initialized) {
+ light.sync(inst_.shadows, ob, light_threshold_);
+ }
+}
+
+void LightModule::end_sync(void)
+{
+ Vector<ObjectKey, 0> deleted_keys;
+
+ light_refs_.clear();
+
+ /* Detect light deletion. */
+ culling_data.items_no_cull_count = 0;
+ for (auto item : lights_.items()) {
+ Light &light = item.value;
+ if (!light.used) {
+ deleted_keys.append(item.key);
+ light.shadow_discard_safe(inst_.shadows);
+ }
+ else {
+ light.used = false;
+ light_refs_.append(&light);
+
+ if (light.type == LIGHT_SUN) {
+ culling_data.items_no_cull_count++;
+ }
+ }
+ }
+
+ if (deleted_keys.size() > 0) {
+ inst_.sampling.reset();
+ }
+ for (auto key : deleted_keys) {
+ lights_.remove(key);
+ }
+
+ if (light_refs_.size() > CULLING_MAX_ITEM) {
+ /* TODO(fclem) Print error to user. */
+ light_refs_.resize(CULLING_MAX_ITEM);
+ }
+
+ batch_len_ = divide_ceil_u(max_ii(light_refs_.size(), 1), CULLING_BATCH_SIZE);
+ lights_data.resize(batch_len_ * CULLING_BATCH_SIZE);
+ culling_key_buf.resize(batch_len_ * CULLING_BATCH_SIZE);
+ culling_light_buf.resize(batch_len_ * CULLING_BATCH_SIZE);
+ culling_zbin_buf.resize(batch_len_ * CULLING_ZBIN_COUNT);
+ culling_data.items_count = light_refs_.size();
+ culling_data.tile_word_len = divide_ceil_u(max_ii(culling_data.items_count, 1), 32);
+
+ /* Call shadows.end_sync after light pruning to avoid packing deleted shadows. */
+ inst_.shadows.end_sync();
+
+ int direc_idx = 0;
+ int punct_idx = culling_data.items_no_cull_count;
+ for (auto l_idx : light_refs_.index_range()) {
+ Light &light = *light_refs_[l_idx];
+ int dst_idx = (light.type == LIGHT_SUN) ? direc_idx++ : punct_idx++;
+ lights_data[dst_idx] = light;
+
+ if (light.shadow_id != LIGHT_NO_SHADOW) {
+ if (light.type == LIGHT_SUN) {
+ lights_data[dst_idx].shadow_data = this->inst_.shadows.directionals[light.shadow_id];
+ }
+ else {
+ lights_data[dst_idx].shadow_data = this->inst_.shadows.punctuals[light.shadow_id];
+ }
+ }
+ }
+
+ lights_data.push_update();
+
+ {
+ culling_ps_ = DRW_pass_create("CullingLight", (DRWState)0);
+
+ uint lights_len = light_refs_.size();
+ uint batch_len = divide_ceil_u(lights_len, CULLING_BATCH_SIZE);
+
+ if (batch_len > 0) {
+ /* NOTE: We reference the buffers that may be resized or updated later. */
+ {
+ GPUShader *sh = inst_.shaders.static_shader_get(CULLING_SELECT);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_ps_);
+ DRW_shgroup_vertex_buffer(grp, "lights_buf", lights_data);
+ DRW_shgroup_vertex_buffer_ref(grp, "culling_buf", &culling_data);
+ DRW_shgroup_vertex_buffer(grp, "key_buf", culling_key_buf);
+ DRW_shgroup_call_compute(grp, batch_len, 1, 1);
+ DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE);
+ }
+ {
+ GPUShader *sh = inst_.shaders.static_shader_get(CULLING_SORT);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_ps_);
+ DRW_shgroup_vertex_buffer(grp, "lights_buf", lights_data);
+ DRW_shgroup_vertex_buffer_ref(grp, "culling_buf", &culling_data);
+ DRW_shgroup_vertex_buffer(grp, "key_buf", culling_key_buf);
+ DRW_shgroup_vertex_buffer_ref(grp, "out_zbins_buf", &culling_zbin_buf);
+ DRW_shgroup_vertex_buffer_ref(grp, "out_items_buf", &culling_light_buf);
+ DRW_shgroup_call_compute(grp, batch_len, 1, 1);
+ DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE);
+ }
+ {
+ GPUShader *sh = inst_.shaders.static_shader_get(CULLING_TILE);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_ps_);
+ DRW_shgroup_vertex_buffer(grp, "lights_buf", culling_light_buf);
+ DRW_shgroup_vertex_buffer_ref(grp, "culling_buf", &culling_data);
+ DRW_shgroup_vertex_buffer_ref(grp, "culling_tile_buf", &culling_tile_buf);
+ DRW_shgroup_call_compute_ref(grp, culling_tile_dispatch_size_);
+ DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH);
+ }
+ }
+ }
+
+ debug_end_sync();
+}
+
+void LightModule::debug_end_sync(void)
+{
+ if (inst_.debug_mode != eDebugMode::DEBUG_LIGHT_CULLING) {
+ debug_draw_ps_ = nullptr;
+ return;
+ }
+
+ debug_draw_ps_ = DRW_pass_create("CullingDebug", DRW_STATE_WRITE_COLOR);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(CULLING_DEBUG);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, debug_draw_ps_);
+ DRW_shgroup_vertex_buffer_ref(grp, "lights_buf", &culling_light_buf);
+ DRW_shgroup_vertex_buffer_ref(grp, "lights_culling_buf", &culling_data);
+ DRW_shgroup_vertex_buffer_ref(grp, "lights_zbins_buf", &culling_zbin_buf);
+ DRW_shgroup_vertex_buffer_ref(grp, "lights_tile_buf", &culling_tile_buf);
+ DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+}
+
+/* Compute acceleration structure for the given view. If extent is 0, bind no lights. */
+void LightModule::set_view(const DRWView *view, const ivec2 extent, bool enable_specular)
+{
+ const bool no_lights = (extent.x == 0);
+
+ /* Target 1bit per pixel. */
+ uint tile_size = 1u << log2_ceil_u(ceil(sqrtf(culling_data.tile_word_len * 32)));
+
+ int3 tiles_extent;
+ tiles_extent.x = divide_ceil_u(extent.x, tile_size);
+ tiles_extent.y = divide_ceil_u(extent.y, tile_size);
+ tiles_extent.z = batch_len_;
+
+ float far_z = DRW_view_far_distance_get(view);
+ float near_z = DRW_view_near_distance_get(view);
+
+ culling_data.zbin_scale = -CULLING_ZBIN_COUNT / fabsf(far_z - near_z);
+ culling_data.zbin_bias = -near_z * culling_data.zbin_scale;
+ culling_data.tile_size = tile_size;
+ culling_data.tile_x_len = tiles_extent.x;
+ culling_data.tile_y_len = tiles_extent.y;
+ culling_data.tile_to_uv_fac = tile_size / float2(UNPACK2(extent));
+
+ culling_data.enable_specular = enable_specular;
+ culling_data.items_count = no_lights ? 0 : light_refs_.size();
+ culling_data.visible_count = 0;
+ culling_data.push_update();
+
+ if (no_lights) {
+ return;
+ }
+
+ uint word_count = tiles_extent.x * tiles_extent.y * tiles_extent.z * culling_data.tile_word_len;
+
+ /* TODO(fclem) Only resize once per redraw. */
+ culling_tile_buf.resize(word_count);
+
+ culling_tile_dispatch_size_.x = divide_ceil_u(word_count, 1024);
+ culling_tile_dispatch_size_.y = 1;
+ culling_tile_dispatch_size_.z = 1;
+
+ DRW_view_set_active(view);
+ DRW_draw_pass(culling_ps_);
+}
+
+void LightModule::debug_draw(GPUFrameBuffer *view_fb, HiZBuffer &hiz)
+{
+ if (debug_draw_ps_ == nullptr) {
+ return;
+ }
+ input_depth_tx_ = hiz.texture_get();
+
+ GPU_framebuffer_bind(view_fb);
+ DRW_draw_pass(debug_draw_ps_);
+}
+
+void LightModule::shgroup_resources(DRWShadingGroup *grp)
+{
+ DRW_shgroup_vertex_buffer_ref(grp, "lights_buf", &culling_light_buf);
+ DRW_shgroup_vertex_buffer_ref(grp, "lights_culling_buf", &culling_data);
+ DRW_shgroup_vertex_buffer_ref(grp, "lights_zbins_buf", &culling_zbin_buf);
+ DRW_shgroup_vertex_buffer_ref(grp, "lights_tile_buf", &culling_tile_buf);
+
+ DRW_shgroup_uniform_texture(grp, "shadow_atlas_tx", inst_.shadows.atlas_tx_get());
+ DRW_shgroup_uniform_texture(grp, "shadow_tilemaps_tx", inst_.shadows.tilemap_tx_get());
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_light.hh b/source/blender/draw/engines/eevee/eevee_light.hh
new file mode 100644
index 00000000000..254d9231eef
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_light.hh
@@ -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 ivec2 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
diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c
deleted file mode 100644
index bcbe17fdabc..00000000000
--- a/source/blender/draw/engines/eevee/eevee_lightcache.c
+++ /dev/null
@@ -1,1533 +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 indirect lighting cache.
- */
-
-#include "DRW_render.h"
-
-#include "BKE_global.h"
-
-#include "BLI_endian_switch.h"
-#include "BLI_threads.h"
-
-#include "DEG_depsgraph_build.h"
-#include "DEG_depsgraph_query.h"
-
-#include "BKE_object.h"
-
-#include "DNA_collection_types.h"
-#include "DNA_lightprobe_types.h"
-
-#include "PIL_time.h"
-
-#include "eevee_lightcache.h"
-#include "eevee_private.h"
-
-#include "GPU_capabilities.h"
-#include "GPU_context.h"
-
-#include "WM_api.h"
-#include "WM_types.h"
-
-#include "BLO_read_write.h"
-
-#include "wm_window.h"
-
-/* Rounded to nearest PowerOfTwo */
-#if defined(IRRADIANCE_SH_L2)
-# define IRRADIANCE_SAMPLE_SIZE_X 4 /* 3 in reality */
-# define IRRADIANCE_SAMPLE_SIZE_Y 4 /* 3 in reality */
-#elif defined(IRRADIANCE_HL2)
-# define IRRADIANCE_SAMPLE_SIZE_X 4 /* 3 in reality */
-# define IRRADIANCE_SAMPLE_SIZE_Y 2
-#endif
-
-#ifdef IRRADIANCE_SH_L2
-/* we need a signed format for Spherical Harmonics */
-# define IRRADIANCE_FORMAT GPU_RGBA16F
-#else
-# define IRRADIANCE_FORMAT GPU_RGBA8
-#endif
-
-/* OpenGL 3.3 core requirement, can be extended but it's already very big */
-#define IRRADIANCE_MAX_POOL_LAYER 256
-#define IRRADIANCE_MAX_POOL_SIZE 1024
-#define MAX_IRRADIANCE_SAMPLES \
- (IRRADIANCE_MAX_POOL_SIZE / IRRADIANCE_SAMPLE_SIZE_X) * \
- (IRRADIANCE_MAX_POOL_SIZE / IRRADIANCE_SAMPLE_SIZE_Y)
-
-/* TODO: should be replace by a more elegant alternative. */
-extern void DRW_opengl_context_enable(void);
-extern void DRW_opengl_context_disable(void);
-
-extern void DRW_opengl_render_context_enable(void *re_gl_context);
-extern void DRW_opengl_render_context_disable(void *re_gl_context);
-extern void DRW_gpu_render_context_enable(void *re_gpu_context);
-extern void DRW_gpu_render_context_disable(void *re_gpu_context);
-
-typedef struct EEVEE_LightBake {
- Depsgraph *depsgraph;
- ViewLayer *view_layer;
- ViewLayer *view_layer_input;
- LightCache *lcache;
- Scene *scene;
- struct Main *bmain;
- EEVEE_ViewLayerData *sldata;
-
- /** Current probe being rendered. */
- LightProbe **probe;
- /** Target cube color texture. */
- GPUTexture *rt_color;
- /** Target cube depth texture. */
- GPUTexture *rt_depth;
- /** Target cube frame-buffers. */
- GPUFrameBuffer *rt_fb[6];
- /** Storage frame-buffer. */
- GPUFrameBuffer *store_fb;
- /** Cube render target resolution. */
- int rt_res;
-
- /* Shared */
- /** Target layer to store the data to. */
- int layer;
- /** Sample count for the convolution. */
- float samples_ct, invsamples_ct;
- /** Sampling bias during convolution step. */
- float lod_factor;
- /** Max cube-map LOD to sample when convolving. */
- float lod_max;
- /** Number of probes to render + world probe. */
- int cube_len, grid_len;
-
- /* Irradiance grid */
- /** Current probe being rendered (UBO data). */
- EEVEE_LightGrid *grid;
- /** Target cube-map at MIP 0. */
- int irr_cube_res;
- /** Size of the irradiance texture. */
- int irr_size[3];
- /** Total for all grids */
- int total_irr_samples;
- /** Nth sample of the current grid being rendered. */
- int grid_sample;
- /** Total number of samples for the current grid. */
- int grid_sample_len;
- /** Nth grid in the cache being rendered. */
- int grid_curr;
- /** The current light bounce being evaluated. */
- int bounce_curr, bounce_len;
- /** Resolution of the Visibility shadow-map. */
- float vis_res;
- /** Result of previous light bounce. */
- GPUTexture *grid_prev;
- /** Pointer to the owner_id of the probe object. */
- LightProbe **grid_prb;
-
- /* Reflection probe */
- /** Current probe being rendered (UBO data). */
- EEVEE_LightProbe *cube;
- /** Target cube-map at MIP 0. */
- int ref_cube_res;
- /** Index of the current cube. */
- int cube_offset;
- /** Pointer to the owner_id of the probe object. */
- LightProbe **cube_prb;
-
- /* Dummy Textures */
- struct GPUTexture *dummy_color, *dummy_depth;
- struct GPUTexture *dummy_layer_color;
-
- int total, done; /* to compute progress */
- short *stop, *do_update;
- float *progress;
-
- /** For only handling the resources. */
- bool resource_only;
- bool own_resources;
- /** If the light-cache was created for baking, it's first owned by the baker. */
- bool own_light_cache;
- /** ms. delay the start of the baking to not slowdown interactions (TODO: remove). */
- int delay;
- /** Scene frame to bake. */
- int frame;
-
- /** If running in parallel (in a separate thread), use this context. */
- void *gl_context, *gpu_context;
-
- ThreadMutex *mutex;
-} EEVEE_LightBake;
-
-/* -------------------------------------------------------------------- */
-/** \name Light Cache
- * \{ */
-
-/* Return memory footprint in bytes. */
-static uint eevee_lightcache_memsize_get(LightCache *lcache)
-{
- uint size = 0;
- if (lcache->grid_tx.data) {
- size += MEM_allocN_len(lcache->grid_tx.data);
- }
- if (lcache->cube_tx.data) {
- size += MEM_allocN_len(lcache->cube_tx.data);
- for (int mip = 0; mip < lcache->mips_len; mip++) {
- size += MEM_allocN_len(lcache->cube_mips[mip].data);
- }
- }
- return size;
-}
-
-static bool eevee_lightcache_version_check(const LightCache *lcache)
-{
- switch (lcache->type) {
- case LIGHTCACHE_TYPE_STATIC:
- return lcache->version == LIGHTCACHE_STATIC_VERSION;
- default:
- return false;
- }
-}
-
-static bool eevee_lightcache_can_be_saved(LightCache *lcache)
-{
- if (lcache->grid_tx.data) {
- if (MEM_allocN_len(lcache->grid_tx.data) >= INT_MAX) {
- return false;
- }
- }
- if (lcache->cube_tx.data) {
- if (MEM_allocN_len(lcache->cube_tx.data) >= INT_MAX) {
- return false;
- }
- }
- return true;
-}
-
-static int eevee_lightcache_irradiance_sample_count(LightCache *lcache)
-{
- int total_irr_samples = 0;
-
- for (int i = 1; i < lcache->grid_len; i++) {
- EEVEE_LightGrid *egrid = lcache->grid_data + i;
- total_irr_samples += egrid->resolution[0] * egrid->resolution[1] * egrid->resolution[2];
- }
- return total_irr_samples;
-}
-
-void EEVEE_lightcache_info_update(SceneEEVEE *eevee)
-{
- LightCache *lcache = eevee->light_cache_data;
-
- if (lcache != NULL) {
- if (!eevee_lightcache_version_check(lcache)) {
- BLI_strncpy(eevee->light_cache_info,
- TIP_("Incompatible Light cache version, please bake again"),
- sizeof(eevee->light_cache_info));
- return;
- }
-
- if (lcache->cube_tx.tex_size[2] > GPU_max_texture_layers()) {
- BLI_strncpy(eevee->light_cache_info,
- TIP_("Error: Light cache is too big for the GPU to be loaded"),
- sizeof(eevee->light_cache_info));
- return;
- }
-
- if (lcache->flag & LIGHTCACHE_INVALID) {
- BLI_strncpy(eevee->light_cache_info,
- TIP_("Error: Light cache dimensions not supported by the GPU"),
- sizeof(eevee->light_cache_info));
- return;
- }
-
- if (lcache->flag & LIGHTCACHE_BAKING) {
- BLI_strncpy(
- eevee->light_cache_info, TIP_("Baking light cache"), sizeof(eevee->light_cache_info));
- return;
- }
-
- if (!eevee_lightcache_can_be_saved(lcache)) {
- BLI_strncpy(eevee->light_cache_info,
- TIP_("Error: LightCache is too large and will not be saved to disk"),
- sizeof(eevee->light_cache_info));
- return;
- }
-
- char formatted_mem[15];
- BLI_str_format_byte_unit(formatted_mem, eevee_lightcache_memsize_get(lcache), false);
-
- int irr_samples = eevee_lightcache_irradiance_sample_count(lcache);
-
- BLI_snprintf(eevee->light_cache_info,
- sizeof(eevee->light_cache_info),
- TIP_("%d Ref. Cubemaps, %d Irr. Samples (%s in memory)"),
- lcache->cube_len - 1,
- irr_samples,
- formatted_mem);
- }
- else {
- BLI_strncpy(eevee->light_cache_info,
- TIP_("No light cache in this scene"),
- sizeof(eevee->light_cache_info));
- }
-}
-
-static void irradiance_pool_size_get(int visibility_size, int total_samples, int r_size[3])
-{
- /* Compute how many irradiance samples we can store per visibility sample. */
- int irr_per_vis = (visibility_size / IRRADIANCE_SAMPLE_SIZE_X) *
- (visibility_size / IRRADIANCE_SAMPLE_SIZE_Y);
-
- /* The irradiance itself take one layer, hence the +1 */
- int layer_ct = MIN2(irr_per_vis + 1, IRRADIANCE_MAX_POOL_LAYER);
-
- int texel_ct = (int)ceilf((float)total_samples / (float)(layer_ct - 1));
- r_size[0] = visibility_size *
- max_ii(1, min_ii(texel_ct, (IRRADIANCE_MAX_POOL_SIZE / visibility_size)));
- r_size[1] = visibility_size *
- max_ii(1, (texel_ct / (IRRADIANCE_MAX_POOL_SIZE / visibility_size)));
- r_size[2] = layer_ct;
-}
-
-static bool EEVEE_lightcache_validate(const LightCache *light_cache,
- const int cube_len,
- const int cube_res,
- const int grid_len,
- const int irr_size[3])
-{
- if (light_cache == NULL) {
- return false;
- }
- if (!eevee_lightcache_version_check(light_cache)) {
- return false;
- }
-
- if (!(light_cache->flag & LIGHTCACHE_INVALID)) {
- /* See if we need the same amount of texture space. */
- if ((irr_size[0] == light_cache->grid_tx.tex_size[0]) &&
- (irr_size[1] == light_cache->grid_tx.tex_size[1]) &&
- (irr_size[2] == light_cache->grid_tx.tex_size[2]) && (grid_len == light_cache->grid_len)) {
- int mip_len = log2_floor_u(cube_res) - MIN_CUBE_LOD_LEVEL;
- if ((cube_res == light_cache->cube_tx.tex_size[0]) &&
- (cube_len == light_cache->cube_tx.tex_size[2] / 6) &&
- (cube_len == light_cache->cube_len) && (mip_len == light_cache->mips_len)) {
- return true;
- }
- }
- }
- return false;
-}
-
-LightCache *EEVEE_lightcache_create(const int grid_len,
- const int cube_len,
- const int cube_size,
- const int vis_size,
- const int irr_size[3])
-{
- LightCache *light_cache = MEM_callocN(sizeof(LightCache), "LightCache");
-
- light_cache->version = LIGHTCACHE_STATIC_VERSION;
- light_cache->type = LIGHTCACHE_TYPE_STATIC;
-
- light_cache->cube_data = MEM_callocN(sizeof(EEVEE_LightProbe) * cube_len, "EEVEE_LightProbe");
- light_cache->grid_data = MEM_callocN(sizeof(EEVEE_LightGrid) * grid_len, "EEVEE_LightGrid");
-
- light_cache->grid_tx.tex = DRW_texture_create_2d_array(
- irr_size[0], irr_size[1], irr_size[2], IRRADIANCE_FORMAT, DRW_TEX_FILTER, NULL);
- light_cache->grid_tx.tex_size[0] = irr_size[0];
- light_cache->grid_tx.tex_size[1] = irr_size[1];
- light_cache->grid_tx.tex_size[2] = irr_size[2];
-
- int mips_len = log2_floor_u(cube_size) - MIN_CUBE_LOD_LEVEL;
-
- /* Try to create a cubemap array. */
- DRWTextureFlag cube_texflag = DRW_TEX_FILTER | DRW_TEX_MIPMAP;
- light_cache->cube_tx.tex = DRW_texture_create_cube_array(
- cube_size, cube_len, GPU_R11F_G11F_B10F, cube_texflag, NULL);
- if (light_cache->cube_tx.tex == NULL) {
- /* Try fallback to 2D array. */
- light_cache->cube_tx.tex = DRW_texture_create_2d_array(
- cube_size, cube_size, cube_len * 6, GPU_R11F_G11F_B10F, cube_texflag, NULL);
- }
-
- light_cache->cube_tx.tex_size[0] = cube_size;
- light_cache->cube_tx.tex_size[1] = cube_size;
- light_cache->cube_tx.tex_size[2] = cube_len * 6;
-
- light_cache->mips_len = mips_len;
- light_cache->vis_res = vis_size;
- light_cache->ref_res = cube_size;
-
- light_cache->cube_mips = MEM_callocN(sizeof(LightCacheTexture) * light_cache->mips_len,
- "LightCacheTexture");
-
- if (light_cache->grid_tx.tex == NULL || light_cache->cube_tx.tex == NULL) {
- /* We could not create the requested textures size. Stop baking and do not use the cache. */
- light_cache->flag = LIGHTCACHE_INVALID;
- }
- else {
- light_cache->flag = LIGHTCACHE_UPDATE_WORLD | LIGHTCACHE_UPDATE_CUBE | LIGHTCACHE_UPDATE_GRID;
-
- for (int mip = 0; mip < light_cache->mips_len; mip++) {
- GPU_texture_get_mipmap_size(
- light_cache->cube_tx.tex, mip + 1, light_cache->cube_mips[mip].tex_size);
- }
- }
-
- return light_cache;
-}
-
-static bool eevee_lightcache_static_load(LightCache *lcache)
-{
- /* We use fallback if a texture is not setup and there is no data to restore it. */
- if ((!lcache->grid_tx.tex && !lcache->grid_tx.data) || !lcache->grid_data ||
- (!lcache->cube_tx.tex && !lcache->cube_tx.data) || !lcache->cube_data) {
- return false;
- }
- /* If cache is too big for this GPU. */
- if (lcache->cube_tx.tex_size[2] > GPU_max_texture_layers()) {
- return false;
- }
-
- if (lcache->grid_tx.tex == NULL) {
- lcache->grid_tx.tex = GPU_texture_create_2d_array(
- "lightcache_irradiance", UNPACK3(lcache->grid_tx.tex_size), 1, IRRADIANCE_FORMAT, NULL);
- GPU_texture_update(lcache->grid_tx.tex, GPU_DATA_UBYTE, lcache->grid_tx.data);
-
- if (lcache->grid_tx.tex == NULL) {
- lcache->flag |= LIGHTCACHE_NOT_USABLE;
- return false;
- }
-
- GPU_texture_filter_mode(lcache->grid_tx.tex, true);
- }
-
- if (lcache->cube_tx.tex == NULL) {
- /* Try to create a cubemap array. */
- lcache->cube_tx.tex = GPU_texture_create_cube_array("lightcache_cubemaps",
- lcache->cube_tx.tex_size[0],
- lcache->cube_tx.tex_size[2] / 6,
- lcache->mips_len + 1,
- GPU_R11F_G11F_B10F,
- NULL);
-
- if (lcache->cube_tx.tex == NULL) {
- /* Try fallback to 2D array. */
- lcache->cube_tx.tex = GPU_texture_create_2d_array("lightcache_cubemaps_fallback",
- UNPACK3(lcache->cube_tx.tex_size),
- lcache->mips_len + 1,
- GPU_R11F_G11F_B10F,
- NULL);
- }
-
- if (lcache->cube_tx.tex == NULL) {
- lcache->flag |= LIGHTCACHE_NOT_USABLE;
- return false;
- }
-
- for (int mip = 0; mip <= lcache->mips_len; mip++) {
- const void *data = (mip == 0) ? lcache->cube_tx.data : lcache->cube_mips[mip - 1].data;
- GPU_texture_update_mipmap(lcache->cube_tx.tex, mip, GPU_DATA_10_11_11_REV, data);
- }
- GPU_texture_mipmap_mode(lcache->cube_tx.tex, true, true);
- }
- return true;
-}
-
-bool EEVEE_lightcache_load(LightCache *lcache)
-{
- if (lcache == NULL) {
- return false;
- }
-
- if (!eevee_lightcache_version_check(lcache)) {
- return false;
- }
-
- if (lcache->flag & (LIGHTCACHE_INVALID | LIGHTCACHE_NOT_USABLE)) {
- return false;
- }
-
- switch (lcache->type) {
- case LIGHTCACHE_TYPE_STATIC:
- return eevee_lightcache_static_load(lcache);
- default:
- return false;
- }
-}
-
-static void eevee_lightbake_readback_irradiance(LightCache *lcache)
-{
- MEM_SAFE_FREE(lcache->grid_tx.data);
- lcache->grid_tx.data = GPU_texture_read(lcache->grid_tx.tex, GPU_DATA_UBYTE, 0);
- lcache->grid_tx.data_type = LIGHTCACHETEX_BYTE;
- lcache->grid_tx.components = 4;
-}
-
-static void eevee_lightbake_readback_reflections(LightCache *lcache)
-{
- MEM_SAFE_FREE(lcache->cube_tx.data);
- lcache->cube_tx.data = GPU_texture_read(lcache->cube_tx.tex, GPU_DATA_10_11_11_REV, 0);
- lcache->cube_tx.data_type = LIGHTCACHETEX_UINT;
- lcache->cube_tx.components = 1;
-
- for (int mip = 0; mip < lcache->mips_len; mip++) {
- LightCacheTexture *cube_mip = lcache->cube_mips + mip;
- MEM_SAFE_FREE(cube_mip->data);
- GPU_texture_get_mipmap_size(lcache->cube_tx.tex, mip + 1, cube_mip->tex_size);
-
- cube_mip->data = GPU_texture_read(lcache->cube_tx.tex, GPU_DATA_10_11_11_REV, mip + 1);
- cube_mip->data_type = LIGHTCACHETEX_UINT;
- cube_mip->components = 1;
- }
-}
-
-void EEVEE_lightcache_free(LightCache *lcache)
-{
- DRW_TEXTURE_FREE_SAFE(lcache->cube_tx.tex);
- MEM_SAFE_FREE(lcache->cube_tx.data);
- DRW_TEXTURE_FREE_SAFE(lcache->grid_tx.tex);
- MEM_SAFE_FREE(lcache->grid_tx.data);
-
- if (lcache->cube_mips) {
- for (int i = 0; i < lcache->mips_len; i++) {
- MEM_SAFE_FREE(lcache->cube_mips[i].data);
- }
- MEM_SAFE_FREE(lcache->cube_mips);
- }
-
- MEM_SAFE_FREE(lcache->cube_data);
- MEM_SAFE_FREE(lcache->grid_data);
- MEM_freeN(lcache);
-}
-
-static void write_lightcache_texture(BlendWriter *writer, LightCacheTexture *tex)
-{
- if (tex->data) {
- size_t data_size = tex->components * tex->tex_size[0] * tex->tex_size[1] * tex->tex_size[2];
- if (tex->data_type == LIGHTCACHETEX_FLOAT) {
- data_size *= sizeof(float);
- }
- else if (tex->data_type == LIGHTCACHETEX_UINT) {
- data_size *= sizeof(uint);
- }
-
- /* FIXME: We can't save more than what 32bit systems can handle.
- * The solution would be to split the texture but it is too late for 2.90. (see T78529) */
- if (data_size < INT_MAX) {
- BLO_write_raw(writer, data_size, tex->data);
- }
- }
-}
-
-void EEVEE_lightcache_blend_write(BlendWriter *writer, LightCache *cache)
-{
- write_lightcache_texture(writer, &cache->grid_tx);
- write_lightcache_texture(writer, &cache->cube_tx);
-
- if (cache->cube_mips) {
- BLO_write_struct_array(writer, LightCacheTexture, cache->mips_len, cache->cube_mips);
- for (int i = 0; i < cache->mips_len; i++) {
- write_lightcache_texture(writer, &cache->cube_mips[i]);
- }
- }
-
- BLO_write_struct_array(writer, LightGridCache, cache->grid_len, cache->grid_data);
- BLO_write_struct_array(writer, LightProbeCache, cache->cube_len, cache->cube_data);
-}
-
-static void direct_link_lightcache_texture(BlendDataReader *reader, LightCacheTexture *lctex)
-{
- lctex->tex = NULL;
-
- if (lctex->data) {
- BLO_read_data_address(reader, &lctex->data);
- if (lctex->data && BLO_read_requires_endian_switch(reader)) {
- int data_size = lctex->components * lctex->tex_size[0] * lctex->tex_size[1] *
- lctex->tex_size[2];
-
- if (lctex->data_type == LIGHTCACHETEX_FLOAT) {
- BLI_endian_switch_float_array((float *)lctex->data, data_size * sizeof(float));
- }
- else if (lctex->data_type == LIGHTCACHETEX_UINT) {
- BLI_endian_switch_uint32_array((uint *)lctex->data, data_size * sizeof(uint));
- }
- }
- }
-
- if (lctex->data == NULL) {
- zero_v3_int(lctex->tex_size);
- }
-}
-
-void EEVEE_lightcache_blend_read_data(BlendDataReader *reader, LightCache *cache)
-{
- cache->flag &= ~LIGHTCACHE_NOT_USABLE;
- direct_link_lightcache_texture(reader, &cache->cube_tx);
- direct_link_lightcache_texture(reader, &cache->grid_tx);
-
- if (cache->cube_mips) {
- BLO_read_data_address(reader, &cache->cube_mips);
- for (int i = 0; i < cache->mips_len; i++) {
- direct_link_lightcache_texture(reader, &cache->cube_mips[i]);
- }
- }
-
- BLO_read_data_address(reader, &cache->cube_data);
- BLO_read_data_address(reader, &cache->grid_data);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Light Bake Context
- * \{ */
-
-static void eevee_lightbake_context_enable(EEVEE_LightBake *lbake)
-{
- if (GPU_use_main_context_workaround() && !BLI_thread_is_main()) {
- GPU_context_main_lock();
- DRW_opengl_context_enable();
- return;
- }
-
- if (lbake->gl_context) {
- DRW_opengl_render_context_enable(lbake->gl_context);
- if (lbake->gpu_context == NULL) {
- lbake->gpu_context = GPU_context_create(NULL);
- }
- DRW_gpu_render_context_enable(lbake->gpu_context);
- }
- else {
- DRW_opengl_context_enable();
- }
-}
-
-static void eevee_lightbake_context_disable(EEVEE_LightBake *lbake)
-{
- if (GPU_use_main_context_workaround() && !BLI_thread_is_main()) {
- DRW_opengl_context_disable();
- GPU_context_main_unlock();
- return;
- }
-
- if (lbake->gl_context) {
- DRW_gpu_render_context_disable(lbake->gpu_context);
- DRW_opengl_render_context_disable(lbake->gl_context);
- }
- else {
- DRW_opengl_context_disable();
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Light Bake Job
- * \{ */
-
-static void eevee_lightbake_count_probes(EEVEE_LightBake *lbake)
-{
- Depsgraph *depsgraph = lbake->depsgraph;
-
- /* At least one of each for the world */
- lbake->grid_len = lbake->cube_len = lbake->total_irr_samples = 1;
-
- DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob) {
- const int ob_visibility = BKE_object_visibility(ob, DAG_EVAL_RENDER);
- if ((ob_visibility & OB_VISIBLE_SELF) == 0) {
- continue;
- }
-
- if (ob->type == OB_LIGHTPROBE) {
- LightProbe *prb = (LightProbe *)ob->data;
-
- if (prb->type == LIGHTPROBE_TYPE_GRID) {
- lbake->total_irr_samples += prb->grid_resolution_x * prb->grid_resolution_y *
- prb->grid_resolution_z;
- lbake->grid_len++;
- }
- else if (prb->type == LIGHTPROBE_TYPE_CUBE && lbake->cube_len < EEVEE_PROBE_MAX) {
- lbake->cube_len++;
- }
- }
- }
- DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END;
-}
-
-static void eevee_lightbake_create_render_target(EEVEE_LightBake *lbake, int rt_res)
-{
- lbake->rt_depth = DRW_texture_create_cube(rt_res, GPU_DEPTH_COMPONENT24, 0, NULL);
- lbake->rt_color = DRW_texture_create_cube(
- rt_res, GPU_RGBA16F, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL);
-
- for (int i = 0; i < 6; i++) {
- GPU_framebuffer_ensure_config(&lbake->rt_fb[i],
- {GPU_ATTACHMENT_TEXTURE_CUBEFACE(lbake->rt_depth, i),
- GPU_ATTACHMENT_TEXTURE_CUBEFACE(lbake->rt_color, i)});
- }
-
- GPU_framebuffer_ensure_config(&lbake->store_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_NONE});
-}
-
-static void eevee_lightbake_create_resources(EEVEE_LightBake *lbake)
-{
- Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph);
- SceneEEVEE *eevee = &scene_eval->eevee;
-
- lbake->bounce_len = eevee->gi_diffuse_bounces;
- lbake->vis_res = eevee->gi_visibility_resolution;
- lbake->rt_res = eevee->gi_cubemap_resolution;
-
- irradiance_pool_size_get(lbake->vis_res, lbake->total_irr_samples, lbake->irr_size);
-
- lbake->ref_cube_res = lbake->rt_res;
- lbake->cube_prb = MEM_callocN(sizeof(LightProbe *) * lbake->cube_len, "EEVEE Cube visgroup ptr");
- lbake->grid_prb = MEM_callocN(sizeof(LightProbe *) * lbake->grid_len, "EEVEE Grid visgroup ptr");
-
- lbake->grid_prev = DRW_texture_create_2d_array(lbake->irr_size[0],
- lbake->irr_size[1],
- lbake->irr_size[2],
- IRRADIANCE_FORMAT,
- DRW_TEX_FILTER,
- NULL);
-
- /* Ensure Light Cache is ready to accept new data. If not recreate one.
- * WARNING: All the following must be threadsafe. It's currently protected
- * by the DRW mutex. */
- lbake->lcache = eevee->light_cache_data;
-
- /* TODO: validate irradiance and reflection cache independently... */
- if (!EEVEE_lightcache_validate(
- lbake->lcache, lbake->cube_len, lbake->ref_cube_res, lbake->grid_len, lbake->irr_size)) {
- eevee->light_cache_data = lbake->lcache = NULL;
- }
-
- if (lbake->lcache == NULL) {
- lbake->lcache = EEVEE_lightcache_create(
- lbake->grid_len, lbake->cube_len, lbake->ref_cube_res, lbake->vis_res, lbake->irr_size);
-
- lbake->own_light_cache = true;
-
- eevee->light_cache_data = lbake->lcache;
- }
-
- EEVEE_lightcache_load(eevee->light_cache_data);
-
- lbake->lcache->flag |= LIGHTCACHE_BAKING;
- lbake->lcache->cube_len = 1;
-}
-
-wmJob *EEVEE_lightbake_job_create(struct wmWindowManager *wm,
- struct wmWindow *win,
- struct Main *bmain,
- struct ViewLayer *view_layer,
- struct Scene *scene,
- int delay,
- int frame)
-{
- EEVEE_LightBake *lbake = NULL;
-
- /* only one render job at a time */
- if (WM_jobs_test(wm, scene, WM_JOB_TYPE_RENDER)) {
- return NULL;
- }
-
- wmJob *wm_job = WM_jobs_get(wm,
- win,
- scene,
- "Bake Lighting",
- WM_JOB_EXCL_RENDER | WM_JOB_PRIORITY | WM_JOB_PROGRESS,
- WM_JOB_TYPE_LIGHT_BAKE);
-
- /* If job exists do not recreate context and depsgraph. */
- EEVEE_LightBake *old_lbake = (EEVEE_LightBake *)WM_jobs_customdata_get(wm_job);
-
- if (old_lbake && (old_lbake->view_layer_input == view_layer) && (old_lbake->bmain == bmain)) {
- lbake = MEM_callocN(sizeof(EEVEE_LightBake), "EEVEE_LightBake");
- /* Cannot reuse depsgraph for now because we cannot get the update from the
- * main database directly. TODO: reuse depsgraph and only update positions. */
- /* lbake->depsgraph = old_lbake->depsgraph; */
- lbake->depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER);
-
- lbake->mutex = BLI_mutex_alloc();
-
- BLI_mutex_lock(old_lbake->mutex);
- old_lbake->own_resources = false;
-
- lbake->scene = scene;
- lbake->bmain = bmain;
- lbake->view_layer_input = view_layer;
- lbake->gl_context = old_lbake->gl_context;
- lbake->own_resources = true;
- lbake->delay = delay;
- lbake->frame = frame;
-
- if (lbake->gl_context == NULL && !GPU_use_main_context_workaround()) {
- lbake->gl_context = WM_opengl_context_create();
- wm_window_reset_drawable();
- }
-
- if (old_lbake->stop != NULL) {
- *old_lbake->stop = 1;
- }
- BLI_mutex_unlock(old_lbake->mutex);
- }
- else {
- lbake = EEVEE_lightbake_job_data_alloc(bmain, view_layer, scene, true, frame);
- lbake->delay = delay;
- }
-
- WM_jobs_customdata_set(wm_job, lbake, EEVEE_lightbake_job_data_free);
- WM_jobs_timer(wm_job, 0.4, NC_SCENE | NA_EDITED, 0);
- WM_jobs_callbacks(
- wm_job, EEVEE_lightbake_job, NULL, EEVEE_lightbake_update, EEVEE_lightbake_update);
-
- G.is_break = false;
-
- return wm_job;
-}
-
-void *EEVEE_lightbake_job_data_alloc(struct Main *bmain,
- struct ViewLayer *view_layer,
- struct Scene *scene,
- bool run_as_job,
- int frame)
-{
- BLI_assert(BLI_thread_is_main());
-
- EEVEE_LightBake *lbake = MEM_callocN(sizeof(EEVEE_LightBake), "EEVEE_LightBake");
-
- lbake->depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER);
- lbake->scene = scene;
- lbake->bmain = bmain;
- lbake->view_layer_input = view_layer;
- lbake->own_resources = true;
- lbake->own_light_cache = false;
- lbake->mutex = BLI_mutex_alloc();
- lbake->frame = frame;
-
- if (run_as_job && !GPU_use_main_context_workaround()) {
- lbake->gl_context = WM_opengl_context_create();
- wm_window_reset_drawable();
- }
-
- return lbake;
-}
-
-void EEVEE_lightbake_job_data_free(void *custom_data)
-{
- EEVEE_LightBake *lbake = (EEVEE_LightBake *)custom_data;
-
- /* TODO: reuse depsgraph. */
- /* if (lbake->own_resources) { */
- DEG_graph_free(lbake->depsgraph);
- /* } */
-
- MEM_SAFE_FREE(lbake->cube_prb);
- MEM_SAFE_FREE(lbake->grid_prb);
-
- BLI_mutex_free(lbake->mutex);
-
- MEM_freeN(lbake);
-}
-
-static void eevee_lightbake_delete_resources(EEVEE_LightBake *lbake)
-{
- if (!lbake->resource_only) {
- BLI_mutex_lock(lbake->mutex);
- }
-
- if (lbake->gl_context) {
- DRW_opengl_render_context_enable(lbake->gl_context);
- DRW_gpu_render_context_enable(lbake->gpu_context);
- }
- else if (!lbake->resource_only) {
- DRW_opengl_context_enable();
- }
-
- /* XXX Free the resources contained in the viewlayer data
- * to be able to free the context before deleting the depsgraph. */
- if (lbake->sldata) {
- EEVEE_view_layer_data_free(lbake->sldata);
- }
-
- DRW_TEXTURE_FREE_SAFE(lbake->rt_depth);
- DRW_TEXTURE_FREE_SAFE(lbake->rt_color);
- DRW_TEXTURE_FREE_SAFE(lbake->grid_prev);
- GPU_FRAMEBUFFER_FREE_SAFE(lbake->store_fb);
- for (int i = 0; i < 6; i++) {
- GPU_FRAMEBUFFER_FREE_SAFE(lbake->rt_fb[i]);
- }
-
- if (lbake->gpu_context) {
- DRW_gpu_render_context_disable(lbake->gpu_context);
- DRW_gpu_render_context_enable(lbake->gpu_context);
- GPU_context_discard(lbake->gpu_context);
- }
-
- if (lbake->gl_context && lbake->own_resources) {
- /* Delete the baking context. */
- DRW_opengl_render_context_disable(lbake->gl_context);
- WM_opengl_context_dispose(lbake->gl_context);
- lbake->gpu_context = NULL;
- lbake->gl_context = NULL;
- }
- else if (lbake->gl_context) {
- DRW_opengl_render_context_disable(lbake->gl_context);
- }
- else if (!lbake->resource_only) {
- DRW_opengl_context_disable();
- }
-
- if (!lbake->resource_only) {
- BLI_mutex_unlock(lbake->mutex);
- }
-}
-
-/* Cache as in draw cache not light cache. */
-static void eevee_lightbake_cache_create(EEVEE_Data *vedata, EEVEE_LightBake *lbake)
-{
- EEVEE_TextureList *txl = vedata->txl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_FramebufferList *fbl = vedata->fbl;
- EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
- Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph);
- lbake->sldata = sldata;
-
- /* Disable all effects BUT high bit-depth shadows. */
- scene_eval->eevee.flag &= SCE_EEVEE_SHADOW_HIGH_BITDEPTH;
- scene_eval->eevee.taa_samples = 1;
- scene_eval->eevee.gi_irradiance_smoothing = 0.0f;
-
- stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__);
- stl->g_data->background_alpha = 1.0f;
- stl->g_data->render_timesteps = 1;
-
- /* XXX TODO: remove this. This is in order to make the init functions work. */
- if (DRW_view_default_get() == NULL) {
- float winmat[4][4], viewmat[4][4];
- unit_m4(viewmat);
- unit_m4(winmat);
- negate_v3(winmat[2]);
- DRWView *view = DRW_view_create(viewmat, winmat, NULL, NULL, NULL);
- DRW_view_default_set(view);
- DRW_view_set_active(view);
- }
-
- /* HACK: set txl->color but unset it before Draw Manager frees it. */
- txl->color = lbake->rt_color;
- const int viewport_size[2] = {
- GPU_texture_width(txl->color),
- GPU_texture_height(txl->color),
- };
- DRW_render_viewport_size_set(viewport_size);
-
- EEVEE_effects_init(sldata, vedata, NULL, true);
- EEVEE_materials_init(sldata, vedata, stl, fbl);
- EEVEE_shadows_init(sldata);
- EEVEE_lightprobes_init(sldata, vedata);
-
- EEVEE_effects_cache_init(sldata, vedata);
- EEVEE_materials_cache_init(sldata, vedata);
- EEVEE_subsurface_cache_init(sldata, vedata);
- EEVEE_volumes_cache_init(sldata, vedata);
- EEVEE_lights_cache_init(sldata, vedata);
- EEVEE_lightprobes_cache_init(sldata, vedata);
-
- EEVEE_lightbake_cache_init(sldata, vedata, lbake->rt_color, lbake->rt_depth);
-
- if (lbake->probe) {
- EEVEE_LightProbesInfo *pinfo = sldata->probes;
- LightProbe *prb = *lbake->probe;
- pinfo->vis_data.collection = prb->visibility_grp;
- pinfo->vis_data.invert = prb->flag & LIGHTPROBE_FLAG_INVERT_GROUP;
- pinfo->vis_data.cached = false;
- }
- DRW_render_object_iter(vedata, NULL, lbake->depsgraph, EEVEE_render_cache);
-
- 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_shadows_update(sldata, vedata);
-
- /* Disable volumetrics when baking. */
- stl->effects->enabled_effects &= ~EFFECT_VOLUMETRIC;
-
- EEVEE_subsurface_draw_init(sldata, vedata);
- EEVEE_effects_draw_init(sldata, vedata);
- EEVEE_volumes_draw_init(sldata, vedata);
-
- txl->color = NULL;
-
- DRW_render_instance_buffer_finish();
- DRW_hair_update();
-}
-
-static void eevee_lightbake_copy_irradiance(EEVEE_LightBake *lbake, LightCache *lcache)
-{
- DRW_TEXTURE_FREE_SAFE(lbake->grid_prev);
-
- /* Copy texture by reading back and re-uploading it. */
- float *tex = GPU_texture_read(lcache->grid_tx.tex, GPU_DATA_FLOAT, 0);
- lbake->grid_prev = DRW_texture_create_2d_array(lbake->irr_size[0],
- lbake->irr_size[1],
- lbake->irr_size[2],
- IRRADIANCE_FORMAT,
- DRW_TEX_FILTER,
- tex);
-
- MEM_freeN(tex);
-}
-
-static void eevee_lightbake_render_world_sample(void *ved, void *user_data)
-{
- EEVEE_Data *vedata = (EEVEE_Data *)ved;
- EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
- EEVEE_LightBake *lbake = (EEVEE_LightBake *)user_data;
- Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph);
- LightCache *lcache = scene_eval->eevee.light_cache_data;
- float clamp = scene_eval->eevee.gi_glossy_clamp;
- float filter_quality = scene_eval->eevee.gi_filter_quality;
-
- /* TODO: do this once for the whole bake when we have independent DRWManagers. */
- eevee_lightbake_cache_create(vedata, lbake);
-
- sldata->common_data.ray_type = EEVEE_RAY_GLOSSY;
- sldata->common_data.ray_depth = 1;
- GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
- EEVEE_lightbake_render_world(sldata, vedata, lbake->rt_fb);
- EEVEE_lightbake_filter_glossy(sldata,
- vedata,
- lbake->rt_color,
- lbake->store_fb,
- 0,
- 1.0f,
- lcache->mips_len,
- filter_quality,
- clamp);
-
- sldata->common_data.ray_type = EEVEE_RAY_DIFFUSE;
- sldata->common_data.ray_depth = 1;
- GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
- EEVEE_lightbake_render_world(sldata, vedata, lbake->rt_fb);
- EEVEE_lightbake_filter_diffuse(sldata, vedata, lbake->rt_color, lbake->store_fb, 0, 1.0f);
-
- if (lcache->flag & LIGHTCACHE_UPDATE_GRID) {
- /* Clear the cache to avoid white values in the grid. */
- GPU_framebuffer_texture_attach(lbake->store_fb, lbake->grid_prev, 0, 0);
- GPU_framebuffer_bind(lbake->store_fb);
- /* Clear to 1.0f for visibility. */
- GPU_framebuffer_clear_color(lbake->store_fb, ((float[4]){1.0f, 1.0f, 1.0f, 1.0f}));
- DRW_draw_pass(vedata->psl->probe_grid_fill);
-
- SWAP(GPUTexture *, lbake->grid_prev, lcache->grid_tx.tex);
-
- /* Make a copy for later. */
- eevee_lightbake_copy_irradiance(lbake, lcache);
- }
-
- lcache->cube_len = 1;
- lcache->grid_len = lbake->grid_len;
-
- lcache->flag |= LIGHTCACHE_CUBE_READY | LIGHTCACHE_GRID_READY;
- lcache->flag &= ~LIGHTCACHE_UPDATE_WORLD;
-}
-
-static void cell_id_to_grid_loc(EEVEE_LightGrid *egrid, int cell_idx, int r_local_cell[3])
-{
- /* Keep in sync with lightprobe_grid_display_vert */
- r_local_cell[2] = cell_idx % egrid->resolution[2];
- r_local_cell[1] = (cell_idx / egrid->resolution[2]) % egrid->resolution[1];
- r_local_cell[0] = cell_idx / (egrid->resolution[2] * egrid->resolution[1]);
-}
-
-static void compute_cell_id(EEVEE_LightGrid *egrid,
- LightProbe *probe,
- int cell_idx,
- int *r_final_idx,
- int r_local_cell[3],
- int *r_stride)
-{
- const int cell_count = probe->grid_resolution_x * probe->grid_resolution_y *
- probe->grid_resolution_z;
-
- /* Add one for level 0 */
- int max_lvl = (int)floorf(log2f(
- (float)MAX3(probe->grid_resolution_x, probe->grid_resolution_y, probe->grid_resolution_z)));
-
- int visited_cells = 0;
- *r_stride = 0;
- *r_final_idx = 0;
- r_local_cell[0] = r_local_cell[1] = r_local_cell[2] = 0;
- for (int lvl = max_lvl; lvl >= 0; lvl--) {
- *r_stride = 1 << lvl;
- int prev_stride = *r_stride << 1;
- for (int i = 0; i < cell_count; i++) {
- *r_final_idx = i;
- cell_id_to_grid_loc(egrid, *r_final_idx, r_local_cell);
- if (((r_local_cell[0] % *r_stride) == 0) && ((r_local_cell[1] % *r_stride) == 0) &&
- ((r_local_cell[2] % *r_stride) == 0)) {
- if (!(((r_local_cell[0] % prev_stride) == 0) && ((r_local_cell[1] % prev_stride) == 0) &&
- ((r_local_cell[2] % prev_stride) == 0)) ||
- ((i == 0) && (lvl == max_lvl))) {
- if (visited_cells == cell_idx) {
- return;
- }
-
- visited_cells++;
- }
- }
- }
- }
-
- BLI_assert(0);
-}
-
-static void grid_loc_to_world_loc(EEVEE_LightGrid *egrid, const int local_cell[3], float r_pos[3])
-{
- copy_v3_v3(r_pos, egrid->corner);
- madd_v3_v3fl(r_pos, egrid->increment_x, local_cell[0]);
- madd_v3_v3fl(r_pos, egrid->increment_y, local_cell[1]);
- madd_v3_v3fl(r_pos, egrid->increment_z, local_cell[2]);
-}
-
-static void eevee_lightbake_render_grid_sample(void *ved, void *user_data)
-{
- EEVEE_Data *vedata = (EEVEE_Data *)ved;
- EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
- EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
- EEVEE_LightBake *lbake = (EEVEE_LightBake *)user_data;
- EEVEE_LightGrid *egrid = lbake->grid;
- LightProbe *prb = *lbake->probe;
- Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph);
- LightCache *lcache = scene_eval->eevee.light_cache_data;
- int grid_loc[3], sample_id, sample_offset, stride;
- float pos[3];
- const bool is_last_bounce_sample = ((egrid->offset + lbake->grid_sample) ==
- (lbake->total_irr_samples - 1));
-
- /* No bias for rendering the probe. */
- egrid->level_bias = 1.0f;
-
- /* Use the previous bounce for rendering this bounce. */
- SWAP(GPUTexture *, lbake->grid_prev, lcache->grid_tx.tex);
-
- /* TODO: do this once for the whole bake when we have independent DRWManagers.
- * Warning: Some of the things above require this. */
- eevee_lightbake_cache_create(vedata, lbake);
-
- /* Compute sample position */
- compute_cell_id(egrid, prb, lbake->grid_sample, &sample_id, grid_loc, &stride);
- sample_offset = egrid->offset + sample_id;
-
- grid_loc_to_world_loc(egrid, grid_loc, pos);
-
- /* Disable specular lighting when rendering probes to avoid feedback loops (looks bad). */
- common_data->spec_toggle = false;
- common_data->sss_toggle = false;
- common_data->prb_num_planar = 0;
- common_data->prb_num_render_cube = 0;
- common_data->ray_type = EEVEE_RAY_DIFFUSE;
- common_data->ray_depth = lbake->bounce_curr + 1;
- if (lbake->bounce_curr == 0) {
- common_data->prb_num_render_grid = 0;
- }
- GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
-
- EEVEE_lightbake_render_scene(sldata, vedata, lbake->rt_fb, pos, prb->clipsta, prb->clipend);
-
- /* Restore before filtering. */
- SWAP(GPUTexture *, lbake->grid_prev, lcache->grid_tx.tex);
-
- EEVEE_lightbake_filter_diffuse(
- sldata, vedata, lbake->rt_color, lbake->store_fb, sample_offset, prb->intensity);
-
- if (lbake->bounce_curr == 0) {
- /* We only need to filter the visibility for the first bounce. */
- EEVEE_lightbake_filter_visibility(sldata,
- vedata,
- lbake->rt_depth,
- lbake->store_fb,
- sample_offset,
- prb->clipsta,
- prb->clipend,
- egrid->visibility_range,
- prb->vis_blur,
- lbake->vis_res);
- }
-
- /* Update level for progressive update. */
- if (is_last_bounce_sample) {
- egrid->level_bias = 1.0f;
- }
- else if (lbake->bounce_curr == 0) {
- egrid->level_bias = (float)(stride << 1);
- }
-
- /* Only run this for the last sample of a bounce. */
- if (is_last_bounce_sample) {
- eevee_lightbake_copy_irradiance(lbake, lcache);
- }
-
- /* If it is the last sample grid sample (and last bounce). */
- if ((lbake->bounce_curr == lbake->bounce_len - 1) && (lbake->grid_curr == lbake->grid_len - 1) &&
- (lbake->grid_sample == lbake->grid_sample_len - 1)) {
- lcache->flag &= ~LIGHTCACHE_UPDATE_GRID;
- }
-}
-
-static void eevee_lightbake_render_probe_sample(void *ved, void *user_data)
-{
- EEVEE_Data *vedata = (EEVEE_Data *)ved;
- EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
- EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
- EEVEE_LightBake *lbake = (EEVEE_LightBake *)user_data;
- Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph);
- LightCache *lcache = scene_eval->eevee.light_cache_data;
- EEVEE_LightProbe *eprobe = lbake->cube;
- LightProbe *prb = *lbake->probe;
- float clamp = scene_eval->eevee.gi_glossy_clamp;
- float filter_quality = scene_eval->eevee.gi_filter_quality;
-
- /* TODO: do this once for the whole bake when we have independent DRWManagers. */
- eevee_lightbake_cache_create(vedata, lbake);
-
- /* Disable specular lighting when rendering probes to avoid feedback loops (looks bad). */
- common_data->spec_toggle = false;
- common_data->sss_toggle = false;
- common_data->prb_num_planar = 0;
- common_data->prb_num_render_cube = 0;
- common_data->ray_type = EEVEE_RAY_GLOSSY;
- common_data->ray_depth = 1;
- GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
-
- EEVEE_lightbake_render_scene(
- sldata, vedata, lbake->rt_fb, eprobe->position, prb->clipsta, prb->clipend);
- EEVEE_lightbake_filter_glossy(sldata,
- vedata,
- lbake->rt_color,
- lbake->store_fb,
- lbake->cube_offset,
- prb->intensity,
- lcache->mips_len,
- filter_quality,
- clamp);
-
- lcache->cube_len += 1;
-
- /* If it's the last probe. */
- if (lbake->cube_offset == lbake->cube_len - 1) {
- lcache->flag &= ~LIGHTCACHE_UPDATE_CUBE;
- }
-}
-
-static float eevee_lightbake_grid_influence_volume(EEVEE_LightGrid *grid)
-{
- return mat4_to_scale(grid->mat);
-}
-
-static float eevee_lightbake_cube_influence_volume(EEVEE_LightProbe *eprb)
-{
- return mat4_to_scale(eprb->attenuationmat);
-}
-
-static bool eevee_lightbake_grid_comp(EEVEE_LightGrid *grid_a, EEVEE_LightGrid *grid_b)
-{
- float vol_a = eevee_lightbake_grid_influence_volume(grid_a);
- float vol_b = eevee_lightbake_grid_influence_volume(grid_b);
- return (vol_a < vol_b);
-}
-
-static bool eevee_lightbake_cube_comp(EEVEE_LightProbe *prb_a, EEVEE_LightProbe *prb_b)
-{
- float vol_a = eevee_lightbake_cube_influence_volume(prb_a);
- float vol_b = eevee_lightbake_cube_influence_volume(prb_b);
- return (vol_a < vol_b);
-}
-
-#define SORT_PROBE(elems_type, prbs, elems, elems_len, comp_fn) \
- { \
- bool sorted = false; \
- while (!sorted) { \
- sorted = true; \
- for (int i = 0; i < (elems_len)-1; i++) { \
- if ((comp_fn)((elems) + i, (elems) + i + 1)) { \
- SWAP(elems_type, (elems)[i], (elems)[i + 1]); \
- SWAP(LightProbe *, (prbs)[i], (prbs)[i + 1]); \
- sorted = false; \
- } \
- } \
- } \
- } \
- ((void)0)
-
-static void eevee_lightbake_gather_probes(EEVEE_LightBake *lbake)
-{
- Depsgraph *depsgraph = lbake->depsgraph;
- Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
- LightCache *lcache = scene_eval->eevee.light_cache_data;
-
- /* At least one for the world */
- int grid_len = 1;
- int cube_len = 1;
- int total_irr_samples = 1;
-
- /* Convert all lightprobes to tight UBO data from all lightprobes in the scene.
- * This allows a large number of probe to be precomputed (even dupli ones). */
- DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob) {
- const int ob_visibility = BKE_object_visibility(ob, DAG_EVAL_RENDER);
- if ((ob_visibility & OB_VISIBLE_SELF) == 0) {
- continue;
- }
-
- if (ob->type == OB_LIGHTPROBE) {
- LightProbe *prb = (LightProbe *)ob->data;
-
- if (prb->type == LIGHTPROBE_TYPE_GRID) {
- lbake->grid_prb[grid_len] = prb;
- EEVEE_LightGrid *egrid = &lcache->grid_data[grid_len++];
- EEVEE_lightprobes_grid_data_from_object(ob, egrid, &total_irr_samples);
- }
- else if (prb->type == LIGHTPROBE_TYPE_CUBE && cube_len < EEVEE_PROBE_MAX) {
- lbake->cube_prb[cube_len] = prb;
- EEVEE_LightProbe *eprobe = &lcache->cube_data[cube_len++];
- EEVEE_lightprobes_cube_data_from_object(ob, eprobe);
- }
- }
- }
- DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END;
-
- SORT_PROBE(EEVEE_LightGrid,
- lbake->grid_prb + 1,
- lcache->grid_data + 1,
- lbake->grid_len - 1,
- eevee_lightbake_grid_comp);
- SORT_PROBE(EEVEE_LightProbe,
- lbake->cube_prb + 1,
- lcache->cube_data + 1,
- lbake->cube_len - 1,
- eevee_lightbake_cube_comp);
-
- lbake->total = lbake->total_irr_samples * lbake->bounce_len + lbake->cube_len;
- lbake->done = 0;
-}
-
-void EEVEE_lightbake_update(void *custom_data)
-{
- EEVEE_LightBake *lbake = (EEVEE_LightBake *)custom_data;
- Scene *scene_orig = lbake->scene;
-
- /* If a new light-cache was created, free the old one and reference the new. */
- if (lbake->lcache && scene_orig->eevee.light_cache_data != lbake->lcache) {
- if (scene_orig->eevee.light_cache_data != NULL) {
- EEVEE_lightcache_free(scene_orig->eevee.light_cache_data);
- }
- scene_orig->eevee.light_cache_data = lbake->lcache;
- lbake->own_light_cache = false;
- }
-
- EEVEE_lightcache_info_update(&lbake->scene->eevee);
-
- DEG_id_tag_update(&scene_orig->id, ID_RECALC_COPY_ON_WRITE);
-}
-
-static bool lightbake_do_sample(EEVEE_LightBake *lbake,
- void (*render_callback)(void *ved, void *user_data))
-{
- if (G.is_break == true || *lbake->stop) {
- return false;
- }
-
- Depsgraph *depsgraph = lbake->depsgraph;
-
- /* TODO: make DRW manager instantiable (and only lock on drawing) */
- eevee_lightbake_context_enable(lbake);
- DRW_custom_pipeline(&draw_engine_eevee_type, depsgraph, render_callback, lbake);
- lbake->done += 1;
- *lbake->progress = lbake->done / (float)lbake->total;
- *lbake->do_update = 1;
- eevee_lightbake_context_disable(lbake);
-
- return true;
-}
-
-void EEVEE_lightbake_job(void *custom_data, short *stop, short *do_update, float *progress)
-{
- EEVEE_LightBake *lbake = (EEVEE_LightBake *)custom_data;
- Depsgraph *depsgraph = lbake->depsgraph;
-
- DEG_graph_relations_update(depsgraph);
- DEG_evaluate_on_framechange(depsgraph, lbake->frame);
-
- lbake->view_layer = DEG_get_evaluated_view_layer(depsgraph);
- lbake->stop = stop;
- lbake->do_update = do_update;
- lbake->progress = progress;
-
- if (G.background) {
- /* Make sure to init GL capabilities before counting probes. */
- eevee_lightbake_context_enable(lbake);
- eevee_lightbake_context_disable(lbake);
- }
-
- /* Count lightprobes */
- eevee_lightbake_count_probes(lbake);
-
- /* We need to create the FBOs in the right context.
- * We cannot do it in the main thread. */
- eevee_lightbake_context_enable(lbake);
- eevee_lightbake_create_resources(lbake);
-
- /* Resource allocation can fail. Early exit in this case. */
- if (lbake->lcache->flag & LIGHTCACHE_INVALID) {
- *lbake->stop = 1;
- *lbake->do_update = 1;
- lbake->lcache->flag &= ~LIGHTCACHE_BAKING;
- eevee_lightbake_context_disable(lbake);
- eevee_lightbake_delete_resources(lbake);
- return;
- }
-
- eevee_lightbake_create_render_target(lbake, lbake->rt_res);
- eevee_lightbake_context_disable(lbake);
-
- /* Gather all probes data */
- eevee_lightbake_gather_probes(lbake);
-
- LightCache *lcache = lbake->lcache;
-
- /* HACK: Sleep to delay the first rendering operation
- * that causes a small freeze (caused by VBO generation)
- * because this step is locking at this moment. */
- /* TODO: remove this. */
- if (lbake->delay) {
- PIL_sleep_ms(lbake->delay);
- }
-
- /* Render world irradiance and reflection first */
- if (lcache->flag & LIGHTCACHE_UPDATE_WORLD) {
- lbake->probe = NULL;
- lightbake_do_sample(lbake, eevee_lightbake_render_world_sample);
- }
-
- /* Render irradiance grids */
- if (lcache->flag & LIGHTCACHE_UPDATE_GRID) {
- for (lbake->bounce_curr = 0; lbake->bounce_curr < lbake->bounce_len; lbake->bounce_curr++) {
- /* Bypass world, start at 1. */
- lbake->probe = lbake->grid_prb + 1;
- lbake->grid = lcache->grid_data + 1;
- for (lbake->grid_curr = 1; lbake->grid_curr < lbake->grid_len;
- lbake->grid_curr++, lbake->probe++, lbake->grid++) {
- LightProbe *prb = *lbake->probe;
- lbake->grid_sample_len = prb->grid_resolution_x * prb->grid_resolution_y *
- prb->grid_resolution_z;
- for (lbake->grid_sample = 0; lbake->grid_sample < lbake->grid_sample_len;
- ++lbake->grid_sample) {
- lightbake_do_sample(lbake, eevee_lightbake_render_grid_sample);
- }
- }
- }
- }
-
- /* Render reflections */
- if (lcache->flag & LIGHTCACHE_UPDATE_CUBE) {
- /* Bypass world, start at 1. */
- lbake->probe = lbake->cube_prb + 1;
- lbake->cube = lcache->cube_data + 1;
- for (lbake->cube_offset = 1; lbake->cube_offset < lbake->cube_len;
- lbake->cube_offset++, lbake->probe++, lbake->cube++) {
- lightbake_do_sample(lbake, eevee_lightbake_render_probe_sample);
- }
- }
-
- /* Read the resulting lighting data to save it to file/disk. */
- eevee_lightbake_context_enable(lbake);
- eevee_lightbake_readback_irradiance(lcache);
- eevee_lightbake_readback_reflections(lcache);
- eevee_lightbake_context_disable(lbake);
-
- lcache->flag |= LIGHTCACHE_BAKED;
- lcache->flag &= ~LIGHTCACHE_BAKING;
-
- /* Assume that if lbake->gl_context is NULL
- * we are not running in this in a job, so update
- * the scene light-cache pointer before deleting it. */
- if (lbake->gl_context == NULL) {
- BLI_assert(BLI_thread_is_main());
- EEVEE_lightbake_update(lbake);
- }
-
- eevee_lightbake_delete_resources(lbake);
-
- /* Free GPU smoke textures and the smoke domain list correctly: See also T73921. */
- EEVEE_volumes_free_smoke_textures();
-}
-
-void EEVEE_lightbake_update_world_quick(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- const Scene *scene)
-{
- LightCache *lcache = vedata->stl->g_data->light_cache;
- float clamp = scene->eevee.gi_glossy_clamp;
- float filter_quality = scene->eevee.gi_filter_quality;
-
- EEVEE_LightBake lbake = {
- .resource_only = true,
- };
-
- /* Create resources. */
- eevee_lightbake_create_render_target(&lbake, scene->eevee.gi_cubemap_resolution);
-
- EEVEE_lightbake_cache_init(sldata, vedata, lbake.rt_color, lbake.rt_depth);
-
- sldata->common_data.ray_type = EEVEE_RAY_GLOSSY;
- sldata->common_data.ray_depth = 1;
- GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
- EEVEE_lightbake_render_world(sldata, vedata, lbake.rt_fb);
- EEVEE_lightbake_filter_glossy(sldata,
- vedata,
- lbake.rt_color,
- lbake.store_fb,
- 0,
- 1.0f,
- lcache->mips_len,
- filter_quality,
- clamp);
-
- sldata->common_data.ray_type = EEVEE_RAY_DIFFUSE;
- sldata->common_data.ray_depth = 1;
- GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
- EEVEE_lightbake_render_world(sldata, vedata, lbake.rt_fb);
- EEVEE_lightbake_filter_diffuse(sldata, vedata, lbake.rt_color, lbake.store_fb, 0, 1.0f);
-
- /* Don't hide grids if they are already rendered. */
- lcache->grid_len = max_ii(1, lcache->grid_len);
- lcache->cube_len = 1;
-
- lcache->flag |= LIGHTCACHE_CUBE_READY | LIGHTCACHE_GRID_READY;
- lcache->flag &= ~LIGHTCACHE_UPDATE_WORLD;
-
- eevee_lightbake_delete_resources(&lbake);
-}
-
-/** \} */
diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.cc b/source/blender/draw/engines/eevee/eevee_lightcache.cc
new file mode 100644
index 00000000000..8c6625e4515
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_lightcache.cc
@@ -0,0 +1,1198 @@
+/*
+ * 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 indirect lighting cache.
+ */
+
+#include "DRW_render.h"
+
+#include "BKE_global.h"
+
+#include "BLI_endian_switch.h"
+#include "BLI_span.hh"
+
+#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
+
+#include "BKE_object.h"
+
+#include "DNA_collection_types.h"
+#include "DNA_lightprobe_types.h"
+
+#include "PIL_time.h"
+
+#include "eevee_instance.hh"
+#include "eevee_lightcache.h"
+#include "eevee_private.h"
+
+#include "GPU_capabilities.h"
+#include "GPU_context.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "BLO_read_write.h"
+
+#include "wm_window.h"
+
+#include <mutex>
+
+extern "C" {
+/* TODO should be replace by a more elegant alternative. */
+extern void DRW_opengl_context_enable(void);
+extern void DRW_opengl_context_disable(void);
+
+extern void DRW_opengl_render_context_enable(void *re_gl_context);
+extern void DRW_opengl_render_context_disable(void *re_gl_context);
+extern void DRW_gpu_render_context_enable(void *re_gpu_context);
+extern void DRW_gpu_render_context_disable(void *re_gpu_context);
+
+extern DrawEngineType draw_engine_eevee_type;
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Light Cache
+ * \{ */
+
+namespace blender::eevee {
+
+LightCache::LightCache(const int cube_len_,
+ const int grid_len_,
+ const int cube_size,
+ const int vis_size,
+ const int irr_size[3])
+{
+ memset(this, 0, sizeof(*this));
+
+ version = LIGHTCACHE_STATIC_VERSION;
+ type = LIGHTCACHE_TYPE_STATIC;
+ mips_len = log2_floor_u(cube_size) - min_cube_lod_level;
+ vis_res = vis_size;
+ ref_res = cube_size;
+ cube_len = cube_len_;
+ grid_len = grid_len_;
+
+ cube_data = (LightProbeCache *)MEM_calloc_arrayN(
+ cube_len, sizeof(LightProbeCache), "LightProbeCache");
+ grid_data = (LightGridCache *)MEM_calloc_arrayN(
+ grid_len, sizeof(LightGridCache), "LightGridCache");
+ cube_mips = (LightCacheTexture *)MEM_calloc_arrayN(
+ mips_len, sizeof(LightCacheTexture), "LightCacheTexture");
+
+ grid_tx.tex_size[0] = irr_size[0];
+ grid_tx.tex_size[1] = irr_size[1];
+ grid_tx.tex_size[2] = irr_size[2];
+
+ cube_tx.tex_size[0] = ref_res;
+ cube_tx.tex_size[1] = ref_res;
+ cube_tx.tex_size[2] = cube_len * 6;
+
+ create_reflection_texture();
+ create_irradiance_texture();
+
+ if (flag & LIGHTCACHE_NOT_USABLE) {
+ /* We could not create the requested textures size. Stop baking and do not use the cache. */
+ flag = LIGHTCACHE_INVALID;
+ return;
+ }
+
+ flag = LIGHTCACHE_UPDATE_WORLD | LIGHTCACHE_UPDATE_CUBE | LIGHTCACHE_UPDATE_GRID;
+
+ for (int mip = 0; mip < mips_len; mip++) {
+ GPU_texture_get_mipmap_size(cube_tx.tex, mip + 1, cube_mips[mip].tex_size);
+ }
+}
+
+LightCache::~LightCache()
+{
+ DRW_TEXTURE_FREE_SAFE(cube_tx.tex);
+ MEM_SAFE_FREE(cube_tx.data);
+ DRW_TEXTURE_FREE_SAFE(grid_tx.tex);
+ MEM_SAFE_FREE(grid_tx.data);
+
+ if (cube_mips) {
+ for (int i = 0; i < mips_len; i++) {
+ MEM_SAFE_FREE(cube_mips[i].data);
+ }
+ MEM_SAFE_FREE(cube_mips);
+ }
+
+ MEM_SAFE_FREE(cube_data);
+ MEM_SAFE_FREE(grid_data);
+}
+
+int LightCache::irradiance_cells_per_row_get(void) const
+{
+ /* Ambient cube is 3x2px. */
+ return grid_tx.tex_size[0] / 3;
+}
+
+/**
+ * Returns dimensions of the irradiance cache texture.
+ **/
+void LightCache::irradiance_cache_size_get(int visibility_size, int total_samples, int r_size[3])
+{
+ /* Compute how many irradiance samples we can store per visibility sample. */
+ int irr_per_vis = (visibility_size / irradiance_sample_size_x) *
+ (visibility_size / irradiance_sample_size_y);
+
+ /* The irradiance itself take one layer, hence the +1 */
+ int layer_ct = min_ii(irr_per_vis + 1, irradiance_max_pool_layer);
+
+ int texel_ct = (int)ceilf((float)total_samples / (float)(layer_ct - 1));
+ r_size[0] = visibility_size *
+ max_ii(1, min_ii(texel_ct, (irradiance_max_pool_size / visibility_size)));
+ r_size[1] = visibility_size *
+ max_ii(1, (texel_ct / (irradiance_max_pool_size / visibility_size)));
+ r_size[2] = layer_ct;
+}
+
+bool LightCache::validate(const int cube_len,
+ const int cube_res,
+ const int grid_len,
+ const int irr_size[3]) const
+{
+ if (!version_check()) {
+ return false;
+ }
+ if ((flag & (LIGHTCACHE_INVALID | LIGHTCACHE_NOT_USABLE)) != 0) {
+ return false;
+ }
+ /* See if we need the same amount of texture space. */
+ if ((ivec3(irr_size) == ivec3(grid_tx.tex_size)) && (grid_len == this->grid_len)) {
+ int mip_len = log2_floor_u(cube_res) - min_cube_lod_level;
+ if ((cube_res == cube_tx.tex_size[0]) && (cube_len == cube_tx.tex_size[2] / 6) &&
+ (cube_len == this->cube_len) && (mip_len == this->mips_len)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Returns true if the lightcache can be loaded correctly with this version of eevee.
+ **/
+bool LightCache::version_check() const
+{
+ switch (type) {
+ case LIGHTCACHE_TYPE_STATIC:
+ return version == LIGHTCACHE_STATIC_VERSION;
+ default:
+ return false;
+ }
+}
+
+/**
+ * Creates empty texture for reflection data.
+ * Returns false on failure and set lightcache as unusable.
+ **/
+bool LightCache::create_reflection_texture(void)
+{
+ /* Try to create a cubemap array. */
+ cube_tx.tex = GPU_texture_create_cube_array("lightcache_cubemaps",
+ cube_tx.tex_size[0],
+ cube_tx.tex_size[2] / 6,
+ mips_len + 1,
+ reflection_format,
+ nullptr);
+
+ if (cube_tx.tex == nullptr) {
+ /* Try fallback to 2D array. */
+ cube_tx.tex = GPU_texture_create_2d_array("lightcache_cubemaps_fallback",
+ UNPACK3(cube_tx.tex_size),
+ mips_len + 1,
+ reflection_format,
+ nullptr);
+ }
+
+ if (cube_tx.tex != nullptr) {
+ GPU_texture_mipmap_mode(cube_tx.tex, true, true);
+ /* TODO(fclem) This fixes incomplete texture. Fix the GPU module instead. */
+ GPU_texture_generate_mipmap(cube_tx.tex);
+ }
+ else {
+ flag |= LIGHTCACHE_NOT_USABLE;
+ }
+ return cube_tx.tex != nullptr;
+}
+
+/**
+ * Creates empty texture for irradiance data.
+ * Returns false on failure and set lightcache as unusable.
+ **/
+bool LightCache::create_irradiance_texture(void)
+{
+ grid_tx.tex = GPU_texture_create_2d_array(
+ "lightcache_irradiance", UNPACK3(grid_tx.tex_size), 1, irradiance_format, nullptr);
+ if (grid_tx.tex != nullptr) {
+ GPU_texture_filter_mode(grid_tx.tex, true);
+ }
+ else {
+ flag |= LIGHTCACHE_NOT_USABLE;
+ }
+ return grid_tx.tex != nullptr;
+}
+
+/**
+ * Loads a static lightcache data into GPU memory.
+ **/
+bool LightCache::load_static(void)
+{
+ /* True during baking. */
+ if (grid_len == 0 || cube_len == 0) {
+ return false;
+ }
+ /* We use fallback if a texture is not setup and there is no data to restore it. */
+ if ((!grid_tx.tex && !grid_tx.data) || !grid_data || (!cube_tx.tex && !cube_tx.data) ||
+ !cube_data) {
+ return false;
+ }
+ /* If cache is too big for this GPU. */
+ if (cube_tx.tex_size[2] > GPU_max_texture_layers()) {
+ return false;
+ }
+
+ if (grid_tx.tex == nullptr) {
+ if (create_irradiance_texture()) {
+ GPU_texture_update(grid_tx.tex, GPU_DATA_UBYTE, grid_tx.data);
+ }
+ /* TODO(fclem) Move to do_version. */
+ for (LightGridCache &grid : MutableSpan<LightGridCache>(grid_data, grid_len)) {
+ grid.is_ready = 1;
+ }
+ }
+
+ if (cube_tx.tex == nullptr) {
+ if (create_reflection_texture()) {
+ for (int mip = 0; mip <= mips_len; mip++) {
+ const void *data = (mip == 0) ? cube_tx.data : cube_mips[mip - 1].data;
+ GPU_texture_update_mipmap(cube_tx.tex, mip, GPU_DATA_10_11_11_REV, data);
+ }
+ }
+ /* TODO(fclem) Move to do_version. */
+ for (LightProbeCache &cube : MutableSpan<LightProbeCache>(cube_data, cube_len)) {
+ cube.is_ready = 1;
+ }
+ }
+ return true;
+}
+
+bool LightCache::load(void)
+{
+ if (!version_check()) {
+ return false;
+ }
+ switch (type) {
+ case LIGHTCACHE_TYPE_STATIC:
+ return load_static();
+ default:
+ return false;
+ }
+}
+
+void LightCache::readback_irradiance(void)
+{
+ MEM_SAFE_FREE(grid_tx.data);
+ grid_tx.data = (char *)GPU_texture_read(grid_tx.tex, GPU_DATA_UBYTE, 0);
+ grid_tx.data_type = LIGHTCACHETEX_BYTE;
+ grid_tx.components = 4;
+}
+
+void LightCache::readback_reflections(void)
+{
+ MEM_SAFE_FREE(cube_tx.data);
+ cube_tx.data = (char *)GPU_texture_read(cube_tx.tex, GPU_DATA_10_11_11_REV, 0);
+ cube_tx.data_type = LIGHTCACHETEX_UINT;
+ cube_tx.components = 1;
+
+ for (int mip = 0; mip < mips_len; mip++) {
+ LightCacheTexture &cube_mip = cube_mips[mip];
+ MEM_SAFE_FREE(cube_mip.data);
+ GPU_texture_get_mipmap_size(cube_tx.tex, mip + 1, cube_mip.tex_size);
+
+ cube_mip.data = (char *)GPU_texture_read(cube_tx.tex, GPU_DATA_10_11_11_REV, mip + 1);
+ cube_mip.data_type = LIGHTCACHETEX_UINT;
+ cube_mip.components = 1;
+ }
+}
+
+/* Return memory footprint in bytes. */
+uint LightCache::memsize_get(void) const
+{
+ uint size = 0;
+ if (grid_tx.data) {
+ size += MEM_allocN_len(grid_tx.data);
+ }
+ if (cube_tx.data) {
+ size += MEM_allocN_len(cube_tx.data);
+ for (int mip = 0; mip < mips_len; mip++) {
+ size += MEM_allocN_len(cube_mips[mip].data);
+ }
+ }
+ return size;
+}
+
+bool LightCache::can_be_saved(void) const
+{
+ if (grid_tx.data) {
+ if (MEM_allocN_len(grid_tx.data) >= INT_MAX) {
+ return false;
+ }
+ }
+ if (cube_tx.data) {
+ if (MEM_allocN_len(cube_tx.data) >= INT_MAX) {
+ return false;
+ }
+ }
+ return true;
+}
+
+int64_t LightCache::irradiance_sample_count(void) const
+{
+ int64_t total_irr_samples = 0;
+ for (const LightGridCache &grid : Span(&grid_data[1], grid_len - 1)) {
+ total_irr_samples += grid.resolution[0] * grid.resolution[1] * grid.resolution[2];
+ }
+ return total_irr_samples;
+}
+
+void LightCache::update_info(SceneEEVEE *eevee)
+{
+ LightCache *lcache = reinterpret_cast<LightCache *>(eevee->light_cache_data);
+
+ if (lcache == nullptr) {
+ BLI_strncpy(eevee->light_cache_info,
+ TIP_("No light cache in this scene"),
+ sizeof(eevee->light_cache_info));
+ return;
+ }
+
+ if (lcache->version_check() == false) {
+ BLI_strncpy(eevee->light_cache_info,
+ TIP_("Incompatible Light cache version, please bake again"),
+ sizeof(eevee->light_cache_info));
+ return;
+ }
+
+ if (lcache->cube_tx.tex_size[2] > GPU_max_texture_layers()) {
+ BLI_strncpy(eevee->light_cache_info,
+ TIP_("Error: Light cache is too big for the GPU to be loaded"),
+ sizeof(eevee->light_cache_info));
+ return;
+ }
+
+ if (lcache->flag & LIGHTCACHE_INVALID) {
+ BLI_strncpy(eevee->light_cache_info,
+ TIP_("Error: Light cache dimensions not supported by the GPU"),
+ sizeof(eevee->light_cache_info));
+ return;
+ }
+
+ if (lcache->flag & LIGHTCACHE_BAKING) {
+ BLI_strncpy(
+ eevee->light_cache_info, TIP_("Baking light cache"), sizeof(eevee->light_cache_info));
+ return;
+ }
+
+ if (lcache->can_be_saved() == false) {
+ BLI_strncpy(eevee->light_cache_info,
+ TIP_("Error: LightCache is too large and will not be saved to disk"),
+ sizeof(eevee->light_cache_info));
+ return;
+ }
+
+ char formatted_mem[15];
+ BLI_str_format_byte_unit(formatted_mem, lcache->memsize_get(), false);
+
+ BLI_snprintf(eevee->light_cache_info,
+ sizeof(eevee->light_cache_info),
+ TIP_("%d Ref. Cubemaps, %lld Irr. Samples (%s in memory)"),
+ lcache->cube_len - 1,
+ lcache->irradiance_sample_count(),
+ formatted_mem);
+}
+
+} // namespace blender::eevee
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Light Bake Job
+ * \{ */
+
+namespace blender::eevee {
+
+class LightBake {
+ private:
+ Depsgraph *depsgraph_;
+ ViewLayer *view_layer_ = nullptr;
+ ViewLayer *view_layer_input_;
+ LightCache *lcache_ = nullptr;
+ Scene *scene_;
+ struct Main *bmain_;
+ /** True if this object owns the gl_context_. */
+ bool own_resources_ = true;
+ /** If the light-cache was created for baking, it's first owned by the baker. */
+ bool own_light_cache_ = false;
+ /** Scene frame to bake. */
+ int frame_ = 0;
+ /** ms. delay the start of the baking to not slowdown interactions (TODO remove) */
+ int delay_ = 5;
+ /** If running in parallel (in a separate thread), use this context. */
+ void *gl_context_ = nullptr;
+ GPUContext *gpu_context_ = nullptr;
+
+ /** Instance used for baking. */
+ Instance *inst_ = nullptr;
+ /** Total for all grids. */
+ int irradiance_samples_count_;
+ /** Number of grid and cube to bake. Data is inside the lightcache. */
+ int grid_len_ = 0;
+ int cube_len_ = 0;
+
+ /* Copy of probes data for rendering. We could make a lighter copy if needed. */
+ Vector<LightProbe> cubes_probe_;
+ Vector<LightProbe> grids_probe_;
+
+ /* To compute progress. */
+ int64_t total_ = 0, done_ = 0;
+ /* Progress bar ratio to update. Only for async bake. */
+ float *progress_ = nullptr;
+ /* Signal to stop baking. Only for async bake. */
+ short *stop_ = nullptr;
+ /* Signal to update scene lightcache. Only for async bake. */
+ short *do_update_ = nullptr;
+
+ std::mutex mutex_;
+
+ struct GridRenderData {
+ LightBake &bake;
+ LightGridCache *grid;
+ int64_t sample_index;
+ int bounce;
+
+ GridRenderData(LightBake &bake_) : bake(bake_){};
+
+ void render(void)
+ {
+ SceneEEVEE &sce_eevee = DEG_get_evaluated_scene(bake.depsgraph_)->eevee;
+ LightProbe *probe = (grid->probe_index > -1) ? &bake.grids_probe_[grid->probe_index] :
+ nullptr;
+
+ /* Swap cache on first grid of each bounce. */
+ if (bounce > 0 && grid->offset == 0) {
+ bake.inst_->lightprobes.swap_irradiance_cache();
+ }
+
+ ivec3 cell_co = grid_cell_index_to_coordinate(sample_index, grid->resolution);
+ vec3 position = vec3(grid->corner) + vec3(grid->increment_x) * cell_co.x +
+ vec3(grid->increment_y) * cell_co.y + vec3(grid->increment_z) * cell_co.z;
+
+ bake.inst_->lightprobes.bake(bake.depsgraph_,
+ LIGHTPROBE_TYPE_GRID,
+ grid->offset + sample_index,
+ bounce,
+ position,
+ probe,
+ grid->visibility_range);
+
+ /* TODO incremental LVL update. */
+ if (sample_index + 1 == (grid->resolution[0] * grid->resolution[1] * grid->resolution[2])) {
+ grid->is_ready = 1;
+ }
+ /* If it's the last grid of the last bounce, tag lighting as updated. */
+ if ((grid->offset + sample_index == bake.irradiance_samples_count_ - 1) &&
+ (bounce == sce_eevee.gi_diffuse_bounces - 1)) {
+ bake.lcache_->flag &= ~LIGHTCACHE_UPDATE_GRID;
+ }
+ }
+
+ static void callback(void *UNUSED(ved), void *user_data)
+ {
+ reinterpret_cast<GridRenderData *>(user_data)->render();
+ }
+ };
+
+ struct CubemapRenderData {
+ LightBake &bake;
+ LightProbeCache *cube;
+ int64_t cube_index_;
+
+ CubemapRenderData(LightBake &bake_) : bake(bake_){};
+
+ void render(void)
+ {
+ LightProbe *probe = (cube->probe_index > -1) ? &bake.cubes_probe_[cube->probe_index] :
+ nullptr;
+ bake.inst_->lightprobes.bake(
+ bake.depsgraph_, LIGHTPROBE_TYPE_CUBE, cube_index_, 0, cube->position, probe);
+ cube->is_ready = 1;
+
+ /* If it's the last probe, tag lighting as updated. */
+ if (cube_index_ == bake.cube_len_ - 1) {
+ bake.lcache_->flag &= ~LIGHTCACHE_UPDATE_CUBE;
+ }
+ }
+
+ static void callback(void *UNUSED(ved), void *user_data)
+ {
+ reinterpret_cast<CubemapRenderData *>(user_data)->render();
+ }
+ };
+
+ public:
+ /* Interupting an existing bake job and reusing its resources if old_bake is not null.
+ * Otherwise just create a new bake context. */
+ LightBake(struct Main *bmain,
+ struct ViewLayer *view_layer,
+ struct Scene *scene,
+ bool run_as_job,
+ int frame,
+ int delay,
+ LightBake *old_bake = nullptr)
+ : /* Cannot reuse depsgraph for now because we cannot get the update from the
+ * main database directly. TODO reuse depsgraph and only update positions. */
+ depsgraph_(DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER)),
+ view_layer_input_(view_layer),
+ scene_(scene),
+ bmain_(bmain),
+ frame_(frame),
+ delay_(delay)
+ {
+ if (old_bake && (old_bake->view_layer_input_ == view_layer) && (old_bake->bmain_ == bmain)) {
+ {
+ /* Steal gl_context. */
+ std::lock_guard<std::mutex> lock(old_bake->mutex_);
+ old_bake->own_resources_ = false;
+ gl_context_ = old_bake->gl_context_;
+ old_bake->stop();
+ }
+
+ if (gl_context_ == nullptr && !GPU_use_main_context_workaround()) {
+ gl_context_ = WM_opengl_context_create();
+ wm_window_reset_drawable();
+ }
+ }
+ else {
+ if (run_as_job && !GPU_use_main_context_workaround()) {
+ gl_context_ = WM_opengl_context_create();
+ wm_window_reset_drawable();
+ }
+ }
+ BLI_assert(BLI_thread_is_main());
+ }
+
+ ~LightBake()
+ {
+ /* TODO reuse depsgraph. */
+ /* if (own_resources_) { */
+ DEG_graph_free(depsgraph_);
+ /* } */
+ }
+
+ void update_scene_cache(void)
+ {
+ /* If a new light-cache was created, free the old one and reference the new. */
+ if (lcache_ && scene_->eevee.light_cache_data != lcache_) {
+ if (scene_->eevee.light_cache_data != NULL) {
+ EEVEE_lightcache_free(scene_->eevee.light_cache_data);
+ }
+ scene_->eevee.light_cache_data = lcache_;
+ own_light_cache_ = false;
+ }
+ lcache_->update_info(&scene_->eevee);
+ /* Tag to flush the pointer update to eval scenes. */
+ DEG_id_tag_update(&scene_->id, ID_RECALC_COPY_ON_WRITE);
+ }
+
+ void do_bake(short *stop, short *do_update, float *progress)
+ {
+ stop_ = stop;
+ do_update_ = do_update;
+ progress_ = progress;
+
+ DEG_graph_relations_update(depsgraph_);
+ DEG_evaluate_on_framechange(depsgraph_, frame_);
+
+ view_layer_ = DEG_get_evaluated_view_layer(depsgraph_);
+
+ context_enable();
+ create_resources();
+
+ /* Resource allocation can fail. Early exit in this case. */
+ if (lcache_->flag & LIGHTCACHE_INVALID) {
+ lcache_->flag &= ~LIGHTCACHE_BAKING;
+ this->stop();
+ context_disable();
+ delete_resources();
+ return;
+ }
+
+ context_disable();
+
+ /* HACK: Sleep to delay the first rendering operation
+ * that causes a small freeze (caused by VBO generation)
+ * because this step is locking at this moment. */
+ /* TODO remove this. */
+ if (delay_) {
+ PIL_sleep_ms(delay_);
+ }
+
+ SceneEEVEE &sce_eevee = DEG_get_evaluated_scene(depsgraph_)->eevee;
+
+ /* Render world reflections first. Needed for realtime bake preview. */
+ if (lcache_->flag & LIGHTCACHE_UPDATE_CUBE) {
+ CubemapRenderData cb_data(*this);
+ cb_data.cube = &lcache_->cube_data[0];
+ cb_data.cube_index_ = 0;
+ lightbake_do_sample(CubemapRenderData::callback, &cb_data);
+ }
+ /* Render irradiance grids. */
+ if (lcache_->flag & LIGHTCACHE_UPDATE_GRID) {
+ for (int bounce : IndexRange(sce_eevee.gi_diffuse_bounces)) {
+ for (LightGridCache &grid : MutableSpan(lcache_->grid_data, grid_len_)) {
+ int64_t grid_sample_len = grid.resolution[0] * grid.resolution[1] * grid.resolution[2];
+ for (auto sample_index : IndexRange(grid_sample_len)) {
+ GridRenderData cb_data(*this);
+ cb_data.grid = &grid;
+ cb_data.sample_index = sample_index;
+ cb_data.bounce = bounce;
+ lightbake_do_sample(GridRenderData::callback, &cb_data);
+ }
+ }
+ }
+ }
+ /* Render reflections. */
+ if (lcache_->flag & LIGHTCACHE_UPDATE_CUBE) {
+ for (auto cube_index : IndexRange(1, cube_len_ - 1)) {
+ CubemapRenderData cb_data(*this);
+ cb_data.cube = &lcache_->cube_data[cube_index];
+ cb_data.cube_index_ = cube_index;
+ lightbake_do_sample(CubemapRenderData::callback, &cb_data);
+ }
+ }
+ /* Read the resulting lighting data to save it to file/disk. */
+ context_enable();
+ lcache_->readback_irradiance();
+ lcache_->readback_reflections();
+ context_disable();
+
+ lcache_->flag |= LIGHTCACHE_BAKED;
+ lcache_->flag &= ~LIGHTCACHE_BAKING;
+
+ /* Assume that if lbake->gl_context is NULL
+ * we are not running in this in a job, so update
+ * the scene light-cache pointer before deleting it. */
+ if (gl_context_ == nullptr) {
+ BLI_assert(BLI_thread_is_main());
+ update_scene_cache();
+ }
+
+ delete_resources();
+
+ /* Free GPU smoke textures and the smoke domain list correctly: See also
+ * T73921.*/
+ /* TODO(fclem) is this still needed? */
+ // EEVEE_volumes_free_smoke_textures();
+
+ stop_ = nullptr;
+ do_update_ = nullptr;
+ progress_ = nullptr;
+ }
+
+ private:
+ bool lightbake_do_sample(void (*render_callback)(void *ved, void *user_data), void *user_data)
+ {
+ if (G.is_break == true || *stop_) {
+ return false;
+ }
+ /* TODO: make DRW manager instantiable (and only lock on drawing) */
+ context_enable();
+ DRW_custom_pipeline(&draw_engine_eevee_type, depsgraph_, render_callback, user_data);
+ done_ += 1;
+ *progress_ = done_ / (float)total_;
+ *do_update_ = 1;
+ context_disable();
+ return true;
+ }
+
+ LightGridCache grid_cache_from_object(Object *ob, int probe_index, int64_t &offset)
+ {
+ LightProbe *probe = (LightProbe *)ob->data;
+
+ LightGridCache grid;
+ copy_v3_v3_int(grid.resolution, &probe->grid_resolution_x);
+
+ /* Save current offset and set it for the next grid. */
+ grid.offset = offset;
+ offset += grid.resolution[0] * grid.resolution[1] * grid.resolution[2];
+
+ /* Add one for level 0 */
+ float fac = 1.0f / max_ff(1e-8f, probe->falloff);
+ grid.attenuation_scale = fac / max_ff(1e-8f, probe->distinf);
+ grid.attenuation_bias = fac;
+
+ /* Update transforms */
+ vec3 half_cell_dim = vec3(1.0f) / vec3(UNPACK3(grid.resolution));
+ vec3 cell_dim = half_cell_dim * 2.0f;
+
+ /* Matrix converting world space to cell ranges. */
+ invert_m4_m4(grid.mat, ob->obmat);
+
+ float4x4 obmat(ob->obmat);
+
+ /* First cell. */
+ vec3 corner = obmat * (half_cell_dim - vec3(1.0f));
+ copy_v3_v3(grid.corner, corner);
+
+ /* Opposite neighbor cell. */
+ vec3 increment_x = (obmat * vec3(cell_dim.x, 0.0f, 0.0f)) - vec3(obmat.values[3]);
+ copy_v3_v3(grid.increment_x, increment_x);
+ vec3 increment_y = (obmat * vec3(0.0f, cell_dim.y, 0.0f)) - vec3(obmat.values[3]);
+ copy_v3_v3(grid.increment_y, increment_y);
+ vec3 increment_z = (obmat * vec3(0.0f, 0.0f, cell_dim.z)) - vec3(obmat.values[3]);
+ copy_v3_v3(grid.increment_z, increment_z);
+
+ grid.probe_index = probe_index;
+ grid.is_ready = 0;
+ /* Update level for progressive update. TODO(fclem) port back. */
+ grid.level_bias = 1.0f;
+
+ grid.visibility_bias = 0.05f * probe->vis_bias;
+ grid.visibility_bleed = probe->vis_bleedbias;
+ grid.visibility_range = 1.0f + sqrtf(max_fff(len_squared_v3(grid.increment_x),
+ len_squared_v3(grid.increment_y),
+ len_squared_v3(grid.increment_z)));
+ return grid;
+ }
+
+ LightProbeCache cube_cache_from_object(Object *ob, int probe_index)
+ {
+ LightProbe *probe = (LightProbe *)ob->data;
+
+ LightProbeCache cube;
+ /* Update transforms. */
+ copy_v3_v3(cube.position, ob->obmat[3]);
+
+ /* Attenuation. */
+ cube.attenuation_type = probe->attenuation_type;
+ cube.attenuation_fac = 1.0f / max_ff(1e-8f, probe->falloff);
+
+ unit_m4(cube.attenuationmat);
+ scale_m4_fl(cube.attenuationmat, probe->distinf);
+ mul_m4_m4m4(cube.attenuationmat, ob->obmat, cube.attenuationmat);
+ invert_m4(cube.attenuationmat);
+
+ /* Parallax. */
+ unit_m4(cube.parallaxmat);
+
+ if ((probe->flag & LIGHTPROBE_FLAG_CUSTOM_PARALLAX) != 0) {
+ cube.parallax_type = probe->parallax_type;
+ scale_m4_fl(cube.parallaxmat, probe->distpar);
+ }
+ else {
+ cube.parallax_type = probe->attenuation_type;
+ scale_m4_fl(cube.parallaxmat, probe->distinf);
+ }
+
+ mul_m4_m4m4(cube.parallaxmat, ob->obmat, cube.parallaxmat);
+ invert_m4(cube.parallaxmat);
+
+ cube.probe_index = probe_index;
+ cube.is_ready = 0;
+ return cube;
+ }
+
+ /* Counts and generate lightprobes cache data. Returns irradiance sample total count. */
+ int64_t sync_probes(Vector<LightGridCache> &grids_data, Vector<LightProbeCache> &cubes_data)
+ {
+ /* Start at one to count world sample. */
+ int64_t irradiance_samples_count = 1;
+
+ grids_probe_.clear();
+ cubes_probe_.clear();
+
+ DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph_, ob) {
+ const int ob_visibility = BKE_object_visibility(ob, DAG_EVAL_RENDER);
+ if ((ob_visibility & OB_VISIBLE_SELF) == 0) {
+ continue;
+ }
+
+ if (ob->type == OB_LIGHTPROBE) {
+ LightProbe *prb = (LightProbe *)ob->data;
+
+ if (prb->type == LIGHTPROBE_TYPE_GRID) {
+ int probe_index = grids_probe_.append_and_get_index(*prb);
+ grids_data.append(grid_cache_from_object(ob, probe_index, irradiance_samples_count));
+ }
+ else if (prb->type == LIGHTPROBE_TYPE_CUBE) {
+ int probe_index = cubes_probe_.append_and_get_index(*prb);
+ cubes_data.append(cube_cache_from_object(ob, probe_index));
+ }
+ }
+ }
+ DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END;
+
+ auto sort_grids = [](const LightGridCache &a, const LightGridCache &b) {
+ return mat4_to_scale(a.mat) < mat4_to_scale(b.mat);
+ };
+ std::sort(grids_data.begin(), grids_data.end(), sort_grids);
+
+ auto sort_cubes = [](const LightProbeCache &a, const LightProbeCache &b) {
+ return mat4_to_scale(a.attenuationmat) < mat4_to_scale(b.attenuationmat);
+ };
+ std::sort(cubes_data.begin(), cubes_data.end(), sort_cubes);
+
+ LightProbeCache world_cube = {};
+ world_cube.probe_index = -1;
+ cubes_data.prepend({world_cube});
+
+ LightGridCache world_grid = {};
+ world_grid.resolution[0] = world_grid.resolution[1] = world_grid.resolution[2] = 1;
+ world_grid.offset = 0;
+ world_grid.probe_index = -1;
+ grids_data.prepend({world_grid});
+
+ return irradiance_samples_count;
+ }
+
+ void create_resources(void)
+ {
+ Scene *scene_eval = DEG_get_evaluated_scene(depsgraph_);
+ SceneEEVEE &sce_eevee = scene_eval->eevee;
+
+ Vector<LightProbeCache> cubes_data;
+ Vector<LightGridCache> grids_data;
+ int64_t irradiance_samples_count = sync_probes(grids_data, cubes_data);
+
+ grid_len_ = grids_data.size();
+ cube_len_ = cubes_data.size();
+
+ ivec3 irradiance_tx_size;
+ LightCache::irradiance_cache_size_get(
+ sce_eevee.gi_visibility_resolution, irradiance_samples_count, irradiance_tx_size);
+
+ /* Ensure Light Cache is ready to accept new data. If not recreate one.
+ * WARNING: All the following must be threadsafe. It's currently protected by the DRW mutex. */
+ lcache_ = (LightCache *)sce_eevee.light_cache_data;
+
+ /* TODO validate irradiance and reflection cache independently... */
+ if (lcache_ &&
+ !lcache_->validate(
+ cube_len_, sce_eevee.gi_cubemap_resolution, grid_len_, irradiance_tx_size)) {
+ /* Note: this is only the scene eval data. This does not count as ownership.
+ * Real owner is original scene which gets the new lightcache in update_scene_cache(). */
+ sce_eevee.light_cache_data = lcache_ = nullptr;
+ }
+
+ if (lcache_ == nullptr) {
+ lcache_ = new LightCache(cube_len_,
+ grid_len_,
+ sce_eevee.gi_cubemap_resolution,
+ sce_eevee.gi_visibility_resolution,
+ irradiance_tx_size);
+ own_light_cache_ = true;
+ /* Note: this is only the scene eval data. This does not count as ownership. */
+ sce_eevee.light_cache_data = lcache_;
+ }
+
+ /* Copy gathered data to cache. */
+ memcpy(lcache_->cube_data, cubes_data.data(), cube_len_ * sizeof(*lcache_->cube_data));
+ memcpy(lcache_->grid_data, grids_data.data(), grid_len_ * sizeof(*lcache_->grid_data));
+
+ lcache_->load();
+ lcache_->flag |= LIGHTCACHE_BAKING;
+
+ inst_ = reinterpret_cast<Instance *>(EEVEE_instance_alloc());
+
+ done_ = 0;
+ total_ = irradiance_samples_count * sce_eevee.gi_diffuse_bounces + grid_len_;
+ }
+
+ void delete_resources(void)
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+
+ if (gl_context_) {
+ DRW_opengl_render_context_enable(gl_context_);
+ DRW_gpu_render_context_enable(gpu_context_);
+ }
+ else {
+ DRW_opengl_context_enable();
+ }
+ /* XXX Free the resources contained in the viewlayer data
+ * to be able to free the context before deleting the depsgraph. */
+ /* TODO(fclem) This is not necessary for now because we do not store anything in view layers
+ * since the start of EEVEE's rewrite. But this might change. */
+ // EEVEE_view_layer_data_free(sldata_);
+
+ if (inst_) {
+ EEVEE_instance_free(reinterpret_cast<EEVEE_Instance *>(inst_));
+ }
+
+ if (gpu_context_) {
+ DRW_gpu_render_context_disable(gpu_context_);
+ DRW_gpu_render_context_enable(gpu_context_);
+ GPU_context_discard(gpu_context_);
+ }
+
+ if (gl_context_ && own_resources_) {
+ /* Delete the baking context. */
+ DRW_opengl_render_context_disable(gl_context_);
+ WM_opengl_context_dispose(gl_context_);
+ gpu_context_ = nullptr;
+ gl_context_ = nullptr;
+ }
+ else if (gl_context_) {
+ DRW_opengl_render_context_disable(gl_context_);
+ }
+ else {
+ DRW_opengl_context_disable();
+ }
+ }
+
+ /* Stop baking (only if async). Threadsafety is responsibility of the caller. */
+ void stop(void)
+ {
+ if (stop_ != nullptr) {
+ *stop_ = 1;
+ }
+ if (do_update_ != nullptr) {
+ *do_update_ = 1;
+ }
+ }
+
+ void context_enable(void)
+ {
+ if (GPU_use_main_context_workaround() && !BLI_thread_is_main()) {
+ GPU_context_main_lock();
+ DRW_opengl_context_enable();
+ return;
+ }
+
+ if (gl_context_) {
+ DRW_opengl_render_context_enable(gl_context_);
+ if (gpu_context_ == NULL) {
+ gpu_context_ = GPU_context_create(NULL);
+ }
+ DRW_gpu_render_context_enable(gpu_context_);
+ }
+ else {
+ DRW_opengl_context_enable();
+ }
+ }
+
+ void context_disable(void)
+ {
+ if (GPU_use_main_context_workaround() && !BLI_thread_is_main()) {
+ DRW_opengl_context_disable();
+ GPU_context_main_unlock();
+ return;
+ }
+
+ if (gl_context_) {
+ DRW_gpu_render_context_disable(gpu_context_);
+ DRW_opengl_render_context_disable(gl_context_);
+ }
+ else {
+ DRW_opengl_context_disable();
+ }
+ }
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("EEVEE:LightBake")
+};
+
+} // namespace blender::eevee
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name C interface
+ * \{ */
+
+using namespace blender;
+
+/**
+ * Allocate a lightbake object to run async baking.
+ * MUST run on the main thread.
+ **/
+struct wmJob *EEVEE_lightbake_job_create(struct wmWindowManager *wm,
+ struct wmWindow *win,
+ struct Main *bmain,
+ struct ViewLayer *view_layer,
+ struct Scene *scene,
+ int delay,
+ int frame)
+{
+ /* Only one render job at a time. */
+ if (WM_jobs_test(wm, scene, WM_JOB_TYPE_RENDER)) {
+ return nullptr;
+ }
+
+ wmJob *wm_job = WM_jobs_get(wm,
+ win,
+ scene,
+ "Bake Lighting",
+ WM_JOB_EXCL_RENDER | WM_JOB_PRIORITY | WM_JOB_PROGRESS,
+ WM_JOB_TYPE_LIGHT_BAKE);
+
+ /* If job exists do not recreate context and depsgraph. */
+ auto *old_lbake = (eevee::LightBake *)WM_jobs_customdata_get(wm_job);
+
+ auto *lbake = new eevee::LightBake(bmain, view_layer, scene, true, frame, delay, old_lbake);
+
+ WM_jobs_customdata_set(wm_job, lbake, EEVEE_lightbake_job_data_free);
+ WM_jobs_timer(wm_job, 0.4, NC_SCENE | NA_EDITED, 0);
+ WM_jobs_callbacks(
+ wm_job, EEVEE_lightbake_job, nullptr, EEVEE_lightbake_update, EEVEE_lightbake_update);
+
+ G.is_break = false;
+
+ return wm_job;
+}
+
+/**
+ * Allocate a lightbake object to run blocking baking. MUST run on the main thread.
+ **/
+void *EEVEE_lightbake_job_data_alloc(struct Main *bmain,
+ struct ViewLayer *view_layer,
+ struct Scene *scene,
+ /* TODO(fclem) remove */
+ bool UNUSED(run_as_job),
+ int frame)
+{
+ return new eevee::LightBake(bmain, view_layer, scene, false, frame, 0);
+}
+
+void EEVEE_lightbake_job_data_free(void *custom_data)
+{
+ delete reinterpret_cast<eevee::LightBake *>(custom_data);
+}
+
+/**
+ * Update function that swaps the lightcache in the scene by the one being baked if it is
+ * already renderable.
+ **/
+void EEVEE_lightbake_update(void *custom_data)
+{
+ reinterpret_cast<eevee::LightBake *>(custom_data)->update_scene_cache();
+}
+
+void EEVEE_lightbake_job(void *custom_data, short *stop, short *do_update, float *progress)
+{
+ reinterpret_cast<eevee::LightBake *>(custom_data)->do_bake(stop, do_update, progress);
+}
+
+void EEVEE_lightcache_free(struct LightCache *lcache_)
+{
+ eevee::LightCache *lcache = reinterpret_cast<eevee::LightCache *>(lcache_);
+ MEM_delete(lcache);
+}
+
+void EEVEE_lightcache_info_update(struct SceneEEVEE *eevee)
+{
+ eevee::LightCache::update_info(eevee);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Read / Write
+ * \{ */
+
+static void write_lightcache_texture(BlendWriter *writer, LightCacheTexture *tex)
+{
+ if (tex->data) {
+ size_t data_size = tex->components * tex->tex_size[0] * tex->tex_size[1] * tex->tex_size[2];
+ if (tex->data_type == LIGHTCACHETEX_FLOAT) {
+ data_size *= sizeof(float);
+ }
+ else if (tex->data_type == LIGHTCACHETEX_UINT) {
+ data_size *= sizeof(uint);
+ }
+
+ /* FIXME: We can't save more than what 32bit systems can handle.
+ * The solution would be to split the texture but it is too late for 2.90.
+ * (see T78529) */
+ if (data_size < INT_MAX) {
+ BLO_write_raw(writer, data_size, tex->data);
+ }
+ }
+}
+
+void EEVEE_lightcache_blend_write(struct BlendWriter *writer, struct LightCache *cache)
+{
+ write_lightcache_texture(writer, &cache->grid_tx);
+ write_lightcache_texture(writer, &cache->cube_tx);
+
+ if (cache->cube_mips) {
+ BLO_write_struct_array(writer, LightCacheTexture, cache->mips_len, cache->cube_mips);
+ for (int i = 0; i < cache->mips_len; i++) {
+ write_lightcache_texture(writer, &cache->cube_mips[i]);
+ }
+ }
+
+ BLO_write_struct_array(writer, LightGridCache, cache->grid_len, cache->grid_data);
+ BLO_write_struct_array(writer, LightProbeCache, cache->cube_len, cache->cube_data);
+}
+
+static void direct_link_lightcache_texture(BlendDataReader *reader, LightCacheTexture *lctex)
+{
+ lctex->tex = nullptr;
+
+ if (lctex->data) {
+ BLO_read_data_address(reader, &lctex->data);
+ if (lctex->data && BLO_read_requires_endian_switch(reader)) {
+ int data_size = lctex->components * lctex->tex_size[0] * lctex->tex_size[1] *
+ lctex->tex_size[2];
+
+ if (lctex->data_type == LIGHTCACHETEX_FLOAT) {
+ BLI_endian_switch_float_array((float *)lctex->data, data_size * sizeof(float));
+ }
+ else if (lctex->data_type == LIGHTCACHETEX_UINT) {
+ BLI_endian_switch_uint32_array((uint *)lctex->data, data_size * sizeof(uint));
+ }
+ }
+ }
+
+ if (lctex->data == nullptr) {
+ zero_v3_int(lctex->tex_size);
+ }
+}
+
+void EEVEE_lightcache_blend_read_data(struct BlendDataReader *reader, struct LightCache *cache)
+{
+ cache->flag &= ~LIGHTCACHE_NOT_USABLE;
+ direct_link_lightcache_texture(reader, &cache->cube_tx);
+ direct_link_lightcache_texture(reader, &cache->grid_tx);
+
+ if (cache->cube_mips) {
+ BLO_read_data_address(reader, &cache->cube_mips);
+ for (int i = 0; i < cache->mips_len; i++) {
+ direct_link_lightcache_texture(reader, &cache->cube_mips[i]);
+ }
+ }
+
+ BLO_read_data_address(reader, &cache->cube_data);
+ BLO_read_data_address(reader, &cache->grid_data);
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.hh b/source/blender/draw/engines/eevee/eevee_lightcache.hh
new file mode 100644
index 00000000000..93655b97f2c
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_lightcache.hh
@@ -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
diff --git a/source/blender/draw/engines/eevee/eevee_lightprobe.cc b/source/blender/draw/engines/eevee/eevee_lightprobe.cc
new file mode 100644
index 00000000000..c8eadea5f9e
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_lightprobe.cc
@@ -0,0 +1,578 @@
+/*
+ * 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;
+
+ ivec3 irr_size;
+ LightCache::irradiance_cache_size_get(
+ sce_eevee.gi_visibility_resolution, irr_samples_len, irr_size);
+
+ lightcache_lookdev_ = new LightCache(cube_len,
+ grid_len,
+ sce_eevee.gi_cubemap_resolution,
+ sce_eevee.gi_visibility_resolution,
+ irr_size);
+ }
+ lightcache_ = lightcache_lookdev_;
+ }
+
+ for (DRWView *&view : face_view_) {
+ view = nullptr;
+ }
+
+ if (info_data_.cubes.display_size != sce_eevee.gi_cubemap_draw_size ||
+ info_data_.grids.display_size != sce_eevee.gi_irradiance_draw_size ||
+ info_data_.grids.irradiance_smooth != square_f(sce_eevee.gi_irradiance_smoothing)) {
+ /* TODO(fclem) reset on scene update instead. */
+ inst_.sampling.reset();
+ }
+
+ info_data_.cubes.display_size = sce_eevee.gi_cubemap_draw_size;
+ info_data_.grids.display_size = sce_eevee.gi_irradiance_draw_size;
+ info_data_.grids.irradiance_smooth = square_f(sce_eevee.gi_irradiance_smoothing);
+ info_data_.grids.irradiance_cells_per_row = lightcache_->irradiance_cells_per_row_get();
+ info_data_.grids.visibility_size = lightcache_->vis_res;
+ info_data_.grids.visibility_cells_per_row = lightcache_->grid_tx.tex_size[0] /
+ info_data_.grids.visibility_size;
+ info_data_.grids.visibility_cells_per_layer = (lightcache_->grid_tx.tex_size[1] /
+ info_data_.grids.visibility_size) *
+ info_data_.grids.visibility_cells_per_row;
+
+ glossy_clamp_ = sce_eevee.gi_glossy_clamp;
+ filter_quality_ = clamp_f(sce_eevee.gi_filter_quality, 1.0f, 8.0f);
+}
+
+void LightProbeModule::begin_sync()
+{
+ {
+ cube_downsample_ps_ = DRW_pass_create("Downsample.Cube", DRW_STATE_WRITE_COLOR);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_FILTER_DOWNSAMPLE_CUBE);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, cube_downsample_ps_);
+ DRW_shgroup_uniform_texture_ref(grp, "input_tx", &cube_downsample_input_tx_);
+ DRW_shgroup_uniform_block(grp, "filter_block", filter_data_.ubo_get());
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 6);
+ }
+ {
+ filter_glossy_ps_ = DRW_pass_create("Filter.GlossyMip", DRW_STATE_WRITE_COLOR);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_FILTER_GLOSSY);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, filter_glossy_ps_);
+ DRW_shgroup_uniform_texture_ref(grp, "radiance_tx", &cube_downsample_input_tx_);
+ DRW_shgroup_uniform_block(grp, "filter_block", filter_data_.ubo_get());
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 6);
+ }
+ {
+ filter_diffuse_ps_ = DRW_pass_create("Filter.Diffuse", DRW_STATE_WRITE_COLOR);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_FILTER_DIFFUSE);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, filter_diffuse_ps_);
+ DRW_shgroup_uniform_texture_ref(grp, "radiance_tx", &cube_downsample_input_tx_);
+ DRW_shgroup_uniform_block(grp, "filter_block", filter_data_.ubo_get());
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+ }
+ {
+ filter_visibility_ps_ = DRW_pass_create("Filter.Visibility", DRW_STATE_WRITE_COLOR);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_FILTER_VISIBILITY);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, filter_visibility_ps_);
+ DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &cube_downsample_input_tx_);
+ DRW_shgroup_uniform_block(grp, "filter_block", filter_data_.ubo_get());
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+ }
+
+ display_ps_ = nullptr;
+
+ if ((inst_.v3d != nullptr) && ((inst_.v3d->flag2 & V3D_HIDE_OVERLAYS) == 0)) {
+ if (inst_.scene->eevee.flag & (SCE_EEVEE_SHOW_CUBEMAPS | SCE_EEVEE_SHOW_IRRADIANCE)) {
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
+ display_ps_ = DRW_pass_create("LightProbe.Display", state);
+ }
+
+ if (inst_.scene->eevee.flag & SCE_EEVEE_SHOW_CUBEMAPS) {
+ if (lightcache_->cube_len > 1) {
+ GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_DISPLAY_CUBEMAP);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, display_ps_);
+ DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", cube_tx_ref_get());
+ DRW_shgroup_uniform_block(grp, "cubes_block", cube_ubo_get());
+ DRW_shgroup_uniform_block(grp, "lightprobes_info_block", info_ubo_get());
+
+ uint cubemap_count = 0;
+ /* Skip world. */
+ for (auto cube_id : IndexRange(1, lightcache_->cube_len - 1)) {
+ const LightProbeCache &cube = lightcache_->cube_data[cube_id];
+ /* Note: only works because probes are rendered in sequential order. */
+ if (cube.is_ready) {
+ cubemap_count++;
+ }
+ }
+ if (cubemap_count > 0) {
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, cubemap_count * 2);
+ }
+ }
+ }
+
+ if (inst_.scene->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE) {
+ if (lightcache_->grid_len > 1) {
+ GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_DISPLAY_IRRADIANCE);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, display_ps_);
+ DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", grid_tx_ref_get());
+ DRW_shgroup_uniform_block(grp, "grids_block", grid_ubo_get());
+ DRW_shgroup_uniform_block(grp, "lightprobes_info_block", info_ubo_get());
+
+ /* Skip world. */
+ for (auto grid_id : IndexRange(1, lightcache_->grid_len - 1)) {
+ const LightGridCache &grid = lightcache_->grid_data[grid_id];
+ if (grid.is_ready) {
+ DRWShadingGroup *grp_sub = DRW_shgroup_create_sub(grp);
+ DRW_shgroup_uniform_int_copy(grp_sub, "grid_id", grid_id);
+ uint sample_count = grid.resolution[0] * grid.resolution[1] * grid.resolution[2];
+ DRW_shgroup_call_procedural_triangles(grp_sub, nullptr, sample_count * 2);
+ }
+ }
+ }
+ }
+ }
+}
+
+void LightProbeModule::end_sync()
+{
+ if (lightcache_->flag & LIGHTCACHE_UPDATE_WORLD) {
+ cubemap_prepare(vec3(0.0f), 0.01f, 1.0f, true);
+ }
+}
+
+void LightProbeModule::cubemap_prepare(vec3 position, float near, float far, bool background_only)
+{
+ SceneEEVEE &sce_eevee = inst_.scene->eevee;
+ int cube_res = sce_eevee.gi_cubemap_resolution;
+ int cube_mip_count = (int)log2_ceil_u(cube_res);
+
+ mat4 viewmat;
+ unit_m4(viewmat);
+ negate_v3_v3(viewmat[3], position);
+
+ /* TODO(fclem) We might want to have theses as temporary textures. */
+ cube_depth_tx_.ensure_cubemap("CubemapDepth", cube_res, cube_mip_count, GPU_DEPTH24_STENCIL8);
+ cube_color_tx_.ensure_cubemap("CubemapColor", cube_res, cube_mip_count, GPU_RGBA16F);
+ GPU_texture_mipmap_mode(cube_color_tx_, true, true);
+
+ cube_downsample_fb_.ensure(GPU_ATTACHMENT_TEXTURE(cube_depth_tx_),
+ GPU_ATTACHMENT_TEXTURE(cube_color_tx_));
+
+ filter_cube_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(lightcache_->cube_tx.tex));
+ filter_grid_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(lightcache_->grid_tx.tex));
+
+ mat4 winmat;
+ cubeface_winmat_get(winmat, near, far);
+
+ for (auto i : IndexRange(ARRAY_SIZE(probe_views_))) {
+ probe_views_[i].sync(cube_color_tx_, cube_depth_tx_, winmat, viewmat, background_only);
+ }
+}
+
+void LightProbeModule::cubemap_render(void)
+{
+ DRW_stats_group_start("Cubemap Render");
+ for (auto i : IndexRange(ARRAY_SIZE(probe_views_))) {
+ probe_views_[i].render();
+ }
+ DRW_stats_group_end();
+
+ /* Update mipchain. */
+ filter_data_.target_layer = 0;
+ filter_data_.push_update();
+ cube_downsample_input_tx_ = cube_color_tx_;
+
+ DRW_stats_group_start("Cubemap Downsample");
+ GPU_framebuffer_recursive_downsample(cube_downsample_fb_, 7, cube_downsample_cb, this);
+ DRW_stats_group_end();
+}
+
+void LightProbeModule::filter_glossy(int cube_index, float intensity)
+{
+ DRW_stats_group_start("Filter.Glossy");
+
+ filter_data_.instensity_fac = intensity;
+ filter_data_.target_layer = cube_index * 6;
+
+ int level_max = lightcache_->mips_len;
+ for (int level = 0; level <= level_max; level++) {
+ filter_data_.luma_max = (glossy_clamp_ > 0.0f) ? glossy_clamp_ : 1e16f;
+ /* Disney Roughness. */
+ filter_data_.roughness = square_f(level / (float)level_max);
+ /* Distribute Roughness across lod more evenly. */
+ filter_data_.roughness = square_f(filter_data_.roughness);
+ /* Avoid artifacts. */
+ filter_data_.roughness = clamp_f(filter_data_.roughness, 1e-4f, 0.9999f);
+ /* Variable sample count and bias to make first levels faster. */
+ switch (level) {
+ case 0:
+ filter_data_.sample_count = 1.0f;
+ filter_data_.lod_bias = -1.0f;
+ break;
+ case 1:
+ filter_data_.sample_count = filter_quality_ * 32.0f;
+ filter_data_.lod_bias = 1.0f;
+ break;
+ case 2:
+ filter_data_.sample_count = filter_quality_ * 40.0f;
+ filter_data_.lod_bias = 2.0f;
+ break;
+ case 3:
+ filter_data_.sample_count = filter_quality_ * 64.0f;
+ filter_data_.lod_bias = 2.0f;
+ break;
+ default:
+ filter_data_.sample_count = filter_quality_ * 128.0f;
+ filter_data_.lod_bias = 2.0f;
+ break;
+ }
+ /* Add automatic LOD bias (based on target size). */
+ filter_data_.lod_bias += lod_bias_from_cubemap();
+
+ filter_data_.push_update();
+
+ filter_cube_fb_.ensure(GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE_MIP(lightcache_->cube_tx.tex, level));
+ GPU_framebuffer_bind(filter_cube_fb_);
+ DRW_draw_pass(filter_glossy_ps_);
+ }
+
+ DRW_stats_group_end();
+}
+
+void LightProbeModule::filter_diffuse(int sample_index, float intensity)
+{
+ filter_data_.instensity_fac = intensity;
+ filter_data_.target_layer = 0;
+ filter_data_.luma_max = 1e16f;
+ filter_data_.sample_count = 1024.0f;
+ filter_data_.lod_bias = lod_bias_from_cubemap();
+
+ filter_data_.push_update();
+
+ ivec2 extent = ivec2(3, 2);
+ ivec2 offset = extent;
+ offset.x *= sample_index % info_data_.grids.irradiance_cells_per_row;
+ offset.y *= sample_index / info_data_.grids.irradiance_cells_per_row;
+
+ GPU_framebuffer_bind(filter_grid_fb_);
+ GPU_framebuffer_viewport_set(filter_grid_fb_, UNPACK2(offset), UNPACK2(extent));
+ DRW_draw_pass(filter_diffuse_ps_);
+ GPU_framebuffer_viewport_reset(filter_grid_fb_);
+}
+
+void LightProbeModule::filter_visibility(int sample_index,
+ float visibility_blur,
+ float visibility_range)
+{
+ ivec2 extent = ivec2(info_data_.grids.visibility_size);
+ ivec2 offset = extent;
+ offset.x *= sample_index % info_data_.grids.visibility_cells_per_row;
+ offset.y *= (sample_index / info_data_.grids.visibility_cells_per_row) %
+ info_data_.grids.visibility_cells_per_layer;
+
+ filter_data_.target_layer = 1 + sample_index / info_data_.grids.visibility_cells_per_layer;
+ filter_data_.sample_count = 512.0f; /* TODO refine */
+ filter_data_.visibility_blur = visibility_blur;
+ filter_data_.visibility_range = visibility_range;
+
+ filter_data_.push_update();
+
+ GPU_framebuffer_bind(filter_grid_fb_);
+ GPU_framebuffer_viewport_set(filter_grid_fb_, UNPACK2(offset), UNPACK2(extent));
+ DRW_draw_pass(filter_visibility_ps_);
+ GPU_framebuffer_viewport_reset(filter_grid_fb_);
+}
+
+void LightProbeModule::update_world_cache()
+{
+ DRW_stats_group_start("LightProbe.world");
+
+ const DRWView *view_active = DRW_view_get_active();
+
+ cubemap_render();
+
+ filter_diffuse(0, 1.0f);
+
+ if ((lightcache_->flag & LIGHTCACHE_NO_REFLECTION) == 0) {
+ /* TODO(fclem) Change ray type. */
+ /* OPTI(fclem) Only re-render if there is a light path node in the world material. */
+ // cubemap_render();
+
+ filter_glossy(0, 1.0f);
+ }
+
+ if (view_active != nullptr) {
+ DRW_view_set_active(view_active);
+ }
+
+ DRW_stats_group_end();
+}
+
+/* Ensure a temporary cache the same size at the target lightcache exists. */
+LightCache *LightProbeModule::baking_cache_get(void)
+{
+ if (lightcache_baking_ == nullptr) {
+ lightcache_baking_ = new LightCache(lightcache_->cube_len,
+ lightcache_->grid_len,
+ lightcache_->cube_tx.tex_size[0],
+ lightcache_->vis_res,
+ lightcache_->grid_tx.tex_size);
+
+ if (lightcache_baking_->flag != LIGHTCACHE_INVALID) {
+ LightCache &lcache_src = *lightcache_;
+ LightCache &lcache = *lightcache_baking_;
+ /* Copy cache structure. */
+ memcpy(lcache.cube_data, lcache_src.cube_data, lcache.cube_len * sizeof(*lcache.cube_data));
+ memcpy(lcache.grid_data, lcache_src.grid_data, lcache.grid_len * sizeof(*lcache.grid_data));
+
+ /* Make grids renderable. */
+ for (LightGridCache &grid : MutableSpan(lcache.grid_data, lcache.grid_len)) {
+ grid.is_ready = 1;
+ }
+ /* Avoid sampling further than mip 0. Mips > 0 being undefined. */
+ lcache.mips_len = 0;
+ lcache.flag |= LIGHTCACHE_NO_REFLECTION;
+
+ /* Init to black. */
+ uint data_cube = 0;
+ uchar data_grid[4] = {0, 0, 0, 0};
+ GPU_texture_clear(lcache.cube_tx.tex, GPU_DATA_10_11_11_REV, &data_cube);
+ GPU_texture_clear(lcache.grid_tx.tex, GPU_DATA_UBYTE, &data_grid);
+ }
+ }
+ return lightcache_baking_;
+}
+
+void LightProbeModule::bake(Depsgraph *depsgraph,
+ int type,
+ int index,
+ int bounce,
+ const float position[3],
+ const LightProbe *probe,
+ float visibility_range)
+{
+ rcti rect;
+ BLI_rcti_init(&rect, 0, 0, 1, 1);
+
+ /* Disable screenspace effects. */
+ SceneEEVEE &sce_eevee = DEG_get_evaluated_scene(depsgraph)->eevee;
+ sce_eevee.flag &= ~(SCE_EEVEE_GTAO_ENABLED | SCE_EEVEE_RAYTRACING_ENABLED);
+
+ inst_.init(ivec2(1), &rect, nullptr, depsgraph, probe);
+ inst_.sampling.reset();
+ inst_.render_sync();
+ inst_.sampling.step();
+
+ float near = (probe) ? probe->clipsta : 0.1f;
+ float far = (probe) ? probe->clipend : 1.0f;
+ float intensity = (probe) ? probe->intensity : 1.0f;
+
+ bool background_only = (probe == nullptr);
+ cubemap_prepare(position, near, far, background_only);
+
+ if (type == LIGHTPROBE_TYPE_CUBE && probe != nullptr) {
+ /* Reflections cubemaps are rendered after all irradiance bounces.
+ * Swap to get the final irradiance in lightcache_baking_. */
+ swap_irradiance_cache();
+ }
+
+ /* Render using the previous bounce to light the scene. */
+ lightcache_ = baking_cache_get();
+
+ cubemap_render();
+
+ /* Filter on the original cache. */
+ lightcache_ = reinterpret_cast<LightCache *>(sce_eevee.light_cache_data);
+
+ if (type == LIGHTPROBE_TYPE_CUBE) {
+ filter_glossy(index, intensity);
+ /* Swap back final irradiance to lightcache_. */
+ if (probe != nullptr) {
+ swap_irradiance_cache();
+ }
+ }
+ else {
+ filter_diffuse(index, intensity);
+ if (probe && bounce < 2) {
+ /* No need to filter visibility after 2nd bounce since both lightcache_ and
+ * lightcache_baking_ will have correct visibility grid. */
+ filter_visibility(index, probe->vis_blur, visibility_range);
+ }
+ }
+}
+
+/* Push world probe to first grid and cubemap slots. */
+void LightProbeModule::sync_world(const DRWView *view)
+{
+ BoundSphere view_bounds = DRW_view_frustum_bsphere_get(view);
+ /* Playing safe. The fake grid needs to be bigger than the frustum. */
+ view_bounds.radius = clamp_f(view_bounds.radius * 2.0, 0.0f, FLT_MAX);
+
+ CubemapData &cube = cube_data_[0];
+ GridData &grid = grid_data_[0];
+
+ scale_m4_fl(grid.local_mat, view_bounds.radius);
+ negate_v3_v3(grid.local_mat[3], view_bounds.center);
+ copy_m4_m4(cube.influence_mat, grid.local_mat);
+ copy_m4_m4(cube.parallax_mat, cube.influence_mat);
+
+ grid.resolution = ivec3(1);
+ grid.offset = 0;
+ grid.level_skip = 1;
+ grid.attenuation_bias = 0.001f;
+ grid.attenuation_scale = 1.0f;
+ grid.visibility_range = 1.0f;
+ grid.visibility_bleed = 0.001f;
+ grid.visibility_bias = 0.0f;
+ grid.increment_x = vec3(0.0f);
+ grid.increment_y = vec3(0.0f);
+ grid.increment_z = vec3(0.0f);
+ grid.corner = vec3(0.0f);
+
+ cube._parallax_type = CUBEMAP_SHAPE_SPHERE;
+ cube._layer = 0.0;
+}
+
+void LightProbeModule::sync_grid(const DRWView *UNUSED(view),
+ const LightGridCache &grid_cache,
+ int grid_index)
+{
+ /* Skip the world probe. */
+ if (grid_index == 0 || grid_cache.is_ready != 1) {
+ return;
+ }
+ GridData &grid = grid_data_[info_data_.grids.grid_count];
+ copy_m4_m4(grid.local_mat, grid_cache.mat);
+ grid.resolution = ivec3(grid_cache.resolution);
+ grid.offset = grid_cache.offset;
+ grid.level_skip = grid_cache.level_bias;
+ grid.attenuation_bias = grid_cache.attenuation_bias;
+ grid.attenuation_scale = grid_cache.attenuation_scale;
+ grid.visibility_range = grid_cache.visibility_range;
+ grid.visibility_bleed = grid_cache.visibility_bleed;
+ grid.visibility_bias = grid_cache.visibility_bias;
+ grid.increment_x = vec3(grid_cache.increment_x);
+ grid.increment_y = vec3(grid_cache.increment_y);
+ grid.increment_z = vec3(grid_cache.increment_z);
+ grid.corner = vec3(grid_cache.corner);
+
+ info_data_.grids.grid_count++;
+}
+
+void LightProbeModule::sync_cubemap(const DRWView *UNUSED(view),
+ const LightProbeCache &cube_cache,
+ int cube_index)
+{
+ /* Skip the world probe. */
+ if (cube_index == 0 || cube_cache.is_ready != 1) {
+ return;
+ }
+ CubemapData &cube = cube_data_[info_data_.cubes.cube_count];
+ copy_m4_m4(cube.parallax_mat, cube_cache.parallaxmat);
+ copy_m4_m4(cube.influence_mat, cube_cache.attenuationmat);
+ cube._attenuation_factor = cube_cache.attenuation_fac;
+ cube._attenuation_type = cube_cache.attenuation_type;
+ cube._parallax_type = cube_cache.parallax_type;
+ cube._layer = cube_index;
+ cube._world_position_x = cube_cache.position[0];
+ cube._world_position_y = cube_cache.position[1];
+ cube._world_position_z = cube_cache.position[2];
+
+ info_data_.cubes.cube_count++;
+}
+
+/* Only enables world light probe if extent is invalid (no culling possible). */
+void LightProbeModule::set_view(const DRWView *view, const ivec2 extent)
+{
+ if (lightcache_->flag & LIGHTCACHE_UPDATE_WORLD) {
+ /* Set before update to avoid infinite recursion. */
+ lightcache_->flag &= ~LIGHTCACHE_UPDATE_WORLD;
+ update_world_cache();
+ }
+
+ /* Only sync when setting the view. This way we can cull probes not in frustum. */
+ /* TODO(fclem) implement culling. But needs to fix display when not all probes are present. */
+ info_data_.grids.grid_count = 1;
+ info_data_.cubes.cube_count = 1;
+
+ sync_world(view);
+ /* Only world if extent is 0. */
+ if (extent.x > 0) {
+ for (auto i : IndexRange(lightcache_->grid_len)) {
+ sync_grid(view, lightcache_->grid_data[i], i);
+ }
+ for (auto i : IndexRange(lightcache_->cube_len)) {
+ sync_cubemap(view, lightcache_->cube_data[i], i);
+ }
+ }
+
+ info_data_.cubes.roughness_max_lod = lightcache_->mips_len;
+ inst_.lookdev.rotation_get(info_data_.cubes.lookdev_rotation);
+ inst_.lookdev.rotation_get(info_data_.grids.lookdev_rotation);
+
+ active_grid_tx_ = lightcache_->grid_tx.tex;
+ active_cube_tx_ = lightcache_->cube_tx.tex;
+
+ info_data_.push_update();
+ grid_data_.push_update();
+ cube_data_.push_update();
+}
+
+void LightProbeModule::draw_cache_display(void)
+{
+ /* Only draws something if enabled. */
+ DRW_draw_pass(display_ps_);
+}
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_lightprobe.hh b/source/blender/draw/engines/eevee/eevee_lightprobe.hh
new file mode 100644
index 00000000000..c13f7a75ca4
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_lightprobe.hh
@@ -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_ = Texture("CubemapDepth");
+ Texture cube_color_tx_ = Texture("CubemapColor");
+ LightProbeView probe_views_[6];
+
+ Framebuffer cube_downsample_fb_ = Framebuffer("cube_downsample");
+ Framebuffer filter_cube_fb_ = Framebuffer("filter_cube");
+ Framebuffer filter_grid_fb_ = Framebuffer("filter_grid");
+
+ std::array<DRWView *, 6> face_view_ = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
+
+ DRWPass *cube_downsample_ps_ = nullptr;
+ DRWPass *filter_glossy_ps_ = nullptr;
+ DRWPass *filter_diffuse_ps_ = nullptr;
+ DRWPass *filter_visibility_ps_ = nullptr;
+
+ DRWPass *display_ps_ = nullptr;
+
+ /** Input texture to downsample cube pass. */
+ GPUTexture *cube_downsample_input_tx_ = nullptr;
+ /** Copy of actual textures from the lightcache_. */
+ GPUTexture *active_grid_tx_ = nullptr;
+ GPUTexture *active_cube_tx_ = nullptr;
+ /** Constant values during baking. */
+ float glossy_clamp_ = 0.0;
+ float filter_quality_ = 0.0;
+
+ public:
+ LightProbeModule(Instance &inst)
+ : inst_(inst),
+ probe_views_{{inst, "posX_view", cubeface_mat[0], 0},
+ {inst, "negX_view", cubeface_mat[1], 1},
+ {inst, "posY_view", cubeface_mat[2], 2},
+ {inst, "negY_view", cubeface_mat[3], 3},
+ {inst, "posZ_view", cubeface_mat[4], 4},
+ {inst, "negZ_view", cubeface_mat[5], 5}}
+ {
+ }
+
+ ~LightProbeModule()
+ {
+ MEM_delete(lightcache_lookdev_);
+ MEM_delete(lightcache_baking_);
+ }
+
+ void init();
+
+ void begin_sync();
+ void end_sync();
+
+ void set_view(const DRWView *view, const ivec2 extent);
+
+ void set_world_dirty(void)
+ {
+ lightcache_->flag |= LIGHTCACHE_UPDATE_WORLD;
+ }
+
+ void swap_irradiance_cache(void)
+ {
+ if (lightcache_baking_ && lightcache_) {
+ SWAP(GPUTexture *, lightcache_baking_->grid_tx.tex, lightcache_->grid_tx.tex);
+ }
+ }
+
+ const GPUUniformBuf *grid_ubo_get() const
+ {
+ return grid_data_.ubo_get();
+ }
+ const GPUUniformBuf *cube_ubo_get() const
+ {
+ return cube_data_.ubo_get();
+ }
+ const GPUUniformBuf *info_ubo_get() const
+ {
+ return info_data_.ubo_get();
+ }
+ GPUTexture **grid_tx_ref_get()
+ {
+ return &active_grid_tx_;
+ }
+ GPUTexture **cube_tx_ref_get()
+ {
+ return &active_cube_tx_;
+ }
+
+ void bake(Depsgraph *depsgraph,
+ int type,
+ int index,
+ int bounce,
+ const float position[3],
+ const LightProbe *probe = nullptr,
+ float visibility_range = 0.0f);
+
+ void draw_cache_display(void);
+
+ private:
+ void update_world_cache();
+
+ void sync_world(const DRWView *view);
+ void sync_grid(const DRWView *view, const struct LightGridCache &grid_cache, int grid_index);
+ void sync_cubemap(const DRWView *view, const struct LightProbeCache &cube_cache, int cube_index);
+
+ LightCache *baking_cache_get(void);
+
+ void cubemap_prepare(vec3 position, float near, float far, bool background_only);
+
+ void filter_glossy(int cube_index, float intensity);
+ void filter_diffuse(int sample_index, float intensity);
+ void filter_visibility(int sample_index, float visibility_blur, float visibility_range);
+
+ float lod_bias_from_cubemap(void)
+ {
+ float target_size_sq = square_f(GPU_texture_width(cube_color_tx_));
+ return 0.5f * logf(target_size_sq / filter_data_.sample_count) / logf(2);
+ }
+
+ static void cube_downsample_cb(void *thunk, int UNUSED(level))
+ {
+ DRW_draw_pass(reinterpret_cast<LightProbeModule *>(thunk)->cube_downsample_ps_);
+ }
+
+ void cubemap_render(void);
+};
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c
deleted file mode 100644
index c51fc18a406..00000000000
--- a/source/blender/draw/engines/eevee/eevee_lightprobes.c
+++ /dev/null
@@ -1,1265 +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 "BLI_rand.h"
-#include "BLI_string_utils.h"
-#include "BLI_utildefines.h"
-
-#include "DNA_image_types.h"
-#include "DNA_lightprobe_types.h"
-#include "DNA_texture_types.h"
-#include "DNA_view3d_types.h"
-#include "DNA_world_types.h"
-
-#include "BKE_collection.h"
-#include "BKE_object.h"
-#include "MEM_guardedalloc.h"
-
-#include "GPU_capabilities.h"
-#include "GPU_material.h"
-#include "GPU_texture.h"
-#include "GPU_uniform_buffer.h"
-
-#include "DEG_depsgraph_query.h"
-
-#include "eevee_lightcache.h"
-#include "eevee_private.h"
-
-#include "WM_api.h"
-#include "WM_types.h"
-
-static struct {
- struct GPUTexture *planar_pool_placeholder;
- struct GPUTexture *depth_placeholder;
- struct GPUTexture *depth_array_placeholder;
-
- struct GPUVertFormat *format_probe_display_planar;
-} e_data = {NULL}; /* Engine data */
-
-/* *********** FUNCTIONS *********** */
-
-/* TODO: find a better way than this. This does not support dupli objects if
- * the original object is hidden. */
-bool EEVEE_lightprobes_obj_visibility_cb(bool vis_in, void *user_data)
-{
- EEVEE_ObjectEngineData *oed = (EEVEE_ObjectEngineData *)user_data;
-
- /* test disabled if group is NULL */
- if (oed == NULL || oed->test_data->collection == NULL) {
- return vis_in;
- }
-
- if (oed->test_data->cached == false) {
- oed->ob_vis_dirty = true;
- }
-
- /* early out, don't need to compute ob_vis yet. */
- if (vis_in == false) {
- return vis_in;
- }
-
- if (oed->ob_vis_dirty) {
- oed->ob_vis_dirty = false;
- oed->ob_vis = BKE_collection_has_object_recursive(oed->test_data->collection, oed->ob);
- oed->ob_vis = (oed->test_data->invert) ? !oed->ob_vis : oed->ob_vis;
- }
-
- return vis_in && oed->ob_vis;
-}
-
-static void planar_pool_ensure_alloc(EEVEE_Data *vedata, int num_planar_ref)
-{
- EEVEE_TextureList *txl = vedata->txl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *fx = stl->effects;
-
- /* XXX TODO: OPTIMIZATION: This is a complete waist of texture memory.
- * Instead of allocating each planar probe for each viewport,
- * only alloc them once using the biggest viewport resolution. */
-
- /* TODO: get screen percentage from layer setting. */
- // const DRWContextState *draw_ctx = DRW_context_state_get();
- // ViewLayer *view_layer = draw_ctx->view_layer;
- int screen_divider = 1;
-
- int width = max_ii(1, fx->hiz_size[0] / screen_divider);
- int height = max_ii(1, fx->hiz_size[1] / screen_divider);
-
- /* Fix case were the pool was allocated width the dummy size (1,1,1). */
- if (txl->planar_pool && (num_planar_ref > 0) &&
- (GPU_texture_width(txl->planar_pool) != width ||
- GPU_texture_height(txl->planar_pool) != height)) {
- DRW_TEXTURE_FREE_SAFE(txl->planar_pool);
- DRW_TEXTURE_FREE_SAFE(txl->planar_depth);
- }
-
- /* We need an Array texture so allocate it ourself */
- if (!txl->planar_pool) {
- if (num_planar_ref > 0) {
- txl->planar_pool = DRW_texture_create_2d_array(width,
- height,
- num_planar_ref,
- GPU_R11F_G11F_B10F,
- DRW_TEX_FILTER | DRW_TEX_MIPMAP,
- NULL);
- txl->planar_depth = DRW_texture_create_2d_array(
- width, height, num_planar_ref, GPU_DEPTH_COMPONENT24, 0, NULL);
- }
- else if (num_planar_ref == 0) {
- /* Makes Opengl Happy : Create a placeholder texture that will never be sampled but still
- * bound to shader. */
- txl->planar_pool = DRW_texture_create_2d_array(
- 1, 1, 1, GPU_RGBA8, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL);
- txl->planar_depth = DRW_texture_create_2d_array(1, 1, 1, GPU_DEPTH_COMPONENT24, 0, NULL);
- }
- }
-}
-
-void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
-{
- EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
- EEVEE_StorageList *stl = vedata->stl;
-
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
- vedata->info[0] = '\0';
-
- EEVEE_shaders_material_shaders_init();
-
- memset(stl->g_data->bake_views, 0, sizeof(stl->g_data->bake_views));
- memset(stl->g_data->cube_views, 0, sizeof(stl->g_data->cube_views));
- memset(stl->g_data->world_views, 0, sizeof(stl->g_data->world_views));
- memset(stl->g_data->planar_views, 0, sizeof(stl->g_data->planar_views));
-
- if (EEVEE_lightcache_load(scene_eval->eevee.light_cache_data)) {
- stl->g_data->light_cache = scene_eval->eevee.light_cache_data;
- }
- else {
- if (scene_eval->eevee.light_cache_data &&
- (scene_eval->eevee.light_cache_data->flag & LIGHTCACHE_NOT_USABLE)) {
- /* Error message info. */
- BLI_snprintf(
- vedata->info, sizeof(vedata->info), "Error: LightCache cannot be loaded on this GPU");
- }
-
- if (!sldata->fallback_lightcache) {
-#if defined(IRRADIANCE_SH_L2)
- int grid_res = 4;
-#elif defined(IRRADIANCE_HL2)
- int grid_res = 4;
-#endif
- sldata->fallback_lightcache = EEVEE_lightcache_create(
- 1,
- 1,
- scene_eval->eevee.gi_cubemap_resolution,
- scene_eval->eevee.gi_visibility_resolution,
- (int[3]){grid_res, grid_res, 1});
- }
- stl->g_data->light_cache = sldata->fallback_lightcache;
- }
-
- if (!sldata->probes) {
- sldata->probes = MEM_callocN(sizeof(EEVEE_LightProbesInfo), "EEVEE_LightProbesInfo");
- sldata->probe_ubo = GPU_uniformbuf_create(sizeof(EEVEE_LightProbe) * MAX_PROBE);
- sldata->grid_ubo = GPU_uniformbuf_create(sizeof(EEVEE_LightGrid) * MAX_GRID);
- sldata->planar_ubo = GPU_uniformbuf_create(sizeof(EEVEE_PlanarReflection) * MAX_PLANAR);
- }
-
- common_data->prb_num_planar = 0;
- common_data->prb_num_render_cube = 1;
- common_data->prb_num_render_grid = 1;
-
- common_data->spec_toggle = true;
- common_data->ssr_toggle = true;
- common_data->ssrefract_toggle = true;
- common_data->sss_toggle = true;
-
- /* Placeholder planar pool: used when rendering planar reflections (avoid dependency loop). */
- if (!e_data.planar_pool_placeholder) {
- e_data.planar_pool_placeholder = DRW_texture_create_2d_array(
- 1, 1, 1, GPU_RGBA8, DRW_TEX_FILTER, NULL);
- }
-}
-
-void EEVEE_lightbake_cache_init(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- GPUTexture *rt_color,
- GPUTexture *rt_depth)
-{
- EEVEE_PassList *psl = vedata->psl;
- LightCache *light_cache = vedata->stl->g_data->light_cache;
- EEVEE_LightProbesInfo *pinfo = sldata->probes;
-
- {
- DRW_PASS_CREATE(psl->probe_glossy_compute, DRW_STATE_WRITE_COLOR);
-
- DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_filter_glossy_sh_get(),
- psl->probe_glossy_compute);
-
- DRW_shgroup_uniform_float(grp, "intensityFac", &pinfo->intensity_fac, 1);
- DRW_shgroup_uniform_float(grp, "sampleCount", &pinfo->samples_len, 1);
- DRW_shgroup_uniform_float(grp, "roughness", &pinfo->roughness, 1);
- DRW_shgroup_uniform_float(grp, "lodFactor", &pinfo->lodfactor, 1);
- DRW_shgroup_uniform_float(grp, "lodMax", &pinfo->lod_rt_max, 1);
- DRW_shgroup_uniform_float(grp, "texelSize", &pinfo->texel_size, 1);
- DRW_shgroup_uniform_float(grp, "paddingSize", &pinfo->padding_size, 1);
- DRW_shgroup_uniform_float(grp, "fireflyFactor", &pinfo->firefly_fac, 1);
- DRW_shgroup_uniform_int(grp, "Layer", &pinfo->layer, 1);
- // DRW_shgroup_uniform_texture(grp, "texJitter", e_data.jitter);
- DRW_shgroup_uniform_texture(grp, "probeHdr", rt_color);
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
-
- struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
- DRW_shgroup_call_instances(grp, NULL, geom, 6);
- }
-
- {
- DRW_PASS_CREATE(psl->probe_diffuse_compute, DRW_STATE_WRITE_COLOR);
- DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_filter_diffuse_sh_get(),
- psl->probe_diffuse_compute);
-#ifdef IRRADIANCE_SH_L2
- DRW_shgroup_uniform_int(grp, "probeSize", &pinfo->shres, 1);
-#else
- DRW_shgroup_uniform_float(grp, "sampleCount", &pinfo->samples_len, 1);
- DRW_shgroup_uniform_float(grp, "lodFactor", &pinfo->lodfactor, 1);
- DRW_shgroup_uniform_float(grp, "lodMax", &pinfo->lod_rt_max, 1);
-#endif
- DRW_shgroup_uniform_float(grp, "intensityFac", &pinfo->intensity_fac, 1);
- DRW_shgroup_uniform_texture(grp, "probeHdr", rt_color);
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
-
- struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
- DRW_shgroup_call(grp, geom, NULL);
- }
-
- {
- DRW_PASS_CREATE(psl->probe_visibility_compute, DRW_STATE_WRITE_COLOR);
- DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_filter_visibility_sh_get(),
- psl->probe_visibility_compute);
- DRW_shgroup_uniform_int(grp, "outputSize", &pinfo->shres, 1);
- DRW_shgroup_uniform_float(grp, "visibilityRange", &pinfo->visibility_range, 1);
- DRW_shgroup_uniform_float(grp, "visibilityBlur", &pinfo->visibility_blur, 1);
- DRW_shgroup_uniform_float(grp, "sampleCount", &pinfo->samples_len, 1);
- DRW_shgroup_uniform_float(grp, "storedTexelSize", &pinfo->texel_size, 1);
- DRW_shgroup_uniform_float(grp, "nearClip", &pinfo->near_clip, 1);
- DRW_shgroup_uniform_float(grp, "farClip", &pinfo->far_clip, 1);
- DRW_shgroup_uniform_texture(grp, "probeDepth", rt_depth);
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
-
- struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
- DRW_shgroup_call(grp, geom, NULL);
- }
-
- {
- DRW_PASS_CREATE(psl->probe_grid_fill, DRW_STATE_WRITE_COLOR);
-
- DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_grid_fill_sh_get(),
- psl->probe_grid_fill);
-
- DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &light_cache->grid_tx.tex);
-
- struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();
- DRW_shgroup_call(grp, geom, NULL);
- }
-}
-
-void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
-{
- EEVEE_TextureList *txl = vedata->txl;
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_LightProbesInfo *pinfo = sldata->probes;
- LightCache *lcache = stl->g_data->light_cache;
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
-
- pinfo->num_planar = 0;
- pinfo->vis_data.collection = NULL;
- pinfo->do_grid_update = false;
- pinfo->do_cube_update = false;
-
- {
- DRW_PASS_CREATE(psl->probe_background, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
-
- DRWShadingGroup *grp = NULL;
- EEVEE_lookdev_cache_init(vedata, sldata, psl->probe_background, pinfo, &grp);
-
- if (grp == NULL) {
- Scene *scene = draw_ctx->scene;
- World *world = (scene->world) ? scene->world : EEVEE_world_default_get();
-
- const int options = VAR_WORLD_BACKGROUND | VAR_WORLD_PROBE;
- struct GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, NULL, world, options);
-
- grp = DRW_shgroup_material_create(gpumat, psl->probe_background);
- DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f);
- }
-
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_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, "light_block", sldata->light_ubo);
- DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
- DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &stl->g_data->renderpass_ubo);
- DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
- }
-
- if (DRW_state_draw_support()) {
- DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL |
- DRW_STATE_CULL_BACK;
- DRW_PASS_CREATE(psl->probe_display, state);
-
- if (!LOOK_DEV_STUDIO_LIGHT_ENABLED(draw_ctx->v3d)) {
- /* Cube Display */
- if (scene_eval->eevee.flag & SCE_EEVEE_SHOW_CUBEMAPS && lcache->cube_len > 1) {
- int cube_len = lcache->cube_len - 1; /* don't count the world. */
- DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_cube_display_sh_get(),
- psl->probe_display);
-
- DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &lcache->cube_tx.tex);
- DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_vec3(grp, "screen_vecs", DRW_viewport_screenvecs_get(), 2);
- DRW_shgroup_uniform_float_copy(
- grp, "sphere_size", scene_eval->eevee.gi_cubemap_draw_size * 0.5f);
- /* TODO(fclem): get rid of those UBO. */
- DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
- DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
-
- DRW_shgroup_call_procedural_triangles(grp, NULL, cube_len * 2);
- }
-
- /* Grid Display */
- if (scene_eval->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE) {
- EEVEE_LightGrid *egrid = lcache->grid_data + 1;
- for (int p = 1; p < lcache->grid_len; p++, egrid++) {
- DRWShadingGroup *shgrp = DRW_shgroup_create(EEVEE_shaders_probe_grid_display_sh_get(),
- psl->probe_display);
-
- DRW_shgroup_uniform_int(shgrp, "offset", &egrid->offset, 1);
- DRW_shgroup_uniform_ivec3(shgrp, "grid_resolution", egrid->resolution, 1);
- DRW_shgroup_uniform_vec3(shgrp, "corner", egrid->corner, 1);
- DRW_shgroup_uniform_vec3(shgrp, "increment_x", egrid->increment_x, 1);
- DRW_shgroup_uniform_vec3(shgrp, "increment_y", egrid->increment_y, 1);
- DRW_shgroup_uniform_vec3(shgrp, "increment_z", egrid->increment_z, 1);
- DRW_shgroup_uniform_vec3(shgrp, "screen_vecs", DRW_viewport_screenvecs_get(), 2);
- DRW_shgroup_uniform_texture_ref(shgrp, "irradianceGrid", &lcache->grid_tx.tex);
- DRW_shgroup_uniform_float_copy(
- shgrp, "sphere_size", scene_eval->eevee.gi_irradiance_draw_size * 0.5f);
- /* TODO(fclem): get rid of those UBO. */
- DRW_shgroup_uniform_block(shgrp, "probe_block", sldata->probe_ubo);
- DRW_shgroup_uniform_block(shgrp, "planar_block", sldata->planar_ubo);
- DRW_shgroup_uniform_block(shgrp, "grid_block", sldata->grid_ubo);
- DRW_shgroup_uniform_block(shgrp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(shgrp, "renderpass_block", sldata->renderpass_ubo.combined);
- int tri_count = egrid->resolution[0] * egrid->resolution[1] * egrid->resolution[2] * 2;
- DRW_shgroup_call_procedural_triangles(shgrp, NULL, tri_count);
- }
- }
- }
-
- /* Planar Display */
- {
- DRW_shgroup_instance_format(e_data.format_probe_display_planar,
- {
- {"probe_id", DRW_ATTR_INT, 1},
- {"probe_mat", DRW_ATTR_FLOAT, 16},
- });
-
- DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_planar_display_sh_get(),
- psl->probe_display);
- DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &txl->planar_pool);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
-
- stl->g_data->planar_display_shgrp = DRW_shgroup_call_buffer_instance(
- grp, e_data.format_probe_display_planar, DRW_cache_quad_get());
- }
- }
- else {
- stl->g_data->planar_display_shgrp = NULL;
- }
-}
-
-static bool eevee_lightprobes_culling_test(Object *ob)
-{
- LightProbe *probe = (LightProbe *)ob->data;
-
- switch (probe->type) {
- case LIGHTPROBE_TYPE_PLANAR: {
- /* See if this planar probe is inside the view frustum. If not, no need to update it. */
- /* NOTE: this could be bypassed if we want feedback loop mirrors for rendering. */
- BoundBox bbox;
- float tmp[4][4];
- const float min[3] = {-1.0f, -1.0f, -1.0f};
- const float max[3] = {1.0f, 1.0f, 1.0f};
- BKE_boundbox_init_from_minmax(&bbox, min, max);
-
- copy_m4_m4(tmp, ob->obmat);
- normalize_v3(tmp[2]);
- mul_v3_fl(tmp[2], probe->distinf);
-
- for (int v = 0; v < 8; v++) {
- mul_m4_v3(tmp, bbox.vec[v]);
- }
- const DRWView *default_view = DRW_view_default_get();
- return DRW_culling_box_test(default_view, &bbox);
- }
- case LIGHTPROBE_TYPE_CUBE:
- return true; /* TODO */
- case LIGHTPROBE_TYPE_GRID:
- return true; /* TODO */
- }
- BLI_assert(0);
- return true;
-}
-
-void EEVEE_lightprobes_cache_add(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *ob)
-{
- EEVEE_LightProbesInfo *pinfo = sldata->probes;
- LightProbe *probe = (LightProbe *)ob->data;
-
- if ((probe->type == LIGHTPROBE_TYPE_CUBE && pinfo->num_cube >= EEVEE_PROBE_MAX) ||
- (probe->type == LIGHTPROBE_TYPE_GRID && pinfo->num_grid >= EEVEE_PROBE_MAX) ||
- (probe->type == LIGHTPROBE_TYPE_PLANAR && pinfo->num_planar >= MAX_PLANAR)) {
- printf("Too many probes in the view !!!\n");
- return;
- }
-
- if (probe->type == LIGHTPROBE_TYPE_PLANAR) {
- /* TODO(fclem): Culling should be done after cache generation.
- * This is needed for future draw cache persistence. */
- if (!eevee_lightprobes_culling_test(ob)) {
- return; /* Culled */
- }
- EEVEE_lightprobes_planar_data_from_object(
- ob, &pinfo->planar_data[pinfo->num_planar], &pinfo->planar_vis_tests[pinfo->num_planar]);
- /* Debug Display */
- DRWCallBuffer *grp = vedata->stl->g_data->planar_display_shgrp;
- if (grp && (probe->flag & LIGHTPROBE_FLAG_SHOW_DATA)) {
- DRW_buffer_add_entry(grp, &pinfo->num_planar, ob->obmat);
- }
-
- pinfo->num_planar++;
- }
- else {
- EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_ensure(ob);
- if (ped->need_update) {
- if (probe->type == LIGHTPROBE_TYPE_GRID) {
- pinfo->do_grid_update = true;
- }
- else {
- pinfo->do_cube_update = true;
- }
- ped->need_update = false;
- }
- }
-}
-
-void EEVEE_lightprobes_grid_data_from_object(Object *ob, EEVEE_LightGrid *egrid, int *offset)
-{
- LightProbe *probe = (LightProbe *)ob->data;
-
- copy_v3_v3_int(egrid->resolution, &probe->grid_resolution_x);
-
- /* Save current offset and advance it for the next grid. */
- egrid->offset = *offset;
- *offset += egrid->resolution[0] * egrid->resolution[1] * egrid->resolution[2];
-
- /* Add one for level 0 */
- float fac = 1.0f / max_ff(1e-8f, probe->falloff);
- egrid->attenuation_scale = fac / max_ff(1e-8f, probe->distinf);
- egrid->attenuation_bias = fac;
-
- /* Update transforms */
- float cell_dim[3], half_cell_dim[3];
- cell_dim[0] = 2.0f / egrid->resolution[0];
- cell_dim[1] = 2.0f / egrid->resolution[1];
- cell_dim[2] = 2.0f / egrid->resolution[2];
-
- mul_v3_v3fl(half_cell_dim, cell_dim, 0.5f);
-
- /* Matrix converting world space to cell ranges. */
- invert_m4_m4(egrid->mat, ob->obmat);
-
- /* First cell. */
- copy_v3_fl(egrid->corner, -1.0f);
- add_v3_v3(egrid->corner, half_cell_dim);
- mul_m4_v3(ob->obmat, egrid->corner);
-
- /* Opposite neighbor cell. */
- copy_v3_fl3(egrid->increment_x, cell_dim[0], 0.0f, 0.0f);
- add_v3_v3(egrid->increment_x, half_cell_dim);
- add_v3_fl(egrid->increment_x, -1.0f);
- mul_m4_v3(ob->obmat, egrid->increment_x);
- sub_v3_v3(egrid->increment_x, egrid->corner);
-
- copy_v3_fl3(egrid->increment_y, 0.0f, cell_dim[1], 0.0f);
- add_v3_v3(egrid->increment_y, half_cell_dim);
- add_v3_fl(egrid->increment_y, -1.0f);
- mul_m4_v3(ob->obmat, egrid->increment_y);
- sub_v3_v3(egrid->increment_y, egrid->corner);
-
- copy_v3_fl3(egrid->increment_z, 0.0f, 0.0f, cell_dim[2]);
- add_v3_v3(egrid->increment_z, half_cell_dim);
- add_v3_fl(egrid->increment_z, -1.0f);
- mul_m4_v3(ob->obmat, egrid->increment_z);
- sub_v3_v3(egrid->increment_z, egrid->corner);
-
- /* Visibility bias */
- egrid->visibility_bias = 0.05f * probe->vis_bias;
- egrid->visibility_bleed = probe->vis_bleedbias;
- egrid->visibility_range = 1.0f + sqrtf(max_fff(len_squared_v3(egrid->increment_x),
- len_squared_v3(egrid->increment_y),
- len_squared_v3(egrid->increment_z)));
-}
-
-void EEVEE_lightprobes_cube_data_from_object(Object *ob, EEVEE_LightProbe *eprobe)
-{
- LightProbe *probe = (LightProbe *)ob->data;
-
- /* Update transforms */
- copy_v3_v3(eprobe->position, ob->obmat[3]);
-
- /* Attenuation */
- eprobe->attenuation_type = probe->attenuation_type;
- eprobe->attenuation_fac = 1.0f / max_ff(1e-8f, probe->falloff);
-
- unit_m4(eprobe->attenuationmat);
- scale_m4_fl(eprobe->attenuationmat, probe->distinf);
- mul_m4_m4m4(eprobe->attenuationmat, ob->obmat, eprobe->attenuationmat);
- invert_m4(eprobe->attenuationmat);
-
- /* Parallax */
- unit_m4(eprobe->parallaxmat);
-
- if ((probe->flag & LIGHTPROBE_FLAG_CUSTOM_PARALLAX) != 0) {
- eprobe->parallax_type = probe->parallax_type;
- scale_m4_fl(eprobe->parallaxmat, probe->distpar);
- }
- else {
- eprobe->parallax_type = probe->attenuation_type;
- scale_m4_fl(eprobe->parallaxmat, probe->distinf);
- }
-
- mul_m4_m4m4(eprobe->parallaxmat, ob->obmat, eprobe->parallaxmat);
- invert_m4(eprobe->parallaxmat);
-}
-
-void EEVEE_lightprobes_planar_data_from_object(Object *ob,
- EEVEE_PlanarReflection *eplanar,
- EEVEE_LightProbeVisTest *vis_test)
-{
- LightProbe *probe = (LightProbe *)ob->data;
- float normat[4][4], imat[4][4];
-
- vis_test->collection = probe->visibility_grp;
- vis_test->invert = probe->flag & LIGHTPROBE_FLAG_INVERT_GROUP;
- vis_test->cached = false;
-
- /* Computing mtx : matrix that mirror position around object's XY plane. */
- normalize_m4_m4(normat, ob->obmat); /* object > world */
- invert_m4_m4(imat, normat); /* world > object */
- /* XY reflection plane */
- imat[0][2] = -imat[0][2];
- imat[1][2] = -imat[1][2];
- imat[2][2] = -imat[2][2];
- imat[3][2] = -imat[3][2]; /* world > object > mirrored obj */
- mul_m4_m4m4(eplanar->mtx, normat, imat); /* world > object > mirrored obj > world */
-
- /* Compute clip plane equation / normal. */
- copy_v3_v3(eplanar->plane_equation, ob->obmat[2]);
- normalize_v3(eplanar->plane_equation); /* plane normal */
- eplanar->plane_equation[3] = -dot_v3v3(eplanar->plane_equation, ob->obmat[3]);
- eplanar->clipsta = probe->clipsta;
-
- /* Compute XY clip planes. */
- normalize_v3_v3(eplanar->clip_vec_x, ob->obmat[0]);
- normalize_v3_v3(eplanar->clip_vec_y, ob->obmat[1]);
-
- float vec[3] = {0.0f, 0.0f, 0.0f};
- vec[0] = 1.0f;
- vec[1] = 0.0f;
- vec[2] = 0.0f;
- mul_m4_v3(ob->obmat, vec); /* Point on the edge */
- eplanar->clip_edge_x_pos = dot_v3v3(eplanar->clip_vec_x, vec);
-
- vec[0] = 0.0f;
- vec[1] = 1.0f;
- vec[2] = 0.0f;
- mul_m4_v3(ob->obmat, vec); /* Point on the edge */
- eplanar->clip_edge_y_pos = dot_v3v3(eplanar->clip_vec_y, vec);
-
- vec[0] = -1.0f;
- vec[1] = 0.0f;
- vec[2] = 0.0f;
- mul_m4_v3(ob->obmat, vec); /* Point on the edge */
- eplanar->clip_edge_x_neg = dot_v3v3(eplanar->clip_vec_x, vec);
-
- vec[0] = 0.0f;
- vec[1] = -1.0f;
- vec[2] = 0.0f;
- mul_m4_v3(ob->obmat, vec); /* Point on the edge */
- eplanar->clip_edge_y_neg = dot_v3v3(eplanar->clip_vec_y, vec);
-
- /* Facing factors */
- float max_angle = max_ff(1e-2f, 1.0f - probe->falloff) * M_PI * 0.5f;
- float min_angle = 0.0f;
- eplanar->facing_scale = 1.0f / max_ff(1e-8f, cosf(min_angle) - cosf(max_angle));
- eplanar->facing_bias = -min_ff(1.0f - 1e-8f, cosf(max_angle)) * eplanar->facing_scale;
-
- /* Distance factors */
- float max_dist = probe->distinf;
- float min_dist = min_ff(1.0f - 1e-8f, 1.0f - probe->falloff) * probe->distinf;
- eplanar->attenuation_scale = -1.0f / max_ff(1e-8f, max_dist - min_dist);
- eplanar->attenuation_bias = max_dist * -eplanar->attenuation_scale;
-}
-
-static void lightbake_planar_ensure_view(EEVEE_PlanarReflection *eplanar,
- const DRWView *main_view,
- DRWView **r_planar_view)
-{
- float winmat[4][4], viewmat[4][4], persmat[4][4];
- DRW_view_viewmat_get(main_view, viewmat, false);
- /* Temporal sampling jitter should be already applied to the DRW_MAT_WIN. */
- DRW_view_winmat_get(main_view, winmat, false);
- DRW_view_persmat_get(main_view, persmat, false);
-
- /* Invert X to avoid flipping the triangle facing direction. */
- winmat[0][0] = -winmat[0][0];
- winmat[1][0] = -winmat[1][0];
- winmat[2][0] = -winmat[2][0];
- winmat[3][0] = -winmat[3][0];
- /* Reflect Camera Matrix. */
- mul_m4_m4m4(viewmat, viewmat, eplanar->mtx);
-
- if (*r_planar_view == NULL) {
- *r_planar_view = DRW_view_create(
- viewmat, winmat, NULL, NULL, EEVEE_lightprobes_obj_visibility_cb);
- /* Compute offset plane equation (fix missing texels near reflection plane). */
- float clip_plane[4];
- copy_v4_v4(clip_plane, eplanar->plane_equation);
- clip_plane[3] += eplanar->clipsta;
- /* Set clipping plane */
- DRW_view_clip_planes_set(*r_planar_view, &clip_plane, 1);
- }
- else {
- DRW_view_update(*r_planar_view, viewmat, winmat, NULL, NULL);
- }
-}
-
-static void eevee_lightprobes_extract_from_cache(EEVEE_LightProbesInfo *pinfo, LightCache *lcache)
-{
- /* copy the entire cache for now (up to MAX_PROBE) */
- /* TODO: frustum cull to only add visible probes. */
- memcpy(pinfo->probe_data,
- lcache->cube_data,
- sizeof(EEVEE_LightProbe) * max_ii(1, min_ii(lcache->cube_len, MAX_PROBE)));
- /* TODO: compute the max number of grid based on sample count. */
- memcpy(pinfo->grid_data,
- lcache->grid_data,
- sizeof(EEVEE_LightGrid) * max_ii(1, min_ii(lcache->grid_len, MAX_GRID)));
-}
-
-void EEVEE_lightprobes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
-{
- EEVEE_StorageList *stl = vedata->stl;
- LightCache *light_cache = stl->g_data->light_cache;
- EEVEE_LightProbesInfo *pinfo = sldata->probes;
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
-
- eevee_lightprobes_extract_from_cache(sldata->probes, light_cache);
-
- GPU_uniformbuf_update(sldata->probe_ubo, &sldata->probes->probe_data);
- GPU_uniformbuf_update(sldata->grid_ubo, &sldata->probes->grid_data);
-
- /* For shading, save max level of the octahedron map */
- sldata->common_data.prb_lod_cube_max = (float)light_cache->mips_len;
- sldata->common_data.prb_irradiance_vis_size = light_cache->vis_res;
- sldata->common_data.prb_irradiance_smooth = square_f(scene_eval->eevee.gi_irradiance_smoothing);
- sldata->common_data.prb_num_render_cube = max_ii(1, light_cache->cube_len);
- sldata->common_data.prb_num_render_grid = max_ii(1, light_cache->grid_len);
- sldata->common_data.prb_num_planar = pinfo->num_planar;
-
- if (pinfo->num_planar != pinfo->cache_num_planar) {
- DRW_TEXTURE_FREE_SAFE(vedata->txl->planar_pool);
- DRW_TEXTURE_FREE_SAFE(vedata->txl->planar_depth);
- pinfo->cache_num_planar = pinfo->num_planar;
- }
- planar_pool_ensure_alloc(vedata, pinfo->num_planar);
-
- /* If light-cache auto-update is enable we tag the relevant part
- * of the cache to update and fire up a baking job. */
- if (!DRW_state_is_image_render() && !DRW_state_is_opengl_render() &&
- (pinfo->do_grid_update || pinfo->do_cube_update)) {
- BLI_assert(draw_ctx->evil_C);
-
- if (draw_ctx->scene->eevee.flag & SCE_EEVEE_GI_AUTOBAKE) {
- Scene *scene_orig = DEG_get_input_scene(draw_ctx->depsgraph);
- if (scene_orig->eevee.light_cache_data != NULL) {
- if (pinfo->do_grid_update) {
- scene_orig->eevee.light_cache_data->flag |= LIGHTCACHE_UPDATE_GRID;
- }
- /* If we update grid we need to update the cube-maps too.
- * So always refresh cube-maps. */
- scene_orig->eevee.light_cache_data->flag |= LIGHTCACHE_UPDATE_CUBE;
- /* Tag the lightcache to auto update. */
- scene_orig->eevee.light_cache_data->flag |= LIGHTCACHE_UPDATE_AUTO;
- /* Use a notifier to trigger the operator after drawing. */
- WM_event_add_notifier(draw_ctx->evil_C, NC_LIGHTPROBE, scene_orig);
- }
- }
- }
-
- if (pinfo->num_planar > 0) {
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_TextureList *txl = vedata->txl;
- DRW_PASS_CREATE(psl->probe_planar_downsample_ps, DRW_STATE_WRITE_COLOR);
-
- DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_planar_downsample_sh_get(),
- psl->probe_planar_downsample_ps);
-
- DRW_shgroup_uniform_texture_ref(grp, "source", &txl->planar_pool);
- DRW_shgroup_uniform_float(grp, "fireflyFactor", &sldata->common_data.ssr_firefly_fac, 1);
- DRW_shgroup_call_procedural_triangles(grp, NULL, pinfo->num_planar);
- }
-}
-
-/* -------------------------------------------------------------------- */
-/** \name Rendering
- * \{ */
-
-typedef struct EEVEE_BakeRenderData {
- EEVEE_Data *vedata;
- EEVEE_ViewLayerData *sldata;
- struct GPUFrameBuffer **face_fb; /* should contain 6 framebuffer */
-} EEVEE_BakeRenderData;
-
-static void render_cubemap(void (*callback)(int face, EEVEE_BakeRenderData *user_data),
- EEVEE_BakeRenderData *user_data,
- const float pos[3],
- float near,
- float far,
- bool do_culling)
-{
- EEVEE_StorageList *stl = user_data->vedata->stl;
- DRWView **views = do_culling ? stl->g_data->bake_views : stl->g_data->world_views;
-
- float winmat[4][4], viewmat[4][4];
- perspective_m4(winmat, -near, near, -near, near, near, far);
-
- /* Prepare views at the same time for faster culling. */
- for (int i = 0; i < 6; i++) {
- unit_m4(viewmat);
- negate_v3_v3(viewmat[3], pos);
- mul_m4_m4m4(viewmat, cubefacemat[i], viewmat);
-
- if (do_culling) {
- if (views[i] == NULL) {
- views[i] = DRW_view_create(viewmat, winmat, NULL, NULL, NULL);
- }
- else {
- DRW_view_update(views[i], viewmat, winmat, NULL, NULL);
- }
- }
- else {
- if (views[i] == NULL) {
- const DRWView *default_view = DRW_view_default_get();
- views[i] = DRW_view_create_sub(default_view, viewmat, winmat);
- }
- else {
- DRW_view_update_sub(views[i], viewmat, winmat);
- }
- }
- }
-
- for (int i = 0; i < 6; i++) {
- DRW_view_set_active(views[i]);
- callback(i, user_data);
- }
-}
-
-static void render_reflections(void (*callback)(int face, EEVEE_BakeRenderData *user_data),
- EEVEE_BakeRenderData *user_data,
- EEVEE_PlanarReflection *planar_data,
- int ref_count)
-{
- EEVEE_StorageList *stl = user_data->vedata->stl;
- DRWView *main_view = stl->effects->taa_view;
- DRWView **views = stl->g_data->planar_views;
- /* Prepare views at the same time for faster culling. */
- for (int i = 0; i < ref_count; i++) {
- lightbake_planar_ensure_view(&planar_data[i], main_view, &views[i]);
- }
-
- for (int i = 0; i < ref_count; i++) {
- DRW_view_set_active(views[i]);
- callback(i, user_data);
- }
-}
-
-static void lightbake_render_world_face(int face, EEVEE_BakeRenderData *user_data)
-{
- EEVEE_PassList *psl = user_data->vedata->psl;
- struct GPUFrameBuffer **face_fb = user_data->face_fb;
-
- /* For world probe, we don't need to clear the color buffer
- * since we render the background directly. */
- GPU_framebuffer_bind(face_fb[face]);
- GPU_framebuffer_clear_depth(face_fb[face], 1.0f);
- DRW_draw_pass(psl->probe_background);
-}
-
-void EEVEE_lightbake_render_world(EEVEE_ViewLayerData *UNUSED(sldata),
- EEVEE_Data *vedata,
- struct GPUFrameBuffer *face_fb[6])
-{
- EEVEE_BakeRenderData brdata = {
- .vedata = vedata,
- .face_fb = face_fb,
- };
-
- render_cubemap(lightbake_render_world_face, &brdata, (float[3]){0.0f}, 1.0f, 10.0f, false);
-}
-
-static void lightbake_render_scene_face(int face, EEVEE_BakeRenderData *user_data)
-{
- EEVEE_ViewLayerData *sldata = user_data->sldata;
- EEVEE_PassList *psl = user_data->vedata->psl;
- EEVEE_PrivateData *g_data = user_data->vedata->stl->g_data;
- DRWView **views = g_data->bake_views;
-
- struct GPUFrameBuffer **face_fb = user_data->face_fb;
-
- /* Be sure that cascaded shadow maps are updated. */
- EEVEE_shadows_draw(sldata, user_data->vedata, views[face]);
-
- GPU_framebuffer_bind(face_fb[face]);
- GPU_framebuffer_clear_depth(face_fb[face], 1.0f);
-
- DRW_draw_pass(psl->depth_ps);
- DRW_draw_pass(psl->probe_background);
- DRW_draw_pass(psl->material_ps);
- DRW_draw_pass(psl->material_sss_ps); /* Only output standard pass */
- DRW_draw_pass(psl->transparent_pass);
-}
-
-void EEVEE_lightbake_render_scene(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- struct GPUFrameBuffer *face_fb[6],
- const float pos[3],
- float near_clip,
- float far_clip)
-{
- EEVEE_BakeRenderData brdata = {
- .vedata = vedata,
- .sldata = sldata,
- .face_fb = face_fb,
- };
-
- render_cubemap(lightbake_render_scene_face, &brdata, pos, near_clip, far_clip, true);
-}
-
-static void lightbake_render_scene_reflected(int layer, EEVEE_BakeRenderData *user_data)
-{
- EEVEE_Data *vedata = user_data->vedata;
- EEVEE_ViewLayerData *sldata = user_data->sldata;
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_TextureList *txl = vedata->txl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_FramebufferList *fbl = vedata->fbl;
- EEVEE_LightProbesInfo *pinfo = sldata->probes;
-
- GPU_framebuffer_ensure_config(&fbl->planarref_fb,
- {GPU_ATTACHMENT_TEXTURE_LAYER(txl->planar_depth, layer),
- GPU_ATTACHMENT_TEXTURE_LAYER(txl->planar_pool, layer)});
-
- /* Use visibility info for this planar reflection. */
- pinfo->vis_data = pinfo->planar_vis_tests[layer];
-
- /* Avoid using the texture attached to framebuffer when rendering. */
- /* XXX */
- GPUTexture *tmp_planar_pool = txl->planar_pool;
- GPUTexture *tmp_planar_depth = txl->planar_depth;
- txl->planar_pool = e_data.planar_pool_placeholder;
- txl->planar_depth = e_data.depth_array_placeholder;
-
- DRW_stats_group_start("Planar Reflection");
-
- /* Be sure that cascaded shadow maps are updated. */
- EEVEE_shadows_draw(sldata, vedata, stl->g_data->planar_views[layer]);
-
- GPU_framebuffer_bind(fbl->planarref_fb);
- GPU_framebuffer_clear_depth(fbl->planarref_fb, 1.0);
-
- float prev_background_alpha = vedata->stl->g_data->background_alpha;
- vedata->stl->g_data->background_alpha = 1.0f;
-
- /* Slight modification: we handle refraction as normal
- * shading and don't do SSRefraction. */
-
- DRW_draw_pass(psl->depth_clip_ps);
- DRW_draw_pass(psl->depth_refract_clip_ps);
-
- DRW_draw_pass(psl->probe_background);
- EEVEE_create_minmax_buffer(vedata, tmp_planar_depth, layer);
- EEVEE_occlusion_compute(sldata, vedata);
-
- GPU_framebuffer_bind(fbl->planarref_fb);
-
- /* Shading pass */
- DRW_draw_pass(psl->material_ps);
- DRW_draw_pass(psl->material_sss_ps); /* Only output standard pass */
- DRW_draw_pass(psl->material_refract_ps);
-
- /* Transparent */
- if (DRW_state_is_image_render()) {
- /* Do the reordering only for offline because it can be costly. */
- DRW_pass_sort_shgroup_z(psl->transparent_pass);
- }
- DRW_draw_pass(psl->transparent_pass);
-
- DRW_stats_group_end();
-
- /* Restore */
- txl->planar_pool = tmp_planar_pool;
- txl->planar_depth = tmp_planar_depth;
-
- vedata->stl->g_data->background_alpha = prev_background_alpha;
-}
-
-static void eevee_lightbake_render_scene_to_planars(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata)
-{
- EEVEE_BakeRenderData brdata = {
- .vedata = vedata,
- .sldata = sldata,
- };
-
- render_reflections(lightbake_render_scene_reflected,
- &brdata,
- sldata->probes->planar_data,
- sldata->probes->num_planar);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Filtering
- * \{ */
-
-void EEVEE_lightbake_filter_glossy(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- struct GPUTexture *rt_color,
- struct GPUFrameBuffer *fb,
- int probe_idx,
- float intensity,
- int maxlevel,
- float filter_quality,
- float firefly_fac)
-{
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_LightProbesInfo *pinfo = sldata->probes;
- LightCache *light_cache = vedata->stl->g_data->light_cache;
-
- float target_size = (float)GPU_texture_width(rt_color);
-
- /* Max lod used from the render target probe */
- pinfo->lod_rt_max = log2_floor_u(target_size) - 2.0f;
- pinfo->intensity_fac = intensity;
-
- /* Start fresh */
- GPU_framebuffer_ensure_config(&fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_NONE});
-
- /* 2 - Let gpu create Mipmaps for Filtered Importance Sampling. */
- /* Bind next framebuffer to be able to gen. mips for probe_rt. */
- EEVEE_downsample_cube_buffer(vedata, rt_color, (int)(pinfo->lod_rt_max));
-
- /* 3 - Render to probe array to the specified layer, do prefiltering. */
- int mipsize = GPU_texture_width(light_cache->cube_tx.tex);
- for (int i = 0; i < maxlevel + 1; i++) {
- float bias = 0.0f;
- pinfo->texel_size = 1.0f / (float)mipsize;
- pinfo->padding_size = (i == maxlevel) ? 0 : (float)(1 << (maxlevel - i - 1));
- pinfo->padding_size *= pinfo->texel_size;
- pinfo->layer = probe_idx * 6;
- pinfo->roughness = i / (float)maxlevel;
- /* Disney Roughness */
- pinfo->roughness = square_f(pinfo->roughness);
- /* Distribute Roughness across lod more evenly */
- pinfo->roughness = square_f(pinfo->roughness);
- CLAMP(pinfo->roughness, 1e-4f, 0.9999f); /* Avoid artifacts */
-
-#if 1 /* Variable Sample count and bias (fast) */
- switch (i) {
- case 0:
- pinfo->samples_len = 1.0f;
- bias = -1.0f;
- break;
- case 1:
- pinfo->samples_len = 32.0f;
- bias = 1.0f;
- break;
- case 2:
- pinfo->samples_len = 40.0f;
- bias = 2.0f;
- break;
- case 3:
- pinfo->samples_len = 64.0f;
- bias = 2.0f;
- break;
- default:
- pinfo->samples_len = 128.0f;
- bias = 2.0f;
- break;
- }
-#else /* Constant Sample count (slow) */
- pinfo->samples_len = 1024.0f;
-#endif
- /* Cannot go higher than HAMMERSLEY_SIZE */
- CLAMP(filter_quality, 1.0f, 8.0f);
- pinfo->samples_len *= filter_quality;
-
- pinfo->lodfactor = bias + 0.5f * log(square_f(target_size) / pinfo->samples_len) / log(2);
- pinfo->firefly_fac = (firefly_fac > 0.0) ? firefly_fac : 1e16;
-
- GPU_framebuffer_ensure_config(&fb,
- {
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE_MIP(light_cache->cube_tx.tex, i),
- });
- GPU_framebuffer_bind(fb);
- DRW_draw_pass(psl->probe_glossy_compute);
-
- mipsize /= 2;
- CLAMP_MIN(mipsize, 1);
- }
-}
-
-void EEVEE_lightbake_filter_diffuse(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- struct GPUTexture *rt_color,
- struct GPUFrameBuffer *fb,
- int grid_offset,
- float intensity)
-{
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_LightProbesInfo *pinfo = sldata->probes;
- LightCache *light_cache = vedata->stl->g_data->light_cache;
-
- float target_size = (float)GPU_texture_width(rt_color);
-
- pinfo->intensity_fac = intensity;
-
- /* find cell position on the virtual 3D texture */
- /* NOTE : Keep in sync with load_irradiance_cell() */
-#if defined(IRRADIANCE_SH_L2)
- int size[2] = {3, 3};
-#elif defined(IRRADIANCE_HL2)
- const int size[2] = {3, 2};
- pinfo->samples_len = 1024.0f;
-#endif
-
- int cell_per_row = GPU_texture_width(light_cache->grid_tx.tex) / size[0];
- int x = size[0] * (grid_offset % cell_per_row);
- int y = size[1] * (grid_offset / cell_per_row);
-
-#ifndef IRRADIANCE_SH_L2
- /* Tweaking parameters to balance perf. vs precision */
- const float bias = 0.0f;
- pinfo->lodfactor = bias + 0.5f * log(square_f(target_size) / pinfo->samples_len) / log(2);
- pinfo->lod_rt_max = log2_floor_u(target_size) - 2.0f;
-#else
- pinfo->shres = 32; /* Less texture fetches & reduce branches */
- pinfo->lod_rt_max = 2.0f; /* Improve cache reuse */
-#endif
-
- /* Start fresh */
- GPU_framebuffer_ensure_config(&fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_NONE});
-
- /* 4 - Compute diffuse irradiance */
- EEVEE_downsample_cube_buffer(vedata, rt_color, (int)(pinfo->lod_rt_max));
-
- GPU_framebuffer_ensure_config(
- &fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_LAYER(light_cache->grid_tx.tex, 0)});
- GPU_framebuffer_bind(fb);
- GPU_framebuffer_viewport_set(fb, x, y, size[0], size[1]);
- DRW_draw_pass(psl->probe_diffuse_compute);
- GPU_framebuffer_viewport_reset(fb);
-}
-
-void EEVEE_lightbake_filter_visibility(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- struct GPUTexture *UNUSED(rt_depth),
- struct GPUFrameBuffer *fb,
- int grid_offset,
- float clipsta,
- float clipend,
- float vis_range,
- float vis_blur,
- int vis_size)
-{
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_LightProbesInfo *pinfo = sldata->probes;
- LightCache *light_cache = vedata->stl->g_data->light_cache;
-
- pinfo->samples_len = 512.0f; /* TODO: refine. */
- pinfo->shres = vis_size;
- pinfo->visibility_range = vis_range;
- pinfo->visibility_blur = vis_blur;
- pinfo->near_clip = -clipsta;
- pinfo->far_clip = -clipend;
- pinfo->texel_size = 1.0f / (float)vis_size;
-
- int cell_per_col = GPU_texture_height(light_cache->grid_tx.tex) / vis_size;
- int cell_per_row = GPU_texture_width(light_cache->grid_tx.tex) / vis_size;
- int x = vis_size * (grid_offset % cell_per_row);
- int y = vis_size * ((grid_offset / cell_per_row) % cell_per_col);
- int layer = 1 + ((grid_offset / cell_per_row) / cell_per_col);
-
- GPU_framebuffer_ensure_config(
- &fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_LAYER(light_cache->grid_tx.tex, layer)});
- GPU_framebuffer_bind(fb);
- GPU_framebuffer_viewport_set(fb, x, y, vis_size, vis_size);
- DRW_draw_pass(psl->probe_visibility_compute);
- GPU_framebuffer_viewport_reset(fb);
-}
-
-/* Actually a simple down-sampling. */
-static void downsample_planar(void *vedata, int level)
-{
- EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
- EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
-
- const float *size = DRW_viewport_size_get();
- copy_v2_v2(stl->g_data->planar_texel_size, size);
- for (int i = 0; i < level - 1; i++) {
- stl->g_data->planar_texel_size[0] /= 2.0f;
- stl->g_data->planar_texel_size[1] /= 2.0f;
- min_ff(floorf(stl->g_data->planar_texel_size[0]), 1.0f);
- min_ff(floorf(stl->g_data->planar_texel_size[1]), 1.0f);
- }
- invert_v2(stl->g_data->planar_texel_size);
-
- DRW_draw_pass(psl->probe_planar_downsample_ps);
-}
-
-static void EEVEE_lightbake_filter_planar(EEVEE_Data *vedata)
-{
- EEVEE_TextureList *txl = vedata->txl;
- EEVEE_FramebufferList *fbl = vedata->fbl;
-
- DRW_stats_group_start("Planar Probe Downsample");
-
- GPU_framebuffer_ensure_config(&fbl->planar_downsample_fb,
- {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->planar_pool)});
-
- GPU_framebuffer_recursive_downsample(
- fbl->planar_downsample_fb, MAX_SCREEN_BUFFERS_LOD_LEVEL, &downsample_planar, vedata);
- DRW_stats_group_end();
-}
-
-/** \} */
-
-void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
-{
- EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
- EEVEE_LightProbesInfo *pinfo = sldata->probes;
-
- if (pinfo->num_planar == 0) {
- /* Disable SSR if we cannot read previous frame */
- common_data->ssr_toggle = vedata->stl->g_data->valid_double_buffer;
- common_data->prb_num_planar = 0;
- return;
- }
-
- float hiz_uv_scale_prev[2] = {UNPACK2(common_data->hiz_uv_scale)};
-
- /* Temporary Remove all planar reflections (avoid lag effect). */
- common_data->prb_num_planar = 0;
- /* Turn off ssr to avoid black specular */
- common_data->ssr_toggle = false;
- common_data->ssrefract_toggle = false;
- common_data->sss_toggle = false;
-
- common_data->ray_type = EEVEE_RAY_GLOSSY;
- common_data->ray_depth = 1.0f;
- /* Planar reflections are rendered at the `hiz` resolution, so no need to scaling. */
- copy_v2_fl(common_data->hiz_uv_scale, 1.0f);
-
- GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
-
- /* Rendering happens here! */
- eevee_lightbake_render_scene_to_planars(sldata, vedata);
-
- /* Make sure no additional visibility check runs after this. */
- pinfo->vis_data.collection = NULL;
-
- GPU_uniformbuf_update(sldata->planar_ubo, &sldata->probes->planar_data);
-
- /* Restore */
- common_data->prb_num_planar = pinfo->num_planar;
- common_data->ssr_toggle = true;
- common_data->ssrefract_toggle = true;
- common_data->sss_toggle = true;
- copy_v2_v2(common_data->hiz_uv_scale, hiz_uv_scale_prev);
-
- /* Prefilter for SSR */
- if ((vedata->stl->effects->enabled_effects & EFFECT_SSR) != 0) {
- EEVEE_lightbake_filter_planar(vedata);
- }
-
- if (DRW_state_is_image_render()) {
- /* Sort the transparent passes because planar reflections could have re-sorted them. */
- DRW_pass_sort_shgroup_z(vedata->psl->transparent_pass);
- }
-
- /* Disable SSR if we cannot read previous frame */
- common_data->ssr_toggle = vedata->stl->g_data->valid_double_buffer;
-}
-
-void EEVEE_lightprobes_refresh(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
-{
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
- LightCache *light_cache = vedata->stl->g_data->light_cache;
-
- if ((light_cache->flag & LIGHTCACHE_UPDATE_WORLD) &&
- (light_cache->flag & LIGHTCACHE_BAKED) == 0) {
- EEVEE_lightbake_update_world_quick(sldata, vedata, scene_eval);
- }
-}
-
-void EEVEE_lightprobes_free(void)
-{
- MEM_SAFE_FREE(e_data.format_probe_display_planar);
- DRW_TEXTURE_FREE_SAFE(e_data.planar_pool_placeholder);
- DRW_TEXTURE_FREE_SAFE(e_data.depth_placeholder);
- DRW_TEXTURE_FREE_SAFE(e_data.depth_array_placeholder);
-}
diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c
deleted file mode 100644
index 4ed968e2935..00000000000
--- a/source/blender/draw/engines/eevee/eevee_lights.c
+++ /dev/null
@@ -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);
-}
diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c
deleted file mode 100644
index 879a7b08eba..00000000000
--- a/source/blender/draw/engines/eevee/eevee_lookdev.c
+++ /dev/null
@@ -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);
- }
-}
diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.cc b/source/blender/draw/engines/eevee/eevee_lookdev.cc
new file mode 100644
index 00000000000..881ab90343f
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_lookdev.cc
@@ -0,0 +1,360 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2018, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ */
+
+#include "BKE_image.h"
+#include "BKE_lib_id.h"
+#include "BKE_node.h"
+#include "BKE_studiolight.h"
+#include "BKE_world.h"
+#include "BLI_math_matrix.h"
+#include "BLI_rect.h"
+#include "DNA_userdef_types.h"
+#include "ED_screen.h"
+#include "NOD_shader.h"
+
+#include "eevee_instance.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name Lookdev Nodetree
+ *
+ * \{ */
+
+LookDevWorldNodeTree::LookDevWorldNodeTree()
+{
+ bNodeTree *ntree = ntreeAddTree(NULL, "Lookdev Nodetree", ntreeType_Shader->idname);
+ bNode *background = nodeAddStaticNode(NULL, ntree, SH_NODE_BACKGROUND);
+ bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_WORLD);
+ bNodeSocket *background_out = nodeFindSocket(background, SOCK_OUT, "Background");
+ bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface");
+ nodeAddLink(ntree, background, background_out, output, output_in);
+ nodeSetActive(ntree, output);
+
+ /* Note that we do not populate the environment texture input.
+ * We plug the GPUTexture directly using the sampler binding name ("samp1"). */
+ bNode *environment = nodeAddStaticNode(NULL, ntree, SH_NODE_TEX_ENVIRONMENT);
+ bNodeSocket *background_in = nodeFindSocket(background, SOCK_IN, "Color");
+ bNodeSocket *environment_out = nodeFindSocket(environment, SOCK_OUT, "Color");
+ nodeAddLink(ntree, environment, environment_out, background, background_in);
+
+ strength_socket_ =
+ (bNodeSocketValueFloat *)nodeFindSocket(background, SOCK_IN, "Strength")->default_value;
+
+ ntree_ = ntree;
+}
+
+LookDevWorldNodeTree::~LookDevWorldNodeTree()
+{
+ ntreeFreeEmbeddedTree(ntree_);
+ MEM_SAFE_FREE(ntree_);
+}
+
+/* Configure a default nodetree with the given parameters. */
+bNodeTree *LookDevWorldNodeTree::nodetree_get(float strength)
+{
+ /* WARNING: This function is not threadsafe. Which is not a problem for the moment. */
+ strength_socket_->value = strength;
+ return ntree_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name LookDev Studiolight
+ *
+ * Light the scene using the studiolight hdri. Overrides the lightcache (if any) and
+ * use custom shader to draw the background.
+ * \{ */
+
+void LookDev::init(const ivec2 &output_res, const rcti *render_border)
+{
+ StudioLight *studiolight = nullptr;
+ if (inst_.v3d) {
+ studiolight = BKE_studiolight_find(inst_.v3d->shading.lookdev_light,
+ STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE);
+ }
+
+ if (inst_.use_studio_light() && studiolight && (studiolight->flag & STUDIOLIGHT_TYPE_WORLD)) {
+ const View3DShading &shading = inst_.v3d->shading;
+ studiolight_ = studiolight;
+
+ /* Detect update. */
+ if ((opacity_ != shading.studiolight_background) || (rotation_ != shading.studiolight_rot_z) ||
+ (instensity_ != shading.studiolight_intensity) || (blur_ != shading.studiolight_blur) ||
+ (view_rotation_ != ((shading.flag & V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION) != 0)) ||
+ (studiolight_index_ != studiolight_->index)) {
+ opacity_ = shading.studiolight_background;
+ instensity_ = shading.studiolight_intensity;
+ blur_ = shading.studiolight_blur;
+ rotation_ = shading.studiolight_rot_z;
+ studiolight_index_ = studiolight_->index;
+ view_rotation_ = (shading.flag & V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION) != 0;
+
+ inst_.sampling.reset();
+ inst_.lightprobes.set_world_dirty();
+
+ /* Update the material. */
+ GPU_material_free(&material);
+ }
+ }
+ else {
+ if (studiolight_ != nullptr) {
+ inst_.sampling.reset();
+ inst_.lightprobes.set_world_dirty();
+ }
+ studiolight_ = nullptr;
+ studiolight_index_ = -1;
+
+ GPU_material_free(&material);
+ }
+
+ if (do_overlay(output_res, render_border)) {
+ rcti rect;
+ if (DRW_state_is_opengl_render()) {
+ BLI_rcti_init(&rect, 0, output_res.x, 0, output_res.y);
+ }
+ else {
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ rect = *ED_region_visible_rect(draw_ctx->region);
+ }
+
+ /* Make the viewport width scale the lookdev spheres a bit.
+ * Scale between 1000px and 2000px. */
+ float viewport_scale = clamp_f(BLI_rcti_size_x(&rect) / (2000.0f * U.dpi_fac), 0.5f, 1.0f);
+ int sphere_size = U.lookdev_sphere_size * U.dpi_fac * viewport_scale;
+ ivec2 anchor = ivec2(rect.xmax, rect.ymin);
+
+ if (sphere_size != sphere_size_ || anchor != anchor_) {
+ /* Make sphere resolution adaptive to viewport_scale, dpi and lookdev_sphere_size */
+ float res_scale = (U.lookdev_sphere_size / 400.0f) * viewport_scale * U.dpi_fac;
+ if (res_scale > 0.7f) {
+ sphere_lod_ = DRW_LOD_HIGH;
+ }
+ else if (res_scale > 0.25f) {
+ sphere_lod_ = DRW_LOD_MEDIUM;
+ }
+ else {
+ sphere_lod_ = DRW_LOD_LOW;
+ }
+ sphere_size_ = sphere_size;
+ anchor_ = anchor;
+ inst_.sampling.reset();
+ }
+ }
+ else if (sphere_size_ != 0) {
+ sphere_size_ = 0;
+ inst_.sampling.reset();
+ }
+}
+
+bool LookDev::do_overlay(const ivec2 &output_res, const rcti *render_border)
+{
+ const View3D *v3d = inst_.v3d;
+ /* Only show the HDRI Preview in Shading Preview in the Viewport. */
+ if (v3d == nullptr || v3d->shading.type != OB_MATERIAL) {
+ return false;
+ }
+ /* Only show the HDRI Preview when viewing the Combined render pass */
+ if (v3d->shading.render_pass != SCE_PASS_COMBINED) {
+ return false;
+ }
+ if (v3d->flag2 & V3D_HIDE_OVERLAYS) {
+ return false;
+ }
+ if ((v3d->overlay.flag & V3D_OVERLAY_LOOK_DEV) == 0) {
+ return false;
+ }
+ if (inst_.camera.is_panoramic()) {
+ return false;
+ }
+ if (output_res != ivec2(BLI_rcti_size_x(render_border), BLI_rcti_size_y(render_border))) {
+ /* TODO(fclem) support this case. */
+ return false;
+ }
+ return true;
+}
+
+bool LookDev::sync_world(void)
+{
+ if (studiolight_ == nullptr) {
+ return false;
+ }
+ /* World light probes render. */
+ bNodeTree *nodetree = world_tree.nodetree_get(instensity_);
+ GPUMaterial *gpumat = inst_.shaders.material_shader_get(
+ "LookDev", material, nodetree, MAT_PIPE_FORWARD, MAT_GEOM_WORLD, true);
+
+ BKE_studiolight_ensure_flag(studiolight_, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE);
+ GPUTexture *gputex = studiolight_->equirect_radiance_gputexture;
+
+ if (gputex == nullptr) {
+ return false;
+ }
+ inst_.shading_passes.background.sync(gpumat, gputex);
+ return true;
+}
+
+void LookDev::rotation_get(mat4 r_mat)
+{
+ if (studiolight_ == nullptr) {
+ unit_m4(r_mat);
+ }
+ else {
+ axis_angle_to_mat4_single(r_mat, 'Z', rotation_);
+ }
+
+ if (view_rotation_) {
+ float x_rot_matrix[4][4];
+ const CameraData &cam = inst_.camera.data_get();
+ axis_angle_to_mat4_single(x_rot_matrix, 'X', M_PI / 2.0f);
+ mul_m4_m4m4(x_rot_matrix, x_rot_matrix, cam.viewmat);
+ mul_m4_m4m4(r_mat, r_mat, x_rot_matrix);
+ }
+}
+
+void LookDev::sync_background(void)
+{
+ if (studiolight_ == nullptr) {
+ return;
+ }
+ /* Viewport display. */
+ background_ps_ = DRW_pass_create("LookDev.Background", DRW_STATE_WRITE_COLOR);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(LOOKDEV_BACKGROUND);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, background_ps_);
+ DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", inst_.lightprobes.cube_tx_ref_get());
+ DRW_shgroup_uniform_block(grp, "lightprobes_info_block", inst_.lightprobes.info_ubo_get());
+ DRW_shgroup_uniform_float_copy(grp, "blur", clamp_f(blur_, 0.0f, 0.99999f));
+ DRW_shgroup_uniform_float_copy(grp, "opacity", opacity_);
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+}
+
+/* Renders background using lightcache. */
+bool LookDev::render_background(void)
+{
+ if (studiolight_ == nullptr) {
+ return false;
+ }
+ DRW_draw_pass(background_ps_);
+ return true;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name LookDev Reference spheres
+ *
+ * Render reference spheres into a separate framebuffer to not distrub the main rendering.
+ * The final texture is composited onto the render.
+ * \{ */
+
+void LookDev::sync_overlay(void)
+{
+ if (sphere_size_ == 0) {
+ return;
+ }
+
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS |
+ DRW_STATE_CULL_BACK;
+ overlay_ps_ = DRW_pass_create("LookDev.Overlay", state);
+
+ GPUBatch *sphere = DRW_cache_sphere_get(sphere_lod_);
+
+ const CameraData &cam = inst_.camera.data_get();
+ LightModule &lights = inst_.lights;
+ LightProbeModule &lightprobes = inst_.lightprobes;
+
+ /* Jitter for AA. */
+ vec2 jitter = -0.5f + vec2(inst_.sampling.rng_get(SAMPLING_FILTER_U),
+ inst_.sampling.rng_get(SAMPLING_FILTER_V));
+
+ /* Matrix used to position the spheres in viewport space. */
+ mat4 sphere_mat;
+ copy_m4_m4(sphere_mat, cam.viewmat);
+
+ const float *viewport_size = DRW_viewport_size_get();
+ const int sphere_margin = sphere_size_ / 6;
+ vec2 offset = vec2(0, sphere_margin);
+
+ std::array<::Material *, 2> materials = {inst_.materials.diffuse_mat_,
+ inst_.materials.glossy_mat_};
+ for (::Material *mat : materials) {
+ GPUMaterial *gpumat = inst_.shaders.material_shader_get(
+ mat, mat->nodetree, MAT_PIPE_FORWARD, MAT_GEOM_LOOKDEV, false);
+ DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, overlay_ps_);
+ lights.shgroup_resources(grp);
+ DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
+ DRW_shgroup_uniform_block(grp, "grids_block", lightprobes.grid_ubo_get());
+ DRW_shgroup_uniform_block(grp, "cubes_block", lightprobes.cube_ubo_get());
+ DRW_shgroup_uniform_block(grp, "lightprobes_info_block", lightprobes.info_ubo_get());
+ DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get());
+ DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
+
+ offset.x -= sphere_size_ + sphere_margin;
+
+ /* Pass 2D scale and bias factor in the last column. */
+ vec2 scale = sphere_size_ / vec2(viewport_size);
+ vec2 bias = -1.0f + scale + 2.0f * (vec2(anchor_) + offset + jitter) / vec2(viewport_size);
+ copy_v4_fl4(sphere_mat[3], UNPACK2(scale), UNPACK2(bias));
+ DRW_shgroup_call_obmat(grp, sphere, sphere_mat);
+
+ offset.x -= sphere_margin;
+ }
+
+ view_ = nullptr;
+}
+
+/* Renders the reference spheres. */
+void LookDev::render_overlay(GPUFrameBuffer *fb)
+{
+ if (sphere_size_ == 0) {
+ return;
+ }
+
+ const DRWView *active_view = DRW_view_get_active();
+
+ inst_.lightprobes.set_view(active_view, ivec2(0));
+ inst_.lights.set_view(active_view, ivec2(0));
+
+ /* Create subview for correct shading. Sub because we don not care about culling. */
+ const CameraData &cam = inst_.camera.data_get();
+ mat4 winmat;
+ orthographic_m4(winmat, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
+ if (view_) {
+ DRW_view_update_sub(view_, cam.viewmat, winmat);
+ }
+ else {
+ view_ = DRW_view_create_sub(active_view, cam.viewmat, winmat);
+ }
+
+ DRW_view_set_active(view_);
+
+ GPU_framebuffer_bind(fb);
+ DRW_draw_pass(overlay_ps_);
+
+ DRW_view_set_active(active_view);
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.hh b/source/blender/draw/engines/eevee/eevee_lookdev.hh
new file mode 100644
index 00000000000..092c33ed98c
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_lookdev.hh
@@ -0,0 +1,112 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2018, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ */
+
+#pragma once
+
+#include "BKE_studiolight.h"
+#include "DNA_world_types.h"
+
+#include "DRW_render.h"
+
+namespace blender::eevee {
+
+class Instance;
+
+/* -------------------------------------------------------------------- */
+/** \name Lookdev World Nodetree
+ *
+ * \{ */
+
+class LookDevWorldNodeTree {
+ private:
+ bNodeTree *ntree_;
+ bNodeSocketValueFloat *strength_socket_;
+
+ public:
+ LookDevWorldNodeTree();
+ ~LookDevWorldNodeTree();
+
+ bNodeTree *nodetree_get(float strength);
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Look Dev
+ *
+ * \{ */
+
+class LookDev {
+ private:
+ Instance &inst_;
+ /** Nodetree used to render the world reflection cubemap and irradiance. */
+ LookDevWorldNodeTree world_tree;
+ /** Compiled gpu material for the nodetree. Owned. */
+ ListBase material = {nullptr, nullptr};
+ /** Choosen studio light. */
+ StudioLight *studiolight_ = nullptr;
+ int studiolight_index_ = -1;
+ /** Draw pass to draw the viewport background. */
+ DRWPass *background_ps_ = nullptr;
+ /** Parameters. */
+ float instensity_ = -1.0f;
+ float blur_ = -1.0f;
+ float opacity_ = -1.0f;
+ float rotation_ = -9999.0f;
+ bool view_rotation_ = false;
+
+ /** Overlay (reference spheres). */
+ DRWPass *overlay_ps_ = nullptr;
+ /** View based on main view with orthographic projection. Without this, shading is incorrect. */
+ DRWView *view_ = nullptr;
+ /** Selected LOD of the sphere mesh. */
+ eDRWLevelOfDetail sphere_lod_;
+ /** Screen space radius in pixels. */
+ int sphere_size_ = 0;
+ /** Lower right corner of the area where we can start drawing. */
+ ivec2 anchor_;
+
+ public:
+ LookDev(Instance &inst) : inst_(inst){};
+ ~LookDev()
+ {
+ GPU_material_free(&material);
+ };
+
+ void init(const ivec2 &output_res, const rcti *render_border);
+
+ void sync_background(void);
+ bool sync_world(void);
+ void sync_overlay(void);
+
+ bool render_background(void);
+ void render_overlay(GPUFrameBuffer *view_fb);
+
+ void rotation_get(mat4 r_mat);
+
+ private:
+ bool do_overlay(const ivec2 &output_res, const rcti *render_border);
+};
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_lut.h b/source/blender/draw/engines/eevee/eevee_lut.h
index 8e107adfe0a..847a7376ecd 100644
--- a/source/blender/draw/engines/eevee/eevee_lut.h
+++ b/source/blender/draw/engines/eevee/eevee_lut.h
@@ -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
diff --git a/source/blender/draw/engines/eevee/eevee_lut_gen.c b/source/blender/draw/engines/eevee/eevee_lut_gen.c
deleted file mode 100644
index 770134d27f9..00000000000
--- a/source/blender/draw/engines/eevee/eevee_lut_gen.c
+++ /dev/null
@@ -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;
-}
diff --git a/source/blender/draw/engines/eevee/eevee_material.cc b/source/blender/draw/engines/eevee/eevee_material.cc
new file mode 100644
index 00000000000..a185616d191
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_material.cc
@@ -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 \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/eevee_material.hh b/source/blender/draw/engines/eevee/eevee_material.hh
new file mode 100644
index 00000000000..df681209ce5
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_material.hh
@@ -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 \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
deleted file mode 100644
index a027a29c813..00000000000
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ /dev/null
@@ -1,1142 +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 "BLI_alloca.h"
-#include "BLI_ghash.h"
-#include "BLI_listbase.h"
-#include "BLI_math_bits.h"
-#include "BLI_memblock.h"
-#include "BLI_rand.h"
-#include "BLI_string_utils.h"
-
-#include "BKE_paint.h"
-#include "BKE_particle.h"
-
-#include "DNA_hair_types.h"
-#include "DNA_modifier_types.h"
-#include "DNA_view3d_types.h"
-#include "DNA_world_types.h"
-
-#include "GPU_material.h"
-
-#include "DEG_depsgraph_query.h"
-
-#include "eevee_engine.h"
-#include "eevee_lut.h"
-#include "eevee_private.h"
-
-/* *********** STATIC *********** */
-static struct {
- /* 64*64 array texture containing all LUTs and other utilitarian arrays.
- * Packing enables us to same precious textures slots. */
- struct GPUTexture *util_tex;
- struct GPUTexture *noise_tex;
-
- float noise_offsets[3];
-} e_data = {NULL}; /* Engine data */
-
-typedef struct EeveeMaterialCache {
- struct DRWShadingGroup *depth_grp;
- struct DRWShadingGroup *shading_grp;
- struct DRWShadingGroup *shadow_grp;
- struct GPUMaterial *shading_gpumat;
- /* Meh, Used by hair to ensure draw order when calling DRW_shgroup_create_sub.
- * Pointers to ghash values. */
- struct DRWShadingGroup **depth_grp_p;
- struct DRWShadingGroup **shading_grp_p;
- struct DRWShadingGroup **shadow_grp_p;
-} EeveeMaterialCache;
-
-/* *********** FUNCTIONS *********** */
-
-/* XXX TODO: define all shared resources in a shared place without duplication. */
-struct GPUTexture *EEVEE_materials_get_util_tex(void)
-{
- return e_data.util_tex;
-}
-
-void EEVEE_material_bind_resources(DRWShadingGroup *shgrp,
- GPUMaterial *gpumat,
- EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- const int *ssr_id,
- const float *refract_depth,
- bool use_ssrefraction,
- bool use_alpha_blend)
-{
- bool use_diffuse = GPU_material_flag_get(gpumat, GPU_MATFLAG_DIFFUSE);
- bool use_glossy = GPU_material_flag_get(gpumat, GPU_MATFLAG_GLOSSY);
- bool use_refract = GPU_material_flag_get(gpumat, GPU_MATFLAG_REFRACT);
-
- LightCache *lcache = vedata->stl->g_data->light_cache;
- EEVEE_EffectsInfo *effects = vedata->stl->effects;
- EEVEE_PrivateData *pd = vedata->stl->g_data;
-
- DRW_shgroup_uniform_block(shgrp, "probe_block", sldata->probe_ubo);
- DRW_shgroup_uniform_block(shgrp, "grid_block", sldata->grid_ubo);
- DRW_shgroup_uniform_block(shgrp, "planar_block", sldata->planar_ubo);
- DRW_shgroup_uniform_block(shgrp, "light_block", sldata->light_ubo);
- DRW_shgroup_uniform_block(shgrp, "shadow_block", sldata->shadow_ubo);
- DRW_shgroup_uniform_block(shgrp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block_ref(shgrp, "renderpass_block", &pd->renderpass_ubo);
-
- DRW_shgroup_uniform_int_copy(shgrp, "outputSssId", 1);
- DRW_shgroup_uniform_texture(shgrp, "utilTex", e_data.util_tex);
- if (use_diffuse || use_glossy || use_refract) {
- DRW_shgroup_uniform_texture_ref(shgrp, "shadowCubeTexture", &sldata->shadow_cube_pool);
- DRW_shgroup_uniform_texture_ref(shgrp, "shadowCascadeTexture", &sldata->shadow_cascade_pool);
- DRW_shgroup_uniform_texture_ref(shgrp, "maxzBuffer", &vedata->txl->maxzbuffer);
- }
- if ((use_diffuse || use_glossy) && !use_ssrefraction) {
- DRW_shgroup_uniform_texture_ref(shgrp, "horizonBuffer", &effects->gtao_horizons);
- }
- if (use_diffuse) {
- DRW_shgroup_uniform_texture_ref(shgrp, "irradianceGrid", &lcache->grid_tx.tex);
- }
- if (use_glossy || use_refract) {
- DRW_shgroup_uniform_texture_ref(shgrp, "probeCubes", &lcache->cube_tx.tex);
- }
- if (use_glossy) {
- DRW_shgroup_uniform_texture_ref(shgrp, "probePlanars", &vedata->txl->planar_pool);
- DRW_shgroup_uniform_int_copy(shgrp, "outputSsrId", ssr_id ? *ssr_id : 0);
- }
- if (use_refract) {
- DRW_shgroup_uniform_float_copy(
- shgrp, "refractionDepth", (refract_depth) ? *refract_depth : 0.0);
- if (use_ssrefraction) {
- DRW_shgroup_uniform_texture_ref(
- shgrp, "refractColorBuffer", &vedata->txl->filtered_radiance);
- }
- }
- if (use_alpha_blend) {
- DRW_shgroup_uniform_texture_ref(shgrp, "inScattering", &effects->volume_scatter);
- DRW_shgroup_uniform_texture_ref(shgrp, "inTransmittance", &effects->volume_transmit);
- }
-}
-
-static void eevee_init_noise_texture(void)
-{
- e_data.noise_tex = DRW_texture_create_2d(64, 64, GPU_RGBA16F, 0, (float *)blue_noise);
-}
-
-#define RUNTIME_LUT_CREATION 0
-
-static void eevee_init_util_texture(void)
-{
- const int layers = 4 + 16;
- float(*texels)[4] = MEM_mallocN(sizeof(float[4]) * 64 * 64 * layers, "utils texels");
- float(*texels_layer)[4] = texels;
-#if RUNTIME_LUT_CREATION
- float *bsdf_ggx_lut = EEVEE_lut_update_ggx_brdf(64);
- float(*btdf_ggx_lut)[64 * 64 * 2] = (float(*)[64 * 64 * 2]) EEVEE_lut_update_ggx_btdf(64, 16);
-#else
- const float *bsdf_ggx_lut = bsdf_split_sum_ggx;
- const float(*btdf_ggx_lut)[64 * 64 * 2] = btdf_split_sum_ggx;
-#endif
-
- /* Copy ltc_mat_ggx into 1st layer */
- memcpy(texels_layer, ltc_mat_ggx, sizeof(float[4]) * 64 * 64);
- texels_layer += 64 * 64;
-
- /* Copy bsdf_split_sum_ggx into 2nd layer red and green channels.
- * Copy ltc_mag_ggx into 2nd layer blue and alpha channel. */
- for (int i = 0; i < 64 * 64; i++) {
- texels_layer[i][0] = bsdf_ggx_lut[i * 2 + 0];
- texels_layer[i][1] = bsdf_ggx_lut[i * 2 + 1];
- texels_layer[i][2] = ltc_mag_ggx[i * 2 + 0];
- texels_layer[i][3] = ltc_mag_ggx[i * 2 + 1];
- }
- texels_layer += 64 * 64;
-
- /* Copy blue noise in 3rd layer. */
- for (int i = 0; i < 64 * 64; i++) {
- texels_layer[i][0] = blue_noise[i][0];
- texels_layer[i][1] = blue_noise[i][2];
- texels_layer[i][2] = cosf(blue_noise[i][1] * 2.0f * M_PI);
- texels_layer[i][3] = sinf(blue_noise[i][1] * 2.0f * M_PI);
- }
- texels_layer += 64 * 64;
-
- /* Copy ltc_disk_integral in 4th layer. */
- for (int i = 0; i < 64 * 64; i++) {
- texels_layer[i][0] = ltc_disk_integral[i];
- texels_layer[i][1] = 0.0; /* UNUSED */
- texels_layer[i][2] = 0.0; /* UNUSED */
- texels_layer[i][3] = 0.0; /* UNUSED */
- }
- texels_layer += 64 * 64;
-
- /* Copy Refraction GGX LUT in layer 5 - 21 */
- for (int j = 0; j < 16; j++) {
- for (int i = 0; i < 64 * 64; i++) {
- texels_layer[i][0] = btdf_ggx_lut[j][i * 2 + 0];
- texels_layer[i][1] = btdf_ggx_lut[j][i * 2 + 1];
- texels_layer[i][2] = 0.0; /* UNUSED */
- texels_layer[i][3] = 0.0; /* UNUSED */
- }
- texels_layer += 64 * 64;
- }
-
- e_data.util_tex = DRW_texture_create_2d_array(
- 64, 64, layers, GPU_RGBA16F, DRW_TEX_FILTER | DRW_TEX_WRAP, (float *)texels);
-
- MEM_freeN(texels);
-#if RUNTIME_LUT_CREATION
- MEM_freeN(bsdf_ggx_lut);
- MEM_freeN(btdf_ggx_lut);
-#endif
-}
-
-void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const double offsets[3])
-{
- e_data.noise_offsets[0] = offsets[0];
- e_data.noise_offsets[1] = offsets[1];
- e_data.noise_offsets[2] = offsets[2];
-
- GPU_framebuffer_bind(fbl->update_noise_fb);
- DRW_draw_pass(psl->update_noise_pass);
-}
-
-void EEVEE_materials_init(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- EEVEE_StorageList *stl,
- EEVEE_FramebufferList *fbl)
-{
- const DRWContextState *draw_ctx = DRW_context_state_get();
- EEVEE_PrivateData *g_data = stl->g_data;
-
- if (!e_data.util_tex) {
- EEVEE_shaders_material_shaders_init();
-
- eevee_init_util_texture();
- eevee_init_noise_texture();
- }
-
- if (!DRW_state_is_image_render() && ((stl->effects->enabled_effects & EFFECT_TAA) == 0)) {
- sldata->common_data.alpha_hash_offset = 0.0f;
- sldata->common_data.alpha_hash_scale = 1.0f;
- }
- else {
- double r;
- BLI_halton_1d(5, 0.0, stl->effects->taa_current_sample - 1, &r);
- sldata->common_data.alpha_hash_offset = (float)r;
- sldata->common_data.alpha_hash_scale = 0.01f;
- }
-
- {
- /* Update noise Frame-buffer. */
- GPU_framebuffer_ensure_config(
- &fbl->update_noise_fb,
- {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_LAYER(e_data.util_tex, 2)});
- }
-
- {
- /* Create RenderPass UBO */
- if (sldata->renderpass_ubo.combined == NULL) {
- EEVEE_RenderPassData data;
- data = (EEVEE_RenderPassData){true, true, true, true, true, false, false, false, 0};
- sldata->renderpass_ubo.combined = GPU_uniformbuf_create_ex(
- sizeof(data), &data, "renderpass_ubo.combined");
-
- data = (EEVEE_RenderPassData){true, false, false, false, false, true, false, false, 0};
- sldata->renderpass_ubo.diff_color = GPU_uniformbuf_create_ex(
- sizeof(data), &data, "renderpass_ubo.diff_color");
-
- data = (EEVEE_RenderPassData){true, true, false, false, false, false, false, false, 0};
- sldata->renderpass_ubo.diff_light = GPU_uniformbuf_create_ex(
- sizeof(data), &data, "renderpass_ubo.diff_light");
-
- data = (EEVEE_RenderPassData){false, false, true, false, false, false, false, false, 0};
- sldata->renderpass_ubo.spec_color = GPU_uniformbuf_create_ex(
- sizeof(data), &data, "renderpass_ubo.spec_color");
-
- data = (EEVEE_RenderPassData){false, false, true, true, false, false, false, false, 0};
- sldata->renderpass_ubo.spec_light = GPU_uniformbuf_create_ex(
- sizeof(data), &data, "renderpass_ubo.spec_light");
-
- data = (EEVEE_RenderPassData){false, false, false, false, true, false, false, false, 0};
- sldata->renderpass_ubo.emit = GPU_uniformbuf_create_ex(
- sizeof(data), &data, "renderpass_ubo.emit");
-
- data = (EEVEE_RenderPassData){true, true, true, true, true, false, true, false, 0};
- sldata->renderpass_ubo.environment = GPU_uniformbuf_create_ex(
- sizeof(data), &data, "renderpass_ubo.environment");
- }
-
- /* Used combined pass by default. */
- g_data->renderpass_ubo = sldata->renderpass_ubo.combined;
-
- {
- g_data->num_aovs_used = 0;
- if ((stl->g_data->render_passes & EEVEE_RENDER_PASS_AOV) != 0) {
- EEVEE_RenderPassData data = {true, true, true, true, true, false, false, true, 0};
- if (stl->g_data->aov_hash == EEVEE_AOV_HASH_ALL) {
- 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;
- }
- if (aov_index == MAX_AOVS) {
- break;
- }
- data.renderPassAOVActive = EEVEE_renderpasses_aov_hash(aov);
- if (sldata->renderpass_ubo.aovs[aov_index]) {
- GPU_uniformbuf_update(sldata->renderpass_ubo.aovs[aov_index], &data);
- }
- else {
- sldata->renderpass_ubo.aovs[aov_index] = GPU_uniformbuf_create_ex(
- sizeof(data), &data, "renderpass_ubo.aovs");
- }
- aov_index++;
- }
- g_data->num_aovs_used = aov_index;
- }
- else {
- /* Rendering a single AOV in the 3d viewport */
- data.renderPassAOVActive = stl->g_data->aov_hash;
- if (sldata->renderpass_ubo.aovs[0]) {
- GPU_uniformbuf_update(sldata->renderpass_ubo.aovs[0], &data);
- }
- else {
- sldata->renderpass_ubo.aovs[0] = GPU_uniformbuf_create_ex(
- sizeof(data), &data, "renderpass_ubo.aovs");
- }
- g_data->num_aovs_used = 1;
- }
- }
- /* Free AOV UBO's that are not in use. */
- for (int aov_index = g_data->num_aovs_used; aov_index < MAX_AOVS; aov_index++) {
- DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.aovs[aov_index]);
- }
- }
-
- /* HACK: EEVEE_material_get can create a new context. This can only be
- * done when there is no active framebuffer. We do this here otherwise
- * `EEVEE_renderpasses_output_init` will fail. It cannot be done in
- * `EEVEE_renderpasses_init` as the `e_data.vertcode` can be uninitialized.
- */
- if (g_data->render_passes & EEVEE_RENDER_PASS_ENVIRONMENT) {
- struct Scene *scene = draw_ctx->scene;
- struct World *wo = scene->world;
- if (wo && wo->use_nodes) {
- EEVEE_material_get(vedata, scene, NULL, wo, VAR_WORLD_BACKGROUND);
- }
- }
- }
-}
-
-void EEVEE_materials_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
-{
- EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
- EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
- const DRWContextState *draw_ctx = DRW_context_state_get();
-
- /* Create Material #GHash. */
- {
- stl->g_data->material_hash = BLI_ghash_ptr_new("Eevee_material ghash");
-
- if (sldata->material_cache == NULL) {
- sldata->material_cache = BLI_memblock_create(sizeof(EeveeMaterialCache));
- }
- else {
- BLI_memblock_clear(sldata->material_cache, NULL);
- }
- }
-
- {
- DRW_PASS_CREATE(psl->background_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
-
- DRWShadingGroup *grp = NULL;
- EEVEE_lookdev_cache_init(vedata, sldata, psl->background_ps, NULL, &grp);
-
- if (grp == NULL) {
- Scene *scene = draw_ctx->scene;
- World *world = (scene->world) ? scene->world : EEVEE_world_default_get();
-
- const int options = VAR_WORLD_BACKGROUND;
- struct GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, NULL, world, options);
-
- grp = DRW_shgroup_material_create(gpumat, psl->background_ps);
- DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1);
- }
-
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_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, "light_block", sldata->light_ubo);
- DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
- DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &stl->g_data->renderpass_ubo);
- DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
- }
-
-#define EEVEE_PASS_CREATE(pass, state) \
- do { \
- DRW_PASS_CREATE(psl->pass##_ps, state); \
- DRW_PASS_CREATE(psl->pass##_cull_ps, state | DRW_STATE_CULL_BACK); \
- DRW_pass_link(psl->pass##_ps, psl->pass##_cull_ps); \
- } while (0)
-
-#define EEVEE_CLIP_PASS_CREATE(pass, state) \
- do { \
- DRWState st = state | DRW_STATE_CLIP_PLANES; \
- DRW_PASS_INSTANCE_CREATE(psl->pass##_clip_ps, psl->pass##_ps, st); \
- DRW_PASS_INSTANCE_CREATE( \
- psl->pass##_clip_cull_ps, psl->pass##_cull_ps, st | DRW_STATE_CULL_BACK); \
- DRW_pass_link(psl->pass##_clip_ps, psl->pass##_clip_cull_ps); \
- } while (0)
-
- {
- DRWState state_depth = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
- DRWState state_shading = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_CLIP_PLANES;
- DRWState state_sss = DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_ALWAYS;
-
- EEVEE_PASS_CREATE(depth, state_depth);
- EEVEE_CLIP_PASS_CREATE(depth, state_depth);
-
- EEVEE_PASS_CREATE(depth_refract, state_depth);
- EEVEE_CLIP_PASS_CREATE(depth_refract, state_depth);
-
- EEVEE_PASS_CREATE(material, state_shading);
- EEVEE_PASS_CREATE(material_refract, state_shading);
- EEVEE_PASS_CREATE(material_sss, state_shading | state_sss);
- }
- {
- /* Renderpass accumulation. */
- DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ADD_FULL;
- /* Create an instance of each of these passes and link them together. */
- DRWPass *passes[] = {
- psl->material_ps,
- psl->material_cull_ps,
- psl->material_sss_ps,
- psl->material_sss_cull_ps,
- };
- DRWPass *first = NULL, *last = NULL;
- for (int i = 0; i < ARRAY_SIZE(passes); i++) {
- DRWPass *pass = DRW_pass_create_instance("Renderpass Accumulation", passes[i], state);
- if (first == NULL) {
- first = last = pass;
- }
- else {
- DRW_pass_link(last, pass);
- last = pass;
- }
- }
- psl->material_accum_ps = first;
-
- /* Same for background */
- DRW_PASS_INSTANCE_CREATE(psl->background_accum_ps, psl->background_ps, state);
- }
- {
- DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CLIP_PLANES;
- DRW_PASS_CREATE(psl->transparent_pass, state);
- }
- {
- DRW_PASS_CREATE(psl->update_noise_pass, DRW_STATE_WRITE_COLOR);
- GPUShader *sh = EEVEE_shaders_update_noise_sh_get();
- DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->update_noise_pass);
- DRW_shgroup_uniform_texture(grp, "blueNoise", e_data.noise_tex);
- DRW_shgroup_uniform_vec3(grp, "offsets", e_data.noise_offsets, 1);
- DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
- }
-}
-
-BLI_INLINE void material_shadow(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Material *ma,
- bool is_hair,
- EeveeMaterialCache *emc)
-{
- EEVEE_PrivateData *pd = vedata->stl->g_data;
- EEVEE_PassList *psl = vedata->psl;
- const DRWContextState *draw_ctx = DRW_context_state_get();
- Scene *scene = draw_ctx->scene;
-
- if (ma->blend_shadow != MA_BS_NONE) {
- /* Shadow Pass */
- const bool use_shadow_shader = ma->use_nodes && ma->nodetree &&
- ELEM(ma->blend_shadow, MA_BS_CLIP, MA_BS_HASHED);
- int mat_options = VAR_MAT_MESH | VAR_MAT_DEPTH;
- SET_FLAG_FROM_TEST(mat_options, use_shadow_shader, VAR_MAT_HASH);
- SET_FLAG_FROM_TEST(mat_options, is_hair, VAR_MAT_HAIR);
- GPUMaterial *gpumat = (use_shadow_shader) ?
- EEVEE_material_get(vedata, scene, ma, NULL, mat_options) :
- EEVEE_material_default_get(scene, ma, mat_options);
-
- /* Avoid possible confusion with depth pre-pass options. */
- int option = KEY_SHADOW;
- SET_FLAG_FROM_TEST(option, is_hair, KEY_HAIR);
-
- /* Search for the same shaders usage in the pass. */
- struct GPUShader *sh = GPU_material_get_shader(gpumat);
- void *cache_key = (char *)sh + option;
- DRWShadingGroup *grp, **grp_p;
-
- if (BLI_ghash_ensure_p(pd->material_hash, cache_key, (void ***)&grp_p)) {
- /* This GPUShader has already been used by another material.
- * Add new shading group just after to avoid shader switching cost. */
- grp = DRW_shgroup_create_sub(*grp_p);
- }
- else {
- *grp_p = grp = DRW_shgroup_create(sh, psl->shadow_pass);
- EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false);
- }
-
- DRW_shgroup_add_material_resources(grp, gpumat);
-
- emc->shadow_grp = grp;
- emc->shadow_grp_p = grp_p;
- }
- else {
- emc->shadow_grp = NULL;
- emc->shadow_grp_p = NULL;
- }
-}
-
-static EeveeMaterialCache material_opaque(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Material *ma,
- const bool is_hair)
-{
- EEVEE_EffectsInfo *effects = vedata->stl->effects;
- EEVEE_PrivateData *pd = vedata->stl->g_data;
- EEVEE_PassList *psl = vedata->psl;
- const DRWContextState *draw_ctx = DRW_context_state_get();
- Scene *scene = draw_ctx->scene;
-
- const bool do_cull = !is_hair && (ma->blend_flag & MA_BL_CULL_BACKFACE) != 0;
- const bool use_gpumat = (ma->use_nodes && ma->nodetree);
- const bool use_ssrefract = use_gpumat && ((ma->blend_flag & MA_BL_SS_REFRACTION) != 0) &&
- ((effects->enabled_effects & EFFECT_REFRACT) != 0);
- const bool use_depth_shader = use_gpumat && ELEM(ma->blend_method, MA_BM_CLIP, MA_BM_HASHED);
-
- /* HACK: Assume the struct will never be smaller than our variations.
- * This allow us to only keep one ghash and avoid bigger keys comparisons/hashing. */
- void *key = (char *)ma + is_hair;
- /* Search for other material instances (sharing the same Material data-block). */
- EeveeMaterialCache **emc_p, *emc;
- if (BLI_ghash_ensure_p(pd->material_hash, key, (void ***)&emc_p)) {
- return **emc_p;
- }
-
- *emc_p = emc = BLI_memblock_alloc(sldata->material_cache);
-
- material_shadow(vedata, sldata, ma, is_hair, emc);
-
- {
- /* Depth Pass */
- int mat_options = VAR_MAT_MESH | VAR_MAT_DEPTH;
- SET_FLAG_FROM_TEST(mat_options, use_ssrefract, VAR_MAT_REFRACT);
- SET_FLAG_FROM_TEST(mat_options, use_depth_shader, VAR_MAT_HASH);
- SET_FLAG_FROM_TEST(mat_options, is_hair, VAR_MAT_HAIR);
- GPUMaterial *gpumat = (use_depth_shader) ?
- EEVEE_material_get(vedata, scene, ma, NULL, mat_options) :
- EEVEE_material_default_get(scene, ma, mat_options);
-
- int option = 0;
- SET_FLAG_FROM_TEST(option, do_cull, KEY_CULL);
- SET_FLAG_FROM_TEST(option, use_ssrefract, KEY_REFRACT);
- DRWPass *depth_ps = (DRWPass *[]){
- psl->depth_ps,
- psl->depth_cull_ps,
- psl->depth_refract_ps,
- psl->depth_refract_cull_ps,
- }[option];
- /* Hair are rendered inside the non-cull pass but needs to have a separate cache key. */
- SET_FLAG_FROM_TEST(option, is_hair, KEY_HAIR);
-
- /* Search for the same shaders usage in the pass. */
- struct GPUShader *sh = GPU_material_get_shader(gpumat);
- void *cache_key = (char *)sh + option;
- DRWShadingGroup *grp, **grp_p;
-
- if (BLI_ghash_ensure_p(pd->material_hash, cache_key, (void ***)&grp_p)) {
- /* This GPUShader has already been used by another material.
- * Add new shading group just after to avoid shader switching cost. */
- grp = DRW_shgroup_create_sub(*grp_p);
- }
- else {
- *grp_p = grp = DRW_shgroup_create(sh, depth_ps);
- EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false);
- }
-
- DRW_shgroup_add_material_resources(grp, gpumat);
-
- emc->depth_grp = grp;
- emc->depth_grp_p = grp_p;
- }
- {
- /* Shading Pass */
- int mat_options = VAR_MAT_MESH;
- SET_FLAG_FROM_TEST(mat_options, use_ssrefract, VAR_MAT_REFRACT);
- SET_FLAG_FROM_TEST(mat_options, is_hair, VAR_MAT_HAIR);
- GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options);
- const bool use_sss = GPU_material_flag_get(gpumat, GPU_MATFLAG_SSS);
-
- int ssr_id = (((effects->enabled_effects & EFFECT_SSR) != 0) && !use_ssrefract) ? 1 : 0;
- int option = (use_ssrefract ? 0 : (use_sss ? 1 : 2)) * 2 + do_cull;
- DRWPass *shading_pass = (DRWPass *[]){
- psl->material_refract_ps,
- psl->material_refract_cull_ps,
- psl->material_sss_ps,
- psl->material_sss_cull_ps,
- psl->material_ps,
- psl->material_cull_ps,
- }[option];
- /* Hair are rendered inside the non-cull pass but needs to have a separate cache key */
- option = option * 2 + is_hair;
-
- /* Search for the same shaders usage in the pass. */
- /* HACK: Assume the struct will never be smaller than our variations.
- * This allow us to only keep one ghash and avoid bigger keys comparisons/hashing. */
- BLI_assert(option <= 16);
- struct GPUShader *sh = GPU_material_get_shader(gpumat);
- void *cache_key = (char *)sh + option;
- DRWShadingGroup *grp, **grp_p;
-
- if (BLI_ghash_ensure_p(pd->material_hash, cache_key, (void ***)&grp_p)) {
- /* This GPUShader has already been used by another material.
- * Add new shading group just after to avoid shader switching cost. */
- grp = DRW_shgroup_create_sub(*grp_p);
-
- /* Per material uniforms. */
- if (use_ssrefract) {
- DRW_shgroup_uniform_float_copy(grp, "refractionDepth", ma->refract_depth);
- }
- }
- else {
- *grp_p = grp = DRW_shgroup_create(sh, shading_pass);
- EEVEE_material_bind_resources(
- grp, gpumat, sldata, vedata, &ssr_id, &ma->refract_depth, use_ssrefract, false);
- }
- DRW_shgroup_add_material_resources(grp, gpumat);
-
- if (use_sss) {
- EEVEE_subsurface_add_pass(sldata, vedata, ma, grp, gpumat);
- }
-
- emc->shading_grp = grp;
- emc->shading_grp_p = grp_p;
- emc->shading_gpumat = gpumat;
- }
- return *emc;
-}
-
-static EeveeMaterialCache material_transparent(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Material *ma)
-{
- const DRWContextState *draw_ctx = DRW_context_state_get();
- Scene *scene = draw_ctx->scene;
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_EffectsInfo *effects = vedata->stl->effects;
- EeveeMaterialCache emc = {0};
-
- const bool do_cull = (ma->blend_flag & MA_BL_CULL_BACKFACE) != 0;
- const bool use_gpumat = ma->use_nodes && ma->nodetree;
- const bool use_ssrefract = use_gpumat && ((ma->blend_flag & MA_BL_SS_REFRACTION) != 0) &&
- ((effects->enabled_effects & EFFECT_REFRACT) != 0);
- const bool use_prepass = ((ma->blend_flag & MA_BL_HIDE_BACKFACE) != 0);
-
- DRWState cur_state;
- DRWState all_state = (DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_CULL_BACK |
- DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_DEPTH_EQUAL |
- DRW_STATE_BLEND_CUSTOM);
-
- material_shadow(vedata, sldata, ma, false, &emc);
-
- if (use_prepass) {
- /* Depth prepass */
- int mat_options = VAR_MAT_MESH | VAR_MAT_DEPTH;
- GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options);
- struct GPUShader *sh = GPU_material_get_shader(gpumat);
-
- DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->transparent_pass);
-
- EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, true);
- DRW_shgroup_add_material_resources(grp, gpumat);
-
- cur_state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
- cur_state |= (do_cull) ? DRW_STATE_CULL_BACK : 0;
-
- DRW_shgroup_state_disable(grp, all_state);
- DRW_shgroup_state_enable(grp, cur_state);
-
- emc.depth_grp = grp;
- }
- {
- /* Shading */
- int ssr_id = -1; /* TODO: transparent SSR. */
- int mat_options = VAR_MAT_MESH | VAR_MAT_BLEND;
- SET_FLAG_FROM_TEST(mat_options, use_ssrefract, VAR_MAT_REFRACT);
- GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options);
-
- DRWShadingGroup *grp = DRW_shgroup_create(GPU_material_get_shader(gpumat),
- psl->transparent_pass);
-
- EEVEE_material_bind_resources(
- grp, gpumat, sldata, vedata, &ssr_id, &ma->refract_depth, use_ssrefract, true);
- DRW_shgroup_add_material_resources(grp, gpumat);
-
- cur_state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM;
- cur_state |= (use_prepass) ? DRW_STATE_DEPTH_EQUAL : DRW_STATE_DEPTH_LESS_EQUAL;
- cur_state |= (do_cull) ? DRW_STATE_CULL_BACK : 0;
-
- /* Disable other blend modes and use the one we want. */
- DRW_shgroup_state_disable(grp, all_state);
- DRW_shgroup_state_enable(grp, cur_state);
-
- emc.shading_grp = grp;
- emc.shading_gpumat = gpumat;
- }
- return emc;
-}
-
-/* Return correct material or empty default material if slot is empty. */
-BLI_INLINE Material *eevee_object_material_get(Object *ob, int slot, bool holdout)
-{
- if (holdout) {
- return BKE_material_default_holdout();
- }
- Material *ma = BKE_object_material_get_eval(ob, slot + 1);
- if (ma == NULL) {
- if (ob->type == OB_VOLUME) {
- ma = BKE_material_default_volume();
- }
- else {
- ma = BKE_material_default_surface();
- }
- }
- return ma;
-}
-
-BLI_INLINE EeveeMaterialCache eevee_material_cache_get(
- EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob, int slot, bool is_hair)
-{
- const bool holdout = (ob->base_flag & BASE_HOLDOUT) != 0;
- EeveeMaterialCache matcache;
- Material *ma = eevee_object_material_get(ob, slot, holdout);
- switch (ma->blend_method) {
- case MA_BM_BLEND:
- if (!is_hair) {
- matcache = material_transparent(vedata, sldata, ma);
- break;
- }
- ATTR_FALLTHROUGH;
- case MA_BM_SOLID:
- case MA_BM_CLIP:
- case MA_BM_HASHED:
- default:
- matcache = material_opaque(vedata, sldata, ma, is_hair);
- break;
- }
- return matcache;
-}
-
-static void eevee_hair_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob,
- ParticleSystem *psys,
- ModifierData *md,
- int matnr,
- bool *cast_shadow)
-{
- EeveeMaterialCache matcache = eevee_material_cache_get(vedata, sldata, ob, matnr - 1, true);
-
- if (matcache.depth_grp) {
- *matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp, NULL);
- DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat);
- }
- if (matcache.shading_grp) {
- *matcache.shading_grp_p = DRW_shgroup_hair_create_sub(
- ob, psys, md, matcache.shading_grp, matcache.shading_gpumat);
- DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat);
- }
- if (matcache.shadow_grp) {
- *matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp, NULL);
- DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat);
- *cast_shadow = true;
- }
-
- EEVEE_motion_blur_hair_cache_populate(sldata, vedata, ob, psys, md);
-}
-
-#define ADD_SHGROUP_CALL(shgrp, ob, geom, oedata) \
- do { \
- if (oedata) { \
- DRW_shgroup_call_with_callback(shgrp, geom, ob, oedata); \
- } \
- else { \
- DRW_shgroup_call(shgrp, geom, ob); \
- } \
- } while (0)
-
-#define ADD_SHGROUP_CALL_SAFE(shgrp, ob, geom, oedata) \
- do { \
- if (shgrp) { \
- ADD_SHGROUP_CALL(shgrp, ob, geom, oedata); \
- } \
- } while (0)
-
-#define MATCACHE_AS_ARRAY(matcache, member, materials_len, output_array) \
- for (int i = 0; i < materials_len; i++) { \
- output_array[i] = matcache[i].member; \
- }
-
-void EEVEE_materials_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob,
- bool *cast_shadow)
-{
- const DRWContextState *draw_ctx = DRW_context_state_get();
- Scene *scene = draw_ctx->scene;
-
- bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) &&
- !DRW_state_is_image_render();
-
- /* First get materials for this mesh. */
- if (ELEM(ob->type, OB_MESH, OB_SURF, OB_MBALL)) {
- const int materials_len = DRW_cache_object_material_count_get(ob);
-
- EeveeMaterialCache *matcache = BLI_array_alloca(matcache, materials_len);
- for (int i = 0; i < materials_len; i++) {
- matcache[i] = eevee_material_cache_get(vedata, sldata, ob, i, false);
- }
-
- /* Only support single volume material for now. */
- /* XXX We rely on the previously compiled surface shader
- * to know if the material has a "volume nodetree".
- */
- bool use_volume_material = (matcache[0].shading_gpumat &&
- GPU_material_has_volume_output(matcache[0].shading_gpumat));
- if ((ob->dt >= OB_SOLID) || DRW_state_is_scene_render()) {
- if (use_sculpt_pbvh) {
- struct DRWShadingGroup **shgrps_array = BLI_array_alloca(shgrps_array, materials_len);
-
- MATCACHE_AS_ARRAY(matcache, shading_grp, materials_len, shgrps_array);
- DRW_shgroup_call_sculpt_with_materials(shgrps_array, materials_len, ob);
-
- MATCACHE_AS_ARRAY(matcache, depth_grp, materials_len, shgrps_array);
- DRW_shgroup_call_sculpt_with_materials(shgrps_array, materials_len, ob);
-
- MATCACHE_AS_ARRAY(matcache, shadow_grp, materials_len, shgrps_array);
- DRW_shgroup_call_sculpt_with_materials(shgrps_array, materials_len, ob);
- }
- else {
- struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len);
- MATCACHE_AS_ARRAY(matcache, shading_gpumat, materials_len, gpumat_array);
- /* Get per-material split surface */
- struct GPUBatch **mat_geom = DRW_cache_object_surface_material_get(
- ob, gpumat_array, materials_len);
-
- if (mat_geom) {
- for (int i = 0; i < materials_len; i++) {
- if (mat_geom[i] == NULL) {
- continue;
- }
-
- /* Do not render surface if we are rendering a volume object
- * and do not have a surface closure. */
- if (use_volume_material &&
- (gpumat_array[i] && !GPU_material_has_surface_output(gpumat_array[i]))) {
- continue;
- }
-
- /* XXX TODO: rewrite this to include the dupli objects.
- * This means we cannot exclude dupli objects from reflections!!! */
- EEVEE_ObjectEngineData *oedata = NULL;
- if ((ob->base_flag & BASE_FROM_DUPLI) == 0) {
- oedata = EEVEE_object_data_ensure(ob);
- oedata->ob = ob;
- oedata->test_data = &sldata->probes->vis_data;
- }
-
- ADD_SHGROUP_CALL(matcache[i].shading_grp, ob, mat_geom[i], oedata);
- ADD_SHGROUP_CALL_SAFE(matcache[i].depth_grp, ob, mat_geom[i], oedata);
- ADD_SHGROUP_CALL_SAFE(matcache[i].shadow_grp, ob, mat_geom[i], oedata);
- *cast_shadow = *cast_shadow || (matcache[i].shadow_grp != NULL);
- }
- }
- }
-
- /* Motion Blur Vectors. */
- EEVEE_motion_blur_cache_populate(sldata, vedata, ob);
- }
-
- /* Volumetrics */
- if (use_volume_material) {
- EEVEE_volumes_cache_object_add(sldata, vedata, scene, ob);
- }
- }
-}
-
-void EEVEE_particle_hair_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob,
- bool *cast_shadow)
-{
- 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;
- }
- eevee_hair_cache_populate(vedata, sldata, ob, psys, md, part->omat, cast_shadow);
- }
- }
- }
-}
-
-void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob,
- bool *cast_shadow)
-{
- eevee_hair_cache_populate(vedata, sldata, ob, NULL, NULL, HAIR_MATERIAL_NR, cast_shadow);
-}
-
-void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
-{
- EEVEE_PrivateData *pd = vedata->stl->g_data;
- EEVEE_EffectsInfo *effects = vedata->stl->effects;
-
- BLI_ghash_free(pd->material_hash, NULL, NULL);
- pd->material_hash = NULL;
-
- SET_FLAG_FROM_TEST(effects->enabled_effects, effects->sss_surface_count > 0, EFFECT_SSS);
-}
-
-void EEVEE_materials_free(void)
-{
- DRW_TEXTURE_FREE_SAFE(e_data.util_tex);
- DRW_TEXTURE_FREE_SAFE(e_data.noise_tex);
-}
-
-/* -------------------------------------------------------------------- */
-/** \name Render Passes
- * \{ */
-
-void EEVEE_material_renderpasses_init(EEVEE_Data *vedata)
-{
- EEVEE_PrivateData *pd = vedata->stl->g_data;
-
- /* For diffuse and glossy we calculate the final light + color buffer where we extract the
- * light from by dividing by the color buffer. When one the light is requested we also tag
- * the color buffer to do the extraction. */
- if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) {
- pd->render_passes |= EEVEE_RENDER_PASS_DIFFUSE_COLOR;
- }
- if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) {
- pd->render_passes |= EEVEE_RENDER_PASS_SPECULAR_COLOR;
- }
-}
-
-static void material_renderpass_init(GPUTexture **output_tx, const eGPUTextureFormat format)
-{
- DRW_texture_ensure_fullscreen_2d(output_tx, format, 0);
-}
-
-void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples)
-{
- EEVEE_FramebufferList *fbl = vedata->fbl;
- DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- EEVEE_TextureList *txl = vedata->txl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
- EEVEE_PrivateData *pd = stl->g_data;
-
- /* Should be enough precision for many samples. */
- const eGPUTextureFormat texture_format = (tot_samples > 128) ? GPU_RGBA32F : GPU_RGBA16F;
-
- /* Create FrameBuffer. */
- GPU_framebuffer_ensure_config(&fbl->material_accum_fb,
- {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_LEAVE});
-
- if (pd->render_passes & EEVEE_RENDER_PASS_ENVIRONMENT) {
- material_renderpass_init(&txl->env_accum, texture_format);
- }
- if (pd->render_passes & EEVEE_RENDER_PASS_EMIT) {
- material_renderpass_init(&txl->emit_accum, texture_format);
- }
- if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_COLOR) {
- material_renderpass_init(&txl->diff_color_accum, texture_format);
- }
- if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) {
- material_renderpass_init(&txl->diff_light_accum, texture_format);
- }
- if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_COLOR) {
- material_renderpass_init(&txl->spec_color_accum, texture_format);
- }
- if (pd->render_passes & EEVEE_RENDER_PASS_AOV) {
- for (int aov_index = 0; aov_index < pd->num_aovs_used; aov_index++) {
- material_renderpass_init(&txl->aov_surface_accum[aov_index], texture_format);
- }
- }
- if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) {
- material_renderpass_init(&txl->spec_light_accum, texture_format);
-
- if (effects->enabled_effects & EFFECT_SSR) {
- EEVEE_reflection_output_init(sldata, vedata, tot_samples);
- }
- }
-}
-
-static void material_renderpass_accumulate(EEVEE_EffectsInfo *effects,
- EEVEE_FramebufferList *fbl,
- DRWPass *renderpass,
- DRWPass *renderpass2,
- EEVEE_PrivateData *pd,
- GPUTexture *output_tx,
- struct GPUUniformBuf *renderpass_option_ubo)
-{
- GPU_framebuffer_texture_attach(fbl->material_accum_fb, output_tx, 0, 0);
- GPU_framebuffer_bind(fbl->material_accum_fb);
-
- if (effects->taa_current_sample == 1) {
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- GPU_framebuffer_clear_color(fbl->material_accum_fb, clear);
- }
-
- pd->renderpass_ubo = renderpass_option_ubo;
- DRW_draw_pass(renderpass);
- if (renderpass2) {
- DRW_draw_pass(renderpass2);
- }
-
- GPU_framebuffer_texture_detach(fbl->material_accum_fb, output_tx);
-}
-
-void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
-{
- EEVEE_FramebufferList *fbl = vedata->fbl;
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_PrivateData *pd = vedata->stl->g_data;
- EEVEE_EffectsInfo *effects = vedata->stl->effects;
- EEVEE_TextureList *txl = vedata->txl;
-
- if (fbl->material_accum_fb != NULL) {
- DRWPass *material_accum_ps = psl->material_accum_ps;
- DRWPass *background_accum_ps = psl->background_accum_ps;
- if (pd->render_passes & EEVEE_RENDER_PASS_ENVIRONMENT) {
- material_renderpass_accumulate(effects,
- fbl,
- background_accum_ps,
- NULL,
- pd,
- txl->env_accum,
- sldata->renderpass_ubo.environment);
- }
- if (pd->render_passes & EEVEE_RENDER_PASS_EMIT) {
- material_renderpass_accumulate(
- effects, fbl, material_accum_ps, NULL, pd, txl->emit_accum, sldata->renderpass_ubo.emit);
- }
- if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_COLOR) {
- material_renderpass_accumulate(effects,
- fbl,
- material_accum_ps,
- NULL,
- pd,
- txl->diff_color_accum,
- sldata->renderpass_ubo.diff_color);
- }
- if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) {
- material_renderpass_accumulate(effects,
- fbl,
- material_accum_ps,
- NULL,
- pd,
- txl->diff_light_accum,
- sldata->renderpass_ubo.diff_light);
-
- if (effects->enabled_effects & EFFECT_SSS) {
- EEVEE_subsurface_output_accumulate(sldata, vedata);
- }
- }
- if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_COLOR) {
- bool prev_ssr = sldata->common_data.ssr_toggle;
- if (prev_ssr) {
- /* We need to disable ssr here so output radiance is not directed to the ssr buffer. */
- sldata->common_data.ssr_toggle = false;
- GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
- }
- material_renderpass_accumulate(effects,
- fbl,
- material_accum_ps,
- NULL,
- pd,
- txl->spec_color_accum,
- sldata->renderpass_ubo.spec_color);
- if (prev_ssr) {
- sldata->common_data.ssr_toggle = prev_ssr;
- GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
- }
- }
- if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) {
- material_renderpass_accumulate(effects,
- fbl,
- material_accum_ps,
- NULL,
- pd,
- txl->spec_light_accum,
- sldata->renderpass_ubo.spec_light);
-
- if (effects->enabled_effects & EFFECT_SSR) {
- EEVEE_reflection_output_accumulate(sldata, vedata);
- }
- }
- if (pd->render_passes & EEVEE_RENDER_PASS_AOV) {
- for (int aov_index = 0; aov_index < pd->num_aovs_used; aov_index++) {
- material_renderpass_accumulate(effects,
- fbl,
- material_accum_ps,
- background_accum_ps,
- pd,
- txl->aov_surface_accum[aov_index],
- sldata->renderpass_ubo.aovs[aov_index]);
- }
- }
- /* Free unused aov textures. */
- for (int aov_index = pd->num_aovs_used; aov_index < MAX_AOVS; aov_index++) {
- DRW_TEXTURE_FREE_SAFE(txl->aov_surface_accum[aov_index]);
- }
-
- /* Restore default. */
- pd->renderpass_ubo = sldata->renderpass_ubo.combined;
- GPU_framebuffer_bind(fbl->main_fb);
- }
-}
-
-/** \} */
diff --git a/source/blender/draw/engines/eevee/eevee_mesh.cc b/source/blender/draw/engines/eevee/eevee_mesh.cc
new file mode 100644
index 00000000000..82c524fc616
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_mesh.cc
@@ -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
diff --git a/source/blender/draw/engines/eevee/eevee_mist.c b/source/blender/draw/engines/eevee/eevee_mist.c
deleted file mode 100644
index d4490d6fd4c..00000000000
--- a/source/blender/draw/engines/eevee/eevee_mist.c
+++ /dev/null
@@ -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);
- }
-}
diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c
deleted file mode 100644
index 703518a32ec..00000000000
--- a/source/blender/draw/engines/eevee/eevee_motion_blur.c
+++ /dev/null
@@ -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();
- }
-}
diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.cc b/source/blender/draw/engines/eevee/eevee_motion_blur.cc
new file mode 100644
index 00000000000..cc841611258
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_motion_blur.cc
@@ -0,0 +1,248 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ */
+
+#include "BLI_map.hh"
+#include "DEG_depsgraph_query.h"
+
+#include "eevee_instance.hh"
+#include "eevee_sampling.hh"
+#include "eevee_shader_shared.hh"
+#include "eevee_velocity.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name MotionBlurModule
+ *
+ * \{ */
+
+void MotionBlurModule::init(void)
+{
+ const Scene *scene = inst_.scene;
+
+ enabled_ = (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0;
+
+ /* Viewport not supported for now. */
+ if (!DRW_state_is_scene_render()) {
+ enabled_ = false;
+ }
+ if (!enabled_) {
+ motion_blur_fx_enabled_ = false;
+ return;
+ }
+
+ /* Take into account the steps needed for fx motion blur. */
+ int steps_count = max_ii(1, scene->eevee.motion_blur_steps) * 2 + 1;
+
+ time_steps_.resize(steps_count);
+
+ initial_frame_ = CFRA;
+ initial_subframe_ = SUBFRA;
+ frame_time_ = initial_frame_ + initial_subframe_;
+ motion_blur_position_ = scene->eevee.motion_blur_position;
+ motion_blur_shutter_ = scene->eevee.motion_blur_shutter;
+
+ /* Without this there is the possibility of the curve table not being allocated. */
+ BKE_curvemapping_changed((struct CurveMapping *)&scene->r.mblur_shutter_curve, false);
+
+ Vector<float> cdf(CM_TABLE);
+ Sampling::cdf_from_curvemapping(scene->r.mblur_shutter_curve, cdf);
+ Sampling::cdf_invert(cdf, time_steps_);
+
+ for (float &time : time_steps_) {
+ time = this->shutter_time_to_scene_time(time);
+ }
+
+ motion_blur_fx_enabled_ = scene->eevee.motion_blur_max > 0.5f;
+ step_id_ = 1;
+
+ if (motion_blur_fx_enabled_) {
+ /* A bit weird but we have to sync the first 2 steps here because the step()
+ * function is only called after rendering a sample. */
+ inst_.velocity.step_sync(VelocityModule::STEP_PREVIOUS, time_steps_[0]);
+ inst_.velocity.step_sync(VelocityModule::STEP_NEXT, time_steps_[2]);
+ }
+ inst_.set_time(time_steps_[1]);
+}
+
+/* Runs after rendering a sample. */
+void MotionBlurModule::step(void)
+{
+ if (!enabled_) {
+ return;
+ }
+ else if (inst_.sampling.finished()) {
+ /* Restore original frame number. This is because the render pipeline expects it. */
+ RE_engine_frame_set(inst_.render, initial_frame_, initial_subframe_);
+ }
+ else if (inst_.sampling.do_render_sync()) {
+ /* Time to change motion step. */
+ BLI_assert(time_steps_.size() > step_id_ + 2);
+ step_id_ += 2;
+
+ if (motion_blur_fx_enabled_) {
+ inst_.velocity.step_swap();
+ inst_.velocity.step_sync(VelocityModule::STEP_NEXT, time_steps_[step_id_ + 1]);
+ }
+ inst_.set_time(time_steps_[step_id_]);
+ }
+}
+
+float MotionBlurModule::shutter_time_to_scene_time(float time)
+{
+ switch (motion_blur_position_) {
+ case SCE_EEVEE_MB_START:
+ /* No offset. */
+ break;
+ case SCE_EEVEE_MB_CENTER:
+ time -= 0.5f;
+ break;
+ case SCE_EEVEE_MB_END:
+ time -= 1.0;
+ break;
+ default:
+ BLI_assert(!"Invalid motion blur position enum!");
+ break;
+ }
+ time *= motion_blur_shutter_;
+ time += frame_time_;
+ return time;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name MotionBlur
+ *
+ * \{ */
+
+void MotionBlur::init()
+{
+ const Scene *scene = inst_.scene;
+ data_.blur_max = scene->eevee.motion_blur_max;
+ data_.depth_scale = scene->eevee.motion_blur_depth_scale;
+ enabled_ = ((scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0) && (data_.blur_max > 0.5f);
+}
+
+void MotionBlur::sync(int extent[2])
+{
+ if (!enabled_) {
+ return;
+ }
+
+ DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
+ eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
+
+ uint res[2] = {divide_ceil_u(extent[0], MB_TILE_DIVISOR),
+ divide_ceil_u(extent[1], MB_TILE_DIVISOR)};
+
+ {
+ /* Create max velocity tiles in 2 passes. One for X and one for Y */
+ DRW_PASS_CREATE(tiles_flatten_ps_, DRW_STATE_WRITE_COLOR);
+ GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_TILE_FLATTEN);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_flatten_ps_);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "velocity_tx", &input_velocity_tx_, no_filter);
+ DRW_shgroup_uniform_block(grp, "motion_blur_block", data_.ubo_get());
+ DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
+
+ tiles_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
+
+ tiles_flatten_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(tiles_tx_));
+ }
+ {
+ /* Expand max tiles by keeping the max tile in each tile neighborhood. */
+ DRW_PASS_CREATE(tiles_dilate_ps_, DRW_STATE_WRITE_COLOR);
+ GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_TILE_DILATE);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_dilate_ps_);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_tx", &tiles_tx_, no_filter);
+ DRW_shgroup_uniform_block(grp, "motion_blur_block", data_.ubo_get());
+ DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
+
+ tiles_dilated_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner);
+
+ tiles_dilate_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(tiles_dilated_tx_));
+ }
+ {
+ data_.target_size_inv[0] = 1.0f / extent[0];
+ data_.target_size_inv[1] = 1.0f / extent[1];
+
+ /* Do the motion blur gather algorithm. */
+ DRW_PASS_CREATE(gather_ps_, DRW_STATE_WRITE_COLOR);
+ GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_GATHER);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_ps_);
+ DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
+ DRW_shgroup_uniform_block(grp, "motion_blur_block", data_.ubo_get());
+ DRW_shgroup_uniform_texture_ref(grp, "color_tx", &input_color_tx_);
+ DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "velocity_tx", &input_velocity_tx_, no_filter);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_tx", &tiles_dilated_tx_, no_filter);
+
+ DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
+ }
+
+ data_.is_viewport = !DRW_state_is_image_render();
+ data_.push_update();
+}
+
+void MotionBlur::render(GPUTexture *depth_tx,
+ GPUTexture *velocity_tx,
+ GPUTexture **input_tx,
+ GPUTexture **output_tx)
+{
+ if (!enabled_) {
+ return;
+ }
+
+ input_color_tx_ = *input_tx;
+ input_depth_tx_ = depth_tx;
+ input_velocity_tx_ = velocity_tx;
+
+ DRW_stats_group_start("Motion Blur");
+
+ GPU_framebuffer_bind(tiles_flatten_fb_);
+ DRW_draw_pass(tiles_flatten_ps_);
+
+ for (int max_blur = data_.blur_max; max_blur > 0; max_blur -= MB_TILE_DIVISOR) {
+ GPU_framebuffer_bind(tiles_dilate_fb_);
+ DRW_draw_pass(tiles_dilate_ps_);
+ SWAP(GPUTexture *, tiles_tx_, tiles_dilated_tx_);
+ SWAP(Framebuffer, tiles_flatten_fb_, tiles_dilate_fb_);
+ }
+ /* Swap again so result is in tiles_dilated_tx_. */
+ SWAP(GPUTexture *, tiles_tx_, tiles_dilated_tx_);
+ SWAP(Framebuffer, tiles_flatten_fb_, tiles_dilate_fb_);
+
+ gather_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(*output_tx));
+
+ GPU_framebuffer_bind(gather_fb_);
+ DRW_draw_pass(gather_ps_);
+
+ DRW_stats_group_end();
+
+ /* Swap buffers so that next effect has the right input. */
+ *input_tx = *output_tx;
+ *output_tx = input_color_tx_;
+}
+
+/** \} */
+
+} // namespace blender::eevee \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.hh b/source/blender/draw/engines/eevee/eevee_motion_blur.hh
new file mode 100644
index 00000000000..5dab68fba5d
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_motion_blur.hh
@@ -0,0 +1,159 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Motion blur is done by accumulating scene samples over shutter time.
+ * Since the number of step is discrete, quite low, and not per pixel randomized,
+ * we couple this with a post processing motion blur.
+ *
+ * The post-fx motion blur is done in two directions, from the previous step and to the next.
+ *
+ * For a scene with 3 motion steps, a flat shutter curve and shutter time of 2 frame
+ * centered on frame we have:
+ *
+ * |--------------------|--------------------|
+ * -1 0 1 Frames
+ *
+ * |-------------|-------------|-------------|
+ * 1 2 3 Motion steps
+ *
+ * |------|------|------|------|------|------|
+ * 0 1 2 4 5 6 7 Time Steps
+ *
+ * |-------------| One motion step blurs this range.
+ * -1 | +1 Objects and geometry steps are recorded here.
+ * 0 Scene is rendered here.
+ *
+ * Since motion step N and N+1 share one time step we reuse it to avoid an extra scene evaluation.
+ *
+ * Note that we have to evaluate -1 and +1 time steps before rendering so eval order is -1, +1, 0.
+ * This is because all GPUBatches from the DRWCache are being free when changing a frame.
+ */
+
+#pragma once
+
+#include "BLI_map.hh"
+#include "DEG_depsgraph_query.h"
+
+#include "eevee_sampling.hh"
+#include "eevee_shader_shared.hh"
+#include "eevee_velocity.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name MotionBlur
+ *
+ * \{ */
+
+/**
+ * Manages timesteps evaluations and accumulation Motion blur.
+ * Post process motion blur is handled by the MotionBlur class.
+ */
+class MotionBlurModule {
+ private:
+ Instance &inst_;
+
+ /**
+ * Array containing all steps (in scene time) we need to evaluate (not render).
+ * Only odd steps are rendered. The even ones are evaluated for fx motion blur.
+ */
+ Vector<float> time_steps_;
+
+ /** Copy of input frame an subframe to restore after render. */
+ int initial_frame_;
+ float initial_subframe_;
+ /** Time of the frame we are rendering. */
+ float frame_time_;
+ /** Copy of scene settings. */
+ int motion_blur_position_;
+ float motion_blur_shutter_;
+
+ bool enabled_ = false;
+ float motion_blur_fx_enabled_ = false;
+
+ int step_id_ = 0;
+
+ public:
+ MotionBlurModule(Instance &inst) : inst_(inst){};
+ ~MotionBlurModule(){};
+
+ void init(void);
+
+ void step(void);
+
+ private:
+ float shutter_time_to_scene_time(float time);
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name MotionBlur
+ *
+ * \{ */
+
+/**
+ * Per view fx module. Perform a motion blur using the result of the velocity pass.
+ */
+class MotionBlur {
+ private:
+ Instance &inst_;
+
+ StringRefNull view_name_;
+
+ /** Textures from pool. Not owned. */
+ GPUTexture *tiles_tx_ = nullptr;
+ GPUTexture *tiles_dilated_tx_ = nullptr;
+ /** Input texture. Not owned. */
+ GPUTexture *input_velocity_tx_ = nullptr;
+ GPUTexture *input_color_tx_ = nullptr;
+ GPUTexture *input_depth_tx_ = nullptr;
+ /** Passes. Not owned. */
+ DRWPass *tiles_flatten_ps_ = nullptr;
+ DRWPass *tiles_dilate_ps_ = nullptr;
+ DRWPass *gather_ps_ = nullptr;
+ /** Framebuffers. Owned. */
+ eevee::Framebuffer tiles_flatten_fb_;
+ eevee::Framebuffer tiles_dilate_fb_;
+ eevee::Framebuffer gather_fb_;
+
+ StructBuffer<MotionBlurData> data_;
+
+ bool enabled_;
+
+ public:
+ MotionBlur(Instance &inst, StringRefNull view_name) : inst_(inst), view_name_(view_name){};
+
+ ~MotionBlur(){};
+
+ void init(void);
+
+ void sync(int extent[2]);
+
+ void render(GPUTexture *depth_tx,
+ GPUTexture *velocity_tx,
+ GPUTexture **input_tx,
+ GPUTexture **output_tx);
+};
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_occlusion.c b/source/blender/draw/engines/eevee/eevee_occlusion.c
deleted file mode 100644
index 1acd9950012..00000000000
--- a/source/blender/draw/engines/eevee/eevee_occlusion.c
+++ /dev/null
@@ -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);
-}
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index 766e721b1b8..e99bef24fa6 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -13,1632 +13,33 @@
* 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
+ * Copyright 2021, Blender Foundation.
*/
#pragma once
-#include "DRW_render.h"
-
-#include "BLI_bitmap.h"
-
-#include "DNA_lightprobe_types.h"
-
-#include "GPU_viewport.h"
-
-#include "BKE_camera.h"
-
#ifdef __cplusplus
extern "C" {
#endif
-struct EEVEE_ShadowCasterBuffer;
-struct GPUFrameBuffer;
struct Object;
-struct RenderLayer;
-
-extern struct DrawEngineType draw_engine_eevee_type;
-
-/* Minimum UBO is 16384 bytes */
-#define MAX_PROBE 128 /* TODO: find size by dividing UBO max size by probe data size. */
-#define MAX_GRID 64 /* TODO: find size by dividing UBO max size by grid data size. */
-#define MAX_PLANAR 16 /* TODO: find size by dividing UBO max size by grid data size. */
-#define MAX_LIGHT 128 /* TODO: find size by dividing UBO max size by light data size. */
-#define MAX_CASCADE_NUM 4
-#define MAX_SHADOW 128 /* TODO: Make this depends on #GL_MAX_ARRAY_TEXTURE_LAYERS. */
-#define MAX_SHADOW_CASCADE 8
-#define MAX_SHADOW_CUBE (MAX_SHADOW - MAX_CASCADE_NUM * MAX_SHADOW_CASCADE)
-#define MAX_BLOOM_STEP 16
-#define MAX_AOVS 64
-
-/* Special value chosen to not be altered by depth of field sample count. */
-#define TAA_MAX_SAMPLE 10000926
-
-// #define DEBUG_SHADOW_DISTRIBUTION
-
-/* Only define one of these. */
-// #define IRRADIANCE_SH_L2
-#define IRRADIANCE_HL2
-
-#if defined(IRRADIANCE_SH_L2)
-# define SHADER_IRRADIANCE "#define IRRADIANCE_SH_L2\n"
-#elif defined(IRRADIANCE_HL2)
-# define SHADER_IRRADIANCE "#define IRRADIANCE_HL2\n"
-#endif
-
-/* Macro causes over indentation. */
-/* clang-format off */
-#define SHADER_DEFINES \
- "#define EEVEE_ENGINE\n" \
- "#define MAX_PROBE " STRINGIFY(MAX_PROBE) "\n" \
- "#define MAX_GRID " STRINGIFY(MAX_GRID) "\n" \
- "#define MAX_PLANAR " STRINGIFY(MAX_PLANAR) "\n" \
- "#define MAX_LIGHT " STRINGIFY(MAX_LIGHT) "\n" \
- "#define MAX_SHADOW " STRINGIFY(MAX_SHADOW) "\n" \
- "#define MAX_SHADOW_CUBE " STRINGIFY(MAX_SHADOW_CUBE) "\n" \
- "#define MAX_SHADOW_CASCADE " STRINGIFY(MAX_SHADOW_CASCADE) "\n" \
- "#define MAX_CASCADE_NUM " STRINGIFY(MAX_CASCADE_NUM) "\n" \
- SHADER_IRRADIANCE
-/* clang-format on */
-
-#define EEVEE_PROBE_MAX min_ii(MAX_PROBE, GPU_max_texture_layers() / 6)
-#define EEVEE_VELOCITY_TILE_SIZE 32
-#define USE_VOLUME_OPTI (GPU_shader_image_load_store_support())
-
-#define SWAP_DOUBLE_BUFFERS() \
- { \
- if (effects->swap_double_buffer) { \
- SWAP(struct GPUFrameBuffer *, fbl->main_fb, fbl->double_buffer_fb); \
- SWAP(struct GPUFrameBuffer *, fbl->main_color_fb, fbl->double_buffer_color_fb); \
- SWAP(GPUTexture *, txl->color, txl->color_double_buffer); \
- effects->swap_double_buffer = false; \
- } \
- } \
- ((void)0)
-
-#define SWAP_BUFFERS() \
- { \
- if (effects->target_buffer == fbl->effect_color_fb) { \
- SWAP_DOUBLE_BUFFERS(); \
- effects->source_buffer = txl->color_post; \
- effects->target_buffer = fbl->main_color_fb; \
- } \
- else { \
- SWAP_DOUBLE_BUFFERS(); \
- effects->source_buffer = txl->color; \
- effects->target_buffer = fbl->effect_color_fb; \
- } \
- } \
- ((void)0)
-
-#define SWAP_BUFFERS_TAA() \
- { \
- if (effects->target_buffer == fbl->effect_color_fb) { \
- SWAP(struct GPUFrameBuffer *, fbl->effect_fb, fbl->taa_history_fb); \
- SWAP(struct GPUFrameBuffer *, fbl->effect_color_fb, fbl->taa_history_color_fb); \
- SWAP(GPUTexture *, txl->color_post, txl->taa_history); \
- effects->source_buffer = txl->taa_history; \
- effects->target_buffer = fbl->effect_color_fb; \
- } \
- else { \
- SWAP(struct GPUFrameBuffer *, fbl->main_fb, fbl->taa_history_fb); \
- SWAP(struct GPUFrameBuffer *, fbl->main_color_fb, fbl->taa_history_color_fb); \
- SWAP(GPUTexture *, txl->color, txl->taa_history); \
- effects->source_buffer = txl->taa_history; \
- effects->target_buffer = fbl->main_color_fb; \
- } \
- } \
- ((void)0)
-
-BLI_INLINE bool eevee_hdri_preview_overlay_enabled(const View3D *v3d)
-{
- /* Only show the HDRI Preview in Shading Preview in the Viewport. */
- if (v3d == NULL || 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;
- }
-
- return ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (v3d->overlay.flag & V3D_OVERLAY_LOOK_DEV);
-}
-
-#define USE_SCENE_LIGHT(v3d) \
- ((!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)))
-#define LOOK_DEV_STUDIO_LIGHT_ENABLED(v3d) \
- ((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))))
-
-#define MIN_CUBE_LOD_LEVEL 3
-#define MAX_SCREEN_BUFFERS_LOD_LEVEL 6
-
-/* All the renderpasses that use the GPUMaterial for accumulation */
-#define EEVEE_RENDERPASSES_MATERIAL \
- (EEVEE_RENDER_PASS_EMIT | EEVEE_RENDER_PASS_DIFFUSE_COLOR | EEVEE_RENDER_PASS_DIFFUSE_LIGHT | \
- EEVEE_RENDER_PASS_SPECULAR_COLOR | EEVEE_RENDER_PASS_SPECULAR_LIGHT | \
- EEVEE_RENDER_PASS_ENVIRONMENT | EEVEE_RENDER_PASS_AOV)
-#define EEVEE_AOV_HASH_ALL -1
-#define EEVEE_AOV_HASH_COLOR_TYPE_MASK 1
-#define MAX_CRYPTOMATTE_LAYERS 3
-
-/* Material shader variations */
-enum {
- VAR_MAT_MESH = (1 << 0),
- VAR_MAT_VOLUME = (1 << 1),
- VAR_MAT_HAIR = (1 << 2),
- VAR_MAT_POINTCLOUD = (1 << 3),
- VAR_MAT_BLEND = (1 << 4),
- VAR_MAT_LOOKDEV = (1 << 5),
- VAR_MAT_HOLDOUT = (1 << 6),
- VAR_MAT_HASH = (1 << 7),
- VAR_MAT_DEPTH = (1 << 8),
- VAR_MAT_REFRACT = (1 << 9),
- VAR_WORLD_BACKGROUND = (1 << 10),
- VAR_WORLD_PROBE = (1 << 11),
- VAR_WORLD_VOLUME = (1 << 12),
- VAR_DEFAULT = (1 << 13),
-};
-
-/* Material shader cache keys */
-enum {
- /* HACK: This assumes the struct GPUShader will never be smaller than our variations.
- * This allow us to only keep one #GHash and avoid bigger keys comparisons/hashing.
- * We combine the #GPUShader pointer with the key. */
- KEY_CULL = (1 << 0),
- KEY_REFRACT = (1 << 1),
- KEY_HAIR = (1 << 2),
- KEY_SHADOW = (1 << 3),
-};
-
-/* DOF Gather pass shader variations */
-typedef enum EEVEE_DofGatherPass {
- DOF_GATHER_FOREGROUND = 0,
- DOF_GATHER_BACKGROUND = 1,
- DOF_GATHER_HOLEFILL = 2,
-
- DOF_GATHER_MAX_PASS,
-} EEVEE_DofGatherPass;
-
-#define DOF_TILE_DIVISOR 16
-#define DOF_BOKEH_LUT_SIZE 32
-#define DOF_GATHER_RING_COUNT 5
-#define DOF_DILATE_RING_COUNT 3
-#define DOF_FAST_GATHER_COC_ERROR 0.05
-
-#define DOF_SHADER_DEFINES \
- "#define DOF_TILE_DIVISOR " STRINGIFY(DOF_TILE_DIVISOR) "\n" \
- "#define DOF_BOKEH_LUT_SIZE " STRINGIFY(DOF_BOKEH_LUT_SIZE) "\n" \
- "#define DOF_GATHER_RING_COUNT " STRINGIFY(DOF_GATHER_RING_COUNT) "\n" \
- "#define DOF_DILATE_RING_COUNT " STRINGIFY(DOF_DILATE_RING_COUNT) "\n" \
- "#define DOF_FAST_GATHER_COC_ERROR " STRINGIFY(DOF_FAST_GATHER_COC_ERROR) "\n"
-
-/* ************ PROBE UBO ************* */
-
-/* They are the same struct as their Cache siblings.
- * typedef'ing just to keep the naming consistent with
- * other eevee types. */
-typedef LightProbeCache EEVEE_LightProbe;
-typedef LightGridCache EEVEE_LightGrid;
-
-typedef struct EEVEE_PlanarReflection {
- float plane_equation[4];
- float clip_vec_x[3], attenuation_scale;
- float clip_vec_y[3], attenuation_bias;
- float clip_edge_x_pos, clip_edge_x_neg;
- float clip_edge_y_pos, clip_edge_y_neg;
- float facing_scale, facing_bias, clipsta, pad;
- float reflectionmat[4][4]; /* Used for sampling the texture. */
- float mtx[4][4]; /* Not used in shader. TODO: move elsewhere. */
-} EEVEE_PlanarReflection;
-
-/* --------------------------------------- */
-
-typedef struct EEVEE_BoundBox {
- float center[3], halfdim[3];
-} EEVEE_BoundBox;
-
-typedef struct EEVEE_PassList {
- /* Shadows */
- struct DRWPass *shadow_pass;
- struct DRWPass *shadow_accum_pass;
-
- /* Probes */
- struct DRWPass *probe_background;
- struct DRWPass *probe_glossy_compute;
- struct DRWPass *probe_diffuse_compute;
- struct DRWPass *probe_visibility_compute;
- struct DRWPass *probe_grid_fill;
- struct DRWPass *probe_display;
- struct DRWPass *probe_planar_downsample_ps;
-
- /* Effects */
- struct DRWPass *ao_horizon_search;
- struct DRWPass *ao_horizon_debug;
- struct DRWPass *ao_accum_ps;
- struct DRWPass *mist_accum_ps;
- struct DRWPass *motion_blur;
- struct DRWPass *bloom_blit;
- struct DRWPass *bloom_downsample_first;
- struct DRWPass *bloom_downsample;
- struct DRWPass *bloom_upsample;
- struct DRWPass *bloom_resolve;
- struct DRWPass *bloom_accum_ps;
- struct DRWPass *dof_setup;
- struct DRWPass *dof_flatten_tiles;
- struct DRWPass *dof_dilate_tiles_minmax;
- struct DRWPass *dof_dilate_tiles_minabs;
- struct DRWPass *dof_reduce_copy;
- struct DRWPass *dof_downsample;
- struct DRWPass *dof_reduce;
- struct DRWPass *dof_bokeh;
- struct DRWPass *dof_gather_fg;
- struct DRWPass *dof_gather_fg_holefill;
- struct DRWPass *dof_gather_bg;
- struct DRWPass *dof_scatter_fg;
- struct DRWPass *dof_scatter_bg;
- struct DRWPass *dof_filter;
- struct DRWPass *dof_resolve;
- struct DRWPass *volumetric_world_ps;
- struct DRWPass *volumetric_objects_ps;
- struct DRWPass *volumetric_scatter_ps;
- struct DRWPass *volumetric_integration_ps;
- struct DRWPass *volumetric_resolve_ps;
- struct DRWPass *volumetric_accum_ps;
- struct DRWPass *ssr_raytrace;
- struct DRWPass *ssr_resolve;
- struct DRWPass *sss_blur_ps;
- struct DRWPass *sss_resolve_ps;
- struct DRWPass *sss_translucency_ps;
- struct DRWPass *color_copy_ps;
- struct DRWPass *color_downsample_ps;
- struct DRWPass *color_downsample_cube_ps;
- struct DRWPass *velocity_object;
- struct DRWPass *velocity_hair;
- struct DRWPass *velocity_resolve;
- struct DRWPass *velocity_tiles_x;
- struct DRWPass *velocity_tiles;
- struct DRWPass *velocity_tiles_expand[2];
- struct DRWPass *taa_resolve;
- struct DRWPass *alpha_checker;
-
- /* HiZ */
- struct DRWPass *maxz_downlevel_ps;
- struct DRWPass *maxz_copydepth_ps;
- struct DRWPass *maxz_copydepth_layer_ps;
-
- /* Renderpass Accumulation. */
- struct DRWPass *material_accum_ps;
- struct DRWPass *background_accum_ps;
- struct DRWPass *cryptomatte_ps;
-
- struct DRWPass *depth_ps;
- struct DRWPass *depth_cull_ps;
- struct DRWPass *depth_clip_ps;
- struct DRWPass *depth_clip_cull_ps;
- struct DRWPass *depth_refract_ps;
- struct DRWPass *depth_refract_cull_ps;
- struct DRWPass *depth_refract_clip_ps;
- struct DRWPass *depth_refract_clip_cull_ps;
- struct DRWPass *material_ps;
- struct DRWPass *material_cull_ps;
- struct DRWPass *material_refract_ps;
- struct DRWPass *material_refract_cull_ps;
- struct DRWPass *material_sss_ps;
- struct DRWPass *material_sss_cull_ps;
- struct DRWPass *transparent_pass;
- struct DRWPass *background_ps;
- struct DRWPass *update_noise_pass;
- struct DRWPass *lookdev_glossy_pass;
- struct DRWPass *lookdev_diffuse_pass;
- struct DRWPass *renderpass_pass;
-} EEVEE_PassList;
-
-typedef struct EEVEE_FramebufferList {
- /* Effects */
- struct GPUFrameBuffer *gtao_fb;
- struct GPUFrameBuffer *gtao_debug_fb;
- struct GPUFrameBuffer *downsample_fb;
- struct GPUFrameBuffer *maxzbuffer_fb;
- struct GPUFrameBuffer *bloom_blit_fb;
- struct GPUFrameBuffer *bloom_down_fb[MAX_BLOOM_STEP];
- struct GPUFrameBuffer *bloom_accum_fb[MAX_BLOOM_STEP - 1];
- struct GPUFrameBuffer *bloom_pass_accum_fb;
- struct GPUFrameBuffer *cryptomatte_fb;
- struct GPUFrameBuffer *shadow_accum_fb;
- struct GPUFrameBuffer *ssr_accum_fb;
- struct GPUFrameBuffer *sss_blur_fb;
- struct GPUFrameBuffer *sss_blit_fb;
- struct GPUFrameBuffer *sss_resolve_fb;
- struct GPUFrameBuffer *sss_clear_fb;
- struct GPUFrameBuffer *sss_translucency_fb;
- struct GPUFrameBuffer *sss_accum_fb;
- struct GPUFrameBuffer *dof_setup_fb;
- struct GPUFrameBuffer *dof_flatten_tiles_fb;
- struct GPUFrameBuffer *dof_dilate_tiles_fb;
- struct GPUFrameBuffer *dof_downsample_fb;
- struct GPUFrameBuffer *dof_reduce_fb;
- struct GPUFrameBuffer *dof_reduce_copy_fb;
- struct GPUFrameBuffer *dof_bokeh_fb;
- struct GPUFrameBuffer *dof_gather_fg_fb;
- struct GPUFrameBuffer *dof_filter_fg_fb;
- struct GPUFrameBuffer *dof_gather_fg_holefill_fb;
- struct GPUFrameBuffer *dof_gather_bg_fb;
- struct GPUFrameBuffer *dof_filter_bg_fb;
- struct GPUFrameBuffer *dof_scatter_fg_fb;
- struct GPUFrameBuffer *dof_scatter_bg_fb;
- struct GPUFrameBuffer *volumetric_fb;
- struct GPUFrameBuffer *volumetric_scat_fb;
- struct GPUFrameBuffer *volumetric_integ_fb;
- struct GPUFrameBuffer *volumetric_accum_fb;
- struct GPUFrameBuffer *screen_tracing_fb;
- struct GPUFrameBuffer *mist_accum_fb;
- struct GPUFrameBuffer *material_accum_fb;
- struct GPUFrameBuffer *renderpass_fb;
- struct GPUFrameBuffer *ao_accum_fb;
- struct GPUFrameBuffer *velocity_resolve_fb;
- struct GPUFrameBuffer *velocity_fb;
- struct GPUFrameBuffer *velocity_tiles_fb[2];
-
- struct GPUFrameBuffer *update_noise_fb;
-
- struct GPUFrameBuffer *planarref_fb;
- struct GPUFrameBuffer *planar_downsample_fb;
-
- struct GPUFrameBuffer *main_fb;
- struct GPUFrameBuffer *main_color_fb;
- struct GPUFrameBuffer *effect_fb;
- struct GPUFrameBuffer *effect_color_fb;
- struct GPUFrameBuffer *radiance_filtered_fb;
- struct GPUFrameBuffer *double_buffer_fb;
- struct GPUFrameBuffer *double_buffer_color_fb;
- struct GPUFrameBuffer *double_buffer_depth_fb;
- struct GPUFrameBuffer *taa_history_fb;
- struct GPUFrameBuffer *taa_history_color_fb;
-} EEVEE_FramebufferList;
-
-typedef struct EEVEE_TextureList {
- /* Effects */
- struct GPUTexture *color_post; /* R16_G16_B16 */
- struct GPUTexture *mist_accum;
- struct GPUTexture *ao_accum;
- struct GPUTexture *sss_accum;
- struct GPUTexture *env_accum;
- struct GPUTexture *diff_color_accum;
- struct GPUTexture *diff_light_accum;
- struct GPUTexture *spec_color_accum;
- struct GPUTexture *spec_light_accum;
- struct GPUTexture *aov_surface_accum[MAX_AOVS];
- struct GPUTexture *emit_accum;
- struct GPUTexture *bloom_accum;
- struct GPUTexture *ssr_accum;
- struct GPUTexture *shadow_accum;
- struct GPUTexture *cryptomatte;
- struct GPUTexture *taa_history;
- /* Could not be pool texture because of mipmapping. */
- struct GPUTexture *dof_reduced_color;
- struct GPUTexture *dof_reduced_coc;
-
- struct GPUTexture *volume_prop_scattering;
- struct GPUTexture *volume_prop_extinction;
- struct GPUTexture *volume_prop_emission;
- struct GPUTexture *volume_prop_phase;
- struct GPUTexture *volume_scatter;
- struct GPUTexture *volume_transmit;
- struct GPUTexture *volume_scatter_history;
- struct GPUTexture *volume_transmit_history;
- struct GPUTexture *volume_scatter_accum;
- struct GPUTexture *volume_transmittance_accum;
-
- struct GPUTexture *lookdev_grid_tx;
- struct GPUTexture *lookdev_cube_tx;
-
- struct GPUTexture *planar_pool;
- struct GPUTexture *planar_depth;
-
- struct GPUTexture *maxzbuffer;
- struct GPUTexture *filtered_radiance;
-
- struct GPUTexture *renderpass;
-
- struct GPUTexture *color; /* R16_G16_B16 */
- struct GPUTexture *color_double_buffer;
- struct GPUTexture *depth_double_buffer;
-} EEVEE_TextureList;
-
-typedef struct EEVEE_StorageList {
- /* Effects */
- struct EEVEE_EffectsInfo *effects;
-
- struct EEVEE_PrivateData *g_data;
-
- struct LightCache *lookdev_lightcache;
- EEVEE_LightProbe *lookdev_cube_data;
- EEVEE_LightGrid *lookdev_grid_data;
- LightCacheTexture *lookdev_cube_mips;
-} EEVEE_StorageList;
-
-/* ************ RENDERPASS UBO ************* */
-typedef struct EEVEE_RenderPassData {
- int renderPassDiffuse;
- int renderPassDiffuseLight;
- int renderPassGlossy;
- int renderPassGlossyLight;
- int renderPassEmit;
- int renderPassSSSColor;
- int renderPassEnvironment;
- int renderPassAOV;
- int renderPassAOVActive;
- int _pad[3];
-} EEVEE_RenderPassData;
-
-/* ************ LIGHT UBO ************* */
-typedef struct EEVEE_Light {
- float position[3], invsqrdist;
- float color[3], invsqrdist_volume;
- float spotsize, spotblend, radius, shadow_id;
- float rightvec[3], sizex;
- float upvec[3], sizey;
- float forwardvec[3], light_type;
- float diff, spec, volume, volume_radius;
-} EEVEE_Light;
-
-/* Special type for elliptic area lights, matches lamps_lib.glsl */
-#define LAMPTYPE_AREA_ELLIPSE 100.0f
-
-typedef struct EEVEE_Shadow {
- float near, far, bias, type_data_id;
- float contact_dist, contact_bias, contact_spread, contact_thickness;
-} EEVEE_Shadow;
-
-typedef struct EEVEE_ShadowCube {
- float shadowmat[4][4];
- float position[3], _pad0[1];
-} EEVEE_ShadowCube;
-
-typedef struct EEVEE_ShadowCascade {
- /* World->Light->NDC->Tex : used for sampling the shadow map. */
- float shadowmat[MAX_CASCADE_NUM][4][4];
- float split_start[4];
- float split_end[4];
- float shadow_vec[3], tex_id;
-} EEVEE_ShadowCascade;
-
-typedef struct EEVEE_ShadowCascadeRender {
- /* World->Light->NDC : used for rendering the shadow map. */
- float projmat[MAX_CASCADE_NUM][4][4];
- float viewmat[4][4], viewinv[4][4];
- float radius[MAX_CASCADE_NUM];
- float original_bias;
- float cascade_max_dist;
- float cascade_exponent;
- float cascade_fade;
- int cascade_count;
-} EEVEE_ShadowCascadeRender;
-
-BLI_STATIC_ASSERT_ALIGN(EEVEE_Light, 16)
-BLI_STATIC_ASSERT_ALIGN(EEVEE_Shadow, 16)
-BLI_STATIC_ASSERT_ALIGN(EEVEE_ShadowCube, 16)
-BLI_STATIC_ASSERT_ALIGN(EEVEE_ShadowCascade, 16)
-BLI_STATIC_ASSERT_ALIGN(EEVEE_RenderPassData, 16)
-
-BLI_STATIC_ASSERT(sizeof(EEVEE_Shadow) * MAX_SHADOW +
- sizeof(EEVEE_ShadowCascade) * MAX_SHADOW_CASCADE +
- sizeof(EEVEE_ShadowCube) * MAX_SHADOW_CUBE <
- 16384,
- "Shadow UBO is too big!!!")
-
-typedef struct EEVEE_ShadowCasterBuffer {
- struct EEVEE_BoundBox *bbox;
- BLI_bitmap *update;
- uint alloc_count;
- uint count;
-} EEVEE_ShadowCasterBuffer;
-
-/* ************ LIGHT DATA ************* */
-typedef struct EEVEE_LightsInfo {
- int num_light, cache_num_light;
- int num_cube_layer, cache_num_cube_layer;
- int num_cascade_layer, cache_num_cascade_layer;
- int cube_len, cascade_len, shadow_len;
- int shadow_cube_size, shadow_cascade_size;
- bool shadow_high_bitdepth, soft_shadows;
- /* UBO Storage : data used by UBO */
- struct EEVEE_Light light_data[MAX_LIGHT];
- struct EEVEE_Shadow shadow_data[MAX_SHADOW];
- struct EEVEE_ShadowCube shadow_cube_data[MAX_SHADOW_CUBE];
- struct EEVEE_ShadowCascade shadow_cascade_data[MAX_SHADOW_CASCADE];
- /* Additional rendering info for cascade. */
- struct EEVEE_ShadowCascadeRender shadow_cascade_render[MAX_SHADOW_CASCADE];
- /* Back index in light_data. */
- uchar shadow_cube_light_indices[MAX_SHADOW_CUBE];
- uchar shadow_cascade_light_indices[MAX_SHADOW_CASCADE];
- /* Update bitmap. */
- BLI_bitmap sh_cube_update[BLI_BITMAP_SIZE(MAX_SHADOW_CUBE)];
- /* Lights tracking */
- struct BoundSphere shadow_bounds[MAX_LIGHT]; /* Tightly packed light bounds. */
- /* List of bbox and update bitmap. Double buffered. */
- struct EEVEE_ShadowCasterBuffer *shcaster_frontbuffer, *shcaster_backbuffer;
- /* AABB of all shadow casters combined. */
- struct {
- float min[3], max[3];
- } shcaster_aabb;
-} EEVEE_LightsInfo;
-
-/* ************ PROBE DATA ************* */
-typedef struct EEVEE_LightProbeVisTest {
- struct Collection *collection; /* Skip test if NULL */
- bool invert;
- bool cached; /* Reuse last test results */
-} EEVEE_LightProbeVisTest;
-
-typedef struct EEVEE_LightProbesInfo {
- int num_cube, cache_num_cube;
- int num_grid, cache_num_grid;
- int num_planar, cache_num_planar;
- int total_irradiance_samples; /* Total for all grids */
- int cache_irradiance_size[3];
- int update_flag;
- int updated_bounce;
- int num_bounce;
- int cubemap_res;
- /* Update */
- bool do_cube_update;
- bool do_grid_update;
- /* For rendering probes */
- float probemat[6][4][4];
- int layer;
- float texel_size;
- float padding_size;
- float samples_len;
- float near_clip;
- float far_clip;
- float roughness;
- float firefly_fac;
- float lodfactor;
- float lod_rt_max, lod_cube_max;
- float visibility_range;
- float visibility_blur;
- float intensity_fac;
- int shres;
- EEVEE_LightProbeVisTest planar_vis_tests[MAX_PLANAR];
- /* UBO Storage : data used by UBO */
- EEVEE_LightProbe probe_data[MAX_PROBE];
- EEVEE_LightGrid grid_data[MAX_GRID];
- EEVEE_PlanarReflection planar_data[MAX_PLANAR];
- /* Probe Visibility Collection */
- EEVEE_LightProbeVisTest vis_data;
-} EEVEE_LightProbesInfo;
-
-/* EEVEE_LightProbesInfo->update_flag */
-enum {
- PROBE_UPDATE_CUBE = (1 << 0),
- PROBE_UPDATE_GRID = (1 << 1),
- PROBE_UPDATE_ALL = 0xFFFFFF,
-};
-
-/* ************** MOTION BLUR ************ */
-
-#define MB_PREV 0
-#define MB_NEXT 1
-#define MB_CURR 2
-
-typedef struct EEVEE_MotionBlurData {
- struct GHash *object;
- struct GHash *geom;
- struct {
- float viewmat[4][4];
- float persmat[4][4];
- float persinv[4][4];
- } camera[3];
- DRWShadingGroup *hair_grp;
-} EEVEE_MotionBlurData;
-
-typedef struct EEVEE_ObjectKey {
- /** Object or source object for duplis */
- struct Object *ob;
- /** Parent object for duplis */
- struct Object *parent;
- /** Dupli objects recursive unique identifier */
- int id[8]; /* MAX_DUPLI_RECUR */
-} EEVEE_ObjectKey;
-
-typedef struct EEVEE_ObjectMotionData {
- float obmat[3][4][4];
-} EEVEE_ObjectMotionData;
-
-typedef enum eEEVEEMotionData {
- EEVEE_MOTION_DATA_MESH = 0,
- EEVEE_MOTION_DATA_HAIR,
-} eEEVEEMotionData;
-
-typedef struct EEVEE_HairMotionData {
- /** Needs to be first to ensure casting. */
- eEEVEEMotionData type;
- int use_deform;
- /** Allocator will alloc enough slot for all particle systems. Or 1 if it's a hair object. */
- int psys_len;
- struct {
- struct GPUVertBuf *hair_pos[2]; /* Position buffer for time = t +/- step. */
- struct GPUTexture *hair_pos_tx[2]; /* Buffer Texture of the corresponding VBO. */
- } psys[0];
-} EEVEE_HairMotionData;
-
-typedef struct EEVEE_GeometryMotionData {
- /** Needs to be first to ensure casting. */
- eEEVEEMotionData type;
- /** To disable deform mb if vertcount mismatch. */
- int use_deform;
-
- struct GPUBatch *batch; /* Batch for time = t. */
- struct GPUVertBuf *vbo[2]; /* VBO for time = t +/- step. */
-} EEVEE_GeometryMotionData;
-
-/* ************ EFFECTS DATA ************* */
-
-typedef enum EEVEE_EffectsFlag {
- EFFECT_MOTION_BLUR = (1 << 0),
- EFFECT_BLOOM = (1 << 1),
- EFFECT_DOF = (1 << 2),
- EFFECT_VOLUMETRIC = (1 << 3),
- EFFECT_SSR = (1 << 4),
- EFFECT_DOUBLE_BUFFER = (1 << 5), /* Not really an effect but a feature */
- EFFECT_REFRACT = (1 << 6),
- EFFECT_GTAO = (1 << 7),
- EFFECT_TAA = (1 << 8),
- EFFECT_POST_BUFFER = (1 << 9), /* Not really an effect but a feature */
- EFFECT_NORMAL_BUFFER = (1 << 10), /* Not really an effect but a feature */
- EFFECT_RADIANCE_BUFFER = (1 << 10), /* Not really an effect but a feature */
- EFFECT_SSS = (1 << 11),
- EFFECT_VELOCITY_BUFFER = (1 << 12), /* Not really an effect but a feature */
- EFFECT_TAA_REPROJECT = (1 << 13), /* should be mutually exclusive with EFFECT_TAA */
- EFFECT_DEPTH_DOUBLE_BUFFER = (1 << 14), /* Not really an effect but a feature */
-} EEVEE_EffectsFlag;
-
-typedef struct EEVEE_EffectsInfo {
- EEVEE_EffectsFlag enabled_effects;
- bool swap_double_buffer;
- /* SSSS */
- int sss_sample_count;
- int sss_surface_count;
- struct GPUTexture *sss_irradiance; /* Textures from pool */
- struct GPUTexture *sss_radius;
- struct GPUTexture *sss_albedo;
- struct GPUTexture *sss_blur;
- struct GPUTexture *sss_stencil;
- /* Volumetrics */
- int volume_current_sample;
- float volume_light_clamp;
- struct GPUTexture *volume_scatter;
- struct GPUTexture *volume_transmit;
- /* SSR */
- bool reflection_trace_full;
- bool ssr_was_persp;
- bool ssr_was_valid_double_buffer;
- struct GPUTexture *ssr_normal_input; /* Textures from pool */
- struct GPUTexture *ssr_specrough_input;
- struct GPUTexture *ssr_hit_output;
- struct GPUTexture *ssr_hit_depth;
- /* Temporal Anti Aliasing */
- int taa_reproject_sample;
- int taa_current_sample;
- int taa_render_sample;
- int taa_total_sample;
- float taa_alpha;
- bool bypass_drawing;
- bool prev_drw_support;
- bool prev_is_navigating;
- float prev_drw_persmat[4][4]; /* Used for checking view validity and reprojection. */
- struct DRWView *taa_view;
- /* Ambient Occlusion */
- struct GPUTexture *gtao_horizons; /* Textures from pool */
- struct GPUTexture *gtao_horizons_renderpass; /* Texture when rendering render pass */
- struct GPUTexture *gtao_horizons_debug;
- /* Motion Blur */
- float current_ndc_to_world[4][4];
- float current_world_to_ndc[4][4];
- float current_world_to_view[4][4];
- float past_world_to_ndc[4][4];
- float past_world_to_view[4][4];
- CameraParams past_cam_params;
- CameraParams current_cam_params;
- char motion_blur_step; /* Which step we are evaluating. */
- int motion_blur_max; /* Maximum distance in pixels a motion-blurred pixel can cover. */
- float motion_blur_near_far[2]; /* Camera near/far clip distances (positive). */
- bool cam_params_init;
- /* TODO(fclem): Only used in render mode for now.
- * This is because we are missing a per scene persistent place to hold this. */
- struct EEVEE_MotionBlurData motion_blur;
- /* Velocity Pass */
- struct GPUTexture *velocity_tx; /* Texture from pool */
- struct GPUTexture *velocity_tiles_x_tx;
- struct GPUTexture *velocity_tiles_tx;
- /* Depth Of Field */
- float dof_jitter_radius;
- float dof_jitter_blades;
- float dof_jitter_focus;
- int dof_jitter_ring_count;
- float dof_coc_params[2], dof_coc_near_dist, dof_coc_far_dist;
- float dof_bokeh_blades, dof_bokeh_rotation, dof_bokeh_aniso[2], dof_bokeh_max_size;
- float dof_bokeh_aniso_inv[2];
- float dof_scatter_color_threshold;
- float dof_scatter_coc_threshold;
- float dof_scatter_neighbor_max_color;
- float dof_fx_max_coc;
- float dof_denoise_factor;
- int dof_dilate_slight_focus;
- int dof_dilate_ring_count;
- int dof_dilate_ring_width_multiplier;
- int dof_reduce_steps;
- bool dof_hq_slight_focus;
- eGPUTextureFormat dof_color_format;
- struct GPUTexture *dof_bg_color_tx; /* All textures from pool... */
- struct GPUTexture *dof_bg_occlusion_tx;
- struct GPUTexture *dof_bg_weight_tx;
- struct GPUTexture *dof_bokeh_gather_lut_tx;
- struct GPUTexture *dof_bokeh_scatter_lut_tx;
- struct GPUTexture *dof_bokeh_resolve_lut_tx;
- struct GPUTexture *dof_coc_dilated_tiles_bg_tx;
- struct GPUTexture *dof_coc_dilated_tiles_fg_tx;
- struct GPUTexture *dof_coc_tiles_bg_tx;
- struct GPUTexture *dof_coc_tiles_fg_tx;
- struct GPUTexture *dof_downsample_tx;
- struct GPUTexture *dof_fg_color_tx;
- struct GPUTexture *dof_fg_occlusion_tx;
- struct GPUTexture *dof_fg_weight_tx;
- struct GPUTexture *dof_fg_holefill_color_tx;
- struct GPUTexture *dof_fg_holefill_weight_tx;
- struct GPUTexture *dof_half_res_coc_tx;
- struct GPUTexture *dof_half_res_color_tx;
- struct GPUTexture *dof_scatter_src_tx;
- struct GPUTexture *dof_reduce_input_coc_tx; /* Just references to actual textures. */
- struct GPUTexture *dof_reduce_input_color_tx;
- /* Other */
- float prev_persmat[4][4];
- /* Size used by all fullscreen buffers using mipmaps. */
- int hiz_size[2];
- /* Lookdev */
- int sphere_size;
- eDRWLevelOfDetail sphere_lod;
- int anchor[2];
- struct DRWView *lookdev_view;
- /* Bloom */
- int bloom_iteration_len;
- float source_texel_size[2];
- float blit_texel_size[2];
- float downsamp_texel_size[MAX_BLOOM_STEP][2];
- float bloom_color[3];
- float bloom_clamp;
- float bloom_sample_scale;
- float bloom_curve_threshold[4];
- float unf_source_texel_size[2];
- struct GPUTexture *bloom_blit; /* Textures from pool */
- struct GPUTexture *bloom_downsample[MAX_BLOOM_STEP];
- struct GPUTexture *bloom_upsample[MAX_BLOOM_STEP - 1];
- struct GPUTexture *unf_source_buffer; /* pointer copy */
- struct GPUTexture *unf_base_buffer; /* pointer copy */
- /* Not alloced, just a copy of a *GPUtexture in EEVEE_TextureList. */
- struct GPUTexture *source_buffer; /* latest updated texture */
- struct GPUFrameBuffer *target_buffer; /* next target to render to */
- struct GPUTexture *final_tx; /* Final color to transform to display color space. */
- struct GPUFrameBuffer *final_fb; /* Frame-buffer with final_tx as attachment. */
-} EEVEE_EffectsInfo;
-
-/* ***************** COMMON DATA **************** */
-
-/* Common uniform buffer containing all "constant" data over the whole drawing pipeline. */
-/* !! CAUTION !!
- * - [i]vec3 need to be padded to [i]vec4 (even in ubo declaration).
- * - Make sure that [i]vec4 start at a multiple of 16 bytes.
- * - Arrays of vec2/vec3 are padded as arrays of vec4.
- * - sizeof(bool) == sizeof(int) in GLSL so use int in C */
-typedef struct EEVEE_CommonUniformBuffer {
- float prev_persmat[4][4]; /* mat4 */
- float hiz_uv_scale[2], ssr_uv_scale[2]; /* vec4 */
- /* Ambient Occlusion */
- /* -- 16 byte aligned -- */
- float ao_dist, pad1, ao_factor, pad2; /* vec4 */
- float ao_offset, ao_bounce_fac, ao_quality, ao_settings; /* vec4 */
- /* Volumetric */
- /* -- 16 byte aligned -- */
- int vol_tex_size[3], pad3; /* ivec3 */
- float vol_depth_param[3], pad4; /* vec3 */
- float vol_inv_tex_size[3], pad5; /* vec3 */
- float vol_jitter[3], pad6; /* vec3 */
- float vol_coord_scale[4]; /* vec4 */
- /* -- 16 byte aligned -- */
- float vol_history_alpha; /* float */
- float vol_shadow_steps; /* float */
- int vol_use_lights; /* bool */
- int vol_use_soft_shadows; /* bool */
- /* Screen Space Reflections */
- /* -- 16 byte aligned -- */
- float ssr_quality, ssr_thickness, ssr_pixelsize[2]; /* vec4 */
- float ssr_border_fac; /* float */
- float ssr_max_roughness; /* float */
- float ssr_firefly_fac; /* float */
- float ssr_brdf_bias; /* float */
- int ssr_toggle; /* bool */
- int ssrefract_toggle; /* bool */
- /* SubSurface Scattering */
- float sss_jitter_threshold; /* float */
- int sss_toggle; /* bool */
- /* Specular */
- int spec_toggle; /* bool */
- /* Lights */
- int la_num_light; /* int */
- /* Probes */
- int prb_num_planar; /* int */
- int prb_num_render_cube; /* int */
- int prb_num_render_grid; /* int */
- int prb_irradiance_vis_size; /* int */
- float prb_irradiance_smooth; /* float */
- float prb_lod_cube_max; /* float */
- /* Misc */
- int ray_type; /* int */
- float ray_depth; /* float */
- float alpha_hash_offset; /* float */
- float alpha_hash_scale; /* float */
- float pad7; /* float */
- float pad8; /* float */
- float pad9; /* float */
- float pad10; /* float */
-} EEVEE_CommonUniformBuffer;
-
-BLI_STATIC_ASSERT_ALIGN(EEVEE_CommonUniformBuffer, 16)
-
-/* ray_type (keep in sync with rayType) */
-#define EEVEE_RAY_CAMERA 0
-#define EEVEE_RAY_SHADOW 1
-#define EEVEE_RAY_DIFFUSE 2
-#define EEVEE_RAY_GLOSSY 3
-
-/* ************** SCENE LAYER DATA ************** */
-typedef struct EEVEE_ViewLayerData {
- /* Lights */
- struct EEVEE_LightsInfo *lights;
-
- struct GPUUniformBuf *light_ubo;
- struct GPUUniformBuf *shadow_ubo;
- struct GPUUniformBuf *shadow_samples_ubo;
-
- struct GPUFrameBuffer *shadow_fb;
-
- struct GPUTexture *shadow_cube_pool;
- struct GPUTexture *shadow_cascade_pool;
-
- struct EEVEE_ShadowCasterBuffer shcasters_buffers[2];
-
- /* Probes */
- struct EEVEE_LightProbesInfo *probes;
-
- struct GPUUniformBuf *probe_ubo;
- struct GPUUniformBuf *grid_ubo;
- struct GPUUniformBuf *planar_ubo;
-
- /* Material Render passes */
- struct {
- struct GPUUniformBuf *combined;
- struct GPUUniformBuf *environment;
- struct GPUUniformBuf *diff_color;
- struct GPUUniformBuf *diff_light;
- struct GPUUniformBuf *spec_color;
- struct GPUUniformBuf *spec_light;
- struct GPUUniformBuf *emit;
- struct GPUUniformBuf *aovs[MAX_AOVS];
- } renderpass_ubo;
-
- /* Common Uniform Buffer */
- struct EEVEE_CommonUniformBuffer common_data;
- struct GPUUniformBuf *common_ubo;
-
- struct LightCache *fallback_lightcache;
-
- struct BLI_memblock *material_cache;
-} EEVEE_ViewLayerData;
-
-/* ************ OBJECT DATA ************ */
-
-/* These are the structs stored inside Objects.
- * It works even if the object is in multiple layers
- * because we don't get the same "Object *" for each layer. */
-typedef struct EEVEE_LightEngineData {
- DrawData dd;
-
- bool need_update;
-} EEVEE_LightEngineData;
-
-typedef struct EEVEE_LightProbeEngineData {
- DrawData dd;
-
- bool need_update;
-} EEVEE_LightProbeEngineData;
-
-typedef struct EEVEE_ObjectEngineData {
- DrawData dd;
-
- Object *ob; /* self reference */
- EEVEE_LightProbeVisTest *test_data;
- bool ob_vis, ob_vis_dirty;
-
- bool need_update;
- bool geom_update;
- uint shadow_caster_id;
-} EEVEE_ObjectEngineData;
-
-typedef struct EEVEE_WorldEngineData {
- DrawData dd;
-} EEVEE_WorldEngineData;
-
-typedef struct EEVEE_CryptomatteSample {
- float hash;
- float weight;
-} EEVEE_CryptomatteSample;
-
-/* *********************************** */
-
-typedef struct EEVEE_Data {
- void *engine_type;
- EEVEE_FramebufferList *fbl;
- EEVEE_TextureList *txl;
- EEVEE_PassList *psl;
- EEVEE_StorageList *stl;
- void *instance_data;
-
- char info[GPU_INFO_SIZE];
-} EEVEE_Data;
-
-typedef struct EEVEE_PrivateData {
- struct DRWShadingGroup *shadow_shgrp;
- struct DRWShadingGroup *shadow_accum_shgrp;
- struct DRWCallBuffer *planar_display_shgrp;
- struct GHash *material_hash;
- float background_alpha; /* TODO: find a better place for this. */
- /* Chosen lightcache: can come from Lookdev or the viewlayer. */
- struct LightCache *light_cache;
- /* For planar probes */
- float planar_texel_size[2];
- /* For double buffering */
- bool view_updated;
- bool valid_double_buffer;
- bool valid_taa_history;
- /* Render Matrices */
- float studiolight_matrix[3][3];
- float overscan, overscan_pixels;
- float camtexcofac[4];
- float size_orig[2];
-
- /* Cached original camera when rendering for motion blur (see T79637). */
- struct Object *cam_original_ob;
-
- /* Mist Settings */
- float mist_start, mist_inv_dist, mist_falloff;
-
- /* Color Management */
- bool use_color_render_settings;
-
- /* Compiling shaders count. This is to track if a shader has finished compiling. */
- int queued_shaders_count;
- int queued_shaders_count_prev;
-
- /* LookDev Settings */
- int studiolight_index;
- float studiolight_rot_z;
- float studiolight_intensity;
- int studiolight_cubemap_res;
- float studiolight_glossy_clamp;
- float studiolight_filter_quality;
-
- /* Renderpasses */
- /* Bitmask containing the active render_passes */
- eViewLayerEEVEEPassType render_passes;
- int aov_hash;
- int num_aovs_used;
- struct CryptomatteSession *cryptomatte_session;
- bool cryptomatte_accurate_mode;
- EEVEE_CryptomatteSample *cryptomatte_accum_buffer;
- float *cryptomatte_download_buffer;
-
- /* Uniform references that are referenced inside the `renderpass_pass`. They are updated
- * to reuse the drawing pass and the shading group. */
- int renderpass_type;
- int renderpass_postprocess;
- int renderpass_current_sample;
- GPUTexture *renderpass_input;
- GPUTexture *renderpass_col_input;
- GPUTexture *renderpass_light_input;
- GPUTexture *renderpass_transmittance_input;
- /* Renderpass ubo reference used by material pass. */
- struct GPUUniformBuf *renderpass_ubo;
- /** For rendering shadows. */
- struct DRWView *cube_views[6];
- /** For rendering probes. */
- struct DRWView *bake_views[6];
- /** Same as bake_views but does not generate culling infos. */
- struct DRWView *world_views[6];
- /** For rendering planar reflections. */
- struct DRWView *planar_views[MAX_PLANAR];
-
- int render_timesteps;
- int render_sample_count_per_timestep;
-} EEVEE_PrivateData; /* Transient data */
-
-/* eevee_data.c */
-
-void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb);
-void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb);
-void EEVEE_view_layer_data_free(void *storage);
-EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void);
-EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer);
-EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure(void);
-EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob);
-EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob);
-EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb,
- Object *ob,
- bool hair);
-EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb,
- Object *ob);
-EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_MotionBlurData *mb, Object *ob);
-EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob);
-EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_ensure(Object *ob);
-EEVEE_LightEngineData *EEVEE_light_data_get(Object *ob);
-EEVEE_LightEngineData *EEVEE_light_data_ensure(Object *ob);
-EEVEE_WorldEngineData *EEVEE_world_data_get(World *wo);
-EEVEE_WorldEngineData *EEVEE_world_data_ensure(World *wo);
-
-void eevee_id_update(void *vedata, ID *id);
-
-/* eevee_materials.c */
-
-struct GPUTexture *EEVEE_materials_get_util_tex(void); /* XXX */
-void EEVEE_materials_init(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- EEVEE_StorageList *stl,
- EEVEE_FramebufferList *fbl);
-void EEVEE_materials_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_materials_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob,
- bool *cast_shadow);
-void EEVEE_particle_hair_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob,
- bool *cast_shadow);
-void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob,
- bool *cast_shadow);
-void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_materials_free(void);
-void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const double offsets[3]);
-void EEVEE_material_renderpasses_init(EEVEE_Data *vedata);
-void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples);
-void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-/**
- * \param ssr_id: Can be null to disable SSR contribution.
- */
-void EEVEE_material_bind_resources(DRWShadingGroup *shgrp,
- struct GPUMaterial *gpumat,
- EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- const int *ssr_id,
- const float *refract_depth,
- bool use_ssrefraction,
- bool use_alpha_blend);
-/* eevee_lights.c */
-
-/**
- * Reconstruct local `obmat` from EEVEE_light. (normalized).
- */
-void eevee_light_matrix_get(const EEVEE_Light *evli, float r_mat[4][4]);
-void EEVEE_lights_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_lights_cache_add(EEVEE_ViewLayerData *sldata, struct Object *ob);
-void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-
-/* eevee_shadows.c */
-
-void eevee_contact_shadow_setup(const Light *la, EEVEE_Shadow *evsh);
-void EEVEE_shadows_init(EEVEE_ViewLayerData *sldata);
-void EEVEE_shadows_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-/**
- * Make that object update shadow casting lights inside its influence bounding box.
- */
-void EEVEE_shadows_caster_register(EEVEE_ViewLayerData *sldata, struct Object *ob);
-void EEVEE_shadows_update(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_shadows_cube_add(EEVEE_LightsInfo *linfo, EEVEE_Light *evli, struct Object *ob);
-/**
- * Return true if sample has changed and light needs to be updated.
- */
-bool EEVEE_shadows_cube_setup(EEVEE_LightsInfo *linfo, const EEVEE_Light *evli, int sample_ofs);
-void EEVEE_shadows_cascade_add(EEVEE_LightsInfo *linfo, EEVEE_Light *evli, struct Object *ob);
-/**
- * This refresh lights shadow buffers.
- */
-void EEVEE_shadows_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, struct DRWView *view);
-void EEVEE_shadows_draw_cubemap(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, int cube_index);
-void EEVEE_shadows_draw_cascades(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- DRWView *view,
- int cascade_index);
-void EEVEE_shadow_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples);
-void EEVEE_shadow_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-
-/* eevee_sampling.c */
-
-/**
- * Special ball distribution:
- * Point are distributed in a way that when they are orthogonally
- * projected into any plane, the resulting distribution is (close to)
- * a uniform disc distribution.
- */
-void EEVEE_sample_ball(int sample_ofs, float radius, float rsample[3]);
-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]);
-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]);
-void EEVEE_random_rotation_m4(int sample_ofs, float scale, float r_mat[4][4]);
-
-/* eevee_shaders.c */
-
-void EEVEE_shaders_material_shaders_init(void);
-struct DRWShaderLibrary *EEVEE_shader_lib_get(void);
-struct GPUShader *EEVEE_shaders_bloom_blit_get(bool high_quality);
-struct GPUShader *EEVEE_shaders_bloom_downsample_get(bool high_quality);
-struct GPUShader *EEVEE_shaders_bloom_upsample_get(bool high_quality);
-struct GPUShader *EEVEE_shaders_bloom_resolve_get(bool high_quality);
-struct GPUShader *EEVEE_shaders_depth_of_field_bokeh_get(void);
-struct GPUShader *EEVEE_shaders_depth_of_field_setup_get(void);
-struct GPUShader *EEVEE_shaders_depth_of_field_flatten_tiles_get(void);
-struct GPUShader *EEVEE_shaders_depth_of_field_dilate_tiles_get(bool pass);
-struct GPUShader *EEVEE_shaders_depth_of_field_downsample_get(void);
-struct GPUShader *EEVEE_shaders_depth_of_field_reduce_get(bool is_copy_pass);
-struct GPUShader *EEVEE_shaders_depth_of_field_gather_get(EEVEE_DofGatherPass pass, bool bokeh_tx);
-struct GPUShader *EEVEE_shaders_depth_of_field_filter_get(void);
-struct GPUShader *EEVEE_shaders_depth_of_field_scatter_get(bool is_foreground, bool bokeh_tx);
-struct GPUShader *EEVEE_shaders_depth_of_field_resolve_get(bool use_bokeh_tx, bool use_hq_gather);
-struct GPUShader *EEVEE_shaders_effect_color_copy_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_downsample_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_downsample_cube_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_minz_downlevel_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_maxz_downlevel_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_minz_downdepth_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_maxz_downdepth_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_minz_downdepth_layer_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_maxz_downdepth_layer_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_maxz_copydepth_layer_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_minz_copydepth_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_maxz_copydepth_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_mist_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_motion_blur_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_motion_blur_object_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_motion_blur_hair_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_motion_blur_velocity_tiles_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_motion_blur_velocity_tiles_expand_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_ambient_occlusion_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_ambient_occlusion_debug_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_reflection_trace_sh_get(void);
-struct GPUShader *EEVEE_shaders_effect_reflection_resolve_sh_get(void);
-struct GPUShader *EEVEE_shaders_renderpasses_post_process_sh_get(void);
-struct GPUShader *EEVEE_shaders_cryptomatte_sh_get(bool is_hair);
-struct GPUShader *EEVEE_shaders_shadow_sh_get(void);
-struct GPUShader *EEVEE_shaders_shadow_accum_sh_get(void);
-struct GPUShader *EEVEE_shaders_subsurface_first_pass_sh_get(void);
-struct GPUShader *EEVEE_shaders_subsurface_second_pass_sh_get(void);
-struct GPUShader *EEVEE_shaders_subsurface_translucency_sh_get(void);
-struct GPUShader *EEVEE_shaders_volumes_clear_sh_get(void);
-struct GPUShader *EEVEE_shaders_volumes_scatter_sh_get(void);
-struct GPUShader *EEVEE_shaders_volumes_scatter_with_lights_sh_get(void);
-struct GPUShader *EEVEE_shaders_volumes_integration_sh_get(void);
-struct GPUShader *EEVEE_shaders_volumes_resolve_sh_get(bool accum);
-struct GPUShader *EEVEE_shaders_volumes_accum_sh_get(void);
-struct GPUShader *EEVEE_shaders_ggx_lut_sh_get(void);
-struct GPUShader *EEVEE_shaders_ggx_refraction_lut_sh_get(void);
-struct GPUShader *EEVEE_shaders_probe_filter_glossy_sh_get(void);
-struct GPUShader *EEVEE_shaders_probe_filter_diffuse_sh_get(void);
-struct GPUShader *EEVEE_shaders_probe_filter_visibility_sh_get(void);
-struct GPUShader *EEVEE_shaders_probe_grid_fill_sh_get(void);
-struct GPUShader *EEVEE_shaders_probe_planar_downsample_sh_get(void);
-struct GPUShader *EEVEE_shaders_studiolight_probe_sh_get(void);
-struct GPUShader *EEVEE_shaders_studiolight_background_sh_get(void);
-struct GPUShader *EEVEE_shaders_probe_cube_display_sh_get(void);
-struct GPUShader *EEVEE_shaders_probe_grid_display_sh_get(void);
-struct GPUShader *EEVEE_shaders_probe_planar_display_sh_get(void);
-struct GPUShader *EEVEE_shaders_update_noise_sh_get(void);
-struct GPUShader *EEVEE_shaders_velocity_resolve_sh_get(void);
-struct GPUShader *EEVEE_shaders_taa_resolve_sh_get(EEVEE_EffectsFlag enabled_effects);
-/**
- * Configure a default node-tree with the given material.
- */
-struct bNodeTree *EEVEE_shader_default_surface_nodetree(Material *ma);
-/**
- * Configure a default node-tree with the given world.
- */
-struct bNodeTree *EEVEE_shader_default_world_nodetree(World *wo);
-Material *EEVEE_material_default_diffuse_get(void);
-Material *EEVEE_material_default_glossy_get(void);
-Material *EEVEE_material_default_error_get(void);
-World *EEVEE_world_default_get(void);
-/**
- * \note Compilation is not deferred.
- */
-struct GPUMaterial *EEVEE_material_default_get(struct Scene *scene, Material *ma, int options);
-struct GPUMaterial *EEVEE_material_get(
- EEVEE_Data *vedata, struct Scene *scene, Material *ma, World *wo, int options);
-void EEVEE_shaders_free(void);
-
-/* eevee_lightprobes.c */
-
-bool EEVEE_lightprobes_obj_visibility_cb(bool vis_in, void *user_data);
-void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_lightprobes_cache_add(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *ob);
-void EEVEE_lightprobes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_lightprobes_refresh(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_lightprobes_free(void);
-
-/**
- * Only initialize the passes useful for rendering the light cache.
- */
-void EEVEE_lightbake_cache_init(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- GPUTexture *rt_color,
- GPUTexture *rt_depth);
-void EEVEE_lightbake_render_world(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- struct GPUFrameBuffer *face_fb[6]);
-/**
- * Render the scene to the `probe_rt` texture.
- */
-void EEVEE_lightbake_render_scene(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- struct GPUFrameBuffer *face_fb[6],
- const float pos[3],
- float near_clip,
- float far_clip);
-/**
- * Glossy filter `rt_color` to `light_cache->cube_tx.tex` at index `probe_idx`.
- */
-void EEVEE_lightbake_filter_glossy(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- struct GPUTexture *rt_color,
- struct GPUFrameBuffer *fb,
- int probe_idx,
- float intensity,
- int maxlevel,
- float filter_quality,
- float firefly_fac);
-/**
- * Diffuse filter `rt_color` to `light_cache->grid_tx.tex` at index `grid_offset`.
- */
-void EEVEE_lightbake_filter_diffuse(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- struct GPUTexture *rt_color,
- struct GPUFrameBuffer *fb,
- int grid_offset,
- float intensity);
-/**
- * Filter `rt_depth` to `light_cache->grid_tx.tex` at index `grid_offset`.
- */
-void EEVEE_lightbake_filter_visibility(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- struct GPUTexture *rt_depth,
- struct GPUFrameBuffer *fb,
- int grid_offset,
- float clipsta,
- float clipend,
- float vis_range,
- float vis_blur,
- int vis_size);
-
-void EEVEE_lightprobes_grid_data_from_object(Object *ob, EEVEE_LightGrid *egrid, int *offset);
-void EEVEE_lightprobes_cube_data_from_object(Object *ob, EEVEE_LightProbe *eprobe);
-void EEVEE_lightprobes_planar_data_from_object(Object *ob,
- EEVEE_PlanarReflection *eplanar,
- EEVEE_LightProbeVisTest *vis_test);
-
-/* eevee_depth_of_field.c */
-
-int EEVEE_depth_of_field_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *camera);
-void EEVEE_depth_of_field_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_depth_of_field_draw(EEVEE_Data *vedata);
-bool EEVEE_depth_of_field_jitter_get(EEVEE_EffectsInfo *effects,
- float r_jitter[2],
- float *r_focus_distance);
-int EEVEE_depth_of_field_sample_count_get(EEVEE_EffectsInfo *effects,
- int sample_count,
- int *r_ring_count);
-
-/* eevee_bloom.c */
-
-int EEVEE_bloom_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_bloom_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_bloom_draw(EEVEE_Data *vedata);
-void EEVEE_bloom_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples);
-void EEVEE_bloom_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-
-/* eevee_cryptomatte.c */
-
-void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata);
-void EEVEE_cryptomatte_output_init(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- int tot_samples);
-void EEVEE_cryptomatte_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob);
-void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob);
-void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob);
-void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-/**
- * Register the render passes needed for cryptomatte
- * normally this is done in `EEVEE_render_update_passes`, but it has been placed here to keep
- * related code side-by-side for clarity.
- */
-void EEVEE_cryptomatte_update_passes(struct RenderEngine *engine,
- struct Scene *scene,
- struct ViewLayer *view_layer);
-void EEVEE_cryptomatte_render_result(struct RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata);
-void EEVEE_cryptomatte_store_metadata(EEVEE_Data *vedata, struct RenderResult *render_result);
-void EEVEE_cryptomatte_free(EEVEE_Data *vedata);
-
-/* eevee_occlusion.c */
-int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- uint tot_samples);
-void EEVEE_occlusion_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_occlusion_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_occlusion_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_occlusion_draw_debug(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_occlusion_free(void);
-
-/* eevee_screen_raytrace.c */
-int EEVEE_screen_raytrace_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_refraction_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_reflection_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_reflection_output_init(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- uint tot_samples);
-void EEVEE_reflection_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-
-/* eevee_subsurface.c */
-void EEVEE_subsurface_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_subsurface_draw_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_subsurface_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_subsurface_output_init(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- uint tot_samples);
-void EEVEE_subsurface_add_pass(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- Material *ma,
- DRWShadingGroup *shgrp,
- struct GPUMaterial *gpumat);
-void EEVEE_subsurface_data_render(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_subsurface_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_subsurface_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-
-/* eevee_motion_blur.c */
-int EEVEE_motion_blur_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_motion_blur_step_set(EEVEE_Data *vedata, int step);
-void EEVEE_motion_blur_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *ob);
-void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- Object *ob,
- struct ParticleSystem *psys,
- struct ModifierData *md);
-void EEVEE_motion_blur_swap_data(EEVEE_Data *vedata);
-void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata);
-void EEVEE_motion_blur_draw(EEVEE_Data *vedata);
-
-/* eevee_mist.c */
-void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_mist_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-
-/* eevee_renderpasses.c */
-void EEVEE_renderpasses_init(EEVEE_Data *vedata);
-void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- uint tot_samples);
-void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_renderpasses_output_accumulate(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- bool post_effect);
-/**
- * Post-process data to construct a specific render-pass
- *
- * This method will create a shading group to perform the post-processing for the given
- * `renderpass_type`. The post-processing will be done and the result will be stored in the
- * `vedata->txl->renderpass` texture.
- *
- * Only invoke this function for passes that need post-processing.
- *
- * After invoking this function the active frame-buffer is set to `vedata->fbl->renderpass_fb`.
- */
-void EEVEE_renderpasses_postprocess(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- eViewLayerEEVEEPassType renderpass_type,
- int aov_index);
-void EEVEE_renderpasses_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_renderpasses_draw_debug(EEVEE_Data *vedata);
-bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata);
-/**
- * Calculate the hash for an AOV. The least significant bit is used to store the AOV
- * type the rest of the bits are used for the name hash.
- */
-int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov);
-
-/* eevee_temporal_sampling.c */
-void EEVEE_temporal_sampling_reset(EEVEE_Data *vedata);
-void EEVEE_temporal_sampling_create_view(EEVEE_Data *vedata);
-int EEVEE_temporal_sampling_sample_count_get(const Scene *scene, const EEVEE_StorageList *stl);
-int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_temporal_sampling_offset_calc(const double ht_point[2],
- float filter_size,
- float r_offset[2]);
-void EEVEE_temporal_sampling_matrices_calc(EEVEE_EffectsInfo *effects, const double ht_point[2]);
-/**
- * Update the matrices based on the current sample.
- * \note `DRW_MAT_PERS` and `DRW_MAT_VIEW` needs to read the original matrices.
- */
-void EEVEE_temporal_sampling_update_matrices(EEVEE_Data *vedata);
-void EEVEE_temporal_sampling_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_temporal_sampling_draw(EEVEE_Data *vedata);
-
-/* eevee_volumes.c */
-
-void EEVEE_volumes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_volumes_set_jitter(EEVEE_ViewLayerData *sldata, uint current_sample);
-void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- struct Scene *scene,
- Object *ob);
-void EEVEE_volumes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_volumes_draw_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_volumes_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_volumes_resolve(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_volumes_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples);
-void EEVEE_volumes_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_volumes_free_smoke_textures(void);
-void EEVEE_volumes_free(void);
-
-/* eevee_effects.c */
-
-void EEVEE_effects_init(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- Object *camera,
- bool minimal);
-void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-void EEVEE_effects_draw_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-/**
- * Simple down-sampling algorithm. Reconstruct mip chain up to mip level.
- */
-void EEVEE_effects_downsample_radiance_buffer(EEVEE_Data *vedata, struct GPUTexture *texture_src);
-void EEVEE_create_minmax_buffer(EEVEE_Data *vedata, struct GPUTexture *depth_src, int layer);
-/**
- * Simple down-sampling algorithm for cube-map. Reconstruct mip chain up to mip level.
- */
-void EEVEE_downsample_cube_buffer(EEVEE_Data *vedata, struct GPUTexture *texture_src, int level);
-void EEVEE_draw_effects(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-
-/* eevee_render.c */
-
-/**
- * Return true if initialized properly.
- */
-bool EEVEE_render_init(EEVEE_Data *vedata,
- struct RenderEngine *engine,
- struct Depsgraph *depsgraph);
-void EEVEE_render_view_sync(EEVEE_Data *vedata,
- struct RenderEngine *engine,
- struct Depsgraph *depsgraph);
-void EEVEE_render_modules_init(EEVEE_Data *vedata,
- struct RenderEngine *engine,
- struct Depsgraph *depsgraph);
-void EEVEE_render_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
-/**
- * Used by light cache. in this case engine is NULL.
- */
-void EEVEE_render_cache(void *vedata,
- struct Object *ob,
- struct RenderEngine *engine,
- struct Depsgraph *depsgraph);
-void EEVEE_render_draw(EEVEE_Data *vedata,
- struct RenderEngine *engine,
- struct RenderLayer *rl,
- const struct rcti *rect);
-void EEVEE_render_read_result(EEVEE_Data *vedata,
- struct RenderEngine *engine,
- struct RenderLayer *rl,
- const rcti *rect);
-void EEVEE_render_update_passes(struct RenderEngine *engine,
- struct Scene *scene,
- struct ViewLayer *view_layer);
+struct EEVEE_Instance;
-/** eevee_lookdev.c */
-void EEVEE_lookdev_init(EEVEE_Data *vedata);
-void EEVEE_lookdev_cache_init(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- DRWPass *pass,
- EEVEE_LightProbesInfo *pinfo,
- DRWShadingGroup **r_shgrp);
-void EEVEE_lookdev_draw(EEVEE_Data *vedata);
+struct EEVEE_Instance *EEVEE_instance_alloc(void);
+void EEVEE_instance_free(struct EEVEE_Instance *instance_data_);
-/** eevee_engine.c */
-void EEVEE_cache_populate(void *vedata, Object *ob);
+void EEVEE_instance_init(struct EEVEE_Instance *instance);
-/** eevee_lut_gen.c */
-float *EEVEE_lut_update_ggx_brdf(int lut_size);
-float *EEVEE_lut_update_ggx_btdf(int lut_size, int lut_depth);
+void EEVEE_instance_cache_init(struct EEVEE_Instance *instance);
+void EEVEE_instance_cache_populate(struct EEVEE_Instance *instance, struct Object *object);
+void EEVEE_instance_cache_finish(struct EEVEE_Instance *instance);
-/* Shadow Matrix */
-static const float texcomat[4][4] = {
- /* From NDC to TexCo */
- {0.5f, 0.0f, 0.0f, 0.0f},
- {0.0f, 0.5f, 0.0f, 0.0f},
- {0.0f, 0.0f, 0.5f, 0.0f},
- {0.5f, 0.5f, 0.5f, 1.0f},
-};
+void EEVEE_instance_draw_viewport(struct EEVEE_Instance *instance_data_);
+void EEVEE_instance_render_frame(struct EEVEE_Instance *instance_data_,
+ struct RenderEngine *engine,
+ struct RenderLayer *layer);
-/* Cube-map Matrices */
-static const float cubefacemat[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}},
-};
+void EEVEE_shared_data_free(void);
#ifdef __cplusplus
}
diff --git a/source/blender/draw/engines/eevee/eevee_raytracing.cc b/source/blender/draw/engines/eevee/eevee_raytracing.cc
new file mode 100644
index 00000000000..613e24ada03
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_raytracing.cc
@@ -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(ivec2 extent)
+{
+ extent_ = extent;
+ dispatch_size_.x = divide_ceil_u(extent.x, 8);
+ dispatch_size_.y = divide_ceil_u(extent.y, 8);
+ dispatch_size_.z = 1;
+
+ /* Make sure the history matrix is up to date. */
+ data_.push_update();
+
+ LightProbeModule &lightprobes = inst_.lightprobes;
+ eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
+
+ /* The raytracing buffer contains the draw passes since it is stored per view and we need to
+ * dispatch compute shaders with the right workgroup size. */
+
+ {
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL;
+ std::array<DRWShadingGroup *, 3> grps;
+ bool do_rt = inst_.raytracing.enabled();
+ {
+ trace_reflection_ps_ = DRW_pass_create("TraceReflection", state);
+ GPUShader *sh = inst_.shaders.static_shader_get(do_rt ? RAYTRACE_REFLECTION :
+ RAYTRACE_REFLECTION_FALLBACK);
+ grps[0] = DRW_shgroup_create(sh, trace_reflection_ps_);
+ DRW_shgroup_uniform_block(grps[0], "raytrace_block", inst_.raytracing.reflection_ubo_get());
+ DRW_shgroup_stencil_set(grps[0], 0x0, 0x0, CLOSURE_REFLECTION);
+ }
+ {
+ trace_refraction_ps_ = DRW_pass_create("TraceRefraction", state);
+ GPUShader *sh = inst_.shaders.static_shader_get(do_rt ? RAYTRACE_REFRACTION :
+ RAYTRACE_REFRACTION_FALLBACK);
+ grps[1] = DRW_shgroup_create(sh, trace_refraction_ps_);
+ DRW_shgroup_uniform_block(grps[1], "raytrace_block", inst_.raytracing.refraction_ubo_get());
+ DRW_shgroup_stencil_set(grps[1], 0x0, 0x0, CLOSURE_REFRACTION);
+ }
+ {
+ trace_diffuse_ps_ = DRW_pass_create("TraceDiffuse", state);
+ GPUShader *sh = inst_.shaders.static_shader_get(do_rt ? RAYTRACE_DIFFUSE :
+ RAYTRACE_DIFFUSE_FALLBACK);
+ grps[2] = DRW_shgroup_create(sh, trace_diffuse_ps_);
+ DRW_shgroup_uniform_block(grps[2], "raytrace_block", inst_.raytracing.diffuse_ubo_get());
+ DRW_shgroup_stencil_set(grps[2], 0x0, 0x0, CLOSURE_DIFFUSE);
+ }
+
+ for (DRWShadingGroup *grp : grps) {
+ DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
+ DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.ubo_get());
+ DRW_shgroup_uniform_block(grp, "cubes_block", lightprobes.cube_ubo_get());
+ DRW_shgroup_uniform_block(grp, "lightprobes_info_block", lightprobes.info_ubo_get());
+ DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_tx_);
+ DRW_shgroup_uniform_texture_ref(grp, "hiz_front_tx", &input_hiz_front_tx_);
+ DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
+ DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_radiance_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "combined_tx", &input_combined_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "cl_color_tx", &input_cl_color_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "cl_normal_tx", &input_cl_normal_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "cl_data_tx", &input_cl_data_tx_, no_interp);
+ DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+ }
+ }
+
+ {
+ /* Compute stage. No state needed. */
+ DRWState state = (DRWState)0;
+ std::array<DRWShadingGroup *, 3> grps;
+ {
+ denoise_reflection_ps_ = DRW_pass_create("DenoiseReflection", state);
+ GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_DENOISE_REFLECTION);
+ grps[0] = DRW_shgroup_create(sh, denoise_reflection_ps_);
+ }
+ {
+ denoise_refraction_ps_ = DRW_pass_create("DenoiseRefraction", state);
+ GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_DENOISE_REFRACTION);
+ grps[1] = DRW_shgroup_create(sh, denoise_refraction_ps_);
+ }
+ {
+ denoise_diffuse_ps_ = DRW_pass_create("DenoiseDiffuse", state);
+ GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_DENOISE_DIFFUSE);
+ grps[2] = DRW_shgroup_create(sh, denoise_diffuse_ps_);
+ }
+
+ for (DRWShadingGroup *grp : grps) {
+ /* Does not matter which raytrace_block we use. */
+ DRW_shgroup_uniform_block(grp, "raytrace_block", inst_.raytracing.diffuse_ubo_get());
+ DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.ubo_get());
+ DRW_shgroup_uniform_block(grp, "rtbuffer_block", data_.ubo_get());
+ DRW_shgroup_uniform_texture_ref_ex(grp, "ray_data_tx", &input_ray_data_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "ray_radiance_tx", &input_ray_color_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "hiz_tx", &input_hiz_front_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "cl_color_tx", &input_cl_color_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "cl_normal_tx", &input_cl_normal_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "cl_data_tx", &input_cl_data_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref(grp, "ray_history_tx", &input_history_tx_);
+ DRW_shgroup_uniform_texture_ref(grp, "ray_variance_tx", &input_variance_tx_);
+ DRW_shgroup_uniform_image_ref(grp, "out_history_img", &output_history_tx_);
+ DRW_shgroup_uniform_image_ref(grp, "out_variance_img", &output_variance_tx_);
+ DRW_shgroup_call_compute_ref(grp, dispatch_size_);
+ DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS);
+ }
+ }
+
+ {
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_ADD_FULL;
+ std::array<DRWShadingGroup *, 3> grps;
+ {
+ resolve_reflection_ps_ = DRW_pass_create("ResolveReflection", state);
+ GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_RESOLVE_REFLECTION);
+ grps[0] = DRW_shgroup_create(sh, resolve_reflection_ps_);
+ DRW_shgroup_stencil_set(grps[0], 0x0, 0x0, CLOSURE_REFLECTION);
+ }
+ {
+ resolve_refraction_ps_ = DRW_pass_create("ResolveRefraction", state);
+ GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_RESOLVE_REFRACTION);
+ grps[1] = DRW_shgroup_create(sh, resolve_refraction_ps_);
+ DRW_shgroup_stencil_set(grps[1], 0x0, 0x0, CLOSURE_REFRACTION);
+ }
+ {
+ resolve_diffuse_ps_ = DRW_pass_create("ResolveDiffuse", state);
+ GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_RESOLVE_DIFFUSE);
+ grps[2] = DRW_shgroup_create(sh, resolve_diffuse_ps_);
+ DRW_shgroup_stencil_set(grps[2], 0x0, 0x0, CLOSURE_DIFFUSE);
+ }
+
+ for (DRWShadingGroup *grp : grps) {
+ DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.ubo_get());
+ DRW_shgroup_uniform_texture_ref_ex(grp, "ray_radiance_tx", &output_history_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "ray_variance_tx", &output_variance_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "cl_color_tx", &input_cl_color_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "cl_normal_tx", &input_cl_normal_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "cl_data_tx", &input_cl_data_tx_, no_interp);
+ // DRW_shgroup_call_compute_ref(grp, dispatch_size_);
+ // DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS);
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+ }
+ }
+}
+
+void RaytraceBuffer::trace(eClosureBits closure_type,
+ GBuffer &gbuffer,
+ HiZBuffer &hiz,
+ HiZBuffer &hiz_front)
+{
+ gbuffer.bind_tracing();
+
+ input_hiz_tx_ = hiz.texture_get();
+ input_hiz_front_tx_ = hiz_front.texture_get();
+ if (closure_type == CLOSURE_REFLECTION) {
+ input_cl_color_tx_ = gbuffer.reflect_color_tx;
+ input_cl_normal_tx_ = gbuffer.reflect_normal_tx;
+ input_cl_data_tx_ = gbuffer.reflect_normal_tx;
+ }
+ else {
+ input_cl_color_tx_ = gbuffer.transmit_color_tx;
+ input_cl_normal_tx_ = gbuffer.transmit_normal_tx;
+ input_cl_data_tx_ = gbuffer.transmit_data_tx;
+ }
+
+ switch (closure_type) {
+ default:
+ case CLOSURE_REFLECTION:
+ input_radiance_tx_ = gbuffer.combined_tx;
+ DRW_draw_pass(trace_reflection_ps_);
+ break;
+ case CLOSURE_REFRACTION:
+ input_radiance_tx_ = gbuffer.combined_tx;
+ DRW_draw_pass(trace_refraction_ps_);
+ break;
+ case CLOSURE_DIFFUSE:
+ input_radiance_tx_ = gbuffer.diffuse_tx;
+ input_combined_tx_ = gbuffer.combined_tx;
+ DRW_draw_pass(trace_diffuse_ps_);
+ break;
+ }
+
+ input_ray_data_tx_ = gbuffer.ray_data_tx;
+ input_ray_color_tx_ = gbuffer.ray_radiance_tx;
+}
+
+void RaytraceBuffer::denoise(eClosureBits closure_type)
+{
+ switch (closure_type) {
+ default:
+ case CLOSURE_REFLECTION:
+ input_history_tx_ = reflection_radiance_history_get();
+ input_variance_tx_ = reflection_variance_history_get();
+ output_history_tx_ = reflection_radiance_get();
+ output_variance_tx_ = reflection_variance_get();
+ DRW_draw_pass(denoise_reflection_ps_);
+ break;
+ case CLOSURE_REFRACTION:
+ input_history_tx_ = refraction_radiance_history_get();
+ input_variance_tx_ = refraction_variance_history_get();
+ output_history_tx_ = refraction_radiance_get();
+ output_variance_tx_ = refraction_variance_get();
+ DRW_draw_pass(denoise_refraction_ps_);
+ break;
+ case CLOSURE_DIFFUSE:
+ input_history_tx_ = diffuse_radiance_history_get();
+ input_variance_tx_ = diffuse_variance_history_get();
+ output_history_tx_ = diffuse_radiance_get();
+ output_variance_tx_ = diffuse_variance_get();
+ DRW_draw_pass(denoise_diffuse_ps_);
+ break;
+ }
+}
+
+void RaytraceBuffer::resolve(eClosureBits closure_type, GBuffer &gbuffer)
+{
+ gbuffer.bind_radiance();
+ switch (closure_type) {
+ default:
+ case CLOSURE_REFLECTION:
+ DRW_draw_pass(resolve_reflection_ps_);
+ break;
+ case CLOSURE_REFRACTION:
+ DRW_draw_pass(resolve_refraction_ps_);
+ break;
+ case CLOSURE_DIFFUSE:
+ DRW_draw_pass(resolve_diffuse_ps_);
+ break;
+ }
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_raytracing.hh b/source/blender/draw/engines/eevee/eevee_raytracing.hh
new file mode 100644
index 00000000000..e31a0eac1c1
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_raytracing.hh
@@ -0,0 +1,228 @@
+/*
+ * 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_.ubo_get();
+ }
+ const GPUUniformBuf *refraction_ubo_get(void) const
+ {
+ return refraction_data_.ubo_get();
+ }
+ const GPUUniformBuf *diffuse_ubo_get(void) const
+ {
+ return diffuse_data_.ubo_get();
+ }
+
+ 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_ = Texture("DiffuseHistory_A");
+ Texture diffuse_radiance_history_tx_ = Texture("DiffuseHistory_B");
+ Texture diffuse_variance_tx_ = Texture("DiffuseVariance_A");
+ Texture diffuse_variance_history_tx_ = Texture("DiffuseVariance_B");
+ Texture reflection_radiance_tx_ = Texture("ReflectionHistory_A");
+ Texture reflection_radiance_history_tx_ = Texture("ReflectionHistory_B");
+ Texture reflection_variance_tx_ = Texture("ReflectionVariance_A");
+ Texture reflection_variance_history_tx_ = Texture("ReflectionVariance_B");
+ Texture refraction_radiance_tx_ = Texture("RefractionHistory_A");
+ Texture refraction_radiance_history_tx_ = Texture("RefractionHistory_B");
+ Texture refraction_variance_tx_ = Texture("RefractionVariance_A");
+ Texture refraction_variance_history_tx_ = Texture("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_;
+
+ ivec2 extent_ = ivec2(0);
+ ivec3 dispatch_size_ = ivec3(1);
+
+ public:
+ RaytraceBuffer(Instance &inst) : inst_(inst){};
+ ~RaytraceBuffer(){};
+
+ void sync(ivec2 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)
+ {
+ DRW_view_persmat_get(view, data_.history_persmat, false);
+ SWAP(Texture, diffuse_radiance_tx_, diffuse_radiance_history_tx_);
+ SWAP(Texture, diffuse_variance_tx_, diffuse_variance_history_tx_);
+ SWAP(Texture, reflection_radiance_tx_, reflection_radiance_history_tx_);
+ SWAP(Texture, reflection_variance_tx_, reflection_variance_history_tx_);
+ SWAP(Texture, refraction_radiance_tx_, refraction_radiance_history_tx_);
+ SWAP(Texture, refraction_variance_tx_, refraction_variance_history_tx_);
+ }
+
+ private:
+ void ensure_buffer(Texture &texture, int &valid_history, eGPUTextureFormat format)
+ {
+ bool was_allocated = texture.ensure(UNPACK2(extent_), 1, format);
+ 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
diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c
deleted file mode 100644
index 9e7a67060da..00000000000
--- a/source/blender/draw/engines/eevee/eevee_render.c
+++ /dev/null
@@ -1,743 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Copyright 2016, Blender Foundation.
- */
-
-/** \file
- * \ingroup draw_engine
- */
-
-/**
- * Render functions for final render outputs.
- */
-
-#include "DRW_engine.h"
-#include "DRW_render.h"
-
-#include "DNA_node_types.h"
-#include "DNA_object_types.h"
-
-#include "BKE_global.h"
-#include "BKE_object.h"
-
-#include "BLI_rand.h"
-#include "BLI_rect.h"
-
-#include "DEG_depsgraph_query.h"
-
-#include "GPU_capabilities.h"
-#include "GPU_framebuffer.h"
-#include "GPU_state.h"
-
-#include "RE_pipeline.h"
-
-#include "eevee_private.h"
-
-bool EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph *depsgraph)
-{
- EEVEE_Data *vedata = (EEVEE_Data *)ved;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_TextureList *txl = vedata->txl;
- EEVEE_FramebufferList *fbl = vedata->fbl;
- Scene *scene = DEG_get_evaluated_scene(depsgraph);
- const float *size_orig = DRW_viewport_size_get();
- float size_final[2];
-
- /* Init default FB and render targets:
- * In render mode the default framebuffer is not generated
- * because there is no viewport. So we need to manually create it or
- * not use it. For code clarity we just allocate it make use of it. */
- DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
- DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
-
- /* Alloc transient data. */
- if (!stl->g_data) {
- stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__);
- }
- EEVEE_PrivateData *g_data = stl->g_data;
- g_data->background_alpha = DRW_state_draw_background() ? 1.0f : 0.0f;
- g_data->valid_double_buffer = 0;
- copy_v2_v2(g_data->size_orig, size_orig);
-
- float *camtexcofac = g_data->camtexcofac;
- if (scene->eevee.flag & SCE_EEVEE_OVERSCAN) {
- g_data->overscan = scene->eevee.overscan / 100.0f;
- g_data->overscan_pixels = roundf(max_ff(size_orig[0], size_orig[1]) * g_data->overscan);
-
- madd_v2_v2v2fl(size_final, size_orig, (float[2]){2.0f, 2.0f}, g_data->overscan_pixels);
-
- camtexcofac[0] = size_final[0] / size_orig[0];
- camtexcofac[1] = size_final[1] / size_orig[1];
-
- camtexcofac[2] = -camtexcofac[0] * g_data->overscan_pixels / size_final[0];
- camtexcofac[3] = -camtexcofac[1] * g_data->overscan_pixels / size_final[1];
- }
- else {
- copy_v2_v2(size_final, size_orig);
- g_data->overscan = 0.0f;
- g_data->overscan_pixels = 0.0f;
- copy_v4_fl4(camtexcofac, 1.0f, 1.0f, 0.0f, 0.0f);
- }
-
- const int final_res[2] = {
- size_orig[0] + g_data->overscan_pixels * 2.0f,
- size_orig[1] + g_data->overscan_pixels * 2.0f,
- };
-
- int max_dim = max_ii(final_res[0], final_res[1]);
- if (max_dim > GPU_max_texture_size()) {
- char error_msg[128];
- BLI_snprintf(error_msg,
- sizeof(error_msg),
- "Error: Reported texture size limit (%dpx) is lower than output size (%dpx).",
- GPU_max_texture_size(),
- max_dim);
- RE_engine_set_error_message(engine, error_msg);
- G.is_break = true;
- return false;
- }
-
- /* XXX overriding viewport size. Simplify things but is not really 100% safe. */
- DRW_render_viewport_size_set(final_res);
-
- /* TODO: 32 bit depth. */
- DRW_texture_ensure_fullscreen_2d(&dtxl->depth, GPU_DEPTH24_STENCIL8, 0);
- DRW_texture_ensure_fullscreen_2d(&txl->color, GPU_RGBA32F, DRW_TEX_FILTER);
-
- GPU_framebuffer_ensure_config(
- &dfbl->default_fb,
- {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(txl->color)});
- GPU_framebuffer_ensure_config(
- &fbl->main_fb, {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(txl->color)});
- GPU_framebuffer_ensure_config(&fbl->main_color_fb,
- {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->color)});
-
- /* Camera could change because of Motion blur. */
- g_data->cam_original_ob = RE_GetCamera(engine->re);
-
- return true;
-}
-
-void EEVEE_render_modules_init(EEVEE_Data *vedata,
- RenderEngine *engine,
- struct Depsgraph *depsgraph)
-{
- EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_PrivateData *g_data = vedata->stl->g_data;
- EEVEE_FramebufferList *fbl = vedata->fbl;
- /* TODO(sergey): Shall render hold pointer to an evaluated camera instead? */
- struct Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, g_data->cam_original_ob);
- EEVEE_render_view_sync(vedata, engine, depsgraph);
-
- /* `EEVEE_renderpasses_init` will set the active render passes used by `EEVEE_effects_init`.
- * `EEVEE_effects_init` needs to go second for TAA. */
- EEVEE_renderpasses_init(vedata);
- EEVEE_effects_init(sldata, vedata, ob_camera_eval, false);
- EEVEE_materials_init(sldata, vedata, stl, fbl);
- EEVEE_shadows_init(sldata);
- EEVEE_lightprobes_init(sldata, vedata);
-}
-
-void EEVEE_render_view_sync(EEVEE_Data *vedata, RenderEngine *engine, struct Depsgraph *depsgraph)
-{
- EEVEE_PrivateData *g_data = vedata->stl->g_data;
-
- /* Set the perspective & view matrix. */
- float winmat[4][4], viewmat[4][4], viewinv[4][4];
- /* TODO(sergey): Shall render hold pointer to an evaluated camera instead? */
- struct Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, g_data->cam_original_ob);
-
- RE_GetCameraWindow(engine->re, ob_camera_eval, winmat);
- RE_GetCameraWindowWithOverscan(engine->re, g_data->overscan, winmat);
- RE_GetCameraModelMatrix(engine->re, ob_camera_eval, viewinv);
-
- invert_m4_m4(viewmat, viewinv);
-
- DRWView *view = DRW_view_create(viewmat, winmat, NULL, NULL, NULL);
- DRW_view_reset();
- DRW_view_default_set(view);
- DRW_view_set_active(view);
-
- DRW_view_camtexco_set(view, g_data->camtexcofac);
-}
-
-void EEVEE_render_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
-{
- EEVEE_view_layer_data_ensure();
- EEVEE_bloom_cache_init(sldata, vedata);
- EEVEE_depth_of_field_cache_init(sldata, vedata);
- EEVEE_effects_cache_init(sldata, vedata);
- EEVEE_lightprobes_cache_init(sldata, vedata);
- EEVEE_lights_cache_init(sldata, vedata);
- EEVEE_materials_cache_init(sldata, vedata);
- EEVEE_motion_blur_cache_init(sldata, vedata);
- EEVEE_occlusion_cache_init(sldata, vedata);
- EEVEE_screen_raytrace_cache_init(sldata, vedata);
- EEVEE_subsurface_cache_init(sldata, vedata);
- EEVEE_temporal_sampling_cache_init(sldata, vedata);
- EEVEE_volumes_cache_init(sldata, vedata);
- EEVEE_cryptomatte_cache_init(sldata, vedata);
-}
-
-void EEVEE_render_cache(void *vedata,
- struct Object *ob,
- struct RenderEngine *engine,
- struct Depsgraph *depsgraph)
-{
- EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
- EEVEE_Data *data = vedata;
- EEVEE_StorageList *stl = data->stl;
- EEVEE_PrivateData *g_data = stl->g_data;
- EEVEE_LightProbesInfo *pinfo = sldata->probes;
- bool cast_shadow = false;
-
- const bool do_cryptomatte = (engine != NULL) &&
- ((g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0);
-
- eevee_id_update(vedata, &ob->id);
-
- if (pinfo->vis_data.collection) {
- /* Used for rendering probe with visibility groups. */
- bool ob_vis = BKE_collection_has_object_recursive(pinfo->vis_data.collection, ob);
- ob_vis = (pinfo->vis_data.invert) ? !ob_vis : ob_vis;
-
- if (!ob_vis) {
- return;
- }
- }
-
- /* Don't print dupli objects as this can be very verbose and
- * increase the render time on Windows because of slow windows term.
- * (see T59649) */
- if (engine && (ob->base_flag & BASE_FROM_DUPLI) == 0) {
- char info[42];
- BLI_snprintf(info, sizeof(info), "Syncing %s", ob->id.name + 2);
- RE_engine_update_stats(engine, NULL, info);
- }
-
- const int ob_visibility = DRW_object_visibility_in_active_context(ob);
- if (ob_visibility & OB_VISIBLE_PARTICLES) {
- EEVEE_particle_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
- if (do_cryptomatte) {
- EEVEE_cryptomatte_particle_hair_cache_populate(data, sldata, ob);
- }
- }
-
- if (ob_visibility & OB_VISIBLE_SELF) {
- if (ELEM(ob->type, OB_MESH, OB_SURF, OB_MBALL)) {
- EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow);
- if (do_cryptomatte) {
- EEVEE_cryptomatte_cache_populate(data, sldata, ob);
- }
- }
- else if (ob->type == OB_HAIR) {
- EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
- if (do_cryptomatte) {
- EEVEE_cryptomatte_object_hair_cache_populate(data, sldata, ob);
- }
- }
- else if (ob->type == OB_VOLUME) {
- Scene *scene = DEG_get_evaluated_scene(depsgraph);
- EEVEE_volumes_cache_object_add(sldata, vedata, scene, ob);
- }
- else if (ob->type == OB_LIGHTPROBE) {
- EEVEE_lightprobes_cache_add(sldata, vedata, ob);
- }
- else if (ob->type == OB_LAMP) {
- EEVEE_lights_cache_add(sldata, ob);
- }
- }
-
- if (cast_shadow) {
- EEVEE_shadows_caster_register(sldata, ob);
- }
-}
-
-static void eevee_render_color_result(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- const char *render_pass_name,
- int num_channels,
- GPUFrameBuffer *framebuffer,
- EEVEE_Data *vedata)
-{
- RenderPass *rp = RE_pass_find_by_name(rl, render_pass_name, viewname);
- if (rp == NULL) {
- return;
- }
- GPU_framebuffer_bind(framebuffer);
- GPU_framebuffer_read_color(framebuffer,
- vedata->stl->g_data->overscan_pixels + rect->xmin,
- vedata->stl->g_data->overscan_pixels + rect->ymin,
- BLI_rcti_size_x(rect),
- BLI_rcti_size_y(rect),
- num_channels,
- 0,
- GPU_DATA_FLOAT,
- rp->rect);
-}
-
-static void eevee_render_result_combined(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *UNUSED(sldata))
-{
- eevee_render_color_result(
- rl, viewname, rect, RE_PASSNAME_COMBINED, 4, vedata->stl->effects->final_fb, vedata);
-}
-
-static void eevee_render_result_normal(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata)
-{
- const int current_sample = vedata->stl->effects->taa_current_sample;
-
- /* Only read the center texel. */
- if (current_sample > 1) {
- return;
- }
-
- if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_NORMAL) != 0) {
- EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_NORMAL, 0);
- eevee_render_color_result(
- rl, viewname, rect, RE_PASSNAME_NORMAL, 3, vedata->fbl->renderpass_fb, vedata);
- }
-}
-
-static void eevee_render_result_z(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata)
-{
- const int current_sample = vedata->stl->effects->taa_current_sample;
-
- /* Only read the center texel. */
- if (current_sample > 1) {
- return;
- }
-
- if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_Z) != 0) {
- EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_Z, 0);
- eevee_render_color_result(
- rl, viewname, rect, RE_PASSNAME_Z, 1, vedata->fbl->renderpass_fb, vedata);
- }
-}
-
-static void eevee_render_result_mist(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata)
-{
- if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_MIST) != 0) {
- EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_MIST, 0);
- eevee_render_color_result(
- rl, viewname, rect, RE_PASSNAME_MIST, 1, vedata->fbl->renderpass_fb, vedata);
- }
-}
-
-static void eevee_render_result_shadow(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata)
-{
- if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_SHADOW) != 0) {
- EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_SHADOW, 0);
- eevee_render_color_result(
- rl, viewname, rect, RE_PASSNAME_SHADOW, 3, vedata->fbl->renderpass_fb, vedata);
- }
-}
-
-static void eevee_render_result_occlusion(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata)
-{
- if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_AO) != 0) {
- EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_AO, 0);
- eevee_render_color_result(
- rl, viewname, rect, RE_PASSNAME_AO, 3, vedata->fbl->renderpass_fb, vedata);
- }
-}
-
-static void eevee_render_result_bloom(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata)
-{
- if ((vedata->stl->effects->enabled_effects & EFFECT_BLOOM) == 0) {
- /* Bloom is not enabled. */
- return;
- }
-
- if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_BLOOM) != 0) {
- EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_BLOOM, 0);
- eevee_render_color_result(
- rl, viewname, rect, RE_PASSNAME_BLOOM, 3, vedata->fbl->renderpass_fb, vedata);
- }
-}
-
-#define EEVEE_RENDER_RESULT_MATERIAL_PASS(pass_name, eevee_pass_type) \
- if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_##eevee_pass_type) != 0) { \
- EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_##eevee_pass_type, 0); \
- eevee_render_color_result( \
- rl, viewname, rect, RE_PASSNAME_##pass_name, 3, vedata->fbl->renderpass_fb, vedata); \
- }
-
-static void eevee_render_result_diffuse_color(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata)
-{
- EEVEE_RENDER_RESULT_MATERIAL_PASS(DIFFUSE_COLOR, DIFFUSE_COLOR)
-}
-
-static void eevee_render_result_diffuse_direct(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata)
-{
- EEVEE_RENDER_RESULT_MATERIAL_PASS(DIFFUSE_DIRECT, DIFFUSE_LIGHT)
-}
-
-static void eevee_render_result_specular_color(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata)
-{
- EEVEE_RENDER_RESULT_MATERIAL_PASS(GLOSSY_COLOR, SPECULAR_COLOR)
-}
-
-static void eevee_render_result_specular_direct(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata)
-{
- EEVEE_RENDER_RESULT_MATERIAL_PASS(GLOSSY_DIRECT, SPECULAR_LIGHT)
-}
-
-static void eevee_render_result_emission(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata)
-{
- EEVEE_RENDER_RESULT_MATERIAL_PASS(EMIT, EMIT)
-}
-
-static void eevee_render_result_environment(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata)
-{
- EEVEE_RENDER_RESULT_MATERIAL_PASS(ENVIRONMENT, ENVIRONMENT)
-}
-
-static void eevee_render_result_volume_light(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata)
-{
- EEVEE_RENDER_RESULT_MATERIAL_PASS(VOLUME_LIGHT, VOLUME_LIGHT)
-}
-
-static void eevee_render_result_aovs(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata)
-{
- if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_AOV) != 0) {
- const DRWContextState *draw_ctx = DRW_context_state_get();
- ViewLayer *view_layer = draw_ctx->view_layer;
- int aov_index = 0;
- LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
- if ((aov->flag & AOV_CONFLICT) != 0) {
- continue;
- }
- EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_AOV, aov_index);
- switch (aov->type) {
- case AOV_TYPE_COLOR:
- eevee_render_color_result(
- rl, viewname, rect, aov->name, 4, vedata->fbl->renderpass_fb, vedata);
- break;
- case AOV_TYPE_VALUE:
- eevee_render_color_result(
- rl, viewname, rect, aov->name, 1, vedata->fbl->renderpass_fb, vedata);
- }
- aov_index++;
- }
- }
-}
-
-#undef EEVEE_RENDER_RESULT_MATERIAL_PASS
-
-static void eevee_render_result_cryptomatte(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata)
-{
- if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) {
- EEVEE_cryptomatte_render_result(rl, viewname, rect, vedata, sldata);
- }
-}
-
-static void eevee_render_draw_background(EEVEE_Data *vedata)
-{
- EEVEE_FramebufferList *fbl = vedata->fbl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_PassList *psl = vedata->psl;
-
- /* Prevent background to write to data buffers.
- * NOTE : This also make sure the textures are bound
- * to the right double buffer. */
- GPU_framebuffer_ensure_config(&fbl->main_fb,
- {GPU_ATTACHMENT_LEAVE,
- GPU_ATTACHMENT_LEAVE,
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_NONE});
- GPU_framebuffer_bind(fbl->main_fb);
-
- DRW_draw_pass(psl->background_ps);
-
- GPU_framebuffer_ensure_config(&fbl->main_fb,
- {GPU_ATTACHMENT_LEAVE,
- GPU_ATTACHMENT_LEAVE,
- GPU_ATTACHMENT_TEXTURE(stl->effects->ssr_normal_input),
- GPU_ATTACHMENT_TEXTURE(stl->effects->ssr_specrough_input),
- GPU_ATTACHMENT_TEXTURE(stl->effects->sss_irradiance),
- GPU_ATTACHMENT_TEXTURE(stl->effects->sss_radius),
- GPU_ATTACHMENT_TEXTURE(stl->effects->sss_albedo)});
- GPU_framebuffer_bind(fbl->main_fb);
-}
-
-void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl, const rcti *rect)
-{
- const char *viewname = RE_GetActiveRenderView(engine->re);
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_FramebufferList *fbl = vedata->fbl;
- DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
-
- /* Push instances attributes to the GPU. */
- DRW_render_instance_buffer_finish();
-
- /* Need to be called after DRW_render_instance_buffer_finish() */
- /* Also we weed to have a correct FBO bound for DRW_hair_update */
- GPU_framebuffer_bind(fbl->main_fb);
- DRW_hair_update();
-
- /* Sort transparents before the loop. */
- DRW_pass_sort_shgroup_z(psl->transparent_pass);
-
- uint tot_sample = stl->g_data->render_sample_count_per_timestep;
- uint render_samples = 0;
-
- /* SSR needs one iteration to start properly. */
- if ((stl->effects->enabled_effects & EFFECT_SSR) && !stl->effects->ssr_was_valid_double_buffer) {
- tot_sample += 1;
- }
-
- while (render_samples < tot_sample && !RE_engine_test_break(engine)) {
- const float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- float clear_depth = 1.0f;
- uint clear_stencil = 0x00;
- const uint primes[3] = {2, 3, 7};
- double offset[3] = {0.0, 0.0, 0.0};
- double r[3];
-
- if ((stl->effects->enabled_effects & EFFECT_SSR) && (render_samples == 1) &&
- !stl->effects->ssr_was_valid_double_buffer) {
- /* SSR needs one iteration to start properly.
- * This iteration was done, reset to the original target sample count. */
- render_samples--;
- tot_sample--;
- /* Reset sampling (and accumulation) after the first sample to avoid
- * washed out first bounce for SSR. */
- EEVEE_temporal_sampling_reset(vedata);
- stl->effects->ssr_was_valid_double_buffer = stl->g_data->valid_double_buffer;
- }
- /* Don't print every samples as it can lead to bad performance. (see T59649) */
- else if ((render_samples % 25) == 0 || (render_samples + 1) == tot_sample) {
- char info[42];
- BLI_snprintf(
- info, sizeof(info), "Rendering %u / %u samples", render_samples + 1, tot_sample);
- RE_engine_update_stats(engine, NULL, info);
- }
-
- /* Copy previous persmat to UBO data */
- copy_m4_m4(sldata->common_data.prev_persmat, stl->effects->prev_persmat);
-
- BLI_halton_3d(primes, offset, stl->effects->taa_current_sample, r);
- EEVEE_update_noise(psl, fbl, r);
- EEVEE_temporal_sampling_matrices_calc(stl->effects, r);
- EEVEE_volumes_set_jitter(sldata, stl->effects->taa_current_sample - 1);
- EEVEE_materials_init(sldata, vedata, stl, fbl);
-
- /* Refresh Probes
- * Shadows needs to be updated for correct probes */
- EEVEE_shadows_update(sldata, vedata);
- EEVEE_lightprobes_refresh(sldata, vedata);
- EEVEE_lightprobes_refresh_planar(sldata, vedata);
-
- /* Refresh Shadows */
- EEVEE_shadows_draw(sldata, vedata, stl->effects->taa_view);
-
- /* Set matrices. */
- DRW_view_set_active(stl->effects->taa_view);
-
- /* Set ray type. */
- sldata->common_data.ray_type = EEVEE_RAY_CAMERA;
- sldata->common_data.ray_depth = 0.0f;
- GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
-
- GPU_framebuffer_bind(fbl->main_fb);
- GPU_framebuffer_clear_color_depth_stencil(fbl->main_fb, clear_col, clear_depth, clear_stencil);
- /* Depth prepass */
- DRW_draw_pass(psl->depth_ps);
- /* Create minmax texture */
- EEVEE_create_minmax_buffer(vedata, dtxl->depth, -1);
- EEVEE_occlusion_compute(sldata, vedata);
- EEVEE_volumes_compute(sldata, vedata);
- /* Shading pass */
- eevee_render_draw_background(vedata);
- GPU_framebuffer_bind(fbl->main_fb);
- DRW_draw_pass(psl->material_ps);
- EEVEE_subsurface_data_render(sldata, vedata);
- /* Effects pre-transparency */
- EEVEE_subsurface_compute(sldata, vedata);
- EEVEE_reflection_compute(sldata, vedata);
- EEVEE_refraction_compute(sldata, vedata);
- /* Opaque refraction */
- DRW_draw_pass(psl->depth_refract_ps);
- DRW_draw_pass(psl->material_refract_ps);
- /* Result NORMAL */
- eevee_render_result_normal(rl, viewname, rect, vedata, sldata);
- /* Volumetrics Resolve Opaque */
- EEVEE_volumes_resolve(sldata, vedata);
- /* Subsurface output, Occlusion output, Mist output */
- EEVEE_renderpasses_output_accumulate(sldata, vedata, false);
- /* Transparent */
- GPU_framebuffer_texture_attach(fbl->main_color_fb, dtxl->depth, 0, 0);
- GPU_framebuffer_bind(fbl->main_color_fb);
- DRW_draw_pass(psl->transparent_pass);
- GPU_framebuffer_bind(fbl->main_fb);
- GPU_framebuffer_texture_detach(fbl->main_color_fb, dtxl->depth);
- /* Result Z */
- eevee_render_result_z(rl, viewname, rect, vedata, sldata);
- /* Post Process */
- EEVEE_draw_effects(sldata, vedata);
-
- /* XXX Seems to fix TDR issue with NVidia drivers on linux. */
- GPU_finish();
-
- RE_engine_update_progress(engine, (float)(render_samples++) / (float)tot_sample);
- }
-}
-
-void EEVEE_render_read_result(EEVEE_Data *vedata,
- RenderEngine *engine,
- RenderLayer *rl,
- const rcti *rect)
-{
- const char *viewname = RE_GetActiveRenderView(engine->re);
- EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
-
- eevee_render_result_combined(rl, viewname, rect, vedata, sldata);
- eevee_render_result_mist(rl, viewname, rect, vedata, sldata);
- eevee_render_result_occlusion(rl, viewname, rect, vedata, sldata);
- eevee_render_result_shadow(rl, viewname, rect, vedata, sldata);
- eevee_render_result_diffuse_color(rl, viewname, rect, vedata, sldata);
- eevee_render_result_diffuse_direct(rl, viewname, rect, vedata, sldata);
- eevee_render_result_specular_color(rl, viewname, rect, vedata, sldata);
- eevee_render_result_specular_direct(rl, viewname, rect, vedata, sldata);
- eevee_render_result_emission(rl, viewname, rect, vedata, sldata);
- eevee_render_result_environment(rl, viewname, rect, vedata, sldata);
- eevee_render_result_bloom(rl, viewname, rect, vedata, sldata);
- eevee_render_result_volume_light(rl, viewname, rect, vedata, sldata);
- eevee_render_result_aovs(rl, viewname, rect, vedata, sldata);
- eevee_render_result_cryptomatte(rl, viewname, rect, vedata, sldata);
-}
-
-void EEVEE_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
-{
- RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA);
-
-#define CHECK_PASS_LEGACY(name, type, channels, chanid) \
- if (view_layer->passflag & (SCE_PASS_##name)) { \
- RE_engine_register_pass( \
- engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
- } \
- ((void)0)
-#define CHECK_PASS_EEVEE(name, type, channels, chanid) \
- if (view_layer->eevee.render_passes & (EEVEE_RENDER_PASS_##name)) { \
- RE_engine_register_pass( \
- engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
- } \
- ((void)0)
-
- CHECK_PASS_LEGACY(Z, SOCK_FLOAT, 1, "Z");
- CHECK_PASS_LEGACY(MIST, SOCK_FLOAT, 1, "Z");
- CHECK_PASS_LEGACY(NORMAL, SOCK_VECTOR, 3, "XYZ");
- CHECK_PASS_LEGACY(DIFFUSE_DIRECT, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_LEGACY(DIFFUSE_COLOR, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_LEGACY(GLOSSY_COLOR, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_LEGACY(SHADOW, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_LEGACY(AO, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_EEVEE(BLOOM, SOCK_RGBA, 3, "RGB");
-
- LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
- if ((aov->flag & AOV_CONFLICT) != 0) {
- continue;
- }
- switch (aov->type) {
- case AOV_TYPE_COLOR:
- RE_engine_register_pass(engine, scene, view_layer, aov->name, 4, "RGBA", SOCK_RGBA);
- break;
- case AOV_TYPE_VALUE:
- RE_engine_register_pass(engine, scene, view_layer, aov->name, 1, "X", SOCK_FLOAT);
- break;
- default:
- break;
- }
- }
- EEVEE_cryptomatte_update_passes(engine, scene, view_layer);
-
-#undef CHECK_PASS_LEGACY
-#undef CHECK_PASS_EEVEE
-}
diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c
deleted file mode 100644
index 3ebfc8a8f0f..00000000000
--- a/source/blender/draw/engines/eevee/eevee_renderpasses.c
+++ /dev/null
@@ -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);
- }
-}
diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.cc b/source/blender/draw/engines/eevee/eevee_renderpasses.cc
new file mode 100644
index 00000000000..46cc2f67537
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_renderpasses.cc
@@ -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
diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.hh b/source/blender/draw/engines/eevee/eevee_renderpasses.hh
new file mode 100644
index 00000000000..aa3591d3ba5
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_renderpasses.hh
@@ -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
diff --git a/source/blender/draw/engines/eevee/eevee_sampling.c b/source/blender/draw/engines/eevee/eevee_sampling.c
deleted file mode 100644
index 99d14bd2c82..00000000000
--- a/source/blender/draw/engines/eevee/eevee_sampling.c
+++ /dev/null
@@ -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);
-}
diff --git a/source/blender/draw/engines/eevee/eevee_sampling.hh b/source/blender/draw/engines/eevee/eevee_sampling.hh
new file mode 100644
index 00000000000..e5301efb1e8
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_sampling.hh
@@ -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;
+
+ StructBuffer<SamplingData> data_;
+
+ public:
+ Sampling(){};
+ ~Sampling(){};
+
+ void init(const Scene *scene)
+ {
+ sample_count_ = DRW_state_is_image_render() ? scene->eevee.taa_render_samples :
+ scene->eevee.taa_samples;
+
+ if (sample_count_ == 0) {
+ BLI_assert(!DRW_state_is_image_render());
+ sample_count_ = infinite_sample_count_;
+ }
+
+ motion_blur_steps_ = DRW_state_is_image_render() ? scene->eevee.motion_blur_steps : 1;
+ sample_count_ = divide_ceil_u(sample_count_, motion_blur_steps_);
+
+ if (scene->eevee.flag & SCE_EEVEE_DOF_JITTER) {
+ if (sample_count_ == infinite_sample_count_) {
+ /* Special case for viewport continuous rendering. We clamp to a max sample
+ * to avoid the jittered dof never converging. */
+ dof_ring_count_ = 6;
+ }
+ else {
+ dof_ring_count_ = web_ring_count_get(dof_web_density_, sample_count_);
+ }
+ dof_sample_count_ = web_sample_count_get(dof_web_density_, dof_ring_count_);
+ /* Change total sample count to fill the web pattern entirely. */
+ sample_count_ = divide_ceil_u(sample_count_, dof_sample_count_) * dof_sample_count_;
+ }
+ else {
+ dof_ring_count_ = 0;
+ dof_sample_count_ = 1;
+ }
+
+ /* Only multiply after to have full the full DoF web pattern for each time steps. */
+ sample_count_ *= motion_blur_steps_;
+
+ if (!DRW_state_is_image_render()) {
+ /* TODO(fclem) UI. */
+ viewport_smoothing_start = 16;
+ viewport_smoothing_duration = 32;
+ /* At minima start when rendering has finished. */
+ viewport_smoothing_start = min_ii(viewport_smoothing_start, sample_count_);
+ /* Basically counts the number of redraw. */
+ sample_viewport_ += 1;
+ }
+ else {
+ viewport_smoothing_start = 0;
+ viewport_smoothing_duration = 0;
+ }
+ }
+
+ void end_sync(void)
+ {
+ if (reset_) {
+ sample_ = 1;
+ sample_viewport_ = 1;
+ }
+ }
+
+ void step(void)
+ {
+ {
+ /* TODO(fclem) we could use some persistent states to speedup the computation. */
+ double r[2], offset[2] = {0, 0};
+ /* Using 2,3 primes as per UE4 Temporal AA presentation.
+ * advances.realtimerendering.com/s2014/epic/TemporalAA.pptx (slide 14) */
+ uint32_t primes[2] = {2, 3};
+ BLI_halton_2d(primes, offset, sample_, r);
+ data_.dimensions[SAMPLING_FILTER_U][0] = r[0];
+ data_.dimensions[SAMPLING_FILTER_V][0] = r[1];
+ /* TODO decorelate. */
+ data_.dimensions[SAMPLING_TIME][0] = r[0];
+ data_.dimensions[SAMPLING_CLOSURE][0] = r[1];
+ 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_.ubo_get();
+ }
+ /* Returns a pseudo random number in [0..1] range. Each dimension are uncorrelated. */
+ float rng_get(eSamplingDimension dimension) const
+ {
+ return data_.dimensions[dimension][0];
+ }
+ /* Returns true if rendering has finished. */
+ bool finished(void) const
+ {
+ return (sample_ > sample_count_);
+ }
+ /* Returns true if viewport smoothing and sampling has finished. */
+ bool finished_viewport(void) const
+ {
+ return finished() &&
+ (sample_viewport_ > (viewport_smoothing_start + viewport_smoothing_duration));
+ }
+ /* Viewport Only: Function to call to notify something in the scene changed.
+ * This will reset accumulation. Do not call after end_sync() or during sample rendering. */
+ void reset(void)
+ {
+ reset_ = true;
+ }
+ /* Viewport Only: true if an update happened in the scene and accumulation needs reset. */
+ bool is_reset(void) const
+ {
+ return reset_;
+ }
+ /* Return true if we are starting a new motion blur step. We need to run sync agains since
+ * depsgraph was updated by MotionBlur::step(). */
+ bool do_render_sync(void) const
+ {
+ return DRW_state_is_image_render() &&
+ (((sample_ - 1) % (sample_count_ / motion_blur_steps_)) == 0);
+ }
+
+ void dof_disk_sample_get(float *r_radius, float *r_theta)
+ {
+ if (dof_ring_count_ == 0) {
+ *r_radius = *r_theta = 0.0f;
+ return;
+ }
+
+ int s = sample_ - 1;
+ int ring = 0;
+ int ring_sample_count = 1;
+ int ring_sample = 1;
+
+ s = s * (dof_web_density_ - 1);
+ s = s % dof_sample_count_;
+
+ /* Choosing sample to we get faster convergence.
+ * The issue here is that we cannot map a low descripency sequence to this sampling pattern
+ * because the same sample could be choosen twice in relatively short intervals. */
+ /* For now just use an ascending sequence with an offset. This gives us relatively quick
+ * initial coverage and relatively high distance between samples. */
+ /* TODO(fclem) We can try to order samples based on a LDS into a table to avoid duplicates.
+ * The drawback would be some memory consumption and init time. */
+ int samples_passed = 1;
+ while (s >= samples_passed) {
+ ring++;
+ ring_sample_count = ring * dof_web_density_;
+ ring_sample = s - samples_passed;
+ ring_sample = (ring_sample + 1) % ring_sample_count;
+ samples_passed += ring_sample_count;
+ }
+
+ *r_radius = ring / (float)dof_ring_count_;
+ *r_theta = 2.0f * M_PI * ring_sample / (float)ring_sample_count;
+ }
+
+ /* Creates a discrete cumulative distribution function table from a given curvemapping.
+ * Output cdf vector is expected to already be sized according to the wanted resolution. */
+ static void cdf_from_curvemapping(const CurveMapping &curve, Vector<float> &cdf)
+ {
+ BLI_assert(cdf.size() > 1);
+ cdf[0] = 0.0f;
+ /* Actual CDF evaluation. */
+ for (int u : cdf.index_range()) {
+ float x = (float)(u + 1) / (float)(cdf.size() - 1);
+ cdf[u + 1] = cdf[u] + BKE_curvemapping_evaluateF(&curve, 0, x);
+ }
+ /* Normalize the CDF. */
+ for (int u : cdf.index_range()) {
+ cdf[u] /= cdf.last();
+ }
+ /* Just to make sure. */
+ cdf.last() = 1.0f;
+ }
+
+ /* Inverts a cumulative distribution function.
+ * Output vector is expected to already be sized according to the wanted resolution. */
+ static void cdf_invert(Vector<float> &cdf, Vector<float> &inverted_cdf)
+ {
+ for (int u : inverted_cdf.index_range()) {
+ float x = (float)u / (float)(inverted_cdf.size() - 1);
+ for (int i : cdf.index_range()) {
+ if (i == cdf.size() - 1) {
+ inverted_cdf[u] = 1.0f;
+ }
+ else if (cdf[i] >= x) {
+ float t = (x - cdf[i]) / (cdf[i + 1] - cdf[i]);
+ inverted_cdf[u] = ((float)i + t) / (float)(cdf.size() - 1);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Special ball distribution:
+ * Point are distributed in a way that when they are orthogonally
+ * projected into any plane, the resulting distribution is (close to)
+ * a uniform disc distribution.
+ */
+ vec3 sample_ball(const float rand[3])
+ {
+ vec3 sample;
+ sample.z = rand[0] * 2.0f - 1.0f; /* cos theta */
+
+ float r = sqrtf(fmaxf(0.0f, 1.0f - square_f(sample.z))); /* sin theta */
+
+ float omega = rand[1] * 2.0f * M_PI;
+ sample.x = r * cosf(omega);
+ sample.y = r * sinf(omega);
+
+ sample *= sqrtf(sqrtf(rand[2]));
+ return sample;
+ }
+
+ vec2 sample_disk(const float rand[2])
+ {
+ float omega = rand[1] * 2.0f * M_PI;
+ return sqrtf(rand[0]) * vec2(cosf(omega), sinf(omega));
+ }
+};
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
deleted file mode 100644
index 0d6bd1f8024..00000000000
--- a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
+++ /dev/null
@@ -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);
- }
-}
diff --git a/source/blender/draw/engines/eevee/eevee_shader.cc b/source/blender/draw/engines/eevee/eevee_shader.cc
new file mode 100644
index 00000000000..e04e3d0bd21
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_shader.cc
@@ -0,0 +1,930 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Shader module that manage shader libraries, deferred compilation,
+ * and static shader usage.
+ */
+
+#include "eevee_shader.hh"
+#include "eevee_material.hh"
+
+extern "C" {
+extern char datatoc_common_attribute_lib_glsl[];
+extern char datatoc_common_debug_lib_glsl[];
+extern char datatoc_common_fullscreen_vert_glsl[];
+extern char datatoc_common_gpencil_lib_glsl[];
+extern char datatoc_common_hair_lib_glsl[];
+extern char datatoc_common_intersection_lib_glsl[];
+extern char datatoc_common_math_geom_lib_glsl[];
+extern char datatoc_common_math_lib_glsl[];
+extern char datatoc_common_obinfos_lib_glsl[];
+extern char datatoc_common_uniform_attribute_lib_glsl[];
+extern char datatoc_common_view_lib_glsl[];
+
+extern char datatoc_eevee_bsdf_lib_glsl[];
+extern char datatoc_eevee_bsdf_microfacet_lib_glsl[];
+extern char datatoc_eevee_bsdf_sampling_lib_glsl[];
+extern char datatoc_eevee_bsdf_stubs_lib_glsl[];
+extern char datatoc_eevee_camera_lib_glsl[];
+extern char datatoc_eevee_camera_velocity_frag_glsl[];
+extern char datatoc_eevee_closure_lib_glsl[];
+extern char datatoc_eevee_cubemap_lib_glsl[];
+extern char datatoc_eevee_culling_debug_frag_glsl[];
+extern char datatoc_eevee_culling_iter_lib_glsl[];
+extern char datatoc_eevee_culling_lib_glsl[];
+extern char datatoc_eevee_culling_select_comp_glsl[];
+extern char datatoc_eevee_culling_sort_comp_glsl[];
+extern char datatoc_eevee_culling_tile_comp_glsl[];
+extern char datatoc_eevee_deferred_direct_frag_glsl[];
+extern char datatoc_eevee_deferred_holdout_frag_glsl[];
+extern char datatoc_eevee_deferred_transparent_frag_glsl[];
+extern char datatoc_eevee_deferred_volume_frag_glsl[];
+extern char datatoc_eevee_depth_clear_frag_glsl[];
+extern char datatoc_eevee_depth_of_field_accumulator_lib_glsl[];
+extern char datatoc_eevee_depth_of_field_bokeh_lut_frag_glsl[];
+extern char datatoc_eevee_depth_of_field_downsample_frag_glsl[];
+extern char datatoc_eevee_depth_of_field_filter_frag_glsl[];
+extern char datatoc_eevee_depth_of_field_gather_frag_glsl[];
+extern char datatoc_eevee_depth_of_field_gather_holefill_frag_glsl[];
+extern char datatoc_eevee_depth_of_field_lib_glsl[];
+extern char datatoc_eevee_depth_of_field_reduce_copy_frag_glsl[];
+extern char datatoc_eevee_depth_of_field_reduce_downsample_frag_glsl[];
+extern char datatoc_eevee_depth_of_field_reduce_recursive_frag_glsl[];
+extern char datatoc_eevee_depth_of_field_resolve_frag_glsl[];
+extern char datatoc_eevee_depth_of_field_scatter_frag_glsl[];
+extern char datatoc_eevee_depth_of_field_scatter_lib_glsl[];
+extern char datatoc_eevee_depth_of_field_scatter_vert_glsl[];
+extern char datatoc_eevee_depth_of_field_setup_frag_glsl[];
+extern char datatoc_eevee_depth_of_field_tiles_dilate_frag_glsl[];
+extern char datatoc_eevee_depth_of_field_tiles_flatten_frag_glsl[];
+extern char datatoc_eevee_film_filter_frag_glsl[];
+extern char datatoc_eevee_film_lib_glsl[];
+extern char datatoc_eevee_film_resolve_depth_frag_glsl[];
+extern char datatoc_eevee_film_resolve_frag_glsl[];
+extern char datatoc_eevee_gbuffer_lib_glsl[];
+extern char datatoc_eevee_hiz_copy_frag_glsl[];
+extern char datatoc_eevee_hiz_downsample_frag_glsl[];
+extern char datatoc_eevee_irradiance_lib_glsl[];
+extern char datatoc_eevee_light_eval_lib_glsl[];
+extern char datatoc_eevee_light_lib_glsl[];
+extern char datatoc_eevee_lightprobe_display_cubemap_frag_glsl[];
+extern char datatoc_eevee_lightprobe_display_cubemap_vert_glsl[];
+extern char datatoc_eevee_lightprobe_display_grid_frag_glsl[];
+extern char datatoc_eevee_lightprobe_display_grid_vert_glsl[];
+extern char datatoc_eevee_lightprobe_display_lib_glsl[];
+extern char datatoc_eevee_lightprobe_eval_cubemap_lib_glsl[];
+extern char datatoc_eevee_lightprobe_eval_grid_lib_glsl[];
+extern char datatoc_eevee_lightprobe_filter_diffuse_frag_glsl[];
+extern char datatoc_eevee_lightprobe_filter_downsample_frag_glsl[];
+extern char datatoc_eevee_lightprobe_filter_geom_glsl[];
+extern char datatoc_eevee_lightprobe_filter_glossy_frag_glsl[];
+extern char datatoc_eevee_lightprobe_filter_lib_glsl[];
+extern char datatoc_eevee_lightprobe_filter_vert_glsl[];
+extern char datatoc_eevee_lightprobe_filter_visibility_frag_glsl[];
+extern char datatoc_eevee_lookdev_background_frag_glsl[];
+extern char datatoc_eevee_ltc_lib_glsl[];
+extern char datatoc_eevee_motion_blur_gather_frag_glsl[];
+extern char datatoc_eevee_motion_blur_lib_glsl[];
+extern char datatoc_eevee_motion_blur_tiles_dilate_frag_glsl[];
+extern char datatoc_eevee_motion_blur_tiles_flatten_frag_glsl[];
+extern char datatoc_eevee_nodetree_eval_lib_glsl[];
+extern char datatoc_eevee_raytrace_denoise_comp_glsl[];
+extern char datatoc_eevee_raytrace_raygen_frag_glsl[];
+extern char datatoc_eevee_raytrace_raygen_lib_glsl[];
+extern char datatoc_eevee_raytrace_resolve_frag_glsl[];
+extern char datatoc_eevee_raytrace_resolve_lib_glsl[];
+extern char datatoc_eevee_raytrace_trace_lib_glsl[];
+extern char datatoc_eevee_sampling_lib_glsl[];
+extern char datatoc_eevee_shadow_debug_frag_glsl[];
+extern char datatoc_eevee_shadow_lib_glsl[];
+extern char datatoc_eevee_shadow_page_alloc_comp_glsl[];
+extern char datatoc_eevee_shadow_page_copy_comp_glsl[];
+extern char datatoc_eevee_shadow_page_debug_comp_glsl[];
+extern char datatoc_eevee_shadow_page_defrag_comp_glsl[];
+extern char datatoc_eevee_shadow_page_free_comp_glsl[];
+extern char datatoc_eevee_shadow_page_init_comp_glsl[];
+extern char datatoc_eevee_shadow_page_lib_glsl[];
+extern char datatoc_eevee_shadow_page_mark_vert_glsl[];
+extern char datatoc_eevee_shadow_tilemap_depth_scan_comp_glsl[];
+extern char datatoc_eevee_shadow_tilemap_lod_mask_comp_glsl[];
+extern char datatoc_eevee_shadow_tilemap_lib_glsl[];
+extern char datatoc_eevee_shadow_tilemap_setup_comp_glsl[];
+extern char datatoc_eevee_shadow_tilemap_tag_comp_glsl[];
+extern char datatoc_eevee_shadow_tilemap_visibility_comp_glsl[];
+extern char datatoc_eevee_subsurface_eval_frag_glsl[];
+extern char datatoc_eevee_surface_background_frag_glsl[];
+extern char datatoc_eevee_surface_deferred_frag_glsl[];
+extern char datatoc_eevee_surface_depth_frag_glsl[];
+extern char datatoc_eevee_surface_depth_simple_frag_glsl[];
+extern char datatoc_eevee_surface_forward_frag_glsl[];
+extern char datatoc_eevee_surface_gpencil_vert_glsl[];
+extern char datatoc_eevee_surface_hair_vert_glsl[];
+extern char datatoc_eevee_surface_lib_glsl[];
+extern char datatoc_eevee_surface_lookdev_vert_glsl[];
+extern char datatoc_eevee_surface_mesh_geom_glsl[];
+extern char datatoc_eevee_surface_mesh_vert_glsl[];
+extern char datatoc_eevee_surface_velocity_frag_glsl[];
+extern char datatoc_eevee_surface_velocity_lib_glsl[];
+extern char datatoc_eevee_surface_velocity_mesh_vert_glsl[];
+extern char datatoc_eevee_surface_world_vert_glsl[];
+extern char datatoc_eevee_velocity_lib_glsl[];
+extern char datatoc_eevee_volume_deferred_frag_glsl[];
+extern char datatoc_eevee_volume_eval_lib_glsl[];
+extern char datatoc_eevee_volume_lib_glsl[];
+extern char datatoc_eevee_volume_vert_glsl[];
+
+extern char datatoc_eevee_shader_shared_hh[];
+
+extern char datatoc_gpu_shader_codegen_lib_glsl[];
+}
+
+namespace blender::eevee {
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Static shaders
+ *
+ * \{ */
+
+ShaderModule::ShaderModule()
+{
+ for (GPUShader *&shader : shaders_) {
+ shader = nullptr;
+ }
+
+ shared_lib_ = enum_preprocess(datatoc_eevee_shader_shared_hh);
+
+ shader_lib_ = DRW_shader_library_create();
+ /* NOTE: These need to be ordered by dependencies. */
+ DRW_SHADER_LIB_ADD(shader_lib_, common_debug_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, common_math_lib);
+ DRW_shader_library_add_file(shader_lib_, shared_lib_.c_str(), "eevee_shader_shared.hh");
+ DRW_SHADER_LIB_ADD(shader_lib_, common_math_geom_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, common_hair_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, common_view_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, common_intersection_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, common_attribute_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, common_obinfos_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, common_gpencil_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, gpu_shader_codegen_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_bsdf_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_bsdf_microfacet_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_bsdf_sampling_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_bsdf_stubs_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_irradiance_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_closure_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_cubemap_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_gbuffer_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_nodetree_eval_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_sampling_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_ltc_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_shadow_page_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_shadow_tilemap_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_shadow_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_camera_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_culling_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_culling_iter_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_light_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_light_eval_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_lightprobe_filter_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_lightprobe_display_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_lightprobe_eval_cubemap_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_lightprobe_eval_grid_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_volume_eval_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_volume_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_velocity_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_depth_of_field_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_depth_of_field_accumulator_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_depth_of_field_scatter_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_film_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_motion_blur_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_raytrace_trace_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_raytrace_raygen_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_raytrace_resolve_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_surface_lib);
+ DRW_SHADER_LIB_ADD(shader_lib_, eevee_surface_velocity_lib);
+
+ /* Meh ¯\_(ツ)_/¯. */
+ char *datatoc_nullptr_glsl = nullptr;
+
+#define SHADER(enum_, vert_, geom_, frag_, defs_) \
+ shader_descriptions_[enum_].name = STRINGIFY(enum_); \
+ shader_descriptions_[enum_].vertex_shader_code = datatoc_##vert_##_glsl; \
+ shader_descriptions_[enum_].geometry_shader_code = datatoc_##geom_##_glsl; \
+ shader_descriptions_[enum_].fragment_shader_code = datatoc_##frag_##_glsl; \
+ shader_descriptions_[enum_].defines_shader_code = defs_;
+#define SHADER_COMPUTE(enum_, comp_, defs_) \
+ shader_descriptions_[enum_].name = STRINGIFY(enum_); \
+ shader_descriptions_[enum_].compute_shader_code = datatoc_##comp_##_glsl; \
+ shader_descriptions_[enum_].defines_shader_code = defs_;
+
+#define SHADER_FULLSCREEN_DEFINES(enum_, frag_, defs_) \
+ SHADER(enum_, common_fullscreen_vert, nullptr, frag_, defs_)
+#define SHADER_FULLSCREEN(enum_, frag_) SHADER_FULLSCREEN_DEFINES(enum_, frag_, nullptr)
+
+ SHADER_FULLSCREEN(CULLING_DEBUG, eevee_culling_debug_frag);
+ SHADER_COMPUTE(CULLING_SELECT, eevee_culling_select_comp, nullptr);
+ SHADER_COMPUTE(CULLING_SORT, eevee_culling_sort_comp, nullptr);
+ SHADER_COMPUTE(CULLING_TILE, eevee_culling_tile_comp, nullptr);
+ SHADER_FULLSCREEN(FILM_FILTER, eevee_film_filter_frag);
+ SHADER_FULLSCREEN(FILM_RESOLVE, eevee_film_resolve_frag);
+ SHADER_FULLSCREEN(FILM_RESOLVE_DEPTH, eevee_film_resolve_depth_frag);
+ SHADER_FULLSCREEN(DEFERRED_EVAL_DIRECT, eevee_deferred_direct_frag);
+ SHADER_FULLSCREEN(DEFERRED_EVAL_HOLDOUT, eevee_deferred_holdout_frag);
+ SHADER_FULLSCREEN(DEFERRED_EVAL_TRANSPARENT, eevee_deferred_transparent_frag);
+ SHADER_FULLSCREEN(DEFERRED_EVAL_VOLUME, eevee_deferred_volume_frag);
+ SHADER(DEFERRED_MESH, eevee_surface_mesh_vert, nullptr, eevee_surface_deferred_frag, nullptr);
+ SHADER(DEFERRED_VOLUME, eevee_volume_vert, nullptr, eevee_volume_deferred_frag, nullptr);
+ SHADER_FULLSCREEN(HIZ_COPY, eevee_hiz_copy_frag);
+ SHADER_FULLSCREEN(HIZ_DOWNSAMPLE, eevee_hiz_downsample_frag);
+ SHADER_FULLSCREEN(DOF_BOKEH_LUT, eevee_depth_of_field_bokeh_lut_frag);
+ SHADER_FULLSCREEN(DOF_FILTER, eevee_depth_of_field_filter_frag);
+ SHADER_FULLSCREEN_DEFINES(DOF_GATHER_BACKGROUND_LUT,
+ eevee_depth_of_field_gather_frag,
+ "#define DOF_FOREGROUND_PASS false\n"
+ "#define DOF_BOKEH_TEXTURE true\n");
+ SHADER_FULLSCREEN_DEFINES(DOF_GATHER_BACKGROUND,
+ eevee_depth_of_field_gather_frag,
+ "#define DOF_FOREGROUND_PASS false\n"
+ "#define DOF_BOKEH_TEXTURE false\n");
+ SHADER_FULLSCREEN_DEFINES(DOF_GATHER_FOREGROUND_LUT,
+ eevee_depth_of_field_gather_frag,
+ "#define DOF_FOREGROUND_PASS true\n"
+ "#define DOF_BOKEH_TEXTURE true\n");
+ SHADER_FULLSCREEN_DEFINES(DOF_GATHER_FOREGROUND,
+ eevee_depth_of_field_gather_frag,
+ "#define DOF_FOREGROUND_PASS true\n"
+ "#define DOF_BOKEH_TEXTURE false\n");
+ SHADER_FULLSCREEN_DEFINES(DOF_GATHER_HOLEFILL,
+ eevee_depth_of_field_gather_holefill_frag,
+ "#define DOF_HOLEFILL_PASS true\n"
+ "#define DOF_FOREGROUND_PASS false\n"
+ "#define DOF_BOKEH_TEXTURE false\n");
+ SHADER_FULLSCREEN(DOF_REDUCE_COPY, eevee_depth_of_field_reduce_copy_frag);
+ SHADER_FULLSCREEN(DOF_REDUCE_DOWNSAMPLE, eevee_depth_of_field_reduce_downsample_frag);
+ SHADER_FULLSCREEN(DOF_REDUCE_RECURSIVE, eevee_depth_of_field_reduce_recursive_frag);
+ SHADER_FULLSCREEN_DEFINES(DOF_RESOLVE_LUT,
+ eevee_depth_of_field_resolve_frag,
+ "#define DOF_RESOLVE_PASS true\n"
+ "#define DOF_BOKEH_TEXTURE true\n"
+ "#define DOF_SLIGHT_FOCUS_DENSITY 2\n");
+ SHADER_FULLSCREEN_DEFINES(DOF_RESOLVE_LUT_HQ,
+ eevee_depth_of_field_resolve_frag,
+ "#define DOF_RESOLVE_PASS true\n"
+ "#define DOF_BOKEH_TEXTURE true\n"
+ "#define DOF_SLIGHT_FOCUS_DENSITY 4\n");
+ SHADER_FULLSCREEN_DEFINES(DOF_RESOLVE,
+ eevee_depth_of_field_resolve_frag,
+ "#define DOF_RESOLVE_PASS true\n"
+ "#define DOF_BOKEH_TEXTURE false\n"
+ "#define DOF_SLIGHT_FOCUS_DENSITY 2\n");
+ SHADER_FULLSCREEN_DEFINES(DOF_RESOLVE_HQ,
+ eevee_depth_of_field_resolve_frag,
+ "#define DOF_RESOLVE_PASS true\n"
+ "#define DOF_BOKEH_TEXTURE false\n"
+ "#define DOF_SLIGHT_FOCUS_DENSITY 4\n");
+ SHADER(DOF_SCATTER_BACKGROUND_LUT,
+ eevee_depth_of_field_scatter_vert,
+ nullptr,
+ eevee_depth_of_field_scatter_frag,
+ "#define DOF_FOREGROUND_PASS false\n"
+ "#define DOF_BOKEH_TEXTURE true\n");
+ SHADER(DOF_SCATTER_BACKGROUND,
+ eevee_depth_of_field_scatter_vert,
+ nullptr,
+ eevee_depth_of_field_scatter_frag,
+ "#define DOF_FOREGROUND_PASS false\n"
+ "#define DOF_BOKEH_TEXTURE false\n");
+ SHADER(DOF_SCATTER_FOREGROUND_LUT,
+ eevee_depth_of_field_scatter_vert,
+ nullptr,
+ eevee_depth_of_field_scatter_frag,
+ "#define DOF_FOREGROUND_PASS true\n"
+ "#define DOF_BOKEH_TEXTURE true\n");
+ SHADER(DOF_SCATTER_FOREGROUND,
+ eevee_depth_of_field_scatter_vert,
+ nullptr,
+ eevee_depth_of_field_scatter_frag,
+ "#define DOF_FOREGROUND_PASS true\n"
+ "#define DOF_BOKEH_TEXTURE false\n");
+ SHADER_FULLSCREEN(DOF_SETUP, eevee_depth_of_field_setup_frag);
+ SHADER_FULLSCREEN_DEFINES(DOF_TILES_DILATE_MINABS,
+ eevee_depth_of_field_tiles_dilate_frag,
+ "#define DILATE_MODE_MIN_MAX false\n");
+ SHADER_FULLSCREEN_DEFINES(DOF_TILES_DILATE_MINMAX,
+ eevee_depth_of_field_tiles_dilate_frag,
+ "#define DILATE_MODE_MIN_MAX true\n");
+ SHADER_FULLSCREEN(DOF_TILES_FLATTEN, eevee_depth_of_field_tiles_flatten_frag);
+ SHADER(LIGHTPROBE_DISPLAY_CUBEMAP,
+ eevee_lightprobe_display_cubemap_vert,
+ nullptr,
+ eevee_lightprobe_display_cubemap_frag,
+ nullptr);
+ SHADER(LIGHTPROBE_DISPLAY_IRRADIANCE,
+ eevee_lightprobe_display_grid_vert,
+ nullptr,
+ eevee_lightprobe_display_grid_frag,
+ nullptr);
+ SHADER(LIGHTPROBE_FILTER_DOWNSAMPLE_CUBE,
+ eevee_lightprobe_filter_vert,
+ eevee_lightprobe_filter_geom,
+ eevee_lightprobe_filter_downsample_frag,
+ "#define CUBEMAP\n");
+ SHADER(LIGHTPROBE_FILTER_GLOSSY,
+ eevee_lightprobe_filter_vert,
+ eevee_lightprobe_filter_geom,
+ eevee_lightprobe_filter_glossy_frag,
+ "#define CUBEMAP\n");
+ SHADER(LIGHTPROBE_FILTER_DIFFUSE,
+ eevee_lightprobe_filter_vert,
+ eevee_lightprobe_filter_geom,
+ eevee_lightprobe_filter_diffuse_frag,
+ nullptr);
+ SHADER(LIGHTPROBE_FILTER_VISIBILITY,
+ eevee_lightprobe_filter_vert,
+ eevee_lightprobe_filter_geom,
+ eevee_lightprobe_filter_visibility_frag,
+ nullptr);
+
+ SHADER_FULLSCREEN(LOOKDEV_BACKGROUND, eevee_lookdev_background_frag);
+ SHADER_FULLSCREEN(MOTION_BLUR_GATHER, eevee_motion_blur_gather_frag);
+ SHADER_FULLSCREEN(MOTION_BLUR_TILE_DILATE, eevee_motion_blur_tiles_dilate_frag);
+ SHADER_FULLSCREEN(MOTION_BLUR_TILE_FLATTEN, eevee_motion_blur_tiles_flatten_frag);
+
+ SHADER_FULLSCREEN_DEFINES(RAYTRACE_DIFFUSE, eevee_raytrace_raygen_frag, "#define DIFFUSE\n");
+ SHADER_FULLSCREEN_DEFINES(RAYTRACE_DIFFUSE_FALLBACK,
+ eevee_raytrace_raygen_frag,
+ "#define DIFFUSE\n"
+ "#define SKIP_TRACE\n");
+ SHADER_FULLSCREEN_DEFINES(
+ RAYTRACE_REFLECTION, eevee_raytrace_raygen_frag, "#define REFLECTION\n");
+ SHADER_FULLSCREEN_DEFINES(RAYTRACE_REFLECTION_FALLBACK,
+ eevee_raytrace_raygen_frag,
+ "#define REFLECTION\n"
+ "#define SKIP_TRACE\n");
+ SHADER_FULLSCREEN_DEFINES(
+ RAYTRACE_REFRACTION, eevee_raytrace_raygen_frag, "#define REFRACTION\n");
+ SHADER_FULLSCREEN_DEFINES(RAYTRACE_REFRACTION_FALLBACK,
+ eevee_raytrace_raygen_frag,
+ "#define REFRACTION\n"
+ "#define SKIP_TRACE\n");
+ SHADER_COMPUTE(RAYTRACE_DENOISE_DIFFUSE, eevee_raytrace_denoise_comp, "#define DIFFUSE\n");
+ SHADER_COMPUTE(RAYTRACE_DENOISE_REFLECTION, eevee_raytrace_denoise_comp, "#define REFLECTION\n");
+ SHADER_COMPUTE(RAYTRACE_DENOISE_REFRACTION, eevee_raytrace_denoise_comp, "#define REFRACTION\n");
+ SHADER_FULLSCREEN_DEFINES(
+ RAYTRACE_RESOLVE_DIFFUSE, eevee_raytrace_resolve_frag, "#define DIFFUSE\n");
+ SHADER_FULLSCREEN_DEFINES(
+ RAYTRACE_RESOLVE_REFLECTION, eevee_raytrace_resolve_frag, "#define REFLECTION\n");
+ SHADER_FULLSCREEN_DEFINES(
+ RAYTRACE_RESOLVE_REFRACTION, eevee_raytrace_resolve_frag, "#define REFRACTION\n");
+
+ SHADER_FULLSCREEN(SHADOW_DEBUG, eevee_shadow_debug_frag);
+ SHADER_COMPUTE(SHADOW_PAGE_ALLOC, eevee_shadow_page_alloc_comp, nullptr);
+ SHADER_COMPUTE(SHADOW_PAGE_COPY, eevee_shadow_page_copy_comp, nullptr);
+ SHADER_COMPUTE(SHADOW_PAGE_DEBUG, eevee_shadow_page_debug_comp, nullptr);
+ SHADER_COMPUTE(SHADOW_PAGE_DEFRAG, eevee_shadow_page_defrag_comp, nullptr);
+ SHADER_COMPUTE(SHADOW_PAGE_FREE, eevee_shadow_page_free_comp, nullptr);
+ SHADER_COMPUTE(SHADOW_PAGE_INIT, eevee_shadow_page_init_comp, nullptr);
+ SHADER(SHADOW_PAGE_MARK, eevee_shadow_page_mark_vert, nullptr, eevee_depth_clear_frag, nullptr);
+ SHADER_COMPUTE(SHADOW_TILE_DEPTH_SCAN, eevee_shadow_tilemap_depth_scan_comp, nullptr);
+ SHADER_COMPUTE(SHADOW_TILE_LOD_MASK, eevee_shadow_tilemap_lod_mask_comp, nullptr);
+ SHADER_COMPUTE(SHADOW_TILE_SETUP, eevee_shadow_tilemap_setup_comp, nullptr);
+ SHADER_COMPUTE(SHADOW_TILE_TAG_UPDATE, eevee_shadow_tilemap_tag_comp, "#define TAG_UPDATE\n");
+ SHADER_COMPUTE(SHADOW_TILE_TAG_USAGE, eevee_shadow_tilemap_tag_comp, "#define TAG_USAGE\n");
+ SHADER_COMPUTE(SHADOW_TILE_TAG_VISIBILITY, eevee_shadow_tilemap_visibility_comp, nullptr);
+
+ SHADER_FULLSCREEN(SUBSURFACE_EVAL, eevee_subsurface_eval_frag);
+
+ SHADER(VELOCITY_MESH,
+ eevee_surface_velocity_mesh_vert,
+ nullptr,
+ eevee_surface_velocity_frag,
+ nullptr);
+ SHADER_FULLSCREEN(VELOCITY_CAMERA, eevee_camera_velocity_frag);
+
+#undef SHADER
+#undef SHADER_FULLSCREEN
+#undef SHADER_FULLSCREEN_DEFINES
+#undef SHADER_COMPUTE
+
+#ifdef DEBUG
+ /* Ensure all shader are described. */
+ for (ShaderDescription &desc : shader_descriptions_) {
+ BLI_assert_msg(desc.name != nullptr, "EEVEE: Mising shader definition.");
+ if (desc.compute_shader_code == nullptr) {
+ BLI_assert(desc.vertex_shader_code != nullptr);
+ BLI_assert(desc.fragment_shader_code != nullptr);
+ }
+ }
+#endif
+}
+
+ShaderModule::~ShaderModule()
+{
+ for (GPUShader *&shader : shaders_) {
+ DRW_SHADER_FREE_SAFE(shader);
+ }
+ DRW_SHADER_LIB_FREE_SAFE(shader_lib_);
+}
+
+GPUShader *ShaderModule::static_shader_get(eShaderType shader_type)
+{
+ if (shaders_[shader_type] == nullptr) {
+ ShaderDescription &desc = shader_descriptions_[shader_type];
+ if (desc.compute_shader_code != nullptr) {
+ char *comp_with_lib = DRW_shader_library_create_shader_string(shader_lib_,
+ desc.compute_shader_code);
+
+ shaders_[shader_type] = GPU_shader_create_compute(
+ comp_with_lib, nullptr, desc.defines_shader_code, desc.name);
+
+ MEM_SAFE_FREE(comp_with_lib);
+ }
+ else {
+ shaders_[shader_type] = DRW_shader_create_with_shaderlib_ex(desc.vertex_shader_code,
+ desc.geometry_shader_code,
+ desc.fragment_shader_code,
+ shader_lib_,
+ desc.defines_shader_code,
+ desc.name);
+ }
+ if (shaders_[shader_type] == nullptr) {
+ fprintf(stderr, "EEVEE: error: Could not compile static shader \"%s\"\n", desc.name);
+ }
+ BLI_assert(shaders_[shader_type] != nullptr);
+ }
+ return shaders_[shader_type];
+}
+
+/* Run some custom preprocessor shader rewrite and returns a new string. */
+std::string ShaderModule::enum_preprocess(const char *input)
+{
+ std::string output = "";
+ /* Not failure safe but this only runs on static data. */
+ const char *cursor = input;
+ while ((cursor = strstr(cursor, "enum "))) {
+ output += StringRef(input, cursor - input);
+
+ /* Skip "enum" keyword. */
+ cursor = strstr(cursor, " ");
+
+ const char *enum_name = cursor;
+ cursor = strstr(cursor, " :");
+
+ output += "#define " + StringRef(enum_name, cursor - enum_name) + " uint\n";
+ output += "const uint ";
+
+ const char *enum_values = strstr(cursor, "{") + 1;
+ cursor = strstr(cursor, "}");
+ output += StringRef(enum_values, cursor - enum_values);
+
+ if (cursor != nullptr) {
+ /* Skip the curly bracket but not the semicolon. */
+ input = cursor + 1;
+ }
+ else {
+ input = nullptr;
+ }
+ }
+ if (input != nullptr) {
+ output += input;
+ }
+ return output;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GPU Materials
+ *
+ * \{ */
+
+char *ShaderModule::material_shader_code_defs_get(eMaterialGeometry geometry_type)
+{
+ std::string output = "";
+
+ switch (geometry_type) {
+ case MAT_GEOM_HAIR:
+ output += "#define MAT_GEOM_HAIR\n";
+ break;
+ case MAT_GEOM_GPENCIL:
+ output += "#define MAT_GEOM_GPENCIL\n";
+ output += "#define UNIFORM_RESOURCE_ID\n";
+ break;
+ default:
+ break;
+ }
+
+ return BLI_strdup(output.c_str());
+}
+
+char *ShaderModule::material_shader_code_vert_get(const GPUCodegenOutput *codegen,
+ GPUMaterial *mat,
+ eMaterialGeometry geometry_type)
+{
+ std::string output = "\n\n";
+
+ /* Might be needed by attr_load_orco. */
+ if (GPU_material_flag_get(mat, GPU_MATFLAG_OBJECT_INFO)) {
+ output += "#pragma BLENDER_REQUIRE(common_obinfos_lib.glsl)\n";
+ }
+
+ if (codegen->attribs_interface) {
+ /* Declare inputs. */
+ std::string delimiter = ";\n";
+ std::string sub(codegen->attribs_declare);
+ size_t start, pos = 0;
+ while ((pos = sub.find(delimiter)) != std::string::npos) {
+ switch (geometry_type) {
+ case MAT_GEOM_MESH:
+ /* Example print:
+ * in vec2 u015684; */
+ output += "in ";
+ output += sub.substr(0, pos + delimiter.length());
+ break;
+ case MAT_GEOM_HAIR:
+ /* Example print:
+ * uniform samplerBuffer u015684; */
+ output += "uniform samplerBuffer ";
+ start = sub.find(" ") + 1;
+ output += sub.substr(start, pos + delimiter.length() - start);
+ break;
+ case MAT_GEOM_GPENCIL:
+ /* Example print:
+ * vec2 u015684;
+ * These are not used and just here to make the attribs_load functions call valids.
+ * Only one uv and one color attribute layer is supported by gpencil objects. */
+ output += sub.substr(0, pos + delimiter.length());
+ break;
+ case MAT_GEOM_WORLD:
+ case MAT_GEOM_VOLUME:
+ case MAT_GEOM_LOOKDEV:
+ /* Not supported. */
+ break;
+ }
+ sub.erase(0, pos + delimiter.length());
+ }
+ output += "\n";
+
+ if (geometry_type != MAT_GEOM_WORLD) {
+ output += "IN_OUT AttributesInterface\n";
+ output += "{\n";
+ output += codegen->attribs_interface;
+ output += "};\n\n";
+ }
+ }
+
+ output += "void attrib_load(void)\n";
+ output += "{\n";
+ if (codegen->attribs_load && geometry_type != MAT_GEOM_WORLD) {
+ output += codegen->attribs_load;
+ }
+ output += "}\n\n";
+
+ if (ELEM(geometry_type, MAT_GEOM_MESH, MAT_GEOM_HAIR)) {
+ if (codegen->displacement) {
+ if (GPU_material_flag_get(mat, GPU_MATFLAG_UNIFORMS_ATTRIB)) {
+ output += datatoc_common_uniform_attribute_lib_glsl;
+ }
+ output += codegen->uniforms;
+ output += "\n";
+ output += codegen->library;
+ output += "\n";
+ }
+
+ output += "vec3 nodetree_displacement(void)\n";
+ output += "{\n";
+ if (codegen->displacement) {
+ output += codegen->displacement;
+ }
+ else {
+ output += "return vec3(0);\n";
+ }
+ output += "}\n\n";
+ }
+
+ switch (geometry_type) {
+ case MAT_GEOM_WORLD:
+ output += datatoc_eevee_surface_world_vert_glsl;
+ break;
+ case MAT_GEOM_VOLUME:
+ output += datatoc_eevee_volume_vert_glsl;
+ break;
+ case MAT_GEOM_GPENCIL:
+ output += datatoc_eevee_surface_gpencil_vert_glsl;
+ break;
+ case MAT_GEOM_LOOKDEV:
+ output += datatoc_eevee_surface_lookdev_vert_glsl;
+ break;
+ case MAT_GEOM_HAIR:
+ output += datatoc_eevee_surface_hair_vert_glsl;
+ break;
+ case MAT_GEOM_MESH:
+ default:
+ output += datatoc_eevee_surface_mesh_vert_glsl;
+ break;
+ }
+
+ return DRW_shader_library_create_shader_string(shader_lib_, output.c_str());
+}
+
+char *ShaderModule::material_shader_code_geom_get(const GPUCodegenOutput *codegen,
+ GPUMaterial *mat,
+ eMaterialGeometry geometry_type)
+{
+ /* Force geometry usage if GPU_BARYCENTRIC_DIST is used. */
+ if (!GPU_material_flag_get(mat, GPU_MATFLAG_BARYCENTRIC) ||
+ !ELEM(geometry_type, MAT_GEOM_GPENCIL, MAT_GEOM_MESH)) {
+ return nullptr;
+ }
+
+ StringRefNull interp_lib(datatoc_eevee_surface_lib_glsl);
+ int64_t start = interp_lib.find("SurfaceInterface");
+ int64_t end = interp_lib.find("interp");
+ StringRef interp_lib_stripped = interp_lib.substr(start, end - start);
+ std::string output = "\n\n";
+
+ if (codegen->attribs_interface) {
+ output += "in AttributesInterface\n";
+ output += "{\n";
+ output += codegen->attribs_interface;
+ output += "} attr_in[];\n\n";
+
+ output += "out AttributesInterface\n";
+ output += "{\n";
+ output += codegen->attribs_interface;
+ output += "} attr_out;\n\n";
+ }
+
+ output += "in ";
+ output += interp_lib_stripped;
+ output += "interp_in[];\n\n";
+
+ output += "out ";
+ output += interp_lib_stripped;
+ output += "interp_out;\n\n";
+
+ output += datatoc_eevee_surface_mesh_geom_glsl;
+
+ output += "void main(void)\n";
+ output += "{\n";
+ output += "interp_out.barycentric_dists = calc_barycentric_distances(";
+ output += " interp_in[0].P, interp_in[1].P, interp_in[2].P);\n ";
+
+ for (int i = 0; i < 3; i++) {
+ output += "{\n";
+ output += "const int vert_id = " + std::to_string(i) + ";\n";
+ output += "interp_out.barycentric_coords = calc_barycentric_co(vert_id);";
+ output += "gl_Position = gl_in[vert_id].gl_Position;";
+ if (codegen->attribs_passthrough) {
+ output += codegen->attribs_passthrough;
+ }
+ output += "EmitVertex();";
+ output += "}\n";
+ }
+ output += "}\n";
+
+ return DRW_shader_library_create_shader_string(shader_lib_, output.c_str());
+}
+
+char *ShaderModule::material_shader_code_frag_get(const GPUCodegenOutput *codegen,
+ GPUMaterial *gpumat,
+ eMaterialGeometry geometry_type,
+ eMaterialPipeline pipeline_type)
+{
+ std::string output = "\n\n";
+
+ /* World material loads attribs in fragment shader (only used for orco). */
+ if (geometry_type == MAT_GEOM_WORLD) {
+ if (codegen->attribs_interface) {
+ /* Declare inputs. */
+ std::string delimiter = ";\n";
+ std::string sub(codegen->attribs_declare);
+ size_t pos = 0;
+ while ((pos = sub.find(delimiter)) != std::string::npos) {
+ /* Example print:
+ * vec2 u015684;
+ * These are not used and just here to make the attribs_load functions call valids.
+ * Only orco layer is supported by world. */
+ output += sub.substr(0, pos + delimiter.length());
+ sub.erase(0, pos + delimiter.length());
+ }
+ output += "\n";
+
+ output += codegen->attribs_interface;
+ output += "\n";
+ }
+
+ output += "void attrib_load(void)\n";
+ output += "{\n";
+ if (codegen->attribs_interface) {
+ output += codegen->attribs_load;
+ }
+ output += "}\n\n";
+ }
+ else {
+ if (codegen->attribs_interface) {
+ output += "IN_OUT AttributesInterface\n";
+ output += "{\n";
+ output += codegen->attribs_interface;
+ output += "};\n\n";
+ }
+ }
+
+ if (codegen->surface || codegen->volume) {
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_UNIFORMS_ATTRIB)) {
+ output += datatoc_common_uniform_attribute_lib_glsl;
+ }
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_OBJECT_INFO)) {
+ output += "#pragma BLENDER_REQUIRE(common_obinfos_lib.glsl)\n";
+ }
+ output += codegen->uniforms;
+ output += "\n";
+ output += codegen->library;
+ output += "\n";
+ }
+
+ output += "Closure nodetree_surface(void)\n";
+ output += "{\n";
+ if (codegen->surface) {
+ output += codegen->surface;
+ }
+ else {
+ output += "return CLOSURE_DEFAULT;\n";
+ }
+ output += "}\n\n";
+
+ output += "Closure nodetree_volume(void)\n";
+ output += "{\n";
+ if (codegen->volume) {
+ output += codegen->volume;
+ }
+ else {
+ output += "return CLOSURE_DEFAULT;\n";
+ }
+ output += "}\n\n";
+
+ output += "float nodetree_thickness(void)\n";
+ output += "{\n";
+ if (codegen->thickness) {
+ output += codegen->thickness;
+ }
+ else {
+ /* TODO(fclem): Better default. */
+ output += "return 0.1;\n";
+ }
+ output += "}\n\n";
+
+ switch (geometry_type) {
+ case MAT_GEOM_WORLD:
+ output += datatoc_eevee_surface_background_frag_glsl;
+ break;
+ case MAT_GEOM_VOLUME:
+ switch (pipeline_type) {
+ case MAT_PIPE_DEFERRED:
+ output += datatoc_eevee_volume_deferred_frag_glsl;
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+ break;
+ default:
+ switch (pipeline_type) {
+ case MAT_PIPE_FORWARD_PREPASS:
+ output += datatoc_eevee_surface_depth_simple_frag_glsl;
+ break;
+ case MAT_PIPE_DEFERRED_PREPASS:
+ case MAT_PIPE_SHADOW:
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
+ output += datatoc_eevee_surface_depth_frag_glsl;
+ }
+ else {
+ output += datatoc_eevee_surface_depth_simple_frag_glsl;
+ }
+ break;
+ case MAT_PIPE_DEFERRED:
+ output += datatoc_eevee_surface_deferred_frag_glsl;
+ break;
+ case MAT_PIPE_FORWARD:
+ output += datatoc_eevee_surface_forward_frag_glsl;
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+ break;
+ }
+
+ return DRW_shader_library_create_shader_string(shader_lib_, output.c_str());
+}
+
+/* WATCH: This can be called from another thread! Needs to not touch the shader module in any
+ * thread unsafe manner. */
+GPUShaderSource ShaderModule::material_shader_code_generate(GPUMaterial *mat,
+ const GPUCodegenOutput *codegen)
+{
+ uint64_t shader_uuid = GPU_material_uuid_get(mat);
+
+ eMaterialPipeline pipeline_type;
+ eMaterialGeometry geometry_type;
+ material_type_from_shader_uuid(shader_uuid, pipeline_type, geometry_type);
+
+ GPUShaderSource source;
+ source.vertex = material_shader_code_vert_get(codegen, mat, geometry_type);
+ source.fragment = material_shader_code_frag_get(codegen, mat, geometry_type, pipeline_type);
+ source.geometry = material_shader_code_geom_get(codegen, mat, geometry_type);
+ source.defines = material_shader_code_defs_get(geometry_type);
+ return source;
+}
+
+static GPUShaderSource codegen_callback(void *thunk,
+ GPUMaterial *mat,
+ const GPUCodegenOutput *codegen)
+{
+ return ((ShaderModule *)thunk)->material_shader_code_generate(mat, codegen);
+}
+
+GPUMaterial *ShaderModule::material_shader_get(::Material *blender_mat,
+ struct bNodeTree *nodetree,
+ eMaterialPipeline pipeline_type,
+ eMaterialGeometry geometry_type,
+ bool deferred_compilation)
+{
+ uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type);
+
+ bool is_volume = (pipeline_type == MAT_PIPE_VOLUME);
+
+ return DRW_shader_from_material(
+ blender_mat, nodetree, shader_uuid, is_volume, deferred_compilation, codegen_callback, this);
+}
+
+GPUMaterial *ShaderModule::world_shader_get(::World *blender_world, struct bNodeTree *nodetree)
+{
+ eMaterialPipeline pipeline_type = MAT_PIPE_DEFERRED; /* Unused. */
+ eMaterialGeometry geometry_type = MAT_GEOM_WORLD;
+
+ uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type);
+
+ bool is_volume = (pipeline_type == MAT_PIPE_VOLUME);
+ bool deferred_compilation = false;
+
+ return DRW_shader_from_world(blender_world,
+ nodetree,
+ shader_uuid,
+ is_volume,
+ deferred_compilation,
+ codegen_callback,
+ this);
+}
+
+/* Variation to compile a material only with a nodetree. Caller needs to maintain the list of
+ * materials and call GPU_material_free on it to update the material. */
+GPUMaterial *ShaderModule::material_shader_get(const char *name,
+ ListBase &materials,
+ struct bNodeTree *nodetree,
+ eMaterialPipeline pipeline_type,
+ eMaterialGeometry geometry_type,
+ bool is_lookdev)
+{
+ uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type);
+
+ bool is_volume = (pipeline_type == MAT_PIPE_VOLUME);
+
+ GPUMaterial *gpumat = GPU_material_from_nodetree(nullptr,
+ nullptr,
+ nodetree,
+ &materials,
+ name,
+ shader_uuid,
+ is_volume,
+ is_lookdev,
+ codegen_callback,
+ this);
+ GPU_material_status_set(gpumat, GPU_MAT_QUEUED);
+ GPU_material_compile(gpumat);
+ return gpumat;
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_shader.hh b/source/blender/draw/engines/eevee/eevee_shader.hh
new file mode 100644
index 00000000000..ec7601b97c9
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_shader.hh
@@ -0,0 +1,191 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Shader module that manage shader libraries, deferred compilation,
+ * and static shader usage.
+ */
+
+#pragma once
+
+#include <array>
+#include <string>
+
+#include "BLI_string_ref.hh"
+#include "DRW_render.h"
+#include "GPU_material.h"
+#include "GPU_shader.h"
+
+#include "eevee_id_map.hh"
+
+namespace blender::eevee {
+
+/* Keep alphabetical order and clean prefix. */
+enum eShaderType {
+ CULLING_DEBUG = 0,
+ CULLING_SELECT,
+ CULLING_SORT,
+ CULLING_TILE,
+
+ DEFERRED_EVAL_DIRECT,
+ DEFERRED_EVAL_HOLDOUT,
+ DEFERRED_EVAL_TRANSPARENT,
+ DEFERRED_EVAL_VOLUME,
+
+ DEFERRED_MESH,
+ DEFERRED_VOLUME,
+
+ DOF_BOKEH_LUT,
+ DOF_GATHER_BACKGROUND_LUT,
+ DOF_GATHER_BACKGROUND,
+ DOF_FILTER,
+ DOF_GATHER_FOREGROUND_LUT,
+ DOF_GATHER_FOREGROUND,
+ DOF_GATHER_HOLEFILL,
+ DOF_REDUCE_COPY,
+ DOF_REDUCE_DOWNSAMPLE,
+ DOF_REDUCE_RECURSIVE,
+ DOF_RESOLVE,
+ DOF_RESOLVE_HQ,
+ DOF_RESOLVE_LUT,
+ DOF_RESOLVE_LUT_HQ,
+ DOF_SCATTER_BACKGROUND_LUT,
+ DOF_SCATTER_BACKGROUND,
+ DOF_SCATTER_FOREGROUND_LUT,
+ DOF_SCATTER_FOREGROUND,
+ DOF_SETUP,
+ DOF_TILES_DILATE_MINABS,
+ DOF_TILES_DILATE_MINMAX,
+ DOF_TILES_FLATTEN,
+
+ FILM_FILTER,
+ FILM_RESOLVE,
+ FILM_RESOLVE_DEPTH,
+
+ HIZ_COPY,
+ HIZ_DOWNSAMPLE,
+
+ LIGHTPROBE_DISPLAY_CUBEMAP,
+ LIGHTPROBE_DISPLAY_IRRADIANCE,
+
+ LIGHTPROBE_FILTER_DOWNSAMPLE_CUBE,
+ LIGHTPROBE_FILTER_GLOSSY,
+ LIGHTPROBE_FILTER_DIFFUSE,
+ LIGHTPROBE_FILTER_VISIBILITY,
+
+ LOOKDEV_BACKGROUND,
+
+ MOTION_BLUR_GATHER,
+ MOTION_BLUR_TILE_DILATE,
+ MOTION_BLUR_TILE_FLATTEN,
+
+ RAYTRACE_DIFFUSE,
+ RAYTRACE_DIFFUSE_FALLBACK,
+ RAYTRACE_REFLECTION,
+ RAYTRACE_REFLECTION_FALLBACK,
+ RAYTRACE_REFRACTION,
+ RAYTRACE_REFRACTION_FALLBACK,
+ RAYTRACE_DENOISE_DIFFUSE,
+ RAYTRACE_DENOISE_REFLECTION,
+ RAYTRACE_DENOISE_REFRACTION,
+ RAYTRACE_RESOLVE_DIFFUSE,
+ RAYTRACE_RESOLVE_REFLECTION,
+ RAYTRACE_RESOLVE_REFRACTION,
+
+ SHADOW_DEBUG,
+ SHADOW_PAGE_ALLOC,
+ SHADOW_PAGE_COPY,
+ SHADOW_PAGE_DEBUG,
+ SHADOW_PAGE_DEFRAG,
+ SHADOW_PAGE_FREE,
+ SHADOW_PAGE_INIT,
+ SHADOW_PAGE_MARK,
+ SHADOW_TILE_DEPTH_SCAN,
+ SHADOW_TILE_LOD_MASK,
+ SHADOW_TILE_SETUP,
+ SHADOW_TILE_TAG_UPDATE,
+ SHADOW_TILE_TAG_USAGE,
+ SHADOW_TILE_TAG_VISIBILITY,
+
+ SUBSURFACE_EVAL,
+
+ VELOCITY_CAMERA,
+ VELOCITY_MESH,
+
+ MAX_SHADER_TYPE,
+};
+
+/**
+ * Shader module. shared between instances.
+ */
+class ShaderModule {
+ private:
+ struct ShaderDescription {
+ const char *name = nullptr;
+ const char *vertex_shader_code = nullptr;
+ const char *geometry_shader_code = nullptr;
+ const char *fragment_shader_code = nullptr;
+ const char *compute_shader_code = nullptr;
+ const char *defines_shader_code = nullptr;
+ };
+
+ DRWShaderLibrary *shader_lib_ = nullptr;
+ std::array<GPUShader *, MAX_SHADER_TYPE> shaders_;
+ std::array<ShaderDescription, MAX_SHADER_TYPE> shader_descriptions_;
+ std::string shared_lib_;
+
+ public:
+ ShaderModule();
+ ~ShaderModule();
+
+ GPUShader *static_shader_get(eShaderType shader_type);
+ GPUMaterial *material_shader_get(::Material *blender_mat,
+ struct bNodeTree *nodetree,
+ eMaterialPipeline pipeline_type,
+ eMaterialGeometry geometry_type,
+ bool deferred_compilation);
+ GPUMaterial *world_shader_get(::World *blender_world, struct bNodeTree *nodetree);
+ GPUMaterial *material_shader_get(const char *name,
+ ListBase &materials,
+ struct bNodeTree *nodetree,
+ eMaterialPipeline pipeline_type,
+ eMaterialGeometry geometry_type,
+ bool is_lookdev);
+
+ GPUShaderSource material_shader_code_generate(GPUMaterial *mat, const GPUCodegenOutput *codegen);
+
+ private:
+ /* Run some custom preprocessor shader rewrite and returns a new string. */
+ std::string enum_preprocess(const char *input);
+
+ char *material_shader_code_defs_get(eMaterialGeometry geometry_type);
+ char *material_shader_code_vert_get(const GPUCodegenOutput *codegen,
+ GPUMaterial *mat,
+ eMaterialGeometry geometry_type);
+ char *material_shader_code_geom_get(const GPUCodegenOutput *codegen,
+ GPUMaterial *mat,
+ eMaterialGeometry geometry_type);
+ char *material_shader_code_frag_get(const GPUCodegenOutput *codegen,
+ GPUMaterial *mat,
+ eMaterialGeometry geometry_type,
+ eMaterialPipeline pipeline_type);
+};
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_shader_shared.hh b/source/blender/draw/engines/eevee/eevee_shader_shared.hh
new file mode 100644
index 00000000000..07690744367
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_shader_shared.hh
@@ -0,0 +1,934 @@
+
+/**
+ * Shared structures, enums & defines between C++ and GLSL.
+ * Can also include some math functions but they need to be simple enough to be valid in both
+ * language.
+ */
+
+/**
+ * NOTE: Enum support is not part of GLSL. It is handled by our own pre-processor pass in
+ * EEVEE's shader module.
+ *
+ * IMPORTANT:
+ * - Don't add trailing comma at the end of the enum. Our custom pre-processor will noy trim it
+ * for GLSL.
+ * - Always use `u` suffix for values. GLSL do not support implicit cast.
+ * - Define all values. This is in order to simplify custom pre-processor code.
+ * - Always use uint32_t as underlying type.
+ * - Use float suffix by default for float literals to avoid double promotion in C++.
+ * - Pack one float or int after a vec3/ivec3 to fullfil alligment rules.
+ *
+ * NOTE: Due to alignment restriction and buggy drivers, do not try to use mat3 inside structs.
+ * Do not use arrays of float. They are padded to arrays of vec4 and are not worth it.
+ *
+ * IMPORTANT: Don't forget to align mat4 and vec4 to 16 bytes.
+ **/
+
+#ifndef __cplusplus /* GLSL */
+# pragma BLENDER_REQUIRE(common_math_lib.glsl)
+# define BLI_STATIC_ASSERT_ALIGN(type_, align_)
+# define BLI_STATIC_ASSERT_SIZE(type_, size_)
+# define static
+# define inline
+# define cosf cos
+# define sinf sin
+# define tanf tan
+# define acosf acos
+# define asinf asin
+# define atanf atan
+# define floorf floor
+# define ceilf ceil
+# define sqrtf sqrt
+
+#else /* C++ */
+# pragma once
+
+# include "BLI_float4x4.hh"
+
+typedef float mat4[4][4];
+using vec4 = blender::float4;
+using vec3 = blender::float3;
+using vec2 = blender::float2;
+typedef int ivec4[4];
+using ivec3 = blender::int3;
+using ivec2 = blender::int2;
+typedef uint uvec4[4];
+typedef uint uvec3[3];
+typedef uint uvec2[2];
+typedef int bvec4[4];
+typedef int bvec2[2];
+/* Ugly but it does the job! */
+# define bool int
+
+# include "eevee_wrapper.hh"
+
+namespace blender::eevee {
+
+#endif
+
+#define UBO_MIN_MAX_SUPPORTED_SIZE 1 << 14
+
+/* -------------------------------------------------------------------- */
+/** \name Sampling
+ * \{ */
+
+enum eSamplingDimension : uint32_t {
+ SAMPLING_FILTER_U = 0u,
+ SAMPLING_FILTER_V = 1u,
+ SAMPLING_LENS_U = 2u,
+ SAMPLING_LENS_V = 3u,
+ SAMPLING_TIME = 4u,
+ SAMPLING_SHADOW_U = 5u,
+ SAMPLING_SHADOW_V = 6u,
+ SAMPLING_SHADOW_W = 7u,
+ SAMPLING_SHADOW_X = 8u,
+ SAMPLING_SHADOW_Y = 9u,
+ SAMPLING_CLOSURE = 10u,
+ SAMPLING_LIGHTPROBE = 11u,
+ SAMPLING_TRANSPARENCY = 12u,
+ 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 vec4 in GLSL. Using vec4 for now to get the same alignment
+ * but this is wasteful. */
+ vec4 dimensions[SAMPLING_DIMENSION_COUNT];
+};
+BLI_STATIC_ASSERT_ALIGN(SamplingData, 16)
+
+/* Returns total sample count in a web pattern of the given size. */
+static inline int web_sample_count_get(int web_density, int ring_count)
+{
+ return ((ring_count * ring_count + ring_count) / 2) * web_density + 1;
+}
+
+/* Returns lowest possible ring count that contains at least sample_count samples. */
+static inline int web_ring_count_get(int web_density, int sample_count)
+{
+ /* Inversion of web_sample_count_get(). */
+ float x = 2.0f * (float(sample_count) - 1.0f) / float(web_density);
+ /* Solving polynomial. We only search positive solution. */
+ float discriminant = 1.0f + 4.0f * x;
+ return int(ceilf(0.5f * (sqrtf(discriminant) - 1.0f)));
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Camera
+ * \{ */
+
+enum eCameraType : uint32_t {
+ CAMERA_PERSP = 0u,
+ CAMERA_ORTHO = 1u,
+ CAMERA_PANO_EQUIRECT = 2u,
+ CAMERA_PANO_EQUISOLID = 3u,
+ CAMERA_PANO_EQUIDISTANT = 4u,
+ CAMERA_PANO_MIRROR = 5u
+};
+
+static inline bool is_panoramic(eCameraType type)
+{
+ return type > CAMERA_ORTHO;
+}
+
+struct CameraData {
+ /* View Matrices of the camera, not from any view! */
+ mat4 persmat;
+ mat4 persinv;
+ mat4 viewmat;
+ mat4 viewinv;
+ mat4 winmat;
+ mat4 wininv;
+ /** Camera UV scale and bias. Also known as viewcamtexcofac. */
+ vec2 uv_scale;
+ vec2 uv_bias;
+ /** Panorama parameters. */
+ vec2 equirect_scale;
+ vec2 equirect_scale_inv;
+ vec2 equirect_bias;
+ float fisheye_fov;
+ float fisheye_lens;
+ /** Clipping distances. */
+ float clip_near;
+ float clip_far;
+ /** Film pixel filter radius. */
+ float filter_size;
+ eCameraType type;
+};
+BLI_STATIC_ASSERT_ALIGN(CameraData, 16)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Film
+ * \{ */
+
+enum 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. */
+ ivec2 extent;
+ /** Offset of the render target in the full-res frame, in pixels. */
+ ivec2 offset;
+ /** Scale and bias to filter only a region of the render (aka. render_border). */
+ vec2 uv_bias;
+ vec2 uv_scale;
+ vec2 uv_scale_inv;
+ /** Data type stored by this film. */
+ eFilmDataType data_type;
+ /** Is true if history is valid and can be sampled. Bypassing history to resets accumulation. */
+ bool use_history;
+ /** Used for fade-in effect. */
+ float opacity;
+ /** Padding to sizeof(vec4). */
+ int _pad0, _pad1, _pad2;
+};
+BLI_STATIC_ASSERT_ALIGN(FilmData, 16)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Depth of field
+ * \{ */
+
+/* 5% error threshold. */
+#define DOF_FAST_GATHER_COC_ERROR 0.05
+#define DOF_GATHER_RING_COUNT 5
+#define DOF_DILATE_RING_COUNT 3
+#define DOF_TILE_DIVISOR 16
+#define DOF_BOKEH_LUT_SIZE 32
+
+struct DepthOfFieldData {
+ /** Size of the render targets for gather & scatter passes. */
+ ivec2 extent;
+ /** Size of a pixel in uv space (1.0 / extent). */
+ vec2 texel_size;
+ /** Bokeh Scale factor. */
+ vec2 bokeh_anisotropic_scale;
+ vec2 bokeh_anisotropic_scale_inv;
+ /* Correction factor to align main target pixels with the filtered mipmap chain texture. */
+ vec2 gather_uv_fac;
+ /** Scatter parameters. */
+ float scatter_coc_threshold;
+ float scatter_color_threshold;
+ float scatter_neighbor_max_color;
+ int scatter_sprite_per_row;
+ /** Downsampling paramters. */
+ float denoise_factor;
+ /** Bokeh Shape parameters. */
+ float bokeh_blades;
+ float bokeh_rotation;
+ float bokeh_aperture_ratio;
+ /** Circle of confusion (CoC) parameters. */
+ float coc_mul;
+ float coc_bias;
+ float coc_abs_max;
+ /** Copy of camera type. */
+ eCameraType camera_type;
+ int _pad0, _pad1;
+};
+BLI_STATIC_ASSERT_ALIGN(DepthOfFieldData, 16)
+
+static inline float coc_radius_from_camera_depth(DepthOfFieldData dof, float depth)
+{
+ depth = (dof.camera_type != CAMERA_ORTHO) ? 1.0f / depth : depth;
+ return dof.coc_mul * depth + dof.coc_bias;
+}
+
+static inline float regular_polygon_side_length(float sides_count)
+{
+ return 2.0f * sinf(M_PI / sides_count);
+}
+
+/* Returns intersection ratio between the radius edge at theta and the regular polygon edge.
+ * Start first corners at theta == 0. */
+static inline float circle_to_polygon_radius(float sides_count, float theta)
+{
+ /* From Graphics Gems from CryENGINE 3 (Siggraph 2013) by Tiago Sousa (slide
+ * 36). */
+ float side_angle = (2.0f * M_PI) / sides_count;
+ return cosf(side_angle * 0.5f) /
+ cosf(theta - side_angle * floorf((sides_count * theta + M_PI) / (2.0f * M_PI)));
+}
+
+/* Remap input angle to have homogenous spacing of points along a polygon edge.
+ * Expects theta to be in [0..2pi] range. */
+static inline float circle_to_polygon_angle(float sides_count, float theta)
+{
+ float side_angle = (2.0f * M_PI) / sides_count;
+ float halfside_angle = side_angle * 0.5f;
+ float side = floorf(theta / side_angle);
+ /* Length of segment from center to the middle of polygon side. */
+ float adjacent = circle_to_polygon_radius(sides_count, 0.0f);
+
+ /* This is the relative position of the sample on the polygon half side. */
+ float local_theta = theta - side * side_angle;
+ float ratio = (local_theta - halfside_angle) / halfside_angle;
+
+ float halfside_len = regular_polygon_side_length(sides_count) * 0.5f;
+ float opposite = ratio * halfside_len;
+
+ /* NOTE: atan(y_over_x) has output range [-M_PI_2..M_PI_2]. */
+ float final_local_theta = atanf(opposite / adjacent);
+
+ return side * side_angle + final_local_theta;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name VelocityModule
+ * \{ */
+
+struct VelocityObjectData {
+ mat4 next_object_mat;
+ mat4 prev_object_mat;
+};
+BLI_STATIC_ASSERT_ALIGN(VelocityObjectData, 16)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Motion Blur
+ * \{ */
+
+#define MB_TILE_DIVISOR 32
+
+struct MotionBlurData {
+ /** Motion vector lengths are clamped to this maximum. A value of 0 means effect is bypassed. */
+ float blur_max;
+ /** Depth scaling factor. Avoid bluring background behind moving objects. */
+ float depth_scale;
+ /** As the name suggests. Used to avoid a division in the sampling. */
+ vec2 target_size_inv;
+ /** Viewport motion blur only blurs using previous frame vectors. */
+ bool is_viewport;
+ int _pad0, _pad1, _pad2;
+};
+BLI_STATIC_ASSERT_ALIGN(MotionBlurData, 16)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Cullings
+ * \{ */
+
+/* TODO(fclem) Rename this. Only used by probes now. */
+#define CULLING_ITEM_BATCH 128
+/* Number of items we can cull. Limited by how we store CullingZBin. */
+#define CULLING_MAX_ITEM 65536
+/* Number of items in a culling batch. Needs to be Power of 2. Must be <= to 65536. */
+/* Current limiting factor is the sorting phase which is single pass and only sort within a
+ * threadgroup which maximum size is 1024. */
+#define CULLING_BATCH_SIZE 1024
+/* Maximum number of 32 bit uint stored per tile. */
+#define CULLING_MAX_WORD (CULLING_BATCH_SIZE / 32)
+/* Fine grained subdivision in the Z direction (Must be multiple of CULLING_BATCH_SIZE). */
+#define CULLING_ZBIN_COUNT 4096
+
+struct CullingData {
+ /** Scale applied to tile pixel coordinates to get target UV coordinate. */
+ vec2 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.. */
+ bool enable_specular;
+ /** Extent of one square tile in pixels. */
+ uint tile_size;
+ /** Number of tiles on the X/Y axis. */
+ uint tile_x_len;
+ uint tile_y_len;
+ /** Number of word per tile. Depends on the maximum number of lights. */
+ uint tile_word_len;
+};
+BLI_STATIC_ASSERT_ALIGN(CullingData, 16)
+
+#define CullingZBin uint
+#define CullingWord uint
+
+static inline int culling_z_to_zbin(CullingData data, float z)
+{
+ return int(z * data.zbin_scale + data.zbin_bias);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Shadows
+ * \{ */
+
+/**
+ * Shadow data for either a directional shadow or a punctual shadow.
+ *
+ * A punctual shadow is composed of 1, 5 or 6 shadow regions.
+ * Regions are sorted in this order -Z, +X, -X, +Y, -Y, +Z.
+ * Face index is computed from light's object space coordinates.
+ *
+ * A directional light shadow is composed of multiple clipmaps with each level
+ * covering twice as much area as the previous one.
+ */
+struct ShadowData {
+ /**
+ * Point : Unused.
+ * Directional : Rotation matrix to local light coordinate.
+ * The scale is uniform for the Z axis.
+ * For the X & Y axes, it is scaled to be the size of a tile.
+ * Origin is the one of the largest clipmap.
+ * So after transformation, you are in the tilemap space [0..SHADOW_TILEMAP_RES]
+ * of the largest clipmap.
+ */
+ mat4 mat;
+ /** NOTE: It is ok to use vec3 here. A float is declared right after it.
+ * vec3 is also aligned to 16 bytes. */
+ /** Shadow offset caused by jittering projection origin (for soft shadows). */
+ vec3 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. */
+ ivec2 base_offset;
+};
+BLI_STATIC_ASSERT_ALIGN(ShadowData, 16)
+
+/**
+ * IMPORTANT: Some data packing are tweaked for these values.
+ * Be sure to update them accordingly.
+ * SHADOW_TILEMAP_RES max is 32 because of the shared bitmaps used for LOD tagging.
+ * It is also limited by the maximum thread group size (1024).
+ */
+#define SHADOW_TILEMAP_RES 16
+#define SHADOW_TILEMAP_LOD 4 /* LOG2(SHADOW_TILEMAP_RES) */
+#define SHADOW_TILEMAP_PER_ROW 64
+#define SHADOW_PAGE_COPY_GROUP_SIZE 32
+#define SHADOW_DEPTH_SCAN_GROUP_SIZE 32
+#define SHADOW_AABB_TAG_GROUP_SIZE 64
+#define SHADOW_MAX_TILEMAP 4096
+#define SHADOW_MAX_PAGE 4096
+#define SHADOW_PAGE_PER_ROW 64
+
+#define SHADOW_DEBUG_PAGE_ALLOCATION_ENABLED
+#define SHADOW_DEBUG_TILE_ALLOCATION_ENABLED
+/** Debug shadow tile allocation. */
+// #define SHADOW_DEBUG_NO_CACHING
+/* Debug: Comment to only use BBox tagging instead of depth scanning. */
+// #define SHADOW_DEBUG_NO_DEPTH_SCAN
+/* Debug: Will freeze the camera used for shadow tagging if G.debug_value is >= 4. */
+// #define SHADOW_DEBUG_FREEZE_CAMERA
+
+#if defined(SHADOW_DEBUG_FREEZE_CAMERA) && defined(SHADOW_DEBUG_NO_DEPTH_SCAN)
+# error Freeze camera debug option is incompatible with depth scanning.
+#endif
+
+/* Given an input tile coordinate [0..SHADOW_TILEMAP_RES] returns the coordinate in NDC [-1..1]. */
+static inline vec2 shadow_tile_coord_to_ndc(ivec2 tile)
+{
+ vec2 co = vec2(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]). */
+ mat4 tilemat;
+ /** Corners of the frustum. */
+ vec4 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. */
+ ivec2 grid_shift;
+ /** True for punctual lights. */
+ bool is_cubeface;
+ /** Index inside the tilemap allocator. */
+ int index;
+ /** Cone direction for punctual shadows. */
+ vec3 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. */
+ mat4 object_mat;
+ /** Packed data in the last column of the object_mat. */
+#define _area_size_x object_mat[0][3]
+#define _area_size_y object_mat[1][3]
+#define _radius _area_size_x
+#define _spot_mul object_mat[2][3]
+#define _spot_bias object_mat[3][3]
+ /** Aliases for axes. */
+#ifdef __cplusplus
+# define _right object_mat[0]
+# define _up object_mat[1]
+# define _back object_mat[2]
+# define _position object_mat[3]
+#else
+# define _right object_mat[0].xyz
+# define _up object_mat[1].xyz
+# define _back object_mat[2].xyz
+# define _position object_mat[3].xyz
+#endif
+ /** Influence radius (inversed and squared) adjusted for Surface / Volume power. */
+ float influence_radius_invsqr_surface;
+ float influence_radius_invsqr_volume;
+ /** Maximum influence radius. Used for culling. */
+ float influence_radius_max;
+ /** Index of the shadow struct on CPU. -1 means no shadow. */
+ int shadow_id;
+ /** NOTE: It is ok to use vec3 here. A float is declared right after it.
+ * vec3 is also aligned to 16 bytes. */
+ vec3 color;
+ /** Power depending on shader type. */
+ float diffuse_power;
+ float specular_power;
+ float volume_power;
+ float transmit_power;
+ /** Special radius factor for point lighting. */
+ float radius_squared;
+ /** Light Type. */
+ eLightType type;
+ /** Padding to sizeof(vec2). */
+ float _pad1;
+ /** Spot size. Aligned to size of vec2. */
+ vec2 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;
+ vec3 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 {
+ mat4 lookdev_rotation;
+ /** Total of visibility cells per row and layer. */
+ int visibility_cells_per_row;
+ int visibility_cells_per_layer;
+ /** Size of visibility cell. */
+ int visibility_size;
+ /** Number of irradiance cells per row. */
+ int irradiance_cells_per_row;
+ /** Smooth irradiance sample interpolation but increases light leaks. */
+ float irradiance_smooth;
+ /** Total number of active irradiance grid including world. */
+ int grid_count;
+ /** Display size of sample spheres. */
+ float display_size;
+ float _pad0;
+};
+BLI_STATIC_ASSERT_ALIGN(GridInfoData, 16)
+
+/**
+ * Data for a single irradiance grid.
+ */
+struct GridData {
+ /** Object matrix inverse (World -> Local). */
+ mat4 local_mat;
+ /** Resolution of the light grid. */
+ ivec3 resolution;
+ /** Offset of the first cell of this grid in the grid texture. */
+ int offset;
+ /** World space vector between 2 adjacent cells. */
+ vec3 increment_x;
+ /** Attenuation Bias. */
+ float attenuation_bias;
+ /** World space vector between 2 adjacent cells. */
+ vec3 increment_y;
+ /** Attenuation scaling. */
+ float attenuation_scale;
+ /** World space vector between 2 adjacent cells. */
+ vec3 increment_z;
+ /** Number of grid levels not ready for display during baking. */
+ int level_skip;
+ /** World space corner position. */
+ vec3 corner;
+ /** Visibility test parameters. */
+ float visibility_range;
+ float visibility_bleed;
+ float visibility_bias;
+ float _pad0;
+ float _pad1;
+};
+BLI_STATIC_ASSERT_ALIGN(GridData, 16)
+
+static inline ivec3 grid_cell_index_to_coordinate(int cell_id, ivec3 resolution)
+{
+ ivec3 cell_coord;
+ cell_coord.z = cell_id % resolution.z;
+ cell_coord.y = (cell_id / resolution.z) % resolution.y;
+ cell_coord.x = cell_id / (resolution.z * resolution.y);
+ return cell_coord;
+}
+
+/**
+ * Common data to all cubemaps.
+ */
+struct CubemapInfoData {
+ mat4 lookdev_rotation;
+ /** LOD containing data for roughness of 1. */
+ float roughness_max_lod;
+ /** Total number of active cubemaps including world. */
+ int cube_count;
+ /** Display size of sample spheres. */
+ float display_size;
+ float _pad2;
+};
+BLI_STATIC_ASSERT_ALIGN(CubemapInfoData, 16)
+
+#define CUBEMAP_SHAPE_SPHERE 0.0
+#define CUBEMAP_SHAPE_BOX 1.0
+
+/**
+ * Data for a single reflection cubemap probe.
+ */
+struct CubemapData {
+ /** Influence shape matrix (World -> Local). */
+ mat4 influence_mat;
+ /** Packed data in the last column of the influence_mat. */
+#define _attenuation_factor influence_mat[0][3]
+#define _attenuation_type influence_mat[1][3]
+#define _parallax_type influence_mat[2][3]
+ /** Layer of the cube array to sample. */
+#define _layer influence_mat[3][3]
+ /** Parallax shape matrix (World -> Local). */
+ mat4 parallax_mat;
+ /** Packed data in the last column of the parallax_mat. */
+#define _world_position_x parallax_mat[0][3]
+#define _world_position_y parallax_mat[1][3]
+#define _world_position_z parallax_mat[2][3]
+};
+BLI_STATIC_ASSERT_ALIGN(CubemapData, 16)
+
+struct LightProbeInfoData {
+ GridInfoData grids;
+ CubemapInfoData cubes;
+};
+BLI_STATIC_ASSERT_ALIGN(LightProbeInfoData, 16)
+
+#define GRID_MAX 64
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Hierarchical-Z Buffer
+ * \{ */
+
+struct HiZData {
+ /** Scale factor to remove HiZBuffer padding. */
+ vec2 uv_scale;
+ /** Scale factor to convert from pixel space to Normalized Device Coordinates [-1..1]. */
+ vec2 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. */
+ mat4 history_persmat;
+ /** False if the history buffer was just allocated and contains uninitialized data. */
+ bool valid_history_diffuse;
+ bool valid_history_reflection;
+ bool 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 vec4 for alignment. */
+ vec4 samples[SSS_SAMPLE_MAX];
+ /** Sample index after which samples are not randomly rotated anymore. */
+ int jitter_threshold;
+ /** Number of samples precomputed in the set. */
+ int sample_len;
+ int _pad0;
+ int _pad1;
+};
+BLI_STATIC_ASSERT_ALIGN(SubsurfaceData, 16)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Utility Texture
+ * \{ */
+
+#define UTIL_TEX_SIZE 64
+#define UTIL_BTDF_LAYER_COUNT 16
+/* Scale and bias to avoid interpolation of the border pixel.
+ * Remap UVs to the border pixels centers. */
+#define UTIL_TEX_UV_SCALE ((UTIL_TEX_SIZE - 1.0f) / UTIL_TEX_SIZE)
+#define UTIL_TEX_UV_BIAS (0.5f / UTIL_TEX_SIZE)
+
+#define UTIL_BLUE_NOISE_LAYER 0
+#define UTIL_LTC_MAT_LAYER 1
+#define UTIL_LTC_MAG_LAYER 2
+#define UTIL_BSDF_LAYER 2
+#define UTIL_BTDF_LAYER 3
+#define UTIL_DISK_INTEGRAL_LAYER 3
+#define UTIL_DISK_INTEGRAL_COMP 2
+
+#ifndef __cplusplus
+/* For codestyle reasons, we do not declare samplers in lib files. Use a prototype instead. */
+vec4 utility_tx_fetch(vec2 texel, float layer);
+vec4 utility_tx_sample(vec2 uv, float layer);
+
+/* Fetch texel. Wrapping if above range. */
+# define utility_tx_fetch_define(utility_tx_) \
+ vec4 utility_tx_fetch(vec2 texel, float layer) \
+ { \
+ return texelFetch(utility_tx_, ivec3(ivec2(texel) % UTIL_TEX_SIZE, layer), 0); \
+ }
+
+/* Sample at uv position. Filtered & Wrapping enabled. */
+# define utility_tx_sample_define(utility_tx_) \
+ vec4 utility_tx_sample(vec2 uv, float layer) \
+ { \
+ return textureLod(utility_tx_, vec3(uv, layer), 0.0); \
+ }
+
+/* Stubs declarations if not using it. */
+# define utility_tx_fetch_define_stub(utility_tx_) \
+ vec4 utility_tx_fetch(vec2 texel, float layer) \
+ { \
+ return vec4(0); \
+ }
+# define utility_tx_sample_define_stub(utility_tx_) \
+ vec4 utility_tx_sample(vec2 uv, float layer) \
+ { \
+ return vec4(0); \
+ }
+#endif
+
+/** \} */
+
+#ifdef __cplusplus
+using CameraDataBuf = StructBuffer<CameraData>;
+using CubemapDataBuf = StructArrayBuffer<CubemapData, CULLING_ITEM_BATCH>;
+using CullingDataBuf = StorageBuffer<CullingData>;
+using CullingKeyBuf = StorageArrayBuffer<uint, CULLING_BATCH_SIZE, true>;
+using CullingLightBuf = StorageArrayBuffer<LightData, CULLING_BATCH_SIZE, true>;
+using CullingTileBuf = StorageArrayBuffer<uint, 16 * 16 * CULLING_MAX_WORD, true>;
+using CullingZbinBuf = StorageArrayBuffer<uint, CULLING_ZBIN_COUNT, true>;
+using DepthOfFieldDataBuf = StructBuffer<DepthOfFieldData>;
+using GridDataBuf = StructArrayBuffer<GridData, GRID_MAX>;
+using HiZDataBuf = StructBuffer<HiZData>;
+using LightDataBuf = StorageArrayBuffer<LightData, CULLING_BATCH_SIZE>;
+using LightProbeFilterDataBuf = StructBuffer<LightProbeFilterData>;
+using LightProbeInfoDataBuf = StructBuffer<LightProbeInfoData>;
+using RaytraceBufferDataBuf = StructBuffer<RaytraceBufferData>;
+using RaytraceDataBuf = StructBuffer<RaytraceData>;
+using ShadowDataBuf = StorageArrayBuffer<ShadowData, CULLING_BATCH_SIZE>;
+using ShadowDebugDataBuf = StructBuffer<ShadowDebugData>;
+using ShadowPagesInfoDataBuf = StorageBuffer<ShadowPagesInfoData, true>;
+using ShadowPageHeapBuf = StorageArrayBuffer<uint, SHADOW_MAX_PAGE, true>;
+using ShadowTileMapDataBuf = StorageArrayBuffer<ShadowTileMapData, SHADOW_MAX_TILEMAP>;
+using SubsurfaceDataBuf = StructBuffer<SubsurfaceData>;
+using VelocityObjectBuf = StructBuffer<VelocityObjectData>;
+
+# undef bool
+} // namespace blender::eevee
+#endif
diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c
deleted file mode 100644
index c4108969c99..00000000000
--- a/source/blender/draw/engines/eevee/eevee_shaders.c
+++ /dev/null
@@ -1,1652 +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_lib_id.h"
-#include "BKE_node.h"
-
-#include "BLI_dynstr.h"
-#include "BLI_string_utils.h"
-
-#include "DNA_world_types.h"
-
-#include "MEM_guardedalloc.h"
-
-#include "GPU_capabilities.h"
-#include "GPU_material.h"
-#include "GPU_shader.h"
-
-#include "NOD_shader.h"
-
-#include "eevee_engine.h"
-#include "eevee_private.h"
-
-static const char *filter_defines =
-#if defined(IRRADIANCE_SH_L2)
- "#define IRRADIANCE_SH_L2\n";
-#elif defined(IRRADIANCE_HL2)
- "#define IRRADIANCE_HL2\n";
-#endif
-
-static struct {
- /* Lookdev */
- struct GPUShader *studiolight_probe_sh;
- struct GPUShader *studiolight_background_sh;
-
- /* Probes */
- struct GPUShader *probe_grid_display_sh;
- struct GPUShader *probe_cube_display_sh;
- struct GPUShader *probe_planar_display_sh;
- struct GPUShader *probe_filter_glossy_sh;
- struct GPUShader *probe_filter_diffuse_sh;
- struct GPUShader *probe_filter_visibility_sh;
- struct GPUShader *probe_grid_fill_sh;
- struct GPUShader *probe_planar_downsample_sh;
-
- /* Velocity Resolve */
- struct GPUShader *velocity_resolve_sh;
-
- /* Temporal Anti Aliasing */
- struct GPUShader *taa_resolve_sh;
- struct GPUShader *taa_resolve_reproject_sh;
-
- /* Bloom */
- struct GPUShader *bloom_blit_sh[2];
- struct GPUShader *bloom_downsample_sh[2];
- struct GPUShader *bloom_upsample_sh[2];
- struct GPUShader *bloom_resolve_sh[2];
-
- /* Depth Of Field */
- struct GPUShader *dof_bokeh_sh;
- struct GPUShader *dof_setup_sh;
- struct GPUShader *dof_flatten_tiles_sh;
- struct GPUShader *dof_dilate_tiles_sh[2];
- struct GPUShader *dof_downsample_sh;
- struct GPUShader *dof_reduce_sh[2];
- struct GPUShader *dof_gather_sh[DOF_GATHER_MAX_PASS][2];
- struct GPUShader *dof_filter_sh;
- struct GPUShader *dof_scatter_sh[2][2];
- struct GPUShader *dof_resolve_sh[2][2];
-
- /* General purpose Shaders. */
- struct GPUShader *lookdev_background;
- struct GPUShader *update_noise_sh;
-
- /* Down-sample Depth */
- struct GPUShader *minz_downlevel_sh;
- struct GPUShader *maxz_downlevel_sh;
- struct GPUShader *minz_downdepth_sh;
- struct GPUShader *maxz_downdepth_sh;
- struct GPUShader *minz_downdepth_layer_sh;
- struct GPUShader *maxz_downdepth_layer_sh;
- struct GPUShader *maxz_copydepth_layer_sh;
- struct GPUShader *minz_copydepth_sh;
- struct GPUShader *maxz_copydepth_sh;
-
- /* Simple Down-sample. */
- struct GPUShader *color_copy_sh;
- struct GPUShader *downsample_sh;
- struct GPUShader *downsample_cube_sh;
-
- /* Mist */
- struct GPUShader *mist_sh;
-
- /* Motion Blur */
- struct GPUShader *motion_blur_sh;
- struct GPUShader *motion_blur_object_sh;
- struct GPUShader *motion_blur_hair_sh;
- struct GPUShader *velocity_tiles_sh;
- struct GPUShader *velocity_tiles_expand_sh;
-
- /* Ground Truth Ambient Occlusion */
- struct GPUShader *gtao_sh;
- struct GPUShader *gtao_layer_sh;
- struct GPUShader *gtao_debug_sh;
-
- /* GGX LUT */
- struct GPUShader *ggx_lut_sh;
- struct GPUShader *ggx_refraction_lut_sh;
-
- /* Render Passes */
- struct GPUShader *postprocess_sh;
- struct GPUShader *cryptomatte_sh[2];
-
- /* Screen Space Reflection */
- struct GPUShader *reflection_trace;
- struct GPUShader *reflection_resolve;
-
- /* Shadows */
- struct GPUShader *shadow_sh;
- struct GPUShader *shadow_accum_sh;
-
- /* Subsurface */
- struct GPUShader *sss_sh[3];
-
- /* Volume */
- struct GPUShader *volumetric_clear_sh;
- struct GPUShader *scatter_sh;
- struct GPUShader *scatter_with_lights_sh;
- struct GPUShader *volumetric_integration_sh;
- struct GPUShader *volumetric_resolve_sh[2];
- struct GPUShader *volumetric_accum_sh;
-
- /* Shader strings */
- char *surface_lit_frag;
- char *surface_prepass_frag;
- char *surface_geom_barycentric;
-
- DRWShaderLibrary *lib;
-
- /* LookDev Materials */
- Material *glossy_mat;
- Material *diffuse_mat;
-
- Material *error_mat;
-
- World *default_world;
-
- /* Default Material */
- struct {
- bNodeTree *ntree;
- bNodeSocketValueRGBA *color_socket;
- bNodeSocketValueFloat *metallic_socket;
- bNodeSocketValueFloat *roughness_socket;
- bNodeSocketValueFloat *specular_socket;
- } surface;
-
- struct {
- bNodeTree *ntree;
- bNodeSocketValueRGBA *color_socket;
- } world;
-} e_data = {NULL}; /* Engine data */
-
-extern char datatoc_common_hair_lib_glsl[];
-extern char datatoc_common_math_lib_glsl[];
-extern char datatoc_common_math_geom_lib_glsl[];
-extern char datatoc_common_view_lib_glsl[];
-extern char datatoc_gpu_shader_common_obinfos_lib_glsl[];
-
-extern char datatoc_ambient_occlusion_lib_glsl[];
-extern char datatoc_background_vert_glsl[];
-extern char datatoc_bsdf_common_lib_glsl[];
-extern char datatoc_bsdf_lut_frag_glsl[];
-extern char datatoc_bsdf_sampling_lib_glsl[];
-extern char datatoc_btdf_lut_frag_glsl[];
-extern char datatoc_closure_type_lib_glsl[];
-extern char datatoc_common_uniforms_lib_glsl[];
-extern char datatoc_common_utiltex_lib_glsl[];
-extern char datatoc_cryptomatte_frag_glsl[];
-extern char datatoc_cubemap_lib_glsl[];
-extern char datatoc_default_frag_glsl[];
-extern char datatoc_lookdev_world_frag_glsl[];
-extern char datatoc_effect_bloom_frag_glsl[];
-extern char datatoc_effect_dof_bokeh_frag_glsl[];
-extern char datatoc_effect_dof_dilate_tiles_frag_glsl[];
-extern char datatoc_effect_dof_downsample_frag_glsl[];
-extern char datatoc_effect_dof_filter_frag_glsl[];
-extern char datatoc_effect_dof_flatten_tiles_frag_glsl[];
-extern char datatoc_effect_dof_gather_frag_glsl[];
-extern char datatoc_effect_dof_lib_glsl[];
-extern char datatoc_effect_dof_reduce_frag_glsl[];
-extern char datatoc_effect_dof_resolve_frag_glsl[];
-extern char datatoc_effect_dof_scatter_frag_glsl[];
-extern char datatoc_effect_dof_scatter_vert_glsl[];
-extern char datatoc_effect_dof_setup_frag_glsl[];
-extern char datatoc_effect_downsample_cube_frag_glsl[];
-extern char datatoc_effect_downsample_frag_glsl[];
-extern char datatoc_effect_gtao_frag_glsl[];
-extern char datatoc_effect_minmaxz_frag_glsl[];
-extern char datatoc_effect_mist_frag_glsl[];
-extern char datatoc_effect_motion_blur_frag_glsl[];
-extern char datatoc_effect_reflection_lib_glsl[];
-extern char datatoc_effect_reflection_resolve_frag_glsl[];
-extern char datatoc_effect_reflection_trace_frag_glsl[];
-extern char datatoc_effect_subsurface_frag_glsl[];
-extern char datatoc_effect_temporal_aa_glsl[];
-extern char datatoc_effect_translucency_frag_glsl[];
-extern char datatoc_effect_velocity_resolve_frag_glsl[];
-extern char datatoc_effect_velocity_tile_frag_glsl[];
-extern char datatoc_irradiance_lib_glsl[];
-extern char datatoc_lightprobe_cube_display_frag_glsl[];
-extern char datatoc_lightprobe_cube_display_vert_glsl[];
-extern char datatoc_lightprobe_filter_diffuse_frag_glsl[];
-extern char datatoc_lightprobe_filter_glossy_frag_glsl[];
-extern char datatoc_lightprobe_filter_visibility_frag_glsl[];
-extern char datatoc_lightprobe_geom_glsl[];
-extern char datatoc_lightprobe_grid_display_frag_glsl[];
-extern char datatoc_lightprobe_grid_display_vert_glsl[];
-extern char datatoc_lightprobe_grid_fill_frag_glsl[];
-extern char datatoc_lightprobe_lib_glsl[];
-extern char datatoc_lightprobe_planar_display_frag_glsl[];
-extern char datatoc_lightprobe_planar_display_vert_glsl[];
-extern char datatoc_lightprobe_planar_downsample_frag_glsl[];
-extern char datatoc_lightprobe_planar_downsample_geom_glsl[];
-extern char datatoc_lightprobe_planar_downsample_vert_glsl[];
-extern char datatoc_lightprobe_vert_glsl[];
-extern char datatoc_lights_lib_glsl[];
-extern char datatoc_closure_eval_lib_glsl[];
-extern char datatoc_closure_eval_diffuse_lib_glsl[];
-extern char datatoc_closure_eval_glossy_lib_glsl[];
-extern char datatoc_closure_eval_refraction_lib_glsl[];
-extern char datatoc_closure_eval_translucent_lib_glsl[];
-extern char datatoc_ltc_lib_glsl[];
-extern char datatoc_object_motion_frag_glsl[];
-extern char datatoc_object_motion_vert_glsl[];
-extern char datatoc_octahedron_lib_glsl[];
-extern char datatoc_prepass_frag_glsl[];
-extern char datatoc_prepass_vert_glsl[];
-extern char datatoc_random_lib_glsl[];
-extern char datatoc_raytrace_lib_glsl[];
-extern char datatoc_renderpass_lib_glsl[];
-extern char datatoc_renderpass_postprocess_frag_glsl[];
-extern char datatoc_shadow_accum_frag_glsl[];
-extern char datatoc_shadow_frag_glsl[];
-extern char datatoc_shadow_vert_glsl[];
-extern char datatoc_ssr_lib_glsl[];
-extern char datatoc_surface_frag_glsl[];
-extern char datatoc_surface_geom_glsl[];
-extern char datatoc_surface_lib_glsl[];
-extern char datatoc_surface_vert_glsl[];
-extern char datatoc_update_noise_frag_glsl[];
-extern char datatoc_volumetric_accum_frag_glsl[];
-extern char datatoc_volumetric_frag_glsl[];
-extern char datatoc_volumetric_geom_glsl[];
-extern char datatoc_volumetric_integration_frag_glsl[];
-extern char datatoc_volumetric_lib_glsl[];
-extern char datatoc_volumetric_resolve_frag_glsl[];
-extern char datatoc_volumetric_scatter_frag_glsl[];
-extern char datatoc_volumetric_vert_glsl[];
-
-/* *********** FUNCTIONS *********** */
-
-static void eevee_shader_library_ensure(void)
-{
- if (e_data.lib == NULL) {
- e_data.lib = DRW_shader_library_create();
- /* NOTE: These need to be ordered by dependencies. */
- DRW_SHADER_LIB_ADD(e_data.lib, common_math_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, common_math_geom_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, common_hair_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, common_uniforms_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, gpu_shader_common_obinfos_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, random_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, renderpass_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, bsdf_common_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, common_utiltex_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, bsdf_sampling_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, cubemap_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, raytrace_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, ambient_occlusion_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, octahedron_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, irradiance_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, lightprobe_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, ltc_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, lights_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, surface_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, volumetric_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, ssr_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, effect_dof_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, effect_reflection_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, closure_type_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_diffuse_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_glossy_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_translucent_lib);
- DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_refraction_lib);
-
- e_data.surface_lit_frag = DRW_shader_library_create_shader_string(e_data.lib,
- datatoc_surface_frag_glsl);
-
- e_data.surface_prepass_frag = DRW_shader_library_create_shader_string(
- e_data.lib, datatoc_prepass_frag_glsl);
-
- e_data.surface_geom_barycentric = DRW_shader_library_create_shader_string(
- e_data.lib, datatoc_surface_geom_glsl);
- }
-}
-
-void EEVEE_shaders_material_shaders_init(void)
-{
- eevee_shader_library_ensure();
-}
-
-DRWShaderLibrary *EEVEE_shader_lib_get(void)
-{
- eevee_shader_library_ensure();
- return e_data.lib;
-}
-
-GPUShader *EEVEE_shaders_probe_filter_glossy_sh_get(void)
-{
- if (e_data.probe_filter_glossy_sh == NULL) {
- e_data.probe_filter_glossy_sh = DRW_shader_create_with_shaderlib(
- datatoc_lightprobe_vert_glsl,
- datatoc_lightprobe_geom_glsl,
- datatoc_lightprobe_filter_glossy_frag_glsl,
- e_data.lib,
- filter_defines);
- }
- return e_data.probe_filter_glossy_sh;
-}
-
-GPUShader *EEVEE_shaders_probe_filter_diffuse_sh_get(void)
-{
- if (e_data.probe_filter_diffuse_sh == NULL) {
- e_data.probe_filter_diffuse_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_lightprobe_filter_diffuse_frag_glsl, e_data.lib, filter_defines);
- }
- return e_data.probe_filter_diffuse_sh;
-}
-
-GPUShader *EEVEE_shaders_probe_filter_visibility_sh_get(void)
-{
- if (e_data.probe_filter_visibility_sh == NULL) {
- e_data.probe_filter_visibility_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_lightprobe_filter_visibility_frag_glsl, e_data.lib, filter_defines);
- }
- return e_data.probe_filter_visibility_sh;
-}
-
-GPUShader *EEVEE_shaders_probe_grid_fill_sh_get(void)
-{
- if (e_data.probe_grid_fill_sh == NULL) {
- e_data.probe_grid_fill_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_lightprobe_grid_fill_frag_glsl, e_data.lib, filter_defines);
- }
- return e_data.probe_grid_fill_sh;
-}
-
-GPUShader *EEVEE_shaders_probe_planar_downsample_sh_get(void)
-{
- if (e_data.probe_planar_downsample_sh == NULL) {
- e_data.probe_planar_downsample_sh = DRW_shader_create_with_shaderlib(
- datatoc_lightprobe_planar_downsample_vert_glsl,
- datatoc_lightprobe_planar_downsample_geom_glsl,
- datatoc_lightprobe_planar_downsample_frag_glsl,
- e_data.lib,
- NULL);
- }
- return e_data.probe_planar_downsample_sh;
-}
-
-GPUShader *EEVEE_shaders_studiolight_probe_sh_get(void)
-{
- if (e_data.studiolight_probe_sh == NULL) {
- e_data.studiolight_probe_sh = DRW_shader_create_with_shaderlib(datatoc_background_vert_glsl,
- NULL,
- datatoc_lookdev_world_frag_glsl,
- e_data.lib,
- SHADER_DEFINES);
- }
- return e_data.studiolight_probe_sh;
-}
-
-GPUShader *EEVEE_shaders_studiolight_background_sh_get(void)
-{
- if (e_data.studiolight_background_sh == NULL) {
- e_data.studiolight_background_sh = DRW_shader_create_with_shaderlib(
- datatoc_background_vert_glsl,
- NULL,
- datatoc_lookdev_world_frag_glsl,
- e_data.lib,
- "#define LOOKDEV_BG\n" SHADER_DEFINES);
- }
- return e_data.studiolight_background_sh;
-}
-
-GPUShader *EEVEE_shaders_probe_cube_display_sh_get(void)
-{
- if (e_data.probe_cube_display_sh == NULL) {
- e_data.probe_cube_display_sh = DRW_shader_create_with_shaderlib(
- datatoc_lightprobe_cube_display_vert_glsl,
- NULL,
- datatoc_lightprobe_cube_display_frag_glsl,
- e_data.lib,
- SHADER_DEFINES);
- }
- return e_data.probe_cube_display_sh;
-}
-
-GPUShader *EEVEE_shaders_probe_grid_display_sh_get(void)
-{
- if (e_data.probe_grid_display_sh == NULL) {
- e_data.probe_grid_display_sh = DRW_shader_create_with_shaderlib(
- datatoc_lightprobe_grid_display_vert_glsl,
- NULL,
- datatoc_lightprobe_grid_display_frag_glsl,
- e_data.lib,
- filter_defines);
- }
- return e_data.probe_grid_display_sh;
-}
-
-GPUShader *EEVEE_shaders_probe_planar_display_sh_get(void)
-{
- if (e_data.probe_planar_display_sh == NULL) {
- e_data.probe_planar_display_sh = DRW_shader_create_with_shaderlib(
- datatoc_lightprobe_planar_display_vert_glsl,
- NULL,
- datatoc_lightprobe_planar_display_frag_glsl,
- e_data.lib,
- NULL);
- }
- return e_data.probe_planar_display_sh;
-}
-
-/* -------------------------------------------------------------------- */
-/** \name Down-sampling
- * \{ */
-
-GPUShader *EEVEE_shaders_effect_color_copy_sh_get(void)
-{
- if (e_data.color_copy_sh == NULL) {
- e_data.color_copy_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_downsample_frag_glsl, e_data.lib, "#define COPY_SRC\n");
- }
- return e_data.color_copy_sh;
-}
-
-GPUShader *EEVEE_shaders_effect_downsample_sh_get(void)
-{
- if (e_data.downsample_sh == NULL) {
- e_data.downsample_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_downsample_frag_glsl, e_data.lib, NULL);
- }
- return e_data.downsample_sh;
-}
-
-GPUShader *EEVEE_shaders_effect_downsample_cube_sh_get(void)
-{
- if (e_data.downsample_cube_sh == NULL) {
- e_data.downsample_cube_sh = DRW_shader_create_with_shaderlib(
- datatoc_lightprobe_vert_glsl,
- datatoc_lightprobe_geom_glsl,
- datatoc_effect_downsample_cube_frag_glsl,
- e_data.lib,
- NULL);
- }
- return e_data.downsample_cube_sh;
-}
-
-GPUShader *EEVEE_shaders_effect_minz_downlevel_sh_get(void)
-{
- if (e_data.minz_downlevel_sh == NULL) {
- e_data.minz_downlevel_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl,
- "#define MIN_PASS\n");
- }
- return e_data.minz_downlevel_sh;
-}
-
-GPUShader *EEVEE_shaders_effect_maxz_downlevel_sh_get(void)
-{
- if (e_data.maxz_downlevel_sh == NULL) {
- e_data.maxz_downlevel_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl,
- "#define MAX_PASS\n");
- }
- return e_data.maxz_downlevel_sh;
-}
-
-GPUShader *EEVEE_shaders_effect_minz_downdepth_sh_get(void)
-{
- if (e_data.minz_downdepth_sh == NULL) {
- e_data.minz_downdepth_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl,
- "#define MIN_PASS\n");
- }
- return e_data.minz_downdepth_sh;
-}
-
-GPUShader *EEVEE_shaders_effect_maxz_downdepth_sh_get(void)
-{
- if (e_data.maxz_downdepth_sh == NULL) {
- e_data.maxz_downdepth_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl,
- "#define MAX_PASS\n");
- }
- return e_data.maxz_downdepth_sh;
-}
-
-GPUShader *EEVEE_shaders_effect_minz_downdepth_layer_sh_get(void)
-{
- if (e_data.minz_downdepth_layer_sh == NULL) {
- e_data.minz_downdepth_layer_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl,
- "#define MIN_PASS\n"
- "#define LAYERED\n");
- }
- return e_data.minz_downdepth_layer_sh;
-}
-
-GPUShader *EEVEE_shaders_effect_maxz_downdepth_layer_sh_get(void)
-{
- if (e_data.maxz_downdepth_layer_sh == NULL) {
- e_data.maxz_downdepth_layer_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl,
- "#define MAX_PASS\n"
- "#define LAYERED\n");
- }
- return e_data.maxz_downdepth_layer_sh;
-}
-
-GPUShader *EEVEE_shaders_effect_maxz_copydepth_layer_sh_get(void)
-{
- if (e_data.maxz_copydepth_layer_sh == NULL) {
- e_data.maxz_copydepth_layer_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl,
- "#define MAX_PASS\n"
- "#define COPY_DEPTH\n"
- "#define LAYERED\n");
- }
- return e_data.maxz_copydepth_layer_sh;
-}
-
-GPUShader *EEVEE_shaders_effect_minz_copydepth_sh_get(void)
-{
- if (e_data.minz_copydepth_sh == NULL) {
- e_data.minz_copydepth_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl,
- "#define MIN_PASS\n"
- "#define COPY_DEPTH\n");
- }
- return e_data.minz_copydepth_sh;
-}
-
-GPUShader *EEVEE_shaders_effect_maxz_copydepth_sh_get(void)
-{
- if (e_data.maxz_copydepth_sh == NULL) {
- e_data.maxz_copydepth_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl,
- "#define MAX_PASS\n"
- "#define COPY_DEPTH\n");
- }
- return e_data.maxz_copydepth_sh;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name GGX LUT
- * \{ */
-
-GPUShader *EEVEE_shaders_ggx_lut_sh_get(void)
-{
- if (e_data.ggx_lut_sh == NULL) {
- e_data.ggx_lut_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_bsdf_lut_frag_glsl, e_data.lib, NULL);
- }
- return e_data.ggx_lut_sh;
-}
-
-GPUShader *EEVEE_shaders_ggx_refraction_lut_sh_get(void)
-{
- if (e_data.ggx_refraction_lut_sh == NULL) {
- e_data.ggx_refraction_lut_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_btdf_lut_frag_glsl, e_data.lib, NULL);
- }
- return e_data.ggx_refraction_lut_sh;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Mist
- * \{ */
-
-GPUShader *EEVEE_shaders_effect_mist_sh_get(void)
-{
- if (e_data.mist_sh == NULL) {
- e_data.mist_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_mist_frag_glsl, e_data.lib, "#define FIRST_PASS\n");
- }
- return e_data.mist_sh;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Motion Blur
- * \{ */
-
-#define TILE_SIZE_STR "#define EEVEE_VELOCITY_TILE_SIZE " STRINGIFY(EEVEE_VELOCITY_TILE_SIZE) "\n"
-GPUShader *EEVEE_shaders_effect_motion_blur_sh_get(void)
-{
- if (e_data.motion_blur_sh == NULL) {
- e_data.motion_blur_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_motion_blur_frag_glsl, e_data.lib, TILE_SIZE_STR);
- }
- return e_data.motion_blur_sh;
-}
-
-GPUShader *EEVEE_shaders_effect_motion_blur_object_sh_get(void)
-{
- if (e_data.motion_blur_object_sh == NULL) {
- e_data.motion_blur_object_sh = DRW_shader_create_with_shaderlib(
- datatoc_object_motion_vert_glsl, NULL, datatoc_object_motion_frag_glsl, e_data.lib, NULL);
- }
- return e_data.motion_blur_object_sh;
-}
-
-GPUShader *EEVEE_shaders_effect_motion_blur_hair_sh_get(void)
-{
- if (e_data.motion_blur_hair_sh == NULL) {
- e_data.motion_blur_hair_sh = DRW_shader_create_with_shaderlib(datatoc_object_motion_vert_glsl,
- NULL,
- datatoc_object_motion_frag_glsl,
- e_data.lib,
- "#define HAIR\n");
- }
- return e_data.motion_blur_hair_sh;
-}
-
-GPUShader *EEVEE_shaders_effect_motion_blur_velocity_tiles_sh_get(void)
-{
- if (e_data.velocity_tiles_sh == NULL) {
- e_data.velocity_tiles_sh = DRW_shader_create_fullscreen(datatoc_effect_velocity_tile_frag_glsl,
- "#define TILE_GATHER\n" TILE_SIZE_STR);
- }
- return e_data.velocity_tiles_sh;
-}
-
-GPUShader *EEVEE_shaders_effect_motion_blur_velocity_tiles_expand_sh_get(void)
-{
- if (e_data.velocity_tiles_expand_sh == NULL) {
- e_data.velocity_tiles_expand_sh = DRW_shader_create_fullscreen(
- datatoc_effect_velocity_tile_frag_glsl, "#define TILE_EXPANSION\n" TILE_SIZE_STR);
- }
- return e_data.velocity_tiles_expand_sh;
-}
-
-#undef TILE_SIZE_STR
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Ambient Occlusion
- * \{ */
-
-GPUShader *EEVEE_shaders_effect_ambient_occlusion_sh_get(void)
-{
- if (e_data.gtao_sh == NULL) {
- e_data.gtao_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_gtao_frag_glsl, e_data.lib, NULL);
- }
- return e_data.gtao_sh;
-}
-
-GPUShader *EEVEE_shaders_effect_ambient_occlusion_debug_sh_get(void)
-{
- if (e_data.gtao_debug_sh == NULL) {
- e_data.gtao_debug_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_gtao_frag_glsl,
- e_data.lib,
- "#define DEBUG_AO\n"
- "#define ENABLE_DEFERED_AO");
- }
- return e_data.gtao_debug_sh;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Render Passes
- * \{ */
-
-GPUShader *EEVEE_shaders_renderpasses_post_process_sh_get(void)
-{
- if (e_data.postprocess_sh == NULL) {
- e_data.postprocess_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_renderpass_postprocess_frag_glsl, e_data.lib, NULL);
- }
- return e_data.postprocess_sh;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Cryptomatte
- * \{ */
-
-GPUShader *EEVEE_shaders_cryptomatte_sh_get(bool is_hair)
-{
- const int index = is_hair ? 1 : 0;
- if (e_data.cryptomatte_sh[index] == NULL) {
- DynStr *ds = BLI_dynstr_new();
- BLI_dynstr_append(ds, SHADER_DEFINES);
-
- if (is_hair) {
- BLI_dynstr_append(ds, "#define HAIR_SHADER\n");
- }
- else {
- BLI_dynstr_append(ds, "#define MESH_SHADER\n");
- }
- char *defines = BLI_dynstr_get_cstring(ds);
- e_data.cryptomatte_sh[index] = DRW_shader_create_with_shaderlib(
- datatoc_surface_vert_glsl, NULL, datatoc_cryptomatte_frag_glsl, e_data.lib, defines);
- BLI_dynstr_free(ds);
- MEM_freeN(defines);
- }
- return e_data.cryptomatte_sh[index];
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Raytraced Reflections
- * \{ */
-
-struct GPUShader *EEVEE_shaders_effect_reflection_trace_sh_get(void)
-{
- if (e_data.reflection_trace == NULL) {
- e_data.reflection_trace = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_reflection_trace_frag_glsl,
- e_data.lib,
- SHADER_DEFINES "#define STEP_RAYTRACE\n");
- }
- return e_data.reflection_trace;
-}
-
-struct GPUShader *EEVEE_shaders_effect_reflection_resolve_sh_get(void)
-{
- if (e_data.reflection_resolve == NULL) {
- e_data.reflection_resolve = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_reflection_resolve_frag_glsl,
- e_data.lib,
- SHADER_DEFINES "#define STEP_RESOLVE\n");
- }
- return e_data.reflection_resolve;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Shadows
- * \{ */
-
-struct GPUShader *EEVEE_shaders_shadow_sh_get()
-{
- if (e_data.shadow_sh == NULL) {
- e_data.shadow_sh = DRW_shader_create_with_shaderlib(
- datatoc_shadow_vert_glsl, NULL, datatoc_shadow_frag_glsl, e_data.lib, NULL);
- }
- return e_data.shadow_sh;
-}
-
-struct GPUShader *EEVEE_shaders_shadow_accum_sh_get()
-{
- if (e_data.shadow_accum_sh == NULL) {
- e_data.shadow_accum_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_shadow_accum_frag_glsl, e_data.lib, SHADER_DEFINES);
- }
- return e_data.shadow_accum_sh;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Subsurface
- * \{ */
-
-struct GPUShader *EEVEE_shaders_subsurface_first_pass_sh_get()
-{
- if (e_data.sss_sh[0] == NULL) {
- e_data.sss_sh[0] = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_subsurface_frag_glsl, e_data.lib, "#define FIRST_PASS\n");
- }
- return e_data.sss_sh[0];
-}
-
-struct GPUShader *EEVEE_shaders_subsurface_second_pass_sh_get()
-{
- if (e_data.sss_sh[1] == NULL) {
- e_data.sss_sh[1] = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_subsurface_frag_glsl, e_data.lib, "#define SECOND_PASS\n");
- }
- return e_data.sss_sh[1];
-}
-
-struct GPUShader *EEVEE_shaders_subsurface_translucency_sh_get()
-{
- if (e_data.sss_sh[2] == NULL) {
- e_data.sss_sh[2] = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_translucency_frag_glsl,
- e_data.lib,
- "#define EEVEE_TRANSLUCENCY\n" SHADER_DEFINES);
- }
- return e_data.sss_sh[2];
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Volumes
- * \{ */
-
-struct GPUShader *EEVEE_shaders_volumes_clear_sh_get()
-{
- if (e_data.volumetric_clear_sh == NULL) {
- e_data.volumetric_clear_sh = DRW_shader_create_with_shaderlib(datatoc_volumetric_vert_glsl,
- datatoc_volumetric_geom_glsl,
- datatoc_volumetric_frag_glsl,
- e_data.lib,
- SHADER_DEFINES
- "#define VOLUMETRICS\n"
- "#define CLEAR\n");
- }
- return e_data.volumetric_clear_sh;
-}
-
-struct GPUShader *EEVEE_shaders_volumes_scatter_sh_get()
-{
- if (e_data.scatter_sh == NULL) {
- e_data.scatter_sh = DRW_shader_create_with_shaderlib(datatoc_volumetric_vert_glsl,
- datatoc_volumetric_geom_glsl,
- datatoc_volumetric_scatter_frag_glsl,
- e_data.lib,
- SHADER_DEFINES
- "#define VOLUMETRICS\n"
- "#define VOLUME_SHADOW\n");
- }
- return e_data.scatter_sh;
-}
-
-struct GPUShader *EEVEE_shaders_volumes_scatter_with_lights_sh_get()
-{
- if (e_data.scatter_with_lights_sh == NULL) {
- e_data.scatter_with_lights_sh = DRW_shader_create_with_shaderlib(
- datatoc_volumetric_vert_glsl,
- datatoc_volumetric_geom_glsl,
- datatoc_volumetric_scatter_frag_glsl,
- e_data.lib,
- SHADER_DEFINES
- "#define VOLUMETRICS\n"
- "#define VOLUME_LIGHTING\n"
- "#define VOLUME_SHADOW\n");
- }
- return e_data.scatter_with_lights_sh;
-}
-
-struct GPUShader *EEVEE_shaders_volumes_integration_sh_get()
-{
- if (e_data.volumetric_integration_sh == NULL) {
- e_data.volumetric_integration_sh = DRW_shader_create_with_shaderlib(
- datatoc_volumetric_vert_glsl,
- datatoc_volumetric_geom_glsl,
- datatoc_volumetric_integration_frag_glsl,
- e_data.lib,
- USE_VOLUME_OPTI ? "#define USE_VOLUME_OPTI\n" SHADER_DEFINES : SHADER_DEFINES);
- }
- return e_data.volumetric_integration_sh;
-}
-
-struct GPUShader *EEVEE_shaders_volumes_resolve_sh_get(bool accum)
-{
- const int index = accum ? 1 : 0;
- if (e_data.volumetric_resolve_sh[index] == NULL) {
- e_data.volumetric_resolve_sh[index] = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_volumetric_resolve_frag_glsl,
- e_data.lib,
- accum ? "#define VOLUMETRICS_ACCUM\n" SHADER_DEFINES : SHADER_DEFINES);
- }
- return e_data.volumetric_resolve_sh[index];
-}
-
-struct GPUShader *EEVEE_shaders_volumes_accum_sh_get()
-{
- if (e_data.volumetric_accum_sh == NULL) {
- e_data.volumetric_accum_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_volumetric_accum_frag_glsl, e_data.lib, SHADER_DEFINES);
- }
- return e_data.volumetric_accum_sh;
-}
-
-/** \} */
-
-GPUShader *EEVEE_shaders_velocity_resolve_sh_get(void)
-{
- if (e_data.velocity_resolve_sh == NULL) {
- e_data.velocity_resolve_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_velocity_resolve_frag_glsl, e_data.lib, NULL);
- }
- return e_data.velocity_resolve_sh;
-}
-
-GPUShader *EEVEE_shaders_update_noise_sh_get(void)
-{
- if (e_data.update_noise_sh == NULL) {
- e_data.update_noise_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_update_noise_frag_glsl, e_data.lib, NULL);
- }
- return e_data.update_noise_sh;
-}
-
-GPUShader *EEVEE_shaders_taa_resolve_sh_get(EEVEE_EffectsFlag enabled_effects)
-{
- GPUShader **sh;
- const char *define = NULL;
- if (enabled_effects & EFFECT_TAA_REPROJECT) {
- sh = &e_data.taa_resolve_reproject_sh;
- define = "#define USE_REPROJECTION\n";
- }
- else {
- sh = &e_data.taa_resolve_sh;
- }
- if (*sh == NULL) {
- *sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_temporal_aa_glsl, e_data.lib, define);
- }
-
- return *sh;
-}
-
-/* -------------------------------------------------------------------- */
-/** \name Bloom
- * \{ */
-
-GPUShader *EEVEE_shaders_bloom_blit_get(bool high_quality)
-{
- int index = high_quality ? 1 : 0;
-
- if (e_data.bloom_blit_sh[index] == NULL) {
- const char *define = high_quality ? "#define STEP_BLIT\n"
- "#define HIGH_QUALITY\n" :
- "#define STEP_BLIT\n";
- e_data.bloom_blit_sh[index] = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_bloom_frag_glsl, e_data.lib, define);
- }
- return e_data.bloom_blit_sh[index];
-}
-
-GPUShader *EEVEE_shaders_bloom_downsample_get(bool high_quality)
-{
- int index = high_quality ? 1 : 0;
-
- if (e_data.bloom_downsample_sh[index] == NULL) {
- const char *define = high_quality ? "#define STEP_DOWNSAMPLE\n"
- "#define HIGH_QUALITY\n" :
- "#define STEP_DOWNSAMPLE\n";
- e_data.bloom_downsample_sh[index] = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_bloom_frag_glsl, e_data.lib, define);
- }
- return e_data.bloom_downsample_sh[index];
-}
-
-GPUShader *EEVEE_shaders_bloom_upsample_get(bool high_quality)
-{
- int index = high_quality ? 1 : 0;
-
- if (e_data.bloom_upsample_sh[index] == NULL) {
- const char *define = high_quality ? "#define STEP_UPSAMPLE\n"
- "#define HIGH_QUALITY\n" :
- "#define STEP_UPSAMPLE\n";
- e_data.bloom_upsample_sh[index] = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_bloom_frag_glsl, e_data.lib, define);
- }
- return e_data.bloom_upsample_sh[index];
-}
-
-GPUShader *EEVEE_shaders_bloom_resolve_get(bool high_quality)
-{
- int index = high_quality ? 1 : 0;
-
- if (e_data.bloom_resolve_sh[index] == NULL) {
- const char *define = high_quality ? "#define STEP_RESOLVE\n"
- "#define HIGH_QUALITY\n" :
- "#define STEP_RESOLVE\n";
- e_data.bloom_resolve_sh[index] = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_bloom_frag_glsl, e_data.lib, define);
- }
- return e_data.bloom_resolve_sh[index];
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Depth of field
- * \{ */
-
-GPUShader *EEVEE_shaders_depth_of_field_bokeh_get(void)
-{
- if (e_data.dof_bokeh_sh == NULL) {
- e_data.dof_bokeh_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_dof_bokeh_frag_glsl, e_data.lib, DOF_SHADER_DEFINES);
- }
- return e_data.dof_bokeh_sh;
-}
-
-GPUShader *EEVEE_shaders_depth_of_field_setup_get(void)
-{
- if (e_data.dof_setup_sh == NULL) {
- e_data.dof_setup_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_dof_setup_frag_glsl, e_data.lib, DOF_SHADER_DEFINES);
- }
- return e_data.dof_setup_sh;
-}
-
-GPUShader *EEVEE_shaders_depth_of_field_flatten_tiles_get(void)
-{
- if (e_data.dof_flatten_tiles_sh == NULL) {
- e_data.dof_flatten_tiles_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_dof_flatten_tiles_frag_glsl, e_data.lib, DOF_SHADER_DEFINES);
- }
- return e_data.dof_flatten_tiles_sh;
-}
-
-GPUShader *EEVEE_shaders_depth_of_field_dilate_tiles_get(bool b_pass)
-{
- int pass = b_pass;
- if (e_data.dof_dilate_tiles_sh[pass] == NULL) {
- e_data.dof_dilate_tiles_sh[pass] = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_dof_dilate_tiles_frag_glsl,
- e_data.lib,
- (pass == 0) ? DOF_SHADER_DEFINES "#define DILATE_MODE_MIN_MAX\n" :
- DOF_SHADER_DEFINES "#define DILATE_MODE_MIN_ABS\n");
- }
- return e_data.dof_dilate_tiles_sh[pass];
-}
-
-GPUShader *EEVEE_shaders_depth_of_field_downsample_get(void)
-{
- if (e_data.dof_downsample_sh == NULL) {
- e_data.dof_downsample_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_dof_downsample_frag_glsl, e_data.lib, DOF_SHADER_DEFINES);
- }
- return e_data.dof_downsample_sh;
-}
-
-GPUShader *EEVEE_shaders_depth_of_field_reduce_get(bool b_is_copy_pass)
-{
- int is_copy_pass = b_is_copy_pass;
- if (e_data.dof_reduce_sh[is_copy_pass] == NULL) {
- e_data.dof_reduce_sh[is_copy_pass] = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_dof_reduce_frag_glsl,
- e_data.lib,
- (is_copy_pass) ? DOF_SHADER_DEFINES "#define COPY_PASS\n" :
- DOF_SHADER_DEFINES "#define REDUCE_PASS\n");
- }
- return e_data.dof_reduce_sh[is_copy_pass];
-}
-
-GPUShader *EEVEE_shaders_depth_of_field_gather_get(EEVEE_DofGatherPass pass, bool b_use_bokeh_tx)
-{
- int use_bokeh_tx = b_use_bokeh_tx;
- if (e_data.dof_gather_sh[pass][use_bokeh_tx] == NULL) {
- DynStr *ds = BLI_dynstr_new();
-
- BLI_dynstr_append(ds, DOF_SHADER_DEFINES);
-
- switch (pass) {
- case DOF_GATHER_FOREGROUND:
- BLI_dynstr_append(ds, "#define DOF_FOREGROUND_PASS\n");
- break;
- case DOF_GATHER_BACKGROUND:
- BLI_dynstr_append(ds, "#define DOF_BACKGROUND_PASS\n");
- break;
- case DOF_GATHER_HOLEFILL:
- BLI_dynstr_append(ds,
- "#define DOF_BACKGROUND_PASS\n"
- "#define DOF_HOLEFILL_PASS\n");
- break;
- default:
- break;
- }
-
- if (use_bokeh_tx) {
- BLI_dynstr_append(ds, "#define DOF_BOKEH_TEXTURE\n");
- }
-
- char *define = BLI_dynstr_get_cstring(ds);
- BLI_dynstr_free(ds);
-
- e_data.dof_gather_sh[pass][use_bokeh_tx] = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_dof_gather_frag_glsl, e_data.lib, define);
-
- MEM_freeN(define);
- }
- return e_data.dof_gather_sh[pass][use_bokeh_tx];
-}
-
-GPUShader *EEVEE_shaders_depth_of_field_filter_get(void)
-{
- if (e_data.dof_filter_sh == NULL) {
- e_data.dof_filter_sh = DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_dof_filter_frag_glsl, e_data.lib, DOF_SHADER_DEFINES);
- }
- return e_data.dof_filter_sh;
-}
-
-GPUShader *EEVEE_shaders_depth_of_field_scatter_get(bool b_is_foreground, bool b_use_bokeh_tx)
-{
- int is_foreground = b_is_foreground;
- int use_bokeh_tx = b_use_bokeh_tx;
- if (e_data.dof_scatter_sh[is_foreground][use_bokeh_tx] == NULL) {
- DynStr *ds = BLI_dynstr_new();
-
- BLI_dynstr_append(ds, DOF_SHADER_DEFINES);
- BLI_dynstr_append(
- ds, (is_foreground) ? "#define DOF_FOREGROUND_PASS\n" : "#define DOF_BACKGROUND_PASS\n");
-
- if (use_bokeh_tx) {
- BLI_dynstr_append(ds, "#define DOF_BOKEH_TEXTURE\n");
- }
-
- char *define = BLI_dynstr_get_cstring(ds);
- BLI_dynstr_free(ds);
-
- e_data.dof_scatter_sh[is_foreground][use_bokeh_tx] = DRW_shader_create_with_shaderlib(
- datatoc_effect_dof_scatter_vert_glsl,
- NULL,
- datatoc_effect_dof_scatter_frag_glsl,
- e_data.lib,
- define);
-
- MEM_freeN(define);
- }
- return e_data.dof_scatter_sh[is_foreground][use_bokeh_tx];
-}
-
-GPUShader *EEVEE_shaders_depth_of_field_resolve_get(bool b_use_bokeh_tx, bool b_use_hq_gather)
-{
- int use_hq_gather = b_use_hq_gather;
- int use_bokeh_tx = b_use_bokeh_tx;
- if (e_data.dof_resolve_sh[use_bokeh_tx][use_hq_gather] == NULL) {
- DynStr *ds = BLI_dynstr_new();
-
- BLI_dynstr_append(ds, DOF_SHADER_DEFINES);
- BLI_dynstr_append(ds, "#define DOF_RESOLVE_PASS\n");
-
- if (use_bokeh_tx) {
- BLI_dynstr_append(ds, "#define DOF_BOKEH_TEXTURE\n");
- }
-
- BLI_dynstr_appendf(ds, "#define DOF_SLIGHT_FOCUS_DENSITY %d\n", use_hq_gather ? 4 : 2);
-
- char *define = BLI_dynstr_get_cstring(ds);
- BLI_dynstr_free(ds);
-
- e_data.dof_resolve_sh[use_bokeh_tx][use_hq_gather] =
- DRW_shader_create_fullscreen_with_shaderlib(
- datatoc_effect_dof_resolve_frag_glsl, e_data.lib, define);
-
- MEM_freeN(define);
- }
- return e_data.dof_resolve_sh[use_bokeh_tx][use_hq_gather];
-}
-
-/** \} */
-
-Material *EEVEE_material_default_diffuse_get(void)
-{
- if (!e_data.diffuse_mat) {
- Material *ma = BKE_id_new_nomain(ID_MA, "EEVEEE default diffuse");
-
- bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
- ma->nodetree = ntree;
- ma->use_nodes = true;
-
- 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);
- e_data.diffuse_mat = ma;
- }
- return e_data.diffuse_mat;
-}
-
-Material *EEVEE_material_default_glossy_get(void)
-{
- if (!e_data.glossy_mat) {
- Material *ma = BKE_id_new_nomain(ID_MA, "EEVEEE default metal");
-
- bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
- ma->nodetree = ntree;
- ma->use_nodes = true;
-
- 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);
- e_data.glossy_mat = ma;
- }
- return e_data.glossy_mat;
-}
-
-Material *EEVEE_material_default_error_get(void)
-{
- if (!e_data.error_mat) {
- Material *ma = BKE_id_new_nomain(ID_MA, "EEVEEE default metal");
-
- bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
- ma->nodetree = ntree;
- ma->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);
- e_data.error_mat = ma;
- }
- return e_data.error_mat;
-}
-
-struct bNodeTree *EEVEE_shader_default_surface_nodetree(Material *ma)
-{
- /* WARNING: This function is not threadsafe. Which is not a problem for the moment. */
- if (!e_data.surface.ntree) {
- 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);
-
- e_data.surface.color_socket = nodeFindSocket(bsdf, SOCK_IN, "Base Color")->default_value;
- e_data.surface.metallic_socket = nodeFindSocket(bsdf, SOCK_IN, "Metallic")->default_value;
- e_data.surface.roughness_socket = nodeFindSocket(bsdf, SOCK_IN, "Roughness")->default_value;
- e_data.surface.specular_socket = nodeFindSocket(bsdf, SOCK_IN, "Specular")->default_value;
- e_data.surface.ntree = ntree;
- }
- /* Update */
- copy_v3_fl3(e_data.surface.color_socket->value, ma->r, ma->g, ma->b);
- e_data.surface.metallic_socket->value = ma->metallic;
- e_data.surface.roughness_socket->value = ma->roughness;
- e_data.surface.specular_socket->value = ma->spec;
-
- return e_data.surface.ntree;
-}
-
-struct bNodeTree *EEVEE_shader_default_world_nodetree(World *wo)
-{
- /* WARNING: This function is not threadsafe. Which is not a problem for the moment. */
- if (!e_data.world.ntree) {
- bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
- bNode *bg = nodeAddStaticNode(NULL, ntree, SH_NODE_BACKGROUND);
- bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_WORLD);
- bNodeSocket *bg_out = nodeFindSocket(bg, SOCK_OUT, "Background");
- bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface");
- nodeAddLink(ntree, bg, bg_out, output, output_in);
- nodeSetActive(ntree, output);
-
- e_data.world.color_socket = nodeFindSocket(bg, SOCK_IN, "Color")->default_value;
- e_data.world.ntree = ntree;
- }
-
- copy_v3_fl3(e_data.world.color_socket->value, wo->horr, wo->horg, wo->horb);
-
- return e_data.world.ntree;
-}
-
-World *EEVEE_world_default_get(void)
-{
- if (e_data.default_world == NULL) {
- e_data.default_world = BKE_id_new_nomain(ID_WO, "EEVEEE default world");
- copy_v3_fl(&e_data.default_world->horr, 0.0f);
- e_data.default_world->use_nodes = 0;
- e_data.default_world->nodetree = NULL;
- BLI_listbase_clear(&e_data.default_world->gpumaterial);
- }
- return e_data.default_world;
-}
-
-static char *eevee_get_defines(int options)
-{
- char *str = NULL;
-
- DynStr *ds = BLI_dynstr_new();
- BLI_dynstr_append(ds, SHADER_DEFINES);
-
- if ((options & VAR_WORLD_BACKGROUND) != 0) {
- BLI_dynstr_append(ds, "#define WORLD_BACKGROUND\n");
- }
- if ((options & VAR_MAT_VOLUME) != 0) {
- BLI_dynstr_append(ds, "#define VOLUMETRICS\n");
- }
- if ((options & VAR_MAT_MESH) != 0) {
- BLI_dynstr_append(ds, "#define MESH_SHADER\n");
- }
- if ((options & VAR_MAT_DEPTH) != 0) {
- BLI_dynstr_append(ds, "#define DEPTH_SHADER\n");
- }
- if ((options & VAR_MAT_HAIR) != 0) {
- BLI_dynstr_append(ds, "#define HAIR_SHADER\n");
- }
- if ((options & VAR_MAT_POINTCLOUD) != 0) {
- BLI_dynstr_append(ds, "#define POINTCLOUD_SHADER\n");
- }
- if ((options & VAR_WORLD_PROBE) != 0) {
- BLI_dynstr_append(ds, "#define PROBE_CAPTURE\n");
- }
- if ((options & VAR_MAT_HASH) != 0) {
- BLI_dynstr_append(ds, "#define USE_ALPHA_HASH\n");
- }
- if ((options & VAR_MAT_BLEND) != 0) {
- BLI_dynstr_append(ds, "#define USE_ALPHA_BLEND\n");
- }
- if ((options & VAR_MAT_REFRACT) != 0) {
- BLI_dynstr_append(ds, "#define USE_REFRACTION\n");
- }
- if ((options & VAR_MAT_LOOKDEV) != 0) {
- BLI_dynstr_append(ds, "#define LOOKDEV\n");
- }
- if ((options & VAR_MAT_HOLDOUT) != 0) {
- BLI_dynstr_append(ds, "#define HOLDOUT\n");
- }
-
- str = BLI_dynstr_get_cstring(ds);
- BLI_dynstr_free(ds);
-
- return str;
-}
-
-static char *eevee_get_vert(int options)
-{
- char *str = NULL;
-
- if ((options & VAR_MAT_VOLUME) != 0) {
- str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_volumetric_vert_glsl);
- }
- else if ((options & (VAR_WORLD_PROBE | VAR_WORLD_BACKGROUND)) != 0) {
- str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_background_vert_glsl);
- }
- else {
- str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_surface_vert_glsl);
- }
-
- return str;
-}
-
-static char *eevee_get_geom(int options)
-{
- char *str = NULL;
-
- if ((options & VAR_MAT_VOLUME) != 0) {
- str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_volumetric_geom_glsl);
- }
-
- return str;
-}
-
-static char *eevee_get_frag(int options)
-{
- char *str = NULL;
-
- if ((options & VAR_MAT_VOLUME) != 0) {
- str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_volumetric_frag_glsl);
- }
- else if ((options & VAR_MAT_DEPTH) != 0) {
- str = BLI_strdup(e_data.surface_prepass_frag);
- }
- else {
- str = BLI_strdup(e_data.surface_lit_frag);
- }
-
- return str;
-}
-
-static void eevee_material_post_eval(GPUMaterial *mat,
- int options,
- const char **UNUSED(vert_code),
- const char **geom_code,
- const char **UNUSED(frag_lib),
- const char **UNUSED(defines))
-{
- const bool is_hair = (options & VAR_MAT_HAIR) != 0;
- const bool is_mesh = (options & VAR_MAT_MESH) != 0;
-
- /* Force geometry usage if GPU_BARYCENTRIC_DIST or GPU_BARYCENTRIC_TEXCO are used.
- * NOTE: GPU_BARYCENTRIC_TEXCO only requires it if the shader is not drawing hairs. */
- if (!is_hair && is_mesh && GPU_material_flag_get(mat, GPU_MATFLAG_BARYCENTRIC) &&
- *geom_code == NULL) {
- *geom_code = e_data.surface_geom_barycentric;
- }
-}
-
-static struct GPUMaterial *eevee_material_get_ex(
- struct Scene *scene, Material *ma, World *wo, int options, bool deferred)
-{
- BLI_assert(ma || wo);
- const bool is_volume = (options & VAR_MAT_VOLUME) != 0;
- const bool is_default = (options & VAR_DEFAULT) != 0;
- const void *engine = &DRW_engine_viewport_eevee_type;
-
- GPUMaterial *mat = NULL;
-
- if (ma) {
- mat = DRW_shader_find_from_material(ma, engine, options, deferred);
- }
- else {
- mat = DRW_shader_find_from_world(wo, engine, options, deferred);
- }
-
- if (mat) {
- return mat;
- }
-
- char *defines = eevee_get_defines(options);
- char *vert = eevee_get_vert(options);
- char *geom = eevee_get_geom(options);
- char *frag = eevee_get_frag(options);
-
- if (ma) {
- GPUMaterialEvalCallbackFn cbfn = &eevee_material_post_eval;
-
- bNodeTree *ntree = !is_default ? ma->nodetree : EEVEE_shader_default_surface_nodetree(ma);
- mat = DRW_shader_create_from_material(
- scene, ma, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred, cbfn);
- }
- else {
- bNodeTree *ntree = !is_default ? wo->nodetree : EEVEE_shader_default_world_nodetree(wo);
- mat = DRW_shader_create_from_world(
- scene, wo, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred, NULL);
- }
-
- MEM_SAFE_FREE(defines);
- MEM_SAFE_FREE(vert);
- MEM_SAFE_FREE(geom);
- MEM_SAFE_FREE(frag);
-
- return mat;
-}
-
-struct GPUMaterial *EEVEE_material_default_get(struct Scene *scene, Material *ma, int options)
-{
- Material *def_ma = (ma && (options & VAR_MAT_VOLUME)) ? BKE_material_default_volume() :
- BKE_material_default_surface();
- BLI_assert(def_ma->use_nodes && def_ma->nodetree);
-
- return eevee_material_get_ex(scene, def_ma, NULL, options, false);
-}
-
-struct GPUMaterial *EEVEE_material_get(
- EEVEE_Data *vedata, struct Scene *scene, Material *ma, World *wo, int options)
-{
- if ((ma && (!ma->use_nodes || !ma->nodetree)) || (wo && (!wo->use_nodes || !wo->nodetree))) {
- options |= VAR_DEFAULT;
- }
-
- /* Meh, implicit option. World probe cannot be deferred because they need
- * to be rendered immediately. */
- const bool deferred = (options & VAR_WORLD_PROBE) == 0;
-
- GPUMaterial *mat = eevee_material_get_ex(scene, ma, wo, options, deferred);
-
- int status = GPU_material_status(mat);
- switch (status) {
- case GPU_MAT_SUCCESS:
- break;
- case GPU_MAT_QUEUED:
- vedata->stl->g_data->queued_shaders_count++;
- mat = EEVEE_material_default_get(scene, ma, options);
- break;
- case GPU_MAT_FAILED:
- default:
- ma = EEVEE_material_default_error_get();
- mat = eevee_material_get_ex(scene, ma, NULL, options, false);
- break;
- }
- /* Returned material should be ready to be drawn. */
- BLI_assert(GPU_material_status(mat) == GPU_MAT_SUCCESS);
- return mat;
-}
-
-void EEVEE_shaders_free(void)
-{
- MEM_SAFE_FREE(e_data.surface_prepass_frag);
- MEM_SAFE_FREE(e_data.surface_lit_frag);
- MEM_SAFE_FREE(e_data.surface_geom_barycentric);
- DRW_SHADER_FREE_SAFE(e_data.lookdev_background);
- DRW_SHADER_FREE_SAFE(e_data.update_noise_sh);
- DRW_SHADER_FREE_SAFE(e_data.color_copy_sh);
- DRW_SHADER_FREE_SAFE(e_data.downsample_sh);
- DRW_SHADER_FREE_SAFE(e_data.downsample_cube_sh);
- DRW_SHADER_FREE_SAFE(e_data.minz_downlevel_sh);
- DRW_SHADER_FREE_SAFE(e_data.maxz_downlevel_sh);
- DRW_SHADER_FREE_SAFE(e_data.minz_downdepth_sh);
- DRW_SHADER_FREE_SAFE(e_data.maxz_downdepth_sh);
- DRW_SHADER_FREE_SAFE(e_data.minz_downdepth_layer_sh);
- DRW_SHADER_FREE_SAFE(e_data.maxz_downdepth_layer_sh);
- DRW_SHADER_FREE_SAFE(e_data.maxz_copydepth_layer_sh);
- DRW_SHADER_FREE_SAFE(e_data.minz_copydepth_sh);
- DRW_SHADER_FREE_SAFE(e_data.maxz_copydepth_sh);
- DRW_SHADER_FREE_SAFE(e_data.ggx_lut_sh);
- DRW_SHADER_FREE_SAFE(e_data.ggx_refraction_lut_sh);
- DRW_SHADER_FREE_SAFE(e_data.mist_sh);
- DRW_SHADER_FREE_SAFE(e_data.motion_blur_sh);
- DRW_SHADER_FREE_SAFE(e_data.motion_blur_object_sh);
- DRW_SHADER_FREE_SAFE(e_data.motion_blur_hair_sh);
- DRW_SHADER_FREE_SAFE(e_data.velocity_tiles_sh);
- DRW_SHADER_FREE_SAFE(e_data.velocity_tiles_expand_sh);
- DRW_SHADER_FREE_SAFE(e_data.gtao_sh);
- DRW_SHADER_FREE_SAFE(e_data.gtao_layer_sh);
- DRW_SHADER_FREE_SAFE(e_data.gtao_debug_sh);
- DRW_SHADER_FREE_SAFE(e_data.velocity_resolve_sh);
- DRW_SHADER_FREE_SAFE(e_data.postprocess_sh);
- DRW_SHADER_FREE_SAFE(e_data.shadow_sh);
- DRW_SHADER_FREE_SAFE(e_data.shadow_accum_sh);
- DRW_SHADER_FREE_SAFE(e_data.sss_sh[0]);
- DRW_SHADER_FREE_SAFE(e_data.sss_sh[1]);
- DRW_SHADER_FREE_SAFE(e_data.sss_sh[2]);
- DRW_SHADER_FREE_SAFE(e_data.volumetric_clear_sh);
- DRW_SHADER_FREE_SAFE(e_data.scatter_sh);
- DRW_SHADER_FREE_SAFE(e_data.scatter_with_lights_sh);
- DRW_SHADER_FREE_SAFE(e_data.volumetric_integration_sh);
- DRW_SHADER_FREE_SAFE(e_data.volumetric_resolve_sh[0]);
- DRW_SHADER_FREE_SAFE(e_data.volumetric_resolve_sh[1]);
- DRW_SHADER_FREE_SAFE(e_data.volumetric_accum_sh);
- DRW_SHADER_FREE_SAFE(e_data.probe_filter_glossy_sh);
- DRW_SHADER_FREE_SAFE(e_data.probe_filter_diffuse_sh);
- DRW_SHADER_FREE_SAFE(e_data.probe_filter_visibility_sh);
- DRW_SHADER_FREE_SAFE(e_data.probe_grid_fill_sh);
- DRW_SHADER_FREE_SAFE(e_data.probe_planar_downsample_sh);
- DRW_SHADER_FREE_SAFE(e_data.studiolight_probe_sh);
- DRW_SHADER_FREE_SAFE(e_data.studiolight_background_sh);
- DRW_SHADER_FREE_SAFE(e_data.probe_grid_display_sh);
- DRW_SHADER_FREE_SAFE(e_data.probe_cube_display_sh);
- DRW_SHADER_FREE_SAFE(e_data.probe_planar_display_sh);
- DRW_SHADER_FREE_SAFE(e_data.velocity_resolve_sh);
- DRW_SHADER_FREE_SAFE(e_data.taa_resolve_sh);
- DRW_SHADER_FREE_SAFE(e_data.taa_resolve_reproject_sh);
- DRW_SHADER_FREE_SAFE(e_data.dof_bokeh_sh);
- DRW_SHADER_FREE_SAFE(e_data.dof_setup_sh);
- DRW_SHADER_FREE_SAFE(e_data.dof_flatten_tiles_sh);
- DRW_SHADER_FREE_SAFE(e_data.dof_dilate_tiles_sh[0]);
- DRW_SHADER_FREE_SAFE(e_data.dof_dilate_tiles_sh[1]);
- DRW_SHADER_FREE_SAFE(e_data.dof_downsample_sh);
- DRW_SHADER_FREE_SAFE(e_data.dof_reduce_sh[0]);
- DRW_SHADER_FREE_SAFE(e_data.dof_reduce_sh[1]);
- for (int i = 0; i < DOF_GATHER_MAX_PASS; i++) {
- DRW_SHADER_FREE_SAFE(e_data.dof_gather_sh[i][0]);
- DRW_SHADER_FREE_SAFE(e_data.dof_gather_sh[i][1]);
- }
- DRW_SHADER_FREE_SAFE(e_data.dof_filter_sh);
- DRW_SHADER_FREE_SAFE(e_data.dof_scatter_sh[0][0]);
- DRW_SHADER_FREE_SAFE(e_data.dof_scatter_sh[0][1]);
- DRW_SHADER_FREE_SAFE(e_data.dof_scatter_sh[1][0]);
- DRW_SHADER_FREE_SAFE(e_data.dof_scatter_sh[1][1]);
- DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh[0][0]);
- DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh[0][1]);
- DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh[1][0]);
- DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh[1][1]);
- DRW_SHADER_FREE_SAFE(e_data.cryptomatte_sh[0]);
- DRW_SHADER_FREE_SAFE(e_data.cryptomatte_sh[1]);
- for (int i = 0; i < 2; i++) {
- DRW_SHADER_FREE_SAFE(e_data.bloom_blit_sh[i]);
- DRW_SHADER_FREE_SAFE(e_data.bloom_downsample_sh[i]);
- DRW_SHADER_FREE_SAFE(e_data.bloom_upsample_sh[i]);
- DRW_SHADER_FREE_SAFE(e_data.bloom_resolve_sh[i]);
- }
- DRW_SHADER_FREE_SAFE(e_data.reflection_trace);
- DRW_SHADER_FREE_SAFE(e_data.reflection_resolve);
- DRW_SHADER_LIB_FREE_SAFE(e_data.lib);
-
- if (e_data.default_world) {
- BKE_id_free(NULL, e_data.default_world);
- e_data.default_world = NULL;
- }
- if (e_data.glossy_mat) {
- BKE_id_free(NULL, e_data.glossy_mat);
- e_data.glossy_mat = NULL;
- }
- if (e_data.diffuse_mat) {
- BKE_id_free(NULL, e_data.diffuse_mat);
- e_data.diffuse_mat = NULL;
- }
- if (e_data.error_mat) {
- BKE_id_free(NULL, e_data.error_mat);
- e_data.error_mat = NULL;
- }
- if (e_data.surface.ntree) {
- ntreeFreeEmbeddedTree(e_data.surface.ntree);
- MEM_freeN(e_data.surface.ntree);
- e_data.surface.ntree = NULL;
- }
- if (e_data.world.ntree) {
- ntreeFreeEmbeddedTree(e_data.world.ntree);
- MEM_freeN(e_data.world.ntree);
- e_data.world.ntree = NULL;
- }
-}
diff --git a/source/blender/draw/engines/eevee/eevee_shading.cc b/source/blender/draw/engines/eevee/eevee_shading.cc
new file mode 100644
index 00000000000..53567ad4611
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_shading.cc
@@ -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. */
+ mat4 camera_mat;
+ unit_m4(camera_mat);
+ copy_v3_v3(camera_mat[3], inst_.camera.data_get().viewinv[3]);
+
+ DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, background_ps_);
+ if (lookdev_tx != nullptr) {
+ /* HACK(fclem) This particular texture has been left without resource to be set here. */
+ DRW_shgroup_uniform_texture(grp, "samp0", lookdev_tx);
+ }
+ DRW_shgroup_call_obmat(grp, DRW_cache_fullscreen_quad_get(), camera_mat);
+}
+
+void BackgroundPass::render(void)
+{
+ DRW_draw_pass(background_ps_);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Forward Pass
+ *
+ * NPR materials (using Closure to RGBA) or material using ALPHA_BLEND.
+ * \{ */
+
+void ForwardPass::sync(void)
+{
+ {
+ DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
+ prepass_ps_ = DRW_pass_create("Forward.Opaque.Prepass", state);
+
+ state |= DRW_STATE_CULL_BACK;
+ prepass_culled_ps_ = DRW_pass_create("Forward.Opaque.Prepass.Culled", state);
+
+ DRW_pass_link(prepass_ps_, prepass_culled_ps_);
+ }
+ {
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL;
+ opaque_ps_ = DRW_pass_create("Forward.Opaque", state);
+
+ state |= DRW_STATE_CULL_BACK;
+ opaque_culled_ps_ = DRW_pass_create("Forward.Opaque.Culled", state);
+
+ DRW_pass_link(opaque_ps_, opaque_culled_ps_);
+ }
+ {
+ DRWState state = DRW_STATE_DEPTH_LESS_EQUAL;
+ transparent_ps_ = DRW_pass_create("Forward.Transparent", state);
+ }
+}
+
+DRWShadingGroup *ForwardPass::material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat)
+{
+ DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? opaque_culled_ps_ : opaque_ps_;
+ LightModule &lights = inst_.lights;
+ LightProbeModule &lightprobes = inst_.lightprobes;
+ eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
+ DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
+ lights.shgroup_resources(grp);
+ DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
+ DRW_shgroup_uniform_block(grp, "grids_block", lightprobes.grid_ubo_get());
+ DRW_shgroup_uniform_block(grp, "cubes_block", lightprobes.cube_ubo_get());
+ DRW_shgroup_uniform_block(grp, "lightprobes_info_block", lightprobes.info_ubo_get());
+ DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get());
+ DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
+ DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
+ /* TODO(fclem): Make this only needed if material uses it ... somehow. */
+ if (true) {
+ DRW_shgroup_uniform_texture_ref(
+ grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get());
+ }
+ if (true) {
+ DRW_shgroup_uniform_block(grp, "rt_diffuse_block", inst_.raytracing.diffuse_ubo_get());
+ DRW_shgroup_uniform_block(grp, "rt_reflection_block", inst_.raytracing.reflection_ubo_get());
+ DRW_shgroup_uniform_block(grp, "rt_refraction_block", inst_.raytracing.refraction_ubo_get());
+ DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_radiance_tx_, no_interp);
+ }
+ if (true) {
+ DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.ubo_get());
+ DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_tx_);
+ }
+ return grp;
+}
+
+DRWShadingGroup *ForwardPass::prepass_opaque_add(::Material *blender_mat, GPUMaterial *gpumat)
+{
+ DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? prepass_culled_ps_ :
+ prepass_ps_;
+ DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
+ return grp;
+}
+
+DRWShadingGroup *ForwardPass::material_transparent_add(::Material *blender_mat,
+ GPUMaterial *gpumat)
+{
+ LightModule &lights = inst_.lights;
+ LightProbeModule &lightprobes = inst_.lightprobes;
+ eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
+ DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, transparent_ps_);
+ lights.shgroup_resources(grp);
+ DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
+ DRW_shgroup_uniform_block(grp, "grids_block", lightprobes.grid_ubo_get());
+ DRW_shgroup_uniform_block(grp, "cubes_block", lightprobes.cube_ubo_get());
+ DRW_shgroup_uniform_block(grp, "lightprobes_info_block", lightprobes.info_ubo_get());
+ DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get());
+ DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
+ DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
+ /* TODO(fclem): Make this only needed if material uses it ... somehow. */
+ if (true) {
+ DRW_shgroup_uniform_texture_ref(
+ grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get());
+ }
+ if (true) {
+ DRW_shgroup_uniform_block(grp, "rt_diffuse_block", inst_.raytracing.diffuse_ubo_get());
+ DRW_shgroup_uniform_block(grp, "rt_reflection_block", inst_.raytracing.reflection_ubo_get());
+ DRW_shgroup_uniform_block(grp, "rt_refraction_block", inst_.raytracing.refraction_ubo_get());
+ DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_radiance_tx_, no_interp);
+ }
+ if (true) {
+ DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.ubo_get());
+ DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_tx_);
+ }
+
+ DRWState state_disable = DRW_STATE_WRITE_DEPTH;
+ DRWState state_enable = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM;
+ if (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) {
+ state_enable |= DRW_STATE_CULL_BACK;
+ }
+ DRW_shgroup_state_disable(grp, state_disable);
+ DRW_shgroup_state_enable(grp, state_enable);
+ return grp;
+}
+
+DRWShadingGroup *ForwardPass::prepass_transparent_add(::Material *blender_mat, GPUMaterial *gpumat)
+{
+ if ((blender_mat->blend_flag & MA_BL_HIDE_BACKFACE) == 0) {
+ return nullptr;
+ }
+
+ DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, transparent_ps_);
+
+ DRWState state_disable = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM;
+ DRWState state_enable = DRW_STATE_WRITE_DEPTH;
+ if (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) {
+ state_enable |= DRW_STATE_CULL_BACK;
+ }
+ DRW_shgroup_state_disable(grp, state_disable);
+ DRW_shgroup_state_enable(grp, state_enable);
+ return grp;
+}
+
+void ForwardPass::render(const DRWView *view,
+ GBuffer &gbuffer,
+ HiZBuffer &hiz,
+ GPUFrameBuffer *view_fb)
+{
+ if (inst_.raytracing.enabled()) {
+ ivec2 extent = {GPU_texture_width(gbuffer.depth_tx), GPU_texture_height(gbuffer.depth_tx)};
+ /* Reuse texture. */
+ gbuffer.ray_radiance_tx.acquire_tmp(UNPACK2(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_tmp();
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name DeferredLayer
+ * \{ */
+
+void DeferredLayer::sync(void)
+{
+ {
+ DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
+ prepass_ps_ = DRW_pass_create("Gbuffer.Prepass", state);
+
+ state |= DRW_STATE_CULL_BACK;
+ prepass_culled_ps_ = DRW_pass_create("Gbuffer.Prepass.Culled", state);
+
+ DRW_pass_link(prepass_ps_, prepass_culled_ps_);
+ }
+ {
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_STENCIL_ALWAYS |
+ DRW_STATE_WRITE_STENCIL;
+ gbuffer_ps_ = DRW_pass_create("Gbuffer", state);
+
+ state |= DRW_STATE_CULL_BACK;
+ gbuffer_culled_ps_ = DRW_pass_create("Gbuffer.Culled", state);
+
+ DRW_pass_link(gbuffer_ps_, gbuffer_culled_ps_);
+ }
+ {
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL |
+ DRW_STATE_CULL_BACK | DRW_STATE_STENCIL_ALWAYS | DRW_STATE_WRITE_STENCIL;
+ volume_ps_ = DRW_pass_create("VolumesHeterogeneous", state);
+ }
+}
+
+DRWShadingGroup *DeferredLayer::material_add(::Material *blender_mat, GPUMaterial *gpumat)
+{
+ /* TODO/OPTI(fclem) Set the right mask for each effect based on gpumat flags. */
+ uint stencil_mask = CLOSURE_DIFFUSE | CLOSURE_SSS | CLOSURE_REFLECTION | CLOSURE_TRANSPARENCY |
+ CLOSURE_EMISSION | CLOSURE_REFRACTION;
+ DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? gbuffer_culled_ps_ :
+ gbuffer_ps_;
+ DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
+ DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
+ DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
+ DRW_shgroup_stencil_set(grp, stencil_mask, 0xFF, 0xFF);
+ return grp;
+}
+
+DRWShadingGroup *DeferredLayer::prepass_add(::Material *blender_mat, GPUMaterial *gpumat)
+{
+ DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? prepass_culled_ps_ :
+ prepass_ps_;
+ DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
+ DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
+ return grp;
+}
+
+void DeferredLayer::volume_add(Object *ob)
+{
+ LightModule &lights = inst_.lights;
+ DeferredPass &deferred_pass = inst_.shading_passes.deferred;
+
+ GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_VOLUME);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, volume_ps_);
+ lights.shgroup_resources(grp);
+ DRW_shgroup_uniform_texture_ref(grp, "depth_max_tx", &deferred_pass.input_depth_behind_tx_);
+ DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
+ DRW_shgroup_stencil_set(grp, CLOSURE_VOLUME | CLOSURE_TRANSPARENCY, 0xFF, 0xFF);
+ DRW_shgroup_call(grp, DRW_cache_cube_get(), ob);
+}
+
+void DeferredLayer::render(const DRWView *view,
+ GBuffer &gbuffer,
+ HiZBuffer &hiz_front,
+ HiZBuffer &hiz_back,
+ RaytraceBuffer &rt_buffer,
+ GPUFrameBuffer *view_fb)
+{
+ DeferredPass &deferred_pass = inst_.shading_passes.deferred;
+
+ const bool no_surfaces = DRW_pass_is_empty(gbuffer_ps_);
+ const bool no_volumes = DRW_pass_is_empty(volume_ps_);
+ if (no_surfaces && no_volumes) {
+ return;
+ }
+ /* TODO(fclem): detect these cases. */
+ const bool use_diffuse = true;
+ const bool use_subsurface = true;
+ const bool use_transparency = true;
+ const bool use_holdout = true;
+ const bool use_refraction = true;
+ const bool use_glossy = true;
+ const bool use_ao = false;
+
+ gbuffer.prepare((eClosureBits)0xFFFFFFFFu);
+ if (use_ao || use_glossy || use_diffuse) {
+ hiz_front.prepare(gbuffer.depth_tx);
+ }
+ if (use_refraction) {
+ hiz_back.prepare(gbuffer.depth_tx);
+ }
+
+ update_pass_inputs(gbuffer, hiz_front, hiz_back);
+
+ if (use_refraction) {
+ /* TODO(fclem) Only update if needed.
+ * i.e: No need when SSR from previous layer has already updated hiz. */
+ hiz_back.update(gbuffer.depth_tx);
+ }
+
+ gbuffer.bind();
+
+ if (!no_surfaces) {
+ DRW_draw_pass(prepass_ps_);
+
+ /* TODO(fclem): Ambient Occlusion texture node. */
+ if (use_ao) {
+ hiz_front.update(gbuffer.depth_tx);
+ gbuffer.bind();
+ }
+
+ DRW_draw_pass(gbuffer_ps_);
+ }
+
+ inst_.shadows.set_view(view, gbuffer.depth_tx);
+
+ if (!no_volumes) {
+ // gbuffer.copy_depth_behind();
+ // deferred_pass.input_depth_behind_tx_ = gbuffer.depth_behind_tx;
+
+ gbuffer.bind_volume();
+ DRW_draw_pass(volume_ps_);
+ }
+
+ if (use_holdout) {
+ gbuffer.bind_holdout();
+ DRW_draw_pass(deferred_pass.eval_holdout_ps_);
+ }
+
+ /* TODO(fclem) We could bypass update if ao already updated it and if there is no volume. */
+ hiz_front.update(gbuffer.depth_tx);
+
+ if (!no_surfaces && use_refraction) {
+ /* Launch and shade refraction rays before transparency changes the combined pass. */
+ rt_buffer.trace(CLOSURE_REFRACTION, gbuffer, hiz_back, hiz_front);
+ }
+
+ GPU_framebuffer_bind(view_fb);
+ if (use_transparency) {
+ DRW_draw_pass(deferred_pass.eval_transparency_ps_);
+ }
+
+ gbuffer.clear_radiance();
+
+ if (!no_surfaces && use_refraction) {
+ rt_buffer.denoise(CLOSURE_REFRACTION);
+ rt_buffer.resolve(CLOSURE_REFRACTION, gbuffer);
+ }
+
+ if (!no_volumes) {
+ /* TODO(fclem) volume fb. */
+ GPU_framebuffer_bind(view_fb);
+ DRW_draw_pass(deferred_pass.eval_volume_homogeneous_ps_);
+ }
+
+ if (!no_surfaces) {
+ gbuffer.bind_radiance();
+ DRW_draw_pass(deferred_pass.eval_direct_ps_);
+
+ if (use_diffuse) {
+ rt_buffer.trace(CLOSURE_DIFFUSE, gbuffer, hiz_front, hiz_front);
+ rt_buffer.denoise(CLOSURE_DIFFUSE);
+ rt_buffer.resolve(CLOSURE_DIFFUSE, gbuffer);
+ }
+
+ if (use_subsurface) {
+ GPU_framebuffer_bind(view_fb);
+ DRW_draw_pass(deferred_pass.eval_subsurface_ps_);
+ }
+
+ if (use_glossy) {
+ rt_buffer.trace(CLOSURE_REFLECTION, gbuffer, hiz_front, hiz_front);
+ rt_buffer.denoise(CLOSURE_REFLECTION);
+ rt_buffer.resolve(CLOSURE_REFLECTION, gbuffer);
+ }
+ }
+}
+
+void DeferredLayer::update_pass_inputs(GBuffer &gbuffer, HiZBuffer &hiz_front, HiZBuffer &hiz_back)
+{
+ DeferredPass &deferred_pass = inst_.shading_passes.deferred;
+ deferred_pass.input_combined_tx_ = gbuffer.combined_tx;
+ deferred_pass.input_emission_data_tx_ = gbuffer.emission_tx;
+ deferred_pass.input_transmit_color_tx_ = gbuffer.transmit_color_tx;
+ deferred_pass.input_transmit_normal_tx_ = gbuffer.transmit_normal_tx;
+ deferred_pass.input_transmit_data_tx_ = gbuffer.transmit_data_tx;
+ deferred_pass.input_reflect_color_tx_ = gbuffer.reflect_color_tx;
+ deferred_pass.input_reflect_normal_tx_ = gbuffer.reflect_normal_tx;
+ deferred_pass.input_diffuse_tx_ = gbuffer.diffuse_tx;
+ deferred_pass.input_transparency_data_tx_ = gbuffer.transparency_tx;
+ deferred_pass.input_volume_data_tx_ = gbuffer.volume_tx;
+ deferred_pass.input_hiz_front_tx_ = hiz_front.texture_get();
+ deferred_pass.input_hiz_back_tx_ = hiz_back.texture_get();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name DeferredLayer
+ * \{ */
+
+void DeferredPass::sync(void)
+{
+ opaque_layer_.sync();
+ refraction_layer_.sync();
+ volumetric_layer_.sync();
+
+ LightModule &lights = inst_.lights;
+ LightProbeModule &lightprobes = inst_.lightprobes;
+
+ eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
+
+ {
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_ADD_FULL;
+ eval_direct_ps_ = DRW_pass_create("DeferredDirect", state);
+ GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_EVAL_DIRECT);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_direct_ps_);
+ lights.shgroup_resources(grp);
+ DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
+ DRW_shgroup_uniform_block(grp, "grids_block", lightprobes.grid_ubo_get());
+ DRW_shgroup_uniform_block(grp, "cubes_block", lightprobes.cube_ubo_get());
+ DRW_shgroup_uniform_block(grp, "lightprobes_info_block", lightprobes.info_ubo_get());
+ DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get());
+ DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
+ DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
+ DRW_shgroup_uniform_texture_ref_ex(
+ grp, "emission_data_tx", &input_emission_data_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(
+ grp, "transmit_color_tx", &input_transmit_color_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(
+ grp, "transmit_normal_tx", &input_transmit_normal_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(
+ grp, "transmit_data_tx", &input_transmit_data_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(
+ grp, "reflect_color_tx", &input_reflect_color_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(
+ grp, "reflect_normal_tx", &input_reflect_normal_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_front_tx_);
+ DRW_shgroup_uniform_texture_ref(
+ grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get());
+ DRW_shgroup_stencil_set(
+ grp, 0x0, 0x0, CLOSURE_DIFFUSE | CLOSURE_REFLECTION | CLOSURE_EMISSION);
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+ }
+ {
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL | DRW_STATE_BLEND_ADD_FULL;
+ eval_subsurface_ps_ = DRW_pass_create("DeferredSubsurface", state);
+ GPUShader *sh = inst_.shaders.static_shader_get(SUBSURFACE_EVAL);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_subsurface_ps_);
+ DRW_shgroup_uniform_block(grp, "subsurface_block", inst_.subsurface.ubo_get());
+ DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
+ DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.ubo_get());
+ DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_front_tx_);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_diffuse_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(
+ grp, "transmit_color_tx", &input_transmit_color_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(
+ grp, "transmit_normal_tx", &input_transmit_normal_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(
+ grp, "transmit_data_tx", &input_transmit_data_tx_, no_interp);
+ DRW_shgroup_stencil_set(grp, 0x0, 0xFF, CLOSURE_SSS);
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+ }
+ {
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_ADD_FULL;
+ eval_volume_homogeneous_ps_ = DRW_pass_create("DeferredVolume", state);
+ GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_EVAL_VOLUME);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_volume_homogeneous_ps_);
+ lights.shgroup_resources(grp);
+ DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx);
+ DRW_shgroup_uniform_texture_ref_ex(
+ grp, "transparency_data_tx", &input_transparency_data_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "volume_data_tx", &input_volume_data_tx_, no_interp);
+ DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_front_tx_);
+ DRW_shgroup_stencil_set(grp, 0x0, 0x0, CLOSURE_VOLUME);
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+ }
+ {
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_MUL;
+ eval_transparency_ps_ = DRW_pass_create("DeferredTransparency", state);
+ GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_EVAL_TRANSPARENT);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_transparency_ps_);
+ DRW_shgroup_uniform_texture_ref(grp, "transparency_data_tx", &input_transparency_data_tx_);
+ DRW_shgroup_uniform_texture_ref_ex(grp, "volume_data_tx", &input_volume_data_tx_, no_interp);
+ DRW_shgroup_stencil_set(grp, 0x0, 0x0, CLOSURE_TRANSPARENCY);
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+ }
+ {
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL;
+ eval_holdout_ps_ = DRW_pass_create("DeferredHoldout", state);
+ GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_EVAL_HOLDOUT);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_volume_homogeneous_ps_);
+ DRW_shgroup_uniform_texture_ref(grp, "combined_tx", &input_combined_tx_);
+ DRW_shgroup_uniform_texture_ref(grp, "transparency_data_tx", &input_transparency_data_tx_);
+ DRW_shgroup_stencil_set(grp, 0x0, 0x0, CLOSURE_TRANSPARENCY);
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+ }
+}
+
+DRWShadingGroup *DeferredPass::material_add(::Material *material, GPUMaterial *gpumat)
+{
+ if (material->blend_flag & MA_BL_SS_REFRACTION) {
+ return refraction_layer_.material_add(material, gpumat);
+ }
+ else {
+ return opaque_layer_.material_add(material, gpumat);
+ }
+}
+
+DRWShadingGroup *DeferredPass::prepass_add(::Material *material, GPUMaterial *gpumat)
+{
+ if (material->blend_flag & MA_BL_SS_REFRACTION) {
+ return refraction_layer_.prepass_add(material, gpumat);
+ }
+ else {
+ return opaque_layer_.prepass_add(material, gpumat);
+ }
+}
+
+void DeferredPass::volume_add(Object *ob)
+{
+ volumetric_layer_.volume_add(ob);
+}
+
+void DeferredPass::render(const DRWView *drw_view,
+ GBuffer &gbuffer,
+ HiZBuffer &hiz_front,
+ HiZBuffer &hiz_back,
+ RaytraceBuffer &rt_buffer_opaque_,
+ RaytraceBuffer &rt_buffer_refract_,
+ GPUFrameBuffer *view_fb)
+{
+ DRW_stats_group_start("OpaqueLayer");
+ opaque_layer_.render(drw_view, gbuffer, hiz_front, hiz_back, rt_buffer_opaque_, view_fb);
+ DRW_stats_group_end();
+
+ DRW_stats_group_start("RefractionLayer");
+ refraction_layer_.render(drw_view, gbuffer, hiz_front, hiz_back, rt_buffer_refract_, view_fb);
+ DRW_stats_group_end();
+
+ /* NOTE(fclem): Reuse the same rtbuffer as refraction but should not use it. */
+ DRW_stats_group_start("VolumetricLayer");
+ volumetric_layer_.render(drw_view, gbuffer, hiz_front, hiz_back, rt_buffer_refract_, view_fb);
+ DRW_stats_group_end();
+
+ gbuffer.render_end();
+ rt_buffer_opaque_.render_end(drw_view);
+ rt_buffer_refract_.render_end(drw_view);
+}
+
+/** \} */
+
+} // namespace blender::eevee \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/eevee_shading.hh b/source/blender/draw/engines/eevee/eevee_shading.hh
new file mode 100644
index 00000000000..132db6bd334
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_shading.hh
@@ -0,0 +1,328 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Shading passes contain drawcalls specific to shading pipelines.
+ * They are to be shared across views.
+ * This file is only for shading passes. Other passes are declared in their own module.
+ */
+
+#pragma once
+
+#include "DRW_render.h"
+
+#include "eevee_lut.h"
+
+#include "eevee_gbuffer.hh"
+#include "eevee_raytracing.hh"
+#include "eevee_shadow.hh"
+#include "eevee_velocity.hh"
+
+namespace blender::eevee {
+
+class Instance;
+
+/* -------------------------------------------------------------------- */
+/** \name Background Pass
+ *
+ * Render world values.
+ * \{ */
+
+class BackgroundPass {
+ private:
+ Instance &inst_;
+
+ DRWPass *background_ps_ = nullptr;
+
+ public:
+ BackgroundPass(Instance &inst) : inst_(inst){};
+
+ void sync(GPUMaterial *gpumat, GPUTexture *loodev_tx = nullptr);
+ void render(void);
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Forward Pass
+ *
+ * Handles alpha blended surfaces and NPR materials (using Closure to RGBA).
+ * \{ */
+
+class ForwardPass {
+ private:
+ Instance &inst_;
+
+ DRWPass *prepass_ps_ = nullptr;
+ DRWPass *prepass_culled_ps_ = nullptr;
+ DRWPass *opaque_ps_ = nullptr;
+ DRWPass *opaque_culled_ps_ = nullptr;
+ DRWPass *transparent_ps_ = nullptr;
+
+ GPUTexture *input_hiz_tx_ = nullptr;
+ GPUTexture *input_radiance_tx_ = nullptr;
+
+ public:
+ ForwardPass(Instance &inst) : inst_(inst){};
+
+ void sync(void);
+
+ DRWShadingGroup *material_add(::Material *blender_mat, GPUMaterial *gpumat)
+ {
+ return (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) ?
+ material_transparent_add(blender_mat, gpumat) :
+ material_opaque_add(blender_mat, gpumat);
+ }
+
+ DRWShadingGroup *prepass_add(::Material *blender_mat, GPUMaterial *gpumat)
+ {
+ return (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) ?
+ prepass_transparent_add(blender_mat, gpumat) :
+ prepass_opaque_add(blender_mat, gpumat);
+ }
+
+ DRWShadingGroup *material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat);
+ DRWShadingGroup *prepass_opaque_add(::Material *blender_mat, GPUMaterial *gpumat);
+ DRWShadingGroup *material_transparent_add(::Material *blender_mat, GPUMaterial *gpumat);
+ DRWShadingGroup *prepass_transparent_add(::Material *blender_mat, GPUMaterial *gpumat);
+
+ void render(const DRWView *view, GBuffer &gbuffer, HiZBuffer &hiz, GPUFrameBuffer *view_fb);
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Deferred lighting.
+ * \{ */
+
+class DeferredLayer {
+ private:
+ Instance &inst_;
+
+ /* TODO */
+ // GPUTexture *input_emission_data_tx_ = nullptr;
+ // GPUTexture *input_diffuse_data_tx_ = nullptr;
+ // GPUTexture *input_depth_tx_ = nullptr;
+
+ DRWPass *prepass_ps_ = nullptr;
+ DRWPass *prepass_culled_ps_ = nullptr;
+ DRWPass *gbuffer_ps_ = nullptr;
+ DRWPass *gbuffer_culled_ps_ = nullptr;
+ DRWPass *volume_ps_ = nullptr;
+
+ public:
+ DeferredLayer(Instance &inst) : inst_(inst){};
+
+ void sync(void);
+ DRWShadingGroup *material_add(::Material *blender_mat, GPUMaterial *gpumat);
+ DRWShadingGroup *prepass_add(::Material *blender_mat, GPUMaterial *gpumat);
+ void volume_add(Object *ob);
+ void render(const DRWView *view,
+ GBuffer &gbuffer,
+ HiZBuffer &hiz_front,
+ HiZBuffer &hiz_back,
+ RaytraceBuffer &rtbuffer,
+ GPUFrameBuffer *view_fb);
+
+ private:
+ void update_pass_inputs(GBuffer &gbuffer, HiZBuffer &hiz_front, HiZBuffer &hiz_back);
+};
+
+class DeferredPass {
+ friend DeferredLayer;
+
+ private:
+ Instance &inst_;
+
+ /* Gbuffer filling passes. We could have an arbitrary number of them but for now we just have
+ * a hardcoded number of them. */
+ DeferredLayer opaque_layer_;
+ DeferredLayer refraction_layer_;
+ DeferredLayer volumetric_layer_;
+
+ DRWPass *eval_direct_ps_ = nullptr;
+ DRWPass *eval_subsurface_ps_ = nullptr;
+ DRWPass *eval_transparency_ps_ = nullptr;
+ DRWPass *eval_holdout_ps_ = nullptr;
+ // DRWPass *eval_volume_heterogeneous_ps_ = nullptr;
+ DRWPass *eval_volume_homogeneous_ps_ = nullptr;
+
+ /* References only. */
+ GPUTexture *input_combined_tx_ = nullptr;
+ GPUTexture *input_depth_behind_tx_ = nullptr;
+ GPUTexture *input_diffuse_tx_ = nullptr;
+ GPUTexture *input_emission_data_tx_ = nullptr;
+ GPUTexture *input_hiz_front_tx_ = nullptr;
+ GPUTexture *input_hiz_back_tx_ = nullptr;
+ GPUTexture *input_reflect_color_tx_ = nullptr;
+ GPUTexture *input_reflect_normal_tx_ = nullptr;
+ GPUTexture *input_transmit_color_tx_ = nullptr;
+ GPUTexture *input_transmit_data_tx_ = nullptr;
+ GPUTexture *input_transmit_normal_tx_ = nullptr;
+ GPUTexture *input_transparency_data_tx_ = nullptr;
+ GPUTexture *input_volume_data_tx_ = nullptr;
+ // GPUTexture *input_volume_radiance_tx_ = nullptr;
+ // GPUTexture *input_volume_transmittance_tx_ = nullptr;
+
+ public:
+ DeferredPass(Instance &inst)
+ : inst_(inst), opaque_layer_(inst), refraction_layer_(inst), volumetric_layer_(inst){};
+
+ void sync(void);
+ DRWShadingGroup *material_add(::Material *material, GPUMaterial *gpumat);
+ DRWShadingGroup *prepass_add(::Material *material, GPUMaterial *gpumat);
+ void volume_add(Object *ob);
+ void render(const DRWView *drw_view,
+ GBuffer &gbuffer,
+ HiZBuffer &hiz_front,
+ HiZBuffer &hiz_back,
+ RaytraceBuffer &rtbuffer_opaque,
+ RaytraceBuffer &rtbuffer_refract,
+ GPUFrameBuffer *view_fb);
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Utility texture
+ *
+ * 64x64 2D array texture containing LUT tables and blue noises.
+ * \{ */
+
+class UtilityTexture : public Texture {
+ struct Layer {
+ float data[UTIL_TEX_SIZE * UTIL_TEX_SIZE][4];
+ };
+
+ static constexpr int lut_size = UTIL_TEX_SIZE;
+ static constexpr int lut_size_sqr = lut_size * lut_size;
+ static constexpr int layer_count = 4 + UTIL_BTDF_LAYER_COUNT;
+
+ public:
+ UtilityTexture()
+ : Texture("UtilityTx", lut_size, lut_size, layer_count, 1, GPU_RGBA16F, nullptr, true)
+ {
+#ifdef RUNTIME_LUT_CREATION
+ float *bsdf_ggx_lut = EEVEE_lut_update_ggx_brdf(lut_size);
+ float(*btdf_ggx_lut)[lut_size_sqr * 2] = (float(*)[lut_size_sqr * 2])
+ EEVEE_lut_update_ggx_btdf(lut_size, UTIL_BTDF_LAYER_COUNT);
+#else
+ const float *bsdf_ggx_lut = bsdf_split_sum_ggx;
+ const float(*btdf_ggx_lut)[lut_size_sqr * 2] = btdf_split_sum_ggx;
+#endif
+
+ Vector<Layer> data(layer_count);
+ {
+ Layer &layer = data[UTIL_BLUE_NOISE_LAYER];
+ memcpy(layer.data, blue_noise, sizeof(layer));
+ }
+ {
+ Layer &layer = data[UTIL_LTC_MAT_LAYER];
+ memcpy(layer.data, ltc_mat_ggx, sizeof(layer));
+ }
+ {
+ Layer &layer = data[UTIL_LTC_MAG_LAYER];
+ for (auto i : IndexRange(lut_size_sqr)) {
+ layer.data[i][0] = bsdf_ggx_lut[i * 2 + 0];
+ layer.data[i][1] = bsdf_ggx_lut[i * 2 + 1];
+ layer.data[i][2] = ltc_mag_ggx[i * 2 + 0];
+ layer.data[i][3] = ltc_mag_ggx[i * 2 + 1];
+ }
+ BLI_assert(UTIL_LTC_MAG_LAYER == UTIL_BSDF_LAYER);
+ }
+ {
+ Layer &layer = data[UTIL_DISK_INTEGRAL_LAYER];
+ for (auto i : IndexRange(lut_size_sqr)) {
+ layer.data[i][UTIL_DISK_INTEGRAL_COMP] = ltc_disk_integral[i];
+ }
+ }
+ {
+ for (auto layer_id : IndexRange(16)) {
+ Layer &layer = data[3 + layer_id];
+ for (auto i : IndexRange(lut_size_sqr)) {
+ layer.data[i][0] = btdf_ggx_lut[layer_id][i * 2 + 0];
+ layer.data[i][1] = btdf_ggx_lut[layer_id][i * 2 + 1];
+ }
+ }
+ }
+ GPU_texture_update_mipmap(*this, 0, GPU_DATA_FLOAT, data.data());
+ }
+
+ ~UtilityTexture(){};
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ShadingPasses
+ *
+ * \{ */
+
+/**
+ * Shading passes. Shared between views. Objects will subscribe to one of them.
+ */
+class ShadingPasses {
+ public:
+ 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 \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/eevee_shadow.cc b/source/blender/draw/engines/eevee/eevee_shadow.cc
new file mode 100644
index 00000000000..06306ff770a
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_shadow.cc
@@ -0,0 +1,1201 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * The shadow module manages shadow update tagging & shadow rendering.
+ */
+
+#include "BKE_global.h"
+#include "BLI_rect.h"
+
+#include "eevee_instance.hh"
+
+#include "draw_manager_text.h"
+
+#include <iostream>
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name Tile map
+ *
+ * \{ */
+
+void ShadowTileMap::sync_clipmap(const float3 &camera_position,
+ const float4x4 &object_mat_,
+ float near_,
+ float far_,
+ int2 new_offset,
+ int clipmap_level)
+{
+#ifdef SHADOW_DEBUG_NO_CACHING
+ set_dirty();
+#endif
+ if (is_cubeface || (level != clipmap_level) || (near != near_) || (far != far_)) {
+ set_dirty();
+ }
+ is_cubeface = false;
+ level = clipmap_level;
+ near = near_;
+ far = far_;
+ cone_direction = float3(1.0f);
+ cone_angle_cos = -2.0f;
+
+ if (grid_shift == int2(0)) {
+ /* Only replace shift if it is not already dirty. */
+ grid_shift = new_offset - grid_offset;
+ }
+ grid_offset = new_offset;
+
+ if (!equals_m4m4(object_mat.ptr(), object_mat_.ptr())) {
+ object_mat = object_mat_;
+ set_dirty();
+ }
+
+ float half_size = tilemap_coverage_get() / 2.0f;
+ float tile_size = tile_size_get();
+ float3 tilemap_center = object_mat *
+ float3(grid_offset.x * tile_size, grid_offset.y * tile_size, 0.0f);
+
+ float4x4 viewinv = object_mat;
+ copy_v3_v3(viewinv.values[3], tilemap_center);
+
+ float camera_distance_to_plane = math::dot(float3(object_mat.values[2]), camera_position);
+ float visible_near = camera_distance_to_plane - half_size;
+ float visible_far = camera_distance_to_plane + half_size;
+
+ /* Update corners. Used for visibility test of each tile. */
+ *(float3 *)(&corners[0]) = viewinv * float3(-half_size, -half_size, visible_near);
+ *(float3 *)(&corners[1]) = viewinv * float3(half_size, -half_size, visible_near);
+ *(float3 *)(&corners[2]) = viewinv * float3(-half_size, half_size, visible_near);
+ *(float3 *)(&corners[3]) = viewinv * float3(-half_size, -half_size, visible_far);
+ /* Store deltas. */
+ corners[1] = (corners[1] - corners[0]) / float(SHADOW_TILEMAP_RES);
+ corners[2] = (corners[2] - corners[0]) / float(SHADOW_TILEMAP_RES);
+ corners[3] -= corners[0];
+
+ /* Usage depth range. Used for usage tagging. */
+ float range = (far - near);
+ /* Need to be after the corners arithmetic because they are stored inside the last component. */
+ _min_usage_depth = clamp_f((2.0f * (-visible_far - near) / range) - 1.0f, -1.0f, 1.0f);
+ _max_usage_depth = clamp_f((2.0f * (-visible_near - near) / range) - 1.0f, -1.0f, 1.0f);
+
+ viewmat = viewinv.inverted_affine();
+ winmat = winmat_get(nullptr);
+ mul_m4_m4m4(tilemat, tilemat_scale_bias_mat, winmat.ptr());
+ mul_m4_m4m4(tilemat, tilemat, viewmat.ptr());
+}
+
+void ShadowTileMap::sync_cubeface(
+ const float4x4 &object_mat_, float near_, float far_, float cone_aperture, eCubeFace face)
+{
+#ifdef SHADOW_DEBUG_NO_CACHING
+ set_dirty();
+#endif
+ if (!is_cubeface || (cubeface != face) || (near != near_) || (far != far_)) {
+ set_dirty();
+ }
+ is_cubeface = true;
+ cubeface = face;
+ near = near_;
+ far = far_;
+
+ if (cone_aperture > DEG2RADF(180.0f)) {
+ cone_angle_cos = -2.0;
+ }
+ else {
+ cone_angle_cos = cosf(min_ff((cone_aperture * 0.5f) + tile_cone_half_angle, M_PI_2));
+ }
+ cone_direction = -float3(object_mat_.values[2]);
+
+ if (!equals_m4m4(object_mat.ptr(), object_mat_.ptr())) {
+ object_mat = object_mat_;
+ set_dirty();
+ }
+
+ viewmat = float4x4(shadow_face_mat[cubeface]) * object_mat.inverted_affine();
+ winmat = winmat_get(nullptr);
+ mul_m4_m4m4(tilemat, tilemat_scale_bias_mat, winmat.ptr());
+ mul_m4_m4m4(tilemat, tilemat, viewmat.ptr());
+
+ /* Update corners. */
+ float4x4 viewinv = viewmat.inverted();
+ *reinterpret_cast<float3 *>(&corners[0]) = viewinv.translation();
+ *reinterpret_cast<float3 *>(&corners[1]) = viewinv * float3(-far, -far, -far);
+ *reinterpret_cast<float3 *>(&corners[2]) = viewinv * float3(far, -far, -far);
+ *reinterpret_cast<float3 *>(&corners[3]) = viewinv * float3(-far, far, -far);
+ /* Store deltas. */
+ corners[2] = (corners[2] - corners[1]) / float(SHADOW_TILEMAP_RES);
+ corners[3] = (corners[3] - corners[1]) / float(SHADOW_TILEMAP_RES);
+ /* Need to be after the corners arithmetic because they are stored inside the last component. */
+ _min_usage_depth = -1.0f;
+ _max_usage_depth = 1.0f;
+ _punctual_distance = far_;
+}
+
+float4x4 ShadowTileMap::winmat_get(const rcti *tile_minmax) const
+{
+ float2 min = float2(-1.0f);
+ float2 max = float2(1.0f);
+
+ if (tile_minmax != nullptr) {
+ min = shadow_tile_coord_to_ndc(int2(tile_minmax->xmin, tile_minmax->ymin));
+ max = shadow_tile_coord_to_ndc(int2(tile_minmax->xmax, tile_minmax->ymax));
+ }
+
+ float4x4 winmat;
+ if (is_cubeface) {
+ perspective_m4(
+ winmat.ptr(), near * min.x, near * max.x, near * min.y, near * max.y, near, far);
+ }
+ else {
+ float half_size = tilemap_coverage_get() / 2.0f;
+ orthographic_m4(winmat.ptr(),
+ half_size * min.x,
+ half_size * max.x,
+ half_size * min.y,
+ half_size * max.y,
+ near,
+ far);
+ }
+ return winmat;
+}
+
+void ShadowTileMap::setup_view(const rcti &rect, DRWView *&view) const
+{
+ float4x4 culling_mat = winmat_get(&rect);
+
+ if (view == nullptr) {
+ view = DRW_view_create(viewmat.ptr(), winmat.ptr(), nullptr, culling_mat.ptr(), nullptr);
+ }
+ else {
+ DRW_view_update(view, viewmat.ptr(), winmat.ptr(), nullptr, culling_mat.ptr());
+ }
+
+#if 0 /* Debug. */
+ float4 debug_color[6] = {
+ {1, .1, .1, 1}, {.1, 1, .1, 1}, {0, .2, 1, 1}, {1, 1, .3, 1}, {.1, .1, .1, 1}, {1, 1, 1, 1}};
+ float4 color = debug_color[((is_cubeface ? cubeface : level) + 9999) % 6];
+ float4x4 persinv_culling = (culling_mat * viewmat).inverted();
+ DRW_debug_m4_as_bbox(persinv_culling.values, color, false);
+#endif
+}
+
+void ShadowTileMap::debug_draw(void) const
+{
+ /** Used for debug drawing. */
+ float4 debug_color[6] = {
+ {1, .1, .1, 1}, {.1, 1, .1, 1}, {0, .2, 1, 1}, {1, 1, .3, 1}, {.1, .1, .1, 1}, {1, 1, 1, 1}};
+ float4 color = debug_color[((is_cubeface ? cubeface : level) + 9999) % 6];
+
+ float4x4 winmat = winmat_get(nullptr);
+ float persinv[4][4];
+ mul_m4_m4m4(persinv, winmat.ptr(), viewmat.ptr());
+ invert_m4(persinv);
+ DRW_debug_m4_as_bbox(persinv, color, false);
+
+ int64_t div = ShadowTileAllocator::maps_per_row;
+ std::stringstream ss;
+ ss << "[" << index % div << ":" << index / div << "]";
+ std::string text = ss.str();
+
+ float3 pos = float3(0.0f, 0.0f, (is_cubeface) ? 1.0f : 0.0f);
+ mul_project_m4_v3(persinv, pos);
+
+ uchar ucolor[4];
+ rgba_float_to_uchar(ucolor, color);
+ struct DRWTextStore *dt = DRW_text_cache_ensure();
+ DRW_text_cache_add(dt, pos, text.c_str(), text.size(), 0, 0, DRW_TEXT_CACHE_GLOBALSPACE, ucolor);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Tile map allocator
+ *
+ * \{ */
+
+ShadowTileAllocator::ShadowTileAllocator()
+{
+ for (auto &bit : usage_bitmap_) {
+ bit = false;
+ }
+
+ /* Try to not have a very long texture since it is
+ * potentially wasteful on most GPU using tiled memory. */
+ int tilemap_res = ShadowTileMap::tile_map_resolution;
+ int2 extent;
+ extent.x = min_ii(size, maps_per_row) * tilemap_res;
+ extent.y = (size / maps_per_row) * tilemap_res;
+ /* Add half the height for LODs. */
+ extent.y += extent.y / 2;
+
+ tilemap_tx.ensure(UNPACK2(extent), 1, GPU_R32UI);
+ tilemap_tx.clear((uint)0);
+
+ /* Allocate one pixel for each tilemap and each lod. */
+ tilemap_rects_tx.ensure(SHADOW_TILEMAP_LOD + 1, size, 1, GPU_RGBA32I);
+ tilemap_rects_tx.clear((int)0);
+}
+
+ShadowTileAllocator::~ShadowTileAllocator()
+{
+ for (ShadowTileMap *map : maps) {
+ delete map;
+ }
+}
+
+/** Returns empty span on failure. */
+Span<ShadowTileMap *> ShadowTileAllocator::alloc(int64_t count)
+{
+ int64_t candidate = -1;
+ /* Iterate through the whole buffer starting from the last known alloc position. */
+ for (int64_t j = 0; j < size; j++) {
+ int64_t i = (next_index + j) % size;
+ if (usage_bitmap_[i] == false) {
+ if (candidate == -1) {
+ candidate = i;
+ }
+ if (i - candidate + 1 == count) {
+ int64_t start = maps.size();
+ for (auto j : IndexRange(candidate, count)) {
+ usage_bitmap_[j] = true;
+ maps.append(new ShadowTileMap(j));
+ }
+ next_index = candidate + count;
+ return maps.as_span().slice(IndexRange(start, count));
+ }
+ }
+ else {
+ candidate = -1;
+ }
+ }
+ return Span<ShadowTileMap *>();
+}
+
+void ShadowTileAllocator::free(Vector<ShadowTileMap *> &free_list)
+{
+ for (ShadowTileMap *map : free_list) {
+ maps.remove_first_occurrence_and_reorder(map);
+ usage_bitmap_[map->index] = false;
+ maps_deleted.append(map);
+ /* Actual deletion happens in end_sync(). */
+ }
+ free_list.clear();
+}
+
+void ShadowTileAllocator::end_sync()
+{
+ active_maps_len = 0;
+ for (ShadowTileMap *map : maps) {
+ tilemaps_data[active_maps_len++] = *map;
+ }
+
+ deleted_maps_len = 0;
+ for (ShadowTileMap *map : maps_deleted) {
+ /* Push to the ShadowTileMapsDataBuf in order to release the tiles.
+ * Only do that if the slot was not reused for another map. */
+ if (usage_bitmap_[map->index] == false) {
+ /* This will effectively release all pages since they will be marked to update but not
+ * marked as visible. */
+ map->set_dirty();
+ tilemaps_data[active_maps_len + deleted_maps_len++] = *map;
+ }
+ delete map;
+ }
+ maps_deleted.clear();
+
+ tilemaps_data.push_update();
+
+ for (ShadowTileMap *map : maps) {
+ map->set_updated();
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Shadow Common
+ *
+ * \{ */
+
+void ShadowCommon::free_resources()
+{
+ shadows_->tilemap_allocator.free(tilemaps);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Shadow Punctual
+ *
+ * \{ */
+
+void ShadowPunctual::sync(eLightType light_type,
+ const mat4 &object_mat,
+ float cone_aperture,
+ float near_clip,
+ float far_clip,
+ float bias)
+{
+ bool is_wide_cone = cone_aperture > DEG2RADF(90.0f);
+ bool is_omni = cone_aperture > DEG2RADF(180.0f);
+
+ far_ = max_ff(far_clip, 3e-4f);
+ near_ = min_ff(near_clip, far_clip - 1e-4f);
+ bias_ = bias;
+ light_type_ = light_type;
+
+ /* Keep custom data. */
+ size_x_ = _area_size_x;
+ size_y_ = _area_size_y;
+
+ position_ = float3(object_mat[3]);
+ random_offset_ = float3(0.0f);
+
+ int face_needed = is_omni ? 6 : (is_wide_cone ? 5 : 1);
+ if (tilemaps.size() != face_needed) {
+ shadows_->tilemap_allocator.free(tilemaps);
+ tilemaps = shadows_->tilemap_allocator.alloc(face_needed);
+ }
+
+ /* Clear embedded custom data. */
+ float4x4 obmat_tmp = float4x4(object_mat);
+ obmat_tmp.values[0][3] = obmat_tmp.values[1][3] = obmat_tmp.values[2][3] = 0.0f;
+ obmat_tmp.values[3][3] = 1.0f;
+
+ tilemaps[Z_NEG]->sync_cubeface(obmat_tmp, near_, far_, cone_aperture, Z_NEG);
+ if (is_wide_cone) {
+ tilemaps[X_POS]->sync_cubeface(obmat_tmp, near_, far_, cone_aperture, X_POS);
+ tilemaps[X_NEG]->sync_cubeface(obmat_tmp, near_, far_, cone_aperture, X_NEG);
+ tilemaps[Y_POS]->sync_cubeface(obmat_tmp, near_, far_, cone_aperture, Y_POS);
+ tilemaps[Y_NEG]->sync_cubeface(obmat_tmp, near_, far_, cone_aperture, Y_NEG);
+ }
+ if (is_omni) {
+ tilemaps[Z_POS]->sync_cubeface(obmat_tmp, near_, far_, cone_aperture, Z_POS);
+ }
+}
+
+ShadowPunctual::operator ShadowData()
+{
+ ShadowData data;
+ cubeface_winmat_get(data.mat, near_, far_);
+ invert_m4(data.mat);
+ data.offset = random_offset_;
+ data.bias = bias_;
+ data.clip_near = near_;
+ data.clip_far = far_;
+ data.tilemap_index = tilemaps.first()->index;
+ data.tilemap_last = data.tilemap_index + tilemaps.size() - 1;
+ return data;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Directional Shadow Maps
+ *
+ * \{ */
+
+void ShadowDirectional::sync(const mat4 &object_mat, float bias, float min_resolution)
+{
+ object_mat_ = float4x4(object_mat);
+ /* Clear embedded custom data. */
+ object_mat_.values[0][3] = object_mat_.values[1][3] = object_mat_.values[2][3] = 0.0f;
+ object_mat_.values[3][3] = 1.0f;
+ /* Remove translation. */
+ zero_v3(object_mat_.values[3]);
+
+ min_resolution_ = min_resolution;
+ bias_ = bias;
+}
+
+void ShadowDirectional::end_sync(int min_level,
+ int max_level,
+ const float3 &camera_position,
+ const AABB &casters_bounds)
+{
+ int user_min_level = floorf(log2(min_resolution_));
+
+ /* FIXME(fclem): We center the clipmap around the camera position which is arbitrary and
+ * can affect lightprobe shadowing quality. To fix, just change camera position during bake and
+ * profit!!! */
+
+ float3 z_axis = float3(object_mat_.values[2]);
+ /* Near & far values used for rendering. Bounds the shadow casters. */
+ near_ = 1.0e30f;
+ far_ = -1.0e30f;
+ BoundBox bbox = casters_bounds;
+ for (auto i : IndexRange(8)) {
+ float dist = -math::dot(z_axis, float3(bbox.vec[i]));
+ near_ = min_ff(near_, dist);
+ far_ = max_ff(far_, dist);
+ }
+ near_ -= 1e-8f;
+ far_ += 1e-8f;
+
+ min_level = clamp_i(user_min_level, min_level, max_level);
+ int level_count = max_level - min_level + 1;
+ /* The maximum level count is bounded by the mantissa of a 32bit float. */
+ if (level_count > 23) {
+ level_count = 23;
+ min_level = max_level - level_count + 1;
+ }
+
+ if (tilemaps.size() != level_count) {
+ shadows_->tilemap_allocator.free(tilemaps);
+ tilemaps = shadows_->tilemap_allocator.alloc(level_count);
+ }
+ ShadowTileMap &first_clipmap = *tilemaps.first();
+ /* Meh... in order to make tile_size_get() work properly. */
+ first_clipmap.set_level(min_level);
+ first_clipmap.set_is_cubemap(false);
+
+ /* Compute full offset from origin to the smallest clipmap tile size. */
+ float tile_size = first_clipmap.tile_size_get();
+ base_offset_ = int2(
+ roundf(math::dot(float3(object_mat_.values[0]), camera_position) / tile_size),
+ roundf(math::dot(float3(object_mat_.values[1]), camera_position) / tile_size));
+
+ int level = min_level;
+ int divisor = 1;
+ for (ShadowTileMap *tilemap : tilemaps) {
+ tilemap->sync_clipmap(
+ camera_position, object_mat_, near_, far_, base_offset_ / divisor, level++);
+ divisor <<= 1;
+ }
+ divisor >>= 1;
+ /* Save only the offset from the first clipmap level to the last. */
+ base_offset_ = base_offset_ - (base_offset_ / divisor) * divisor;
+}
+
+ShadowDirectional::operator ShadowData()
+{
+ ShadowData data;
+ ShadowTileMap &last_level = *tilemaps.last();
+ mul_m4_m4m4(data.mat, shadow_clipmap_scale_mat, last_level.winmat.ptr());
+ mul_m4_m4m4(data.mat, data.mat, last_level.viewmat.ptr());
+ data.bias = bias_;
+ data.clip_near = near_;
+ data.clip_far = far_;
+ data.base_offset = base_offset_;
+ data.tilemap_index = tilemaps.first()->index;
+ data.tilemap_last = data.tilemap_index + tilemaps.size() - 1;
+ data.clipmap_lod_min = min_resolution_;
+ data.clipmap_lod_max = min_resolution_ + tilemaps.size() - 1;
+ return data;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Shadow Module
+ *
+ * \{ */
+
+void ShadowModule::init(void)
+{
+ /* TODO(fclem) New resolution parameter. */
+ // if (cube_shadow_res_ != inst_.scene->eevee.shadow_cube_size) {
+ // inst_.sampling.reset();
+ // }
+
+ eGPUTextureFormat shadow_format = (inst_.scene->eevee.flag & SCE_EEVEE_SHADOW_HIGH_BITDEPTH) ?
+ GPU_DEPTH_COMPONENT32F :
+ GPU_DEPTH_COMPONENT16;
+
+ if (shadow_format_ != shadow_format) {
+ shadow_format_ = shadow_format;
+ inst_.sampling.reset();
+
+ int2 atlas_extent = int2(shadow_page_size_ * SHADOW_PAGE_PER_ROW);
+ int2 render_extent = int2(shadow_page_size_ * SHADOW_TILEMAP_RES);
+ GPUTexture *tex = atlas_tx_;
+
+ /* Global update. */
+ if ((tex == nullptr) || GPU_texture_format(atlas_tx_) != shadow_format_ ||
+ GPU_texture_width(atlas_tx_) != atlas_extent.x ||
+ GPU_texture_height(atlas_tx_) != atlas_extent.y) {
+ for (ShadowTileMap *tilemap : tilemap_allocator.maps) {
+ tilemap->set_dirty();
+ }
+ }
+
+ /* TODO(fclem) GPU_DEPTH_COMPONENT16 support in copy shader? */
+ /* TODO(fclem) Make allocation safe. */
+ atlas_tx_.ensure(UNPACK2(atlas_extent), 1, GPU_R32F);
+ atlas_tx_.filter_mode(false);
+#if DEBUG
+ atlas_tx_.clear(0.0f);
+#endif
+ /* Temporary render buffer. */
+ render_tx_.ensure(UNPACK2(render_extent), 1, GPU_DEPTH_COMPONENT32F);
+ render_fb_.ensure(GPU_ATTACHMENT_TEXTURE(render_tx_));
+ }
+
+ const bool soft_shadow_enabled = (inst_.scene->eevee.flag & SCE_EEVEE_SHADOW_SOFT) != 0;
+ if (soft_shadows_enabled_ != soft_shadow_enabled) {
+ soft_shadows_enabled_ = soft_shadow_enabled;
+ inst_.sampling.reset();
+ }
+
+#ifndef SHADOW_DEBUG_PAGE_ALLOCATION_ENABLED
+ if (inst_.debug_mode == SHADOW_DEBUG_PAGE_ALLOCATION) {
+ BLI_assert_msg(0,
+ "Error: EEVEE: SHADOW_DEBUG_PAGE_ALLOCATION used but "
+ "SHADOW_DEBUG_PAGE_ALLOCATION_ENABLED "
+ "is not defined");
+ }
+#endif
+
+ tilemap_pixel_radius_ = M_SQRT2 * 2.0f / (SHADOW_TILEMAP_RES * shadow_page_size_);
+
+ debug_data_.type = inst_.debug_mode;
+}
+
+void ShadowModule::begin_sync(void)
+{
+ casters_bounds_.init_min_max();
+ receivers_non_opaque_ = DRW_call_buffer_create(&aabb_format_);
+ casters_updated_ = DRW_call_buffer_create(&aabb_format_);
+
+ for (int i = 0; i < ARRAY_SIZE(views_); i++) {
+ views_[i] = nullptr;
+ }
+}
+
+void ShadowModule::sync_object(Object *ob,
+ const ObjectHandle &handle,
+ bool is_shadow_caster,
+ bool is_alpha_blend)
+{
+#ifdef SHADOW_DEBUG_NO_DEPTH_SCAN
+ is_alpha_blend = true;
+#endif
+ if (!is_shadow_caster && !is_alpha_blend) {
+ return;
+ }
+
+ ShadowObject &shadow_ob = objects_.lookup_or_add_default(handle.object_key);
+ shadow_ob.used = true;
+ if (handle.recalc != 0 || !shadow_ob.initialized) {
+ if (is_shadow_caster && shadow_ob.initialized) {
+ DRW_buffer_add_entry_struct(casters_updated_, &shadow_ob.aabb);
+ }
+ shadow_ob.sync(ob);
+ if (is_shadow_caster) {
+ DRW_buffer_add_entry_struct(casters_updated_, &shadow_ob.aabb);
+ }
+ }
+
+ if (is_shadow_caster) {
+ casters_bounds_.merge(shadow_ob.aabb);
+ }
+
+ if (is_alpha_blend) {
+ printf("receivers_non_opaque_\n");
+ DRW_buffer_add_entry_struct(receivers_non_opaque_, &shadow_ob.aabb);
+ }
+}
+
+void ShadowModule::end_sync(void)
+{
+ /* Search for deleted or updated shadow casters */
+ Vector<ObjectKey, 0> deleted_keys;
+ for (auto item : objects_.items()) {
+ ShadowObject &shadow_ob = item.value;
+ if (!shadow_ob.used) {
+ deleted_keys.append(item.key);
+ /* May not be a caster, but it does not matter, be conservative. */
+ DRW_buffer_add_entry_struct(casters_updated_, &shadow_ob.aabb);
+ }
+ else {
+ /* Clear for next sync. */
+ shadow_ob.used = false;
+ }
+ }
+ for (auto key : deleted_keys) {
+ objects_.remove(key);
+ }
+ if (deleted_keys.size() > 0) {
+ inst_.sampling.reset();
+ }
+
+ /* WARNING: Fragile, use same value as INIT_MINMAX. */
+ bool no_casters = (casters_bounds_.min.x == 1.0e-30f);
+ if (no_casters) {
+ /* Avoid problems down the road. */
+ casters_bounds_ = AABB(1.0f);
+ }
+
+ /* Finish setting up the tilemaps. */
+ punctuals.resize();
+ directionals.resize();
+
+ {
+ /* Get the farthest point from camera to know what distance to cover. */
+ float3 farthest_point = float3(1.0f, 1.0f, 1.0f);
+ mul_project_m4_v3(inst_.camera.data_get().wininv, farthest_point);
+ float far_dist = math::length(farthest_point);
+ float near_dist = fabsf(inst_.camera.data_get().clip_near);
+ float3 cam_position = inst_.camera.position();
+
+#ifdef SHADOW_DEBUG_FREEZE_CAMERA
+ static bool valid = false;
+ static float far_dist_freezed;
+ static float near_dist_freezed;
+ static float3 cam_position_freezed;
+ if (G.debug_value < 4 || !valid) {
+ valid = true;
+ far_dist_freezed = far_dist;
+ near_dist_freezed = near_dist;
+ cam_position_freezed = cam_position;
+ debug_data_.camera_position = cam_position;
+ }
+ else {
+ far_dist = far_dist_freezed;
+ near_dist = near_dist_freezed;
+ cam_position = cam_position_freezed;
+ debug_data_.camera_position = cam_position_freezed;
+ }
+#endif
+
+ int min_level, max_level;
+ if (false /* is_ortho_camera */) {
+ /* TODO(fclem): To have the best resolution we need to find the smallest
+ * Clipmap that covers the intersection of the Camera Frustum with casters_bounds_.
+ * cam_position should be the center of this intersection. */
+ }
+ else {
+ max_level = ceil(log2(far_dist));
+ min_level = floor(log2(near_dist));
+ }
+
+ for (ShadowDirectional &directional : directionals) {
+ directional.end_sync(min_level, max_level, cam_position, casters_bounds_);
+ }
+ }
+
+ tilemap_allocator.end_sync();
+
+ last_processed_view = nullptr;
+
+ /**
+ * Tilemap Management
+ */
+
+ int64_t tilemaps_len = tilemap_allocator.active_maps_len;
+ {
+ tilemap_setup_ps_ = DRW_pass_create("ShadowTilemapSetup", (DRWState)0);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_TILE_SETUP);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, tilemap_setup_ps_);
+ DRW_shgroup_vertex_buffer(grp, "pages_infos_buf", pages_infos_data_);
+ DRW_shgroup_vertex_buffer(grp, "pages_free_buf", pages_free_data_);
+ DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data);
+ DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx);
+ DRW_shgroup_uniform_bool(grp, "do_tilemap_setup", &do_tilemap_setup_, 1);
+ int64_t tilemaps_updated_len = tilemaps_len + tilemap_allocator.deleted_maps_len;
+ if (tilemaps_updated_len > 0) {
+ DRW_shgroup_call_compute(grp, 1, 1, tilemaps_updated_len);
+ DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_SHADER_STORAGE);
+ }
+ do_tilemap_setup_ = true;
+
+ if (G.debug & G_DEBUG_GPU) {
+ debug_page_map_call(tilemap_setup_ps_);
+ }
+ }
+ {
+ tilemap_visibility_ps_ = DRW_pass_create("ShadowVisibilityTag", (DRWState)0);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_TILE_TAG_VISIBILITY);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, tilemap_visibility_ps_);
+ DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data);
+ DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx);
+ DRW_shgroup_uniform_float(grp, "tilemap_pixel_radius", &tilemap_pixel_radius_, 1);
+ DRW_shgroup_uniform_float(grp, "screen_pixel_radius_inv", &screen_pixel_radius_inv_, 1);
+ if (tilemaps_len > 0) {
+ DRW_shgroup_call_compute(grp, 1, 1, tilemaps_len);
+ DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS);
+ }
+ }
+ {
+ tilemap_usage_tag_ps_ = DRW_pass_create("ShadowUsageTag", (DRWState)0);
+
+ GPUVertBuf *receivers_aabbs = DRW_call_buffer_as_vertbuf(receivers_non_opaque_);
+ uint aabb_len = GPU_vertbuf_get_vertex_len(receivers_aabbs);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_TILE_TAG_USAGE);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, tilemap_usage_tag_ps_);
+ DRW_shgroup_vertex_buffer(grp, "aabb_buf", receivers_aabbs);
+ DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data);
+ DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx);
+ DRW_shgroup_uniform_float(grp, "tilemap_pixel_radius", &tilemap_pixel_radius_, 1);
+ DRW_shgroup_uniform_float(grp, "screen_pixel_radius_inv", &screen_pixel_radius_inv_, 1);
+ DRW_shgroup_uniform_int_copy(grp, "aabb_len", aabb_len);
+ if (tilemaps_len > 0 && aabb_len > 0) {
+ uint group_len = divide_ceil_u(aabb_len, SHADOW_AABB_TAG_GROUP_SIZE);
+ DRW_shgroup_call_compute(grp, group_len, 1, tilemaps_len);
+ DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS);
+ }
+ }
+ {
+ tilemap_depth_scan_ps_ = DRW_pass_create("ShadowDepthScan", (DRWState)0);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_TILE_DEPTH_SCAN);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, tilemap_depth_scan_ps_);
+ DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
+ DRW_shgroup_vertex_buffer_ref(grp, "lights_buf", &inst_.lights.culling_light_buf);
+ DRW_shgroup_vertex_buffer_ref(grp, "lights_culling_buf", &inst_.lights.culling_data);
+ DRW_shgroup_vertex_buffer_ref(grp, "lights_zbins_buf", &inst_.lights.culling_zbin_buf);
+ DRW_shgroup_vertex_buffer_ref(grp, "lights_tile_buf", &inst_.lights.culling_tile_buf);
+ DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx);
+ DRW_shgroup_uniform_float(grp, "tilemap_pixel_radius", &tilemap_pixel_radius_, 1);
+ DRW_shgroup_uniform_float(grp, "screen_pixel_radius_inv", &screen_pixel_radius_inv_, 1);
+ DRW_shgroup_call_compute_ref(grp, scan_dispatch_size_);
+ DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS);
+ }
+ {
+ tilemap_update_tag_ps_ = DRW_pass_create("ShadowUpdateTag", (DRWState)0);
+
+ GPUVertBuf *casters_aabbs = DRW_call_buffer_as_vertbuf(casters_updated_);
+ uint aabb_len = GPU_vertbuf_get_vertex_len(casters_aabbs);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_TILE_TAG_UPDATE);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, tilemap_update_tag_ps_);
+ DRW_shgroup_vertex_buffer(grp, "aabb_buf", casters_aabbs);
+ DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data);
+ DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx);
+ DRW_shgroup_uniform_int_copy(grp, "aabb_len", GPU_vertbuf_get_vertex_len(casters_aabbs));
+ if (tilemaps_len > 0 && aabb_len > 0) {
+ uint group_len = divide_ceil_u(aabb_len, SHADOW_AABB_TAG_GROUP_SIZE);
+ DRW_shgroup_call_compute(grp, group_len, 1, tilemaps_len);
+ DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS);
+ }
+ }
+ {
+ tilemap_lod_mask_ps_ = DRW_pass_create("ShadowLodMaskTag", (DRWState)0);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_TILE_LOD_MASK);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, tilemap_lod_mask_ps_);
+ DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data);
+ DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx);
+ if (tilemaps_len > 0) {
+ DRW_shgroup_call_compute(grp, 1, 1, tilemaps_len);
+ DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS);
+ }
+ }
+
+ /**
+ * Page Management
+ */
+
+ {
+ page_init_ps_ = DRW_pass_create("ShadowPageInit", (DRWState)0);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_PAGE_INIT);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, page_init_ps_);
+ DRW_shgroup_vertex_buffer(grp, "pages_infos_buf", pages_infos_data_);
+ DRW_shgroup_vertex_buffer(grp, "pages_free_buf", pages_free_data_);
+ DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx);
+ DRW_shgroup_call_compute(grp, SHADOW_MAX_PAGE / SHADOW_PAGE_PER_ROW, 1, 1);
+ DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_SHADER_STORAGE);
+
+ if (G.debug & G_DEBUG_GPU) {
+ debug_page_map_call(page_init_ps_);
+ }
+ }
+ {
+ page_free_ps_ = DRW_pass_create("ShadowPageFree", (DRWState)0);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_PAGE_FREE);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, page_free_ps_);
+ DRW_shgroup_vertex_buffer(grp, "pages_infos_buf", pages_infos_data_);
+ DRW_shgroup_vertex_buffer(grp, "pages_free_buf", pages_free_data_);
+ DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data);
+ DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx);
+ int64_t tilemaps_updated_len = tilemaps_len + tilemap_allocator.deleted_maps_len;
+ if (tilemaps_updated_len > 0) {
+ DRW_shgroup_call_compute(grp, 1, 1, tilemaps_updated_len);
+ DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_SHADER_STORAGE);
+ }
+
+ if (G.debug & G_DEBUG_GPU) {
+ debug_page_map_call(page_free_ps_);
+ }
+ }
+ {
+ page_defrag_ps_ = DRW_pass_create("ShadowPageDefrag", (DRWState)0);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_PAGE_DEFRAG);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, page_defrag_ps_);
+ DRW_shgroup_vertex_buffer(grp, "pages_free_buf", pages_free_data_);
+ DRW_shgroup_vertex_buffer(grp, "pages_infos_buf", pages_infos_data_);
+ DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx);
+ DRW_shgroup_call_compute(grp, 1, 1, 1);
+ DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_SHADER_STORAGE);
+ }
+ {
+ page_alloc_ps_ = DRW_pass_create("ShadowPageAllocate", (DRWState)0);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_PAGE_ALLOC);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, page_alloc_ps_);
+ DRW_shgroup_vertex_buffer(grp, "pages_infos_buf", pages_infos_data_);
+ DRW_shgroup_vertex_buffer(grp, "pages_free_buf", pages_free_data_);
+ DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data);
+ DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx);
+ DRW_shgroup_uniform_image(grp, "tilemap_rects_img", tilemap_allocator.tilemap_rects_tx);
+ if (tilemaps_len > 0) {
+ DRW_shgroup_call_compute(grp, 1, 1, tilemaps_len);
+ eGPUBarrier barrier = GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_SHADER_STORAGE;
+ barrier |= GPU_BARRIER_TEXTURE_FETCH; /* Needed for ShadowPageMark / ShadowPageCopy. */
+ barrier |= GPU_BARRIER_TEXTURE_UPDATE; /* Needed for readback. */
+ DRW_shgroup_barrier(grp, barrier);
+ }
+
+ if (G.debug & G_DEBUG_GPU) {
+ debug_page_map_call(page_alloc_ps_);
+ }
+ }
+ {
+ DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS;
+ page_mark_ps_ = DRW_pass_create("ShadowPageMark", state);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_PAGE_MARK);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, page_mark_ps_);
+ DRW_shgroup_uniform_texture(grp, "tilemaps_tx", tilemap_allocator.tilemap_tx);
+ DRW_shgroup_uniform_int(grp, "tilemap_lod", &rendering_lod_, 1);
+ DRW_shgroup_uniform_int(grp, "tilemap_index", &rendering_tilemap_, 1);
+ DRW_shgroup_clear_framebuffer(grp, GPU_DEPTH_BIT, 0, 0, 0, 0, 0.0f, 0x0);
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, square_i(SHADOW_TILEMAP_RES) * 2);
+ }
+ {
+ page_copy_ps_ = DRW_pass_create("ShadowPageCopy", (DRWState)0);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_PAGE_COPY);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, page_copy_ps_);
+ DRW_shgroup_uniform_texture(grp, "tilemaps_tx", tilemap_allocator.tilemap_tx);
+ DRW_shgroup_uniform_texture(grp, "render_tx", render_tx_);
+ DRW_shgroup_uniform_image(grp, "out_atlas_img", atlas_tx_);
+ DRW_shgroup_uniform_int(grp, "tilemap_lod", &rendering_lod_, 1);
+ DRW_shgroup_uniform_int(grp, "tilemap_index", &rendering_tilemap_, 1);
+ DRW_shgroup_call_compute_ref(grp, copy_dispatch_size_);
+ DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH);
+ }
+
+ debug_end_sync();
+}
+
+void ShadowModule::debug_page_map_call(DRWPass *pass)
+{
+ if (debug_data_.type == SHADOW_DEBUG_NONE) {
+ return;
+ }
+ debug_page_tx_.ensure(SHADOW_PAGE_PER_ROW, SHADOW_PAGE_PER_ROW, 0, GPU_R32UI);
+
+ GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_PAGE_DEBUG);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, pass);
+ DRW_shgroup_vertex_buffer(grp, "pages_free_buf", pages_free_data_);
+ DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx);
+ DRW_shgroup_uniform_image(grp, "debug_img", debug_page_tx_);
+ DRW_shgroup_call_compute(grp, 1, 1, 1);
+ DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_TEXTURE_FETCH);
+}
+
+void ShadowModule::debug_end_sync(void)
+{
+ debug_draw_ps_ = nullptr;
+
+ if (debug_data_.type == SHADOW_DEBUG_NONE) {
+ return;
+ }
+
+ const bool need_active_light_data = !ELEM(
+ debug_data_.type, SHADOW_DEBUG_TILE_ALLOCATION, SHADOW_DEBUG_PAGE_ALLOCATION);
+
+ if (need_active_light_data) {
+ Object *obact = DRW_context_state_get()->obact;
+ if (obact && (obact->type == OB_LAMP)) {
+ /* Dangerous. But only used for debug. */
+ debug_light_key = inst_.sync.sync_object(obact).object_key;
+ }
+
+ if (debug_light_key.ob == nullptr) {
+ return;
+ }
+
+ LightModule &light_module = inst_.lights;
+ if (light_module.lights_.contains(debug_light_key) == false) {
+ return;
+ }
+ Light &light = light_module.lights_.lookup(debug_light_key);
+ if (light.shadow_id == LIGHT_NO_SHADOW) {
+ return;
+ }
+
+ debug_data_.light = light;
+ if (light.type == LIGHT_SUN) {
+ debug_data_.shadow = directionals[light.shadow_id];
+ }
+ else {
+ debug_data_.shadow = punctuals[light.shadow_id];
+ }
+ /* Find index of tilemap data. */
+ for (auto index : IndexRange(tilemap_allocator.size)) {
+ if (debug_data_.shadow.tilemap_index == tilemap_allocator.tilemaps_data[index].index) {
+ debug_data_.tilemap_data_index = index;
+ break;
+ }
+ }
+ }
+
+ debug_data_.push_update();
+
+ {
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL |
+ DRW_STATE_BLEND_CUSTOM;
+ debug_draw_ps_ = DRW_pass_create("ShadowDebugDraw", state);
+
+ if (debug_data_.type == SHADOW_DEBUG_PAGE_ALLOCATION) {
+ debug_page_map_call(debug_draw_ps_);
+ }
+
+ GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_DEBUG);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, debug_draw_ps_);
+ DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data);
+ DRW_shgroup_uniform_texture(grp, "tilemaps_tx", tilemap_allocator.tilemap_tx);
+ DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
+ DRW_shgroup_uniform_texture(grp, "atlas_tx", atlas_tx_);
+ DRW_shgroup_uniform_block(grp, "debug_block", debug_data_.ubo_get());
+ if (debug_data_.type == SHADOW_DEBUG_PAGE_ALLOCATION) {
+ DRW_shgroup_uniform_texture(grp, "debug_page_tx", debug_page_tx_);
+ }
+ else {
+ DRW_shgroup_uniform_texture(grp, "debug_page_tx", tilemap_allocator.tilemap_tx);
+ }
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+ }
+}
+
+/* Update all shadow regions visible inside the view.
+ * If called multiple time for the same view, it will only do the depth buffer scanning
+ * to check any new opaque surfaces.
+ * Needs to be called after LightModule::set_view(); */
+void ShadowModule::set_view(const DRWView *view, GPUTexture *depth_tx)
+{
+ /* Only process each view once. */
+ bool do_process_view = (view != last_processed_view);
+ last_processed_view = view;
+
+#if 0 /* TODO */
+ bool force_update = false;
+ if (soft_shadows_enabled_ && (inst_.sampling.sample_get() != last_sample_)) {
+ force_update = true;
+ last_sample_ = inst_.sampling.sample_get();
+ }
+ else {
+ last_sample_ = 0;
+ }
+#endif
+
+ ivec2 extent(GPU_texture_width(depth_tx), GPU_texture_height(depth_tx));
+ input_depth_tx_ = depth_tx;
+
+ scan_dispatch_size_.x = divide_ceil_u(extent.x, SHADOW_DEPTH_SCAN_GROUP_SIZE);
+ scan_dispatch_size_.y = divide_ceil_u(extent.y, SHADOW_DEPTH_SCAN_GROUP_SIZE);
+ scan_dispatch_size_.z = 1;
+
+ DRW_view_set_active(view);
+
+ float4x4 wininv;
+ DRW_view_winmat_get(view, wininv.values, true);
+
+ {
+ /* Compute approximate screen pixel density (as world space radius). */
+ float min_dim = float(min_ii(extent.x, extent.y));
+ float3 p0 = float3(-1.0f, -1.0f, 0.0f);
+ float3 p1 = float3(
+ (min_dim / extent.x) * 2.0f - 1.0f, (min_dim / extent.y) * 2.0f - 1.0f, 0.0f);
+ mul_project_m4_v3(wininv.values, p0);
+ mul_project_m4_v3(wininv.values, p1);
+ /* Compute radius at unit plane from the camera. */
+ if (DRW_view_is_persp_get(view)) {
+ p0 = p0 / p0.z;
+ p1 = p1 / p1.z;
+ }
+ screen_pixel_radius_inv_ = min_dim / math::distance(p0, p1);
+ }
+
+#ifdef SHADOW_DEBUG_FREEZE_CAMERA
+ static bool valid = false;
+ static float4x4 viewmat_freezed;
+ if (G.debug_value < 4 || !valid) {
+ valid = true;
+ DRW_view_viewmat_get(view, viewmat_freezed.values, false);
+ }
+ else {
+ float4x4 winmat;
+ DRW_view_winmat_get(view, winmat.values, false);
+ DRWView *debug_view = DRW_view_create(
+ viewmat_freezed.values, winmat.values, nullptr, nullptr, nullptr);
+ DRW_view_set_active(debug_view);
+
+ float4 color(1.0f);
+ float4x4 persinv;
+ DRW_view_persmat_get(debug_view, persinv.values, true);
+ DRW_debug_m4_as_bbox(persinv.values, color, false);
+ }
+#endif
+
+ DRW_stats_group_start("ShadowUpdate");
+ {
+ if (do_tilemap_setup_) {
+ if (do_page_init_) {
+#ifndef SHADOW_DEBUG_NO_CACHING
+ do_page_init_ = false;
+#endif
+ tilemap_allocator.tilemap_tx.clear((uint)0);
+ DRW_draw_pass(page_init_ps_);
+ }
+ }
+ /* Run every every time but only process tilemap update once. */
+ DRW_draw_pass(tilemap_setup_ps_);
+ if (do_tilemap_setup_) {
+ DRW_draw_pass(tilemap_update_tag_ps_);
+ do_tilemap_setup_ = false;
+ }
+ if (do_process_view) {
+ DRW_draw_pass(tilemap_visibility_ps_);
+ DRW_draw_pass(tilemap_usage_tag_ps_);
+ }
+#ifndef SHADOW_DEBUG_NO_DEPTH_SCAN
+ if (input_depth_tx_ != nullptr) {
+ DRW_draw_pass(tilemap_depth_scan_ps_);
+ }
+#endif
+ DRW_draw_pass(tilemap_lod_mask_ps_);
+ DRW_draw_pass(page_free_ps_);
+ DRW_draw_pass(page_defrag_ps_);
+ DRW_draw_pass(page_alloc_ps_);
+ }
+ DRW_stats_group_end();
+
+ DRW_stats_group_start("ShadowRender");
+ {
+ /* Readback update list. Ugly sync point. */
+ rcti *rect = tilemap_allocator.tilemap_rects_tx.read<rcti>(GPU_DATA_INT);
+
+ Span<rcti> regions(rect, tilemap_allocator.maps.size() * (SHADOW_TILEMAP_LOD + 1));
+ Span<ShadowTileMap *> tilemaps = tilemap_allocator.maps.as_span();
+
+ int rect_idx = 0;
+ while (!regions.is_empty()) {
+ Vector<int, 6> regions_index;
+ Vector<int, 6> regions_lod;
+
+ /* Group updates by pack of 6. This is to workaround the current DRWView limitation.
+ * Future goal is to have GPU culling and create the views on GPU. */
+ while (!regions.is_empty() && regions_index.size() < 6) {
+ int lod = rect_idx % (SHADOW_TILEMAP_LOD + 1);
+ ShadowTileMap &tilemap = *tilemaps.first();
+ if (!tilemap.is_cubeface && lod > 0) {
+ /* Do not process lod for clipmaps as they are undefined. */
+ }
+ else if (!BLI_rcti_is_empty(&regions.first())) {
+ tilemap.setup_view(regions.first(), views_[regions_index.size()]);
+ regions_index.append(tilemap.index);
+ regions_lod.append(lod);
+ }
+ rect_idx++;
+ regions = regions.drop_front(1);
+ if (lod == SHADOW_TILEMAP_LOD) {
+ tilemaps = tilemaps.drop_front(1);
+ }
+ }
+
+ for (auto i : regions_index.index_range()) {
+ rendering_tilemap_ = regions_index[i];
+ rendering_lod_ = regions_lod[i];
+
+ DRW_view_set_active(views_[i]);
+ GPU_framebuffer_bind(render_fb_);
+
+ copy_dispatch_size_.x = shadow_page_size_ / SHADOW_PAGE_COPY_GROUP_SIZE;
+ copy_dispatch_size_.y = copy_dispatch_size_.x;
+ copy_dispatch_size_.z = 1;
+
+ int viewport_size = render_tx_.width() >> rendering_lod_;
+ GPU_framebuffer_viewport_set(render_fb_, 0, 0, viewport_size, viewport_size);
+
+ DRW_draw_pass(page_mark_ps_);
+ inst_.shading_passes.shadow.render();
+ DRW_draw_pass(page_copy_ps_);
+ }
+ }
+
+ MEM_freeN(rect);
+ }
+ DRW_stats_group_end();
+
+ DRW_view_set_active(view);
+}
+
+void ShadowModule::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_);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Shadow Pass
+ *
+ * \{ */
+
+void ShadowPass::sync(void)
+{
+ {
+ DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_SHADOW_OFFSET;
+ surface_ps_ = DRW_pass_create("ShadowSurface", state);
+ }
+}
+
+DRWShadingGroup *ShadowPass::material_add(::Material *UNUSED(material), GPUMaterial *gpumat)
+{
+ DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, surface_ps_);
+ DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get());
+ return grp;
+}
+
+void ShadowPass::render(void)
+{
+ DRW_draw_pass(surface_ps_);
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_shadow.hh b/source/blender/draw/engines/eevee/eevee_shadow.hh
new file mode 100644
index 00000000000..a7db2f6dc0e
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_shadow.hh
@@ -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;
+ vec4 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 * vec3(1, 1, 1);
+ *reinterpret_cast<float3 *>(bb.vec[1]) = middle + halfdim * vec3(-1, 1, 1);
+ *reinterpret_cast<float3 *>(bb.vec[2]) = middle + halfdim * vec3(-1, -1, 1);
+ *reinterpret_cast<float3 *>(bb.vec[3]) = middle + halfdim * vec3(1, -1, 1);
+ *reinterpret_cast<float3 *>(bb.vec[4]) = middle + halfdim * vec3(1, 1, -1);
+ *reinterpret_cast<float3 *>(bb.vec[5]) = middle + halfdim * vec3(-1, 1, -1);
+ *reinterpret_cast<float3 *>(bb.vec[6]) = middle + halfdim * vec3(-1, -1, -1);
+ *reinterpret_cast<float3 *>(bb.vec[7]) = middle + halfdim * vec3(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 constexpr float tile_cone_half_angle = atan(0.5 * M_SQRT2 / (SHADOW_TILEMAP_RES / 2));
+
+ /** 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. */
+ vec3 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 mat4 &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. */
+ ivec2 base_offset_;
+ /** Copy of object matrix. */
+ float4x4 object_mat_;
+
+ public:
+ ShadowDirectional(ShadowModule *shadows) : ShadowCommon(shadows){};
+
+ void sync(const mat4 &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 = Texture("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 = Texture("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.
+ */
+ eevee::Texture render_tx_ = Texture("shadow_target_tx_");
+ eevee::Framebuffer render_fb_ = Framebuffer("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
+ * \{ */
+
+ eevee::Texture atlas_tx_ = Texture("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_ = Texture("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
diff --git a/source/blender/draw/engines/eevee/eevee_shadows.c b/source/blender/draw/engines/eevee/eevee_shadows.c
deleted file mode 100644
index d8a40fd13cc..00000000000
--- a/source/blender/draw/engines/eevee/eevee_shadows.c
+++ /dev/null
@@ -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);
- }
-}
-
-/** \} */
diff --git a/source/blender/draw/engines/eevee/eevee_shadows_cascade.c b/source/blender/draw/engines/eevee/eevee_shadows_cascade.c
deleted file mode 100644
index 22ee821933c..00000000000
--- a/source/blender/draw/engines/eevee/eevee_shadows_cascade.c
+++ /dev/null
@@ -1,437 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Copyright 2019, Blender Foundation.
- */
-
-/** \file
- * \ingroup EEVEE
- */
-
-#include "BLI_rect.h"
-#include "BLI_sys_types.h" /* bool */
-
-#include "BKE_object.h"
-
-#include "eevee_private.h"
-
-#include "BLI_rand.h" /* needs to be after for some reason. */
-
-void EEVEE_shadows_cascade_add(EEVEE_LightsInfo *linfo, EEVEE_Light *evli, Object *ob)
-{
- if (linfo->cascade_len >= MAX_SHADOW_CASCADE) {
- return;
- }
-
- const Light *la = (Light *)ob->data;
- EEVEE_Shadow *sh_data = linfo->shadow_data + linfo->shadow_len;
- EEVEE_ShadowCascade *csm_data = linfo->shadow_cascade_data + linfo->cascade_len;
- EEVEE_ShadowCascadeRender *csm_render = linfo->shadow_cascade_render + linfo->cascade_len;
-
- eevee_contact_shadow_setup(la, sh_data);
-
- linfo->shadow_cascade_light_indices[linfo->cascade_len] = linfo->num_light;
- evli->shadow_id = linfo->shadow_len++;
- sh_data->type_data_id = linfo->cascade_len++;
- csm_data->tex_id = linfo->num_cascade_layer;
- csm_render->cascade_fade = la->cascade_fade;
- csm_render->cascade_count = la->cascade_count;
- csm_render->cascade_exponent = la->cascade_exponent;
- csm_render->cascade_max_dist = la->cascade_max_dist;
- csm_render->original_bias = max_ff(la->bias, 0.0f);
-
- linfo->num_cascade_layer += la->cascade_count;
-}
-
-static void shadow_cascade_random_matrix_set(float mat[4][4], float radius, int sample_ofs)
-{
- float jitter[3];
-#ifndef DEBUG_SHADOW_DISTRIBUTION
- EEVEE_sample_ellipse(sample_ofs, mat[0], mat[1], radius, radius, jitter);
-#else
- for (int i = 0; i <= sample_ofs; i++) {
- EEVEE_sample_ellipse(i, mat[0], mat[1], radius, radius, jitter);
- float p[3];
- add_v3_v3v3(p, jitter, mat[2]);
- DRW_debug_sphere(p, 0.01f, (float[4]){1.0f, (sample_ofs == i) ? 1.0f : 0.0f, 0.0f, 1.0f});
- }
-#endif
- add_v3_v3(mat[2], jitter);
- orthogonalize_m4(mat, 2);
-}
-
-static double round_to_digits(double value, int digits)
-{
- double factor = pow(10.0, digits - ceil(log10(fabs(value))));
- return round(value * factor) / factor;
-}
-
-static void frustum_min_bounding_sphere(const float corners[8][3],
- float r_center[3],
- float *r_radius)
-{
-#if 0 /* Simple solution but waste too much space. */
- float minvec[3], maxvec[3];
-
- /* compute the bounding box */
- INIT_MINMAX(minvec, maxvec);
- for (int i = 0; i < 8; i++) {
- minmax_v3v3_v3(minvec, maxvec, corners[i]);
- }
-
- /* compute the bounding sphere of this box */
- r_radius = len_v3v3(minvec, maxvec) * 0.5f;
- add_v3_v3v3(r_center, minvec, maxvec);
- mul_v3_fl(r_center, 0.5f);
-#else
- /* Find averaged center. */
- zero_v3(r_center);
- for (int i = 0; i < 8; i++) {
- add_v3_v3(r_center, corners[i]);
- }
- mul_v3_fl(r_center, 1.0f / 8.0f);
-
- /* Search the largest distance from the sphere center. */
- *r_radius = 0.0f;
- for (int i = 0; i < 8; i++) {
- float rad = len_squared_v3v3(corners[i], r_center);
- if (rad > *r_radius) {
- *r_radius = rad;
- }
- }
-
- /* TODO: try to reduce the radius further by moving the center.
- * Remember we need a __stable__ solution! */
-
- /* Try to reduce float imprecision leading to shimmering. */
- *r_radius = (float)round_to_digits(sqrtf(*r_radius), 3);
-#endif
-}
-
-static void eevee_shadow_cascade_setup(EEVEE_LightsInfo *linfo,
- EEVEE_Light *evli,
- DRWView *view,
- float view_near,
- float view_far,
- int sample_ofs)
-{
- EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id;
- EEVEE_ShadowCascade *csm_data = linfo->shadow_cascade_data + (int)shdw_data->type_data_id;
- EEVEE_ShadowCascadeRender *csm_render = linfo->shadow_cascade_render +
- (int)shdw_data->type_data_id;
- int cascade_nbr = csm_render->cascade_count;
- float cascade_fade = csm_render->cascade_fade;
- float cascade_max_dist = csm_render->cascade_max_dist;
- float cascade_exponent = csm_render->cascade_exponent;
-
- float jitter_ofs[2];
- double ht_point[2];
- double ht_offset[2] = {0.0, 0.0};
- const uint ht_primes[2] = {2, 3};
-
- BLI_halton_2d(ht_primes, ht_offset, sample_ofs, ht_point);
-
- /* Not really sure why we need 4.0 factor here. */
- jitter_ofs[0] = (ht_point[0] * 2.0 - 1.0) * 4.0 / linfo->shadow_cascade_size;
- jitter_ofs[1] = (ht_point[1] * 2.0 - 1.0) * 4.0 / linfo->shadow_cascade_size;
-
- /* Camera Matrices */
- float persinv[4][4], vp_projmat[4][4];
- DRW_view_persmat_get(view, persinv, true);
- DRW_view_winmat_get(view, vp_projmat, false);
- bool is_persp = DRW_view_is_persp_get(view);
-
- /* obmat = Object Space > World Space */
- /* viewmat = World Space > View Space */
- float(*viewmat)[4] = csm_render->viewmat;
- eevee_light_matrix_get(evli, viewmat);
- /* At this point, viewmat == normalize_m4(obmat) */
-
- if (linfo->soft_shadows) {
- shadow_cascade_random_matrix_set(viewmat, evli->radius, sample_ofs);
- }
-
- copy_m4_m4(csm_render->viewinv, viewmat);
- invert_m4(viewmat);
-
- copy_v3_v3(csm_data->shadow_vec, csm_render->viewinv[2]);
-
- /* Compute near and far value based on all shadow casters cumulated AABBs. */
- float sh_near = -1.0e30f, sh_far = 1.0e30f;
- BoundBox shcaster_bounds;
- BKE_boundbox_init_from_minmax(
- &shcaster_bounds, linfo->shcaster_aabb.min, linfo->shcaster_aabb.max);
-#ifdef DEBUG_CSM
- float dbg_col1[4] = {1.0f, 0.5f, 0.6f, 1.0f};
- DRW_debug_bbox(&shcaster_bounds, dbg_col1);
-#endif
- for (int i = 0; i < 8; i++) {
- mul_m4_v3(viewmat, shcaster_bounds.vec[i]);
- sh_near = max_ff(sh_near, shcaster_bounds.vec[i][2]);
- sh_far = min_ff(sh_far, shcaster_bounds.vec[i][2]);
- }
-#ifdef DEBUG_CSM
- float dbg_col2[4] = {0.5f, 1.0f, 0.6f, 1.0f};
- float pts[2][3] = {{0.0, 0.0, sh_near}, {0.0, 0.0, sh_far}};
- mul_m4_v3(csm_render->viewinv, pts[0]);
- mul_m4_v3(csm_render->viewinv, pts[1]);
- DRW_debug_sphere(pts[0], 1.0f, dbg_col1);
- DRW_debug_sphere(pts[1], 1.0f, dbg_col2);
-#endif
- /* The rest of the function is assuming inverted Z. */
- /* Add a little bias to avoid invalid matrices. */
- sh_far = -(sh_far - 1e-3);
- sh_near = -sh_near;
-
- /* The technique consists into splitting
- * the view frustum into several sub-frustum
- * that are individually receiving one shadow map */
-
- float csm_start, csm_end;
-
- if (is_persp) {
- csm_start = view_near;
- csm_end = max_ff(view_far, -cascade_max_dist);
- /* Avoid artifacts */
- csm_end = min_ff(view_near, csm_end);
- }
- else {
- csm_start = -view_far;
- csm_end = view_far;
- }
-
- /* init near/far */
- for (int c = 0; c < MAX_CASCADE_NUM; c++) {
- csm_data->split_start[c] = csm_end;
- csm_data->split_end[c] = csm_end;
- }
-
- /* Compute split planes */
- float splits_start_ndc[MAX_CASCADE_NUM];
- float splits_end_ndc[MAX_CASCADE_NUM];
-
- {
- /* Nearest plane */
- float p[4] = {1.0f, 1.0f, csm_start, 1.0f};
- /* TODO: we don't need full m4 multiply here */
- mul_m4_v4(vp_projmat, p);
- splits_start_ndc[0] = p[2];
- if (is_persp) {
- splits_start_ndc[0] /= p[3];
- }
- }
-
- {
- /* Farthest plane */
- float p[4] = {1.0f, 1.0f, csm_end, 1.0f};
- /* TODO: we don't need full m4 multiply here */
- mul_m4_v4(vp_projmat, p);
- splits_end_ndc[cascade_nbr - 1] = p[2];
- if (is_persp) {
- splits_end_ndc[cascade_nbr - 1] /= p[3];
- }
- }
-
- csm_data->split_start[0] = csm_start;
- csm_data->split_end[cascade_nbr - 1] = csm_end;
-
- for (int c = 1; c < cascade_nbr; c++) {
- /* View Space */
- float linear_split = interpf(csm_end, csm_start, c / (float)cascade_nbr);
- float exp_split = csm_start * powf(csm_end / csm_start, c / (float)cascade_nbr);
-
- if (is_persp) {
- csm_data->split_start[c] = interpf(exp_split, linear_split, cascade_exponent);
- }
- else {
- csm_data->split_start[c] = linear_split;
- }
- csm_data->split_end[c - 1] = csm_data->split_start[c];
-
- /* Add some overlap for smooth transition */
- csm_data->split_start[c] = interpf((c > 1) ? csm_data->split_end[c - 2] :
- csm_data->split_start[0],
- csm_data->split_end[c - 1],
- cascade_fade);
-
- /* NDC Space */
- {
- float p[4] = {1.0f, 1.0f, csm_data->split_start[c], 1.0f};
- /* TODO: we don't need full m4 multiply here */
- mul_m4_v4(vp_projmat, p);
- splits_start_ndc[c] = p[2];
-
- if (is_persp) {
- splits_start_ndc[c] /= p[3];
- }
- }
-
- {
- float p[4] = {1.0f, 1.0f, csm_data->split_end[c - 1], 1.0f};
- /* TODO: we don't need full m4 multiply here */
- mul_m4_v4(vp_projmat, p);
- splits_end_ndc[c - 1] = p[2];
-
- if (is_persp) {
- splits_end_ndc[c - 1] /= p[3];
- }
- }
- }
-
- /* Set last cascade split fade distance into the first split_start. */
- float prev_split = (cascade_nbr > 1) ? csm_data->split_end[cascade_nbr - 2] :
- csm_data->split_start[0];
- csm_data->split_start[0] = interpf(
- prev_split, csm_data->split_end[cascade_nbr - 1], cascade_fade);
-
- /* For each cascade */
- for (int c = 0; c < cascade_nbr; c++) {
- float(*projmat)[4] = csm_render->projmat[c];
- /* Given 8 frustum corners */
- float corners[8][3] = {
- /* Near Cap */
- {1.0f, -1.0f, splits_start_ndc[c]},
- {-1.0f, -1.0f, splits_start_ndc[c]},
- {-1.0f, 1.0f, splits_start_ndc[c]},
- {1.0f, 1.0f, splits_start_ndc[c]},
- /* Far Cap */
- {1.0f, -1.0f, splits_end_ndc[c]},
- {-1.0f, -1.0f, splits_end_ndc[c]},
- {-1.0f, 1.0f, splits_end_ndc[c]},
- {1.0f, 1.0f, splits_end_ndc[c]},
- };
-
- /* Transform them into world space */
- for (int i = 0; i < 8; i++) {
- mul_project_m4_v3(persinv, corners[i]);
- }
-
- float center[3];
- frustum_min_bounding_sphere(corners, center, &(csm_render->radius[c]));
-
-#ifdef DEBUG_CSM
- float dbg_col[4] = {0.0f, 0.0f, 0.0f, 1.0f};
- if (c < 3) {
- dbg_col[c] = 1.0f;
- }
- DRW_debug_bbox((BoundBox *)&corners, dbg_col);
- DRW_debug_sphere(center, csm_render->radius[c], dbg_col);
-#endif
-
- /* Project into light-space. */
- mul_m4_v3(viewmat, center);
-
- /* Snap projection center to nearest texel to cancel shimmering. */
- float shadow_origin[2], shadow_texco[2];
- /* Light to texture space. */
- mul_v2_v2fl(
- shadow_origin, center, linfo->shadow_cascade_size / (2.0f * csm_render->radius[c]));
-
- /* Find the nearest texel. */
- shadow_texco[0] = roundf(shadow_origin[0]);
- shadow_texco[1] = roundf(shadow_origin[1]);
-
- /* Compute offset. */
- sub_v2_v2(shadow_texco, shadow_origin);
- /* Texture to light space. */
- mul_v2_fl(shadow_texco, (2.0f * csm_render->radius[c]) / linfo->shadow_cascade_size);
-
- /* Apply offset. */
- add_v2_v2(center, shadow_texco);
-
- /* Expand the projection to cover frustum range */
- rctf rect_cascade;
- BLI_rctf_init_pt_radius(&rect_cascade, center, csm_render->radius[c]);
- orthographic_m4(projmat,
- rect_cascade.xmin,
- rect_cascade.xmax,
- rect_cascade.ymin,
- rect_cascade.ymax,
- sh_near,
- sh_far);
-
- /* Anti-Aliasing */
- if (linfo->soft_shadows) {
- add_v2_v2(projmat[3], jitter_ofs);
- }
-
- float viewprojmat[4][4];
- mul_m4_m4m4(viewprojmat, projmat, viewmat);
- mul_m4_m4m4(csm_data->shadowmat[c], texcomat, viewprojmat);
-
-#ifdef DEBUG_CSM
- DRW_debug_m4_as_bbox(viewprojmat, dbg_col, true);
-#endif
- }
-
- /* Bias is in clip-space, divide by range. */
- shdw_data->bias = csm_render->original_bias * 0.05f / fabsf(sh_far - sh_near);
- shdw_data->near = sh_near;
- shdw_data->far = sh_far;
-}
-
-static void eevee_ensure_cascade_views(EEVEE_ShadowCascadeRender *csm_render,
- DRWView *view[MAX_CASCADE_NUM])
-{
- for (int i = 0; i < csm_render->cascade_count; i++) {
- if (view[i] == NULL) {
- view[i] = DRW_view_create(csm_render->viewmat, csm_render->projmat[i], NULL, NULL, NULL);
- }
- else {
- DRW_view_update(view[i], csm_render->viewmat, csm_render->projmat[i], NULL, NULL);
- }
- }
-}
-
-void EEVEE_shadows_draw_cascades(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- DRWView *view,
- int cascade_index)
-{
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
- EEVEE_PrivateData *g_data = stl->g_data;
- EEVEE_LightsInfo *linfo = sldata->lights;
-
- EEVEE_Light *evli = linfo->light_data + linfo->shadow_cascade_light_indices[cascade_index];
- EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id;
- EEVEE_ShadowCascade *csm_data = linfo->shadow_cascade_data + (int)shdw_data->type_data_id;
- EEVEE_ShadowCascadeRender *csm_render = linfo->shadow_cascade_render +
- (int)shdw_data->type_data_id;
-
- float near = DRW_view_near_distance_get(view);
- float far = DRW_view_far_distance_get(view);
-
- eevee_shadow_cascade_setup(linfo, evli, view, near, far, effects->taa_current_sample - 1);
-
- /* Meh, Reusing the cube views. */
- BLI_assert(MAX_CASCADE_NUM <= 6);
- eevee_ensure_cascade_views(csm_render, g_data->cube_views);
-
- /* Render shadow cascades */
- /* Render cascade separately: seems to be faster for the general case.
- * The only time it's more beneficial is when the CPU culling overhead
- * outweigh the instancing overhead. which is rarely the case. */
- for (int j = 0; j < csm_render->cascade_count; j++) {
- DRW_view_set_active(g_data->cube_views[j]);
- int layer = csm_data->tex_id + j;
- GPU_framebuffer_texture_layer_attach(
- sldata->shadow_fb, sldata->shadow_cascade_pool, 0, layer, 0);
- GPU_framebuffer_bind(sldata->shadow_fb);
- GPU_framebuffer_clear_depth(sldata->shadow_fb, 1.0f);
- DRW_draw_pass(psl->shadow_pass);
- }
-}
diff --git a/source/blender/draw/engines/eevee/eevee_shadows_cube.c b/source/blender/draw/engines/eevee/eevee_shadows_cube.c
deleted file mode 100644
index ed9f05df13c..00000000000
--- a/source/blender/draw/engines/eevee/eevee_shadows_cube.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Copyright 2019, Blender Foundation.
- */
-
-/** \file
- * \ingroup EEVEE
- */
-
-#include "eevee_private.h"
-
-void EEVEE_shadows_cube_add(EEVEE_LightsInfo *linfo, EEVEE_Light *evli, Object *ob)
-{
- if (linfo->cube_len >= MAX_SHADOW_CUBE) {
- return;
- }
-
- const Light *la = (Light *)ob->data;
- EEVEE_Shadow *sh_data = linfo->shadow_data + linfo->shadow_len;
-
- /* Always update dupli lights as EEVEE_LightEngineData is not saved.
- * Same issue with dupli shadow casters. */
- bool update = (ob->base_flag & BASE_FROM_DUPLI) != 0;
- if (!update) {
- EEVEE_LightEngineData *led = EEVEE_light_data_ensure(ob);
- if (led->need_update) {
- update = true;
- led->need_update = false;
- }
- }
-
- if (update) {
- BLI_BITMAP_ENABLE(&linfo->sh_cube_update[0], linfo->cube_len);
- }
-
- sh_data->near = max_ff(la->clipsta, 1e-8f);
- sh_data->bias = max_ff(la->bias * 0.05f, 0.0f);
- eevee_contact_shadow_setup(la, sh_data);
-
- /* Saving light bounds for later. */
- BoundSphere *cube_bound = linfo->shadow_bounds + linfo->cube_len;
- copy_v3_v3(cube_bound->center, evli->position);
- cube_bound->radius = sqrt(1.0f / evli->invsqrdist);
-
- linfo->shadow_cube_light_indices[linfo->cube_len] = linfo->num_light;
- evli->shadow_id = linfo->shadow_len++;
- sh_data->type_data_id = linfo->cube_len++;
-
- /* Same as linfo->cube_len, no need to save. */
- linfo->num_cube_layer++;
-}
-
-static void shadow_cube_random_position_set(const EEVEE_Light *evli,
- int sample_ofs,
- float ws_sample_pos[3])
-{
- float jitter[3];
-#ifdef DEBUG_SHADOW_DISTRIBUTION
- int i = 0;
-start:
-#else
- int i = sample_ofs;
-#endif
- switch ((int)evli->light_type) {
- case LA_AREA:
- EEVEE_sample_rectangle(i, evli->rightvec, evli->upvec, evli->sizex, evli->sizey, jitter);
- break;
- case (int)LAMPTYPE_AREA_ELLIPSE:
- EEVEE_sample_ellipse(i, evli->rightvec, evli->upvec, evli->sizex, evli->sizey, jitter);
- break;
- default:
- EEVEE_sample_ball(i, evli->radius, jitter);
- }
-#ifdef DEBUG_SHADOW_DISTRIBUTION
- float p[3];
- add_v3_v3v3(p, jitter, ws_sample_pos);
- DRW_debug_sphere(p, 0.01f, (float[4]){1.0f, (sample_ofs == i) ? 1.0f : 0.0f, 0.0f, 1.0f});
- if (i++ < sample_ofs) {
- goto start;
- }
-#endif
- add_v3_v3(ws_sample_pos, jitter);
-}
-
-bool EEVEE_shadows_cube_setup(EEVEE_LightsInfo *linfo, const EEVEE_Light *evli, int sample_ofs)
-{
- EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id;
- EEVEE_ShadowCube *cube_data = linfo->shadow_cube_data + (int)shdw_data->type_data_id;
-
- eevee_light_matrix_get(evli, cube_data->shadowmat);
-
- shdw_data->far = max_ff(sqrt(1.0f / evli->invsqrdist), 3e-4);
- shdw_data->near = min_ff(shdw_data->near, shdw_data->far - 1e-4);
-
- bool update = false;
-
- if (linfo->soft_shadows) {
- shadow_cube_random_position_set(evli, sample_ofs, cube_data->shadowmat[3]);
- /* Update if position changes (avoid infinite update if soft shadows does not move).
- * Other changes are caught by depsgraph tagging. This one is for update between samples. */
- update = !compare_v3v3(cube_data->shadowmat[3], cube_data->position, 1e-10f);
- /**
- * Anti-Aliasing jitter: Add random rotation.
- *
- * The 2.0 factor is because texel angular size is not even across the cube-map,
- * so we make the rotation range a bit bigger.
- * This will not blur the shadow even if the spread is too big since we are just
- * rotating the shadow cube-map.
- * Note that this may be a rough approximation an may not converge to a perfectly
- * smooth shadow (because sample distribution is quite non-uniform) but is enough
- * in practice.
- */
- /* NOTE: this has implication for spotlight rendering optimization
- * (see EEVEE_shadows_draw_cubemap). */
- float angular_texel_size = 2.0f * DEG2RADF(90) / (float)linfo->shadow_cube_size;
- EEVEE_random_rotation_m4(sample_ofs, angular_texel_size, cube_data->shadowmat);
- }
-
- copy_v3_v3(cube_data->position, cube_data->shadowmat[3]);
- invert_m4(cube_data->shadowmat);
-
- return update;
-}
-
-static void eevee_ensure_cube_views(
- float near, float far, int cube_res, const float viewmat[4][4], DRWView *view[6])
-{
- float winmat[4][4];
- float side = near;
-
- /* TODO: shadow-cube array. */
- if (true) {
- /* This half texel offset is used to ensure correct filtering between faces. */
- /* FIXME: This exhibit float precision issue with lower cube_res.
- * But it seems to be caused by the perspective_m4. */
- side *= ((float)cube_res + 1.0f) / (float)(cube_res);
- }
-
- perspective_m4(winmat, -side, side, -side, side, near, far);
-
- for (int i = 0; i < 6; i++) {
- float tmp[4][4];
- mul_m4_m4m4(tmp, cubefacemat[i], viewmat);
-
- if (view[i] == NULL) {
- view[i] = DRW_view_create(tmp, winmat, NULL, NULL, NULL);
- }
- else {
- DRW_view_update(view[i], tmp, winmat, NULL, NULL);
- }
- }
-}
-
-/* Does a spot angle fits a single cubeface. */
-static bool spot_angle_fit_single_face(const EEVEE_Light *evli)
-{
- /* alpha = spot/cone half angle. */
- /* beta = scaled spot/cone half angle. */
- float cos_alpha = evli->spotsize;
- float sin_alpha = sqrtf(max_ff(0.0f, 1.0f - cos_alpha * cos_alpha));
- float cos_beta = min_ff(cos_alpha / hypotf(cos_alpha, sin_alpha * evli->sizex),
- cos_alpha / hypotf(cos_alpha, sin_alpha * evli->sizey));
- /* Don't use 45 degrees because AA jitter can offset the face. */
- return cos_beta > cosf(DEG2RADF(42.0f));
-}
-
-void EEVEE_shadows_draw_cubemap(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, int cube_index)
-{
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_PrivateData *g_data = stl->g_data;
- EEVEE_LightsInfo *linfo = sldata->lights;
-
- EEVEE_Light *evli = linfo->light_data + linfo->shadow_cube_light_indices[cube_index];
- EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id;
- EEVEE_ShadowCube *cube_data = linfo->shadow_cube_data + (int)shdw_data->type_data_id;
-
- eevee_ensure_cube_views(shdw_data->near,
- shdw_data->far,
- linfo->shadow_cube_size,
- cube_data->shadowmat,
- g_data->cube_views);
-
- /* Render shadow cube */
- /* Render 6 faces separately: seems to be faster for the general case.
- * The only time it's more beneficial is when the CPU culling overhead
- * outweigh the instancing overhead. which is rarely the case. */
- for (int j = 0; j < 6; j++) {
- /* Optimization: Only render the needed faces. */
- /* Skip all but -Z face. */
- if (evli->light_type == LA_SPOT && j != 5 && spot_angle_fit_single_face(evli)) {
- continue;
- }
- /* Skip +Z face. */
- if (evli->light_type != LA_LOCAL && j == 4) {
- continue;
- }
- /* TODO(fclem): some cube sides can be invisible in the main views. Cull them. */
- // if (frustum_intersect(g_data->cube_views[j], main_view))
- // continue;
-
- DRW_view_set_active(g_data->cube_views[j]);
- int layer = cube_index * 6 + j;
- GPU_framebuffer_texture_layer_attach(sldata->shadow_fb, sldata->shadow_cube_pool, 0, layer, 0);
- GPU_framebuffer_bind(sldata->shadow_fb);
- GPU_framebuffer_clear_depth(sldata->shadow_fb, 1.0f);
- DRW_draw_pass(psl->shadow_pass);
- }
-
- BLI_BITMAP_SET(&linfo->sh_cube_update[0], cube_index, false);
-}
diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.c b/source/blender/draw/engines/eevee/eevee_subsurface.c
deleted file mode 100644
index 48c24d138e6..00000000000
--- a/source/blender/draw/engines/eevee/eevee_subsurface.c
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Copyright 2016, Blender Foundation.
- */
-
-/** \file
- * \ingroup draw_engine
- *
- * Screen space subsurface scattering technique.
- */
-
-#include "DRW_render.h"
-
-#include "BLI_string_utils.h"
-
-#include "DEG_depsgraph_query.h"
-
-#include "GPU_capabilities.h"
-#include "GPU_material.h"
-#include "GPU_texture.h"
-
-#include "eevee_private.h"
-
-void EEVEE_subsurface_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *UNUSED(vedata))
-{
-}
-
-void EEVEE_subsurface_draw_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
-{
- EEVEE_EffectsInfo *effects = vedata->stl->effects;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_FramebufferList *fbl = vedata->fbl;
- EEVEE_TextureList *txl = vedata->txl;
- DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- const float *viewport_size = DRW_viewport_size_get();
- const int fs_size[2] = {(int)viewport_size[0], (int)viewport_size[1]};
-
- if (effects->enabled_effects & EFFECT_SSS) {
- /* NOTE : we need another stencil because the stencil buffer is on the same texture
- * as the depth buffer we are sampling from. This could be avoided if the stencil is
- * a separate texture but that needs OpenGL 4.4 or ARB_texture_stencil8.
- * OR OpenGL 4.3 / ARB_ES3_compatibility if using a render-buffer instead. */
- effects->sss_stencil = DRW_texture_pool_query_2d(
- fs_size[0], fs_size[1], GPU_DEPTH24_STENCIL8, &draw_engine_eevee_type);
- effects->sss_blur = DRW_texture_pool_query_2d(
- fs_size[0], fs_size[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type);
- effects->sss_irradiance = DRW_texture_pool_query_2d(
- fs_size[0], fs_size[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type);
- effects->sss_radius = DRW_texture_pool_query_2d(
- fs_size[0], fs_size[1], GPU_R16F, &draw_engine_eevee_type);
- effects->sss_albedo = DRW_texture_pool_query_2d(
- fs_size[0], fs_size[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type);
-
- GPUTexture *stencil_tex = effects->sss_stencil;
-
- if (GPU_depth_blitting_workaround()) {
- /* Blitting stencil buffer does not work on macOS + Radeon Pro.
- * Blit depth instead and use sss_stencil's depth as depth texture,
- * and dtxl->depth as stencil mask. */
- GPU_framebuffer_ensure_config(
- &fbl->sss_blit_fb, {GPU_ATTACHMENT_TEXTURE(effects->sss_stencil), GPU_ATTACHMENT_NONE});
-
- stencil_tex = dtxl->depth;
- }
-
- GPU_framebuffer_ensure_config(
- &fbl->sss_blur_fb,
- {GPU_ATTACHMENT_TEXTURE(stencil_tex), GPU_ATTACHMENT_TEXTURE(effects->sss_blur)});
-
- GPU_framebuffer_ensure_config(
- &fbl->sss_resolve_fb,
- {GPU_ATTACHMENT_TEXTURE(stencil_tex), GPU_ATTACHMENT_TEXTURE(txl->color)});
-
- GPU_framebuffer_ensure_config(
- &fbl->sss_translucency_fb,
- {GPU_ATTACHMENT_TEXTURE(stencil_tex), GPU_ATTACHMENT_TEXTURE(effects->sss_irradiance)});
-
- GPU_framebuffer_ensure_config(&fbl->sss_clear_fb,
- {GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(effects->sss_irradiance),
- GPU_ATTACHMENT_TEXTURE(effects->sss_radius)});
- if ((stl->g_data->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) != 0) {
- EEVEE_subsurface_output_init(sldata, vedata, 0);
- }
- else {
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_accum_fb);
- DRW_TEXTURE_FREE_SAFE(txl->sss_accum);
- }
- }
- else {
- /* Cleanup to release memory */
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_blur_fb);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_resolve_fb);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_clear_fb);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_accum_fb);
- DRW_TEXTURE_FREE_SAFE(txl->sss_accum);
- effects->sss_stencil = NULL;
- effects->sss_blur = NULL;
- effects->sss_irradiance = NULL;
- effects->sss_radius = NULL;
- }
-}
-
-void EEVEE_subsurface_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
- EEVEE_Data *vedata,
- uint UNUSED(tot_samples))
-{
- EEVEE_FramebufferList *fbl = vedata->fbl;
- EEVEE_TextureList *txl = vedata->txl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
-
- const eGPUTextureFormat texture_format_light = GPU_RGBA32F;
- const bool texture_created = txl->sss_accum == NULL;
- DRW_texture_ensure_fullscreen_2d(&txl->sss_accum, texture_format_light, 0);
-
- GPUTexture *stencil_tex = effects->sss_stencil;
-
- if (GPU_depth_blitting_workaround()) {
- DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- /* Blitting stencil buffer does not work on macOS + Radeon Pro.
- * Blit depth instead and use sss_stencil's depth as depth texture,
- * and dtxl->depth as stencil mask. */
- stencil_tex = dtxl->depth;
- }
-
- GPU_framebuffer_ensure_config(
- &fbl->sss_accum_fb,
- {GPU_ATTACHMENT_TEXTURE(stencil_tex), GPU_ATTACHMENT_TEXTURE(txl->sss_accum)});
-
- /* Clear texture.
- * Due to the late initialization of the SSS it can happen that the `taa_current_sample` is
- * already higher than one. This is noticeable when loading a file that has the diffuse light
- * pass in look-dev mode active. `texture_created` will make sure that newly created textures
- * are cleared. */
- if (effects->taa_current_sample == 1 || texture_created) {
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- GPU_framebuffer_bind(fbl->sss_accum_fb);
- GPU_framebuffer_clear_color(fbl->sss_accum_fb, clear);
- }
-}
-
-void EEVEE_subsurface_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
-{
- EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
- EEVEE_EffectsInfo *effects = vedata->stl->effects;
- EEVEE_PassList *psl = vedata->psl;
-
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
-
- effects->sss_sample_count = 1 + scene_eval->eevee.sss_samples * 2;
- effects->sss_surface_count = 0;
- common_data->sss_jitter_threshold = scene_eval->eevee.sss_jitter_threshold;
-
- /** Screen Space SubSurface Scattering overview
- * TODO
- */
- DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL;
- DRW_PASS_CREATE(psl->sss_blur_ps, state);
- DRW_PASS_CREATE(psl->sss_resolve_ps, state | DRW_STATE_BLEND_ADD);
- DRW_PASS_CREATE(psl->sss_translucency_ps, state | DRW_STATE_BLEND_ADD);
-}
-
-void EEVEE_subsurface_add_pass(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- Material *ma,
- DRWShadingGroup *shgrp,
- struct GPUMaterial *gpumat)
-{
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
- DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- GPUTexture **depth_src = GPU_depth_blitting_workaround() ? &effects->sss_stencil : &dtxl->depth;
-
- struct GPUTexture *sss_tex_profile = NULL;
- struct GPUUniformBuf *sss_profile = GPU_material_sss_profile_get(
- gpumat, stl->effects->sss_sample_count, &sss_tex_profile);
-
- if (!sss_profile) {
- BLI_assert_msg(0, "SSS pass requested but no SSS data was found");
- return;
- }
-
- /* Limit of 8 bit stencil buffer. ID 255 is refraction. */
- if (effects->sss_surface_count >= 254) {
- /* TODO: display message. */
- printf("Error: Too many different Subsurface shader in the scene.\n");
- return;
- }
-
- int sss_id = ++(effects->sss_surface_count);
- /* Make main pass output stencil mask. */
- DRW_shgroup_stencil_mask(shgrp, sss_id);
-
- {
- eGPUSamplerState state = GPU_SAMPLER_DEFAULT;
-
- DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_subsurface_first_pass_sh_get(),
- psl->sss_blur_ps);
- DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
- DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", depth_src);
- DRW_shgroup_uniform_texture_ref_ex(grp, "sssIrradiance", &effects->sss_irradiance, state);
- DRW_shgroup_uniform_texture_ref_ex(grp, "sssRadius", &effects->sss_radius, state);
- DRW_shgroup_uniform_block(grp, "sssProfile", sss_profile);
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
- DRW_shgroup_stencil_mask(grp, sss_id);
- DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
-
- grp = DRW_shgroup_create(EEVEE_shaders_subsurface_second_pass_sh_get(), psl->sss_resolve_ps);
- DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
- DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", depth_src);
- DRW_shgroup_uniform_texture_ref_ex(grp, "sssIrradiance", &effects->sss_blur, state);
- DRW_shgroup_uniform_texture_ref_ex(grp, "sssAlbedo", &effects->sss_albedo, state);
- DRW_shgroup_uniform_texture_ref_ex(grp, "sssRadius", &effects->sss_radius, state);
- DRW_shgroup_uniform_block(grp, "sssProfile", sss_profile);
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
- DRW_shgroup_stencil_mask(grp, sss_id);
- DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
- }
-
- if (ma->blend_flag & MA_BL_TRANSLUCENCY) {
- DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_subsurface_translucency_sh_get(),
- psl->sss_translucency_ps);
- DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex());
- DRW_shgroup_uniform_texture(grp, "sssTexProfile", sss_tex_profile);
- DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", depth_src);
- DRW_shgroup_uniform_texture_ref(grp, "sssRadius", &effects->sss_radius);
- DRW_shgroup_uniform_texture_ref(grp, "sssShadowCubes", &sldata->shadow_cube_pool);
- DRW_shgroup_uniform_texture_ref(grp, "sssShadowCascades", &sldata->shadow_cascade_pool);
- DRW_shgroup_uniform_block(grp, "sssProfile", sss_profile);
- DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
- DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
- DRW_shgroup_stencil_mask(grp, sss_id);
- DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
- }
-}
-
-void EEVEE_subsurface_data_render(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
-{
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_FramebufferList *fbl = vedata->fbl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
-
- if ((effects->enabled_effects & EFFECT_SSS) != 0) {
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- /* Clear sss_data texture only... can this be done in a more clever way? */
- GPU_framebuffer_bind(fbl->sss_clear_fb);
- GPU_framebuffer_clear_color(fbl->sss_clear_fb, clear);
-
- GPU_framebuffer_ensure_config(&fbl->main_fb,
- {GPU_ATTACHMENT_LEAVE,
- GPU_ATTACHMENT_LEAVE,
- GPU_ATTACHMENT_LEAVE,
- GPU_ATTACHMENT_LEAVE,
- GPU_ATTACHMENT_TEXTURE(effects->sss_irradiance),
- GPU_ATTACHMENT_TEXTURE(effects->sss_radius),
- GPU_ATTACHMENT_TEXTURE(effects->sss_albedo)});
-
- GPU_framebuffer_bind(fbl->main_fb);
- DRW_draw_pass(psl->material_sss_ps);
-
- /* Restore */
- GPU_framebuffer_ensure_config(&fbl->main_fb,
- {GPU_ATTACHMENT_LEAVE,
- GPU_ATTACHMENT_LEAVE,
- GPU_ATTACHMENT_LEAVE,
- GPU_ATTACHMENT_LEAVE,
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_NONE});
- }
-}
-
-void EEVEE_subsurface_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
-{
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_TextureList *txl = vedata->txl;
- EEVEE_FramebufferList *fbl = vedata->fbl;
- EEVEE_EffectsInfo *effects = stl->effects;
-
- if ((effects->enabled_effects & EFFECT_SSS) != 0) {
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
-
- DRW_stats_group_start("SSS");
-
- if (GPU_depth_blitting_workaround()) {
- /* Copy depth channel */
- GPU_framebuffer_blit(fbl->main_fb, 0, fbl->sss_blit_fb, 0, GPU_DEPTH_BIT);
- }
- else {
- /* Copy stencil channel, could be avoided (see EEVEE_subsurface_init) */
- GPU_framebuffer_blit(fbl->main_fb, 0, fbl->sss_blur_fb, 0, GPU_STENCIL_BIT);
- }
-
- if (!DRW_pass_is_empty(psl->sss_translucency_ps)) {
- /* We sample the shadow-maps using normal sampler. We need to disable Comparison mode.
- * TODO(fclem): avoid this by using sampler objects. */
- GPU_texture_compare_mode(sldata->shadow_cube_pool, false);
- GPU_texture_compare_mode(sldata->shadow_cascade_pool, false);
-
- GPU_framebuffer_bind(fbl->sss_translucency_fb);
- DRW_draw_pass(psl->sss_translucency_ps);
-
- /* Reset original state. */
- GPU_texture_compare_mode(sldata->shadow_cube_pool, true);
- GPU_texture_compare_mode(sldata->shadow_cascade_pool, true);
- }
-
- /* 1. horizontal pass */
- GPU_framebuffer_bind(fbl->sss_blur_fb);
- GPU_framebuffer_clear_color(fbl->sss_blur_fb, clear);
- DRW_draw_pass(psl->sss_blur_ps);
-
- /* 2. vertical pass + Resolve */
- GPU_framebuffer_texture_attach(fbl->sss_resolve_fb, txl->color, 0, 0);
- GPU_framebuffer_bind(fbl->sss_resolve_fb);
- DRW_draw_pass(psl->sss_resolve_ps);
-
- GPU_framebuffer_bind(fbl->main_fb);
- DRW_stats_group_end();
- }
-}
-
-void EEVEE_subsurface_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
-{
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_FramebufferList *fbl = vedata->fbl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
-
- if (((effects->enabled_effects & EFFECT_SSS) != 0) && (fbl->sss_accum_fb != NULL)) {
- /* Copy stencil channel, could be avoided (see EEVEE_subsurface_init) */
- GPU_framebuffer_blit(fbl->main_fb, 0, fbl->sss_accum_fb, 0, GPU_STENCIL_BIT);
-
- /* Only do vertical pass + Resolve */
- GPU_framebuffer_bind(fbl->sss_accum_fb);
- DRW_draw_pass(psl->sss_resolve_ps);
-
- /* Restore */
- GPU_framebuffer_bind(fbl->main_fb);
- }
-}
diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.cc b/source/blender/draw/engines/eevee/eevee_subsurface.cc
new file mode 100644
index 00000000000..83917bc4697
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_subsurface.cc
@@ -0,0 +1,205 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ */
+
+#include "BLI_vector.hh"
+
+#include "eevee_instance.hh"
+#include "eevee_subsurface.hh"
+
+#include <iostream>
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name Subsurface
+ *
+ * \{ */
+
+/* TODO(fclem) Only enable this module if there is any SSS object in the scene. */
+void SubsurfaceModule::end_sync()
+{
+ data_.jitter_threshold = inst_.scene->eevee.sss_jitter_threshold;
+ if (data_.sample_len != inst_.scene->eevee.sss_samples) {
+ /* Convert sample count from old implementation which was using a separable filter. */
+ /* TODO(fclem) better remapping. */
+ // data_.sample_len = square_f(1 + 2 * inst_.scene->eevee.sss_samples);
+ data_.sample_len = 55;
+ }
+
+ if (transmittance_tx == nullptr) {
+ precompute_transmittance_profile();
+ }
+
+ precompute_samples_location();
+
+ data_.push_update();
+}
+
+void SubsurfaceModule::precompute_samples_location()
+{
+ /* Precompute sample position with white albedo. */
+ float d = burley_setup(1.0f, 1.0f);
+
+ float rand_u = inst_.sampling.rng_get(SAMPLING_SSS_U);
+ float rand_v = inst_.sampling.rng_get(SAMPLING_SSS_V);
+
+ double golden_angle = M_PI * (3.0 - sqrt(5.0));
+ for (auto i : IndexRange(data_.sample_len)) {
+ float theta = golden_angle * i + M_PI * 2.0f * rand_u;
+ /* Scale using rand_v in order to keep first sample always at center. */
+ float x = (1.0f + (rand_v / data_.sample_len)) * (i / (float)data_.sample_len);
+ float r = burley_sample(d, x);
+ data_.samples[i].x = cosf(theta) * r;
+ data_.samples[i].y = sinf(theta) * r;
+ data_.samples[i].z = 1.0f / burley_pdf(d, r);
+ }
+}
+
+void SubsurfaceModule::precompute_transmittance_profile()
+{
+ Vector<float> profile(SSS_TRANSMIT_LUT_SIZE);
+
+ /* Precompute sample position with white albedo. */
+ float radius = 1.0f;
+ float d = burley_setup(radius, 1.0f);
+
+ /* For each distance d we compute the radiance incoming from an hypothetical parallel plane. */
+ for (auto i : IndexRange(SSS_TRANSMIT_LUT_SIZE)) {
+ /* Distance from the lit surface plane.
+ * Compute to a larger maximum distance to have a smoother falloff for all channels. */
+ float lut_radius = SSS_TRANSMIT_LUT_RADIUS * radius;
+ float distance = lut_radius * (i + 1e-5f) / profile.size();
+ /* Compute radius of the footprint on the hypothetical plane. */
+ float r_fp = sqrtf(square_f(lut_radius) - square_f(distance));
+
+ profile[i] = 0.0f;
+ float area_accum = 0.0f;
+ for (auto j : IndexRange(SSS_TRANSMIT_LUT_STEP_RES)) {
+ /* Compute distance to the "shading" point through the medium. */
+ float r = (r_fp * (j + 0.5f)) / SSS_TRANSMIT_LUT_STEP_RES;
+ float r_prev = (r_fp * (j + 0.0f)) / SSS_TRANSMIT_LUT_STEP_RES;
+ float r_next = (r_fp * (j + 1.0f)) / SSS_TRANSMIT_LUT_STEP_RES;
+ r = hypotf(r, distance);
+ float R = burley_eval(d, r);
+ /* Since the profile and configuration are radially symmetrical we
+ * can just evaluate it once and weight it accordingly */
+ float disk_area = square_f(r_next) - square_f(r_prev);
+
+ profile[i] += R * disk_area;
+ area_accum += disk_area;
+ }
+ /* Normalize over the disk. */
+ profile[i] /= area_accum;
+ }
+ /* Make a smooth gradient from 1 to 0. */
+ float range = profile.first() - profile.last();
+ float offset = profile.last();
+ for (float &value : profile) {
+ value = (value - offset) / range;
+ }
+ profile.first() = 1;
+ profile.last() = 0;
+
+ transmittance_tx = GPU_texture_create_1d(
+ "SSSTransmittanceProfile", profile.size(), 1, GPU_R16F, profile.data());
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Christensen-Burley SSS model
+ *
+ * Based on: "Approximate Reflectance Profiles for Efficient Subsurface Scattering"
+ * by Per Christensen
+ * https://graphics.pixar.com/library/ApproxBSSRDF/approxbssrdfslides.pdf
+ * \{ */
+
+float SubsurfaceModule::burley_setup(float radius, float albedo)
+{
+ float A = albedo;
+ /* Diffuse surface transmission, equation (6). */
+ float s = 1.9f - A + 3.5f * square_f(A - 0.8f);
+ /* Mean free path length adapted to fit ancient Cubic and Gaussian models. */
+ float l = 0.25 * M_1_PI * radius;
+
+ return l / s;
+}
+
+float SubsurfaceModule::burley_sample(float d, float x_rand)
+{
+ x_rand *= SSS_BURLEY_TRUNCATE_CDF;
+
+ const float tolerance = 1e-6;
+ const int max_iteration_count = 10;
+ /* Do initial guess based on manual curve fitting, this allows us to reduce
+ * number of iterations to maximum 4 across the [0..1] range. We keep maximum
+ * number of iteration higher just to be sure we didn't miss root in some
+ * corner case.
+ */
+ float r;
+ if (x_rand <= 0.9) {
+ r = exp(x_rand * x_rand * 2.4) - 1.0;
+ }
+ else {
+ /* TODO(sergey): Some nicer curve fit is possible here. */
+ r = 15.0;
+ }
+ /* Solve against scaled radius. */
+ for (int i = 0; i < max_iteration_count; i++) {
+ float exp_r_3 = exp(-r / 3.0);
+ float exp_r = exp_r_3 * exp_r_3 * exp_r_3;
+ float f = 1.0 - 0.25 * exp_r - 0.75 * exp_r_3 - x_rand;
+ float f_ = 0.25 * exp_r + 0.25 * exp_r_3;
+
+ if (abs(f) < tolerance || f_ == 0.0) {
+ break;
+ }
+
+ r = r - f / f_;
+ if (r < 0.0) {
+ r = 0.0;
+ }
+ }
+
+ return r * d;
+}
+
+float SubsurfaceModule::burley_eval(float d, float r)
+{
+ if (r >= SSS_BURLEY_TRUNCATE * d) {
+ return 0.0;
+ }
+ /* Slide 33. */
+ float exp_r_3_d = expf(-r / (3.0f * d));
+ float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d;
+ return (exp_r_d + exp_r_3_d) / (8.0f * (float)M_PI * d);
+}
+
+float SubsurfaceModule::burley_pdf(float d, float r)
+{
+ return burley_eval(d, r) / SSS_BURLEY_TRUNCATE_CDF;
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.hh b/source/blender/draw/engines/eevee/eevee_subsurface.hh
new file mode 100644
index 00000000000..c8b65fdfafb
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_subsurface.hh
@@ -0,0 +1,84 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ */
+
+#pragma once
+
+#include "eevee_shader.hh"
+#include "eevee_shader_shared.hh"
+#include "eevee_wrapper.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name Subsurface
+ *
+ * \{ */
+
+class Instance;
+
+struct SubsurfaceModule {
+ private:
+ Instance &inst_;
+ /** Contains samples locations. */
+ SubsurfaceDataBuf data_;
+ /** Contains translucence profile for a single color channel. */
+ GPUTexture *transmittance_tx = nullptr;
+
+ public:
+ SubsurfaceModule(Instance &inst) : inst_(inst)
+ {
+ /* Force first update. */
+ data_.sample_len = -1;
+ };
+
+ ~SubsurfaceModule()
+ {
+ GPU_TEXTURE_FREE_SAFE(transmittance_tx);
+ };
+
+ void end_sync();
+
+ const GPUUniformBuf *ubo_get(void) const
+ {
+ return data_.ubo_get();
+ }
+
+ GPUTexture **transmittance_ref_get(void)
+ {
+ return &transmittance_tx;
+ }
+
+ private:
+ void precompute_samples_location();
+ void precompute_transmittance_profile();
+
+ /** Christensen-Burley implementation. */
+ static float burley_setup(float radius, float albedo);
+ static float burley_sample(float d, float x_rand);
+ static float burley_eval(float d, float r);
+ static float burley_pdf(float d, float r);
+};
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
deleted file mode 100644
index 361fa2704c9..00000000000
--- a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Copyright 2016, Blender Foundation.
- */
-
-/** \file
- * \ingroup draw_engine
- *
- * Temporal super sampling technique
- */
-
-#include "DRW_render.h"
-
-#include "ED_screen.h"
-
-#include "BLI_rand.h"
-
-#include "DEG_depsgraph_query.h"
-
-#include "GPU_texture.h"
-#include "eevee_private.h"
-
-#define FILTER_CDF_TABLE_SIZE 512
-
-static struct {
- /* Pixel filter table: Only blackman-harris for now. */
- bool inited;
- float inverted_cdf[FILTER_CDF_TABLE_SIZE];
-} e_data = {false}; /* Engine data */
-
-static float UNUSED_FUNCTION(filter_box)(float UNUSED(x))
-{
- return 1.0f;
-}
-
-static float filter_blackman_harris(float x)
-{
- /* Hardcoded 1px footprint [-0.5..0.5]. We resize later. */
- const float width = 1.0f;
- x = 2.0f * M_PI * (x / width + 0.5f);
- return 0.35875f - 0.48829f * cosf(x) + 0.14128f * cosf(2.0f * x) - 0.01168f * cosf(3.0f * x);
-}
-
-/* Compute cumulative distribution function of a discrete function. */
-static void compute_cdf(float (*func)(float x), float cdf[FILTER_CDF_TABLE_SIZE])
-{
- cdf[0] = 0.0f;
- /* Actual CDF evaluation. */
- for (int u = 0; u < FILTER_CDF_TABLE_SIZE - 1; u++) {
- float x = (float)(u + 1) / (float)(FILTER_CDF_TABLE_SIZE - 1);
- cdf[u + 1] = cdf[u] + func(x - 0.5f); /* [-0.5..0.5]. We resize later. */
- }
- /* Normalize the CDF. */
- for (int u = 0; u < FILTER_CDF_TABLE_SIZE - 1; u++) {
- cdf[u] /= cdf[FILTER_CDF_TABLE_SIZE - 1];
- }
- /* Just to make sure. */
- cdf[FILTER_CDF_TABLE_SIZE - 1] = 1.0f;
-}
-
-static void invert_cdf(const float cdf[FILTER_CDF_TABLE_SIZE],
- float invert_cdf[FILTER_CDF_TABLE_SIZE])
-{
- for (int u = 0; u < FILTER_CDF_TABLE_SIZE; u++) {
- float x = (float)u / (float)(FILTER_CDF_TABLE_SIZE - 1);
- for (int i = 0; i < FILTER_CDF_TABLE_SIZE; i++) {
- if (cdf[i] >= x) {
- if (i == FILTER_CDF_TABLE_SIZE - 1) {
- invert_cdf[u] = 1.0f;
- }
- else {
- float t = (x - cdf[i]) / (cdf[i + 1] - cdf[i]);
- invert_cdf[u] = ((float)i + t) / (float)(FILTER_CDF_TABLE_SIZE - 1);
- }
- break;
- }
- }
- }
-}
-
-/* Evaluate a discrete function table with linear interpolation. */
-static float eval_table(const float *table, float x)
-{
- CLAMP(x, 0.0f, 1.0f);
- x = x * (FILTER_CDF_TABLE_SIZE - 1);
-
- int index = min_ii((int)(x), FILTER_CDF_TABLE_SIZE - 1);
- int nindex = min_ii(index + 1, FILTER_CDF_TABLE_SIZE - 1);
- float t = x - index;
-
- return (1.0f - t) * table[index] + t * table[nindex];
-}
-
-static void eevee_create_cdf_table_temporal_sampling(void)
-{
- float *cdf_table = MEM_mallocN(sizeof(float) * FILTER_CDF_TABLE_SIZE, "Eevee Filter CDF table");
-
- float filter_width = 2.0f; /* Use a 2 pixel footprint by default. */
-
- {
- /* Use blackman-harris filter. */
- filter_width *= 2.0f;
- compute_cdf(filter_blackman_harris, cdf_table);
- }
-
- invert_cdf(cdf_table, e_data.inverted_cdf);
-
- /* Scale and offset table. */
- for (int i = 0; i < FILTER_CDF_TABLE_SIZE; i++) {
- e_data.inverted_cdf[i] = (e_data.inverted_cdf[i] - 0.5f) * filter_width;
- }
-
- MEM_freeN(cdf_table);
- e_data.inited = true;
-}
-
-void EEVEE_temporal_sampling_offset_calc(const double ht_point[2],
- const float filter_size,
- float r_offset[2])
-{
- r_offset[0] = eval_table(e_data.inverted_cdf, (float)(ht_point[0])) * filter_size;
- r_offset[1] = eval_table(e_data.inverted_cdf, (float)(ht_point[1])) * filter_size;
-}
-
-void EEVEE_temporal_sampling_matrices_calc(EEVEE_EffectsInfo *effects, const double ht_point[2])
-{
- const float *viewport_size = DRW_viewport_size_get();
- const DRWContextState *draw_ctx = DRW_context_state_get();
- Scene *scene = draw_ctx->scene;
- RenderData *rd = &scene->r;
-
- float persmat[4][4], viewmat[4][4], winmat[4][4], wininv[4][4];
- DRW_view_persmat_get(NULL, persmat, false);
- DRW_view_viewmat_get(NULL, viewmat, false);
- DRW_view_winmat_get(NULL, winmat, false);
- DRW_view_winmat_get(NULL, wininv, true);
-
- float ofs[2];
- EEVEE_temporal_sampling_offset_calc(ht_point, rd->gauss, ofs);
-
- if (effects->taa_current_sample > 1) {
- window_translate_m4(winmat, persmat, ofs[0] / viewport_size[0], ofs[1] / viewport_size[1]);
- }
-
- /* Jitter is in pixel space. Focus distance in world space units. */
- float dof_jitter[2], focus_distance;
- if (EEVEE_depth_of_field_jitter_get(effects, dof_jitter, &focus_distance)) {
- /* Convert to NDC space [-1..1]. */
- dof_jitter[0] /= viewport_size[0] * 0.5f;
- dof_jitter[1] /= viewport_size[1] * 0.5f;
-
- /* Skew the projection matrix in the ray direction and offset it to ray origin.
- * Make it focus at focus_distance. */
- if (winmat[2][3] != -1.0f) {
- /* Orthographic */
- add_v2_v2(winmat[2], dof_jitter);
-
- window_translate_m4(
- winmat, persmat, dof_jitter[0] * focus_distance, dof_jitter[1] * focus_distance);
- }
- else {
- /* Get focus distance in NDC. */
- float focus_pt[3] = {0.0f, 0.0f, -focus_distance};
- mul_project_m4_v3(winmat, focus_pt);
- /* Get pixel footprint in view-space. */
- float jitter_scaled[3] = {dof_jitter[0], dof_jitter[1], focus_pt[2]};
- float center[3] = {0.0f, 0.0f, focus_pt[2]};
- mul_project_m4_v3(wininv, jitter_scaled);
- mul_project_m4_v3(wininv, center);
-
- /* FIXME(fclem): The offset is noticeably large and the culling might make object pop out
- * of the blurring radius. To fix this, use custom enlarged culling matrix. */
- sub_v2_v2v2(jitter_scaled, jitter_scaled, center);
- add_v2_v2(viewmat[3], jitter_scaled);
-
- window_translate_m4(winmat, persmat, -dof_jitter[0], -dof_jitter[1]);
- }
- }
-
- BLI_assert(effects->taa_view != NULL);
-
- /* When rendering just update the view. This avoids recomputing the culling. */
- DRW_view_update_sub(effects->taa_view, viewmat, winmat);
-}
-
-void EEVEE_temporal_sampling_update_matrices(EEVEE_Data *vedata)
-{
- EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
-
- double ht_point[2];
- double ht_offset[2] = {0.0, 0.0};
- const uint ht_primes[2] = {2, 3};
-
- BLI_halton_2d(ht_primes, ht_offset, effects->taa_current_sample - 1, ht_point);
-
- EEVEE_temporal_sampling_matrices_calc(effects, ht_point);
-
- DRW_view_set_active(effects->taa_view);
-}
-
-void EEVEE_temporal_sampling_reset(EEVEE_Data *vedata)
-{
- vedata->stl->effects->taa_render_sample = 1;
- vedata->stl->effects->taa_current_sample = 1;
-}
-
-void EEVEE_temporal_sampling_create_view(EEVEE_Data *vedata)
-{
- EEVEE_EffectsInfo *effects = vedata->stl->effects;
- /* Create a sub view to disable clipping planes (if any). */
- const DRWView *default_view = DRW_view_default_get();
- float viewmat[4][4], winmat[4][4];
- DRW_view_viewmat_get(default_view, viewmat, false);
- DRW_view_winmat_get(default_view, winmat, false);
- effects->taa_view = DRW_view_create_sub(default_view, viewmat, winmat);
- DRW_view_clip_planes_set(effects->taa_view, NULL, 0);
-}
-
-int EEVEE_temporal_sampling_sample_count_get(const Scene *scene, const EEVEE_StorageList *stl)
-{
- const bool is_render = DRW_state_is_image_render();
- int sample_count = is_render ? scene->eevee.taa_render_samples : scene->eevee.taa_samples;
- int timesteps = is_render ? stl->g_data->render_timesteps : 1;
-
- sample_count = max_ii(0, sample_count);
- sample_count = (sample_count == 0) ? TAA_MAX_SAMPLE : sample_count;
- sample_count = divide_ceil_u(sample_count, timesteps);
-
- int dof_sample_count = EEVEE_depth_of_field_sample_count_get(stl->effects, sample_count, NULL);
- sample_count = dof_sample_count * divide_ceil_u(sample_count, dof_sample_count);
- return sample_count;
-}
-
-int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
-{
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
- int repro_flag = 0;
-
- if (!e_data.inited) {
- eevee_create_cdf_table_temporal_sampling();
- }
-
- /**
- * Reset for each "redraw". When rendering using ogl render,
- * we accumulate the redraw inside the drawing loop in eevee_draw_scene().
- */
- if (DRW_state_is_opengl_render()) {
- effects->taa_render_sample = 1;
- }
- effects->bypass_drawing = false;
-
- EEVEE_temporal_sampling_create_view(vedata);
-
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
-
- if ((scene_eval->eevee.taa_samples != 1) || DRW_state_is_image_render()) {
- float persmat[4][4];
-
- if (!DRW_state_is_image_render() && (scene_eval->eevee.flag & SCE_EEVEE_TAA_REPROJECTION)) {
- repro_flag = EFFECT_TAA_REPROJECT | EFFECT_VELOCITY_BUFFER | EFFECT_DEPTH_DOUBLE_BUFFER |
- EFFECT_DOUBLE_BUFFER | EFFECT_POST_BUFFER;
- effects->taa_reproject_sample = ((effects->taa_reproject_sample + 1) % 16);
- }
-
- /* Until we support reprojection, we need to make sure
- * that the history buffer contains correct information. */
- bool view_is_valid = stl->g_data->valid_double_buffer;
-
- view_is_valid = view_is_valid && (stl->g_data->view_updated == false);
-
- if (draw_ctx->evil_C != NULL) {
- struct wmWindowManager *wm = CTX_wm_manager(draw_ctx->evil_C);
- view_is_valid = view_is_valid && (ED_screen_animation_no_scrub(wm) == NULL);
- }
-
- effects->taa_total_sample = EEVEE_temporal_sampling_sample_count_get(scene_eval, stl);
-
- if (EEVEE_renderpasses_only_first_sample_pass_active(vedata)) {
- view_is_valid = false;
- effects->taa_total_sample = 1;
- }
-
- /* Motion blur steps could reset the sampling when camera is animated (see T79970). */
- if (!DRW_state_is_scene_render()) {
- DRW_view_persmat_get(NULL, persmat, false);
- view_is_valid = view_is_valid && compare_m4m4(persmat, effects->prev_drw_persmat, FLT_MIN);
- }
-
- /* Prevent ghosting from probe data. */
- view_is_valid = view_is_valid && (effects->prev_drw_support == DRW_state_draw_support()) &&
- (effects->prev_is_navigating == DRW_state_is_navigating());
- effects->prev_drw_support = DRW_state_draw_support();
- effects->prev_is_navigating = DRW_state_is_navigating();
-
- if (((effects->taa_total_sample == 0) ||
- (effects->taa_current_sample < effects->taa_total_sample)) ||
- (!view_is_valid) || DRW_state_is_image_render()) {
- if (view_is_valid) {
- /* Viewport rendering updates the matrices in `eevee_draw_scene` */
- if (!DRW_state_is_image_render()) {
- effects->taa_current_sample += 1;
- repro_flag = 0;
- }
- }
- else {
- effects->taa_current_sample = 1;
- }
- }
- else {
- const bool all_shaders_compiled = stl->g_data->queued_shaders_count_prev == 0;
- /* Fix Texture painting (see T79370) and shader compilation (see T78520). */
- if (DRW_state_is_navigating() || !all_shaders_compiled) {
- effects->taa_current_sample = 1;
- }
- else {
- effects->bypass_drawing = true;
- }
- }
-
- return repro_flag | EFFECT_TAA | EFFECT_DOUBLE_BUFFER | EFFECT_DEPTH_DOUBLE_BUFFER |
- EFFECT_POST_BUFFER;
- }
-
- effects->taa_current_sample = 1;
-
- return repro_flag;
-}
-
-void EEVEE_temporal_sampling_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
-{
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_TextureList *txl = vedata->txl;
- EEVEE_EffectsInfo *effects = stl->effects;
-
- if (effects->enabled_effects & EFFECT_TAA) {
- struct GPUShader *sh = EEVEE_shaders_taa_resolve_sh_get(effects->enabled_effects);
-
- DRW_PASS_CREATE(psl->taa_resolve, DRW_STATE_WRITE_COLOR);
- DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->taa_resolve);
-
- DRW_shgroup_uniform_texture_ref(grp, "colorHistoryBuffer", &txl->taa_history);
- DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &effects->source_buffer);
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
-
- if (effects->enabled_effects & EFFECT_TAA_REPROJECT) {
- DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
- DRW_shgroup_uniform_mat4(grp, "prevViewProjectionMatrix", effects->prev_drw_persmat);
- }
- else {
- DRW_shgroup_uniform_float(grp, "alpha", &effects->taa_alpha, 1);
- }
- DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
- }
-}
-
-void EEVEE_temporal_sampling_draw(EEVEE_Data *vedata)
-{
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_TextureList *txl = vedata->txl;
- EEVEE_FramebufferList *fbl = vedata->fbl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
-
- if ((effects->enabled_effects & (EFFECT_TAA | EFFECT_TAA_REPROJECT)) != 0) {
- if ((effects->enabled_effects & EFFECT_TAA) != 0 && effects->taa_current_sample != 1) {
- if (DRW_state_is_image_render()) {
- /* See EEVEE_temporal_sampling_init() for more details. */
- effects->taa_alpha = 1.0f / (float)(effects->taa_render_sample);
- }
- else {
- effects->taa_alpha = 1.0f / (float)(effects->taa_current_sample);
- }
-
- GPU_framebuffer_bind(effects->target_buffer);
- DRW_draw_pass(psl->taa_resolve);
-
- /* Restore the depth from sample 1. */
- GPU_framebuffer_blit(fbl->double_buffer_depth_fb, 0, fbl->main_fb, 0, GPU_DEPTH_BIT);
-
- SWAP_BUFFERS_TAA();
- }
- else {
- /* Save the depth buffer for the next frame.
- * This saves us from doing anything special
- * in the other mode engines. */
- GPU_framebuffer_blit(fbl->main_fb, 0, fbl->double_buffer_depth_fb, 0, GPU_DEPTH_BIT);
-
- /* Do reprojection for noise reduction */
- /* TODO: do AA jitter if in only render view. */
- if (!DRW_state_is_image_render() && (effects->enabled_effects & EFFECT_TAA_REPROJECT) != 0 &&
- stl->g_data->valid_taa_history) {
- GPU_framebuffer_bind(effects->target_buffer);
- DRW_draw_pass(psl->taa_resolve);
- SWAP_BUFFERS_TAA();
- }
- else {
- struct GPUFrameBuffer *source_fb = (effects->target_buffer == fbl->main_color_fb) ?
- fbl->effect_color_fb :
- fbl->main_color_fb;
- GPU_framebuffer_blit(source_fb, 0, fbl->taa_history_color_fb, 0, GPU_COLOR_BIT);
- }
- }
-
- /* Make each loop count when doing a render. */
- if (DRW_state_is_image_render()) {
- effects->taa_render_sample += 1;
- effects->taa_current_sample += 1;
- }
- else {
- if (!DRW_state_is_playback() &&
- ((effects->taa_total_sample == 0) ||
- (effects->taa_current_sample < effects->taa_total_sample))) {
- DRW_viewport_request_redraw();
- }
- }
-
- DRW_view_persmat_get(NULL, effects->prev_drw_persmat, false);
- }
-}
diff --git a/source/blender/draw/engines/eevee/eevee_velocity.cc b/source/blender/draw/engines/eevee/eevee_velocity.cc
new file mode 100644
index 00000000000..6c221b66aec
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_velocity.cc
@@ -0,0 +1,356 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * The velocity pass outputs motion vectors to use for either
+ * temporal re-projection or motion blur.
+ *
+ * It is the module that tracks the objects between frames updates.
+ */
+
+#include "BKE_duplilist.h"
+#include "BKE_object.h"
+#include "BLI_map.hh"
+#include "DEG_depsgraph_query.h"
+#include "DNA_rigidbody_types.h"
+
+#include "eevee_instance.hh"
+#include "eevee_renderpasses.hh"
+#include "eevee_shader.hh"
+#include "eevee_shader_shared.hh"
+#include "eevee_velocity.hh"
+#include "eevee_wrapper.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name VelocityModule
+ *
+ * \{ */
+
+void VelocityModule::init(void)
+{
+ if (inst_.is_viewport()) {
+ /* For viewport we sync when object is evaluated and we swap at init time.
+ * Use next step to store the current position. This one will become the previous step after
+ * next swapping. */
+ step_ = STEP_NEXT;
+ step_swap();
+ }
+
+ if (inst_.render && (inst_.render_passes.vector != nullptr)) {
+ /* No motion blur and the vector pass was requested. Do the step sync here. */
+ const Scene *scene = inst_.scene;
+ float initial_time = scene->r.cfra + scene->r.subframe;
+ step_sync(STEP_PREVIOUS, initial_time - 1.0f);
+ step_sync(STEP_NEXT, initial_time + 1.0f);
+ inst_.set_time(initial_time);
+ }
+}
+
+static void step_object_sync_render(void *velocity,
+ Object *ob,
+ RenderEngine *UNUSED(engine),
+ Depsgraph *UNUSED(depsgraph))
+{
+ ObjectKey object_key(ob);
+ reinterpret_cast<VelocityModule *>(velocity)->step_object_sync(ob, object_key);
+}
+
+void VelocityModule::step_sync(eStep step, float time)
+{
+ inst_.set_time(time);
+ step_ = step;
+ step_camera_sync();
+ DRW_render_object_iter(this, inst_.render, inst_.depsgraph, step_object_sync_render);
+}
+
+void VelocityModule::step_camera_sync()
+{
+ if (!inst_.is_viewport()) {
+ inst_.camera.sync();
+ }
+
+ if (step_ == STEP_NEXT) {
+ camera_step.next = inst_.camera.data_get();
+ }
+ else if (step_ == STEP_PREVIOUS) {
+ camera_step.prev = inst_.camera.data_get();
+ }
+}
+
+/* Gather motion data from all objects in the scene. */
+void VelocityModule::step_object_sync(Object *ob, ObjectKey &object_key)
+{
+ if (!object_has_velocity(ob) && !object_is_deform(ob)) {
+ return;
+ }
+
+ auto add_cb = [&]() {
+ inst_.sampling.reset();
+ return new VelocityObjectBuf();
+ };
+ auto data = objects_steps.lookup_or_add_cb(object_key, add_cb);
+
+ if (step_ == STEP_NEXT) {
+ copy_m4_m4(data->next_object_mat, ob->obmat);
+ }
+ else if (step_ == STEP_PREVIOUS) {
+ copy_m4_m4(data->prev_object_mat, ob->obmat);
+ }
+}
+
+/* Moves next frame data to previous frame data. Nullify next frame data. */
+void VelocityModule::step_swap(void)
+{
+ for (VelocityObjectBuf *data : objects_steps.values()) {
+ copy_m4_m4(data->prev_object_mat, data->next_object_mat);
+ /* Important: This let us known if object is missing from the next time step. */
+ zero_m4(data->next_object_mat);
+ }
+ camera_step.prev = static_cast<CameraData>(camera_step.next);
+}
+
+void VelocityModule::begin_sync(void)
+{
+ if (inst_.is_viewport()) {
+ step_camera_sync();
+ }
+}
+
+/* This is the end of the current frame sync. Not the step_sync. */
+void VelocityModule::end_sync(void)
+{
+ Vector<ObjectKey, 1> deleted_keys;
+
+ for (auto item : objects_steps.items()) {
+ /* Detect object deletion. Only do this on viewport as STEP_NEXT means current step. */
+ if (inst_.is_viewport() && is_zero_m4(item.value->next_object_mat)) {
+ deleted_keys.append(item.key);
+ delete item.value;
+ }
+ else {
+ item.value->push_update();
+ }
+ }
+
+ if (deleted_keys.size() > 0) {
+ inst_.sampling.reset();
+ }
+
+ for (auto key : deleted_keys) {
+ objects_steps.remove(key);
+ }
+
+ camera_step.prev.push_update();
+ camera_step.next.push_update();
+}
+
+bool VelocityModule::object_has_velocity(const Object *ob)
+{
+#if 0
+ RigidBodyOb *rbo = ob->rigidbody_object;
+ /* Active rigidbody objects only, as only those are affected by sim. */
+ const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE));
+ /* For now we assume dupli objects are moving. */
+ const bool is_dupli = (ob->base_flag & BASE_FROM_DUPLI) != 0;
+ const bool object_moves = is_dupli || has_rigidbody || BKE_object_moves_in_time(ob, true);
+#else
+ UNUSED_VARS(ob);
+ /* BKE_object_moves_in_time does not work in some cases.
+ * Better detect non moving object after evaluation. */
+ const bool object_moves = true;
+#endif
+ return object_moves;
+}
+
+bool VelocityModule::object_is_deform(const Object *ob)
+{
+ RigidBodyOb *rbo = ob->rigidbody_object;
+ /* Active rigidbody objects only, as only those are affected by sim. */
+ const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE));
+ const bool is_deform = BKE_object_is_deform_modified(inst_.scene, (Object *)ob) ||
+ (has_rigidbody && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0);
+
+ return is_deform;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name VelocityPass
+ *
+ * Draws velocity data from VelocityModule module to a framebuffer / texture.
+ * \{ */
+
+void VelocityPass::sync(void)
+{
+ VelocityModule &velocity = inst_.velocity;
+ {
+ /* Outputs camera motion vector. */
+ /* TODO(fclem) Ideally, we should run this only where the motion vectors were not written.
+ * But without imageLoadStore, we cannot do that without another buffer. */
+ DRWState state = DRW_STATE_WRITE_COLOR;
+ DRW_PASS_CREATE(camera_ps_, state);
+ GPUShader *sh = inst_.shaders.static_shader_get(VELOCITY_CAMERA);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, camera_ps_);
+ DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
+ DRW_shgroup_uniform_block(grp, "camera_prev_block", velocity.camera_step.prev.ubo_get());
+ DRW_shgroup_uniform_block(grp, "camera_next_block", velocity.camera_step.next.ubo_get());
+ DRW_shgroup_uniform_block(grp, "camera_curr_block", inst_.camera.ubo_get());
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+ }
+ {
+ /* Animated objects are rendered and output the correct motion vector. */
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL;
+ DRW_PASS_CREATE(object_ps_, state);
+ {
+ GPUShader *sh = inst_.shaders.static_shader_get(VELOCITY_MESH);
+ DRWShadingGroup *grp = mesh_grp_ = DRW_shgroup_create(sh, object_ps_);
+ DRW_shgroup_uniform_block(grp, "camera_prev_block", velocity.camera_step.prev.ubo_get());
+ DRW_shgroup_uniform_block(grp, "camera_next_block", velocity.camera_step.next.ubo_get());
+ DRW_shgroup_uniform_block(grp, "camera_curr_block", inst_.camera.ubo_get());
+ }
+ }
+}
+
+void VelocityPass::mesh_add(Object *ob, ObjectHandle &handle)
+{
+ if (inst_.is_viewport()) {
+ /* FIXME(fclem) As we are using original objects pointers, there is a chance the previous
+ * object key matches a totally different object if the scene was changed by user or python
+ * callback. In this case, we cannot correctly match objects between updates.
+ * What this means is that there will be incorrect motion vectors for these objects.
+ * We live with that until we have a correct way of identifying new objects. */
+ if (handle.recalc & ID_RECALC_TRANSFORM) {
+ inst_.velocity.step_object_sync(ob, handle.object_key);
+ }
+ }
+
+ VelocityObjectBuf **data_ptr = inst_.velocity.objects_steps.lookup_ptr(handle.object_key);
+
+ if (data_ptr == nullptr) {
+ return;
+ }
+
+ VelocityObjectBuf *data = *data_ptr;
+
+ GPUBatch *geom = DRW_cache_object_surface_get(ob);
+ if (geom == NULL) {
+ return;
+ }
+
+ if (!inst_.is_viewport()) {
+ /* Fill missing matrices if the object was hidden in previous or next frame. */
+ if (is_zero_m4(data->prev_object_mat)) {
+ copy_m4_m4(data->prev_object_mat, ob->obmat);
+ }
+
+ /* Avoid drawing object that has no motions since object_moves is always true. */
+ if (/* !mb_geom->use_deform && */ /* Object deformation can happen without transform. */
+ equals_m4m4(data->prev_object_mat, ob->obmat)) {
+ return;
+ }
+ }
+ else {
+ /* Fill missing matrices if the object was hidden in previous or next frame. */
+ if (is_zero_m4(data->prev_object_mat)) {
+ copy_m4_m4(data->prev_object_mat, ob->obmat);
+ }
+ if (is_zero_m4(data->next_object_mat)) {
+ copy_m4_m4(data->next_object_mat, ob->obmat);
+ }
+
+ // if (mb_geom->use_deform) {
+ // /* Keep to modify later (after init). */
+ // mb_geom->batch = geom;
+ // }
+
+ /* Avoid drawing object that has no motions since object_moves is always true. */
+ if (/* !mb_geom->use_deform && */ /* Object deformation can happen without transform. */
+ equals_m4m4(data->prev_object_mat, ob->obmat) &&
+ equals_m4m4(data->next_object_mat, ob->obmat)) {
+ return;
+ }
+ }
+
+ /* TODO(fclem) Use the same layout as modelBlock from draw so we can reuse the same offset
+ * and avoid the overhead of 1 shading group and one UBO per object. */
+ DRWShadingGroup *grp = DRW_shgroup_create_sub(mesh_grp_);
+ DRW_shgroup_uniform_block(grp, "object_block", data->ubo_get());
+ DRW_shgroup_call(grp, geom, ob);
+}
+
+void VelocityPass::render_objects(void)
+{
+ DRW_draw_pass(object_ps_);
+}
+
+void VelocityPass::resolve_camera_motion(GPUTexture *depth_tx)
+{
+ input_depth_tx_ = depth_tx;
+ DRW_draw_pass(camera_ps_);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name VelocityPass
+ *
+ * Draws velocity data from VelocityModule module to a framebuffer / texture.
+ * \{ */
+
+void Velocity::sync(int extent[2])
+{
+ /* HACK: View name should be unique and static.
+ * With this, we can reuse the same texture across views. */
+ DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
+
+ /* TODO(fclem) Only allocate if needed. RG16F when only doing reprojection. */
+ velocity_camera_tx_ = DRW_texture_pool_query_2d(UNPACK2(extent), GPU_RGBA16F, owner);
+ /* TODO(fclem) Only allocate if needed. RG16F when only doing motion blur post fx in
+ * panoramic camera. */
+ velocity_view_tx_ = DRW_texture_pool_query_2d(UNPACK2(extent), GPU_RGBA16F, owner);
+
+ velocity_only_fb_.ensure(GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(velocity_camera_tx_),
+ GPU_ATTACHMENT_TEXTURE(velocity_view_tx_));
+}
+
+void Velocity::render(GPUTexture *depth_tx)
+{
+ DRW_stats_group_start("Velocity");
+
+ GPU_framebuffer_bind(velocity_only_fb_);
+ inst_.shading_passes.velocity.resolve_camera_motion(depth_tx);
+
+ velocity_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx),
+ GPU_ATTACHMENT_TEXTURE(velocity_camera_tx_),
+ GPU_ATTACHMENT_TEXTURE(velocity_view_tx_));
+
+ GPU_framebuffer_bind(velocity_fb_);
+ inst_.shading_passes.velocity.render_objects();
+
+ DRW_stats_group_end();
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_velocity.hh b/source/blender/draw/engines/eevee/eevee_velocity.hh
new file mode 100644
index 00000000000..5829737fc43
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_velocity.hh
@@ -0,0 +1,171 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * The velocity pass outputs motion vectors to use for either
+ * temporal re-projection or motion blur.
+ *
+ * It is the module that tracks the objects between frames updates.
+ */
+
+#pragma once
+
+#include "BLI_map.hh"
+
+#include "eevee_id_map.hh"
+#include "eevee_renderpasses.hh"
+#include "eevee_shader_shared.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name VelocityModule
+ *
+ * \{ */
+
+/** Container for scene velocity data. */
+class VelocityModule {
+ public:
+ enum eStep {
+ STEP_PREVIOUS = 0,
+ STEP_NEXT = 1,
+ STEP_CURRENT = 2,
+ };
+
+ /** Map an object key to a velocity data. */
+ Map<ObjectKey, VelocityObjectBuf *> objects_steps;
+ struct {
+ /** Copies of camera data. One for previous and one for next time step. */
+ StructBuffer<CameraData> prev, next;
+ } camera_step;
+
+ private:
+ Instance &inst_;
+
+ eStep step_;
+
+ public:
+ VelocityModule(Instance &inst) : inst_(inst){};
+
+ ~VelocityModule()
+ {
+ for (VelocityObjectBuf *data : objects_steps.values()) {
+ delete data;
+ }
+ }
+
+ void init(void);
+
+ void step_camera_sync(void);
+ void step_sync(eStep step, float time);
+
+ /* Gather motion data from all objects in the scene. */
+ void step_object_sync(Object *ob, ObjectKey &ob_key);
+
+ /* Moves next frame data to previous frame data. Nullify next frame data. */
+ void step_swap(void);
+
+ void begin_sync(void);
+ void end_sync(void);
+
+ private:
+ bool object_has_velocity(const Object *ob);
+ bool object_is_deform(const Object *ob);
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name VelocityPass
+ *
+ * Draws velocity data from VelocityModule module to a framebuffer / texture.
+ * \{ */
+
+class VelocityPass {
+ private:
+ Instance &inst_;
+
+ DRWPass *object_ps_ = nullptr;
+ DRWPass *camera_ps_ = nullptr;
+
+ /** Shading groups from object_ps_ */
+ DRWShadingGroup *mesh_grp_;
+
+ /** Reference only. Not owned. */
+ GPUTexture *input_depth_tx_;
+
+ public:
+ VelocityPass(Instance &inst) : inst_(inst){};
+
+ void sync(void);
+
+ void mesh_add(Object *ob, ObjectHandle &handle);
+
+ void render_objects(void);
+ void resolve_camera_motion(GPUTexture *depth_tx);
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Velocity
+ *
+ * \{ */
+
+/**
+ * Per view module.
+ */
+class Velocity {
+ private:
+ Instance &inst_;
+
+ StringRefNull view_name_;
+
+ /** Owned resources. */
+ eevee::Framebuffer velocity_fb_;
+ eevee::Framebuffer velocity_only_fb_;
+ /** Draw resources. Not owned. */
+ GPUTexture *velocity_camera_tx_ = nullptr;
+ GPUTexture *velocity_view_tx_ = nullptr;
+
+ public:
+ Velocity(Instance &inst, const char *name) : inst_(inst), view_name_(name){};
+ ~Velocity(){};
+
+ void sync(int extent[2]);
+
+ void render(GPUTexture *depth_tx);
+
+ /**
+ * Getters
+ **/
+ GPUTexture *view_vectors_get(void) const
+ {
+ return (velocity_view_tx_ != nullptr) ? velocity_view_tx_ : velocity_camera_tx_;
+ }
+ GPUTexture *camera_vectors_get(void) const
+ {
+ return velocity_camera_tx_;
+ }
+};
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_view.cc b/source/blender/draw/engines/eevee/eevee_view.cc
new file mode 100644
index 00000000000..d147fabc3dc
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_view.cc
@@ -0,0 +1,262 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * A view is either:
+ * - The entire main view.
+ * - A fragment of the main view (for panoramic projections).
+ * - A shadow map view.
+ * - A lightprobe view (either planar, cubemap, irradiance grid).
+ *
+ * A pass is a container for scene data. It is view agnostic but has specific logic depending on
+ * its type. Passes are shared between views.
+ */
+
+#include "BKE_global.h"
+#include "DRW_render.h"
+
+#include "eevee_instance.hh"
+
+#include "eevee_view.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name ShadingView
+ * \{ */
+
+void ShadingView::init()
+{
+ dof_.init();
+ mb_.init();
+}
+
+void ShadingView::sync(ivec2 render_extent_)
+{
+ if (inst_.camera.is_panoramic()) {
+ int64_t render_pixel_count = render_extent_.x * (int64_t)render_extent_.y;
+ /* Divide pixel count between the 6 views. Rendering to a square target. */
+ extent_[0] = extent_[1] = ceilf(sqrtf(1 + (render_pixel_count / 6)));
+ /* TODO(fclem) Clip unused views heres. */
+ is_enabled_ = true;
+ }
+ else {
+ extent_ = render_extent_;
+ /* Only enable -Z view. */
+ is_enabled_ = (StringRefNull(name_) == "negZ_view");
+ }
+
+ if (!is_enabled_) {
+ return;
+ }
+
+ /* Create views. */
+ const CameraData &data = inst_.camera.data_get();
+
+ float viewmat[4][4], winmat[4][4];
+ const float(*viewmat_p)[4] = viewmat, (*winmat_p)[4] = winmat;
+ if (inst_.camera.is_panoramic()) {
+ /* TODO(fclem) Overscans. */
+ /* For now a mandatory 5% overscan for DoF. */
+ float side = data.clip_near * 1.05f;
+ float near = data.clip_near;
+ float far = data.clip_far;
+ perspective_m4(winmat, -side, side, -side, side, near, far);
+ mul_m4_m4m4(viewmat, face_matrix_, data.viewmat);
+ }
+ else {
+ viewmat_p = data.viewmat;
+ winmat_p = data.winmat;
+ }
+
+ main_view_ = DRW_view_create(viewmat_p, winmat_p, nullptr, nullptr, nullptr);
+ sub_view_ = DRW_view_create_sub(main_view_, viewmat_p, winmat_p);
+ render_view_ = DRW_view_create_sub(main_view_, viewmat_p, winmat_p);
+
+ dof_.sync(winmat_p, extent_);
+ mb_.sync(extent_);
+ velocity_.sync(extent_);
+ rt_buffer_opaque_.sync(extent_);
+ rt_buffer_refract_.sync(extent_);
+
+ {
+ /* Query temp textures and create framebuffers. */
+ /* HACK: View name should be unique and static.
+ * With this, we can reuse the same texture across views. */
+ DrawEngineType *owner = (DrawEngineType *)name_;
+
+ depth_tx_ = DRW_texture_pool_query_2d(UNPACK2(extent_), GPU_DEPTH24_STENCIL8, owner);
+ combined_tx_ = DRW_texture_pool_query_2d(UNPACK2(extent_), GPU_RGBA16F, owner);
+ /* TODO(fclem) Only allocate if needed. */
+ postfx_tx_ = DRW_texture_pool_query_2d(UNPACK2(extent_), GPU_RGBA16F, owner);
+
+ view_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx_), GPU_ATTACHMENT_TEXTURE(combined_tx_));
+
+ gbuffer_.sync(depth_tx_, combined_tx_, owner);
+ }
+}
+
+void ShadingView::render(void)
+{
+ if (!is_enabled_) {
+ return;
+ }
+
+ float color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+
+ update_view();
+
+ DRW_stats_group_start(name_);
+ DRW_view_set_active(render_view_);
+
+ GPU_framebuffer_bind(view_fb_);
+ GPU_framebuffer_clear_color_depth(view_fb_, color, 1.0f);
+
+ if (inst_.lookdev.render_background() == false) {
+ inst_.shading_passes.background.render();
+ }
+
+ inst_.shading_passes.deferred.render(render_view_,
+ gbuffer_,
+ hiz_front_,
+ hiz_back_,
+ rt_buffer_opaque_,
+ rt_buffer_refract_,
+ view_fb_);
+
+ inst_.lightprobes.draw_cache_display();
+
+ inst_.lookdev.render_overlay(view_fb_);
+
+ inst_.shading_passes.forward.render(render_view_, gbuffer_, hiz_front_, view_fb_);
+
+ inst_.lights.debug_draw(view_fb_, hiz_front_);
+ inst_.shadows.debug_draw(view_fb_, hiz_front_);
+
+ velocity_.render(depth_tx_);
+
+ if (inst_.render_passes.vector) {
+ inst_.render_passes.vector->accumulate(velocity_.camera_vectors_get(), sub_view_);
+ }
+
+ GPUTexture *final_radiance_tx = render_post(combined_tx_);
+
+ if (inst_.render_passes.combined) {
+ inst_.render_passes.combined->accumulate(final_radiance_tx, sub_view_);
+ }
+
+ if (inst_.render_passes.depth) {
+ inst_.render_passes.depth->accumulate(depth_tx_, sub_view_);
+ }
+
+ DRW_stats_group_end();
+}
+
+GPUTexture *ShadingView::render_post(GPUTexture *input_tx)
+{
+ GPUTexture *velocity_tx = velocity_.view_vectors_get();
+ GPUTexture *output_tx = postfx_tx_;
+ /* Swapping is done internally. Actual output is set to the next input. */
+ dof_.render(depth_tx_, &input_tx, &output_tx);
+ mb_.render(depth_tx_, velocity_tx, &input_tx, &output_tx);
+ return input_tx;
+}
+
+void ShadingView::update_view(void)
+{
+ float viewmat[4][4], winmat[4][4];
+ DRW_view_viewmat_get(main_view_, viewmat, false);
+ DRW_view_winmat_get(main_view_, winmat, false);
+
+ /* Anti-Aliasing / Super-Sampling jitter. */
+ float jitter_u = 2.0f * (inst_.sampling.rng_get(SAMPLING_FILTER_U) - 0.5f) / extent_[0];
+ float jitter_v = 2.0f * (inst_.sampling.rng_get(SAMPLING_FILTER_V) - 0.5f) / extent_[1];
+
+ window_translate_m4(winmat, winmat, jitter_u, jitter_v);
+ DRW_view_update_sub(sub_view_, viewmat, winmat);
+
+ /* FIXME(fclem): The offset may be is noticeably large and the culling might make object pop
+ * out of the blurring radius. To fix this, use custom enlarged culling matrix. */
+ dof_.jitter_apply(winmat, viewmat);
+ DRW_view_update_sub(render_view_, viewmat, winmat);
+
+ inst_.lightprobes.set_view(render_view_, extent_);
+ inst_.lights.set_view(render_view_, extent_);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name LightProbeView
+ * \{ */
+
+void LightProbeView::sync(Texture &color_tx,
+ Texture &depth_tx,
+ const mat4 winmat,
+ const mat4 viewmat,
+ bool is_only_background)
+{
+ mat4 facemat;
+ mul_m4_m4m4(facemat, face_matrix_, viewmat);
+
+ is_only_background_ = is_only_background;
+ extent_ = ivec2(color_tx.width());
+ view_ = DRW_view_create(facemat, winmat, nullptr, nullptr, nullptr);
+ view_fb_.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer_),
+ GPU_ATTACHMENT_TEXTURE_LAYER(color_tx, layer_));
+
+ if (!is_only_background_) {
+ /* Query temp textures and create framebuffers. */
+ /* HACK: View name should be unique and static.
+ * With this, we can reuse the same texture across views. */
+ DrawEngineType *owner = (DrawEngineType *)name_;
+ gbuffer_.sync(depth_tx, color_tx, owner, layer_);
+ rt_buffer_opaque_.sync(extent_);
+ rt_buffer_refract_.sync(extent_);
+ }
+}
+
+void LightProbeView::render(void)
+{
+ if (!is_only_background_) {
+ inst_.lightprobes.set_view(view_, extent_);
+ inst_.lights.set_view(view_, extent_, false);
+ }
+
+ DRW_stats_group_start(name_);
+ DRW_view_set_active(view_);
+
+ GPU_framebuffer_bind(view_fb_);
+
+ inst_.shading_passes.background.render();
+
+ if (!is_only_background_) {
+ GPU_framebuffer_clear_depth(view_fb_, 1.0f);
+
+ inst_.shading_passes.deferred.render(
+ view_, gbuffer_, hiz_front_, hiz_back_, rt_buffer_opaque_, rt_buffer_refract_, view_fb_);
+ inst_.shading_passes.forward.render(view_, gbuffer_, hiz_front_, view_fb_);
+ }
+ DRW_stats_group_end();
+}
+
+/** \} */
+
+} // namespace blender::eevee \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/eevee_view.hh b/source/blender/draw/engines/eevee/eevee_view.hh
new file mode 100644
index 00000000000..4f1aae0d825
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_view.hh
@@ -0,0 +1,239 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * A view is either:
+ * - The entire main view.
+ * - A fragment of the main view (for panoramic projections).
+ * - A shadow map view.
+ * - A lightprobe view (either planar, cubemap, irradiance grid).
+ *
+ * A pass is a container for scene data. It is view agnostic but has specific logic depending on
+ * its type. Passes are shared between views.
+ */
+
+#pragma once
+
+#include "GPU_framebuffer.h"
+#include "GPU_texture.h"
+
+#include "DRW_render.h"
+
+#include "eevee_depth_of_field.hh"
+#include "eevee_hizbuffer.hh"
+#include "eevee_motion_blur.hh"
+#include "eevee_raytracing.hh"
+#include "eevee_renderpasses.hh"
+#include "eevee_shader.hh"
+#include "eevee_shading.hh"
+#include "eevee_velocity.hh"
+
+namespace blender::eevee {
+
+class Instance;
+
+/* -------------------------------------------------------------------- */
+/** \name ShadingView
+ *
+ * Render the scene and fill all render passes data.
+ * \{ */
+
+class ShadingView {
+ private:
+ Instance &inst_;
+ /** Static string pointer. Used as debug name and as UUID for texture pool. */
+ const char *name_;
+ /** Matrix to apply to the viewmat. */
+ const float (*face_matrix_)[4];
+
+ /** Post-fx modules. */
+ DepthOfField dof_;
+ MotionBlur mb_;
+ Velocity velocity_;
+
+ /** GBuffer for deferred passes. */
+ GBuffer gbuffer_;
+ /** HiZBuffer for screenspace effects. */
+ HiZBuffer hiz_front_, hiz_back_;
+ /** Raytracing persistent buffers. Only opaque and refraction can have surface tracing. */
+ RaytraceBuffer rt_buffer_opaque_;
+ RaytraceBuffer rt_buffer_refract_;
+
+ /** Owned resources. */
+ eevee::Framebuffer view_fb_;
+ /** Draw resources. Not owned. */
+ GPUTexture *combined_tx_ = nullptr;
+ GPUTexture *depth_tx_ = nullptr;
+ GPUTexture *postfx_tx_ = nullptr;
+
+ /** Main views is created from the camera (or is from the viewport). It is not jittered. */
+ DRWView *main_view_ = nullptr;
+ /** Sub views is jittered versions or the main views. This allows jitter updates without trashing
+ * the visibility culling cache. */
+ DRWView *sub_view_ = nullptr;
+ /** Same as sub_view_ but has Depth Of Field jitter applied. */
+ DRWView *render_view_ = nullptr;
+
+ /** Render size of the view. Can change between scene sample eval. */
+ ivec2 extent_ = {-1, -1};
+
+ bool is_enabled_ = false;
+
+ public:
+ ShadingView(Instance &inst, const char *name, const float (*face_matrix)[4])
+ : inst_(inst),
+ name_(name),
+ face_matrix_(face_matrix),
+ dof_(inst, name),
+ mb_(inst, name),
+ velocity_(inst, name),
+ hiz_front_(inst),
+ hiz_back_(inst),
+ rt_buffer_opaque_(inst),
+ rt_buffer_refract_(inst){};
+
+ ~ShadingView(){};
+
+ void init(void);
+
+ void sync(ivec2 render_extent_);
+
+ void render(void);
+
+ GPUTexture *render_post(GPUTexture *input_tx);
+
+ private:
+ void update_view(void);
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name LightProbeView
+ *
+ * Render the scene to a lightprobe target cubemap face.
+ * \{ */
+
+class LightProbeView {
+ private:
+ Instance &inst_;
+ /** Static string pointer. Used as debug name and as UUID for texture pool. */
+ const char *name_;
+ /** Matrix to apply to the viewmat. */
+ const float (*face_matrix_)[4];
+ /** GBuffer for deferred passes. */
+ GBuffer gbuffer_;
+ /** HiZBuffer for screenspace effects. */
+ HiZBuffer hiz_front_, hiz_back_;
+ /** Raytracing persistent buffers. Only opaque and refraction can have surface tracing. */
+ RaytraceBuffer rt_buffer_opaque_;
+ RaytraceBuffer rt_buffer_refract_;
+ /** Owned resources. */
+ Framebuffer view_fb_;
+ /** DRWView of this face. */
+ DRWView *view_ = nullptr;
+ /** Render size of the view. */
+ ivec2 extent_ = {-1, -1};
+
+ int layer_ = 0;
+ bool is_only_background_ = false;
+
+ public:
+ LightProbeView(Instance &inst, const char *name, const float (*face_matrix)[4], int layer_)
+ : inst_(inst),
+ name_(name),
+ face_matrix_(face_matrix),
+ hiz_front_(inst),
+ hiz_back_(inst),
+ rt_buffer_opaque_(inst),
+ rt_buffer_refract_(inst),
+ layer_(layer_){};
+
+ ~LightProbeView(){};
+
+ void sync(Texture &color_tx,
+ Texture &depth_tx,
+ const mat4 winmat,
+ const mat4 viewmat,
+ bool is_only_background);
+
+ void render(void);
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Main View
+ *
+ * \{ */
+
+/**
+ * Container for all views needed to render the final image.
+ * We might need up to 6 views for panoramic cameras.
+ */
+class MainView {
+ private:
+ ShadingView shading_views_[6];
+ /** Internal render size. */
+ int render_extent_[2];
+
+ public:
+ MainView(Instance &inst)
+ : shading_views_{{inst, "posX_view", cubeface_mat[0]},
+ {inst, "negX_view", cubeface_mat[1]},
+ {inst, "posY_view", cubeface_mat[2]},
+ {inst, "negY_view", cubeface_mat[3]},
+ {inst, "posZ_view", cubeface_mat[4]},
+ {inst, "negZ_view", cubeface_mat[5]}}
+ {
+ }
+
+ void init(const ivec2 full_extent_)
+ {
+ /* TODO(fclem) parameter hidden in experimental. We need to figure out mipmap bias to preserve
+ * texture crispiness. */
+ float resolution_scale = 1.0f;
+ for (int i = 0; i < 2; i++) {
+ render_extent_[i] = max_ii(1, roundf(full_extent_[i] * resolution_scale));
+ }
+
+ for (auto i : IndexRange(ARRAY_SIZE(shading_views_))) {
+ shading_views_[i].init();
+ }
+ }
+
+ void sync(void)
+ {
+ for (auto i : IndexRange(ARRAY_SIZE(shading_views_))) {
+ shading_views_[i].sync(render_extent_);
+ }
+ }
+
+ void render(void)
+ {
+ for (auto i : IndexRange(ARRAY_SIZE(shading_views_))) {
+ shading_views_[i].render();
+ }
+ }
+};
+
+/** \} */
+
+} // namespace blender::eevee \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c
deleted file mode 100644
index ac9abcca16b..00000000000
--- a/source/blender/draw/engines/eevee/eevee_volumes.c
+++ /dev/null
@@ -1,856 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Copyright 2016, Blender Foundation.
- */
-
-/** \file
- * \ingroup draw_engine
- *
- * Volumetric effects rendering using frostbite approach.
- */
-
-#include "DRW_render.h"
-
-#include "BLI_listbase.h"
-#include "BLI_rand.h"
-#include "BLI_string_utils.h"
-
-#include "DNA_fluid_types.h"
-#include "DNA_object_force_types.h"
-#include "DNA_volume_types.h"
-#include "DNA_world_types.h"
-
-#include "BKE_fluid.h"
-#include "BKE_global.h"
-#include "BKE_mesh.h"
-#include "BKE_modifier.h"
-#include "BKE_volume.h"
-#include "BKE_volume_render.h"
-
-#include "ED_screen.h"
-
-#include "DEG_depsgraph_query.h"
-
-#include "GPU_capabilities.h"
-#include "GPU_material.h"
-#include "GPU_texture.h"
-#include "eevee_private.h"
-
-static struct {
- GPUTexture *depth_src;
-
- GPUTexture *dummy_zero;
- GPUTexture *dummy_one;
- GPUTexture *dummy_flame;
-
- GPUTexture *dummy_scatter;
- GPUTexture *dummy_transmit;
-
- /* List of all fluid simulation / smoke domains rendered within this frame. */
- ListBase smoke_domains;
-} e_data = {NULL}; /* Engine data */
-
-static void eevee_create_textures_volumes(void)
-{
- const float zero[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- e_data.dummy_zero = DRW_texture_create_3d(1, 1, 1, GPU_RGBA8, DRW_TEX_WRAP, zero);
-
- const float one[4] = {1.0f, 1.0f, 1.0f, 1.0f};
- e_data.dummy_one = DRW_texture_create_3d(1, 1, 1, GPU_RGBA8, DRW_TEX_WRAP, one);
-
- const float flame = 0.0f;
- e_data.dummy_flame = DRW_texture_create_3d(1, 1, 1, GPU_R8, DRW_TEX_WRAP, &flame);
-}
-
-static GPUTexture *eevee_volume_default_texture(eGPUVolumeDefaultValue default_value)
-{
- switch (default_value) {
- case GPU_VOLUME_DEFAULT_0:
- return e_data.dummy_zero;
- case GPU_VOLUME_DEFAULT_1:
- return e_data.dummy_one;
- }
-
- return e_data.dummy_zero;
-}
-
-void EEVEE_volumes_set_jitter(EEVEE_ViewLayerData *sldata, uint current_sample)
-{
- EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
-
- double ht_point[3];
- double ht_offset[3] = {0.0, 0.0};
- const uint ht_primes[3] = {3, 7, 2};
-
- BLI_halton_3d(ht_primes, ht_offset, current_sample, ht_point);
-
- common_data->vol_jitter[0] = (float)ht_point[0];
- common_data->vol_jitter[1] = (float)ht_point[1];
- common_data->vol_jitter[2] = (float)ht_point[2];
-}
-
-void EEVEE_volumes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
-{
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_FramebufferList *fbl = vedata->fbl;
- EEVEE_TextureList *txl = vedata->txl;
- EEVEE_EffectsInfo *effects = stl->effects;
- EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
-
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
-
- const float *viewport_size = DRW_viewport_size_get();
-
- const int tile_size = scene_eval->eevee.volumetric_tile_size;
-
- /* Find Froxel Texture resolution. */
- int tex_size[3];
-
- tex_size[0] = (int)ceilf(fmaxf(1.0f, viewport_size[0] / (float)tile_size));
- tex_size[1] = (int)ceilf(fmaxf(1.0f, viewport_size[1] / (float)tile_size));
- tex_size[2] = max_ii(scene_eval->eevee.volumetric_samples, 1);
-
- common_data->vol_coord_scale[0] = viewport_size[0] / (float)(tile_size * tex_size[0]);
- common_data->vol_coord_scale[1] = viewport_size[1] / (float)(tile_size * tex_size[1]);
- common_data->vol_coord_scale[2] = 1.0f / viewport_size[0];
- common_data->vol_coord_scale[3] = 1.0f / viewport_size[1];
-
- /* TODO: compute snap to maxZBuffer for clustered rendering. */
- if ((common_data->vol_tex_size[0] != tex_size[0]) ||
- (common_data->vol_tex_size[1] != tex_size[1]) ||
- (common_data->vol_tex_size[2] != tex_size[2])) {
- DRW_TEXTURE_FREE_SAFE(txl->volume_prop_scattering);
- DRW_TEXTURE_FREE_SAFE(txl->volume_prop_extinction);
- DRW_TEXTURE_FREE_SAFE(txl->volume_prop_emission);
- DRW_TEXTURE_FREE_SAFE(txl->volume_prop_phase);
- DRW_TEXTURE_FREE_SAFE(txl->volume_scatter);
- DRW_TEXTURE_FREE_SAFE(txl->volume_transmit);
- DRW_TEXTURE_FREE_SAFE(txl->volume_scatter_history);
- DRW_TEXTURE_FREE_SAFE(txl->volume_transmit_history);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_fb);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_scat_fb);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_integ_fb);
- copy_v3_v3_int(common_data->vol_tex_size, tex_size);
-
- common_data->vol_inv_tex_size[0] = 1.0f / (float)(tex_size[0]);
- common_data->vol_inv_tex_size[1] = 1.0f / (float)(tex_size[1]);
- common_data->vol_inv_tex_size[2] = 1.0f / (float)(tex_size[2]);
- }
-
- /* Like frostbite's paper, 5% blend of the new frame. */
- common_data->vol_history_alpha = (txl->volume_prop_scattering == NULL) ? 0.0f : 0.95f;
-
- /* Temporal Super sampling jitter */
- uint ht_primes[3] = {3, 7, 2};
- uint current_sample = 0;
-
- /* If TAA is in use do not use the history buffer. */
- bool do_taa = ((effects->enabled_effects & EFFECT_TAA) != 0);
-
- if (draw_ctx->evil_C != NULL) {
- struct wmWindowManager *wm = CTX_wm_manager(draw_ctx->evil_C);
- do_taa = do_taa && (ED_screen_animation_no_scrub(wm) == NULL);
- }
-
- if (do_taa) {
- common_data->vol_history_alpha = 0.0f;
- current_sample = effects->taa_current_sample - 1;
- effects->volume_current_sample = -1;
- }
- else if (DRW_state_is_image_render()) {
- const uint max_sample = (ht_primes[0] * ht_primes[1] * ht_primes[2]);
- current_sample = effects->volume_current_sample = (effects->volume_current_sample + 1) %
- max_sample;
- if (current_sample != max_sample - 1) {
- DRW_viewport_request_redraw();
- }
- }
-
- EEVEE_volumes_set_jitter(sldata, current_sample);
-
- float integration_start = scene_eval->eevee.volumetric_start;
- float integration_end = scene_eval->eevee.volumetric_end;
- effects->volume_light_clamp = scene_eval->eevee.volumetric_light_clamp;
- common_data->vol_shadow_steps = (float)scene_eval->eevee.volumetric_shadow_samples;
- if ((scene_eval->eevee.flag & SCE_EEVEE_VOLUMETRIC_SHADOWS) == 0) {
- common_data->vol_shadow_steps = 0;
- }
-
- if (DRW_view_is_persp_get(NULL)) {
- float sample_distribution = scene_eval->eevee.volumetric_sample_distribution;
- sample_distribution = 4.0f * (max_ff(1.0f - sample_distribution, 1e-2f));
-
- const float clip_start = DRW_view_near_distance_get(NULL);
- /* Negate */
- float near = integration_start = min_ff(-integration_start, clip_start - 1e-4f);
- float far = integration_end = min_ff(-integration_end, near - 1e-4f);
-
- common_data->vol_depth_param[0] = (far - near * exp2(1.0f / sample_distribution)) /
- (far - near);
- common_data->vol_depth_param[1] = (1.0f - common_data->vol_depth_param[0]) / near;
- common_data->vol_depth_param[2] = sample_distribution;
- }
- else {
- const float clip_start = DRW_view_near_distance_get(NULL);
- const float clip_end = DRW_view_far_distance_get(NULL);
- integration_start = min_ff(integration_end, clip_start);
- integration_end = max_ff(-integration_end, clip_end);
-
- common_data->vol_depth_param[0] = integration_start;
- common_data->vol_depth_param[1] = integration_end;
- common_data->vol_depth_param[2] = 1.0f / (integration_end - integration_start);
- }
-
- /* Disable clamp if equal to 0. */
- if (effects->volume_light_clamp == 0.0) {
- effects->volume_light_clamp = FLT_MAX;
- }
-
- common_data->vol_use_lights = (scene_eval->eevee.flag & SCE_EEVEE_VOLUMETRIC_LIGHTS) != 0;
- common_data->vol_use_soft_shadows = (scene_eval->eevee.flag & SCE_EEVEE_SHADOW_SOFT) != 0;
-
- if (!e_data.dummy_scatter) {
- const float scatter[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- const float transmit[4] = {1.0f, 1.0f, 1.0f, 1.0f};
- e_data.dummy_scatter = DRW_texture_create_3d(1, 1, 1, GPU_RGBA8, DRW_TEX_WRAP, scatter);
- e_data.dummy_transmit = DRW_texture_create_3d(1, 1, 1, GPU_RGBA8, DRW_TEX_WRAP, transmit);
- }
-}
-
-void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
-{
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
- EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
-
- const DRWContextState *draw_ctx = DRW_context_state_get();
- Scene *scene = draw_ctx->scene;
- DRWShadingGroup *grp = NULL;
-
- /* Textures */
- if (!e_data.dummy_zero) {
- eevee_create_textures_volumes();
- }
-
- /* Quick breakdown of the Volumetric rendering:
- *
- * The rendering is separated in 4 stages:
- *
- * - Material Parameters : we collect volume properties of
- * all participating media in the scene and store them in
- * a 3D texture aligned with the 3D frustum.
- * This is done in 2 passes, one that clear the texture
- * and/or evaluate the world volumes, and the 2nd one that
- * additively render object volumes.
- *
- * - Light Scattering : the volume properties then are sampled
- * and light scattering is evaluated for each cell of the
- * volume texture. Temporal super-sampling (if enabled) occurs here.
- *
- * - Volume Integration : the scattered light and extinction is
- * integrated (accumulated) along the view-rays. The result is stored
- * for every cell in another texture.
- *
- * - Full-screen Resolve : From the previous stage, we get two
- * 3D textures that contains integrated scattered light and extinction
- * for "every" positions in the frustum. We only need to sample
- * them and blend the scene color with those factors. This also
- * work for alpha blended materials.
- */
-
- /* World pass is not additive as it also clear the buffer. */
- DRW_PASS_CREATE(psl->volumetric_world_ps, DRW_STATE_WRITE_COLOR);
- DRW_PASS_CREATE(psl->volumetric_objects_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD);
-
- /* World Volumetric */
- struct World *wo = scene->world;
- if (wo != NULL && wo->use_nodes && wo->nodetree &&
- !LOOK_DEV_STUDIO_LIGHT_ENABLED(draw_ctx->v3d)) {
- struct GPUMaterial *mat = EEVEE_material_get(vedata, scene, NULL, wo, VAR_MAT_VOLUME);
-
- if (GPU_material_has_volume_output(mat)) {
- grp = DRW_shgroup_material_create(mat, psl->volumetric_world_ps);
- }
-
- if (grp) {
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- /* TODO(fclem): remove those (need to clean the GLSL files). */
- DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
- DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
- DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
- DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
- DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
-
- /* Fix principle volumetric not working with world materials. */
- ListBase gpu_grids = GPU_material_volume_grids(mat);
- LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, &gpu_grids) {
- DRW_shgroup_uniform_texture(
- grp, gpu_grid->sampler_name, eevee_volume_default_texture(gpu_grid->default_value));
- }
-
- DRW_shgroup_call_procedural_triangles(grp, NULL, common_data->vol_tex_size[2]);
-
- effects->enabled_effects |= (EFFECT_VOLUMETRIC | EFFECT_POST_BUFFER);
- }
- }
-
- if (grp == NULL) {
- /* If no world or volume material is present just clear the buffer with this drawcall */
- grp = DRW_shgroup_create(EEVEE_shaders_volumes_clear_sh_get(), psl->volumetric_world_ps);
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
- DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
-
- DRW_shgroup_call_procedural_triangles(grp, NULL, common_data->vol_tex_size[2]);
- }
-}
-
-static bool eevee_volume_object_grids_init(Object *ob, ListBase *gpu_grids, DRWShadingGroup *grp)
-{
- Volume *volume = ob->data;
- BKE_volume_load(volume, G.main);
-
- /* Test if we need to use multiple transforms. */
- DRWVolumeGrid *first_drw_grid = NULL;
- bool multiple_transforms = true;
-
- LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) {
- const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, gpu_grid->name);
- DRWVolumeGrid *drw_grid = (volume_grid) ?
- DRW_volume_batch_cache_get_grid(volume, volume_grid) :
- NULL;
-
- if (drw_grid) {
- if (first_drw_grid == NULL) {
- first_drw_grid = drw_grid;
- }
- else if (drw_grid &&
- !equals_m4m4(drw_grid->object_to_texture, first_drw_grid->object_to_texture)) {
- multiple_transforms = true;
- break;
- }
- }
- }
-
- /* Bail out of no grids to render. */
- if (first_drw_grid == NULL) {
- return false;
- }
-
- /* Set transform matrix for the volume as a whole. This one is also used for
- * clipping so must map the entire bounding box to 0..1. */
- float bounds_to_object[4][4];
-
- if (multiple_transforms) {
- /* For multiple grids with different transform, we first transform from object space
- * to bounds, then for each individual grid from bounds to texture. */
- BoundBox *bb = BKE_volume_boundbox_get(ob);
- float bb_size[3];
- sub_v3_v3v3(bb_size, bb->vec[6], bb->vec[0]);
- size_to_mat4(bounds_to_object, bb_size);
- copy_v3_v3(bounds_to_object[3], bb->vec[0]);
-
- invert_m4_m4(first_drw_grid->object_to_bounds, bounds_to_object);
- DRW_shgroup_uniform_mat4(grp, "volumeObjectToTexture", first_drw_grid->object_to_bounds);
- }
- else {
- /* All grid transforms are equal, we can transform to texture space immediately. */
- DRW_shgroup_uniform_mat4(grp, "volumeObjectToTexture", first_drw_grid->object_to_texture);
- }
-
- /* Don't use orco transform here, only matrix. */
- DRW_shgroup_uniform_vec3_copy(grp, "volumeOrcoLoc", (float[3]){0.5f, 0.5f, 0.5f});
- DRW_shgroup_uniform_vec3_copy(grp, "volumeOrcoSize", (float[3]){0.5f, 0.5f, 0.5f});
-
- /* Set density scale. */
- const float density_scale = BKE_volume_density_scale(volume, ob->obmat);
- DRW_shgroup_uniform_float_copy(grp, "volumeDensityScale", density_scale);
-
- /* Bind volume grid textures. */
- LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) {
- const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, gpu_grid->name);
- DRWVolumeGrid *drw_grid = (volume_grid) ?
- DRW_volume_batch_cache_get_grid(volume, volume_grid) :
- NULL;
-
- /* Handle 3 cases here:
- * - Grid exists and texture was loaded -> use texture.
- * - Grid exists but has zero size or failed to load -> use zero.
- * - Grid does not exist -> use default value. */
- GPUTexture *grid_tex = (drw_grid) ? drw_grid->texture :
- (volume_grid) ? e_data.dummy_zero :
- eevee_volume_default_texture(gpu_grid->default_value);
-
- DRW_shgroup_uniform_texture(grp, gpu_grid->sampler_name, grid_tex);
-
- if (drw_grid && multiple_transforms) {
- /* Specify per-volume transform matrix that is applied after the
- * transform from object to bounds. */
- mul_m4_m4m4(drw_grid->bounds_to_texture, drw_grid->object_to_texture, bounds_to_object);
- DRW_shgroup_uniform_mat4(grp, gpu_grid->transform_name, drw_grid->bounds_to_texture);
- }
- }
-
- return true;
-}
-
-static bool eevee_volume_object_mesh_init(Scene *scene,
- Object *ob,
- ListBase *gpu_grids,
- DRWShadingGroup *grp)
-{
- static const float white[3] = {1.0f, 1.0f, 1.0f};
- ModifierData *md = NULL;
-
- /* Smoke Simulation */
- if ((md = BKE_modifiers_findby_type(ob, eModifierType_Fluid)) &&
- (BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) &&
- ((FluidModifierData *)md)->domain != NULL) {
- FluidModifierData *fmd = (FluidModifierData *)md;
- FluidDomainSettings *fds = fmd->domain;
-
- /* Don't try to show liquid domains here. */
- if (!fds->fluid || !(fds->type == FLUID_DOMAIN_TYPE_GAS)) {
- return false;
- }
-
- /* Don't show smoke before simulation starts, this could be made an option in the future. */
- /* (sebbas): Always show smoke for manta */
-#if 0
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const bool show_smoke = ((int)DEG_get_ctime(draw_ctx->depsgraph) >=
- *fds->point_cache[0]->startframe);
-#endif
-
- if (fds->fluid && (fds->type == FLUID_DOMAIN_TYPE_GAS) /* && show_smoke */) {
- DRW_smoke_ensure(fmd, fds->flags & FLUID_DOMAIN_USE_NOISE);
- BLI_addtail(&e_data.smoke_domains, BLI_genericNodeN(fmd));
- }
-
- LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) {
- if (STREQ(gpu_grid->name, "density")) {
- DRW_shgroup_uniform_texture_ref(
- grp, gpu_grid->sampler_name, fds->tex_density ? &fds->tex_density : &e_data.dummy_one);
- }
- else if (STREQ(gpu_grid->name, "color")) {
- DRW_shgroup_uniform_texture_ref(
- grp, gpu_grid->sampler_name, fds->tex_color ? &fds->tex_color : &e_data.dummy_one);
- }
- else if (STR_ELEM(gpu_grid->name, "flame", "temperature")) {
- DRW_shgroup_uniform_texture_ref(
- grp, gpu_grid->sampler_name, fds->tex_flame ? &fds->tex_flame : &e_data.dummy_flame);
- }
- else {
- DRW_shgroup_uniform_texture(
- grp, gpu_grid->sampler_name, eevee_volume_default_texture(gpu_grid->default_value));
- }
- }
-
- /* Constant Volume color. */
- bool use_constant_color = ((fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) == 0 &&
- (fds->active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET) != 0);
-
- DRW_shgroup_uniform_vec3(
- grp, "volumeColor", (use_constant_color) ? fds->active_color : white, 1);
-
- /* Output is such that 0..1 maps to 0..1000K */
- DRW_shgroup_uniform_vec2(grp, "volumeTemperature", &fds->flame_ignition, 1);
- }
- else {
- LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) {
- DRW_shgroup_uniform_texture(
- grp, gpu_grid->sampler_name, eevee_volume_default_texture(gpu_grid->default_value));
- }
- }
-
- /* Transform for mesh volumes. */
- static const float unit_mat[4][4] = {{1.0f, 0.0f, 0.0f, 0.0f},
- {0.0f, 1.0f, 0.0f, 0.0f},
- {0.0f, 0.0f, 1.0f, 0.0f},
- {0.0f, 0.0f, 0.0f, 1.0f}};
- float *texco_loc, *texco_size;
- BKE_mesh_texspace_get_reference((struct Mesh *)ob->data, NULL, &texco_loc, &texco_size);
-
- DRW_shgroup_uniform_mat4(grp, "volumeObjectToTexture", unit_mat);
- DRW_shgroup_uniform_vec3(grp, "volumeOrcoLoc", texco_loc, 1);
- DRW_shgroup_uniform_vec3(grp, "volumeOrcoSize", texco_size, 1);
-
- return true;
-}
-
-void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata,
- EEVEE_Data *vedata,
- Scene *scene,
- Object *ob)
-{
- Material *ma = BKE_object_material_get_eval(ob, 1);
-
- if (ma == NULL) {
- if (ob->type == OB_VOLUME) {
- ma = BKE_material_default_volume();
- }
- else {
- return;
- }
- }
-
- float size[3];
- mat4_to_size(size, ob->obmat);
- /* Check if any of the axes have 0 length. (see T69070) */
- const float epsilon = 1e-8f;
- if ((size[0] < epsilon) || (size[1] < epsilon) || (size[2] < epsilon)) {
- return;
- }
-
- int mat_options = VAR_MAT_VOLUME | VAR_MAT_MESH;
- struct GPUMaterial *mat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options);
- eGPUMaterialStatus status = GPU_material_status(mat);
-
- /* If shader failed to compile or is currently compiling. */
- if (status != GPU_MAT_SUCCESS) {
- return;
- }
-
- DRWShadingGroup *grp = DRW_shgroup_material_create(mat, vedata->psl->volumetric_objects_ps);
-
- /* TODO(fclem): remove those "unnecessary" UBOs */
- DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
- DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
- DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
- DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
- DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
-
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
-
- ListBase gpu_grids = GPU_material_volume_grids(mat);
-
- if (ob->type == OB_VOLUME) {
- if (!eevee_volume_object_grids_init(ob, &gpu_grids, grp)) {
- return;
- }
- }
- else {
- if (!eevee_volume_object_mesh_init(scene, ob, &gpu_grids, grp)) {
- return;
- }
- }
-
- /* TODO: Reduce to number of slices intersecting. */
- /* TODO: Preemptive culling. */
- DRW_shgroup_call_procedural_triangles(grp, ob, sldata->common_data.vol_tex_size[2]);
-
- vedata->stl->effects->enabled_effects |= (EFFECT_VOLUMETRIC | EFFECT_POST_BUFFER);
-}
-
-void EEVEE_volumes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
-{
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_TextureList *txl = vedata->txl;
- EEVEE_EffectsInfo *effects = vedata->stl->effects;
- LightCache *lcache = vedata->stl->g_data->light_cache;
- EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
-
- if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
- DRWShadingGroup *grp;
- struct GPUShader *sh;
-
- DRW_PASS_CREATE(psl->volumetric_scatter_ps, DRW_STATE_WRITE_COLOR);
- sh = (common_data->vol_use_lights) ? EEVEE_shaders_volumes_scatter_with_lights_sh_get() :
- EEVEE_shaders_volumes_scatter_sh_get();
- grp = DRW_shgroup_create(sh, psl->volumetric_scatter_ps);
- DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &lcache->grid_tx.tex);
- DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool);
- DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool);
- DRW_shgroup_uniform_texture_ref(grp, "volumeScattering", &txl->volume_prop_scattering);
- DRW_shgroup_uniform_texture_ref(grp, "volumeExtinction", &txl->volume_prop_extinction);
- DRW_shgroup_uniform_texture_ref(grp, "volumeEmission", &txl->volume_prop_emission);
- DRW_shgroup_uniform_texture_ref(grp, "volumePhase", &txl->volume_prop_phase);
- DRW_shgroup_uniform_texture_ref(grp, "historyScattering", &txl->volume_scatter_history);
- DRW_shgroup_uniform_texture_ref(grp, "historyTransmittance", &txl->volume_transmit_history);
- DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
- DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
-
- DRW_shgroup_call_procedural_triangles(grp, NULL, common_data->vol_tex_size[2]);
-
- DRW_PASS_CREATE(psl->volumetric_integration_ps, DRW_STATE_WRITE_COLOR);
- grp = DRW_shgroup_create(EEVEE_shaders_volumes_integration_sh_get(),
- psl->volumetric_integration_ps);
- DRW_shgroup_uniform_texture_ref(grp, "volumeScattering", &txl->volume_scatter);
- DRW_shgroup_uniform_texture_ref(grp, "volumeExtinction", &txl->volume_transmit);
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
- if (USE_VOLUME_OPTI) {
- DRW_shgroup_uniform_image_ref(grp, "finalScattering_img", &txl->volume_scatter_history);
- DRW_shgroup_uniform_image_ref(grp, "finalTransmittance_img", &txl->volume_transmit_history);
- }
-
- DRW_shgroup_call_procedural_triangles(
- grp, NULL, USE_VOLUME_OPTI ? 1 : common_data->vol_tex_size[2]);
-
- DRW_PASS_CREATE(psl->volumetric_resolve_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
- grp = DRW_shgroup_create(EEVEE_shaders_volumes_resolve_sh_get(false),
- psl->volumetric_resolve_ps);
- DRW_shgroup_uniform_texture_ref(grp, "inScattering", &txl->volume_scatter);
- DRW_shgroup_uniform_texture_ref(grp, "inTransmittance", &txl->volume_transmit);
- DRW_shgroup_uniform_texture_ref(grp, "inSceneDepth", &e_data.depth_src);
- DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
-
- DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
- }
-}
-
-void EEVEE_volumes_draw_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
-{
- EEVEE_FramebufferList *fbl = vedata->fbl;
- EEVEE_TextureList *txl = vedata->txl;
- EEVEE_EffectsInfo *effects = vedata->stl->effects;
- EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
-
- if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
- int *tex_size = common_data->vol_tex_size;
-
- if (txl->volume_prop_scattering == NULL) {
- /* Volume properties: We evaluate all volumetric objects
- * and store their final properties into each froxel */
- txl->volume_prop_scattering = DRW_texture_create_3d(
- tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL);
- txl->volume_prop_extinction = DRW_texture_create_3d(
- tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL);
- txl->volume_prop_emission = DRW_texture_create_3d(
- tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL);
- txl->volume_prop_phase = DRW_texture_create_3d(
- tex_size[0], tex_size[1], tex_size[2], GPU_RG16F, DRW_TEX_FILTER, NULL);
-
- /* Volume scattering: We compute for each froxel the
- * Scattered light towards the view. We also resolve temporal
- * super sampling during this stage. */
- txl->volume_scatter = DRW_texture_create_3d(
- tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL);
- txl->volume_transmit = DRW_texture_create_3d(
- tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL);
-
- /* Final integration: We compute for each froxel the
- * amount of scattered light and extinction coef at this
- * given depth. We use these textures as double buffer
- * for the volumetric history. */
- txl->volume_scatter_history = DRW_texture_create_3d(
- tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL);
- txl->volume_transmit_history = DRW_texture_create_3d(
- tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL);
- }
-
- GPU_framebuffer_ensure_config(&fbl->volumetric_fb,
- {GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(txl->volume_prop_scattering),
- GPU_ATTACHMENT_TEXTURE(txl->volume_prop_extinction),
- GPU_ATTACHMENT_TEXTURE(txl->volume_prop_emission),
- GPU_ATTACHMENT_TEXTURE(txl->volume_prop_phase)});
- GPU_framebuffer_ensure_config(&fbl->volumetric_scat_fb,
- {GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(txl->volume_scatter),
- GPU_ATTACHMENT_TEXTURE(txl->volume_transmit)});
- GPU_framebuffer_ensure_config(&fbl->volumetric_integ_fb,
- {GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(txl->volume_scatter_history),
- GPU_ATTACHMENT_TEXTURE(txl->volume_transmit_history)});
- }
- else {
- DRW_TEXTURE_FREE_SAFE(txl->volume_prop_scattering);
- DRW_TEXTURE_FREE_SAFE(txl->volume_prop_extinction);
- DRW_TEXTURE_FREE_SAFE(txl->volume_prop_emission);
- DRW_TEXTURE_FREE_SAFE(txl->volume_prop_phase);
- DRW_TEXTURE_FREE_SAFE(txl->volume_scatter);
- DRW_TEXTURE_FREE_SAFE(txl->volume_transmit);
- DRW_TEXTURE_FREE_SAFE(txl->volume_scatter_history);
- DRW_TEXTURE_FREE_SAFE(txl->volume_transmit_history);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_fb);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_scat_fb);
- GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_integ_fb);
- }
-
- effects->volume_scatter = e_data.dummy_scatter;
- effects->volume_transmit = e_data.dummy_transmit;
-}
-
-void EEVEE_volumes_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
-{
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_TextureList *txl = vedata->txl;
- EEVEE_FramebufferList *fbl = vedata->fbl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
- if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
- DRW_stats_group_start("Volumetrics");
-
- /* We sample the shadow-maps using shadow sampler. We need to enable Comparison mode.
- * TODO(fclem): avoid this by using sampler objects. */
- GPU_texture_compare_mode(sldata->shadow_cube_pool, true);
- GPU_texture_compare_mode(sldata->shadow_cascade_pool, true);
-
- GPU_framebuffer_bind(fbl->volumetric_fb);
- DRW_draw_pass(psl->volumetric_world_ps);
- DRW_draw_pass(psl->volumetric_objects_ps);
-
- GPU_framebuffer_bind(fbl->volumetric_scat_fb);
- DRW_draw_pass(psl->volumetric_scatter_ps);
-
- if (USE_VOLUME_OPTI) {
- /* Avoid feedback loop assert. */
- GPU_framebuffer_bind(fbl->volumetric_fb);
- }
- else {
- GPU_framebuffer_bind(fbl->volumetric_integ_fb);
- }
-
- DRW_draw_pass(psl->volumetric_integration_ps);
-
- SWAP(struct GPUFrameBuffer *, fbl->volumetric_scat_fb, fbl->volumetric_integ_fb);
- SWAP(GPUTexture *, txl->volume_scatter, txl->volume_scatter_history);
- SWAP(GPUTexture *, txl->volume_transmit, txl->volume_transmit_history);
-
- effects->volume_scatter = txl->volume_scatter;
- effects->volume_transmit = txl->volume_transmit;
-
- /* Restore */
- GPU_framebuffer_bind(fbl->main_fb);
-
- DRW_stats_group_end();
- }
-}
-
-void EEVEE_volumes_resolve(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
-{
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_FramebufferList *fbl = vedata->fbl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
-
- if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
- DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- e_data.depth_src = dtxl->depth;
-
- if (USE_VOLUME_OPTI) {
- GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
- }
-
- /* Apply for opaque geometry. */
- GPU_framebuffer_bind(fbl->main_color_fb);
- DRW_draw_pass(psl->volumetric_resolve_ps);
-
- /* Restore. */
- GPU_framebuffer_bind(fbl->main_fb);
- }
-}
-
-void EEVEE_volumes_free_smoke_textures(void)
-{
- /* Free Smoke Textures after rendering */
- LISTBASE_FOREACH (LinkData *, link, &e_data.smoke_domains) {
- FluidModifierData *fmd = (FluidModifierData *)link->data;
- DRW_smoke_free(fmd);
- }
- BLI_freelistN(&e_data.smoke_domains);
-}
-
-void EEVEE_volumes_free(void)
-{
- DRW_TEXTURE_FREE_SAFE(e_data.dummy_scatter);
- DRW_TEXTURE_FREE_SAFE(e_data.dummy_transmit);
-
- DRW_TEXTURE_FREE_SAFE(e_data.dummy_zero);
- DRW_TEXTURE_FREE_SAFE(e_data.dummy_one);
- DRW_TEXTURE_FREE_SAFE(e_data.dummy_flame);
-}
-
-/* -------------------------------------------------------------------- */
-/** \name Render Passes
- * \{ */
-
-void EEVEE_volumes_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples)
-{
- EEVEE_FramebufferList *fbl = vedata->fbl;
- EEVEE_TextureList *txl = vedata->txl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_EffectsInfo *effects = stl->effects;
-
- /* Create FrameBuffer. */
-
- /* Should be enough precision for many samples. */
- const eGPUTextureFormat texture_format_accum = (tot_samples > 128) ? GPU_RGBA32F : GPU_RGBA16F;
- DRW_texture_ensure_fullscreen_2d(&txl->volume_scatter_accum, texture_format_accum, 0);
- DRW_texture_ensure_fullscreen_2d(&txl->volume_transmittance_accum, texture_format_accum, 0);
-
- GPU_framebuffer_ensure_config(&fbl->volumetric_accum_fb,
- {GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(txl->volume_scatter_accum),
- GPU_ATTACHMENT_TEXTURE(txl->volume_transmittance_accum)});
-
- /* Create Pass and shgroup. */
- DRW_PASS_CREATE(psl->volumetric_accum_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
- DRWShadingGroup *grp = NULL;
- if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
- grp = DRW_shgroup_create(EEVEE_shaders_volumes_resolve_sh_get(true), psl->volumetric_accum_ps);
- DRW_shgroup_uniform_texture_ref(grp, "inScattering", &txl->volume_scatter);
- DRW_shgroup_uniform_texture_ref(grp, "inTransmittance", &txl->volume_transmit);
- DRW_shgroup_uniform_texture_ref(grp, "inSceneDepth", &e_data.depth_src);
- DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
- DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined);
- }
- else {
- /* There is no volumetrics in the scene. Use a shader to fill the accum textures with a default
- * value. */
- grp = DRW_shgroup_create(EEVEE_shaders_volumes_accum_sh_get(), psl->volumetric_accum_ps);
- }
- DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
-}
-
-void EEVEE_volumes_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
-{
- EEVEE_FramebufferList *fbl = vedata->fbl;
- EEVEE_PassList *psl = vedata->psl;
- EEVEE_EffectsInfo *effects = vedata->stl->effects;
-
- if (fbl->volumetric_accum_fb != NULL) {
- /* Accum pass */
- GPU_framebuffer_bind(fbl->volumetric_accum_fb);
-
- /* Clear texture. */
- if (effects->taa_current_sample == 1) {
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- GPU_framebuffer_clear_color(fbl->volumetric_accum_fb, clear);
- }
-
- DRW_draw_pass(psl->volumetric_accum_ps);
-
- /* Restore */
- GPU_framebuffer_bind(fbl->main_fb);
- }
-}
-
-/** \} */
diff --git a/source/blender/draw/engines/eevee/eevee_world.cc b/source/blender/draw/engines/eevee/eevee_world.cc
new file mode 100644
index 00000000000..04978d67fa1
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_world.cc
@@ -0,0 +1,111 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ */
+
+#include "NOD_shader.h"
+
+#include "BKE_lib_id.h"
+#include "BKE_node.h"
+#include "BKE_world.h"
+
+#include "eevee_instance.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name Default Material
+ *
+ * \{ */
+
+DefaultWorldNodeTree::DefaultWorldNodeTree()
+{
+ bNodeTree *ntree = ntreeAddTree(NULL, "World Nodetree", ntreeType_Shader->idname);
+ bNode *background = nodeAddStaticNode(NULL, ntree, SH_NODE_BACKGROUND);
+ bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_WORLD);
+ bNodeSocket *background_out = nodeFindSocket(background, SOCK_OUT, "Background");
+ bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface");
+ nodeAddLink(ntree, background, background_out, output, output_in);
+ nodeSetActive(ntree, output);
+
+ color_socket_ =
+ (bNodeSocketValueRGBA *)nodeFindSocket(background, SOCK_IN, "Color")->default_value;
+ ntree_ = ntree;
+}
+
+DefaultWorldNodeTree::~DefaultWorldNodeTree()
+{
+ ntreeFreeEmbeddedTree(ntree_);
+ MEM_SAFE_FREE(ntree_);
+}
+
+/* Configure a default nodetree with the given world. */
+bNodeTree *DefaultWorldNodeTree::nodetree_get(::World *wo)
+{
+ /* WARNING: This function is not threadsafe. Which is not a problem for the moment. */
+ copy_v3_fl3(color_socket_->value, wo->horr, wo->horg, wo->horb);
+ return ntree_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name World
+ *
+ * \{ */
+
+void World::sync()
+{
+ if (inst_.lookdev.sync_world()) {
+ return;
+ }
+
+ ::World *bl_world = inst_.scene->world;
+
+ if (bl_world == nullptr) {
+ bl_world = BKE_world_default();
+ }
+ else {
+ WorldHandle &wo_handle = inst_.sync.sync_world(bl_world);
+
+ if (wo_handle.recalc != 0) {
+ inst_.lightprobes.set_world_dirty();
+ }
+ wo_handle.reset_recalc_flag();
+ }
+
+ /* TODO(fclem) This should be detected to scene level. */
+ ::World *orig_world = (::World *)DEG_get_original_id(&bl_world->id);
+ if (prev_original_world != orig_world) {
+ prev_original_world = orig_world;
+ inst_.sampling.reset();
+ }
+
+ bNodeTree *ntree = (bl_world->nodetree && bl_world->use_nodes) ?
+ bl_world->nodetree :
+ default_tree.nodetree_get(bl_world);
+
+ GPUMaterial *gpumat = inst_.shaders.world_shader_get(bl_world, ntree);
+ inst_.shading_passes.background.sync(gpumat);
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_world.hh b/source/blender/draw/engines/eevee/eevee_world.hh
new file mode 100644
index 00000000000..ecf4a8cb260
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_world.hh
@@ -0,0 +1,78 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * World rendering with material handling. Also take care of lookdev
+ * HDRI and default material.
+ */
+
+#pragma once
+
+#include "DNA_world_types.h"
+
+namespace blender::eevee {
+
+class Instance;
+
+/* -------------------------------------------------------------------- */
+/** \name Default World Nodetree
+ *
+ * In order to support worlds without nodetree we reuse and configure a standalone nodetree that
+ * we pass for shader generation. The GPUMaterial is still stored inside the World even if
+ * it does not use a nodetree.
+ * \{ */
+
+class DefaultWorldNodeTree {
+ private:
+ bNodeTree *ntree_;
+ bNodeSocketValueRGBA *color_socket_;
+
+ public:
+ DefaultWorldNodeTree();
+ ~DefaultWorldNodeTree();
+
+ bNodeTree *nodetree_get(::World *world);
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name World
+ *
+ * \{ */
+
+class World {
+ private:
+ Instance &inst_;
+
+ DefaultWorldNodeTree default_tree;
+
+ /* Used to detect if world change. */
+ ::World *prev_original_world = nullptr;
+
+ public:
+ World(Instance &inst) : inst_(inst){};
+
+ void sync(void);
+};
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/eevee_wrapper.hh b/source/blender/draw/engines/eevee/eevee_wrapper.hh
new file mode 100644
index 00000000000..d5daa247d1c
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_wrapper.hh
@@ -0,0 +1,682 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Templated wrappers to make it easier to use GPU objects in C++.
+ */
+
+#pragma once
+
+#include "BLI_utildefines.h"
+#include "BLI_utility_mixins.hh"
+#include "GPU_framebuffer.h"
+#include "GPU_texture.h"
+#include "GPU_uniform_buffer.h"
+#include "GPU_vertex_buffer.h"
+
+namespace blender::eevee {
+
+template<
+ /** Type of the values stored in this uniform buffer. */
+ typename T,
+ /** The number of values that can be stored in this uniform buffer. */
+ int64_t len>
+class StructArrayBuffer : NonMovable, NonCopyable {
+ private:
+ T data_[len];
+ GPUUniformBuf *ubo_;
+
+#ifdef DEBUG
+ const char *name_ = typeid(T).name();
+#else
+ constexpr static const char *name_ = "StructArrayBuffer";
+#endif
+
+ public:
+ StructArrayBuffer()
+ {
+ ubo_ = GPU_uniformbuf_create_ex(sizeof(data_), nullptr, name_);
+ }
+ ~StructArrayBuffer()
+ {
+ GPU_uniformbuf_free(ubo_);
+ }
+
+ void push_update(void)
+ {
+ GPU_uniformbuf_update(ubo_, data_);
+ }
+
+ const GPUUniformBuf *ubo_get(void) const
+ {
+ return ubo_;
+ }
+
+ /**
+ * Get the value at the given index. This invokes undefined behavior when the index is out of
+ * bounds.
+ */
+ const T &operator[](int64_t index) const
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < len);
+ return data_[index];
+ }
+
+ T &operator[](int64_t index)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < len);
+ return data_[index];
+ }
+
+ /**
+ * Get a pointer to the beginning of the array.
+ */
+ const T *data() const
+ {
+ return data_;
+ }
+ T *data()
+ {
+ return data_;
+ }
+
+ /**
+ * Iterator
+ */
+ const T *begin() const
+ {
+ return data_;
+ }
+ const T *end() const
+ {
+ return data_ + len;
+ }
+
+ T *begin()
+ {
+ return data_;
+ }
+ T *end()
+ {
+ return data_ + len;
+ }
+
+ operator Span<T>() const
+ {
+ return Span<T>(data_, len);
+ }
+};
+
+template<
+ /** Type of the values stored in this uniform buffer. */
+ typename T,
+ /** The number of values that can be stored in this uniform buffer. */
+ int64_t len,
+ /** True if created on device and no memory host memory is allocated. */
+ bool device_only = false>
+/* Same thing as StructArrayBuffer but can be arbitrary large, and are writtable on GPU. */
+class StorageArrayBuffer : NonMovable, NonCopyable {
+ private:
+ T *data_ = nullptr;
+ /* Use vertex buffer for now. Until there is a complete GPUStorageBuf implementation. */
+ GPUVertBuf *ssbo_;
+ /* Currently allocated size. */
+ int64_t size;
+
+#ifdef DEBUG
+ const char *name_ = typeid(T).name();
+#else
+ constexpr static const char *name_ = "StorageArrayBuffer";
+#endif
+
+ public:
+ StorageArrayBuffer()
+ {
+ init(len);
+ }
+ ~StorageArrayBuffer()
+ {
+ GPU_vertbuf_discard(ssbo_);
+ }
+
+ void init(int64_t new_size)
+ {
+ size = new_size;
+
+ GPUVertFormat format = {0};
+ GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+
+ GPUUsageType usage = device_only ? GPU_USAGE_DEVICE_ONLY : GPU_USAGE_DYNAMIC;
+ ssbo_ = GPU_vertbuf_create_with_format_ex(&format, usage);
+ GPU_vertbuf_data_alloc(ssbo_, divide_ceil_u(sizeof(T) * size, 4));
+ if (!device_only) {
+ data_ = (T *)GPU_vertbuf_get_data(ssbo_);
+ GPU_vertbuf_use(ssbo_);
+ }
+ }
+
+ void resize(int64_t new_size)
+ {
+ BLI_assert(new_size > 0);
+ if (new_size != size) {
+ GPU_vertbuf_discard(ssbo_);
+ this->init(new_size);
+ }
+ }
+
+ void push_update(void)
+ {
+ BLI_assert(!device_only);
+ /* Get the data again to tag for update. The actual pointer should not change. */
+ data_ = (T *)GPU_vertbuf_get_data(ssbo_);
+ GPU_vertbuf_use(ssbo_);
+ }
+
+ operator GPUVertBuf *() const
+ {
+ return ssbo_;
+ }
+ /* To be able to use it with DRW_shgroup_*_ref(). */
+ GPUVertBuf **operator&()
+ {
+ return &ssbo_;
+ }
+
+ /**
+ * Get the value at the given index. This invokes undefined behavior when the index is out of
+ * bounds.
+ */
+ const T &operator[](int64_t index) const
+ {
+ BLI_assert(!device_only);
+ BLI_assert(index >= 0);
+ BLI_assert(index < size);
+ return data_[index];
+ }
+
+ T &operator[](int64_t index)
+ {
+ BLI_assert(!device_only);
+ BLI_assert(index >= 0);
+ BLI_assert(index < size);
+ return data_[index];
+ }
+
+ /**
+ * Get a pointer to the beginning of the array.
+ */
+ const T *data() const
+ {
+ BLI_assert(!device_only);
+ return data_;
+ }
+ T *data()
+ {
+ BLI_assert(!device_only);
+ return data_;
+ }
+
+ /**
+ * Iterator
+ */
+ const T *begin() const
+ {
+ BLI_assert(!device_only);
+ return data_;
+ }
+ const T *end() const
+ {
+ BLI_assert(!device_only);
+ return data_ + len;
+ }
+
+ T *begin()
+ {
+ BLI_assert(!device_only);
+ return data_;
+ }
+ T *end()
+ {
+ BLI_assert(!device_only);
+ return data_ + len;
+ }
+
+ operator Span<T>() const
+ {
+ BLI_assert(!device_only);
+ return Span<T>(data_, len);
+ }
+};
+
+/** Simpler version where data is not an array. */
+template<
+ /** Type of the values stored in this uniform buffer. */
+ typename T,
+ /** True if created on device and no memory host memory is allocated. */
+ bool device_only = false>
+class StorageBuffer : public T, NonMovable, NonCopyable {
+ private:
+ /* Use vertex buffer for now. Until there is a complete GPUStorageBuf implementation. */
+ GPUVertBuf *ssbo_;
+
+#ifdef DEBUG
+ const char *name_ = typeid(T).name();
+#else
+ constexpr static const char *name_ = "StorageBuffer";
+#endif
+
+ public:
+ StorageBuffer()
+ {
+ GPUVertFormat format = {0};
+ GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+
+ GPUUsageType usage = device_only ? GPU_USAGE_DEVICE_ONLY : GPU_USAGE_DYNAMIC;
+ ssbo_ = GPU_vertbuf_create_with_format_ex(&format, usage);
+ GPU_vertbuf_data_alloc(ssbo_, divide_ceil_u(sizeof(T), 4));
+ if (!device_only) {
+ GPU_vertbuf_use(ssbo_);
+ }
+ }
+ ~StorageBuffer()
+ {
+ GPU_vertbuf_discard(ssbo_);
+ }
+
+ void push_update(void)
+ {
+ BLI_assert(!device_only);
+ /* TODO(fclem): Avoid a full copy. */
+ T *data = (T *)GPU_vertbuf_get_data(ssbo_);
+ *data = *this;
+
+ GPU_vertbuf_use(ssbo_);
+ }
+
+ operator GPUVertBuf *() const
+ {
+ return ssbo_;
+ }
+ /* To be able to use it with DRW_shgroup_*_ref(). */
+ GPUVertBuf **operator&()
+ {
+ return &ssbo_;
+ }
+
+ StorageBuffer<T> &operator=(const T &other)
+ {
+ *static_cast<T *>(this) = other;
+ return *this;
+ }
+};
+
+/** Simpler version where data is not an array. */
+template<typename T> class StructBuffer : public T, NonMovable, NonCopyable {
+ private:
+ GPUUniformBuf *ubo_;
+
+#ifdef DEBUG
+ const char *name_ = typeid(T).name();
+#else
+ constexpr static const char *name_ = "StructBuffer";
+#endif
+
+ public:
+ StructBuffer()
+ {
+ ubo_ = GPU_uniformbuf_create_ex(sizeof(T), nullptr, name_);
+ }
+ ~StructBuffer()
+ {
+ DRW_UBO_FREE_SAFE(ubo_);
+ }
+
+ void push_update(void)
+ {
+ T *data = static_cast<T *>(this);
+ GPU_uniformbuf_update(ubo_, data);
+ }
+
+ const GPUUniformBuf *ubo_get(void) const
+ {
+ return ubo_;
+ }
+
+ StructBuffer<T> &operator=(const T &other)
+ {
+ *static_cast<T *>(this) = other;
+ return *this;
+ }
+};
+
+class Texture {
+ private:
+ GPUTexture *tx_ = nullptr;
+ GPUTexture *tx_tmp_saved_ = nullptr;
+ const char *name_;
+
+ public:
+ Texture() : name_("eevee::Texture"){};
+ Texture(const char *name) : name_(name){};
+
+ Texture(const char *name,
+ int w,
+ int h = 0,
+ int d = 0,
+ int mips = 1,
+ eGPUTextureFormat format = GPU_RGBA8,
+ float *data = nullptr,
+ bool layered = false,
+ bool cubemap = false)
+ : Texture(name)
+ {
+ if (h == 0) {
+ tx_ = GPU_texture_create_1d(name, w, mips, format, data);
+ }
+ else if (d == 0) {
+ if (layered) {
+ tx_ = GPU_texture_create_1d_array(name, w, h, mips, format, data);
+ }
+ else {
+ tx_ = GPU_texture_create_2d(name, w, h, mips, format, data);
+ }
+ }
+ else if (cubemap) {
+ if (layered) {
+ tx_ = GPU_texture_create_cube_array(name, w, d, mips, format, data);
+ }
+ else {
+ tx_ = GPU_texture_create_cube(name, w, mips, format, data);
+ }
+ }
+ else {
+ if (layered) {
+ tx_ = GPU_texture_create_2d_array(name, w, h, d, mips, format, data);
+ }
+ else {
+ tx_ = GPU_texture_create_3d(name, w, h, d, mips, format, GPU_DATA_FLOAT, data);
+ }
+ }
+ }
+
+ ~Texture()
+ {
+ GPU_TEXTURE_FREE_SAFE(tx_);
+ }
+
+ /* Use release_tmp after rendering or else mayhem will ensue. */
+ void acquire_tmp(int w, int h, eGPUTextureFormat format, void *owner_)
+ {
+ if (tx_ == nullptr) {
+ if (tx_tmp_saved_ != nullptr) {
+ tx_ = tx_tmp_saved_;
+ return;
+ }
+ DrawEngineType *owner = (DrawEngineType *)owner_;
+ tx_ = DRW_texture_pool_query_2d(w, h, format, owner);
+ }
+ }
+
+ /* Clears any reference. Workaround for pool texture not being releasable on demand. */
+ void sync_tmp(void)
+ {
+ tx_tmp_saved_ = nullptr;
+ }
+
+ void release_tmp(void)
+ {
+ tx_tmp_saved_ = tx_;
+ tx_ = nullptr;
+ }
+
+ /* Return true is a texture has been created. */
+ bool ensure(const char *name,
+ int w,
+ int h,
+ int d,
+ int mips,
+ eGPUTextureFormat format,
+ bool layered = false)
+ {
+
+ /* TODO(fclem) In the future, we need to check if mip_count did not change.
+ * For now it's ok as we always define all mip level.*/
+ if (tx_) {
+ int3 size = this->size();
+ BLI_assert(GPU_texture_array(tx_) == layered);
+ if (size != int3(w, h, d) || GPU_texture_format(tx_) != format) {
+ GPU_TEXTURE_FREE_SAFE(tx_);
+ }
+ }
+ if (tx_ == nullptr) {
+ if (layered) {
+ tx_ = GPU_texture_create_2d_array(name, w, h, d, mips, format, nullptr);
+ }
+ else {
+ tx_ = GPU_texture_create_3d(name, w, h, d, mips, format, GPU_DATA_FLOAT, nullptr);
+ }
+ if (mips > 1) {
+ /* TODO(fclem) Remove once we have immutable storage or when mips are
+ * generated on creation. */
+ GPU_texture_generate_mipmap(tx_);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /* Return true is a texture has been created. */
+ bool ensure(const char *name, int w, int h, int mips, eGPUTextureFormat format)
+ {
+ /* TODO(fclem) In the future, we need to check if mip_count did not change.
+ * For now it's ok as we always define all mip level.*/
+ if (tx_ && (GPU_texture_width(tx_) != w || GPU_texture_height(tx_) != h ||
+ GPU_texture_format(tx_) != format)) {
+ GPU_TEXTURE_FREE_SAFE(tx_);
+ }
+ if (tx_ == nullptr) {
+ tx_ = GPU_texture_create_2d(name, w, h, mips, format, nullptr);
+ if (mips > 1) {
+ /* TODO(fclem) Remove once we have immutable storage or when mips are
+ * generated on creation. */
+ GPU_texture_generate_mipmap(tx_);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /* Return true is a texture has been created. */
+ bool ensure_cubemap(const char *name, int w, int mips, eGPUTextureFormat format)
+ {
+ /* TODO(fclem) In the future, we need to check if mip_count did not change.
+ * For now it's ok as we always define all mip level.*/
+ if (tx_ && (GPU_texture_width(tx_) != w || GPU_texture_cube(tx_) != true ||
+ GPU_texture_format(tx_) != format)) {
+ GPU_TEXTURE_FREE_SAFE(tx_);
+ }
+ if (tx_ == nullptr) {
+ tx_ = GPU_texture_create_cube(name, w, mips, format, nullptr);
+ if (mips > 1) {
+ /* TODO(fclem) Remove once we have immutable storage or when mips are
+ * generated on creation. */
+ GPU_texture_generate_mipmap(tx_);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /* Return true if a texture has been created. */
+ bool ensure(int w, int h, int mips, eGPUTextureFormat format)
+ {
+ return ensure(name_, w, h, mips, format);
+ }
+
+ void clear(vec4 color)
+ {
+ GPU_texture_clear(tx_, GPU_DATA_FLOAT, &color[0]);
+ }
+ void clear(float val)
+ {
+ float color[4] = {val, val, val, val};
+ GPU_texture_clear(tx_, GPU_DATA_FLOAT, &color[0]);
+ }
+ void clear(uint val)
+ {
+ uint color[4] = {val, val, val, val};
+ GPU_texture_clear(tx_, GPU_DATA_UINT, &color[0]);
+ }
+ void clear(uchar val)
+ {
+ uchar color[4] = {val, val, val, val};
+ GPU_texture_clear(tx_, GPU_DATA_UBYTE, &color[0]);
+ }
+ void clear(int val)
+ {
+ int color[4] = {val, val, val, val};
+ GPU_texture_clear(tx_, GPU_DATA_INT, &color[0]);
+ }
+
+ void filter_mode(bool do_filter)
+ {
+ GPU_texture_filter_mode(tx_, do_filter);
+ }
+
+ void release()
+ {
+ GPU_TEXTURE_FREE_SAFE(tx_);
+ }
+
+ /* Returns a memory block that needs to be manually freed by MEM_freeN(). */
+ template<typename T> T *read(eGPUDataFormat format, int miplvl = 0)
+ {
+ return reinterpret_cast<T *>(GPU_texture_read(tx_, format, miplvl));
+ }
+
+ Texture &operator=(Texture &a)
+ {
+ if (*this != a) {
+ this->tx_ = a.tx_;
+ this->name_ = a.name_;
+ a.tx_ = nullptr;
+ }
+ return *this;
+ }
+ /* To be able to use it with DRW_shgroup_uniform_texture(). */
+ operator GPUTexture *() const
+ {
+ return tx_;
+ }
+ /* To be able to use it with DRW_shgroup_uniform_texture_ref(). */
+ GPUTexture **operator&()
+ {
+ return &tx_;
+ }
+
+ bool is_valid(void) const
+ {
+ return !!tx_;
+ }
+ int width(void) const
+ {
+ return GPU_texture_width(tx_);
+ }
+ int height(void) const
+ {
+ return GPU_texture_height(tx_);
+ }
+ int3 size(void) const
+ {
+ int3 size;
+ GPU_texture_get_mipmap_size(tx_, 0, size);
+ return size;
+ }
+};
+
+class Framebuffer {
+ private:
+ GPUFrameBuffer *fb_ = nullptr;
+ const char *name_;
+
+ public:
+ Framebuffer() : name_(""){};
+ Framebuffer(const char *name) : name_(name){};
+
+ ~Framebuffer()
+ {
+ GPU_FRAMEBUFFER_FREE_SAFE(fb_);
+ }
+
+ void ensure(GPUAttachment depth = GPU_ATTACHMENT_NONE,
+ GPUAttachment color1 = GPU_ATTACHMENT_NONE,
+ GPUAttachment color2 = GPU_ATTACHMENT_NONE,
+ GPUAttachment color3 = GPU_ATTACHMENT_NONE,
+ GPUAttachment color4 = GPU_ATTACHMENT_NONE,
+ GPUAttachment color5 = GPU_ATTACHMENT_NONE,
+ GPUAttachment color6 = GPU_ATTACHMENT_NONE,
+ GPUAttachment color7 = GPU_ATTACHMENT_NONE,
+ GPUAttachment color8 = GPU_ATTACHMENT_NONE)
+ {
+ GPU_framebuffer_ensure_config(
+ &fb_, {depth, color1, color2, color3, color4, color5, color6, color7, color8});
+ }
+
+ Framebuffer &operator=(Framebuffer &a)
+ {
+ if (*this != a) {
+ this->fb_ = a.fb_;
+ this->name_ = a.name_;
+ a.fb_ = nullptr;
+ }
+ return *this;
+ }
+
+ operator GPUFrameBuffer *() const
+ {
+ return fb_;
+ }
+};
+
+static inline void shgroup_geometry_call(DRWShadingGroup *grp,
+ Object *ob,
+ GPUBatch *geom,
+ int v_first = -1,
+ int v_count = -1,
+ bool use_instancing = false)
+{
+ if (grp == nullptr) {
+ return;
+ }
+
+ if (v_first == -1) {
+ DRW_shgroup_call(grp, geom, ob);
+ }
+ else if (use_instancing) {
+ DRW_shgroup_call_instance_range(grp, ob, geom, v_first, v_count);
+ }
+ else {
+ DRW_shgroup_call_range(grp, ob, geom, v_first, v_count);
+ }
+}
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
deleted file mode 100644
index 1061b2f91a2..00000000000
--- a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
+++ /dev/null
@@ -1,417 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
-#pragma BLENDER_REQUIRE(raytrace_lib.glsl)
-
-/* Based on Practical Realtime Strategies for Accurate Indirect Occlusion
- * http://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf
- * http://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pptx
- */
-
-#if defined(MESH_SHADER)
-# if !defined(USE_ALPHA_HASH)
-# if !defined(DEPTH_SHADER)
-# if !defined(USE_ALPHA_BLEND)
-# if !defined(USE_REFRACTION)
-# define ENABLE_DEFERED_AO
-# endif
-# endif
-# endif
-# endif
-#endif
-
-#ifndef ENABLE_DEFERED_AO
-# if defined(STEP_RESOLVE)
-# define ENABLE_DEFERED_AO
-# endif
-#endif
-
-uniform sampler2D horizonBuffer;
-
-/* aoSettings flags */
-#define USE_AO 1
-#define USE_BENT_NORMAL 2
-#define USE_DENOISE 4
-
-#define NO_OCCLUSION_DATA OcclusionData(vec4(M_PI, -M_PI, M_PI, -M_PI), 1.0)
-
-struct OcclusionData {
- /* 4 horizons angles, one in each direction around the view vector to form a cross pattern. */
- vec4 horizons;
- /* Custom large scale occlusion. */
- float custom_occlusion;
-};
-
-vec4 pack_occlusion_data(OcclusionData data)
-{
- return vec4(1.0 - data.horizons * vec4(1, -1, 1, -1) * M_1_PI);
-}
-
-OcclusionData unpack_occlusion_data(vec4 v)
-{
- return OcclusionData((1.0 - v) * vec4(1, -1, 1, -1) * M_PI, 0.0);
-}
-
-vec2 get_ao_noise(void)
-{
- vec2 noise = texelfetch_noise_tex(gl_FragCoord.xy).xy;
- /* Decorrelate noise from AA. */
- /* TODO(fclem) we should use a more general approach for more random number dimensions. */
- noise = fract(noise * 6.1803402007);
- return noise;
-}
-
-vec2 get_ao_dir(float jitter)
-{
- /* Only a quarter of a turn because we integrate using 2 slices.
- * We use this instead of using utiltex circle noise to improve cache hits
- * since all tracing direction will be in the same quadrant. */
- jitter *= M_PI_2;
- return vec2(cos(jitter), sin(jitter));
-}
-
-/* Return horizon angle cosine. */
-float search_horizon(vec3 vI,
- vec3 vP,
- float noise,
- ScreenSpaceRay ssray,
- sampler2D depth_tx,
- const float inverted,
- float radius,
- const float sample_count)
-{
- /* Init at cos(M_PI). */
- float h = (inverted != 0.0) ? 1.0 : -1.0;
-
- ssray.max_time -= 1.0;
-
- if (ssray.max_time <= 2.0) {
- /* Produces self shadowing under this threshold. */
- return fast_acos(h);
- }
-
- float prev_time, time = 0.0;
- for (float iter = 0.0; time < ssray.max_time && iter < sample_count; iter++) {
- prev_time = time;
- /* Gives us good precision at center and ensure we cross at least one pixel per iteration. */
- time = 1.0 + iter + sqr((iter + noise) / sample_count) * ssray.max_time;
- float stride = time - prev_time;
- float lod = (log2(stride) - noise) / (1.0 + aoQuality);
-
- vec2 uv = ssray.origin.xy + ssray.direction.xy * time;
- float depth = textureLod(depth_tx, uv * hizUvScale.xy, floor(lod)).r;
-
- if (depth == 1.0 && inverted == 0.0) {
- /* Skip background. Avoids making shadow on the geometry near the far plane. */
- continue;
- }
-
- /* Bias depth a bit to avoid self shadowing issues. */
- const float bias = 2.0 * 2.4e-7;
- depth += (inverted != 0.0) ? -bias : bias;
-
- vec3 s = get_view_space_from_depth(uv, depth);
- vec3 omega_s = s - vP;
- float len = length(omega_s);
- /* Sample's horizon angle cosine. */
- float s_h = dot(vI, omega_s / len);
- /* Blend weight to fade artifacts. */
- float dist_ratio = abs(len) / radius;
- /* Sphere falloff. */
- float dist_fac = sqr(saturate(dist_ratio));
- /* Unbiased, gives too much hard cut behind objects */
- // float dist_fac = step(0.999, dist_ratio);
-
- if (inverted != 0.0) {
- h = min(h, s_h);
- }
- else {
- h = mix(max(h, s_h), h, dist_fac);
- }
- }
- return fast_acos(h);
-}
-
-OcclusionData occlusion_search(
- vec3 vP, sampler2D depth_tx, float radius, const float inverted, const float dir_sample_count)
-{
- if ((int(aoSettings) & USE_AO) == 0) {
- return NO_OCCLUSION_DATA;
- }
-
- vec2 noise = get_ao_noise();
- vec2 dir = get_ao_dir(noise.x);
- vec2 uv = get_uvs_from_view(vP);
- vec3 vI = ((ProjectionMatrix[3][3] == 0.0) ? normalize(-vP) : vec3(0.0, 0.0, 1.0));
- vec3 avg_dir = vec3(0.0);
- float avg_apperture = 0.0;
-
- OcclusionData data = (inverted != 0.0) ? OcclusionData(vec4(0, 0, 0, 0), 1.0) :
- NO_OCCLUSION_DATA;
-
- for (int i = 0; i < 2; i++) {
- Ray ray;
- ray.origin = vP;
- ray.direction = vec3(dir * radius, 0.0);
-
- ScreenSpaceRay ssray;
-
- ssray = raytrace_screenspace_ray_create(ray);
- data.horizons[0 + i * 2] = search_horizon(
- vI, vP, noise.y, ssray, depth_tx, inverted, radius, dir_sample_count);
-
- ray.direction = -ray.direction;
-
- ssray = raytrace_screenspace_ray_create(ray);
- data.horizons[1 + i * 2] = -search_horizon(
- vI, vP, noise.y, ssray, depth_tx, inverted, radius, dir_sample_count);
-
- /* Rotate 90 degrees. */
- dir = vec2(-dir.y, dir.x);
- }
-
- return data;
-}
-
-vec2 clamp_horizons_to_hemisphere(vec2 horizons, float angle_N, const float inverted)
-{
- /* Add a little bias to fight self shadowing. */
- const float max_angle = M_PI_2 - 0.05;
-
- if (inverted != 0.0) {
- horizons.x = max(horizons.x, angle_N + max_angle);
- horizons.y = min(horizons.y, angle_N - max_angle);
- }
- else {
- horizons.x = min(horizons.x, angle_N + max_angle);
- horizons.y = max(horizons.y, angle_N - max_angle);
- }
- return horizons;
-}
-
-void occlusion_eval(OcclusionData data,
- vec3 V,
- vec3 N,
- vec3 Ng,
- const float inverted,
- out float visibility,
- out float visibility_error,
- out vec3 bent_normal)
-{
- /* No error by default. */
- visibility_error = 1.0;
-
- if ((int(aoSettings) & USE_AO) == 0) {
- visibility = data.custom_occlusion;
- bent_normal = N;
- return;
- }
-
- bool early_out = (inverted != 0.0) ? (max_v4(abs(data.horizons)) == 0.0) :
- (min_v4(abs(data.horizons)) == M_PI);
- if (early_out) {
- visibility = saturate(dot(N, Ng) * 0.5 + 0.5);
- visibility = min(visibility, data.custom_occlusion);
-
- if ((int(aoSettings) & USE_BENT_NORMAL) == 0) {
- bent_normal = N;
- }
- else {
- bent_normal = safe_normalize(N + Ng);
- }
- return;
- }
-
- vec2 noise = get_ao_noise();
- vec2 dir = get_ao_dir(noise.x);
-
- visibility_error = 0.0;
- visibility = 0.0;
- bent_normal = N * 0.001;
-
- for (int i = 0; i < 2; i++) {
- vec3 T = transform_direction(ViewMatrixInverse, vec3(dir, 0.0));
- /* Setup integration domain around V. */
- vec3 B = normalize(cross(V, T));
- T = normalize(cross(B, V));
-
- float proj_N_len;
- vec3 proj_N = normalize_len(N - B * dot(N, B), proj_N_len);
- vec3 proj_Ng = normalize(Ng - B * dot(Ng, B));
-
- vec2 h = (i == 0) ? data.horizons.xy : data.horizons.zw;
-
- float N_sin = dot(proj_N, T);
- float Ng_sin = dot(proj_Ng, T);
- float N_cos = saturate(dot(proj_N, V));
- float Ng_cos = saturate(dot(proj_Ng, V));
- /* Gamma, angle between normalized projected normal and view vector. */
- float angle_Ng = sign(Ng_sin) * fast_acos(Ng_cos);
- float angle_N = sign(N_sin) * fast_acos(N_cos);
- /* Clamp horizons to hemisphere around shading normal. */
- h = clamp_horizons_to_hemisphere(h, angle_N, inverted);
-
- float bent_angle = (h.x + h.y) * 0.5;
- /* NOTE: here we multiply z by 0.5 as it shows less difference with the geometric normal.
- * Also modulate by projected normal length to reduce issues with slanted surfaces.
- * All of this is ad-hoc and not really grounded. */
- bent_normal += proj_N_len * (T * sin(bent_angle) + V * 0.5 * cos(bent_angle));
-
- /* Clamp to geometric normal only for integral to keep smooth bent normal. */
- /* This is done to match Cycles ground truth but adds some computation. */
- h = clamp_horizons_to_hemisphere(h, angle_Ng, inverted);
-
- /* Inner integral (Eq. 7). */
- float a = dot(-cos(2.0 * h - angle_N) + N_cos + 2.0 * h * N_sin, vec2(0.25));
- /* Correct normal not on plane (Eq. 8). */
- visibility += proj_N_len * a;
- /* Using a very low number of slices (2) leads to over-darkening of surfaces orthogonal to
- * the view. This is particularly annoying for sharp reflections occlusion. So we compute how
- * much the error is and correct the visibility later. */
- visibility_error += proj_N_len;
-
- /* Rotate 90 degrees. */
- dir = vec2(-dir.y, dir.x);
- }
- /* We integrated 2 directions. */
- visibility *= 0.5;
- visibility_error *= 0.5;
-
- visibility = min(visibility, data.custom_occlusion);
-
- if ((int(aoSettings) & USE_BENT_NORMAL) == 0) {
- bent_normal = N;
- }
- else {
- /* NOTE: using pow(visibility, 6.0) produces NaN (see T87369). */
- float tmp = saturate(pow6(visibility));
- bent_normal = normalize(mix(bent_normal, N, tmp));
- }
-}
-
-/* Multibounce approximation base on surface albedo.
- * Page 78 in the .pdf version. */
-float gtao_multibounce(float visibility, vec3 albedo)
-{
- if (aoBounceFac == 0.0) {
- return visibility;
- }
-
- /* Median luminance. Because Colored multibounce looks bad. */
- float lum = dot(albedo, vec3(0.3333));
-
- float a = 2.0404 * lum - 0.3324;
- float b = -4.7951 * lum + 0.6417;
- float c = 2.7552 * lum + 0.6903;
-
- float x = visibility;
- return max(x, ((x * a + b) * x + c) * x);
-}
-
-float diffuse_occlusion(OcclusionData data, vec3 V, vec3 N, vec3 Ng)
-{
- vec3 unused;
- float unused_error;
- float visibility;
- occlusion_eval(data, V, N, Ng, 0.0, visibility, unused_error, unused);
- /* Scale by user factor */
- visibility = pow(saturate(visibility), aoFactor);
- return visibility;
-}
-
-float diffuse_occlusion(
- OcclusionData data, vec3 V, vec3 N, vec3 Ng, vec3 albedo, out vec3 bent_normal)
-{
- float visibility;
- float unused_error;
- occlusion_eval(data, V, N, Ng, 0.0, visibility, unused_error, bent_normal);
-
- visibility = gtao_multibounce(visibility, albedo);
- /* Scale by user factor */
- visibility = pow(saturate(visibility), aoFactor);
- return visibility;
-}
-
-/**
- * Approximate the area of intersection of two spherical caps
- * radius1 : First cap’s radius (arc length in radians)
- * radius2 : Second caps’ radius (in radians)
- * dist : Distance between caps (radians between centers of caps)
- * NOTE: Result is divided by pi to save one multiply.
- */
-float spherical_cap_intersection(float radius1, float radius2, float dist)
-{
- /* From "Ambient Aperture Lighting" by Chris Oat
- * Slide 15. */
- float max_radius = max(radius1, radius2);
- float min_radius = min(radius1, radius2);
- float sum_radius = radius1 + radius2;
- float area;
- if (dist <= max_radius - min_radius) {
- /* One cap in completely inside the other */
- area = 1.0 - cos(min_radius);
- }
- else if (dist >= sum_radius) {
- /* No intersection exists */
- area = 0;
- }
- else {
- float diff = max_radius - min_radius;
- area = smoothstep(0.0, 1.0, 1.0 - saturate((dist - diff) / (sum_radius - diff)));
- area *= 1.0 - cos(min_radius);
- }
- return area;
-}
-
-float specular_occlusion(
- OcclusionData data, vec3 V, vec3 N, float roughness, inout vec3 specular_dir)
-{
- vec3 visibility_dir;
- float visibility_error;
- float visibility;
- occlusion_eval(data, V, N, N, 0.0, visibility, visibility_error, visibility_dir);
-
- /* Correct visibility error for very sharp surfaces. */
- visibility *= mix(safe_rcp(visibility_error), 1.0, roughness);
-
- specular_dir = normalize(mix(specular_dir, visibility_dir, roughness * (1.0 - visibility)));
-
- /* Visibility to cone angle (eq. 18). */
- float vis_angle = fast_acos(sqrt(1 - visibility));
- /* Roughness to cone angle (eq. 26). */
- float spec_angle = max(0.00990998744964599609375, fast_acos(cone_cosine(roughness)));
- /* Angle between cone axes. */
- float cone_cone_dist = fast_acos(saturate(dot(visibility_dir, specular_dir)));
- float cone_nor_dist = fast_acos(saturate(dot(N, specular_dir)));
-
- float isect_solid_angle = spherical_cap_intersection(vis_angle, spec_angle, cone_cone_dist);
- float specular_solid_angle = spherical_cap_intersection(M_PI_2, spec_angle, cone_nor_dist);
- float specular_occlusion = isect_solid_angle / specular_solid_angle;
- /* Mix because it is unstable in unoccluded areas. */
- float tmp = saturate(pow8(visibility));
- visibility = mix(specular_occlusion, 1.0, tmp);
-
- /* Scale by user factor */
- visibility = pow(saturate(visibility), aoFactor);
- return visibility;
-}
-
-/* Use the right occlusion. */
-OcclusionData occlusion_load(vec3 vP, float custom_occlusion)
-{
- /* Default to fully opened cone. */
- OcclusionData data = NO_OCCLUSION_DATA;
-
-#ifdef ENABLE_DEFERED_AO
- if ((int(aoSettings) & USE_AO) != 0) {
- data = unpack_occlusion_data(texelFetch(horizonBuffer, ivec2(gl_FragCoord.xy), 0));
- }
-#else
- /* For blended surfaces. */
- data = occlusion_search(vP, maxzBuffer, aoDistance, 0.0, 8.0);
-#endif
-
- data.custom_occlusion = custom_occlusion;
-
- return data;
-}
diff --git a/source/blender/draw/engines/eevee/shaders/background_vert.glsl b/source/blender/draw/engines/eevee/shaders/background_vert.glsl
deleted file mode 100644
index ab5d9a7ebe4..00000000000
--- a/source/blender/draw/engines/eevee/shaders/background_vert.glsl
+++ /dev/null
@@ -1,27 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(surface_lib.glsl)
-
-in vec2 pos;
-
-RESOURCE_ID_VARYING
-
-void main()
-{
- GPU_INTEL_VERTEX_SHADER_WORKAROUND
-
- PASS_RESOURCE_ID
-
- gl_Position = vec4(pos, 1.0, 1.0);
- viewPosition = vec3(pos, -1.0);
-
-#ifndef VOLUMETRICS
- /* Not used in practice but needed to avoid compilation errors. */
- worldPosition = viewPosition;
- worldNormal = viewNormal = normalize(-viewPosition);
-#endif
-
-#ifdef USE_ATTR
- pass_attr(viewPosition, NormalMatrix, ModelMatrixInverse);
-#endif
-}
diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
deleted file mode 100644
index 4ee21cf8c2e..00000000000
--- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
+++ /dev/null
@@ -1,206 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-
-vec3 diffuse_dominant_dir(vec3 bent_normal)
-{
- return bent_normal;
-}
-
-vec3 specular_dominant_dir(vec3 N, vec3 V, float roughness)
-{
- vec3 R = -reflect(V, N);
- float smoothness = 1.0 - roughness;
- float fac = smoothness * (sqrt(smoothness) + roughness);
- return normalize(mix(N, R, fac));
-}
-
-float ior_from_f0(float f0)
-{
- float f = sqrt(f0);
- return (-f - 1.0) / (f - 1.0);
-}
-
-/* Simplified form of F_eta(eta, 1.0). */
-float f0_from_ior(float eta)
-{
- float A = (eta - 1.0) / (eta + 1.0);
- return A * A;
-}
-
-vec3 refraction_dominant_dir(vec3 N, vec3 V, float roughness, float ior)
-{
- /* TODO: This a bad approximation. Better approximation should fit
- * the refracted vector and roughness into the best prefiltered reflection
- * lobe. */
- /* Correct the IOR for ior < 1.0 to not see the abrupt delimitation or the TIR */
- ior = (ior < 1.0) ? mix(ior, 1.0, roughness) : ior;
- float eta = 1.0 / ior;
-
- float NV = dot(N, -V);
-
- /* Custom Refraction. */
- float k = 1.0 - eta * eta * (1.0 - NV * NV);
- k = max(0.0, k); /* Only this changes. */
- vec3 R = eta * -V - (eta * NV + sqrt(k)) * N;
-
- return R;
-}
-
-/* Fresnel monochromatic, perfect mirror */
-float F_eta(float eta, float cos_theta)
-{
- /* compute fresnel reflectance without explicitly computing
- * the refracted direction */
- float c = abs(cos_theta);
- float g = eta * eta - 1.0 + c * c;
- if (g > 0.0) {
- g = sqrt(g);
- float A = (g - c) / (g + c);
- float B = (c * (g + c) - 1.0) / (c * (g - c) + 1.0);
- return 0.5 * A * A * (1.0 + B * B);
- }
- /* Total internal reflections. */
- return 1.0;
-}
-
-/* Fresnel color blend base on fresnel factor */
-vec3 F_color_blend(float eta, float fresnel, vec3 f0_color)
-{
- float f0 = f0_from_ior(eta);
- float fac = saturate((fresnel - f0) / (1.0 - f0));
- return mix(f0_color, vec3(1.0), fac);
-}
-
-/* Fresnel split-sum approximation. */
-vec3 F_brdf_single_scatter(vec3 f0, vec3 f90, vec2 lut)
-{
- /* Unreal specular matching : if specular color is below 2% intensity,
- * treat as shadowning */
- return lut.y * f90 + lut.x * f0;
-}
-
-/* Multi-scattering brdf approximation from :
- * "A Multiple-Scattering Microfacet Model for Real-Time Image-based Lighting"
- * by Carmelo J. Fdez-Agüera. */
-vec3 F_brdf_multi_scatter(vec3 f0, vec3 f90, vec2 lut)
-{
- vec3 FssEss = lut.y * f90 + lut.x * f0;
-
- float Ess = lut.x + lut.y;
- float Ems = 1.0 - Ess;
- vec3 Favg = f0 + (1.0 - f0) / 21.0;
- vec3 Fms = FssEss * Favg / (1.0 - (1.0 - Ess) * Favg);
- /* We don't do anything special for diffuse surfaces because the principle bsdf
- * does not care about energy conservation of the specular layer for dielectrics. */
- return FssEss + Fms * Ems;
-}
-
-/* GGX */
-float D_ggx_opti(float NH, float a2)
-{
- float tmp = (NH * a2 - NH) * NH + 1.0;
- return M_PI * tmp * tmp; /* Doing RCP and mul a2 at the end */
-}
-
-float G1_Smith_GGX_opti(float NX, float a2)
-{
- /* Using Brian Karis approach and refactoring by NX/NX
- * this way the (2*NL)*(2*NV) in G = G1(V) * G1(L) gets canceled by the brdf denominator 4*NL*NV
- * Rcp is done on the whole G later
- * Note that this is not convenient for the transmission formula */
- return NX + sqrt(NX * (NX - NX * a2) + a2);
- /* return 2 / (1 + sqrt(1 + a2 * (1 - NX*NX) / (NX*NX) ) ); /* Reference function */
-}
-
-float bsdf_ggx(vec3 N, vec3 L, vec3 V, float roughness)
-{
- float a = roughness;
- float a2 = a * a;
-
- vec3 H = normalize(L + V);
- float NH = max(dot(N, H), 1e-8);
- float NL = max(dot(N, L), 1e-8);
- float NV = max(dot(N, V), 1e-8);
-
- float G = G1_Smith_GGX_opti(NV, a2) * G1_Smith_GGX_opti(NL, a2); /* Doing RCP at the end */
- float D = D_ggx_opti(NH, a2);
-
- /* Denominator is canceled by G1_Smith */
- /* bsdf = D * G / (4.0 * NL * NV); /* Reference function */
- return NL * a2 / (D * G); /* NL to Fit cycles Equation : line. 345 in bsdf_microfacet.h */
-}
-
-void accumulate_light(vec3 light, float fac, inout vec4 accum)
-{
- accum += vec4(light, 1.0) * min(fac, (1.0 - accum.a));
-}
-
-/* Same thing as Cycles without the comments to make it shorter. */
-vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N)
-{
- vec3 R = -reflect(I, N);
-
- /* Reflection rays may always be at least as shallow as the incoming ray. */
- float threshold = min(0.9 * dot(Ng, I), 0.025);
- if (dot(Ng, R) >= threshold) {
- return N;
- }
-
- float NdotNg = dot(N, Ng);
- vec3 X = normalize(N - NdotNg * Ng);
-
- float Ix = dot(I, X), Iz = dot(I, Ng);
- float Ix2 = sqr(Ix), Iz2 = sqr(Iz);
- float a = Ix2 + Iz2;
-
- float b = sqrt(Ix2 * (a - sqr(threshold)));
- float c = Iz * threshold + a;
-
- float fac = 0.5 / a;
- float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c);
- bool valid1 = (N1_z2 > 1e-5) && (N1_z2 <= (1.0 + 1e-5));
- bool valid2 = (N2_z2 > 1e-5) && (N2_z2 <= (1.0 + 1e-5));
-
- vec2 N_new;
- if (valid1 && valid2) {
- /* If both are possible, do the expensive reflection-based check. */
- vec2 N1 = vec2(safe_sqrt(1.0 - N1_z2), safe_sqrt(N1_z2));
- vec2 N2 = vec2(safe_sqrt(1.0 - N2_z2), safe_sqrt(N2_z2));
-
- float R1 = 2.0 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz;
- float R2 = 2.0 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz;
-
- valid1 = (R1 >= 1e-5);
- valid2 = (R2 >= 1e-5);
- if (valid1 && valid2) {
- N_new = (R1 < R2) ? N1 : N2;
- }
- else {
- N_new = (R1 > R2) ? N1 : N2;
- }
- }
- else if (valid1 || valid2) {
- float Nz2 = valid1 ? N1_z2 : N2_z2;
- N_new = vec2(safe_sqrt(1.0 - Nz2), safe_sqrt(Nz2));
- }
- else {
- return Ng;
- }
- return N_new.x * X + N_new.y * Ng;
-}
-
-/* ----------- Cone angle Approximation --------- */
-
-/* Return a fitted cone angle given the input roughness */
-float cone_cosine(float r)
-{
- /* Using phong gloss
- * roughness = sqrt(2/(gloss+2)) */
- float gloss = -2 + 2 / (r * r);
- /* Drobot 2014 in GPUPro5 */
- // return cos(2.0 * sqrt(2.0 / (gloss + 2)));
- /* Uludag 2014 in GPUPro5 */
- // return pow(0.244, 1 / (gloss + 1));
- /* Jimenez 2016 in Practical Realtime Strategies for Accurate Indirect Occlusion. */
- return exp2(-3.32193 * r * r);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl
deleted file mode 100644
index 4c1544654c1..00000000000
--- a/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl
+++ /dev/null
@@ -1,59 +0,0 @@
-#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
-#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
-
-uniform float sampleCount;
-
-out vec2 FragColor;
-
-void main()
-{
- /* Make sure coordinates are covering the whole [0..1] range at texel center. */
- float y = floor(gl_FragCoord.y) / (LUT_SIZE - 1);
- float x = floor(gl_FragCoord.x) / (LUT_SIZE - 1);
-
- float NV = clamp(1.0 - y * y, 1e-4, 0.9999);
- float a = x * x;
- float a2 = clamp(a * a, 1e-4, 0.9999);
-
- vec3 V = vec3(sqrt(1.0 - NV * NV), 0.0, NV);
-
- /* Integrating BRDF */
- float brdf_accum = 0.0;
- float fresnel_accum = 0.0;
- for (float j = 0.0; j < sampleCount; j++) {
- for (float i = 0.0; i < sampleCount; i++) {
- vec3 Xi = (vec3(i, j, 0.0) + 0.5) / sampleCount;
- Xi.yz = vec2(cos(Xi.y * M_2PI), sin(Xi.y * M_2PI));
-
- /* Microfacet normal */
- vec3 H = sample_ggx(Xi, a, V);
- vec3 L = -reflect(V, H);
- float NL = L.z;
-
- if (NL > 0.0) {
- float NH = max(H.z, 0.0);
- float VH = max(dot(V, H), 0.0);
-
- float G1_v = G1_Smith_GGX_opti(NV, a2);
- float G1_l = G1_Smith_GGX_opti(NL, a2);
- /* See G1_Smith_GGX_opti for explanations. */
- float G_smith = 4.0 * NV * NL / (G1_v * G1_l);
-
- float brdf = (G_smith * VH) / (NH * NV);
-
- /* Follow maximum specular value for principled bsdf. */
- const float specular = 1.0;
- const float eta = (2.0 / (1.0 - sqrt(0.08 * specular))) - 1.0;
- float fresnel = F_eta(eta, VH);
- float Fc = F_color_blend(eta, fresnel, vec3(0)).r;
-
- brdf_accum += (1.0 - Fc) * brdf;
- fresnel_accum += Fc * brdf;
- }
- }
- }
- brdf_accum /= sampleCount * sampleCount;
- fresnel_accum /= sampleCount * sampleCount;
-
- FragColor = vec2(brdf_accum, fresnel_accum);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl b/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl
deleted file mode 100644
index c8c3fa548fc..00000000000
--- a/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl
+++ /dev/null
@@ -1,89 +0,0 @@
-#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
-#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
-
-uniform float sampleCount;
-uniform float z;
-
-out vec4 FragColor;
-
-void main()
-{
- float x = floor(gl_FragCoord.x) / (LUT_SIZE - 1.0);
- float y = floor(gl_FragCoord.y) / (LUT_SIZE - 1.0);
-
- float ior = clamp(sqrt(x), 0.05, 0.999);
- /* ior is sin of critical angle. */
- float critical_cos = sqrt(1.0 - saturate(ior * ior));
-
- y = y * 2.0 - 1.0;
- /* Maximize texture usage on both sides of the critical angle. */
- y *= (y > 0.0) ? (1.0 - critical_cos) : critical_cos;
- /* Center LUT around critical angle to avoid strange interpolation issues when the critical
- * angle is changing. */
- y += critical_cos;
- float NV = clamp(y, 1e-4, 0.9999);
-
- float a = z * z;
- float a2 = clamp(a * a, 1e-8, 0.9999);
-
- vec3 V = vec3(sqrt(1.0 - NV * NV), 0.0, NV);
-
- /* Integrating BTDF */
- float btdf_accum = 0.0;
- float fresnel_accum = 0.0;
- for (float j = 0.0; j < sampleCount; j++) {
- for (float i = 0.0; i < sampleCount; i++) {
- vec3 Xi = (vec3(i, j, 0.0) + 0.5) / sampleCount;
- Xi.yz = vec2(cos(Xi.y * M_2PI), sin(Xi.y * M_2PI));
-
- /* Microfacet normal. */
- vec3 H = sample_ggx(Xi, a2, V);
-
- float VH = dot(V, H);
-
- /* Check if there is total internal reflections. */
- float fresnel = F_eta(ior, VH);
-
- fresnel_accum += fresnel;
-
- float eta = 1.0 / ior;
- if (dot(H, V) < 0.0) {
- H = -H;
- eta = ior;
- }
-
- vec3 L = refract(-V, H, eta);
- float NL = -L.z;
-
- if ((NL > 0.0) && (fresnel < 0.999)) {
- float LH = dot(L, H);
-
- /* Balancing the adjustments made in G1_Smith. */
- float G1_l = NL * 2.0 / G1_Smith_GGX_opti(NL, a2);
-
- /* btdf = abs(VH*LH) * (ior*ior) * D * G(V) * G(L) / (Ht2 * NV)
- * pdf = (VH * abs(LH)) * (ior*ior) * D * G(V) / (Ht2 * NV) */
- float btdf = G1_l * abs(VH * LH) / (VH * abs(LH));
-
- btdf_accum += btdf;
- }
- }
- }
- btdf_accum /= sampleCount * sampleCount;
- fresnel_accum /= sampleCount * sampleCount;
-
- if (z == 0.0) {
- /* Perfect mirror. Increased precision because the roughness is clamped. */
- fresnel_accum = F_eta(ior, NV);
- }
-
- if (x == 0.0) {
- /* Special case. */
- fresnel_accum = 1.0;
- btdf_accum = 0.0;
- }
-
- /* There is place to put multi-scatter result (which is a little bit different still)
- * and / or lobe fitting for better sampling of. */
- FragColor = vec4(btdf_accum, fresnel_accum, 0.0, 1.0);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl
deleted file mode 100644
index 4f9791ac95f..00000000000
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl
+++ /dev/null
@@ -1,88 +0,0 @@
-#pragma BLENDER_REQUIRE(lights_lib.glsl)
-#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
-#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
-
-struct ClosureInputDiffuse {
- vec3 N; /** Shading normal. */
- vec3 albedo; /** Used for multibounce GTAO approximation. Not applied to final radiance. */
-};
-
-#define CLOSURE_INPUT_Diffuse_DEFAULT ClosureInputDiffuse(vec3(0.0), vec3(0.0))
-
-struct ClosureEvalDiffuse {
- vec3 probe_sampling_dir; /** Direction to sample probes from. */
- float ambient_occlusion; /** Final occlusion for distant lighting. */
-};
-
-/* Stubs. */
-#define ClosureOutputDiffuse ClosureOutput
-#define closure_Diffuse_planar_eval(cl_in, cl_eval, cl_common, data, cl_out)
-#define closure_Diffuse_cubemap_eval(cl_in, cl_eval, cl_common, data, cl_out)
-
-ClosureEvalDiffuse closure_Diffuse_eval_init(inout ClosureInputDiffuse cl_in,
- ClosureEvalCommon cl_common,
- out ClosureOutputDiffuse cl_out)
-{
- cl_in.N = safe_normalize(cl_in.N);
- cl_out.radiance = vec3(0.0);
-
- ClosureEvalDiffuse cl_eval;
- cl_eval.ambient_occlusion = diffuse_occlusion(cl_common.occlusion_data,
- cl_common.V,
- cl_in.N,
- cl_common.Ng,
- cl_in.albedo,
- cl_eval.probe_sampling_dir);
- return cl_eval;
-}
-
-void closure_Diffuse_light_eval(ClosureInputDiffuse cl_in,
- ClosureEvalDiffuse cl_eval,
- ClosureEvalCommon cl_common,
- ClosureLightData light,
- inout ClosureOutputDiffuse cl_out)
-{
- float radiance = light_diffuse(light.data, cl_in.N, cl_common.V, light.L);
- /* TODO(fclem) We could try to shadow lights that are shadowless with the ambient_occlusion
- * factor here. */
- cl_out.radiance += light.data.l_color *
- (light.data.l_diff * light.vis * light.contact_shadow * radiance);
-}
-
-void closure_Diffuse_grid_eval(ClosureInputDiffuse cl_in,
- ClosureEvalDiffuse cl_eval,
- ClosureEvalCommon cl_common,
- ClosureGridData grid,
- inout ClosureOutputDiffuse cl_out)
-{
- vec3 probe_radiance = probe_evaluate_grid(
- grid.data, cl_common.P, cl_eval.probe_sampling_dir, grid.local_pos);
- cl_out.radiance += grid.attenuation * probe_radiance;
-}
-
-void closure_Diffuse_indirect_end(ClosureInputDiffuse cl_in,
- ClosureEvalDiffuse cl_eval,
- ClosureEvalCommon cl_common,
- inout ClosureOutputDiffuse cl_out)
-{
- /* If not enough light has been accumulated from probes, use the world specular cubemap
- * to fill the remaining energy needed. */
- if (cl_common.diffuse_accum > 0.0) {
- vec3 probe_radiance = probe_evaluate_world_diff(cl_eval.probe_sampling_dir);
- cl_out.radiance += cl_common.diffuse_accum * probe_radiance;
- }
- /* Apply occlusion on radiance before the light loop. */
- cl_out.radiance *= cl_eval.ambient_occlusion;
-}
-
-void closure_Diffuse_eval_end(ClosureInputDiffuse cl_in,
- ClosureEvalDiffuse cl_eval,
- ClosureEvalCommon cl_common,
- inout ClosureOutputDiffuse cl_out)
-{
-#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
- /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
- cl_out.radiance = vec3(0.0);
- return;
-#endif
-}
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl
deleted file mode 100644
index 00d265a48b0..00000000000
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl
+++ /dev/null
@@ -1,145 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
-#pragma BLENDER_REQUIRE(lights_lib.glsl)
-#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
-#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
-#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl)
-
-struct ClosureInputGlossy {
- vec3 N; /** Shading normal. */
- float roughness; /** Input roughness, not squared. */
-};
-
-#define CLOSURE_INPUT_Glossy_DEFAULT ClosureInputGlossy(vec3(0.0), 0.0)
-
-struct ClosureEvalGlossy {
- vec4 ltc_mat; /** LTC matrix values. */
- float ltc_brdf_scale; /** LTC BRDF scaling. */
- vec3 probe_sampling_dir; /** Direction to sample probes from. */
- float spec_occlusion; /** Specular Occlusion. */
- vec3 raytrace_radiance; /** Raytrace reflection to be accumulated after occlusion. */
-};
-
-/* Stubs. */
-#define ClosureOutputGlossy ClosureOutput
-#define closure_Glossy_grid_eval(cl_in, cl_eval, cl_common, data, cl_out)
-
-#ifdef STEP_RESOLVE /* SSR */
-/* Prototype. */
-void raytrace_resolve(ClosureInputGlossy cl_in,
- inout ClosureEvalGlossy cl_eval,
- inout ClosureEvalCommon cl_common,
- inout ClosureOutputGlossy cl_out);
-#endif
-
-ClosureEvalGlossy closure_Glossy_eval_init(inout ClosureInputGlossy cl_in,
- inout ClosureEvalCommon cl_common,
- out ClosureOutputGlossy cl_out)
-{
- cl_in.N = safe_normalize(cl_in.N);
- cl_in.roughness = clamp(cl_in.roughness, 1e-8, 0.9999);
- cl_out.radiance = vec3(0.0);
-
-#ifndef STEP_RESOLVE /* SSR */
- cl_in.N = ensure_valid_reflection(cl_common.Ng, cl_common.V, cl_in.N);
-#endif
-
- float NV = dot(cl_in.N, cl_common.V);
- vec2 lut_uv = lut_coords(NV, cl_in.roughness);
-
- ClosureEvalGlossy cl_eval;
- cl_eval.ltc_mat = texture(utilTex, vec3(lut_uv, LTC_MAT_LAYER));
- cl_eval.probe_sampling_dir = specular_dominant_dir(cl_in.N, cl_common.V, sqr(cl_in.roughness));
- cl_eval.spec_occlusion = specular_occlusion(cl_common.occlusion_data,
- cl_common.V,
- cl_common.N,
- cl_in.roughness,
- cl_eval.probe_sampling_dir);
- cl_eval.raytrace_radiance = vec3(0.0);
-
-#ifdef STEP_RESOLVE /* SSR */
- raytrace_resolve(cl_in, cl_eval, cl_common, cl_out);
-#endif
-
- /* The brdf split sum LUT is applied after the radiance accumulation.
- * Correct the LTC so that its energy is constant. */
- /* TODO(fclem) Optimize this so that only one scale factor is stored. */
- vec4 ltc_brdf = texture(utilTex, vec3(lut_uv, LTC_BRDF_LAYER)).barg;
- vec2 split_sum_brdf = ltc_brdf.zw;
- cl_eval.ltc_brdf_scale = (ltc_brdf.x + ltc_brdf.y) / (split_sum_brdf.x + split_sum_brdf.y);
- return cl_eval;
-}
-
-void closure_Glossy_light_eval(ClosureInputGlossy cl_in,
- ClosureEvalGlossy cl_eval,
- ClosureEvalCommon cl_common,
- ClosureLightData light,
- inout ClosureOutputGlossy cl_out)
-{
- float radiance = light_specular(light.data, cl_eval.ltc_mat, cl_in.N, cl_common.V, light.L);
- radiance *= cl_eval.ltc_brdf_scale;
- cl_out.radiance += light.data.l_color *
- (light.data.l_spec * light.vis * light.contact_shadow * radiance);
-}
-
-void closure_Glossy_planar_eval(ClosureInputGlossy cl_in,
- ClosureEvalGlossy cl_eval,
- inout ClosureEvalCommon cl_common,
- ClosurePlanarData planar,
- inout ClosureOutputGlossy cl_out)
-{
-#ifndef STEP_RESOLVE /* SSR already evaluates planar reflections. */
- float attenuation = planar.attenuation * probe_attenuation_planar_normal_roughness(
- planar.data, cl_in.N, cl_in.roughness);
-
- vec3 probe_radiance = probe_evaluate_planar(
- planar.id, planar.data, cl_common.P, cl_in.N, cl_common.V, cl_in.roughness);
-
- cl_out.radiance = mix(cl_out.radiance, probe_radiance, attenuation);
-#endif
-}
-
-void closure_Glossy_cubemap_eval(ClosureInputGlossy cl_in,
- ClosureEvalGlossy cl_eval,
- ClosureEvalCommon cl_common,
- ClosureCubemapData cube,
- inout ClosureOutputGlossy cl_out)
-{
- vec3 probe_radiance = probe_evaluate_cube(
- cube.id, cl_common.P, cl_eval.probe_sampling_dir, cl_in.roughness);
- cl_out.radiance += cube.attenuation * probe_radiance;
-}
-
-void closure_Glossy_indirect_end(ClosureInputGlossy cl_in,
- ClosureEvalGlossy cl_eval,
- ClosureEvalCommon cl_common,
- inout ClosureOutputGlossy cl_out)
-{
- /* If not enough light has been accumulated from probes, use the world specular cubemap
- * to fill the remaining energy needed. */
- if (specToggle && cl_common.specular_accum > 0.0) {
- vec3 probe_radiance = probe_evaluate_world_spec(cl_eval.probe_sampling_dir, cl_in.roughness);
- cl_out.radiance += cl_common.specular_accum * probe_radiance;
- }
-
- /* Apply occlusion on distant lighting. */
- cl_out.radiance *= cl_eval.spec_occlusion;
- /* Apply Raytrace reflections after occlusion since they are direct, local reflections. */
- cl_out.radiance += cl_eval.raytrace_radiance;
-}
-
-void closure_Glossy_eval_end(ClosureInputGlossy cl_in,
- ClosureEvalGlossy cl_eval,
- ClosureEvalCommon cl_common,
- inout ClosureOutputGlossy cl_out)
-{
-#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
- /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
- cl_out.radiance = vec3(0.0);
- return;
-#endif
-
- if (!specToggle) {
- cl_out.radiance = vec3(0.0);
- }
-}
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
deleted file mode 100644
index 311887cf2f5..00000000000
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
+++ /dev/null
@@ -1,324 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
-#pragma BLENDER_REQUIRE(lights_lib.glsl)
-#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
-
-/**
- * Extensive use of Macros to be able to change the maximum amount of evaluated closure easily.
- * NOTE: GLSL does not support variadic macros.
- *
- * Example
- * // Declare the cl_eval function
- * CLOSURE_EVAL_FUNCTION_DECLARE_3(name, Diffuse, Glossy, Refraction);
- * // Declare the inputs & outputs
- * CLOSURE_VARS_DECLARE(Diffuse, Glossy, Refraction);
- * // Specify inputs
- * in_Diffuse_0.N = N;
- * ...
- * // Call the cl_eval function
- * CLOSURE_EVAL_FUNCTION_3(name, Diffuse, Glossy, Refraction);
- * // Get the cl_out
- * closure.radiance = out_Diffuse_0.radiance + out_Glossy_1.radiance + out_Refraction_2.radiance;
- */
-
-#define CLOSURE_VARS_DECLARE(t0, t1, t2, t3) \
- ClosureInputCommon in_common = CLOSURE_INPUT_COMMON_DEFAULT; \
- ClosureInput##t0 in_##t0##_0 = CLOSURE_INPUT_##t0##_DEFAULT; \
- ClosureInput##t1 in_##t1##_1 = CLOSURE_INPUT_##t1##_DEFAULT; \
- ClosureInput##t2 in_##t2##_2 = CLOSURE_INPUT_##t2##_DEFAULT; \
- ClosureInput##t3 in_##t3##_3 = CLOSURE_INPUT_##t3##_DEFAULT; \
- ClosureOutput##t0 out_##t0##_0; \
- ClosureOutput##t1 out_##t1##_1; \
- ClosureOutput##t2 out_##t2##_2; \
- ClosureOutput##t3 out_##t3##_3;
-
-#define CLOSURE_EVAL_DECLARE(t0, t1, t2, t3) \
- ClosureEvalCommon cl_common = closure_Common_eval_init(in_common); \
- ClosureEval##t0 eval_##t0##_0 = closure_##t0##_eval_init(in_##t0##_0, cl_common, out_##t0##_0); \
- ClosureEval##t1 eval_##t1##_1 = closure_##t1##_eval_init(in_##t1##_1, cl_common, out_##t1##_1); \
- ClosureEval##t2 eval_##t2##_2 = closure_##t2##_eval_init(in_##t2##_2, cl_common, out_##t2##_2); \
- ClosureEval##t3 eval_##t3##_3 = closure_##t3##_eval_init(in_##t3##_3, cl_common, out_##t3##_3);
-
-#define CLOSURE_META_SUBROUTINE(subroutine, t0, t1, t2, t3) \
- closure_##t0##_##subroutine(in_##t0##_0, eval_##t0##_0, cl_common, out_##t0##_0); \
- closure_##t1##_##subroutine(in_##t1##_1, eval_##t1##_1, cl_common, out_##t1##_1); \
- closure_##t2##_##subroutine(in_##t2##_2, eval_##t2##_2, cl_common, out_##t2##_2); \
- closure_##t3##_##subroutine(in_##t3##_3, eval_##t3##_3, cl_common, out_##t3##_3);
-
-#define CLOSURE_META_SUBROUTINE_DATA(subroutine, sub_data, t0, t1, t2, t3) \
- closure_##t0##_##subroutine(in_##t0##_0, eval_##t0##_0, cl_common, sub_data, out_##t0##_0); \
- closure_##t1##_##subroutine(in_##t1##_1, eval_##t1##_1, cl_common, sub_data, out_##t1##_1); \
- closure_##t2##_##subroutine(in_##t2##_2, eval_##t2##_2, cl_common, sub_data, out_##t2##_2); \
- closure_##t3##_##subroutine(in_##t3##_3, eval_##t3##_3, cl_common, sub_data, out_##t3##_3);
-
-#ifndef DEPTH_SHADER
-/* Inputs are inout so that callers can get the final inputs used for evaluation. */
-# define CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3) \
- void closure_##name##_eval(ClosureInputCommon in_common, \
- inout ClosureInput##t0 in_##t0##_0, \
- inout ClosureInput##t1 in_##t1##_1, \
- inout ClosureInput##t2 in_##t2##_2, \
- inout ClosureInput##t3 in_##t3##_3, \
- out ClosureOutput##t0 out_##t0##_0, \
- out ClosureOutput##t1 out_##t1##_1, \
- out ClosureOutput##t2 out_##t2##_2, \
- out ClosureOutput##t3 out_##t3##_3) \
- { \
- CLOSURE_EVAL_DECLARE(t0, t1, t2, t3); \
-\
- /* Starts at 1 because 0 is world cubemap. */ \
- for (int i = 1; cl_common.specular_accum > 0.0 && i < prbNumRenderCube && i < MAX_PROBE; \
- i++) { \
- ClosureCubemapData cube = closure_cubemap_eval_init(i, cl_common); \
- if (cube.attenuation > 1e-8) { \
- CLOSURE_META_SUBROUTINE_DATA(cubemap_eval, cube, t0, t1, t2, t3); \
- } \
- } \
-\
- /* Starts at 1 because 0 is world irradiance. */ \
- for (int i = 1; cl_common.diffuse_accum > 0.0 && i < prbNumRenderGrid && i < MAX_GRID; \
- i++) { \
- ClosureGridData grid = closure_grid_eval_init(i, cl_common); \
- if (grid.attenuation > 1e-8) { \
- CLOSURE_META_SUBROUTINE_DATA(grid_eval, grid, t0, t1, t2, t3); \
- } \
- } \
-\
- CLOSURE_META_SUBROUTINE(indirect_end, t0, t1, t2, t3); \
-\
- ClosurePlanarData planar = closure_planar_eval_init(cl_common); \
- if (planar.attenuation > 1e-8) { \
- CLOSURE_META_SUBROUTINE_DATA(planar_eval, planar, t0, t1, t2, t3); \
- } \
-\
- for (int i = 0; i < laNumLight && i < MAX_LIGHT; i++) { \
- ClosureLightData light = closure_light_eval_init(cl_common, i); \
- if (light.vis > 1e-8) { \
- CLOSURE_META_SUBROUTINE_DATA(light_eval, light, t0, t1, t2, t3); \
- } \
- } \
-\
- CLOSURE_META_SUBROUTINE(eval_end, t0, t1, t2, t3); \
- }
-
-#else
-/* Inputs are inout so that callers can get the final inputs used for evaluation. */
-# define CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3) \
- void closure_##name##_eval(ClosureInputCommon in_common, \
- inout ClosureInput##t0 in_##t0##_0, \
- inout ClosureInput##t1 in_##t1##_1, \
- inout ClosureInput##t2 in_##t2##_2, \
- inout ClosureInput##t3 in_##t3##_3, \
- out ClosureOutput##t0 out_##t0##_0, \
- out ClosureOutput##t1 out_##t1##_1, \
- out ClosureOutput##t2 out_##t2##_2, \
- out ClosureOutput##t3 out_##t3##_3) \
- { \
- CLOSURE_EVAL_DECLARE(t0, t1, t2, t3); \
- }
-#endif
-
-#define CLOSURE_EVAL_FUNCTION(name, t0, t1, t2, t3) \
- closure_##name##_eval(in_common, \
- in_##t0##_0, \
- in_##t1##_1, \
- in_##t2##_2, \
- in_##t3##_3, \
- out_##t0##_0, \
- out_##t1##_1, \
- out_##t2##_2, \
- out_##t3##_3)
-
-#define CLOSURE_EVAL_FUNCTION_DECLARE_1(name, t0) \
- CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, Dummy, Dummy, Dummy)
-#define CLOSURE_EVAL_FUNCTION_DECLARE_2(name, t0, t1) \
- CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, Dummy, Dummy)
-#define CLOSURE_EVAL_FUNCTION_DECLARE_3(name, t0, t1, t2) \
- CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, Dummy)
-#define CLOSURE_EVAL_FUNCTION_DECLARE_4(name, t0, t1, t2, t3) \
- CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3)
-
-#define CLOSURE_VARS_DECLARE_1(t0) CLOSURE_VARS_DECLARE(t0, Dummy, Dummy, Dummy)
-#define CLOSURE_VARS_DECLARE_2(t0, t1) CLOSURE_VARS_DECLARE(t0, t1, Dummy, Dummy)
-#define CLOSURE_VARS_DECLARE_3(t0, t1, t2) CLOSURE_VARS_DECLARE(t0, t1, t2, Dummy)
-#define CLOSURE_VARS_DECLARE_4(t0, t1, t2, t3) CLOSURE_VARS_DECLARE(t0, t1, t2, t3)
-
-#define CLOSURE_EVAL_FUNCTION_1(name, t0) CLOSURE_EVAL_FUNCTION(name, t0, Dummy, Dummy, Dummy)
-#define CLOSURE_EVAL_FUNCTION_2(name, t0, t1) CLOSURE_EVAL_FUNCTION(name, t0, t1, Dummy, Dummy)
-#define CLOSURE_EVAL_FUNCTION_3(name, t0, t1, t2) CLOSURE_EVAL_FUNCTION(name, t0, t1, t2, Dummy)
-#define CLOSURE_EVAL_FUNCTION_4(name, t0, t1, t2, t3) CLOSURE_EVAL_FUNCTION(name, t0, t1, t2, t3)
-
-/* -------------------------------------------------------------------- */
-/** \name Dummy Closure
- *
- * Dummy closure type that will be optimized out by the compiler.
- * \{ */
-
-#define ClosureInputDummy ClosureOutput
-#define ClosureOutputDummy ClosureOutput
-#define ClosureEvalDummy ClosureOutput
-#define CLOSURE_EVAL_DUMMY ClosureOutput(vec3(0))
-#define CLOSURE_INPUT_Dummy_DEFAULT CLOSURE_EVAL_DUMMY
-#define closure_Dummy_eval_init(cl_in, cl_common, cl_out) CLOSURE_EVAL_DUMMY
-#define closure_Dummy_planar_eval(cl_in, cl_eval, cl_common, data, cl_out)
-#define closure_Dummy_cubemap_eval(cl_in, cl_eval, cl_common, data, cl_out)
-#define closure_Dummy_grid_eval(cl_in, cl_eval, cl_common, data, cl_out)
-#define closure_Dummy_indirect_end(cl_in, cl_eval, cl_common, cl_out)
-#define closure_Dummy_light_eval(cl_in, cl_eval, cl_common, data, cl_out)
-#define closure_Dummy_eval_end(cl_in, cl_eval, cl_common, cl_out)
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Common cl_eval data
- *
- * Eval data not dependent on input parameters. All might not be used but unused ones
- * will be optimized out.
- * \{ */
-
-struct ClosureInputCommon {
- /** Custom occlusion value set by the user. */
- float occlusion;
-};
-
-#define CLOSURE_INPUT_COMMON_DEFAULT ClosureInputCommon(1.0)
-
-struct ClosureEvalCommon {
- /** Result of SSAO. */
- OcclusionData occlusion_data;
- /** View vector. */
- vec3 V;
- /** Surface position. */
- vec3 P;
- /** Normal vector, always facing camera. */
- vec3 N;
- /** Normal vector, always facing camera. (viewspace) */
- vec3 vN;
- /** Surface position. (viewspace) */
- vec3 vP;
- /** Geometric normal, always facing camera. */
- vec3 Ng;
- /** Geometric normal, always facing camera. (viewspace) */
- vec3 vNg;
- /** Random numbers. 3 random sequences. zw is a random point on a circle. */
- vec4 rand;
- /** Specular probe accumulator. Shared between planar and cubemap probe. */
- float specular_accum;
- /** Diffuse probe accumulator. */
- float diffuse_accum;
-};
-
-/* Common cl_out struct used by most closures. */
-struct ClosureOutput {
- vec3 radiance;
-};
-
-/* Workaround for screenspace shadows in SSR pass. */
-float FragDepth;
-
-ClosureEvalCommon closure_Common_eval_init(ClosureInputCommon cl_in)
-{
- ClosureEvalCommon cl_eval;
- cl_eval.rand = texelfetch_noise_tex(gl_FragCoord.xy);
- cl_eval.V = cameraVec(worldPosition);
- cl_eval.P = worldPosition;
- cl_eval.N = safe_normalize(gl_FrontFacing ? worldNormal : -worldNormal);
- cl_eval.vN = safe_normalize(gl_FrontFacing ? viewNormal : -viewNormal);
- cl_eval.vP = viewPosition;
- cl_eval.Ng = safe_normalize(cross(dFdx(cl_eval.P), dFdy(cl_eval.P)));
- cl_eval.vNg = transform_direction(ViewMatrix, cl_eval.Ng);
-
- cl_eval.occlusion_data = occlusion_load(cl_eval.vP, cl_in.occlusion);
-
- cl_eval.specular_accum = 1.0;
- cl_eval.diffuse_accum = 1.0;
- return cl_eval;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Loop data
- *
- * Loop data is conveniently packed into struct to make it future proof.
- * \{ */
-
-struct ClosureLightData {
- LightData data; /** Light Data. */
- vec4 L; /** Non-Normalized Light Vector (surface to light) with length in W component. */
- float vis; /** Light visibility. */
- float contact_shadow; /** Result of contact shadow tracing. */
-};
-
-ClosureLightData closure_light_eval_init(ClosureEvalCommon cl_common, int light_id)
-{
- ClosureLightData light;
- light.data = lights_data[light_id];
-
- light.L.xyz = light.data.l_position - cl_common.P;
- light.L.w = length(light.L.xyz);
-
- light.vis = light_visibility(light.data, cl_common.P, light.L);
- light.contact_shadow = light_contact_shadows(
- light.data, cl_common.P, cl_common.vP, cl_common.vNg, cl_common.rand.x, light.vis);
-
- return light;
-}
-
-struct ClosureCubemapData {
- int id; /** Probe id. */
- float attenuation; /** Attenuation. */
-};
-
-ClosureCubemapData closure_cubemap_eval_init(int cube_id, inout ClosureEvalCommon cl_common)
-{
- ClosureCubemapData cube;
- cube.id = cube_id;
- cube.attenuation = probe_attenuation_cube(cube_id, cl_common.P);
- cube.attenuation = min(cube.attenuation, cl_common.specular_accum);
- cl_common.specular_accum -= cube.attenuation;
- return cube;
-}
-
-struct ClosurePlanarData {
- int id; /** Probe id. */
- PlanarData data; /** planars_data[id]. */
- float attenuation; /** Attenuation. */
-};
-
-ClosurePlanarData closure_planar_eval_init(inout ClosureEvalCommon cl_common)
-{
- ClosurePlanarData planar;
- planar.attenuation = 0.0;
-
- /* TODO(fclem): Find planar with the maximum weight. */
- for (int i = 0; i < prbNumPlanar && i < MAX_PLANAR; i++) {
- float attenuation = probe_attenuation_planar(planars_data[i], cl_common.P);
- if (attenuation > planar.attenuation) {
- planar.id = i;
- planar.attenuation = attenuation;
- planar.data = planars_data[i];
- }
- }
- return planar;
-}
-
-struct ClosureGridData {
- int id; /** Grid id. */
- GridData data; /** grids_data[id] */
- float attenuation; /** Attenuation. */
- vec3 local_pos; /** Local position inside the grid. */
-};
-
-ClosureGridData closure_grid_eval_init(int id, inout ClosureEvalCommon cl_common)
-{
- ClosureGridData grid;
- grid.id = id;
- grid.data = grids_data[id];
- grid.attenuation = probe_attenuation_grid(grid.data, cl_common.P, grid.local_pos);
- grid.attenuation = min(grid.attenuation, cl_common.diffuse_accum);
- cl_common.diffuse_accum -= grid.attenuation;
- return grid;
-}
-
-/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl
deleted file mode 100644
index 9011eea07c4..00000000000
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl
+++ /dev/null
@@ -1,128 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
-#pragma BLENDER_REQUIRE(lights_lib.glsl)
-#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
-#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
-#pragma BLENDER_REQUIRE(ssr_lib.glsl)
-
-struct ClosureInputRefraction {
- vec3 N; /** Shading normal. */
- float roughness; /** Input roughness, not squared. */
- float ior; /** Index of refraction ratio. */
-};
-
-#define CLOSURE_INPUT_Refraction_DEFAULT ClosureInputRefraction(vec3(0.0), 0.0, 0.0)
-
-struct ClosureEvalRefraction {
- vec3 P; /** LTC matrix values. */
- vec3 ltc_brdf; /** LTC BRDF values. */
- vec3 probe_sampling_dir; /** Direction to sample probes from. */
- float probes_weight; /** Factor to apply to probe radiance. */
-};
-
-/* Stubs. */
-#define ClosureOutputRefraction ClosureOutput
-#define closure_Refraction_grid_eval(cl_in, cl_eval, cl_common, data, cl_out)
-
-ClosureEvalRefraction closure_Refraction_eval_init(inout ClosureInputRefraction cl_in,
- ClosureEvalCommon cl_common,
- out ClosureOutputRefraction cl_out)
-{
- cl_in.N = safe_normalize(cl_in.N);
- cl_in.roughness = clamp(cl_in.roughness, 1e-8, 0.9999);
- cl_in.ior = max(cl_in.ior, 1e-5);
- cl_out.radiance = vec3(0.0);
-
- ClosureEvalRefraction cl_eval;
- vec3 cl_V;
- float eval_ior;
- /* Refract the view vector using the depth heuristic.
- * Then later Refract a second time the already refracted
- * ray using the inverse ior. */
- if (refractionDepth > 0.0) {
- eval_ior = 1.0 / cl_in.ior;
- cl_V = -refract(-cl_common.V, cl_in.N, eval_ior);
- vec3 plane_pos = cl_common.P - cl_in.N * refractionDepth;
- cl_eval.P = line_plane_intersect(cl_common.P, cl_V, plane_pos, cl_in.N);
- }
- else {
- eval_ior = cl_in.ior;
- cl_V = cl_common.V;
- cl_eval.P = cl_common.P;
- }
-
- cl_eval.probe_sampling_dir = refraction_dominant_dir(cl_in.N, cl_V, cl_in.roughness, eval_ior);
- cl_eval.probes_weight = 1.0;
-
-#ifdef USE_REFRACTION
- if (ssrefractToggle && cl_in.roughness < ssrMaxRoughness + 0.2) {
- /* Find approximated position of the 2nd refraction event. */
- vec3 vP = (refractionDepth > 0.0) ? transform_point(ViewMatrix, cl_eval.P) : cl_common.vP;
- vec4 ssr_output = screen_space_refraction(
- vP, cl_in.N, cl_V, eval_ior, sqr(cl_in.roughness), cl_common.rand);
- ssr_output.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, cl_in.roughness);
- cl_out.radiance += ssr_output.rgb * ssr_output.a;
- cl_eval.probes_weight -= ssr_output.a;
- }
-#endif
- return cl_eval;
-}
-
-void closure_Refraction_light_eval(ClosureInputRefraction cl_in,
- ClosureEvalRefraction cl_eval,
- ClosureEvalCommon cl_common,
- ClosureLightData light,
- inout ClosureOutputRefraction cl_out)
-{
- /* Not implemented yet. */
-}
-
-void closure_Refraction_planar_eval(ClosureInputRefraction cl_in,
- ClosureEvalRefraction cl_eval,
- ClosureEvalCommon cl_common,
- ClosurePlanarData planar,
- inout ClosureOutputRefraction cl_out)
-{
- /* Not implemented yet. */
-}
-
-void closure_Refraction_cubemap_eval(ClosureInputRefraction cl_in,
- ClosureEvalRefraction cl_eval,
- ClosureEvalCommon cl_common,
- ClosureCubemapData cube,
- inout ClosureOutputRefraction cl_out)
-{
- vec3 probe_radiance = probe_evaluate_cube(
- cube.id, cl_eval.P, cl_eval.probe_sampling_dir, sqr(cl_in.roughness));
- cl_out.radiance += (cube.attenuation * cl_eval.probes_weight) * probe_radiance;
-}
-
-void closure_Refraction_indirect_end(ClosureInputRefraction cl_in,
- ClosureEvalRefraction cl_eval,
- ClosureEvalCommon cl_common,
- inout ClosureOutputRefraction cl_out)
-{
- /* If not enough light has been accumulated from probes, use the world specular cubemap
- * to fill the remaining energy needed. */
- if (specToggle && cl_common.specular_accum > 0.0) {
- vec3 probe_radiance = probe_evaluate_world_spec(cl_eval.probe_sampling_dir,
- sqr(cl_in.roughness));
- cl_out.radiance += (cl_common.specular_accum * cl_eval.probes_weight) * probe_radiance;
- }
-}
-
-void closure_Refraction_eval_end(ClosureInputRefraction cl_in,
- ClosureEvalRefraction cl_eval,
- ClosureEvalCommon cl_common,
- inout ClosureOutputRefraction cl_out)
-{
-#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
- /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
- cl_out.radiance = vec3(0.0);
- return;
-#endif
-
- if (!specToggle) {
- cl_out.radiance = vec3(0.0);
- }
-}
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl
deleted file mode 100644
index 183219c9088..00000000000
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl
+++ /dev/null
@@ -1,71 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
-#pragma BLENDER_REQUIRE(lights_lib.glsl)
-#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
-#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
-
-struct ClosureInputTranslucent {
- vec3 N; /** Shading normal. */
-};
-
-#define CLOSURE_INPUT_Translucent_DEFAULT ClosureInputTranslucent(vec3(0.0))
-
-/* Stubs. */
-#define ClosureEvalTranslucent ClosureEvalDummy
-#define ClosureOutputTranslucent ClosureOutput
-#define closure_Translucent_planar_eval(cl_in, cl_eval, cl_common, data, cl_out)
-#define closure_Translucent_cubemap_eval(cl_in, cl_eval, cl_common, data, cl_out)
-
-ClosureEvalTranslucent closure_Translucent_eval_init(inout ClosureInputTranslucent cl_in,
- ClosureEvalCommon cl_common,
- out ClosureOutputTranslucent cl_out)
-{
- cl_in.N = safe_normalize(cl_in.N);
- cl_out.radiance = vec3(0.0);
- return CLOSURE_EVAL_DUMMY;
-}
-
-void closure_Translucent_light_eval(ClosureInputTranslucent cl_in,
- ClosureEvalTranslucent cl_eval,
- ClosureEvalCommon cl_common,
- ClosureLightData light,
- inout ClosureOutputTranslucent cl_out)
-{
- float radiance = light_diffuse(light.data, cl_in.N, cl_common.V, light.L);
- cl_out.radiance += light.data.l_color * (light.data.l_diff * light.vis * radiance);
-}
-
-void closure_Translucent_grid_eval(ClosureInputTranslucent cl_in,
- ClosureEvalTranslucent cl_eval,
- ClosureEvalCommon cl_common,
- ClosureGridData grid,
- inout ClosureOutputTranslucent cl_out)
-{
- vec3 probe_radiance = probe_evaluate_grid(grid.data, cl_common.P, cl_in.N, grid.local_pos);
- cl_out.radiance += grid.attenuation * probe_radiance;
-}
-
-void closure_Translucent_indirect_end(ClosureInputTranslucent cl_in,
- ClosureEvalTranslucent cl_eval,
- ClosureEvalCommon cl_common,
- inout ClosureOutputTranslucent cl_out)
-{
- /* If not enough light has been accumulated from probes, use the world specular cubemap
- * to fill the remaining energy needed. */
- if (cl_common.diffuse_accum > 0.0) {
- vec3 probe_radiance = probe_evaluate_world_diff(cl_in.N);
- cl_out.radiance += cl_common.diffuse_accum * probe_radiance;
- }
-}
-
-void closure_Translucent_eval_end(ClosureInputTranslucent cl_in,
- ClosureEvalTranslucent cl_eval,
- ClosureEvalCommon cl_common,
- inout ClosureOutputTranslucent cl_out)
-{
-#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
- /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
- cl_out.radiance = vec3(0.0);
- return;
-#endif
-}
diff --git a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl
deleted file mode 100644
index f66f45635f4..00000000000
--- a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl
+++ /dev/null
@@ -1,189 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
-#pragma BLENDER_REQUIRE(renderpass_lib.glsl)
-
-#ifndef VOLUMETRICS
-
-uniform int outputSsrId = 1;
-uniform int outputSssId = 1;
-
-#endif
-
-struct Closure {
-#ifdef VOLUMETRICS
- vec3 absorption;
- vec3 scatter;
- vec3 emission;
- float anisotropy;
-
-#else /* SURFACE */
- vec3 radiance;
- vec3 transmittance;
- float holdout;
- vec4 ssr_data;
- vec2 ssr_normal;
- int flag;
-# ifdef USE_SSS
- vec3 sss_irradiance;
- vec3 sss_albedo;
- float sss_radius;
-# endif
-
-#endif
-};
-
-/* Prototype */
-Closure nodetree_exec(void);
-
-/* clang-format off */
-/* Avoid multi-line defines. */
-#ifdef VOLUMETRICS
-# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), vec3(0), 0.0)
-#elif !defined(USE_SSS)
-# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), 0.0, vec4(0), vec2(0), 0)
-#else
-# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), 0.0, vec4(0), vec2(0), 0, vec3(0), vec3(0), 0.0)
-#endif
-/* clang-format on */
-
-#define FLAG_TEST(flag, val) (((flag) & (val)) != 0)
-
-#define CLOSURE_SSR_FLAG 1
-#define CLOSURE_SSS_FLAG 2
-#define CLOSURE_HOLDOUT_FLAG 4
-
-#ifdef VOLUMETRICS
-Closure closure_mix(Closure cl1, Closure cl2, float fac)
-{
- Closure cl;
- cl.absorption = mix(cl1.absorption, cl2.absorption, fac);
- cl.scatter = mix(cl1.scatter, cl2.scatter, fac);
- cl.emission = mix(cl1.emission, cl2.emission, fac);
- cl.anisotropy = mix(cl1.anisotropy, cl2.anisotropy, fac);
- return cl;
-}
-
-Closure closure_add(Closure cl1, Closure cl2)
-{
- Closure cl;
- cl.absorption = cl1.absorption + cl2.absorption;
- cl.scatter = cl1.scatter + cl2.scatter;
- cl.emission = cl1.emission + cl2.emission;
- cl.anisotropy = (cl1.anisotropy + cl2.anisotropy) / 2.0; /* Average phase (no multi lobe) */
- return cl;
-}
-
-Closure closure_emission(vec3 rgb)
-{
- Closure cl = CLOSURE_DEFAULT;
- cl.emission = rgb;
- return cl;
-}
-
-#else /* SURFACE */
-
-Closure closure_mix(Closure cl1, Closure cl2, float fac)
-{
- Closure cl;
- cl.holdout = mix(cl1.holdout, cl2.holdout, fac);
-
- if (FLAG_TEST(cl1.flag, CLOSURE_HOLDOUT_FLAG)) {
- fac = 1.0;
- }
- else if (FLAG_TEST(cl2.flag, CLOSURE_HOLDOUT_FLAG)) {
- fac = 0.0;
- }
-
- cl.transmittance = mix(cl1.transmittance, cl2.transmittance, fac);
- cl.radiance = mix(cl1.radiance, cl2.radiance, fac);
- cl.flag = cl1.flag | cl2.flag;
- cl.ssr_data = mix(cl1.ssr_data, cl2.ssr_data, fac);
- bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG);
- /* When mixing SSR don't blend roughness and normals but only specular (ssr_data.xyz). */
- cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w;
- cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal;
-
-# ifdef USE_SSS
- cl.sss_albedo = mix(cl1.sss_albedo, cl2.sss_albedo, fac);
- bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG);
- /* It also does not make sense to mix SSS radius or irradiance. */
- cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius;
- cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance;
-# endif
- return cl;
-}
-
-Closure closure_add(Closure cl1, Closure cl2)
-{
- Closure cl;
- cl.transmittance = cl1.transmittance + cl2.transmittance;
- cl.radiance = cl1.radiance + cl2.radiance;
- cl.holdout = cl1.holdout + cl2.holdout;
- cl.flag = cl1.flag | cl2.flag;
- cl.ssr_data = cl1.ssr_data + cl2.ssr_data;
- bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG);
- /* When mixing SSR don't blend roughness and normals. */
- cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w;
- cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal;
-
-# ifdef USE_SSS
- cl.sss_albedo = cl1.sss_albedo + cl2.sss_albedo;
- bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG);
- /* It also does not make sense to mix SSS radius or irradiance. */
- cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius;
- cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance;
-# endif
- return cl;
-}
-
-Closure closure_emission(vec3 rgb)
-{
- Closure cl = CLOSURE_DEFAULT;
- cl.radiance = rgb;
- return cl;
-}
-
-#endif
-
-#ifndef VOLUMETRICS
-
-/* Let radiance passthrough or replace it to get the BRDF and color
- * to applied to the SSR result. */
-vec3 closure_mask_ssr_radiance(vec3 radiance, float ssr_id)
-{
- return (ssrToggle && int(ssr_id) == outputSsrId) ? vec3(1.0) : radiance;
-}
-
-void closure_load_ssr_data(
- vec3 ssr_radiance, float roughness, vec3 N, float ssr_id, inout Closure cl)
-{
- /* Still encode to avoid artifacts in the SSR pass. */
- vec3 vN = normalize(mat3(ViewMatrix) * N);
- cl.ssr_normal = normal_encode(vN, viewCameraVec(viewPosition));
-
- if (ssrToggle && int(ssr_id) == outputSsrId) {
- cl.ssr_data = vec4(ssr_radiance, roughness);
- cl.flag |= CLOSURE_SSR_FLAG;
- }
- else {
- cl.radiance += ssr_radiance;
- }
-}
-
-void closure_load_sss_data(
- float radius, vec3 sss_irradiance, vec3 sss_albedo, int sss_id, inout Closure cl)
-{
-# ifdef USE_SSS
- if (sss_id == outputSssId) {
- cl.sss_irradiance = sss_irradiance;
- cl.sss_radius = radius;
- cl.sss_albedo = sss_albedo;
- cl.flag |= CLOSURE_SSS_FLAG;
- /* Irradiance will be convolved by SSSS pass. Do not add to radiance. */
- sss_irradiance = vec3(0);
- }
-# endif
- cl.radiance += render_pass_diffuse_mask(vec3(1), sss_irradiance) * sss_albedo;
-}
-
-#endif
diff --git a/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl b/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl
deleted file mode 100644
index c935eca6a39..00000000000
--- a/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl
+++ /dev/null
@@ -1,73 +0,0 @@
-
-layout(std140) uniform common_block
-{
- mat4 pastViewProjectionMatrix;
- vec4 hizUvScale; /* To correct mip level texel misalignment */
- /* Ambient Occlusion */
- vec4 aoParameters[2];
- /* Volumetric */
- ivec4 volTexSize;
- vec4 volDepthParameters; /* Parameters to the volume Z equation */
- vec4 volInvTexSize;
- vec4 volJitter;
- vec4 volCoordScale; /* To convert volume uvs to screen uvs */
- float volHistoryAlpha;
- float volShadowSteps;
- bool volUseLights;
- bool volUseSoftShadows;
- /* Screen Space Reflections */
- vec4 ssrParameters;
- float ssrBorderFac;
- float ssrMaxRoughness;
- float ssrFireflyFac;
- float ssrBrdfBias;
- bool ssrToggle;
- bool ssrefractToggle;
- /* SubSurface Scattering */
- float sssJitterThreshold;
- bool sssToggle;
- /* Specular */
- bool specToggle;
- /* Lights */
- int laNumLight;
- /* Probes */
- int prbNumPlanar;
- int prbNumRenderCube;
- int prbNumRenderGrid;
- int prbIrradianceVisSize;
- float prbIrradianceSmooth;
- float prbLodCubeMax;
- /* Misc */
- int rayType;
- float rayDepth;
- float alphaHashOffset;
- float alphaHashScale;
- float pad6;
- float pad7;
- float pad8;
- float pad9;
-};
-
-/* rayType (keep in sync with ray_type) */
-#define EEVEE_RAY_CAMERA 0
-#define EEVEE_RAY_SHADOW 1
-#define EEVEE_RAY_DIFFUSE 2
-#define EEVEE_RAY_GLOSSY 3
-
-/* aoParameters */
-#define aoDistance aoParameters[0].x
-#define aoSamples aoParameters[0].y /* UNUSED */
-#define aoFactor aoParameters[0].z
-#define aoInvSamples aoParameters[0].w /* UNUSED */
-
-#define aoOffset aoParameters[1].x /* UNUSED */
-#define aoBounceFac aoParameters[1].y
-#define aoQuality aoParameters[1].z
-#define aoSettings aoParameters[1].w
-
-/* ssrParameters */
-#define ssrQuality ssrParameters.x
-#define ssrThickness ssrParameters.y
-#define ssrPixelSize ssrParameters.zw
-
-#define ssrUvScale hizUvScale.zw
diff --git a/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl b/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl
deleted file mode 100644
index 77a1560f3a7..00000000000
--- a/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl
+++ /dev/null
@@ -1,108 +0,0 @@
-
-#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl)
-
-/* ---------------------------------------------------------------------- */
-/** \name Utiltex
- *
- * Utiltex is a sampler2DArray that stores a number of useful small utilitary textures and lookup
- * tables.
- * \{ */
-
-uniform sampler2DArray utilTex;
-
-#define LUT_SIZE 64
-
-#define LTC_MAT_LAYER 0
-#define LTC_BRDF_LAYER 1
-#define BRDF_LUT_LAYER 1
-#define NOISE_LAYER 2
-#define LTC_DISK_LAYER 3 /* UNUSED */
-
-/* Layers 4 to 20 are for BTDF Lut. */
-const float lut_btdf_layer_first = 4.0;
-const float lut_btdf_layer_count = 16.0;
-
-/**
- * Reminder: The 4 noise values are based of 3 uncorrelated blue noises:
- * x : Uniformly distributed value [0..1] (noise 1).
- * y : Uniformly distributed value [0..1] (noise 2).
- * z,w : Uniformly distributed point on the unit circle [-1..1] (noise 3).
- */
-#define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0)
-
-/* Return texture coordinates to sample Surface LUT. */
-vec2 lut_coords(float cos_theta, float roughness)
-{
- vec2 coords = vec2(roughness, sqrt(1.0 - cos_theta));
- /* scale and bias coordinates, for correct filtered lookup */
- return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE;
-}
-
-/* Returns the GGX split-sum precomputed in LUT. */
-vec2 brdf_lut(float cos_theta, float roughness)
-{
- return textureLod(utilTex, vec3(lut_coords(cos_theta, roughness), BRDF_LUT_LAYER), 0.0).rg;
-}
-
-/* Return texture coordinates to sample Surface LUT. */
-vec3 lut_coords_btdf(float cos_theta, float roughness, float ior)
-{
- /* ior is sin of critical angle. */
- float critical_cos = sqrt(1.0 - ior * ior);
-
- vec3 coords;
- coords.x = sqr(ior);
- coords.y = cos_theta;
- coords.y -= critical_cos;
- coords.y /= (coords.y > 0.0) ? (1.0 - critical_cos) : critical_cos;
- coords.y = coords.y * 0.5 + 0.5;
- coords.z = roughness;
-
- coords = saturate(coords);
-
- /* scale and bias coordinates, for correct filtered lookup */
- coords.xy = coords.xy * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE;
-
- return coords;
-}
-
-/* Returns GGX BTDF in first component and fresnel in second. */
-vec2 btdf_lut(float cos_theta, float roughness, float ior)
-{
- if (ior <= 1e-5) {
- return vec2(0.0);
- }
-
- if (ior >= 1.0) {
- vec2 split_sum = brdf_lut(cos_theta, roughness);
- float f0 = f0_from_ior(ior);
- /* Baked IOR for GGX BRDF. */
- const float specular = 1.0;
- const float eta_brdf = (2.0 / (1.0 - sqrt(0.08 * specular))) - 1.0;
- /* Avoid harsh transition coming from ior == 1. */
- float f90 = fast_sqrt(saturate(f0 / (f0_from_ior(eta_brdf) * 0.25)));
- float fresnel = F_brdf_single_scatter(vec3(f0), vec3(f90), split_sum).r;
- /* Setting the BTDF to one is not really important since it is only used for multiscatter
- * and it's already quite close to ground truth. */
- float btdf = 1.0;
- return vec2(btdf, fresnel);
- }
-
- vec3 coords = lut_coords_btdf(cos_theta, roughness, ior);
-
- float layer = coords.z * lut_btdf_layer_count;
- float layer_floored = floor(layer);
-
- coords.z = lut_btdf_layer_first + layer_floored;
- vec2 btdf_low = textureLod(utilTex, coords, 0.0).rg;
-
- coords.z += 1.0;
- vec2 btdf_high = textureLod(utilTex, coords, 0.0).rg;
-
- /* Manual trilinear interpolation. */
- vec2 btdf = mix(btdf_low, btdf_high, layer - layer_floored);
-
- return btdf;
-}
-
-/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl b/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl
deleted file mode 100644
index 9426b8e4a7b..00000000000
--- a/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl
+++ /dev/null
@@ -1,7 +0,0 @@
-uniform vec4 cryptohash;
-out vec4 fragColor;
-
-void main()
-{
- fragColor = cryptohash;
-}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_bsdf_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_bsdf_lib.glsl
new file mode 100644
index 00000000000..c015eaf09d7
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_bsdf_lib.glsl
@@ -0,0 +1,242 @@
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+/* -------------------------------------------------------------------- */
+/** \name Utility functions to work with BSDFs
+ * \{ */
+
+vec3 diffuse_dominant_dir(vec3 bent_normal)
+{
+ return bent_normal;
+}
+
+vec3 specular_dominant_dir(vec3 N, vec3 V, float roughness)
+{
+ vec3 R = -reflect(V, N);
+ float smoothness = 1.0 - roughness;
+ float fac = smoothness * (sqrt(smoothness) + roughness);
+ return normalize(mix(N, R, fac));
+}
+
+vec3 refraction_dominant_dir(vec3 N, vec3 V, float roughness, float ior)
+{
+ /* TODO: This a bad approximation. Better approximation should fit
+ * the refracted vector and roughness into the best prefiltered reflection
+ * lobe. */
+ /* Correct the IOR for ior < 1.0 to not see the abrupt delimitation or the TIR */
+ ior = (ior < 1.0) ? mix(ior, 1.0, roughness) : ior;
+ float eta = 1.0 / ior;
+
+ float NV = dot(N, -V);
+
+ /* Custom Refraction. */
+ float k = 1.0 - eta * eta * (1.0 - NV * NV);
+ k = max(0.0, k); /* Only this changes. */
+ vec3 R = eta * -V - (eta * NV + sqrt(k)) * N;
+
+ return R;
+}
+
+float ior_from_f0(float f0)
+{
+ float f = sqrt(f0);
+ return (-f - 1.0) / (f - 1.0);
+}
+
+/* Simplified form of F_eta(eta, 1.0). */
+float f0_from_ior(float eta)
+{
+ float A = (eta - 1.0) / (eta + 1.0);
+ return A * A;
+}
+
+/* Fresnel monochromatic, perfect mirror. */
+float F_eta(float eta, float cos_theta)
+{
+ /* Compute fresnel reflectance without explicitly computing the refracted direction. */
+ float c = abs(cos_theta);
+ float g = eta * eta - 1.0 + c * c;
+ if (g > 0.0) {
+ g = sqrt(g);
+ float A = (g - c) / (g + c);
+ float B = (c * (g + c) - 1.0) / (c * (g - c) + 1.0);
+ return 0.5 * A * A * (1.0 + B * B);
+ }
+ /* Total internal reflections. */
+ return 1.0;
+}
+
+/* Fresnel color blend base on fresnel factor. */
+vec3 F_color_blend(float eta, float fresnel, vec3 f0_color)
+{
+ float f0 = f0_from_ior(eta);
+ float fac = saturate((fresnel - f0) / (1.0 - f0));
+ return mix(f0_color, vec3(1.0), fac);
+}
+
+/* Fresnel split-sum approximation. */
+vec3 F_brdf_single_scatter(vec3 f0, vec3 f90, vec2 lut)
+{
+ /* Unreal specular matching : if specular color is below 2% intensity, treat as shadowning */
+ return lut.y * f90 + lut.x * f0;
+}
+
+/* Multi-scattering brdf approximation from :
+ * "A Multiple-Scattering Microfacet Model for Real-Time Image-based Lighting"
+ * by Carmelo J. Fdez-Agüera. */
+vec3 F_brdf_multi_scatter(vec3 f0, vec3 f90, vec2 lut)
+{
+ vec3 FssEss = lut.y * f90 + lut.x * f0;
+
+ float Ess = lut.x + lut.y;
+ float Ems = 1.0 - Ess;
+ vec3 Favg = f0 + (1.0 - f0) / 21.0;
+ vec3 Fms = FssEss * Favg / (1.0 - (1.0 - Ess) * Favg);
+ /* We don't do anything special for diffuse surfaces because the principle bsdf
+ * does not care about energy conservation of the specular layer for dielectrics. */
+ return FssEss + Fms * Ems;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Utility functions to work with BSDFs
+ * \{ */
+
+/* Same thing as Cycles without the comments to make it shorter. */
+vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N)
+{
+ vec3 R;
+ float NI = dot(N, I);
+ float NgR, threshold;
+ /* Check if the incident ray is coming from behind normal N. */
+ if (NI > 0.0) {
+ /* Normal reflection. */
+ R = (2.0 * NI) * N - I;
+ NgR = dot(Ng, R);
+ /* Reflection rays may always be at least as shallow as the incoming ray. */
+ threshold = min(0.9 * dot(Ng, I), 0.01);
+ if (NgR >= threshold) {
+ return N;
+ }
+ }
+ else {
+ /* Bad incident. */
+ R = -I;
+ NgR = dot(Ng, R);
+ threshold = 0.01;
+ }
+ /* Lift the reflection above the threshold. */
+ R = R + Ng * (threshold - NgR);
+ /* Find a bisector. */
+ return safe_normalize(I * length(R) + R * length(I));
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Cone angle approximation
+ *
+ * Returns a fitted cone angle given the input roughness
+ * \{ */
+
+float cone_cosine(float r)
+{
+ /* Using phong gloss
+ * roughness = sqrt(2/(gloss+2)) */
+ float gloss = -2 + 2 / (r * r);
+ /* Drobot 2014 in GPUPro5 */
+ // return cos(2.0 * sqrt(2.0 / (gloss + 2)));
+ /* Uludag 2014 in GPUPro5 */
+ // return pow(0.244, 1 / (gloss + 1));
+ /* Jimenez 2016 in Practical Realtime Strategies for Accurate Indirect Occlusion*/
+ return exp2(-3.32193 * r * r);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name BRDF & BTDF Look up table functions
+ *
+ * BRDFs are costly to evaluated at runtime. We precompute them by using the split-sum
+ * approximation method descibed by Brian Karis in "Real Shading in Unreal Engine 4" from
+ * Siggraph 2013.
+ * \{ */
+
+/* Return texture coordinates to sample Surface LUT. */
+vec2 lut_coords(float cos_theta, float roughness)
+{
+ vec2 coords = vec2(roughness, sqrt(1.0 - cos_theta));
+ /* scale and bias coordinates, for correct filtered lookup */
+ return coords * UTIL_TEX_UV_SCALE + UTIL_TEX_UV_BIAS;
+}
+
+/* Returns the GGX split-sum precomputed in LUT. */
+vec2 brdf_lut(float cos_theta, float roughness)
+{
+ return utility_tx_sample(lut_coords(cos_theta, roughness), UTIL_BSDF_LAYER).rg;
+}
+
+/* Return texture coordinates to sample Surface LUT. */
+vec3 lut_coords_btdf(float cos_theta, float roughness, float ior)
+{
+ /* ior is sin of critical angle. */
+ float critical_cos = sqrt(1.0 - ior * ior);
+
+ vec3 coords;
+ coords.x = sqr(ior);
+ coords.y = cos_theta;
+ coords.y -= critical_cos;
+ coords.y /= (coords.y > 0.0) ? (1.0 - critical_cos) : critical_cos;
+ coords.y = coords.y * 0.5 + 0.5;
+ coords.z = roughness;
+
+ coords = saturate(coords);
+
+ /* Scale and bias coordinates, for correct filtered lookup. */
+ coords.xy = coords.xy * UTIL_TEX_UV_SCALE + UTIL_TEX_UV_BIAS;
+
+ return coords;
+}
+
+/* Returns GGX BTDF in first component and fresnel in second. */
+vec2 btdf_lut(float cos_theta, float roughness, float ior)
+{
+ if (ior <= 1e-5) {
+ return vec2(0.0);
+ }
+
+ if (ior >= 1.0) {
+ vec2 split_sum = brdf_lut(cos_theta, roughness);
+ float f0 = f0_from_ior(ior);
+ /* Baked IOR for GGX BRDF. */
+ const float specular = 1.0;
+ const float eta_brdf = (2.0 / (1.0 - sqrt(0.08 * specular))) - 1.0;
+ /* Avoid harsh transition comming from ior == 1. */
+ float f90 = fast_sqrt(saturate(f0 / (f0_from_ior(eta_brdf) * 0.25)));
+ float fresnel = F_brdf_single_scatter(vec3(f0), vec3(f90), split_sum).r;
+ /* Setting the BTDF to one is not really important since it is only used for multiscatter
+ * and it's already quite close to ground truth. */
+ float btdf = 1.0;
+ return vec2(btdf, fresnel);
+ }
+
+ vec3 coords = lut_coords_btdf(cos_theta, roughness, ior);
+
+ float layer = coords.z * UTIL_BTDF_LAYER_COUNT;
+ float layer_floored = floor(layer);
+
+ coords.z = UTIL_BTDF_LAYER + layer_floored;
+ vec2 btdf_low = utility_tx_sample(coords.xy, coords.z).rg;
+
+ coords.z += 1.0;
+ vec2 btdf_high = utility_tx_sample(coords.xy, coords.z).rg;
+
+ /* Manual trilinear interpolation. */
+ vec2 btdf = mix(btdf_low, btdf_high, layer - layer_floored);
+
+ return btdf;
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_bsdf_microfacet_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_bsdf_microfacet_lib.glsl
new file mode 100644
index 00000000000..efb3c73797a
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_bsdf_microfacet_lib.glsl
@@ -0,0 +1,59 @@
+
+/* -------------------------------------------------------------------- */
+/** \name GGX
+ *
+ * \{ */
+
+float D_ggx_opti(float NH, float a2)
+{
+ float tmp = (NH * a2 - NH) * NH + 1.0;
+ return M_PI * tmp * tmp; /* Doing RCP and mul a2 at the end. */
+}
+
+float G1_Smith_GGX_opti(float NX, float a2)
+{
+ /* Using Brian Karis approach and refactoring by NX/NX
+ * this way the (2*NL)*(2*NV) in G = G1(V) * G1(L) gets canceled by the brdf denominator 4*NL*NV
+ * Rcp is done on the whole G later.
+ * Note that this is not convenient for the transmission formula. */
+ return NX + sqrt(NX * (NX - NX * a2) + a2);
+ /* return 2 / (1 + sqrt(1 + a2 * (1 - NX*NX) / (NX*NX) ) ); /* Reference function. */
+}
+
+float bsdf_ggx(vec3 N, vec3 L, vec3 V, float roughness)
+{
+ float a2 = sqr(roughness);
+
+ vec3 H = normalize(L + V);
+ float NH = max(dot(N, H), 1e-8);
+ float NL = max(dot(N, L), 1e-8);
+ float NV = max(dot(N, V), 1e-8);
+
+ float G = G1_Smith_GGX_opti(NV, a2) * G1_Smith_GGX_opti(NL, a2); /* Doing RCP at the end */
+ float D = D_ggx_opti(NH, a2);
+
+ /* Denominator is canceled by G1_Smith */
+ /* bsdf = D * G / (4.0 * NL * NV); /* Reference function. */
+ return NL * a2 / (D * G); /* NL to Fit cycles Equation : line. 345 in bsdf_microfacet.h */
+}
+
+float btdf_ggx(vec3 N, vec3 L, vec3 V, float roughness, float eta)
+{
+ float a2 = sqr(roughness);
+
+ vec3 H = normalize(L + V);
+ float NH = max(dot(N, H), 1e-8);
+ float NL = max(dot(N, L), 1e-8);
+ float NV = max(dot(N, V), 1e-8);
+ float VH = max(dot(V, H), 1e-8);
+ float LH = max(dot(L, H), 1e-8);
+ float Ht2 = sqr(eta * LH + VH);
+
+ float G = G1_Smith_GGX_opti(NV, a2) * G1_Smith_GGX_opti(NL, a2); /* Doing RCP at the end */
+ float D = D_ggx_opti(NH, a2);
+
+ /* btdf = abs(VH*LH) * (ior*ior) * D * G(V) * G(L) / (Ht2 * NV) */
+ return abs(VH * LH) * sqr(eta) * 4.0 * a2 / (D * G * (Ht2 * NV));
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_bsdf_sampling_lib.glsl
index e0f52338914..7928fe76cb8 100644
--- a/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/eevee_bsdf_sampling_lib.glsl
@@ -4,7 +4,7 @@
*/
#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
-#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_bsdf_microfacet_lib.glsl)
/* -------------------------------------------------------------------- */
/** \name Microfacet GGX distribution
@@ -12,19 +12,27 @@
#define USE_VISIBLE_NORMAL 1
-float pdf_ggx_reflect(float NH, float NV, float VH, float alpha)
+float sample_pdf_ggx_reflect(float NH, float NV, float VH, float G1, float alpha)
{
float a2 = sqr(alpha);
#if USE_VISIBLE_NORMAL
float D = a2 / D_ggx_opti(NH, a2);
- float G1 = NV * 2.0 / G1_Smith_GGX_opti(NV, a2);
return G1 * VH * D / NV;
#else
return NH * a2 / D_ggx_opti(NH, a2);
#endif
}
-vec3 sample_ggx(vec3 rand, float alpha, vec3 Vt)
+float sample_pdf_ggx_refract(
+ float NH, float NV, float VH, float LH, float G1, float alpha, float eta)
+{
+ float a2 = sqr(alpha);
+ float D = D_ggx_opti(NH, a2);
+ float Ht2 = sqr(eta * LH + VH);
+ return VH * abs(LH) * ((G1 * D) * sqr(eta) * a2 / (D * NV * Ht2));
+}
+
+vec3 sample_ggx(vec3 rand, float alpha, vec3 Vt, out float G1)
{
#if USE_VISIBLE_NORMAL
/* From:
@@ -41,6 +49,7 @@ vec3 sample_ggx(vec3 rand, float alpha, vec3 Vt)
float x = r * rand.y;
float y = r * rand.z;
float s = 0.5 * (1.0 + Vh.z);
+ G1 = 1.0 / s;
y = (1.0 - s) * sqrt(1.0 - x * x) + s * y;
float z = sqrt(saturate(1.0 - x * x - y * y));
/* Compute normal. */
@@ -60,15 +69,49 @@ vec3 sample_ggx(vec3 rand, float alpha, vec3 Vt)
#endif
}
-vec3 sample_ggx(vec3 rand, float alpha, vec3 V, vec3 N, vec3 T, vec3 B, out float pdf)
+vec3 sample_ggx_reflect(vec3 rand, float alpha, vec3 V, vec3 N, vec3 T, vec3 B, out float pdf)
{
+ float G1;
vec3 Vt = world_to_tangent(V, N, T, B);
- vec3 Ht = sample_ggx(rand, alpha, Vt);
+ vec3 Ht = sample_ggx(rand, alpha, Vt, G1);
float NH = saturate(Ht.z);
float NV = saturate(Vt.z);
- float VH = saturate(dot(Vt, Ht));
- pdf = pdf_ggx_reflect(NH, NV, VH, alpha);
- return tangent_to_world(Ht, N, T, B);
+ float VH = dot(Vt, Ht);
+ vec3 H = tangent_to_world(Ht, N, T, B);
+
+ if (VH > 0.0) {
+ vec3 L = reflect(-V, H);
+ pdf = sample_pdf_ggx_reflect(NH, NV, VH, G1, alpha);
+ return L;
+ }
+ else {
+ pdf = 0.0;
+ return vec3(1.0, 0.0, 0.0);
+ }
+}
+
+vec3 sample_ggx_refract(
+ vec3 rand, float alpha, float ior, vec3 V, vec3 N, vec3 T, vec3 B, out float pdf)
+{
+ float G1;
+ vec3 Vt = world_to_tangent(V, N, T, B);
+ vec3 Ht = sample_ggx(rand, alpha, Vt, G1);
+ float NH = saturate(Ht.z);
+ float NV = saturate(Vt.z);
+ float VH = dot(Vt, Ht);
+ vec3 H = tangent_to_world(Ht, N, T, B);
+
+ if (VH > 0.0) {
+ /* NOTE: Ior is already inverted for front faces. */
+ vec3 L = refract(-V, H, ior);
+ float LH = dot(L, H);
+ pdf = sample_pdf_ggx_refract(NH, NV, VH, LH, G1, alpha, ior);
+ return L;
+ }
+ else {
+ pdf = 0.0;
+ return vec3(1.0, 0.0, 0.0);
+ }
}
/** \} */
@@ -77,7 +120,7 @@ vec3 sample_ggx(vec3 rand, float alpha, vec3 V, vec3 N, vec3 T, vec3 B, out floa
/** \name Uniform Hemisphere
* \{ */
-float pdf_uniform_hemisphere()
+float sample_pdf_uniform_hemisphere()
{
return 0.5 * M_1_PI;
}
@@ -94,7 +137,34 @@ vec3 sample_uniform_hemisphere(vec3 rand)
vec3 sample_uniform_hemisphere(vec3 rand, vec3 N, vec3 T, vec3 B, out float pdf)
{
vec3 Ht = sample_uniform_hemisphere(rand);
- pdf = pdf_uniform_hemisphere();
+ pdf = sample_pdf_uniform_hemisphere();
+ return tangent_to_world(Ht, N, T, B);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Cosine Hemisphere
+ * \{ */
+
+float sample_pdf_cosine_hemisphere(float cos_theta)
+{
+ return cos_theta * M_1_PI;
+}
+
+vec3 sample_cosine_hemisphere(vec3 rand)
+{
+ float z = sqrt(max(1e-16, rand.x)); /* cos theta */
+ float r = sqrt(max(0.0, 1.0 - z * z)); /* sin theta */
+ float x = r * rand.y;
+ float y = r * rand.z;
+ return vec3(x, y, z);
+}
+
+vec3 sample_cosine_hemisphere(vec3 rand, vec3 N, vec3 T, vec3 B, out float pdf)
+{
+ vec3 Ht = sample_cosine_hemisphere(rand);
+ pdf = sample_pdf_cosine_hemisphere(Ht.z);
return tangent_to_world(Ht, N, T, B);
}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_bsdf_stubs_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_bsdf_stubs_lib.glsl
new file mode 100644
index 00000000000..3d7b91bcc1e
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_bsdf_stubs_lib.glsl
@@ -0,0 +1,41 @@
+
+/* Lib file to include to evaluate a nodetree without evaluating BSDFs. */
+
+/* -------------------------------------------------------------------- */
+/** \name Utility functions to work with BSDFs
+ * \{ */
+
+vec3 diffuse_dominant_dir(vec3 bent_normal)
+{
+ return vec3(0);
+}
+vec3 specular_dominant_dir(vec3 N, vec3 V, float roughness)
+{
+ return vec3(0.0);
+}
+vec3 refraction_dominant_dir(vec3 N, vec3 V, float roughness, float ior)
+{
+ return vec3(0.0);
+}
+float F_eta(float eta, float cos_theta)
+{
+ return 0.0;
+}
+vec3 F_brdf_single_scatter(vec3 f0, vec3 f90, vec2 lut)
+{
+ return vec3(0);
+}
+vec3 F_brdf_multi_scatter(vec3 f0, vec3 f90, vec2 lut)
+{
+ return vec3(0);
+}
+vec2 brdf_lut(float cos_theta, float roughness)
+{
+ return vec2(0);
+}
+vec2 btdf_lut(float cos_theta, float roughness, float ior)
+{
+ return vec2(0);
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_camera_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_camera_lib.glsl
new file mode 100644
index 00000000000..59806477654
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_camera_lib.glsl
@@ -0,0 +1,167 @@
+
+/**
+ * Camera projection / uv functions and utils.
+ **/
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+/* -------------------------------------------------------------------- */
+/** \name Panoramic Projections
+ *
+ * Adapted from Cycles to match EEVEE's coordinate system.
+ * \{ */
+
+vec2 camera_equirectangular_from_direction(CameraData camera, vec3 dir)
+{
+ float phi = atan(-dir.z, dir.x);
+ float theta = acos(dir.y / length(dir));
+ return (vec2(phi, theta) - camera.equirect_bias) * camera.equirect_scale_inv;
+}
+
+vec3 camera_equirectangular_to_direction(CameraData camera, vec2 uv)
+{
+ uv = uv * camera.equirect_scale + camera.equirect_bias;
+ float phi = uv.x;
+ float theta = uv.y;
+ float sin_theta = sin(theta);
+ return vec3(sin_theta * cos(phi), cos(theta), -sin_theta * sin(phi));
+}
+
+vec2 camera_fisheye_from_direction(CameraData camera, vec3 dir)
+{
+ float r = atan(length(dir.xy), -dir.z) / camera.fisheye_fov;
+ float phi = atan(dir.y, dir.x);
+ vec2 uv = r * vec2(cos(phi), sin(phi)) + 0.5;
+ return (uv - camera.uv_bias) / camera.uv_scale;
+}
+
+vec3 camera_fisheye_to_direction(CameraData camera, vec2 uv)
+{
+ uv = uv * camera.uv_scale + camera.uv_bias;
+ uv = (uv - 0.5) * 2.0;
+ float r = length(uv);
+ if (r > 1.0) {
+ return vec3(0.0);
+ }
+ float phi = safe_acos(uv.x * safe_rcp(r));
+ float theta = r * camera.fisheye_fov * 0.5;
+ if (uv.y < 0.0) {
+ phi = -phi;
+ }
+ return vec3(cos(phi) * sin(theta), sin(phi) * sin(theta), -cos(theta));
+}
+
+vec2 camera_mirror_ball_from_direction(CameraData camera, vec3 dir)
+{
+ dir = normalize(dir);
+ dir.z -= 1.0;
+ dir *= safe_rcp(2.0 * safe_sqrt(-0.5 * dir.z));
+ vec2 uv = 0.5 * dir.xy + 0.5;
+ return (uv - camera.uv_bias) / camera.uv_scale;
+}
+
+vec3 camera_mirror_ball_to_direction(CameraData camera, vec2 uv)
+{
+ uv = uv * camera.uv_scale + camera.uv_bias;
+ vec3 dir;
+ dir.xy = uv * 2.0 - 1.0;
+ if (len_squared(dir.xy) > 1.0) {
+ return vec3(0.0);
+ }
+ dir.z = -safe_sqrt(1.0 - sqr(dir.x) - sqr(dir.y));
+ const vec3 I = vec3(0.0, 0.0, 1.0);
+ return reflect(I, dir);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Regular projections
+ * \{ */
+
+vec3 camera_view_from_uv(mat4 projmat, vec2 uv)
+{
+ return project_point(projmat, vec3(uv * 2.0 - 1.0, 0.0));
+}
+
+vec2 camera_uv_from_view(mat4 projmat, bool is_persp, vec3 vV)
+{
+ vec4 tmp = projmat * vec4(vV, 1.0);
+ if (is_persp && tmp.w <= 0.0) {
+ /* Return invalid coordinates for points behind the camera.
+ * This can happen with panoramic projections. */
+ return vec2(-1.0);
+ }
+ return (tmp.xy / tmp.w) * 0.5 + 0.5;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name General functions handling all projections
+ * \{ */
+
+vec3 camera_view_from_uv(CameraData camera, vec2 uv)
+{
+ vec3 vV;
+ switch (camera.type) {
+ default:
+ case CAMERA_ORTHO:
+ case CAMERA_PERSP:
+ return camera_view_from_uv(camera.wininv, uv);
+ case CAMERA_PANO_EQUIRECT:
+ vV = camera_equirectangular_to_direction(camera, uv);
+ break;
+ case CAMERA_PANO_EQUIDISTANT:
+ /* ATTR_FALLTHROUGH; */
+ case CAMERA_PANO_EQUISOLID:
+ vV = camera_fisheye_to_direction(camera, uv);
+ break;
+ case CAMERA_PANO_MIRROR:
+ vV = camera_mirror_ball_to_direction(camera, uv);
+ break;
+ }
+ return vV;
+}
+
+vec2 camera_uv_from_view(CameraData camera, vec3 vV)
+{
+ switch (camera.type) {
+ default:
+ case CAMERA_ORTHO:
+ return camera_uv_from_view(camera.winmat, false, vV);
+ case CAMERA_PERSP:
+ return camera_uv_from_view(camera.winmat, true, vV);
+ case CAMERA_PANO_EQUIRECT:
+ return camera_equirectangular_from_direction(camera, vV);
+ case CAMERA_PANO_EQUISOLID:
+ /* ATTR_FALLTHROUGH; */
+ case CAMERA_PANO_EQUIDISTANT:
+ return camera_fisheye_from_direction(camera, vV);
+ case CAMERA_PANO_MIRROR:
+ return camera_mirror_ball_from_direction(camera, vV);
+ }
+}
+
+vec2 camera_uv_from_world(CameraData camera, vec3 V)
+{
+ vec3 vV = transform_point(camera.viewmat, V);
+ switch (camera.type) {
+ default:
+ case CAMERA_ORTHO:
+ return camera_uv_from_view(camera.persmat, false, V);
+ case CAMERA_PERSP:
+ return camera_uv_from_view(camera.persmat, true, V);
+ case CAMERA_PANO_EQUIRECT:
+ return camera_equirectangular_from_direction(camera, vV);
+ case CAMERA_PANO_EQUISOLID:
+ /* ATTR_FALLTHROUGH; */
+ case CAMERA_PANO_EQUIDISTANT:
+ return camera_fisheye_from_direction(camera, vV);
+ case CAMERA_PANO_MIRROR:
+ return camera_mirror_ball_from_direction(camera, vV);
+ }
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_camera_velocity_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_camera_velocity_frag.glsl
new file mode 100644
index 00000000000..25158cf69c7
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_camera_velocity_frag.glsl
@@ -0,0 +1,59 @@
+
+/**
+ * Extract two 2D screen space velocity vector from depth buffer.
+ * Note that the offsets are in camera uv space, not view uv space.
+ * xy: Previous position > Current position
+ * zw: Current position > Next position
+ **/
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(std140) uniform camera_prev_block
+{
+ CameraData cam_prev;
+};
+
+layout(std140) uniform camera_curr_block
+{
+ CameraData cam_curr;
+};
+
+layout(std140) uniform camera_next_block
+{
+ CameraData cam_next;
+};
+
+uniform sampler2D depth_tx;
+
+in vec4 uvcoordsvar;
+
+layout(location = 0) out vec4 out_velocity_camera;
+layout(location = 1) out vec4 out_velocity_view;
+
+void main(void)
+{
+ float depth = textureLod(depth_tx, uvcoordsvar.xy, 0.0).r;
+
+ vec3 P = get_world_space_from_depth(uvcoordsvar.xy, depth);
+ vec3 P_prev = P, P_next = P;
+
+ if (depth == 1.0) {
+ /* Background case. Only compute rotation velocity. */
+ vec3 V = -cameraVec(P);
+ P_prev = cam_prev.viewinv[3].xyz + V;
+ P_next = cam_next.viewinv[3].xyz + V;
+ }
+
+ if (depth == 1.0) {
+ /* Lookdev Sphere. Ouput no velocity. */
+ out_velocity_camera = vec4(0);
+ out_velocity_view = vec4(0);
+ return;
+ }
+
+ compute_velocity(
+ P_prev, P, P_next, cam_prev, cam_curr, cam_next, out_velocity_camera, out_velocity_view);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_closure_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_closure_lib.glsl
new file mode 100644
index 00000000000..d255b9ad7a3
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_closure_lib.glsl
@@ -0,0 +1,104 @@
+
+struct ClosureDiffuse {
+ vec3 color;
+ vec3 N;
+ vec3 sss_radius;
+ uint sss_id;
+};
+
+struct ClosureReflection {
+ vec3 color;
+ vec3 N;
+ float roughness;
+};
+
+struct ClosureRefraction {
+ vec3 color;
+ vec3 N;
+ float roughness;
+ float ior;
+};
+
+struct ClosureVolume {
+ vec3 emission;
+ vec3 scattering;
+ vec3 transmittance;
+ float anisotropy;
+};
+
+struct ClosureEmission {
+ vec3 emission;
+};
+
+struct ClosureTransparency {
+ vec3 transmittance;
+ float holdout;
+};
+
+/* We use the weight tree pre-evaluation to weight the closures.
+ * There is no need for the Closure type. */
+struct Closure {
+ float dummy;
+};
+#define CLOSURE_DEFAULT Closure(0.0)
+#define closure_add(a, b) CLOSURE_DEFAULT
+#define closure_mix(a, b, c) CLOSURE_DEFAULT
+
+/* Store weight in red channel. Store negative to differentiate with evaluated closure. */
+void closure_weight_add(inout ClosureDiffuse closure, float weight)
+{
+ closure.color.r -= weight;
+}
+void closure_weight_add(inout ClosureReflection closure, float weight)
+{
+ closure.color.r -= weight;
+}
+void closure_weight_add(inout ClosureRefraction closure, float weight)
+{
+ closure.color.r -= weight;
+}
+
+/* Create a random threshold inside the weight range. */
+void closure_weight_randomize(inout ClosureDiffuse closure, float randu)
+{
+ closure.color.g = closure.color.r * randu;
+}
+void closure_weight_randomize(inout ClosureReflection closure, float randu)
+{
+ closure.color.g = closure.color.r * randu;
+}
+void closure_weight_randomize(inout ClosureRefraction closure, float randu)
+{
+ closure.color.g = closure.color.r * randu;
+}
+
+bool closure_weight_threshold(inout ClosureDiffuse closure, inout float weight)
+{
+ /* Decrement weight from random threshold. */
+ closure.color.g += weight;
+ /* Evaluate this closure if threshold reaches 0. */
+ if (closure.color.g >= 0.0) {
+ /* Returns the sum of all weights. */
+ weight = abs(closure.color.r);
+ return true;
+ }
+ return false;
+}
+bool closure_weight_threshold(inout ClosureReflection closure, inout float weight)
+{
+ closure.color.g += weight;
+ if (closure.color.g >= 0.0) {
+ weight = abs(closure.color.r);
+ return true;
+ }
+ return false;
+}
+bool closure_weight_threshold(inout ClosureRefraction closure, inout float weight)
+{
+ closure.color.g += weight;
+ if (closure.color.g >= 0.0) {
+ weight = abs(closure.color.r);
+ return true;
+ }
+ return false;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/cubemap_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_cubemap_lib.glsl
index 90272400915..3b6d6959183 100644
--- a/source/blender/draw/engines/eevee/shaders/cubemap_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/eevee_cubemap_lib.glsl
@@ -1,12 +1,8 @@
-#ifdef GPU_ARB_texture_cube_map_array
-
-# define textureLod_cubemapArray(tex, co, lod) textureLod(tex, co, lod)
-
-#else
-
-/* Fallback implementation for hardware not supporting cubemap arrays. */
-# define samplerCubeArray sampler2DArray
+/**
+ * Own implementation of cubemap sampling routines. Either used for custom cubemap
+ * storage (2D packing) or fallback for lack of samplerCubeArray support.
+ **/
float cubemap_face_index(vec3 P)
{
@@ -35,6 +31,35 @@ vec2 cubemap_face_coord(vec3 P, float face)
}
}
+vec2 cubemap_face_coord(vec3 P, float face, float scale)
+{
+ if (face < 2.0) {
+ return (P.zy / P.x) * scale * vec2(-0.5, -sign(P.x) * 0.5) + 0.5;
+ }
+ else if (face < 4.0) {
+ return (P.xz / P.y) * scale * vec2(sign(P.y) * 0.5, 0.5) + 0.5;
+ }
+ else {
+ return (P.xy / P.z) * scale * vec2(0.5, -sign(P.z) * 0.5) + 0.5;
+ }
+}
+
+vec2 cubemap_face_coord(vec3 P, float face, sampler2DArray tex)
+{
+ /* Scaling to compensate the 1px border around the face. */
+ float cube_res = float(textureSize(tex, 0).x);
+ float scale = (cube_res) / (cube_res + 1.0);
+ return cubemap_face_coord(P, face, scale);
+}
+
+vec2 cubemap_face_coord(vec3 P, float face, sampler2DArrayShadow tex)
+{
+ /* Scaling to compensate the 1px border around the face. */
+ float cube_res = float(textureSize(tex, 0).x);
+ float scale = (cube_res) / (cube_res + 1.0);
+ return cubemap_face_coord(P, face, scale);
+}
+
vec3 cubemap_adj_x(float face)
{
bool y_axis = (face == 2.0 || face == 3.0);
@@ -60,7 +85,7 @@ vec3 cubemap_adj_xy(float face)
}
}
-vec4 cubemap_seamless(sampler2DArray tex, vec4 cubevec, float lod)
+vec4 cubemap_sample_lod_seamless(sampler2DArray tex, vec4 cubevec, float lod)
{
/* Manual Cube map Layer indexing. */
float face = cubemap_face_index(cubevec.xyz);
@@ -116,15 +141,20 @@ vec4 cubemap_seamless(sampler2DArray tex, vec4 cubevec, float lod)
}
}
-vec4 textureLod_cubemapArray(sampler2DArray tex, vec4 cubevec, float lod)
+/* Fallback implementation for hardware not supporting cubemap arrays. */
+vec4 cubemap_array_sample(sampler2DArray tex, vec4 cubevec, float lod)
{
float lod1 = floor(lod);
float lod2 = ceil(lod);
- vec4 col_lod1 = cubemap_seamless(tex, cubevec, lod1);
- vec4 col_lod2 = cubemap_seamless(tex, cubevec, lod2);
+ vec4 col_lod1 = cubemap_sample_lod_seamless(tex, cubevec, lod1);
+ vec4 col_lod2 = cubemap_sample_lod_seamless(tex, cubevec, lod2);
return mix(col_lod1, col_lod2, lod - lod1);
}
+#ifdef GPU_ARB_texture_cube_map_array
+# define cubemap_array_sample textureLod
+#else
+# define samplerCubeArray sampler2DArray
#endif
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_culling_debug_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_culling_debug_frag.glsl
new file mode 100644
index 00000000000..7b988a7b941
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_culling_debug_frag.glsl
@@ -0,0 +1,72 @@
+
+/**
+ * Debug Shader outputing a gradient of orange - white - blue to mark culling hotspots.
+ * Green pixels are error pixels that are missing lights from the culling pass (i.e: when culling
+ * pass is not conservative enough).
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_culling_iter_lib.glsl)
+
+layout(std430, binding = 0) readonly restrict buffer lights_buf
+{
+ LightData lights[];
+};
+
+layout(std430, binding = 1) readonly restrict buffer lights_zbins_buf
+{
+ CullingZBin lights_zbins[];
+};
+
+layout(std430, binding = 2) readonly restrict buffer lights_culling_buf
+{
+ CullingData light_culling;
+};
+
+layout(std430, binding = 3) readonly restrict buffer lights_tile_buf
+{
+ CullingWord lights_culling_words[];
+};
+
+uniform sampler2D depth_tx;
+
+in vec4 uvcoordsvar;
+
+layout(location = 0) out vec4 out_debug_color;
+
+void main(void)
+{
+ float depth = texelFetch(depth_tx, ivec2(gl_FragCoord.xy), 0).r;
+ float vP_z = get_view_z_from_depth(depth);
+
+ vec3 P = get_world_space_from_depth(uvcoordsvar.xy, depth);
+
+ float lights_count = 0.0;
+ uint lights_cull = 0u;
+ LIGHT_FOREACH_BEGIN_LOCAL (
+ light_culling, lights_zbins, lights_culling_words, gl_FragCoord.xy, vP_z, l_idx) {
+ LightData light = lights[l_idx];
+ lights_cull |= 1u << l_idx;
+ lights_count += 1.0;
+ }
+ LIGHT_FOREACH_END
+
+ uint lights_nocull = 0u;
+ LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL (light_culling, l_idx) {
+ LightData light = lights[l_idx];
+ if (distance(light._position, P) < light.influence_radius_max) {
+ lights_nocull |= 1u << l_idx;
+ }
+ }
+ LIGHT_FOREACH_END
+
+ if ((lights_cull & lights_nocull) != lights_nocull) {
+ /* ERROR. Some lights were culled incorrectly. */
+ out_debug_color = vec4(0.0, 1.0, 0.0, 1.0);
+ }
+ else {
+ out_debug_color = vec4(heatmap_gradient(lights_count / 4.0), 1.0);
+ }
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_culling_iter_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_culling_iter_lib.glsl
new file mode 100644
index 00000000000..05290be3e96
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_culling_iter_lib.glsl
@@ -0,0 +1,73 @@
+
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+uint zbin_mask(uint word_index, uint zbin_min, uint zbin_max)
+{
+ uint word_start = word_index * 32u;
+ uint word_end = word_start + 31u;
+ uint local_min = max(zbin_min, word_start);
+ uint local_max = min(zbin_max, word_end);
+ uint mask_width = local_max - local_min + 1;
+ return bit_field_mask(mask_width, local_min);
+}
+
+/* Waiting to implement extensions support. We need:
+ * - GL_KHR_shader_subgroup_ballot
+ * - GL_KHR_shader_subgroup_arithmetic
+ * or
+ * - Vulkan 1.1
+ */
+#if 1
+# define subgroupMin(a) a
+# define subgroupMax(a) a
+# define subgroupOr(a) a
+# define subgroupBroadcastFirst(a) a
+#endif
+
+#define LIGHT_FOREACH_BEGIN_DIRECTIONAL(_culling, _item_index) \
+ { \
+ { \
+ { \
+ for (uint _item_index = 0u; _item_index < _culling.items_no_cull_count; _item_index++) {
+
+#define LIGHT_FOREACH_BEGIN_LOCAL(_culling, _zbins, _words, _pixel, _linearz, _item_index) \
+ { \
+ uint batch_count = divide_ceil_u(_culling.visible_count, CULLING_BATCH_SIZE); \
+ uvec2 tile_co = uvec2(_pixel) / _culling.tile_size; \
+ uint tile_word_offset = (tile_co.x + tile_co.y * _culling.tile_x_len) * \
+ _culling.tile_word_len; \
+ for (uint batch = 0; batch < batch_count; batch++) { \
+ int zbin_index = culling_z_to_zbin(_culling, _linearz); \
+ zbin_index = clamp(zbin_index, 0, CULLING_ZBIN_COUNT - 1); \
+ uint zbin_data = _zbins[zbin_index + batch * CULLING_ZBIN_COUNT]; \
+ uint min_index = zbin_data & 0xFFFFu; \
+ uint max_index = zbin_data >> 16u; \
+ /* Ensure all threads inside a subgroup get the same value to reduce VGPR usage. */ \
+ min_index = subgroupBroadcastFirst(subgroupMin(min_index)); \
+ max_index = subgroupBroadcastFirst(subgroupMax(max_index)); \
+ uint word_min = min_index / 32u; \
+ uint word_max = max_index / 32u; \
+ for (uint word_idx = word_min; word_idx <= word_max; word_idx++) { \
+ uint word = _words[tile_word_offset + word_idx]; \
+ word &= zbin_mask(word_idx, min_index, max_index); \
+ /* Ensure all threads inside a subgroup get the same value to reduce VGPR usage. */ \
+ word = subgroupBroadcastFirst(subgroupOr(word)); \
+ while (word != 0u) { \
+ uint bit_index = uint(findLSB(word)); \
+ word &= ~1u << bit_index; \
+ uint _item_index = word_idx * 32u + bit_index;
+
+/* No culling. Iterate over all items. */
+#define LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL(_culling, _item_index) \
+ { \
+ { \
+ { \
+ for (uint _item_index = _culling.items_no_cull_count; \
+ _item_index < _culling.visible_count; \
+ _item_index++) {
+
+#define LIGHT_FOREACH_END \
+ } \
+ } \
+ } \
+ }
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_culling_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_culling_lib.glsl
new file mode 100644
index 00000000000..27a39817140
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_culling_lib.glsl
@@ -0,0 +1,175 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+/* ---------------------------------------------------------------------- */
+/** \name Intersection Tests
+ * \{ */
+
+struct Cone {
+ vec3 direction;
+ float angle_cos;
+};
+
+struct Cylinder {
+ /* Assume Z axis as direction. */
+ vec3 center;
+ float radius;
+};
+
+struct Frustum {
+ vec4 planes[4];
+};
+
+struct CullingTile {
+ Frustum frustum;
+ Cone cone;
+};
+
+bool culling_sphere_cone_isect(Sphere sphere, Cone cone)
+{
+ /**
+ * Following "Improve Tile-based Light Culling with Spherical-sliced Cone"
+ * by Eric Zhang
+ * https://lxjk.github.io/2018/03/25/Improve-Tile-based-Light-Culling-with-Spherical-sliced-Cone.html
+ */
+ float sphere_distance = length(sphere.center);
+ float sphere_sin = saturate(sphere.radius / sphere_distance);
+ float sphere_cos = sqrt(1.0 - sphere_sin * sphere_sin);
+ float cone_aperture_sin = sqrt(1.0 - cone.angle_cos * cone.angle_cos);
+
+ float cone_sphere_center_cos = dot(sphere.center / sphere_distance, cone.direction);
+ /* cos(A+B) = cos(A) * cos(B) - sin(A) * sin(B). */
+ float cone_sphere_angle_sum_cos = (sphere.radius > sphere_distance) ?
+ -1.0 :
+ (cone.angle_cos * sphere_cos -
+ cone_aperture_sin * sphere_sin);
+
+ /* Comparing cosines instead of angles since we are interested
+ * only in the monotonic region [0 .. M_PI / 2]. This saves costly acos() calls. */
+ return (cone_sphere_center_cos >= cone_sphere_angle_sum_cos);
+}
+
+bool culling_sphere_cylinder_isect(Sphere sphere, Cylinder cylinder)
+{
+ float distance_squared = len_squared(sphere.center.xy - cylinder.center.xy);
+ return (distance_squared < sqr(cylinder.radius + sphere.radius));
+}
+
+bool culling_sphere_frustum_isect(Sphere sphere, Frustum frustum)
+{
+ if (dot(vec4(sphere.center, 1.0), frustum.planes[0]) > sphere.radius) {
+ return false;
+ }
+ if (dot(vec4(sphere.center, 1.0), frustum.planes[1]) > sphere.radius) {
+ return false;
+ }
+ if (dot(vec4(sphere.center, 1.0), frustum.planes[2]) > sphere.radius) {
+ return false;
+ }
+ if (dot(vec4(sphere.center, 1.0), frustum.planes[3]) > sphere.radius) {
+ return false;
+ }
+ return true;
+}
+
+bool culling_sphere_tile_isect(Sphere sphere, CullingTile tile)
+{
+ /* Culling in view space for precision and simplicity. */
+ sphere.center = transform_point(ViewMatrix, sphere.center);
+ bool isect;
+ /* Test tile intersection using bounding cone or bounding cylinder.
+ * This has less false positive cases when the sphere is large. */
+ if (ProjectionMatrix[3][3] == 0.0) {
+ isect = culling_sphere_cone_isect(sphere, tile.cone);
+ }
+ else {
+ Cylinder cylinder = Cylinder(tile.cone.direction, tile.cone.angle_cos);
+ isect = culling_sphere_cylinder_isect(sphere, cylinder);
+ }
+ /* Refine using frustum test. If the sphere is small it avoids intersection
+ * with a neighbor tile. */
+ if (isect) {
+ isect = culling_sphere_frustum_isect(sphere, tile.frustum);
+ }
+ return isect;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Culling shapes extraction
+ * \{ */
+
+vec4 plane_from_quad(vec3 v0, vec3 v1, vec3 v2, vec3 v3)
+{
+ vec3 nor = normalize(cross(v2 - v1, v0 - v1) + cross(v0 - v3, v2 - v3));
+ return vec4(nor, -dot(nor, v2));
+}
+
+/* Corners are expected to be in viewspace. */
+Cone cone_from_quad(vec3 corners[8])
+{
+ for (int i = 0; i < 4; i++) {
+ corners[i] = normalize(corners[i]);
+ }
+ vec3 center = normalize(corners[0] + corners[1] + corners[2] + corners[3]);
+
+ vec4 corners_cos;
+ for (int i = 0; i < 4; i++) {
+ corners_cos[i] = dot(center, corners[i]);
+ }
+ return Cone(center, max_v4(corners_cos));
+}
+
+/* Corners are expected to be in viewspace. Returns Z-aligned bounding cylinder. */
+Cone cylinder_from_quad(vec3 corners[8])
+{
+ vec3 center = (corners[0] + corners[1] + corners[2] + corners[3]) * 0.25;
+
+ vec4 corners_dist;
+ for (int i = 0; i < 4; i++) {
+ corners_dist[i] = distance_squared(center, corners[i]);
+ }
+ /* Return a cone. Later converted to cylinder. */
+ return Cone(center, sqrt(max_v4(corners_dist)));
+}
+
+vec2 tile_to_ndc(CullingData culling, vec2 tile_co, vec2 offset)
+{
+ /* Add a margin to prevent culling too much if the frustum becomes too much unstable. */
+ tile_co += /* culling.tile_margin * */ offset;
+ return tile_co * culling.tile_to_uv_fac * 2.0 - 1.0;
+}
+
+CullingTile culling_tile_get(CullingData culling, uvec2 tile_co)
+{
+ vec2 ftile = vec2(tile_co);
+ /* Culling frustum corners for this tile. */
+ vec3 corners[8];
+ corners[0].xy = corners[4].xy = tile_to_ndc(culling, ftile, vec2(1, 1));
+ corners[1].xy = corners[5].xy = tile_to_ndc(culling, ftile, vec2(1, 0));
+ corners[2].xy = corners[6].xy = tile_to_ndc(culling, ftile, vec2(0, 0));
+ corners[3].xy = corners[7].xy = tile_to_ndc(culling, ftile, vec2(0, 1));
+ /* The corners depth only matter for precision. Use a mix of not so close to clip plane to
+ * avoid small float imprecision if near clip is low. */
+ corners[0].z = corners[1].z = corners[2].z = corners[3].z = -0.5;
+ corners[4].z = corners[5].z = corners[6].z = corners[7].z = 0.1;
+
+ for (int i = 0; i < 8; i++) {
+ /* Culling in view space for precision. */
+ corners[i] = project_point(ProjectionMatrixInverse, corners[i]);
+ }
+
+ bool is_persp = ProjectionMatrix[3][3] == 0.0;
+ CullingTile tile;
+ tile.cone = (is_persp) ? cone_from_quad(corners) : cylinder_from_quad(corners);
+ tile.frustum.planes[0] = plane_from_quad(corners[0], corners[1], corners[5], corners[4]);
+ tile.frustum.planes[1] = plane_from_quad(corners[1], corners[2], corners[6], corners[5]);
+ tile.frustum.planes[2] = plane_from_quad(corners[2], corners[3], corners[7], corners[6]);
+ tile.frustum.planes[3] = plane_from_quad(corners[3], corners[0], corners[4], corners[7]);
+ return tile;
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_culling_select_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_culling_select_comp.glsl
new file mode 100644
index 00000000000..f26e6621b58
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_culling_select_comp.glsl
@@ -0,0 +1,60 @@
+
+/**
+ * Select the visible items inside the active view and put them inside the sorting buffer.
+ */
+
+#pragma BLENDER_REQUIRE(common_debug_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(common_intersection_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(local_size_x = CULLING_ITEM_BATCH) in;
+
+layout(std430, binding = 0) readonly restrict buffer lights_buf
+{
+ LightData lights[];
+};
+
+layout(std430, binding = 1) restrict buffer culling_buf
+{
+ CullingData culling;
+};
+
+layout(std430, binding = 2) restrict buffer key_buf
+{
+ uint keys[];
+};
+
+void main()
+{
+ uint l_idx = gl_GlobalInvocationID.x;
+ if (l_idx >= culling.items_count) {
+ return;
+ }
+
+ LightData light = lights[l_idx];
+
+ /* Sun lights are packed at the start of the array. */
+ if (light.type == LIGHT_SUN) {
+ keys[l_idx] = l_idx;
+ return;
+ }
+
+ Sphere sphere;
+ switch (light.type) {
+ case LIGHT_SPOT:
+ /* TODO cone culling. */
+ case LIGHT_RECT:
+ case LIGHT_ELLIPSE:
+ case LIGHT_POINT:
+ sphere = Sphere(light._position, light.influence_radius_max);
+ break;
+ }
+
+ if (intersect_view(sphere)) {
+ uint index = culling.items_no_cull_count + atomicAdd(culling.visible_count, 1u);
+ keys[index] = l_idx;
+ }
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_culling_sort_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_culling_sort_comp.glsl
new file mode 100644
index 00000000000..71307525b6d
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_culling_sort_comp.glsl
@@ -0,0 +1,148 @@
+
+/**
+ * Sort the lights by their Z distance to the camera.
+ * Outputs ordered light buffer and associated zbins.
+ * We split the work in CULLING_BATCH_SIZE and iterate to cover all zbins.
+ * One thread process one Light entity.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(local_size_x = CULLING_BATCH_SIZE) in;
+
+layout(std430, binding = 0) readonly restrict buffer lights_buf
+{
+ LightData lights[];
+};
+
+layout(std430, binding = 1) restrict buffer culling_buf
+{
+ CullingData culling;
+};
+
+layout(std430, binding = 2) readonly restrict buffer key_buf
+{
+ uint keys[];
+};
+
+layout(std430, binding = 3) writeonly restrict buffer out_zbins_buf
+{
+ CullingZBin out_zbins[];
+};
+
+layout(std430, binding = 4) writeonly restrict buffer out_items_buf
+{
+ LightData out_lights[];
+};
+
+void main()
+{
+ uint src_index = gl_GlobalInvocationID.x;
+ bool valid_thread = true;
+
+ uint items_count_total = culling.items_no_cull_count + culling.visible_count;
+
+ if (src_index >= items_count_total) {
+ /* Do not return because we use barriers later on (which need uniform control flow).
+ * Just process the same last item but avoid insertion. */
+ src_index = items_count_total - 1;
+ valid_thread = false;
+ }
+
+ uint key = keys[src_index];
+ LightData light = lights[key];
+
+ if (light.type == LIGHT_SUN) {
+ valid_thread = false;
+ }
+
+ if (!culling.enable_specular) {
+ light.specular_power = 0.0;
+ }
+
+ int index = 0;
+ int contenders = 0;
+
+ vec3 lP = light._position;
+ float radius = light.influence_radius_max;
+ float z_dist = dot(cameraForward, lP) - dot(cameraForward, cameraPos);
+
+ int z_min = clamp(culling_z_to_zbin(culling, z_dist + radius), 0, CULLING_ZBIN_COUNT - 1);
+ int z_max = clamp(culling_z_to_zbin(culling, z_dist - radius), 0, CULLING_ZBIN_COUNT - 1);
+
+ /* Fits the limit of 32KB. */
+ shared int zbin_max[CULLING_ZBIN_COUNT];
+ shared int zbin_min[CULLING_ZBIN_COUNT];
+ /* Compilers do not release shared memory from early declaration.
+ * So we are forced to reuse the same variables in another form. */
+#define z_dists zbin_max
+#define contender_table zbin_min
+
+ /**
+ * Find how many values are before the local value.
+ * This finds the first possible destination index.
+ */
+ z_dists[gl_LocalInvocationID.x] = floatBitsToInt(z_dist);
+ barrier();
+
+ int batch_start = int(gl_WorkGroupID.x) * CULLING_BATCH_SIZE;
+ int i_start = max(batch_start, int(culling.items_no_cull_count));
+ int i_max = min(CULLING_BATCH_SIZE, int(items_count_total) - batch_start);
+ for (int i = i_start; i < i_max; i++) {
+ float ref = intBitsToFloat(z_dists[i]);
+ if (ref > z_dist) {
+ index++;
+ }
+ else if (ref == z_dist) {
+ contenders++;
+ }
+ }
+
+ if (valid_thread) {
+ atomicExchange(contender_table[index], contenders);
+ }
+ barrier();
+
+ if (valid_thread) {
+ /**
+ * For each clashing index (where two lights have exactly the same z distances)
+ * we use an atomic counter to know how much to offset from the disputed index.
+ */
+ index += atomicAdd(contender_table[index], -1) - 1;
+ index += i_start;
+ out_lights[index] = light;
+ }
+ else if (light.type == LIGHT_SUN) {
+ /* Directional lights are just copied to the same index. */
+ out_lights[key] = light;
+ }
+ barrier();
+
+ const uint iter = uint(CULLING_ZBIN_COUNT / CULLING_BATCH_SIZE);
+ const uint zbin_local = gl_LocalInvocationID.x * iter;
+ const uint zbin_global = gl_WorkGroupID.x * CULLING_ZBIN_COUNT + zbin_local;
+
+ for (uint i = 0u, l = zbin_local; i < iter; i++, l++) {
+ zbin_max[l] = 0x0000;
+ zbin_min[l] = 0xFFFF;
+ }
+ barrier();
+
+ /* Register to Z bins. */
+ if (valid_thread) {
+ for (int z = z_min; z <= z_max; z++) {
+ atomicMin(zbin_min[z], index);
+ atomicMax(zbin_max[z], index);
+ }
+ }
+ barrier();
+
+ /* Write result to zbins buffer. */
+ for (uint i = 0u, g = zbin_global, l = zbin_local; i < iter; i++, g++, l++) {
+ /* Pack min & max into 1 uint. */
+ out_zbins[g] = (uint(zbin_max[l]) << 16u) | uint(zbin_min[l]);
+ }
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_culling_tile_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_culling_tile_comp.glsl
new file mode 100644
index 00000000000..7c14b3c42ee
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_culling_tile_comp.glsl
@@ -0,0 +1,70 @@
+
+/**
+ * 2D Culling pass for lights.
+ * We iterate over all items and check if they intersect with the tile frustum.
+ * Dispatch one thread per word.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+#pragma BLENDER_REQUIRE(eevee_culling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_culling_iter_lib.glsl)
+
+layout(local_size_x = 1024) in;
+
+layout(std430, binding = 0) readonly restrict buffer lights_buf
+{
+ LightData lights[];
+};
+
+layout(std430, binding = 1) readonly restrict buffer culling_buf
+{
+ CullingData culling;
+};
+
+layout(std430, binding = 2) writeonly restrict buffer culling_tile_buf
+{
+ CullingWord culling_words[];
+};
+
+void main(void)
+{
+ uint word_idx = gl_GlobalInvocationID.x % culling.tile_word_len;
+ uint tile_idx = gl_GlobalInvocationID.x / culling.tile_word_len;
+ uvec2 tile_co = uvec2(tile_idx % culling.tile_x_len, tile_idx / culling.tile_x_len);
+
+ if (tile_co.y >= culling.tile_y_len) {
+ return;
+ }
+
+ /* TODO(fclem): We could stop the tile at the HiZ depth. */
+ CullingTile tile = culling_tile_get(culling, tile_co);
+
+ uint l_idx = max(word_idx * 32u, culling.items_no_cull_count);
+ uint l_end = min(l_idx + 32u, culling.visible_count + culling.items_no_cull_count);
+ uint word = 0u;
+
+ for (; l_idx < l_end; l_idx++) {
+ LightData light = lights[l_idx];
+
+ bool intersect_tile;
+ switch (light.type) {
+ case LIGHT_SPOT:
+ /* TODO cone culling. */
+ case LIGHT_RECT:
+ case LIGHT_ELLIPSE:
+ case LIGHT_POINT:
+ Sphere sphere = Sphere(light._position, light.influence_radius_max);
+ intersect_tile = culling_sphere_tile_isect(sphere, tile);
+ break;
+ }
+
+ if (intersect_tile) {
+ word |= 1u << (l_idx & 0x1Fu);
+ }
+ }
+
+ culling_words[gl_GlobalInvocationID.x] = word;
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_deferred_direct_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_deferred_direct_frag.glsl
new file mode 100644
index 00000000000..41c38efc4fd
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_deferred_direct_frag.glsl
@@ -0,0 +1,136 @@
+
+/**
+ * Direct lighting evaluation: Evaluate lights and light-probes contributions for all bsdfs.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_debug_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_culling_iter_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
+
+layout(std140) uniform sampling_block
+{
+ SamplingData sampling;
+};
+
+layout(std430, binding = 0) readonly restrict buffer lights_buf
+{
+ LightData lights[];
+};
+
+layout(std430, binding = 1) readonly restrict buffer lights_zbins_buf
+{
+ CullingZBin lights_zbins[];
+};
+
+layout(std430, binding = 2) readonly restrict buffer lights_culling_buf
+{
+ CullingData light_culling;
+};
+
+layout(std430, binding = 3) readonly restrict buffer lights_tile_buf
+{
+ CullingWord lights_culling_words[];
+};
+
+layout(std140) uniform grids_block
+{
+ GridData grids[GRID_MAX];
+};
+
+layout(std140) uniform cubes_block
+{
+ CubemapData cubes[CULLING_ITEM_BATCH];
+};
+
+layout(std140) uniform lightprobes_info_block
+{
+ LightProbeInfoData probes_info;
+};
+
+uniform sampler2D hiz_tx;
+uniform sampler2D emission_data_tx;
+uniform sampler2D transmit_color_tx;
+uniform sampler2D transmit_normal_tx;
+uniform sampler2D transmit_data_tx;
+uniform sampler2D reflect_color_tx;
+uniform sampler2D reflect_normal_tx;
+uniform sampler1D sss_transmittance_tx;
+uniform sampler2DArray utility_tx;
+uniform sampler2D shadow_atlas_tx;
+uniform usampler2D shadow_tilemaps_tx;
+uniform sampler2DArray lightprobe_grid_tx;
+uniform samplerCubeArray lightprobe_cube_tx;
+
+utility_tx_fetch_define(utility_tx);
+utility_tx_sample_define(utility_tx);
+
+in vec4 uvcoordsvar;
+
+layout(location = 0) out vec4 out_combined;
+layout(location = 1) out vec4 out_diffuse;
+layout(location = 2) out vec3 out_specular;
+
+/* Prototypes. */
+void light_eval(ClosureDiffuse diffuse,
+ ClosureReflection reflection,
+ vec3 P,
+ vec3 V,
+ float vP_z,
+ float thickness,
+ inout vec3 out_diffuse,
+ inout vec3 out_specular);
+vec3 lightprobe_grid_eval(vec3 P, vec3 N, float random_threshold);
+vec3 lightprobe_cubemap_eval(vec3 P, vec3 R, float roughness, float random_threshold);
+
+void main(void)
+{
+ vec2 uv = uvcoordsvar.xy;
+ float gbuffer_depth = texelFetch(hiz_tx, ivec2(gl_FragCoord.xy), 0).r;
+ vec3 vP = get_view_space_from_depth(uv, gbuffer_depth);
+ vec3 P = point_view_to_world(vP);
+ vec3 V = cameraVec(P);
+
+ vec4 tra_col_in = texture(transmit_color_tx, uv);
+ vec4 tra_nor_in = texture(transmit_normal_tx, uv);
+ vec4 tra_dat_in = texture(transmit_data_tx, uv);
+ vec4 ref_col_in = texture(reflect_color_tx, uv);
+ vec4 ref_nor_in = texture(reflect_normal_tx, uv);
+
+ ClosureEmission emission = gbuffer_load_emission_data(emission_data_tx, uv);
+ ClosureDiffuse diffuse = gbuffer_load_diffuse_data(tra_col_in, tra_nor_in, tra_dat_in);
+ ClosureReflection reflection = gbuffer_load_reflection_data(ref_col_in, ref_nor_in);
+
+ float thickness;
+ gbuffer_load_global_data(tra_nor_in, thickness);
+
+ float noise_offset = sampling_rng_1D_get(sampling, SAMPLING_LIGHTPROBE);
+ float noise = utility_tx_fetch(gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r;
+ float random_probe = fract(noise + noise_offset);
+
+ vec3 radiance_diffuse = vec3(0);
+ vec3 radiance_reflection = vec3(0);
+ vec3 R = -reflect(V, reflection.N);
+
+ light_eval(diffuse, reflection, P, V, vP.z, thickness, radiance_diffuse, radiance_reflection);
+
+ out_combined = vec4(emission.emission, 0.0);
+ out_diffuse.rgb = radiance_diffuse;
+ /* FIXME(fclem): This won't work after the first light batch since we use additive blending. */
+ out_diffuse.a = fract(float(diffuse.sss_id) / 1024.0) * 1024.0;
+ /* Do not apply color to diffuse term for SSS material. */
+ if (diffuse.sss_id == 0u) {
+ out_diffuse.rgb *= diffuse.color;
+ out_combined.rgb += out_diffuse.rgb;
+ }
+ out_specular = radiance_reflection * reflection.color;
+ out_combined.rgb += out_specular;
+}
+
+#pragma BLENDER_REQUIRE_POST(eevee_light_eval_lib.glsl)
+#pragma BLENDER_REQUIRE_POST(eevee_lightprobe_eval_cubemap_lib.glsl)
+#pragma BLENDER_REQUIRE_POST(eevee_lightprobe_eval_grid_lib.glsl)
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_deferred_holdout_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_deferred_holdout_frag.glsl
new file mode 100644
index 00000000000..5078b97951f
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_deferred_holdout_frag.glsl
@@ -0,0 +1,25 @@
+
+/**
+ * Save radiance from main pass to subtract to final render.
+ *
+ * This way all screen space effects (SSS, SSR) are not altered by the presence of the holdout.
+ **/
+
+#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
+
+uniform sampler2D combined_tx;
+uniform sampler2D transparency_data_tx;
+
+in vec4 uvcoordsvar;
+
+layout(location = 5) out vec3 out_holdout;
+
+void main(void)
+{
+ vec3 combined_radiance = texture(combined_tx, uvcoordsvar.xy).rgb;
+
+ ClosureTransparency transparency_data = gbuffer_load_transparency_data(transparency_data_tx,
+ uvcoordsvar.xy);
+
+ out_holdout = combined_radiance * transparency_data.holdout;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_deferred_transparent_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_deferred_transparent_frag.glsl
new file mode 100644
index 00000000000..92bfa845e87
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_deferred_transparent_frag.glsl
@@ -0,0 +1,59 @@
+
+/**
+ * Apply transmittance to all radiance passes.
+ *
+ * We needs to evaluate the transmittance of homogeneous volumes if any is present.
+ * Hopefully, this has O(1) complexity as we do not need to raymarch the volume.
+ *
+ * Using blend mode multiply.
+ **/
+
+#pragma BLENDER_REQUIRE(eevee_volume_eval_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
+
+uniform usampler2D volume_data_tx;
+uniform sampler2D transparency_data_tx;
+
+in vec4 uvcoordsvar;
+
+layout(location = 0) out vec4 out_combined;
+layout(location = 1) out vec3 out_diffuse;
+layout(location = 2) out vec3 out_specular;
+layout(location = 3) out vec3 out_volume;
+layout(location = 4) out vec3 out_background;
+layout(location = 5) out vec3 out_holdout;
+
+void main(void)
+{
+ ClosureVolume volume_data = gbuffer_load_volume_data(volume_data_tx, uvcoordsvar.xy);
+
+ /* For volumes from solid objects. */
+ // float depth_max = linear_z(texture(depth_max_tx, uv).r);
+ // float depth_min = linear_z(texture(depth_min_tx, uv).r);
+
+ /* Refine bounds to skip empty areas. */
+ // float dist_from_bbox = intersect_bbox_ray(P, V, bbox);
+ // depth_min = max(dist_from_bbox, depth_min);
+
+ vec3 volume_transmittance;
+ if (volume_data.anisotropy == VOLUME_HETEROGENEOUS) {
+ volume_transmittance = volume_data.transmittance;
+ }
+ else {
+ // volume_eval_homogeneous(P, depth_min, depth_max, volume_transmittance);
+ volume_transmittance = vec3(0.0);
+ }
+
+ vec3 surface_transmittance =
+ gbuffer_load_transparency_data(transparency_data_tx, uvcoordsvar.xy).transmittance;
+
+ vec3 final_transmittance = volume_transmittance * surface_transmittance;
+
+ /* Multiply transmittance all radiance buffers. Remember that blend mode is multiply. */
+ out_combined = vec4(final_transmittance, avg(final_transmittance));
+ out_diffuse = final_transmittance;
+ out_specular = final_transmittance;
+ out_volume = final_transmittance;
+ out_background = final_transmittance;
+ out_holdout = final_transmittance;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_deferred_volume_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_deferred_volume_frag.glsl
new file mode 100644
index 00000000000..7e83ea356ca
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_deferred_volume_frag.glsl
@@ -0,0 +1,80 @@
+
+/**
+ * Apply heterogeneous volume lighting and evaluates homogeneous volumetrics if needed.
+ *
+ * We read volume parameters from the gbuffer and consider them constant for the whole volume.
+ * This only applies to solid objects not volumes.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_bsdf_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_culling_iter_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_volume_eval_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(std430, binding = 0) readonly restrict buffer lights_buf
+{
+ LightData lights[];
+};
+
+layout(std430, binding = 1) readonly restrict buffer lights_zbins_buf
+{
+ CullingZBin lights_zbins[];
+};
+
+layout(std430, binding = 2) readonly restrict buffer lights_culling_buf
+{
+ CullingData light_culling;
+};
+
+layout(std430, binding = 3) readonly restrict buffer lights_tile_buf
+{
+ CullingWord lights_culling_words[];
+};
+
+uniform sampler2D transparency_data_tx;
+uniform usampler2D volume_data_tx;
+uniform sampler2DArray utility_tx;
+uniform sampler2DShadow shadow_atlas_tx;
+uniform usampler2D shadow_tilemaps_tx;
+
+utility_tx_fetch_define(utility_tx) utility_tx_sample_define(utility_tx)
+
+ in vec4 uvcoordsvar;
+
+layout(location = 0) out vec4 out_combined;
+layout(location = 1) out vec3 out_volume;
+
+void main(void)
+{
+ ClosureVolume volume_data = gbuffer_load_volume_data(volume_data_tx, uvcoordsvar.xy);
+
+ /* For volumes from solid objects. */
+ // float depth_max = linear_z(texture(depth_max_tx, uv).r);
+ // float depth_min = linear_z(texture(depth_min_tx, uv).r);
+
+ /* Refine bounds to skip empty areas. */
+ // float dist_from_bbox = intersect_bbox_ray(P, V, bbox);
+ // depth_min = max(dist_from_bbox, depth_min);
+
+ vec3 volume_radiance;
+ if (volume_data.anisotropy == VOLUME_HETEROGENEOUS) {
+ volume_radiance = volume_data.scattering;
+ }
+ else {
+ // volume_eval_homogeneous(P, depth_min, depth_max, volume_radiance);
+ volume_radiance = vec3(0.0);
+ }
+
+ /* Apply transmittance of surface on volumetric radiance because
+ * the volume is behind the surface. */
+ ClosureTransparency transparency_data = gbuffer_load_transparency_data(transparency_data_tx,
+ uvcoordsvar.xy);
+ volume_radiance *= transparency_data.transmittance;
+
+ out_combined = vec4(volume_radiance, 0.0);
+ out_volume = volume_radiance;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_clear_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_clear_frag.glsl
new file mode 100644
index 00000000000..8adad15baeb
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_clear_frag.glsl
@@ -0,0 +1,6 @@
+
+void main(void)
+{
+ /* No color output, only depth (line below is implicit). */
+ /* gl_FragDepth = gl_FragCoord.z; */
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_accumulator_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_accumulator_lib.glsl
new file mode 100644
index 00000000000..f51807e71c6
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_accumulator_lib.glsl
@@ -0,0 +1,685 @@
+
+/**
+ * Depth of Field Gather accumulator.
+ * We currently have only 2 which are very similar.
+ * One is for the halfres gather passes and the other one for slight in focus regions.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
+
+/* -------------------------------------------------------------------- */
+/** \name Options.
+ * \{ */
+
+/* Quality options */
+#ifdef DOF_HOLEFILL_PASS
+/* No need for very high density for holefill. */
+const int gather_ring_count = 3;
+const int gather_ring_density = 3;
+const int gather_max_density_change = 0;
+const int gather_density_change_ring = 1;
+#else
+const int gather_ring_count = DOF_GATHER_RING_COUNT;
+const int gather_ring_density = 3;
+const int gather_max_density_change = 50; /* Dictates the maximum good quality blur. */
+const int gather_density_change_ring = 1;
+#endif
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Constants.
+ * \{ */
+
+const float unit_ring_radius = 1.0 / float(gather_ring_count);
+const float unit_sample_radius = 1.0 / float(gather_ring_count + 0.5);
+const float large_kernel_radius = 0.5 + float(gather_ring_count);
+const float smaller_kernel_radius = 0.5 + float(gather_ring_count - gather_density_change_ring);
+/* NOTE(fclem) the bias is reducing issues with density change visible transition. */
+const float radius_downscale_factor = smaller_kernel_radius / large_kernel_radius;
+const int change_density_at_ring = (gather_ring_count - gather_density_change_ring + 1);
+const float coc_radius_error = 2.0;
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Gather common.
+ * \{ */
+
+struct DofGatherData {
+ vec4 color;
+ float weight;
+ float dist; /* TODO remove */
+ /* For scatter occlusion. */
+ float coc;
+ float coc_sqr;
+ /* For ring bucket merging. */
+ float transparency;
+
+ float layer_opacity;
+};
+
+#define GATHER_DATA_INIT DofGatherData(vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
+
+/* Intersection with the center of the kernel. */
+float dof_intersection_weight(float coc, float distance_from_center, float intersection_multiplier)
+{
+ if (no_smooth_intersection) {
+ return step(0.0, (abs(coc) - distance_from_center));
+ }
+ else {
+ /* (Slide 64). */
+ return saturate((abs(coc) - distance_from_center) * intersection_multiplier + 0.5);
+ }
+}
+
+/* Returns weight of the sample for the outer bucket (containing previous
+ * rings). */
+float dof_gather_accum_weight(float coc, float bordering_radius, bool first_ring)
+{
+ /* First ring has nothing to be mixed against. */
+ if (first_ring) {
+ return 0.0;
+ }
+ return saturate(coc - bordering_radius);
+}
+
+void dof_gather_ammend_weight(inout DofGatherData sample_data, float weight)
+{
+ sample_data.color *= weight;
+ sample_data.coc *= weight;
+ sample_data.coc_sqr *= weight;
+ sample_data.weight *= weight;
+}
+
+void dof_gather_accumulate_sample(DofGatherData sample_data,
+ float weight,
+ inout DofGatherData accum_data)
+{
+ accum_data.color += sample_data.color * weight;
+ accum_data.coc += sample_data.coc * weight;
+ accum_data.coc_sqr += sample_data.coc * (sample_data.coc * weight);
+ accum_data.weight += weight;
+}
+
+void dof_gather_accumulate_sample_pair(DofGatherData pair_data[2],
+ float bordering_radius,
+ float intersection_multiplier,
+ bool first_ring,
+ const bool do_fast_gather,
+ const bool is_foreground,
+ inout DofGatherData ring_data,
+ inout DofGatherData accum_data)
+{
+ if (do_fast_gather) {
+ for (int i = 0; i < 2; i++) {
+ dof_gather_accumulate_sample(pair_data[i], 1.0, accum_data);
+ accum_data.layer_opacity += 1.0;
+ }
+ return;
+ }
+
+#if 0
+ const float mirroring_threshold = -dof_layer_threshold - dof_layer_offset;
+ /* TODO(fclem) Promote to parameter? dither with Noise? */
+ const float mirroring_min_distance = 15.0;
+ if (pair_data[0].coc < mirroring_threshold &&
+ (pair_data[1].coc - mirroring_min_distance) > pair_data[0].coc) {
+ pair_data[1].coc = pair_data[0].coc;
+ }
+ else if (pair_data[1].coc < mirroring_threshold &&
+ (pair_data[0].coc - mirroring_min_distance) > pair_data[1].coc) {
+ pair_data[0].coc = pair_data[1].coc;
+ }
+#endif
+
+ for (int i = 0; i < 2; i++) {
+ float sample_weight = dof_sample_weight(pair_data[i].coc);
+ float layer_weight = dof_layer_weight(pair_data[i].coc, is_foreground);
+ float inter_weight = dof_intersection_weight(
+ pair_data[i].coc, pair_data[i].dist, intersection_multiplier);
+ float weight = inter_weight * layer_weight * sample_weight;
+
+ /**
+ * If a CoC is larger than bordering radius we accumulate it to the general accumulator.
+ * If not, we accumulate to the ring bucket. This is to have more consistent sample occlusion.
+ **/
+ float accum_weight = dof_gather_accum_weight(pair_data[i].coc, bordering_radius, first_ring);
+ dof_gather_accumulate_sample(pair_data[i], weight * accum_weight, accum_data);
+ dof_gather_accumulate_sample(pair_data[i], weight * (1.0 - accum_weight), ring_data);
+
+ accum_data.layer_opacity += layer_weight;
+
+ if (is_foreground) {
+ ring_data.transparency += 1.0 - inter_weight * layer_weight;
+ }
+ else {
+ float coc = is_foreground ? -pair_data[i].coc : pair_data[i].coc;
+ ring_data.transparency += saturate(coc - bordering_radius);
+ }
+ }
+}
+
+void dof_gather_accumulate_sample_ring(DofGatherData ring_data,
+ int sample_count,
+ bool first_ring,
+ const bool do_fast_gather,
+ /* accum_data occludes the ring_data if true. */
+ const bool reversed_occlusion,
+ inout DofGatherData accum_data)
+{
+ if (do_fast_gather) {
+ /* Do nothing as ring_data contains nothing. All samples are already in
+ * accum_data. */
+ return;
+ }
+
+ if (first_ring) {
+ /* Layer opacity is directly accumulated into accum_data data. */
+ accum_data.color = ring_data.color;
+ accum_data.coc = ring_data.coc;
+ accum_data.coc_sqr = ring_data.coc_sqr;
+ accum_data.weight = ring_data.weight;
+
+ accum_data.transparency = ring_data.transparency / float(sample_count);
+ return;
+ }
+
+ if (ring_data.weight == 0.0) {
+ return;
+ }
+
+ float ring_avg_coc = ring_data.coc / ring_data.weight;
+ float accum_avg_coc = accum_data.coc / accum_data.weight;
+
+ /* Smooth test to set opacity to see if the ring average coc occludes the
+ * accumulation. Test is reversed to be multiplied against opacity. */
+ float ring_occlu = saturate(accum_avg_coc - ring_avg_coc);
+ /* The bias here is arbitrary. Seems to avoid weird looking foreground in most
+ * cases. We might need to make it a parameter or find a relative bias. */
+ float accum_occlu = saturate((ring_avg_coc - accum_avg_coc) * 0.1 - 1.0);
+
+ if (is_resolve) {
+ ring_occlu = accum_occlu = 0.0;
+ }
+
+ if (no_gather_occlusion) {
+ ring_occlu = 0.0;
+ accum_occlu = 0.0;
+ }
+
+ /* (Slide 40) */
+ float ring_opacity = saturate(1.0 - ring_data.transparency / float(sample_count));
+ float accum_opacity = 1.0 - accum_data.transparency;
+
+ if (reversed_occlusion) {
+ /* Accum_data occludes the ring. */
+ float alpha = (accum_data.weight == 0.0) ? 0.0 : accum_opacity * accum_occlu;
+ float one_minus_alpha = 1.0 - alpha;
+
+ accum_data.color += ring_data.color * one_minus_alpha;
+ accum_data.coc += ring_data.coc * one_minus_alpha;
+ accum_data.coc_sqr += ring_data.coc_sqr * one_minus_alpha;
+ accum_data.weight += ring_data.weight * one_minus_alpha;
+
+ accum_data.transparency *= 1.0 - ring_opacity;
+ }
+ else {
+ /* Ring occludes the accum_data (Same as reference). */
+ float alpha = (accum_data.weight == 0.0) ? 1.0 : (ring_opacity * ring_occlu);
+ float one_minus_alpha = 1.0 - alpha;
+
+ accum_data.color = accum_data.color * one_minus_alpha + ring_data.color;
+ accum_data.coc = accum_data.coc * one_minus_alpha + ring_data.coc;
+ accum_data.coc_sqr = accum_data.coc_sqr * one_minus_alpha + ring_data.coc_sqr;
+ accum_data.weight = accum_data.weight * one_minus_alpha + ring_data.weight;
+ }
+}
+
+/* FIXME(fclem) Seems to be wrong since it needs ringcount+1 as input for
+ * slightfocus gather. */
+/* This should be replaced by web_sample_count_get() but doing so is breaking other things. */
+int dof_gather_total_sample_count(const int ring_count, const int ring_density)
+{
+ return (ring_count * ring_count - ring_count) * ring_density + 1;
+}
+
+void dof_gather_accumulate_center_sample(DofGatherData center_data,
+ float bordering_radius,
+ int i_radius,
+ const bool do_fast_gather,
+ const bool is_foreground,
+ const bool is_resolve,
+ inout DofGatherData accum_data)
+{
+ float layer_weight = dof_layer_weight(center_data.coc, is_foreground);
+ float sample_weight = dof_sample_weight(center_data.coc);
+ float weight = layer_weight * sample_weight;
+ float accum_weight = dof_gather_accum_weight(center_data.coc, bordering_radius, false);
+
+ if (do_fast_gather) {
+ /* Hope for the compiler to optimize the above. */
+ layer_weight = 1.0;
+ sample_weight = 1.0;
+ accum_weight = 1.0;
+ weight = 1.0;
+ }
+
+ center_data.transparency = 1.0 - weight;
+
+ dof_gather_accumulate_sample(center_data, weight * accum_weight, accum_data);
+
+ if (!do_fast_gather) {
+ if (is_resolve) {
+ /* NOTE(fclem): Hack to smooth transition to full in-focus opacity. */
+ int total_sample_count = dof_gather_total_sample_count(i_radius + 1,
+ DOF_SLIGHT_FOCUS_DENSITY);
+ float fac = saturate(1.0 - abs(center_data.coc) / float(dof_layer_threshold));
+ accum_data.layer_opacity += float(total_sample_count) * fac * fac;
+ }
+ accum_data.layer_opacity += layer_weight;
+
+ /* Logic of dof_gather_accumulate_sample(). */
+ weight *= (1.0 - accum_weight);
+ center_data.coc_sqr = center_data.coc * (center_data.coc * weight);
+ center_data.color *= weight;
+ center_data.coc *= weight;
+ center_data.weight = weight;
+
+ if (is_foreground && !is_resolve) {
+ /* Reduce issue with closer foreground over distant foreground. */
+ float ring_area = sqr(bordering_radius);
+ dof_gather_ammend_weight(center_data, ring_area);
+ }
+
+ /* Accumulate center as its own ring. */
+ dof_gather_accumulate_sample_ring(
+ center_data, 1, false, do_fast_gather, is_foreground, accum_data);
+ }
+}
+
+int dof_gather_total_sample_count_with_density_change(const int ring_count,
+ const int ring_density,
+ int density_change)
+{
+ int sample_count_per_density_change = dof_gather_total_sample_count(ring_count, ring_density) -
+ dof_gather_total_sample_count(
+ ring_count - gather_density_change_ring, ring_density);
+
+ return dof_gather_total_sample_count(ring_count, ring_density) +
+ sample_count_per_density_change * density_change;
+}
+
+void dof_gather_accumulate_resolve(int total_sample_count,
+ DofGatherData accum_data,
+ out vec4 out_col,
+ out float out_weight,
+ out vec2 out_occlusion)
+{
+ float weight_inv = safe_rcp(accum_data.weight);
+ out_col = accum_data.color * weight_inv;
+ out_occlusion = vec2(abs(accum_data.coc), accum_data.coc_sqr) * weight_inv;
+
+ if (is_foreground) {
+ out_weight = 1.0 - accum_data.transparency;
+ }
+ else if (accum_data.weight > 0.0) {
+ out_weight = accum_data.layer_opacity / float(total_sample_count);
+ }
+ else {
+ out_weight = 0.0;
+ }
+ /* Gathering may not accumulate to 1.0 alpha because of float precision. */
+ if (out_weight > 0.99) {
+ out_weight = 1.0;
+ }
+ else if (out_weight < 0.01) {
+ out_weight = 0.0;
+ }
+ /* Same thing for alpha channel. */
+ if (out_col.a > 0.99) {
+ out_col.a = 1.0;
+ }
+ else if (out_col.a < 0.01) {
+ out_col.a = 0.0;
+ }
+}
+
+float dof_load_gather_coc(sampler2D gather_input_coc_tx, vec2 uv, float lod)
+{
+ float coc = textureLod(gather_input_coc_tx, uv, lod).r;
+ /* We gather at halfres. CoC must be divided by 2 to be compared against radii. */
+ return coc * 0.5;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Common Gather accumulator.
+ * \{ */
+
+/* Radii needs to be halfres CoC sizes. */
+bool dof_do_density_change(float base_radius, float min_intersectable_radius)
+{
+ /* Reduce artifact for very large blur. */
+ min_intersectable_radius *= 0.1;
+
+ bool need_new_density = (base_radius * unit_ring_radius > min_intersectable_radius);
+ bool larger_than_min_density = (base_radius * radius_downscale_factor >
+ float(gather_ring_count));
+
+ return need_new_density && larger_than_min_density;
+}
+
+void dof_gather_init(DepthOfFieldData dof,
+ float base_radius,
+ vec2 noise,
+ out vec2 center_co,
+ out float lod,
+ out float intersection_multiplier)
+{
+ /* Jitter center half a ring to reduce undersampling. */
+ vec2 jitter_ofs = 0.499 * sample_disk(noise);
+ if (DOF_BOKEH_TEXTURE) {
+ jitter_ofs *= dof.bokeh_anisotropic_scale;
+ }
+ center_co = gl_FragCoord.xy + jitter_ofs * base_radius * unit_sample_radius;
+
+ /* TODO(fclem) Seems like the default lod selection is too big. Bias to avoid blocky moving out
+ * of focus shapes. */
+ const float lod_bias = -2.0;
+ lod = max(floor(log2(base_radius * unit_sample_radius) + 0.5) + lod_bias, 0.0);
+
+ if (no_gather_mipmaps) {
+ lod = 0.0;
+ }
+ /* (Slide 64). */
+ intersection_multiplier = pow(0.5, lod);
+}
+
+void dof_gather_accumulator(SamplingData sampling,
+ DepthOfFieldData dof,
+ sampler2D color_tx,
+ sampler2D color_bilinear_tx,
+ sampler2D coc_tx,
+ sampler2D bokeh_lut_tx,
+ float base_radius,
+ float min_intersectable_radius,
+ const bool do_fast_gather,
+ const bool do_density_change,
+ out vec4 out_color,
+ out float out_weight,
+ out vec2 out_occlusion)
+{
+ vec2 noise_offset = sampling_rng_2D_get(sampling, SAMPLING_LENS_U);
+ vec2 noise = no_gather_random ?
+ vec2(0.0, 0.0) :
+ vec2(interlieved_gradient_noise(gl_FragCoord.xy, 0, noise_offset.x),
+ interlieved_gradient_noise(gl_FragCoord.xy, 1, noise_offset.y));
+
+ if (!do_fast_gather) {
+ /* Jitter the radius to reduce noticeable density changes. */
+ base_radius += noise.x * unit_ring_radius * base_radius;
+ }
+ else {
+ /* Jittering the radius more than we need means we are going to feather the bokeh shape half a
+ * ring. So we need to compensate for fast gather that does not check CoC intersection. */
+ base_radius += (0.5 - noise.x) * 1.5 * unit_ring_radius * base_radius;
+ }
+ /* TODO(fclem) another seed? For now Cranly-Partterson rotation with golden ratio. */
+ noise.x = fract(noise.x * 6.1803398875);
+
+ float lod, isect_mul;
+ vec2 center_co;
+ dof_gather_init(dof, base_radius, noise, center_co, lod, isect_mul);
+
+ bool first_ring = true;
+
+ DofGatherData accum_data = GATHER_DATA_INIT;
+
+ int density_change = 0;
+ for (int ring = gather_ring_count; ring > 0; ring--) {
+ int sample_pair_count = gather_ring_density * ring;
+
+ float step_rot = M_PI / float(sample_pair_count);
+ mat2 step_rot_mat = rot2_from_angle(step_rot);
+
+ float angle_offset = noise.y * step_rot;
+ vec2 offset = vec2(cos(angle_offset), sin(angle_offset));
+
+ float ring_radius = float(ring) * unit_sample_radius * base_radius;
+
+ /* Slide 38. */
+ float bordering_radius = ring_radius +
+ (0.5 + coc_radius_error) * base_radius * unit_sample_radius;
+ DofGatherData ring_data = GATHER_DATA_INIT;
+ for (int sample_pair = 0; sample_pair < sample_pair_count; sample_pair++) {
+ offset = step_rot_mat * offset;
+
+ DofGatherData pair_data[2];
+ for (int i = 0; i < 2; i++) {
+ vec2 offset_co = ((i == 0) ? offset : -offset);
+ if (DOF_BOKEH_TEXTURE) {
+ /* Scaling to 0.25 for speed. Improves texture cache hit. */
+ offset_co = texture(bokeh_lut_tx, offset_co * 0.25 + 0.5).rg;
+ offset_co *= (is_foreground) ? -dof.bokeh_anisotropic_scale :
+ dof.bokeh_anisotropic_scale;
+ }
+ vec2 sample_co = center_co + offset_co * ring_radius;
+ vec2 sample_uv = sample_co * dof.gather_uv_fac;
+ if (do_fast_gather) {
+ pair_data[i].color = textureLod(color_bilinear_tx, sample_uv, lod);
+ }
+ else {
+ pair_data[i].color = textureLod(color_tx, sample_uv, lod);
+ }
+ pair_data[i].coc = dof_load_gather_coc(coc_tx, sample_uv, lod);
+ pair_data[i].dist = ring_radius;
+ }
+
+ dof_gather_accumulate_sample_pair(pair_data,
+ bordering_radius,
+ isect_mul,
+ first_ring,
+ do_fast_gather,
+ is_foreground,
+ ring_data,
+ accum_data);
+ }
+
+ if (is_foreground) {
+ /* Reduce issue with closer foreground over distant foreground. */
+ /* TODO(fclem) this seems to not be completely correct as the issue remains. */
+ float ring_area = (sqr(float(ring) + 0.5 + coc_radius_error) -
+ sqr(float(ring) - 0.5 + coc_radius_error)) *
+ sqr(base_radius * unit_sample_radius);
+ dof_gather_ammend_weight(ring_data, ring_area);
+ }
+
+ dof_gather_accumulate_sample_ring(
+ ring_data, sample_pair_count * 2, first_ring, do_fast_gather, is_foreground, accum_data);
+
+ first_ring = false;
+
+ if (do_density_change && (ring == change_density_at_ring) &&
+ (density_change < gather_max_density_change)) {
+ if (dof_do_density_change(base_radius, min_intersectable_radius)) {
+ base_radius *= radius_downscale_factor;
+ ring += gather_density_change_ring;
+ /* We need to account for the density change in the weights (slide 62).
+ * For that multiply old kernel data by its area divided by the new kernel area. */
+ const float outer_rings_weight = 1.0 / (radius_downscale_factor * radius_downscale_factor);
+ /* Samples are already weighted per ring in foreground pass. */
+ if (!is_foreground) {
+ dof_gather_ammend_weight(accum_data, outer_rings_weight);
+ }
+ /* Re-init kernel position & sampling parameters. */
+ dof_gather_init(dof, base_radius, noise, center_co, lod, isect_mul);
+ density_change++;
+ }
+ }
+ }
+
+ {
+ /* Center sample. */
+ vec2 sample_uv = center_co * dof.gather_uv_fac;
+ DofGatherData center_data;
+ if (do_fast_gather) {
+ center_data.color = textureLod(color_bilinear_tx, sample_uv, lod);
+ }
+ else {
+ center_data.color = textureLod(color_tx, sample_uv, lod);
+ }
+ center_data.coc = dof_load_gather_coc(coc_tx, sample_uv, lod);
+ center_data.dist = 0.0;
+
+ /* Slide 38. */
+ float bordering_radius = (0.5 + coc_radius_error) * base_radius * unit_sample_radius;
+
+ dof_gather_accumulate_center_sample(
+ center_data, bordering_radius, 0, do_fast_gather, is_foreground, false, accum_data);
+ }
+
+ int total_sample_count = dof_gather_total_sample_count_with_density_change(
+ gather_ring_count, gather_ring_density, density_change);
+ dof_gather_accumulate_resolve(
+ total_sample_count, accum_data, out_color, out_weight, out_occlusion);
+
+ if (debug_gather_perf && density_change > 0) {
+ float fac = saturate(float(density_change) / float(10.0));
+ out_color.rgb = avg(out_color.rgb) * neon_gradient(fac);
+ }
+ if (debug_gather_perf && do_fast_gather) {
+ out_color.rgb = avg(out_color.rgb) * vec3(0.0, 1.0, 0.0);
+ }
+ if (debug_scatter_perf) {
+ out_color.rgb = avg(out_color.rgb) * vec3(0.0, 1.0, 0.0);
+ }
+
+ /* Output premultiplied color so we can use bilinear sampler in resolve pass. */
+ out_color *= out_weight;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Slight focus accumulator.
+ *
+ * The full pixel neighborhood is gathered.
+ * \{ */
+
+void dof_slight_focus_gather(SamplingData sampling,
+ DepthOfFieldData dof,
+ sampler2D depth_tx,
+ sampler2D color_tx,
+ sampler2D bokeh_lut_tx,
+ float radius,
+ out vec4 out_color,
+ out float out_weight)
+{
+ float noise_offset = sampling_rng_1D_get(sampling, SAMPLING_LENS_U);
+ float noise = no_gather_random ? 0.0 :
+ interlieved_gradient_noise(gl_FragCoord.xy, 3, noise_offset);
+
+ DofGatherData fg_accum = GATHER_DATA_INIT;
+ DofGatherData bg_accum = GATHER_DATA_INIT;
+
+ int i_radius = clamp(int(radius), 0, int(dof_layer_threshold));
+ const int resolve_ring_density = DOF_SLIGHT_FOCUS_DENSITY;
+ ivec2 texel = ivec2(gl_FragCoord.xy);
+
+ bool first_ring = true;
+
+ for (int ring = i_radius; ring > 0; ring--) {
+ DofGatherData fg_ring = GATHER_DATA_INIT;
+ DofGatherData bg_ring = GATHER_DATA_INIT;
+
+ int ring_distance = ring;
+ int ring_sample_count = resolve_ring_density * ring_distance;
+ for (int sample_id = 0; sample_id < ring_sample_count; sample_id++) {
+ int s = sample_id * (4 / resolve_ring_density) +
+ int(noise * float((4 - resolve_ring_density) * ring_distance));
+
+ ivec2 offset = dof_square_ring_sample_offset(ring_distance, s);
+ float ring_dist = length(vec2(offset));
+
+ DofGatherData pair_data[2];
+ for (int i = 0; i < 2; i++) {
+ ivec2 sample_offset = ((i == 0) ? offset : -offset);
+ ivec2 sample_texel = texel + sample_offset;
+ /* OPTI: could precompute the factor. */
+ vec2 sample_uv = (vec2(sample_texel) + 0.5) / vec2(textureSize(depth_tx, 0));
+ float depth = textureLod(depth_tx, sample_uv, 0.0).r;
+ pair_data[i].coc = dof_coc_from_depth(dof, sample_uv, depth);
+ pair_data[i].color = safe_color(textureLod(color_tx, sample_uv, 0.0));
+ pair_data[i].dist = ring_dist;
+ if (DOF_BOKEH_TEXTURE) {
+ /* Contains subpixel distance to bokeh shape. */
+ sample_offset += dof_max_slight_focus_radius;
+ pair_data[i].dist = texelFetch(bokeh_lut_tx, sample_offset, 0).r;
+ }
+ pair_data[i].coc = clamp(pair_data[i].coc, -dof.coc_abs_max, dof.coc_abs_max);
+ }
+
+ float bordering_radius = ring_dist + 0.5;
+ const float isect_mul = 1.0;
+ dof_gather_accumulate_sample_pair(
+ pair_data, bordering_radius, isect_mul, first_ring, false, false, bg_ring, bg_accum);
+
+ if (DOF_BOKEH_TEXTURE) {
+ /* Swap distances in order to flip bokeh shape for foreground. */
+ float tmp = pair_data[0].dist;
+ pair_data[0].dist = pair_data[1].dist;
+ pair_data[1].dist = tmp;
+ }
+ dof_gather_accumulate_sample_pair(
+ pair_data, bordering_radius, isect_mul, first_ring, false, true, fg_ring, fg_accum);
+ }
+
+ dof_gather_accumulate_sample_ring(
+ bg_ring, ring_sample_count * 2, first_ring, false, false, bg_accum);
+ dof_gather_accumulate_sample_ring(
+ fg_ring, ring_sample_count * 2, first_ring, false, true, fg_accum);
+
+ first_ring = false;
+ }
+
+ /* Center sample. */
+ vec2 sample_uv = gl_FragCoord.xy / vec2(textureSize(depth_tx, 0));
+ DofGatherData center_data;
+ center_data.color = safe_color(textureLod(color_tx, sample_uv, 0.0));
+ center_data.coc = dof_coc_from_depth(dof, sample_uv, textureLod(depth_tx, sample_uv, 0.0).r);
+ center_data.coc = clamp(center_data.coc, -dof.coc_abs_max, dof.coc_abs_max);
+ center_data.dist = 0.0;
+
+ /* Slide 38. */
+ float bordering_radius = 0.5;
+
+ dof_gather_accumulate_center_sample(
+ center_data, bordering_radius, i_radius, false, true, true, fg_accum);
+ dof_gather_accumulate_center_sample(
+ center_data, bordering_radius, i_radius, false, false, true, bg_accum);
+
+ vec4 bg_col, fg_col;
+ float bg_weight, fg_weight;
+ vec2 unused_occlusion;
+
+ int total_sample_count = dof_gather_total_sample_count(i_radius + 1, resolve_ring_density);
+ dof_gather_accumulate_resolve(total_sample_count, bg_accum, bg_col, bg_weight, unused_occlusion);
+ dof_gather_accumulate_resolve(total_sample_count, fg_accum, fg_col, fg_weight, unused_occlusion);
+
+ /* Fix weighting issues on perfectly focus > slight focus transitionning areas. */
+ if (abs(center_data.coc) < 0.5) {
+ bg_col = center_data.color;
+ bg_weight = 1.0;
+ }
+
+ /* Alpha Over */
+ float alpha = 1.0 - fg_weight;
+ out_weight = bg_weight * alpha + fg_weight;
+ out_color = bg_col * bg_weight * alpha + fg_col * fg_weight;
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_bokeh_lut_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_bokeh_lut_frag.glsl
new file mode 100644
index 00000000000..9ab450064db
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_bokeh_lut_frag.glsl
@@ -0,0 +1,63 @@
+
+/**
+ * Bokeh Look Up Table: This outputs a radius multiplier to shape the sampling in gather pass or
+ * the scatter sprite appearance. This is only used if bokeh shape is either anamorphic or is not
+ * a perfect circle.
+ * We correct samples spacing for polygonal bokeh shapes. However, we do not for anamorphic bokeh
+ * as it is way more complex and expensive to do.
+ **/
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
+
+layout(std140) uniform dof_block
+{
+ DepthOfFieldData dof;
+};
+
+in vec4 uvcoordsvar;
+
+layout(location = 0) out vec2 out_gather_lut;
+layout(location = 1) out float out_scatter_Lut;
+layout(location = 2) out float outResolveLut;
+
+void main()
+{
+ /* Center uv in range [-1..1]. */
+ vec2 uv = uvcoordsvar.xy * 2.0 - 1.0;
+
+ float radius = length(uv);
+
+ vec2 texel = floor(gl_FragCoord.xy) - float(dof_max_slight_focus_radius);
+
+ if (dof.bokeh_blades > 0.0) {
+ /* NOTE: atan(y,x) has output range [-M_PI..M_PI], so add 2pi to avoid negative angles. */
+ float theta = atan(uv.y, uv.x) + M_2PI;
+ float r = length(uv);
+
+ radius /= circle_to_polygon_radius(dof.bokeh_blades, theta - dof.bokeh_rotation);
+
+ float theta_new = circle_to_polygon_angle(dof.bokeh_blades, theta);
+ float r_new = circle_to_polygon_radius(dof.bokeh_blades, theta_new);
+
+ theta_new -= dof.bokeh_rotation;
+
+ uv = r_new * vec2(-cos(theta_new), sin(theta_new));
+
+ {
+ /* Slight focus distance */
+ texel *= dof.bokeh_anisotropic_scale_inv;
+ float theta = atan(texel.y, -texel.x) + M_2PI;
+ texel /= circle_to_polygon_radius(dof.bokeh_blades, theta + dof.bokeh_rotation);
+ }
+ }
+ else {
+ uv *= safe_rcp(length(uv));
+ }
+
+ /* For gather store the normalized UV. */
+ out_gather_lut = uv;
+ /* For scatter store distance. */
+ out_scatter_Lut = radius;
+ /* For slight focus gather store pixel perfect distance. */
+ outResolveLut = length(texel);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_filter_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_filter_frag.glsl
index aa72cda4fb7..70f3a4b9aa7 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_filter_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_filter_frag.glsl
@@ -6,15 +6,15 @@
* cheaper.
*/
-#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
-uniform sampler2D colorBuffer;
-uniform sampler2D weightBuffer;
+uniform sampler2D color_tx;
+uniform sampler2D weight_tx;
in vec4 uvcoordsvar;
-layout(location = 0) out vec4 outColor;
-layout(location = 1) out float outWeight;
+layout(location = 0) out vec4 out_color;
+layout(location = 1) out float out_weight;
/* From:
* Implementing Median Filters in XC4000E FPGAs
@@ -88,6 +88,6 @@ void main()
{
/* OPTI(fclem) Could early return on some tiles. */
- outColor = median_filter(colorBuffer, uvcoordsvar.xy);
- outWeight = median_filter(weightBuffer, uvcoordsvar.xy).r;
+ out_color = median_filter(color_tx, uvcoordsvar.xy);
+ out_weight = median_filter(weight_tx, uvcoordsvar.xy).r;
}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_gather_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_gather_frag.glsl
new file mode 100644
index 00000000000..03768a62ab9
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_gather_frag.glsl
@@ -0,0 +1,118 @@
+
+/**
+ * Gather pass: Convolve foreground and background parts in separate passes.
+ *
+ * Using the min&max CoC tile buffer, we select the best apropriate method to blur the scene color.
+ * A fast gather path is taken if there is not many CoC variation inside the tile.
+ *
+ * We sample using an octaweb sampling pattern. We randomize the kernel center and each ring
+ * rotation to ensure maximum coverage.
+ *
+ * Outputs:
+ * - Color * Weight, Weight, Occlusion 'CoC' Depth (mean and variance)
+ **/
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_accumulator_lib.glsl)
+
+layout(std140) uniform sampling_block
+{
+ SamplingData sampling;
+};
+
+layout(std140) uniform dof_block
+{
+ DepthOfFieldData dof;
+};
+
+uniform sampler2D color_tx;
+uniform sampler2D color_bilinear_tx;
+uniform sampler2D coc_tx;
+uniform sampler2D tiles_fg_tx;
+uniform sampler2D tiles_bg_tx;
+uniform sampler2D bokeh_lut_tx;
+
+layout(location = 0) out vec4 out_color;
+layout(location = 1) out float out_weight;
+layout(location = 2) out vec2 out_occlusion;
+
+void main()
+{
+ ivec2 tile_co = ivec2(gl_FragCoord.xy / float(DOF_TILE_DIVISOR / 2));
+ CocTile coc_tile = dof_coc_tile_load(tiles_fg_tx, tiles_bg_tx, tile_co);
+ CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile);
+
+ float base_radius, min_radius, min_intersectable_radius;
+ bool can_early_out;
+ if (is_foreground) {
+ base_radius = -coc_tile.fg_min_coc;
+ min_radius = -coc_tile.fg_max_coc;
+ min_intersectable_radius = -coc_tile.fg_max_intersectable_coc;
+ can_early_out = !prediction.do_foreground;
+ }
+ else {
+ base_radius = coc_tile.bg_max_coc;
+ min_radius = coc_tile.bg_min_coc;
+ min_intersectable_radius = coc_tile.bg_min_intersectable_coc;
+ can_early_out = !prediction.do_background;
+ }
+
+ bool do_fast_gather = dof_do_fast_gather(base_radius, min_radius, is_foreground);
+
+ /* Gather at half resolution. Divide CoC by 2. */
+ base_radius *= 0.5;
+ min_intersectable_radius *= 0.5;
+
+ bool do_density_change = dof_do_density_change(base_radius, min_intersectable_radius);
+
+ if (can_early_out) {
+ /* Early out. */
+ out_color = vec4(0.0);
+ out_weight = 0.0;
+ out_occlusion = vec2(0.0, 0.0);
+ }
+ else if (do_fast_gather) {
+ dof_gather_accumulator(sampling,
+ dof,
+ color_tx,
+ color_bilinear_tx,
+ coc_tx,
+ bokeh_lut_tx,
+ base_radius,
+ min_intersectable_radius,
+ true,
+ false,
+ out_color,
+ out_weight,
+ out_occlusion);
+ }
+ else if (do_density_change) {
+ dof_gather_accumulator(sampling,
+ dof,
+ color_tx,
+ color_bilinear_tx,
+ coc_tx,
+ bokeh_lut_tx,
+ base_radius,
+ min_intersectable_radius,
+ false,
+ true,
+ out_color,
+ out_weight,
+ out_occlusion);
+ }
+ else {
+ dof_gather_accumulator(sampling,
+ dof,
+ color_tx,
+ color_bilinear_tx,
+ coc_tx,
+ bokeh_lut_tx,
+ base_radius,
+ min_intersectable_radius,
+ false,
+ false,
+ out_color,
+ out_weight,
+ out_occlusion);
+ }
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_gather_holefill_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_gather_holefill_frag.glsl
new file mode 100644
index 00000000000..df6ff9f6792
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_gather_holefill_frag.glsl
@@ -0,0 +1,89 @@
+
+/**
+ * Holefill pass: Gather background parts where foreground is present.
+ *
+ * Using the min&max CoC tile buffer, we select the best apropriate method to blur the scene color.
+ * A fast gather path is taken if there is not many CoC variation inside the tile.
+ *
+ * We sample using an octaweb sampling pattern. We randomize the kernel center and each ring
+ * rotation to ensure maximum coverage.
+ **/
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_accumulator_lib.glsl)
+
+layout(std140) uniform sampling_block
+{
+ SamplingData sampling;
+};
+
+layout(std140) uniform dof_block
+{
+ DepthOfFieldData dof;
+};
+
+uniform sampler2D color_tx;
+uniform sampler2D color_bilinear_tx;
+uniform sampler2D coc_tx;
+uniform sampler2D tiles_fg_tx;
+uniform sampler2D tiles_bg_tx;
+
+layout(location = 0) out vec4 out_color;
+layout(location = 1) out float out_weight;
+
+void main()
+{
+ ivec2 tile_co = ivec2(gl_FragCoord.xy / float(DOF_TILE_DIVISOR / 2));
+ CocTile coc_tile = dof_coc_tile_load(tiles_fg_tx, tiles_bg_tx, tile_co);
+ CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile);
+
+ float base_radius = -coc_tile.fg_min_coc;
+ float min_radius = -coc_tile.fg_max_coc;
+ float min_intersectable_radius = dof_tile_large_coc;
+ bool can_early_out = !prediction.do_holefill;
+
+ bool do_fast_gather = dof_do_fast_gather(base_radius, min_radius, is_foreground);
+
+ /* Gather at half resolution. Divide CoC by 2. */
+ base_radius *= 0.5;
+ min_intersectable_radius *= 0.5;
+
+ bool do_density_change = dof_do_density_change(base_radius, min_intersectable_radius);
+
+ if (can_early_out) {
+ /* Early out. */
+ out_color = vec4(0.0);
+ out_weight = 0.0;
+ }
+ else if (do_fast_gather) {
+ vec2 unused_occlusion;
+ dof_gather_accumulator(sampling,
+ dof,
+ color_tx,
+ color_bilinear_tx,
+ coc_tx,
+ coc_tx,
+ base_radius,
+ min_intersectable_radius,
+ true,
+ false,
+ out_color,
+ out_weight,
+ unused_occlusion);
+ }
+ else {
+ vec2 unused_occlusion;
+ dof_gather_accumulator(sampling,
+ dof,
+ color_tx,
+ color_bilinear_tx,
+ coc_tx,
+ coc_tx,
+ base_radius,
+ min_intersectable_radius,
+ false,
+ false,
+ out_color,
+ out_weight,
+ unused_occlusion);
+ }
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_lib.glsl
new file mode 100644
index 00000000000..0cc36dd0d03
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_lib.glsl
@@ -0,0 +1,328 @@
+/**
+ * Depth of Field utils.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+/* -------------------------------------------------------------------- */
+/** \name Constants.
+ * \{ */
+
+#ifndef DOF_SLIGHT_FOCUS_DENSITY
+# define DOF_SLIGHT_FOCUS_DENSITY 2
+#endif
+
+#ifdef DOF_RESOLVE_PASS
+const bool is_resolve = true;
+#else
+const bool is_resolve = false;
+#endif
+#ifdef DOF_FOREGROUND_PASS
+const bool is_foreground = DOF_FOREGROUND_PASS;
+#else
+const bool is_foreground = false;
+#endif
+/* Debug options */
+const bool debug_gather_perf = false;
+const bool debug_scatter_perf = false;
+const bool debug_resolve_perf = false;
+
+const bool no_smooth_intersection = false;
+const bool no_gather_occlusion = false;
+const bool no_gather_mipmaps = false;
+const bool no_gather_random = false;
+const bool no_gather_filtering = false;
+const bool no_scatter_occlusion = false;
+const bool no_scatter_pass = false;
+const bool no_foreground_pass = false;
+const bool no_background_pass = false;
+const bool no_slight_focus_pass = false;
+const bool no_focus_pass = false;
+const bool no_holefill_pass = false;
+
+/* Distribute weights between near/slightfocus/far fields (slide 117). */
+const float dof_layer_threshold = 4.0;
+/* Make sure it overlaps. */
+const float dof_layer_offset_fg = 0.5 + 1.0;
+/* Extra offset for convolution layers to avoid light leaking from background. */
+const float dof_layer_offset = 0.5 + 0.5;
+
+const int dof_max_slight_focus_radius = 5;
+
+const vec2 quad_offsets[4] = vec2[4](
+ vec2(-0.5, 0.5), vec2(0.5, 0.5), vec2(0.5, -0.5), vec2(-0.5, -0.5));
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Weighting and downsampling utils.
+ * \{ */
+
+float dof_hdr_color_weight(vec4 color)
+{
+ /* Very fast "luma" weighting. */
+ float luma = (color.g * 2.0) + (color.r + color.b);
+ /* TODO(fclem) Pass correct exposure. */
+ const float exposure = 1.0;
+ return 1.0 / (luma * exposure + 4.0);
+}
+
+float dof_coc_select(vec4 cocs)
+{
+ /* Select biggest coc. */
+ float selected_coc = cocs.x;
+ if (abs(cocs.y) > abs(selected_coc)) {
+ selected_coc = cocs.y;
+ }
+ if (abs(cocs.z) > abs(selected_coc)) {
+ selected_coc = cocs.z;
+ }
+ if (abs(cocs.w) > abs(selected_coc)) {
+ selected_coc = cocs.w;
+ }
+ return selected_coc;
+}
+
+/* NOTE: Do not forget to normalize weights afterwards. */
+vec4 dof_bilateral_coc_weights(vec4 cocs)
+{
+ float chosen_coc = dof_coc_select(cocs);
+
+ const float scale = 4.0; /* TODO(fclem) revisit. */
+ /* NOTE: The difference between the cocs should be inside a abs() function,
+ * but we follow UE4 implementation to improve how dithered transparency looks (see slide 19). */
+ return saturate(1.0 - (chosen_coc - cocs) * scale);
+}
+
+/* NOTE: Do not forget to normalize weights afterwards. */
+vec4 dof_bilateral_color_weights(vec4 colors[4])
+{
+ vec4 weights;
+ for (int i = 0; i < 4; i++) {
+ weights[i] = dof_hdr_color_weight(colors[i]);
+ }
+ return weights;
+}
+
+/* Returns signed Circle of confusion radius (in pixel) based on depth buffer value [0..1]. */
+float dof_coc_from_depth(DepthOfFieldData dof, vec2 uv, float depth)
+{
+ if (is_panoramic(dof.camera_type)) {
+ /* Use radial depth. */
+ depth = -length(get_view_space_from_depth(uv, depth));
+ }
+ else {
+ depth = get_view_z_from_depth(depth);
+ }
+ return coc_radius_from_camera_depth(dof, depth);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Gather & Scatter Weighting
+ * \{ */
+
+float dof_layer_weight(float coc, const bool is_foreground)
+{
+ /* NOTE: These are fullres pixel CoC value. */
+ if (is_resolve) {
+ return saturate(-abs(coc) + dof_layer_threshold + dof_layer_offset) *
+ float(is_foreground ? (coc <= 0.5) : (coc > -0.5));
+ }
+ else {
+ coc *= 2.0; /* Account for half pixel gather. */
+ float threshold = dof_layer_threshold -
+ ((is_foreground) ? dof_layer_offset_fg : dof_layer_offset);
+ return saturate(((is_foreground) ? -coc : coc) - threshold);
+ }
+}
+vec4 dof_layer_weight(vec4 coc)
+{
+ /* NOTE: Used for scatter pass which already flipped the sign correctly. */
+ coc *= 2.0; /* Account for half pixel gather. */
+ return saturate(coc - dof_layer_threshold + dof_layer_offset);
+}
+
+/* NOTE: This is halfres CoC radius. */
+float dof_sample_weight(float coc)
+{
+ /* Full intensity if CoC radius is below the pixel footprint. */
+ const float min_coc = 1.0;
+ coc = max(min_coc, abs(coc));
+ return (M_PI * min_coc * min_coc) / (M_PI * coc * coc);
+}
+vec4 dof_sample_weight(vec4 coc)
+{
+ /* Full intensity if CoC radius is below the pixel footprint. */
+ const float min_coc = 1.0;
+ coc = max(vec4(min_coc), abs(coc));
+ return (M_PI * min_coc * min_coc) / (M_PI * coc * coc);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Circle of Confusion tiles
+ * \{ */
+
+struct CocTile {
+ float fg_min_coc;
+ float fg_max_coc;
+ float fg_max_intersectable_coc;
+ float fg_slight_focus_max_coc;
+ float bg_min_coc;
+ float bg_max_coc;
+ float bg_min_intersectable_coc;
+};
+
+struct CocTilePrediction {
+ bool do_foreground;
+ bool do_slight_focus;
+ bool do_focus;
+ bool do_background;
+ bool do_holefill;
+};
+
+/* WATCH: Might have to change depending on the texture format. */
+const float dof_tile_defocus = 0.25;
+const float dof_tile_focus = 0.0;
+const float dof_tile_mixed = 0.75;
+const float dof_tile_large_coc = 1024.0;
+
+/* Init a CoC tile for reduction algorithms. */
+CocTile dof_coc_tile_init(void)
+{
+ CocTile tile;
+ tile.fg_min_coc = 0.0;
+ tile.fg_max_coc = -dof_tile_large_coc;
+ tile.fg_max_intersectable_coc = dof_tile_large_coc;
+ tile.fg_slight_focus_max_coc = -1.0;
+ tile.bg_min_coc = dof_tile_large_coc;
+ tile.bg_max_coc = 0.0;
+ tile.bg_min_intersectable_coc = dof_tile_large_coc;
+ return tile;
+}
+
+CocTile dof_coc_tile_load(sampler2D fg_buffer, sampler2D bg_buffer, ivec2 tile_co)
+{
+ ivec2 tex_size = textureSize(fg_buffer, 0).xy;
+ tile_co = clamp(tile_co, ivec2(0), tex_size - 1);
+
+ vec4 fg = texelFetch(fg_buffer, tile_co, 0);
+ vec3 bg = texelFetch(bg_buffer, tile_co, 0).xyz;
+
+ CocTile tile;
+ tile.fg_min_coc = -fg.x;
+ tile.fg_max_coc = -fg.y;
+ tile.fg_max_intersectable_coc = -fg.z;
+ tile.fg_slight_focus_max_coc = fg.w;
+ tile.bg_min_coc = bg.x;
+ tile.bg_max_coc = bg.y;
+ tile.bg_min_intersectable_coc = bg.z;
+ return tile;
+}
+
+void dof_coc_tile_store(CocTile tile, out vec4 out_fg, out vec3 out_bg)
+{
+ out_fg.x = -tile.fg_min_coc;
+ out_fg.y = -tile.fg_max_coc;
+ out_fg.z = -tile.fg_max_intersectable_coc;
+ out_fg.w = tile.fg_slight_focus_max_coc;
+ out_bg.x = tile.bg_min_coc;
+ out_bg.y = tile.bg_max_coc;
+ out_bg.z = tile.bg_min_intersectable_coc;
+}
+
+bool dof_do_fast_gather(float max_absolute_coc, float min_absolute_coc, const bool is_foreground)
+{
+ float min_weight = dof_layer_weight((is_foreground) ? -min_absolute_coc : min_absolute_coc,
+ is_foreground);
+ if (min_weight < 1.0) {
+ return false;
+ }
+ /* FIXME(fclem): This is a workaround to fast gather triggering too early. Since we use custom
+ * opacity mask, the opacity is not given to be 100% even for after normal threshold. */
+ if (is_foreground && min_absolute_coc < dof_layer_threshold) {
+ return false;
+ }
+ return (max_absolute_coc - min_absolute_coc) < (DOF_FAST_GATHER_COC_ERROR * max_absolute_coc);
+}
+
+CocTilePrediction dof_coc_tile_prediction_get(CocTile tile)
+{
+ /* Based on tile value, predict what pass we need to load. */
+ CocTilePrediction predict;
+
+ predict.do_foreground = (-tile.fg_min_coc > dof_layer_threshold - dof_layer_offset_fg);
+ bool fg_fully_opaque = predict.do_foreground &&
+ dof_do_fast_gather(-tile.fg_min_coc, -tile.fg_max_coc, true);
+
+ predict.do_slight_focus = !fg_fully_opaque && (tile.fg_slight_focus_max_coc >= 0.5);
+ predict.do_focus = !fg_fully_opaque && (tile.fg_slight_focus_max_coc == dof_tile_focus);
+
+ predict.do_background = !predict.do_focus && !fg_fully_opaque &&
+ (tile.bg_max_coc > dof_layer_threshold - dof_layer_offset);
+ bool bg_fully_opaque = predict.do_background &&
+ dof_do_fast_gather(-tile.bg_max_coc, tile.bg_min_coc, false);
+ predict.do_holefill = !predict.do_focus && !fg_fully_opaque && -tile.fg_max_coc > 0.0;
+
+#if 0 /* Debug */
+ predict.do_foreground = predict.do_background = predict.do_holefill = true;
+#endif
+ return predict;
+}
+
+/* Special function to return the correct max value of 2 slight focus coc. */
+float dof_coc_max_slight_focus(float coc1, float coc2)
+{
+ /* Do not consider values below 0.5 for expansion as they are "encoded".
+ * See setup pass shader for more infos. */
+ if ((coc1 == dof_tile_defocus && coc2 == dof_tile_focus) ||
+ (coc1 == dof_tile_focus && coc2 == dof_tile_defocus)) {
+ /* Tile where completely out of focus and in focus are both present.
+ * Consider as very slightly out of focus. */
+ return dof_tile_mixed;
+ }
+ return max(coc1, coc2);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Gathering
+ * \{ */
+
+/**
+ * Generate samples in a square pattern with the ring radius. X is the center tile.
+ *
+ * Dist1 Dist2
+ * 6 5 4 3 2
+ * 3 2 1 7 1
+ * . X 0 . X 0
+ * . . . . .
+ * . . . . .
+ *
+ * Samples are expected to be mirrored to complete the pattern.
+ **/
+ivec2 dof_square_ring_sample_offset(int ring_distance, int sample_id)
+{
+ ivec2 offset;
+ if (sample_id < ring_distance) {
+ offset.x = ring_distance;
+ offset.y = sample_id;
+ }
+ else if (sample_id < ring_distance * 3) {
+ offset.x = ring_distance - sample_id + ring_distance;
+ offset.y = ring_distance;
+ }
+ else {
+ offset.x = -ring_distance;
+ offset.y = ring_distance - sample_id + 3 * ring_distance;
+ }
+ return offset;
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_copy_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_copy_frag.glsl
new file mode 100644
index 00000000000..420476f6f37
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_copy_frag.glsl
@@ -0,0 +1,144 @@
+
+/**
+ * Reduce copy pass: filter fireflies and split color between scatter and gather input.
+ *
+ * NOTE: The texture can end up being too big because of the mipmap padding. We correct for
+ * that during the convolution phase.
+ *
+ * Inputs:
+ * - Output of setup pass (halfres) and reduce downsample pass (quarter res).
+ * Outputs:
+ * - Halfres padded to avoid mipmap mis-alignment (so possibly not matching input size).
+ * - Gather input color (mip 0), Scatter input color, Signed CoC.
+ **/
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
+
+layout(std140) uniform dof_block
+{
+ DepthOfFieldData dof;
+};
+
+uniform sampler2D color_tx;
+uniform sampler2D coc_tx;
+uniform sampler2D downsampled_tx;
+
+layout(location = 0) out vec4 out_color_gather;
+layout(location = 1) out float out_coc;
+layout(location = 2) out vec3 out_color_scatter;
+
+/* NOTE: Do not compare alpha as it is not scattered by the scatter pass. */
+float dof_scatter_neighborhood_rejection(vec3 color)
+{
+ color = min(vec3(dof.scatter_neighbor_max_color), color);
+
+ float validity = 0.0;
+
+ /* Centered in the middle of 4 quarter res texel. */
+ vec2 texel_size = 1.0 / vec2(textureSize(downsampled_tx, 0).xy);
+ vec2 uv = (gl_FragCoord.xy * 0.5) * texel_size;
+
+ vec3 max_diff = vec3(0.0);
+ for (int i = 0; i < 4; i++) {
+ vec2 sample_uv = uv + quad_offsets[i] * texel_size;
+ vec3 ref = textureLod(downsampled_tx, sample_uv, 0.0).rgb;
+
+ ref = min(vec3(dof.scatter_neighbor_max_color), ref);
+ float diff = max_v3(max(vec3(0.0), abs(ref - color)));
+
+ const float rejection_threshold = 0.7;
+ diff = saturate(diff / rejection_threshold - 1.0);
+ validity = max(validity, diff);
+ }
+
+ return validity;
+}
+
+/* This avoids sprite popping in and out at the screen border and
+ * drawing sprites larger than the screen. */
+float dof_scatter_screen_border_rejection(float coc, vec2 uv, vec2 screen_size)
+{
+ vec2 screen_pos = uv * screen_size;
+ float min_screen_border_distance = min_v2(min(screen_pos, screen_size - screen_pos));
+ /* Fullres to halfres CoC. */
+ coc *= 0.5;
+ /* Allow 10px transition. */
+ const float rejection_hardeness = 1.0 / 10.0;
+ return saturate((min_screen_border_distance - abs(coc)) * rejection_hardeness + 1.0);
+}
+
+float dof_scatter_luminosity_rejection(vec3 color)
+{
+ const float rejection_hardness = 1.0;
+ return saturate(max_v3(color - dof.scatter_color_threshold) * rejection_hardness);
+}
+
+float dof_scatter_coc_radius_rejection(float coc)
+{
+ const float rejection_hardness = 0.3;
+ return saturate((abs(coc) - dof.scatter_coc_threshold) * rejection_hardness);
+}
+
+float fast_luma(vec3 color)
+{
+ return (2.0 * color.g) + color.r + color.b;
+}
+
+/* Lightweight version of neighborhood clamping found in TAA. */
+vec3 dof_neighborhood_clamping(vec3 color)
+{
+ vec2 texel_size = 1.0 / vec2(textureSize(color_tx, 0));
+ vec2 uv = gl_FragCoord.xy * texel_size;
+ vec4 ofs = vec4(-1, 1, -1, 1) * texel_size.xxyy;
+
+ /* Luma clamping. 3x3 square neighborhood. */
+ float c00 = fast_luma(textureLod(color_tx, uv + ofs.xz, 0.0).rgb);
+ float c01 = fast_luma(textureLod(color_tx, uv + ofs.xz * vec2(1.0, 0.0), 0.0).rgb);
+ float c02 = fast_luma(textureLod(color_tx, uv + ofs.xw, 0.0).rgb);
+
+ float c10 = fast_luma(textureLod(color_tx, uv + ofs.xz * vec2(0.0, 1.0), 0.0).rgb);
+ float c11 = fast_luma(color);
+ float c12 = fast_luma(textureLod(color_tx, uv + ofs.xw * vec2(0.0, 1.0), 0.0).rgb);
+
+ float c20 = fast_luma(textureLod(color_tx, uv + ofs.yz, 0.0).rgb);
+ float c21 = fast_luma(textureLod(color_tx, uv + ofs.yz * vec2(1.0, 0.0), 0.0).rgb);
+ float c22 = fast_luma(textureLod(color_tx, uv + ofs.yw, 0.0).rgb);
+
+ float avg_luma = avg8(c00, c01, c02, c10, c12, c20, c21, c22);
+ float max_luma = max8(c00, c01, c02, c10, c12, c20, c21, c22);
+
+ float upper_bound = mix(max_luma, avg_luma, dof.denoise_factor);
+ upper_bound = mix(c11, upper_bound, dof.denoise_factor);
+
+ float clamped_luma = min(upper_bound, c11);
+
+ return color * clamped_luma * safe_rcp(c11);
+}
+
+void main()
+{
+ vec2 halfres = vec2(textureSize(color_tx, 0).xy);
+ vec2 uv = gl_FragCoord.xy / halfres;
+
+ out_color_gather = textureLod(color_tx, uv, 0.0);
+ out_coc = textureLod(coc_tx, uv, 0.0).r;
+
+ out_color_gather.rgb = dof_neighborhood_clamping(out_color_gather.rgb);
+
+ /* Only scatter if luminous enough. */
+ float do_scatter = dof_scatter_luminosity_rejection(out_color_gather.rgb);
+ /* Only scatter if CoC is big enough. */
+ do_scatter *= dof_scatter_coc_radius_rejection(out_coc);
+ /* Only scatter if CoC is not too big to avoid performance issues. */
+ do_scatter *= dof_scatter_screen_border_rejection(out_coc, uv, halfres);
+ /* Only scatter if neighborhood is different enough. */
+ do_scatter *= dof_scatter_neighborhood_rejection(out_color_gather.rgb);
+ /* For debuging. */
+ do_scatter *= float(!no_scatter_pass);
+
+ out_color_scatter = mix(vec3(0.0), out_color_gather.rgb, do_scatter);
+ out_color_gather.rgb = mix(out_color_gather.rgb, vec3(0.0), do_scatter);
+
+ /* Apply energy conservation to anamorphic scattered bokeh. */
+ out_color_scatter *= max_v2(dof.bokeh_anisotropic_scale_inv);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_downsample_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_downsample_frag.glsl
index c33eda0acd2..f255400ac97 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_downsample_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_downsample_frag.glsl
@@ -6,18 +6,16 @@
* weight luma for the bilateral weights.
*/
-#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
-/* Half resolution. */
-uniform sampler2D colorBuffer;
-uniform sampler2D cocBuffer;
+uniform sampler2D color_tx;
+uniform sampler2D coc_tx;
-/* Quarter resolution. */
-layout(location = 0) out vec4 outColor;
+layout(location = 0) out vec4 out_color;
void main()
{
- vec2 halfres_texel_size = 1.0 / vec2(textureSize(colorBuffer, 0).xy);
+ vec2 halfres_texel_size = 1.0 / vec2(textureSize(color_tx, 0).xy);
/* Center uv around the 4 halfres pixels. */
vec2 quad_center = (floor(gl_FragCoord.xy) * 2.0 + 1.0) * halfres_texel_size;
@@ -25,13 +23,13 @@ void main()
vec4 cocs;
for (int i = 0; i < 4; i++) {
vec2 sample_uv = quad_center + quad_offsets[i] * halfres_texel_size;
- colors[i] = textureLod(colorBuffer, sample_uv, 0.0);
- cocs[i] = textureLod(cocBuffer, sample_uv, 0.0).r;
+ colors[i] = textureLod(color_tx, sample_uv, 0.0);
+ cocs[i] = textureLod(coc_tx, sample_uv, 0.0).r;
}
- vec4 weights = dof_downsample_bilateral_coc_weights(cocs);
+ vec4 weights = dof_bilateral_coc_weights(cocs);
/* Normalize so that the sum is 1. */
weights *= safe_rcp(sum(weights));
- outColor = weighted_sum_array(colors, weights);
+ out_color = weighted_sum_array(colors, weights);
}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_recursive_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_recursive_frag.glsl
new file mode 100644
index 00000000000..fc19d9cec29
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_recursive_frag.glsl
@@ -0,0 +1,36 @@
+
+/**
+ * Reduce recursive pass: Simple coc & luma aware downsampling pass to generate mipmaps.
+ **/
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
+
+uniform sampler2D color_tx;
+uniform sampler2D coc_tx;
+
+layout(location = 0) out vec4 out_color;
+layout(location = 1) out float out_coc;
+
+/* Downsample pass done for each mip starting from mip1. */
+void main()
+{
+ vec2 input_texel_size = 1.0 / vec2(textureSize(color_tx, 0).xy);
+ /* Center uv around the 4 pixels of the previous mip. */
+ vec2 quad_center = (floor(gl_FragCoord.xy) * 2.0 + 1.0) * input_texel_size;
+
+ vec4 colors[4];
+ vec4 cocs;
+ for (int i = 0; i < 4; i++) {
+ vec2 sample_uv = quad_center + quad_offsets[i] * input_texel_size;
+ colors[i] = textureLod(color_tx, sample_uv, 0.0);
+ cocs[i] = textureLod(coc_tx, sample_uv, 0.0).r;
+ }
+
+ vec4 weights = dof_bilateral_coc_weights(cocs);
+ weights *= dof_bilateral_color_weights(colors);
+ /* Normalize so that the sum is 1. */
+ weights *= safe_rcp(sum(weights));
+
+ out_color = weighted_sum_array(colors, weights);
+ out_coc = dot(cocs, weights);
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_resolve_frag.glsl
new file mode 100644
index 00000000000..a6569d14090
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_resolve_frag.glsl
@@ -0,0 +1,111 @@
+
+/**
+ * Recombine Pass: Load separate convolution layer and composite with self
+ * slight defocus convolution and in-focus fields.
+ *
+ * The halfres gather methods are fast but lack precision for small CoC areas.
+ * To fix this we do a bruteforce gather to have a smooth transition between
+ * in-focus and defocus regions.
+ */
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_accumulator_lib.glsl)
+
+layout(std140) uniform sampling_block
+{
+ SamplingData sampling;
+};
+
+layout(std140) uniform dof_block
+{
+ DepthOfFieldData dof;
+};
+
+uniform sampler2D depth_tx;
+uniform sampler2D color_tx;
+uniform sampler2D color_bg_tx;
+uniform sampler2D color_fg_tx;
+uniform sampler2D color_holefill_tx;
+uniform sampler2D tiles_bg_tx;
+uniform sampler2D tiles_fg_tx;
+uniform sampler2D weight_bg_tx;
+uniform sampler2D weight_fg_tx;
+uniform sampler2D weight_holefill_tx;
+uniform sampler2D bokeh_lut_tx;
+
+in vec4 uvcoordsvar;
+
+layout(location = 0) out vec4 out_color;
+
+void main(void)
+{
+ ivec2 tile_co = ivec2(gl_FragCoord.xy / float(DOF_TILE_DIVISOR));
+ CocTile coc_tile = dof_coc_tile_load(tiles_fg_tx, tiles_bg_tx, tile_co);
+ CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile);
+
+ out_color = vec4(0.0);
+ float weight = 0.0;
+
+ vec4 layer_color;
+ float layer_weight;
+
+ vec2 uv_halfres = gl_FragCoord.xy / (2.0 * vec2(textureSize(color_bg_tx, 0)));
+
+ if (!no_holefill_pass && prediction.do_holefill) {
+ layer_color = textureLod(color_holefill_tx, uv_halfres, 0.0);
+ layer_weight = textureLod(weight_holefill_tx, uv_halfres, 0.0).r;
+ out_color = layer_color * safe_rcp(layer_weight);
+ weight = float(layer_weight > 0.0);
+ }
+
+ if (!no_background_pass && prediction.do_background) {
+ layer_color = textureLod(color_bg_tx, uv_halfres, 0.0);
+ layer_weight = textureLod(weight_bg_tx, uv_halfres, 0.0).r;
+ /* Always prefer background to holefill pass. */
+ layer_color *= safe_rcp(layer_weight);
+ layer_weight = float(layer_weight > 0.0);
+ /* Composite background. */
+ out_color = out_color * (1.0 - layer_weight) + layer_color;
+ weight = weight * (1.0 - layer_weight) + layer_weight;
+ /* Fill holes with the composited background. */
+ out_color *= safe_rcp(weight);
+ weight = float(weight > 0.0);
+ }
+
+ if (!no_slight_focus_pass && prediction.do_slight_focus) {
+ dof_slight_focus_gather(sampling,
+ dof,
+ depth_tx,
+ color_tx,
+ bokeh_lut_tx,
+ coc_tile.fg_slight_focus_max_coc,
+ layer_color,
+ layer_weight);
+ /* Composite slight defocus. */
+ out_color = out_color * (1.0 - layer_weight) + layer_color;
+ weight = weight * (1.0 - layer_weight) + layer_weight;
+ }
+
+ if (!no_focus_pass && prediction.do_focus) {
+ layer_color = safe_color(textureLod(color_tx, uvcoordsvar.xy, 0.0));
+ layer_weight = 1.0;
+ /* Composite in focus. */
+ out_color = out_color * (1.0 - layer_weight) + layer_color;
+ weight = weight * (1.0 - layer_weight) + layer_weight;
+ }
+
+ if (!no_foreground_pass && prediction.do_foreground) {
+ layer_color = textureLod(color_fg_tx, uv_halfres, 0.0);
+ layer_weight = textureLod(weight_fg_tx, uv_halfres, 0.0).r;
+ /* Composite foreground. */
+ out_color = out_color * (1.0 - layer_weight) + layer_color;
+ }
+
+ /* Fix float precision issue in alpha compositing. */
+ if (out_color.a > 0.99) {
+ out_color.a = 1.0;
+ }
+
+ if (debug_resolve_perf && coc_tile.fg_slight_focus_max_coc >= 0.5) {
+ out_color.rgb *= vec3(1.0, 0.1, 0.1);
+ }
+}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_scatter_frag.glsl
index 06dcbeaed66..e03b1903ece 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_scatter_frag.glsl
@@ -6,38 +6,36 @@
* invocations and overdraw.
*/
-#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_scatter_lib.glsl)
-uniform sampler2D occlusionBuffer;
-uniform sampler2D bokehLut;
-
-uniform vec2 bokehAnisotropyInv;
+layout(std140) uniform dof_block
+{
+ DepthOfFieldData dof;
+};
-flat in vec4 color1;
-flat in vec4 color2;
-flat in vec4 color3;
-flat in vec4 color4;
-flat in vec4 weights;
-flat in vec4 cocs;
-flat in vec2 spritepos;
-flat in float spritesize; /* MaxCoC */
+uniform sampler2D occlusion_tx;
+uniform sampler2D bokeh_lut_tx;
layout(location = 0) out vec4 fragColor;
float bokeh_shape(vec2 center)
{
vec2 co = gl_FragCoord.xy - center;
-
-#ifdef DOF_BOKEH_TEXTURE
- co *= bokehAnisotropyInv;
- float texture_size = float(textureSize(bokehLut, 0).x);
- /* Bias scale to avoid sampling at the texture's border. */
- float scale_fac = spritesize * (float(DOF_BOKEH_LUT_SIZE) / float(DOF_BOKEH_LUT_SIZE - 1));
- float dist = scale_fac * textureLod(bokehLut, (co / scale_fac) * 0.5 + 0.5, 0.0).r;
-#else
- float dist = length(co);
-#endif
-
+ float dist;
+ if (DOF_BOKEH_TEXTURE) {
+ if (DOF_FOREGROUND_PASS) {
+ /* Negate to flip bokeh shape. Mimics optical phenomenon. */
+ co = -co;
+ }
+ co *= dof.bokeh_anisotropic_scale_inv;
+ float texture_size = float(textureSize(bokeh_lut_tx, 0).x);
+ /* Bias scale to avoid sampling at the texture's border. */
+ float scale_fac = spritesize * (float(DOF_BOKEH_LUT_SIZE) / float(DOF_BOKEH_LUT_SIZE - 1));
+ dist = scale_fac * textureLod(bokeh_lut_tx, (co / scale_fac) * 0.5 + 0.5, 0.0).r;
+ }
+ else {
+ dist = length(co);
+ }
return dist;
}
@@ -59,9 +57,9 @@ void main(void)
}
if (!no_scatter_occlusion) {
- /* Works because target is the same size as occlusionBuffer. */
- vec2 uv = gl_FragCoord.xy / vec2(textureSize(occlusionBuffer, 0).xy);
- vec2 occlusion_data = texture(occlusionBuffer, uv).rg;
+ /* Works because target is the same size as occlusion_tx. */
+ vec2 uv = gl_FragCoord.xy / vec2(textureSize(occlusion_tx, 0).xy);
+ vec2 occlusion_data = texture(occlusion_tx, uv).rg;
/* Fix tilling artifacts. (Slide 90) */
const float correction_fac = 1.0 - DOF_FAST_GATHER_COC_ERROR;
/* Occlude the sprite with geometry from the same field
@@ -79,7 +77,7 @@ void main(void)
/* Do not accumulate alpha. This has already been accumulated by the gather pass. */
fragColor.a = 0.0;
-#ifdef DOF_DEBUG_SCATTER_PERF
- fragColor.rgb = avg(fragColor.rgb) * vec3(1.0, 0.0, 0.0);
-#endif
+ if (debug_scatter_perf) {
+ fragColor.rgb = avg(fragColor.rgb) * vec3(1.0, 0.0, 0.0);
+ }
}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_scatter_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_scatter_lib.glsl
new file mode 100644
index 00000000000..d31940f1968
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_scatter_lib.glsl
@@ -0,0 +1,17 @@
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
+
+IN_OUT DofScatterStageInterface
+{
+ /** Colors, weights, and Circle of confusion radii for the 4 pixels to scatter. */
+ flat vec4 color1;
+ flat vec4 color2;
+ flat vec4 color3;
+ flat vec4 color4;
+ flat vec4 weights;
+ flat vec4 cocs;
+ /** Sprite center position. In pixels. */
+ flat vec2 spritepos;
+ /* MaxCoC */
+ flat float spritesize;
+};
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_scatter_vert.glsl
index f349806d37e..0fcfbd3a8c5 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_scatter_vert.glsl
@@ -1,26 +1,20 @@
-#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
+/**
+ * Scatter pass: Use sprites to scatter the color of very bright pixel to have higher quality blur.
+ *
+ * We only scatter one triangle per sprite and one sprite per 4 pixels to reduce vertex shader
+ * invocations and overdraw.
+ **/
-uniform vec2 targetTexelSize;
-uniform int spritePerRow;
-uniform vec2 bokehAnisotropy;
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_scatter_lib.glsl)
-uniform sampler2D colorBuffer;
-uniform sampler2D cocBuffer;
-
-/* Scatter pass, calculate a triangle covering the CoC.
- * We render to a half resolution target with double width so we can
- * separate near and far fields. We also generate only one triangle per group of 4 pixels
- * to limit overdraw. */
+layout(std140) uniform dof_block
+{
+ DepthOfFieldData dof;
+};
-flat out vec4 color1;
-flat out vec4 color2;
-flat out vec4 color3;
-flat out vec4 color4;
-flat out vec4 weights;
-flat out vec4 cocs;
-flat out vec2 spritepos;
-flat out float spritesize;
+uniform sampler2D color_tx;
+uniform sampler2D coc_tx;
/* Load 4 Circle of confusion values. texel_co is centered around the 4 taps. */
vec4 fetch_cocs(vec2 texel_co)
@@ -28,21 +22,21 @@ vec4 fetch_cocs(vec2 texel_co)
/* TODO(fclem) The textureGather(sampler, co, comp) variant isn't here on some implementations.
*/
#if 0 // GPU_ARB_texture_gather
- vec2 uvs = texel_co / vec2(textureSize(cocBuffer, 0));
+ vec2 uvs = texel_co / vec2(textureSize(coc_tx, 0));
/* Reminder: Samples order is CW starting from top left. */
- cocs = textureGather(cocBuffer, uvs, isForegroundPass ? 0 : 1);
+ cocs = textureGather(coc_tx, uvs, isForegroundPass ? 0 : 1);
#else
ivec2 texel = ivec2(texel_co - 0.5);
vec4 cocs;
- cocs.x = texelFetchOffset(cocBuffer, texel, 0, ivec2(0, 1)).r;
- cocs.y = texelFetchOffset(cocBuffer, texel, 0, ivec2(1, 1)).r;
- cocs.z = texelFetchOffset(cocBuffer, texel, 0, ivec2(1, 0)).r;
- cocs.w = texelFetchOffset(cocBuffer, texel, 0, ivec2(0, 0)).r;
+ cocs.x = texelFetchOffset(coc_tx, texel, 0, ivec2(0, 1)).r;
+ cocs.y = texelFetchOffset(coc_tx, texel, 0, ivec2(1, 1)).r;
+ cocs.z = texelFetchOffset(coc_tx, texel, 0, ivec2(1, 0)).r;
+ cocs.w = texelFetchOffset(coc_tx, texel, 0, ivec2(0, 0)).r;
#endif
-#ifdef DOF_FOREGROUND_PASS
- cocs *= -1.0;
-#endif
+ if (is_foreground) {
+ cocs *= -1.0;
+ }
cocs = max(vec4(0.0), cocs);
/* We are scattering at half resolution, so divide CoC by 2. */
@@ -57,12 +51,12 @@ void vertex_discard()
void main()
{
- ivec2 tex_size = textureSize(cocBuffer, 0);
+ ivec2 tex_size = textureSize(coc_tx, 0);
int t_id = gl_VertexID / 3; /* Triangle Id */
/* Some math to get the target pixel. */
- ivec2 texelco = ivec2(t_id % spritePerRow, t_id / spritePerRow) * 2;
+ ivec2 texelco = ivec2(t_id % dof.scatter_sprite_per_row, t_id / dof.scatter_sprite_per_row) * 2;
/* Center sprite around the 4 texture taps. */
spritepos = vec2(texelco) + 1.0;
@@ -82,7 +76,7 @@ void main()
for (int i = 0; i < 4; i++) {
vec2 sample_uv = quad_center + quad_offsets[i] * input_texel_size;
- colors[i] = dof_load_scatter_color(colorBuffer, sample_uv, 0.0);
+ colors[i] = textureLod(color_tx, sample_uv, 0.0);
no_color = no_color && all(equal(colors[i].rgb, vec3(0.0)));
}
@@ -128,11 +122,11 @@ void main()
/* Add 2.5 to max_coc because the max_coc may not be centered on the sprite origin
* and because we smooth the bokeh shape a bit in the pixel shader. */
- gl_Position.xy *= spritesize * bokehAnisotropy + 2.5;
+ gl_Position.xy *= spritesize * dof.bokeh_anisotropic_scale + 2.5;
/* Position the sprite. */
gl_Position.xy += spritepos;
/* NDC range [-1..1]. */
- gl_Position.xy = gl_Position.xy * targetTexelSize * 2.0 - 1.0;
+ gl_Position.xy = gl_Position.xy * dof.texel_size * 2.0 - 1.0;
/* Add 2.5 for the same reason but without the ratio. */
spritesize += 2.5;
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_setup_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_setup_frag.glsl
new file mode 100644
index 00000000000..aba91e9bd12
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_setup_frag.glsl
@@ -0,0 +1,73 @@
+
+/**
+ * Setup pass: CoC and luma aware downsample to half resolution of the input scene color buffer.
+ *
+ * An addition to the downsample CoC, we output the maximum slight out of focus CoC to be
+ * sure we don't miss a pixel.
+ *
+ * Input:
+ * Full-resolution color & depth buffer
+ * Output:
+ * Half-resolution Color, signed CoC (out_coc.x), and max slight focus abs CoC (out_coc.y).
+ **/
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
+
+layout(std140) uniform dof_block
+{
+ DepthOfFieldData dof;
+};
+
+uniform sampler2D color_tx;
+uniform sampler2D depth_tx;
+
+layout(location = 0) out vec4 out_color;
+layout(location = 1) out vec2 out_coc;
+
+float dof_abs_max_slight_of_focus_coc(vec4 cocs)
+{
+ /* Clamp to 0.5 if full in defocus to differentiate full focus tiles with coc == 0.0.
+ * This enables an optimization in the resolve pass. */
+ const vec4 threshold = vec4(dof_layer_threshold + dof_layer_offset);
+ cocs = abs(cocs);
+ bvec4 defocus = greaterThan(cocs, threshold);
+ bvec4 focus = lessThanEqual(cocs, vec4(0.5));
+ if (any(defocus) && any(focus)) {
+ /* For the same reason as in the flatten pass. This is a case we cannot optimize for. */
+ cocs = mix(cocs, vec4(dof_tile_mixed), focus);
+ cocs = mix(cocs, vec4(dof_tile_mixed), defocus);
+ }
+ else {
+ cocs = mix(cocs, vec4(dof_tile_focus), focus);
+ cocs = mix(cocs, vec4(dof_tile_defocus), defocus);
+ }
+ return max_v4(cocs);
+}
+
+void main()
+{
+ vec2 fullres_texel_size = 1.0 / vec2(textureSize(color_tx, 0).xy);
+ /* Center uv around the 4 fullres pixels. */
+ vec2 quad_center = (floor(gl_FragCoord.xy) * 2.0 + 1.0) * fullres_texel_size;
+
+ vec4 colors[4];
+ vec4 cocs;
+ for (int i = 0; i < 4; i++) {
+ vec2 sample_uv = quad_center + quad_offsets[i] * fullres_texel_size;
+ colors[i] = safe_color(textureLod(color_tx, sample_uv, 0.0));
+ cocs[i] = dof_coc_from_depth(dof, sample_uv, textureLod(depth_tx, sample_uv, 0.0).r);
+ }
+
+ cocs = clamp(cocs, -dof.coc_abs_max, dof.coc_abs_max);
+
+ vec4 weights = dof_bilateral_coc_weights(cocs);
+ weights *= dof_bilateral_color_weights(colors);
+ /* Normalize so that the sum is 1. */
+ weights *= safe_rcp(sum(weights));
+
+ out_color = weighted_sum_array(colors, weights);
+ out_coc.x = dot(cocs, weights);
+ out_coc.y = dof_abs_max_slight_of_focus_coc(cocs);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_tiles_dilate_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_tiles_dilate_frag.glsl
new file mode 100644
index 00000000000..ced77716789
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_tiles_dilate_frag.glsl
@@ -0,0 +1,117 @@
+
+/**
+ * Tile dilate pass: Takes the 8x8 Tiles buffer and converts dilates the tiles with large CoC to
+ * their neighborhood. This pass is repeated multiple time until the maximum CoC can be covered.
+ *
+ * Input & Output:
+ * - Separated foreground and background CoC. 1/8th of half-res resolution. So 1/16th of full-res.
+ **/
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
+
+uniform int ring_count;
+uniform int ring_width_multiplier;
+uniform bool dilate_slight_focus;
+
+uniform sampler2D tiles_fg_tx;
+uniform sampler2D tiles_bg_tx;
+
+layout(location = 0) out vec4 out_tile_fg;
+layout(location = 1) out vec3 out_tile_bg;
+
+/* Error introduced by the random offset of the gathering kernel's center. */
+const float bluring_radius_error = 1.0 + 1.0 / (float(DOF_GATHER_RING_COUNT) + 0.5);
+const float tile_to_fullres_factor = float(DOF_TILE_DIVISOR);
+
+void main()
+{
+ ivec2 center_tile_pos = ivec2(gl_FragCoord.xy);
+
+ CocTile ring_buckets[DOF_DILATE_RING_COUNT];
+
+ for (int ring = 0; ring < ring_count && ring < DOF_DILATE_RING_COUNT; ring++) {
+ ring_buckets[ring] = dof_coc_tile_init();
+
+ int ring_distance = ring + 1;
+ for (int sample_id = 0; sample_id < 4 * ring_distance; sample_id++) {
+ ivec2 offset = dof_square_ring_sample_offset(ring_distance, sample_id);
+
+ offset *= ring_width_multiplier;
+
+ for (int i = 0; i < 2; i++) {
+ ivec2 adj_tile_pos = center_tile_pos + ((i == 0) ? offset : -offset);
+
+ CocTile adj_tile = dof_coc_tile_load(tiles_fg_tx, tiles_bg_tx, adj_tile_pos);
+
+ if (DILATE_MODE_MIN_MAX) {
+ /* Actually gather the "absolute" biggest coc but keeping the sign. */
+ ring_buckets[ring].fg_min_coc = min(ring_buckets[ring].fg_min_coc, adj_tile.fg_min_coc);
+ ring_buckets[ring].bg_max_coc = max(ring_buckets[ring].bg_max_coc, adj_tile.bg_max_coc);
+
+ if (dilate_slight_focus) {
+ ring_buckets[ring].fg_slight_focus_max_coc = dof_coc_max_slight_focus(
+ ring_buckets[ring].fg_slight_focus_max_coc, adj_tile.fg_slight_focus_max_coc);
+ }
+ }
+ else { /* DILATE_MODE_MIN_ABS */
+ ring_buckets[ring].fg_max_coc = max(ring_buckets[ring].fg_max_coc, adj_tile.fg_max_coc);
+ ring_buckets[ring].bg_min_coc = min(ring_buckets[ring].bg_min_coc, adj_tile.bg_min_coc);
+
+ /* Should be tight as possible to reduce gather overhead (see slide 61). */
+ float closest_neighbor_distance = length(max(abs(vec2(offset)) - 1.0, 0.0)) *
+ tile_to_fullres_factor;
+
+ ring_buckets[ring].fg_max_intersectable_coc = max(
+ ring_buckets[ring].fg_max_intersectable_coc,
+ adj_tile.fg_max_intersectable_coc + closest_neighbor_distance);
+ ring_buckets[ring].bg_min_intersectable_coc = min(
+ ring_buckets[ring].bg_min_intersectable_coc,
+ adj_tile.bg_min_intersectable_coc + closest_neighbor_distance);
+ }
+ }
+ }
+ }
+
+ /* Load center tile. */
+ CocTile out_tile = dof_coc_tile_load(tiles_fg_tx, tiles_bg_tx, center_tile_pos);
+
+ /* Dilate once. */
+ if (dilate_slight_focus) {
+ out_tile.fg_slight_focus_max_coc = dof_coc_max_slight_focus(
+ out_tile.fg_slight_focus_max_coc, ring_buckets[0].fg_slight_focus_max_coc);
+ }
+
+ for (int ring = 0; ring < ring_count && ring < DOF_DILATE_RING_COUNT; ring++) {
+ float ring_distance = float(ring + 1);
+
+ ring_distance = (ring_distance * ring_width_multiplier - 1) * tile_to_fullres_factor;
+
+ if (DILATE_MODE_MIN_MAX) {
+ /* NOTE(fclem): Unsure if both sides of the inequalities have the same unit. */
+ if (-ring_buckets[ring].fg_min_coc * bluring_radius_error > ring_distance) {
+ out_tile.fg_min_coc = min(out_tile.fg_min_coc, ring_buckets[ring].fg_min_coc);
+ }
+
+ if (ring_buckets[ring].bg_max_coc * bluring_radius_error > ring_distance) {
+ out_tile.bg_max_coc = max(out_tile.bg_max_coc, ring_buckets[ring].bg_max_coc);
+ }
+ }
+ else { /* DILATE_MODE_MIN_ABS */
+ /* Find minimum absolute CoC radii that will be intersected for the previously
+ * computed maximum CoC values. */
+ if (-out_tile.fg_min_coc * bluring_radius_error > ring_distance) {
+ out_tile.fg_max_coc = max(out_tile.fg_max_coc, ring_buckets[ring].fg_max_coc);
+ out_tile.fg_max_intersectable_coc = max(out_tile.fg_max_intersectable_coc,
+ ring_buckets[ring].fg_max_intersectable_coc);
+ }
+
+ if (out_tile.bg_max_coc * bluring_radius_error > ring_distance) {
+ out_tile.bg_min_coc = min(out_tile.bg_min_coc, ring_buckets[ring].bg_min_coc);
+ out_tile.bg_min_intersectable_coc = min(out_tile.bg_min_intersectable_coc,
+ ring_buckets[ring].bg_min_intersectable_coc);
+ }
+ }
+ }
+
+ dof_coc_tile_store(out_tile, out_tile_fg, out_tile_bg);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_flatten_tiles_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_tiles_flatten_frag.glsl
index 48195a1daea..7462a272fbd 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_flatten_tiles_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_tiles_flatten_frag.glsl
@@ -5,22 +5,25 @@
* Output min and max values for each tile and for both foreground & background.
* Also outputs min intersectable CoC for the background, which is the minimum CoC
* that comes from the background pixels.
+ *
+ * Input:
+ * - Half-resolution Circle of confusion. Out of setup pass.
+ * Output:
+ * - Separated foreground and background CoC. 1/8th of half-res resolution. So 1/16th of full-res.
*/
-#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
-/* Half resolution. */
-uniform sampler2D halfResCocBuffer;
+uniform sampler2D coc_tx;
-/* 1/8th of halfResCocBuffer resolution. So 1/16th of fullres. */
-layout(location = 0) out vec4 outFgCoc;
-layout(location = 1) out vec3 outBgCoc;
+layout(location = 0) out vec4 out_tile_fg;
+layout(location = 1) out vec3 out_tile_bg;
const int halfres_tile_divisor = DOF_TILE_DIVISOR / 2;
void main()
{
- ivec2 halfres_bounds = textureSize(halfResCocBuffer, 0).xy - 1;
+ ivec2 halfres_bounds = textureSize(coc_tx, 0).xy - 1;
ivec2 tile_co = ivec2(gl_FragCoord.xy);
CocTile tile = dof_coc_tile_init();
@@ -29,7 +32,7 @@ void main()
/* OPTI: Could be done in separate passes. */
for (int y = 0; y < halfres_tile_divisor; y++) {
ivec2 sample_texel = tile_co * halfres_tile_divisor + ivec2(x, y);
- vec2 sample_data = texelFetch(halfResCocBuffer, min(sample_texel, halfres_bounds), 0).rg;
+ vec2 sample_data = texelFetch(coc_tx, min(sample_texel, halfres_bounds), 0).rg;
float sample_coc = sample_data.x;
float sample_slight_focus_coc = sample_data.y;
@@ -53,5 +56,5 @@ void main()
}
}
- dof_coc_tile_store(tile, outFgCoc, outBgCoc);
+ dof_coc_tile_store(tile, out_tile_fg, out_tile_bg);
}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_film_filter_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_film_filter_frag.glsl
new file mode 100644
index 00000000000..f924e321deb
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_film_filter_frag.glsl
@@ -0,0 +1,114 @@
+
+/**
+ * Accumulate input texture into the film accumulation buffer.
+ *
+ * All samples inside the filter radius are projected to the input texture.
+ * The nearest input sample is then projected back to the destination texture space
+ * to get an accurate filter weight.
+ *
+ * If using nearest filtering (for non-color data) only the closest sample is considered
+ * and the weight is use as a distance metric.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_film_lib.glsl)
+
+layout(std140) uniform camera_block
+{
+ CameraData camera;
+};
+
+layout(std140) uniform film_block
+{
+ FilmData film;
+};
+
+uniform sampler2D input_tx;
+uniform sampler2D data_tx;
+uniform sampler2D weight_tx;
+
+in vec4 uvcoordsvar;
+
+layout(location = 0) out vec4 out_data;
+layout(location = 1) out float out_weight;
+
+/* clang-format off */
+const vec2 sample_offsets_plus[5] = vec2[5](vec2(0, 1), vec2(-1, 0), vec2(0, 0), vec2(1, 0), vec2(0, -1));
+const vec2 sample_offsets_3x3[9] = vec2[9](vec2(-1, 1), vec2(0, 1), vec2(1, 1), vec2(-1, 0), vec2(0, 0), vec2(1, 0), vec2(-1, -1), vec2(0, -1), vec2(1, -1));
+/* clang-format on */
+
+void main(void)
+{
+ out_data = vec4(0.0);
+ out_weight = 0.0;
+
+ /* TODO(fclem) Split into multiple shaders? Measure benefits. */
+ if (camera.filter_size < 1.0 || !film_is_color_data(film)) {
+ film_process_sample(camera,
+ film,
+ ProjectionMatrix,
+ ProjectionMatrixInverse,
+ input_tx,
+ vec2(0.0),
+ out_data,
+ out_weight);
+ }
+ else if (camera.filter_size < M_SQRT2) {
+ for (int i = 0; i < 5; i++) {
+ film_process_sample(camera,
+ film,
+ ProjectionMatrix,
+ ProjectionMatrixInverse,
+ input_tx,
+ sample_offsets_plus[i],
+ out_data,
+ out_weight);
+ }
+ }
+ else if (camera.filter_size < 2.0) {
+ for (int i = 0; i < 9; i++) {
+ film_process_sample(camera,
+ film,
+ ProjectionMatrix,
+ ProjectionMatrixInverse,
+ input_tx,
+ sample_offsets_3x3[i],
+ out_data,
+ out_weight);
+ }
+ }
+ else {
+ /* This is slow but using large filter is not very common. */
+ float extent = floor(camera.filter_size);
+ for (float x = -extent; x < extent; x++) {
+ for (float y = -extent; y < extent; y++) {
+ film_process_sample(camera,
+ film,
+ ProjectionMatrix,
+ ProjectionMatrixInverse,
+ input_tx,
+ vec2(x, y),
+ out_data,
+ out_weight);
+ }
+ }
+ }
+
+ if (film.use_history) {
+ vec2 uv_history = film_uv_history_get(camera, camera, uvcoordsvar.xy);
+ vec4 history_data = textureLod(data_tx, uv_history, 0.0);
+ float history_weight = textureLod(weight_tx, uv_history, 0.0).r;
+
+ if (film_is_color_data(film)) {
+ out_data += history_data;
+ out_weight += history_weight;
+ }
+ else {
+ /* Non-color data do not accumulates. It is replaced by nearest value. */
+ if (history_weight > out_weight) {
+ out_weight = history_weight;
+ out_data = history_data;
+ }
+ }
+ }
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_film_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_film_lib.glsl
new file mode 100644
index 00000000000..a990567800a
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_film_lib.glsl
@@ -0,0 +1,149 @@
+
+/**
+ * Film accumulation utils functions.
+ **/
+
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+#pragma BLENDER_REQUIRE(eevee_camera_lib.glsl)
+
+bool film_is_color_data(FilmData film)
+{
+ return film.data_type < FILM_DATA_FLOAT;
+}
+
+vec4 film_data_encode(FilmData film, vec4 data, float weight)
+{
+ if (film_is_color_data(film)) {
+ /* Could we assume safe color from earlier pass? */
+ data = safe_color(data);
+ /* Convert transmittance to opacity. */
+ data.a = saturate(1.0 - data.a);
+ }
+
+ if (film.data_type == FILM_DATA_COLOR_LOG) {
+ /* TODO(fclem) Pre-expose. */
+ data.rgb = log2(1.0 + data.rgb);
+ }
+ else if (film.data_type == FILM_DATA_DEPTH) {
+ /* TODO(fclem) Depth should be converted to radial depth in panoramic projection. */
+ }
+ else if (film.data_type == FILM_DATA_MOTION) {
+ /* Motion vectors are in camera uv space. But final motion vectors are in pixel units. */
+ data *= film.uv_scale_inv.xyxy;
+ }
+
+ if (film_is_color_data(film)) {
+ data *= weight;
+ }
+ return data;
+}
+
+vec4 film_data_decode(FilmData film, vec4 data, float weight)
+{
+ if (film_is_color_data(film)) {
+ data *= safe_rcp(weight);
+ }
+
+ if (film.data_type == FILM_DATA_COLOR_LOG) {
+ /* TODO(fclem) undo Pre-expose. */
+ data.rgb = exp2(data.rgb) - 1.0;
+ }
+ return data;
+}
+
+/* Returns uv's position in the previous frame. */
+vec2 film_uv_history_get(CameraData camera, CameraData camera_history, vec2 uv)
+{
+#if 0 /* TODO reproject history */
+ vec3 V = camera_view_from_uv(camera, uv);
+ vec3 V_prev = transform_point(hitory_mat, V);
+ vec2 uv_history = camera_uv_from_view(camera_history, V_prev);
+ return uv_history;
+#endif
+ return uv;
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Filter
+ * \{ */
+
+float film_filter_weight(CameraData camera, vec2 offset)
+{
+#if 1 /* Faster */
+ /* Gaussian fitted to Blackman-Harris. */
+ float r = len_squared(offset) / sqr(camera.filter_size);
+ const float sigma = 0.284;
+ const float fac = -0.5 / (sigma * sigma);
+ float weight = exp(fac * r);
+#else
+ /* Blackman-Harris filter. */
+ float r = M_2PI * saturate(0.5 + length(offset) / (2.0 * camera.filter_size));
+ float weight = 0.35875 - 0.48829 * cos(r) + 0.14128 * cos(2.0 * r) - 0.01168 * cos(3.0 * r);
+#endif
+ /* Always return a weight above 0 to avoid blind spots between samples. */
+ return max(weight, 1e-6);
+}
+
+/* Camera UV is the full-frame UV. Film uv is after cropping from render border. */
+vec2 film_sample_from_camera_uv(FilmData film, vec2 sample_uv)
+{
+ return (sample_uv - film.uv_bias) * film.uv_scale_inv;
+}
+
+vec2 film_sample_to_camera_uv(FilmData film, vec2 sample_co)
+{
+ return sample_co * film.uv_scale + film.uv_bias;
+}
+
+void film_process_sample(CameraData camera,
+ FilmData film,
+ mat4 input_persmat,
+ mat4 input_persinv,
+ sampler2D input_tx,
+ vec2 sample_offset,
+ inout vec4 data,
+ inout float weight)
+{
+ /* Project sample from destrination space to source texture. */
+ vec2 sample_center = gl_FragCoord.xy;
+ vec2 sample_uv = film_sample_to_camera_uv(film, sample_center + sample_offset);
+ vec3 vV_dst = camera_view_from_uv(camera, sample_uv);
+ /* Pixels outside of projection range. */
+ if (vV_dst == vec3(0.0)) {
+ return;
+ }
+
+ bool is_persp = camera.type != CAMERA_ORTHO;
+ vec2 uv_src = camera_uv_from_view(input_persmat, is_persp, vV_dst);
+ /* Snap to sample actual location (pixel center). */
+ vec2 input_size = vec2(textureSize(input_tx, 0));
+ vec2 texel_center_src = floor(uv_src * input_size) + 0.5;
+ uv_src = texel_center_src / input_size;
+ /* Discard pixels outside of input range. */
+ if (any(greaterThan(abs(uv_src - 0.5), vec2(0.5)))) {
+ return;
+ }
+
+ /* Reproject sample location in destination space to have correct distance metric. */
+ vec3 vV_src = camera_view_from_uv(input_persinv, uv_src);
+ vec2 uv_cam = camera_uv_from_view(camera, vV_src);
+ vec2 sample_dst = film_sample_from_camera_uv(film, uv_cam);
+
+ /* Equirectangular projection might wrap and have more than one point mapping to the same
+ * original coordinate. We need to get the closest pixel center.
+ * NOTE: This is wrong for projection outside the main frame. */
+ if (camera.type == CAMERA_PANO_EQUIRECT) {
+ sample_center = film_sample_to_camera_uv(film, sample_center);
+ vec3 vV_center = camera_view_from_uv(camera, sample_center);
+ sample_center = camera_uv_from_view(camera, vV_center);
+ sample_center = film_sample_from_camera_uv(film, sample_center);
+ }
+ /* Compute filter weight and add to weighted sum. */
+ vec2 offset = sample_dst - sample_center;
+ float sample_weight = film_filter_weight(camera, offset);
+ vec4 sample_data = textureLod(input_tx, uv_src, 0.0);
+ data += film_data_encode(film, sample_data, sample_weight);
+ weight += sample_weight;
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_film_resolve_depth_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_film_resolve_depth_frag.glsl
new file mode 100644
index 00000000000..85ec0186d7f
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_film_resolve_depth_frag.glsl
@@ -0,0 +1,22 @@
+
+#pragma BLENDER_REQUIRE(eevee_film_lib.glsl)
+
+layout(std140) uniform film_block
+{
+ FilmData film;
+};
+
+uniform sampler2D data_tx;
+uniform sampler2D weight_tx;
+
+in vec4 uvcoordsvar;
+
+void main(void)
+{
+ vec2 uv = uvcoordsvar.xy;
+
+ vec4 color = textureLod(data_tx, uv, 0.0);
+ float weight = textureLod(weight_tx, uv, 0.0).r;
+
+ gl_FragDepth = film_data_decode(film, color, weight).r;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_film_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_film_resolve_frag.glsl
new file mode 100644
index 00000000000..bdf36d52e96
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_film_resolve_frag.glsl
@@ -0,0 +1,31 @@
+
+#pragma BLENDER_REQUIRE(eevee_film_lib.glsl)
+
+layout(std140) uniform film_block
+{
+ FilmData film;
+};
+
+uniform sampler2D data_tx;
+uniform sampler2D weight_tx;
+uniform sampler2D first_sample_tx;
+
+in vec4 uvcoordsvar;
+
+layout(location = 0) out vec4 out_color;
+
+void main(void)
+{
+ vec2 uv = uvcoordsvar.xy;
+
+ vec4 color = textureLod(data_tx, uv, 0.0);
+ float weight = textureLod(weight_tx, uv, 0.0).r;
+
+ out_color = film_data_decode(film, color, weight);
+
+ /* First sample is stored in a fullscreen buffer. */
+ vec2 uv_first_sample = ((uv * film.extent) + film.offset) /
+ vec2(textureSize(first_sample_tx, 0).xy);
+ vec4 first_sample = textureLod(first_sample_tx, uv_first_sample, 0.0);
+ out_color = mix(first_sample, out_color, film.opacity);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_gbuffer_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_gbuffer_lib.glsl
new file mode 100644
index 00000000000..224a376bde4
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_gbuffer_lib.glsl
@@ -0,0 +1,272 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_closure_lib.glsl)
+
+/* -------------------------------------------------------------------- */
+/** \name Encoding and decoding functions
+ *
+ * \{ */
+
+uint gbuffer_encode_unit_float_to_uint(float scalar, const uint bit_size)
+{
+ float fac = float((1u << bit_size) - 1u);
+ return uint(saturate(scalar) * fac);
+}
+
+float gbuffer_decode_unit_float_from_uint(uint packed_scalar, const uint bit_size)
+{
+ float fac = 1.0 / float((1u << bit_size) - 1u);
+ uint mask = ~(0xFFFFFFFFu << bit_size);
+ return float(packed_scalar & mask) * fac;
+}
+
+/* Expects input to be normalized. */
+vec2 gbuffer_encode_normal(vec3 normal)
+{
+ vec3 vN = normal_world_to_view(normal);
+ bool neg = vN.z < 0.0;
+ if (neg) {
+ vN.z = -vN.z;
+ }
+ vec2 packed_normal = normal_encode(vN);
+ // return packed_normal;
+ return (neg) ? -packed_normal : packed_normal;
+}
+
+vec3 gbuffer_decode_normal(vec2 packed_normal)
+{
+ bool neg = packed_normal.y < 0.0;
+ vec3 vN = normal_decode(abs(packed_normal));
+ if (neg) {
+ vN.z = -vN.z;
+ }
+ return normal_view_to_world(vN);
+}
+
+/* Note: does not handle negative colors. */
+uint gbuffer_encode_color(vec3 color)
+{
+ color *= 1.0; /* Test */
+ float intensity = length(color);
+ /* Normalize to store it like a normal vector. */
+ // color *= safe_rcp(intensity);
+
+ uint encoded_color;
+ // encoded_color = gbuffer_encode_unit_float_to_uint(saturate(color.x), 10u) << 10u;
+ // encoded_color |= gbuffer_encode_unit_float_to_uint(saturate(color.y), 10u);
+ // encoded_color |= gbuffer_encode_unit_float_to_uint(saturate(intensity), 12u) << 20u;
+
+ encoded_color = gbuffer_encode_unit_float_to_uint(saturate(color.x), 11u);
+ encoded_color |= gbuffer_encode_unit_float_to_uint(saturate(color.y), 11u) << 11u;
+ encoded_color |= gbuffer_encode_unit_float_to_uint(saturate(color.z), 10u) << 21u;
+ return encoded_color;
+}
+
+vec3 gbuffer_decode_color(uint packed_data)
+{
+ vec3 color;
+ // color.x = gbuffer_decode_unit_float_from_uint(packed_data >> 10u, 10u);
+ // color.y = gbuffer_decode_unit_float_from_uint(packed_data, 10u);
+ // color.z = sqrt(1.0 - clamp(dot(color.xy, color.xy), 0.0, 1.0));
+ // color *= gbuffer_decode_unit_float_from_uint(packed_data >> 20u, 12u);
+
+ color.x = gbuffer_decode_unit_float_from_uint(packed_data, 11u);
+ color.y = gbuffer_decode_unit_float_from_uint(packed_data >> 11u, 11u);
+ color.z = gbuffer_decode_unit_float_from_uint(packed_data >> 21u, 10u);
+ color *= 1.0; /* Test */
+ return color;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Global data
+ *
+ * \{ */
+
+void gbuffer_load_global_data(vec4 transmit_normal_in, out float thickness)
+{
+ thickness = transmit_normal_in.w;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Diffuse data
+ *
+ * \{ */
+
+ClosureDiffuse gbuffer_load_diffuse_data(vec4 color_in, vec4 normal_in, vec4 data_in)
+{
+ ClosureDiffuse data_out;
+ if (normal_in.z == -1.0) {
+ /* Transmission data is Refraction data. */
+ data_out.color = vec3(0.0);
+ data_out.N = vec3(1.0);
+ data_out.sss_id = 0u;
+ data_out.sss_radius = vec3(-1.0);
+ }
+ else {
+ data_out.color = color_in.rgb;
+ data_out.N = gbuffer_decode_normal(normal_in.xy);
+ data_out.sss_id = uint(normal_in.z * 1024.0);
+ data_out.sss_radius = data_in.rgb;
+ }
+ return data_out;
+}
+
+ClosureDiffuse gbuffer_load_diffuse_data(sampler2D transmit_color_tx,
+ sampler2D transmit_normal_tx,
+ sampler2D transmit_data_tx,
+ vec2 uv)
+{
+ vec4 tra_col_in = texture(transmit_color_tx, uv);
+ vec4 tra_nor_in = texture(transmit_normal_tx, uv);
+ vec4 tra_dat_in = texture(transmit_data_tx, uv);
+ return gbuffer_load_diffuse_data(tra_col_in, tra_nor_in, tra_dat_in);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Glossy data
+ *
+ * \{ */
+
+ClosureReflection gbuffer_load_reflection_data(vec4 color_in, vec4 normal_in)
+{
+ ClosureReflection data_out;
+ data_out.color = color_in.rgb;
+ data_out.N = gbuffer_decode_normal(normal_in.xy);
+ data_out.roughness = normal_in.z;
+ return data_out;
+}
+
+ClosureReflection gbuffer_load_reflection_data(sampler2D reflect_color_tx,
+ sampler2D reflect_normal_tx,
+ vec2 uv)
+{
+ vec4 ref_col_in = texture(reflect_color_tx, uv);
+ vec4 ref_nor_in = texture(reflect_normal_tx, uv);
+ return gbuffer_load_reflection_data(ref_col_in, ref_nor_in);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Refraction data
+ *
+ * \{ */
+
+ClosureRefraction gbuffer_load_refraction_data(vec4 color_in, vec4 normal_in, vec4 data_in)
+{
+ ClosureRefraction data_out;
+ if (normal_in.z == -1.0) {
+ data_out.color = color_in.rgb;
+ data_out.N = gbuffer_decode_normal(normal_in.xy);
+ data_out.ior = data_in.x;
+ data_out.roughness = data_in.y;
+ }
+ else {
+ /* Transmission data is Diffuse/SSS data. */
+ data_out.color = vec3(0.0);
+ data_out.N = vec3(1.0);
+ data_out.ior = -1.0;
+ data_out.roughness = 0.0;
+ }
+ return data_out;
+}
+
+ClosureRefraction gbuffer_load_refraction_data(sampler2D transmit_color_tx,
+ sampler2D transmit_normal_tx,
+ sampler2D transmit_data_tx,
+ vec2 uv)
+{
+ vec4 tra_col_in = texture(transmit_color_tx, uv);
+ vec4 tra_nor_in = texture(transmit_normal_tx, uv);
+ vec4 tra_dat_in = texture(transmit_data_tx, uv);
+ return gbuffer_load_refraction_data(tra_col_in, tra_nor_in, tra_dat_in);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Volume data
+ *
+ * Pack all volumetric effects.
+ *
+ * \{ */
+
+#define VOLUME_HETEROGENEOUS -2.0
+
+uvec4 gbuffer_store_volume_data(ClosureVolume data_in)
+{
+ uvec4 data_out;
+ data_out.x = gbuffer_encode_color(data_in.emission);
+ data_out.y = gbuffer_encode_color(data_in.scattering);
+ data_out.z = gbuffer_encode_color(data_in.transmittance);
+ data_out.w = floatBitsToUint(data_in.anisotropy);
+ return data_out;
+}
+
+ClosureVolume gbuffer_load_volume_data(usampler2D gbuffer_tx, vec2 uv)
+{
+ uvec4 data_in = texture(gbuffer_tx, uv);
+
+ ClosureVolume data_out;
+ data_out.emission = gbuffer_decode_color(data_in.x);
+ data_out.scattering = gbuffer_decode_color(data_in.y);
+ data_out.transmittance = gbuffer_decode_color(data_in.z);
+ data_out.anisotropy = uintBitsToFloat(data_in.w);
+ return data_out;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Emission data
+ *
+ * \{ */
+
+vec3 gbuffer_store_emission_data(ClosureEmission data_in)
+{
+ return data_in.emission;
+}
+
+ClosureEmission gbuffer_load_emission_data(sampler2D gbuffer_tx, vec2 uv)
+{
+ vec4 data_in = texture(gbuffer_tx, uv);
+
+ ClosureEmission data_out;
+ data_out.emission = data_in.xyz;
+ return data_out;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Transparency data
+ *
+ * \{ */
+
+vec4 gbuffer_store_transparency_data(ClosureTransparency data_in)
+{
+ vec4 data_out;
+ data_out.xyz = data_in.transmittance;
+ data_out.w = data_in.holdout;
+ return data_out;
+}
+
+ClosureTransparency gbuffer_load_transparency_data(sampler2D gbuffer_tx, vec2 uv)
+{
+ vec4 data_in = texture(gbuffer_tx, uv);
+
+ ClosureTransparency data_out;
+ data_out.transmittance = data_in.xyz;
+ data_out.holdout = data_in.w;
+ return data_out;
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_hiz_copy_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_hiz_copy_frag.glsl
new file mode 100644
index 00000000000..f0ee9f515c9
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_hiz_copy_frag.glsl
@@ -0,0 +1,17 @@
+/**
+ * Copy input depth texture to lower left corner of the destination, filling any padding with
+ * clamped texture extrapolation.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
+uniform sampler2D depth_tx;
+
+layout(location = 0) out float out_depth;
+
+void main()
+{
+ vec2 uv = gl_FragCoord.xy / vec2(textureSize(depth_tx, 0).xy);
+
+ out_depth = texture(depth_tx, uv).r;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_hiz_downsample_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_hiz_downsample_frag.glsl
new file mode 100644
index 00000000000..9a095d960b2
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_hiz_downsample_frag.glsl
@@ -0,0 +1,40 @@
+/**
+ * Shader that down-sample depth buffer, creating a Hierarchical-Z buffer.
+ * Saves max value of each 2x2 texel in the mipmap above the one we are rendering to.
+ * Adapted from http://rastergrid.com/blog/2010/10/hierarchical-z-map-based-occlusion-culling/
+ *
+ * Major simplification has been made since we pad the buffer to always be bigger than input to
+ * avoid mipmapping misalignement.
+ */
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
+uniform sampler2D depth_tx;
+
+uniform vec2 texel_size;
+
+layout(location = 0) out float out_depth;
+
+#ifndef GPU_ARB_texture_gather
+vec4 texGather(sampler2D tex, vec2 uv)
+{
+ vec4 ofs = vec2(0.5, 0.5, -0.5, -0.5) * texel_size.xyxy;
+ return vec4(texture(tex, uv + ofs.zw).r,
+ texture(tex, uv + ofs.zy).r,
+ texture(tex, uv + ofs.xw).r,
+ texture(tex, uv + ofs.xy).r);
+}
+#else
+# define texGather(a, b) textureGather(a, b)
+#endif
+
+void main()
+{
+ /* NOTE(@fclem): textureSize() does not work the same on all implementations
+ * when changing the min and max texture levels. Use uniform instead (see T87801). */
+ vec2 uv = gl_FragCoord.xy * texel_size;
+
+ vec4 samp = texGather(depth_tx, uv);
+
+ out_depth = max_v4(samp);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_irradiance_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_irradiance_lib.glsl
new file mode 100644
index 00000000000..143de5b0093
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_irradiance_lib.glsl
@@ -0,0 +1,139 @@
+
+/**
+ * Diffuse Irradiance encoding, decoding, loading, evaluation functions.
+ **/
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
+/* ---------------------------------------------------------------------- */
+/** \name Visibility
+ *
+ * Irradiance grid is backed by a grid of small filtered distance map that reduces
+ * light leak by performing a chebishev test.
+ *
+ * The technique is similar to the one in the paper even if not using raytracing to update
+ * lightprobes.
+ * "Dynamic Diffuse Global Illumination with Ray-Traced Irradiance Fields"
+ * http://jcgt.org/published/0008/02/01/
+ * \{ */
+
+vec4 visibility_encode(vec2 accum, float range)
+{
+ accum /= range;
+ vec4 data;
+ data.x = fract(accum.x);
+ data.y = floor(accum.x) / 255.0;
+ data.z = fract(accum.y);
+ data.w = floor(accum.y) / 255.0;
+ return data;
+}
+
+vec2 visibility_decode(vec4 data, float range)
+{
+ return (data.xz + data.yw * 255.0) * range;
+}
+
+vec2 visibility_mapping_octahedron(vec3 cubevec, vec2 texel_size)
+{
+ /* Projection onto octahedron. */
+ cubevec /= dot(vec3(1.0), abs(cubevec));
+ /* Out-folding of the downward faces. */
+ if (cubevec.z < 0.0) {
+ vec2 cubevec_sign = step(0.0, cubevec.xy) * 2.0 - 1.0;
+ cubevec.xy = (1.0 - abs(cubevec.yx)) * cubevec_sign;
+ }
+ /* Mapping to [0;1]ˆ2 texture space. */
+ vec2 uvs = cubevec.xy * (0.5) + 0.5;
+ /* Edge filtering fix. */
+ uvs = (1.0 - 2.0 * texel_size) * uvs + texel_size;
+ return uvs;
+}
+
+/* Returns the cell weight using the visibility data and a smooth test. */
+float visibility_load_cell(GridInfoData info,
+ sampler2DArray irradiance_tx,
+ int cell,
+ vec3 L,
+ float dist,
+ float bias,
+ float bleed_bias,
+ float range)
+{
+ /* Keep in sync with diffuse_filter_probe(). */
+ ivec2 cell_co = ivec2(info.visibility_size);
+ cell_co.x *= (cell % info.visibility_cells_per_row);
+ cell_co.y *= (cell % info.visibility_cells_per_layer) / info.visibility_cells_per_row;
+ float layer = 1.0 + float((cell / info.visibility_cells_per_layer));
+
+ vec2 texel_size = 1.0 / vec2(textureSize(irradiance_tx, 0).xy);
+ vec2 co = vec2(cell_co) * texel_size;
+
+ vec2 uv = visibility_mapping_octahedron(-L, vec2(1.0 / float(info.visibility_size)));
+ uv *= vec2(info.visibility_size) * texel_size;
+
+ vec4 data = texture(irradiance_tx, vec3(co + uv, layer));
+
+ /* Decoding compressed data. */
+ vec2 moments = visibility_decode(data, range);
+ /* Doing chebishev test. */
+ float variance = abs(moments.x * moments.x - moments.y);
+ variance = max(variance, bias / 10.0);
+
+ float d = dist - moments.x;
+ float p_max = variance / (variance + d * d);
+
+ /* Increase contrast in the weight by squaring it */
+ p_max *= p_max;
+ /* Now reduce light-bleeding by removing the [0, x] tail and linearly rescaling (x, 1] */
+ p_max = clamp((p_max - bleed_bias) / (1.0 - bleed_bias), 0.0, 1.0);
+
+ return (dist <= moments.x) ? 1.0 : p_max;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Irradiance
+ *
+ * Using HalfLife2 ambient cube. We encode data using RGBE compression.
+ * https://cdn.cloudflare.steamstatic.com/apps/valve/2006/SIGGRAPH06_Course_ShadingInValvesSourceEngine.pdf
+ * \{ */
+
+vec4 irradiance_encode(vec3 rgb)
+{
+ float maxRGB = max_v3(rgb);
+ float fexp = ceil(log2(maxRGB));
+ return vec4(rgb / exp2(fexp), (fexp + 128.0) / 255.0);
+}
+
+vec3 irradiance_decode(vec4 data)
+{
+ float fexp = data.a * 255.0 - 128.0;
+ return data.rgb * exp2(fexp);
+}
+
+/* Samples an irradiance grid cell in the given direction. */
+vec3 irradiance_load_cell(GridInfoData info, sampler2DArray irradiance_tx, int cell, vec3 N)
+{
+ ivec2 cell_co = ivec2(3, 2);
+ cell_co.x *= cell % info.irradiance_cells_per_row;
+ cell_co.y *= cell / info.irradiance_cells_per_row;
+
+ ivec3 is_negative = ivec3(step(0.0, -N));
+
+ /* Listing 1. */
+ vec3 n_squared = N * N;
+ vec3 irradiance;
+ vec4 data;
+ data = texelFetch(irradiance_tx, ivec3(cell_co + ivec2(0, is_negative.x), 0), 0);
+ irradiance = n_squared.x * irradiance_decode(data);
+
+ data = texelFetch(irradiance_tx, ivec3(cell_co + ivec2(1, is_negative.y), 0), 0);
+ irradiance += n_squared.y * irradiance_decode(data);
+
+ data = texelFetch(irradiance_tx, ivec3(cell_co + ivec2(2, is_negative.z), 0), 0);
+ irradiance += n_squared.z * irradiance_decode(data);
+ return irradiance;
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_light_eval_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_light_eval_lib.glsl
new file mode 100644
index 00000000000..433688b70c4
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_light_eval_lib.glsl
@@ -0,0 +1,103 @@
+
+/**
+ * This is an eval function that needs to be added after main fragment shader.
+ * A prototype needs to be declared before main in order to use it.
+ *
+ * The resources expected to be defined are:
+ * - lights
+ * - lights_zbins
+ * - light_culling
+ * - lights_culling_words
+ * - shadow_atlas_tx
+ * - shadow_tilemaps_tx
+ * - sss_transmittance_tx
+ * - utility_tx
+ *
+ * All of this is needed to avoid using macros and performance issues with large
+ * arrays as function arguments.
+ */
+
+#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
+
+/* TODO(fclem): We could reduce register pressure by only having static branches for sun lights. */
+void light_eval_ex(ClosureDiffuse diffuse,
+ ClosureReflection reflection,
+ vec3 P,
+ vec3 V,
+ float vP_z,
+ float thickness,
+ vec4 ltc_mat,
+ uint l_idx,
+ inout vec3 out_diffuse,
+ inout vec3 out_specular)
+{
+ LightData light = lights[l_idx];
+ vec3 L;
+ float dist;
+ light_vector_get(light, P, L, dist);
+
+ float visibility = light_attenuation(light, L, dist);
+
+ if ((light.shadow_id != LIGHT_NO_SHADOW) && (visibility > 0.0)) {
+ vec3 lL = light_world_to_local(light, -L) * dist;
+
+ float shadow_delta = shadow_delta_get(
+ shadow_atlas_tx, shadow_tilemaps_tx, light, light.shadow_data, lL, dist, P);
+
+ /* Transmittance evaluation first to use initial visibility. */
+ if (diffuse.sss_id != 0u && light.diffuse_power > 0.0) {
+ float delta = max(thickness, shadow_delta);
+
+ vec3 intensity =
+ visibility * light.transmit_power *
+ light_translucent(
+ sss_transmittance_tx, light, diffuse.N, L, dist, diffuse.sss_radius, delta);
+ out_diffuse += light.color * intensity;
+ }
+
+ visibility *= float(shadow_delta - light.shadow_data.bias <= 0.0);
+ }
+
+ if (visibility < 1e-6) {
+ return;
+ }
+
+ if (light.diffuse_power > 0.0) {
+ float intensity = visibility * light.diffuse_power *
+ light_diffuse(utility_tx, light, diffuse.N, V, L, dist);
+ out_diffuse += light.color * intensity;
+ }
+
+ if (light.specular_power > 0.0) {
+ float intensity = visibility * light.specular_power *
+ light_ltc(utility_tx, light, reflection.N, V, L, dist, ltc_mat);
+ out_specular += light.color * intensity;
+ }
+}
+
+void light_eval(ClosureDiffuse diffuse,
+ ClosureReflection reflection,
+ vec3 P,
+ vec3 V,
+ float vP_z,
+ float thickness,
+ inout vec3 out_diffuse,
+ inout vec3 out_specular)
+{
+ vec2 uv = vec2(reflection.roughness, safe_sqrt(1.0 - dot(reflection.N, V)));
+ uv = uv * UTIL_TEX_UV_SCALE + UTIL_TEX_UV_BIAS;
+ vec4 ltc_mat = utility_tx_sample(uv, UTIL_LTC_MAT_LAYER);
+
+ LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_culling, l_idx) {
+ light_eval_ex(
+ diffuse, reflection, P, V, vP_z, thickness, ltc_mat, l_idx, out_diffuse, out_specular);
+ }
+ LIGHT_FOREACH_END
+
+ LIGHT_FOREACH_BEGIN_LOCAL (
+ light_culling, lights_zbins, lights_culling_words, gl_FragCoord.xy, vP_z, l_idx) {
+ light_eval_ex(
+ diffuse, reflection, P, V, vP_z, thickness, ltc_mat, l_idx, out_diffuse, out_specular);
+ }
+ LIGHT_FOREACH_END
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_light_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_light_lib.glsl
new file mode 100644
index 00000000000..a288224a2e3
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_light_lib.glsl
@@ -0,0 +1,197 @@
+
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_ltc_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_culling_iter_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+/* ---------------------------------------------------------------------- */
+/** \name Light Functions
+ * \{ */
+
+void light_vector_get(LightData ld, vec3 P, out vec3 L, out float dist)
+{
+ if (ld.type == LIGHT_SUN) {
+ L = ld._back;
+ dist = 1.0;
+ }
+ else {
+ L = ld._position - P;
+ dist = inversesqrt(len_squared(L));
+ L *= dist;
+ dist = 1.0 / dist;
+ }
+}
+
+/* Rotate vector to light's local space. Does not translate. */
+vec3 light_world_to_local(LightData ld, vec3 L)
+{
+ /* Avoid relying on compiler to optimize this.
+ * vec3 lL = transpose(mat3(ld.object_mat)) * L; */
+ vec3 lL;
+ lL.x = dot(ld.object_mat[0].xyz, L);
+ lL.y = dot(ld.object_mat[1].xyz, L);
+ lL.z = dot(ld.object_mat[2].xyz, L);
+ return lL;
+}
+
+/* From Frostbite PBR Course
+ * Distance based attenuation
+ * http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf */
+float light_influence_attenuation(float dist, float inv_sqr_influence)
+{
+ float factor = sqr(dist) * inv_sqr_influence;
+ float fac = saturate(1.0 - sqr(factor));
+ return sqr(fac);
+}
+
+float light_spot_attenuation(LightData ld, vec3 L)
+{
+ vec3 lL = light_world_to_local(ld, L);
+ float ellipse = inversesqrt(1.0 + len_squared(lL.xy * ld.spot_size_inv / lL.z));
+ float spotmask = smoothstep(0.0, 1.0, ellipse * ld._spot_mul + ld._spot_bias);
+ return spotmask;
+}
+
+float light_attenuation(LightData ld, vec3 L, float dist)
+{
+ float vis = 1.0;
+ if (ld.type == LIGHT_SPOT) {
+ vis *= light_spot_attenuation(ld, L);
+ }
+ if (ld.type >= LIGHT_SPOT) {
+ vis *= step(0.0, -dot(L, -ld._back));
+ }
+ if (ld.type != LIGHT_SUN) {
+#ifdef VOLUME_LIGHTING
+ vis *= light_influence_attenuation(dist, ld.influence_radius_invsqr_volume);
+#else
+ vis *= light_influence_attenuation(dist, ld.influence_radius_invsqr_surface);
+#endif
+ }
+ return vis;
+}
+
+/* Cheaper alternative than evaluating the LTC.
+ * The result needs to be multiplied by BSDF or Phase Function. */
+float light_point_light(LightData ld, vec3 L, float dist)
+{
+ if (ld.type == LIGHT_SUN) {
+ return 1.0;
+ }
+ /**
+ * Using "Point Light Attenuation Without Singularity" from Cem Yuksel
+ * http://www.cemyuksel.com/research/pointlightattenuation/pointlightattenuation.pdf
+ * http://www.cemyuksel.com/research/pointlightattenuation/
+ **/
+ float d_sqr = sqr(dist);
+ float r_sqr = ld.radius_squared;
+ /* Using reformulation that has better numerical percision. */
+ float power = 2.0 / (d_sqr + r_sqr + dist * sqrt(d_sqr + r_sqr));
+
+ if (is_area_light(ld.type)) {
+ /* Modulate by light plane orientation / solid angle. */
+ power *= saturate(dot(ld._back, L));
+ }
+ return power;
+}
+
+float light_diffuse(sampler2DArray utility_tx, LightData ld, vec3 N, vec3 V, vec3 L, float dist)
+{
+ if (!is_area_light(ld.type)) {
+ float radius = ld._radius / dist;
+ return ltc_evaluate_disk_simple(utility_tx, radius, dot(N, L));
+ }
+ else if (ld.type == LIGHT_RECT) {
+ vec3 corners[4];
+ corners[0] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y;
+ corners[1] = ld._right * ld._area_size_x + ld._up * ld._area_size_y;
+ corners[2] = -corners[0];
+ corners[3] = -corners[1];
+
+ corners[0] = normalize(L * dist + corners[0]);
+ corners[1] = normalize(L * dist + corners[1]);
+ corners[2] = normalize(L * dist + corners[2]);
+ corners[3] = normalize(L * dist + corners[3]);
+
+ return ltc_evaluate_quad(utility_tx, corners, N);
+ }
+ else /* (ld.type == LIGHT_ELLIPSE) */ {
+ vec3 points[3];
+ points[0] = ld._right * -ld._area_size_x + ld._up * -ld._area_size_y;
+ points[1] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y;
+ points[2] = -points[0];
+
+ points[0] += L * dist;
+ points[1] += L * dist;
+ points[2] += L * dist;
+
+ return ltc_evaluate_disk(utility_tx, N, V, mat3(1.0), points);
+ }
+}
+
+float light_ltc(
+ sampler2DArray utility_tx, LightData ld, vec3 N, vec3 V, vec3 L, float dist, vec4 ltc_mat)
+{
+ if (ld.type == LIGHT_RECT) {
+ vec3 corners[4];
+ corners[0] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y;
+ corners[1] = ld._right * ld._area_size_x + ld._up * ld._area_size_y;
+ corners[2] = -corners[0];
+ corners[3] = -corners[1];
+
+ corners[0] += L * dist;
+ corners[1] += L * dist;
+ corners[2] += L * dist;
+ corners[3] += L * dist;
+
+ ltc_transform_quad(N, V, ltc_matrix(ltc_mat), corners);
+
+ return ltc_evaluate_quad(utility_tx, corners, vec3(0.0, 0.0, 1.0));
+ }
+ else {
+ vec3 Px = ld._right;
+ vec3 Py = ld._up;
+
+ if (!is_area_light(ld.type)) {
+ make_orthonormal_basis(L, Px, Py);
+ }
+
+ vec3 points[3];
+ points[0] = Px * -ld._area_size_x + Py * -ld._area_size_y;
+ points[1] = Px * ld._area_size_x + Py * -ld._area_size_y;
+ points[2] = -points[0];
+
+ points[0] += L * dist;
+ points[1] += L * dist;
+ points[2] += L * dist;
+
+ return ltc_evaluate_disk(utility_tx, N, V, ltc_matrix(ltc_mat), points);
+ }
+}
+
+vec3 light_translucent(sampler1D transmittance_tx,
+ LightData ld,
+ vec3 N,
+ vec3 L,
+ float dist,
+ vec3 sss_radius,
+ float delta)
+{
+ /* TODO(fclem): We should compute the power at the entry point. */
+ /* NOTE(fclem): we compute the light attenuation using the light vector but the transmittance
+ * using the shadow depth delta. */
+ float power = light_point_light(ld, L, dist);
+ /* Do not add more energy on front faces. Also apply lambertian BSDF. */
+ power *= max(0.0, dot(-N, L)) * M_1_PI;
+
+ sss_radius *= SSS_TRANSMIT_LUT_RADIUS;
+ vec3 channels_co = saturate(delta / sss_radius) * SSS_TRANSMIT_LUT_SCALE + SSS_TRANSMIT_LUT_BIAS;
+
+ vec3 translucency;
+ translucency.x = (sss_radius.x > 0.0) ? texture(transmittance_tx, channels_co.x).r : 0.0;
+ translucency.y = (sss_radius.y > 0.0) ? texture(transmittance_tx, channels_co.y).r : 0.0;
+ translucency.z = (sss_radius.z > 0.0) ? texture(transmittance_tx, channels_co.z).r : 0.0;
+ return translucency * power;
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_cubemap_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_cubemap_frag.glsl
new file mode 100644
index 00000000000..e19063d562a
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_cubemap_frag.glsl
@@ -0,0 +1,26 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_cubemap_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_lightprobe_display_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+uniform samplerCubeArray lightprobe_cube_tx;
+
+layout(location = 0) out vec4 out_color;
+
+void main()
+{
+ float len_sqr = len_squared(interp.coord);
+ if (len_sqr > 1.0) {
+ discard;
+ }
+
+ vec3 vN = vec3(interp.coord, sqrt(1.0 - len_sqr));
+ vec3 N = normal_view_to_world(vN);
+
+ vec3 V = cameraVec(interp.P);
+ vec3 R = -reflect(V, N);
+
+ out_color.rgb = cubemap_array_sample(lightprobe_cube_tx, vec4(R, interp.sample), 0.0).rgb;
+ out_color.a = 0.0;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_cubemap_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_cubemap_vert.glsl
new file mode 100644
index 00000000000..a1e8232908f
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_cubemap_vert.glsl
@@ -0,0 +1,41 @@
+
+/**
+ * Generate camera-facing quad procedurally for each reflection cubemap of the lightcache.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_lightprobe_display_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(std140) uniform cubes_block
+{
+ CubemapData cubes[CULLING_ITEM_BATCH];
+};
+
+layout(std140) uniform lightprobes_info_block
+{
+ LightProbeInfoData probes_info;
+};
+
+const vec2 pos[6] = vec2[6](vec2(-1.0, -1.0),
+ vec2(1.0, -1.0),
+ vec2(-1.0, 1.0),
+
+ vec2(1.0, -1.0),
+ vec2(1.0, 1.0),
+ vec2(-1.0, 1.0));
+
+void main(void)
+{
+ interp.sample = 1 + (gl_VertexID / 6);
+ interp.coord = pos[gl_VertexID % 6];
+
+ CubemapData cube = cubes[interp.sample];
+
+ vec3 quad = vec3(interp.coord * probes_info.cubes.display_size * 0.5, 0.0);
+
+ interp.P = vec3(cube._world_position_x, cube._world_position_y, cube._world_position_z);
+ interp.P += transform_direction(ViewMatrixInverse, quad);
+
+ gl_Position = point_world_to_ndc(interp.P);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_grid_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_grid_frag.glsl
new file mode 100644
index 00000000000..fd2322c9acd
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_grid_frag.glsl
@@ -0,0 +1,29 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_irradiance_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_lightprobe_display_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(std140) uniform lightprobes_info_block
+{
+ LightProbeInfoData probes_info;
+};
+
+uniform sampler2DArray lightprobe_grid_tx;
+uniform int grid_id;
+
+layout(location = 0) out vec4 out_color;
+
+void main()
+{
+ float len_sqr = len_squared(interp.coord);
+ if (len_sqr > 1.0) {
+ discard;
+ }
+
+ vec3 vN = vec3(interp.coord, sqrt(1.0 - len_sqr));
+ vec3 N = normal_view_to_world(vN);
+
+ out_color.rgb = irradiance_load_cell(probes_info.grids, lightprobe_grid_tx, interp.sample, N);
+ out_color.a = 0.0;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_grid_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_grid_vert.glsl
new file mode 100644
index 00000000000..a04c280526e
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_grid_vert.glsl
@@ -0,0 +1,53 @@
+
+/**
+ * Generate camera-facing quad procedurally for each irradiance sample of the lightcache.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_lightprobe_display_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(std140) uniform grids_block
+{
+ GridData grids[GRID_MAX];
+};
+
+layout(std140) uniform lightprobes_info_block
+{
+ LightProbeInfoData probes_info;
+};
+
+uniform int grid_id;
+
+const vec2 pos[6] = vec2[6](vec2(-1.0, -1.0),
+ vec2(1.0, -1.0),
+ vec2(-1.0, 1.0),
+
+ vec2(1.0, -1.0),
+ vec2(1.0, 1.0),
+ vec2(-1.0, 1.0));
+
+void main(void)
+{
+
+ interp.sample = gl_VertexID / 6;
+ interp.coord = pos[gl_VertexID % 6];
+
+ GridData grid = grids[grid_id];
+
+ ivec3 cell_coord = grid_cell_index_to_coordinate(interp.sample, grid.resolution);
+
+ interp.sample += grid.offset;
+
+ mat4 cell_to_world = mat4(vec4(grid.increment_x, 0.0),
+ vec4(grid.increment_y, 0.0),
+ vec4(grid.increment_z, 0.0),
+ vec4(grid.corner, 1.0));
+
+ vec3 quad = vec3(interp.coord * probes_info.grids.display_size * 0.5, 0.0);
+
+ interp.P = transform_point(cell_to_world, vec3(cell_coord));
+ interp.P += transform_direction(ViewMatrixInverse, quad);
+
+ gl_Position = point_world_to_ndc(interp.P);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_lib.glsl
new file mode 100644
index 00000000000..9999ee45794
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_lib.glsl
@@ -0,0 +1,8 @@
+
+IN_OUT LightProbeDisplayInterface
+{
+ vec3 P;
+ vec2 coord;
+ flat int sample;
+}
+interp;
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_eval_cubemap_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_eval_cubemap_lib.glsl
new file mode 100644
index 00000000000..16f5004308b
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_eval_cubemap_lib.glsl
@@ -0,0 +1,89 @@
+
+/**
+ * This is an eval function that needs to be added after main fragment shader.
+ * A prototype needs to be declared before main in order to use it.
+ *
+ * The resources expected to be defined are:
+ * - probes_info
+ * - lightprobe_cube_tx
+ * - cubes
+ *
+ * All of this is needed to avoid using macros and performance issues with large
+ * arrays as function arguments.
+ */
+
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_cubemap_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+float lightprobe_cubemap_weight(CubemapData cube, vec3 P)
+{
+ /* Composed transform to remove the packed data. */
+ vec3 lP = transform_direction(cube.influence_mat, P) + cube.influence_mat[3].xyz;
+ float attenuation;
+ if (cube._attenuation_type == CUBEMAP_SHAPE_SPHERE) {
+ attenuation = saturate(cube._attenuation_factor * (1.0 - length(lP)));
+ }
+ else {
+ attenuation = min_v3(saturate(cube._attenuation_factor * (1.0 - abs(lP))));
+ }
+ return attenuation;
+}
+
+vec3 lightprobe_cubemap_evaluate(CubemapInfoData info,
+ samplerCubeArray cubemap_tx,
+ CubemapData cube,
+ vec3 P,
+ vec3 R,
+ float roughness)
+{
+ float linear_roughness = fast_sqrt(roughness);
+ if (cube._layer > 0.0) {
+ /* Correct reflection ray using parallax volume intersection. */
+ vec3 lR = transform_direction(cube.parallax_mat, R);
+ /* Composed transform to remove the packed data. */
+ vec3 lP = transform_direction(cube.parallax_mat, P) + cube.parallax_mat[3].xyz;
+ float dist;
+ if (cube._parallax_type == CUBEMAP_SHAPE_SPHERE) {
+ dist = line_unit_sphere_intersect_dist(lP, lR);
+ }
+ else {
+ dist = line_unit_box_intersect_dist(lP, lR);
+ }
+ vec3 cube_pos = vec3(cube._world_position_x, cube._world_position_y, cube._world_position_z);
+ /* Use Distance in WS directly to recover intersection. */
+ vec3 intersection = (P + R * dist) - cube_pos;
+ /* Distance based roughness from Frostbite PBR Course.
+ * http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf
+ */
+ float original_roughness = roughness;
+ float distance_roughness = saturate(dist * linear_roughness / length(intersection));
+ linear_roughness = mix(distance_roughness, linear_roughness, linear_roughness);
+ roughness = linear_roughness * linear_roughness;
+
+ float fac = saturate(original_roughness * 2.0 - 1.0);
+ R = mix(intersection, R, fac * fac);
+ }
+ else {
+ R = transform_direction(info.lookdev_rotation, R);
+ }
+ float lod = linear_roughness * info.roughness_max_lod;
+ return cubemap_array_sample(cubemap_tx, vec4(R, cube._layer), lod).rgb;
+}
+
+vec3 lightprobe_cubemap_eval(vec3 P, vec3 R, float roughness, float random_threshold)
+{
+ /* Go through all cubemaps, computing and adding their weights for this pixel
+ * until reaching a random threshold. */
+ float weight = 0.0;
+ int cube_index = probes_info.cubes.cube_count - 1;
+ for (; cube_index > 0; cube_index--) {
+ weight += lightprobe_cubemap_weight(cubes[cube_index], P);
+ if (weight >= random_threshold) {
+ break;
+ }
+ }
+
+ return lightprobe_cubemap_evaluate(
+ probes_info.cubes, lightprobe_cube_tx, cubes[cube_index], P, R, roughness);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_eval_grid_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_eval_grid_lib.glsl
new file mode 100644
index 00000000000..a66179979d4
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_eval_grid_lib.glsl
@@ -0,0 +1,105 @@
+
+/**
+ * This is an eval function that needs to be added after main fragment shader.
+ * A prototype needs to be declared before main in order to use it.
+ *
+ * The resources expected to be defined are:
+ * - probes_info
+ * - lightprobe_grid_tx
+ * - grids
+ *
+ * All of this is needed to avoid using macros and performance issues with large
+ * arrays as function arguments.
+ */
+
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_irradiance_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+float lightprobe_grid_weight(GridData grid, vec3 P)
+{
+ vec3 lP = transform_point(grid.local_mat, P);
+ vec3 pos_to_edge = max(vec3(0.0), abs(lP) - 1.0);
+ float fade = length(pos_to_edge);
+ return saturate(-fade * grid.attenuation_scale + grid.attenuation_bias);
+}
+
+vec3 lightprobe_grid_evaluate(
+ GridInfoData info, sampler2DArray grid_tx, GridData grid, vec3 P, vec3 N)
+{
+ vec3 lP = transform_point(grid.local_mat, P) * 0.5 + 0.5;
+ lP = lP * vec3(grid.resolution) - 0.5;
+
+ ivec3 lP_floored = ivec3(floor(lP));
+ vec3 trilinear_weight = fract(lP);
+
+ if (grid.offset == 0) {
+ N = transform_direction(info.lookdev_rotation, N);
+ }
+
+ float weight_accum = 0.0;
+ vec3 irradiance_accum = vec3(0.0);
+ /* For each neighbor cells */
+ for (int i = 0; i < 8; i++) {
+ ivec3 offset = ivec3(i, i >> 1, i >> 2) & ivec3(1);
+ ivec3 cell_coord = clamp(lP_floored + offset, ivec3(0), grid.resolution - 1);
+ /* Skip cells not yet rendered during baking. */
+ cell_coord = (cell_coord / grid.level_skip) * grid.level_skip;
+ /* Keep in sync with update_irradiance_probe. */
+ int cell_id = grid.offset + cell_coord.z + cell_coord.y * grid.resolution.z +
+ cell_coord.x * grid.resolution.z * grid.resolution.y;
+
+ vec3 color = irradiance_load_cell(info, grid_tx, cell_id, N);
+
+ /* We need this because we render probes in world space (so we need light vector in WS).
+ * And rendering them in local probe space is too much problem. */
+ mat4 cell_to_world = mat4(vec4(grid.increment_x, 0.0),
+ vec4(grid.increment_y, 0.0),
+ vec4(grid.increment_z, 0.0),
+ vec4(grid.corner, 1.0));
+ vec3 ws_cell_location = transform_point(cell_to_world, vec3(cell_coord));
+
+ vec3 ws_point_to_cell = ws_cell_location - P;
+ float ws_dist_point_to_cell = length(ws_point_to_cell);
+ vec3 ws_light = ws_point_to_cell / ws_dist_point_to_cell;
+
+ /* Smooth backface test. */
+ float weight = saturate(dot(ws_light, N));
+ /* Precomputed visibility. */
+ weight *= visibility_load_cell(info,
+ grid_tx,
+ cell_id,
+ ws_light,
+ ws_dist_point_to_cell,
+ grid.visibility_bias,
+ grid.visibility_bleed,
+ grid.visibility_range);
+ /* Smoother transition. */
+ weight += info.irradiance_smooth;
+ /* Trilinear weights. */
+ vec3 trilinear = mix(1.0 - trilinear_weight, trilinear_weight, offset);
+ weight *= trilinear.x * trilinear.y * trilinear.z;
+ /* Avoid zero weight. */
+ weight = max(0.00001, weight);
+
+ weight_accum += weight;
+ irradiance_accum += color * weight;
+ }
+ return irradiance_accum / weight_accum;
+}
+
+vec3 lightprobe_grid_eval(vec3 P, vec3 N, float random_threshold)
+{
+ /* Go through all grids, computing and adding their weights for this pixel
+ * until reaching a random threshold. */
+ float weight = 0.0;
+ int grid_index = probes_info.grids.grid_count - 1;
+ for (; grid_index > 0; grid_index--) {
+ weight += lightprobe_grid_weight(grids[grid_index], P);
+ if (weight >= random_threshold) {
+ break;
+ }
+ }
+
+ return lightprobe_grid_evaluate(probes_info.grids, lightprobe_grid_tx, grids[grid_index], P, N);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_diffuse_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_diffuse_frag.glsl
new file mode 100644
index 00000000000..2c044378ea6
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_diffuse_frag.glsl
@@ -0,0 +1,73 @@
+
+/**
+ * Outputs pre-convolved diffuse irradiance from an input radiance cubemap.
+ * Input radiance has its mipmaps updated so we can use filtered importance sampling.
+ * The output is a really small 6 directional basis similar to the ambient cube technique.
+ * Downside: very very low resolution (6 texels), bleed lighting because of interpolation.
+ *
+ * https://cdn.cloudflare.steamstatic.com/apps/valve/2006/SIGGRAPH06_Course_ShadingInValvesSourceEngine.pdf
+ */
+
+#pragma BLENDER_REQUIRE(eevee_irradiance_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_bsdf_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_lightprobe_filter_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+uniform samplerCube radiance_tx;
+
+layout(std140) uniform filter_block
+{
+ LightProbeFilterData probe;
+};
+
+layout(location = 0) out vec4 out_irradiance;
+
+void main()
+{
+ int x = int(gl_FragCoord.x) % 3;
+ int y = int(gl_FragCoord.y) % 2;
+
+ vec3 N, T, B;
+ switch (x) {
+ default:
+ N = vec3(1.0, 0.0, 0.0);
+ break;
+ case 1:
+ N = vec3(0.0, 1.0, 0.0);
+ break;
+ case 2:
+ N = vec3(0.0, 0.0, 1.0);
+ break;
+ }
+
+ if (y == 1) {
+ N = -N;
+ }
+
+ make_orthonormal_basis(N, T, B);
+
+ /* Integrating Envmap. */
+ float weight = 0.0;
+ vec3 radiance = vec3(0.0);
+ for (float i = 0; i < probe.sample_count; i++) {
+ vec3 Xi = sample_cylinder(hammersley_2d(i, probe.sample_count));
+
+ float pdf;
+ vec3 L = sample_uniform_hemisphere(Xi, N, T, B, pdf);
+ float NL = dot(N, L);
+
+ if (NL > 0.0) {
+ /* Coarse Approximation of the mapping distortion.
+ * Unit Sphere -> Cubemap Face. */
+ const float dist = 4.0 * M_PI / 6.0;
+ /* http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html : Equation 13 */
+ float lod = probe.lod_bias - 0.5 * log2(pdf * dist);
+
+ radiance += textureLod(radiance_tx, L, lod).rgb * NL;
+ weight += NL;
+ }
+ }
+
+ out_irradiance = irradiance_encode(probe.instensity_fac * radiance / weight);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_downsample_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_downsample_frag.glsl
new file mode 100644
index 00000000000..6db16afdbc2
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_downsample_frag.glsl
@@ -0,0 +1,20 @@
+
+/**
+ * Fullscreen pass that filter previous mipmap level using a 1 bilinear tap.
+ * This uses layered rendering to filter all cubeface / layers in one drawcall.
+ */
+
+#pragma BLENDER_REQUIRE(eevee_lightprobe_filter_lib.glsl)
+
+#ifdef CUBEMAP
+uniform samplerCube input_tx;
+#else
+uniform sampler2DArray input_tx;
+#endif
+
+layout(location = 0) out vec4 out_color;
+
+void main(void)
+{
+ out_color = textureLod(input_tx, interp.coord, 0.0);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_geom.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_geom.glsl
new file mode 100644
index 00000000000..bc4d4624551
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_geom.glsl
@@ -0,0 +1,42 @@
+
+/**
+ * Passthrough geometry shader to use layered rendering..
+ */
+
+/* TODO(fclem) Use vendor extensions to bypass geometry shader. */
+
+layout(triangles) in;
+layout(triangle_strip, max_vertices = 3) out;
+
+in FilterInterface
+{
+ vec3 coord;
+ flat int layer;
+}
+interp_in[];
+
+out FilterInterface
+{
+ vec3 coord;
+ flat int layer;
+}
+interp_out;
+
+void main()
+{
+ gl_Layer = interp_in[0].layer;
+
+ interp_out.coord = interp_in[0].coord;
+ gl_Position = gl_in[0].gl_Position;
+ EmitVertex();
+
+ interp_out.coord = interp_in[1].coord;
+ gl_Position = gl_in[1].gl_Position;
+ EmitVertex();
+
+ interp_out.coord = interp_in[2].coord;
+ gl_Position = gl_in[2].gl_Position;
+ EmitVertex();
+
+ EndPrimitive();
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_glossy_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_glossy_frag.glsl
new file mode 100644
index 00000000000..7ad81605141
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_glossy_frag.glsl
@@ -0,0 +1,67 @@
+
+/**
+ * Outputs pre-convolved glossy BSDF irradiance from an input radiance cubemap.
+ * Input radiance has its mipmaps updated so we can use filtered importance sampling.
+ *
+ * Follows the principle of:
+ * https://developer.nvidia.com/gpugems/gpugems3/part-iii-rendering/chapter-20-gpu-based-importance-sampling
+ */
+
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_bsdf_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_lightprobe_filter_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+uniform samplerCube radiance_tx;
+
+layout(std140) uniform filter_block
+{
+ LightProbeFilterData probe;
+};
+
+layout(location = 0) out vec4 out_irradiance;
+
+void main()
+{
+ vec3 N, T, B, V;
+ vec3 R = normalize(interp.coord);
+
+ /* Isotropic assumption. */
+ N = V = R;
+
+ make_orthonormal_basis(N, T, B);
+
+ /* Integrating Envmap. */
+ float weight = 0.0;
+ vec3 radiance = vec3(0.0);
+ for (float i = 0; i < probe.sample_count; i++) {
+ vec3 Xi = sample_cylinder(hammersley_2d(i, probe.sample_count));
+
+ float pdf;
+ vec3 L = sample_ggx_reflect(Xi, probe.roughness, V, N, T, B, pdf);
+
+ if (pdf > 0.0) {
+ /* Microfacet normal. */
+ vec3 H = normalize(V + L);
+ float NL = dot(N, L);
+ float NH = max(1e-8, dot(N, H));
+
+ /* Coarse Approximation of the mapping distortion.
+ * Unit Sphere -> Cubemap Face. */
+ const float dist = 4.0 * M_PI / 6.0;
+ /* Equation 13. */
+ float lod = probe.lod_bias - 0.5 * log2(pdf * dist);
+
+ vec3 l_col = textureLod(radiance_tx, L, lod).rgb;
+
+ /* Clamped brightness. */
+ float luma = max(1e-8, max_v3(l_col));
+ l_col *= 1.0 - max(0.0, luma - probe.luma_max) / luma;
+
+ radiance += l_col * NL;
+ weight += NL;
+ }
+ }
+
+ out_irradiance = vec4(probe.instensity_fac * radiance / weight, 1.0);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_lib.glsl
new file mode 100644
index 00000000000..80335bcdbc6
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_lib.glsl
@@ -0,0 +1,7 @@
+
+IN_OUT FilterInterface
+{
+ vec3 coord;
+ flat int layer;
+}
+interp;
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_vert.glsl
new file mode 100644
index 00000000000..9ea8e66e2a7
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_vert.glsl
@@ -0,0 +1,49 @@
+
+/**
+ * Fullscreen pass that outputs one triangle to a specific layer.
+ * This uses layered rendering to filter all cubeface / layers in one drawcall.
+ */
+
+#pragma BLENDER_REQUIRE(eevee_lightprobe_filter_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(std140) uniform filter_block
+{
+ LightProbeFilterData probe;
+};
+
+void main(void)
+{
+ /* Fullscreen triangle. */
+ int v = gl_VertexID % 3;
+ interp.coord.x = float((v & 1) << 2);
+ interp.coord.y = float((v & 2) << 1);
+ gl_Position = vec4(interp.coord.xy * 2.0 - 1.0, 1.0, 1.0);
+
+ int cube_face = gl_VertexID / 3;
+ interp.layer = probe.target_layer + cube_face;
+ interp.coord.z = float(interp.layer);
+
+#ifdef CUBEMAP
+ switch (cube_face) {
+ case 0:
+ interp.coord = gl_Position.zyx * vec3(1, -1, -1);
+ break;
+ case 1:
+ interp.coord = gl_Position.zyx * vec3(-1, -1, 1);
+ break;
+ case 2:
+ interp.coord = gl_Position.xzy * vec3(1, 1, 1);
+ break;
+ case 3:
+ interp.coord = gl_Position.xzy * vec3(1, -1, -1);
+ break;
+ case 4:
+ interp.coord = gl_Position.xyz * vec3(1, -1, 1);
+ break;
+ default:
+ interp.coord = gl_Position.xyz * vec3(-1, -1, -1);
+ break;
+ }
+#endif
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_visibility_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_visibility_frag.glsl
new file mode 100644
index 00000000000..0fe784baeaf
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_visibility_frag.glsl
@@ -0,0 +1,78 @@
+
+/**
+ * Outputs convolved visibility from an input depth cubemap.
+ * The output is an octahedral map which encodes depth in 4 components of 1 byte each.
+ */
+
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_bsdf_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_irradiance_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_lightprobe_filter_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+uniform samplerCube depth_tx;
+
+layout(std140) uniform filter_block
+{
+ LightProbeFilterData probe;
+};
+
+layout(location = 0) out vec4 out_irradiance;
+
+vec3 octahedral_to_cubemap_proj(vec2 co)
+{
+ co = co * 2.0 - 1.0;
+
+ vec2 abs_co = abs(co);
+ vec3 v = vec3(co, 1.0 - (abs_co.x + abs_co.y));
+
+ if (abs_co.x + abs_co.y > 1.0) {
+ v.xy = (abs(co.yx) - 1.0) * -sign(co.xy);
+ }
+
+ return v;
+}
+
+float get_world_distance(float depth, vec3 coords)
+{
+ float is_background = step(1.0, depth);
+ depth = get_view_z_from_depth(depth);
+ depth += 1e1 * is_background;
+ coords = normalize(abs(coords));
+ float cos_vec = max_v3(coords);
+ return depth / cos_vec;
+}
+
+void main()
+{
+ vec2 uv = interp.coord.xy;
+ vec2 stored_texel_size = dFdx(uv);
+ /* Add a 1 pixel border all around the octahedral map to ensure filtering is correct. */
+ uv = (uv - stored_texel_size) / (1.0 - 2.0 * stored_texel_size);
+ /* Edge mirroring : only mirror if directly adjacent (not diagonally adjacent). */
+ vec2 m = abs(uv - 0.5) + 0.5;
+ vec2 f = floor(m);
+ if (f.x - f.y != 0.0) {
+ uv = 1.0 - uv;
+ }
+ uv = fract(uv);
+
+ vec3 T, B, N;
+ N = normalize(octahedral_to_cubemap_proj(uv));
+ make_orthonormal_basis(N, T, B);
+
+ vec2 accum = vec2(0.0);
+
+ for (float i = 0; i < probe.sample_count; i++) {
+ vec3 Xi = sample_cylinder(hammersley_2d(i, probe.sample_count));
+
+ vec3 dir = sample_uniform_cone(Xi, M_PI_2 * probe.visibility_blur, N, T, B);
+ float depth = texture(depth_tx, dir).r;
+ depth = get_world_distance(depth, dir);
+ accum += vec2(depth, depth * depth);
+ }
+
+ out_irradiance = visibility_encode(abs(accum / probe.sample_count), probe.visibility_range);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lookdev_background_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lookdev_background_frag.glsl
new file mode 100644
index 00000000000..0d8b76b5e99
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_lookdev_background_frag.glsl
@@ -0,0 +1,38 @@
+
+/**
+ * Special background shader for lookdev mode.
+ * It samples the world cubemap at specified blur level and can fade to fully transparent.
+ *
+ * Note: This might becode obsolete if using glossy bsdf becomes supported in world shaders.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_cubemap_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(std140) uniform lightprobes_info_block
+{
+ LightProbeInfoData probes_info;
+};
+
+uniform samplerCubeArray lightprobe_cube_tx;
+uniform float opacity;
+uniform float blur;
+
+in vec4 uvcoordsvar;
+
+layout(location = 0) out vec4 out_background;
+
+void main()
+{
+ vec3 vP = get_view_space_from_depth(uvcoordsvar.xy, 0.5);
+ vec3 vV = viewCameraVec(vP);
+ vec3 R = -normal_view_to_world(vV);
+
+ R = transform_direction(probes_info.cubes.lookdev_rotation, R);
+
+ float lod = blur * probes_info.cubes.roughness_max_lod;
+ out_background.rgb = cubemap_array_sample(lightprobe_cube_tx, vec4(R, 0), lod).rgb;
+ out_background.rgb *= opacity;
+ out_background.a = 1.0 - opacity;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_ltc_lib.glsl
index 2750d42a74a..57e92b0b9b4 100644
--- a/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/eevee_ltc_lib.glsl
@@ -1,3 +1,4 @@
+
/**
* Adapted from :
* Real-Time Polygonal-Light Shading with Linearly Transformed Cosines.
@@ -6,17 +7,15 @@
* Project page: https://eheitzresearch.wordpress.com/415-2/
*/
-#define USE_LTC
-
/* Diffuse *clipped* sphere integral. */
-float diffuse_sphere_integral(float avg_dir_z, float form_factor)
+float ltc_diffuse_sphere_integral(sampler2DArray utility_tx, float avg_dir_z, float form_factor)
{
#if 1
/* use tabulated horizon-clipped sphere */
vec2 uv = vec2(avg_dir_z * 0.5 + 0.5, form_factor);
- uv = uv * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE;
+ uv = uv * UTIL_TEX_UV_SCALE + UTIL_TEX_UV_BIAS;
- return texture(utilTex, vec3(uv, 3.0)).x;
+ return texture(utility_tx, vec3(uv, UTIL_DISK_INTEGRAL_LAYER))[UTIL_DISK_INTEGRAL_COMP];
#else
/* Cheap approximation. Less smooth and have energy issues. */
return max((form_factor * form_factor + avg_dir_z) / (form_factor + 1.0), 0.0);
@@ -28,7 +27,7 @@ float diffuse_sphere_integral(float avg_dir_z, float form_factor)
* "How to solve a cubic equation, revisited"
* http://momentsingraphics.de/?p=105
*/
-vec3 solve_cubic(vec4 coefs)
+vec3 ltc_solve_cubic(vec4 coefs)
{
/* Normalize the polynomial */
coefs.xyz /= coefs.w;
@@ -119,7 +118,7 @@ vec3 solve_cubic(vec4 coefs)
/* from Real-Time Area Lighting: a Journey from Research to Production
* Stephen Hill and Eric Heitz */
-vec3 edge_integral_vec(vec3 v1, vec3 v2)
+vec3 ltc_edge_integral_vec(vec3 v1, vec3 v2)
{
float x = dot(v1, v2);
float y = abs(x);
@@ -135,10 +134,8 @@ vec3 edge_integral_vec(vec3 v1, vec3 v2)
mat3 ltc_matrix(vec4 lut)
{
- /* load inverse matrix */
- mat3 Minv = mat3(vec3(lut.x, 0, lut.y), vec3(0, 1, 0), vec3(lut.z, 0, lut.w));
-
- return Minv;
+ /* Load inverse matrix. */
+ return mat3(vec3(lut.x, 0, lut.y), vec3(0, 1, 0), vec3(lut.z, 0, lut.w));
}
void ltc_transform_quad(vec3 N, vec3 V, mat3 Minv, inout vec3 corners[4])
@@ -146,12 +143,12 @@ void ltc_transform_quad(vec3 N, vec3 V, mat3 Minv, inout vec3 corners[4])
/* Avoid dot(N, V) == 1 in ortho mode, leading T1 normalize to fail. */
V = normalize(V + 1e-8);
- /* construct orthonormal basis around N */
+ /* Construct orthonormal basis around N. */
vec3 T1, T2;
T1 = normalize(V - N * dot(N, V));
T2 = cross(N, T1);
- /* rotate area light in (T1, T2, R) basis */
+ /* Rotate area light in (T1, T2, R) basis. */
Minv = Minv * transpose(mat3(T1, T2, N));
/* Apply LTC inverse matrix. */
@@ -163,32 +160,32 @@ void ltc_transform_quad(vec3 N, vec3 V, mat3 Minv, inout vec3 corners[4])
/* If corners have already pass through ltc_transform_quad(),
* then N **MUST** be vec3(0.0, 0.0, 1.0), corresponding to the Up axis of the shading basis. */
-float ltc_evaluate_quad(vec3 corners[4], vec3 N)
+float ltc_evaluate_quad(sampler2DArray utility_tx, vec3 corners[4], vec3 N)
{
/* Approximation using a sphere of the same solid angle than the quad.
* Finding the clipped sphere diffuse integral is easier than clipping the quad. */
vec3 avg_dir;
- avg_dir = edge_integral_vec(corners[0], corners[1]);
- avg_dir += edge_integral_vec(corners[1], corners[2]);
- avg_dir += edge_integral_vec(corners[2], corners[3]);
- avg_dir += edge_integral_vec(corners[3], corners[0]);
+ avg_dir = ltc_edge_integral_vec(corners[0], corners[1]);
+ avg_dir += ltc_edge_integral_vec(corners[1], corners[2]);
+ avg_dir += ltc_edge_integral_vec(corners[2], corners[3]);
+ avg_dir += ltc_edge_integral_vec(corners[3], corners[0]);
float form_factor = length(avg_dir);
float avg_dir_z = dot(N, avg_dir / form_factor);
- return form_factor * diffuse_sphere_integral(avg_dir_z, form_factor);
+ return form_factor * ltc_diffuse_sphere_integral(utility_tx, avg_dir_z, form_factor);
}
/* If disk does not need to be transformed and is already front facing. */
-float ltc_evaluate_disk_simple(float disk_radius, float NL)
+float ltc_evaluate_disk_simple(sampler2DArray utility_tx, float disk_radius, float NL)
{
float r_sqr = disk_radius * disk_radius;
float one_r_sqr = 1.0 + r_sqr;
float form_factor = r_sqr * inversesqrt(one_r_sqr * one_r_sqr);
- return form_factor * diffuse_sphere_integral(NL, form_factor);
+ return form_factor * ltc_diffuse_sphere_integral(utility_tx, NL, form_factor);
}
/* disk_points are WS vectors from the shading point to the disk "bounding domain" */
-float ltc_evaluate_disk(vec3 N, vec3 V, mat3 Minv, vec3 disk_points[3])
+float ltc_evaluate_disk(sampler2DArray utility_tx, vec3 N, vec3 V, mat3 Minv, vec3 disk_points[3])
{
/* Avoid dot(N, V) == 1 in ortho mode, leading T1 normalize to fail. */
V = normalize(V + 1e-8);
@@ -280,7 +277,7 @@ float ltc_evaluate_disk(vec3 N, vec3 V, mat3 Minv, vec3 disk_points[3])
float c2 = (1.0 - a * t) - b * (1.0 + y0 * y0);
float c3 = 1.0;
- vec3 roots = solve_cubic(vec4(c0, c1, c2, c3));
+ vec3 roots = ltc_solve_cubic(vec4(c0, c1, c2, c3));
float e1 = roots.x;
float e2 = roots.y;
float e3 = roots.z;
@@ -298,5 +295,5 @@ float ltc_evaluate_disk(vec3 N, vec3 V, mat3 Minv, vec3 disk_points[3])
/* Find the sphere and compute lighting. */
float form_factor = max(0.0, L1 * L2 * inversesqrt((1.0 + L1 * L1) * (1.0 + L2 * L2)));
- return form_factor * diffuse_sphere_integral(avg_dir.z, form_factor);
+ return form_factor * ltc_diffuse_sphere_integral(utility_tx, avg_dir.z, form_factor);
}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_gather_frag.glsl
index 0aa54715460..8aaf21201f8 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_gather_frag.glsl
@@ -1,7 +1,6 @@
-#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
-
-/*
+/**
+ * Perform two gather blur in the 2 motion blur directions
* Based on:
* A Fast and Stable Feature-Aware Motion Blur Filter
* by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai
@@ -10,29 +9,33 @@
* Next Generation Post Processing in Call of Duty Advanced Warfare
* by Jorge Jimenez
*/
-uniform sampler2D colorBuffer;
-uniform sampler2D depthBuffer;
-uniform sampler2D velocityBuffer;
-uniform sampler2D tileMaxBuffer;
-#define KERNEL 8
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_motion_blur_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(std140) uniform sampling_block
+{
+ SamplingData sampling;
+};
-uniform float depthScale;
-uniform ivec2 tileBufferSize;
-uniform vec2 viewportSize;
-uniform vec2 viewportSizeInv;
-uniform bool isPerspective;
-uniform vec2 nearFar; /* Near & far view depths values */
+layout(std140) uniform motion_blur_block
+{
+ MotionBlurData mb;
+};
-#define linear_depth(z) \
- ((isPerspective) ? (nearFar.x * nearFar.y) / (z * (nearFar.x - nearFar.y) + nearFar.y) : \
- z * (nearFar.y - nearFar.x) + nearFar.x) /* Only true for camera view! */
+uniform sampler2D color_tx;
+uniform sampler2D depth_tx;
+uniform sampler2D velocity_tx;
+uniform sampler2D tiles_tx;
in vec4 uvcoordsvar;
-out vec4 fragColor;
+layout(location = 0) out vec4 out_color;
-#define saturate(a) clamp(a, 0.0, 1.0)
+const int gather_sample_count = 8;
vec2 spread_compare(float center_motion_length, float sample_motion_length, float offset_length)
{
@@ -41,7 +44,7 @@ vec2 spread_compare(float center_motion_length, float sample_motion_length, floa
vec2 depth_compare(float center_depth, float sample_depth)
{
- return saturate(0.5 + vec2(depthScale, -depthScale) * (sample_depth - center_depth));
+ return saturate(0.5 + vec2(-mb.depth_scale, mb.depth_scale) * (sample_depth - center_depth));
}
/* Kill contribution if not going the same direction. */
@@ -67,29 +70,6 @@ vec2 sample_weights(float center_depth,
return depth_weight * spread_weight;
}
-vec4 decode_velocity(vec4 velocity)
-{
- velocity = velocity * 2.0 - 1.0;
- /* Needed to match cycles. Can't find why... (fclem) */
- velocity *= 0.5;
- /* Transpose to pixelspace. */
- velocity *= viewportSize.xyxy;
- return velocity;
-}
-
-vec4 sample_velocity(vec2 uv)
-{
- vec4 data = texture(velocityBuffer, uv);
- return decode_velocity(data);
-}
-
-vec2 sample_velocity(vec2 uv, const bool next)
-{
- vec4 data = sample_velocity(uv);
- data.xy = (next ? data.zw : data.xy);
- return data.xy;
-}
-
void gather_sample(vec2 screen_uv,
float center_depth,
float center_motion_len,
@@ -100,11 +80,16 @@ void gather_sample(vec2 screen_uv,
inout vec4 accum_bg,
inout vec3 w_accum)
{
- vec2 sample_uv = screen_uv - offset * viewportSizeInv;
- vec2 sample_motion = sample_velocity(sample_uv, next);
+ vec2 sample_uv = screen_uv - offset * mb.target_size_inv;
+ vec2 sample_motion = sample_velocity(mb, velocity_tx, sample_uv, next);
float sample_motion_len = length(sample_motion);
- float sample_depth = linear_depth(texture(depthBuffer, sample_uv).r);
- vec4 col = textureLod(colorBuffer, sample_uv, 0.0);
+ float sample_depth = texture(depth_tx, sample_uv).r;
+ vec4 sample_color = textureLod(color_tx, sample_uv, 0.0);
+
+ /* Meh, a quirk of the motion vector pass... */
+ sample_motion = (next) ? -sample_motion : sample_motion;
+
+ sample_depth = get_view_z_from_depth(sample_depth);
vec3 weights;
weights.xy = sample_weights(
@@ -112,8 +97,8 @@ void gather_sample(vec2 screen_uv,
weights.z = dir_compare(offset, sample_motion, sample_motion_len);
weights.xy *= weights.z;
- accum += col * weights.y;
- accum_bg += col * weights.x;
+ accum += sample_color * weights.y;
+ accum_bg += sample_color * weights.x;
w_accum += weights;
}
@@ -142,8 +127,8 @@ void gather_blur(vec2 screen_uv,
}
int i;
- float t, inc = 1.0 / float(KERNEL);
- for (i = 0, t = ofs * inc; i < KERNEL; i++, t += inc) {
+ float t, inc = 1.0 / float(gather_sample_count);
+ for (i = 0, t = ofs * inc; i < gather_sample_count; i++, t += inc) {
gather_sample(screen_uv,
center_depth,
center_motion_len,
@@ -159,7 +144,7 @@ void gather_blur(vec2 screen_uv,
return;
}
- for (i = 0, t = ofs * inc; i < KERNEL; i++, t += inc) {
+ for (i = 0, t = ofs * inc; i < gather_sample_count; i++, t += inc) {
/* Also sample in center motion direction.
* Allow recovering motion where there is conflicting
* motion between foreground and background. */
@@ -180,19 +165,22 @@ void main()
vec2 uv = uvcoordsvar.xy;
/* Data of the center pixel of the gather (target). */
- float center_depth = linear_depth(texture(depthBuffer, uv).r);
- vec4 center_motion = sample_velocity(uv);
- vec4 center_color = textureLod(colorBuffer, uv, 0.0);
+ float center_depth = get_view_z_from_depth(texture(depth_tx, uv).r);
+ vec4 center_motion = sample_velocity(mb, velocity_tx, ivec2(gl_FragCoord.xy));
+
+ vec4 center_color = textureLod(color_tx, uv, 0.0);
- vec2 rand = texelfetch_noise_tex(gl_FragCoord.xy).xy;
+ float noise_offset = sampling_rng_1D_get(sampling, SAMPLING_TIME);
+ /** TODO(fclem) Blue noise. */
+ vec2 rand = vec2(interlieved_gradient_noise(gl_FragCoord.xy, 0, noise_offset),
+ interlieved_gradient_noise(gl_FragCoord.xy, 1, noise_offset));
/* Randomize tile boundary to avoid ugly discontinuities. Randomize 1/4th of the tile.
* Note this randomize only in one direction but in practice it's enough. */
rand.x = rand.x * 2.0 - 1.0;
- ivec2 tile = ivec2(gl_FragCoord.xy + rand.x * float(EEVEE_VELOCITY_TILE_SIZE) * 0.25) /
- EEVEE_VELOCITY_TILE_SIZE;
- tile = clamp(tile, ivec2(0), tileBufferSize - 1);
- vec4 max_motion = decode_velocity(texelFetch(tileMaxBuffer, tile, 0));
+ ivec2 tile = ivec2(gl_FragCoord.xy + rand.x * float(MB_TILE_DIVISOR) * 0.25) / MB_TILE_DIVISOR;
+ tile = clamp(tile, ivec2(0), textureSize(tiles_tx, 0) - 1);
+ vec4 max_motion = texelFetch(tiles_tx, tile, 0);
/* First (center) sample: time = T */
/* x: Background, y: Foreground, z: dir. */
@@ -204,11 +192,11 @@ void main()
uv, center_motion.xy, center_depth, max_motion.xy, rand.y, false, accum, accum_bg, w_accum);
/* Second linear gather. time = [T, T + delta] */
gather_blur(
- uv, center_motion.zw, center_depth, max_motion.zw, rand.y, true, accum, accum_bg, w_accum);
+ uv, -center_motion.zw, center_depth, -max_motion.zw, rand.y, true, accum, accum_bg, w_accum);
-#if 1
+#if 1 /* Own addition. Not present in reference implementation. */
/* Avoid division by 0.0. */
- float w = 1.0 / (50.0 * float(KERNEL) * 4.0);
+ float w = 1.0 / (50.0 * float(gather_sample_count) * 4.0);
accum_bg += center_color * w;
w_accum.x += w;
/* NOTE: In Jimenez's presentation, they used center sample.
@@ -223,10 +211,10 @@ void main()
/* Balance accumulation for failed samples.
* We replace the missing foreground by the background. */
float blend_fac = saturate(1.0 - w_accum.y / w_accum.z);
- fragColor = (accum / w_accum.z) + center_color * blend_fac;
+ out_color = (accum / w_accum.z) + center_color * blend_fac;
#if 0 /* For debugging. */
- fragColor.rgb = fragColor.ggg;
- fragColor.rg += max_motion.xy;
+ out_color.rgb = out_color.ggg;
+ out_color.rg += max_motion.xy;
#endif
}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_lib.glsl
new file mode 100644
index 00000000000..f88b8dee5d7
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_lib.glsl
@@ -0,0 +1,18 @@
+
+#pragma BLENDER_REQUIRE(eevee_motion_blur_lib.glsl)
+
+/* Converts uv velocity into pixel space. Assumes velocity_tx is the same resolution as the
+ * target post-fx framebuffer. */
+vec4 sample_velocity(MotionBlurData mb, sampler2D velocity_tx, ivec2 texel)
+{
+ vec4 velocity = texelFetch(velocity_tx, texel, 0);
+ velocity *= vec2(textureSize(velocity_tx, 0)).xyxy;
+ velocity = (mb.is_viewport) ? velocity.xyxy : velocity;
+ return velocity;
+}
+vec2 sample_velocity(MotionBlurData mb, sampler2D velocity_tx, vec2 uv, const bool next)
+{
+ vec4 velocity = texture(velocity_tx, uv);
+ velocity *= vec2(textureSize(velocity_tx, 0)).xyxy;
+ return (next && !mb.is_viewport) ? velocity.zw : velocity.xy;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_tiles_dilate_frag.glsl
index 300477570d0..786d6131eaa 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_tiles_dilate_frag.glsl
@@ -1,5 +1,7 @@
+
/**
- * Shaders that down-sample velocity buffer,
+ * Samples a 3x3 tile neighborhood to find potentially intersecting motions.
+ * Outputs the largest intersecting motion vector in the neighboorhod.
*
* Based on:
* A Fast and Stable Feature-Aware Motion Blur Filter
@@ -8,57 +10,17 @@
* Adapted from G3D Innovation Engine implementation.
*/
-uniform sampler2D velocityBuffer;
-uniform vec2 viewportSize;
-uniform vec2 viewportSizeInv;
-uniform ivec2 velocityBufferSize;
-
-out vec4 tileMaxVelocity;
-
-vec4 sample_velocity(ivec2 texel)
-{
- texel = clamp(texel, ivec2(0), velocityBufferSize - 1);
- vec4 data = texelFetch(velocityBuffer, texel, 0);
- /* Decode data. */
- return (data * 2.0 - 1.0) * viewportSize.xyxy;
-}
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
-vec4 encode_velocity(vec4 velocity)
+layout(std140) uniform motion_blur_block
{
- return velocity * viewportSizeInv.xyxy * 0.5 + 0.5;
-}
+ MotionBlurData mb;
+};
-#ifdef TILE_GATHER
+uniform sampler2D tiles_tx;
-uniform ivec2 gatherStep;
-
-void main()
-{
- vec4 max_motion = vec4(0.0);
- float max_motion_len_sqr_prev = 0.0;
- float max_motion_len_sqr_next = 0.0;
- ivec2 texel = ivec2(gl_FragCoord.xy);
- texel = texel * gatherStep.yx + texel * EEVEE_VELOCITY_TILE_SIZE * gatherStep;
-
- for (int i = 0; i < EEVEE_VELOCITY_TILE_SIZE; ++i) {
- vec4 motion = sample_velocity(texel + i * gatherStep);
- float motion_len_sqr_prev = dot(motion.xy, motion.xy);
- float motion_len_sqr_next = dot(motion.zw, motion.zw);
-
- if (motion_len_sqr_prev > max_motion_len_sqr_prev) {
- max_motion_len_sqr_prev = motion_len_sqr_prev;
- max_motion.xy = motion.xy;
- }
- if (motion_len_sqr_next > max_motion_len_sqr_next) {
- max_motion_len_sqr_next = motion_len_sqr_next;
- max_motion.zw = motion.zw;
- }
- }
-
- tileMaxVelocity = encode_velocity(max_motion);
-}
-
-#else /* TILE_EXPANSION */
+layout(location = 0) out vec4 out_max_motion;
bool neighbor_affect_this_tile(ivec2 offset, vec2 velocity)
{
@@ -117,35 +79,35 @@ bool neighbor_affect_this_tile(ivec2 offset, vec2 velocity)
*/
void main()
{
- vec4 max_motion = vec4(0.0);
+ ivec2 tile = ivec2(gl_FragCoord.xy);
+ ivec2 texture_bounds = textureSize(tiles_tx, 0) - 1;
+
+ out_max_motion = vec4(0.0);
float max_motion_len_sqr_prev = -1.0;
float max_motion_len_sqr_next = -1.0;
- ivec2 tile = ivec2(gl_FragCoord.xy);
ivec2 offset = ivec2(0);
for (offset.y = -1; offset.y <= 1; ++offset.y) {
for (offset.x = -1; offset.x <= 1; ++offset.x) {
- vec4 motion = sample_velocity(tile + offset);
- float motion_len_sqr_prev = dot(motion.xy, motion.xy);
- float motion_len_sqr_next = dot(motion.zw, motion.zw);
+ ivec2 sample_tile = clamp(tile + offset, ivec2(0), texture_bounds);
+ vec4 motion = texelFetch(tiles_tx, sample_tile, 0);
+
+ float motion_len_sqr_prev = len_squared(motion.xy);
+ float motion_len_sqr_next = len_squared(motion.zw);
if (motion_len_sqr_prev > max_motion_len_sqr_prev) {
if (neighbor_affect_this_tile(offset, motion.xy)) {
max_motion_len_sqr_prev = motion_len_sqr_prev;
- max_motion.xy = motion.xy;
+ out_max_motion.xy = motion.xy;
}
}
if (motion_len_sqr_next > max_motion_len_sqr_next) {
if (neighbor_affect_this_tile(offset, motion.zw)) {
max_motion_len_sqr_next = motion_len_sqr_next;
- max_motion.zw = motion.zw;
+ out_max_motion.zw = motion.zw;
}
}
}
}
-
- tileMaxVelocity = encode_velocity(max_motion);
}
-
-#endif
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_tiles_flatten_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_tiles_flatten_frag.glsl
new file mode 100644
index 00000000000..0cf380d6191
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_tiles_flatten_frag.glsl
@@ -0,0 +1,51 @@
+
+/**
+ * Shaders that down-sample velocity buffer into squared tile of MB_TILE_DIVISOR pixels wide.
+ * Outputs the largest motion vector in the tile area.
+ *
+ * Based on:
+ * A Fast and Stable Feature-Aware Motion Blur Filter
+ * by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai
+ *
+ * Adapted from G3D Innovation Engine implementation.
+ */
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_motion_blur_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(std140) uniform motion_blur_block
+{
+ MotionBlurData mb;
+};
+
+uniform sampler2D velocity_tx;
+
+layout(location = 0) out vec4 out_max_motion;
+
+void main()
+{
+ ivec2 texture_bounds = textureSize(velocity_tx, 0) - 1;
+ ivec2 tile_co = ivec2(gl_FragCoord.xy);
+
+ float max_motion_len_sqr_prev = -1.0;
+ float max_motion_len_sqr_next = -1.0;
+ for (int x = 0; x < MB_TILE_DIVISOR; x++) {
+ for (int y = 0; y < MB_TILE_DIVISOR; y++) {
+ ivec2 sample_texel = tile_co * MB_TILE_DIVISOR + ivec2(x, y);
+ vec4 motion = sample_velocity(mb, velocity_tx, min(sample_texel, texture_bounds));
+
+ float motion_len_sqr_prev = len_squared(motion.xy);
+ float motion_len_sqr_next = len_squared(motion.zw);
+
+ if (motion_len_sqr_prev > max_motion_len_sqr_prev) {
+ max_motion_len_sqr_prev = motion_len_sqr_prev;
+ out_max_motion.xy = motion.xy;
+ }
+ if (motion_len_sqr_next > max_motion_len_sqr_next) {
+ max_motion_len_sqr_next = motion_len_sqr_next;
+ out_max_motion.zw = motion.zw;
+ }
+ }
+ }
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_nodetree_eval_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_nodetree_eval_lib.glsl
new file mode 100644
index 00000000000..5bc3a1ecc0f
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_nodetree_eval_lib.glsl
@@ -0,0 +1,97 @@
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_closure_lib.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
+
+/* Globals used by closure. */
+ClosureDiffuse g_diffuse_data;
+ClosureReflection g_reflection_data;
+ClosureRefraction g_refraction_data;
+ClosureVolume g_volume_data;
+ClosureEmission g_emission_data;
+ClosureTransparency g_transparency_data;
+
+struct GlobalData {
+ /** World position. */
+ vec3 P;
+ /** Surface Normal. */
+ vec3 N;
+ /** Geometric Normal. */
+ vec3 Ng;
+ /** Barycentric coordinates. */
+ vec2 barycentric_coords;
+ vec3 barycentric_dists;
+ /** Ray properties (approximation). */
+ int ray_type;
+ float ray_depth;
+ float ray_length;
+ /** Random number to sample a closure. */
+ float closure_rand;
+ float transmit_rand;
+ /** Hair time along hair length. 0 at base 1 at tip. */
+ float hair_time;
+ /** Hair time along width of the hair. */
+ float hair_time_width;
+ /** Hair thickness in world space. */
+ float hair_thickness;
+ /** Index of the strand for per strand effects. */
+ int hair_strand_id;
+ /** Is hair. */
+ bool is_strand;
+};
+
+GlobalData g_data;
+
+void ntree_eval_init()
+{
+ g_diffuse_data.color = vec3(0.0);
+ g_diffuse_data.N = vec3(1.0, 0.0, 0.0);
+ g_diffuse_data.sss_radius = vec3(0);
+ g_diffuse_data.sss_id = 0u;
+
+ g_reflection_data.color = vec3(0.0);
+ g_reflection_data.N = vec3(1.0, 0.0, 0.0);
+ g_reflection_data.roughness = 0.5;
+
+ g_refraction_data.color = vec3(0.0);
+ g_refraction_data.N = vec3(1.0, 0.0, 0.0);
+ g_refraction_data.roughness = 0.5;
+
+ g_volume_data.emission = vec3(0.0);
+ g_volume_data.scattering = vec3(0.0);
+ g_volume_data.transmittance = vec3(1.0);
+ g_volume_data.anisotropy = 0.0;
+
+ g_emission_data.emission = vec3(0.0);
+
+ g_transparency_data.transmittance = vec3(0.0);
+ g_transparency_data.holdout = 0.0;
+}
+
+void ntree_eval_weights()
+{
+ float transmit_total = g_diffuse_data.color.r + g_refraction_data.color.r;
+ if (g_data.transmit_rand >= 0.0) {
+ float transmit_threshold = g_diffuse_data.color.r * safe_rcp(transmit_total);
+ if (g_data.transmit_rand > transmit_threshold) {
+ /* Signal that we will use the transmition closure. */
+ g_data.transmit_rand = 0.0;
+ }
+ else {
+ /* Signal that we will use the diffuse closure. */
+ g_data.transmit_rand = 1.0;
+ }
+ }
+
+ closure_weight_randomize(g_diffuse_data, g_data.closure_rand);
+ closure_weight_randomize(g_reflection_data, g_data.closure_rand);
+ closure_weight_randomize(g_refraction_data, g_data.closure_rand);
+
+ /* Amend total weight to avoid loosing energy. */
+ if (g_data.transmit_rand > 0.0) {
+ g_diffuse_data.color.r += g_refraction_data.color.r;
+ }
+ else if (g_data.transmit_rand == 0.0) {
+ g_refraction_data.color.r += g_diffuse_data.color.r;
+ }
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_raytrace_denoise_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_denoise_comp.glsl
new file mode 100644
index 00000000000..96718358f6a
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_denoise_comp.glsl
@@ -0,0 +1,260 @@
+
+/**
+ * Spatial ray reuse. Denoise raytrace result using ratio estimator.
+ * Also add in temporal reuse.
+ *
+ * Input: Ray direction * hit time, Ray radiance, Ray hit depth
+ * Ouput: Ray radiance reconstructed, Mean Ray hit depth, Radiance Variance
+ *
+ * Shader is specialized depending on the type of ray to denoise.
+ *
+ * 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
+ */
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_bsdf_microfacet_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_raytrace_resolve_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(local_size_x = 8, local_size_y = 8) in;
+
+layout(std140) uniform raytrace_block
+{
+ RaytraceData raytrace;
+};
+
+layout(std140) uniform rtbuffer_block
+{
+ RaytraceBufferData rtbuffer;
+};
+
+layout(std140) uniform hiz_block
+{
+ HiZData hiz;
+};
+
+uniform sampler2D hiz_tx;
+uniform sampler2D ray_data_tx;
+uniform sampler2D ray_radiance_tx;
+uniform sampler2D cl_color_tx;
+uniform sampler2D cl_normal_tx;
+uniform sampler2D cl_data_tx;
+uniform sampler2D ray_history_tx;
+uniform sampler2D ray_variance_tx;
+
+layout(rgba16f) restrict uniform image2D out_history_img;
+layout(r8) restrict uniform image2D out_variance_img;
+
+//#define USE_HISTORY
+
+void history_weigh_and_accumulate(vec3 rgb_history,
+ vec3 rgb_min,
+ vec3 rgb_max,
+ vec3 rgb_mean,
+ vec3 rgb_deviation,
+ inout vec3 rgb_accum,
+ inout float weight_accum)
+{
+ /* Basically tells us how much the given sample is inside the range of values
+ * we found during spatial reconstruction. */
+ /* Slide 46. */
+ vec3 dist = (rgb_history - rgb_mean) * safe_rcp(rgb_deviation);
+ float weight = exp2(-10.0 * max_v3(dist));
+ /* Slide 47. */
+ rgb_history = clamp(rgb_history, rgb_min, rgb_max);
+
+#ifdef USE_HISTORY
+ rgb_accum += rgb_history * weight;
+ weight_accum += weight;
+#endif
+}
+
+void main(void)
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ ivec2 img_size = textureSize(ray_data_tx, 0).xy;
+
+ if (any(greaterThan(texel, img_size))) {
+ return;
+ }
+
+ /* Skip pixels that have not been raytraced. */
+ if (texelFetch(ray_data_tx, texel, 0).w == 0.0) {
+ imageStore(out_history_img, texel, vec4(0.0));
+ imageStore(out_variance_img, texel, vec4(0.0));
+ return;
+ }
+
+ vec2 uv = vec2(texel) / vec2(img_size);
+ float gbuffer_depth = texelFetch(hiz_tx, texel, 0).r;
+ vec3 vP = get_view_space_from_depth(uv, gbuffer_depth);
+ vec3 vV = viewCameraVec(vP);
+ vec2 texel_size = hiz.pixel_to_ndc * 0.5;
+
+ int sample_count = resolve_sample_max;
+#if defined(DIFFUSE)
+ vec4 tra_col_in = texture(cl_color_tx, uv);
+ vec4 tra_nor_in = texture(cl_normal_tx, uv);
+ vec4 tra_dat_in = vec4(0.0); /* UNUSED */
+
+ ClosureDiffuse diffuse = gbuffer_load_diffuse_data(tra_col_in, tra_nor_in, tra_dat_in);
+
+ if (diffuse.sss_radius.r < 0.0) {
+ /* Refraction pixel. */
+ imageStore(out_history_img, texel, vec4(0.0));
+ imageStore(out_variance_img, texel, vec4(0.0));
+ return;
+ }
+
+ vec3 vN = transform_direction(ViewMatrix, diffuse.N);
+ vec3 color = diffuse.color;
+
+#elif defined(REFRACTION)
+ vec4 tra_col_in = texture(cl_color_tx, uv);
+ vec4 tra_nor_in = texture(cl_normal_tx, uv);
+ vec4 tra_dat_in = texture(cl_data_tx, uv);
+
+ ClosureRefraction refraction = gbuffer_load_refraction_data(tra_col_in, tra_nor_in, tra_dat_in);
+
+ if (refraction.ior == -1.0) {
+ /* Diffuse/SSS pixel. */
+ imageStore(out_history_img, texel, vec4(0.0));
+ imageStore(out_variance_img, texel, vec4(0.0));
+ return;
+ }
+ float thickness;
+ gbuffer_load_global_data(tra_nor_in, thickness);
+
+ vec3 vN = transform_direction(ViewMatrix, refraction.N);
+ float roughness_sqr = max(1e-3, sqr(refraction.roughness));
+ vec3 color = refraction.color;
+
+ /* TODO(fclem): Unfortunately Refraction ray reuse does not work great for some reasons.
+ * To investigate. */
+ sample_count = 1;
+#else
+ ClosureReflection reflection = gbuffer_load_reflection_data(cl_color_tx, cl_normal_tx, uv);
+
+ vec3 vN = transform_direction(ViewMatrix, reflection.N);
+ float roughness_sqr = max(1e-3, sqr(reflection.roughness));
+ vec3 color = reflection.color;
+
+ if (roughness_sqr == 1e-3) {
+ sample_count = 1;
+ }
+#endif
+
+ /* ----- SPATIAL DENOISE ----- */
+
+ /* Blue noise categorised into 4 sets of samples.
+ * See "Stochastic all the things" presentation slide 32-37. */
+ int sample_pool = int((gl_GlobalInvocationID.x & 1u) + (gl_GlobalInvocationID.y & 1u) * 2u);
+ sample_pool = (sample_pool + raytrace.pool_offset) % 4;
+ int sample_id = sample_pool * resolve_sample_max;
+
+ float hit_depth_mean = 0.0;
+ vec3 rgb_mean = vec3(0.0);
+ vec3 rgb_moment = vec3(0.0);
+ vec3 radiance_accum = vec3(0.0);
+ float weight_accum = 0.0;
+ for (int i = 0; i < sample_count; i++, sample_id++) {
+ vec2 sample_uv = uv + resolve_sample_offsets[sample_id] * texel_size;
+
+ vec4 ray_data = texture(ray_data_tx, sample_uv);
+ vec4 ray_radiance = texture(ray_radiance_tx, sample_uv);
+
+ vec3 vR = normalize(ray_data.xyz);
+ float ray_pdf_inv = ray_data.w;
+ /* Skip invalid pixels. */
+ if (ray_pdf_inv == 0.0) {
+ continue;
+ }
+
+ /* Slide 54. */
+#if defined(DIFFUSE)
+ float bsdf = saturate(dot(vN, vR));
+#elif defined(REFRACTION)
+ float bsdf = btdf_ggx(vN, vR, vV, roughness_sqr, refraction.ior);
+#else
+ float bsdf = bsdf_ggx(vN, vR, vV, roughness_sqr);
+#endif
+ float weight = bsdf * ray_pdf_inv;
+
+#ifdef REFRACTION
+ /* Transmit twice if thickness is set and ray is longer than thickness. */
+ if (thickness > 0.0 && length(ray_data.xyz) > thickness) {
+ ray_radiance.rgb *= color;
+ }
+#endif
+ radiance_accum += ray_radiance.rgb * weight;
+ weight_accum += weight;
+
+ hit_depth_mean += ray_radiance.a;
+ rgb_mean += ray_radiance.rgb;
+ rgb_moment += sqr(ray_radiance.rgb);
+ }
+
+ /* ----- TEMPORAL DENOISE ----- */
+
+ /* Local statistics. */
+ float sample_count_inv = 1.0 / float(sample_count);
+ rgb_mean *= sample_count_inv;
+ rgb_moment *= sample_count_inv;
+ hit_depth_mean *= sample_count_inv;
+ vec3 rgb_variance = abs(rgb_moment - sqr(rgb_mean));
+ vec3 rgb_deviation = sqrt(rgb_variance);
+
+ float variance = max_v3(rgb_variance);
+
+ radiance_accum *= safe_rcp(weight_accum);
+ weight_accum = 1.0;
+
+#if defined(DIFFUSE)
+ if (rtbuffer.valid_history_diffuse) {
+#elif defined(REFRACTION)
+ if (rtbuffer.valid_history_refraction) {
+#else
+ if (rtbuffer.valid_history_reflection) {
+#endif
+
+ vec3 rgb_min = rgb_mean - rgb_deviation;
+ vec3 rgb_max = rgb_mean + rgb_deviation;
+
+ /* Surface reprojection. */
+ vec3 P_surf = transform_point(ViewMatrixInverse, vP);
+ vec2 uv_surf = project_point(rtbuffer.history_persmat, P_surf).xy * 0.5 + 0.5;
+ ivec2 texel_surf = ivec2(uv_surf * vec2(img_size));
+ if (all(lessThan(texel_surf, img_size)) && all(greaterThan(texel_surf, ivec2(0)))) {
+ vec3 radiance = texelFetch(ray_history_tx, texel_surf, 0).rgb;
+ history_weigh_and_accumulate(
+ radiance, rgb_min, rgb_max, rgb_mean, rgb_deviation, radiance_accum, weight_accum);
+
+ /* Variance estimate (slide 41). */
+ float variance_history = texelFetch(ray_variance_tx, texel_surf, 0).r;
+ variance = mix(variance, variance_history, 0.5);
+ }
+
+#if !defined(DIFFUSE)
+ /* Reflexion reprojection. */
+ vec3 P_hit = get_world_space_from_depth(uv, hit_depth_mean);
+ vec2 uv_hit = project_point(rtbuffer.history_persmat, P_hit).xy * 0.5 + 0.5;
+ ivec2 texel_hit = ivec2(uv_hit * vec2(img_size));
+ if (all(lessThan(texel_hit, img_size)) && all(greaterThan(texel_hit, ivec2(0)))) {
+ vec3 radiance = texelFetch(ray_history_tx, texel_hit, 0).rgb;
+ history_weigh_and_accumulate(
+ radiance, rgb_min, rgb_max, rgb_mean, rgb_deviation, radiance_accum, weight_accum);
+ }
+#endif
+
+ radiance_accum *= safe_rcp(weight_accum);
+ }
+
+ /* Save linear depth in alpha to speed-up the bilateral filter. */
+ imageStore(out_history_img, texel, vec4(radiance_accum, -vP.z));
+ imageStore(out_variance_img, texel, vec4(variance, 0.0, 0.0, 0.0));
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_raytrace_raygen_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_raygen_frag.glsl
new file mode 100644
index 00000000000..8f8c8457135
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_raygen_frag.glsl
@@ -0,0 +1,183 @@
+/**
+ * Simple wrapper around the screen-space raytracing routine.
+ * The goal is to output the tracing result buffer that can be denoised.
+ * We fallback to reflection probe if the ray fails.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_raytrace_raygen_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_raytrace_trace_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+
+layout(std140) uniform sampling_block
+{
+ SamplingData sampling;
+};
+
+layout(std140) uniform raytrace_block
+{
+ RaytraceData raytrace;
+};
+
+layout(std140) uniform hiz_block
+{
+ HiZData hiz;
+};
+
+layout(std140) uniform cubes_block
+{
+ CubemapData cubes[CULLING_ITEM_BATCH];
+};
+
+layout(std140) uniform lightprobes_info_block
+{
+ LightProbeInfoData probes_info;
+};
+
+uniform sampler2D hiz_tx;
+uniform sampler2D hiz_front_tx;
+uniform samplerCubeArray lightprobe_cube_tx;
+uniform sampler2D radiance_tx;
+uniform sampler2D combined_tx;
+uniform sampler2D cl_color_tx;
+uniform sampler2D cl_normal_tx;
+uniform sampler2D cl_data_tx;
+uniform sampler2DArray utility_tx;
+
+utility_tx_fetch_define(utility_tx);
+utility_tx_sample_define(utility_tx);
+
+in vec4 uvcoordsvar;
+
+layout(location = 0) out vec4 out_ray_data;
+layout(location = 1) out vec4 out_ray_radiance;
+
+/* Prototypes. */
+vec3 lightprobe_cubemap_eval(vec3 P, vec3 R, float roughness, float random_threshold);
+
+void main()
+{
+ vec2 uv = uvcoordsvar.xy;
+ float gbuffer_depth = texelFetch(hiz_front_tx, ivec2(gl_FragCoord.xy), 0).r;
+ vec3 P = get_world_space_from_depth(uv, gbuffer_depth);
+ vec3 V = cameraVec(P);
+
+ vec4 noise = utility_tx_fetch(gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).gbar;
+
+ out_ray_data = vec4(0.0);
+ out_ray_radiance = vec4(0.0);
+
+#if defined(DIFFUSE)
+ vec4 tra_col_in = vec4(0.0); /* UNUSED */
+ vec4 tra_nor_in = texture(cl_normal_tx, uv);
+ vec4 tra_dat_in = vec4(0.0); /* UNUSED */
+
+ ClosureDiffuse diffuse = gbuffer_load_diffuse_data(tra_col_in, tra_nor_in, tra_dat_in);
+
+ if (diffuse.sss_radius.r < 0.0) {
+ /* Refraction pixel. */
+ return;
+ }
+ float roughness = 1.0;
+
+#elif defined(REFRACTION)
+ vec4 tra_col_in = vec4(0.0); /* UNUSED */
+ vec4 tra_nor_in = texture(cl_normal_tx, uv);
+ vec4 tra_dat_in = texture(cl_data_tx, uv);
+
+ ClosureRefraction refraction = gbuffer_load_refraction_data(tra_col_in, tra_nor_in, tra_dat_in);
+
+ float thickness;
+ gbuffer_load_global_data(tra_nor_in, thickness);
+
+ if (refraction.ior == -1.0) {
+ /* Diffuse/SSS pixel. */
+ return;
+ }
+ float roughness = refraction.roughness;
+
+#else
+ ClosureReflection reflection = gbuffer_load_reflection_data(cl_color_tx, cl_normal_tx, uv);
+
+ float roughness = reflection.roughness;
+#endif
+
+ /* Generate ray. */
+ float pdf;
+#if defined(DIFFUSE)
+ Ray ray = raytrace_create_diffuse_ray(sampling, noise.xy, diffuse, P, pdf);
+#elif defined(REFRACTION)
+ Ray ray = raytrace_create_refraction_ray(sampling, noise.xy, refraction, V, P, pdf);
+#else
+ Ray ray = raytrace_create_reflection_ray(sampling, noise.xy, reflection, V, P, pdf);
+#endif
+ ray = raytrace_world_ray_to_view(ray);
+
+ bool hit = false;
+
+#ifndef SKIP_TRACE
+ vec2 noise_offset = sampling_rng_2D_get(sampling, SAMPLING_RAYTRACE_W);
+ vec2 rand = fract(noise.zw + noise_offset.xy);
+ /* Extend the ray to cover the whole view. */
+ ray.direction *= 1e16;
+
+ if (roughness - rand.y * 0.2 < raytrace.max_roughness) {
+# ifdef REFRACTION
+ const bool discard_backface = false;
+ const bool allow_self_intersection = true;
+ /* TODO(fclem): Take IOR into account in the roughness LOD bias. */
+# else
+ const bool discard_backface = true;
+ const bool allow_self_intersection = false;
+# endif
+ hit = raytrace_screen(
+ raytrace, hiz, hiz_tx, rand.x, roughness, discard_backface, allow_self_intersection, ray);
+ }
+#endif
+
+ vec3 radiance;
+ if (hit) {
+ /* Evaluate radiance at hitpoint. */
+ vec2 hit_uv = get_uvs_from_view(ray.origin + ray.direction);
+
+ radiance = textureLod(radiance_tx, hit_uv, 0.0).rgb;
+
+#if defined(DIFFUSE)
+ /* For diffuse, the radiance is still split and SSS materials don't have the color applied. */
+ vec4 tra_col_in = texture(cl_color_tx, hit_uv);
+ vec4 tra_nor_in = texture(cl_normal_tx, hit_uv);
+ vec4 tra_dat_in = vec4(0.0); /* UNUSED */
+ ClosureDiffuse hit_diffuse = gbuffer_load_diffuse_data(tra_col_in, tra_nor_in, tra_dat_in);
+
+ if (hit_diffuse.sss_id > 0u) {
+ /* Refraction pixel. */
+ radiance *= hit_diffuse.color;
+ }
+ radiance += textureLod(combined_tx, hit_uv, 0.0).rgb;
+#endif
+ }
+ else {
+ /* Evaluate fallback lightprobe. */
+ float noise_offset = sampling_rng_1D_get(sampling, SAMPLING_LIGHTPROBE);
+ float random_probe = fract(noise.w + noise_offset);
+
+ ray = raytrace_view_ray_to_world(ray);
+ /* TOOD(fclem): We could reduce noise by mapping ray pdf to roughness. */
+ float roughness = 0.0;
+
+ radiance = lightprobe_cubemap_eval(ray.origin, ray.direction, roughness, random_probe);
+ }
+ /* Apply brightness clamping. */
+ float luma = max_v3(radiance);
+ radiance *= 1.0 - max(0.0, luma - raytrace.brightness_clamp) * safe_rcp(luma);
+ /* Limit to the smallest non-0 value that the format can encode.
+ * Strangely it does not correspond to the IEEE spec. */
+ float inv_pdf = (pdf == 0.0) ? 0.0 : max(6e-8, 1.0 / pdf);
+ float hit_depth = saturate(get_depth_from_view_z(ray.origin.z + ray.direction.z));
+ /* Output the ray. */
+ out_ray_data = vec4(ray.direction, inv_pdf);
+ out_ray_radiance = vec4(radiance, hit_depth);
+}
+
+#pragma BLENDER_REQUIRE_POST(eevee_lightprobe_eval_cubemap_lib.glsl)
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_raytrace_raygen_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_raygen_lib.glsl
new file mode 100644
index 00000000000..8c1504f2e16
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_raygen_lib.glsl
@@ -0,0 +1,83 @@
+/**
+ * Ray generation routines for each BSDF types.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_bsdf_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_raytrace_trace_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+/* Returns viewspace ray. */
+Ray raytrace_create_reflection_ray(
+ SamplingData sampling, vec2 noise, ClosureReflection reflection, vec3 V, vec3 P, out float pdf)
+{
+ vec2 noise_offset = sampling_rng_2D_get(sampling, SAMPLING_RAYTRACE_U);
+ vec3 Xi = sample_cylinder(fract(noise_offset + noise));
+
+ float roughness_sqr = max(1e-3, sqr(reflection.roughness));
+ /* Gives *perfect* reflection for very small roughness. */
+ if (reflection.roughness < 0.0016) {
+ Xi = vec3(0.0);
+ }
+
+ vec3 T, B, N = reflection.N;
+ make_orthonormal_basis(N, T, B);
+
+ Ray ray;
+ ray.origin = P;
+ ray.direction = sample_ggx_reflect(Xi, roughness_sqr, V, N, T, B, pdf);
+ return ray;
+}
+
+Ray raytrace_create_refraction_ray(
+ SamplingData sampling, vec2 noise, ClosureRefraction refraction, vec3 V, vec3 P, out float pdf)
+{
+ vec2 noise_offset = sampling_rng_2D_get(sampling, SAMPLING_RAYTRACE_U);
+ vec3 Xi = sample_cylinder(fract(noise_offset + noise));
+
+ float roughness_sqr = max(1e-3, sqr(refraction.roughness));
+ /* Gives *perfect* refraction for very small roughness. */
+ if (refraction.roughness < 0.0016) {
+ Xi = vec3(0.0);
+ }
+ vec3 T, B, N = refraction.N;
+ make_orthonormal_basis(N, T, B);
+
+ Ray ray;
+ ray.origin = P;
+ ray.direction = sample_ggx_refract(Xi, roughness_sqr, refraction.ior, V, N, T, B, pdf);
+ return ray;
+}
+
+Ray raytrace_create_diffuse_ray(
+ SamplingData sampling, vec2 noise, ClosureDiffuse diffuse, vec3 P, out float pdf)
+{
+ vec2 noise_offset = sampling_rng_2D_get(sampling, SAMPLING_RAYTRACE_U);
+ vec3 Xi = sample_cylinder(fract(noise_offset + noise));
+
+ /* Bias the rays so we never get really high energy rays almost parallel to the surface. */
+ Xi.x = Xi.x * 0.98 + 0.02;
+
+ vec3 T, B, N = diffuse.N;
+ make_orthonormal_basis(N, T, B);
+
+ Ray ray;
+ ray.origin = P;
+ ray.direction = sample_cosine_hemisphere(Xi, N, T, B, pdf);
+ return ray;
+}
+
+Ray raytrace_world_ray_to_view(Ray ray)
+{
+ ray.origin = transform_point(ViewMatrix, ray.origin);
+ ray.direction = transform_direction(ViewMatrix, ray.direction);
+ return ray;
+}
+
+Ray raytrace_view_ray_to_world(Ray ray)
+{
+ ray.origin = transform_point(ViewMatrixInverse, ray.origin);
+ ray.direction = transform_direction(ViewMatrixInverse, ray.direction);
+ return ray;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_raytrace_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_resolve_frag.glsl
new file mode 100644
index 00000000000..e532152d2f1
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_resolve_frag.glsl
@@ -0,0 +1,124 @@
+
+/**
+ * Final denoise step using a bilateral filter. Filter radius is controled by variance estimate.
+ *
+ * Inputs: Ray radiance (denoised), Mean hit depth, Extimated variance from ray reconstruction
+ * Outputs: Ray radiance (filtered).
+ *
+ * Linear depth is packed in ray_radiance for this step.
+ * 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
+ */
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_bsdf_microfacet_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(std140) uniform hiz_block
+{
+ HiZData hiz;
+};
+
+uniform sampler2D ray_radiance_tx;
+uniform sampler2D ray_variance_tx;
+uniform sampler2D cl_color_tx;
+uniform sampler2D cl_normal_tx;
+uniform sampler2D cl_data_tx;
+
+in vec4 uvcoordsvar;
+
+layout(location = 0) out vec4 out_combined;
+layout(location = 1) out vec4 out_diffuse;
+layout(location = 2) out vec3 out_specular;
+
+#if defined(DIFFUSE)
+# define RADIUS 4
+#elif defined(REFRACTION)
+# define RADIUS 1
+#else
+# define RADIUS 1
+#endif
+
+float normal_pdf(float x_sqr, float sigma_inv, float sigma_inv_sqr)
+{
+ return exp(-0.5 * x_sqr * sigma_inv_sqr) * sigma_inv;
+}
+
+void main(void)
+{
+ vec2 uv = uvcoordsvar.xy;
+ float ray_variance = texture(ray_variance_tx, uv).r;
+ vec4 ray_data = texture(ray_radiance_tx, uv);
+ float center_depth = ray_data.w;
+ vec2 texel_size = hiz.pixel_to_ndc * 0.5;
+
+ out_combined = vec4(0.0);
+ out_diffuse = vec4(0.0);
+ out_specular = vec3(0.0);
+
+#if defined(DIFFUSE)
+ ClosureDiffuse closure = gbuffer_load_diffuse_data(cl_color_tx, cl_normal_tx, cl_data_tx, uv);
+ if (closure.sss_radius.r < 0.0) {
+ return;
+ }
+ float sigma_pixel = 3.0;
+#elif defined(REFRACTION)
+ ClosureRefraction closure = gbuffer_load_refraction_data(
+ cl_color_tx, cl_normal_tx, cl_data_tx, uv);
+ if (closure.ior == -1.0) {
+ return;
+ }
+ float sigma_pixel = 1.0;
+#else
+ ClosureReflection closure = gbuffer_load_reflection_data(cl_color_tx, cl_normal_tx, uv);
+ float sigma_pixel = 1.0;
+#endif
+ /* TODO(fclem): Sigma based on variance. */
+ float sigma_depth = 0.1; /* TODO user option? */
+
+ float px_sigma_inv = 1.0 / sigma_pixel;
+ float px_sigma_inv_sqr = sqr(px_sigma_inv);
+ float depth_sigma_inv = 1.0 / sigma_depth;
+ float depth_sigma_inv_sqr = sqr(depth_sigma_inv);
+
+ float weight_accum = normal_pdf(0.0, px_sigma_inv, px_sigma_inv_sqr);
+ vec3 radiance_accum = ray_data.rgb * weight_accum;
+ for (int x = -RADIUS; x <= RADIUS; x++) {
+ for (int y = -RADIUS; y <= RADIUS; y++) {
+ /* Skip center pixels. */
+ if (x == 0 && y == 0) {
+ continue;
+ }
+ vec2 sample_uv = uv + vec2(x, y) * texel_size;
+ vec4 ray_data = texture(ray_radiance_tx, sample_uv);
+ /* Skip unprocessed pixels. */
+ if (ray_data.w == 0.0) {
+ continue;
+ }
+ float delta_pixel_sqr = len_squared(vec2(x, y));
+ float delta_depth_sqr = sqr(abs(center_depth - ray_data.w));
+ /* TODO(fclem): OPTI might be a good idea to compare view normal to avoid one matrix mult. */
+ vec3 sample_N = gbuffer_decode_normal(texture(cl_normal_tx, sample_uv).xy);
+ /* Bilateral weight. */
+ float weight = saturate(dot(sample_N, closure.N)) *
+ normal_pdf(delta_pixel_sqr, px_sigma_inv, px_sigma_inv_sqr) *
+ normal_pdf(delta_depth_sqr, depth_sigma_inv, depth_sigma_inv_sqr);
+
+ radiance_accum += ray_data.rgb * weight;
+ weight_accum += weight;
+ }
+ }
+ radiance_accum *= safe_rcp(weight_accum);
+ radiance_accum *= closure.color;
+
+ out_combined = vec4(radiance_accum, 0.0);
+#if defined(DIFFUSE)
+ out_diffuse.rgb = radiance_accum;
+#else
+ out_specular = radiance_accum;
+#endif
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_raytrace_trace_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_trace_lib.glsl
new file mode 100644
index 00000000000..e33968a36d5
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_trace_lib.glsl
@@ -0,0 +1,182 @@
+/**
+ * Screen-space raytracing routine.
+ *
+ * Based on "Efficient GPU Screen-Space Ray Tracing"
+ * by Morgan McGuire & Michael Mara
+ * https://jcgt.org/published/0003/04/04/paper.pdf
+ *
+ * Many modifications were made for our own usage.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+/**
+ * As input to the tracing function, direction is premultiplied by its maximum length.
+ * As output, direction is scaled to hit point or to latest step.
+ */
+struct Ray {
+ vec3 origin;
+ vec3 direction;
+};
+
+/**
+ * Screenspace ray ([0..1] "uv" range) where direction is normalize to be as small as one
+ * full-resolution pixel. The ray is also clipped to all frustum sides.
+ */
+struct ScreenSpaceRay {
+ vec4 origin;
+ vec4 direction;
+ float max_time;
+};
+
+/* Inputs expected to be in viewspace. */
+void raytrace_clip_ray_to_near_plane(inout Ray ray)
+{
+ float near_dist = get_view_z_from_depth(0.0);
+ if ((ray.origin.z + ray.direction.z) > near_dist) {
+ ray.direction *= abs((near_dist - ray.origin.z) / ray.direction.z);
+ }
+}
+
+void raytrace_screenspace_ray_finalize(HiZData hiz, inout ScreenSpaceRay ray)
+{
+ /* Constant bias (due to depth buffer precision). Helps with self intersection. */
+ /* Magic numbers for 24bits of precision.
+ * From http://terathon.com/gdc07_lengyel.pdf (slide 26) */
+ const float bias = -2.4e-7 * 2.0;
+ ray.origin.zw += bias;
+ ray.direction.zw += bias;
+
+ ray.direction -= ray.origin;
+ /* If the line is degenerate, make it cover at least one pixel
+ * to not have to handle zero-pixel extent as a special case later. */
+ if (len_squared(ray.direction.xy) < 0.00001) {
+ ray.direction.xy = vec2(0.0, 0.00001);
+ }
+ float ray_len_sqr = len_squared(ray.direction.xyz);
+ /* Make ray.direction cover one pixel. */
+ bool is_more_vertical = abs(ray.direction.x) < abs(ray.direction.y);
+ ray.direction /= (is_more_vertical) ? abs(ray.direction.y) : abs(ray.direction.x);
+ ray.direction *= (is_more_vertical) ? hiz.pixel_to_ndc.y : hiz.pixel_to_ndc.x;
+ /* Clip to segment's end. */
+ ray.max_time = sqrt(ray_len_sqr * safe_rcp(len_squared(ray.direction.xyz)));
+ /* Clipping to frustum sides. */
+ float clip_dist = line_unit_box_intersect_dist_safe(ray.origin.xyz, ray.direction.xyz);
+ ray.max_time = min(ray.max_time, clip_dist);
+ /* Convert to texture coords [0..1] range. */
+ ray.origin = ray.origin * 0.5 + 0.5;
+ ray.direction *= 0.5;
+}
+
+ScreenSpaceRay raytrace_screenspace_ray_create(HiZData hiz, Ray ray)
+{
+ ScreenSpaceRay ssray;
+ ssray.origin.xyz = project_point(ProjectionMatrix, ray.origin);
+ ssray.direction.xyz = project_point(ProjectionMatrix, ray.origin + ray.direction);
+
+ raytrace_screenspace_ray_finalize(hiz, ssray);
+ return ssray;
+}
+
+ScreenSpaceRay raytrace_screenspace_ray_create(HiZData hiz, Ray ray, float thickness)
+{
+ ScreenSpaceRay ssray;
+ ssray.origin.xyz = project_point(ProjectionMatrix, ray.origin);
+ ssray.direction.xyz = project_point(ProjectionMatrix, ray.origin + ray.direction);
+ /* Interpolate thickness in screen space.
+ * Calculate thickness further away to avoid near plane clipping issues. */
+ ssray.origin.w = get_depth_from_view_z(ray.origin.z - thickness);
+ ssray.direction.w = get_depth_from_view_z(ray.origin.z + ray.direction.z - thickness);
+ ssray.origin.w = ssray.origin.w * 2.0 - 1.0;
+ ssray.direction.w = ssray.direction.w * 2.0 - 1.0;
+
+ raytrace_screenspace_ray_finalize(hiz, ssray);
+ return ssray;
+}
+
+/**
+ * Raytrace against the given hizbuffer heightfield.
+ *
+ * \param stride_rand: Random number in [0..1] range. Offset along the ray to avoid banding
+ * artifact when steps are too large.
+ * \param roughness: Determine how lower depth mipmaps are used to make the tracing faster. Lower
+ * roughness will use lower mipmaps.
+ * \param discard_backface: If true, raytrace will return false if we hit a surface from behind.
+ * \param allow_self_intersection: If false, raytrace will return false if the ray is not covering
+ * at least one pixel.
+ * \param ray: Viewspace ray. Direction premultiplied by maximum length.
+ *
+ * \return True if there is a valid intersection.
+ */
+bool raytrace_screen(RaytraceData raytrace,
+ HiZData hiz,
+ sampler2D hiz_tx,
+ float stride_rand,
+ float roughness,
+ const bool discard_backface,
+ const bool allow_self_intersection,
+ inout Ray ray)
+{
+ /* Clip to near plane for perspective view where there is a singularity at the camera origin. */
+ if (ProjectionMatrix[3][3] == 0.0) {
+ raytrace_clip_ray_to_near_plane(ray);
+ }
+
+ ScreenSpaceRay ssray = raytrace_screenspace_ray_create(hiz, ray, raytrace.thickness);
+ /* Avoid no iteration. */
+ if (!allow_self_intersection && ssray.max_time < 1.1) {
+ /* Still output the clipped ray. */
+ vec3 hit_ssP = ssray.origin.xyz + ssray.direction.xyz * ssray.max_time;
+ vec3 hit_P = get_view_space_from_depth(hit_ssP.xy, saturate(hit_ssP.z));
+ ray.direction = hit_P - ray.origin;
+ return false;
+ }
+
+ ssray.max_time = max(1.1, ssray.max_time);
+
+ float prev_delta = 0.0, prev_time = 0.0;
+ float depth_sample = get_depth_from_view_z(ray.origin.z);
+ float delta = depth_sample - ssray.origin.z;
+
+ float lod_fac = saturate(fast_sqrt(roughness) * 2.0 - 0.4);
+
+ /* Cross at least one pixel. */
+ float t = 1.001, time = 1.001;
+ bool hit = false;
+ const float max_steps = 255.0;
+ for (float iter = 1.0; !hit && (time < ssray.max_time) && (iter < max_steps); iter++) {
+ float stride = 1.0 + iter * raytrace.quality;
+ float lod = log2(stride) * lod_fac;
+
+ prev_time = time;
+ prev_delta = delta;
+
+ time = min(t + stride * stride_rand, ssray.max_time);
+ t += stride;
+
+ vec4 ss_p = ssray.origin + ssray.direction * time;
+ depth_sample = textureLod(hiz_tx, ss_p.xy * hiz.uv_scale, floor(lod)).r;
+
+ delta = depth_sample - ss_p.z;
+ /* Check if the ray is below the surface ... */
+ hit = (delta < 0.0);
+ /* ... and above it with the added thickness. */
+ hit = hit && (delta > ss_p.z - ss_p.w || abs(delta) < abs(ssray.direction.z * stride * 2.0));
+ }
+ /* Discard backface hits. */
+ hit = hit && !(discard_backface && prev_delta < 0.0);
+ /* Reject hit if background. */
+ hit = hit && (depth_sample != 1.0);
+ /* Refine hit using intersection between the sampled heightfield and the ray.
+ * This simplifies nicely to this single line. */
+ time = mix(prev_time, time, saturate(prev_delta / (prev_delta - delta)));
+
+ vec3 hit_ssP = ssray.origin.xyz + ssray.direction.xyz * time;
+ /* Set ray to where tracing ended. */
+ vec3 hit_P = get_view_space_from_depth(hit_ssP.xy, saturate(hit_ssP.z));
+ ray.direction = hit_P - ray.origin;
+
+ return hit;
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_sampling_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_sampling_lib.glsl
new file mode 100644
index 00000000000..74fcf10e5f7
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_sampling_lib.glsl
@@ -0,0 +1,96 @@
+
+/**
+ * Sampling data accessors and random number generators.
+ * Also contains some sample mapping functions.
+ **/
+
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+/* -------------------------------------------------------------------- */
+/** \name Sampling data.
+ *
+ * Return a random values from Low Discrepency Sequence in [0..1) range.
+ * This value is uniform (constant) for the whole scene sample.
+ * You might want to couple it with a noise function.
+ * \{ */
+
+float sampling_rng_1D_get(SamplingData data, const eSamplingDimension dimension)
+{
+ return data.dimensions[dimension].x;
+}
+
+vec2 sampling_rng_2D_get(SamplingData data, const eSamplingDimension dimension)
+{
+ return vec2(data.dimensions[dimension].x, data.dimensions[dimension + 1u].x);
+}
+
+vec3 sampling_rng_3D_get(SamplingData data, const eSamplingDimension dimension)
+{
+ return vec3(data.dimensions[dimension].x,
+ data.dimensions[dimension + 1u].x,
+ data.dimensions[dimension + 2u].x);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Random Number Generators.
+ * \{ */
+
+/* Interlieved gradient noise by Jorge Jimenez
+ * http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
+ * Seeding found by Epic Game. */
+float interlieved_gradient_noise(vec2 pixel, float seed, float offset)
+{
+ pixel += seed * (vec2(47, 17) * 0.695);
+ return fract(offset + 52.9829189 * fract(0.06711056 * pixel.x + 0.00583715 * pixel.y));
+}
+
+/* From: http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html */
+float van_der_corput_radical_inverse(uint bits)
+{
+ bits = (bits << 16u) | (bits >> 16u);
+ bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
+ bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
+ bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
+ bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
+ /* Same as dividing by 0x100000000. */
+ return float(bits) * 2.3283064365386963e-10;
+}
+
+vec2 hammersley_2d(float i, float sample_count)
+{
+ vec2 rand;
+ rand.x = i / sample_count;
+ rand.y = van_der_corput_radical_inverse(uint(i));
+ return rand;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Distribution mapping.
+ *
+ * Functions mapping input random numbers to sampling shapes (i.e: hemisphere).
+ * \{ */
+
+/* Given 2 randome number in [0..1] range, return a random unit disk sample. */
+vec2 sample_disk(vec2 noise)
+{
+ float angle = noise.x * M_2PI;
+ return vec2(cos(angle), sin(angle)) * sqrt(noise.y);
+}
+
+/* This transform a 2d random sample (in [0..1] range) to a sample located on a cylinder of the
+ * same range. This is because the sampling functions expect such a random sample which is
+ * normally precomputed. */
+vec3 sample_cylinder(vec2 rand)
+{
+ float theta = rand.x;
+ float phi = (rand.y - 0.5) * M_2PI;
+ float cos_phi = cos(phi);
+ float sin_phi = sqrt(1.0 - sqr(cos_phi)) * sign(phi);
+ return vec3(theta, cos_phi, sin_phi);
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_debug_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_debug_frag.glsl
new file mode 100644
index 00000000000..99dc6d5ee56
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_debug_frag.glsl
@@ -0,0 +1,279 @@
+
+/**
+ * Debug drawing for virtual shadowmaps.
+ * See eShadowDebug for more information.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shadow_page_lib.glsl)
+
+/** Control the scaling of the tilemap splat. */
+const float pixel_scale = 4.0;
+
+layout(std140) uniform debug_block
+{
+ ShadowDebugData debug;
+};
+
+layout(std430, binding = 0) readonly buffer tilemaps_buf
+{
+ ShadowTileMapData tilemaps[];
+};
+
+uniform usampler2D debug_page_tx;
+uniform usampler2D tilemaps_tx;
+uniform sampler2D depth_tx;
+uniform sampler2D atlas_tx;
+
+in vec4 uvcoordsvar;
+
+layout(location = 0, index = 0) out vec4 out_color_add;
+layout(location = 0, index = 1) out vec4 out_color_mul;
+
+vec3 debug_random_color(ivec2 v)
+{
+ float r = interlieved_gradient_noise(vec2(v), 0.0, 0.0);
+ return hue_gradient(r);
+}
+
+vec3 debug_random_color(int v)
+{
+ return debug_random_color(ivec2(v, 0));
+}
+
+vec3 debug_tile_state_color(ShadowTileData tile)
+{
+ if (tile.lod > 0) {
+ return vec3(1, 0.5, 0) * float(tile.lod) / float(SHADOW_TILEMAP_LOD);
+ }
+ if (tile.do_update && tile.is_used && tile.is_visible) {
+ return vec3(1, 0, 0);
+ }
+ else if (tile.is_used && tile.is_visible) {
+ return vec3(0, 1, 0);
+ }
+ else if (tile.is_visible) {
+ return vec3(0, 0.2, 0.8);
+ }
+ return vec3(0);
+}
+
+bool debug_tilemap()
+{
+ ivec2 tile = ivec2(gl_FragCoord.xy / pixel_scale);
+ int tilemap_lod = tile.y / (SHADOW_TILEMAP_RES + 2);
+ int tilemap_index = tile.x / (SHADOW_TILEMAP_RES + 2);
+ tile = (tile % (SHADOW_TILEMAP_RES + 2)) - 1;
+ tilemap_index += debug.shadow.tilemap_index;
+ int tilemap_lod_max = (debug.light.type != LIGHT_SUN) ? SHADOW_TILEMAP_LOD : 0;
+
+ if ((tilemap_index >= debug.shadow.tilemap_index) &&
+ (tilemap_index <= debug.shadow.tilemap_last) && (tilemap_lod >= 0) &&
+ (tilemap_lod <= tilemap_lod_max) &&
+ in_range_inclusive(tile, ivec2(0), ivec2(SHADOW_TILEMAP_RES - 1))) {
+ tile >>= tilemap_lod;
+ ShadowTileData tile_data = shadow_tile_load(tilemaps_tx, tile, tilemap_lod, tilemap_index);
+ /* Write depth to overlap overlays. */
+ gl_FragDepth = 0.0;
+ out_color_add = vec4(debug_tile_state_color(tile_data), 0);
+ out_color_mul = vec4(0);
+ return true;
+ }
+ return false;
+}
+
+bool debug_tilemap_point_is_inside(vec3 P, int tilemap_index)
+{
+ int tilemap_data_index = debug.tilemap_data_index + tilemap_index - debug.shadow.tilemap_index;
+ vec3 clipP = project_point(tilemaps[tilemap_data_index].tilemat, P);
+ return in_range_inclusive(clipP, vec3(0.0), vec3(SHADOW_TILEMAP_RES));
+}
+
+/** Unlike shadow_directional_tilemap_index, returns the first tilemap overlapping the position. */
+int debug_directional_tilemap_index(vec3 P)
+{
+ for (int tilemap_index = debug.shadow.tilemap_index; tilemap_index <= debug.shadow.tilemap_last;
+ tilemap_index++) {
+ if (debug_tilemap_point_is_inside(P, tilemap_index)) {
+ return tilemap_index;
+ }
+ }
+ return -1;
+}
+
+int debug_punctual_tilemap_index(vec3 P)
+{
+ vec3 L;
+ float dist;
+ light_vector_get(debug.light, P, L, dist);
+ vec3 lL = light_world_to_local(debug.light, -L) * dist;
+ lL -= debug.shadow.offset;
+ int tilemap_index = debug.shadow.tilemap_index + shadow_punctual_face_index_get(lL);
+ if (tilemap_index > debug.shadow.tilemap_last) {
+ return -1;
+ }
+ if (debug_tilemap_point_is_inside(P, tilemap_index)) {
+ return tilemap_index;
+ }
+ return -1;
+}
+
+void debug_pages(vec3 P)
+{
+ int tilemap_index = (debug.light.type == LIGHT_SUN) ? debug_directional_tilemap_index(P) :
+ debug_punctual_tilemap_index(P);
+ if (tilemap_index != -1) {
+ int tilemap_data_index = debug.tilemap_data_index + tilemap_index - debug.shadow.tilemap_index;
+ vec3 clipP = project_point(tilemaps[tilemap_data_index].tilemat, P);
+ ivec2 tile = ivec2(clipP.xy);
+ ShadowTileData tile_data = shadow_tile_load(tilemaps_tx, tile, 0, tilemap_index);
+ vec3 color = debug_random_color(ivec2(tile_data.page));
+ out_color_add = vec4(color * 0.5, 0);
+ out_color_mul = out_color_add * 0.5 + 0.5;
+ }
+ else {
+ out_color_add = vec4(0.0);
+ out_color_mul = vec4(0.5);
+ }
+}
+
+void debug_lod(vec3 P)
+{
+ int tilemap_index = (debug.light.type == LIGHT_SUN) ? debug_directional_tilemap_index(P) :
+ debug_punctual_tilemap_index(P);
+ if (tilemap_index != -1) {
+ vec3 color = debug_random_color(tilemap_index);
+ out_color_add = vec4(color * 0.5, 0.0);
+ out_color_mul = out_color_add * 0.5 + 0.5;
+ }
+ else {
+ out_color_add = vec4(0.0);
+ out_color_mul = vec4(0.5);
+ }
+}
+
+void debug_tile_state(vec3 P)
+{
+ int tilemap_index = (debug.light.type == LIGHT_SUN) ? debug_directional_tilemap_index(P) :
+ debug_punctual_tilemap_index(P);
+ if (tilemap_index != -1) {
+ int tilemap_data_index = debug.tilemap_data_index + tilemap_index - debug.shadow.tilemap_index;
+ vec3 clipP = project_point(tilemaps[tilemap_data_index].tilemat, P);
+ ivec2 tile = ivec2(clipP.xy);
+ ShadowTileData tile_data = shadow_tile_load(tilemaps_tx, tile, 0, tilemap_index);
+ vec3 color = debug_tile_state_color(tile_data);
+ out_color_add = vec4(color * 0.5, 0);
+ out_color_mul = out_color_add * 0.5 + 0.5;
+ }
+ else {
+ out_color_add = vec4(0.0);
+ out_color_mul = vec4(0.5);
+ }
+}
+
+void debug_page_allocation(void)
+{
+ ivec2 page = ivec2(gl_FragCoord.xy / pixel_scale) - 1;
+
+ if (in_range_inclusive(page, ivec2(0), textureSize(debug_page_tx, 0).xy - 1)) {
+ uint page = texelFetch(debug_page_tx, page, 0).x;
+
+ bool error = (page & 0xFFFFu) != 1u;
+ bool is_cached = (page & SHADOW_PAGE_IS_CACHED) != 0u;
+ bool is_needed = (page & SHADOW_PAGE_IS_NEEDED) != 0u;
+ bool in_heap = (page & SHADOW_PAGE_IN_FREE_HEAP) != 0u;
+ error = error || (is_cached && !in_heap);
+ error = error || (is_needed && is_cached);
+
+ vec3 col = vec3(error, is_cached, is_needed);
+
+ out_color_add = vec4(col, 0);
+ out_color_mul = vec4(0);
+ /* Write depth to overlap overlays. */
+ gl_FragDepth = 0.0;
+ }
+}
+
+void debug_tile_allocation(void)
+{
+ ivec2 tile_co = ivec2(gl_FragCoord.xy) - 32;
+ /* Assumes tilemap buffer is squared. */
+ if (in_range_inclusive(tile_co, ivec2(0), textureSize(tilemaps_tx, 0).xy - 1)) {
+ ShadowTileData tile = shadow_tile_data_unpack(texelFetch(tilemaps_tx, tile_co, 0).x);
+ out_color_add = vec4(debug_tile_state_color(tile), 0);
+ out_color_mul = vec4(0);
+ /* Write depth to overlap overlays. */
+ gl_FragDepth = 0.0;
+ }
+}
+
+void debug_shadow_depth(vec3 P)
+{
+ vec3 L;
+ float dist;
+ light_vector_get(debug.light, P, L, dist);
+ vec3 lL = light_world_to_local(debug.light, -L) * dist;
+ lL -= debug.shadow.offset;
+ vec3 lP = transform_point(debug.shadow.mat, P);
+ float depth;
+ if (debug.light.type == LIGHT_SUN) {
+ shadow_directional_depth_get(
+ atlas_tx, tilemaps_tx, debug.light, debug.shadow, debug.camera_position, lP, P);
+ }
+ else {
+ shadow_punctual_depth_get(atlas_tx, tilemaps_tx, debug.light, debug.shadow, lL);
+ }
+ out_color_add = vec4(vec3(depth), 0);
+ out_color_mul = vec4(0);
+}
+
+void main()
+{
+ /* Default to no output. */
+ gl_FragDepth = 1.0;
+ out_color_add = vec4(0.0);
+ out_color_mul = vec4(1.0);
+
+ if (debug.type == SHADOW_DEBUG_PAGE_ALLOCATION) {
+ debug_page_allocation();
+ return;
+ }
+
+ if (debug.type == SHADOW_DEBUG_TILE_ALLOCATION) {
+ debug_tile_allocation();
+ return;
+ }
+
+ if (debug_tilemap()) {
+ return;
+ }
+
+ float depth = texelFetch(depth_tx, ivec2(gl_FragCoord.xy), 0).r;
+ vec3 P = get_world_space_from_depth(uvcoordsvar.xy, depth);
+ /* Make it pass the depth test. */
+ gl_FragDepth = depth - 1e-6;
+
+ if (depth != 1.0) {
+ switch (debug.type) {
+ case SHADOW_DEBUG_TILEMAPS:
+ debug_tile_state(P);
+ break;
+ case SHADOW_DEBUG_PAGES:
+ debug_pages(P);
+ break;
+ case SHADOW_DEBUG_LOD:
+ debug_lod(P);
+ break;
+ case SHADOW_DEBUG_SHADOW_DEPTH:
+ debug_shadow_depth(P);
+ break;
+ default:
+ discard;
+ }
+ }
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_lib.glsl
new file mode 100644
index 00000000000..a1657a830ff
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_lib.glsl
@@ -0,0 +1,128 @@
+
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shadow_page_lib.glsl)
+
+/* ---------------------------------------------------------------------- */
+/** \name Shadow Sampling Functions
+ * \{ */
+
+/* Turns local light coordinate into shadow region index. Matches eCubeFace order. */
+int shadow_punctual_face_index_get(vec3 lL)
+{
+ vec3 aP = abs(lL);
+ if (all(greaterThan(aP.xx, aP.yz))) {
+ return (lL.x > 0.0) ? 1 : 2;
+ }
+ else if (all(greaterThan(aP.yy, aP.xz))) {
+ return (lL.y > 0.0) ? 3 : 4;
+ }
+ else {
+ return (lL.z > 0.0) ? 5 : 0;
+ }
+}
+
+/* Transform vector to face local coordinate. */
+vec3 shadow_punctual_local_position_to_face_local(int face_id, vec3 lL)
+{
+ switch (face_id) {
+ case 1:
+ return vec3(-lL.y, lL.z, -lL.x);
+ case 2:
+ return vec3(lL.y, lL.z, lL.x);
+ case 3:
+ return vec3(lL.x, lL.z, -lL.y);
+ case 4:
+ return vec3(-lL.x, lL.z, lL.y);
+ case 5:
+ return vec3(lL.x, -lL.y, -lL.z);
+ default:
+ return lL;
+ }
+}
+
+float shadow_punctual_depth_get(
+ sampler2D atlas_tx, usampler2D tilemaps_tx, LightData light, ShadowData shadow, vec3 lL)
+{
+ lL -= shadow.offset;
+ int face_id = shadow_punctual_face_index_get(lL);
+ lL = shadow_punctual_local_position_to_face_local(face_id, lL);
+ /* UVs in [0..SHADOW_TILEMAP_RES] range. */
+ const float lod0_res = float(SHADOW_TILEMAP_RES / 2);
+ vec2 uv = (lL.xy / abs(lL.z)) * lod0_res + lod0_res;
+ ivec2 tile_co = ivec2(floor(uv));
+ int tilemap_index = shadow.tilemap_index + face_id;
+ ShadowTileData tile = shadow_tile_load(tilemaps_tx, tile_co, 0, tilemap_index);
+
+ /* TODO(fclem): Remove this indirection. But this means having to store another indirection
+ * table for the pages. */
+ if (tile.lod > 0u) {
+ tile_co >>= int(tile.lod);
+ tile.page = shadow_tile_load(tilemaps_tx, tile_co, int(tile.lod), tilemap_index).page;
+ }
+
+ float depth = 1.0;
+ if ((tilemap_index <= shadow.tilemap_last) && (tile.is_allocated || tile.lod > 0)) {
+ vec2 shadow_uv = shadow_page_uv_transform(tile.page, tile.lod, uv);
+ depth = texture(atlas_tx, shadow_uv).r;
+ }
+ return depth;
+}
+
+float shadow_directional_depth_get(sampler2D atlas_tx,
+ usampler2D tilemaps_tx,
+ LightData light,
+ ShadowData shadow,
+ vec3 camera_P,
+ vec3 lP,
+ vec3 P)
+{
+ int clipmap_lod = shadow_directional_clipmap_level(shadow, distance(P, camera_P));
+ int clipmap_lod_relative = clipmap_lod - shadow.clipmap_lod_min;
+ int tilemap_index = clamp(
+ shadow.tilemap_index + clipmap_lod_relative, shadow.tilemap_index, shadow.tilemap_last);
+ /* Compute how many time we need to subdivide. */
+ float clipmap_res_mul = float(1 << (shadow.clipmap_lod_max - clipmap_lod));
+ /* Compute offset of the clipmap from the largest LOD. */
+ vec2 clipmap_offset = vec2(abs(shadow.base_offset) >> clipmap_lod_relative) *
+ sign(shadow.base_offset);
+
+ vec2 uv = (lP.xy * clipmap_res_mul - clipmap_offset) + float(SHADOW_TILEMAP_RES / 2);
+ ivec2 tile_co = ivec2(floor(uv));
+ ShadowTileData tile = shadow_tile_load(tilemaps_tx, tile_co, 0, tilemap_index);
+
+ float depth = 1.0;
+ if (tile.is_allocated) {
+ vec2 shadow_uv = shadow_page_uv_transform(tile.page, 0, uv);
+ depth = texture(atlas_tx, shadow_uv).r;
+ }
+ return depth;
+}
+
+/* Returns world distance delta from light between shading point and first occluder. */
+float shadow_delta_get(sampler2D atlas_tx,
+ usampler2D tilemaps_tx,
+ LightData light,
+ ShadowData shadow,
+ vec3 lL,
+ float receiver_dist,
+ vec3 P)
+{
+ if (light.type == LIGHT_SUN) {
+ /* [-SHADOW_TILEMAP_RES/2..SHADOW_TILEMAP_RES/2] range for highest LOD. */
+ vec3 lP = transform_point(shadow.mat, P);
+ float occluder_z = shadow_directional_depth_get(
+ atlas_tx, tilemaps_tx, light, shadow, cameraPos, lP, P);
+ /* Transform to world space distance. */
+ return (lP.z - occluder_z) * abs(shadow.clip_far - shadow.clip_near);
+ }
+ else {
+ float occluder_z = shadow_punctual_depth_get(atlas_tx, tilemaps_tx, light, shadow, lL);
+ occluder_z = linear_depth(true, occluder_z, shadow.clip_far, shadow.clip_near);
+ /* Take into account the cubemap projection. We want the radial distance. */
+ float occluder_dist = receiver_dist * occluder_z / max_v3(abs(lL));
+ return receiver_dist - occluder_dist;
+ }
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_alloc_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_alloc_comp.glsl
new file mode 100644
index 00000000000..3d217cdfc95
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_alloc_comp.glsl
@@ -0,0 +1,132 @@
+
+/**
+ * Virtual shadowmapping: Schedule phase for tilemaps.
+ * This is the most complex part in the entire shadow pipeline.
+ * This step will read each updated tilemap to see if any tile is both visible and to be
+ * updated. If that is the case, it computes the bounds of the tiles to update and write it
+ * in a texture to be read back by the CPU. This is a sync step that is the main performance
+ * bottleneck of the pipeline.
+ *
+ * Unused tile might be reallocated at this stage.
+ *
+ * For each unallocated tile it will reserve a new page in the atlas. If the tile is to be
+ * rendered, it will also write the tile copy coordinates required in another buffer.
+ * This is also a slow part and should be improved in the future by moving the least amount of
+ * tiles.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shadow_page_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
+
+layout(local_size_x = SHADOW_TILEMAP_RES, local_size_y = SHADOW_TILEMAP_RES) in;
+
+layout(std430, binding = 0) restrict readonly buffer tilemaps_buf
+{
+ ShadowTileMapData tilemaps[];
+};
+
+layout(std430, binding = 1) restrict buffer pages_free_buf
+{
+ uint free_page_owners[];
+};
+
+layout(std430, binding = 3) restrict buffer pages_infos_buf
+{
+ ShadowPagesInfoData infos;
+};
+
+layout(r32ui) restrict uniform uimage2D tilemaps_img;
+layout(r32i) writeonly restrict uniform iimage2D tilemap_rects_img;
+
+void main()
+{
+ ShadowTileMapData tilemap_data = tilemaps[gl_GlobalInvocationID.z];
+ int tilemap_idx = tilemap_data.index;
+ int lod_max = tilemap_data.is_cubeface ? SHADOW_TILEMAP_LOD : 0;
+
+ int lod_valid = 0;
+ for (int lod = lod_max; lod >= 0; lod--) {
+ ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy) >> lod;
+ int tile_index = (SHADOW_TILEMAP_RES / 2) * tile_co.y + tile_co.x;
+ uint stride = 1u << lod;
+ /* We load the same data for each thread covering the same LOD tile, but we avoid
+ * allocating the same tile twice. This is because we need uniform control flow for the
+ * barriers to be valid. */
+ bool valid_thread = (gl_GlobalInvocationID.xy % stride) == uvec2(0);
+
+ ivec2 texel = shadow_tile_coord_in_atlas(tile_co, tilemap_idx, lod);
+ ShadowTileData tile = shadow_tile_data_unpack(imageLoad(tilemaps_img, texel).x);
+
+ if (valid_thread) {
+ if (tile.is_visible && tile.is_used && !tile.is_allocated) {
+ /** Tile allocation. */
+ int free_index = atomicAdd(infos.page_free_next, -1);
+ if (free_index >= 0) {
+ ivec2 owner_texel = ivec2(unpackUvec2x16(free_page_owners[free_index]));
+ free_page_owners[free_index] = uint(-1);
+
+ tile.page = shadow_tile_data_unpack(imageLoad(tilemaps_img, owner_texel).x).page;
+ tile.do_update = true;
+ tile.is_allocated = true;
+ tile.is_cached = false;
+ imageStore(tilemaps_img, texel, uvec4(shadow_tile_data_pack(tile)));
+
+ const uint flag = SHADOW_TILE_IS_ALLOCATED | SHADOW_TILE_IS_CACHED;
+ imageAtomicAnd(tilemaps_img, owner_texel, ~flag);
+ }
+ else {
+ /* Well, hum ... you blew up your budget! */
+ }
+ }
+ }
+
+ barrier();
+
+ /* Save highest quality valid lod for this thread. */
+ if (tile.is_visible && tile.is_used) {
+ lod_valid = lod;
+ }
+ else if (lod == 0) {
+ /* If the tile is not used, store the valid LOD level. */
+ /* This is tricky because the tile might be processed by another thread doing an allocation.
+ * So we need to set the LOD using atomics. */
+ uint lod_mask = 7u << 12u;
+ uint lod_store = uint(lod_valid) << 12u;
+ imageAtomicAnd(tilemaps_img, texel, ~lod_mask);
+ imageAtomicOr(tilemaps_img, texel, lod_store);
+ }
+
+ /** Compute area to render and write to buffer for CPU to read. */
+ {
+ shared ivec2 min_tile;
+ shared ivec2 max_tile;
+ ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy);
+
+ if (gl_GlobalInvocationID.xy == uvec2(0)) {
+ min_tile = ivec2(SHADOW_TILEMAP_RES - 1);
+ max_tile = ivec2(0);
+ }
+ /* Makes initial value visible to other threads. */
+ barrier();
+
+ if (valid_thread && tile.do_update && tile.is_visible && tile.is_used) {
+ atomicAdd(infos.page_updated_count, 1);
+ atomicMin(min_tile.x, tile_co.x);
+ atomicMin(min_tile.y, tile_co.y);
+ atomicMax(max_tile.x, tile_co.x);
+ atomicMax(max_tile.y, tile_co.y);
+ }
+ /* Makes final value visible to first threads. */
+ barrier();
+
+ if (gl_GlobalInvocationID.xy == uvec2(0)) {
+ max_tile += 1;
+ /* Must match the rcti structure. */
+ ivec4 out_data = ivec4(min_tile.x, max_tile.x, min_tile.y, max_tile.y);
+ imageStore(tilemap_rects_img, ivec2(lod, gl_GlobalInvocationID.z), out_data);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_copy_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_copy_comp.glsl
new file mode 100644
index 00000000000..6129428704e
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_copy_comp.glsl
@@ -0,0 +1,45 @@
+
+/**
+ * Virtual shadowmapping: Tile copy.
+ *
+ * This pass copies the pages rendered in the render target to the page atlas.
+ * This might not be the fastest way to blit shadow regions but at least it is fully GPU driven.
+ */
+
+/* TODO(fclem): The goal would be to render on the atlas texture and only move pages if
+ * they overlap with the rendering. */
+
+#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
+
+layout(local_size_x = SHADOW_PAGE_COPY_GROUP_SIZE, local_size_y = SHADOW_PAGE_COPY_GROUP_SIZE) in;
+
+uniform usampler2D tilemaps_tx;
+uniform sampler2D render_tx;
+
+/* TODO(fclem): 16bit format. */
+layout(r32f) writeonly restrict uniform image2D out_atlas_img;
+
+uniform int tilemap_index;
+uniform int tilemap_lod;
+
+void main()
+{
+ int page_size = textureSize(render_tx, 0).x / SHADOW_TILEMAP_RES;
+ int lod_size = SHADOW_TILEMAP_RES >> tilemap_lod;
+ /* TODO(fclem) Experiment with biggest dispatch instead of iterating. Or a list. */
+ for (int y = 0; y < lod_size; y++) {
+ for (int x = 0; x < lod_size; x++) {
+ ivec2 tile_co = ivec2(x, y);
+ ShadowTileData tile = shadow_tile_load(tilemaps_tx, tile_co, tilemap_lod, tilemap_index);
+ if (tile.do_update && tile.is_used && tile.is_visible && tile.is_allocated) {
+ /* We dispatch enough group to cover one page. */
+ ivec2 page_texel = ivec2(gl_GlobalInvocationID.xy);
+ ivec2 in_texel = page_texel + tile_co * page_size;
+ ivec2 out_texel = page_texel + ivec2(tile.page) * page_size;
+
+ float depth = texelFetch(render_tx, in_texel, 0).r;
+ imageStore(out_atlas_img, out_texel, vec4(depth));
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_debug_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_debug_comp.glsl
new file mode 100644
index 00000000000..a2ef545248a
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_debug_comp.glsl
@@ -0,0 +1,113 @@
+
+/**
+ * Virtual shadowmapping: Debug pages.
+ *
+ * Since pages are only existing if they are attached to a tilemap or the free list,
+ * this shader will scan every possible position and create a debug map out of it.
+ * This is nice to inspect the state of the page allocation during the pipeline.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shadow_page_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
+
+layout(local_size_x = 8, local_size_y = 8) in;
+
+layout(std430, binding = 1) readonly restrict buffer pages_free_buf
+{
+ uint free_page_owners[];
+};
+
+layout(r32ui) restrict uniform uimage2D tilemaps_img;
+
+layout(r32ui) restrict uniform uimage2D debug_img;
+
+/* Use this as custom channel viewer in renderdoc to inspect debug_img. */
+#if 0
+
+layout(binding = 2) uniform usampler2D texUInt2D;
+in vec2 uv;
+out vec4 color_out;
+
+void main()
+{
+ uint page = texture(texUInt2D, uv).x;
+
+ bool error = (page & 0xFFFFu) != 1u;
+ bool is_cached = (page & SHADOW_PAGE_IS_CACHED) != 0u;
+ bool is_needed = (page & SHADOW_PAGE_IS_NEEDED) != 0u;
+ bool in_heap = (page & SHADOW_PAGE_IN_FREE_HEAP) != 0u;
+ error = error || (is_cached && !in_heap);
+ error = error || (is_needed && is_cached);
+
+ color_out = vec4(error, is_cached, is_needed, in_heap);
+}
+
+#endif
+
+void main()
+{
+ for (int y = 0; y < imageSize(debug_img).y / int(gl_WorkGroupSize.y); y++) {
+ for (int x = 0; x < imageSize(debug_img).x / int(gl_WorkGroupSize.x); x++) {
+ ivec2 co = ivec2(x, y) * ivec2(gl_WorkGroupSize.xy) + ivec2(gl_LocalInvocationID.xy);
+ imageStore(debug_img, co, uvec4(0));
+ }
+ }
+
+ barrier();
+
+ /* TODO(fclem): We only scan the first line of tilemap, otherwise it is too slow.
+ * Finish and do it properly one day... */
+ for (int y = 0; y < imageSize(tilemaps_img).y / int(gl_WorkGroupSize.y); y++) {
+ for (int x = 0; x < imageSize(tilemaps_img).x / int(gl_WorkGroupSize.x); x++) {
+ ivec2 co = ivec2(x, y) * ivec2(gl_WorkGroupSize.xy) + ivec2(gl_LocalInvocationID.xy);
+ ShadowTileData tile = shadow_tile_data_unpack(imageLoad(tilemaps_img, co).x);
+
+ if (tile.is_allocated) {
+ /* User count. */
+ imageAtomicAdd(debug_img, ivec2(tile.page), 1u);
+ }
+ }
+ }
+
+ barrier();
+
+ for (int y = 0; y < imageSize(tilemaps_img).y / int(gl_WorkGroupSize.y); y++) {
+ for (int x = 0; x < imageSize(tilemaps_img).x / int(gl_WorkGroupSize.x); x++) {
+ ivec2 co = ivec2(x, y) * ivec2(gl_WorkGroupSize.xy) + ivec2(gl_LocalInvocationID.xy);
+ ShadowTileData tile = shadow_tile_data_unpack(imageLoad(tilemaps_img, co).x);
+
+ if (tile.is_allocated) {
+ imageAtomicOr(debug_img, ivec2(tile.page), SHADOW_PAGE_ALLOCATED);
+ if (tile.is_cached) {
+ imageAtomicOr(debug_img, ivec2(tile.page), SHADOW_PAGE_IS_CACHED);
+ /* Verify reference. */
+ ivec2 ref = ivec2(unpackUvec2x16(free_page_owners[tile.free_page_owner_index]));
+ if (ref == co) {
+ imageAtomicOr(debug_img, ivec2(tile.page), SHADOW_PAGE_IN_FREE_HEAP);
+ }
+ }
+ if (tile.is_used && tile.is_visible) {
+ imageAtomicOr(debug_img, ivec2(tile.page), SHADOW_PAGE_IS_NEEDED);
+ }
+ if (tile.do_update) {
+ imageAtomicOr(debug_img, ivec2(tile.page), SHADOW_PAGE_DO_UPDATE);
+ }
+ }
+ }
+ }
+
+#if 0
+ for (int x = 0; x < SHADOW_MAX_PAGE; x++) {
+ if (free_page_owners[x] != uint(-1)) {
+ uvec2 owner = unpackUvec2x16(free_page_owners[x]);
+ uvec2 page = shadow_tile_data_unpack(imageLoad(tilemaps_img, ivec2(owner)).x).page;
+ /* User count. */
+ imageAtomicAdd(debug_img, ivec2(page), 1u);
+
+ imageAtomicOr(debug_img, ivec2(page), SHADOW_PAGE_IN_FREE_HEAP);
+ }
+ }
+#endif
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_defrag_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_defrag_comp.glsl
new file mode 100644
index 00000000000..30f0f1e8925
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_defrag_comp.glsl
@@ -0,0 +1,59 @@
+
+/**
+ * Virtual shadowmapping: Defrag.
+ *
+ * Defragment the free page owner heap making one continuous array.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shadow_page_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
+
+layout(local_size_x = 1) in;
+
+layout(std430, binding = 1) restrict buffer pages_free_buf
+{
+ uint free_page_owners[];
+};
+
+layout(std430, binding = 3) restrict buffer pages_infos_buf
+{
+ ShadowPagesInfoData infos;
+};
+
+layout(r32ui) restrict uniform uimage2D tilemaps_img;
+
+void find_last_valid(inout uint last_valid)
+{
+ for (uint i = last_valid; i > 0u; i--) {
+ if (free_page_owners[i] != uint(-1)) {
+ last_valid = i;
+ break;
+ }
+ }
+}
+
+void main()
+{
+ uint last_valid = uint(infos.page_free_next);
+
+ find_last_valid(last_valid);
+
+ for (uint i = 0u; i < last_valid; i++) {
+ if (free_page_owners[i] == uint(-1)) {
+ free_page_owners[i] = free_page_owners[last_valid];
+
+ /* Update corresponding reference in tile. */
+ ivec2 texel = ivec2(unpackUvec2x16(free_page_owners[last_valid]));
+ ShadowTileData tile = shadow_tile_data_unpack(imageLoad(tilemaps_img, texel).x);
+ tile.free_page_owner_index = i;
+ imageStore(tilemaps_img, texel, uvec4(shadow_tile_data_pack(tile)));
+
+ free_page_owners[last_valid] = uint(-1);
+ find_last_valid(last_valid);
+ }
+ }
+
+ infos.page_free_next = int(last_valid);
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_free_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_free_comp.glsl
new file mode 100644
index 00000000000..4c013ab526c
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_free_comp.glsl
@@ -0,0 +1,73 @@
+
+/**
+ * Virtual shadowmapping: Free pages.
+ *
+ * Scan all tilemaps and add all free pages inside the free page heap.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shadow_page_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
+
+layout(local_size_x = SHADOW_TILEMAP_RES, local_size_y = SHADOW_TILEMAP_RES) in;
+
+layout(std430, binding = 0) restrict readonly buffer tilemaps_buf
+{
+ ShadowTileMapData tilemaps[];
+};
+
+layout(std430, binding = 1) restrict buffer pages_free_buf
+{
+ uint free_page_owners[];
+};
+
+layout(std430, binding = 3) restrict buffer pages_infos_buf
+{
+ ShadowPagesInfoData infos;
+};
+
+layout(r32ui) restrict uniform uimage2D tilemaps_img;
+
+void main()
+{
+ ShadowTileMapData tilemap_data = tilemaps[gl_GlobalInvocationID.z];
+ ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy);
+ int tilemap_idx = tilemap_data.index;
+
+ int lod_max = tilemap_data.is_cubeface ? SHADOW_TILEMAP_LOD : 0;
+ for (int lod = 0; lod <= lod_max; lod++) {
+ uint lod_size = SHADOW_TILEMAP_RES >> lod;
+
+ if (any(greaterThanEqual(tile_co, ivec2(lod_size)))) {
+ continue;
+ }
+
+ ivec2 texel = shadow_tile_coord_in_atlas(tile_co, tilemap_idx, lod);
+ ShadowTileData tile = shadow_tile_data_unpack(imageLoad(tilemaps_img, texel).x);
+
+ if (tile.is_allocated) {
+ if (tile.is_visible && tile.is_used && tile.is_cached) {
+ /* Try to recover cached tiles. Update flag is kept untouched as content might be valid. */
+ free_page_owners[tile.free_page_owner_index] = uint(-1);
+ tile.is_cached = false;
+ tile.free_page_owner_index = uint(-1);
+ imageStore(tilemaps_img, texel, uvec4(shadow_tile_data_pack(tile)));
+ }
+ else if ((!tile.is_visible || !tile.is_used) && !tile.is_cached) {
+ /* Push page to the free page heap. */
+ int free_index = atomicAdd(infos.page_free_next, 1) + 1;
+ if (free_index < SHADOW_MAX_PAGE) {
+ free_page_owners[free_index] = packUvec2x16(uvec2(texel));
+ tile.is_cached = true;
+ tile.free_page_owner_index = uint(free_index);
+ imageStore(tilemaps_img, texel, uvec4(shadow_tile_data_pack(tile)));
+ }
+ else {
+ /* Well, this should never happen. This would mean some pages were marked
+ * for deletion multiple times. */
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_init_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_init_comp.glsl
new file mode 100644
index 00000000000..adc00a18948
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_init_comp.glsl
@@ -0,0 +1,49 @@
+
+/**
+ * Virtual shadowmapping: Init page buffer.
+ *
+ * All pages are always owned by tiles. This step init all owners.
+ * This avoid mapping the buffer to host memory.
+ */
+
+#pragma BLENDER_REQUIRE(eevee_shadow_page_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
+
+layout(local_size_x = SHADOW_PAGE_PER_ROW) in;
+
+layout(std430, binding = 1) restrict writeonly buffer pages_free_buf
+{
+ uint free_page_owners[];
+};
+
+layout(std430, binding = 3) restrict writeonly buffer pages_infos_buf
+{
+ ShadowPagesInfoData infos;
+};
+
+layout(r32ui) restrict uniform uimage2D tilemaps_img;
+
+void main()
+{
+ if (gl_GlobalInvocationID == uvec3(0)) {
+ infos.page_free_next = SHADOW_MAX_PAGE - 1;
+ infos.page_free_next_prev = 0;
+ infos.page_updated_count = 0;
+ }
+
+ uint page_index = gl_GlobalInvocationID.x;
+
+ ivec2 texel = ivec2(page_index % (SHADOW_TILEMAP_PER_ROW * SHADOW_TILEMAP_RES),
+ page_index / (SHADOW_TILEMAP_PER_ROW * SHADOW_TILEMAP_RES));
+ free_page_owners[page_index] = packUvec2x16(uvec2(texel));
+
+ /* Start with a blank tile. */
+ ShadowTileData tile = shadow_tile_data_unpack(0u);
+ tile.page = uvec2(page_index % uint(SHADOW_PAGE_PER_ROW),
+ page_index / uint(SHADOW_PAGE_PER_ROW));
+ tile.free_page_owner_index = page_index;
+ tile.is_allocated = true;
+ tile.is_cached = true;
+ tile.do_update = true;
+ imageStore(tilemaps_img, texel, uvec4(shadow_tile_data_pack(tile)));
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_lib.glsl
new file mode 100644
index 00000000000..4dc95dfd011
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_lib.glsl
@@ -0,0 +1,17 @@
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+#define SHADOW_PAGE_ALLOCATED (1u << 16u)
+#define SHADOW_PAGE_IS_CACHED (1u << 17u)
+#define SHADOW_PAGE_IS_NEEDED (1u << 18u)
+#define SHADOW_PAGE_IN_FREE_HEAP (1u << 19u)
+#define SHADOW_PAGE_DO_UPDATE (1u << 20u)
+
+/** \a unormalized_uv is the uv coordinates for the whole tilemap [0..SHADOW_TILEMAP_RES]. */
+vec2 shadow_page_uv_transform(uvec2 page, uint lod, vec2 unormalized_uv)
+{
+ vec2 page_texel = fract(unormalized_uv / float(1u << lod));
+ /* Assumes atlas is squared. */
+ return (vec2(page) + page_texel) / vec2(SHADOW_PAGE_PER_ROW);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_mark_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_mark_vert.glsl
new file mode 100644
index 00000000000..20de208519f
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_mark_vert.glsl
@@ -0,0 +1,40 @@
+
+/**
+ * Virtual shadowmapping: Page marking / preparation
+ *
+ * This renders a series of quad to needed pages render locations.
+ * This is in order to clear the depth to 1.0 only where it is needed
+ * to occlude any potentially costly fragment shader invocation.
+ */
+
+#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
+
+uniform usampler2D tilemaps_tx;
+
+uniform int tilemap_index;
+uniform int tilemap_lod;
+
+void main()
+{
+ int tile_index = gl_VertexID / 6;
+ ivec2 tile_co = ivec2(tile_index % SHADOW_TILEMAP_RES, tile_index / SHADOW_TILEMAP_RES);
+ tile_co >>= tilemap_lod;
+
+ ShadowTileData tile = shadow_tile_load(tilemaps_tx, tile_co, tilemap_lod, tilemap_index);
+
+ if (!tile.is_visible || !tile.is_used || !tile.do_update) {
+ /* Don't draw anything as we already cleared the render target for these areas. */
+ gl_Position = vec4(0.0);
+ return;
+ }
+
+ int v = gl_VertexID % 3;
+ /* Triangle in lower left corner in [-1..1] square. */
+ vec2 pos = -1.0 + vec2((v & 1) << 1, (v & 2) << 0);
+ /* NOTE: this only renders if backface cull is off. */
+ pos = ((gl_VertexID % 6) > 2) ? -pos : pos;
+
+ pos = ((pos * 0.5 + 0.5) + vec2(tile_co)) / float(SHADOW_TILEMAP_RES >> tilemap_lod);
+
+ gl_Position = vec4(pos * 2.0 - 1.0, 1.0, 1.0);
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_depth_scan_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_depth_scan_comp.glsl
new file mode 100644
index 00000000000..cdb9a1a4bad
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_depth_scan_comp.glsl
@@ -0,0 +1,134 @@
+
+/**
+ * Virtual shadowmapping: Depth buffer scanning.
+ * We iterate through the visible lights at each scene pixel depth in order to tag only the visible
+ * shadow pages.
+ */
+
+#pragma BLENDER_REQUIRE(common_intersection_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_culling_iter_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
+
+layout(local_size_x = SHADOW_DEPTH_SCAN_GROUP_SIZE,
+ local_size_y = SHADOW_DEPTH_SCAN_GROUP_SIZE) in;
+
+layout(std430, binding = 0) readonly restrict buffer lights_buf
+{
+ LightData lights[];
+};
+
+layout(std430, binding = 1) readonly restrict buffer lights_zbins_buf
+{
+ CullingZBin lights_zbins[];
+};
+
+layout(std430, binding = 2) readonly restrict buffer lights_culling_buf
+{
+ CullingData light_culling;
+};
+
+layout(std430, binding = 3) readonly restrict buffer lights_tile_buf
+{
+ CullingWord lights_culling_words[];
+};
+
+layout(r32ui) restrict uniform uimage2D tilemaps_img;
+
+uniform sampler2D depth_tx;
+
+uniform float tilemap_pixel_radius;
+uniform float screen_pixel_radius_inv;
+
+void tag_tilemap(uint l_idx, vec3 P, float dist_to_cam, const bool is_directional)
+{
+ LightData light = lights[l_idx];
+ ShadowData shadow = light.shadow_data;
+
+ if (light.shadow_id == LIGHT_NO_SHADOW) {
+ return;
+ }
+
+ int lod = 0;
+ ivec2 tile_co;
+ int tilemap_index = shadow.tilemap_index;
+ if (is_directional) {
+ int clipmap_lod = shadow_directional_clipmap_level(shadow, dist_to_cam);
+ int clipmap_lod_relative = clipmap_lod - shadow.clipmap_lod_min;
+ /* Compute how many time we need to subdivide. */
+ float clipmap_res_mul = float(1 << (shadow.clipmap_lod_max - clipmap_lod));
+ /* Compute offset of the clipmap from the largest LOD. */
+ vec2 clipmap_offset = vec2(abs(shadow.base_offset) >> clipmap_lod_relative) *
+ sign(shadow.base_offset);
+
+ /* [-SHADOW_TILEMAP_RES/2..SHADOW_TILEMAP_RES/2] range for highest LOD. */
+ vec3 lP = transform_point(shadow.mat, P);
+ tile_co = ivec2(floor(lP.xy * clipmap_res_mul - clipmap_offset)) + SHADOW_TILEMAP_RES / 2;
+ tile_co = clamp(tile_co, ivec2(0), ivec2(SHADOW_TILEMAP_RES - 1));
+ tilemap_index += clipmap_lod_relative;
+ tilemap_index = clamp(tilemap_index, shadow.tilemap_index, shadow.tilemap_last);
+ }
+ else {
+ vec3 lL = light_world_to_local(light, P - light._position);
+ float dist_to_light = length(lL);
+ if (dist_to_light > light.influence_radius_max) {
+ return;
+ }
+ /* How much a shadow map pixel covers a final image pixel. */
+ float footprint_ratio = dist_to_light * (tilemap_pixel_radius * screen_pixel_radius_inv);
+ /* Project the radius to the screen. 1 unit away from the camera the same way
+ * pixel_world_radius_inv was computed. Not needed in orthographic mode. */
+ bool is_persp = (ProjectionMatrix[3][3] == 0.0);
+ if (is_persp) {
+ footprint_ratio /= dist_to_cam;
+ }
+ lod = int(ceil(-log2(footprint_ratio)));
+ lod = clamp(lod, 0, SHADOW_TILEMAP_LOD);
+
+ int face_id = shadow_punctual_face_index_get(lL);
+ lL = shadow_punctual_local_position_to_face_local(face_id, lL);
+
+ uint lod_res = uint(SHADOW_TILEMAP_RES) >> uint(lod);
+ tile_co = ivec2(((lL.xy / abs(lL.z)) * 0.5 + 0.5) * float(lod_res));
+ tile_co = clamp(tile_co, ivec2(0), ivec2(lod_res - 1));
+ tilemap_index += face_id;
+ }
+
+ const uint flag = SHADOW_TILE_IS_USED;
+ shadow_tile_set_flag(tilemaps_img, tile_co, lod, tilemap_index, flag);
+}
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ ivec2 tex_size = textureSize(depth_tx, 0).xy;
+
+ if (!in_range_inclusive(texel, ivec2(0), ivec2(tex_size - 1))) {
+ return;
+ }
+
+ float depth = texelFetch(depth_tx, texel, 0).r;
+ vec2 uv = vec2(texel) / vec2(tex_size);
+ vec3 vP = get_view_space_from_depth(uv, depth);
+ vec3 P = transform_point(ViewMatrixInverse, vP);
+
+ if (depth == 1.0) {
+ return;
+ }
+
+ float dist_to_cam = length(vP);
+
+ LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_culling, l_idx) {
+ tag_tilemap(l_idx, P, dist_to_cam, true);
+ }
+ LIGHT_FOREACH_END
+
+ LIGHT_FOREACH_BEGIN_LOCAL (
+ light_culling, lights_zbins, lights_culling_words, gl_GlobalInvocationID.xy, vP.z, l_idx) {
+ tag_tilemap(l_idx, P, dist_to_cam, false);
+ }
+ LIGHT_FOREACH_END
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lib.glsl
new file mode 100644
index 00000000000..39553d5eb92
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lib.glsl
@@ -0,0 +1,216 @@
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+/* ---------------------------------------------------------------------- */
+/** \name Tilemap data
+ * \{ */
+
+/** Decoded tile data structure. */
+struct ShadowTileData {
+ /** Page inside the virtual shadow map atlas. */
+ uvec2 page;
+ /** Page owner index inside free_page_owners heap. Only valid if is_cached is true. */
+ uint free_page_owner_index;
+ /** Lod pointed to by LOD 0 tile page. (cubemap only) */
+ uint lod;
+ /** Set to true during the setup phase if the tile is inside the view frustum. */
+ bool is_visible;
+ /** If the tile is needed for rendering. */
+ bool is_used;
+ /** True if this tile owns the page and if it points to a valid page. */
+ bool is_allocated;
+ /** True if an update is needed. */
+ bool do_update;
+ /** True if the tile is indexed inside the free_page_owners heap. */
+ bool is_cached;
+};
+
+#define SHADOW_TILE_NO_DATA 0u
+#define SHADOW_TILE_IS_CACHED (1u << 27u)
+#define SHADOW_TILE_IS_ALLOCATED (1u << 28u)
+#define SHADOW_TILE_DO_UPDATE (1u << 29u)
+#define SHADOW_TILE_IS_VISIBLE (1u << 30u)
+#define SHADOW_TILE_IS_USED (1u << 31u)
+
+ShadowTileData shadow_tile_data_unpack(uint data)
+{
+ ShadowTileData tile;
+ /* Tweaked for SHADOW_PAGE_PER_ROW = 64. */
+ tile.page.x = data & 63u;
+ tile.page.y = (data >> 6u) & 63u;
+ /* Tweaked for SHADOW_TILEMAP_LOD < 8. */
+ tile.lod = (data >> 12u) & 7u;
+ /* Tweaked for SHADOW_MAX_TILEMAP = 4096. */
+ tile.free_page_owner_index = (data >> 15u) & 4095u;
+ tile.is_visible = flag_test(data, SHADOW_TILE_IS_VISIBLE);
+ tile.is_used = flag_test(data, SHADOW_TILE_IS_USED);
+ tile.is_cached = flag_test(data, SHADOW_TILE_IS_CACHED);
+ tile.is_allocated = flag_test(data, SHADOW_TILE_IS_ALLOCATED);
+ tile.do_update = flag_test(data, SHADOW_TILE_DO_UPDATE);
+ return tile;
+}
+
+uint shadow_tile_data_pack(ShadowTileData tile)
+{
+ uint data;
+ data = tile.page.x;
+ data |= tile.page.y << 6u;
+ data |= tile.lod << 12u;
+ data |= tile.free_page_owner_index << 15u;
+ set_flag_from_test(data, tile.is_visible, SHADOW_TILE_IS_VISIBLE);
+ set_flag_from_test(data, tile.is_used, SHADOW_TILE_IS_USED);
+ set_flag_from_test(data, tile.is_allocated, SHADOW_TILE_IS_ALLOCATED);
+ set_flag_from_test(data, tile.is_cached, SHADOW_TILE_IS_CACHED);
+ set_flag_from_test(data, tile.do_update, SHADOW_TILE_DO_UPDATE);
+ return data;
+}
+
+int shadow_tile_index(ivec2 tile)
+{
+ return tile.x + tile.y * SHADOW_TILEMAP_RES;
+}
+
+ivec2 shadow_tile_coord(int tile_index)
+{
+ return ivec2(tile_index % SHADOW_TILEMAP_RES, tile_index / SHADOW_TILEMAP_RES);
+}
+
+/* Return bottom left pixel position of the tilemap inside the tilemap atlas. */
+ivec2 shadow_tilemap_start(int tilemap_index)
+{
+ /* Assumes base map is squared. */
+ ivec2 start = SHADOW_TILEMAP_RES * ivec2(tilemap_index % SHADOW_TILEMAP_PER_ROW,
+ tilemap_index / SHADOW_TILEMAP_PER_ROW);
+ return start;
+}
+
+/* Return bottom left pixel position of the tilemap inside the tilemap atlas. */
+ivec2 shadow_tilemap_start(int tilemap_index, int lod)
+{
+ /* Assumes base map is squared. */
+ ivec2 start = shadow_tilemap_start(tilemap_index) >> lod;
+ if (lod > 0) {
+ const int lod0_res = SHADOW_TILEMAP_RES * SHADOW_TILEMAP_PER_ROW;
+ start.y += lod0_res;
+ start.x += lod0_res - (lod0_res >> (lod - 1));
+ }
+ return start;
+}
+
+ivec2 shadow_tile_coord_in_atlas(ivec2 tile, int tilemap_index)
+{
+ return shadow_tilemap_start(tilemap_index) + tile;
+}
+
+ivec2 shadow_tile_coord_in_atlas(ivec2 tile, int tilemap_index, int lod)
+{
+ return shadow_tilemap_start(tilemap_index, lod) + tile;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Load / Store functions.
+ * \{ */
+
+void shadow_tile_store(restrict uimage2D tilemaps_img,
+ ivec2 tile_co,
+ int tilemap_index,
+ ShadowTileData data)
+{
+ uint tile_data = shadow_tile_data_pack(data);
+ imageStore(tilemaps_img, shadow_tile_coord_in_atlas(tile_co, tilemap_index), uvec4(tile_data));
+}
+
+void shadow_tile_store(
+ restrict uimage2D tilemaps_img, ivec2 tile_co, int lod, int tilemap_index, ShadowTileData data)
+{
+ uint tile_data = shadow_tile_data_pack(data);
+ imageStore(
+ tilemaps_img, shadow_tile_coord_in_atlas(tile_co, tilemap_index, lod), uvec4(tile_data));
+}
+/* Ugly define because some compilers seems to not like the fact the imageAtomicOr is inside
+ * a function. */
+#define shadow_tile_set_flag(tilemaps_img, tile_co, lod, tilemap_index, flag) \
+ imageAtomicOr(tilemaps_img, shadow_tile_coord_in_atlas(tile_co, tilemap_index, lod), flag)
+#define shadow_tile_unset_flag(tilemaps_img, tile_co, lod, tilemap_index, flag) \
+ imageAtomicAnd(tilemaps_img, shadow_tile_coord_in_atlas(tile_co, tilemap_index, lod), ~(flag))
+
+ShadowTileData shadow_tile_load(restrict uimage2D tilemaps_img,
+ ivec2 tile_co,
+ int lod,
+ int tilemap_index)
+{
+ uint tile_data = SHADOW_TILE_NO_DATA;
+ if (in_range_inclusive(tile_co, ivec2(0), ivec2(SHADOW_TILEMAP_RES - 1))) {
+ tile_data = imageLoad(tilemaps_img, shadow_tile_coord_in_atlas(tile_co, tilemap_index, lod)).x;
+ }
+ return shadow_tile_data_unpack(tile_data);
+}
+
+ShadowTileData shadow_tile_load(usampler2D tilemaps_tx, ivec2 tile_co, int lod, int tilemap_index)
+{
+ uint tile_data = SHADOW_TILE_NO_DATA;
+ if (in_range_inclusive(tile_co, ivec2(0), ivec2(SHADOW_TILEMAP_RES - 1))) {
+ tile_data =
+ texelFetch(tilemaps_tx, shadow_tile_coord_in_atlas(tile_co, tilemap_index, lod), 0).x;
+ }
+ return shadow_tile_data_unpack(tile_data);
+}
+
+/* This function should be the inverse of ShadowTileMap::tilemap_coverage_get. */
+int shadow_directional_clipmap_level(ShadowData shadow, float distance_to_camera)
+{
+ /* Why do we need to bias by 2 here? I don't know... */
+ int clipmap_lod = int(ceil(log2(distance_to_camera))) + 2;
+ return clamp(clipmap_lod, shadow.clipmap_lod_min, shadow.clipmap_lod_max);
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Frustum shapes.
+ * \{ */
+
+vec3 shadow_tile_corner_persp(ShadowTileMapData tilemap, ivec2 tile)
+{
+ return tilemap.corners[1].xyz + tilemap.corners[2].xyz * float(tile.x) +
+ tilemap.corners[3].xyz * float(tile.y);
+}
+
+Pyramid shadow_tilemap_cubeface_bounds(ShadowTileMapData tilemap,
+ ivec2 tile_start,
+ const ivec2 extent)
+{
+ Pyramid shape;
+ shape.corners[0] = tilemap.corners[0].xyz;
+ shape.corners[1] = shadow_tile_corner_persp(tilemap, tile_start + ivec2(0, 0));
+ shape.corners[2] = shadow_tile_corner_persp(tilemap, tile_start + ivec2(extent.x, 0));
+ shape.corners[3] = shadow_tile_corner_persp(tilemap, tile_start + extent);
+ shape.corners[4] = shadow_tile_corner_persp(tilemap, tile_start + ivec2(0, extent.y));
+ return shape;
+}
+
+vec3 shadow_tile_corner_ortho(ShadowTileMapData tilemap, ivec2 tile, const bool far)
+{
+ return tilemap.corners[0].xyz + tilemap.corners[1].xyz * float(tile.x) +
+ tilemap.corners[2].xyz * float(tile.y) + tilemap.corners[3].xyz * float(far);
+}
+
+Box shadow_tilemap_clipmap_bounds(ShadowTileMapData tilemap, ivec2 tile_start, const ivec2 extent)
+{
+ Box shape;
+ shape.corners[0] = shadow_tile_corner_ortho(tilemap, tile_start + ivec2(0, 0), false);
+ shape.corners[1] = shadow_tile_corner_ortho(tilemap, tile_start + ivec2(extent.x, 0), false);
+ shape.corners[2] = shadow_tile_corner_ortho(tilemap, tile_start + extent, false);
+ shape.corners[3] = shadow_tile_corner_ortho(tilemap, tile_start + ivec2(0, extent.y), false);
+ shape.corners[4] = shadow_tile_corner_ortho(tilemap, tile_start + ivec2(0, 0), true);
+ shape.corners[5] = shadow_tile_corner_ortho(tilemap, tile_start + ivec2(extent.x, 0), true);
+ shape.corners[6] = shadow_tile_corner_ortho(tilemap, tile_start + extent, true);
+ shape.corners[7] = shadow_tile_corner_ortho(tilemap, tile_start + ivec2(0, extent.y), true);
+ return shape;
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lod_mask_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lod_mask_comp.glsl
new file mode 100644
index 00000000000..3a6c1eb8128
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lod_mask_comp.glsl
@@ -0,0 +1,70 @@
+
+/**
+ * Virtual shadowmapping: LOD mask.
+ *
+ * Discard pages that are redundant in the mipmap chain.
+ * We mask tiles that are completely covered by higher LOD tiles.
+ */
+
+#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
+
+layout(local_size_x = SHADOW_TILEMAP_RES, local_size_y = SHADOW_TILEMAP_RES) in;
+
+layout(std430, binding = 0) restrict readonly buffer tilemaps_buf
+{
+ ShadowTileMapData tilemaps[];
+};
+
+layout(r32ui) restrict uniform uimage2D tilemaps_img;
+
+void main()
+{
+ ShadowTileMapData tilemap_data = tilemaps[gl_GlobalInvocationID.z];
+ int tilemap_idx = tilemap_data.index;
+ int lod_max = tilemap_data.is_cubeface ? SHADOW_TILEMAP_LOD : 0;
+
+ /* Bitmap of usage tests. Use one uint per tile. One bit per lod level. */
+ shared uint lod_map[SHADOW_TILEMAP_RES * SHADOW_TILEMAP_RES];
+
+ /* For now there is nothing to do for directional shadows. */
+ if (tilemap_data.is_cubeface) {
+ ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy);
+
+ lod_map[SHADOW_TILEMAP_RES * tile_co.y + tile_co.x] = 0u;
+
+ int map_index = tile_co.y * SHADOW_TILEMAP_RES + tile_co.x;
+ for (int lod = 0; lod <= lod_max; lod++) {
+ ivec2 tile_co_lod = ivec2(gl_GlobalInvocationID.xy) >> lod;
+ ShadowTileData tile = shadow_tile_load(tilemaps_img, tile_co_lod, lod, tilemap_idx);
+
+ if (tile.is_used && tile.is_visible) {
+ lod_map[map_index] |= 1u << uint(lod);
+ }
+ }
+
+ barrier();
+
+ /* We mask tiles from lower LOD that are completely covered by the lods above it. */
+ for (int lod = 1; lod <= SHADOW_TILEMAP_LOD; lod++) {
+ uint stride = 1u << uint(lod);
+ if ((gl_GlobalInvocationID.xy % stride) != uvec2(0)) {
+ continue;
+ }
+ uint lod_mask = ~(~0x0u << uint(lod));
+ bool tiles_covered = true;
+ for (int x = 0; x < stride && tiles_covered; x++) {
+ for (int y = 0; y < stride && tiles_covered; y++) {
+ ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy) + ivec2(x, y);
+ uint lod_bits = lod_map[tile_co.y * SHADOW_TILEMAP_RES + tile_co.x];
+ if ((lod_bits & lod_mask) == 0u) {
+ tiles_covered = false;
+ }
+ }
+ }
+ if (tiles_covered) {
+ ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy >> uint(lod));
+ shadow_tile_unset_flag(tilemaps_img, tile_co, lod, tilemap_idx, SHADOW_TILE_IS_USED);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_setup_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_setup_comp.glsl
new file mode 100644
index 00000000000..d6f01f6326e
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_setup_comp.glsl
@@ -0,0 +1,90 @@
+
+/**
+ * Virtual shadowmapping: Setup phase for tilemaps.
+ * During this phase we clear the visibility, usage and request bits.
+ * This is also where we shifts the whole tilemap for directional shadow clipmaps
+ */
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+#pragma BLENDER_REQUIRE(eevee_shadow_page_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
+
+layout(local_size_x = SHADOW_TILEMAP_RES, local_size_y = SHADOW_TILEMAP_RES) in;
+
+layout(std430, binding = 0) readonly buffer tilemaps_buf
+{
+ ShadowTileMapData tilemaps[];
+};
+
+layout(std430, binding = 1) restrict buffer pages_free_buf
+{
+ uint free_page_owners[];
+};
+
+layout(std430, binding = 3) restrict buffer pages_infos_buf
+{
+ ShadowPagesInfoData infos;
+};
+
+layout(r32ui) restrict uniform uimage2D tilemaps_img;
+
+uniform bool do_tilemap_setup;
+
+void main()
+{
+ ShadowTileMapData tilemap = tilemaps[gl_GlobalInvocationID.z];
+ ivec2 grid_shift = (do_tilemap_setup) ? tilemap.grid_shift : ivec2(0);
+ ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy);
+ ivec2 tile_shifted = tile_co + grid_shift;
+ /* Still load a valid tile after the shifting in order to not loose any page reference.
+ * This way the tile can even be reused if it is needed. Also avoid negative modulo. */
+ ivec2 tile_wrapped = (tile_shifted + SHADOW_TILEMAP_RES) % SHADOW_TILEMAP_RES;
+
+ ivec2 texel_out = shadow_tile_coord_in_atlas(tile_co, tilemap.index, 0);
+ ivec2 texel_in = shadow_tile_coord_in_atlas(tile_wrapped, tilemap.index, 0);
+
+ ShadowTileData tile_data = shadow_tile_data_unpack(imageLoad(tilemaps_img, texel_in).x);
+ tile_data.is_visible = false;
+ tile_data.is_used = false;
+ tile_data.lod = 0;
+
+ if (!in_range_inclusive(tile_shifted, ivec2(0), ivec2(SHADOW_TILEMAP_RES - 1))) {
+ /* This tile was shifted in. */
+ tile_data.do_update = true;
+ }
+
+ if (grid_shift != ivec2(0) && tile_data.is_cached) {
+ /* Update page location after shift. */
+ free_page_owners[tile_data.free_page_owner_index] = packUvec2x16(uvec2(texel_out));
+ }
+
+ imageStore(tilemaps_img, texel_out, uvec4(shadow_tile_data_pack(tile_data)));
+
+ if (tilemap.is_cubeface) {
+ /* Cubemap shift update is always all or nothing. */
+ bool do_update = (grid_shift.x != 0);
+
+ /* Number of lod0 tiles covered by the current lod level (in one dimension). */
+ uint lod_stride = 1u << 1u;
+ uint lod_size = uint(SHADOW_TILEMAP_RES) >> 1u;
+ for (int lod = 1; lod <= SHADOW_TILEMAP_LOD; lod++, lod_size >>= 1u, lod_stride <<= 1u) {
+ if (all(lessThan(tile_co, ivec2(lod_size)))) {
+ ivec2 texel = shadow_tile_coord_in_atlas(tile_co, tilemap.index, lod);
+
+ ShadowTileData tile_data = shadow_tile_data_unpack(imageLoad(tilemaps_img, texel).x);
+ tile_data.is_visible = false;
+ tile_data.is_used = false;
+ tile_data.do_update = do_update;
+ tile_data.lod = 0;
+ imageStore(tilemaps_img, texel, uvec4(shadow_tile_data_pack(tile_data)));
+ }
+ }
+ }
+
+ if (gl_GlobalInvocationID == uvec3(0)) {
+ infos.page_free_next = max(-1, infos.page_free_next);
+ infos.page_free_next_prev = infos.page_free_next;
+ infos.page_updated_count = 0;
+ }
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_tag_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_tag_comp.glsl
new file mode 100644
index 00000000000..72457880b73
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_tag_comp.glsl
@@ -0,0 +1,221 @@
+
+/**
+ * Virtual shadowmapping: AABB rendering
+ * Making entity checks on CPU can be an intensive task if scene is really complex.
+ * Some entities may need to tag certain shadow pages to be updated or needed.
+ */
+
+/**
+ * TODO(fclem) : Future plans. Do not rely on rasterization and use a single compute shader for
+ * both visibility and usage. Une one thread group per tilemap and each thread compute one AABB.
+ * Use groupshared memory to hold a bitmap of the result. Each thread "rasterize" onto the bitmap
+ * using atomicOr. Iterate through all AABBs using this threagroup and can early out if all tiles
+ * are tagged. Could even compute AABB of each batch of 64 to skip an entire batch.
+ *
+ * This would avoid relying on arbitrary point size support and be more performant.
+ */
+
+#pragma BLENDER_REQUIRE(common_intersection_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
+
+layout(local_size_x = SHADOW_AABB_TAG_GROUP_SIZE) in;
+
+layout(std430, binding = 0) readonly buffer tilemaps_buf
+{
+ ShadowTileMapData tilemaps[];
+};
+
+layout(std430, binding = 1) readonly buffer aabb_buf
+{
+ AABB aabbs[];
+};
+
+layout(r32ui) restrict uniform uimage2D tilemaps_img;
+
+uniform int aabb_len;
+uniform float tilemap_pixel_radius;
+uniform float screen_pixel_radius_inv;
+
+vec3 safe_project(ShadowTileMapData tilemap, inout int clipped, vec3 v)
+{
+ vec4 tmp = tilemap.tilemat * vec4(v, 1.0);
+ /* Detect case when point is behind the camera. */
+ clipped += int(tmp.w < 0.0);
+ return tmp.xyz / tmp.w;
+}
+
+void main()
+{
+ ShadowTileMapData tilemap = tilemaps[gl_GlobalInvocationID.z];
+
+ /* Bitmap of tile intersection tests. Use one uint per row for each LOD. */
+ shared uint flag_map[SHADOW_TILEMAP_RES * 2];
+ if (gl_LocalInvocationID.x == 0u) {
+ for (int i = 0; i < SHADOW_TILEMAP_RES * 2; i++) {
+ flag_map[i] = 0u;
+ }
+ }
+ barrier();
+
+ Pyramid frustum;
+ if (tilemap.is_cubeface) {
+ frustum = shadow_tilemap_cubeface_bounds(tilemap, ivec2(0), ivec2(SHADOW_TILEMAP_RES));
+ }
+
+ int iter = divide_ceil_i(aabb_len, int(gl_WorkGroupSize.x));
+ for (int i = 0; i < iter; i++) {
+ int aabb_index = i * int(gl_WorkGroupSize.x) + int(gl_GlobalInvocationID.x);
+ if (aabb_index >= aabb_len) {
+ break;
+ }
+ AABB aabb = aabbs[aabb_index];
+ /* Avoid completely flat object disapearing. */
+ aabb.max += 1e-6;
+ aabb.min -= 1e-6;
+
+ Box box = to_box(aabb);
+
+#ifdef TAG_USAGE
+ /* Skip non visible objects. */
+ if (!intersect_view(box)) {
+ continue;
+ }
+#endif
+
+ int lod_min = 0;
+ int lod_max = 0;
+#ifdef TAG_USAGE
+ if (tilemap.is_cubeface) {
+ /* TODO(fclem): This is imprecise as we only evaluate one point.
+ * Evaluate more point to get a range? but which ones? */
+ vec3 nearest_receiver = (aabb.min + aabb.max) * 0.5;
+ float len = distance(nearest_receiver, frustum.corners[0]);
+ /* How much a shadow map pixel covers a final image pixel. */
+ float footprint_ratio = len * (tilemap_pixel_radius * screen_pixel_radius_inv);
+ /* Project the radius to the screen. 1 unit away from the camera the same way
+ * pixel_world_radius_inv was computed. Not needed in orthographic mode. */
+ bool is_persp = (ProjectionMatrix[3][3] == 0.0);
+ if (is_persp) {
+ footprint_ratio /= distance(nearest_receiver, cameraPos);
+ }
+
+ lod_min = lod_max = int(ceil(-log2(footprint_ratio)));
+
+# if 0 /* DEBUG */
+ vec4 green = vec4(0, 1, 0, 1);
+ vec4 yellow = vec4(1, 1, 0, 1);
+ vec4 red = vec4(1, 0, 0, 1);
+ float dist_fac = (is_persp) ? distance(nearest_receiver, cameraPos) : 1.0;
+ drw_debug_point(nearest_receiver, 128.0 * dist_fac / screen_pixel_radius_inv, green);
+ drw_debug_point(nearest_receiver, len * tilemap_pixel_radius * 128.0, yellow);
+# endif
+ }
+ lod_max = clamp(lod_max, 0, SHADOW_TILEMAP_LOD);
+ lod_min = clamp(lod_min, 0, SHADOW_TILEMAP_LOD);
+#endif
+
+#ifdef TAG_UPDATE
+ // drw_debug(aabb, vec4(0, 1, 0, 1));
+#else /* TAG_USAGE */
+ // drw_debug(aabb, vec4(1, 1, 0, 1));
+#endif
+
+ int clipped = 0;
+ /* TODO(fclem) project bbox instead of AABB. */
+ /* NDC space post projection [-1..1] (unclamped). */
+ AABB aabb_ndc = init_min_max();
+ for (int v = 0; v < 8; v++) {
+ merge(aabb_ndc, safe_project(tilemap, clipped, box.corners[v]));
+ }
+
+#ifdef TAG_UPDATE
+ /* Update tag all LODs. */
+ lod_max = SHADOW_TILEMAP_LOD;
+ lod_min = 0;
+#endif
+
+ if (tilemap.is_cubeface) {
+ if (clipped == 8) {
+ /* All verts are behind the camera. */
+ continue;
+ }
+ else if (clipped > 0) {
+ /* Not all verts are behind the near clip plane. */
+ if (intersect(frustum, box)) {
+ /* We cannot correctly handle this case so we fallback by covering the whole view. */
+ aabb_ndc.max = vec3(vec2(SHADOW_TILEMAP_RES), 1.0);
+ aabb_ndc.min = vec3(0.0, 0.0, -1.0);
+ }
+ else {
+ /* Still out of the frustum. Ignore. */
+ continue;
+ }
+ }
+ else {
+ /* Reject false positive when box is on the side of the frustum but fail other tests. */
+ /* AABB of the entire light is enough for this case. */
+ AABB aabb_light = AABB(frustum.corners[0] - vec3(tilemap._punctual_distance),
+ frustum.corners[0] + vec3(tilemap._punctual_distance));
+ if (!intersect(aabb, aabb_light)) {
+ continue;
+ }
+ }
+ }
+
+ /* Discard if the bbox does not touch the rendering frustum. */
+#ifdef TAG_UPDATE
+ const float min_depth = -1.0;
+ const float max_depth = 1.0;
+#else /* TAG_USAGE */
+ float max_depth = tilemap._max_usage_depth;
+ float min_depth = tilemap._min_usage_depth;
+#endif
+ AABB aabb_tag;
+ const AABB aabb_map = AABB(vec3(0.0, 0.0, min_depth),
+ vec3(vec2(SHADOW_TILEMAP_RES) - 1e-6, max_depth));
+ if (!intersect(aabb_map, aabb_ndc, aabb_tag)) {
+ continue;
+ }
+
+ /* Raster the Box. */
+ ivec2 range_min = ivec2(aabb_tag.min.xy) >> lod_min;
+ ivec2 range_max = ivec2(aabb_tag.max.xy) >> lod_min;
+
+ for (int lod = lod_min; lod <= lod_max; lod++) {
+ for (int y = range_min.y; y <= range_max.y; y++) {
+ int flag_idx = (SHADOW_TILEMAP_RES >> lod) + y;
+ uint row_bits = bit_field_mask(range_max.x - range_min.x + 1, range_min.x);
+ atomicOr(flag_map[flag_idx], row_bits);
+ }
+ range_min >>= 1;
+ range_max >>= 1;
+ }
+ }
+
+ barrier();
+
+ if (gl_LocalInvocationID.x == 0u) {
+#ifdef TAG_UPDATE
+ const uint flag = SHADOW_TILE_DO_UPDATE;
+#else /* TAG_USAGE */
+ const uint flag = SHADOW_TILE_IS_USED;
+#endif
+ int lod_max = tilemap.is_cubeface ? SHADOW_TILEMAP_LOD : 0;
+ /* Number of lod0 tiles covered by the current lod level (in one dimension). */
+ uint lod_size = uint(SHADOW_TILEMAP_RES);
+ /* TODO(fclem): Could use multiple thread to set flag. */
+ for (int lod = 0; lod <= lod_max; lod++, lod_size >>= 1) {
+ for (int y = 0; y < lod_size; y++) {
+ int flag_idx = (SHADOW_TILEMAP_RES >> lod) + y;
+ uint row_bits = flag_map[flag_idx];
+ while (row_bits != 0u) {
+ int x = findLSB(row_bits);
+ row_bits &= ~1u << uint(x);
+ shadow_tile_set_flag(tilemaps_img, ivec2(x, y), lod, tilemap.index, flag);
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_visibility_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_visibility_comp.glsl
new file mode 100644
index 00000000000..34d6b43ea05
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_visibility_comp.glsl
@@ -0,0 +1,149 @@
+
+/**
+ * Virtual shadowmapping: Visibility phase for tilemaps.
+ * During this phase we compute the visibility of each tile for the active view frustum.
+ * TODO(fclem) Could also test visibility against Z buffer (would help in interiors space).
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_intersection_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(local_size_x = SHADOW_TILEMAP_RES, local_size_y = SHADOW_TILEMAP_RES) in;
+
+layout(std430, binding = 0) readonly buffer tilemaps_buf
+{
+ ShadowTileMapData tilemaps[];
+};
+
+uniform float tilemap_pixel_radius;
+uniform float screen_pixel_radius_inv;
+
+layout(r32ui) restrict uniform uimage2D tilemaps_img;
+
+void main()
+{
+ ShadowTileMapData tilemap = tilemaps[gl_GlobalInvocationID.z];
+ ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy);
+
+ bool is_intersecting;
+ int lod_visible_min = 0;
+ int lod_visible_max = 0;
+
+ if (tilemap.is_cubeface) {
+ Pyramid shape = shadow_tilemap_cubeface_bounds(tilemap, tile_co, ivec2(1));
+
+ is_intersecting = intersect_view(shape);
+
+ if (is_intersecting && (tilemap.cone_angle_cos > -1.0)) {
+ /* Reject tile not in spot light cone angle. */
+ vec3 tile_dir = normalize((shape.corners[3] - shape.corners[1]) * 0.5 +
+ (shape.corners[1] - shape.corners[0]));
+ /* cone_angle_cos is already biased to include the maximum tile cone half angle. */
+ if (dot(tilemap.cone_direction, tile_dir) < tilemap.cone_angle_cos) {
+ is_intersecting = false;
+ }
+ }
+
+ if (is_intersecting) {
+ /* Test minimum receiver distance and compute min and max visible LOD. */
+ float len;
+ vec3 tile_center = (shape.corners[1] + shape.corners[3]) * 0.5;
+ vec3 tile_center_dir = normalize_len(tile_center - shape.corners[0], len);
+ /* Project the tile center to the frustum and compare the shadow texel density at this
+ * position since this is where the density ratio will be the lowest (meanning the highest
+ * LOD). NOTE: There is some inacuracy because we only project one point instead of
+ * projecting each individual pixels. */
+ for (int p = 0; p < 6; p++) {
+ float facing = dot(tile_center_dir, -frustum_planes[p].xyz);
+ float d = line_plane_intersect_dist(shape.corners[0], tile_center_dir, frustum_planes[p]);
+ if (d > 0.0 && facing > 0.0) {
+ len = min(d, len);
+ }
+ }
+ vec3 nearest_receiver = shape.corners[0] + tile_center_dir * len;
+ /* How much a shadow map pixel covers a final image pixel. */
+ float footprint_ratio = len * (tilemap_pixel_radius * screen_pixel_radius_inv);
+ /* Project the radius to the screen. 1 unit away from the camera the same way
+ * pixel_world_radius_inv was computed. Not needed in orthographic mode. */
+ bool is_persp = (ProjectionMatrix[3][3] == 0.0);
+ if (is_persp) {
+ footprint_ratio /= distance(nearest_receiver, cameraPos);
+ }
+
+#if 0 /* DEBUG */
+ if (gl_GlobalInvocationID.z == 0u) {
+ vec4 green = vec4(0, 1, 0, 1);
+ vec4 yellow = vec4(1, 1, 0, 1);
+ vec4 red = vec4(1, 0, 0, 1);
+ float dist_fac = (is_persp) ? distance(nearest_receiver, cameraPos) : 1.0;
+ drw_debug_point(nearest_receiver, 128.0 * dist_fac / screen_pixel_radius_inv, green);
+ drw_debug_point(shape.corners[0] + tile_center_dir, tilemap_pixel_radius * 128.0, red);
+ drw_debug_point(nearest_receiver, len * tilemap_pixel_radius * 128.0, yellow);
+ }
+#endif
+
+ lod_visible_min = int(ceil(-log2(footprint_ratio)));
+ /* FIXME(fclem): This should be computed using the farthest intersection with the view. */
+ lod_visible_max = SHADOW_TILEMAP_LOD;
+
+ lod_visible_max = clamp(lod_visible_max, 0, SHADOW_TILEMAP_LOD);
+ lod_visible_min = clamp(lod_visible_min, 0, SHADOW_TILEMAP_LOD);
+ }
+
+ /* Bitmap of intersection tests. Use one uint per row. */
+ shared uint intersect_map[SHADOW_TILEMAP_RES];
+
+ /* Number of lod0 tiles covered by the current lod level (in one dimension). */
+ uint lod_stride = 1u;
+ uint lod_size = uint(SHADOW_TILEMAP_RES);
+ for (int lod = 1; lod <= SHADOW_TILEMAP_LOD; lod++) {
+ lod_size >>= 1;
+ lod_stride <<= 1;
+
+ barrier();
+ if (is_intersecting && lod >= lod_visible_min && lod <= lod_visible_max) {
+ atomicOr(intersect_map[tile_co.y], (1u << tile_co.x));
+ }
+ else {
+ atomicAnd(intersect_map[tile_co.y], ~(1u << tile_co.x));
+ }
+ /* Control flow is uniform inside a workgroup. */
+ barrier();
+
+ if (all(lessThan(tile_co, ivec2(lod_size)))) {
+ uint col_mask = bit_field_mask(lod_stride, lod_stride * tile_co.x);
+ bool visible = false;
+ uint row = lod_stride * tile_co.y;
+ uint row_max = lod_stride + row;
+ for (; row < row_max && !visible; row++) {
+ visible = (intersect_map[row] & col_mask) != 0;
+ }
+ if (visible) {
+ shadow_tile_set_flag(tilemaps_img, tile_co, lod, tilemap.index, SHADOW_TILE_IS_VISIBLE);
+ }
+ }
+ }
+ }
+ else {
+ /* TODO(fclem): We can save a few tile more by shaping the BBoxes in depth based on their
+ * distance to the center. */
+ Box shape = shadow_tilemap_clipmap_bounds(tilemap, tile_co, ivec2(1));
+
+ is_intersecting = intersect_view(shape);
+
+ if (is_intersecting) {
+ /* Reject tiles not in view distance. */
+ float tile_dist = length(vec2(tile_co - (SHADOW_TILEMAP_RES / 2)) + 0.5);
+ if (tile_dist > (SHADOW_TILEMAP_RES / 2) + M_SQRT2 * 0.5) {
+ is_intersecting = false;
+ }
+ }
+ }
+
+ if (is_intersecting && lod_visible_min == 0) {
+ shadow_tile_set_flag(tilemaps_img, tile_co, 0, tilemap.index, SHADOW_TILE_IS_VISIBLE);
+ }
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_subsurface_eval_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_subsurface_eval_frag.glsl
new file mode 100644
index 00000000000..eff25cc7cf8
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_subsurface_eval_frag.glsl
@@ -0,0 +1,155 @@
+
+/**
+ * Postprocess diffuse radiance output from the diffuse evaluation pass to mimic subsurface
+ * transmission.
+ *
+ * This implementation follows the technique described in the siggraph presentation:
+ * "Efficient screen space subsurface scattering Siggraph 2018"
+ * by Evgenii Golubev
+ *
+ * But, instead of having all the precomputed weights for all three color primaries,
+ * we precompute a weight profile texture to be able to support per pixel AND per channel radius.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_closure_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(std140) uniform subsurface_block
+{
+ SubsurfaceData sss;
+};
+
+layout(std140) uniform hiz_block
+{
+ HiZData hiz;
+};
+
+uniform sampler2D hiz_tx;
+uniform sampler2D radiance_tx;
+uniform sampler2D transmit_color_tx;
+uniform sampler2D transmit_normal_tx;
+uniform sampler2D transmit_data_tx;
+
+in vec4 uvcoordsvar;
+
+layout(location = 0) out vec4 out_combined;
+/* TODO(fclem) Output to diffuse pass without feedback loop. */
+
+vec3 burley_setup(vec3 radius, vec3 albedo)
+{
+ /* Scale albedo because we can have HDR value caused by BSDF sampling. */
+ vec3 A = albedo / max(1e-6, max_v3(albedo));
+ /* Diffuse surface transmission, equation (6). */
+ vec3 s = 1.9 - A + 3.5 * sqr(A - 0.8);
+ /* Mean free path length adapted to fit ancient Cubic and Gaussian models. */
+ vec3 l = 0.25 * M_1_PI * radius;
+
+ return l / s;
+}
+
+vec3 burley_eval(vec3 d, float r)
+{
+ /* Slide 33. */
+ vec3 exp_r_3_d = exp(-r / (3.0 * d));
+ vec3 exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d;
+ /** NOTE:
+ * - Surface albedo is applied at the end.
+ * - This is normalized diffuse model, so the equation is multiplied
+ * by 2*pi, which also matches cdf().
+ */
+ return (exp_r_d + exp_r_3_d) / (4.0 * d);
+}
+
+void main(void)
+{
+ vec2 center_uv = uvcoordsvar.xy;
+
+ float gbuffer_depth = texelFetch(hiz_tx, ivec2(gl_FragCoord.xy), 0).r;
+ vec3 vP = get_view_space_from_depth(center_uv, gbuffer_depth);
+ vec4 tra_col_in = texture(transmit_color_tx, center_uv);
+ vec4 tra_nor_in = texture(transmit_normal_tx, center_uv);
+ vec4 tra_dat_in = texture(transmit_data_tx, center_uv);
+
+ ClosureDiffuse diffuse = gbuffer_load_diffuse_data(tra_col_in, tra_nor_in, tra_dat_in);
+
+ if (diffuse.sss_id == 0u) {
+ /* Normal diffuse is already in combined pass. */
+ /* Refraction also go into this case. */
+ out_combined = vec4(0.0);
+ return;
+ }
+
+ float max_radius = max_v3(diffuse.sss_radius);
+
+ float homcoord = ProjectionMatrix[2][3] * vP.z + ProjectionMatrix[3][3];
+ vec2 sample_scale = vec2(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) *
+ (0.5 * max_radius / homcoord);
+
+ float pixel_footprint = sample_scale.x * float(textureSize(radiance_tx, 0).x);
+ if (pixel_footprint <= 1.0) {
+ /* Early out. */
+ out_combined = vec4(texture(radiance_tx, center_uv).rgb * diffuse.color, 0.0);
+ return;
+ }
+
+ diffuse.sss_radius = max(vec3(1e-4), diffuse.sss_radius / max_radius) * max_radius;
+ vec3 d = burley_setup(diffuse.sss_radius, diffuse.color);
+
+ /* Do not rotate too much to avoid too much cache misses. */
+ float golden_angle = M_PI * (3.0 - sqrt(5.0));
+ float theta = interlieved_gradient_noise(gl_FragCoord.xy, 0, 0.0) * golden_angle;
+ float cos_theta = cos(theta);
+ float sin_theta = sqrt(1.0 - sqr(cos_theta));
+ mat2 rot = mat2(cos_theta, sin_theta, -sin_theta, cos_theta);
+
+ mat2 scale = mat2(sample_scale.x, 0.0, 0.0, sample_scale.y);
+ mat2 sample_space = scale * rot;
+
+ vec3 accum_weight = vec3(0.0);
+ vec3 accum = vec3(0.0);
+
+ /* TODO/OPTI(fclem) Make separate sample set for lower radius. */
+
+ for (int i = 0; i < sss.sample_len; i++) {
+ vec2 sample_uv = center_uv + sample_space * sss.samples[i].xy;
+ float pdf_inv = sss.samples[i].z;
+
+ float sample_depth = textureLod(hiz_tx, sample_uv * hiz.uv_scale, 0.0).r;
+ vec3 sample_vP = get_view_space_from_depth(sample_uv, sample_depth);
+
+ vec4 sample_data = texture(radiance_tx, sample_uv);
+ vec3 sample_radiance = sample_data.rgb;
+ uint sample_sss_id = uint(sample_data.a);
+
+ if (sample_sss_id != diffuse.sss_id) {
+ continue;
+ }
+
+ /* Discard out of bounds samples. */
+ if (any(lessThan(sample_uv, vec2(0.0))) || any(greaterThan(sample_uv, vec2(1.0)))) {
+ continue;
+ }
+
+ /* Slide 34. */
+ float r = distance(sample_vP, vP);
+ vec3 weight = burley_eval(d, r) * pdf_inv;
+
+ accum += sample_radiance * weight;
+ accum_weight += weight;
+ }
+ /* Normalize the sum (slide 34). */
+ accum /= accum_weight;
+ /* Apply surface color on final radiance. */
+ accum *= diffuse.color;
+
+ /* Debug, detect NaNs. */
+ if (any(isnan(accum))) {
+ accum = vec3(1.0, 0.0, 1.0);
+ }
+
+ out_combined = vec4(accum, 0.0);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_background_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_background_frag.glsl
new file mode 100644
index 00000000000..ddfcf8eaef8
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_background_frag.glsl
@@ -0,0 +1,57 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_attribute_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_bsdf_stubs_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(location = 0) out vec4 out_background;
+
+void main(void)
+{
+ g_data = init_globals();
+ /* View position is passed to keep accuracy. */
+ g_data.N = normal_view_to_world(viewCameraVec(interp.P));
+ g_data.Ng = g_data.N;
+ g_data.P = -g_data.N + cameraPos;
+
+ attrib_load();
+
+ nodetree_surface();
+
+ out_background.rgb = safe_color(g_emission_data.emission);
+ out_background.a = saturate(avg(g_transparency_data.transmittance));
+}
+
+vec3 attr_load_orco(vec4 orco)
+{
+ return -g_data.N;
+}
+
+/* Unsupported. */
+vec4 attr_load_tangent(vec4 tangent)
+{
+ return vec4(0);
+}
+vec4 attr_load_vec4(vec4 attr)
+{
+ return vec4(0);
+}
+vec3 attr_load_vec3(vec3 attr)
+{
+ return vec3(0);
+}
+vec2 attr_load_vec2(vec2 attr)
+{
+ return vec2(0);
+}
+vec4 attr_load_color(vec4 attr)
+{
+ return vec4(0);
+}
+vec3 attr_load_uv(vec3 attr)
+{
+ return vec3(0);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_deferred_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_deferred_frag.glsl
new file mode 100644
index 00000000000..51e00ef43d3
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_deferred_frag.glsl
@@ -0,0 +1,96 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_bsdf_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(std140) uniform sampling_block
+{
+ SamplingData sampling;
+};
+
+uniform sampler2DArray utility_tx;
+
+utility_tx_fetch_define(utility_tx);
+utility_tx_sample_define(utility_tx);
+
+/* Diffuse or Transmission Color. */
+layout(location = 0) out vec3 out_transmit_color;
+/* RG: Normal (negative if Tranmission), B: SSS ID, A: Min-Thickness */
+layout(location = 1) out vec4 out_transmit_normal;
+/* RGB: SSS RGB Radius.
+ * or
+ * R: Transmission IOR, G: Transmission Roughness, B: Unused. */
+layout(location = 2) out vec3 out_transmit_data;
+/* Reflection Color. */
+layout(location = 3) out vec3 out_reflection_color;
+/* RG: Normal, B: Roughness X, A: Roughness Y. */
+layout(location = 4) out vec4 out_reflection_normal;
+/* Volume Emission, Absorption, Scatter, Phase. */
+layout(location = 5) out uvec4 out_volume_data;
+/* Emission. */
+layout(location = 6) out vec3 out_emission_data;
+/* Transparent BSDF, Holdout. */
+layout(location = 7) out vec4 out_transparency_data;
+
+void main(void)
+{
+ g_data = init_globals();
+
+ float noise_offset = sampling_rng_1D_get(sampling, SAMPLING_CLOSURE);
+ float noise = utility_tx_fetch(gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r;
+ g_data.closure_rand = fract(noise + noise_offset);
+ /* TODO(fclem) other RNG. */
+ g_data.transmit_rand = fract(g_data.closure_rand * 6.1803398875);
+
+ float thickness = nodetree_thickness();
+
+ nodetree_surface();
+
+ float alpha = saturate(1.0 - avg(g_transparency_data.transmittance));
+ vec3 V = cameraVec(g_data.P);
+
+ if (alpha > 0.0) {
+ g_diffuse_data.color /= alpha;
+ g_reflection_data.color /= alpha;
+ g_refraction_data.color /= alpha;
+ g_emission_data.emission /= alpha;
+ }
+
+ if (gl_FrontFacing) {
+ g_refraction_data.ior = safe_rcp(g_refraction_data.ior);
+ }
+
+ g_reflection_data.N = ensure_valid_reflection(g_data.Ng, V, g_reflection_data.N);
+
+ {
+ out_reflection_color = g_reflection_data.color;
+ out_reflection_normal.xy = gbuffer_encode_normal(g_reflection_data.N);
+ out_reflection_normal.z = max(1e-4, g_reflection_data.roughness);
+ }
+
+ if (g_data.transmit_rand == 0.0) {
+ out_transmit_color = g_refraction_data.color;
+ out_transmit_normal.xy = gbuffer_encode_normal(g_refraction_data.N);
+ out_transmit_normal.z = -1.0;
+ out_transmit_normal.w = thickness;
+ out_transmit_data.x = g_refraction_data.ior;
+ out_transmit_data.y = g_refraction_data.roughness;
+ }
+ else {
+ /* Output diffuse / SSS in transmit data. */
+ out_transmit_color = g_diffuse_data.color;
+ out_transmit_normal.xy = gbuffer_encode_normal(g_diffuse_data.N);
+ out_transmit_normal.z = fract(float(g_diffuse_data.sss_id) / 1024.0);
+ out_transmit_normal.w = thickness;
+ out_transmit_data = g_diffuse_data.sss_radius;
+ }
+
+ out_volume_data = gbuffer_store_volume_data(g_volume_data);
+ out_emission_data = gbuffer_store_emission_data(g_emission_data);
+ out_transparency_data = gbuffer_store_transparency_data(g_transparency_data);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_depth_frag.glsl
index fd08dfda060..f4fe834d5e0 100644
--- a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_depth_frag.glsl
@@ -1,21 +1,24 @@
-/* Required by some nodes. */
-#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
-#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
+/**
+ * Complex depth shader that stochastically discard transparent pixel.
+ */
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_diffuse_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_translucent_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_refraction_lib.glsl)
-#pragma BLENDER_REQUIRE(surface_lib.glsl)
-
-#ifdef USE_ALPHA_HASH
-
-/* From the paper "Hashed Alpha Testing" by Chris Wyman and Morgan McGuire */
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_bsdf_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl)
+
+layout(std140) uniform sampling_block
+{
+ SamplingData sampling;
+};
+
+utility_tx_sample_define_stub(utility_tx)
+
+/* From the paper "Hashed Alpha Testing" by Chris Wyman and Morgan McGuire. */
float hash(vec2 a)
{
return fract(1e4 * sin(17.0 * a.x + 0.1 * a.y) * (0.1 + abs(sin(13.0 * a.y + a.x))));
@@ -26,11 +29,11 @@ float hash3d(vec3 a)
return hash(vec2(hash(a.xy), a.z));
}
-float hashed_alpha_threshold(vec3 co)
+float hashed_alpha_threshold(float hash_scale, float hash_offset, vec3 P)
{
/* Find the discretized derivatives of our coordinates. */
- float max_deriv = max(length(dFdx(co)), length(dFdy(co)));
- float pix_scale = 1.0 / (alphaHashScale * max_deriv);
+ float max_deriv = max(length(dFdx(P)), length(dFdy(P)));
+ float pix_scale = 1.0 / (hash_scale * max_deriv);
/* Find two nearest log-discretized noise scales. */
float pix_scale_log = log2(pix_scale);
@@ -40,8 +43,8 @@ float hashed_alpha_threshold(vec3 co)
/* Compute alpha thresholds at our two noise scales. */
vec2 alpha;
- alpha.x = hash3d(floor(pix_scales.x * co));
- alpha.y = hash3d(floor(pix_scales.y * co));
+ alpha.x = hash3d(floor(pix_scales.x * P));
+ alpha.y = hash3d(floor(pix_scales.y * P));
/* Factor to interpolate lerp with. */
float fac = fract(log2(pix_scale));
@@ -60,7 +63,7 @@ float hashed_alpha_threshold(vec3 co)
float threshold = (x < one_a) ? ((x < a) ? cases.x : cases.y) : cases.z;
/* Jitter the threshold for TAA accumulation. */
- threshold = fract(threshold + alphaHashOffset);
+ threshold = fract(threshold + hash_offset);
/* Avoids threshold == 0. */
threshold = clamp(threshold, 1.0e-6, 1.0);
@@ -68,19 +71,17 @@ float hashed_alpha_threshold(vec3 co)
return threshold;
}
-#endif
-
-void main()
+void main(void)
{
-#if defined(USE_ALPHA_HASH)
+ g_data = init_globals();
- Closure cl = nodetree_exec();
+ nodetree_surface();
- float opacity = saturate(1.0 - avg(cl.transmittance));
+ float noise_offset = sampling_rng_1D_get(sampling, SAMPLING_TRANSPARENCY);
+ float random_threshold = hashed_alpha_threshold(1.0, noise_offset, g_data.P);
- /* Hashed Alpha Testing */
- if (opacity < hashed_alpha_threshold(worldPosition)) {
+ float transparency = avg(g_transparency_data.transmittance);
+ if (transparency > random_threshold) {
discard;
}
-#endif
}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_depth_simple_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_depth_simple_frag.glsl
new file mode 100644
index 00000000000..e9ac4cfcfb0
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_depth_simple_frag.glsl
@@ -0,0 +1,25 @@
+
+/**
+ * Simple passthrough shader. Outputs depth without ammendment.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_bsdf_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl)
+
+layout(std140) uniform sampling_block
+{
+ SamplingData sampling;
+};
+
+utility_tx_sample_define_stub(utility_tx)
+
+void main(void)
+{
+ /* No color output, only depth (line below is implicit). */
+ /* gl_FragDepth = gl_FragCoord.z; */
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_forward_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_forward_frag.glsl
new file mode 100644
index 00000000000..2a4f0176a08
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_forward_frag.glsl
@@ -0,0 +1,237 @@
+
+/**
+ * Forward lighting evaluation: Lighting is evaluated during the geometry rasterization.
+ *
+ * This is used by alpha blended materials and materials using Shader to RGB nodes.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_bsdf_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_culling_iter_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_raytrace_raygen_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_raytrace_trace_lib.glsl)
+
+/* TODO(fclem) Option. */
+#define USE_RAYTRACING
+
+layout(std140) uniform sampling_block
+{
+ SamplingData sampling;
+};
+
+layout(std430, binding = 0) readonly restrict buffer lights_buf
+{
+ LightData lights[];
+};
+
+layout(std430, binding = 1) readonly restrict buffer lights_zbins_buf
+{
+ CullingZBin lights_zbins[];
+};
+
+layout(std430, binding = 2) readonly restrict buffer lights_culling_buf
+{
+ CullingData light_culling;
+};
+
+layout(std430, binding = 3) readonly restrict buffer lights_tile_buf
+{
+ CullingWord lights_culling_words[];
+};
+
+layout(std140) uniform grids_block
+{
+ GridData grids[GRID_MAX];
+};
+
+layout(std140) uniform cubes_block
+{
+ CubemapData cubes[CULLING_ITEM_BATCH];
+};
+
+layout(std140) uniform lightprobes_info_block
+{
+ LightProbeInfoData probes_info;
+};
+
+layout(std140) uniform rt_diffuse_block
+{
+ RaytraceData raytrace_diffuse;
+};
+
+layout(std140) uniform rt_reflection_block
+{
+ RaytraceData raytrace_reflection;
+};
+
+layout(std140) uniform rt_refraction_block
+{
+ RaytraceData raytrace_refraction;
+};
+
+layout(std140) uniform hiz_block
+{
+ HiZData hiz;
+};
+
+uniform sampler2DArray utility_tx;
+uniform sampler2D shadow_atlas_tx;
+uniform usampler2D shadow_tilemaps_tx;
+uniform sampler1D sss_transmittance_tx;
+uniform sampler2DArray lightprobe_grid_tx;
+uniform samplerCubeArray lightprobe_cube_tx;
+uniform sampler2D hiz_tx;
+uniform sampler2D radiance_tx;
+
+utility_tx_fetch_define(utility_tx);
+utility_tx_sample_define(utility_tx);
+
+layout(location = 0, index = 0) out vec4 out_radiance;
+layout(location = 0, index = 1) out vec4 out_transmittance;
+
+/* Prototypes. */
+void light_eval(ClosureDiffuse diffuse,
+ ClosureReflection reflection,
+ vec3 P,
+ vec3 V,
+ float vP_z,
+ float thickness,
+ inout vec3 out_diffuse,
+ inout vec3 out_specular);
+vec3 lightprobe_grid_eval(vec3 P, vec3 N, float random_threshold);
+vec3 lightprobe_cubemap_eval(vec3 P, vec3 R, float roughness, float random_threshold);
+
+void main(void)
+{
+ g_data = init_globals();
+
+ float noise = utility_tx_fetch(gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r;
+ g_data.closure_rand = fract(noise + sampling_rng_1D_get(sampling, SAMPLING_CLOSURE));
+ g_data.transmit_rand = -1.0;
+
+ float thickness = nodetree_thickness();
+
+ nodetree_surface();
+
+ float vP_z = get_view_z_from_depth(gl_FragCoord.z);
+ vec3 V = cameraVec(g_data.P);
+ vec3 P = g_data.P;
+
+ float noise_probe = utility_tx_fetch(gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).g;
+ float random_probe = fract(noise_probe + sampling_rng_1D_get(sampling, SAMPLING_LIGHTPROBE));
+
+ if (gl_FrontFacing) {
+ g_refraction_data.ior = safe_rcp(g_refraction_data.ior);
+ }
+
+ g_reflection_data.N = ensure_valid_reflection(g_data.Ng, V, g_reflection_data.N);
+
+ vec3 radiance_diffuse = vec3(0);
+ vec3 radiance_reflection = vec3(0);
+ vec3 radiance_refraction = vec3(0);
+
+ light_eval(g_diffuse_data,
+ g_reflection_data,
+ P,
+ V,
+ vP_z,
+ thickness,
+ radiance_diffuse,
+ radiance_reflection);
+
+ vec4 noise_rt = utility_tx_fetch(gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).rgba;
+ vec2 noise_offset = sampling_rng_2D_get(sampling, SAMPLING_RAYTRACE_W);
+ noise_rt.zw = fract(noise_rt.zw + noise_offset);
+
+ {
+ float pdf; /* UNUSED */
+ bool hit = false;
+ Ray ray = raytrace_create_diffuse_ray(sampling, noise_rt.xy, g_diffuse_data, P, pdf);
+ ray = raytrace_world_ray_to_view(ray);
+#ifdef USE_RAYTRACING
+ /* Extend the ray to cover the whole view. */
+ ray.direction *= 1e16;
+ hit = raytrace_screen(raytrace_diffuse, hiz, hiz_tx, noise_rt.w, 1.0, false, true, ray);
+#endif
+ if (hit) {
+ vec2 hit_uv = get_uvs_from_view(ray.origin + ray.direction);
+ radiance_diffuse += textureLod(radiance_tx, hit_uv, 0.0).rgb;
+ }
+ else {
+ ray = raytrace_view_ray_to_world(ray);
+ radiance_diffuse += lightprobe_cubemap_eval(P, ray.direction, 1.0, random_probe);
+ }
+ }
+
+ {
+ float pdf; /* UNUSED */
+ bool hit = false;
+ float roughness = g_reflection_data.roughness;
+ Ray ray = raytrace_create_reflection_ray(sampling, noise_rt.xy, g_reflection_data, V, P, pdf);
+ ray = raytrace_world_ray_to_view(ray);
+#ifdef USE_RAYTRACING
+ if (roughness - noise_rt.z * 0.2 < raytrace_reflection.max_roughness) {
+ /* Extend the ray to cover the whole view. */
+ ray.direction *= 1e16;
+ hit = raytrace_screen(
+ raytrace_reflection, hiz, hiz_tx, noise_rt.w, roughness, false, true, ray);
+ }
+#endif
+ if (hit) {
+ vec2 hit_uv = get_uvs_from_view(ray.origin + ray.direction);
+ radiance_reflection += textureLod(radiance_tx, hit_uv, 0.0).rgb;
+ }
+ else {
+ ray = raytrace_view_ray_to_world(ray);
+ radiance_reflection += lightprobe_cubemap_eval(
+ P, ray.direction, sqr(roughness), random_probe);
+ }
+ }
+
+ {
+ float pdf; /* UNUSED */
+ bool hit = false;
+ float roughness = g_refraction_data.roughness;
+ Ray ray = raytrace_create_refraction_ray(sampling, noise_rt.xy, g_refraction_data, V, P, pdf);
+ ray = raytrace_world_ray_to_view(ray);
+#ifdef USE_RAYTRACING
+ if (roughness - noise_rt.z * 0.2 < raytrace_refraction.max_roughness) {
+ /* Extend the ray to cover the whole view. */
+ ray.direction *= 1e16;
+ /* TODO(fclem): Take IOR into account in the roughness LOD bias. */
+ hit = raytrace_screen(
+ raytrace_refraction, hiz, hiz_tx, noise_rt.w, roughness, false, true, ray);
+ }
+#endif
+ if (hit) {
+ vec2 hit_uv = get_uvs_from_view(ray.origin + ray.direction);
+ radiance_refraction += textureLod(radiance_tx, hit_uv, 0.0).rgb;
+ }
+ else {
+ ray = raytrace_view_ray_to_world(ray);
+ radiance_refraction += lightprobe_cubemap_eval(
+ P, ray.direction, sqr(roughness), random_probe);
+ }
+ }
+
+ // volume_eval(ray, volume_radiance, volume_transmittance, volume_depth);
+
+ out_radiance.rgb = radiance_diffuse * g_diffuse_data.color;
+ out_radiance.rgb += radiance_reflection * g_reflection_data.color;
+ out_radiance.rgb += radiance_refraction * g_refraction_data.color;
+ out_radiance.rgb += g_emission_data.emission;
+ out_radiance.a = 0.0;
+
+ out_transmittance.rgb = g_transparency_data.transmittance;
+ out_transmittance.a = saturate(avg(out_transmittance.rgb));
+}
+
+#pragma BLENDER_REQUIRE_POST(eevee_light_eval_lib.glsl)
+#pragma BLENDER_REQUIRE_POST(eevee_lightprobe_eval_cubemap_lib.glsl)
+#pragma BLENDER_REQUIRE_POST(eevee_lightprobe_eval_grid_lib.glsl)
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_gpencil_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_gpencil_vert.glsl
new file mode 100644
index 00000000000..d69aa22b3e0
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_gpencil_vert.glsl
@@ -0,0 +1,86 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_attribute_lib.glsl)
+#pragma BLENDER_REQUIRE(common_gpencil_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl)
+
+in ivec4 ma, ma1, ma2, ma3;
+in vec4 pos, pos1, pos2, pos3, uv1, uv2, col1, col2, fcol1;
+
+/* Globals to feed the load functions */
+vec2 uvs;
+vec4 color;
+
+/* TODO(fclem) remove use of macro. use interface block instead. */
+RESOURCE_ID_VARYING
+
+void main(void)
+{
+ vec4 sspos;
+ vec2 aspect;
+ vec2 thickness;
+
+ gl_Position = gpencil_vertex(ma,
+ ma1,
+ ma2,
+ ma3,
+ pos,
+ pos1,
+ pos2,
+ pos3,
+ uv1,
+ uv2,
+ col1,
+ col2,
+ fcol1,
+ vec4(ViewportSize, ViewportSizeInverse),
+ interp.P,
+ interp.N,
+ color,
+ uvs,
+ sspos,
+ aspect,
+ thickness);
+
+ interp.barycentric_coords = vec2(0.0);
+ interp.barycentric_dists = vec3(0.0);
+
+ PASS_RESOURCE_ID
+ attrib_load();
+}
+
+vec3 attr_load_orco(vec4 orco)
+{
+ vec3 lP = point_world_to_object(interp.P);
+ return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz;
+}
+
+vec4 attr_load_tangent(vec4 tangent)
+{
+ /* TODO */
+ return vec4(0.0, 0.0, 0.0, 1.0);
+}
+
+/* Only have one uv and one color attribute layer. */
+vec3 attr_load_uv(vec3 dummy)
+{
+ return vec3(uvs, 0.0);
+}
+vec4 attr_load_color(vec4 dummy)
+{
+ return color;
+}
+
+/* Not supported. */
+vec4 attr_load_vec4(vec4 attr)
+{
+ return vec4(0.0);
+}
+vec3 attr_load_vec3(vec3 attr)
+{
+ return vec3(0.0);
+}
+vec2 attr_load_vec2(vec2 attr)
+{
+ return vec2(0.0);
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_hair_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_hair_vert.glsl
new file mode 100644
index 00000000000..ed749b33463
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_hair_vert.glsl
@@ -0,0 +1,80 @@
+
+#pragma BLENDER_REQUIRE(common_attribute_lib.glsl)
+#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl)
+
+/* TODO(fclem) remove use of macro. use interface block instead. */
+RESOURCE_ID_VARYING
+
+/* Globals to feed the load functions. */
+vec3 T;
+
+void main(void)
+{
+ bool is_persp = (ProjectionMatrix[3][3] == 0.0);
+ hair_get_pos_tan_binor_time(is_persp,
+ ModelMatrixInverse,
+ ViewMatrixInverse[3].xyz,
+ ViewMatrixInverse[2].xyz,
+ interp.P,
+ T,
+ interp.hair_binormal,
+ interp.hair_time,
+ interp.hair_thickness,
+ interp.hair_time_width);
+
+ interp.N = cross(T, interp.hair_binormal);
+ interp.hair_strand_id = hair_get_strand_id();
+ interp.barycentric_coords = hair_get_barycentric();
+
+ PASS_RESOURCE_ID
+ attrib_load();
+
+ g_data = init_globals();
+ interp.P += nodetree_displacement();
+
+ gl_Position = point_world_to_ndc(interp.P);
+}
+
+#ifdef OBINFO_LIB
+vec3 attr_load_orco(samplerBuffer cd_buf)
+{
+ vec3 P = hair_get_strand_pos();
+ vec3 lP = transform_point(ModelMatrixInverse, P);
+ return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz;
+}
+#endif
+
+vec4 attr_load_tangent(samplerBuffer cd_buf)
+{
+ /* Not supported. */
+ return vec4(0.0, 0.0, 0.0, 1.0);
+}
+
+vec3 attr_load_uv(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, interp.hair_strand_id).rgb;
+}
+
+vec4 attr_load_color(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, interp.hair_strand_id).rgba;
+}
+
+vec4 attr_load_vec4(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, interp.hair_strand_id).rgba;
+}
+
+vec3 attr_load_vec3(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, interp.hair_strand_id).rgb;
+}
+
+vec2 attr_load_vec2(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, interp.hair_strand_id).rg;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_lib.glsl
new file mode 100644
index 00000000000..d20e7860a38
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_lib.glsl
@@ -0,0 +1,63 @@
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl)
+
+IN_OUT SurfaceInterface
+{
+ vec3 P;
+ vec3 N;
+ vec2 barycentric_coords;
+ flat vec3 barycentric_dists;
+ vec3 hair_binormal;
+ float hair_time;
+ float hair_time_width;
+ float hair_thickness;
+ flat int hair_strand_id;
+}
+interp;
+
+#if defined(GPU_FRAGMENT_SHADER) || defined(GPU_VERTEX_SHADER)
+GlobalData init_globals(void)
+{
+ GlobalData surf;
+ surf.P = interp.P;
+ surf.N = normalize(interp.N);
+# ifndef MAT_GEOM_GPENCIL
+ surf.N = (FrontFacing) ? surf.N : -surf.N;
+# endif
+# ifdef GPU_FRAGMENT_SHADER
+ surf.Ng = safe_normalize(cross(dFdx(surf.P), dFdy(surf.P)));
+# else
+ surf.Ng = surf.N;
+# endif
+
+# ifdef MAT_GEOM_HAIR
+ /* Shade as a cylinder. */
+ float cos_theta = interp.hair_time_width / interp.hair_thickness;
+ float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta));
+ surf.N = normalize(interp.N * sin_theta + interp.hair_binormal * cos_theta);
+# endif
+
+# ifdef MAT_GEOM_HAIR
+ surf.is_strand = true;
+ surf.hair_time = interp.hair_time;
+ surf.hair_thickness = interp.hair_thickness;
+ surf.hair_strand_id = interp.hair_strand_id;
+ surf.barycentric_coords = hair_resolve_barycentric(interp.barycentric_coords);
+# else
+ surf.is_strand = false;
+ surf.hair_time = 0.0;
+ surf.hair_thickness = 0.0;
+ surf.hair_strand_id = 0;
+ surf.barycentric_coords = interp.barycentric_coords;
+# endif
+ surf.barycentric_dists = interp.barycentric_dists;
+ surf.ray_type = RAY_TYPE_CAMERA;
+ surf.ray_depth = 0.0;
+ surf.ray_length = distance(surf.P, cameraPos);
+ surf.closure_rand = 0.5;
+ return surf;
+}
+#endif
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_lookdev_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_lookdev_vert.glsl
new file mode 100644
index 00000000000..1fc84f1f267
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_lookdev_vert.glsl
@@ -0,0 +1,34 @@
+
+/**
+ * Custom vertex shader for rendering the lookdev overlay (reference material spheres).
+ * The input mesh is a sphere. The output is a flattened version that will render at depth 0.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl)
+
+in vec3 pos;
+in vec3 nor;
+
+/* TODO(fclem) remove use of macro. use interface block instead. */
+RESOURCE_ID_VARYING
+
+void main(void)
+{
+ interp.P = pos;
+ interp.N = nor;
+ interp.barycentric_coords = vec2(0.0);
+ interp.barycentric_dists = vec3(0.0);
+
+ PASS_RESOURCE_ID
+
+ /* Camera transform is passed via the model matrix. */
+ gl_Position.xyz = transform_direction(ModelMatrix, interp.P);
+ /* Apply packed bias & scale. */
+ gl_Position.xy *= ModelMatrix[3].xy;
+ gl_Position.xy += ModelMatrix[3].zw;
+
+ /* Override depth. */
+ gl_Position.z = -1.0;
+ gl_Position.w = 1.0;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_mesh_geom.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_mesh_geom.glsl
new file mode 100644
index 00000000000..29558a0958e
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_mesh_geom.glsl
@@ -0,0 +1,33 @@
+
+/**
+ * Optional geometry shader stage to compute barycentric coords
+ * Only needed / compatible with mesh or gpencil geometry.
+ * Main is generated in eevee_shader.cc to avoid compilation issue on some drivers.
+ */
+
+layout(triangles) in;
+layout(triangle_strip, max_vertices = 3) out;
+
+vec3 calc_barycentric_distances(vec3 pos0, vec3 pos1, vec3 pos2)
+{
+ vec3 edge21 = pos2 - pos1;
+ vec3 edge10 = pos1 - pos0;
+ vec3 edge02 = pos0 - pos2;
+ vec3 d21 = normalize(edge21);
+ vec3 d10 = normalize(edge10);
+ vec3 d02 = normalize(edge02);
+
+ vec3 dists;
+ float d = dot(d21, edge02);
+ dists.x = sqrt(dot(edge02, edge02) - d * d);
+ d = dot(d02, edge10);
+ dists.y = sqrt(dot(edge10, edge10) - d * d);
+ d = dot(d10, edge21);
+ dists.z = sqrt(dot(edge21, edge21) - d * d);
+ return dists;
+}
+
+vec2 calc_barycentric_co(int vertid)
+{
+ return vec2((vertid % 3) == 0, (vertid % 3) == 1);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_mesh_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_mesh_vert.glsl
new file mode 100644
index 00000000000..f29cf57c1a8
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_mesh_vert.glsl
@@ -0,0 +1,72 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_attribute_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl)
+
+in vec3 pos;
+in vec3 nor;
+
+/* TODO(fclem) remove use of macro. use interface block instead. */
+RESOURCE_ID_VARYING
+
+void main(void)
+{
+ interp.P = point_object_to_world(pos);
+ interp.N = normal_object_to_world(nor);
+ interp.barycentric_coords = vec2(0.0);
+ interp.barycentric_dists = vec3(0.0);
+
+ PASS_RESOURCE_ID
+ attrib_load();
+
+ g_data = init_globals();
+
+ interp.P += nodetree_displacement();
+
+ gl_Position = point_world_to_ndc(interp.P);
+}
+
+#ifdef OBINFO_LIB
+vec3 attr_load_orco(vec4 orco)
+{
+ /* We know when there is no orco layer when orco.w is 1.0 because it uses the generic vertex
+ * attrib (which is [0,0,0,1]). */
+ if (orco.w == 0.0) {
+ return orco.xyz * 0.5 + 0.5;
+ }
+ else {
+ /* If the object does not have any deformation, the orco layer calculation is done on the fly
+ * using the orco_madd factors. */
+ return OrcoTexCoFactors[0].xyz + pos * OrcoTexCoFactors[1].xyz;
+ }
+}
+#endif
+
+vec4 attr_load_tangent(vec4 tangent)
+{
+ tangent.xyz = safe_normalize(normal_object_to_world(tangent.xyz));
+ return tangent;
+}
+
+/* Simple passthrough. */
+vec4 attr_load_vec4(vec4 attr)
+{
+ return attr;
+}
+vec3 attr_load_vec3(vec3 attr)
+{
+ return attr;
+}
+vec2 attr_load_vec2(vec2 attr)
+{
+ return attr;
+}
+vec4 attr_load_color(vec4 attr)
+{
+ return attr;
+}
+vec3 attr_load_uv(vec3 attr)
+{
+ return attr;
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_frag.glsl
new file mode 100644
index 00000000000..06e3eea9413
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_frag.glsl
@@ -0,0 +1,43 @@
+
+/**
+ * Output two 2D screen space velocity vector from object motion.
+ * There is a separate output for view and camera vectors.
+ * Camera vectors are used for reprojections and view vectors are used for motion blur fx.
+ * xy: Previous position > Current position
+ * zw: Current position > Next position
+ **/
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surface_velocity_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(std140) uniform camera_prev_block
+{
+ CameraData cam_prev;
+};
+
+layout(std140) uniform camera_curr_block
+{
+ CameraData cam_curr;
+};
+
+layout(std140) uniform camera_next_block
+{
+ CameraData cam_next;
+};
+
+layout(location = 0) out vec4 out_velocity_camera;
+layout(location = 1) out vec4 out_velocity_view;
+
+void main(void)
+{
+ compute_velocity(interp.P_prev,
+ interp.P,
+ interp.P_next,
+ cam_prev,
+ cam_curr,
+ cam_next,
+ out_velocity_camera,
+ out_velocity_view);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_lib.glsl
new file mode 100644
index 00000000000..a1c1075b472
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_lib.glsl
@@ -0,0 +1,8 @@
+
+IN_OUT VelocityInterface
+{
+ vec3 P;
+ vec3 P_next;
+ vec3 P_prev;
+}
+interp;
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_mesh_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_mesh_vert.glsl
new file mode 100644
index 00000000000..4abadc9f18d
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_mesh_vert.glsl
@@ -0,0 +1,40 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surface_velocity_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+
+layout(std140) uniform object_block
+{
+ VelocityObjectData velocity;
+};
+
+uniform int data_offset;
+
+in vec3 pos;
+in vec3 prv;
+in vec3 nxt;
+
+vec3 velocity_object_to_world_prev(VelocityObjectData data, vec3 prev_pos, vec3 current_pos)
+{
+ /* Encoded use_deform inside the matrix to save up space. */
+ bool use_deform = data.next_object_mat[3][3] == 0.0;
+ return transform_point(data.prev_object_mat, use_deform ? prev_pos : current_pos);
+}
+
+vec3 velocity_object_to_world_next(VelocityObjectData data, vec3 next_pos, vec3 current_pos)
+{
+ /* Encoded use_deform inside the matrix to save up space. */
+ bool use_deform = data.next_object_mat[3][3] == 0.0;
+ mat4 obmat = data.next_object_mat;
+ obmat[3][3] = 1.0;
+ return transform_point(obmat, use_deform ? next_pos : current_pos);
+}
+
+void main(void)
+{
+ interp.P = point_object_to_world(pos);
+ interp.P_prev = velocity_object_to_world_prev(velocity, prv, pos);
+ interp.P_next = velocity_object_to_world_next(velocity, nxt, pos);
+
+ gl_Position = point_world_to_ndc(interp.P);
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_world_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_world_vert.glsl
new file mode 100644
index 00000000000..bdff0582b45
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_world_vert.glsl
@@ -0,0 +1,26 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl)
+
+/* TODO(fclem) remove use of macro. use interface block instead. */
+RESOURCE_ID_VARYING
+
+void main(void)
+{
+ /* Fullscreen triangle. */
+ int v = gl_VertexID % 3;
+ float x = float((v & 1) << 2) - 1.0;
+ float y = float((v & 2) << 1) - 1.0;
+ gl_Position = vec4(x, y, 1.0, 1.0);
+
+ /* Pass view position to keep accuracy. */
+ interp.P = project_point(ProjectionMatrixInverse, gl_Position.xyz);
+ interp.N = vec3(1);
+ /* Unsupported. */
+ interp.barycentric_coords = vec2(0.0);
+ interp.barycentric_dists = vec3(0.0);
+
+ /* Used to pass the correct model matrix. */
+ PASS_RESOURCE_ID
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_velocity_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_velocity_lib.glsl
new file mode 100644
index 00000000000..766cb9b8cce
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_velocity_lib.glsl
@@ -0,0 +1,38 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)
+#pragma BLENDER_REQUIRE(eevee_camera_lib.glsl)
+
+void compute_velocity(vec3 P_prev,
+ vec3 P,
+ vec3 P_next,
+ CameraData cam_prev,
+ CameraData cam_curr,
+ CameraData cam_next,
+ out vec4 velocity_camera,
+ out vec4 velocity_view)
+{
+ vec2 prev_uv, curr_uv, next_uv;
+ prev_uv = camera_uv_from_world(cam_prev, P_prev);
+ curr_uv = camera_uv_from_world(cam_curr, P);
+ next_uv = camera_uv_from_world(cam_next, P_next);
+
+ velocity_camera.xy = prev_uv - curr_uv;
+ velocity_camera.zw = curr_uv - next_uv;
+
+ if (is_panoramic(cam_curr.type)) {
+ /* This path is only used if using using panoramic projections. Since the views always have
+ * the same 45° aperture angle, we can safely reuse the projection matrix. */
+ prev_uv = transform_point(ProjectionMatrix, transform_point(cam_prev.viewmat, P_prev)).xy;
+ curr_uv = transform_point(ViewProjectionMatrix, P).xy;
+ next_uv = transform_point(ProjectionMatrix, transform_point(cam_next.viewmat, P_next)).xy;
+
+ velocity_view.xy = prev_uv - curr_uv;
+ velocity_view.zw = curr_uv - next_uv;
+ /* Convert NDC velocity to UV velocity */
+ velocity_view *= 0.5;
+ }
+ else {
+ velocity_view = velocity_camera;
+ }
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_volume_deferred_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_volume_deferred_frag.glsl
new file mode 100644
index 00000000000..359480e9e3c
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_volume_deferred_frag.glsl
@@ -0,0 +1,66 @@
+
+/**
+ * Renders heterogeneous volumes.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_volume_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_volume_eval_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
+
+uniform sampler2D depth_max_tx;
+
+layout(location = 0) out uvec4 out_volume_data; /* Volume Emission, Absorption, Scatter. */
+layout(location = 1) out vec4 out_transparency_data; /* Transparent BSDF, Holdout. */
+
+void main(void)
+{
+ // g_volume = init_globals();
+
+ vec2 uv = gl_FragCoord.xy / vec2(textureSize(depth_max_tx, 0).xy);
+
+ /* For volumes from solid objects. */
+ vec3 vP_start = get_view_space_from_depth(uv, gl_FragCoord.z);
+ vec3 vP_end = get_view_space_from_depth(uv, texture(depth_max_tx, uv).r);
+
+ Ray ray;
+ ray.origin = vP_start;
+ ray.direction = vP_end - vP_start;
+ ray.direction /= abs(ray.direction.z);
+ ray.max_time = max(vP_start.z - vP_end.z, 0.0);
+
+ /* Refine bounds to skip empty areas. */
+ // float dist = line_unit_box_intersect_dist(ls_ray_ori, ls_ray_dir);
+ // if (dist > 0.0) {
+ // ls_ray_ori = ls_ray_dir * dist + ls_ray_ori;
+ // }
+
+ // vec3 ls_vol_isect = ls_ray_end - ls_ray_ori;
+ // if (dot(ls_ray_dir, ls_vol_isect) < 0.0) {
+ // /* Start is further away than the end.
+ // * That means no volume is intersected. */
+ // discard;
+ // }
+
+ vec3 out_depth_time;
+ vec3 out_radiance = vec3(0.0);
+ vec3 out_transmittance = vec3(1.0);
+ // volume_eval_scattering_transmittance(
+ // P, depth_min, depth_max, out_radiance, out_transmittance, gl_FragDepth);
+
+ volume_eval_homogenous(ray, out_transmittance, out_depth_time);
+
+ gl_FragDepth = get_depth_from_view_z(ray.origin.z - avg(out_depth_time));
+
+ g_volume_data.emission = vec3(0);
+ g_volume_data.scattering = out_radiance;
+ g_volume_data.transmittance = out_transmittance;
+ g_volume_data.anisotropy = VOLUME_HETEROGENEOUS;
+
+ g_transparency_data.transmittance = vec3(1.0);
+ g_transparency_data.holdout = 0.0;
+
+ out_volume_data = gbuffer_store_volume_data(g_volume_data);
+ out_transparency_data = gbuffer_store_transparency_data(g_transparency_data);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_volume_eval_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_volume_eval_lib.glsl
new file mode 100644
index 00000000000..272b43047e9
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_volume_eval_lib.glsl
@@ -0,0 +1,90 @@
+
+#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+
+struct Ray {
+ vec3 origin;
+ vec3 direction;
+ float max_time;
+};
+
+vec3 volume_eval_light(vec3 P, float anisotropy)
+{
+ vec3 light_out = vec3(0.0);
+ // LIGHT_ITER(g_volume)
+ // {
+ // LightData ld = lights_data[i];
+
+ // if (ld.l_volume == 0.0) {
+ // continue;
+ // }
+
+ // vec3 L;
+ // float dist;
+ // light_vector_get(light, P, L, dist);
+
+ // float visibility = light_attenuation(light, L, dist);
+
+ // if (light.shadow_id != LIGHT_NO_SHADOW && visibility > 0.0) {
+ // vec3 lL = light_world_to_local(light, -L) * dist;
+ // vec3 shadow_co = shadow_punctual_coordinates_get(shadows[l_idx], lL);
+ // visibility *= texture(shadow_atlas_tx, shadow_co);
+ // }
+
+ // if (visibility > 1e-4) {
+ // float intensity = light_volume(utility_tx, light, V, L, dist, anisotropy);
+ // light_out += light.color * (intensity * visibility);
+ // }
+ // }
+ return light_out;
+}
+
+void volume_eval(vec3 P, vec3 V, float max_time, out vec3 out_radiance, out vec3 out_transmittance)
+{
+ // out_radiance = vec3(0);
+ // out_transmittance = vec3(1);
+
+ // VOLUME_ITER(g_volume)
+ // {
+ // nodetree_eval();
+
+ // vec3 scattered_light = g_volume_data.emission +
+ // g_volume_data.scattering * volume_eval_light(P,
+ // g_volume_data.anisotropy);
+
+ // /* Emission does not work if there is no extinction because
+ // * Tr evaluates to 1.0 leading to Lscat = 0.0. (See T65771) */
+ // /* s_extinction. */
+ // g_volume_data.transmittance = max(vec3(1e-7) * step(1e-5, Lscat),
+ // g_volume_data.transmittance);
+ // /* Evaluate Scattering. */
+ // float s_len = abs(ray_len - prev_ray_len);
+ // prev_ray_len = ray_len;
+ // vec3 Tr = exp(-g_volume_data.transmittance * s_len);
+ // /* Integrate along the current step segment. */
+ // scattered_light = (scattered_light - scattered_light * Tr) *
+ // safe_rcp(g_volume_data.transmittance);
+ // /* Accumulate and also take into account the transmittance from previous steps. */
+ // out_radiance += out_transmittance * scattered_light;
+ // out_transmittance *= Tr;
+
+ // if (all(lessThan(out_transmittance, vec3(1e-4)))) {
+ // break;
+ // }
+ // }
+}
+
+/* Simple version that compute transmittance only. */
+void volume_eval_homogenous(Ray ray, inout vec3 out_transmittance, out vec3 out_depth_time)
+{
+ // nodetree_eval();
+
+ float step_len = length(ray.direction);
+
+ out_transmittance *= exp(-g_volume_data.transmittance * step_len * ray.max_time);
+
+ float threshold = interlieved_gradient_noise(gl_FragCoord.xy, 0, 0.0);
+ /* Find depth at which we have threshold opacity. */
+ out_depth_time = log(max(threshold, 1e-6)) * safe_rcp(-g_volume_data.transmittance * step_len);
+ out_depth_time = min(out_depth_time, vec3(ray.max_time));
+}
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_volume_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_volume_lib.glsl
new file mode 100644
index 00000000000..da39c842579
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_volume_lib.glsl
@@ -0,0 +1,16 @@
+
+IN_OUT VolumeDataInterface
+{
+ vec3 P_start;
+ vec3 P_end;
+}
+interp;
+
+#ifdef GPU_FRAGMENT_SHADER
+GlobalData init_globals(void)
+{
+ GlobalData volume;
+ volume.P = interp.P_start;
+ return volume;
+}
+#endif
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_volume_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_volume_vert.glsl
new file mode 100644
index 00000000000..c679870bc27
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_volume_vert.glsl
@@ -0,0 +1,34 @@
+
+/**
+ * Renders volume objects with no surfaces.
+ *
+ * The vertex shader outputs geometry at nearest depth.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_volume_lib.glsl)
+
+in vec3 pos;
+
+void main(void)
+{
+ /* TODO(fclem) Make the quad cover only the bounding box. */
+
+ // int v = gl_VertexID % 4;
+ // float x = -1.0 + float((v & 1) << 2);
+ // float y = -1.0 + float((v & 2) << 1);
+ // gl_Position = vec4(x, y, 1.0, 1.0);
+
+ // vec3 aabb_min,;
+
+ // uvcoordsvar = vec4((gl_Position.xy + 1.0) * 0.5, 0.0, 0.0);
+
+ // interp.P_start = point_ndc_to_world(pos);
+ // interp.P_end = point_ndc_to_world(pos);
+
+ interp.P_start = point_object_to_world(pos);
+ interp.P_end = interp.P_start;
+
+ gl_Position = point_world_to_ndc(interp.P_start);
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/effect_bloom_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_bloom_frag.glsl
deleted file mode 100644
index c6f61d1d443..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_bloom_frag.glsl
+++ /dev/null
@@ -1,218 +0,0 @@
-/* Original implementation by Keijiro Takahashi
- * Blender integration by Clément Foucault
- *
- * Original License :
- *
- * Kino/Bloom v2 - Bloom filter for Unity
- *
- * Copyright (C) 2015, 2016 Keijiro Takahashi
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-
-uniform sampler2D sourceBuffer; /* Buffer to filter */
-uniform vec2 sourceBufferTexelSize;
-
-/* Step Blit */
-uniform vec4 curveThreshold;
-uniform float clampIntensity;
-
-/* Step Upsample */
-uniform sampler2D baseBuffer; /* Previous accumulation buffer */
-uniform vec2 baseBufferTexelSize;
-uniform float sampleScale;
-
-/* Step Resolve */
-uniform vec3 bloomColor;
-uniform bool bloomAddBase;
-
-in vec4 uvcoordsvar;
-
-out vec4 FragColor;
-
-/* -------------- Utils ------------- */
-
-vec3 safe_color(vec3 c)
-{
- /* Clamp to avoid black square artifacts if a pixel goes NaN. */
- return clamp(c, vec3(0.0), vec3(1e20)); /* 1e20 arbitrary. */
-}
-
-/* 3-tap median filter */
-vec3 median(vec3 a, vec3 b, vec3 c)
-{
- return a + b + c - min(min(a, b), c) - max(max(a, b), c);
-}
-
-/* ------------- Filters ------------ */
-
-vec3 downsample_filter_high(sampler2D tex, vec2 uv, vec2 texelSize)
-{
- /* Downsample with a 4x4 box filter + anti-flicker filter */
- vec4 d = texelSize.xyxy * vec4(-1, -1, +1, +1);
-
- vec3 s1 = textureLod(tex, uv + d.xy, 0.0).rgb;
- vec3 s2 = textureLod(tex, uv + d.zy, 0.0).rgb;
- vec3 s3 = textureLod(tex, uv + d.xw, 0.0).rgb;
- vec3 s4 = textureLod(tex, uv + d.zw, 0.0).rgb;
-
- /* Karis's luma weighted average (using brightness instead of luma) */
- float s1w = 1.0 / (max_v3(s1) + 1.0);
- float s2w = 1.0 / (max_v3(s2) + 1.0);
- float s3w = 1.0 / (max_v3(s3) + 1.0);
- float s4w = 1.0 / (max_v3(s4) + 1.0);
- float one_div_wsum = 1.0 / (s1w + s2w + s3w + s4w);
-
- return (s1 * s1w + s2 * s2w + s3 * s3w + s4 * s4w) * one_div_wsum;
-}
-
-vec3 downsample_filter(sampler2D tex, vec2 uv, vec2 texelSize)
-{
- /* Downsample with a 4x4 box filter */
- vec4 d = texelSize.xyxy * vec4(-1, -1, +1, +1);
-
- vec3 s;
- s = textureLod(tex, uv + d.xy, 0.0).rgb;
- s += textureLod(tex, uv + d.zy, 0.0).rgb;
- s += textureLod(tex, uv + d.xw, 0.0).rgb;
- s += textureLod(tex, uv + d.zw, 0.0).rgb;
-
- return s * (1.0 / 4);
-}
-
-vec3 upsample_filter_high(sampler2D tex, vec2 uv, vec2 texelSize)
-{
- /* 9-tap bilinear upsampler (tent filter) */
- vec4 d = texelSize.xyxy * vec4(1, 1, -1, 0) * sampleScale;
-
- vec3 s;
- s = textureLod(tex, uv - d.xy, 0.0).rgb;
- s += textureLod(tex, uv - d.wy, 0.0).rgb * 2;
- s += textureLod(tex, uv - d.zy, 0.0).rgb;
-
- s += textureLod(tex, uv + d.zw, 0.0).rgb * 2;
- s += textureLod(tex, uv, 0.0).rgb * 4;
- s += textureLod(tex, uv + d.xw, 0.0).rgb * 2;
-
- s += textureLod(tex, uv + d.zy, 0.0).rgb;
- s += textureLod(tex, uv + d.wy, 0.0).rgb * 2;
- s += textureLod(tex, uv + d.xy, 0.0).rgb;
-
- return s * (1.0 / 16.0);
-}
-
-vec3 upsample_filter(sampler2D tex, vec2 uv, vec2 texelSize)
-{
- /* 4-tap bilinear upsampler */
- vec4 d = texelSize.xyxy * vec4(-1, -1, +1, +1) * (sampleScale * 0.5);
-
- vec3 s;
- s = textureLod(tex, uv + d.xy, 0.0).rgb;
- s += textureLod(tex, uv + d.zy, 0.0).rgb;
- s += textureLod(tex, uv + d.xw, 0.0).rgb;
- s += textureLod(tex, uv + d.zw, 0.0).rgb;
-
- return s * (1.0 / 4.0);
-}
-
-/* ----------- Steps ----------- */
-
-vec4 step_blit(void)
-{
- vec2 uv = uvcoordsvar.xy + sourceBufferTexelSize.xy * 0.5;
-
-#ifdef HIGH_QUALITY /* Anti flicker */
- vec3 d = sourceBufferTexelSize.xyx * vec3(1, 1, 0);
- vec3 s0 = safe_color(textureLod(sourceBuffer, uvcoordsvar.xy, 0.0).rgb);
- vec3 s1 = safe_color(textureLod(sourceBuffer, uvcoordsvar.xy - d.xz, 0.0).rgb);
- vec3 s2 = safe_color(textureLod(sourceBuffer, uvcoordsvar.xy + d.xz, 0.0).rgb);
- vec3 s3 = safe_color(textureLod(sourceBuffer, uvcoordsvar.xy - d.zy, 0.0).rgb);
- vec3 s4 = safe_color(textureLod(sourceBuffer, uvcoordsvar.xy + d.zy, 0.0).rgb);
- vec3 m = median(median(s0.rgb, s1, s2), s3, s4);
-#else
- vec3 s0 = safe_color(textureLod(sourceBuffer, uvcoordsvar.xy, 0.0).rgb);
- vec3 m = s0.rgb;
-#endif
-
- /* Pixel brightness */
- float br = max_v3(m);
-
- /* Under-threshold part: quadratic curve */
- float rq = clamp(br - curveThreshold.x, 0, curveThreshold.y);
- rq = curveThreshold.z * rq * rq;
-
- /* Combine and apply the brightness response curve. */
- m *= max(rq, br - curveThreshold.w) / max(1e-5, br);
-
- /* Clamp pixel intensity if clamping enabled */
- if (clampIntensity > 0.0) {
- br = max(1e-5, max_v3(m));
- m *= 1.0 - max(0.0, br - clampIntensity) / br;
- }
-
- return vec4(m, 1.0);
-}
-
-vec4 step_downsample(void)
-{
-#ifdef HIGH_QUALITY /* Anti flicker */
- vec3 samp = downsample_filter_high(sourceBuffer, uvcoordsvar.xy, sourceBufferTexelSize);
-#else
- vec3 samp = downsample_filter(sourceBuffer, uvcoordsvar.xy, sourceBufferTexelSize);
-#endif
- return vec4(samp, 1.0);
-}
-
-vec4 step_upsample(void)
-{
-#ifdef HIGH_QUALITY
- vec3 blur = upsample_filter_high(sourceBuffer, uvcoordsvar.xy, sourceBufferTexelSize);
-#else
- vec3 blur = upsample_filter(sourceBuffer, uvcoordsvar.xy, sourceBufferTexelSize);
-#endif
- vec3 base = textureLod(baseBuffer, uvcoordsvar.xy, 0.0).rgb;
- return vec4(base + blur, 1.0);
-}
-
-vec4 step_resolve(void)
-{
-#ifdef HIGH_QUALITY
- vec3 blur = upsample_filter_high(sourceBuffer, uvcoordsvar.xy, sourceBufferTexelSize);
-#else
- vec3 blur = upsample_filter(sourceBuffer, uvcoordsvar.xy, sourceBufferTexelSize);
-#endif
- vec4 base = bloomAddBase ? textureLod(baseBuffer, uvcoordsvar.xy, 0.0) : vec4(0.0);
- vec3 cout = base.rgb + blur * bloomColor;
- return vec4(cout, base.a);
-}
-
-void main(void)
-{
-#if defined(STEP_BLIT)
- FragColor = step_blit();
-#elif defined(STEP_DOWNSAMPLE)
- FragColor = step_downsample();
-#elif defined(STEP_UPSAMPLE)
- FragColor = step_upsample();
-#elif defined(STEP_RESOLVE)
- FragColor = step_resolve();
-#endif
-}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_bokeh_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_bokeh_frag.glsl
deleted file mode 100644
index 051a08d25e6..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_bokeh_frag.glsl
+++ /dev/null
@@ -1,101 +0,0 @@
-
-/**
- * Bokeh Look Up Table: This outputs a radius multiplier to shape the sampling in gather pass or
- * the scatter sprite appearance. This is only used if bokeh shape is either anamorphic or is not
- * a perfect circle.
- * We correct samples spacing for polygonal bokeh shapes. However, we do not for anamorphic bokeh
- * as it is way more complex and expensive to do.
- */
-
-#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
-
-uniform float bokehSides;
-uniform float bokehRotation;
-uniform vec2 bokehAnisotropyInv;
-
-in vec4 uvcoordsvar;
-
-layout(location = 0) out vec2 outGatherLut;
-layout(location = 1) out float outScatterLut;
-layout(location = 2) out float outResolveLut;
-
-float polygon_sides_length(float sides_count)
-{
- return 2.0 * sin(M_PI / sides_count);
-}
-
-/* Returns intersection ratio between the radius edge at theta and the polygon edge.
- * Start first corners at theta == 0. */
-float circle_to_polygon_radius(float sides_count, float theta)
-{
- /* From Graphics Gems from CryENGINE 3 (Siggraph 2013) by Tiago Sousa (slide 36). */
- float side_angle = M_2PI / sides_count;
- float halfside_angle = side_angle * 0.5;
- return cos(side_angle * 0.5) /
- cos(theta - side_angle * floor((sides_count * theta + M_PI) / M_2PI));
-}
-
-/* Remap input angle to have homogenous spacing of points along a polygon edge.
- * Expect theta to be in [0..2pi] range. */
-float circle_to_polygon_angle(float sides_count, float theta)
-{
- float side_angle = M_2PI / sides_count;
- float halfside_angle = side_angle * 0.5;
- float side = floor(theta / side_angle);
- /* Length of segment from center to the middle of polygon side. */
- float adjacent = circle_to_polygon_radius(sides_count, 0.0);
-
- /* This is the relative position of the sample on the polygon half side. */
- float local_theta = theta - side * side_angle;
- float ratio = (local_theta - halfside_angle) / halfside_angle;
-
- float halfside_len = polygon_sides_length(sides_count) * 0.5;
- float opposite = ratio * halfside_len;
-
- /* NOTE: atan(y_over_x) has output range [-M_PI_2..M_PI_2]. */
- float final_local_theta = atan(opposite / adjacent);
-
- return side * side_angle + final_local_theta;
-}
-
-void main()
-{
- /* Center uv in range [-1..1]. */
- vec2 uv = uvcoordsvar.xy * 2.0 - 1.0;
-
- float radius = length(uv);
-
- vec2 texel = floor(gl_FragCoord.xy) - float(DOF_MAX_SLIGHT_FOCUS_RADIUS);
-
- if (bokehSides > 0.0) {
- /* NOTE: atan(y,x) has output range [-M_PI..M_PI], so add 2pi to avoid negative angles. */
- float theta = atan(uv.y, uv.x) + M_2PI;
- float r = length(uv);
-
- radius /= circle_to_polygon_radius(bokehSides, theta - bokehRotation);
-
- float theta_new = circle_to_polygon_angle(bokehSides, theta);
- float r_new = circle_to_polygon_radius(bokehSides, theta_new);
-
- theta_new -= bokehRotation;
-
- uv = r_new * vec2(-cos(theta_new), sin(theta_new));
-
- {
- /* Slight focus distance */
- texel *= bokehAnisotropyInv;
- float theta = atan(texel.y, -texel.x) + M_2PI;
- texel /= circle_to_polygon_radius(bokehSides, theta + bokehRotation);
- }
- }
- else {
- uv *= safe_rcp(length(uv));
- }
-
- /* For gather store the normalized UV. */
- outGatherLut = uv;
- /* For scatter store distance. */
- outScatterLut = radius;
- /* For slight focus gather store pixel perfect distance. */
- outResolveLut = length(texel);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_dilate_tiles_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_dilate_tiles_frag.glsl
deleted file mode 100644
index 0cbf92466aa..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_dilate_tiles_frag.glsl
+++ /dev/null
@@ -1,117 +0,0 @@
-
-/**
- * Tile dilate pass: Takes the 8x8 Tiles buffer and converts dilates the tiles with large CoC to
- * their neighborhood. This pass is repeated multiple time until the maximum CoC can be covered.
- */
-
-#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
-
-/* 1/16th of fullres. */
-uniform sampler2D cocTilesFgBuffer;
-uniform sampler2D cocTilesBgBuffer;
-
-uniform int ringCount;
-uniform int ringWidthMultiplier;
-uniform bool dilateSlightFocus;
-
-/* 1/16th of fullres. Same format as input. */
-layout(location = 0) out vec4 outFgCoc;
-layout(location = 1) out vec3 outBgCoc;
-
-const float tile_to_fullres_factor = float(DOF_TILE_DIVISOR);
-
-/* Error introduced by the random offset of the gathering kernel's center. */
-const float bluring_radius_error = 1.0 + 1.0 / (gather_ring_count + 0.5);
-
-void main()
-{
- ivec2 center_tile_pos = ivec2(gl_FragCoord.xy);
-
- CocTile ring_buckets[DOF_DILATE_RING_COUNT];
-
- for (int ring = 0; ring < ringCount && ring < DOF_DILATE_RING_COUNT; ring++) {
- ring_buckets[ring] = dof_coc_tile_init();
-
- int ring_distance = ring + 1;
- for (int sample_id = 0; sample_id < 4 * ring_distance; sample_id++) {
- ivec2 offset = dof_square_ring_sample_offset(ring_distance, sample_id);
-
- offset *= ringWidthMultiplier;
-
- for (int i = 0; i < 2; i++) {
- ivec2 adj_tile_pos = center_tile_pos + ((i == 0) ? offset : -offset);
-
- CocTile adj_tile = dof_coc_tile_load(cocTilesFgBuffer, cocTilesBgBuffer, adj_tile_pos);
-
-#ifdef DILATE_MODE_MIN_MAX
- /* Actually gather the "absolute" biggest coc but keeping the sign. */
- ring_buckets[ring].fg_min_coc = min(ring_buckets[ring].fg_min_coc, adj_tile.fg_min_coc);
- ring_buckets[ring].bg_max_coc = max(ring_buckets[ring].bg_max_coc, adj_tile.bg_max_coc);
-
- if (dilateSlightFocus) {
- ring_buckets[ring].fg_slight_focus_max_coc = dof_coc_max_slight_focus(
- ring_buckets[ring].fg_slight_focus_max_coc, adj_tile.fg_slight_focus_max_coc);
- }
-
-#else /* DILATE_MODE_MIN_ABS */
- ring_buckets[ring].fg_max_coc = max(ring_buckets[ring].fg_max_coc, adj_tile.fg_max_coc);
- ring_buckets[ring].bg_min_coc = min(ring_buckets[ring].bg_min_coc, adj_tile.bg_min_coc);
-
- /* Should be tight as possible to reduce gather overhead (see slide 61). */
- float closest_neighbor_distance = length(max(abs(vec2(offset)) - 1.0, 0.0)) *
- tile_to_fullres_factor;
-
- ring_buckets[ring].fg_max_intersectable_coc = max(
- ring_buckets[ring].fg_max_intersectable_coc,
- adj_tile.fg_max_intersectable_coc + closest_neighbor_distance);
- ring_buckets[ring].bg_min_intersectable_coc = min(
- ring_buckets[ring].bg_min_intersectable_coc,
- adj_tile.bg_min_intersectable_coc + closest_neighbor_distance);
-#endif
- }
- }
- }
-
- /* Load center tile. */
- CocTile out_tile = dof_coc_tile_load(cocTilesFgBuffer, cocTilesBgBuffer, center_tile_pos);
-
- /* Dilate once. */
- if (dilateSlightFocus) {
- out_tile.fg_slight_focus_max_coc = dof_coc_max_slight_focus(
- out_tile.fg_slight_focus_max_coc, ring_buckets[0].fg_slight_focus_max_coc);
- }
-
- for (int ring = 0; ring < ringCount && ring < DOF_DILATE_RING_COUNT; ring++) {
- float ring_distance = float(ring + 1);
-
- ring_distance = (ring_distance * ringWidthMultiplier - 1) * tile_to_fullres_factor;
-
- /* NOTE(fclem): Unsure if both sides of the inequalities have the same unit. */
-#ifdef DILATE_MODE_MIN_MAX
- if (-ring_buckets[ring].fg_min_coc * bluring_radius_error > ring_distance) {
- out_tile.fg_min_coc = min(out_tile.fg_min_coc, ring_buckets[ring].fg_min_coc);
- }
-
- if (ring_buckets[ring].bg_max_coc * bluring_radius_error > ring_distance) {
- out_tile.bg_max_coc = max(out_tile.bg_max_coc, ring_buckets[ring].bg_max_coc);
- }
-
-#else /* DILATE_MODE_MIN_ABS */
- /* Find minimum absolute CoC radii that will be intersected for the previously
- * computed maximum CoC values. */
- if (-out_tile.fg_min_coc * bluring_radius_error > ring_distance) {
- out_tile.fg_max_coc = max(out_tile.fg_max_coc, ring_buckets[ring].fg_max_coc);
- out_tile.fg_max_intersectable_coc = max(out_tile.fg_max_intersectable_coc,
- ring_buckets[ring].fg_max_intersectable_coc);
- }
-
- if (out_tile.bg_max_coc * bluring_radius_error > ring_distance) {
- out_tile.bg_min_coc = min(out_tile.bg_min_coc, ring_buckets[ring].bg_min_coc);
- out_tile.bg_min_intersectable_coc = min(out_tile.bg_min_intersectable_coc,
- ring_buckets[ring].bg_min_intersectable_coc);
- }
-#endif
- }
-
- dof_coc_tile_store(out_tile, outFgCoc, outBgCoc);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl
deleted file mode 100644
index db9ae0f7034..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl
+++ /dev/null
@@ -1,293 +0,0 @@
-
-/**
- * Gather pass: Convolve foreground and background parts in separate passes.
- *
- * Using the min&max CoC tile buffer, we select the best appropriate method to blur the scene
- * color. A fast gather path is taken if there is not many CoC variation inside the tile.
- *
- * We sample using an octaweb sampling pattern. We randomize the kernel center and each ring
- * rotation to ensure maximum coverage.
- */
-
-#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
-#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
-
-/* Mipmapped input buffers, halfres but with padding to ensure mipmap alignement. */
-uniform sampler2D colorBuffer;
-uniform sampler2D cocBuffer;
-
-/* Same input buffer but with a bilinear sampler object. */
-uniform sampler2D colorBufferBilinear;
-
-/* CoC Min&Max tile buffer at 1/16th of fullres. */
-uniform sampler2D cocTilesFgBuffer;
-uniform sampler2D cocTilesBgBuffer;
-
-uniform sampler2D bokehLut;
-
-/* Used to correct the padding in the color and CoC buffers. */
-uniform vec2 gatherInputUvCorrection;
-
-uniform vec2 gatherOutputTexelSize;
-
-uniform vec2 bokehAnisotropy;
-
-layout(location = 0) out vec4 outColor;
-layout(location = 1) out float outWeight;
-#ifndef DOF_HOLEFILL_PASS
-layout(location = 2) out vec2 outOcclusion;
-#else
-
-/* Dirty global variable that isn't used. So it should get optimized out. */
-vec2 outOcclusion;
-#endif
-
-#ifdef DOF_FOREGROUND_PASS
-const bool is_foreground = true;
-#else /* DOF_BACKGROUND_PASS */
-const bool is_foreground = false;
-#endif
-
-const float unit_ring_radius = 1.0 / float(gather_ring_count);
-const float unit_sample_radius = 1.0 / float(gather_ring_count + 0.5);
-const float large_kernel_radius = 0.5 + float(gather_ring_count);
-const float smaller_kernel_radius = 0.5 + float(gather_ring_count - gather_density_change_ring);
-/* NOTE(fclem) the bias is reducing issues with density change visible transition. */
-const float radius_downscale_factor = smaller_kernel_radius / large_kernel_radius;
-const int change_density_at_ring = (gather_ring_count - gather_density_change_ring + 1);
-const float coc_radius_error = 2.0;
-
-/* Radii needs to be halfres CoC sizes. */
-bool dof_do_density_change(float base_radius, float min_intersectable_radius)
-{
- /* Reduce artifact for very large blur. */
- min_intersectable_radius *= 0.1;
-
- bool need_new_density = (base_radius * unit_ring_radius > min_intersectable_radius);
- bool larger_than_min_density = (base_radius * radius_downscale_factor >
- float(gather_ring_count));
-
- return need_new_density && larger_than_min_density;
-}
-
-void dof_gather_init(float base_radius,
- vec4 noise,
- out vec2 center_co,
- out float lod,
- out float intersection_multiplier)
-{
- /* Jitter center half a ring to reduce undersampling. */
- vec2 jitter_ofs = 0.499 * noise.zw * sqrt(noise.x);
-#ifdef DOF_BOKEH_TEXTURE
- jitter_ofs *= bokehAnisotropy;
-#endif
- center_co = gl_FragCoord.xy + jitter_ofs * base_radius * unit_sample_radius;
-
- /* TODO(fclem) Seems like the default lod selection is too big. Bias to avoid blocky moving
- * out of focus shapes. */
- const float lod_bias = -2.0;
- lod = max(floor(log2(base_radius * unit_sample_radius) + 0.5) + lod_bias, 0.0);
-
- if (no_gather_mipmaps) {
- lod = 0.0;
- }
- /* (Slide 64). */
- intersection_multiplier = pow(0.5, lod);
-}
-
-void dof_gather_accumulator(float base_radius,
- float min_intersectable_radius,
- const bool do_fast_gather,
- const bool do_density_change)
-{
- vec4 noise = no_gather_random ? vec4(0.0, 0.0, 0.0, 1.0) : texelfetch_noise_tex(gl_FragCoord.xy);
-
- if (!do_fast_gather) {
- /* Jitter the radius to reduce noticeable density changes. */
- base_radius += noise.x * unit_ring_radius * base_radius;
- }
- else {
- /* Jittering the radius more than we need means we are going to feather the bokeh shape half
- * a ring. So we need to compensate for fast gather that does not check CoC intersection. */
- base_radius += (0.5 - noise.x) * 1.5 * unit_ring_radius * base_radius;
- }
- /* TODO(fclem) another seed? For now Cranly-Partterson rotation with golden ratio. */
- noise.x = fract(noise.x + 0.61803398875);
-
- float lod, isect_mul;
- vec2 center_co;
- dof_gather_init(base_radius, noise, center_co, lod, isect_mul);
-
- bool first_ring = true;
-
- DofGatherData accum_data = GATHER_DATA_INIT;
-
- int density_change = 0;
- for (int ring = gather_ring_count; ring > 0; ring--) {
- int sample_pair_count = gather_ring_density * ring;
-
- float step_rot = M_PI / float(sample_pair_count);
- mat2 step_rot_mat = rot2_from_angle(step_rot);
-
- float angle_offset = noise.y * step_rot;
- vec2 offset = vec2(cos(angle_offset), sin(angle_offset));
-
- float ring_radius = float(ring) * unit_sample_radius * base_radius;
-
- /* Slide 38. */
- float bordering_radius = ring_radius +
- (0.5 + coc_radius_error) * base_radius * unit_sample_radius;
- DofGatherData ring_data = GATHER_DATA_INIT;
- for (int sample_pair = 0; sample_pair < sample_pair_count; sample_pair++) {
- offset = step_rot_mat * offset;
-
- DofGatherData pair_data[2];
- for (int i = 0; i < 2; i++) {
- vec2 offset_co = ((i == 0) ? offset : -offset);
-#ifdef DOF_BOKEH_TEXTURE
- /* Scaling to 0.25 for speed. Improves texture cache hit. */
- offset_co = texture(bokehLut, offset_co * 0.25 + 0.5).rg;
- offset_co *= bokehAnisotropy;
-#endif
- vec2 sample_co = center_co + offset_co * ring_radius;
- vec2 sample_uv = sample_co * gatherOutputTexelSize * gatherInputUvCorrection;
- if (do_fast_gather) {
- pair_data[i].color = dof_load_gather_color(colorBufferBilinear, sample_uv, lod);
- }
- else {
- pair_data[i].color = dof_load_gather_color(colorBuffer, sample_uv, lod);
- }
- pair_data[i].coc = dof_load_gather_coc(cocBuffer, sample_uv, lod);
- pair_data[i].dist = ring_radius;
- }
-
- dof_gather_accumulate_sample_pair(pair_data,
- bordering_radius,
- isect_mul,
- first_ring,
- do_fast_gather,
- is_foreground,
- ring_data,
- accum_data);
- }
-
-#ifdef DOF_FOREGROUND_PASS /* Reduce issue with closer foreground over distant foreground. */
- /* TODO(fclem) this seems to not be completely correct as the issue remains. */
- float ring_area = (sqr(float(ring) + 0.5 + coc_radius_error) -
- sqr(float(ring) - 0.5 + coc_radius_error)) *
- sqr(base_radius * unit_sample_radius);
- dof_gather_ammend_weight(ring_data, ring_area);
-#endif
-
- dof_gather_accumulate_sample_ring(
- ring_data, sample_pair_count * 2, first_ring, do_fast_gather, is_foreground, accum_data);
-
- first_ring = false;
-
- if (do_density_change && (ring == change_density_at_ring) &&
- (density_change < gather_max_density_change)) {
- if (dof_do_density_change(base_radius, min_intersectable_radius)) {
- base_radius *= radius_downscale_factor;
- ring += gather_density_change_ring;
- /* We need to account for the density change in the weights (slide 62).
- * For that multiply old kernel data by its area divided by the new kernel area. */
- const float outer_rings_weight = 1.0 / (radius_downscale_factor * radius_downscale_factor);
-#ifndef DOF_FOREGROUND_PASS /* Samples are already weighted per ring in foreground pass. */
- dof_gather_ammend_weight(accum_data, outer_rings_weight);
-#endif
- /* Re-init kernel position & sampling parameters. */
- dof_gather_init(base_radius, noise, center_co, lod, isect_mul);
- density_change++;
- }
- }
- }
-
- {
- /* Center sample. */
- vec2 sample_uv = center_co * gatherOutputTexelSize * gatherInputUvCorrection;
- DofGatherData center_data;
- if (do_fast_gather) {
- center_data.color = dof_load_gather_color(colorBufferBilinear, sample_uv, lod);
- }
- else {
- center_data.color = dof_load_gather_color(colorBuffer, sample_uv, lod);
- }
- center_data.coc = dof_load_gather_coc(cocBuffer, sample_uv, lod);
- center_data.dist = 0.0;
-
- /* Slide 38. */
- float bordering_radius = (0.5 + coc_radius_error) * base_radius * unit_sample_radius;
-
- dof_gather_accumulate_center_sample(
- center_data, bordering_radius, do_fast_gather, is_foreground, accum_data);
- }
-
- int total_sample_count = dof_gather_total_sample_count_with_density_change(
- gather_ring_count, gather_ring_density, density_change);
- dof_gather_accumulate_resolve(total_sample_count, accum_data, outColor, outWeight, outOcclusion);
-
-#if defined(DOF_DEBUG_GATHER_PERF)
- if (density_change > 0) {
- float fac = saturate(float(density_change) / float(10.0));
- outColor.rgb = avg(outColor.rgb) * neon_gradient(fac);
- }
- if (do_fast_gather) {
- outColor.rgb = avg(outColor.rgb) * vec3(0.0, 1.0, 0.0);
- }
-#elif defined(DOF_DEBUG_SCATTER_PERF)
- outColor.rgb = avg(outColor.rgb) * vec3(0.0, 1.0, 0.0);
-#endif
-
- /* Output premultiplied color so we can use bilinear sampler in resolve pass. */
- outColor *= outWeight;
-}
-
-void main()
-{
- ivec2 tile_co = ivec2(gl_FragCoord.xy / float(DOF_TILE_DIVISOR / 2));
- CocTile coc_tile = dof_coc_tile_load(cocTilesFgBuffer, cocTilesBgBuffer, tile_co);
- CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile);
-
-#if defined(DOF_FOREGROUND_PASS)
- float base_radius = -coc_tile.fg_min_coc;
- float min_radius = -coc_tile.fg_max_coc;
- float min_intersectable_radius = -coc_tile.fg_max_intersectable_coc;
- bool can_early_out = !prediction.do_foreground;
-
-#elif defined(DOF_HOLEFILL_PASS)
- float base_radius = -coc_tile.fg_min_coc;
- float min_radius = -coc_tile.fg_max_coc;
- float min_intersectable_radius = DOF_TILE_LARGE_COC;
- bool can_early_out = !prediction.do_holefill;
-
-#else /* DOF_BACKGROUND_PASS */
- float base_radius = coc_tile.bg_max_coc;
- float min_radius = coc_tile.bg_min_coc;
- float min_intersectable_radius = coc_tile.bg_min_intersectable_coc;
- bool can_early_out = !prediction.do_background;
-#endif
-
- bool do_fast_gather = dof_do_fast_gather(base_radius, min_radius, is_foreground);
-
- /* Gather at half resolution. Divide CoC by 2. */
- base_radius *= 0.5;
- min_intersectable_radius *= 0.5;
-
- bool do_density_change = dof_do_density_change(base_radius, min_intersectable_radius);
-
- if (can_early_out) {
- /* Early out. */
- outColor = vec4(0.0);
- outWeight = 0.0;
- outOcclusion = vec2(0.0, 0.0);
- }
- else if (do_fast_gather) {
- dof_gather_accumulator(base_radius, min_intersectable_radius, true, false);
- }
- else if (do_density_change) {
- dof_gather_accumulator(base_radius, min_intersectable_radius, false, true);
- }
- else {
- dof_gather_accumulator(base_radius, min_intersectable_radius, false, false);
- }
-}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
deleted file mode 100644
index e5b68637563..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
+++ /dev/null
@@ -1,625 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-
-uniform vec4 cocParams;
-
-#define cocMul cocParams[0] /* distance * aperturesize * invsensorsize */
-#define cocBias cocParams[1] /* aperturesize * invsensorsize */
-#define cocNear cocParams[2] /* Near view depths value. */
-#define cocFar cocParams[3] /* Far view depths value. */
-
-/* -------------- Debug Defines ------------- */
-
-// #define DOF_DEBUG_GATHER_PERF
-// #define DOF_DEBUG_SCATTER_PERF
-
-const bool no_smooth_intersection = false;
-const bool no_gather_occlusion = false;
-const bool no_gather_mipmaps = false;
-const bool no_gather_random = false;
-const bool no_gather_filtering = false;
-const bool no_scatter_occlusion = false;
-const bool no_scatter_pass = false;
-const bool no_foreground_pass = false;
-const bool no_background_pass = false;
-const bool no_slight_focus_pass = false;
-const bool no_focus_pass = false;
-const bool no_holefill_pass = false;
-
-/* -------------- Quality Defines ------------- */
-
-#ifdef DOF_HOLEFILL_PASS
-/* No need for very high density for holefill. */
-const int gather_ring_count = 3;
-const int gather_ring_density = 3;
-const int gather_max_density_change = 0;
-const int gather_density_change_ring = 1;
-#else
-const int gather_ring_count = DOF_GATHER_RING_COUNT;
-const int gather_ring_density = 3;
-const int gather_max_density_change = 50; /* Dictates the maximum good quality blur. */
-const int gather_density_change_ring = 1;
-#endif
-
-/* -------------- Utils ------------- */
-
-const vec2 quad_offsets[4] = vec2[4](
- vec2(-0.5, 0.5), vec2(0.5, 0.5), vec2(0.5, -0.5), vec2(-0.5, -0.5));
-
-/* Divide by sensor size to get the normalized size. */
-#define calculate_coc_persp(zdepth) (cocMul / zdepth - cocBias)
-#define calculate_coc_ortho(zdepth) ((zdepth + cocMul / cocBias) * cocMul)
-#define calculate_coc(z) \
- (ProjectionMatrix[3][3] == 0.0) ? calculate_coc_persp(z) : calculate_coc_ortho(z)
-
-/* Ortho conversion is only true for camera view! */
-#define linear_depth_persp(d) ((cocNear * cocFar) / (d * (cocNear - cocFar) + cocFar))
-#define linear_depth_ortho(d) (d * (cocNear - cocFar) + cocNear)
-
-#define linear_depth(d) \
- ((ProjectionMatrix[3][3] == 0.0) ? linear_depth_persp(d) : linear_depth_ortho(d))
-
-#define dof_coc_from_zdepth(d) calculate_coc(linear_depth(d))
-
-float dof_hdr_color_weight(vec4 color)
-{
- /* From UE4. Very fast "luma" weighting. */
- float luma = (color.g * 2.0) + (color.r + color.b);
- /* TODO(fclem) Pass correct exposure. */
- const float exposure = 1.0;
- return 1.0 / (luma * exposure + 4.0);
-}
-
-float dof_coc_select(vec4 cocs)
-{
- /* Select biggest coc. */
- float selected_coc = cocs.x;
- if (abs(cocs.y) > abs(selected_coc)) {
- selected_coc = cocs.y;
- }
- if (abs(cocs.z) > abs(selected_coc)) {
- selected_coc = cocs.z;
- }
- if (abs(cocs.w) > abs(selected_coc)) {
- selected_coc = cocs.w;
- }
- return selected_coc;
-}
-
-/* NOTE: Do not forget to normalize weights afterwards. */
-vec4 dof_downsample_bilateral_coc_weights(vec4 cocs)
-{
- float chosen_coc = dof_coc_select(cocs);
-
- const float scale = 4.0; /* TODO(fclem) revisit. */
- /* NOTE: The difference between the cocs should be inside a abs() function,
- * but we follow UE4 implementation to improve how dithered transparency looks (see slide 19). */
- return saturate(1.0 - (chosen_coc - cocs) * scale);
-}
-
-/* NOTE: Do not forget to normalize weights afterwards. */
-vec4 dof_downsample_bilateral_color_weights(vec4 colors[4])
-{
- vec4 weights;
- for (int i = 0; i < 4; i++) {
- weights[i] = dof_hdr_color_weight(colors[i]);
- }
- return weights;
-}
-
-/* Makes sure the load functions distribute the energy correctly
- * to both scatter and gather passes. */
-vec4 dof_load_gather_color(sampler2D gather_input_color_buffer, vec2 uv, float lod)
-{
- vec4 color = textureLod(gather_input_color_buffer, uv, lod);
- return color;
-}
-
-vec4 dof_load_scatter_color(sampler2D scatter_input_color_buffer, vec2 uv, float lod)
-{
- vec4 color = textureLod(scatter_input_color_buffer, uv, lod);
- return color;
-}
-
-float dof_load_gather_coc(sampler2D gather_input_coc_buffer, vec2 uv, float lod)
-{
- float coc = textureLod(gather_input_coc_buffer, uv, lod).r;
- /* We gather at halfres. CoC must be divided by 2 to be compared against radii. */
- return coc * 0.5;
-}
-
-/* Distribute weights between near/slightfocus/far fields (slide 117). */
-const float layer_threshold = 4.0;
-/* Make sure it overlaps. */
-const float layer_offset_fg = 0.5 + 1.0;
-/* Extra offset for convolution layers to avoid light leaking from background. */
-const float layer_offset = 0.5 + 0.5;
-
-#define DOF_MAX_SLIGHT_FOCUS_RADIUS 16
-
-float dof_layer_weight(float coc, const bool is_foreground)
-{
-/* NOTE: These are fullres pixel CoC value. */
-#ifdef DOF_RESOLVE_PASS
- return saturate(-abs(coc) + layer_threshold + layer_offset) *
- float(is_foreground ? (coc <= 0.5) : (coc > -0.5));
-#else
- coc *= 2.0; /* Account for half pixel gather. */
- float threshold = layer_threshold - ((is_foreground) ? layer_offset_fg : layer_offset);
- return saturate(((is_foreground) ? -coc : coc) - threshold);
-#endif
-}
-vec4 dof_layer_weight(vec4 coc)
-{
- /* NOTE: Used for scatter pass which already flipped the sign correctly. */
- coc *= 2.0; /* Account for half pixel gather. */
- return saturate(coc - layer_threshold + layer_offset);
-}
-
-/* NOTE: This is halfres CoC radius. */
-float dof_sample_weight(float coc)
-{
- /* Full intensity if CoC radius is below the pixel footprint. */
- const float min_coc = 1.0;
- coc = max(min_coc, abs(coc));
- return (M_PI * min_coc * min_coc) / (M_PI * coc * coc);
-}
-vec4 dof_sample_weight(vec4 coc)
-{
- /* Full intensity if CoC radius is below the pixel footprint. */
- const float min_coc = 1.0;
- coc = max(vec4(min_coc), abs(coc));
- return (M_PI * min_coc * min_coc) / (M_PI * coc * coc);
-}
-
-/* Intersection with the center of the kernel. */
-float dof_intersection_weight(float coc, float distance_from_center, float intersection_multiplier)
-{
- if (no_smooth_intersection) {
- return step(0.0, (abs(coc) - distance_from_center));
- }
- else {
- /* (Slide 64). */
- return saturate((abs(coc) - distance_from_center) * intersection_multiplier + 0.5);
- }
-}
-
-/* Returns weight of the sample for the outer bucket (containing previous rings). */
-float dof_gather_accum_weight(float coc, float bordering_radius, bool first_ring)
-{
- /* First ring has nothing to be mixed against. */
- if (first_ring) {
- return 0.0;
- }
- return saturate(coc - bordering_radius);
-}
-
-bool dof_do_fast_gather(float max_absolute_coc, float min_absolute_coc, const bool is_foreground)
-{
- float min_weight = dof_layer_weight((is_foreground) ? -min_absolute_coc : min_absolute_coc,
- is_foreground);
- if (min_weight < 1.0) {
- return false;
- }
- /* FIXME(fclem): This is a workaround to fast gather triggering too early.
- * Since we use custom opacity mask, the opacity is not given to be 100% even for
- * after normal threshold. */
- if (is_foreground && min_absolute_coc < layer_threshold) {
- return false;
- }
- return (max_absolute_coc - min_absolute_coc) < (DOF_FAST_GATHER_COC_ERROR * max_absolute_coc);
-}
-
-/* ------------------- COC TILES UTILS ------------------- */
-
-struct CocTile {
- float fg_min_coc;
- float fg_max_coc;
- float fg_max_intersectable_coc;
- float fg_slight_focus_max_coc;
- float bg_min_coc;
- float bg_max_coc;
- float bg_min_intersectable_coc;
-};
-
-struct CocTilePrediction {
- bool do_foreground;
- bool do_slight_focus;
- bool do_focus;
- bool do_background;
- bool do_holefill;
-};
-
-/* WATCH: Might have to change depending on the texture format. */
-#define DOF_TILE_DEFOCUS 0.25
-#define DOF_TILE_FOCUS 0.0
-#define DOF_TILE_MIXED 0.75
-#define DOF_TILE_LARGE_COC 1024.0
-
-/* Init a CoC tile for reduction algorithms. */
-CocTile dof_coc_tile_init(void)
-{
- CocTile tile;
- tile.fg_min_coc = 0.0;
- tile.fg_max_coc = -DOF_TILE_LARGE_COC;
- tile.fg_max_intersectable_coc = DOF_TILE_LARGE_COC;
- tile.fg_slight_focus_max_coc = -1.0;
- tile.bg_min_coc = DOF_TILE_LARGE_COC;
- tile.bg_max_coc = 0.0;
- tile.bg_min_intersectable_coc = DOF_TILE_LARGE_COC;
- return tile;
-}
-
-CocTile dof_coc_tile_load(sampler2D fg_buffer, sampler2D bg_buffer, ivec2 tile_co)
-{
- ivec2 tex_size = textureSize(fg_buffer, 0).xy;
- tile_co = clamp(tile_co, ivec2(0), tex_size - 1);
-
- vec4 fg = texelFetch(fg_buffer, tile_co, 0);
- vec3 bg = texelFetch(bg_buffer, tile_co, 0).xyz;
-
- CocTile tile;
- tile.fg_min_coc = -fg.x;
- tile.fg_max_coc = -fg.y;
- tile.fg_max_intersectable_coc = -fg.z;
- tile.fg_slight_focus_max_coc = fg.w;
- tile.bg_min_coc = bg.x;
- tile.bg_max_coc = bg.y;
- tile.bg_min_intersectable_coc = bg.z;
- return tile;
-}
-
-void dof_coc_tile_store(CocTile tile, out vec4 out_fg, out vec3 out_bg)
-{
- out_fg.x = -tile.fg_min_coc;
- out_fg.y = -tile.fg_max_coc;
- out_fg.z = -tile.fg_max_intersectable_coc;
- out_fg.w = tile.fg_slight_focus_max_coc;
- out_bg.x = tile.bg_min_coc;
- out_bg.y = tile.bg_max_coc;
- out_bg.z = tile.bg_min_intersectable_coc;
-}
-
-CocTilePrediction dof_coc_tile_prediction_get(CocTile tile)
-{
- /* Based on tile value, predict what pass we need to load. */
- CocTilePrediction predict;
-
- predict.do_foreground = (-tile.fg_min_coc > layer_threshold - layer_offset_fg);
- bool fg_fully_opaque = predict.do_foreground &&
- dof_do_fast_gather(-tile.fg_min_coc, -tile.fg_max_coc, true);
-
- predict.do_slight_focus = !fg_fully_opaque && (tile.fg_slight_focus_max_coc >= 0.5);
- predict.do_focus = !fg_fully_opaque && (tile.fg_slight_focus_max_coc == DOF_TILE_FOCUS);
-
- predict.do_background = !predict.do_focus && !fg_fully_opaque &&
- (tile.bg_max_coc > layer_threshold - layer_offset);
- bool bg_fully_opaque = predict.do_background &&
- dof_do_fast_gather(-tile.bg_max_coc, tile.bg_min_coc, false);
- predict.do_holefill = !predict.do_focus && !fg_fully_opaque && -tile.fg_max_coc > 0.0;
-
-#if 0 /* Debug */
- predict.do_foreground = predict.do_background = predict.do_holefill = true;
-#endif
- return predict;
-}
-
-/* Special function to return the correct max value of 2 slight focus coc. */
-float dof_coc_max_slight_focus(float coc1, float coc2)
-{
- /* Do not consider values below 0.5 for expansion as they are "encoded".
- * See setup pass shader for more infos. */
- if ((coc1 == DOF_TILE_DEFOCUS && coc2 == DOF_TILE_FOCUS) ||
- (coc1 == DOF_TILE_FOCUS && coc2 == DOF_TILE_DEFOCUS)) {
- /* Tile where completely out of focus and in focus are both present.
- * Consider as very slightly out of focus. */
- return DOF_TILE_MIXED;
- }
- return max(coc1, coc2);
-}
-
-/* ------------------- GATHER UTILS ------------------- */
-
-struct DofGatherData {
- vec4 color;
- float weight;
- float dist; /* TODO: remove. */
- /* For scatter occlusion. */
- float coc;
- float coc_sqr;
- /* For ring bucket merging. */
- float transparency;
-
- float layer_opacity;
-};
-
-#define GATHER_DATA_INIT DofGatherData(vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
-
-void dof_gather_ammend_weight(inout DofGatherData sample_data, float weight)
-{
- sample_data.color *= weight;
- sample_data.coc *= weight;
- sample_data.coc_sqr *= weight;
- sample_data.weight *= weight;
-}
-
-void dof_gather_accumulate_sample(DofGatherData sample_data,
- float weight,
- inout DofGatherData accum_data)
-{
- accum_data.color += sample_data.color * weight;
- accum_data.coc += sample_data.coc * weight;
- accum_data.coc_sqr += sample_data.coc * (sample_data.coc * weight);
- accum_data.weight += weight;
-}
-
-void dof_gather_accumulate_sample_pair(DofGatherData pair_data[2],
- float bordering_radius,
- float intersection_multiplier,
- bool first_ring,
- const bool do_fast_gather,
- const bool is_foreground,
- inout DofGatherData ring_data,
- inout DofGatherData accum_data)
-{
- if (do_fast_gather) {
- for (int i = 0; i < 2; i++) {
- dof_gather_accumulate_sample(pair_data[i], 1.0, accum_data);
- accum_data.layer_opacity += 1.0;
- }
- return;
- }
-
-#if 0
- const float mirroring_threshold = -layer_threshold - layer_offset;
- /* TODO(fclem) Promote to parameter? dither with Noise? */
- const float mirroring_min_distance = 15.0;
- if (pair_data[0].coc < mirroring_threshold &&
- (pair_data[1].coc - mirroring_min_distance) > pair_data[0].coc) {
- pair_data[1].coc = pair_data[0].coc;
- }
- else if (pair_data[1].coc < mirroring_threshold &&
- (pair_data[0].coc - mirroring_min_distance) > pair_data[1].coc) {
- pair_data[0].coc = pair_data[1].coc;
- }
-#endif
-
- for (int i = 0; i < 2; i++) {
- float sample_weight = dof_sample_weight(pair_data[i].coc);
- float layer_weight = dof_layer_weight(pair_data[i].coc, is_foreground);
- float inter_weight = dof_intersection_weight(
- pair_data[i].coc, pair_data[i].dist, intersection_multiplier);
- float weight = inter_weight * layer_weight * sample_weight;
-
- /**
- * If a CoC is larger than bordering radius we accumulate it to the general accumulator.
- * If not, we accumulate to the ring bucket. This is to have more consistent sample occlusion.
- */
- float accum_weight = dof_gather_accum_weight(pair_data[i].coc, bordering_radius, first_ring);
- dof_gather_accumulate_sample(pair_data[i], weight * accum_weight, accum_data);
- dof_gather_accumulate_sample(pair_data[i], weight * (1.0 - accum_weight), ring_data);
-
- accum_data.layer_opacity += layer_weight;
-
- if (is_foreground) {
- ring_data.transparency += 1.0 - inter_weight * layer_weight;
- }
- else {
- float coc = is_foreground ? -pair_data[i].coc : pair_data[i].coc;
- ring_data.transparency += saturate(coc - bordering_radius);
- }
- }
-}
-
-void dof_gather_accumulate_sample_ring(DofGatherData ring_data,
- int sample_count,
- bool first_ring,
- const bool do_fast_gather,
- /* accum_data occludes the ring_data if true. */
- const bool reversed_occlusion,
- inout DofGatherData accum_data)
-{
- if (do_fast_gather) {
- /* Do nothing as ring_data contains nothing. All samples are already in accum_data. */
- return;
- }
-
- if (first_ring) {
- /* Layer opacity is directly accumulated into accum_data data. */
- accum_data.color = ring_data.color;
- accum_data.coc = ring_data.coc;
- accum_data.coc_sqr = ring_data.coc_sqr;
- accum_data.weight = ring_data.weight;
-
- accum_data.transparency = ring_data.transparency / float(sample_count);
- return;
- }
-
- if (ring_data.weight == 0.0) {
- return;
- }
-
- float ring_avg_coc = ring_data.coc / ring_data.weight;
- float accum_avg_coc = accum_data.coc / accum_data.weight;
-
- /* Smooth test to set opacity to see if the ring average coc occludes the accumulation.
- * Test is reversed to be multiplied against opacity. */
- float ring_occlu = saturate(accum_avg_coc - ring_avg_coc);
- /* The bias here is arbitrary. Seems to avoid weird looking foreground in most cases.
- * We might need to make it a parameter or find a relative bias. */
- float accum_occlu = saturate((ring_avg_coc - accum_avg_coc) * 0.1 - 1.0);
-
-#ifdef DOF_RESOLVE_PASS
- ring_occlu = accum_occlu = 0.0;
-#endif
-
- if (no_gather_occlusion) {
- ring_occlu = 0.0;
- accum_occlu = 0.0;
- }
-
- /* (Slide 40) */
- float ring_opacity = saturate(1.0 - ring_data.transparency / float(sample_count));
- float accum_opacity = 1.0 - accum_data.transparency;
-
- if (reversed_occlusion) {
- /* Accum_data occludes the ring. */
- float alpha = (accum_data.weight == 0.0) ? 0.0 : accum_opacity * accum_occlu;
- float one_minus_alpha = 1.0 - alpha;
-
- accum_data.color += ring_data.color * one_minus_alpha;
- accum_data.coc += ring_data.coc * one_minus_alpha;
- accum_data.coc_sqr += ring_data.coc_sqr * one_minus_alpha;
- accum_data.weight += ring_data.weight * one_minus_alpha;
-
- accum_data.transparency *= 1.0 - ring_opacity;
- }
- else {
- /* Ring occludes the accum_data (Same as reference). */
- float alpha = (accum_data.weight == 0.0) ? 1.0 : (ring_opacity * ring_occlu);
- float one_minus_alpha = 1.0 - alpha;
-
- accum_data.color = accum_data.color * one_minus_alpha + ring_data.color;
- accum_data.coc = accum_data.coc * one_minus_alpha + ring_data.coc;
- accum_data.coc_sqr = accum_data.coc_sqr * one_minus_alpha + ring_data.coc_sqr;
- accum_data.weight = accum_data.weight * one_minus_alpha + ring_data.weight;
- }
-}
-
-/* FIXME(fclem) Seems to be wrong since it needs ringcount+1 as input for slightfocus gather. */
-int dof_gather_total_sample_count(const int ring_count, const int ring_density)
-{
- return (ring_count * ring_count - ring_count) * ring_density + 1;
-}
-
-void dof_gather_accumulate_center_sample(DofGatherData center_data,
- float bordering_radius,
-#ifdef DOF_RESOLVE_PASS
- int i_radius,
-#endif
- const bool do_fast_gather,
- const bool is_foreground,
- inout DofGatherData accum_data)
-{
- float layer_weight = dof_layer_weight(center_data.coc, is_foreground);
- float sample_weight = dof_sample_weight(center_data.coc);
- float weight = layer_weight * sample_weight;
- float accum_weight = dof_gather_accum_weight(center_data.coc, bordering_radius, false);
-
- if (do_fast_gather) {
- /* Hope for the compiler to optimize the above. */
- layer_weight = 1.0;
- sample_weight = 1.0;
- accum_weight = 1.0;
- weight = 1.0;
- }
-
- center_data.transparency = 1.0 - weight;
-
- dof_gather_accumulate_sample(center_data, weight * accum_weight, accum_data);
-
- if (!do_fast_gather) {
-#ifdef DOF_RESOLVE_PASS
- /* NOTE(fclem): Hack to smooth transition to full in-focus opacity. */
- int total_sample_count = dof_gather_total_sample_count(i_radius + 1, DOF_SLIGHT_FOCUS_DENSITY);
- float fac = saturate(1.0 - abs(center_data.coc) / float(layer_threshold));
- accum_data.layer_opacity += float(total_sample_count) * fac * fac;
-#endif
- accum_data.layer_opacity += layer_weight;
-
- /* Logic of dof_gather_accumulate_sample(). */
- weight *= (1.0 - accum_weight);
- center_data.coc_sqr = center_data.coc * (center_data.coc * weight);
- center_data.color *= weight;
- center_data.coc *= weight;
- center_data.weight = weight;
-
-#ifdef DOF_FOREGROUND_PASS /* Reduce issue with closer foreground over distant foreground. */
- float ring_area = sqr(bordering_radius);
- dof_gather_ammend_weight(center_data, ring_area);
-#endif
-
- /* Accumulate center as its own ring. */
- dof_gather_accumulate_sample_ring(
- center_data, 1, false, do_fast_gather, is_foreground, accum_data);
- }
-}
-
-int dof_gather_total_sample_count_with_density_change(const int ring_count,
- const int ring_density,
- int density_change)
-{
- int sample_count_per_density_change = dof_gather_total_sample_count(ring_count, ring_density) -
- dof_gather_total_sample_count(
- ring_count - gather_density_change_ring, ring_density);
-
- return dof_gather_total_sample_count(ring_count, ring_density) +
- sample_count_per_density_change * density_change;
-}
-
-void dof_gather_accumulate_resolve(int total_sample_count,
- DofGatherData accum_data,
- out vec4 out_col,
- out float out_weight,
- out vec2 out_occlusion)
-{
- float weight_inv = safe_rcp(accum_data.weight);
- out_col = accum_data.color * weight_inv;
- out_occlusion = vec2(abs(accum_data.coc), accum_data.coc_sqr) * weight_inv;
-
-#ifdef DOF_FOREGROUND_PASS
- out_weight = 1.0 - accum_data.transparency;
-#else
- if (accum_data.weight > 0.0) {
- out_weight = accum_data.layer_opacity / float(total_sample_count);
- }
- else {
- out_weight = 0.0;
- }
-#endif
- /* Gathering may not accumulate to 1.0 alpha because of float precision. */
- if (out_weight > 0.99) {
- out_weight = 1.0;
- }
- else if (out_weight < 0.01) {
- out_weight = 0.0;
- }
- /* Same thing for alpha channel. */
- if (out_col.a > 0.99) {
- out_col.a = 1.0;
- }
- else if (out_col.a < 0.01) {
- out_col.a = 0.0;
- }
-}
-
-ivec2 dof_square_ring_sample_offset(int ring_distance, int sample_id)
-{
- /**
- * Generate samples in a square pattern with the ring radius. X is the center tile.
- *
- * Dist1 Dist2
- * 6 5 4 3 2
- * 3 2 1 7 1
- * . X 0 . X 0
- * . . . . .
- * . . . . .
- *
- * Samples are expected to be mirrored to complete the pattern.
- */
- ivec2 offset;
- if (sample_id < ring_distance) {
- offset.x = ring_distance;
- offset.y = sample_id;
- }
- else if (sample_id < ring_distance * 3) {
- offset.x = ring_distance - sample_id + ring_distance;
- offset.y = ring_distance;
- }
- else {
- offset.x = -ring_distance;
- offset.y = ring_distance - sample_id + 3 * ring_distance;
- }
- return offset;
-}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_reduce_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_reduce_frag.glsl
deleted file mode 100644
index d21254003f9..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_reduce_frag.glsl
+++ /dev/null
@@ -1,179 +0,0 @@
-
-/**
- * Reduce pass: Downsample the color buffer to generate mipmaps.
- * Also decide if a pixel is to be convolved by scattering or gathering during the first pass.
- */
-
-#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
-
-/** Inputs:
- * COPY_PASS: Is output of setup pass (halfres) and downsample pass (quarter res).
- * REDUCE_PASS: Is previous Gather input miplvl (halfres >> miplvl).
- */
-uniform sampler2D colorBuffer;
-uniform sampler2D cocBuffer;
-uniform sampler2D downsampledBuffer;
-
-uniform vec2 bokehAnisotropy;
-uniform float scatterColorThreshold;
-uniform float scatterCocThreshold;
-uniform float scatterColorNeighborMax;
-uniform float colorNeighborClamping;
-
-/** Outputs:
- * COPY_PASS: Gather input mip0.
- * REDUCE_PASS: Is next Gather input miplvl (halfres >> miplvl).
- */
-layout(location = 0) out vec4 outColor;
-layout(location = 1) out float outCoc;
-
-#ifdef COPY_PASS
-
-layout(location = 2) out vec3 outScatterColor;
-
-/* NOTE: Do not compare alpha as it is not scattered by the scatter pass. */
-float dof_scatter_neighborhood_rejection(vec3 color)
-{
- color = min(vec3(scatterColorNeighborMax), color);
-
- float validity = 0.0;
-
- /* Centered in the middle of 4 quarter res texel. */
- vec2 texel_size = 1.0 / vec2(textureSize(downsampledBuffer, 0).xy);
- vec2 uv = (gl_FragCoord.xy * 0.5) * texel_size;
-
- vec3 max_diff = vec3(0.0);
- for (int i = 0; i < 4; i++) {
- vec2 sample_uv = uv + quad_offsets[i] * texel_size;
- vec3 ref = textureLod(downsampledBuffer, sample_uv, 0.0).rgb;
-
- ref = min(vec3(scatterColorNeighborMax), ref);
- float diff = max_v3(max(vec3(0.0), abs(ref - color)));
-
- const float rejection_threshold = 0.7;
- diff = saturate(diff / rejection_threshold - 1.0);
- validity = max(validity, diff);
- }
-
- return validity;
-}
-
-/* This avoids sprite popping in and out at the screen border and
- * drawing sprites larger than the screen. */
-float dof_scatter_screen_border_rejection(float coc, vec2 uv, vec2 screen_size)
-{
- vec2 screen_pos = uv * screen_size;
- float min_screen_border_distance = min_v2(min(screen_pos, screen_size - screen_pos));
- /* Fullres to halfres CoC. */
- coc *= 0.5;
- /* Allow 10px transition. */
- const float rejection_hardeness = 1.0 / 10.0;
- return saturate((min_screen_border_distance - abs(coc)) * rejection_hardeness + 1.0);
-}
-
-float dof_scatter_luminosity_rejection(vec3 color)
-{
- const float rejection_hardness = 1.0;
- return saturate(max_v3(color - scatterColorThreshold) * rejection_hardness);
-}
-
-float dof_scatter_coc_radius_rejection(float coc)
-{
- const float rejection_hardness = 0.3;
- return saturate((abs(coc) - scatterCocThreshold) * rejection_hardness);
-}
-
-float fast_luma(vec3 color)
-{
- return (2.0 * color.g) + color.r + color.b;
-}
-
-/* Lightweight version of neighborhood clamping found in TAA. */
-vec3 dof_neighborhood_clamping(vec3 color)
-{
- vec2 texel_size = 1.0 / vec2(textureSize(colorBuffer, 0));
- vec2 uv = gl_FragCoord.xy * texel_size;
- vec4 ofs = vec4(-1, 1, -1, 1) * texel_size.xxyy;
-
- /* Luma clamping. 3x3 square neighborhood. */
- float c00 = fast_luma(textureLod(colorBuffer, uv + ofs.xz, 0.0).rgb);
- float c01 = fast_luma(textureLod(colorBuffer, uv + ofs.xz * vec2(1.0, 0.0), 0.0).rgb);
- float c02 = fast_luma(textureLod(colorBuffer, uv + ofs.xw, 0.0).rgb);
-
- float c10 = fast_luma(textureLod(colorBuffer, uv + ofs.xz * vec2(0.0, 1.0), 0.0).rgb);
- float c11 = fast_luma(color);
- float c12 = fast_luma(textureLod(colorBuffer, uv + ofs.xw * vec2(0.0, 1.0), 0.0).rgb);
-
- float c20 = fast_luma(textureLod(colorBuffer, uv + ofs.yz, 0.0).rgb);
- float c21 = fast_luma(textureLod(colorBuffer, uv + ofs.yz * vec2(1.0, 0.0), 0.0).rgb);
- float c22 = fast_luma(textureLod(colorBuffer, uv + ofs.yw, 0.0).rgb);
-
- float avg_luma = avg8(c00, c01, c02, c10, c12, c20, c21, c22);
- float max_luma = max8(c00, c01, c02, c10, c12, c20, c21, c22);
-
- float upper_bound = mix(max_luma, avg_luma, colorNeighborClamping);
- upper_bound = mix(c11, upper_bound, colorNeighborClamping);
-
- float clamped_luma = min(upper_bound, c11);
-
- return color * clamped_luma * safe_rcp(c11);
-}
-
-/* Simple copy pass where we select what pixels to scatter. Also the resolution might change.
- * NOTE: The texture can end up being too big because of the mipmap padding. We correct for
- * that during the convolution phase. */
-void main()
-{
- vec2 halfres = vec2(textureSize(colorBuffer, 0).xy);
- vec2 uv = gl_FragCoord.xy / halfres;
-
- outColor = textureLod(colorBuffer, uv, 0.0);
- outCoc = textureLod(cocBuffer, uv, 0.0).r;
-
- outColor.rgb = dof_neighborhood_clamping(outColor.rgb);
-
- /* Only scatter if luminous enough. */
- float do_scatter = dof_scatter_luminosity_rejection(outColor.rgb);
- /* Only scatter if CoC is big enough. */
- do_scatter *= dof_scatter_coc_radius_rejection(outCoc);
- /* Only scatter if CoC is not too big to avoid performance issues. */
- do_scatter *= dof_scatter_screen_border_rejection(outCoc, uv, halfres);
- /* Only scatter if neighborhood is different enough. */
- do_scatter *= dof_scatter_neighborhood_rejection(outColor.rgb);
- /* For debugging. */
- do_scatter *= float(!no_scatter_pass);
-
- outScatterColor = mix(vec3(0.0), outColor.rgb, do_scatter);
- outColor.rgb = mix(outColor.rgb, vec3(0.0), do_scatter);
-
- /* Apply energy conservation to anamorphic scattered bokeh. */
- outScatterColor /= min_v2(bokehAnisotropy);
-}
-
-#else /* REDUCE_PASS */
-
-/* Downsample pass done for each mip starting from mip1. */
-void main()
-{
- vec2 input_texel_size = 1.0 / vec2(textureSize(colorBuffer, 0).xy);
- /* Center uv around the 4 pixels of the previous mip. */
- vec2 quad_center = (floor(gl_FragCoord.xy) * 2.0 + 1.0) * input_texel_size;
-
- vec4 colors[4];
- vec4 cocs;
- for (int i = 0; i < 4; i++) {
- vec2 sample_uv = quad_center + quad_offsets[i] * input_texel_size;
- colors[i] = dof_load_gather_color(colorBuffer, sample_uv, 0.0);
- cocs[i] = textureLod(cocBuffer, sample_uv, 0.0).r;
- }
-
- vec4 weights = dof_downsample_bilateral_coc_weights(cocs);
- weights *= dof_downsample_bilateral_color_weights(colors);
- /* Normalize so that the sum is 1. */
- weights *= safe_rcp(sum(weights));
-
- outColor = weighted_sum_array(colors, weights);
- outCoc = dot(cocs, weights);
-}
-
-#endif
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl
deleted file mode 100644
index 57027c71156..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl
+++ /dev/null
@@ -1,212 +0,0 @@
-
-/**
- * Recombine Pass: Load separate convolution layer and composite with self slight defocus
- * convolution and in-focus fields.
- *
- * The halfres gather methods are fast but lack precision for small CoC areas. To fix this we
- * do a bruteforce gather to have a smooth transition between in-focus and defocus regions.
- */
-
-#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
-#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
-
-uniform sampler2D fullResColorBuffer;
-uniform sampler2D fullResDepthBuffer;
-
-uniform sampler2D bgColorBuffer;
-uniform sampler2D bgWeightBuffer;
-uniform sampler2D bgTileBuffer;
-
-uniform sampler2D fgColorBuffer;
-uniform sampler2D fgWeightBuffer;
-uniform sampler2D fgTileBuffer;
-
-uniform sampler2D holefillColorBuffer;
-uniform sampler2D holefillWeightBuffer;
-
-uniform sampler2D bokehLut;
-
-uniform float bokehMaxSize;
-
-in vec4 uvcoordsvar;
-
-out vec4 fragColor;
-
-void dof_slight_focus_gather(float radius, out vec4 out_color, out float out_weight)
-{
- /* offset coord to avoid correlation with sampling pattern. */
- vec4 noise = texelfetch_noise_tex(gl_FragCoord.xy + 7.0);
-
- DofGatherData fg_accum = GATHER_DATA_INIT;
- DofGatherData bg_accum = GATHER_DATA_INIT;
-
- int i_radius = clamp(int(radius + 0.5), 0, int(layer_threshold));
- const int resolve_ring_density = DOF_SLIGHT_FOCUS_DENSITY;
- ivec2 texel = ivec2(gl_FragCoord.xy);
-
- bool first_ring = true;
-
- for (int ring = i_radius; ring > 0; ring--) {
- DofGatherData fg_ring = GATHER_DATA_INIT;
- DofGatherData bg_ring = GATHER_DATA_INIT;
-
- int ring_distance = ring;
- int ring_sample_count = resolve_ring_density * ring_distance;
- for (int sample_id = 0; sample_id < ring_sample_count; sample_id++) {
- int s = sample_id * (4 / resolve_ring_density) +
- int(noise.y * float((4 - resolve_ring_density) * ring_distance));
-
- ivec2 offset = dof_square_ring_sample_offset(ring_distance, s);
- float ring_dist = length(vec2(offset));
-
- DofGatherData pair_data[2];
- for (int i = 0; i < 2; i++) {
- ivec2 sample_offset = ((i == 0) ? offset : -offset);
- ivec2 sample_texel = texel + sample_offset;
- /* OPTI: could precompute the factor. */
- vec2 sample_uv = (vec2(sample_texel) + 0.5) / vec2(textureSize(fullResDepthBuffer, 0));
- float depth = textureLod(fullResDepthBuffer, sample_uv, 0.0).r;
- pair_data[i].color = safe_color(textureLod(fullResColorBuffer, sample_uv, 0.0));
- pair_data[i].coc = dof_coc_from_zdepth(depth);
- pair_data[i].dist = ring_dist;
-#ifdef DOF_BOKEH_TEXTURE
- /* Contains subpixel distance to bokeh shape. */
- pair_data[i].dist = texelFetch(bokehLut, sample_offset + DOF_MAX_SLIGHT_FOCUS_RADIUS, 0).r;
-#endif
- pair_data[i].coc = clamp(pair_data[i].coc, -bokehMaxSize, bokehMaxSize);
- }
-
- float bordering_radius = ring_dist + 0.5;
- const float isect_mul = 1.0;
- dof_gather_accumulate_sample_pair(
- pair_data, bordering_radius, isect_mul, first_ring, false, false, bg_ring, bg_accum);
-
-#ifdef DOF_BOKEH_TEXTURE
- /* Swap distances in order to flip bokeh shape for foreground. */
- float tmp = pair_data[0].dist;
- pair_data[0].dist = pair_data[1].dist;
- pair_data[1].dist = tmp;
-#endif
- dof_gather_accumulate_sample_pair(
- pair_data, bordering_radius, isect_mul, first_ring, false, true, fg_ring, fg_accum);
- }
-
- dof_gather_accumulate_sample_ring(
- bg_ring, ring_sample_count * 2, first_ring, false, false, bg_accum);
- dof_gather_accumulate_sample_ring(
- fg_ring, ring_sample_count * 2, first_ring, false, true, fg_accum);
-
- first_ring = false;
- }
-
- /* Center sample. */
- vec2 sample_uv = uvcoordsvar.xy;
- float depth = textureLod(fullResDepthBuffer, sample_uv, 0.0).r;
- DofGatherData center_data;
- center_data.color = safe_color(textureLod(fullResColorBuffer, sample_uv, 0.0));
- center_data.coc = dof_coc_from_zdepth(depth);
- center_data.coc = clamp(center_data.coc, -bokehMaxSize, bokehMaxSize);
- center_data.dist = 0.0;
-
- /* Slide 38. */
- float bordering_radius = 0.5;
-
- dof_gather_accumulate_center_sample(
- center_data, bordering_radius, i_radius, false, true, fg_accum);
- dof_gather_accumulate_center_sample(
- center_data, bordering_radius, i_radius, false, false, bg_accum);
-
- vec4 bg_col, fg_col;
- float bg_weight, fg_weight;
- vec2 unused_occlusion;
-
- int total_sample_count = dof_gather_total_sample_count(i_radius + 1, resolve_ring_density);
- dof_gather_accumulate_resolve(total_sample_count, bg_accum, bg_col, bg_weight, unused_occlusion);
- dof_gather_accumulate_resolve(total_sample_count, fg_accum, fg_col, fg_weight, unused_occlusion);
-
- /* Fix weighting issues on perfectly focus > slight focus transitionning areas. */
- if (abs(center_data.coc) < 0.5) {
- bg_col = center_data.color;
- bg_weight = 1.0;
- }
-
- /* Alpha Over */
- float alpha = 1.0 - fg_weight;
- out_weight = bg_weight * alpha + fg_weight;
- out_color = bg_col * bg_weight * alpha + fg_col * fg_weight;
-}
-
-void dof_resolve_load_layer(sampler2D color_tex,
- sampler2D weight_tex,
- out vec4 out_color,
- out float out_weight)
-{
- vec2 pixel_co = gl_FragCoord.xy / 2.0;
- vec2 uv = pixel_co / textureSize(color_tex, 0).xy;
- out_color = textureLod(color_tex, uv, 0.0);
- out_weight = textureLod(weight_tex, uv, 0.0).r;
-}
-
-void main(void)
-{
- ivec2 tile_co = ivec2(gl_FragCoord.xy / float(DOF_TILE_DIVISOR));
- CocTile coc_tile = dof_coc_tile_load(fgTileBuffer, bgTileBuffer, tile_co);
- CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile);
-
- fragColor = vec4(0.0);
- float weight = 0.0;
-
- vec4 layer_color;
- float layer_weight;
-
- if (!no_holefill_pass && prediction.do_holefill) {
- dof_resolve_load_layer(holefillColorBuffer, holefillWeightBuffer, layer_color, layer_weight);
- fragColor = layer_color * safe_rcp(layer_weight);
- weight = float(layer_weight > 0.0);
- }
-
- if (!no_background_pass && prediction.do_background) {
- dof_resolve_load_layer(bgColorBuffer, bgWeightBuffer, layer_color, layer_weight);
- /* Always prefer background to holefill pass. */
- layer_color *= safe_rcp(layer_weight);
- layer_weight = float(layer_weight > 0.0);
- /* Composite background. */
- fragColor = fragColor * (1.0 - layer_weight) + layer_color;
- weight = weight * (1.0 - layer_weight) + layer_weight;
- /* Fill holes with the composited background. */
- fragColor *= safe_rcp(weight);
- weight = float(weight > 0.0);
- }
-
- if (!no_slight_focus_pass && prediction.do_slight_focus) {
- dof_slight_focus_gather(coc_tile.fg_slight_focus_max_coc, layer_color, layer_weight);
- /* Composite slight defocus. */
- fragColor = fragColor * (1.0 - layer_weight) + layer_color;
- weight = weight * (1.0 - layer_weight) + layer_weight;
- }
-
- if (!no_focus_pass && prediction.do_focus) {
- layer_color = safe_color(textureLod(fullResColorBuffer, uvcoordsvar.xy, 0.0));
- layer_weight = 1.0;
- /* Composite in focus. */
- fragColor = fragColor * (1.0 - layer_weight) + layer_color;
- weight = weight * (1.0 - layer_weight) + layer_weight;
- }
-
- if (!no_foreground_pass && prediction.do_foreground) {
- dof_resolve_load_layer(fgColorBuffer, fgWeightBuffer, layer_color, layer_weight);
- /* Composite foreground. */
- fragColor = fragColor * (1.0 - layer_weight) + layer_color;
- }
-
- /* Fix float precision issue in alpha compositing. */
- if (fragColor.a > 0.99) {
- fragColor.a = 1.0;
- }
-
-#if 0 /* Debug */
- if (coc_tile.fg_slight_focus_max_coc >= 0.5) {
- fragColor.rgb *= vec3(1.0, 0.1, 0.1);
- }
-#endif
-}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_setup_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_setup_frag.glsl
deleted file mode 100644
index 235145b221b..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_setup_frag.glsl
+++ /dev/null
@@ -1,65 +0,0 @@
-
-/**
- * Setup pass: CoC and luma aware downsample to half resolution of the input scene color buffer.
- *
- * An addition to the downsample CoC, we output the maximum slight out of focus CoC to be
- * sure we don't miss a pixel.
- */
-
-#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
-
-/* Full resolution. */
-uniform sampler2D colorBuffer;
-uniform sampler2D depthBuffer;
-
-uniform float bokehMaxSize;
-
-/* Half resolution. */
-layout(location = 0) out vec4 outColor;
-layout(location = 1) out vec2 outCoc; /* x: Downsample CoC, y: Max slight focus abs CoC */
-
-void main()
-{
- vec2 fullres_texel_size = 1.0 / vec2(textureSize(colorBuffer, 0).xy);
- /* Center uv around the 4 fullres pixels. */
- vec2 quad_center = (floor(gl_FragCoord.xy) * 2.0 + 1.0) * fullres_texel_size;
-
- vec4 colors[4];
- vec4 depths;
- for (int i = 0; i < 4; i++) {
- vec2 sample_uv = quad_center + quad_offsets[i] * fullres_texel_size;
- colors[i] = safe_color(textureLod(colorBuffer, sample_uv, 0.0));
- depths[i] = textureLod(depthBuffer, sample_uv, 0.0).r;
- }
-
- vec4 cocs = dof_coc_from_zdepth(depths);
-
- cocs = clamp(cocs, -bokehMaxSize, bokehMaxSize);
-
- vec4 weights = dof_downsample_bilateral_coc_weights(cocs);
- weights *= dof_downsample_bilateral_color_weights(colors);
- /* Normalize so that the sum is 1. */
- weights *= safe_rcp(sum(weights));
-
- outColor = weighted_sum_array(colors, weights);
- outCoc.x = dot(cocs, weights);
-
- /* Max slight focus abs CoC. */
-
- /* Clamp to 0.5 if full in defocus to differentiate full focus tiles with coc == 0.0.
- * This enables an optimization in the resolve pass. */
- const vec4 threshold = vec4(layer_threshold + layer_offset);
- cocs = abs(cocs);
- bvec4 defocus = greaterThan(cocs, threshold);
- bvec4 focus = lessThanEqual(cocs, vec4(0.5));
- if (any(defocus) && any(focus)) {
- /* For the same reason as in the flatten pass. This is a case we cannot optimize for. */
- cocs = mix(cocs, vec4(DOF_TILE_MIXED), focus);
- cocs = mix(cocs, vec4(DOF_TILE_MIXED), defocus);
- }
- else {
- cocs = mix(cocs, vec4(DOF_TILE_FOCUS), focus);
- cocs = mix(cocs, vec4(DOF_TILE_DEFOCUS), defocus);
- }
- outCoc.y = max_v4(cocs);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_downsample_cube_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_downsample_cube_frag.glsl
deleted file mode 100644
index 05f16b866e0..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_downsample_cube_frag.glsl
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * Simple down-sample shader. Takes the average of the 4 texels of lower mip.
- */
-
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-
-uniform samplerCube source;
-uniform float texelSize;
-
-flat in int fFace;
-
-out vec4 FragColor;
-
-const vec3 maj_axes[6] = vec3[6](vec3(1.0, 0.0, 0.0),
- vec3(-1.0, 0.0, 0.0),
- vec3(0.0, 1.0, 0.0),
- vec3(0.0, -1.0, 0.0),
- vec3(0.0, 0.0, 1.0),
- vec3(0.0, 0.0, -1.0));
-const vec3 x_axis[6] = vec3[6](vec3(0.0, 0.0, -1.0),
- vec3(0.0, 0.0, 1.0),
- vec3(1.0, 0.0, 0.0),
- vec3(1.0, 0.0, 0.0),
- vec3(1.0, 0.0, 0.0),
- vec3(-1.0, 0.0, 0.0));
-const vec3 y_axis[6] = vec3[6](vec3(0.0, -1.0, 0.0),
- vec3(0.0, -1.0, 0.0),
- vec3(0.0, 0.0, 1.0),
- vec3(0.0, 0.0, -1.0),
- vec3(0.0, -1.0, 0.0),
- vec3(0.0, -1.0, 0.0));
-
-void main()
-{
- vec2 uvs = gl_FragCoord.xy * texelSize;
-
- uvs = 2.0 * uvs - 1.0;
-
- vec3 cubevec = x_axis[fFace] * uvs.x + y_axis[fFace] * uvs.y + maj_axes[fFace];
-
- FragColor = textureLod(source, cubevec, 0.0);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_downsample_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_downsample_frag.glsl
deleted file mode 100644
index 9fc258da185..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_downsample_frag.glsl
+++ /dev/null
@@ -1,42 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-
-/**
- * Simple down-sample shader.
- * Do a gaussian filter using 4 bilinear texture samples.
- */
-
-uniform sampler2D source;
-uniform float fireflyFactor;
-
-#ifndef COPY_SRC
-uniform vec2 texelSize;
-#endif
-
-out vec4 FragColor;
-
-void main()
-{
-#ifdef COPY_SRC
- vec2 uvs = gl_FragCoord.xy / vec2(textureSize(source, 0));
- FragColor = textureLod(source, uvs, 0.0);
- FragColor = safe_color(FragColor);
-
- /* Clamped brightness. */
- float luma = max(1e-8, max_v3(FragColor.rgb));
- FragColor *= 1.0 - max(0.0, luma - fireflyFactor) / luma;
-
-#else
- /* NOTE(@fclem): textureSize() does not work the same on all implementations
- * when changing the min and max texture levels. Use uniform instead (see T87801). */
- vec2 uvs = gl_FragCoord.xy * texelSize;
- vec4 ofs = texelSize.xyxy * vec4(0.75, 0.75, -0.75, -0.75);
- uvs *= 2.0;
-
- FragColor = textureLod(source, uvs + ofs.xy, 0.0);
- FragColor += textureLod(source, uvs + ofs.xw, 0.0);
- FragColor += textureLod(source, uvs + ofs.zy, 0.0);
- FragColor += textureLod(source, uvs + ofs.zw, 0.0);
- FragColor *= 0.25;
-#endif
-}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl
deleted file mode 100644
index 70f1e9f1e66..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl
+++ /dev/null
@@ -1,115 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
-#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
-#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
-
-/**
- * This shader only compute maximum horizon angles for each directions.
- * The final integration is done at the resolve stage with the shading normal.
- */
-
-in vec4 uvcoordsvar;
-
-out vec4 FragColor;
-
-uniform sampler2D normalBuffer;
-
-/* Similar to https://atyuwen.github.io/posts/normal-reconstruction/.
- * This samples the depth buffer 4 time for each direction to get the most correct
- * implicit normal reconstruction out of the depth buffer. */
-vec3 view_position_derivative_from_depth(vec2 uvs, vec2 ofs, vec3 vP, float depth_center)
-{
- vec2 uv1 = uvs - ofs * 2.0;
- vec2 uv2 = uvs - ofs;
- vec2 uv3 = uvs + ofs;
- vec2 uv4 = uvs + ofs * 2.0;
- vec4 H;
- H.x = textureLod(maxzBuffer, uv1, 0.0).r;
- H.y = textureLod(maxzBuffer, uv2, 0.0).r;
- H.z = textureLod(maxzBuffer, uv3, 0.0).r;
- H.w = textureLod(maxzBuffer, uv4, 0.0).r;
- /* Fix issue with depth precision. Take even larger diff. */
- vec4 diff = abs(vec4(depth_center, H.yzw) - H.x);
- if (max_v4(diff) < 2.4e-7 && all(lessThan(diff.xyz, diff.www))) {
- return 0.25 * (get_view_space_from_depth(uv3, H.w) - get_view_space_from_depth(uv1, H.x));
- }
- /* Simplified (H.xw + 2.0 * (H.yz - H.xw)) - depth_center */
- vec2 deltas = abs((2.0 * H.yz - H.xw) - depth_center);
- if (deltas.x < deltas.y) {
- return vP - get_view_space_from_depth(uv2, H.y);
- }
- else {
- return get_view_space_from_depth(uv3, H.z) - vP;
- }
-}
-
-/* TODO(fclem) port to a common place for other effects to use. */
-bool reconstruct_view_position_and_normal_from_depth(vec2 uvs, out vec3 vP, out vec3 vNg)
-{
- vec2 texel_size = vec2(abs(dFdx(uvs.x)), abs(dFdy(uvs.y)));
- float depth_center = textureLod(maxzBuffer, uvs, 0.0).r;
-
- vP = get_view_space_from_depth(uvs, depth_center);
-
- vec3 dPdx = view_position_derivative_from_depth(uvs, texel_size * vec2(1, 0), vP, depth_center);
- vec3 dPdy = view_position_derivative_from_depth(uvs, texel_size * vec2(0, 1), vP, depth_center);
-
- vNg = safe_normalize(cross(dPdx, dPdy));
-
- /* Background case. */
- if (depth_center == 1.0) {
- return false;
- }
-
- return true;
-}
-
-#ifdef DEBUG_AO
-
-void main()
-{
- vec3 vP, vNg;
- vec2 uvs = uvcoordsvar.xy;
-
- if (!reconstruct_view_position_and_normal_from_depth(uvs * hizUvScale.xy, vP, vNg)) {
- /* Handle Background case. Prevent artifact due to uncleared Horizon Render Target. */
- FragColor = vec4(0.0);
- }
- else {
- vec3 P = transform_point(ViewMatrixInverse, vP);
- vec3 V = cameraVec(P);
- vec3 vV = viewCameraVec(vP);
- vec3 vN = normal_decode(texture(normalBuffer, uvs).rg, vV);
- vec3 N = transform_direction(ViewMatrixInverse, vN);
- vec3 Ng = transform_direction(ViewMatrixInverse, vNg);
-
- OcclusionData data = occlusion_load(vP, 1.0);
-
- if (min_v4(abs(data.horizons)) != M_PI) {
- FragColor = vec4(diffuse_occlusion(data, V, N, Ng));
- }
- else {
- FragColor = vec4(1.0);
- }
- }
-}
-
-#else
-
-void main()
-{
- vec2 uvs = uvcoordsvar.xy;
- float depth = textureLod(maxzBuffer, uvs * hizUvScale.xy, 0.0).r;
- vec3 vP = get_view_space_from_depth(uvs, depth);
-
- OcclusionData data = NO_OCCLUSION_DATA;
- /* Do not trace for background */
- if (depth != 1.0) {
- data = occlusion_search(vP, maxzBuffer, aoDistance, 0.0, 8.0);
- }
-
- FragColor = pack_occlusion_data(data);
-}
-#endif
diff --git a/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl
deleted file mode 100644
index 8ef39a55921..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl
+++ /dev/null
@@ -1,76 +0,0 @@
-/**
- * Shader that down-sample depth buffer,
- * saving min and max value of each texel in the above mipmaps.
- * Adapted from http://rastergrid.com/blog/2010/10/hierarchical-z-map-based-occlusion-culling/
- *
- * Major simplification has been made since we pad the buffer to always be bigger than input to
- * avoid mipmapping misalignement.
- */
-
-#ifdef LAYERED
-uniform sampler2DArray depthBuffer;
-uniform int depthLayer;
-#else
-uniform sampler2D depthBuffer;
-#endif
-
-#ifndef COPY_DEPTH
-uniform vec2 texelSize;
-#endif
-
-#ifdef LAYERED
-# define sampleLowerMip(t) texture(depthBuffer, vec3(t, depthLayer)).r
-# define gatherLowerMip(t) textureGather(depthBuffer, vec3(t, depthLayer))
-#else
-# define sampleLowerMip(t) texture(depthBuffer, t).r
-# define gatherLowerMip(t) textureGather(depthBuffer, t)
-#endif
-
-#ifdef MIN_PASS
-# define minmax2(a, b) min(a, b)
-# define minmax3(a, b, c) min(min(a, b), c)
-# define minmax4(a, b, c, d) min(min(min(a, b), c), d)
-#else /* MAX_PASS */
-# define minmax2(a, b) max(a, b)
-# define minmax3(a, b, c) max(max(a, b), c)
-# define minmax4(a, b, c, d) max(max(max(a, b), c), d)
-#endif
-
-/* On some AMD card / driver combination, it is needed otherwise,
- * the shader does not write anything. */
-#if defined(GPU_INTEL) || defined(GPU_ATI)
-out vec4 fragColor;
-#endif
-
-void main()
-{
- vec2 texel = gl_FragCoord.xy;
-
-#ifdef COPY_DEPTH
- vec2 uv = texel / vec2(textureSize(depthBuffer, 0).xy);
-
- float val = sampleLowerMip(uv);
-#else
- /* NOTE(@fclem): textureSize() does not work the same on all implementations
- * when changing the min and max texture levels. Use uniform instead (see T87801). */
- vec2 uv = texel * 2.0 * texelSize;
-
- vec4 samp;
-# ifdef GPU_ARB_texture_gather
- samp = gatherLowerMip(uv);
-# else
- samp.x = sampleLowerMip(uv + vec2(-0.5, -0.5) * texelSize);
- samp.y = sampleLowerMip(uv + vec2(-0.5, 0.5) * texelSize);
- samp.z = sampleLowerMip(uv + vec2(0.5, -0.5) * texelSize);
- samp.w = sampleLowerMip(uv + vec2(0.5, 0.5) * texelSize);
-# endif
-
- float val = minmax4(samp.x, samp.y, samp.z, samp.w);
-#endif
-
-#if defined(GPU_INTEL) || defined(GPU_ATI)
- /* Use color format instead of 24bit depth texture */
- fragColor = vec4(val);
-#endif
- gl_FragDepth = val;
-}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl
deleted file mode 100644
index 7331f92ba6d..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl
+++ /dev/null
@@ -1,36 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-
-/* Convert depth to Mist factor */
-uniform vec3 mistSettings;
-uniform sampler2D depthBuffer;
-
-#define mistStart mistSettings.x
-#define mistInvDistance mistSettings.y
-#define mistFalloff mistSettings.z
-
-out vec4 fragColor;
-
-void main()
-{
- vec2 texel_size = 1.0 / vec2(textureSize(depthBuffer, 0)).xy;
- vec2 uvs = gl_FragCoord.xy * texel_size;
-
- float depth = textureLod(depthBuffer, uvs, 0.0).r;
- vec3 co = get_view_space_from_depth(uvs, depth);
-
- float zcor = (ProjectionMatrix[3][3] == 0.0) ? length(co) : -co.z;
-
- /* bring depth into 0..1 range */
- float mist = saturate((zcor - mistStart) * mistInvDistance);
-
- /* falloff */
- mist = pow(mist, mistFalloff);
-
- fragColor = vec4(mist);
-
- // if (mist > 0.999) fragColor = vec4(1.0);
- // else if (mist > 0.0001) fragColor = vec4(0.5);
- // else fragColor = vec4(0.0);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_reflection_lib.glsl b/source/blender/draw/engines/eevee/shaders/effect_reflection_lib.glsl
deleted file mode 100644
index ed600a3be86..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_reflection_lib.glsl
+++ /dev/null
@@ -1,101 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-
-/* Based on:
- * "Stochastic Screen Space Reflections"
- * by Tomasz Stachowiak.
- * https://www.ea.com/frostbite/news/stochastic-screen-space-reflections
- * and
- * "Stochastic all the things: raytracing in hybrid real-time rendering"
- * by Tomasz Stachowiak.
- * https://media.contentapi.ea.com/content/dam/ea/seed/presentations/dd18-seed-raytracing-in-hybrid-real-time-rendering.pdf
- */
-
-uniform ivec2 halfresOffset;
-
-struct HitData {
- /** Hit direction scaled by intersection time. */
- vec3 hit_dir;
- /** Screen space [0..1] depth of the reflection hit position, or -1.0 for planar reflections. */
- float hit_depth;
- /** Inverse probability of ray spawning in this direction. */
- float ray_pdf_inv;
- /** True if ray has hit valid geometry. */
- bool is_hit;
- /** True if ray was generated from a planar reflection probe. */
- bool is_planar;
-};
-
-void encode_hit_data(HitData data, vec3 hit_sP, vec3 vP, out vec4 hit_data, out float hit_depth)
-{
- vec3 hit_vP = get_view_space_from_depth(hit_sP.xy, hit_sP.z);
- hit_data.xyz = hit_vP - vP;
- hit_depth = data.is_planar ? -1.0 : hit_sP.z;
- /* Record 1.0 / pdf to reduce the computation in the resolve phase. */
- /* Encode hit validity in sign. */
- hit_data.w = data.ray_pdf_inv * ((data.is_hit) ? 1.0 : -1.0);
-}
-
-HitData decode_hit_data(vec4 hit_data, float hit_depth)
-{
- HitData data;
- data.hit_dir.xyz = hit_data.xyz;
- data.hit_depth = hit_depth;
- data.is_planar = (hit_depth == -1.0);
- data.ray_pdf_inv = abs(hit_data.w);
- data.is_hit = (hit_data.w > 0.0);
- return data;
-}
-
-/* Blue noise categorised into 4 sets of samples.
- * See "Stochastic all the things" presentation slide 32-37. */
-const int resolve_samples_count = 9;
-const vec2 resolve_sample_offsets[36] = vec2[36](
- /* Set 1. */
- /* First Ring (2x2). */
- vec2(0, 0),
- /* Second Ring (6x6). */
- vec2(-1, 3),
- vec2(1, 3),
- vec2(-1, 1),
- vec2(3, 1),
- vec2(-2, 0),
- vec2(3, 0),
- vec2(2, -1),
- vec2(1, -2),
- /* Set 2. */
- /* First Ring (2x2). */
- vec2(1, 1),
- /* Second Ring (6x6). */
- vec2(-2, 3),
- vec2(3, 3),
- vec2(0, 2),
- vec2(2, 2),
- vec2(-2, -1),
- vec2(1, -1),
- vec2(0, -2),
- vec2(3, -2),
- /* Set 3. */
- /* First Ring (2x2). */
- vec2(0, 1),
- /* Second Ring (6x6). */
- vec2(0, 3),
- vec2(3, 2),
- vec2(-2, 1),
- vec2(2, 1),
- vec2(-1, 0),
- vec2(-2, -2),
- vec2(0, -1),
- vec2(2, -2),
- /* Set 4. */
- /* First Ring (2x2). */
- vec2(1, 0),
- /* Second Ring (6x6). */
- vec2(2, 3),
- vec2(-2, 2),
- vec2(-1, 2),
- vec2(1, 2),
- vec2(2, 0),
- vec2(-1, -1),
- vec2(3, -1),
- vec2(-1, -2));
diff --git a/source/blender/draw/engines/eevee/shaders/effect_reflection_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_reflection_resolve_frag.glsl
deleted file mode 100644
index 7568d70bd14..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_reflection_resolve_frag.glsl
+++ /dev/null
@@ -1,220 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
-#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
-#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl)
-#pragma BLENDER_REQUIRE(surface_lib.glsl)
-#pragma BLENDER_REQUIRE(effect_reflection_lib.glsl)
-
-/* Based on:
- * "Stochastic Screen Space Reflections"
- * by Tomasz Stachowiak.
- * https://www.ea.com/frostbite/news/stochastic-screen-space-reflections
- * and
- * "Stochastic all the things: raytracing in hybrid real-time rendering"
- * by Tomasz Stachowiak.
- * https://media.contentapi.ea.com/content/dam/ea/seed/presentations/dd18-seed-raytracing-in-hybrid-real-time-rendering.pdf
- */
-
-uniform sampler2D colorBuffer;
-uniform sampler2D normalBuffer;
-uniform sampler2D specroughBuffer;
-uniform sampler2D hitBuffer;
-uniform sampler2D hitDepth;
-
-uniform int samplePoolOffset;
-
-in vec4 uvcoordsvar;
-
-out vec4 fragColor;
-
-vec4 ssr_get_scene_color_and_mask(vec3 hit_vP, int planar_index, float mip)
-{
- vec2 uv;
- if (planar_index != -1) {
- uv = get_uvs_from_view(hit_vP);
- /* Planar X axis is flipped. */
- uv.x = 1.0 - uv.x;
- }
- else {
- /* Find hit position in previous frame. */
- /* TODO: Combine matrices. */
- vec3 hit_P = transform_point(ViewMatrixInverse, hit_vP);
- /* TODO: real reprojection with motion vectors, etc... */
- uv = project_point(pastViewProjectionMatrix, hit_P).xy * 0.5 + 0.5;
- }
-
- vec3 color;
- if (planar_index != -1) {
- color = textureLod(probePlanars, vec3(uv, planar_index), mip).rgb;
- }
- else {
- color = textureLod(colorBuffer, uv * hizUvScale.xy, mip).rgb;
- }
-
- /* Clamped brightness. */
- float luma = max_v3(color);
- color *= 1.0 - max(0.0, luma - ssrFireflyFac) * safe_rcp(luma);
-
- float mask = screen_border_mask(uv);
- return vec4(color, mask);
-}
-
-void resolve_reflection_sample(int planar_index,
- vec2 sample_uv,
- vec3 vP,
- vec3 vN,
- vec3 vV,
- float roughness_squared,
- float cone_tan,
- inout float weight_accum,
- inout vec4 ssr_accum)
-{
- vec4 hit_data = texture(hitBuffer, sample_uv);
- float hit_depth = texture(hitDepth, sample_uv).r;
- HitData data = decode_hit_data(hit_data, hit_depth);
-
- float hit_dist = length(data.hit_dir);
-
- /* Slide 54. */
- float bsdf = bsdf_ggx(vN, data.hit_dir / hit_dist, vV, roughness_squared);
-
- float weight = bsdf * data.ray_pdf_inv;
-
- /* Do not reuse hitpoint from planar reflections for normal reflections and vice versa. */
- if ((planar_index == -1 && data.is_planar) || (planar_index != -1 && !data.is_planar)) {
- return;
- }
- /* Do not add light if ray has failed but still weight it. */
- if (!data.is_hit) {
- weight_accum += weight;
- return;
- }
-
- vec3 hit_vP = vP + data.hit_dir;
-
- /* Compute cone footprint in screen space. */
- float cone_footprint = hit_dist * cone_tan;
- float homcoord = ProjectionMatrix[2][3] * hit_vP.z + ProjectionMatrix[3][3];
- cone_footprint *= max(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) / homcoord;
- cone_footprint *= ssrBrdfBias * 0.5;
- /* Estimate a cone footprint to sample a corresponding mipmap level. */
- float mip = log2(cone_footprint * max_v2(vec2(textureSize(specroughBuffer, 0))));
-
- vec4 radiance_mask = ssr_get_scene_color_and_mask(hit_vP, planar_index, mip);
-
- ssr_accum += radiance_mask * weight;
- weight_accum += weight;
-}
-
-void raytrace_resolve(ClosureInputGlossy cl_in,
- inout ClosureEvalGlossy cl_eval,
- inout ClosureEvalCommon cl_common,
- inout ClosureOutputGlossy cl_out)
-{
- float roughness = cl_in.roughness;
-
- vec4 ssr_accum = vec4(0.0);
- float weight_acc = 0.0;
-
- if (roughness < ssrMaxRoughness + 0.2) {
- /* Find Planar Reflections affecting this pixel */
- int planar_index = -1;
- for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) {
- float fade = probe_attenuation_planar(planars_data[i], cl_common.P);
- fade *= probe_attenuation_planar_normal_roughness(planars_data[i], cl_in.N, 0.0);
- if (fade > 0.5) {
- planar_index = i;
- break;
- }
- }
-
- vec3 V, P, N;
- if (planar_index != -1) {
- PlanarData pd = planars_data[planar_index];
- /* Evaluate everything in reflected space. */
- P = line_plane_intersect(cl_common.P, cl_common.V, pd.pl_plane_eq);
- V = reflect(cl_common.V, pd.pl_normal);
- N = reflect(cl_in.N, pd.pl_normal);
- }
- else {
- V = cl_common.V;
- P = cl_common.P;
- N = cl_in.N;
- }
-
- /* Using view space */
- vec3 vV = transform_direction(ViewMatrix, cl_common.V);
- vec3 vP = transform_point(ViewMatrix, cl_common.P);
- vec3 vN = transform_direction(ViewMatrix, cl_in.N);
-
- float roughness_squared = max(1e-3, sqr(roughness));
- float cone_cos = cone_cosine(roughness_squared);
- float cone_tan = sqrt(1.0 - cone_cos * cone_cos) / cone_cos;
- cone_tan *= mix(saturate(dot(vN, -vV) * 2.0), 1.0, roughness); /* Elongation fit */
-
- int sample_pool = int((uint(gl_FragCoord.x) & 1u) + (uint(gl_FragCoord.y) & 1u) * 2u);
- sample_pool = (sample_pool + (samplePoolOffset / 5)) % 4;
-
- for (int i = 0; i < resolve_samples_count; i++) {
- int sample_id = sample_pool * resolve_samples_count + i;
- vec2 texture_size = vec2(textureSize(hitBuffer, 0));
- vec2 sample_texel = texture_size * uvcoordsvar.xy * ssrUvScale;
- vec2 sample_uv = (sample_texel + resolve_sample_offsets[sample_id]) / texture_size;
-
- resolve_reflection_sample(
- planar_index, sample_uv, vP, vN, vV, roughness_squared, cone_tan, weight_acc, ssr_accum);
- }
- }
-
- /* Compute SSR contribution */
- ssr_accum *= safe_rcp(weight_acc);
- /* fade between 0.5 and 1.0 roughness */
- ssr_accum.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, roughness);
-
- cl_eval.raytrace_radiance = ssr_accum.rgb * ssr_accum.a;
- cl_common.specular_accum -= ssr_accum.a;
-}
-
-CLOSURE_EVAL_FUNCTION_DECLARE_1(ssr_resolve, Glossy)
-
-void main()
-{
- float depth = textureLod(maxzBuffer, uvcoordsvar.xy * hizUvScale.xy, 0.0).r;
-
- if (depth == 1.0) {
- discard;
- }
-
- ivec2 texel = ivec2(gl_FragCoord.xy);
- vec4 speccol_roughness = texelFetch(specroughBuffer, texel, 0).rgba;
- vec3 brdf = speccol_roughness.rgb;
- float roughness = speccol_roughness.a;
-
- if (max_v3(brdf) <= 0.0) {
- discard;
- }
-
- FragDepth = depth;
-
- viewPosition = get_view_space_from_depth(uvcoordsvar.xy, depth);
- worldPosition = transform_point(ViewMatrixInverse, viewPosition);
-
- vec2 normal_encoded = texelFetch(normalBuffer, texel, 0).rg;
- viewNormal = normal_decode(normal_encoded, viewCameraVec(viewPosition));
- worldNormal = transform_direction(ViewMatrixInverse, viewNormal);
-
- CLOSURE_VARS_DECLARE_1(Glossy);
-
- in_Glossy_0.N = worldNormal;
- in_Glossy_0.roughness = roughness;
-
- /* Do a full deferred evaluation of the glossy BSDF. The only difference is that we inject the
- * SSR resolve before the cubemap iter. BRDF term is already computed during main pass and is
- * passed as specular color. */
- CLOSURE_EVAL_FUNCTION_1(ssr_resolve, Glossy);
-
- fragColor = vec4(out_Glossy_0.radiance * brdf, 1.0);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl
deleted file mode 100644
index 2f1efd588f7..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl
+++ /dev/null
@@ -1,149 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
-#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
-#pragma BLENDER_REQUIRE(raytrace_lib.glsl)
-#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
-#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
-#pragma BLENDER_REQUIRE(effect_reflection_lib.glsl)
-
-/* Based on:
- * "Stochastic Screen Space Reflections"
- * by Tomasz Stachowiak.
- * https://www.ea.com/frostbite/news/stochastic-screen-space-reflections
- * and
- * "Stochastic all the things: raytracing in hybrid real-time rendering"
- * by Tomasz Stachowiak.
- * https://media.contentapi.ea.com/content/dam/ea/seed/presentations/dd18-seed-raytracing-in-hybrid-real-time-rendering.pdf
- */
-
-uniform sampler2D normalBuffer;
-uniform sampler2D specroughBuffer;
-uniform vec2 targetSize;
-uniform float randomScale;
-
-in vec4 uvcoordsvar;
-
-layout(location = 0) out vec4 hitData;
-layout(location = 1) out float hitDepth;
-
-void main()
-{
- vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
- /* Decorrelate from AA. */
- /* TODO(fclem) we should use a more general approach for more random number dimensions. */
- vec2 random_px = floor(fract(rand.xy * 2.2074408460575947536) * 1.99999) - 0.5;
- rand.xy = fract(rand.xy * 3.2471795724474602596);
-
- /* Randomly choose the pixel to start the ray from when tracing at lower resolution.
- * This method also make sure we always start from the center of a fullres texel. */
- vec2 uvs = (gl_FragCoord.xy + random_px * randomScale) / (targetSize * ssrUvScale);
-
- float depth = textureLod(maxzBuffer, uvs * hizUvScale.xy, 0.0).r;
-
- HitData data;
- data.is_planar = false;
- data.ray_pdf_inv = 0.0;
- data.is_hit = false;
- data.hit_dir = vec3(0.0, 0.0, 0.0);
- /* Default: not hits. */
- encode_hit_data(data, data.hit_dir, data.hit_dir, hitData, hitDepth);
-
- /* Early out */
- /* We can't do discard because we don't clear the render target. */
- if (depth == 1.0) {
- return;
- }
-
- /* Using view space */
- vec3 vP = get_view_space_from_depth(uvs, depth);
- vec3 P = transform_point(ViewMatrixInverse, vP);
- vec3 vV = viewCameraVec(vP);
- vec3 V = cameraVec(P);
- vec3 vN = normal_decode(texture(normalBuffer, uvs, 0).rg, vV);
- vec3 N = transform_direction(ViewMatrixInverse, vN);
-
- /* Retrieve pixel data */
- vec4 speccol_roughness = texture(specroughBuffer, uvs, 0).rgba;
-
- /* Early out */
- if (dot(speccol_roughness.rgb, vec3(1.0)) == 0.0) {
- return;
- }
-
- float roughness = speccol_roughness.a;
- float alpha = max(1e-3, roughness * roughness);
-
- /* Early out */
- if (roughness > ssrMaxRoughness + 0.2) {
- return;
- }
-
- /* Planar Reflections */
- int planar_id = -1;
- for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) {
- PlanarData pd = planars_data[i];
-
- float fade = probe_attenuation_planar(pd, P);
- fade *= probe_attenuation_planar_normal_roughness(pd, N, 0.0);
-
- if (fade > 0.5) {
- /* Find view vector / reflection plane intersection. */
- /* TODO: optimize, use view space for all. */
- vec3 P_plane = line_plane_intersect(P, V, pd.pl_plane_eq);
- vP = transform_point(ViewMatrix, P_plane);
-
- planar_id = i;
- data.is_planar = true;
- break;
- }
- }
-
- /* Gives *perfect* reflection for very small roughness */
- if (roughness < 0.04) {
- rand.xzw *= 0.0;
- }
- /* Importance sampling bias */
- rand.x = mix(rand.x, 0.0, ssrBrdfBias);
-
- vec3 vT, vB;
- make_orthonormal_basis(vN, vT, vB); /* Generate tangent space */
-
- float pdf;
- vec3 vH = sample_ggx(rand.xzw, alpha, vV, vN, vT, vB, pdf);
- vec3 vR = reflect(-vV, vH);
-
- if (isnan(pdf)) {
- /* Seems that somethings went wrong.
- * This only happens on extreme cases where the normal deformed too much to have any valid
- * reflections. */
- return;
- }
-
- if (data.is_planar) {
- vec3 view_plane_normal = transform_direction(ViewMatrix, planars_data[planar_id].pl_normal);
- /* For planar reflections, we trace inside the reflected view. */
- vR = reflect(vR, view_plane_normal);
- }
-
- Ray ray;
- ray.origin = vP;
- ray.direction = vR * 1e16;
-
- RayTraceParameters params;
- params.thickness = ssrThickness;
- params.jitter = rand.y;
- params.trace_quality = ssrQuality;
- params.roughness = alpha * alpha;
-
- vec3 hit_sP;
- if (data.is_planar) {
- data.is_hit = raytrace_planar(ray, params, planar_id, hit_sP);
- }
- else {
- data.is_hit = raytrace(ray, params, true, false, hit_sP);
- }
- data.ray_pdf_inv = safe_rcp(pdf);
-
- encode_hit_data(data, hit_sP, ray.origin, hitData, hitDepth);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl
deleted file mode 100644
index 58bbb825043..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl
+++ /dev/null
@@ -1,78 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
-#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl)
-
-/* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */
-
-#define MAX_SSS_SAMPLES 65
-layout(std140) uniform sssProfile
-{
- vec4 kernel[MAX_SSS_SAMPLES];
- vec4 radii_max_radius;
- int sss_samples;
-};
-
-uniform sampler2D depthBuffer;
-uniform sampler2D sssIrradiance;
-uniform sampler2D sssRadius;
-uniform sampler2D sssAlbedo;
-
-layout(location = 0) out vec4 sssRadiance;
-
-void main(void)
-{
- vec2 pixel_size = 1.0 / vec2(textureSize(depthBuffer, 0).xy); /* TODO: precompute. */
- vec2 uvs = gl_FragCoord.xy * pixel_size;
- vec3 sss_irradiance = texture(sssIrradiance, uvs).rgb;
- float sss_radius = texture(sssRadius, uvs).r * radii_max_radius.w;
- float depth = texture(depthBuffer, uvs).r;
- float depth_view = get_view_z_from_depth(depth);
-
- float rand = texelfetch_noise_tex(gl_FragCoord.xy).r;
-#ifdef FIRST_PASS
- float angle = M_2PI * rand + M_PI_2;
- vec2 dir = vec2(1.0, 0.0);
-#else /* SECOND_PASS */
- float angle = M_2PI * rand;
- vec2 dir = vec2(0.0, 1.0);
-#endif
- vec2 dir_rand = vec2(cos(angle), sin(angle));
-
- /* Compute kernel bounds in 2D. */
- float homcoord = ProjectionMatrix[2][3] * depth_view + ProjectionMatrix[3][3];
- vec2 scale = vec2(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) * sss_radius / homcoord;
- vec2 finalStep = scale * 0.5; /* samples range -1..1 */
-
- float sss_radius_inv = 1.0 / max(1e-8, sss_radius);
-
- /* Center sample */
- vec3 accum = sss_irradiance * kernel[0].rgb;
-
- for (int i = 1; i < sss_samples && i < MAX_SSS_SAMPLES; i++) {
- vec2 sample_uv = uvs + kernel[i].a * finalStep *
- ((abs(kernel[i].a) > sssJitterThreshold) ? dir : dir_rand);
- vec3 color = texture(sssIrradiance, sample_uv).rgb;
- float sample_depth = texture(depthBuffer, sample_uv).r;
- sample_depth = get_view_z_from_depth(sample_depth);
- /* Depth correction factor. See Real Time Realistic Skin Translucency 2010
- * by Jimenez, eqs. 2 and 9, and D9740.
- * Coefficient -2 follows from gaussian_profile() from gpu_material.c and
- * from the definition of finalStep. */
- float depth_delta = (depth_view - sample_depth) * sss_radius_inv;
- float s = exp(-2.0 * sqr(depth_delta));
- /* Out of view samples. */
- if (any(lessThan(sample_uv, vec2(0.0))) || any(greaterThan(sample_uv, vec2(1.0)))) {
- s = 0.0;
- }
- /* Mix with first sample in failure case and apply kernel color. */
- accum += kernel[i].rgb * mix(sss_irradiance, color, s);
- }
-
-#if defined(FIRST_PASS)
- sssRadiance = vec4(accum, 1.0);
-#else /* SECOND_PASS */
- sssRadiance = vec4(accum * texture(sssAlbedo, uvs).rgb, 1.0);
-#endif
-}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl b/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl
deleted file mode 100644
index c54621f123d..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl
+++ /dev/null
@@ -1,117 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-
-uniform sampler2D colorBuffer;
-uniform sampler2D depthBuffer;
-uniform sampler2D colorHistoryBuffer;
-
-uniform mat4 prevViewProjectionMatrix;
-
-out vec4 FragColor;
-
-#ifdef USE_REPROJECTION
-
-/**
- * Adapted from https://casual-effects.com/g3d/G3D10/data-files/shader/Film/Film_temporalAA.pix
- * which is adapted from
- * https://github.com/gokselgoktas/temporal-anti-aliasing/blob/master/Assets/Resources/Shaders/TemporalAntiAliasing.cginc
- * which is adapted from https://github.com/playdeadgames/temporal
- * Optimization by Stubbesaurus and epsilon adjustment to avoid division by zero.
- *
- * This can cause 3x3 blocks of color when there is a thin edge of a similar color that
- * is varying in intensity.
- */
-vec3 clip_to_aabb(vec3 color, vec3 minimum, vec3 maximum, vec3 average)
-{
- /* NOTE: only clips towards aabb center (but fast!) */
- vec3 center = 0.5 * (maximum + minimum);
- vec3 extents = 0.5 * (maximum - minimum);
- vec3 dist = color - center;
- vec3 ts = abs(extents) / max(abs(dist), vec3(0.0001));
- float t = saturate(min_v3(ts));
- return center + dist * t;
-}
-
-/**
- * Vastly based on https://github.com/playdeadgames/temporal
- */
-void main()
-{
- vec2 screen_res = vec2(textureSize(colorBuffer, 0).xy);
- vec2 uv = gl_FragCoord.xy / screen_res;
- ivec2 texel = ivec2(gl_FragCoord.xy);
-
- /* Compute pixel position in previous frame. */
- float depth = textureLod(depthBuffer, uv, 0.0).r;
- vec3 pos = get_world_space_from_depth(uv, depth);
- vec2 uv_history = project_point(prevViewProjectionMatrix, pos).xy * 0.5 + 0.5;
-
- /* HACK: Reject lookdev spheres from TAA reprojection. */
- if (depth == 0.0) {
- uv_history = uv;
- }
-
- ivec2 texel_history = ivec2(uv_history * screen_res);
- vec4 color_history = textureLod(colorHistoryBuffer, uv_history, 0.0);
-
- /* Color bounding box clamping. 3x3 neighborhood. */
- vec4 c02 = texelFetchOffset(colorBuffer, texel, 0, ivec2(-1, 1));
- vec4 c12 = texelFetchOffset(colorBuffer, texel, 0, ivec2(0, 1));
- vec4 c22 = texelFetchOffset(colorBuffer, texel, 0, ivec2(1, 1));
- vec4 c01 = texelFetchOffset(colorBuffer, texel, 0, ivec2(-1, 0));
- vec4 c11 = texelFetchOffset(colorBuffer, texel, 0, ivec2(0, 0));
- vec4 c21 = texelFetchOffset(colorBuffer, texel, 0, ivec2(1, 0));
- vec4 c00 = texelFetchOffset(colorBuffer, texel, 0, ivec2(-1, -1));
- vec4 c10 = texelFetchOffset(colorBuffer, texel, 0, ivec2(0, -1));
- vec4 c20 = texelFetchOffset(colorBuffer, texel, 0, ivec2(1, -1));
-
- vec4 color = c11;
-
- /* AABB minmax */
- vec4 min_col = min9(c02, c12, c22, c01, c11, c21, c00, c10, c20);
- vec4 max_col = max9(c02, c12, c22, c01, c11, c21, c00, c10, c20);
- vec4 avg_col = avg9(c02, c12, c22, c01, c11, c21, c00, c10, c20);
-
- /* bias the color aabb toward the center (rounding the shape) */
- vec4 min_center = min5(c12, c01, c11, c21, c10);
- vec4 max_center = max5(c12, c01, c11, c21, c10);
- vec4 avg_center = avg5(c12, c01, c11, c21, c10);
- min_col = (min_col + min_center) * 0.5;
- max_col = (max_col + max_center) * 0.5;
- avg_col = (avg_col + avg_center) * 0.5;
-
- /* Clip color toward the center of the neighborhood colors AABB box. */
- color_history.rgb = clip_to_aabb(color_history.rgb, min_col.rgb, max_col.rgb, avg_col.rgb);
-
- /* Luminance weighting. */
- /* TODO: correct luminance. */
- float lum0 = dot(color.rgb, vec3(0.333));
- float lum1 = dot(color_history.rgb, vec3(0.333));
- float diff = abs(lum0 - lum1) / max(lum0, max(lum1, 0.2));
- float weight = 1.0 - diff;
- float alpha = mix(0.04, 0.12, weight * weight);
-
- color_history = mix(color_history, color, alpha);
-
- bool out_of_view = any(greaterThanEqual(abs(uv_history - 0.5), vec2(0.5)));
- color_history = (out_of_view) ? color : color_history;
-
- FragColor = safe_color(color_history);
- /* There is some ghost issue if we use the alpha
- * in the viewport. Overwriting alpha fixes it. */
- FragColor.a = color.a;
-}
-
-#else
-
-uniform float alpha;
-
-void main()
-{
- ivec2 texel = ivec2(gl_FragCoord.xy);
- vec4 color = texelFetch(colorBuffer, texel, 0);
- vec4 color_history = texelFetch(colorHistoryBuffer, texel, 0);
- FragColor = safe_color(mix(color_history, color, alpha));
-}
-#endif
diff --git a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
deleted file mode 100644
index ee48c468630..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
+++ /dev/null
@@ -1,272 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
-#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
-#pragma BLENDER_REQUIRE(lights_lib.glsl)
-
-in vec4 uvcoordsvar;
-
-out vec4 FragColor;
-
-uniform sampler2D depthBuffer;
-uniform sampler1D sssTexProfile;
-uniform sampler2D sssRadius;
-uniform sampler2DArray sssShadowCubes;
-uniform sampler2DArray sssShadowCascades;
-
-#define MAX_SSS_SAMPLES 65
-#define SSS_LUT_SIZE 64.0
-#define SSS_LUT_SCALE ((SSS_LUT_SIZE - 1.0) / float(SSS_LUT_SIZE))
-#define SSS_LUT_BIAS (0.5 / float(SSS_LUT_SIZE))
-
-layout(std140) uniform sssProfile
-{
- vec4 kernel[MAX_SSS_SAMPLES];
- vec4 radii_max_radius;
- int sss_samples;
-};
-
-vec3 sss_profile(float s)
-{
- s /= radii_max_radius.w;
- return texture(sssTexProfile, saturate(s) * SSS_LUT_SCALE + SSS_LUT_BIAS).rgb;
-}
-
-float light_translucent_power_with_falloff(LightData ld, vec3 N, vec4 l_vector)
-{
- float power, falloff;
- /* XXX: Removing Area Power. */
- /* TODO: put this out of the shader. */
- if (ld.l_type >= AREA_RECT) {
- power = (ld.l_sizex * ld.l_sizey * 4.0 * M_PI) * (1.0 / 80.0);
- if (ld.l_type == AREA_ELLIPSE) {
- power *= M_PI * 0.25;
- }
- power *= 0.3 * 20.0 *
- max(0.0, dot(-ld.l_forward, l_vector.xyz / l_vector.w)); /* XXX ad hoc, empirical */
- power /= (l_vector.w * l_vector.w);
- falloff = dot(N, l_vector.xyz / l_vector.w);
- }
- else if (ld.l_type == SUN) {
- power = 1.0 / (1.0 + (ld.l_radius * ld.l_radius * 0.5));
- power *= ld.l_radius * ld.l_radius * M_PI; /* Removing area light power. */
- power *= M_2PI * 0.78; /* Matching cycles with point light. */
- power *= 0.082; /* XXX ad hoc, empirical */
- falloff = dot(N, -ld.l_forward);
- }
- else {
- power = (4.0 * ld.l_radius * ld.l_radius) * (1.0 / 10.0);
- power *= 1.5; /* XXX ad hoc, empirical */
- power /= (l_vector.w * l_vector.w);
- falloff = dot(N, l_vector.xyz / l_vector.w);
- }
- /* No transmittance at grazing angle (hide artifacts) */
- return power * saturate(falloff * 2.0);
-}
-
-/* Some driver poorly optimize this code. Use direct reference to matrices. */
-#define sd(x) shadows_data[x]
-#define scube(x) shadows_cube_data[x]
-#define scascade(x) shadows_cascade_data[x]
-
-float shadow_cube_radial_depth(vec3 cubevec, float tex_id, int shadow_id)
-{
- float depth = sample_cube(sssShadowCubes, cubevec, tex_id).r;
- /* To reverting the constant bias from shadow rendering. (Tweaked for 16bit shadowmaps) */
- const float depth_bias = 3.1e-5;
- depth = saturate(depth - depth_bias);
-
- depth = linear_depth(true, depth, sd(shadow_id).sh_far, sd(shadow_id).sh_near);
- depth *= length(cubevec / max_v3(abs(cubevec)));
- return depth;
-}
-
-vec3 light_translucent(LightData ld, vec3 P, vec3 N, vec4 l_vector, vec2 rand, float sss_scale)
-{
- int shadow_id = int(ld.l_shadowid);
-
- vec4 L = (ld.l_type != SUN) ? l_vector : vec4(-ld.l_forward, 1.0);
-
- /* We use the full l_vector.xyz so that the spread is minimize
- * if the shading point is further away from the light source */
- /* TODO(fclem): do something better than this. */
- vec3 T, B;
- make_orthonormal_basis(L.xyz / L.w, T, B);
-
- vec3 n;
- vec4 depths;
- float d, dist;
- int data_id = int(sd(shadow_id).sh_data_index);
- if (ld.l_type == SUN) {
- vec4 view_z = vec4(dot(P - cameraPos, cameraForward));
-
- vec4 weights = step(scascade(data_id).split_end_distances, view_z);
- float id = abs(4.0 - dot(weights, weights));
- if (id > 3.0) {
- return vec3(0.0);
- }
-
- /* Same factor as in get_cascade_world_distance(). */
- float range = abs(sd(shadow_id).sh_far - sd(shadow_id).sh_near);
-
- vec4 shpos = scascade(data_id).shadowmat[int(id)] * vec4(P, 1.0);
- dist = shpos.z * range;
-
- if (shpos.z > 1.0 || shpos.z < 0.0) {
- return vec3(0.0);
- }
-
- float tex_id = scascade(data_id).sh_tex_index + id;
-
- /* Assume cascades have same height and width. */
- vec2 ofs = vec2(1.0, 0.0) / float(textureSize(sssShadowCascades, 0).x);
- d = sample_cascade(sssShadowCascades, shpos.xy, tex_id).r;
- depths.x = sample_cascade(sssShadowCascades, shpos.xy + ofs.xy, tex_id).r;
- depths.y = sample_cascade(sssShadowCascades, shpos.xy + ofs.yx, tex_id).r;
- depths.z = sample_cascade(sssShadowCascades, shpos.xy - ofs.xy, tex_id).r;
- depths.w = sample_cascade(sssShadowCascades, shpos.xy - ofs.yx, tex_id).r;
-
- /* To reverting the constant bias from shadow rendering. (Tweaked for 16bit shadowmaps) */
- float depth_bias = 3.1e-5;
- depths = saturate(depths - depth_bias);
- d = saturate(d - depth_bias);
-
- /* Size of a texel in world space.
- * FIXME This is only correct if l_right is the same right vector used for shadowmap creation.
- * This won't work if the shadow matrix is rotated (soft shadows).
- * TODO: precompute. */
- float unit_world_in_uv_space = length(mat3(scascade(data_id).shadowmat[int(id)]) * ld.l_right);
- float dx_scale = 2.0 * ofs.x / unit_world_in_uv_space;
-
- d *= range;
- depths *= range;
-
- /* This is the normal of the occluder in world space. */
- // vec3 T = ld.l_forward * dx + ld.l_right * dx_scale;
- // vec3 B = ld.l_forward * dy + ld.l_up * dx_scale;
- // n = normalize(cross(T, B));
- }
- else {
- float ofs = 1.0 / float(textureSize(sssShadowCubes, 0).x);
-
- vec3 cubevec = transform_point(scube(data_id).shadowmat, P);
- dist = length(cubevec);
- cubevec /= dist;
- /* tex_id == data_id for cube shadowmap */
- float tex_id = float(data_id);
- d = shadow_cube_radial_depth(cubevec, tex_id, shadow_id);
- /* NOTE: The offset is irregular in respect to cubeface uvs. But it has
- * a much more uniform behavior than biasing based on face derivatives. */
- depths.x = shadow_cube_radial_depth(cubevec + T * ofs, tex_id, shadow_id);
- depths.y = shadow_cube_radial_depth(cubevec + B * ofs, tex_id, shadow_id);
- depths.z = shadow_cube_radial_depth(cubevec - T * ofs, tex_id, shadow_id);
- depths.w = shadow_cube_radial_depth(cubevec - B * ofs, tex_id, shadow_id);
- }
-
- float dx = depths.x - depths.z;
- float dy = depths.y - depths.w;
-
- float s = min(d, min_v4(depths));
-
- /* To avoid light leak from depth discontinuity and shadowmap aliasing. */
- float slope_bias = (abs(dx) + abs(dy)) * 0.5;
- s -= slope_bias;
-
- float delta = dist - s;
-
- float power = light_translucent_power_with_falloff(ld, N, l_vector);
-
- return power * sss_profile(abs(delta) / sss_scale);
-}
-
-#undef sd
-#undef scube
-#undef scsmd
-
-/* Similar to https://atyuwen.github.io/posts/normal-reconstruction/.
- * This samples the depth buffer 4 time for each direction to get the most correct
- * implicit normal reconstruction out of the depth buffer. */
-vec3 view_position_derivative_from_depth(vec2 uvs, vec2 ofs, vec3 vP, float depth_center)
-{
- vec2 uv1 = uvs - ofs * 2.0;
- vec2 uv2 = uvs - ofs;
- vec2 uv3 = uvs + ofs;
- vec2 uv4 = uvs + ofs * 2.0;
- vec4 H;
- H.x = textureLod(depthBuffer, uv1, 0.0).r;
- H.y = textureLod(depthBuffer, uv2, 0.0).r;
- H.z = textureLod(depthBuffer, uv3, 0.0).r;
- H.w = textureLod(depthBuffer, uv4, 0.0).r;
- /* Fix issue with depth precision. Take even larger diff. */
- vec4 diff = abs(vec4(depth_center, H.yzw) - H.x);
- if (max_v4(diff) < 2.4e-7 && all(lessThan(diff.xyz, diff.www))) {
- return 0.25 * (get_view_space_from_depth(uv3, H.w) - get_view_space_from_depth(uv1, H.x));
- }
- /* Simplified (H.xw + 2.0 * (H.yz - H.xw)) - depth_center */
- vec2 deltas = abs((2.0 * H.yz - H.xw) - depth_center);
- if (deltas.x < deltas.y) {
- return vP - get_view_space_from_depth(uv2, H.y);
- }
- else {
- return get_view_space_from_depth(uv3, H.z) - vP;
- }
-}
-
-/* TODO(fclem) port to a common place for other effects to use. */
-bool reconstruct_view_position_and_normal_from_depth(vec2 uvs, out vec3 vP, out vec3 vNg)
-{
- vec2 texel_size = vec2(abs(dFdx(uvs.x)), abs(dFdy(uvs.y)));
- float depth_center = textureLod(depthBuffer, uvs, 0.0).r;
-
- vP = get_view_space_from_depth(uvs, depth_center);
-
- vec3 dPdx = view_position_derivative_from_depth(uvs, texel_size * vec2(1, 0), vP, depth_center);
- vec3 dPdy = view_position_derivative_from_depth(uvs, texel_size * vec2(0, 1), vP, depth_center);
-
- vNg = safe_normalize(cross(dPdx, dPdy));
-
- /* Background case. */
- if (depth_center == 1.0) {
- return false;
- }
-
- return true;
-}
-
-void main(void)
-{
- vec2 uvs = uvcoordsvar.xy;
- float sss_scale = texture(sssRadius, uvs).r;
-
- vec3 rand = texelfetch_noise_tex(gl_FragCoord.xy).zwy;
- rand.xy *= fast_sqrt(rand.z);
-
- vec3 vP, vNg;
- reconstruct_view_position_and_normal_from_depth(uvs, vP, vNg);
-
- vec3 P = point_view_to_world(vP);
- vec3 Ng = normal_view_to_world(vNg);
-
- vec3 accum = vec3(0.0);
- for (int i = 0; i < MAX_LIGHT && i < laNumLight; i++) {
- LightData ld = lights_data[i];
-
- /* Only shadowed light can produce translucency */
- if (ld.l_shadowid < 0.0) {
- continue;
- }
-
- vec4 l_vector; /* Non-Normalized Light Vector with length in last component. */
- l_vector.xyz = ld.l_position - P;
- l_vector.w = length(l_vector.xyz);
-
- float att = light_attenuation(ld, l_vector);
- if (att < 1e-8) {
- continue;
- }
-
- accum += att * ld.l_color * light_translucent(ld, P, -Ng, l_vector, rand.xy, sss_scale);
- }
-
- FragColor = vec4(accum, 1.0);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl
deleted file mode 100644
index 9182171bab4..00000000000
--- a/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl
+++ /dev/null
@@ -1,32 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-
-uniform sampler2D depthBuffer;
-
-uniform mat4 prevViewProjMatrix;
-uniform mat4 currViewProjMatrixInv;
-uniform mat4 nextViewProjMatrix;
-
-out vec4 outData;
-
-void main()
-{
- /* Extract pixel motion vector from camera movement. */
- ivec2 texel = ivec2(gl_FragCoord.xy);
- vec2 uv_curr = gl_FragCoord.xy / vec2(textureSize(depthBuffer, 0).xy);
-
- float depth = texelFetch(depthBuffer, texel, 0).r;
-
- uv_curr = uv_curr * 2.0 - 1.0;
- depth = depth * 2.0 - 1.0;
-
- vec3 world_position = project_point(currViewProjMatrixInv, vec3(uv_curr, depth));
- vec2 uv_prev = project_point(prevViewProjMatrix, world_position).xy;
- vec2 uv_next = project_point(nextViewProjMatrix, world_position).xy;
-
- outData.xy = uv_prev - uv_curr;
- outData.zw = uv_next - uv_curr;
-
- /* Encode to unsigned normalized 16bit texture. */
- outData = outData * 0.5 + 0.5;
-}
diff --git a/source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl b/source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl
deleted file mode 100644
index 2274bf8b950..00000000000
--- a/source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl
+++ /dev/null
@@ -1,212 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl)
-#pragma BLENDER_REQUIRE(octahedron_lib.glsl)
-
-#define IRRADIANCE_LIB
-
-/* ---------------------------------------------------------------------- */
-/** \name Structure
- * \{ */
-
-#if defined(IRRADIANCE_SH_L2)
-struct IrradianceData {
- vec3 shcoefs[9];
-};
-
-#else /* defined(IRRADIANCE_HL2) */
-struct IrradianceData {
- vec3 cubesides[3];
-};
-
-#endif
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Resources
- * \{ */
-
-uniform sampler2DArray irradianceGrid;
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Functions
- * \{ */
-
-vec4 irradiance_encode(vec3 rgb)
-{
- float maxRGB = max_v3(rgb);
- float fexp = ceil(log2(maxRGB));
- return vec4(rgb / exp2(fexp), (fexp + 128.0) / 255.0);
-}
-
-vec3 irradiance_decode(vec4 data)
-{
- float fexp = data.a * 255.0 - 128.0;
- return data.rgb * exp2(fexp);
-}
-
-vec4 visibility_encode(vec2 accum, float range)
-{
- accum /= range;
-
- vec4 data;
- data.x = fract(accum.x);
- data.y = floor(accum.x) / 255.0;
- data.z = fract(accum.y);
- data.w = floor(accum.y) / 255.0;
-
- return data;
-}
-
-vec2 visibility_decode(vec4 data, float range)
-{
- return (data.xz + data.yw * 255.0) * range;
-}
-
-IrradianceData load_irradiance_cell(int cell, vec3 N)
-{
- /* Keep in sync with diffuse_filter_probe() */
-
-#if defined(IRRADIANCE_SH_L2)
-
- ivec2 cell_co = ivec2(3, 3);
- int cell_per_row = textureSize(irradianceGrid, 0).x / cell_co.x;
- cell_co.x *= cell % cell_per_row;
- cell_co.y *= cell / cell_per_row;
-
- ivec3 ofs = ivec3(0, 1, 2);
-
- IrradianceData ir;
- ir.shcoefs[0] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.xx, 0), 0).rgb;
- ir.shcoefs[1] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.yx, 0), 0).rgb;
- ir.shcoefs[2] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.zx, 0), 0).rgb;
- ir.shcoefs[3] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.xy, 0), 0).rgb;
- ir.shcoefs[4] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.yy, 0), 0).rgb;
- ir.shcoefs[5] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.zy, 0), 0).rgb;
- ir.shcoefs[6] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.xz, 0), 0).rgb;
- ir.shcoefs[7] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.yz, 0), 0).rgb;
- ir.shcoefs[8] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.zz, 0), 0).rgb;
-
-#else /* defined(IRRADIANCE_HL2) */
-
- ivec2 cell_co = ivec2(3, 2);
- int cell_per_row = textureSize(irradianceGrid, 0).x / cell_co.x;
- cell_co.x *= cell % cell_per_row;
- cell_co.y *= cell / cell_per_row;
-
- ivec3 is_negative = ivec3(step(0.0, -N));
-
- IrradianceData ir;
- ir.cubesides[0] = irradiance_decode(
- texelFetch(irradianceGrid, ivec3(cell_co + ivec2(0, is_negative.x), 0), 0));
- ir.cubesides[1] = irradiance_decode(
- texelFetch(irradianceGrid, ivec3(cell_co + ivec2(1, is_negative.y), 0), 0));
- ir.cubesides[2] = irradiance_decode(
- texelFetch(irradianceGrid, ivec3(cell_co + ivec2(2, is_negative.z), 0), 0));
-
-#endif
-
- return ir;
-}
-
-float load_visibility_cell(int cell, vec3 L, float dist, float bias, float bleed_bias, float range)
-{
- /* Keep in sync with diffuse_filter_probe() */
- ivec2 cell_co = ivec2(prbIrradianceVisSize);
- ivec2 cell_per_row_col = textureSize(irradianceGrid, 0).xy / prbIrradianceVisSize;
- cell_co.x *= (cell % cell_per_row_col.x);
- cell_co.y *= (cell / cell_per_row_col.x) % cell_per_row_col.y;
- float layer = 1.0 + float((cell / cell_per_row_col.x) / cell_per_row_col.y);
-
- vec2 texel_size = 1.0 / vec2(textureSize(irradianceGrid, 0).xy);
- vec2 co = vec2(cell_co) * texel_size;
-
- vec2 uv = mapping_octahedron(-L, vec2(1.0 / float(prbIrradianceVisSize)));
- uv *= vec2(prbIrradianceVisSize) * texel_size;
-
- vec4 data = texture(irradianceGrid, vec3(co + uv, layer));
-
- /* Decoding compressed data */
- vec2 moments = visibility_decode(data, range);
-
- /* Doing chebishev test */
- float variance = abs(moments.x * moments.x - moments.y);
- variance = max(variance, bias / 10.0);
-
- float d = dist - moments.x;
- float p_max = variance / (variance + d * d);
-
- /* Increase contrast in the weight by squaring it */
- p_max *= p_max;
-
- /* Now reduce light-bleeding by removing the [0, x] tail and linearly rescaling (x, 1] */
- p_max = clamp((p_max - bleed_bias) / (1.0 - bleed_bias), 0.0, 1.0);
-
- return (dist <= moments.x) ? 1.0 : p_max;
-}
-
-/* http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/ */
-vec3 spherical_harmonics_L1(vec3 N, vec3 shcoefs[4])
-{
- vec3 sh = vec3(0.0);
-
- sh += 0.282095 * shcoefs[0];
-
- sh += -0.488603 * N.z * shcoefs[1];
- sh += 0.488603 * N.y * shcoefs[2];
- sh += -0.488603 * N.x * shcoefs[3];
-
- return sh;
-}
-
-vec3 spherical_harmonics_L2(vec3 N, vec3 shcoefs[9])
-{
- vec3 sh = vec3(0.0);
-
- sh += 0.282095 * shcoefs[0];
-
- sh += -0.488603 * N.z * shcoefs[1];
- sh += 0.488603 * N.y * shcoefs[2];
- sh += -0.488603 * N.x * shcoefs[3];
-
- sh += 1.092548 * N.x * N.z * shcoefs[4];
- sh += -1.092548 * N.z * N.y * shcoefs[5];
- sh += 0.315392 * (3.0 * N.y * N.y - 1.0) * shcoefs[6];
- sh += -1.092548 * N.x * N.y * shcoefs[7];
- sh += 0.546274 * (N.x * N.x - N.z * N.z) * shcoefs[8];
-
- return sh;
-}
-
-vec3 hl2_basis(vec3 N, vec3 cubesides[3])
-{
- vec3 irradiance = vec3(0.0);
-
- vec3 n_squared = N * N;
-
- irradiance += n_squared.x * cubesides[0];
- irradiance += n_squared.y * cubesides[1];
- irradiance += n_squared.z * cubesides[2];
-
- return irradiance;
-}
-
-vec3 compute_irradiance(vec3 N, IrradianceData ird)
-{
-#if defined(IRRADIANCE_SH_L2)
- return spherical_harmonics_L2(N, ird.shcoefs);
-#else /* defined(IRRADIANCE_HL2) */
- return hl2_basis(N, ird.cubesides);
-#endif
-}
-
-vec3 irradiance_from_cell_get(int cell, vec3 ir_dir)
-{
- IrradianceData ir_data = load_irradiance_cell(cell, ir_dir);
- return compute_irradiance(ir_dir, ir_data);
-}
-
-/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl
deleted file mode 100644
index b0da4274a13..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl
+++ /dev/null
@@ -1,22 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
-
-flat in int pid;
-in vec2 quadCoord;
-
-out vec4 FragColor;
-
-void main()
-{
- float dist_sqr = dot(quadCoord, quadCoord);
-
- /* Discard outside the circle. */
- if (dist_sqr > 1.0) {
- discard;
- }
-
- vec3 view_nor = vec3(quadCoord, sqrt(max(0.0, 1.0 - dist_sqr)));
- vec3 world_ref = mat3(ViewMatrixInverse) * reflect(vec3(0.0, 0.0, -1.0), view_nor);
- FragColor = vec4(textureLod_cubemapArray(probeCubes, vec4(world_ref, pid), 0.0).rgb, 1.0);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl
deleted file mode 100644
index c9af2753baa..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl
+++ /dev/null
@@ -1,44 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-
-/* XXX TODO: fix code duplication. */
-struct CubeData {
- vec4 position_type;
- vec4 attenuation_fac_type;
- mat4 influencemat;
- mat4 parallaxmat;
-};
-
-layout(std140) uniform probe_block
-{
- CubeData probes_data[MAX_PROBE];
-};
-
-uniform float sphere_size;
-uniform vec3 screen_vecs[2];
-
-flat out int pid;
-out vec2 quadCoord;
-
-const vec2 pos[6] = vec2[6](vec2(-1.0, -1.0),
- vec2(1.0, -1.0),
- vec2(-1.0, 1.0),
-
- vec2(1.0, -1.0),
- vec2(1.0, 1.0),
- vec2(-1.0, 1.0));
-
-void main()
-{
- pid = 1 + (gl_VertexID / 6); /* +1 for the world */
- int vert_id = gl_VertexID % 6;
-
- quadCoord = pos[vert_id];
-
- vec3 ws_location = probes_data[pid].position_type.xyz;
- vec3 screen_pos = screen_vecs[0] * quadCoord.x + screen_vecs[1] * quadCoord.y;
- ws_location += screen_pos * sphere_size;
-
- gl_Position = ViewProjectionMatrix * vec4(ws_location, 1.0);
- gl_Position.z += 0.0001; /* Small bias to let the icon draw without zfighting */
-}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl
deleted file mode 100644
index 9ecc50d9df5..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl
+++ /dev/null
@@ -1,170 +0,0 @@
-
-#pragma BLENDER_REQUIRE(random_lib.glsl)
-#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
-#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
-#pragma BLENDER_REQUIRE(irradiance_lib.glsl)
-
-uniform samplerCube probeHdr;
-uniform int probeSize;
-uniform float lodFactor;
-uniform float lodMax;
-uniform float intensityFac;
-
-uniform float sampleCount;
-
-in vec3 worldPosition;
-
-out vec4 FragColor;
-
-#define M_4PI 12.5663706143591729
-
-const mat3 CUBE_ROTATIONS[6] = mat3[](
- mat3(vec3(0.0, 0.0, -1.0), vec3(0.0, -1.0, 0.0), vec3(-1.0, 0.0, 0.0)),
- mat3(vec3(0.0, 0.0, 1.0), vec3(0.0, -1.0, 0.0), vec3(1.0, 0.0, 0.0)),
- mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, -1.0, 0.0)),
- mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 0.0, -1.0), vec3(0.0, 1.0, 0.0)),
- mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, -1.0)),
- mat3(vec3(-1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0)));
-
-vec3 get_cubemap_vector(vec2 co, int face)
-{
- return normalize(CUBE_ROTATIONS[face] * vec3(co * 2.0 - 1.0, 1.0));
-}
-
-float area_element(float x, float y)
-{
- return atan(x * y, sqrt(x * x + y * y + 1));
-}
-
-float texel_solid_angle(vec2 co, float halfpix)
-{
- vec2 v1 = (co - vec2(halfpix)) * 2.0 - 1.0;
- vec2 v2 = (co + vec2(halfpix)) * 2.0 - 1.0;
-
- return area_element(v1.x, v1.y) - area_element(v1.x, v2.y) - area_element(v2.x, v1.y) +
- area_element(v2.x, v2.y);
-}
-
-vec3 octahedral_to_cubemap_proj(vec2 co)
-{
- co = co * 2.0 - 1.0;
-
- vec2 abs_co = abs(co);
- vec3 v = vec3(co, 1.0 - (abs_co.x + abs_co.y));
-
- if (abs_co.x + abs_co.y > 1.0) {
- v.xy = (abs(co.yx) - 1.0) * -sign(co.xy);
- }
-
- return v;
-}
-
-void main()
-{
-#if defined(IRRADIANCE_SH_L2)
- float pixstep = 1.0 / probeSize;
- float halfpix = pixstep / 2.0;
-
- /* Downside: leaks negative values, very bandwidth consuming */
- int comp = int(gl_FragCoord.x) % 3 + (int(gl_FragCoord.y) % 3) * 3;
-
- float weight_accum = 0.0;
- vec3 sh = vec3(0.0);
-
- for (int face = 0; face < 6; face++) {
- for (float x = halfpix; x < 1.0; x += pixstep) {
- for (float y = halfpix; y < 1.0; y += pixstep) {
- float weight, coef;
- vec2 facecoord = vec2(x, y);
- vec3 cubevec = get_cubemap_vector(facecoord, face);
-
- if (comp == 0) {
- coef = 0.282095;
- }
- else if (comp == 1) {
- coef = -0.488603 * cubevec.z * 2.0 / 3.0;
- }
- else if (comp == 2) {
- coef = 0.488603 * cubevec.y * 2.0 / 3.0;
- }
- else if (comp == 3) {
- coef = -0.488603 * cubevec.x * 2.0 / 3.0;
- }
- else if (comp == 4) {
- coef = 1.092548 * cubevec.x * cubevec.z * 1.0 / 4.0;
- }
- else if (comp == 5) {
- coef = -1.092548 * cubevec.z * cubevec.y * 1.0 / 4.0;
- }
- else if (comp == 6) {
- coef = 0.315392 * (3.0 * cubevec.y * cubevec.y - 1.0) * 1.0 / 4.0;
- }
- else if (comp == 7) {
- coef = 1.092548 * cubevec.x * cubevec.y * 1.0 / 4.0;
- }
- else { /* (comp == 8) */
- coef = 0.546274 * (cubevec.x * cubevec.x - cubevec.z * cubevec.z) * 1.0 / 4.0;
- }
-
- weight = texel_solid_angle(facecoord, halfpix);
-
- vec4 samp = textureLod(probeHdr, cubevec, lodMax);
- sh += samp.rgb * coef * weight;
- weight_accum += weight;
- }
- }
- }
- sh *= M_4PI / weight_accum;
-
- FragColor = vec4(sh, 1.0);
-#else
-# if defined(IRRADIANCE_HL2)
- /* Downside: very very low resolution (6 texels), bleed lighting because of interpolation */
- int x = int(gl_FragCoord.x) % 3;
- int y = int(gl_FragCoord.y) % 2;
-
- vec3 cubevec = vec3(1.0, 0.0, 0.0);
-
- if (x == 1) {
- cubevec = cubevec.yxy;
- }
- else if (x == 2) {
- cubevec = cubevec.yyx;
- }
-
- if (y == 1) {
- cubevec = -cubevec;
- }
-# endif
-
- vec3 N, T, B, V;
-
- N = normalize(cubevec);
-
- make_orthonormal_basis(N, T, B); /* Generate tangent space */
-
- /* Integrating Envmap */
- float weight = 0.0;
- vec3 out_radiance = vec3(0.0);
- for (float i = 0; i < sampleCount; i++) {
- vec3 Xi = rand2d_to_cylinder(hammersley_2d(i, sampleCount));
-
- float pdf;
- vec3 L = sample_uniform_hemisphere(Xi, N, T, B, pdf);
- float NL = dot(N, L);
-
- if (NL > 0.0) {
- /* Coarse Approximation of the mapping distortion
- * Unit Sphere -> Cubemap Face */
- const float dist = 4.0 * M_PI / 6.0;
- /* http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html : Equation 13 */
- float lod = clamp(lodFactor - 0.5 * log2(pdf * dist), 0.0, lodMax);
-
- out_radiance += textureLod(probeHdr, L, lod).rgb * NL;
- weight += NL;
- }
- }
-
- FragColor = irradiance_encode(intensityFac * out_radiance / weight);
-#endif
-}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl
deleted file mode 100644
index a5d11f52a1d..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl
+++ /dev/null
@@ -1,79 +0,0 @@
-
-#pragma BLENDER_REQUIRE(random_lib.glsl)
-#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
-#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
-
-uniform samplerCube probeHdr;
-uniform float roughness;
-uniform float texelSize;
-uniform float lodFactor;
-uniform float lodMax;
-uniform float paddingSize;
-uniform float intensityFac;
-uniform float fireflyFactor;
-
-uniform float sampleCount;
-
-in vec3 worldPosition;
-
-out vec4 FragColor;
-
-vec3 octahedral_to_cubemap_proj(vec2 co)
-{
- co = co * 2.0 - 1.0;
-
- vec2 abs_co = abs(co);
- vec3 v = vec3(co, 1.0 - (abs_co.x + abs_co.y));
-
- if (abs_co.x + abs_co.y > 1.0) {
- v.xy = (abs(co.yx) - 1.0) * -sign(co.xy);
- }
-
- return v;
-}
-
-void main()
-{
- vec3 N, T, B, V;
-
- vec3 R = normalize(worldPosition);
-
- /* Isotropic assumption */
- N = V = R;
-
- make_orthonormal_basis(N, T, B); /* Generate tangent space */
-
- /* Integrating Envmap */
- float weight = 0.0;
- vec3 out_radiance = vec3(0.0);
- for (float i = 0; i < sampleCount; i++) {
- vec3 Xi = rand2d_to_cylinder(hammersley_2d(i, sampleCount));
-
- float pdf;
- /* Microfacet normal */
- vec3 H = sample_ggx(Xi, roughness, V, N, T, B, pdf);
- vec3 L = -reflect(V, H);
- float NL = dot(N, L);
-
- if (NL > 0.0) {
- float NH = max(1e-8, dot(N, H)); /* cosTheta */
-
- /* Coarse Approximation of the mapping distortion
- * Unit Sphere -> Cubemap Face */
- const float dist = 4.0 * M_PI / 6.0;
- /* http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html : Equation 13 */
- float lod = clamp(lodFactor - 0.5 * log2(pdf * dist), 0.0, lodMax);
-
- vec3 l_col = textureLod(probeHdr, L, lod).rgb;
-
- /* Clamped brightness. */
- float luma = max(1e-8, max_v3(l_col));
- l_col *= 1.0 - max(0.0, luma - fireflyFactor) / luma;
-
- out_radiance += l_col * NL;
- weight += NL;
- }
- }
-
- FragColor = vec4(intensityFac * out_radiance / weight, 1.0);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl
deleted file mode 100644
index d25ef23a706..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl
+++ /dev/null
@@ -1,97 +0,0 @@
-
-#pragma BLENDER_REQUIRE(random_lib.glsl)
-#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
-#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
-#pragma BLENDER_REQUIRE(irradiance_lib.glsl)
-
-uniform samplerCube probeDepth;
-uniform int outputSize;
-uniform float lodFactor;
-uniform float storedTexelSize;
-uniform float lodMax;
-uniform float nearClip;
-uniform float farClip;
-uniform float visibilityRange;
-uniform float visibilityBlur;
-
-uniform float sampleCount;
-uniform float;
-
-out vec4 FragColor;
-
-vec3 octahedral_to_cubemap_proj(vec2 co)
-{
- co = co * 2.0 - 1.0;
-
- vec2 abs_co = abs(co);
- vec3 v = vec3(co, 1.0 - (abs_co.x + abs_co.y));
-
- if (abs_co.x + abs_co.y > 1.0) {
- v.xy = (abs(co.yx) - 1.0) * -sign(co.xy);
- }
-
- return v;
-}
-
-float linear_depth(float z)
-{
- return (nearClip * farClip) / (z * (nearClip - farClip) + farClip);
-}
-
-float get_world_distance(float depth, vec3 cos)
-{
- float is_background = step(1.0, depth);
- depth = linear_depth(depth);
- depth += 1e1 * is_background;
- cos = normalize(abs(cos));
- float cos_vec = max(cos.x, max(cos.y, cos.z));
- return depth / cos_vec;
-}
-
-void main()
-{
- ivec2 texel = ivec2(gl_FragCoord.xy) % ivec2(outputSize);
-
- vec3 cos;
-
- cos.xy = (vec2(texel) + 0.5) * storedTexelSize;
-
- /* add a 2 pixel border to ensure filtering is correct */
- cos.xy = (cos.xy - storedTexelSize) / (1.0 - 2.0 * storedTexelSize);
-
- float pattern = 1.0;
-
- /* edge mirroring : only mirror if directly adjacent
- * (not diagonally adjacent) */
- vec2 m = abs(cos.xy - 0.5) + 0.5;
- vec2 f = floor(m);
- if (f.x - f.y != 0.0) {
- cos.xy = 1.0 - cos.xy;
- }
-
- /* clamp to [0-1] */
- cos.xy = fract(cos.xy);
-
- /* get cubemap vector */
- cos = normalize(octahedral_to_cubemap_proj(cos.xy));
-
- vec3 T, B;
- make_orthonormal_basis(cos, T, B); /* Generate tangent space */
-
- vec2 accum = vec2(0.0);
-
- for (float i = 0; i < sampleCount; i++) {
- vec3 Xi = rand2d_to_cylinder(hammersley_2d(i, sampleCount));
-
- vec3 samp = sample_uniform_cone(Xi, M_PI_2 * visibilityBlur, cos, T, B);
- float depth = texture(probeDepth, samp).r;
- depth = get_world_distance(depth, samp);
- accum += vec2(depth, depth * depth);
- }
-
- accum /= sampleCount;
- accum = abs(accum);
-
- /* Encode to normalized RGBA 8 */
- FragColor = visibility_encode(accum, visibilityRange);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl
deleted file mode 100644
index 009ccf6535e..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl
+++ /dev/null
@@ -1,47 +0,0 @@
-
-layout(triangles) in;
-layout(triangle_strip, max_vertices = 3) out;
-
-uniform int Layer;
-
-in vec4 vPos[];
-flat in int face[];
-flat out int fFace;
-
-out vec3 worldPosition;
-out vec3 viewPosition; /* Required. otherwise generate linking error. */
-out vec3 worldNormal; /* Required. otherwise generate linking error. */
-out vec3 viewNormal; /* Required. otherwise generate linking error. */
-
-const vec3 maj_axes[6] = vec3[6](vec3(1.0, 0.0, 0.0),
- vec3(-1.0, 0.0, 0.0),
- vec3(0.0, 1.0, 0.0),
- vec3(0.0, -1.0, 0.0),
- vec3(0.0, 0.0, 1.0),
- vec3(0.0, 0.0, -1.0));
-const vec3 x_axis[6] = vec3[6](vec3(0.0, 0.0, -1.0),
- vec3(0.0, 0.0, 1.0),
- vec3(1.0, 0.0, 0.0),
- vec3(1.0, 0.0, 0.0),
- vec3(1.0, 0.0, 0.0),
- vec3(-1.0, 0.0, 0.0));
-const vec3 y_axis[6] = vec3[6](vec3(0.0, -1.0, 0.0),
- vec3(0.0, -1.0, 0.0),
- vec3(0.0, 0.0, 1.0),
- vec3(0.0, 0.0, -1.0),
- vec3(0.0, -1.0, 0.0),
- vec3(0.0, -1.0, 0.0));
-
-void main()
-{
- fFace = face[0];
- gl_Layer = Layer + fFace;
-
- for (int v = 0; v < 3; v++) {
- gl_Position = vPos[v];
- worldPosition = x_axis[fFace] * vPos[v].x + y_axis[fFace] * vPos[v].y + maj_axes[fFace];
- EmitVertex();
- }
-
- EndPrimitive();
-}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl
deleted file mode 100644
index dc5ec1e40f5..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(irradiance_lib.glsl)
-
-flat in int cellOffset;
-in vec2 quadCoord;
-
-out vec4 FragColor;
-
-void main()
-{
- float dist_sqr = dot(quadCoord, quadCoord);
-
- /* Discard outside the circle. */
- if (dist_sqr > 1.0) {
- discard;
- }
-
- vec3 view_nor = vec3(quadCoord, sqrt(max(0.0, 1.0 - dist_sqr)));
- vec3 world_nor = mat3(ViewMatrixInverse) * view_nor;
- IrradianceData ir_data = load_irradiance_cell(cellOffset, world_nor);
- FragColor = vec4(compute_irradiance(world_nor, ir_data), 1.0);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl
deleted file mode 100644
index 6fefe1319bd..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl
+++ /dev/null
@@ -1,47 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-
-uniform float sphere_size;
-uniform int offset;
-uniform ivec3 grid_resolution;
-uniform vec3 corner;
-uniform vec3 increment_x;
-uniform vec3 increment_y;
-uniform vec3 increment_z;
-uniform vec3 screen_vecs[2];
-
-flat out int cellOffset;
-out vec2 quadCoord;
-
-const vec2 pos[6] = vec2[6](vec2(-1.0, -1.0),
- vec2(1.0, -1.0),
- vec2(-1.0, 1.0),
-
- vec2(1.0, -1.0),
- vec2(1.0, 1.0),
- vec2(-1.0, 1.0));
-
-void main()
-{
- int cell_id = gl_VertexID / 6;
- int vert_id = gl_VertexID % 6;
-
- vec3 ls_cell_location;
- /* Keep in sync with update_irradiance_probe */
- ls_cell_location.z = float(cell_id % grid_resolution.z);
- ls_cell_location.y = float((cell_id / grid_resolution.z) % grid_resolution.y);
- ls_cell_location.x = float(cell_id / (grid_resolution.z * grid_resolution.y));
-
- cellOffset = offset + cell_id;
-
- vec3 ws_cell_location = corner +
- (increment_x * ls_cell_location.x + increment_y * ls_cell_location.y +
- increment_z * ls_cell_location.z);
-
- quadCoord = pos[vert_id];
- vec3 screen_pos = screen_vecs[0] * quadCoord.x + screen_vecs[1] * quadCoord.y;
- ws_cell_location += screen_pos * sphere_size;
-
- gl_Position = ViewProjectionMatrix * vec4(ws_cell_location, 1.0);
- gl_Position.z += 0.0001; /* Small bias to let the icon draw without zfighting */
-}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_fill_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_fill_frag.glsl
deleted file mode 100644
index 71e4d6e2c4a..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_fill_frag.glsl
+++ /dev/null
@@ -1,18 +0,0 @@
-uniform sampler2DArray irradianceGrid;
-
-out vec4 FragColor;
-
-void main()
-{
-#if defined(IRRADIANCE_SH_L2)
- const ivec2 data_size = ivec2(3, 3);
-#elif defined(IRRADIANCE_HL2)
- const ivec2 data_size = ivec2(3, 2);
-#endif
- ivec2 coord = ivec2(gl_FragCoord.xy) % data_size;
- FragColor = texelFetch(irradianceGrid, ivec3(coord, 0), 0);
-
- if (any(greaterThanEqual(ivec2(gl_FragCoord.xy), data_size))) {
- FragColor = vec4(0.0, 0.0, 0.0, 1.0);
- }
-}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl
deleted file mode 100644
index 84626eac4cf..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl
+++ /dev/null
@@ -1,311 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
-#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl)
-#pragma BLENDER_REQUIRE(cubemap_lib.glsl)
-#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
-#pragma BLENDER_REQUIRE(irradiance_lib.glsl)
-
-/* ----------- Uniforms --------- */
-
-uniform sampler2DArray probePlanars;
-uniform samplerCubeArray probeCubes;
-
-/* ----------- Structures --------- */
-
-struct CubeData {
- vec4 position_type;
- vec4 attenuation_fac_type;
- mat4 influencemat;
- mat4 parallaxmat;
-};
-
-#define PROBE_PARALLAX_BOX 1.0
-#define PROBE_ATTENUATION_BOX 1.0
-
-#define p_position position_type.xyz
-#define p_parallax_type position_type.w
-#define p_atten_fac attenuation_fac_type.x
-#define p_atten_type attenuation_fac_type.y
-
-struct PlanarData {
- vec4 plane_equation;
- vec4 clip_vec_x_fade_scale;
- vec4 clip_vec_y_fade_bias;
- vec4 clip_edges;
- vec4 facing_scale_bias;
- mat4 reflectionmat; /* transform world space into reflection texture space */
- mat4 unused;
-};
-
-#define pl_plane_eq plane_equation
-#define pl_normal plane_equation.xyz
-#define pl_facing_scale facing_scale_bias.x
-#define pl_facing_bias facing_scale_bias.y
-#define pl_fade_scale clip_vec_x_fade_scale.w
-#define pl_fade_bias clip_vec_y_fade_bias.w
-#define pl_clip_pos_x clip_vec_x_fade_scale.xyz
-#define pl_clip_pos_y clip_vec_y_fade_bias.xyz
-#define pl_clip_edges clip_edges
-
-struct GridData {
- mat4 localmat;
- ivec4 resolution_offset;
- vec4 ws_corner_atten_scale; /* world space corner position */
- vec4 ws_increment_x_atten_bias; /* world space vector between 2 opposite cells */
- vec4 ws_increment_y_lvl_bias;
- vec4 ws_increment_z;
- vec4 vis_bias_bleed_range;
-};
-
-#define g_corner ws_corner_atten_scale.xyz
-#define g_atten_scale ws_corner_atten_scale.w
-#define g_atten_bias ws_increment_x_atten_bias.w
-#define g_level_bias ws_increment_y_lvl_bias.w
-#define g_increment_x ws_increment_x_atten_bias.xyz
-#define g_increment_y ws_increment_y_lvl_bias.xyz
-#define g_increment_z ws_increment_z.xyz
-#define g_resolution resolution_offset.xyz
-#define g_offset resolution_offset.w
-#define g_vis_bias vis_bias_bleed_range.x
-#define g_vis_bleed vis_bias_bleed_range.y
-#define g_vis_range vis_bias_bleed_range.z
-
-#ifndef MAX_PROBE
-# define MAX_PROBE 1
-#endif
-#ifndef MAX_GRID
-# define MAX_GRID 1
-#endif
-#ifndef MAX_PLANAR
-# define MAX_PLANAR 1
-#endif
-
-layout(std140) uniform probe_block
-{
- CubeData probes_data[MAX_PROBE];
-};
-
-layout(std140) uniform grid_block
-{
- GridData grids_data[MAX_GRID];
-};
-
-layout(std140) uniform planar_block
-{
- PlanarData planars_data[MAX_PLANAR];
-};
-
-/* ----------- Functions --------- */
-
-float probe_attenuation_cube(int pd_id, vec3 P)
-{
- vec3 localpos = transform_point(probes_data[pd_id].influencemat, P);
-
- float probe_atten_fac = probes_data[pd_id].p_atten_fac;
- float fac;
- if (probes_data[pd_id].p_atten_type == PROBE_ATTENUATION_BOX) {
- vec3 axes_fac = saturate(probe_atten_fac - probe_atten_fac * abs(localpos));
- fac = min_v3(axes_fac);
- }
- else {
- fac = saturate(probe_atten_fac - probe_atten_fac * length(localpos));
- }
-
- return fac;
-}
-
-float probe_attenuation_planar(PlanarData pd, vec3 P)
-{
- /* Distance from plane */
- float fac = saturate(abs(dot(pd.pl_plane_eq, vec4(P, 1.0))) * pd.pl_fade_scale +
- pd.pl_fade_bias);
- /* Fancy fast clipping calculation */
- vec2 dist_to_clip;
- dist_to_clip.x = dot(pd.pl_clip_pos_x, P);
- dist_to_clip.y = dot(pd.pl_clip_pos_y, P);
- /* compare and add all tests */
- fac *= step(2.0, dot(step(pd.pl_clip_edges, dist_to_clip.xxyy), vec2(-1.0, 1.0).xyxy));
- return fac;
-}
-
-float probe_attenuation_planar_normal_roughness(PlanarData pd, vec3 N, float roughness)
-{
- /* Normal Facing */
- float fac = saturate(dot(pd.pl_normal, N) * pd.pl_facing_scale + pd.pl_facing_bias);
- /* Decrease influence for high roughness */
- return fac * saturate(1.0 - roughness * 10.0);
-}
-
-float probe_attenuation_grid(GridData gd, vec3 P, out vec3 localpos)
-{
- localpos = transform_point(gd.localmat, P);
- vec3 pos_to_edge = max(vec3(0.0), abs(localpos) - 1.0);
- float fade = length(pos_to_edge);
- return saturate(-fade * gd.g_atten_scale + gd.g_atten_bias);
-}
-
-vec3 probe_evaluate_cube(int pd_id, vec3 P, vec3 R, float roughness)
-{
- /* Correct reflection ray using parallax volume intersection. */
- vec3 localpos = transform_point(probes_data[pd_id].parallaxmat, P);
- vec3 localray = transform_direction(probes_data[pd_id].parallaxmat, R);
-
- float dist;
- if (probes_data[pd_id].p_parallax_type == PROBE_PARALLAX_BOX) {
- dist = line_unit_box_intersect_dist(localpos, localray);
- }
- else {
- dist = line_unit_sphere_intersect_dist(localpos, localray);
- }
-
- /* Use Distance in WS directly to recover intersection */
- vec3 intersection = P + R * dist - probes_data[pd_id].p_position;
-
- /* From Frostbite PBR Course
- * Distance based roughness
- * http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf
- */
- float original_roughness = roughness;
- float linear_roughness = fast_sqrt(roughness);
- float distance_roughness = saturate(dist * linear_roughness / length(intersection));
- linear_roughness = mix(distance_roughness, linear_roughness, linear_roughness);
- roughness = linear_roughness * linear_roughness;
-
- float fac = saturate(original_roughness * 2.0 - 1.0);
- R = mix(intersection, R, fac * fac);
-
- float lod = linear_roughness * prbLodCubeMax;
- return textureLod_cubemapArray(probeCubes, vec4(R, float(pd_id)), lod).rgb;
-}
-
-vec3 probe_evaluate_world_spec(vec3 R, float roughness)
-{
- float lod = fast_sqrt(roughness) * prbLodCubeMax;
- return textureLod_cubemapArray(probeCubes, vec4(R, 0.0), lod).rgb;
-}
-
-vec3 probe_evaluate_planar(int id, PlanarData pd, vec3 P, vec3 N, vec3 V, float roughness)
-{
- /* Find view vector / reflection plane intersection. */
- vec3 point_on_plane = line_plane_intersect(P, V, pd.pl_plane_eq);
-
- /* How far the pixel is from the plane. */
- float ref_depth = 1.0; /* TODO: parameter. */
-
- /* Compute distorded reflection vector based on the distance to the reflected object.
- * In other words find intersection between reflection vector and the sphere center
- * around point_on_plane. */
- vec3 proj_ref = reflect(reflect(-V, N) * ref_depth, pd.pl_normal);
-
- /* Final point in world space. */
- vec3 ref_pos = point_on_plane + proj_ref;
-
- /* Reproject to find texture coords. */
- vec4 refco = ViewProjectionMatrix * vec4(ref_pos, 1.0);
- refco.xy /= refco.w;
-
- /* TODO: If we support non-ssr planar reflection, we should blur them with gaussian
- * and chose the right mip depending on the cone footprint after projection */
- /* NOTE: X is inverted here to compensate inverted drawing. */
- vec3 radiance = textureLod(probePlanars, vec3(refco.xy * vec2(-0.5, 0.5) + 0.5, id), 0.0).rgb;
-
- return radiance;
-}
-
-void fallback_cubemap(vec3 N,
- vec3 V,
- vec3 P,
- vec3 vP,
- float roughness,
- float roughnessSquared,
- inout vec4 spec_accum)
-{
- /* Specular probes */
- vec3 spec_dir = specular_dominant_dir(N, V, roughnessSquared);
-
- OcclusionData occlusion_data = occlusion_load(vP, 1.0);
- float final_ao = specular_occlusion(occlusion_data, V, N, roughness, spec_dir);
-
- /* Starts at 1 because 0 is world probe */
- for (int i = 1; i < MAX_PROBE && i < prbNumRenderCube && spec_accum.a < 0.999; i++) {
- float fade = probe_attenuation_cube(i, P);
-
- if (fade > 0.0) {
- vec3 spec = final_ao * probe_evaluate_cube(i, P, spec_dir, roughness);
- accumulate_light(spec, fade, spec_accum);
- }
- }
-
- /* World Specular */
- if (spec_accum.a < 0.999) {
- vec3 spec = final_ao * probe_evaluate_world_spec(spec_dir, roughness);
- accumulate_light(spec, 1.0, spec_accum);
- }
-}
-
-vec3 probe_evaluate_grid(GridData gd, vec3 P, vec3 N, vec3 localpos)
-{
- localpos = localpos * 0.5 + 0.5;
- localpos = localpos * vec3(gd.g_resolution) - 0.5;
-
- vec3 localpos_floored = floor(localpos);
- vec3 trilinear_weight = fract(localpos);
-
- float weight_accum = 0.0;
- vec3 irradiance_accum = vec3(0.0);
-
- /* For each neighbor cells */
- for (int i = 0; i < 8; i++) {
- ivec3 offset = ivec3(i, i >> 1, i >> 2) & ivec3(1);
- vec3 cell_cos = clamp(localpos_floored + vec3(offset), vec3(0.0), vec3(gd.g_resolution) - 1.0);
-
- /* Keep in sync with update_irradiance_probe */
- ivec3 icell_cos = ivec3(gd.g_level_bias * floor(cell_cos / gd.g_level_bias));
- int cell = gd.g_offset + icell_cos.z + icell_cos.y * gd.g_resolution.z +
- icell_cos.x * gd.g_resolution.z * gd.g_resolution.y;
-
- vec3 color = irradiance_from_cell_get(cell, N);
-
- /* We need this because we render probes in world space (so we need light vector in WS).
- * And rendering them in local probe space is too much problem. */
- vec3 ws_cell_location = gd.g_corner +
- (gd.g_increment_x * cell_cos.x + gd.g_increment_y * cell_cos.y +
- gd.g_increment_z * cell_cos.z);
-
- vec3 ws_point_to_cell = ws_cell_location - P;
- float ws_dist_point_to_cell = length(ws_point_to_cell);
- vec3 ws_light = ws_point_to_cell / ws_dist_point_to_cell;
-
- /* Smooth back-face test. */
- float weight = saturate(dot(ws_light, N));
-
- /* Precomputed visibility */
- weight *= load_visibility_cell(
- cell, ws_light, ws_dist_point_to_cell, gd.g_vis_bias, gd.g_vis_bleed, gd.g_vis_range);
-
- /* Smoother transition */
- weight += prbIrradianceSmooth;
-
- /* Trilinear weights */
- vec3 trilinear = mix(1.0 - trilinear_weight, trilinear_weight, offset);
- weight *= trilinear.x * trilinear.y * trilinear.z;
-
- /* Avoid zero weight */
- weight = max(0.00001, weight);
-
- weight_accum += weight;
- irradiance_accum += color * weight;
- }
-
- return irradiance_accum / weight_accum;
-}
-
-vec3 probe_evaluate_world_diff(vec3 N)
-{
- if (prbNumRenderGrid == 0) {
- return vec3(0);
- }
- return irradiance_from_cell_get(0, N);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl
deleted file mode 100644
index 0a53abcb04a..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl
+++ /dev/null
@@ -1,18 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-
-uniform sampler2DArray probePlanars;
-
-in vec3 worldPosition;
-flat in int probeIdx;
-
-out vec4 FragColor;
-
-void main()
-{
- vec4 refco = ViewProjectionMatrix * vec4(worldPosition, 1.0);
- refco.xy /= refco.w;
- FragColor = vec4(
- textureLod(probePlanars, vec3(refco.xy * vec2(-0.5, 0.5) + 0.5, float(probeIdx)), 0.0).rgb,
- 1.0);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl
deleted file mode 100644
index 6759c060259..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl
+++ /dev/null
@@ -1,17 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-
-in vec3 pos;
-
-in int probe_id;
-in mat4 probe_mat;
-
-out vec3 worldPosition;
-flat out int probeIdx;
-
-void main()
-{
- worldPosition = (probe_mat * vec4(-pos.x, pos.y, 0.0, 1.0)).xyz;
- gl_Position = ViewProjectionMatrix * vec4(worldPosition, 1.0);
- probeIdx = probe_id;
-}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl
deleted file mode 100644
index cf44a04b707..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * Simple down-sample shader. Takes the average of the 4 texels of lower mip.
- */
-
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-
-uniform sampler2DArray source;
-uniform float fireflyFactor;
-
-in vec2 uvs;
-flat in float layer;
-
-out vec4 FragColor;
-
-void main()
-{
-#if 0
- /* Reconstructing Target uvs like this avoid missing pixels if NPO2 */
- vec2 uvs = gl_FragCoord.xy * 2.0 / vec2(textureSize(source, 0));
-
- FragColor = textureLod(source, vec3(uvs, layer), 0.0);
-#else
- vec2 texel_size = 1.0 / vec2(textureSize(source, 0));
- vec2 uvs = gl_FragCoord.xy * 2.0 * texel_size;
- vec4 ofs = texel_size.xyxy * vec4(0.75, 0.75, -0.75, -0.75);
-
- FragColor = textureLod(source, vec3(uvs + ofs.xy, layer), 0.0);
- FragColor += textureLod(source, vec3(uvs + ofs.xw, layer), 0.0);
- FragColor += textureLod(source, vec3(uvs + ofs.zy, layer), 0.0);
- FragColor += textureLod(source, vec3(uvs + ofs.zw, layer), 0.0);
- FragColor *= 0.25;
-
- /* Clamped brightness. */
- float luma = max(1e-8, max_v3(FragColor.rgb));
- FragColor *= 1.0 - max(0.0, luma - fireflyFactor) / luma;
-#endif
-}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_geom.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_geom.glsl
deleted file mode 100644
index 54847b612e2..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_geom.glsl
+++ /dev/null
@@ -1,25 +0,0 @@
-
-layout(triangles) in;
-layout(triangle_strip, max_vertices = 3) out;
-
-in int instance[];
-in vec2 vPos[];
-
-flat out float layer;
-
-void main()
-{
- gl_Layer = instance[0];
- layer = float(instance[0]);
-
- gl_Position = vec4(vPos[0], 0.0, 1.0);
- EmitVertex();
-
- gl_Position = vec4(vPos[1], 0.0, 1.0);
- EmitVertex();
-
- gl_Position = vec4(vPos[2], 0.0, 1.0);
- EmitVertex();
-
- EndPrimitive();
-}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_vert.glsl
deleted file mode 100644
index 588cd402bb3..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_vert.glsl
+++ /dev/null
@@ -1,12 +0,0 @@
-
-out int instance;
-out vec2 vPos;
-
-void main()
-{
- int v = gl_VertexID % 3;
- vPos.x = -1.0 + float((v & 1) << 2);
- vPos.y = -1.0 + float((v & 2) << 1);
-
- instance = gl_VertexID / 3;
-}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_vert.glsl
deleted file mode 100644
index 8639ca6fa1b..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_vert.glsl
+++ /dev/null
@@ -1,11 +0,0 @@
-
-in vec3 pos;
-
-out vec4 vPos;
-flat out int face;
-
-void main()
-{
- vPos = vec4(pos, 1.0);
- face = gl_InstanceID;
-}
diff --git a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl
deleted file mode 100644
index dc98a00a1cd..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl
+++ /dev/null
@@ -1,387 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
-#pragma BLENDER_REQUIRE(raytrace_lib.glsl)
-#pragma BLENDER_REQUIRE(ltc_lib.glsl)
-
-#ifndef MAX_CASCADE_NUM
-# define MAX_CASCADE_NUM 4
-#endif
-
-/* ---------------------------------------------------------------------- */
-/** \name Structure
- * \{ */
-
-struct LightData {
- vec4 position_influence; /* w : InfluenceRadius (inversed and squared) */
- vec4 color_influence_volume; /* w : InfluenceRadius but for Volume power */
- vec4 spotdata_radius_shadow; /* x : spot size, y : spot blend, z : radius, w: shadow id */
- vec4 rightvec_sizex; /* xyz: Normalized up vector, w: area size X or spot scale X */
- vec4 upvec_sizey; /* xyz: Normalized right vector, w: area size Y or spot scale Y */
- vec4 forwardvec_type; /* xyz: Normalized forward vector, w: Light Type */
- vec4 diff_spec_volume; /* xyz: Diffuse/Spec/Volume power, w: radius for volumetric. */
-};
-
-/* convenience aliases */
-#define l_color color_influence_volume.rgb
-#define l_diff diff_spec_volume.x
-#define l_spec diff_spec_volume.y
-#define l_volume diff_spec_volume.z
-#define l_volume_radius diff_spec_volume.w
-#define l_position position_influence.xyz
-#define l_influence position_influence.w
-#define l_influence_volume color_influence_volume.w
-#define l_sizex rightvec_sizex.w
-#define l_sizey upvec_sizey.w
-#define l_right rightvec_sizex.xyz
-#define l_up upvec_sizey.xyz
-#define l_forward forwardvec_type.xyz
-#define l_type forwardvec_type.w
-#define l_spot_size spotdata_radius_shadow.x
-#define l_spot_blend spotdata_radius_shadow.y
-#define l_radius spotdata_radius_shadow.z
-#define l_shadowid spotdata_radius_shadow.w
-
-struct ShadowData {
- vec4 near_far_bias_id;
- vec4 contact_shadow_data;
-};
-
-struct ShadowCubeData {
- mat4 shadowmat;
- vec4 position;
-};
-
-struct ShadowCascadeData {
- mat4 shadowmat[MAX_CASCADE_NUM];
- vec4 split_start_distances;
- vec4 split_end_distances;
- vec4 shadow_vec_id;
-};
-
-/* convenience aliases */
-#define sh_near near_far_bias_id.x
-#define sh_far near_far_bias_id.y
-#define sh_bias near_far_bias_id.z
-#define sh_data_index near_far_bias_id.w
-#define sh_contact_dist contact_shadow_data.x
-#define sh_contact_offset contact_shadow_data.y
-#define sh_contact_spread contact_shadow_data.z
-#define sh_contact_thickness contact_shadow_data.w
-#define sh_shadow_vec shadow_vec_id.xyz
-#define sh_tex_index shadow_vec_id.w
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Resources
- * \{ */
-
-layout(std140) uniform shadow_block
-{
- ShadowData shadows_data[MAX_SHADOW];
- ShadowCubeData shadows_cube_data[MAX_SHADOW_CUBE];
- ShadowCascadeData shadows_cascade_data[MAX_SHADOW_CASCADE];
-};
-
-layout(std140) uniform light_block
-{
- LightData lights_data[MAX_LIGHT];
-};
-
-uniform sampler2DArrayShadow shadowCubeTexture;
-uniform sampler2DArrayShadow shadowCascadeTexture;
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Shadow Functions
- * \{ */
-
-/* type */
-#define POINT 0.0
-#define SUN 1.0
-#define SPOT 2.0
-#define AREA_RECT 4.0
-/* Used to define the area light shape, doesn't directly correspond to a Blender light type. */
-#define AREA_ELLIPSE 100.0
-
-float cubeFaceIndexEEVEE(vec3 P)
-{
- vec3 aP = abs(P);
- if (all(greaterThan(aP.xx, aP.yz))) {
- return (P.x > 0.0) ? 0.0 : 1.0;
- }
- else if (all(greaterThan(aP.yy, aP.xz))) {
- return (P.y > 0.0) ? 2.0 : 3.0;
- }
- else {
- return (P.z > 0.0) ? 4.0 : 5.0;
- }
-}
-
-vec2 cubeFaceCoordEEVEE(vec3 P, float face, float scale)
-{
- if (face < 2.0) {
- return (P.zy / P.x) * scale * vec2(-0.5, -sign(P.x) * 0.5) + 0.5;
- }
- else if (face < 4.0) {
- return (P.xz / P.y) * scale * vec2(sign(P.y) * 0.5, 0.5) + 0.5;
- }
- else {
- return (P.xy / P.z) * scale * vec2(0.5, -sign(P.z) * 0.5) + 0.5;
- }
-}
-
-vec2 cubeFaceCoordEEVEE(vec3 P, float face, sampler2DArray tex)
-{
- /* Scaling to compensate the 1px border around the face. */
- float cube_res = float(textureSize(tex, 0).x);
- float scale = (cube_res) / (cube_res + 1.0);
- return cubeFaceCoordEEVEE(P, face, scale);
-}
-
-vec2 cubeFaceCoordEEVEE(vec3 P, float face, sampler2DArrayShadow tex)
-{
- /* Scaling to compensate the 1px border around the face. */
- float cube_res = float(textureSize(tex, 0).x);
- float scale = (cube_res) / (cube_res + 1.0);
- return cubeFaceCoordEEVEE(P, face, scale);
-}
-
-vec4 sample_cube(sampler2DArray tex, vec3 cubevec, float cube)
-{
- /* Manual Shadow Cube Layer indexing. */
- float face = cubeFaceIndexEEVEE(cubevec);
- vec2 uv = cubeFaceCoordEEVEE(cubevec, face, tex);
-
- vec3 coord = vec3(uv, cube * 6.0 + face);
- return texture(tex, coord);
-}
-
-vec4 sample_cascade(sampler2DArray tex, vec2 co, float cascade_id)
-{
- return texture(tex, vec3(co, cascade_id));
-}
-
-/* Some driver poorly optimize this code. Use direct reference to matrices. */
-#define sd(x) shadows_data[x]
-#define scube(x) shadows_cube_data[x]
-#define scascade(x) shadows_cascade_data[x]
-
-float sample_cube_shadow(int shadow_id, vec3 P)
-{
- int data_id = int(sd(shadow_id).sh_data_index);
- vec3 cubevec = transform_point(scube(data_id).shadowmat, P);
- float dist = max(sd(shadow_id).sh_near, max_v3(abs(cubevec)) - sd(shadow_id).sh_bias);
- dist = buffer_depth(true, dist, sd(shadow_id).sh_far, sd(shadow_id).sh_near);
- /* Manual Shadow Cube Layer indexing. */
- /* TODO: Shadow Cube Array. */
- float face = cubeFaceIndexEEVEE(cubevec);
- vec2 coord = cubeFaceCoordEEVEE(cubevec, face, shadowCubeTexture);
- /* tex_id == data_id for cube shadowmap */
- float tex_id = float(data_id);
- return texture(shadowCubeTexture, vec4(coord, tex_id * 6.0 + face, dist));
-}
-
-float sample_cascade_shadow(int shadow_id, vec3 P)
-{
- int data_id = int(sd(shadow_id).sh_data_index);
- float tex_id = scascade(data_id).sh_tex_index;
- vec4 view_z = vec4(dot(P - cameraPos, cameraForward));
- vec4 weights = 1.0 - smoothstep(scascade(data_id).split_end_distances,
- scascade(data_id).split_start_distances.yzwx,
- view_z);
- float tot_weight = dot(weights.xyz, vec3(1.0));
-
- int cascade = int(clamp(tot_weight, 0.0, 3.0));
- float blend = fract(tot_weight);
- float vis = weights.w;
- vec4 coord, shpos;
- /* Main cascade. */
- shpos = scascade(data_id).shadowmat[cascade] * vec4(P, 1.0);
- coord = vec4(shpos.xy, tex_id + float(cascade), shpos.z - sd(shadow_id).sh_bias);
- vis += texture(shadowCascadeTexture, coord) * (1.0 - blend);
-
- cascade = min(3, cascade + 1);
- /* Second cascade. */
- shpos = scascade(data_id).shadowmat[cascade] * vec4(P, 1.0);
- coord = vec4(shpos.xy, tex_id + float(cascade), shpos.z - sd(shadow_id).sh_bias);
- vis += texture(shadowCascadeTexture, coord) * blend;
-
- return saturate(vis);
-}
-#undef sd
-#undef scube
-#undef scsmd
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Light Functions
- * \{ */
-
-/* From Frostbite PBR Course
- * Distance based attenuation
- * http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf */
-float distance_attenuation(float dist_sqr, float inv_sqr_influence)
-{
- float factor = dist_sqr * inv_sqr_influence;
- float fac = saturate(1.0 - factor * factor);
- return fac * fac;
-}
-
-float spot_attenuation(LightData ld, vec3 l_vector)
-{
- float z = dot(ld.l_forward, l_vector.xyz);
- vec3 lL = l_vector.xyz / z;
- float x = dot(ld.l_right, lL) / ld.l_sizex;
- float y = dot(ld.l_up, lL) / ld.l_sizey;
- float ellipse = inversesqrt(1.0 + x * x + y * y);
- float spotmask = smoothstep(0.0, 1.0, (ellipse - ld.l_spot_size) / ld.l_spot_blend);
- return spotmask;
-}
-
-float light_attenuation(LightData ld, vec4 l_vector)
-{
- float vis = 1.0;
- if (ld.l_type == SPOT) {
- vis *= spot_attenuation(ld, l_vector.xyz);
- }
- if (ld.l_type >= SPOT) {
- vis *= step(0.0, -dot(l_vector.xyz, ld.l_forward));
- }
- if (ld.l_type != SUN) {
-#ifdef VOLUME_LIGHTING
- vis *= distance_attenuation(l_vector.w * l_vector.w, ld.l_influence_volume);
-#else
- vis *= distance_attenuation(l_vector.w * l_vector.w, ld.l_influence);
-#endif
- }
- return vis;
-}
-
-float light_shadowing(LightData ld, vec3 P, float vis)
-{
-#if !defined(VOLUMETRICS) || defined(VOLUME_SHADOW)
- if (ld.l_shadowid >= 0.0 && vis > 0.001) {
- if (ld.l_type == SUN) {
- vis *= sample_cascade_shadow(int(ld.l_shadowid), P);
- }
- else {
- vis *= sample_cube_shadow(int(ld.l_shadowid), P);
- }
- }
-#endif
- return vis;
-}
-
-#ifndef VOLUMETRICS
-float light_contact_shadows(LightData ld, vec3 P, vec3 vP, vec3 vNg, float rand_x, float vis)
-{
- if (ld.l_shadowid >= 0.0 && vis > 0.001) {
- ShadowData sd = shadows_data[int(ld.l_shadowid)];
- /* Only compute if not already in shadow. */
- if (sd.sh_contact_dist > 0.0) {
- /* Contact Shadows. */
- Ray ray;
-
- if (ld.l_type == SUN) {
- ray.direction = shadows_cascade_data[int(sd.sh_data_index)].sh_shadow_vec *
- sd.sh_contact_dist;
- }
- else {
- ray.direction = shadows_cube_data[int(sd.sh_data_index)].position.xyz - P;
- ray.direction *= saturate(sd.sh_contact_dist * safe_rcp(length(ray.direction)));
- }
-
- ray.direction = transform_direction(ViewMatrix, ray.direction);
- ray.origin = vP + vNg * sd.sh_contact_offset;
-
- RayTraceParameters params;
- params.thickness = sd.sh_contact_thickness;
- params.jitter = rand_x;
- params.trace_quality = 0.1;
- params.roughness = 0.001;
-
- vec3 hit_position_unused;
-
- if (raytrace(ray, params, false, false, hit_position_unused)) {
- return 0.0;
- }
- }
- }
- return 1.0;
-}
-#endif /* VOLUMETRICS */
-
-float light_visibility(LightData ld, vec3 P, vec4 l_vector)
-{
- float l_atten = light_attenuation(ld, l_vector);
- return light_shadowing(ld, P, l_atten);
-}
-
-float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector)
-{
- if (ld.l_type == AREA_RECT) {
- vec3 corners[4];
- corners[0] = normalize((l_vector.xyz + ld.l_right * -ld.l_sizex) + ld.l_up * ld.l_sizey);
- corners[1] = normalize((l_vector.xyz + ld.l_right * -ld.l_sizex) + ld.l_up * -ld.l_sizey);
- corners[2] = normalize((l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * -ld.l_sizey);
- corners[3] = normalize((l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * ld.l_sizey);
-
- return ltc_evaluate_quad(corners, N);
- }
- else if (ld.l_type == AREA_ELLIPSE) {
- vec3 points[3];
- points[0] = (l_vector.xyz + ld.l_right * -ld.l_sizex) + ld.l_up * -ld.l_sizey;
- points[1] = (l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * -ld.l_sizey;
- points[2] = (l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * ld.l_sizey;
-
- return ltc_evaluate_disk(N, V, mat3(1.0), points);
- }
- else {
- float radius = ld.l_radius;
- radius /= (ld.l_type == SUN) ? 1.0 : l_vector.w;
- vec3 L = (ld.l_type == SUN) ? -ld.l_forward : (l_vector.xyz / l_vector.w);
-
- return ltc_evaluate_disk_simple(radius, dot(N, L));
- }
-}
-
-float light_specular(LightData ld, vec4 ltc_mat, vec3 N, vec3 V, vec4 l_vector)
-{
- if (ld.l_type == AREA_RECT) {
- vec3 corners[4];
- corners[0] = (l_vector.xyz + ld.l_right * -ld.l_sizex) + ld.l_up * ld.l_sizey;
- corners[1] = (l_vector.xyz + ld.l_right * -ld.l_sizex) + ld.l_up * -ld.l_sizey;
- corners[2] = (l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * -ld.l_sizey;
- corners[3] = (l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * ld.l_sizey;
-
- ltc_transform_quad(N, V, ltc_matrix(ltc_mat), corners);
-
- return ltc_evaluate_quad(corners, vec3(0.0, 0.0, 1.0));
- }
- else {
- bool is_ellipse = (ld.l_type == AREA_ELLIPSE);
- float radius_x = is_ellipse ? ld.l_sizex : ld.l_radius;
- float radius_y = is_ellipse ? ld.l_sizey : ld.l_radius;
-
- vec3 L = (ld.l_type == SUN) ? -ld.l_forward : l_vector.xyz;
- vec3 Px = ld.l_right;
- vec3 Py = ld.l_up;
-
- if (ld.l_type == SPOT || ld.l_type == POINT) {
- make_orthonormal_basis(l_vector.xyz / l_vector.w, Px, Py);
- }
-
- vec3 points[3];
- points[0] = (L + Px * -radius_x) + Py * -radius_y;
- points[1] = (L + Px * radius_x) + Py * -radius_y;
- points[2] = (L + Px * radius_x) + Py * radius_y;
-
- return ltc_evaluate_disk(N, V, ltc_matrix(ltc_mat), points);
- }
-}
-
-/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl b/source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl
deleted file mode 100644
index 9077b414026..00000000000
--- a/source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl
+++ /dev/null
@@ -1,52 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
-#pragma BLENDER_REQUIRE(surface_lib.glsl)
-
-uniform sampler2D studioLight;
-
-uniform float backgroundAlpha;
-uniform mat3 StudioLightMatrix;
-uniform float studioLightIntensity = 1.0;
-uniform float studioLightBlur = 0.0;
-
-out vec4 FragColor;
-
-vec3 background_transform_to_world(vec3 viewvec)
-{
- vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(viewvec, 1.0) : vec4(0.0, 0.0, 1.0, 1.0);
- vec4 co_homogenous = (ProjectionMatrixInverse * v);
-
- vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0);
- return (ViewMatrixInverse * co).xyz;
-}
-
-float hypot(float x, float y)
-{
- return sqrt(x * x + y * y);
-}
-
-vec4 node_tex_environment_equirectangular(vec3 co, sampler2D ima)
-{
- vec3 nco = normalize(co);
- float u = -atan(nco.y, nco.x) / (2.0 * M_PI) + 0.5;
- float v = atan(nco.z, hypot(nco.x, nco.y)) / M_PI + 0.5;
- return textureLod(ima, vec2(u, v), 0.0);
-}
-
-void main()
-{
- vec3 worldvec = background_transform_to_world(viewPosition);
-
- vec3 background_color;
-#if defined(LOOKDEV_BG)
- background_color = probe_evaluate_world_spec(worldvec, studioLightBlur).rgb;
-#else
- worldvec = StudioLightMatrix * worldvec;
- background_color = node_tex_environment_equirectangular(worldvec, studioLight).rgb;
- background_color *= studioLightIntensity;
-#endif
-
- FragColor = vec4(clamp(background_color, vec3(0.0), vec3(1e10)), 1.0) * backgroundAlpha;
-}
diff --git a/source/blender/draw/engines/eevee/shaders/object_motion_frag.glsl b/source/blender/draw/engines/eevee/shaders/object_motion_frag.glsl
deleted file mode 100644
index 66b098ef6c5..00000000000
--- a/source/blender/draw/engines/eevee/shaders/object_motion_frag.glsl
+++ /dev/null
@@ -1,27 +0,0 @@
-
-uniform mat4 prevViewProjMatrix;
-uniform mat4 currViewProjMatrix;
-uniform mat4 nextViewProjMatrix;
-
-in vec3 prevWorldPos;
-in vec3 currWorldPos;
-in vec3 nextWorldPos;
-
-out vec4 outData;
-
-void main()
-{
- vec4 prev_wpos = prevViewProjMatrix * vec4(prevWorldPos, 1.0);
- vec4 curr_wpos = currViewProjMatrix * vec4(currWorldPos, 1.0);
- vec4 next_wpos = nextViewProjMatrix * vec4(nextWorldPos, 1.0);
-
- vec2 prev_uv = (prev_wpos.xy / prev_wpos.w);
- vec2 curr_uv = (curr_wpos.xy / curr_wpos.w);
- vec2 next_uv = (next_wpos.xy / next_wpos.w);
-
- outData.xy = prev_uv - curr_uv;
- outData.zw = next_uv - curr_uv;
-
- /* Encode to unsigned normalized 16bit texture. */
- outData = outData * 0.5 + 0.5;
-}
diff --git a/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl b/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl
deleted file mode 100644
index ef96bcbaedb..00000000000
--- a/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl
+++ /dev/null
@@ -1,59 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
-
-uniform mat4 currModelMatrix;
-uniform mat4 prevModelMatrix;
-uniform mat4 nextModelMatrix;
-uniform bool useDeform;
-
-#ifdef HAIR
-uniform samplerBuffer prvBuffer; /* RGBA32F */
-uniform samplerBuffer nxtBuffer; /* RGBA32F */
-#else
-in vec3 pos;
-in vec3 prv; /* Previous frame position. */
-in vec3 nxt; /* Next frame position. */
-#endif
-
-out vec3 currWorldPos;
-out vec3 prevWorldPos;
-out vec3 nextWorldPos;
-
-void main()
-{
- GPU_INTEL_VERTEX_SHADER_WORKAROUND
-
-#ifdef HAIR
- bool is_persp = (ProjectionMatrix[3][3] == 0.0);
- float time, thick_time, thickness;
- vec3 tan, binor;
- vec3 wpos;
-
- hair_get_pos_tan_binor_time(is_persp,
- ModelMatrixInverse,
- ViewMatrixInverse[3].xyz,
- ViewMatrixInverse[2].xyz,
- wpos,
- tan,
- binor,
- time,
- thickness,
- thick_time);
-
- int id = hair_get_base_id();
- vec3 pos = texelFetch(hairPointBuffer, id).point_position;
- vec3 prv = texelFetch(prvBuffer, id).point_position;
- vec3 nxt = texelFetch(nxtBuffer, id).point_position;
-#endif
- prevWorldPos = (prevModelMatrix * vec4(useDeform ? prv : pos, 1.0)).xyz;
- currWorldPos = (currModelMatrix * vec4(pos, 1.0)).xyz;
- nextWorldPos = (nextModelMatrix * vec4(useDeform ? nxt : pos, 1.0)).xyz;
- /* Use jittered projmatrix to be able to match exact sample depth (depth equal test).
- * Note that currModelMatrix needs to also be equal to ModelMatrix for the samples to match. */
-#ifndef HAIR
- gl_Position = ViewProjectionMatrix * vec4(currWorldPos, 1.0);
-#else
- gl_Position = ViewProjectionMatrix * vec4(wpos, 1.0);
-#endif
-}
diff --git a/source/blender/draw/engines/eevee/shaders/octahedron_lib.glsl b/source/blender/draw/engines/eevee/shaders/octahedron_lib.glsl
deleted file mode 100644
index e05cc2719fa..00000000000
--- a/source/blender/draw/engines/eevee/shaders/octahedron_lib.glsl
+++ /dev/null
@@ -1,20 +0,0 @@
-
-vec2 mapping_octahedron(vec3 cubevec, vec2 texel_size)
-{
- /* projection onto octahedron */
- cubevec /= dot(vec3(1.0), abs(cubevec));
-
- /* out-folding of the downward faces */
- if (cubevec.z < 0.0) {
- vec2 cubevec_sign = step(0.0, cubevec.xy) * 2.0 - 1.0;
- cubevec.xy = (1.0 - abs(cubevec.yx)) * cubevec_sign;
- }
-
- /* mapping to [0;1]ˆ2 texture space */
- vec2 uvs = cubevec.xy * (0.5) + 0.5;
-
- /* edge filtering fix */
- uvs = (1.0 - 2.0 * texel_size) * uvs + texel_size;
-
- return uvs;
-}
diff --git a/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl b/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl
deleted file mode 100644
index f650e2eeb8c..00000000000
--- a/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl
+++ /dev/null
@@ -1,35 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-
-#ifndef HAIR_SHADER
-in vec3 pos;
-#endif
-
-void main()
-{
- GPU_INTEL_VERTEX_SHADER_WORKAROUND
-
-#ifdef HAIR_SHADER
- float time, thick_time, thickness;
- vec3 worldPosition, tan, binor;
- hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0),
- ModelMatrixInverse,
- ViewMatrixInverse[3].xyz,
- ViewMatrixInverse[2].xyz,
- worldPosition,
- tan,
- binor,
- time,
- thickness,
- thick_time);
-#else
- vec3 worldPosition = point_object_to_world(pos);
-#endif
-
- gl_Position = point_world_to_ndc(worldPosition);
-
-#ifdef CLIP_PLANES
- gl_ClipDistance[0] = dot(vec4(worldPosition.xyz, 1.0), clipPlanes[0]);
-#endif
-}
diff --git a/source/blender/draw/engines/eevee/shaders/random_lib.glsl b/source/blender/draw/engines/eevee/shaders/random_lib.glsl
deleted file mode 100644
index c2388f61346..00000000000
--- a/source/blender/draw/engines/eevee/shaders/random_lib.glsl
+++ /dev/null
@@ -1,38 +0,0 @@
-
-/**
- * Random numbers and low discrepancy sequences utilities.
- */
-
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-
-/* From: http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html */
-float van_der_corput_radical_inverse(uint bits)
-{
- bits = (bits << 16u) | (bits >> 16u);
- bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
- bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
- bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
- bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
- /* Same as dividing by 0x100000000. */
- return float(bits) * 2.3283064365386963e-10;
-}
-
-vec2 hammersley_2d(float i, float sample_count)
-{
- vec2 rand;
- rand.x = i / sample_count;
- rand.y = van_der_corput_radical_inverse(uint(i));
- return rand;
-}
-
-/* This transform a 2d random sample (in [0..1] range) to a sample located on a cylinder of the
- * same range. This is because the sampling functions expect such a random sample which is
- * normally precomputed. */
-vec3 rand2d_to_cylinder(vec2 rand)
-{
- float theta = rand.x;
- float phi = (rand.y - 0.5) * M_2PI;
- float cos_phi = cos(phi);
- float sin_phi = sqrt(1.0 - sqr(cos_phi)) * sign(phi);
- return vec3(theta, cos_phi, sin_phi);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl b/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl
deleted file mode 100644
index 73c4b521b05..00000000000
--- a/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl
+++ /dev/null
@@ -1,228 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl)
-
-/**
- * Screen-Space Raytracing functions.
- */
-
-uniform sampler2D maxzBuffer;
-uniform sampler2DArray planarDepth;
-
-struct Ray {
- vec3 origin;
- /* Ray direction premultiplied by its maximum length. */
- vec3 direction;
-};
-
-/* Inputs expected to be in viewspace. */
-void raytrace_clip_ray_to_near_plane(inout Ray ray)
-{
- float near_dist = get_view_z_from_depth(0.0);
- if ((ray.origin.z + ray.direction.z) > near_dist) {
- ray.direction *= abs((near_dist - ray.origin.z) / ray.direction.z);
- }
-}
-
-/* Screenspace ray ([0..1] "uv" range) where direction is normalize to be as small as one
- * full-resolution pixel. The ray is also clipped to all frustum sides.
- */
-struct ScreenSpaceRay {
- vec4 origin;
- vec4 direction;
- float max_time;
-};
-
-void raytrace_screenspace_ray_finalize(inout ScreenSpaceRay ray)
-{
- /* Constant bias (due to depth buffer precision). Helps with self intersection. */
- /* Magic numbers for 24bits of precision.
- * From http://terathon.com/gdc07_lengyel.pdf (slide 26) */
- const float bias = -2.4e-7 * 2.0;
- ray.origin.zw += bias;
- ray.direction.zw += bias;
-
- ray.direction -= ray.origin;
- /* If the line is degenerate, make it cover at least one pixel
- * to not have to handle zero-pixel extent as a special case later */
- if (len_squared(ray.direction.xy) < 0.00001) {
- ray.direction.xy = vec2(0.0, 0.00001);
- }
- float ray_len_sqr = len_squared(ray.direction.xyz);
- /* Make ray.direction cover one pixel. */
- bool is_more_vertical = abs(ray.direction.x) < abs(ray.direction.y);
- ray.direction /= (is_more_vertical) ? abs(ray.direction.y) : abs(ray.direction.x);
- ray.direction *= (is_more_vertical) ? ssrPixelSize.y : ssrPixelSize.x;
- /* Clip to segment's end. */
- ray.max_time = sqrt(ray_len_sqr * safe_rcp(len_squared(ray.direction.xyz)));
- /* Clipping to frustum sides. */
- float clip_dist = line_unit_box_intersect_dist_safe(ray.origin.xyz, ray.direction.xyz);
- ray.max_time = min(ray.max_time, clip_dist);
- /* Convert to texture coords [0..1] range. */
- ray.origin = ray.origin * 0.5 + 0.5;
- ray.direction *= 0.5;
-}
-
-ScreenSpaceRay raytrace_screenspace_ray_create(Ray ray)
-{
- ScreenSpaceRay ssray;
- ssray.origin.xyz = project_point(ProjectionMatrix, ray.origin);
- ssray.direction.xyz = project_point(ProjectionMatrix, ray.origin + ray.direction);
-
- raytrace_screenspace_ray_finalize(ssray);
- return ssray;
-}
-
-ScreenSpaceRay raytrace_screenspace_ray_create(Ray ray, float thickness)
-{
- ScreenSpaceRay ssray;
- ssray.origin.xyz = project_point(ProjectionMatrix, ray.origin);
- ssray.direction.xyz = project_point(ProjectionMatrix, ray.origin + ray.direction);
- /* Interpolate thickness in screen space.
- * Calculate thickness further away to avoid near plane clipping issues. */
- ssray.origin.w = get_depth_from_view_z(ray.origin.z - thickness) * 2.0 - 1.0;
- ssray.direction.w = get_depth_from_view_z(ray.origin.z + ray.direction.z - thickness) * 2.0 -
- 1.0;
-
- raytrace_screenspace_ray_finalize(ssray);
- return ssray;
-}
-
-struct RayTraceParameters {
- /** ViewSpace thickness the objects. */
- float thickness;
- /** Jitter along the ray to avoid banding artifact when steps are too large. */
- float jitter;
- /** Determine how fast the sample steps are getting bigger. */
- float trace_quality;
- /** Determine how we can use lower depth mipmaps to make the tracing faster. */
- float roughness;
-};
-
-/* Returns true on hit. */
-/* TODO(fclem): remove the back-face check and do it the SSR resolve code. */
-bool raytrace(Ray ray,
- RayTraceParameters params,
- const bool discard_backface,
- const bool allow_self_intersection,
- out vec3 hit_position)
-{
- /* Clip to near plane for perspective view where there is a singularity at the camera origin. */
- if (ProjectionMatrix[3][3] == 0.0) {
- raytrace_clip_ray_to_near_plane(ray);
- }
-
- ScreenSpaceRay ssray = raytrace_screenspace_ray_create(ray, params.thickness);
- /* Avoid no iteration. */
- if (!allow_self_intersection && ssray.max_time < 1.1) {
- hit_position = ssray.origin.xyz + ssray.direction.xyz;
- return false;
- }
-
- ssray.max_time = max(1.1, ssray.max_time);
-
- float prev_delta = 0.0, prev_time = 0.0;
- float depth_sample = get_depth_from_view_z(ray.origin.z);
- float delta = depth_sample - ssray.origin.z;
-
- float lod_fac = saturate(fast_sqrt(params.roughness) * 2.0 - 0.4);
-
- /* Cross at least one pixel. */
- float t = 1.001, time = 1.001;
- bool hit = false;
- const float max_steps = 255.0;
- for (float iter = 1.0; !hit && (time < ssray.max_time) && (iter < max_steps); iter++) {
- float stride = 1.0 + iter * params.trace_quality;
- float lod = log2(stride) * lod_fac;
-
- prev_time = time;
- prev_delta = delta;
-
- time = min(t + stride * params.jitter, ssray.max_time);
- t += stride;
-
- vec4 ss_p = ssray.origin + ssray.direction * time;
- depth_sample = textureLod(maxzBuffer, ss_p.xy * hizUvScale.xy, floor(lod)).r;
-
- delta = depth_sample - ss_p.z;
- /* Check if the ray is below the surface ... */
- hit = (delta < 0.0);
- /* ... and above it with the added thickness. */
- hit = hit && (delta > ss_p.z - ss_p.w || abs(delta) < abs(ssray.direction.z * stride * 2.0));
- }
- /* Discard back-face hits. */
- hit = hit && !(discard_backface && prev_delta < 0.0);
- /* Reject hit if background. */
- hit = hit && (depth_sample != 1.0);
- /* Refine hit using intersection between the sampled heightfield and the ray.
- * This simplifies nicely to this single line. */
- time = mix(prev_time, time, saturate(prev_delta / (prev_delta - delta)));
-
- hit_position = ssray.origin.xyz + ssray.direction.xyz * time;
-
- return hit;
-}
-
-bool raytrace_planar(Ray ray, RayTraceParameters params, int planar_ref_id, out vec3 hit_position)
-{
- /* Clip to near plane for perspective view where there is a singularity at the camera origin. */
- if (ProjectionMatrix[3][3] == 0.0) {
- raytrace_clip_ray_to_near_plane(ray);
- }
-
- ScreenSpaceRay ssray = raytrace_screenspace_ray_create(ray);
-
- /* Planar Reflections have X mirrored. */
- ssray.origin.x = 1.0 - ssray.origin.x;
- ssray.direction.x = -ssray.direction.x;
-
- float prev_delta = 0.0, prev_time = 0.0;
- float depth_sample = texture(planarDepth, vec3(ssray.origin.xy, planar_ref_id)).r;
- float delta = depth_sample - ssray.origin.z;
-
- float t = 0.0, time = 0.0;
- /* On very sharp reflections, the ray can be perfectly aligned with the view direction
- * making the tracing useless. Bypass tracing in this case. */
- bool hit = false;
- const float max_steps = 255.0;
- for (float iter = 1.0; !hit && (time < ssray.max_time) && (iter < max_steps); iter++) {
- float stride = 1.0 + iter * params.trace_quality;
-
- prev_time = time;
- prev_delta = delta;
-
- time = min(t + stride * params.jitter, ssray.max_time);
- t += stride;
-
- vec4 ss_ray = ssray.origin + ssray.direction * time;
-
- depth_sample = texture(planarDepth, vec3(ss_ray.xy, planar_ref_id)).r;
-
- delta = depth_sample - ss_ray.z;
- /* Check if the ray is below the surface. */
- hit = (delta < 0.0);
- }
- /* Reject hit if background. */
- hit = hit && (depth_sample != 1.0);
- /* Refine hit using intersection between the sampled heightfield and the ray.
- * This simplifies nicely to this single line. */
- time = mix(prev_time, time, saturate(prev_delta / (prev_delta - delta)));
-
- hit_position = ssray.origin.xyz + ssray.direction.xyz * time;
- /* Planar Reflections have X mirrored. */
- hit_position.x = 1.0 - hit_position.x;
-
- return hit;
-}
-
-float screen_border_mask(vec2 hit_co)
-{
- const float margin = 0.003;
- float atten = ssrBorderFac + margin; /* Screen percentage */
- hit_co = smoothstep(0.0, atten, hit_co) * (1.0 - smoothstep(1.0 - atten, 1.0, hit_co));
-
- float screenfade = hit_co.x * hit_co.y;
-
- return screenfade;
-}
diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl
deleted file mode 100644
index 3e0a5e76d00..00000000000
--- a/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl
+++ /dev/null
@@ -1,56 +0,0 @@
-#define EEVEE_AOV_HASH_COLOR_TYPE_MASK 1
-
-/* ---------------------------------------------------------------------- */
-/** \name Resources
- * \{ */
-
-layout(std140) uniform renderpass_block
-{
- bool renderPassDiffuse;
- bool renderPassDiffuseLight;
- bool renderPassGlossy;
- bool renderPassGlossyLight;
- bool renderPassEmit;
- bool renderPassSSSColor;
- bool renderPassEnvironment;
- bool renderPassAOV;
- int renderPassAOVActive;
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Functions
- * \{ */
-
-vec3 render_pass_diffuse_mask(vec3 diffuse_color, vec3 diffuse_light)
-{
- return renderPassDiffuse ? (renderPassDiffuseLight ? diffuse_light : diffuse_color) : vec3(0.0);
-}
-
-vec3 render_pass_sss_mask(vec3 sss_color)
-{
- return renderPassSSSColor ? sss_color : vec3(0.0);
-}
-
-vec3 render_pass_glossy_mask(vec3 specular_color, vec3 specular_light)
-{
- return renderPassGlossy ? (renderPassGlossyLight ? specular_light : specular_color) : vec3(0.0);
-}
-
-vec3 render_pass_emission_mask(vec3 emission_light)
-{
- return renderPassEmit ? emission_light : vec3(0.0);
-}
-
-bool render_pass_aov_is_color()
-{
- return (renderPassAOVActive & EEVEE_AOV_HASH_COLOR_TYPE_MASK) != 0;
-}
-
-int render_pass_aov_hash()
-{
- return renderPassAOVActive & ~EEVEE_AOV_HASH_COLOR_TYPE_MASK;
-}
-
-/** \} */
diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl
deleted file mode 100644
index 363b5cb978a..00000000000
--- a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl
+++ /dev/null
@@ -1,126 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
-
-#define PASS_POST_UNDEFINED 0
-#define PASS_POST_ACCUMULATED_COLOR 1
-#define PASS_POST_ACCUMULATED_COLOR_ALPHA 2
-#define PASS_POST_ACCUMULATED_LIGHT 3
-#define PASS_POST_ACCUMULATED_VALUE 4
-#define PASS_POST_DEPTH 5
-#define PASS_POST_AO 6
-#define PASS_POST_NORMAL 7
-#define PASS_POST_TWO_LIGHT_BUFFERS 8
-#define PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR 9
-
-uniform int postProcessType;
-uniform int currentSample;
-
-uniform sampler2D depthBuffer;
-uniform sampler2D inputBuffer;
-uniform sampler2D inputSecondLightBuffer;
-uniform sampler2D inputColorBuffer;
-uniform sampler2D inputTransmittanceBuffer;
-
-out vec4 fragColor;
-
-vec3 safe_divide_even_color(vec3 a, vec3 b)
-{
- vec3 result = vec3((b.r != 0.0) ? a.r / b.r : 0.0,
- (b.g != 0.0) ? a.g / b.g : 0.0,
- (b.b != 0.0) ? a.b / b.b : 0.0);
- /* try to get gray even if b is zero */
- if (b.r == 0.0) {
- if (b.g == 0.0) {
- result = result.bbb;
- }
- else if (b.b == 0.0) {
- result = result.ggg;
- }
- else {
- result.r = 0.5 * (result.g + result.b);
- }
- }
- else if (b.g == 0.0) {
- if (b.b == 0.0) {
- result = result.rrr;
- }
- else {
- result.g = 0.5 * (result.r + result.b);
- }
- }
- else if (b.b == 0.0) {
- result.b = 0.5 * (result.r + result.g);
- }
-
- return result;
-}
-
-void main()
-{
- vec4 color = vec4(0.0, 0.0, 0.0, 1.0);
- ivec2 texel = ivec2(gl_FragCoord.xy);
-
- if (postProcessType == PASS_POST_DEPTH) {
- float depth = texelFetch(depthBuffer, texel, 0).r;
- if (depth == 1.0f) {
- depth = 1e10;
- }
- else {
- depth = -get_view_z_from_depth(depth);
- }
- color.rgb = vec3(depth);
- }
- else if (postProcessType == PASS_POST_AO) {
- float ao_accum = texelFetch(inputBuffer, texel, 0).r;
- color.rgb = vec3(min(1.0, ao_accum / currentSample));
- }
- else if (postProcessType == PASS_POST_NORMAL) {
- float depth = texelFetch(depthBuffer, texel, 0).r;
- vec2 encoded_normal = texelFetch(inputBuffer, texel, 0).rg;
- /* decode the normals only when they are valid. otherwise the result buffer will be filled
- * with NaN's */
- if (depth != 1.0 && any(notEqual(encoded_normal, vec2(0.0)))) {
- vec3 decoded_normal = normal_decode(texelFetch(inputBuffer, texel, 0).rg, vec3(0.0));
- vec3 world_normal = transform_direction(ViewMatrixInverse, decoded_normal);
- color.rgb = world_normal;
- }
- else {
- color.rgb = vec3(0.0);
- }
- }
- else if (postProcessType == PASS_POST_ACCUMULATED_VALUE) {
- float accumulated_value = texelFetch(inputBuffer, texel, 0).r;
- color.rgb = vec3(accumulated_value / currentSample);
- }
- else if (postProcessType == PASS_POST_ACCUMULATED_COLOR) {
- vec3 accumulated_color = texelFetch(inputBuffer, texel, 0).rgb;
- color.rgb = (accumulated_color / currentSample);
- }
- else if (postProcessType == PASS_POST_ACCUMULATED_COLOR_ALPHA) {
- vec4 accumulated_color = texelFetch(inputBuffer, texel, 0);
- color = (accumulated_color / currentSample);
- }
- else if (postProcessType == PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR) {
- vec3 accumulated_color = texelFetch(inputBuffer, texel, 0).rgb;
- vec3 transmittance = texelFetch(inputTransmittanceBuffer, texel, 0).rgb;
- color.rgb = (accumulated_color / currentSample) * (transmittance / currentSample);
- }
- else if (postProcessType == PASS_POST_ACCUMULATED_LIGHT) {
- vec3 accumulated_light = texelFetch(inputBuffer, texel, 0).rgb;
- vec3 accumulated_color = texelFetch(inputColorBuffer, texel, 0).rgb;
- color.rgb = safe_divide_even_color(accumulated_light, accumulated_color);
- }
- else if (postProcessType == PASS_POST_TWO_LIGHT_BUFFERS) {
- vec3 accumulated_light = texelFetch(inputBuffer, texel, 0).rgb +
- texelFetch(inputSecondLightBuffer, texel, 0).rgb;
- vec3 accumulated_color = texelFetch(inputColorBuffer, texel, 0).rgb;
- color.rgb = safe_divide_even_color(accumulated_light, accumulated_color);
- }
- else {
- /* Output error color: Unknown how to post process this pass. */
- color.rgb = vec3(1.0, 0.0, 1.0);
- }
-
- fragColor = color;
-}
diff --git a/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl b/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl
deleted file mode 100644
index ad0d682dcf4..00000000000
--- a/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl
+++ /dev/null
@@ -1,52 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
-#pragma BLENDER_REQUIRE(lights_lib.glsl)
-
-uniform sampler2D depthBuffer;
-
-out vec4 fragColor;
-
-void main()
-{
- if (laNumLight == 0) {
- /* Early exit: No lights in scene */
- fragColor.r = 1.0;
- return;
- }
-
- ivec2 texel = ivec2(gl_FragCoord.xy);
- float depth = texelFetch(depthBuffer, texel, 0).r;
- if (depth == 1.0f) {
- /* Early exit background does not receive shadows */
- fragColor.r = 1.0;
- return;
- }
-
- vec2 texel_size = 1.0 / vec2(textureSize(depthBuffer, 0)).xy;
- vec2 uvs = saturate(gl_FragCoord.xy * texel_size);
- vec4 rand = texelfetch_noise_tex(texel);
-
- float accum_light = 0.0;
-
- vec3 vP = get_view_space_from_depth(uvs, depth);
- vec3 P = transform_point(ViewMatrixInverse, vP);
-
- vec3 vNg = safe_normalize(cross(dFdx(vP), dFdy(vP)));
-
- for (int i = 0; i < MAX_LIGHT && i < laNumLight; i++) {
- LightData ld = lights_data[i];
-
- vec4 l_vector; /* Non-Normalized Light Vector with length in last component. */
- l_vector.xyz = ld.l_position - P;
- l_vector.w = length(l_vector.xyz);
-
- float l_vis = light_shadowing(ld, P, 1.0);
-
- l_vis *= light_contact_shadows(ld, P, vP, vNg, rand.x, 1.0);
-
- accum_light += l_vis;
- }
-
- fragColor.r = accum_light / float(laNumLight);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/shadow_frag.glsl b/source/blender/draw/engines/eevee/shaders/shadow_frag.glsl
deleted file mode 100644
index a394ce2b6f4..00000000000
--- a/source/blender/draw/engines/eevee/shaders/shadow_frag.glsl
+++ /dev/null
@@ -1,5 +0,0 @@
-
-void main()
-{
- /* Do nothing */
-}
diff --git a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
deleted file mode 100644
index cbfa9737a84..00000000000
--- a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
+++ /dev/null
@@ -1,54 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
-#pragma BLENDER_REQUIRE(surface_lib.glsl)
-
-in vec3 pos;
-in vec3 nor;
-
-void main()
-{
- GPU_INTEL_VERTEX_SHADER_WORKAROUND
-
-#ifdef HAIR_SHADER
- hairStrandID = hair_get_strand_id();
- vec3 pos, binor;
- hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0),
- ModelMatrixInverse,
- ViewMatrixInverse[3].xyz,
- ViewMatrixInverse[2].xyz,
- pos,
- hairTangent,
- binor,
- hairTime,
- hairThickness,
- hairThickTime);
-
- worldNormal = cross(hairTangent, binor);
- vec3 world_pos = pos;
-#elif defined(POINTCLOUD_SHADER)
- pointcloud_get_pos_and_radius(pointPosition, pointRadius);
- pointID = gl_VertexID;
-#else
- vec3 world_pos = point_object_to_world(pos);
-#endif
-
- gl_Position = point_world_to_ndc(world_pos);
-#ifdef MESH_SHADER
- worldPosition = world_pos;
- viewPosition = point_world_to_view(worldPosition);
-
-# ifndef HAIR_SHADER
- worldNormal = normalize(normal_object_to_world(nor));
-# endif
-
- /* No need to normalize since this is just a rotation. */
- viewNormal = normal_world_to_view(worldNormal);
-# ifdef USE_ATTR
-# ifdef HAIR_SHADER
- pos = hair_get_strand_pos();
-# endif
- pass_attr(pos, NormalMatrix, ModelMatrixInverse);
-# endif
-#endif
-}
diff --git a/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl b/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl
deleted file mode 100644
index a563291bb90..00000000000
--- a/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl
+++ /dev/null
@@ -1,91 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
-#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl)
-#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
-#pragma BLENDER_REQUIRE(raytrace_lib.glsl)
-#pragma BLENDER_REQUIRE(surface_lib.glsl)
-
-/* ------------ Refraction ------------ */
-
-#define BTDF_BIAS 0.85
-
-uniform sampler2D refractColorBuffer;
-
-uniform float refractionDepth;
-
-vec4 screen_space_refraction(vec3 vP, vec3 N, vec3 V, float ior, float roughnessSquared, vec4 rand)
-{
- float alpha = max(0.002, roughnessSquared);
-
- /* Importance sampling bias */
- rand.x = mix(rand.x, 0.0, BTDF_BIAS);
-
- vec3 T, B;
- make_orthonormal_basis(N, T, B);
- float pdf;
- /* Microfacet normal */
- vec3 H = sample_ggx(rand.xzw, alpha, V, N, T, B, pdf);
-
- /* If ray is bad (i.e. going below the plane) regenerate. */
- if (F_eta(ior, dot(H, V)) < 1.0) {
- H = sample_ggx(rand.xzw * vec3(1.0, -1.0, -1.0), alpha, V, N, T, B, pdf);
- }
-
- vec3 vV = viewCameraVec(vP);
- float eta = 1.0 / ior;
- if (dot(H, V) < 0.0) {
- H = -H;
- eta = ior;
- }
-
- vec3 R = refract(-V, H, 1.0 / ior);
-
- R = transform_direction(ViewMatrix, R);
-
- Ray ray;
- ray.origin = vP;
- ray.direction = R * 1e16;
-
- RayTraceParameters params;
- params.thickness = ssrThickness;
- params.jitter = rand.y;
- params.trace_quality = ssrQuality;
- params.roughness = roughnessSquared;
-
- vec3 hit_pos;
- bool hit = raytrace(ray, params, false, true, hit_pos);
-
- if (hit && (F_eta(ior, dot(H, V)) < 1.0)) {
- hit_pos = get_view_space_from_depth(hit_pos.xy, hit_pos.z);
- float hit_dist = distance(hit_pos, vP);
-
- float cone_cos = cone_cosine(roughnessSquared);
- float cone_tan = sqrt(1 - cone_cos * cone_cos) / cone_cos;
-
- /* Empirical fit for refraction. */
- /* TODO: find a better fit or precompute inside the LUT. */
- cone_tan *= 0.5 * fast_sqrt(f0_from_ior((ior < 1.0) ? 1.0 / ior : ior));
-
- float cone_footprint = hit_dist * cone_tan;
-
- /* find the offset in screen space by multiplying a point
- * in camera space at the depth of the point by the projection matrix. */
- float homcoord = ProjectionMatrix[2][3] * hit_pos.z + ProjectionMatrix[3][3];
- /* UV space footprint */
- cone_footprint = BTDF_BIAS * 0.5 * max(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) *
- cone_footprint / homcoord;
-
- vec2 hit_uvs = project_point(ProjectionMatrix, hit_pos).xy * 0.5 + 0.5;
-
- /* Texel footprint */
- vec2 texture_size = vec2(textureSize(refractColorBuffer, 0).xy) / hizUvScale.xy;
- float mip = clamp(log2(cone_footprint * max(texture_size.x, texture_size.y)), 0.0, 9.0);
-
- vec3 spec = textureLod(refractColorBuffer, hit_uvs * hizUvScale.xy, mip).xyz;
- float mask = screen_border_mask(hit_uvs);
-
- return vec4(spec, mask);
- }
-
- return vec4(0.0);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl
deleted file mode 100644
index 889bf439d5f..00000000000
--- a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl
+++ /dev/null
@@ -1,94 +0,0 @@
-
-/* Required by some nodes. */
-#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
-#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
-
-#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_diffuse_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_translucent_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_refraction_lib.glsl)
-
-#pragma BLENDER_REQUIRE(surface_lib.glsl)
-#pragma BLENDER_REQUIRE(volumetric_lib.glsl)
-
-#ifdef USE_ALPHA_BLEND
-/* Use dual source blending to be able to make a whole range of effects. */
-layout(location = 0, index = 0) out vec4 outRadiance;
-layout(location = 0, index = 1) out vec4 outTransmittance;
-
-#else /* OPAQUE */
-layout(location = 0) out vec4 outRadiance;
-layout(location = 1) out vec2 ssrNormals;
-layout(location = 2) out vec4 ssrData;
-# ifdef USE_SSS
-layout(location = 3) out vec3 sssIrradiance;
-layout(location = 4) out float sssRadius;
-layout(location = 5) out vec3 sssAlbedo;
-# endif
-
-#endif
-
-void main()
-{
- Closure cl = nodetree_exec();
-
- float holdout = saturate(1.0 - cl.holdout);
- float transmit = saturate(avg(cl.transmittance));
- float alpha = 1.0 - transmit;
-
-#ifdef USE_ALPHA_BLEND
- vec2 uvs = gl_FragCoord.xy * volCoordScale.zw;
- vec3 vol_transmit, vol_scatter;
- volumetric_resolve(uvs, gl_FragCoord.z, vol_transmit, vol_scatter);
-
- /* Removes part of the volume scattering that have
- * already been added to the destination pixels.
- * Since we do that using the blending pipeline we need to account for material transmittance. */
- vol_scatter -= vol_scatter * cl.transmittance;
-
- cl.radiance = cl.radiance * holdout * vol_transmit + vol_scatter;
- outRadiance = vec4(cl.radiance, alpha * holdout);
- outTransmittance = vec4(cl.transmittance, transmit) * holdout;
-#else
- outRadiance = vec4(cl.radiance, holdout);
- ssrNormals = cl.ssr_normal;
- ssrData = cl.ssr_data;
-# ifdef USE_SSS
- sssIrradiance = cl.sss_irradiance;
- sssRadius = cl.sss_radius;
- sssAlbedo = cl.sss_albedo;
-# endif
-#endif
-
- /* For Probe capture */
-#ifdef USE_SSS
- float fac = float(!sssToggle);
-
- /* TODO(fclem): we shouldn't need this.
- * Just disable USE_SSS when USE_REFRACTION is enabled. */
-# ifdef USE_REFRACTION
- /* SSRefraction pass is done after the SSS pass.
- * In order to not lose the diffuse light totally we
- * need to merge the SSS radiance to the main radiance. */
- fac = 1.0;
-# endif
-
- outRadiance.rgb += cl.sss_irradiance.rgb * cl.sss_albedo.rgb * fac;
-#endif
-
-#ifndef USE_ALPHA_BLEND
- float alpha_div = safe_rcp(alpha);
- outRadiance.rgb *= alpha_div;
- ssrData.rgb *= alpha_div;
-# ifdef USE_SSS
- sssAlbedo.rgb *= alpha_div;
-# endif
-#endif
-
-#ifdef LOOKDEV
- /* Lookdev spheres are rendered in front. */
- gl_FragDepth = 0.0;
-#endif
-}
diff --git a/source/blender/draw/engines/eevee/shaders/surface_geom.glsl b/source/blender/draw/engines/eevee/shaders/surface_geom.glsl
deleted file mode 100644
index ad437dca79a..00000000000
--- a/source/blender/draw/engines/eevee/shaders/surface_geom.glsl
+++ /dev/null
@@ -1,46 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(surface_lib.glsl)
-
-layout(triangles) in;
-layout(triangle_strip, max_vertices = 3) out;
-
-RESOURCE_ID_VARYING
-
-/* Only used to compute barycentric coordinates. */
-
-void main()
-{
-#ifdef DO_BARYCENTRIC_DISTANCES
- dataAttrOut.barycentricDist = calc_barycentric_distances(
- dataIn[0].worldPosition, dataIn[1].worldPosition, dataIn[2].worldPosition);
-#endif
-
- PASS_RESOURCE_ID
-
-#ifdef USE_ATTR
- pass_attr(0);
-#endif
- PASS_SURFACE_INTERFACE(0);
- gl_Position = gl_in[0].gl_Position;
- gl_ClipDistance[0] = gl_in[0].gl_ClipDistance[0];
- EmitVertex();
-
-#ifdef USE_ATTR
- pass_attr(1);
-#endif
- PASS_SURFACE_INTERFACE(1);
- gl_Position = gl_in[1].gl_Position;
- gl_ClipDistance[0] = gl_in[1].gl_ClipDistance[0];
- EmitVertex();
-
-#ifdef USE_ATTR
- pass_attr(2);
-#endif
- PASS_SURFACE_INTERFACE(2);
- gl_Position = gl_in[2].gl_Position;
- gl_ClipDistance[0] = gl_in[2].gl_ClipDistance[0];
- EmitVertex();
-
- EndPrimitive();
-}
diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl
deleted file mode 100644
index d7fc5e0b52a..00000000000
--- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl
+++ /dev/null
@@ -1,54 +0,0 @@
-/** This describe the entire interface of the shader. */
-
-#define SURFACE_INTERFACE \
- vec3 worldPosition; \
- vec3 viewPosition; \
- vec3 worldNormal; \
- vec3 viewNormal;
-
-#if defined(STEP_RESOLVE) || defined(STEP_RAYTRACE)
-/* SSR will set these global variables itself.
- * Also make false positive compiler warnings disappear by setting values. */
-vec3 worldPosition = vec3(0);
-vec3 viewPosition = vec3(0);
-vec3 worldNormal = vec3(0);
-vec3 viewNormal = vec3(0);
-
-#elif defined(GPU_GEOMETRY_SHADER)
-in ShaderStageInterface{SURFACE_INTERFACE} dataIn[];
-
-out ShaderStageInterface{SURFACE_INTERFACE} dataOut;
-
-# define PASS_SURFACE_INTERFACE(vert) \
- dataOut.worldPosition = dataIn[vert].worldPosition; \
- dataOut.viewPosition = dataIn[vert].viewPosition; \
- dataOut.worldNormal = dataIn[vert].worldNormal; \
- dataOut.viewNormal = dataIn[vert].viewNormal;
-
-#else /* GPU_VERTEX_SHADER || GPU_FRAGMENT_SHADER*/
-
-IN_OUT ShaderStageInterface{SURFACE_INTERFACE};
-
-#endif
-
-#ifdef HAIR_SHADER
-IN_OUT ShaderHairInterface
-{
- /* world space */
- vec3 hairTangent;
- float hairThickTime;
- float hairThickness;
- float hairTime;
- flat int hairStrandID;
-};
-#endif
-
-#ifdef POINTCLOUD_SHADER
-IN_OUT ShaderPointCloudInterface
-{
- /* world space */
- float pointRadius;
- float pointPosition;
- flat int pointID;
-};
-#endif
diff --git a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl
deleted file mode 100644
index 51e9eda6cc2..00000000000
--- a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl
+++ /dev/null
@@ -1,63 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(surface_lib.glsl)
-
-#ifndef HAIR_SHADER
-in vec3 pos;
-in vec3 nor;
-#endif
-
-RESOURCE_ID_VARYING
-
-void main()
-{
- GPU_INTEL_VERTEX_SHADER_WORKAROUND
-
- PASS_RESOURCE_ID
-
-#ifdef HAIR_SHADER
- hairStrandID = hair_get_strand_id();
- vec3 pos, binor;
- hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0),
- ModelMatrixInverse,
- ViewMatrixInverse[3].xyz,
- ViewMatrixInverse[2].xyz,
- pos,
- hairTangent,
- binor,
- hairTime,
- hairThickness,
- hairThickTime);
- worldNormal = cross(hairTangent, binor);
- vec3 world_pos = pos;
-#elif defined(POINTCLOUD_SHADER)
- pointcloud_get_pos_and_radius(pointPosition, pointRadius);
- pointID = gl_VertexID;
-#else
- vec3 world_pos = point_object_to_world(pos);
-#endif
-
- gl_Position = point_world_to_ndc(world_pos);
-
- /* Used for planar reflections */
- gl_ClipDistance[0] = dot(vec4(world_pos, 1.0), clipPlanes[0]);
-
-#ifdef MESH_SHADER
- worldPosition = world_pos;
- viewPosition = point_world_to_view(worldPosition);
-
-# ifndef HAIR_SHADER
- worldNormal = normalize(normal_object_to_world(nor));
-# endif
-
- /* No need to normalize since this is just a rotation. */
- viewNormal = normal_world_to_view(worldNormal);
-# ifdef USE_ATTR
-# ifdef HAIR_SHADER
- pos = hair_get_strand_pos();
-# endif
- pass_attr(pos, NormalMatrix, ModelMatrixInverse);
-# endif
-#endif
-}
diff --git a/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl b/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl
deleted file mode 100644
index 0c01c46c2ba..00000000000
--- a/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl
+++ /dev/null
@@ -1,18 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
-
-uniform sampler2D blueNoise;
-uniform vec3 offsets;
-
-out vec4 FragColor;
-
-void main(void)
-{
- vec3 blue_noise = texelFetch(blueNoise, ivec2(gl_FragCoord.xy), 0).xyz;
-
- float noise = fract(blue_noise.y + offsets.z);
- FragColor.x = fract(blue_noise.x + offsets.x);
- FragColor.y = fract(blue_noise.z + offsets.y);
- FragColor.z = cos(noise * M_2PI);
- FragColor.w = sin(noise * M_2PI);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_accum_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_accum_frag.glsl
deleted file mode 100644
index d8ce7905a1a..00000000000
--- a/source/blender/draw/engines/eevee/shaders/volumetric_accum_frag.glsl
+++ /dev/null
@@ -1,11 +0,0 @@
-
-/* This shader is used to add default values to the volume accum textures.
- * so it looks similar (transmittance = 1, scattering = 0) */
-layout(location = 0) out vec4 FragColor0;
-layout(location = 1) out vec4 FragColor1;
-
-void main()
-{
- FragColor0 = vec4(0.0);
- FragColor1 = vec4(1.0);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl
deleted file mode 100644
index 25661a0d731..00000000000
--- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl
+++ /dev/null
@@ -1,74 +0,0 @@
-
-#pragma BLENDER_REQUIRE(volumetric_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
-
-/* Based on Frosbite Unified Volumetric.
- * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
-
-#ifdef MESH_SHADER
-uniform vec3 volumeOrcoLoc;
-uniform vec3 volumeOrcoSize;
-uniform mat4 volumeObjectToTexture;
-uniform float volumeDensityScale = 1.0;
-#endif
-
-flat in int slice;
-
-/* Warning: these are not attributes, these are global vars. */
-vec3 worldPosition = vec3(0.0);
-vec3 viewPosition = vec3(0.0);
-vec3 viewNormal = vec3(0.0);
-#ifdef MESH_SHADER
-vec3 volumeObjectLocalCoord = vec3(0.0);
-#endif
-
-layout(location = 0) out vec4 volumeScattering;
-layout(location = 1) out vec4 volumeExtinction;
-layout(location = 2) out vec4 volumeEmissive;
-layout(location = 3) out vec4 volumePhase;
-
-/* Store volumetric properties into the froxel textures. */
-
-void main()
-{
- ivec3 volume_cell = ivec3(gl_FragCoord.xy, slice);
- vec3 ndc_cell = volume_to_ndc((vec3(volume_cell) + volJitter.xyz) * volInvTexSize.xyz);
-
- viewPosition = get_view_space_from_depth(ndc_cell.xy, ndc_cell.z);
- worldPosition = point_view_to_world(viewPosition);
-#ifdef MESH_SHADER
- volumeObjectLocalCoord = point_world_to_object(worldPosition);
- /* TODO: redundant transform */
- volumeObjectLocalCoord = (volumeObjectLocalCoord - volumeOrcoLoc + volumeOrcoSize) /
- (volumeOrcoSize * 2.0);
- volumeObjectLocalCoord = (volumeObjectToTexture * vec4(volumeObjectLocalCoord, 1.0)).xyz;
-
- if (any(lessThan(volumeObjectLocalCoord, vec3(0.0))) ||
- any(greaterThan(volumeObjectLocalCoord, vec3(1.0))))
- discard;
-#endif
-
-#ifdef CLEAR
- Closure cl = CLOSURE_DEFAULT;
-#else
- Closure cl = nodetree_exec();
-#endif
-
-#ifdef MESH_SHADER
- cl.scatter *= volumeDensityScale;
- cl.absorption *= volumeDensityScale;
- cl.emission *= volumeDensityScale;
-#endif
-
- volumeScattering = vec4(cl.scatter, 1.0);
- volumeExtinction = vec4(cl.absorption + cl.scatter, 1.0);
- volumeEmissive = vec4(cl.emission, 1.0);
-
- /* Do not add phase weight if no scattering. */
- if (all(equal(cl.scatter, vec3(0.0)))) {
- volumePhase = vec4(0.0);
- }
- else {
- volumePhase = vec4(cl.anisotropy, vec3(1.0));
- }
-}
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl
deleted file mode 100644
index 5226da57a06..00000000000
--- a/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl
+++ /dev/null
@@ -1,80 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-
-#ifdef MESH_SHADER
-/* TODO: tight slices. */
-layout(triangles) in;
-layout(triangle_strip, max_vertices = 3) out;
-#else /* World */
-layout(triangles) in;
-layout(triangle_strip, max_vertices = 3) out;
-#endif
-
-in vec4 vPos[];
-
-flat out int slice;
-
-RESOURCE_ID_VARYING
-
-#ifdef MESH_SHADER
-/* TODO: tight slices. */
-void main()
-{
- gl_Layer = slice = int(vPos[0].z);
-
- PASS_RESOURCE_ID
-
-# ifdef USE_ATTR
- pass_attr(0);
-# endif
- gl_Position = vPos[0].xyww;
- EmitVertex();
-
-# ifdef USE_ATTR
- pass_attr(1);
-# endif
- gl_Position = vPos[1].xyww;
- EmitVertex();
-
-# ifdef USE_ATTR
- pass_attr(2);
-# endif
- gl_Position = vPos[2].xyww;
- EmitVertex();
-
- EndPrimitive();
-}
-
-#else /* World */
-
-/* This is just a pass-through geometry shader that send the geometry
- * to the layer corresponding to its depth. */
-
-void main()
-{
- gl_Layer = slice = int(vPos[0].z);
-
- PASS_RESOURCE_ID
-
-# ifdef USE_ATTR
- pass_attr(0);
-# endif
- gl_Position = vPos[0].xyww;
- EmitVertex();
-
-# ifdef USE_ATTR
- pass_attr(1);
-# endif
- gl_Position = vPos[1].xyww;
- EmitVertex();
-
-# ifdef USE_ATTR
- pass_attr(2);
-# endif
- gl_Position = vPos[2].xyww;
- EmitVertex();
-
- EndPrimitive();
-}
-
-#endif
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl
deleted file mode 100644
index 12b7d8acbea..00000000000
--- a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl
+++ /dev/null
@@ -1,85 +0,0 @@
-
-#pragma BLENDER_REQUIRE(volumetric_lib.glsl)
-
-/* Based on Frosbite Unified Volumetric.
- * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
-
-/* Step 3 : Integrate for each froxel the final amount of light
- * scattered back to the viewer and the amount of transmittance. */
-
-uniform sampler3D volumeScattering; /* Result of the scatter step */
-uniform sampler3D volumeExtinction;
-
-#ifdef USE_VOLUME_OPTI
-uniform layout(r11f_g11f_b10f) writeonly restrict image3D finalScattering_img;
-uniform layout(r11f_g11f_b10f) writeonly restrict image3D finalTransmittance_img;
-
-vec3 finalScattering;
-vec3 finalTransmittance;
-#else
-
-flat in int slice;
-
-layout(location = 0) out vec3 finalScattering;
-layout(location = 1) out vec3 finalTransmittance;
-#endif
-
-void main()
-{
- /* Start with full transmittance and no scattered light. */
- finalScattering = vec3(0.0);
- finalTransmittance = vec3(1.0);
-
- vec3 tex_size = vec3(textureSize(volumeScattering, 0).xyz);
-
- /* Compute view ray. */
- vec2 uvs = gl_FragCoord.xy / tex_size.xy;
- vec3 ndc_cell = volume_to_ndc(vec3(uvs, 1e-5));
- vec3 view_cell = get_view_space_from_depth(ndc_cell.xy, ndc_cell.z);
-
- /* Ortho */
- float prev_ray_len = view_cell.z;
- float orig_ray_len = 1.0;
-
- /* Persp */
- if (ProjectionMatrix[3][3] == 0.0) {
- prev_ray_len = length(view_cell);
- orig_ray_len = prev_ray_len / view_cell.z;
- }
-
-#ifdef USE_VOLUME_OPTI
- int slice = textureSize(volumeScattering, 0).z;
- ivec2 texco = ivec2(gl_FragCoord.xy);
-#endif
- for (int i = 0; i <= slice; i++) {
- ivec3 volume_cell = ivec3(gl_FragCoord.xy, i);
-
- vec3 Lscat = texelFetch(volumeScattering, volume_cell, 0).rgb;
- vec3 s_extinction = texelFetch(volumeExtinction, volume_cell, 0).rgb;
-
- float cell_depth = volume_z_to_view_z((float(i) + 1.0) / tex_size.z);
- float ray_len = orig_ray_len * cell_depth;
-
- /* Emission does not work of there is no extinction because
- * Tr evaluates to 1.0 leading to Lscat = 0.0. (See T65771) */
- s_extinction = max(vec3(1e-7) * step(1e-5, Lscat), s_extinction);
-
- /* Evaluate Scattering */
- float s_len = abs(ray_len - prev_ray_len);
- prev_ray_len = ray_len;
- vec3 Tr = exp(-s_extinction * s_len);
-
- /* integrate along the current step segment */
- Lscat = (Lscat - Lscat * Tr) / max(vec3(1e-8), s_extinction);
- /* accumulate and also take into account the transmittance from previous steps */
- finalScattering += finalTransmittance * Lscat;
-
- finalTransmittance *= Tr;
-
-#ifdef USE_VOLUME_OPTI
- ivec3 coord = ivec3(texco, i);
- imageStore(finalScattering_img, coord, vec4(finalScattering, 0.0));
- imageStore(finalTransmittance_img, coord, vec4(finalTransmittance, 0.0));
-#endif
- }
-}
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl
deleted file mode 100644
index 777e48fde34..00000000000
--- a/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl
+++ /dev/null
@@ -1,198 +0,0 @@
-
-#pragma BLENDER_REQUIRE(lights_lib.glsl)
-#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
-#pragma BLENDER_REQUIRE(irradiance_lib.glsl)
-
-/* Based on Frosbite Unified Volumetric.
- * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
-
-/* Volume slice to view space depth. */
-float volume_z_to_view_z(float z)
-{
- if (ProjectionMatrix[3][3] == 0.0) {
- /* Exponential distribution */
- return (exp2(z / volDepthParameters.z) - volDepthParameters.x) / volDepthParameters.y;
- }
- else {
- /* Linear distribution */
- return mix(volDepthParameters.x, volDepthParameters.y, z);
- }
-}
-
-float view_z_to_volume_z(float depth)
-{
- if (ProjectionMatrix[3][3] == 0.0) {
- /* Exponential distribution */
- return volDepthParameters.z * log2(depth * volDepthParameters.y + volDepthParameters.x);
- }
- else {
- /* Linear distribution */
- return (depth - volDepthParameters.x) * volDepthParameters.z;
- }
-}
-
-/* Volume texture normalized coordinates to NDC (special range [0, 1]). */
-vec3 volume_to_ndc(vec3 cos)
-{
- cos.z = volume_z_to_view_z(cos.z);
- cos.z = get_depth_from_view_z(cos.z);
- cos.xy /= volCoordScale.xy;
- return cos;
-}
-
-vec3 ndc_to_volume(vec3 cos)
-{
- cos.z = get_view_z_from_depth(cos.z);
- cos.z = view_z_to_volume_z(cos.z);
- cos.xy *= volCoordScale.xy;
- return cos;
-}
-
-float phase_function_isotropic()
-{
- return 1.0 / (4.0 * M_PI);
-}
-
-float phase_function(vec3 v, vec3 l, float g)
-{
- /* Henyey-Greenstein */
- float cos_theta = dot(v, l);
- g = clamp(g, -1.0 + 1e-3, 1.0 - 1e-3);
- float sqr_g = g * g;
- return (1 - sqr_g) / max(1e-8, 4.0 * M_PI * pow(1 + sqr_g - 2 * g * cos_theta, 3.0 / 2.0));
-}
-
-vec3 light_volume(LightData ld, vec4 l_vector)
-{
- float power = 1.0;
- if (ld.l_type != SUN) {
- /**
- * Using "Point Light Attenuation Without Singularity" from Cem Yuksel
- * http://www.cemyuksel.com/research/pointlightattenuation/pointlightattenuation.pdf
- * http://www.cemyuksel.com/research/pointlightattenuation/
- */
- float d = l_vector.w;
- float d_sqr = sqr(d);
- float r_sqr = ld.l_volume_radius;
- /* Using reformulation that has better numerical precision. */
- power = 2.0 / (d_sqr + r_sqr + d * sqrt(d_sqr + r_sqr));
-
- if (ld.l_type == AREA_RECT || ld.l_type == AREA_ELLIPSE) {
- /* Modulate by light plane orientation / solid angle. */
- power *= saturate(dot(-ld.l_forward, l_vector.xyz / l_vector.w));
- }
- }
- return ld.l_color * ld.l_volume * power;
-}
-
-vec3 light_volume_light_vector(LightData ld, vec3 P)
-{
- if (ld.l_type == SUN) {
- return -ld.l_forward;
- }
- else if (ld.l_type == AREA_RECT || ld.l_type == AREA_ELLIPSE) {
- vec3 L = P - ld.l_position;
- vec2 closest_point = vec2(dot(ld.l_right, L), dot(ld.l_up, L));
- vec2 max_pos = vec2(ld.l_sizex, ld.l_sizey);
- closest_point /= max_pos;
-
- if (ld.l_type == AREA_ELLIPSE) {
- closest_point /= max(1.0, length(closest_point));
- }
- else {
- closest_point = clamp(closest_point, -1.0, 1.0);
- }
- closest_point *= max_pos;
-
- vec3 L_prime = ld.l_right * closest_point.x + ld.l_up * closest_point.y;
- return L_prime - L;
- }
- else {
- return ld.l_position - P;
- }
-}
-
-#define VOLUMETRIC_SHADOW_MAX_STEP 128.0
-
-vec3 participating_media_extinction(vec3 wpos, sampler3D volume_extinction)
-{
- /* Waiting for proper volume shadowmaps and out of frustum shadow map. */
- vec3 ndc = project_point(ViewProjectionMatrix, wpos);
- vec3 volume_co = ndc_to_volume(ndc * 0.5 + 0.5);
-
- /* Let the texture be clamped to edge. This reduce visual glitches. */
- return texture(volume_extinction, volume_co).rgb;
-}
-
-vec3 light_volume_shadow(LightData ld, vec3 ray_wpos, vec4 l_vector, sampler3D volume_extinction)
-{
-#if defined(VOLUME_SHADOW)
- /* If light is shadowed, use the shadow vector, if not, reuse the light vector. */
- if (volUseSoftShadows && ld.l_shadowid >= 0.0) {
- ShadowData sd = shadows_data[int(ld.l_shadowid)];
-
- if (ld.l_type == SUN) {
- l_vector.xyz = shadows_cascade_data[int(sd.sh_data_index)].sh_shadow_vec;
- /* No need for length, it is recomputed later. */
- }
- else {
- l_vector.xyz = shadows_cube_data[int(sd.sh_data_index)].position.xyz - ray_wpos;
- l_vector.w = length(l_vector.xyz);
- }
- }
-
- /* Heterogeneous volume shadows */
- float dd = l_vector.w / volShadowSteps;
- vec3 L = l_vector.xyz / volShadowSteps;
-
- if (ld.l_type == SUN) {
- /* For sun light we scan the whole frustum. So we need to get the correct endpoints. */
- vec3 ndcP = project_point(ViewProjectionMatrix, ray_wpos);
- vec3 ndcL = project_point(ViewProjectionMatrix, ray_wpos + l_vector.xyz) - ndcP;
-
- vec3 frustum_isect = ndcP + ndcL * line_unit_box_intersect_dist_safe(ndcP, ndcL);
-
- L = project_point(ViewProjectionMatrixInverse, frustum_isect) - ray_wpos;
- L /= volShadowSteps;
- dd = length(L);
- }
-
- vec3 shadow = vec3(1.0);
- for (float s = 1.0; s < VOLUMETRIC_SHADOW_MAX_STEP && s <= volShadowSteps; s += 1.0) {
- vec3 pos = ray_wpos + L * s;
- vec3 s_extinction = participating_media_extinction(pos, volume_extinction);
- shadow *= exp(-s_extinction * dd);
- }
- return shadow;
-#else
- return vec3(1.0);
-#endif /* VOLUME_SHADOW */
-}
-
-vec3 irradiance_volumetric(vec3 wpos)
-{
-#ifdef IRRADIANCE_HL2
- IrradianceData ir_data = load_irradiance_cell(0, vec3(1.0));
- vec3 irradiance = ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2];
- ir_data = load_irradiance_cell(0, vec3(-1.0));
- irradiance += ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2];
- irradiance *= 0.16666666; /* 1/6 */
- return irradiance;
-#else
- return vec3(0.0);
-#endif
-}
-
-uniform sampler3D inScattering;
-uniform sampler3D inTransmittance;
-
-void volumetric_resolve(vec2 frag_uvs,
- float frag_depth,
- out vec3 transmittance,
- out vec3 scattering)
-{
- vec3 volume_cos = ndc_to_volume(vec3(frag_uvs, frag_depth));
-
- scattering = texture(inScattering, volume_cos).rgb;
- transmittance = texture(inTransmittance, volume_cos).rgb;
-}
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl
deleted file mode 100644
index 665a34741ca..00000000000
--- a/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl
+++ /dev/null
@@ -1,34 +0,0 @@
-
-#pragma BLENDER_REQUIRE(volumetric_lib.glsl)
-
-/* Based on Frosbite Unified Volumetric.
- * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
-
-/* Step 4 : Apply final integration on top of the scene color. */
-
-uniform sampler2D inSceneDepth;
-
-/* Blend equation is : FragColor0 + FragColor1 * DstColor */
-#ifdef VOLUMETRICS_ACCUM
-layout(location = 0) out vec4 FragColor0;
-layout(location = 1) out vec4 FragColor1;
-#else
-layout(location = 0, index = 0) out vec4 FragColor0;
-layout(location = 0, index = 1) out vec4 FragColor1;
-#endif
-
-void main()
-{
- vec2 uvs = gl_FragCoord.xy / vec2(textureSize(inSceneDepth, 0));
- float scene_depth = texture(inSceneDepth, uvs).r;
-
- vec3 transmittance, scattering;
- volumetric_resolve(uvs, scene_depth, transmittance, scattering);
-
- /* Approximate volume alpha by using a monochromatic transmittance
- * and adding it to the scene alpha. */
- float alpha = dot(transmittance, vec3(1.0 / 3.0));
-
- FragColor0 = vec4(scattering, 1.0 - alpha);
- FragColor1 = vec4(transmittance, alpha);
-}
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl
deleted file mode 100644
index dc755aeab8b..00000000000
--- a/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl
+++ /dev/null
@@ -1,86 +0,0 @@
-
-#pragma BLENDER_REQUIRE(volumetric_lib.glsl)
-
-/* Based on Frosbite Unified Volumetric.
- * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
-
-/* Step 2 : Evaluate all light scattering for each froxels.
- * Also do the temporal reprojection to fight aliasing artifacts. */
-
-uniform sampler3D volumeScattering;
-uniform sampler3D volumeExtinction;
-uniform sampler3D volumeEmission;
-uniform sampler3D volumePhase;
-
-uniform sampler3D historyScattering;
-uniform sampler3D historyTransmittance;
-
-flat in int slice;
-
-layout(location = 0) out vec4 outScattering;
-layout(location = 1) out vec4 outTransmittance;
-
-void main()
-{
- ivec3 volume_cell = ivec3(gl_FragCoord.xy, slice);
-
- /* Emission */
- outScattering = texelFetch(volumeEmission, volume_cell, 0);
- outTransmittance = texelFetch(volumeExtinction, volume_cell, 0);
- vec3 s_scattering = texelFetch(volumeScattering, volume_cell, 0).rgb;
- vec3 volume_ndc = volume_to_ndc((vec3(volume_cell) + volJitter.xyz) * volInvTexSize.xyz);
- vec3 P = get_world_space_from_depth(volume_ndc.xy, volume_ndc.z);
- vec3 V = cameraVec(P);
-
- vec2 phase = texelFetch(volumePhase, volume_cell, 0).rg;
- float s_anisotropy = phase.x / max(1.0, phase.y);
-
- /* Environment : Average color. */
- outScattering.rgb += irradiance_volumetric(P) * s_scattering * phase_function_isotropic();
-
-#ifdef VOLUME_LIGHTING /* Lights */
- for (int i = 0; i < MAX_LIGHT && i < laNumLight; i++) {
- LightData ld = lights_data[i];
-
- if (ld.l_volume == 0.0) {
- continue;
- }
-
- vec4 l_vector;
- l_vector.xyz = light_volume_light_vector(ld, P);
- l_vector.w = length(l_vector.xyz);
-
- float vis = light_visibility(ld, P, l_vector);
-
- if (vis < 1e-4) {
- continue;
- }
-
- vec3 Li = light_volume(ld, l_vector) * light_volume_shadow(ld, P, l_vector, volumeExtinction);
-
- outScattering.rgb += Li * vis * s_scattering *
- phase_function(-V, l_vector.xyz / l_vector.w, s_anisotropy);
- }
-#endif
-
- /* Temporal supersampling */
- /* Note : this uses the cell non-jittered position (texel center). */
- vec3 curr_ndc = volume_to_ndc(vec3(gl_FragCoord.xy, float(slice) + 0.5) * volInvTexSize.xyz);
- vec3 wpos = get_world_space_from_depth(curr_ndc.xy, curr_ndc.z);
- vec3 prev_ndc = project_point(pastViewProjectionMatrix, wpos);
- vec3 prev_volume = ndc_to_volume(prev_ndc * 0.5 + 0.5);
-
- if ((volHistoryAlpha > 0.0) && all(greaterThan(prev_volume, vec3(0.0))) &&
- all(lessThan(prev_volume, vec3(1.0)))) {
- vec4 h_Scattering = texture(historyScattering, prev_volume);
- vec4 h_Transmittance = texture(historyTransmittance, prev_volume);
- outScattering = mix(outScattering, h_Scattering, volHistoryAlpha);
- outTransmittance = mix(outTransmittance, h_Transmittance, volHistoryAlpha);
- }
-
- /* Catch NaNs */
- if (any(isnan(outScattering)) || any(isnan(outTransmittance))) {
- outScattering = vec4(0.0);
- outTransmittance = vec4(1.0);
- }
-}
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl
deleted file mode 100644
index b70747ecec3..00000000000
--- a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl
+++ /dev/null
@@ -1,37 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-
-out vec4 vPos;
-
-RESOURCE_ID_VARYING
-
-void main()
-{
- /* Generate Triangle : less memory fetches from a VBO */
- int v_id = gl_VertexID % 3; /* Vertex Id */
- int t_id = gl_VertexID / 3; /* Triangle Id */
-
- /* Crappy diagram
- * ex 1
- * | \
- * | \
- * 1 | \
- * | \
- * | \
- * 0 | \
- * | \
- * | \
- * -1 0 --------------- 2
- * -1 0 1 ex
- */
- vPos.x = float(v_id / 2) * 4.0 - 1.0; /* int divisor round down */
- vPos.y = float(v_id % 2) * 4.0 - 1.0;
- vPos.z = float(t_id);
- vPos.w = 1.0;
-
- PASS_RESOURCE_ID
-
-#ifdef USE_ATTR
- pass_attr(vec3(0.0), mat3(1), mat4(1));
-#endif
-}
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h
index bf5dbac9f68..17a0084689b 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.h
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.h
@@ -46,9 +46,6 @@ struct RenderLayer;
struct View3D;
struct bGPDstroke;
-/* used to convert pixel scale. */
-#define GPENCIL_PIXEL_FACTOR 2000.0f
-
/* used to expand VBOs. Size has a big impact in the speed */
#define GPENCIL_VBO_BLOCK_SIZE 128
diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.c
index 4c09349c35d..c1a2b184c6b 100644
--- a/source/blender/draw/engines/overlay/overlay_shader.c
+++ b/source/blender/draw/engines/overlay/overlay_shader.c
@@ -130,7 +130,6 @@ extern char datatoc_gpu_shader_2D_smooth_color_frag_glsl[];
extern char datatoc_gpu_shader_uniform_color_frag_glsl[];
extern char datatoc_gpu_shader_flat_color_frag_glsl[];
extern char datatoc_gpu_shader_point_varying_color_varying_outline_aa_frag_glsl[];
-extern char datatoc_gpu_shader_common_obinfos_lib_glsl[];
extern char datatoc_gpencil_common_lib_glsl[];
@@ -142,6 +141,7 @@ extern char datatoc_common_smaa_lib_glsl[];
extern char datatoc_common_globals_lib_glsl[];
extern char datatoc_common_pointcloud_lib_glsl[];
extern char datatoc_common_view_lib_glsl[];
+extern char datatoc_common_obinfos_lib_glsl[];
typedef struct OVERLAY_Shaders {
GPUShader *antialiasing;
@@ -897,7 +897,7 @@ GPUShader *OVERLAY_shader_extra_grid(void)
.vert = (const char *[]){sh_cfg->lib,
datatoc_common_view_lib_glsl,
datatoc_common_globals_lib_glsl,
- datatoc_gpu_shader_common_obinfos_lib_glsl,
+ datatoc_common_obinfos_lib_glsl,
datatoc_extra_lightprobe_grid_vert_glsl,
NULL},
.frag = (const char *[]){datatoc_gpu_shader_point_varying_color_frag_glsl, NULL},
@@ -1181,7 +1181,7 @@ GPUShader *OVERLAY_shader_outline_prepass(bool use_wire)
sh_data->outline_prepass_wire = GPU_shader_create_from_arrays({
.vert = (const char *[]){sh_cfg->lib,
datatoc_common_view_lib_glsl,
- datatoc_gpu_shader_common_obinfos_lib_glsl,
+ datatoc_common_obinfos_lib_glsl,
datatoc_outline_prepass_vert_glsl,
NULL},
.geom = (const char *[]){sh_cfg->lib,
@@ -1196,7 +1196,7 @@ GPUShader *OVERLAY_shader_outline_prepass(bool use_wire)
sh_data->outline_prepass = GPU_shader_create_from_arrays({
.vert = (const char *[]){sh_cfg->lib,
datatoc_common_view_lib_glsl,
- datatoc_gpu_shader_common_obinfos_lib_glsl,
+ datatoc_common_obinfos_lib_glsl,
datatoc_outline_prepass_vert_glsl,
NULL},
.frag = (const char *[]){datatoc_outline_prepass_frag_glsl, NULL},
@@ -1216,7 +1216,7 @@ GPUShader *OVERLAY_shader_outline_prepass_gpencil(void)
.vert = (const char *[]){sh_cfg->lib,
datatoc_common_view_lib_glsl,
datatoc_gpencil_common_lib_glsl,
- datatoc_gpu_shader_common_obinfos_lib_glsl,
+ datatoc_common_obinfos_lib_glsl,
datatoc_outline_prepass_vert_glsl,
NULL},
.frag = (const char *[]){datatoc_common_view_lib_glsl,
@@ -1242,7 +1242,7 @@ GPUShader *OVERLAY_shader_outline_prepass_pointcloud(void)
.vert = (const char *[]){sh_cfg->lib,
datatoc_common_view_lib_glsl,
datatoc_common_pointcloud_lib_glsl,
- datatoc_gpu_shader_common_obinfos_lib_glsl,
+ datatoc_common_obinfos_lib_glsl,
datatoc_outline_prepass_vert_glsl,
NULL},
.frag = (const char *[]){datatoc_common_view_lib_glsl,
@@ -1556,7 +1556,7 @@ GPUShader *OVERLAY_shader_wireframe_select(void)
.vert = (const char *[]){sh_cfg->lib,
datatoc_common_view_lib_glsl,
datatoc_common_globals_lib_glsl,
- datatoc_gpu_shader_common_obinfos_lib_glsl,
+ datatoc_common_obinfos_lib_glsl,
datatoc_wireframe_vert_glsl,
NULL},
.frag = (const char *[]){datatoc_wireframe_frag_glsl, NULL},
@@ -1576,7 +1576,7 @@ GPUShader *OVERLAY_shader_wireframe(bool custom_bias)
.vert = (const char *[]){sh_cfg->lib,
datatoc_common_view_lib_glsl,
datatoc_common_globals_lib_glsl,
- datatoc_gpu_shader_common_obinfos_lib_glsl,
+ datatoc_common_obinfos_lib_glsl,
datatoc_wireframe_vert_glsl,
NULL},
.frag = (const char *[]){datatoc_common_view_lib_glsl,
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index b16caf49209..ee5aa401c6e 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -43,6 +43,7 @@
#include "DNA_world_types.h"
#include "GPU_framebuffer.h"
+#include "GPU_material.h"
#include "GPU_primitive.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
@@ -202,13 +203,6 @@ void DRW_texture_free(struct GPUTexture *tex);
/* Shaders */
-typedef void (*GPUMaterialEvalCallbackFn)(struct GPUMaterial *mat,
- int options,
- const char **vert_code,
- const char **geom_code,
- const char **frag_lib,
- const char **defines);
-
struct GPUShader *DRW_shader_create_ex(
const char *vert, const char *geom, const char *frag, const char *defines, const char *name);
struct GPUShader *DRW_shader_create_with_lib_ex(const char *vert,
@@ -247,38 +241,20 @@ struct GPUShader *DRW_shader_create_fullscreen_with_shaderlib_ex(const char *fra
#define DRW_shader_create_fullscreen_with_shaderlib(frag, lib, defines) \
DRW_shader_create_fullscreen_with_shaderlib_ex(frag, lib, defines, __func__)
-struct GPUMaterial *DRW_shader_find_from_world(struct World *wo,
- const void *engine_type,
- int options,
- bool deferred);
-struct GPUMaterial *DRW_shader_find_from_material(struct Material *ma,
- const void *engine_type,
- int options,
- bool deferred);
-struct GPUMaterial *DRW_shader_create_from_world(struct Scene *scene,
- struct World *wo,
- struct bNodeTree *ntree,
- const void *engine_type,
- int options,
- bool is_volume_shader,
- const char *vert,
- const char *geom,
- const char *frag_lib,
- const char *defines,
- bool deferred,
- GPUMaterialEvalCallbackFn callback);
-struct GPUMaterial *DRW_shader_create_from_material(struct Scene *scene,
- struct Material *ma,
- struct bNodeTree *ntree,
- const void *engine_type,
- int options,
- bool is_volume_shader,
- const char *vert,
- const char *geom,
- const char *frag_lib,
- const char *defines,
- bool deferred,
- GPUMaterialEvalCallbackFn callback);
+struct GPUMaterial *DRW_shader_from_world(struct World *wo,
+ struct bNodeTree *ntree,
+ const uint64_t shader_id,
+ const bool is_volume_shader,
+ bool deferred,
+ GPUCodegenCallbackFn callback,
+ void *thunk);
+struct GPUMaterial *DRW_shader_from_material(struct Material *ma,
+ struct bNodeTree *ntree,
+ const uint64_t shader_id,
+ const bool is_volume_shader,
+ bool deferred,
+ GPUCodegenCallbackFn callback,
+ void *thunk);
void DRW_shader_free(struct GPUShader *shader);
#define DRW_SHADER_FREE_SAFE(shader) \
do { \
@@ -370,6 +346,8 @@ typedef enum {
DRW_STATE_PROGRAM_POINT_SIZE = (1u << 31),
} DRWState;
+ENUM_OPERATORS(DRWState, DRW_STATE_PROGRAM_POINT_SIZE)
+
#define DRW_STATE_DEFAULT \
(DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL)
#define DRW_STATE_BLEND_ENABLED \
@@ -466,6 +444,9 @@ void DRW_shgroup_call_compute(DRWShadingGroup *shgroup,
int groups_x_len,
int groups_y_len,
int groups_z_len);
+/* Warning this keeps the ref to groups until it actually draws. */
+void DRW_shgroup_call_compute_ref(DRWShadingGroup *shgroup, int groups_ref[3]);
+
void DRW_shgroup_call_procedural_points(DRWShadingGroup *sh, Object *ob, uint point_count);
void DRW_shgroup_call_procedural_lines(DRWShadingGroup *sh, Object *ob, uint line_count);
void DRW_shgroup_call_procedural_triangles(DRWShadingGroup *sh, Object *ob, uint tri_count);
@@ -488,6 +469,11 @@ void DRW_shgroup_call_instances_with_attrs(DRWShadingGroup *shgroup,
void DRW_shgroup_call_sculpt(DRWShadingGroup *sh, Object *ob, bool wire, bool mask);
void DRW_shgroup_call_sculpt_with_materials(DRWShadingGroup **sh, int num_sh, Object *ob);
+/* Lower level functions. Use DRW_shgroup_call_buffer(_instance) preferably. */
+DRWCallBuffer *DRW_call_buffer_create(struct GPUVertFormat *format);
+
+struct GPUVertBuf *DRW_call_buffer_as_vertbuf(DRWCallBuffer *callbuf);
+
DRWCallBuffer *DRW_shgroup_call_buffer(DRWShadingGroup *shgroup,
struct GPUVertFormat *format,
GPUPrimType prim_type);
@@ -531,6 +517,8 @@ void DRW_shgroup_stencil_set(DRWShadingGroup *shgroup,
*/
void DRW_shgroup_stencil_mask(DRWShadingGroup *shgroup, uint mask);
+void DRW_shgroup_barrier(DRWShadingGroup *shgroup, eGPUBarrier type);
+
/**
* Issue a clear command.
*/
@@ -562,7 +550,7 @@ void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup,
const struct GPUUniformBuf *ubo);
void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup,
const char *name,
- struct GPUUniformBuf **ubo);
+ const struct GPUUniformBuf **ubo);
void DRW_shgroup_uniform_float(DRWShadingGroup *shgroup,
const char *name,
const float *value,
@@ -625,6 +613,9 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup,
void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup,
const char *name,
struct GPUVertBuf *vertex_buffer);
+void DRW_shgroup_vertex_buffer_ref(DRWShadingGroup *shgroup,
+ const char *name,
+ struct GPUVertBuf **vertex_buffer);
bool DRW_shgroup_is_empty(DRWShadingGroup *shgroup);
@@ -713,7 +704,8 @@ const DRWView *DRW_view_get_active(void);
* \note planes must be in world space.
*/
void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len);
-void DRW_view_camtexco_set(DRWView *view, float texco[4]);
+void DRW_view_camtexco_set(DRWView *view, const float texco[4]);
+void DRW_view_camtexco_get(const DRWView *view, float r_texco[4]);
/* For all getters, if view is NULL, default view is assumed. */
@@ -730,6 +722,7 @@ void DRW_view_frustum_corners_get(const DRWView *view, BoundBox *corners);
* See #draw_frustum_culling_planes_calc() for the plane order.
*/
void DRW_view_frustum_planes_get(const DRWView *view, float planes[6][4]);
+BoundSphere DRW_view_frustum_bsphere_get(const DRWView *view);
/**
* These are in view-space, so negative if in perspective.
diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h
index fdb7cfba580..f4be85d5116 100644
--- a/source/blender/draw/intern/draw_common.h
+++ b/source/blender/draw/intern/draw_common.h
@@ -22,6 +22,10 @@
#pragma once
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct DRWShadingGroup;
struct FluidModifierData;
struct GPUMaterial;
@@ -225,3 +229,7 @@ struct DRW_Global {
struct GPUUniformBuf *view_ubo;
};
extern struct DRW_Global G_draw;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/intern/draw_debug.c b/source/blender/draw/intern/draw_debug.c
index e0114a6230e..d5183f4e368 100644
--- a/source/blender/draw/intern/draw_debug.c
+++ b/source/blender/draw/intern/draw_debug.c
@@ -122,6 +122,17 @@ void DRW_debug_m4_as_bbox(const float m[4][4], const float color[4], const bool
DRW_debug_bbox(&bb, color);
}
+void DRW_debug_view(const DRWView *view, const float color[4])
+{
+ float persinv[4][4], viewinv[4][4];
+ DRW_view_persmat_get(view, persinv, true);
+ DRW_view_viewmat_get(view, viewinv, true);
+
+ DRW_debug_modelmat_reset();
+ DRW_debug_m4_as_bbox(persinv, color, false);
+ DRW_debug_m4(viewinv);
+}
+
void DRW_debug_sphere(const float center[3], const float radius, const float color[4])
{
float size_mat[4][4];
@@ -136,6 +147,44 @@ void DRW_debug_sphere(const float center[3], const float radius, const float col
BLI_LINKS_PREPEND(DST.debug.spheres, sphere);
}
+/* --------- Indirect Rendering --------- */
+
+/* Keep in sync with shader. */
+#define DEBUG_VERT_MAX 16 * 4096
+
+static GPUVertFormat *debug_buf_format(void)
+{
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ GPU_vertformat_attr_add(&format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ }
+ return &format;
+}
+
+GPUVertBuf *drw_debug_line_buffer_get()
+{
+ DRWDebugBuffer *buf = MEM_mallocN(sizeof(DRWDebugBuffer), "DRWDebugBuffer");
+
+ buf->verts = GPU_vertbuf_create_with_format(debug_buf_format());
+ GPU_vertbuf_data_alloc(buf->verts, DEBUG_VERT_MAX);
+ uint(*data)[4] = GPU_vertbuf_get_data(buf->verts);
+ /* Set vertex count to 1 to skip the first 2 degenerate verts.
+ * This is because the first one is already being aliased in the shader definition. */
+ data[0][3] = 1;
+ /* Fill positions to NaN to avoid rendering unused verts. */
+ /* TODO(fclem): This could be done on GPU if that becomes a bottleneck. */
+ float(*fdata)[4] = (float(*)[4])data;
+ for (int v = 0; v < DEBUG_VERT_MAX; v++) {
+ for (int i = 0; i < 3; i++) {
+ fdata[v][i] = 0.0f / 0.0f;
+ }
+ }
+ BLI_LINKS_PREPEND(DST.debug.line_buffers, buf);
+
+ return buf->verts;
+}
+
/* --------- Render --------- */
static void drw_debug_draw_lines(void)
@@ -217,10 +266,38 @@ static void drw_debug_draw_spheres(void)
GPU_batch_discard(draw_batch);
}
+static void drw_debug_draw_buffers(void)
+{
+ int count = BLI_linklist_count((LinkNode *)DST.debug.line_buffers);
+
+ if (count == 0) {
+ return;
+ }
+
+ while (DST.debug.line_buffers) {
+ void *next = DST.debug.line_buffers->next;
+
+ GPUBatch *batch = GPU_batch_create_ex(
+ GPU_PRIM_LINES, DST.debug.line_buffers->verts, NULL, GPU_BATCH_OWNS_VBO);
+ GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_FLAT_COLOR);
+
+ float persmat[4][4];
+ DRW_view_persmat_get(NULL, persmat, false);
+ GPU_batch_uniform_mat4(batch, "ViewProjectionMatrix", persmat);
+
+ GPU_batch_draw(batch);
+ GPU_batch_discard(batch);
+
+ MEM_freeN(DST.debug.line_buffers);
+ DST.debug.line_buffers = next;
+ }
+}
+
void drw_debug_draw(void)
{
drw_debug_draw_lines();
drw_debug_draw_spheres();
+ drw_debug_draw_buffers();
}
void drw_debug_init(void)
diff --git a/source/blender/draw/intern/draw_debug.h b/source/blender/draw/intern/draw_debug.h
index 2616bc0af97..3f3cfe91652 100644
--- a/source/blender/draw/intern/draw_debug.h
+++ b/source/blender/draw/intern/draw_debug.h
@@ -22,6 +22,11 @@
#pragma once
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct DRWView;
struct BoundBox;
void DRW_debug_modelmat_reset(void);
@@ -34,5 +39,10 @@ void DRW_debug_polygon_v3(const float (*v)[3], int vert_len, const float color[4
*/
void DRW_debug_m4(const float m[4][4]);
void DRW_debug_m4_as_bbox(const float m[4][4], const float color[4], bool invert);
-void DRW_debug_bbox(const BoundBox *bbox, const float color[4]);
+void DRW_debug_view(const struct DRWView *view, const float color[4]);
+void DRW_debug_bbox(const struct BoundBox *bbox, const float color[4]);
void DRW_debug_sphere(const float center[3], float radius, const float color[4]);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h
index d27eb8be9e0..929fe9e2e1d 100644
--- a/source/blender/draw/intern/draw_manager.h
+++ b/source/blender/draw/intern/draw_manager.h
@@ -205,8 +205,10 @@ typedef enum {
/* Compute Commands. */
DRW_CMD_COMPUTE = 8,
+ DRW_CMD_COMPUTE_REF = 9,
/* Other Commands */
+ DRW_CMD_BARRIER = 11,
DRW_CMD_CLEAR = 12,
DRW_CMD_DRWSTATE = 13,
DRW_CMD_STENCIL = 14,
@@ -249,6 +251,14 @@ typedef struct DRWCommandCompute {
int groups_z_len;
} DRWCommandCompute;
+typedef struct DRWCommandComputeRef {
+ int *groups_ref;
+} DRWCommandComputeRef;
+
+typedef struct DRWCommandBarrier {
+ eGPUBarrier type;
+} DRWCommandBarrier;
+
typedef struct DRWCommandDrawProcedural {
GPUBatch *batch;
DRWResourceHandle handle;
@@ -286,6 +296,8 @@ typedef union DRWCommand {
DRWCommandDrawInstanceRange instance_range;
DRWCommandDrawProcedural procedural;
DRWCommandCompute compute;
+ DRWCommandComputeRef compute_ref;
+ DRWCommandBarrier barrier;
DRWCommandSetMutableState state;
DRWCommandSetStencil stencil;
DRWCommandSetSelectID select_id;
@@ -314,6 +326,7 @@ typedef enum {
DRW_UNIFORM_BLOCK_REF,
DRW_UNIFORM_TFEEDBACK_TARGET,
DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE,
+ DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF,
/** Per drawcall uniforms/UBO */
DRW_UNIFORM_BLOCK_OBMATS,
DRW_UNIFORM_BLOCK_OBINFOS,
@@ -345,6 +358,11 @@ struct DRWUniform {
GPUUniformBuf *block;
GPUUniformBuf **block_ref;
};
+ /* DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE */
+ union {
+ GPUVertBuf *vertbuf;
+ GPUVertBuf **vertbuf_ref;
+ };
/* DRW_UNIFORM_FLOAT_COPY */
float fvalue[4];
/* DRW_UNIFORM_INT_COPY */
@@ -422,6 +440,13 @@ typedef struct DRWViewUboStorage {
float viewvecs[2][4];
/* Should not be here. Not view dependent (only main view). */
float viewcamtexcofac[4];
+ float viewport_size[2];
+ float viewport_size_inv[2];
+
+ /** Frustum culling data. */
+ /** NOTE: vec3 arrays are paded to vec4. */
+ float frustum_corners[8][4];
+ float frustum_planes[6][4];
} DRWViewUboStorage;
BLI_STATIC_ASSERT_ALIGN(DRWViewUboStorage, 16)
@@ -506,6 +531,11 @@ typedef struct DRWDebugSphere {
float color[4];
} DRWDebugSphere;
+typedef struct DRWDebugBuffer {
+ struct DRWDebugBuffer *next; /* linked list */
+ struct GPUVertBuf *verts;
+} DRWDebugBuffer;
+
/* ------------- Memory Pools ------------ */
/* Contains memory pools information */
@@ -643,6 +673,7 @@ typedef struct DRWManager {
/* TODO(fclem): optimize: use chunks. */
DRWDebugLine *lines;
DRWDebugSphere *spheres;
+ DRWDebugBuffer *line_buffers;
} debug;
} DRWManager;
@@ -656,6 +687,7 @@ void *drw_viewport_engine_data_ensure(void *engine_type);
void drw_state_set(DRWState state);
+GPUVertBuf *drw_debug_line_buffer_get(void);
void drw_debug_draw(void);
void drw_debug_init(void);
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index ab570667a77..0a41e1a27c3 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -32,6 +32,7 @@
#include "BKE_pbvh.h"
#include "DNA_curve_types.h"
+#include "DNA_gpencil_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meta_types.h"
@@ -292,7 +293,9 @@ void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup,
drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_BLOCK, ubo, 0, 0, 1);
}
-void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup, const char *name, GPUUniformBuf **ubo)
+void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup,
+ const char *name,
+ const GPUUniformBuf **ubo)
{
BLI_assert(ubo != NULL);
int loc = GPU_shader_get_uniform_block_binding(shgroup->shader, name);
@@ -460,6 +463,19 @@ void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup,
shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, vertex_buffer, 0, 0, 1);
}
+void DRW_shgroup_vertex_buffer_ref(DRWShadingGroup *shgroup,
+ const char *name,
+ GPUVertBuf **vertex_buffer)
+{
+ int location = GPU_shader_get_ssbo(shgroup->shader, name);
+ if (location == -1) {
+ BLI_assert_msg(0, "Unable to locate binding of shader storage buffer objects.");
+ return;
+ }
+ drw_shgroup_uniform_create_ex(
+ shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF, vertex_buffer, 0, 0, 1);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -489,6 +505,19 @@ static void drw_call_calc_orco(Object *ob, float (*r_orcofacs)[4])
texcosize = mb->size;
break;
}
+ case ID_GD: {
+ /* TODO(fclem) Put in the object eval somehow. */
+ /* Note this is used for shading to get the average normal.
+ * A user modified texture space would not have this bounding property. */
+ BoundBox *bbox = BKE_object_boundbox_get(ob);
+ /* TODO(fclem) Do real orco. */
+ copy_v3_fl(r_orcofacs[0], 0.0f);
+ BKE_boundbox_calc_size_aabb(bbox, r_orcofacs[1]);
+ /* Avoid division by 0. */
+ add_v3_fl(r_orcofacs[1], 1e-8f);
+ invert_v3(r_orcofacs[1]);
+ return;
+ }
default:
break;
}
@@ -528,7 +557,7 @@ static void drw_call_obinfos_init(DRWObjectInfos *ob_infos, Object *ob)
drw_call_calc_orco(ob, ob_infos->orcotexfac);
/* Random float value. */
uint random = (DST.dupli_source) ?
- DST.dupli_source->random_id :
+ DST.dupli_source->random_id :
/* TODO(fclem): this is rather costly to do at runtime. Maybe we can
* put it in ob->runtime and make depsgraph ensure it is up to date. */
BLI_hash_int_2d(BLI_hash_string(ob->id.name + 2), 0);
@@ -548,6 +577,15 @@ static void drw_call_obinfos_init(DRWObjectInfos *ob_infos, Object *ob)
ob_infos->ob_flag *= (ob->transflag & OB_NEG_SCALE) ? -1.0f : 1.0f;
/* Object Color. */
copy_v4_v4(ob_infos->ob_color, ob->color);
+ /* Grease Pencil object parameters. */
+ if (ob->type == OB_GPENCIL) {
+ bGPdata *gpdata = ob->data;
+ float ob_scale = mat4_to_scale(ob->obmat);
+ ob_infos->orcotexfac[0][3] = (gpdata->flag & GP_DATA_STROKE_KEEPTHICKNESS) ?
+ -1.0 :
+ (gpdata->pixfactor / GPENCIL_PIXEL_FACTOR);
+ ob_infos->orcotexfac[1][3] = (gpdata->draw_mode == GP_DRAWMODE_2D) ? -ob_scale : ob_scale;
+ }
}
static void drw_call_culling_init(DRWCullingState *cull, Object *ob)
@@ -730,6 +768,18 @@ static void drw_command_compute(DRWShadingGroup *shgroup,
cmd->groups_z_len = groups_z_len;
}
+static void drw_command_compute_ref(DRWShadingGroup *shgroup, int groups_ref[3])
+{
+ DRWCommandComputeRef *cmd = drw_command_create(shgroup, DRW_CMD_COMPUTE_REF);
+ cmd->groups_ref = groups_ref;
+}
+
+static void drw_command_barrier(DRWShadingGroup *shgroup, eGPUBarrier type)
+{
+ DRWCommandBarrier *cmd = drw_command_create(shgroup, DRW_CMD_BARRIER);
+ cmd->type = type;
+}
+
static void drw_command_draw_procedural(DRWShadingGroup *shgroup,
GPUBatch *batch,
DRWResourceHandle handle,
@@ -855,6 +905,20 @@ void DRW_shgroup_call_compute(DRWShadingGroup *shgroup,
drw_command_compute(shgroup, groups_x_len, groups_y_len, groups_z_len);
}
+void DRW_shgroup_call_compute_ref(DRWShadingGroup *shgroup, int groups_ref[3])
+{
+ BLI_assert(GPU_compute_shader_support());
+
+ drw_command_compute_ref(shgroup, groups_ref);
+}
+
+void DRW_shgroup_barrier(DRWShadingGroup *shgroup, eGPUBarrier type)
+{
+ BLI_assert(GPU_compute_shader_support());
+
+ drw_command_barrier(shgroup, type);
+}
+
static void drw_shgroup_call_procedural_add_ex(DRWShadingGroup *shgroup,
GPUBatch *geom,
Object *ob,
@@ -1124,6 +1188,33 @@ void DRW_shgroup_call_sculpt_with_materials(DRWShadingGroup **shgroups,
static GPUVertFormat inst_select_format = {0};
+DRWCallBuffer *DRW_call_buffer_create(struct GPUVertFormat *format)
+{
+ BLI_assert(format != NULL);
+
+ DRWCallBuffer *callbuf = BLI_memblock_alloc(DST.vmempool->callbuffers);
+ callbuf->buf = DRW_temp_buffer_request(DST.vmempool->idatalist, format, &callbuf->count);
+ callbuf->buf_select = NULL;
+ callbuf->count = 0;
+
+ if (G.f & G_FLAG_PICKSEL) {
+ /* Not actually used for rendering but alloced in one chunk. */
+ if (inst_select_format.attr_len == 0) {
+ GPU_vertformat_attr_add(&inst_select_format, "selectId", GPU_COMP_I32, 1, GPU_FETCH_INT);
+ }
+ callbuf->buf_select = DRW_temp_buffer_request(
+ DST.vmempool->idatalist, &inst_select_format, &callbuf->count);
+ }
+
+ return callbuf;
+}
+
+GPUVertBuf *DRW_call_buffer_as_vertbuf(DRWCallBuffer *callbuf)
+{
+ GPU_vertbuf_data_len_set(callbuf->buf, callbuf->count);
+ return callbuf->buf;
+}
+
DRWCallBuffer *DRW_shgroup_call_buffer(DRWShadingGroup *shgroup,
struct GPUVertFormat *format,
GPUPrimType prim_type)
@@ -1292,6 +1383,15 @@ static void drw_shgroup_init(DRWShadingGroup *shgroup, GPUShader *shader)
}
}
+#ifdef DEBUG
+ int debugbuf_location = GPU_shader_get_builtin_ssbo(shader, GPU_BUFFER_BLOCK_DEBUG);
+ if (debugbuf_location != -1) {
+ GPUVertBuf *vertbuf = drw_debug_line_buffer_get();
+ drw_shgroup_uniform_create_ex(
+ shgroup, debugbuf_location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, vertbuf, 0, 0, 1);
+ }
+#endif
+
if (info_ubo_location != -1) {
drw_shgroup_uniform_create_ex(
shgroup, info_ubo_location, DRW_UNIFORM_BLOCK_OBINFOS, NULL, 0, 0, 1);
@@ -1883,6 +1983,14 @@ void DRW_view_update(DRWView *view,
draw_frustum_bound_sphere_calc(
&view->frustum_corners, viewinv, winmat, wininv, &view->frustum_bsphere);
+ /* TODO(fclem): Deduplicate. */
+ for (int i = 0; i < 8; i++) {
+ copy_v3_v3(view->storage.frustum_corners[i], view->frustum_corners.vec[i]);
+ }
+ for (int i = 0; i < 6; i++) {
+ copy_v4_v4(view->storage.frustum_planes[i], view->frustum_planes[i]);
+ }
+
#ifdef DRW_DEBUG_CULLING
if (G.debug_value != 0) {
DRW_debug_sphere(
@@ -1919,11 +2027,16 @@ void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len)
}
}
-void DRW_view_camtexco_set(DRWView *view, float texco[4])
+void DRW_view_camtexco_set(DRWView *view, const float texco[4])
{
copy_v4_v4(view->storage.viewcamtexcofac, texco);
}
+void DRW_view_camtexco_get(const DRWView *view, float r_texco[4])
+{
+ copy_v4_v4(r_texco, view->storage.viewcamtexcofac);
+}
+
void DRW_view_frustum_corners_get(const DRWView *view, BoundBox *corners)
{
memcpy(corners, &view->frustum_corners, sizeof(view->frustum_corners));
@@ -1934,6 +2047,12 @@ void DRW_view_frustum_planes_get(const DRWView *view, float planes[6][4])
memcpy(planes, &view->frustum_planes, sizeof(view->frustum_planes));
}
+/* Return world space frustum bounding sphere. */
+BoundSphere DRW_view_frustum_bsphere_get(const DRWView *view)
+{
+ return view->frustum_bsphere;
+}
+
bool DRW_view_is_persp_get(const DRWView *view)
{
view = (view) ? view : DST.view_default;
diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c
index 8dd24c01337..070b6d2eae8 100644
--- a/source/blender/draw/intern/draw_manager_exec.c
+++ b/source/blender/draw/intern/draw_manager_exec.c
@@ -356,7 +356,8 @@ static bool draw_call_is_culled(const DRWResourceHandle *handle, DRWView *view)
void DRW_view_set_active(DRWView *view)
{
- DST.view_active = (view) ? view : DST.view_default;
+ /* TODO(fclem) DST.view_active should be const too. */
+ DST.view_active = (view) ? (DRWView *)view : DST.view_default;
}
const DRWView *DRW_view_get_active(void)
@@ -662,8 +663,11 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup,
*use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader,
((GPUVertBuf *)uni->pvalue));
break;
+ case DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF:
+ GPU_vertbuf_bind_as_ssbo(*uni->vertbuf_ref, uni->location);
+ break;
case DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE:
- GPU_vertbuf_bind_as_ssbo((GPUVertBuf *)uni->pvalue, uni->location);
+ GPU_vertbuf_bind_as_ssbo(uni->vertbuf, uni->location);
break;
/* Legacy/Fallback support. */
case DRW_UNIFORM_BASE_INSTANCE:
@@ -1049,6 +1053,15 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
cmd->compute.groups_y_len,
cmd->compute.groups_z_len);
break;
+ case DRW_CMD_COMPUTE_REF:
+ GPU_compute_dispatch(shgroup->shader,
+ cmd->compute_ref.groups_ref[0],
+ cmd->compute_ref.groups_ref[1],
+ cmd->compute_ref.groups_ref[2]);
+ break;
+ case DRW_CMD_BARRIER:
+ GPU_memory_barrier(cmd->barrier.type);
+ break;
}
}
@@ -1060,8 +1073,13 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
}
}
-static void drw_update_view(void)
+static void drw_update_view(const float viewport_size[2])
{
+ DRWViewUboStorage *storage = &DST.view_active->storage;
+ copy_v2_v2(storage->viewport_size, viewport_size);
+ copy_v2_v2(storage->viewport_size_inv, viewport_size);
+ invert_v2(storage->viewport_size_inv);
+
/* TODO(fclem): update a big UBO and only bind ranges here. */
GPU_uniformbuf_update(G_draw.view_ubo, &DST.view_active->storage);
@@ -1089,8 +1107,11 @@ static void drw_draw_pass_ex(DRWPass *pass,
BLI_assert(DST.buffer_finish_called &&
"DRW_render_instance_buffer_finish had not been called before drawing");
- if (DST.view_previous != DST.view_active || DST.view_active->is_dirty) {
- drw_update_view();
+ float viewport[4];
+ GPU_viewport_size_get_f(viewport);
+ if (DST.view_previous != DST.view_active || DST.view_active->is_dirty ||
+ !equals_v2v2(DST.view_active->storage.viewport_size, &viewport[2])) {
+ drw_update_view(&viewport[2]);
DST.view_active->is_dirty = false;
DST.view_previous = DST.view_active;
}
diff --git a/source/blender/draw/intern/draw_manager_profiling.h b/source/blender/draw/intern/draw_manager_profiling.h
index e7c84491bdb..5626ac52049 100644
--- a/source/blender/draw/intern/draw_manager_profiling.h
+++ b/source/blender/draw/intern/draw_manager_profiling.h
@@ -22,6 +22,10 @@
#pragma once
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct rcti;
void DRW_stats_free(void);
@@ -42,3 +46,7 @@ void DRW_stats_query_start(const char *name);
void DRW_stats_query_end(void);
void DRW_stats_draw(const rcti *rect);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c
index 84440a8effe..ca8284af691 100644
--- a/source/blender/draw/intern/draw_manager_shader.c
+++ b/source/blender/draw/intern/draw_manager_shader.c
@@ -426,119 +426,81 @@ GPUShader *DRW_shader_create_fullscreen_with_shaderlib_ex(const char *frag,
return sh;
}
-GPUMaterial *DRW_shader_find_from_world(World *wo,
- const void *engine_type,
- const int options,
- bool deferred)
+GPUMaterial *DRW_shader_from_world(World *wo,
+ struct bNodeTree *ntree,
+ const uint64_t shader_id,
+ const bool is_volume_shader,
+ bool deferred,
+ GPUCodegenCallbackFn callback,
+ void *thunk)
{
- GPUMaterial *mat = GPU_material_from_nodetree_find(&wo->gpumaterial, engine_type, options);
- if (DRW_state_is_image_render() || !deferred) {
- if (mat != NULL && GPU_material_status(mat) == GPU_MAT_QUEUED) {
- /* XXX Hack : we return NULL so that the engine will call DRW_shader_create_from_XXX
- * with the shader code and we will resume the compilation from there. */
- return NULL;
- }
- }
- return mat;
-}
-
-GPUMaterial *DRW_shader_find_from_material(Material *ma,
- const void *engine_type,
- const int options,
- bool deferred)
-{
- GPUMaterial *mat = GPU_material_from_nodetree_find(&ma->gpumaterial, engine_type, options);
- if (DRW_state_is_image_render() || !deferred) {
- if (mat != NULL && GPU_material_status(mat) == GPU_MAT_QUEUED) {
- /* XXX Hack : we return NULL so that the engine will call DRW_shader_create_from_XXX
- * with the shader code and we will resume the compilation from there. */
- return NULL;
- }
- }
- return mat;
-}
-
-GPUMaterial *DRW_shader_create_from_world(struct Scene *scene,
- World *wo,
- struct bNodeTree *ntree,
- const void *engine_type,
- const int options,
- const bool is_volume_shader,
- const char *vert,
- const char *geom,
- const char *frag_lib,
- const char *defines,
- bool deferred,
- GPUMaterialEvalCallbackFn callback)
-{
- GPUMaterial *mat = NULL;
- if (DRW_state_is_image_render() || !deferred) {
- mat = GPU_material_from_nodetree_find(&wo->gpumaterial, engine_type, options);
- }
-
- if (mat == NULL) {
- scene = (Scene *)DEG_get_original_id(&DST.draw_ctx.scene->id);
- mat = GPU_material_from_nodetree(scene,
- NULL,
- ntree,
- &wo->gpumaterial,
- engine_type,
- options,
- is_volume_shader,
- vert,
- geom,
- frag_lib,
- defines,
- wo->id.name,
- callback);
+ Scene *scene = (Scene *)DEG_get_original_id(&DST.draw_ctx.scene->id);
+ GPUMaterial *mat = GPU_material_from_nodetree(scene,
+ NULL,
+ ntree,
+ &wo->gpumaterial,
+ wo->id.name,
+ shader_id,
+ is_volume_shader,
+ false,
+ callback,
+ thunk);
+ if (!DRW_state_is_image_render() && deferred && GPU_material_status(mat) == GPU_MAT_QUEUED) {
+ /* Shader has been already queued. */
+ return mat;
}
- if (GPU_material_status(mat) == GPU_MAT_QUEUED) {
+ if (GPU_material_status(mat) == GPU_MAT_CREATED) {
+ GPU_material_status_set(mat, GPU_MAT_QUEUED);
drw_deferred_shader_add(mat, deferred);
}
+ if (!deferred && GPU_material_status(mat) == GPU_MAT_QUEUED) {
+ /* Force compilation for shaders already queued. */
+ drw_deferred_shader_add(mat, false);
+ }
return mat;
}
-GPUMaterial *DRW_shader_create_from_material(struct Scene *scene,
- Material *ma,
- struct bNodeTree *ntree,
- const void *engine_type,
- const int options,
- const bool is_volume_shader,
- const char *vert,
- const char *geom,
- const char *frag_lib,
- const char *defines,
- bool deferred,
- GPUMaterialEvalCallbackFn callback)
+GPUMaterial *DRW_shader_from_material(Material *ma,
+ struct bNodeTree *ntree,
+ const uint64_t shader_id,
+ const bool is_volume_shader,
+ bool deferred,
+ GPUCodegenCallbackFn callback,
+ void *thunk)
{
- GPUMaterial *mat = NULL;
- if (DRW_state_is_image_render() || !deferred) {
- mat = GPU_material_from_nodetree_find(&ma->gpumaterial, engine_type, options);
+ Scene *scene = (Scene *)DEG_get_original_id(&DST.draw_ctx.scene->id);
+ GPUMaterial *mat = GPU_material_from_nodetree(scene,
+ ma,
+ ntree,
+ &ma->gpumaterial,
+ ma->id.name,
+ shader_id,
+ is_volume_shader,
+ false,
+ callback,
+ thunk);
+
+ if (DRW_state_is_image_render()) {
+ /* Do not deferred if doing render. */
+ deferred = false;
}
- if (mat == NULL) {
- scene = (Scene *)DEG_get_original_id(&DST.draw_ctx.scene->id);
- mat = GPU_material_from_nodetree(scene,
- ma,
- ntree,
- &ma->gpumaterial,
- engine_type,
- options,
- is_volume_shader,
- vert,
- geom,
- frag_lib,
- defines,
- ma->id.name,
- callback);
+ if (deferred && GPU_material_status(mat) == GPU_MAT_QUEUED) {
+ /* Shader has been already queued. */
+ return mat;
}
- if (GPU_material_status(mat) == GPU_MAT_QUEUED) {
+ if (GPU_material_status(mat) == GPU_MAT_CREATED) {
+ GPU_material_status_set(mat, GPU_MAT_QUEUED);
drw_deferred_shader_add(mat, deferred);
}
+ if (!deferred && GPU_material_status(mat) == GPU_MAT_QUEUED) {
+ /* Force compilation for shaders already queued. */
+ drw_deferred_shader_add(mat, false);
+ }
return mat;
}
@@ -561,15 +523,15 @@ void DRW_shader_free(GPUShader *shader)
* contains the needed libraries for this shader.
* \{ */
-/* 32 because we use a 32bit bitmap. */
-#define MAX_LIB 32
+/* 64 because we use a 64bit bitmap. */
+#define MAX_LIB 64
#define MAX_LIB_NAME 64
#define MAX_LIB_DEPS 8
struct DRWShaderLibrary {
- char *libs[MAX_LIB];
+ const char *libs[MAX_LIB];
char libs_name[MAX_LIB][MAX_LIB_NAME];
- uint32_t libs_deps[MAX_LIB];
+ uint64_t libs_deps[MAX_LIB];
};
DRWShaderLibrary *DRW_shader_library_create(void)
@@ -598,38 +560,43 @@ static int drw_shader_library_search(const DRWShaderLibrary *lib, const char *na
}
/* Return bitmap of dependencies. */
-static uint32_t drw_shader_dependencies_get(const DRWShaderLibrary *lib, const char *lib_code)
+static uint64_t drw_shader_dependencies_get(const DRWShaderLibrary *lib,
+ const char *pragma_str,
+ const char *lib_code,
+ const char *lib_name)
{
/* Search dependencies. */
- uint32_t deps = 0;
+ uint pragma_len = strlen(pragma_str);
+ uint64_t deps = 0;
const char *haystack = lib_code;
- while ((haystack = strstr(haystack, "BLENDER_REQUIRE("))) {
- haystack += 16;
+ while ((haystack = strstr(haystack, pragma_str))) {
+ haystack += pragma_len;
int dep = drw_shader_library_search(lib, haystack);
if (dep == -1) {
- char dbg_name[33];
+ char dbg_name[MAX_NAME];
int i = 0;
while ((*haystack != ')') && (i < (sizeof(dbg_name) - 2))) {
dbg_name[i] = *haystack;
haystack++;
i++;
}
- dbg_name[i + 1] = '\0';
+ dbg_name[i] = '\0';
printf(
- "Error: Dependency not found: %s\n"
+ "Error: Dependency %s not found for %s.\n"
"This might be due to bad lib ordering.\n",
- dbg_name);
+ dbg_name,
+ lib_name);
BLI_assert(0);
}
else {
- deps |= 1u << (uint32_t)dep;
+ deps |= 1llu << ((uint64_t)dep);
}
}
return deps;
}
-void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const char *lib_name)
+void DRW_shader_library_add_file(DRWShaderLibrary *lib, const char *lib_code, const char *lib_name)
{
int index = -1;
for (int i = 0; i < MAX_LIB; i++) {
@@ -642,7 +609,8 @@ void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const ch
if (index > -1) {
lib->libs[index] = lib_code;
BLI_strncpy(lib->libs_name[index], lib_name, MAX_LIB_NAME);
- lib->libs_deps[index] = drw_shader_dependencies_get(lib, lib_code);
+ lib->libs_deps[index] = drw_shader_dependencies_get(
+ lib, "BLENDER_REQUIRE(", lib_code, lib_name);
}
else {
printf("Error: Too many libraries. Cannot add %s.\n", lib_name);
@@ -652,25 +620,34 @@ void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const ch
char *DRW_shader_library_create_shader_string(const DRWShaderLibrary *lib, const char *shader_code)
{
- uint32_t deps = drw_shader_dependencies_get(lib, shader_code);
+ /* TODO(fclem) Could be done in one pass. */
+ uint64_t deps = drw_shader_dependencies_get(lib, "BLENDER_REQUIRE(", shader_code, "shader code");
+ uint64_t deps_post = drw_shader_dependencies_get(
+ lib, "BLENDER_REQUIRE_POST(", shader_code, "shader code");
DynStr *ds = BLI_dynstr_new();
/* Add all dependencies recursively. */
for (int i = MAX_LIB - 1; i > -1; i--) {
- if (lib->libs[i] && (deps & (1u << (uint32_t)i))) {
+ if (lib->libs[i] && ((deps & (1llu << (uint64_t)i)) || (deps_post & (1llu << (uint64_t)i)))) {
deps |= lib->libs_deps[i];
}
}
/* Concatenate all needed libs into one string. */
- for (int i = 0; i < MAX_LIB; i++) {
- if (deps & 1u) {
+ for (int i = 0; i < MAX_LIB && deps != 0llu; i++, deps >>= 1llu) {
+ if (deps & 1llu) {
BLI_dynstr_append(ds, lib->libs[i]);
}
- deps = deps >> 1;
}
BLI_dynstr_append(ds, shader_code);
+ /* Concatenate all needed libs into one string. */
+ for (int i = 0; i < MAX_LIB && deps_post != 0llu; i++, deps_post >>= 1llu) {
+ if (deps_post & 1llu) {
+ BLI_dynstr_append(ds, lib->libs[i]);
+ }
+ }
+
char *str = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
diff --git a/source/blender/draw/intern/draw_manager_texture.c b/source/blender/draw/intern/draw_manager_texture.c
index 86242468e4a..5062c9c970e 100644
--- a/source/blender/draw/intern/draw_manager_texture.c
+++ b/source/blender/draw/intern/draw_manager_texture.c
@@ -43,13 +43,20 @@ static bool drw_texture_format_supports_framebuffer(eGPUTextureFormat format)
case GPU_RG16:
case GPU_RG16F:
case GPU_RG16I:
+ case GPU_RG16UI:
case GPU_RG32F:
+ case GPU_RG32I:
+ case GPU_RG32UI:
case GPU_RGB10_A2:
case GPU_R11F_G11F_B10F:
case GPU_RGBA8:
case GPU_RGBA16:
case GPU_RGBA16F:
+ case GPU_RGBA16I:
+ case GPU_RGBA16UI:
case GPU_RGBA32F:
+ case GPU_RGBA32I:
+ case GPU_RGBA32UI:
case GPU_DEPTH_COMPONENT16:
case GPU_DEPTH_COMPONENT24:
case GPU_DEPTH24_STENCIL8:
diff --git a/source/blender/draw/intern/shaders/common_attribute_lib.glsl b/source/blender/draw/intern/shaders/common_attribute_lib.glsl
new file mode 100644
index 00000000000..1b693c2b01d
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_attribute_lib.glsl
@@ -0,0 +1,19 @@
+
+/* Prototype of functions to implement to load attributes data.
+ * Implementation changes based on object data type. */
+
+vec3 attr_load_orco(vec4 orco);
+vec4 attr_load_tangent(vec4 tangent);
+vec3 attr_load_uv(vec3 uv);
+vec4 attr_load_color(vec4 color);
+vec4 attr_load_vec4(vec4 attr);
+vec3 attr_load_vec3(vec3 attr);
+vec2 attr_load_vec2(vec2 attr);
+
+vec3 attr_load_orco(samplerBuffer orco);
+vec4 attr_load_tangent(samplerBuffer tangent);
+vec3 attr_load_uv(samplerBuffer uv);
+vec4 attr_load_color(samplerBuffer color);
+vec4 attr_load_vec4(samplerBuffer attr);
+vec3 attr_load_vec3(samplerBuffer attr);
+vec2 attr_load_vec2(samplerBuffer attr);
diff --git a/source/blender/draw/intern/shaders/common_debug_lib.glsl b/source/blender/draw/intern/shaders/common_debug_lib.glsl
new file mode 100644
index 00000000000..396bbb759ec
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_debug_lib.glsl
@@ -0,0 +1,131 @@
+
+/**
+ * Debugging drawing library
+ *
+ * Quick way to draw debug geometry. All input should be in world space and
+ * will be rendered in the default view. No additional setup required.
+ **/
+#define DEBUG_DRAW
+
+/* Keep in sync with buffer creation. */
+#define DEBUG_VERT_MAX 16 * 4096
+
+struct DebugVert {
+ vec3 pos;
+ uint color;
+};
+
+layout(std430, binding = 7) restrict buffer debugBuf
+{
+ /** Start the buffer with a degenerate vertice. */
+ uint _pad0;
+ uint _pad1;
+ uint _pad2;
+ uint v_count;
+ DebugVert verts[];
+}
+drw_debug_verts;
+
+bool drw_debug_draw_enable = true;
+
+uint drw_debug_color_pack(vec4 color)
+{
+ color = clamp(color, 0.0, 1.0);
+ uint result = 0;
+ result |= uint(color.x * 255.0) << 0u;
+ result |= uint(color.y * 255.0) << 8u;
+ result |= uint(color.z * 255.0) << 16u;
+ result |= uint(color.w * 255.0) << 24u;
+ return result;
+}
+
+void drw_debug_line_do(inout uint vertid, vec3 v1, vec3 v2, uint color)
+{
+ drw_debug_verts.verts[vertid++] = DebugVert(v1, color);
+ drw_debug_verts.verts[vertid++] = DebugVert(v2, color);
+}
+
+void drw_debug_line(vec3 v1, vec3 v2, vec4 color)
+{
+ if (!drw_debug_draw_enable) {
+ return;
+ }
+ const uint vneeded = 2;
+ uint vertid = atomicAdd(drw_debug_verts.v_count, vneeded);
+ if (vertid + vneeded < DEBUG_VERT_MAX + 1) {
+ drw_debug_line_do(vertid, v1, v2, drw_debug_color_pack(color));
+ }
+}
+
+void drw_debug_quad(vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec4 color)
+{
+ if (!drw_debug_draw_enable) {
+ return;
+ }
+ const uint vneeded = 8;
+ uint vertid = atomicAdd(drw_debug_verts.v_count, vneeded);
+ if (vertid + vneeded < DEBUG_VERT_MAX + 1) {
+ uint pcolor = drw_debug_color_pack(color);
+ drw_debug_line_do(vertid, v1, v2, pcolor);
+ drw_debug_line_do(vertid, v2, v3, pcolor);
+ drw_debug_line_do(vertid, v3, v4, pcolor);
+ drw_debug_line_do(vertid, v4, v1, pcolor);
+ }
+}
+
+/* Draw an octahedron. */
+void drw_debug_point(vec3 p, float radius, vec4 color)
+{
+ if (!drw_debug_draw_enable) {
+ return;
+ }
+ vec3 c = vec3(radius, -radius, 0);
+ vec3 v1 = p + c.xzz;
+ vec3 v2 = p + c.zxz;
+ vec3 v3 = p + c.yzz;
+ vec3 v4 = p + c.zyz;
+ vec3 v5 = p + c.zzx;
+ vec3 v6 = p + c.zzy;
+
+ const uint vneeded = 12 * 2;
+ uint vertid = atomicAdd(drw_debug_verts.v_count, vneeded);
+ if (vertid + vneeded < DEBUG_VERT_MAX + 1) {
+ uint pcolor = drw_debug_color_pack(color);
+ drw_debug_line_do(vertid, v1, v2, pcolor);
+ drw_debug_line_do(vertid, v2, v3, pcolor);
+ drw_debug_line_do(vertid, v3, v4, pcolor);
+ drw_debug_line_do(vertid, v4, v1, pcolor);
+ drw_debug_line_do(vertid, v1, v5, pcolor);
+ drw_debug_line_do(vertid, v2, v5, pcolor);
+ drw_debug_line_do(vertid, v3, v5, pcolor);
+ drw_debug_line_do(vertid, v4, v5, pcolor);
+ drw_debug_line_do(vertid, v1, v6, pcolor);
+ drw_debug_line_do(vertid, v2, v6, pcolor);
+ drw_debug_line_do(vertid, v3, v6, pcolor);
+ drw_debug_line_do(vertid, v4, v6, pcolor);
+ }
+}
+
+void drw_debug_matrix(mat4 mat, vec4 color, const bool do_project)
+{
+ vec4 p[8] = vec4[8](vec4(-1, -1, -1, 1),
+ vec4(1, -1, -1, 1),
+ vec4(1, 1, -1, 1),
+ vec4(-1, 1, -1, 1),
+ vec4(-1, -1, 1, 1),
+ vec4(1, -1, 1, 1),
+ vec4(1, 1, 1, 1),
+ vec4(-1, 1, 1, 1));
+ for (int i = 0; i < 8; i++) {
+ p[i] = mat * p[i];
+ if (do_project) {
+ p[i].xyz /= p[i].w;
+ }
+ }
+ drw_debug_quad(p[0].xyz, p[1].xyz, p[2].xyz, p[3].xyz, color);
+ drw_debug_line(p[0].xyz, p[4].xyz, color);
+ drw_debug_line(p[1].xyz, p[5].xyz, color);
+ drw_debug_line(p[2].xyz, p[6].xyz, color);
+ drw_debug_line(p[3].xyz, p[7].xyz, color);
+ drw_debug_quad(p[4].xyz, p[5].xyz, p[6].xyz, p[7].xyz, color);
+}
diff --git a/source/blender/draw/intern/shaders/common_gpencil_lib.glsl b/source/blender/draw/intern/shaders/common_gpencil_lib.glsl
new file mode 100644
index 00000000000..785c6948d8f
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_gpencil_lib.glsl
@@ -0,0 +1,309 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_obinfos_lib.glsl)
+
+#ifdef GPU_FRAGMENT_SHADER
+float gpencil_stroke_round_cap_mask(vec2 p1, vec2 p2, vec2 aspect, float thickness, float hardfac)
+{
+ /* We create our own uv space to avoid issues with triangulation and linear
+ * interpolation artifacts. */
+ vec2 line = p2.xy - p1.xy;
+ vec2 pos = gl_FragCoord.xy - p1.xy;
+ float line_len = length(line);
+ float half_line_len = line_len * 0.5;
+ /* Normalize */
+ line = (line_len > 0.0) ? (line / line_len) : vec2(1.0, 0.0);
+ /* Create a uv space that englobe the whole segment into a capsule. */
+ vec2 uv_end;
+ uv_end.x = max(abs(dot(line, pos) - half_line_len) - half_line_len, 0.0);
+ uv_end.y = dot(vec2(-line.y, line.x), pos);
+ /* Divide by stroke radius. */
+ uv_end /= thickness;
+ uv_end *= aspect;
+
+ float dist = clamp(1.0 - length(uv_end) * 2.0, 0.0, 1.0);
+ if (hardfac > 0.999) {
+ return step(1e-8, dist);
+ }
+ else {
+ /* Modulate the falloff profile */
+ float hardness = 1.0 - hardfac;
+ dist = pow(dist, mix(0.01, 10.0, hardness));
+ return smoothstep(0.0, 1.0, dist);
+ }
+}
+#endif
+
+vec2 gpencil_decode_aspect(int packed_data)
+{
+ float asp = float(uint(packed_data) & 0x1FFu) * (1.0 / 255.0);
+ return (asp > 1.0) ? vec2(1.0, (asp - 1.0)) : vec2(asp, 1.0);
+}
+
+float gpencil_decode_uvrot(int packed_data)
+{
+ uint udata = uint(packed_data);
+ float uvrot = 1e-8 + float((udata & 0x1FE00u) >> 9u) * (1.0 / 255.0);
+ return ((udata & 0x20000u) != 0u) ? -uvrot : uvrot;
+}
+
+float gpencil_decode_hardness(int packed_data)
+{
+ return float((uint(packed_data) & 0x3FC0000u) >> 18u) * (1.0 / 255.0);
+}
+
+vec2 gpencil_project_to_screenspace(vec4 v, vec4 viewport_size)
+{
+ return ((v.xy / v.w) * 0.5 + 0.5) * viewport_size.xy;
+}
+
+float gpencil_stroke_thickness_modulate(float thickness, vec4 ndc_pos, vec4 viewport_size)
+{
+ /* Modify stroke thickness by object and layer factors. */
+ /* TODO */
+ thickness = max(1.0, thickness * ObjectGpencilThickness /* + thicknessOffset */);
+
+ if (ObjectGpencilThicknessIsScreenSpace) {
+ /* Multiply offset by view Z so that offset is constant in screenspace.
+ * (e.i: does not change with the distance to camera) */
+ thickness *= ndc_pos.w;
+ }
+ else {
+ /* World space point size. */
+ thickness *= ObjectGpencilWorldScale * ProjectionMatrix[1][1] * viewport_size.y;
+ }
+ return thickness;
+}
+
+float gpencil_clamp_small_stroke_thickness(float thickness, vec4 ndc_pos)
+{
+ /* To avoid aliasing artifacts, we clamp the line thickness and
+ * reduce its opacity in the fragment shader.*/
+ float min_thickness = ndc_pos.w * 1.3;
+ thickness = max(min_thickness, thickness);
+
+ return thickness;
+}
+
+/**
+ * Returns value of gl_Position.
+ *
+ * To declare in vertex shader.
+ * in ivec4 ma, ma1, ma2, ma3;
+ * in vec4 pos, pos1, pos2, pos3, uv1, uv2, col1, col2, fcol1;
+ *
+ * All of these attributes are quad loaded the same way
+ * as GL_LINES_ADJACENCY would feed a geometry shader:
+ * - ma reference the previous adjacency point.
+ * - ma1 reference the current line first point.
+ * - ma2 reference the current line second point.
+ * - ma3 reference the next adjacency point.
+ * Note that we are rendering quad instances and not using any index buffer (except for fills).
+ *
+ * Material : x is material index, y is stroke_id, z is point_id,
+ * w is aspect & rotation & hardness packed.
+ * Position : contains thickness in 4th component.
+ * UV : xy is UV for fills, z is U of stroke, w is strength.
+ *
+ *
+ * WARNING: Max attribute count is actually 14 because OSX OpenGL implementation
+ * considers gl_VertexID and gl_InstanceID as vertex attribute. (see T74536)
+ **/
+vec4 gpencil_vertex(ivec4 ma,
+ ivec4 ma1,
+ ivec4 ma2,
+ ivec4 ma3,
+ vec4 pos,
+ vec4 pos1,
+ vec4 pos2,
+ vec4 pos3,
+ vec4 uv1,
+ vec4 uv2,
+ vec4 col1,
+ vec4 col2,
+ vec4 fcol1,
+ vec4 viewport_size,
+ out vec3 out_pos,
+ out vec3 out_nor,
+ out vec4 out_color,
+ out vec2 out_uv,
+ out vec4 out_sspos,
+ out vec2 out_aspect,
+ out vec2 out_thickness)
+{
+#define stroke_id1 ma1.y
+#define point_id1 ma1.z
+#define thickness1 pos1.w
+#define thickness2 pos2.w
+#define strength1 uv1.w
+#define strength2 uv2.w
+/* Packed! need to be decoded. */
+#define hardness1 ma1.w
+#define hardness2 ma2.w
+#define uvrot1 ma1.w
+#define aspect1 ma1.w
+
+ /* Trick to detect if a drawcall is stroke or fill.
+ * This does mean that we need to draw an empty stroke segment before starting
+ * to draw the real stroke segments. */
+ bool is_fill = (gl_InstanceID == 0);
+
+ vec4 out_ndc;
+
+ if (!is_fill) {
+ int m = ma1.x;
+ bool is_dot = false;
+ bool is_squares = false;
+
+ /* Special Case. Stroke with single vert are rendered as dots. Do not discard them. */
+ if (!is_dot && ma.x == -1 && ma2.x == -1) {
+ is_dot = true;
+ is_squares = false;
+ }
+
+ /* Endpoints, we discard the vertices. */
+ if (ma1.x == -1 || (!is_dot && ma2.x == -1)) {
+ /* We set the vertex at the camera origin to generate 0 fragments. */
+ out_ndc = vec4(0.0, 0.0, -3e36, 0.0);
+ return out_ndc;
+ }
+
+ /* Avoid using a vertex attribute for quad positioning. */
+ float x = float(gl_VertexID & 1) * 2.0 - 1.0; /* [-1..1] */
+ float y = float(gl_VertexID & 2) - 1.0; /* [-1..1] */
+
+ bool use_curr = is_dot || (x == -1.0);
+
+ vec3 wpos_adj = transform_point(ModelMatrix, (use_curr) ? pos.xyz : pos3.xyz);
+ vec3 wpos1 = transform_point(ModelMatrix, pos1.xyz);
+ vec3 wpos2 = transform_point(ModelMatrix, pos2.xyz);
+
+ vec3 T;
+ if (is_dot) {
+ /* Shade as facing billboards. */
+ T = ViewMatrixInverse[0].xyz;
+ }
+ else if (use_curr && ma.x != -1) {
+ T = wpos1 - wpos_adj;
+ }
+ else {
+ T = wpos2 - wpos1;
+ }
+ T = safe_normalize(T);
+
+ vec3 B = cross(T, ViewMatrixInverse[2].xyz);
+ out_nor = normalize(cross(B, T));
+
+ vec4 ndc_adj = point_world_to_ndc(wpos_adj);
+ vec4 ndc1 = point_world_to_ndc(wpos1);
+ vec4 ndc2 = point_world_to_ndc(wpos2);
+
+ out_ndc = (use_curr) ? ndc1 : ndc2;
+ out_pos = (use_curr) ? wpos1 : wpos2;
+
+ vec2 ss_adj = gpencil_project_to_screenspace(ndc_adj, viewport_size);
+ vec2 ss1 = gpencil_project_to_screenspace(ndc1, viewport_size);
+ vec2 ss2 = gpencil_project_to_screenspace(ndc2, viewport_size);
+ /* Screenspace Lines tangents. */
+ float line_len;
+ vec2 line = safe_normalize_len(ss2 - ss1, line_len);
+ vec2 line_adj = safe_normalize((use_curr) ? (ss1 - ss_adj) : (ss_adj - ss2));
+
+ float thickness = abs((use_curr) ? thickness1 : thickness2);
+ thickness = gpencil_stroke_thickness_modulate(thickness, out_ndc, viewport_size);
+ float clamped_thickness = gpencil_clamp_small_stroke_thickness(thickness, out_ndc);
+
+ out_uv = vec2(x, y) * 0.5 + 0.5;
+
+ if (is_dot) {
+ vec2 x_axis;
+ { /* GP_STROKE_ALIGNMENT_OBJECT */
+ vec4 ndc_x = point_world_to_ndc(wpos1 + ModelMatrix[0].xyz);
+ vec2 ss_x = gpencil_project_to_screenspace(ndc_x, viewport_size);
+ x_axis = safe_normalize(ss_x - ss1);
+ }
+
+ /* Rotation: Encoded as Cos + Sin sign. */
+ float uv_rot = gpencil_decode_uvrot(uvrot1);
+ float rot_sin = sqrt(max(0.0, 1.0 - uv_rot * uv_rot)) * sign(uv_rot);
+ float rot_cos = abs(uv_rot);
+ x_axis = mat2(rot_cos, -rot_sin, rot_sin, rot_cos) * x_axis;
+ /* Rotate 90° Counter-Clockwise. */
+ vec2 y_axis = vec2(-x_axis.y, x_axis.x);
+
+ out_aspect = gpencil_decode_aspect(aspect1);
+
+ x *= out_aspect.x;
+ y *= out_aspect.y;
+
+ /* Invert for vertex shader. */
+ out_aspect = 1.0 / out_aspect;
+
+ out_ndc.xy += (x * x_axis + y * y_axis) * viewport_size.zw * clamped_thickness;
+
+ out_sspos.xy = ss1;
+ out_sspos.zw = ss1 + x_axis * 0.5;
+ out_thickness.x = (is_squares) ? 1e18 : (clamped_thickness / out_ndc.w);
+ out_thickness.y = (is_squares) ? 1e18 : (thickness / out_ndc.w);
+ }
+ else {
+ bool is_stroke_start = (ma.x == -1 && x == -1);
+ bool is_stroke_end = (ma3.x == -1 && x == 1);
+
+ /* Mitter tangent vector. */
+ vec2 miter_tan = safe_normalize(line_adj + line);
+ float miter_dot = dot(miter_tan, line_adj);
+ /* Break corners after a certain angle to avoid really thick corners. */
+ const float miter_limit = 0.5; /* cos(60°) */
+ bool miter_break = (miter_dot < miter_limit);
+ miter_tan = (miter_break || is_stroke_start || is_stroke_end) ? line :
+ (miter_tan / miter_dot);
+ /* Rotate 90° Counter-Clockwise. */
+ vec2 miter = vec2(-miter_tan.y, miter_tan.x);
+
+ out_sspos.xy = ss1;
+ out_sspos.zw = ss2;
+ out_thickness.x = clamped_thickness / out_ndc.w;
+ out_thickness.y = thickness / out_ndc.w;
+ out_aspect = vec2(1.0);
+
+ vec2 screen_ofs = miter * y;
+
+ /* Reminder: we packed the cap flag into the sign of strength and thickness sign. */
+ if ((is_stroke_start && strength1 > 0.0) || (is_stroke_end && thickness1 > 0.0) ||
+ (miter_break && !is_stroke_start && !is_stroke_end)) {
+ screen_ofs += line * x;
+ }
+
+ out_ndc.xy += screen_ofs * viewport_size.zw * clamped_thickness;
+
+ out_uv.x = (use_curr) ? uv1.z : uv2.z;
+ }
+ }
+ else {
+ /* Fill vertex. */
+ out_pos = transform_point(ModelMatrix, pos1.xyz);
+ out_ndc = point_world_to_ndc(out_pos);
+ out_uv = uv1.xy;
+ out_thickness.x = 1e18;
+ out_thickness.y = 1e20;
+ out_aspect = vec2(1.0);
+ out_sspos = vec4(0.0);
+
+ /* Flat normal following camera and object bounds. */
+ vec3 V = cameraVec(ModelMatrix[3].xyz);
+ vec3 N = normal_world_to_object(V);
+ N *= OrcoTexCoFactors[1].xyz;
+ N = normal_object_to_world(N);
+ out_nor = safe_normalize(N);
+
+ /* Decode fill opacity. */
+ out_color = vec4(fcol1.rgb, floor(fcol1.a / 10.0));
+ out_color.a /= 10000.0;
+
+ /* We still offset the fills a little to avoid overlaps */
+ out_ndc.z += 0.000002;
+ }
+ return out_ndc;
+}
diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl
index ed8b8aeb849..25e4abc5ebc 100644
--- a/source/blender/draw/intern/shaders/common_hair_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl
@@ -162,6 +162,14 @@ float hair_shaperadius(float shape, float root, float tip, float time)
in float dummy;
# endif
+/* From http://libnoise.sourceforge.net/noisegen/index.html */
+float hair_integer_noise(int n)
+{
+ n = (n >> 13) ^ n;
+ int nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7FFFFFFF;
+ return (float(nn) / 1073741824.0);
+}
+
void hair_get_pos_tan_binor_time(bool is_persp,
mat4 invmodel_mat,
vec3 camera_pos,
@@ -213,6 +221,11 @@ void hair_get_pos_tan_binor_time(bool is_persp,
wpos += wbinor * thick_time * scale;
}
+ else {
+ /* Random hair time in V direction (binormal). Mimics cylinder shading. */
+ thick_time = hair_integer_noise(hair_get_strand_id());
+ thick_time = thickness * (thick_time * 2.0 - 1.0);
+ }
}
float hair_get_customdata_float(const samplerBuffer cd_buf)
diff --git a/source/blender/draw/intern/shaders/common_intersection_lib.glsl b/source/blender/draw/intern/shaders/common_intersection_lib.glsl
new file mode 100644
index 00000000000..c8a97808247
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_intersection_lib.glsl
@@ -0,0 +1,209 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+
+/** Require/include common_debug_lib.glsl before this file to debug draw intersections volumes. */
+
+#if defined(DEBUG_DRAW_ISECT) && !defined(DEBUG_DRAW)
+# error "You must include common_debug_lib.glsl before this file to enabled debug draw"
+#endif
+
+/* ---------------------------------------------------------------------- */
+/** \name Plane extraction functions.
+ * \{ */
+
+/** \a v1 and \a v2 are vectors on the plane. \a p is a point on the plane. */
+vec4 plane_setup(vec3 p, vec3 v1, vec3 v2)
+{
+ vec3 normal_to_plane = normalize(cross(v1, v2));
+ return vec4(normal_to_plane, -dot(normal_to_plane, p));
+}
+
+void planes_setup(Pyramid shape, out vec4 planes[5])
+{
+ vec3 A1 = shape.corners[1] - shape.corners[0];
+ vec3 A2 = shape.corners[2] - shape.corners[0];
+ vec3 A3 = shape.corners[3] - shape.corners[0];
+ vec3 A4 = shape.corners[4] - shape.corners[0];
+ vec3 S1 = shape.corners[4] - shape.corners[1];
+ vec3 S2 = shape.corners[2] - shape.corners[1];
+
+ planes[0] = plane_setup(shape.corners[0], A2, A1);
+ planes[1] = plane_setup(shape.corners[0], A3, A2);
+ planes[2] = plane_setup(shape.corners[0], A4, A3);
+ planes[3] = plane_setup(shape.corners[0], A1, A4);
+ planes[4] = plane_setup(shape.corners[1], S2, S1);
+}
+
+void planes_setup(Box shape, out vec4 planes[6])
+{
+ vec3 A1 = shape.corners[1] - shape.corners[0];
+ vec3 A3 = shape.corners[3] - shape.corners[0];
+ vec3 A4 = shape.corners[4] - shape.corners[0];
+
+ planes[0] = plane_setup(shape.corners[0], A1, A3);
+ planes[1] = plane_setup(shape.corners[0], A3, A4);
+ planes[2] = plane_setup(shape.corners[0], A4, A1);
+ planes[3] = vec4(-planes[0].xyz, -dot(-planes[0].xyz, shape.corners[6]));
+ planes[4] = vec4(-planes[1].xyz, -dot(-planes[1].xyz, shape.corners[6]));
+ planes[5] = vec4(-planes[2].xyz, -dot(-planes[2].xyz, shape.corners[6]));
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Intersection functions.
+ * \{ */
+
+#define TEST_ENABLED 1
+#define FALSE_POSITIVE_REJECTION 1
+
+bool intersect_view(Pyramid pyramid)
+{
+ /**
+ * Frustum vs. Pyramid test from
+ * https://www.yosoygames.com.ar/wp/2016/12/frustum-vs-pyramid-intersection-also-frustum-vs-frustum/
+ */
+ bool intersects = true;
+
+ /* Do Pyramid vertices vs Frustum planes. */
+ for (int p = 0; p < 6 && intersects; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 5 && !is_any_vertex_on_positive_side; ++v) {
+ if (dot(frustum_planes[p], vec4(pyramid.corners[v], 1.0)) > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ }
+ }
+ if (!is_any_vertex_on_positive_side) {
+ intersects = false;
+ }
+ }
+
+ if (intersects) {
+ vec4 pyramid_planes[5];
+ planes_setup(pyramid, pyramid_planes);
+ /* Now do Frustum vertices vs Pyramid planes. */
+ for (int p = 0; p < 5 && intersects; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 8 && !is_any_vertex_on_positive_side; ++v) {
+ if (dot(pyramid_planes[p], vec4(frustum_corners[v], 1.0)) > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ }
+ }
+ if (!is_any_vertex_on_positive_side) {
+ intersects = false;
+ }
+ }
+ }
+
+#if defined(DEBUG_DRAW) && defined(DEBUG_DRAW_ISECT)
+ drw_debug(pyramid, intersects ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1));
+#endif
+ return intersects;
+}
+
+bool intersect_view(Box box)
+{
+ bool intersects = true;
+
+ /* Do Box vertices vs Frustum planes. */
+ for (int p = 0; p < 6 && intersects; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 8 && !is_any_vertex_on_positive_side; ++v) {
+ if (dot(frustum_planes[p], vec4(box.corners[v], 1.0)) > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ }
+ }
+ if (!is_any_vertex_on_positive_side) {
+ intersects = false;
+ }
+ }
+
+ if (intersects) {
+ vec4 box_planes[6];
+ planes_setup(box, box_planes);
+ /* Now do Frustum vertices vs Box planes. */
+ for (int p = 0; p < 6 && intersects; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 8 && !is_any_vertex_on_positive_side; ++v) {
+ if (dot(box_planes[p], vec4(frustum_corners[v], 1.0)) > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ }
+ }
+ if (!is_any_vertex_on_positive_side) {
+ intersects = false;
+ }
+ }
+ }
+
+#if defined(DEBUG_DRAW) && defined(DEBUG_DRAW_ISECT)
+ if (intersects) {
+ drw_debug(box, intersects ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1));
+ }
+#endif
+ return intersects;
+}
+
+bool intersect_view(Sphere sph)
+{
+ bool intersects = true;
+
+ for (int p = 0; p < 6 && intersects; ++p) {
+ float dist_to_plane = dot(frustum_planes[p], vec4(sph.center, 1.0));
+ if (dist_to_plane < -sph.radius) {
+ intersects = false;
+ }
+ }
+
+ /* TODO reject false positive. */
+
+#if defined(DEBUG_DRAW) && defined(DEBUG_DRAW_ISECT)
+ if (intersects) {
+ drw_debug(sph, intersects ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1));
+ }
+#endif
+ return intersects;
+}
+
+bool intersect(Pyramid pyramid, Box box)
+{
+ bool intersects = true;
+
+ vec4 box_planes[6];
+ planes_setup(box, box_planes);
+ /* Do Pyramid vertices vs Box planes. */
+ for (int p = 0; p < 6 && intersects; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 5 && !is_any_vertex_on_positive_side; ++v) {
+ if (dot(box_planes[p], vec4(pyramid.corners[v], 1.0)) > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ }
+ }
+ if (!is_any_vertex_on_positive_side) {
+ intersects = false;
+ }
+ }
+
+ if (intersects) {
+ vec4 pyramid_planes[5];
+ planes_setup(pyramid, pyramid_planes);
+ /* Now do Box vertices vs Pyramid planes. */
+ for (int p = 0; p < 5 && intersects; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 8 && !is_any_vertex_on_positive_side; ++v) {
+ if (dot(pyramid_planes[p], vec4(box.corners[v], 1.0)) > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ }
+ }
+ if (!is_any_vertex_on_positive_side) {
+ intersects = false;
+ }
+ }
+ }
+ return intersects;
+}
+
+#undef TEST_ENABLED
+#undef FALSE_POSITIVE_REJECTION
+
+/** \} */
diff --git a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl
index 7b701d1d81b..6c6ee599168 100644
--- a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl
@@ -106,17 +106,17 @@ void make_orthonormal_basis(vec3 N, out vec3 T, out vec3 B)
/* ---- Encode / Decode Normal buffer data ---- */
/* From http://aras-p.info/texts/CompactNormalStorage.html
* Using Method #4: Spheremap Transform */
-vec2 normal_encode(vec3 n, vec3 view)
+vec2 normal_encode(vec3 n)
{
- float p = sqrt(n.z * 8.0 + 8.0);
+ float p = safe_sqrt(n.z * 8.0 + 8.0);
return n.xy / p + 0.5;
}
-vec3 normal_decode(vec2 enc, vec3 view)
+vec3 normal_decode(vec2 enc)
{
vec2 fenc = enc * 4.0 - 2.0;
float f = dot(fenc, fenc);
- float g = sqrt(1.0 - f / 4.0);
+ float g = safe_sqrt(1.0 - f / 4.0);
vec3 n;
n.xy = fenc * g;
n.z = 1 - f / 2;
@@ -134,3 +134,114 @@ vec3 world_to_tangent(vec3 vector, vec3 N, vec3 T, vec3 B)
}
/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Shapes
+ * \{ */
+
+struct Sphere {
+ vec3 center;
+ float radius;
+};
+
+struct Box {
+ vec3 corners[8];
+};
+
+struct Pyramid {
+ /* Apex is the first. Base vertices are in clockwise order from front view. */
+ vec3 corners[5];
+};
+
+#if defined(DEBUG_DRAW)
+
+void drw_debug(Box shape, vec4 color)
+{
+ drw_debug_quad(shape.corners[0], shape.corners[1], shape.corners[2], shape.corners[3], color);
+ drw_debug_line(shape.corners[0], shape.corners[4], color);
+ drw_debug_line(shape.corners[1], shape.corners[5], color);
+ drw_debug_line(shape.corners[2], shape.corners[6], color);
+ drw_debug_line(shape.corners[3], shape.corners[7], color);
+ drw_debug_quad(shape.corners[4], shape.corners[5], shape.corners[6], shape.corners[7], color);
+}
+
+void drw_debug(Pyramid shape, vec4 color)
+{
+ drw_debug_line(shape.corners[0], shape.corners[1], color);
+ drw_debug_line(shape.corners[0], shape.corners[2], color);
+ drw_debug_line(shape.corners[0], shape.corners[3], color);
+ drw_debug_line(shape.corners[0], shape.corners[4], color);
+ drw_debug_quad(shape.corners[1], shape.corners[2], shape.corners[3], shape.corners[4], color);
+}
+
+void drw_debug(Sphere shape, vec4 color)
+{
+ /* TODO(fclem): Counld be better. */
+ drw_debug_point(shape.center, shape.radius, color);
+}
+
+#endif
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Axis Aligned Bound Box
+ * \{ */
+
+struct AABB {
+ vec3 min, max;
+};
+
+AABB init_min_max()
+{
+ AABB aabb;
+ aabb.min = vec3(1.0e30);
+ aabb.max = vec3(-1.0e30);
+ return aabb;
+}
+
+void merge(inout AABB aabb, vec3 v)
+{
+ /* Fix for perspective where */
+ aabb.min = min(aabb.min, v);
+ aabb.max = max(aabb.max, v);
+}
+
+bool intersect(AABB a, AABB b)
+{
+ return all(greaterThanEqual(min(a.max, b.max), max(a.min, b.min)));
+}
+
+bool intersect(AABB a, AABB b, out AABB c)
+{
+ bool isect = intersect(a, b);
+ c.min = max(a.min, b.min);
+ c.max = min(a.max, b.max);
+ return isect;
+}
+
+Box to_box(AABB aabb)
+{
+ Box box;
+ box.corners[0] = aabb.min;
+ box.corners[1] = vec3(aabb.max.x, aabb.min.y, aabb.min.z);
+ box.corners[2] = vec3(aabb.max.x, aabb.max.y, aabb.min.z);
+ box.corners[3] = vec3(aabb.min.x, aabb.max.y, aabb.min.z);
+ box.corners[4] = vec3(aabb.min.x, aabb.min.y, aabb.max.z);
+ box.corners[5] = vec3(aabb.max.x, aabb.min.y, aabb.max.z);
+ box.corners[6] = aabb.max;
+ box.corners[7] = vec3(aabb.min.x, aabb.max.y, aabb.max.z);
+ return box;
+}
+
+#if defined(DEBUG_DRAW)
+
+void drw_debug(AABB shape, vec4 color)
+{
+ Box box = to_box(shape);
+ drw_debug(box, color);
+}
+
+#endif
+
+/** \} */
diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl
index 479f9cd1827..e2941f1b049 100644
--- a/source/blender/draw/intern/shaders/common_math_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_math_lib.glsl
@@ -84,10 +84,13 @@ float avg(vec4 v) { return dot(vec4(1.0 / 4.0), v); }
float safe_rcp(float a) { return (a != 0.0) ? (1.0 / a) : 0.0; }
vec2 safe_rcp(vec2 a) { return mix(vec2(0.0), (1.0 / a), notEqual(a, vec2(0.0))); }
+vec3 safe_rcp(vec3 a) { return mix(vec3(0.0), (1.0 / a), notEqual(a, vec3(0.0))); }
vec4 safe_rcp(vec4 a) { return mix(vec4(0.0), (1.0 / a), notEqual(a, vec4(0.0))); }
float safe_sqrt(float a) { return sqrt(max(a, 0.0)); }
+float safe_acos(float a) { return acos(clamp(a, -1.0, 1.0)); }
+
float sqr(float a) { return a * a; }
vec2 sqr(vec2 a) { return a * a; }
vec3 sqr(vec3 a) { return a * a; }
@@ -101,6 +104,12 @@ float pow8(float x) { return sqr(sqr(sqr(x))); }
float len_squared(vec3 a) { return dot(a, a); }
float len_squared(vec2 a) { return dot(a, a); }
+bool flag_test(uint flag, uint val) { return (flag & val) != 0u; }
+bool flag_test(int flag, int val) { return (flag & val) != 0; }
+
+void set_flag_from_test(inout uint value, bool test, uint flag) { if (test) { value |= flag; } else { value &= ~flag; } }
+void set_flag_from_test(inout int value, bool test, int flag) { if (test) { value |= flag; } else { value &= ~flag; } }
+
#define weighted_sum(val0, val1, val2, val3, weights) ((val0 * weights[0] + val1 * weights[1] + val2 * weights[2] + val3 * weights[3]) * safe_rcp(sum(weights)));
#define weighted_sum_array(val, weights) ((val[0] * weights[0] + val[1] * weights[1] + val[2] * weights[2] + val[3] * weights[3]) * safe_rcp(sum(weights)));
@@ -108,6 +117,38 @@ float len_squared(vec2 a) { return dot(a, a); }
#define saturate(a) clamp(a, 0.0, 1.0)
+#define in_range_inclusive(val, min_v, max_v) \
+ (all(greaterThanEqual(val, min_v)) && all(lessThanEqual(val, max_v)))
+#define in_range_exclusive(val, min_v, max_v) \
+ (all(greaterThan(val, min_v)) && all(lessThan(val, max_v)))
+
+uint divide_ceil_u(uint visible_count, uint divisor)
+{
+ return (visible_count + (divisor - 1u)) / divisor;
+}
+
+int divide_ceil_i(int visible_count, int divisor)
+{
+ return (visible_count + (divisor - 1)) / divisor;
+}
+
+uint bit_field_mask(uint bit_width, uint bit_min)
+{
+ /* Cannot bit shift more than 31 positions. */
+ uint mask = (bit_width > 31u) ? 0x0u : (0xFFFFFFFFu << bit_width);
+ return ~mask << bit_min;
+}
+
+uvec2 unpackUvec2x16(uint data)
+{
+ return uvec2(data >> 16u, data & 0xFFFFu);
+}
+
+uint packUvec2x16(uvec2 data)
+{
+ return (data.x << 16u) | (data.y & 0xFFFFu);
+}
+
float distance_squared(vec2 a, vec2 b)
{
a -= b;
@@ -129,6 +170,21 @@ vec3 safe_normalize(vec3 v)
return v / len;
}
+vec2 safe_normalize_len(vec2 v, out float len)
+{
+ len = length(v);
+ if (isnan(len) || len == 0.0) {
+ return vec2(1.0, 0.0);
+ }
+ return v / len;
+}
+
+vec2 safe_normalize(vec2 v)
+{
+ float len;
+ return safe_normalize_len(v, len);
+}
+
vec3 normalize_len(vec3 v, out float len)
{
len = length(v);
@@ -140,6 +196,11 @@ vec4 safe_color(vec4 c)
/* Clamp to avoid black square artifacts if a pixel goes NaN. */
return clamp(c, vec4(0.0), vec4(1e20)); /* 1e20 arbitrary. */
}
+vec3 safe_color(vec3 c)
+{
+ /* Clamp to avoid black square artifacts if a pixel goes NaN. */
+ return clamp(c, vec3(0.0), vec3(1e20)); /* 1e20 arbitrary. */
+}
/** \} */
@@ -187,3 +248,14 @@ vec3 neon_gradient(float t)
{
return clamp(vec3(t * 1.3 + 0.1, sqr(abs(0.43 - t) * 1.7), (1.0 - t) * 1.7), 0.0, 1.0);
}
+vec3 heatmap_gradient(float t)
+{
+ return saturate((pow(t, 1.5) * 0.8 + 0.2) * vec3(smoothstep(0.0, 0.35, t) + t * 0.5,
+ smoothstep(0.5, 1.0, t),
+ max(1.0 - t * 1.7, t * 7.0 - 6.0)));
+}
+vec3 hue_gradient(float t)
+{
+ vec3 p = abs(fract(t + vec3(1.0, 2.0 / 3.0, 1.0 / 3.0)) * 6.0 - 3.0);
+ return (clamp(p - 1.0, 0.0, 1.0));
+}
diff --git a/source/blender/draw/intern/shaders/common_obinfos_lib.glsl b/source/blender/draw/intern/shaders/common_obinfos_lib.glsl
new file mode 100644
index 00000000000..a56a4dae3c9
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_obinfos_lib.glsl
@@ -0,0 +1,25 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
+struct ObjectInfos {
+ vec4 drw_OrcoTexCoFactors[2];
+ vec4 drw_ObjectColor;
+ vec4 drw_Infos;
+};
+
+layout(std140) uniform infoBlock
+{
+ /* DRW_RESOURCE_CHUNK_LEN = 512 */
+ ObjectInfos drw_infos[512];
+};
+
+#define OrcoTexCoFactors (drw_infos[resource_id].drw_OrcoTexCoFactors)
+#define ObjectInfo (drw_infos[resource_id].drw_Infos)
+#define ObjectColor (drw_infos[resource_id].drw_ObjectColor)
+#define ObjectGpencilWorldScale abs(drw_infos[resource_id].drw_OrcoTexCoFactors[0].w)
+#define ObjectGpencilThicknessIsScreenSpace \
+ (drw_infos[resource_id].drw_OrcoTexCoFactors[0].w < 0.0)
+#define ObjectGpencilDepthOrder2D (drw_infos[resource_id].drw_OrcoTexCoFactors[1].w < 0.0)
+#define ObjectGpencilThickness abs(drw_infos[resource_id].drw_OrcoTexCoFactors[1].w)
+
+#define OBINFO_LIB
diff --git a/source/blender/draw/intern/shaders/common_uniform_attribute_lib.glsl b/source/blender/draw/intern/shaders/common_uniform_attribute_lib.glsl
new file mode 100644
index 00000000000..b34182d77ab
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_uniform_attribute_lib.glsl
@@ -0,0 +1,12 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
+/* UniformAttributes is defined by codegen. */
+
+layout(std140) uniform uniformAttrs
+{
+ /* DRW_RESOURCE_CHUNK_LEN = 512 */
+ UniformAttributes uniform_attrs[512];
+};
+
+#define UNIFORM_ATTR_UBO uniform_attrs[resource_id]
diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl
index d4d18b95238..573ad046ea9 100644
--- a/source/blender/draw/intern/shaders/common_view_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_view_lib.glsl
@@ -22,6 +22,11 @@ layout(std140) uniform viewBlock
/* TODO: move it elsewhere. */
vec4 CameraTexCoFactors;
+ vec2 ViewportSize;
+ vec2 ViewportSizeInverse;
+
+ vec3 frustum_corners[8];
+ vec4 frustum_planes[6];
};
#endif /* DRW_SHADER_SHARED_H */
@@ -258,23 +263,25 @@ uniform mat4 ModelMatrixInverse;
/* ---- Opengl Depth conversion ---- */
+/* Expects positive near/far values. Returns positive value. */
float linear_depth(bool is_persp, float z, float zf, float zn)
{
if (is_persp) {
return (zn * zf) / (z * (zn - zf) + zf);
}
else {
- return (z * 2.0 - 1.0) * zf;
+ return z * (zf - zn) + zn;
}
}
+/* Expects positive near/far values. Returns positive value. */
float buffer_depth(bool is_persp, float z, float zf, float zn)
{
if (is_persp) {
return (zf * (zn - z)) / (z * (zn - zf));
}
else {
- return (z / (zf * 2.0)) + 0.5;
+ return (z - zn) / (zf - zn);
}
}
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 49b6b478148..5bca82b52e4 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -62,7 +62,7 @@ set(SRC
intern/gpu_batch_utils.c
intern/gpu_buffers.c
intern/gpu_capabilities.cc
- intern/gpu_codegen.c
+ intern/gpu_codegen.cc
intern/gpu_compute.cc
intern/gpu_context.cc
intern/gpu_debug.cc
@@ -373,8 +373,6 @@ set(GLSL_SRC
shaders/gpu_shader_cfg_world_clip_lib.glsl
shaders/gpu_shader_colorspace_lib.glsl
- shaders/gpu_shader_common_obinfos_lib.glsl
-
intern/gpu_shader_shared_utils.h
)
diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h
index 3555b957d67..f143730c5cb 100644
--- a/source/blender/gpu/GPU_material.h
+++ b/source/blender/gpu/GPU_material.h
@@ -29,6 +29,7 @@
#include "BLI_sys_types.h" /* for bool */
+#include "GPU_shader.h" /* for GPUShaderSource */
#include "GPU_texture.h" /* for eGPUSamplerState */
#ifdef __cplusplus
@@ -84,35 +85,29 @@ typedef enum eGPUType {
GPU_ATTR = 3001,
} eGPUType;
-typedef enum eGPUBuiltin {
- GPU_VIEW_MATRIX = (1 << 0),
- GPU_OBJECT_MATRIX = (1 << 1),
- GPU_INVERSE_VIEW_MATRIX = (1 << 2),
- GPU_INVERSE_OBJECT_MATRIX = (1 << 3),
- GPU_VIEW_POSITION = (1 << 4),
- GPU_VIEW_NORMAL = (1 << 5),
- GPU_OBJECT_COLOR = (1 << 6),
- GPU_AUTO_BUMPSCALE = (1 << 7),
- GPU_CAMERA_TEXCO_FACTORS = (1 << 8),
- GPU_PARTICLE_SCALAR_PROPS = (1 << 9),
- GPU_PARTICLE_LOCATION = (1 << 10),
- GPU_PARTICLE_VELOCITY = (1 << 11),
- GPU_PARTICLE_ANG_VELOCITY = (1 << 12),
- GPU_LOC_TO_VIEW_MATRIX = (1 << 13),
- GPU_INVERSE_LOC_TO_VIEW_MATRIX = (1 << 14),
- GPU_OBJECT_INFO = (1 << 15),
- GPU_BARYCENTRIC_TEXCO = (1 << 16),
- GPU_BARYCENTRIC_DIST = (1 << 17),
- GPU_WORLD_NORMAL = (1 << 18),
-} eGPUBuiltin;
-
-typedef enum eGPUMatFlag {
+typedef enum eGPUMaterialFlag {
GPU_MATFLAG_DIFFUSE = (1 << 0),
- GPU_MATFLAG_GLOSSY = (1 << 1),
- GPU_MATFLAG_REFRACT = (1 << 2),
- GPU_MATFLAG_SSS = (1 << 3),
- GPU_MATFLAG_BARYCENTRIC = (1 << 4),
-} eGPUMatFlag;
+ GPU_MATFLAG_SUBSURFACE = (1 << 1),
+ GPU_MATFLAG_GLOSSY = (1 << 2),
+ GPU_MATFLAG_REFRACT = (1 << 3),
+ GPU_MATFLAG_EMISSION = (1 << 4),
+ GPU_MATFLAG_TRANSPARENT = (1 << 5),
+ GPU_MATFLAG_HOLDOUT = (1 << 6),
+ GPU_MATFLAG_SHADER_TO_RGBA = (1 << 7),
+
+ GPU_MATFLAG_OBJECT_INFO = (1 << 10),
+ GPU_MATFLAG_UNIFORMS_ATTRIB = (1 << 11),
+
+ GPU_MATFLAG_BARYCENTRIC = (1 << 20),
+
+ /* Tells the render engine the material was just compiled or updated. */
+ GPU_MATFLAG_UPDATED = (1 << 29),
+
+ /* HACK(fclem) Tells the environment texture node to not bail out if empty. */
+ GPU_MATFLAG_LOOKDEV_HACK = (1 << 30),
+} eGPUMaterialFlag;
+
+ENUM_OPERATORS(eGPUMaterialFlag, GPU_MATFLAG_LOOKDEV_HACK);
typedef struct GPUNodeStack {
eGPUType type;
@@ -126,6 +121,7 @@ typedef struct GPUNodeStack {
typedef enum eGPUMaterialStatus {
GPU_MAT_FAILED = 0,
+ GPU_MAT_CREATED,
GPU_MAT_QUEUED,
GPU_MAT_SUCCESS,
} eGPUMaterialStatus;
@@ -135,12 +131,24 @@ typedef enum eGPUVolumeDefaultValue {
GPU_VOLUME_DEFAULT_1,
} eGPUVolumeDefaultValue;
-typedef void (*GPUMaterialEvalCallbackFn)(GPUMaterial *mat,
- int options,
- const char **vert_code,
- const char **geom_code,
- const char **frag_lib,
- const char **defines);
+typedef struct GPUCodegenOutput {
+ char *attribs_declare;
+ char *attribs_interface;
+ char *attribs_passthrough;
+ char *attribs_load;
+
+ char *library;
+ char *uniforms;
+ /* Nodetree functions calls. */
+ char *displacement;
+ char *surface;
+ char *volume;
+ char *thickness;
+} GPUCodegenOutput;
+
+typedef GPUShaderSource (*GPUCodegenCallbackFn)(void *thunk,
+ GPUMaterial *mat,
+ const GPUCodegenOutput *codegen);
GPUNodeLink *GPU_constant(const float *num);
GPUNodeLink *GPU_uniform(const float *num);
@@ -159,7 +167,6 @@ GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *ro
GPUNodeLink *GPU_volume_grid(GPUMaterial *mat,
const char *name,
eGPUVolumeDefaultValue default_value);
-GPUNodeLink *GPU_builtin(eGPUBuiltin builtin);
bool GPU_link(GPUMaterial *mat, const char *name, ...);
bool GPU_stack_link(GPUMaterial *mat,
@@ -168,18 +175,31 @@ bool GPU_stack_link(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out,
...);
+/**
+ * This is a special function to call the "*_eval" function of a BSDF node.
+ * \note This must be call right after GPU_stack_link() so that out[0] contains a valid link.
+ */
+bool GPU_stack_eval_link(GPUMaterial *material,
+ struct bNode *bnode,
+ const char *name,
+ GPUNodeStack *in,
+ GPUNodeStack *out,
+ ...);
GPUNodeLink *GPU_uniformbuf_link_out(struct GPUMaterial *mat,
struct bNode *node,
struct GPUNodeStack *stack,
int index);
-void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link);
+void GPU_material_output_surface(GPUMaterial *material, GPUNodeLink *link);
+void GPU_material_output_volume(GPUMaterial *material, GPUNodeLink *link);
+void GPU_material_output_displacement(GPUMaterial *material, GPUNodeLink *link);
+void GPU_material_output_thickness(GPUMaterial *material, GPUNodeLink *link);
+
void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, int hash);
-void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3]);
-struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material,
- int sample_len,
- struct GPUTexture **tex_profile);
+void GPU_material_add_closure_eval(GPUMaterial *material,
+ const GPUNodeLink *weight_link,
+ const GPUNodeLink *eval_link);
/**
* High level functions to create and use GPU materials.
@@ -196,15 +216,13 @@ GPUMaterial *GPU_material_from_nodetree(struct Scene *scene,
struct Material *ma,
struct bNodeTree *ntree,
struct ListBase *gpumaterials,
- const void *engine_type,
- int options,
- bool is_volume_shader,
- const char *vert_code,
- const char *geom_code,
- const char *frag_lib,
- const char *defines,
const char *name,
- GPUMaterialEvalCallbackFn callback);
+ uint64_t shader_uuid,
+ bool is_volume_shader,
+ bool is_lookdev,
+ GPUCodegenCallbackFn callback,
+ void *thunk);
+
void GPU_material_compile(GPUMaterial *mat);
void GPU_material_free(struct ListBase *gpumaterial);
@@ -221,6 +239,7 @@ struct Material *GPU_material_get_material(GPUMaterial *material);
* Return true if the material compilation has not yet begin or begin.
*/
eGPUMaterialStatus GPU_material_status(GPUMaterial *mat);
+void GPU_material_status_set(GPUMaterial *mat, eGPUMaterialStatus status);
struct GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material);
/**
@@ -231,13 +250,12 @@ struct GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material);
void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs);
struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void);
-bool GPU_material_has_surface_output(GPUMaterial *mat);
-bool GPU_material_has_volume_output(GPUMaterial *mat);
-
bool GPU_material_is_volume_shader(GPUMaterial *mat);
-void GPU_material_flag_set(GPUMaterial *mat, eGPUMatFlag flag);
-bool GPU_material_flag_get(GPUMaterial *mat, eGPUMatFlag flag);
+void GPU_material_flag_set(GPUMaterial *mat, eGPUMaterialFlag flag);
+bool GPU_material_flag_get(const GPUMaterial *mat, eGPUMaterialFlag flag);
+bool GPU_material_recalc_flag_get(GPUMaterial *mat);
+uint64_t GPU_material_uuid_get(GPUMaterial *mat);
void GPU_pass_cache_init(void);
void GPU_pass_cache_garbage_collect(void);
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index 972758febd4..564a02bd068 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -42,6 +42,14 @@ typedef enum eGPUShaderTFBType {
GPU_SHADER_TFB_TRIANGLES = 3,
} eGPUShaderTFBType;
+/* TODO(fclem) Only used by GPUPass but could be used by GPU_shader_create too. */
+typedef struct GPUShaderSource {
+ char *vertex;
+ char *geometry;
+ char *fragment;
+ char *defines;
+} GPUShaderSource;
+
GPUShader *GPU_shader_create(const char *vertcode,
const char *fragcode,
const char *geomcode,
@@ -161,11 +169,18 @@ typedef enum {
GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */
} GPUUniformBlockBuiltin;
+typedef enum {
+ GPU_BUFFER_BLOCK_DEBUG = 0, /* debugBuf */
+
+ GPU_NUM_BUFFER_BLOCKS, /* Special value, denotes number of builtin buffer blocks. */
+} GPUBufferBlockBuiltin;
+
void GPU_shader_set_srgb_uniform(GPUShader *shader);
int GPU_shader_get_uniform(GPUShader *shader, const char *name);
int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin);
int GPU_shader_get_builtin_block(GPUShader *shader, int builtin);
+int GPU_shader_get_builtin_ssbo(GPUShader *shader, int builtin);
/** DEPRECATED: Kept only because of Python GPU API. */
int GPU_shader_get_uniform_block(GPUShader *shader, const char *name);
int GPU_shader_get_ssbo(GPUShader *shader, const char *name);
diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h
index 22cd7eab927..bd52b8321bb 100644
--- a/source/blender/gpu/GPU_state.h
+++ b/source/blender/gpu/GPU_state.h
@@ -37,11 +37,14 @@ ENUM_OPERATORS(eGPUWriteMask, GPU_WRITE_COLOR)
typedef enum eGPUBarrier {
GPU_BARRIER_NONE = 0,
- GPU_BARRIER_SHADER_IMAGE_ACCESS = (1 << 0),
- GPU_BARRIER_TEXTURE_FETCH = (1 << 1),
- GPU_BARRIER_SHADER_STORAGE = (1 << 2),
- GPU_BARRIER_VERTEX_ATTRIB_ARRAY = (1 << 3),
- GPU_BARRIER_ELEMENT_ARRAY = (1 << 4),
+ GPU_BARRIER_COMMAND = (1 << 0),
+ GPU_BARRIER_FRAMEBUFFER = (1 << 1),
+ GPU_BARRIER_SHADER_IMAGE_ACCESS = (1 << 2),
+ GPU_BARRIER_SHADER_STORAGE = (1 << 3),
+ GPU_BARRIER_TEXTURE_FETCH = (1 << 4),
+ GPU_BARRIER_TEXTURE_UPDATE = (1 << 5),
+ GPU_BARRIER_VERTEX_ATTRIB_ARRAY = (1 << 6),
+ GPU_BARRIER_ELEMENT_ARRAY = (1 << 7),
} eGPUBarrier;
ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_ELEMENT_ARRAY)
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c
deleted file mode 100644
index 568462ec2a6..00000000000
--- a/source/blender/gpu/intern/gpu_codegen.c
+++ /dev/null
@@ -1,1138 +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.
- *
- * The Original Code is Copyright (C) 2005 Blender Foundation.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup gpu
- *
- * Convert material node-trees to GLSL.
- */
-
-#include "MEM_guardedalloc.h"
-
-#include "DNA_customdata_types.h"
-#include "DNA_image_types.h"
-
-#include "BLI_blenlib.h"
-#include "BLI_dynstr.h"
-#include "BLI_ghash.h"
-#include "BLI_hash_mm2a.h"
-#include "BLI_link_utils.h"
-#include "BLI_threads.h"
-#include "BLI_utildefines.h"
-
-#include "PIL_time.h"
-
-#include "BKE_material.h"
-
-#include "GPU_capabilities.h"
-#include "GPU_material.h"
-#include "GPU_shader.h"
-#include "GPU_uniform_buffer.h"
-#include "GPU_vertex_format.h"
-
-#include "BLI_sys_types.h" /* for intptr_t support */
-
-#include "gpu_codegen.h"
-#include "gpu_material_library.h"
-#include "gpu_node_graph.h"
-
-#include <stdarg.h>
-#include <string.h>
-
-extern char datatoc_gpu_shader_codegen_lib_glsl[];
-extern char datatoc_gpu_shader_common_obinfos_lib_glsl[];
-
-/* -------------------- GPUPass Cache ------------------ */
-/**
- * Internal shader cache: This prevent the shader recompilation / stall when
- * using undo/redo AND also allows for GPUPass reuse if the Shader code is the
- * same for 2 different Materials. Unused GPUPasses are free by Garbage collection.
- */
-
-/* Only use one linked-list that contains the GPUPasses grouped by hash. */
-static GPUPass *pass_cache = NULL;
-static SpinLock pass_cache_spin;
-
-static uint32_t gpu_pass_hash(const char *frag_gen, const char *defs, ListBase *attributes)
-{
- BLI_HashMurmur2A hm2a;
- BLI_hash_mm2a_init(&hm2a, 0);
- BLI_hash_mm2a_add(&hm2a, (uchar *)frag_gen, strlen(frag_gen));
- LISTBASE_FOREACH (GPUMaterialAttribute *, attr, attributes) {
- BLI_hash_mm2a_add(&hm2a, (uchar *)attr->name, strlen(attr->name));
- }
- if (defs) {
- BLI_hash_mm2a_add(&hm2a, (uchar *)defs, strlen(defs));
- }
-
- return BLI_hash_mm2a_end(&hm2a);
-}
-
-/* Search by hash only. Return first pass with the same hash.
- * There is hash collision if (pass->next && pass->next->hash == hash) */
-static GPUPass *gpu_pass_cache_lookup(uint32_t hash)
-{
- BLI_spin_lock(&pass_cache_spin);
- /* Could be optimized with a Lookup table. */
- for (GPUPass *pass = pass_cache; pass; pass = pass->next) {
- if (pass->hash == hash) {
- BLI_spin_unlock(&pass_cache_spin);
- return pass;
- }
- }
- BLI_spin_unlock(&pass_cache_spin);
- return NULL;
-}
-
-/* Check all possible passes with the same hash. */
-static GPUPass *gpu_pass_cache_resolve_collision(GPUPass *pass,
- const char *vert,
- const char *geom,
- const char *frag,
- const char *defs,
- uint32_t hash)
-{
- BLI_spin_lock(&pass_cache_spin);
- /* Collision, need to `strcmp` the whole shader. */
- for (; pass && (pass->hash == hash); pass = pass->next) {
- if ((defs != NULL) && (!STREQ(pass->defines, defs))) { /* Pass */
- }
- else if ((geom != NULL) && (!STREQ(pass->geometrycode, geom))) { /* Pass */
- }
- else if ((!STREQ(pass->fragmentcode, frag) == 0) && (STREQ(pass->vertexcode, vert))) {
- BLI_spin_unlock(&pass_cache_spin);
- return pass;
- }
- }
- BLI_spin_unlock(&pass_cache_spin);
- return NULL;
-}
-
-/* GLSL code generation */
-
-static void codegen_convert_datatype(DynStr *ds, int from, int to, const char *tmp, int id)
-{
- char name[1024];
-
- BLI_snprintf(name, sizeof(name), "%s%d", tmp, id);
-
- if (from == to) {
- BLI_dynstr_append(ds, name);
- }
- else if (to == GPU_FLOAT) {
- if (from == GPU_VEC4) {
- BLI_dynstr_appendf(ds, "dot(%s.rgb, vec3(0.2126, 0.7152, 0.0722))", name);
- }
- else if (from == GPU_VEC3) {
- BLI_dynstr_appendf(ds, "(%s.r + %s.g + %s.b) / 3.0", name, name, name);
- }
- else if (from == GPU_VEC2) {
- BLI_dynstr_appendf(ds, "%s.r", name);
- }
- }
- else if (to == GPU_VEC2) {
- if (from == GPU_VEC4) {
- BLI_dynstr_appendf(ds, "vec2((%s.r + %s.g + %s.b) / 3.0, %s.a)", name, name, name, name);
- }
- else if (from == GPU_VEC3) {
- BLI_dynstr_appendf(ds, "vec2((%s.r + %s.g + %s.b) / 3.0, 1.0)", name, name, name);
- }
- else if (from == GPU_FLOAT) {
- BLI_dynstr_appendf(ds, "vec2(%s, 1.0)", name);
- }
- }
- else if (to == GPU_VEC3) {
- if (from == GPU_VEC4) {
- BLI_dynstr_appendf(ds, "%s.rgb", name);
- }
- else if (from == GPU_VEC2) {
- BLI_dynstr_appendf(ds, "vec3(%s.r, %s.r, %s.r)", name, name, name);
- }
- else if (from == GPU_FLOAT) {
- BLI_dynstr_appendf(ds, "vec3(%s, %s, %s)", name, name, name);
- }
- }
- else if (to == GPU_VEC4) {
- if (from == GPU_VEC3) {
- BLI_dynstr_appendf(ds, "vec4(%s, 1.0)", name);
- }
- else if (from == GPU_VEC2) {
- BLI_dynstr_appendf(ds, "vec4(%s.r, %s.r, %s.r, %s.g)", name, name, name, name);
- }
- else if (from == GPU_FLOAT) {
- BLI_dynstr_appendf(ds, "vec4(%s, %s, %s, 1.0)", name, name, name);
- }
- }
- else if (to == GPU_CLOSURE) {
- if (from == GPU_VEC4) {
- BLI_dynstr_appendf(ds, "closure_emission(%s.rgb)", name);
- }
- else if (from == GPU_VEC3) {
- BLI_dynstr_appendf(ds, "closure_emission(%s.rgb)", name);
- }
- else if (from == GPU_VEC2) {
- BLI_dynstr_appendf(ds, "closure_emission(%s.rrr)", name);
- }
- else if (from == GPU_FLOAT) {
- BLI_dynstr_appendf(ds, "closure_emission(vec3(%s, %s, %s))", name, name, name);
- }
- }
- else {
- BLI_dynstr_append(ds, name);
- }
-}
-
-static void codegen_print_datatype(DynStr *ds, const eGPUType type, float *data)
-{
- int i;
-
- BLI_dynstr_appendf(ds, "%s(", gpu_data_type_to_string(type));
-
- for (i = 0; i < type; i++) {
- BLI_dynstr_appendf(ds, "%.12f", data[i]);
- if (i == type - 1) {
- BLI_dynstr_append(ds, ")");
- }
- else {
- BLI_dynstr_append(ds, ", ");
- }
- }
-}
-
-static const char *gpu_builtin_name(eGPUBuiltin builtin)
-{
- if (builtin == GPU_VIEW_MATRIX) {
- return "unfviewmat";
- }
- if (builtin == GPU_OBJECT_MATRIX) {
- return "unfobmat";
- }
- if (builtin == GPU_INVERSE_VIEW_MATRIX) {
- return "unfinvviewmat";
- }
- if (builtin == GPU_INVERSE_OBJECT_MATRIX) {
- return "unfinvobmat";
- }
- if (builtin == GPU_LOC_TO_VIEW_MATRIX) {
- return "unflocaltoviewmat";
- }
- if (builtin == GPU_INVERSE_LOC_TO_VIEW_MATRIX) {
- return "unfinvlocaltoviewmat";
- }
- if (builtin == GPU_VIEW_POSITION) {
- return "varposition";
- }
- if (builtin == GPU_WORLD_NORMAL) {
- return "varwnormal";
- }
- if (builtin == GPU_VIEW_NORMAL) {
- return "varnormal";
- }
- if (builtin == GPU_OBJECT_COLOR) {
- return "unfobjectcolor";
- }
- if (builtin == GPU_AUTO_BUMPSCALE) {
- return "unfobautobumpscale";
- }
- if (builtin == GPU_CAMERA_TEXCO_FACTORS) {
- return "unfcameratexfactors";
- }
- if (builtin == GPU_PARTICLE_SCALAR_PROPS) {
- return "unfparticlescalarprops";
- }
- if (builtin == GPU_PARTICLE_LOCATION) {
- return "unfparticleco";
- }
- if (builtin == GPU_PARTICLE_VELOCITY) {
- return "unfparticlevel";
- }
- if (builtin == GPU_PARTICLE_ANG_VELOCITY) {
- return "unfparticleangvel";
- }
- if (builtin == GPU_OBJECT_INFO) {
- return "unfobjectinfo";
- }
- if (builtin == GPU_BARYCENTRIC_TEXCO) {
- return "unfbarycentrictex";
- }
- if (builtin == GPU_BARYCENTRIC_DIST) {
- return "unfbarycentricdist";
- }
- return "";
-}
-
-static void codegen_set_unique_ids(GPUNodeGraph *graph)
-{
- int id = 1;
-
- LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
- LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
- /* set id for unique names of uniform variables */
- input->id = id++;
- }
-
- LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) {
- /* set id for unique names of tmp variables storing output */
- output->id = id++;
- }
- }
-}
-
-/**
- * It will create an UBO for GPUMaterial if there is any GPU_DYNAMIC_UBO.
- */
-static int codegen_process_uniforms_functions(GPUMaterial *material,
- DynStr *ds,
- GPUNodeGraph *graph)
-{
- const char *name;
- int builtins = 0;
- ListBase ubo_inputs = {NULL, NULL};
-
- /* Textures */
- LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph->textures) {
- if (tex->colorband) {
- BLI_dynstr_appendf(ds, "uniform sampler1DArray %s;\n", tex->sampler_name);
- }
- else if (tex->tiled_mapping_name[0]) {
- BLI_dynstr_appendf(ds, "uniform sampler2DArray %s;\n", tex->sampler_name);
- BLI_dynstr_appendf(ds, "uniform sampler1DArray %s;\n", tex->tiled_mapping_name);
- }
- else {
- BLI_dynstr_appendf(ds, "uniform sampler2D %s;\n", tex->sampler_name);
- }
- }
-
- /* Volume Grids */
- LISTBASE_FOREACH (GPUMaterialVolumeGrid *, grid, &graph->volume_grids) {
- BLI_dynstr_appendf(ds, "uniform sampler3D %s;\n", grid->sampler_name);
- BLI_dynstr_appendf(ds, "uniform mat4 %s = mat4(0.0);\n", grid->transform_name);
- }
-
- /* Print other uniforms */
-
- LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
- LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
- if (input->source == GPU_SOURCE_BUILTIN) {
- /* only define each builtin uniform/varying once */
- if (!(builtins & input->builtin)) {
- builtins |= input->builtin;
- name = gpu_builtin_name(input->builtin);
-
- if (BLI_str_startswith(name, "unf")) {
- BLI_dynstr_appendf(ds, "uniform %s %s;\n", gpu_data_type_to_string(input->type), name);
- }
- else {
- BLI_dynstr_appendf(ds, "in %s %s;\n", gpu_data_type_to_string(input->type), name);
- }
- }
- }
- else if (input->source == GPU_SOURCE_STRUCT) {
- /* Add other struct here if needed. */
- BLI_dynstr_appendf(ds, "Closure strct%d = CLOSURE_DEFAULT;\n", input->id);
- }
- else if (input->source == GPU_SOURCE_UNIFORM) {
- if (!input->link) {
- /* We handle the UBOuniforms separately. */
- BLI_addtail(&ubo_inputs, BLI_genericNodeN(input));
- }
- }
- else if (input->source == GPU_SOURCE_CONSTANT) {
- BLI_dynstr_appendf(
- ds, "const %s cons%d = ", gpu_data_type_to_string(input->type), input->id);
- codegen_print_datatype(ds, input->type, input->vec);
- BLI_dynstr_append(ds, ";\n");
- }
- }
- }
-
- /* Handle the UBO block separately. */
- if ((material != NULL) && !BLI_listbase_is_empty(&ubo_inputs)) {
- GPU_material_uniform_buffer_create(material, &ubo_inputs);
-
- /* Inputs are sorted */
- BLI_dynstr_appendf(ds, "\nlayout (std140) uniform %s {\n", GPU_UBO_BLOCK_NAME);
-
- LISTBASE_FOREACH (LinkData *, link, &ubo_inputs) {
- GPUInput *input = (GPUInput *)(link->data);
- BLI_dynstr_appendf(ds, " %s unf%d;\n", gpu_data_type_to_string(input->type), input->id);
- }
- BLI_dynstr_append(ds, "};\n");
- BLI_freelistN(&ubo_inputs);
- }
-
- /* Generate the uniform attribute UBO if necessary. */
- if (!BLI_listbase_is_empty(&graph->uniform_attrs.list)) {
- BLI_dynstr_append(ds, "\nstruct UniformAttributes {\n");
- LISTBASE_FOREACH (GPUUniformAttr *, attr, &graph->uniform_attrs.list) {
- BLI_dynstr_appendf(ds, " vec4 attr%d;\n", attr->id);
- }
- BLI_dynstr_append(ds, "};\n");
- BLI_dynstr_appendf(ds, "layout (std140) uniform %s {\n", GPU_ATTRIBUTE_UBO_BLOCK_NAME);
- BLI_dynstr_append(ds, " UniformAttributes uniform_attrs[DRW_RESOURCE_CHUNK_LEN];\n");
- BLI_dynstr_append(ds, "};\n");
- BLI_dynstr_append(ds, "#define GET_UNIFORM_ATTR(name) (uniform_attrs[resource_id].name)\n");
- }
-
- BLI_dynstr_append(ds, "\n");
-
- return builtins;
-}
-
-static void codegen_declare_tmps(DynStr *ds, GPUNodeGraph *graph)
-{
- LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
- /* declare temporary variables for node output storage */
- LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) {
- if (output->type == GPU_CLOSURE) {
- BLI_dynstr_appendf(ds, " Closure tmp%d;\n", output->id);
- }
- else {
- BLI_dynstr_appendf(ds, " %s tmp%d;\n", gpu_data_type_to_string(output->type), output->id);
- }
- }
- }
- BLI_dynstr_append(ds, "\n");
-}
-
-static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph)
-{
- LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
- BLI_dynstr_appendf(ds, " %s(", node->name);
-
- LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
- if (input->source == GPU_SOURCE_TEX) {
- BLI_dynstr_append(ds, input->texture->sampler_name);
- }
- else if (input->source == GPU_SOURCE_TEX_TILED_MAPPING) {
- BLI_dynstr_append(ds, input->texture->tiled_mapping_name);
- }
- else if (input->source == GPU_SOURCE_VOLUME_GRID) {
- BLI_dynstr_append(ds, input->volume_grid->sampler_name);
- }
- else if (input->source == GPU_SOURCE_VOLUME_GRID_TRANSFORM) {
- BLI_dynstr_append(ds, input->volume_grid->transform_name);
- }
- else if (input->source == GPU_SOURCE_OUTPUT) {
- codegen_convert_datatype(
- ds, input->link->output->type, input->type, "tmp", input->link->output->id);
- }
- else if (input->source == GPU_SOURCE_BUILTIN) {
- /* TODO(fclem): get rid of that. */
- if (input->builtin == GPU_INVERSE_VIEW_MATRIX) {
- BLI_dynstr_append(ds, "viewinv");
- }
- else if (input->builtin == GPU_VIEW_MATRIX) {
- BLI_dynstr_append(ds, "viewmat");
- }
- else if (input->builtin == GPU_CAMERA_TEXCO_FACTORS) {
- BLI_dynstr_append(ds, "camtexfac");
- }
- else if (input->builtin == GPU_LOC_TO_VIEW_MATRIX) {
- BLI_dynstr_append(ds, "localtoviewmat");
- }
- else if (input->builtin == GPU_INVERSE_LOC_TO_VIEW_MATRIX) {
- BLI_dynstr_append(ds, "invlocaltoviewmat");
- }
- else if (input->builtin == GPU_BARYCENTRIC_DIST) {
- BLI_dynstr_append(ds, "barycentricDist");
- }
- else if (input->builtin == GPU_BARYCENTRIC_TEXCO) {
- BLI_dynstr_append(ds, "barytexco");
- }
- else if (input->builtin == GPU_OBJECT_MATRIX) {
- BLI_dynstr_append(ds, "objmat");
- }
- else if (input->builtin == GPU_OBJECT_INFO) {
- BLI_dynstr_append(ds, "ObjectInfo");
- }
- else if (input->builtin == GPU_OBJECT_COLOR) {
- BLI_dynstr_append(ds, "ObjectColor");
- }
- else if (input->builtin == GPU_INVERSE_OBJECT_MATRIX) {
- BLI_dynstr_append(ds, "objinv");
- }
- else if (input->builtin == GPU_VIEW_POSITION) {
- BLI_dynstr_append(ds, "viewposition");
- }
- else if (input->builtin == GPU_VIEW_NORMAL) {
- BLI_dynstr_append(ds, "facingnormal");
- }
- else if (input->builtin == GPU_WORLD_NORMAL) {
- BLI_dynstr_append(ds, "facingwnormal");
- }
- else {
- BLI_dynstr_append(ds, gpu_builtin_name(input->builtin));
- }
- }
- else if (input->source == GPU_SOURCE_STRUCT) {
- BLI_dynstr_appendf(ds, "strct%d", input->id);
- }
- else if (input->source == GPU_SOURCE_UNIFORM) {
- BLI_dynstr_appendf(ds, "unf%d", input->id);
- }
- else if (input->source == GPU_SOURCE_CONSTANT) {
- BLI_dynstr_appendf(ds, "cons%d", input->id);
- }
- else if (input->source == GPU_SOURCE_ATTR) {
- codegen_convert_datatype(ds, input->attr->gputype, input->type, "var", input->attr->id);
- }
- else if (input->source == GPU_SOURCE_UNIFORM_ATTR) {
- BLI_dynstr_appendf(ds, "GET_UNIFORM_ATTR(attr%d)", input->uniform_attr->id);
- }
-
- BLI_dynstr_append(ds, ", ");
- }
-
- LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) {
- BLI_dynstr_appendf(ds, "tmp%d", output->id);
- if (output->next) {
- BLI_dynstr_append(ds, ", ");
- }
- }
-
- BLI_dynstr_append(ds, ");\n");
- }
-}
-
-static void codegen_final_output(DynStr *ds, GPUOutput *finaloutput)
-{
- BLI_dynstr_appendf(ds, "return tmp%d;\n", finaloutput->id);
-}
-
-static char *code_generate_fragment(GPUMaterial *material,
- GPUNodeGraph *graph,
- const char *interface_str)
-{
- DynStr *ds = BLI_dynstr_new();
- char *code;
- int builtins;
-
- codegen_set_unique_ids(graph);
-
- /* Attributes, Shader stage interface. */
- if (interface_str) {
- BLI_dynstr_appendf(ds, "in codegenInterface {%s};\n\n", interface_str);
- }
-
- builtins = codegen_process_uniforms_functions(material, ds, graph);
-
- if (builtins & (GPU_OBJECT_INFO | GPU_OBJECT_COLOR)) {
- BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl);
- }
-
- if (builtins & GPU_BARYCENTRIC_TEXCO) {
- BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl);
- }
-
- BLI_dynstr_append(ds, "Closure nodetree_exec(void)\n{\n");
-
- if (builtins & GPU_BARYCENTRIC_TEXCO) {
- BLI_dynstr_append(ds, " vec2 barytexco = barycentric_resolve(barycentricTexCo);\n");
- }
- /* TODO(fclem): get rid of that. */
- if (builtins & GPU_VIEW_MATRIX) {
- BLI_dynstr_append(ds, " #define viewmat ViewMatrix\n");
- }
- if (builtins & GPU_CAMERA_TEXCO_FACTORS) {
- BLI_dynstr_append(ds, " #define camtexfac CameraTexCoFactors\n");
- }
- if (builtins & GPU_OBJECT_MATRIX) {
- BLI_dynstr_append(ds, " #define objmat ModelMatrix\n");
- }
- if (builtins & GPU_INVERSE_OBJECT_MATRIX) {
- BLI_dynstr_append(ds, " #define objinv ModelMatrixInverse\n");
- }
- if (builtins & GPU_INVERSE_VIEW_MATRIX) {
- BLI_dynstr_append(ds, " #define viewinv ViewMatrixInverse\n");
- }
- if (builtins & GPU_LOC_TO_VIEW_MATRIX) {
- BLI_dynstr_append(ds, " #define localtoviewmat (ViewMatrix * ModelMatrix)\n");
- }
- if (builtins & GPU_INVERSE_LOC_TO_VIEW_MATRIX) {
- BLI_dynstr_append(ds,
- " #define invlocaltoviewmat (ModelMatrixInverse * ViewMatrixInverse)\n");
- }
- if (builtins & GPU_VIEW_NORMAL) {
- BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n");
- BLI_dynstr_append(ds, " vec3 n;\n");
- BLI_dynstr_append(ds, " world_normals_get(n);\n");
- BLI_dynstr_append(ds, " vec3 facingnormal = transform_direction(ViewMatrix, n);\n");
- BLI_dynstr_append(ds, "#else\n");
- BLI_dynstr_append(ds, " vec3 facingnormal = gl_FrontFacing ? viewNormal: -viewNormal;\n");
- BLI_dynstr_append(ds, "#endif\n");
- }
- if (builtins & GPU_WORLD_NORMAL) {
- BLI_dynstr_append(ds, " vec3 facingwnormal;\n");
- if (builtins & GPU_VIEW_NORMAL) {
- BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n");
- BLI_dynstr_append(ds, " facingwnormal = n;\n");
- BLI_dynstr_append(ds, "#else\n");
- BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n");
- BLI_dynstr_append(ds, "#endif\n");
- }
- else {
- BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n");
- }
- }
- if (builtins & GPU_VIEW_POSITION) {
- BLI_dynstr_append(ds, " #define viewposition viewPosition\n");
- }
-
- codegen_declare_tmps(ds, graph);
- codegen_call_functions(ds, graph);
-
- BLI_dynstr_append(ds, " #ifndef VOLUMETRICS\n");
- BLI_dynstr_append(ds, " if (renderPassAOV) {\n");
- BLI_dynstr_append(ds, " switch (render_pass_aov_hash()) {\n");
- GSet *aovhashes_added = BLI_gset_int_new(__func__);
- LISTBASE_FOREACH (GPUNodeGraphOutputLink *, aovlink, &graph->outlink_aovs) {
- void *aov_key = POINTER_FROM_INT(aovlink->hash);
- if (BLI_gset_haskey(aovhashes_added, aov_key)) {
- continue;
- }
- BLI_dynstr_appendf(ds, " case %d: {\n ", aovlink->hash);
- codegen_final_output(ds, aovlink->outlink->output);
- BLI_dynstr_append(ds, " }\n");
- BLI_gset_add(aovhashes_added, aov_key);
- }
- BLI_gset_free(aovhashes_added, NULL);
- BLI_dynstr_append(ds, " default: {\n");
- BLI_dynstr_append(ds, " Closure no_aov = CLOSURE_DEFAULT;\n");
- BLI_dynstr_append(ds, " no_aov.holdout = 1.0;\n");
- BLI_dynstr_append(ds, " return no_aov;\n");
- BLI_dynstr_append(ds, " }\n");
- BLI_dynstr_append(ds, " }\n");
- BLI_dynstr_append(ds, " } else {\n");
- BLI_dynstr_append(ds, " #else /* VOLUMETRICS */\n");
- BLI_dynstr_append(ds, " {\n");
- BLI_dynstr_append(ds, " #endif /* VOLUMETRICS */\n ");
- codegen_final_output(ds, graph->outlink->output);
- BLI_dynstr_append(ds, " }\n");
-
- BLI_dynstr_append(ds, "}\n");
-
- /* create shader */
- code = BLI_dynstr_get_cstring(ds);
- BLI_dynstr_free(ds);
-
-#if 0
- if (G.debug & G_DEBUG) {
- printf("%s\n", code);
- }
-#endif
-
- return code;
-}
-
-static const char *attr_prefix_get(CustomDataType type)
-{
- switch (type) {
- case CD_ORCO:
- return "orco";
- case CD_MTFACE:
- return "u";
- case CD_TANGENT:
- return "t";
- case CD_MCOL:
- return "c";
- case CD_PROP_COLOR:
- return "c";
- case CD_AUTO_FROM_NAME:
- return "a";
- case CD_HAIRLENGTH:
- return "hl";
- default:
- BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!");
- return "";
- }
-}
-
-/* We talk about shader stage interface, not to be mistaken with GPUShaderInterface. */
-static char *code_generate_interface(GPUNodeGraph *graph, int builtins)
-{
- if (BLI_listbase_is_empty(&graph->attributes) &&
- (builtins & (GPU_BARYCENTRIC_DIST | GPU_BARYCENTRIC_TEXCO)) == 0) {
- return NULL;
- }
-
- DynStr *ds = BLI_dynstr_new();
-
- BLI_dynstr_append(ds, "\n");
-
- LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
- if (attr->type == CD_HAIRLENGTH) {
- BLI_dynstr_appendf(ds, "float var%d;\n", attr->id);
- }
- else {
- BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id);
- }
- }
- if (builtins & GPU_BARYCENTRIC_TEXCO) {
- BLI_dynstr_append(ds, "vec2 barycentricTexCo;\n");
- }
- if (builtins & GPU_BARYCENTRIC_DIST) {
- BLI_dynstr_append(ds, "vec3 barycentricDist;\n");
- }
-
- char *code = BLI_dynstr_get_cstring(ds);
-
- BLI_dynstr_free(ds);
-
- return code;
-}
-
-static char *code_generate_vertex(GPUNodeGraph *graph,
- const char *interface_str,
- const char *vert_code,
- int builtins)
-{
- DynStr *ds = BLI_dynstr_new();
-
- BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl);
-
- /* Inputs */
- LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
- const char *type_str = gpu_data_type_to_string(attr->gputype);
- const char *prefix = attr_prefix_get(attr->type);
- /* XXX FIXME : see notes in mesh_render_data_create() */
- /* NOTE : Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */
- if (attr->type == CD_ORCO) {
- /* OPTI : orco is computed from local positions, but only if no modifier is present. */
- BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl);
- BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n");
- }
- else if (attr->type == CD_HAIRLENGTH) {
- BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl);
- BLI_dynstr_append(ds, "DEFINE_ATTR(float, hairLen);\n");
- }
- else if (attr->name[0] == '\0') {
- BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s);\n", type_str, prefix);
- BLI_dynstr_appendf(ds, "#define att%d %s\n", attr->id, prefix);
- }
- else {
- char attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
- GPU_vertformat_safe_attr_name(attr->name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
- BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s%s);\n", type_str, prefix, attr_safe_name);
- BLI_dynstr_appendf(ds, "#define att%d %s%s\n", attr->id, prefix, attr_safe_name);
- }
- }
-
- /* Outputs interface */
- if (interface_str) {
- BLI_dynstr_appendf(ds, "out codegenInterface {%s};\n\n", interface_str);
- }
-
- /* Prototype. Needed for hair functions. */
- BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv);\n");
- BLI_dynstr_append(ds, "#define USE_ATTR\n\n");
-
- BLI_dynstr_append(ds, vert_code);
- BLI_dynstr_append(ds, "\n\n");
-
- BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv) {\n");
-
- /* GPU_BARYCENTRIC_TEXCO cannot be computed based on gl_VertexID
- * for MESH_SHADER because of indexed drawing. In this case a
- * geometry shader is needed. */
- if (builtins & GPU_BARYCENTRIC_TEXCO) {
- BLI_dynstr_appendf(ds, " barycentricTexCo = barycentric_get();\n");
- }
- if (builtins & GPU_BARYCENTRIC_DIST) {
- BLI_dynstr_appendf(ds, " barycentricDist = vec3(0);\n");
- }
-
- LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
- if (attr->type == CD_TANGENT) { /* silly exception */
- BLI_dynstr_appendf(ds, " var%d = tangent_get(att%d, normalmat);\n", attr->id, attr->id);
- }
- else if (attr->type == CD_ORCO) {
- BLI_dynstr_appendf(
- ds, " var%d = orco_get(position, modelmatinv, OrcoTexCoFactors, orco);\n", attr->id);
- }
- else if (attr->type == CD_HAIRLENGTH) {
- BLI_dynstr_appendf(ds, " var%d = hair_len_get(hair_get_strand_id(), hairLen);\n", attr->id);
- }
- else {
- const char *type_str = gpu_data_type_to_string(attr->gputype);
- BLI_dynstr_appendf(ds, " var%d = GET_ATTR(%s, att%d);\n", attr->id, type_str, attr->id);
- }
- }
-
- BLI_dynstr_append(ds, "}\n");
-
- char *code = BLI_dynstr_get_cstring(ds);
-
- BLI_dynstr_free(ds);
-
-#if 0
- if (G.debug & G_DEBUG) {
- printf("%s\n", code);
- }
-#endif
-
- return code;
-}
-
-static char *code_generate_geometry(GPUNodeGraph *graph,
- const char *interface_str,
- const char *geom_code,
- int builtins)
-{
- if (!geom_code) {
- return NULL;
- }
-
- DynStr *ds = BLI_dynstr_new();
-
- /* Attributes, Shader interface; */
- if (interface_str) {
- BLI_dynstr_appendf(ds, "in codegenInterface {%s} dataAttrIn[];\n\n", interface_str);
- BLI_dynstr_appendf(ds, "out codegenInterface {%s} dataAttrOut;\n\n", interface_str);
- }
-
- BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl);
-
- if (builtins & GPU_BARYCENTRIC_DIST) {
- /* geom_code should do something with this, but may not. */
- BLI_dynstr_append(ds, "#define DO_BARYCENTRIC_DISTANCES\n");
- }
-
- /* Generate varying assignments. */
- BLI_dynstr_append(ds, "#define USE_ATTR\n");
- /* This needs to be a define. Some drivers don't like variable vert index inside dataAttrIn. */
- BLI_dynstr_append(ds, "#define pass_attr(vert) {\\\n");
-
- if (builtins & GPU_BARYCENTRIC_TEXCO) {
- BLI_dynstr_append(ds, "dataAttrOut.barycentricTexCo = calc_barycentric_co(vert);\\\n");
- }
-
- LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
- /* TODO: let shader choose what to do depending on what the attribute is. */
- BLI_dynstr_appendf(ds, "dataAttrOut.var%d = dataAttrIn[vert].var%d;\\\n", attr->id, attr->id);
- }
- BLI_dynstr_append(ds, "}\n\n");
-
- BLI_dynstr_append(ds, geom_code);
-
- char *code = BLI_dynstr_get_cstring(ds);
- BLI_dynstr_free(ds);
-
- return code;
-}
-
-GPUShader *GPU_pass_shader_get(GPUPass *pass)
-{
- return pass->shader;
-}
-
-/* Pass create/free */
-
-static bool gpu_pass_is_valid(GPUPass *pass)
-{
- /* Shader is not null if compilation is successful. */
- return (pass->compiled == false || pass->shader != NULL);
-}
-
-GPUPass *GPU_generate_pass(GPUMaterial *material,
- GPUNodeGraph *graph,
- const char *vert_code,
- const char *geom_code,
- const char *frag_lib,
- const char *defines)
-{
- /* Prune the unused nodes and extract attributes before compiling so the
- * generated VBOs are ready to accept the future shader. */
- gpu_node_graph_prune_unused(graph);
- gpu_node_graph_finalize_uniform_attrs(graph);
-
- int builtins = 0;
- LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
- LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
- if (input->source == GPU_SOURCE_BUILTIN) {
- builtins |= input->builtin;
- }
- }
- }
- /* generate code */
- char *interface_str = code_generate_interface(graph, builtins);
- char *fragmentgen = code_generate_fragment(material, graph, interface_str);
-
- /* Cache lookup: Reuse shaders already compiled */
- uint32_t hash = gpu_pass_hash(fragmentgen, defines, &graph->attributes);
- GPUPass *pass_hash = gpu_pass_cache_lookup(hash);
-
- if (pass_hash && (pass_hash->next == NULL || pass_hash->next->hash != hash)) {
- /* No collision, just return the pass. */
- MEM_SAFE_FREE(interface_str);
- MEM_freeN(fragmentgen);
- if (!gpu_pass_is_valid(pass_hash)) {
- /* Shader has already been created but failed to compile. */
- return NULL;
- }
- pass_hash->refcount += 1;
- return pass_hash;
- }
-
- /* Either the shader is not compiled or there is a hash collision...
- * continue generating the shader strings. */
- GSet *used_libraries = gpu_material_used_libraries(material);
- char *tmp = gpu_material_library_generate_code(used_libraries, frag_lib);
-
- char *geometrycode = code_generate_geometry(graph, interface_str, geom_code, builtins);
- char *vertexcode = code_generate_vertex(graph, interface_str, vert_code, builtins);
- char *fragmentcode = BLI_strdupcat(tmp, fragmentgen);
-
- MEM_SAFE_FREE(interface_str);
- MEM_freeN(fragmentgen);
- MEM_freeN(tmp);
-
- GPUPass *pass = NULL;
- if (pass_hash) {
- /* Cache lookup: Reuse shaders already compiled */
- pass = gpu_pass_cache_resolve_collision(
- pass_hash, vertexcode, geometrycode, fragmentcode, defines, hash);
- }
-
- if (pass) {
- MEM_SAFE_FREE(vertexcode);
- MEM_SAFE_FREE(fragmentcode);
- MEM_SAFE_FREE(geometrycode);
-
- /* Cache hit. Reuse the same GPUPass and GPUShader. */
- if (!gpu_pass_is_valid(pass)) {
- /* Shader has already been created but failed to compile. */
- return NULL;
- }
-
- pass->refcount += 1;
- }
- else {
- /* We still create a pass even if shader compilation
- * fails to avoid trying to compile again and again. */
- pass = MEM_callocN(sizeof(GPUPass), "GPUPass");
- pass->shader = NULL;
- pass->refcount = 1;
- pass->hash = hash;
- pass->vertexcode = vertexcode;
- pass->fragmentcode = fragmentcode;
- pass->geometrycode = geometrycode;
- pass->defines = (defines) ? BLI_strdup(defines) : NULL;
- pass->compiled = false;
-
- BLI_spin_lock(&pass_cache_spin);
- if (pass_hash != NULL) {
- /* Add after the first pass having the same hash. */
- pass->next = pass_hash->next;
- pass_hash->next = pass;
- }
- else {
- /* No other pass have same hash, just prepend to the list. */
- BLI_LINKS_PREPEND(pass_cache, pass);
- }
- BLI_spin_unlock(&pass_cache_spin);
- }
-
- return pass;
-}
-
-static int count_active_texture_sampler(GPUShader *shader, const char *source)
-{
- const char *code = source;
-
- /* Remember this is per stage. */
- GSet *sampler_ids = BLI_gset_int_new(__func__);
- int num_samplers = 0;
-
- while ((code = strstr(code, "uniform "))) {
- /* Move past "uniform". */
- code += 7;
- /* Skip following spaces. */
- while (*code == ' ') {
- code++;
- }
- /* Skip "i" from potential isamplers. */
- if (*code == 'i') {
- code++;
- }
- /* Skip following spaces. */
- if (BLI_str_startswith(code, "sampler")) {
- /* Move past "uniform". */
- code += 7;
- /* Skip sampler type suffix. */
- while (!ELEM(*code, ' ', '\0')) {
- code++;
- }
- /* Skip following spaces. */
- while (*code == ' ') {
- code++;
- }
-
- if (*code != '\0') {
- char sampler_name[64];
- code = gpu_str_skip_token(code, sampler_name, sizeof(sampler_name));
- int id = GPU_shader_get_uniform(shader, sampler_name);
-
- if (id == -1) {
- continue;
- }
- /* Catch duplicates. */
- if (BLI_gset_add(sampler_ids, POINTER_FROM_INT(id))) {
- num_samplers++;
- }
- }
- }
- }
-
- BLI_gset_free(sampler_ids, NULL);
-
- return num_samplers;
-}
-
-static bool gpu_pass_shader_validate(GPUPass *pass, GPUShader *shader)
-{
- if (shader == NULL) {
- return false;
- }
-
- /* NOTE: The only drawback of this method is that it will count a sampler
- * used in the fragment shader and only declared (but not used) in the vertex
- * shader as used by both. But this corner case is not happening for now. */
- int vert_samplers_len = count_active_texture_sampler(shader, pass->vertexcode);
- int frag_samplers_len = count_active_texture_sampler(shader, pass->fragmentcode);
-
- int total_samplers_len = vert_samplers_len + frag_samplers_len;
-
- /* Validate against opengl limit. */
- if ((frag_samplers_len > GPU_max_textures_frag()) ||
- (vert_samplers_len > GPU_max_textures_vert())) {
- return false;
- }
-
- if (pass->geometrycode) {
- int geom_samplers_len = count_active_texture_sampler(shader, pass->geometrycode);
- total_samplers_len += geom_samplers_len;
- if (geom_samplers_len > GPU_max_textures_geom()) {
- return false;
- }
- }
-
- return (total_samplers_len <= GPU_max_textures());
-}
-
-bool GPU_pass_compile(GPUPass *pass, const char *shname)
-{
- bool success = true;
- if (!pass->compiled) {
- GPUShader *shader = GPU_shader_create(
- pass->vertexcode, pass->fragmentcode, pass->geometrycode, NULL, pass->defines, shname);
-
- /* NOTE: Some drivers / gpu allows more active samplers than the opengl limit.
- * We need to make sure to count active samplers to avoid undefined behavior. */
- if (!gpu_pass_shader_validate(pass, shader)) {
- success = false;
- if (shader != NULL) {
- fprintf(stderr, "GPUShader: error: too many samplers in shader.\n");
- GPU_shader_free(shader);
- shader = NULL;
- }
- }
- pass->shader = shader;
- pass->compiled = true;
- }
-
- return success;
-}
-
-void GPU_pass_release(GPUPass *pass)
-{
- BLI_assert(pass->refcount > 0);
- pass->refcount--;
-}
-
-static void gpu_pass_free(GPUPass *pass)
-{
- BLI_assert(pass->refcount == 0);
- if (pass->shader) {
- GPU_shader_free(pass->shader);
- }
- MEM_SAFE_FREE(pass->fragmentcode);
- MEM_SAFE_FREE(pass->geometrycode);
- MEM_SAFE_FREE(pass->vertexcode);
- MEM_SAFE_FREE(pass->defines);
- MEM_freeN(pass);
-}
-
-void GPU_pass_cache_garbage_collect(void)
-{
- static int lasttime = 0;
- const int shadercollectrate = 60; /* hardcoded for now. */
- int ctime = (int)PIL_check_seconds_timer();
-
- if (ctime < shadercollectrate + lasttime) {
- return;
- }
-
- lasttime = ctime;
-
- BLI_spin_lock(&pass_cache_spin);
- GPUPass *next, **prev_pass = &pass_cache;
- for (GPUPass *pass = pass_cache; pass; pass = next) {
- next = pass->next;
- if (pass->refcount == 0) {
- /* Remove from list */
- *prev_pass = next;
- gpu_pass_free(pass);
- }
- else {
- prev_pass = &pass->next;
- }
- }
- BLI_spin_unlock(&pass_cache_spin);
-}
-
-void GPU_pass_cache_init(void)
-{
- BLI_spin_init(&pass_cache_spin);
-}
-
-void GPU_pass_cache_free(void)
-{
- BLI_spin_lock(&pass_cache_spin);
- while (pass_cache) {
- GPUPass *next = pass_cache->next;
- gpu_pass_free(pass_cache);
- pass_cache = next;
- }
- BLI_spin_unlock(&pass_cache_spin);
-
- BLI_spin_end(&pass_cache_spin);
-}
-
-/* Module */
-
-void gpu_codegen_init(void)
-{
-}
-
-void gpu_codegen_exit(void)
-{
- BKE_material_defaults_free_gpu();
- GPU_shader_free_builtin_shaders();
-}
diff --git a/source/blender/gpu/intern/gpu_codegen.cc b/source/blender/gpu/intern/gpu_codegen.cc
new file mode 100644
index 00000000000..7777fc19997
--- /dev/null
+++ b/source/blender/gpu/intern/gpu_codegen.cc
@@ -0,0 +1,824 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2005 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup gpu
+ *
+ * Convert material node-trees to GLSL.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_customdata_types.h"
+#include "DNA_image_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_dynstr.h"
+#include "BLI_ghash.h"
+#include "BLI_hash_mm2a.h"
+#include "BLI_link_utils.h"
+#include "BLI_threads.h"
+#include "BLI_utildefines.h"
+
+#include "PIL_time.h"
+
+#include "BKE_material.h"
+#include "BKE_world.h"
+
+#include "GPU_capabilities.h"
+#include "GPU_material.h"
+#include "GPU_shader.h"
+#include "GPU_uniform_buffer.h"
+#include "GPU_vertex_format.h"
+
+#include "BLI_sys_types.h" /* for intptr_t support */
+
+#include "gpu_codegen.h"
+#include "gpu_material_library.h"
+#include "gpu_node_graph.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+#include <sstream>
+#include <string>
+
+extern "C" {
+extern char datatoc_gpu_shader_codegen_lib_glsl[];
+}
+/* -------------------------------------------------------------------- */
+/** \name GPUPass Cache
+ *
+ * Internal shader cache: This prevent the shader recompilation / stall when
+ * using undo/redo AND also allows for GPUPass reuse if the Shader code is the
+ * same for 2 different Materials. Unused GPUPasses are free by Garbage collection.
+ */
+
+/* Only use one linklist that contains the GPUPasses grouped by hash. */
+static GPUPass *pass_cache = nullptr;
+static SpinLock pass_cache_spin;
+
+/* Search by hash only. Return first pass with the same hash.
+ * There is hash collision if (pass->next && pass->next->hash == hash) */
+static GPUPass *gpu_pass_cache_lookup(uint32_t hash)
+{
+ BLI_spin_lock(&pass_cache_spin);
+ /* Could be optimized with a Lookup table. */
+ for (GPUPass *pass = pass_cache; pass; pass = pass->next) {
+ if (pass->hash == hash) {
+ BLI_spin_unlock(&pass_cache_spin);
+ return pass;
+ }
+ }
+ BLI_spin_unlock(&pass_cache_spin);
+ return nullptr;
+}
+
+static void gpu_pass_cache_insert_after(GPUPass *node, GPUPass *pass)
+{
+ BLI_spin_lock(&pass_cache_spin);
+ if (node != nullptr) {
+ /* Add after the first pass having the same hash. */
+ pass->next = node->next;
+ node->next = pass;
+ }
+ else {
+ /* No other pass have same hash, just prepend to the list. */
+ BLI_LINKS_PREPEND(pass_cache, pass);
+ }
+ BLI_spin_unlock(&pass_cache_spin);
+}
+
+/* Check all possible passes with the same hash. */
+static GPUPass *gpu_pass_cache_resolve_collision(GPUPass *pass,
+ GPUShaderSource *source,
+ uint32_t hash)
+{
+ BLI_spin_lock(&pass_cache_spin);
+ /* Collision, need to `strcmp` the whole shader. */
+ for (; pass && (pass->hash == hash); pass = pass->next) {
+ if ((source->defines != nullptr) && (!STREQ(pass->source.defines, source->defines))) {
+ /* Pass */
+ }
+ else if ((source->geometry != nullptr) && (!STREQ(pass->source.geometry, source->geometry))) {
+ /* Pass */
+ }
+ else if (STREQ(pass->source.fragment, source->fragment) &&
+ STREQ(pass->source.vertex, source->vertex)) {
+ BLI_spin_unlock(&pass_cache_spin);
+ return pass;
+ }
+ }
+ BLI_spin_unlock(&pass_cache_spin);
+ return nullptr;
+}
+
+static bool gpu_pass_is_valid(GPUPass *pass)
+{
+ /* Shader is not null if compilation is successful. */
+ return (pass->compiled == false || pass->shader != nullptr);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Type > string conversion
+ * \{ */
+
+static std::ostream &operator<<(std::ostream &stream, const GPUInput *input)
+{
+ switch (input->source) {
+ case GPU_SOURCE_OUTPUT:
+ return stream << "tmp" << input->id;
+ case GPU_SOURCE_CONSTANT:
+ return stream << "cons" << input->id;
+ case GPU_SOURCE_UNIFORM:
+ return stream << "unf" << input->id;
+ case GPU_SOURCE_ATTR:
+ return stream << "var" << input->attr->id;
+ case GPU_SOURCE_UNIFORM_ATTR:
+ return stream << "UNIFORM_ATTR_UBO.attr" << input->uniform_attr->id;
+ case GPU_SOURCE_STRUCT:
+ return stream << "strct" << input->id;
+ case GPU_SOURCE_TEX:
+ return stream << input->texture->sampler_name;
+ case GPU_SOURCE_TEX_TILED_MAPPING:
+ return stream << input->texture->tiled_mapping_name;
+ case GPU_SOURCE_VOLUME_GRID:
+ return stream << input->volume_grid->sampler_name;
+ case GPU_SOURCE_VOLUME_GRID_TRANSFORM:
+ return stream << input->volume_grid->transform_name;
+ default:
+ BLI_assert(0);
+ return stream;
+ }
+}
+
+static std::ostream &operator<<(std::ostream &stream, const GPUOutput *output)
+{
+ return stream << "tmp" << output->id;
+}
+
+static std::ostream &operator<<(std::ostream &stream, const eGPUType &type)
+{
+ return stream << gpu_data_type_to_string(type);
+}
+
+/* Trick type to change overload and keep a somewhat nice syntax. */
+struct GPUConstant : public GPUInput {
+};
+
+/* Print data constructor (i.e: vec2(1.0f, 1.0f)). */
+static std::ostream &operator<<(std::ostream &stream, const GPUConstant *input)
+{
+ stream << input->type << "(";
+ for (int i = 0; i < input->type; i++) {
+ char formated_float[32];
+ /* Print with the maximum precision for single precision float using scientific notation.
+ * See https://stackoverflow.com/questions/16839658/#answer-21162120 */
+ SNPRINTF(formated_float, "%.9g", input->vec[i]);
+ stream << formated_float;
+ if (i < input->type - 1) {
+ stream << ", ";
+ }
+ }
+ stream << ")";
+ return stream;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GLSL code generation
+ * \{ */
+
+class GPUCodegen {
+ public:
+ GPUMaterial &mat;
+ GPUNodeGraph &graph;
+ GPUCodegenOutput output = {};
+
+ private:
+ uint32_t hash_ = 0;
+ BLI_HashMurmur2A hm2a_;
+ ListBase ubo_inputs_ = {nullptr, nullptr};
+
+ public:
+ GPUCodegen(GPUMaterial *mat_, GPUNodeGraph *graph_) : mat(*mat_), graph(*graph_)
+ {
+ BLI_hash_mm2a_init(&hm2a_, GPU_material_uuid_get(&mat));
+ }
+
+ ~GPUCodegen()
+ {
+ MEM_SAFE_FREE(output.attribs_declare);
+ MEM_SAFE_FREE(output.attribs_interface);
+ MEM_SAFE_FREE(output.attribs_load);
+ MEM_SAFE_FREE(output.attribs_passthrough);
+ MEM_SAFE_FREE(output.surface);
+ MEM_SAFE_FREE(output.volume);
+ MEM_SAFE_FREE(output.thickness);
+ MEM_SAFE_FREE(output.displacement);
+ MEM_SAFE_FREE(output.uniforms);
+ MEM_SAFE_FREE(output.library);
+ BLI_freelistN(&ubo_inputs_);
+ };
+
+ void generate_graphs();
+ void generate_uniform_buffer();
+ void generate_attribs();
+ void generate_resources();
+ void generate_library();
+
+ uint32_t hash_get() const
+ {
+ return hash_;
+ }
+
+ private:
+ void set_unique_ids();
+
+ void node_serialize(std::stringstream &eval_ss, const GPUNode *node);
+ char *graph_serialize(eGPUNodeTag tree_tag, GPUNodeLink *output_link);
+
+ static char *extract_c_str(std::stringstream &stream)
+ {
+ auto string = stream.str();
+ return BLI_strdup(string.c_str());
+ }
+};
+
+static char attr_prefix_get(CustomDataType type)
+{
+ switch (type) {
+ case CD_MTFACE:
+ return 'u';
+ case CD_TANGENT:
+ return 't';
+ case CD_MCOL:
+ return 'c';
+ case CD_PROP_COLOR:
+ return 'c';
+ case CD_AUTO_FROM_NAME:
+ return 'a';
+ case CD_HAIRLENGTH:
+ return 'l';
+ default:
+ BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!");
+ return '\0';
+ }
+}
+
+void GPUCodegen::generate_attribs()
+{
+ if (BLI_listbase_is_empty(&graph.attributes)) {
+ output.attribs_declare = nullptr;
+ output.attribs_interface = nullptr;
+ output.attribs_load = nullptr;
+ output.attribs_passthrough = nullptr;
+ return;
+ }
+
+ /* Input declaration, loading / assignment to interface and geometry shader passthrough. */
+ std::stringstream decl_ss, iface_ss, load_ss, pass_ss;
+
+ LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph.attributes) {
+ eGPUType type = attr->gputype;
+ eGPUType in_type = attr->gputype;
+ char name[GPU_MAX_SAFE_ATTR_NAME + 1] = "orco";
+ if (attr->type == CD_ORCO) {
+ /* OPTI : orco is computed from local positions, but only if no modifier is present. */
+ GPU_material_flag_set(&mat, GPU_MATFLAG_OBJECT_INFO);
+ /* Need vec4 to detect usage of default attribute. */
+ in_type = GPU_VEC4;
+ }
+ else {
+ name[0] = attr_prefix_get((CustomDataType)(attr->type));
+ name[1] = '\0';
+ }
+
+ if (attr->name[0] != '\0') {
+ /* XXX FIXME : see notes in mesh_render_data_create() */
+ GPU_vertformat_safe_attr_name(attr->name, &name[1], GPU_MAX_SAFE_ATTR_NAME);
+ }
+ /* NOTE : Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */
+ decl_ss << in_type << " " << name << ";\n";
+
+ iface_ss << type << " var" << attr->id << ";\n";
+
+ load_ss << "var" << attr->id;
+
+ switch (attr->type) {
+ case CD_ORCO:
+ load_ss << " = attr_load_orco(" << name << ");\n";
+ break;
+ case CD_TANGENT:
+ load_ss << " = attr_load_tangent(" << name << ");\n";
+ break;
+ case CD_MTFACE:
+ load_ss << " = attr_load_uv(" << name << ");\n";
+ break;
+ case CD_MCOL:
+ load_ss << " = attr_load_color(" << name << ");\n";
+ break;
+ default:
+ load_ss << " = attr_load_" << type << "(" << name << ");\n";
+ break;
+ }
+ pass_ss << "attr_out.var" << attr->id << " = attr_in[vert_id].var" << attr->id << ";\n";
+ }
+
+ output.attribs_declare = extract_c_str(decl_ss);
+ output.attribs_interface = extract_c_str(iface_ss);
+ output.attribs_load = extract_c_str(load_ss);
+ output.attribs_passthrough = extract_c_str(pass_ss);
+}
+
+void GPUCodegen::generate_resources()
+{
+ std::stringstream ss;
+
+ /* Textures. */
+ LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph.textures) {
+ if (tex->colorband) {
+ ss << "uniform sampler1DArray " << tex->sampler_name << ";\n";
+ }
+ else if (tex->tiled_mapping_name[0] != '\0') {
+ ss << "uniform sampler2DArray " << tex->sampler_name << ";\n";
+ ss << "uniform sampler1DArray " << tex->tiled_mapping_name << ";\n";
+ }
+ else {
+ ss << "uniform sampler2D " << tex->sampler_name << ";\n";
+ }
+ }
+ /* Volume Grids. */
+ LISTBASE_FOREACH (GPUMaterialVolumeGrid *, grid, &graph.volume_grids) {
+ ss << "uniform sampler3D " << grid->sampler_name << ";\n";
+ /* TODO(fclem) global uniform. To put in a UBO. */
+ ss << "uniform mat4 " << grid->transform_name << " = mat4(0.0);\n";
+ }
+
+ if (!BLI_listbase_is_empty(&ubo_inputs_)) {
+ /* NOTE: generate_uniform_buffer() should have sorted the inputs before this. */
+ ss << "layout(std140) uniform nodeTree {\n";
+ LISTBASE_FOREACH (LinkData *, link, &ubo_inputs_) {
+ GPUInput *input = (GPUInput *)(link->data);
+ ss << input->type << " " << input << ";\n";
+ }
+ ss << "};\n\n";
+ }
+
+ if (!BLI_listbase_is_empty(&graph.uniform_attrs.list)) {
+ GPU_material_flag_set(&mat, GPU_MATFLAG_UNIFORMS_ATTRIB);
+
+ ss << "struct UniformAttributes {\n";
+ LISTBASE_FOREACH (GPUUniformAttr *, attr, &graph.uniform_attrs.list) {
+ ss << "vec4 attr" << attr->id << ";\n";
+ }
+ ss << "};\n\n";
+ }
+
+ output.uniforms = extract_c_str(ss);
+}
+
+void GPUCodegen::generate_library()
+{
+ output.library = gpu_material_library_generate_code(graph.used_libraries);
+}
+
+void GPUCodegen::node_serialize(std::stringstream &eval_ss, const GPUNode *node)
+{
+ /* Declare constants. */
+ LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
+ switch (input->source) {
+ case GPU_SOURCE_STRUCT:
+ eval_ss << input->type << " " << input << " = CLOSURE_DEFAULT;\n";
+ break;
+ case GPU_SOURCE_CONSTANT:
+ eval_ss << input->type << " " << input << " = " << (GPUConstant *)input << ";\n";
+ break;
+ default:
+ break;
+ }
+ }
+ /* Declare temporary variables for node output storage. */
+ LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) {
+ eval_ss << output->type << " " << output << ";\n";
+ }
+
+ /* Function call. */
+ eval_ss << node->name << "(";
+ /* Input arguments. */
+ LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
+ switch (input->source) {
+ case GPU_SOURCE_OUTPUT:
+ case GPU_SOURCE_ATTR: {
+ /* These inputs can have non matching types. Do conversion. */
+ eGPUType to = input->type;
+ eGPUType from = (input->source == GPU_SOURCE_ATTR) ? input->attr->gputype :
+ input->link->output->type;
+ if (from != to) {
+ /* Use defines declared inside codegen_lib (i.e: vec4_from_float). */
+ eval_ss << to << "_from_" << from << "(";
+ }
+
+ if (input->source == GPU_SOURCE_ATTR) {
+ eval_ss << input;
+ }
+ else {
+ eval_ss << input->link->output;
+ }
+
+ if (from != to) {
+ eval_ss << ")";
+ }
+ break;
+ }
+ default:
+ eval_ss << input;
+ break;
+ }
+ eval_ss << ", ";
+ }
+ /* Output arguments. */
+ LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) {
+ eval_ss << output;
+ if (output->next) {
+ eval_ss << ", ";
+ }
+ }
+ eval_ss << ");\n\n";
+}
+
+char *GPUCodegen::graph_serialize(eGPUNodeTag tree_tag, GPUNodeLink *output_link)
+{
+ if (output_link == nullptr) {
+ return nullptr;
+ }
+
+ std::stringstream eval_ss;
+ /* Render engine implement this function if needed. */
+ eval_ss << "ntree_eval_init();\n\n";
+ /* NOTE: The node order is already top to bottom (or left to right in node editor)
+ * because of the evaluation order inside ntreeExecGPUNodes(). */
+ LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) {
+ if ((node->tag & tree_tag) == 0 || (node->tag & GPU_NODE_TAG_EVAL) != 0) {
+ continue;
+ }
+ node_serialize(eval_ss, node);
+ }
+ /* Render engine implement this function if needed. */
+ eval_ss << "ntree_eval_weights();\n\n";
+ /* Output eval function at the end. */
+ LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) {
+ if ((node->tag & tree_tag) == 0 || (node->tag & GPU_NODE_TAG_EVAL) == 0) {
+ continue;
+ }
+ node_serialize(eval_ss, node);
+ }
+ eval_ss << "return " << output_link->output << ";\n";
+
+ char *eval_c_str = extract_c_str(eval_ss);
+ BLI_hash_mm2a_add(&hm2a_, (uchar *)eval_c_str, eval_ss.str().size());
+ return eval_c_str;
+}
+
+void GPUCodegen::generate_uniform_buffer()
+{
+ /* Extract uniform inputs. */
+ LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) {
+ LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
+ if (input->source == GPU_SOURCE_UNIFORM && !input->link) {
+ /* We handle the UBO uniforms separately. */
+ BLI_addtail(&ubo_inputs_, BLI_genericNodeN(input));
+ }
+ }
+ }
+ if (!BLI_listbase_is_empty(&ubo_inputs_)) {
+ /* This sorts the inputs based on size. */
+ GPU_material_uniform_buffer_create(&mat, &ubo_inputs_);
+ }
+}
+
+/* Sets id for unique names for all inputs, resources and temp variables. */
+void GPUCodegen::set_unique_ids()
+{
+ int id = 1;
+ LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) {
+ LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
+ input->id = id++;
+ }
+ LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) {
+ output->id = id++;
+ }
+ }
+}
+
+void GPUCodegen::generate_graphs()
+{
+ set_unique_ids();
+
+ output.surface = graph_serialize(GPU_NODE_TAG_SURFACE, graph.outlink_surface);
+ output.volume = graph_serialize(GPU_NODE_TAG_VOLUME, graph.outlink_volume);
+ output.displacement = graph_serialize(GPU_NODE_TAG_DISPLACEMENT, graph.outlink_displacement);
+ output.thickness = graph_serialize(GPU_NODE_TAG_THICKNESS, graph.outlink_thickness);
+
+ LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph.attributes) {
+ BLI_hash_mm2a_add(&hm2a_, (uchar *)attr->name, strlen(attr->name));
+ }
+
+ hash_ = BLI_hash_mm2a_end(&hm2a_);
+}
+
+GPUPass *GPU_generate_pass(GPUMaterial *material,
+ GPUNodeGraph *graph,
+ GPUCodegenCallbackFn finalize_source_cb,
+ void *thunk)
+{
+ /* Prune the unused nodes and extract attributes before compiling so the
+ * generated VBOs are ready to accept the future shader. */
+ gpu_node_graph_prune_unused(graph);
+ gpu_node_graph_finalize_uniform_attrs(graph);
+
+ GPUCodegen codegen(material, graph);
+ codegen.generate_graphs();
+ codegen.generate_uniform_buffer();
+
+ /* Cache lookup: Reuse shaders already compiled. */
+ GPUPass *pass_hash = gpu_pass_cache_lookup(codegen.hash_get());
+
+ /* FIXME(fclem): This is broken. Since we only check for the hash and not the full source
+ * there is no way to have a collision currently. Some advocated to only use a bigger hash. */
+ if (pass_hash && (pass_hash->next == nullptr || pass_hash->next->hash != codegen.hash_get())) {
+ if (!gpu_pass_is_valid(pass_hash)) {
+ /* Shader has already been created but failed to compile. */
+ return nullptr;
+ }
+ /* No collision, just return the pass. */
+ pass_hash->refcount += 1;
+ return pass_hash;
+ }
+
+ /* Either the shader is not compiled or there is a hash collision...
+ * continue generating the shader strings. */
+ codegen.generate_attribs();
+ codegen.generate_resources();
+ codegen.generate_library();
+
+ /* Make engine add its own code and implement the generated functions. */
+ GPUShaderSource source = finalize_source_cb(thunk, material, &codegen.output);
+
+ GPUPass *pass = nullptr;
+ if (pass_hash) {
+ /* Cache lookup: Reuse shaders already compiled. */
+ pass = gpu_pass_cache_resolve_collision(pass_hash, &source, codegen.hash_get());
+ }
+
+ if (pass) {
+ MEM_SAFE_FREE(source.vertex);
+ MEM_SAFE_FREE(source.geometry);
+ MEM_SAFE_FREE(source.fragment);
+ MEM_SAFE_FREE(source.defines);
+ /* Cache hit. Reuse the same GPUPass and GPUShader. */
+ if (!gpu_pass_is_valid(pass)) {
+ /* Shader has already been created but failed to compile. */
+ return nullptr;
+ }
+ pass->refcount += 1;
+ }
+ else {
+ /* We still create a pass even if shader compilation
+ * fails to avoid trying to compile again and again. */
+ pass = (GPUPass *)MEM_callocN(sizeof(GPUPass), "GPUPass");
+ pass->shader = nullptr;
+ pass->refcount = 1;
+ pass->hash = codegen.hash_get();
+ pass->source = source;
+ pass->compiled = false;
+
+ gpu_pass_cache_insert_after(pass_hash, pass);
+ }
+ return pass;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Compilation
+ * \{ */
+
+static int count_active_texture_sampler(GPUShader *shader, char *source)
+{
+ const char *code = source;
+
+ /* Remember this is per stage. */
+ GSet *sampler_ids = BLI_gset_int_new(__func__);
+ int num_samplers = 0;
+
+ while ((code = strstr(code, "uniform "))) {
+ /* Move past "uniform". */
+ code += 7;
+ /* Skip following spaces. */
+ while (*code == ' ') {
+ code++;
+ }
+ /* Skip "i" from potential isamplers. */
+ if (*code == 'i') {
+ code++;
+ }
+ /* Skip following spaces. */
+ if (BLI_str_startswith(code, "sampler")) {
+ /* Move past "uniform". */
+ code += 7;
+ /* Skip sampler type suffix. */
+ while (!ELEM(*code, ' ', '\0')) {
+ code++;
+ }
+ /* Skip following spaces. */
+ while (*code == ' ') {
+ code++;
+ }
+
+ if (*code != '\0') {
+ char sampler_name[64];
+ code = gpu_str_skip_token(code, sampler_name, sizeof(sampler_name));
+ int id = GPU_shader_get_uniform(shader, sampler_name);
+
+ if (id == -1) {
+ continue;
+ }
+ /* Catch duplicates. */
+ if (BLI_gset_add(sampler_ids, POINTER_FROM_INT(id))) {
+ num_samplers++;
+ }
+ }
+ }
+ }
+
+ BLI_gset_free(sampler_ids, nullptr);
+
+ return num_samplers;
+}
+
+static bool gpu_pass_shader_validate(GPUPass *pass, GPUShader *shader)
+{
+ if (shader == nullptr) {
+ return false;
+ }
+
+ /* NOTE: The only drawback of this method is that it will count a sampler
+ * used in the fragment shader and only declared (but not used) in the vertex
+ * shader as used by both. But this corner case is not happening for now. */
+ int vert_samplers_len = count_active_texture_sampler(shader, pass->source.vertex);
+ int frag_samplers_len = count_active_texture_sampler(shader, pass->source.fragment);
+
+ int total_samplers_len = vert_samplers_len + frag_samplers_len;
+
+ /* Validate against opengl limit. */
+ if ((frag_samplers_len > GPU_max_textures_frag()) ||
+ (vert_samplers_len > GPU_max_textures_vert())) {
+ return false;
+ }
+
+ if (pass->source.geometry) {
+ int geom_samplers_len = count_active_texture_sampler(shader, pass->source.geometry);
+ total_samplers_len += geom_samplers_len;
+ if (geom_samplers_len > GPU_max_textures_geom()) {
+ return false;
+ }
+ }
+
+ return (total_samplers_len <= GPU_max_textures());
+}
+
+bool GPU_pass_compile(GPUPass *pass, const char *shname)
+{
+ bool success = true;
+ if (!pass->compiled) {
+ GPUShader *shader = GPU_shader_create(pass->source.vertex,
+ pass->source.fragment,
+ pass->source.geometry,
+ nullptr,
+ pass->source.defines,
+ shname);
+
+ /* NOTE: Some drivers / gpu allows more active samplers than the opengl limit.
+ * We need to make sure to count active samplers to avoid undefined behavior. */
+ if (!gpu_pass_shader_validate(pass, shader)) {
+ success = false;
+ if (shader != nullptr) {
+ fprintf(stderr, "GPUShader: error: too many samplers in shader.\n");
+ GPU_shader_free(shader);
+ shader = nullptr;
+ }
+ }
+ pass->shader = shader;
+ pass->compiled = true;
+ }
+ return success;
+}
+
+GPUShader *GPU_pass_shader_get(GPUPass *pass)
+{
+ return pass->shader;
+}
+
+void GPU_pass_release(GPUPass *pass)
+{
+ BLI_assert(pass->refcount > 0);
+ pass->refcount--;
+}
+
+static void gpu_pass_free(GPUPass *pass)
+{
+ BLI_assert(pass->refcount == 0);
+ if (pass->shader) {
+ GPU_shader_free(pass->shader);
+ }
+ MEM_SAFE_FREE(pass->source.vertex);
+ MEM_SAFE_FREE(pass->source.fragment);
+ MEM_SAFE_FREE(pass->source.geometry);
+ MEM_SAFE_FREE(pass->source.defines);
+ MEM_freeN(pass);
+}
+
+void GPU_pass_cache_garbage_collect(void)
+{
+ static int lasttime = 0;
+ const int shadercollectrate = 60; /* hardcoded for now. */
+ int ctime = (int)PIL_check_seconds_timer();
+
+ if (ctime < shadercollectrate + lasttime) {
+ return;
+ }
+
+ lasttime = ctime;
+
+ BLI_spin_lock(&pass_cache_spin);
+ GPUPass *next, **prev_pass = &pass_cache;
+ for (GPUPass *pass = pass_cache; pass; pass = next) {
+ next = pass->next;
+ if (pass->refcount == 0) {
+ /* Remove from list */
+ *prev_pass = next;
+ gpu_pass_free(pass);
+ }
+ else {
+ prev_pass = &pass->next;
+ }
+ }
+ BLI_spin_unlock(&pass_cache_spin);
+}
+
+void GPU_pass_cache_init(void)
+{
+ BLI_spin_init(&pass_cache_spin);
+}
+
+void GPU_pass_cache_free(void)
+{
+ BLI_spin_lock(&pass_cache_spin);
+ while (pass_cache) {
+ GPUPass *next = pass_cache->next;
+ gpu_pass_free(pass_cache);
+ pass_cache = next;
+ }
+ BLI_spin_unlock(&pass_cache_spin);
+
+ BLI_spin_end(&pass_cache_spin);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Module
+ * \{ */
+
+void gpu_codegen_init(void)
+{
+}
+
+void gpu_codegen_exit(void)
+{
+ BKE_world_defaults_free_gpu();
+ BKE_material_defaults_free_gpu();
+ GPU_shader_free_builtin_shaders();
+}
+
+/** \} */ \ No newline at end of file
diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h
index f3d58a55814..cac92d1a3c3 100644
--- a/source/blender/gpu/intern/gpu_codegen.h
+++ b/source/blender/gpu/intern/gpu_codegen.h
@@ -25,22 +25,20 @@
#pragma once
+#include "GPU_material.h"
+#include "GPU_shader.h"
+
#ifdef __cplusplus
extern "C" {
#endif
-struct GPUMaterial;
struct GPUNodeGraph;
-struct GPUShader;
typedef struct GPUPass {
struct GPUPass *next;
- struct GPUShader *shader;
- char *fragmentcode;
- char *geometrycode;
- char *vertexcode;
- char *defines;
+ GPUShader *shader;
+ GPUShaderSource source;
uint refcount; /* Orphaned GPUPasses gets freed by the garbage collector. */
uint32_t hash; /* Identity hash generated from all GLSL code. */
bool compiled; /* Did we already tried to compile the attached GPUShader. */
@@ -48,13 +46,11 @@ typedef struct GPUPass {
/* Pass */
-GPUPass *GPU_generate_pass(struct GPUMaterial *material,
+GPUPass *GPU_generate_pass(GPUMaterial *material,
struct GPUNodeGraph *graph,
- const char *vert_code,
- const char *geom_code,
- const char *frag_lib,
- const char *defines);
-struct GPUShader *GPU_pass_shader_get(GPUPass *pass);
+ GPUCodegenCallbackFn finalize_source_cb,
+ void *thunk);
+GPUShader *GPU_pass_shader_get(GPUPass *pass);
bool GPU_pass_compile(GPUPass *pass, const char *shname);
void GPU_pass_release(GPUPass *pass);
diff --git a/source/blender/gpu/intern/gpu_framebuffer_private.hh b/source/blender/gpu/intern/gpu_framebuffer_private.hh
index a936e889e57..e7505258d2a 100644
--- a/source/blender/gpu/intern/gpu_framebuffer_private.hh
+++ b/source/blender/gpu/intern/gpu_framebuffer_private.hh
@@ -43,8 +43,9 @@ typedef enum GPUAttachmentType : int {
GPU_FB_COLOR_ATTACHMENT3,
GPU_FB_COLOR_ATTACHMENT4,
GPU_FB_COLOR_ATTACHMENT5,
- /* Number of maximum output slots.
- * We support 6 outputs for now (usually we wouldn't need more to preserve fill rate). */
+ GPU_FB_COLOR_ATTACHMENT6,
+ GPU_FB_COLOR_ATTACHMENT7,
+ /* Number of maximum output slots. */
/* Keep in mind that GL max is GL_MAX_DRAW_BUFFERS and is at least 8, corresponding to
* the maximum number of COLOR attachments specified by glDrawBuffers. */
GPU_FB_MAX_ATTACHMENT,
diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c
index 11c61e4cf72..ca1f45f7238 100644
--- a/source/blender/gpu/intern/gpu_material.c
+++ b/source/blender/gpu/intern/gpu_material.c
@@ -32,7 +32,6 @@
#include "DNA_scene_types.h"
#include "DNA_world_types.h"
-#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_string.h"
@@ -43,6 +42,7 @@
#include "BKE_material.h"
#include "BKE_node.h"
#include "BKE_scene.h"
+#include "BKE_world.h"
#include "NOD_shader.h"
@@ -65,45 +65,31 @@ typedef struct GPUColorBandBuilder {
} GPUColorBandBuilder;
struct GPUMaterial {
- Scene *scene; /* DEPRECATED was only useful for lights. */
- Material *ma;
-
- eGPUMaterialStatus status;
-
- const void *engine_type; /* attached engine type */
- int options; /* to identify shader variations (shadow, probe, world background...) */
- bool is_volume_shader; /* is volumetric shader */
-
- /* Nodes */
- GPUNodeGraph graph;
-
- /* for binding the material */
+ /* Contains GPUShader and source code for deferred compilation.
+ * Can be shared between similar material (i.e: sharing same nodetree topology). */
GPUPass *pass;
+ /** UBOs for this material parameters. */
+ GPUUniformBuf *ubo;
+ /** Compilation status. Do not use if shader is not GPU_MAT_SUCCESS. */
+ eGPUMaterialStatus status;
+ /** Some flags about the nodetree & the needed resources. */
+ eGPUMaterialFlag flag;
+ /* Identify shader variations (shadow, probe, world background...).
+ * Should be unique even across render engines. */
+ uint64_t uuid;
+ /** Object type for attribute fetching. */
+ bool is_volume_shader;
+
+ /** DEPRECATED Currently only used for deferred compilation. */
+ Scene *scene;
+ /** Source material, might be null. */
+ Material *ma;
+ /** 1D Texture array containing all color bands. */
+ GPUTexture *coba_tex;
- /* XXX: Should be in Material. But it depends on the output node
- * used and since the output selection is different for GPUMaterial...
- */
- bool has_volume_output;
- bool has_surface_output;
-
- /* Only used by Eevee to know which BSDF are used. */
- eGPUMatFlag flag;
-
- /* Used by 2.8 pipeline */
- GPUUniformBuf *ubo; /* UBOs for shader uniforms. */
-
- /* Eevee SSS */
- GPUUniformBuf *sss_profile; /* UBO containing SSS profile. */
- GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */
- float sss_enabled;
- float sss_radii[3];
- int sss_samples;
- bool sss_dirty;
-
- GPUTexture *coba_tex; /* 1D Texture array containing all color bands. */
GPUColorBandBuilder *coba_builder;
-
- GSet *used_libraries;
+ /* Low level node graph(s). Also contains resources needed by the material. */
+ GPUNodeGraph graph;
#ifndef NDEBUG
char name[64];
@@ -175,17 +161,9 @@ static void gpu_material_free_single(GPUMaterial *material)
if (material->ubo != NULL) {
GPU_uniformbuf_free(material->ubo);
}
- if (material->sss_tex_profile != NULL) {
- GPU_texture_free(material->sss_tex_profile);
- }
- if (material->sss_profile != NULL) {
- GPU_uniformbuf_free(material->sss_profile);
- }
if (material->coba_tex != NULL) {
GPU_texture_free(material->coba_tex);
}
-
- BLI_gset_free(material->used_libraries, NULL);
}
void GPU_material_free(ListBase *gpumaterial)
@@ -233,292 +211,52 @@ void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs)
material->ubo = GPU_uniformbuf_create_from_list(inputs, name);
}
-/* Eevee Subsurface scattering. */
-/* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */
-
-#define SSS_SAMPLES 65
-#define SSS_EXPONENT 2.0f /* Importance sampling exponent */
-
-typedef struct GPUSssKernelData {
- float kernel[SSS_SAMPLES][4];
- float param[3], max_radius;
- int samples;
- int pad[3];
-} GPUSssKernelData;
-
-BLI_STATIC_ASSERT_ALIGN(GPUSssKernelData, 16)
-
-static void sss_calculate_offsets(GPUSssKernelData *kd, int count, float exponent)
+ListBase GPU_material_attributes(GPUMaterial *material)
{
- float step = 2.0f / (float)(count - 1);
- for (int i = 0; i < count; i++) {
- float o = ((float)i) * step - 1.0f;
- float sign = (o < 0.0f) ? -1.0f : 1.0f;
- float ofs = sign * fabsf(powf(o, exponent));
- kd->kernel[i][3] = ofs;
- }
+ return material->graph.attributes;
}
-#define BURLEY_TRUNCATE 16.0f
-#define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE)
-static float burley_profile(float r, float d)
+ListBase GPU_material_textures(GPUMaterial *material)
{
- float exp_r_3_d = expf(-r / (3.0f * d));
- float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d;
- return (exp_r_d + exp_r_3_d) / (4.0f * d);
+ return material->graph.textures;
}
-static float eval_profile(float r, float param)
+ListBase GPU_material_volume_grids(GPUMaterial *material)
{
- r = fabsf(r);
- return burley_profile(r, param) / BURLEY_TRUNCATE_CDF;
+ return material->graph.volume_grids;
}
-/* Resolution for each sample of the precomputed kernel profile */
-#define INTEGRAL_RESOLUTION 32
-static float eval_integral(float x0, float x1, float param)
+GPUUniformAttrList *GPU_material_uniform_attributes(GPUMaterial *material)
{
- const float range = x1 - x0;
- const float step = range / INTEGRAL_RESOLUTION;
- float integral = 0.0f;
-
- for (int i = 0; i < INTEGRAL_RESOLUTION; i++) {
- float x = x0 + range * ((float)i + 0.5f) / (float)INTEGRAL_RESOLUTION;
- float y = eval_profile(x, param);
- integral += y * step;
- }
-
- return integral;
+ GPUUniformAttrList *attrs = &material->graph.uniform_attrs;
+ return attrs->count > 0 ? attrs : NULL;
}
-#undef INTEGRAL_RESOLUTION
-static void compute_sss_kernel(GPUSssKernelData *kd, const float radii[3], int sample_len)
+void GPU_material_output_surface(GPUMaterial *material, GPUNodeLink *link)
{
- float rad[3];
- /* Minimum radius */
- rad[0] = MAX2(radii[0], 1e-15f);
- rad[1] = MAX2(radii[1], 1e-15f);
- rad[2] = MAX2(radii[2], 1e-15f);
-
- /* Christensen-Burley fitting */
- float l[3], d[3];
-
- mul_v3_v3fl(l, rad, 0.25f * M_1_PI);
- const float A = 1.0f;
- const float s = 1.9f - A + 3.5f * (A - 0.8f) * (A - 0.8f);
- /* XXX 0.6f Out of nowhere to match cycles! Empirical! Can be tweak better. */
- mul_v3_v3fl(d, l, 0.6f / s);
- mul_v3_v3fl(rad, d, BURLEY_TRUNCATE);
- kd->max_radius = MAX3(rad[0], rad[1], rad[2]);
-
- copy_v3_v3(kd->param, d);
-
- /* Compute samples locations on the 1d kernel [-1..1] */
- sss_calculate_offsets(kd, sample_len, SSS_EXPONENT);
-
- /* Weights sum for normalization */
- float sum[3] = {0.0f, 0.0f, 0.0f};
-
- /* Compute integral of each sample footprint */
- for (int i = 0; i < sample_len; i++) {
- float x0, x1;
-
- if (i == 0) {
- x0 = kd->kernel[0][3] - fabsf(kd->kernel[0][3] - kd->kernel[1][3]) / 2.0f;
- }
- else {
- x0 = (kd->kernel[i - 1][3] + kd->kernel[i][3]) / 2.0f;
- }
-
- if (i == sample_len - 1) {
- x1 = kd->kernel[sample_len - 1][3] +
- fabsf(kd->kernel[sample_len - 2][3] - kd->kernel[sample_len - 1][3]) / 2.0f;
- }
- else {
- x1 = (kd->kernel[i][3] + kd->kernel[i + 1][3]) / 2.0f;
- }
-
- x0 *= kd->max_radius;
- x1 *= kd->max_radius;
-
- kd->kernel[i][0] = eval_integral(x0, x1, kd->param[0]);
- kd->kernel[i][1] = eval_integral(x0, x1, kd->param[1]);
- kd->kernel[i][2] = eval_integral(x0, x1, kd->param[2]);
-
- sum[0] += kd->kernel[i][0];
- sum[1] += kd->kernel[i][1];
- sum[2] += kd->kernel[i][2];
+ if (!material->graph.outlink_surface) {
+ material->graph.outlink_surface = link;
}
-
- for (int i = 0; i < 3; i++) {
- if (sum[i] > 0.0f) {
- /* Normalize */
- for (int j = 0; j < sample_len; j++) {
- kd->kernel[j][i] /= sum[i];
- }
- }
- else {
- /* Avoid 0 kernel sum. */
- kd->kernel[sample_len / 2][i] = 1.0f;
- }
- }
-
- /* Put center sample at the start of the array (to sample first) */
- float tmpv[4];
- copy_v4_v4(tmpv, kd->kernel[sample_len / 2]);
- for (int i = sample_len / 2; i > 0; i--) {
- copy_v4_v4(kd->kernel[i], kd->kernel[i - 1]);
- }
- copy_v4_v4(kd->kernel[0], tmpv);
-
- kd->samples = sample_len;
}
-#define INTEGRAL_RESOLUTION 512
-static void compute_sss_translucence_kernel(const GPUSssKernelData *kd,
- int resolution,
- float **output)
+void GPU_material_output_volume(GPUMaterial *material, GPUNodeLink *link)
{
- float(*texels)[4];
- texels = MEM_callocN(sizeof(float[4]) * resolution, "compute_sss_translucence_kernel");
- *output = (float *)texels;
-
- /* Last texel should be black, hence the - 1. */
- for (int i = 0; i < resolution - 1; i++) {
- /* Distance from surface. */
- float d = kd->max_radius * ((float)i + 0.00001f) / ((float)resolution);
-
- /* For each distance d we compute the radiance incoming from an hypothetical parallel plane. */
- /* Compute radius of the footprint on the hypothetical plane. */
- float r_fp = sqrtf(kd->max_radius * kd->max_radius - d * d);
- float r_step = r_fp / INTEGRAL_RESOLUTION;
- float area_accum = 0.0f;
- for (float r = 0.0f; r < r_fp; r += r_step) {
- /* Compute distance to the "shading" point through the medium. */
- /* r_step * 0.5f to put sample between the area borders */
- float dist = hypotf(r + r_step * 0.5f, d);
-
- float profile[3];
- profile[0] = eval_profile(dist, kd->param[0]);
- profile[1] = eval_profile(dist, kd->param[1]);
- profile[2] = eval_profile(dist, kd->param[2]);
-
- /* Since the profile and configuration are radially symmetrical we
- * can just evaluate it once and weight it accordingly */
- float r_next = r + r_step;
- float disk_area = (M_PI * r_next * r_next) - (M_PI * r * r);
-
- mul_v3_fl(profile, disk_area);
- add_v3_v3(texels[i], profile);
- area_accum += disk_area;
- }
- /* Normalize over the disk. */
- mul_v3_fl(texels[i], 1.0f / (area_accum));
- }
-
- /* Normalize */
- for (int j = resolution - 2; j > 0; j--) {
- texels[j][0] /= (texels[0][0] > 0.0f) ? texels[0][0] : 1.0f;
- texels[j][1] /= (texels[0][1] > 0.0f) ? texels[0][1] : 1.0f;
- texels[j][2] /= (texels[0][2] > 0.0f) ? texels[0][2] : 1.0f;
+ if (!material->graph.outlink_volume) {
+ material->graph.outlink_volume = link;
}
-
- /* First texel should be white */
- texels[0][0] = (texels[0][0] > 0.0f) ? 1.0f : 0.0f;
- texels[0][1] = (texels[0][1] > 0.0f) ? 1.0f : 0.0f;
- texels[0][2] = (texels[0][2] > 0.0f) ? 1.0f : 0.0f;
-
- /* dim the last few texels for smoother transition */
- mul_v3_fl(texels[resolution - 2], 0.25f);
- mul_v3_fl(texels[resolution - 3], 0.5f);
- mul_v3_fl(texels[resolution - 4], 0.75f);
}
-#undef INTEGRAL_RESOLUTION
-void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3])
+void GPU_material_output_displacement(GPUMaterial *material, GPUNodeLink *link)
{
- copy_v3_v3(material->sss_radii, radii);
- material->sss_dirty = true;
- material->sss_enabled = true;
-
- /* Update / Create UBO */
- if (material->sss_profile == NULL) {
- material->sss_profile = GPU_uniformbuf_create(sizeof(GPUSssKernelData));
+ if (!material->graph.outlink_displacement) {
+ material->graph.outlink_displacement = link;
}
}
-struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material,
- int sample_len,
- GPUTexture **tex_profile)
+void GPU_material_output_thickness(GPUMaterial *material, GPUNodeLink *link)
{
- if (!material->sss_enabled) {
- return NULL;
- }
-
- if (material->sss_dirty || (material->sss_samples != sample_len)) {
- GPUSssKernelData kd;
-
- compute_sss_kernel(&kd, material->sss_radii, sample_len);
-
- /* Update / Create UBO */
- GPU_uniformbuf_update(material->sss_profile, &kd);
-
- /* Update / Create Tex */
- float *translucence_profile;
- compute_sss_translucence_kernel(&kd, 64, &translucence_profile);
-
- if (material->sss_tex_profile != NULL) {
- GPU_texture_free(material->sss_tex_profile);
- }
-
- material->sss_tex_profile = GPU_texture_create_1d(
- "sss_tex_profile", 64, 1, GPU_RGBA16F, translucence_profile);
-
- MEM_freeN(translucence_profile);
-
- material->sss_samples = sample_len;
- material->sss_dirty = false;
- }
-
- if (tex_profile != NULL) {
- *tex_profile = material->sss_tex_profile;
- }
- return material->sss_profile;
-}
-
-struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void)
-{
- return GPU_uniformbuf_create(sizeof(GPUSssKernelData));
-}
-
-#undef SSS_EXPONENT
-#undef SSS_SAMPLES
-
-ListBase GPU_material_attributes(GPUMaterial *material)
-{
- return material->graph.attributes;
-}
-
-ListBase GPU_material_textures(GPUMaterial *material)
-{
- return material->graph.textures;
-}
-
-ListBase GPU_material_volume_grids(GPUMaterial *material)
-{
- return material->graph.volume_grids;
-}
-
-GPUUniformAttrList *GPU_material_uniform_attributes(GPUMaterial *material)
-{
- GPUUniformAttrList *attrs = &material->graph.uniform_attrs;
- return attrs->count > 0 ? attrs : NULL;
-}
-
-void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link)
-{
- if (!material->graph.outlink) {
- material->graph.outlink = link;
+ if (!material->graph.outlink_thickness) {
+ material->graph.outlink_thickness = link;
}
}
@@ -530,14 +268,19 @@ void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link,
BLI_addtail(&material->graph.outlink_aovs, aov_link);
}
-GPUNodeGraph *gpu_material_node_graph(GPUMaterial *material)
+void GPU_material_add_closure_eval(GPUMaterial *material,
+ const GPUNodeLink *weight_link,
+ const GPUNodeLink *eval_link)
{
- return &material->graph;
+ GPUNodeGraphEvalNode *node = MEM_callocN(sizeof(GPUNodeGraphEvalNode), __func__);
+ node->weight_node = weight_link->output->node;
+ node->eval_node = eval_link->output->node;
+ BLI_addtail(&material->graph.eval_nodes, node);
}
-GSet *gpu_material_used_libraries(GPUMaterial *material)
+GPUNodeGraph *gpu_material_node_graph(GPUMaterial *material)
{
- return material->used_libraries;
+ return &material->graph;
}
eGPUMaterialStatus GPU_material_status(GPUMaterial *mat)
@@ -545,112 +288,86 @@ eGPUMaterialStatus GPU_material_status(GPUMaterial *mat)
return mat->status;
}
-/* Code generation */
-
-bool GPU_material_has_surface_output(GPUMaterial *mat)
+void GPU_material_status_set(GPUMaterial *mat, eGPUMaterialStatus status)
{
- return mat->has_surface_output;
+ mat->status = status;
}
-bool GPU_material_has_volume_output(GPUMaterial *mat)
-{
- return mat->has_volume_output;
-}
+/* Code generation */
bool GPU_material_is_volume_shader(GPUMaterial *mat)
{
return mat->is_volume_shader;
}
-void GPU_material_flag_set(GPUMaterial *mat, eGPUMatFlag flag)
+void GPU_material_flag_set(GPUMaterial *mat, eGPUMaterialFlag flag)
{
mat->flag |= flag;
}
-bool GPU_material_flag_get(GPUMaterial *mat, eGPUMatFlag flag)
+bool GPU_material_flag_get(const GPUMaterial *mat, eGPUMaterialFlag flag)
{
return (mat->flag & flag) != 0;
}
-GPUMaterial *GPU_material_from_nodetree_find(ListBase *gpumaterials,
- const void *engine_type,
- int options)
+/* Note: Consumes the flags. */
+bool GPU_material_recalc_flag_get(GPUMaterial *mat)
{
- LISTBASE_FOREACH (LinkData *, link, gpumaterials) {
- GPUMaterial *current_material = (GPUMaterial *)link->data;
- if (current_material->engine_type == engine_type && current_material->options == options) {
- return current_material;
- }
- }
+ bool updated = (mat->flag & GPU_MATFLAG_UPDATED) != 0;
+ mat->flag &= ~GPU_MATFLAG_UPDATED;
+ return updated;
+}
- return NULL;
+uint64_t GPU_material_uuid_get(GPUMaterial *mat)
+{
+ return mat->uuid;
}
GPUMaterial *GPU_material_from_nodetree(Scene *scene,
- struct Material *ma,
- struct bNodeTree *ntree,
+ Material *ma,
+ bNodeTree *ntree,
ListBase *gpumaterials,
- const void *engine_type,
- const int options,
- const bool is_volume_shader,
- const char *vert_code,
- const char *geom_code,
- const char *frag_lib,
- const char *defines,
const char *name,
- GPUMaterialEvalCallbackFn callback)
+ uint64_t shader_uuid,
+ bool is_volume_shader,
+ bool is_lookdev,
+ GPUCodegenCallbackFn callback,
+ void *thunk)
{
- LinkData *link;
- bool has_volume_output, has_surface_output;
-
- /* Caller must re-use materials. */
- BLI_assert(GPU_material_from_nodetree_find(gpumaterials, engine_type, options) == NULL);
-
- /* HACK: Eevee assume this to create #GHash keys. */
- BLI_assert(sizeof(GPUPass) > 16);
+ /* Search if this material is not already compiled. */
+ LISTBASE_FOREACH (LinkData *, link, gpumaterials) {
+ GPUMaterial *mat = (GPUMaterial *)link->data;
+ if (mat->uuid == shader_uuid) {
+ return mat;
+ }
+ }
- /* allocate material */
GPUMaterial *mat = MEM_callocN(sizeof(GPUMaterial), "GPUMaterial");
mat->ma = ma;
mat->scene = scene;
- mat->engine_type = engine_type;
- mat->options = options;
+ mat->uuid = shader_uuid;
+ mat->flag = GPU_MATFLAG_UPDATED;
mat->is_volume_shader = is_volume_shader;
+ mat->graph.used_libraries = BLI_gset_new(
+ BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "GPUNodeGraph.used_libraries");
#ifndef NDEBUG
BLI_snprintf(mat->name, sizeof(mat->name), "%s", name);
#else
UNUSED_VARS(name);
#endif
+ if (is_lookdev) {
+ mat->flag |= GPU_MATFLAG_LOOKDEV_HACK;
+ }
- mat->used_libraries = BLI_gset_new(
- BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "GPUMaterial.used_libraries");
-
- /* localize tree to create links for reroute and mute */
+ /* Localize tree to create links for reroute and mute. */
bNodeTree *localtree = ntreeLocalize(ntree);
- ntreeGPUMaterialNodes(localtree, mat, &has_surface_output, &has_volume_output);
+ ntreeGPUMaterialNodes(localtree, mat);
gpu_material_ramp_texture_build(mat);
- mat->has_surface_output = has_surface_output;
- mat->has_volume_output = has_volume_output;
-
- if (mat->graph.outlink) {
- if (callback) {
- callback(mat, options, &vert_code, &geom_code, &frag_lib, &defines);
- }
- /* HACK: this is only for eevee. We add the define here after the nodetree evaluation. */
- if (GPU_material_flag_get(mat, GPU_MATFLAG_SSS)) {
- defines = BLI_string_joinN(defines,
- "#ifndef USE_ALPHA_BLEND\n"
- "# define USE_SSS\n"
- "#endif\n");
- }
+ if (mat->graph.outlink_surface || mat->graph.outlink_volume) {
/* Create source code and search pass cache for an already compiled version. */
- mat->pass = GPU_generate_pass(mat, &mat->graph, vert_code, geom_code, frag_lib, defines);
-
- if (GPU_material_flag_get(mat, GPU_MATFLAG_SSS)) {
- MEM_freeN((char *)defines);
- }
+ mat->pass = GPU_generate_pass(mat, &mat->graph, callback, thunk);
if (mat->pass == NULL) {
/* We had a cache hit and the shader has already failed to compile. */
@@ -665,7 +382,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene,
gpu_node_graph_free_nodes(&mat->graph);
}
else {
- mat->status = GPU_MAT_QUEUED;
+ mat->status = GPU_MAT_CREATED;
}
}
}
@@ -674,17 +391,15 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene,
gpu_node_graph_free(&mat->graph);
}
- /* Only free after GPU_pass_shader_get where GPUUniformBuf
- * read data from the local tree. */
+ /* Only free after GPU_pass_shader_get where GPUUniformBuf read data from the local tree. */
ntreeFreeLocalTree(localtree);
BLI_assert(!localtree->id.py_instance); /* Or call #BKE_libblock_free_data_py. */
MEM_freeN(localtree);
- /* note that even if building the shader fails in some way, we still keep
+ /* Note that even if building the shader fails in some way, we still keep
* it to avoid trying to compile again and again, and simply do not use
- * the actual shader on drawing */
-
- link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink");
+ * the actual shader on drawing. */
+ LinkData *link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink");
link->data = mat;
BLI_addtail(gpumaterials, link);
@@ -706,6 +421,8 @@ void GPU_material_compile(GPUMaterial *mat)
success = GPU_pass_compile(mat->pass, __func__);
#endif
+ mat->flag |= GPU_MATFLAG_UPDATED;
+
if (success) {
GPUShader *sh = GPU_pass_shader_get(mat->pass);
if (sh != NULL) {
@@ -731,5 +448,6 @@ void GPU_materials_free(Main *bmain)
GPU_material_free(&wo->gpumaterial);
}
+ BKE_world_defaults_free_gpu();
BKE_material_defaults_free_gpu();
}
diff --git a/source/blender/gpu/intern/gpu_material_library.c b/source/blender/gpu/intern/gpu_material_library.c
index 712ab44adb3..6f68729eaae 100644
--- a/source/blender/gpu/intern/gpu_material_library.c
+++ b/source/blender/gpu/intern/gpu_material_library.c
@@ -757,7 +757,7 @@ static const char *GPU_DATATYPE_STR[17] = {
const char *gpu_data_type_to_string(const eGPUType type)
{
- return GPU_DATATYPE_STR[type];
+ return (type == GPU_CLOSURE) ? "Closure" : GPU_DATATYPE_STR[type];
}
static void gpu_parse_material_library(GHash *hash, GPUMaterialLibrary *library)
@@ -893,14 +893,10 @@ GPUFunction *gpu_material_library_use_function(GSet *used_libraries, const char
return function;
}
-char *gpu_material_library_generate_code(GSet *used_libraries, const char *frag_lib)
+char *gpu_material_library_generate_code(GSet *used_libraries)
{
DynStr *ds = BLI_dynstr_new();
- if (frag_lib) {
- BLI_dynstr_append(ds, frag_lib);
- }
-
/* Always include those because they may be needed by the execution function. */
gpu_material_use_library_with_dependencies(used_libraries,
&gpu_shader_material_world_normals_library);
diff --git a/source/blender/gpu/intern/gpu_material_library.h b/source/blender/gpu/intern/gpu_material_library.h
index 7f9eeb8166a..d9c56ddd425 100644
--- a/source/blender/gpu/intern/gpu_material_library.h
+++ b/source/blender/gpu/intern/gpu_material_library.h
@@ -26,6 +26,10 @@
#include "GPU_material.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define MAX_FUNCTION_NAME 64
#define MAX_PARAMETER 36
@@ -58,9 +62,13 @@ void gpu_material_library_exit(void);
/* Code Generation */
GPUFunction *gpu_material_library_use_function(struct GSet *used_libraries, const char *name);
-char *gpu_material_library_generate_code(struct GSet *used_libraries, const char *frag_lib);
+char *gpu_material_library_generate_code(struct GSet *used_libraries);
/* Code Parsing */
const char *gpu_str_skip_token(const char *str, char *token, int max);
const char *gpu_data_type_to_string(eGPUType type);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c
index e0d60aa5fda..65848620339 100644
--- a/source/blender/gpu/intern/gpu_node_graph.c
+++ b/source/blender/gpu/intern/gpu_node_graph.c
@@ -103,10 +103,6 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType
input->type = type;
switch (link->link_type) {
- case GPU_NODE_LINK_BUILTIN:
- input->source = GPU_SOURCE_BUILTIN;
- input->builtin = link->builtin;
- break;
case GPU_NODE_LINK_OUTPUT:
input->source = GPU_SOURCE_OUTPUT;
input->link = link;
@@ -619,26 +615,18 @@ GPUNodeLink *GPU_volume_grid(GPUMaterial *mat,
return link;
}
-GPUNodeLink *GPU_builtin(eGPUBuiltin builtin)
-{
- GPUNodeLink *link = gpu_node_link_create();
- link->link_type = GPU_NODE_LINK_BUILTIN;
- link->builtin = builtin;
- return link;
-}
-
/* Creating Nodes */
bool GPU_link(GPUMaterial *mat, const char *name, ...)
{
- GSet *used_libraries = gpu_material_used_libraries(mat);
+ GPUNodeGraph *graph = gpu_material_node_graph(mat);
GPUNode *node;
GPUFunction *function;
GPUNodeLink *link, **linkptr;
va_list params;
int i;
- function = gpu_material_library_use_function(used_libraries, name);
+ function = gpu_material_library_use_function(graph->used_libraries, name);
if (!function) {
fprintf(stderr, "GPU failed to find function %s\n", name);
return false;
@@ -659,27 +647,25 @@ bool GPU_link(GPUMaterial *mat, const char *name, ...)
}
va_end(params);
- GPUNodeGraph *graph = gpu_material_node_graph(mat);
BLI_addtail(&graph->nodes, node);
return true;
}
-bool GPU_stack_link(GPUMaterial *material,
- bNode *bnode,
- const char *name,
- GPUNodeStack *in,
- GPUNodeStack *out,
- ...)
+static bool gpu_stack_link_v(GPUMaterial *material,
+ bNode *bnode,
+ const char *name,
+ GPUNodeStack *in,
+ GPUNodeStack *out,
+ va_list params)
{
- GSet *used_libraries = gpu_material_used_libraries(material);
+ GPUNodeGraph *graph = gpu_material_node_graph(material);
GPUNode *node;
GPUFunction *function;
GPUNodeLink *link, **linkptr;
- va_list params;
int i, totin, totout;
- function = gpu_material_library_use_function(used_libraries, name);
+ function = gpu_material_library_use_function(graph->used_libraries, name);
if (!function) {
fprintf(stderr, "GPU failed to find function %s\n", name);
return false;
@@ -707,7 +693,6 @@ bool GPU_stack_link(GPUMaterial *material,
}
}
- va_start(params, out);
for (i = 0; i < function->totparam; i++) {
if (function->paramqual[i] != FUNCTION_QUAL_IN) {
if (totout == 0) {
@@ -733,14 +718,51 @@ bool GPU_stack_link(GPUMaterial *material,
}
}
}
- va_end(params);
- GPUNodeGraph *graph = gpu_material_node_graph(material);
BLI_addtail(&graph->nodes, node);
return true;
}
+bool GPU_stack_link(GPUMaterial *material,
+ bNode *bnode,
+ const char *name,
+ GPUNodeStack *in,
+ GPUNodeStack *out,
+ ...)
+{
+ va_list params;
+ va_start(params, out);
+ bool valid = gpu_stack_link_v(material, bnode, name, in, out, params);
+ va_end(params);
+
+ return valid;
+}
+
+bool GPU_stack_eval_link(GPUMaterial *material,
+ bNode *bnode,
+ const char *name,
+ GPUNodeStack *in,
+ GPUNodeStack *out,
+ ...)
+{
+ /* Save the closure link to replace the one created by the eval function call. Avoiding
+ * dependency to the eval call before the end of the graph. */
+ GPUNodeLink *closure_out = out[0].link;
+
+ va_list params;
+ va_start(params, out);
+ bool valid = gpu_stack_link_v(material, bnode, name, in, out, params);
+ va_end(params);
+
+ /* Save both nodes for graph amendment. */
+ GPU_material_add_closure_eval(material, closure_out, out[0].link);
+ /* Restore original link. */
+ out[0].link = closure_out;
+
+ return valid;
+}
+
GPUNodeLink *GPU_uniformbuf_link_out(GPUMaterial *mat,
bNode *node,
GPUNodeStack *stack,
@@ -802,7 +824,11 @@ void gpu_node_graph_free_nodes(GPUNodeGraph *graph)
gpu_node_free(node);
}
- graph->outlink = NULL;
+ BLI_freelistN(&graph->eval_nodes);
+ graph->outlink_surface = NULL;
+ graph->outlink_volume = NULL;
+ graph->outlink_displacement = NULL;
+ graph->outlink_thickness = NULL;
}
void gpu_node_graph_free(GPUNodeGraph *graph)
@@ -817,28 +843,32 @@ void gpu_node_graph_free(GPUNodeGraph *graph)
BLI_freelistN(&graph->textures);
BLI_freelistN(&graph->attributes);
GPU_uniform_attr_list_free(&graph->uniform_attrs);
+
+ if (graph->used_libraries) {
+ BLI_gset_free(graph->used_libraries, NULL);
+ graph->used_libraries = NULL;
+ }
}
/* Prune Unused Nodes */
-static void gpu_nodes_tag(GPUNodeLink *link)
+static void gpu_nodes_tag(GPUNodeLink *link, eGPUNodeTag tag)
{
GPUNode *node;
- GPUInput *input;
- if (!link->output) {
+ if (!link || !link->output) {
return;
}
node = link->output->node;
- if (node->tag) {
+ if (node->tag & tag) {
return;
}
- node->tag = true;
- for (input = node->inputs.first; input; input = input->next) {
+ node->tag |= tag;
+ LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
if (input->link) {
- gpu_nodes_tag(input->link);
+ gpu_nodes_tag(input->link, tag);
}
}
}
@@ -846,18 +876,30 @@ static void gpu_nodes_tag(GPUNodeLink *link)
void gpu_node_graph_prune_unused(GPUNodeGraph *graph)
{
LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
- node->tag = false;
+ node->tag = GPU_NODE_TAG_NONE;
}
- gpu_nodes_tag(graph->outlink);
+ gpu_nodes_tag(graph->outlink_surface, GPU_NODE_TAG_SURFACE);
+ gpu_nodes_tag(graph->outlink_volume, GPU_NODE_TAG_VOLUME);
+ gpu_nodes_tag(graph->outlink_displacement, GPU_NODE_TAG_DISPLACEMENT);
+ gpu_nodes_tag(graph->outlink_thickness, GPU_NODE_TAG_THICKNESS);
+
LISTBASE_FOREACH (GPUNodeGraphOutputLink *, aovlink, &graph->outlink_aovs) {
- gpu_nodes_tag(aovlink->outlink);
+ gpu_nodes_tag(aovlink->outlink, GPU_NODE_TAG_AOV);
+ }
+
+ LISTBASE_FOREACH (GPUNodeGraphEvalNode *, node, &graph->eval_nodes) {
+ /* Copy weight node tag to avoid pruning of eval node since they are node connected to
+ * output. */
+ if (node->weight_node->tag != GPU_NODE_TAG_NONE) {
+ node->eval_node->tag = GPU_NODE_TAG_EVAL | node->weight_node->tag;
+ }
}
for (GPUNode *node = graph->nodes.first, *next = NULL; node; node = next) {
next = node->next;
- if (!node->tag) {
+ if (node->tag == GPU_NODE_TAG_NONE) {
BLI_remlink(&graph->nodes, node);
gpu_node_free(node);
}
diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h
index 8a112ac9c4d..9f1a729738b 100644
--- a/source/blender/gpu/intern/gpu_node_graph.h
+++ b/source/blender/gpu/intern/gpu_node_graph.h
@@ -28,9 +28,15 @@
#include "DNA_customdata_types.h"
#include "DNA_listBase.h"
+#include "BLI_ghash.h"
+
#include "GPU_material.h"
#include "GPU_shader.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct GPUNode;
struct GPUOutput;
struct ListBase;
@@ -41,7 +47,6 @@ typedef enum eGPUDataSource {
GPU_SOURCE_UNIFORM,
GPU_SOURCE_ATTR,
GPU_SOURCE_UNIFORM_ATTR,
- GPU_SOURCE_BUILTIN,
GPU_SOURCE_STRUCT,
GPU_SOURCE_TEX,
GPU_SOURCE_TEX_TILED_MAPPING,
@@ -53,7 +58,6 @@ typedef enum {
GPU_NODE_LINK_NONE = 0,
GPU_NODE_LINK_ATTR,
GPU_NODE_LINK_UNIFORM_ATTR,
- GPU_NODE_LINK_BUILTIN,
GPU_NODE_LINK_COLORBAND,
GPU_NODE_LINK_CONSTANT,
GPU_NODE_LINK_IMAGE,
@@ -65,13 +69,23 @@ typedef enum {
GPU_NODE_LINK_UNIFORM,
} GPUNodeLinkType;
+typedef enum {
+ GPU_NODE_TAG_NONE = 0,
+ GPU_NODE_TAG_SURFACE = (1 << 0),
+ GPU_NODE_TAG_VOLUME = (1 << 1),
+ GPU_NODE_TAG_DISPLACEMENT = (1 << 2),
+ GPU_NODE_TAG_THICKNESS = (1 << 3),
+ GPU_NODE_TAG_AOV = (1 << 4),
+ GPU_NODE_TAG_EVAL = (1 << 5),
+} eGPUNodeTag;
+
struct GPUNode {
struct GPUNode *next, *prev;
const char *name;
/* Internal flag to mark nodes during pruning */
- bool tag;
+ eGPUNodeTag tag;
ListBase inputs;
ListBase outputs;
@@ -86,8 +100,6 @@ struct GPUNodeLink {
union {
/* GPU_NODE_LINK_CONSTANT | GPU_NODE_LINK_UNIFORM */
const float *data;
- /* GPU_NODE_LINK_BUILTIN */
- eGPUBuiltin builtin;
/* GPU_NODE_LINK_COLORBAND */
struct GPUTexture **colorband;
/* GPU_NODE_LINK_VOLUME_GRID */
@@ -126,8 +138,6 @@ typedef struct GPUInput {
union {
/* GPU_SOURCE_CONSTANT | GPU_SOURCE_UNIFORM */
float vec[16]; /* vector data */
- /* GPU_SOURCE_BUILTIN */
- eGPUBuiltin builtin; /* builtin uniform */
/* GPU_SOURCE_TEX | GPU_SOURCE_TEX_TILED_MAPPING */
struct GPUMaterialTexture *texture;
/* GPU_SOURCE_ATTR */
@@ -145,14 +155,25 @@ typedef struct GPUNodeGraphOutputLink {
GPUNodeLink *outlink;
} GPUNodeGraphOutputLink;
+typedef struct GPUNodeGraphEvalNode {
+ struct GPUNodeGraphEvalNode *next, *prev;
+ GPUNode *weight_node;
+ GPUNode *eval_node;
+} GPUNodeGraphEvalNode;
+
typedef struct GPUNodeGraph {
/* Nodes */
ListBase nodes;
- /* Main Output. */
- GPUNodeLink *outlink;
+ /* Main Outputs. */
+ GPUNodeLink *outlink_surface;
+ GPUNodeLink *outlink_volume;
+ GPUNodeLink *outlink_displacement;
+ GPUNodeLink *outlink_thickness;
/* List of GPUNodeGraphOutputLink */
ListBase outlink_aovs;
+ /* List of GPUNodeGraphEvalNode. */
+ ListBase eval_nodes;
/* Requested attributes and textures. */
ListBase attributes;
@@ -161,6 +182,9 @@ typedef struct GPUNodeGraph {
/* The list of uniform attributes. */
GPUUniformAttrList uniform_attrs;
+
+ /** Set of all the GLSL lib code blocks . */
+ GSet *used_libraries;
} GPUNodeGraph;
/* Node Graph */
@@ -187,4 +211,6 @@ struct GPUTexture **gpu_material_ramp_texture_row_set(struct GPUMaterial *mat,
float *pixels,
float *row);
-struct GSet *gpu_material_used_libraries(struct GPUMaterial *material);
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc
index ef800abc3c9..830e65f69d9 100644
--- a/source/blender/gpu/intern/gpu_shader.cc
+++ b/source/blender/gpu/intern/gpu_shader.cc
@@ -610,6 +610,12 @@ int GPU_shader_get_builtin_block(GPUShader *shader, int builtin)
return interface->ubo_builtin((GPUUniformBlockBuiltin)builtin);
}
+int GPU_shader_get_builtin_ssbo(GPUShader *shader, int builtin)
+{
+ ShaderInterface *interface = unwrap(shader)->interface;
+ return interface->buffer_builtin((GPUBufferBlockBuiltin)builtin);
+}
+
int GPU_shader_get_ssbo(GPUShader *shader, const char *name)
{
ShaderInterface *interface = unwrap(shader)->interface;
diff --git a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
index 40e54ab4394..469085026b3 100644
--- a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
+++ b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
@@ -216,10 +216,7 @@ int BKE_subdiv_ccg_grid_to_face_index(const SubdivCCG *UNUSED(subdiv_ccg),
/* -------------------------------------------------------------------- */
/** \name Stubs of BKE_node.h
* \{ */
-void ntreeGPUMaterialNodes(struct bNodeTree *UNUSED(localtree),
- struct GPUMaterial *UNUSED(mat),
- bool *UNUSED(has_surface_output),
- bool *UNUSED(has_volume_output))
+void ntreeGPUMaterialNodes(struct bNodeTree *UNUSED(localtree), struct GPUMaterial *UNUSED(mat))
{
BLI_assert_unreachable();
}
diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh
index 8c6d6ede11b..f94006d1407 100644
--- a/source/blender/gpu/intern/gpu_shader_interface.hh
+++ b/source/blender/gpu/intern/gpu_shader_interface.hh
@@ -71,6 +71,7 @@ class ShaderInterface {
/** Location of builtin uniforms. Fast access, no lookup needed. */
int32_t builtins_[GPU_NUM_UNIFORMS];
int32_t builtin_blocks_[GPU_NUM_UNIFORM_BLOCKS];
+ int32_t builtin_buffers_[GPU_NUM_BUFFER_BLOCKS];
public:
ShaderInterface();
@@ -126,10 +127,17 @@ class ShaderInterface {
BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORM_BLOCKS);
return builtin_blocks_[builtin];
}
+ /* Returns binding position. */
+ inline int32_t buffer_builtin(const GPUBufferBlockBuiltin builtin) const
+ {
+ BLI_assert(builtin >= 0 && builtin < GPU_NUM_BUFFER_BLOCKS);
+ return builtin_buffers_[builtin];
+ }
protected:
static inline const char *builtin_uniform_name(GPUUniformBuiltin u);
static inline const char *builtin_uniform_block_name(GPUUniformBlockBuiltin u);
+ static inline const char *builtin_buffer_block_name(GPUBufferBlockBuiltin u);
inline uint32_t set_input_name(ShaderInput *input, char *name, uint32_t name_len) const;
inline void copy_input_name(ShaderInput *input,
@@ -223,6 +231,16 @@ inline const char *ShaderInterface::builtin_uniform_block_name(GPUUniformBlockBu
}
}
+inline const char *ShaderInterface::builtin_buffer_block_name(GPUBufferBlockBuiltin u)
+{
+ switch (u) {
+ case GPU_BUFFER_BLOCK_DEBUG:
+ return "debugBuf";
+ default:
+ return NULL;
+ }
+}
+
/* Returns string length including '\0' terminator. */
inline uint32_t ShaderInterface::set_input_name(ShaderInput *input,
char *name,
diff --git a/source/blender/gpu/intern/gpu_shader_log.cc b/source/blender/gpu/intern/gpu_shader_log.cc
index 12459b4b721..0c328876485 100644
--- a/source/blender/gpu/intern/gpu_shader_log.cc
+++ b/source/blender/gpu/intern/gpu_shader_log.cc
@@ -41,6 +41,9 @@ namespace blender::gpu {
/** \name Debug functions
* \{ */
+/* Number of lines before and after the error line to print for compilation errors. */
+#define DEBUG_CONTEXT_LINES 2
+
void Shader::print_log(Span<const char *> sources,
char *log,
const char *stage,
@@ -102,11 +105,10 @@ void Shader::print_log(Span<const char *> sources,
found_line_id = true;
break;
}
-/* TODO(fclem) Make this an option to display N lines before error. */
-#if 0 /* Uncomment to print shader file up to the error line to have more context. */
- BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index);
- BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line);
-#endif
+ if (src_line_index >= log_item.cursor.row - DEBUG_CONTEXT_LINES) {
+ BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index);
+ BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line);
+ }
/* Continue to next line. */
src_line = src_line_end + 1;
src_line_index++;
@@ -129,6 +131,20 @@ void Shader::print_log(Span<const char *> sources,
BLI_dynstr_appendf(dynstr, "^");
}
BLI_dynstr_appendf(dynstr, "\n");
+
+ /* Skip the error line. */
+ src_line = src_line_end + 1;
+ src_line_index++;
+ while ((src_line_end = strchr(src_line, '\n'))) {
+ if (src_line_index > log_item.cursor.row + DEBUG_CONTEXT_LINES) {
+ break;
+ }
+ BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index);
+ BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line);
+ /* Continue to next line. */
+ src_line = src_line_end + 1;
+ src_line_index++;
+ }
}
}
BLI_dynstr_appendf(dynstr, line_prefix);
diff --git a/source/blender/gpu/opengl/gl_framebuffer.hh b/source/blender/gpu/opengl/gl_framebuffer.hh
index 9ebe549efe7..00a7676dd3f 100644
--- a/source/blender/gpu/opengl/gl_framebuffer.hh
+++ b/source/blender/gpu/opengl/gl_framebuffer.hh
@@ -141,6 +141,8 @@ static inline GLenum to_gl(const GPUAttachmentType type)
ATTACHMENT(COLOR_ATTACHMENT3);
ATTACHMENT(COLOR_ATTACHMENT4);
ATTACHMENT(COLOR_ATTACHMENT5);
+ ATTACHMENT(COLOR_ATTACHMENT6);
+ ATTACHMENT(COLOR_ATTACHMENT7);
default:
BLI_assert(0);
return GL_COLOR_ATTACHMENT0;
diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc
index 299ea150408..467731f2889 100644
--- a/source/blender/gpu/opengl/gl_shader_interface.cc
+++ b/source/blender/gpu/opengl/gl_shader_interface.cc
@@ -117,7 +117,28 @@ static inline int image_binding(int32_t program,
switch (type) {
case GL_IMAGE_1D:
case GL_IMAGE_2D:
- case GL_IMAGE_3D: {
+ case GL_IMAGE_3D:
+ case GL_IMAGE_CUBE:
+ case GL_IMAGE_BUFFER:
+ case GL_IMAGE_1D_ARRAY:
+ case GL_IMAGE_2D_ARRAY:
+ case GL_IMAGE_CUBE_MAP_ARRAY:
+ case GL_INT_IMAGE_1D:
+ case GL_INT_IMAGE_2D:
+ case GL_INT_IMAGE_3D:
+ case GL_INT_IMAGE_CUBE:
+ case GL_INT_IMAGE_BUFFER:
+ case GL_INT_IMAGE_1D_ARRAY:
+ case GL_INT_IMAGE_2D_ARRAY:
+ case GL_INT_IMAGE_CUBE_MAP_ARRAY:
+ case GL_UNSIGNED_INT_IMAGE_1D:
+ case GL_UNSIGNED_INT_IMAGE_2D:
+ case GL_UNSIGNED_INT_IMAGE_3D:
+ case GL_UNSIGNED_INT_IMAGE_CUBE:
+ case GL_UNSIGNED_INT_IMAGE_BUFFER:
+ case GL_UNSIGNED_INT_IMAGE_1D_ARRAY:
+ case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
+ case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: {
/* For now just assign a consecutive index. In the future, we should set it in
* the shader using layout(binding = i) and query its value. */
int binding = *image_len;
@@ -313,6 +334,12 @@ GLShaderInterface::GLShaderInterface(GLuint program)
builtin_blocks_[u] = (block != nullptr) ? block->binding : -1;
}
+ for (int32_t u_int = 0; u_int < GPU_NUM_BUFFER_BLOCKS; u_int++) {
+ GPUBufferBlockBuiltin u = static_cast<GPUBufferBlockBuiltin>(u_int);
+ const ShaderInput *buffer = this->ssbo_get(builtin_buffer_block_name(u));
+ builtin_buffers_[u] = (buffer != nullptr) ? buffer->binding : -1;
+ }
+
MEM_freeN(uniforms_from_blocks);
/* Resize name buffer to save some memory. */
diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh
index 83ff3ffc9e9..5985f1583f2 100644
--- a/source/blender/gpu/opengl/gl_state.hh
+++ b/source/blender/gpu/opengl/gl_state.hh
@@ -124,11 +124,20 @@ static inline GLbitfield to_gl(eGPUBarrier barrier_bits)
if (barrier_bits & GPU_BARRIER_SHADER_IMAGE_ACCESS) {
barrier |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
}
+ if (barrier_bits & GPU_BARRIER_SHADER_STORAGE) {
+ barrier |= GL_SHADER_STORAGE_BARRIER_BIT;
+ }
if (barrier_bits & GPU_BARRIER_TEXTURE_FETCH) {
barrier |= GL_TEXTURE_FETCH_BARRIER_BIT;
}
- if (barrier_bits & GPU_BARRIER_SHADER_STORAGE) {
- barrier |= GL_SHADER_STORAGE_BARRIER_BIT;
+ if (barrier_bits & GPU_BARRIER_TEXTURE_UPDATE) {
+ barrier |= GL_TEXTURE_UPDATE_BARRIER_BIT;
+ }
+ if (barrier_bits & GPU_BARRIER_COMMAND) {
+ barrier |= GL_COMMAND_BARRIER_BIT;
+ }
+ if (barrier_bits & GPU_BARRIER_FRAMEBUFFER) {
+ barrier |= GL_FRAMEBUFFER_BARRIER_BIT;
}
if (barrier_bits & GPU_BARRIER_VERTEX_ATTRIB_ARRAY) {
barrier |= GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT;
diff --git a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
index 193a4190cbf..086ff5b0217 100644
--- a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
@@ -1,97 +1,28 @@
-vec3 calc_barycentric_distances(vec3 pos0, vec3 pos1, vec3 pos2)
-{
- vec3 edge21 = pos2 - pos1;
- vec3 edge10 = pos1 - pos0;
- vec3 edge02 = pos0 - pos2;
- vec3 d21 = normalize(edge21);
- vec3 d10 = normalize(edge10);
- vec3 d02 = normalize(edge02);
-
- vec3 dists;
- float d = dot(d21, edge02);
- dists.x = sqrt(dot(edge02, edge02) - d * d);
- d = dot(d02, edge10);
- dists.y = sqrt(dot(edge10, edge10) - d * d);
- d = dot(d10, edge21);
- dists.z = sqrt(dot(edge21, edge21) - d * d);
- return dists;
-}
-
-vec2 calc_barycentric_co(int vertid)
-{
- vec2 bary;
- bary.x = float((vertid % 3) == 0);
- bary.y = float((vertid % 3) == 1);
- return bary;
-}
-
-#ifdef HAIR_SHADER
-
-/* Hairs uv and col attributes are passed by bufferTextures. */
-# define DEFINE_ATTR(type, attr) uniform samplerBuffer attr
-# define GET_ATTR(type, attr) hair_get_customdata_##type(attr)
-
-# define barycentric_get() hair_get_barycentric()
-# define barycentric_resolve(bary) hair_resolve_barycentric(bary)
-
-vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], const samplerBuffer orco_samp)
-{
- /* TODO: fix ORCO with modifiers. */
- vec3 orco = (modelmatinv * vec4(local_pos, 1.0)).xyz;
- return orco_madd[0].xyz + orco * orco_madd[1].xyz;
-}
-
-float hair_len_get(int id, const samplerBuffer len)
-{
- return texelFetch(len, id).x;
-}
-
-vec4 tangent_get(const samplerBuffer attr, mat3 normalmat)
-{
- /* Unsupported */
- return vec4(0.0);
-}
-
-#else /* MESH_SHADER */
-
-# define DEFINE_ATTR(type, attr) in type attr
-# define GET_ATTR(type, attr) attr
-
-/* Calculated in geom shader later with calc_barycentric_co. */
-# define barycentric_get() vec2(0)
-# define barycentric_resolve(bary) bary
-
-vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], vec4 orco)
-{
- /* If the object does not have any deformation, the orco layer calculation is done on the fly
- * using the orco_madd factors.
- * We know when there is no orco layer when orco.w is 1.0 because it uses the generic vertex
- * attrib (which is [0,0,0,1]). */
- if (orco.w == 0.0) {
- return orco.xyz * 0.5 + 0.5;
- }
- else {
- return orco_madd[0].xyz + local_pos * orco_madd[1].xyz;
- }
-}
-
-float hair_len_get(int id, const float len)
-{
- return len;
-}
-
-vec4 tangent_get(vec4 attr, mat3 normalmat)
-{
- vec4 tangent;
- tangent.xyz = normalmat * attr.xyz;
- tangent.w = attr.w;
- float len_sqr = dot(tangent.xyz, tangent.xyz);
- /* Normalize only if vector is not null. */
- if (len_sqr > 0.0) {
- tangent.xyz *= inversesqrt(len_sqr);
- }
- return tangent;
-}
-
+/* Assumes GPU_VEC4 is color data. So converting to luminance like cycles. */
+#define float_from_vec4(v) dot(v.rgb, vec3(0.2126, 0.7152, 0.0722))
+#define float_from_vec3(v) avg(v.rgb)
+#define float_from_vec2(v) v.r
+
+#define vec2_from_vec4(v) vec2(avg(v.rgb), v.a)
+#define vec2_from_vec3(v) vec2(avg(v.rgb), 1.0)
+#define vec2_from_float(v) vec2(v)
+
+#define vec3_from_vec4(v) v.rgb
+#define vec3_from_vec2(v) v.rrr
+#define vec3_from_float(v) vec3(v)
+
+#define vec4_from_vec3(v) vec4(v, 1.0)
+#define vec4_from_vec2(v) v.rrrg
+#define vec4_from_float(v) vec4(vec3(v), 1.0)
+
+#define RAY_TYPE_CAMERA 0
+#define RAY_TYPE_SHADOW 1
+#define RAY_TYPE_DIFFUSE 2
+#define RAY_TYPE_GLOSSY 3
+
+#ifdef GPU_FRAGMENT_SHADER
+# define FrontFacing gl_FrontFacing
+#else
+# define FrontFacing true
#endif
diff --git a/source/blender/gpu/shaders/gpu_shader_common_obinfos_lib.glsl b/source/blender/gpu/shaders/gpu_shader_common_obinfos_lib.glsl
deleted file mode 100644
index f5b6de4899f..00000000000
--- a/source/blender/gpu/shaders/gpu_shader_common_obinfos_lib.glsl
+++ /dev/null
@@ -1,23 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-
-#ifndef GPU_OBINFOS_UBO
-# define GPU_OBINFOS_UBO
-struct ObjectInfos {
- vec4 drw_OrcoTexCoFactors[2];
- vec4 drw_ObjectColor;
- vec4 drw_Infos;
-};
-
-# ifndef USE_GPU_SHADER_CREATE_INFO
-layout(std140) uniform infoBlock
-{
- /* DRW_RESOURCE_CHUNK_LEN = 512 */
- ObjectInfos drw_infos[512];
-};
-# endif
-
-# define OrcoTexCoFactors (drw_infos[resource_id].drw_OrcoTexCoFactors)
-# define ObjectInfo (drw_infos[resource_id].drw_Infos)
-# define ObjectColor (drw_infos[resource_id].drw_ObjectColor)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl
index 12921a31b23..c2062be667b 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl
@@ -1,4 +1,3 @@
-#ifndef VOLUMETRICS
void node_ambient_occlusion(vec4 color,
float dist,
vec3 normal,
@@ -11,16 +10,12 @@ void node_ambient_occlusion(vec4 color,
vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
OcclusionData data = occlusion_search(viewPosition, maxzBuffer, dist, inverted, sample_count);
- vec3 V = cameraVec(worldPosition);
+ vec3 V = cameraVec(g_data.P);
vec3 N = normalize(normal);
- vec3 Ng = safe_normalize(cross(dFdx(worldPosition), dFdy(worldPosition)));
+ vec3 Ng = safe_normalize(cross(dFdx(g_data.P), dFdy(g_data.P)));
float unused_error;
vec3 unused;
occlusion_eval(data, V, N, Ng, inverted, result_ao, unused_error, unused);
result_color = result_ao * color;
}
-#else
-/* Stub ambient occlusion because it is not compatible with volumetrics. */
-# define node_ambient_occlusion(a, b, c, d, e, f) (e = vec4(0); f = 0.0)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl
index ec49cc86761..e70f62dfc59 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl
@@ -1,17 +1,20 @@
-#ifndef VOLUMETRICS
void node_bsdf_anisotropic(vec4 color,
float roughness,
float anisotropy,
float rotation,
vec3 N,
vec3 T,
- const float use_multiscatter,
- const float ssr_id,
+ float weight,
out Closure result)
{
- node_bsdf_glossy(color, roughness, N, use_multiscatter, ssr_id, result);
+ closure_weight_add(g_reflection_data, weight);
+}
+
+void node_bsdf_anisotropic_eval(
+ vec4 color, float roughness, vec3 N, float weight, float use_multiscatter, out Closure result)
+{
+ if (closure_weight_threshold(g_reflection_data, weight)) {
+ g_reflection_data.color = color.rgb * weight;
+ g_reflection_data.N = N;
+ }
}
-#else
-/* Stub anisotropic because it is not compatible with volumetrics. */
-# define node_bsdf_anisotropic(a, b, c, d, e, f, g, h, result) (result = CLOSURE_DEFAULT)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_background.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_background.glsl
index 69ef4dcb7c7..7402e14da0a 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_background.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_background.glsl
@@ -1,11 +1,4 @@
-void node_background(vec4 color, float strength, out Closure result)
+void node_background(vec4 color, float strength, float weight, out Closure result)
{
-#ifndef VOLUMETRICS
- color *= strength;
- result = CLOSURE_DEFAULT;
- result.radiance = color.rgb;
- result.transmittance = vec3(0.0);
-#else
- result = CLOSURE_DEFAULT;
-#endif
+ g_emission_data.emission += color.rgb * strength * weight;
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl
index 9f73f654217..b9e5d26f7ed 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl
@@ -14,15 +14,15 @@ void node_bump(float strength,
float height_dx,
float height_dy,
vec3 N,
- vec3 surf_pos,
float invert,
out vec3 result)
{
- N = mat3(ViewMatrix) * normalize(N);
- dist *= gl_FrontFacing ? invert : -invert;
+ N = normalize(N);
+ dist *= FrontFacing ? invert : -invert;
- vec3 dPdx = dFdx(surf_pos);
- vec3 dPdy = dFdy(surf_pos);
+#ifdef GPU_FRAGMENT_SHADER
+ vec3 dPdx = dFdx(g_data.P);
+ vec3 dPdy = dFdy(g_data.P);
/* Get surface tangents from normal. */
vec3 Rx = cross(dPdy, N);
@@ -39,6 +39,7 @@ void node_bump(float strength,
result = normalize(abs(det) * N - dist * sign(det) * surfgrad);
result = normalize(mix(N, result, strength));
-
- result = mat3(ViewMatrixInverse) * result;
+#else
+ result = N;
+#endif
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl
index 03e61e9f472..29319fe342a 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl
@@ -1,6 +1,6 @@
-void camera(vec3 co, out vec3 outview, out float outdepth, out float outdist)
+void camera(out vec3 outview, out float outdepth, out float outdist)
{
- outdepth = abs(co.z);
- outdist = length(co);
- outview = normalize(co);
+ outdepth = abs(g_data.P.z);
+ outdist = length(g_data.P);
+ outview = normalize(-g_data.P);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl
index 01a16e194ca..973c830c476 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl
@@ -1,28 +1,13 @@
-#ifndef VOLUMETRICS
-CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_diffuse, Diffuse)
-
-void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out Closure result)
+void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, float weight, out Closure result)
{
- CLOSURE_VARS_DECLARE_1(Diffuse);
-
- in_Diffuse_0.N = N; /* Normalized during eval. */
- in_Diffuse_0.albedo = color.rgb;
-
- CLOSURE_EVAL_FUNCTION_1(node_bsdf_diffuse, Diffuse);
-
- result = CLOSURE_DEFAULT;
-
- out_Diffuse_0.radiance = render_pass_diffuse_mask(vec3(1.0), out_Diffuse_0.radiance);
- out_Diffuse_0.radiance *= color.rgb;
-
- result.radiance = out_Diffuse_0.radiance;
-
- /* TODO(fclem) Try to not use this. */
- closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result);
+ closure_weight_add(g_diffuse_data, weight);
}
-#else
-/* Stub diffuse because it is not compatible with volumetrics. */
-# define node_bsdf_diffuse(a, b, c, d) (d = CLOSURE_DEFAULT)
-#endif
+void node_bsdf_diffuse_eval(vec4 color, float roughness, vec3 N, float weight, out Closure result)
+{
+ if (closure_weight_threshold(g_diffuse_data, weight)) {
+ g_diffuse_data.color = color.rgb * weight;
+ g_diffuse_data.N = N;
+ }
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl
index 0838b5c8b71..3bfd718df5d 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl
@@ -1,9 +1,8 @@
-void node_displacement_object(
- float height, float midlevel, float scale, vec3 N, mat4 obmat, out vec3 result)
+void node_displacement_object(float height, float midlevel, float scale, vec3 N, out vec3 result)
{
- N = (vec4(N, 0.0) * obmat).xyz;
+ N = normal_world_to_object(N);
result = (height - midlevel) * scale * normalize(N);
- result = (obmat * vec4(result, 0.0)).xyz;
+ result = normal_object_to_world(result);
}
void node_displacement_world(float height, float midlevel, float scale, vec3 N, out vec3 result)
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl
index 0941482df45..f5dbffdee67 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl
@@ -1,6 +1,3 @@
-#ifndef VOLUMETRICS
-
-CLOSURE_EVAL_FUNCTION_DECLARE_3(node_eevee_specular, Diffuse, Glossy, Glossy)
void node_eevee_specular(vec4 diffuse,
vec4 specular,
@@ -12,69 +9,65 @@ void node_eevee_specular(vec4 diffuse,
float clearcoat_roughness,
vec3 clearcoat_normal,
float occlusion,
- float ssr_id,
+ float weight,
out Closure result)
{
- CLOSURE_VARS_DECLARE_3(Diffuse, Glossy, Glossy);
-
- in_common.occlusion = occlusion;
-
- in_Diffuse_0.N = normal; /* Normalized during eval. */
- in_Diffuse_0.albedo = diffuse.rgb;
- in_Glossy_1.N = normal; /* Normalized during eval. */
- in_Glossy_1.roughness = roughness;
+ /* Evaluate non sampled closures. */
+ g_emission_data.emission += emissive.rgb * weight;
+ g_transparency_data.transmittance += vec3(transp) * weight;
- in_Glossy_2.N = clearcoat_normal; /* Normalized during eval. */
- in_Glossy_2.roughness = clearcoat_roughness;
+ float alpha = (1.0 - transp) * weight;
- CLOSURE_EVAL_FUNCTION_3(node_eevee_specular, Diffuse, Glossy, Glossy);
+ closure_weight_add(g_diffuse_data, alpha);
+ closure_weight_add(g_reflection_data, alpha);
+ closure_weight_add(g_reflection_data, alpha * clearcoat * 0.25);
+}
- result = CLOSURE_DEFAULT;
+void node_eevee_specular_eval(vec4 diffuse,
+ vec4 specular,
+ float roughness,
+ vec4 emissive,
+ float transp,
+ vec3 N,
+ float clearcoat,
+ float clearcoat_roughness,
+ vec3 CN,
+ float occlusion,
+ float weight,
+ out Closure result)
+{
+ N = safe_normalize(N);
+ vec3 V = cameraVec(g_data.P);
- vec3 V = cameraVec(worldPosition);
+ float alpha = (1.0 - transp) * weight;
- {
- /* Diffuse. */
- out_Diffuse_0.radiance = render_pass_diffuse_mask(vec3(1), out_Diffuse_0.radiance);
- out_Diffuse_0.radiance *= in_Diffuse_0.albedo;
- result.radiance += out_Diffuse_0.radiance;
+ float diffuse_weight = alpha;
+ if (closure_weight_threshold(g_diffuse_data, diffuse_weight)) {
+ g_diffuse_data.color = diffuse.rgb * diffuse_weight;
+ g_diffuse_data.N = N;
}
- {
- /* Glossy. */
- float NV = dot(in_Glossy_1.N, V);
- vec2 split_sum = brdf_lut(NV, in_Glossy_1.roughness);
+
+ float specular_weight = alpha;
+ if (closure_weight_threshold(g_reflection_data, specular_weight)) {
+ float NV = dot(N, V);
+ vec2 split_sum = brdf_lut(NV, roughness);
vec3 brdf = F_brdf_single_scatter(specular.rgb, vec3(1.0), split_sum);
- out_Glossy_1.radiance = closure_mask_ssr_radiance(out_Glossy_1.radiance, ssr_id);
- out_Glossy_1.radiance *= brdf;
- out_Glossy_1.radiance = render_pass_glossy_mask(specular.rgb, out_Glossy_1.radiance);
- closure_load_ssr_data(
- out_Glossy_1.radiance, in_Glossy_1.roughness, in_Glossy_1.N, ssr_id, result);
+ g_reflection_data.color = specular.rgb * brdf * specular_weight;
+ g_reflection_data.N = N;
+ g_reflection_data.roughness = roughness;
}
- {
- /* Clearcoat. */
- float NV = dot(in_Glossy_2.N, V);
- vec2 split_sum = brdf_lut(NV, in_Glossy_2.roughness);
+
+ float clearcoat_weight = alpha * clearcoat * 0.25;
+ if (closure_weight_threshold(g_reflection_data, clearcoat_weight)) {
+ CN = safe_normalize(CN);
+ float NV = dot(CN, V);
+ vec2 split_sum = brdf_lut(NV, clearcoat_roughness);
vec3 brdf = F_brdf_single_scatter(vec3(0.04), vec3(1.0), split_sum);
- out_Glossy_2.radiance *= brdf * clearcoat * 0.25;
- out_Glossy_2.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_2.radiance);
- result.radiance += out_Glossy_2.radiance;
- }
- {
- /* Emission. */
- vec3 out_emission_radiance = render_pass_emission_mask(emissive.rgb);
- result.radiance += out_emission_radiance;
+ g_reflection_data.color = brdf * clearcoat_weight;
+ g_reflection_data.N = CN;
+ g_reflection_data.roughness = clearcoat_roughness;
}
-
- float alpha = 1.0 - transp;
- result.transmittance = vec3(transp);
- result.radiance *= alpha;
- result.ssr_data.rgb *= alpha;
}
-
-#else
-/* Stub specular because it is not compatible with volumetrics. */
-# define node_eevee_specular(a, b, c, d, e, f, g, h, i, j, k, result) (result = CLOSURE_DEFAULT)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl
index f2de7c2da39..fb7c814a2b3 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl
@@ -1,10 +1,4 @@
-void node_emission(vec4 color, float strength, vec3 vN, out Closure result)
+void node_emission(vec4 color, float strength, float weight, out Closure result)
{
- result = CLOSURE_DEFAULT;
-#ifndef VOLUMETRICS
- result.radiance = render_pass_emission_mask(color.rgb) * strength;
- result.ssr_normal = normal_encode(vN, viewCameraVec(viewPosition));
-#else
- result.emission = color.rgb * strength;
-#endif
+ g_emission_data.emission += color.rgb * strength * weight;
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl
index 7a4d28f2dd6..9fb98d598ab 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl
@@ -26,12 +26,11 @@ float fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta)
return fresnel_dielectric_cos(dot(Incoming, Normal), eta);
}
-void node_fresnel(float ior, vec3 N, vec3 I, out float result)
+void node_fresnel(float ior, vec3 N, out float result)
{
N = normalize(N);
- /* handle perspective/orthographic */
- vec3 I_view = (ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
+ vec3 V = cameraVec(g_data.P);
float eta = max(ior, 0.00001);
- result = fresnel_dielectric(I_view, N, (gl_FrontFacing) ? eta : 1.0 / eta);
+ result = fresnel_dielectric(V, N, (FrontFacing) ? eta : 1.0 / eta);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl
index a14ff5021bf..9c40715db47 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl
@@ -1,9 +1,4 @@
-void node_geometry(vec3 I,
- vec3 N,
- vec3 orco,
- mat4 objmat,
- mat4 toworld,
- vec2 barycentric,
+void node_geometry(vec3 orco,
out vec3 position,
out vec3 normal,
out vec3 tangent,
@@ -15,8 +10,7 @@ void node_geometry(vec3 I,
out float random_per_island)
{
/* handle perspective/orthographic */
- vec3 I_view = (ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
- incoming = -(toworld * vec4(I_view, 0.0)).xyz;
+ incoming = cameraVec(g_data.P);
#if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE)
position = -incoming;
@@ -27,14 +21,18 @@ void node_geometry(vec3 I,
pointiness = 0.0;
#else
- position = worldPosition;
+ position = g_data.P;
# ifndef VOLUMETRICS
- normal = normalize(N);
- vec3 B = dFdx(worldPosition);
- vec3 T = dFdy(worldPosition);
+ normal = normalize(g_data.N);
+# ifdef GPU_FRAGMENT_SHADER
+ vec3 B = dFdx(g_data.P);
+ vec3 T = dFdy(g_data.P);
true_normal = normalize(cross(B, T));
+# else /* GPU_VERTEX_SHADER */
+ true_normal = normal;
+# endif
# else
- normal = (toworld * vec4(N, 0.0)).xyz;
+ normal = (toworld * vec4(g_data.N, 0.0)).xyz;
true_normal = normal;
# endif
@@ -42,11 +40,11 @@ void node_geometry(vec3 I,
tangent = -hairTangent;
# else
tangent_orco_z(orco, orco);
- node_tangent(N, orco, objmat, tangent);
+ node_tangent(orco, tangent);
# endif
- parametric = vec3(barycentric, 0.0);
- backfacing = (gl_FrontFacing) ? 0.0 : 1.0;
+ parametric = vec3(g_data.barycentric_coords, 0.0);
+ backfacing = (FrontFacing) ? 0.0 : 1.0;
pointiness = 0.5;
random_per_island = 0.0;
#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl
index aa0a8873596..72d74949940 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl
@@ -1,57 +1,49 @@
-#ifndef VOLUMETRICS
-
-CLOSURE_EVAL_FUNCTION_DECLARE_2(node_bsdf_glass, Glossy, Refraction)
void node_bsdf_glass(vec4 color,
float roughness,
float ior,
vec3 N,
- const float do_multiscatter,
- const float ssr_id,
- out Closure result)
+ float weight,
+ float do_multiscatter,
+ out Closure result,
+ out float reflection_weight,
+ out float refraction_weight)
{
- CLOSURE_VARS_DECLARE_2(Glossy, Refraction);
-
- in_Glossy_0.N = N; /* Normalized during eval. */
- in_Glossy_0.roughness = roughness;
-
- in_Refraction_1.N = N; /* Normalized during eval. */
- in_Refraction_1.roughness = roughness;
- in_Refraction_1.ior = ior;
-
- CLOSURE_EVAL_FUNCTION_2(node_bsdf_glass, Glossy, Refraction);
-
- result = CLOSURE_DEFAULT;
-
- float NV = dot(in_Refraction_1.N, cameraVec(worldPosition));
-
- float fresnel = (do_multiscatter != 0.0) ?
- btdf_lut(NV, in_Refraction_1.roughness, in_Refraction_1.ior).y :
- F_eta(in_Refraction_1.ior, NV);
-
- vec2 split_sum = brdf_lut(NV, in_Glossy_0.roughness);
- vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(vec3(1.0), vec3(1.0), split_sum) :
- F_brdf_single_scatter(vec3(1.0), vec3(1.0), split_sum);
-
- out_Glossy_0.radiance = closure_mask_ssr_radiance(out_Glossy_0.radiance, ssr_id);
- out_Glossy_0.radiance *= brdf;
- out_Glossy_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Glossy_0.radiance);
- out_Glossy_0.radiance *= color.rgb * fresnel;
- closure_load_ssr_data(
- out_Glossy_0.radiance, in_Glossy_0.roughness, in_Glossy_0.N, ssr_id, result);
-
- float btdf = (do_multiscatter != 0.0) ?
- 1.0 :
- btdf_lut(NV, in_Refraction_1.roughness, in_Refraction_1.ior).x;
- out_Refraction_1.radiance *= btdf;
- out_Refraction_1.radiance = render_pass_glossy_mask(vec3(1.0), out_Refraction_1.radiance);
- out_Refraction_1.radiance *= color.rgb * (1.0 - fresnel);
- /* Simulate 2nd absorption event. */
- out_Refraction_1.radiance *= (refractionDepth > 0.0) ? color.rgb : vec3(1.0);
- result.radiance += out_Refraction_1.radiance;
+ N = safe_normalize(N);
+ vec3 V = cameraVec(g_data.P);
+ float NV = dot(N, V);
+
+ float fresnel = (do_multiscatter != 0.0) ? btdf_lut(NV, roughness, ior).y : F_eta(ior, NV);
+ reflection_weight = fresnel;
+ refraction_weight = 1.0 - fresnel;
+ closure_weight_add(g_reflection_data, reflection_weight);
+ closure_weight_add(g_refraction_data, refraction_weight);
}
-#else
-/* Stub glass because it is not compatible with volumetrics. */
-# define node_bsdf_glass(a, b, c, d, e, f, result) (result = CLOSURE_DEFAULT)
-#endif
+void node_bsdf_glass_eval(vec4 color,
+ float roughness,
+ float ior,
+ vec3 N,
+ float weight,
+ float do_multiscatter,
+ float reflection_weight,
+ float refraction_weight,
+ out Closure result)
+{
+ N = safe_normalize(N);
+ if (closure_weight_threshold(g_reflection_data, reflection_weight)) {
+ g_reflection_data.color = color.rgb * reflection_weight;
+ g_reflection_data.N = N;
+ g_reflection_data.roughness = roughness;
+ }
+ if (closure_weight_threshold(g_refraction_data, refraction_weight)) {
+ vec3 V = cameraVec(g_data.P);
+ float NV = dot(N, V);
+ float btdf = (do_multiscatter != 0.0) ? 1.0 : btdf_lut(NV, roughness, ior).x;
+
+ g_refraction_data.color = color.rgb * (refraction_weight * btdf);
+ g_refraction_data.N = N;
+ g_refraction_data.roughness = roughness;
+ g_refraction_data.ior = ior;
+ }
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl
index fa83bfb6c7a..b73c6f9437f 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl
@@ -1,33 +1,15 @@
-#ifndef VOLUMETRICS
-CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_glossy, Glossy)
-
-void node_bsdf_glossy(
- vec4 color, float roughness, vec3 N, float use_multiscatter, float ssr_id, out Closure result)
+void node_bsdf_glossy(vec4 color, float roughness, vec3 N, float weight, out Closure result)
{
- bool do_ssr = (ssrToggle && int(ssr_id) == outputSsrId);
-
- CLOSURE_VARS_DECLARE_1(Glossy);
-
- in_Glossy_0.N = N; /* Normalized during eval. */
- in_Glossy_0.roughness = roughness;
-
- CLOSURE_EVAL_FUNCTION_1(node_bsdf_glossy, Glossy);
-
- result = CLOSURE_DEFAULT;
-
- vec2 split_sum = brdf_lut(dot(in_Glossy_0.N, cameraVec(worldPosition)), in_Glossy_0.roughness);
- vec3 brdf = (use_multiscatter != 0.0) ? F_brdf_multi_scatter(vec3(1.0), vec3(1.0), split_sum) :
- F_brdf_single_scatter(vec3(1.0), vec3(1.0), split_sum);
- out_Glossy_0.radiance = closure_mask_ssr_radiance(out_Glossy_0.radiance, ssr_id);
- out_Glossy_0.radiance *= brdf;
- out_Glossy_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Glossy_0.radiance);
- out_Glossy_0.radiance *= color.rgb;
- closure_load_ssr_data(
- out_Glossy_0.radiance, in_Glossy_0.roughness, in_Glossy_0.N, ssr_id, result);
+ closure_weight_add(g_reflection_data, weight);
}
-#else
-/* Stub glossy because it is not compatible with volumetrics. */
-# define node_bsdf_glossy(a, b, c, d, e, result) (result = CLOSURE_DEFAULT)
-#endif
+void node_bsdf_glossy_eval(
+ vec4 color, float roughness, vec3 N, float weight, float use_multiscatter, out Closure result)
+{
+ if (closure_weight_threshold(g_reflection_data, weight)) {
+ g_reflection_data.color = color.rgb * weight;
+ g_reflection_data.N = N;
+ g_reflection_data.roughness = roughness;
+ }
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
index 59f0377869b..e74389f93e4 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
@@ -2,24 +2,16 @@
void node_hair_info(float hair_length,
out float is_strand,
out float intercept,
- out float length,
+ out float out_length,
out float thickness,
out vec3 tangent,
out float random)
{
- length = hair_length;
-#ifdef HAIR_SHADER
- is_strand = 1.0;
- intercept = hairTime;
- thickness = hairThickness;
- tangent = normalize(worldNormal);
- random = wang_hash_noise(
- uint(hairStrandID)); /* TODO: could be precomputed per strand instead. */
-#else
- is_strand = 0.0;
- intercept = 0.0;
- thickness = 0.0;
- tangent = vec3(1.0);
- random = 0.0;
-#endif
+ is_strand = float(g_data.is_strand);
+ intercept = g_data.hair_time;
+ thickness = g_data.hair_thickness;
+ out_length = hair_length;
+ tangent = normalize(interp.N);
+ /* TODO: could be precomputed per strand instead. */
+ random = wang_hash_noise(uint(g_data.hair_strand_id));
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl
index 50ce2bf2ab8..075fd3ec835 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl
@@ -1,8 +1,4 @@
-void node_holdout(out Closure result)
+void node_holdout(float weight, out Closure result)
{
- result = CLOSURE_DEFAULT;
-#ifndef VOLUMETRICS
- result.holdout = 1.0;
- result.flag = CLOSURE_HOLDOUT_FLAG;
-#endif
+ g_transparency_data.holdout += weight;
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl
index 588d295bcc4..fe09b8bcd4c 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl
@@ -1,15 +1,15 @@
-void node_layer_weight(float blend, vec3 N, vec3 I, out float fresnel, out float facing)
+void node_layer_weight(float blend, vec3 N, out float fresnel, out float facing)
{
N = normalize(N);
/* fresnel */
float eta = max(1.0 - blend, 0.00001);
- vec3 I_view = (ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
+ vec3 V = cameraVec(g_data.P);
- fresnel = fresnel_dielectric(I_view, N, (gl_FrontFacing) ? 1.0 / eta : eta);
+ fresnel = fresnel_dielectric(V, N, (FrontFacing) ? 1.0 / eta : eta);
/* facing */
- facing = abs(dot(I_view, N));
+ facing = abs(dot(V, N));
if (blend != 0.5) {
blend = clamp(blend, 0.0, 0.99999);
blend = (blend < 0.5) ? 2.0 * blend : 0.5 / (1.0 - blend);
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl
index 50c87e3f105..628a3d5e0e5 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl
@@ -13,19 +13,19 @@ void node_light_path(out float is_camera_ray,
out float transmission_depth)
{
/* Supported. */
- is_camera_ray = (rayType == EEVEE_RAY_CAMERA) ? 1.0 : 0.0;
- is_shadow_ray = (rayType == EEVEE_RAY_SHADOW) ? 1.0 : 0.0;
- is_diffuse_ray = (rayType == EEVEE_RAY_DIFFUSE) ? 1.0 : 0.0;
- is_glossy_ray = (rayType == EEVEE_RAY_GLOSSY) ? 1.0 : 0.0;
+ is_camera_ray = float(g_data.ray_type == RAY_TYPE_CAMERA);
+ is_shadow_ray = float(g_data.ray_type == RAY_TYPE_SHADOW);
+ is_diffuse_ray = float(g_data.ray_type == RAY_TYPE_DIFFUSE);
+ is_glossy_ray = float(g_data.ray_type == RAY_TYPE_GLOSSY);
/* Kind of supported. */
is_singular_ray = is_glossy_ray;
is_reflection_ray = is_glossy_ray;
is_transmission_ray = is_glossy_ray;
- ray_depth = rayDepth;
- diffuse_depth = (is_diffuse_ray == 1.0) ? rayDepth : 0.0;
- glossy_depth = (is_glossy_ray == 1.0) ? rayDepth : 0.0;
+ ray_depth = g_data.ray_depth;
+ diffuse_depth = (is_diffuse_ray == 1.0) ? g_data.ray_depth : 0.0;
+ glossy_depth = (is_glossy_ray == 1.0) ? g_data.ray_depth : 0.0;
transmission_depth = (is_transmission_ray == 1.0) ? glossy_depth : 0.0;
+ ray_length = g_data.ray_length;
/* Not supported. */
- ray_length = 1.0;
transparent_depth = 0.0;
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl
index c6203bc36ab..523c44fa36a 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl
@@ -135,17 +135,74 @@ mat3 euler_to_mat3(vec3 euler)
return mat;
}
-void direction_transform_m4v3(vec3 vin, mat4 mat, out vec3 vout)
+void normal_transform_object_to_world(vec3 vin, out vec3 vout)
{
- vout = (mat * vec4(vin, 0.0)).xyz;
+ vout = normal_object_to_world(vin);
}
-void normal_transform_transposed_m4v3(vec3 vin, mat4 mat, out vec3 vout)
+void normal_transform_world_to_object(vec3 vin, out vec3 vout)
{
- vout = transpose(mat3(mat)) * vin;
+ vout = normal_world_to_object(vin);
}
-void point_transform_m4v3(vec3 vin, mat4 mat, out vec3 vout)
+void direction_transform_object_to_world(vec3 vin, out vec3 vout)
{
- vout = (mat * vec4(vin, 1.0)).xyz;
+ vout = transform_direction(ModelMatrix, vin);
+}
+
+void direction_transform_object_to_view(vec3 vin, out vec3 vout)
+{
+ vout = transform_direction(ModelMatrix, vin);
+ vout = transform_direction(ViewMatrix, vout);
+}
+
+void direction_transform_view_to_world(vec3 vin, out vec3 vout)
+{
+ vout = transform_direction(ViewMatrixInverse, vin);
+}
+
+void direction_transform_view_to_object(vec3 vin, out vec3 vout)
+{
+ vout = transform_direction(ViewMatrixInverse, vin);
+ vout = transform_direction(ModelMatrixInverse, vout);
+}
+
+void direction_transform_world_to_view(vec3 vin, out vec3 vout)
+{
+ vout = transform_direction(ViewMatrix, vin);
+}
+
+void direction_transform_world_to_object(vec3 vin, out vec3 vout)
+{
+ vout = transform_direction(ModelMatrixInverse, vin);
+}
+
+void point_transform_object_to_world(vec3 vin, out vec3 vout)
+{
+ vout = point_object_to_view(vin);
+}
+
+void point_transform_object_to_view(vec3 vin, out vec3 vout)
+{
+ vout = point_object_to_world(vin);
+}
+
+void point_transform_view_to_world(vec3 vin, out vec3 vout)
+{
+ vout = point_view_to_object(vin);
+}
+
+void point_transform_view_to_object(vec3 vin, out vec3 vout)
+{
+ vout = point_view_to_world(vin);
+}
+
+void point_transform_world_to_view(vec3 vin, out vec3 vout)
+{
+ vout = point_world_to_object(vin);
+}
+
+void point_transform_world_to_object(vec3 vin, out vec3 vout)
+{
+ vout = point_world_to_view(vin);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl
index 2b4a0204d97..e219e2b9bbe 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl
@@ -1,13 +1,13 @@
-void node_normal_map(vec4 info, vec4 tangent, vec3 normal, vec3 texnormal, out vec3 outnormal)
+void node_normal_map(vec4 tangent, vec3 texnormal, out vec3 outnormal)
{
if (all(equal(tangent, vec4(0.0, 0.0, 0.0, 1.0)))) {
- outnormal = normal;
+ outnormal = g_data.N;
return;
}
- tangent *= (gl_FrontFacing ? 1.0 : -1.0);
- vec3 B = tangent.w * cross(normal, tangent.xyz) * sign(info.w);
+ tangent *= (FrontFacing ? 1.0 : -1.0);
+ vec3 B = tangent.w * cross(g_data.N, tangent.xyz) * sign(ObjectInfo.w);
- outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * normal;
+ outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * g_data.N;
outnormal = normalize(outnormal);
}
@@ -21,7 +21,7 @@ void color_to_blender_normal_new_shading(vec3 color, out vec3 normal)
normal = vec3(2.0, -2.0, -2.0) * color - vec3(1.0);
}
-void node_normal_map_mix(float strength, vec3 newnormal, vec3 oldnormal, out vec3 outnormal)
+void node_normal_map_mix(float strength, vec3 newnormal, out vec3 outnormal)
{
- outnormal = normalize(mix(oldnormal, newnormal, max(strength, 0.0)));
+ outnormal = normalize(mix(g_data.N, newnormal, max(strength, 0.0)));
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl
index ff77b0beea2..c932fa39dd8 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl
@@ -1,16 +1,13 @@
-void node_object_info(mat4 obmat,
- vec4 obcolor,
- vec4 info,
- float mat_index,
+void node_object_info(float mat_index,
out vec3 location,
out vec4 color,
out float object_index,
out float material_index,
out float random)
{
- location = obmat[3].xyz;
- color = obcolor;
- object_index = info.x;
+ location = ModelMatrix[3].xyz;
+ color = ObjectColor;
+ object_index = ObjectInfo.x;
material_index = mat_index;
- random = info.z;
+ random = ObjectInfo.z;
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl
index 14271f9d107..2c24f50264c 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl
@@ -1,20 +1,20 @@
-void node_output_material(Closure surface,
- Closure volume,
- vec3 displacement,
- float alpha_threshold,
- float shadow_threshold,
- out Closure result)
+
+void node_output_material_surface(Closure surface, out Closure out_surface)
{
-#ifdef VOLUMETRICS
- result = volume;
-#else
- result = surface;
-# if defined(USE_ALPHA_HASH)
- /* Alpha clip emulation. */
- if ((rayType != EEVEE_RAY_SHADOW) ? (alpha_threshold >= 0.0) : (shadow_threshold >= 0.0)) {
- float alpha = saturate(1.0 - avg(result.transmittance));
- result.transmittance = vec3(step(alpha, max(alpha_threshold, shadow_threshold)));
- }
-# endif
-#endif
+ out_surface = surface;
+}
+
+void node_output_material_volume(Closure volume, out Closure out_volume)
+{
+ out_volume = volume;
+}
+
+void node_output_material_displacement(vec3 displacement, out vec3 out_displacement)
+{
+ out_displacement = displacement;
+}
+
+void node_output_material_thickness(float thickness, out float out_thickness)
+{
+ out_thickness = thickness;
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl
index 5eb853a4c1a..37c34a4f0d7 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl
@@ -1,14 +1,10 @@
-uniform float backgroundAlpha;
-void node_output_world(Closure surface, Closure volume, out Closure result)
+void node_output_world_surface(Closure surface, out Closure out_surface)
{
-#ifndef VOLUMETRICS
- float alpha = renderPassEnvironment ? 1.0 : backgroundAlpha;
- result = CLOSURE_DEFAULT;
- result.radiance = surface.radiance * alpha;
- result.transmittance = vec3(0.0);
- result.holdout = (1.0 - alpha);
-#else
- result = volume;
-#endif /* VOLUMETRICS */
+ out_surface = surface;
+}
+
+void node_output_world_volume(Closure volume, out Closure out_volume)
+{
+ out_volume = volume;
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl
index bdd60c20a81..5602345ea4a 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl
@@ -1,8 +1,4 @@
-void particle_info(vec4 sprops,
- vec4 loc,
- vec3 vel,
- vec3 avel,
- out float index,
+void particle_info(out float index,
out float random,
out float age,
out float life_time,
@@ -11,13 +7,14 @@ void particle_info(vec4 sprops,
out vec3 velocity,
out vec3 angular_velocity)
{
- index = sprops.x;
- random = loc.w;
- age = sprops.y;
- life_time = sprops.z;
- size = sprops.w;
+ /* Unsupported for now. */
+ index = 0.0;
+ random = 0.0;
+ age = 0.0;
+ life_time = 0.0;
+ size = 0.0;
- location = loc.xyz;
- velocity = vel;
- angular_velocity = avel;
+ location = vec3(0.0);
+ velocity = vec3(0.0);
+ angular_velocity = vec3(0.0);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
index bba84c2be52..355046e1dba 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
@@ -1,4 +1,4 @@
-#ifndef VOLUMETRICS
+
vec3 tint_from_color(vec3 color)
{
float lum = dot(color, vec3(0.3, 0.6, 0.1)); /* luminance approx. */
@@ -13,8 +13,6 @@ float principled_sheen(float NV)
return sheen;
}
-CLOSURE_EVAL_FUNCTION_DECLARE_4(node_bsdf_principled, Diffuse, Glossy, Glossy, Refraction)
-
void node_bsdf_principled(vec4 base_color,
float subsurface,
vec3 subsurface_radius,
@@ -40,169 +38,152 @@ void node_bsdf_principled(vec4 base_color,
vec3 N,
vec3 CN,
vec3 T,
- const float do_diffuse,
- const float do_clearcoat,
- const float do_refraction,
+ float weight,
const float do_multiscatter,
- float ssr_id,
- float sss_id,
- vec3 sss_scale,
- out Closure result)
+ out Closure result,
+ out float diffuse_weight,
+ out float specular_weight,
+ out float glass_reflection_weight,
+ out float glass_transmission_weight,
+ out float clearcoat_weight)
{
/* Match cycles. */
metallic = saturate(metallic);
transmission = saturate(transmission);
- float diffuse_weight = (1.0 - transmission) * (1.0 - metallic);
+ diffuse_weight = (1.0 - transmission) * (1.0 - metallic);
transmission *= (1.0 - metallic);
- float specular_weight = (1.0 - transmission);
- clearcoat = max(clearcoat, 0.0);
+ specular_weight = (1.0 - transmission);
+ clearcoat_weight = max(clearcoat, 0.0) * 0.25;
transmission_roughness = 1.0 - (1.0 - roughness) * (1.0 - transmission_roughness);
specular = max(0.0, specular);
- CLOSURE_VARS_DECLARE_4(Diffuse, Glossy, Glossy, Refraction);
-
- in_Diffuse_0.N = N; /* Normalized during eval. */
- in_Diffuse_0.albedo = mix(base_color.rgb, subsurface_color.rgb, subsurface);
-
- in_Glossy_1.N = N; /* Normalized during eval. */
- in_Glossy_1.roughness = roughness;
-
- in_Glossy_2.N = CN; /* Normalized during eval. */
- in_Glossy_2.roughness = clearcoat_roughness;
-
- in_Refraction_3.N = N; /* Normalized during eval. */
- in_Refraction_3.roughness = do_multiscatter != 0.0 ? roughness : transmission_roughness;
- in_Refraction_3.ior = ior;
-
- CLOSURE_EVAL_FUNCTION_4(node_bsdf_principled, Diffuse, Glossy, Glossy, Refraction);
-
- result = CLOSURE_DEFAULT;
-
- /* This will tag the whole eval for optimisation. */
- if (do_diffuse == 0.0) {
- out_Diffuse_0.radiance = vec3(0);
- }
- if (do_clearcoat == 0.0) {
- out_Glossy_2.radiance = vec3(0);
- }
- if (do_refraction == 0.0) {
- out_Refraction_3.radiance = vec3(0);
- }
-
- vec3 V = cameraVec(worldPosition);
-
- /* Glossy_1 will always be evaluated. */
- float NV = dot(in_Glossy_1.N, V);
+ N = safe_normalize(N);
+ vec3 V = cameraVec(g_data.P);
+ float NV = dot(N, V);
+
+ /* Evaluate non sampled closures. */
+ g_emission_data.emission += emission.rgb * emission_strength * weight;
+ g_transparency_data.transmittance += vec3(1.0 - alpha) * weight;
+ /* Apply alpha and weight on sampled closures. */
+ alpha *= weight;
+ diffuse_weight *= alpha;
+ specular_weight *= alpha;
+ transmission *= alpha;
+ clearcoat_weight *= alpha;
+
+ float fresnel = (do_multiscatter != 0.0) ? btdf_lut(NV, roughness, ior).y : F_eta(ior, NV);
+ glass_reflection_weight = fresnel * transmission;
+ glass_transmission_weight = (1.0 - fresnel) * transmission;
+
+ closure_weight_add(g_diffuse_data, diffuse_weight);
+ closure_weight_add(g_reflection_data, glass_reflection_weight);
+ closure_weight_add(g_reflection_data, specular_weight);
+ closure_weight_add(g_reflection_data, clearcoat_weight);
+ closure_weight_add(g_refraction_data, glass_transmission_weight);
+}
+void node_bsdf_principled_eval(vec4 base_color,
+ float subsurface,
+ vec3 subsurface_radius,
+ vec4 subsurface_color,
+ float subsurface_ior,
+ float subsurface_anisotropy,
+ float metallic,
+ float specular,
+ float specular_tint,
+ float roughness,
+ float anisotropic,
+ float anisotropic_rotation,
+ float sheen,
+ float sheen_tint,
+ float clearcoat,
+ float clearcoat_roughness,
+ float ior,
+ float transmission,
+ float transmission_roughness,
+ vec4 emission,
+ float emission_strength,
+ float alpha,
+ vec3 N,
+ vec3 CN,
+ vec3 T,
+ float weight,
+ const float do_multiscatter,
+ float diffuse_weight,
+ float specular_weight,
+ float glass_reflection_weight,
+ float glass_transmission_weight,
+ float clearcoat_weight,
+ out Closure result)
+{
vec3 base_color_tint = tint_from_color(base_color.rgb);
- float fresnel = (do_multiscatter != 0.0) ?
- btdf_lut(NV, in_Glossy_1.roughness, in_Refraction_3.ior).y :
- F_eta(in_Refraction_3.ior, NV);
+ N = safe_normalize(N);
+ vec3 V = cameraVec(g_data.P);
+ float NV = dot(N, V);
- {
- /* Glossy reflections.
- * Separate Glass reflections and main specular reflections to match Cycles renderpasses. */
- out_Glossy_1.radiance = closure_mask_ssr_radiance(out_Glossy_1.radiance, ssr_id);
-
- vec2 split_sum = brdf_lut(NV, roughness);
+ vec2 split_sum = brdf_lut(NV, roughness);
- vec3 glossy_radiance_final = vec3(0.0);
- if (transmission > 1e-5) {
- /* Glass Reflection: Reuse radiance from Glossy1. */
- vec3 out_glass_refl_radiance = out_Glossy_1.radiance;
-
- /* Poor approximation since we baked the LUT using a fixed IOR. */
- vec3 f0 = mix(vec3(1.0), base_color.rgb, specular_tint);
- vec3 f90 = vec3(1);
-
- vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) :
- F_brdf_single_scatter(f0, f90, split_sum);
-
- out_glass_refl_radiance *= brdf;
- out_glass_refl_radiance = render_pass_glossy_mask(vec3(1), out_glass_refl_radiance);
- out_glass_refl_radiance *= fresnel * transmission;
- glossy_radiance_final += out_glass_refl_radiance;
- }
- if (specular_weight > 1e-5) {
- vec3 dielectric_f0_color = mix(vec3(1.0), base_color_tint, specular_tint);
- vec3 metallic_f0_color = base_color.rgb;
- vec3 f0 = mix((0.08 * specular) * dielectric_f0_color, metallic_f0_color, metallic);
- /* Cycles does this blending using the microfacet fresnel factor. However, our fresnel
- * is already baked inside the split sum LUT. We approximate using by modifying the
- * changing the f90 color directly in a non linear fashion. */
- vec3 f90 = mix(f0, vec3(1), fast_sqrt(specular));
-
- vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) :
- F_brdf_single_scatter(f0, f90, split_sum);
-
- out_Glossy_1.radiance *= brdf;
- out_Glossy_1.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_1.radiance);
- out_Glossy_1.radiance *= specular_weight;
- glossy_radiance_final += out_Glossy_1.radiance;
- }
-
- closure_load_ssr_data(
- glossy_radiance_final, in_Glossy_1.roughness, in_Glossy_1.N, ssr_id, result);
+ /* Diffuse. */
+ if (closure_weight_threshold(g_diffuse_data, diffuse_weight)) {
+ g_diffuse_data.color = mix(base_color.rgb, subsurface_color.rgb, subsurface);
+ /* Sheen Coarse approximation: We reuse the diffuse radiance and just scale it. */
+ vec3 sheen_color = mix(vec3(1.0), base_color_tint, sheen_tint);
+ g_diffuse_data.color += sheen * sheen_color * principled_sheen(NV);
+ g_diffuse_data.color *= diffuse_weight;
+ g_diffuse_data.N = N;
+ g_diffuse_data.sss_radius = subsurface_radius * subsurface;
+ g_diffuse_data.sss_id = uint(resource_handle + 1);
}
- if (diffuse_weight > 1e-5) {
- /* Mask over all diffuse radiance. */
- out_Diffuse_0.radiance *= diffuse_weight;
+ /* Reflection. */
+ if (closure_weight_threshold(g_reflection_data, glass_reflection_weight)) {
+ /* Poor approximation since we baked the LUT using a fixed IOR. */
+ vec3 f0 = mix(vec3(1.0), base_color.rgb, specular_tint);
+ vec3 f90 = vec3(1);
- /* Sheen Coarse approximation: We reuse the diffuse radiance and just scale it. */
- vec3 sheen_color = mix(vec3(1), base_color_tint, sheen_tint);
- vec3 out_sheen_radiance = out_Diffuse_0.radiance * principled_sheen(NV);
- out_sheen_radiance = render_pass_diffuse_mask(vec3(1), out_sheen_radiance);
- out_sheen_radiance *= sheen * sheen_color;
- result.radiance += out_sheen_radiance;
-
- /* Diffuse / Subsurface. */
- float scale = avg(sss_scale) * subsurface;
- closure_load_sss_data(scale, out_Diffuse_0.radiance, in_Diffuse_0.albedo, int(sss_id), result);
- }
+ vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) :
+ F_brdf_single_scatter(f0, f90, split_sum);
- if (transmission > 1e-5) {
- float btdf = (do_multiscatter != 0.0) ?
- 1.0 :
- btdf_lut(NV, in_Refraction_3.roughness, in_Refraction_3.ior).x;
- /* TODO(fclem) This could be going to a transmission render pass instead. */
- out_Refraction_3.radiance *= btdf;
- out_Refraction_3.radiance = render_pass_glossy_mask(vec3(1), out_Refraction_3.radiance);
- out_Refraction_3.radiance *= base_color.rgb;
- /* Simulate 2nd transmission event. */
- out_Refraction_3.radiance *= (refractionDepth > 0.0) ? base_color.rgb : vec3(1);
- out_Refraction_3.radiance *= (1.0 - fresnel) * transmission;
- result.radiance += out_Refraction_3.radiance;
+ g_reflection_data.color = brdf * glass_reflection_weight;
+ g_reflection_data.N = N;
+ g_reflection_data.roughness = roughness;
}
-
- if (clearcoat > 1e-5) {
- float NV = dot(in_Glossy_2.N, V);
- vec2 split_sum = brdf_lut(NV, in_Glossy_2.roughness);
+ else if (closure_weight_threshold(g_reflection_data, specular_weight)) {
+ vec3 dielectric_f0_color = mix(vec3(1.0), base_color_tint, specular_tint);
+ vec3 metallic_f0_color = base_color.rgb;
+ vec3 f0 = mix((0.08 * specular) * dielectric_f0_color, metallic_f0_color, metallic);
+ /* Cycles does this blending using the microfacet fresnel factor. However, our fresnel
+ * is already baked inside the split sum LUT. We approximate using by modifying the
+ * changing the f90 color directly in a non linear fashion. */
+ vec3 f90 = mix(f0, vec3(1.0), fast_sqrt(specular));
+
+ vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) :
+ F_brdf_single_scatter(f0, f90, split_sum);
+
+ g_reflection_data.color = brdf * specular_weight;
+ g_reflection_data.N = N;
+ g_reflection_data.roughness = roughness;
+ }
+ else if (closure_weight_threshold(g_reflection_data, clearcoat_weight)) {
+ N = safe_normalize(CN);
+ float NV = dot(CN, V);
+ vec2 split_sum = brdf_lut(NV, roughness);
vec3 brdf = F_brdf_single_scatter(vec3(0.04), vec3(1.0), split_sum);
- out_Glossy_2.radiance *= brdf * clearcoat * 0.25;
- out_Glossy_2.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_2.radiance);
- result.radiance += out_Glossy_2.radiance;
+ g_reflection_data.color = brdf * clearcoat_weight;
+ g_reflection_data.N = CN;
+ g_reflection_data.roughness = clearcoat_roughness;
}
- {
- vec3 out_emission_radiance = render_pass_emission_mask(emission.rgb);
- out_emission_radiance *= emission_strength;
- result.radiance += out_emission_radiance;
- }
+ /* Refraction. */
+ if (closure_weight_threshold(g_refraction_data, glass_transmission_weight)) {
+ float btdf = (do_multiscatter != 0.0) ? 1.0 : btdf_lut(NV, roughness, ior).x;
- result.transmittance = vec3(1.0 - alpha);
- result.radiance *= alpha;
- result.ssr_data.rgb *= alpha;
-# ifdef USE_SSS
- result.sss_albedo *= alpha;
-# endif
+ g_refraction_data.color = base_color.rgb * (btdf * glass_transmission_weight);
+ g_refraction_data.N = N;
+ g_refraction_data.roughness = do_multiscatter != 0.0 ? roughness :
+ max(roughness, transmission_roughness);
+ g_refraction_data.ior = ior;
+ }
}
-
-#else
-/* clang-format off */
-/* Stub principled because it is not compatible with volumetrics. */
-# define node_bsdf_principled(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, cc, dd, ee, ff, result) (result = CLOSURE_DEFAULT)
-/* clang-format on */
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl
index 7cbc7218f5c..f8a7b7a77b1 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl
@@ -1,32 +1,17 @@
-#ifndef VOLUMETRICS
-CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_refraction, Refraction)
-
-void node_bsdf_refraction(vec4 color, float roughness, float ior, vec3 N, out Closure result)
+void node_bsdf_refraction(
+ vec4 color, float roughness, float ior, vec3 N, float weight, out Closure result)
{
- CLOSURE_VARS_DECLARE_1(Refraction);
-
- in_Refraction_0.N = N; /* Normalized during eval. */
- in_Refraction_0.roughness = roughness;
- in_Refraction_0.ior = ior;
-
- CLOSURE_EVAL_FUNCTION_1(node_bsdf_refraction, Refraction);
-
- result = CLOSURE_DEFAULT;
-
- out_Refraction_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Refraction_0.radiance);
- out_Refraction_0.radiance *= color.rgb;
- /* Simulate 2nd absorption event. */
- out_Refraction_0.radiance *= (refractionDepth > 0.0) ? color.rgb : vec3(1.0);
-
- result.radiance = out_Refraction_0.radiance;
-
- /* TODO(fclem) Try to not use this. */
- result.ssr_normal = normal_encode(mat3(ViewMatrix) * in_Refraction_0.N,
- viewCameraVec(viewPosition));
+ closure_weight_add(g_refraction_data, weight);
}
-#else
-/* Stub refraction because it is not compatible with volumetrics. */
-# define node_bsdf_refraction(a, b, c, d, e) (e = CLOSURE_DEFAULT)
-#endif
+void node_bsdf_refraction_eval(
+ vec4 color, float roughness, float ior, vec3 N, float weight, out Closure result)
+{
+ if (closure_weight_threshold(g_refraction_data, weight)) {
+ g_refraction_data.color = color.rgb * weight;
+ g_refraction_data.N = N;
+ g_refraction_data.roughness = roughness;
+ g_refraction_data.ior = ior;
+ }
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl
index f0f2f79c60e..d446860e2c7 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl
@@ -1,29 +1,4 @@
-#ifndef VOLUMETRICS
-
-CLOSURE_EVAL_FUNCTION_DECLARE_1(node_shader_to_rgba, Glossy)
void node_shader_to_rgba(Closure cl, out vec4 outcol, out float outalpha)
{
- vec4 spec_accum = vec4(0.0);
- if (ssrToggle && FLAG_TEST(cl.flag, CLOSURE_SSR_FLAG)) {
- CLOSURE_VARS_DECLARE_1(Glossy);
-
- vec3 vN = normal_decode(cl.ssr_normal, viewCameraVec(viewPosition));
- vec3 N = transform_direction(ViewMatrixInverse, vN);
-
- in_Glossy_0.N = N; /* Normalized during eval. */
- in_Glossy_0.roughness = cl.ssr_data.a;
-
- CLOSURE_EVAL_FUNCTION_1(node_shader_to_rgba, Glossy);
-
- spec_accum.rgb = out_Glossy_0.radiance;
- }
-
- outalpha = saturate(1.0 - avg(cl.transmittance));
- outcol = vec4((spec_accum.rgb * cl.ssr_data.rgb) + cl.radiance, 1.0);
-
-# ifdef USE_SSS
- outcol.rgb += cl.sss_irradiance.rgb * cl.sss_albedo;
-# endif
}
-#endif /* VOLUMETRICS */
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
index d0c159cdf37..786775b5857 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
@@ -1,6 +1,3 @@
-#ifndef VOLUMETRICS
-
-CLOSURE_EVAL_FUNCTION_DECLARE_1(node_subsurface_scattering, Diffuse)
void node_subsurface_scattering(vec4 color,
float scale,
@@ -8,25 +5,25 @@ void node_subsurface_scattering(vec4 color,
float ior,
float anisotropy,
vec3 N,
- float sss_id,
+ float weight,
out Closure result)
{
- CLOSURE_VARS_DECLARE_1(Diffuse);
-
- in_Diffuse_0.N = N; /* Normalized during eval. */
- in_Diffuse_0.albedo = color.rgb;
-
- CLOSURE_EVAL_FUNCTION_1(node_subsurface_scattering, Diffuse);
-
- result = CLOSURE_DEFAULT;
-
- closure_load_sss_data(scale, out_Diffuse_0.radiance, color.rgb, int(sss_id), result);
-
- /* TODO(fclem) Try to not use this. */
- closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result);
+ closure_weight_add(g_diffuse_data, weight);
}
-#else
-/* Stub subsurface scattering because it is not compatible with volumetrics. */
-# define node_subsurface_scattering(a, b, c, d, e, f, g, h) (h = CLOSURE_DEFAULT)
-#endif
+void node_subsurface_scattering_eval(vec4 color,
+ float scale,
+ vec3 radius,
+ float ior,
+ float anisotropy,
+ vec3 N,
+ float weight,
+ out Closure result)
+{
+ if (closure_weight_threshold(g_diffuse_data, weight)) {
+ g_diffuse_data.color = color.rgb * weight;
+ g_diffuse_data.N = N;
+ g_diffuse_data.sss_radius = radius * scale;
+ g_diffuse_data.sss_id = uint(resource_handle + 1);
+ }
+} \ No newline at end of file
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl
index ff2dbc7ead3..4e4bf759ec9 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl
@@ -18,8 +18,8 @@ void node_tangentmap(vec4 attr_tangent, out vec3 tangent)
tangent = normalize(attr_tangent.xyz);
}
-void node_tangent(vec3 N, vec3 orco, mat4 objmat, out vec3 T)
+void node_tangent(vec3 orco, out vec3 T)
{
- T = (objmat * vec4(orco, 0.0)).xyz;
- T = cross(N, normalize(cross(T, N)));
+ T = transform_direction(ModelMatrix, orco);
+ T = cross(g_data.N, normalize(cross(T, g_data.N)));
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl
index 20a65f23c05..b6e9b3cab91 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl
@@ -1,19 +1,3 @@
-void node_tex_environment_texco(vec3 viewvec, out vec3 worldvec)
-{
-#ifdef MESH_SHADER
- worldvec = worldPosition;
-#else
- vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(viewvec, 1.0) : vec4(0.0, 0.0, 1.0, 1.0);
- vec4 co_homogenous = (ProjectionMatrixInverse * v);
-
- vec3 co = co_homogenous.xyz / co_homogenous.w;
-# if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE)
- worldvec = mat3(ViewMatrixInverse) * co;
-# else
- worldvec = mat3(ModelMatrixInverse) * (mat3(ViewMatrixInverse) * co);
-# endif
-#endif
-}
void node_tex_environment_equirectangular(vec3 co, out vec3 uv)
{
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl
index 08d566224bf..a9954fdb14a 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl
@@ -1,7 +1,3 @@
-vec3 mtex_2d_mapping(vec3 vec)
-{
- return vec3(vec.xy * 0.5 + vec2(0.5), vec.z);
-}
void generated_from_orco(vec3 orco, out vec3 generated)
{
@@ -9,30 +5,19 @@ void generated_from_orco(vec3 orco, out vec3 generated)
# ifdef MESH_SHADER
generated = volumeObjectLocalCoord;
# else
- generated = worldPosition;
+ generated = g_data.P;
# endif
#else
generated = orco;
#endif
}
-void generated_texco(vec3 I, vec3 attr_orco, out vec3 generated)
+void generated_texco(vec3 attr_orco, out vec3 generated)
{
- vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(I, 1.0) : vec4(0.0, 0.0, 1.0, 1.0);
- vec4 co_homogenous = (ProjectionMatrixInverse * v);
- vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0);
- co.xyz = normalize(co.xyz);
-#if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE)
- generated = (ViewMatrixInverse * co).xyz;
-#else
generated_from_orco(attr_orco, generated);
-#endif
}
-void node_tex_coord(vec3 I,
- vec3 wN,
- mat4 obmatinv,
- vec4 camerafac,
+void node_tex_coord(mat4 obmatinv,
vec3 attr_orco,
vec3 attr_uv,
out vec3 generated,
@@ -43,50 +28,20 @@ void node_tex_coord(vec3 I,
out vec3 window,
out vec3 reflection)
{
+ vec3 N = safe_normalize(g_data.N);
+
generated = attr_orco;
- normal = normalize(normal_world_to_object(wN));
+ normal = normal_world_to_object(N);
uv = attr_uv;
- object = (obmatinv * (ViewMatrixInverse * vec4(I, 1.0))).xyz;
- camera = vec3(I.xy, -I.z);
- vec4 projvec = ProjectionMatrix * vec4(I, 1.0);
- window = vec3(mtex_2d_mapping(projvec.xyz / projvec.w).xy * camerafac.xy + camerafac.zw, 0.0);
- reflection = -reflect(cameraVec(worldPosition), normalize(wN));
-}
-void node_tex_coord_background(vec3 I,
- vec3 N,
- mat4 obmatinv,
- vec4 camerafac,
- vec3 attr_orco,
- vec3 attr_uv,
- out vec3 generated,
- out vec3 normal,
- out vec3 uv,
- out vec3 object,
- out vec3 camera,
- out vec3 window,
- out vec3 reflection)
-{
- vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(I, 1.0) : vec4(0.0, 0.0, 1.0, 1.0);
- vec4 co_homogenous = (ProjectionMatrixInverse * v);
+ object = transform_point((obmatinv[3][3] == 0.0) ? ModelMatrixInverse : obmatinv, g_data.P);
- vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0);
+ camera = transform_point(ViewMatrix, g_data.P);
+ camera.z = -camera.z;
+ /* TODO fix in panoramic view. */
+ window.xy = project_point(ViewProjectionMatrix, g_data.P).xy * 0.5 + 0.5;
+ window.xy = window.xy * CameraTexCoFactors.xy + CameraTexCoFactors.zw;
+ window.z = 0.0;
- co = normalize(co);
-
- vec3 coords = (ViewMatrixInverse * co).xyz;
-
- generated = coords;
- normal = -coords;
- uv = vec3(attr_uv.xy, 0.0);
- object = (obmatinv * vec4(coords, 1.0)).xyz;
-
- camera = vec3(co.xy, -co.z);
- window = vec3(mtex_2d_mapping(I).xy * camerafac.xy + camerafac.zw, 0.0);
-
- reflection = -coords;
+ reflection = -reflect(cameraVec(g_data.P), N);
}
-
-#if defined(WORLD_BACKGROUND) || (defined(PROBE_CAPTURE) && !defined(MESH_SHADER))
-# define node_tex_coord node_tex_coord_background
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl
index bbfc99ccc73..e46e16ba8c0 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl
@@ -1,9 +1,13 @@
-#ifndef VOLUMETRICS
-void node_bsdf_toon(vec4 color, float size, float tsmooth, vec3 N, out Closure result)
+void node_bsdf_toon(
+ vec4 color, float size, float tsmooth, vec3 N, float weight, out Closure result)
{
- node_bsdf_diffuse(color, 0.0, N, result);
+ closure_weight_add(g_diffuse_data, weight);
+}
+
+void node_bsdf_toon_eval(vec4 color, float roughness, vec3 N, float weight, out Closure result)
+{
+ if (closure_weight_threshold(g_diffuse_data, weight)) {
+ g_diffuse_data.color = color.rgb * weight;
+ g_diffuse_data.N = N;
+ }
}
-#else
-/* Stub toon because it is not compatible with volumetrics. */
-# define node_bsdf_toon(a, b, c, d, e) (e = CLOSURE_DEFAULT)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl
index 80bd3941b22..550afbb8b24 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl
@@ -1,21 +1,13 @@
-#ifndef VOLUMETRICS
-CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_translucent, Translucent)
-
-void node_bsdf_translucent(vec4 color, vec3 N, out Closure result)
+void node_bsdf_translucent(vec4 color, vec3 N, float weight, out Closure result)
{
- CLOSURE_VARS_DECLARE_1(Translucent);
-
- in_Translucent_0.N = -N; /* Normalized during eval. */
-
- CLOSURE_EVAL_FUNCTION_1(node_bsdf_translucent, Translucent);
-
- result = CLOSURE_DEFAULT;
- closure_load_ssr_data(vec3(0.0), 0.0, -in_Translucent_0.N, -1.0, result);
- result.radiance = render_pass_diffuse_mask(color.rgb, out_Translucent_0.radiance * color.rgb);
+ closure_weight_add(g_diffuse_data, weight);
}
-#else
-/* Stub translucent because it is not compatible with volumetrics. */
-# define node_bsdf_translucent(a, b, c) (c = CLOSURE_DEFAULT)
-#endif
+void node_bsdf_translucent_eval(vec4 color, vec3 N, float weight, out Closure result)
+{
+ if (closure_weight_threshold(g_diffuse_data, weight)) {
+ g_diffuse_data.color = color.rgb * weight;
+ g_diffuse_data.N = -N;
+ }
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl
index 9040f62bd3f..cfbd6178d95 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl
@@ -1,11 +1,4 @@
-#ifndef VOLUMETRICS
-void node_bsdf_transparent(vec4 color, out Closure result)
+void node_bsdf_transparent(vec4 color, float weight, out Closure result)
{
- result = CLOSURE_DEFAULT;
- result.radiance = vec3(0.0);
- result.transmittance = abs(color.rgb);
+ g_transparency_data.transmittance += color.rgb * weight;
}
-#else
-/* Stub transparent because it is not compatible with volumetrics. */
-# define node_bsdf_transparent(a, b) (b = CLOSURE_DEFAULT)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl
index 4b5ed172081..7f1344d03fe 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl
@@ -1,4 +1,4 @@
-void node_vector_displacement_tangent(vec4 vector,
+void node_vector_displacement_tangent(vec3 vector,
float midlevel,
float scale,
vec4 tangent,
@@ -7,24 +7,23 @@ void node_vector_displacement_tangent(vec4 vector,
mat4 viewmat,
out vec3 result)
{
- /* TODO(fclem): this is broken. revisit latter. */
- vec3 N_object = normalize(((vec4(normal, 0.0) * viewmat) * obmat).xyz);
- vec3 T_object = normalize(((vec4(tangent.xyz, 0.0) * viewmat) * obmat).xyz);
- vec3 B_object = tangent.w * normalize(cross(N_object, T_object));
+ vec3 oN = normalize(normal_world_to_object(normal));
+ vec3 oT = normalize(normal_world_to_object(tangent.xyz));
+ vec3 oB = tangent.w * normalize(cross(oN, oT));
- vec3 offset = (vector.xyz - vec3(midlevel)) * scale;
- result = offset.x * T_object + offset.y * N_object + offset.z * B_object;
- result = (obmat * vec4(result, 0.0)).xyz;
+ result = (vector - midlevel) * scale;
+ result = result.x * oT + result.y * oN + result.z * oB;
+ result = transform_point(ModelMatrix, result);
}
void node_vector_displacement_object(
- vec4 vector, float midlevel, float scale, mat4 obmat, out vec3 result)
+ vec3 vector, float midlevel, float scale, mat4 obmat, out vec3 result)
{
- result = (vector.xyz - vec3(midlevel)) * scale;
- result = (obmat * vec4(result, 0.0)).xyz;
+ result = (vector - midlevel) * scale;
+ result = transform_point(ModelMatrix, result);
}
-void node_vector_displacement_world(vec4 vector, float midlevel, float scale, out vec3 result)
+void node_vector_displacement_world(vec3 vector, float midlevel, float scale, out vec3 result)
{
- result = (vector.xyz - vec3(midlevel)) * scale;
+ result = (vector - midlevel) * scale;
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl
index 989f18b881a..642112418c6 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl
@@ -1,9 +1,12 @@
-#ifndef VOLUMETRICS
-void node_bsdf_velvet(vec4 color, float sigma, vec3 N, out Closure result)
+void node_bsdf_velvet(vec4 color, float sigma, vec3 N, float weight, out Closure result)
{
- node_bsdf_diffuse(color, 0.0, N, result);
+ closure_weight_add(g_diffuse_data, weight);
+}
+
+void node_bsdf_velvet_eval(vec4 color, float roughness, vec3 N, float weight, out Closure result)
+{
+ if (closure_weight_threshold(g_diffuse_data, weight)) {
+ g_diffuse_data.color = color.rgb * weight;
+ g_diffuse_data.N = N;
+ }
}
-#else
-/* Stub velvet because it is not compatible with volumetrics. */
-# define node_bsdf_velvet(a, b, c, d) (d = CLOSURE_DEFAULT)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl
index e6c0880cd07..d11c29c089a 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl
@@ -1,8 +1,3 @@
-void node_volume_absorption(vec4 color, float density, out Closure result)
+void node_volume_absorption(vec4 color, float density, float weight, out Closure result)
{
-#ifdef VOLUMETRICS
- result = Closure((1.0 - color.rgb) * density, vec3(0.0), vec3(0.0), 0.0);
-#else
- result = CLOSURE_DEFAULT;
-#endif
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl
index 884d5415c51..471440c88b5 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl
@@ -7,6 +7,7 @@ void node_volume_principled(vec4 color,
float blackbody_intensity,
vec4 blackbody_tint,
float temperature,
+ float weight,
float density_attribute,
vec4 color_attribute,
float temperature_attribute,
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl
index 02c54658be5..cbaf5cb3a08 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl
@@ -1,8 +1,4 @@
-void node_volume_scatter(vec4 color, float density, float anisotropy, out Closure result)
+void node_volume_scatter(
+ vec4 color, float density, float anisotropy, float weight, out Closure result)
{
-#ifdef VOLUMETRICS
- result = Closure(vec3(0.0), color.rgb * density, vec3(0.0), anisotropy);
-#else
- result = CLOSURE_DEFAULT;
-#endif
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl
index e2789e046e1..8881b99ddc0 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl
@@ -1,20 +1,21 @@
-#ifndef VOLUMETRICS
-void node_wireframe(float size, vec2 barycentric, vec3 barycentric_dist, out float fac)
+
+void node_wireframe(float size, out float fac)
{
- vec3 barys = barycentric.xyy;
- barys.z = 1.0 - barycentric.x - barycentric.y;
+ vec3 barys = g_data.barycentric_coords.xyy;
+ barys.z = 1.0 - barys.x - barys.y;
size *= 0.5;
- vec3 s = step(-size, -barys * barycentric_dist);
+ vec3 s = step(-size, -barys * g_data.barycentric_dists);
fac = max(s.x, max(s.y, s.z));
}
-void node_wireframe_screenspace(float size, vec2 barycentric, out float fac)
+void node_wireframe_screenspace(float size, out float fac)
{
- vec3 barys = barycentric.xyy;
- barys.z = 1.0 - barycentric.x - barycentric.y;
+ vec3 barys = g_data.barycentric_coords.xyy;
+ barys.z = 1.0 - barys.x - barys.y;
+#ifdef GPU_FRAGMENT_SHADER
size *= (1.0 / 3.0);
vec3 dx = dFdx(barys);
vec3 dy = dFdy(barys);
@@ -23,9 +24,7 @@ void node_wireframe_screenspace(float size, vec2 barycentric, out float fac)
vec3 s = step(-deltas * size, -barys);
fac = max(s.x, max(s.y, s.z));
-}
#else
-/* Stub wireframe because it is not compatible with volumetrics. */
-# define node_wireframe(a, b, c, d) (d = 0.0)
-# define node_wireframe_screenspace(a, b, c) (c = 0.0)
+ fac = 1.0;
#endif
+} \ No newline at end of file
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl
index 40e46bc250c..5a0aeb2f932 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl
@@ -1,25 +1,5 @@
-/* TODO: clean this `ifdef` mess. */
+
void world_normals_get(out vec3 N)
{
-#ifndef VOLUMETRICS
-# ifdef HAIR_SHADER
- vec3 B = normalize(cross(worldNormal, hairTangent));
- float cos_theta;
- if (hairThicknessRes == 1) {
- vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
- /* Random cosine normal distribution on the hair surface. */
- cos_theta = rand.x * 2.0 - 1.0;
- }
- else {
- /* Shade as a cylinder. */
- cos_theta = hairThickTime / hairThickness;
- }
- float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta));
- N = normalize(worldNormal * sin_theta + B * cos_theta);
-# else
- N = gl_FrontFacing ? worldNormal : -worldNormal;
-# endif
-#else
- generated_from_orco(vec3(0.0), N);
-#endif
+ N = g_data.N;
}
diff --git a/source/blender/makesdna/DNA_camera_defaults.h b/source/blender/makesdna/DNA_camera_defaults.h
index b0237caa544..6ad12137a47 100644
--- a/source/blender/makesdna/DNA_camera_defaults.h
+++ b/source/blender/makesdna/DNA_camera_defaults.h
@@ -52,6 +52,13 @@
.drawsize = 1.0f, \
.ortho_scale = 6.0, \
.flag = CAM_SHOWPASSEPARTOUT, \
+ .panorama_type = CAM_PANO_FISHEYE_EQUISOLID, \
+ .fisheye_fov = DEG2RADF(180.0f), \
+ .fisheye_lens = 10.5f, \
+ .latitude_min = DEG2RADF(-90.0f), \
+ .latitude_max = DEG2RADF(90.0f), \
+ .longitude_min = DEG2RADF(-180.0f), \
+ .longitude_max = DEG2RADF(180.0f), \
.passepartalpha = 0.5f, \
\
.dof = _DNA_DEFAULT_CameraDOFSettings, \
diff --git a/source/blender/makesdna/DNA_camera_types.h b/source/blender/makesdna/DNA_camera_types.h
index 32ebcade76d..9759fd8ad82 100644
--- a/source/blender/makesdna/DNA_camera_types.h
+++ b/source/blender/makesdna/DNA_camera_types.h
@@ -116,7 +116,15 @@ typedef struct Camera {
struct ListBase bg_images;
char sensor_fit;
- char _pad[7];
+ char panorama_type;
+ char _pad[6];
+ /** Panoramic properties. */
+ float fisheye_fov;
+ float fisheye_lens;
+ float latitude_min;
+ float latitude_max;
+ float longitude_min;
+ float longitude_max;
/* Stereo settings */
struct CameraStereoSettings stereo;
@@ -134,6 +142,14 @@ enum {
CAM_PANO = 2,
};
+/* panorama_type */
+enum {
+ CAM_PANO_EQUIRECTANGULAR = 0,
+ CAM_PANO_FISHEYE_EQUIDISTANT = 1,
+ CAM_PANO_FISHEYE_EQUISOLID = 2,
+ CAM_PANO_MIRRORBALL = 3,
+};
+
/* dtx */
enum {
CAM_DTX_CENTER = (1 << 0),
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index 0f570f8603d..b0ccb185ef6 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -49,6 +49,9 @@ struct MDeformVert;
#define GPENCIL_MIN_FILL_FAC 0.05f
#define GPENCIL_MAX_FILL_FAC 8.0f
+/* Used to convert pixel scale. */
+#define GPENCIL_PIXEL_FACTOR 2000.0f
+
#define GPENCIL_MAX_THICKNESS 5000
/* ***************************************** */
diff --git a/source/blender/makesdna/DNA_lightprobe_types.h b/source/blender/makesdna/DNA_lightprobe_types.h
index 1a3bda34a84..d8ab21727c1 100644
--- a/source/blender/makesdna/DNA_lightprobe_types.h
+++ b/source/blender/makesdna/DNA_lightprobe_types.h
@@ -123,7 +123,9 @@ typedef struct LightProbeCache {
float position[3], parallax_type;
float attenuation_fac;
float attenuation_type;
- float _pad3[2];
+ /* Used during baking. */
+ int probe_index;
+ int is_ready;
float attenuationmat[4][4];
float parallaxmat[4][4];
} LightProbeCache;
@@ -137,8 +139,10 @@ typedef struct LightGridCache {
/** World space vector between 2 opposite cells. */
float increment_x[3], attenuation_bias;
float increment_y[3], level_bias;
- float increment_z[3], _pad4;
- float visibility_bias, visibility_bleed, visibility_range, _pad5;
+ float increment_z[3], is_ready;
+ float visibility_bias, visibility_bleed, visibility_range;
+ /* Used during baking. */
+ int probe_index;
} LightGridCache;
/* These are used as UBO data. They need to be aligned to size of vec4. */
@@ -207,6 +211,8 @@ enum {
LIGHTCACHE_INVALID = (1 << 8),
/** The data present in the cache is valid but unusable on this GPU. */
LIGHTCACHE_NOT_USABLE = (1 << 9),
+ /** Used by baking cache to keep reflections black. */
+ LIGHTCACHE_NO_REFLECTION = (1 << 10),
};
/* EEVEE_LightCacheTexture->data_type */
diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h
index 5d682d963ab..b91d681df0c 100644
--- a/source/blender/makesdna/DNA_material_types.h
+++ b/source/blender/makesdna/DNA_material_types.h
@@ -213,7 +213,7 @@ typedef struct Material {
/* Transparency. */
float alpha_threshold;
- float refract_depth;
+ char _pad4[4];
char blend_method;
char blend_shadow;
char blend_flag;
@@ -331,7 +331,7 @@ enum {
MA_BL_HIDE_BACKFACE = (1 << 0),
MA_BL_SS_REFRACTION = (1 << 1),
MA_BL_CULL_BACKFACE = (1 << 2),
- MA_BL_TRANSLUCENCY = (1 << 3),
+ // MA_BL_TRANSLUCENCY = (1 << 3), /* deprecated */
};
/* blend_shadow */
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 099e41d7ff0..0b24dfea0b0 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -340,17 +340,6 @@ typedef struct bNode {
char iter_flag;
/**
- * XXX: eevee only, id of screen space reflection layer,
- * needs to be a float to feed GPU_uniform.
- */
- float ssr_id;
- /**
- * XXX: eevee only, id of screen subsurface scatter layer,
- * needs to be a float to feed GPU_uniform.
- */
- float sss_id;
-
- /**
* Describes the desired interface of the node. This is run-time data only.
* The actual interface of the node may deviate from the declaration temporarily.
* It's possible to sync the actual state of the node to the desired state. Currently, this is
diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h
index 7ba054e3133..502abfbdf1d 100644
--- a/source/blender/makesdna/DNA_scene_defaults.h
+++ b/source/blender/makesdna/DNA_scene_defaults.h
@@ -236,7 +236,8 @@
\
.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_SHADOW_SOFT, \
+ SCE_EEVEE_SHADOW_SOFT | \
+ SCE_EEVEE_FILM_LOG_ENCODING, \
}
#define _DNA_DEFAULT_Scene \
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 864358e040c..36fef503efc 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1640,7 +1640,7 @@ typedef struct SceneEEVEE {
float ssr_quality;
float ssr_max_roughness;
float ssr_thickness;
- float ssr_border_fade;
+ float ssr_border_fade DNA_DEPRECATED;
float ssr_firefly_fac;
float volumetric_start;
@@ -2440,16 +2440,16 @@ enum {
SCE_EEVEE_GTAO_ENABLED = (1 << 4),
SCE_EEVEE_GTAO_BENT_NORMALS = (1 << 5),
SCE_EEVEE_GTAO_BOUNCE = (1 << 6),
- // SCE_EEVEE_DOF_ENABLED = (1 << 7), /* Moved to camera->dof.flag */
+ SCE_EEVEE_FILM_LOG_ENCODING = (1 << 7),
SCE_EEVEE_BLOOM_ENABLED = (1 << 8),
SCE_EEVEE_MOTION_BLUR_ENABLED = (1 << 9),
SCE_EEVEE_SHADOW_HIGH_BITDEPTH = (1 << 10),
SCE_EEVEE_TAA_REPROJECTION = (1 << 11),
// SCE_EEVEE_SSS_ENABLED = (1 << 12), /* Unused */
// SCE_EEVEE_SSS_SEPARATE_ALBEDO = (1 << 13), /* Unused */
- SCE_EEVEE_SSR_ENABLED = (1 << 14),
- SCE_EEVEE_SSR_REFRACTION = (1 << 15),
- SCE_EEVEE_SSR_HALF_RESOLUTION = (1 << 16),
+ SCE_EEVEE_RAYTRACING_ENABLED = (1 << 14),
+ // SCE_EEVEE_SSR_REFRACTION = (1 << 15),
+ // SCE_EEVEE_SSR_HALF_RESOLUTION = (1 << 16), /* Unused */
SCE_EEVEE_SHOW_IRRADIANCE = (1 << 17),
SCE_EEVEE_SHOW_CUBEMAPS = (1 << 18),
SCE_EEVEE_GI_AUTOBAKE = (1 << 19),
diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c
index d218f92e639..2c955bfcbc4 100644
--- a/source/blender/makesrna/intern/rna_camera.c
+++ b/source/blender/makesrna/intern/rna_camera.c
@@ -513,6 +513,25 @@ void RNA_def_camera(BlenderRNA *brna)
{CAMERA_SENSOR_FIT_VERT, "VERTICAL", 0, "Vertical", "Fit to the sensor height"},
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem panorama_type_items[] = {
+ {CAM_PANO_EQUIRECTANGULAR,
+ "EQUIRECTANGULAR",
+ 0,
+ "Equirectangular",
+ "Render the scene with a spherical camera, also known as Lat Long panorama"},
+ {CAM_PANO_FISHEYE_EQUIDISTANT,
+ "FISHEYE_EQUIDISTANT",
+ 0,
+ "Fisheye Equidistant",
+ "Ideal for fulldomes, ignore the sensor dimensions"},
+ {CAM_PANO_FISHEYE_EQUISOLID,
+ "FISHEYE_EQUISOLID",
+ 0,
+ "Fisheye Equisolid",
+ "Similar to most fisheye modern lens, takes sensor dimensions into consideration"},
+ {CAM_PANO_MIRRORBALL, "MIRRORBALL", 0, "Mirror Ball", "Uses the mirror ball mapping"},
+ {0, NULL, 0, NULL, NULL},
+ };
srna = RNA_def_struct(brna, "Camera", "ID");
RNA_def_struct_ui_text(srna, "Camera", "Camera data-block for storing camera settings");
@@ -526,6 +545,11 @@ void RNA_def_camera(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Type", "Camera types");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update");
+ prop = RNA_def_property(srna, "panorama_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, panorama_type_items);
+ RNA_def_property_ui_text(prop, "Panorama Type", "Distortion to use for the calculation");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update");
+
prop = RNA_def_property(srna, "sensor_fit", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "sensor_fit");
RNA_def_property_enum_items(prop, sensor_fit_items);
@@ -581,6 +605,42 @@ void RNA_def_camera(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Focal Length", "Perspective Camera lens value in millimeters");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update");
+ prop = RNA_def_property(srna, "fisheye_fov", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_range(prop, 0.1745f, DEG2RAD(360.0f));
+ RNA_def_property_ui_range(prop, 0.1745f, DEG2RAD(360.0f * 5.0f), 10, 3);
+ RNA_def_property_ui_text(prop, "Field of View", "Field of view for the fisheye lens");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update");
+
+ prop = RNA_def_property(srna, "fisheye_lens", PROP_FLOAT, PROP_DISTANCE_CAMERA);
+ RNA_def_property_range(prop, 0.01f, 100.0f);
+ RNA_def_property_ui_range(prop, 0.01f, 15.0f, 100, 4);
+ RNA_def_property_ui_text(prop, "Fisheye Lens", "Lens focal length (mm)");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update");
+
+ prop = RNA_def_property(srna, "latitude_min", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_range(prop, DEG2RAD(-90.0f), DEG2RAD(90.0f));
+ RNA_def_property_ui_text(
+ prop, "Min Latitude", "Minimum latitude (vertical angle) for the equirectangular lens");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update");
+
+ prop = RNA_def_property(srna, "latitude_max", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_range(prop, DEG2RAD(-90.0f), DEG2RAD(90.0f));
+ RNA_def_property_ui_text(
+ prop, "Max Latitude", "Maximum latitude (vertical angle) for the equirectangular lens");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update");
+
+ prop = RNA_def_property(srna, "longitude_min", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_range(prop, DEG2RAD(-180.0f), DEG2RAD(180.0f));
+ RNA_def_property_ui_text(
+ prop, "Min Longitude", "Minimum longitude (horizontal angle) for the equirectangular lens");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update");
+
+ prop = RNA_def_property(srna, "longitude_max", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_range(prop, DEG2RAD(-180.0f), DEG2RAD(180.0f));
+ RNA_def_property_ui_text(
+ prop, "Max Longitude", "Maximum longitude (horizontal angle) for the equirectangular lens");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update");
+
prop = RNA_def_property(srna, "sensor_width", PROP_FLOAT, PROP_DISTANCE_CAMERA);
RNA_def_property_float_sdna(prop, NULL, "sensor_x");
RNA_def_property_range(prop, 1.0f, FLT_MAX);
diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c
index 22a75c0d992..9f3ee179ac7 100644
--- a/source/blender/makesrna/intern/rna_material.c
+++ b/source/blender/makesrna/intern/rna_material.c
@@ -807,21 +807,6 @@ void RNA_def_material(BlenderRNA *brna)
prop, "Screen Space Refraction", "Use raytraced screen space refractions");
RNA_def_property_update(prop, 0, "rna_Material_draw_update");
- prop = RNA_def_property(srna, "use_sss_translucency", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "blend_flag", MA_BL_TRANSLUCENCY);
- RNA_def_property_ui_text(
- prop, "Subsurface Translucency", "Add translucency effect to subsurface");
- RNA_def_property_update(prop, 0, "rna_Material_draw_update");
-
- prop = RNA_def_property(srna, "refraction_depth", PROP_FLOAT, PROP_DISTANCE);
- RNA_def_property_float_sdna(prop, NULL, "refract_depth");
- RNA_def_property_range(prop, 0.0f, FLT_MAX);
- RNA_def_property_ui_text(prop,
- "Refraction Depth",
- "Approximate the thickness of the object to compute two refraction "
- "event (0 is disabled)");
- RNA_def_property_update(prop, 0, "rna_Material_draw_update");
-
/* For Preview Render */
prop = RNA_def_property(srna, "preview_render_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "pr_type");
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 7f9890e492d..18e50be031b 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -7088,23 +7088,11 @@ static void rna_def_scene_eevee(BlenderRNA *brna)
/* Screen Space Reflection */
prop = RNA_def_property(srna, "use_ssr", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_SSR_ENABLED);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_RAYTRACING_ENABLED);
RNA_def_property_ui_text(prop, "Screen Space Reflections", "Enable screen space reflection");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
- prop = RNA_def_property(srna, "use_ssr_refraction", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_SSR_REFRACTION);
- RNA_def_property_ui_text(prop, "Screen Space Refractions", "Enable screen space Refractions");
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
-
- prop = RNA_def_property(srna, "use_ssr_halfres", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_SSR_HALF_RESOLUTION);
- RNA_def_property_ui_text(prop, "Half Res Trace", "Raytrace at a lower resolution");
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
-
prop = RNA_def_property(srna, "ssr_quality", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_ui_text(prop, "Trace Precision", "Precision of the screen space ray-tracing");
RNA_def_property_range(prop, 0.0f, 1.0f);
@@ -7125,12 +7113,6 @@ static void rna_def_scene_eevee(BlenderRNA *brna)
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
- prop = RNA_def_property(srna, "ssr_border_fade", PROP_FLOAT, PROP_FACTOR);
- RNA_def_property_ui_text(prop, "Edge Fading", "Screen percentage used to fade the SSR");
- RNA_def_property_range(prop, 0.0f, 0.5f);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
-
prop = RNA_def_property(srna, "ssr_firefly_fac", PROP_FLOAT, PROP_NONE);
RNA_def_property_ui_text(prop, "Clamp", "Clamp pixel intensity to remove noise (0 to disabled)");
RNA_def_property_range(prop, 0.0f, FLT_MAX);
@@ -7460,6 +7442,15 @@ static void rna_def_scene_eevee(BlenderRNA *brna)
RNA_def_property_range(prop, 0.0f, 50.0f);
RNA_def_property_ui_range(prop, 0.0f, 10.0f, 1, 2);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+
+ prop = RNA_def_property(srna, "use_log_space", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_FILM_LOG_ENCODING);
+ RNA_def_property_ui_text(prop,
+ "Log Space",
+ "Use pre-exposed logarithmic space for Anti-Aliasing accumulation "
+ "(reduces aliasing of bright areas in color passes)");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
}
static void rna_def_scene_gpencil(BlenderRNA *brna)
diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h
index 57740ce3ad9..3ebd3ef8a2a 100644
--- a/source/blender/nodes/NOD_shader.h
+++ b/source/blender/nodes/NOD_shader.h
@@ -160,10 +160,7 @@ struct bNode *ntreeShaderOutputNode(struct bNodeTree *ntree, int target);
/**
* This one needs to work on a local tree.
*/
-void ntreeGPUMaterialNodes(struct bNodeTree *localtree,
- struct GPUMaterial *mat,
- bool *has_surface_output,
- bool *has_volume_output);
+void ntreeGPUMaterialNodes(struct bNodeTree *localtree, struct GPUMaterial *mat);
#ifdef __cplusplus
}
diff --git a/source/blender/nodes/shader/node_shader_tree.cc b/source/blender/nodes/shader/node_shader_tree.cc
index 5c3c5889c98..8bf4f37a70b 100644
--- a/source/blender/nodes/shader/node_shader_tree.cc
+++ b/source/blender/nodes/shader/node_shader_tree.cc
@@ -62,12 +62,6 @@
#include "node_shader_util.hh"
#include "node_util.h"
-struct nTreeTags {
- float ssr_id, sss_id;
-};
-
-static void ntree_shader_tag_nodes(bNodeTree *ntree, bNode *output_node, nTreeTags *tags);
-
static bool shader_tree_poll(const bContext *C, bNodeTreeType *UNUSED(treetype))
{
Scene *scene = CTX_data_scene(C);
@@ -265,6 +259,18 @@ static bNodeSocket *ntree_shader_node_find_output(bNode *node, const char *ident
return ntree_shader_node_find_socket(&node->outputs, identifier);
}
+/* Find input socket at a specific position. */
+static bNodeSocket *ntree_shader_node_input_get(bNode *node, int n)
+{
+ return reinterpret_cast<bNodeSocket *>(BLI_findlink(&node->inputs, n));
+}
+
+/* Find output socket at a specific position. */
+static bNodeSocket *ntree_shader_node_output_get(bNode *node, int n)
+{
+ return reinterpret_cast<bNodeSocket *>(BLI_findlink(&node->outputs, n));
+}
+
/* Return true on success. */
static bool ntree_shader_expand_socket_default(bNodeTree *localtree,
bNode *node,
@@ -838,54 +844,309 @@ static bool ntree_shader_bump_branches(bNode *fromnode, bNode *UNUSED(tonode), v
return true;
}
-static bool ntree_tag_bsdf_cb(bNode *fromnode, bNode *UNUSED(tonode), void *userdata)
+/* Generate emission node to convert regular data to closure sockets.
+ * Returns validity of the tree.
+ */
+static bool ntree_shader_implicit_closure_cast(bNodeTree *ntree)
{
- switch (fromnode->type) {
- case SH_NODE_BSDF_ANISOTROPIC:
- case SH_NODE_EEVEE_SPECULAR:
- case SH_NODE_BSDF_GLOSSY:
- case SH_NODE_BSDF_GLASS:
- fromnode->ssr_id = ((nTreeTags *)userdata)->ssr_id;
- ((nTreeTags *)userdata)->ssr_id += 1;
- break;
- case SH_NODE_SUBSURFACE_SCATTERING:
- fromnode->sss_id = ((nTreeTags *)userdata)->sss_id;
- ((nTreeTags *)userdata)->sss_id += 1;
- break;
- case SH_NODE_BSDF_PRINCIPLED:
- fromnode->ssr_id = ((nTreeTags *)userdata)->ssr_id;
- fromnode->sss_id = ((nTreeTags *)userdata)->sss_id;
- ((nTreeTags *)userdata)->sss_id += 1;
- ((nTreeTags *)userdata)->ssr_id += 1;
- break;
- default:
- /* We could return false here but since we
- * allow the use of Closure as RGBA, we can have
- * BSDF nodes linked to other BSDF nodes. */
- break;
+ bool modified = false;
+ LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
+ if ((link->fromsock->type != SOCK_SHADER) && (link->tosock->type == SOCK_SHADER)) {
+ bNode *emission_node = nodeAddStaticNode(NULL, ntree, SH_NODE_EMISSION);
+ bNodeSocket *in_sock = ntree_shader_node_find_input(emission_node, "Color");
+ bNodeSocket *out_sock = ntree_shader_node_find_output(emission_node, "Emission");
+ nodeAddLink(ntree, link->fromnode, link->fromsock, emission_node, in_sock);
+ nodeAddLink(ntree, emission_node, out_sock, link->tonode, link->tosock);
+ nodeRemLink(ntree, link);
+ modified = true;
+ }
+ else if ((link->fromsock->type == SOCK_SHADER) && (link->tosock->type != SOCK_SHADER)) {
+ /* Meh. Not directly visible to the user. But better than nothing. */
+ fprintf(stderr, "Shader Nodetree Error: Invalid implicit socket conversion\n");
+ BKE_ntree_update_main_tree(G.main, ntree, nullptr);
+ return false;
+ }
+ }
+ if (modified) {
+ BKE_ntree_update_main_tree(G.main, ntree, nullptr);
}
-
return true;
}
-/* EEVEE: Scan the ntree to set the Screen Space Reflection
- * layer id of every specular node AND the Subsurface Scattering id of every SSS node.
- */
-void ntree_shader_tag_nodes(bNodeTree *ntree, bNode *output_node, nTreeTags *tags)
+/* Socket already has a link to it. Add weights together. */
+static void ntree_weight_tree_merge_weight(bNodeTree *ntree,
+ bNode *UNUSED(fromnode),
+ bNodeSocket *fromsock,
+ bNode **tonode,
+ bNodeSocket **tosock)
{
- if (output_node == nullptr) {
- return;
+ bNode *addnode = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ addnode->custom1 = NODE_MATH_ADD;
+ addnode->tmp_flag = -2; /* Copy */
+ bNodeSocket *addsock_out = ntree_shader_node_output_get(addnode, 0);
+ bNodeSocket *addsock_in0 = ntree_shader_node_input_get(addnode, 0);
+ bNodeSocket *addsock_in1 = ntree_shader_node_input_get(addnode, 1);
+ bNodeLink *oldlink = fromsock->link;
+ nodeAddLink(ntree, oldlink->fromnode, oldlink->fromsock, addnode, addsock_in0);
+ nodeAddLink(ntree, *tonode, *tosock, addnode, addsock_in1);
+ nodeRemLink(ntree, oldlink);
+ *tonode = addnode;
+ *tosock = addsock_out;
+}
+
+static bool ntree_weight_tree_tag_nodes(bNode *fromnode, bNode *tonode, void *userdata)
+{
+ int *node_count = (int *)userdata;
+ bool to_node_from_weight_tree = ELEM(tonode->type,
+ SH_NODE_ADD_SHADER,
+ SH_NODE_MIX_SHADER,
+ SH_NODE_OUTPUT_WORLD,
+ SH_NODE_OUTPUT_MATERIAL);
+ if (tonode->tmp_flag == -1 && to_node_from_weight_tree) {
+ tonode->tmp_flag = *node_count;
+ *node_count += (tonode->type == SH_NODE_MIX_SHADER) ? 4 : 1;
+ }
+ if (fromnode->tmp_flag == -1 && ELEM(fromnode->type, SH_NODE_ADD_SHADER, SH_NODE_MIX_SHADER)) {
+ fromnode->tmp_flag = *node_count;
+ *node_count += (fromnode->type == SH_NODE_MIX_SHADER) ? 4 : 1;
+ }
+ return to_node_from_weight_tree;
+}
+
+/* Invert evaluation order of the weight tree (add & mix closure nodes) to feed the closure nodes
+ * with their respective weights. */
+static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node)
+{
+ bNodeLink *displace_link = NULL;
+ bNodeSocket *displace_output = ntree_shader_node_find_input(output_node, "Displacement");
+ if (displace_output && displace_output->link) {
+ /* Remove any displacement link to avoid tagging it later on. */
+ displace_link = displace_output->link;
+ displace_output->link = NULL;
+ }
+ bNodeLink *thickness_link = NULL;
+ bNodeSocket *thickness_output = ntree_shader_node_find_input(output_node, "Thickness");
+ if (thickness_output && thickness_output->link) {
+ /* Remove any thickness link to avoid tagging it later on. */
+ thickness_link = thickness_output->link;
+ thickness_output->link = NULL;
+ }
+ /* Init tmp flag. */
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ node->tmp_flag = -1;
+ }
+ /* Tag nodes from the weight tree. Only tag output node and mix/add shader nodes. */
+ output_node->tmp_flag = 0;
+ int node_count = 1;
+ nodeChainIterBackwards(ntree, output_node, ntree_weight_tree_tag_nodes, &node_count, 0);
+ /* Make a mirror copy of the weight tree. */
+ bNode **nodes_copy = static_cast<bNode **>(MEM_mallocN(sizeof(bNode *) * node_count, __func__));
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->tmp_flag >= 0) {
+ int id = node->tmp_flag;
+
+ switch (node->type) {
+ case SH_NODE_OUTPUT_WORLD:
+ case SH_NODE_OUTPUT_MATERIAL: {
+ /* Start the tree with full weight. */
+ nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_VALUE);
+ nodes_copy[id]->tmp_flag = -2; /* Copy */
+ ((bNodeSocketValueFloat *)ntree_shader_node_output_get(nodes_copy[id], 0)->default_value)
+ ->value = 1.0f;
+ break;
+ }
+ case SH_NODE_ADD_SHADER: {
+ /* Simple passthrough node. Each original inputs will get the same weight. */
+ /* TODO(fclem) Better use some kind of reroute node? */
+ nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id]->custom1 = NODE_MATH_ADD;
+ nodes_copy[id]->tmp_flag = -2; /* Copy */
+ ((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value)
+ ->value = 0.0f;
+ break;
+ }
+ case SH_NODE_MIX_SHADER: {
+ /* We need multiple nodes to emulate the mix node in reverse. */
+ bNode *fromnode, *tonode;
+ bNodeSocket *fromsock, *tosock;
+ int id_start = id;
+ /* output = (factor * input_weight) */
+ nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id]->custom1 = NODE_MATH_MULTIPLY;
+ nodes_copy[id]->tmp_flag = -2; /* Copy */
+ id++;
+ /* output = ((1.0 - factor) * input_weight) <=> (input_weight - factor * input_weight) */
+ nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id]->custom1 = NODE_MATH_SUBTRACT;
+ nodes_copy[id]->tmp_flag = -2; /* Copy */
+ id++;
+ /* Node sanitizes the input mix factor by clamping it. */
+ nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id]->custom1 = NODE_MATH_ADD;
+ nodes_copy[id]->custom2 = SHD_MATH_CLAMP;
+ nodes_copy[id]->tmp_flag = -2; /* Copy */
+ ((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value)
+ ->value = 0.0f;
+ /* Copy default value if no link present. */
+ bNodeSocket *fac_sock = ntree_shader_node_find_input(node, "Fac");
+ if (!fac_sock->link) {
+ float default_value = ((bNodeSocketValueFloat *)fac_sock->default_value)->value;
+ bNodeSocket *dst_sock = ntree_shader_node_input_get(nodes_copy[id], 1);
+ ((bNodeSocketValueFloat *)dst_sock->default_value)->value = default_value;
+ }
+ id++;
+ /* Reroute the weight input to the 3 processing nodes. Simplify linking later-on. */
+ /* TODO(fclem) Better use some kind of reroute node? */
+ nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id]->custom1 = NODE_MATH_ADD;
+ nodes_copy[id]->tmp_flag = -2; /* Copy */
+ ((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value)
+ ->value = 0.0f;
+ id++;
+ /* Link between nodes for the substraction. */
+ fromnode = nodes_copy[id_start];
+ tonode = nodes_copy[id_start + 1];
+ fromsock = ntree_shader_node_output_get(fromnode, 0);
+ tosock = ntree_shader_node_input_get(tonode, 1);
+ nodeAddLink(ntree, fromnode, fromsock, tonode, tosock);
+ /* Link mix input to first node. */
+ fromnode = nodes_copy[id_start + 2];
+ tonode = nodes_copy[id_start];
+ fromsock = ntree_shader_node_output_get(fromnode, 0);
+ tosock = ntree_shader_node_input_get(tonode, 1);
+ nodeAddLink(ntree, fromnode, fromsock, tonode, tosock);
+ /* Link weight input to both multiply nodes. */
+ fromnode = nodes_copy[id_start + 3];
+ fromsock = ntree_shader_node_output_get(fromnode, 0);
+ tonode = nodes_copy[id_start];
+ tosock = ntree_shader_node_input_get(tonode, 0);
+ nodeAddLink(ntree, fromnode, fromsock, tonode, tosock);
+ tonode = nodes_copy[id_start + 1];
+ tosock = ntree_shader_node_input_get(tonode, 0);
+ nodeAddLink(ntree, fromnode, fromsock, tonode, tosock);
+ break;
+ }
+ default:
+ BLI_assert(0);
+ break;
+ }
+ }
+ }
+ /* Recreate links between copied nodes. */
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->tmp_flag >= 0) {
+ /* Naming can be confusing here. We use original nodelink name for from/to prefix.
+ * The final link is in reversed order. */
+ int socket_index;
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->inputs, socket_index) {
+ bNodeSocket *tosock;
+ bNode *tonode;
+
+ switch (node->type) {
+ case SH_NODE_OUTPUT_WORLD:
+ case SH_NODE_OUTPUT_MATERIAL:
+ case SH_NODE_ADD_SHADER: {
+ tonode = nodes_copy[node->tmp_flag];
+ tosock = ntree_shader_node_output_get(tonode, 0);
+ break;
+ }
+ case SH_NODE_MIX_SHADER: {
+ if (socket_index == 0) {
+ /* Mix Factor. */
+ tonode = nodes_copy[node->tmp_flag + 2];
+ tosock = ntree_shader_node_input_get(tonode, 1);
+ }
+ else if (socket_index == 1) {
+ /* Shader 1. */
+ tonode = nodes_copy[node->tmp_flag + 1];
+ tosock = ntree_shader_node_output_get(tonode, 0);
+ }
+ else {
+ /* Shader 2. */
+ tonode = nodes_copy[node->tmp_flag];
+ tosock = ntree_shader_node_output_get(tonode, 0);
+ }
+ break;
+ }
+ default:
+ BLI_assert(0);
+ break;
+ }
+
+ if (sock->link) {
+ bNodeSocket *fromsock;
+ bNode *fromnode = sock->link->fromnode;
+
+ switch (fromnode->type) {
+ case SH_NODE_ADD_SHADER: {
+ fromnode = nodes_copy[fromnode->tmp_flag];
+ fromsock = ntree_shader_node_input_get(fromnode, 1);
+ if (fromsock->link) {
+ ntree_weight_tree_merge_weight(ntree, fromnode, fromsock, &tonode, &tosock);
+ }
+ break;
+ }
+ case SH_NODE_MIX_SHADER: {
+ fromnode = nodes_copy[fromnode->tmp_flag + 3];
+ fromsock = ntree_shader_node_input_get(fromnode, 1);
+ if (fromsock->link) {
+ ntree_weight_tree_merge_weight(ntree, fromnode, fromsock, &tonode, &tosock);
+ }
+ break;
+ }
+ case SH_NODE_BACKGROUND:
+ case SH_NODE_BSDF_ANISOTROPIC:
+ case SH_NODE_BSDF_DIFFUSE:
+ case SH_NODE_BSDF_GLASS:
+ case SH_NODE_BSDF_GLOSSY:
+ case SH_NODE_BSDF_HAIR_PRINCIPLED:
+ case SH_NODE_BSDF_HAIR:
+ case SH_NODE_BSDF_PRINCIPLED:
+ case SH_NODE_BSDF_REFRACTION:
+ case SH_NODE_BSDF_TOON:
+ case SH_NODE_BSDF_TRANSLUCENT:
+ case SH_NODE_BSDF_TRANSPARENT:
+ case SH_NODE_BSDF_VELVET:
+ case SH_NODE_EEVEE_SPECULAR:
+ case SH_NODE_EMISSION:
+ case SH_NODE_HOLDOUT:
+ case SH_NODE_SUBSURFACE_SCATTERING:
+ case SH_NODE_VOLUME_ABSORPTION:
+ case SH_NODE_VOLUME_PRINCIPLED:
+ case SH_NODE_VOLUME_SCATTER:
+ fromsock = ntree_shader_node_find_input(fromnode, "Weight");
+ if (fromsock->link) {
+ ntree_weight_tree_merge_weight(ntree, fromnode, fromsock, &tonode, &tosock);
+ }
+ break;
+ default:
+ fromsock = sock->link->fromsock;
+ break;
+ }
+
+ /* Manually add the link to the socket to avoid calling
+ * BKE_ntree_update_main_tree(G.main, oop, nullptr. */
+ fromsock->link = nodeAddLink(ntree, fromnode, fromsock, tonode, tosock);
+ BLI_assert(fromsock->link);
+ }
+ }
+ }
+ }
+ /* Restore displacement & thickness link. */
+ if (displace_link) {
+ nodeAddLink(
+ ntree, displace_link->fromnode, displace_link->fromsock, output_node, displace_output);
+ }
+ if (thickness_link) {
+ nodeAddLink(
+ ntree, thickness_link->fromnode, thickness_link->fromsock, output_node, thickness_output);
}
- /* Make sure sockets links pointers are correct. */
BKE_ntree_update_main_tree(G.main, ntree, nullptr);
- nodeChainIterBackwards(ntree, output_node, ntree_tag_bsdf_cb, tags, 0);
+ MEM_freeN(nodes_copy);
}
-void ntreeGPUMaterialNodes(bNodeTree *localtree,
- GPUMaterial *mat,
- bool *has_surface_output,
- bool *has_volume_output)
+void ntreeGPUMaterialNodes(bNodeTree *localtree, GPUMaterial *mat)
{
bNodeTreeExec *exec;
@@ -901,6 +1162,13 @@ void ntreeGPUMaterialNodes(bNodeTree *localtree,
output = ntreeShaderOutputNode(localtree, SHD_OUTPUT_EEVEE);
}
+ /* Tree is valid if it contains no undefined implicit socket type cast. */
+ bool valid_tree = ntree_shader_implicit_closure_cast(localtree);
+
+ if (valid_tree && output != NULL) {
+ ntree_shader_weight_tree_invert(localtree, output);
+ }
+
/* Perform all needed modifications on the tree in order to support
* displacement/bump mapping.
*/
@@ -912,19 +1180,8 @@ void ntreeGPUMaterialNodes(bNodeTree *localtree,
LISTBASE_FOREACH (bNode *, node, &localtree->nodes) {
if (node->type == SH_NODE_OUTPUT_AOV) {
nodeChainIterBackwards(localtree, node, ntree_shader_bump_branches, localtree, 0);
- nTreeTags tags = {};
- tags.ssr_id = 1.0;
- tags.sss_id = 1.0;
- ntree_shader_tag_nodes(localtree, node, &tags);
}
}
-
- /* TODO(fclem): consider moving this to the gpu shader tree evaluation. */
- nTreeTags tags = {};
- tags.ssr_id = 1.0;
- tags.sss_id = 1.0;
- ntree_shader_tag_nodes(localtree, output, &tags);
-
exec = ntreeShaderBeginExecTree(localtree);
ntreeExecGPUNodes(exec, mat, output);
LISTBASE_FOREACH (bNode *, node, &localtree->nodes) {
@@ -933,23 +1190,6 @@ void ntreeGPUMaterialNodes(bNodeTree *localtree,
}
}
ntreeShaderEndExecTree(exec);
-
- /* EEVEE: Find which material domain was used (volume, surface ...). */
- *has_surface_output = false;
- *has_volume_output = false;
-
- if (output != nullptr) {
- bNodeSocket *surface_sock = ntree_shader_node_find_input(output, "Surface");
- bNodeSocket *volume_sock = ntree_shader_node_find_input(output, "Volume");
-
- if (surface_sock != nullptr) {
- *has_surface_output = (nodeCountSocketLinks(localtree, surface_sock) > 0);
- }
-
- if (volume_sock != nullptr) {
- *has_volume_output = (nodeCountSocketLinks(localtree, volume_sock) > 0);
- }
- }
}
bNodeTreeExec *ntreeShaderBeginExecTree_internal(bNodeExecContext *context,
diff --git a/source/blender/nodes/shader/node_shader_util.cc b/source/blender/nodes/shader/node_shader_util.cc
index bb377e2607a..8ea690a426f 100644
--- a/source/blender/nodes/shader/node_shader_util.cc
+++ b/source/blender/nodes/shader/node_shader_util.cc
@@ -296,7 +296,7 @@ void node_shader_gpu_default_tex_coord(GPUMaterial *mat, bNode *node, GPUNodeLin
{
if (!*link) {
*link = GPU_attribute(mat, CD_ORCO, "");
- GPU_link(mat, "generated_texco", GPU_builtin(GPU_VIEW_POSITION), *link, link);
+ GPU_link(mat, "generated_texco", *link, link);
node_shader_gpu_bump_tex_coord(mat, node, link);
}
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_bevel.cc b/source/blender/nodes/shader/nodes/node_shader_bevel.cc
index c4fa61753f5..dfde18d8e2a 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bevel.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bevel.cc
@@ -48,11 +48,7 @@ static int gpu_shader_bevel(GPUMaterial *mat,
GPUNodeStack *out)
{
if (!in[1].link) {
- GPU_link(mat,
- "direction_transform_m4v3",
- GPU_builtin(GPU_VIEW_NORMAL),
- GPU_builtin(GPU_INVERSE_VIEW_MATRIX),
- &in[1].link);
+ GPU_link(mat, "world_normals_get", &in[1].link);
}
return GPU_stack_link(mat, node, "node_bevel", in, out);
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc
index 3f0749ab2af..42f56a0b576 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc
@@ -40,6 +40,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.subtype(PROP_FACTOR);
b.add_input<decl::Vector>(N_("Normal")).hide_value();
b.add_input<decl::Vector>(N_("Tangent")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
@@ -67,13 +68,12 @@ static int node_shader_gpu_bsdf_anisotropic(GPUMaterial *mat,
float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f;
- return GPU_stack_link(mat,
- node,
- "node_bsdf_anisotropic",
- in,
- out,
- GPU_constant(&use_multi_scatter),
- GPU_constant(&node->ssr_id));
+ GPU_stack_link(mat, node, "node_bsdf_anisotropic", in, out);
+
+ GPU_stack_eval_link(
+ mat, node, "node_bsdf_anisotropic_eval", in, out, GPU_constant(&use_multi_scatter));
+
+ return true;
}
} // namespace blender::nodes::node_shader_bsdf_anisotropic_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc
index 5848ca76cdd..9767da497ed 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc
@@ -30,6 +30,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Vector>(N_("Normal")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
@@ -45,7 +46,8 @@ static int node_shader_gpu_bsdf_diffuse(GPUMaterial *mat,
GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE);
- return GPU_stack_link(mat, node, "node_bsdf_diffuse", in, out);
+ GPU_stack_link(mat, node, "node_bsdf_diffuse", in, out);
+ return GPU_stack_eval_link(mat, node, "node_bsdf_diffuse_eval", in, out);
}
} // namespace blender::nodes::node_shader_bsdf_diffuse_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc
index 47d4b87198b..fbc35c23734 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc
@@ -31,6 +31,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.subtype(PROP_FACTOR);
b.add_input<decl::Float>(N_("IOR")).default_value(1.45f).min(0.0f).max(1000.0f);
b.add_input<decl::Vector>(N_("Normal")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
@@ -53,17 +54,30 @@ static int node_shader_gpu_bsdf_glass(GPUMaterial *mat,
GPU_link(mat, "set_value_zero", &in[1].link);
}
- GPU_material_flag_set(mat, (eGPUMatFlag)(GPU_MATFLAG_GLOSSY | GPU_MATFLAG_REFRACT));
+ GPU_material_flag_set(mat, GPU_MATFLAG_GLOSSY | GPU_MATFLAG_REFRACT);
float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f;
- return GPU_stack_link(mat,
- node,
- "node_bsdf_glass",
- in,
- out,
- GPU_constant(&use_multi_scatter),
- GPU_constant(&node->ssr_id));
+ GPUNodeLink *reflection_weight;
+ GPUNodeLink *refraction_weight;
+
+ GPU_stack_link(mat,
+ node,
+ "node_bsdf_glass",
+ in,
+ out,
+ GPU_constant(&use_multi_scatter),
+ &reflection_weight,
+ &refraction_weight);
+
+ return GPU_stack_eval_link(mat,
+ node,
+ "node_bsdf_glass_eval",
+ in,
+ out,
+ GPU_constant(&use_multi_scatter),
+ reflection_weight,
+ refraction_weight);
}
} // namespace blender::nodes::node_shader_bsdf_glass_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc
index 03a3e634f56..879e00a6995 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc
@@ -30,6 +30,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Vector>(N_("Normal")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
@@ -56,13 +57,9 @@ static int node_shader_gpu_bsdf_glossy(GPUMaterial *mat,
float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f;
- return GPU_stack_link(mat,
- node,
- "node_bsdf_glossy",
- in,
- out,
- GPU_constant(&use_multi_scatter),
- GPU_constant(&node->ssr_id));
+ GPU_stack_link(mat, node, "node_bsdf_glossy", in, out);
+ return GPU_stack_eval_link(
+ mat, node, "node_bsdf_glossy_eval", in, out, GPU_constant(&use_multi_scatter));
}
} // namespace blender::nodes::node_shader_bsdf_glossy_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc
index 4c378d9bc09..e49a39b5a4a 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc
@@ -144,8 +144,6 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- GPUNodeLink *sss_scale;
-
/* Normals */
if (!in[22].link) {
GPU_link(mat, "world_normals_get", &in[22].link);
@@ -161,36 +159,17 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat,
if (!in[24].link) {
GPUNodeLink *orco = GPU_attribute(CD_ORCO, "");
GPU_link(mat, "tangent_orco_z", orco, &in[24].link);
- GPU_link(mat,
- "node_tangent",
- GPU_builtin(GPU_WORLD_NORMAL),
- in[24].link,
- GPU_builtin(GPU_OBJECT_MATRIX),
- &in[24].link);
+ GPU_link(mat, "node_tangent", in[24].link, &in[24].link);
}
#endif
bool use_diffuse = socket_not_one(6) && socket_not_one(17);
- bool use_subsurf = socket_not_zero(1) && use_diffuse && node->sss_id > 0;
+ bool use_subsurf = socket_not_zero(1) && use_diffuse;
bool use_refract = socket_not_one(6) && socket_not_zero(17);
- bool use_clear = socket_not_zero(14);
+ bool use_transparency = socket_not_one(21);
+ // bool use_clear = socket_not_zero(14);
- /* SSS Profile */
- if (use_subsurf) {
- bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2);
- bNodeSocketValueRGBA *socket_data = (bNodeSocketValueRGBA *)socket->default_value;
- /* For some reason it seems that the socket value is in ARGB format. */
- GPU_material_sss_profile_create(mat, &socket_data->value[1]);
- }
-
- if (in[2].link) {
- sss_scale = in[2].link;
- }
- else {
- GPU_link(mat, "set_rgb_one", &sss_scale);
- }
-
- uint flag = GPU_MATFLAG_GLOSSY;
+ eGPUMaterialFlag flag = GPU_MATFLAG_GLOSSY;
if (use_diffuse) {
flag |= GPU_MATFLAG_DIFFUSE;
}
@@ -198,28 +177,42 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat,
flag |= GPU_MATFLAG_REFRACT;
}
if (use_subsurf) {
- flag |= GPU_MATFLAG_SSS;
+ flag |= GPU_MATFLAG_SUBSURFACE;
+ }
+ if (use_transparency) {
+ flag |= GPU_MATFLAG_TRANSPARENT;
}
- float f_use_diffuse = use_diffuse ? 1.0f : 0.0f;
- float f_use_clearcoat = use_clear ? 1.0f : 0.0f;
- float f_use_refraction = use_refract ? 1.0f : 0.0f;
float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f;
- GPU_material_flag_set(mat, (eGPUMatFlag)flag);
-
- return GPU_stack_link(mat,
- node,
- "node_bsdf_principled",
- in,
- out,
- GPU_constant(&f_use_diffuse),
- GPU_constant(&f_use_clearcoat),
- GPU_constant(&f_use_refraction),
- GPU_constant(&use_multi_scatter),
- GPU_constant(&node->ssr_id),
- GPU_constant(&node->sss_id),
- sss_scale);
+ GPU_material_flag_set(mat, flag);
+
+ GPUNodeLink *diffuse_weight, *specular_weight, *glass_reflection_weight,
+ *glass_transmission_weight, *clearcoat_weight;
+
+ GPU_stack_link(mat,
+ node,
+ "node_bsdf_principled",
+ in,
+ out,
+ GPU_constant(&use_multi_scatter),
+ &diffuse_weight,
+ &specular_weight,
+ &glass_reflection_weight,
+ &glass_transmission_weight,
+ &clearcoat_weight);
+
+ return GPU_stack_eval_link(mat,
+ node,
+ "node_bsdf_principled_eval",
+ in,
+ out,
+ GPU_constant(&use_multi_scatter),
+ diffuse_weight,
+ specular_weight,
+ glass_reflection_weight,
+ glass_transmission_weight,
+ clearcoat_weight);
}
static void node_shader_update_principled(bNodeTree *ntree, bNode *node)
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc
index 0d588c82869..b1320714c70 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc
@@ -31,6 +31,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.subtype(PROP_FACTOR);
b.add_input<decl::Float>(N_("IOR")).default_value(1.45f).min(0.0f).max(1000.0f);
b.add_input<decl::Vector>(N_("Normal")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
@@ -55,7 +56,8 @@ static int node_shader_gpu_bsdf_refraction(GPUMaterial *mat,
GPU_material_flag_set(mat, GPU_MATFLAG_REFRACT);
- return GPU_stack_link(mat, node, "node_bsdf_refraction", in, out);
+ GPU_stack_link(mat, node, "node_bsdf_refraction", in, out);
+ return GPU_stack_eval_link(mat, node, "node_bsdf_refraction_eval", in, out);
}
} // namespace blender::nodes::node_shader_bsdf_refraction_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc
index 5093b896764..4c9ca56eab2 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc
@@ -38,6 +38,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Vector>(N_("Normal")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
@@ -58,7 +59,8 @@ static int node_shader_gpu_bsdf_toon(GPUMaterial *mat,
GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE);
- return GPU_stack_link(mat, node, "node_bsdf_toon", in, out);
+ GPU_stack_link(mat, node, "node_bsdf_toon", in, out);
+ return GPU_stack_eval_link(mat, node, "node_bsdf_toon_eval", in, out);
}
} // namespace blender::nodes::node_shader_bsdf_toon_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc
index 22891738299..ec85b5357fa 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc
@@ -25,6 +25,7 @@ static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
b.add_input<decl::Vector>(N_("Normal")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
@@ -40,7 +41,8 @@ static int node_shader_gpu_bsdf_translucent(GPUMaterial *mat,
GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE);
- return GPU_stack_link(mat, node, "node_bsdf_translucent", in, out);
+ GPU_stack_link(mat, node, "node_bsdf_translucent", in, out);
+ return GPU_stack_eval_link(mat, node, "node_bsdf_translucent_eval", in, out);
}
} // namespace blender::nodes::node_shader_bsdf_translucent_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc
index d764f4dd76b..d79f3884a4b 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc
@@ -33,6 +33,9 @@ static int node_shader_gpu_bsdf_transparent(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
+ if (in[0].link || !is_zero_v3(in[0].vec)) {
+ GPU_material_flag_set(mat, GPU_MATFLAG_TRANSPARENT);
+ }
return GPU_stack_link(mat, node, "node_bsdf_transparent", in, out);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc
index dd090236c08..7822c2f5961 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc
@@ -30,6 +30,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Vector>(N_("Normal")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
@@ -45,7 +46,8 @@ static int node_shader_gpu_bsdf_velvet(GPUMaterial *mat,
GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE);
- return GPU_stack_link(mat, node, "node_bsdf_velvet", in, out);
+ GPU_stack_link(mat, node, "node_bsdf_velvet", in, out);
+ return GPU_stack_link(mat, node, "node_bsdf_velvet_eval", in, out);
}
} // namespace blender::nodes::node_shader_bsdf_velvet_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_bump.cc b/source/blender/nodes/shader/nodes/node_shader_bump.cc
index 252abd02ad7..f0788c543c2 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bump.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bump.cc
@@ -66,8 +66,7 @@ static int gpu_shader_bump(GPUMaterial *mat,
float invert = (node->custom1) ? -1.0 : 1.0;
- return GPU_stack_link(
- mat, node, "node_bump", in, out, GPU_builtin(GPU_VIEW_POSITION), GPU_constant(&invert));
+ return GPU_stack_link(mat, node, "node_bump", in, out, GPU_constant(&invert));
}
} // namespace blender::nodes::node_shader_bump_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_camera.cc b/source/blender/nodes/shader/nodes/node_shader_camera.cc
index b9c624faed9..90d2b55b0f7 100644
--- a/source/blender/nodes/shader/nodes/node_shader_camera.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_camera.cc
@@ -38,11 +38,7 @@ static int gpu_shader_camera(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- GPUNodeLink *viewvec;
-
- viewvec = GPU_builtin(GPU_VIEW_POSITION);
- GPU_link(mat, "invert_z", viewvec, &viewvec);
- return GPU_stack_link(mat, node, "camera", in, out, viewvec);
+ return GPU_stack_link(mat, node, "camera", in, out);
}
} // namespace blender::nodes::node_shader_camera_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_displacement.cc b/source/blender/nodes/shader/nodes/node_shader_displacement.cc
index c56218e795a..47af2447440 100644
--- a/source/blender/nodes/shader/nodes/node_shader_displacement.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_displacement.cc
@@ -49,16 +49,11 @@ static int gpu_shader_displacement(GPUMaterial *mat,
GPUNodeStack *out)
{
if (!in[3].link) {
- GPU_link(mat,
- "direction_transform_m4v3",
- GPU_builtin(GPU_VIEW_NORMAL),
- GPU_builtin(GPU_INVERSE_VIEW_MATRIX),
- &in[3].link);
+ GPU_link(mat, "world_normals_get", &in[3].link);
}
if (node->custom1 == SHD_SPACE_OBJECT) {
- return GPU_stack_link(
- mat, node, "node_displacement_object", in, out, GPU_builtin(GPU_OBJECT_MATRIX));
+ return GPU_stack_link(mat, node, "node_displacement_object", in, out);
}
return GPU_stack_link(mat, node, "node_displacement_world", in, out);
diff --git a/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc b/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc
index e4c80725c8e..8db56551236 100644
--- a/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc
@@ -49,6 +49,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.subtype(PROP_FACTOR);
b.add_input<decl::Vector>(N_("Clear Coat Normal")).hide_value();
b.add_input<decl::Float>(N_("Ambient Occlusion")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
@@ -75,9 +76,10 @@ static int node_shader_gpu_eevee_specular(GPUMaterial *mat,
GPU_link(mat, "set_value", GPU_constant(&one), &in[9].link);
}
- GPU_material_flag_set(mat, static_cast<eGPUMatFlag>(GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_GLOSSY));
+ GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_GLOSSY);
- return GPU_stack_link(mat, node, "node_eevee_specular", in, out, GPU_constant(&node->ssr_id));
+ GPU_stack_link(mat, node, "node_eevee_specular", in, out);
+ return GPU_stack_eval_link(mat, node, "node_eevee_specular_eval", in, out);
}
} // namespace blender::nodes::node_shader_eevee_specular_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_emission.cc b/source/blender/nodes/shader/nodes/node_shader_emission.cc
index ea36763578f..93dadf8e898 100644
--- a/source/blender/nodes/shader/nodes/node_shader_emission.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_emission.cc
@@ -25,6 +25,7 @@ static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_input<decl::Float>(N_("Strength")).default_value(1.0f).min(0.0f).max(1000000.0f);
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("Emission"));
}
@@ -34,7 +35,8 @@ static int node_shader_gpu_emission(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- return GPU_stack_link(mat, node, "node_emission", in, out, GPU_builtin(GPU_VIEW_NORMAL));
+ GPU_material_flag_set(mat, GPU_MATFLAG_EMISSION);
+ return GPU_stack_link(mat, node, "node_emission", in, out);
}
} // namespace blender::nodes::node_shader_emission_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_fresnel.cc b/source/blender/nodes/shader/nodes/node_shader_fresnel.cc
index 5dba42fcc30..19eb79e2138 100644
--- a/source/blender/nodes/shader/nodes/node_shader_fresnel.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_fresnel.cc
@@ -35,14 +35,10 @@ static int node_shader_gpu_fresnel(GPUMaterial *mat,
GPUNodeStack *out)
{
if (!in[1].link) {
- in[1].link = GPU_builtin(GPU_VIEW_NORMAL);
- }
- else {
- GPU_link(
- mat, "direction_transform_m4v3", in[1].link, GPU_builtin(GPU_VIEW_MATRIX), &in[1].link);
+ GPU_link(mat, "world_normals_get", &in[1].link);
}
- return GPU_stack_link(mat, node, "node_fresnel", in, out, GPU_builtin(GPU_VIEW_POSITION));
+ return GPU_stack_link(mat, node, "node_fresnel", in, out);
}
} // namespace blender::nodes::node_shader_fresnel_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_geometry.cc b/source/blender/nodes/shader/nodes/node_shader_geometry.cc
index 2a5868333a9..35eef95adb5 100644
--- a/source/blender/nodes/shader/nodes/node_shader_geometry.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_geometry.cc
@@ -42,27 +42,15 @@ static int node_shader_gpu_geometry(GPUMaterial *mat,
{
/* HACK: Don't request GPU_BARYCENTRIC_TEXCO if not used because it will
* trigger the use of geometry shader (and the performance penalty it implies). */
- const float val[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- GPUNodeLink *bary_link = (!out[5].hasoutput) ? GPU_constant(val) :
- GPU_builtin(GPU_BARYCENTRIC_TEXCO);
if (out[5].hasoutput) {
GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC);
}
/* Opti: don't request orco if not needed. */
+ const float val[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPUNodeLink *orco_link = (!out[2].hasoutput) ? GPU_constant(val) :
GPU_attribute(mat, CD_ORCO, "");
- const bool success = GPU_stack_link(mat,
- node,
- "node_geometry",
- in,
- out,
- GPU_builtin(GPU_VIEW_POSITION),
- GPU_builtin(GPU_WORLD_NORMAL),
- orco_link,
- GPU_builtin(GPU_OBJECT_MATRIX),
- GPU_builtin(GPU_INVERSE_VIEW_MATRIX),
- bary_link);
+ const bool success = GPU_stack_link(mat, node, "node_geometry", in, out, orco_link);
int i;
LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) {
diff --git a/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc b/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc
index b4210ce154c..0294a0bde07 100644
--- a/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc
@@ -36,14 +36,10 @@ static int node_shader_gpu_layer_weight(GPUMaterial *mat,
GPUNodeStack *out)
{
if (!in[1].link) {
- in[1].link = GPU_builtin(GPU_VIEW_NORMAL);
- }
- else {
- GPU_link(
- mat, "direction_transform_m4v3", in[1].link, GPU_builtin(GPU_VIEW_MATRIX), &in[1].link);
+ GPU_link(mat, "world_normals_get", &in[1].link);
}
- return GPU_stack_link(mat, node, "node_layer_weight", in, out, GPU_builtin(GPU_VIEW_POSITION));
+ return GPU_stack_link(mat, node, "node_layer_weight", in, out);
}
} // namespace blender::nodes::node_shader_layer_weight_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_normal_map.cc b/source/blender/nodes/shader/nodes/node_shader_normal_map.cc
index 338b4c62a3a..9eadb6ce014 100644
--- a/source/blender/nodes/shader/nodes/node_shader_normal_map.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_normal_map.cc
@@ -99,18 +99,16 @@ static int gpu_shader_normal_map(GPUMaterial *mat,
GPU_link(mat, color_to_normal_fnc_name, newnormal, &newnormal);
switch (nm->space) {
case SHD_SPACE_TANGENT:
+ GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO);
GPU_link(mat,
"node_normal_map",
- GPU_builtin(GPU_OBJECT_INFO),
GPU_attribute(mat, CD_TANGENT, nm->uv_map),
- GPU_builtin(GPU_WORLD_NORMAL),
newnormal,
&newnormal);
break;
case SHD_SPACE_OBJECT:
case SHD_SPACE_BLENDER_OBJECT:
- GPU_link(
- mat, "direction_transform_m4v3", newnormal, GPU_builtin(GPU_OBJECT_MATRIX), &newnormal);
+ GPU_link(mat, "normal_transform_object_to_world", newnormal, &newnormal);
break;
case SHD_SPACE_WORLD:
case SHD_SPACE_BLENDER_WORLD:
@@ -118,8 +116,7 @@ static int gpu_shader_normal_map(GPUMaterial *mat,
break;
}
- GPUNodeLink *oldnormal = GPU_builtin(GPU_WORLD_NORMAL);
- GPU_link(mat, "node_normal_map_mix", strength, newnormal, oldnormal, &out[0].link);
+ GPU_link(mat, "node_normal_map_mix", strength, newnormal, &out[0].link);
return true;
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_object_info.cc b/source/blender/nodes/shader/nodes/node_shader_object_info.cc
index 7bc7039d11e..75c9c8f9206 100644
--- a/source/blender/nodes/shader/nodes/node_shader_object_info.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_object_info.cc
@@ -38,15 +38,8 @@ static int node_shader_gpu_object_info(GPUMaterial *mat,
{
Material *ma = GPU_material_get_material(mat);
float index = ma ? ma->index : 0.0f;
- return GPU_stack_link(mat,
- node,
- "node_object_info",
- in,
- out,
- GPU_builtin(GPU_OBJECT_MATRIX),
- GPU_builtin(GPU_OBJECT_COLOR),
- GPU_builtin(GPU_OBJECT_INFO),
- GPU_constant(&index));
+ GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO);
+ return GPU_stack_link(mat, node, "node_object_info", in, out, GPU_constant(&index));
}
} // namespace blender::nodes::node_shader_object_info_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_output_aov.cc b/source/blender/nodes/shader/nodes/node_shader_output_aov.cc
index 6ffd763b0d1..b5177014f3a 100644
--- a/source/blender/nodes/shader/nodes/node_shader_output_aov.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_output_aov.cc
@@ -47,16 +47,13 @@ static int node_shader_gpu_output_aov(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
GPUNodeStack *in,
- GPUNodeStack *out)
+ GPUNodeStack *UNUSED(out))
{
- GPUNodeLink *outlink;
NodeShaderOutputAOV *aov = (NodeShaderOutputAOV *)node->storage;
/* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash` and
* `EEVEE_renderpasses_aov_hash`. */
unsigned int hash = BLI_hash_string(aov->name) << 1;
- GPU_stack_link(mat, node, "node_output_aov", in, out, &outlink);
- GPU_material_add_output_link_aov(mat, outlink, hash);
-
+ GPU_material_add_output_link_aov(mat, in[0].link, hash);
return true;
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_output_material.cc b/source/blender/nodes/shader/nodes/node_shader_output_material.cc
index 5fc95b92e3f..e96f55cf3cf 100644
--- a/source/blender/nodes/shader/nodes/node_shader_output_material.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_output_material.cc
@@ -36,31 +36,24 @@ static int node_shader_gpu_output_material(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- GPUNodeLink *outlink, *alpha_threshold_link, *shadow_threshold_link;
- Material *ma = GPU_material_get_material(mat);
-
- static float no_alpha_threshold = -1.0f;
- if (ma) {
- alpha_threshold_link = GPU_uniform((ma->blend_method == MA_BM_CLIP) ? &ma->alpha_threshold :
- &no_alpha_threshold);
- shadow_threshold_link = GPU_uniform((ma->blend_shadow == MA_BS_CLIP) ? &ma->alpha_threshold :
- &no_alpha_threshold);
+ GPUNodeLink *outlink_surface, *outlink_volume, *outlink_displacement, *outlink_thickness;
+ /* Passthrough node in order to do the right socket conversions (important for displacement). */
+ if (in[0].link) {
+ GPU_link(mat, "node_output_material_surface", in[0].link, &outlink_surface);
+ GPU_material_output_surface(mat, outlink_surface);
}
- else {
- alpha_threshold_link = GPU_uniform(&no_alpha_threshold);
- shadow_threshold_link = GPU_uniform(&no_alpha_threshold);
+ if (in[1].link) {
+ GPU_link(mat, "node_output_material_volume", in[1].link, &outlink_volume);
+ GPU_material_output_volume(mat, outlink_volume);
+ }
+ if (in[2].link) {
+ GPU_link(mat, "node_output_material_displacement", in[2].link, &outlink_displacement);
+ GPU_material_output_displacement(mat, outlink_displacement);
+ }
+ if (in[3].link) {
+ GPU_link(mat, "node_output_material_thickness", in[3].link, &outlink_thickness);
+ GPU_material_output_thickness(mat, outlink_thickness);
}
-
- GPU_stack_link(mat,
- node,
- "node_output_material",
- in,
- out,
- alpha_threshold_link,
- shadow_threshold_link,
- &outlink);
- GPU_material_output_link(mat, outlink);
-
return true;
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_output_world.cc b/source/blender/nodes/shader/nodes/node_shader_output_world.cc
index 501dc088cbe..a3509fd3ff4 100644
--- a/source/blender/nodes/shader/nodes/node_shader_output_world.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_output_world.cc
@@ -28,16 +28,20 @@ static void node_declare(NodeDeclarationBuilder &b)
}
static int node_shader_gpu_output_world(GPUMaterial *mat,
- bNode *node,
+ bNode *UNUSED(node),
bNodeExecData *UNUSED(execdata),
GPUNodeStack *in,
- GPUNodeStack *out)
+ GPUNodeStack *UNUSED(out))
{
- GPUNodeLink *outlink;
-
- GPU_stack_link(mat, node, "node_output_world", in, out, &outlink);
- GPU_material_output_link(mat, outlink);
-
+ GPUNodeLink *outlink_surface, *outlink_volume;
+ if (in[0].link) {
+ GPU_link(mat, "node_output_world_surface", in[0].link, &outlink_surface);
+ GPU_material_output_surface(mat, outlink_surface);
+ }
+ if (in[1].link) {
+ GPU_link(mat, "node_output_world_volume", in[1].link, &outlink_volume);
+ GPU_material_output_volume(mat, outlink_volume);
+ }
return true;
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_particle_info.cc b/source/blender/nodes/shader/nodes/node_shader_particle_info.cc
index d68d0fe0d72..5792282fa0f 100644
--- a/source/blender/nodes/shader/nodes/node_shader_particle_info.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_particle_info.cc
@@ -44,16 +44,9 @@ static int gpu_shader_particle_info(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
-
- return GPU_stack_link(mat,
- node,
- "particle_info",
- in,
- out,
- GPU_builtin(GPU_PARTICLE_SCALAR_PROPS),
- GPU_builtin(GPU_PARTICLE_LOCATION),
- GPU_builtin(GPU_PARTICLE_VELOCITY),
- GPU_builtin(GPU_PARTICLE_ANG_VELOCITY));
+ GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO);
+ /* TODO(fclem) Pass particle data in obinfo. */
+ return GPU_stack_link(mat, node, "particle_info", in, out);
}
} // namespace blender::nodes::node_shader_particle_info_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc
index 153b1abfbca..d601f45b49f 100644
--- a/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc
@@ -34,9 +34,7 @@ static int node_shader_gpu_shadertorgb(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- /* Because node_shader_to_rgba is using fallback_cubemap()
- * we need to tag material as glossy. */
- GPU_material_flag_set(mat, GPU_MATFLAG_GLOSSY);
+ GPU_material_flag_set(mat, GPU_MATFLAG_SHADER_TO_RGBA);
return GPU_stack_link(mat, node, "node_shader_to_rgba", in, out);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc
index f60db81b4a9..a3e03d00a1f 100644
--- a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc
@@ -41,6 +41,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Vector>(N_("Normal")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSSRDF"));
}
@@ -65,19 +66,9 @@ static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat,
GPU_link(mat, "world_normals_get", &in[5].link);
}
- if (node->sss_id > 0) {
- bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2);
- bNodeSocketValueRGBA *socket_data = (bNodeSocketValueRGBA *)socket->default_value;
- /* For some reason it seems that the socket value is in ARGB format. */
- GPU_material_sss_profile_create(mat, &socket_data->value[1]);
-
- /* sss_id is 0 only the node is not connected to any output.
- * In this case flagging the material would trigger a bug (see T68736). */
- GPU_material_flag_set(mat, (eGPUMatFlag)(GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_SSS));
- }
-
- return GPU_stack_link(
- mat, node, "node_subsurface_scattering", in, out, GPU_constant(&node->sss_id));
+ GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_SUBSURFACE);
+ GPU_stack_link(mat, node, "node_subsurface_scattering", in, out);
+ return GPU_stack_eval_link(mat, node, "node_subsurface_scattering_eval", in, out);
}
static void node_shader_update_subsurface_scattering(bNodeTree *ntree, bNode *node)
diff --git a/source/blender/nodes/shader/nodes/node_shader_tangent.cc b/source/blender/nodes/shader/nodes/node_shader_tangent.cc
index c4e5660b9f8..58625cacf9c 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tangent.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tangent.cc
@@ -89,14 +89,7 @@ static int node_shader_gpu_tangent(GPUMaterial *mat,
GPU_link(mat, "tangent_orco_z", orco, &orco);
}
- return GPU_stack_link(mat,
- node,
- "node_tangent",
- in,
- out,
- GPU_builtin(GPU_WORLD_NORMAL),
- orco,
- GPU_builtin(GPU_OBJECT_MATRIX));
+ return GPU_stack_link(mat, node, "node_tangent", in, out, orco);
}
} // namespace blender::nodes::node_shader_tangent_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc
index 1bbaed88ea5..d81316b0f8b 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc
@@ -51,24 +51,22 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat,
{
Object *ob = (Object *)node->id;
- GPUNodeLink *inv_obmat = (ob != nullptr) ? GPU_uniform(&ob->imat[0][0]) :
- GPU_builtin(GPU_INVERSE_OBJECT_MATRIX);
+ /* Use special matrix to let the shader branch to using the render object's matrix. */
+ float dummy_matrix[4][4];
+ dummy_matrix[3][3] = 0.0f;
+ GPUNodeLink *inv_obmat = (ob != NULL) ? GPU_uniform(&ob->imat[0][0]) :
+ GPU_uniform(&dummy_matrix[0][0]);
/* Opti: don't request orco if not needed. */
- const float default_coords[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- GPUNodeLink *orco = (!out[0].hasoutput) ? GPU_constant(default_coords) :
- GPU_attribute(mat, CD_ORCO, "");
+ float4 zero(0.0f);
+ GPUNodeLink *orco = (!out[0].hasoutput) ? GPU_constant(zero) : GPU_attribute(mat, CD_ORCO, "");
GPUNodeLink *mtface = GPU_attribute(mat, CD_MTFACE, "");
- GPUNodeLink *viewpos = GPU_builtin(GPU_VIEW_POSITION);
- GPUNodeLink *worldnor = GPU_builtin(GPU_WORLD_NORMAL);
- GPUNodeLink *texcofacs = GPU_builtin(GPU_CAMERA_TEXCO_FACTORS);
if (out[0].hasoutput) {
GPU_link(mat, "generated_from_orco", orco, &orco);
}
- GPU_stack_link(
- mat, node, "node_tex_coord", in, out, viewpos, worldnor, inv_obmat, texcofacs, orco, mtface);
+ GPU_stack_link(mat, node, "node_tex_coord", in, out, inv_obmat, orco, mtface);
int i;
LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) {
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc
index 7b947392084..24ecdee12d8 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc
@@ -60,15 +60,13 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat,
GPUNodeLink *outalpha;
- if (!ima) {
+ /* HACK(fclem): For lookdev mode: do not compile an empty environment and just create an empty
+ * texture entry point. We manually bind to it after DRW_shgroup_add_material_resources(). */
+ if (!ima && !GPU_material_flag_get(mat, GPU_MATFLAG_LOOKDEV_HACK)) {
return GPU_stack_link(mat, node, "node_tex_environment_empty", in, out);
}
- if (!in[0].link) {
- GPU_link(mat, "node_tex_environment_texco", GPU_builtin(GPU_VIEW_POSITION), &in[0].link);
- node_shader_gpu_bump_tex_coord(mat, node, &in[0].link);
- }
-
+ node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
node_shader_gpu_tex_mapping(mat, node, in, out);
/* Compute texture coordinate. */
@@ -108,7 +106,7 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat,
/* Sample texture with correct interpolation. */
GPU_link(mat, gpu_fn, in[0].link, GPU_image(mat, ima, iuser, sampler), &out[0].link, &outalpha);
- if (out[0].hasoutput) {
+ if (out[0].hasoutput && ima) {
if (ELEM(ima->alpha_mode, IMA_ALPHA_IGNORE, IMA_ALPHA_CHANNEL_PACKED) ||
IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name)) {
/* Don't let alpha affect color output in these cases. */
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc
index d5479f46a35..7c7154b8f7a 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc
@@ -104,13 +104,11 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat,
}
case SHD_PROJ_BOX: {
gpu_node_name = use_cubic ? "tex_box_sample_cubic" : "tex_box_sample_linear";
- GPUNodeLink *wnor, *col1, *col2, *col3;
- GPUNodeLink *vnor = GPU_builtin(GPU_WORLD_NORMAL);
- GPUNodeLink *ob_mat = GPU_builtin(GPU_OBJECT_MATRIX);
+ GPUNodeLink *vnor, *wnor, *col1, *col2, *col3;
GPUNodeLink *blend = GPU_uniform(&tex->projection_blend);
GPUNodeLink *gpu_image = GPU_image(mat, ima, iuser, sampler_state);
- /* equivalent to normal_world_to_object */
- GPU_link(mat, "normal_transform_transposed_m4v3", vnor, ob_mat, &wnor);
+ GPU_link(mat, "world_normals_get", &vnor);
+ GPU_link(mat, "normal_transform_world_to_object", vnor, &wnor);
GPU_link(mat, gpu_node_name, in[0].link, wnor, gpu_image, &col1, &col2, &col3);
GPU_link(mat, "tex_box_blend", wnor, col1, col2, col3, blend, &out[0].link, &out[1].link);
break;
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc b/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc
index 3486eda1635..d34ef29c30a 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc
@@ -40,23 +40,20 @@ static int gpu_shader_vector_displacement(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- if (node->custom1 == SHD_SPACE_TANGENT) {
- return GPU_stack_link(mat,
- node,
- "node_vector_displacement_tangent",
- in,
- out,
- GPU_attribute(mat, CD_TANGENT, ""),
- GPU_builtin(GPU_WORLD_NORMAL),
- GPU_builtin(GPU_OBJECT_MATRIX),
- GPU_builtin(GPU_VIEW_MATRIX));
+ switch (node->custom1) {
+ case SHD_SPACE_TANGENT:
+ return GPU_stack_link(mat,
+ node,
+ "node_vector_displacement_tangent",
+ in,
+ out,
+ GPU_attribute(mat, CD_TANGENT, ""));
+ case SHD_SPACE_OBJECT:
+ return GPU_stack_link(mat, node, "node_vector_displacement_object", in, out);
+ case SHD_SPACE_WORLD:
+ default:
+ return GPU_stack_link(mat, node, "node_vector_displacement_world", in, out);
}
- if (node->custom1 == SHD_SPACE_OBJECT) {
- return GPU_stack_link(
- mat, node, "node_vector_displacement_object", in, out, GPU_builtin(GPU_OBJECT_MATRIX));
- }
-
- return GPU_stack_link(mat, node, "node_vector_displacement_world", in, out);
}
} // namespace blender::nodes::node_shader_vector_displacement_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc
index a8a6902e30c..e38f67fb751 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc
@@ -59,42 +59,43 @@ static void node_shader_init_vect_transform(bNodeTree *UNUSED(ntree), bNode *nod
node->storage = vect;
}
-static GPUNodeLink *get_gpulink_matrix_from_to(short from, short to)
+static const char *get_gpufn_name_from_to(short from, short to)
{
switch (from) {
case SHD_VECT_TRANSFORM_SPACE_OBJECT:
switch (to) {
case SHD_VECT_TRANSFORM_SPACE_OBJECT:
- return nullptr;
+ return NULL;
case SHD_VECT_TRANSFORM_SPACE_WORLD:
- return GPU_builtin(GPU_OBJECT_MATRIX);
+ return "object_to_world";
case SHD_VECT_TRANSFORM_SPACE_CAMERA:
- return GPU_builtin(GPU_LOC_TO_VIEW_MATRIX);
+ return "object_to_view";
}
break;
case SHD_VECT_TRANSFORM_SPACE_WORLD:
switch (to) {
case SHD_VECT_TRANSFORM_SPACE_WORLD:
- return nullptr;
+ return NULL;
case SHD_VECT_TRANSFORM_SPACE_CAMERA:
- return GPU_builtin(GPU_VIEW_MATRIX);
+ return "world_to_view";
case SHD_VECT_TRANSFORM_SPACE_OBJECT:
- return GPU_builtin(GPU_INVERSE_OBJECT_MATRIX);
+ return "world_to_object";
}
break;
case SHD_VECT_TRANSFORM_SPACE_CAMERA:
switch (to) {
case SHD_VECT_TRANSFORM_SPACE_CAMERA:
- return nullptr;
+ return NULL;
case SHD_VECT_TRANSFORM_SPACE_WORLD:
- return GPU_builtin(GPU_INVERSE_VIEW_MATRIX);
+ return "view_to_world";
case SHD_VECT_TRANSFORM_SPACE_OBJECT:
- return GPU_builtin(GPU_INVERSE_LOC_TO_VIEW_MATRIX);
+ return "view_to_object";
}
break;
}
- return nullptr;
+ return NULL;
}
+
static int gpu_shader_vect_transform(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
@@ -102,11 +103,6 @@ static int gpu_shader_vect_transform(GPUMaterial *mat,
GPUNodeStack *out)
{
struct GPUNodeLink *inputlink;
- struct GPUNodeLink *fromto;
-
- const char *vtransform = "direction_transform_m4v3";
- const char *ptransform = "point_transform_m4v3";
- const char *func_name = nullptr;
NodeShaderVectTransform *nodeprop = (NodeShaderVectTransform *)node->storage;
@@ -117,9 +113,10 @@ static int gpu_shader_vect_transform(GPUMaterial *mat,
inputlink = GPU_constant(in[0].vec);
}
- fromto = get_gpulink_matrix_from_to(nodeprop->convert_from, nodeprop->convert_to);
+ const char *xform = (nodeprop->type == SHD_VECT_TRANSFORM_TYPE_POINT) ? "point_transform_" :
+ "direction_transform_";
+ const char *fromto = get_gpufn_name_from_to(nodeprop->convert_from, nodeprop->convert_to);
- func_name = (nodeprop->type == SHD_VECT_TRANSFORM_TYPE_POINT) ? ptransform : vtransform;
if (fromto) {
/* For cycles we have inverted Z */
/* TODO: pass here the correct matrices */
@@ -127,7 +124,11 @@ static int gpu_shader_vect_transform(GPUMaterial *mat,
nodeprop->convert_to != SHD_VECT_TRANSFORM_SPACE_CAMERA) {
GPU_link(mat, "invert_z", inputlink, &inputlink);
}
- GPU_link(mat, func_name, inputlink, fromto, &out[0].link);
+
+ char func_name[48];
+ SNPRINTF(func_name, "%s%s", xform, fromto);
+ GPU_link(mat, func_name, inputlink, &out[0].link);
+
if (nodeprop->convert_to == SHD_VECT_TRANSFORM_SPACE_CAMERA &&
nodeprop->convert_from != SHD_VECT_TRANSFORM_SPACE_CAMERA) {
GPU_link(mat, "invert_z", out[0].link, &out[0].link);
diff --git a/source/blender/nodes/shader/nodes/node_shader_wireframe.cc b/source/blender/nodes/shader/nodes/node_shader_wireframe.cc
index 1ba2391a09d..c4771cc13e9 100644
--- a/source/blender/nodes/shader/nodes/node_shader_wireframe.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_wireframe.cc
@@ -44,17 +44,11 @@ static int node_shader_gpu_wireframe(GPUMaterial *mat,
GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC);
/* node->custom1 is use_pixel_size */
if (node->custom1) {
- return GPU_stack_link(
- mat, node, "node_wireframe_screenspace", in, out, GPU_builtin(GPU_BARYCENTRIC_TEXCO));
+ return GPU_stack_link(mat, node, "node_wireframe_screenspace", in, out);
+ }
+ else {
+ return GPU_stack_link(mat, node, "node_wireframe", in, out);
}
-
- return GPU_stack_link(mat,
- node,
- "node_wireframe",
- in,
- out,
- GPU_builtin(GPU_BARYCENTRIC_TEXCO),
- GPU_builtin(GPU_BARYCENTRIC_DIST));
}
} // namespace blender::nodes::node_shader_wireframe_cc
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 6caac79c4d5..b7eafc6dfad 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -132,6 +132,7 @@
#include "BKE_sound.h"
#include "BKE_subdiv.h"
+#include "BKE_world.h"
#include "COM_compositor.h"
@@ -513,6 +514,7 @@ void WM_exit_ex(bContext *C, const bool do_python)
BKE_addon_pref_type_free();
BKE_keyconfig_pref_type_free();
BKE_materials_exit();
+ BKE_worlds_exit();
wm_operatortype_free();
wm_surfaces_free();
diff --git a/source/creator/creator.c b/source/creator/creator.c
index d69e4d075fd..900071ec1b0 100644
--- a/source/creator/creator.c
+++ b/source/creator/creator.c
@@ -67,6 +67,7 @@
#include "BKE_sound.h"
#include "BKE_vfont.h"
#include "BKE_volume.h"
+#include "BKE_world.h"
#include "DEG_depsgraph.h"
@@ -494,6 +495,7 @@ int main(int argc,
BKE_sound_init_once();
BKE_materials_init();
+ BKE_worlds_init();
#ifndef WITH_PYTHON_MODULE
if (G.background == 0) {
diff --git a/tests/python/eevee_render_tests.py b/tests/python/eevee_render_tests.py
index 6a21afc7c9a..f52b37a28cb 100644
--- a/tests/python/eevee_render_tests.py
+++ b/tests/python/eevee_render_tests.py
@@ -30,12 +30,6 @@ def setup():
eevee.use_volumetric_shadows = True
eevee.volumetric_tile_size = '2'
- for mat in bpy.data.materials:
- # This needs to be enabled case by case,
- # otherwise we loose SSR and GTAO everywhere.
- # mat.use_screen_refraction = True
- mat.use_sss_translucency = True
-
cubemap = None
grid = None
# Does not work in edit mode