EEVEE: Remove EEVEE-Legacy

This handles the transition to EEVEE-Next (now EEVEE).

This removes some things that make no sense to keep
even for compatibility.
- Scene.eevee.light_cache_data
- Scene Light cache operators
- Scene Light cache RNA properties

The remaining legacy properties will be removed later
on to avoid python API breakage.

We keep the identifier of EEVEE-Next as `BLENDER_EEVEE_NEXT`
to avoid addons being incorrectly silently made compatible
with the EEVEE-Next where the Python API is different.
This renaming should be done in 5.0 release.

Thank you EEVEE-Legacy, you served us well.

Pull Request: https://projects.blender.org/blender/blender/pulls/122433
This commit is contained in:
Clément Foucault 2024-06-04 14:17:58 +02:00 committed by Clément Foucault
parent 76f4a4da6a
commit cc0d12dd20
153 changed files with 10 additions and 31027 deletions

@ -918,17 +918,6 @@ class RENDER_PT_eevee_indirect_lighting(RenderButtonsPanel, Panel):
scene = context.scene
props = scene.eevee
col = layout.column()
col.operator("scene.light_cache_bake", text="Bake Indirect Lighting", icon='RENDER_STILL')
col.operator("scene.light_cache_bake", text="Bake Cubemap Only", icon='LIGHTPROBE_SPHERE').subset = 'CUBEMAPS'
col.operator("scene.light_cache_free", text="Delete Lighting Cache")
cache_info = scene.eevee.gi_cache_info
if cache_info:
col.label(text=rpt_(cache_info), translate=False)
col.prop(props, "gi_auto_bake")
col.prop(props, "gi_diffuse_bounces")
col.prop(props, "gi_cubemap_resolution")
col.prop(props, "gi_visibility_resolution", text="Diffuse Occlusion")

@ -100,8 +100,6 @@
#include "BLO_read_write.hh"
#include "engines/eevee/eevee_lightcache.h"
#include "IMB_colormanagement.hh"
#include "IMB_imbuf.hh"
@ -443,11 +441,6 @@ static void scene_free_data(ID *id)
scene->master_collection = nullptr;
}
if (scene->eevee.light_cache_data) {
EEVEE_lightcache_free(scene->eevee.light_cache_data);
scene->eevee.light_cache_data = nullptr;
}
if (scene->display.shading.prop) {
IDP_FreeProperty(scene->display.shading.prop);
scene->display.shading.prop = nullptr;
@ -945,22 +938,6 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data)
}
}
static void scene_foreach_cache(ID *id,
IDTypeForeachCacheFunctionCallback function_callback,
void *user_data)
{
Scene *scene = (Scene *)id;
IDCacheKey key{};
key.id_session_uid = id->session_uid;
key.identifier = offsetof(Scene, eevee.light_cache_data);
function_callback(id,
&key,
(void **)&scene->eevee.light_cache_data,
IDTYPE_CACHE_CB_FLAGS_PERSISTENT,
user_data);
}
static bool seq_foreach_path_callback(Sequence *seq, void *user_data)
{
if (SEQ_HAS_PATH(seq)) {
@ -1176,12 +1153,6 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres
reinterpret_cast<Collection *>(BLO_write_get_id_buffer_temp_id(temp_embedded_id_buffer)));
}
/* Eevee Light-cache */
if (sce->eevee.light_cache_data && !BLO_write_is_undo(writer)) {
BLO_write_struct(writer, LightCache, sce->eevee.light_cache_data);
EEVEE_lightcache_blend_write(writer, sce->eevee.light_cache_data);
}
BKE_screen_view3d_shading_blend_write(writer, &sce->display.shading);
/* Freed on `do_versions()`. */
@ -1496,19 +1467,6 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
BKE_view_layer_blend_read_data(reader, view_layer);
}
if (BLO_read_data_is_undo(reader)) {
/* If it's undo do nothing here, caches are handled by higher-level generic calling code. */
}
else {
/* else try to read the cache from file. */
BLO_read_struct(reader, LightCache, &sce->eevee.light_cache_data);
if (sce->eevee.light_cache_data) {
EEVEE_lightcache_blend_read_data(reader, sce->eevee.light_cache_data);
}
}
EEVEE_lightcache_info_update(&sce->eevee);
BKE_screen_view3d_shading_blend_read_data(reader, &sce->display.shading);
BLO_read_struct(reader, IDProperty, &sce->layer_properties);
@ -1604,7 +1562,7 @@ constexpr IDTypeInfo get_type_info()
* support all possible corner cases. */
info.make_local = nullptr;
info.foreach_id = scene_foreach_id;
info.foreach_cache = scene_foreach_cache;
info.foreach_cache = nullptr;
info.foreach_path = scene_foreach_path;
info.owner_pointer_get = nullptr;
@ -1786,9 +1744,6 @@ void BKE_scene_copy_data_eevee(Scene *sce_dst, const Scene *sce_src)
{
/* Copy eevee data between scenes. */
sce_dst->eevee = sce_src->eevee;
sce_dst->eevee.light_cache_data = nullptr;
sce_dst->eevee.light_cache_info[0] = '\0';
/* TODO: Copy the cache. */
}
Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type)

@ -4041,6 +4041,14 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 52)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (STREQ(scene->r.engine, RE_engine_id_BLENDER_EEVEE)) {
STRNCPY(scene->r.engine, RE_engine_id_BLENDER_EEVEE_NEXT);
}
}
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

@ -725,7 +725,6 @@ void update_id_after_copy(const Depsgraph *depsgraph,
Scene *scene_cow = (Scene *)id_cow;
const Scene *scene_orig = (const Scene *)id_orig;
scene_cow->toolsettings = scene_orig->toolsettings;
scene_cow->eevee.light_cache_data = scene_orig->eevee.light_cache_data;
scene_setup_view_layers_after_remap(depsgraph, id_node, reinterpret_cast<Scene *>(id_cow));
break;
}
@ -954,7 +953,6 @@ void discard_scene_pointers(ID *id_cow)
{
Scene *scene_cow = (Scene *)id_cow;
scene_cow->toolsettings = nullptr;
scene_cow->eevee.light_cache_data = nullptr;
}
/* nullptr-ify all edit mode pointers which points to data from

@ -108,33 +108,6 @@ set(SRC
engines/compositor/compositor_engine.cc
engines/image/image_engine.cc
engines/image/image_shader.cc
engines/eevee/eevee_bloom.cc
engines/eevee/eevee_cryptomatte.cc
engines/eevee/eevee_data.cc
engines/eevee/eevee_depth_of_field.cc
engines/eevee/eevee_effects.cc
engines/eevee/eevee_engine.cc
engines/eevee/eevee_lightcache.cc
engines/eevee/eevee_lightprobes.cc
engines/eevee/eevee_lights.cc
engines/eevee/eevee_lookdev.cc
engines/eevee/eevee_lut_gen.cc
engines/eevee/eevee_materials.cc
engines/eevee/eevee_mist.cc
engines/eevee/eevee_motion_blur.cc
engines/eevee/eevee_occlusion.cc
engines/eevee/eevee_render.cc
engines/eevee/eevee_renderpasses.cc
engines/eevee/eevee_sampling.cc
engines/eevee/eevee_screen_raytrace.cc
engines/eevee/eevee_shaders.cc
engines/eevee/eevee_shaders_extra.cc
engines/eevee/eevee_shadows.cc
engines/eevee/eevee_shadows_cascade.cc
engines/eevee/eevee_shadows_cube.cc
engines/eevee/eevee_subsurface.cc
engines/eevee/eevee_temporal_sampling.cc
engines/eevee/eevee_volumes.cc
engines/eevee_next/eevee_ambient_occlusion.cc
engines/eevee_next/eevee_camera.cc
engines/eevee_next/eevee_cryptomatte.cc
@ -267,10 +240,6 @@ set(SRC
engines/basic/basic_engine.h
engines/basic/basic_private.h
engines/compositor/compositor_engine.h
engines/eevee/eevee_engine.h
engines/eevee/eevee_lightcache.h
engines/eevee/eevee_private.hh
engines/eevee/engine_eevee_shared_defines.h
engines/eevee_next/eevee_ambient_occlusion.hh
engines/eevee_next/eevee_camera.hh
engines/eevee_next/eevee_cryptomatte.hh
@ -353,104 +322,6 @@ 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_vert_no_geom.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_surface_lib.glsl
engines/eevee/shaders/closure_eval_refraction_lib.glsl
engines/eevee/shaders/closure_eval_volume_lib.glsl
engines/eevee/shaders/closure_eval_translucent_lib.glsl
engines/eevee/shaders/closure_type_lib.glsl
engines/eevee/shaders/eevee_empty.glsl
engines/eevee/shaders/eevee_empty_volume.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/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/bsdf_sampling_lib.glsl
engines/eevee/shaders/random_lib.glsl
engines/eevee/shaders/raytrace_lib.glsl
engines/eevee/shaders/renderpass_accumulate_frag.glsl
engines/eevee/shaders/renderpass_lib.glsl
engines/eevee/shaders/renderpass_postprocess_frag.glsl
engines/eevee/shaders/cryptomatte_frag.glsl
engines/eevee/shaders/cryptomatte_vert.glsl
engines/eevee/shaders/ltc_lib.glsl
engines/eevee/shaders/ssr_lib.glsl
engines/eevee/shaders/surface_frag.glsl
engines/eevee/shaders/surface_geom.glsl
engines/eevee/shaders/surface_lib.glsl
engines/eevee/shaders/surface_vert.glsl
engines/eevee/shaders/update_noise_frag.glsl
engines/eevee/shaders/volumetric_accum_frag.glsl
engines/eevee/shaders/volumetric_lib.glsl
engines/eevee/shaders/volumetric_frag.glsl
engines/eevee/shaders/volumetric_geom.glsl
engines/eevee/shaders/volumetric_vert.glsl
engines/eevee/shaders/volumetric_resolve_frag.glsl
engines/eevee/shaders/volumetric_scatter_frag.glsl
engines/eevee/shaders/volumetric_integration_frag.glsl
engines/eevee/shaders/world_vert.glsl
engines/eevee/shaders/infos/engine_eevee_legacy_shared.h
engines/eevee/engine_eevee_shared_defines.h
engines/eevee_next/shaders/eevee_ambient_occlusion_lib.glsl
engines/eevee_next/shaders/eevee_ambient_occlusion_pass_comp.glsl
engines/eevee_next/shaders/eevee_attributes_lib.glsl

@ -1,342 +0,0 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*
* Eevee's bloom shader.
*/
#include "DRW_render.hh"
#include "GPU_texture.hh"
#include "DEG_depsgraph_query.hh"
#include "eevee_private.hh"
static const bool use_highres = true;
int EEVEE_bloom_init(EEVEE_ViewLayerData * /*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]);
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT;
effects->bloom_blit = DRW_texture_pool_query_2d_ex(
blitsize[0], blitsize[1], GPU_R11F_G11F_B10F, usage, &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(std::min(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] = std::max(texsize[0], 2);
texsize[1] = std::max(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]);
eGPUTextureUsage downsample_usage = GPU_TEXTURE_USAGE_SHADER_READ |
GPU_TEXTURE_USAGE_ATTACHMENT;
effects->bloom_downsample[i] = DRW_texture_pool_query_2d_ex(
texsize[0], texsize[1], GPU_R11F_G11F_B10F, downsample_usage, &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] = std::max(texsize[0], 2);
texsize[1] = std::max(texsize[1], 2);
eGPUTextureUsage upsample_usage = GPU_TEXTURE_USAGE_SHADER_READ |
GPU_TEXTURE_USAGE_ATTACHMENT;
effects->bloom_upsample[i] = DRW_texture_pool_query_2d_ex(
texsize[0], texsize[1], GPU_R11F_G11F_B10F, upsample_usage, &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,
GPUShader *sh,
DRWPass **pass,
bool upsample,
bool resolve,
bool resolve_add_base)
{
blender::gpu::Batch *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, nullptr);
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", resolve_add_base);
}
return grp;
}
void EEVEE_bloom_cache_init(EEVEE_ViewLayerData * /*sldata*/, EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
psl->bloom_accum_ps = nullptr;
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,
false);
eevee_create_bloom_pass("Bloom Downsample",
effects,
EEVEE_shaders_bloom_downsample_get(false),
&psl->bloom_downsample,
false,
false,
false);
eevee_create_bloom_pass("Bloom Upsample",
effects,
EEVEE_shaders_bloom_upsample_get(use_highres),
&psl->bloom_upsample,
true,
false,
false);
grp = eevee_create_bloom_pass("Bloom Blit",
effects,
EEVEE_shaders_bloom_blit_get(use_antiflicker),
&psl->bloom_blit,
false,
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,
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) {
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 * /*sldata*/,
EEVEE_Data *vedata,
uint /*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, DRWTextureFlag(0));
GPU_framebuffer_ensure_config(&fbl->bloom_pass_accum_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->bloom_accum)});
/* Create Pass and shgroup. */
eevee_create_bloom_pass("Bloom Accumulate",
effects,
EEVEE_shaders_bloom_resolve_get(use_highres),
&psl->bloom_accum_ps,
true,
true,
false);
}
void EEVEE_bloom_output_accumulate(EEVEE_ViewLayerData * /*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);
}
}

@ -1,716 +0,0 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \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.hh"
#include "DRW_render.hh"
#include "BKE_cryptomatte.h"
#include "GPU_batch.hh"
#include "RE_pipeline.h"
#include "BLI_alloca.h"
#include "BLI_math_bits.h"
#include "BLI_rect.h"
#include "DNA_curves_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_particle_types.h"
#include "IMB_imbuf_types.hh"
#include "eevee_private.hh"
/* -------------------------------------------------------------------- */
/** \name Data Management cryptomatte accum buffer
* \{ */
BLI_INLINE eViewLayerCryptomatteFlags eevee_cryptomatte_active_layers(const ViewLayer *view_layer)
{
const eViewLayerCryptomatteFlags cryptomatte_layers = eViewLayerCryptomatteFlags(
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
* render-pass 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 Render-Passes
* \{ */
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) {
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 = eViewLayerEEVEEPassType(
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 * /*sldata*/,
EEVEE_Data *vedata,
int /*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 == nullptr) {
g_data->cryptomatte_accum_buffer = static_cast<EEVEE_CryptomatteSample *>(
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 = static_cast<float *>(
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, DRWTextureFlag(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 * /*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 *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);
DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
return grp;
}
static void eevee_cryptomatte_curves_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, nullptr);
}
void EEVEE_cryptomatte_object_curves_cache_populate(EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
Object *ob)
{
using namespace blender::draw;
BLI_assert(ob->type == OB_CURVES);
Material *material = BKE_object_material_get_eval(ob, CURVES_MATERIAL_NR);
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
vedata, sldata, ob, material, true);
DRW_shgroup_curves_create_sub(ob, grp, nullptr);
}
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_curves_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);
GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len);
memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len);
blender::gpu::Batch **geoms = DRW_cache_object_surface_material_get(
ob, gpumat_array, materials_len);
if (geoms) {
for (int i = 0; i < materials_len; i++) {
blender::gpu::Batch *geom = geoms[i];
if (geom == nullptr) {
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 {
blender::gpu::Batch *geom = DRW_cache_object_surface_get(ob);
if (geom) {
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
vedata, sldata, ob, nullptr, 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 accumulation 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
* sub-samples 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 * /*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)
{
/* NOTE: Name channels lowercase RGBA so that compression rules check in OpenEXR DWA code uses
* lossless compression. Reportedly this naming is the only one which works good from the
* interoperability point of view. Using XYZW naming is not portable. */
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++) {
SNPRINTF_RLEN(cryptomatte_pass_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++) {
SNPRINTF_RLEN(cryptomatte_pass_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++) {
SNPRINTF_RLEN(cryptomatte_pass_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 = static_cast<const EEVEE_CryptomatteSample *>(a_);
const EEVEE_CryptomatteSample *b = static_cast<const EEVEE_CryptomatteSample *>(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 = nullptr;
if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
volumetric_transmittance_buffer = static_cast<float *>(
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 != nullptr) {
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;
SNPRINTF_RLEN(cryptomatte_pass_name, render_pass_name_format, pass);
RenderPass *rp_object = RE_pass_find_by_name(rl, cryptomatte_pass_name, viewname);
float *rp_buffer_data = rp_object->ibuf->float_buffer.data;
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_buffer_data[render_pass_offset] = accum_buffer[accum_buffer_offset].hash;
rp_buffer_data[render_pass_offset + 1] = accum_buffer[accum_buffer_offset].weight;
if (levels_done + 1 < num_cryptomatte_levels) {
rp_buffer_data[render_pass_offset + 2] = accum_buffer[accum_buffer_offset + 1].hash;
rp_buffer_data[render_pass_offset + 3] = accum_buffer[accum_buffer_offset + 1].weight;
}
else {
rp_buffer_data[render_pass_offset + 2] = 0.0f;
rp_buffer_data[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 * /*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 = eViewLayerCryptomatteFlags(
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 = nullptr;
}
}

@ -1,378 +0,0 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*
* All specific data handler for Objects, Lights, ViewLayers, ...
*/
#include "DRW_render.hh"
#include "BLI_ghash.h"
#include "BLI_memblock.h"
#include "BKE_duplilist.hh"
#include "BKE_modifier.hh"
#include "BKE_object.hh"
#include "DEG_depsgraph_query.hh"
#include "GPU_vertex_buffer.hh"
#include "eevee_lightcache.h"
#include "eevee_private.hh"
/* Motion Blur data. */
static void eevee_motion_blur_mesh_data_free(void *val)
{
EEVEE_ObjectMotionData *mb_data = (EEVEE_ObjectMotionData *)val;
if (mb_data->hair_data != nullptr) {
MEM_freeN(mb_data->hair_data);
}
if (mb_data->geometry_data != nullptr) {
MEM_freeN(mb_data->geometry_data);
}
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_hair_step_free(EEVEE_HairMotionStepData *step_data)
{
GPU_vertbuf_discard(step_data->hair_pos);
DRW_texture_free(step_data->hair_pos_tx);
MEM_freeN(step_data);
}
void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb)
{
if (mb->object == nullptr) {
mb->object = BLI_ghash_new(eevee_object_key_hash, eevee_object_key_cmp, "EEVEE Object Motion");
}
for (int i = 0; i < 2; i++) {
if (mb->position_vbo_cache[i] == nullptr) {
mb->position_vbo_cache[i] = BLI_ghash_new(
BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE duplicate vbo cache");
}
if (mb->hair_motion_step_cache[i] == nullptr) {
mb->hair_motion_step_cache[i] = BLI_ghash_new(
BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE hair motion step cache");
}
}
}
void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb)
{
if (mb->object) {
BLI_ghash_free(mb->object, MEM_freeN, eevee_motion_blur_mesh_data_free);
mb->object = nullptr;
}
for (int i = 0; i < 2; i++) {
if (mb->position_vbo_cache[i]) {
BLI_ghash_free(mb->position_vbo_cache[i], nullptr, (GHashValFreeFP)GPU_vertbuf_discard);
mb->position_vbo_cache[i] = nullptr;
}
if (mb->hair_motion_step_cache[i]) {
BLI_ghash_free(
mb->hair_motion_step_cache[i], nullptr, (GHashValFreeFP)EEVEE_motion_hair_step_free);
mb->hair_motion_step_cache[i] = nullptr;
}
}
}
EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb,
Object *ob,
bool is_psys)
{
if (mb->object == nullptr) {
return nullptr;
}
EEVEE_ObjectKey key, *key_p;
/* Assumes that all instances have the same object pointer. This is currently the case because
* instance objects are temporary objects on the stack. */
/* WORKAROUND: Duplicate object key for particle system (hairs) to be able to store dupli offset
* matrix along with the emitter obmat. (see #97380) */
key.ob = (void *)((char *)ob + is_psys);
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 = ob;
memset(key.id, 0, sizeof(key.id));
}
EEVEE_ObjectMotionData *ob_step = static_cast<EEVEE_ObjectMotionData *>(
BLI_ghash_lookup(mb->object, &key));
if (ob_step == nullptr) {
key_p = static_cast<EEVEE_ObjectKey *>(MEM_mallocN(sizeof(*key_p), __func__));
memcpy(key_p, &key, sizeof(*key_p));
ob_step = static_cast<EEVEE_ObjectMotionData *>(
MEM_callocN(sizeof(EEVEE_ObjectMotionData), __func__));
BLI_ghash_insert(mb->object, key_p, ob_step);
}
return ob_step;
}
EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_ObjectMotionData *mb_data)
{
if (mb_data->geometry_data == nullptr) {
EEVEE_GeometryMotionData *geom_step = static_cast<EEVEE_GeometryMotionData *>(
MEM_callocN(sizeof(EEVEE_GeometryMotionData), __func__));
geom_step->type = EEVEE_MOTION_DATA_MESH;
mb_data->geometry_data = geom_step;
}
return mb_data->geometry_data;
}
EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb_data, Object *ob)
{
if (mb_data->hair_data == nullptr) {
/* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */
int psys_len = BLI_listbase_count(&ob->modifiers);
EEVEE_HairMotionData *hair_step = static_cast<EEVEE_HairMotionData *>(MEM_callocN(
sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len, __func__));
hair_step->psys_len = psys_len;
hair_step->type = EEVEE_MOTION_DATA_HAIR;
mb_data->hair_data = hair_step;
}
return mb_data->hair_data;
}
EEVEE_HairMotionData *EEVEE_motion_blur_curves_data_get(EEVEE_ObjectMotionData *mb_data)
{
if (mb_data->hair_data == nullptr) {
EEVEE_HairMotionData *hair_step = static_cast<EEVEE_HairMotionData *>(
MEM_callocN(sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]), __func__));
hair_step->psys_len = 1;
hair_step->type = EEVEE_MOTION_DATA_HAIR;
mb_data->hair_data = hair_step;
}
return mb_data->hair_data;
}
/* 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 = nullptr;
}
/* 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, nullptr);
sldata->material_cache = nullptr;
}
}
EEVEE_ViewLayerData *EEVEE_view_layer_data_get()
{
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(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 == nullptr) {
*sldata = static_cast<EEVEE_ViewLayerData *>(
MEM_callocN(sizeof(**sldata), "EEVEE_ViewLayerData"));
eevee_view_layer_init(*sldata);
}
return *sldata;
}
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure()
{
EEVEE_ViewLayerData **sldata = (EEVEE_ViewLayerData **)DRW_view_layer_engine_data_ensure(
&draw_engine_eevee_type, &EEVEE_view_layer_data_free);
if (*sldata == nullptr) {
*sldata = static_cast<EEVEE_ViewLayerData *>(
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 nullptr;
}
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,
nullptr);
}
/* 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 nullptr;
}
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,
nullptr);
}
/* 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 nullptr;
}
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,
nullptr);
}
/* 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,
nullptr);
}

File diff suppressed because it is too large Load Diff

@ -1,527 +0,0 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*
* Gather all screen space effects technique such as Bloom, Motion Blur, DoF, SSAO, SSR, ...
*/
#include "DRW_render.hh"
#include "BKE_global.hh" /* for G.debug_value */
#include "GPU_capabilities.hh"
#include "GPU_platform.hh"
#include "GPU_state.hh"
#include "GPU_texture.hh"
#include "eevee_private.hh"
static struct {
/* These are just references, not actually allocated */
GPUTexture *depth_src;
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 = {nullptr}; /* 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 = static_cast<EEVEE_EffectsInfo *>(
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 = EEVEE_EffectsFlag(0);
effects->enabled_effects |= (G.debug_value == 9) ? EFFECT_VELOCITY_BUFFER : EEVEE_EffectsFlag(0);
effects->enabled_effects |= EEVEE_EffectsFlag(EEVEE_motion_blur_init(sldata, vedata));
effects->enabled_effects |= EEVEE_EffectsFlag(EEVEE_bloom_init(sldata, vedata));
effects->enabled_effects |= EEVEE_EffectsFlag(EEVEE_depth_of_field_init(sldata, vedata, camera));
effects->enabled_effects |= EEVEE_EffectsFlag(EEVEE_temporal_sampling_init(sldata, vedata));
effects->enabled_effects |= EEVEE_EffectsFlag(EEVEE_occlusion_init(sldata, vedata));
effects->enabled_effects |= EEVEE_EffectsFlag(EEVEE_screen_raytrace_init(sldata, vedata));
/* Update matrices here because EEVEE_screen_raytrace_init can have reset the
* taa_current_sample. (See #66811) */
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_ex(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL)) {
/* 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 {
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_SHADER_READ;
DRW_texture_ensure_2d_ex(&txl->maxzbuffer,
UNPACK2(effects->hiz_size),
GPU_DEPTH_COMPONENT24,
usage,
DRW_TEX_MIPMAP);
GPU_framebuffer_ensure_config(&fbl->maxzbuffer_fb,
{
GPU_ATTACHMENT_TEXTURE(txl->maxzbuffer),
GPU_ATTACHMENT_NONE,
});
}
if (fbl->downsample_fb == nullptr) {
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) {
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_SHADER_READ;
DRW_texture_ensure_2d_ex(&txl->filtered_radiance,
UNPACK2(effects->hiz_size),
GPU_R11F_G11F_B10F,
usage,
DRWTextureFlag(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) {
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT;
effects->ssr_normal_input = DRW_texture_pool_query_2d_ex(
size_fs[0], size_fs[1], GPU_RG16, usage, &draw_engine_eevee_type);
GPU_framebuffer_texture_attach(fbl->main_fb, effects->ssr_normal_input, 1, 0);
}
else {
effects->ssr_normal_input = nullptr;
}
/**
* Motion vector buffer for correct TAA / motion blur.
*/
if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) {
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT;
effects->velocity_tx = DRW_texture_pool_query_2d_ex(
size_fs[0], size_fs[1], GPU_RGBA16, usage, &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 = nullptr;
}
/**
* 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, DRWTextureFlag(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_ex(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL)) {
downsample_write = DRW_STATE_WRITE_COLOR;
}
blender::gpu::Batch *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, GPUSamplerState::default_sampler());
DRW_shgroup_uniform_float(grp, "fireflyFactor", &sldata->common_data.ssr_firefly_fac, 1);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 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);
const GPUSamplerState sampler_state = {GPU_SAMPLER_FILTERING_LINEAR};
DRW_shgroup_uniform_texture_ex(grp, "source", txl->filtered_radiance, sampler_state);
DRW_shgroup_uniform_vec2(grp, "texelSize", e_data.texel_size, 1);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 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, nullptr, 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, GPUSamplerState::default_sampler());
DRW_shgroup_uniform_vec2(grp, "texelSize", e_data.texel_size, 1);
DRW_shgroup_call(grp, quad, nullptr);
/* 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, GPUSamplerState::default_sampler());
DRW_shgroup_call(grp, quad, nullptr);
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, GPUSamplerState::default_sampler());
DRW_shgroup_uniform_int(grp, "depthLayer", &e_data.depth_src_layer, 1);
DRW_shgroup_call(grp, quad, nullptr);
}
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, nullptr);
}
}
void EEVEE_effects_draw_init(EEVEE_ViewLayerData * /*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 /*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 != nullptr);
stl->g_data->valid_taa_history = (txl->taa_history != nullptr);
}
}

@ -1,691 +0,0 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*/
#include "DRW_render.hh"
#include "draw_color_management.hh" /* TODO: remove dependency. */
#include "BLI_rand.h"
#include "BLT_translation.hh"
#include "BKE_object.hh"
#include "DEG_depsgraph_query.hh"
#include "DNA_world_types.h"
#include "GPU_context.hh"
#include "IMB_imbuf.hh"
#include "eevee_private.hh"
#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 : nullptr;
if (!stl->g_data) {
/* Alloc transient pointers */
stl->g_data = static_cast<EEVEE_PrivateData *>(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 != nullptr);
stl->g_data->valid_taa_history = (txl->taa_history != nullptr);
stl->g_data->queued_shaders_count = 0;
stl->g_data->queued_optimise_shaders_count = 0;
stl->g_data->render_timesteps = 1;
stl->g_data->disable_ligthprobes = v3d &&
(v3d->object_type_exclude_viewport & (1 << OB_LIGHTPROBE));
/* 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, static_cast<EEVEE_Data *>(vedata));
EEVEE_depth_of_field_cache_init(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_effects_cache_init(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_lightprobes_cache_init(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_lights_cache_init(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_materials_cache_init(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_motion_blur_cache_init(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_occlusion_cache_init(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_screen_raytrace_cache_init(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_subsurface_cache_init(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_temporal_sampling_cache_init(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_volumes_cache_init(sldata, static_cast<EEVEE_Data *>(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(
static_cast<EEVEE_Data *>(vedata), sldata, ob, &cast_shadow);
}
if (DRW_object_is_renderable(ob) && (ob_visibility & OB_VISIBLE_SELF)) {
if (ob->type == OB_MESH) {
EEVEE_materials_cache_populate(static_cast<EEVEE_Data *>(vedata), sldata, ob, &cast_shadow);
}
else if (ob->type == OB_CURVES) {
EEVEE_object_curves_cache_populate(
static_cast<EEVEE_Data *>(vedata), sldata, ob, &cast_shadow);
}
else if (ob->type == OB_VOLUME) {
EEVEE_volumes_cache_object_add(
sldata, static_cast<EEVEE_Data *>(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, static_cast<EEVEE_Data *>(vedata), ob);
}
}
else if (ob->type == OB_LAMP) {
EEVEE_lights_cache_add(sldata, ob);
}
}
if (cast_shadow) {
EEVEE_shadows_caster_register(sldata, ob);
}
}
static void eevee_cache_finish(void *vedata)
{
EEVEE_Data *ved = (EEVEE_Data *)vedata;
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
EEVEE_StorageList *stl = ved->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, static_cast<EEVEE_Data *>(vedata));
EEVEE_materials_cache_finish(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_lights_cache_finish(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_lightprobes_cache_finish(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_renderpasses_cache_finish(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_subsurface_draw_init(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_effects_draw_init(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_volumes_draw_init(sldata, static_cast<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;
}
EEVEE_renderpasses_output_init(sldata, static_cast<EEVEE_Data *>(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(static_cast<EEVEE_Data *>(vedata));
}
if (g_data->queued_shaders_count > 0) {
SNPRINTF(ved->info, RPT_("Compiling Shaders (%d remaining)"), g_data->queued_shaders_count);
}
else if (g_data->queued_optimise_shaders_count > 0) {
SNPRINTF(ved->info,
RPT_("Optimizing Shaders (%d remaining)"),
g_data->queued_optimise_shaders_count);
}
}
/* 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 = std::max(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, static_cast<EEVEE_Data *>(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, static_cast<EEVEE_Data *>(vedata));
EEVEE_lightprobes_refresh(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_lightprobes_refresh_planar(sldata, static_cast<EEVEE_Data *>(vedata));
DRW_stats_group_end();
/* Refresh shadows */
DRW_stats_group_start("Shadows");
EEVEE_shadows_draw(sldata, static_cast<EEVEE_Data *>(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(static_cast<EEVEE_Data *>(vedata));
}
/* Set ray type. */
sldata->common_data.ray_type = EEVEE_RAY_CAMERA;
sldata->common_data.ray_depth = 0.0f;
if (stl->g_data->disable_ligthprobes) {
sldata->common_data.prb_num_render_cube = 1;
sldata->common_data.prb_num_render_grid = 1;
}
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 pre-pass. */
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(static_cast<EEVEE_Data *>(vedata), dtxl->depth, -1);
DRW_stats_group_end();
EEVEE_occlusion_compute(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_volumes_compute(sldata, static_cast<EEVEE_Data *>(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, static_cast<EEVEE_Data *>(vedata));
DRW_stats_group_end();
/* Effects pre-transparency */
EEVEE_subsurface_compute(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_reflection_compute(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_occlusion_draw_debug(sldata, static_cast<EEVEE_Data *>(vedata));
if (psl->probe_display) {
DRW_draw_pass(psl->probe_display);
}
EEVEE_refraction_compute(sldata, static_cast<EEVEE_Data *>(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, static_cast<EEVEE_Data *>(vedata));
/* Render-passes. */
EEVEE_renderpasses_output_accumulate(sldata, static_cast<EEVEE_Data *>(vedata), false);
/* Transparent */
EEVEE_material_transparent_output_accumulate(static_cast<EEVEE_Data *>(vedata));
/* TODO(@fclem): should be its own Frame-buffer.
* This is needed because dual-source 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, static_cast<EEVEE_Data *>(vedata));
DRW_stats_group_end();
DRW_view_set_active(nullptr);
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(static_cast<EEVEE_Data *>(vedata));
stl->effects->ssr_was_valid_double_buffer = stl->g_data->valid_double_buffer;
}
/* Perform render step between samples to allow flushing of freed temporary GPUBackend
* resources. This prevents the GPU backend accumulating a high amount of in-flight memory when
* performing renders using eevee_draw_scene. e.g. During file thumbnail generation. */
if (loop_len > 2) {
if (GPU_backend_get_type() == GPU_BACKEND_METAL) {
GPU_flush();
GPU_render_step();
}
}
}
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, static_cast<EEVEE_Data *>(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(static_cast<EEVEE_Data *>(vedata));
stl->g_data->view_updated = false;
DRW_view_set_active(nullptr);
}
static void eevee_view_update(void *vedata)
{
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
if (stl && stl->g_data) {
stl->g_data->view_updated = true;
}
}
static void eevee_id_object_update(void * /*vedata*/, Object *object)
{
EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(object);
if (ped != nullptr && 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 != nullptr && led->dd.recalc != 0) {
led->need_update = true;
led->dd.recalc = 0;
}
EEVEE_ObjectEngineData *oedata = EEVEE_object_data_get(object);
if (oedata != nullptr && oedata->dd.recalc != 0) {
oedata->need_update = true;
oedata->geom_update = (oedata->dd.recalc & (ID_RECALC_GEOMETRY)) != 0;
oedata->dd.recalc = 0;
}
}
static void eevee_id_world_update(void *vedata, World *wo)
{
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
LightCache *lcache = stl->g_data->light_cache;
if (ELEM(lcache, nullptr, stl->lookdev_lightcache)) {
/* Avoid Lookdev viewport clearing the update flag (see #67741). */
return;
}
EEVEE_WorldEngineData *wedata = EEVEE_world_data_ensure(wo);
if (wedata != nullptr && wedata->dd.recalc != 0) {
if ((lcache->flag & LIGHTCACHE_BAKING) == 0) {
lcache->flag |= LIGHTCACHE_UPDATE_WORLD;
}
wedata->dd.recalc = 0;
}
}
void eevee_id_update(void *vedata, ID *id)
{
/* Handle updates based on ID type. */
switch (GS(id->name)) {
case ID_WO:
eevee_id_world_update(vedata, (World *)id);
break;
case ID_OB:
eevee_id_object_update(vedata, (Object *)id);
break;
default:
/* pass */
break;
}
}
static void eevee_render_reset_passes(EEVEE_Data *vedata)
{
/* Reset pass-list. This is safe as they are stored into managed memory chunks. */
memset(vedata->psl, 0, sizeof(*vedata->psl));
}
static void eevee_render_to_image(void *vedata,
RenderEngine *engine,
RenderLayer *render_layer,
const rcti *rect)
{
EEVEE_Data *ved = (EEVEE_Data *)vedata;
const DRWContextState *draw_ctx = DRW_context_state_get();
Depsgraph *depsgraph = draw_ctx->depsgraph;
Scene *scene = DEG_get_evaluated_scene(depsgraph);
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
const bool do_motion_blur = (scene->r.mode & R_MBLUR) != 0;
const bool do_motion_blur_fx = do_motion_blur && (scene->eevee.motion_blur_max > 0);
if (!EEVEE_render_init(static_cast<EEVEE_Data *>(vedata), engine, depsgraph)) {
return;
}
EEVEE_PrivateData *g_data = ved->stl->g_data;
int initial_frame = scene->r.cfra;
float initial_subframe = scene->r.subframe;
float shuttertime = (do_motion_blur) ? scene->r.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(static_cast<EEVEE_Data *>(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(static_cast<EEVEE_Data *>(vedata));
/* Compute start time. The motion blur will cover `[time ...time + shuttertime]`. */
float time = initial_frame + initial_subframe;
switch (scene->r.motion_blur_position) {
case SCE_MB_START:
/* No offset. */
break;
case SCE_MB_CENTER:
time -= shuttertime * 0.5f;
break;
case SCE_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(static_cast<EEVEE_Data *>(vedata), engine, depsgraph);
sldata = EEVEE_view_layer_data_ensure();
EEVEE_render_cache_init(sldata, static_cast<EEVEE_Data *>(vedata));
DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
EEVEE_motion_blur_cache_finish(static_cast<EEVEE_Data *>(vedata));
EEVEE_materials_cache_finish(sldata, static_cast<EEVEE_Data *>(vedata));
eevee_render_reset_passes(static_cast<EEVEE_Data *>(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(static_cast<EEVEE_Data *>(vedata), engine, depsgraph);
sldata = EEVEE_view_layer_data_ensure();
EEVEE_render_cache_init(sldata, static_cast<EEVEE_Data *>(vedata));
DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
EEVEE_motion_blur_cache_finish(static_cast<EEVEE_Data *>(vedata));
EEVEE_materials_cache_finish(sldata, static_cast<EEVEE_Data *>(vedata));
eevee_render_reset_passes(static_cast<EEVEE_Data *>(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(static_cast<EEVEE_Data *>(vedata), engine, depsgraph);
sldata = EEVEE_view_layer_data_ensure();
}
EEVEE_render_cache_init(sldata, static_cast<EEVEE_Data *>(vedata));
DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache);
EEVEE_motion_blur_cache_finish(static_cast<EEVEE_Data *>(vedata));
EEVEE_volumes_cache_finish(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_materials_cache_finish(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_lights_cache_finish(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_lightprobes_cache_finish(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_renderpasses_cache_finish(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_subsurface_draw_init(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_effects_draw_init(sldata, static_cast<EEVEE_Data *>(vedata));
EEVEE_volumes_draw_init(sldata, static_cast<EEVEE_Data *>(vedata));
}
/* Actual drawing. */
{
EEVEE_renderpasses_output_init(sldata,
static_cast<EEVEE_Data *>(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(static_cast<EEVEE_Data *>(vedata));
EEVEE_render_draw(static_cast<EEVEE_Data *>(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(static_cast<EEVEE_Data *>(vedata));
}
}
EEVEE_motion_blur_data_free(&ved->stl->effects->motion_blur);
if (RE_engine_test_break(engine)) {
return;
}
EEVEE_render_read_result(static_cast<EEVEE_Data *>(vedata), engine, render_layer, rect);
/* Restore original viewport size. */
int viewport_size[2] = {int(g_data->size_orig[0]), int(g_data->size_orig[1])};
DRW_render_viewport_size_set(viewport_size);
if (scene->r.cfra != initial_frame || scene->r.subframe != initial_subframe) {
/* Restore original frame number. This is because the render pipeline expects it. */
RE_engine_frame_set(engine, initial_frame, initial_subframe);
}
}
static void eevee_store_metadata(void *vedata, RenderResult *render_result)
{
EEVEE_Data *ved = (EEVEE_Data *)vedata;
EEVEE_PrivateData *g_data = ved->stl->g_data;
if (g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) {
EEVEE_cryptomatte_store_metadata(ved, render_result);
EEVEE_cryptomatte_free(ved);
}
}
static void eevee_engine_free()
{
EEVEE_shaders_free();
EEVEE_lightprobes_free();
EEVEE_materials_free();
EEVEE_occlusion_free();
EEVEE_volumes_free();
}
static const DrawEngineDataSize eevee_data_size = DRW_VIEWPORT_DATA_SIZE(EEVEE_Data);
DrawEngineType draw_engine_eevee_type = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*idname*/ N_("EEVEE"),
/*vedata_size*/ &eevee_data_size,
/*engine_init*/ &eevee_engine_init,
/*engine_free*/ &eevee_engine_free,
/*instance_free*/ nullptr,
/*cache_init*/ &eevee_cache_init,
/*cache_populate*/ &EEVEE_cache_populate,
/*cache_finish*/ &eevee_cache_finish,
/*draw_scene*/ &eevee_draw_scene,
/*view_update*/ &eevee_view_update,
/*id_update*/ &eevee_id_update,
/*render_to_image*/ &eevee_render_to_image,
/*store_metadata*/ &eevee_store_metadata,
};
RenderEngineType DRW_engine_viewport_eevee_type = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*idname*/ EEVEE_ENGINE,
/*name*/ N_("EEVEE (Legacy)"),
/*flag*/ RE_INTERNAL | RE_USE_PREVIEW | RE_USE_STEREO_VIEWPORT | RE_USE_GPU_CONTEXT,
/*update*/ nullptr,
/*render*/ &DRW_render_to_image,
/*render_frame_finish*/ nullptr,
/*draw*/ nullptr,
/*bake*/ nullptr,
/*view_update*/ nullptr,
/*view_draw*/ nullptr,
/*update_script_node*/ nullptr,
/*update_render_passes*/ &EEVEE_render_update_passes,
/*draw_engine*/ &draw_engine_eevee_type,
/*rna_ext*/
{
/*data*/ nullptr,
/*srna*/ nullptr,
/*call*/ nullptr,
},
};
#undef EEVEE_ENGINE

@ -1,19 +0,0 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup DNA
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
extern RenderEngineType DRW_engine_viewport_eevee_type;
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

@ -1,71 +0,0 @@
/* SPDX-FileCopyrightText: 2018 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup eevee
*/
#pragma once
#include "BLI_sys_types.h" /* for bool */
struct BlendDataReader;
struct BlendWriter;
struct EEVEE_Data;
struct EEVEE_ViewLayerData;
struct LightCache;
struct Scene;
struct SceneEEVEE;
struct ViewLayer;
struct wmJobWorkerStatus;
#ifdef __cplusplus
extern "C" {
#endif
/**
* Light Bake.
*/
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);
/**
* MUST run on the main thread.
*/
void *EEVEE_lightbake_job_data_alloc(struct Main *bmain,
struct ViewLayer *view_layer,
struct Scene *scene,
bool run_as_job,
int frame);
void EEVEE_lightbake_job_data_free(void *custom_data);
void EEVEE_lightbake_update(void *custom_data);
void EEVEE_lightbake_job(void *custom_data, wmJobWorkerStatus *worker_status);
/**
* This is to update the world irradiance and reflection contribution from
* within the viewport drawing (does not have the overhead of a full light cache rebuild.)
*/
void EEVEE_lightbake_update_world_quick(struct EEVEE_ViewLayerData *sldata,
struct EEVEE_Data *vedata,
const Scene *scene);
/**
* Light Cache.
*/
struct LightCache *EEVEE_lightcache_create(
int grid_len, int cube_len, int cube_size, int vis_size, const int irr_size[3]);
void EEVEE_lightcache_free(struct LightCache *lcache);
bool EEVEE_lightcache_load(struct LightCache *lcache);
void EEVEE_lightcache_info_update(struct SceneEEVEE *eevee);
void EEVEE_lightcache_blend_write(struct BlendWriter *writer, struct LightCache *cache);
void EEVEE_lightcache_blend_read_data(struct BlendDataReader *reader, struct LightCache *cache);
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

@ -1,273 +0,0 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup DNA
*/
#include "BLI_math_rotation.h"
#include "BLI_sys_types.h" /* bool */
#include "BKE_object.hh"
#include "DEG_depsgraph_query.hh"
#include "eevee_private.hh"
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->radius);
}
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->radius);
}
}
static float light_shape_radiance_get(const Light *la, const EEVEE_Light *evli)
{
/* Make illumination power constant. */
switch (la->type) {
case LA_AREA: {
/* Rectangle area. */
float area = (evli->sizex * 2.0f) * (evli->sizey * 2.0f);
/* Scale for the lower area of the ellipse compared to the surrounding rectangle. */
if (ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) {
area *= M_PI / 4.0f;
}
/* Convert radiant flux to radiance. */
return float(M_1_PI) / area;
}
case LA_SPOT:
case LA_LOCAL: {
/* Sphere area. */
float area = 4.0f * float(M_PI) * square_f(evli->radius);
/* Convert radiant flux to radiance. */
return 1.0f / (area * float(M_PI));
}
default:
case LA_SUN: {
float inv_sin_sq = 1.0f + 1.0f / square_f(evli->radius);
/* Convert irradiance to radiance. */
return float(M_1_PI) * inv_sin_sq;
}
}
}
/* Returns a factor to apply to light power to get a point light radiance instead of a shape
* radiance. */
static float light_volume_radiance_factor_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;
switch (la->type) {
case LA_AREA: {
/* 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 * 2.0f) * (evli->sizey * 2.0f);
float tmp = M_PI_2 / (M_PI_2 + sqrtf(area));
/* Lerp between 1.0 and the limit (1 / pi). */
float mrp_scaling = tmp + (1.0f - tmp) * M_1_PI;
power *= float(M_1_PI) * mrp_scaling;
break;
}
case LA_SPOT:
case LA_LOCAL: {
/* Convert radiant flux to intensity. */
/* Inverse of sphere solid angle. */
power *= 0.25f * float(M_1_PI);
break;
}
default:
case LA_SUN: {
/* Do nothing. */
break;
}
}
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->object_to_world().location());
/* 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->object_to_world().ptr(), 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;
}
else if (la->mode & LA_USE_SOFT_FALLOFF) {
if (la->type == LA_LOCAL) {
evli->light_type = LAMPTYPE_OMNI_DISK;
}
else if (la->type == LA_SPOT) {
evli->light_type = LAMPTYPE_SPOT_DISK;
}
}
float shape_power = light_shape_radiance_get(la, evli);
mul_v3_fl(evli->color, shape_power * la->energy);
evli->volume *= light_volume_radiance_factor_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);
}

@ -1,379 +0,0 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*/
#include "DRW_render.hh"
#include "BKE_camera.h"
#include "BKE_studiolight.h"
#include "BLI_math_rotation.h"
#include "BLI_rand.h"
#include "BLI_rect.h"
#include "DNA_screen_types.h"
#include "DNA_world_types.h"
#include "DEG_depsgraph_query.hh"
#include "ED_screen.hh"
#include "GPU_material.hh"
#include "UI_resources.hh"
#include "eevee_lightcache.h"
#include "eevee_private.hh"
#include "draw_common_c.hh"
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;
blender::gpu::Batch *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, nullptr, mat_options);
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, nullptr, nullptr, -1.0f, false, false);
DRW_shgroup_add_material_resources(grp, gpumat);
DRW_shgroup_call(grp, sphere, nullptr);
}
{
Material *ma = EEVEE_material_default_glossy_get();
GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, nullptr, mat_options);
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, nullptr, nullptr, -1.0f, false, false);
DRW_shgroup_add_material_resources(grp, gpumat);
DRW_shgroup_call(grp, sphere, nullptr);
}
}
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 nullptr 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_viewport_image_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 * UI_SCALE_FAC), 0.5f, 1.0f);
const int sphere_size = U.lookdev_sphere_size * UI_SCALE_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 #U.lookdev_sphere_size. */
float res_scale = clamp_f(
(U.lookdev_sphere_size / 400.0f) * viewport_scale * UI_SCALE_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 nullptr when rendering previews. */
const View3D *v3d = draw_ctx->v3d;
const Scene *scene = draw_ctx->scene;
const bool probe_render = pinfo != nullptr;
effects->lookdev_view = nullptr;
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 == nullptr || (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 == nullptr) || (stl->lookdev_cube_data == nullptr) ||
(txl->lookdev_grid_tx == nullptr) || (txl->lookdev_cube_tx == nullptr) ||
(g_data->light_cache && g_data->light_cache->ref_res != cube_res))
{
eevee_lookdev_lightcache_delete(vedata);
}
if (stl->lookdev_lightcache == nullptr) {
#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, blender::int3{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(nullptr, view_matrix, false);
copy_m3_m4(view_rot_matrix, view_matrix);
axis_angle_to_mat3_single(x_rot_matrix, 'X', M_PI_2);
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. */
GPUSamplerState state = {GPU_SAMPLER_FILTERING_LINEAR,
GPU_SAMPLER_EXTEND_MODE_REPEAT,
GPU_SAMPLER_EXTEND_MODE_EXTEND};
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);
DRW_shgroup_uniform_float_copy(grp, "studioLightBlur", 0.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);
DRW_shgroup_uniform_float_copy(grp, "studioLightIntensity", 1.0f);
}
/* 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(nullptr, 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, float(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(nullptr);
}
}

@ -1,111 +0,0 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \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.hh"
#include "BLI_fileops.h"
#include "BLI_rand.h"
#include "BLI_string_utils.hh"
#include "eevee_private.hh"
#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, nullptr, 1);
GPUTexture *tex = DRW_texture_create_2d(
lut_size, lut_size, GPU_RG16F, DRWTextureFlag(0), nullptr);
GPUFrameBuffer *fb = nullptr;
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 = static_cast<float *>(GPU_texture_read(tex, GPU_DATA_FLOAT, 0));
GPU_texture_free(tex);
#if DO_FILE_OUTPUT
/* Content is to be put inside `eevee_lut.cc`. */
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_factor", &roughness, 1);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
GPUTexture *tex = DRW_texture_create_2d_array(
lut_size, lut_size, lut_depth, GPU_RG16F, DRWTextureFlag(0), nullptr);
GPUFrameBuffer *fb = nullptr;
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 = static_cast<float *>(GPU_texture_read(tex, GPU_DATA_FLOAT, 0));
GPU_texture_free(tex);
#if DO_FILE_OUTPUT
/* Content is to be put inside `eevee_lut.cc`. 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;
}

File diff suppressed because it is too large Load Diff

@ -1,99 +0,0 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \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.hh"
#include "DRW_render.hh"
#include "DNA_world_types.h"
#include "BLI_string_utils.hh"
#include "eevee_private.hh"
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, DRWTextureFlag(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(nullptr);
float far = DRW_view_far_distance_get(nullptr);
/* 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(), nullptr);
}
void EEVEE_mist_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->mist_accum_fb != nullptr) {
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);
}
}

@ -1,670 +0,0 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*
* Gather all screen space effects technique such as Bloom, Motion Blur, DoF, SSAO, SSR, ...
*/
#include "DRW_render.hh"
#include "BLI_rand.h"
#include "BLI_string_utils.hh"
#include "BKE_animsys.h"
#include "BKE_camera.h"
#include "BKE_duplilist.hh"
#include "BKE_object.hh"
#include "BKE_screen.hh"
#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.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_query.hh"
#include "GPU_batch.hh"
#include "GPU_texture.hh"
#include "eevee_private.hh"
int EEVEE_motion_blur_init(EEVEE_ViewLayerData * /*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->r.mode & R_MBLUR)) {
if (DRW_state_is_scene_render()) {
int mb_step = effects->motion_blur_step;
DRW_view_viewmat_get(nullptr, effects->motion_blur.camera[mb_step].viewmat, false);
DRW_view_persmat_get(nullptr, effects->motion_blur.camera[mb_step].persmat, false);
DRW_view_persmat_get(nullptr, 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),
};
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT;
effects->velocity_tiles_x_tx = DRW_texture_pool_query_2d_ex(
tx_size[0], fs_size[1], GPU_RGBA16, usage, &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_ex(
tx_size[0], tx_size[1], GPU_RGBA16, usage, &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(nullptr, effects->motion_blur.camera[mb_step].viewmat, false);
DRW_view_persmat_get(nullptr, effects->motion_blur.camera[mb_step].persmat, false);
DRW_view_persmat_get(nullptr, effects->motion_blur.camera[mb_step].persinv, true);
}
effects->motion_blur_near_far[0] = fabsf(DRW_view_near_distance_get(nullptr));
effects->motion_blur_near_far[1] = fabsf(DRW_view_far_distance_get(nullptr));
}
void EEVEE_motion_blur_cache_init(EEVEE_ViewLayerData * /*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", blender::int2{int(fs_size[0]), int(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", blender::int2{1, 0});
DRW_shgroup_call_procedural_triangles(grp, nullptr, 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", blender::int2{tx_size[0], int(fs_size[1])});
DRW_shgroup_uniform_ivec2_copy(grp, "gatherStep", blender::int2{0, 1});
DRW_shgroup_call_procedural_triangles(grp, nullptr, 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, nullptr, 1);
}
}
{
DRW_PASS_CREATE(psl->motion_blur, DRW_STATE_WRITE_COLOR);
const GPUSamplerState state = GPUSamplerState::default_sampler();
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(nullptr));
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, nullptr, 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 = nullptr;
psl->velocity_object = nullptr;
psl->velocity_hair = nullptr;
}
}
void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData * /*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 = nullptr;
if (!DRW_state_is_scene_render() || psl->velocity_hair == nullptr) {
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(mb_data, ob);
int psys_id = (md != nullptr) ? 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].step_data[MB_PREV].hair_pos_tx;
GPUTexture *tex_next = mb_hair->psys[psys_id].step_data[MB_NEXT].hair_pos_tx;
grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp, nullptr);
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].step_data[mb_step].hair_pos = DRW_hair_pos_buffer_get(ob, psys, md);
mb_hair->use_deform = true;
}
}
}
void EEVEE_motion_blur_curves_cache_populate(EEVEE_ViewLayerData * /*sldata*/,
EEVEE_Data *vedata,
Object *ob)
{
using namespace blender::draw;
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
if (!DRW_state_is_scene_render() || psl->velocity_hair == nullptr) {
return;
}
/* For now we assume curves objects are always moving. */
EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(
&effects->motion_blur, ob, false);
if (mb_data == nullptr) {
return;
}
int mb_step = effects->motion_blur_step;
/* Store transform. */
copy_m4_m4(mb_data->obmat[mb_step], ob->object_to_world().ptr());
EEVEE_HairMotionData *mb_curves = EEVEE_motion_blur_curves_data_get(mb_data);
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_curves->psys[0].step_data[MB_PREV].hair_pos_tx;
GPUTexture *tex_next = mb_curves->psys[0].step_data[MB_NEXT].hair_pos_tx;
DRWShadingGroup *grp = DRW_shgroup_curves_create_sub(
ob, effects->motion_blur.hair_grp, nullptr);
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_curves->use_deform, 1);
}
else {
/* Store vertex position buffer. */
mb_curves->psys[0].step_data[mb_step].hair_pos = DRW_curves_pos_buffer_get(ob);
mb_curves->use_deform = true;
}
}
void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData * /*sldata*/,
EEVEE_Data *vedata,
Object *ob)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
DRWShadingGroup *grp = nullptr;
if (!DRW_state_is_scene_render() || psl->velocity_object == nullptr) {
return;
}
RigidBodyOb *rbo = ob->rigidbody_object;
/* active rigidbody objects only, as only those are affected by sim. */
const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE));
#if 0
/* For now we assume dupli objects are moving. */
const bool is_dupli = (ob->base_flag & BASE_FROM_DUPLI) != 0;
const bool object_moves = is_dupli || has_rigidbody || BKE_object_moves_in_time(ob, true);
#else
/* BKE_object_moves_in_time does not work in some cases.
* Better detect non moving object after evaluation. */
const bool object_moves = true;
#endif
const bool is_deform = BKE_object_is_deform_modified(DRW_context_state_get()->scene, ob) ||
(has_rigidbody && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0);
if (!(object_moves || is_deform)) {
return;
}
EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(
&effects->motion_blur, ob, false);
if (mb_data) {
int mb_step = effects->motion_blur_step;
/* Store transform. */
copy_m4_m4(mb_data->obmat[mb_step], ob->object_to_world().ptr());
EEVEE_GeometryMotionData *mb_geom = EEVEE_motion_blur_geometry_data_get(mb_data);
if (mb_step == MB_CURR) {
blender::gpu::Batch *batch = DRW_cache_object_surface_get(ob);
if (batch == nullptr) {
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] != nullptr);
}
else {
mb_geom->vbo[mb_step] = nullptr;
mb_geom->use_deform = false;
}
}
}
static void motion_blur_remove_vbo_reference_from_batch(blender::gpu::Batch *batch,
blender::gpu::VertBuf *vbo1,
blender::gpu::VertBuf *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] = nullptr;
}
}
}
void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata)
{
using namespace blender::draw;
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_curves_update. */
GPU_framebuffer_bind(vedata->fbl->main_fb);
DRW_curves_update();
DRW_cache_restart();
}
for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object);
BLI_ghashIterator_done(&ghi) == false;
BLI_ghashIterator_step(&ghi))
{
EEVEE_ObjectMotionData *mb_data = static_cast<EEVEE_ObjectMotionData *>(
BLI_ghashIterator_getValue(&ghi));
EEVEE_HairMotionData *mb_hair = mb_data->hair_data;
EEVEE_GeometryMotionData *mb_geom = mb_data->geometry_data;
if (mb_hair != nullptr && mb_hair->use_deform) {
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++) {
blender::gpu::VertBuf *vbo = mb_hair->psys[i].step_data[mb_step].hair_pos;
if (vbo == nullptr) {
continue;
}
EEVEE_HairMotionStepData **step_data_cache_ptr;
if (!BLI_ghash_ensure_p(effects->motion_blur.hair_motion_step_cache[mb_step],
vbo,
(void ***)&step_data_cache_ptr))
{
EEVEE_HairMotionStepData *new_step_data = static_cast<EEVEE_HairMotionStepData *>(
MEM_callocN(sizeof(EEVEE_HairMotionStepData), __func__));
/* Duplicate the vbo, otherwise it would be lost when evaluating another frame. */
new_step_data->hair_pos = GPU_vertbuf_duplicate(vbo);
/* Create vbo immediately to bind to texture buffer. */
GPU_vertbuf_use(new_step_data->hair_pos);
new_step_data->hair_pos_tx = GPU_texture_create_from_vertbuf("hair_pos_motion_blur",
new_step_data->hair_pos);
*step_data_cache_ptr = new_step_data;
}
mb_hair->psys[i].step_data[mb_step] = **step_data_cache_ptr;
}
}
}
if (mb_geom != nullptr && mb_geom->use_deform) {
if (mb_step == MB_CURR) {
/* Modify batch to have data from adjacent frames. */
blender::gpu::Batch *batch = mb_geom->batch;
for (int i = 0; i < MB_CURR; i++) {
blender::gpu::VertBuf *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]);
break;
}
/* Avoid adding the same vbo more than once when the batch is used by multiple
* instances. */
if (!GPU_batch_vertbuf_has(batch, vbo)) {
/* Currently, the code assumes that all objects that share the same mesh in the
* current frame also share the same mesh on other frames. */
GPU_batch_vertbuf_add(batch, vbo, false);
}
}
}
}
else {
blender::gpu::VertBuf *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(). */
blender::gpu::VertBuf **vbo_cache_ptr;
if (!BLI_ghash_ensure_p(
effects->motion_blur.position_vbo_cache[mb_step], vbo, (void ***)&vbo_cache_ptr))
{
/* Duplicate the vbo, otherwise it would be lost when evaluating another frame. */
blender::gpu::VertBuf *duplicated_vbo = GPU_vertbuf_duplicate(vbo);
*vbo_cache_ptr = duplicated_vbo;
/* Find and replace "pos" attrib name. */
GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(duplicated_vbo);
int attrib_id = GPU_vertformat_attr_id_get(format, "pos");
GPU_vertformat_attr_rename(format, attrib_id, (mb_step == MB_PREV) ? "prv" : "nxt");
}
mb_geom->vbo[mb_step] = vbo = *vbo_cache_ptr;
}
else {
/* This might happen if the object visibility has been animated. */
mb_geom->use_deform = false;
}
}
}
}
}
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];
/* Swap #position_vbo_cache pointers. */
if (effects->motion_blur.position_vbo_cache[MB_PREV]) {
BLI_ghash_free(effects->motion_blur.position_vbo_cache[MB_PREV],
nullptr,
(GHashValFreeFP)GPU_vertbuf_discard);
}
effects->motion_blur.position_vbo_cache[MB_PREV] =
effects->motion_blur.position_vbo_cache[MB_NEXT];
effects->motion_blur.position_vbo_cache[MB_NEXT] = nullptr;
/* Swap #hair_motion_step_cache pointers. */
if (effects->motion_blur.hair_motion_step_cache[MB_PREV]) {
BLI_ghash_free(effects->motion_blur.hair_motion_step_cache[MB_PREV],
nullptr,
(GHashValFreeFP)EEVEE_motion_hair_step_free);
}
effects->motion_blur.hair_motion_step_cache[MB_PREV] =
effects->motion_blur.hair_motion_step_cache[MB_NEXT];
effects->motion_blur.hair_motion_step_cache[MB_NEXT] = nullptr;
/* Rename attributes in #position_vbo_cache. */
for (BLI_ghashIterator_init(&ghi, effects->motion_blur.position_vbo_cache[MB_PREV]);
!BLI_ghashIterator_done(&ghi);
BLI_ghashIterator_step(&ghi))
{
blender::gpu::VertBuf *vbo = static_cast<blender::gpu::VertBuf *>(
BLI_ghashIterator_getValue(&ghi));
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");
}
/* Object Data. */
for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object); !BLI_ghashIterator_done(&ghi);
BLI_ghashIterator_step(&ghi))
{
EEVEE_ObjectMotionData *mb_data = static_cast<EEVEE_ObjectMotionData *>(
BLI_ghashIterator_getValue(&ghi));
EEVEE_GeometryMotionData *mb_geom = mb_data->geometry_data;
EEVEE_HairMotionData *mb_hair = mb_data->hair_data;
copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_NEXT]);
if (mb_hair != nullptr) {
for (int i = 0; i < mb_hair->psys_len; i++) {
mb_hair->psys[i].step_data[MB_PREV].hair_pos =
mb_hair->psys[i].step_data[MB_NEXT].hair_pos;
mb_hair->psys[i].step_data[MB_PREV].hair_pos_tx =
mb_hair->psys[i].step_data[MB_NEXT].hair_pos_tx;
mb_hair->psys[i].step_data[MB_NEXT].hair_pos = nullptr;
mb_hair->psys[i].step_data[MB_NEXT].hair_pos_tx = nullptr;
}
}
if (mb_geom != nullptr) {
if (mb_geom->batch != nullptr) {
motion_blur_remove_vbo_reference_from_batch(
mb_geom->batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]);
}
mb_geom->vbo[MB_PREV] = mb_geom->vbo[MB_NEXT];
mb_geom->vbo[MB_NEXT] = nullptr;
}
}
}
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();
}
}

@ -1,272 +0,0 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*
* Implementation of the screen space Ground Truth Ambient Occlusion.
*/
#include "DRW_render.hh"
#include "BLI_string_utils.hh"
#include "DEG_depsgraph_query.hh"
#include "BKE_global.hh" /* for G.debug_value */
#include "eevee_private.hh"
#include "GPU_capabilities.hh"
#include "GPU_platform.hh"
#include "GPU_state.hh"
static struct {
GPUTexture *dummy_horizon_tx;
} e_data = {nullptr}; /* 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) {
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_SHADER_READ;
const float pixel[4] = {0.0f, 0.0f, 0.0f, 0.0f};
e_data.dummy_horizon_tx = DRW_texture_create_2d_ex(
1, 1, GPU_RGBA8, usage, 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;
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_SHADER_READ;
effects->gtao_horizons_renderpass = DRW_texture_pool_query_2d_ex(
UNPACK2(effects->hiz_size), GPU_RGBA8, usage, &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 = nullptr;
}
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, DRWTextureFlag(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(), nullptr);
}
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, nullptr, 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, nullptr, 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_ex(GPU_DEVICE_INTEL_UHD, GPU_OS_WIN, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL))
{
/* 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 * /*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 != nullptr) {
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()
{
DRW_TEXTURE_FREE_SAFE(e_data.dummy_horizon_tx);
}

File diff suppressed because it is too large Load Diff

@ -1,758 +0,0 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*/
/**
* Render functions for final render outputs.
*/
#include "DRW_engine.hh"
#include "DRW_render.hh"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "BKE_global.hh"
#include "BKE_object.hh"
#include "BLI_rand.h"
#include "BLI_rect.h"
#include "DEG_depsgraph_query.hh"
#include "GPU_capabilities.hh"
#include "GPU_context.hh"
#include "GPU_framebuffer.hh"
#include "GPU_state.hh"
#include "RE_pipeline.h"
#include "IMB_imbuf_types.hh"
#include "eevee_private.hh"
bool EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, 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 = static_cast<EEVEE_PrivateData *>(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 = false;
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, blender::float2{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] = {
int(size_orig[0] + g_data->overscan_pixels * 2.0f),
int(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];
SNPRINTF(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, DRWTextureFlag(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, 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? */
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, 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? */
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, nullptr, nullptr, nullptr);
DRW_view_reset();
DRW_view_default_set(view);
DRW_view_set_active(view);
}
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, Object *ob, RenderEngine *engine, Depsgraph *depsgraph)
{
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
EEVEE_Data *data = static_cast<EEVEE_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 != nullptr) &&
((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 #59649) */
if (engine && (ob->base_flag & BASE_FROM_DUPLI) == 0) {
char info[42];
SNPRINTF(info, "Syncing %s", ob->id.name + 2);
RE_engine_update_stats(engine, nullptr, info);
}
const int ob_visibility = DRW_object_visibility_in_active_context(ob);
if (ob_visibility & OB_VISIBLE_PARTICLES) {
EEVEE_particle_hair_cache_populate(
static_cast<EEVEE_Data *>(vedata), sldata, ob, &cast_shadow);
if (do_cryptomatte) {
EEVEE_cryptomatte_particle_hair_cache_populate(data, sldata, ob);
}
}
if (ob_visibility & OB_VISIBLE_SELF) {
if (ob->type == OB_MESH) {
EEVEE_materials_cache_populate(static_cast<EEVEE_Data *>(vedata), sldata, ob, &cast_shadow);
if (do_cryptomatte) {
EEVEE_cryptomatte_cache_populate(data, sldata, ob);
}
}
else if (ob->type == OB_CURVES) {
EEVEE_object_curves_cache_populate(
static_cast<EEVEE_Data *>(vedata), sldata, ob, &cast_shadow);
if (do_cryptomatte) {
EEVEE_cryptomatte_object_curves_cache_populate(data, sldata, ob);
}
}
else if (ob->type == OB_VOLUME) {
Scene *scene = DEG_get_evaluated_scene(depsgraph);
EEVEE_volumes_cache_object_add(sldata, static_cast<EEVEE_Data *>(vedata), scene, ob);
}
else if (ob->type == OB_LIGHTPROBE) {
EEVEE_lightprobes_cache_add(sldata, static_cast<EEVEE_Data *>(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 == nullptr) {
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->ibuf->float_buffer.data);
}
static void eevee_render_result_combined(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData * /*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);
}
}
static void eevee_render_result_transparent(RenderLayer *rl,
const char *viewname,
const rcti *rect,
EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata)
{
if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_TRANSPARENT) != 0) {
EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_TRANSPARENT, 0);
eevee_render_color_result(
rl, viewname, rect, RE_PASSNAME_TRANSPARENT, 4, 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)
{
using namespace blender::draw;
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_curves_update */
GPU_framebuffer_bind(fbl->main_fb);
DRW_curves_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 #59649) */
else if ((render_samples % 25) == 0 || (render_samples + 1) == tot_sample) {
char info[42];
SNPRINTF(info, "Rendering %u / %u samples", render_samples + 1, tot_sample);
RE_engine_update_stats(engine, nullptr, 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 pre-pass. */
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 */
EEVEE_material_transparent_output_accumulate(vedata);
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);
/* NOTE(@fclem): Seems to fix TDR issue with NVidia drivers. */
if (GPU_type_matches_ex(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL)) {
GPU_finish();
}
/* Perform render step between samples to allow
* flushing of freed GPUBackend resources. */
GPU_render_step();
if (GPU_type_matches_ex(GPU_DEVICE_ANY, GPU_OS_ANY, GPU_DRIVER_ANY, GPU_BACKEND_METAL)) {
if (render_samples > 0 && ((render_samples % 64) == 0)) {
/* Allow GPU to sync with CPU to prevent overly large command submissions being in-flight
* simultaneously. Reduces total in-flight memory required for rendering. */
GPU_finish();
}
else {
GPU_flush();
}
}
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_transparent(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");
CHECK_PASS_EEVEE(TRANSPARENT, SOCK_RGBA, 4, "RGBA");
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
}

@ -1,517 +0,0 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*/
#include "DRW_engine.hh"
#include "DRW_render.hh"
#include "draw_color_management.hh" /* TODO: remove dependency. */
#include "BKE_global.hh" /* for G.debug_value */
#include "BLI_hash.h"
#include "BLI_string_utils.hh"
#include "DEG_depsgraph_query.hh"
#include "eevee_private.hh"
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,
};
/* 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_TRANSPARENT | 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;
}
uint EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov)
{
uint hash = BLI_hash_string(aov->name) << 1u;
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 = eViewLayerEEVEEPassType(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 = static_cast<ViewLayerAOV *>(
BLI_findstring(&view_layer->aovs, v3d->shading.aov_name, offsetof(ViewLayerAOV, name)));
if (aov != nullptr) {
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 = eViewLayerEEVEEPassType(
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_material_transparent_output_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, DRWTextureFlag(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(), nullptr);
}
else {
psl->renderpass_pass = nullptr;
}
}
void EEVEE_renderpasses_postprocess(EEVEE_ViewLayerData * /*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_TRANSPARENT: {
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR_ALPHA;
g_data->renderpass_input = txl->transparent_accum;
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_viewport_image_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 = nullptr;
/* 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);
}
}

@ -1,110 +0,0 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup EEVEE
*/
#include "eevee_private.hh"
#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 #68594) */
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 #68594) */
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);
/* Decorrelate AA and shadow samples. (see #68594) */
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);
/* Decorrelate AA and shadow samples. (see #68594) */
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);
}

@ -1,321 +0,0 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*
* Screen space reflections and refractions techniques.
*/
#include "DRW_render.hh"
#include "BLI_dynstr.h"
#include "BLI_string_utils.hh"
#include "DEG_depsgraph_query.hh"
#include "GPU_platform.hh"
#include "GPU_texture.hh"
#include "eevee_private.hh"
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(nullptr);
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. */
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT;
effects->ssr_specrough_input = DRW_texture_pool_query_2d_ex(
UNPACK2(size_fs), format, usage, static_cast<DrawEngineType *>(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_ex(
UNPACK2(tracing_res), GPU_RGBA16F, usage, static_cast<DrawEngineType *>(owner));
effects->ssr_hit_depth = DRW_texture_pool_query_2d_ex(
UNPACK2(tracing_res), GPU_R16F, usage, static_cast<DrawEngineType *>(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),
});
/* NOTE(Metal): Intel GPUs rendering with Metal require the reflections pass to be split
* into two separate phases. This reduces the individual complexity of each shader
* invocation. */
effects->use_split_ssr_pass = GPU_type_matches_ex(
GPU_DEVICE_INTEL, GPU_OS_MAC, GPU_DRIVER_ANY, GPU_BACKEND_METAL);
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 = nullptr;
effects->ssr_hit_output = nullptr;
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) {
GPUShader *trace_shader = EEVEE_shaders_effect_reflection_trace_sh_get();
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", blender::float2{float(hitbuf_size[0]), float(hitbuf_size[1])});
DRW_shgroup_uniform_float_copy(
grp, "randomScale", effects->reflection_trace_full ? 0.0f : 0.5f);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
GPUSamplerState no_filter = GPUSamplerState::default_sampler();
if (effects->use_split_ssr_pass) {
/* Prepare passes for split reflections resolve variant. */
for (int i = 0; i < 2; i++) {
if (i == 0) {
/* Prepare Reflection Probes resolve pass. */
GPUShader *resolve_shader_probe = EEVEE_shaders_effect_reflection_resolve_probe_sh_get();
DRW_PASS_CREATE(psl->ssr_resolve_probe, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD);
grp = DRW_shgroup_create(resolve_shader_probe, psl->ssr_resolve_probe);
}
else if (i == 1) {
/* Prepare SSR resolve pass. */
GPUShader *resolve_shader_refl = EEVEE_shaders_effect_reflection_resolve_refl_sh_get();
DRW_PASS_CREATE(psl->ssr_resolve_refl, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD);
grp = DRW_shgroup_create(resolve_shader_refl, psl->ssr_resolve_refl);
}
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, nullptr, 1);
}
}
else {
/* Prepare standard reflections resolve pass. */
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, nullptr, 1);
}
}
}
void EEVEE_refraction_compute(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;
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 * /*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");
/* Ray-trace. */
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);
if (effects->use_split_ssr_pass) {
/* Trace reflections for probes and SSR independently */
DRW_draw_pass(psl->ssr_resolve_probe);
DRW_draw_pass(psl->ssr_resolve_refl);
}
else {
DRW_draw_pass(psl->ssr_resolve);
}
/* Restore */
GPU_framebuffer_bind(fbl->main_fb);
DRW_stats_group_end();
}
}
void EEVEE_reflection_output_init(EEVEE_ViewLayerData * /*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, DRWTextureFlag(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 * /*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);
}
if (effects->use_split_ssr_pass) {
DRW_draw_pass(psl->ssr_resolve_probe);
DRW_draw_pass(psl->ssr_resolve_refl);
}
else {
DRW_draw_pass(psl->ssr_resolve);
}
}
}

File diff suppressed because it is too large Load Diff

@ -1,191 +0,0 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup EEVEE
*
* This file is only there to handle ShaderCreateInfos.
*/
#include "GPU_shader.hh"
#include "BLI_string_ref.hh"
#include "gpu_shader_create_info.hh"
#include "eevee_private.hh"
#include <sstream>
using blender::gpu::shader::StageInterfaceInfo;
void eevee_shader_material_create_info_amend(GPUMaterial *gpumat,
GPUCodegenOutput *codegen_,
char *vert,
char *geom,
char *frag,
const char *vert_info_name,
const char *geom_info_name,
const char *frag_info_name,
char *defines)
{
using namespace blender::gpu::shader;
uint64_t options = GPU_material_uuid_get(gpumat);
const bool is_background = (options & (VAR_WORLD_PROBE | VAR_WORLD_BACKGROUND)) != 0;
const bool is_volume = (options & (VAR_MAT_VOLUME)) != 0;
const bool is_hair = (options & (VAR_MAT_HAIR)) != 0;
const bool is_mesh = (options & (VAR_MAT_MESH)) != 0;
const bool is_point_cloud = (options & (VAR_MAT_POINTCLOUD)) != 0;
GPUCodegenOutput &codegen = *codegen_;
ShaderCreateInfo &info = *reinterpret_cast<ShaderCreateInfo *>(codegen.create_info);
/* Append stage-specific create info. */
if (vert_info_name) {
info.additional_info(vert_info_name);
}
if (geom_info_name) {
info.additional_info(geom_info_name);
}
if (frag_info_name) {
info.additional_info(frag_info_name);
}
info.auto_resource_location(true);
info.define("UNI_ATTR(a)", "a");
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_SUBSURFACE)) {
info.define("USE_SSS");
}
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_SHADER_TO_RGBA)) {
info.define("USE_SHADER_TO_RGBA");
}
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_BARYCENTRIC) && !is_volume && !is_hair &&
!is_point_cloud && !is_background)
{
info.define("USE_BARYCENTRICS");
info.builtins(BuiltinBits::BARYCENTRIC_COORD);
}
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_BARYCENTRIC) && is_hair) {
info.define("USE_BARYCENTRICS");
}
/* Lookdev - Add FragDepth. */
if (options & VAR_MAT_LOOKDEV) {
info.define("LOOKDEV");
info.depth_write(DepthWrite::ANY);
}
std::stringstream attr_load;
const bool do_fragment_attrib_load = is_background || is_volume;
if (is_hair && !info.vertex_out_interfaces_.is_empty()) {
/** Hair attributes come from sampler buffer. Transfer attributes to sampler. */
for (auto &input : info.vertex_inputs_) {
info.sampler(0, ImageType::FLOAT_BUFFER, input.name, Frequency::BATCH);
}
info.vertex_inputs_.clear();
}
else if (do_fragment_attrib_load && !info.vertex_out_interfaces_.is_empty()) {
/* Code-generation outputs only one interface. */
const StageInterfaceInfo &iface = *info.vertex_out_interfaces_.first();
/* Globals the attrib_load() can write to when it is in the fragment shader. */
attr_load << "struct " << iface.name << " {\n";
for (const auto &inout : iface.inouts) {
attr_load << " " << inout.type << " " << inout.name << ";\n";
}
attr_load << "};\n";
attr_load << iface.name << " " << iface.instance_name << ";\n";
if (!is_volume) {
/* Global vars just to make code valid. Only Orco is supported. */
for (const ShaderCreateInfo::VertIn &in : info.vertex_inputs_) {
attr_load << in.type << " " << in.name << ";\n";
}
}
info.vertex_out_interfaces_.clear();
}
if (is_volume) {
/** Volume grid attributes come from 3D textures. Transfer attributes to samplers. */
for (auto &input : info.vertex_inputs_) {
info.sampler(0, ImageType::FLOAT_3D, input.name, Frequency::BATCH);
}
info.additional_info("draw_volume_infos");
/* Do not add twice. */
if (!GPU_material_flag_get(gpumat, GPU_MATFLAG_OBJECT_INFO)) {
info.additional_info("draw_object_infos");
}
info.vertex_inputs_.clear();
}
if (is_hair) {
info.additional_info("draw_curves_infos");
}
if (!is_volume) {
info.define("EEVEE_GENERATED_INTERFACE");
}
attr_load << "void attrib_load()\n";
attr_load << "{\n";
attr_load << (!codegen.attr_load.empty() ? codegen.attr_load : "");
attr_load << "}\n\n";
std::stringstream vert_gen, frag_gen, geom_gen;
if (do_fragment_attrib_load) {
frag_gen << attr_load.str();
}
else {
vert_gen << attr_load.str();
}
{
vert_gen << vert;
info.vertex_source_generated = vert_gen.str();
/* Everything is in generated source. */
info.vertex_source(is_volume ? "eevee_empty_volume.glsl" : "eevee_empty.glsl");
}
{
frag_gen << frag;
frag_gen << codegen.material_functions;
frag_gen << "Closure nodetree_exec()\n";
frag_gen << "{\n";
if (is_volume) {
frag_gen << (!codegen.volume.empty() ? codegen.volume : "return CLOSURE_DEFAULT;\n");
}
else {
frag_gen << (!codegen.surface.empty() ? codegen.surface : "return CLOSURE_DEFAULT;\n");
}
frag_gen << "}\n\n";
if (!codegen.displacement.empty() && (is_hair || is_mesh)) {
info.define("EEVEE_DISPLACEMENT_BUMP");
frag_gen << "vec3 displacement_exec()\n";
frag_gen << "{\n";
frag_gen << codegen.displacement;
frag_gen << "}\n\n";
}
info.fragment_source_generated = frag_gen.str();
/* Everything is in generated source. */
info.fragment_source(is_volume ? "eevee_empty_volume.glsl" : "eevee_empty.glsl");
}
if (geom) {
geom_gen << geom;
info.geometry_source_generated = geom_gen.str();
info.geometry_layout(PrimitiveIn::TRIANGLES, PrimitiveOut::TRIANGLE_STRIP, 3);
/* Everything is in generated source. */
info.geometry_source("eevee_empty.glsl");
}
if (defines) {
info.typedef_source_generated += blender::StringRefNull(defines);
}
}

@ -1,411 +0,0 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup EEVEE
*/
#include "BLI_string_utils.hh"
#include "BLI_sys_types.h" /* bool */
#include "BKE_object.hh"
#include "DEG_depsgraph_query.hh"
#include "eevee_private.hh"
#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 = static_cast<EEVEE_LightsInfo *>(
MEM_callocN(sizeof(EEVEE_LightsInfo), "EEVEE_LightsInfo"));
sldata->light_ubo = GPU_uniformbuf_create_ex(
sizeof(EEVEE_Light) * MAX_LIGHT, nullptr, "evLight");
sldata->shadow_ubo = GPU_uniformbuf_create_ex(shadow_ubo_size, nullptr, "evShadow");
for (int i = 0; i < 2; i++) {
sldata->shcasters_buffers[i].bbox = static_cast<EEVEE_BoundBox *>(
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 */
std::swap(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)
{
using namespace blender;
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 = static_cast<EEVEE_BoundBox *>(
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;
/* Always update shadow buffers in sculpt modes. */
update |= ob->sculpt != nullptr;
oedata->need_update = false;
}
if (update) {
BLI_BITMAP_ENABLE(frontbuffer->update, id);
}
/* Update World AABB in frontbuffer. */
const Bounds<float3> bounds = BKE_object_boundbox_get(ob).value_or(Bounds(float3(0)));
BoundBox bb;
BKE_boundbox_init_from_minmax(&bb, bounds.min, bounds.max);
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->object_to_world().ptr(), 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;
}
eGPUTextureUsage shadow_usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_SHADER_READ;
if (!sldata->shadow_cube_pool) {
sldata->shadow_cube_pool = DRW_texture_create_2d_array_ex(
linfo->shadow_cube_size,
linfo->shadow_cube_size,
max_ii(1, linfo->num_cube_layer * 6),
shadow_pool_format,
shadow_usage,
DRWTextureFlag(DRW_TEX_FILTER | DRW_TEX_COMPARE),
nullptr);
}
if (!sldata->shadow_cascade_pool) {
sldata->shadow_cascade_pool = DRW_texture_create_2d_array_ex(
linfo->shadow_cascade_size,
linfo->shadow_cascade_size,
max_ii(1, linfo->num_cascade_layer),
shadow_pool_format,
shadow_usage,
DRWTextureFlag(DRW_TEX_FILTER | DRW_TEX_COMPARE),
nullptr);
}
if (sldata->shadow_fb == nullptr) {
sldata->shadow_fb = GPU_framebuffer_create("shadow_fb");
}
/* Gather all the light's 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 = divide_ceil_u(max_ii(1, frontbuffer->count),
SH_CASTER_ALLOC_CHUNK) *
SH_CASTER_ALLOC_CHUNK;
frontbuffer->bbox = static_cast<EEVEE_BoundBox *>(
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 /*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, DRWTextureFlag(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(), nullptr);
}
void EEVEE_shadow_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->shadow_accum_fb != nullptr) {
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);
}
}
/** \} */

@ -1,424 +0,0 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup EEVEE
*/
#include "BLI_rect.h"
#include "BLI_sys_types.h" /* bool */
#include "BKE_object.hh"
#include "eevee_private.hh"
#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, blender::float4{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_count = 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 accumulated 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_count - 1] = p[2];
if (is_persp) {
splits_end_ndc[cascade_count - 1] /= p[3];
}
}
csm_data->split_start[0] = csm_start;
csm_data->split_end[cascade_count - 1] = csm_end;
for (int c = 1; c < cascade_count; c++) {
/* View Space */
float linear_split = interpf(csm_end, csm_start, c / float(cascade_count));
float exp_split = csm_start * powf(csm_end / csm_start, c / float(cascade_count));
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_count > 1) ? csm_data->split_end[cascade_count - 2] :
csm_data->split_start[0];
csm_data->split_start[0] = interpf(
prev_split, csm_data->split_end[cascade_count - 1], cascade_fade);
/* For each cascade */
for (int c = 0; c < cascade_count; 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((const 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, true, dbg_col);
#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] == nullptr) {
view[i] = DRW_view_create(
csm_render->viewmat, csm_render->projmat[i], nullptr, nullptr, nullptr);
}
else {
DRW_view_update(view[i], csm_render->viewmat, csm_render->projmat[i], nullptr, nullptr);
}
}
}
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);
}
}

@ -1,214 +0,0 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup EEVEE
*/
#include "eevee_private.hh"
#include "BLI_math_rotation.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 / min_ff(evli->invsqrdist, evli->invsqrdist_volume));
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, blender::float4{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 / min_ff(evli->invsqrdist, evli->invsqrdist_volume)), 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] == nullptr) {
view[i] = DRW_view_create(tmp, winmat, nullptr, nullptr, nullptr);
}
else {
DRW_view_update(view[i], tmp, winmat, nullptr, nullptr);
}
}
}
/* Does a spot angle fits a single cube-face. */
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 (ELEM(evli->light_type, LA_SPOT, LAMPTYPE_SPOT_DISK) && j != 5 &&
spot_angle_fit_single_face(evli))
{
continue;
}
/* Skip +Z face. */
if (!ELEM(evli->light_type, LA_LOCAL, LAMPTYPE_OMNI_DISK) && 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);
}

@ -1,348 +0,0 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*
* Screen space subsurface scattering technique.
*/
#include "DRW_render.hh"
#include "BLI_string_utils.hh"
#include "DEG_depsgraph_query.hh"
#include "GPU_capabilities.hh"
#include "GPU_material.hh"
#include "GPU_texture.hh"
#include "eevee_private.hh"
void EEVEE_subsurface_init(EEVEE_ViewLayerData * /*sldata*/, EEVEE_Data * /*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. */
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT;
effects->sss_stencil = DRW_texture_pool_query_2d_ex(
fs_size[0], fs_size[1], GPU_DEPTH24_STENCIL8, usage, &draw_engine_eevee_type);
effects->sss_blur = DRW_texture_pool_query_2d_ex(
fs_size[0], fs_size[1], GPU_R11F_G11F_B10F, usage, &draw_engine_eevee_type);
effects->sss_irradiance = DRW_texture_pool_query_2d_ex(
fs_size[0], fs_size[1], GPU_R11F_G11F_B10F, usage, &draw_engine_eevee_type);
effects->sss_radius = DRW_texture_pool_query_2d_ex(
fs_size[0], fs_size[1], GPU_R16F, usage, &draw_engine_eevee_type);
effects->sss_albedo = DRW_texture_pool_query_2d_ex(
fs_size[0], fs_size[1], GPU_R11F_G11F_B10F, usage, &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 = nullptr;
effects->sss_blur = nullptr;
effects->sss_irradiance = nullptr;
effects->sss_radius = nullptr;
}
}
void EEVEE_subsurface_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;
const eGPUTextureFormat texture_format_light = GPU_RGBA32F;
const bool texture_created = txl->sss_accum == nullptr;
DRW_texture_ensure_fullscreen_2d(&txl->sss_accum, texture_format_light, DRWTextureFlag(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,
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;
GPUTexture *sss_tex_profile = nullptr;
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);
{
GPUSamplerState state = GPUSamplerState::default_sampler();
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, nullptr, 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, nullptr, 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, nullptr, 1);
}
}
void EEVEE_subsurface_data_render(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;
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 * /*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 != nullptr)) {
/* 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);
}
}

@ -1,428 +0,0 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*
* Temporal super sampling technique
*/
#include "DRW_render.hh"
#include "ED_screen.hh"
#include "BLI_rand.h"
#include "DEG_depsgraph_query.hh"
#include "GPU_texture.hh"
#include "eevee_private.hh"
#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 /*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()
{
float *cdf_table = static_cast<float *>(
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(nullptr, persmat, false);
DRW_view_viewmat_get(nullptr, viewmat, false);
DRW_view_winmat_get(nullptr, winmat, false);
DRW_view_winmat_get(nullptr, 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 != nullptr);
/* 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, nullptr, 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, nullptr);
sample_count = dof_sample_count * divide_ceil_u(sample_count, dof_sample_count);
return sample_count;
}
int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData * /*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 OpenGL render,
* we accumulate the redraw inside the drawing loop in eevee_draw_scene().
*/
if (DRW_state_is_viewport_image_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 != nullptr) {
wmWindowManager *wm = CTX_wm_manager(draw_ctx->evil_C);
view_is_valid = view_is_valid && (ED_screen_animation_no_scrub(wm) == nullptr);
}
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 #79970). */
if (!DRW_state_is_scene_render()) {
DRW_view_persmat_get(nullptr, 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 #79370) and shader compilation (see #78520). */
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) {
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(), nullptr);
}
}
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 {
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(nullptr, effects->prev_drw_persmat, false);
}
}

@ -1,679 +0,0 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*
* Volumetric effects rendering using frostbite approach.
*/
#include "DRW_render.hh"
#include "BLI_listbase.h"
#include "BLI_rand.h"
#include "BLI_string_utils.hh"
#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.hh"
#include "BKE_mesh.hh"
#include "BKE_modifier.hh"
#include "BKE_volume.hh"
#include "BKE_volume_render.hh"
#include "ED_screen.hh"
#include "DEG_depsgraph_query.hh"
#include "GPU_capabilities.hh"
#include "GPU_context.hh"
#include "GPU_material.hh"
#include "GPU_texture.hh"
#include "eevee_private.hh"
static struct {
GPUTexture *depth_src;
GPUTexture *dummy_zero;
GPUTexture *dummy_one;
GPUTexture *dummy_flame;
GPUTexture *dummy_scatter;
GPUTexture *dummy_transmit;
} e_data = {nullptr}; /* Engine data */
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);
/* Clamp 3D texture size based on device maximum. */
int maxSize = GPU_max_texture_3d_size();
BLI_assert(tex_size[0] <= maxSize);
tex_size[0] = tex_size[0] > maxSize ? maxSize : tex_size[0];
tex_size[1] = tex_size[1] > maxSize ? maxSize : tex_size[1];
tex_size[2] = tex_size[2] > maxSize ? maxSize : tex_size[2];
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 == nullptr) ? 0.0f : 0.95f;
/* Temporal Super sampling jitter */
const 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 != nullptr) {
wmWindowManager *wm = CTX_wm_manager(draw_ctx->evil_C);
do_taa = do_taa && (ED_screen_animation_no_scrub(wm) == nullptr);
}
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(nullptr)) {
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(nullptr);
/* 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(nullptr);
const float clip_end = DRW_view_far_distance_get(nullptr);
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};
eGPUTextureUsage dummy_usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_SHADER_READ;
e_data.dummy_scatter = DRW_texture_create_3d_ex(
1, 1, 1, GPU_RGBA8, dummy_usage, DRW_TEX_WRAP, scatter);
e_data.dummy_transmit = DRW_texture_create_3d_ex(
1, 1, 1, GPU_RGBA8, dummy_usage, 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 = nullptr;
/* 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 */
World *wo = scene->world;
if (wo != nullptr && wo->use_nodes && wo->nodetree &&
!LOOK_DEV_STUDIO_LIGHT_ENABLED(draw_ctx->v3d))
{
GPUMaterial *mat = EEVEE_material_get(vedata, scene, nullptr, wo, VAR_MAT_VOLUME);
if (mat && 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. */
grp = DRW_shgroup_volume_create_sub(nullptr, nullptr, grp, mat);
DRW_shgroup_call_procedural_triangles(grp, nullptr, common_data->vol_tex_size[2]);
effects->enabled_effects |= (EFFECT_VOLUMETRIC | EFFECT_POST_BUFFER);
}
}
if (grp == nullptr) {
/* 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, "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);
DRW_shgroup_call_procedural_triangles(grp, nullptr, common_data->vol_tex_size[2]);
}
}
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 == nullptr) {
if (ob->type == OB_VOLUME) {
ma = BKE_material_default_volume();
}
else {
return;
}
}
float size[3];
mat4_to_size(size, ob->object_to_world().ptr());
/* Check if any of the axes have 0 length. (see #69070) */
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;
GPUMaterial *mat = EEVEE_material_get(vedata, scene, ma, nullptr, mat_options);
/* If shader failed to compile or is currently compiling. */
if (mat == nullptr) {
return;
}
GPUShader *sh = GPU_material_get_shader(mat);
if (sh == nullptr) {
return;
}
/* TODO(fclem): Reuse main shading group to avoid shading binding cost just like for surface
* shaders. */
DRWShadingGroup *grp = DRW_shgroup_create(sh, vedata->psl->volumetric_objects_ps);
grp = DRW_shgroup_volume_create_sub(scene, ob, grp, mat);
if (grp == nullptr) {
return;
}
DRW_shgroup_add_material_resources(grp, mat);
/* 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);
/* 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;
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, nullptr, 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, nullptr, 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_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 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 == nullptr) {
/* Volume properties: We evaluate all volumetric objects
* and store their final properties into each froxel */
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_SHADER_READ;
txl->volume_prop_scattering = DRW_texture_create_3d_ex(tex_size[0],
tex_size[1],
tex_size[2],
GPU_R11F_G11F_B10F,
usage,
DRW_TEX_FILTER,
nullptr);
txl->volume_prop_extinction = DRW_texture_create_3d_ex(tex_size[0],
tex_size[1],
tex_size[2],
GPU_R11F_G11F_B10F,
usage,
DRW_TEX_FILTER,
nullptr);
txl->volume_prop_emission = DRW_texture_create_3d_ex(tex_size[0],
tex_size[1],
tex_size[2],
GPU_R11F_G11F_B10F,
usage,
DRW_TEX_FILTER,
nullptr);
txl->volume_prop_phase = DRW_texture_create_3d_ex(
tex_size[0], tex_size[1], tex_size[2], GPU_RG16F, usage, DRW_TEX_FILTER, nullptr);
/* Volume scattering: We compute for each froxel the
* Scattered light towards the view. We also resolve temporal
* super sampling during this stage. */
eGPUTextureUsage usage_write = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_SHADER_READ |
GPU_TEXTURE_USAGE_SHADER_WRITE;
txl->volume_scatter = DRW_texture_create_3d_ex(tex_size[0],
tex_size[1],
tex_size[2],
GPU_R11F_G11F_B10F,
usage_write,
DRW_TEX_FILTER,
nullptr);
txl->volume_transmit = DRW_texture_create_3d_ex(tex_size[0],
tex_size[1],
tex_size[2],
GPU_R11F_G11F_B10F,
usage_write,
DRW_TEX_FILTER,
nullptr);
/* Final integration: We compute for each froxel the
* amount of scattered light and extinction coefficient at this
* given depth. We use these textures as double buffer
* for the volumetric history. */
txl->volume_scatter_history = DRW_texture_create_3d_ex(tex_size[0],
tex_size[1],
tex_size[2],
GPU_R11F_G11F_B10F,
usage_write,
DRW_TEX_FILTER,
nullptr);
txl->volume_transmit_history = DRW_texture_create_3d_ex(tex_size[0],
tex_size[1],
tex_size[2],
GPU_R11F_G11F_B10F,
usage_write,
DRW_TEX_FILTER,
nullptr);
}
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);
std::swap(fbl->volumetric_scat_fb, fbl->volumetric_integ_fb);
std::swap(txl->volume_scatter, txl->volume_scatter_history);
std::swap(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 * /*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()
{
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, DRWTextureFlag(0));
DRW_texture_ensure_fullscreen_2d(
&txl->volume_transmittance_accum, texture_format_accum, DRWTextureFlag(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 = nullptr;
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(), nullptr);
}
void EEVEE_volumes_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->volumetric_accum_fb != nullptr) {
/* Accumulation 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);
}
}
/** \} */

@ -1,33 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef GPU_SHADER_EEVEE_LEGACY_DEFINES
#define GPU_SHADER_EEVEE_LEGACY_DEFINES
#ifdef GPU_SHADER
# define EEVEE_ENGINE
#endif
/* 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
/* Motion Blur. */
#define EEVEE_VELOCITY_TILE_SIZE 32
/* Depth of Field. */
#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
#endif

@ -1,483 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
#pragma BLENDER_REQUIRE(raytrace_lib.glsl)
#pragma BLENDER_REQUIRE(surface_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
#ifndef GPU_FRAGMENT_SHADER
# define gl_FragCoord vec4(0.0)
#endif
/* 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;
#ifdef GPU_METAL
/* Constructors required for OcclusionData(..) syntax. */
inline OcclusionData() = default;
inline OcclusionData(vec4 in_horizons, float in_custom_occlusion)
: horizons(in_horizons), custom_occlusion(in_custom_occlusion)
{
}
#endif
};
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));
}
/* Certain intel drivers on macOS lose precision, resulting in rendering artifacts,
* when using standard pow(A, B) function.
* Using logarithmic identity provides higher precision results. */
#if defined(GPU_INTEL) && defined(OS_MAC)
float occlusion_pow(float a, float b)
{
return exp(b * log(a));
}
#else
float occlusion_pow(float a, float b)
{
return pow(a, b);
}
#endif
/* Return horizon angle cosine. */
#if (defined(GPU_METAL) && defined(GPU_ATI))
__attribute__((noinline))
#endif
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;
int i_sample_count = int(sample_count);
for (int iter = 0; time < ssray.max_time && iter < i_sample_count; iter++) {
prev_time = time;
/* Gives us good precision at center and ensure we cross at least one pixel per iteration. */
float fl_iter = float(iter);
time = 1.0 + fl_iter + sqr((fl_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 #87369). */
float tmp = saturate(pow6(visibility));
bent_normal = normalize(mix(bent_normal, N, tmp));
}
}
/* Multi-bounce 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 = occlusion_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 = occlusion_pow(saturate(visibility), aoFactor);
return visibility;
}
/**
* Approximate the area of intersection of two spherical caps
* radius1 : First caps radius (arc length in radians)
* radius2 : Second caps radius (in radians)
* dist : Distance between caps (radians between centers of caps)
* NOTE: Result is divided by pi to save one multiply.
*/
float spherical_cap_intersection(float radius1, float radius2, float dist)
{
/* From "Ambient Aperture Lighting" by Chris Oat
* Slide 15. */
float max_radius = max(radius1, radius2);
float min_radius = min(radius1, radius2);
float sum_radius = radius1 + radius2;
float area;
if (dist <= max_radius - min_radius) {
/* One cap in completely inside the other */
area = 1.0 - cos(min_radius);
}
else if (dist >= sum_radius) {
/* No intersection exists */
area = 0;
}
else {
float diff = max_radius - min_radius;
area = smoothstep(0.0, 1.0, 1.0 - saturate((dist - diff) / (sum_radius - diff)));
area *= 1.0 - cos(min_radius);
}
return area;
}
float specular_occlusion(
OcclusionData data, vec3 V, vec3 N, float roughness, inout vec3 specular_dir)
{
vec3 visibility_dir;
float visibility_error;
float visibility;
occlusion_eval(data, V, N, N, 0.0, visibility, visibility_error, visibility_dir);
/* Correct visibility error for very sharp surfaces. */
visibility *= mix(safe_rcp(visibility_error), 1.0, roughness);
specular_dir = normalize(mix(specular_dir, visibility_dir, roughness * (1.0 - visibility)));
/* Visibility to cone angle (eq. 18). */
float vis_angle = fast_acos(sqrt(1 - visibility));
/* Roughness to cone angle (eq. 26). */
float spec_angle = max(0.00990998744964599609375, fast_acos(cone_cosine(roughness)));
/* Angle between cone axes. */
float cone_cone_dist = fast_acos(saturate(dot(visibility_dir, specular_dir)));
float cone_nor_dist = fast_acos(saturate(dot(N, specular_dir)));
float isect_solid_angle = spherical_cap_intersection(vis_angle, spec_angle, cone_cone_dist);
float specular_solid_angle = spherical_cap_intersection(M_PI_2, spec_angle, cone_nor_dist);
float specular_occlusion = isect_solid_angle / specular_solid_angle;
/* Mix because it is unstable in unoccluded areas. */
float tmp = saturate(pow8(visibility));
visibility = mix(specular_occlusion, 1.0, tmp);
/* Scale by user factor */
visibility = occlusion_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;
}
#ifndef GPU_FRAGMENT_SHADER
# undef gl_FragCoord
#endif
float ambient_occlusion_eval(vec3 normal,
float max_distance,
const float inverted,
const float sample_count)
{
/* Avoid multi-line define causing compiler issues. */
/* clang-format off */
#if defined(GPU_FRAGMENT_SHADER) && (defined(MESH_SHADER) || defined(HAIR_SHADER)) && !defined(DEPTH_SHADER) && !defined(VOLUMETRICS)
/* clang-format on */
vec3 bent_normal;
vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
OcclusionData data = occlusion_search(
viewPosition, maxzBuffer, max_distance, inverted, sample_count);
vec3 V = cameraVec(worldPosition);
vec3 N = normalize(normal);
vec3 Ng = safe_normalize(cross(dFdx(worldPosition), dFdy(worldPosition)));
float unused_error, visibility;
vec3 unused;
occlusion_eval(data, V, N, Ng, inverted, visibility, unused_error, unused);
return visibility;
#else
return 1.0;
#endif
}

@ -1,26 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(surface_lib.glsl)
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
}

@ -1,194 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#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));
}
/* 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)
{
return F0 * lut.x + F90 * lut.y;
}
/* Multi-scattering brdf approximation from
* "A Multiple-Scattering Microfacet Model for Real-Time Image-based Lighting"
* https://jcgt.org/published/0008/01/03/paper.pdf by Carmelo J. Fdez-Agüera. */
vec3 F_brdf_multi_scatter(vec3 F0, vec3 F90, vec2 lut)
{
vec3 FssEss = F_brdf_single_scatter(F0, F90, lut);
float Ess = lut.x + lut.y;
float Ems = 1.0 - Ess;
vec3 Favg = F0 + (F90 - F0) / 21.0;
/* The original paper uses `FssEss * radiance + Fms*Ems * irradiance`, but
* "A Journey Through Implementing Multi-scattering BRDFs and Area Lights" by Steve McAuley
* suggests to use `FssEss * radiance + Fms*Ems * radiance` which results in comparable quality.
* We handle `radiance` outside of this function, so the result simplifies to:
* `FssEss + Fms*Ems = FssEss * (1 + Ems*Favg / (1 - Ems*Favg)) = FssEss / (1 - Ems*Favg)`.
* This is a simple albedo scaling very similar to the approach used by Cycles:
* "Practical multiple scattering compensation for microfacet model". */
return FssEss / (1.0 - Ems * Favg);
}
/* GGX */
float bxdf_ggx_D(float NH, float a2)
{
return a2 / (M_PI * sqr((a2 - 1.0) * (NH * NH) + 1.0));
}
float D_ggx_opti(float NH, float a2)
{
float tmp = (NH * a2 - NH) * NH + 1.0;
return M_PI * tmp * tmp; /* Doing RCP and multiply a2 at the end. */
}
float bxdf_ggx_smith_G1(float NX, float a2)
{
return 2.0 / (1.0 + sqrt(1.0 + a2 * (1.0 / (NX * NX) - 1.0)));
}
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. */
}
/* Compute the GGX BRDF without the Fresnel term, multiplied by the cosine foreshortening term. */
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 = bxdf_ggx_smith_G1(NV, a2) * bxdf_ggx_smith_G1(NL, a2);
float D = bxdf_ggx_D(NH, a2);
/* brdf * NL = `((D * G) / (4 * NV * NL)) * NL`. */
return (0.25 * D * G) / NV;
}
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_specular_reflection(vec3 Ng, vec3 I, vec3 N)
{
vec3 R = -reflect(I, N);
float Iz = dot(I, Ng);
/* Reflection rays may always be at least as shallow as the incoming ray. */
float threshold = min(0.9 * Iz, 0.025);
if (dot(Ng, R) >= threshold) {
return N;
}
vec3 X = normalize(N - dot(N, Ng) * Ng);
if (any(isnan(X))) {
X = N;
}
float Ix = dot(I, X);
float a = sqr(Ix) + sqr(Iz);
float b = 2.0 * (a + Iz * threshold);
float c = sqr(threshold + Iz);
float Nz2 = (Ix < 0.0) ? 0.25 * (b + safe_sqrt(sqr(b) - 4.0 * a * c)) / a :
0.25 * (b - safe_sqrt(sqr(b) - 4.0 * a * c)) / a;
float Nx = safe_sqrt(1.0 - Nz2);
float Nz = safe_sqrt(Nz2);
return Nx * X + Nz * 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);
}

@ -1,56 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
/* Generate BRDF LUT following "Real shading in unreal engine 4" by Brian Karis
* https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
* Parametrizing with `x = roughness` and `y = sqrt(1.0 - cos(theta))`.
* The result is interpreted as: `integral = f0 * scale + f90 * bias`. */
void main()
{
/* Make sure coordinates are covering the whole [0..1] range at texel center. */
float x = floor(gl_FragCoord.x) / (LUT_SIZE - 1);
float y = floor(gl_FragCoord.y) / (LUT_SIZE - 1);
/* Squaring for perceptually linear roughness, see [Physically Based Shading at Disney]
* (https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf)
* Section 5.4. */
float roughness = x * x;
float roughness_sq = roughness * roughness;
float NV = clamp(1.0 - y * y, 1e-4, 0.9999);
vec3 V = vec3(sqrt(1.0 - NV * NV), 0.0, NV);
/* Integrating BRDF */
float scale = 0.0;
float bias = 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, roughness, V);
vec3 L = -reflect(V, H);
float NL = L.z;
if (NL > 0.0) {
/* Assuming sample visible normals, `weight = brdf * NV / (pdf * fresnel).` */
float weight = bxdf_ggx_smith_G1(NL, roughness_sq);
/* Schlick's Fresnel. */
float s = pow(1.0 - saturate(dot(V, H)), 5.0);
scale += (1.0 - s) * weight;
bias += s * weight;
}
}
}
scale /= sampleCount * sampleCount;
bias /= sampleCount * sampleCount;
FragColor = vec2(scale, bias);
}

@ -1,126 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2021 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Sampling distribution routines for Monte-carlo integration.
*/
#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl)
/* -------------------------------------------------------------------- */
/** \name Microfacet GGX distribution
* \{ */
#define USE_VISIBLE_NORMAL 1
float pdf_ggx_reflect(float NH, float NV, float VH, float alpha)
{
float a2 = sqr(alpha);
#if USE_VISIBLE_NORMAL
float D = bxdf_ggx_D(NH, a2);
float G1 = bxdf_ggx_smith_G1(NV, a2);
return 0.25 * D * G1 / NV;
#else
return NH * a2 / D_ggx_opti(NH, a2);
#endif
}
vec3 sample_ggx(vec3 rand, float alpha, vec3 Vt)
{
#if USE_VISIBLE_NORMAL
/* From:
* "A Simpler and Exact Sampling Routine for the GGXDistribution of Visible Normals"
* by Eric Heitz.
* http://jcgt.org/published/0007/04/01/slides.pdf
* View vector is expected to be in tangent space. */
/* Stretch view. */
vec3 Th, Bh, Vh = normalize(vec3(alpha * Vt.xy, Vt.z));
make_orthonormal_basis(Vh, Th, Bh);
/* Sample point with polar coordinates (r, phi). */
float r = sqrt(rand.x);
float x = r * rand.y;
float y = r * rand.z;
float s = 0.5 * (1.0 + Vh.z);
y = (1.0 - s) * sqrt(1.0 - x * x) + s * y;
float z = sqrt(saturate(1.0 - x * x - y * y));
/* Compute normal. */
vec3 Hh = x * Th + y * Bh + z * Vh;
/* Unstretch. */
vec3 Ht = normalize(vec3(alpha * Hh.xy, saturate(Hh.z)));
/* Microfacet Normal. */
return Ht;
#else
/* Theta is the cone angle. */
float z = sqrt((1.0 - rand.x) / (1.0 + sqr(alpha) * rand.x - 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;
/* Microfacet Normal */
return vec3(x, y, z);
#endif
}
vec3 sample_ggx(vec3 rand, float alpha, vec3 V, vec3 N, vec3 T, vec3 B, out float pdf)
{
vec3 Vt = world_to_tangent(V, N, T, B);
vec3 Ht = sample_ggx(rand, alpha, Vt);
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);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Uniform Hemisphere
* \{ */
float pdf_uniform_hemisphere()
{
return 0.5 * M_1_PI;
}
vec3 sample_uniform_hemisphere(vec3 rand)
{
float z = 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_uniform_hemisphere(vec3 rand, vec3 N, vec3 T, vec3 B, out float pdf)
{
vec3 Ht = sample_uniform_hemisphere(rand);
pdf = pdf_uniform_hemisphere();
return tangent_to_world(Ht, N, T, B);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Uniform Cone sampling
* \{ */
vec3 sample_uniform_cone(vec3 rand, float angle)
{
float z = cos(angle * 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_uniform_cone(vec3 rand, float angle, vec3 N, vec3 T, vec3 B)
{
vec3 Ht = sample_uniform_cone(rand, angle);
/* TODO: pdf? */
return tangent_to_world(Ht, N, T, B);
}
/** \} */

@ -1,72 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
/* Generate BSDF LUT for `IOR < 1`. Returns the integrated BTDF and BRDF, multiplied by the cosine
* foreshortening factor. */
void main()
{
/* Make sure coordinates are covering the whole [0..1] range at texel center. */
float x = floor(gl_FragCoord.x) / (LUT_SIZE - 1.0);
float y = floor(gl_FragCoord.y) / (LUT_SIZE - 1.0);
float ior = sqrt(x);
/* 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);
/* Squaring for perceptually linear roughness, see [Physically Based Shading at Disney]
* (https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf)
* Section 5.4. */
float roughness = z_factor * z_factor;
float roughness_sq = roughness * roughness;
vec3 V = vec3(sqrt(1.0 - NV * NV), 0.0, NV);
/* Integrating BSDF */
float btdf = 0.0;
float brdf = 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, roughness, V);
float fresnel = F_eta(ior, dot(V, H));
/* Reflection. */
vec3 R = -reflect(V, H);
float NR = R.z;
if (NR > 0.0) {
/* Assuming sample visible normals, accumulating `brdf * NV / pdf.` */
brdf += fresnel * bxdf_ggx_smith_G1(NR, roughness_sq);
}
/* Refraction. */
vec3 T = refract(-V, H, ior);
float NT = T.z;
/* In the case of TIR, `T == vec3(0)`. */
if (NT < 0.0) {
/* Assuming sample visible normals, accumulating `btdf * NV / pdf.` */
btdf += (1.0 - fresnel) * bxdf_ggx_smith_G1(NT, roughness_sq);
}
}
}
btdf /= sampleCount * sampleCount;
brdf /= sampleCount * sampleCount;
/* 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, brdf, 0.0, 1.0);
}

@ -1,103 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(lights_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
#pragma BLENDER_REQUIRE(renderpass_lib.glsl)
struct ClosureInputDiffuse {
vec3 N; /** Shading normal. */
vec3 albedo; /** Used for multi-bounce GTAO approximation. Not applied to final radiance. */
};
#ifdef GPU_METAL
/* C++ struct initialization. */
# define CLOSURE_INPUT_Diffuse_DEFAULT \
{ \
vec3(0.0), vec3(0.0) \
}
#else
# define CLOSURE_INPUT_Diffuse_DEFAULT ClosureInputDiffuse(vec3(0.0), vec3(0.0))
#endif
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 cube-map
* 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)
{
cl_out.radiance = render_pass_diffuse_mask(cl_out.radiance);
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see #59747) */
cl_out.radiance = vec3(0.0);
return;
#endif
}

@ -1,175 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#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)
#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
#pragma BLENDER_REQUIRE(renderpass_lib.glsl)
struct ClosureInputGlossy {
vec3 N; /** Shading normal. */
float roughness; /** Input roughness, not squared. */
};
#ifdef GPU_METAL
# define CLOSURE_INPUT_Glossy_DEFAULT \
{ \
vec3(0.0), 0.0 \
}
#else
# define CLOSURE_INPUT_Glossy_DEFAULT ClosureInputGlossy(vec3(0.0), 0.0)
#endif
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; /** Ray-trace 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. */
# ifndef GPU_METAL
/* MSL does not require prototypes. */
void raytrace_resolve(ClosureInputGlossy cl_in,
inout ClosureEvalGlossy cl_eval,
inout ClosureEvalCommon cl_common,
inout ClosureOutputGlossy cl_out);
# endif
#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_specular_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. */
cl_eval.ltc_brdf_scale = texture(utilTex, vec3(lut_uv, LTC_BRDF_LAYER)).g;
return cl_eval;
}
void closure_Glossy_light_eval(ClosureInputGlossy cl_in,
ClosureEvalGlossy cl_eval,
ClosureEvalCommon cl_common,
ClosureLightData light,
inout ClosureOutputGlossy cl_out)
{
/* Ensure specular light contribution only gets applied once when running split pass */
#ifndef RESOLVE_SSR
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);
#endif
}
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)
{
/* Ensure cube-map probes contribution only gets applied once when running split pass */
#ifndef RESOLVE_SSR
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;
#endif
}
void closure_Glossy_indirect_end(ClosureInputGlossy cl_in,
ClosureEvalGlossy cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputGlossy cl_out)
{
/* Ensure specular contribution only gets applied once when running split pass */
#ifndef RESOLVE_SSR
/* If not enough light has been accumulated from probes, use the world specular cube-map
* 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;
#endif
/* Apply Ray-trace reflections after occlusion since they are direct, local reflections. */
#if defined(RESOLVE_PROBE)
/* NO OP - output base radiance. */
#elif defined(RESOLVE_SSR)
/* Output only ray-trace radiance. */
cl_out.radiance = cl_eval.raytrace_radiance;
#else
/* Standard resolve */
cl_out.radiance += cl_eval.raytrace_radiance;
#endif
}
void closure_Glossy_eval_end(ClosureInputGlossy cl_in,
ClosureEvalGlossy cl_eval,
ClosureEvalCommon cl_common,
inout ClosureOutputGlossy cl_out)
{
cl_out.radiance = render_pass_glossy_mask(cl_out.radiance);
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see #59747) */
cl_out.radiance = vec3(0.0);
return;
#endif
if (!specToggle) {
cl_out.radiance = vec3(0.0);
}
}

@ -1,357 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
// #pragma (gpu_shader_codegen_lib.glsl)
#pragma BLENDER_REQUIRE(lights_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#ifndef GPU_FRAGMENT_SHADER
# define gl_FragCoord vec4(0.0)
# define gl_FrontFacing true
#endif
/**
* 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 cube-map. */ \
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
#ifdef GPU_METAL
/* C++ struct initialization. */
# define CLOSURE_EVAL_DUMMY \
{ \
vec3(0) \
}
#else
# define CLOSURE_EVAL_DUMMY ClosureOutput(vec3(0))
#endif
#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;
};
#ifdef GPU_METAL
/* C++ struct initialization. */
# define CLOSURE_INPUT_COMMON_DEFAULT \
{ \
1.0 \
}
#else
# define CLOSURE_INPUT_COMMON_DEFAULT ClosureInputCommon(1.0)
#endif
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. (view-space) */
vec3 vN;
/** Surface position. (view-space) */
vec3 vP;
/** Geometric normal, always facing camera. */
vec3 Ng;
/** Geometric normal, always facing camera. (view-space) */
vec3 vNg;
/** Random numbers. 3 random sequences. `zw` is a random point on a circle. */
vec4 rand;
/** Specular probe accumulator. Shared between planar and cube-map probe. */
float specular_accum;
/** Diffuse probe accumulator. */
float diffuse_accum;
};
/* Common cl_out struct used by most closures. */
struct ClosureOutput {
vec3 radiance;
};
/* Workaround for screen-space 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;
#ifdef GPU_FRAGMENT_SHADER
cl_eval.Ng = safe_normalize(cross(dFdx(cl_eval.P), dFdy(cl_eval.P)));
#else
cl_eval.Ng = cl_eval.N;
#endif
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;
}
/** \} */
#ifndef GPU_FRAGMENT_SHADER
# undef gl_FragCoord
# undef gl_FrontFacing
#endif

@ -1,141 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#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)
#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
#pragma BLENDER_REQUIRE(renderpass_lib.glsl)
struct ClosureInputRefraction {
vec3 N; /** Shading normal. */
float roughness; /** Input roughness, not squared. */
float ior; /** Index of refraction ratio. */
};
#ifdef GPU_METAL
/* C++ struct initialization. */
# define CLOSURE_INPUT_Refraction_DEFAULT \
{ \
vec3(0.0), 0.0, 0.0 \
}
#else
# define CLOSURE_INPUT_Refraction_DEFAULT ClosureInputRefraction(vec3(0.0), 0.0, 0.0)
#endif
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 cube-map
* 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)
{
cl_out.radiance = render_pass_glossy_mask(cl_out.radiance);
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see #59747) */
cl_out.radiance = vec3(0.0);
return;
#endif
if (!specToggle) {
cl_out.radiance = vec3(0.0);
}
}

@ -1,400 +0,0 @@
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(closure_eval_diffuse_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_refraction_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_translucent_lib.glsl)
#pragma BLENDER_REQUIRE(renderpass_lib.glsl)
#if defined(USE_SHADER_TO_RGBA) || defined(USE_ALPHA_BLEND)
bool do_sss = false;
bool do_ssr = false;
#else
bool do_sss = true;
bool do_ssr = true;
#endif
vec3 out_sss_radiance;
vec3 out_sss_color;
float out_sss_radius;
float out_ssr_roughness;
vec3 out_ssr_color;
vec3 out_ssr_N;
bool aov_is_valid = false;
vec3 out_aov;
bool output_sss(ClosureSubsurface diffuse, ClosureOutputDiffuse diffuse_out)
{
if (diffuse.sss_radius.b == -1.0 || !do_sss || !sssToggle || outputSssId == 0) {
return false;
}
if (renderPassSSSColor) {
return false;
}
out_sss_radiance = diffuse_out.radiance;
out_sss_color = diffuse.color * diffuse.weight;
out_sss_radius = avg(diffuse.sss_radius);
do_sss = false;
return true;
}
bool output_ssr(ClosureReflection reflection)
{
if (!do_ssr || !ssrToggle || outputSsrId == 0) {
return false;
}
out_ssr_roughness = reflection.roughness;
out_ssr_color = reflection.color * reflection.weight;
out_ssr_N = reflection.N;
do_ssr = false;
return true;
}
void output_aov(vec4 color, float value, uint hash)
{
/* Keep in sync with `render_pass_aov_hash` and `EEVEE_renderpasses_aov_hash`. */
hash <<= 1u;
if (renderPassAOV && !aov_is_valid && hash == render_pass_aov_hash()) {
aov_is_valid = true;
if (render_pass_aov_is_color()) {
out_aov = color.rgb;
}
else {
out_aov = vec3(value);
}
}
}
/* Single BSDFs. */
CLOSURE_EVAL_FUNCTION_DECLARE_1(DiffuseBSDF, Diffuse)
Closure closure_eval(ClosureDiffuse diffuse)
{
/* Glue with the old system. */
CLOSURE_VARS_DECLARE_1(Diffuse);
in_Diffuse_0.N = diffuse.N;
in_Diffuse_0.albedo = diffuse.color;
CLOSURE_EVAL_FUNCTION_1(DiffuseBSDF, Diffuse);
Closure closure = CLOSURE_DEFAULT;
closure.radiance += out_Diffuse_0.radiance * diffuse.color * diffuse.weight;
return closure;
}
/* NOTE: Reuse the diffuse eval function. */
Closure closure_eval(ClosureSubsurface subsurface)
{
/* Glue with the old system. */
CLOSURE_VARS_DECLARE_1(Diffuse);
in_Diffuse_0.N = subsurface.N;
in_Diffuse_0.albedo = subsurface.color;
CLOSURE_EVAL_FUNCTION_1(DiffuseBSDF, Diffuse);
Closure closure = CLOSURE_DEFAULT;
if (!output_sss(subsurface, out_Diffuse_0)) {
closure.radiance += out_Diffuse_0.radiance * subsurface.color * subsurface.weight;
}
return closure;
}
CLOSURE_EVAL_FUNCTION_DECLARE_1(TranslucentBSDF, Translucent)
Closure closure_eval(ClosureTranslucent translucent)
{
/* Glue with the old system. */
CLOSURE_VARS_DECLARE_1(Translucent);
in_Translucent_0.N = -translucent.N;
CLOSURE_EVAL_FUNCTION_1(TranslucentBSDF, Translucent);
Closure closure = CLOSURE_DEFAULT;
closure.radiance += out_Translucent_0.radiance * translucent.color * translucent.weight;
return closure;
}
CLOSURE_EVAL_FUNCTION_DECLARE_1(GlossyBSDF, Glossy)
Closure closure_eval(ClosureReflection reflection, const bool do_output_ssr)
{
/* Glue with the old system. */
CLOSURE_VARS_DECLARE_1(Glossy);
in_Glossy_0.N = reflection.N;
in_Glossy_0.roughness = reflection.roughness;
CLOSURE_EVAL_FUNCTION_1(GlossyBSDF, Glossy);
Closure closure = CLOSURE_DEFAULT;
bool output_radiance = true;
if (do_output_ssr) {
output_radiance = !output_ssr(reflection);
}
if (output_radiance) {
closure.radiance += out_Glossy_0.radiance * reflection.color * reflection.weight;
}
return closure;
}
Closure closure_eval(ClosureReflection reflection)
{
return closure_eval(reflection, true);
}
CLOSURE_EVAL_FUNCTION_DECLARE_1(RefractionBSDF, Refraction)
Closure closure_eval(ClosureRefraction refraction)
{
/* Glue with the old system. */
CLOSURE_VARS_DECLARE_1(Refraction);
in_Refraction_0.N = refraction.N;
in_Refraction_0.roughness = refraction.roughness;
in_Refraction_0.ior = refraction.ior;
CLOSURE_EVAL_FUNCTION_1(RefractionBSDF, Refraction);
Closure closure = CLOSURE_DEFAULT;
closure.radiance += out_Refraction_0.radiance * refraction.color * refraction.weight;
return closure;
}
Closure closure_eval(ClosureEmission emission)
{
Closure closure = CLOSURE_DEFAULT;
closure.radiance += render_pass_emission_mask(emission.emission) * emission.weight;
return closure;
}
Closure closure_eval(ClosureTransparency transparency)
{
Closure closure = CLOSURE_DEFAULT;
closure.transmittance += transparency.transmittance * transparency.weight;
closure.holdout += transparency.holdout * transparency.weight;
return closure;
}
/* Glass BSDF. */
CLOSURE_EVAL_FUNCTION_DECLARE_2(GlassBSDF, Glossy, Refraction)
Closure closure_eval(ClosureReflection reflection, ClosureRefraction refraction)
{
#if defined(DO_SPLIT_CLOSURE_EVAL)
Closure closure = closure_eval(refraction);
Closure closure_reflection = closure_eval(reflection);
closure.radiance += closure_reflection.radiance;
return closure;
#else
/* Glue with the old system. */
CLOSURE_VARS_DECLARE_2(Glossy, Refraction);
in_Glossy_0.N = reflection.N;
in_Glossy_0.roughness = reflection.roughness;
in_Refraction_1.N = refraction.N;
in_Refraction_1.roughness = refraction.roughness;
in_Refraction_1.ior = refraction.ior;
CLOSURE_EVAL_FUNCTION_2(GlassBSDF, Glossy, Refraction);
Closure closure = CLOSURE_DEFAULT;
closure.radiance += out_Refraction_1.radiance * refraction.color * refraction.weight;
if (!output_ssr(reflection)) {
closure.radiance += out_Glossy_0.radiance * reflection.color * reflection.weight;
}
return closure;
#endif
}
/* Dielectric BSDF */
CLOSURE_EVAL_FUNCTION_DECLARE_2(DielectricBSDF, Diffuse, Glossy)
Closure closure_eval(ClosureSubsurface diffuse, ClosureReflection reflection)
{
#if defined(DO_SPLIT_CLOSURE_EVAL)
Closure closure = closure_eval(diffuse);
Closure closure_reflection = closure_eval(reflection);
closure.radiance += closure_reflection.radiance;
return closure;
#else
/* Glue with the old system. */
CLOSURE_VARS_DECLARE_2(Diffuse, Glossy);
in_common.occlusion = 1.0;
in_Diffuse_0.N = diffuse.N;
in_Diffuse_0.albedo = diffuse.color;
in_Glossy_1.N = reflection.N;
in_Glossy_1.roughness = reflection.roughness;
CLOSURE_EVAL_FUNCTION_2(DielectricBSDF, Diffuse, Glossy);
Closure closure = CLOSURE_DEFAULT;
if (!output_sss(diffuse, out_Diffuse_0)) {
closure.radiance += out_Diffuse_0.radiance * diffuse.color * diffuse.weight;
}
if (!output_ssr(reflection)) {
closure.radiance += out_Glossy_1.radiance * reflection.color * reflection.weight;
}
return closure;
#endif
}
/* Specular BSDF */
CLOSURE_EVAL_FUNCTION_DECLARE_3(SpecularBSDF, Diffuse, Glossy, Glossy)
Closure closure_eval(ClosureSubsurface diffuse,
ClosureReflection reflection,
ClosureReflection coat)
{
#if defined(DO_SPLIT_CLOSURE_EVAL)
Closure closure = closure_eval(diffuse);
Closure closure_reflection = closure_eval(reflection);
Closure closure_coat = closure_eval(coat, false);
closure.radiance += closure_reflection.radiance + closure_coat.radiance;
return closure;
#else
/* Glue with the old system. */
CLOSURE_VARS_DECLARE_3(Diffuse, Glossy, Glossy);
in_common.occlusion = 1.0;
in_Diffuse_0.N = diffuse.N;
in_Diffuse_0.albedo = diffuse.color;
in_Glossy_1.N = reflection.N;
in_Glossy_1.roughness = reflection.roughness;
in_Glossy_2.N = coat.N;
in_Glossy_2.roughness = coat.roughness;
CLOSURE_EVAL_FUNCTION_3(SpecularBSDF, Diffuse, Glossy, Glossy);
Closure closure = CLOSURE_DEFAULT;
if (!output_sss(diffuse, out_Diffuse_0)) {
closure.radiance += out_Diffuse_0.radiance * diffuse.color * diffuse.weight;
}
closure.radiance += out_Glossy_2.radiance * coat.color * coat.weight;
if (!output_ssr(reflection)) {
closure.radiance += out_Glossy_1.radiance * reflection.color * reflection.weight;
}
return closure;
#endif
}
/* Principled BSDF */
CLOSURE_EVAL_FUNCTION_DECLARE_4(PrincipledBSDF, Diffuse, Glossy, Glossy, Refraction)
Closure closure_eval(ClosureSubsurface diffuse,
ClosureReflection reflection,
ClosureReflection coat,
ClosureRefraction refraction)
{
#if defined(DO_SPLIT_CLOSURE_EVAL)
Closure closure = closure_eval(diffuse);
Closure closure_reflection = closure_eval(reflection);
Closure closure_coat = closure_eval(coat, false);
Closure closure_refraction = closure_eval(refraction);
closure.radiance += closure_reflection.radiance + closure_coat.radiance +
closure_refraction.radiance;
return closure;
#else
/* Glue with the old system. */
CLOSURE_VARS_DECLARE_4(Diffuse, Glossy, Glossy, Refraction);
in_Diffuse_0.N = diffuse.N;
in_Diffuse_0.albedo = diffuse.color;
in_Glossy_1.N = reflection.N;
in_Glossy_1.roughness = reflection.roughness;
in_Glossy_2.N = coat.N;
in_Glossy_2.roughness = coat.roughness;
in_Refraction_3.N = refraction.N;
in_Refraction_3.roughness = refraction.roughness;
in_Refraction_3.ior = refraction.ior;
CLOSURE_EVAL_FUNCTION_4(PrincipledBSDF, Diffuse, Glossy, Glossy, Refraction);
Closure closure = CLOSURE_DEFAULT;
closure.radiance += out_Glossy_2.radiance * coat.color * coat.weight;
closure.radiance += out_Refraction_3.radiance * refraction.color * refraction.weight;
if (!output_sss(diffuse, out_Diffuse_0)) {
closure.radiance += out_Diffuse_0.radiance * diffuse.color * diffuse.weight;
}
if (!output_ssr(reflection)) {
closure.radiance += out_Glossy_1.radiance * reflection.color * reflection.weight;
}
return closure;
#endif
}
CLOSURE_EVAL_FUNCTION_DECLARE_2(PrincipledBSDFMetalClearCoat, Glossy, Glossy)
Closure closure_eval(ClosureReflection reflection, ClosureReflection coat)
{
#if defined(DO_SPLIT_CLOSURE_EVAL)
Closure closure = closure_eval(coat);
Closure closure_reflection = closure_eval(reflection);
closure.radiance += closure_reflection.radiance;
return closure;
#else
/* Glue with the old system. */
CLOSURE_VARS_DECLARE_2(Glossy, Glossy);
in_Glossy_0.N = reflection.N;
in_Glossy_0.roughness = reflection.roughness;
in_Glossy_1.N = coat.N;
in_Glossy_1.roughness = coat.roughness;
CLOSURE_EVAL_FUNCTION_2(PrincipledBSDFMetalClearCoat, Glossy, Glossy);
Closure closure = CLOSURE_DEFAULT;
closure.radiance += out_Glossy_1.radiance * coat.color * coat.weight;
if (!output_ssr(reflection)) {
closure.radiance += out_Glossy_0.radiance * reflection.color * reflection.weight;
}
return closure;
#endif
}
/* Not supported for surface shaders. */
Closure closure_eval(ClosureVolumeScatter volume_scatter)
{
return CLOSURE_DEFAULT;
}
Closure closure_eval(ClosureVolumeAbsorption volume_absorption)
{
return CLOSURE_DEFAULT;
}
Closure closure_eval(ClosureVolumeScatter volume_scatter,
ClosureVolumeAbsorption volume_absorption,
ClosureEmission emission)
{
return CLOSURE_DEFAULT;
}
/* Not implemented yet. */
Closure closure_eval(ClosureHair hair)
{
return CLOSURE_DEFAULT;
}
vec4 closure_to_rgba(Closure closure)
{
return vec4(closure.radiance, 1.0 - saturate(avg(closure.transmittance)));
}
Closure closure_add(inout Closure cl1, inout Closure cl2)
{
Closure cl;
cl.radiance = cl1.radiance + cl2.radiance;
cl.transmittance = cl1.transmittance + cl2.transmittance;
cl.holdout = cl1.holdout + cl2.holdout;
/* Make sure each closure is only added once to the result. */
cl1 = CLOSURE_DEFAULT;
cl2 = CLOSURE_DEFAULT;
return cl;
}
Closure closure_mix(inout Closure cl1, inout Closure cl2, float fac)
{
/* Weights have already been applied. */
return closure_add(cl1, cl2);
}

@ -1,83 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#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(closure_eval_lib.glsl)
#pragma BLENDER_REQUIRE(renderpass_lib.glsl)
struct ClosureInputTranslucent {
vec3 N; /** Shading normal. */
};
#ifdef GPU_METAL
/* C++ struct initialization. */
# define CLOSURE_INPUT_Translucent_DEFAULT \
{ \
vec3(0.0) \
}
#else
# define CLOSURE_INPUT_Translucent_DEFAULT ClosureInputTranslucent(vec3(0.0))
#endif
/* 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 cube-map
* 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)
{
cl_out.radiance = render_pass_diffuse_mask(cl_out.radiance);
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see #59747) */
cl_out.radiance = vec3(0.0);
return;
#endif
}

@ -1,114 +0,0 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
void output_aov(vec4 color, float value, uint hash)
{
/* Unsupported. */
}
/* Surface BSDFs. */
Closure closure_eval(ClosureDiffuse diffuse)
{
return CLOSURE_DEFAULT;
}
Closure closure_eval(ClosureTranslucent translucent)
{
return CLOSURE_DEFAULT;
}
Closure closure_eval(ClosureReflection reflection)
{
return CLOSURE_DEFAULT;
}
Closure closure_eval(ClosureRefraction refraction)
{
return CLOSURE_DEFAULT;
}
Closure closure_eval(ClosureEmission emission)
{
Closure closure = CLOSURE_DEFAULT;
closure.emission = emission.emission;
return closure;
}
Closure closure_eval(ClosureTransparency transparency)
{
return CLOSURE_DEFAULT;
}
Closure closure_eval(ClosureReflection reflection, ClosureRefraction refraction)
{
return CLOSURE_DEFAULT;
}
Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection)
{
return CLOSURE_DEFAULT;
}
Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection, ClosureReflection coat)
{
return CLOSURE_DEFAULT;
}
Closure closure_eval(ClosureDiffuse diffuse,
ClosureReflection reflection,
ClosureReflection coat,
ClosureRefraction refraction)
{
return CLOSURE_DEFAULT;
}
Closure closure_eval(ClosureReflection reflection, ClosureReflection coat)
{
return CLOSURE_DEFAULT;
}
Closure closure_eval(ClosureHair hair)
{
return CLOSURE_DEFAULT;
}
Closure closure_eval(ClosureVolumeScatter volume_scatter)
{
Closure closure = CLOSURE_DEFAULT;
closure.scatter = volume_scatter.scattering;
closure.anisotropy = volume_scatter.anisotropy;
return closure;
}
Closure closure_eval(ClosureVolumeAbsorption volume_absorption)
{
Closure closure = CLOSURE_DEFAULT;
closure.absorption = volume_absorption.absorption;
return closure;
}
Closure closure_eval(ClosureVolumeScatter volume_scatter,
ClosureVolumeAbsorption volume_absorption,
ClosureEmission emission)
{
Closure closure = CLOSURE_DEFAULT;
closure.absorption = volume_absorption.absorption;
closure.scatter = volume_scatter.scattering;
closure.anisotropy = volume_scatter.anisotropy;
closure.emission = emission.emission;
return closure;
}
vec4 closure_to_rgba(Closure closure)
{
/* Not supported */
return vec4(0.0);
}
Closure closure_mix(inout Closure cl1, inout 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(inout Closure cl1, inout 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;
}

@ -1,127 +0,0 @@
/* SPDX-FileCopyrightText: 2020-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
/* #pragma (common_math_geom_lib.glsl) */
/* #pragma (common_uniforms_lib.glsl) */
/* #pragma (renderpass_lib.glsl) */
struct Closure {
#ifdef VOLUMETRICS
vec3 absorption;
vec3 scatter;
vec3 emission;
float anisotropy;
#else /* SURFACE */
vec3 radiance;
vec3 transmittance;
float holdout;
#endif
/* Metal Default Constructor - Required for C++ constructor syntax. */
#ifdef GPU_METAL
inline Closure() = default;
# ifdef VOLUMETRICS
/* Explicit Closure constructors -- To support GLSL syntax */
inline Closure(vec3 in_absorption, vec3 in_scatter, vec3 in_emission, float in_anisotropy)
: absorption(in_absorption),
scatter(in_scatter),
emission(in_emission),
anisotropy(in_anisotropy)
{
}
# else
/* Explicit Closure constructors -- To support GLSL syntax */
inline Closure(vec3 in_radiance, vec3 in_transmittance, float in_holdout)
: radiance(in_radiance), transmittance(in_transmittance), holdout(in_holdout)
{
}
# endif /* VOLUMETRICS */
#endif /* GPU_METAL */
};
#ifndef GPU_METAL
/* Prototype */
Closure nodetree_exec();
vec4 closure_to_rgba(Closure cl);
void output_aov(vec4 color, float value, uint hash);
vec3 coordinate_camera(vec3 P);
vec3 coordinate_screen(vec3 P);
vec3 coordinate_reflect(vec3 P, vec3 N);
vec3 coordinate_incoming(vec3 P);
float film_scaling_factor_get();
/* Single BSDFs. */
Closure closure_eval(ClosureDiffuse diffuse);
Closure closure_eval(ClosureSubsurface diffuse);
Closure closure_eval(ClosureTranslucent translucent);
Closure closure_eval(ClosureReflection reflection);
Closure closure_eval(ClosureRefraction refraction);
Closure closure_eval(ClosureEmission emission);
Closure closure_eval(ClosureTransparency transparency);
Closure closure_eval(ClosureVolumeScatter volume_scatter);
Closure closure_eval(ClosureVolumeAbsorption volume_absorption);
Closure closure_eval(ClosureHair hair);
/* Glass BSDF. */
Closure closure_eval(ClosureReflection reflection, ClosureRefraction refraction);
/* Dielectric BSDF. */
Closure closure_eval(ClosureSubsurface diffuse, ClosureReflection reflection);
/* Coat BSDF. */
Closure closure_eval(ClosureReflection reflection, ClosureReflection coat);
/* Volume BSDF. */
Closure closure_eval(ClosureVolumeScatter volume_scatter,
ClosureVolumeAbsorption volume_absorption,
ClosureEmission emission);
/* Specular BSDF. */
Closure closure_eval(ClosureSubsurface diffuse,
ClosureReflection reflection,
ClosureReflection coat);
/* Principled BSDF. */
Closure closure_eval(ClosureSubsurface diffuse,
ClosureReflection reflection,
ClosureReflection coat,
ClosureRefraction refraction);
Closure closure_add(inout Closure cl1, inout Closure cl2);
Closure closure_mix(inout Closure cl1, inout Closure cl2, float fac);
float ambient_occlusion_eval(vec3 normal,
float distance,
const float inverted,
const float sample_count);
/* WORKAROUND: Included later with libraries. This is because we are mixing include systems. */
vec3 safe_normalize(vec3 N);
float fast_sqrt(float a);
vec3 cameraVec(vec3 P);
vec2 bsdf_lut(float a, float b, float c, bool d);
void bsdf_lut(vec3 F0,
vec3 F90,
vec3 transmission_tint,
float cos_theta,
float roughness,
float ior,
bool do_multiscatter,
out vec3 reflectance,
out vec3 transmittance);
vec2 brdf_lut(float a, float b);
void brdf_f82_tint_lut(vec3 F0,
vec3 F82,
float cos_theta,
float roughness,
bool do_multiscatter,
out vec3 reflectance);
vec3 F_brdf_multi_scatter(vec3 a, vec3 b, vec2 c);
vec3 F_brdf_single_scatter(vec3 a, vec3 b, vec2 c);
float F_eta(float a, float b);
float F0_from_ior(float a);
#endif
#ifdef VOLUMETRICS
# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), vec3(0), 0.0)
#else
# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), 0.0)
#endif

@ -1,87 +0,0 @@
/* SPDX-FileCopyrightText: 2018-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#define COMMON_UNIFORMS_LIB
#if !defined(USE_GPU_SHADER_CREATE_INFO)
/* keep in sync with CommonUniformBlock */
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;
bool32_t volUseLights;
bool32_t volUseSoftShadows;
/* Screen Space Reflections */
vec4 ssrParameters;
float ssrBorderFac;
float ssrMaxRoughness;
float ssrFireflyFac;
float ssrBrdfBias;
bool32_t ssrToggle;
bool32_t ssrefractToggle;
/* SubSurface Scattering */
float sssJitterThreshold;
bool32_t sssToggle;
/* Specular */
bool32_t 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;
/* Misc */
vec4 cameraUvScaleBias;
vec4 planarClipPlane;
};
#endif /* !USE_GPU_SHADER_CREATE_INFO */
#ifdef USE_GPU_SHADER_CREATE_INFO
# ifndef EEVEE_SHADER_SHARED_H
# error Missing eevee_legacy_common_lib additional create info on shader create info
# endif
#endif
/* 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

@ -1,205 +0,0 @@
/* SPDX-FileCopyrightText: 2020-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#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.
* \{ */
#if !defined(USE_GPU_SHADER_CREATE_INFO)
uniform sampler2DArray utilTex;
#endif
#define LUT_SIZE 64
#define LTC_MAT_LAYER 0
#define LTC_BRDF_LAYER 3
#define LTC_DISK_LAYER 3
#define BRDF_LUT_LAYER 1
#define NOISE_LAYER 2
/* Layers 4 to 20 are for BTDF LUT. */
#define lut_btdf_layer_first 4.0
#define 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;
}
void brdf_f82_tint_lut(vec3 F0,
vec3 F82,
float cos_theta,
float roughness,
bool do_multiscatter,
out vec3 reflectance)
{
vec2 uv = lut_coords(cos_theta, roughness);
vec3 split_sum = textureLod(utilTex, vec3(uv, BRDF_LUT_LAYER), 0.0).rgb;
reflectance = do_multiscatter ? F_brdf_multi_scatter(F0, vec3(1.0), split_sum.xy) :
F_brdf_single_scatter(F0, vec3(1.0), split_sum.xy);
/* Precompute the F82 term factor for the Fresnel model.
* In the classic F82 model, the F82 input directly determines the value of the Fresnel
* model at ~82°, similar to F0 and F90.
* With F82-Tint, on the other hand, the value at 82° is the value of the classic Schlick
* model multiplied by the tint input.
* Therefore, the factor follows by setting `F82Tint(cosI) = FSchlick(cosI) - b*cosI*(1-cosI)^6`
* and `F82Tint(acos(1/7)) = FSchlick(acos(1/7)) * f82_tint` and solving for `b`. */
const float f = 6.0 / 7.0;
const float f5 = (f * f) * (f * f) * f;
const float f6 = (f * f) * (f * f) * (f * f);
vec3 F_schlick = mix(F0, vec3(1.0), f5);
vec3 b = F_schlick * (7.0 / f6) * (1.0 - F82);
reflectance -= b * split_sum.z;
}
vec4 sample_3D_texture(sampler2DArray tex, vec3 coords)
{
float layer_floored;
float interp = modf(coords.z, layer_floored);
coords.z = layer_floored;
vec4 tex_low = textureLod(tex, coords, 0.0);
coords.z += 1.0;
vec4 tex_high = textureLod(tex, coords, 0.0);
/* Manual trilinear interpolation. */
return mix(tex_low, tex_high, interp);
}
/* Return texture coordinates to sample Surface LUT. */
vec3 lut_coords_btdf(float cos_theta, float roughness, float ior)
{
vec3 coords = vec3(sqrt((ior - 1.0) / (ior + 1.0)), sqrt(1.0 - cos_theta), roughness);
/* scale and bias coordinates, for correct filtered lookup */
coords.xy = coords.xy * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE;
coords.z = coords.z * lut_btdf_layer_count + lut_btdf_layer_first;
return coords;
}
/* Return texture coordinates to sample BSDF LUT. */
vec3 lut_coords_bsdf(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;
coords.z = coords.z * lut_btdf_layer_count + lut_btdf_layer_first;
return coords;
}
/* Computes the reflectance and transmittance based on the tint (`f0`, `f90`, `transmission_tint`)
* and the BSDF LUT. */
void bsdf_lut(vec3 F0,
vec3 F90,
vec3 transmission_tint,
float cos_theta,
float roughness,
float ior,
bool do_multiscatter,
out vec3 reflectance,
out vec3 transmittance)
{
if (ior == 1.0) {
reflectance = vec3(0.0);
transmittance = transmission_tint;
return;
}
vec2 split_sum;
float transmission_factor;
if (ior > 1.0) {
split_sum = brdf_lut(cos_theta, roughness);
transmission_factor = sample_3D_texture(utilTex, lut_coords_btdf(cos_theta, roughness, ior)).a;
/* Gradually increase `f90` from 0 to 1 when IOR is in the range of [1.0, 1.33], to avoid harsh
* transition at `IOR == 1`. */
if (all(equal(F90, vec3(1.0)))) {
F90 = vec3(saturate(2.33 / 0.33 * (ior - 1.0) / (ior + 1.0)));
}
}
else {
vec3 bsdf = sample_3D_texture(utilTex, lut_coords_bsdf(cos_theta, roughness, ior)).rgb;
split_sum = bsdf.rg;
transmission_factor = bsdf.b;
}
reflectance = F_brdf_single_scatter(F0, F90, split_sum);
transmittance = (vec3(1.0) - F0) * transmission_factor * transmission_tint;
if (do_multiscatter) {
float real_F0 = F0_from_ior(ior);
float Ess = real_F0 * split_sum.x + split_sum.y + (1.0 - real_F0) * transmission_factor;
float Ems = 1.0 - Ess;
/* Assume that the transmissive tint makes up most of the overall color if it's not zero. */
vec3 Favg = all(equal(transmission_tint, vec3(0.0))) ? F0 + (F90 - F0) / 21.0 :
transmission_tint;
vec3 scale = 1.0 / (1.0 - Ems * Favg);
reflectance *= scale;
transmittance *= scale;
}
return;
}
/* Computes the reflectance and transmittance based on the BSDF LUT. */
vec2 bsdf_lut(float cos_theta, float roughness, float ior, bool do_multiscatter)
{
float F0 = F0_from_ior(ior);
vec3 color = vec3(1.0);
vec3 reflectance, transmittance;
bsdf_lut(vec3(F0),
color,
color,
cos_theta,
roughness,
ior,
do_multiscatter,
reflectance,
transmittance);
return vec2(reflectance.r, transmittance.r);
}
/** \} */

@ -1,8 +0,0 @@
/* SPDX-FileCopyrightText: 2020-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
void main()
{
fragColor = cryptohash;
}

@ -1,9 +0,0 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_attribute_lib.glsl)
#pragma BLENDER_REQUIRE(surface_vert.glsl)

@ -1,10 +0,0 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/* Empty GLSL source to satisfy the GPUShaderCreateInfo requirements. */
/* Needed includes for shader nodes. */
#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_attribute_lib.glsl)

@ -1,11 +0,0 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/* Empty GLSL source to satisfy the GPUShaderCreateInfo requirements. */
/* Needed includes for shader nodes. */
#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_attribute_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_volume_lib.glsl)

@ -1,172 +0,0 @@
/* SPDX-FileCopyrightText: 2015-2016 Keijiro Takahashi
* SPDX-FileCopyrightText: 2017-2022 Blender Authors
*
* SPDX-License-Identifier: MIT AND GPL-2.0-or-later */
/* Original implementation by Keijiro Takahashi (MIT license).
* Blender integration by Clément Foucault. */
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
/* -------------- Utils ------------- */
/* 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.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
}

@ -1,94 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* 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)
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 homogeneous 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);
}

@ -1,108 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* 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)
#define tile_to_fullres_factor float(DOF_TILE_DIVISOR)
/* Error introduced by the random offset of the gathering kernel's center. */
#define 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);
}

@ -1,34 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Downsample pass: CoC aware downsample to quarter resolution.
*
* Pretty much identical to the setup pass but get CoC from buffer. Also does not
* weight luma for the bilateral weights.
*/
#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
void main()
{
DEFINE_DOF_QUAD_OFFSETS
vec2 halfres_texel_size = 1.0 / vec2(textureSize(colorBuffer, 0).xy);
/* Center uv around the 4 half-resolution pixels. */
vec2 quad_center = (floor(gl_FragCoord.xy) * 2.0 + 1.0) * halfres_texel_size;
vec4 colors[4];
vec4 cocs;
for (int i = 0; i < 4; i++) {
vec2 sample_uv = quad_center + quad_offsets[i] * halfres_texel_size;
colors[i] = textureLod(colorBuffer, sample_uv, 0.0);
cocs[i] = textureLod(cocBuffer, sample_uv, 0.0).r;
}
vec4 weights = dof_downsample_bilateral_coc_weights(cocs);
/* Normalize so that the sum is 1. */
weights *= safe_rcp(sum(weights));
outColor = weighted_sum_array(colors, weights);
}

@ -1,88 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Gather Filter pass: Filter the gather pass result to reduce noise.
*
* This is a simple 3x3 median filter to avoid dilating highlights with a 3x3 max filter even if
* cheaper.
*/
#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
/* From:
* Implementing Median Filters in XC4000E FPGAs
* JOHN L. SMITH, Univision Technologies Inc., Billerica, MA
* http://users.utcluj.ro/~baruch/resources/Image/xl23_16.pdf
* Figure 1 */
/* Outputs low median and high value of a triple. */
void lmh(vec4 s1, vec4 s2, vec4 s3, out vec4 l, out vec4 m, out vec4 h)
{
/* From diagram, with nodes numbered from top to bottom. */
vec4 h1 = max(s2, s3);
vec4 l1 = min(s2, s3);
vec4 h2 = max(s1, l1);
vec4 l2 = min(s1, l1);
vec4 h3 = max(h2, h1);
vec4 l3 = min(h2, h1);
l = l2;
m = l3;
h = h3;
}
vec4 median_filter(sampler2D tex, vec2 uv)
{
vec2 texel_size = 1.0 / vec2(textureSize(tex, 0).xy);
vec4 samples[9];
int s = 0;
const vec2 ofs[9] = vec2[9](vec2(-1, -1),
vec2(0, -1),
vec2(1, -1),
vec2(-1, 0),
vec2(0, 0),
vec2(1, 0),
vec2(-1, 1),
vec2(0, 1),
vec2(1, 1));
for (int s = 0; s < 9; s++) {
samples[s] = textureLod(tex, uv + ofs[s] * texel_size, 0.0);
}
if (no_gather_filtering) {
return samples[4];
}
for (int s = 0; s < 9; s += 3) {
lmh(samples[s], samples[s + 1], samples[s + 2], samples[s], samples[s + 1], samples[s + 2]);
}
/* Some aliases to better understand what's happening. */
vec4 L123 = samples[0 + 0], L456 = samples[3 + 0], L789 = samples[6 + 0];
vec4 M123 = samples[0 + 1], M456 = samples[3 + 1], M789 = samples[6 + 1];
vec4 H123 = samples[0 + 2], H456 = samples[3 + 2], H789 = samples[6 + 2];
vec4 dummy, l, m, h;
/* Left nodes. */
h = max(max(L123, L456), L789);
/* Right nodes. */
l = min(min(H123, H456), H789);
/* Center nodes. */
lmh(M123, M456, M789, dummy, m, dummy);
/* Last bottom nodes. */
lmh(l, m, h, dummy, m, dummy);
return m;
}
void main()
{
/* OPTI(fclem) Could early return on some tiles. */
outColor = median_filter(colorBuffer, uvcoordsvar.xy);
outWeight = median_filter(weightBuffer, uvcoordsvar.xy).r;
}

@ -1,53 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Tile flatten pass: Takes the half-resolution CoC buffer and converts it to 8x8 tiles.
*
* Output min and max values for each tile and for both foreground & background.
* Also outputs min intersectable CoC for the background, which is the minimum CoC
* that comes from the background pixels.
*/
#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
#define halfres_tile_divisor (DOF_TILE_DIVISOR / 2)
void main()
{
ivec2 halfres_bounds = textureSize(halfResCocBuffer, 0).xy - 1;
ivec2 tile_co = ivec2(gl_FragCoord.xy);
CocTile tile = dof_coc_tile_init();
for (int x = 0; x < halfres_tile_divisor; x++) {
/* OPTI: Could be done in separate passes. */
for (int y = 0; y < halfres_tile_divisor; y++) {
ivec2 sample_texel = tile_co * halfres_tile_divisor + ivec2(x, y);
vec2 sample_data = texelFetch(halfResCocBuffer, min(sample_texel, halfres_bounds), 0).rg;
float sample_coc = sample_data.x;
float sample_slight_focus_coc = sample_data.y;
float fg_coc = min(sample_coc, 0.0);
tile.fg_min_coc = min(tile.fg_min_coc, fg_coc);
tile.fg_max_coc = max(tile.fg_max_coc, fg_coc);
float bg_coc = max(sample_coc, 0.0);
tile.bg_min_coc = min(tile.bg_min_coc, bg_coc);
tile.bg_max_coc = max(tile.bg_max_coc, bg_coc);
if (sample_coc > 0.0) {
tile.bg_min_intersectable_coc = min(tile.bg_min_intersectable_coc, bg_coc);
}
if (sample_coc < 0.0) {
tile.fg_max_intersectable_coc = max(tile.fg_max_intersectable_coc, fg_coc);
}
tile.fg_slight_focus_max_coc = dof_coc_max_slight_focus(tile.fg_slight_focus_max_coc,
sample_slight_focus_coc);
}
}
dof_coc_tile_store(tile, outFgCoc, outBgCoc);
}

@ -1,272 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* 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)
#ifdef DOF_HOLEFILL_PASS
/* Dirty global variable that isn't used. So it should get optimized out. */
vec2 outOcclusion;
#endif
#ifdef DOF_FOREGROUND_PASS
# define is_foreground true
#else /* DOF_BACKGROUND_PASS */
# define 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 half-resolution 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);
}
}

@ -1,642 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#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
#define no_smooth_intersection false
#define no_gather_occlusion false
#define no_gather_mipmaps false
#define no_gather_random false
#define no_gather_filtering false
#define no_scatter_occlusion false
#define no_scatter_pass false
#define no_foreground_pass false
#define no_background_pass false
#define no_slight_focus_pass false
#define no_focus_pass false
#define no_holefill_pass false
/* -------------- Quality Defines ------------- */
#ifdef DOF_HOLEFILL_PASS
/* No need for very high density for holefill. */
# define gather_ring_count 3
# define gather_ring_density 3
# define gather_max_density_change 0
# define gather_density_change_ring 1
#else
# define gather_ring_count DOF_GATHER_RING_COUNT
# define gather_ring_density 3
# define gather_max_density_change 50 /* Dictates the maximum good quality blur. */
# define gather_density_change_ring 1
#endif
/* -------------- Utils ------------- */
/* For performance on macOS, constants declared within function scope utilize constant uniform
* register space rather than per-thread, reducing spill and increasing
* thread execution width - and thus performance. */
#define DEFINE_DOF_QUAD_OFFSETS \
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 half-resolution. CoC must be divided by 2 to be compared against radii. */
return coc * 0.5;
}
/* Distribute weights between near/slight-focus/far fields (slide 117). */
#define layer_threshold 4.0
/* Make sure it overlaps. */
#define layer_offset_fg (0.5 + 1.0)
/* Extra offset for convolution layers to avoid light leaking from background. */
#define 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 full-resolution 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 half-resolution 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;
};
#ifdef GPU_METAL
/* C++ struct initialization. */
# define GATHER_DATA_INIT \
{ \
vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 \
}
#else
# define GATHER_DATA_INIT DofGatherData(vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
#endif
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 slight-focus
* 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;
}

@ -1,161 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* 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)
#ifdef COPY_PASS
/* NOTE: Do not compare alpha as it is not scattered by the scatter pass. */
float dof_scatter_neighborhood_rejection(vec3 color)
{
DEFINE_DOF_QUAD_OFFSETS;
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));
/* Full-resolution to half-resolution CoC. */
coc *= 0.5;
/* Allow 10px transition. */
const float rejection_hardness = 1.0 / 10.0;
return saturate((min_screen_border_distance - abs(coc)) * rejection_hardness + 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()
{
DEFINE_DOF_QUAD_OFFSETS
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

@ -1,193 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Recombine Pass: Load separate convolution layer and composite with self slight defocus
* convolution and in-focus fields.
*
* The half-resolution gather methods are fast but lack precision for small CoC areas. To fix this
* we do a brute-force 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)
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 sub-pixel 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 transitioning 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 / vec2(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
}

@ -1,74 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Scatter pass: Use sprites to scatter the color of very bright pixel to have higher quality blur.
*
* We only scatter one triangle per sprite and one sprite per 4 pixels to reduce vertex shader
* invocations and overdraw.
*/
#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
float bokeh_shape(vec2 center)
{
vec2 co = gl_FragCoord.xy - center;
#ifdef DOF_BOKEH_TEXTURE
co *= bokehAnisotropyInv;
float texture_size = float(textureSize(bokehLut, 0).x);
/* Bias scale to avoid sampling at the texture's border. */
float scale_fac = spritesize * (float(DOF_BOKEH_LUT_SIZE) / float(DOF_BOKEH_LUT_SIZE - 1));
float dist = scale_fac * textureLod(bokehLut, (co / scale_fac) * 0.5 + 0.5, 0.0).r;
#else
float dist = length(co);
#endif
return dist;
}
#define linearstep(p0, p1, v) (clamp(((v) - (p0)) / abs((p1) - (p0)), 0.0, 1.0))
void main(void)
{
DEFINE_DOF_QUAD_OFFSETS
vec4 shapes;
for (int i = 0; i < 4; i++) {
shapes[i] = bokeh_shape(spritepos + quad_offsets[i]);
}
/* Becomes signed distance field in pixel units. */
shapes -= cocs;
/* Smooth the edges a bit to fade out the undersampling artifacts. */
shapes = 1.0 - linearstep(-0.8, 0.8, shapes);
/* Outside of bokeh shape. Try to avoid overloading ROPs. */
if (max_v4(shapes) == 0.0) {
discard;
return;
}
if (!no_scatter_occlusion) {
/* Works because target is the same size as occlusionBuffer. */
vec2 uv = gl_FragCoord.xy / vec2(textureSize(occlusionBuffer, 0).xy);
vec2 occlusion_data = texture(occlusionBuffer, uv).rg;
/* Fix tilling artifacts. (Slide 90) */
const float correction_fac = 1.0 - DOF_FAST_GATHER_COC_ERROR;
/* Occlude the sprite with geometry from the same field
* using a VSM like chebychev test (slide 85). */
float mean = occlusion_data.x;
float variance = occlusion_data.y;
shapes *= variance * safe_rcp(variance + sqr(max(cocs * correction_fac - mean, 0.0)));
}
fragColor = color1 * shapes.x;
fragColor += color2 * shapes.y;
fragColor += color3 * shapes.z;
fragColor += color4 * shapes.w;
/* Do not accumulate alpha. This has already been accumulated by the gather pass. */
fragColor.a = 0.0;
#ifdef DOF_DEBUG_SCATTER_PERF
fragColor.rgb = avg(fragColor.rgb) * vec3(1.0, 0.0, 0.0);
#endif
}

@ -1,127 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)
/* Load 4 Circle of confusion values. texel_co is centered around the 4 taps. */
vec4 fetch_cocs(vec2 texel_co)
{
/* TODO(@fclem): The `textureGather(sampler, co, comp)` variant isn't here on some
* implementations.
*/
#if 0 // GPU_ARB_texture_gather
vec2 uvs = texel_co / vec2(textureSize(cocBuffer, 0));
/* Reminder: Samples order is CW starting from top left. */
cocs = textureGather(cocBuffer, uvs, isForegroundPass ? 0 : 1);
#else
ivec2 texel = ivec2(texel_co - 0.5);
vec4 cocs;
cocs.x = texelFetchOffset(cocBuffer, texel, 0, ivec2(0, 1)).r;
cocs.y = texelFetchOffset(cocBuffer, texel, 0, ivec2(1, 1)).r;
cocs.z = texelFetchOffset(cocBuffer, texel, 0, ivec2(1, 0)).r;
cocs.w = texelFetchOffset(cocBuffer, texel, 0, ivec2(0, 0)).r;
#endif
#ifdef DOF_FOREGROUND_PASS
cocs *= -1.0;
#endif
cocs = max(vec4(0.0), cocs);
/* We are scattering at half resolution, so divide CoC by 2. */
return cocs * 0.5;
}
void vertex_discard()
{
/* Don't produce any fragments */
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
}
void main()
{
DEFINE_DOF_QUAD_OFFSETS
ivec2 tex_size = textureSize(cocBuffer, 0);
int t_id = gl_VertexID / 3; /* Triangle Id */
/* Some math to get the target pixel. */
ivec2 texelco = ivec2(t_id % spritePerRow, t_id / spritePerRow) * 2;
/* Center sprite around the 4 texture taps. */
spritepos = vec2(texelco) + 1.0;
cocs = fetch_cocs(spritepos);
/* Early out from local CoC radius. */
if (all(lessThan(cocs, vec4(0.5)))) {
vertex_discard();
return;
}
vec2 input_texel_size = 1.0 / vec2(tex_size);
vec2 quad_center = spritepos * input_texel_size;
vec4 colors[4];
bool no_color = true;
for (int i = 0; i < 4; i++) {
vec2 sample_uv = quad_center + quad_offsets[i] * input_texel_size;
colors[i] = dof_load_scatter_color(colorBuffer, sample_uv, 0.0);
no_color = no_color && all(equal(colors[i].rgb, vec3(0.0)));
}
/* Early out from no color to scatter. */
if (no_color) {
vertex_discard();
return;
}
weights = dof_layer_weight(cocs) * dof_sample_weight(cocs);
/* Filter NaNs. */
for (int i = 0; i < 4; i++) {
if (isnan(weights[i]) || isinf(weights[i])) {
weights[i] = 0.0;
}
}
color1 = colors[0] * weights[0];
color2 = colors[1] * weights[1];
color3 = colors[2] * weights[2];
color4 = colors[3] * weights[3];
/* Extend to cover at least the unit circle */
const float extend = (cos(M_PI_4) + 1.0) * 2.0;
/* Crappy diagram
* ex 1
* | \
* | \
* 1 | \
* | \
* | \
* 0 | x \
* | Circle \
* | Origin \
* -1 0 --------------- 2
* -1 0 1 ex
*/
/* Generate Triangle : less memory fetches from a VBO */
int v_id = gl_VertexID % 3; /* Vertex Id */
gl_Position.x = float(v_id / 2) * extend - 1.0; /* int divisor round down */
gl_Position.y = float(v_id % 2) * extend - 1.0;
gl_Position.z = 0.0;
gl_Position.w = 1.0;
spritesize = max_v4(cocs);
/* Add 2.5 to max_coc because the max_coc may not be centered on the sprite origin
* and because we smooth the bokeh shape a bit in the pixel shader. */
gl_Position.xy *= spritesize * bokehAnisotropy + 2.5;
/* Position the sprite. */
gl_Position.xy += spritepos;
/* NDC range [-1..1]. */
gl_Position.xy = gl_Position.xy * targetTexelSize * 2.0 - 1.0;
/* Add 2.5 for the same reason but without the ratio. */
spritesize += 2.5;
}

@ -1,59 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* 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)
void main()
{
DEFINE_DOF_QUAD_OFFSETS
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 = select(cocs, vec4(DOF_TILE_MIXED), focus);
cocs = select(cocs, vec4(DOF_TILE_MIXED), defocus);
}
else {
cocs = select(cocs, vec4(DOF_TILE_FOCUS), focus);
cocs = select(cocs, vec4(DOF_TILE_DEFOCUS), defocus);
}
outCoc.y = max_v4(cocs);
}

@ -1,43 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Simple down-sample shader. Takes the average of the 4 texels of lower mip.
*/
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
void main()
{
/* Global scope arrays get allocated using local memory in Metal. Moving inside function scope to
* reduce register pressure. */
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));
vec2 uvs = gl_FragCoord.xy * texelSize;
uvs = 2.0 * uvs - 1.0;
vec3 cubevec = x_axis[geom_iface_flat.fFace] * uvs.x + y_axis[geom_iface_flat.fFace] * uvs.y +
maj_axes[geom_iface_flat.fFace];
FragColor = textureLod(source, cubevec, 0.0);
}

@ -1,36 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Simple down-sample shader.
* Do a gaussian filter using 4 bilinear texture samples.
*/
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
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 #87801). */
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
}

@ -1,112 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* This shader only compute maximum horizon angles for each directions.
* The final integration is done at the resolve stage with the shading normal.
*/
#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)
/* 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

@ -1,68 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* 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 misalignment.
*/
#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
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 #87801). */
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)) && defined(GPU_OPENGL)
/* Use color format instead of 24bit depth texture */
fragColor = vec4(val);
#endif
#if !(defined(GPU_INTEL) && defined(GPU_OPENGL))
/* If using Intel workaround, do not write out depth as there will be no depth target and this is
* invalid. */
gl_FragDepth = val;
#endif
}

@ -1,33 +0,0 @@
/* SPDX-FileCopyrightText: 2018-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#define mistStart mistSettings.x
#define mistInvDistance mistSettings.y
#define mistFalloff mistSettings.z
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);
}

@ -1,220 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Based on:
* A Fast and Stable Feature-Aware Motion Blur Filter
* by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai
*
* With modification from the presentation:
* Next Generation Post Processing in Call of Duty Advanced Warfare
* by Jorge Jimenez
*/
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#define KERNEL 8
#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! */
#define saturate(a) clamp(a, 0.0, 1.0)
vec2 spread_compare(float center_motion_length, float sample_motion_length, float offset_length)
{
return saturate(vec2(center_motion_length, sample_motion_length) - offset_length + 1.0);
}
vec2 depth_compare(float center_depth, float sample_depth)
{
return saturate(0.5 + vec2(depthScale, -depthScale) * (sample_depth - center_depth));
}
/* Kill contribution if not going the same direction. */
float dir_compare(vec2 offset, vec2 sample_motion, float sample_motion_length)
{
if (sample_motion_length < 0.5) {
return 1.0;
}
return (dot(offset, sample_motion) > 0.0) ? 1.0 : 0.0;
}
/* Return background (x) and foreground (y) weights. */
vec2 sample_weights(float center_depth,
float sample_depth,
float center_motion_length,
float sample_motion_length,
float offset_length)
{
/* Classify foreground/background. */
vec2 depth_weight = depth_compare(center_depth, sample_depth);
/* Weight if sample is overlapping or under the center pixel. */
vec2 spread_weight = spread_compare(center_motion_length, sample_motion_length, offset_length);
return depth_weight * spread_weight;
}
vec4 decode_velocity(vec4 velocity)
{
velocity = velocity * 2.0 - 1.0;
/* NOTE(@fclem): Needed to match cycles. Can't find why. */
velocity *= 0.5;
/* Transpose to pixel-space. */
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,
vec2 offset,
float offset_len,
const bool next,
inout vec4 accum,
inout vec4 accum_bg,
inout vec3 w_accum)
{
vec2 sample_uv = screen_uv - offset * viewportSizeInv;
vec2 sample_motion = sample_velocity(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);
vec3 weights;
weights.xy = sample_weights(
center_depth, sample_depth, center_motion_len, sample_motion_len, offset_len);
weights.z = dir_compare(offset, sample_motion, sample_motion_len);
weights.xy *= weights.z;
accum += col * weights.y;
accum_bg += col * weights.x;
w_accum += weights;
}
void gather_blur(vec2 screen_uv,
vec2 center_motion,
float center_depth,
vec2 max_motion,
float ofs,
const bool next,
inout vec4 accum,
inout vec4 accum_bg,
inout vec3 w_accum)
{
float center_motion_len = length(center_motion);
float max_motion_len = length(max_motion);
/* Tile boundaries randomization can fetch a tile where there is less motion than this pixel.
* Fix this by overriding the max_motion. */
if (max_motion_len < center_motion_len) {
max_motion_len = center_motion_len;
max_motion = center_motion;
}
if (max_motion_len < 0.5) {
return;
}
int i;
float t, inc = 1.0 / float(KERNEL);
for (i = 0, t = ofs * inc; i < KERNEL; i++, t += inc) {
gather_sample(screen_uv,
center_depth,
center_motion_len,
max_motion * t,
max_motion_len * t,
next,
accum,
accum_bg,
w_accum);
}
if (center_motion_len < 0.5) {
return;
}
for (i = 0, t = ofs * inc; i < KERNEL; i++, t += inc) {
/* Also sample in center motion direction.
* Allow recovering motion where there is conflicting
* motion between foreground and background. */
gather_sample(screen_uv,
center_depth,
center_motion_len,
center_motion * t,
center_motion_len * t,
next,
accum,
accum_bg,
w_accum);
}
}
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);
vec2 rand = texelfetch_noise_tex(gl_FragCoord.xy).xy;
/* 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));
/* First (center) sample: time = T */
/* x: Background, y: Foreground, z: dir. */
vec3 w_accum = vec3(0.0, 0.0, 1.0);
vec4 accum_bg = vec4(0.0);
vec4 accum = vec4(0.0);
/* First linear gather. time = [T - delta, T] */
gather_blur(
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);
#if 1
/* Avoid division by 0.0. */
float w = 1.0 / (50.0 * float(KERNEL) * 4.0);
accum_bg += center_color * w;
w_accum.x += w;
/* NOTE: In Jimenez's presentation, they used center sample.
* We use background color as it contains more information for foreground
* elements that have not enough weights.
* Yield better blur in complex motion. */
center_color = accum_bg / w_accum.x;
#endif
/* Merge background. */
accum += accum_bg;
w_accum.y += w_accum.x;
/* 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;
#if 0 /* For debugging. */
fragColor.rgb = fragColor.ggg;
fragColor.rg += max_motion.xy;
#endif
}

@ -1,53 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/* 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
*/
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
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 categorized into 4 sets of samples.
* See "Stochastic all the things" presentation slide 32-37. */
#define resolve_samples_count 9

@ -1,312 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#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
*/
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 {
/* Do not sample scene buffer if running probe pass in split reflection mode. */
#ifndef RESOLVE_PROBE
color = textureLod(colorBuffer, uv * hizUvScale.xy, mip).rgb;
#else
color = vec3(0.0);
#endif
}
/* 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 hit-point 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;
}
/* NOTE(Metal): For Apple silicon GPUs executing this particular shader, by default, memory read
* pressure is high while ALU remains low. Packing the sample data into a smaller format balances
* this trade-off by reducing local shader register pressure and expensive memory look-ups into
* spilled local shader memory, resulting in an increase in performance of 20% for this shader. */
#ifdef GPU_METAL
# define SAMPLE_STORAGE_TYPE uchar
# define pack_sample(x, y) uchar(((uchar(x + 2)) << uchar(3)) + (uchar(y + 2)))
# define unpack_sample(x) vec2((char(x) >> 3) - 2, (char(x) & 7) - 2)
#else
# define SAMPLE_STORAGE_TYPE vec2
# define pack_sample(x, y) SAMPLE_STORAGE_TYPE(x, y)
# define unpack_sample(x) x
#endif
void raytrace_resolve(ClosureInputGlossy cl_in,
inout ClosureEvalGlossy cl_eval,
inout ClosureEvalCommon cl_common,
inout ClosureOutputGlossy cl_out)
{
/* NOTE: Reflection samples declared in function scope to avoid per-thread memory pressure on
* tile-based GPUs e.g. Apple Silicon. */
const SAMPLE_STORAGE_TYPE resolve_sample_offsets[36] = SAMPLE_STORAGE_TYPE[36](
/* Set 1. */
/* First Ring (2x2). */
pack_sample(0, 0),
/* Second Ring (6x6). */
pack_sample(-1, 3),
pack_sample(1, 3),
pack_sample(-1, 1),
pack_sample(3, 1),
pack_sample(-2, 0),
pack_sample(3, 0),
pack_sample(2, -1),
pack_sample(1, -2),
/* Set 2. */
/* First Ring (2x2). */
pack_sample(1, 1),
/* Second Ring (6x6). */
pack_sample(-2, 3),
pack_sample(3, 3),
pack_sample(0, 2),
pack_sample(2, 2),
pack_sample(-2, -1),
pack_sample(1, -1),
pack_sample(0, -2),
pack_sample(3, -2),
/* Set 3. */
/* First Ring (2x2). */
pack_sample(0, 1),
/* Second Ring (6x6). */
pack_sample(0, 3),
pack_sample(3, 2),
pack_sample(-2, 1),
pack_sample(2, 1),
pack_sample(-1, 0),
pack_sample(-2, -2),
pack_sample(0, -1),
pack_sample(2, -2),
/* Set 4. */
/* First Ring (2x2). */
pack_sample(1, 0),
/* Second Ring (6x6). */
pack_sample(2, 3),
pack_sample(-2, 2),
pack_sample(-1, 2),
pack_sample(1, 2),
pack_sample(2, 0),
pack_sample(-1, -1),
pack_sample(3, -1),
pack_sample(-1, -2));
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 + unpack_sample(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 defined(GPU_INTEL) && defined(GPU_METAL)
float factor = 1.0f;
#endif
if (depth == 1.0) {
#if defined(GPU_INTEL) && defined(GPU_METAL)
/* Divergent code execution (and sampling) causes corruption due to undefined
* derivative/sampling behavior, on Intel GPUs. Using a mask factor to ensure shaders do not
* diverge and only the final result is masked. */
factor = 0.0f;
#else
/* NOTE: In the Metal API, prior to Metal 2.3, Discard is not an explicit return and can
* produce undefined behavior. This is especially prominent with derivatives if control-flow
* divergence is present.
*
* Adding a return call eliminates undefined behavior and a later out-of-bounds read causing
* a crash on AMD platforms.
* This behavior can also affect OpenGL on certain devices. */
discard;
return;
#endif
}
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) {
#if defined(GPU_INTEL) && defined(GPU_METAL)
factor = 0.0f;
#else
discard;
return;
#endif
}
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 cube-map iter. BRDF term is already computed during main pass and is
* passed as specular color. */
CLOSURE_EVAL_FUNCTION_1(ssr_resolve, Glossy);
/* Default single pass resolve */
fragColor = vec4(out_Glossy_0.radiance * brdf, 1.0);
#if defined(GPU_INTEL) && defined(GPU_METAL)
/* Due to non-uniform control flow with discard, Intel on macOS requires blending factor
* to discard unwanted fragments. */
fragColor *= factor;
#endif
}

@ -1,142 +0,0 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/* 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
*/
#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)
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 full-resolution 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(textureLod(normalBuffer, uvs, 0.0).rg, vV);
vec3 N = transform_direction(ViewMatrixInverse, vN);
/* Retrieve pixel data */
vec4 speccol_roughness = textureLod(specroughBuffer, uvs, 0.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);
}

@ -1,66 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#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 */
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 * avg_inv_radius;
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 * sss_kernel[0].rgb;
for (int i = 1; i < sss_samples && i < MAX_SSS_SAMPLES; i++) {
vec2 sample_uv = uvs + sss_kernel[i].a * finalStep *
((abs(sss_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 sss_kernel color. */
accum += sss_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
}

@ -1,120 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#if 0
uniform sampler2D colorBuffer;
uniform depth2D depthBuffer;
uniform sampler2D colorHistoryBuffer;
uniform mat4 prevViewProjectionMatrix;
out vec4 FragColor;
#endif
#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
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

@ -1,253 +0,0 @@
/* SPDX-FileCopyrightText: 2019-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#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)
vec3 sss_profile(float s)
{
s /= radii_max_radius.w * avg_inv_radius;
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_4;
}
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 shadow-maps) */
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 shadow-maps) */
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 shadow-map 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 shadow-map. */
float tex_id = float(data_id);
d = shadow_cube_radial_depth(cubevec, tex_id, shadow_id);
/* NOTE: The offset is irregular in respect to cube-face 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 shadow-map 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);
}

@ -1,27 +0,0 @@
/* SPDX-FileCopyrightText: 2018-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
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;
}

@ -1,146 +0,0 @@
/* SPDX-FileCopyrightText: 2020-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Shaders that down-sample velocity buffer,
*
* 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.
*/
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;
}
vec4 encode_velocity(vec4 velocity)
{
return velocity * viewportSizeInv.xyxy * 0.5 + 0.5;
}
#ifdef TILE_GATHER
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 */
bool neighbor_affect_this_tile(ivec2 offset, vec2 velocity)
{
/* Manhattan distance to the tiles, which is used for
* differentiating corners versus middle blocks */
float displacement = float(abs(offset.x) + abs(offset.y));
/**
* Relative sign on each axis of the offset compared
* to the velocity for that tile. In order for a tile
* to affect the center tile, it must have a
* neighborhood velocity in which x and y both have
* identical or both have opposite signs relative to
* offset. If the offset coordinate is zero then
* velocity is irrelevant.
*/
vec2 point = sign(vec2(offset) * velocity);
float dist = (point.x + point.y);
/**
* Here's an example of the logic for this code.
* In this diagram, the upper-left tile has offset = (-1, -1).
* V1 is velocity = (1, -2). point in this case = (-1, 1), and therefore dist = 0,
* so the upper-left tile does not affect the center.
*
* Now, look at another case. V2 = (-1, -2). point = (1, 1), so dist = 2 and the tile
* does affect the center.
*
* V2(-1,-2) V1(1, -2)
* \ /
* \ /
* \/___ ____ ____
* (-1, -1)| | | |
* |____|____|____|
* | | | |
* |____|____|____|
* | | | |
* |____|____|____|
*/
return (abs(dist) == displacement);
}
/**
* Only gather neighborhood velocity into tiles that could be affected by it.
* In the general case, only six of the eight neighbors contribute:
*
* This tile can't possibly be affected by the center one
* |
* v
* ____ ____ ____
* | | ///|/// |
* |____|////|//__|
* | |////|/ |
* |___/|////|____|
* | //|////| | <--- This tile can't possibly be affected by the center one
* |_///|///_|____|
*/
void main()
{
vec4 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);
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;
}
}
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;
}
}
}
}
tileMaxVelocity = encode_velocity(max_motion);
}
#endif

@ -1,59 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(eevee_legacy_bloom_common)
.push_constant(Type::VEC2, "sourceBufferTexelSize")
.push_constant(Type::VEC4, "curveThreshold")
.push_constant(Type::FLOAT, "clampIntensity")
.push_constant(Type::VEC2, "baseBufferTexelSize")
.push_constant(Type::FLOAT, "sampleScale")
.push_constant(Type::VEC3, "bloomColor")
.push_constant(Type::BOOL, "bloomAddBase")
.sampler(0, ImageType::FLOAT_2D, "sourceBuffer")
.sampler(1, ImageType::FLOAT_2D, "baseBuffer")
.fragment_out(0, Type::VEC4, "FragColor")
.additional_info("draw_fullscreen")
.fragment_source("effect_bloom_frag.glsl");
GPU_SHADER_CREATE_INFO(eevee_legacy_bloom_blit)
.define("STEP_BLIT")
.additional_info("eevee_legacy_bloom_common")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_bloom_blit_hq)
.define("HIGH_QUALITY")
.additional_info("eevee_legacy_bloom_blit")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_bloom_downsample)
.define("STEP_DOWNSAMPLE")
.additional_info("eevee_legacy_bloom_common")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_bloom_downsample_hq)
.define("HIGH_QUALITY")
.additional_info("eevee_legacy_bloom_downsample")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_bloom_upsample)
.define("STEP_UPSAMPLE")
.additional_info("eevee_legacy_bloom_common")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_bloom_upsample_hq)
.define("HIGH_QUALITY")
.additional_info("eevee_legacy_bloom_upsample")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_bloom_resolve)
.define("STEP_RESOLVE")
.additional_info("eevee_legacy_bloom_common")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_bloom_resolve_hq)
.define("HIGH_QUALITY")
.additional_info("eevee_legacy_bloom_resolve")
.do_static_compilation(true);

@ -1,211 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
#pragma once
/* EEVEE defines. */
GPU_SHADER_CREATE_INFO(eevee_legacy_defines_info).typedef_source("engine_eevee_shared_defines.h");
/* Only specifies bindings for common_uniform_lib.glsl. */
GPU_SHADER_CREATE_INFO(eevee_legacy_common_lib)
.typedef_source("engine_eevee_shared_defines.h")
.typedef_source("engine_eevee_legacy_shared.h")
.uniform_buf(1, "CommonUniformBlock", "common_block", Frequency::PASS);
/* Only specifies bindings for irradiance_lib.glsl. */
GPU_SHADER_CREATE_INFO(eevee_legacy_irradiance_lib)
.additional_info("eevee_legacy_common_lib")
.sampler(1, ImageType::FLOAT_2D_ARRAY, "irradianceGrid");
/* Utiltex Lib. */
GPU_SHADER_CREATE_INFO(eevee_legacy_common_utiltex_lib)
.sampler(2, ImageType::FLOAT_2D_ARRAY, "utilTex");
/* Ray-trace lib. */
GPU_SHADER_CREATE_INFO(eevee_legacy_raytrace_lib)
.additional_info("draw_view")
.additional_info("eevee_legacy_common_lib")
.sampler(3, ImageType::FLOAT_2D, "maxzBuffer")
.sampler(4, ImageType::DEPTH_2D_ARRAY, "planarDepth");
/* Ambient occlusion lib. */
GPU_SHADER_CREATE_INFO(eevee_legacy_ambient_occlusion_lib)
.additional_info("eevee_legacy_raytrace_lib")
.sampler(5, ImageType::FLOAT_2D, "horizonBuffer");
/* Light-probe lib. */
GPU_SHADER_CREATE_INFO(eevee_legacy_lightprobe_lib)
.additional_info("eevee_legacy_common_lib")
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("eevee_legacy_ambient_occlusion_lib")
.additional_info("eevee_legacy_irradiance_lib")
.sampler(6, ImageType::FLOAT_2D_ARRAY, "probePlanars")
.sampler(7, ImageType::FLOAT_CUBE_ARRAY, "probeCubes")
.uniform_buf(2, "ProbeBlock", "probe_block", Frequency::PASS)
.uniform_buf(3, "GridBlock", "grid_block", Frequency::PASS)
.uniform_buf(4, "PlanarBlock", "planar_block", Frequency::PASS);
/* LTC Lib. */
GPU_SHADER_CREATE_INFO(eevee_legacy_ltc_lib).additional_info("eevee_legacy_common_utiltex_lib");
/* Lights lib. */
GPU_SHADER_CREATE_INFO(eevee_legacy_lights_lib)
.additional_info("eevee_legacy_ltc_lib")
.additional_info("eevee_legacy_raytrace_lib")
.uniform_buf(5, "ShadowBlock", "shadow_block", Frequency::PASS)
.uniform_buf(6, "LightBlock", "light_block", Frequency::PASS)
.sampler(8, ImageType::SHADOW_2D_ARRAY, "shadowCubeTexture")
.sampler(9, ImageType::SHADOW_2D_ARRAY, "shadowCascadeTexture");
/* Hair lib. */
GPU_SHADER_CREATE_INFO(eevee_legacy_hair_lib)
.additional_info("draw_hair")
.sampler(10, ImageType::UINT_BUFFER, "hairStrandBuffer")
.sampler(11, ImageType::UINT_BUFFER, "hairStrandSegBuffer");
/* SSR Lib */
GPU_SHADER_CREATE_INFO(eevee_legacy_ssr_lib)
.additional_info("eevee_legacy_raytrace_lib")
.push_constant(Type::FLOAT, "refractionDepth")
.sampler(12, ImageType::FLOAT_2D, "refractColorBuffer");
/* renderpass_lib */
GPU_SHADER_CREATE_INFO(eevee_legacy_renderpass_lib)
.additional_info("eevee_legacy_common_lib")
.uniform_buf(12, "RenderpassBlock", "renderpass_block", Frequency::PASS);
/* Reflection lib */
GPU_SHADER_CREATE_INFO(eevee_legacy_reflection_lib)
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_view")
.push_constant(Type::IVEC2, "halfresOffset");
/* Volumetric lib. */
GPU_SHADER_CREATE_INFO(eevee_legacy_volumetric_lib)
.additional_info("eevee_legacy_lights_lib")
.additional_info("eevee_legacy_lightprobe_lib")
.additional_info("eevee_legacy_irradiance_lib")
.sampler(13, ImageType::FLOAT_3D, "inScattering")
.sampler(14, ImageType::FLOAT_3D, "inTransmittance");
/* eevee_legacy_cryptomatte_lib. */
GPU_SHADER_CREATE_INFO(eevee_legacy_cryptomatte_lib).additional_info("draw_curves_infos");
/* ----- SURFACE LIB ----- */
/* Surface lib has several different components depending on how it is used.
* Differing root permutations need to be generated and included depending
* on use-case. */
/* SURFACE LIB INTERFACES */
GPU_SHADER_INTERFACE_INFO(eevee_legacy_surface_common_iface, "")
.smooth(Type::VEC3, "worldPosition")
.smooth(Type::VEC3, "viewPosition")
.smooth(Type::VEC3, "worldNormal")
.smooth(Type::VEC3, "viewNormal");
GPU_SHADER_INTERFACE_INFO(eevee_legacy_surface_point_cloud_iface, "")
.smooth(Type::FLOAT, "pointRadius")
.smooth(Type::FLOAT, "pointPosition")
.flat(Type::INT, "pointID");
GPU_SHADER_INTERFACE_INFO(eevee_legacy_surface_hair_iface, "")
.smooth(Type::VEC3, "hairTangent")
.smooth(Type::FLOAT, "hairThickTime")
.smooth(Type::FLOAT, "hairThickness")
.smooth(Type::FLOAT, "hairTime")
.flat(Type::INT, "hairStrandID")
.smooth(Type::VEC2, "hairBary");
/* Surface lib components */
GPU_SHADER_CREATE_INFO(eevee_legacy_surface_lib_common)
.vertex_out(eevee_legacy_surface_common_iface);
GPU_SHADER_CREATE_INFO(eevee_legacy_surface_lib_hair)
.define("USE_SURFACE_LIB_HAIR")
/* Hair still uses the common interface as well. */
.additional_info("eevee_legacy_surface_lib_common")
.vertex_out(eevee_legacy_surface_hair_iface);
GPU_SHADER_CREATE_INFO(eevee_legacy_surface_lib_pointcloud)
.define("USE_SURFACE_LIB_POINTCLOUD")
/* Point-cloud still uses the common interface as well. */
.additional_info("eevee_legacy_surface_lib_common")
.vertex_out(eevee_legacy_surface_point_cloud_iface);
GPU_SHADER_CREATE_INFO(eevee_legacy_surface_lib_step_resolve).define("STEP_RESOLVE");
GPU_SHADER_CREATE_INFO(eevee_legacy_surface_lib_step_raytrace).define("STEP_RAYTRACE");
GPU_SHADER_CREATE_INFO(eevee_legacy_surface_lib_world_background).define("WORLD_BACKGROUND");
GPU_SHADER_CREATE_INFO(eevee_legacy_surface_lib_step_probe_capture).define("PROBE_CAPTURE");
GPU_SHADER_CREATE_INFO(eevee_legacy_surface_lib_use_barycentrics).define("USE_BARYCENTRICS");
GPU_SHADER_CREATE_INFO(eevee_legacy_surface_lib_codegen_lib).define("CODEGEN_LIB");
/* Surface lib permutations. */
/* Basic - lookdev world frag */
GPU_SHADER_CREATE_INFO(eevee_legacy_surface_lib_lookdev)
.additional_info("eevee_legacy_surface_lib_common");
/** Closure evaluation libraries **/
/* eevee_legacy_closure_type_lib */
GPU_SHADER_CREATE_INFO(eevee_legacy_closure_type_lib)
.push_constant(Type::INT, "outputSsrId")
.push_constant(Type::INT, "outputSssId");
/* eevee_legacy_closure_eval_lib */
GPU_SHADER_CREATE_INFO(eevee_legacy_closure_eval_lib)
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("eevee_legacy_lights_lib")
.additional_info("eevee_legacy_lightprobe_lib");
/* eevee_legacy_closure_eval_diffuse_lib */
GPU_SHADER_CREATE_INFO(eevee_legacy_closure_eval_diffuse_lib)
.additional_info("eevee_legacy_lights_lib")
.additional_info("eevee_legacy_lightprobe_lib")
.additional_info("eevee_legacy_ambient_occlusion_lib")
.additional_info("eevee_legacy_closure_eval_lib")
.additional_info("eevee_legacy_renderpass_lib");
/* eevee_legacy_closure_eval_glossy_lib */
GPU_SHADER_CREATE_INFO(eevee_legacy_closure_eval_glossy_lib)
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("eevee_legacy_lights_lib")
.additional_info("eevee_legacy_lightprobe_lib")
.additional_info("eevee_legacy_ambient_occlusion_lib")
.additional_info("eevee_legacy_closure_eval_lib")
.additional_info("eevee_legacy_renderpass_lib");
/* eevee_legacy_closure_eval_refraction_lib */
GPU_SHADER_CREATE_INFO(eevee_legacy_closure_eval_refraction_lib)
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("eevee_legacy_lights_lib")
.additional_info("eevee_legacy_lightprobe_lib")
.additional_info("eevee_legacy_ambient_occlusion_lib")
.additional_info("eevee_legacy_ssr_lib")
.additional_info("eevee_legacy_closure_eval_lib")
.additional_info("eevee_legacy_renderpass_lib");
/* eevee_legacy_closure_eval_translucent_lib */
GPU_SHADER_CREATE_INFO(eevee_legacy_closure_eval_translucent_lib)
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("eevee_legacy_lights_lib")
.additional_info("eevee_legacy_lightprobe_lib")
.additional_info("eevee_legacy_ambient_occlusion_lib")
.additional_info("eevee_legacy_closure_eval_lib")
.additional_info("eevee_legacy_renderpass_lib");
/* eevee_legacy_closure_eval_surface_lib */
GPU_SHADER_CREATE_INFO(eevee_legacy_closure_eval_surface_lib)
.additional_info("eevee_legacy_closure_eval_diffuse_lib")
.additional_info("eevee_legacy_closure_eval_glossy_lib")
.additional_info("eevee_legacy_closure_eval_refraction_lib")
.additional_info("eevee_legacy_closure_eval_translucent_lib")
.additional_info("eevee_legacy_renderpass_lib");

@ -1,293 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
/* DOF Lib */
GPU_SHADER_CREATE_INFO(eevee_legacy_dof_lib)
.additional_info("draw_view")
.push_constant(Type::VEC4, "cocParams");
/* EEVEE_shaders_depth_of_field_bokeh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_bokeh)
.additional_info("draw_fullscreen")
.additional_info("eevee_legacy_defines_info")
.additional_info("eevee_legacy_dof_lib")
.fragment_source("effect_dof_bokeh_frag.glsl")
.push_constant(Type::FLOAT, "bokehSides")
.push_constant(Type::FLOAT, "bokehRotation")
.push_constant(Type::VEC2, "bokehAnisotropyInv")
.fragment_out(0, Type::VEC2, "outGatherLut")
.fragment_out(1, Type::FLOAT, "outScatterLut")
.fragment_out(2, Type::FLOAT, "outResolveLut")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_depth_of_field_setup_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_setup)
.additional_info("draw_fullscreen")
.additional_info("eevee_legacy_defines_info")
.additional_info("eevee_legacy_dof_lib")
.fragment_source("effect_dof_setup_frag.glsl")
.sampler(0, ImageType::FLOAT_2D, "colorBuffer")
.sampler(1, ImageType::DEPTH_2D, "depthBuffer")
.push_constant(Type::FLOAT, "bokehMaxSize")
.fragment_out(0, Type::VEC4, "outColor")
.fragment_out(1, Type::VEC2, "outCoc")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_depth_of_field_flatten_tiles_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_flatten_tiles)
.additional_info("draw_fullscreen")
.additional_info("eevee_legacy_defines_info")
.additional_info("eevee_legacy_dof_lib")
.fragment_source("effect_dof_flatten_tiles_frag.glsl")
.sampler(0, ImageType::FLOAT_2D, "halfResCocBuffer")
.fragment_out(0, Type::VEC4, "outFgCoc")
.fragment_out(1, Type::VEC3, "outBgCoc")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_depth_of_field_dilate_tiles_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_dilate_tiles_common)
.additional_info("draw_fullscreen")
.additional_info("eevee_legacy_defines_info")
.additional_info("eevee_legacy_dof_lib")
.fragment_source("effect_dof_dilate_tiles_frag.glsl")
.push_constant(Type::INT, "ringCount")
.push_constant(Type::INT, "ringWidthMultiplier")
.push_constant(Type::BOOL, "dilateSlightFocus")
.sampler(0, ImageType::FLOAT_2D, "cocTilesFgBuffer")
.sampler(1, ImageType::FLOAT_2D, "cocTilesBgBuffer")
.fragment_out(0, Type::VEC4, "outFgCoc")
.fragment_out(1, Type::VEC3, "outBgCoc")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_dilate_tiles_MINMAX)
.define("DILATE_MODE_MIN_MAX")
.additional_info("eevee_legacy_depth_of_field_dilate_tiles_common")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_dilate_tiles_MINABS)
.define("DILATE_MODE_MIN_ABS")
.additional_info("eevee_legacy_depth_of_field_dilate_tiles_common")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_depth_of_field_downsample_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_downsample)
.additional_info("draw_fullscreen")
.additional_info("eevee_legacy_defines_info")
.additional_info("eevee_legacy_dof_lib")
.fragment_source("effect_dof_downsample_frag.glsl")
.sampler(0, ImageType::FLOAT_2D, "colorBuffer")
.sampler(1, ImageType::FLOAT_2D, "cocBuffer")
.fragment_out(0, Type::VEC4, "outColor")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_depth_of_field_reduce_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_reduce_common)
.additional_info("draw_fullscreen")
.additional_info("eevee_legacy_defines_info")
.additional_info("eevee_legacy_dof_lib")
.fragment_source("effect_dof_reduce_frag.glsl")
.sampler(0, ImageType::FLOAT_2D, "colorBuffer")
.sampler(1, ImageType::FLOAT_2D, "cocBuffer")
.sampler(2, ImageType::FLOAT_2D, "downsampledBuffer")
.push_constant(Type::VEC2, "bokehAnisotropy")
.push_constant(Type::FLOAT, "scatterColorThreshold")
.push_constant(Type::FLOAT, "scatterCocThreshold")
.push_constant(Type::FLOAT, "scatterColorNeighborMax")
.push_constant(Type::FLOAT, "colorNeighborClamping")
.fragment_out(0, Type::VEC4, "outColor")
.fragment_out(1, Type::FLOAT, "outCoc")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_reduce_COPY_PASS)
.define("COPY_PASS")
.fragment_out(2, Type::VEC3, "outScatterColor")
.additional_info("eevee_legacy_depth_of_field_reduce_common")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_reduce_REDUCE_PASS)
.define("REDUCE_PASS")
.additional_info("eevee_legacy_depth_of_field_reduce_common")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_depth_of_field_gather_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_gather_common)
.additional_info("draw_fullscreen")
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("eevee_legacy_defines_info")
.additional_info("eevee_legacy_dof_lib")
.fragment_source("effect_dof_gather_frag.glsl")
.sampler(0, ImageType::FLOAT_2D, "colorBuffer")
.sampler(1, ImageType::FLOAT_2D, "cocBuffer")
.sampler(2, ImageType::FLOAT_2D, "colorBufferBilinear")
.sampler(3, ImageType::FLOAT_2D, "cocTilesFgBuffer")
.sampler(4, ImageType::FLOAT_2D, "cocTilesBgBuffer")
.sampler(5, ImageType::FLOAT_2D, "bokehLut")
.push_constant(Type::VEC2, "gatherInputUvCorrection")
.push_constant(Type::VEC2, "gatherOutputTexelSize")
.push_constant(Type::VEC2, "bokehAnisotropy")
.fragment_out(0, Type::VEC4, "outColor")
.fragment_out(1, Type::FLOAT, "outWeight")
.auto_resource_location(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_gather_bokeh).define("DOF_BOKEH_TEXTURE");
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_gather_FOREGROUND)
.define("DOF_FOREGROUND_PASS")
.additional_info("eevee_legacy_depth_of_field_gather_common")
.fragment_out(2, Type::VEC2, "outOcclusion") /* NOT DOF_HOLEFILL_PASS */
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_gather_BACKGROUND)
.define("DOF_BACKGROUND_PASS")
.additional_info("eevee_legacy_depth_of_field_gather_common")
.fragment_out(2, Type::VEC2, "outOcclusion") /* NOT DOF_HOLEFILL_PASS */
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_gather_HOLEFILL)
.define("DOF_BACKGROUND_PASS")
.define("DOF_HOLEFILL_PASS")
.additional_info("eevee_legacy_depth_of_field_gather_common")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_gather_FOREGROUND_BOKEH)
.additional_info("eevee_legacy_depth_of_field_gather_bokeh")
.additional_info("eevee_legacy_depth_of_field_gather_FOREGROUND")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_gather_BACKGROUND_BOKEH)
.additional_info("eevee_legacy_depth_of_field_gather_bokeh")
.additional_info("eevee_legacy_depth_of_field_gather_BACKGROUND")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_gather_HOLEFILL_BOKEH)
.additional_info("eevee_legacy_depth_of_field_gather_bokeh")
.additional_info("eevee_legacy_depth_of_field_gather_HOLEFILL")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_depth_of_field_filter_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_filter)
.additional_info("draw_fullscreen")
.additional_info("eevee_legacy_defines_info")
.additional_info("eevee_legacy_dof_lib")
.fragment_source("effect_dof_filter_frag.glsl")
.sampler(0, ImageType::FLOAT_2D, "colorBuffer")
.sampler(1, ImageType::FLOAT_2D, "weightBuffer")
.fragment_out(0, Type::VEC4, "outColor")
.fragment_out(1, Type::FLOAT, "outWeight")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_depth_of_field_scatter_get */
GPU_SHADER_INTERFACE_INFO(eevee_legacy_dof_scatter_iface, "")
.flat(Type::VEC4, "color1")
.flat(Type::VEC4, "color2")
.flat(Type::VEC4, "color3")
.flat(Type::VEC4, "color4")
.flat(Type::VEC4, "weights")
.flat(Type::VEC4, "cocs")
.flat(Type::VEC2, "spritepos")
.flat(Type::FLOAT, "spritesize");
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_scatter_common)
.additional_info("eevee_legacy_defines_info")
.additional_info("eevee_legacy_dof_lib")
.vertex_source("effect_dof_scatter_vert.glsl")
.fragment_source("effect_dof_scatter_frag.glsl")
.vertex_out(eevee_legacy_dof_scatter_iface)
.push_constant(Type::VEC2, "targetTexelSize")
.push_constant(Type::INT, "spritePerRow")
.push_constant(Type::VEC2, "bokehAnisotropy")
.push_constant(Type::VEC2, "bokehAnisotropyInv")
.sampler(0, ImageType::FLOAT_2D, "colorBuffer")
.sampler(1, ImageType::FLOAT_2D, "cocBuffer")
.sampler(2, ImageType::FLOAT_2D, "occlusionBuffer")
.sampler(3, ImageType::FLOAT_2D, "bokehLut")
.fragment_out(0, Type::VEC4, "fragColor")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_scatter_bokeh).define("DOF_BOKEH_TEXTURE");
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_scatter_FOREGROUND)
.define("DOF_FOREGROUND_PASS")
.additional_info("eevee_legacy_depth_of_field_scatter_common")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_scatter_BACKGROUND)
.define("DOF_BACKGROUND_PASS")
.additional_info("eevee_legacy_depth_of_field_scatter_common")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_scatter_FOREGROUND_BOKEH)
.additional_info("eevee_legacy_depth_of_field_scatter_bokeh")
.additional_info("eevee_legacy_depth_of_field_scatter_FOREGROUND")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_scatter_BACKGROUND_BOKEH)
.additional_info("eevee_legacy_depth_of_field_scatter_bokeh")
.additional_info("eevee_legacy_depth_of_field_scatter_BACKGROUND")
.do_static_compilation(true);
/* EEVEE_shaders_depth_of_field_resolve_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_resolve_common)
.define("DOF_RESOLVE_PASS")
.additional_info("draw_fullscreen")
.additional_info("eevee_legacy_defines_info")
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("eevee_legacy_dof_lib")
.fragment_source("effect_dof_resolve_frag.glsl")
.sampler(0, ImageType::FLOAT_2D, "fullResColorBuffer")
.sampler(1, ImageType::DEPTH_2D, "fullResDepthBuffer")
.sampler(2, ImageType::FLOAT_2D, "bgColorBuffer")
.sampler(3, ImageType::FLOAT_2D, "bgWeightBuffer")
.sampler(4, ImageType::FLOAT_2D, "bgTileBuffer")
.sampler(5, ImageType::FLOAT_2D, "fgColorBuffer")
.sampler(6, ImageType::FLOAT_2D, "fgWeightBuffer")
.sampler(7, ImageType::FLOAT_2D, "fgTileBuffer")
.sampler(8, ImageType::FLOAT_2D, "holefillColorBuffer")
.sampler(9, ImageType::FLOAT_2D, "holefillWeightBuffer")
.sampler(10, ImageType::FLOAT_2D, "bokehLut")
.push_constant(Type::FLOAT, "bokehMaxSize")
.fragment_out(0, Type::VEC4, "fragColor")
.auto_resource_location(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_resolve_bokeh).define("DOF_BOKEH_TEXTURE");
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_resolve_HQ)
.define("DOF_SLIGHT_FOCUS_DENSITY", "4")
.additional_info("eevee_legacy_depth_of_field_resolve_common")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_resolve_LQ)
.define("DOF_SLIGHT_FOCUS_DENSITY", "2")
.additional_info("eevee_legacy_depth_of_field_resolve_common")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_resolve_HQ_BOKEH)
.additional_info("eevee_legacy_depth_of_field_resolve_HQ")
.additional_info("eevee_legacy_depth_of_field_resolve_bokeh")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_depth_of_field_resolve_LQ_BOKEH)
.additional_info("eevee_legacy_depth_of_field_resolve_LQ")
.additional_info("eevee_legacy_depth_of_field_resolve_bokeh")
.do_static_compilation(true);

@ -1,377 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
/* effect_minmaxz_frag permutation inputs. */
GPU_SHADER_CREATE_INFO(eevee_legacy_minmaxz_common)
.additional_info("draw_fullscreen")
.fragment_source("effect_minmaxz_frag.glsl")
.fragment_out(0, Type::VEC4, "fragColor") /* Needed by certain drivers. */
.depth_write(DepthWrite::ANY);
GPU_SHADER_CREATE_INFO(eevee_legacy_minmaxz_layered_common)
.define("LAYERED")
.sampler(0, ImageType::DEPTH_2D_ARRAY, "depthBuffer")
.push_constant(Type::INT, "depthLayer");
GPU_SHADER_CREATE_INFO(eevee_legacy_minmaxz_non_layered_common)
.sampler(0, ImageType::DEPTH_2D, "depthBuffer");
GPU_SHADER_CREATE_INFO(eevee_legacy_minmaxz_non_copy).push_constant(Type::VEC2, "texelSize");
GPU_SHADER_CREATE_INFO(eevee_legacy_minmaxz_copy).define("COPY_DEPTH");
/* Permutations. */
GPU_SHADER_CREATE_INFO(eevee_legacy_minz_downlevel)
.define("MIN_PASS")
.additional_info("eevee_legacy_minmaxz_common")
.additional_info("eevee_legacy_minmaxz_non_layered_common")
.additional_info("eevee_legacy_minmaxz_non_copy")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_maxz_downlevel)
.define("MAX_PASS")
.additional_info("eevee_legacy_minmaxz_common")
.additional_info("eevee_legacy_minmaxz_non_layered_common")
.additional_info("eevee_legacy_minmaxz_non_copy")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_minz_downdepth)
.define("MIN_PASS")
.additional_info("eevee_legacy_minmaxz_common")
.additional_info("eevee_legacy_minmaxz_non_layered_common")
.additional_info("eevee_legacy_minmaxz_non_copy")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_maxz_downdepth)
.define("MAX_PASS")
.additional_info("eevee_legacy_minmaxz_common")
.additional_info("eevee_legacy_minmaxz_non_layered_common")
.additional_info("eevee_legacy_minmaxz_non_copy")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_minz_downdepth_layer)
.define("MIN_PASS")
.additional_info("eevee_legacy_minmaxz_layered_common")
.additional_info("eevee_legacy_minmaxz_common")
.additional_info("eevee_legacy_minmaxz_non_copy")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_maxz_downdepth_layer)
.define("MAX_PASS")
.additional_info("eevee_legacy_minmaxz_layered_common")
.additional_info("eevee_legacy_minmaxz_common")
.additional_info("eevee_legacy_minmaxz_non_copy")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_maxz_copydepth_layer)
.define("MAX_PASS")
.additional_info("eevee_legacy_minmaxz_copy")
.additional_info("eevee_legacy_minmaxz_layered_common")
.additional_info("eevee_legacy_minmaxz_common")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_minz_copydepth)
.define("MIN_PASS")
.additional_info("eevee_legacy_minmaxz_copy")
.additional_info("eevee_legacy_minmaxz_common")
.additional_info("eevee_legacy_minmaxz_non_layered_common")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_maxz_copydepth)
.define("MAX_PASS")
.additional_info("eevee_legacy_minmaxz_copy")
.additional_info("eevee_legacy_minmaxz_common")
.additional_info("eevee_legacy_minmaxz_non_layered_common")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_update_noise_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_update_noise)
.sampler(0, ImageType::FLOAT_2D, "blueNoise")
.push_constant(Type::VEC3, "offsets")
.fragment_out(0, Type::VEC4, "FragColor")
.additional_info("draw_fullscreen")
.fragment_source("update_noise_frag.glsl")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_taa_resolve_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_taa_resolve)
.sampler(0, ImageType::FLOAT_2D, "colorBuffer")
.sampler(1, ImageType::FLOAT_2D, "colorHistoryBuffer")
.fragment_out(0, Type::VEC4, "FragColor")
.additional_info("draw_fullscreen")
.additional_info("draw_view")
.fragment_source("effect_temporal_aa.glsl")
.auto_resource_location(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_taa_resolve_basic)
.push_constant(Type::FLOAT, "alpha")
.additional_info("eevee_legacy_taa_resolve")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_taa_resolve_reprojection)
.define("USE_REPROJECTION")
.sampler(2, ImageType::DEPTH_2D, "depthBuffer")
.push_constant(Type::MAT4, "prevViewProjectionMatrix")
.additional_info("eevee_legacy_taa_resolve")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_velocity_resolve_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_velocity_resolve)
.sampler(0, ImageType::DEPTH_2D, "depthBuffer")
.push_constant(Type::MAT4, "prevViewProjMatrix")
.push_constant(Type::MAT4, "currViewProjMatrixInv")
.push_constant(Type::MAT4, "nextViewProjMatrix")
.fragment_out(0, Type::VEC4, "outData")
.additional_info("draw_fullscreen")
.fragment_source("effect_velocity_resolve_frag.glsl")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_effect_downsample_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_downsample_shared)
.additional_info("draw_fullscreen")
.sampler(0, ImageType::FLOAT_2D, "source")
.push_constant(Type::FLOAT, "fireflyFactor")
.fragment_out(0, Type::VEC4, "FragColor")
.fragment_source("effect_downsample_frag.glsl")
.auto_resource_location(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_downsample)
.additional_info("eevee_legacy_downsample_shared")
.push_constant(Type::VEC2, "texelSize")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_effect_color_copy_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_color_copy)
.define("COPY_SRC")
.additional_info("eevee_legacy_downsample_shared")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_effect_ambient_occlusion_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_ambient_occlusion)
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_view")
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("eevee_legacy_ambient_occlusion_lib")
.additional_info("draw_fullscreen")
.sampler(0, ImageType::FLOAT_2D, "normalBuffer")
.push_constant(Type::FLOAT, "fireflyFactor")
.fragment_out(0, Type::VEC4, "FragColor")
.fragment_source("effect_gtao_frag.glsl")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_effect_ambient_occlusion_debug_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_ambient_occlusion_debug)
.define("DEBUG_AO")
.define("ENABLE_DEFERED_AO")
.additional_info("eevee_legacy_ambient_occlusion")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_effect_reflection_trace_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_effect_reflection_trace)
.additional_info("eevee_legacy_surface_lib_step_raytrace")
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_view")
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("eevee_legacy_raytrace_lib")
.additional_info("eevee_legacy_lightprobe_lib")
.additional_info("eevee_legacy_reflection_lib")
.additional_info("draw_fullscreen")
.sampler(0, ImageType::FLOAT_2D, "normalBuffer")
.sampler(1, ImageType::FLOAT_2D, "specroughBuffer")
.push_constant(Type::VEC2, "targetSize")
.push_constant(Type::FLOAT, "randomScale")
.fragment_out(0, Type::VEC4, "hitData")
.fragment_out(1, Type::FLOAT, "hitDepth")
.fragment_source("effect_reflection_trace_frag.glsl")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_effect_reflection_resolve_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_effect_reflection_resolve)
.additional_info("eevee_legacy_surface_lib_step_resolve")
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_view")
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("eevee_legacy_raytrace_lib")
.additional_info("eevee_legacy_lightprobe_lib")
.additional_info("eevee_legacy_reflection_lib")
.additional_info("eevee_legacy_closure_eval_glossy_lib")
.additional_info("draw_fullscreen")
.sampler(0, ImageType::FLOAT_2D, "colorBuffer")
.sampler(1, ImageType::FLOAT_2D, "normalBuffer")
.sampler(2, ImageType::FLOAT_2D, "specroughBuffer")
.sampler(3, ImageType::FLOAT_2D, "hitBuffer")
.sampler(4, ImageType::FLOAT_2D, "hitDepth")
.push_constant(Type::INT, "samplePoolOffset")
.fragment_out(0, Type::VEC4, "fragColor")
.fragment_source("effect_reflection_resolve_frag.glsl")
.auto_resource_location(true)
.do_static_compilation(true);
/* Split reflection resolve support for Intel-based MacBooks. */
GPU_SHADER_CREATE_INFO(eevee_legacy_effect_reflection_resolve_probe)
.define("RESOLVE_PROBE")
.additional_info("eevee_legacy_effect_reflection_resolve")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_effect_reflection_resolve_ssr)
.define("RESOLVE_SSR")
.additional_info("eevee_legacy_effect_reflection_resolve")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_subsurface_first_pass_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_shader_effect_subsurface_common)
.additional_info("draw_fullscreen")
.additional_info("draw_view")
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("eevee_legacy_common_lib")
.fragment_out(0, Type::VEC4, "sssRadiance")
.fragment_source("effect_subsurface_frag.glsl")
.uniform_buf(0, "SSSProfileBlock", "sssProfile", Frequency::PASS)
.sampler(0, ImageType::DEPTH_2D, "depthBuffer")
.sampler(1, ImageType::FLOAT_2D, "sssIrradiance")
.sampler(2, ImageType::FLOAT_2D, "sssRadius")
.sampler(3, ImageType::FLOAT_2D, "sssAlbedo")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_shader_effect_subsurface_common_FIRST_PASS)
.define("FIRST_PASS")
.additional_info("eevee_legacy_shader_effect_subsurface_common")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_shader_effect_subsurface_common_SECOND_PASS)
.define("SECOND_PASS")
.additional_info("eevee_legacy_shader_effect_subsurface_common")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_subsurface_translucency_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_shader_effect_subsurface_translucency)
.define("EEVEE_TRANSLUCENCY")
.additional_info("draw_fullscreen")
.additional_info("draw_view")
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("eevee_legacy_common_lib")
.additional_info("eevee_legacy_lights_lib")
.fragment_source("effect_translucency_frag.glsl")
.fragment_out(0, Type::VEC4, "FragColor")
.sampler(1, ImageType::DEPTH_2D, "depthBuffer")
.sampler(1, ImageType::FLOAT_1D, "sssTexProfile")
.sampler(1, ImageType::FLOAT_2D, "sssRadius")
.sampler(1, ImageType::FLOAT_2D_ARRAY, "sssShadowCubes")
.sampler(1, ImageType::FLOAT_2D_ARRAY, "sssShadowCascades")
.uniform_buf(0, "SSSProfileBlock", "sssProfile", Frequency::PASS)
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_renderpasses_post_process_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_post_process)
.additional_info("draw_fullscreen")
.additional_info("draw_view")
.additional_info("eevee_legacy_common_lib")
.fragment_source("renderpass_postprocess_frag.glsl")
.push_constant(Type::INT, "postProcessType")
.push_constant(Type::INT, "currentSample")
.sampler(0, ImageType::DEPTH_2D, "depthBuffer")
.sampler(1, ImageType::FLOAT_2D, "inputBuffer")
.sampler(2, ImageType::FLOAT_2D, "inputSecondLightBuffer")
.sampler(3, ImageType::FLOAT_2D, "inputColorBuffer")
.sampler(4, ImageType::FLOAT_2D, "inputTransmittanceBuffer")
.fragment_out(0, Type::VEC4, "fragColor")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_renderpasses_accumulate_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_renderpass_accumulate)
.additional_info("draw_fullscreen")
.fragment_source("renderpass_accumulate_frag.glsl")
.sampler(1, ImageType::FLOAT_2D, "inputBuffer")
.fragment_out(0, Type::VEC4, "fragColor")
.do_static_compilation(true);
/* EEVEE_shaders_effect_mist_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_effect_mist_FIRST_PASS)
.define("FIRST_PASS")
.additional_info("draw_fullscreen")
.additional_info("draw_view")
.additional_info("eevee_legacy_common_lib")
.fragment_source("effect_mist_frag.glsl")
.push_constant(Type::VEC3, "mistSettings")
.sampler(0, ImageType::DEPTH_2D, "depthBuffer")
.fragment_out(0, Type::VEC4, "fragColor")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_ggx_lut_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_ggx_lut_bsdf)
.additional_info("draw_fullscreen")
.additional_info("eevee_legacy_common_lib")
.additional_info("eevee_legacy_common_utiltex_lib")
.fragment_source("bsdf_lut_frag.glsl")
.push_constant(Type::FLOAT, "sampleCount")
.fragment_out(0, Type::VEC2, "FragColor")
.do_static_compilation(true);
/* EEVEE_shaders_ggx_lut_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_ggx_lut_btdf)
.additional_info("draw_fullscreen")
.additional_info("eevee_legacy_common_lib")
.additional_info("eevee_legacy_common_utiltex_lib")
.fragment_source("btdf_lut_frag.glsl")
.push_constant(Type::FLOAT, "sampleCount")
.push_constant(Type::FLOAT, "z_factor")
.fragment_out(0, Type::VEC4, "FragColor")
.do_static_compilation(true);
/* Cryptomatte */
GPU_SHADER_CREATE_INFO(eevee_legacy_cryptomatte_common)
.additional_info("eevee_legacy_closure_type_lib")
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_view")
.additional_info("eevee_legacy_cryptomatte_lib")
.push_constant(Type::VEC4, "cryptohash")
.fragment_out(0, Type::VEC4, "fragColor")
.vertex_source("cryptomatte_vert.glsl")
.fragment_source("cryptomatte_frag.glsl");
GPU_SHADER_CREATE_INFO(eevee_legacy_cryptomatte_hair)
.define("HAIR_SHADER")
.define("NO_ATTRIB_LOAD")
.additional_info("eevee_legacy_cryptomatte_common")
.additional_info("eevee_legacy_mateiral_surface_vert_hair")
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_cryptomatte_mesh)
.define("MESH_SHADER")
.define("NO_ATTRIB_LOAD")
.additional_info("eevee_legacy_cryptomatte_common")
.additional_info("eevee_legacy_material_surface_vert")
.auto_resource_location(true)
.do_static_compilation(true);

@ -1,307 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
/* EEVEE_shaders_probe_filter_glossy_sh_get */
GPU_SHADER_INTERFACE_INFO(eevee_legacy_lightprobe_vert_geom_iface, "vert_iface")
.smooth(Type::VEC4, "vPos");
GPU_SHADER_INTERFACE_INFO(eevee_legacy_lightprobe_vert_geom_flat_iface, "vert_iface_flat")
.flat(Type::INT, "face");
GPU_SHADER_INTERFACE_INFO(eevee_legacy_lightprobe_geom_frag_iface, "geom_iface")
.smooth(Type::VEC3, "worldPosition")
.smooth(Type::VEC3, "viewPosition")
.smooth(Type::VEC3, "worldNormal")
.smooth(Type::VEC3, "viewNormal");
GPU_SHADER_INTERFACE_INFO(eevee_legacy_lightprobe_geom_frag_flat_iface, "geom_iface_flat")
.flat(Type::INT, "fFace");
GPU_SHADER_CREATE_INFO(eevee_legacy_lightprobe_vert)
.vertex_in(0, Type::VEC3, "pos")
.vertex_source("lightprobe_vert.glsl")
.vertex_out(eevee_legacy_lightprobe_vert_geom_iface)
.vertex_out(eevee_legacy_lightprobe_vert_geom_flat_iface)
.builtins(BuiltinBits::INSTANCE_ID);
#ifdef WITH_METAL_BACKEND
GPU_SHADER_CREATE_INFO(eevee_legacy_lightprobe_vert_no_geom)
.builtins(BuiltinBits::LAYER)
.vertex_in(0, Type::VEC3, "pos")
.push_constant(Type::INT, "Layer")
.vertex_source("lightprobe_vert_no_geom.glsl")
.vertex_out(eevee_legacy_lightprobe_geom_frag_iface)
.vertex_out(eevee_legacy_lightprobe_geom_frag_flat_iface)
.builtins(BuiltinBits::INSTANCE_ID);
#endif
GPU_SHADER_CREATE_INFO(eevee_legacy_lightprobe_geom)
.geometry_source("lightprobe_geom.glsl")
.geometry_out(eevee_legacy_lightprobe_geom_frag_iface)
.geometry_out(eevee_legacy_lightprobe_geom_frag_flat_iface)
.push_constant(Type::INT, "Layer")
.geometry_layout(PrimitiveIn::TRIANGLES, PrimitiveOut::TRIANGLE_STRIP, 3);
#ifdef WITH_METAL_BACKEND
GPU_SHADER_CREATE_INFO(eevee_legacy_probe_filter_glossy_no_geom)
.additional_info("eevee_legacy_lightprobe_vert_no_geom")
.fragment_source("lightprobe_filter_glossy_frag.glsl")
.sampler(0, ImageType::FLOAT_CUBE, "probeHdr")
.push_constant(Type::FLOAT, "probe_roughness")
.push_constant(Type::FLOAT, "texelSize")
.push_constant(Type::FLOAT, "lodFactor")
.push_constant(Type::FLOAT, "lodMax")
.push_constant(Type::FLOAT, "paddingSize")
.push_constant(Type::FLOAT, "intensityFac")
.push_constant(Type::FLOAT, "fireflyFactor")
.push_constant(Type::FLOAT, "sampleCount")
.fragment_out(0, Type::VEC4, "FragColor")
.metal_backend_only(true)
.do_static_compilation(true)
.auto_resource_location(true);
#endif
GPU_SHADER_CREATE_INFO(eevee_legacy_probe_filter_glossy)
.additional_info("eevee_legacy_lightprobe_vert")
.additional_info("eevee_legacy_lightprobe_geom")
.fragment_source("lightprobe_filter_glossy_frag.glsl")
.sampler(0, ImageType::FLOAT_CUBE, "probeHdr")
.push_constant(Type::FLOAT, "probe_roughness")
.push_constant(Type::FLOAT, "texelSize")
.push_constant(Type::FLOAT, "lodFactor")
.push_constant(Type::FLOAT, "lodMax")
.push_constant(Type::FLOAT, "paddingSize")
.push_constant(Type::FLOAT, "intensityFac")
.push_constant(Type::FLOAT, "fireflyFactor")
.push_constant(Type::FLOAT, "sampleCount")
.fragment_out(0, Type::VEC4, "FragColor")
.do_static_compilation(true)
.auto_resource_location(true);
/* EEVEE_shaders_effect_downsample_cube_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_effect_downsample_cube)
.additional_info("eevee_legacy_lightprobe_vert")
.additional_info("eevee_legacy_lightprobe_geom")
.fragment_source("effect_downsample_cube_frag.glsl")
.sampler(0, ImageType::FLOAT_CUBE, "source")
.push_constant(Type::FLOAT, "texelSize")
.fragment_out(0, Type::VEC4, "FragColor")
.do_static_compilation(true)
.auto_resource_location(true);
#ifdef WITH_METAL_BACKEND
GPU_SHADER_CREATE_INFO(eevee_legacy_effect_downsample_cube_no_geom)
.additional_info("eevee_legacy_lightprobe_vert_no_geom")
.fragment_source("effect_downsample_cube_frag.glsl")
.sampler(0, ImageType::FLOAT_CUBE, "source")
.push_constant(Type::FLOAT, "texelSize")
.fragment_out(0, Type::VEC4, "FragColor")
.builtins(BuiltinBits::LAYER)
.metal_backend_only(true)
.do_static_compilation(true)
.auto_resource_location(true);
#endif
/* EEVEE_shaders_probe_filter_diffuse_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_probe_filter_diffuse)
.additional_info("eevee_legacy_irradiance_lib")
.additional_info("draw_fullscreen")
.fragment_source("lightprobe_filter_diffuse_frag.glsl")
.sampler(0, ImageType::FLOAT_CUBE, "probeHdr")
.push_constant(Type::INT, "probeSize")
.push_constant(Type::FLOAT, "lodFactor")
.push_constant(Type::FLOAT, "lodMax")
.push_constant(Type::FLOAT, "intensityFac")
.push_constant(Type::FLOAT, "sampleCount")
.fragment_out(0, Type::VEC4, "FragColor")
.auto_resource_location(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_probe_filter_diffuse_sh_l2)
.define("IRRADIANCE_SH_L2")
.additional_info("eevee_legacy_probe_filter_diffuse")
.do_static_compilation(true)
.auto_resource_location(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_probe_filter_diffuse_hl2)
.define("IRRADIANCE_HL2")
.additional_info("eevee_legacy_probe_filter_diffuse")
.do_static_compilation(true)
.auto_resource_location(true);
/* EEVEE_shaders_probe_filter_visibility_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_probe_filter_visibility)
.define("IRRADIANCE_HL2")
.additional_info("eevee_legacy_irradiance_lib")
.additional_info("draw_fullscreen")
.fragment_source("lightprobe_filter_visibility_frag.glsl")
.sampler(0, ImageType::FLOAT_CUBE, "probeDepth")
.push_constant(Type::INT, "outputSize")
.push_constant(Type::FLOAT, "lodFactor")
.push_constant(Type::FLOAT, "storedTexelSize")
.push_constant(Type::FLOAT, "lodMax")
.push_constant(Type::FLOAT, "nearClip")
.push_constant(Type::FLOAT, "farClip")
.push_constant(Type::FLOAT, "visibilityRange")
.push_constant(Type::FLOAT, "visibilityBlur")
.push_constant(Type::FLOAT, "sampleCount")
.fragment_out(0, Type::VEC4, "FragColor")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_probe_grid_fill_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_probe_grid_fill)
.additional_info("draw_fullscreen")
.fragment_source("lightprobe_grid_fill_frag.glsl")
.sampler(0, ImageType::FLOAT_2D_ARRAY, "irradianceGrid")
.fragment_out(0, Type::VEC4, "FragColor")
.auto_resource_location(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_probe_grid_fill_sh_l2)
.define("IRRADIANCE_SH_L2")
.additional_info("eevee_legacy_probe_grid_fill")
.do_static_compilation(true)
.auto_resource_location(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_probe_grid_fill_hl2)
.define("IRRADIANCE_HL2")
.additional_info("eevee_legacy_probe_grid_fill")
.do_static_compilation(true)
.auto_resource_location(true);
/* EEVEE_shaders_probe_planar_display_sh_get */
GPU_SHADER_INTERFACE_INFO(legacy_probe_planar_iface, "")
.smooth(Type::VEC3, "worldPosition")
.flat(Type::INT, "probeIdx");
GPU_SHADER_CREATE_INFO(eevee_legacy_probe_planar_display)
.sampler(0, ImageType::FLOAT_2D_ARRAY, "probePlanars")
.vertex_in(0, Type::VEC3, "pos")
.vertex_in(1, Type::INT, "probe_id")
.vertex_in(2, Type::MAT4, "probe_mat")
.vertex_out(legacy_probe_planar_iface)
.vertex_source("lightprobe_planar_display_vert.glsl")
.fragment_source("lightprobe_planar_display_frag.glsl")
.additional_info("draw_view")
.fragment_out(0, Type::VEC4, "FragColor")
.do_static_compilation(true)
.auto_resource_location(true);
/* EEVEE_shaders_studiolight_probe_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_studiolight_probe)
.additional_info("draw_resource_id_varying")
.additional_info("eevee_legacy_lightprobe_lib")
.additional_info("eevee_legacy_surface_lib_lookdev")
.vertex_in(0, Type::VEC2, "pos")
.sampler(0, ImageType::FLOAT_2D, "studioLight")
.push_constant(Type::FLOAT, "backgroundAlpha")
.push_constant(Type::MAT3, "StudioLightMatrix")
.push_constant(Type::FLOAT, "studioLightIntensity")
.push_constant(Type::FLOAT, "studioLightBlur")
.fragment_out(0, Type::VEC4, "FragColor")
.vertex_source("background_vert.glsl")
.fragment_source("lookdev_world_frag.glsl")
.do_static_compilation(true)
.auto_resource_location(true);
/* EEVEE_shaders_studiolight_background_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_studiolight_background)
.define("LOOKDEV_BG")
.additional_info("eevee_legacy_studiolight_probe")
.do_static_compilation(true)
.auto_resource_location(true);
/* EEVEE_shaders_probe_planar_downsample_sh_get */
GPU_SHADER_INTERFACE_INFO(eevee_legacy_probe_planar_downsample_vert_geom_iface,
"lightprobe_vert_iface")
.smooth(Type::VEC2, "vPos");
GPU_SHADER_INTERFACE_INFO(eevee_legacy_probe_planar_downsample_vert_geom_flat_iface,
"lightprobe_vert_iface_flat")
.flat(Type::INT, "instance");
GPU_SHADER_INTERFACE_INFO(eevee_legacy_probe_planar_downsample_geom_frag_iface,
"lightprobe_geom_iface")
.flat(Type::FLOAT, "layer");
GPU_SHADER_CREATE_INFO(eevee_legacy_lightprobe_planar_downsample_common)
.vertex_source("lightprobe_planar_downsample_vert.glsl")
.fragment_source("lightprobe_planar_downsample_frag.glsl")
.vertex_out(eevee_legacy_probe_planar_downsample_vert_geom_iface)
.vertex_out(eevee_legacy_probe_planar_downsample_vert_geom_flat_iface)
.sampler(0, ImageType::FLOAT_2D_ARRAY, "source")
.push_constant(Type::FLOAT, "fireflyFactor")
.fragment_out(0, Type::VEC4, "FragColor")
.auto_resource_location(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_lightprobe_planar_downsample)
.additional_info("eevee_legacy_lightprobe_planar_downsample_common")
.geometry_source("lightprobe_planar_downsample_geom.glsl")
.geometry_out(eevee_legacy_probe_planar_downsample_geom_frag_iface)
.geometry_layout(PrimitiveIn::TRIANGLES, PrimitiveOut::TRIANGLE_STRIP, 3)
.do_static_compilation(true)
.auto_resource_location(true);
#ifdef WITH_METAL_BACKEND
GPU_SHADER_CREATE_INFO(eevee_legacy_lightprobe_planar_downsample_no_geom)
.additional_info("eevee_legacy_lightprobe_planar_downsample_common")
.builtins(BuiltinBits::LAYER)
.vertex_out(eevee_legacy_probe_planar_downsample_geom_frag_iface)
.metal_backend_only(true)
.do_static_compilation(true)
.auto_resource_location(true);
#endif
/* EEVEE_shaders_probe_cube_display_sh_get */
GPU_SHADER_INTERFACE_INFO(eevee_legacy_lightprobe_cube_display_iface, "")
.flat(Type::INT, "pid")
.smooth(Type::VEC2, "quadCoord");
GPU_SHADER_CREATE_INFO(eevee_legacy_lightprobe_cube_display)
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_view")
.additional_info("eevee_legacy_lightprobe_lib")
.vertex_source("lightprobe_cube_display_vert.glsl")
.fragment_source("lightprobe_cube_display_frag.glsl")
.vertex_out(eevee_legacy_lightprobe_cube_display_iface)
.push_constant(Type::FLOAT, "sphere_size")
.push_constant(Type::VEC3, "screen_vecs", 2)
.fragment_out(0, Type::VEC4, "FragColor")
.do_static_compilation(true)
.auto_resource_location(true);
/* EEVEE_shaders_probe_grid_display_sh_get */
GPU_SHADER_INTERFACE_INFO(eevee_legacy_lightprobe_grid_display_iface, "")
.flat(Type::INT, "cellOffset")
.smooth(Type::VEC2, "quadCoord");
GPU_SHADER_CREATE_INFO(eevee_legacy_lightprobe_grid_display_common)
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_view")
.additional_info("eevee_legacy_irradiance_lib")
.vertex_source("lightprobe_grid_display_vert.glsl")
.fragment_source("lightprobe_grid_display_frag.glsl")
.vertex_out(eevee_legacy_lightprobe_grid_display_iface)
.push_constant(Type::FLOAT, "sphere_size")
.push_constant(Type::INT, "offset")
.push_constant(Type::IVEC3, "grid_resolution")
.push_constant(Type::VEC3, "corner")
.push_constant(Type::VEC3, "increment_x")
.push_constant(Type::VEC3, "increment_y")
.push_constant(Type::VEC3, "increment_z")
.push_constant(Type::VEC3, "screen_vecs", 2)
.fragment_out(0, Type::VEC4, "FragColor")
.do_static_compilation(true)
.auto_resource_location(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_lightprobe_grid_display_common_sh_l2)
.define("IRRADIANCE_SH_L2")
.additional_info("eevee_legacy_lightprobe_grid_display_common")
.do_static_compilation(true)
.auto_resource_location(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_lightprobe_grid_display_common_hl2)
.define("IRRADIANCE_HL2")
.additional_info("eevee_legacy_lightprobe_grid_display_common")
.do_static_compilation(true)
.auto_resource_location(true);

@ -1,211 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "eevee_legacy_volume_info.hh"
#include "gpu_shader_create_info.hh"
/* For EEVEE Materials prepared in `eevee_shader_material_create_info_amend`,
* differing permutations are generated based on material options.
*
* Sources, e.g.
* -> datatoc_volumetric_vert_glsl
* -> datatoc_world_vert_glsl
* -> datatoc_surface_vert_glsl
*
* Are not included in the create-infos, but should have a corresponding
* Create info block, which defines bindings and other library requirements.
*/
/*** EMPTY EEVEE STUB COMMON INCLUDES following 'eevee_empty.glsl' and
* 'eevee_empty_volume.glsl'****/
GPU_SHADER_CREATE_INFO(eevee_legacy_material_empty_base)
.additional_info("eevee_legacy_closure_type_lib")
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_modelmat")
.additional_info("draw_view");
/* Volumetrics skips uniform bindings in `closure_type_lib`. */
GPU_SHADER_CREATE_INFO(eevee_legacy_material_empty_base_volume)
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_modelmat")
.additional_info("draw_view");
/**** MATERIAL VERTEX SHADER PERMUTATIONS ****/
/* -------------------------------------------------------------------- */
/** \name Volumetric
* \{ */
GPU_SHADER_CREATE_INFO(eevee_legacy_material_volumetric_vert)
.additional_info("eevee_legacy_material_empty_base_volume")
.vertex_out(legacy_volume_vert_geom_iface)
.additional_info("draw_resource_id_varying");
#ifdef WITH_METAL_BACKEND
GPU_SHADER_CREATE_INFO(eevee_legacy_material_volumetric_vert_no_geom)
.additional_info("eevee_legacy_material_empty_base_volume")
.builtins(BuiltinBits::LAYER)
.vertex_out(legacy_volume_vert_geom_iface)
.vertex_out(legacy_volume_geom_frag_iface)
.additional_info("draw_resource_id_varying");
#endif
/** \} */
/* -------------------------------------------------------------------- */
/** \name World Shader
* \{ */
GPU_SHADER_CREATE_INFO(eevee_legacy_material_world_vert)
.additional_info("eevee_legacy_material_empty_base")
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("eevee_legacy_closure_eval_surface_lib")
.additional_info("eevee_legacy_surface_lib_common")
.additional_info("draw_resource_id_varying")
.vertex_in(0, Type::VEC2, "pos");
/** \} */
/* -------------------------------------------------------------------- */
/** \name Surface Shader
* \{ */
GPU_SHADER_CREATE_INFO(eevee_legacy_material_surface_vert_common)
.additional_info("eevee_legacy_material_empty_base")
.additional_info("draw_resource_id_varying")
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("eevee_legacy_closure_eval_surface_lib")
/* Planar reflections assigns to gl_ClipDistance via surface_vert.glsl. */
.define("USE_CLIP_PLANES");
GPU_SHADER_CREATE_INFO(eevee_legacy_material_surface_vert)
.additional_info("eevee_legacy_material_surface_vert_common")
.additional_info("eevee_legacy_surface_lib_common")
.vertex_in(0, Type::VEC3, "pos")
.vertex_in(1, Type::VEC3, "nor");
GPU_SHADER_CREATE_INFO(eevee_legacy_mateiral_surface_vert_hair)
.additional_info("eevee_legacy_material_surface_vert_common")
.additional_info("eevee_legacy_surface_lib_hair")
.additional_info("eevee_legacy_hair_lib");
GPU_SHADER_CREATE_INFO(eevee_legacy_mateiral_surface_vert_pointcloud)
.additional_info("draw_pointcloud")
.additional_info("eevee_legacy_material_surface_vert_common")
.additional_info("eevee_legacy_surface_lib_pointcloud")
.auto_resource_location(true);
/**** MATERIAL GEOMETRY SHADER PERMUTATIONS ****/
/** \} */
/* -------------------------------------------------------------------- */
/** \name Volumetric
* \{ */
GPU_SHADER_CREATE_INFO(eevee_legacy_material_volumetric_geom)
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_view")
.geometry_out(legacy_volume_geom_frag_iface)
.geometry_layout(PrimitiveIn::TRIANGLES, PrimitiveOut::TRIANGLE_STRIP, 3)
.additional_info("draw_resource_id_varying");
/** \} */
/**** MATERIAL FRAGMENT SHADER PERMUTATIONS ****/
/* -------------------------------------------------------------------- */
/** \name Volumetric Shader
* \{ */
GPU_SHADER_CREATE_INFO(eevee_legacy_material_volumetric_frag)
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_view")
.additional_info("draw_resource_id_varying")
.additional_info("eevee_legacy_volumetric_lib")
.fragment_out(0, Type::VEC4, "volumeScattering")
.fragment_out(1, Type::VEC4, "volumeExtinction")
.fragment_out(2, Type::VEC4, "volumeEmissive")
.fragment_out(3, Type::VEC4, "volumePhase");
/** \} */
/* -------------------------------------------------------------------- */
/** \name Pre-pass Shader
* \{ */
/* Common info for all `prepass_frag` variants. */
GPU_SHADER_CREATE_INFO(eevee_legacy_material_prepass_frag_common)
.additional_info("eevee_legacy_common_lib")
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("draw_view")
.additional_info("eevee_legacy_closure_eval_surface_lib");
/* Common info for all `prepass_frag_opaque` variants. */
GPU_SHADER_CREATE_INFO(eevee_legacy_material_prepass_frag_opaque_common)
.additional_info("eevee_legacy_material_prepass_frag_common");
GPU_SHADER_CREATE_INFO(eevee_legacy_material_prepass_frag_opaque)
.additional_info("eevee_legacy_surface_lib_common")
.additional_info("eevee_legacy_material_prepass_frag_opaque_common");
GPU_SHADER_CREATE_INFO(eevee_legacy_material_prepass_frag_opaque_hair)
.additional_info("eevee_legacy_surface_lib_hair")
.additional_info("eevee_legacy_material_prepass_frag_opaque_common")
.additional_info("draw_hair");
GPU_SHADER_CREATE_INFO(eevee_legacy_material_prepass_frag_opaque_pointcloud)
.additional_info("eevee_legacy_material_prepass_frag_opaque_common")
.additional_info("draw_pointcloud");
/* Common info for all `prepass_frag_alpha_hash` variants. */
GPU_SHADER_CREATE_INFO(eevee_legacy_material_prepass_frag_alpha_hash_common)
.define("USE_ALPHA_HASH")
.additional_info("eevee_legacy_material_prepass_frag_common")
.push_constant(Type::FLOAT, "alphaClipThreshold");
GPU_SHADER_CREATE_INFO(eevee_legacy_material_prepass_frag_alpha_hash)
.additional_info("eevee_legacy_surface_lib_common")
.additional_info("eevee_legacy_material_prepass_frag_alpha_hash_common");
GPU_SHADER_CREATE_INFO(eevee_legacy_material_prepass_frag_alpha_hash_hair)
.additional_info("eevee_legacy_surface_lib_hair")
.additional_info("eevee_legacy_material_prepass_frag_alpha_hash_common")
.additional_info("draw_hair");
GPU_SHADER_CREATE_INFO(eevee_legacy_material_prepass_frag_alpha_hash_pointcloud)
.additional_info("eevee_legacy_surface_lib_pointcloud")
.additional_info("eevee_legacy_material_prepass_frag_alpha_hash_common")
.additional_info("draw_pointcloud");
/** \} */
/* -------------------------------------------------------------------- */
/** \name Surface Shader
* \{ */
GPU_SHADER_CREATE_INFO(eevee_legacy_material_surface_frag_common)
.additional_info("eevee_legacy_common_lib")
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("eevee_legacy_closure_eval_surface_lib")
.additional_info("eevee_legacy_renderpass_lib")
.additional_info("eevee_legacy_volumetric_lib")
.push_constant(Type::FLOAT, "backgroundAlpha");
GPU_SHADER_CREATE_INFO(eevee_legacy_material_surface_frag_opaque)
.additional_info("eevee_legacy_material_surface_frag_common")
.fragment_out(0, Type::VEC4, "outRadiance")
.fragment_out(1, Type::VEC2, "ssrNormals")
.fragment_out(2, Type::VEC4, "ssrData")
.fragment_out(3, Type::VEC3, "sssIrradiance")
.fragment_out(4, Type::FLOAT, "sssRadius")
.fragment_out(5, Type::VEC3, "sssAlbedo");
GPU_SHADER_CREATE_INFO(eevee_legacy_material_surface_frag_alpha_blend)
.define("USE_ALPHA_BLEND")
.additional_info("eevee_legacy_material_surface_frag_common")
.fragment_out(0, Type::VEC4, "outRadiance", DualBlend::SRC_0)
.fragment_out(0, Type::VEC4, "outTransmittance", DualBlend::SRC_1);
/** \} */

@ -1,90 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
/* EEVEE_shaders_effect_motion_blur_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_effect_motion_blur)
.additional_info("eevee_legacy_common_lib")
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("draw_fullscreen")
.additional_info("eevee_legacy_defines_info")
.fragment_source("effect_motion_blur_frag.glsl")
.sampler(0, ImageType::FLOAT_2D, "colorBuffer")
.sampler(1, ImageType::DEPTH_2D, "depthBuffer")
.sampler(2, ImageType::FLOAT_2D, "velocityBuffer")
.sampler(3, ImageType::FLOAT_2D, "tileMaxBuffer")
.push_constant(Type::FLOAT, "depthScale")
.push_constant(Type::IVEC2, "tileBufferSize")
.push_constant(Type::VEC2, "viewportSize")
.push_constant(Type::VEC2, "viewportSizeInv")
.push_constant(Type::BOOL, "isPerspective")
.push_constant(Type::VEC2, "nearFar")
.fragment_out(0, Type::VEC4, "fragColor")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_effect_motion_blur_object_sh_get */
GPU_SHADER_INTERFACE_INFO(eevee_legacy_motion_object_iface, "")
.smooth(Type::VEC3, "currWorldPos")
.smooth(Type::VEC3, "prevWorldPos")
.smooth(Type::VEC3, "nextWorldPos");
GPU_SHADER_CREATE_INFO(eevee_legacy_effect_motion_blur_object_common)
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_view")
.vertex_source("object_motion_vert.glsl")
.fragment_source("object_motion_frag.glsl")
.vertex_out(eevee_legacy_motion_object_iface)
.fragment_out(0, Type::VEC4, "outData")
.push_constant(Type::MAT4, "currModelMatrix")
.push_constant(Type::MAT4, "prevModelMatrix")
.push_constant(Type::MAT4, "nextModelMatrix")
.push_constant(Type::MAT4, "prevViewProjMatrix")
.push_constant(Type::MAT4, "currViewProjMatrix")
.push_constant(Type::MAT4, "nextViewProjMatrix")
.push_constant(Type::BOOL, "useDeform");
GPU_SHADER_CREATE_INFO(eevee_legacy_effect_motion_blur_object_hair)
.define("HAIR")
.define("HAIR_SHADER")
.additional_info("eevee_legacy_hair_lib")
.additional_info("eevee_legacy_effect_motion_blur_object_common")
.sampler(0, ImageType::FLOAT_BUFFER, "prvBuffer")
.sampler(1, ImageType::FLOAT_BUFFER, "nxtBuffer")
.do_static_compilation(true)
.auto_resource_location(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_effect_motion_blur_object)
.additional_info("eevee_legacy_effect_motion_blur_object_common")
.vertex_in(0, Type::VEC3, "pos")
.vertex_in(1, Type::VEC3, "prv")
.vertex_in(2, Type::VEC3, "nxt")
.do_static_compilation(true)
.auto_resource_location(true);
/* EEVEE_shaders_effect_motion_blur_velocity_tiles_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_effect_motion_blur_velocity_tiles_common)
.additional_info("draw_fullscreen")
.additional_info("eevee_legacy_defines_info")
.fragment_source("effect_velocity_tile_frag.glsl")
.sampler(0, ImageType::FLOAT_2D, "velocityBuffer")
.push_constant(Type::VEC2, "viewportSize")
.push_constant(Type::VEC2, "viewportSizeInv")
.push_constant(Type::IVEC2, "velocityBufferSize")
.fragment_out(0, Type::VEC4, "tileMaxVelocity")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_effect_motion_blur_velocity_tiles_GATHER)
.define("TILE_GATHER")
.additional_info("eevee_legacy_effect_motion_blur_velocity_tiles_common")
.push_constant(Type::IVEC2, "gatherStep")
.do_static_compilation(true)
.auto_resource_location(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_effect_motion_blur_velocity_tiles_EXPANSION)
.define("TILE_EXPANSION")
.additional_info("eevee_legacy_effect_motion_blur_velocity_tiles_common")
.do_static_compilation(true)
.auto_resource_location(true);

@ -1,33 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
/* EEVEE_shaders_shadow_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_shader_shadow)
.additional_info("draw_view")
.additional_info("draw_modelmat")
.additional_info("draw_curves_infos")
.additional_info("eevee_legacy_hair_lib")
.additional_info("eevee_legacy_surface_lib_common")
.additional_info("eevee_legacy_surface_lib_hair")
.vertex_in(0, Type::VEC3, "pos")
.vertex_in(1, Type::VEC3, "nor")
.vertex_source("shadow_vert.glsl")
.fragment_source("shadow_frag.glsl")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_shadow_accum_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_shader_shadow_accum)
.additional_info("draw_fullscreen")
.additional_info("draw_view")
.additional_info("eevee_legacy_common_lib")
.additional_info("eevee_legacy_common_utiltex_lib")
.additional_info("eevee_legacy_lights_lib")
.fragment_source("shadow_accum_frag.glsl")
.sampler(0, ImageType::DEPTH_2D, "depthBuffer")
.fragment_out(0, Type::VEC4, "fragColor")
.auto_resource_location(true)
.do_static_compilation(true);

@ -1,229 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
#pragma once
/* Volumetric iface. */
GPU_SHADER_INTERFACE_INFO(legacy_volume_vert_geom_iface, "volumetric_vert_iface")
.smooth(Type::VEC4, "vPos");
GPU_SHADER_INTERFACE_INFO(legacy_volume_geom_frag_iface, "volumetric_geom_iface")
.flat(Type::INT, "slice");
/* EEVEE_shaders_volumes_clear_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_clear)
.define("STANDALONE")
.define("VOLUMETRICS")
.define("CLEAR")
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_view")
.additional_info("draw_resource_id_varying")
.additional_info("eevee_legacy_volumetric_lib")
.vertex_source("volumetric_vert.glsl")
.geometry_source("volumetric_geom.glsl")
.fragment_source("volumetric_frag.glsl")
.vertex_out(legacy_volume_vert_geom_iface)
.geometry_out(legacy_volume_geom_frag_iface)
.geometry_layout(PrimitiveIn::TRIANGLES, PrimitiveOut::TRIANGLE_STRIP, 3)
.fragment_out(0, Type::VEC4, "volumeScattering")
.fragment_out(1, Type::VEC4, "volumeExtinction")
.fragment_out(2, Type::VEC4, "volumeEmissive")
.fragment_out(3, Type::VEC4, "volumePhase")
.do_static_compilation(true)
.auto_resource_location(true);
#ifdef WITH_METAL_BACKEND
/* Non-geometry shader equivalent for multilayered rendering.
* NOTE: Layer selection can be done in vertex shader, and thus
* vertex shader emits both vertex and geometry shader output
* interfaces. */
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_clear_no_geom)
.define("STANDALONE")
.define("VOLUMETRICS")
.define("CLEAR")
.builtins(BuiltinBits::LAYER)
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_view")
.additional_info("draw_resource_id_varying")
.additional_info("eevee_legacy_volumetric_lib")
.vertex_source("volumetric_vert.glsl")
.fragment_source("volumetric_frag.glsl")
.vertex_out(legacy_volume_vert_geom_iface)
.vertex_out(legacy_volume_geom_frag_iface)
.fragment_out(0, Type::VEC4, "volumeScattering")
.fragment_out(1, Type::VEC4, "volumeExtinction")
.fragment_out(2, Type::VEC4, "volumeEmissive")
.fragment_out(3, Type::VEC4, "volumePhase")
.metal_backend_only(true)
.do_static_compilation(true)
.auto_resource_location(true);
#endif
/* EEVEE_shaders_volumes_scatter_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_scatter_common)
.define("STANDALONE")
.define("VOLUMETRICS")
.define("VOLUME_SHADOW")
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_view")
.additional_info("draw_resource_id_varying")
.additional_info("eevee_legacy_volumetric_lib")
/* NOTE: Unique sampler IDs assigned for consistency between library includes,
* and to avoid unique assignment collision validation error.
* However, resources will be auto assigned locations within shader usage. */
.sampler(15, ImageType::FLOAT_3D, "volumeScattering")
.sampler(16, ImageType::FLOAT_3D, "volumeExtinction")
.sampler(17, ImageType::FLOAT_3D, "volumeEmission")
.sampler(18, ImageType::FLOAT_3D, "volumePhase")
.sampler(19, ImageType::FLOAT_3D, "historyScattering")
.sampler(20, ImageType::FLOAT_3D, "historyTransmittance")
.fragment_out(0, Type::VEC4, "outScattering")
.fragment_out(1, Type::VEC4, "outTransmittance")
.vertex_source("volumetric_vert.glsl")
.fragment_source("volumetric_scatter_frag.glsl")
.vertex_out(legacy_volume_vert_geom_iface);
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_scatter)
.additional_info("eevee_legacy_volumes_scatter_common")
.geometry_source("volumetric_geom.glsl")
.geometry_out(legacy_volume_geom_frag_iface)
.geometry_layout(PrimitiveIn::TRIANGLES, PrimitiveOut::TRIANGLE_STRIP, 3)
.do_static_compilation(true)
.auto_resource_location(true);
#ifdef WITH_METAL_BACKEND
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_scatter_no_geom)
.additional_info("eevee_legacy_volumes_scatter_common")
.builtins(BuiltinBits::LAYER)
.vertex_out(legacy_volume_geom_frag_iface)
.metal_backend_only(true)
.do_static_compilation(true)
.auto_resource_location(true);
#endif
/* EEVEE_shaders_volumes_scatter_with_lights_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_scatter_with_lights_common)
.define("VOLUME_LIGHTING")
.define("IRRADIANCE_HL2");
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_scatter_with_lights)
.additional_info("eevee_legacy_volumes_scatter_with_lights_common")
.additional_info("eevee_legacy_volumes_scatter")
.do_static_compilation(true)
.auto_resource_location(true);
#ifdef WITH_METAL_BACKEND
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_scatter_with_lights_no_geom)
.additional_info("eevee_legacy_volumes_scatter_with_lights_common")
.additional_info("eevee_legacy_volumes_scatter_no_geom")
.metal_backend_only(true)
.do_static_compilation(true)
.auto_resource_location(true);
#endif
/* EEVEE_shaders_volumes_integration_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_integration_common)
.define("STANDALONE")
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_view")
.additional_info("eevee_legacy_volumetric_lib")
.additional_info("draw_resource_id_varying")
/* NOTE: Unique sampler IDs assigned for consistency between library includes,
* and to avoid unique assignment collision validation error.
* However, resources will be auto assigned locations within shader usage. */
.sampler(20, ImageType::FLOAT_3D, "volumeScattering")
.sampler(21, ImageType::FLOAT_3D, "volumeExtinction")
.vertex_out(legacy_volume_vert_geom_iface)
.vertex_source("volumetric_vert.glsl")
.fragment_source("volumetric_integration_frag.glsl");
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_integration_common_opti)
.define("USE_VOLUME_OPTI")
.image(0, GPU_R11F_G11F_B10F, Qualifier::WRITE, ImageType::FLOAT_3D, "finalScattering_img")
.image(1, GPU_R11F_G11F_B10F, Qualifier::WRITE, ImageType::FLOAT_3D, "finalTransmittance_img");
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_integration_common_no_opti)
.fragment_out(0, Type::VEC3, "finalScattering")
.fragment_out(1, Type::VEC3, "finalTransmittance");
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_integration_common_geom)
.additional_info("eevee_legacy_volumes_integration_common")
.geometry_source("volumetric_geom.glsl")
.geometry_out(legacy_volume_geom_frag_iface)
.geometry_layout(PrimitiveIn::TRIANGLES, PrimitiveOut::TRIANGLE_STRIP, 3);
#ifdef WITH_METAL_BACKEND
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_integration_common_no_geom)
.additional_info("eevee_legacy_volumes_integration_common")
.builtins(BuiltinBits::LAYER)
.vertex_out(legacy_volume_geom_frag_iface);
#endif
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_integration)
.additional_info("eevee_legacy_volumes_integration_common_geom")
.additional_info("eevee_legacy_volumes_integration_common_no_opti")
.do_static_compilation(true)
.auto_resource_location(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_integration_OPTI)
.additional_info("eevee_legacy_volumes_integration_common_geom")
.additional_info("eevee_legacy_volumes_integration_common_opti")
.do_static_compilation(true)
.auto_resource_location(true);
#ifdef WITH_METAL_BACKEND
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_integration_no_geom)
.additional_info("eevee_legacy_volumes_integration_common_no_geom")
.additional_info("eevee_legacy_volumes_integration_common_no_opti")
.metal_backend_only(true)
.do_static_compilation(true)
.auto_resource_location(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_integration_OPTI_no_geom)
.additional_info("eevee_legacy_volumes_integration_common_no_geom")
.additional_info("eevee_legacy_volumes_integration_common_opti")
.metal_backend_only(true)
.do_static_compilation(true)
.auto_resource_location(true);
#endif
/* EEVEE_shaders_volumes_resolve_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_resolve_common)
.additional_info("draw_fullscreen")
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_view")
.additional_info("eevee_legacy_volumetric_lib")
.sampler(0, ImageType::DEPTH_2D, "inSceneDepth")
.fragment_source("volumetric_resolve_frag.glsl")
.auto_resource_location(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_resolve)
.additional_info("eevee_legacy_volumes_resolve_common")
.fragment_out(0, Type::VEC4, "FragColor0", DualBlend::SRC_0)
.fragment_out(0, Type::VEC4, "FragColor1", DualBlend::SRC_1)
.auto_resource_location(true)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_resolve_accum)
.define("VOLUMETRICS_ACCUM")
.additional_info("eevee_legacy_volumes_resolve_common")
.fragment_out(0, Type::VEC4, "FragColor0")
.fragment_out(1, Type::VEC4, "FragColor1")
.auto_resource_location(true)
.do_static_compilation(true);
/* EEVEE_shaders_volumes_accum_sh_get */
GPU_SHADER_CREATE_INFO(eevee_legacy_volumes_accum)
.additional_info("draw_fullscreen")
.additional_info("eevee_legacy_common_lib")
.additional_info("draw_view")
.additional_info("eevee_legacy_volumetric_lib")
.fragment_out(0, Type::VEC4, "FragColor0")
.fragment_out(1, Type::VEC4, "FragColor1")
.fragment_source("volumetric_accum_frag.glsl")
.auto_resource_location(true)
.do_static_compilation(true);

@ -1,272 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*/
#ifndef EEVEE_SHADER_SHARED_H
#define EEVEE_SHADER_SHARED_H
#ifndef GPU_SHADER
typedef struct CommonUniformBlock CommonUniformBlock;
#endif
#ifdef GPU_SHADER
/* Catch for non-create info case. */
# ifndef BLI_STATIC_ASSERT_ALIGN
# define BLI_STATIC_ASSERT_ALIGN(type, alignment)
# endif
#endif
/* NOTE: AMD-based macOS platforms experience performance and correctness issues with EEVEE
* material closure evaluation. Using singular closure evaluation, rather than the compound
* function calls reduces register overflow, by limiting the simultaneous number of live
* registers used by the virtual GPU function stack. */
#if (defined(GPU_METAL) && defined(GPU_ATI))
# define DO_SPLIT_CLOSURE_EVAL 1
#endif
struct CommonUniformBlock {
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;
bool32_t _volUseLights;
bool32_t _volUseSoftShadows;
/* Screen Space Reflections */
vec4 _ssrParameters;
float _ssrBorderFac;
float _ssrMaxRoughness;
float _ssrFireflyFac;
float _ssrBrdfBias;
bool32_t _ssrToggle;
bool32_t _ssrefractToggle;
/* SubSurface Scattering */
float _sssJitterThreshold;
bool32_t _sssToggle;
/* Specular */
bool32_t _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;
vec4 _cameraUvScaleBias;
vec4 _planarClipPlane;
};
BLI_STATIC_ASSERT_ALIGN(CommonUniformBlock, 16)
struct CubeData {
vec4 position_type;
vec4 attenuation_fac_type;
mat4 influencemat;
mat4 parallaxmat;
};
BLI_STATIC_ASSERT_ALIGN(CubeData, 16)
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;
};
BLI_STATIC_ASSERT_ALIGN(PlanarData, 16)
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;
};
BLI_STATIC_ASSERT_ALIGN(GridData, 16)
struct ProbeBlock {
CubeData _probes_data[MAX_PROBE];
};
BLI_STATIC_ASSERT_ALIGN(ProbeBlock, 16)
struct GridBlock {
GridData _grids_data[MAX_GRID];
};
BLI_STATIC_ASSERT_ALIGN(GridBlock, 16)
struct PlanarBlock {
PlanarData _planars_data[MAX_PLANAR];
};
BLI_STATIC_ASSERT_ALIGN(PlanarBlock, 16)
#ifndef MAX_CASCADE_NUM
# define MAX_CASCADE_NUM 4
#endif
struct ShadowData {
vec4 near_far_bias_id;
vec4 contact_shadow_data;
};
BLI_STATIC_ASSERT_ALIGN(ShadowData, 16)
struct ShadowCubeData {
mat4 shadowmat;
vec4 position;
};
BLI_STATIC_ASSERT_ALIGN(ShadowCubeData, 16)
struct ShadowCascadeData {
mat4 shadowmat[MAX_CASCADE_NUM];
vec4 split_start_distances;
vec4 split_end_distances;
vec4 shadow_vec_id;
};
BLI_STATIC_ASSERT_ALIGN(ShadowCascadeData, 16)
struct ShadowBlock {
ShadowData _shadows_data[MAX_SHADOW];
ShadowCubeData _shadows_cube_data[MAX_SHADOW_CUBE];
ShadowCascadeData _shadows_cascade_data[MAX_SHADOW_CASCADE];
};
BLI_STATIC_ASSERT_ALIGN(ShadowBlock, 16)
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. */
};
BLI_STATIC_ASSERT_ALIGN(LightData, 16)
struct LightBlock {
LightData _lights_data[MAX_LIGHT];
};
BLI_STATIC_ASSERT_ALIGN(LightBlock, 16)
struct RenderpassBlock {
bool32_t _renderPassDiffuse;
bool32_t _renderPassDiffuseLight;
bool32_t _renderPassGlossy;
bool32_t _renderPassGlossyLight;
bool32_t _renderPassEmit;
bool32_t _renderPassSSSColor;
bool32_t _renderPassEnvironment;
bool32_t _renderPassAOV;
uint _renderPassAOVActive;
};
BLI_STATIC_ASSERT_ALIGN(RenderpassBlock, 16)
#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))
struct SSSProfileBlock {
vec4 _sss_kernel[MAX_SSS_SAMPLES];
vec4 _radii_max_radius;
float _avg_inv_radius;
int _sss_samples;
};
BLI_STATIC_ASSERT_ALIGN(SSSProfileBlock, 16)
#ifdef GPU_SHADER
# if defined(USE_GPU_SHADER_CREATE_INFO)
/* Keep compatibility_with old global scope syntax. */
# define pastViewProjectionMatrix common_block._pastViewProjectionMatrix
# define hizUvScale common_block._hizUvScale
# define aoParameters common_block._aoParameters
# define volTexSize common_block._volTexSize
# define volDepthParameters common_block._volDepthParameters
# define volInvTexSize common_block._volInvTexSize
# define volJitter common_block._volJitter
# define volCoordScale common_block._volCoordScale
# define volHistoryAlpha common_block._volHistoryAlpha
# define volShadowSteps common_block._volShadowSteps
# define volUseLights common_block._volUseLights
# define volUseSoftShadows common_block._volUseSoftShadows
# define ssrParameters common_block._ssrParameters
# define ssrBorderFac common_block._ssrBorderFac
# define ssrMaxRoughness common_block._ssrMaxRoughness
# define ssrFireflyFac common_block._ssrFireflyFac
# define ssrBrdfBias common_block._ssrBrdfBias
# define ssrToggle common_block._ssrToggle
# define ssrefractToggle common_block._ssrefractToggle
# define sssJitterThreshold common_block._sssJitterThreshold
# define sssToggle common_block._sssToggle
# define specToggle common_block._specToggle
# define laNumLight common_block._laNumLight
# define prbNumPlanar common_block._prbNumPlanar
# define prbNumRenderCube common_block._prbNumRenderCube
# define prbNumRenderGrid common_block._prbNumRenderGrid
# define prbIrradianceVisSize common_block._prbIrradianceVisSize
# define prbIrradianceSmooth common_block._prbIrradianceSmooth
# define prbLodCubeMax common_block._prbLodCubeMax
# define rayType common_block._rayType
# define rayDepth common_block._rayDepth
# define alphaHashOffset common_block._alphaHashOffset
# define alphaHashScale common_block._alphaHashScale
# define cameraUvScaleBias common_block._cameraUvScaleBias
# define planarClipPlane common_block._planarClipPlane
/* ProbeBlock */
# define probes_data probe_block._probes_data
/* GridBlock */
# define grids_data grid_block._grids_data
/* PlanarBlock */
# define planars_data planar_block._planars_data
/* ShadowBlock */
# define shadows_data shadow_block._shadows_data
# define shadows_cube_data shadow_block._shadows_cube_data
# define shadows_cascade_data shadow_block._shadows_cascade_data
/* LightBlock */
# define lights_data light_block._lights_data
/* RenderpassBlock */
# define renderPassDiffuse renderpass_block._renderPassDiffuse
# define renderPassDiffuseLight renderpass_block._renderPassDiffuseLight
# define renderPassGlossy renderpass_block._renderPassGlossy
# define renderPassGlossyLight renderpass_block._renderPassGlossyLight
# define renderPassEmit renderpass_block._renderPassEmit
# define renderPassSSSColor renderpass_block._renderPassSSSColor
# define renderPassEnvironment renderpass_block._renderPassEnvironment
# define renderPassAOV renderpass_block._renderPassAOV
# define renderPassAOVActive renderpass_block._renderPassAOVActive
/* SSSProfileBlock */
# define sss_kernel sssProfile._sss_kernel
# define radii_max_radius sssProfile._radii_max_radius
# define avg_inv_radius sssProfile._avg_inv_radius
# define sss_samples sssProfile._sss_samples
# endif /* USE_GPU_SHADER_CREATE_INFO */
#endif
#endif

@ -1,207 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#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 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 re-scaling [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);
}
/** \} */

@ -1,21 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
void main()
{
float dist_sqr = dot(quadCoord, quadCoord);
/* Discard outside the circle. */
if (dist_sqr > 1.0) {
discard;
return;
}
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(probeCubes, vec4(world_ref, pid), 0.0).rgb, 1.0);
}

@ -1,31 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
void main()
{
/* Constant array moved inside function scope.
* Minimizes local register allocation in MSL. */
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));
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 = ViewMatrixInverse[0].xyz * quadCoord.x +
ViewMatrixInverse[1].xyz * quadCoord.y;
ws_location += screen_pos * sphere_size;
gl_Position = ProjectionMatrix * (ViewMatrix * vec4(ws_location, 1.0));
gl_Position.z += 0.0001; /* Small bias to let the icon draw without Z-fighting. */
}

@ -1,162 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#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)
#define M_4PI 12.5663706143591729
vec3 get_cubemap_vector(vec2 co, int face)
{
/* NOTE(Metal): Declaring constant array in function scope to avoid increasing local shader
* memory pressure. */
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)));
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: extremely 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 environment-map. */
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 -> Cube-map 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
}

@ -1,67 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(random_lib.glsl)
#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
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(geom_iface.worldPosition);
/* Isotropic assumption */
N = V = R;
make_orthonormal_basis(N, T, B); /* Generate tangent space */
/* Integrating environment-map. */
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, probe_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 -> Cube-map 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);
}

@ -1,85 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#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)
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 cube-map 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);
}

@ -1,38 +0,0 @@
/* SPDX-FileCopyrightText: 2017-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
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()
{
geom_iface_flat.fFace = vert_iface_flat[0].face;
gl_Layer = Layer + geom_iface_flat.fFace;
for (int v = 0; v < 3; v++) {
gl_Position = vert_iface[v].vPos;
geom_iface.worldPosition = x_axis[geom_iface_flat.fFace] * vert_iface[v].vPos.x +
y_axis[geom_iface_flat.fFace] * vert_iface[v].vPos.y +
maj_axes[geom_iface_flat.fFace];
gpu_EmitVertex();
}
EndPrimitive();
}

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