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:
parent
76f4a4da6a
commit
cc0d12dd20
@ -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 cap’s radius (arc length in radians)
|
||||
* radius2 : Second caps’ radius (in radians)
|
||||
* dist : Distance between caps (radians between centers of caps)
|
||||
* NOTE: Result is divided by pi to save one multiply.
|
||||
*/
|
||||
float spherical_cap_intersection(float radius1, float radius2, float dist)
|
||||
{
|
||||
/* From "Ambient Aperture Lighting" by Chris Oat
|
||||
* Slide 15. */
|
||||
float max_radius = max(radius1, radius2);
|
||||
float min_radius = min(radius1, radius2);
|
||||
float sum_radius = radius1 + radius2;
|
||||
float area;
|
||||
if (dist <= max_radius - min_radius) {
|
||||
/* One cap in completely inside the other */
|
||||
area = 1.0 - cos(min_radius);
|
||||
}
|
||||
else if (dist >= sum_radius) {
|
||||
/* No intersection exists */
|
||||
area = 0;
|
||||
}
|
||||
else {
|
||||
float diff = max_radius - min_radius;
|
||||
area = smoothstep(0.0, 1.0, 1.0 - saturate((dist - diff) / (sum_radius - diff)));
|
||||
area *= 1.0 - cos(min_radius);
|
||||
}
|
||||
return area;
|
||||
}
|
||||
|
||||
float specular_occlusion(
|
||||
OcclusionData data, vec3 V, vec3 N, float roughness, inout vec3 specular_dir)
|
||||
{
|
||||
vec3 visibility_dir;
|
||||
float visibility_error;
|
||||
float visibility;
|
||||
occlusion_eval(data, V, N, N, 0.0, visibility, visibility_error, visibility_dir);
|
||||
|
||||
/* Correct visibility error for very sharp surfaces. */
|
||||
visibility *= mix(safe_rcp(visibility_error), 1.0, roughness);
|
||||
|
||||
specular_dir = normalize(mix(specular_dir, visibility_dir, roughness * (1.0 - visibility)));
|
||||
|
||||
/* Visibility to cone angle (eq. 18). */
|
||||
float vis_angle = fast_acos(sqrt(1 - visibility));
|
||||
/* Roughness to cone angle (eq. 26). */
|
||||
float spec_angle = max(0.00990998744964599609375, fast_acos(cone_cosine(roughness)));
|
||||
/* Angle between cone axes. */
|
||||
float cone_cone_dist = fast_acos(saturate(dot(visibility_dir, specular_dir)));
|
||||
float cone_nor_dist = fast_acos(saturate(dot(N, specular_dir)));
|
||||
|
||||
float isect_solid_angle = spherical_cap_intersection(vis_angle, spec_angle, cone_cone_dist);
|
||||
float specular_solid_angle = spherical_cap_intersection(M_PI_2, spec_angle, cone_nor_dist);
|
||||
float specular_occlusion = isect_solid_angle / specular_solid_angle;
|
||||
/* Mix because it is unstable in unoccluded areas. */
|
||||
float tmp = saturate(pow8(visibility));
|
||||
visibility = mix(specular_occlusion, 1.0, tmp);
|
||||
|
||||
/* Scale by user factor */
|
||||
visibility = 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
Loading…
Reference in New Issue
Block a user