diff options
417 files changed, 30214 insertions, 28796 deletions
diff --git a/.clang-format b/.clang-format index 41f828787b2..cd8d4c66674 100644 --- a/.clang-format +++ b/.clang-format @@ -264,6 +264,9 @@ ForEachMacros: - SET_SLOT_PROBING_BEGIN - MAP_SLOT_PROBING_BEGIN - VECTOR_SET_SLOT_PROBING_BEGIN + - LIGHT_FOREACH_BEGIN_DIRECTIONAL + - LIGHT_FOREACH_BEGIN_LOCAL + - LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL StatementMacros: - PyObject_HEAD diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py index edd0623d8fe..20bee833847 100644 --- a/release/scripts/startup/bl_ui/properties_data_camera.py +++ b/release/scripts/startup/bl_ui/properties_data_camera.py @@ -117,8 +117,21 @@ class DATA_PT_lens(CameraButtonsPanel, Panel): col.prop(ccam, "fisheye_polynomial_k2", text="K2") col.prop(ccam, "fisheye_polynomial_k3", text="K3") col.prop(ccam, "fisheye_polynomial_k4", text="K4") - - elif engine in {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}: + elif engine == 'BLENDER_EEVEE': + col.prop(cam, "panorama_type") + if cam.panorama_type == 'FISHEYE_EQUIDISTANT': + col.prop(cam, "fisheye_fov") + elif cam.panorama_type == 'FISHEYE_EQUISOLID': + col.prop(cam, "fisheye_lens", text="Lens") + col.prop(cam, "fisheye_fov") + elif cam.panorama_type == 'EQUIRECTANGULAR': + sub = col.column(align=True) + sub.prop(cam, "latitude_min", text="Latitude Min") + sub.prop(cam, "latitude_max", text="Max") + sub = col.column(align=True) + sub.prop(cam, "longitude_min", text="Longitude Min") + sub.prop(cam, "longitude_max", text="Max") + elif engine in {'BLENDER_RENDER', 'BLENDER_WORKBENCH'}: if cam.lens_unit == 'MILLIMETERS': col.prop(cam, "lens") elif cam.lens_unit == 'FOV': diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py index 1c7f3639f0a..5226a0efea7 100644 --- a/release/scripts/startup/bl_ui/properties_material.py +++ b/release/scripts/startup/bl_ui/properties_material.py @@ -227,8 +227,6 @@ def draw_material_settings(self, context): layout.prop(mat, "show_transparent_back") layout.prop(mat, "use_screen_refraction") - layout.prop(mat, "refraction_depth") - layout.prop(mat, "use_sss_translucency") layout.prop(mat, "pass_index") diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index 3abaa490d02..02b37eec10f 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -180,6 +180,37 @@ class RENDER_PT_eevee_motion_blur(RenderButtonsPanel, Panel): col.prop(props, "motion_blur_max") col.prop(props, "motion_blur_steps", text="Steps") +class RENDER_PT_eevee_motion_blur_curve(RenderButtonsPanel, Panel): + bl_label = "Shutter Curve" + bl_parent_id = "RENDER_PT_eevee_motion_blur" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_EEVEE'} + + @classmethod + def poll(cls, context): + return (context.engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + scene = context.scene + rd = scene.render + props = scene.eevee + + layout.active = props.use_motion_blur + col = layout.column() + + col.template_curve_mapping(rd, "motion_blur_shutter_curve") + + col = layout.column(align=True) + row = col.row(align=True) + row.operator("render.shutter_curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH' + row.operator("render.shutter_curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND' + row.operator("render.shutter_curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT' + row.operator("render.shutter_curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP' + row.operator("render.shutter_curve_preset", icon='LINCURVE', text="").shape = 'LINE' + row.operator("render.shutter_curve_preset", icon='NOCURVE', text="").shape = 'MAX' + class RENDER_PT_eevee_depth_of_field(RenderButtonsPanel, Panel): bl_label = "Depth of Field" @@ -329,8 +360,8 @@ class RENDER_PT_eevee_subsurface_scattering(RenderButtonsPanel, Panel): col.prop(props, "sss_jitter_threshold") -class RENDER_PT_eevee_screen_space_reflections(RenderButtonsPanel, Panel): - bl_label = "Screen Space Reflections" +class RENDER_PT_eevee_raytracing(RenderButtonsPanel, Panel): + bl_label = "Raytracing" bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_EEVEE'} @@ -352,12 +383,9 @@ class RENDER_PT_eevee_screen_space_reflections(RenderButtonsPanel, Panel): col = layout.column() col.active = props.use_ssr - col.prop(props, "use_ssr_refraction", text="Refraction") - col.prop(props, "use_ssr_halfres") col.prop(props, "ssr_quality") col.prop(props, "ssr_max_roughness") col.prop(props, "ssr_thickness") - col.prop(props, "ssr_border_fade") col.prop(props, "ssr_firefly_fac") @@ -491,6 +519,7 @@ class RENDER_PT_eevee_film(RenderButtonsPanel, Panel): col = layout.column() col.prop(rd, "filter_size") col.prop(rd, "film_transparent", text="Transparent") + col.prop(props, "use_log_space") col = layout.column(align=False, heading="Overscan") row = col.row(align=True) @@ -703,8 +732,9 @@ classes = ( RENDER_PT_eevee_bloom, RENDER_PT_eevee_depth_of_field, RENDER_PT_eevee_subsurface_scattering, - RENDER_PT_eevee_screen_space_reflections, + RENDER_PT_eevee_raytracing, RENDER_PT_eevee_motion_blur, + RENDER_PT_eevee_motion_blur_curve, RENDER_PT_eevee_volumetric, RENDER_PT_eevee_volumetric_lighting, RENDER_PT_eevee_volumetric_shadows, diff --git a/release/scripts/startup/bl_ui/properties_view_layer.py b/release/scripts/startup/bl_ui/properties_view_layer.py index 3ced6a31db5..1387b1e7450 100644 --- a/release/scripts/startup/bl_ui/properties_view_layer.py +++ b/release/scripts/startup/bl_ui/properties_view_layer.py @@ -76,6 +76,8 @@ class VIEWLAYER_PT_eevee_layer_passes_data(ViewLayerButtonsPanel, Panel): layout = self.layout layout.use_property_split = True layout.use_property_decorate = False + scene = context.scene + scene_eevee = scene.eevee view_layer = context.view_layer @@ -84,6 +86,9 @@ class VIEWLAYER_PT_eevee_layer_passes_data(ViewLayerButtonsPanel, Panel): col.prop(view_layer, "use_pass_z") col.prop(view_layer, "use_pass_mist") col.prop(view_layer, "use_pass_normal") + sub = col.column() + sub.active = not scene_eevee.use_motion_blur + sub.prop(view_layer, "use_pass_vector") class VIEWLAYER_PT_eevee_layer_passes_light(ViewLayerButtonsPanel, Panel): diff --git a/source/blender/blenkernel/BKE_world.h b/source/blender/blenkernel/BKE_world.h index bbab6fa2712..fe67a0388dc 100644 --- a/source/blender/blenkernel/BKE_world.h +++ b/source/blender/blenkernel/BKE_world.h @@ -33,6 +33,15 @@ struct World; struct World *BKE_world_add(struct Main *bmain, const char *name); void BKE_world_eval(struct Depsgraph *depsgraph, struct World *world); +struct World *BKE_world_default(void); + +void BKE_world_defaults_free_gpu(void); + +/* Module */ + +void BKE_worlds_init(void); +void BKE_worlds_exit(void); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index b2e90b7ba12..d4f0e016697 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -49,6 +49,8 @@ #include "BLT_translation.h" +#include "NOD_shader.h" + #include "DRW_engine.h" #include "DEG_depsgraph.h" @@ -227,3 +229,57 @@ void BKE_world_eval(struct Depsgraph *depsgraph, World *world) DEG_debug_print_eval(depsgraph, __func__, world->id.name, world); GPU_material_free(&world->gpumaterial); } + +/* Default World + * + * Used for rendering when a scene have no world assigned. */ + +static World default_world; + +static void world_default_init(World *ma) +{ + bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname); + ma->nodetree = ntree; + ma->use_nodes = true; + + bNode *background = nodeAddStaticNode(NULL, ntree, SH_NODE_BACKGROUND); + bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_WORLD); + bNodeSocket *background_out = nodeFindSocket(background, SOCK_OUT, "Background"); + bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface"); + nodeAddLink(ntree, background, background_out, output, output_in); + nodeSetActive(ntree, output); + + bNodeSocketValueRGBA *color_socket_ = + (bNodeSocketValueRGBA *)nodeFindSocket(background, SOCK_IN, "Color")->default_value; + + color_socket_->value[0] = 0.0f; + color_socket_->value[1] = 0.0f; + color_socket_->value[2] = 0.0f; + color_socket_->value[3] = 1.0f; +} + +World *BKE_world_default(void) +{ + return &default_world; +} + +void BKE_world_defaults_free_gpu(void) +{ + if (default_world.gpumaterial.first) { + GPU_material_free(&default_world.gpumaterial); + } +} + +/* Module functions called on startup and exit. */ + +void BKE_worlds_init(void) +{ + world_init_data(&default_world.id); + + world_default_init(&default_world); +} + +void BKE_worlds_exit(void) +{ + world_free_data(&default_world.id); +} diff --git a/source/blender/blenlib/BLI_assert.h b/source/blender/blenlib/BLI_assert.h index ad5e2b29766..71b00b77998 100644 --- a/source/blender/blenlib/BLI_assert.h +++ b/source/blender/blenlib/BLI_assert.h @@ -100,6 +100,9 @@ void _BLI_assert_unreachable_print(const char *file, int line, const char *funct #define BLI_STATIC_ASSERT_ALIGN(st, align) \ BLI_STATIC_ASSERT((sizeof(st) % (align) == 0), "Structure must be strictly aligned") +#define BLI_STATIC_ASSERT_SIZE(st, max_size) \ + BLI_STATIC_ASSERT(sizeof(CullingData) <= max_size, "Structure is too big") + /** * Indicates that this line of code should never be executed. If it is reached, it will abort in * debug builds and print an error in release builds. diff --git a/source/blender/blenlib/BLI_int2.hh b/source/blender/blenlib/BLI_int2.hh new file mode 100644 index 00000000000..b710105be17 --- /dev/null +++ b/source/blender/blenlib/BLI_int2.hh @@ -0,0 +1,170 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "BLI_float2.hh" +#include "BLI_int3.hh" + +namespace blender { + +struct int2 { + int32_t x, y; + + int2() = default; + + int2(const int32_t *ptr) : x{ptr[0]}, y{ptr[1]} + { + } + + explicit int2(int32_t value) : x(value), y(value) + { + } + + int2(int32_t x, int32_t y) : x(x), y(y) + { + } + + explicit int2(const float2 &other) : x(other.x), y(other.y) + { + } + + int2(const int3 &other) : x(other.x), y(other.y) + { + } + + operator int32_t *() + { + return &x; + } + + operator float2() const + { + return float2(x, y); + } + + operator const int32_t *() const + { + return &x; + } + + bool is_zero() const + { + return this->x == 0 && this->y == 0; + } + + int2 &operator+=(const int2 &other) + { + x += other.x; + y += other.y; + return *this; + } + + int2 &operator-=(const int2 &other) + { + x -= other.x; + y -= other.y; + return *this; + } + + int2 &operator*=(int32_t factor) + { + x *= factor; + y *= factor; + return *this; + } + + int2 &operator/=(int32_t divisor) + { + x /= divisor; + y /= divisor; + return *this; + } + + friend int2 operator+(const int2 &a, const int2 &b) + { + return {a.x + b.x, a.y + b.y}; + } + + friend int2 operator-(const int2 &a, const int2 &b) + { + return {a.x - b.x, a.y - b.y}; + } + + friend int2 operator*(const int2 &a, int32_t b) + { + return {a.x * b, a.y * b}; + } + + friend int2 operator/(const int2 &a, int32_t b) + { + BLI_assert(b != 0); + return {a.x / b, a.y / b}; + } + + friend int2 operator*(int32_t a, const int2 &b) + { + return b * a; + } + + friend float2 operator*(const int2 &a, float b) + { + return b * float2(a.x, a.y); + } + + friend float2 operator/(const int2 &a, float b) + { + return float2(a.x, a.y) / b; + } + + friend float2 operator*(float a, const int2 &b) + { + return a * float2(b.x, b.y); + } + + friend float2 operator/(float a, const int2 &b) + { + return a / float2(b.x, b.y); + } + + friend std::ostream &operator<<(std::ostream &stream, const int2 &v) + { + stream << "(" << v.x << ", " << v.y << ")"; + return stream; + } + + static int2 clamp(const int2 &a, const int2 &min, const int2 &max) + { + return int2(clamp_i(a.x, min.x, max.x), clamp_i(a.y, min.y, max.y)); + } + + static int2 clamp(const int2 &a, const int32_t &min, const int32_t &max) + { + return int2(clamp_i(a.x, min, max), clamp_i(a.y, min, max)); + } + + friend bool operator==(const int2 &a, const int2 &b) + { + return a.x == b.x && a.y == b.y; + } + + friend bool operator!=(const int2 &a, const int2 &b) + { + return !(a == b); + } +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_int3.hh b/source/blender/blenlib/BLI_int3.hh new file mode 100644 index 00000000000..4891584d505 --- /dev/null +++ b/source/blender/blenlib/BLI_int3.hh @@ -0,0 +1,156 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include <iostream> + +#include "BLI_float3.hh" + +namespace blender { + +struct int3 { + int32_t x, y, z; + + int3() = default; + + int3(const int *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]} + { + } + + explicit int3(int value) : x(value), y(value), z(value) + { + } + + int3(int x, int y, int z) : x(x), y(y), z(z) + { + } + + int3(const int3 &other) : x(other.x), y(other.y), z(other.z) + { + } + + explicit int3(const float3 &other) : x(other.x), y(other.y), z(other.z) + { + } + + operator int *() + { + return &x; + } + + operator float3() const + { + return float3(x, y, z); + } + + operator const int *() const + { + return &x; + } + + bool is_zero() const + { + return this->x == 0 && this->y == 0 && this->z == 0; + } + + int3 &operator+=(const int3 &other) + { + x += other.x; + y += other.y; + z += other.z; + return *this; + } + + int3 &operator-=(const int3 &other) + { + x -= other.x; + y -= other.y; + z -= other.z; + return *this; + } + + int3 &operator*=(int factor) + { + x *= factor; + y *= factor; + z *= factor; + return *this; + } + + int3 &operator/=(int divisor) + { + x /= divisor; + y /= divisor; + z /= divisor; + return *this; + } + + friend int3 operator+(const int3 &a, const int3 &b) + { + return {a.x + b.x, a.y + b.y, a.z + b.z}; + } + + friend int3 operator-(const int3 &a, const int3 &b) + { + return {a.x - b.x, a.y - b.y, a.z - b.z}; + } + + friend int3 operator*(const int3 &a, int b) + { + return {a.x * b, a.y * b, a.z * b}; + } + + friend int3 operator/(const int3 &a, int b) + { + BLI_assert(b != 0); + return {a.x / b, a.y / b, a.z / b}; + } + + friend int3 operator*(int a, const int3 &b) + { + return b * a; + } + + friend std::ostream &operator<<(std::ostream &stream, const int3 &v) + { + stream << "(" << v.x << ", " << v.y << ", " << v.z << ")"; + return stream; + } + + static int3 clamp(const int3 &a, const int3 &min, const int3 &max) + { + return int3( + clamp_i(a.x, min.x, max.x), clamp_i(a.y, min.y, max.y), clamp_i(a.z, min.z, max.z)); + } + + static int3 clamp(const int3 &a, const int32_t &min, const int32_t &max) + { + return int3(clamp_i(a.x, min, max), clamp_i(a.y, min, max), clamp_i(a.z, min, max)); + } + + friend bool operator==(const int3 &a, const int3 &b) + { + return a.x == b.x && a.y == b.y; + } + + friend bool operator!=(const int3 &a, const int3 &b) + { + return !(a == b); + } +}; + +} // namespace blender diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index 3022dbbe4ff..5893f909183 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -681,6 +681,12 @@ MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f) r[2] += a[2] * f; } +MINLINE void madd_v2_v2v2(float r[2], const float a[2], const float b[2]) +{ + r[0] += a[0] * b[0]; + r[1] += a[1] * b[1]; +} + MINLINE void madd_v3_v3v3(float r[3], const float a[3], const float b[3]) { r[0] += a[0] * b[0]; diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index ceddc451a46..8d478b1e1c1 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -2273,8 +2273,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) scene->eevee.shadow_cascade_size = 1024; scene->eevee.flag = SCE_EEVEE_VOLUMETRIC_LIGHTS | SCE_EEVEE_GTAO_BENT_NORMALS | - SCE_EEVEE_GTAO_BOUNCE | SCE_EEVEE_TAA_REPROJECTION | - SCE_EEVEE_SSR_HALF_RESOLUTION; + SCE_EEVEE_GTAO_BOUNCE | SCE_EEVEE_TAA_REPROJECTION; /* If the file is pre-2.80 move on. */ if (scene->layer_properties == NULL) { @@ -2342,9 +2341,9 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) EEVEE_GET_BOOL(props, taa_reprojection, SCE_EEVEE_TAA_REPROJECTION); // EEVEE_GET_BOOL(props, sss_enable, SCE_EEVEE_SSS_ENABLED); // EEVEE_GET_BOOL(props, sss_separate_albedo, SCE_EEVEE_SSS_SEPARATE_ALBEDO); - EEVEE_GET_BOOL(props, ssr_enable, SCE_EEVEE_SSR_ENABLED); - EEVEE_GET_BOOL(props, ssr_refraction, SCE_EEVEE_SSR_REFRACTION); - EEVEE_GET_BOOL(props, ssr_halfres, SCE_EEVEE_SSR_HALF_RESOLUTION); + EEVEE_GET_BOOL(props, ssr_enable, SCE_EEVEE_RAYTRACING_ENABLED); + // EEVEE_GET_BOOL(props, ssr_refraction, SCE_EEVEE_SSR_REFRACTION); + // EEVEE_GET_BOOL(props, ssr_halfres, SCE_EEVEE_SSR_HALF_RESOLUTION); EEVEE_GET_INT(props, gi_diffuse_bounces); EEVEE_GET_INT(props, gi_diffuse_bounces); diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 888bd244007..e158838008b 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -30,6 +30,7 @@ #include "DNA_armature_types.h" #include "DNA_brush_types.h" #include "DNA_cachefile_types.h" +#include "DNA_camera_types.h" #include "DNA_collection_types.h" #include "DNA_constraint_types.h" #include "DNA_fluid_types.h" @@ -2036,5 +2037,22 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + if (!DNA_struct_elem_find(fd->filesdna, "Camera", "float", "fisheye_fov")) { + /* EEVEE Panoramic Camera support. */ + LISTBASE_FOREACH (Camera *, camera, &bmain->cameras) { + camera->panorama_type = CAM_PANO_FISHEYE_EQUISOLID; + camera->fisheye_fov = DEG2RADF(180.0f); + camera->fisheye_lens = 10.5f; + camera->latitude_min = DEG2RADF(-90.0f); + camera->latitude_max = DEG2RADF(90.0f); + camera->longitude_min = DEG2RADF(-180.0f); + camera->longitude_max = DEG2RADF(180.0f); + } + + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + scene->eevee.flag |= SCE_EEVEE_FILM_LOG_ENCODING; + } + } } } diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 0c5b158ac80..f8741ea48c4 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -51,6 +51,25 @@ set(INC ${OPENSUBDIV_INCLUDE_DIRS} ) +set(EEVEE_BUILD_DIR + ${CMAKE_CURRENT_BINARY_DIR}/engines/eevee +) + +set(EEVEE_BUILD_SRC + engines/eevee/eevee_build.cc + engines/eevee/eevee_lut.c +) +add_executable(eevee_build ${EEVEE_BUILD_SRC}) +target_include_directories(eevee_build PRIVATE ${INC}) + +add_custom_command( + OUTPUT + ${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_lib.glsl + COMMAND + "$<TARGET_FILE:eevee_build>" --resolve_sample_table ${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_lib.glsl + DEPENDS eevee_build +) + set(SRC intern/draw_cache.c intern/draw_cache_extract_mesh.cc @@ -118,33 +137,33 @@ set(SRC engines/basic/basic_shader.c engines/image/image_engine.cc engines/image/image_shader.cc - engines/eevee/eevee_bloom.c - engines/eevee/eevee_cryptomatte.c - engines/eevee/eevee_data.c - engines/eevee/eevee_depth_of_field.c - engines/eevee/eevee_effects.c + engines/eevee/eevee_camera.cc + engines/eevee/eevee_depth_of_field.cc engines/eevee/eevee_engine.c - engines/eevee/eevee_lightcache.c - engines/eevee/eevee_lightprobes.c - engines/eevee/eevee_lights.c - engines/eevee/eevee_lookdev.c + engines/eevee/eevee_engine.cc + engines/eevee/eevee_film.cc + engines/eevee/eevee_gpencil.cc + engines/eevee/eevee_hair.cc + engines/eevee/eevee_hizbuffer.cc + engines/eevee/eevee_id_map.cc + engines/eevee/eevee_instance.cc + engines/eevee/eevee_light.cc + engines/eevee/eevee_lightcache.cc + engines/eevee/eevee_lightprobe.cc + engines/eevee/eevee_lookdev.cc engines/eevee/eevee_lut.c - engines/eevee/eevee_lut_gen.c - engines/eevee/eevee_materials.c - engines/eevee/eevee_mist.c - engines/eevee/eevee_motion_blur.c - engines/eevee/eevee_occlusion.c - engines/eevee/eevee_render.c - engines/eevee/eevee_renderpasses.c - engines/eevee/eevee_sampling.c - engines/eevee/eevee_screen_raytrace.c - engines/eevee/eevee_shaders.c - engines/eevee/eevee_shadows.c - engines/eevee/eevee_shadows_cascade.c - engines/eevee/eevee_shadows_cube.c - engines/eevee/eevee_subsurface.c - engines/eevee/eevee_temporal_sampling.c - engines/eevee/eevee_volumes.c + engines/eevee/eevee_material.cc + engines/eevee/eevee_mesh.cc + engines/eevee/eevee_motion_blur.cc + engines/eevee/eevee_raytracing.cc + engines/eevee/eevee_renderpasses.cc + engines/eevee/eevee_shader.cc + engines/eevee/eevee_shading.cc + engines/eevee/eevee_shadow.cc + engines/eevee/eevee_subsurface.cc + engines/eevee/eevee_velocity.cc + engines/eevee/eevee_view.cc + engines/eevee/eevee_world.cc engines/workbench/workbench_data.c engines/workbench/workbench_effect_antialiasing.c engines/workbench/workbench_effect_cavity.c @@ -224,6 +243,10 @@ set(SRC engines/basic/basic_private.h engines/eevee/eevee_engine.h engines/eevee/eevee_lightcache.h + engines/eevee/eevee_camera.hh + engines/eevee/eevee_instance.hh + engines/eevee/eevee_light.hh + engines/eevee/eevee_depth_of_field.hh engines/eevee/eevee_lut.h engines/eevee/eevee_private.h engines/external/external_engine.h @@ -248,95 +271,119 @@ set(LIB ) set(GLSL_SRC - engines/eevee/shaders/ambient_occlusion_lib.glsl - engines/eevee/shaders/background_vert.glsl - engines/eevee/shaders/common_uniforms_lib.glsl - engines/eevee/shaders/common_utiltex_lib.glsl - engines/eevee/shaders/lights_lib.glsl - engines/eevee/shaders/lightprobe_lib.glsl - engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl - engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl - engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl - engines/eevee/shaders/lightprobe_geom.glsl - engines/eevee/shaders/lightprobe_vert.glsl - engines/eevee/shaders/lightprobe_cube_display_frag.glsl - engines/eevee/shaders/lightprobe_cube_display_vert.glsl - engines/eevee/shaders/lightprobe_grid_display_frag.glsl - engines/eevee/shaders/lightprobe_grid_display_vert.glsl - engines/eevee/shaders/lightprobe_grid_fill_frag.glsl - engines/eevee/shaders/lightprobe_planar_display_frag.glsl - engines/eevee/shaders/lightprobe_planar_display_vert.glsl - engines/eevee/shaders/lookdev_world_frag.glsl - engines/eevee/shaders/closure_eval_lib.glsl - engines/eevee/shaders/closure_eval_diffuse_lib.glsl - engines/eevee/shaders/closure_eval_glossy_lib.glsl - engines/eevee/shaders/closure_eval_refraction_lib.glsl - engines/eevee/shaders/closure_eval_translucent_lib.glsl - engines/eevee/shaders/closure_type_lib.glsl - engines/eevee/shaders/effect_bloom_frag.glsl - engines/eevee/shaders/effect_dof_bokeh_frag.glsl - engines/eevee/shaders/effect_dof_dilate_tiles_frag.glsl - engines/eevee/shaders/effect_dof_downsample_frag.glsl - engines/eevee/shaders/effect_dof_filter_frag.glsl - engines/eevee/shaders/effect_dof_flatten_tiles_frag.glsl - engines/eevee/shaders/effect_dof_gather_frag.glsl - engines/eevee/shaders/effect_dof_lib.glsl - engines/eevee/shaders/effect_dof_reduce_frag.glsl - engines/eevee/shaders/effect_dof_resolve_frag.glsl - engines/eevee/shaders/effect_dof_scatter_frag.glsl - engines/eevee/shaders/effect_dof_scatter_vert.glsl - engines/eevee/shaders/effect_dof_setup_frag.glsl - engines/eevee/shaders/effect_reflection_lib.glsl - engines/eevee/shaders/effect_reflection_resolve_frag.glsl - engines/eevee/shaders/effect_reflection_trace_frag.glsl - engines/eevee/shaders/effect_downsample_frag.glsl - engines/eevee/shaders/effect_downsample_cube_frag.glsl - engines/eevee/shaders/effect_gtao_frag.glsl - engines/eevee/shaders/effect_velocity_resolve_frag.glsl - engines/eevee/shaders/effect_velocity_tile_frag.glsl - engines/eevee/shaders/effect_minmaxz_frag.glsl - engines/eevee/shaders/effect_mist_frag.glsl - engines/eevee/shaders/effect_motion_blur_frag.glsl - engines/eevee/shaders/effect_subsurface_frag.glsl - engines/eevee/shaders/effect_translucency_frag.glsl - engines/eevee/shaders/effect_temporal_aa.glsl - engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl - engines/eevee/shaders/lightprobe_planar_downsample_geom.glsl - engines/eevee/shaders/lightprobe_planar_downsample_vert.glsl - engines/eevee/shaders/object_motion_frag.glsl - engines/eevee/shaders/object_motion_vert.glsl - engines/eevee/shaders/prepass_frag.glsl - engines/eevee/shaders/prepass_vert.glsl - engines/eevee/shaders/shadow_accum_frag.glsl - engines/eevee/shaders/shadow_frag.glsl - engines/eevee/shaders/shadow_vert.glsl - engines/eevee/shaders/bsdf_lut_frag.glsl - engines/eevee/shaders/btdf_lut_frag.glsl - engines/eevee/shaders/bsdf_common_lib.glsl - engines/eevee/shaders/irradiance_lib.glsl - engines/eevee/shaders/octahedron_lib.glsl - engines/eevee/shaders/cubemap_lib.glsl - engines/eevee/shaders/bsdf_sampling_lib.glsl - engines/eevee/shaders/random_lib.glsl - engines/eevee/shaders/raytrace_lib.glsl - engines/eevee/shaders/renderpass_lib.glsl - engines/eevee/shaders/renderpass_postprocess_frag.glsl - engines/eevee/shaders/cryptomatte_frag.glsl - engines/eevee/shaders/ltc_lib.glsl - engines/eevee/shaders/ssr_lib.glsl - engines/eevee/shaders/surface_frag.glsl - engines/eevee/shaders/surface_geom.glsl - engines/eevee/shaders/surface_lib.glsl - engines/eevee/shaders/surface_vert.glsl - engines/eevee/shaders/update_noise_frag.glsl - engines/eevee/shaders/volumetric_accum_frag.glsl - engines/eevee/shaders/volumetric_lib.glsl - engines/eevee/shaders/volumetric_frag.glsl - engines/eevee/shaders/volumetric_geom.glsl - engines/eevee/shaders/volumetric_vert.glsl - engines/eevee/shaders/volumetric_resolve_frag.glsl - engines/eevee/shaders/volumetric_scatter_frag.glsl - engines/eevee/shaders/volumetric_integration_frag.glsl + ${EEVEE_BUILD_DIR}/shaders/eevee_raytrace_resolve_lib.glsl + + engines/eevee/shaders/eevee_bsdf_lib.glsl + engines/eevee/shaders/eevee_bsdf_microfacet_lib.glsl + engines/eevee/shaders/eevee_bsdf_sampling_lib.glsl + engines/eevee/shaders/eevee_bsdf_stubs_lib.glsl + engines/eevee/shaders/eevee_camera_lib.glsl + engines/eevee/shaders/eevee_camera_velocity_frag.glsl + engines/eevee/shaders/eevee_closure_lib.glsl + engines/eevee/shaders/eevee_cubemap_lib.glsl + engines/eevee/shaders/eevee_culling_debug_frag.glsl + engines/eevee/shaders/eevee_culling_iter_lib.glsl + engines/eevee/shaders/eevee_culling_lib.glsl + engines/eevee/shaders/eevee_culling_select_comp.glsl + engines/eevee/shaders/eevee_culling_sort_comp.glsl + engines/eevee/shaders/eevee_culling_tile_comp.glsl + engines/eevee/shaders/eevee_deferred_direct_frag.glsl + engines/eevee/shaders/eevee_deferred_holdout_frag.glsl + engines/eevee/shaders/eevee_deferred_transparent_frag.glsl + engines/eevee/shaders/eevee_deferred_volume_frag.glsl + engines/eevee/shaders/eevee_depth_clear_frag.glsl + engines/eevee/shaders/eevee_hiz_copy_frag.glsl + engines/eevee/shaders/eevee_hiz_downsample_frag.glsl + engines/eevee/shaders/eevee_depth_of_field_accumulator_lib.glsl + engines/eevee/shaders/eevee_depth_of_field_bokeh_lut_frag.glsl + engines/eevee/shaders/eevee_depth_of_field_filter_frag.glsl + engines/eevee/shaders/eevee_depth_of_field_gather_frag.glsl + engines/eevee/shaders/eevee_depth_of_field_gather_holefill_frag.glsl + engines/eevee/shaders/eevee_depth_of_field_lib.glsl + engines/eevee/shaders/eevee_depth_of_field_reduce_copy_frag.glsl + engines/eevee/shaders/eevee_depth_of_field_reduce_downsample_frag.glsl + engines/eevee/shaders/eevee_depth_of_field_reduce_recursive_frag.glsl + engines/eevee/shaders/eevee_depth_of_field_resolve_frag.glsl + engines/eevee/shaders/eevee_depth_of_field_scatter_frag.glsl + engines/eevee/shaders/eevee_depth_of_field_scatter_lib.glsl + engines/eevee/shaders/eevee_depth_of_field_scatter_vert.glsl + engines/eevee/shaders/eevee_depth_of_field_setup_frag.glsl + engines/eevee/shaders/eevee_depth_of_field_tiles_dilate_frag.glsl + engines/eevee/shaders/eevee_depth_of_field_tiles_flatten_frag.glsl + engines/eevee/shaders/eevee_film_filter_frag.glsl + engines/eevee/shaders/eevee_film_lib.glsl + engines/eevee/shaders/eevee_film_resolve_frag.glsl + engines/eevee/shaders/eevee_film_resolve_depth_frag.glsl + engines/eevee/shaders/eevee_gbuffer_lib.glsl + engines/eevee/shaders/eevee_irradiance_lib.glsl + engines/eevee/shaders/eevee_light_lib.glsl + engines/eevee/shaders/eevee_light_eval_lib.glsl + engines/eevee/shaders/eevee_lightprobe_display_cubemap_frag.glsl + engines/eevee/shaders/eevee_lightprobe_display_cubemap_vert.glsl + engines/eevee/shaders/eevee_lightprobe_display_grid_frag.glsl + engines/eevee/shaders/eevee_lightprobe_display_grid_vert.glsl + engines/eevee/shaders/eevee_lightprobe_display_lib.glsl + engines/eevee/shaders/eevee_lightprobe_eval_cubemap_lib.glsl + engines/eevee/shaders/eevee_lightprobe_eval_grid_lib.glsl + engines/eevee/shaders/eevee_lightprobe_filter_diffuse_frag.glsl + engines/eevee/shaders/eevee_lightprobe_filter_downsample_frag.glsl + engines/eevee/shaders/eevee_lightprobe_filter_geom.glsl + engines/eevee/shaders/eevee_lightprobe_filter_glossy_frag.glsl + engines/eevee/shaders/eevee_lightprobe_filter_lib.glsl + engines/eevee/shaders/eevee_lightprobe_filter_vert.glsl + engines/eevee/shaders/eevee_lightprobe_filter_visibility_frag.glsl + engines/eevee/shaders/eevee_lookdev_background_frag.glsl + engines/eevee/shaders/eevee_ltc_lib.glsl + engines/eevee/shaders/eevee_motion_blur_gather_frag.glsl + engines/eevee/shaders/eevee_motion_blur_lib.glsl + engines/eevee/shaders/eevee_motion_blur_tiles_dilate_frag.glsl + engines/eevee/shaders/eevee_motion_blur_tiles_flatten_frag.glsl + engines/eevee/shaders/eevee_nodetree_eval_lib.glsl + engines/eevee/shaders/eevee_raytrace_denoise_comp.glsl + engines/eevee/shaders/eevee_raytrace_raygen_frag.glsl + engines/eevee/shaders/eevee_raytrace_raygen_lib.glsl + engines/eevee/shaders/eevee_raytrace_resolve_frag.glsl + engines/eevee/shaders/eevee_raytrace_trace_lib.glsl + engines/eevee/shaders/eevee_sampling_lib.glsl + engines/eevee/shaders/eevee_shadow_debug_frag.glsl + engines/eevee/shaders/eevee_shadow_lib.glsl + engines/eevee/shaders/eevee_shadow_page_alloc_comp.glsl + engines/eevee/shaders/eevee_shadow_page_copy_comp.glsl + engines/eevee/shaders/eevee_shadow_page_debug_comp.glsl + engines/eevee/shaders/eevee_shadow_page_defrag_comp.glsl + engines/eevee/shaders/eevee_shadow_page_free_comp.glsl + engines/eevee/shaders/eevee_shadow_page_init_comp.glsl + engines/eevee/shaders/eevee_shadow_page_lib.glsl + engines/eevee/shaders/eevee_shadow_page_mark_vert.glsl + engines/eevee/shaders/eevee_shadow_tilemap_depth_scan_comp.glsl + engines/eevee/shaders/eevee_shadow_tilemap_lod_mask_comp.glsl + engines/eevee/shaders/eevee_shadow_tilemap_lib.glsl + engines/eevee/shaders/eevee_shadow_tilemap_setup_comp.glsl + engines/eevee/shaders/eevee_shadow_tilemap_tag_comp.glsl + engines/eevee/shaders/eevee_shadow_tilemap_visibility_comp.glsl + engines/eevee/shaders/eevee_subsurface_eval_frag.glsl + engines/eevee/shaders/eevee_surface_background_frag.glsl + engines/eevee/shaders/eevee_surface_deferred_frag.glsl + engines/eevee/shaders/eevee_surface_depth_frag.glsl + engines/eevee/shaders/eevee_surface_depth_simple_frag.glsl + engines/eevee/shaders/eevee_surface_forward_frag.glsl + engines/eevee/shaders/eevee_surface_gpencil_vert.glsl + engines/eevee/shaders/eevee_surface_hair_vert.glsl + engines/eevee/shaders/eevee_surface_lib.glsl + engines/eevee/shaders/eevee_surface_lookdev_vert.glsl + engines/eevee/shaders/eevee_surface_mesh_geom.glsl + engines/eevee/shaders/eevee_surface_mesh_vert.glsl + engines/eevee/shaders/eevee_surface_velocity_frag.glsl + engines/eevee/shaders/eevee_surface_velocity_lib.glsl + engines/eevee/shaders/eevee_surface_velocity_mesh_vert.glsl + engines/eevee/shaders/eevee_surface_world_vert.glsl + engines/eevee/shaders/eevee_velocity_lib.glsl + engines/eevee/shaders/eevee_volume_deferred_frag.glsl + engines/eevee/shaders/eevee_volume_eval_lib.glsl + engines/eevee/shaders/eevee_volume_lib.glsl + engines/eevee/shaders/eevee_volume_vert.glsl + + engines/eevee/eevee_shader_shared.hh engines/workbench/shaders/workbench_cavity_lib.glsl engines/workbench/shaders/workbench_common_lib.glsl @@ -368,19 +415,24 @@ set(GLSL_SRC engines/workbench/workbench_shader_shared.h + intern/shaders/common_attribute_lib.glsl intern/shaders/common_colormanagement_lib.glsl + intern/shaders/common_debug_lib.glsl + intern/shaders/common_fullscreen_vert.glsl + intern/shaders/common_fxaa_lib.glsl intern/shaders/common_globals_lib.glsl - intern/shaders/common_pointcloud_lib.glsl + intern/shaders/common_gpencil_lib.glsl intern/shaders/common_hair_lib.glsl - intern/shaders/common_hair_refine_vert.glsl intern/shaders/common_hair_refine_comp.glsl - intern/shaders/common_math_lib.glsl + intern/shaders/common_hair_refine_vert.glsl + intern/shaders/common_intersection_lib.glsl intern/shaders/common_math_geom_lib.glsl - intern/shaders/common_view_clipping_lib.glsl - intern/shaders/common_view_lib.glsl - intern/shaders/common_fxaa_lib.glsl + intern/shaders/common_math_lib.glsl + intern/shaders/common_obinfos_lib.glsl + intern/shaders/common_pointcloud_lib.glsl intern/shaders/common_smaa_lib.glsl - intern/shaders/common_fullscreen_vert.glsl + intern/shaders/common_uniform_attribute_lib.glsl + intern/shaders/common_view_lib.glsl intern/shaders/common_subdiv_custom_data_interp_comp.glsl intern/shaders/common_subdiv_ibo_lines_comp.glsl diff --git a/source/blender/draw/engines/eevee/eevee_allocator.hh b/source/blender/draw/engines/eevee/eevee_allocator.hh new file mode 100644 index 00000000000..5aa4bf2569c --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_allocator.hh @@ -0,0 +1,219 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Allocators that may be moved to BLI at some point. + */ + +#pragma once + +#include "BLI_math_bits.h" +#include "BLI_vector.hh" + +namespace blender::eevee { + +/** + * Allow allocation and deletion of elements without reordering. + * Useful to keed valid indices to items inside this allocator. + * Type T need to implement the free_resources() method. + */ +template<typename T> class IndexedAllocator { + private: + Vector<T> items_; + /** Bitmap of used items. Make search of unused slot faster. */ + Vector<uint32_t> unused_; + /** First unused batch of items in the vector for fast reallocating. */ + int64_t unused_first_ = LONG_MAX; + /** Unused item count in the vector for fast reallocating. */ + int64_t unused_count_ = 0; + + public: + int64_t alloc(T &&value) + { + if (unused_count_ > 0) { + /* Reclaim unused slot. */ + int64_t index = unused_first_; + items_[index] = std::move(value); + set_slot_used(index); + + unused_count_ -= 1; + unused_first_ = first_unused_slot_get(); + return index; + } + /* Not enough place, grow the vector. */ + int64_t index = items_.append_and_get_index(value); + int64_t size_needed = (index + 32) / 32; + int64_t size_old = unused_.size(); + unused_.resize(size_needed); + if (size_old < size_needed) { + for (auto i : IndexRange(size_old, size_needed - size_old)) { + /* Used by default. */ + unused_[i] = 0; + } + } + set_slot_used(index); + return index; + } + + void free(int64_t index) + { + unused_count_ += 1; + if (index < unused_first_) { + unused_first_ = index; + } + set_slot_unused(index); + is_slot_unused(index); + items_[index].free_resources(); + } + + /* Pruned unused shadows at the end of the vector. */ + void resize() + { + while (items_.size() > 0 && is_slot_unused(items_.size() - 1)) { + set_slot_used(items_.size() - 1); + items_.remove_last(); + unused_count_--; + } + if (unused_first_ >= items_.size()) { + /* First unused has been pruned. */ + unused_first_ = first_unused_slot_get(); + } + } + + int64_t size() const + { + return items_.size() - unused_count_; + } + + class Iterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using pointer = const T *; + using reference = const T &; + using difference_type = std::ptrdiff_t; + + private: + IndexedAllocator &allocator_; + int64_t current_; + + public: + constexpr explicit Iterator(IndexedAllocator &allocator, int64_t current) + : allocator_(allocator), current_(current) + { + } + + constexpr Iterator &operator++() + { + current_++; + while ((current_ < allocator_.items_.size()) && allocator_.is_slot_unused(current_)) { + current_++; + } + return *this; + } + + constexpr Iterator operator++(int) const + { + Iterator iterator = *this; + ++*this; + return iterator; + } + + constexpr friend bool operator!=(const Iterator &a, const Iterator &b) + { + return a.current_ != b.current_; + } + + T &operator*() + { + BLI_assert(allocator_.is_slot_unused(current_) == false); + return allocator_[current_]; + } + }; + + constexpr Iterator begin() + { + int64_t first_used = first_used_slot_get(); + if (first_used == LONG_MAX) { + /* Will produce no iteration. */ + first_used = items_.size(); + } + return Iterator(*this, first_used); + } + + constexpr Iterator end() + { + return Iterator(*this, items_.size()); + } + + const T &operator[](int64_t index) const + { + BLI_assert(is_slot_unused(index) == false); + return items_[index]; + } + + T &operator[](int64_t index) + { + BLI_assert(is_slot_unused(index) == false); + return items_[index]; + } + + private: + int64_t first_unused_slot_get(void) const + { + if (unused_count_ > 0) { + for (auto i : IndexRange(unused_.size())) { + if (unused_[i] != 0) { + return i * 32 + bitscan_forward_uint(unused_[i]); + } + } + } + return LONG_MAX; + } + + int64_t first_used_slot_get(void) const + { + if (unused_count_ < items_.size()) { + for (auto i : IndexRange(unused_.size())) { + if (~unused_[i] != 0) { + return i * 32 + bitscan_forward_uint(~unused_[i]); + } + } + } + return LONG_MAX; + } + + bool is_slot_unused(int64_t index) const + { + return (unused_[index / 32] & (1u << uint32_t(index % 32))) != 0; + } + + void set_slot_unused(int64_t index) + { + SET_FLAG_FROM_TEST(unused_[index / 32], true, (1u << uint32_t(index % 32))); + } + + void set_slot_used(int64_t index) + { + SET_FLAG_FROM_TEST(unused_[index / 32], false, (1u << uint32_t(index % 32))); + } +}; + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_bloom.c b/source/blender/draw/engines/eevee/eevee_bloom.c deleted file mode 100644 index cc6e75859c8..00000000000 --- a/source/blender/draw/engines/eevee/eevee_bloom.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - * - * Eevee's bloom shader. - */ - -#include "DRW_render.h" - -#include "GPU_texture.h" - -#include "DEG_depsgraph_query.h" - -#include "eevee_private.h" - -static const bool use_highres = true; - -int EEVEE_bloom_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_StorageList *stl = vedata->stl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_EffectsInfo *effects = stl->effects; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - - if (scene_eval->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) { - const float *viewport_size = DRW_viewport_size_get(); - - /* Bloom */ - int blitsize[2], texsize[2]; - - /* Blit Buffer */ - effects->source_texel_size[0] = 1.0f / viewport_size[0]; - effects->source_texel_size[1] = 1.0f / viewport_size[1]; - - blitsize[0] = (int)viewport_size[0]; - blitsize[1] = (int)viewport_size[1]; - - effects->blit_texel_size[0] = 1.0f / (float)blitsize[0]; - effects->blit_texel_size[1] = 1.0f / (float)blitsize[1]; - - effects->bloom_blit = DRW_texture_pool_query_2d( - blitsize[0], blitsize[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type); - - GPU_framebuffer_ensure_config( - &fbl->bloom_blit_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->bloom_blit)}); - - /* Parameters */ - const float threshold = scene_eval->eevee.bloom_threshold; - const float knee = scene_eval->eevee.bloom_knee; - const float intensity = scene_eval->eevee.bloom_intensity; - const float *color = scene_eval->eevee.bloom_color; - const float radius = scene_eval->eevee.bloom_radius; - effects->bloom_clamp = scene_eval->eevee.bloom_clamp; - - /* determine the iteration count */ - const float minDim = (float)MIN2(blitsize[0], blitsize[1]); - const float maxIter = (radius - 8.0f) + log(minDim) / log(2); - const int maxIterInt = effects->bloom_iteration_len = (int)maxIter; - - CLAMP(effects->bloom_iteration_len, 1, MAX_BLOOM_STEP); - - effects->bloom_sample_scale = 0.5f + maxIter - (float)maxIterInt; - effects->bloom_curve_threshold[0] = threshold - knee; - effects->bloom_curve_threshold[1] = knee * 2.0f; - effects->bloom_curve_threshold[2] = 0.25f / max_ff(1e-5f, knee); - effects->bloom_curve_threshold[3] = threshold; - - mul_v3_v3fl(effects->bloom_color, color, intensity); - - /* Downsample buffers */ - copy_v2_v2_int(texsize, blitsize); - for (int i = 0; i < effects->bloom_iteration_len; i++) { - texsize[0] /= 2; - texsize[1] /= 2; - - texsize[0] = MAX2(texsize[0], 2); - texsize[1] = MAX2(texsize[1], 2); - - effects->downsamp_texel_size[i][0] = 1.0f / (float)texsize[0]; - effects->downsamp_texel_size[i][1] = 1.0f / (float)texsize[1]; - - effects->bloom_downsample[i] = DRW_texture_pool_query_2d( - texsize[0], texsize[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type); - GPU_framebuffer_ensure_config( - &fbl->bloom_down_fb[i], - {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->bloom_downsample[i])}); - } - - /* Upsample buffers */ - copy_v2_v2_int(texsize, blitsize); - for (int i = 0; i < effects->bloom_iteration_len - 1; i++) { - texsize[0] /= 2; - texsize[1] /= 2; - - texsize[0] = MAX2(texsize[0], 2); - texsize[1] = MAX2(texsize[1], 2); - - effects->bloom_upsample[i] = DRW_texture_pool_query_2d( - texsize[0], texsize[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type); - GPU_framebuffer_ensure_config( - &fbl->bloom_accum_fb[i], - {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->bloom_upsample[i])}); - } - - return EFFECT_BLOOM | EFFECT_POST_BUFFER; - } - - /* Cleanup to release memory */ - GPU_FRAMEBUFFER_FREE_SAFE(fbl->bloom_blit_fb); - - for (int i = 0; i < MAX_BLOOM_STEP - 1; i++) { - GPU_FRAMEBUFFER_FREE_SAFE(fbl->bloom_down_fb[i]); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->bloom_accum_fb[i]); - } - - return 0; -} - -static DRWShadingGroup *eevee_create_bloom_pass(const char *name, - EEVEE_EffectsInfo *effects, - struct GPUShader *sh, - DRWPass **pass, - bool upsample, - bool resolve) -{ - struct GPUBatch *quad = DRW_cache_fullscreen_quad_get(); - - *pass = DRW_pass_create(name, DRW_STATE_WRITE_COLOR); - - DRWShadingGroup *grp = DRW_shgroup_create(sh, *pass); - DRW_shgroup_call(grp, quad, NULL); - DRW_shgroup_uniform_texture_ref(grp, "sourceBuffer", &effects->unf_source_buffer); - DRW_shgroup_uniform_vec2(grp, "sourceBufferTexelSize", effects->unf_source_texel_size, 1); - if (upsample) { - DRW_shgroup_uniform_texture_ref(grp, "baseBuffer", &effects->unf_base_buffer); - DRW_shgroup_uniform_float(grp, "sampleScale", &effects->bloom_sample_scale, 1); - } - if (resolve) { - DRW_shgroup_uniform_vec3(grp, "bloomColor", effects->bloom_color, 1); - DRW_shgroup_uniform_bool_copy(grp, "bloomAddBase", true); - } - - return grp; -} - -void EEVEE_bloom_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - - psl->bloom_accum_ps = NULL; - - if ((effects->enabled_effects & EFFECT_BLOOM) != 0) { - /** - * Bloom Algorithm - * - * Overview: - * - Down-sample the color buffer doing a small blur during each step. - * - Accumulate bloom color using previously down-sampled color buffers - * and do an up-sample blur for each new accumulated layer. - * - Finally add accumulation buffer onto the source color buffer. - * - * [1/1] is original copy resolution (can be half or quarter res for performance) - * <pre> - * [DOWNSAMPLE CHAIN] [UPSAMPLE CHAIN] - * - * Source Color ─ [Blit] ─> Bright Color Extract [1/1] Final Color - * | Λ - * [Downsample First] Source Color ─> + [Resolve] - * v | - * Color Downsampled [1/2] ────────────> + Accumulation Buffer [1/2] - * | Λ - * ─── ─── - * Repeat Repeat - * ─── ─── - * v | - * Color Downsampled [1/N-1] ──────────> + Accumulation Buffer [1/N-1] - * | Λ - * [Downsample] [Upsample] - * v | - * Color Downsampled [1/N] ─────────────────────────┘ - * </pre> - */ - DRWShadingGroup *grp; - const bool use_antiflicker = true; - eevee_create_bloom_pass("Bloom Downsample First", - effects, - EEVEE_shaders_bloom_downsample_get(use_antiflicker), - &psl->bloom_downsample_first, - false, - false); - eevee_create_bloom_pass("Bloom Downsample", - effects, - EEVEE_shaders_bloom_downsample_get(false), - &psl->bloom_downsample, - false, - false); - eevee_create_bloom_pass("Bloom Upsample", - effects, - EEVEE_shaders_bloom_upsample_get(use_highres), - &psl->bloom_upsample, - true, - false); - - grp = eevee_create_bloom_pass("Bloom Blit", - effects, - EEVEE_shaders_bloom_blit_get(use_antiflicker), - &psl->bloom_blit, - false, - false); - DRW_shgroup_uniform_vec4(grp, "curveThreshold", effects->bloom_curve_threshold, 1); - DRW_shgroup_uniform_float(grp, "clampIntensity", &effects->bloom_clamp, 1); - - grp = eevee_create_bloom_pass("Bloom Resolve", - effects, - EEVEE_shaders_bloom_resolve_get(use_highres), - &psl->bloom_resolve, - true, - true); - } -} - -void EEVEE_bloom_draw(EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - - /* Bloom */ - if ((effects->enabled_effects & EFFECT_BLOOM) != 0) { - struct GPUTexture *last; - - /* Extract bright pixels */ - copy_v2_v2(effects->unf_source_texel_size, effects->source_texel_size); - effects->unf_source_buffer = effects->source_buffer; - - GPU_framebuffer_bind(fbl->bloom_blit_fb); - DRW_draw_pass(psl->bloom_blit); - - /* Downsample */ - copy_v2_v2(effects->unf_source_texel_size, effects->blit_texel_size); - effects->unf_source_buffer = effects->bloom_blit; - - GPU_framebuffer_bind(fbl->bloom_down_fb[0]); - DRW_draw_pass(psl->bloom_downsample_first); - - last = effects->bloom_downsample[0]; - - for (int i = 1; i < effects->bloom_iteration_len; i++) { - copy_v2_v2(effects->unf_source_texel_size, effects->downsamp_texel_size[i - 1]); - effects->unf_source_buffer = last; - - GPU_framebuffer_bind(fbl->bloom_down_fb[i]); - DRW_draw_pass(psl->bloom_downsample); - - /* Used in next loop */ - last = effects->bloom_downsample[i]; - } - - /* Upsample and accumulate */ - for (int i = effects->bloom_iteration_len - 2; i >= 0; i--) { - copy_v2_v2(effects->unf_source_texel_size, effects->downsamp_texel_size[i]); - effects->unf_source_buffer = last; - effects->unf_base_buffer = effects->bloom_downsample[i]; - - GPU_framebuffer_bind(fbl->bloom_accum_fb[i]); - DRW_draw_pass(psl->bloom_upsample); - - last = effects->bloom_upsample[i]; - } - - /* Resolve */ - copy_v2_v2(effects->unf_source_texel_size, effects->downsamp_texel_size[0]); - effects->unf_source_buffer = last; - effects->unf_base_buffer = effects->source_buffer; - - GPU_framebuffer_bind(effects->target_buffer); - DRW_draw_pass(psl->bloom_resolve); - SWAP_BUFFERS(); - } -} - -void EEVEE_bloom_output_init(EEVEE_ViewLayerData *UNUSED(sldata), - EEVEE_Data *vedata, - uint UNUSED(tot_samples)) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - - /* Create FrameBuffer. */ - DRW_texture_ensure_fullscreen_2d(&txl->bloom_accum, GPU_R11F_G11F_B10F, 0); - - GPU_framebuffer_ensure_config(&fbl->bloom_pass_accum_fb, - {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->bloom_accum)}); - - /* Create Pass and shgroup. */ - DRWShadingGroup *grp = eevee_create_bloom_pass("Bloom Accumulate", - effects, - EEVEE_shaders_bloom_resolve_get(use_highres), - &psl->bloom_accum_ps, - true, - true); - DRW_shgroup_uniform_bool_copy(grp, "bloomAddBase", false); -} - -void EEVEE_bloom_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - - if (stl->g_data->render_passes & EEVEE_RENDER_PASS_BLOOM) { - GPU_framebuffer_bind(fbl->bloom_pass_accum_fb); - DRW_draw_pass(psl->bloom_accum_ps); - - /* Restore */ - GPU_framebuffer_bind(fbl->main_fb); - } -} diff --git a/source/blender/draw/engines/eevee/eevee_build.cc b/source/blender/draw/engines/eevee/eevee_build.cc new file mode 100644 index 00000000000..59be843fcbb --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_build.cc @@ -0,0 +1,155 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Compile time computation and code generation. + */ + +#include <algorithm> +#include <array> +#include <fstream> +#include <iostream> +#include <math.h> +#include <string> +#include <vector> + +/* For blue_noise. */ +#include "eevee_lut.h" + +using namespace std; + +typedef unsigned char uchar; +typedef float vec3[3]; + +const int samples_per_pool = 32; + +static void raytrace_sample_reuse_table(string &output_name, bool debug) +{ + /** Following "Stochastic All The Things: Raytracing in Hybrid Real-Time Rendering" + * by Tomasz Stachowiak + * https://www.ea.com/seed/news/seed-dd18-presentation-slides-raytracing + */ + + struct sample { + int x, y; + sample(int _x, int _y) : x(_x), y(_y){}; + }; + + array<vector<sample>, 4> pools; + array<vec3, 4> pools_color = {1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0}; + vector<vec3> debug_image(64 * 64); + + ofstream ppm; + auto ppm_file_out = [&](const char *name, vector<vec3> &debug_image) { + ppm.open(name); + ppm << "P3\n64 64\n255\n"; + for (auto &vec : debug_image) { + uchar ucol[3] = {(uchar)(vec[0] * 255), (uchar)(vec[1] * 255), (uchar)(vec[2] * 255)}; + ppm << (int)ucol[0] << " " << (int)ucol[1] << " " << (int)ucol[2] << "\n"; + /* Clear the image. */ + vec[0] = vec[1] = vec[2] = 0.0f; + } + ppm.close(); + }; + + /* Using sample_reuse_1.ppm, set a center position where there is 4 different pool (color). */ + int center[4][2] = {{49, 47}, {48, 46}, {49, 46}, {48, 47}}; + /* Remapping of pool order since the center samples may not match the pool order from shader. + * Order is (0,0), (1,0), (0,1), (1,1). + * IMPORTANT: the actual PPM picture has Y flipped compared to OpenGL. */ + int poolmap[4] = {3, 0, 1, 2}; + + for (int x = 0; x < 64; x++) { + for (int y = 0; y < 64; y++) { + int px = y * 64 + x; + float noise_value = blue_noise[px][0]; + int pool_id = floorf(noise_value * 4.0f); + sample ofs(x - center[pool_id][0], y - center[pool_id][1]); + pools[pool_id].push_back(ofs); + + for (int i = 0; i < 3; i++) { + debug_image[px][i] = pools_color[pool_id][i]; + } + } + } + + if (debug) { + ppm_file_out("sample_reuse_1.ppm", debug_image); + } + + for (int pool_id = 0; pool_id < 4; pool_id++) { + auto &pool = pools[pool_id]; + auto sort = [](const sample &a, const sample &b) { + /* Manhattan distance. */ + return abs(a.x) + abs(a.y) < abs(b.x) + abs(b.y); + }; + std::sort(pool.begin(), pool.end(), sort); + + for (int j = 0; j < samples_per_pool; j++) { + int pos[2] = {pool[j].x + center[pool_id][0], pool[j].y + center[pool_id][1]}; + int px = pos[1] * 64 + pos[0]; + for (int i = 0; i < 3; i++) { + debug_image[px][i] = pools_color[pool_id][i]; + } + } + } + + if (debug) { + ppm_file_out("sample_reuse_2.ppm", debug_image); + } + + /* TODO(fclem): Order the samples to have better spatial coherence. */ + + ofstream table_out; + int total = samples_per_pool * 4; + table_out.open(output_name); + + table_out << "\n/* Sample table generated at build time. */\n"; + table_out << "const int resolve_sample_max = " << samples_per_pool << ";\n"; + table_out << "const vec2 resolve_sample_offsets[" << total << "] = vec2[" << total << "](\n"; + for (int pool_id = 0; pool_id < 4; pool_id++) { + auto &pool = pools[poolmap[pool_id]]; + for (int i = 0; i < samples_per_pool; i++) { + table_out << " vec2(" << pool[i].x << ", " << pool[i].y << ")"; + if (i < samples_per_pool - 1) { + table_out << ",\n"; + } + } + if (pool_id < 3) { + table_out << ",\n"; + } + } + table_out << ");\n\n"; + + table_out.close(); +} + +int main(int argc, char **argv) +{ + if (argc != 3) { + fprintf(stderr, "usage: eevee_build [--resolve_sample_table] output_file\n"); + return -1; + } + if (string(argv[1]) == "--resolve_sample_table") { + string output_name(argv[2]); + raytrace_sample_reuse_table(output_name, false); + } + return 0; +} diff --git a/source/blender/draw/engines/eevee/eevee_camera.cc b/source/blender/draw/engines/eevee/eevee_camera.cc new file mode 100644 index 00000000000..3af221e8689 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_camera.cc @@ -0,0 +1,167 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +#include <array> + +#include "DRW_render.h" + +#include "DNA_camera_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_camera.h" +#include "DEG_depsgraph_query.h" +#include "RE_pipeline.h" + +#include "eevee_camera.hh" +#include "eevee_instance.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name eCameraType + * \{ */ + +static eCameraType from_camera(const ::Camera *camera) +{ + switch (camera->type) { + default: + case CAM_PERSP: + return CAMERA_PERSP; + case CAM_ORTHO: + return CAMERA_ORTHO; + case CAM_PANO: + switch (camera->panorama_type) { + default: + case CAM_PANO_EQUIRECTANGULAR: + return CAMERA_PANO_EQUIRECT; + case CAM_PANO_FISHEYE_EQUIDISTANT: + return CAMERA_PANO_EQUIDISTANT; + case CAM_PANO_FISHEYE_EQUISOLID: + return CAMERA_PANO_EQUISOLID; + case CAM_PANO_MIRRORBALL: + return CAMERA_PANO_MIRROR; + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Camera + * \{ */ + +void Camera::init(void) +{ + const Object *camera_eval = inst_.camera_eval_object; + synced_ = false; + /* Swap! */ + data_id_ = !data_id_; + + CameraData &data = data_[data_id_]; + + if (camera_eval) { + const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data); + data.type = from_camera(cam); + } + else if (inst_.drw_view) { + data.type = DRW_view_is_persp_get(inst_.drw_view) ? CAMERA_PERSP : CAMERA_ORTHO; + } + else { + /* Lightprobe baking. */ + data.type = CAMERA_PERSP; + } +} + +void Camera::sync(void) +{ + const Object *camera_eval = inst_.camera_eval_object; + CameraData &data = data_[data_id_]; + + data.filter_size = inst_.scene->r.gauss; + + if (inst_.drw_view) { + DRW_view_viewmat_get(inst_.drw_view, data.viewmat, false); + DRW_view_viewmat_get(inst_.drw_view, data.viewinv, true); + DRW_view_winmat_get(inst_.drw_view, data.winmat, false); + DRW_view_winmat_get(inst_.drw_view, data.wininv, true); + DRW_view_persmat_get(inst_.drw_view, data.persmat, false); + DRW_view_persmat_get(inst_.drw_view, data.persinv, true); + DRW_view_camtexco_get(inst_.drw_view, data.uv_scale); + } + else if (inst_.render) { + /* TODO(fclem) Overscan */ + // RE_GetCameraWindowWithOverscan(inst_.render->re, g_data->overscan, data.winmat); + RE_GetCameraWindow(inst_.render->re, camera_eval, data.winmat); + RE_GetCameraModelMatrix(inst_.render->re, camera_eval, data.viewinv); + invert_m4_m4(data.viewmat, data.viewinv); + invert_m4_m4(data.wininv, data.winmat); + mul_m4_m4m4(data.persmat, data.winmat, data.viewmat); + invert_m4_m4(data.persinv, data.persmat); + data.uv_scale = vec2(1.0f); + data.uv_bias = vec2(0.0f); + } + else { + unit_m4(data.viewmat); + unit_m4(data.viewinv); + perspective_m4(data.winmat, -0.1f, 0.1f, -0.1f, 0.1f, 0.1f, 1.0f); + invert_m4_m4(data.wininv, data.winmat); + mul_m4_m4m4(data.persmat, data.winmat, data.viewmat); + invert_m4_m4(data.persinv, data.persmat); + } + + if (camera_eval) { + const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data); + data.clip_near = cam->clip_start; + data.clip_far = cam->clip_end; + data.fisheye_fov = cam->fisheye_fov; + data.fisheye_lens = cam->fisheye_lens; + data.equirect_bias.x = -cam->longitude_min + M_PI_2; + data.equirect_bias.y = -cam->latitude_min + M_PI_2; + data.equirect_scale.x = cam->longitude_min - cam->longitude_max; + data.equirect_scale.y = cam->latitude_min - cam->latitude_max; + /* Combine with uv_scale/bias to avoid doing extra computation. */ + data.equirect_bias += data.uv_bias * data.equirect_scale; + data.equirect_scale *= data.uv_scale; + + data.equirect_scale_inv = 1.0f / data.equirect_scale; + } + else if (inst_.drw_view) { + data.clip_near = DRW_view_near_distance_get(inst_.drw_view); + data.clip_far = DRW_view_far_distance_get(inst_.drw_view); + data.fisheye_fov = data.fisheye_lens = -1.0f; + data.equirect_bias = vec2(0.0f); + data.equirect_scale = vec2(0.0f); + } + + data_[data_id_].push_update(); + + synced_ = true; + + /* Detect changes in parameters. */ + if (data_[data_id_] != data_[!data_id_]) { + inst_.sampling.reset(); + } +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_camera.hh b/source/blender/draw/engines/eevee/eevee_camera.hh new file mode 100644 index 00000000000..702c73866f8 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_camera.hh @@ -0,0 +1,149 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +#pragma once + +#include <array> + +#include "DNA_object_types.h" + +#include "eevee_sampling.hh" +#include "eevee_shader_shared.hh" + +namespace blender::eevee { + +class Instance; + +/* TODO(fclem) Might want to move to eevee_shader_shared.hh. */ +static const float cubeface_mat[6][4][4] = { + /* Pos X */ + {{0.0f, 0.0f, -1.0f, 0.0f}, + {0.0f, -1.0f, 0.0f, 0.0f}, + {-1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f}}, + /* Neg X */ + {{0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, -1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f}}, + /* Pos Y */ + {{1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, -1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f}}, + /* Neg Y */ + {{1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, -1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f}}, + /* Pos Z */ + {{1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, -1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, -1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f}}, + /* Neg Z */ + {{-1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, -1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f}}, +}; + +inline void cubeface_winmat_get(mat4 &winmat, float near, float far) +{ + /* Simple 90° FOV projection. */ + perspective_m4(winmat, -near, near, -near, near, near, far); +} + +/* -------------------------------------------------------------------- */ +/** \name CameraData operators + * \{ */ + +inline bool operator==(const CameraData &a, const CameraData &b) +{ + return compare_m4m4(a.persmat, b.persmat, FLT_MIN) && (a.uv_scale == b.uv_scale) && + (a.uv_bias == b.uv_bias) && (a.equirect_scale == b.equirect_scale) && + (a.equirect_bias == b.equirect_bias) && (a.fisheye_fov == b.fisheye_fov) && + (a.fisheye_lens == b.fisheye_lens) && (a.filter_size == b.filter_size) && + (a.type == b.type); +} + +inline bool operator!=(const CameraData &a, const CameraData &b) +{ + return !(a == b); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Camera + * \{ */ + +/** + * Point of view in the scene. Can be init from viewport or camera object. + */ +class Camera { + private: + Instance &inst_; + + /** Double buffered to detect changes and have history for re-projection. */ + CameraDataBuf data_[2]; + /** Active data index in data_. */ + int data_id_ = 0; + /** Detects wrong usage. */ + bool synced_ = false; + + public: + Camera(Instance &inst) : inst_(inst){}; + ~Camera(){}; + + void init(void); + void sync(void); + + /** + * Getters + **/ + const CameraData &data_get(void) const + { + BLI_assert(synced_); + return data_[data_id_]; + } + const GPUUniformBuf *ubo_get(void) const + { + return data_[data_id_].ubo_get(); + } + bool is_panoramic(void) const + { + return eevee::is_panoramic(data_[data_id_].type); + } + bool is_orthographic(void) const + { + return data_[data_id_].type == CAMERA_ORTHO; + } + vec3 position(void) const + { + return vec3(data_[data_id_].viewinv[3]); + } +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c deleted file mode 100644 index 80207523a65..00000000000 --- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c +++ /dev/null @@ -1,721 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2020, Blender Foundation. - */ - -/** \file - * \ingroup EEVEE - * - * This file implements Cryptomatte for EEVEE. Cryptomatte is used to extract mattes using - * information already available at render time. See - * https://raw.githubusercontent.com/Psyop/Cryptomatte/master/specification/IDmattes_poster.pdf - * for reference to the cryptomatte specification. - * - * The challenge with cryptomatte in EEVEE is the merging and sorting of the samples. - * User can enable up to 3 cryptomatte layers (Object, Material and Asset). - * - * Process - * - * - Cryptomatte sample: Rendering of a cryptomatte sample is stored in a GPUBuffer. The buffer - * holds a single float per pixel per number of active cryptomatte layers. The float is the - * cryptomatte hash of each layer. After drawing the cryptomatte sample the intermediate result is - * downloaded to a CPU buffer (`cryptomatte_download_buffer`). - * - * Accurate mode - * - * There are two accuracy modes. The difference between the two is the number of render samples - * they take into account to create the render passes. When accurate mode is off the number of - * levels is used as the number of cryptomatte samples to take. When accuracy mode is on the number - * of render samples is used. - * - */ - -#include "DRW_engine.h" -#include "DRW_render.h" - -#include "BKE_cryptomatte.h" - -#include "GPU_batch.h" - -#include "RE_pipeline.h" - -#include "BLI_alloca.h" -#include "BLI_math_bits.h" -#include "BLI_rect.h" - -#include "DNA_hair_types.h" -#include "DNA_mesh_types.h" -#include "DNA_modifier_types.h" -#include "DNA_particle_types.h" - -#include "eevee_private.h" - -/* -------------------------------------------------------------------- */ -/** \name Data Management cryptomatte accum buffer - * \{ */ - -BLI_INLINE eViewLayerCryptomatteFlags eevee_cryptomatte_active_layers(const ViewLayer *view_layer) -{ - const eViewLayerCryptomatteFlags cryptomatte_layers = view_layer->cryptomatte_flag & - VIEW_LAYER_CRYPTOMATTE_ALL; - return cryptomatte_layers; -} - -/* The number of cryptomatte layers that are enabled */ -BLI_INLINE int eevee_cryptomatte_layers_count(const ViewLayer *view_layer) -{ - const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers( - view_layer); - return count_bits_i(cryptomatte_layers); -} - -/* The number of render result passes are needed to store a single cryptomatte layer. Per - * renderpass 2 cryptomatte samples can be stored. */ -BLI_INLINE int eevee_cryptomatte_passes_per_layer(const ViewLayer *view_layer) -{ - const int num_cryptomatte_levels = view_layer->cryptomatte_levels; - const int num_cryptomatte_passes = (num_cryptomatte_levels + 1) / 2; - return num_cryptomatte_passes; -} - -BLI_INLINE int eevee_cryptomatte_layer_stride(const ViewLayer *view_layer) -{ - return view_layer->cryptomatte_levels; -} - -BLI_INLINE int eevee_cryptomatte_layer_offset(const ViewLayer *view_layer, const int layer) -{ - return view_layer->cryptomatte_levels * layer; -} - -BLI_INLINE int eevee_cryptomatte_pixel_stride(const ViewLayer *view_layer) -{ - return eevee_cryptomatte_layer_stride(view_layer) * eevee_cryptomatte_layers_count(view_layer); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Init Renderpasses - * \{ */ - -void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata) -{ - EEVEE_StorageList *stl = vedata->stl; - EEVEE_PrivateData *g_data = stl->g_data; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - ViewLayer *view_layer = draw_ctx->view_layer; - - /* Cryptomatte is only rendered for final image renders */ - if (!DRW_state_is_scene_render()) { - return; - } - const eViewLayerCryptomatteFlags active_layers = eevee_cryptomatte_active_layers(view_layer); - if (active_layers) { - struct CryptomatteSession *session = BKE_cryptomatte_init(); - if ((active_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) { - BKE_cryptomatte_add_layer(session, "CryptoObject"); - } - if ((active_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) { - BKE_cryptomatte_add_layer(session, "CryptoMaterial"); - } - if ((active_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) { - BKE_cryptomatte_add_layer(session, "CryptoAsset"); - } - g_data->cryptomatte_session = session; - - g_data->render_passes |= EEVEE_RENDER_PASS_CRYPTOMATTE | EEVEE_RENDER_PASS_VOLUME_LIGHT; - g_data->cryptomatte_accurate_mode = (view_layer->cryptomatte_flag & - VIEW_LAYER_CRYPTOMATTE_ACCURATE) != 0; - } -} - -void EEVEE_cryptomatte_output_init(EEVEE_ViewLayerData *UNUSED(sldata), - EEVEE_Data *vedata, - int UNUSED(tot_samples)) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_PrivateData *g_data = stl->g_data; - - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - const DRWContextState *draw_ctx = DRW_context_state_get(); - const ViewLayer *view_layer = draw_ctx->view_layer; - - const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer); - eGPUTextureFormat format = (num_cryptomatte_layers == 1) ? GPU_R32F : - (num_cryptomatte_layers == 2) ? GPU_RG32F : - GPU_RGBA32F; - const float *viewport_size = DRW_viewport_size_get(); - const int buffer_size = viewport_size[0] * viewport_size[1]; - - if (g_data->cryptomatte_accum_buffer == NULL) { - g_data->cryptomatte_accum_buffer = MEM_calloc_arrayN( - buffer_size * eevee_cryptomatte_pixel_stride(view_layer), - sizeof(EEVEE_CryptomatteSample), - __func__); - /* Download buffer should store a float per active cryptomatte layer. */ - g_data->cryptomatte_download_buffer = MEM_malloc_arrayN( - buffer_size * num_cryptomatte_layers, sizeof(float), __func__); - } - else { - /* During multiview rendering the `cryptomatte_accum_buffer` is deallocated after all views - * have been rendered. Clear it here to be reused by the next view. */ - memset(g_data->cryptomatte_accum_buffer, - 0, - buffer_size * eevee_cryptomatte_pixel_stride(view_layer) * - sizeof(EEVEE_CryptomatteSample)); - } - - DRW_texture_ensure_fullscreen_2d(&txl->cryptomatte, format, 0); - GPU_framebuffer_ensure_config(&fbl->cryptomatte_fb, - { - GPU_ATTACHMENT_TEXTURE(dtxl->depth), - GPU_ATTACHMENT_TEXTURE(txl->cryptomatte), - }); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Populate Cache - * \{ */ - -void EEVEE_cryptomatte_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) { - DRW_PASS_CREATE(psl->cryptomatte_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); - } -} - -static DRWShadingGroup *eevee_cryptomatte_shading_group_create(EEVEE_Data *vedata, - EEVEE_ViewLayerData *UNUSED(sldata), - Object *ob, - Material *material, - bool is_hair) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - const ViewLayer *view_layer = draw_ctx->view_layer; - const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers( - view_layer); - EEVEE_PrivateData *g_data = vedata->stl->g_data; - float cryptohash[4] = {0.0f}; - - EEVEE_PassList *psl = vedata->psl; - int layer_offset = 0; - if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) { - uint32_t cryptomatte_hash = BKE_cryptomatte_object_hash( - g_data->cryptomatte_session, "CryptoObject", ob); - float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash); - cryptohash[layer_offset] = cryptomatte_color_value; - layer_offset++; - } - if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) { - uint32_t cryptomatte_hash = BKE_cryptomatte_material_hash( - g_data->cryptomatte_session, "CryptoMaterial", material); - float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash); - cryptohash[layer_offset] = cryptomatte_color_value; - layer_offset++; - } - if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) { - uint32_t cryptomatte_hash = BKE_cryptomatte_asset_hash( - g_data->cryptomatte_session, "CryptoAsset", ob); - float cryptomatte_color_value = BKE_cryptomatte_hash_to_float(cryptomatte_hash); - cryptohash[layer_offset] = cryptomatte_color_value; - layer_offset++; - } - - DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_cryptomatte_sh_get(is_hair), - psl->cryptomatte_ps); - DRW_shgroup_uniform_vec4_copy(grp, "cryptohash", cryptohash); - - return grp; -} - -static void eevee_cryptomatte_hair_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob, - ParticleSystem *psys, - ModifierData *md, - Material *material) -{ - DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create( - vedata, sldata, ob, material, true); - DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL); -} - -void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob) -{ - BLI_assert(ob->type == OB_HAIR); - Material *material = BKE_object_material_get_eval(ob, HAIR_MATERIAL_NR); - eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, NULL, NULL, material); -} - -void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - - if (ob->type == OB_MESH) { - if (ob != draw_ctx->object_edit) { - LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { - if (md->type != eModifierType_ParticleSystem) { - continue; - } - ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; - if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) { - continue; - } - ParticleSettings *part = psys->part; - const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; - if (draw_as != PART_DRAW_PATH) { - continue; - } - Material *material = BKE_object_material_get_eval(ob, part->omat); - eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, psys, md, material); - } - } - } -} - -void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - const ViewLayer *view_layer = draw_ctx->view_layer; - const eViewLayerCryptomatteFlags cryptomatte_layers = eevee_cryptomatte_active_layers( - view_layer); - - if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) { - const int materials_len = DRW_cache_object_material_count_get(ob); - struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len); - memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len); - struct GPUBatch **geoms = DRW_cache_object_surface_material_get( - ob, gpumat_array, materials_len); - if (geoms) { - for (int i = 0; i < materials_len; i++) { - struct GPUBatch *geom = geoms[i]; - if (geom == NULL) { - continue; - } - Material *material = BKE_object_material_get_eval(ob, i + 1); - DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create( - vedata, sldata, ob, material, false); - DRW_shgroup_call(grp, geom, ob); - } - } - } - else { - GPUBatch *geom = DRW_cache_object_surface_get(ob); - if (geom) { - DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create( - vedata, sldata, ob, NULL, false); - DRW_shgroup_call(grp, geom, ob); - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Accumulate Samples - * \{ */ - -/* Downloads cryptomatte sample buffer from the GPU and integrate the samples with the accumulated - * cryptomatte samples. */ -static void eevee_cryptomatte_download_buffer(EEVEE_Data *vedata, GPUFrameBuffer *framebuffer) -{ - EEVEE_StorageList *stl = vedata->stl; - EEVEE_PrivateData *g_data = stl->g_data; - const DRWContextState *draw_ctx = DRW_context_state_get(); - const ViewLayer *view_layer = draw_ctx->view_layer; - const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer); - const int num_levels = view_layer->cryptomatte_levels; - const float *viewport_size = DRW_viewport_size_get(); - const int buffer_size = viewport_size[0] * viewport_size[1]; - - EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer; - float *download_buffer = g_data->cryptomatte_download_buffer; - - BLI_assert(accum_buffer); - BLI_assert(download_buffer); - - GPU_framebuffer_read_color(framebuffer, - 0, - 0, - viewport_size[0], - viewport_size[1], - num_cryptomatte_layers, - 0, - GPU_DATA_FLOAT, - download_buffer); - - /* Integrate download buffer into the accum buffer. - * The download buffer contains up to 3 floats per pixel (one float per cryptomatte layer. - * - * NOTE: here we deviate from the cryptomatte standard. During integration the standard always - * sort the samples by its weight to make sure that samples with the lowest weight - * are discarded first. In our case the weight of each sample is always 1 as we don't have - * subsamples and apply the coverage during the post processing. When there is no room for new - * samples the new samples has a weight of 1 and will always be discarded. */ - int download_pixel_index = 0; - int accum_pixel_index = 0; - int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer); - for (int pixel_index = 0; pixel_index < buffer_size; pixel_index++) { - for (int layer = 0; layer < num_cryptomatte_layers; layer++) { - const int layer_offset = eevee_cryptomatte_layer_offset(view_layer, layer); - float download_hash = download_buffer[download_pixel_index++]; - for (int level = 0; level < num_levels; level++) { - EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level]; - if (sample->hash == download_hash) { - sample->weight += 1.0f; - break; - } - /* We test against weight as hash 0.0f is used for samples hitting the world background. */ - if (sample->weight == 0.0f) { - sample->hash = download_hash; - sample->weight = 1.0f; - break; - } - } - } - accum_pixel_index += accum_pixel_stride; - } -} - -void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_PrivateData *g_data = stl->g_data; - EEVEE_EffectsInfo *effects = stl->effects; - EEVEE_PassList *psl = vedata->psl; - const DRWContextState *draw_ctx = DRW_context_state_get(); - const ViewLayer *view_layer = draw_ctx->view_layer; - const int cryptomatte_levels = view_layer->cryptomatte_levels; - const int current_sample = effects->taa_current_sample; - - /* In accurate mode all render samples are evaluated. In inaccurate mode this is limited to the - * number of cryptomatte levels. This will reduce the overhead of downloading the GPU buffer and - * integrating it into the accum buffer. */ - if (g_data->cryptomatte_accurate_mode || current_sample < cryptomatte_levels) { - static float clear_color[4] = {0.0}; - GPU_framebuffer_bind(fbl->cryptomatte_fb); - GPU_framebuffer_clear_color(fbl->cryptomatte_fb, clear_color); - DRW_draw_pass(psl->cryptomatte_ps); - - eevee_cryptomatte_download_buffer(vedata, fbl->cryptomatte_fb); - - /* Restore */ - GPU_framebuffer_bind(fbl->main_fb); - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Update Render Passes - * \{ */ - -void EEVEE_cryptomatte_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer) -{ - char cryptomatte_pass_name[MAX_NAME]; - const short num_passes = eevee_cryptomatte_passes_per_layer(view_layer); - if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) { - for (short pass = 0; pass < num_passes; pass++) { - BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoObject%02d", pass); - RE_engine_register_pass( - engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA); - } - } - if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) { - for (short pass = 0; pass < num_passes; pass++) { - BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoMaterial%02d", pass); - RE_engine_register_pass( - engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA); - } - } - if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) { - for (short pass = 0; pass < num_passes; pass++) { - BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoAsset%02d", pass); - RE_engine_register_pass( - engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA); - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Construct Render Result - * \{ */ - -/* Compare function for cryptomatte samples. Samples with the highest weight will be at the - * beginning of the list. */ -static int eevee_cryptomatte_sample_cmp_reverse(const void *a_, const void *b_) -{ - const EEVEE_CryptomatteSample *a = a_; - const EEVEE_CryptomatteSample *b = b_; - if (a->weight < b->weight) { - return 1; - } - if (a->weight > b->weight) { - return -1; - } - - return 0; -} - -/* Post process the weights. The accumulated weights buffer adds one to each weight per sample. - * During post processing ensure that the total of weights per sample is between 0 and 1. */ -static void eevee_cryptomatte_postprocess_weights(EEVEE_Data *vedata) -{ - EEVEE_StorageList *stl = vedata->stl; - EEVEE_PrivateData *g_data = stl->g_data; - EEVEE_EffectsInfo *effects = stl->effects; - EEVEE_TextureList *txl = vedata->txl; - const DRWContextState *draw_ctx = DRW_context_state_get(); - const ViewLayer *view_layer = draw_ctx->view_layer; - const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer); - const int num_levels = view_layer->cryptomatte_levels; - const float *viewport_size = DRW_viewport_size_get(); - const int buffer_size = viewport_size[0] * viewport_size[1]; - - EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer; - BLI_assert(accum_buffer); - float *volumetric_transmittance_buffer = NULL; - if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) { - volumetric_transmittance_buffer = GPU_texture_read( - txl->volume_transmittance_accum, GPU_DATA_FLOAT, 0); - } - const int num_samples = effects->taa_current_sample - 1; - - int accum_pixel_index = 0; - int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer); - - for (int pixel_index = 0; pixel_index < buffer_size; - pixel_index++, accum_pixel_index += accum_pixel_stride) { - float coverage = 1.0f; - if (volumetric_transmittance_buffer != NULL) { - coverage = (volumetric_transmittance_buffer[pixel_index * 4] + - volumetric_transmittance_buffer[pixel_index * 4 + 1] + - volumetric_transmittance_buffer[pixel_index * 4 + 2]) / - (3.0f * num_samples); - } - for (int layer = 0; layer < num_cryptomatte_layers; layer++) { - const int layer_offset = eevee_cryptomatte_layer_offset(view_layer, layer); - /* Calculate the total weight of the sample. */ - float total_weight = 0.0f; - for (int level = 0; level < num_levels; level++) { - EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level]; - total_weight += sample->weight; - } - BLI_assert(total_weight > 0.0f); - - float total_weight_inv = coverage / total_weight; - if (total_weight_inv > 0.0f) { - for (int level = 0; level < num_levels; level++) { - EEVEE_CryptomatteSample *sample = - &accum_buffer[accum_pixel_index + layer_offset + level]; - /* Remove background samples. These samples were used to determine the correct weight - * but won't be part of the final result. */ - if (sample->hash == 0.0f) { - sample->weight = 0.0f; - } - sample->weight *= total_weight_inv; - } - - /* Sort accum buffer by coverage of each sample. */ - qsort(&accum_buffer[accum_pixel_index + layer_offset], - num_levels, - sizeof(EEVEE_CryptomatteSample), - eevee_cryptomatte_sample_cmp_reverse); - } - else { - /* This pixel doesn't have any weight, so clear it fully. */ - for (int level = 0; level < num_levels; level++) { - EEVEE_CryptomatteSample *sample = - &accum_buffer[accum_pixel_index + layer_offset + level]; - sample->weight = 0.0f; - sample->hash = 0.0f; - } - } - } - } - - if (volumetric_transmittance_buffer) { - MEM_freeN(volumetric_transmittance_buffer); - } -} - -/* Extract cryptomatte layer from the cryptomatte_accum_buffer to render passes. */ -static void eevee_cryptomatte_extract_render_passes( - RenderLayer *rl, - const char *viewname, - const char *render_pass_name_format, - EEVEE_CryptomatteSample *accum_buffer, - /* number of render passes per cryptomatte layer. */ - const int num_cryptomatte_passes, - const int num_cryptomatte_levels, - const int accum_pixel_stride, - const int layer_stride, - const int layer_index, - const int rect_width, - const int rect_height, - const int rect_offset_x, - const int rect_offset_y, - const int viewport_width) -{ - char cryptomatte_pass_name[MAX_NAME]; - /* A pass can store 2 levels. Technically the last pass can have a single level if the number of - * levels is an odd number. This parameter counts the number of levels it has processed. */ - int levels_done = 0; - for (int pass = 0; pass < num_cryptomatte_passes; pass++) { - /* Each pass holds 2 cryptomatte samples. */ - const int pass_offset = pass * 2; - BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, render_pass_name_format, pass); - RenderPass *rp_object = RE_pass_find_by_name(rl, cryptomatte_pass_name, viewname); - for (int y = 0; y < rect_height; y++) { - for (int x = 0; x < rect_width; x++) { - const int accum_buffer_offset = (rect_offset_x + x + - (rect_offset_y + y) * viewport_width) * - accum_pixel_stride + - layer_index * layer_stride + pass_offset; - const int render_pass_offset = (y * rect_width + x) * 4; - rp_object->rect[render_pass_offset] = accum_buffer[accum_buffer_offset].hash; - rp_object->rect[render_pass_offset + 1] = accum_buffer[accum_buffer_offset].weight; - if (levels_done + 1 < num_cryptomatte_levels) { - rp_object->rect[render_pass_offset + 2] = accum_buffer[accum_buffer_offset + 1].hash; - rp_object->rect[render_pass_offset + 3] = accum_buffer[accum_buffer_offset + 1].weight; - } - else { - rp_object->rect[render_pass_offset + 2] = 0.0f; - rp_object->rect[render_pass_offset + 3] = 0.0f; - } - } - } - levels_done++; - } -} - -void EEVEE_cryptomatte_render_result(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *UNUSED(sldata)) -{ - EEVEE_PrivateData *g_data = vedata->stl->g_data; - const DRWContextState *draw_ctx = DRW_context_state_get(); - const ViewLayer *view_layer = draw_ctx->view_layer; - const eViewLayerCryptomatteFlags cryptomatte_layers = view_layer->cryptomatte_flag & - VIEW_LAYER_CRYPTOMATTE_ALL; - - eevee_cryptomatte_postprocess_weights(vedata); - - const int rect_width = BLI_rcti_size_x(rect); - const int rect_height = BLI_rcti_size_y(rect); - const int rect_offset_x = vedata->stl->g_data->overscan_pixels + rect->xmin; - const int rect_offset_y = vedata->stl->g_data->overscan_pixels + rect->ymin; - const float *viewport_size = DRW_viewport_size_get(); - const int viewport_width = viewport_size[0]; - EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer; - BLI_assert(accum_buffer); - const int num_cryptomatte_levels = view_layer->cryptomatte_levels; - const int num_cryptomatte_passes = eevee_cryptomatte_passes_per_layer(view_layer); - const int layer_stride = eevee_cryptomatte_layer_stride(view_layer); - const int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer); - - int layer_index = 0; - if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) { - eevee_cryptomatte_extract_render_passes(rl, - viewname, - "CryptoObject%02d", - accum_buffer, - num_cryptomatte_passes, - num_cryptomatte_levels, - accum_pixel_stride, - layer_stride, - layer_index, - rect_width, - rect_height, - rect_offset_x, - rect_offset_y, - viewport_width); - layer_index++; - } - if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) { - eevee_cryptomatte_extract_render_passes(rl, - viewname, - "CryptoMaterial%02d", - accum_buffer, - num_cryptomatte_passes, - num_cryptomatte_levels, - accum_pixel_stride, - layer_stride, - layer_index, - rect_width, - rect_height, - rect_offset_x, - rect_offset_y, - viewport_width); - layer_index++; - } - if ((cryptomatte_layers & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) { - eevee_cryptomatte_extract_render_passes(rl, - viewname, - "CryptoAsset%02d", - accum_buffer, - num_cryptomatte_passes, - num_cryptomatte_levels, - accum_pixel_stride, - layer_stride, - layer_index, - rect_width, - rect_height, - rect_offset_x, - rect_offset_y, - viewport_width); - layer_index++; - } -} - -void EEVEE_cryptomatte_store_metadata(EEVEE_Data *vedata, RenderResult *render_result) -{ - EEVEE_PrivateData *g_data = vedata->stl->g_data; - const DRWContextState *draw_ctx = DRW_context_state_get(); - const ViewLayer *view_layer = draw_ctx->view_layer; - BLI_assert(g_data->cryptomatte_session); - - BKE_cryptomatte_store_metadata(g_data->cryptomatte_session, render_result, view_layer); -} - -/** \} */ - -void EEVEE_cryptomatte_free(EEVEE_Data *vedata) -{ - EEVEE_PrivateData *g_data = vedata->stl->g_data; - MEM_SAFE_FREE(g_data->cryptomatte_accum_buffer); - MEM_SAFE_FREE(g_data->cryptomatte_download_buffer); - if (g_data->cryptomatte_session) { - BKE_cryptomatte_free(g_data->cryptomatte_session); - g_data->cryptomatte_session = NULL; - } -} diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c deleted file mode 100644 index b453df284ed..00000000000 --- a/source/blender/draw/engines/eevee/eevee_data.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - * - * All specific data handler for Objects, Lights, ViewLayers, ... - */ - -#include "DRW_render.h" - -#include "BLI_ghash.h" -#include "BLI_memblock.h" - -#include "BKE_duplilist.h" -#include "BKE_modifier.h" -#include "BKE_object.h" - -#include "DEG_depsgraph_query.h" - -#include "GPU_vertex_buffer.h" - -#include "eevee_lightcache.h" -#include "eevee_private.h" - -/* Motion Blur data. */ - -static void eevee_motion_blur_mesh_data_free(void *val) -{ - EEVEE_GeometryMotionData *geom_mb = (EEVEE_GeometryMotionData *)val; - EEVEE_HairMotionData *hair_mb = (EEVEE_HairMotionData *)val; - switch (geom_mb->type) { - case EEVEE_MOTION_DATA_HAIR: - for (int j = 0; j < hair_mb->psys_len; j++) { - for (int i = 0; i < ARRAY_SIZE(hair_mb->psys[0].hair_pos); i++) { - GPU_VERTBUF_DISCARD_SAFE(hair_mb->psys[j].hair_pos[i]); - } - for (int i = 0; i < ARRAY_SIZE(hair_mb->psys[0].hair_pos); i++) { - DRW_TEXTURE_FREE_SAFE(hair_mb->psys[j].hair_pos_tx[i]); - } - } - break; - - case EEVEE_MOTION_DATA_MESH: - for (int i = 0; i < ARRAY_SIZE(geom_mb->vbo); i++) { - GPU_VERTBUF_DISCARD_SAFE(geom_mb->vbo[i]); - } - break; - } - MEM_freeN(val); -} - -static uint eevee_object_key_hash(const void *key) -{ - EEVEE_ObjectKey *ob_key = (EEVEE_ObjectKey *)key; - uint hash = BLI_ghashutil_ptrhash(ob_key->ob); - hash = BLI_ghashutil_combine_hash(hash, BLI_ghashutil_ptrhash(ob_key->parent)); - for (int i = 0; i < MAX_DUPLI_RECUR; i++) { - if (ob_key->id[i] != 0) { - hash = BLI_ghashutil_combine_hash(hash, BLI_ghashutil_inthash(ob_key->id[i])); - } - else { - break; - } - } - return hash; -} - -/* Return false if equal. */ -static bool eevee_object_key_cmp(const void *a, const void *b) -{ - EEVEE_ObjectKey *key_a = (EEVEE_ObjectKey *)a; - EEVEE_ObjectKey *key_b = (EEVEE_ObjectKey *)b; - - if (key_a->ob != key_b->ob) { - return true; - } - if (key_a->parent != key_b->parent) { - return true; - } - if (memcmp(key_a->id, key_b->id, sizeof(key_a->id)) != 0) { - return true; - } - return false; -} - -void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb) -{ - if (mb->object == NULL) { - mb->object = BLI_ghash_new(eevee_object_key_hash, eevee_object_key_cmp, "EEVEE Object Motion"); - } - if (mb->geom == NULL) { - mb->geom = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE Mesh Motion"); - } -} - -void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb) -{ - if (mb->object) { - BLI_ghash_free(mb->object, MEM_freeN, MEM_freeN); - mb->object = NULL; - } - if (mb->geom) { - BLI_ghash_free(mb->geom, NULL, eevee_motion_blur_mesh_data_free); - mb->geom = NULL; - } -} - -EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, - Object *ob, - bool hair) -{ - if (mb->object == NULL) { - return NULL; - } - - EEVEE_ObjectKey key, *key_p; - /* Small hack to avoid another comparison. */ - key.ob = (Object *)((char *)ob + hair); - DupliObject *dup = DRW_object_get_dupli(ob); - if (dup) { - key.parent = DRW_object_get_dupli_parent(ob); - memcpy(key.id, dup->persistent_id, sizeof(key.id)); - } - else { - key.parent = key.ob; - memset(key.id, 0, sizeof(key.id)); - } - - EEVEE_ObjectMotionData *ob_step = BLI_ghash_lookup(mb->object, &key); - if (ob_step == NULL) { - key_p = MEM_mallocN(sizeof(*key_p), __func__); - memcpy(key_p, &key, sizeof(*key_p)); - - ob_step = MEM_callocN(sizeof(EEVEE_ObjectMotionData), __func__); - - BLI_ghash_insert(mb->object, key_p, ob_step); - } - return ob_step; -} - -static void *motion_blur_deform_data_get(EEVEE_MotionBlurData *mb, Object *ob, bool hair) -{ - if (mb->geom == NULL) { - return NULL; - } - DupliObject *dup = DRW_object_get_dupli(ob); - void *key; - if (dup) { - key = dup->ob; - } - else { - key = ob; - } - /* Only use data for object that have no modifiers. */ - if (!BKE_object_is_modified(DRW_context_state_get()->scene, ob)) { - key = ob->data; - } - key = (char *)key + (int)hair; - EEVEE_GeometryMotionData *geom_step = BLI_ghash_lookup(mb->geom, key); - if (geom_step == NULL) { - if (hair) { - EEVEE_HairMotionData *hair_step; - /* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */ - int psys_len = (ob->type != OB_HAIR) ? BLI_listbase_count(&ob->modifiers) : 1; - hair_step = MEM_callocN(sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len, - __func__); - hair_step->psys_len = psys_len; - geom_step = (EEVEE_GeometryMotionData *)hair_step; - geom_step->type = EEVEE_MOTION_DATA_HAIR; - } - else { - geom_step = MEM_callocN(sizeof(EEVEE_GeometryMotionData), __func__); - geom_step->type = EEVEE_MOTION_DATA_MESH; - } - BLI_ghash_insert(mb->geom, key, geom_step); - } - return geom_step; -} - -EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb, Object *ob) -{ - return motion_blur_deform_data_get(mb, ob, false); -} - -EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_MotionBlurData *mb, Object *ob) -{ - return motion_blur_deform_data_get(mb, ob, true); -} - -/* View Layer data. */ - -void EEVEE_view_layer_data_free(void *storage) -{ - EEVEE_ViewLayerData *sldata = (EEVEE_ViewLayerData *)storage; - - /* Lights */ - MEM_SAFE_FREE(sldata->lights); - DRW_UBO_FREE_SAFE(sldata->light_ubo); - DRW_UBO_FREE_SAFE(sldata->shadow_ubo); - GPU_FRAMEBUFFER_FREE_SAFE(sldata->shadow_fb); - DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_pool); - DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_pool); - for (int i = 0; i < 2; i++) { - MEM_SAFE_FREE(sldata->shcasters_buffers[i].bbox); - MEM_SAFE_FREE(sldata->shcasters_buffers[i].update); - } - - if (sldata->fallback_lightcache) { - EEVEE_lightcache_free(sldata->fallback_lightcache); - sldata->fallback_lightcache = NULL; - } - - /* Probes */ - MEM_SAFE_FREE(sldata->probes); - DRW_UBO_FREE_SAFE(sldata->probe_ubo); - DRW_UBO_FREE_SAFE(sldata->grid_ubo); - DRW_UBO_FREE_SAFE(sldata->planar_ubo); - DRW_UBO_FREE_SAFE(sldata->common_ubo); - - DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.combined); - DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.diff_color); - DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.diff_light); - DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.spec_color); - DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.spec_light); - DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.emit); - DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.environment); - for (int aov_index = 0; aov_index < MAX_AOVS; aov_index++) { - DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.aovs[aov_index]); - } - - if (sldata->material_cache) { - BLI_memblock_destroy(sldata->material_cache, NULL); - sldata->material_cache = NULL; - } -} - -EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void) -{ - return (EEVEE_ViewLayerData *)DRW_view_layer_engine_data_get(&draw_engine_eevee_type); -} - -static void eevee_view_layer_init(EEVEE_ViewLayerData *sldata) -{ - sldata->common_ubo = GPU_uniformbuf_create(sizeof(sldata->common_data)); -} - -EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer) -{ - EEVEE_ViewLayerData **sldata = (EEVEE_ViewLayerData **)DRW_view_layer_engine_data_ensure_ex( - view_layer, &draw_engine_eevee_type, &EEVEE_view_layer_data_free); - - if (*sldata == NULL) { - *sldata = MEM_callocN(sizeof(**sldata), "EEVEE_ViewLayerData"); - eevee_view_layer_init(*sldata); - } - - return *sldata; -} - -EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure(void) -{ - EEVEE_ViewLayerData **sldata = (EEVEE_ViewLayerData **)DRW_view_layer_engine_data_ensure( - &draw_engine_eevee_type, &EEVEE_view_layer_data_free); - - if (*sldata == NULL) { - *sldata = MEM_callocN(sizeof(**sldata), "EEVEE_ViewLayerData"); - eevee_view_layer_init(*sldata); - } - - return *sldata; -} - -/* Object data. */ - -static void eevee_object_data_init(DrawData *dd) -{ - EEVEE_ObjectEngineData *eevee_data = (EEVEE_ObjectEngineData *)dd; - eevee_data->shadow_caster_id = -1; - eevee_data->need_update = false; - eevee_data->geom_update = false; -} - -EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob) -{ - if (ELEM(ob->type, OB_LIGHTPROBE, OB_LAMP)) { - return NULL; - } - return (EEVEE_ObjectEngineData *)DRW_drawdata_get(&ob->id, &draw_engine_eevee_type); -} - -EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob) -{ - BLI_assert(!ELEM(ob->type, OB_LIGHTPROBE, OB_LAMP)); - return (EEVEE_ObjectEngineData *)DRW_drawdata_ensure(&ob->id, - &draw_engine_eevee_type, - sizeof(EEVEE_ObjectEngineData), - eevee_object_data_init, - NULL); -} - -/* Light probe data. */ - -static void eevee_lightprobe_data_init(DrawData *dd) -{ - EEVEE_LightProbeEngineData *ped = (EEVEE_LightProbeEngineData *)dd; - ped->need_update = false; -} - -EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob) -{ - if (ob->type != OB_LIGHTPROBE) { - return NULL; - } - return (EEVEE_LightProbeEngineData *)DRW_drawdata_get(&ob->id, &draw_engine_eevee_type); -} - -EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_ensure(Object *ob) -{ - BLI_assert(ob->type == OB_LIGHTPROBE); - return (EEVEE_LightProbeEngineData *)DRW_drawdata_ensure(&ob->id, - &draw_engine_eevee_type, - sizeof(EEVEE_LightProbeEngineData), - eevee_lightprobe_data_init, - NULL); -} - -/* Light data. */ - -static void eevee_light_data_init(DrawData *dd) -{ - EEVEE_LightEngineData *led = (EEVEE_LightEngineData *)dd; - led->need_update = true; -} - -EEVEE_LightEngineData *EEVEE_light_data_get(Object *ob) -{ - if (ob->type != OB_LAMP) { - return NULL; - } - return (EEVEE_LightEngineData *)DRW_drawdata_get(&ob->id, &draw_engine_eevee_type); -} - -EEVEE_LightEngineData *EEVEE_light_data_ensure(Object *ob) -{ - BLI_assert(ob->type == OB_LAMP); - return (EEVEE_LightEngineData *)DRW_drawdata_ensure(&ob->id, - &draw_engine_eevee_type, - sizeof(EEVEE_LightEngineData), - eevee_light_data_init, - NULL); -} - -/* World data. */ - -static void eevee_world_data_init(DrawData *dd) -{ - EEVEE_WorldEngineData *wed = (EEVEE_WorldEngineData *)dd; - wed->dd.recalc |= 1; -} - -EEVEE_WorldEngineData *EEVEE_world_data_get(World *wo) -{ - return (EEVEE_WorldEngineData *)DRW_drawdata_get(&wo->id, &draw_engine_eevee_type); -} - -EEVEE_WorldEngineData *EEVEE_world_data_ensure(World *wo) -{ - return (EEVEE_WorldEngineData *)DRW_drawdata_ensure(&wo->id, - &draw_engine_eevee_type, - sizeof(EEVEE_WorldEngineData), - eevee_world_data_init, - NULL); -} diff --git a/source/blender/draw/engines/eevee/eevee_depth_of_field.c b/source/blender/draw/engines/eevee/eevee_depth_of_field.c deleted file mode 100644 index 4748323d6a7..00000000000 --- a/source/blender/draw/engines/eevee/eevee_depth_of_field.c +++ /dev/null @@ -1,1048 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - * - * Depth of field post process effect. - * - * There are 2 methods to achieve this effect. - * - The first uses projection matrix offsetting and sample accumulation to give reference quality - * depth of field. But this needs many samples to hide the under-sampling. - * - The second one is a post-processing based one. It follows the implementation described in - * the presentation "Life of a Bokeh - Siggraph 2018" from Guillaume Abadie. There are some - * difference with our actual implementation that prioritize quality. - */ - -#include "DRW_render.h" - -#include "DNA_camera_types.h" -#include "DNA_screen_types.h" -#include "DNA_view3d_types.h" -#include "DNA_world_types.h" - -#include "BKE_camera.h" - -#include "BLI_string_utils.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" - -#include "GPU_framebuffer.h" -#include "GPU_texture.h" -#include "eevee_private.h" - -#define CAMERA_JITTER_RING_DENSITY 6 - -static float coc_radius_from_camera_depth(bool is_ortho, EEVEE_EffectsInfo *fx, float camera_depth) -{ - float multiplier = fx->dof_coc_params[0]; - float bias = fx->dof_coc_params[1]; - if (multiplier == 0.0f || bias == 0.0f) { - return 0.0f; - } - if (is_ortho) { - return (camera_depth + multiplier / bias) * multiplier; - } - return multiplier / camera_depth - bias; -} - -static float polygon_sides_length(float sides_count) -{ - return 2.0 * sin(M_PI / sides_count); -} - -/* Returns intersection ratio between the radius edge at theta and the polygon edge. - * Start first corners at theta == 0. */ -static float circle_to_polygon_radius(float sides_count, float theta) -{ - /* From Graphics Gems from CryENGINE 3 (Siggraph 2013) by Tiago Sousa (slide 36). */ - float side_angle = (2.0f * M_PI) / sides_count; - return cosf(side_angle * 0.5f) / - cosf(theta - side_angle * floorf((sides_count * theta + M_PI) / (2.0f * M_PI))); -} - -/* Remap input angle to have homogenous spacing of points along a polygon edge. - * Expect theta to be in [0..2pi] range. */ -static float circle_to_polygon_angle(float sides_count, float theta) -{ - float side_angle = (2.0f * M_PI) / sides_count; - float halfside_angle = side_angle * 0.5f; - float side = floorf(theta / side_angle); - /* Length of segment from center to the middle of polygon side. */ - float adjacent = circle_to_polygon_radius(sides_count, 0.0f); - - /* This is the relative position of the sample on the polygon half side. */ - float local_theta = theta - side * side_angle; - float ratio = (local_theta - halfside_angle) / halfside_angle; - - float halfside_len = polygon_sides_length(sides_count) * 0.5f; - float opposite = ratio * halfside_len; - - /* NOTE: atan(y_over_x) has output range [-M_PI_2..M_PI_2]. */ - float final_local_theta = atanf(opposite / adjacent); - - return side * side_angle + final_local_theta; -} - -static int dof_jitter_total_sample_count(int ring_density, int ring_count) -{ - return ((ring_count * ring_count + ring_count) / 2) * ring_density + 1; -} - -bool EEVEE_depth_of_field_jitter_get(EEVEE_EffectsInfo *fx, - float r_jitter[2], - float *r_focus_distance) -{ - if (fx->dof_jitter_radius == 0.0f) { - return false; - } - - int ring_density = CAMERA_JITTER_RING_DENSITY; - int ring_count = fx->dof_jitter_ring_count; - int sample_count = dof_jitter_total_sample_count(ring_density, ring_count); - - int s = fx->taa_current_sample - 1; - - int ring = 0; - int ring_sample_count = 1; - int ring_sample = 1; - - s = s * (ring_density - 1); - s = s % sample_count; - - int samples_passed = 1; - while (s >= samples_passed) { - ring++; - ring_sample_count = ring * ring_density; - ring_sample = s - samples_passed; - ring_sample = (ring_sample + 1) % ring_sample_count; - samples_passed += ring_sample_count; - } - - r_jitter[0] = (float)ring / ring_count; - r_jitter[1] = (float)ring_sample / ring_sample_count; - - { - /* Bokeh shape parameterization. */ - float r = r_jitter[0]; - float T = r_jitter[1] * 2.0f * M_PI; - - if (fx->dof_jitter_blades >= 3.0f) { - T = circle_to_polygon_angle(fx->dof_jitter_blades, T); - r *= circle_to_polygon_radius(fx->dof_jitter_blades, T); - } - - T += fx->dof_bokeh_rotation; - - r_jitter[0] = r * cosf(T); - r_jitter[1] = r * sinf(T); - - mul_v2_v2(r_jitter, fx->dof_bokeh_aniso); - } - - mul_v2_fl(r_jitter, fx->dof_jitter_radius); - - *r_focus_distance = fx->dof_jitter_focus; - return true; -} - -int EEVEE_depth_of_field_sample_count_get(EEVEE_EffectsInfo *effects, - int sample_count, - int *r_ring_count) -{ - if (effects->dof_jitter_radius == 0.0f) { - if (r_ring_count != NULL) { - *r_ring_count = 0; - } - return 1; - } - - if (sample_count == TAA_MAX_SAMPLE) { - /* Special case for viewport continuous rendering. We clamp to a max sample to avoid the - * jittered dof never converging. */ - sample_count = 1024; - } - /* Inversion of dof_jitter_total_sample_count. */ - float x = 2.0f * (sample_count - 1.0f) / CAMERA_JITTER_RING_DENSITY; - /* Solving polynomial. We only search positive solution. */ - float discriminant = 1.0f + 4.0f * x; - int ring_count = ceilf(0.5f * (sqrt(discriminant) - 1.0f)); - - sample_count = dof_jitter_total_sample_count(CAMERA_JITTER_RING_DENSITY, ring_count); - - if (r_ring_count != NULL) { - *r_ring_count = ring_count; - } - return sample_count; -} - -int EEVEE_depth_of_field_init(EEVEE_ViewLayerData *UNUSED(sldata), - EEVEE_Data *vedata, - Object *camera) -{ - EEVEE_TextureList *txl = vedata->txl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_EffectsInfo *effects = stl->effects; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - - Camera *cam = (camera != NULL) ? camera->data : NULL; - - if (cam && (cam->dof.flag & CAM_DOF_ENABLED)) { - RegionView3D *rv3d = draw_ctx->rv3d; - const float *viewport_size = DRW_viewport_size_get(); - - effects->dof_hq_slight_focus = (scene_eval->eevee.flag & SCE_EEVEE_DOF_HQ_SLIGHT_FOCUS) != 0; - - /* Retrieve Near and Far distance */ - effects->dof_coc_near_dist = -cam->clip_start; - effects->dof_coc_far_dist = -cam->clip_end; - - /* Parameters */ - bool is_ortho = cam->type == CAM_ORTHO; - float fstop = cam->dof.aperture_fstop; - float blades = cam->dof.aperture_blades; - float rotation = cam->dof.aperture_rotation; - float ratio = 1.0f / max_ff(cam->dof.aperture_ratio, 0.00001f); - float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y); - float focus_dist = BKE_camera_object_dof_distance(camera); - float focal_len = cam->lens; - - if (is_ortho) { - /* (fclem) A bit of black magic here. I don't know if this is correct. */ - fstop *= 1.3f; - focal_len = 1.0f; - sensor = cam->ortho_scale; - } - - const float scale_camera = (is_ortho) ? 1.0 : 0.001f; - /* We want radius here for the aperture number. */ - float aperture = 0.5f * scale_camera * focal_len / fstop; - float focal_len_scaled = scale_camera * focal_len; - float sensor_scaled = scale_camera * sensor; - - if (rv3d != NULL) { - sensor_scaled *= rv3d->viewcamtexcofac[0]; - } - - if (ratio > 1.0) { - /* If ratio is scaling the bokeh outwards, we scale the aperture so that the gather - * kernel size will encompass the maximum axis. */ - aperture *= ratio; - } - - effects->dof_coc_params[1] = -aperture * - fabsf(focal_len_scaled / (focus_dist - focal_len_scaled)); - /* FIXME(fclem) This is broken for vertically fit sensor. */ - effects->dof_coc_params[1] *= viewport_size[0] / sensor_scaled; - - if ((scene_eval->eevee.flag & SCE_EEVEE_DOF_JITTER) != 0) { - effects->dof_jitter_radius = effects->dof_coc_params[1]; - effects->dof_jitter_focus = focus_dist; - effects->dof_jitter_blades = blades; - - int sample_count = EEVEE_temporal_sampling_sample_count_get(scene_eval, stl); - sample_count = EEVEE_depth_of_field_sample_count_get( - effects, sample_count, &effects->dof_jitter_ring_count); - - if (effects->dof_jitter_ring_count == 0) { - effects->dof_jitter_radius = 0.0f; - } - else { - /* Compute a minimal overblur radius to fill the gaps between the samples. - * This is just the simplified form of dividing the area of the bokeh - * by the number of samples. */ - float minimal_overblur = 1.0f / sqrtf(sample_count); - float user_overblur = scene_eval->eevee.bokeh_overblur / 100.0f; - - minimal_overblur *= effects->dof_coc_params[1]; - user_overblur *= effects->dof_coc_params[1]; - - effects->dof_coc_params[1] = minimal_overblur + user_overblur; - /* Avoid dilating the shape. Over-blur only soften. */ - effects->dof_jitter_radius -= minimal_overblur + user_overblur * 0.5f; - } - } - else { - effects->dof_jitter_radius = 0.0f; - } - - if (is_ortho) { - /* (fclem) A bit of black magic here. Needed to match cycles. */ - effects->dof_coc_params[1] *= 0.225; - } - - effects->dof_coc_params[0] = -focus_dist * effects->dof_coc_params[1]; - - effects->dof_bokeh_blades = blades; - effects->dof_bokeh_rotation = rotation; - effects->dof_bokeh_aniso[0] = min_ff(ratio, 1.0f); - effects->dof_bokeh_aniso[1] = min_ff(1.0f / ratio, 1.0f); - effects->dof_bokeh_max_size = scene_eval->eevee.bokeh_max_size; - - copy_v2_v2(effects->dof_bokeh_aniso_inv, effects->dof_bokeh_aniso); - invert_v2(effects->dof_bokeh_aniso_inv); - - effects->dof_scatter_color_threshold = scene_eval->eevee.bokeh_threshold; - effects->dof_scatter_neighbor_max_color = scene_eval->eevee.bokeh_neighbor_max; - effects->dof_denoise_factor = clamp_f(scene_eval->eevee.bokeh_denoise_fac, 0.0f, 1.0f); - - float max_abs_fg_coc, max_abs_bg_coc; - if (is_ortho) { - max_abs_fg_coc = fabsf(coc_radius_from_camera_depth(true, effects, -cam->clip_start)); - max_abs_bg_coc = fabsf(coc_radius_from_camera_depth(true, effects, -cam->clip_end)); - } - else { - max_abs_fg_coc = fabsf(coc_radius_from_camera_depth(false, effects, -cam->clip_start)); - /* Background is at infinity so maximum CoC is the limit of the function at -inf. */ - max_abs_bg_coc = fabsf(effects->dof_coc_params[1]); - } - - float max_coc = max_ff(max_abs_bg_coc, max_abs_fg_coc); - /* Clamp with user defined max. */ - effects->dof_fx_max_coc = min_ff(scene_eval->eevee.bokeh_max_size, max_coc); - - if (effects->dof_fx_max_coc < 0.5f) { - return 0; - } - - return EFFECT_DOF | EFFECT_POST_BUFFER; - } - - effects->dof_jitter_radius = 0.0f; - - /* Cleanup to release memory */ - GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_setup_fb); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_flatten_tiles_fb); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_dilate_tiles_fb); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_reduce_fb); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_reduce_copy_fb); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_gather_fg_fb); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_gather_bg_fb); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->dof_scatter_bg_fb); - DRW_TEXTURE_FREE_SAFE(txl->dof_reduced_color); - DRW_TEXTURE_FREE_SAFE(txl->dof_reduced_coc); - - return 0; -} - -#define WITH_FILTERING (GPU_SAMPLER_MIPMAP | GPU_SAMPLER_FILTER) -#define NO_FILTERING GPU_SAMPLER_MIPMAP -#define COLOR_FORMAT fx->dof_color_format -#define FG_TILE_FORMAT GPU_RGBA16F -#define BG_TILE_FORMAT GPU_R11F_G11F_B10F - -/** - * Create bokeh texture. - */ -static void dof_bokeh_pass_init(EEVEE_FramebufferList *fbl, - EEVEE_PassList *psl, - EEVEE_EffectsInfo *fx) -{ - if ((fx->dof_bokeh_aniso[0] == 1.0f) && (fx->dof_bokeh_aniso[1] == 1.0f) && - (fx->dof_bokeh_blades == 0.0)) { - fx->dof_bokeh_gather_lut_tx = NULL; - fx->dof_bokeh_scatter_lut_tx = NULL; - fx->dof_bokeh_resolve_lut_tx = NULL; - return; - } - - void *owner = (void *)&EEVEE_depth_of_field_init; - int res[2] = {DOF_BOKEH_LUT_SIZE, DOF_BOKEH_LUT_SIZE}; - - DRW_PASS_CREATE(psl->dof_bokeh, DRW_STATE_WRITE_COLOR); - - GPUShader *sh = EEVEE_shaders_depth_of_field_bokeh_get(); - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_bokeh); - DRW_shgroup_uniform_float_copy(grp, "bokehSides", fx->dof_bokeh_blades); - DRW_shgroup_uniform_float_copy(grp, "bokehRotation", fx->dof_bokeh_rotation); - DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropyInv", fx->dof_bokeh_aniso_inv); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - fx->dof_bokeh_gather_lut_tx = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RG16F, owner); - fx->dof_bokeh_scatter_lut_tx = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner); - fx->dof_bokeh_resolve_lut_tx = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner); - - GPU_framebuffer_ensure_config(&fbl->dof_bokeh_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(fx->dof_bokeh_gather_lut_tx), - GPU_ATTACHMENT_TEXTURE(fx->dof_bokeh_scatter_lut_tx), - GPU_ATTACHMENT_TEXTURE(fx->dof_bokeh_resolve_lut_tx), - }); -} - -/** - * Outputs halfResColorBuffer and halfResCocBuffer. - */ -static void dof_setup_pass_init(EEVEE_FramebufferList *fbl, - EEVEE_PassList *psl, - EEVEE_EffectsInfo *fx) -{ - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - - void *owner = (void *)&EEVEE_depth_of_field_init; - const float *fullres = DRW_viewport_size_get(); - int res[2] = {divide_ceil_u(fullres[0], 2), divide_ceil_u(fullres[1], 2)}; - - DRW_PASS_CREATE(psl->dof_setup, DRW_STATE_WRITE_COLOR); - - GPUShader *sh = EEVEE_shaders_depth_of_field_setup_get(); - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_setup); - DRW_shgroup_uniform_texture_ref_ex(grp, "colorBuffer", &fx->source_buffer, NO_FILTERING); - DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &dtxl->depth, NO_FILTERING); - DRW_shgroup_uniform_vec4_copy(grp, "cocParams", fx->dof_coc_params); - DRW_shgroup_uniform_float_copy(grp, "bokehMaxSize", fx->dof_bokeh_max_size); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - fx->dof_half_res_color_tx = DRW_texture_pool_query_2d(UNPACK2(res), COLOR_FORMAT, owner); - fx->dof_half_res_coc_tx = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RG16F, owner); - - GPU_framebuffer_ensure_config(&fbl->dof_setup_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(fx->dof_half_res_color_tx), - GPU_ATTACHMENT_TEXTURE(fx->dof_half_res_coc_tx), - }); -} - -/** - * Outputs min & max COC in each 8x8 half res pixel tiles (so 1/16th of full resolution). - */ -static void dof_flatten_tiles_pass_init(EEVEE_FramebufferList *fbl, - EEVEE_PassList *psl, - EEVEE_EffectsInfo *fx) -{ - void *owner = (void *)&EEVEE_depth_of_field_init; - const float *fullres = DRW_viewport_size_get(); - int res[2] = {divide_ceil_u(fullres[0], DOF_TILE_DIVISOR), - divide_ceil_u(fullres[1], DOF_TILE_DIVISOR)}; - - DRW_PASS_CREATE(psl->dof_flatten_tiles, DRW_STATE_WRITE_COLOR); - - GPUShader *sh = EEVEE_shaders_depth_of_field_flatten_tiles_get(); - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_flatten_tiles); - DRW_shgroup_uniform_texture_ref_ex( - grp, "halfResCocBuffer", &fx->dof_half_res_coc_tx, NO_FILTERING); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - fx->dof_coc_tiles_fg_tx = DRW_texture_pool_query_2d(UNPACK2(res), FG_TILE_FORMAT, owner); - fx->dof_coc_tiles_bg_tx = DRW_texture_pool_query_2d(UNPACK2(res), BG_TILE_FORMAT, owner); - - GPU_framebuffer_ensure_config(&fbl->dof_flatten_tiles_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(fx->dof_coc_tiles_fg_tx), - GPU_ATTACHMENT_TEXTURE(fx->dof_coc_tiles_bg_tx), - }); -} - -/** - * Dilates the min & max COCS to cover maximum COC values. - * Output format/dimensions should be the same as coc_flatten_pass as they are swapped for - * doing multiple dilation passes. - */ -static void dof_dilate_tiles_pass_init(EEVEE_FramebufferList *fbl, - EEVEE_PassList *psl, - EEVEE_EffectsInfo *fx) -{ - void *owner = (void *)&EEVEE_depth_of_field_init; - const float *fullres = DRW_viewport_size_get(); - int res[2] = {divide_ceil_u(fullres[0], DOF_TILE_DIVISOR), - divide_ceil_u(fullres[1], DOF_TILE_DIVISOR)}; - - DRW_PASS_CREATE(psl->dof_dilate_tiles_minmax, DRW_STATE_WRITE_COLOR); - DRW_PASS_CREATE(psl->dof_dilate_tiles_minabs, DRW_STATE_WRITE_COLOR); - - for (int pass = 0; pass < 2; pass++) { - DRWPass *drw_pass = (pass == 0) ? psl->dof_dilate_tiles_minmax : psl->dof_dilate_tiles_minabs; - GPUShader *sh = EEVEE_shaders_depth_of_field_dilate_tiles_get(pass); - DRWShadingGroup *grp = DRW_shgroup_create(sh, drw_pass); - DRW_shgroup_uniform_texture_ref(grp, "cocTilesFgBuffer", &fx->dof_coc_tiles_fg_tx); - DRW_shgroup_uniform_texture_ref(grp, "cocTilesBgBuffer", &fx->dof_coc_tiles_bg_tx); - DRW_shgroup_uniform_bool(grp, "dilateSlightFocus", &fx->dof_dilate_slight_focus, 1); - DRW_shgroup_uniform_int(grp, "ringCount", &fx->dof_dilate_ring_count, 1); - DRW_shgroup_uniform_int(grp, "ringWidthMultiplier", &fx->dof_dilate_ring_width_multiplier, 1); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - } - - fx->dof_coc_dilated_tiles_fg_tx = DRW_texture_pool_query_2d(UNPACK2(res), FG_TILE_FORMAT, owner); - fx->dof_coc_dilated_tiles_bg_tx = DRW_texture_pool_query_2d(UNPACK2(res), BG_TILE_FORMAT, owner); - - GPU_framebuffer_ensure_config(&fbl->dof_dilate_tiles_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(fx->dof_coc_dilated_tiles_fg_tx), - GPU_ATTACHMENT_TEXTURE(fx->dof_coc_dilated_tiles_bg_tx), - }); -} - -static void dof_dilate_tiles_pass_draw(EEVEE_FramebufferList *fbl, - EEVEE_PassList *psl, - EEVEE_EffectsInfo *fx) -{ - for (int pass = 0; pass < 2; pass++) { - DRWPass *drw_pass = (pass == 0) ? psl->dof_dilate_tiles_minmax : psl->dof_dilate_tiles_minabs; - - /* Error introduced by gather center jittering. */ - const float error_multiplier = 1.0f + 1.0f / (DOF_GATHER_RING_COUNT + 0.5f); - int dilation_end_radius = ceilf((fx->dof_fx_max_coc * error_multiplier) / DOF_TILE_DIVISOR); - - /* This algorithm produce the exact dilation radius by dividing it in multiple passes. */ - int dilation_radius = 0; - while (dilation_radius < dilation_end_radius) { - /* Dilate slight focus only on first iteration. */ - fx->dof_dilate_slight_focus = (dilation_radius == 0) ? 1 : 0; - - int remainder = dilation_end_radius - dilation_radius; - /* Do not step over any unvisited tile. */ - int max_multiplier = dilation_radius + 1; - - int ring_count = min_ii(DOF_DILATE_RING_COUNT, ceilf(remainder / (float)max_multiplier)); - int multiplier = min_ii(max_multiplier, floor(remainder / (float)ring_count)); - - dilation_radius += ring_count * multiplier; - - fx->dof_dilate_ring_count = ring_count; - fx->dof_dilate_ring_width_multiplier = multiplier; - - GPU_framebuffer_bind(fbl->dof_dilate_tiles_fb); - DRW_draw_pass(drw_pass); - - SWAP(GPUFrameBuffer *, fbl->dof_dilate_tiles_fb, fbl->dof_flatten_tiles_fb); - SWAP(GPUTexture *, fx->dof_coc_dilated_tiles_bg_tx, fx->dof_coc_tiles_bg_tx); - SWAP(GPUTexture *, fx->dof_coc_dilated_tiles_fg_tx, fx->dof_coc_tiles_fg_tx); - } - } - /* Swap again so that final textures are dof_coc_dilated_tiles_*_tx. */ - SWAP(GPUFrameBuffer *, fbl->dof_dilate_tiles_fb, fbl->dof_flatten_tiles_fb); - SWAP(GPUTexture *, fx->dof_coc_dilated_tiles_bg_tx, fx->dof_coc_tiles_bg_tx); - SWAP(GPUTexture *, fx->dof_coc_dilated_tiles_fg_tx, fx->dof_coc_tiles_fg_tx); -} - -/** - * Create mipmapped color & COC textures for gather passes. - */ -static void dof_reduce_pass_init(EEVEE_FramebufferList *fbl, - EEVEE_PassList *psl, - EEVEE_TextureList *txl, - EEVEE_EffectsInfo *fx) -{ - const float *fullres = DRW_viewport_size_get(); - - /* Divide by 2 because dof_fx_max_coc is in fullres CoC radius and the reduce texture begins at - * half resolution. */ - float max_space_between_sample = fx->dof_fx_max_coc * 0.5f / DOF_GATHER_RING_COUNT; - - int mip_count = max_ii(1, log2_ceil_u(max_space_between_sample)); - - fx->dof_reduce_steps = mip_count - 1; - /* This ensure the mipmaps are aligned for the needed 4 mip levels. - * Starts at 2 because already at half resolution. */ - int multiple = 2 << (mip_count - 1); - int res[2] = {(multiple * divide_ceil_u(fullres[0], multiple)) / 2, - (multiple * divide_ceil_u(fullres[1], multiple)) / 2}; - - int quater_res[2] = {divide_ceil_u(fullres[0], 4), divide_ceil_u(fullres[1], 4)}; - - /* TODO(fclem): Make this dependent of the quality of the gather pass. */ - fx->dof_scatter_coc_threshold = 4.0f; - - { - DRW_PASS_CREATE(psl->dof_downsample, DRW_STATE_WRITE_COLOR); - - GPUShader *sh = EEVEE_shaders_depth_of_field_downsample_get(); - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_downsample); - DRW_shgroup_uniform_texture_ref_ex( - grp, "colorBuffer", &fx->dof_reduce_input_color_tx, NO_FILTERING); - DRW_shgroup_uniform_texture_ref_ex( - grp, "cocBuffer", &fx->dof_reduce_input_coc_tx, NO_FILTERING); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - void *owner = (void *)&EEVEE_depth_of_field_init; - fx->dof_downsample_tx = DRW_texture_pool_query_2d(UNPACK2(quater_res), COLOR_FORMAT, owner); - - GPU_framebuffer_ensure_config(&fbl->dof_downsample_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(fx->dof_downsample_tx), - }); - } - - { - DRW_PASS_CREATE(psl->dof_reduce_copy, DRW_STATE_WRITE_COLOR); - - const bool is_copy_pass = true; - GPUShader *sh = EEVEE_shaders_depth_of_field_reduce_get(is_copy_pass); - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_reduce_copy); - DRW_shgroup_uniform_texture_ref_ex( - grp, "colorBuffer", &fx->dof_reduce_input_color_tx, NO_FILTERING); - DRW_shgroup_uniform_texture_ref_ex( - grp, "cocBuffer", &fx->dof_reduce_input_coc_tx, NO_FILTERING); - DRW_shgroup_uniform_texture_ref_ex( - grp, "downsampledBuffer", &fx->dof_downsample_tx, NO_FILTERING); - DRW_shgroup_uniform_float_copy(grp, "scatterColorThreshold", fx->dof_scatter_color_threshold); - DRW_shgroup_uniform_float_copy( - grp, "scatterColorNeighborMax", fx->dof_scatter_neighbor_max_color); - DRW_shgroup_uniform_float_copy(grp, "scatterCocThreshold", fx->dof_scatter_coc_threshold); - DRW_shgroup_uniform_float_copy(grp, "colorNeighborClamping", fx->dof_denoise_factor); - DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropy", fx->dof_bokeh_aniso); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - void *owner = (void *)&EEVEE_depth_of_field_init; - fx->dof_scatter_src_tx = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R11F_G11F_B10F, owner); - } - - { - DRW_PASS_CREATE(psl->dof_reduce, DRW_STATE_WRITE_COLOR); - - const bool is_copy_pass = false; - GPUShader *sh = EEVEE_shaders_depth_of_field_reduce_get(is_copy_pass); - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_reduce); - DRW_shgroup_uniform_texture_ref_ex( - grp, "colorBuffer", &fx->dof_reduce_input_color_tx, NO_FILTERING); - DRW_shgroup_uniform_texture_ref_ex( - grp, "cocBuffer", &fx->dof_reduce_input_coc_tx, NO_FILTERING); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - } - - if (txl->dof_reduced_color) { - /* TODO(fclem) In the future, we need to check if mip_count did not change. - * For now it's ok as we always define all mip level. */ - if (res[0] != GPU_texture_width(txl->dof_reduced_color) || - res[1] != GPU_texture_width(txl->dof_reduced_color)) { - DRW_TEXTURE_FREE_SAFE(txl->dof_reduced_color); - DRW_TEXTURE_FREE_SAFE(txl->dof_reduced_coc); - } - } - - if (txl->dof_reduced_color == NULL) { - /* Color needs to be signed format here. See note in shader for explanation. */ - /* Do not use texture pool because of needs mipmaps. */ - txl->dof_reduced_color = GPU_texture_create_2d( - "dof_reduced_color", UNPACK2(res), mip_count, GPU_RGBA16F, NULL); - txl->dof_reduced_coc = GPU_texture_create_2d( - "dof_reduced_coc", UNPACK2(res), mip_count, GPU_R16F, NULL); - - /* TODO(fclem) Remove once we have immutable storage or when mips are generated on creation. */ - GPU_texture_generate_mipmap(txl->dof_reduced_color); - GPU_texture_generate_mipmap(txl->dof_reduced_coc); - } - - GPU_framebuffer_ensure_config(&fbl->dof_reduce_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(txl->dof_reduced_color), - GPU_ATTACHMENT_TEXTURE(txl->dof_reduced_coc), - }); - - GPU_framebuffer_ensure_config(&fbl->dof_reduce_copy_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(txl->dof_reduced_color), - GPU_ATTACHMENT_TEXTURE(txl->dof_reduced_coc), - GPU_ATTACHMENT_TEXTURE(fx->dof_scatter_src_tx), - }); -} - -/** - * Do the gather convolution. For each pixels we gather multiple pixels in its neighborhood - * depending on the min & max CoC tiles. - */ -static void dof_gather_pass_init(EEVEE_FramebufferList *fbl, - EEVEE_PassList *psl, - EEVEE_TextureList *txl, - EEVEE_EffectsInfo *fx) -{ - void *owner = (void *)&EEVEE_depth_of_field_init; - const float *fullres = DRW_viewport_size_get(); - int res[2] = {divide_ceil_u(fullres[0], 2), divide_ceil_u(fullres[1], 2)}; - int input_size[2]; - GPU_texture_get_mipmap_size(txl->dof_reduced_color, 0, input_size); - float uv_correction_fac[2] = {res[0] / (float)input_size[0], res[1] / (float)input_size[1]}; - float output_texel_size[2] = {1.0f / res[0], 1.0f / res[1]}; - const bool use_bokeh_tx = (fx->dof_bokeh_gather_lut_tx != NULL); - - { - DRW_PASS_CREATE(psl->dof_gather_fg_holefill, DRW_STATE_WRITE_COLOR); - - GPUShader *sh = EEVEE_shaders_depth_of_field_gather_get(DOF_GATHER_HOLEFILL, false); - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_gather_fg_holefill); - DRW_shgroup_uniform_texture_ref_ex( - grp, "colorBufferBilinear", &txl->dof_reduced_color, WITH_FILTERING); - DRW_shgroup_uniform_texture_ref_ex(grp, "colorBuffer", &txl->dof_reduced_color, NO_FILTERING); - DRW_shgroup_uniform_texture_ref_ex(grp, "cocBuffer", &txl->dof_reduced_coc, NO_FILTERING); - DRW_shgroup_uniform_texture_ref(grp, "cocTilesFgBuffer", &fx->dof_coc_dilated_tiles_fg_tx); - DRW_shgroup_uniform_texture_ref(grp, "cocTilesBgBuffer", &fx->dof_coc_dilated_tiles_bg_tx); - DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); - DRW_shgroup_uniform_vec2_copy(grp, "gatherInputUvCorrection", uv_correction_fac); - DRW_shgroup_uniform_vec2_copy(grp, "gatherOutputTexelSize", output_texel_size); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - /* Reuse textures from the setup pass. */ - /* NOTE: We could use the texture pool do that for us but it does not track usage and it might - * backfire (it does in practice). */ - fx->dof_fg_holefill_color_tx = fx->dof_half_res_color_tx; - fx->dof_fg_holefill_weight_tx = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner); - - GPU_framebuffer_ensure_config(&fbl->dof_gather_fg_holefill_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(fx->dof_fg_holefill_color_tx), - GPU_ATTACHMENT_TEXTURE(fx->dof_fg_holefill_weight_tx), - }); - } - { - DRW_PASS_CREATE(psl->dof_gather_fg, DRW_STATE_WRITE_COLOR); - - GPUShader *sh = EEVEE_shaders_depth_of_field_gather_get(DOF_GATHER_FOREGROUND, use_bokeh_tx); - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_gather_fg); - DRW_shgroup_uniform_texture_ref_ex( - grp, "colorBufferBilinear", &txl->dof_reduced_color, WITH_FILTERING); - DRW_shgroup_uniform_texture_ref_ex(grp, "colorBuffer", &txl->dof_reduced_color, NO_FILTERING); - DRW_shgroup_uniform_texture_ref_ex(grp, "cocBuffer", &txl->dof_reduced_coc, NO_FILTERING); - DRW_shgroup_uniform_texture_ref(grp, "cocTilesFgBuffer", &fx->dof_coc_dilated_tiles_fg_tx); - DRW_shgroup_uniform_texture_ref(grp, "cocTilesBgBuffer", &fx->dof_coc_dilated_tiles_bg_tx); - DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); - DRW_shgroup_uniform_vec2_copy(grp, "gatherInputUvCorrection", uv_correction_fac); - DRW_shgroup_uniform_vec2_copy(grp, "gatherOutputTexelSize", output_texel_size); - if (use_bokeh_tx) { - /* Negate to flip bokeh shape. Mimics optical phenomenon. */ - negate_v2(fx->dof_bokeh_aniso); - DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropy", fx->dof_bokeh_aniso); - DRW_shgroup_uniform_texture_ref(grp, "bokehLut", &fx->dof_bokeh_gather_lut_tx); - /* Restore. */ - negate_v2(fx->dof_bokeh_aniso); - } - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - fx->dof_fg_color_tx = DRW_texture_pool_query_2d(UNPACK2(res), COLOR_FORMAT, owner); - fx->dof_fg_weight_tx = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner); - /* Reuse textures from the setup pass. */ - /* NOTE: We could use the texture pool do that for us but it does not track usage and it might - * backfire (it does in practice). */ - fx->dof_fg_occlusion_tx = fx->dof_half_res_coc_tx; - - /* NOTE: First target is holefill texture so we can use the median filter on it. - * See the filter function. */ - GPU_framebuffer_ensure_config(&fbl->dof_gather_fg_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(fx->dof_fg_holefill_color_tx), - GPU_ATTACHMENT_TEXTURE(fx->dof_fg_holefill_weight_tx), - GPU_ATTACHMENT_TEXTURE(fx->dof_fg_occlusion_tx), - }); - } - { - DRW_PASS_CREATE(psl->dof_gather_bg, DRW_STATE_WRITE_COLOR); - - GPUShader *sh = EEVEE_shaders_depth_of_field_gather_get(DOF_GATHER_BACKGROUND, use_bokeh_tx); - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_gather_bg); - DRW_shgroup_uniform_texture_ref_ex( - grp, "colorBufferBilinear", &txl->dof_reduced_color, WITH_FILTERING); - DRW_shgroup_uniform_texture_ref_ex(grp, "colorBuffer", &txl->dof_reduced_color, NO_FILTERING); - DRW_shgroup_uniform_texture_ref_ex(grp, "cocBuffer", &txl->dof_reduced_coc, NO_FILTERING); - DRW_shgroup_uniform_texture_ref(grp, "cocTilesFgBuffer", &fx->dof_coc_dilated_tiles_fg_tx); - DRW_shgroup_uniform_texture_ref(grp, "cocTilesBgBuffer", &fx->dof_coc_dilated_tiles_bg_tx); - DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); - DRW_shgroup_uniform_vec2_copy(grp, "gatherInputUvCorrection", uv_correction_fac); - DRW_shgroup_uniform_vec2_copy(grp, "gatherOutputTexelSize", output_texel_size); - if (use_bokeh_tx) { - DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropy", fx->dof_bokeh_aniso); - DRW_shgroup_uniform_texture_ref(grp, "bokehLut", &fx->dof_bokeh_gather_lut_tx); - } - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - fx->dof_bg_color_tx = DRW_texture_pool_query_2d(UNPACK2(res), COLOR_FORMAT, owner); - fx->dof_bg_weight_tx = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner); - /* Reuse, since only used for scatter. Foreground is processed before background. */ - fx->dof_bg_occlusion_tx = fx->dof_fg_occlusion_tx; - - /* NOTE: First target is holefill texture so we can use the median filter on it. - * See the filter function. */ - GPU_framebuffer_ensure_config(&fbl->dof_gather_bg_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(fx->dof_fg_holefill_color_tx), - GPU_ATTACHMENT_TEXTURE(fx->dof_fg_holefill_weight_tx), - GPU_ATTACHMENT_TEXTURE(fx->dof_bg_occlusion_tx), - }); - } -} - -/** - * Filter an input buffer using a median filter to reduce noise. - * NOTE: We use the holefill texture as our input to reduce memory usage. - * Thus, the holefill pass cannot be filtered. - */ -static void dof_filter_pass_init(EEVEE_FramebufferList *fbl, - EEVEE_PassList *psl, - EEVEE_EffectsInfo *fx) -{ - DRW_PASS_CREATE(psl->dof_filter, DRW_STATE_WRITE_COLOR); - - GPUShader *sh = EEVEE_shaders_depth_of_field_filter_get(); - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_filter); - DRW_shgroup_uniform_texture_ref_ex( - grp, "colorBuffer", &fx->dof_fg_holefill_color_tx, NO_FILTERING); - DRW_shgroup_uniform_texture_ref_ex( - grp, "weightBuffer", &fx->dof_fg_holefill_weight_tx, NO_FILTERING); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - GPU_framebuffer_ensure_config(&fbl->dof_filter_fg_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(fx->dof_fg_color_tx), - GPU_ATTACHMENT_TEXTURE(fx->dof_fg_weight_tx), - }); - - GPU_framebuffer_ensure_config(&fbl->dof_filter_bg_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(fx->dof_bg_color_tx), - GPU_ATTACHMENT_TEXTURE(fx->dof_bg_weight_tx), - }); -} - -/** - * Do the Scatter convolution. A sprite is emitted for every 4 pixels but is only expanded if the - * pixels are bright enough to be scattered. - */ -static void dof_scatter_pass_init(EEVEE_FramebufferList *fbl, - EEVEE_PassList *psl, - EEVEE_TextureList *txl, - EEVEE_EffectsInfo *fx) -{ - int input_size[2], target_size[2]; - GPU_texture_get_mipmap_size(fx->dof_half_res_color_tx, 0, input_size); - GPU_texture_get_mipmap_size(fx->dof_bg_color_tx, 0, target_size); - /* Draw a sprite for every four half-res pixels. */ - int sprite_count = (input_size[0] / 2) * (input_size[1] / 2); - float target_texel_size[2] = {1.0f / target_size[0], 1.0f / target_size[1]}; - const bool use_bokeh_tx = (fx->dof_bokeh_gather_lut_tx != NULL); - - { - DRW_PASS_CREATE(psl->dof_scatter_fg, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL); - - const bool is_foreground = true; - GPUShader *sh = EEVEE_shaders_depth_of_field_scatter_get(is_foreground, use_bokeh_tx); - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_scatter_fg); - DRW_shgroup_uniform_texture_ref_ex(grp, "colorBuffer", &fx->dof_scatter_src_tx, NO_FILTERING); - DRW_shgroup_uniform_texture_ref_ex(grp, "cocBuffer", &txl->dof_reduced_coc, NO_FILTERING); - DRW_shgroup_uniform_texture_ref(grp, "occlusionBuffer", &fx->dof_fg_occlusion_tx); - DRW_shgroup_uniform_vec2_copy(grp, "targetTexelSize", target_texel_size); - DRW_shgroup_uniform_int_copy(grp, "spritePerRow", input_size[0] / 2); - DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropy", fx->dof_bokeh_aniso); - if (use_bokeh_tx) { - /* Negate to flip bokeh shape. Mimics optical phenomenon. */ - negate_v2(fx->dof_bokeh_aniso_inv); - DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropyInv", fx->dof_bokeh_aniso_inv); - DRW_shgroup_uniform_texture_ref(grp, "bokehLut", &fx->dof_bokeh_scatter_lut_tx); - /* Restore. */ - negate_v2(fx->dof_bokeh_aniso_inv); - } - DRW_shgroup_call_procedural_triangles(grp, NULL, sprite_count); - - GPU_framebuffer_ensure_config(&fbl->dof_scatter_fg_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(fx->dof_fg_color_tx), - }); - } - { - DRW_PASS_CREATE(psl->dof_scatter_bg, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL); - - const bool is_foreground = false; - GPUShader *sh = EEVEE_shaders_depth_of_field_scatter_get(is_foreground, use_bokeh_tx); - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_scatter_bg); - DRW_shgroup_uniform_texture_ref_ex(grp, "colorBuffer", &fx->dof_scatter_src_tx, NO_FILTERING); - DRW_shgroup_uniform_texture_ref_ex(grp, "cocBuffer", &txl->dof_reduced_coc, NO_FILTERING); - DRW_shgroup_uniform_texture_ref(grp, "occlusionBuffer", &fx->dof_bg_occlusion_tx); - DRW_shgroup_uniform_vec2_copy(grp, "targetTexelSize", target_texel_size); - DRW_shgroup_uniform_int_copy(grp, "spritePerRow", input_size[0] / 2); - DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropy", fx->dof_bokeh_aniso); - if (use_bokeh_tx) { - DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropyInv", fx->dof_bokeh_aniso_inv); - DRW_shgroup_uniform_texture_ref(grp, "bokehLut", &fx->dof_bokeh_scatter_lut_tx); - } - DRW_shgroup_call_procedural_triangles(grp, NULL, sprite_count); - - GPU_framebuffer_ensure_config(&fbl->dof_scatter_bg_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(fx->dof_bg_color_tx), - }); - } -} - -/** - * Recombine the result of the foreground and background processing. Also perform a slight out of - * focus blur to improve geometric continuity. - */ -static void dof_recombine_pass_init(EEVEE_FramebufferList *UNUSED(fbl), - EEVEE_PassList *psl, - EEVEE_EffectsInfo *fx) -{ - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - const bool use_bokeh_tx = (fx->dof_bokeh_gather_lut_tx != NULL); - - DRW_PASS_CREATE(psl->dof_resolve, DRW_STATE_WRITE_COLOR); - - GPUShader *sh = EEVEE_shaders_depth_of_field_resolve_get(use_bokeh_tx, fx->dof_hq_slight_focus); - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->dof_resolve); - DRW_shgroup_uniform_texture_ref_ex(grp, "fullResColorBuffer", &fx->source_buffer, NO_FILTERING); - DRW_shgroup_uniform_texture_ref_ex(grp, "fullResDepthBuffer", &dtxl->depth, NO_FILTERING); - DRW_shgroup_uniform_texture_ref(grp, "bgColorBuffer", &fx->dof_bg_color_tx); - DRW_shgroup_uniform_texture_ref(grp, "bgWeightBuffer", &fx->dof_bg_weight_tx); - DRW_shgroup_uniform_texture_ref(grp, "bgTileBuffer", &fx->dof_coc_dilated_tiles_bg_tx); - DRW_shgroup_uniform_texture_ref(grp, "fgColorBuffer", &fx->dof_fg_color_tx); - DRW_shgroup_uniform_texture_ref(grp, "fgWeightBuffer", &fx->dof_fg_weight_tx); - DRW_shgroup_uniform_texture_ref(grp, "holefillColorBuffer", &fx->dof_fg_holefill_color_tx); - DRW_shgroup_uniform_texture_ref(grp, "holefillWeightBuffer", &fx->dof_fg_holefill_weight_tx); - DRW_shgroup_uniform_texture_ref(grp, "fgTileBuffer", &fx->dof_coc_dilated_tiles_fg_tx); - DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); - DRW_shgroup_uniform_vec4_copy(grp, "cocParams", fx->dof_coc_params); - DRW_shgroup_uniform_float_copy(grp, "bokehMaxSize", fx->dof_bokeh_max_size); - if (use_bokeh_tx) { - DRW_shgroup_uniform_vec2_copy(grp, "bokehAnisotropyInv", fx->dof_bokeh_aniso_inv); - DRW_shgroup_uniform_texture_ref(grp, "bokehLut", &fx->dof_bokeh_resolve_lut_tx); - } - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); -} - -void EEVEE_depth_of_field_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_TextureList *txl = vedata->txl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *fx = stl->effects; - - if ((fx->enabled_effects & EFFECT_DOF) != 0) { - /* GPU_RGBA16F is sufficient now that all scattered bokeh are premultiplied. - * GPU_R11F_G11F_B10F is not enough when lots of scattered sprites are big and offers - * relatively small benefits. */ - fx->dof_color_format = GPU_RGBA16F; - - dof_bokeh_pass_init(fbl, psl, fx); - dof_setup_pass_init(fbl, psl, fx); - dof_flatten_tiles_pass_init(fbl, psl, fx); - dof_dilate_tiles_pass_init(fbl, psl, fx); - dof_reduce_pass_init(fbl, psl, txl, fx); - dof_gather_pass_init(fbl, psl, txl, fx); - dof_filter_pass_init(fbl, psl, fx); - dof_scatter_pass_init(fbl, psl, txl, fx); - dof_recombine_pass_init(fbl, psl, fx); - } -} - -static void dof_recursive_reduce(void *vedata, int UNUSED(level)) -{ - EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; - EEVEE_TextureList *txl = ((EEVEE_Data *)vedata)->txl; - EEVEE_EffectsInfo *fx = ((EEVEE_Data *)vedata)->stl->effects; - - fx->dof_reduce_input_color_tx = txl->dof_reduced_color; - fx->dof_reduce_input_coc_tx = txl->dof_reduced_coc; - - DRW_draw_pass(psl->dof_reduce); -} - -void EEVEE_depth_of_field_draw(EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; /* TODO(fclem): Because of silly SWAP_BUFFERS. */ - EEVEE_EffectsInfo *fx = effects; - - /* Depth Of Field */ - if ((effects->enabled_effects & EFFECT_DOF) != 0) { - DRW_stats_group_start("Depth of Field"); - - if (fx->dof_bokeh_gather_lut_tx != NULL) { - GPU_framebuffer_bind(fbl->dof_bokeh_fb); - DRW_draw_pass(psl->dof_bokeh); - } - - GPU_framebuffer_bind(fbl->dof_setup_fb); - DRW_draw_pass(psl->dof_setup); - - GPU_framebuffer_bind(fbl->dof_flatten_tiles_fb); - DRW_draw_pass(psl->dof_flatten_tiles); - - dof_dilate_tiles_pass_draw(fbl, psl, fx); - - fx->dof_reduce_input_color_tx = fx->dof_half_res_color_tx; - fx->dof_reduce_input_coc_tx = fx->dof_half_res_coc_tx; - - /* First step is just a copy. */ - GPU_framebuffer_bind(fbl->dof_downsample_fb); - DRW_draw_pass(psl->dof_downsample); - - /* First step is just a copy. */ - GPU_framebuffer_bind(fbl->dof_reduce_copy_fb); - DRW_draw_pass(psl->dof_reduce_copy); - - GPU_framebuffer_recursive_downsample( - fbl->dof_reduce_fb, fx->dof_reduce_steps, &dof_recursive_reduce, vedata); - - { - /* Foreground convolution. */ - GPU_framebuffer_bind(fbl->dof_gather_fg_fb); - DRW_draw_pass(psl->dof_gather_fg); - - GPU_framebuffer_bind(fbl->dof_filter_fg_fb); - DRW_draw_pass(psl->dof_filter); - - GPU_framebuffer_bind(fbl->dof_scatter_fg_fb); - DRW_draw_pass(psl->dof_scatter_fg); - } - - { - /* Background convolution. */ - GPU_framebuffer_bind(fbl->dof_gather_bg_fb); - DRW_draw_pass(psl->dof_gather_bg); - - GPU_framebuffer_bind(fbl->dof_filter_bg_fb); - DRW_draw_pass(psl->dof_filter); - - GPU_framebuffer_bind(fbl->dof_scatter_bg_fb); - DRW_draw_pass(psl->dof_scatter_bg); - } - - { - /* Hole-fill convolution. */ - GPU_framebuffer_bind(fbl->dof_gather_fg_holefill_fb); - DRW_draw_pass(psl->dof_gather_fg_holefill); - - /* NOTE: do not filter the hole-fill pass as we use it as out filter input buffer. */ - } - - GPU_framebuffer_bind(fx->target_buffer); - DRW_draw_pass(psl->dof_resolve); - - SWAP_BUFFERS(); - - DRW_stats_group_end(); - } -} diff --git a/source/blender/draw/engines/eevee/eevee_depth_of_field.cc b/source/blender/draw/engines/eevee/eevee_depth_of_field.cc new file mode 100644 index 00000000000..131b80fbd83 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_depth_of_field.cc @@ -0,0 +1,731 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Depth of field post process effect. + * + * There are 2 methods to achieve this effect. + * - The first uses projection matrix offsetting and sample accumulation to give + * reference quality depth of field. But this needs many samples to hide the + * under-sampling. + * - The second one is a post-processing based one. It follows the + * implementation described in the presentation "Life of a Bokeh - Siggraph + * 2018" from Guillaume Abadie. There are some difference with our actual + * implementation that prioritize quality. + */ + +#include "DRW_render.h" + +#include "BKE_camera.h" +#include "DNA_camera_types.h" + +#include "GPU_texture.h" +#include "GPU_uniform_buffer.h" + +#include "eevee_camera.hh" +#include "eevee_instance.hh" +#include "eevee_sampling.hh" +#include "eevee_shader.hh" +#include "eevee_shader_shared.hh" + +#include "eevee_depth_of_field.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Depth of field + * \{ */ + +void DepthOfField::init(void) +{ + const Instance &inst = inst_; + const SceneEEVEE &sce_eevee = inst.scene->eevee; + do_hq_slight_focus_ = (sce_eevee.flag & SCE_EEVEE_DOF_HQ_SLIGHT_FOCUS) != 0; + do_jitter_ = (sce_eevee.flag & SCE_EEVEE_DOF_JITTER) != 0; + user_overblur_ = sce_eevee.bokeh_overblur / 100.0f; + fx_max_coc_ = sce_eevee.bokeh_max_size; + data_.scatter_color_threshold = sce_eevee.bokeh_threshold; + data_.scatter_neighbor_max_color = sce_eevee.bokeh_neighbor_max; + data_.denoise_factor = sce_eevee.bokeh_denoise_fac; + /* Default to no depth of field. */ + fx_radius_ = 0.0f; + jitter_radius_ = 0.0f; +} + +void DepthOfField::sync(const mat4 winmat, ivec2 input_extent) +{ + const Object *camera_object_eval = inst_.camera_eval_object; + const ::Camera *cam = (camera_object_eval) ? + reinterpret_cast<const ::Camera *>(camera_object_eval->data) : + nullptr; + + if (cam == nullptr || (cam->dof.flag & CAM_DOF_ENABLED) == 0) { + fx_radius_ = 0.0f; + jitter_radius_ = 0.0f; + return; + } + + extent_ = input_extent; + + data_.bokeh_blades = cam->dof.aperture_blades; + data_.bokeh_rotation = cam->dof.aperture_rotation; + data_.bokeh_anisotropic_scale.x = clamp_f(1.0f / cam->dof.aperture_ratio, 0.00001f, 1.0f); + data_.bokeh_anisotropic_scale.y = clamp_f(cam->dof.aperture_ratio, 0.00001f, 1.0f); + data_.bokeh_anisotropic_scale_inv = 1.0f / data_.bokeh_anisotropic_scale; + + focus_distance_ = BKE_camera_object_dof_distance(camera_object_eval); + float fstop = max_ff(cam->dof.aperture_fstop, 1e-5f); + float aperture = 1.0f / (2.0f * fstop); + if (cam->type == CAM_PERSP) { + aperture *= cam->lens * 1e-3f; + } + + if (cam->type == CAM_ORTHO) { + /* FIXME: Why is this needed? Some kind of implicit unit conversion? */ + aperture *= 0.04f; + /* Really strange behavior from Cycles but replicating. */ + focus_distance_ += cam->clip_start; + } + + if (cam->type == CAM_PANO) { + /* FIXME: Eyeballed. */ + aperture *= 0.185f; + } + + if (cam->dof.aperture_ratio < 1.0) { + /* If ratio is scaling the bokeh outwards, we scale the aperture so that + * the gather kernel size will encompass the maximum axis. */ + aperture /= max_ff(cam->dof.aperture_ratio, 1e-5f); + } + + /* Balance blur radius between fx dof and jitter dof. */ + if (do_jitter_ && (inst_.sampling.dof_ring_count_get() > 0) && (cam->type != CAM_PANO)) { + /* Compute a minimal overblur radius to fill the gaps between the samples. + * This is just the simplified form of dividing the area of the bokeh by + * the number of samples. */ + float minimal_overblur = 1.0f / sqrtf(inst_.sampling.dof_sample_count_get()); + + fx_radius_ = (minimal_overblur + user_overblur_) * aperture; + /* Avoid dilating the shape. Over-blur only soften. */ + jitter_radius_ = max_ff(0.0f, aperture - fx_radius_); + } + else { + jitter_radius_ = 0.0f; + fx_radius_ = aperture; + } + + if (fx_max_coc_ > 0.0f && fx_radius_ > 0.0f) { + data_.camera_type = inst_.camera.data_get().type; + /* OPTI(fclem) Could be optimized. */ + float jitter[3] = {fx_radius_, 0.0f, -focus_distance_}; + float center[3] = {0.0f, 0.0f, -focus_distance_}; + mul_project_m4_v3(winmat, jitter); + mul_project_m4_v3(winmat, center); + /* Simplify CoC calculation to a simple MADD. */ + if (data_.camera_type != CAMERA_ORTHO) { + data_.coc_bias = -(center[0] - jitter[0]) * 0.5f * extent_[0]; + data_.coc_mul = focus_distance_ * data_.coc_bias; + } + else { + data_.coc_mul = (center[0] - jitter[0]) * 0.5f * extent_[0]; + data_.coc_bias = focus_distance_ * data_.coc_mul; + } + + float min_fg_coc = coc_radius_from_camera_depth(data_, -cam->clip_start); + float max_bg_coc = coc_radius_from_camera_depth(data_, -cam->clip_end); + if (data_.camera_type != CAMERA_ORTHO) { + /* Background is at infinity so maximum CoC is the limit of the function at -inf. */ + /* NOTE: we only do this for perspective camera since orthographic coc limit is inf. */ + max_bg_coc = data_.coc_bias; + } + /* Clamp with user defined max. */ + data_.coc_abs_max = min_ff(max_ff(fabsf(min_fg_coc), fabsf(max_bg_coc)), fx_max_coc_); + + bokeh_lut_pass_sync(); + setup_pass_sync(); + tiles_prepare_pass_sync(); + reduce_pass_sync(); + convolve_pass_sync(); + resolve_pass_sync(); + + data_.push_update(); + } +} + +void DepthOfField::jitter_apply(mat4 winmat, mat4 viewmat) +{ + if (jitter_radius_ == 0.0f) { + return; + } + float radius, theta; + inst_.sampling.dof_disk_sample_get(&radius, &theta); + + if (data_.bokeh_blades >= 3.0f) { + theta = circle_to_polygon_angle(data_.bokeh_blades, theta); + radius *= circle_to_polygon_radius(data_.bokeh_blades, theta); + } + radius *= jitter_radius_; + theta += data_.bokeh_rotation; + + /* Sample in View Space. */ + vec2 sample = vec2(radius * cosf(theta), radius * sinf(theta)); + sample *= data_.bokeh_anisotropic_scale; + /* Convert to NDC Space. */ + vec3 jitter = vec3(UNPACK2(sample), -focus_distance_); + vec3 center = vec3(0.0f, 0.0f, -focus_distance_); + mul_project_m4_v3(winmat, jitter); + mul_project_m4_v3(winmat, center); + + const bool is_ortho = (winmat[2][3] != -1.0f); + if (is_ortho) { + sample *= focus_distance_; + } + /* Translate origin. */ + sub_v2_v2(viewmat[3], sample); + /* Skew winmat Z axis. */ + add_v2_v2(winmat[2], center - jitter); +} + +/** Will swap input and output texture if rendering happens. The actual output of this function + * is in intput_tx. */ +void DepthOfField::render(GPUTexture *depth_tx, GPUTexture **input_tx, GPUTexture **output_tx) +{ + if (fx_radius_ == 0.0f || fx_max_coc_ < 0.5f) { + return; + } + + input_color_tx_ = *input_tx; + input_depth_tx_ = depth_tx; + + DRW_stats_group_start("Depth of Field"); + + bokeh_lut_pass_render(); + + setup_pass_render(); + tiles_prepare_pass_render(); + reduce_pass_render(); + convolve_pass_render(); + resolve_pass_render(*output_tx); + + DRW_stats_group_end(); + + /* Swap buffers so that next effect has the right input. */ + *input_tx = *output_tx; + *output_tx = input_color_tx_; +} + +/** + * Creates bokeh texture. + **/ +void DepthOfField::bokeh_lut_pass_sync(void) +{ + const bool has_anisotropy = data_.bokeh_anisotropic_scale != vec2(1.0f); + if (has_anisotropy && (data_.bokeh_blades == 0.0)) { + bokeh_gather_lut_tx_ = nullptr; + bokeh_scatter_lut_tx_ = nullptr; + bokeh_resolve_lut_tx_ = nullptr; + bokeh_lut_ps_ = nullptr; + return; + } + + DrawEngineType *owner = (DrawEngineType *)view_name_.c_str(); + int res[2] = {DOF_BOKEH_LUT_SIZE, DOF_BOKEH_LUT_SIZE}; + + DRW_PASS_CREATE(bokeh_lut_ps_, DRW_STATE_WRITE_COLOR); + GPUShader *sh = inst_.shaders.static_shader_get(DOF_BOKEH_LUT); + DRWShadingGroup *grp = DRW_shgroup_create(sh, bokeh_lut_ps_); + DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get()); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + + bokeh_gather_lut_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RG16F, owner); + bokeh_scatter_lut_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner); + bokeh_resolve_lut_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner); + + bokeh_lut_fb_.ensure(GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(bokeh_gather_lut_tx_), + GPU_ATTACHMENT_TEXTURE(bokeh_scatter_lut_tx_), + GPU_ATTACHMENT_TEXTURE(bokeh_resolve_lut_tx_)); +} + +void DepthOfField::bokeh_lut_pass_render(void) +{ + if (bokeh_lut_ps_) { + GPU_framebuffer_bind(bokeh_lut_fb_); + DRW_draw_pass(bokeh_lut_ps_); + } +} + +/** + * Outputs halfResColorBuffer and halfResCocBuffer. + **/ +void DepthOfField::setup_pass_sync(void) +{ + DrawEngineType *owner = (DrawEngineType *)view_name_.c_str(); + uint res[2] = {divide_ceil_u(extent_[0], 2), divide_ceil_u(extent_[1], 2)}; + + DRW_PASS_CREATE(setup_ps_, DRW_STATE_WRITE_COLOR); + GPUShader *sh = inst_.shaders.static_shader_get(DOF_SETUP); + DRWShadingGroup *grp = DRW_shgroup_create(sh, setup_ps_); + + eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT; + DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &input_color_tx_, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &input_depth_tx_, no_filter); + DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get()); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + + setup_color_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner); + setup_coc_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RG16F, owner); + + setup_fb_.ensure(GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(setup_color_tx_), + GPU_ATTACHMENT_TEXTURE(setup_coc_tx_)); +} + +void DepthOfField::setup_pass_render(void) +{ + GPU_framebuffer_bind(setup_fb_); + DRW_draw_pass(setup_ps_); +} + +/** + * Outputs min & max COC in each 8x8 half res pixel tiles (so 1/16th of full resolution). + * Then dilates the min & max CoCs to cover maximum COC values. + **/ +void DepthOfField::tiles_prepare_pass_sync(void) +{ + DrawEngineType *owner = (DrawEngineType *)view_name_.c_str(); + uint res[2] = {divide_ceil_u(extent_[0], DOF_TILE_DIVISOR), + divide_ceil_u(extent_[1], DOF_TILE_DIVISOR)}; + /* WARNING: If you change this, make sure dof_tile_* GLSL constants can be properly encoded. */ + eGPUTextureFormat fg_tile_format = GPU_RGBA16F; + eGPUTextureFormat bg_tile_format = GPU_R11F_G11F_B10F; + + eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT; + { + DRW_PASS_CREATE(tiles_flatten_ps_, DRW_STATE_WRITE_COLOR); + + GPUShader *sh = inst_.shaders.static_shader_get(DOF_TILES_FLATTEN); + DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_flatten_ps_); + DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + + tiles_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), fg_tile_format, owner); + tiles_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), bg_tile_format, owner); + + tiles_flatten_fb_.ensure(GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(tiles_fg_tx_), + GPU_ATTACHMENT_TEXTURE(tiles_bg_tx_)); + } + { + DRW_PASS_CREATE(tiles_dilate_minmax_ps_, DRW_STATE_WRITE_COLOR); + DRW_PASS_CREATE(tiles_dilate_minabs_ps_, DRW_STATE_WRITE_COLOR); + + for (int pass = 0; pass < 2; pass++) { + DRWPass *drw_pass = (pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_; + GPUShader *sh = inst_.shaders.static_shader_get((pass == 0) ? DOF_TILES_DILATE_MINMAX : + DOF_TILES_DILATE_MINABS); + DRWShadingGroup *grp = DRW_shgroup_create(sh, drw_pass); + DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_fg_tx", &tiles_fg_tx_, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_bg_tx", &tiles_bg_tx_, no_filter); + DRW_shgroup_uniform_bool(grp, "dilate_slight_focus", &tiles_dilate_slight_focus_, 1); + DRW_shgroup_uniform_int(grp, "ring_count", &tiles_dilate_ring_count_, 1); + DRW_shgroup_uniform_int( + grp, "ring_width_multiplier", &tiles_dilate_ring_width_multiplier_, 1); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + } + + tiles_dilated_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), fg_tile_format, owner); + tiles_dilated_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), bg_tile_format, owner); + + tiles_dilate_fb_.ensure(GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(tiles_dilated_fg_tx_), + GPU_ATTACHMENT_TEXTURE(tiles_dilated_bg_tx_)); + } +} + +void DepthOfField::tiles_prepare_pass_render(void) +{ + GPU_framebuffer_bind(tiles_flatten_fb_); + DRW_draw_pass(tiles_flatten_ps_); + + /* Run dilation twice. One for minmax and one for minabs. */ + for (int pass = 0; pass < 2; pass++) { + /* Error introduced by gather center jittering. */ + const float error_multiplier = 1.0f + 1.0f / (DOF_GATHER_RING_COUNT + 0.5f); + int dilation_end_radius = ceilf((fx_max_coc_ * error_multiplier) / DOF_TILE_DIVISOR); + + /* This algorithm produce the exact dilation radius by dividing it in multiple passes. */ + int dilation_radius = 0; + while (dilation_radius < dilation_end_radius) { + /* Dilate slight focus only on first iteration. */ + tiles_dilate_slight_focus_ = (dilation_radius == 0) ? 1 : 0; + + int remainder = dilation_end_radius - dilation_radius; + /* Do not step over any unvisited tile. */ + int max_multiplier = dilation_radius + 1; + + int ring_count = min_ii(DOF_DILATE_RING_COUNT, ceilf(remainder / (float)max_multiplier)); + int multiplier = min_ii(max_multiplier, floor(remainder / (float)ring_count)); + + dilation_radius += ring_count * multiplier; + + tiles_dilate_ring_count_ = ring_count; + tiles_dilate_ring_width_multiplier_ = multiplier; + + GPU_framebuffer_bind(tiles_dilate_fb_); + DRW_draw_pass((pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_); + + SWAP(eevee::Framebuffer, tiles_dilate_fb_, tiles_flatten_fb_); + SWAP(GPUTexture *, tiles_dilated_bg_tx_, tiles_bg_tx_); + SWAP(GPUTexture *, tiles_dilated_fg_tx_, tiles_fg_tx_); + } + } + /* Swap again so that final textures are tiles_dilated_*_tx_. */ + SWAP(eevee::Framebuffer, tiles_dilate_fb_, tiles_flatten_fb_); + SWAP(GPUTexture *, tiles_dilated_bg_tx_, tiles_bg_tx_); + SWAP(GPUTexture *, tiles_dilated_fg_tx_, tiles_fg_tx_); +} + +/** + * Create mipmapped color & COC textures for gather passes. + **/ +void DepthOfField::reduce_pass_sync(void) +{ + DrawEngineType *owner = (DrawEngineType *)view_name_.c_str(); + eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT; + /* Divide by 2 because dof_fx_max_coc is in fullres CoC radius and the reduce + * texture begins at half resolution. */ + float max_space_between_sample = fx_max_coc_ * 0.5f / DOF_GATHER_RING_COUNT; + + int mip_count = max_ii(1, log2_ceil_u(max_space_between_sample)); + + reduce_steps_ = mip_count - 1; + /* This ensure the mipmaps are aligned for the needed 4 mip levels. + * Starts at 2 because already at half resolution. */ + int multiple = 2 << (mip_count - 1); + uint res[2] = {(multiple * divide_ceil_u(extent_[0], multiple)) / 2, + (multiple * divide_ceil_u(extent_[1], multiple)) / 2}; + + uint quater_res[2] = {divide_ceil_u(extent_[0], 4), divide_ceil_u(extent_[1], 4)}; + + /* TODO(fclem): Make this dependent of the quality of the gather pass. */ + data_.scatter_coc_threshold = 4.0f; + + /* Color needs to be signed format here. See note in shader for explanation. */ + /* Do not use texture pool because of needs mipmaps. */ + reduced_color_tx_.ensure(UNPACK2(res), mip_count, GPU_RGBA16F); + reduced_coc_tx_.ensure(UNPACK2(res), mip_count, GPU_R16F); + + { + DRW_PASS_CREATE(reduce_downsample_ps_, DRW_STATE_WRITE_COLOR); + GPUShader *sh = inst_.shaders.static_shader_get(DOF_REDUCE_DOWNSAMPLE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, reduce_downsample_ps_); + DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &setup_color_tx_, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + + reduce_downsample_tx_ = DRW_texture_pool_query_2d(UNPACK2(quater_res), GPU_RGBA16F, owner); + + reduce_downsample_fb_.ensure(GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(reduce_downsample_tx_)); + } + { + DRW_PASS_CREATE(reduce_copy_ps_, DRW_STATE_WRITE_COLOR); + GPUShader *sh = inst_.shaders.static_shader_get(DOF_REDUCE_COPY); + DRWShadingGroup *grp = DRW_shgroup_create(sh, reduce_copy_ps_); + DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &setup_color_tx_, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter); + DRW_shgroup_uniform_texture_ex(grp, "downsampled_tx", reduce_downsample_tx_, no_filter); + DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get()); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + + scatter_src_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R11F_G11F_B10F, owner); + } + { + DRW_PASS_CREATE(reduce_recursive_ps_, DRW_STATE_WRITE_COLOR); + GPUShader *sh = inst_.shaders.static_shader_get(DOF_REDUCE_RECURSIVE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, reduce_recursive_ps_); + DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter); + DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + } + + reduce_fb_.ensure(GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(reduced_color_tx_), + GPU_ATTACHMENT_TEXTURE(reduced_coc_tx_)); + + reduce_copy_fb_.ensure(GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(reduced_color_tx_), + GPU_ATTACHMENT_TEXTURE(reduced_coc_tx_), + GPU_ATTACHMENT_TEXTURE(scatter_src_tx_)); +} + +void DepthOfField::reduce_recusive(void *thunk, int UNUSED(level)) +{ + DepthOfField *dof = reinterpret_cast<DepthOfField *>(thunk); + DRW_draw_pass(dof->reduce_recursive_ps_); +} + +void DepthOfField::reduce_pass_render(void) +{ + GPU_framebuffer_bind(reduce_downsample_fb_); + DRW_draw_pass(reduce_downsample_ps_); + + /* First step is just a copy. */ + GPU_framebuffer_bind(reduce_copy_fb_); + DRW_draw_pass(reduce_copy_ps_); + + GPU_framebuffer_recursive_downsample(reduce_fb_, reduce_steps_, &reduce_recusive, this); +} + +/** + * Do the gather & scatter convolution. For each pixels we gather multiple pixels in its + * neighborhood depending on the min & max CoC tiles. We apply a median filter on the output. + * We also scatter a sprite for very bright pixels for high quality bokeh. + **/ +void DepthOfField::convolve_pass_sync(void) +{ + eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT; + eGPUSamplerState with_filter = (GPU_SAMPLER_MIPMAP | GPU_SAMPLER_FILTER); + DrawEngineType *owner = (DrawEngineType *)view_name_.c_str(); + uint res[2] = {divide_ceil_u(extent_[0], 2), divide_ceil_u(extent_[1], 2)}; + int input_size[2]; + GPU_texture_get_mipmap_size(reduced_color_tx_, 0, input_size); + for (int i = 0; i < 2; i++) { + data_.gather_uv_fac[i] = 1.0f / (float)input_size[i]; + data_.texel_size[i] = 1.0f / res[i]; + } + + /* Reuse textures from the setup pass. */ + /* NOTE: We could use the texture pool do that for us but it does not track + * usage and it might backfire (it does in practice). */ + /* Since it is only used for scatter, and foreground is processed before background, we can + * reuse the occlusion_tx for both field. */ + occlusion_tx_ = setup_coc_tx_; + + { + DRW_PASS_CREATE(gather_holefill_ps_, DRW_STATE_WRITE_COLOR); + GPUShader *sh = inst_.shaders.static_shader_get(DOF_GATHER_HOLEFILL); + DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_holefill_ps_); + DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, with_filter); + DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter); + DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter); + DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_); + DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_); + DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get()); + DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get()); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + /* Reuse textures from the setup pass. */ + /* NOTE: We could use the texture pool do that for us but it does not track + * usage and it might backfire (it does in practice). */ + color_holefill_tx_ = setup_color_tx_; + weight_holefill_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner); + + gather_holefill_fb_.ensure(GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(color_holefill_tx_), + GPU_ATTACHMENT_TEXTURE(weight_holefill_tx_)); + } + { + DRW_PASS_CREATE(gather_fg_ps_, DRW_STATE_WRITE_COLOR); + GPUShader *sh = inst_.shaders.static_shader_get( + bokeh_gather_lut_tx_ ? DOF_GATHER_FOREGROUND_LUT : DOF_GATHER_FOREGROUND); + DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_fg_ps_); + DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, with_filter); + DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter); + DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter); + DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_); + DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_); + if (bokeh_gather_lut_tx_) { + DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_gather_lut_tx_); + } + DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get()); + DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get()); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + color_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner); + weight_fg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner); + } + { + DRW_PASS_CREATE(gather_bg_ps_, DRW_STATE_WRITE_COLOR); + GPUShader *sh = inst_.shaders.static_shader_get( + bokeh_gather_lut_tx_ ? DOF_GATHER_BACKGROUND_LUT : DOF_GATHER_BACKGROUND); + DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_bg_ps_); + DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, with_filter); + DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, no_filter); + DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter); + DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_); + DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_); + if (bokeh_gather_lut_tx_) { + DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_gather_lut_tx_); + } + DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get()); + DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get()); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + color_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner); + weight_bg_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_R16F, owner); + } + + /* NOTE: First target is holefill texture so we can use the median filter on it and save some + * texture memory. Both field use the same framebuffer. */ + gather_fb_.ensure(GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(color_holefill_tx_), + GPU_ATTACHMENT_TEXTURE(weight_holefill_tx_), + GPU_ATTACHMENT_TEXTURE(occlusion_tx_)); + { + /** + * Filter an input buffer using a median filter to reduce noise. + * NOTE: We use the holefill texture as our input to reduce memory usage. + * Thus, the holefill pass cannot be filtered. + **/ + DRW_PASS_CREATE(filter_ps_, DRW_STATE_WRITE_COLOR); + GPUShader *sh = inst_.shaders.static_shader_get(DOF_FILTER); + DRWShadingGroup *grp = DRW_shgroup_create(sh, filter_ps_); + DRW_shgroup_uniform_texture_ex(grp, "color_tx", color_holefill_tx_, no_filter); + DRW_shgroup_uniform_texture_ex(grp, "weight_tx", weight_holefill_tx_, no_filter); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + filter_fg_fb_.ensure(GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(color_fg_tx_), + GPU_ATTACHMENT_TEXTURE(weight_fg_tx_)); + + filter_bg_fb_.ensure(GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(color_bg_tx_), + GPU_ATTACHMENT_TEXTURE(weight_bg_tx_)); + } + + /** + * Do the Scatter convolution. A sprite is emitted for every 4 pixels but is only expanded + * if the pixels are bright enough to be scattered. + **/ + data_.scatter_sprite_per_row = input_size[0] / 2; + int sprite_count = data_.scatter_sprite_per_row * (input_size[1] / 2); + { + DRW_PASS_CREATE(scatter_fg_ps_, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL); + GPUShader *sh = inst_.shaders.static_shader_get( + bokeh_gather_lut_tx_ ? DOF_SCATTER_FOREGROUND_LUT : DOF_SCATTER_FOREGROUND); + DRWShadingGroup *grp = DRW_shgroup_create(sh, scatter_fg_ps_); + DRW_shgroup_uniform_texture_ex(grp, "color_tx", scatter_src_tx_, no_filter); + DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter); + DRW_shgroup_uniform_texture(grp, "occlusion_tx", occlusion_tx_); + if (bokeh_scatter_lut_tx_) { + DRW_shgroup_uniform_texture(grp, "bokehLut", bokeh_scatter_lut_tx_); + } + DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get()); + DRW_shgroup_call_procedural_triangles(grp, NULL, sprite_count); + + scatter_fg_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(color_fg_tx_)); + } + { + DRW_PASS_CREATE(scatter_bg_ps_, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL); + GPUShader *sh = inst_.shaders.static_shader_get( + bokeh_gather_lut_tx_ ? DOF_SCATTER_BACKGROUND_LUT : DOF_SCATTER_BACKGROUND); + DRWShadingGroup *grp = DRW_shgroup_create(sh, scatter_bg_ps_); + DRW_shgroup_uniform_texture_ex(grp, "color_tx", scatter_src_tx_, no_filter); + DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, no_filter); + DRW_shgroup_uniform_texture(grp, "occlusion_tx", occlusion_tx_); + if (bokeh_scatter_lut_tx_) { + DRW_shgroup_uniform_texture(grp, "bokehLut", bokeh_scatter_lut_tx_); + } + DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get()); + DRW_shgroup_call_procedural_triangles(grp, NULL, sprite_count); + + scatter_bg_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(color_bg_tx_)); + } +} + +void DepthOfField::convolve_pass_render(void) +{ + DRW_stats_group_start("Foreground convolution"); + GPU_framebuffer_bind(gather_fb_); + DRW_draw_pass(gather_fg_ps_); + + GPU_framebuffer_bind(filter_fg_fb_); + DRW_draw_pass(filter_ps_); + + GPU_framebuffer_bind(scatter_fg_fb_); + DRW_draw_pass(scatter_fg_ps_); + DRW_stats_group_end(); + + DRW_stats_group_start("Background convolution"); + GPU_framebuffer_bind(gather_fb_); + DRW_draw_pass(gather_bg_ps_); + + GPU_framebuffer_bind(filter_bg_fb_); + DRW_draw_pass(filter_ps_); + + GPU_framebuffer_bind(scatter_bg_fb_); + DRW_draw_pass(scatter_bg_ps_); + DRW_stats_group_end(); + + DRW_stats_group_start("Background convolution"); + /* Hole-fill convolution. */ + GPU_framebuffer_bind(gather_holefill_fb_); + DRW_draw_pass(gather_holefill_ps_); + /* NOTE: We do not filter the hole-fill pass as we use it as out filter input + * buffer. Also effect is likely to not be noticeable. */ + DRW_stats_group_end(); +} + +/** + * Recombine the result of the foreground and background processing. + * Also perform a slight out of focus gather to improve geometric continuity. + **/ +void DepthOfField::resolve_pass_sync(void) +{ + eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT; + eGPUSamplerState with_filter = GPU_SAMPLER_FILTER; + eShaderType sh_type = (bokeh_resolve_lut_tx_) ? + (do_hq_slight_focus_ ? DOF_RESOLVE_LUT_HQ : DOF_RESOLVE_LUT) : + (do_hq_slight_focus_ ? DOF_RESOLVE_HQ : DOF_RESOLVE); + + DRW_PASS_CREATE(resolve_ps_, DRW_STATE_WRITE_COLOR); + GPUShader *sh = inst_.shaders.static_shader_get(sh_type); + DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_); + DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &input_depth_tx_, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &input_color_tx_, no_filter); + DRW_shgroup_uniform_texture_ex(grp, "color_bg_tx", color_bg_tx_, with_filter); + DRW_shgroup_uniform_texture_ex(grp, "color_fg_tx", color_fg_tx_, with_filter); + DRW_shgroup_uniform_texture_ex(grp, "color_holefill_tx", color_holefill_tx_, with_filter); + DRW_shgroup_uniform_texture(grp, "tiles_bg_tx", tiles_dilated_bg_tx_); + DRW_shgroup_uniform_texture(grp, "tiles_fg_tx", tiles_dilated_fg_tx_); + DRW_shgroup_uniform_texture_ex(grp, "weight_bg_tx", weight_bg_tx_, with_filter); + DRW_shgroup_uniform_texture_ex(grp, "weight_fg_tx", weight_fg_tx_, with_filter); + DRW_shgroup_uniform_texture_ex(grp, "weight_holefill_tx", weight_holefill_tx_, with_filter); + DRW_shgroup_uniform_block(grp, "dof_block", data_.ubo_get()); + DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get()); + if (bokeh_resolve_lut_tx_) { + DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_resolve_lut_tx_); + } + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); +} + +void DepthOfField::resolve_pass_render(GPUTexture *output_tx) +{ + resolve_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(output_tx)); + + GPU_framebuffer_bind(resolve_fb_); + DRW_draw_pass(resolve_ps_); +} + +/** \} */ + +} // namespace blender::eevee
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/eevee_depth_of_field.hh b/source/blender/draw/engines/eevee/eevee_depth_of_field.hh new file mode 100644 index 00000000000..657a22b990a --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_depth_of_field.hh @@ -0,0 +1,176 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Depth of field post process effect. + * + * There are 2 methods to achieve this effect. + * - The first uses projection matrix offsetting and sample accumulation to give + * reference quality depth of field. But this needs many samples to hide the + * under-sampling. + * - The second one is a post-processing based one. It follows the + * implementation described in the presentation "Life of a Bokeh - Siggraph + * 2018" from Guillaume Abadie. There are some difference with our actual + * implementation that prioritize quality. + */ + +#pragma once + +#include "eevee_shader_shared.hh" + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name Depth of field + * \{ */ + +class DepthOfField { + private: + class Instance &inst_; + + DepthOfFieldDataBuf data_; + + /** Textures from pool. Not owned. */ + GPUTexture *bokeh_gather_lut_tx_ = nullptr; + GPUTexture *bokeh_resolve_lut_tx_ = nullptr; + GPUTexture *bokeh_scatter_lut_tx_ = nullptr; + GPUTexture *color_bg_tx_ = nullptr; + GPUTexture *color_fg_tx_ = nullptr; + GPUTexture *color_holefill_tx_ = nullptr; + GPUTexture *occlusion_tx_ = nullptr; + GPUTexture *reduce_downsample_tx_ = nullptr; + GPUTexture *scatter_src_tx_ = nullptr; + GPUTexture *setup_coc_tx_ = nullptr; + GPUTexture *setup_color_tx_ = nullptr; + GPUTexture *tiles_bg_tx_ = nullptr; + GPUTexture *tiles_fg_tx_ = nullptr; + GPUTexture *tiles_dilated_bg_tx_ = nullptr; + GPUTexture *tiles_dilated_fg_tx_ = nullptr; + GPUTexture *weight_bg_tx_ = nullptr; + GPUTexture *weight_fg_tx_ = nullptr; + GPUTexture *weight_holefill_tx_ = nullptr; + /** Allocated textures. Owned. */ + eevee::Texture reduced_coc_tx_ = Texture("dof_reduced_coc"); + eevee::Texture reduced_color_tx_ = Texture("dof_reduced_color"); + /** Input texture. Not owned. */ + GPUTexture *input_color_tx_; + GPUTexture *input_depth_tx_; + /** Passes. Not owned. */ + DRWPass *bokeh_lut_ps_ = nullptr; + DRWPass *gather_bg_ps_ = nullptr; + DRWPass *gather_fg_ps_ = nullptr; + DRWPass *filter_ps_ = nullptr; + DRWPass *gather_holefill_ps_ = nullptr; + DRWPass *reduce_copy_ps_ = nullptr; + DRWPass *reduce_downsample_ps_ = nullptr; + DRWPass *reduce_recursive_ps_ = nullptr; + DRWPass *resolve_ps_ = nullptr; + DRWPass *scatter_bg_ps_ = nullptr; + DRWPass *scatter_fg_ps_ = nullptr; + DRWPass *setup_ps_ = nullptr; + DRWPass *tiles_dilate_minabs_ps_ = nullptr; + DRWPass *tiles_dilate_minmax_ps_ = nullptr; + DRWPass *tiles_flatten_ps_ = nullptr; + /** Framebuffers. Owned. */ + eevee::Framebuffer bokeh_lut_fb_ = Framebuffer("bokeh_lut_fb_"); + eevee::Framebuffer filter_bg_fb_ = Framebuffer("filter_bg_fb_"); + eevee::Framebuffer filter_fg_fb_ = Framebuffer("filter_fg_fb_"); + eevee::Framebuffer gather_fb_ = Framebuffer("gather_fb_"); + eevee::Framebuffer gather_filter_bg_fb_ = Framebuffer("gather_filter_bg_fb_"); + eevee::Framebuffer gather_holefill_fb_ = Framebuffer("gather_holefill_fb_"); + eevee::Framebuffer reduce_copy_fb_ = Framebuffer("reduce_copy_fb_"); + eevee::Framebuffer reduce_downsample_fb_ = Framebuffer("reduce_downsample_fb_"); + eevee::Framebuffer reduce_fb_ = Framebuffer("reduce_fb_"); + eevee::Framebuffer resolve_fb_ = Framebuffer("resolve_fb_"); + eevee::Framebuffer scatter_bg_fb_ = Framebuffer("scatter_bg_fb_"); + eevee::Framebuffer scatter_fg_fb_ = Framebuffer("scatter_fg_fb_"); + eevee::Framebuffer setup_fb_ = Framebuffer("setup_fb_"); + eevee::Framebuffer tiles_dilate_fb_ = Framebuffer("tiles_dilate_fb_"); + eevee::Framebuffer tiles_flatten_fb_ = Framebuffer("tiles_flatten_fb_"); + + /** Scene settings that are immutable. */ + float user_overblur_; + float fx_max_coc_; + /** Use Hiqh Quality (expensive) in-focus gather pass. */ + bool do_hq_slight_focus_; + /** Use jittered depth of field where we randomize camera location. */ + bool do_jitter_; + + /** Circle of Confusion radius for FX DoF passes. Is in view X direction in [0..1] range. */ + float fx_radius_; + /** Circle of Confusion radius for jittered DoF. Is in view X direction in [0..1] range. */ + float jitter_radius_; + /** Focus distance in view space. */ + float focus_distance_; + /** Extent of the input buffer. */ + ivec2 extent_; + + /** Tile dilation uniforms. */ + int tiles_dilate_slight_focus_; + int tiles_dilate_ring_count_; + int tiles_dilate_ring_width_multiplier_; + + /** Reduce pass info. */ + int reduce_steps_; + + /** Static string pointer. Used as debug name and as UUID for texture pool. */ + StringRefNull view_name_; + + public: + DepthOfField(Instance &inst, StringRefNull view_name) : inst_(inst), view_name_(view_name){}; + ~DepthOfField(){}; + + void init(); + + void sync(const mat4 winmat, ivec2 input_extent); + + /** Apply Depth Of Field jittering to the view and projection matrices.. */ + void jitter_apply(mat4 winmat, mat4 viewmat); + + /** Will swap input and output texture if rendering happens. The actual output of this function + * is in intput_tx. */ + void render(GPUTexture *depth_tx, GPUTexture **input_tx, GPUTexture **output_tx); + + private: + void bokeh_lut_pass_sync(void); + void bokeh_lut_pass_render(void); + + void setup_pass_sync(void); + void setup_pass_render(void); + + void tiles_prepare_pass_sync(void); + void tiles_prepare_pass_render(void); + + static void reduce_recusive(void *thunk, int level); + void reduce_pass_sync(void); + void reduce_pass_render(void); + + void convolve_pass_sync(void); + void convolve_pass_render(void); + + void resolve_pass_sync(void); + void resolve_pass_render(GPUTexture *output_tx); +}; + +/** \} */ + +} // namespace blender::eevee
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c deleted file mode 100644 index 3d32b4acd82..00000000000 --- a/source/blender/draw/engines/eevee/eevee_effects.c +++ /dev/null @@ -1,524 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - * - * Gather all screen space effects technique such as Bloom, Motion Blur, DoF, SSAO, SSR, ... - */ - -#include "DRW_render.h" - -#include "BKE_global.h" /* for G.debug_value */ - -#include "GPU_capabilities.h" -#include "GPU_platform.h" -#include "GPU_state.h" -#include "GPU_texture.h" -#include "eevee_private.h" - -static struct { - /* These are just references, not actually allocated */ - struct GPUTexture *depth_src; - struct GPUTexture *color_src; - - int depth_src_layer; - /* Size can be vec3. But we only use 2 components in the shader. */ - float texel_size[2]; -} e_data = {NULL}; /* Engine data */ - -#define SETUP_BUFFER(tex, fb, fb_color) \ - { \ - eGPUTextureFormat format = (DRW_state_is_scene_render()) ? GPU_RGBA32F : GPU_RGBA16F; \ - DRW_texture_ensure_fullscreen_2d(&tex, format, DRW_TEX_FILTER); \ - GPU_framebuffer_ensure_config(&fb, \ - { \ - GPU_ATTACHMENT_TEXTURE(dtxl->depth), \ - GPU_ATTACHMENT_TEXTURE(tex), \ - }); \ - GPU_framebuffer_ensure_config(&fb_color, \ - { \ - GPU_ATTACHMENT_NONE, \ - GPU_ATTACHMENT_TEXTURE(tex), \ - }); \ - } \ - ((void)0) - -#define CLEANUP_BUFFER(tex, fb, fb_color) \ - { \ - /* Cleanup to release memory */ \ - DRW_TEXTURE_FREE_SAFE(tex); \ - GPU_FRAMEBUFFER_FREE_SAFE(fb); \ - GPU_FRAMEBUFFER_FREE_SAFE(fb_color); \ - } \ - ((void)0) - -void EEVEE_effects_init(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - Object *camera, - const bool minimal) -{ - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_EffectsInfo *effects; - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - - const float *viewport_size = DRW_viewport_size_get(); - const int size_fs[2] = {(int)viewport_size[0], (int)viewport_size[1]}; - - if (!stl->effects) { - stl->effects = MEM_callocN(sizeof(EEVEE_EffectsInfo), "EEVEE_EffectsInfo"); - stl->effects->taa_render_sample = 1; - } - - /* WORKAROUND: EEVEE_lookdev_init can reset TAA and needs a stl->effect. - * So putting this before EEVEE_temporal_sampling_init for now. */ - EEVEE_lookdev_init(vedata); - - effects = stl->effects; - - int div = 1 << MAX_SCREEN_BUFFERS_LOD_LEVEL; - effects->hiz_size[0] = divide_ceil_u(size_fs[0], div) * div; - effects->hiz_size[1] = divide_ceil_u(size_fs[1], div) * div; - - effects->enabled_effects = 0; - effects->enabled_effects |= (G.debug_value == 9) ? EFFECT_VELOCITY_BUFFER : 0; - effects->enabled_effects |= EEVEE_motion_blur_init(sldata, vedata); - effects->enabled_effects |= EEVEE_bloom_init(sldata, vedata); - effects->enabled_effects |= EEVEE_depth_of_field_init(sldata, vedata, camera); - effects->enabled_effects |= EEVEE_temporal_sampling_init(sldata, vedata); - effects->enabled_effects |= EEVEE_occlusion_init(sldata, vedata); - effects->enabled_effects |= EEVEE_screen_raytrace_init(sldata, vedata); - - /* Update matrices here because EEVEE_screen_raytrace_init can have reset the - * taa_current_sample. (See T66811) */ - EEVEE_temporal_sampling_update_matrices(vedata); - - EEVEE_volumes_init(sldata, vedata); - EEVEE_subsurface_init(sldata, vedata); - - /* Force normal buffer creation. */ - if (!minimal && (stl->g_data->render_passes & EEVEE_RENDER_PASS_NORMAL) != 0) { - effects->enabled_effects |= EFFECT_NORMAL_BUFFER; - } - - /** - * MinMax Pyramid - */ - - if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) { - /* Intel gpu seems to have problem rendering to only depth hiz_format */ - DRW_texture_ensure_2d(&txl->maxzbuffer, UNPACK2(effects->hiz_size), GPU_R32F, DRW_TEX_MIPMAP); - GPU_framebuffer_ensure_config(&fbl->maxzbuffer_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(txl->maxzbuffer), - }); - } - else { - DRW_texture_ensure_2d( - &txl->maxzbuffer, UNPACK2(effects->hiz_size), GPU_DEPTH_COMPONENT24, DRW_TEX_MIPMAP); - GPU_framebuffer_ensure_config(&fbl->maxzbuffer_fb, - { - GPU_ATTACHMENT_TEXTURE(txl->maxzbuffer), - GPU_ATTACHMENT_NONE, - }); - } - - if (fbl->downsample_fb == NULL) { - fbl->downsample_fb = GPU_framebuffer_create("downsample_fb"); - } - - /** - * Compute hiZ texel alignment. - */ - common_data->hiz_uv_scale[0] = viewport_size[0] / effects->hiz_size[0]; - common_data->hiz_uv_scale[1] = viewport_size[1] / effects->hiz_size[1]; - - /* Compute pixel size. Size is multiplied by 2 because it is applied in NDC [-1..1] range. */ - sldata->common_data.ssr_pixelsize[0] = 2.0f / size_fs[0]; - sldata->common_data.ssr_pixelsize[1] = 2.0f / size_fs[1]; - - /** - * Color buffer with correct down-sampling alignment. - * Used for SSReflections & SSRefractions. - */ - if ((effects->enabled_effects & EFFECT_RADIANCE_BUFFER) != 0) { - DRW_texture_ensure_2d(&txl->filtered_radiance, - UNPACK2(effects->hiz_size), - GPU_R11F_G11F_B10F, - DRW_TEX_FILTER | DRW_TEX_MIPMAP); - - GPU_framebuffer_ensure_config(&fbl->radiance_filtered_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(txl->filtered_radiance), - }); - } - else { - DRW_TEXTURE_FREE_SAFE(txl->filtered_radiance); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->radiance_filtered_fb); - } - - /** - * Normal buffer for deferred passes. - */ - if ((effects->enabled_effects & EFFECT_NORMAL_BUFFER) != 0) { - effects->ssr_normal_input = DRW_texture_pool_query_2d( - size_fs[0], size_fs[1], GPU_RG16, &draw_engine_eevee_type); - - GPU_framebuffer_texture_attach(fbl->main_fb, effects->ssr_normal_input, 1, 0); - } - else { - effects->ssr_normal_input = NULL; - } - - /** - * Motion vector buffer for correct TAA / motion blur. - */ - if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) { - effects->velocity_tx = DRW_texture_pool_query_2d( - size_fs[0], size_fs[1], GPU_RGBA16, &draw_engine_eevee_type); - - GPU_framebuffer_ensure_config(&fbl->velocity_fb, - { - GPU_ATTACHMENT_TEXTURE(dtxl->depth), - GPU_ATTACHMENT_TEXTURE(effects->velocity_tx), - }); - - GPU_framebuffer_ensure_config( - &fbl->velocity_resolve_fb, - {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->velocity_tx)}); - } - else { - effects->velocity_tx = NULL; - } - - /** - * Setup depth double buffer. - */ - if ((effects->enabled_effects & EFFECT_DEPTH_DOUBLE_BUFFER) != 0) { - DRW_texture_ensure_fullscreen_2d(&txl->depth_double_buffer, GPU_DEPTH24_STENCIL8, 0); - - GPU_framebuffer_ensure_config(&fbl->double_buffer_depth_fb, - {GPU_ATTACHMENT_TEXTURE(txl->depth_double_buffer)}); - } - else { - /* Cleanup to release memory */ - DRW_TEXTURE_FREE_SAFE(txl->depth_double_buffer); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->double_buffer_depth_fb); - } - - if ((effects->enabled_effects & (EFFECT_TAA | EFFECT_TAA_REPROJECT)) != 0) { - SETUP_BUFFER(txl->taa_history, fbl->taa_history_fb, fbl->taa_history_color_fb); - } - else { - CLEANUP_BUFFER(txl->taa_history, fbl->taa_history_fb, fbl->taa_history_color_fb); - } -} - -void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - DRWState downsample_write = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS; - DRWShadingGroup *grp; - - /* Intel gpu seems to have problem rendering to only depth format. - * Use color texture instead. */ - if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) { - downsample_write = DRW_STATE_WRITE_COLOR; - } - - struct GPUBatch *quad = DRW_cache_fullscreen_quad_get(); - - if (effects->enabled_effects & EFFECT_RADIANCE_BUFFER) { - DRW_PASS_CREATE(psl->color_copy_ps, DRW_STATE_WRITE_COLOR); - grp = DRW_shgroup_create(EEVEE_shaders_effect_color_copy_sh_get(), psl->color_copy_ps); - DRW_shgroup_uniform_texture_ref_ex(grp, "source", &e_data.color_src, GPU_SAMPLER_DEFAULT); - DRW_shgroup_uniform_float(grp, "fireflyFactor", &sldata->common_data.ssr_firefly_fac, 1); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - DRW_PASS_CREATE(psl->color_downsample_ps, DRW_STATE_WRITE_COLOR); - grp = DRW_shgroup_create(EEVEE_shaders_effect_downsample_sh_get(), psl->color_downsample_ps); - DRW_shgroup_uniform_texture_ex(grp, "source", txl->filtered_radiance, GPU_SAMPLER_FILTER); - DRW_shgroup_uniform_vec2(grp, "texelSize", e_data.texel_size, 1); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - } - - { - DRW_PASS_CREATE(psl->color_downsample_cube_ps, DRW_STATE_WRITE_COLOR); - grp = DRW_shgroup_create(EEVEE_shaders_effect_downsample_cube_sh_get(), - psl->color_downsample_cube_ps); - DRW_shgroup_uniform_texture_ref(grp, "source", &e_data.color_src); - DRW_shgroup_uniform_float(grp, "texelSize", e_data.texel_size, 1); - DRW_shgroup_uniform_int_copy(grp, "Layer", 0); - DRW_shgroup_call_instances(grp, NULL, quad, 6); - } - - { - /* Perform min/max down-sample. */ - DRW_PASS_CREATE(psl->maxz_downlevel_ps, downsample_write); - grp = DRW_shgroup_create(EEVEE_shaders_effect_maxz_downlevel_sh_get(), psl->maxz_downlevel_ps); - DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &txl->maxzbuffer, GPU_SAMPLER_DEFAULT); - DRW_shgroup_uniform_vec2(grp, "texelSize", e_data.texel_size, 1); - DRW_shgroup_call(grp, quad, NULL); - - /* Copy depth buffer to top level of HiZ */ - DRW_PASS_CREATE(psl->maxz_copydepth_ps, downsample_write); - grp = DRW_shgroup_create(EEVEE_shaders_effect_maxz_copydepth_sh_get(), psl->maxz_copydepth_ps); - DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &e_data.depth_src, GPU_SAMPLER_DEFAULT); - DRW_shgroup_call(grp, quad, NULL); - - DRW_PASS_CREATE(psl->maxz_copydepth_layer_ps, downsample_write); - grp = DRW_shgroup_create(EEVEE_shaders_effect_maxz_copydepth_layer_sh_get(), - psl->maxz_copydepth_layer_ps); - DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &e_data.depth_src, GPU_SAMPLER_DEFAULT); - DRW_shgroup_uniform_int(grp, "depthLayer", &e_data.depth_src_layer, 1); - DRW_shgroup_call(grp, quad, NULL); - } - - if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) { - EEVEE_MotionBlurData *mb_data = &effects->motion_blur; - - /* This pass compute camera motions to the non moving objects. */ - DRW_PASS_CREATE(psl->velocity_resolve, DRW_STATE_WRITE_COLOR); - grp = DRW_shgroup_create(EEVEE_shaders_velocity_resolve_sh_get(), psl->velocity_resolve); - DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - - DRW_shgroup_uniform_mat4(grp, "prevViewProjMatrix", mb_data->camera[MB_PREV].persmat); - DRW_shgroup_uniform_mat4(grp, "currViewProjMatrixInv", mb_data->camera[MB_CURR].persinv); - DRW_shgroup_uniform_mat4(grp, "nextViewProjMatrix", mb_data->camera[MB_NEXT].persmat); - DRW_shgroup_call(grp, quad, NULL); - } -} - -void EEVEE_effects_draw_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_EffectsInfo *effects = vedata->stl->effects; - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - /** - * Setup double buffer so we can access last frame as it was before post processes. - */ - if ((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0) { - SETUP_BUFFER(txl->color_double_buffer, fbl->double_buffer_fb, fbl->double_buffer_color_fb); - } - else { - CLEANUP_BUFFER(txl->color_double_buffer, fbl->double_buffer_fb, fbl->double_buffer_color_fb); - } - - /** - * Ping Pong buffer - */ - if ((effects->enabled_effects & EFFECT_POST_BUFFER) != 0) { - SETUP_BUFFER(txl->color_post, fbl->effect_fb, fbl->effect_color_fb); - } - else { - CLEANUP_BUFFER(txl->color_post, fbl->effect_fb, fbl->effect_color_fb); - } -} - -#if 0 /* Not required for now */ -static void min_downsample_cb(void *vedata, int UNUSED(level)) -{ - EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; - DRW_draw_pass(psl->minz_downlevel_ps); -} -#endif - -static void max_downsample_cb(void *vedata, int level) -{ - EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; - EEVEE_TextureList *txl = ((EEVEE_Data *)vedata)->txl; - int texture_size[3]; - GPU_texture_get_mipmap_size(txl->maxzbuffer, level - 1, texture_size); - e_data.texel_size[0] = 1.0f / texture_size[0]; - e_data.texel_size[1] = 1.0f / texture_size[1]; - DRW_draw_pass(psl->maxz_downlevel_ps); -} - -static void simple_downsample_cube_cb(void *vedata, int level) -{ - EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; - e_data.texel_size[0] = (float)(1 << level) / (float)GPU_texture_width(e_data.color_src); - e_data.texel_size[1] = e_data.texel_size[0]; - DRW_draw_pass(psl->color_downsample_cube_ps); -} - -void EEVEE_create_minmax_buffer(EEVEE_Data *vedata, GPUTexture *depth_src, int layer) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_FramebufferList *fbl = vedata->fbl; - - e_data.depth_src = depth_src; - e_data.depth_src_layer = layer; - - DRW_stats_group_start("Max buffer"); - /* Copy depth buffer to max texture top level */ - GPU_framebuffer_bind(fbl->maxzbuffer_fb); - if (layer >= 0) { - DRW_draw_pass(psl->maxz_copydepth_layer_ps); - } - else { - DRW_draw_pass(psl->maxz_copydepth_ps); - } - /* Create lower levels */ - GPU_framebuffer_recursive_downsample( - fbl->maxzbuffer_fb, MAX_SCREEN_BUFFERS_LOD_LEVEL, &max_downsample_cb, vedata); - DRW_stats_group_end(); - - /* Restore */ - GPU_framebuffer_bind(fbl->main_fb); - - if (GPU_mip_render_workaround() || - GPU_type_matches(GPU_DEVICE_INTEL_UHD, GPU_OS_WIN, GPU_DRIVER_ANY)) { - /* Fix dot corruption on intel HD5XX/HD6XX series. */ - GPU_flush(); - } -} - -static void downsample_radiance_cb(void *vedata, int level) -{ - EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; - EEVEE_TextureList *txl = ((EEVEE_Data *)vedata)->txl; - int texture_size[3]; - GPU_texture_get_mipmap_size(txl->filtered_radiance, level - 1, texture_size); - e_data.texel_size[0] = 1.0f / texture_size[0]; - e_data.texel_size[1] = 1.0f / texture_size[1]; - DRW_draw_pass(psl->color_downsample_ps); -} - -void EEVEE_effects_downsample_radiance_buffer(EEVEE_Data *vedata, GPUTexture *texture_src) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_FramebufferList *fbl = vedata->fbl; - - e_data.color_src = texture_src; - DRW_stats_group_start("Downsample Radiance"); - - GPU_framebuffer_bind(fbl->radiance_filtered_fb); - DRW_draw_pass(psl->color_copy_ps); - - GPU_framebuffer_recursive_downsample( - fbl->radiance_filtered_fb, MAX_SCREEN_BUFFERS_LOD_LEVEL, &downsample_radiance_cb, vedata); - DRW_stats_group_end(); -} - -void EEVEE_downsample_cube_buffer(EEVEE_Data *vedata, GPUTexture *texture_src, int level) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - e_data.color_src = texture_src; - - /* Create lower levels */ - DRW_stats_group_start("Downsample Cube buffer"); - GPU_framebuffer_texture_attach(fbl->downsample_fb, texture_src, 0, 0); - GPU_framebuffer_recursive_downsample( - fbl->downsample_fb, level, &simple_downsample_cube_cb, vedata); - GPU_framebuffer_texture_detach(fbl->downsample_fb, texture_src); - DRW_stats_group_end(); -} - -static void EEVEE_velocity_resolve(EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - - if ((effects->enabled_effects & EFFECT_VELOCITY_BUFFER) != 0) { - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - e_data.depth_src = dtxl->depth; - - GPU_framebuffer_bind(fbl->velocity_resolve_fb); - DRW_draw_pass(psl->velocity_resolve); - - if (psl->velocity_object) { - GPU_framebuffer_bind(fbl->velocity_fb); - DRW_draw_pass(psl->velocity_object); - } - } -} - -void EEVEE_draw_effects(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_TextureList *txl = vedata->txl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - - /* only once per frame after the first post process */ - effects->swap_double_buffer = ((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0); - - /* Init pointers */ - effects->source_buffer = txl->color; /* latest updated texture */ - effects->target_buffer = fbl->effect_color_fb; /* next target to render to */ - - /* Post process stack (order matters) */ - EEVEE_velocity_resolve(vedata); - EEVEE_motion_blur_draw(vedata); - EEVEE_depth_of_field_draw(vedata); - - /* NOTE: Lookdev drawing happens before TAA but after - * motion blur and DOF to avoid distortions. - * Velocity resolve use a hack to exclude lookdev - * spheres from creating shimmering re-projection vectors. */ - EEVEE_lookdev_draw(vedata); - - EEVEE_temporal_sampling_draw(vedata); - EEVEE_bloom_draw(vedata); - - /* Post effect render passes are done here just after the drawing of the effects and just before - * the swapping of the buffers. */ - EEVEE_renderpasses_output_accumulate(sldata, vedata, true); - - /* Save the final texture and frame-buffer for final transformation or read. */ - effects->final_tx = effects->source_buffer; - effects->final_fb = (effects->target_buffer != fbl->main_color_fb) ? fbl->main_fb : - fbl->effect_fb; - if ((effects->enabled_effects & EFFECT_TAA) && (effects->source_buffer == txl->taa_history)) { - effects->final_fb = fbl->taa_history_fb; - } - - /* If no post processes is enabled, buffers are still not swapped, do it now. */ - SWAP_DOUBLE_BUFFERS(); - - if (!stl->g_data->valid_double_buffer && - ((effects->enabled_effects & EFFECT_DOUBLE_BUFFER) != 0) && - (DRW_state_is_image_render() == false)) { - /* If history buffer is not valid request another frame. - * This fix black reflections on area resize. */ - DRW_viewport_request_redraw(); - } - - /* Record perspective matrix for the next frame. */ - DRW_view_persmat_get(effects->taa_view, effects->prev_persmat, false); - - /* Update double buffer status if render mode. */ - if (DRW_state_is_image_render()) { - stl->g_data->valid_double_buffer = (txl->color_double_buffer != NULL); - stl->g_data->valid_taa_history = (txl->taa_history != NULL); - } -} diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index fc9b8b0cde4..a744d1f8cd5 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -22,602 +22,123 @@ #include "DRW_render.h" -#include "draw_color_management.h" /* TODO: remove dependency. */ - -#include "BLI_rand.h" - -#include "BKE_object.h" - -#include "DEG_depsgraph_query.h" - -#include "DNA_world_types.h" - -#include "IMB_imbuf.h" +#include "DRW_engine.h" +#include "GPU_framebuffer.h" +#include "eevee_engine.h" #include "eevee_private.h" -#include "eevee_engine.h" /* own include */ - -#define EEVEE_ENGINE "BLENDER_EEVEE" - -/* *********** FUNCTIONS *********** */ - -static void eevee_engine_init(void *ved) -{ - EEVEE_Data *vedata = (EEVEE_Data *)ved; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; - EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - RegionView3D *rv3d = draw_ctx->rv3d; - Object *camera = (rv3d->persp == RV3D_CAMOB) ? v3d->camera : NULL; - - if (!stl->g_data) { - /* Alloc transient pointers */ - stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__); - } - stl->g_data->use_color_render_settings = USE_SCENE_LIGHT(v3d) || - !LOOK_DEV_STUDIO_LIGHT_ENABLED(v3d); - stl->g_data->background_alpha = DRW_state_draw_background() ? 1.0f : 0.0f; - stl->g_data->valid_double_buffer = (txl->color_double_buffer != NULL); - stl->g_data->valid_taa_history = (txl->taa_history != NULL); - stl->g_data->queued_shaders_count = 0; - stl->g_data->render_timesteps = 1; - - /* Main Buffer */ - DRW_texture_ensure_fullscreen_2d(&txl->color, GPU_RGBA16F, DRW_TEX_FILTER); - - GPU_framebuffer_ensure_config(&fbl->main_fb, - {GPU_ATTACHMENT_TEXTURE(dtxl->depth), - GPU_ATTACHMENT_TEXTURE(txl->color), - GPU_ATTACHMENT_LEAVE, - GPU_ATTACHMENT_LEAVE, - GPU_ATTACHMENT_LEAVE, - GPU_ATTACHMENT_LEAVE}); - - GPU_framebuffer_ensure_config(&fbl->main_color_fb, - {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->color)}); - - /* `EEVEE_renderpasses_init` will set the active render passes used by `EEVEE_effects_init`. - * `EEVEE_effects_init` needs to go second for TAA. */ - EEVEE_renderpasses_init(vedata); - EEVEE_effects_init(sldata, vedata, camera, false); - EEVEE_materials_init(sldata, vedata, stl, fbl); - EEVEE_shadows_init(sldata); - EEVEE_lightprobes_init(sldata, vedata); -} - -static void eevee_cache_init(void *vedata) -{ - EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); - - EEVEE_bloom_cache_init(sldata, vedata); - EEVEE_depth_of_field_cache_init(sldata, vedata); - EEVEE_effects_cache_init(sldata, vedata); - EEVEE_lightprobes_cache_init(sldata, vedata); - EEVEE_lights_cache_init(sldata, vedata); - EEVEE_materials_cache_init(sldata, vedata); - EEVEE_motion_blur_cache_init(sldata, vedata); - EEVEE_occlusion_cache_init(sldata, vedata); - EEVEE_screen_raytrace_cache_init(sldata, vedata); - EEVEE_subsurface_cache_init(sldata, vedata); - EEVEE_temporal_sampling_cache_init(sldata, vedata); - EEVEE_volumes_cache_init(sldata, vedata); -} - -void EEVEE_cache_populate(void *vedata, Object *ob) -{ - EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); - - const DRWContextState *draw_ctx = DRW_context_state_get(); - const int ob_visibility = DRW_object_visibility_in_active_context(ob); - bool cast_shadow = false; - - if (ob_visibility & OB_VISIBLE_PARTICLES) { - EEVEE_particle_hair_cache_populate(vedata, sldata, ob, &cast_shadow); - } - - if (DRW_object_is_renderable(ob) && (ob_visibility & OB_VISIBLE_SELF)) { - if (ELEM(ob->type, OB_MESH, OB_SURF, OB_MBALL)) { - EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow); - } - else if (ob->type == OB_HAIR) { - EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow); - } - else if (ob->type == OB_VOLUME) { - EEVEE_volumes_cache_object_add(sldata, vedata, draw_ctx->scene, ob); - } - else if (!USE_SCENE_LIGHT(draw_ctx->v3d)) { - /* do not add any scene light sources to the cache */ - } - else if (ob->type == OB_LIGHTPROBE) { - if ((ob->base_flag & BASE_FROM_DUPLI) != 0) { - /* TODO: Special case for dupli objects because we cannot save the object pointer. */ - } - else { - EEVEE_lightprobes_cache_add(sldata, vedata, ob); - } - } - else if (ob->type == OB_LAMP) { - EEVEE_lights_cache_add(sldata, ob); - } - } - - if (cast_shadow) { - EEVEE_shadows_caster_register(sldata, ob); - } -} +typedef struct EEVEE_Data { + DrawEngineType *engine_type; + DRWViewportEmptyList *fbl; + DRWViewportEmptyList *txl; + DRWViewportEmptyList *psl; + DRWViewportEmptyList *stl; + struct EEVEE_Instance *instance_data; +} EEVEE_Data; -static void eevee_cache_finish(void *vedata) +static void eevee_engine_init(void *vedata) { - EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); - EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; - EEVEE_PrivateData *g_data = stl->g_data; - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - - EEVEE_volumes_cache_finish(sldata, vedata); - EEVEE_materials_cache_finish(sldata, vedata); - EEVEE_lights_cache_finish(sldata, vedata); - EEVEE_lightprobes_cache_finish(sldata, vedata); - EEVEE_renderpasses_cache_finish(sldata, vedata); - - EEVEE_subsurface_draw_init(sldata, vedata); - EEVEE_effects_draw_init(sldata, vedata); - EEVEE_volumes_draw_init(sldata, vedata); + EEVEE_Data *ved = (EEVEE_Data *)vedata; - uint tot_samples = scene_eval->eevee.taa_render_samples; - if (tot_samples == 0) { - /* Use a high number of samples so the outputs accumulation buffers - * will have the highest possible precision. */ - tot_samples = 1024; + if (ved->instance_data == NULL) { + ved->instance_data = EEVEE_instance_alloc(); } - EEVEE_renderpasses_output_init(sldata, vedata, tot_samples); - /* Restart TAA if a shader has finish compiling. */ - /* HACK: We should use notification of some sort from the compilation job instead. */ - if (g_data->queued_shaders_count != g_data->queued_shaders_count_prev) { - g_data->queued_shaders_count_prev = g_data->queued_shaders_count; - EEVEE_temporal_sampling_reset(vedata); - } + EEVEE_instance_init(ved->instance_data); } -/* As renders in an HDR off-screen buffer, we need draw everything once - * during the background pass. This way the other drawing callback between - * the background and the scene pass are visible. - * NOTE: we could break it up in two passes using some depth test - * to reduce the fill-rate. */ static void eevee_draw_scene(void *vedata) { - EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; - EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; - EEVEE_FramebufferList *fbl = ((EEVEE_Data *)vedata)->fbl; - EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); - - /* Default framebuffer and texture */ - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); - - /* Sort transparents before the loop. */ - DRW_pass_sort_shgroup_z(psl->transparent_pass); - - /* Number of iteration: Use viewport taa_samples when using viewport rendering */ - int loop_len = 1; - if (DRW_state_is_image_render()) { - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene = draw_ctx->scene; - loop_len = MAX2(1, scene->eevee.taa_samples); - } - - if (stl->effects->bypass_drawing) { - loop_len = 0; - } - - while (loop_len--) { - const float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - float clear_depth = 1.0f; - uint clear_stencil = 0x0; - const uint primes[3] = {2, 3, 7}; - double offset[3] = {0.0, 0.0, 0.0}; - double r[3]; - - bool taa_use_reprojection = (stl->effects->enabled_effects & EFFECT_TAA_REPROJECT) != 0; - - if (DRW_state_is_image_render() || taa_use_reprojection || - ((stl->effects->enabled_effects & EFFECT_TAA) != 0)) { - int samp = taa_use_reprojection ? stl->effects->taa_reproject_sample + 1 : - stl->effects->taa_current_sample; - BLI_halton_3d(primes, offset, samp, r); - EEVEE_update_noise(psl, fbl, r); - EEVEE_volumes_set_jitter(sldata, samp - 1); - EEVEE_materials_init(sldata, vedata, stl, fbl); - } - /* Copy previous persmat to UBO data */ - copy_m4_m4(sldata->common_data.prev_persmat, stl->effects->prev_persmat); - - /* Refresh Probes - * Shadows needs to be updated for correct probes */ - DRW_stats_group_start("Probes Refresh"); - EEVEE_shadows_update(sldata, vedata); - EEVEE_lightprobes_refresh(sldata, vedata); - EEVEE_lightprobes_refresh_planar(sldata, vedata); - DRW_stats_group_end(); - - /* Refresh shadows */ - DRW_stats_group_start("Shadows"); - EEVEE_shadows_draw(sldata, vedata, stl->effects->taa_view); - DRW_stats_group_end(); - - if (((stl->effects->enabled_effects & EFFECT_TAA) != 0) && - (stl->effects->taa_current_sample > 1) && !DRW_state_is_image_render() && - !taa_use_reprojection) { - DRW_view_set_active(stl->effects->taa_view); - } - /* when doing viewport rendering the overrides needs to be recalculated for - * every loop as this normally happens once inside - * `EEVEE_temporal_sampling_init` */ - else if (((stl->effects->enabled_effects & EFFECT_TAA) != 0) && - (stl->effects->taa_current_sample > 1) && DRW_state_is_image_render()) { - EEVEE_temporal_sampling_update_matrices(vedata); - } - - /* Set ray type. */ - sldata->common_data.ray_type = EEVEE_RAY_CAMERA; - sldata->common_data.ray_depth = 0.0f; - GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); - - GPU_framebuffer_bind(fbl->main_fb); - eGPUFrameBufferBits clear_bits = GPU_DEPTH_BIT; - SET_FLAG_FROM_TEST(clear_bits, !DRW_state_draw_background(), GPU_COLOR_BIT); - SET_FLAG_FROM_TEST(clear_bits, (stl->effects->enabled_effects & EFFECT_SSS), GPU_STENCIL_BIT); - GPU_framebuffer_clear(fbl->main_fb, clear_bits, clear_col, clear_depth, clear_stencil); - - /* Depth prepass */ - DRW_stats_group_start("Prepass"); - DRW_draw_pass(psl->depth_ps); - DRW_stats_group_end(); - - /* Create minmax texture */ - DRW_stats_group_start("Main MinMax buffer"); - EEVEE_create_minmax_buffer(vedata, dtxl->depth, -1); - DRW_stats_group_end(); - - EEVEE_occlusion_compute(sldata, vedata); - EEVEE_volumes_compute(sldata, vedata); - - /* Shading pass */ - DRW_stats_group_start("Shading"); - if (DRW_state_draw_background()) { - DRW_draw_pass(psl->background_ps); - } - DRW_draw_pass(psl->material_ps); - EEVEE_subsurface_data_render(sldata, vedata); - DRW_stats_group_end(); - - /* Effects pre-transparency */ - EEVEE_subsurface_compute(sldata, vedata); - EEVEE_reflection_compute(sldata, vedata); - EEVEE_occlusion_draw_debug(sldata, vedata); - if (psl->probe_display) { - DRW_draw_pass(psl->probe_display); - } - EEVEE_refraction_compute(sldata, vedata); - - /* Opaque refraction */ - DRW_stats_group_start("Opaque Refraction"); - DRW_draw_pass(psl->depth_refract_ps); - DRW_draw_pass(psl->material_refract_ps); - DRW_stats_group_end(); - - /* Volumetrics Resolve Opaque */ - EEVEE_volumes_resolve(sldata, vedata); - - /* Renderpasses */ - EEVEE_renderpasses_output_accumulate(sldata, vedata, false); - - /* Transparent */ - /* TODO(fclem): should be its own Frame-buffer. - * This is needed because dualsource blending only works with 1 color buffer. */ - GPU_framebuffer_texture_attach(fbl->main_color_fb, dtxl->depth, 0, 0); - GPU_framebuffer_bind(fbl->main_color_fb); - DRW_draw_pass(psl->transparent_pass); - GPU_framebuffer_bind(fbl->main_fb); - GPU_framebuffer_texture_detach(fbl->main_color_fb, dtxl->depth); - - /* Post Process */ - DRW_stats_group_start("Post FX"); - EEVEE_draw_effects(sldata, vedata); - DRW_stats_group_end(); - - DRW_view_set_active(NULL); - - if (DRW_state_is_image_render() && (stl->effects->enabled_effects & EFFECT_SSR) && - !stl->effects->ssr_was_valid_double_buffer) { - /* SSR needs one iteration to start properly. */ - loop_len++; - /* Reset sampling (and accumulation) after the first sample to avoid - * washed out first bounce for SSR. */ - EEVEE_temporal_sampling_reset(vedata); - stl->effects->ssr_was_valid_double_buffer = stl->g_data->valid_double_buffer; - } - } - - if ((stl->g_data->render_passes & EEVEE_RENDER_PASS_COMBINED) != 0) { - /* Transfer result to default framebuffer. */ - GPU_framebuffer_bind(dfbl->default_fb); - DRW_transform_none(stl->effects->final_tx); - } - else { - EEVEE_renderpasses_draw(sldata, vedata); - } - - if (stl->effects->bypass_drawing) { - /* Restore the depth from sample 1. */ - GPU_framebuffer_blit(fbl->double_buffer_depth_fb, 0, dfbl->default_fb, 0, GPU_DEPTH_BIT); - } - - EEVEE_renderpasses_draw_debug(vedata); - - EEVEE_volumes_free_smoke_textures(); - - stl->g_data->view_updated = false; - - DRW_view_set_active(NULL); -} - -static void eevee_view_update(void *vedata) -{ - EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; - if (stl->g_data) { - stl->g_data->view_updated = true; - } + EEVEE_instance_draw_viewport(((EEVEE_Data *)vedata)->instance_data); } -static void eevee_id_object_update(void *UNUSED(vedata), Object *object) +static void eevee_cache_init(void *vedata) { - EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(object); - if (ped != NULL && ped->dd.recalc != 0) { - ped->need_update = (ped->dd.recalc & ID_RECALC_TRANSFORM) != 0; - ped->dd.recalc = 0; - } - EEVEE_LightEngineData *led = EEVEE_light_data_get(object); - if (led != NULL && led->dd.recalc != 0) { - led->need_update = true; - led->dd.recalc = 0; - } - EEVEE_ObjectEngineData *oedata = EEVEE_object_data_get(object); - if (oedata != NULL && oedata->dd.recalc != 0) { - oedata->need_update = true; - oedata->geom_update = (oedata->dd.recalc & (ID_RECALC_GEOMETRY)) != 0; - oedata->dd.recalc = 0; - } + EEVEE_instance_cache_init(((EEVEE_Data *)vedata)->instance_data); } -static void eevee_id_world_update(void *vedata, World *wo) +static void eevee_cache_populate(void *vedata, Object *object) { - EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; - LightCache *lcache = stl->g_data->light_cache; - - if (ELEM(lcache, NULL, stl->lookdev_lightcache)) { - /* Avoid Lookdev viewport clearing the update flag (see T67741). */ - return; - } - - EEVEE_WorldEngineData *wedata = EEVEE_world_data_ensure(wo); - - if (wedata != NULL && wedata->dd.recalc != 0) { - if ((lcache->flag & LIGHTCACHE_BAKING) == 0) { - lcache->flag |= LIGHTCACHE_UPDATE_WORLD; - } - wedata->dd.recalc = 0; - } + EEVEE_instance_cache_populate(((EEVEE_Data *)vedata)->instance_data, object); } -void eevee_id_update(void *vedata, ID *id) +static void eevee_cache_finish(void *vedata) { - /* Handle updates based on ID type. */ - switch (GS(id->name)) { - case ID_WO: - eevee_id_world_update(vedata, (World *)id); - break; - case ID_OB: - eevee_id_object_update(vedata, (Object *)id); - break; - default: - /* pass */ - break; - } + EEVEE_instance_cache_finish(((EEVEE_Data *)vedata)->instance_data); } -static void eevee_render_reset_passes(EEVEE_Data *vedata) +static void eevee_engine_free(void) { - /* Reset passlist. This is safe as they are stored into managed memory chunks. */ - memset(vedata->psl, 0, sizeof(*vedata->psl)); + EEVEE_shared_data_free(); } -static void eevee_render_to_image(void *vedata, - RenderEngine *engine, - struct RenderLayer *render_layer, - const rcti *rect) +static void eevee_instance_free(void *instance_data) { - EEVEE_Data *ved = (EEVEE_Data *)vedata; - const DRWContextState *draw_ctx = DRW_context_state_get(); - Depsgraph *depsgraph = draw_ctx->depsgraph; - Scene *scene = DEG_get_evaluated_scene(depsgraph); - EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); - const bool do_motion_blur = (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0; - const bool do_motion_blur_fx = do_motion_blur && (scene->eevee.motion_blur_max > 0); - - if (!EEVEE_render_init(vedata, engine, depsgraph)) { - return; - } - EEVEE_PrivateData *g_data = ved->stl->g_data; - - int initial_frame = CFRA; - float initial_subframe = SUBFRA; - float shuttertime = (do_motion_blur) ? scene->eevee.motion_blur_shutter : 0.0f; - int time_steps_tot = (do_motion_blur) ? max_ii(1, scene->eevee.motion_blur_steps) : 1; - g_data->render_timesteps = time_steps_tot; - - EEVEE_render_modules_init(vedata, engine, depsgraph); - - g_data->render_sample_count_per_timestep = EEVEE_temporal_sampling_sample_count_get(scene, - ved->stl); - - /* Reset in case the same engine is used on multiple views. */ - EEVEE_temporal_sampling_reset(vedata); - - /* Compute start time. The motion blur will cover `[time ...time + shuttertime]`. */ - float time = initial_frame + initial_subframe; - switch (scene->eevee.motion_blur_position) { - case SCE_EEVEE_MB_START: - /* No offset. */ - break; - case SCE_EEVEE_MB_CENTER: - time -= shuttertime * 0.5f; - break; - case SCE_EEVEE_MB_END: - time -= shuttertime; - break; - default: - BLI_assert_msg(0, "Invalid motion blur position enum!"); - break; - } - - float time_step = shuttertime / time_steps_tot; - for (int i = 0; i < time_steps_tot && !RE_engine_test_break(engine); i++) { - float time_prev = time; - float time_curr = time + time_step * 0.5f; - float time_next = time + time_step; - time += time_step; - - /* Previous motion step. */ - if (do_motion_blur_fx) { - if (i == 0) { - EEVEE_motion_blur_step_set(ved, MB_PREV); - DRW_render_set_time(engine, depsgraph, floorf(time_prev), fractf(time_prev)); - EEVEE_render_modules_init(vedata, engine, depsgraph); - sldata = EEVEE_view_layer_data_ensure(); - - EEVEE_render_cache_init(sldata, vedata); - - DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache); - - EEVEE_motion_blur_cache_finish(vedata); - EEVEE_materials_cache_finish(sldata, vedata); - eevee_render_reset_passes(vedata); - } - } - - /* Next motion step. */ - if (do_motion_blur_fx) { - EEVEE_motion_blur_step_set(ved, MB_NEXT); - DRW_render_set_time(engine, depsgraph, floorf(time_next), fractf(time_next)); - EEVEE_render_modules_init(vedata, engine, depsgraph); - sldata = EEVEE_view_layer_data_ensure(); - - EEVEE_render_cache_init(sldata, vedata); - - DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache); - - EEVEE_motion_blur_cache_finish(vedata); - EEVEE_materials_cache_finish(sldata, vedata); - eevee_render_reset_passes(vedata); - } - - /* Current motion step. */ - { - if (do_motion_blur) { - EEVEE_motion_blur_step_set(ved, MB_CURR); - DRW_render_set_time(engine, depsgraph, floorf(time_curr), fractf(time_curr)); - EEVEE_render_modules_init(vedata, engine, depsgraph); - sldata = EEVEE_view_layer_data_ensure(); - } - - EEVEE_render_cache_init(sldata, vedata); - - DRW_render_object_iter(vedata, engine, depsgraph, EEVEE_render_cache); - - EEVEE_motion_blur_cache_finish(vedata); - EEVEE_volumes_cache_finish(sldata, vedata); - EEVEE_materials_cache_finish(sldata, vedata); - EEVEE_lights_cache_finish(sldata, vedata); - EEVEE_lightprobes_cache_finish(sldata, vedata); - EEVEE_renderpasses_cache_finish(sldata, vedata); - - EEVEE_subsurface_draw_init(sldata, vedata); - EEVEE_effects_draw_init(sldata, vedata); - EEVEE_volumes_draw_init(sldata, vedata); - } - - /* Actual drawing. */ - { - EEVEE_renderpasses_output_init( - sldata, vedata, g_data->render_sample_count_per_timestep * time_steps_tot); - - if (scene->world) { - /* Update world in case of animated world material. */ - eevee_id_world_update(vedata, scene->world); - } - - EEVEE_temporal_sampling_create_view(vedata); - EEVEE_render_draw(vedata, engine, render_layer, rect); - - if (i < time_steps_tot - 1) { - /* Don't reset after the last loop. Since EEVEE_render_read_result - * might need some DRWPasses. */ - DRW_cache_restart(); - } - } - - if (do_motion_blur_fx) { - /* The previous step of next iteration N is exactly the next step of this iteration N - 1. - * So we just swap the resources to avoid too much re-evaluation. - * Note that this also clears the VBO references from the GPUBatches of deformed - * geometries. */ - EEVEE_motion_blur_swap_data(vedata); - } - } - - EEVEE_volumes_free_smoke_textures(); - EEVEE_motion_blur_data_free(&ved->stl->effects->motion_blur); - - if (RE_engine_test_break(engine)) { - return; - } - - EEVEE_render_read_result(vedata, engine, render_layer, rect); - - /* Restore original viewport size. */ - DRW_render_viewport_size_set((int[2]){g_data->size_orig[0], g_data->size_orig[1]}); - - if (CFRA != initial_frame || SUBFRA != initial_subframe) { - /* Restore original frame number. This is because the render pipeline expects it. */ - RE_engine_frame_set(engine, initial_frame, initial_subframe); - } + EEVEE_instance_free((struct EEVEE_Instance *)instance_data); } -static void eevee_store_metadata(void *vedata, struct RenderResult *render_result) +static void eevee_render_to_image(void *UNUSED(vedata), + struct RenderEngine *engine, + struct RenderLayer *layer, + const struct rcti *UNUSED(rect)) { - EEVEE_Data *ved = (EEVEE_Data *)vedata; - EEVEE_PrivateData *g_data = ved->stl->g_data; - if (g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) { - EEVEE_cryptomatte_store_metadata(ved, render_result); - EEVEE_cryptomatte_free(ved); - } + struct EEVEE_Instance *instance = EEVEE_instance_alloc(); + EEVEE_instance_render_frame(instance, engine, layer); + EEVEE_instance_free(instance); } -static void eevee_engine_free(void) +static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer) { - EEVEE_shaders_free(); - EEVEE_lightprobes_free(); - EEVEE_materials_free(); - EEVEE_occlusion_free(); - EEVEE_volumes_free(); + RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA); + +#define CHECK_PASS_LEGACY(name, type, channels, chanid) \ + if (view_layer->passflag & (SCE_PASS_##name)) { \ + RE_engine_register_pass( \ + engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \ + } \ + ((void)0) +#define CHECK_PASS_EEVEE(name, type, channels, chanid) \ + if (view_layer->eevee.render_passes & (EEVEE_RENDER_PASS_##name)) { \ + RE_engine_register_pass( \ + engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \ + } \ + ((void)0) + + CHECK_PASS_LEGACY(Z, SOCK_FLOAT, 1, "Z"); + CHECK_PASS_LEGACY(MIST, SOCK_FLOAT, 1, "Z"); + CHECK_PASS_LEGACY(NORMAL, SOCK_VECTOR, 3, "XYZ"); + CHECK_PASS_LEGACY(VECTOR, SOCK_RGBA, 4, "RGBA"); + CHECK_PASS_LEGACY(DIFFUSE_DIRECT, SOCK_RGBA, 3, "RGB"); + CHECK_PASS_LEGACY(DIFFUSE_COLOR, SOCK_RGBA, 3, "RGB"); + CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB"); + CHECK_PASS_LEGACY(GLOSSY_COLOR, SOCK_RGBA, 3, "RGB"); + CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB"); + CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB"); + CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB"); + CHECK_PASS_LEGACY(SHADOW, SOCK_RGBA, 3, "RGB"); + CHECK_PASS_LEGACY(AO, SOCK_RGBA, 3, "RGB"); + CHECK_PASS_EEVEE(BLOOM, SOCK_RGBA, 3, "RGB"); + + LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) { + if ((aov->flag & AOV_CONFLICT) != 0) { + continue; + } + switch (aov->type) { + case AOV_TYPE_COLOR: + RE_engine_register_pass(engine, scene, view_layer, aov->name, 4, "RGBA", SOCK_RGBA); + break; + case AOV_TYPE_VALUE: + RE_engine_register_pass(engine, scene, view_layer, aov->name, 1, "X", SOCK_FLOAT); + break; + default: + break; + } + } + // EEVEE_cryptomatte_update_passes(engine, scene, view_layer); + +#undef CHECK_PASS_LEGACY +#undef CHECK_PASS_EEVEE } static const DrawEngineDataSize eevee_data_size = DRW_VIEWPORT_DATA_SIZE(EEVEE_Data); @@ -629,17 +150,19 @@ DrawEngineType draw_engine_eevee_type = { &eevee_data_size, &eevee_engine_init, &eevee_engine_free, - NULL, /* instance_free */ + &eevee_instance_free, &eevee_cache_init, - &EEVEE_cache_populate, + &eevee_cache_populate, &eevee_cache_finish, &eevee_draw_scene, - &eevee_view_update, - &eevee_id_update, + NULL, + NULL, &eevee_render_to_image, - &eevee_store_metadata, + NULL, }; +#define EEVEE_ENGINE "BLENDER_EEVEE" + RenderEngineType DRW_engine_viewport_eevee_type = { NULL, NULL, @@ -654,7 +177,7 @@ RenderEngineType DRW_engine_viewport_eevee_type = { NULL, NULL, NULL, - &EEVEE_render_update_passes, + &eevee_render_update_passes, &draw_engine_eevee_type, {NULL, NULL, NULL}, }; diff --git a/source/blender/draw/engines/eevee/eevee_engine.cc b/source/blender/draw/engines/eevee/eevee_engine.cc new file mode 100644 index 00000000000..a25c31f4690 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_engine.cc @@ -0,0 +1,167 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "BKE_global.h" +#include "BLI_rect.h" + +#include "GPU_framebuffer.h" + +#include "ED_view3d.h" + +#include "DRW_render.h" + +#include "eevee_private.h" + +#include "eevee_instance.hh" + +using namespace blender::eevee; + +static ShaderModule *g_shader_module = nullptr; + +/* -------------------------------------------------------------------- */ +/** \name EEVEE Instance C interface + * \{ */ + +EEVEE_Instance *EEVEE_instance_alloc(void) +{ + if (g_shader_module == nullptr) { + /* TODO(fclem) threadsafety. */ + g_shader_module = new ShaderModule(); + } + return reinterpret_cast<EEVEE_Instance *>(new Instance(*g_shader_module)); +} + +void EEVEE_instance_free(EEVEE_Instance *instance_) +{ + Instance *instance = reinterpret_cast<Instance *>(instance_); + delete instance; +} + +void EEVEE_instance_init(EEVEE_Instance *instance_) +{ + Instance *instance = reinterpret_cast<Instance *>(instance_); + + const DRWContextState *ctx_state = DRW_context_state_get(); + Depsgraph *depsgraph = ctx_state->depsgraph; + Scene *scene = ctx_state->scene; + View3D *v3d = ctx_state->v3d; + const ARegion *region = ctx_state->region; + RegionView3D *rv3d = ctx_state->rv3d; + + /* Scaling output to better see what happens with accumulation. */ + int resolution_divider = (ELEM(G.debug_value, 1, 2)) ? 16 : 1; + + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + int size[2]; + size[0] = divide_ceil_u(GPU_texture_width(dtxl->color), resolution_divider); + size[1] = divide_ceil_u(GPU_texture_height(dtxl->color), resolution_divider); + + const DRWView *default_view = DRW_view_default_get(); + + Object *camera = nullptr; + /* Get render borders. */ + rcti rect; + BLI_rcti_init(&rect, 0, size[0], 0, size[1]); + if (v3d) { + if (rv3d && (rv3d->persp == RV3D_CAMOB)) { + camera = v3d->camera; + } + + if (v3d->flag2 & V3D_RENDER_BORDER) { + if (camera) { + rctf viewborder; + /* TODO(fclem) Might be better to get it from DRW. */ + ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &viewborder, false); + float viewborder_sizex = BLI_rctf_size_x(&viewborder); + float viewborder_sizey = BLI_rctf_size_y(&viewborder); + rect.xmin = floorf(viewborder.xmin + (scene->r.border.xmin * viewborder_sizex)); + rect.ymin = floorf(viewborder.ymin + (scene->r.border.ymin * viewborder_sizey)); + rect.xmax = floorf(viewborder.xmin + (scene->r.border.xmax * viewborder_sizex)); + rect.ymax = floorf(viewborder.ymin + (scene->r.border.ymax * viewborder_sizey)); + } + else { + rect.xmin = v3d->render_border.xmin * size[0]; + rect.ymin = v3d->render_border.ymin * size[1]; + rect.xmax = v3d->render_border.xmax * size[0]; + rect.ymax = v3d->render_border.ymax * size[1]; + } + } + } + + instance->init( + size, &rect, nullptr, depsgraph, nullptr, camera, nullptr, default_view, v3d, rv3d); +} + +void EEVEE_instance_cache_init(EEVEE_Instance *instance_) +{ + Instance *instance = reinterpret_cast<Instance *>(instance_); + instance->begin_sync(); +} + +void EEVEE_instance_cache_populate(EEVEE_Instance *instance_, Object *object) +{ + Instance *instance = reinterpret_cast<Instance *>(instance_); + instance->object_sync(object); +} + +void EEVEE_instance_cache_finish(EEVEE_Instance *instance_) +{ + Instance *instance = reinterpret_cast<Instance *>(instance_); + instance->end_sync(); +} + +void EEVEE_instance_draw_viewport(EEVEE_Instance *instance_) +{ + Instance *instance = reinterpret_cast<Instance *>(instance_); + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + + instance->draw_viewport(dfbl); +} + +void EEVEE_instance_render_frame(EEVEE_Instance *instance_, + struct RenderEngine *engine, + struct RenderLayer *render_layer) +{ + Instance *instance = reinterpret_cast<Instance *>(instance_); + Render *render = engine->re; + Depsgraph *depsgraph = DRW_context_state_get()->depsgraph; + Object *camera_original_ob = RE_GetCamera(engine->re); + const char *viewname = RE_GetActiveRenderView(engine->re); + int size[2] = {engine->resolution_x, engine->resolution_y}; + + rctf view_rect; + rcti rect; + RE_GetViewPlane(render, &view_rect, &rect); + + instance->init(size, &rect, engine, depsgraph, nullptr, camera_original_ob, render_layer); + instance->render_frame(render_layer, viewname); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name EEVEE Shaders C interface + * \{ */ + +void EEVEE_shared_data_free(void) +{ + delete g_shader_module; + g_shader_module = nullptr; +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/eevee_engine.h b/source/blender/draw/engines/eevee/eevee_engine.h index 40784e2980b..94d2e541b88 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.h +++ b/source/blender/draw/engines/eevee/eevee_engine.h @@ -22,4 +22,12 @@ #pragma once -extern RenderEngineType DRW_engine_viewport_eevee_type; +#ifdef __cplusplus +extern "C" { +#endif + + extern RenderEngineType DRW_engine_viewport_eevee_type; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/engines/eevee/eevee_film.cc b/source/blender/draw/engines/eevee/eevee_film.cc new file mode 100644 index 00000000000..2542e39aed0 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_film.cc @@ -0,0 +1,257 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * A film is a fullscreen buffer (usually at output extent) + * that will be able to accumulate sample in any distorted camera_type + * using a pixel filter. + * + * Input needs to be jittered so that the filter converges to the right result. + */ + +#include "BLI_rect.h" + +#include "GPU_framebuffer.h" +#include "GPU_texture.h" + +#include "DRW_render.h" + +#include "eevee_film.hh" +#include "eevee_instance.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name FilmData + * \{ */ + +static eGPUTextureFormat to_gpu_texture_format(eFilmDataType film_type) +{ + switch (film_type) { + default: + case FILM_DATA_COLOR_LOG: + case FILM_DATA_COLOR: + case FILM_DATA_MOTION: + case FILM_DATA_VEC4: + return GPU_RGBA16F; + case FILM_DATA_FLOAT: + return GPU_R16F; + case FILM_DATA_VEC2: + return GPU_RG16F; + case FILM_DATA_NORMAL: + return GPU_RGB10_A2; + case FILM_DATA_DEPTH: + return GPU_R32F; + } +} + +inline bool operator==(const FilmData &a, const FilmData &b) +{ + return (a.extent == b.extent) && (a.offset == b.offset); +} + +inline bool operator!=(const FilmData &a, const FilmData &b) +{ + return !(a == b); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Film + * \{ */ + +void Film::init(const ivec2 &full_extent, const rcti *output_rect) +{ + FilmData data = data_; + data.extent = ivec2(BLI_rcti_size_x(output_rect), BLI_rcti_size_y(output_rect)); + data.offset = ivec2(output_rect->xmin, output_rect->ymin); + + has_changed_ = data_ != data; + + if (has_changed_) { + data_ = data; + inst_.sampling.reset(); + } + + data_.opacity = 1.0f; + data_.uv_scale_inv = vec2(full_extent); + data_.uv_scale = 1.0f / data_.uv_scale_inv; + data_.uv_bias = vec2(data_.offset) * data_.uv_scale; +} + +void Film::sync(void) +{ + char full_name[32]; + for (int i = 0; i < 2; i++) { + if (data_tx_[i] == nullptr) { + eGPUTextureFormat tex_format = to_gpu_texture_format(data_.data_type); + SNPRINTF(full_name, "Film.%s.data", name_.c_str()); + data_tx_[i].ensure(full_name, UNPACK2(data_.extent), 1, tex_format); + /* TODO(fclem) The weight texture could be shared between all similar accumulators. */ + SNPRINTF(full_name, "Film.%s.weight", name_.c_str()); + weight_tx_[i].ensure(full_name, UNPACK2(data_.extent), 1, GPU_R16F); + + accumulation_fb_[i].ensure(GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(data_tx_[i]), + GPU_ATTACHMENT_TEXTURE(weight_tx_[i])); + } + } + + eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT; + { + SNPRINTF(full_name, "Film.%s.Accumulate", name_.c_str()); + accumulate_ps_ = DRW_pass_create(full_name, DRW_STATE_WRITE_COLOR); + GPUShader *sh = inst_.shaders.static_shader_get(FILM_FILTER); + DRWShadingGroup *grp = DRW_shgroup_create(sh, accumulate_ps_); + DRW_shgroup_uniform_block(grp, "film_block", data_.ubo_get()); + DRW_shgroup_uniform_block(grp, "camera_block", inst_.camera.ubo_get()); + DRW_shgroup_uniform_texture_ref_ex(grp, "input_tx", &input_tx_, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "data_tx", &data_tx_[0], no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "weight_tx", &weight_tx_[0], no_filter); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + { + SNPRINTF(full_name, "Film.%s.Resolve", name_.c_str()); + DRWState state = DRW_STATE_WRITE_COLOR; + eShaderType sh_type = FILM_RESOLVE; + if (data_.data_type == FILM_DATA_DEPTH) { + state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS; + sh_type = FILM_RESOLVE_DEPTH; + } + resolve_ps_ = DRW_pass_create(full_name, state); + GPUShader *sh = inst_.shaders.static_shader_get(sh_type); + DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_); + DRW_shgroup_uniform_block(grp, "film_block", data_.ubo_get()); + DRW_shgroup_uniform_texture_ref_ex(grp, "first_sample_tx", &first_sample_ref_, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "data_tx", &data_tx_[0], no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "weight_tx", &weight_tx_[0], no_filter); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } +} + +void Film::end_sync() +{ + /* TODO reprojection. */ + if (inst_.sampling.is_reset()) { + data_.use_history = 0; + } + + if (inst_.is_viewport()) { + data_.opacity = inst_.sampling.viewport_smoothing_opacity_factor_get(); + } + + if (data_.use_history == 0 || inst_.is_viewport()) { + data_.push_update(); + } + + const bool is_first_sample = (inst_.sampling.sample_get() == 1); + if (do_smooth_viewport_smooth_transition() && (data_.opacity < 1.0f || is_first_sample)) { + char full_name[32]; + SNPRINTF(full_name, "Film.%s.first_sample", name_.c_str()); + GPUTexture *dtxl_color = DRW_viewport_texture_list_get()->color; + eGPUTextureFormat tex_format = GPU_texture_format(dtxl_color); + int extent[2] = {GPU_texture_width(dtxl_color), GPU_texture_height(dtxl_color)}; + first_sample_tx_.ensure(full_name, UNPACK2(extent), 1, tex_format); + first_sample_ref_ = first_sample_tx_; + } + else { + /* Reuse the data_tx since there is no need to blend. */ + first_sample_tx_.release(); + first_sample_ref_ = data_tx_[0]; + } +} + +void Film::accumulate(GPUTexture *input, const DRWView *view) +{ + input_tx_ = input; + + DRW_view_set_active(view); + + GPU_framebuffer_bind(accumulation_fb_[1]); + DRW_draw_pass(accumulate_ps_); + + SWAP(Framebuffer, accumulation_fb_[0], accumulation_fb_[1]); + SWAP(Texture, data_tx_[0], data_tx_[1]); + SWAP(Texture, weight_tx_[0], weight_tx_[1]); + + /* Use history after first sample. */ + if (data_.use_history == 0) { + data_.use_history = 1; + data_.push_update(); + } +} + +void Film::resolve_viewport(GPUFrameBuffer *target) +{ + int viewport[4]; + + GPU_framebuffer_bind(target); + GPU_framebuffer_viewport_get(target, viewport); + + const bool use_render_border = (data_.offset[0] > 0) || (data_.offset[1] > 0) || + (data_.extent[0] < viewport[2]) || + (data_.extent[1] < viewport[3]); + if (use_render_border) { + if (has_changed_) { + /* Film is cropped and does not fill the view completely. Clear the background. */ + if (data_.data_type == FILM_DATA_DEPTH) { + GPU_framebuffer_clear_depth(target, 1.0f); + } + else { + float color[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + GPU_framebuffer_clear_color(target, color); + } + } + GPU_framebuffer_viewport_set(target, UNPACK2(data_.offset), UNPACK2(data_.extent)); + } + + DRW_draw_pass(resolve_ps_); + + /* Minus one because we already incremented it in step() which is the first + * thing to happen in the sample loop. */ + const bool is_first_sample = (inst_.sampling.sample_get() - 1 == 1); + const bool is_only_one_sample = is_first_sample && inst_.sampling.finished(); + if (is_first_sample && !is_only_one_sample && do_smooth_viewport_smooth_transition()) { + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + GPU_texture_copy(first_sample_tx_, dtxl->color); + } + + if (use_render_border) { + GPU_framebuffer_viewport_reset(target); + } +} + +void Film::read_result(float *data) +{ + /* Resolve onto the next data texture. */ + read_result_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(data_tx_[1])); + GPU_framebuffer_bind(read_result_fb_); + DRW_draw_pass(resolve_ps_); + + eGPUTextureFormat format = to_gpu_texture_format(data_.data_type); + int channel_count = GPU_texture_component_len(format); + GPU_framebuffer_read_color( + read_result_fb_, 0, 0, UNPACK2(data_.extent), channel_count, 0, GPU_DATA_FLOAT, data); +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_film.hh b/source/blender/draw/engines/eevee/eevee_film.hh new file mode 100644 index 00000000000..06e019e5a3a --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_film.hh @@ -0,0 +1,112 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * A film is a fullscreen buffer (usually at output extent) + * that will be able to accumulate sample in any distorted camera_type + * using a pixel filter. + * + * Input needs to be jittered so that the filter converges to the right result. + */ + +#pragma once + +#include "DRW_render.h" + +#include "eevee_camera.hh" +#include "eevee_sampling.hh" +#include "eevee_shader.hh" +#include "eevee_wrapper.hh" + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name Film + * \{ */ + +class Film { + private: + Instance &inst_; + + /** Owned resources. */ + eevee::Framebuffer read_result_fb_; + eevee::Framebuffer accumulation_fb_[2]; + eevee::Texture data_tx_[2]; + eevee::Texture weight_tx_[2]; + /** First sample in case we need to blend using it or just reuse it. */ + eevee::Texture first_sample_tx_; + + /** Reference to first_sample_tx_ or data_tx_ depending on the context. */ + GPUTexture *first_sample_ref_; + + // DRWPass *clear_ps_ = nullptr; + DRWPass *accumulate_ps_ = nullptr; + DRWPass *resolve_ps_ = nullptr; + + /** Shader parameter, not allocated. */ + GPUTexture *input_tx_; + /** ViewProjection matrix used to render the input. */ + // float src_persmat_[4][4]; + /** ViewProjection matrix Inverse used to render the input. */ + // float src_persinv_[4][4]; + + StructBuffer<FilmData> data_; + + /** True if offset or size changed. */ + bool has_changed_ = true; + + /** Debug static name. */ + StringRefNull name_; + + public: + /* NOTE: name needs to be static. */ + Film(Instance &inst, eFilmDataType data_type, const char *name) : inst_(inst), name_(name) + { + data_.extent[0] = data_.extent[1] = -1; + data_.data_type = data_type; + data_.use_history = 0; + } + + ~Film(){}; + + void init(const ivec2 &full_extent, const rcti *output_rect); + + void sync(void); + void end_sync(void); + + void accumulate(GPUTexture *input, const DRWView *view); + + void resolve_viewport(GPUFrameBuffer *target); + + void read_result(float *data); + + private: + bool do_smooth_viewport_smooth_transition(void) + { + return ELEM(data_.data_type, FILM_DATA_COLOR, FILM_DATA_COLOR_LOG) && + !DRW_state_is_image_render(); + } +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_gbuffer.hh b/source/blender/draw/engines/eevee/eevee_gbuffer.hh new file mode 100644 index 00000000000..452aca584d0 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_gbuffer.hh @@ -0,0 +1,258 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Gbuffer layout used for deferred shading pipeline. + */ + +#pragma once + +#include "DRW_render.h" + +#include "eevee_wrapper.hh" + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name Gbuffer + * + * Fullscreen textures containing geometric, surface and volume data. + * Used by deferred shading layers. Only one gbuffer is allocated per view + * and is reused for each deferred layer. This is why there can only be temporary + * texture inside it. + * \{ */ + +/** NOTE: Theses are used as stencil bits. So we are limited to 8bits. */ +enum eClosureBits { + CLOSURE_DIFFUSE = 1 << 0, + CLOSURE_SSS = 1 << 1, + CLOSURE_REFLECTION = 1 << 2, + CLOSURE_REFRACTION = 1 << 3, + CLOSURE_VOLUME = 1 << 4, + CLOSURE_EMISSION = 1 << 5, + CLOSURE_TRANSPARENCY = 1 << 6, +}; + +struct GBuffer { + Texture transmit_color_tx = Texture("GbufferTransmitColor"); + Texture transmit_normal_tx = Texture("GbufferTransmitNormal"); + Texture transmit_data_tx = Texture("GbufferTransmitData"); + Texture reflect_color_tx = Texture("GbufferReflectionColor"); + Texture reflect_normal_tx = Texture("GbufferReflectionNormal"); + Texture volume_tx = Texture("GbufferVolume"); + Texture emission_tx = Texture("GbufferEmission"); + Texture transparency_tx = Texture("GbufferTransparency"); + + Framebuffer gbuffer_fb = Framebuffer("Gbuffer"); + Framebuffer volume_fb = Framebuffer("VolumeHeterogeneous"); + + Texture holdout_tx = Texture("HoldoutRadiance"); + Texture diffuse_tx = Texture("DiffuseRadiance"); + + Framebuffer radiance_fb = Framebuffer("Radiance"); + Framebuffer radiance_clear_fb = Framebuffer("RadianceClear"); + + Framebuffer holdout_fb = Framebuffer("Holdout"); + + Texture depth_behind_tx = Texture("DepthBehind"); + + Framebuffer depth_behind_fb = Framebuffer("DepthCopy"); + + /** Raytracing. */ + Texture ray_data_tx = Texture("RayData"); + Texture ray_radiance_tx = Texture("RayRadiance"); + Texture ray_variance_tx = Texture("RayVariance"); + Framebuffer ray_data_fb = Framebuffer("RayData"); + Framebuffer ray_denoise_fb = Framebuffer("RayDenoise"); + + /* Owner of this GBuffer. Used to query temp textures. */ + void *owner; + + /* Pointer to the view's buffers. */ + GPUTexture *depth_tx = nullptr; + GPUTexture *combined_tx = nullptr; + int layer = -1; + + void sync(GPUTexture *depth_tx_, GPUTexture *combined_tx_, void *owner_, int layer_ = -1) + { + owner = owner_; + depth_tx = depth_tx_; + combined_tx = combined_tx_; + layer = layer_; + transmit_color_tx.sync_tmp(); + transmit_normal_tx.sync_tmp(); + transmit_data_tx.sync_tmp(); + reflect_color_tx.sync_tmp(); + reflect_normal_tx.sync_tmp(); + volume_tx.sync_tmp(); + emission_tx.sync_tmp(); + transparency_tx.sync_tmp(); + holdout_tx.sync_tmp(); + diffuse_tx.sync_tmp(); + depth_behind_tx.sync_tmp(); + ray_data_tx.sync_tmp(); + ray_radiance_tx.sync_tmp(); + ray_variance_tx.sync_tmp(); + } + + void prepare(eClosureBits closures_used) + { + ivec2 extent = {GPU_texture_width(depth_tx), GPU_texture_height(depth_tx)}; + + /* TODO Reuse for different config. */ + if (closures_used & (CLOSURE_DIFFUSE | CLOSURE_SSS | CLOSURE_REFRACTION)) { + transmit_color_tx.acquire_tmp(UNPACK2(extent), GPU_R11F_G11F_B10F, owner); + } + if (closures_used & (CLOSURE_SSS | CLOSURE_REFRACTION)) { + transmit_normal_tx.acquire_tmp(UNPACK2(extent), GPU_RGBA16F, owner); + transmit_data_tx.acquire_tmp(UNPACK2(extent), GPU_R11F_G11F_B10F, owner); + } + else if (closures_used & CLOSURE_DIFFUSE) { + transmit_normal_tx.acquire_tmp(UNPACK2(extent), GPU_RG16F, owner); + } + if (closures_used & CLOSURE_SSS) { + diffuse_tx.acquire_tmp(UNPACK2(extent), GPU_RGBA16F, owner); + } + + if (closures_used & CLOSURE_REFLECTION) { + reflect_color_tx.acquire_tmp(UNPACK2(extent), GPU_R11F_G11F_B10F, owner); + reflect_normal_tx.acquire_tmp(UNPACK2(extent), GPU_RGBA16F, owner); + } + + if (closures_used & CLOSURE_VOLUME) { + /* TODO(fclem): This is killing performance. + * Idea: use interleaved data pattern to fill only a 32bpp buffer. */ + volume_tx.acquire_tmp(UNPACK2(extent), GPU_RGBA32UI, owner); + } + + if (closures_used & CLOSURE_EMISSION) { + emission_tx.acquire_tmp(UNPACK2(extent), GPU_R11F_G11F_B10F, owner); + } + + if (closures_used & CLOSURE_TRANSPARENCY) { + /* TODO(fclem): Speedup by using Dithered holdout and GPU_RGB10_A2. */ + transparency_tx.acquire_tmp(UNPACK2(extent), GPU_RGBA16, owner); + } + + if (closures_used & (CLOSURE_DIFFUSE | CLOSURE_REFLECTION | CLOSURE_REFRACTION)) { + ray_data_tx.acquire_tmp(UNPACK2(extent), GPU_RGBA16F, owner); + ray_radiance_tx.acquire_tmp(UNPACK2(extent), GPU_RGBA16F, owner); + ray_variance_tx.acquire_tmp(UNPACK2(extent), GPU_R8, owner); + } + + holdout_tx.acquire_tmp(UNPACK2(extent), GPU_R11F_G11F_B10F, owner); + depth_behind_tx.acquire_tmp(UNPACK2(extent), GPU_DEPTH24_STENCIL8, owner); + + /* Layer attachement also works with cubemap. */ + gbuffer_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer), + GPU_ATTACHMENT_TEXTURE(transmit_color_tx), + GPU_ATTACHMENT_TEXTURE(transmit_normal_tx), + GPU_ATTACHMENT_TEXTURE(transmit_data_tx), + GPU_ATTACHMENT_TEXTURE(reflect_color_tx), + GPU_ATTACHMENT_TEXTURE(reflect_normal_tx), + GPU_ATTACHMENT_TEXTURE(volume_tx), + GPU_ATTACHMENT_TEXTURE(emission_tx), + GPU_ATTACHMENT_TEXTURE(transparency_tx)); + } + + void bind(void) + { + GPU_framebuffer_bind(gbuffer_fb); + GPU_framebuffer_clear_stencil(gbuffer_fb, 0x0); + } + + void bind_radiance(void) + { + /* Layer attachement also works with cubemap. */ + radiance_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer), + GPU_ATTACHMENT_TEXTURE(combined_tx), + GPU_ATTACHMENT_TEXTURE(diffuse_tx)); + GPU_framebuffer_bind(radiance_fb); + } + + void bind_volume(void) + { + /* Layer attachement also works with cubemap. */ + volume_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer), + GPU_ATTACHMENT_TEXTURE(volume_tx), + GPU_ATTACHMENT_TEXTURE(transparency_tx)); + GPU_framebuffer_bind(volume_fb); + } + + void bind_tracing(void) + { + /* Layer attachement also works with cubemap. */ + /* Attach depth_stencil buffer to only trace the surfaces that need it. */ + ray_data_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer), + GPU_ATTACHMENT_TEXTURE(ray_data_tx), + GPU_ATTACHMENT_TEXTURE(ray_radiance_tx)); + GPU_framebuffer_bind(ray_data_fb); + + float color[4] = {0.0f}; + GPU_framebuffer_clear_color(ray_data_fb, color); + } + + void bind_holdout(void) + { + holdout_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(holdout_tx)); + GPU_framebuffer_bind(holdout_fb); + } + + void copy_depth_behind(void) + { + depth_behind_fb.ensure(GPU_ATTACHMENT_TEXTURE(depth_behind_tx)); + GPU_framebuffer_bind(depth_behind_fb); + + GPU_framebuffer_blit(gbuffer_fb, 0, depth_behind_fb, 0, GPU_DEPTH_BIT); + } + + void clear_radiance(void) + { + radiance_clear_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(diffuse_tx)); + GPU_framebuffer_bind(radiance_clear_fb); + + float color[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + GPU_framebuffer_clear_color(radiance_clear_fb, color); + } + + void render_end(void) + { + transmit_color_tx.release_tmp(); + transmit_normal_tx.release_tmp(); + transmit_data_tx.release_tmp(); + reflect_color_tx.release_tmp(); + reflect_normal_tx.release_tmp(); + volume_tx.release_tmp(); + emission_tx.release_tmp(); + transparency_tx.release_tmp(); + holdout_tx.release_tmp(); + diffuse_tx.release_tmp(); + depth_behind_tx.release_tmp(); + ray_data_tx.release_tmp(); + ray_radiance_tx.release_tmp(); + ray_variance_tx.release_tmp(); + } +}; + +/** \} */ + +} // namespace blender::eevee
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/eevee_gpencil.cc b/source/blender/draw/engines/eevee/eevee_gpencil.cc new file mode 100644 index 00000000000..d5d34040c4f --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_gpencil.cc @@ -0,0 +1,155 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +#include "BKE_gpencil.h" +#include "BKE_object.h" +#include "DEG_depsgraph_query.h" +#include "DNA_gpencil_types.h" + +#include "eevee_instance.hh" + +namespace blender::eevee { + +#define DO_BATCHING true + +struct gpIterData { + Instance &inst; + Object *ob; + MaterialArray &material_array; + int cfra; + + /* Drawcall batching. */ + GPUBatch *geom = nullptr; + Material *material = nullptr; + int vfirst = 0; + int vcount = 0; + bool instancing = false; + + gpIterData(Instance &inst_, Object *ob_) + : inst(inst_), ob(ob_), material_array(inst_.materials.material_array_get(ob_)) + { + cfra = DEG_get_ctime(inst.depsgraph); + }; +}; + +static void gpencil_drawcall_flush(gpIterData &iter) +{ + if (iter.geom != NULL) { + shgroup_geometry_call(iter.material->shading.shgrp, + iter.ob, + iter.geom, + iter.vfirst, + iter.vcount, + iter.instancing); + shgroup_geometry_call(iter.material->prepass.shgrp, + iter.ob, + iter.geom, + iter.vfirst, + iter.vcount, + iter.instancing); + shgroup_geometry_call(iter.material->shadow.shgrp, + iter.ob, + iter.geom, + iter.vfirst, + iter.vcount, + iter.instancing); + } + iter.geom = NULL; + iter.vfirst = -1; + iter.vcount = 0; +} + +/* Group draw-calls that are consecutive and with the same type. Reduces GPU driver overhead. */ +static void gpencil_drawcall_add(gpIterData &iter, + GPUBatch *geom, + Material *material, + int v_first, + int v_count, + bool instancing) +{ + int last = iter.vfirst + iter.vcount; + /* Interrupt draw-call grouping if the sequence is not consecutive. */ + if (!DO_BATCHING || (geom != iter.geom) || (material != iter.material) || (v_first - last > 3)) { + gpencil_drawcall_flush(iter); + } + iter.geom = geom; + iter.material = material; + iter.instancing = instancing; + if (iter.vfirst == -1) { + iter.vfirst = v_first; + } + iter.vcount = v_first + v_count - iter.vfirst; +} + +static void gpencil_stroke_sync(bGPDlayer *UNUSED(gpl), + bGPDframe *UNUSED(gpf), + bGPDstroke *gps, + void *thunk) +{ + gpIterData &iter = *(gpIterData *)thunk; + + Material *material = iter.material_array.materials[gps->mat_nr]; + MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter.ob, gps->mat_nr + 1); + + bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0; + bool show_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0) || + (!DRW_state_is_image_render() && ((gps->flag & GP_STROKE_NOFILL) != 0)); + bool show_fill = (gps->tot_triangles > 0) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0); + + if (hide_material) { + return; + } + + if (show_fill) { + GPUBatch *geom = DRW_cache_gpencil_fills_get(iter.ob, iter.cfra); + int vfirst = gps->runtime.fill_start * 3; + int vcount = gps->tot_triangles * 3; + gpencil_drawcall_add(iter, geom, material, vfirst, vcount, false); + } + + if (show_stroke) { + GPUBatch *geom = DRW_cache_gpencil_strokes_get(iter.ob, iter.cfra); + /* Start one vert before to have gl_InstanceID > 0 (see shader). */ + int vfirst = gps->runtime.stroke_start - 1; + /* Include "potential" cyclic vertex and start adj vertex (see shader). */ + int vcount = gps->totpoints + 1 + 1; + gpencil_drawcall_add(iter, geom, material, vfirst, vcount, true); + } +} + +void Instance::gpencil_sync(Object *ob, ObjectHandle &ob_handle) +{ + gpIterData iter(*this, ob); + + BKE_gpencil_visible_stroke_iter((bGPdata *)ob->data, nullptr, gpencil_stroke_sync, &iter); + + gpencil_drawcall_flush(iter); + + /* TODO(fclem) Gpencil velocity. */ + // shading_passes.velocity.gpencil_add(ob, ob_handle); + + bool is_caster = true; /* TODO material.shadow.shgrp. */ + bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */ + shadows.sync_object(ob, ob_handle, is_caster, is_alpha_blend); +} + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_hair.cc b/source/blender/draw/engines/eevee/eevee_hair.cc new file mode 100644 index 00000000000..519be551fbb --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_hair.cc @@ -0,0 +1,74 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +#include "DNA_hair_types.h" +#include "DNA_modifier_types.h" +#include "DNA_particle_types.h" + +#include "eevee_instance.hh" + +namespace blender::eevee { + +static void shgroup_hair_call(MaterialPass &matpass, + Object *ob, + ParticleSystem *part_sys = nullptr, + ModifierData *modifier_data = nullptr) +{ + if (matpass.shgrp == nullptr) { + return; + } + DRW_shgroup_hair_create_sub(ob, part_sys, modifier_data, matpass.shgrp, matpass.gpumat); +} + +void Instance::hair_sync(Object *ob, ObjectHandle &ob_handle, ModifierData *modifier_data) +{ + int mat_nr = HAIR_MATERIAL_NR; + + ParticleSystem *part_sys = nullptr; + if (modifier_data != nullptr) { + part_sys = reinterpret_cast<ParticleSystemModifierData *>(modifier_data)->psys; + if (!DRW_object_is_visible_psys_in_active_context(ob, part_sys)) { + return; + } + ParticleSettings *part_settings = part_sys->part; + const int draw_as = (part_settings->draw_as == PART_DRAW_REND) ? part_settings->ren_as : + part_settings->draw_as; + if (draw_as != PART_DRAW_PATH) { + return; + } + mat_nr = part_settings->omat; + } + + Material &material = materials.material_get(ob, mat_nr - 1, MAT_GEOM_HAIR); + + shgroup_hair_call(material.shading, ob, part_sys, modifier_data); + shgroup_hair_call(material.prepass, ob, part_sys, modifier_data); + shgroup_hair_call(material.shadow, ob, part_sys, modifier_data); + /* TODO(fclem) Hair velocity. */ + // shading_passes.velocity.gpencil_add(ob, ob_handle); + + bool is_caster = material.shadow.shgrp != nullptr; + bool is_alpha_blend = material.is_alpha_blend_transparent; + shadows.sync_object(ob, ob_handle, is_caster, is_alpha_blend); +} + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_hizbuffer.cc b/source/blender/draw/engines/eevee/eevee_hizbuffer.cc new file mode 100644 index 00000000000..038773aecab --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_hizbuffer.cc @@ -0,0 +1,102 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * The Hierarchical-Z buffer is texture containing a copy of the depth buffer with mipmaps. + * Each mip contains the maximum depth of each 4 pixels on the upper level. + * The size of the texture is padded to avoid messing with the mipmap pixels alignments. + */ + +#include "eevee_instance.hh" + +#include "eevee_hizbuffer.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Hierarchical-Z buffer + * + * \{ */ + +void HiZBufferModule::sync(void) +{ + { + hiz_copy_ps_ = DRW_pass_create("HizCopy", DRW_STATE_WRITE_COLOR); + GPUShader *sh = inst_.shaders.static_shader_get(HIZ_COPY); + DRWShadingGroup *grp = DRW_shgroup_create(sh, hiz_copy_ps_); + DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + } + { + hiz_downsample_ps_ = DRW_pass_create("HizDownsample", DRW_STATE_WRITE_COLOR); + GPUShader *sh = inst_.shaders.static_shader_get(HIZ_DOWNSAMPLE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, hiz_downsample_ps_); + DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_); + DRW_shgroup_uniform_vec2(grp, "texel_size", texel_size_, 1); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + } +} + +void HiZBuffer::prepare(GPUTexture *depth_src_tx) +{ + int div = 1 << mip_count_; + vec2 extent_src(GPU_texture_width(depth_src_tx), GPU_texture_height(depth_src_tx)); + vec2 extent_hiz(divide_ceil_u(extent_src.x, div) * div, divide_ceil_u(extent_src.y, div) * div); + + inst_.hiz.data_.pixel_to_ndc = 2.0f / extent_src; + inst_.hiz.texel_size_ = 1.0f / extent_hiz; + inst_.hiz.data_.uv_scale = extent_src / extent_hiz; + + inst_.hiz.data_.push_update(); + + /* TODO/OPTI(fclem): Share it between similar views. + * Not possible right now because request_tmp does not support mipmaps. */ + hiz_tx_.ensure("HiZ", UNPACK2(extent_hiz), mip_count_, GPU_R32F); + hiz_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(hiz_tx_)); + + GPU_texture_mipmap_mode(hiz_tx_, true, false); +} + +void HiZBuffer::recursive_downsample(void *thunk, int UNUSED(lvl)) +{ + HiZBufferModule &hiz = *reinterpret_cast<HiZBufferModule *>(thunk); + hiz.texel_size_ *= 2.0f; + DRW_draw_pass(hiz.hiz_downsample_ps_); +} + +void HiZBuffer::update(GPUTexture *depth_src_tx) +{ + DRW_stats_group_start("Hiz"); + + inst_.hiz.texel_size_ = 1.0f / vec2(GPU_texture_width(hiz_tx_), GPU_texture_height(hiz_tx_)); + + inst_.hiz.input_depth_tx_ = depth_src_tx; + GPU_framebuffer_bind(hiz_fb_); + DRW_draw_pass(inst_.hiz.hiz_copy_ps_); + + inst_.hiz.input_depth_tx_ = hiz_tx_; + GPU_framebuffer_recursive_downsample(hiz_fb_, mip_count_, &recursive_downsample, &inst_.hiz); + + DRW_stats_group_end(); +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_hizbuffer.hh b/source/blender/draw/engines/eevee/eevee_hizbuffer.hh new file mode 100644 index 00000000000..80bd7be912c --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_hizbuffer.hh @@ -0,0 +1,103 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * The Hierarchical-Z buffer is texture containing a copy of the depth buffer with mipmaps. + * Each mip contains the maximum depth of each 4 pixels on the upper level. + * The size of the texture is padded to avoid messing with the mipmap pixels alignments. + */ + +#pragma once + +#include "DRW_render.h" + +#include "eevee_shader_shared.hh" + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name Hierarchical-Z buffer + * \{ */ + +class HiZBuffer { + private: + Instance &inst_; + /** Framebuffer use for recursive downsampling. */ + /* TODO(fclem) Remove this and use a compute shader instead. */ + Framebuffer hiz_fb_ = Framebuffer("DepthHiz"); + /** Max mip to downsample to. We ensure the hiz has enough padding to never + * have to compensate the mipmap alignments. */ + constexpr static int mip_count_ = 6; + /** TODO/OPTI(fclem): Share it between similar views. */ + Texture hiz_tx_; + + public: + HiZBuffer(Instance &inst) : inst_(inst){}; + + void prepare(GPUTexture *depth_src); + void update(GPUTexture *depth_src); + + GPUTexture *texture_get(void) const + { + return hiz_tx_; + } + + private: + static void recursive_downsample(void *thunk, int lvl); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Hierarchical-Z buffer Module + * \{ */ + +class HiZBufferModule { + friend HiZBuffer; + + private: + Instance &inst_; + + HiZDataBuf data_; + /** Copy input depth to hiz-buffer with border padding. */ + DRWPass *hiz_copy_ps_; + /** Downsample one mipmap level. */ + DRWPass *hiz_downsample_ps_; + /** References only. */ + GPUTexture *input_depth_tx_ = nullptr; + /** Pixel size of the render target during hiz downsampling. */ + vec2 texel_size_; + + public: + HiZBufferModule(Instance &inst) : inst_(inst){}; + + void sync(void); + + const GPUUniformBuf *ubo_get(void) const + { + return data_.ubo_get(); + } +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_id_map.cc b/source/blender/draw/engines/eevee/eevee_id_map.cc new file mode 100644 index 00000000000..ab701e382a1 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_id_map.cc @@ -0,0 +1,76 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Structures to identify unique data blocks. The keys are unique so we are able to + * match ids across frame updates. + */ + +#include "eevee_instance.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Draw Data + * + * \{ */ + +static void draw_data_init_cb(struct DrawData *dd) +{ + /* Object has just been created or was never evaluated by the engine. */ + dd->recalc = ID_RECALC_ALL; +} + +ObjectHandle &SyncModule::sync_object(Object *ob) +{ + DrawEngineType *owner = (DrawEngineType *)&DRW_engine_viewport_eevee_type; + struct DrawData *dd = DRW_drawdata_ensure( + (ID *)ob, owner, sizeof(eevee::ObjectHandle), draw_data_init_cb, nullptr); + ObjectHandle &eevee_dd = *reinterpret_cast<ObjectHandle *>(dd); + + if (eevee_dd.object_key.ob == nullptr) { + eevee_dd.object_key = ObjectKey(ob); + } + + const int recalc_flags = ID_RECALC_COPY_ON_WRITE | ID_RECALC_TRANSFORM | ID_RECALC_SHADING | + ID_RECALC_GEOMETRY; + if ((eevee_dd.recalc & recalc_flags) != 0) { + inst_.sampling.reset(); + } + + return eevee_dd; +} + +WorldHandle &SyncModule::sync_world(::World *world) +{ + DrawEngineType *owner = (DrawEngineType *)&DRW_engine_viewport_eevee_type; + struct DrawData *dd = DRW_drawdata_ensure( + (ID *)world, owner, sizeof(eevee::WorldHandle), draw_data_init_cb, nullptr); + WorldHandle &eevee_dd = *reinterpret_cast<WorldHandle *>(dd); + + const int recalc_flags = ID_RECALC_ALL; + if ((eevee_dd.recalc & recalc_flags) != 0) { + inst_.sampling.reset(); + } + return eevee_dd; +} + +/** \} */ +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_id_map.hh b/source/blender/draw/engines/eevee/eevee_id_map.hh new file mode 100644 index 00000000000..b8d1246c6bb --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_id_map.hh @@ -0,0 +1,284 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Structures to identify unique data blocks. The keys are unique so we are able to + * match ids across frame updates. + */ + +#pragma once + +#include "BKE_duplilist.h" +#include "BLI_ghash.h" +#include "BLI_map.hh" +#include "DNA_object_types.h" +#include "GPU_material.h" + +#include "eevee_engine.h" + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name ObjectKey + * + * \{ */ + +/** Unique key to identify each object in the hashmap. */ +struct ObjectKey { + /** Hash value of the key. */ + uint64_t hash_value; + /** Original Object or source object for duplis. */ + Object *ob; + /** Original Parent object for duplis. */ + Object *parent; + /** Dupli objects recursive unique identifier */ + int id[MAX_DUPLI_RECUR]; + /** If object uses particle system hair. */ + bool use_particle_hair; +#ifdef DEBUG + char name[64]; +#endif + ObjectKey() : ob(nullptr), parent(nullptr){}; + + ObjectKey(Object *ob_, Object *parent_, int id_[MAX_DUPLI_RECUR], bool use_particle_hair_) + : ob(ob_), parent(parent_), use_particle_hair(use_particle_hair_) + { + if (id_) { + memcpy(id, id_, sizeof(id)); + } + else { + memset(id, 0, sizeof(id)); + } + /* Compute hash on creation so we avoid the cost of it for every sync. */ + hash_value = BLI_ghashutil_ptrhash(ob); + hash_value = BLI_ghashutil_combine_hash(hash_value, BLI_ghashutil_ptrhash(parent)); + for (int i = 0; i < MAX_DUPLI_RECUR; i++) { + if (id[i] != 0) { + hash_value = BLI_ghashutil_combine_hash(hash_value, BLI_ghashutil_inthash(id[i])); + } + else { + break; + } + } +#ifdef DEBUG + STRNCPY(name, ob->id.name); +#endif + } + + ObjectKey(Object *ob, DupliObject *dupli, Object *parent) + : ObjectKey(ob, parent, dupli ? dupli->persistent_id : nullptr, false){}; + + ObjectKey(Object *ob) + : ObjectKey(ob, DRW_object_get_dupli(ob), DRW_object_get_dupli_parent(ob)){}; + + uint64_t hash(void) const + { + return hash_value; + } + + bool operator<(const ObjectKey &k) const + { + if (ob != k.ob) { + return (ob < k.ob); + } + if (parent != k.parent) { + return (parent < k.parent); + } + if (use_particle_hair != k.use_particle_hair) { + return (use_particle_hair < k.use_particle_hair); + } + return memcmp(id, k.id, sizeof(id)) < 0; + } + + bool operator==(const ObjectKey &k) const + { + if (ob != k.ob) { + return false; + } + if (parent != k.parent) { + return false; + } + if (use_particle_hair != k.use_particle_hair) { + return false; + } + return memcmp(id, k.id, sizeof(id)) == 0; + } +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw Data + * + * \{ */ + +struct ObjectHandle : public DrawData { + ObjectKey object_key; + + void reset_recalc_flag(void) + { + if (recalc != 0) { + recalc = 0; + } + } +}; + +struct WorldHandle : public DrawData { + void reset_recalc_flag(void) + { + if (recalc != 0) { + recalc = 0; + } + } +}; + +class SyncModule { + private: + Instance &inst_; + + public: + SyncModule(Instance &inst) : inst_(inst){}; + ~SyncModule(){}; + + ObjectHandle &sync_object(Object *ob); + WorldHandle &sync_world(::World *world); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name MaterialKey + * + * \{ */ + +enum eMaterialPipeline { + MAT_PIPE_DEFERRED = 0, + MAT_PIPE_FORWARD = 1, + MAT_PIPE_DEFERRED_PREPASS = 2, + MAT_PIPE_FORWARD_PREPASS = 3, + MAT_PIPE_VOLUME = 4, + MAT_PIPE_SHADOW = 5, +}; + +enum eMaterialGeometry { + MAT_GEOM_MESH = 0, + MAT_GEOM_HAIR = 1, + MAT_GEOM_GPENCIL = 2, + MAT_GEOM_VOLUME = 3, + MAT_GEOM_WORLD = 4, + MAT_GEOM_LOOKDEV = 5, +}; + +static inline void material_type_from_shader_uuid(uint64_t shader_uuid, + eMaterialPipeline &pipeline_type, + eMaterialGeometry &geometry_type) +{ + const uint64_t geometry_mask = ((1u << 3u) - 1u); + const uint64_t pipeline_mask = ((1u << 3u) - 1u); + geometry_type = static_cast<eMaterialGeometry>(shader_uuid & geometry_mask); + pipeline_type = static_cast<eMaterialPipeline>((shader_uuid >> 3u) & pipeline_mask); +} + +static inline uint64_t shader_uuid_from_material_type(eMaterialPipeline pipeline_type, + eMaterialGeometry geometry_type) +{ + return geometry_type | (pipeline_type << 3); +} + +static inline eMaterialGeometry to_material_geometry(const Object *ob) +{ + switch (ob->type) { + case OB_HAIR: + return MAT_GEOM_HAIR; + case OB_VOLUME: + return MAT_GEOM_VOLUME; + case OB_GPENCIL: + return MAT_GEOM_GPENCIL; + default: + return MAT_GEOM_MESH; + } +} + +/** Unique key to identify each material in the hashmap. */ +struct MaterialKey { + Material *mat; + uint64_t options; + + MaterialKey(::Material *mat_, eMaterialGeometry geometry, eMaterialPipeline surface_pipeline) + : mat(mat_) + { + options = shader_uuid_from_material_type(surface_pipeline, geometry); + } + + uint64_t hash(void) const + { + BLI_assert(options < sizeof(*mat)); + return (uint64_t)mat + options; + } + + bool operator<(const MaterialKey &k) const + { + return (mat < k.mat) || (options < k.options); + } + + bool operator==(const MaterialKey &k) const + { + return (mat == k.mat) && (options == k.options); + } +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name ShaderKey + * + * \{ */ + +struct ShaderKey { + GPUShader *shader; + uint64_t options; + + ShaderKey(GPUMaterial *gpumat, eMaterialGeometry geometry, eMaterialPipeline pipeline) + { + shader = GPU_material_get_shader(gpumat); + options = shader_uuid_from_material_type(pipeline, geometry); + } + + uint64_t hash(void) const + { + return (uint64_t)shader + options; + } + + bool operator<(const ShaderKey &k) const + { + return (shader < k.shader) || (options < k.options); + } + + bool operator==(const ShaderKey &k) const + { + return (shader == k.shader) && (options == k.options); + } +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_instance.cc b/source/blender/draw/engines/eevee/eevee_instance.cc new file mode 100644 index 00000000000..f614d4b4a97 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_instance.cc @@ -0,0 +1,316 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * An instance contains all structures needed to do a complete render. + */ + +#include "BKE_global.h" +#include "BKE_object.h" +#include "BLI_rect.h" +#include "DEG_depsgraph_query.h" +#include "DNA_ID.h" +#include "DNA_lightprobe_types.h" +#include "DNA_modifier_types.h" + +#include "eevee_instance.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Init + * + * Init funcions need to be called once at the start of a frame. + * Active camera, render extent and enabled render passes are immutable until next init. + * This takes care of resizing output buffers and view in case a parameter changed. + * IMPORTANT: xxx.init() functions are NOT meant to acquire and allocate DRW resources. + * Any attempt to do so will likely produce use after free situations. + * \{ */ + +void Instance::init(const ivec2 &output_res, + const rcti *output_rect, + RenderEngine *render_, + Depsgraph *depsgraph_, + const struct LightProbe *light_probe_, + Object *camera_object_, + const RenderLayer *render_layer_, + const DRWView *drw_view_, + const View3D *v3d_, + const RegionView3D *rv3d_) +{ + render = render_; + depsgraph = depsgraph_; + render_layer = render_layer_; + camera_orig_object = camera_object_; + drw_view = drw_view_; + v3d = v3d_; + rv3d = rv3d_; + baking_probe = light_probe_; + + debug_mode = (eDebugMode)G.debug_value; + + update_eval_members(); + + rcti render_border = output_crop(output_res, output_rect); + + /* Needs to be first. */ + sampling.init(scene); + + camera.init(); + motion_blur.init(); + render_passes.init(output_res, &render_border); + main_view.init(output_res); + velocity.init(); + shadows.init(); + lightprobes.init(); + lookdev.init(output_res, &render_border); +} + +rcti Instance::output_crop(const int res[2], const rcti *crop) +{ + rcti rect; + BLI_rcti_init(&rect, 0, res[0], 0, res[1]); + /* Clip the render border to region bounds. */ + BLI_rcti_isect(crop, &rect, &rect); + if (BLI_rcti_is_empty(&rect)) { + BLI_rcti_init(&rect, 0, res[0], 0, res[1]); + } + return rect; +} + +void Instance::set_time(float time) +{ + BLI_assert(render); + DRW_render_set_time(render, depsgraph, floorf(time), fractf(time)); + update_eval_members(); +} + +void Instance::update_eval_members(void) +{ + scene = DEG_get_evaluated_scene(depsgraph); + view_layer = DEG_get_evaluated_view_layer(depsgraph); + camera_eval_object = (camera_orig_object) ? + DEG_get_evaluated_object(depsgraph, camera_orig_object) : + nullptr; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sync + * + * Sync will gather data from the scene that can change over a time step (i.e: motion steps). + * IMPORTANT: xxx.sync() functions area responsible for creating DRW resources (i.e: DRWView) as + * well as querying temp texture pool. All DRWPasses should be ready by the end end_sync(). + * \{ */ + +void Instance::begin_sync() +{ + camera.sync(); + render_passes.sync(); + shading_passes.sync(); + main_view.sync(); + world.sync(); + raytracing.sync(); + hiz.sync(); + + lookdev.sync_background(); + lookdev.sync_overlay(); + + materials.begin_sync(); + velocity.begin_sync(); + lights.begin_sync(); + shadows.begin_sync(); + lightprobes.begin_sync(); +} + +void Instance::object_sync(Object *ob) +{ + const bool is_renderable_type = ELEM( + ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_LAMP, OB_VOLUME, OB_GPENCIL); + const int ob_visibility = DRW_object_visibility_in_active_context(ob); + const bool partsys_is_visible = (ob_visibility & OB_VISIBLE_PARTICLES) != 0 && + (ob->type == OB_MESH); + const bool object_is_visible = DRW_object_is_renderable(ob) && + (ob_visibility & OB_VISIBLE_SELF) != 0; + + if (!is_renderable_type || (!partsys_is_visible && !object_is_visible)) { + return; + } + + ObjectHandle &ob_handle = sync.sync_object(ob); + + if (partsys_is_visible && ob != DRW_context_state_get()->object_edit) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_ParticleSystem) { + hair_sync(ob, ob_handle, md); + } + } + } + + if (object_is_visible) { + switch (ob->type) { + case OB_LAMP: + lights.sync_light(ob, ob_handle); + break; + case OB_MESH: + case OB_CURVE: + case OB_SURF: + case OB_FONT: + case OB_MBALL: { + mesh_sync(ob, ob_handle); + break; + } + case OB_VOLUME: + shading_passes.deferred.volume_add(ob); + break; + case OB_HAIR: + hair_sync(ob, ob_handle); + break; + case OB_GPENCIL: + gpencil_sync(ob, ob_handle); + break; + default: + break; + } + } + + ob_handle.reset_recalc_flag(); +} + +/* Wrapper to use with DRW_render_object_iter. */ +void Instance::object_sync_render(void *instance_, + Object *ob, + RenderEngine *engine, + Depsgraph *depsgraph) +{ + UNUSED_VARS(engine, depsgraph); + + Instance &inst = *reinterpret_cast<Instance *>(instance_); + + if (inst.baking_probe != nullptr) { + if (inst.baking_probe->visibility_grp != nullptr) { + bool test = BKE_collection_has_object_recursive(inst.baking_probe->visibility_grp, ob); + test = (inst.baking_probe->flag & LIGHTPROBE_FLAG_INVERT_GROUP) ? !test : test; + if (!test) { + return; + } + } + /* Exclude planar lightprobes. */ + if (ob->type == OB_LIGHTPROBE) { + LightProbe *prb = (LightProbe *)ob->data; + if (prb->type == LIGHTPROBE_TYPE_PLANAR) { + return; + } + } + } + inst.object_sync(ob); +} + +void Instance::end_sync(void) +{ + velocity.end_sync(); + lights.end_sync(); + sampling.end_sync(); + render_passes.end_sync(); + lightprobes.end_sync(); + subsurface.end_sync(); +} + +void Instance::render_sync(void) +{ + DRW_cache_restart(); + + this->begin_sync(); + DRW_render_object_iter(this, render, depsgraph, object_sync_render); + this->end_sync(); + + DRW_render_instance_buffer_finish(); + /* Also we weed to have a correct fbo bound for DRW_hair_update */ + // GPU_framebuffer_bind(); + // DRW_hair_update(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Rendering + * \{ */ + +/** + * Conceptually renders one sample per pixel. + * Everything based on random sampling should be done here (i.e: DRWViews jitter) + **/ +void Instance::render_sample(void) +{ + if (sampling.finished()) { + return; + } + + /* Motion blur may need to do re-sync after a certain number of sample. */ + if (sampling.do_render_sync()) { + this->render_sync(); + } + + sampling.step(); + + /* TODO update shadowmaps, planars, etc... */ + // shadow_view_.render(); + + main_view.render(); + + motion_blur.step(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Interface + * \{ */ + +void Instance::render_frame(RenderLayer *render_layer, const char *view_name) +{ + while (!sampling.finished()) { + this->render_sample(); + /* TODO(fclem) print progression. */ + } + + render_passes.read_result(render_layer, view_name); +} + +void Instance::draw_viewport(DefaultFramebufferList *dfbl) +{ + this->render_sample(); + + render_passes.resolve_viewport(dfbl); + + if (!sampling.finished_viewport()) { + DRW_viewport_request_redraw(); + } +} + +bool Instance::finished(void) const +{ + return sampling.finished(); +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_instance.hh b/source/blender/draw/engines/eevee/eevee_instance.hh new file mode 100644 index 00000000000..3079f931231 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_instance.hh @@ -0,0 +1,190 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * An instance contains all structures needed to do a complete render. + */ + +#pragma once + +#include "BKE_object.h" +#include "DEG_depsgraph.h" +#include "DRW_render.h" + +#include "eevee_film.hh" +#include "eevee_hizbuffer.hh" +#include "eevee_id_map.hh" +#include "eevee_light.hh" +#include "eevee_lightprobe.hh" +#include "eevee_lookdev.hh" +#include "eevee_material.hh" +#include "eevee_motion_blur.hh" +#include "eevee_raytracing.hh" +#include "eevee_renderpasses.hh" +#include "eevee_sampling.hh" +#include "eevee_shader.hh" +#include "eevee_shading.hh" +#include "eevee_shadow.hh" +#include "eevee_subsurface.hh" +#include "eevee_view.hh" +#include "eevee_world.hh" + +#include "eevee_engine.h" + +namespace blender::eevee { + +/** + * \class Instance + * \brief A running instance of the engine. + */ +class Instance { + friend MotionBlur; + friend MotionBlurModule; + friend VelocityModule; + + public: + ShaderModule &shaders; + Sampling sampling; + RenderPasses render_passes; + ShadingPasses shading_passes; + MainView main_view; + Camera camera; + World world; + VelocityModule velocity; + MotionBlurModule motion_blur; + LightModule lights; + LightProbeModule lightprobes; + RaytracingModule raytracing; + HiZBufferModule hiz; + /* TODO(fclem) Move it to scene layer data. */ + ShadowModule shadows; + SubsurfaceModule subsurface; + SyncModule sync; + MaterialModule materials; + /** Lookdev own lightweight instance. May not be allocated. */ + LookDev lookdev; + + /** Input data. */ + Depsgraph *depsgraph; + /** Evaluated IDs. */ + Scene *scene; + ViewLayer *view_layer; + Object *camera_eval_object; + Object *camera_orig_object; + /** Only available when rendering for final render. */ + const RenderLayer *render_layer; + RenderEngine *render; + /** Only available when rendering for viewport. */ + const DRWView *drw_view; + const View3D *v3d; + const RegionView3D *rv3d; + + /** Can be null. Used to exclude objects during baking. */ + const struct LightProbe *baking_probe = nullptr; + + eDebugMode debug_mode = SHADOW_DEBUG_NONE; + + /* Info string displayed at the top of the render / viewport. */ + char info[64]; + + public: + Instance(ShaderModule &shared_shaders) + : shaders(shared_shaders), + render_passes(*this), + shading_passes(*this), + main_view(*this), + camera(*this), + world(*this), + velocity(*this), + motion_blur(*this), + lights(*this), + lightprobes(*this), + raytracing(*this), + hiz(*this), + shadows(*this), + subsurface(*this), + sync(*this), + materials(*this), + lookdev(*this){}; + ~Instance(){}; + + void init(const ivec2 &output_res, + const rcti *output_rect, + RenderEngine *render, + Depsgraph *depsgraph, + const struct LightProbe *light_probe_ = nullptr, + Object *camera_object = nullptr, + const RenderLayer *render_layer = nullptr, + const DRWView *drw_view = nullptr, + const View3D *v3d = nullptr, + const RegionView3D *rv3d = nullptr); + + void begin_sync(void); + void object_sync(Object *ob); + void end_sync(void); + + void render_sync(void); + void render_frame(RenderLayer *render_layer, const char *view_name); + + void draw_viewport(DefaultFramebufferList *dfbl); + + bool finished(void) const; + + bool is_viewport(void) + { + return !DRW_state_is_scene_render(); + } + + bool use_scene_light(void) const + { + return (!v3d) || + ((v3d->shading.type == OB_MATERIAL) && + (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS)) || + ((v3d->shading.type == OB_RENDER) && + (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER)); + } + + /* Do we light the scene using the HDRI setup in the viewport settings. */ + bool use_studio_light(void) const + { + return (v3d) && (((v3d->shading.type == OB_MATERIAL) && + ((v3d->shading.flag & V3D_SHADING_SCENE_WORLD) == 0)) || + ((v3d->shading.type == OB_RENDER) && + ((v3d->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER) == 0))); + } + + private: + void render_sample(void); + static void object_sync_render(void *instance_, + Object *ob, + RenderEngine *engine, + Depsgraph *depsgraph); + + void mesh_sync(Object *ob, ObjectHandle &ob_handle); + void gpencil_sync(Object *ob, ObjectHandle &ob_handle); + void hair_sync(Object *ob, ObjectHandle &ob_handle, ModifierData *modifier_data = nullptr); + + rcti output_crop(const int output_res[2], const rcti *crop); + + void set_time(float time); + void update_eval_members(void); +}; + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_light.cc b/source/blender/draw/engines/eevee/eevee_light.cc new file mode 100644 index 00000000000..bcc68d773f5 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_light.cc @@ -0,0 +1,482 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * The light module manages light data buffers and light culling system. + */ + +#include "eevee_instance.hh" + +#include "eevee_light.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name LightData + * \{ */ + +static eLightType to_light_type(short blender_light_type, short blender_area_type) +{ + switch (blender_light_type) { + default: + case LA_LOCAL: + return LIGHT_POINT; + case LA_SUN: + return LIGHT_SUN; + case LA_SPOT: + return LIGHT_SPOT; + case LA_AREA: + return ELEM(blender_area_type, LA_AREA_DISK, LA_AREA_ELLIPSE) ? LIGHT_ELLIPSE : LIGHT_RECT; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Light Object + * \{ */ + +void Light::sync(ShadowModule &shadows, const Object *ob, float threshold) +{ + const ::Light *la = (const ::Light *)ob->data; + float scale[3]; + + float max_power = max_fff(la->r, la->g, la->b) * fabsf(la->energy / 100.0f); + float surface_max_power = max_ff(la->diff_fac, la->spec_fac) * max_power; + float volume_max_power = la->volume_fac * max_power; + + float influence_radius_surface = attenuation_radius_get(la, threshold, surface_max_power); + float influence_radius_volume = attenuation_radius_get(la, threshold, volume_max_power); + + this->influence_radius_max = max_ff(influence_radius_surface, influence_radius_volume); + this->influence_radius_invsqr_surface = (influence_radius_surface > 1e-8f) ? + (1.0f / square_f(influence_radius_surface)) : + 0.0f; + this->influence_radius_invsqr_volume = (influence_radius_volume > 1e-8f) ? + (1.0f / square_f(influence_radius_volume)) : + 0.0f; + + this->color = vec3(&la->r) * la->energy; + normalize_m4_m4_ex(this->object_mat, ob->obmat, scale); + /* Make sure we have consistent handedness (in case of negatively scaled Z axis). */ + vec3 cross = math::cross(float3(this->_right), float3(this->_up)); + if (math::dot(cross, float3(this->_back)) < 0.0f) { + negate_v3(this->_up); + } + + shape_parameters_set(la, scale); + + float shape_power = shape_power_get(la); + float point_power = point_power_get(la); + this->diffuse_power = la->diff_fac * shape_power; + this->transmit_power = la->diff_fac * point_power; + this->specular_power = la->spec_fac * shape_power; + this->volume_power = la->volume_fac * point_power; + + eLightType new_type = to_light_type(la->type, la->area_shape); + if (this->type != new_type) { + shadow_discard_safe(shadows); + this->type = new_type; + } + + if (la->mode & LA_SHADOW) { + if (la->type == LA_SUN) { + if (this->shadow_id == LIGHT_NO_SHADOW) { + this->shadow_id = shadows.directionals.alloc(); + } + + ShadowDirectional &shadow = shadows.directionals[this->shadow_id]; + shadow.sync(this->object_mat, la->bias * 0.05f, 1.0f); + } + else { + float cone_aperture = DEG2RAD(360.0); + if (la->type == LA_SPOT) { + cone_aperture = min_ff(DEG2RAD(179.9), la->spotsize); + } + else if (la->type == LA_AREA) { + cone_aperture = DEG2RAD(179.9); + } + + if (this->shadow_id == LIGHT_NO_SHADOW) { + this->shadow_id = shadows.punctuals.alloc(); + } + + ShadowPunctual &shadow = shadows.punctuals[this->shadow_id]; + shadow.sync(this->type, + this->object_mat, + cone_aperture, + la->clipsta, + this->influence_radius_max, + la->bias * 0.05f); + } + } + else { + shadow_discard_safe(shadows); + } + + this->initialized = true; +} + +void Light::shadow_discard_safe(ShadowModule &shadows) +{ + if (shadow_id != LIGHT_NO_SHADOW) { + if (this->type != LIGHT_SUN) { + shadows.punctuals.free(shadow_id); + } + else { + shadows.directionals.free(shadow_id); + } + shadow_id = LIGHT_NO_SHADOW; + } +} + +/* Returns attenuation radius inversed & squared for easy bound checking inside the shader. */ +float Light::attenuation_radius_get(const ::Light *la, float light_threshold, float light_power) +{ + if (la->type == LA_SUN) { + return (light_power > 1e-5f) ? 1e16f : 0.0f; + } + + if (la->mode & LA_CUSTOM_ATTENUATION) { + return la->att_dist; + } + /* Compute the distance (using the inverse square law) + * at which the light power reaches the light_threshold. */ + /* TODO take area light scale into account. */ + return sqrtf(light_power / light_threshold); +} + +void Light::shape_parameters_set(const ::Light *la, const float scale[3]) +{ + if (la->type == LA_AREA) { + float area_size_y = (ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE)) ? la->area_sizey : + la->area_size; + _area_size_x = max_ff(0.003f, la->area_size * scale[0] * 0.5f); + _area_size_y = max_ff(0.003f, area_size_y * scale[1] * 0.5f); + /* For volume point lighting. */ + radius_squared = max_ff(0.001f, hypotf(_area_size_x, _area_size_y) * 0.5f); + radius_squared = square_f(radius_squared); + } + else { + if (la->type == LA_SPOT) { + /* Spot size & blend */ + spot_size_inv[0] = scale[2] / scale[0]; + spot_size_inv[1] = scale[2] / scale[1]; + float spot_size = cosf(la->spotsize * 0.5f); + float spot_blend = (1.0f - spot_size) * la->spotblend; + _spot_mul = 1.0f / max_ff(1e-8f, spot_blend); + _spot_bias = -spot_size * _spot_mul; + } + + if (la->type == LA_SUN) { + _area_size_x = max_ff(0.001f, tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f)); + _area_size_y = _area_size_x; + } + else { + _area_size_x = _area_size_y = max_ff(0.001f, la->area_size); + } + radius_squared = square_f(_area_size_x); + } +} + +float Light::shape_power_get(const ::Light *la) +{ + float power; + /* Make illumination power constant */ + if (la->type == LA_AREA) { + float area = _area_size_x * _area_size_y; + power = 1.0f / (area * 4.0f * float(M_PI)); + /* FIXME : Empirical, Fit cycles power */ + power *= 0.8f; + if (ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) { + /* Scale power to account for the lower area of the ellipse compared to the surrounding + * rectangle. */ + power *= 4.0f / M_PI; + } + } + else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) { + power = 1.0f / (4.0f * square_f(_radius) * float(M_PI * M_PI)); + } + else { /* LA_SUN */ + power = 1.0f / (square_f(_radius) * float(M_PI)); + /* Make illumination power closer to cycles for bigger radii. Cycles uses a cos^3 term that + * we cannot reproduce so we account for that by scaling the light power. This function is + * the result of a rough manual fitting. */ + /* Simplification of: + * power *= 1 + r²/2 */ + power += 1.0f / (2.0f * M_PI); + } + return power; +} + +float Light::point_power_get(const ::Light *la) +{ + /* Volume light is evaluated as point lights. Remove the shape power. */ + if (la->type == LA_AREA) { + /* Match cycles. Empirical fit... must correspond to some constant. */ + float power = 0.0792f * M_PI; + + /* This corrects for area light most representative point trick. The fit was found by + * reducing the average error compared to cycles. */ + float area = _area_size_x * _area_size_y; + float tmp = M_PI_2 / (M_PI_2 + sqrtf(area)); + /* Lerp between 1.0 and the limit (1 / pi). */ + power *= tmp + (1.0f - tmp) * M_1_PI; + + return power; + } + else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) { + /* Match cycles. Empirical fit... must correspond to some constant. */ + return 0.0792f; + } + else { /* LA_SUN */ + return 1.0f; + } +} + +void Light::debug_draw(void) +{ + const float color[4] = {0.8, 0.3, 0, 1}; + DRW_debug_sphere(_position, influence_radius_max, color); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name LightModule + * \{ */ + +void LightModule::begin_sync(void) +{ + /* In begin_sync so it can be aninated. */ + float light_threshold = max_ff(1e-16f, inst_.scene->eevee.light_threshold); + if (light_threshold != light_threshold_) { + light_threshold_ = light_threshold; + inst_.sampling.reset(); + } +} + +void LightModule::sync_light(const Object *ob, ObjectHandle &handle) +{ + Light &light = lights_.lookup_or_add_default(handle.object_key); + light.used = true; + if (handle.recalc != 0 || !light.initialized) { + light.sync(inst_.shadows, ob, light_threshold_); + } +} + +void LightModule::end_sync(void) +{ + Vector<ObjectKey, 0> deleted_keys; + + light_refs_.clear(); + + /* Detect light deletion. */ + culling_data.items_no_cull_count = 0; + for (auto item : lights_.items()) { + Light &light = item.value; + if (!light.used) { + deleted_keys.append(item.key); + light.shadow_discard_safe(inst_.shadows); + } + else { + light.used = false; + light_refs_.append(&light); + + if (light.type == LIGHT_SUN) { + culling_data.items_no_cull_count++; + } + } + } + + if (deleted_keys.size() > 0) { + inst_.sampling.reset(); + } + for (auto key : deleted_keys) { + lights_.remove(key); + } + + if (light_refs_.size() > CULLING_MAX_ITEM) { + /* TODO(fclem) Print error to user. */ + light_refs_.resize(CULLING_MAX_ITEM); + } + + batch_len_ = divide_ceil_u(max_ii(light_refs_.size(), 1), CULLING_BATCH_SIZE); + lights_data.resize(batch_len_ * CULLING_BATCH_SIZE); + culling_key_buf.resize(batch_len_ * CULLING_BATCH_SIZE); + culling_light_buf.resize(batch_len_ * CULLING_BATCH_SIZE); + culling_zbin_buf.resize(batch_len_ * CULLING_ZBIN_COUNT); + culling_data.items_count = light_refs_.size(); + culling_data.tile_word_len = divide_ceil_u(max_ii(culling_data.items_count, 1), 32); + + /* Call shadows.end_sync after light pruning to avoid packing deleted shadows. */ + inst_.shadows.end_sync(); + + int direc_idx = 0; + int punct_idx = culling_data.items_no_cull_count; + for (auto l_idx : light_refs_.index_range()) { + Light &light = *light_refs_[l_idx]; + int dst_idx = (light.type == LIGHT_SUN) ? direc_idx++ : punct_idx++; + lights_data[dst_idx] = light; + + if (light.shadow_id != LIGHT_NO_SHADOW) { + if (light.type == LIGHT_SUN) { + lights_data[dst_idx].shadow_data = this->inst_.shadows.directionals[light.shadow_id]; + } + else { + lights_data[dst_idx].shadow_data = this->inst_.shadows.punctuals[light.shadow_id]; + } + } + } + + lights_data.push_update(); + + { + culling_ps_ = DRW_pass_create("CullingLight", (DRWState)0); + + uint lights_len = light_refs_.size(); + uint batch_len = divide_ceil_u(lights_len, CULLING_BATCH_SIZE); + + if (batch_len > 0) { + /* NOTE: We reference the buffers that may be resized or updated later. */ + { + GPUShader *sh = inst_.shaders.static_shader_get(CULLING_SELECT); + DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_ps_); + DRW_shgroup_vertex_buffer(grp, "lights_buf", lights_data); + DRW_shgroup_vertex_buffer_ref(grp, "culling_buf", &culling_data); + DRW_shgroup_vertex_buffer(grp, "key_buf", culling_key_buf); + DRW_shgroup_call_compute(grp, batch_len, 1, 1); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE); + } + { + GPUShader *sh = inst_.shaders.static_shader_get(CULLING_SORT); + DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_ps_); + DRW_shgroup_vertex_buffer(grp, "lights_buf", lights_data); + DRW_shgroup_vertex_buffer_ref(grp, "culling_buf", &culling_data); + DRW_shgroup_vertex_buffer(grp, "key_buf", culling_key_buf); + DRW_shgroup_vertex_buffer_ref(grp, "out_zbins_buf", &culling_zbin_buf); + DRW_shgroup_vertex_buffer_ref(grp, "out_items_buf", &culling_light_buf); + DRW_shgroup_call_compute(grp, batch_len, 1, 1); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE); + } + { + GPUShader *sh = inst_.shaders.static_shader_get(CULLING_TILE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_ps_); + DRW_shgroup_vertex_buffer(grp, "lights_buf", culling_light_buf); + DRW_shgroup_vertex_buffer_ref(grp, "culling_buf", &culling_data); + DRW_shgroup_vertex_buffer_ref(grp, "culling_tile_buf", &culling_tile_buf); + DRW_shgroup_call_compute_ref(grp, culling_tile_dispatch_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); + } + } + } + + debug_end_sync(); +} + +void LightModule::debug_end_sync(void) +{ + if (inst_.debug_mode != eDebugMode::DEBUG_LIGHT_CULLING) { + debug_draw_ps_ = nullptr; + return; + } + + debug_draw_ps_ = DRW_pass_create("CullingDebug", DRW_STATE_WRITE_COLOR); + + GPUShader *sh = inst_.shaders.static_shader_get(CULLING_DEBUG); + DRWShadingGroup *grp = DRW_shgroup_create(sh, debug_draw_ps_); + DRW_shgroup_vertex_buffer_ref(grp, "lights_buf", &culling_light_buf); + DRW_shgroup_vertex_buffer_ref(grp, "lights_culling_buf", &culling_data); + DRW_shgroup_vertex_buffer_ref(grp, "lights_zbins_buf", &culling_zbin_buf); + DRW_shgroup_vertex_buffer_ref(grp, "lights_tile_buf", &culling_tile_buf); + DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); +} + +/* Compute acceleration structure for the given view. If extent is 0, bind no lights. */ +void LightModule::set_view(const DRWView *view, const ivec2 extent, bool enable_specular) +{ + const bool no_lights = (extent.x == 0); + + /* Target 1bit per pixel. */ + uint tile_size = 1u << log2_ceil_u(ceil(sqrtf(culling_data.tile_word_len * 32))); + + int3 tiles_extent; + tiles_extent.x = divide_ceil_u(extent.x, tile_size); + tiles_extent.y = divide_ceil_u(extent.y, tile_size); + tiles_extent.z = batch_len_; + + float far_z = DRW_view_far_distance_get(view); + float near_z = DRW_view_near_distance_get(view); + + culling_data.zbin_scale = -CULLING_ZBIN_COUNT / fabsf(far_z - near_z); + culling_data.zbin_bias = -near_z * culling_data.zbin_scale; + culling_data.tile_size = tile_size; + culling_data.tile_x_len = tiles_extent.x; + culling_data.tile_y_len = tiles_extent.y; + culling_data.tile_to_uv_fac = tile_size / float2(UNPACK2(extent)); + + culling_data.enable_specular = enable_specular; + culling_data.items_count = no_lights ? 0 : light_refs_.size(); + culling_data.visible_count = 0; + culling_data.push_update(); + + if (no_lights) { + return; + } + + uint word_count = tiles_extent.x * tiles_extent.y * tiles_extent.z * culling_data.tile_word_len; + + /* TODO(fclem) Only resize once per redraw. */ + culling_tile_buf.resize(word_count); + + culling_tile_dispatch_size_.x = divide_ceil_u(word_count, 1024); + culling_tile_dispatch_size_.y = 1; + culling_tile_dispatch_size_.z = 1; + + DRW_view_set_active(view); + DRW_draw_pass(culling_ps_); +} + +void LightModule::debug_draw(GPUFrameBuffer *view_fb, HiZBuffer &hiz) +{ + if (debug_draw_ps_ == nullptr) { + return; + } + input_depth_tx_ = hiz.texture_get(); + + GPU_framebuffer_bind(view_fb); + DRW_draw_pass(debug_draw_ps_); +} + +void LightModule::shgroup_resources(DRWShadingGroup *grp) +{ + DRW_shgroup_vertex_buffer_ref(grp, "lights_buf", &culling_light_buf); + DRW_shgroup_vertex_buffer_ref(grp, "lights_culling_buf", &culling_data); + DRW_shgroup_vertex_buffer_ref(grp, "lights_zbins_buf", &culling_zbin_buf); + DRW_shgroup_vertex_buffer_ref(grp, "lights_tile_buf", &culling_tile_buf); + + DRW_shgroup_uniform_texture(grp, "shadow_atlas_tx", inst_.shadows.atlas_tx_get()); + DRW_shgroup_uniform_texture(grp, "shadow_tilemaps_tx", inst_.shadows.tilemap_tx_get()); +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_light.hh b/source/blender/draw/engines/eevee/eevee_light.hh new file mode 100644 index 00000000000..254d9231eef --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_light.hh @@ -0,0 +1,140 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * The light module manages light data buffers and light culling system. + */ + +#pragma once + +#include "BLI_bitmap.h" +#include "BLI_vector.hh" +#include "DNA_light_types.h" + +#include "eevee_camera.hh" +#include "eevee_id_map.hh" +#include "eevee_sampling.hh" +#include "eevee_shader.hh" +#include "eevee_shader_shared.hh" +#include "eevee_shadow.hh" +#include "eevee_wrapper.hh" + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name Light Object + * \{ */ + +struct Light : public LightData { + public: + bool initialized = false; + bool used = false; + + public: + Light() + { + shadow_id = LIGHT_NO_SHADOW; + } + + void sync(ShadowModule &shadows, const Object *ob, float threshold); + + void shadow_discard_safe(ShadowModule &shadows); + + void debug_draw(void); + + private: + float attenuation_radius_get(const ::Light *la, float light_threshold, float light_power); + void shape_parameters_set(const ::Light *la, const float scale[3]); + float shape_power_get(const ::Light *la); + float point_power_get(const ::Light *la); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name LightModule + * \{ */ + +/** + * The light module manages light data buffers and light culling system. + */ +class LightModule { + friend ShadowModule; + + public: + /** Scene lights data. */ + LightDataBuf lights_data; + /** Shadow data. TODO(fclem): merge with lights_data. */ + ShadowDataBuf shadows_data; + /** Culling infos. */ + CullingDataBuf culling_data; + /** Key buffer containing only visible lights indices. */ + CullingKeyBuf culling_key_buf; + /** LightData buffer used for rendering. Ordered by the culling phase. */ + CullingLightBuf culling_light_buf; + /** Zbins containing min and max light index for each Z bin. */ + CullingZbinBuf culling_zbin_buf; + /** Bitmap of lights touching each tiles. Using one layer for each culling batch. */ + CullingTileBuf culling_tile_buf; + + private: + Instance &inst_; + + /** Map of light objects. This is used to track light deletion. */ + Map<ObjectKey, Light> lights_; + + Vector<Light *> light_refs_; + + /** Follows the principles of Tiled Culling + Z binning from: + * "Improved Culling for Tiled and Clustered Rendering" + * by Michal Drobot + * http://advances.realtimerendering.com/s2017/2017_Sig_Improved_Culling_final.pdf */ + DRWPass *culling_ps_ = nullptr; + int3 culling_tile_dispatch_size_ = int3(1); + /* Number of batches of lights that are separately processed. */ + int batch_len_ = 1; + + float light_threshold_; + + /** Debug Culling visualization. */ + DRWPass *debug_draw_ps_ = nullptr; + GPUTexture *input_depth_tx_ = nullptr; + + public: + LightModule(Instance &inst) : inst_(inst){}; + ~LightModule(){}; + + void begin_sync(void); + void sync_light(const Object *ob, ObjectHandle &handle); + void end_sync(void); + + void set_view(const DRWView *view, const ivec2 extent, bool enable_specular = true); + + void shgroup_resources(DRWShadingGroup *grp); + + void debug_end_sync(void); + void debug_draw(GPUFrameBuffer *view_fb, HiZBuffer &hiz); +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c deleted file mode 100644 index bcbe17fdabc..00000000000 --- a/source/blender/draw/engines/eevee/eevee_lightcache.c +++ /dev/null @@ -1,1533 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - * - * Eevee's indirect lighting cache. - */ - -#include "DRW_render.h" - -#include "BKE_global.h" - -#include "BLI_endian_switch.h" -#include "BLI_threads.h" - -#include "DEG_depsgraph_build.h" -#include "DEG_depsgraph_query.h" - -#include "BKE_object.h" - -#include "DNA_collection_types.h" -#include "DNA_lightprobe_types.h" - -#include "PIL_time.h" - -#include "eevee_lightcache.h" -#include "eevee_private.h" - -#include "GPU_capabilities.h" -#include "GPU_context.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "BLO_read_write.h" - -#include "wm_window.h" - -/* Rounded to nearest PowerOfTwo */ -#if defined(IRRADIANCE_SH_L2) -# define IRRADIANCE_SAMPLE_SIZE_X 4 /* 3 in reality */ -# define IRRADIANCE_SAMPLE_SIZE_Y 4 /* 3 in reality */ -#elif defined(IRRADIANCE_HL2) -# define IRRADIANCE_SAMPLE_SIZE_X 4 /* 3 in reality */ -# define IRRADIANCE_SAMPLE_SIZE_Y 2 -#endif - -#ifdef IRRADIANCE_SH_L2 -/* we need a signed format for Spherical Harmonics */ -# define IRRADIANCE_FORMAT GPU_RGBA16F -#else -# define IRRADIANCE_FORMAT GPU_RGBA8 -#endif - -/* OpenGL 3.3 core requirement, can be extended but it's already very big */ -#define IRRADIANCE_MAX_POOL_LAYER 256 -#define IRRADIANCE_MAX_POOL_SIZE 1024 -#define MAX_IRRADIANCE_SAMPLES \ - (IRRADIANCE_MAX_POOL_SIZE / IRRADIANCE_SAMPLE_SIZE_X) * \ - (IRRADIANCE_MAX_POOL_SIZE / IRRADIANCE_SAMPLE_SIZE_Y) - -/* TODO: should be replace by a more elegant alternative. */ -extern void DRW_opengl_context_enable(void); -extern void DRW_opengl_context_disable(void); - -extern void DRW_opengl_render_context_enable(void *re_gl_context); -extern void DRW_opengl_render_context_disable(void *re_gl_context); -extern void DRW_gpu_render_context_enable(void *re_gpu_context); -extern void DRW_gpu_render_context_disable(void *re_gpu_context); - -typedef struct EEVEE_LightBake { - Depsgraph *depsgraph; - ViewLayer *view_layer; - ViewLayer *view_layer_input; - LightCache *lcache; - Scene *scene; - struct Main *bmain; - EEVEE_ViewLayerData *sldata; - - /** Current probe being rendered. */ - LightProbe **probe; - /** Target cube color texture. */ - GPUTexture *rt_color; - /** Target cube depth texture. */ - GPUTexture *rt_depth; - /** Target cube frame-buffers. */ - GPUFrameBuffer *rt_fb[6]; - /** Storage frame-buffer. */ - GPUFrameBuffer *store_fb; - /** Cube render target resolution. */ - int rt_res; - - /* Shared */ - /** Target layer to store the data to. */ - int layer; - /** Sample count for the convolution. */ - float samples_ct, invsamples_ct; - /** Sampling bias during convolution step. */ - float lod_factor; - /** Max cube-map LOD to sample when convolving. */ - float lod_max; - /** Number of probes to render + world probe. */ - int cube_len, grid_len; - - /* Irradiance grid */ - /** Current probe being rendered (UBO data). */ - EEVEE_LightGrid *grid; - /** Target cube-map at MIP 0. */ - int irr_cube_res; - /** Size of the irradiance texture. */ - int irr_size[3]; - /** Total for all grids */ - int total_irr_samples; - /** Nth sample of the current grid being rendered. */ - int grid_sample; - /** Total number of samples for the current grid. */ - int grid_sample_len; - /** Nth grid in the cache being rendered. */ - int grid_curr; - /** The current light bounce being evaluated. */ - int bounce_curr, bounce_len; - /** Resolution of the Visibility shadow-map. */ - float vis_res; - /** Result of previous light bounce. */ - GPUTexture *grid_prev; - /** Pointer to the owner_id of the probe object. */ - LightProbe **grid_prb; - - /* Reflection probe */ - /** Current probe being rendered (UBO data). */ - EEVEE_LightProbe *cube; - /** Target cube-map at MIP 0. */ - int ref_cube_res; - /** Index of the current cube. */ - int cube_offset; - /** Pointer to the owner_id of the probe object. */ - LightProbe **cube_prb; - - /* Dummy Textures */ - struct GPUTexture *dummy_color, *dummy_depth; - struct GPUTexture *dummy_layer_color; - - int total, done; /* to compute progress */ - short *stop, *do_update; - float *progress; - - /** For only handling the resources. */ - bool resource_only; - bool own_resources; - /** If the light-cache was created for baking, it's first owned by the baker. */ - bool own_light_cache; - /** ms. delay the start of the baking to not slowdown interactions (TODO: remove). */ - int delay; - /** Scene frame to bake. */ - int frame; - - /** If running in parallel (in a separate thread), use this context. */ - void *gl_context, *gpu_context; - - ThreadMutex *mutex; -} EEVEE_LightBake; - -/* -------------------------------------------------------------------- */ -/** \name Light Cache - * \{ */ - -/* Return memory footprint in bytes. */ -static uint eevee_lightcache_memsize_get(LightCache *lcache) -{ - uint size = 0; - if (lcache->grid_tx.data) { - size += MEM_allocN_len(lcache->grid_tx.data); - } - if (lcache->cube_tx.data) { - size += MEM_allocN_len(lcache->cube_tx.data); - for (int mip = 0; mip < lcache->mips_len; mip++) { - size += MEM_allocN_len(lcache->cube_mips[mip].data); - } - } - return size; -} - -static bool eevee_lightcache_version_check(const LightCache *lcache) -{ - switch (lcache->type) { - case LIGHTCACHE_TYPE_STATIC: - return lcache->version == LIGHTCACHE_STATIC_VERSION; - default: - return false; - } -} - -static bool eevee_lightcache_can_be_saved(LightCache *lcache) -{ - if (lcache->grid_tx.data) { - if (MEM_allocN_len(lcache->grid_tx.data) >= INT_MAX) { - return false; - } - } - if (lcache->cube_tx.data) { - if (MEM_allocN_len(lcache->cube_tx.data) >= INT_MAX) { - return false; - } - } - return true; -} - -static int eevee_lightcache_irradiance_sample_count(LightCache *lcache) -{ - int total_irr_samples = 0; - - for (int i = 1; i < lcache->grid_len; i++) { - EEVEE_LightGrid *egrid = lcache->grid_data + i; - total_irr_samples += egrid->resolution[0] * egrid->resolution[1] * egrid->resolution[2]; - } - return total_irr_samples; -} - -void EEVEE_lightcache_info_update(SceneEEVEE *eevee) -{ - LightCache *lcache = eevee->light_cache_data; - - if (lcache != NULL) { - if (!eevee_lightcache_version_check(lcache)) { - BLI_strncpy(eevee->light_cache_info, - TIP_("Incompatible Light cache version, please bake again"), - sizeof(eevee->light_cache_info)); - return; - } - - if (lcache->cube_tx.tex_size[2] > GPU_max_texture_layers()) { - BLI_strncpy(eevee->light_cache_info, - TIP_("Error: Light cache is too big for the GPU to be loaded"), - sizeof(eevee->light_cache_info)); - return; - } - - if (lcache->flag & LIGHTCACHE_INVALID) { - BLI_strncpy(eevee->light_cache_info, - TIP_("Error: Light cache dimensions not supported by the GPU"), - sizeof(eevee->light_cache_info)); - return; - } - - if (lcache->flag & LIGHTCACHE_BAKING) { - BLI_strncpy( - eevee->light_cache_info, TIP_("Baking light cache"), sizeof(eevee->light_cache_info)); - return; - } - - if (!eevee_lightcache_can_be_saved(lcache)) { - BLI_strncpy(eevee->light_cache_info, - TIP_("Error: LightCache is too large and will not be saved to disk"), - sizeof(eevee->light_cache_info)); - return; - } - - char formatted_mem[15]; - BLI_str_format_byte_unit(formatted_mem, eevee_lightcache_memsize_get(lcache), false); - - int irr_samples = eevee_lightcache_irradiance_sample_count(lcache); - - BLI_snprintf(eevee->light_cache_info, - sizeof(eevee->light_cache_info), - TIP_("%d Ref. Cubemaps, %d Irr. Samples (%s in memory)"), - lcache->cube_len - 1, - irr_samples, - formatted_mem); - } - else { - BLI_strncpy(eevee->light_cache_info, - TIP_("No light cache in this scene"), - sizeof(eevee->light_cache_info)); - } -} - -static void irradiance_pool_size_get(int visibility_size, int total_samples, int r_size[3]) -{ - /* Compute how many irradiance samples we can store per visibility sample. */ - int irr_per_vis = (visibility_size / IRRADIANCE_SAMPLE_SIZE_X) * - (visibility_size / IRRADIANCE_SAMPLE_SIZE_Y); - - /* The irradiance itself take one layer, hence the +1 */ - int layer_ct = MIN2(irr_per_vis + 1, IRRADIANCE_MAX_POOL_LAYER); - - int texel_ct = (int)ceilf((float)total_samples / (float)(layer_ct - 1)); - r_size[0] = visibility_size * - max_ii(1, min_ii(texel_ct, (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); - r_size[1] = visibility_size * - max_ii(1, (texel_ct / (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); - r_size[2] = layer_ct; -} - -static bool EEVEE_lightcache_validate(const LightCache *light_cache, - const int cube_len, - const int cube_res, - const int grid_len, - const int irr_size[3]) -{ - if (light_cache == NULL) { - return false; - } - if (!eevee_lightcache_version_check(light_cache)) { - return false; - } - - if (!(light_cache->flag & LIGHTCACHE_INVALID)) { - /* See if we need the same amount of texture space. */ - if ((irr_size[0] == light_cache->grid_tx.tex_size[0]) && - (irr_size[1] == light_cache->grid_tx.tex_size[1]) && - (irr_size[2] == light_cache->grid_tx.tex_size[2]) && (grid_len == light_cache->grid_len)) { - int mip_len = log2_floor_u(cube_res) - MIN_CUBE_LOD_LEVEL; - if ((cube_res == light_cache->cube_tx.tex_size[0]) && - (cube_len == light_cache->cube_tx.tex_size[2] / 6) && - (cube_len == light_cache->cube_len) && (mip_len == light_cache->mips_len)) { - return true; - } - } - } - return false; -} - -LightCache *EEVEE_lightcache_create(const int grid_len, - const int cube_len, - const int cube_size, - const int vis_size, - const int irr_size[3]) -{ - LightCache *light_cache = MEM_callocN(sizeof(LightCache), "LightCache"); - - light_cache->version = LIGHTCACHE_STATIC_VERSION; - light_cache->type = LIGHTCACHE_TYPE_STATIC; - - light_cache->cube_data = MEM_callocN(sizeof(EEVEE_LightProbe) * cube_len, "EEVEE_LightProbe"); - light_cache->grid_data = MEM_callocN(sizeof(EEVEE_LightGrid) * grid_len, "EEVEE_LightGrid"); - - light_cache->grid_tx.tex = DRW_texture_create_2d_array( - irr_size[0], irr_size[1], irr_size[2], IRRADIANCE_FORMAT, DRW_TEX_FILTER, NULL); - light_cache->grid_tx.tex_size[0] = irr_size[0]; - light_cache->grid_tx.tex_size[1] = irr_size[1]; - light_cache->grid_tx.tex_size[2] = irr_size[2]; - - int mips_len = log2_floor_u(cube_size) - MIN_CUBE_LOD_LEVEL; - - /* Try to create a cubemap array. */ - DRWTextureFlag cube_texflag = DRW_TEX_FILTER | DRW_TEX_MIPMAP; - light_cache->cube_tx.tex = DRW_texture_create_cube_array( - cube_size, cube_len, GPU_R11F_G11F_B10F, cube_texflag, NULL); - if (light_cache->cube_tx.tex == NULL) { - /* Try fallback to 2D array. */ - light_cache->cube_tx.tex = DRW_texture_create_2d_array( - cube_size, cube_size, cube_len * 6, GPU_R11F_G11F_B10F, cube_texflag, NULL); - } - - light_cache->cube_tx.tex_size[0] = cube_size; - light_cache->cube_tx.tex_size[1] = cube_size; - light_cache->cube_tx.tex_size[2] = cube_len * 6; - - light_cache->mips_len = mips_len; - light_cache->vis_res = vis_size; - light_cache->ref_res = cube_size; - - light_cache->cube_mips = MEM_callocN(sizeof(LightCacheTexture) * light_cache->mips_len, - "LightCacheTexture"); - - if (light_cache->grid_tx.tex == NULL || light_cache->cube_tx.tex == NULL) { - /* We could not create the requested textures size. Stop baking and do not use the cache. */ - light_cache->flag = LIGHTCACHE_INVALID; - } - else { - light_cache->flag = LIGHTCACHE_UPDATE_WORLD | LIGHTCACHE_UPDATE_CUBE | LIGHTCACHE_UPDATE_GRID; - - for (int mip = 0; mip < light_cache->mips_len; mip++) { - GPU_texture_get_mipmap_size( - light_cache->cube_tx.tex, mip + 1, light_cache->cube_mips[mip].tex_size); - } - } - - return light_cache; -} - -static bool eevee_lightcache_static_load(LightCache *lcache) -{ - /* We use fallback if a texture is not setup and there is no data to restore it. */ - if ((!lcache->grid_tx.tex && !lcache->grid_tx.data) || !lcache->grid_data || - (!lcache->cube_tx.tex && !lcache->cube_tx.data) || !lcache->cube_data) { - return false; - } - /* If cache is too big for this GPU. */ - if (lcache->cube_tx.tex_size[2] > GPU_max_texture_layers()) { - return false; - } - - if (lcache->grid_tx.tex == NULL) { - lcache->grid_tx.tex = GPU_texture_create_2d_array( - "lightcache_irradiance", UNPACK3(lcache->grid_tx.tex_size), 1, IRRADIANCE_FORMAT, NULL); - GPU_texture_update(lcache->grid_tx.tex, GPU_DATA_UBYTE, lcache->grid_tx.data); - - if (lcache->grid_tx.tex == NULL) { - lcache->flag |= LIGHTCACHE_NOT_USABLE; - return false; - } - - GPU_texture_filter_mode(lcache->grid_tx.tex, true); - } - - if (lcache->cube_tx.tex == NULL) { - /* Try to create a cubemap array. */ - lcache->cube_tx.tex = GPU_texture_create_cube_array("lightcache_cubemaps", - lcache->cube_tx.tex_size[0], - lcache->cube_tx.tex_size[2] / 6, - lcache->mips_len + 1, - GPU_R11F_G11F_B10F, - NULL); - - if (lcache->cube_tx.tex == NULL) { - /* Try fallback to 2D array. */ - lcache->cube_tx.tex = GPU_texture_create_2d_array("lightcache_cubemaps_fallback", - UNPACK3(lcache->cube_tx.tex_size), - lcache->mips_len + 1, - GPU_R11F_G11F_B10F, - NULL); - } - - if (lcache->cube_tx.tex == NULL) { - lcache->flag |= LIGHTCACHE_NOT_USABLE; - return false; - } - - for (int mip = 0; mip <= lcache->mips_len; mip++) { - const void *data = (mip == 0) ? lcache->cube_tx.data : lcache->cube_mips[mip - 1].data; - GPU_texture_update_mipmap(lcache->cube_tx.tex, mip, GPU_DATA_10_11_11_REV, data); - } - GPU_texture_mipmap_mode(lcache->cube_tx.tex, true, true); - } - return true; -} - -bool EEVEE_lightcache_load(LightCache *lcache) -{ - if (lcache == NULL) { - return false; - } - - if (!eevee_lightcache_version_check(lcache)) { - return false; - } - - if (lcache->flag & (LIGHTCACHE_INVALID | LIGHTCACHE_NOT_USABLE)) { - return false; - } - - switch (lcache->type) { - case LIGHTCACHE_TYPE_STATIC: - return eevee_lightcache_static_load(lcache); - default: - return false; - } -} - -static void eevee_lightbake_readback_irradiance(LightCache *lcache) -{ - MEM_SAFE_FREE(lcache->grid_tx.data); - lcache->grid_tx.data = GPU_texture_read(lcache->grid_tx.tex, GPU_DATA_UBYTE, 0); - lcache->grid_tx.data_type = LIGHTCACHETEX_BYTE; - lcache->grid_tx.components = 4; -} - -static void eevee_lightbake_readback_reflections(LightCache *lcache) -{ - MEM_SAFE_FREE(lcache->cube_tx.data); - lcache->cube_tx.data = GPU_texture_read(lcache->cube_tx.tex, GPU_DATA_10_11_11_REV, 0); - lcache->cube_tx.data_type = LIGHTCACHETEX_UINT; - lcache->cube_tx.components = 1; - - for (int mip = 0; mip < lcache->mips_len; mip++) { - LightCacheTexture *cube_mip = lcache->cube_mips + mip; - MEM_SAFE_FREE(cube_mip->data); - GPU_texture_get_mipmap_size(lcache->cube_tx.tex, mip + 1, cube_mip->tex_size); - - cube_mip->data = GPU_texture_read(lcache->cube_tx.tex, GPU_DATA_10_11_11_REV, mip + 1); - cube_mip->data_type = LIGHTCACHETEX_UINT; - cube_mip->components = 1; - } -} - -void EEVEE_lightcache_free(LightCache *lcache) -{ - DRW_TEXTURE_FREE_SAFE(lcache->cube_tx.tex); - MEM_SAFE_FREE(lcache->cube_tx.data); - DRW_TEXTURE_FREE_SAFE(lcache->grid_tx.tex); - MEM_SAFE_FREE(lcache->grid_tx.data); - - if (lcache->cube_mips) { - for (int i = 0; i < lcache->mips_len; i++) { - MEM_SAFE_FREE(lcache->cube_mips[i].data); - } - MEM_SAFE_FREE(lcache->cube_mips); - } - - MEM_SAFE_FREE(lcache->cube_data); - MEM_SAFE_FREE(lcache->grid_data); - MEM_freeN(lcache); -} - -static void write_lightcache_texture(BlendWriter *writer, LightCacheTexture *tex) -{ - if (tex->data) { - size_t data_size = tex->components * tex->tex_size[0] * tex->tex_size[1] * tex->tex_size[2]; - if (tex->data_type == LIGHTCACHETEX_FLOAT) { - data_size *= sizeof(float); - } - else if (tex->data_type == LIGHTCACHETEX_UINT) { - data_size *= sizeof(uint); - } - - /* FIXME: We can't save more than what 32bit systems can handle. - * The solution would be to split the texture but it is too late for 2.90. (see T78529) */ - if (data_size < INT_MAX) { - BLO_write_raw(writer, data_size, tex->data); - } - } -} - -void EEVEE_lightcache_blend_write(BlendWriter *writer, LightCache *cache) -{ - write_lightcache_texture(writer, &cache->grid_tx); - write_lightcache_texture(writer, &cache->cube_tx); - - if (cache->cube_mips) { - BLO_write_struct_array(writer, LightCacheTexture, cache->mips_len, cache->cube_mips); - for (int i = 0; i < cache->mips_len; i++) { - write_lightcache_texture(writer, &cache->cube_mips[i]); - } - } - - BLO_write_struct_array(writer, LightGridCache, cache->grid_len, cache->grid_data); - BLO_write_struct_array(writer, LightProbeCache, cache->cube_len, cache->cube_data); -} - -static void direct_link_lightcache_texture(BlendDataReader *reader, LightCacheTexture *lctex) -{ - lctex->tex = NULL; - - if (lctex->data) { - BLO_read_data_address(reader, &lctex->data); - if (lctex->data && BLO_read_requires_endian_switch(reader)) { - int data_size = lctex->components * lctex->tex_size[0] * lctex->tex_size[1] * - lctex->tex_size[2]; - - if (lctex->data_type == LIGHTCACHETEX_FLOAT) { - BLI_endian_switch_float_array((float *)lctex->data, data_size * sizeof(float)); - } - else if (lctex->data_type == LIGHTCACHETEX_UINT) { - BLI_endian_switch_uint32_array((uint *)lctex->data, data_size * sizeof(uint)); - } - } - } - - if (lctex->data == NULL) { - zero_v3_int(lctex->tex_size); - } -} - -void EEVEE_lightcache_blend_read_data(BlendDataReader *reader, LightCache *cache) -{ - cache->flag &= ~LIGHTCACHE_NOT_USABLE; - direct_link_lightcache_texture(reader, &cache->cube_tx); - direct_link_lightcache_texture(reader, &cache->grid_tx); - - if (cache->cube_mips) { - BLO_read_data_address(reader, &cache->cube_mips); - for (int i = 0; i < cache->mips_len; i++) { - direct_link_lightcache_texture(reader, &cache->cube_mips[i]); - } - } - - BLO_read_data_address(reader, &cache->cube_data); - BLO_read_data_address(reader, &cache->grid_data); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Light Bake Context - * \{ */ - -static void eevee_lightbake_context_enable(EEVEE_LightBake *lbake) -{ - if (GPU_use_main_context_workaround() && !BLI_thread_is_main()) { - GPU_context_main_lock(); - DRW_opengl_context_enable(); - return; - } - - if (lbake->gl_context) { - DRW_opengl_render_context_enable(lbake->gl_context); - if (lbake->gpu_context == NULL) { - lbake->gpu_context = GPU_context_create(NULL); - } - DRW_gpu_render_context_enable(lbake->gpu_context); - } - else { - DRW_opengl_context_enable(); - } -} - -static void eevee_lightbake_context_disable(EEVEE_LightBake *lbake) -{ - if (GPU_use_main_context_workaround() && !BLI_thread_is_main()) { - DRW_opengl_context_disable(); - GPU_context_main_unlock(); - return; - } - - if (lbake->gl_context) { - DRW_gpu_render_context_disable(lbake->gpu_context); - DRW_opengl_render_context_disable(lbake->gl_context); - } - else { - DRW_opengl_context_disable(); - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Light Bake Job - * \{ */ - -static void eevee_lightbake_count_probes(EEVEE_LightBake *lbake) -{ - Depsgraph *depsgraph = lbake->depsgraph; - - /* At least one of each for the world */ - lbake->grid_len = lbake->cube_len = lbake->total_irr_samples = 1; - - DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob) { - const int ob_visibility = BKE_object_visibility(ob, DAG_EVAL_RENDER); - if ((ob_visibility & OB_VISIBLE_SELF) == 0) { - continue; - } - - if (ob->type == OB_LIGHTPROBE) { - LightProbe *prb = (LightProbe *)ob->data; - - if (prb->type == LIGHTPROBE_TYPE_GRID) { - lbake->total_irr_samples += prb->grid_resolution_x * prb->grid_resolution_y * - prb->grid_resolution_z; - lbake->grid_len++; - } - else if (prb->type == LIGHTPROBE_TYPE_CUBE && lbake->cube_len < EEVEE_PROBE_MAX) { - lbake->cube_len++; - } - } - } - DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END; -} - -static void eevee_lightbake_create_render_target(EEVEE_LightBake *lbake, int rt_res) -{ - lbake->rt_depth = DRW_texture_create_cube(rt_res, GPU_DEPTH_COMPONENT24, 0, NULL); - lbake->rt_color = DRW_texture_create_cube( - rt_res, GPU_RGBA16F, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL); - - for (int i = 0; i < 6; i++) { - GPU_framebuffer_ensure_config(&lbake->rt_fb[i], - {GPU_ATTACHMENT_TEXTURE_CUBEFACE(lbake->rt_depth, i), - GPU_ATTACHMENT_TEXTURE_CUBEFACE(lbake->rt_color, i)}); - } - - GPU_framebuffer_ensure_config(&lbake->store_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_NONE}); -} - -static void eevee_lightbake_create_resources(EEVEE_LightBake *lbake) -{ - Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph); - SceneEEVEE *eevee = &scene_eval->eevee; - - lbake->bounce_len = eevee->gi_diffuse_bounces; - lbake->vis_res = eevee->gi_visibility_resolution; - lbake->rt_res = eevee->gi_cubemap_resolution; - - irradiance_pool_size_get(lbake->vis_res, lbake->total_irr_samples, lbake->irr_size); - - lbake->ref_cube_res = lbake->rt_res; - lbake->cube_prb = MEM_callocN(sizeof(LightProbe *) * lbake->cube_len, "EEVEE Cube visgroup ptr"); - lbake->grid_prb = MEM_callocN(sizeof(LightProbe *) * lbake->grid_len, "EEVEE Grid visgroup ptr"); - - lbake->grid_prev = DRW_texture_create_2d_array(lbake->irr_size[0], - lbake->irr_size[1], - lbake->irr_size[2], - IRRADIANCE_FORMAT, - DRW_TEX_FILTER, - NULL); - - /* Ensure Light Cache is ready to accept new data. If not recreate one. - * WARNING: All the following must be threadsafe. It's currently protected - * by the DRW mutex. */ - lbake->lcache = eevee->light_cache_data; - - /* TODO: validate irradiance and reflection cache independently... */ - if (!EEVEE_lightcache_validate( - lbake->lcache, lbake->cube_len, lbake->ref_cube_res, lbake->grid_len, lbake->irr_size)) { - eevee->light_cache_data = lbake->lcache = NULL; - } - - if (lbake->lcache == NULL) { - lbake->lcache = EEVEE_lightcache_create( - lbake->grid_len, lbake->cube_len, lbake->ref_cube_res, lbake->vis_res, lbake->irr_size); - - lbake->own_light_cache = true; - - eevee->light_cache_data = lbake->lcache; - } - - EEVEE_lightcache_load(eevee->light_cache_data); - - lbake->lcache->flag |= LIGHTCACHE_BAKING; - lbake->lcache->cube_len = 1; -} - -wmJob *EEVEE_lightbake_job_create(struct wmWindowManager *wm, - struct wmWindow *win, - struct Main *bmain, - struct ViewLayer *view_layer, - struct Scene *scene, - int delay, - int frame) -{ - EEVEE_LightBake *lbake = NULL; - - /* only one render job at a time */ - if (WM_jobs_test(wm, scene, WM_JOB_TYPE_RENDER)) { - return NULL; - } - - wmJob *wm_job = WM_jobs_get(wm, - win, - scene, - "Bake Lighting", - WM_JOB_EXCL_RENDER | WM_JOB_PRIORITY | WM_JOB_PROGRESS, - WM_JOB_TYPE_LIGHT_BAKE); - - /* If job exists do not recreate context and depsgraph. */ - EEVEE_LightBake *old_lbake = (EEVEE_LightBake *)WM_jobs_customdata_get(wm_job); - - if (old_lbake && (old_lbake->view_layer_input == view_layer) && (old_lbake->bmain == bmain)) { - lbake = MEM_callocN(sizeof(EEVEE_LightBake), "EEVEE_LightBake"); - /* Cannot reuse depsgraph for now because we cannot get the update from the - * main database directly. TODO: reuse depsgraph and only update positions. */ - /* lbake->depsgraph = old_lbake->depsgraph; */ - lbake->depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER); - - lbake->mutex = BLI_mutex_alloc(); - - BLI_mutex_lock(old_lbake->mutex); - old_lbake->own_resources = false; - - lbake->scene = scene; - lbake->bmain = bmain; - lbake->view_layer_input = view_layer; - lbake->gl_context = old_lbake->gl_context; - lbake->own_resources = true; - lbake->delay = delay; - lbake->frame = frame; - - if (lbake->gl_context == NULL && !GPU_use_main_context_workaround()) { - lbake->gl_context = WM_opengl_context_create(); - wm_window_reset_drawable(); - } - - if (old_lbake->stop != NULL) { - *old_lbake->stop = 1; - } - BLI_mutex_unlock(old_lbake->mutex); - } - else { - lbake = EEVEE_lightbake_job_data_alloc(bmain, view_layer, scene, true, frame); - lbake->delay = delay; - } - - WM_jobs_customdata_set(wm_job, lbake, EEVEE_lightbake_job_data_free); - WM_jobs_timer(wm_job, 0.4, NC_SCENE | NA_EDITED, 0); - WM_jobs_callbacks( - wm_job, EEVEE_lightbake_job, NULL, EEVEE_lightbake_update, EEVEE_lightbake_update); - - G.is_break = false; - - return wm_job; -} - -void *EEVEE_lightbake_job_data_alloc(struct Main *bmain, - struct ViewLayer *view_layer, - struct Scene *scene, - bool run_as_job, - int frame) -{ - BLI_assert(BLI_thread_is_main()); - - EEVEE_LightBake *lbake = MEM_callocN(sizeof(EEVEE_LightBake), "EEVEE_LightBake"); - - lbake->depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER); - lbake->scene = scene; - lbake->bmain = bmain; - lbake->view_layer_input = view_layer; - lbake->own_resources = true; - lbake->own_light_cache = false; - lbake->mutex = BLI_mutex_alloc(); - lbake->frame = frame; - - if (run_as_job && !GPU_use_main_context_workaround()) { - lbake->gl_context = WM_opengl_context_create(); - wm_window_reset_drawable(); - } - - return lbake; -} - -void EEVEE_lightbake_job_data_free(void *custom_data) -{ - EEVEE_LightBake *lbake = (EEVEE_LightBake *)custom_data; - - /* TODO: reuse depsgraph. */ - /* if (lbake->own_resources) { */ - DEG_graph_free(lbake->depsgraph); - /* } */ - - MEM_SAFE_FREE(lbake->cube_prb); - MEM_SAFE_FREE(lbake->grid_prb); - - BLI_mutex_free(lbake->mutex); - - MEM_freeN(lbake); -} - -static void eevee_lightbake_delete_resources(EEVEE_LightBake *lbake) -{ - if (!lbake->resource_only) { - BLI_mutex_lock(lbake->mutex); - } - - if (lbake->gl_context) { - DRW_opengl_render_context_enable(lbake->gl_context); - DRW_gpu_render_context_enable(lbake->gpu_context); - } - else if (!lbake->resource_only) { - DRW_opengl_context_enable(); - } - - /* XXX Free the resources contained in the viewlayer data - * to be able to free the context before deleting the depsgraph. */ - if (lbake->sldata) { - EEVEE_view_layer_data_free(lbake->sldata); - } - - DRW_TEXTURE_FREE_SAFE(lbake->rt_depth); - DRW_TEXTURE_FREE_SAFE(lbake->rt_color); - DRW_TEXTURE_FREE_SAFE(lbake->grid_prev); - GPU_FRAMEBUFFER_FREE_SAFE(lbake->store_fb); - for (int i = 0; i < 6; i++) { - GPU_FRAMEBUFFER_FREE_SAFE(lbake->rt_fb[i]); - } - - if (lbake->gpu_context) { - DRW_gpu_render_context_disable(lbake->gpu_context); - DRW_gpu_render_context_enable(lbake->gpu_context); - GPU_context_discard(lbake->gpu_context); - } - - if (lbake->gl_context && lbake->own_resources) { - /* Delete the baking context. */ - DRW_opengl_render_context_disable(lbake->gl_context); - WM_opengl_context_dispose(lbake->gl_context); - lbake->gpu_context = NULL; - lbake->gl_context = NULL; - } - else if (lbake->gl_context) { - DRW_opengl_render_context_disable(lbake->gl_context); - } - else if (!lbake->resource_only) { - DRW_opengl_context_disable(); - } - - if (!lbake->resource_only) { - BLI_mutex_unlock(lbake->mutex); - } -} - -/* Cache as in draw cache not light cache. */ -static void eevee_lightbake_cache_create(EEVEE_Data *vedata, EEVEE_LightBake *lbake) -{ - EEVEE_TextureList *txl = vedata->txl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); - Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph); - lbake->sldata = sldata; - - /* Disable all effects BUT high bit-depth shadows. */ - scene_eval->eevee.flag &= SCE_EEVEE_SHADOW_HIGH_BITDEPTH; - scene_eval->eevee.taa_samples = 1; - scene_eval->eevee.gi_irradiance_smoothing = 0.0f; - - stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__); - stl->g_data->background_alpha = 1.0f; - stl->g_data->render_timesteps = 1; - - /* XXX TODO: remove this. This is in order to make the init functions work. */ - if (DRW_view_default_get() == NULL) { - float winmat[4][4], viewmat[4][4]; - unit_m4(viewmat); - unit_m4(winmat); - negate_v3(winmat[2]); - DRWView *view = DRW_view_create(viewmat, winmat, NULL, NULL, NULL); - DRW_view_default_set(view); - DRW_view_set_active(view); - } - - /* HACK: set txl->color but unset it before Draw Manager frees it. */ - txl->color = lbake->rt_color; - const int viewport_size[2] = { - GPU_texture_width(txl->color), - GPU_texture_height(txl->color), - }; - DRW_render_viewport_size_set(viewport_size); - - EEVEE_effects_init(sldata, vedata, NULL, true); - EEVEE_materials_init(sldata, vedata, stl, fbl); - EEVEE_shadows_init(sldata); - EEVEE_lightprobes_init(sldata, vedata); - - EEVEE_effects_cache_init(sldata, vedata); - EEVEE_materials_cache_init(sldata, vedata); - EEVEE_subsurface_cache_init(sldata, vedata); - EEVEE_volumes_cache_init(sldata, vedata); - EEVEE_lights_cache_init(sldata, vedata); - EEVEE_lightprobes_cache_init(sldata, vedata); - - EEVEE_lightbake_cache_init(sldata, vedata, lbake->rt_color, lbake->rt_depth); - - if (lbake->probe) { - EEVEE_LightProbesInfo *pinfo = sldata->probes; - LightProbe *prb = *lbake->probe; - pinfo->vis_data.collection = prb->visibility_grp; - pinfo->vis_data.invert = prb->flag & LIGHTPROBE_FLAG_INVERT_GROUP; - pinfo->vis_data.cached = false; - } - DRW_render_object_iter(vedata, NULL, lbake->depsgraph, EEVEE_render_cache); - - EEVEE_volumes_cache_finish(sldata, vedata); - EEVEE_materials_cache_finish(sldata, vedata); - EEVEE_lights_cache_finish(sldata, vedata); - EEVEE_lightprobes_cache_finish(sldata, vedata); - EEVEE_shadows_update(sldata, vedata); - - /* Disable volumetrics when baking. */ - stl->effects->enabled_effects &= ~EFFECT_VOLUMETRIC; - - EEVEE_subsurface_draw_init(sldata, vedata); - EEVEE_effects_draw_init(sldata, vedata); - EEVEE_volumes_draw_init(sldata, vedata); - - txl->color = NULL; - - DRW_render_instance_buffer_finish(); - DRW_hair_update(); -} - -static void eevee_lightbake_copy_irradiance(EEVEE_LightBake *lbake, LightCache *lcache) -{ - DRW_TEXTURE_FREE_SAFE(lbake->grid_prev); - - /* Copy texture by reading back and re-uploading it. */ - float *tex = GPU_texture_read(lcache->grid_tx.tex, GPU_DATA_FLOAT, 0); - lbake->grid_prev = DRW_texture_create_2d_array(lbake->irr_size[0], - lbake->irr_size[1], - lbake->irr_size[2], - IRRADIANCE_FORMAT, - DRW_TEX_FILTER, - tex); - - MEM_freeN(tex); -} - -static void eevee_lightbake_render_world_sample(void *ved, void *user_data) -{ - EEVEE_Data *vedata = (EEVEE_Data *)ved; - EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); - EEVEE_LightBake *lbake = (EEVEE_LightBake *)user_data; - Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph); - LightCache *lcache = scene_eval->eevee.light_cache_data; - float clamp = scene_eval->eevee.gi_glossy_clamp; - float filter_quality = scene_eval->eevee.gi_filter_quality; - - /* TODO: do this once for the whole bake when we have independent DRWManagers. */ - eevee_lightbake_cache_create(vedata, lbake); - - sldata->common_data.ray_type = EEVEE_RAY_GLOSSY; - sldata->common_data.ray_depth = 1; - GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); - EEVEE_lightbake_render_world(sldata, vedata, lbake->rt_fb); - EEVEE_lightbake_filter_glossy(sldata, - vedata, - lbake->rt_color, - lbake->store_fb, - 0, - 1.0f, - lcache->mips_len, - filter_quality, - clamp); - - sldata->common_data.ray_type = EEVEE_RAY_DIFFUSE; - sldata->common_data.ray_depth = 1; - GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); - EEVEE_lightbake_render_world(sldata, vedata, lbake->rt_fb); - EEVEE_lightbake_filter_diffuse(sldata, vedata, lbake->rt_color, lbake->store_fb, 0, 1.0f); - - if (lcache->flag & LIGHTCACHE_UPDATE_GRID) { - /* Clear the cache to avoid white values in the grid. */ - GPU_framebuffer_texture_attach(lbake->store_fb, lbake->grid_prev, 0, 0); - GPU_framebuffer_bind(lbake->store_fb); - /* Clear to 1.0f for visibility. */ - GPU_framebuffer_clear_color(lbake->store_fb, ((float[4]){1.0f, 1.0f, 1.0f, 1.0f})); - DRW_draw_pass(vedata->psl->probe_grid_fill); - - SWAP(GPUTexture *, lbake->grid_prev, lcache->grid_tx.tex); - - /* Make a copy for later. */ - eevee_lightbake_copy_irradiance(lbake, lcache); - } - - lcache->cube_len = 1; - lcache->grid_len = lbake->grid_len; - - lcache->flag |= LIGHTCACHE_CUBE_READY | LIGHTCACHE_GRID_READY; - lcache->flag &= ~LIGHTCACHE_UPDATE_WORLD; -} - -static void cell_id_to_grid_loc(EEVEE_LightGrid *egrid, int cell_idx, int r_local_cell[3]) -{ - /* Keep in sync with lightprobe_grid_display_vert */ - r_local_cell[2] = cell_idx % egrid->resolution[2]; - r_local_cell[1] = (cell_idx / egrid->resolution[2]) % egrid->resolution[1]; - r_local_cell[0] = cell_idx / (egrid->resolution[2] * egrid->resolution[1]); -} - -static void compute_cell_id(EEVEE_LightGrid *egrid, - LightProbe *probe, - int cell_idx, - int *r_final_idx, - int r_local_cell[3], - int *r_stride) -{ - const int cell_count = probe->grid_resolution_x * probe->grid_resolution_y * - probe->grid_resolution_z; - - /* Add one for level 0 */ - int max_lvl = (int)floorf(log2f( - (float)MAX3(probe->grid_resolution_x, probe->grid_resolution_y, probe->grid_resolution_z))); - - int visited_cells = 0; - *r_stride = 0; - *r_final_idx = 0; - r_local_cell[0] = r_local_cell[1] = r_local_cell[2] = 0; - for (int lvl = max_lvl; lvl >= 0; lvl--) { - *r_stride = 1 << lvl; - int prev_stride = *r_stride << 1; - for (int i = 0; i < cell_count; i++) { - *r_final_idx = i; - cell_id_to_grid_loc(egrid, *r_final_idx, r_local_cell); - if (((r_local_cell[0] % *r_stride) == 0) && ((r_local_cell[1] % *r_stride) == 0) && - ((r_local_cell[2] % *r_stride) == 0)) { - if (!(((r_local_cell[0] % prev_stride) == 0) && ((r_local_cell[1] % prev_stride) == 0) && - ((r_local_cell[2] % prev_stride) == 0)) || - ((i == 0) && (lvl == max_lvl))) { - if (visited_cells == cell_idx) { - return; - } - - visited_cells++; - } - } - } - } - - BLI_assert(0); -} - -static void grid_loc_to_world_loc(EEVEE_LightGrid *egrid, const int local_cell[3], float r_pos[3]) -{ - copy_v3_v3(r_pos, egrid->corner); - madd_v3_v3fl(r_pos, egrid->increment_x, local_cell[0]); - madd_v3_v3fl(r_pos, egrid->increment_y, local_cell[1]); - madd_v3_v3fl(r_pos, egrid->increment_z, local_cell[2]); -} - -static void eevee_lightbake_render_grid_sample(void *ved, void *user_data) -{ - EEVEE_Data *vedata = (EEVEE_Data *)ved; - EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - EEVEE_LightBake *lbake = (EEVEE_LightBake *)user_data; - EEVEE_LightGrid *egrid = lbake->grid; - LightProbe *prb = *lbake->probe; - Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph); - LightCache *lcache = scene_eval->eevee.light_cache_data; - int grid_loc[3], sample_id, sample_offset, stride; - float pos[3]; - const bool is_last_bounce_sample = ((egrid->offset + lbake->grid_sample) == - (lbake->total_irr_samples - 1)); - - /* No bias for rendering the probe. */ - egrid->level_bias = 1.0f; - - /* Use the previous bounce for rendering this bounce. */ - SWAP(GPUTexture *, lbake->grid_prev, lcache->grid_tx.tex); - - /* TODO: do this once for the whole bake when we have independent DRWManagers. - * Warning: Some of the things above require this. */ - eevee_lightbake_cache_create(vedata, lbake); - - /* Compute sample position */ - compute_cell_id(egrid, prb, lbake->grid_sample, &sample_id, grid_loc, &stride); - sample_offset = egrid->offset + sample_id; - - grid_loc_to_world_loc(egrid, grid_loc, pos); - - /* Disable specular lighting when rendering probes to avoid feedback loops (looks bad). */ - common_data->spec_toggle = false; - common_data->sss_toggle = false; - common_data->prb_num_planar = 0; - common_data->prb_num_render_cube = 0; - common_data->ray_type = EEVEE_RAY_DIFFUSE; - common_data->ray_depth = lbake->bounce_curr + 1; - if (lbake->bounce_curr == 0) { - common_data->prb_num_render_grid = 0; - } - GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); - - EEVEE_lightbake_render_scene(sldata, vedata, lbake->rt_fb, pos, prb->clipsta, prb->clipend); - - /* Restore before filtering. */ - SWAP(GPUTexture *, lbake->grid_prev, lcache->grid_tx.tex); - - EEVEE_lightbake_filter_diffuse( - sldata, vedata, lbake->rt_color, lbake->store_fb, sample_offset, prb->intensity); - - if (lbake->bounce_curr == 0) { - /* We only need to filter the visibility for the first bounce. */ - EEVEE_lightbake_filter_visibility(sldata, - vedata, - lbake->rt_depth, - lbake->store_fb, - sample_offset, - prb->clipsta, - prb->clipend, - egrid->visibility_range, - prb->vis_blur, - lbake->vis_res); - } - - /* Update level for progressive update. */ - if (is_last_bounce_sample) { - egrid->level_bias = 1.0f; - } - else if (lbake->bounce_curr == 0) { - egrid->level_bias = (float)(stride << 1); - } - - /* Only run this for the last sample of a bounce. */ - if (is_last_bounce_sample) { - eevee_lightbake_copy_irradiance(lbake, lcache); - } - - /* If it is the last sample grid sample (and last bounce). */ - if ((lbake->bounce_curr == lbake->bounce_len - 1) && (lbake->grid_curr == lbake->grid_len - 1) && - (lbake->grid_sample == lbake->grid_sample_len - 1)) { - lcache->flag &= ~LIGHTCACHE_UPDATE_GRID; - } -} - -static void eevee_lightbake_render_probe_sample(void *ved, void *user_data) -{ - EEVEE_Data *vedata = (EEVEE_Data *)ved; - EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - EEVEE_LightBake *lbake = (EEVEE_LightBake *)user_data; - Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph); - LightCache *lcache = scene_eval->eevee.light_cache_data; - EEVEE_LightProbe *eprobe = lbake->cube; - LightProbe *prb = *lbake->probe; - float clamp = scene_eval->eevee.gi_glossy_clamp; - float filter_quality = scene_eval->eevee.gi_filter_quality; - - /* TODO: do this once for the whole bake when we have independent DRWManagers. */ - eevee_lightbake_cache_create(vedata, lbake); - - /* Disable specular lighting when rendering probes to avoid feedback loops (looks bad). */ - common_data->spec_toggle = false; - common_data->sss_toggle = false; - common_data->prb_num_planar = 0; - common_data->prb_num_render_cube = 0; - common_data->ray_type = EEVEE_RAY_GLOSSY; - common_data->ray_depth = 1; - GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); - - EEVEE_lightbake_render_scene( - sldata, vedata, lbake->rt_fb, eprobe->position, prb->clipsta, prb->clipend); - EEVEE_lightbake_filter_glossy(sldata, - vedata, - lbake->rt_color, - lbake->store_fb, - lbake->cube_offset, - prb->intensity, - lcache->mips_len, - filter_quality, - clamp); - - lcache->cube_len += 1; - - /* If it's the last probe. */ - if (lbake->cube_offset == lbake->cube_len - 1) { - lcache->flag &= ~LIGHTCACHE_UPDATE_CUBE; - } -} - -static float eevee_lightbake_grid_influence_volume(EEVEE_LightGrid *grid) -{ - return mat4_to_scale(grid->mat); -} - -static float eevee_lightbake_cube_influence_volume(EEVEE_LightProbe *eprb) -{ - return mat4_to_scale(eprb->attenuationmat); -} - -static bool eevee_lightbake_grid_comp(EEVEE_LightGrid *grid_a, EEVEE_LightGrid *grid_b) -{ - float vol_a = eevee_lightbake_grid_influence_volume(grid_a); - float vol_b = eevee_lightbake_grid_influence_volume(grid_b); - return (vol_a < vol_b); -} - -static bool eevee_lightbake_cube_comp(EEVEE_LightProbe *prb_a, EEVEE_LightProbe *prb_b) -{ - float vol_a = eevee_lightbake_cube_influence_volume(prb_a); - float vol_b = eevee_lightbake_cube_influence_volume(prb_b); - return (vol_a < vol_b); -} - -#define SORT_PROBE(elems_type, prbs, elems, elems_len, comp_fn) \ - { \ - bool sorted = false; \ - while (!sorted) { \ - sorted = true; \ - for (int i = 0; i < (elems_len)-1; i++) { \ - if ((comp_fn)((elems) + i, (elems) + i + 1)) { \ - SWAP(elems_type, (elems)[i], (elems)[i + 1]); \ - SWAP(LightProbe *, (prbs)[i], (prbs)[i + 1]); \ - sorted = false; \ - } \ - } \ - } \ - } \ - ((void)0) - -static void eevee_lightbake_gather_probes(EEVEE_LightBake *lbake) -{ - Depsgraph *depsgraph = lbake->depsgraph; - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - LightCache *lcache = scene_eval->eevee.light_cache_data; - - /* At least one for the world */ - int grid_len = 1; - int cube_len = 1; - int total_irr_samples = 1; - - /* Convert all lightprobes to tight UBO data from all lightprobes in the scene. - * This allows a large number of probe to be precomputed (even dupli ones). */ - DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob) { - const int ob_visibility = BKE_object_visibility(ob, DAG_EVAL_RENDER); - if ((ob_visibility & OB_VISIBLE_SELF) == 0) { - continue; - } - - if (ob->type == OB_LIGHTPROBE) { - LightProbe *prb = (LightProbe *)ob->data; - - if (prb->type == LIGHTPROBE_TYPE_GRID) { - lbake->grid_prb[grid_len] = prb; - EEVEE_LightGrid *egrid = &lcache->grid_data[grid_len++]; - EEVEE_lightprobes_grid_data_from_object(ob, egrid, &total_irr_samples); - } - else if (prb->type == LIGHTPROBE_TYPE_CUBE && cube_len < EEVEE_PROBE_MAX) { - lbake->cube_prb[cube_len] = prb; - EEVEE_LightProbe *eprobe = &lcache->cube_data[cube_len++]; - EEVEE_lightprobes_cube_data_from_object(ob, eprobe); - } - } - } - DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END; - - SORT_PROBE(EEVEE_LightGrid, - lbake->grid_prb + 1, - lcache->grid_data + 1, - lbake->grid_len - 1, - eevee_lightbake_grid_comp); - SORT_PROBE(EEVEE_LightProbe, - lbake->cube_prb + 1, - lcache->cube_data + 1, - lbake->cube_len - 1, - eevee_lightbake_cube_comp); - - lbake->total = lbake->total_irr_samples * lbake->bounce_len + lbake->cube_len; - lbake->done = 0; -} - -void EEVEE_lightbake_update(void *custom_data) -{ - EEVEE_LightBake *lbake = (EEVEE_LightBake *)custom_data; - Scene *scene_orig = lbake->scene; - - /* If a new light-cache was created, free the old one and reference the new. */ - if (lbake->lcache && scene_orig->eevee.light_cache_data != lbake->lcache) { - if (scene_orig->eevee.light_cache_data != NULL) { - EEVEE_lightcache_free(scene_orig->eevee.light_cache_data); - } - scene_orig->eevee.light_cache_data = lbake->lcache; - lbake->own_light_cache = false; - } - - EEVEE_lightcache_info_update(&lbake->scene->eevee); - - DEG_id_tag_update(&scene_orig->id, ID_RECALC_COPY_ON_WRITE); -} - -static bool lightbake_do_sample(EEVEE_LightBake *lbake, - void (*render_callback)(void *ved, void *user_data)) -{ - if (G.is_break == true || *lbake->stop) { - return false; - } - - Depsgraph *depsgraph = lbake->depsgraph; - - /* TODO: make DRW manager instantiable (and only lock on drawing) */ - eevee_lightbake_context_enable(lbake); - DRW_custom_pipeline(&draw_engine_eevee_type, depsgraph, render_callback, lbake); - lbake->done += 1; - *lbake->progress = lbake->done / (float)lbake->total; - *lbake->do_update = 1; - eevee_lightbake_context_disable(lbake); - - return true; -} - -void EEVEE_lightbake_job(void *custom_data, short *stop, short *do_update, float *progress) -{ - EEVEE_LightBake *lbake = (EEVEE_LightBake *)custom_data; - Depsgraph *depsgraph = lbake->depsgraph; - - DEG_graph_relations_update(depsgraph); - DEG_evaluate_on_framechange(depsgraph, lbake->frame); - - lbake->view_layer = DEG_get_evaluated_view_layer(depsgraph); - lbake->stop = stop; - lbake->do_update = do_update; - lbake->progress = progress; - - if (G.background) { - /* Make sure to init GL capabilities before counting probes. */ - eevee_lightbake_context_enable(lbake); - eevee_lightbake_context_disable(lbake); - } - - /* Count lightprobes */ - eevee_lightbake_count_probes(lbake); - - /* We need to create the FBOs in the right context. - * We cannot do it in the main thread. */ - eevee_lightbake_context_enable(lbake); - eevee_lightbake_create_resources(lbake); - - /* Resource allocation can fail. Early exit in this case. */ - if (lbake->lcache->flag & LIGHTCACHE_INVALID) { - *lbake->stop = 1; - *lbake->do_update = 1; - lbake->lcache->flag &= ~LIGHTCACHE_BAKING; - eevee_lightbake_context_disable(lbake); - eevee_lightbake_delete_resources(lbake); - return; - } - - eevee_lightbake_create_render_target(lbake, lbake->rt_res); - eevee_lightbake_context_disable(lbake); - - /* Gather all probes data */ - eevee_lightbake_gather_probes(lbake); - - LightCache *lcache = lbake->lcache; - - /* HACK: Sleep to delay the first rendering operation - * that causes a small freeze (caused by VBO generation) - * because this step is locking at this moment. */ - /* TODO: remove this. */ - if (lbake->delay) { - PIL_sleep_ms(lbake->delay); - } - - /* Render world irradiance and reflection first */ - if (lcache->flag & LIGHTCACHE_UPDATE_WORLD) { - lbake->probe = NULL; - lightbake_do_sample(lbake, eevee_lightbake_render_world_sample); - } - - /* Render irradiance grids */ - if (lcache->flag & LIGHTCACHE_UPDATE_GRID) { - for (lbake->bounce_curr = 0; lbake->bounce_curr < lbake->bounce_len; lbake->bounce_curr++) { - /* Bypass world, start at 1. */ - lbake->probe = lbake->grid_prb + 1; - lbake->grid = lcache->grid_data + 1; - for (lbake->grid_curr = 1; lbake->grid_curr < lbake->grid_len; - lbake->grid_curr++, lbake->probe++, lbake->grid++) { - LightProbe *prb = *lbake->probe; - lbake->grid_sample_len = prb->grid_resolution_x * prb->grid_resolution_y * - prb->grid_resolution_z; - for (lbake->grid_sample = 0; lbake->grid_sample < lbake->grid_sample_len; - ++lbake->grid_sample) { - lightbake_do_sample(lbake, eevee_lightbake_render_grid_sample); - } - } - } - } - - /* Render reflections */ - if (lcache->flag & LIGHTCACHE_UPDATE_CUBE) { - /* Bypass world, start at 1. */ - lbake->probe = lbake->cube_prb + 1; - lbake->cube = lcache->cube_data + 1; - for (lbake->cube_offset = 1; lbake->cube_offset < lbake->cube_len; - lbake->cube_offset++, lbake->probe++, lbake->cube++) { - lightbake_do_sample(lbake, eevee_lightbake_render_probe_sample); - } - } - - /* Read the resulting lighting data to save it to file/disk. */ - eevee_lightbake_context_enable(lbake); - eevee_lightbake_readback_irradiance(lcache); - eevee_lightbake_readback_reflections(lcache); - eevee_lightbake_context_disable(lbake); - - lcache->flag |= LIGHTCACHE_BAKED; - lcache->flag &= ~LIGHTCACHE_BAKING; - - /* Assume that if lbake->gl_context is NULL - * we are not running in this in a job, so update - * the scene light-cache pointer before deleting it. */ - if (lbake->gl_context == NULL) { - BLI_assert(BLI_thread_is_main()); - EEVEE_lightbake_update(lbake); - } - - eevee_lightbake_delete_resources(lbake); - - /* Free GPU smoke textures and the smoke domain list correctly: See also T73921. */ - EEVEE_volumes_free_smoke_textures(); -} - -void EEVEE_lightbake_update_world_quick(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - const Scene *scene) -{ - LightCache *lcache = vedata->stl->g_data->light_cache; - float clamp = scene->eevee.gi_glossy_clamp; - float filter_quality = scene->eevee.gi_filter_quality; - - EEVEE_LightBake lbake = { - .resource_only = true, - }; - - /* Create resources. */ - eevee_lightbake_create_render_target(&lbake, scene->eevee.gi_cubemap_resolution); - - EEVEE_lightbake_cache_init(sldata, vedata, lbake.rt_color, lbake.rt_depth); - - sldata->common_data.ray_type = EEVEE_RAY_GLOSSY; - sldata->common_data.ray_depth = 1; - GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); - EEVEE_lightbake_render_world(sldata, vedata, lbake.rt_fb); - EEVEE_lightbake_filter_glossy(sldata, - vedata, - lbake.rt_color, - lbake.store_fb, - 0, - 1.0f, - lcache->mips_len, - filter_quality, - clamp); - - sldata->common_data.ray_type = EEVEE_RAY_DIFFUSE; - sldata->common_data.ray_depth = 1; - GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); - EEVEE_lightbake_render_world(sldata, vedata, lbake.rt_fb); - EEVEE_lightbake_filter_diffuse(sldata, vedata, lbake.rt_color, lbake.store_fb, 0, 1.0f); - - /* Don't hide grids if they are already rendered. */ - lcache->grid_len = max_ii(1, lcache->grid_len); - lcache->cube_len = 1; - - lcache->flag |= LIGHTCACHE_CUBE_READY | LIGHTCACHE_GRID_READY; - lcache->flag &= ~LIGHTCACHE_UPDATE_WORLD; - - eevee_lightbake_delete_resources(&lbake); -} - -/** \} */ diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.cc b/source/blender/draw/engines/eevee/eevee_lightcache.cc new file mode 100644 index 00000000000..8c6625e4515 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_lightcache.cc @@ -0,0 +1,1198 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2016, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + * + * Eevee's indirect lighting cache. + */ + +#include "DRW_render.h" + +#include "BKE_global.h" + +#include "BLI_endian_switch.h" +#include "BLI_span.hh" + +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +#include "BKE_object.h" + +#include "DNA_collection_types.h" +#include "DNA_lightprobe_types.h" + +#include "PIL_time.h" + +#include "eevee_instance.hh" +#include "eevee_lightcache.h" +#include "eevee_private.h" + +#include "GPU_capabilities.h" +#include "GPU_context.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "BLO_read_write.h" + +#include "wm_window.h" + +#include <mutex> + +extern "C" { +/* TODO should be replace by a more elegant alternative. */ +extern void DRW_opengl_context_enable(void); +extern void DRW_opengl_context_disable(void); + +extern void DRW_opengl_render_context_enable(void *re_gl_context); +extern void DRW_opengl_render_context_disable(void *re_gl_context); +extern void DRW_gpu_render_context_enable(void *re_gpu_context); +extern void DRW_gpu_render_context_disable(void *re_gpu_context); + +extern DrawEngineType draw_engine_eevee_type; +} + +/* -------------------------------------------------------------------- */ +/** \name Light Cache + * \{ */ + +namespace blender::eevee { + +LightCache::LightCache(const int cube_len_, + const int grid_len_, + const int cube_size, + const int vis_size, + const int irr_size[3]) +{ + memset(this, 0, sizeof(*this)); + + version = LIGHTCACHE_STATIC_VERSION; + type = LIGHTCACHE_TYPE_STATIC; + mips_len = log2_floor_u(cube_size) - min_cube_lod_level; + vis_res = vis_size; + ref_res = cube_size; + cube_len = cube_len_; + grid_len = grid_len_; + + cube_data = (LightProbeCache *)MEM_calloc_arrayN( + cube_len, sizeof(LightProbeCache), "LightProbeCache"); + grid_data = (LightGridCache *)MEM_calloc_arrayN( + grid_len, sizeof(LightGridCache), "LightGridCache"); + cube_mips = (LightCacheTexture *)MEM_calloc_arrayN( + mips_len, sizeof(LightCacheTexture), "LightCacheTexture"); + + grid_tx.tex_size[0] = irr_size[0]; + grid_tx.tex_size[1] = irr_size[1]; + grid_tx.tex_size[2] = irr_size[2]; + + cube_tx.tex_size[0] = ref_res; + cube_tx.tex_size[1] = ref_res; + cube_tx.tex_size[2] = cube_len * 6; + + create_reflection_texture(); + create_irradiance_texture(); + + if (flag & LIGHTCACHE_NOT_USABLE) { + /* We could not create the requested textures size. Stop baking and do not use the cache. */ + flag = LIGHTCACHE_INVALID; + return; + } + + flag = LIGHTCACHE_UPDATE_WORLD | LIGHTCACHE_UPDATE_CUBE | LIGHTCACHE_UPDATE_GRID; + + for (int mip = 0; mip < mips_len; mip++) { + GPU_texture_get_mipmap_size(cube_tx.tex, mip + 1, cube_mips[mip].tex_size); + } +} + +LightCache::~LightCache() +{ + DRW_TEXTURE_FREE_SAFE(cube_tx.tex); + MEM_SAFE_FREE(cube_tx.data); + DRW_TEXTURE_FREE_SAFE(grid_tx.tex); + MEM_SAFE_FREE(grid_tx.data); + + if (cube_mips) { + for (int i = 0; i < mips_len; i++) { + MEM_SAFE_FREE(cube_mips[i].data); + } + MEM_SAFE_FREE(cube_mips); + } + + MEM_SAFE_FREE(cube_data); + MEM_SAFE_FREE(grid_data); +} + +int LightCache::irradiance_cells_per_row_get(void) const +{ + /* Ambient cube is 3x2px. */ + return grid_tx.tex_size[0] / 3; +} + +/** + * Returns dimensions of the irradiance cache texture. + **/ +void LightCache::irradiance_cache_size_get(int visibility_size, int total_samples, int r_size[3]) +{ + /* Compute how many irradiance samples we can store per visibility sample. */ + int irr_per_vis = (visibility_size / irradiance_sample_size_x) * + (visibility_size / irradiance_sample_size_y); + + /* The irradiance itself take one layer, hence the +1 */ + int layer_ct = min_ii(irr_per_vis + 1, irradiance_max_pool_layer); + + int texel_ct = (int)ceilf((float)total_samples / (float)(layer_ct - 1)); + r_size[0] = visibility_size * + max_ii(1, min_ii(texel_ct, (irradiance_max_pool_size / visibility_size))); + r_size[1] = visibility_size * + max_ii(1, (texel_ct / (irradiance_max_pool_size / visibility_size))); + r_size[2] = layer_ct; +} + +bool LightCache::validate(const int cube_len, + const int cube_res, + const int grid_len, + const int irr_size[3]) const +{ + if (!version_check()) { + return false; + } + if ((flag & (LIGHTCACHE_INVALID | LIGHTCACHE_NOT_USABLE)) != 0) { + return false; + } + /* See if we need the same amount of texture space. */ + if ((ivec3(irr_size) == ivec3(grid_tx.tex_size)) && (grid_len == this->grid_len)) { + int mip_len = log2_floor_u(cube_res) - min_cube_lod_level; + if ((cube_res == cube_tx.tex_size[0]) && (cube_len == cube_tx.tex_size[2] / 6) && + (cube_len == this->cube_len) && (mip_len == this->mips_len)) { + return true; + } + } + return false; +} + +/** + * Returns true if the lightcache can be loaded correctly with this version of eevee. + **/ +bool LightCache::version_check() const +{ + switch (type) { + case LIGHTCACHE_TYPE_STATIC: + return version == LIGHTCACHE_STATIC_VERSION; + default: + return false; + } +} + +/** + * Creates empty texture for reflection data. + * Returns false on failure and set lightcache as unusable. + **/ +bool LightCache::create_reflection_texture(void) +{ + /* Try to create a cubemap array. */ + cube_tx.tex = GPU_texture_create_cube_array("lightcache_cubemaps", + cube_tx.tex_size[0], + cube_tx.tex_size[2] / 6, + mips_len + 1, + reflection_format, + nullptr); + + if (cube_tx.tex == nullptr) { + /* Try fallback to 2D array. */ + cube_tx.tex = GPU_texture_create_2d_array("lightcache_cubemaps_fallback", + UNPACK3(cube_tx.tex_size), + mips_len + 1, + reflection_format, + nullptr); + } + + if (cube_tx.tex != nullptr) { + GPU_texture_mipmap_mode(cube_tx.tex, true, true); + /* TODO(fclem) This fixes incomplete texture. Fix the GPU module instead. */ + GPU_texture_generate_mipmap(cube_tx.tex); + } + else { + flag |= LIGHTCACHE_NOT_USABLE; + } + return cube_tx.tex != nullptr; +} + +/** + * Creates empty texture for irradiance data. + * Returns false on failure and set lightcache as unusable. + **/ +bool LightCache::create_irradiance_texture(void) +{ + grid_tx.tex = GPU_texture_create_2d_array( + "lightcache_irradiance", UNPACK3(grid_tx.tex_size), 1, irradiance_format, nullptr); + if (grid_tx.tex != nullptr) { + GPU_texture_filter_mode(grid_tx.tex, true); + } + else { + flag |= LIGHTCACHE_NOT_USABLE; + } + return grid_tx.tex != nullptr; +} + +/** + * Loads a static lightcache data into GPU memory. + **/ +bool LightCache::load_static(void) +{ + /* True during baking. */ + if (grid_len == 0 || cube_len == 0) { + return false; + } + /* We use fallback if a texture is not setup and there is no data to restore it. */ + if ((!grid_tx.tex && !grid_tx.data) || !grid_data || (!cube_tx.tex && !cube_tx.data) || + !cube_data) { + return false; + } + /* If cache is too big for this GPU. */ + if (cube_tx.tex_size[2] > GPU_max_texture_layers()) { + return false; + } + + if (grid_tx.tex == nullptr) { + if (create_irradiance_texture()) { + GPU_texture_update(grid_tx.tex, GPU_DATA_UBYTE, grid_tx.data); + } + /* TODO(fclem) Move to do_version. */ + for (LightGridCache &grid : MutableSpan<LightGridCache>(grid_data, grid_len)) { + grid.is_ready = 1; + } + } + + if (cube_tx.tex == nullptr) { + if (create_reflection_texture()) { + for (int mip = 0; mip <= mips_len; mip++) { + const void *data = (mip == 0) ? cube_tx.data : cube_mips[mip - 1].data; + GPU_texture_update_mipmap(cube_tx.tex, mip, GPU_DATA_10_11_11_REV, data); + } + } + /* TODO(fclem) Move to do_version. */ + for (LightProbeCache &cube : MutableSpan<LightProbeCache>(cube_data, cube_len)) { + cube.is_ready = 1; + } + } + return true; +} + +bool LightCache::load(void) +{ + if (!version_check()) { + return false; + } + switch (type) { + case LIGHTCACHE_TYPE_STATIC: + return load_static(); + default: + return false; + } +} + +void LightCache::readback_irradiance(void) +{ + MEM_SAFE_FREE(grid_tx.data); + grid_tx.data = (char *)GPU_texture_read(grid_tx.tex, GPU_DATA_UBYTE, 0); + grid_tx.data_type = LIGHTCACHETEX_BYTE; + grid_tx.components = 4; +} + +void LightCache::readback_reflections(void) +{ + MEM_SAFE_FREE(cube_tx.data); + cube_tx.data = (char *)GPU_texture_read(cube_tx.tex, GPU_DATA_10_11_11_REV, 0); + cube_tx.data_type = LIGHTCACHETEX_UINT; + cube_tx.components = 1; + + for (int mip = 0; mip < mips_len; mip++) { + LightCacheTexture &cube_mip = cube_mips[mip]; + MEM_SAFE_FREE(cube_mip.data); + GPU_texture_get_mipmap_size(cube_tx.tex, mip + 1, cube_mip.tex_size); + + cube_mip.data = (char *)GPU_texture_read(cube_tx.tex, GPU_DATA_10_11_11_REV, mip + 1); + cube_mip.data_type = LIGHTCACHETEX_UINT; + cube_mip.components = 1; + } +} + +/* Return memory footprint in bytes. */ +uint LightCache::memsize_get(void) const +{ + uint size = 0; + if (grid_tx.data) { + size += MEM_allocN_len(grid_tx.data); + } + if (cube_tx.data) { + size += MEM_allocN_len(cube_tx.data); + for (int mip = 0; mip < mips_len; mip++) { + size += MEM_allocN_len(cube_mips[mip].data); + } + } + return size; +} + +bool LightCache::can_be_saved(void) const +{ + if (grid_tx.data) { + if (MEM_allocN_len(grid_tx.data) >= INT_MAX) { + return false; + } + } + if (cube_tx.data) { + if (MEM_allocN_len(cube_tx.data) >= INT_MAX) { + return false; + } + } + return true; +} + +int64_t LightCache::irradiance_sample_count(void) const +{ + int64_t total_irr_samples = 0; + for (const LightGridCache &grid : Span(&grid_data[1], grid_len - 1)) { + total_irr_samples += grid.resolution[0] * grid.resolution[1] * grid.resolution[2]; + } + return total_irr_samples; +} + +void LightCache::update_info(SceneEEVEE *eevee) +{ + LightCache *lcache = reinterpret_cast<LightCache *>(eevee->light_cache_data); + + if (lcache == nullptr) { + BLI_strncpy(eevee->light_cache_info, + TIP_("No light cache in this scene"), + sizeof(eevee->light_cache_info)); + return; + } + + if (lcache->version_check() == false) { + BLI_strncpy(eevee->light_cache_info, + TIP_("Incompatible Light cache version, please bake again"), + sizeof(eevee->light_cache_info)); + return; + } + + if (lcache->cube_tx.tex_size[2] > GPU_max_texture_layers()) { + BLI_strncpy(eevee->light_cache_info, + TIP_("Error: Light cache is too big for the GPU to be loaded"), + sizeof(eevee->light_cache_info)); + return; + } + + if (lcache->flag & LIGHTCACHE_INVALID) { + BLI_strncpy(eevee->light_cache_info, + TIP_("Error: Light cache dimensions not supported by the GPU"), + sizeof(eevee->light_cache_info)); + return; + } + + if (lcache->flag & LIGHTCACHE_BAKING) { + BLI_strncpy( + eevee->light_cache_info, TIP_("Baking light cache"), sizeof(eevee->light_cache_info)); + return; + } + + if (lcache->can_be_saved() == false) { + BLI_strncpy(eevee->light_cache_info, + TIP_("Error: LightCache is too large and will not be saved to disk"), + sizeof(eevee->light_cache_info)); + return; + } + + char formatted_mem[15]; + BLI_str_format_byte_unit(formatted_mem, lcache->memsize_get(), false); + + BLI_snprintf(eevee->light_cache_info, + sizeof(eevee->light_cache_info), + TIP_("%d Ref. Cubemaps, %lld Irr. Samples (%s in memory)"), + lcache->cube_len - 1, + lcache->irradiance_sample_count(), + formatted_mem); +} + +} // namespace blender::eevee + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Light Bake Job + * \{ */ + +namespace blender::eevee { + +class LightBake { + private: + Depsgraph *depsgraph_; + ViewLayer *view_layer_ = nullptr; + ViewLayer *view_layer_input_; + LightCache *lcache_ = nullptr; + Scene *scene_; + struct Main *bmain_; + /** True if this object owns the gl_context_. */ + bool own_resources_ = true; + /** If the light-cache was created for baking, it's first owned by the baker. */ + bool own_light_cache_ = false; + /** Scene frame to bake. */ + int frame_ = 0; + /** ms. delay the start of the baking to not slowdown interactions (TODO remove) */ + int delay_ = 5; + /** If running in parallel (in a separate thread), use this context. */ + void *gl_context_ = nullptr; + GPUContext *gpu_context_ = nullptr; + + /** Instance used for baking. */ + Instance *inst_ = nullptr; + /** Total for all grids. */ + int irradiance_samples_count_; + /** Number of grid and cube to bake. Data is inside the lightcache. */ + int grid_len_ = 0; + int cube_len_ = 0; + + /* Copy of probes data for rendering. We could make a lighter copy if needed. */ + Vector<LightProbe> cubes_probe_; + Vector<LightProbe> grids_probe_; + + /* To compute progress. */ + int64_t total_ = 0, done_ = 0; + /* Progress bar ratio to update. Only for async bake. */ + float *progress_ = nullptr; + /* Signal to stop baking. Only for async bake. */ + short *stop_ = nullptr; + /* Signal to update scene lightcache. Only for async bake. */ + short *do_update_ = nullptr; + + std::mutex mutex_; + + struct GridRenderData { + LightBake &bake; + LightGridCache *grid; + int64_t sample_index; + int bounce; + + GridRenderData(LightBake &bake_) : bake(bake_){}; + + void render(void) + { + SceneEEVEE &sce_eevee = DEG_get_evaluated_scene(bake.depsgraph_)->eevee; + LightProbe *probe = (grid->probe_index > -1) ? &bake.grids_probe_[grid->probe_index] : + nullptr; + + /* Swap cache on first grid of each bounce. */ + if (bounce > 0 && grid->offset == 0) { + bake.inst_->lightprobes.swap_irradiance_cache(); + } + + ivec3 cell_co = grid_cell_index_to_coordinate(sample_index, grid->resolution); + vec3 position = vec3(grid->corner) + vec3(grid->increment_x) * cell_co.x + + vec3(grid->increment_y) * cell_co.y + vec3(grid->increment_z) * cell_co.z; + + bake.inst_->lightprobes.bake(bake.depsgraph_, + LIGHTPROBE_TYPE_GRID, + grid->offset + sample_index, + bounce, + position, + probe, + grid->visibility_range); + + /* TODO incremental LVL update. */ + if (sample_index + 1 == (grid->resolution[0] * grid->resolution[1] * grid->resolution[2])) { + grid->is_ready = 1; + } + /* If it's the last grid of the last bounce, tag lighting as updated. */ + if ((grid->offset + sample_index == bake.irradiance_samples_count_ - 1) && + (bounce == sce_eevee.gi_diffuse_bounces - 1)) { + bake.lcache_->flag &= ~LIGHTCACHE_UPDATE_GRID; + } + } + + static void callback(void *UNUSED(ved), void *user_data) + { + reinterpret_cast<GridRenderData *>(user_data)->render(); + } + }; + + struct CubemapRenderData { + LightBake &bake; + LightProbeCache *cube; + int64_t cube_index_; + + CubemapRenderData(LightBake &bake_) : bake(bake_){}; + + void render(void) + { + LightProbe *probe = (cube->probe_index > -1) ? &bake.cubes_probe_[cube->probe_index] : + nullptr; + bake.inst_->lightprobes.bake( + bake.depsgraph_, LIGHTPROBE_TYPE_CUBE, cube_index_, 0, cube->position, probe); + cube->is_ready = 1; + + /* If it's the last probe, tag lighting as updated. */ + if (cube_index_ == bake.cube_len_ - 1) { + bake.lcache_->flag &= ~LIGHTCACHE_UPDATE_CUBE; + } + } + + static void callback(void *UNUSED(ved), void *user_data) + { + reinterpret_cast<CubemapRenderData *>(user_data)->render(); + } + }; + + public: + /* Interupting an existing bake job and reusing its resources if old_bake is not null. + * Otherwise just create a new bake context. */ + LightBake(struct Main *bmain, + struct ViewLayer *view_layer, + struct Scene *scene, + bool run_as_job, + int frame, + int delay, + LightBake *old_bake = nullptr) + : /* Cannot reuse depsgraph for now because we cannot get the update from the + * main database directly. TODO reuse depsgraph and only update positions. */ + depsgraph_(DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER)), + view_layer_input_(view_layer), + scene_(scene), + bmain_(bmain), + frame_(frame), + delay_(delay) + { + if (old_bake && (old_bake->view_layer_input_ == view_layer) && (old_bake->bmain_ == bmain)) { + { + /* Steal gl_context. */ + std::lock_guard<std::mutex> lock(old_bake->mutex_); + old_bake->own_resources_ = false; + gl_context_ = old_bake->gl_context_; + old_bake->stop(); + } + + if (gl_context_ == nullptr && !GPU_use_main_context_workaround()) { + gl_context_ = WM_opengl_context_create(); + wm_window_reset_drawable(); + } + } + else { + if (run_as_job && !GPU_use_main_context_workaround()) { + gl_context_ = WM_opengl_context_create(); + wm_window_reset_drawable(); + } + } + BLI_assert(BLI_thread_is_main()); + } + + ~LightBake() + { + /* TODO reuse depsgraph. */ + /* if (own_resources_) { */ + DEG_graph_free(depsgraph_); + /* } */ + } + + void update_scene_cache(void) + { + /* If a new light-cache was created, free the old one and reference the new. */ + if (lcache_ && scene_->eevee.light_cache_data != lcache_) { + if (scene_->eevee.light_cache_data != NULL) { + EEVEE_lightcache_free(scene_->eevee.light_cache_data); + } + scene_->eevee.light_cache_data = lcache_; + own_light_cache_ = false; + } + lcache_->update_info(&scene_->eevee); + /* Tag to flush the pointer update to eval scenes. */ + DEG_id_tag_update(&scene_->id, ID_RECALC_COPY_ON_WRITE); + } + + void do_bake(short *stop, short *do_update, float *progress) + { + stop_ = stop; + do_update_ = do_update; + progress_ = progress; + + DEG_graph_relations_update(depsgraph_); + DEG_evaluate_on_framechange(depsgraph_, frame_); + + view_layer_ = DEG_get_evaluated_view_layer(depsgraph_); + + context_enable(); + create_resources(); + + /* Resource allocation can fail. Early exit in this case. */ + if (lcache_->flag & LIGHTCACHE_INVALID) { + lcache_->flag &= ~LIGHTCACHE_BAKING; + this->stop(); + context_disable(); + delete_resources(); + return; + } + + context_disable(); + + /* HACK: Sleep to delay the first rendering operation + * that causes a small freeze (caused by VBO generation) + * because this step is locking at this moment. */ + /* TODO remove this. */ + if (delay_) { + PIL_sleep_ms(delay_); + } + + SceneEEVEE &sce_eevee = DEG_get_evaluated_scene(depsgraph_)->eevee; + + /* Render world reflections first. Needed for realtime bake preview. */ + if (lcache_->flag & LIGHTCACHE_UPDATE_CUBE) { + CubemapRenderData cb_data(*this); + cb_data.cube = &lcache_->cube_data[0]; + cb_data.cube_index_ = 0; + lightbake_do_sample(CubemapRenderData::callback, &cb_data); + } + /* Render irradiance grids. */ + if (lcache_->flag & LIGHTCACHE_UPDATE_GRID) { + for (int bounce : IndexRange(sce_eevee.gi_diffuse_bounces)) { + for (LightGridCache &grid : MutableSpan(lcache_->grid_data, grid_len_)) { + int64_t grid_sample_len = grid.resolution[0] * grid.resolution[1] * grid.resolution[2]; + for (auto sample_index : IndexRange(grid_sample_len)) { + GridRenderData cb_data(*this); + cb_data.grid = &grid; + cb_data.sample_index = sample_index; + cb_data.bounce = bounce; + lightbake_do_sample(GridRenderData::callback, &cb_data); + } + } + } + } + /* Render reflections. */ + if (lcache_->flag & LIGHTCACHE_UPDATE_CUBE) { + for (auto cube_index : IndexRange(1, cube_len_ - 1)) { + CubemapRenderData cb_data(*this); + cb_data.cube = &lcache_->cube_data[cube_index]; + cb_data.cube_index_ = cube_index; + lightbake_do_sample(CubemapRenderData::callback, &cb_data); + } + } + /* Read the resulting lighting data to save it to file/disk. */ + context_enable(); + lcache_->readback_irradiance(); + lcache_->readback_reflections(); + context_disable(); + + lcache_->flag |= LIGHTCACHE_BAKED; + lcache_->flag &= ~LIGHTCACHE_BAKING; + + /* Assume that if lbake->gl_context is NULL + * we are not running in this in a job, so update + * the scene light-cache pointer before deleting it. */ + if (gl_context_ == nullptr) { + BLI_assert(BLI_thread_is_main()); + update_scene_cache(); + } + + delete_resources(); + + /* Free GPU smoke textures and the smoke domain list correctly: See also + * T73921.*/ + /* TODO(fclem) is this still needed? */ + // EEVEE_volumes_free_smoke_textures(); + + stop_ = nullptr; + do_update_ = nullptr; + progress_ = nullptr; + } + + private: + bool lightbake_do_sample(void (*render_callback)(void *ved, void *user_data), void *user_data) + { + if (G.is_break == true || *stop_) { + return false; + } + /* TODO: make DRW manager instantiable (and only lock on drawing) */ + context_enable(); + DRW_custom_pipeline(&draw_engine_eevee_type, depsgraph_, render_callback, user_data); + done_ += 1; + *progress_ = done_ / (float)total_; + *do_update_ = 1; + context_disable(); + return true; + } + + LightGridCache grid_cache_from_object(Object *ob, int probe_index, int64_t &offset) + { + LightProbe *probe = (LightProbe *)ob->data; + + LightGridCache grid; + copy_v3_v3_int(grid.resolution, &probe->grid_resolution_x); + + /* Save current offset and set it for the next grid. */ + grid.offset = offset; + offset += grid.resolution[0] * grid.resolution[1] * grid.resolution[2]; + + /* Add one for level 0 */ + float fac = 1.0f / max_ff(1e-8f, probe->falloff); + grid.attenuation_scale = fac / max_ff(1e-8f, probe->distinf); + grid.attenuation_bias = fac; + + /* Update transforms */ + vec3 half_cell_dim = vec3(1.0f) / vec3(UNPACK3(grid.resolution)); + vec3 cell_dim = half_cell_dim * 2.0f; + + /* Matrix converting world space to cell ranges. */ + invert_m4_m4(grid.mat, ob->obmat); + + float4x4 obmat(ob->obmat); + + /* First cell. */ + vec3 corner = obmat * (half_cell_dim - vec3(1.0f)); + copy_v3_v3(grid.corner, corner); + + /* Opposite neighbor cell. */ + vec3 increment_x = (obmat * vec3(cell_dim.x, 0.0f, 0.0f)) - vec3(obmat.values[3]); + copy_v3_v3(grid.increment_x, increment_x); + vec3 increment_y = (obmat * vec3(0.0f, cell_dim.y, 0.0f)) - vec3(obmat.values[3]); + copy_v3_v3(grid.increment_y, increment_y); + vec3 increment_z = (obmat * vec3(0.0f, 0.0f, cell_dim.z)) - vec3(obmat.values[3]); + copy_v3_v3(grid.increment_z, increment_z); + + grid.probe_index = probe_index; + grid.is_ready = 0; + /* Update level for progressive update. TODO(fclem) port back. */ + grid.level_bias = 1.0f; + + grid.visibility_bias = 0.05f * probe->vis_bias; + grid.visibility_bleed = probe->vis_bleedbias; + grid.visibility_range = 1.0f + sqrtf(max_fff(len_squared_v3(grid.increment_x), + len_squared_v3(grid.increment_y), + len_squared_v3(grid.increment_z))); + return grid; + } + + LightProbeCache cube_cache_from_object(Object *ob, int probe_index) + { + LightProbe *probe = (LightProbe *)ob->data; + + LightProbeCache cube; + /* Update transforms. */ + copy_v3_v3(cube.position, ob->obmat[3]); + + /* Attenuation. */ + cube.attenuation_type = probe->attenuation_type; + cube.attenuation_fac = 1.0f / max_ff(1e-8f, probe->falloff); + + unit_m4(cube.attenuationmat); + scale_m4_fl(cube.attenuationmat, probe->distinf); + mul_m4_m4m4(cube.attenuationmat, ob->obmat, cube.attenuationmat); + invert_m4(cube.attenuationmat); + + /* Parallax. */ + unit_m4(cube.parallaxmat); + + if ((probe->flag & LIGHTPROBE_FLAG_CUSTOM_PARALLAX) != 0) { + cube.parallax_type = probe->parallax_type; + scale_m4_fl(cube.parallaxmat, probe->distpar); + } + else { + cube.parallax_type = probe->attenuation_type; + scale_m4_fl(cube.parallaxmat, probe->distinf); + } + + mul_m4_m4m4(cube.parallaxmat, ob->obmat, cube.parallaxmat); + invert_m4(cube.parallaxmat); + + cube.probe_index = probe_index; + cube.is_ready = 0; + return cube; + } + + /* Counts and generate lightprobes cache data. Returns irradiance sample total count. */ + int64_t sync_probes(Vector<LightGridCache> &grids_data, Vector<LightProbeCache> &cubes_data) + { + /* Start at one to count world sample. */ + int64_t irradiance_samples_count = 1; + + grids_probe_.clear(); + cubes_probe_.clear(); + + DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph_, ob) { + const int ob_visibility = BKE_object_visibility(ob, DAG_EVAL_RENDER); + if ((ob_visibility & OB_VISIBLE_SELF) == 0) { + continue; + } + + if (ob->type == OB_LIGHTPROBE) { + LightProbe *prb = (LightProbe *)ob->data; + + if (prb->type == LIGHTPROBE_TYPE_GRID) { + int probe_index = grids_probe_.append_and_get_index(*prb); + grids_data.append(grid_cache_from_object(ob, probe_index, irradiance_samples_count)); + } + else if (prb->type == LIGHTPROBE_TYPE_CUBE) { + int probe_index = cubes_probe_.append_and_get_index(*prb); + cubes_data.append(cube_cache_from_object(ob, probe_index)); + } + } + } + DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END; + + auto sort_grids = [](const LightGridCache &a, const LightGridCache &b) { + return mat4_to_scale(a.mat) < mat4_to_scale(b.mat); + }; + std::sort(grids_data.begin(), grids_data.end(), sort_grids); + + auto sort_cubes = [](const LightProbeCache &a, const LightProbeCache &b) { + return mat4_to_scale(a.attenuationmat) < mat4_to_scale(b.attenuationmat); + }; + std::sort(cubes_data.begin(), cubes_data.end(), sort_cubes); + + LightProbeCache world_cube = {}; + world_cube.probe_index = -1; + cubes_data.prepend({world_cube}); + + LightGridCache world_grid = {}; + world_grid.resolution[0] = world_grid.resolution[1] = world_grid.resolution[2] = 1; + world_grid.offset = 0; + world_grid.probe_index = -1; + grids_data.prepend({world_grid}); + + return irradiance_samples_count; + } + + void create_resources(void) + { + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph_); + SceneEEVEE &sce_eevee = scene_eval->eevee; + + Vector<LightProbeCache> cubes_data; + Vector<LightGridCache> grids_data; + int64_t irradiance_samples_count = sync_probes(grids_data, cubes_data); + + grid_len_ = grids_data.size(); + cube_len_ = cubes_data.size(); + + ivec3 irradiance_tx_size; + LightCache::irradiance_cache_size_get( + sce_eevee.gi_visibility_resolution, irradiance_samples_count, irradiance_tx_size); + + /* Ensure Light Cache is ready to accept new data. If not recreate one. + * WARNING: All the following must be threadsafe. It's currently protected by the DRW mutex. */ + lcache_ = (LightCache *)sce_eevee.light_cache_data; + + /* TODO validate irradiance and reflection cache independently... */ + if (lcache_ && + !lcache_->validate( + cube_len_, sce_eevee.gi_cubemap_resolution, grid_len_, irradiance_tx_size)) { + /* Note: this is only the scene eval data. This does not count as ownership. + * Real owner is original scene which gets the new lightcache in update_scene_cache(). */ + sce_eevee.light_cache_data = lcache_ = nullptr; + } + + if (lcache_ == nullptr) { + lcache_ = new LightCache(cube_len_, + grid_len_, + sce_eevee.gi_cubemap_resolution, + sce_eevee.gi_visibility_resolution, + irradiance_tx_size); + own_light_cache_ = true; + /* Note: this is only the scene eval data. This does not count as ownership. */ + sce_eevee.light_cache_data = lcache_; + } + + /* Copy gathered data to cache. */ + memcpy(lcache_->cube_data, cubes_data.data(), cube_len_ * sizeof(*lcache_->cube_data)); + memcpy(lcache_->grid_data, grids_data.data(), grid_len_ * sizeof(*lcache_->grid_data)); + + lcache_->load(); + lcache_->flag |= LIGHTCACHE_BAKING; + + inst_ = reinterpret_cast<Instance *>(EEVEE_instance_alloc()); + + done_ = 0; + total_ = irradiance_samples_count * sce_eevee.gi_diffuse_bounces + grid_len_; + } + + void delete_resources(void) + { + std::lock_guard<std::mutex> lock(mutex_); + + if (gl_context_) { + DRW_opengl_render_context_enable(gl_context_); + DRW_gpu_render_context_enable(gpu_context_); + } + else { + DRW_opengl_context_enable(); + } + /* XXX Free the resources contained in the viewlayer data + * to be able to free the context before deleting the depsgraph. */ + /* TODO(fclem) This is not necessary for now because we do not store anything in view layers + * since the start of EEVEE's rewrite. But this might change. */ + // EEVEE_view_layer_data_free(sldata_); + + if (inst_) { + EEVEE_instance_free(reinterpret_cast<EEVEE_Instance *>(inst_)); + } + + if (gpu_context_) { + DRW_gpu_render_context_disable(gpu_context_); + DRW_gpu_render_context_enable(gpu_context_); + GPU_context_discard(gpu_context_); + } + + if (gl_context_ && own_resources_) { + /* Delete the baking context. */ + DRW_opengl_render_context_disable(gl_context_); + WM_opengl_context_dispose(gl_context_); + gpu_context_ = nullptr; + gl_context_ = nullptr; + } + else if (gl_context_) { + DRW_opengl_render_context_disable(gl_context_); + } + else { + DRW_opengl_context_disable(); + } + } + + /* Stop baking (only if async). Threadsafety is responsibility of the caller. */ + void stop(void) + { + if (stop_ != nullptr) { + *stop_ = 1; + } + if (do_update_ != nullptr) { + *do_update_ = 1; + } + } + + void context_enable(void) + { + if (GPU_use_main_context_workaround() && !BLI_thread_is_main()) { + GPU_context_main_lock(); + DRW_opengl_context_enable(); + return; + } + + if (gl_context_) { + DRW_opengl_render_context_enable(gl_context_); + if (gpu_context_ == NULL) { + gpu_context_ = GPU_context_create(NULL); + } + DRW_gpu_render_context_enable(gpu_context_); + } + else { + DRW_opengl_context_enable(); + } + } + + void context_disable(void) + { + if (GPU_use_main_context_workaround() && !BLI_thread_is_main()) { + DRW_opengl_context_disable(); + GPU_context_main_unlock(); + return; + } + + if (gl_context_) { + DRW_gpu_render_context_disable(gpu_context_); + DRW_opengl_render_context_disable(gl_context_); + } + else { + DRW_opengl_context_disable(); + } + } + + MEM_CXX_CLASS_ALLOC_FUNCS("EEVEE:LightBake") +}; + +} // namespace blender::eevee + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name C interface + * \{ */ + +using namespace blender; + +/** + * Allocate a lightbake object to run async baking. + * MUST run on the main thread. + **/ +struct wmJob *EEVEE_lightbake_job_create(struct wmWindowManager *wm, + struct wmWindow *win, + struct Main *bmain, + struct ViewLayer *view_layer, + struct Scene *scene, + int delay, + int frame) +{ + /* Only one render job at a time. */ + if (WM_jobs_test(wm, scene, WM_JOB_TYPE_RENDER)) { + return nullptr; + } + + wmJob *wm_job = WM_jobs_get(wm, + win, + scene, + "Bake Lighting", + WM_JOB_EXCL_RENDER | WM_JOB_PRIORITY | WM_JOB_PROGRESS, + WM_JOB_TYPE_LIGHT_BAKE); + + /* If job exists do not recreate context and depsgraph. */ + auto *old_lbake = (eevee::LightBake *)WM_jobs_customdata_get(wm_job); + + auto *lbake = new eevee::LightBake(bmain, view_layer, scene, true, frame, delay, old_lbake); + + WM_jobs_customdata_set(wm_job, lbake, EEVEE_lightbake_job_data_free); + WM_jobs_timer(wm_job, 0.4, NC_SCENE | NA_EDITED, 0); + WM_jobs_callbacks( + wm_job, EEVEE_lightbake_job, nullptr, EEVEE_lightbake_update, EEVEE_lightbake_update); + + G.is_break = false; + + return wm_job; +} + +/** + * Allocate a lightbake object to run blocking baking. MUST run on the main thread. + **/ +void *EEVEE_lightbake_job_data_alloc(struct Main *bmain, + struct ViewLayer *view_layer, + struct Scene *scene, + /* TODO(fclem) remove */ + bool UNUSED(run_as_job), + int frame) +{ + return new eevee::LightBake(bmain, view_layer, scene, false, frame, 0); +} + +void EEVEE_lightbake_job_data_free(void *custom_data) +{ + delete reinterpret_cast<eevee::LightBake *>(custom_data); +} + +/** + * Update function that swaps the lightcache in the scene by the one being baked if it is + * already renderable. + **/ +void EEVEE_lightbake_update(void *custom_data) +{ + reinterpret_cast<eevee::LightBake *>(custom_data)->update_scene_cache(); +} + +void EEVEE_lightbake_job(void *custom_data, short *stop, short *do_update, float *progress) +{ + reinterpret_cast<eevee::LightBake *>(custom_data)->do_bake(stop, do_update, progress); +} + +void EEVEE_lightcache_free(struct LightCache *lcache_) +{ + eevee::LightCache *lcache = reinterpret_cast<eevee::LightCache *>(lcache_); + MEM_delete(lcache); +} + +void EEVEE_lightcache_info_update(struct SceneEEVEE *eevee) +{ + eevee::LightCache::update_info(eevee); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Read / Write + * \{ */ + +static void write_lightcache_texture(BlendWriter *writer, LightCacheTexture *tex) +{ + if (tex->data) { + size_t data_size = tex->components * tex->tex_size[0] * tex->tex_size[1] * tex->tex_size[2]; + if (tex->data_type == LIGHTCACHETEX_FLOAT) { + data_size *= sizeof(float); + } + else if (tex->data_type == LIGHTCACHETEX_UINT) { + data_size *= sizeof(uint); + } + + /* FIXME: We can't save more than what 32bit systems can handle. + * The solution would be to split the texture but it is too late for 2.90. + * (see T78529) */ + if (data_size < INT_MAX) { + BLO_write_raw(writer, data_size, tex->data); + } + } +} + +void EEVEE_lightcache_blend_write(struct BlendWriter *writer, struct LightCache *cache) +{ + write_lightcache_texture(writer, &cache->grid_tx); + write_lightcache_texture(writer, &cache->cube_tx); + + if (cache->cube_mips) { + BLO_write_struct_array(writer, LightCacheTexture, cache->mips_len, cache->cube_mips); + for (int i = 0; i < cache->mips_len; i++) { + write_lightcache_texture(writer, &cache->cube_mips[i]); + } + } + + BLO_write_struct_array(writer, LightGridCache, cache->grid_len, cache->grid_data); + BLO_write_struct_array(writer, LightProbeCache, cache->cube_len, cache->cube_data); +} + +static void direct_link_lightcache_texture(BlendDataReader *reader, LightCacheTexture *lctex) +{ + lctex->tex = nullptr; + + if (lctex->data) { + BLO_read_data_address(reader, &lctex->data); + if (lctex->data && BLO_read_requires_endian_switch(reader)) { + int data_size = lctex->components * lctex->tex_size[0] * lctex->tex_size[1] * + lctex->tex_size[2]; + + if (lctex->data_type == LIGHTCACHETEX_FLOAT) { + BLI_endian_switch_float_array((float *)lctex->data, data_size * sizeof(float)); + } + else if (lctex->data_type == LIGHTCACHETEX_UINT) { + BLI_endian_switch_uint32_array((uint *)lctex->data, data_size * sizeof(uint)); + } + } + } + + if (lctex->data == nullptr) { + zero_v3_int(lctex->tex_size); + } +} + +void EEVEE_lightcache_blend_read_data(struct BlendDataReader *reader, struct LightCache *cache) +{ + cache->flag &= ~LIGHTCACHE_NOT_USABLE; + direct_link_lightcache_texture(reader, &cache->cube_tx); + direct_link_lightcache_texture(reader, &cache->grid_tx); + + if (cache->cube_mips) { + BLO_read_data_address(reader, &cache->cube_mips); + for (int i = 0; i < cache->mips_len; i++) { + direct_link_lightcache_texture(reader, &cache->cube_mips[i]); + } + } + + BLO_read_data_address(reader, &cache->cube_data); + BLO_read_data_address(reader, &cache->grid_data); +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.hh b/source/blender/draw/engines/eevee/eevee_lightcache.hh new file mode 100644 index 00000000000..93655b97f2c --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_lightcache.hh @@ -0,0 +1,92 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2018, Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "DNA_lightprobe_types.h" + +#include "GPU_capabilities.h" + +namespace blender::eevee { + +/** + * Wrapper to blender lightcache structure. + * Used to define methods for the light cache. + **/ +struct LightCache : public ::LightCache { + private: + constexpr static int min_cube_lod_level = 3; + /* Rounded to nearest PowerOfTwo */ + constexpr static int irradiance_sample_size_x = 4; /* 3 in reality */ + constexpr static int irradiance_sample_size_y = 2; + /* Manually encoded as RGBM. Also encodes visibility. */ + constexpr static eGPUTextureFormat irradiance_format = GPU_RGBA8; + /* OpenGL 3.3 core requirement, can be extended but it's already very big */ + constexpr static int irradiance_max_pool_layer = 256; + constexpr static int irradiance_max_pool_size = 1024; + constexpr static int max_irradiance_samples = + (irradiance_max_pool_size / irradiance_sample_size_x) * + (irradiance_max_pool_size / irradiance_sample_size_y); + + constexpr static eGPUTextureFormat reflection_format = GPU_R11F_G11F_B10F; + + public: + LightCache(const int cube_len, + const int grid_len, + const int cube_size, + const int vis_size, + const int irr_size[3]); + + ~LightCache(); + + static void irradiance_cache_size_get(int visibility_size, int total_samples, int r_size[3]); + static void update_info(SceneEEVEE *eevee); + + int irradiance_cells_per_row_get(void) const; + + bool load(void); + + bool validate(const int cube_len, + const int cube_res, + const int grid_len, + const int irr_size[3]) const; + + uint memsize_get(void) const; + int64_t irradiance_sample_count(void) const; + + void readback_irradiance(void); + void readback_reflections(void); + + private: + bool version_check(void) const; + bool can_be_saved(void) const; + bool load_static(void); + + bool create_reflection_texture(void); + bool create_irradiance_texture(void); + + MEM_CXX_CLASS_ALLOC_FUNCS("EEVEE:LightCache") +}; + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_lightprobe.cc b/source/blender/draw/engines/eevee/eevee_lightprobe.cc new file mode 100644 index 00000000000..c8eadea5f9e --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_lightprobe.cc @@ -0,0 +1,578 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2018, Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +#include "BLI_rect.h" +#include "BLI_span.hh" +#include "DNA_defaults.h" +#include "DNA_lightprobe_types.h" + +#include "eevee_camera.hh" +#include "eevee_instance.hh" + +namespace blender::eevee { + +void LightProbeModule::init() +{ + SceneEEVEE &sce_eevee = inst_.scene->eevee; + + lightcache_ = static_cast<LightCache *>(sce_eevee.light_cache_data); + + bool use_lookdev = inst_.use_studio_light(); + if (!use_lookdev && lightcache_ && lightcache_->load()) { + MEM_delete(lightcache_lookdev_); + } + else { + if (lightcache_ && (lightcache_->flag & LIGHTCACHE_NOT_USABLE)) { + BLI_snprintf( + inst_.info, sizeof(inst_.info), "Error: LightCache cannot be loaded on this GPU"); + } + + if (lightcache_lookdev_ == nullptr) { + int cube_len = 1; + int grid_len = 1; + int irr_samples_len = 1; + + ivec3 irr_size; + LightCache::irradiance_cache_size_get( + sce_eevee.gi_visibility_resolution, irr_samples_len, irr_size); + + lightcache_lookdev_ = new LightCache(cube_len, + grid_len, + sce_eevee.gi_cubemap_resolution, + sce_eevee.gi_visibility_resolution, + irr_size); + } + lightcache_ = lightcache_lookdev_; + } + + for (DRWView *&view : face_view_) { + view = nullptr; + } + + if (info_data_.cubes.display_size != sce_eevee.gi_cubemap_draw_size || + info_data_.grids.display_size != sce_eevee.gi_irradiance_draw_size || + info_data_.grids.irradiance_smooth != square_f(sce_eevee.gi_irradiance_smoothing)) { + /* TODO(fclem) reset on scene update instead. */ + inst_.sampling.reset(); + } + + info_data_.cubes.display_size = sce_eevee.gi_cubemap_draw_size; + info_data_.grids.display_size = sce_eevee.gi_irradiance_draw_size; + info_data_.grids.irradiance_smooth = square_f(sce_eevee.gi_irradiance_smoothing); + info_data_.grids.irradiance_cells_per_row = lightcache_->irradiance_cells_per_row_get(); + info_data_.grids.visibility_size = lightcache_->vis_res; + info_data_.grids.visibility_cells_per_row = lightcache_->grid_tx.tex_size[0] / + info_data_.grids.visibility_size; + info_data_.grids.visibility_cells_per_layer = (lightcache_->grid_tx.tex_size[1] / + info_data_.grids.visibility_size) * + info_data_.grids.visibility_cells_per_row; + + glossy_clamp_ = sce_eevee.gi_glossy_clamp; + filter_quality_ = clamp_f(sce_eevee.gi_filter_quality, 1.0f, 8.0f); +} + +void LightProbeModule::begin_sync() +{ + { + cube_downsample_ps_ = DRW_pass_create("Downsample.Cube", DRW_STATE_WRITE_COLOR); + + GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_FILTER_DOWNSAMPLE_CUBE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, cube_downsample_ps_); + DRW_shgroup_uniform_texture_ref(grp, "input_tx", &cube_downsample_input_tx_); + DRW_shgroup_uniform_block(grp, "filter_block", filter_data_.ubo_get()); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 6); + } + { + filter_glossy_ps_ = DRW_pass_create("Filter.GlossyMip", DRW_STATE_WRITE_COLOR); + + GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_FILTER_GLOSSY); + DRWShadingGroup *grp = DRW_shgroup_create(sh, filter_glossy_ps_); + DRW_shgroup_uniform_texture_ref(grp, "radiance_tx", &cube_downsample_input_tx_); + DRW_shgroup_uniform_block(grp, "filter_block", filter_data_.ubo_get()); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 6); + } + { + filter_diffuse_ps_ = DRW_pass_create("Filter.Diffuse", DRW_STATE_WRITE_COLOR); + + GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_FILTER_DIFFUSE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, filter_diffuse_ps_); + DRW_shgroup_uniform_texture_ref(grp, "radiance_tx", &cube_downsample_input_tx_); + DRW_shgroup_uniform_block(grp, "filter_block", filter_data_.ubo_get()); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + } + { + filter_visibility_ps_ = DRW_pass_create("Filter.Visibility", DRW_STATE_WRITE_COLOR); + + GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_FILTER_VISIBILITY); + DRWShadingGroup *grp = DRW_shgroup_create(sh, filter_visibility_ps_); + DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &cube_downsample_input_tx_); + DRW_shgroup_uniform_block(grp, "filter_block", filter_data_.ubo_get()); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + } + + display_ps_ = nullptr; + + if ((inst_.v3d != nullptr) && ((inst_.v3d->flag2 & V3D_HIDE_OVERLAYS) == 0)) { + if (inst_.scene->eevee.flag & (SCE_EEVEE_SHOW_CUBEMAPS | SCE_EEVEE_SHOW_IRRADIANCE)) { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; + display_ps_ = DRW_pass_create("LightProbe.Display", state); + } + + if (inst_.scene->eevee.flag & SCE_EEVEE_SHOW_CUBEMAPS) { + if (lightcache_->cube_len > 1) { + GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_DISPLAY_CUBEMAP); + DRWShadingGroup *grp = DRW_shgroup_create(sh, display_ps_); + DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", cube_tx_ref_get()); + DRW_shgroup_uniform_block(grp, "cubes_block", cube_ubo_get()); + DRW_shgroup_uniform_block(grp, "lightprobes_info_block", info_ubo_get()); + + uint cubemap_count = 0; + /* Skip world. */ + for (auto cube_id : IndexRange(1, lightcache_->cube_len - 1)) { + const LightProbeCache &cube = lightcache_->cube_data[cube_id]; + /* Note: only works because probes are rendered in sequential order. */ + if (cube.is_ready) { + cubemap_count++; + } + } + if (cubemap_count > 0) { + DRW_shgroup_call_procedural_triangles(grp, nullptr, cubemap_count * 2); + } + } + } + + if (inst_.scene->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE) { + if (lightcache_->grid_len > 1) { + GPUShader *sh = inst_.shaders.static_shader_get(LIGHTPROBE_DISPLAY_IRRADIANCE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, display_ps_); + DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", grid_tx_ref_get()); + DRW_shgroup_uniform_block(grp, "grids_block", grid_ubo_get()); + DRW_shgroup_uniform_block(grp, "lightprobes_info_block", info_ubo_get()); + + /* Skip world. */ + for (auto grid_id : IndexRange(1, lightcache_->grid_len - 1)) { + const LightGridCache &grid = lightcache_->grid_data[grid_id]; + if (grid.is_ready) { + DRWShadingGroup *grp_sub = DRW_shgroup_create_sub(grp); + DRW_shgroup_uniform_int_copy(grp_sub, "grid_id", grid_id); + uint sample_count = grid.resolution[0] * grid.resolution[1] * grid.resolution[2]; + DRW_shgroup_call_procedural_triangles(grp_sub, nullptr, sample_count * 2); + } + } + } + } + } +} + +void LightProbeModule::end_sync() +{ + if (lightcache_->flag & LIGHTCACHE_UPDATE_WORLD) { + cubemap_prepare(vec3(0.0f), 0.01f, 1.0f, true); + } +} + +void LightProbeModule::cubemap_prepare(vec3 position, float near, float far, bool background_only) +{ + SceneEEVEE &sce_eevee = inst_.scene->eevee; + int cube_res = sce_eevee.gi_cubemap_resolution; + int cube_mip_count = (int)log2_ceil_u(cube_res); + + mat4 viewmat; + unit_m4(viewmat); + negate_v3_v3(viewmat[3], position); + + /* TODO(fclem) We might want to have theses as temporary textures. */ + cube_depth_tx_.ensure_cubemap("CubemapDepth", cube_res, cube_mip_count, GPU_DEPTH24_STENCIL8); + cube_color_tx_.ensure_cubemap("CubemapColor", cube_res, cube_mip_count, GPU_RGBA16F); + GPU_texture_mipmap_mode(cube_color_tx_, true, true); + + cube_downsample_fb_.ensure(GPU_ATTACHMENT_TEXTURE(cube_depth_tx_), + GPU_ATTACHMENT_TEXTURE(cube_color_tx_)); + + filter_cube_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(lightcache_->cube_tx.tex)); + filter_grid_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(lightcache_->grid_tx.tex)); + + mat4 winmat; + cubeface_winmat_get(winmat, near, far); + + for (auto i : IndexRange(ARRAY_SIZE(probe_views_))) { + probe_views_[i].sync(cube_color_tx_, cube_depth_tx_, winmat, viewmat, background_only); + } +} + +void LightProbeModule::cubemap_render(void) +{ + DRW_stats_group_start("Cubemap Render"); + for (auto i : IndexRange(ARRAY_SIZE(probe_views_))) { + probe_views_[i].render(); + } + DRW_stats_group_end(); + + /* Update mipchain. */ + filter_data_.target_layer = 0; + filter_data_.push_update(); + cube_downsample_input_tx_ = cube_color_tx_; + + DRW_stats_group_start("Cubemap Downsample"); + GPU_framebuffer_recursive_downsample(cube_downsample_fb_, 7, cube_downsample_cb, this); + DRW_stats_group_end(); +} + +void LightProbeModule::filter_glossy(int cube_index, float intensity) +{ + DRW_stats_group_start("Filter.Glossy"); + + filter_data_.instensity_fac = intensity; + filter_data_.target_layer = cube_index * 6; + + int level_max = lightcache_->mips_len; + for (int level = 0; level <= level_max; level++) { + filter_data_.luma_max = (glossy_clamp_ > 0.0f) ? glossy_clamp_ : 1e16f; + /* Disney Roughness. */ + filter_data_.roughness = square_f(level / (float)level_max); + /* Distribute Roughness across lod more evenly. */ + filter_data_.roughness = square_f(filter_data_.roughness); + /* Avoid artifacts. */ + filter_data_.roughness = clamp_f(filter_data_.roughness, 1e-4f, 0.9999f); + /* Variable sample count and bias to make first levels faster. */ + switch (level) { + case 0: + filter_data_.sample_count = 1.0f; + filter_data_.lod_bias = -1.0f; + break; + case 1: + filter_data_.sample_count = filter_quality_ * 32.0f; + filter_data_.lod_bias = 1.0f; + break; + case 2: + filter_data_.sample_count = filter_quality_ * 40.0f; + filter_data_.lod_bias = 2.0f; + break; + case 3: + filter_data_.sample_count = filter_quality_ * 64.0f; + filter_data_.lod_bias = 2.0f; + break; + default: + filter_data_.sample_count = filter_quality_ * 128.0f; + filter_data_.lod_bias = 2.0f; + break; + } + /* Add automatic LOD bias (based on target size). */ + filter_data_.lod_bias += lod_bias_from_cubemap(); + + filter_data_.push_update(); + + filter_cube_fb_.ensure(GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE_MIP(lightcache_->cube_tx.tex, level)); + GPU_framebuffer_bind(filter_cube_fb_); + DRW_draw_pass(filter_glossy_ps_); + } + + DRW_stats_group_end(); +} + +void LightProbeModule::filter_diffuse(int sample_index, float intensity) +{ + filter_data_.instensity_fac = intensity; + filter_data_.target_layer = 0; + filter_data_.luma_max = 1e16f; + filter_data_.sample_count = 1024.0f; + filter_data_.lod_bias = lod_bias_from_cubemap(); + + filter_data_.push_update(); + + ivec2 extent = ivec2(3, 2); + ivec2 offset = extent; + offset.x *= sample_index % info_data_.grids.irradiance_cells_per_row; + offset.y *= sample_index / info_data_.grids.irradiance_cells_per_row; + + GPU_framebuffer_bind(filter_grid_fb_); + GPU_framebuffer_viewport_set(filter_grid_fb_, UNPACK2(offset), UNPACK2(extent)); + DRW_draw_pass(filter_diffuse_ps_); + GPU_framebuffer_viewport_reset(filter_grid_fb_); +} + +void LightProbeModule::filter_visibility(int sample_index, + float visibility_blur, + float visibility_range) +{ + ivec2 extent = ivec2(info_data_.grids.visibility_size); + ivec2 offset = extent; + offset.x *= sample_index % info_data_.grids.visibility_cells_per_row; + offset.y *= (sample_index / info_data_.grids.visibility_cells_per_row) % + info_data_.grids.visibility_cells_per_layer; + + filter_data_.target_layer = 1 + sample_index / info_data_.grids.visibility_cells_per_layer; + filter_data_.sample_count = 512.0f; /* TODO refine */ + filter_data_.visibility_blur = visibility_blur; + filter_data_.visibility_range = visibility_range; + + filter_data_.push_update(); + + GPU_framebuffer_bind(filter_grid_fb_); + GPU_framebuffer_viewport_set(filter_grid_fb_, UNPACK2(offset), UNPACK2(extent)); + DRW_draw_pass(filter_visibility_ps_); + GPU_framebuffer_viewport_reset(filter_grid_fb_); +} + +void LightProbeModule::update_world_cache() +{ + DRW_stats_group_start("LightProbe.world"); + + const DRWView *view_active = DRW_view_get_active(); + + cubemap_render(); + + filter_diffuse(0, 1.0f); + + if ((lightcache_->flag & LIGHTCACHE_NO_REFLECTION) == 0) { + /* TODO(fclem) Change ray type. */ + /* OPTI(fclem) Only re-render if there is a light path node in the world material. */ + // cubemap_render(); + + filter_glossy(0, 1.0f); + } + + if (view_active != nullptr) { + DRW_view_set_active(view_active); + } + + DRW_stats_group_end(); +} + +/* Ensure a temporary cache the same size at the target lightcache exists. */ +LightCache *LightProbeModule::baking_cache_get(void) +{ + if (lightcache_baking_ == nullptr) { + lightcache_baking_ = new LightCache(lightcache_->cube_len, + lightcache_->grid_len, + lightcache_->cube_tx.tex_size[0], + lightcache_->vis_res, + lightcache_->grid_tx.tex_size); + + if (lightcache_baking_->flag != LIGHTCACHE_INVALID) { + LightCache &lcache_src = *lightcache_; + LightCache &lcache = *lightcache_baking_; + /* Copy cache structure. */ + memcpy(lcache.cube_data, lcache_src.cube_data, lcache.cube_len * sizeof(*lcache.cube_data)); + memcpy(lcache.grid_data, lcache_src.grid_data, lcache.grid_len * sizeof(*lcache.grid_data)); + + /* Make grids renderable. */ + for (LightGridCache &grid : MutableSpan(lcache.grid_data, lcache.grid_len)) { + grid.is_ready = 1; + } + /* Avoid sampling further than mip 0. Mips > 0 being undefined. */ + lcache.mips_len = 0; + lcache.flag |= LIGHTCACHE_NO_REFLECTION; + + /* Init to black. */ + uint data_cube = 0; + uchar data_grid[4] = {0, 0, 0, 0}; + GPU_texture_clear(lcache.cube_tx.tex, GPU_DATA_10_11_11_REV, &data_cube); + GPU_texture_clear(lcache.grid_tx.tex, GPU_DATA_UBYTE, &data_grid); + } + } + return lightcache_baking_; +} + +void LightProbeModule::bake(Depsgraph *depsgraph, + int type, + int index, + int bounce, + const float position[3], + const LightProbe *probe, + float visibility_range) +{ + rcti rect; + BLI_rcti_init(&rect, 0, 0, 1, 1); + + /* Disable screenspace effects. */ + SceneEEVEE &sce_eevee = DEG_get_evaluated_scene(depsgraph)->eevee; + sce_eevee.flag &= ~(SCE_EEVEE_GTAO_ENABLED | SCE_EEVEE_RAYTRACING_ENABLED); + + inst_.init(ivec2(1), &rect, nullptr, depsgraph, probe); + inst_.sampling.reset(); + inst_.render_sync(); + inst_.sampling.step(); + + float near = (probe) ? probe->clipsta : 0.1f; + float far = (probe) ? probe->clipend : 1.0f; + float intensity = (probe) ? probe->intensity : 1.0f; + + bool background_only = (probe == nullptr); + cubemap_prepare(position, near, far, background_only); + + if (type == LIGHTPROBE_TYPE_CUBE && probe != nullptr) { + /* Reflections cubemaps are rendered after all irradiance bounces. + * Swap to get the final irradiance in lightcache_baking_. */ + swap_irradiance_cache(); + } + + /* Render using the previous bounce to light the scene. */ + lightcache_ = baking_cache_get(); + + cubemap_render(); + + /* Filter on the original cache. */ + lightcache_ = reinterpret_cast<LightCache *>(sce_eevee.light_cache_data); + + if (type == LIGHTPROBE_TYPE_CUBE) { + filter_glossy(index, intensity); + /* Swap back final irradiance to lightcache_. */ + if (probe != nullptr) { + swap_irradiance_cache(); + } + } + else { + filter_diffuse(index, intensity); + if (probe && bounce < 2) { + /* No need to filter visibility after 2nd bounce since both lightcache_ and + * lightcache_baking_ will have correct visibility grid. */ + filter_visibility(index, probe->vis_blur, visibility_range); + } + } +} + +/* Push world probe to first grid and cubemap slots. */ +void LightProbeModule::sync_world(const DRWView *view) +{ + BoundSphere view_bounds = DRW_view_frustum_bsphere_get(view); + /* Playing safe. The fake grid needs to be bigger than the frustum. */ + view_bounds.radius = clamp_f(view_bounds.radius * 2.0, 0.0f, FLT_MAX); + + CubemapData &cube = cube_data_[0]; + GridData &grid = grid_data_[0]; + + scale_m4_fl(grid.local_mat, view_bounds.radius); + negate_v3_v3(grid.local_mat[3], view_bounds.center); + copy_m4_m4(cube.influence_mat, grid.local_mat); + copy_m4_m4(cube.parallax_mat, cube.influence_mat); + + grid.resolution = ivec3(1); + grid.offset = 0; + grid.level_skip = 1; + grid.attenuation_bias = 0.001f; + grid.attenuation_scale = 1.0f; + grid.visibility_range = 1.0f; + grid.visibility_bleed = 0.001f; + grid.visibility_bias = 0.0f; + grid.increment_x = vec3(0.0f); + grid.increment_y = vec3(0.0f); + grid.increment_z = vec3(0.0f); + grid.corner = vec3(0.0f); + + cube._parallax_type = CUBEMAP_SHAPE_SPHERE; + cube._layer = 0.0; +} + +void LightProbeModule::sync_grid(const DRWView *UNUSED(view), + const LightGridCache &grid_cache, + int grid_index) +{ + /* Skip the world probe. */ + if (grid_index == 0 || grid_cache.is_ready != 1) { + return; + } + GridData &grid = grid_data_[info_data_.grids.grid_count]; + copy_m4_m4(grid.local_mat, grid_cache.mat); + grid.resolution = ivec3(grid_cache.resolution); + grid.offset = grid_cache.offset; + grid.level_skip = grid_cache.level_bias; + grid.attenuation_bias = grid_cache.attenuation_bias; + grid.attenuation_scale = grid_cache.attenuation_scale; + grid.visibility_range = grid_cache.visibility_range; + grid.visibility_bleed = grid_cache.visibility_bleed; + grid.visibility_bias = grid_cache.visibility_bias; + grid.increment_x = vec3(grid_cache.increment_x); + grid.increment_y = vec3(grid_cache.increment_y); + grid.increment_z = vec3(grid_cache.increment_z); + grid.corner = vec3(grid_cache.corner); + + info_data_.grids.grid_count++; +} + +void LightProbeModule::sync_cubemap(const DRWView *UNUSED(view), + const LightProbeCache &cube_cache, + int cube_index) +{ + /* Skip the world probe. */ + if (cube_index == 0 || cube_cache.is_ready != 1) { + return; + } + CubemapData &cube = cube_data_[info_data_.cubes.cube_count]; + copy_m4_m4(cube.parallax_mat, cube_cache.parallaxmat); + copy_m4_m4(cube.influence_mat, cube_cache.attenuationmat); + cube._attenuation_factor = cube_cache.attenuation_fac; + cube._attenuation_type = cube_cache.attenuation_type; + cube._parallax_type = cube_cache.parallax_type; + cube._layer = cube_index; + cube._world_position_x = cube_cache.position[0]; + cube._world_position_y = cube_cache.position[1]; + cube._world_position_z = cube_cache.position[2]; + + info_data_.cubes.cube_count++; +} + +/* Only enables world light probe if extent is invalid (no culling possible). */ +void LightProbeModule::set_view(const DRWView *view, const ivec2 extent) +{ + if (lightcache_->flag & LIGHTCACHE_UPDATE_WORLD) { + /* Set before update to avoid infinite recursion. */ + lightcache_->flag &= ~LIGHTCACHE_UPDATE_WORLD; + update_world_cache(); + } + + /* Only sync when setting the view. This way we can cull probes not in frustum. */ + /* TODO(fclem) implement culling. But needs to fix display when not all probes are present. */ + info_data_.grids.grid_count = 1; + info_data_.cubes.cube_count = 1; + + sync_world(view); + /* Only world if extent is 0. */ + if (extent.x > 0) { + for (auto i : IndexRange(lightcache_->grid_len)) { + sync_grid(view, lightcache_->grid_data[i], i); + } + for (auto i : IndexRange(lightcache_->cube_len)) { + sync_cubemap(view, lightcache_->cube_data[i], i); + } + } + + info_data_.cubes.roughness_max_lod = lightcache_->mips_len; + inst_.lookdev.rotation_get(info_data_.cubes.lookdev_rotation); + inst_.lookdev.rotation_get(info_data_.grids.lookdev_rotation); + + active_grid_tx_ = lightcache_->grid_tx.tex; + active_cube_tx_ = lightcache_->cube_tx.tex; + + info_data_.push_update(); + grid_data_.push_update(); + cube_data_.push_update(); +} + +void LightProbeModule::draw_cache_display(void) +{ + /* Only draws something if enabled. */ + DRW_draw_pass(display_ps_); +} + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_lightprobe.hh b/source/blender/draw/engines/eevee/eevee_lightprobe.hh new file mode 100644 index 00000000000..c13f7a75ca4 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_lightprobe.hh @@ -0,0 +1,175 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2018, Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +#pragma once + +#include "eevee_lightcache.hh" +#include "eevee_view.hh" + +#include "eevee_wrapper.hh" + +namespace blender::eevee { + +class Instance; + +class LightProbeModule { + private: + Instance &inst_; + + LightProbeFilterDataBuf filter_data_; + LightProbeInfoDataBuf info_data_; + GridDataBuf grid_data_; + CubemapDataBuf cube_data_; + + /* Either scene lightcache or lookdev lightcache */ + LightCache *lightcache_ = nullptr; + /* Own lightcache used for lookdev lighting or as fallback. */ + LightCache *lightcache_lookdev_ = nullptr; + /* Temporary cache used for baking. */ + LightCache *lightcache_baking_ = nullptr; + + /* Used for rendering probes. */ + /* OPTI(fclem) Share for the whole scene? Only allocate temporary? */ + Texture cube_depth_tx_ = Texture("CubemapDepth"); + Texture cube_color_tx_ = Texture("CubemapColor"); + LightProbeView probe_views_[6]; + + Framebuffer cube_downsample_fb_ = Framebuffer("cube_downsample"); + Framebuffer filter_cube_fb_ = Framebuffer("filter_cube"); + Framebuffer filter_grid_fb_ = Framebuffer("filter_grid"); + + std::array<DRWView *, 6> face_view_ = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; + + DRWPass *cube_downsample_ps_ = nullptr; + DRWPass *filter_glossy_ps_ = nullptr; + DRWPass *filter_diffuse_ps_ = nullptr; + DRWPass *filter_visibility_ps_ = nullptr; + + DRWPass *display_ps_ = nullptr; + + /** Input texture to downsample cube pass. */ + GPUTexture *cube_downsample_input_tx_ = nullptr; + /** Copy of actual textures from the lightcache_. */ + GPUTexture *active_grid_tx_ = nullptr; + GPUTexture *active_cube_tx_ = nullptr; + /** Constant values during baking. */ + float glossy_clamp_ = 0.0; + float filter_quality_ = 0.0; + + public: + LightProbeModule(Instance &inst) + : inst_(inst), + probe_views_{{inst, "posX_view", cubeface_mat[0], 0}, + {inst, "negX_view", cubeface_mat[1], 1}, + {inst, "posY_view", cubeface_mat[2], 2}, + {inst, "negY_view", cubeface_mat[3], 3}, + {inst, "posZ_view", cubeface_mat[4], 4}, + {inst, "negZ_view", cubeface_mat[5], 5}} + { + } + + ~LightProbeModule() + { + MEM_delete(lightcache_lookdev_); + MEM_delete(lightcache_baking_); + } + + void init(); + + void begin_sync(); + void end_sync(); + + void set_view(const DRWView *view, const ivec2 extent); + + void set_world_dirty(void) + { + lightcache_->flag |= LIGHTCACHE_UPDATE_WORLD; + } + + void swap_irradiance_cache(void) + { + if (lightcache_baking_ && lightcache_) { + SWAP(GPUTexture *, lightcache_baking_->grid_tx.tex, lightcache_->grid_tx.tex); + } + } + + const GPUUniformBuf *grid_ubo_get() const + { + return grid_data_.ubo_get(); + } + const GPUUniformBuf *cube_ubo_get() const + { + return cube_data_.ubo_get(); + } + const GPUUniformBuf *info_ubo_get() const + { + return info_data_.ubo_get(); + } + GPUTexture **grid_tx_ref_get() + { + return &active_grid_tx_; + } + GPUTexture **cube_tx_ref_get() + { + return &active_cube_tx_; + } + + void bake(Depsgraph *depsgraph, + int type, + int index, + int bounce, + const float position[3], + const LightProbe *probe = nullptr, + float visibility_range = 0.0f); + + void draw_cache_display(void); + + private: + void update_world_cache(); + + void sync_world(const DRWView *view); + void sync_grid(const DRWView *view, const struct LightGridCache &grid_cache, int grid_index); + void sync_cubemap(const DRWView *view, const struct LightProbeCache &cube_cache, int cube_index); + + LightCache *baking_cache_get(void); + + void cubemap_prepare(vec3 position, float near, float far, bool background_only); + + void filter_glossy(int cube_index, float intensity); + void filter_diffuse(int sample_index, float intensity); + void filter_visibility(int sample_index, float visibility_blur, float visibility_range); + + float lod_bias_from_cubemap(void) + { + float target_size_sq = square_f(GPU_texture_width(cube_color_tx_)); + return 0.5f * logf(target_size_sq / filter_data_.sample_count) / logf(2); + } + + static void cube_downsample_cb(void *thunk, int UNUSED(level)) + { + DRW_draw_pass(reinterpret_cast<LightProbeModule *>(thunk)->cube_downsample_ps_); + } + + void cubemap_render(void); +}; + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c deleted file mode 100644 index c51fc18a406..00000000000 --- a/source/blender/draw/engines/eevee/eevee_lightprobes.c +++ /dev/null @@ -1,1265 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - */ - -#include "DRW_render.h" - -#include "BLI_rand.h" -#include "BLI_string_utils.h" -#include "BLI_utildefines.h" - -#include "DNA_image_types.h" -#include "DNA_lightprobe_types.h" -#include "DNA_texture_types.h" -#include "DNA_view3d_types.h" -#include "DNA_world_types.h" - -#include "BKE_collection.h" -#include "BKE_object.h" -#include "MEM_guardedalloc.h" - -#include "GPU_capabilities.h" -#include "GPU_material.h" -#include "GPU_texture.h" -#include "GPU_uniform_buffer.h" - -#include "DEG_depsgraph_query.h" - -#include "eevee_lightcache.h" -#include "eevee_private.h" - -#include "WM_api.h" -#include "WM_types.h" - -static struct { - struct GPUTexture *planar_pool_placeholder; - struct GPUTexture *depth_placeholder; - struct GPUTexture *depth_array_placeholder; - - struct GPUVertFormat *format_probe_display_planar; -} e_data = {NULL}; /* Engine data */ - -/* *********** FUNCTIONS *********** */ - -/* TODO: find a better way than this. This does not support dupli objects if - * the original object is hidden. */ -bool EEVEE_lightprobes_obj_visibility_cb(bool vis_in, void *user_data) -{ - EEVEE_ObjectEngineData *oed = (EEVEE_ObjectEngineData *)user_data; - - /* test disabled if group is NULL */ - if (oed == NULL || oed->test_data->collection == NULL) { - return vis_in; - } - - if (oed->test_data->cached == false) { - oed->ob_vis_dirty = true; - } - - /* early out, don't need to compute ob_vis yet. */ - if (vis_in == false) { - return vis_in; - } - - if (oed->ob_vis_dirty) { - oed->ob_vis_dirty = false; - oed->ob_vis = BKE_collection_has_object_recursive(oed->test_data->collection, oed->ob); - oed->ob_vis = (oed->test_data->invert) ? !oed->ob_vis : oed->ob_vis; - } - - return vis_in && oed->ob_vis; -} - -static void planar_pool_ensure_alloc(EEVEE_Data *vedata, int num_planar_ref) -{ - EEVEE_TextureList *txl = vedata->txl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *fx = stl->effects; - - /* XXX TODO: OPTIMIZATION: This is a complete waist of texture memory. - * Instead of allocating each planar probe for each viewport, - * only alloc them once using the biggest viewport resolution. */ - - /* TODO: get screen percentage from layer setting. */ - // const DRWContextState *draw_ctx = DRW_context_state_get(); - // ViewLayer *view_layer = draw_ctx->view_layer; - int screen_divider = 1; - - int width = max_ii(1, fx->hiz_size[0] / screen_divider); - int height = max_ii(1, fx->hiz_size[1] / screen_divider); - - /* Fix case were the pool was allocated width the dummy size (1,1,1). */ - if (txl->planar_pool && (num_planar_ref > 0) && - (GPU_texture_width(txl->planar_pool) != width || - GPU_texture_height(txl->planar_pool) != height)) { - DRW_TEXTURE_FREE_SAFE(txl->planar_pool); - DRW_TEXTURE_FREE_SAFE(txl->planar_depth); - } - - /* We need an Array texture so allocate it ourself */ - if (!txl->planar_pool) { - if (num_planar_ref > 0) { - txl->planar_pool = DRW_texture_create_2d_array(width, - height, - num_planar_ref, - GPU_R11F_G11F_B10F, - DRW_TEX_FILTER | DRW_TEX_MIPMAP, - NULL); - txl->planar_depth = DRW_texture_create_2d_array( - width, height, num_planar_ref, GPU_DEPTH_COMPONENT24, 0, NULL); - } - else if (num_planar_ref == 0) { - /* Makes Opengl Happy : Create a placeholder texture that will never be sampled but still - * bound to shader. */ - txl->planar_pool = DRW_texture_create_2d_array( - 1, 1, 1, GPU_RGBA8, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL); - txl->planar_depth = DRW_texture_create_2d_array(1, 1, 1, GPU_DEPTH_COMPONENT24, 0, NULL); - } - } -} - -void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - EEVEE_StorageList *stl = vedata->stl; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - vedata->info[0] = '\0'; - - EEVEE_shaders_material_shaders_init(); - - memset(stl->g_data->bake_views, 0, sizeof(stl->g_data->bake_views)); - memset(stl->g_data->cube_views, 0, sizeof(stl->g_data->cube_views)); - memset(stl->g_data->world_views, 0, sizeof(stl->g_data->world_views)); - memset(stl->g_data->planar_views, 0, sizeof(stl->g_data->planar_views)); - - if (EEVEE_lightcache_load(scene_eval->eevee.light_cache_data)) { - stl->g_data->light_cache = scene_eval->eevee.light_cache_data; - } - else { - if (scene_eval->eevee.light_cache_data && - (scene_eval->eevee.light_cache_data->flag & LIGHTCACHE_NOT_USABLE)) { - /* Error message info. */ - BLI_snprintf( - vedata->info, sizeof(vedata->info), "Error: LightCache cannot be loaded on this GPU"); - } - - if (!sldata->fallback_lightcache) { -#if defined(IRRADIANCE_SH_L2) - int grid_res = 4; -#elif defined(IRRADIANCE_HL2) - int grid_res = 4; -#endif - sldata->fallback_lightcache = EEVEE_lightcache_create( - 1, - 1, - scene_eval->eevee.gi_cubemap_resolution, - scene_eval->eevee.gi_visibility_resolution, - (int[3]){grid_res, grid_res, 1}); - } - stl->g_data->light_cache = sldata->fallback_lightcache; - } - - if (!sldata->probes) { - sldata->probes = MEM_callocN(sizeof(EEVEE_LightProbesInfo), "EEVEE_LightProbesInfo"); - sldata->probe_ubo = GPU_uniformbuf_create(sizeof(EEVEE_LightProbe) * MAX_PROBE); - sldata->grid_ubo = GPU_uniformbuf_create(sizeof(EEVEE_LightGrid) * MAX_GRID); - sldata->planar_ubo = GPU_uniformbuf_create(sizeof(EEVEE_PlanarReflection) * MAX_PLANAR); - } - - common_data->prb_num_planar = 0; - common_data->prb_num_render_cube = 1; - common_data->prb_num_render_grid = 1; - - common_data->spec_toggle = true; - common_data->ssr_toggle = true; - common_data->ssrefract_toggle = true; - common_data->sss_toggle = true; - - /* Placeholder planar pool: used when rendering planar reflections (avoid dependency loop). */ - if (!e_data.planar_pool_placeholder) { - e_data.planar_pool_placeholder = DRW_texture_create_2d_array( - 1, 1, 1, GPU_RGBA8, DRW_TEX_FILTER, NULL); - } -} - -void EEVEE_lightbake_cache_init(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - GPUTexture *rt_color, - GPUTexture *rt_depth) -{ - EEVEE_PassList *psl = vedata->psl; - LightCache *light_cache = vedata->stl->g_data->light_cache; - EEVEE_LightProbesInfo *pinfo = sldata->probes; - - { - DRW_PASS_CREATE(psl->probe_glossy_compute, DRW_STATE_WRITE_COLOR); - - DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_filter_glossy_sh_get(), - psl->probe_glossy_compute); - - DRW_shgroup_uniform_float(grp, "intensityFac", &pinfo->intensity_fac, 1); - DRW_shgroup_uniform_float(grp, "sampleCount", &pinfo->samples_len, 1); - DRW_shgroup_uniform_float(grp, "roughness", &pinfo->roughness, 1); - DRW_shgroup_uniform_float(grp, "lodFactor", &pinfo->lodfactor, 1); - DRW_shgroup_uniform_float(grp, "lodMax", &pinfo->lod_rt_max, 1); - DRW_shgroup_uniform_float(grp, "texelSize", &pinfo->texel_size, 1); - DRW_shgroup_uniform_float(grp, "paddingSize", &pinfo->padding_size, 1); - DRW_shgroup_uniform_float(grp, "fireflyFactor", &pinfo->firefly_fac, 1); - DRW_shgroup_uniform_int(grp, "Layer", &pinfo->layer, 1); - // DRW_shgroup_uniform_texture(grp, "texJitter", e_data.jitter); - DRW_shgroup_uniform_texture(grp, "probeHdr", rt_color); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - - struct GPUBatch *geom = DRW_cache_fullscreen_quad_get(); - DRW_shgroup_call_instances(grp, NULL, geom, 6); - } - - { - DRW_PASS_CREATE(psl->probe_diffuse_compute, DRW_STATE_WRITE_COLOR); - DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_filter_diffuse_sh_get(), - psl->probe_diffuse_compute); -#ifdef IRRADIANCE_SH_L2 - DRW_shgroup_uniform_int(grp, "probeSize", &pinfo->shres, 1); -#else - DRW_shgroup_uniform_float(grp, "sampleCount", &pinfo->samples_len, 1); - DRW_shgroup_uniform_float(grp, "lodFactor", &pinfo->lodfactor, 1); - DRW_shgroup_uniform_float(grp, "lodMax", &pinfo->lod_rt_max, 1); -#endif - DRW_shgroup_uniform_float(grp, "intensityFac", &pinfo->intensity_fac, 1); - DRW_shgroup_uniform_texture(grp, "probeHdr", rt_color); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - - struct GPUBatch *geom = DRW_cache_fullscreen_quad_get(); - DRW_shgroup_call(grp, geom, NULL); - } - - { - DRW_PASS_CREATE(psl->probe_visibility_compute, DRW_STATE_WRITE_COLOR); - DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_filter_visibility_sh_get(), - psl->probe_visibility_compute); - DRW_shgroup_uniform_int(grp, "outputSize", &pinfo->shres, 1); - DRW_shgroup_uniform_float(grp, "visibilityRange", &pinfo->visibility_range, 1); - DRW_shgroup_uniform_float(grp, "visibilityBlur", &pinfo->visibility_blur, 1); - DRW_shgroup_uniform_float(grp, "sampleCount", &pinfo->samples_len, 1); - DRW_shgroup_uniform_float(grp, "storedTexelSize", &pinfo->texel_size, 1); - DRW_shgroup_uniform_float(grp, "nearClip", &pinfo->near_clip, 1); - DRW_shgroup_uniform_float(grp, "farClip", &pinfo->far_clip, 1); - DRW_shgroup_uniform_texture(grp, "probeDepth", rt_depth); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - - struct GPUBatch *geom = DRW_cache_fullscreen_quad_get(); - DRW_shgroup_call(grp, geom, NULL); - } - - { - DRW_PASS_CREATE(psl->probe_grid_fill, DRW_STATE_WRITE_COLOR); - - DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_grid_fill_sh_get(), - psl->probe_grid_fill); - - DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &light_cache->grid_tx.tex); - - struct GPUBatch *geom = DRW_cache_fullscreen_quad_get(); - DRW_shgroup_call(grp, geom, NULL); - } -} - -void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_TextureList *txl = vedata->txl; - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_LightProbesInfo *pinfo = sldata->probes; - LightCache *lcache = stl->g_data->light_cache; - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - - pinfo->num_planar = 0; - pinfo->vis_data.collection = NULL; - pinfo->do_grid_update = false; - pinfo->do_cube_update = false; - - { - DRW_PASS_CREATE(psl->probe_background, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); - - DRWShadingGroup *grp = NULL; - EEVEE_lookdev_cache_init(vedata, sldata, psl->probe_background, pinfo, &grp); - - if (grp == NULL) { - Scene *scene = draw_ctx->scene; - World *world = (scene->world) ? scene->world : EEVEE_world_default_get(); - - const int options = VAR_WORLD_BACKGROUND | VAR_WORLD_PROBE; - struct GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, NULL, world, options); - - grp = DRW_shgroup_material_create(gpumat, psl->probe_background); - DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f); - } - - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); - DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); - DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); - DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &stl->g_data->renderpass_ubo); - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); - } - - if (DRW_state_draw_support()) { - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | - DRW_STATE_CULL_BACK; - DRW_PASS_CREATE(psl->probe_display, state); - - if (!LOOK_DEV_STUDIO_LIGHT_ENABLED(draw_ctx->v3d)) { - /* Cube Display */ - if (scene_eval->eevee.flag & SCE_EEVEE_SHOW_CUBEMAPS && lcache->cube_len > 1) { - int cube_len = lcache->cube_len - 1; /* don't count the world. */ - DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_cube_display_sh_get(), - psl->probe_display); - - DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &lcache->cube_tx.tex); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_vec3(grp, "screen_vecs", DRW_viewport_screenvecs_get(), 2); - DRW_shgroup_uniform_float_copy( - grp, "sphere_size", scene_eval->eevee.gi_cubemap_draw_size * 0.5f); - /* TODO(fclem): get rid of those UBO. */ - DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); - DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - - DRW_shgroup_call_procedural_triangles(grp, NULL, cube_len * 2); - } - - /* Grid Display */ - if (scene_eval->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE) { - EEVEE_LightGrid *egrid = lcache->grid_data + 1; - for (int p = 1; p < lcache->grid_len; p++, egrid++) { - DRWShadingGroup *shgrp = DRW_shgroup_create(EEVEE_shaders_probe_grid_display_sh_get(), - psl->probe_display); - - DRW_shgroup_uniform_int(shgrp, "offset", &egrid->offset, 1); - DRW_shgroup_uniform_ivec3(shgrp, "grid_resolution", egrid->resolution, 1); - DRW_shgroup_uniform_vec3(shgrp, "corner", egrid->corner, 1); - DRW_shgroup_uniform_vec3(shgrp, "increment_x", egrid->increment_x, 1); - DRW_shgroup_uniform_vec3(shgrp, "increment_y", egrid->increment_y, 1); - DRW_shgroup_uniform_vec3(shgrp, "increment_z", egrid->increment_z, 1); - DRW_shgroup_uniform_vec3(shgrp, "screen_vecs", DRW_viewport_screenvecs_get(), 2); - DRW_shgroup_uniform_texture_ref(shgrp, "irradianceGrid", &lcache->grid_tx.tex); - DRW_shgroup_uniform_float_copy( - shgrp, "sphere_size", scene_eval->eevee.gi_irradiance_draw_size * 0.5f); - /* TODO(fclem): get rid of those UBO. */ - DRW_shgroup_uniform_block(shgrp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(shgrp, "planar_block", sldata->planar_ubo); - DRW_shgroup_uniform_block(shgrp, "grid_block", sldata->grid_ubo); - DRW_shgroup_uniform_block(shgrp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(shgrp, "renderpass_block", sldata->renderpass_ubo.combined); - int tri_count = egrid->resolution[0] * egrid->resolution[1] * egrid->resolution[2] * 2; - DRW_shgroup_call_procedural_triangles(shgrp, NULL, tri_count); - } - } - } - - /* Planar Display */ - { - DRW_shgroup_instance_format(e_data.format_probe_display_planar, - { - {"probe_id", DRW_ATTR_INT, 1}, - {"probe_mat", DRW_ATTR_FLOAT, 16}, - }); - - DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_planar_display_sh_get(), - psl->probe_display); - DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &txl->planar_pool); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - - stl->g_data->planar_display_shgrp = DRW_shgroup_call_buffer_instance( - grp, e_data.format_probe_display_planar, DRW_cache_quad_get()); - } - } - else { - stl->g_data->planar_display_shgrp = NULL; - } -} - -static bool eevee_lightprobes_culling_test(Object *ob) -{ - LightProbe *probe = (LightProbe *)ob->data; - - switch (probe->type) { - case LIGHTPROBE_TYPE_PLANAR: { - /* See if this planar probe is inside the view frustum. If not, no need to update it. */ - /* NOTE: this could be bypassed if we want feedback loop mirrors for rendering. */ - BoundBox bbox; - float tmp[4][4]; - const float min[3] = {-1.0f, -1.0f, -1.0f}; - const float max[3] = {1.0f, 1.0f, 1.0f}; - BKE_boundbox_init_from_minmax(&bbox, min, max); - - copy_m4_m4(tmp, ob->obmat); - normalize_v3(tmp[2]); - mul_v3_fl(tmp[2], probe->distinf); - - for (int v = 0; v < 8; v++) { - mul_m4_v3(tmp, bbox.vec[v]); - } - const DRWView *default_view = DRW_view_default_get(); - return DRW_culling_box_test(default_view, &bbox); - } - case LIGHTPROBE_TYPE_CUBE: - return true; /* TODO */ - case LIGHTPROBE_TYPE_GRID: - return true; /* TODO */ - } - BLI_assert(0); - return true; -} - -void EEVEE_lightprobes_cache_add(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *ob) -{ - EEVEE_LightProbesInfo *pinfo = sldata->probes; - LightProbe *probe = (LightProbe *)ob->data; - - if ((probe->type == LIGHTPROBE_TYPE_CUBE && pinfo->num_cube >= EEVEE_PROBE_MAX) || - (probe->type == LIGHTPROBE_TYPE_GRID && pinfo->num_grid >= EEVEE_PROBE_MAX) || - (probe->type == LIGHTPROBE_TYPE_PLANAR && pinfo->num_planar >= MAX_PLANAR)) { - printf("Too many probes in the view !!!\n"); - return; - } - - if (probe->type == LIGHTPROBE_TYPE_PLANAR) { - /* TODO(fclem): Culling should be done after cache generation. - * This is needed for future draw cache persistence. */ - if (!eevee_lightprobes_culling_test(ob)) { - return; /* Culled */ - } - EEVEE_lightprobes_planar_data_from_object( - ob, &pinfo->planar_data[pinfo->num_planar], &pinfo->planar_vis_tests[pinfo->num_planar]); - /* Debug Display */ - DRWCallBuffer *grp = vedata->stl->g_data->planar_display_shgrp; - if (grp && (probe->flag & LIGHTPROBE_FLAG_SHOW_DATA)) { - DRW_buffer_add_entry(grp, &pinfo->num_planar, ob->obmat); - } - - pinfo->num_planar++; - } - else { - EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_ensure(ob); - if (ped->need_update) { - if (probe->type == LIGHTPROBE_TYPE_GRID) { - pinfo->do_grid_update = true; - } - else { - pinfo->do_cube_update = true; - } - ped->need_update = false; - } - } -} - -void EEVEE_lightprobes_grid_data_from_object(Object *ob, EEVEE_LightGrid *egrid, int *offset) -{ - LightProbe *probe = (LightProbe *)ob->data; - - copy_v3_v3_int(egrid->resolution, &probe->grid_resolution_x); - - /* Save current offset and advance it for the next grid. */ - egrid->offset = *offset; - *offset += egrid->resolution[0] * egrid->resolution[1] * egrid->resolution[2]; - - /* Add one for level 0 */ - float fac = 1.0f / max_ff(1e-8f, probe->falloff); - egrid->attenuation_scale = fac / max_ff(1e-8f, probe->distinf); - egrid->attenuation_bias = fac; - - /* Update transforms */ - float cell_dim[3], half_cell_dim[3]; - cell_dim[0] = 2.0f / egrid->resolution[0]; - cell_dim[1] = 2.0f / egrid->resolution[1]; - cell_dim[2] = 2.0f / egrid->resolution[2]; - - mul_v3_v3fl(half_cell_dim, cell_dim, 0.5f); - - /* Matrix converting world space to cell ranges. */ - invert_m4_m4(egrid->mat, ob->obmat); - - /* First cell. */ - copy_v3_fl(egrid->corner, -1.0f); - add_v3_v3(egrid->corner, half_cell_dim); - mul_m4_v3(ob->obmat, egrid->corner); - - /* Opposite neighbor cell. */ - copy_v3_fl3(egrid->increment_x, cell_dim[0], 0.0f, 0.0f); - add_v3_v3(egrid->increment_x, half_cell_dim); - add_v3_fl(egrid->increment_x, -1.0f); - mul_m4_v3(ob->obmat, egrid->increment_x); - sub_v3_v3(egrid->increment_x, egrid->corner); - - copy_v3_fl3(egrid->increment_y, 0.0f, cell_dim[1], 0.0f); - add_v3_v3(egrid->increment_y, half_cell_dim); - add_v3_fl(egrid->increment_y, -1.0f); - mul_m4_v3(ob->obmat, egrid->increment_y); - sub_v3_v3(egrid->increment_y, egrid->corner); - - copy_v3_fl3(egrid->increment_z, 0.0f, 0.0f, cell_dim[2]); - add_v3_v3(egrid->increment_z, half_cell_dim); - add_v3_fl(egrid->increment_z, -1.0f); - mul_m4_v3(ob->obmat, egrid->increment_z); - sub_v3_v3(egrid->increment_z, egrid->corner); - - /* Visibility bias */ - egrid->visibility_bias = 0.05f * probe->vis_bias; - egrid->visibility_bleed = probe->vis_bleedbias; - egrid->visibility_range = 1.0f + sqrtf(max_fff(len_squared_v3(egrid->increment_x), - len_squared_v3(egrid->increment_y), - len_squared_v3(egrid->increment_z))); -} - -void EEVEE_lightprobes_cube_data_from_object(Object *ob, EEVEE_LightProbe *eprobe) -{ - LightProbe *probe = (LightProbe *)ob->data; - - /* Update transforms */ - copy_v3_v3(eprobe->position, ob->obmat[3]); - - /* Attenuation */ - eprobe->attenuation_type = probe->attenuation_type; - eprobe->attenuation_fac = 1.0f / max_ff(1e-8f, probe->falloff); - - unit_m4(eprobe->attenuationmat); - scale_m4_fl(eprobe->attenuationmat, probe->distinf); - mul_m4_m4m4(eprobe->attenuationmat, ob->obmat, eprobe->attenuationmat); - invert_m4(eprobe->attenuationmat); - - /* Parallax */ - unit_m4(eprobe->parallaxmat); - - if ((probe->flag & LIGHTPROBE_FLAG_CUSTOM_PARALLAX) != 0) { - eprobe->parallax_type = probe->parallax_type; - scale_m4_fl(eprobe->parallaxmat, probe->distpar); - } - else { - eprobe->parallax_type = probe->attenuation_type; - scale_m4_fl(eprobe->parallaxmat, probe->distinf); - } - - mul_m4_m4m4(eprobe->parallaxmat, ob->obmat, eprobe->parallaxmat); - invert_m4(eprobe->parallaxmat); -} - -void EEVEE_lightprobes_planar_data_from_object(Object *ob, - EEVEE_PlanarReflection *eplanar, - EEVEE_LightProbeVisTest *vis_test) -{ - LightProbe *probe = (LightProbe *)ob->data; - float normat[4][4], imat[4][4]; - - vis_test->collection = probe->visibility_grp; - vis_test->invert = probe->flag & LIGHTPROBE_FLAG_INVERT_GROUP; - vis_test->cached = false; - - /* Computing mtx : matrix that mirror position around object's XY plane. */ - normalize_m4_m4(normat, ob->obmat); /* object > world */ - invert_m4_m4(imat, normat); /* world > object */ - /* XY reflection plane */ - imat[0][2] = -imat[0][2]; - imat[1][2] = -imat[1][2]; - imat[2][2] = -imat[2][2]; - imat[3][2] = -imat[3][2]; /* world > object > mirrored obj */ - mul_m4_m4m4(eplanar->mtx, normat, imat); /* world > object > mirrored obj > world */ - - /* Compute clip plane equation / normal. */ - copy_v3_v3(eplanar->plane_equation, ob->obmat[2]); - normalize_v3(eplanar->plane_equation); /* plane normal */ - eplanar->plane_equation[3] = -dot_v3v3(eplanar->plane_equation, ob->obmat[3]); - eplanar->clipsta = probe->clipsta; - - /* Compute XY clip planes. */ - normalize_v3_v3(eplanar->clip_vec_x, ob->obmat[0]); - normalize_v3_v3(eplanar->clip_vec_y, ob->obmat[1]); - - float vec[3] = {0.0f, 0.0f, 0.0f}; - vec[0] = 1.0f; - vec[1] = 0.0f; - vec[2] = 0.0f; - mul_m4_v3(ob->obmat, vec); /* Point on the edge */ - eplanar->clip_edge_x_pos = dot_v3v3(eplanar->clip_vec_x, vec); - - vec[0] = 0.0f; - vec[1] = 1.0f; - vec[2] = 0.0f; - mul_m4_v3(ob->obmat, vec); /* Point on the edge */ - eplanar->clip_edge_y_pos = dot_v3v3(eplanar->clip_vec_y, vec); - - vec[0] = -1.0f; - vec[1] = 0.0f; - vec[2] = 0.0f; - mul_m4_v3(ob->obmat, vec); /* Point on the edge */ - eplanar->clip_edge_x_neg = dot_v3v3(eplanar->clip_vec_x, vec); - - vec[0] = 0.0f; - vec[1] = -1.0f; - vec[2] = 0.0f; - mul_m4_v3(ob->obmat, vec); /* Point on the edge */ - eplanar->clip_edge_y_neg = dot_v3v3(eplanar->clip_vec_y, vec); - - /* Facing factors */ - float max_angle = max_ff(1e-2f, 1.0f - probe->falloff) * M_PI * 0.5f; - float min_angle = 0.0f; - eplanar->facing_scale = 1.0f / max_ff(1e-8f, cosf(min_angle) - cosf(max_angle)); - eplanar->facing_bias = -min_ff(1.0f - 1e-8f, cosf(max_angle)) * eplanar->facing_scale; - - /* Distance factors */ - float max_dist = probe->distinf; - float min_dist = min_ff(1.0f - 1e-8f, 1.0f - probe->falloff) * probe->distinf; - eplanar->attenuation_scale = -1.0f / max_ff(1e-8f, max_dist - min_dist); - eplanar->attenuation_bias = max_dist * -eplanar->attenuation_scale; -} - -static void lightbake_planar_ensure_view(EEVEE_PlanarReflection *eplanar, - const DRWView *main_view, - DRWView **r_planar_view) -{ - float winmat[4][4], viewmat[4][4], persmat[4][4]; - DRW_view_viewmat_get(main_view, viewmat, false); - /* Temporal sampling jitter should be already applied to the DRW_MAT_WIN. */ - DRW_view_winmat_get(main_view, winmat, false); - DRW_view_persmat_get(main_view, persmat, false); - - /* Invert X to avoid flipping the triangle facing direction. */ - winmat[0][0] = -winmat[0][0]; - winmat[1][0] = -winmat[1][0]; - winmat[2][0] = -winmat[2][0]; - winmat[3][0] = -winmat[3][0]; - /* Reflect Camera Matrix. */ - mul_m4_m4m4(viewmat, viewmat, eplanar->mtx); - - if (*r_planar_view == NULL) { - *r_planar_view = DRW_view_create( - viewmat, winmat, NULL, NULL, EEVEE_lightprobes_obj_visibility_cb); - /* Compute offset plane equation (fix missing texels near reflection plane). */ - float clip_plane[4]; - copy_v4_v4(clip_plane, eplanar->plane_equation); - clip_plane[3] += eplanar->clipsta; - /* Set clipping plane */ - DRW_view_clip_planes_set(*r_planar_view, &clip_plane, 1); - } - else { - DRW_view_update(*r_planar_view, viewmat, winmat, NULL, NULL); - } -} - -static void eevee_lightprobes_extract_from_cache(EEVEE_LightProbesInfo *pinfo, LightCache *lcache) -{ - /* copy the entire cache for now (up to MAX_PROBE) */ - /* TODO: frustum cull to only add visible probes. */ - memcpy(pinfo->probe_data, - lcache->cube_data, - sizeof(EEVEE_LightProbe) * max_ii(1, min_ii(lcache->cube_len, MAX_PROBE))); - /* TODO: compute the max number of grid based on sample count. */ - memcpy(pinfo->grid_data, - lcache->grid_data, - sizeof(EEVEE_LightGrid) * max_ii(1, min_ii(lcache->grid_len, MAX_GRID))); -} - -void EEVEE_lightprobes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_StorageList *stl = vedata->stl; - LightCache *light_cache = stl->g_data->light_cache; - EEVEE_LightProbesInfo *pinfo = sldata->probes; - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - - eevee_lightprobes_extract_from_cache(sldata->probes, light_cache); - - GPU_uniformbuf_update(sldata->probe_ubo, &sldata->probes->probe_data); - GPU_uniformbuf_update(sldata->grid_ubo, &sldata->probes->grid_data); - - /* For shading, save max level of the octahedron map */ - sldata->common_data.prb_lod_cube_max = (float)light_cache->mips_len; - sldata->common_data.prb_irradiance_vis_size = light_cache->vis_res; - sldata->common_data.prb_irradiance_smooth = square_f(scene_eval->eevee.gi_irradiance_smoothing); - sldata->common_data.prb_num_render_cube = max_ii(1, light_cache->cube_len); - sldata->common_data.prb_num_render_grid = max_ii(1, light_cache->grid_len); - sldata->common_data.prb_num_planar = pinfo->num_planar; - - if (pinfo->num_planar != pinfo->cache_num_planar) { - DRW_TEXTURE_FREE_SAFE(vedata->txl->planar_pool); - DRW_TEXTURE_FREE_SAFE(vedata->txl->planar_depth); - pinfo->cache_num_planar = pinfo->num_planar; - } - planar_pool_ensure_alloc(vedata, pinfo->num_planar); - - /* If light-cache auto-update is enable we tag the relevant part - * of the cache to update and fire up a baking job. */ - if (!DRW_state_is_image_render() && !DRW_state_is_opengl_render() && - (pinfo->do_grid_update || pinfo->do_cube_update)) { - BLI_assert(draw_ctx->evil_C); - - if (draw_ctx->scene->eevee.flag & SCE_EEVEE_GI_AUTOBAKE) { - Scene *scene_orig = DEG_get_input_scene(draw_ctx->depsgraph); - if (scene_orig->eevee.light_cache_data != NULL) { - if (pinfo->do_grid_update) { - scene_orig->eevee.light_cache_data->flag |= LIGHTCACHE_UPDATE_GRID; - } - /* If we update grid we need to update the cube-maps too. - * So always refresh cube-maps. */ - scene_orig->eevee.light_cache_data->flag |= LIGHTCACHE_UPDATE_CUBE; - /* Tag the lightcache to auto update. */ - scene_orig->eevee.light_cache_data->flag |= LIGHTCACHE_UPDATE_AUTO; - /* Use a notifier to trigger the operator after drawing. */ - WM_event_add_notifier(draw_ctx->evil_C, NC_LIGHTPROBE, scene_orig); - } - } - } - - if (pinfo->num_planar > 0) { - EEVEE_PassList *psl = vedata->psl; - EEVEE_TextureList *txl = vedata->txl; - DRW_PASS_CREATE(psl->probe_planar_downsample_ps, DRW_STATE_WRITE_COLOR); - - DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_planar_downsample_sh_get(), - psl->probe_planar_downsample_ps); - - DRW_shgroup_uniform_texture_ref(grp, "source", &txl->planar_pool); - DRW_shgroup_uniform_float(grp, "fireflyFactor", &sldata->common_data.ssr_firefly_fac, 1); - DRW_shgroup_call_procedural_triangles(grp, NULL, pinfo->num_planar); - } -} - -/* -------------------------------------------------------------------- */ -/** \name Rendering - * \{ */ - -typedef struct EEVEE_BakeRenderData { - EEVEE_Data *vedata; - EEVEE_ViewLayerData *sldata; - struct GPUFrameBuffer **face_fb; /* should contain 6 framebuffer */ -} EEVEE_BakeRenderData; - -static void render_cubemap(void (*callback)(int face, EEVEE_BakeRenderData *user_data), - EEVEE_BakeRenderData *user_data, - const float pos[3], - float near, - float far, - bool do_culling) -{ - EEVEE_StorageList *stl = user_data->vedata->stl; - DRWView **views = do_culling ? stl->g_data->bake_views : stl->g_data->world_views; - - float winmat[4][4], viewmat[4][4]; - perspective_m4(winmat, -near, near, -near, near, near, far); - - /* Prepare views at the same time for faster culling. */ - for (int i = 0; i < 6; i++) { - unit_m4(viewmat); - negate_v3_v3(viewmat[3], pos); - mul_m4_m4m4(viewmat, cubefacemat[i], viewmat); - - if (do_culling) { - if (views[i] == NULL) { - views[i] = DRW_view_create(viewmat, winmat, NULL, NULL, NULL); - } - else { - DRW_view_update(views[i], viewmat, winmat, NULL, NULL); - } - } - else { - if (views[i] == NULL) { - const DRWView *default_view = DRW_view_default_get(); - views[i] = DRW_view_create_sub(default_view, viewmat, winmat); - } - else { - DRW_view_update_sub(views[i], viewmat, winmat); - } - } - } - - for (int i = 0; i < 6; i++) { - DRW_view_set_active(views[i]); - callback(i, user_data); - } -} - -static void render_reflections(void (*callback)(int face, EEVEE_BakeRenderData *user_data), - EEVEE_BakeRenderData *user_data, - EEVEE_PlanarReflection *planar_data, - int ref_count) -{ - EEVEE_StorageList *stl = user_data->vedata->stl; - DRWView *main_view = stl->effects->taa_view; - DRWView **views = stl->g_data->planar_views; - /* Prepare views at the same time for faster culling. */ - for (int i = 0; i < ref_count; i++) { - lightbake_planar_ensure_view(&planar_data[i], main_view, &views[i]); - } - - for (int i = 0; i < ref_count; i++) { - DRW_view_set_active(views[i]); - callback(i, user_data); - } -} - -static void lightbake_render_world_face(int face, EEVEE_BakeRenderData *user_data) -{ - EEVEE_PassList *psl = user_data->vedata->psl; - struct GPUFrameBuffer **face_fb = user_data->face_fb; - - /* For world probe, we don't need to clear the color buffer - * since we render the background directly. */ - GPU_framebuffer_bind(face_fb[face]); - GPU_framebuffer_clear_depth(face_fb[face], 1.0f); - DRW_draw_pass(psl->probe_background); -} - -void EEVEE_lightbake_render_world(EEVEE_ViewLayerData *UNUSED(sldata), - EEVEE_Data *vedata, - struct GPUFrameBuffer *face_fb[6]) -{ - EEVEE_BakeRenderData brdata = { - .vedata = vedata, - .face_fb = face_fb, - }; - - render_cubemap(lightbake_render_world_face, &brdata, (float[3]){0.0f}, 1.0f, 10.0f, false); -} - -static void lightbake_render_scene_face(int face, EEVEE_BakeRenderData *user_data) -{ - EEVEE_ViewLayerData *sldata = user_data->sldata; - EEVEE_PassList *psl = user_data->vedata->psl; - EEVEE_PrivateData *g_data = user_data->vedata->stl->g_data; - DRWView **views = g_data->bake_views; - - struct GPUFrameBuffer **face_fb = user_data->face_fb; - - /* Be sure that cascaded shadow maps are updated. */ - EEVEE_shadows_draw(sldata, user_data->vedata, views[face]); - - GPU_framebuffer_bind(face_fb[face]); - GPU_framebuffer_clear_depth(face_fb[face], 1.0f); - - DRW_draw_pass(psl->depth_ps); - DRW_draw_pass(psl->probe_background); - DRW_draw_pass(psl->material_ps); - DRW_draw_pass(psl->material_sss_ps); /* Only output standard pass */ - DRW_draw_pass(psl->transparent_pass); -} - -void EEVEE_lightbake_render_scene(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - struct GPUFrameBuffer *face_fb[6], - const float pos[3], - float near_clip, - float far_clip) -{ - EEVEE_BakeRenderData brdata = { - .vedata = vedata, - .sldata = sldata, - .face_fb = face_fb, - }; - - render_cubemap(lightbake_render_scene_face, &brdata, pos, near_clip, far_clip, true); -} - -static void lightbake_render_scene_reflected(int layer, EEVEE_BakeRenderData *user_data) -{ - EEVEE_Data *vedata = user_data->vedata; - EEVEE_ViewLayerData *sldata = user_data->sldata; - EEVEE_PassList *psl = vedata->psl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_LightProbesInfo *pinfo = sldata->probes; - - GPU_framebuffer_ensure_config(&fbl->planarref_fb, - {GPU_ATTACHMENT_TEXTURE_LAYER(txl->planar_depth, layer), - GPU_ATTACHMENT_TEXTURE_LAYER(txl->planar_pool, layer)}); - - /* Use visibility info for this planar reflection. */ - pinfo->vis_data = pinfo->planar_vis_tests[layer]; - - /* Avoid using the texture attached to framebuffer when rendering. */ - /* XXX */ - GPUTexture *tmp_planar_pool = txl->planar_pool; - GPUTexture *tmp_planar_depth = txl->planar_depth; - txl->planar_pool = e_data.planar_pool_placeholder; - txl->planar_depth = e_data.depth_array_placeholder; - - DRW_stats_group_start("Planar Reflection"); - - /* Be sure that cascaded shadow maps are updated. */ - EEVEE_shadows_draw(sldata, vedata, stl->g_data->planar_views[layer]); - - GPU_framebuffer_bind(fbl->planarref_fb); - GPU_framebuffer_clear_depth(fbl->planarref_fb, 1.0); - - float prev_background_alpha = vedata->stl->g_data->background_alpha; - vedata->stl->g_data->background_alpha = 1.0f; - - /* Slight modification: we handle refraction as normal - * shading and don't do SSRefraction. */ - - DRW_draw_pass(psl->depth_clip_ps); - DRW_draw_pass(psl->depth_refract_clip_ps); - - DRW_draw_pass(psl->probe_background); - EEVEE_create_minmax_buffer(vedata, tmp_planar_depth, layer); - EEVEE_occlusion_compute(sldata, vedata); - - GPU_framebuffer_bind(fbl->planarref_fb); - - /* Shading pass */ - DRW_draw_pass(psl->material_ps); - DRW_draw_pass(psl->material_sss_ps); /* Only output standard pass */ - DRW_draw_pass(psl->material_refract_ps); - - /* Transparent */ - if (DRW_state_is_image_render()) { - /* Do the reordering only for offline because it can be costly. */ - DRW_pass_sort_shgroup_z(psl->transparent_pass); - } - DRW_draw_pass(psl->transparent_pass); - - DRW_stats_group_end(); - - /* Restore */ - txl->planar_pool = tmp_planar_pool; - txl->planar_depth = tmp_planar_depth; - - vedata->stl->g_data->background_alpha = prev_background_alpha; -} - -static void eevee_lightbake_render_scene_to_planars(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata) -{ - EEVEE_BakeRenderData brdata = { - .vedata = vedata, - .sldata = sldata, - }; - - render_reflections(lightbake_render_scene_reflected, - &brdata, - sldata->probes->planar_data, - sldata->probes->num_planar); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Filtering - * \{ */ - -void EEVEE_lightbake_filter_glossy(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - struct GPUTexture *rt_color, - struct GPUFrameBuffer *fb, - int probe_idx, - float intensity, - int maxlevel, - float filter_quality, - float firefly_fac) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_LightProbesInfo *pinfo = sldata->probes; - LightCache *light_cache = vedata->stl->g_data->light_cache; - - float target_size = (float)GPU_texture_width(rt_color); - - /* Max lod used from the render target probe */ - pinfo->lod_rt_max = log2_floor_u(target_size) - 2.0f; - pinfo->intensity_fac = intensity; - - /* Start fresh */ - GPU_framebuffer_ensure_config(&fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_NONE}); - - /* 2 - Let gpu create Mipmaps for Filtered Importance Sampling. */ - /* Bind next framebuffer to be able to gen. mips for probe_rt. */ - EEVEE_downsample_cube_buffer(vedata, rt_color, (int)(pinfo->lod_rt_max)); - - /* 3 - Render to probe array to the specified layer, do prefiltering. */ - int mipsize = GPU_texture_width(light_cache->cube_tx.tex); - for (int i = 0; i < maxlevel + 1; i++) { - float bias = 0.0f; - pinfo->texel_size = 1.0f / (float)mipsize; - pinfo->padding_size = (i == maxlevel) ? 0 : (float)(1 << (maxlevel - i - 1)); - pinfo->padding_size *= pinfo->texel_size; - pinfo->layer = probe_idx * 6; - pinfo->roughness = i / (float)maxlevel; - /* Disney Roughness */ - pinfo->roughness = square_f(pinfo->roughness); - /* Distribute Roughness across lod more evenly */ - pinfo->roughness = square_f(pinfo->roughness); - CLAMP(pinfo->roughness, 1e-4f, 0.9999f); /* Avoid artifacts */ - -#if 1 /* Variable Sample count and bias (fast) */ - switch (i) { - case 0: - pinfo->samples_len = 1.0f; - bias = -1.0f; - break; - case 1: - pinfo->samples_len = 32.0f; - bias = 1.0f; - break; - case 2: - pinfo->samples_len = 40.0f; - bias = 2.0f; - break; - case 3: - pinfo->samples_len = 64.0f; - bias = 2.0f; - break; - default: - pinfo->samples_len = 128.0f; - bias = 2.0f; - break; - } -#else /* Constant Sample count (slow) */ - pinfo->samples_len = 1024.0f; -#endif - /* Cannot go higher than HAMMERSLEY_SIZE */ - CLAMP(filter_quality, 1.0f, 8.0f); - pinfo->samples_len *= filter_quality; - - pinfo->lodfactor = bias + 0.5f * log(square_f(target_size) / pinfo->samples_len) / log(2); - pinfo->firefly_fac = (firefly_fac > 0.0) ? firefly_fac : 1e16; - - GPU_framebuffer_ensure_config(&fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE_MIP(light_cache->cube_tx.tex, i), - }); - GPU_framebuffer_bind(fb); - DRW_draw_pass(psl->probe_glossy_compute); - - mipsize /= 2; - CLAMP_MIN(mipsize, 1); - } -} - -void EEVEE_lightbake_filter_diffuse(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - struct GPUTexture *rt_color, - struct GPUFrameBuffer *fb, - int grid_offset, - float intensity) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_LightProbesInfo *pinfo = sldata->probes; - LightCache *light_cache = vedata->stl->g_data->light_cache; - - float target_size = (float)GPU_texture_width(rt_color); - - pinfo->intensity_fac = intensity; - - /* find cell position on the virtual 3D texture */ - /* NOTE : Keep in sync with load_irradiance_cell() */ -#if defined(IRRADIANCE_SH_L2) - int size[2] = {3, 3}; -#elif defined(IRRADIANCE_HL2) - const int size[2] = {3, 2}; - pinfo->samples_len = 1024.0f; -#endif - - int cell_per_row = GPU_texture_width(light_cache->grid_tx.tex) / size[0]; - int x = size[0] * (grid_offset % cell_per_row); - int y = size[1] * (grid_offset / cell_per_row); - -#ifndef IRRADIANCE_SH_L2 - /* Tweaking parameters to balance perf. vs precision */ - const float bias = 0.0f; - pinfo->lodfactor = bias + 0.5f * log(square_f(target_size) / pinfo->samples_len) / log(2); - pinfo->lod_rt_max = log2_floor_u(target_size) - 2.0f; -#else - pinfo->shres = 32; /* Less texture fetches & reduce branches */ - pinfo->lod_rt_max = 2.0f; /* Improve cache reuse */ -#endif - - /* Start fresh */ - GPU_framebuffer_ensure_config(&fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_NONE}); - - /* 4 - Compute diffuse irradiance */ - EEVEE_downsample_cube_buffer(vedata, rt_color, (int)(pinfo->lod_rt_max)); - - GPU_framebuffer_ensure_config( - &fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_LAYER(light_cache->grid_tx.tex, 0)}); - GPU_framebuffer_bind(fb); - GPU_framebuffer_viewport_set(fb, x, y, size[0], size[1]); - DRW_draw_pass(psl->probe_diffuse_compute); - GPU_framebuffer_viewport_reset(fb); -} - -void EEVEE_lightbake_filter_visibility(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - struct GPUTexture *UNUSED(rt_depth), - struct GPUFrameBuffer *fb, - int grid_offset, - float clipsta, - float clipend, - float vis_range, - float vis_blur, - int vis_size) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_LightProbesInfo *pinfo = sldata->probes; - LightCache *light_cache = vedata->stl->g_data->light_cache; - - pinfo->samples_len = 512.0f; /* TODO: refine. */ - pinfo->shres = vis_size; - pinfo->visibility_range = vis_range; - pinfo->visibility_blur = vis_blur; - pinfo->near_clip = -clipsta; - pinfo->far_clip = -clipend; - pinfo->texel_size = 1.0f / (float)vis_size; - - int cell_per_col = GPU_texture_height(light_cache->grid_tx.tex) / vis_size; - int cell_per_row = GPU_texture_width(light_cache->grid_tx.tex) / vis_size; - int x = vis_size * (grid_offset % cell_per_row); - int y = vis_size * ((grid_offset / cell_per_row) % cell_per_col); - int layer = 1 + ((grid_offset / cell_per_row) / cell_per_col); - - GPU_framebuffer_ensure_config( - &fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_LAYER(light_cache->grid_tx.tex, layer)}); - GPU_framebuffer_bind(fb); - GPU_framebuffer_viewport_set(fb, x, y, vis_size, vis_size); - DRW_draw_pass(psl->probe_visibility_compute); - GPU_framebuffer_viewport_reset(fb); -} - -/* Actually a simple down-sampling. */ -static void downsample_planar(void *vedata, int level) -{ - EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; - EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; - - const float *size = DRW_viewport_size_get(); - copy_v2_v2(stl->g_data->planar_texel_size, size); - for (int i = 0; i < level - 1; i++) { - stl->g_data->planar_texel_size[0] /= 2.0f; - stl->g_data->planar_texel_size[1] /= 2.0f; - min_ff(floorf(stl->g_data->planar_texel_size[0]), 1.0f); - min_ff(floorf(stl->g_data->planar_texel_size[1]), 1.0f); - } - invert_v2(stl->g_data->planar_texel_size); - - DRW_draw_pass(psl->probe_planar_downsample_ps); -} - -static void EEVEE_lightbake_filter_planar(EEVEE_Data *vedata) -{ - EEVEE_TextureList *txl = vedata->txl; - EEVEE_FramebufferList *fbl = vedata->fbl; - - DRW_stats_group_start("Planar Probe Downsample"); - - GPU_framebuffer_ensure_config(&fbl->planar_downsample_fb, - {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->planar_pool)}); - - GPU_framebuffer_recursive_downsample( - fbl->planar_downsample_fb, MAX_SCREEN_BUFFERS_LOD_LEVEL, &downsample_planar, vedata); - DRW_stats_group_end(); -} - -/** \} */ - -void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - EEVEE_LightProbesInfo *pinfo = sldata->probes; - - if (pinfo->num_planar == 0) { - /* Disable SSR if we cannot read previous frame */ - common_data->ssr_toggle = vedata->stl->g_data->valid_double_buffer; - common_data->prb_num_planar = 0; - return; - } - - float hiz_uv_scale_prev[2] = {UNPACK2(common_data->hiz_uv_scale)}; - - /* Temporary Remove all planar reflections (avoid lag effect). */ - common_data->prb_num_planar = 0; - /* Turn off ssr to avoid black specular */ - common_data->ssr_toggle = false; - common_data->ssrefract_toggle = false; - common_data->sss_toggle = false; - - common_data->ray_type = EEVEE_RAY_GLOSSY; - common_data->ray_depth = 1.0f; - /* Planar reflections are rendered at the `hiz` resolution, so no need to scaling. */ - copy_v2_fl(common_data->hiz_uv_scale, 1.0f); - - GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); - - /* Rendering happens here! */ - eevee_lightbake_render_scene_to_planars(sldata, vedata); - - /* Make sure no additional visibility check runs after this. */ - pinfo->vis_data.collection = NULL; - - GPU_uniformbuf_update(sldata->planar_ubo, &sldata->probes->planar_data); - - /* Restore */ - common_data->prb_num_planar = pinfo->num_planar; - common_data->ssr_toggle = true; - common_data->ssrefract_toggle = true; - common_data->sss_toggle = true; - copy_v2_v2(common_data->hiz_uv_scale, hiz_uv_scale_prev); - - /* Prefilter for SSR */ - if ((vedata->stl->effects->enabled_effects & EFFECT_SSR) != 0) { - EEVEE_lightbake_filter_planar(vedata); - } - - if (DRW_state_is_image_render()) { - /* Sort the transparent passes because planar reflections could have re-sorted them. */ - DRW_pass_sort_shgroup_z(vedata->psl->transparent_pass); - } - - /* Disable SSR if we cannot read previous frame */ - common_data->ssr_toggle = vedata->stl->g_data->valid_double_buffer; -} - -void EEVEE_lightprobes_refresh(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - LightCache *light_cache = vedata->stl->g_data->light_cache; - - if ((light_cache->flag & LIGHTCACHE_UPDATE_WORLD) && - (light_cache->flag & LIGHTCACHE_BAKED) == 0) { - EEVEE_lightbake_update_world_quick(sldata, vedata, scene_eval); - } -} - -void EEVEE_lightprobes_free(void) -{ - MEM_SAFE_FREE(e_data.format_probe_display_planar); - DRW_TEXTURE_FREE_SAFE(e_data.planar_pool_placeholder); - DRW_TEXTURE_FREE_SAFE(e_data.depth_placeholder); - DRW_TEXTURE_FREE_SAFE(e_data.depth_array_placeholder); -} diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c deleted file mode 100644 index 4ed968e2935..00000000000 --- a/source/blender/draw/engines/eevee/eevee_lights.c +++ /dev/null @@ -1,268 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup DNA - */ - -#include "BLI_sys_types.h" /* bool */ - -#include "BKE_object.h" - -#include "DEG_depsgraph_query.h" - -#include "eevee_private.h" - -void eevee_light_matrix_get(const EEVEE_Light *evli, float r_mat[4][4]) -{ - copy_v3_v3(r_mat[0], evli->rightvec); - copy_v3_v3(r_mat[1], evli->upvec); - negate_v3_v3(r_mat[2], evli->forwardvec); - copy_v3_v3(r_mat[3], evli->position); - r_mat[0][3] = 0.0f; - r_mat[1][3] = 0.0f; - r_mat[2][3] = 0.0f; - r_mat[3][3] = 1.0f; -} - -static float light_attenuation_radius_get(const Light *la, - float light_threshold, - float light_power) -{ - if (la->mode & LA_CUSTOM_ATTENUATION) { - return la->att_dist; - } - /* Compute the distance (using the inverse square law) - * at which the light power reaches the light_threshold. */ - return sqrtf(max_ff(1e-16, light_power / max_ff(1e-16, light_threshold))); -} - -static void light_shape_parameters_set(EEVEE_Light *evli, const Light *la, const float scale[3]) -{ - if (la->type == LA_SPOT) { - /* Spot size & blend */ - evli->sizex = scale[0] / scale[2]; - evli->sizey = scale[1] / scale[2]; - evli->spotsize = cosf(la->spotsize * 0.5f); - evli->spotblend = (1.0f - evli->spotsize) * la->spotblend; - evli->radius = max_ff(0.001f, la->area_size); - } - else if (la->type == LA_AREA) { - evli->sizex = max_ff(0.003f, la->area_size * scale[0] * 0.5f); - if (ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE)) { - evli->sizey = max_ff(0.003f, la->area_sizey * scale[1] * 0.5f); - } - else { - evli->sizey = max_ff(0.003f, la->area_size * scale[1] * 0.5f); - } - /* For volume point lighting. */ - evli->radius = max_ff(0.001f, hypotf(evli->sizex, evli->sizey) * 0.5f); - } - else if (la->type == LA_SUN) { - evli->radius = max_ff(0.001f, tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f)); - } - else { - evli->radius = max_ff(0.001f, la->area_size); - } -} - -static float light_shape_power_get(const Light *la, const EEVEE_Light *evli) -{ - float power; - /* Make illumination power constant */ - if (la->type == LA_AREA) { - power = 1.0f / (evli->sizex * evli->sizey * 4.0f * M_PI) * /* 1/(w*h*Pi) */ - 0.8f; /* XXX: Empirical, Fit cycles power. */ - if (ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) { - /* Scale power to account for the lower area of the ellipse compared to the surrounding - * rectangle. */ - power *= 4.0f / M_PI; - } - } - else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) { - power = 1.0f / (4.0f * evli->radius * evli->radius * M_PI * M_PI); /* `1/(4*(r^2)*(Pi^2))` */ - - /* for point lights (a.k.a radius == 0.0) */ - // power = M_PI * M_PI * 0.78; /* XXX: Empirical, Fit cycles power. */ - } - else { /* LA_SUN */ - power = 1.0f / (evli->radius * evli->radius * M_PI); - /* Make illumination power closer to cycles for bigger radii. Cycles uses a cos^3 term that we - * cannot reproduce so we account for that by scaling the light power. This function is the - * result of a rough manual fitting. */ - power += 1.0f / (2.0f * M_PI); /* `power *= 1 + (r^2)/2` */ - } - return power; -} - -static float light_shape_power_volume_get(const Light *la, - const EEVEE_Light *evli, - float area_power) -{ - /* Volume light is evaluated as point lights. Remove the shape power. */ - float power = 1.0f / area_power; - - if (la->type == LA_AREA) { - /* Match cycles. Empirical fit... must correspond to some constant. */ - power *= 0.0792f * M_PI; - /* This corrects for area light most representative point trick. The fit was found by - * reducing the average error compared to cycles. */ - float area = evli->sizex * evli->sizey; - float tmp = M_PI_2 / (M_PI_2 + sqrtf(area)); - /* Lerp between 1.0 and the limit (1 / pi). */ - power *= tmp + (1.0f - tmp) * M_1_PI; - } - else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) { - /* Match cycles. Empirical fit... must correspond to some constant. */ - power *= 0.0792f; - } - else { /* LA_SUN */ - /* Nothing to do. */ - } - return power; -} - -/* Update buffer with light data */ -static void eevee_light_setup(Object *ob, EEVEE_Light *evli) -{ - const Light *la = (Light *)ob->data; - float mat[4][4], scale[3]; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - const float light_threshold = draw_ctx->scene->eevee.light_threshold; - - /* Position */ - copy_v3_v3(evli->position, ob->obmat[3]); - - /* Color */ - copy_v3_v3(evli->color, &la->r); - - evli->diff = la->diff_fac; - evli->spec = la->spec_fac; - evli->volume = la->volume_fac; - - float max_power = max_fff(la->r, la->g, la->b) * fabsf(la->energy / 100.0f); - float surface_max_power = max_ff(evli->diff, evli->spec) * max_power; - float volume_max_power = evli->volume * max_power; - - /* Influence Radii. */ - float att_radius = light_attenuation_radius_get(la, light_threshold, surface_max_power); - float att_radius_volume = light_attenuation_radius_get(la, light_threshold, volume_max_power); - /* Take the inverse square of this distance. */ - evli->invsqrdist = 1.0f / max_ff(1e-4f, square_f(att_radius)); - evli->invsqrdist_volume = 1.0f / max_ff(1e-4f, square_f(att_radius_volume)); - - /* Vectors */ - normalize_m4_m4_ex(mat, ob->obmat, scale); - copy_v3_v3(evli->forwardvec, mat[2]); - normalize_v3(evli->forwardvec); - negate_v3(evli->forwardvec); - - copy_v3_v3(evli->rightvec, mat[0]); - normalize_v3(evli->rightvec); - - copy_v3_v3(evli->upvec, mat[1]); - normalize_v3(evli->upvec); - - /* Make sure we have a consistent Right Hand coord frame. - * (in case of negatively scaled Z axis) */ - float cross[3]; - cross_v3_v3v3(cross, evli->rightvec, evli->forwardvec); - if (dot_v3v3(cross, evli->upvec) < 0.0) { - negate_v3(evli->upvec); - } - - light_shape_parameters_set(evli, la, scale); - - /* Light Type */ - evli->light_type = (float)la->type; - if ((la->type == LA_AREA) && ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) { - evli->light_type = LAMPTYPE_AREA_ELLIPSE; - } - - float shape_power = light_shape_power_get(la, evli); - mul_v3_fl(evli->color, shape_power * la->energy); - - evli->volume *= light_shape_power_volume_get(la, evli, shape_power); - - /* No shadow by default */ - evli->shadow_id = -1.0f; -} - -void EEVEE_lights_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_LightsInfo *linfo = sldata->lights; - linfo->num_light = 0; - - EEVEE_shadows_cache_init(sldata, vedata); -} - -void EEVEE_lights_cache_add(EEVEE_ViewLayerData *sldata, Object *ob) -{ - EEVEE_LightsInfo *linfo = sldata->lights; - const Light *la = (Light *)ob->data; - - if (linfo->num_light >= MAX_LIGHT) { - printf("Too many lights in the scene !!!\n"); - return; - } - - /* Early out if light has no power. */ - if (la->energy == 0.0f || is_zero_v3(&la->r)) { - return; - } - - EEVEE_Light *evli = linfo->light_data + linfo->num_light; - eevee_light_setup(ob, evli); - - if (la->mode & LA_SHADOW) { - if (la->type == LA_SUN) { - EEVEE_shadows_cascade_add(linfo, evli, ob); - } - else if (ELEM(la->type, LA_SPOT, LA_LOCAL, LA_AREA)) { - EEVEE_shadows_cube_add(linfo, evli, ob); - } - } - - linfo->num_light++; -} - -void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_LightsInfo *linfo = sldata->lights; - - sldata->common_data.la_num_light = linfo->num_light; - - /* Clamp volume lights power. */ - float upper_bound = vedata->stl->effects->volume_light_clamp; - for (int i = 0; i < linfo->num_light; i++) { - EEVEE_Light *evli = linfo->light_data + i; - - float power = max_fff(UNPACK3(evli->color)) * evli->volume; - if (power > 0.0f && evli->light_type != LA_SUN) { - /* The limit of the power attenuation function when the distance to the light goes to 0 is - * `2 / r^2` where r is the light radius. We need to find the right radius that emits at most - * the volume light upper bound. Inverting the function we get: */ - float min_radius = 1.0f / sqrtf(0.5f * upper_bound / power); - /* Square it here to avoid a multiplication inside the shader. */ - evli->volume_radius = square_f(max_ff(min_radius, evli->radius)); - } - } - - GPU_uniformbuf_update(sldata->light_ubo, &linfo->light_data); -} diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c deleted file mode 100644 index 879a7b08eba..00000000000 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ /dev/null @@ -1,383 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - */ -#include "DRW_render.h" - -#include "BKE_camera.h" -#include "BKE_studiolight.h" - -#include "BLI_rand.h" -#include "BLI_rect.h" - -#include "DNA_screen_types.h" -#include "DNA_world_types.h" - -#include "DEG_depsgraph_query.h" - -#include "ED_screen.h" - -#include "GPU_material.h" - -#include "UI_resources.h" - -#include "eevee_lightcache.h" -#include "eevee_private.h" - -#include "draw_common.h" - -static void eevee_lookdev_lightcache_delete(EEVEE_Data *vedata) -{ - EEVEE_StorageList *stl = vedata->stl; - EEVEE_PrivateData *g_data = stl->g_data; - EEVEE_TextureList *txl = vedata->txl; - - MEM_SAFE_FREE(stl->lookdev_lightcache); - MEM_SAFE_FREE(stl->lookdev_grid_data); - MEM_SAFE_FREE(stl->lookdev_cube_data); - DRW_TEXTURE_FREE_SAFE(txl->lookdev_grid_tx); - DRW_TEXTURE_FREE_SAFE(txl->lookdev_cube_tx); - g_data->studiolight_index = -1; - g_data->studiolight_rot_z = 0.0f; -} - -static void eevee_lookdev_hdri_preview_init(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata) -{ - EEVEE_PassList *psl = vedata->psl; - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - DRWShadingGroup *grp; - - const EEVEE_EffectsInfo *effects = vedata->stl->effects; - struct GPUBatch *sphere = DRW_cache_sphere_get(effects->sphere_lod); - int mat_options = VAR_MAT_MESH | VAR_MAT_LOOKDEV; - - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS | - DRW_STATE_CULL_BACK; - - { - Material *ma = EEVEE_material_default_diffuse_get(); - GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options); - struct GPUShader *sh = GPU_material_get_shader(gpumat); - - DRW_PASS_CREATE(psl->lookdev_diffuse_pass, state); - grp = DRW_shgroup_create(sh, psl->lookdev_diffuse_pass); - EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false); - DRW_shgroup_add_material_resources(grp, gpumat); - DRW_shgroup_call(grp, sphere, NULL); - } - { - Material *ma = EEVEE_material_default_glossy_get(); - GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options); - struct GPUShader *sh = GPU_material_get_shader(gpumat); - - DRW_PASS_CREATE(psl->lookdev_glossy_pass, state); - grp = DRW_shgroup_create(sh, psl->lookdev_glossy_pass); - EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false); - DRW_shgroup_add_material_resources(grp, gpumat); - DRW_shgroup_call(grp, sphere, NULL); - } -} - -void EEVEE_lookdev_init(EEVEE_Data *vedata) -{ - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - const DRWContextState *draw_ctx = DRW_context_state_get(); - /* The view will be NULL when rendering previews. */ - const View3D *v3d = draw_ctx->v3d; - - if (eevee_hdri_preview_overlay_enabled(v3d)) { - /* Viewport / Spheres size. */ - const rcti *rect; - rcti fallback_rect; - if (DRW_state_is_opengl_render()) { - const float *vp_size = DRW_viewport_size_get(); - fallback_rect.xmax = vp_size[0]; - fallback_rect.ymax = vp_size[1]; - fallback_rect.xmin = fallback_rect.ymin = 0; - rect = &fallback_rect; - } - else { - rect = ED_region_visible_rect(draw_ctx->region); - } - - /* Make the viewport width scale the lookdev spheres a bit. - * Scale between 1000px and 2000px. */ - const float viewport_scale = clamp_f( - BLI_rcti_size_x(rect) / (2000.0f * U.dpi_fac), 0.5f, 1.0f); - const int sphere_size = U.lookdev_sphere_size * U.dpi_fac * viewport_scale; - - if (sphere_size != effects->sphere_size || rect->xmax != effects->anchor[0] || - rect->ymin != effects->anchor[1]) { - /* Make sphere resolution adaptive to viewport_scale, dpi and lookdev_sphere_size */ - float res_scale = clamp_f( - (U.lookdev_sphere_size / 400.0f) * viewport_scale * U.dpi_fac, 0.1f, 1.0f); - - if (res_scale > 0.7f) { - effects->sphere_lod = DRW_LOD_HIGH; - } - else if (res_scale > 0.25f) { - effects->sphere_lod = DRW_LOD_MEDIUM; - } - else { - effects->sphere_lod = DRW_LOD_LOW; - } - /* If sphere size or anchor point moves, reset TAA to avoid ghosting issue. - * This needs to happen early because we are changing taa_current_sample. */ - effects->sphere_size = sphere_size; - effects->anchor[0] = rect->xmax; - effects->anchor[1] = rect->ymin; - stl->g_data->valid_double_buffer = false; - EEVEE_temporal_sampling_reset(vedata); - } - } -} - -void EEVEE_lookdev_cache_init(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - DRWPass *pass, - EEVEE_LightProbesInfo *pinfo, - DRWShadingGroup **r_shgrp) -{ - EEVEE_StorageList *stl = vedata->stl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_EffectsInfo *effects = stl->effects; - EEVEE_PrivateData *g_data = stl->g_data; - const DRWContextState *draw_ctx = DRW_context_state_get(); - /* The view will be NULL when rendering previews. */ - const View3D *v3d = draw_ctx->v3d; - const Scene *scene = draw_ctx->scene; - - const bool probe_render = pinfo != NULL; - - effects->lookdev_view = NULL; - - if (eevee_hdri_preview_overlay_enabled(v3d)) { - eevee_lookdev_hdri_preview_init(vedata, sldata); - } - - if (LOOK_DEV_STUDIO_LIGHT_ENABLED(v3d)) { - const View3DShading *shading = &v3d->shading; - StudioLight *sl = BKE_studiolight_find(shading->lookdev_light, - STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE); - if (sl == NULL || (sl->flag & STUDIOLIGHT_TYPE_WORLD) == 0) { - return; - } - - GPUShader *shader = probe_render ? EEVEE_shaders_studiolight_probe_sh_get() : - EEVEE_shaders_studiolight_background_sh_get(); - - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - int cube_res = scene_eval->eevee.gi_cubemap_resolution; - - /* If one of the component is missing we start from scratch. */ - if ((stl->lookdev_grid_data == NULL) || (stl->lookdev_cube_data == NULL) || - (txl->lookdev_grid_tx == NULL) || (txl->lookdev_cube_tx == NULL) || - (g_data->light_cache && g_data->light_cache->ref_res != cube_res)) { - eevee_lookdev_lightcache_delete(vedata); - } - - if (stl->lookdev_lightcache == NULL) { -#if defined(IRRADIANCE_SH_L2) - int grid_res = 4; -#elif defined(IRRADIANCE_HL2) - int grid_res = 4; -#endif - - stl->lookdev_lightcache = EEVEE_lightcache_create( - 1, 1, cube_res, 8, (int[3]){grid_res, grid_res, 1}); - - /* XXX: Fix memleak. TODO: find out why. */ - MEM_SAFE_FREE(stl->lookdev_cube_mips); - - /* We do this to use a special light cache for lookdev. - * This light-cache needs to be per viewport. But we need to - * have correct freeing when the viewport is closed. So we - * need to reference all textures to the txl and the memblocks - * to the stl. */ - stl->lookdev_grid_data = stl->lookdev_lightcache->grid_data; - stl->lookdev_cube_data = stl->lookdev_lightcache->cube_data; - stl->lookdev_cube_mips = stl->lookdev_lightcache->cube_mips; - txl->lookdev_grid_tx = stl->lookdev_lightcache->grid_tx.tex; - txl->lookdev_cube_tx = stl->lookdev_lightcache->cube_tx.tex; - } - - g_data->light_cache = stl->lookdev_lightcache; - - DRWShadingGroup *grp = DRW_shgroup_create(shader, pass); - axis_angle_to_mat3_single(g_data->studiolight_matrix, 'Z', shading->studiolight_rot_z); - - float studiolight_matrix[3][3] = {{0.0f}}; - if (shading->flag & V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION) { - float view_matrix[4][4]; - float view_rot_matrix[3][3]; - float x_rot_matrix[3][3]; - DRW_view_viewmat_get(NULL, view_matrix, false); - copy_m3_m4(view_rot_matrix, view_matrix); - axis_angle_to_mat3_single(x_rot_matrix, 'X', M_PI / 2.0f); - mul_m3_m3m3(view_rot_matrix, x_rot_matrix, view_rot_matrix); - mul_m3_m3m3(view_rot_matrix, g_data->studiolight_matrix, view_rot_matrix); - copy_m3_m3(studiolight_matrix, view_rot_matrix); - } - - DRW_shgroup_uniform_mat3(grp, "StudioLightMatrix", g_data->studiolight_matrix); - - if (probe_render) { - /* Avoid artifact with equirectangular mapping. */ - eGPUSamplerState state = (GPU_SAMPLER_FILTER | GPU_SAMPLER_REPEAT_S); - DRW_shgroup_uniform_float_copy(grp, "studioLightIntensity", shading->studiolight_intensity); - BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE); - DRW_shgroup_uniform_texture_ex(grp, "studioLight", sl->equirect_radiance_gputexture, state); - /* Do not fade-out when doing probe rendering, only when drawing the background. */ - DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f); - } - else { - float background_alpha = g_data->background_alpha * shading->studiolight_background; - float studiolight_blur = powf(shading->studiolight_blur, 2.5f); - DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", background_alpha); - DRW_shgroup_uniform_float_copy(grp, "studioLightBlur", studiolight_blur); - DRW_shgroup_uniform_texture(grp, "probeCubes", txl->lookdev_cube_tx); - } - - /* Common UBOs are setup latter. */ - *r_shgrp = grp; - - /* Do we need to recalc the lightprobes? */ - if (g_data->studiolight_index != sl->index || - (shading->flag & V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION && - !equals_m3m3(g_data->studiolight_matrix, studiolight_matrix)) || - g_data->studiolight_rot_z != shading->studiolight_rot_z || - g_data->studiolight_intensity != shading->studiolight_intensity || - g_data->studiolight_cubemap_res != scene->eevee.gi_cubemap_resolution || - g_data->studiolight_glossy_clamp != scene->eevee.gi_glossy_clamp || - g_data->studiolight_filter_quality != scene->eevee.gi_filter_quality) { - stl->lookdev_lightcache->flag |= LIGHTCACHE_UPDATE_WORLD; - g_data->studiolight_index = sl->index; - copy_m3_m3(g_data->studiolight_matrix, studiolight_matrix); - g_data->studiolight_rot_z = shading->studiolight_rot_z; - g_data->studiolight_intensity = shading->studiolight_intensity; - g_data->studiolight_cubemap_res = scene->eevee.gi_cubemap_resolution; - g_data->studiolight_glossy_clamp = scene->eevee.gi_glossy_clamp; - g_data->studiolight_filter_quality = scene->eevee.gi_filter_quality; - } - } -} - -static void eevee_lookdev_apply_taa(const EEVEE_EffectsInfo *effects, - int sphere_size, - float winmat[4][4]) -{ - if (DRW_state_is_image_render() || ((effects->enabled_effects & EFFECT_TAA) != 0)) { - double ht_point[2]; - double ht_offset[2] = {0.0, 0.0}; - const uint ht_primes[2] = {2, 3}; - float ofs[2]; - - BLI_halton_2d(ht_primes, ht_offset, effects->taa_current_sample, ht_point); - EEVEE_temporal_sampling_offset_calc(ht_point, 1.5f, ofs); - winmat[3][0] += ofs[0] / sphere_size; - winmat[3][1] += ofs[1] / sphere_size; - } -} - -void EEVEE_lookdev_draw(EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; - EEVEE_EffectsInfo *effects = stl->effects; - EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); - - const DRWContextState *draw_ctx = DRW_context_state_get(); - - if (psl->lookdev_diffuse_pass && eevee_hdri_preview_overlay_enabled(draw_ctx->v3d)) { - /* Config renderer. */ - EEVEE_CommonUniformBuffer *common = &sldata->common_data; - common->la_num_light = 0; - common->prb_num_planar = 0; - common->prb_num_render_cube = 1; - common->prb_num_render_grid = 1; - common->ao_dist = 0.0f; - common->ao_factor = 0.0f; - common->ao_settings = 0.0f; - GPU_uniformbuf_update(sldata->common_ubo, common); - - /* override matrices */ - float winmat[4][4], viewmat[4][4]; - unit_m4(winmat); - /* Look through the negative Z. */ - negate_v3(winmat[2]); - - eevee_lookdev_apply_taa(effects, effects->sphere_size, winmat); - - /* "Remove" view matrix location. Leaving only rotation. */ - DRW_view_viewmat_get(NULL, viewmat, false); - zero_v3(viewmat[3]); - - if (effects->lookdev_view) { - /* When rendering just update the view. This avoids recomputing the culling. */ - DRW_view_update_sub(effects->lookdev_view, viewmat, winmat); - } - else { - /* Using default view bypasses the culling. */ - const DRWView *default_view = DRW_view_default_get(); - effects->lookdev_view = DRW_view_create_sub(default_view, viewmat, winmat); - } - - DRW_view_set_active(effects->lookdev_view); - - /* Find the right frame-buffers to render to. */ - GPUFrameBuffer *fb = (effects->target_buffer == fbl->effect_color_fb) ? fbl->main_fb : - fbl->effect_fb; - - DRW_stats_group_start("Look Dev"); - - GPU_framebuffer_bind(fb); - - const int sphere_margin = effects->sphere_size / 6.0f; - float offset[2] = {0.0f, sphere_margin}; - - offset[0] = effects->sphere_size + sphere_margin; - GPU_framebuffer_viewport_set(fb, - effects->anchor[0] - offset[0], - effects->anchor[1] + offset[1], - effects->sphere_size, - effects->sphere_size); - - DRW_draw_pass(psl->lookdev_diffuse_pass); - - offset[0] = (effects->sphere_size + sphere_margin) + - (sphere_margin + effects->sphere_size + sphere_margin); - GPU_framebuffer_viewport_set(fb, - effects->anchor[0] - offset[0], - effects->anchor[1] + offset[1], - effects->sphere_size, - effects->sphere_size); - - DRW_draw_pass(psl->lookdev_glossy_pass); - - GPU_framebuffer_viewport_reset(fb); - - DRW_stats_group_end(); - - DRW_view_set_active(NULL); - } -} diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.cc b/source/blender/draw/engines/eevee/eevee_lookdev.cc new file mode 100644 index 00000000000..881ab90343f --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_lookdev.cc @@ -0,0 +1,360 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2018, Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +#include "BKE_image.h" +#include "BKE_lib_id.h" +#include "BKE_node.h" +#include "BKE_studiolight.h" +#include "BKE_world.h" +#include "BLI_math_matrix.h" +#include "BLI_rect.h" +#include "DNA_userdef_types.h" +#include "ED_screen.h" +#include "NOD_shader.h" + +#include "eevee_instance.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Lookdev Nodetree + * + * \{ */ + +LookDevWorldNodeTree::LookDevWorldNodeTree() +{ + bNodeTree *ntree = ntreeAddTree(NULL, "Lookdev Nodetree", ntreeType_Shader->idname); + bNode *background = nodeAddStaticNode(NULL, ntree, SH_NODE_BACKGROUND); + bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_WORLD); + bNodeSocket *background_out = nodeFindSocket(background, SOCK_OUT, "Background"); + bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface"); + nodeAddLink(ntree, background, background_out, output, output_in); + nodeSetActive(ntree, output); + + /* Note that we do not populate the environment texture input. + * We plug the GPUTexture directly using the sampler binding name ("samp1"). */ + bNode *environment = nodeAddStaticNode(NULL, ntree, SH_NODE_TEX_ENVIRONMENT); + bNodeSocket *background_in = nodeFindSocket(background, SOCK_IN, "Color"); + bNodeSocket *environment_out = nodeFindSocket(environment, SOCK_OUT, "Color"); + nodeAddLink(ntree, environment, environment_out, background, background_in); + + strength_socket_ = + (bNodeSocketValueFloat *)nodeFindSocket(background, SOCK_IN, "Strength")->default_value; + + ntree_ = ntree; +} + +LookDevWorldNodeTree::~LookDevWorldNodeTree() +{ + ntreeFreeEmbeddedTree(ntree_); + MEM_SAFE_FREE(ntree_); +} + +/* Configure a default nodetree with the given parameters. */ +bNodeTree *LookDevWorldNodeTree::nodetree_get(float strength) +{ + /* WARNING: This function is not threadsafe. Which is not a problem for the moment. */ + strength_socket_->value = strength; + return ntree_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name LookDev Studiolight + * + * Light the scene using the studiolight hdri. Overrides the lightcache (if any) and + * use custom shader to draw the background. + * \{ */ + +void LookDev::init(const ivec2 &output_res, const rcti *render_border) +{ + StudioLight *studiolight = nullptr; + if (inst_.v3d) { + studiolight = BKE_studiolight_find(inst_.v3d->shading.lookdev_light, + STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE); + } + + if (inst_.use_studio_light() && studiolight && (studiolight->flag & STUDIOLIGHT_TYPE_WORLD)) { + const View3DShading &shading = inst_.v3d->shading; + studiolight_ = studiolight; + + /* Detect update. */ + if ((opacity_ != shading.studiolight_background) || (rotation_ != shading.studiolight_rot_z) || + (instensity_ != shading.studiolight_intensity) || (blur_ != shading.studiolight_blur) || + (view_rotation_ != ((shading.flag & V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION) != 0)) || + (studiolight_index_ != studiolight_->index)) { + opacity_ = shading.studiolight_background; + instensity_ = shading.studiolight_intensity; + blur_ = shading.studiolight_blur; + rotation_ = shading.studiolight_rot_z; + studiolight_index_ = studiolight_->index; + view_rotation_ = (shading.flag & V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION) != 0; + + inst_.sampling.reset(); + inst_.lightprobes.set_world_dirty(); + + /* Update the material. */ + GPU_material_free(&material); + } + } + else { + if (studiolight_ != nullptr) { + inst_.sampling.reset(); + inst_.lightprobes.set_world_dirty(); + } + studiolight_ = nullptr; + studiolight_index_ = -1; + + GPU_material_free(&material); + } + + if (do_overlay(output_res, render_border)) { + rcti rect; + if (DRW_state_is_opengl_render()) { + BLI_rcti_init(&rect, 0, output_res.x, 0, output_res.y); + } + else { + const DRWContextState *draw_ctx = DRW_context_state_get(); + rect = *ED_region_visible_rect(draw_ctx->region); + } + + /* Make the viewport width scale the lookdev spheres a bit. + * Scale between 1000px and 2000px. */ + float viewport_scale = clamp_f(BLI_rcti_size_x(&rect) / (2000.0f * U.dpi_fac), 0.5f, 1.0f); + int sphere_size = U.lookdev_sphere_size * U.dpi_fac * viewport_scale; + ivec2 anchor = ivec2(rect.xmax, rect.ymin); + + if (sphere_size != sphere_size_ || anchor != anchor_) { + /* Make sphere resolution adaptive to viewport_scale, dpi and lookdev_sphere_size */ + float res_scale = (U.lookdev_sphere_size / 400.0f) * viewport_scale * U.dpi_fac; + if (res_scale > 0.7f) { + sphere_lod_ = DRW_LOD_HIGH; + } + else if (res_scale > 0.25f) { + sphere_lod_ = DRW_LOD_MEDIUM; + } + else { + sphere_lod_ = DRW_LOD_LOW; + } + sphere_size_ = sphere_size; + anchor_ = anchor; + inst_.sampling.reset(); + } + } + else if (sphere_size_ != 0) { + sphere_size_ = 0; + inst_.sampling.reset(); + } +} + +bool LookDev::do_overlay(const ivec2 &output_res, const rcti *render_border) +{ + const View3D *v3d = inst_.v3d; + /* Only show the HDRI Preview in Shading Preview in the Viewport. */ + if (v3d == nullptr || v3d->shading.type != OB_MATERIAL) { + return false; + } + /* Only show the HDRI Preview when viewing the Combined render pass */ + if (v3d->shading.render_pass != SCE_PASS_COMBINED) { + return false; + } + if (v3d->flag2 & V3D_HIDE_OVERLAYS) { + return false; + } + if ((v3d->overlay.flag & V3D_OVERLAY_LOOK_DEV) == 0) { + return false; + } + if (inst_.camera.is_panoramic()) { + return false; + } + if (output_res != ivec2(BLI_rcti_size_x(render_border), BLI_rcti_size_y(render_border))) { + /* TODO(fclem) support this case. */ + return false; + } + return true; +} + +bool LookDev::sync_world(void) +{ + if (studiolight_ == nullptr) { + return false; + } + /* World light probes render. */ + bNodeTree *nodetree = world_tree.nodetree_get(instensity_); + GPUMaterial *gpumat = inst_.shaders.material_shader_get( + "LookDev", material, nodetree, MAT_PIPE_FORWARD, MAT_GEOM_WORLD, true); + + BKE_studiolight_ensure_flag(studiolight_, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE); + GPUTexture *gputex = studiolight_->equirect_radiance_gputexture; + + if (gputex == nullptr) { + return false; + } + inst_.shading_passes.background.sync(gpumat, gputex); + return true; +} + +void LookDev::rotation_get(mat4 r_mat) +{ + if (studiolight_ == nullptr) { + unit_m4(r_mat); + } + else { + axis_angle_to_mat4_single(r_mat, 'Z', rotation_); + } + + if (view_rotation_) { + float x_rot_matrix[4][4]; + const CameraData &cam = inst_.camera.data_get(); + axis_angle_to_mat4_single(x_rot_matrix, 'X', M_PI / 2.0f); + mul_m4_m4m4(x_rot_matrix, x_rot_matrix, cam.viewmat); + mul_m4_m4m4(r_mat, r_mat, x_rot_matrix); + } +} + +void LookDev::sync_background(void) +{ + if (studiolight_ == nullptr) { + return; + } + /* Viewport display. */ + background_ps_ = DRW_pass_create("LookDev.Background", DRW_STATE_WRITE_COLOR); + + GPUShader *sh = inst_.shaders.static_shader_get(LOOKDEV_BACKGROUND); + DRWShadingGroup *grp = DRW_shgroup_create(sh, background_ps_); + DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", inst_.lightprobes.cube_tx_ref_get()); + DRW_shgroup_uniform_block(grp, "lightprobes_info_block", inst_.lightprobes.info_ubo_get()); + DRW_shgroup_uniform_float_copy(grp, "blur", clamp_f(blur_, 0.0f, 0.99999f)); + DRW_shgroup_uniform_float_copy(grp, "opacity", opacity_); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); +} + +/* Renders background using lightcache. */ +bool LookDev::render_background(void) +{ + if (studiolight_ == nullptr) { + return false; + } + DRW_draw_pass(background_ps_); + return true; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name LookDev Reference spheres + * + * Render reference spheres into a separate framebuffer to not distrub the main rendering. + * The final texture is composited onto the render. + * \{ */ + +void LookDev::sync_overlay(void) +{ + if (sphere_size_ == 0) { + return; + } + + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS | + DRW_STATE_CULL_BACK; + overlay_ps_ = DRW_pass_create("LookDev.Overlay", state); + + GPUBatch *sphere = DRW_cache_sphere_get(sphere_lod_); + + const CameraData &cam = inst_.camera.data_get(); + LightModule &lights = inst_.lights; + LightProbeModule &lightprobes = inst_.lightprobes; + + /* Jitter for AA. */ + vec2 jitter = -0.5f + vec2(inst_.sampling.rng_get(SAMPLING_FILTER_U), + inst_.sampling.rng_get(SAMPLING_FILTER_V)); + + /* Matrix used to position the spheres in viewport space. */ + mat4 sphere_mat; + copy_m4_m4(sphere_mat, cam.viewmat); + + const float *viewport_size = DRW_viewport_size_get(); + const int sphere_margin = sphere_size_ / 6; + vec2 offset = vec2(0, sphere_margin); + + std::array<::Material *, 2> materials = {inst_.materials.diffuse_mat_, + inst_.materials.glossy_mat_}; + for (::Material *mat : materials) { + GPUMaterial *gpumat = inst_.shaders.material_shader_get( + mat, mat->nodetree, MAT_PIPE_FORWARD, MAT_GEOM_LOOKDEV, false); + DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, overlay_ps_); + lights.shgroup_resources(grp); + DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get()); + DRW_shgroup_uniform_block(grp, "grids_block", lightprobes.grid_ubo_get()); + DRW_shgroup_uniform_block(grp, "cubes_block", lightprobes.cube_ubo_get()); + DRW_shgroup_uniform_block(grp, "lightprobes_info_block", lightprobes.info_ubo_get()); + DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get()); + DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get()); + + offset.x -= sphere_size_ + sphere_margin; + + /* Pass 2D scale and bias factor in the last column. */ + vec2 scale = sphere_size_ / vec2(viewport_size); + vec2 bias = -1.0f + scale + 2.0f * (vec2(anchor_) + offset + jitter) / vec2(viewport_size); + copy_v4_fl4(sphere_mat[3], UNPACK2(scale), UNPACK2(bias)); + DRW_shgroup_call_obmat(grp, sphere, sphere_mat); + + offset.x -= sphere_margin; + } + + view_ = nullptr; +} + +/* Renders the reference spheres. */ +void LookDev::render_overlay(GPUFrameBuffer *fb) +{ + if (sphere_size_ == 0) { + return; + } + + const DRWView *active_view = DRW_view_get_active(); + + inst_.lightprobes.set_view(active_view, ivec2(0)); + inst_.lights.set_view(active_view, ivec2(0)); + + /* Create subview for correct shading. Sub because we don not care about culling. */ + const CameraData &cam = inst_.camera.data_get(); + mat4 winmat; + orthographic_m4(winmat, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); + if (view_) { + DRW_view_update_sub(view_, cam.viewmat, winmat); + } + else { + view_ = DRW_view_create_sub(active_view, cam.viewmat, winmat); + } + + DRW_view_set_active(view_); + + GPU_framebuffer_bind(fb); + DRW_draw_pass(overlay_ps_); + + DRW_view_set_active(active_view); +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.hh b/source/blender/draw/engines/eevee/eevee_lookdev.hh new file mode 100644 index 00000000000..092c33ed98c --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_lookdev.hh @@ -0,0 +1,112 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2018, Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +#pragma once + +#include "BKE_studiolight.h" +#include "DNA_world_types.h" + +#include "DRW_render.h" + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name Lookdev World Nodetree + * + * \{ */ + +class LookDevWorldNodeTree { + private: + bNodeTree *ntree_; + bNodeSocketValueFloat *strength_socket_; + + public: + LookDevWorldNodeTree(); + ~LookDevWorldNodeTree(); + + bNodeTree *nodetree_get(float strength); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Look Dev + * + * \{ */ + +class LookDev { + private: + Instance &inst_; + /** Nodetree used to render the world reflection cubemap and irradiance. */ + LookDevWorldNodeTree world_tree; + /** Compiled gpu material for the nodetree. Owned. */ + ListBase material = {nullptr, nullptr}; + /** Choosen studio light. */ + StudioLight *studiolight_ = nullptr; + int studiolight_index_ = -1; + /** Draw pass to draw the viewport background. */ + DRWPass *background_ps_ = nullptr; + /** Parameters. */ + float instensity_ = -1.0f; + float blur_ = -1.0f; + float opacity_ = -1.0f; + float rotation_ = -9999.0f; + bool view_rotation_ = false; + + /** Overlay (reference spheres). */ + DRWPass *overlay_ps_ = nullptr; + /** View based on main view with orthographic projection. Without this, shading is incorrect. */ + DRWView *view_ = nullptr; + /** Selected LOD of the sphere mesh. */ + eDRWLevelOfDetail sphere_lod_; + /** Screen space radius in pixels. */ + int sphere_size_ = 0; + /** Lower right corner of the area where we can start drawing. */ + ivec2 anchor_; + + public: + LookDev(Instance &inst) : inst_(inst){}; + ~LookDev() + { + GPU_material_free(&material); + }; + + void init(const ivec2 &output_res, const rcti *render_border); + + void sync_background(void); + bool sync_world(void); + void sync_overlay(void); + + bool render_background(void); + void render_overlay(GPUFrameBuffer *view_fb); + + void rotation_get(mat4 r_mat); + + private: + bool do_overlay(const ivec2 &output_res, const rcti *render_border); +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_lut.h b/source/blender/draw/engines/eevee/eevee_lut.h index 8e107adfe0a..847a7376ecd 100644 --- a/source/blender/draw/engines/eevee/eevee_lut.h +++ b/source/blender/draw/engines/eevee/eevee_lut.h @@ -23,9 +23,17 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + extern const float ltc_mat_ggx[64 * 64 * 4]; extern const float ltc_mag_ggx[64 * 64 * 2]; extern const float bsdf_split_sum_ggx[64 * 64 * 2]; extern const float ltc_disk_integral[64 * 64]; extern const float btdf_split_sum_ggx[16][64 * 64 * 2]; extern const float blue_noise[64 * 64][4]; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/engines/eevee/eevee_lut_gen.c b/source/blender/draw/engines/eevee/eevee_lut_gen.c deleted file mode 100644 index 770134d27f9..00000000000 --- a/source/blender/draw/engines/eevee/eevee_lut_gen.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2020, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - * - * EEVEE LUT generation: - * - * Routine to generate the LUT used by eevee stored in eevee_lut.h - * These functions are not to be used in the final executable. - */ - -#include "DRW_render.h" - -#include "BLI_fileops.h" -#include "BLI_rand.h" -#include "BLI_string_utils.h" - -#include "eevee_private.h" - -#define DO_FILE_OUTPUT 0 - -float *EEVEE_lut_update_ggx_brdf(int lut_size) -{ - DRWPass *pass = DRW_pass_create(__func__, DRW_STATE_WRITE_COLOR); - DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_ggx_lut_sh_get(), pass); - DRW_shgroup_uniform_float_copy(grp, "sampleCount", 64.0f); /* Actual sample count is squared. */ - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - GPUTexture *tex = DRW_texture_create_2d(lut_size, lut_size, GPU_RG16F, 0, NULL); - GPUFrameBuffer *fb = NULL; - GPU_framebuffer_ensure_config(&fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(tex), - }); - GPU_framebuffer_bind(fb); - DRW_draw_pass(pass); - GPU_FRAMEBUFFER_FREE_SAFE(fb); - - float *data = GPU_texture_read(tex, GPU_DATA_FLOAT, 0); - GPU_texture_free(tex); -#if DO_FILE_OUTPUT - /* Content is to be put inside eevee_lut.c */ - FILE *f = BLI_fopen("bsdf_split_sum_ggx.h", "w"); - fprintf(f, "const float bsdf_split_sum_ggx[%d * %d * 2] = {", lut_size, lut_size); - for (int i = 0; i < lut_size * lut_size * 2;) { - fprintf(f, "\n "); - for (int j = 0; j < 4; j++, i += 2) { - fprintf(f, "%ff, %ff, ", data[i], data[i + 1]); - } - } - fprintf(f, "\n};\n"); - fclose(f); -#endif - - return data; -} - -float *EEVEE_lut_update_ggx_btdf(int lut_size, int lut_depth) -{ - float roughness; - DRWPass *pass = DRW_pass_create(__func__, DRW_STATE_WRITE_COLOR); - DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_ggx_refraction_lut_sh_get(), pass); - DRW_shgroup_uniform_float_copy(grp, "sampleCount", 64.0f); /* Actual sample count is squared. */ - DRW_shgroup_uniform_float(grp, "z", &roughness, 1); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - GPUTexture *tex = DRW_texture_create_2d_array(lut_size, lut_size, lut_depth, GPU_RG16F, 0, NULL); - GPUFrameBuffer *fb = NULL; - for (int i = 0; i < lut_depth; i++) { - GPU_framebuffer_ensure_config(&fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE_LAYER(tex, i), - }); - GPU_framebuffer_bind(fb); - roughness = i / (lut_depth - 1.0f); - DRW_draw_pass(pass); - } - - GPU_FRAMEBUFFER_FREE_SAFE(fb); - - float *data = GPU_texture_read(tex, GPU_DATA_FLOAT, 0); - GPU_texture_free(tex); - -#if DO_FILE_OUTPUT - /* Content is to be put inside eevee_lut.c. Don't forget to format the output. */ - FILE *f = BLI_fopen("btdf_split_sum_ggx.h", "w"); - fprintf(f, "const float btdf_split_sum_ggx[%d][%d * %d * 2] = {", lut_depth, lut_size, lut_size); - fprintf(f, "\n "); - int ofs = 0; - for (int d = 0; d < lut_depth; d++) { - fprintf(f, "{\n"); - for (int i = 0; i < lut_size * lut_size * 2;) { - for (int j = 0; j < 4; j++, i += 2, ofs += 2) { - fprintf(f, "%ff, %ff, ", data[ofs], data[ofs + 1]); - } - fprintf(f, "\n "); - } - fprintf(f, "},\n"); - } - fprintf(f, "};\n"); - fclose(f); -#endif - - return data; -} diff --git a/source/blender/draw/engines/eevee/eevee_material.cc b/source/blender/draw/engines/eevee/eevee_material.cc new file mode 100644 index 00000000000..a185616d191 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_material.cc @@ -0,0 +1,335 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +#include "DNA_material_types.h" + +#include "BKE_lib_id.h" +#include "BKE_material.h" +#include "BKE_node.h" +#include "NOD_shader.h" + +#include "eevee_instance.hh" + +#include "eevee_material.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Default Material + * + * \{ */ + +DefaultSurfaceNodeTree::DefaultSurfaceNodeTree() +{ + bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname); + bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_PRINCIPLED); + bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_MATERIAL); + bNodeSocket *bsdf_out = nodeFindSocket(bsdf, SOCK_OUT, "BSDF"); + bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface"); + nodeAddLink(ntree, bsdf, bsdf_out, output, output_in); + nodeSetActive(ntree, output); + + color_socket_ = + (bNodeSocketValueRGBA *)nodeFindSocket(bsdf, SOCK_IN, "Base Color")->default_value; + metallic_socket_ = + (bNodeSocketValueFloat *)nodeFindSocket(bsdf, SOCK_IN, "Metallic")->default_value; + roughness_socket_ = + (bNodeSocketValueFloat *)nodeFindSocket(bsdf, SOCK_IN, "Roughness")->default_value; + specular_socket_ = + (bNodeSocketValueFloat *)nodeFindSocket(bsdf, SOCK_IN, "Specular")->default_value; + ntree_ = ntree; +} + +DefaultSurfaceNodeTree::~DefaultSurfaceNodeTree() +{ + ntreeFreeEmbeddedTree(ntree_); + MEM_SAFE_FREE(ntree_); +} + +/* Configure a default nodetree with the given material. */ +bNodeTree *DefaultSurfaceNodeTree::nodetree_get(::Material *ma) +{ + /* WARNING: This function is not threadsafe. Which is not a problem for the moment. */ + copy_v3_fl3(color_socket_->value, ma->r, ma->g, ma->b); + metallic_socket_->value = ma->metallic; + roughness_socket_->value = ma->roughness; + specular_socket_->value = ma->spec; + + return ntree_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material + * + * \{ */ + +MaterialModule::MaterialModule(Instance &inst) : inst_(inst) +{ + { + bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname); + + diffuse_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default diffuse"); + diffuse_mat_->nodetree = ntree; + diffuse_mat_->use_nodes = true; + /* To use the forward pipeline. */ + diffuse_mat_->blend_method = MA_BM_BLEND; + + bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_DIFFUSE); + bNodeSocket *base_color = nodeFindSocket(bsdf, SOCK_IN, "Color"); + copy_v3_fl(((bNodeSocketValueRGBA *)base_color->default_value)->value, 0.8f); + + bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_MATERIAL); + + nodeAddLink(ntree, + bsdf, + nodeFindSocket(bsdf, SOCK_OUT, "BSDF"), + output, + nodeFindSocket(output, SOCK_IN, "Surface")); + + nodeSetActive(ntree, output); + } + { + bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname); + + glossy_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default metal"); + glossy_mat_->nodetree = ntree; + glossy_mat_->use_nodes = true; + /* To use the forward pipeline. */ + glossy_mat_->blend_method = MA_BM_BLEND; + + bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_GLOSSY); + bNodeSocket *base_color = nodeFindSocket(bsdf, SOCK_IN, "Color"); + copy_v3_fl(((bNodeSocketValueRGBA *)base_color->default_value)->value, 1.0f); + bNodeSocket *roughness = nodeFindSocket(bsdf, SOCK_IN, "Roughness"); + ((bNodeSocketValueFloat *)roughness->default_value)->value = 0.0f; + + bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_MATERIAL); + + nodeAddLink(ntree, + bsdf, + nodeFindSocket(bsdf, SOCK_OUT, "BSDF"), + output, + nodeFindSocket(output, SOCK_IN, "Surface")); + + nodeSetActive(ntree, output); + } + { + bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname); + + error_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default error"); + error_mat_->nodetree = ntree; + error_mat_->use_nodes = true; + + /* Use emission and output material to be compatible with both World and Material. */ + bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_EMISSION); + bNodeSocket *color = nodeFindSocket(bsdf, SOCK_IN, "Color"); + copy_v3_fl3(((bNodeSocketValueRGBA *)color->default_value)->value, 1.0f, 0.0f, 1.0f); + + bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_MATERIAL); + + nodeAddLink(ntree, + bsdf, + nodeFindSocket(bsdf, SOCK_OUT, "Emission"), + output, + nodeFindSocket(output, SOCK_IN, "Surface")); + + nodeSetActive(ntree, output); + } +} + +MaterialModule::~MaterialModule() +{ + for (Material *mat : material_map_.values()) { + delete mat; + mat = nullptr; + } + for (DRWShadingGroup **shgroup : shader_map_.values()) { + delete shgroup; + shgroup = nullptr; + } + BKE_id_free(NULL, glossy_mat_); + BKE_id_free(NULL, diffuse_mat_); + BKE_id_free(NULL, error_mat_); +} + +void MaterialModule::begin_sync(void) +{ + queued_shaders_count_ = 0; + + for (Material *mat : material_map_.values()) { + mat->init = false; + } + for (DRWShadingGroup **shgroup : shader_map_.values()) { + *shgroup = nullptr; + } +} + +MaterialPass MaterialModule::material_pass_get(::Material *blender_mat, + eMaterialPipeline pipeline_type, + eMaterialGeometry geometry_type) +{ + bNodeTree *ntree = (blender_mat->use_nodes && blender_mat->nodetree != nullptr) ? + blender_mat->nodetree : + default_surface_ntree_.nodetree_get(blender_mat); + + MaterialPass matpass; + matpass.gpumat = inst_.shaders.material_shader_get( + blender_mat, ntree, pipeline_type, geometry_type, true); + + switch (GPU_material_status(matpass.gpumat)) { + case GPU_MAT_SUCCESS: + break; + case GPU_MAT_QUEUED: + queued_shaders_count_++; + blender_mat = (geometry_type == MAT_GEOM_VOLUME) ? BKE_material_default_volume() : + BKE_material_default_surface(); + matpass.gpumat = inst_.shaders.material_shader_get( + blender_mat, blender_mat->nodetree, pipeline_type, geometry_type, false); + break; + case GPU_MAT_FAILED: + default: + matpass.gpumat = inst_.shaders.material_shader_get( + error_mat_, error_mat_->nodetree, pipeline_type, geometry_type, false); + break; + } + /* Returned material should be ready to be drawn. */ + BLI_assert(GPU_material_status(matpass.gpumat) == GPU_MAT_SUCCESS); + + if (GPU_material_recalc_flag_get(matpass.gpumat)) { + inst_.sampling.reset(); + } + + if ((pipeline_type == MAT_PIPE_DEFERRED) && + GPU_material_flag_get(matpass.gpumat, GPU_MATFLAG_SHADER_TO_RGBA)) { + pipeline_type = MAT_PIPE_FORWARD; + } + + if ((pipeline_type == MAT_PIPE_FORWARD) && + GPU_material_flag_get(matpass.gpumat, GPU_MATFLAG_TRANSPARENT)) { + /* Transparent needs to use one shgroup per object to support reordering. */ + matpass.shgrp = inst_.shading_passes.material_add(blender_mat, matpass.gpumat, pipeline_type); + } + else { + ShaderKey shader_key(matpass.gpumat, geometry_type, pipeline_type); + + /* TODO(fclem) allocate in blocks to avoid memory fragmentation. */ + auto add_cb = [&]() { return new DRWShadingGroup *(); }; + DRWShadingGroup *&grp = *shader_map_.lookup_or_add_cb(shader_key, add_cb); + + if (grp == nullptr) { + /* First time encountering this shader. Create a shading group. */ + grp = inst_.shading_passes.material_add(blender_mat, matpass.gpumat, pipeline_type); + } + + if (grp != nullptr) { + /* Shading group for this shader already exists. Create a sub one for this material. */ + /* IMPORTANT: We always create a subgroup so that all subgroups are inserted after the + * first "empty" shgroup. This avoids messing the order of subgroups when there is more + * nested subgroup (i.e: hair drawing). */ + /* TODO(fclem) Remove material resource binding from the first group creation. */ + matpass.shgrp = DRW_shgroup_create_sub(grp); + DRW_shgroup_add_material_resources(matpass.shgrp, matpass.gpumat); + } + } + + return matpass; +} + +Material &MaterialModule::material_sync(::Material *blender_mat, eMaterialGeometry geometry_type) +{ + eMaterialPipeline surface_pipe = (blender_mat->blend_method == MA_BM_BLEND) ? MAT_PIPE_FORWARD : + MAT_PIPE_DEFERRED; + eMaterialPipeline prepass_pipe = (blender_mat->blend_method == MA_BM_BLEND) ? + MAT_PIPE_FORWARD_PREPASS : + MAT_PIPE_DEFERRED_PREPASS; + + MaterialKey material_key(blender_mat, geometry_type, surface_pipe); + + /* TODO allocate in blocks to avoid memory fragmentation. */ + auto add_cb = [&]() { return new Material(); }; + Material &mat = *material_map_.lookup_or_add_cb(material_key, add_cb); + + /* Forward pipeline needs to use one shgroup per object. */ + if (mat.init == false || (surface_pipe == MAT_PIPE_FORWARD)) { + mat.init = true; + /* Order is important for transparent. */ + mat.prepass = material_pass_get(blender_mat, prepass_pipe, geometry_type); + mat.shading = material_pass_get(blender_mat, surface_pipe, geometry_type); + mat.shadow = material_pass_get(blender_mat, MAT_PIPE_SHADOW, geometry_type); + + mat.is_alpha_blend_transparent = (blender_mat->blend_method == MA_BM_BLEND) && + GPU_material_flag_get(mat.prepass.gpumat, + GPU_MATFLAG_TRANSPARENT); + } + return mat; +} + +/* Return correct material or empty default material if slot is empty. */ +::Material *MaterialModule::material_from_slot(Object *ob, int slot) +{ + if (ob->base_flag & BASE_HOLDOUT) { + return BKE_material_default_holdout(); + } + ::Material *ma = BKE_object_material_get(ob, slot + 1); + if (ma == nullptr) { + if (ob->type == OB_VOLUME) { + return BKE_material_default_volume(); + } + else { + return BKE_material_default_surface(); + } + } + return ma; +} + +/* Return Material references are valid until the next call to this function or + * material_get(). */ +MaterialArray &MaterialModule::material_array_get(Object *ob) +{ + material_array_.materials.clear(); + material_array_.gpu_materials.clear(); + + const int materials_len = DRW_cache_object_material_count_get(ob); + + for (auto i : IndexRange(materials_len)) { + ::Material *blender_mat = material_from_slot(ob, i); + Material &mat = material_sync(blender_mat, to_material_geometry(ob)); + material_array_.materials.append(&mat); + material_array_.gpu_materials.append(mat.shading.gpumat); + } + return material_array_; +} + +/* Return Material references are valid until the next call to this function or + * material_array_get(). */ +Material &MaterialModule::material_get(Object *ob, int mat_nr, eMaterialGeometry geometry_type) +{ + ::Material *blender_mat = material_from_slot(ob, mat_nr); + Material &mat = material_sync(blender_mat, geometry_type); + return mat; +} + +/** \} */ + +} // namespace blender::eevee
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/eevee_material.hh b/source/blender/draw/engines/eevee/eevee_material.hh new file mode 100644 index 00000000000..df681209ce5 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_material.hh @@ -0,0 +1,123 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +#pragma once + +#include "DRW_render.h" + +#include "BLI_map.hh" +#include "BLI_vector.hh" +#include "GPU_material.h" + +#include "eevee_id_map.hh" + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name Default Material Nodetree + * + * In order to support materials without nodetree we reuse and configure a standalone nodetree that + * we pass for shader generation. The GPUMaterial is still stored inside the Material even if + * it does not use a nodetree. + * + * \{ */ + +class DefaultSurfaceNodeTree { + private: + bNodeTree *ntree_; + bNodeSocketValueRGBA *color_socket_; + bNodeSocketValueFloat *metallic_socket_; + bNodeSocketValueFloat *roughness_socket_; + bNodeSocketValueFloat *specular_socket_; + + public: + DefaultSurfaceNodeTree(); + ~DefaultSurfaceNodeTree(); + + bNodeTree *nodetree_get(::Material *ma); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material + * + * \{ */ + +struct MaterialPass { + GPUMaterial *gpumat = nullptr; + DRWShadingGroup *shgrp = nullptr; +}; + +struct Material { + bool init = false; + bool is_alpha_blend_transparent; + MaterialPass shadow, shading, prepass; +}; + +struct MaterialArray { + Vector<Material *> materials; + Vector<GPUMaterial *> gpu_materials; +}; + +class MaterialModule { + public: + ::Material *diffuse_mat_; + ::Material *glossy_mat_; + + private: + Instance &inst_; + + Map<MaterialKey, Material *> material_map_; + Map<ShaderKey, DRWShadingGroup **> shader_map_; + + MaterialArray material_array_; + + DefaultSurfaceNodeTree default_surface_ntree_; + + ::Material *error_mat_; + + int64_t queued_shaders_count_ = 0; + + public: + MaterialModule(Instance &inst); + ~MaterialModule(); + + void begin_sync(void); + + MaterialArray &material_array_get(Object *ob); + Material &material_get(Object *ob, int mat_nr, eMaterialGeometry geometry_type); + + private: + Material &material_sync(::Material *blender_mat, eMaterialGeometry geometry_type); + + ::Material *material_from_slot(Object *ob, int slot); + MaterialPass material_pass_get(::Material *blender_mat, + eMaterialPipeline pipeline_type, + eMaterialGeometry geometry_type); +}; + +/** \} */ + +} // namespace blender::eevee
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c deleted file mode 100644 index a027a29c813..00000000000 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ /dev/null @@ -1,1142 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - */ - -#include "DRW_render.h" - -#include "BLI_alloca.h" -#include "BLI_ghash.h" -#include "BLI_listbase.h" -#include "BLI_math_bits.h" -#include "BLI_memblock.h" -#include "BLI_rand.h" -#include "BLI_string_utils.h" - -#include "BKE_paint.h" -#include "BKE_particle.h" - -#include "DNA_hair_types.h" -#include "DNA_modifier_types.h" -#include "DNA_view3d_types.h" -#include "DNA_world_types.h" - -#include "GPU_material.h" - -#include "DEG_depsgraph_query.h" - -#include "eevee_engine.h" -#include "eevee_lut.h" -#include "eevee_private.h" - -/* *********** STATIC *********** */ -static struct { - /* 64*64 array texture containing all LUTs and other utilitarian arrays. - * Packing enables us to same precious textures slots. */ - struct GPUTexture *util_tex; - struct GPUTexture *noise_tex; - - float noise_offsets[3]; -} e_data = {NULL}; /* Engine data */ - -typedef struct EeveeMaterialCache { - struct DRWShadingGroup *depth_grp; - struct DRWShadingGroup *shading_grp; - struct DRWShadingGroup *shadow_grp; - struct GPUMaterial *shading_gpumat; - /* Meh, Used by hair to ensure draw order when calling DRW_shgroup_create_sub. - * Pointers to ghash values. */ - struct DRWShadingGroup **depth_grp_p; - struct DRWShadingGroup **shading_grp_p; - struct DRWShadingGroup **shadow_grp_p; -} EeveeMaterialCache; - -/* *********** FUNCTIONS *********** */ - -/* XXX TODO: define all shared resources in a shared place without duplication. */ -struct GPUTexture *EEVEE_materials_get_util_tex(void) -{ - return e_data.util_tex; -} - -void EEVEE_material_bind_resources(DRWShadingGroup *shgrp, - GPUMaterial *gpumat, - EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - const int *ssr_id, - const float *refract_depth, - bool use_ssrefraction, - bool use_alpha_blend) -{ - bool use_diffuse = GPU_material_flag_get(gpumat, GPU_MATFLAG_DIFFUSE); - bool use_glossy = GPU_material_flag_get(gpumat, GPU_MATFLAG_GLOSSY); - bool use_refract = GPU_material_flag_get(gpumat, GPU_MATFLAG_REFRACT); - - LightCache *lcache = vedata->stl->g_data->light_cache; - EEVEE_EffectsInfo *effects = vedata->stl->effects; - EEVEE_PrivateData *pd = vedata->stl->g_data; - - DRW_shgroup_uniform_block(shgrp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(shgrp, "grid_block", sldata->grid_ubo); - DRW_shgroup_uniform_block(shgrp, "planar_block", sldata->planar_ubo); - DRW_shgroup_uniform_block(shgrp, "light_block", sldata->light_ubo); - DRW_shgroup_uniform_block(shgrp, "shadow_block", sldata->shadow_ubo); - DRW_shgroup_uniform_block(shgrp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block_ref(shgrp, "renderpass_block", &pd->renderpass_ubo); - - DRW_shgroup_uniform_int_copy(shgrp, "outputSssId", 1); - DRW_shgroup_uniform_texture(shgrp, "utilTex", e_data.util_tex); - if (use_diffuse || use_glossy || use_refract) { - DRW_shgroup_uniform_texture_ref(shgrp, "shadowCubeTexture", &sldata->shadow_cube_pool); - DRW_shgroup_uniform_texture_ref(shgrp, "shadowCascadeTexture", &sldata->shadow_cascade_pool); - DRW_shgroup_uniform_texture_ref(shgrp, "maxzBuffer", &vedata->txl->maxzbuffer); - } - if ((use_diffuse || use_glossy) && !use_ssrefraction) { - DRW_shgroup_uniform_texture_ref(shgrp, "horizonBuffer", &effects->gtao_horizons); - } - if (use_diffuse) { - DRW_shgroup_uniform_texture_ref(shgrp, "irradianceGrid", &lcache->grid_tx.tex); - } - if (use_glossy || use_refract) { - DRW_shgroup_uniform_texture_ref(shgrp, "probeCubes", &lcache->cube_tx.tex); - } - if (use_glossy) { - DRW_shgroup_uniform_texture_ref(shgrp, "probePlanars", &vedata->txl->planar_pool); - DRW_shgroup_uniform_int_copy(shgrp, "outputSsrId", ssr_id ? *ssr_id : 0); - } - if (use_refract) { - DRW_shgroup_uniform_float_copy( - shgrp, "refractionDepth", (refract_depth) ? *refract_depth : 0.0); - if (use_ssrefraction) { - DRW_shgroup_uniform_texture_ref( - shgrp, "refractColorBuffer", &vedata->txl->filtered_radiance); - } - } - if (use_alpha_blend) { - DRW_shgroup_uniform_texture_ref(shgrp, "inScattering", &effects->volume_scatter); - DRW_shgroup_uniform_texture_ref(shgrp, "inTransmittance", &effects->volume_transmit); - } -} - -static void eevee_init_noise_texture(void) -{ - e_data.noise_tex = DRW_texture_create_2d(64, 64, GPU_RGBA16F, 0, (float *)blue_noise); -} - -#define RUNTIME_LUT_CREATION 0 - -static void eevee_init_util_texture(void) -{ - const int layers = 4 + 16; - float(*texels)[4] = MEM_mallocN(sizeof(float[4]) * 64 * 64 * layers, "utils texels"); - float(*texels_layer)[4] = texels; -#if RUNTIME_LUT_CREATION - float *bsdf_ggx_lut = EEVEE_lut_update_ggx_brdf(64); - float(*btdf_ggx_lut)[64 * 64 * 2] = (float(*)[64 * 64 * 2]) EEVEE_lut_update_ggx_btdf(64, 16); -#else - const float *bsdf_ggx_lut = bsdf_split_sum_ggx; - const float(*btdf_ggx_lut)[64 * 64 * 2] = btdf_split_sum_ggx; -#endif - - /* Copy ltc_mat_ggx into 1st layer */ - memcpy(texels_layer, ltc_mat_ggx, sizeof(float[4]) * 64 * 64); - texels_layer += 64 * 64; - - /* Copy bsdf_split_sum_ggx into 2nd layer red and green channels. - * Copy ltc_mag_ggx into 2nd layer blue and alpha channel. */ - for (int i = 0; i < 64 * 64; i++) { - texels_layer[i][0] = bsdf_ggx_lut[i * 2 + 0]; - texels_layer[i][1] = bsdf_ggx_lut[i * 2 + 1]; - texels_layer[i][2] = ltc_mag_ggx[i * 2 + 0]; - texels_layer[i][3] = ltc_mag_ggx[i * 2 + 1]; - } - texels_layer += 64 * 64; - - /* Copy blue noise in 3rd layer. */ - for (int i = 0; i < 64 * 64; i++) { - texels_layer[i][0] = blue_noise[i][0]; - texels_layer[i][1] = blue_noise[i][2]; - texels_layer[i][2] = cosf(blue_noise[i][1] * 2.0f * M_PI); - texels_layer[i][3] = sinf(blue_noise[i][1] * 2.0f * M_PI); - } - texels_layer += 64 * 64; - - /* Copy ltc_disk_integral in 4th layer. */ - for (int i = 0; i < 64 * 64; i++) { - texels_layer[i][0] = ltc_disk_integral[i]; - texels_layer[i][1] = 0.0; /* UNUSED */ - texels_layer[i][2] = 0.0; /* UNUSED */ - texels_layer[i][3] = 0.0; /* UNUSED */ - } - texels_layer += 64 * 64; - - /* Copy Refraction GGX LUT in layer 5 - 21 */ - for (int j = 0; j < 16; j++) { - for (int i = 0; i < 64 * 64; i++) { - texels_layer[i][0] = btdf_ggx_lut[j][i * 2 + 0]; - texels_layer[i][1] = btdf_ggx_lut[j][i * 2 + 1]; - texels_layer[i][2] = 0.0; /* UNUSED */ - texels_layer[i][3] = 0.0; /* UNUSED */ - } - texels_layer += 64 * 64; - } - - e_data.util_tex = DRW_texture_create_2d_array( - 64, 64, layers, GPU_RGBA16F, DRW_TEX_FILTER | DRW_TEX_WRAP, (float *)texels); - - MEM_freeN(texels); -#if RUNTIME_LUT_CREATION - MEM_freeN(bsdf_ggx_lut); - MEM_freeN(btdf_ggx_lut); -#endif -} - -void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const double offsets[3]) -{ - e_data.noise_offsets[0] = offsets[0]; - e_data.noise_offsets[1] = offsets[1]; - e_data.noise_offsets[2] = offsets[2]; - - GPU_framebuffer_bind(fbl->update_noise_fb); - DRW_draw_pass(psl->update_noise_pass); -} - -void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - EEVEE_StorageList *stl, - EEVEE_FramebufferList *fbl) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - EEVEE_PrivateData *g_data = stl->g_data; - - if (!e_data.util_tex) { - EEVEE_shaders_material_shaders_init(); - - eevee_init_util_texture(); - eevee_init_noise_texture(); - } - - if (!DRW_state_is_image_render() && ((stl->effects->enabled_effects & EFFECT_TAA) == 0)) { - sldata->common_data.alpha_hash_offset = 0.0f; - sldata->common_data.alpha_hash_scale = 1.0f; - } - else { - double r; - BLI_halton_1d(5, 0.0, stl->effects->taa_current_sample - 1, &r); - sldata->common_data.alpha_hash_offset = (float)r; - sldata->common_data.alpha_hash_scale = 0.01f; - } - - { - /* Update noise Frame-buffer. */ - GPU_framebuffer_ensure_config( - &fbl->update_noise_fb, - {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_LAYER(e_data.util_tex, 2)}); - } - - { - /* Create RenderPass UBO */ - if (sldata->renderpass_ubo.combined == NULL) { - EEVEE_RenderPassData data; - data = (EEVEE_RenderPassData){true, true, true, true, true, false, false, false, 0}; - sldata->renderpass_ubo.combined = GPU_uniformbuf_create_ex( - sizeof(data), &data, "renderpass_ubo.combined"); - - data = (EEVEE_RenderPassData){true, false, false, false, false, true, false, false, 0}; - sldata->renderpass_ubo.diff_color = GPU_uniformbuf_create_ex( - sizeof(data), &data, "renderpass_ubo.diff_color"); - - data = (EEVEE_RenderPassData){true, true, false, false, false, false, false, false, 0}; - sldata->renderpass_ubo.diff_light = GPU_uniformbuf_create_ex( - sizeof(data), &data, "renderpass_ubo.diff_light"); - - data = (EEVEE_RenderPassData){false, false, true, false, false, false, false, false, 0}; - sldata->renderpass_ubo.spec_color = GPU_uniformbuf_create_ex( - sizeof(data), &data, "renderpass_ubo.spec_color"); - - data = (EEVEE_RenderPassData){false, false, true, true, false, false, false, false, 0}; - sldata->renderpass_ubo.spec_light = GPU_uniformbuf_create_ex( - sizeof(data), &data, "renderpass_ubo.spec_light"); - - data = (EEVEE_RenderPassData){false, false, false, false, true, false, false, false, 0}; - sldata->renderpass_ubo.emit = GPU_uniformbuf_create_ex( - sizeof(data), &data, "renderpass_ubo.emit"); - - data = (EEVEE_RenderPassData){true, true, true, true, true, false, true, false, 0}; - sldata->renderpass_ubo.environment = GPU_uniformbuf_create_ex( - sizeof(data), &data, "renderpass_ubo.environment"); - } - - /* Used combined pass by default. */ - g_data->renderpass_ubo = sldata->renderpass_ubo.combined; - - { - g_data->num_aovs_used = 0; - if ((stl->g_data->render_passes & EEVEE_RENDER_PASS_AOV) != 0) { - EEVEE_RenderPassData data = {true, true, true, true, true, false, false, true, 0}; - if (stl->g_data->aov_hash == EEVEE_AOV_HASH_ALL) { - ViewLayer *view_layer = draw_ctx->view_layer; - int aov_index = 0; - LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) { - if ((aov->flag & AOV_CONFLICT) != 0) { - continue; - } - if (aov_index == MAX_AOVS) { - break; - } - data.renderPassAOVActive = EEVEE_renderpasses_aov_hash(aov); - if (sldata->renderpass_ubo.aovs[aov_index]) { - GPU_uniformbuf_update(sldata->renderpass_ubo.aovs[aov_index], &data); - } - else { - sldata->renderpass_ubo.aovs[aov_index] = GPU_uniformbuf_create_ex( - sizeof(data), &data, "renderpass_ubo.aovs"); - } - aov_index++; - } - g_data->num_aovs_used = aov_index; - } - else { - /* Rendering a single AOV in the 3d viewport */ - data.renderPassAOVActive = stl->g_data->aov_hash; - if (sldata->renderpass_ubo.aovs[0]) { - GPU_uniformbuf_update(sldata->renderpass_ubo.aovs[0], &data); - } - else { - sldata->renderpass_ubo.aovs[0] = GPU_uniformbuf_create_ex( - sizeof(data), &data, "renderpass_ubo.aovs"); - } - g_data->num_aovs_used = 1; - } - } - /* Free AOV UBO's that are not in use. */ - for (int aov_index = g_data->num_aovs_used; aov_index < MAX_AOVS; aov_index++) { - DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.aovs[aov_index]); - } - } - - /* HACK: EEVEE_material_get can create a new context. This can only be - * done when there is no active framebuffer. We do this here otherwise - * `EEVEE_renderpasses_output_init` will fail. It cannot be done in - * `EEVEE_renderpasses_init` as the `e_data.vertcode` can be uninitialized. - */ - if (g_data->render_passes & EEVEE_RENDER_PASS_ENVIRONMENT) { - struct Scene *scene = draw_ctx->scene; - struct World *wo = scene->world; - if (wo && wo->use_nodes) { - EEVEE_material_get(vedata, scene, NULL, wo, VAR_WORLD_BACKGROUND); - } - } - } -} - -void EEVEE_materials_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; - EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; - const DRWContextState *draw_ctx = DRW_context_state_get(); - - /* Create Material #GHash. */ - { - stl->g_data->material_hash = BLI_ghash_ptr_new("Eevee_material ghash"); - - if (sldata->material_cache == NULL) { - sldata->material_cache = BLI_memblock_create(sizeof(EeveeMaterialCache)); - } - else { - BLI_memblock_clear(sldata->material_cache, NULL); - } - } - - { - DRW_PASS_CREATE(psl->background_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); - - DRWShadingGroup *grp = NULL; - EEVEE_lookdev_cache_init(vedata, sldata, psl->background_ps, NULL, &grp); - - if (grp == NULL) { - Scene *scene = draw_ctx->scene; - World *world = (scene->world) ? scene->world : EEVEE_world_default_get(); - - const int options = VAR_WORLD_BACKGROUND; - struct GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, NULL, world, options); - - grp = DRW_shgroup_material_create(gpumat, psl->background_ps); - DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1); - } - - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); - DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); - DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); - DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &stl->g_data->renderpass_ubo); - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); - } - -#define EEVEE_PASS_CREATE(pass, state) \ - do { \ - DRW_PASS_CREATE(psl->pass##_ps, state); \ - DRW_PASS_CREATE(psl->pass##_cull_ps, state | DRW_STATE_CULL_BACK); \ - DRW_pass_link(psl->pass##_ps, psl->pass##_cull_ps); \ - } while (0) - -#define EEVEE_CLIP_PASS_CREATE(pass, state) \ - do { \ - DRWState st = state | DRW_STATE_CLIP_PLANES; \ - DRW_PASS_INSTANCE_CREATE(psl->pass##_clip_ps, psl->pass##_ps, st); \ - DRW_PASS_INSTANCE_CREATE( \ - psl->pass##_clip_cull_ps, psl->pass##_cull_ps, st | DRW_STATE_CULL_BACK); \ - DRW_pass_link(psl->pass##_clip_ps, psl->pass##_clip_cull_ps); \ - } while (0) - - { - DRWState state_depth = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; - DRWState state_shading = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_CLIP_PLANES; - DRWState state_sss = DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_ALWAYS; - - EEVEE_PASS_CREATE(depth, state_depth); - EEVEE_CLIP_PASS_CREATE(depth, state_depth); - - EEVEE_PASS_CREATE(depth_refract, state_depth); - EEVEE_CLIP_PASS_CREATE(depth_refract, state_depth); - - EEVEE_PASS_CREATE(material, state_shading); - EEVEE_PASS_CREATE(material_refract, state_shading); - EEVEE_PASS_CREATE(material_sss, state_shading | state_sss); - } - { - /* Renderpass accumulation. */ - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ADD_FULL; - /* Create an instance of each of these passes and link them together. */ - DRWPass *passes[] = { - psl->material_ps, - psl->material_cull_ps, - psl->material_sss_ps, - psl->material_sss_cull_ps, - }; - DRWPass *first = NULL, *last = NULL; - for (int i = 0; i < ARRAY_SIZE(passes); i++) { - DRWPass *pass = DRW_pass_create_instance("Renderpass Accumulation", passes[i], state); - if (first == NULL) { - first = last = pass; - } - else { - DRW_pass_link(last, pass); - last = pass; - } - } - psl->material_accum_ps = first; - - /* Same for background */ - DRW_PASS_INSTANCE_CREATE(psl->background_accum_ps, psl->background_ps, state); - } - { - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CLIP_PLANES; - DRW_PASS_CREATE(psl->transparent_pass, state); - } - { - DRW_PASS_CREATE(psl->update_noise_pass, DRW_STATE_WRITE_COLOR); - GPUShader *sh = EEVEE_shaders_update_noise_sh_get(); - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->update_noise_pass); - DRW_shgroup_uniform_texture(grp, "blueNoise", e_data.noise_tex); - DRW_shgroup_uniform_vec3(grp, "offsets", e_data.noise_offsets, 1); - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); - } -} - -BLI_INLINE void material_shadow(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Material *ma, - bool is_hair, - EeveeMaterialCache *emc) -{ - EEVEE_PrivateData *pd = vedata->stl->g_data; - EEVEE_PassList *psl = vedata->psl; - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - - if (ma->blend_shadow != MA_BS_NONE) { - /* Shadow Pass */ - const bool use_shadow_shader = ma->use_nodes && ma->nodetree && - ELEM(ma->blend_shadow, MA_BS_CLIP, MA_BS_HASHED); - int mat_options = VAR_MAT_MESH | VAR_MAT_DEPTH; - SET_FLAG_FROM_TEST(mat_options, use_shadow_shader, VAR_MAT_HASH); - SET_FLAG_FROM_TEST(mat_options, is_hair, VAR_MAT_HAIR); - GPUMaterial *gpumat = (use_shadow_shader) ? - EEVEE_material_get(vedata, scene, ma, NULL, mat_options) : - EEVEE_material_default_get(scene, ma, mat_options); - - /* Avoid possible confusion with depth pre-pass options. */ - int option = KEY_SHADOW; - SET_FLAG_FROM_TEST(option, is_hair, KEY_HAIR); - - /* Search for the same shaders usage in the pass. */ - struct GPUShader *sh = GPU_material_get_shader(gpumat); - void *cache_key = (char *)sh + option; - DRWShadingGroup *grp, **grp_p; - - if (BLI_ghash_ensure_p(pd->material_hash, cache_key, (void ***)&grp_p)) { - /* This GPUShader has already been used by another material. - * Add new shading group just after to avoid shader switching cost. */ - grp = DRW_shgroup_create_sub(*grp_p); - } - else { - *grp_p = grp = DRW_shgroup_create(sh, psl->shadow_pass); - EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false); - } - - DRW_shgroup_add_material_resources(grp, gpumat); - - emc->shadow_grp = grp; - emc->shadow_grp_p = grp_p; - } - else { - emc->shadow_grp = NULL; - emc->shadow_grp_p = NULL; - } -} - -static EeveeMaterialCache material_opaque(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Material *ma, - const bool is_hair) -{ - EEVEE_EffectsInfo *effects = vedata->stl->effects; - EEVEE_PrivateData *pd = vedata->stl->g_data; - EEVEE_PassList *psl = vedata->psl; - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - - const bool do_cull = !is_hair && (ma->blend_flag & MA_BL_CULL_BACKFACE) != 0; - const bool use_gpumat = (ma->use_nodes && ma->nodetree); - const bool use_ssrefract = use_gpumat && ((ma->blend_flag & MA_BL_SS_REFRACTION) != 0) && - ((effects->enabled_effects & EFFECT_REFRACT) != 0); - const bool use_depth_shader = use_gpumat && ELEM(ma->blend_method, MA_BM_CLIP, MA_BM_HASHED); - - /* HACK: Assume the struct will never be smaller than our variations. - * This allow us to only keep one ghash and avoid bigger keys comparisons/hashing. */ - void *key = (char *)ma + is_hair; - /* Search for other material instances (sharing the same Material data-block). */ - EeveeMaterialCache **emc_p, *emc; - if (BLI_ghash_ensure_p(pd->material_hash, key, (void ***)&emc_p)) { - return **emc_p; - } - - *emc_p = emc = BLI_memblock_alloc(sldata->material_cache); - - material_shadow(vedata, sldata, ma, is_hair, emc); - - { - /* Depth Pass */ - int mat_options = VAR_MAT_MESH | VAR_MAT_DEPTH; - SET_FLAG_FROM_TEST(mat_options, use_ssrefract, VAR_MAT_REFRACT); - SET_FLAG_FROM_TEST(mat_options, use_depth_shader, VAR_MAT_HASH); - SET_FLAG_FROM_TEST(mat_options, is_hair, VAR_MAT_HAIR); - GPUMaterial *gpumat = (use_depth_shader) ? - EEVEE_material_get(vedata, scene, ma, NULL, mat_options) : - EEVEE_material_default_get(scene, ma, mat_options); - - int option = 0; - SET_FLAG_FROM_TEST(option, do_cull, KEY_CULL); - SET_FLAG_FROM_TEST(option, use_ssrefract, KEY_REFRACT); - DRWPass *depth_ps = (DRWPass *[]){ - psl->depth_ps, - psl->depth_cull_ps, - psl->depth_refract_ps, - psl->depth_refract_cull_ps, - }[option]; - /* Hair are rendered inside the non-cull pass but needs to have a separate cache key. */ - SET_FLAG_FROM_TEST(option, is_hair, KEY_HAIR); - - /* Search for the same shaders usage in the pass. */ - struct GPUShader *sh = GPU_material_get_shader(gpumat); - void *cache_key = (char *)sh + option; - DRWShadingGroup *grp, **grp_p; - - if (BLI_ghash_ensure_p(pd->material_hash, cache_key, (void ***)&grp_p)) { - /* This GPUShader has already been used by another material. - * Add new shading group just after to avoid shader switching cost. */ - grp = DRW_shgroup_create_sub(*grp_p); - } - else { - *grp_p = grp = DRW_shgroup_create(sh, depth_ps); - EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false); - } - - DRW_shgroup_add_material_resources(grp, gpumat); - - emc->depth_grp = grp; - emc->depth_grp_p = grp_p; - } - { - /* Shading Pass */ - int mat_options = VAR_MAT_MESH; - SET_FLAG_FROM_TEST(mat_options, use_ssrefract, VAR_MAT_REFRACT); - SET_FLAG_FROM_TEST(mat_options, is_hair, VAR_MAT_HAIR); - GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options); - const bool use_sss = GPU_material_flag_get(gpumat, GPU_MATFLAG_SSS); - - int ssr_id = (((effects->enabled_effects & EFFECT_SSR) != 0) && !use_ssrefract) ? 1 : 0; - int option = (use_ssrefract ? 0 : (use_sss ? 1 : 2)) * 2 + do_cull; - DRWPass *shading_pass = (DRWPass *[]){ - psl->material_refract_ps, - psl->material_refract_cull_ps, - psl->material_sss_ps, - psl->material_sss_cull_ps, - psl->material_ps, - psl->material_cull_ps, - }[option]; - /* Hair are rendered inside the non-cull pass but needs to have a separate cache key */ - option = option * 2 + is_hair; - - /* Search for the same shaders usage in the pass. */ - /* HACK: Assume the struct will never be smaller than our variations. - * This allow us to only keep one ghash and avoid bigger keys comparisons/hashing. */ - BLI_assert(option <= 16); - struct GPUShader *sh = GPU_material_get_shader(gpumat); - void *cache_key = (char *)sh + option; - DRWShadingGroup *grp, **grp_p; - - if (BLI_ghash_ensure_p(pd->material_hash, cache_key, (void ***)&grp_p)) { - /* This GPUShader has already been used by another material. - * Add new shading group just after to avoid shader switching cost. */ - grp = DRW_shgroup_create_sub(*grp_p); - - /* Per material uniforms. */ - if (use_ssrefract) { - DRW_shgroup_uniform_float_copy(grp, "refractionDepth", ma->refract_depth); - } - } - else { - *grp_p = grp = DRW_shgroup_create(sh, shading_pass); - EEVEE_material_bind_resources( - grp, gpumat, sldata, vedata, &ssr_id, &ma->refract_depth, use_ssrefract, false); - } - DRW_shgroup_add_material_resources(grp, gpumat); - - if (use_sss) { - EEVEE_subsurface_add_pass(sldata, vedata, ma, grp, gpumat); - } - - emc->shading_grp = grp; - emc->shading_grp_p = grp_p; - emc->shading_gpumat = gpumat; - } - return *emc; -} - -static EeveeMaterialCache material_transparent(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Material *ma) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - EEVEE_PassList *psl = vedata->psl; - EEVEE_EffectsInfo *effects = vedata->stl->effects; - EeveeMaterialCache emc = {0}; - - const bool do_cull = (ma->blend_flag & MA_BL_CULL_BACKFACE) != 0; - const bool use_gpumat = ma->use_nodes && ma->nodetree; - const bool use_ssrefract = use_gpumat && ((ma->blend_flag & MA_BL_SS_REFRACTION) != 0) && - ((effects->enabled_effects & EFFECT_REFRACT) != 0); - const bool use_prepass = ((ma->blend_flag & MA_BL_HIDE_BACKFACE) != 0); - - DRWState cur_state; - DRWState all_state = (DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_CULL_BACK | - DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_DEPTH_EQUAL | - DRW_STATE_BLEND_CUSTOM); - - material_shadow(vedata, sldata, ma, false, &emc); - - if (use_prepass) { - /* Depth prepass */ - int mat_options = VAR_MAT_MESH | VAR_MAT_DEPTH; - GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options); - struct GPUShader *sh = GPU_material_get_shader(gpumat); - - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->transparent_pass); - - EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, true); - DRW_shgroup_add_material_resources(grp, gpumat); - - cur_state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; - cur_state |= (do_cull) ? DRW_STATE_CULL_BACK : 0; - - DRW_shgroup_state_disable(grp, all_state); - DRW_shgroup_state_enable(grp, cur_state); - - emc.depth_grp = grp; - } - { - /* Shading */ - int ssr_id = -1; /* TODO: transparent SSR. */ - int mat_options = VAR_MAT_MESH | VAR_MAT_BLEND; - SET_FLAG_FROM_TEST(mat_options, use_ssrefract, VAR_MAT_REFRACT); - GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options); - - DRWShadingGroup *grp = DRW_shgroup_create(GPU_material_get_shader(gpumat), - psl->transparent_pass); - - EEVEE_material_bind_resources( - grp, gpumat, sldata, vedata, &ssr_id, &ma->refract_depth, use_ssrefract, true); - DRW_shgroup_add_material_resources(grp, gpumat); - - cur_state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM; - cur_state |= (use_prepass) ? DRW_STATE_DEPTH_EQUAL : DRW_STATE_DEPTH_LESS_EQUAL; - cur_state |= (do_cull) ? DRW_STATE_CULL_BACK : 0; - - /* Disable other blend modes and use the one we want. */ - DRW_shgroup_state_disable(grp, all_state); - DRW_shgroup_state_enable(grp, cur_state); - - emc.shading_grp = grp; - emc.shading_gpumat = gpumat; - } - return emc; -} - -/* Return correct material or empty default material if slot is empty. */ -BLI_INLINE Material *eevee_object_material_get(Object *ob, int slot, bool holdout) -{ - if (holdout) { - return BKE_material_default_holdout(); - } - Material *ma = BKE_object_material_get_eval(ob, slot + 1); - if (ma == NULL) { - if (ob->type == OB_VOLUME) { - ma = BKE_material_default_volume(); - } - else { - ma = BKE_material_default_surface(); - } - } - return ma; -} - -BLI_INLINE EeveeMaterialCache eevee_material_cache_get( - EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob, int slot, bool is_hair) -{ - const bool holdout = (ob->base_flag & BASE_HOLDOUT) != 0; - EeveeMaterialCache matcache; - Material *ma = eevee_object_material_get(ob, slot, holdout); - switch (ma->blend_method) { - case MA_BM_BLEND: - if (!is_hair) { - matcache = material_transparent(vedata, sldata, ma); - break; - } - ATTR_FALLTHROUGH; - case MA_BM_SOLID: - case MA_BM_CLIP: - case MA_BM_HASHED: - default: - matcache = material_opaque(vedata, sldata, ma, is_hair); - break; - } - return matcache; -} - -static void eevee_hair_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob, - ParticleSystem *psys, - ModifierData *md, - int matnr, - bool *cast_shadow) -{ - EeveeMaterialCache matcache = eevee_material_cache_get(vedata, sldata, ob, matnr - 1, true); - - if (matcache.depth_grp) { - *matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp, NULL); - DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat); - } - if (matcache.shading_grp) { - *matcache.shading_grp_p = DRW_shgroup_hair_create_sub( - ob, psys, md, matcache.shading_grp, matcache.shading_gpumat); - DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat); - } - if (matcache.shadow_grp) { - *matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp, NULL); - DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat); - *cast_shadow = true; - } - - EEVEE_motion_blur_hair_cache_populate(sldata, vedata, ob, psys, md); -} - -#define ADD_SHGROUP_CALL(shgrp, ob, geom, oedata) \ - do { \ - if (oedata) { \ - DRW_shgroup_call_with_callback(shgrp, geom, ob, oedata); \ - } \ - else { \ - DRW_shgroup_call(shgrp, geom, ob); \ - } \ - } while (0) - -#define ADD_SHGROUP_CALL_SAFE(shgrp, ob, geom, oedata) \ - do { \ - if (shgrp) { \ - ADD_SHGROUP_CALL(shgrp, ob, geom, oedata); \ - } \ - } while (0) - -#define MATCACHE_AS_ARRAY(matcache, member, materials_len, output_array) \ - for (int i = 0; i < materials_len; i++) { \ - output_array[i] = matcache[i].member; \ - } - -void EEVEE_materials_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob, - bool *cast_shadow) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - - bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && - !DRW_state_is_image_render(); - - /* First get materials for this mesh. */ - if (ELEM(ob->type, OB_MESH, OB_SURF, OB_MBALL)) { - const int materials_len = DRW_cache_object_material_count_get(ob); - - EeveeMaterialCache *matcache = BLI_array_alloca(matcache, materials_len); - for (int i = 0; i < materials_len; i++) { - matcache[i] = eevee_material_cache_get(vedata, sldata, ob, i, false); - } - - /* Only support single volume material for now. */ - /* XXX We rely on the previously compiled surface shader - * to know if the material has a "volume nodetree". - */ - bool use_volume_material = (matcache[0].shading_gpumat && - GPU_material_has_volume_output(matcache[0].shading_gpumat)); - if ((ob->dt >= OB_SOLID) || DRW_state_is_scene_render()) { - if (use_sculpt_pbvh) { - struct DRWShadingGroup **shgrps_array = BLI_array_alloca(shgrps_array, materials_len); - - MATCACHE_AS_ARRAY(matcache, shading_grp, materials_len, shgrps_array); - DRW_shgroup_call_sculpt_with_materials(shgrps_array, materials_len, ob); - - MATCACHE_AS_ARRAY(matcache, depth_grp, materials_len, shgrps_array); - DRW_shgroup_call_sculpt_with_materials(shgrps_array, materials_len, ob); - - MATCACHE_AS_ARRAY(matcache, shadow_grp, materials_len, shgrps_array); - DRW_shgroup_call_sculpt_with_materials(shgrps_array, materials_len, ob); - } - else { - struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len); - MATCACHE_AS_ARRAY(matcache, shading_gpumat, materials_len, gpumat_array); - /* Get per-material split surface */ - struct GPUBatch **mat_geom = DRW_cache_object_surface_material_get( - ob, gpumat_array, materials_len); - - if (mat_geom) { - for (int i = 0; i < materials_len; i++) { - if (mat_geom[i] == NULL) { - continue; - } - - /* Do not render surface if we are rendering a volume object - * and do not have a surface closure. */ - if (use_volume_material && - (gpumat_array[i] && !GPU_material_has_surface_output(gpumat_array[i]))) { - continue; - } - - /* XXX TODO: rewrite this to include the dupli objects. - * This means we cannot exclude dupli objects from reflections!!! */ - EEVEE_ObjectEngineData *oedata = NULL; - if ((ob->base_flag & BASE_FROM_DUPLI) == 0) { - oedata = EEVEE_object_data_ensure(ob); - oedata->ob = ob; - oedata->test_data = &sldata->probes->vis_data; - } - - ADD_SHGROUP_CALL(matcache[i].shading_grp, ob, mat_geom[i], oedata); - ADD_SHGROUP_CALL_SAFE(matcache[i].depth_grp, ob, mat_geom[i], oedata); - ADD_SHGROUP_CALL_SAFE(matcache[i].shadow_grp, ob, mat_geom[i], oedata); - *cast_shadow = *cast_shadow || (matcache[i].shadow_grp != NULL); - } - } - } - - /* Motion Blur Vectors. */ - EEVEE_motion_blur_cache_populate(sldata, vedata, ob); - } - - /* Volumetrics */ - if (use_volume_material) { - EEVEE_volumes_cache_object_add(sldata, vedata, scene, ob); - } - } -} - -void EEVEE_particle_hair_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob, - bool *cast_shadow) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - - if (ob->type == OB_MESH) { - if (ob != draw_ctx->object_edit) { - LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { - if (md->type != eModifierType_ParticleSystem) { - continue; - } - ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; - if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) { - continue; - } - ParticleSettings *part = psys->part; - const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; - if (draw_as != PART_DRAW_PATH) { - continue; - } - eevee_hair_cache_populate(vedata, sldata, ob, psys, md, part->omat, cast_shadow); - } - } - } -} - -void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob, - bool *cast_shadow) -{ - eevee_hair_cache_populate(vedata, sldata, ob, NULL, NULL, HAIR_MATERIAL_NR, cast_shadow); -} - -void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_PrivateData *pd = vedata->stl->g_data; - EEVEE_EffectsInfo *effects = vedata->stl->effects; - - BLI_ghash_free(pd->material_hash, NULL, NULL); - pd->material_hash = NULL; - - SET_FLAG_FROM_TEST(effects->enabled_effects, effects->sss_surface_count > 0, EFFECT_SSS); -} - -void EEVEE_materials_free(void) -{ - DRW_TEXTURE_FREE_SAFE(e_data.util_tex); - DRW_TEXTURE_FREE_SAFE(e_data.noise_tex); -} - -/* -------------------------------------------------------------------- */ -/** \name Render Passes - * \{ */ - -void EEVEE_material_renderpasses_init(EEVEE_Data *vedata) -{ - EEVEE_PrivateData *pd = vedata->stl->g_data; - - /* For diffuse and glossy we calculate the final light + color buffer where we extract the - * light from by dividing by the color buffer. When one the light is requested we also tag - * the color buffer to do the extraction. */ - if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) { - pd->render_passes |= EEVEE_RENDER_PASS_DIFFUSE_COLOR; - } - if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) { - pd->render_passes |= EEVEE_RENDER_PASS_SPECULAR_COLOR; - } -} - -static void material_renderpass_init(GPUTexture **output_tx, const eGPUTextureFormat format) -{ - DRW_texture_ensure_fullscreen_2d(output_tx, format, 0); -} - -void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - EEVEE_TextureList *txl = vedata->txl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - EEVEE_PrivateData *pd = stl->g_data; - - /* Should be enough precision for many samples. */ - const eGPUTextureFormat texture_format = (tot_samples > 128) ? GPU_RGBA32F : GPU_RGBA16F; - - /* Create FrameBuffer. */ - GPU_framebuffer_ensure_config(&fbl->material_accum_fb, - {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_LEAVE}); - - if (pd->render_passes & EEVEE_RENDER_PASS_ENVIRONMENT) { - material_renderpass_init(&txl->env_accum, texture_format); - } - if (pd->render_passes & EEVEE_RENDER_PASS_EMIT) { - material_renderpass_init(&txl->emit_accum, texture_format); - } - if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_COLOR) { - material_renderpass_init(&txl->diff_color_accum, texture_format); - } - if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) { - material_renderpass_init(&txl->diff_light_accum, texture_format); - } - if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_COLOR) { - material_renderpass_init(&txl->spec_color_accum, texture_format); - } - if (pd->render_passes & EEVEE_RENDER_PASS_AOV) { - for (int aov_index = 0; aov_index < pd->num_aovs_used; aov_index++) { - material_renderpass_init(&txl->aov_surface_accum[aov_index], texture_format); - } - } - if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) { - material_renderpass_init(&txl->spec_light_accum, texture_format); - - if (effects->enabled_effects & EFFECT_SSR) { - EEVEE_reflection_output_init(sldata, vedata, tot_samples); - } - } -} - -static void material_renderpass_accumulate(EEVEE_EffectsInfo *effects, - EEVEE_FramebufferList *fbl, - DRWPass *renderpass, - DRWPass *renderpass2, - EEVEE_PrivateData *pd, - GPUTexture *output_tx, - struct GPUUniformBuf *renderpass_option_ubo) -{ - GPU_framebuffer_texture_attach(fbl->material_accum_fb, output_tx, 0, 0); - GPU_framebuffer_bind(fbl->material_accum_fb); - - if (effects->taa_current_sample == 1) { - const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPU_framebuffer_clear_color(fbl->material_accum_fb, clear); - } - - pd->renderpass_ubo = renderpass_option_ubo; - DRW_draw_pass(renderpass); - if (renderpass2) { - DRW_draw_pass(renderpass2); - } - - GPU_framebuffer_texture_detach(fbl->material_accum_fb, output_tx); -} - -void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_PassList *psl = vedata->psl; - EEVEE_PrivateData *pd = vedata->stl->g_data; - EEVEE_EffectsInfo *effects = vedata->stl->effects; - EEVEE_TextureList *txl = vedata->txl; - - if (fbl->material_accum_fb != NULL) { - DRWPass *material_accum_ps = psl->material_accum_ps; - DRWPass *background_accum_ps = psl->background_accum_ps; - if (pd->render_passes & EEVEE_RENDER_PASS_ENVIRONMENT) { - material_renderpass_accumulate(effects, - fbl, - background_accum_ps, - NULL, - pd, - txl->env_accum, - sldata->renderpass_ubo.environment); - } - if (pd->render_passes & EEVEE_RENDER_PASS_EMIT) { - material_renderpass_accumulate( - effects, fbl, material_accum_ps, NULL, pd, txl->emit_accum, sldata->renderpass_ubo.emit); - } - if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_COLOR) { - material_renderpass_accumulate(effects, - fbl, - material_accum_ps, - NULL, - pd, - txl->diff_color_accum, - sldata->renderpass_ubo.diff_color); - } - if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) { - material_renderpass_accumulate(effects, - fbl, - material_accum_ps, - NULL, - pd, - txl->diff_light_accum, - sldata->renderpass_ubo.diff_light); - - if (effects->enabled_effects & EFFECT_SSS) { - EEVEE_subsurface_output_accumulate(sldata, vedata); - } - } - if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_COLOR) { - bool prev_ssr = sldata->common_data.ssr_toggle; - if (prev_ssr) { - /* We need to disable ssr here so output radiance is not directed to the ssr buffer. */ - sldata->common_data.ssr_toggle = false; - GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); - } - material_renderpass_accumulate(effects, - fbl, - material_accum_ps, - NULL, - pd, - txl->spec_color_accum, - sldata->renderpass_ubo.spec_color); - if (prev_ssr) { - sldata->common_data.ssr_toggle = prev_ssr; - GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); - } - } - if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) { - material_renderpass_accumulate(effects, - fbl, - material_accum_ps, - NULL, - pd, - txl->spec_light_accum, - sldata->renderpass_ubo.spec_light); - - if (effects->enabled_effects & EFFECT_SSR) { - EEVEE_reflection_output_accumulate(sldata, vedata); - } - } - if (pd->render_passes & EEVEE_RENDER_PASS_AOV) { - for (int aov_index = 0; aov_index < pd->num_aovs_used; aov_index++) { - material_renderpass_accumulate(effects, - fbl, - material_accum_ps, - background_accum_ps, - pd, - txl->aov_surface_accum[aov_index], - sldata->renderpass_ubo.aovs[aov_index]); - } - } - /* Free unused aov textures. */ - for (int aov_index = pd->num_aovs_used; aov_index < MAX_AOVS; aov_index++) { - DRW_TEXTURE_FREE_SAFE(txl->aov_surface_accum[aov_index]); - } - - /* Restore default. */ - pd->renderpass_ubo = sldata->renderpass_ubo.combined; - GPU_framebuffer_bind(fbl->main_fb); - } -} - -/** \} */ diff --git a/source/blender/draw/engines/eevee/eevee_mesh.cc b/source/blender/draw/engines/eevee/eevee_mesh.cc new file mode 100644 index 00000000000..82c524fc616 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_mesh.cc @@ -0,0 +1,58 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +#include "eevee_instance.hh" + +namespace blender::eevee { + +void Instance::mesh_sync(Object *ob, ObjectHandle &ob_handle) +{ + MaterialArray &material_array = materials.material_array_get(ob); + + GPUBatch **mat_geom = DRW_cache_object_surface_material_get( + ob, material_array.gpu_materials.data(), material_array.gpu_materials.size()); + + if (mat_geom == nullptr) { + return; + } + + bool is_shadow_caster = false; + bool is_alpha_blend = false; + for (auto i : material_array.gpu_materials.index_range()) { + GPUBatch *geom = mat_geom[i]; + if (geom == nullptr) { + continue; + } + Material *material = material_array.materials[i]; + shgroup_geometry_call(material->shading.shgrp, ob, geom); + shgroup_geometry_call(material->prepass.shgrp, ob, geom); + shgroup_geometry_call(material->shadow.shgrp, ob, geom); + + is_shadow_caster = is_shadow_caster || material->shadow.shgrp != nullptr; + is_alpha_blend = is_alpha_blend || material->is_alpha_blend_transparent; + } + shading_passes.velocity.mesh_add(ob, ob_handle); + + shadows.sync_object(ob, ob_handle, is_shadow_caster, is_alpha_blend); +} + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_mist.c b/source/blender/draw/engines/eevee/eevee_mist.c deleted file mode 100644 index d4490d6fd4c..00000000000 --- a/source/blender/draw/engines/eevee/eevee_mist.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - * - * Implementation of Blender Mist pass. - * IMPORTANT: This is a "post process" of the Z depth so it will lack any transparent objects. - */ - -#include "DRW_engine.h" -#include "DRW_render.h" - -#include "DNA_world_types.h" - -#include "BLI_string_utils.h" - -#include "eevee_private.h" - -void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - EEVEE_FramebufferList *fbl = vedata->fbl; - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - EEVEE_TextureList *txl = vedata->txl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_PassList *psl = vedata->psl; - EEVEE_PrivateData *g_data = stl->g_data; - Scene *scene = draw_ctx->scene; - - /* Create FrameBuffer. */ - /* Should be enough precision for many samples. */ - DRW_texture_ensure_fullscreen_2d(&txl->mist_accum, GPU_R32F, 0); - - GPU_framebuffer_ensure_config(&fbl->mist_accum_fb, - {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->mist_accum)}); - - /* Mist settings. */ - if (scene && scene->world) { - g_data->mist_start = scene->world->miststa; - g_data->mist_inv_dist = (scene->world->mistdist > 0.0f) ? 1.0f / scene->world->mistdist : 0.0f; - - switch (scene->world->mistype) { - case WO_MIST_QUADRATIC: - g_data->mist_falloff = 2.0f; - break; - case WO_MIST_LINEAR: - g_data->mist_falloff = 1.0f; - break; - case WO_MIST_INVERSE_QUADRATIC: - g_data->mist_falloff = 0.5f; - break; - } - } - else { - float near = DRW_view_near_distance_get(NULL); - float far = DRW_view_far_distance_get(NULL); - /* Fallback */ - g_data->mist_start = near; - g_data->mist_inv_dist = 1.0f / fabsf(far - near); - g_data->mist_falloff = 1.0f; - } - - /* XXX ??!! WHY? If not it does not match cycles. */ - g_data->mist_falloff *= 0.5f; - - /* Create Pass and shgroup. */ - DRW_PASS_CREATE(psl->mist_accum_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD); - DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_effect_mist_sh_get(), - psl->mist_accum_ps); - DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - DRW_shgroup_uniform_vec3(grp, "mistSettings", &g_data->mist_start, 1); - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); -} - -void EEVEE_mist_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_PassList *psl = vedata->psl; - EEVEE_EffectsInfo *effects = vedata->stl->effects; - - if (fbl->mist_accum_fb != NULL) { - GPU_framebuffer_bind(fbl->mist_accum_fb); - - /* Clear texture. */ - if (effects->taa_current_sample == 1) { - const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPU_framebuffer_clear_color(fbl->mist_accum_fb, clear); - } - - DRW_draw_pass(psl->mist_accum_ps); - - /* Restore */ - GPU_framebuffer_bind(fbl->main_fb); - } -} diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c deleted file mode 100644 index 703518a32ec..00000000000 --- a/source/blender/draw/engines/eevee/eevee_motion_blur.c +++ /dev/null @@ -1,613 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - * - * Gather all screen space effects technique such as Bloom, Motion Blur, DoF, SSAO, SSR, ... - */ - -#include "DRW_render.h" - -#include "BLI_rand.h" -#include "BLI_string_utils.h" - -#include "BKE_animsys.h" -#include "BKE_camera.h" -#include "BKE_duplilist.h" -#include "BKE_object.h" -#include "BKE_screen.h" - -#include "DNA_anim_types.h" -#include "DNA_camera_types.h" -#include "DNA_mesh_types.h" -#include "DNA_modifier_types.h" -#include "DNA_particle_types.h" -#include "DNA_rigidbody_types.h" -#include "DNA_screen_types.h" - -#include "ED_screen.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" - -#include "GPU_batch.h" -#include "GPU_texture.h" -#include "eevee_private.h" - -int EEVEE_motion_blur_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_StorageList *stl = vedata->stl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_EffectsInfo *effects = stl->effects; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - - /* Viewport not supported for now. */ - if (!DRW_state_is_scene_render()) { - return 0; - } - - effects->motion_blur_max = max_ii(0, scene->eevee.motion_blur_max); - - if ((effects->motion_blur_max > 0) && (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED)) { - if (DRW_state_is_scene_render()) { - int mb_step = effects->motion_blur_step; - DRW_view_viewmat_get(NULL, effects->motion_blur.camera[mb_step].viewmat, false); - DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persmat, false); - DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persinv, true); - } - - const float *fs_size = DRW_viewport_size_get(); - const int tx_size[2] = { - 1 + ((int)fs_size[0] / EEVEE_VELOCITY_TILE_SIZE), - 1 + ((int)fs_size[1] / EEVEE_VELOCITY_TILE_SIZE), - }; - - effects->velocity_tiles_x_tx = DRW_texture_pool_query_2d( - tx_size[0], fs_size[1], GPU_RGBA16, &draw_engine_eevee_type); - GPU_framebuffer_ensure_config(&fbl->velocity_tiles_fb[0], - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(effects->velocity_tiles_x_tx), - }); - - effects->velocity_tiles_tx = DRW_texture_pool_query_2d( - tx_size[0], tx_size[1], GPU_RGBA16, &draw_engine_eevee_type); - GPU_framebuffer_ensure_config(&fbl->velocity_tiles_fb[1], - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(effects->velocity_tiles_tx), - }); - - return EFFECT_MOTION_BLUR | EFFECT_POST_BUFFER | EFFECT_VELOCITY_BUFFER; - } - return 0; -} - -void EEVEE_motion_blur_step_set(EEVEE_Data *vedata, int step) -{ - BLI_assert(step < 3); - vedata->stl->effects->motion_blur_step = step; -} - -static void eevee_motion_blur_sync_camera(EEVEE_Data *vedata) -{ - EEVEE_EffectsInfo *effects = vedata->stl->effects; - if (DRW_state_is_scene_render()) { - int mb_step = effects->motion_blur_step; - DRW_view_viewmat_get(NULL, effects->motion_blur.camera[mb_step].viewmat, false); - DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persmat, false); - DRW_view_persmat_get(NULL, effects->motion_blur.camera[mb_step].persinv, true); - } - - effects->motion_blur_near_far[0] = fabsf(DRW_view_near_distance_get(NULL)); - effects->motion_blur_near_far[1] = fabsf(DRW_view_far_distance_get(NULL)); -} - -void EEVEE_motion_blur_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - EEVEE_MotionBlurData *mb_data = &effects->motion_blur; - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - - if ((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0) { - const float *fs_size = DRW_viewport_size_get(); - const int tx_size[2] = { - GPU_texture_width(effects->velocity_tiles_tx), - GPU_texture_height(effects->velocity_tiles_tx), - }; - - eevee_motion_blur_sync_camera(vedata); - - DRWShadingGroup *grp; - { - DRW_PASS_CREATE(psl->velocity_tiles_x, DRW_STATE_WRITE_COLOR); - DRW_PASS_CREATE(psl->velocity_tiles, DRW_STATE_WRITE_COLOR); - - /* Create max velocity tiles in 2 passes. One for X and one for Y */ - GPUShader *sh = EEVEE_shaders_effect_motion_blur_velocity_tiles_sh_get(); - grp = DRW_shgroup_create(sh, psl->velocity_tiles_x); - DRW_shgroup_uniform_texture(grp, "velocityBuffer", effects->velocity_tx); - DRW_shgroup_uniform_ivec2_copy(grp, "velocityBufferSize", (int[2]){fs_size[0], fs_size[1]}); - DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1); - DRW_shgroup_uniform_vec2(grp, "viewportSizeInv", DRW_viewport_invert_size_get(), 1); - DRW_shgroup_uniform_ivec2_copy(grp, "gatherStep", (int[2]){1, 0}); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - grp = DRW_shgroup_create(sh, psl->velocity_tiles); - DRW_shgroup_uniform_texture(grp, "velocityBuffer", effects->velocity_tiles_x_tx); - DRW_shgroup_uniform_ivec2_copy(grp, "velocityBufferSize", (int[2]){tx_size[0], fs_size[1]}); - DRW_shgroup_uniform_ivec2_copy(grp, "gatherStep", (int[2]){0, 1}); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - /* Expand max tiles by keeping the max tile in each tile neighborhood. */ - DRW_PASS_CREATE(psl->velocity_tiles_expand[0], DRW_STATE_WRITE_COLOR); - DRW_PASS_CREATE(psl->velocity_tiles_expand[1], DRW_STATE_WRITE_COLOR); - for (int i = 0; i < 2; i++) { - GPUTexture *tile_tx = (i == 0) ? effects->velocity_tiles_tx : effects->velocity_tiles_x_tx; - GPUShader *sh_expand = EEVEE_shaders_effect_motion_blur_velocity_tiles_expand_sh_get(); - grp = DRW_shgroup_create(sh_expand, psl->velocity_tiles_expand[i]); - DRW_shgroup_uniform_ivec2_copy(grp, "velocityBufferSize", tx_size); - DRW_shgroup_uniform_texture(grp, "velocityBuffer", tile_tx); - DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1); - DRW_shgroup_uniform_vec2(grp, "viewportSizeInv", DRW_viewport_invert_size_get(), 1); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - } - } - { - DRW_PASS_CREATE(psl->motion_blur, DRW_STATE_WRITE_COLOR); - eGPUSamplerState state = 0; - int expand_steps = 1 + (max_ii(0, effects->motion_blur_max - 1) / EEVEE_VELOCITY_TILE_SIZE); - GPUTexture *tile_tx = (expand_steps & 1) ? effects->velocity_tiles_x_tx : - effects->velocity_tiles_tx; - - grp = DRW_shgroup_create(EEVEE_shaders_effect_motion_blur_sh_get(), psl->motion_blur); - DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); - DRW_shgroup_uniform_texture_ref_ex(grp, "colorBuffer", &effects->source_buffer, state); - DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &dtxl->depth, state); - DRW_shgroup_uniform_texture_ref_ex(grp, "velocityBuffer", &effects->velocity_tx, state); - DRW_shgroup_uniform_texture(grp, "tileMaxBuffer", tile_tx); - DRW_shgroup_uniform_float_copy(grp, "depthScale", scene->eevee.motion_blur_depth_scale); - DRW_shgroup_uniform_vec2(grp, "nearFar", effects->motion_blur_near_far, 1); - DRW_shgroup_uniform_bool_copy(grp, "isPerspective", DRW_view_is_persp_get(NULL)); - DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1); - DRW_shgroup_uniform_vec2(grp, "viewportSizeInv", DRW_viewport_invert_size_get(), 1); - DRW_shgroup_uniform_ivec2_copy(grp, "tileBufferSize", tx_size); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - } - { - DRW_PASS_CREATE(psl->velocity_object, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); - - grp = DRW_shgroup_create(EEVEE_shaders_effect_motion_blur_object_sh_get(), - psl->velocity_object); - DRW_shgroup_uniform_mat4(grp, "prevViewProjMatrix", mb_data->camera[MB_PREV].persmat); - DRW_shgroup_uniform_mat4(grp, "currViewProjMatrix", mb_data->camera[MB_CURR].persmat); - DRW_shgroup_uniform_mat4(grp, "nextViewProjMatrix", mb_data->camera[MB_NEXT].persmat); - - DRW_PASS_CREATE(psl->velocity_hair, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); - - mb_data->hair_grp = grp = DRW_shgroup_create(EEVEE_shaders_effect_motion_blur_hair_sh_get(), - psl->velocity_hair); - DRW_shgroup_uniform_mat4(grp, "prevViewProjMatrix", mb_data->camera[MB_PREV].persmat); - DRW_shgroup_uniform_mat4(grp, "currViewProjMatrix", mb_data->camera[MB_CURR].persmat); - DRW_shgroup_uniform_mat4(grp, "nextViewProjMatrix", mb_data->camera[MB_NEXT].persmat); - - DRW_pass_link(psl->velocity_object, psl->velocity_hair); - } - - EEVEE_motion_blur_data_init(mb_data); - } - else { - psl->motion_blur = NULL; - psl->velocity_object = NULL; - psl->velocity_hair = NULL; - } -} - -void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), - EEVEE_Data *vedata, - Object *ob, - ParticleSystem *psys, - ModifierData *md) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - DRWShadingGroup *grp = NULL; - - if (!DRW_state_is_scene_render() || psl->velocity_hair == NULL) { - return; - } - - /* For now we assume hair objects are always moving. */ - EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get( - &effects->motion_blur, ob, true); - - if (mb_data) { - int mb_step = effects->motion_blur_step; - /* Store transform. */ - DRW_hair_duplimat_get(ob, psys, md, mb_data->obmat[mb_step]); - - EEVEE_HairMotionData *mb_hair = EEVEE_motion_blur_hair_data_get(&effects->motion_blur, ob); - int psys_id = (md != NULL) ? BLI_findindex(&ob->modifiers, md) : 0; - - if (psys_id >= mb_hair->psys_len) { - /* This should never happen. It means the modifier list was changed by frame evaluation. */ - BLI_assert(0); - return; - } - - if (mb_step == MB_CURR) { - /* Fill missing matrices if the object was hidden in previous or next frame. */ - if (is_zero_m4(mb_data->obmat[MB_PREV])) { - copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]); - } - if (is_zero_m4(mb_data->obmat[MB_NEXT])) { - copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]); - } - - GPUTexture *tex_prev = mb_hair->psys[psys_id].hair_pos_tx[MB_PREV]; - GPUTexture *tex_next = mb_hair->psys[psys_id].hair_pos_tx[MB_NEXT]; - - grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp, NULL); - DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]); - DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]); - DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]); - DRW_shgroup_uniform_texture(grp, "prvBuffer", tex_prev); - DRW_shgroup_uniform_texture(grp, "nxtBuffer", tex_next); - DRW_shgroup_uniform_bool(grp, "useDeform", &mb_hair->use_deform, 1); - } - else { - /* Store vertex position buffer. */ - mb_hair->psys[psys_id].hair_pos[mb_step] = DRW_hair_pos_buffer_get(ob, psys, md); - mb_hair->use_deform = true; - } - } -} - -void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), - EEVEE_Data *vedata, - Object *ob) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - DRWShadingGroup *grp = NULL; - - if (!DRW_state_is_scene_render() || psl->velocity_object == NULL) { - return; - } - - RigidBodyOb *rbo = ob->rigidbody_object; - - /* active rigidbody objects only, as only those are affected by sim. */ - const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE)); -#if 0 - /* For now we assume dupli objects are moving. */ - const bool is_dupli = (ob->base_flag & BASE_FROM_DUPLI) != 0; - const bool object_moves = is_dupli || has_rigidbody || BKE_object_moves_in_time(ob, true); -#else - /* BKE_object_moves_in_time does not work in some cases. - * Better detect non moving object after evaluation. */ - const bool object_moves = true; -#endif - const bool is_deform = BKE_object_is_deform_modified(DRW_context_state_get()->scene, ob) || - (has_rigidbody && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0); - - if (!(object_moves || is_deform)) { - return; - } - - const DupliObject *dup = DRW_object_get_dupli(ob); - if (dup != NULL && dup->ob->data != dup->ob_data) { - /* Geometry instances do not support motion blur correctly yet. The #key used in - * #motion_blur_deform_data_get has to take ids of instances (#DupliObject.persistent_id) into - * account. Otherwise it can't find matching geometry instances at different points in time. */ - return; - } - - EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get( - &effects->motion_blur, ob, false); - - if (mb_data) { - int mb_step = effects->motion_blur_step; - /* Store transform. */ - copy_m4_m4(mb_data->obmat[mb_step], ob->obmat); - - EEVEE_GeometryMotionData *mb_geom = EEVEE_motion_blur_geometry_data_get(&effects->motion_blur, - ob); - - if (mb_step == MB_CURR) { - GPUBatch *batch = DRW_cache_object_surface_get(ob); - if (batch == NULL) { - return; - } - - /* Fill missing matrices if the object was hidden in previous or next frame. */ - if (is_zero_m4(mb_data->obmat[MB_PREV])) { - copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]); - } - if (is_zero_m4(mb_data->obmat[MB_NEXT])) { - copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]); - } - - if (mb_geom->use_deform) { - /* Keep to modify later (after init). */ - mb_geom->batch = batch; - } - - /* Avoid drawing object that has no motions since object_moves is always true. */ - if (!mb_geom->use_deform && /* Object deformation can happen without transform. */ - equals_m4m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]) && - equals_m4m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR])) { - return; - } - - grp = DRW_shgroup_create(EEVEE_shaders_effect_motion_blur_object_sh_get(), - psl->velocity_object); - DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]); - DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]); - DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]); - DRW_shgroup_uniform_bool(grp, "useDeform", &mb_geom->use_deform, 1); - - DRW_shgroup_call(grp, batch, ob); - } - else if (is_deform) { - /* Store vertex position buffer. */ - mb_geom->vbo[mb_step] = DRW_cache_object_pos_vertbuf_get(ob); - mb_geom->use_deform = (mb_geom->vbo[mb_step] != NULL); - } - else { - mb_geom->vbo[mb_step] = NULL; - mb_geom->use_deform = false; - } - } -} - -static void motion_blur_remove_vbo_reference_from_batch(GPUBatch *batch, - GPUVertBuf *vbo1, - GPUVertBuf *vbo2) -{ - - for (int i = 0; i < GPU_BATCH_VBO_MAX_LEN; i++) { - if (ELEM(batch->verts[i], vbo1, vbo2)) { - /* Avoid double reference of the VBOs. */ - batch->verts[i] = NULL; - } - } -} - -void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata) -{ - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - GHashIterator ghi; - - if ((effects->enabled_effects & EFFECT_MOTION_BLUR) == 0) { - return; - } - - int mb_step = effects->motion_blur_step; - - if (mb_step != MB_CURR) { - /* Push instances attributes to the GPU. */ - DRW_render_instance_buffer_finish(); - - /* Need to be called after #DRW_render_instance_buffer_finish() */ - /* Also we weed to have a correct FBO bound for #DRW_hair_update. */ - GPU_framebuffer_bind(vedata->fbl->main_fb); - DRW_hair_update(); - - DRW_cache_restart(); - } - - for (BLI_ghashIterator_init(&ghi, effects->motion_blur.geom); - BLI_ghashIterator_done(&ghi) == false; - BLI_ghashIterator_step(&ghi)) { - EEVEE_GeometryMotionData *mb_geom = BLI_ghashIterator_getValue(&ghi); - EEVEE_HairMotionData *mb_hair = (EEVEE_HairMotionData *)mb_geom; - - if (!mb_geom->use_deform) { - continue; - } - - switch (mb_geom->type) { - case EEVEE_MOTION_DATA_HAIR: - if (mb_step == MB_CURR) { - /* TODO(fclem): Check if vertex count mismatch. */ - mb_hair->use_deform = true; - } - else { - for (int i = 0; i < mb_hair->psys_len; i++) { - if (mb_hair->psys[i].hair_pos[mb_step] == NULL) { - continue; - } - mb_hair->psys[i].hair_pos[mb_step] = GPU_vertbuf_duplicate( - mb_hair->psys[i].hair_pos[mb_step]); - - /* Create vbo immediately to bind to texture buffer. */ - GPU_vertbuf_use(mb_hair->psys[i].hair_pos[mb_step]); - - mb_hair->psys[i].hair_pos_tx[mb_step] = GPU_texture_create_from_vertbuf( - "hair_pos_motion_blur", mb_hair->psys[i].hair_pos[mb_step]); - } - } - break; - - case EEVEE_MOTION_DATA_MESH: - if (mb_step == MB_CURR) { - /* Modify batch to have data from adjacent frames. */ - GPUBatch *batch = mb_geom->batch; - for (int i = 0; i < MB_CURR; i++) { - GPUVertBuf *vbo = mb_geom->vbo[i]; - if (vbo && batch) { - if (GPU_vertbuf_get_vertex_len(vbo) != GPU_vertbuf_get_vertex_len(batch->verts[0])) { - /* Vertex count mismatch, disable deform motion blur. */ - mb_geom->use_deform = false; - } - - if (mb_geom->use_deform == false) { - motion_blur_remove_vbo_reference_from_batch( - batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]); - - GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]); - GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_NEXT]); - break; - } - - GPU_batch_vertbuf_add_ex(batch, vbo, false); - } - } - } - else { - GPUVertBuf *vbo = mb_geom->vbo[mb_step]; - if (vbo) { - /* Use the vbo to perform the copy on the GPU. */ - GPU_vertbuf_use(vbo); - /* Perform a copy to avoid losing it after RE_engine_frame_set(). */ - mb_geom->vbo[mb_step] = vbo = GPU_vertbuf_duplicate(vbo); - /* Find and replace "pos" attrib name. */ - GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo); - int attrib_id = GPU_vertformat_attr_id_get(format, "pos"); - GPU_vertformat_attr_rename(format, attrib_id, (mb_step == MB_PREV) ? "prv" : "nxt"); - } - else { - /* This might happen if the object visibility has been animated. */ - mb_geom->use_deform = false; - } - } - break; - - default: - BLI_assert(0); - break; - } - } -} - -void EEVEE_motion_blur_swap_data(EEVEE_Data *vedata) -{ - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - - GHashIterator ghi; - - BLI_assert((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0); - - /* Camera Data. */ - effects->motion_blur.camera[MB_PREV] = effects->motion_blur.camera[MB_NEXT]; - - /* Object Data. */ - for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object); - BLI_ghashIterator_done(&ghi) == false; - BLI_ghashIterator_step(&ghi)) { - EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi); - - copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_NEXT]); - } - - /* Deformation Data. */ - for (BLI_ghashIterator_init(&ghi, effects->motion_blur.geom); - BLI_ghashIterator_done(&ghi) == false; - BLI_ghashIterator_step(&ghi)) { - EEVEE_GeometryMotionData *mb_geom = BLI_ghashIterator_getValue(&ghi); - EEVEE_HairMotionData *mb_hair = (EEVEE_HairMotionData *)mb_geom; - - switch (mb_geom->type) { - case EEVEE_MOTION_DATA_HAIR: - for (int i = 0; i < mb_hair->psys_len; i++) { - GPU_VERTBUF_DISCARD_SAFE(mb_hair->psys[i].hair_pos[MB_PREV]); - DRW_TEXTURE_FREE_SAFE(mb_hair->psys[i].hair_pos_tx[MB_PREV]); - mb_hair->psys[i].hair_pos[MB_PREV] = mb_hair->psys[i].hair_pos[MB_NEXT]; - mb_hair->psys[i].hair_pos_tx[MB_PREV] = mb_hair->psys[i].hair_pos_tx[MB_NEXT]; - mb_hair->psys[i].hair_pos[MB_NEXT] = NULL; - mb_hair->psys[i].hair_pos_tx[MB_NEXT] = NULL; - } - break; - - case EEVEE_MOTION_DATA_MESH: - if (mb_geom->batch != NULL) { - motion_blur_remove_vbo_reference_from_batch( - mb_geom->batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]); - } - GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]); - mb_geom->vbo[MB_PREV] = mb_geom->vbo[MB_NEXT]; - mb_geom->vbo[MB_NEXT] = NULL; - - if (mb_geom->vbo[MB_PREV]) { - GPUVertBuf *vbo = mb_geom->vbo[MB_PREV]; - GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo); - int attrib_id = GPU_vertformat_attr_id_get(format, "nxt"); - GPU_vertformat_attr_rename(format, attrib_id, "prv"); - } - break; - - default: - BLI_assert(0); - break; - } - } -} - -void EEVEE_motion_blur_draw(EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - - /* Motion Blur */ - if ((effects->enabled_effects & EFFECT_MOTION_BLUR) != 0) { - /* Create velocity max tiles in 2 passes. One for each dimension. */ - GPU_framebuffer_bind(fbl->velocity_tiles_fb[0]); - DRW_draw_pass(psl->velocity_tiles_x); - - GPU_framebuffer_bind(fbl->velocity_tiles_fb[1]); - DRW_draw_pass(psl->velocity_tiles); - - /* Expand the tiles by reading the neighborhood. Do as many passes as required. */ - int buf = 0; - for (int i = effects->motion_blur_max; i > 0; i -= EEVEE_VELOCITY_TILE_SIZE) { - GPU_framebuffer_bind(fbl->velocity_tiles_fb[buf]); - - /* Change viewport to avoid invoking more pixel shaders than necessary since in one of the - * buffer the texture is way bigger in height. This avoid creating another texture and - * reduce VRAM usage. */ - int w = GPU_texture_width(effects->velocity_tiles_tx); - int h = GPU_texture_height(effects->velocity_tiles_tx); - GPU_framebuffer_viewport_set(fbl->velocity_tiles_fb[buf], 0, 0, w, h); - - DRW_draw_pass(psl->velocity_tiles_expand[buf]); - - GPU_framebuffer_viewport_reset(fbl->velocity_tiles_fb[buf]); - - buf = buf ? 0 : 1; - } - - GPU_framebuffer_bind(effects->target_buffer); - DRW_draw_pass(psl->motion_blur); - SWAP_BUFFERS(); - } -} diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.cc b/source/blender/draw/engines/eevee/eevee_motion_blur.cc new file mode 100644 index 00000000000..cc841611258 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_motion_blur.cc @@ -0,0 +1,248 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +#include "BLI_map.hh" +#include "DEG_depsgraph_query.h" + +#include "eevee_instance.hh" +#include "eevee_sampling.hh" +#include "eevee_shader_shared.hh" +#include "eevee_velocity.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name MotionBlurModule + * + * \{ */ + +void MotionBlurModule::init(void) +{ + const Scene *scene = inst_.scene; + + enabled_ = (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0; + + /* Viewport not supported for now. */ + if (!DRW_state_is_scene_render()) { + enabled_ = false; + } + if (!enabled_) { + motion_blur_fx_enabled_ = false; + return; + } + + /* Take into account the steps needed for fx motion blur. */ + int steps_count = max_ii(1, scene->eevee.motion_blur_steps) * 2 + 1; + + time_steps_.resize(steps_count); + + initial_frame_ = CFRA; + initial_subframe_ = SUBFRA; + frame_time_ = initial_frame_ + initial_subframe_; + motion_blur_position_ = scene->eevee.motion_blur_position; + motion_blur_shutter_ = scene->eevee.motion_blur_shutter; + + /* Without this there is the possibility of the curve table not being allocated. */ + BKE_curvemapping_changed((struct CurveMapping *)&scene->r.mblur_shutter_curve, false); + + Vector<float> cdf(CM_TABLE); + Sampling::cdf_from_curvemapping(scene->r.mblur_shutter_curve, cdf); + Sampling::cdf_invert(cdf, time_steps_); + + for (float &time : time_steps_) { + time = this->shutter_time_to_scene_time(time); + } + + motion_blur_fx_enabled_ = scene->eevee.motion_blur_max > 0.5f; + step_id_ = 1; + + if (motion_blur_fx_enabled_) { + /* A bit weird but we have to sync the first 2 steps here because the step() + * function is only called after rendering a sample. */ + inst_.velocity.step_sync(VelocityModule::STEP_PREVIOUS, time_steps_[0]); + inst_.velocity.step_sync(VelocityModule::STEP_NEXT, time_steps_[2]); + } + inst_.set_time(time_steps_[1]); +} + +/* Runs after rendering a sample. */ +void MotionBlurModule::step(void) +{ + if (!enabled_) { + return; + } + else if (inst_.sampling.finished()) { + /* Restore original frame number. This is because the render pipeline expects it. */ + RE_engine_frame_set(inst_.render, initial_frame_, initial_subframe_); + } + else if (inst_.sampling.do_render_sync()) { + /* Time to change motion step. */ + BLI_assert(time_steps_.size() > step_id_ + 2); + step_id_ += 2; + + if (motion_blur_fx_enabled_) { + inst_.velocity.step_swap(); + inst_.velocity.step_sync(VelocityModule::STEP_NEXT, time_steps_[step_id_ + 1]); + } + inst_.set_time(time_steps_[step_id_]); + } +} + +float MotionBlurModule::shutter_time_to_scene_time(float time) +{ + switch (motion_blur_position_) { + case SCE_EEVEE_MB_START: + /* No offset. */ + break; + case SCE_EEVEE_MB_CENTER: + time -= 0.5f; + break; + case SCE_EEVEE_MB_END: + time -= 1.0; + break; + default: + BLI_assert(!"Invalid motion blur position enum!"); + break; + } + time *= motion_blur_shutter_; + time += frame_time_; + return time; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name MotionBlur + * + * \{ */ + +void MotionBlur::init() +{ + const Scene *scene = inst_.scene; + data_.blur_max = scene->eevee.motion_blur_max; + data_.depth_scale = scene->eevee.motion_blur_depth_scale; + enabled_ = ((scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0) && (data_.blur_max > 0.5f); +} + +void MotionBlur::sync(int extent[2]) +{ + if (!enabled_) { + return; + } + + DrawEngineType *owner = (DrawEngineType *)view_name_.c_str(); + eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT; + + uint res[2] = {divide_ceil_u(extent[0], MB_TILE_DIVISOR), + divide_ceil_u(extent[1], MB_TILE_DIVISOR)}; + + { + /* Create max velocity tiles in 2 passes. One for X and one for Y */ + DRW_PASS_CREATE(tiles_flatten_ps_, DRW_STATE_WRITE_COLOR); + GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_TILE_FLATTEN); + DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_flatten_ps_); + DRW_shgroup_uniform_texture_ref_ex(grp, "velocity_tx", &input_velocity_tx_, no_filter); + DRW_shgroup_uniform_block(grp, "motion_blur_block", data_.ubo_get()); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + tiles_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner); + + tiles_flatten_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(tiles_tx_)); + } + { + /* Expand max tiles by keeping the max tile in each tile neighborhood. */ + DRW_PASS_CREATE(tiles_dilate_ps_, DRW_STATE_WRITE_COLOR); + GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_TILE_DILATE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_dilate_ps_); + DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_tx", &tiles_tx_, no_filter); + DRW_shgroup_uniform_block(grp, "motion_blur_block", data_.ubo_get()); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + tiles_dilated_tx_ = DRW_texture_pool_query_2d(UNPACK2(res), GPU_RGBA16F, owner); + + tiles_dilate_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(tiles_dilated_tx_)); + } + { + data_.target_size_inv[0] = 1.0f / extent[0]; + data_.target_size_inv[1] = 1.0f / extent[1]; + + /* Do the motion blur gather algorithm. */ + DRW_PASS_CREATE(gather_ps_, DRW_STATE_WRITE_COLOR); + GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_GATHER); + DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_ps_); + DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get()); + DRW_shgroup_uniform_block(grp, "motion_blur_block", data_.ubo_get()); + DRW_shgroup_uniform_texture_ref(grp, "color_tx", &input_color_tx_); + DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_); + DRW_shgroup_uniform_texture_ref_ex(grp, "velocity_tx", &input_velocity_tx_, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "tiles_tx", &tiles_dilated_tx_, no_filter); + + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + + data_.is_viewport = !DRW_state_is_image_render(); + data_.push_update(); +} + +void MotionBlur::render(GPUTexture *depth_tx, + GPUTexture *velocity_tx, + GPUTexture **input_tx, + GPUTexture **output_tx) +{ + if (!enabled_) { + return; + } + + input_color_tx_ = *input_tx; + input_depth_tx_ = depth_tx; + input_velocity_tx_ = velocity_tx; + + DRW_stats_group_start("Motion Blur"); + + GPU_framebuffer_bind(tiles_flatten_fb_); + DRW_draw_pass(tiles_flatten_ps_); + + for (int max_blur = data_.blur_max; max_blur > 0; max_blur -= MB_TILE_DIVISOR) { + GPU_framebuffer_bind(tiles_dilate_fb_); + DRW_draw_pass(tiles_dilate_ps_); + SWAP(GPUTexture *, tiles_tx_, tiles_dilated_tx_); + SWAP(Framebuffer, tiles_flatten_fb_, tiles_dilate_fb_); + } + /* Swap again so result is in tiles_dilated_tx_. */ + SWAP(GPUTexture *, tiles_tx_, tiles_dilated_tx_); + SWAP(Framebuffer, tiles_flatten_fb_, tiles_dilate_fb_); + + gather_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(*output_tx)); + + GPU_framebuffer_bind(gather_fb_); + DRW_draw_pass(gather_ps_); + + DRW_stats_group_end(); + + /* Swap buffers so that next effect has the right input. */ + *input_tx = *output_tx; + *output_tx = input_color_tx_; +} + +/** \} */ + +} // namespace blender::eevee
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.hh b/source/blender/draw/engines/eevee/eevee_motion_blur.hh new file mode 100644 index 00000000000..5dab68fba5d --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_motion_blur.hh @@ -0,0 +1,159 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Motion blur is done by accumulating scene samples over shutter time. + * Since the number of step is discrete, quite low, and not per pixel randomized, + * we couple this with a post processing motion blur. + * + * The post-fx motion blur is done in two directions, from the previous step and to the next. + * + * For a scene with 3 motion steps, a flat shutter curve and shutter time of 2 frame + * centered on frame we have: + * + * |--------------------|--------------------| + * -1 0 1 Frames + * + * |-------------|-------------|-------------| + * 1 2 3 Motion steps + * + * |------|------|------|------|------|------| + * 0 1 2 4 5 6 7 Time Steps + * + * |-------------| One motion step blurs this range. + * -1 | +1 Objects and geometry steps are recorded here. + * 0 Scene is rendered here. + * + * Since motion step N and N+1 share one time step we reuse it to avoid an extra scene evaluation. + * + * Note that we have to evaluate -1 and +1 time steps before rendering so eval order is -1, +1, 0. + * This is because all GPUBatches from the DRWCache are being free when changing a frame. + */ + +#pragma once + +#include "BLI_map.hh" +#include "DEG_depsgraph_query.h" + +#include "eevee_sampling.hh" +#include "eevee_shader_shared.hh" +#include "eevee_velocity.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name MotionBlur + * + * \{ */ + +/** + * Manages timesteps evaluations and accumulation Motion blur. + * Post process motion blur is handled by the MotionBlur class. + */ +class MotionBlurModule { + private: + Instance &inst_; + + /** + * Array containing all steps (in scene time) we need to evaluate (not render). + * Only odd steps are rendered. The even ones are evaluated for fx motion blur. + */ + Vector<float> time_steps_; + + /** Copy of input frame an subframe to restore after render. */ + int initial_frame_; + float initial_subframe_; + /** Time of the frame we are rendering. */ + float frame_time_; + /** Copy of scene settings. */ + int motion_blur_position_; + float motion_blur_shutter_; + + bool enabled_ = false; + float motion_blur_fx_enabled_ = false; + + int step_id_ = 0; + + public: + MotionBlurModule(Instance &inst) : inst_(inst){}; + ~MotionBlurModule(){}; + + void init(void); + + void step(void); + + private: + float shutter_time_to_scene_time(float time); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name MotionBlur + * + * \{ */ + +/** + * Per view fx module. Perform a motion blur using the result of the velocity pass. + */ +class MotionBlur { + private: + Instance &inst_; + + StringRefNull view_name_; + + /** Textures from pool. Not owned. */ + GPUTexture *tiles_tx_ = nullptr; + GPUTexture *tiles_dilated_tx_ = nullptr; + /** Input texture. Not owned. */ + GPUTexture *input_velocity_tx_ = nullptr; + GPUTexture *input_color_tx_ = nullptr; + GPUTexture *input_depth_tx_ = nullptr; + /** Passes. Not owned. */ + DRWPass *tiles_flatten_ps_ = nullptr; + DRWPass *tiles_dilate_ps_ = nullptr; + DRWPass *gather_ps_ = nullptr; + /** Framebuffers. Owned. */ + eevee::Framebuffer tiles_flatten_fb_; + eevee::Framebuffer tiles_dilate_fb_; + eevee::Framebuffer gather_fb_; + + StructBuffer<MotionBlurData> data_; + + bool enabled_; + + public: + MotionBlur(Instance &inst, StringRefNull view_name) : inst_(inst), view_name_(view_name){}; + + ~MotionBlur(){}; + + void init(void); + + void sync(int extent[2]); + + void render(GPUTexture *depth_tx, + GPUTexture *velocity_tx, + GPUTexture **input_tx, + GPUTexture **output_tx); +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_occlusion.c b/source/blender/draw/engines/eevee/eevee_occlusion.c deleted file mode 100644 index 1acd9950012..00000000000 --- a/source/blender/draw/engines/eevee/eevee_occlusion.c +++ /dev/null @@ -1,281 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - * - * Implementation of the screen space Ground Truth Ambient Occlusion. - */ - -#include "DRW_render.h" - -#include "BLI_string_utils.h" - -#include "DEG_depsgraph_query.h" - -#include "BKE_global.h" /* for G.debug_value */ - -#include "eevee_private.h" - -#include "GPU_capabilities.h" -#include "GPU_platform.h" -#include "GPU_state.h" - -static struct { - struct GPUTexture *dummy_horizon_tx; -} e_data = {NULL}; /* Engine data */ - -int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - - if (!e_data.dummy_horizon_tx) { - const float pixel[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - e_data.dummy_horizon_tx = DRW_texture_create_2d(1, 1, GPU_RGBA8, DRW_TEX_WRAP, pixel); - } - - if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED || - stl->g_data->render_passes & EEVEE_RENDER_PASS_AO) { - const float *viewport_size = DRW_viewport_size_get(); - const int fs_size[2] = {(int)viewport_size[0], (int)viewport_size[1]}; - - common_data->ao_dist = scene_eval->eevee.gtao_distance; - common_data->ao_factor = max_ff(1e-4f, scene_eval->eevee.gtao_factor); - common_data->ao_quality = scene_eval->eevee.gtao_quality; - - if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) { - common_data->ao_settings = 1.0f; /* USE_AO */ - } - if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_BENT_NORMALS) { - common_data->ao_settings += 2.0f; /* USE_BENT_NORMAL */ - } - if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_BOUNCE) { - common_data->ao_settings += 4.0f; /* USE_DENOISE */ - } - - common_data->ao_bounce_fac = (scene_eval->eevee.flag & SCE_EEVEE_GTAO_BOUNCE) ? 1.0f : 0.0f; - - effects->gtao_horizons_renderpass = DRW_texture_pool_query_2d( - UNPACK2(effects->hiz_size), GPU_RGBA8, &draw_engine_eevee_type); - GPU_framebuffer_ensure_config( - &fbl->gtao_fb, - {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons_renderpass)}); - - if (G.debug_value == 6) { - effects->gtao_horizons_debug = DRW_texture_pool_query_2d( - UNPACK2(fs_size), GPU_RGBA8, &draw_engine_eevee_type); - GPU_framebuffer_ensure_config( - &fbl->gtao_debug_fb, - {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons_debug)}); - } - else { - effects->gtao_horizons_debug = NULL; - } - - effects->gtao_horizons = (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) ? - effects->gtao_horizons_renderpass : - e_data.dummy_horizon_tx; - - return EFFECT_GTAO | EFFECT_NORMAL_BUFFER; - } - - /* Cleanup */ - effects->gtao_horizons_renderpass = e_data.dummy_horizon_tx; - effects->gtao_horizons = e_data.dummy_horizon_tx; - GPU_FRAMEBUFFER_FREE_SAFE(fbl->gtao_fb); - common_data->ao_settings = 0.0f; - - return 0; -} - -void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_PassList *psl = vedata->psl; - EEVEE_EffectsInfo *effects = stl->effects; - - const eGPUTextureFormat texture_format = (tot_samples > 128) ? GPU_R32F : GPU_R16F; - - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - - /* Should be enough precision for many samples. */ - DRW_texture_ensure_fullscreen_2d(&txl->ao_accum, texture_format, 0); - - GPU_framebuffer_ensure_config(&fbl->ao_accum_fb, - {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ao_accum)}); - - /* Accumulation pass */ - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD; - DRW_PASS_CREATE(psl->ao_accum_ps, state); - DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_effect_ambient_occlusion_debug_sh_get(), - psl->ao_accum_ps); - DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); - DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer); - DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); - DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input); - DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons_renderpass); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); -} - -void EEVEE_occlusion_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_EffectsInfo *effects = stl->effects; - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - - if ((effects->enabled_effects & EFFECT_GTAO) != 0) { - /** - * Occlusion Algorithm Overview: - * - * We separate the computation into 2 steps. - * - * - First we scan the neighborhood pixels to find the maximum horizon angle. - * We save this angle in a RG8 array texture. - * - * - Then we use this angle to compute occlusion with the shading normal at - * the shading stage. This let us do correct shadowing for each diffuse / specular - * lobe present in the shader using the correct normal. - */ - DRW_PASS_CREATE(psl->ao_horizon_search, DRW_STATE_WRITE_COLOR); - DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_effect_ambient_occlusion_sh_get(), - psl->ao_horizon_search); - DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); - DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - if (G.debug_value == 6) { - DRW_PASS_CREATE(psl->ao_horizon_debug, DRW_STATE_WRITE_COLOR); - grp = DRW_shgroup_create(EEVEE_shaders_effect_ambient_occlusion_debug_sh_get(), - psl->ao_horizon_debug); - DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); - DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer); - DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); - DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input); - DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons_renderpass); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - } - } -} - -void EEVEE_occlusion_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - - if ((effects->enabled_effects & EFFECT_GTAO) != 0) { - DRW_stats_group_start("GTAO Horizon Scan"); - - GPU_framebuffer_bind(fbl->gtao_fb); - - /** NOTE(fclem): Kind of fragile. We need this to make sure everything lines up - * nicely during planar reflection. */ - if (common_data->ray_type != EEVEE_RAY_GLOSSY) { - const float *viewport_size = DRW_viewport_size_get(); - GPU_framebuffer_viewport_set(fbl->gtao_fb, 0, 0, UNPACK2(viewport_size)); - } - - DRW_draw_pass(psl->ao_horizon_search); - - if (common_data->ray_type != EEVEE_RAY_GLOSSY) { - GPU_framebuffer_viewport_reset(fbl->gtao_fb); - } - - if (GPU_mip_render_workaround() || - GPU_type_matches(GPU_DEVICE_INTEL_UHD, GPU_OS_WIN, GPU_DRIVER_ANY)) { - /* Fix dot corruption on intel HD5XX/HD6XX series. */ - GPU_flush(); - } - - /* Restore */ - GPU_framebuffer_bind(fbl->main_fb); - - DRW_stats_group_end(); - } -} - -void EEVEE_occlusion_draw_debug(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - - if (((effects->enabled_effects & EFFECT_GTAO) != 0) && (G.debug_value == 6)) { - DRW_stats_group_start("GTAO Debug"); - - GPU_framebuffer_bind(fbl->gtao_debug_fb); - DRW_draw_pass(psl->ao_horizon_debug); - - /* Restore */ - GPU_framebuffer_bind(fbl->main_fb); - - DRW_stats_group_end(); - } -} - -void EEVEE_occlusion_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_PassList *psl = vedata->psl; - EEVEE_EffectsInfo *effects = vedata->stl->effects; - - if (fbl->ao_accum_fb != NULL) { - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - - /* Update the min_max/horizon buffers so the refraction materials appear in it. */ - EEVEE_create_minmax_buffer(vedata, dtxl->depth, -1); - EEVEE_occlusion_compute(sldata, vedata); - - GPU_framebuffer_bind(fbl->ao_accum_fb); - - /* Clear texture. */ - if (effects->taa_current_sample == 1) { - const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPU_framebuffer_clear_color(fbl->ao_accum_fb, clear); - } - - DRW_draw_pass(psl->ao_accum_ps); - - /* Restore */ - GPU_framebuffer_bind(fbl->main_fb); - } -} - -void EEVEE_occlusion_free(void) -{ - DRW_TEXTURE_FREE_SAFE(e_data.dummy_horizon_tx); -} diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 766e721b1b8..e99bef24fa6 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -13,1632 +13,33 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup DNA + * Copyright 2021, Blender Foundation. */ #pragma once -#include "DRW_render.h" - -#include "BLI_bitmap.h" - -#include "DNA_lightprobe_types.h" - -#include "GPU_viewport.h" - -#include "BKE_camera.h" - #ifdef __cplusplus extern "C" { #endif -struct EEVEE_ShadowCasterBuffer; -struct GPUFrameBuffer; struct Object; -struct RenderLayer; - -extern struct DrawEngineType draw_engine_eevee_type; - -/* Minimum UBO is 16384 bytes */ -#define MAX_PROBE 128 /* TODO: find size by dividing UBO max size by probe data size. */ -#define MAX_GRID 64 /* TODO: find size by dividing UBO max size by grid data size. */ -#define MAX_PLANAR 16 /* TODO: find size by dividing UBO max size by grid data size. */ -#define MAX_LIGHT 128 /* TODO: find size by dividing UBO max size by light data size. */ -#define MAX_CASCADE_NUM 4 -#define MAX_SHADOW 128 /* TODO: Make this depends on #GL_MAX_ARRAY_TEXTURE_LAYERS. */ -#define MAX_SHADOW_CASCADE 8 -#define MAX_SHADOW_CUBE (MAX_SHADOW - MAX_CASCADE_NUM * MAX_SHADOW_CASCADE) -#define MAX_BLOOM_STEP 16 -#define MAX_AOVS 64 - -/* Special value chosen to not be altered by depth of field sample count. */ -#define TAA_MAX_SAMPLE 10000926 - -// #define DEBUG_SHADOW_DISTRIBUTION - -/* Only define one of these. */ -// #define IRRADIANCE_SH_L2 -#define IRRADIANCE_HL2 - -#if defined(IRRADIANCE_SH_L2) -# define SHADER_IRRADIANCE "#define IRRADIANCE_SH_L2\n" -#elif defined(IRRADIANCE_HL2) -# define SHADER_IRRADIANCE "#define IRRADIANCE_HL2\n" -#endif - -/* Macro causes over indentation. */ -/* clang-format off */ -#define SHADER_DEFINES \ - "#define EEVEE_ENGINE\n" \ - "#define MAX_PROBE " STRINGIFY(MAX_PROBE) "\n" \ - "#define MAX_GRID " STRINGIFY(MAX_GRID) "\n" \ - "#define MAX_PLANAR " STRINGIFY(MAX_PLANAR) "\n" \ - "#define MAX_LIGHT " STRINGIFY(MAX_LIGHT) "\n" \ - "#define MAX_SHADOW " STRINGIFY(MAX_SHADOW) "\n" \ - "#define MAX_SHADOW_CUBE " STRINGIFY(MAX_SHADOW_CUBE) "\n" \ - "#define MAX_SHADOW_CASCADE " STRINGIFY(MAX_SHADOW_CASCADE) "\n" \ - "#define MAX_CASCADE_NUM " STRINGIFY(MAX_CASCADE_NUM) "\n" \ - SHADER_IRRADIANCE -/* clang-format on */ - -#define EEVEE_PROBE_MAX min_ii(MAX_PROBE, GPU_max_texture_layers() / 6) -#define EEVEE_VELOCITY_TILE_SIZE 32 -#define USE_VOLUME_OPTI (GPU_shader_image_load_store_support()) - -#define SWAP_DOUBLE_BUFFERS() \ - { \ - if (effects->swap_double_buffer) { \ - SWAP(struct GPUFrameBuffer *, fbl->main_fb, fbl->double_buffer_fb); \ - SWAP(struct GPUFrameBuffer *, fbl->main_color_fb, fbl->double_buffer_color_fb); \ - SWAP(GPUTexture *, txl->color, txl->color_double_buffer); \ - effects->swap_double_buffer = false; \ - } \ - } \ - ((void)0) - -#define SWAP_BUFFERS() \ - { \ - if (effects->target_buffer == fbl->effect_color_fb) { \ - SWAP_DOUBLE_BUFFERS(); \ - effects->source_buffer = txl->color_post; \ - effects->target_buffer = fbl->main_color_fb; \ - } \ - else { \ - SWAP_DOUBLE_BUFFERS(); \ - effects->source_buffer = txl->color; \ - effects->target_buffer = fbl->effect_color_fb; \ - } \ - } \ - ((void)0) - -#define SWAP_BUFFERS_TAA() \ - { \ - if (effects->target_buffer == fbl->effect_color_fb) { \ - SWAP(struct GPUFrameBuffer *, fbl->effect_fb, fbl->taa_history_fb); \ - SWAP(struct GPUFrameBuffer *, fbl->effect_color_fb, fbl->taa_history_color_fb); \ - SWAP(GPUTexture *, txl->color_post, txl->taa_history); \ - effects->source_buffer = txl->taa_history; \ - effects->target_buffer = fbl->effect_color_fb; \ - } \ - else { \ - SWAP(struct GPUFrameBuffer *, fbl->main_fb, fbl->taa_history_fb); \ - SWAP(struct GPUFrameBuffer *, fbl->main_color_fb, fbl->taa_history_color_fb); \ - SWAP(GPUTexture *, txl->color, txl->taa_history); \ - effects->source_buffer = txl->taa_history; \ - effects->target_buffer = fbl->main_color_fb; \ - } \ - } \ - ((void)0) - -BLI_INLINE bool eevee_hdri_preview_overlay_enabled(const View3D *v3d) -{ - /* Only show the HDRI Preview in Shading Preview in the Viewport. */ - if (v3d == NULL || v3d->shading.type != OB_MATERIAL) { - return false; - } - - /* Only show the HDRI Preview when viewing the Combined render pass */ - if (v3d->shading.render_pass != SCE_PASS_COMBINED) { - return false; - } - - return ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (v3d->overlay.flag & V3D_OVERLAY_LOOK_DEV); -} - -#define USE_SCENE_LIGHT(v3d) \ - ((!v3d) || \ - ((v3d->shading.type == OB_MATERIAL) && (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS)) || \ - ((v3d->shading.type == OB_RENDER) && (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER))) -#define LOOK_DEV_STUDIO_LIGHT_ENABLED(v3d) \ - ((v3d) && (((v3d->shading.type == OB_MATERIAL) && \ - ((v3d->shading.flag & V3D_SHADING_SCENE_WORLD) == 0)) || \ - ((v3d->shading.type == OB_RENDER) && \ - ((v3d->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER) == 0)))) - -#define MIN_CUBE_LOD_LEVEL 3 -#define MAX_SCREEN_BUFFERS_LOD_LEVEL 6 - -/* All the renderpasses that use the GPUMaterial for accumulation */ -#define EEVEE_RENDERPASSES_MATERIAL \ - (EEVEE_RENDER_PASS_EMIT | EEVEE_RENDER_PASS_DIFFUSE_COLOR | EEVEE_RENDER_PASS_DIFFUSE_LIGHT | \ - EEVEE_RENDER_PASS_SPECULAR_COLOR | EEVEE_RENDER_PASS_SPECULAR_LIGHT | \ - EEVEE_RENDER_PASS_ENVIRONMENT | EEVEE_RENDER_PASS_AOV) -#define EEVEE_AOV_HASH_ALL -1 -#define EEVEE_AOV_HASH_COLOR_TYPE_MASK 1 -#define MAX_CRYPTOMATTE_LAYERS 3 - -/* Material shader variations */ -enum { - VAR_MAT_MESH = (1 << 0), - VAR_MAT_VOLUME = (1 << 1), - VAR_MAT_HAIR = (1 << 2), - VAR_MAT_POINTCLOUD = (1 << 3), - VAR_MAT_BLEND = (1 << 4), - VAR_MAT_LOOKDEV = (1 << 5), - VAR_MAT_HOLDOUT = (1 << 6), - VAR_MAT_HASH = (1 << 7), - VAR_MAT_DEPTH = (1 << 8), - VAR_MAT_REFRACT = (1 << 9), - VAR_WORLD_BACKGROUND = (1 << 10), - VAR_WORLD_PROBE = (1 << 11), - VAR_WORLD_VOLUME = (1 << 12), - VAR_DEFAULT = (1 << 13), -}; - -/* Material shader cache keys */ -enum { - /* HACK: This assumes the struct GPUShader will never be smaller than our variations. - * This allow us to only keep one #GHash and avoid bigger keys comparisons/hashing. - * We combine the #GPUShader pointer with the key. */ - KEY_CULL = (1 << 0), - KEY_REFRACT = (1 << 1), - KEY_HAIR = (1 << 2), - KEY_SHADOW = (1 << 3), -}; - -/* DOF Gather pass shader variations */ -typedef enum EEVEE_DofGatherPass { - DOF_GATHER_FOREGROUND = 0, - DOF_GATHER_BACKGROUND = 1, - DOF_GATHER_HOLEFILL = 2, - - DOF_GATHER_MAX_PASS, -} EEVEE_DofGatherPass; - -#define DOF_TILE_DIVISOR 16 -#define DOF_BOKEH_LUT_SIZE 32 -#define DOF_GATHER_RING_COUNT 5 -#define DOF_DILATE_RING_COUNT 3 -#define DOF_FAST_GATHER_COC_ERROR 0.05 - -#define DOF_SHADER_DEFINES \ - "#define DOF_TILE_DIVISOR " STRINGIFY(DOF_TILE_DIVISOR) "\n" \ - "#define DOF_BOKEH_LUT_SIZE " STRINGIFY(DOF_BOKEH_LUT_SIZE) "\n" \ - "#define DOF_GATHER_RING_COUNT " STRINGIFY(DOF_GATHER_RING_COUNT) "\n" \ - "#define DOF_DILATE_RING_COUNT " STRINGIFY(DOF_DILATE_RING_COUNT) "\n" \ - "#define DOF_FAST_GATHER_COC_ERROR " STRINGIFY(DOF_FAST_GATHER_COC_ERROR) "\n" - -/* ************ PROBE UBO ************* */ - -/* They are the same struct as their Cache siblings. - * typedef'ing just to keep the naming consistent with - * other eevee types. */ -typedef LightProbeCache EEVEE_LightProbe; -typedef LightGridCache EEVEE_LightGrid; - -typedef struct EEVEE_PlanarReflection { - float plane_equation[4]; - float clip_vec_x[3], attenuation_scale; - float clip_vec_y[3], attenuation_bias; - float clip_edge_x_pos, clip_edge_x_neg; - float clip_edge_y_pos, clip_edge_y_neg; - float facing_scale, facing_bias, clipsta, pad; - float reflectionmat[4][4]; /* Used for sampling the texture. */ - float mtx[4][4]; /* Not used in shader. TODO: move elsewhere. */ -} EEVEE_PlanarReflection; - -/* --------------------------------------- */ - -typedef struct EEVEE_BoundBox { - float center[3], halfdim[3]; -} EEVEE_BoundBox; - -typedef struct EEVEE_PassList { - /* Shadows */ - struct DRWPass *shadow_pass; - struct DRWPass *shadow_accum_pass; - - /* Probes */ - struct DRWPass *probe_background; - struct DRWPass *probe_glossy_compute; - struct DRWPass *probe_diffuse_compute; - struct DRWPass *probe_visibility_compute; - struct DRWPass *probe_grid_fill; - struct DRWPass *probe_display; - struct DRWPass *probe_planar_downsample_ps; - - /* Effects */ - struct DRWPass *ao_horizon_search; - struct DRWPass *ao_horizon_debug; - struct DRWPass *ao_accum_ps; - struct DRWPass *mist_accum_ps; - struct DRWPass *motion_blur; - struct DRWPass *bloom_blit; - struct DRWPass *bloom_downsample_first; - struct DRWPass *bloom_downsample; - struct DRWPass *bloom_upsample; - struct DRWPass *bloom_resolve; - struct DRWPass *bloom_accum_ps; - struct DRWPass *dof_setup; - struct DRWPass *dof_flatten_tiles; - struct DRWPass *dof_dilate_tiles_minmax; - struct DRWPass *dof_dilate_tiles_minabs; - struct DRWPass *dof_reduce_copy; - struct DRWPass *dof_downsample; - struct DRWPass *dof_reduce; - struct DRWPass *dof_bokeh; - struct DRWPass *dof_gather_fg; - struct DRWPass *dof_gather_fg_holefill; - struct DRWPass *dof_gather_bg; - struct DRWPass *dof_scatter_fg; - struct DRWPass *dof_scatter_bg; - struct DRWPass *dof_filter; - struct DRWPass *dof_resolve; - struct DRWPass *volumetric_world_ps; - struct DRWPass *volumetric_objects_ps; - struct DRWPass *volumetric_scatter_ps; - struct DRWPass *volumetric_integration_ps; - struct DRWPass *volumetric_resolve_ps; - struct DRWPass *volumetric_accum_ps; - struct DRWPass *ssr_raytrace; - struct DRWPass *ssr_resolve; - struct DRWPass *sss_blur_ps; - struct DRWPass *sss_resolve_ps; - struct DRWPass *sss_translucency_ps; - struct DRWPass *color_copy_ps; - struct DRWPass *color_downsample_ps; - struct DRWPass *color_downsample_cube_ps; - struct DRWPass *velocity_object; - struct DRWPass *velocity_hair; - struct DRWPass *velocity_resolve; - struct DRWPass *velocity_tiles_x; - struct DRWPass *velocity_tiles; - struct DRWPass *velocity_tiles_expand[2]; - struct DRWPass *taa_resolve; - struct DRWPass *alpha_checker; - - /* HiZ */ - struct DRWPass *maxz_downlevel_ps; - struct DRWPass *maxz_copydepth_ps; - struct DRWPass *maxz_copydepth_layer_ps; - - /* Renderpass Accumulation. */ - struct DRWPass *material_accum_ps; - struct DRWPass *background_accum_ps; - struct DRWPass *cryptomatte_ps; - - struct DRWPass *depth_ps; - struct DRWPass *depth_cull_ps; - struct DRWPass *depth_clip_ps; - struct DRWPass *depth_clip_cull_ps; - struct DRWPass *depth_refract_ps; - struct DRWPass *depth_refract_cull_ps; - struct DRWPass *depth_refract_clip_ps; - struct DRWPass *depth_refract_clip_cull_ps; - struct DRWPass *material_ps; - struct DRWPass *material_cull_ps; - struct DRWPass *material_refract_ps; - struct DRWPass *material_refract_cull_ps; - struct DRWPass *material_sss_ps; - struct DRWPass *material_sss_cull_ps; - struct DRWPass *transparent_pass; - struct DRWPass *background_ps; - struct DRWPass *update_noise_pass; - struct DRWPass *lookdev_glossy_pass; - struct DRWPass *lookdev_diffuse_pass; - struct DRWPass *renderpass_pass; -} EEVEE_PassList; - -typedef struct EEVEE_FramebufferList { - /* Effects */ - struct GPUFrameBuffer *gtao_fb; - struct GPUFrameBuffer *gtao_debug_fb; - struct GPUFrameBuffer *downsample_fb; - struct GPUFrameBuffer *maxzbuffer_fb; - struct GPUFrameBuffer *bloom_blit_fb; - struct GPUFrameBuffer *bloom_down_fb[MAX_BLOOM_STEP]; - struct GPUFrameBuffer *bloom_accum_fb[MAX_BLOOM_STEP - 1]; - struct GPUFrameBuffer *bloom_pass_accum_fb; - struct GPUFrameBuffer *cryptomatte_fb; - struct GPUFrameBuffer *shadow_accum_fb; - struct GPUFrameBuffer *ssr_accum_fb; - struct GPUFrameBuffer *sss_blur_fb; - struct GPUFrameBuffer *sss_blit_fb; - struct GPUFrameBuffer *sss_resolve_fb; - struct GPUFrameBuffer *sss_clear_fb; - struct GPUFrameBuffer *sss_translucency_fb; - struct GPUFrameBuffer *sss_accum_fb; - struct GPUFrameBuffer *dof_setup_fb; - struct GPUFrameBuffer *dof_flatten_tiles_fb; - struct GPUFrameBuffer *dof_dilate_tiles_fb; - struct GPUFrameBuffer *dof_downsample_fb; - struct GPUFrameBuffer *dof_reduce_fb; - struct GPUFrameBuffer *dof_reduce_copy_fb; - struct GPUFrameBuffer *dof_bokeh_fb; - struct GPUFrameBuffer *dof_gather_fg_fb; - struct GPUFrameBuffer *dof_filter_fg_fb; - struct GPUFrameBuffer *dof_gather_fg_holefill_fb; - struct GPUFrameBuffer *dof_gather_bg_fb; - struct GPUFrameBuffer *dof_filter_bg_fb; - struct GPUFrameBuffer *dof_scatter_fg_fb; - struct GPUFrameBuffer *dof_scatter_bg_fb; - struct GPUFrameBuffer *volumetric_fb; - struct GPUFrameBuffer *volumetric_scat_fb; - struct GPUFrameBuffer *volumetric_integ_fb; - struct GPUFrameBuffer *volumetric_accum_fb; - struct GPUFrameBuffer *screen_tracing_fb; - struct GPUFrameBuffer *mist_accum_fb; - struct GPUFrameBuffer *material_accum_fb; - struct GPUFrameBuffer *renderpass_fb; - struct GPUFrameBuffer *ao_accum_fb; - struct GPUFrameBuffer *velocity_resolve_fb; - struct GPUFrameBuffer *velocity_fb; - struct GPUFrameBuffer *velocity_tiles_fb[2]; - - struct GPUFrameBuffer *update_noise_fb; - - struct GPUFrameBuffer *planarref_fb; - struct GPUFrameBuffer *planar_downsample_fb; - - struct GPUFrameBuffer *main_fb; - struct GPUFrameBuffer *main_color_fb; - struct GPUFrameBuffer *effect_fb; - struct GPUFrameBuffer *effect_color_fb; - struct GPUFrameBuffer *radiance_filtered_fb; - struct GPUFrameBuffer *double_buffer_fb; - struct GPUFrameBuffer *double_buffer_color_fb; - struct GPUFrameBuffer *double_buffer_depth_fb; - struct GPUFrameBuffer *taa_history_fb; - struct GPUFrameBuffer *taa_history_color_fb; -} EEVEE_FramebufferList; - -typedef struct EEVEE_TextureList { - /* Effects */ - struct GPUTexture *color_post; /* R16_G16_B16 */ - struct GPUTexture *mist_accum; - struct GPUTexture *ao_accum; - struct GPUTexture *sss_accum; - struct GPUTexture *env_accum; - struct GPUTexture *diff_color_accum; - struct GPUTexture *diff_light_accum; - struct GPUTexture *spec_color_accum; - struct GPUTexture *spec_light_accum; - struct GPUTexture *aov_surface_accum[MAX_AOVS]; - struct GPUTexture *emit_accum; - struct GPUTexture *bloom_accum; - struct GPUTexture *ssr_accum; - struct GPUTexture *shadow_accum; - struct GPUTexture *cryptomatte; - struct GPUTexture *taa_history; - /* Could not be pool texture because of mipmapping. */ - struct GPUTexture *dof_reduced_color; - struct GPUTexture *dof_reduced_coc; - - struct GPUTexture *volume_prop_scattering; - struct GPUTexture *volume_prop_extinction; - struct GPUTexture *volume_prop_emission; - struct GPUTexture *volume_prop_phase; - struct GPUTexture *volume_scatter; - struct GPUTexture *volume_transmit; - struct GPUTexture *volume_scatter_history; - struct GPUTexture *volume_transmit_history; - struct GPUTexture *volume_scatter_accum; - struct GPUTexture *volume_transmittance_accum; - - struct GPUTexture *lookdev_grid_tx; - struct GPUTexture *lookdev_cube_tx; - - struct GPUTexture *planar_pool; - struct GPUTexture *planar_depth; - - struct GPUTexture *maxzbuffer; - struct GPUTexture *filtered_radiance; - - struct GPUTexture *renderpass; - - struct GPUTexture *color; /* R16_G16_B16 */ - struct GPUTexture *color_double_buffer; - struct GPUTexture *depth_double_buffer; -} EEVEE_TextureList; - -typedef struct EEVEE_StorageList { - /* Effects */ - struct EEVEE_EffectsInfo *effects; - - struct EEVEE_PrivateData *g_data; - - struct LightCache *lookdev_lightcache; - EEVEE_LightProbe *lookdev_cube_data; - EEVEE_LightGrid *lookdev_grid_data; - LightCacheTexture *lookdev_cube_mips; -} EEVEE_StorageList; - -/* ************ RENDERPASS UBO ************* */ -typedef struct EEVEE_RenderPassData { - int renderPassDiffuse; - int renderPassDiffuseLight; - int renderPassGlossy; - int renderPassGlossyLight; - int renderPassEmit; - int renderPassSSSColor; - int renderPassEnvironment; - int renderPassAOV; - int renderPassAOVActive; - int _pad[3]; -} EEVEE_RenderPassData; - -/* ************ LIGHT UBO ************* */ -typedef struct EEVEE_Light { - float position[3], invsqrdist; - float color[3], invsqrdist_volume; - float spotsize, spotblend, radius, shadow_id; - float rightvec[3], sizex; - float upvec[3], sizey; - float forwardvec[3], light_type; - float diff, spec, volume, volume_radius; -} EEVEE_Light; - -/* Special type for elliptic area lights, matches lamps_lib.glsl */ -#define LAMPTYPE_AREA_ELLIPSE 100.0f - -typedef struct EEVEE_Shadow { - float near, far, bias, type_data_id; - float contact_dist, contact_bias, contact_spread, contact_thickness; -} EEVEE_Shadow; - -typedef struct EEVEE_ShadowCube { - float shadowmat[4][4]; - float position[3], _pad0[1]; -} EEVEE_ShadowCube; - -typedef struct EEVEE_ShadowCascade { - /* World->Light->NDC->Tex : used for sampling the shadow map. */ - float shadowmat[MAX_CASCADE_NUM][4][4]; - float split_start[4]; - float split_end[4]; - float shadow_vec[3], tex_id; -} EEVEE_ShadowCascade; - -typedef struct EEVEE_ShadowCascadeRender { - /* World->Light->NDC : used for rendering the shadow map. */ - float projmat[MAX_CASCADE_NUM][4][4]; - float viewmat[4][4], viewinv[4][4]; - float radius[MAX_CASCADE_NUM]; - float original_bias; - float cascade_max_dist; - float cascade_exponent; - float cascade_fade; - int cascade_count; -} EEVEE_ShadowCascadeRender; - -BLI_STATIC_ASSERT_ALIGN(EEVEE_Light, 16) -BLI_STATIC_ASSERT_ALIGN(EEVEE_Shadow, 16) -BLI_STATIC_ASSERT_ALIGN(EEVEE_ShadowCube, 16) -BLI_STATIC_ASSERT_ALIGN(EEVEE_ShadowCascade, 16) -BLI_STATIC_ASSERT_ALIGN(EEVEE_RenderPassData, 16) - -BLI_STATIC_ASSERT(sizeof(EEVEE_Shadow) * MAX_SHADOW + - sizeof(EEVEE_ShadowCascade) * MAX_SHADOW_CASCADE + - sizeof(EEVEE_ShadowCube) * MAX_SHADOW_CUBE < - 16384, - "Shadow UBO is too big!!!") - -typedef struct EEVEE_ShadowCasterBuffer { - struct EEVEE_BoundBox *bbox; - BLI_bitmap *update; - uint alloc_count; - uint count; -} EEVEE_ShadowCasterBuffer; - -/* ************ LIGHT DATA ************* */ -typedef struct EEVEE_LightsInfo { - int num_light, cache_num_light; - int num_cube_layer, cache_num_cube_layer; - int num_cascade_layer, cache_num_cascade_layer; - int cube_len, cascade_len, shadow_len; - int shadow_cube_size, shadow_cascade_size; - bool shadow_high_bitdepth, soft_shadows; - /* UBO Storage : data used by UBO */ - struct EEVEE_Light light_data[MAX_LIGHT]; - struct EEVEE_Shadow shadow_data[MAX_SHADOW]; - struct EEVEE_ShadowCube shadow_cube_data[MAX_SHADOW_CUBE]; - struct EEVEE_ShadowCascade shadow_cascade_data[MAX_SHADOW_CASCADE]; - /* Additional rendering info for cascade. */ - struct EEVEE_ShadowCascadeRender shadow_cascade_render[MAX_SHADOW_CASCADE]; - /* Back index in light_data. */ - uchar shadow_cube_light_indices[MAX_SHADOW_CUBE]; - uchar shadow_cascade_light_indices[MAX_SHADOW_CASCADE]; - /* Update bitmap. */ - BLI_bitmap sh_cube_update[BLI_BITMAP_SIZE(MAX_SHADOW_CUBE)]; - /* Lights tracking */ - struct BoundSphere shadow_bounds[MAX_LIGHT]; /* Tightly packed light bounds. */ - /* List of bbox and update bitmap. Double buffered. */ - struct EEVEE_ShadowCasterBuffer *shcaster_frontbuffer, *shcaster_backbuffer; - /* AABB of all shadow casters combined. */ - struct { - float min[3], max[3]; - } shcaster_aabb; -} EEVEE_LightsInfo; - -/* ************ PROBE DATA ************* */ -typedef struct EEVEE_LightProbeVisTest { - struct Collection *collection; /* Skip test if NULL */ - bool invert; - bool cached; /* Reuse last test results */ -} EEVEE_LightProbeVisTest; - -typedef struct EEVEE_LightProbesInfo { - int num_cube, cache_num_cube; - int num_grid, cache_num_grid; - int num_planar, cache_num_planar; - int total_irradiance_samples; /* Total for all grids */ - int cache_irradiance_size[3]; - int update_flag; - int updated_bounce; - int num_bounce; - int cubemap_res; - /* Update */ - bool do_cube_update; - bool do_grid_update; - /* For rendering probes */ - float probemat[6][4][4]; - int layer; - float texel_size; - float padding_size; - float samples_len; - float near_clip; - float far_clip; - float roughness; - float firefly_fac; - float lodfactor; - float lod_rt_max, lod_cube_max; - float visibility_range; - float visibility_blur; - float intensity_fac; - int shres; - EEVEE_LightProbeVisTest planar_vis_tests[MAX_PLANAR]; - /* UBO Storage : data used by UBO */ - EEVEE_LightProbe probe_data[MAX_PROBE]; - EEVEE_LightGrid grid_data[MAX_GRID]; - EEVEE_PlanarReflection planar_data[MAX_PLANAR]; - /* Probe Visibility Collection */ - EEVEE_LightProbeVisTest vis_data; -} EEVEE_LightProbesInfo; - -/* EEVEE_LightProbesInfo->update_flag */ -enum { - PROBE_UPDATE_CUBE = (1 << 0), - PROBE_UPDATE_GRID = (1 << 1), - PROBE_UPDATE_ALL = 0xFFFFFF, -}; - -/* ************** MOTION BLUR ************ */ - -#define MB_PREV 0 -#define MB_NEXT 1 -#define MB_CURR 2 - -typedef struct EEVEE_MotionBlurData { - struct GHash *object; - struct GHash *geom; - struct { - float viewmat[4][4]; - float persmat[4][4]; - float persinv[4][4]; - } camera[3]; - DRWShadingGroup *hair_grp; -} EEVEE_MotionBlurData; - -typedef struct EEVEE_ObjectKey { - /** Object or source object for duplis */ - struct Object *ob; - /** Parent object for duplis */ - struct Object *parent; - /** Dupli objects recursive unique identifier */ - int id[8]; /* MAX_DUPLI_RECUR */ -} EEVEE_ObjectKey; - -typedef struct EEVEE_ObjectMotionData { - float obmat[3][4][4]; -} EEVEE_ObjectMotionData; - -typedef enum eEEVEEMotionData { - EEVEE_MOTION_DATA_MESH = 0, - EEVEE_MOTION_DATA_HAIR, -} eEEVEEMotionData; - -typedef struct EEVEE_HairMotionData { - /** Needs to be first to ensure casting. */ - eEEVEEMotionData type; - int use_deform; - /** Allocator will alloc enough slot for all particle systems. Or 1 if it's a hair object. */ - int psys_len; - struct { - struct GPUVertBuf *hair_pos[2]; /* Position buffer for time = t +/- step. */ - struct GPUTexture *hair_pos_tx[2]; /* Buffer Texture of the corresponding VBO. */ - } psys[0]; -} EEVEE_HairMotionData; - -typedef struct EEVEE_GeometryMotionData { - /** Needs to be first to ensure casting. */ - eEEVEEMotionData type; - /** To disable deform mb if vertcount mismatch. */ - int use_deform; - - struct GPUBatch *batch; /* Batch for time = t. */ - struct GPUVertBuf *vbo[2]; /* VBO for time = t +/- step. */ -} EEVEE_GeometryMotionData; - -/* ************ EFFECTS DATA ************* */ - -typedef enum EEVEE_EffectsFlag { - EFFECT_MOTION_BLUR = (1 << 0), - EFFECT_BLOOM = (1 << 1), - EFFECT_DOF = (1 << 2), - EFFECT_VOLUMETRIC = (1 << 3), - EFFECT_SSR = (1 << 4), - EFFECT_DOUBLE_BUFFER = (1 << 5), /* Not really an effect but a feature */ - EFFECT_REFRACT = (1 << 6), - EFFECT_GTAO = (1 << 7), - EFFECT_TAA = (1 << 8), - EFFECT_POST_BUFFER = (1 << 9), /* Not really an effect but a feature */ - EFFECT_NORMAL_BUFFER = (1 << 10), /* Not really an effect but a feature */ - EFFECT_RADIANCE_BUFFER = (1 << 10), /* Not really an effect but a feature */ - EFFECT_SSS = (1 << 11), - EFFECT_VELOCITY_BUFFER = (1 << 12), /* Not really an effect but a feature */ - EFFECT_TAA_REPROJECT = (1 << 13), /* should be mutually exclusive with EFFECT_TAA */ - EFFECT_DEPTH_DOUBLE_BUFFER = (1 << 14), /* Not really an effect but a feature */ -} EEVEE_EffectsFlag; - -typedef struct EEVEE_EffectsInfo { - EEVEE_EffectsFlag enabled_effects; - bool swap_double_buffer; - /* SSSS */ - int sss_sample_count; - int sss_surface_count; - struct GPUTexture *sss_irradiance; /* Textures from pool */ - struct GPUTexture *sss_radius; - struct GPUTexture *sss_albedo; - struct GPUTexture *sss_blur; - struct GPUTexture *sss_stencil; - /* Volumetrics */ - int volume_current_sample; - float volume_light_clamp; - struct GPUTexture *volume_scatter; - struct GPUTexture *volume_transmit; - /* SSR */ - bool reflection_trace_full; - bool ssr_was_persp; - bool ssr_was_valid_double_buffer; - struct GPUTexture *ssr_normal_input; /* Textures from pool */ - struct GPUTexture *ssr_specrough_input; - struct GPUTexture *ssr_hit_output; - struct GPUTexture *ssr_hit_depth; - /* Temporal Anti Aliasing */ - int taa_reproject_sample; - int taa_current_sample; - int taa_render_sample; - int taa_total_sample; - float taa_alpha; - bool bypass_drawing; - bool prev_drw_support; - bool prev_is_navigating; - float prev_drw_persmat[4][4]; /* Used for checking view validity and reprojection. */ - struct DRWView *taa_view; - /* Ambient Occlusion */ - struct GPUTexture *gtao_horizons; /* Textures from pool */ - struct GPUTexture *gtao_horizons_renderpass; /* Texture when rendering render pass */ - struct GPUTexture *gtao_horizons_debug; - /* Motion Blur */ - float current_ndc_to_world[4][4]; - float current_world_to_ndc[4][4]; - float current_world_to_view[4][4]; - float past_world_to_ndc[4][4]; - float past_world_to_view[4][4]; - CameraParams past_cam_params; - CameraParams current_cam_params; - char motion_blur_step; /* Which step we are evaluating. */ - int motion_blur_max; /* Maximum distance in pixels a motion-blurred pixel can cover. */ - float motion_blur_near_far[2]; /* Camera near/far clip distances (positive). */ - bool cam_params_init; - /* TODO(fclem): Only used in render mode for now. - * This is because we are missing a per scene persistent place to hold this. */ - struct EEVEE_MotionBlurData motion_blur; - /* Velocity Pass */ - struct GPUTexture *velocity_tx; /* Texture from pool */ - struct GPUTexture *velocity_tiles_x_tx; - struct GPUTexture *velocity_tiles_tx; - /* Depth Of Field */ - float dof_jitter_radius; - float dof_jitter_blades; - float dof_jitter_focus; - int dof_jitter_ring_count; - float dof_coc_params[2], dof_coc_near_dist, dof_coc_far_dist; - float dof_bokeh_blades, dof_bokeh_rotation, dof_bokeh_aniso[2], dof_bokeh_max_size; - float dof_bokeh_aniso_inv[2]; - float dof_scatter_color_threshold; - float dof_scatter_coc_threshold; - float dof_scatter_neighbor_max_color; - float dof_fx_max_coc; - float dof_denoise_factor; - int dof_dilate_slight_focus; - int dof_dilate_ring_count; - int dof_dilate_ring_width_multiplier; - int dof_reduce_steps; - bool dof_hq_slight_focus; - eGPUTextureFormat dof_color_format; - struct GPUTexture *dof_bg_color_tx; /* All textures from pool... */ - struct GPUTexture *dof_bg_occlusion_tx; - struct GPUTexture *dof_bg_weight_tx; - struct GPUTexture *dof_bokeh_gather_lut_tx; - struct GPUTexture *dof_bokeh_scatter_lut_tx; - struct GPUTexture *dof_bokeh_resolve_lut_tx; - struct GPUTexture *dof_coc_dilated_tiles_bg_tx; - struct GPUTexture *dof_coc_dilated_tiles_fg_tx; - struct GPUTexture *dof_coc_tiles_bg_tx; - struct GPUTexture *dof_coc_tiles_fg_tx; - struct GPUTexture *dof_downsample_tx; - struct GPUTexture *dof_fg_color_tx; - struct GPUTexture *dof_fg_occlusion_tx; - struct GPUTexture *dof_fg_weight_tx; - struct GPUTexture *dof_fg_holefill_color_tx; - struct GPUTexture *dof_fg_holefill_weight_tx; - struct GPUTexture *dof_half_res_coc_tx; - struct GPUTexture *dof_half_res_color_tx; - struct GPUTexture *dof_scatter_src_tx; - struct GPUTexture *dof_reduce_input_coc_tx; /* Just references to actual textures. */ - struct GPUTexture *dof_reduce_input_color_tx; - /* Other */ - float prev_persmat[4][4]; - /* Size used by all fullscreen buffers using mipmaps. */ - int hiz_size[2]; - /* Lookdev */ - int sphere_size; - eDRWLevelOfDetail sphere_lod; - int anchor[2]; - struct DRWView *lookdev_view; - /* Bloom */ - int bloom_iteration_len; - float source_texel_size[2]; - float blit_texel_size[2]; - float downsamp_texel_size[MAX_BLOOM_STEP][2]; - float bloom_color[3]; - float bloom_clamp; - float bloom_sample_scale; - float bloom_curve_threshold[4]; - float unf_source_texel_size[2]; - struct GPUTexture *bloom_blit; /* Textures from pool */ - struct GPUTexture *bloom_downsample[MAX_BLOOM_STEP]; - struct GPUTexture *bloom_upsample[MAX_BLOOM_STEP - 1]; - struct GPUTexture *unf_source_buffer; /* pointer copy */ - struct GPUTexture *unf_base_buffer; /* pointer copy */ - /* Not alloced, just a copy of a *GPUtexture in EEVEE_TextureList. */ - struct GPUTexture *source_buffer; /* latest updated texture */ - struct GPUFrameBuffer *target_buffer; /* next target to render to */ - struct GPUTexture *final_tx; /* Final color to transform to display color space. */ - struct GPUFrameBuffer *final_fb; /* Frame-buffer with final_tx as attachment. */ -} EEVEE_EffectsInfo; - -/* ***************** COMMON DATA **************** */ - -/* Common uniform buffer containing all "constant" data over the whole drawing pipeline. */ -/* !! CAUTION !! - * - [i]vec3 need to be padded to [i]vec4 (even in ubo declaration). - * - Make sure that [i]vec4 start at a multiple of 16 bytes. - * - Arrays of vec2/vec3 are padded as arrays of vec4. - * - sizeof(bool) == sizeof(int) in GLSL so use int in C */ -typedef struct EEVEE_CommonUniformBuffer { - float prev_persmat[4][4]; /* mat4 */ - float hiz_uv_scale[2], ssr_uv_scale[2]; /* vec4 */ - /* Ambient Occlusion */ - /* -- 16 byte aligned -- */ - float ao_dist, pad1, ao_factor, pad2; /* vec4 */ - float ao_offset, ao_bounce_fac, ao_quality, ao_settings; /* vec4 */ - /* Volumetric */ - /* -- 16 byte aligned -- */ - int vol_tex_size[3], pad3; /* ivec3 */ - float vol_depth_param[3], pad4; /* vec3 */ - float vol_inv_tex_size[3], pad5; /* vec3 */ - float vol_jitter[3], pad6; /* vec3 */ - float vol_coord_scale[4]; /* vec4 */ - /* -- 16 byte aligned -- */ - float vol_history_alpha; /* float */ - float vol_shadow_steps; /* float */ - int vol_use_lights; /* bool */ - int vol_use_soft_shadows; /* bool */ - /* Screen Space Reflections */ - /* -- 16 byte aligned -- */ - float ssr_quality, ssr_thickness, ssr_pixelsize[2]; /* vec4 */ - float ssr_border_fac; /* float */ - float ssr_max_roughness; /* float */ - float ssr_firefly_fac; /* float */ - float ssr_brdf_bias; /* float */ - int ssr_toggle; /* bool */ - int ssrefract_toggle; /* bool */ - /* SubSurface Scattering */ - float sss_jitter_threshold; /* float */ - int sss_toggle; /* bool */ - /* Specular */ - int spec_toggle; /* bool */ - /* Lights */ - int la_num_light; /* int */ - /* Probes */ - int prb_num_planar; /* int */ - int prb_num_render_cube; /* int */ - int prb_num_render_grid; /* int */ - int prb_irradiance_vis_size; /* int */ - float prb_irradiance_smooth; /* float */ - float prb_lod_cube_max; /* float */ - /* Misc */ - int ray_type; /* int */ - float ray_depth; /* float */ - float alpha_hash_offset; /* float */ - float alpha_hash_scale; /* float */ - float pad7; /* float */ - float pad8; /* float */ - float pad9; /* float */ - float pad10; /* float */ -} EEVEE_CommonUniformBuffer; - -BLI_STATIC_ASSERT_ALIGN(EEVEE_CommonUniformBuffer, 16) - -/* ray_type (keep in sync with rayType) */ -#define EEVEE_RAY_CAMERA 0 -#define EEVEE_RAY_SHADOW 1 -#define EEVEE_RAY_DIFFUSE 2 -#define EEVEE_RAY_GLOSSY 3 - -/* ************** SCENE LAYER DATA ************** */ -typedef struct EEVEE_ViewLayerData { - /* Lights */ - struct EEVEE_LightsInfo *lights; - - struct GPUUniformBuf *light_ubo; - struct GPUUniformBuf *shadow_ubo; - struct GPUUniformBuf *shadow_samples_ubo; - - struct GPUFrameBuffer *shadow_fb; - - struct GPUTexture *shadow_cube_pool; - struct GPUTexture *shadow_cascade_pool; - - struct EEVEE_ShadowCasterBuffer shcasters_buffers[2]; - - /* Probes */ - struct EEVEE_LightProbesInfo *probes; - - struct GPUUniformBuf *probe_ubo; - struct GPUUniformBuf *grid_ubo; - struct GPUUniformBuf *planar_ubo; - - /* Material Render passes */ - struct { - struct GPUUniformBuf *combined; - struct GPUUniformBuf *environment; - struct GPUUniformBuf *diff_color; - struct GPUUniformBuf *diff_light; - struct GPUUniformBuf *spec_color; - struct GPUUniformBuf *spec_light; - struct GPUUniformBuf *emit; - struct GPUUniformBuf *aovs[MAX_AOVS]; - } renderpass_ubo; - - /* Common Uniform Buffer */ - struct EEVEE_CommonUniformBuffer common_data; - struct GPUUniformBuf *common_ubo; - - struct LightCache *fallback_lightcache; - - struct BLI_memblock *material_cache; -} EEVEE_ViewLayerData; - -/* ************ OBJECT DATA ************ */ - -/* These are the structs stored inside Objects. - * It works even if the object is in multiple layers - * because we don't get the same "Object *" for each layer. */ -typedef struct EEVEE_LightEngineData { - DrawData dd; - - bool need_update; -} EEVEE_LightEngineData; - -typedef struct EEVEE_LightProbeEngineData { - DrawData dd; - - bool need_update; -} EEVEE_LightProbeEngineData; - -typedef struct EEVEE_ObjectEngineData { - DrawData dd; - - Object *ob; /* self reference */ - EEVEE_LightProbeVisTest *test_data; - bool ob_vis, ob_vis_dirty; - - bool need_update; - bool geom_update; - uint shadow_caster_id; -} EEVEE_ObjectEngineData; - -typedef struct EEVEE_WorldEngineData { - DrawData dd; -} EEVEE_WorldEngineData; - -typedef struct EEVEE_CryptomatteSample { - float hash; - float weight; -} EEVEE_CryptomatteSample; - -/* *********************************** */ - -typedef struct EEVEE_Data { - void *engine_type; - EEVEE_FramebufferList *fbl; - EEVEE_TextureList *txl; - EEVEE_PassList *psl; - EEVEE_StorageList *stl; - void *instance_data; - - char info[GPU_INFO_SIZE]; -} EEVEE_Data; - -typedef struct EEVEE_PrivateData { - struct DRWShadingGroup *shadow_shgrp; - struct DRWShadingGroup *shadow_accum_shgrp; - struct DRWCallBuffer *planar_display_shgrp; - struct GHash *material_hash; - float background_alpha; /* TODO: find a better place for this. */ - /* Chosen lightcache: can come from Lookdev or the viewlayer. */ - struct LightCache *light_cache; - /* For planar probes */ - float planar_texel_size[2]; - /* For double buffering */ - bool view_updated; - bool valid_double_buffer; - bool valid_taa_history; - /* Render Matrices */ - float studiolight_matrix[3][3]; - float overscan, overscan_pixels; - float camtexcofac[4]; - float size_orig[2]; - - /* Cached original camera when rendering for motion blur (see T79637). */ - struct Object *cam_original_ob; - - /* Mist Settings */ - float mist_start, mist_inv_dist, mist_falloff; - - /* Color Management */ - bool use_color_render_settings; - - /* Compiling shaders count. This is to track if a shader has finished compiling. */ - int queued_shaders_count; - int queued_shaders_count_prev; - - /* LookDev Settings */ - int studiolight_index; - float studiolight_rot_z; - float studiolight_intensity; - int studiolight_cubemap_res; - float studiolight_glossy_clamp; - float studiolight_filter_quality; - - /* Renderpasses */ - /* Bitmask containing the active render_passes */ - eViewLayerEEVEEPassType render_passes; - int aov_hash; - int num_aovs_used; - struct CryptomatteSession *cryptomatte_session; - bool cryptomatte_accurate_mode; - EEVEE_CryptomatteSample *cryptomatte_accum_buffer; - float *cryptomatte_download_buffer; - - /* Uniform references that are referenced inside the `renderpass_pass`. They are updated - * to reuse the drawing pass and the shading group. */ - int renderpass_type; - int renderpass_postprocess; - int renderpass_current_sample; - GPUTexture *renderpass_input; - GPUTexture *renderpass_col_input; - GPUTexture *renderpass_light_input; - GPUTexture *renderpass_transmittance_input; - /* Renderpass ubo reference used by material pass. */ - struct GPUUniformBuf *renderpass_ubo; - /** For rendering shadows. */ - struct DRWView *cube_views[6]; - /** For rendering probes. */ - struct DRWView *bake_views[6]; - /** Same as bake_views but does not generate culling infos. */ - struct DRWView *world_views[6]; - /** For rendering planar reflections. */ - struct DRWView *planar_views[MAX_PLANAR]; - - int render_timesteps; - int render_sample_count_per_timestep; -} EEVEE_PrivateData; /* Transient data */ - -/* eevee_data.c */ - -void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb); -void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb); -void EEVEE_view_layer_data_free(void *storage); -EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void); -EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer); -EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure(void); -EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob); -EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob); -EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, - Object *ob, - bool hair); -EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb, - Object *ob); -EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_MotionBlurData *mb, Object *ob); -EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob); -EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_ensure(Object *ob); -EEVEE_LightEngineData *EEVEE_light_data_get(Object *ob); -EEVEE_LightEngineData *EEVEE_light_data_ensure(Object *ob); -EEVEE_WorldEngineData *EEVEE_world_data_get(World *wo); -EEVEE_WorldEngineData *EEVEE_world_data_ensure(World *wo); - -void eevee_id_update(void *vedata, ID *id); - -/* eevee_materials.c */ - -struct GPUTexture *EEVEE_materials_get_util_tex(void); /* XXX */ -void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - EEVEE_StorageList *stl, - EEVEE_FramebufferList *fbl); -void EEVEE_materials_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_materials_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob, - bool *cast_shadow); -void EEVEE_particle_hair_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob, - bool *cast_shadow); -void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob, - bool *cast_shadow); -void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_materials_free(void); -void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const double offsets[3]); -void EEVEE_material_renderpasses_init(EEVEE_Data *vedata); -void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples); -void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -/** - * \param ssr_id: Can be null to disable SSR contribution. - */ -void EEVEE_material_bind_resources(DRWShadingGroup *shgrp, - struct GPUMaterial *gpumat, - EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - const int *ssr_id, - const float *refract_depth, - bool use_ssrefraction, - bool use_alpha_blend); -/* eevee_lights.c */ - -/** - * Reconstruct local `obmat` from EEVEE_light. (normalized). - */ -void eevee_light_matrix_get(const EEVEE_Light *evli, float r_mat[4][4]); -void EEVEE_lights_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_lights_cache_add(EEVEE_ViewLayerData *sldata, struct Object *ob); -void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); - -/* eevee_shadows.c */ - -void eevee_contact_shadow_setup(const Light *la, EEVEE_Shadow *evsh); -void EEVEE_shadows_init(EEVEE_ViewLayerData *sldata); -void EEVEE_shadows_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -/** - * Make that object update shadow casting lights inside its influence bounding box. - */ -void EEVEE_shadows_caster_register(EEVEE_ViewLayerData *sldata, struct Object *ob); -void EEVEE_shadows_update(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_shadows_cube_add(EEVEE_LightsInfo *linfo, EEVEE_Light *evli, struct Object *ob); -/** - * Return true if sample has changed and light needs to be updated. - */ -bool EEVEE_shadows_cube_setup(EEVEE_LightsInfo *linfo, const EEVEE_Light *evli, int sample_ofs); -void EEVEE_shadows_cascade_add(EEVEE_LightsInfo *linfo, EEVEE_Light *evli, struct Object *ob); -/** - * This refresh lights shadow buffers. - */ -void EEVEE_shadows_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, struct DRWView *view); -void EEVEE_shadows_draw_cubemap(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, int cube_index); -void EEVEE_shadows_draw_cascades(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - DRWView *view, - int cascade_index); -void EEVEE_shadow_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples); -void EEVEE_shadow_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); - -/* eevee_sampling.c */ - -/** - * Special ball distribution: - * Point are distributed in a way that when they are orthogonally - * projected into any plane, the resulting distribution is (close to) - * a uniform disc distribution. - */ -void EEVEE_sample_ball(int sample_ofs, float radius, float rsample[3]); -void EEVEE_sample_rectangle(int sample_ofs, - const float x_axis[3], - const float y_axis[3], - float size_x, - float size_y, - float rsample[3]); -void EEVEE_sample_ellipse(int sample_ofs, - const float x_axis[3], - const float y_axis[3], - float size_x, - float size_y, - float rsample[3]); -void EEVEE_random_rotation_m4(int sample_ofs, float scale, float r_mat[4][4]); - -/* eevee_shaders.c */ - -void EEVEE_shaders_material_shaders_init(void); -struct DRWShaderLibrary *EEVEE_shader_lib_get(void); -struct GPUShader *EEVEE_shaders_bloom_blit_get(bool high_quality); -struct GPUShader *EEVEE_shaders_bloom_downsample_get(bool high_quality); -struct GPUShader *EEVEE_shaders_bloom_upsample_get(bool high_quality); -struct GPUShader *EEVEE_shaders_bloom_resolve_get(bool high_quality); -struct GPUShader *EEVEE_shaders_depth_of_field_bokeh_get(void); -struct GPUShader *EEVEE_shaders_depth_of_field_setup_get(void); -struct GPUShader *EEVEE_shaders_depth_of_field_flatten_tiles_get(void); -struct GPUShader *EEVEE_shaders_depth_of_field_dilate_tiles_get(bool pass); -struct GPUShader *EEVEE_shaders_depth_of_field_downsample_get(void); -struct GPUShader *EEVEE_shaders_depth_of_field_reduce_get(bool is_copy_pass); -struct GPUShader *EEVEE_shaders_depth_of_field_gather_get(EEVEE_DofGatherPass pass, bool bokeh_tx); -struct GPUShader *EEVEE_shaders_depth_of_field_filter_get(void); -struct GPUShader *EEVEE_shaders_depth_of_field_scatter_get(bool is_foreground, bool bokeh_tx); -struct GPUShader *EEVEE_shaders_depth_of_field_resolve_get(bool use_bokeh_tx, bool use_hq_gather); -struct GPUShader *EEVEE_shaders_effect_color_copy_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_downsample_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_downsample_cube_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_minz_downlevel_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_maxz_downlevel_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_minz_downdepth_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_maxz_downdepth_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_minz_downdepth_layer_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_maxz_downdepth_layer_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_maxz_copydepth_layer_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_minz_copydepth_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_maxz_copydepth_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_mist_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_motion_blur_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_motion_blur_object_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_motion_blur_hair_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_motion_blur_velocity_tiles_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_motion_blur_velocity_tiles_expand_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_ambient_occlusion_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_ambient_occlusion_debug_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_reflection_trace_sh_get(void); -struct GPUShader *EEVEE_shaders_effect_reflection_resolve_sh_get(void); -struct GPUShader *EEVEE_shaders_renderpasses_post_process_sh_get(void); -struct GPUShader *EEVEE_shaders_cryptomatte_sh_get(bool is_hair); -struct GPUShader *EEVEE_shaders_shadow_sh_get(void); -struct GPUShader *EEVEE_shaders_shadow_accum_sh_get(void); -struct GPUShader *EEVEE_shaders_subsurface_first_pass_sh_get(void); -struct GPUShader *EEVEE_shaders_subsurface_second_pass_sh_get(void); -struct GPUShader *EEVEE_shaders_subsurface_translucency_sh_get(void); -struct GPUShader *EEVEE_shaders_volumes_clear_sh_get(void); -struct GPUShader *EEVEE_shaders_volumes_scatter_sh_get(void); -struct GPUShader *EEVEE_shaders_volumes_scatter_with_lights_sh_get(void); -struct GPUShader *EEVEE_shaders_volumes_integration_sh_get(void); -struct GPUShader *EEVEE_shaders_volumes_resolve_sh_get(bool accum); -struct GPUShader *EEVEE_shaders_volumes_accum_sh_get(void); -struct GPUShader *EEVEE_shaders_ggx_lut_sh_get(void); -struct GPUShader *EEVEE_shaders_ggx_refraction_lut_sh_get(void); -struct GPUShader *EEVEE_shaders_probe_filter_glossy_sh_get(void); -struct GPUShader *EEVEE_shaders_probe_filter_diffuse_sh_get(void); -struct GPUShader *EEVEE_shaders_probe_filter_visibility_sh_get(void); -struct GPUShader *EEVEE_shaders_probe_grid_fill_sh_get(void); -struct GPUShader *EEVEE_shaders_probe_planar_downsample_sh_get(void); -struct GPUShader *EEVEE_shaders_studiolight_probe_sh_get(void); -struct GPUShader *EEVEE_shaders_studiolight_background_sh_get(void); -struct GPUShader *EEVEE_shaders_probe_cube_display_sh_get(void); -struct GPUShader *EEVEE_shaders_probe_grid_display_sh_get(void); -struct GPUShader *EEVEE_shaders_probe_planar_display_sh_get(void); -struct GPUShader *EEVEE_shaders_update_noise_sh_get(void); -struct GPUShader *EEVEE_shaders_velocity_resolve_sh_get(void); -struct GPUShader *EEVEE_shaders_taa_resolve_sh_get(EEVEE_EffectsFlag enabled_effects); -/** - * Configure a default node-tree with the given material. - */ -struct bNodeTree *EEVEE_shader_default_surface_nodetree(Material *ma); -/** - * Configure a default node-tree with the given world. - */ -struct bNodeTree *EEVEE_shader_default_world_nodetree(World *wo); -Material *EEVEE_material_default_diffuse_get(void); -Material *EEVEE_material_default_glossy_get(void); -Material *EEVEE_material_default_error_get(void); -World *EEVEE_world_default_get(void); -/** - * \note Compilation is not deferred. - */ -struct GPUMaterial *EEVEE_material_default_get(struct Scene *scene, Material *ma, int options); -struct GPUMaterial *EEVEE_material_get( - EEVEE_Data *vedata, struct Scene *scene, Material *ma, World *wo, int options); -void EEVEE_shaders_free(void); - -/* eevee_lightprobes.c */ - -bool EEVEE_lightprobes_obj_visibility_cb(bool vis_in, void *user_data); -void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_lightprobes_cache_add(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *ob); -void EEVEE_lightprobes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_lightprobes_refresh(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_lightprobes_free(void); - -/** - * Only initialize the passes useful for rendering the light cache. - */ -void EEVEE_lightbake_cache_init(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - GPUTexture *rt_color, - GPUTexture *rt_depth); -void EEVEE_lightbake_render_world(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - struct GPUFrameBuffer *face_fb[6]); -/** - * Render the scene to the `probe_rt` texture. - */ -void EEVEE_lightbake_render_scene(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - struct GPUFrameBuffer *face_fb[6], - const float pos[3], - float near_clip, - float far_clip); -/** - * Glossy filter `rt_color` to `light_cache->cube_tx.tex` at index `probe_idx`. - */ -void EEVEE_lightbake_filter_glossy(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - struct GPUTexture *rt_color, - struct GPUFrameBuffer *fb, - int probe_idx, - float intensity, - int maxlevel, - float filter_quality, - float firefly_fac); -/** - * Diffuse filter `rt_color` to `light_cache->grid_tx.tex` at index `grid_offset`. - */ -void EEVEE_lightbake_filter_diffuse(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - struct GPUTexture *rt_color, - struct GPUFrameBuffer *fb, - int grid_offset, - float intensity); -/** - * Filter `rt_depth` to `light_cache->grid_tx.tex` at index `grid_offset`. - */ -void EEVEE_lightbake_filter_visibility(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - struct GPUTexture *rt_depth, - struct GPUFrameBuffer *fb, - int grid_offset, - float clipsta, - float clipend, - float vis_range, - float vis_blur, - int vis_size); - -void EEVEE_lightprobes_grid_data_from_object(Object *ob, EEVEE_LightGrid *egrid, int *offset); -void EEVEE_lightprobes_cube_data_from_object(Object *ob, EEVEE_LightProbe *eprobe); -void EEVEE_lightprobes_planar_data_from_object(Object *ob, - EEVEE_PlanarReflection *eplanar, - EEVEE_LightProbeVisTest *vis_test); - -/* eevee_depth_of_field.c */ - -int EEVEE_depth_of_field_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *camera); -void EEVEE_depth_of_field_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_depth_of_field_draw(EEVEE_Data *vedata); -bool EEVEE_depth_of_field_jitter_get(EEVEE_EffectsInfo *effects, - float r_jitter[2], - float *r_focus_distance); -int EEVEE_depth_of_field_sample_count_get(EEVEE_EffectsInfo *effects, - int sample_count, - int *r_ring_count); - -/* eevee_bloom.c */ - -int EEVEE_bloom_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_bloom_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_bloom_draw(EEVEE_Data *vedata); -void EEVEE_bloom_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples); -void EEVEE_bloom_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); - -/* eevee_cryptomatte.c */ - -void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata); -void EEVEE_cryptomatte_output_init(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - int tot_samples); -void EEVEE_cryptomatte_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob); -void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob); -void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob); -void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -/** - * Register the render passes needed for cryptomatte - * normally this is done in `EEVEE_render_update_passes`, but it has been placed here to keep - * related code side-by-side for clarity. - */ -void EEVEE_cryptomatte_update_passes(struct RenderEngine *engine, - struct Scene *scene, - struct ViewLayer *view_layer); -void EEVEE_cryptomatte_render_result(struct RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata); -void EEVEE_cryptomatte_store_metadata(EEVEE_Data *vedata, struct RenderResult *render_result); -void EEVEE_cryptomatte_free(EEVEE_Data *vedata); - -/* eevee_occlusion.c */ -int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - uint tot_samples); -void EEVEE_occlusion_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_occlusion_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_occlusion_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_occlusion_draw_debug(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_occlusion_free(void); - -/* eevee_screen_raytrace.c */ -int EEVEE_screen_raytrace_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_refraction_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_reflection_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_reflection_output_init(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - uint tot_samples); -void EEVEE_reflection_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); - -/* eevee_subsurface.c */ -void EEVEE_subsurface_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_subsurface_draw_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_subsurface_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_subsurface_output_init(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - uint tot_samples); -void EEVEE_subsurface_add_pass(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - Material *ma, - DRWShadingGroup *shgrp, - struct GPUMaterial *gpumat); -void EEVEE_subsurface_data_render(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_subsurface_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_subsurface_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); - -/* eevee_motion_blur.c */ -int EEVEE_motion_blur_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_motion_blur_step_set(EEVEE_Data *vedata, int step); -void EEVEE_motion_blur_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *ob); -void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - Object *ob, - struct ParticleSystem *psys, - struct ModifierData *md); -void EEVEE_motion_blur_swap_data(EEVEE_Data *vedata); -void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata); -void EEVEE_motion_blur_draw(EEVEE_Data *vedata); - -/* eevee_mist.c */ -void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_mist_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); - -/* eevee_renderpasses.c */ -void EEVEE_renderpasses_init(EEVEE_Data *vedata); -void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - uint tot_samples); -void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_renderpasses_output_accumulate(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - bool post_effect); -/** - * Post-process data to construct a specific render-pass - * - * This method will create a shading group to perform the post-processing for the given - * `renderpass_type`. The post-processing will be done and the result will be stored in the - * `vedata->txl->renderpass` texture. - * - * Only invoke this function for passes that need post-processing. - * - * After invoking this function the active frame-buffer is set to `vedata->fbl->renderpass_fb`. - */ -void EEVEE_renderpasses_postprocess(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - eViewLayerEEVEEPassType renderpass_type, - int aov_index); -void EEVEE_renderpasses_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_renderpasses_draw_debug(EEVEE_Data *vedata); -bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata); -/** - * Calculate the hash for an AOV. The least significant bit is used to store the AOV - * type the rest of the bits are used for the name hash. - */ -int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov); - -/* eevee_temporal_sampling.c */ -void EEVEE_temporal_sampling_reset(EEVEE_Data *vedata); -void EEVEE_temporal_sampling_create_view(EEVEE_Data *vedata); -int EEVEE_temporal_sampling_sample_count_get(const Scene *scene, const EEVEE_StorageList *stl); -int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_temporal_sampling_offset_calc(const double ht_point[2], - float filter_size, - float r_offset[2]); -void EEVEE_temporal_sampling_matrices_calc(EEVEE_EffectsInfo *effects, const double ht_point[2]); -/** - * Update the matrices based on the current sample. - * \note `DRW_MAT_PERS` and `DRW_MAT_VIEW` needs to read the original matrices. - */ -void EEVEE_temporal_sampling_update_matrices(EEVEE_Data *vedata); -void EEVEE_temporal_sampling_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_temporal_sampling_draw(EEVEE_Data *vedata); - -/* eevee_volumes.c */ - -void EEVEE_volumes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_volumes_set_jitter(EEVEE_ViewLayerData *sldata, uint current_sample); -void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - struct Scene *scene, - Object *ob); -void EEVEE_volumes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_volumes_draw_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_volumes_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_volumes_resolve(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_volumes_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples); -void EEVEE_volumes_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_volumes_free_smoke_textures(void); -void EEVEE_volumes_free(void); - -/* eevee_effects.c */ - -void EEVEE_effects_init(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - Object *camera, - bool minimal); -void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_effects_draw_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -/** - * Simple down-sampling algorithm. Reconstruct mip chain up to mip level. - */ -void EEVEE_effects_downsample_radiance_buffer(EEVEE_Data *vedata, struct GPUTexture *texture_src); -void EEVEE_create_minmax_buffer(EEVEE_Data *vedata, struct GPUTexture *depth_src, int layer); -/** - * Simple down-sampling algorithm for cube-map. Reconstruct mip chain up to mip level. - */ -void EEVEE_downsample_cube_buffer(EEVEE_Data *vedata, struct GPUTexture *texture_src, int level); -void EEVEE_draw_effects(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); - -/* eevee_render.c */ - -/** - * Return true if initialized properly. - */ -bool EEVEE_render_init(EEVEE_Data *vedata, - struct RenderEngine *engine, - struct Depsgraph *depsgraph); -void EEVEE_render_view_sync(EEVEE_Data *vedata, - struct RenderEngine *engine, - struct Depsgraph *depsgraph); -void EEVEE_render_modules_init(EEVEE_Data *vedata, - struct RenderEngine *engine, - struct Depsgraph *depsgraph); -void EEVEE_render_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -/** - * Used by light cache. in this case engine is NULL. - */ -void EEVEE_render_cache(void *vedata, - struct Object *ob, - struct RenderEngine *engine, - struct Depsgraph *depsgraph); -void EEVEE_render_draw(EEVEE_Data *vedata, - struct RenderEngine *engine, - struct RenderLayer *rl, - const struct rcti *rect); -void EEVEE_render_read_result(EEVEE_Data *vedata, - struct RenderEngine *engine, - struct RenderLayer *rl, - const rcti *rect); -void EEVEE_render_update_passes(struct RenderEngine *engine, - struct Scene *scene, - struct ViewLayer *view_layer); +struct EEVEE_Instance; -/** eevee_lookdev.c */ -void EEVEE_lookdev_init(EEVEE_Data *vedata); -void EEVEE_lookdev_cache_init(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - DRWPass *pass, - EEVEE_LightProbesInfo *pinfo, - DRWShadingGroup **r_shgrp); -void EEVEE_lookdev_draw(EEVEE_Data *vedata); +struct EEVEE_Instance *EEVEE_instance_alloc(void); +void EEVEE_instance_free(struct EEVEE_Instance *instance_data_); -/** eevee_engine.c */ -void EEVEE_cache_populate(void *vedata, Object *ob); +void EEVEE_instance_init(struct EEVEE_Instance *instance); -/** eevee_lut_gen.c */ -float *EEVEE_lut_update_ggx_brdf(int lut_size); -float *EEVEE_lut_update_ggx_btdf(int lut_size, int lut_depth); +void EEVEE_instance_cache_init(struct EEVEE_Instance *instance); +void EEVEE_instance_cache_populate(struct EEVEE_Instance *instance, struct Object *object); +void EEVEE_instance_cache_finish(struct EEVEE_Instance *instance); -/* Shadow Matrix */ -static const float texcomat[4][4] = { - /* From NDC to TexCo */ - {0.5f, 0.0f, 0.0f, 0.0f}, - {0.0f, 0.5f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.5f, 0.0f}, - {0.5f, 0.5f, 0.5f, 1.0f}, -}; +void EEVEE_instance_draw_viewport(struct EEVEE_Instance *instance_data_); +void EEVEE_instance_render_frame(struct EEVEE_Instance *instance_data_, + struct RenderEngine *engine, + struct RenderLayer *layer); -/* Cube-map Matrices */ -static const float cubefacemat[6][4][4] = { - /* Pos X */ - {{0.0f, 0.0f, -1.0f, 0.0f}, - {0.0f, -1.0f, 0.0f, 0.0f}, - {-1.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 1.0f}}, - /* Neg X */ - {{0.0f, 0.0f, 1.0f, 0.0f}, - {0.0f, -1.0f, 0.0f, 0.0f}, - {1.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 1.0f}}, - /* Pos Y */ - {{1.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, -1.0f, 0.0f}, - {0.0f, 1.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 1.0f}}, - /* Neg Y */ - {{1.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 1.0f, 0.0f}, - {0.0f, -1.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 1.0f}}, - /* Pos Z */ - {{1.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, -1.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, -1.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 1.0f}}, - /* Neg Z */ - {{-1.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, -1.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 1.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 1.0f}}, -}; +void EEVEE_shared_data_free(void); #ifdef __cplusplus } diff --git a/source/blender/draw/engines/eevee/eevee_raytracing.cc b/source/blender/draw/engines/eevee/eevee_raytracing.cc new file mode 100644 index 00000000000..613e24ada03 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_raytracing.cc @@ -0,0 +1,303 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Module containing passes and parameters used for raytracing. + * NOTE: For now only screen space raytracing is supported. + */ + +#include <fstream> +#include <iostream> + +#include "eevee_instance.hh" + +#include "eevee_raytracing.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Raytracing + * + * \{ */ + +void RaytracingModule::sync(void) +{ + SceneEEVEE &sce_eevee = inst_.scene->eevee; + + reflection_data_.thickness = sce_eevee.ssr_thickness; + reflection_data_.brightness_clamp = (sce_eevee.ssr_firefly_fac < 1e-8f) ? + FLT_MAX : + sce_eevee.ssr_firefly_fac; + reflection_data_.max_roughness = sce_eevee.ssr_max_roughness + 0.01f; + reflection_data_.quality = 1.0f - 0.95f * sce_eevee.ssr_quality; + reflection_data_.bias = 0.8f + sce_eevee.ssr_quality * 0.15f; + reflection_data_.pool_offset = inst_.sampling.sample_get() / 5; + + refraction_data_ = static_cast<RaytraceData>(reflection_data_); + // refraction_data_.thickness = 1e16; + /* TODO(fclem): Clamp option for refraction. */ + /* TODO(fclem): bias option for refraction. */ + /* TODO(fclem): bias option for refraction. */ + + diffuse_data_ = static_cast<RaytraceData>(reflection_data_); + diffuse_data_.max_roughness = 1.01f; + + reflection_data_.push_update(); + refraction_data_.push_update(); + diffuse_data_.push_update(); + + enabled_ = (sce_eevee.flag & SCE_EEVEE_RAYTRACING_ENABLED) != 0; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Raytracing Buffers + * + * \{ */ + +void RaytraceBuffer::sync(ivec2 extent) +{ + extent_ = extent; + dispatch_size_.x = divide_ceil_u(extent.x, 8); + dispatch_size_.y = divide_ceil_u(extent.y, 8); + dispatch_size_.z = 1; + + /* Make sure the history matrix is up to date. */ + data_.push_update(); + + LightProbeModule &lightprobes = inst_.lightprobes; + eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT; + + /* The raytracing buffer contains the draw passes since it is stored per view and we need to + * dispatch compute shaders with the right workgroup size. */ + + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL; + std::array<DRWShadingGroup *, 3> grps; + bool do_rt = inst_.raytracing.enabled(); + { + trace_reflection_ps_ = DRW_pass_create("TraceReflection", state); + GPUShader *sh = inst_.shaders.static_shader_get(do_rt ? RAYTRACE_REFLECTION : + RAYTRACE_REFLECTION_FALLBACK); + grps[0] = DRW_shgroup_create(sh, trace_reflection_ps_); + DRW_shgroup_uniform_block(grps[0], "raytrace_block", inst_.raytracing.reflection_ubo_get()); + DRW_shgroup_stencil_set(grps[0], 0x0, 0x0, CLOSURE_REFLECTION); + } + { + trace_refraction_ps_ = DRW_pass_create("TraceRefraction", state); + GPUShader *sh = inst_.shaders.static_shader_get(do_rt ? RAYTRACE_REFRACTION : + RAYTRACE_REFRACTION_FALLBACK); + grps[1] = DRW_shgroup_create(sh, trace_refraction_ps_); + DRW_shgroup_uniform_block(grps[1], "raytrace_block", inst_.raytracing.refraction_ubo_get()); + DRW_shgroup_stencil_set(grps[1], 0x0, 0x0, CLOSURE_REFRACTION); + } + { + trace_diffuse_ps_ = DRW_pass_create("TraceDiffuse", state); + GPUShader *sh = inst_.shaders.static_shader_get(do_rt ? RAYTRACE_DIFFUSE : + RAYTRACE_DIFFUSE_FALLBACK); + grps[2] = DRW_shgroup_create(sh, trace_diffuse_ps_); + DRW_shgroup_uniform_block(grps[2], "raytrace_block", inst_.raytracing.diffuse_ubo_get()); + DRW_shgroup_stencil_set(grps[2], 0x0, 0x0, CLOSURE_DIFFUSE); + } + + for (DRWShadingGroup *grp : grps) { + DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get()); + DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.ubo_get()); + DRW_shgroup_uniform_block(grp, "cubes_block", lightprobes.cube_ubo_get()); + DRW_shgroup_uniform_block(grp, "lightprobes_info_block", lightprobes.info_ubo_get()); + DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_tx_); + DRW_shgroup_uniform_texture_ref(grp, "hiz_front_tx", &input_hiz_front_tx_); + DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get()); + DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_radiance_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex(grp, "combined_tx", &input_combined_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex(grp, "cl_color_tx", &input_cl_color_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex(grp, "cl_normal_tx", &input_cl_normal_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex(grp, "cl_data_tx", &input_cl_data_tx_, no_interp); + DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + } + } + + { + /* Compute stage. No state needed. */ + DRWState state = (DRWState)0; + std::array<DRWShadingGroup *, 3> grps; + { + denoise_reflection_ps_ = DRW_pass_create("DenoiseReflection", state); + GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_DENOISE_REFLECTION); + grps[0] = DRW_shgroup_create(sh, denoise_reflection_ps_); + } + { + denoise_refraction_ps_ = DRW_pass_create("DenoiseRefraction", state); + GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_DENOISE_REFRACTION); + grps[1] = DRW_shgroup_create(sh, denoise_refraction_ps_); + } + { + denoise_diffuse_ps_ = DRW_pass_create("DenoiseDiffuse", state); + GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_DENOISE_DIFFUSE); + grps[2] = DRW_shgroup_create(sh, denoise_diffuse_ps_); + } + + for (DRWShadingGroup *grp : grps) { + /* Does not matter which raytrace_block we use. */ + DRW_shgroup_uniform_block(grp, "raytrace_block", inst_.raytracing.diffuse_ubo_get()); + DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.ubo_get()); + DRW_shgroup_uniform_block(grp, "rtbuffer_block", data_.ubo_get()); + DRW_shgroup_uniform_texture_ref_ex(grp, "ray_data_tx", &input_ray_data_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex(grp, "ray_radiance_tx", &input_ray_color_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex(grp, "hiz_tx", &input_hiz_front_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex(grp, "cl_color_tx", &input_cl_color_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex(grp, "cl_normal_tx", &input_cl_normal_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex(grp, "cl_data_tx", &input_cl_data_tx_, no_interp); + DRW_shgroup_uniform_texture_ref(grp, "ray_history_tx", &input_history_tx_); + DRW_shgroup_uniform_texture_ref(grp, "ray_variance_tx", &input_variance_tx_); + DRW_shgroup_uniform_image_ref(grp, "out_history_img", &output_history_tx_); + DRW_shgroup_uniform_image_ref(grp, "out_variance_img", &output_variance_tx_); + DRW_shgroup_call_compute_ref(grp, dispatch_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); + } + } + + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_ADD_FULL; + std::array<DRWShadingGroup *, 3> grps; + { + resolve_reflection_ps_ = DRW_pass_create("ResolveReflection", state); + GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_RESOLVE_REFLECTION); + grps[0] = DRW_shgroup_create(sh, resolve_reflection_ps_); + DRW_shgroup_stencil_set(grps[0], 0x0, 0x0, CLOSURE_REFLECTION); + } + { + resolve_refraction_ps_ = DRW_pass_create("ResolveRefraction", state); + GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_RESOLVE_REFRACTION); + grps[1] = DRW_shgroup_create(sh, resolve_refraction_ps_); + DRW_shgroup_stencil_set(grps[1], 0x0, 0x0, CLOSURE_REFRACTION); + } + { + resolve_diffuse_ps_ = DRW_pass_create("ResolveDiffuse", state); + GPUShader *sh = inst_.shaders.static_shader_get(RAYTRACE_RESOLVE_DIFFUSE); + grps[2] = DRW_shgroup_create(sh, resolve_diffuse_ps_); + DRW_shgroup_stencil_set(grps[2], 0x0, 0x0, CLOSURE_DIFFUSE); + } + + for (DRWShadingGroup *grp : grps) { + DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.ubo_get()); + DRW_shgroup_uniform_texture_ref_ex(grp, "ray_radiance_tx", &output_history_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex(grp, "ray_variance_tx", &output_variance_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex(grp, "cl_color_tx", &input_cl_color_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex(grp, "cl_normal_tx", &input_cl_normal_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex(grp, "cl_data_tx", &input_cl_data_tx_, no_interp); + // DRW_shgroup_call_compute_ref(grp, dispatch_size_); + // DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + } + } +} + +void RaytraceBuffer::trace(eClosureBits closure_type, + GBuffer &gbuffer, + HiZBuffer &hiz, + HiZBuffer &hiz_front) +{ + gbuffer.bind_tracing(); + + input_hiz_tx_ = hiz.texture_get(); + input_hiz_front_tx_ = hiz_front.texture_get(); + if (closure_type == CLOSURE_REFLECTION) { + input_cl_color_tx_ = gbuffer.reflect_color_tx; + input_cl_normal_tx_ = gbuffer.reflect_normal_tx; + input_cl_data_tx_ = gbuffer.reflect_normal_tx; + } + else { + input_cl_color_tx_ = gbuffer.transmit_color_tx; + input_cl_normal_tx_ = gbuffer.transmit_normal_tx; + input_cl_data_tx_ = gbuffer.transmit_data_tx; + } + + switch (closure_type) { + default: + case CLOSURE_REFLECTION: + input_radiance_tx_ = gbuffer.combined_tx; + DRW_draw_pass(trace_reflection_ps_); + break; + case CLOSURE_REFRACTION: + input_radiance_tx_ = gbuffer.combined_tx; + DRW_draw_pass(trace_refraction_ps_); + break; + case CLOSURE_DIFFUSE: + input_radiance_tx_ = gbuffer.diffuse_tx; + input_combined_tx_ = gbuffer.combined_tx; + DRW_draw_pass(trace_diffuse_ps_); + break; + } + + input_ray_data_tx_ = gbuffer.ray_data_tx; + input_ray_color_tx_ = gbuffer.ray_radiance_tx; +} + +void RaytraceBuffer::denoise(eClosureBits closure_type) +{ + switch (closure_type) { + default: + case CLOSURE_REFLECTION: + input_history_tx_ = reflection_radiance_history_get(); + input_variance_tx_ = reflection_variance_history_get(); + output_history_tx_ = reflection_radiance_get(); + output_variance_tx_ = reflection_variance_get(); + DRW_draw_pass(denoise_reflection_ps_); + break; + case CLOSURE_REFRACTION: + input_history_tx_ = refraction_radiance_history_get(); + input_variance_tx_ = refraction_variance_history_get(); + output_history_tx_ = refraction_radiance_get(); + output_variance_tx_ = refraction_variance_get(); + DRW_draw_pass(denoise_refraction_ps_); + break; + case CLOSURE_DIFFUSE: + input_history_tx_ = diffuse_radiance_history_get(); + input_variance_tx_ = diffuse_variance_history_get(); + output_history_tx_ = diffuse_radiance_get(); + output_variance_tx_ = diffuse_variance_get(); + DRW_draw_pass(denoise_diffuse_ps_); + break; + } +} + +void RaytraceBuffer::resolve(eClosureBits closure_type, GBuffer &gbuffer) +{ + gbuffer.bind_radiance(); + switch (closure_type) { + default: + case CLOSURE_REFLECTION: + DRW_draw_pass(resolve_reflection_ps_); + break; + case CLOSURE_REFRACTION: + DRW_draw_pass(resolve_refraction_ps_); + break; + case CLOSURE_DIFFUSE: + DRW_draw_pass(resolve_diffuse_ps_); + break; + } +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_raytracing.hh b/source/blender/draw/engines/eevee/eevee_raytracing.hh new file mode 100644 index 00000000000..e31a0eac1c1 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_raytracing.hh @@ -0,0 +1,228 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "DRW_render.h" + +#include "eevee_gbuffer.hh" +#include "eevee_shader_shared.hh" + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name Raytracing + * \{ */ + +class RaytracingModule { + private: + Instance &inst_; + + RaytraceDataBuf reflection_data_; + RaytraceDataBuf refraction_data_; + RaytraceDataBuf diffuse_data_; + + bool enabled_ = false; + + public: + RaytracingModule(Instance &inst) : inst_(inst){}; + + void sync(void); + + const GPUUniformBuf *reflection_ubo_get(void) const + { + return reflection_data_.ubo_get(); + } + const GPUUniformBuf *refraction_ubo_get(void) const + { + return refraction_data_.ubo_get(); + } + const GPUUniformBuf *diffuse_ubo_get(void) const + { + return diffuse_data_.ubo_get(); + } + + bool enabled(void) const + { + return enabled_; + } +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Raytracing Buffers + * + * Contain persistent data used for temporal denoising. Similar to \class GBuffer but only contains + * persistent data. + * \{ */ + +struct RaytraceBuffer { + public: + DRWPass *denoise_diffuse_ps_ = nullptr; + DRWPass *denoise_reflection_ps_ = nullptr; + DRWPass *denoise_refraction_ps_ = nullptr; + DRWPass *resolve_diffuse_ps_ = nullptr; + DRWPass *resolve_reflection_ps_ = nullptr; + DRWPass *resolve_refraction_ps_ = nullptr; + DRWPass *trace_diffuse_ps_ = nullptr; + DRWPass *trace_reflection_ps_ = nullptr; + DRWPass *trace_refraction_ps_ = nullptr; + + private: + Instance &inst_; + + /* Only allocated if used. */ + Texture diffuse_radiance_tx_ = Texture("DiffuseHistory_A"); + Texture diffuse_radiance_history_tx_ = Texture("DiffuseHistory_B"); + Texture diffuse_variance_tx_ = Texture("DiffuseVariance_A"); + Texture diffuse_variance_history_tx_ = Texture("DiffuseVariance_B"); + Texture reflection_radiance_tx_ = Texture("ReflectionHistory_A"); + Texture reflection_radiance_history_tx_ = Texture("ReflectionHistory_B"); + Texture reflection_variance_tx_ = Texture("ReflectionVariance_A"); + Texture reflection_variance_history_tx_ = Texture("ReflectionVariance_B"); + Texture refraction_radiance_tx_ = Texture("RefractionHistory_A"); + Texture refraction_radiance_history_tx_ = Texture("RefractionHistory_B"); + Texture refraction_variance_tx_ = Texture("RefractionVariance_A"); + Texture refraction_variance_history_tx_ = Texture("RefractionVariance_B"); + + /* Reference only. */ + GPUTexture *input_radiance_tx_; + GPUTexture *input_combined_tx_; + GPUTexture *input_ray_data_tx_; + GPUTexture *input_ray_color_tx_; + GPUTexture *input_hiz_tx_; + GPUTexture *input_hiz_front_tx_; + GPUTexture *input_cl_color_tx_; + GPUTexture *input_cl_normal_tx_; + GPUTexture *input_cl_data_tx_; + GPUTexture *input_history_tx_; + GPUTexture *input_variance_tx_; + GPUTexture *output_history_tx_; + GPUTexture *output_variance_tx_; + + RaytraceBufferDataBuf data_; + + ivec2 extent_ = ivec2(0); + ivec3 dispatch_size_ = ivec3(1); + + public: + RaytraceBuffer(Instance &inst) : inst_(inst){}; + ~RaytraceBuffer(){}; + + void sync(ivec2 extent); + + void trace(eClosureBits closure_type, GBuffer &gbuffer, HiZBuffer &hiz, HiZBuffer &hiz_front); + void denoise(eClosureBits closure_type); + void resolve(eClosureBits closure_type, GBuffer &gbuffer); + + GPUTexture *diffuse_radiance_history_get(void) + { + ensure_buffer(diffuse_radiance_history_tx_, data_.valid_history_diffuse, GPU_RGBA16F); + return diffuse_radiance_history_tx_; + } + GPUTexture *reflection_radiance_history_get(void) + { + ensure_buffer(reflection_radiance_history_tx_, data_.valid_history_reflection, GPU_RGBA16F); + return reflection_radiance_history_tx_; + } + GPUTexture *refraction_radiance_history_get(void) + { + ensure_buffer(refraction_radiance_history_tx_, data_.valid_history_refraction, GPU_RGBA16F); + return refraction_radiance_history_tx_; + } + + GPUTexture *diffuse_variance_history_get(void) + { + ensure_buffer(diffuse_variance_history_tx_, data_.valid_history_diffuse, GPU_R8); + return diffuse_variance_history_tx_; + } + GPUTexture *reflection_variance_history_get(void) + { + ensure_buffer(reflection_variance_history_tx_, data_.valid_history_reflection, GPU_R8); + return reflection_variance_history_tx_; + } + GPUTexture *refraction_variance_history_get(void) + { + ensure_buffer(refraction_variance_history_tx_, data_.valid_history_refraction, GPU_R8); + return refraction_variance_history_tx_; + } + + GPUTexture *diffuse_radiance_get(void) + { + ensure_buffer(diffuse_radiance_tx_, data_.valid_history_diffuse, GPU_RGBA16F); + return diffuse_radiance_tx_; + } + GPUTexture *reflection_radiance_get(void) + { + ensure_buffer(reflection_radiance_tx_, data_.valid_history_reflection, GPU_RGBA16F); + return reflection_radiance_tx_; + } + GPUTexture *refraction_radiance_get(void) + { + ensure_buffer(refraction_radiance_tx_, data_.valid_history_refraction, GPU_RGBA16F); + return refraction_radiance_tx_; + } + + GPUTexture *diffuse_variance_get(void) + { + ensure_buffer(diffuse_variance_tx_, data_.valid_history_diffuse, GPU_R8); + return diffuse_variance_tx_; + } + GPUTexture *reflection_variance_get(void) + { + ensure_buffer(reflection_variance_tx_, data_.valid_history_reflection, GPU_R8); + return reflection_variance_tx_; + } + GPUTexture *refraction_variance_get(void) + { + ensure_buffer(refraction_variance_tx_, data_.valid_history_refraction, GPU_R8); + return refraction_variance_tx_; + } + + void render_end(const DRWView *view) + { + DRW_view_persmat_get(view, data_.history_persmat, false); + SWAP(Texture, diffuse_radiance_tx_, diffuse_radiance_history_tx_); + SWAP(Texture, diffuse_variance_tx_, diffuse_variance_history_tx_); + SWAP(Texture, reflection_radiance_tx_, reflection_radiance_history_tx_); + SWAP(Texture, reflection_variance_tx_, reflection_variance_history_tx_); + SWAP(Texture, refraction_radiance_tx_, refraction_radiance_history_tx_); + SWAP(Texture, refraction_variance_tx_, refraction_variance_history_tx_); + } + + private: + void ensure_buffer(Texture &texture, int &valid_history, eGPUTextureFormat format) + { + bool was_allocated = texture.ensure(UNPACK2(extent_), 1, format); + if (was_allocated && valid_history) { + valid_history = false; + data_.push_update(); + } + else if (!was_allocated && !valid_history) { + valid_history = true; + data_.push_update(); + } + } +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c deleted file mode 100644 index 9e7a67060da..00000000000 --- a/source/blender/draw/engines/eevee/eevee_render.c +++ /dev/null @@ -1,743 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - */ - -/** - * Render functions for final render outputs. - */ - -#include "DRW_engine.h" -#include "DRW_render.h" - -#include "DNA_node_types.h" -#include "DNA_object_types.h" - -#include "BKE_global.h" -#include "BKE_object.h" - -#include "BLI_rand.h" -#include "BLI_rect.h" - -#include "DEG_depsgraph_query.h" - -#include "GPU_capabilities.h" -#include "GPU_framebuffer.h" -#include "GPU_state.h" - -#include "RE_pipeline.h" - -#include "eevee_private.h" - -bool EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph *depsgraph) -{ - EEVEE_Data *vedata = (EEVEE_Data *)ved; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_FramebufferList *fbl = vedata->fbl; - Scene *scene = DEG_get_evaluated_scene(depsgraph); - const float *size_orig = DRW_viewport_size_get(); - float size_final[2]; - - /* Init default FB and render targets: - * In render mode the default framebuffer is not generated - * because there is no viewport. So we need to manually create it or - * not use it. For code clarity we just allocate it make use of it. */ - DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - - /* Alloc transient data. */ - if (!stl->g_data) { - stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__); - } - EEVEE_PrivateData *g_data = stl->g_data; - g_data->background_alpha = DRW_state_draw_background() ? 1.0f : 0.0f; - g_data->valid_double_buffer = 0; - copy_v2_v2(g_data->size_orig, size_orig); - - float *camtexcofac = g_data->camtexcofac; - if (scene->eevee.flag & SCE_EEVEE_OVERSCAN) { - g_data->overscan = scene->eevee.overscan / 100.0f; - g_data->overscan_pixels = roundf(max_ff(size_orig[0], size_orig[1]) * g_data->overscan); - - madd_v2_v2v2fl(size_final, size_orig, (float[2]){2.0f, 2.0f}, g_data->overscan_pixels); - - camtexcofac[0] = size_final[0] / size_orig[0]; - camtexcofac[1] = size_final[1] / size_orig[1]; - - camtexcofac[2] = -camtexcofac[0] * g_data->overscan_pixels / size_final[0]; - camtexcofac[3] = -camtexcofac[1] * g_data->overscan_pixels / size_final[1]; - } - else { - copy_v2_v2(size_final, size_orig); - g_data->overscan = 0.0f; - g_data->overscan_pixels = 0.0f; - copy_v4_fl4(camtexcofac, 1.0f, 1.0f, 0.0f, 0.0f); - } - - const int final_res[2] = { - size_orig[0] + g_data->overscan_pixels * 2.0f, - size_orig[1] + g_data->overscan_pixels * 2.0f, - }; - - int max_dim = max_ii(final_res[0], final_res[1]); - if (max_dim > GPU_max_texture_size()) { - char error_msg[128]; - BLI_snprintf(error_msg, - sizeof(error_msg), - "Error: Reported texture size limit (%dpx) is lower than output size (%dpx).", - GPU_max_texture_size(), - max_dim); - RE_engine_set_error_message(engine, error_msg); - G.is_break = true; - return false; - } - - /* XXX overriding viewport size. Simplify things but is not really 100% safe. */ - DRW_render_viewport_size_set(final_res); - - /* TODO: 32 bit depth. */ - DRW_texture_ensure_fullscreen_2d(&dtxl->depth, GPU_DEPTH24_STENCIL8, 0); - DRW_texture_ensure_fullscreen_2d(&txl->color, GPU_RGBA32F, DRW_TEX_FILTER); - - GPU_framebuffer_ensure_config( - &dfbl->default_fb, - {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(txl->color)}); - GPU_framebuffer_ensure_config( - &fbl->main_fb, {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(txl->color)}); - GPU_framebuffer_ensure_config(&fbl->main_color_fb, - {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->color)}); - - /* Camera could change because of Motion blur. */ - g_data->cam_original_ob = RE_GetCamera(engine->re); - - return true; -} - -void EEVEE_render_modules_init(EEVEE_Data *vedata, - RenderEngine *engine, - struct Depsgraph *depsgraph) -{ - EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); - EEVEE_StorageList *stl = vedata->stl; - EEVEE_PrivateData *g_data = vedata->stl->g_data; - EEVEE_FramebufferList *fbl = vedata->fbl; - /* TODO(sergey): Shall render hold pointer to an evaluated camera instead? */ - struct Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, g_data->cam_original_ob); - EEVEE_render_view_sync(vedata, engine, depsgraph); - - /* `EEVEE_renderpasses_init` will set the active render passes used by `EEVEE_effects_init`. - * `EEVEE_effects_init` needs to go second for TAA. */ - EEVEE_renderpasses_init(vedata); - EEVEE_effects_init(sldata, vedata, ob_camera_eval, false); - EEVEE_materials_init(sldata, vedata, stl, fbl); - EEVEE_shadows_init(sldata); - EEVEE_lightprobes_init(sldata, vedata); -} - -void EEVEE_render_view_sync(EEVEE_Data *vedata, RenderEngine *engine, struct Depsgraph *depsgraph) -{ - EEVEE_PrivateData *g_data = vedata->stl->g_data; - - /* Set the perspective & view matrix. */ - float winmat[4][4], viewmat[4][4], viewinv[4][4]; - /* TODO(sergey): Shall render hold pointer to an evaluated camera instead? */ - struct Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, g_data->cam_original_ob); - - RE_GetCameraWindow(engine->re, ob_camera_eval, winmat); - RE_GetCameraWindowWithOverscan(engine->re, g_data->overscan, winmat); - RE_GetCameraModelMatrix(engine->re, ob_camera_eval, viewinv); - - invert_m4_m4(viewmat, viewinv); - - DRWView *view = DRW_view_create(viewmat, winmat, NULL, NULL, NULL); - DRW_view_reset(); - DRW_view_default_set(view); - DRW_view_set_active(view); - - DRW_view_camtexco_set(view, g_data->camtexcofac); -} - -void EEVEE_render_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_view_layer_data_ensure(); - EEVEE_bloom_cache_init(sldata, vedata); - EEVEE_depth_of_field_cache_init(sldata, vedata); - EEVEE_effects_cache_init(sldata, vedata); - EEVEE_lightprobes_cache_init(sldata, vedata); - EEVEE_lights_cache_init(sldata, vedata); - EEVEE_materials_cache_init(sldata, vedata); - EEVEE_motion_blur_cache_init(sldata, vedata); - EEVEE_occlusion_cache_init(sldata, vedata); - EEVEE_screen_raytrace_cache_init(sldata, vedata); - EEVEE_subsurface_cache_init(sldata, vedata); - EEVEE_temporal_sampling_cache_init(sldata, vedata); - EEVEE_volumes_cache_init(sldata, vedata); - EEVEE_cryptomatte_cache_init(sldata, vedata); -} - -void EEVEE_render_cache(void *vedata, - struct Object *ob, - struct RenderEngine *engine, - struct Depsgraph *depsgraph) -{ - EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); - EEVEE_Data *data = vedata; - EEVEE_StorageList *stl = data->stl; - EEVEE_PrivateData *g_data = stl->g_data; - EEVEE_LightProbesInfo *pinfo = sldata->probes; - bool cast_shadow = false; - - const bool do_cryptomatte = (engine != NULL) && - ((g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0); - - eevee_id_update(vedata, &ob->id); - - if (pinfo->vis_data.collection) { - /* Used for rendering probe with visibility groups. */ - bool ob_vis = BKE_collection_has_object_recursive(pinfo->vis_data.collection, ob); - ob_vis = (pinfo->vis_data.invert) ? !ob_vis : ob_vis; - - if (!ob_vis) { - return; - } - } - - /* Don't print dupli objects as this can be very verbose and - * increase the render time on Windows because of slow windows term. - * (see T59649) */ - if (engine && (ob->base_flag & BASE_FROM_DUPLI) == 0) { - char info[42]; - BLI_snprintf(info, sizeof(info), "Syncing %s", ob->id.name + 2); - RE_engine_update_stats(engine, NULL, info); - } - - const int ob_visibility = DRW_object_visibility_in_active_context(ob); - if (ob_visibility & OB_VISIBLE_PARTICLES) { - EEVEE_particle_hair_cache_populate(vedata, sldata, ob, &cast_shadow); - if (do_cryptomatte) { - EEVEE_cryptomatte_particle_hair_cache_populate(data, sldata, ob); - } - } - - if (ob_visibility & OB_VISIBLE_SELF) { - if (ELEM(ob->type, OB_MESH, OB_SURF, OB_MBALL)) { - EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow); - if (do_cryptomatte) { - EEVEE_cryptomatte_cache_populate(data, sldata, ob); - } - } - else if (ob->type == OB_HAIR) { - EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow); - if (do_cryptomatte) { - EEVEE_cryptomatte_object_hair_cache_populate(data, sldata, ob); - } - } - else if (ob->type == OB_VOLUME) { - Scene *scene = DEG_get_evaluated_scene(depsgraph); - EEVEE_volumes_cache_object_add(sldata, vedata, scene, ob); - } - else if (ob->type == OB_LIGHTPROBE) { - EEVEE_lightprobes_cache_add(sldata, vedata, ob); - } - else if (ob->type == OB_LAMP) { - EEVEE_lights_cache_add(sldata, ob); - } - } - - if (cast_shadow) { - EEVEE_shadows_caster_register(sldata, ob); - } -} - -static void eevee_render_color_result(RenderLayer *rl, - const char *viewname, - const rcti *rect, - const char *render_pass_name, - int num_channels, - GPUFrameBuffer *framebuffer, - EEVEE_Data *vedata) -{ - RenderPass *rp = RE_pass_find_by_name(rl, render_pass_name, viewname); - if (rp == NULL) { - return; - } - GPU_framebuffer_bind(framebuffer); - GPU_framebuffer_read_color(framebuffer, - vedata->stl->g_data->overscan_pixels + rect->xmin, - vedata->stl->g_data->overscan_pixels + rect->ymin, - BLI_rcti_size_x(rect), - BLI_rcti_size_y(rect), - num_channels, - 0, - GPU_DATA_FLOAT, - rp->rect); -} - -static void eevee_render_result_combined(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *UNUSED(sldata)) -{ - eevee_render_color_result( - rl, viewname, rect, RE_PASSNAME_COMBINED, 4, vedata->stl->effects->final_fb, vedata); -} - -static void eevee_render_result_normal(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata) -{ - const int current_sample = vedata->stl->effects->taa_current_sample; - - /* Only read the center texel. */ - if (current_sample > 1) { - return; - } - - if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_NORMAL) != 0) { - EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_NORMAL, 0); - eevee_render_color_result( - rl, viewname, rect, RE_PASSNAME_NORMAL, 3, vedata->fbl->renderpass_fb, vedata); - } -} - -static void eevee_render_result_z(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata) -{ - const int current_sample = vedata->stl->effects->taa_current_sample; - - /* Only read the center texel. */ - if (current_sample > 1) { - return; - } - - if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_Z) != 0) { - EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_Z, 0); - eevee_render_color_result( - rl, viewname, rect, RE_PASSNAME_Z, 1, vedata->fbl->renderpass_fb, vedata); - } -} - -static void eevee_render_result_mist(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata) -{ - if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_MIST) != 0) { - EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_MIST, 0); - eevee_render_color_result( - rl, viewname, rect, RE_PASSNAME_MIST, 1, vedata->fbl->renderpass_fb, vedata); - } -} - -static void eevee_render_result_shadow(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata) -{ - if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_SHADOW) != 0) { - EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_SHADOW, 0); - eevee_render_color_result( - rl, viewname, rect, RE_PASSNAME_SHADOW, 3, vedata->fbl->renderpass_fb, vedata); - } -} - -static void eevee_render_result_occlusion(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata) -{ - if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_AO) != 0) { - EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_AO, 0); - eevee_render_color_result( - rl, viewname, rect, RE_PASSNAME_AO, 3, vedata->fbl->renderpass_fb, vedata); - } -} - -static void eevee_render_result_bloom(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata) -{ - if ((vedata->stl->effects->enabled_effects & EFFECT_BLOOM) == 0) { - /* Bloom is not enabled. */ - return; - } - - if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_BLOOM) != 0) { - EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_BLOOM, 0); - eevee_render_color_result( - rl, viewname, rect, RE_PASSNAME_BLOOM, 3, vedata->fbl->renderpass_fb, vedata); - } -} - -#define EEVEE_RENDER_RESULT_MATERIAL_PASS(pass_name, eevee_pass_type) \ - if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_##eevee_pass_type) != 0) { \ - EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_##eevee_pass_type, 0); \ - eevee_render_color_result( \ - rl, viewname, rect, RE_PASSNAME_##pass_name, 3, vedata->fbl->renderpass_fb, vedata); \ - } - -static void eevee_render_result_diffuse_color(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata) -{ - EEVEE_RENDER_RESULT_MATERIAL_PASS(DIFFUSE_COLOR, DIFFUSE_COLOR) -} - -static void eevee_render_result_diffuse_direct(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata) -{ - EEVEE_RENDER_RESULT_MATERIAL_PASS(DIFFUSE_DIRECT, DIFFUSE_LIGHT) -} - -static void eevee_render_result_specular_color(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata) -{ - EEVEE_RENDER_RESULT_MATERIAL_PASS(GLOSSY_COLOR, SPECULAR_COLOR) -} - -static void eevee_render_result_specular_direct(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata) -{ - EEVEE_RENDER_RESULT_MATERIAL_PASS(GLOSSY_DIRECT, SPECULAR_LIGHT) -} - -static void eevee_render_result_emission(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata) -{ - EEVEE_RENDER_RESULT_MATERIAL_PASS(EMIT, EMIT) -} - -static void eevee_render_result_environment(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata) -{ - EEVEE_RENDER_RESULT_MATERIAL_PASS(ENVIRONMENT, ENVIRONMENT) -} - -static void eevee_render_result_volume_light(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata) -{ - EEVEE_RENDER_RESULT_MATERIAL_PASS(VOLUME_LIGHT, VOLUME_LIGHT) -} - -static void eevee_render_result_aovs(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata) -{ - if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_AOV) != 0) { - const DRWContextState *draw_ctx = DRW_context_state_get(); - ViewLayer *view_layer = draw_ctx->view_layer; - int aov_index = 0; - LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) { - if ((aov->flag & AOV_CONFLICT) != 0) { - continue; - } - EEVEE_renderpasses_postprocess(sldata, vedata, EEVEE_RENDER_PASS_AOV, aov_index); - switch (aov->type) { - case AOV_TYPE_COLOR: - eevee_render_color_result( - rl, viewname, rect, aov->name, 4, vedata->fbl->renderpass_fb, vedata); - break; - case AOV_TYPE_VALUE: - eevee_render_color_result( - rl, viewname, rect, aov->name, 1, vedata->fbl->renderpass_fb, vedata); - } - aov_index++; - } - } -} - -#undef EEVEE_RENDER_RESULT_MATERIAL_PASS - -static void eevee_render_result_cryptomatte(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata) -{ - if ((vedata->stl->g_data->render_passes & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) { - EEVEE_cryptomatte_render_result(rl, viewname, rect, vedata, sldata); - } -} - -static void eevee_render_draw_background(EEVEE_Data *vedata) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_PassList *psl = vedata->psl; - - /* Prevent background to write to data buffers. - * NOTE : This also make sure the textures are bound - * to the right double buffer. */ - GPU_framebuffer_ensure_config(&fbl->main_fb, - {GPU_ATTACHMENT_LEAVE, - GPU_ATTACHMENT_LEAVE, - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_NONE}); - GPU_framebuffer_bind(fbl->main_fb); - - DRW_draw_pass(psl->background_ps); - - GPU_framebuffer_ensure_config(&fbl->main_fb, - {GPU_ATTACHMENT_LEAVE, - GPU_ATTACHMENT_LEAVE, - GPU_ATTACHMENT_TEXTURE(stl->effects->ssr_normal_input), - GPU_ATTACHMENT_TEXTURE(stl->effects->ssr_specrough_input), - GPU_ATTACHMENT_TEXTURE(stl->effects->sss_irradiance), - GPU_ATTACHMENT_TEXTURE(stl->effects->sss_radius), - GPU_ATTACHMENT_TEXTURE(stl->effects->sss_albedo)}); - GPU_framebuffer_bind(fbl->main_fb); -} - -void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl, const rcti *rect) -{ - const char *viewname = RE_GetActiveRenderView(engine->re); - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_FramebufferList *fbl = vedata->fbl; - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); - - /* Push instances attributes to the GPU. */ - DRW_render_instance_buffer_finish(); - - /* Need to be called after DRW_render_instance_buffer_finish() */ - /* Also we weed to have a correct FBO bound for DRW_hair_update */ - GPU_framebuffer_bind(fbl->main_fb); - DRW_hair_update(); - - /* Sort transparents before the loop. */ - DRW_pass_sort_shgroup_z(psl->transparent_pass); - - uint tot_sample = stl->g_data->render_sample_count_per_timestep; - uint render_samples = 0; - - /* SSR needs one iteration to start properly. */ - if ((stl->effects->enabled_effects & EFFECT_SSR) && !stl->effects->ssr_was_valid_double_buffer) { - tot_sample += 1; - } - - while (render_samples < tot_sample && !RE_engine_test_break(engine)) { - const float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - float clear_depth = 1.0f; - uint clear_stencil = 0x00; - const uint primes[3] = {2, 3, 7}; - double offset[3] = {0.0, 0.0, 0.0}; - double r[3]; - - if ((stl->effects->enabled_effects & EFFECT_SSR) && (render_samples == 1) && - !stl->effects->ssr_was_valid_double_buffer) { - /* SSR needs one iteration to start properly. - * This iteration was done, reset to the original target sample count. */ - render_samples--; - tot_sample--; - /* Reset sampling (and accumulation) after the first sample to avoid - * washed out first bounce for SSR. */ - EEVEE_temporal_sampling_reset(vedata); - stl->effects->ssr_was_valid_double_buffer = stl->g_data->valid_double_buffer; - } - /* Don't print every samples as it can lead to bad performance. (see T59649) */ - else if ((render_samples % 25) == 0 || (render_samples + 1) == tot_sample) { - char info[42]; - BLI_snprintf( - info, sizeof(info), "Rendering %u / %u samples", render_samples + 1, tot_sample); - RE_engine_update_stats(engine, NULL, info); - } - - /* Copy previous persmat to UBO data */ - copy_m4_m4(sldata->common_data.prev_persmat, stl->effects->prev_persmat); - - BLI_halton_3d(primes, offset, stl->effects->taa_current_sample, r); - EEVEE_update_noise(psl, fbl, r); - EEVEE_temporal_sampling_matrices_calc(stl->effects, r); - EEVEE_volumes_set_jitter(sldata, stl->effects->taa_current_sample - 1); - EEVEE_materials_init(sldata, vedata, stl, fbl); - - /* Refresh Probes - * Shadows needs to be updated for correct probes */ - EEVEE_shadows_update(sldata, vedata); - EEVEE_lightprobes_refresh(sldata, vedata); - EEVEE_lightprobes_refresh_planar(sldata, vedata); - - /* Refresh Shadows */ - EEVEE_shadows_draw(sldata, vedata, stl->effects->taa_view); - - /* Set matrices. */ - DRW_view_set_active(stl->effects->taa_view); - - /* Set ray type. */ - sldata->common_data.ray_type = EEVEE_RAY_CAMERA; - sldata->common_data.ray_depth = 0.0f; - GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); - - GPU_framebuffer_bind(fbl->main_fb); - GPU_framebuffer_clear_color_depth_stencil(fbl->main_fb, clear_col, clear_depth, clear_stencil); - /* Depth prepass */ - DRW_draw_pass(psl->depth_ps); - /* Create minmax texture */ - EEVEE_create_minmax_buffer(vedata, dtxl->depth, -1); - EEVEE_occlusion_compute(sldata, vedata); - EEVEE_volumes_compute(sldata, vedata); - /* Shading pass */ - eevee_render_draw_background(vedata); - GPU_framebuffer_bind(fbl->main_fb); - DRW_draw_pass(psl->material_ps); - EEVEE_subsurface_data_render(sldata, vedata); - /* Effects pre-transparency */ - EEVEE_subsurface_compute(sldata, vedata); - EEVEE_reflection_compute(sldata, vedata); - EEVEE_refraction_compute(sldata, vedata); - /* Opaque refraction */ - DRW_draw_pass(psl->depth_refract_ps); - DRW_draw_pass(psl->material_refract_ps); - /* Result NORMAL */ - eevee_render_result_normal(rl, viewname, rect, vedata, sldata); - /* Volumetrics Resolve Opaque */ - EEVEE_volumes_resolve(sldata, vedata); - /* Subsurface output, Occlusion output, Mist output */ - EEVEE_renderpasses_output_accumulate(sldata, vedata, false); - /* Transparent */ - GPU_framebuffer_texture_attach(fbl->main_color_fb, dtxl->depth, 0, 0); - GPU_framebuffer_bind(fbl->main_color_fb); - DRW_draw_pass(psl->transparent_pass); - GPU_framebuffer_bind(fbl->main_fb); - GPU_framebuffer_texture_detach(fbl->main_color_fb, dtxl->depth); - /* Result Z */ - eevee_render_result_z(rl, viewname, rect, vedata, sldata); - /* Post Process */ - EEVEE_draw_effects(sldata, vedata); - - /* XXX Seems to fix TDR issue with NVidia drivers on linux. */ - GPU_finish(); - - RE_engine_update_progress(engine, (float)(render_samples++) / (float)tot_sample); - } -} - -void EEVEE_render_read_result(EEVEE_Data *vedata, - RenderEngine *engine, - RenderLayer *rl, - const rcti *rect) -{ - const char *viewname = RE_GetActiveRenderView(engine->re); - EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); - - eevee_render_result_combined(rl, viewname, rect, vedata, sldata); - eevee_render_result_mist(rl, viewname, rect, vedata, sldata); - eevee_render_result_occlusion(rl, viewname, rect, vedata, sldata); - eevee_render_result_shadow(rl, viewname, rect, vedata, sldata); - eevee_render_result_diffuse_color(rl, viewname, rect, vedata, sldata); - eevee_render_result_diffuse_direct(rl, viewname, rect, vedata, sldata); - eevee_render_result_specular_color(rl, viewname, rect, vedata, sldata); - eevee_render_result_specular_direct(rl, viewname, rect, vedata, sldata); - eevee_render_result_emission(rl, viewname, rect, vedata, sldata); - eevee_render_result_environment(rl, viewname, rect, vedata, sldata); - eevee_render_result_bloom(rl, viewname, rect, vedata, sldata); - eevee_render_result_volume_light(rl, viewname, rect, vedata, sldata); - eevee_render_result_aovs(rl, viewname, rect, vedata, sldata); - eevee_render_result_cryptomatte(rl, viewname, rect, vedata, sldata); -} - -void EEVEE_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer) -{ - RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA); - -#define CHECK_PASS_LEGACY(name, type, channels, chanid) \ - if (view_layer->passflag & (SCE_PASS_##name)) { \ - RE_engine_register_pass( \ - engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \ - } \ - ((void)0) -#define CHECK_PASS_EEVEE(name, type, channels, chanid) \ - if (view_layer->eevee.render_passes & (EEVEE_RENDER_PASS_##name)) { \ - RE_engine_register_pass( \ - engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \ - } \ - ((void)0) - - CHECK_PASS_LEGACY(Z, SOCK_FLOAT, 1, "Z"); - CHECK_PASS_LEGACY(MIST, SOCK_FLOAT, 1, "Z"); - CHECK_PASS_LEGACY(NORMAL, SOCK_VECTOR, 3, "XYZ"); - CHECK_PASS_LEGACY(DIFFUSE_DIRECT, SOCK_RGBA, 3, "RGB"); - CHECK_PASS_LEGACY(DIFFUSE_COLOR, SOCK_RGBA, 3, "RGB"); - CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB"); - CHECK_PASS_LEGACY(GLOSSY_COLOR, SOCK_RGBA, 3, "RGB"); - CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB"); - CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB"); - CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB"); - CHECK_PASS_LEGACY(SHADOW, SOCK_RGBA, 3, "RGB"); - CHECK_PASS_LEGACY(AO, SOCK_RGBA, 3, "RGB"); - CHECK_PASS_EEVEE(BLOOM, SOCK_RGBA, 3, "RGB"); - - LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) { - if ((aov->flag & AOV_CONFLICT) != 0) { - continue; - } - switch (aov->type) { - case AOV_TYPE_COLOR: - RE_engine_register_pass(engine, scene, view_layer, aov->name, 4, "RGBA", SOCK_RGBA); - break; - case AOV_TYPE_VALUE: - RE_engine_register_pass(engine, scene, view_layer, aov->name, 1, "X", SOCK_FLOAT); - break; - default: - break; - } - } - EEVEE_cryptomatte_update_passes(engine, scene, view_layer); - -#undef CHECK_PASS_LEGACY -#undef CHECK_PASS_EEVEE -} diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c deleted file mode 100644 index 3ebfc8a8f0f..00000000000 --- a/source/blender/draw/engines/eevee/eevee_renderpasses.c +++ /dev/null @@ -1,518 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2019, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - */ - -#include "DRW_engine.h" -#include "DRW_render.h" - -#include "draw_color_management.h" /* TODO: remove dependency. */ - -#include "BKE_global.h" /* for G.debug_value */ - -#include "BLI_hash.h" -#include "BLI_string_utils.h" - -#include "DEG_depsgraph_query.h" - -#include "eevee_private.h" - -typedef enum eRenderPassPostProcessType { - PASS_POST_UNDEFINED = 0, - PASS_POST_ACCUMULATED_COLOR = 1, - PASS_POST_ACCUMULATED_COLOR_ALPHA = 2, - PASS_POST_ACCUMULATED_LIGHT = 3, - PASS_POST_ACCUMULATED_VALUE = 4, - PASS_POST_DEPTH = 5, - PASS_POST_AO = 6, - PASS_POST_NORMAL = 7, - PASS_POST_TWO_LIGHT_BUFFERS = 8, - PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR = 9, -} eRenderPassPostProcessType; - -/* bitmask containing all renderpasses that need post-processing */ -#define EEVEE_RENDERPASSES_WITH_POST_PROCESSING \ - (EEVEE_RENDER_PASS_Z | EEVEE_RENDER_PASS_MIST | EEVEE_RENDER_PASS_NORMAL | \ - EEVEE_RENDER_PASS_AO | EEVEE_RENDER_PASS_BLOOM | EEVEE_RENDER_PASS_VOLUME_LIGHT | \ - EEVEE_RENDER_PASS_SHADOW | EEVEE_RENDERPASSES_MATERIAL) - -#define EEVEE_RENDERPASSES_ALL \ - (EEVEE_RENDERPASSES_WITH_POST_PROCESSING | EEVEE_RENDER_PASS_COMBINED) - -#define EEVEE_RENDERPASSES_POST_PROCESS_ON_FIRST_SAMPLE \ - (EEVEE_RENDER_PASS_Z | EEVEE_RENDER_PASS_NORMAL) - -#define EEVEE_RENDERPASSES_COLOR_PASS \ - (EEVEE_RENDER_PASS_DIFFUSE_COLOR | EEVEE_RENDER_PASS_SPECULAR_COLOR | EEVEE_RENDER_PASS_EMIT | \ - EEVEE_RENDER_PASS_BLOOM) -#define EEVEE_RENDERPASSES_LIGHT_PASS \ - (EEVEE_RENDER_PASS_DIFFUSE_LIGHT | EEVEE_RENDER_PASS_SPECULAR_LIGHT) -/* Render passes that uses volume transmittance when available */ -#define EEVEE_RENDERPASSES_USES_TRANSMITTANCE \ - (EEVEE_RENDER_PASS_DIFFUSE_COLOR | EEVEE_RENDER_PASS_SPECULAR_COLOR | EEVEE_RENDER_PASS_EMIT | \ - EEVEE_RENDER_PASS_ENVIRONMENT) -bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata) -{ - EEVEE_StorageList *stl = vedata->stl; - EEVEE_PrivateData *g_data = stl->g_data; - return (g_data->render_passes & ~EEVEE_RENDERPASSES_POST_PROCESS_ON_FIRST_SAMPLE) == 0; -} - -int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov) -{ - int hash = BLI_hash_string(aov->name) << 1; - SET_FLAG_FROM_TEST(hash, aov->type == AOV_TYPE_COLOR, EEVEE_AOV_HASH_COLOR_TYPE_MASK); - return hash; -} - -void EEVEE_renderpasses_init(EEVEE_Data *vedata) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - EEVEE_StorageList *stl = vedata->stl; - EEVEE_PrivateData *g_data = stl->g_data; - ViewLayer *view_layer = draw_ctx->view_layer; - View3D *v3d = draw_ctx->v3d; - - if (v3d) { - const Scene *scene = draw_ctx->scene; - eViewLayerEEVEEPassType render_pass = v3d->shading.render_pass; - g_data->aov_hash = 0; - - if (render_pass == EEVEE_RENDER_PASS_BLOOM && - ((scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) == 0)) { - render_pass = EEVEE_RENDER_PASS_COMBINED; - } - if (render_pass == EEVEE_RENDER_PASS_AOV) { - ViewLayerAOV *aov = BLI_findstring( - &view_layer->aovs, v3d->shading.aov_name, offsetof(ViewLayerAOV, name)); - if (aov != NULL) { - g_data->aov_hash = EEVEE_renderpasses_aov_hash(aov); - } - else { - /* AOV not found in view layer. */ - render_pass = EEVEE_RENDER_PASS_COMBINED; - } - } - - g_data->render_passes = render_pass; - } - else { - eViewLayerEEVEEPassType enabled_render_passes = view_layer->eevee.render_passes; - -#define ENABLE_FROM_LEGACY(name_legacy, name_eevee) \ - SET_FLAG_FROM_TEST(enabled_render_passes, \ - (view_layer->passflag & SCE_PASS_##name_legacy) != 0, \ - EEVEE_RENDER_PASS_##name_eevee); - - ENABLE_FROM_LEGACY(Z, Z) - ENABLE_FROM_LEGACY(MIST, MIST) - ENABLE_FROM_LEGACY(NORMAL, NORMAL) - ENABLE_FROM_LEGACY(SHADOW, SHADOW) - ENABLE_FROM_LEGACY(AO, AO) - ENABLE_FROM_LEGACY(EMIT, EMIT) - ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT) - ENABLE_FROM_LEGACY(DIFFUSE_COLOR, DIFFUSE_COLOR) - ENABLE_FROM_LEGACY(GLOSSY_COLOR, SPECULAR_COLOR) - ENABLE_FROM_LEGACY(DIFFUSE_DIRECT, DIFFUSE_LIGHT) - ENABLE_FROM_LEGACY(GLOSSY_DIRECT, SPECULAR_LIGHT) - - ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT) - -#undef ENABLE_FROM_LEGACY - if (DRW_state_is_image_render() && !BLI_listbase_is_empty(&view_layer->aovs)) { - enabled_render_passes |= EEVEE_RENDER_PASS_AOV; - g_data->aov_hash = EEVEE_AOV_HASH_ALL; - } - - g_data->render_passes = (enabled_render_passes & EEVEE_RENDERPASSES_ALL) | - EEVEE_RENDER_PASS_COMBINED; - } - EEVEE_material_renderpasses_init(vedata); - EEVEE_cryptomatte_renderpasses_init(vedata); -} - -BLI_INLINE bool eevee_renderpasses_volumetric_active(const EEVEE_EffectsInfo *effects, - const EEVEE_PrivateData *g_data) -{ - if (effects->enabled_effects & EFFECT_VOLUMETRIC) { - if (g_data->render_passes & - (EEVEE_RENDER_PASS_VOLUME_LIGHT | EEVEE_RENDERPASSES_USES_TRANSMITTANCE)) { - return true; - } - } - return false; -} - -void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - uint tot_samples) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - EEVEE_PrivateData *g_data = stl->g_data; - - const bool needs_post_processing = (g_data->render_passes & - EEVEE_RENDERPASSES_WITH_POST_PROCESSING) > 0; - if (needs_post_processing) { - /* Create FrameBuffer. */ - - /* Should be enough to store the data needs for a single pass. - * Some passes will use less, but it is only relevant for final renderings and - * when renderpasses other than `EEVEE_RENDER_PASS_COMBINED` are requested */ - DRW_texture_ensure_fullscreen_2d(&txl->renderpass, GPU_RGBA16F, 0); - GPU_framebuffer_ensure_config(&fbl->renderpass_fb, - {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->renderpass)}); - - if ((g_data->render_passes & EEVEE_RENDERPASSES_MATERIAL) != 0) { - EEVEE_material_output_init(sldata, vedata, tot_samples); - } - - if ((g_data->render_passes & EEVEE_RENDER_PASS_MIST) != 0) { - EEVEE_mist_output_init(sldata, vedata); - } - if ((g_data->render_passes & EEVEE_RENDER_PASS_SHADOW) != 0) { - EEVEE_shadow_output_init(sldata, vedata, tot_samples); - } - - if ((g_data->render_passes & EEVEE_RENDER_PASS_AO) != 0) { - EEVEE_occlusion_output_init(sldata, vedata, tot_samples); - } - - if ((g_data->render_passes & EEVEE_RENDER_PASS_BLOOM) != 0 && - (effects->enabled_effects & EFFECT_BLOOM) != 0) { - EEVEE_bloom_output_init(sldata, vedata, tot_samples); - } - - if (eevee_renderpasses_volumetric_active(effects, g_data)) { - EEVEE_volumes_output_init(sldata, vedata, tot_samples); - } - - /* We set a default texture as not all post processes uses the inputBuffer. */ - g_data->renderpass_input = txl->color; - g_data->renderpass_col_input = txl->color; - g_data->renderpass_light_input = txl->color; - g_data->renderpass_transmittance_input = txl->color; - } - else { - /* Free unneeded memory */ - DRW_TEXTURE_FREE_SAFE(txl->renderpass); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->renderpass_fb); - } - - /* Cryptomatte doesn't use the GPU shader for post processing */ - if ((g_data->render_passes & (EEVEE_RENDER_PASS_CRYPTOMATTE)) != 0) { - EEVEE_cryptomatte_output_init(sldata, vedata, tot_samples); - } -} - -void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_PrivateData *g_data = vedata->stl->g_data; - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - - const bool needs_post_processing = (g_data->render_passes & - EEVEE_RENDERPASSES_WITH_POST_PROCESSING) > 0; - if (needs_post_processing) { - DRW_PASS_CREATE(psl->renderpass_pass, DRW_STATE_WRITE_COLOR); - DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_renderpasses_post_process_sh_get(), - psl->renderpass_pass); - DRW_shgroup_uniform_texture_ref(grp, "inputBuffer", &g_data->renderpass_input); - DRW_shgroup_uniform_texture_ref(grp, "inputColorBuffer", &g_data->renderpass_col_input); - DRW_shgroup_uniform_texture_ref( - grp, "inputSecondLightBuffer", &g_data->renderpass_light_input); - DRW_shgroup_uniform_texture_ref( - grp, "inputTransmittanceBuffer", &g_data->renderpass_transmittance_input); - DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); - DRW_shgroup_uniform_block_ref(grp, "common_block", &sldata->common_ubo); - DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &sldata->renderpass_ubo.combined); - DRW_shgroup_uniform_int(grp, "currentSample", &g_data->renderpass_current_sample, 1); - DRW_shgroup_uniform_int(grp, "renderpassType", &g_data->renderpass_type, 1); - DRW_shgroup_uniform_int(grp, "postProcessType", &g_data->renderpass_postprocess, 1); - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); - } - else { - psl->renderpass_pass = NULL; - } -} - -void EEVEE_renderpasses_postprocess(EEVEE_ViewLayerData *UNUSED(sldata), - EEVEE_Data *vedata, - eViewLayerEEVEEPassType renderpass_type, - int aov_index) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_PrivateData *g_data = stl->g_data; - EEVEE_EffectsInfo *effects = stl->effects; - - /* Compensate for taa_current_sample being incremented after last drawing in - * EEVEE_temporal_sampling_draw when DRW_state_is_image_render(). */ - const int current_sample = DRW_state_is_image_render() ? effects->taa_current_sample - 1 : - effects->taa_current_sample; - g_data->renderpass_current_sample = current_sample; - g_data->renderpass_type = renderpass_type; - g_data->renderpass_postprocess = PASS_POST_UNDEFINED; - const bool volumetric_active = eevee_renderpasses_volumetric_active(effects, g_data); - eRenderPassPostProcessType default_color_pass_type = - volumetric_active ? PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR : PASS_POST_ACCUMULATED_COLOR; - g_data->renderpass_transmittance_input = volumetric_active ? txl->volume_transmittance_accum : - txl->color; - - if (!volumetric_active && renderpass_type == EEVEE_RENDER_PASS_VOLUME_LIGHT) { - /* Early exit: Volumetric effect is off, but the volume light pass was requested. */ - static float clear_col[4] = {0.0f}; - GPU_framebuffer_bind(fbl->renderpass_fb); - GPU_framebuffer_clear_color(fbl->renderpass_fb, clear_col); - return; - } - - switch (renderpass_type) { - case EEVEE_RENDER_PASS_Z: { - g_data->renderpass_postprocess = PASS_POST_DEPTH; - break; - } - case EEVEE_RENDER_PASS_AO: { - g_data->renderpass_postprocess = PASS_POST_AO; - g_data->renderpass_input = txl->ao_accum; - break; - } - case EEVEE_RENDER_PASS_NORMAL: { - g_data->renderpass_postprocess = PASS_POST_NORMAL; - g_data->renderpass_input = effects->ssr_normal_input; - break; - } - case EEVEE_RENDER_PASS_MIST: { - g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_VALUE; - g_data->renderpass_input = txl->mist_accum; - break; - } - case EEVEE_RENDER_PASS_VOLUME_LIGHT: { - g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR; - g_data->renderpass_input = txl->volume_scatter_accum; - break; - } - case EEVEE_RENDER_PASS_SHADOW: { - g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_VALUE; - g_data->renderpass_input = txl->shadow_accum; - break; - } - case EEVEE_RENDER_PASS_DIFFUSE_COLOR: { - g_data->renderpass_postprocess = default_color_pass_type; - g_data->renderpass_input = txl->diff_color_accum; - break; - } - case EEVEE_RENDER_PASS_SPECULAR_COLOR: { - g_data->renderpass_postprocess = default_color_pass_type; - g_data->renderpass_input = txl->spec_color_accum; - break; - } - case EEVEE_RENDER_PASS_ENVIRONMENT: { - g_data->renderpass_postprocess = default_color_pass_type; - g_data->renderpass_input = txl->env_accum; - break; - } - case EEVEE_RENDER_PASS_EMIT: { - g_data->renderpass_postprocess = default_color_pass_type; - g_data->renderpass_input = txl->emit_accum; - break; - } - case EEVEE_RENDER_PASS_SPECULAR_LIGHT: { - g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_LIGHT; - g_data->renderpass_input = txl->spec_light_accum; - g_data->renderpass_col_input = txl->spec_color_accum; - if ((stl->effects->enabled_effects & EFFECT_SSR) != 0) { - g_data->renderpass_postprocess = PASS_POST_TWO_LIGHT_BUFFERS; - g_data->renderpass_light_input = txl->ssr_accum; - } - else { - g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_LIGHT; - } - break; - } - case EEVEE_RENDER_PASS_DIFFUSE_LIGHT: { - g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_LIGHT; - g_data->renderpass_input = txl->diff_light_accum; - g_data->renderpass_col_input = txl->diff_color_accum; - if ((stl->effects->enabled_effects & EFFECT_SSS) != 0) { - g_data->renderpass_postprocess = PASS_POST_TWO_LIGHT_BUFFERS; - g_data->renderpass_light_input = txl->sss_accum; - } - else { - g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_LIGHT; - } - break; - } - case EEVEE_RENDER_PASS_AOV: { - g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR_ALPHA; - g_data->renderpass_input = txl->aov_surface_accum[aov_index]; - break; - } - case EEVEE_RENDER_PASS_BLOOM: { - g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR; - g_data->renderpass_input = txl->bloom_accum; - g_data->renderpass_current_sample = 1; - break; - } - default: { - break; - } - } - GPU_framebuffer_bind(fbl->renderpass_fb); - DRW_draw_pass(psl->renderpass_pass); -} - -void EEVEE_renderpasses_output_accumulate(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - bool post_effect) -{ - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - EEVEE_PrivateData *g_data = stl->g_data; - eViewLayerEEVEEPassType render_pass = g_data->render_passes; - - if (!post_effect) { - if ((render_pass & EEVEE_RENDER_PASS_MIST) != 0) { - EEVEE_mist_output_accumulate(sldata, vedata); - } - if ((render_pass & EEVEE_RENDER_PASS_AO) != 0) { - EEVEE_occlusion_output_accumulate(sldata, vedata); - } - if ((render_pass & EEVEE_RENDER_PASS_SHADOW) != 0) { - EEVEE_shadow_output_accumulate(sldata, vedata); - } - if ((render_pass & EEVEE_RENDERPASSES_MATERIAL) != 0) { - EEVEE_material_output_accumulate(sldata, vedata); - } - if (eevee_renderpasses_volumetric_active(effects, g_data)) { - EEVEE_volumes_output_accumulate(sldata, vedata); - } - if ((render_pass & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) { - EEVEE_cryptomatte_output_accumulate(sldata, vedata); - } - } - else { - if ((render_pass & EEVEE_RENDER_PASS_BLOOM) != 0 && - (effects->enabled_effects & EFFECT_BLOOM) != 0) { - EEVEE_bloom_output_accumulate(sldata, vedata); - } - } -} - -void EEVEE_renderpasses_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); - - /* We can only draw a single render-pass. Light-passes also select their color pass - * (a second pass). We mask the light pass when a light pass is selected. */ - const eViewLayerEEVEEPassType render_pass = - ((stl->g_data->render_passes & EEVEE_RENDERPASSES_LIGHT_PASS) != 0) ? - (stl->g_data->render_passes & EEVEE_RENDERPASSES_LIGHT_PASS) : - stl->g_data->render_passes; - - bool is_valid = (render_pass & EEVEE_RENDERPASSES_ALL) != 0; - bool needs_color_transfer = (render_pass & EEVEE_RENDERPASSES_COLOR_PASS) != 0 && - DRW_state_is_opengl_render(); - UNUSED_VARS(needs_color_transfer); - - if ((render_pass & EEVEE_RENDER_PASS_BLOOM) != 0 && - (effects->enabled_effects & EFFECT_BLOOM) == 0) { - is_valid = false; - } - - const int current_sample = stl->effects->taa_current_sample; - const int total_samples = stl->effects->taa_total_sample; - if ((render_pass & EEVEE_RENDERPASSES_POST_PROCESS_ON_FIRST_SAMPLE) && - (current_sample > 1 && total_samples != 1)) { - return; - } - - if (is_valid) { - EEVEE_renderpasses_postprocess(sldata, vedata, render_pass, 0); - GPU_framebuffer_bind(dfbl->default_fb); - DRW_transform_none(txl->renderpass); - } - else { - /* Draw state is not valid for this pass, clear the buffer */ - static float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - GPU_framebuffer_bind(dfbl->default_fb); - GPU_framebuffer_clear_color(dfbl->default_fb, clear_color); - } - GPU_framebuffer_bind(fbl->main_fb); -} - -void EEVEE_renderpasses_draw_debug(EEVEE_Data *vedata) -{ - EEVEE_TextureList *txl = vedata->txl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - - GPUTexture *tx = NULL; - /* Debug : Output buffer to view. */ - switch (G.debug_value) { - case 1: - tx = txl->maxzbuffer; - break; - case 2: - /* UNUSED */ - break; - case 3: - tx = effects->ssr_normal_input; - break; - case 4: - tx = effects->ssr_specrough_input; - break; - case 5: - tx = txl->color_double_buffer; - break; - case 6: - tx = effects->gtao_horizons_renderpass; - break; - case 7: - tx = effects->gtao_horizons_renderpass; - break; - case 8: - tx = effects->sss_irradiance; - break; - case 9: - tx = effects->sss_radius; - break; - case 10: - tx = effects->sss_albedo; - break; - case 11: - tx = effects->velocity_tx; - break; - default: - break; - } - - if (tx) { - DRW_transform_none(tx); - } -} diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.cc b/source/blender/draw/engines/eevee/eevee_renderpasses.cc new file mode 100644 index 00000000000..46cc2f67537 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_renderpasses.cc @@ -0,0 +1,92 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#include "BLI_hash_tables.hh" +#include "BLI_rect.h" +#include "BLI_vector.hh" + +#include "RE_pipeline.h" + +#include "eevee_instance.hh" +#include "eevee_renderpasses.hh" + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name RenderPasses + * \{ */ + +void RenderPasses::init(const int extent[2], const rcti *output_rect) +{ + const Scene *scene = inst_.scene; + + eRenderPassBit enabled_passes; + if (inst_.render_layer) { + enabled_passes = to_render_passes_bits(inst_.render_layer->passflag); + /* Cannot output motion vectors when using motion blur. */ + if (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) { + enabled_passes &= ~RENDERPASS_VECTOR; + } + } + else if (inst_.v3d) { + enabled_passes = to_render_passes_bits(inst_.v3d->shading.render_pass); + /* We need the depth pass for compositing overlays or GPencil. */ + if (!DRW_state_is_scene_render()) { + enabled_passes |= RENDERPASS_DEPTH; + } + } + else { + enabled_passes = RENDERPASS_COMBINED; + } + + const bool use_log_encoding = scene->eevee.flag & SCE_EEVEE_FILM_LOG_ENCODING; + + rcti fallback_rect; + if (BLI_rcti_is_empty(output_rect)) { + BLI_rcti_init(&fallback_rect, 0, extent[0], 0, extent[1]); + output_rect = &fallback_rect; + } + + /* HACK to iterate over all passes. */ + enabled_passes_ = RENDERPASS_ALL; + for (RenderPassItem rpi : *this) { + bool enable = (enabled_passes & rpi.pass_bit) != 0; + if (enable && rpi.film == nullptr) { + rpi.film = new Film(inst_, + to_render_passes_data_type(rpi.pass_bit, use_log_encoding), + to_render_passes_name(rpi.pass_bit)); + } + else if (!enable && rpi.film != nullptr) { + /* Delete unused passes. */ + delete rpi.film; + rpi.film = nullptr; + } + + if (rpi.film) { + rpi.film->init(extent, output_rect); + } + } + + enabled_passes_ = enabled_passes; +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.hh b/source/blender/draw/engines/eevee/eevee_renderpasses.hh new file mode 100644 index 00000000000..aa3591d3ba5 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_renderpasses.hh @@ -0,0 +1,249 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "DRW_render.h" + +#include "BLI_hash_tables.hh" +#include "BLI_vector.hh" + +#include "RE_pipeline.h" + +#include "eevee_film.hh" + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name eRenderPassBit + * + * This enum might seems redundant but there is an opportunity to use it for internal debug passes. + * \{ */ + +enum eRenderPassBit { + RENDERPASS_NONE = 0, + RENDERPASS_COMBINED = (1 << 0), + RENDERPASS_DEPTH = (1 << 1), + RENDERPASS_NORMAL = (1 << 2), + RENDERPASS_VECTOR = (1 << 3), + /** Used for iterator. */ + RENDERPASS_MAX, + RENDERPASS_ALL = ((RENDERPASS_MAX - 1) << 1) - 1, +}; + +ENUM_OPERATORS(eRenderPassBit, RENDERPASS_NORMAL) + +static inline eRenderPassBit to_render_passes_bits(int i_rpasses) +{ + eRenderPassBit rpasses = RENDERPASS_NONE; + SET_FLAG_FROM_TEST(rpasses, i_rpasses & SCE_PASS_COMBINED, RENDERPASS_COMBINED); + SET_FLAG_FROM_TEST(rpasses, i_rpasses & SCE_PASS_Z, RENDERPASS_DEPTH); + SET_FLAG_FROM_TEST(rpasses, i_rpasses & SCE_PASS_NORMAL, RENDERPASS_NORMAL); + SET_FLAG_FROM_TEST(rpasses, i_rpasses & SCE_PASS_VECTOR, RENDERPASS_VECTOR); + return rpasses; +} + +static inline const char *to_render_passes_name(eRenderPassBit rpass) +{ + switch (rpass) { + case RENDERPASS_COMBINED: + return RE_PASSNAME_COMBINED; + case RENDERPASS_DEPTH: + return RE_PASSNAME_Z; + case RENDERPASS_NORMAL: + return RE_PASSNAME_NORMAL; + case RENDERPASS_VECTOR: + return RE_PASSNAME_VECTOR; + default: + BLI_assert(0); + return ""; + } +} + +static inline eFilmDataType to_render_passes_data_type(eRenderPassBit rpass, + const bool use_log_encoding) +{ + switch (rpass) { + case RENDERPASS_COMBINED: + return (use_log_encoding) ? FILM_DATA_COLOR_LOG : FILM_DATA_COLOR; + case RENDERPASS_DEPTH: + return FILM_DATA_DEPTH; + case RENDERPASS_NORMAL: + return FILM_DATA_NORMAL; + case RENDERPASS_VECTOR: + return FILM_DATA_MOTION; + default: + BLI_assert(0); + return FILM_DATA_COLOR; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name RenderPasses + * \{ */ + +/** + * Contains and manages each \c Film output for each render pass output. + */ +class RenderPasses { + public: + /** Film for each render pass. A nullptr means the pass is not needed. */ + Film *combined = nullptr; + Film *depth = nullptr; + Film *normal = nullptr; + Film *vector = nullptr; + Vector<Film *> aovs; + + private: + Instance &inst_; + + eRenderPassBit enabled_passes_ = RENDERPASS_NONE; + + public: + RenderPasses(Instance &inst) : inst_(inst){}; + + ~RenderPasses() + { + delete combined; + delete depth; + delete normal; + delete vector; + } + + void init(const int extent[2], const rcti *output_rect); + + void sync(void) + { + for (RenderPassItem rpi : *this) { + rpi.film->sync(); + } + } + + void end_sync(void) + { + for (RenderPassItem rpi : *this) { + rpi.film->end_sync(); + } + } + + void resolve_viewport(DefaultFramebufferList *dfbl) + { + for (RenderPassItem rpi : *this) { + if (rpi.pass_bit == RENDERPASS_DEPTH) { + rpi.film->resolve_viewport(dfbl->depth_only_fb); + } + else { + /* Ensures only one color render pass is enabled. */ + BLI_assert((enabled_passes_ & ~RENDERPASS_DEPTH) == rpi.pass_bit); + rpi.film->resolve_viewport(dfbl->color_only_fb); + } + } + } + + void read_result(RenderLayer *render_layer, const char *view_name) + { + for (RenderPassItem rpi : *this) { + const char *pass_name = to_render_passes_name(rpi.pass_bit); + RenderPass *rp = RE_pass_find_by_name(render_layer, pass_name, view_name); + if (rp) { + rpi.film->read_result(rp->rect); + } + } + } + + private: + constexpr Film *&render_pass_bit_to_film_p(eRenderPassBit rpass) + { + switch (rpass) { + case RENDERPASS_COMBINED: + return combined; + case RENDERPASS_DEPTH: + return depth; + case RENDERPASS_NORMAL: + return normal; + case RENDERPASS_VECTOR: + return vector; + default: + BLI_assert(0); + return combined; + } + } + + /** + * Iterator + **/ + + struct RenderPassItem { + Film *&film; + eRenderPassBit pass_bit; + + constexpr explicit RenderPassItem(Film *&film_, eRenderPassBit pass_bit_) + : film(film_), pass_bit(pass_bit_){}; + }; + + class Iterator { + private: + RenderPasses &render_passes_; + int64_t current_; + + public: + constexpr explicit Iterator(RenderPasses &rpasses, int64_t current) + : render_passes_(rpasses), current_(current){}; + + constexpr Iterator &operator++() + { + while (current_ < RENDERPASS_MAX) { + current_ <<= 1; + if (current_ & render_passes_.enabled_passes_) { + break; + } + } + return *this; + } + + constexpr friend bool operator!=(const Iterator &a, const Iterator &b) + { + return a.current_ != b.current_; + } + + constexpr RenderPassItem operator*() + { + eRenderPassBit pass_bit = static_cast<eRenderPassBit>(current_); + return RenderPassItem(render_passes_.render_pass_bit_to_film_p(pass_bit), pass_bit); + } + }; + + /* Iterator over all enabled passes. */ + constexpr Iterator begin() + { + return Iterator(*this, 1); + } + + constexpr Iterator end() + { + return Iterator(*this, power_of_2_max_constexpr(RENDERPASS_MAX)); + } +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_sampling.c b/source/blender/draw/engines/eevee/eevee_sampling.c deleted file mode 100644 index 99d14bd2c82..00000000000 --- a/source/blender/draw/engines/eevee/eevee_sampling.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2019, Blender Foundation. - */ - -/** \file - * \ingroup EEVEE - */ - -#include "eevee_private.h" - -#include "BLI_rand.h" - -void EEVEE_sample_ball(int sample_ofs, float radius, float rsample[3]) -{ - double ht_point[3]; - double ht_offset[3] = {0.0, 0.0, 0.0}; - const uint ht_primes[3] = {2, 3, 7}; - - BLI_halton_3d(ht_primes, ht_offset, sample_ofs, ht_point); - - /* De-correlate AA and shadow samples. (see T68594) */ - ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0); - ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0); - ht_point[2] = fmod(ht_point[2] * 1151.0, 1.0); - - float omega = ht_point[1] * 2.0f * M_PI; - - rsample[2] = ht_point[0] * 2.0f - 1.0f; /* cos theta */ - - float r = sqrtf(fmaxf(0.0f, 1.0f - rsample[2] * rsample[2])); /* sin theta */ - - rsample[0] = r * cosf(omega); - rsample[1] = r * sinf(omega); - - radius *= sqrt(sqrt(ht_point[2])); - mul_v3_fl(rsample, radius); -} - -void EEVEE_sample_rectangle(int sample_ofs, - const float x_axis[3], - const float y_axis[3], - float size_x, - float size_y, - float rsample[3]) -{ - double ht_point[2]; - double ht_offset[2] = {0.0, 0.0}; - const uint ht_primes[2] = {2, 3}; - - BLI_halton_2d(ht_primes, ht_offset, sample_ofs, ht_point); - - /* De-correlate AA and shadow samples. (see T68594) */ - ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0); - ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0); - - /* Change distribution center to be 0,0 */ - ht_point[0] = (ht_point[0] > 0.5f) ? ht_point[0] - 1.0f : ht_point[0]; - ht_point[1] = (ht_point[1] > 0.5f) ? ht_point[1] - 1.0f : ht_point[1]; - - zero_v3(rsample); - madd_v3_v3fl(rsample, x_axis, (ht_point[0] * 2.0f) * size_x); - madd_v3_v3fl(rsample, y_axis, (ht_point[1] * 2.0f) * size_y); -} - -void EEVEE_sample_ellipse(int sample_ofs, - const float x_axis[3], - const float y_axis[3], - float size_x, - float size_y, - float rsample[3]) -{ - double ht_point[2]; - double ht_offset[2] = {0.0, 0.0}; - const uint ht_primes[2] = {2, 3}; - - BLI_halton_2d(ht_primes, ht_offset, sample_ofs, ht_point); - - /* Decorelate AA and shadow samples. (see T68594) */ - ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0); - ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0); - - /* Uniform disc sampling. */ - float omega = ht_point[1] * 2.0f * M_PI; - float r = sqrtf(ht_point[0]); - ht_point[0] = r * cosf(omega) * size_x; - ht_point[1] = r * sinf(omega) * size_y; - - zero_v3(rsample); - madd_v3_v3fl(rsample, x_axis, ht_point[0]); - madd_v3_v3fl(rsample, y_axis, ht_point[1]); -} - -void EEVEE_random_rotation_m4(int sample_ofs, float scale, float r_mat[4][4]) -{ - double ht_point[3]; - double ht_offset[3] = {0.0, 0.0, 0.0}; - const uint ht_primes[3] = {2, 3, 5}; - - BLI_halton_3d(ht_primes, ht_offset, sample_ofs, ht_point); - - /* Decorelate AA and shadow samples. (see T68594) */ - ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0); - ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0); - ht_point[2] = fmod(ht_point[2] * 1151.0, 1.0); - - rotate_m4(r_mat, 'X', ht_point[0] * scale); - rotate_m4(r_mat, 'Y', ht_point[1] * scale); - rotate_m4(r_mat, 'Z', ht_point[2] * scale); -} diff --git a/source/blender/draw/engines/eevee/eevee_sampling.hh b/source/blender/draw/engines/eevee/eevee_sampling.hh new file mode 100644 index 00000000000..e5301efb1e8 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_sampling.hh @@ -0,0 +1,358 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Random Number Generator + */ + +#pragma once + +#include "BKE_colortools.h" +#include "BLI_rand.h" +#include "BLI_vector.hh" +#include "DNA_scene_types.h" +#include "DRW_render.h" +#include "GPU_framebuffer.h" +#include "GPU_texture.h" + +#include "eevee_shader_shared.hh" +#include "eevee_wrapper.hh" + +namespace blender::eevee { + +/** + * Random number generator, contains persistent state and sample count logic. + */ +class Sampling { + private: + /* Number of samples in the first ring of jittered depth of field. */ + constexpr static uint64_t dof_web_density_ = 6; + /* High number of sample for viewport infinite rendering. */ + constexpr static uint64_t infinite_sample_count_ = 0xFFFFFFu; + + /** 1 based current sample. */ + uint64_t sample_ = 1; + /** Target sample count. */ + uint64_t sample_count_ = 64; + /** Number of ring in the web pattern of the jittered Depth of Field. */ + uint64_t dof_ring_count_ = 0; + /** Number of samples in the web pattern of the jittered Depth of Field. */ + uint64_t dof_sample_count_ = 1; + /** Motion blur steps. */ + uint64_t motion_blur_steps_ = 1; + /** Used for viewport smooth transition. */ + int64_t sample_viewport_ = 1; + int64_t viewport_smoothing_start = 0; + int64_t viewport_smoothing_duration = 0; + /** Tag to reset sampling for the next sample. */ + bool reset_ = false; + + StructBuffer<SamplingData> data_; + + public: + Sampling(){}; + ~Sampling(){}; + + void init(const Scene *scene) + { + sample_count_ = DRW_state_is_image_render() ? scene->eevee.taa_render_samples : + scene->eevee.taa_samples; + + if (sample_count_ == 0) { + BLI_assert(!DRW_state_is_image_render()); + sample_count_ = infinite_sample_count_; + } + + motion_blur_steps_ = DRW_state_is_image_render() ? scene->eevee.motion_blur_steps : 1; + sample_count_ = divide_ceil_u(sample_count_, motion_blur_steps_); + + if (scene->eevee.flag & SCE_EEVEE_DOF_JITTER) { + if (sample_count_ == infinite_sample_count_) { + /* Special case for viewport continuous rendering. We clamp to a max sample + * to avoid the jittered dof never converging. */ + dof_ring_count_ = 6; + } + else { + dof_ring_count_ = web_ring_count_get(dof_web_density_, sample_count_); + } + dof_sample_count_ = web_sample_count_get(dof_web_density_, dof_ring_count_); + /* Change total sample count to fill the web pattern entirely. */ + sample_count_ = divide_ceil_u(sample_count_, dof_sample_count_) * dof_sample_count_; + } + else { + dof_ring_count_ = 0; + dof_sample_count_ = 1; + } + + /* Only multiply after to have full the full DoF web pattern for each time steps. */ + sample_count_ *= motion_blur_steps_; + + if (!DRW_state_is_image_render()) { + /* TODO(fclem) UI. */ + viewport_smoothing_start = 16; + viewport_smoothing_duration = 32; + /* At minima start when rendering has finished. */ + viewport_smoothing_start = min_ii(viewport_smoothing_start, sample_count_); + /* Basically counts the number of redraw. */ + sample_viewport_ += 1; + } + else { + viewport_smoothing_start = 0; + viewport_smoothing_duration = 0; + } + } + + void end_sync(void) + { + if (reset_) { + sample_ = 1; + sample_viewport_ = 1; + } + } + + void step(void) + { + { + /* TODO(fclem) we could use some persistent states to speedup the computation. */ + double r[2], offset[2] = {0, 0}; + /* Using 2,3 primes as per UE4 Temporal AA presentation. + * advances.realtimerendering.com/s2014/epic/TemporalAA.pptx (slide 14) */ + uint32_t primes[2] = {2, 3}; + BLI_halton_2d(primes, offset, sample_, r); + data_.dimensions[SAMPLING_FILTER_U][0] = r[0]; + data_.dimensions[SAMPLING_FILTER_V][0] = r[1]; + /* TODO decorelate. */ + data_.dimensions[SAMPLING_TIME][0] = r[0]; + data_.dimensions[SAMPLING_CLOSURE][0] = r[1]; + data_.dimensions[SAMPLING_RAYTRACE_X][0] = r[0]; + } + { + double r[2], offset[2] = {0, 0}; + uint32_t primes[2] = {5, 7}; + BLI_halton_2d(primes, offset, sample_, r); + data_.dimensions[SAMPLING_LENS_U][0] = r[0]; + data_.dimensions[SAMPLING_LENS_V][0] = r[1]; + /* TODO decorelate. */ + data_.dimensions[SAMPLING_LIGHTPROBE][0] = r[0]; + data_.dimensions[SAMPLING_TRANSPARENCY][0] = r[1]; + } + { + /* Using leaped halton sequence so we can reused the same primes as lens. */ + double r[3], offset[3] = {0, 0, 0}; + uint64_t leap = 11; + uint32_t primes[3] = {5, 4, 7}; + BLI_halton_3d(primes, offset, (sample_ - 1) * leap, r); + data_.dimensions[SAMPLING_SHADOW_U][0] = r[0]; + data_.dimensions[SAMPLING_SHADOW_V][0] = r[1]; + data_.dimensions[SAMPLING_SHADOW_W][0] = r[2]; + /* TODO decorelate. */ + data_.dimensions[SAMPLING_RAYTRACE_U][0] = r[0]; + data_.dimensions[SAMPLING_RAYTRACE_V][0] = r[1]; + data_.dimensions[SAMPLING_RAYTRACE_W][0] = r[2]; + } + { + /* Using leaped halton sequence so we can reused the same primes. */ + double r[2], offset[2] = {0, 0}; + uint64_t leap = 5; + uint32_t primes[2] = {2, 3}; + BLI_halton_2d(primes, offset, (sample_ - 1) * leap, r); + data_.dimensions[SAMPLING_SHADOW_X][0] = r[0]; + data_.dimensions[SAMPLING_SHADOW_Y][0] = r[1]; + /* TODO decorelate. */ + data_.dimensions[SAMPLING_SSS_U][0] = r[0]; + data_.dimensions[SAMPLING_SSS_V][0] = r[1]; + } + + data_.push_update(); + sample_++; + + reset_ = false; + } + + /** + * Getters + **/ + /* Returns current, 1 based, scene sample index. */ + uint64_t sample_get(void) const + { + return sample_; + } + /* Returns blend factor to apply to film to have a smooth transition instead of flickering + * for the first samples of random shadows. */ + float viewport_smoothing_opacity_factor_get(void) const + { + return (sample_ == 1 || viewport_smoothing_duration == 0) ? + 1.0f : + square_f(clamp_f((sample_viewport_ - viewport_smoothing_start) / + float(viewport_smoothing_duration), + 0.0f, + 1.0f)); + } + /* Returns sample count inside the jittered depth of field web pattern. */ + uint64_t dof_ring_count_get(void) const + { + return dof_ring_count_; + } + /* Returns sample count inside the jittered depth of field web pattern. */ + uint64_t dof_sample_count_get(void) const + { + return dof_sample_count_; + } + const GPUUniformBuf *ubo_get(void) const + { + return data_.ubo_get(); + } + /* Returns a pseudo random number in [0..1] range. Each dimension are uncorrelated. */ + float rng_get(eSamplingDimension dimension) const + { + return data_.dimensions[dimension][0]; + } + /* Returns true if rendering has finished. */ + bool finished(void) const + { + return (sample_ > sample_count_); + } + /* Returns true if viewport smoothing and sampling has finished. */ + bool finished_viewport(void) const + { + return finished() && + (sample_viewport_ > (viewport_smoothing_start + viewport_smoothing_duration)); + } + /* Viewport Only: Function to call to notify something in the scene changed. + * This will reset accumulation. Do not call after end_sync() or during sample rendering. */ + void reset(void) + { + reset_ = true; + } + /* Viewport Only: true if an update happened in the scene and accumulation needs reset. */ + bool is_reset(void) const + { + return reset_; + } + /* Return true if we are starting a new motion blur step. We need to run sync agains since + * depsgraph was updated by MotionBlur::step(). */ + bool do_render_sync(void) const + { + return DRW_state_is_image_render() && + (((sample_ - 1) % (sample_count_ / motion_blur_steps_)) == 0); + } + + void dof_disk_sample_get(float *r_radius, float *r_theta) + { + if (dof_ring_count_ == 0) { + *r_radius = *r_theta = 0.0f; + return; + } + + int s = sample_ - 1; + int ring = 0; + int ring_sample_count = 1; + int ring_sample = 1; + + s = s * (dof_web_density_ - 1); + s = s % dof_sample_count_; + + /* Choosing sample to we get faster convergence. + * The issue here is that we cannot map a low descripency sequence to this sampling pattern + * because the same sample could be choosen twice in relatively short intervals. */ + /* For now just use an ascending sequence with an offset. This gives us relatively quick + * initial coverage and relatively high distance between samples. */ + /* TODO(fclem) We can try to order samples based on a LDS into a table to avoid duplicates. + * The drawback would be some memory consumption and init time. */ + int samples_passed = 1; + while (s >= samples_passed) { + ring++; + ring_sample_count = ring * dof_web_density_; + ring_sample = s - samples_passed; + ring_sample = (ring_sample + 1) % ring_sample_count; + samples_passed += ring_sample_count; + } + + *r_radius = ring / (float)dof_ring_count_; + *r_theta = 2.0f * M_PI * ring_sample / (float)ring_sample_count; + } + + /* Creates a discrete cumulative distribution function table from a given curvemapping. + * Output cdf vector is expected to already be sized according to the wanted resolution. */ + static void cdf_from_curvemapping(const CurveMapping &curve, Vector<float> &cdf) + { + BLI_assert(cdf.size() > 1); + cdf[0] = 0.0f; + /* Actual CDF evaluation. */ + for (int u : cdf.index_range()) { + float x = (float)(u + 1) / (float)(cdf.size() - 1); + cdf[u + 1] = cdf[u] + BKE_curvemapping_evaluateF(&curve, 0, x); + } + /* Normalize the CDF. */ + for (int u : cdf.index_range()) { + cdf[u] /= cdf.last(); + } + /* Just to make sure. */ + cdf.last() = 1.0f; + } + + /* Inverts a cumulative distribution function. + * Output vector is expected to already be sized according to the wanted resolution. */ + static void cdf_invert(Vector<float> &cdf, Vector<float> &inverted_cdf) + { + for (int u : inverted_cdf.index_range()) { + float x = (float)u / (float)(inverted_cdf.size() - 1); + for (int i : cdf.index_range()) { + if (i == cdf.size() - 1) { + inverted_cdf[u] = 1.0f; + } + else if (cdf[i] >= x) { + float t = (x - cdf[i]) / (cdf[i + 1] - cdf[i]); + inverted_cdf[u] = ((float)i + t) / (float)(cdf.size() - 1); + break; + } + } + } + } + + /** + * Special ball distribution: + * Point are distributed in a way that when they are orthogonally + * projected into any plane, the resulting distribution is (close to) + * a uniform disc distribution. + */ + vec3 sample_ball(const float rand[3]) + { + vec3 sample; + sample.z = rand[0] * 2.0f - 1.0f; /* cos theta */ + + float r = sqrtf(fmaxf(0.0f, 1.0f - square_f(sample.z))); /* sin theta */ + + float omega = rand[1] * 2.0f * M_PI; + sample.x = r * cosf(omega); + sample.y = r * sinf(omega); + + sample *= sqrtf(sqrtf(rand[2])); + return sample; + } + + vec2 sample_disk(const float rand[2]) + { + float omega = rand[1] * 2.0f * M_PI; + return sqrtf(rand[0]) * vec2(cosf(omega), sinf(omega)); + } +}; + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c deleted file mode 100644 index 0d6bd1f8024..00000000000 --- a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c +++ /dev/null @@ -1,264 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - * - * Screen space reflections and refractions techniques. - */ - -#include "DRW_render.h" - -#include "BLI_dynstr.h" -#include "BLI_string_utils.h" - -#include "DEG_depsgraph_query.h" - -#include "GPU_texture.h" -#include "eevee_private.h" - -int EEVEE_screen_raytrace_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_EffectsInfo *effects = stl->effects; - const float *viewport_size = DRW_viewport_size_get(); - - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - - if (scene_eval->eevee.flag & SCE_EEVEE_SSR_ENABLED) { - const bool use_refraction = (scene_eval->eevee.flag & SCE_EEVEE_SSR_REFRACTION) != 0; - - const bool is_persp = DRW_view_is_persp_get(NULL); - if (effects->ssr_was_persp != is_persp) { - effects->ssr_was_persp = is_persp; - DRW_viewport_request_redraw(); - EEVEE_temporal_sampling_reset(vedata); - stl->g_data->valid_double_buffer = false; - } - - if (!effects->ssr_was_valid_double_buffer) { - DRW_viewport_request_redraw(); - EEVEE_temporal_sampling_reset(vedata); - } - effects->ssr_was_valid_double_buffer = stl->g_data->valid_double_buffer; - - effects->reflection_trace_full = (scene_eval->eevee.flag & SCE_EEVEE_SSR_HALF_RESOLUTION) == 0; - common_data->ssr_thickness = scene_eval->eevee.ssr_thickness; - common_data->ssr_border_fac = scene_eval->eevee.ssr_border_fade; - common_data->ssr_firefly_fac = scene_eval->eevee.ssr_firefly_fac; - common_data->ssr_max_roughness = scene_eval->eevee.ssr_max_roughness; - common_data->ssr_quality = 1.0f - 0.95f * scene_eval->eevee.ssr_quality; - common_data->ssr_brdf_bias = 0.1f + common_data->ssr_quality * 0.6f; /* Range [0.1, 0.7]. */ - - if (common_data->ssr_firefly_fac < 1e-8f) { - common_data->ssr_firefly_fac = FLT_MAX; - } - - void *owner = (void *)EEVEE_screen_raytrace_init; - const int divisor = (effects->reflection_trace_full) ? 1 : 2; - int tracing_res[2] = {(int)viewport_size[0] / divisor, (int)viewport_size[1] / divisor}; - const int size_fs[2] = {(int)viewport_size[0], (int)viewport_size[1]}; - const bool high_qual_input = true; /* TODO: dither low quality input. */ - const eGPUTextureFormat format = (high_qual_input) ? GPU_RGBA16F : GPU_RGBA8; - - tracing_res[0] = max_ii(1, tracing_res[0]); - tracing_res[1] = max_ii(1, tracing_res[1]); - - common_data->ssr_uv_scale[0] = size_fs[0] / ((float)tracing_res[0] * divisor); - common_data->ssr_uv_scale[1] = size_fs[1] / ((float)tracing_res[1] * divisor); - - /* MRT for the shading pass in order to output needed data for the SSR pass. */ - effects->ssr_specrough_input = DRW_texture_pool_query_2d(UNPACK2(size_fs), format, owner); - - GPU_framebuffer_texture_attach(fbl->main_fb, effects->ssr_specrough_input, 2, 0); - - /* Ray-tracing output. */ - effects->ssr_hit_output = DRW_texture_pool_query_2d(UNPACK2(tracing_res), GPU_RGBA16F, owner); - effects->ssr_hit_depth = DRW_texture_pool_query_2d(UNPACK2(tracing_res), GPU_R16F, owner); - - GPU_framebuffer_ensure_config(&fbl->screen_tracing_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(effects->ssr_hit_output), - GPU_ATTACHMENT_TEXTURE(effects->ssr_hit_depth), - }); - - return EFFECT_SSR | EFFECT_NORMAL_BUFFER | EFFECT_RADIANCE_BUFFER | EFFECT_DOUBLE_BUFFER | - ((use_refraction) ? EFFECT_REFRACT : 0); - } - - /* Cleanup to release memory */ - GPU_FRAMEBUFFER_FREE_SAFE(fbl->screen_tracing_fb); - effects->ssr_specrough_input = NULL; - effects->ssr_hit_output = NULL; - - return 0; -} - -void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_EffectsInfo *effects = stl->effects; - LightCache *lcache = stl->g_data->light_cache; - - if ((effects->enabled_effects & EFFECT_SSR) != 0) { - struct GPUShader *trace_shader = EEVEE_shaders_effect_reflection_trace_sh_get(); - struct GPUShader *resolve_shader = EEVEE_shaders_effect_reflection_resolve_sh_get(); - - int hitbuf_size[3]; - GPU_texture_get_mipmap_size(effects->ssr_hit_output, 0, hitbuf_size); - - /** Screen space ray-tracing overview - * - * Following Frostbite stochastic SSR. - * - * - First pass Trace rays across the depth buffer. The hit position and PDF are - * recorded in a RGBA16F render target for each ray (sample). - * - * - We down-sample the previous frame color buffer. - * - * - For each final pixel, we gather neighbors rays and choose a color buffer - * mipmap for each ray using its PDF. (filtered importance sampling) - * We then evaluate the lighting from the probes and mix the results together. - */ - DRW_PASS_CREATE(psl->ssr_raytrace, DRW_STATE_WRITE_COLOR); - DRWShadingGroup *grp = DRW_shgroup_create(trace_shader, psl->ssr_raytrace); - DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input); - DRW_shgroup_uniform_texture_ref(grp, "specroughBuffer", &effects->ssr_specrough_input); - DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer); - DRW_shgroup_uniform_texture_ref(grp, "planarDepth", &vedata->txl->planar_depth); - DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); - DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - DRW_shgroup_uniform_vec2_copy(grp, "targetSize", (float[2]){hitbuf_size[0], hitbuf_size[1]}); - DRW_shgroup_uniform_float_copy( - grp, "randomScale", effects->reflection_trace_full ? 0.0f : 0.5f); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT; - - DRW_PASS_CREATE(psl->ssr_resolve, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD); - grp = DRW_shgroup_create(resolve_shader, psl->ssr_resolve); - DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input); - DRW_shgroup_uniform_texture_ref(grp, "specroughBuffer", &effects->ssr_specrough_input); - DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &lcache->cube_tx.tex); - DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &vedata->txl->planar_pool); - DRW_shgroup_uniform_texture_ref(grp, "planarDepth", &vedata->txl->planar_depth); - DRW_shgroup_uniform_texture_ref_ex(grp, "hitBuffer", &effects->ssr_hit_output, no_filter); - DRW_shgroup_uniform_texture_ref_ex(grp, "hitDepth", &effects->ssr_hit_depth, no_filter); - DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &txl->filtered_radiance); - DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer); - DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool); - DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool); - DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); - DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); - DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); - DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - DRW_shgroup_uniform_int(grp, "samplePoolOffset", &effects->taa_current_sample, 1); - DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - } -} - -void EEVEE_refraction_compute(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - - if ((effects->enabled_effects & EFFECT_REFRACT) != 0) { - EEVEE_effects_downsample_radiance_buffer(vedata, txl->color); - - /* Restore */ - GPU_framebuffer_bind(fbl->main_fb); - } -} - -void EEVEE_reflection_compute(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_EffectsInfo *effects = stl->effects; - - if (((effects->enabled_effects & EFFECT_SSR) != 0) && stl->g_data->valid_double_buffer) { - DRW_stats_group_start("SSR"); - - /* Raytrace. */ - GPU_framebuffer_bind(fbl->screen_tracing_fb); - DRW_draw_pass(psl->ssr_raytrace); - - EEVEE_effects_downsample_radiance_buffer(vedata, txl->color_double_buffer); - - GPU_framebuffer_bind(fbl->main_color_fb); - DRW_draw_pass(psl->ssr_resolve); - - /* Restore */ - GPU_framebuffer_bind(fbl->main_fb); - DRW_stats_group_end(); - } -} - -void EEVEE_reflection_output_init(EEVEE_ViewLayerData *UNUSED(sldata), - EEVEE_Data *vedata, - uint tot_samples) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_TextureList *txl = vedata->txl; - - /* Create FrameBuffer. */ - const eGPUTextureFormat texture_format = (tot_samples > 256) ? GPU_RGBA32F : GPU_RGBA16F; - DRW_texture_ensure_fullscreen_2d(&txl->ssr_accum, texture_format, 0); - - GPU_framebuffer_ensure_config(&fbl->ssr_accum_fb, - {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ssr_accum)}); -} - -void EEVEE_reflection_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = vedata->stl->effects; - - if (stl->g_data->valid_double_buffer) { - GPU_framebuffer_bind(fbl->ssr_accum_fb); - - /* Clear texture. */ - if (effects->taa_current_sample == 1) { - const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPU_framebuffer_clear_color(fbl->ssr_accum_fb, clear); - } - - DRW_draw_pass(psl->ssr_resolve); - } -} diff --git a/source/blender/draw/engines/eevee/eevee_shader.cc b/source/blender/draw/engines/eevee/eevee_shader.cc new file mode 100644 index 00000000000..e04e3d0bd21 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_shader.cc @@ -0,0 +1,930 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Shader module that manage shader libraries, deferred compilation, + * and static shader usage. + */ + +#include "eevee_shader.hh" +#include "eevee_material.hh" + +extern "C" { +extern char datatoc_common_attribute_lib_glsl[]; +extern char datatoc_common_debug_lib_glsl[]; +extern char datatoc_common_fullscreen_vert_glsl[]; +extern char datatoc_common_gpencil_lib_glsl[]; +extern char datatoc_common_hair_lib_glsl[]; +extern char datatoc_common_intersection_lib_glsl[]; +extern char datatoc_common_math_geom_lib_glsl[]; +extern char datatoc_common_math_lib_glsl[]; +extern char datatoc_common_obinfos_lib_glsl[]; +extern char datatoc_common_uniform_attribute_lib_glsl[]; +extern char datatoc_common_view_lib_glsl[]; + +extern char datatoc_eevee_bsdf_lib_glsl[]; +extern char datatoc_eevee_bsdf_microfacet_lib_glsl[]; +extern char datatoc_eevee_bsdf_sampling_lib_glsl[]; +extern char datatoc_eevee_bsdf_stubs_lib_glsl[]; +extern char datatoc_eevee_camera_lib_glsl[]; +extern char datatoc_eevee_camera_velocity_frag_glsl[]; +extern char datatoc_eevee_closure_lib_glsl[]; +extern char datatoc_eevee_cubemap_lib_glsl[]; +extern char datatoc_eevee_culling_debug_frag_glsl[]; +extern char datatoc_eevee_culling_iter_lib_glsl[]; +extern char datatoc_eevee_culling_lib_glsl[]; +extern char datatoc_eevee_culling_select_comp_glsl[]; +extern char datatoc_eevee_culling_sort_comp_glsl[]; +extern char datatoc_eevee_culling_tile_comp_glsl[]; +extern char datatoc_eevee_deferred_direct_frag_glsl[]; +extern char datatoc_eevee_deferred_holdout_frag_glsl[]; +extern char datatoc_eevee_deferred_transparent_frag_glsl[]; +extern char datatoc_eevee_deferred_volume_frag_glsl[]; +extern char datatoc_eevee_depth_clear_frag_glsl[]; +extern char datatoc_eevee_depth_of_field_accumulator_lib_glsl[]; +extern char datatoc_eevee_depth_of_field_bokeh_lut_frag_glsl[]; +extern char datatoc_eevee_depth_of_field_downsample_frag_glsl[]; +extern char datatoc_eevee_depth_of_field_filter_frag_glsl[]; +extern char datatoc_eevee_depth_of_field_gather_frag_glsl[]; +extern char datatoc_eevee_depth_of_field_gather_holefill_frag_glsl[]; +extern char datatoc_eevee_depth_of_field_lib_glsl[]; +extern char datatoc_eevee_depth_of_field_reduce_copy_frag_glsl[]; +extern char datatoc_eevee_depth_of_field_reduce_downsample_frag_glsl[]; +extern char datatoc_eevee_depth_of_field_reduce_recursive_frag_glsl[]; +extern char datatoc_eevee_depth_of_field_resolve_frag_glsl[]; +extern char datatoc_eevee_depth_of_field_scatter_frag_glsl[]; +extern char datatoc_eevee_depth_of_field_scatter_lib_glsl[]; +extern char datatoc_eevee_depth_of_field_scatter_vert_glsl[]; +extern char datatoc_eevee_depth_of_field_setup_frag_glsl[]; +extern char datatoc_eevee_depth_of_field_tiles_dilate_frag_glsl[]; +extern char datatoc_eevee_depth_of_field_tiles_flatten_frag_glsl[]; +extern char datatoc_eevee_film_filter_frag_glsl[]; +extern char datatoc_eevee_film_lib_glsl[]; +extern char datatoc_eevee_film_resolve_depth_frag_glsl[]; +extern char datatoc_eevee_film_resolve_frag_glsl[]; +extern char datatoc_eevee_gbuffer_lib_glsl[]; +extern char datatoc_eevee_hiz_copy_frag_glsl[]; +extern char datatoc_eevee_hiz_downsample_frag_glsl[]; +extern char datatoc_eevee_irradiance_lib_glsl[]; +extern char datatoc_eevee_light_eval_lib_glsl[]; +extern char datatoc_eevee_light_lib_glsl[]; +extern char datatoc_eevee_lightprobe_display_cubemap_frag_glsl[]; +extern char datatoc_eevee_lightprobe_display_cubemap_vert_glsl[]; +extern char datatoc_eevee_lightprobe_display_grid_frag_glsl[]; +extern char datatoc_eevee_lightprobe_display_grid_vert_glsl[]; +extern char datatoc_eevee_lightprobe_display_lib_glsl[]; +extern char datatoc_eevee_lightprobe_eval_cubemap_lib_glsl[]; +extern char datatoc_eevee_lightprobe_eval_grid_lib_glsl[]; +extern char datatoc_eevee_lightprobe_filter_diffuse_frag_glsl[]; +extern char datatoc_eevee_lightprobe_filter_downsample_frag_glsl[]; +extern char datatoc_eevee_lightprobe_filter_geom_glsl[]; +extern char datatoc_eevee_lightprobe_filter_glossy_frag_glsl[]; +extern char datatoc_eevee_lightprobe_filter_lib_glsl[]; +extern char datatoc_eevee_lightprobe_filter_vert_glsl[]; +extern char datatoc_eevee_lightprobe_filter_visibility_frag_glsl[]; +extern char datatoc_eevee_lookdev_background_frag_glsl[]; +extern char datatoc_eevee_ltc_lib_glsl[]; +extern char datatoc_eevee_motion_blur_gather_frag_glsl[]; +extern char datatoc_eevee_motion_blur_lib_glsl[]; +extern char datatoc_eevee_motion_blur_tiles_dilate_frag_glsl[]; +extern char datatoc_eevee_motion_blur_tiles_flatten_frag_glsl[]; +extern char datatoc_eevee_nodetree_eval_lib_glsl[]; +extern char datatoc_eevee_raytrace_denoise_comp_glsl[]; +extern char datatoc_eevee_raytrace_raygen_frag_glsl[]; +extern char datatoc_eevee_raytrace_raygen_lib_glsl[]; +extern char datatoc_eevee_raytrace_resolve_frag_glsl[]; +extern char datatoc_eevee_raytrace_resolve_lib_glsl[]; +extern char datatoc_eevee_raytrace_trace_lib_glsl[]; +extern char datatoc_eevee_sampling_lib_glsl[]; +extern char datatoc_eevee_shadow_debug_frag_glsl[]; +extern char datatoc_eevee_shadow_lib_glsl[]; +extern char datatoc_eevee_shadow_page_alloc_comp_glsl[]; +extern char datatoc_eevee_shadow_page_copy_comp_glsl[]; +extern char datatoc_eevee_shadow_page_debug_comp_glsl[]; +extern char datatoc_eevee_shadow_page_defrag_comp_glsl[]; +extern char datatoc_eevee_shadow_page_free_comp_glsl[]; +extern char datatoc_eevee_shadow_page_init_comp_glsl[]; +extern char datatoc_eevee_shadow_page_lib_glsl[]; +extern char datatoc_eevee_shadow_page_mark_vert_glsl[]; +extern char datatoc_eevee_shadow_tilemap_depth_scan_comp_glsl[]; +extern char datatoc_eevee_shadow_tilemap_lod_mask_comp_glsl[]; +extern char datatoc_eevee_shadow_tilemap_lib_glsl[]; +extern char datatoc_eevee_shadow_tilemap_setup_comp_glsl[]; +extern char datatoc_eevee_shadow_tilemap_tag_comp_glsl[]; +extern char datatoc_eevee_shadow_tilemap_visibility_comp_glsl[]; +extern char datatoc_eevee_subsurface_eval_frag_glsl[]; +extern char datatoc_eevee_surface_background_frag_glsl[]; +extern char datatoc_eevee_surface_deferred_frag_glsl[]; +extern char datatoc_eevee_surface_depth_frag_glsl[]; +extern char datatoc_eevee_surface_depth_simple_frag_glsl[]; +extern char datatoc_eevee_surface_forward_frag_glsl[]; +extern char datatoc_eevee_surface_gpencil_vert_glsl[]; +extern char datatoc_eevee_surface_hair_vert_glsl[]; +extern char datatoc_eevee_surface_lib_glsl[]; +extern char datatoc_eevee_surface_lookdev_vert_glsl[]; +extern char datatoc_eevee_surface_mesh_geom_glsl[]; +extern char datatoc_eevee_surface_mesh_vert_glsl[]; +extern char datatoc_eevee_surface_velocity_frag_glsl[]; +extern char datatoc_eevee_surface_velocity_lib_glsl[]; +extern char datatoc_eevee_surface_velocity_mesh_vert_glsl[]; +extern char datatoc_eevee_surface_world_vert_glsl[]; +extern char datatoc_eevee_velocity_lib_glsl[]; +extern char datatoc_eevee_volume_deferred_frag_glsl[]; +extern char datatoc_eevee_volume_eval_lib_glsl[]; +extern char datatoc_eevee_volume_lib_glsl[]; +extern char datatoc_eevee_volume_vert_glsl[]; + +extern char datatoc_eevee_shader_shared_hh[]; + +extern char datatoc_gpu_shader_codegen_lib_glsl[]; +} + +namespace blender::eevee { + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Static shaders + * + * \{ */ + +ShaderModule::ShaderModule() +{ + for (GPUShader *&shader : shaders_) { + shader = nullptr; + } + + shared_lib_ = enum_preprocess(datatoc_eevee_shader_shared_hh); + + shader_lib_ = DRW_shader_library_create(); + /* NOTE: These need to be ordered by dependencies. */ + DRW_SHADER_LIB_ADD(shader_lib_, common_debug_lib); + DRW_SHADER_LIB_ADD(shader_lib_, common_math_lib); + DRW_shader_library_add_file(shader_lib_, shared_lib_.c_str(), "eevee_shader_shared.hh"); + DRW_SHADER_LIB_ADD(shader_lib_, common_math_geom_lib); + DRW_SHADER_LIB_ADD(shader_lib_, common_hair_lib); + DRW_SHADER_LIB_ADD(shader_lib_, common_view_lib); + DRW_SHADER_LIB_ADD(shader_lib_, common_intersection_lib); + DRW_SHADER_LIB_ADD(shader_lib_, common_attribute_lib); + DRW_SHADER_LIB_ADD(shader_lib_, common_obinfos_lib); + DRW_SHADER_LIB_ADD(shader_lib_, common_gpencil_lib); + DRW_SHADER_LIB_ADD(shader_lib_, gpu_shader_codegen_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_bsdf_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_bsdf_microfacet_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_bsdf_sampling_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_bsdf_stubs_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_irradiance_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_closure_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_cubemap_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_gbuffer_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_nodetree_eval_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_sampling_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_ltc_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_shadow_page_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_shadow_tilemap_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_shadow_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_camera_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_culling_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_culling_iter_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_light_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_light_eval_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_lightprobe_filter_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_lightprobe_display_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_lightprobe_eval_cubemap_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_lightprobe_eval_grid_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_volume_eval_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_volume_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_velocity_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_depth_of_field_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_depth_of_field_accumulator_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_depth_of_field_scatter_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_film_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_motion_blur_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_raytrace_trace_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_raytrace_raygen_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_raytrace_resolve_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_surface_lib); + DRW_SHADER_LIB_ADD(shader_lib_, eevee_surface_velocity_lib); + + /* Meh ¯\_(ツ)_/¯. */ + char *datatoc_nullptr_glsl = nullptr; + +#define SHADER(enum_, vert_, geom_, frag_, defs_) \ + shader_descriptions_[enum_].name = STRINGIFY(enum_); \ + shader_descriptions_[enum_].vertex_shader_code = datatoc_##vert_##_glsl; \ + shader_descriptions_[enum_].geometry_shader_code = datatoc_##geom_##_glsl; \ + shader_descriptions_[enum_].fragment_shader_code = datatoc_##frag_##_glsl; \ + shader_descriptions_[enum_].defines_shader_code = defs_; +#define SHADER_COMPUTE(enum_, comp_, defs_) \ + shader_descriptions_[enum_].name = STRINGIFY(enum_); \ + shader_descriptions_[enum_].compute_shader_code = datatoc_##comp_##_glsl; \ + shader_descriptions_[enum_].defines_shader_code = defs_; + +#define SHADER_FULLSCREEN_DEFINES(enum_, frag_, defs_) \ + SHADER(enum_, common_fullscreen_vert, nullptr, frag_, defs_) +#define SHADER_FULLSCREEN(enum_, frag_) SHADER_FULLSCREEN_DEFINES(enum_, frag_, nullptr) + + SHADER_FULLSCREEN(CULLING_DEBUG, eevee_culling_debug_frag); + SHADER_COMPUTE(CULLING_SELECT, eevee_culling_select_comp, nullptr); + SHADER_COMPUTE(CULLING_SORT, eevee_culling_sort_comp, nullptr); + SHADER_COMPUTE(CULLING_TILE, eevee_culling_tile_comp, nullptr); + SHADER_FULLSCREEN(FILM_FILTER, eevee_film_filter_frag); + SHADER_FULLSCREEN(FILM_RESOLVE, eevee_film_resolve_frag); + SHADER_FULLSCREEN(FILM_RESOLVE_DEPTH, eevee_film_resolve_depth_frag); + SHADER_FULLSCREEN(DEFERRED_EVAL_DIRECT, eevee_deferred_direct_frag); + SHADER_FULLSCREEN(DEFERRED_EVAL_HOLDOUT, eevee_deferred_holdout_frag); + SHADER_FULLSCREEN(DEFERRED_EVAL_TRANSPARENT, eevee_deferred_transparent_frag); + SHADER_FULLSCREEN(DEFERRED_EVAL_VOLUME, eevee_deferred_volume_frag); + SHADER(DEFERRED_MESH, eevee_surface_mesh_vert, nullptr, eevee_surface_deferred_frag, nullptr); + SHADER(DEFERRED_VOLUME, eevee_volume_vert, nullptr, eevee_volume_deferred_frag, nullptr); + SHADER_FULLSCREEN(HIZ_COPY, eevee_hiz_copy_frag); + SHADER_FULLSCREEN(HIZ_DOWNSAMPLE, eevee_hiz_downsample_frag); + SHADER_FULLSCREEN(DOF_BOKEH_LUT, eevee_depth_of_field_bokeh_lut_frag); + SHADER_FULLSCREEN(DOF_FILTER, eevee_depth_of_field_filter_frag); + SHADER_FULLSCREEN_DEFINES(DOF_GATHER_BACKGROUND_LUT, + eevee_depth_of_field_gather_frag, + "#define DOF_FOREGROUND_PASS false\n" + "#define DOF_BOKEH_TEXTURE true\n"); + SHADER_FULLSCREEN_DEFINES(DOF_GATHER_BACKGROUND, + eevee_depth_of_field_gather_frag, + "#define DOF_FOREGROUND_PASS false\n" + "#define DOF_BOKEH_TEXTURE false\n"); + SHADER_FULLSCREEN_DEFINES(DOF_GATHER_FOREGROUND_LUT, + eevee_depth_of_field_gather_frag, + "#define DOF_FOREGROUND_PASS true\n" + "#define DOF_BOKEH_TEXTURE true\n"); + SHADER_FULLSCREEN_DEFINES(DOF_GATHER_FOREGROUND, + eevee_depth_of_field_gather_frag, + "#define DOF_FOREGROUND_PASS true\n" + "#define DOF_BOKEH_TEXTURE false\n"); + SHADER_FULLSCREEN_DEFINES(DOF_GATHER_HOLEFILL, + eevee_depth_of_field_gather_holefill_frag, + "#define DOF_HOLEFILL_PASS true\n" + "#define DOF_FOREGROUND_PASS false\n" + "#define DOF_BOKEH_TEXTURE false\n"); + SHADER_FULLSCREEN(DOF_REDUCE_COPY, eevee_depth_of_field_reduce_copy_frag); + SHADER_FULLSCREEN(DOF_REDUCE_DOWNSAMPLE, eevee_depth_of_field_reduce_downsample_frag); + SHADER_FULLSCREEN(DOF_REDUCE_RECURSIVE, eevee_depth_of_field_reduce_recursive_frag); + SHADER_FULLSCREEN_DEFINES(DOF_RESOLVE_LUT, + eevee_depth_of_field_resolve_frag, + "#define DOF_RESOLVE_PASS true\n" + "#define DOF_BOKEH_TEXTURE true\n" + "#define DOF_SLIGHT_FOCUS_DENSITY 2\n"); + SHADER_FULLSCREEN_DEFINES(DOF_RESOLVE_LUT_HQ, + eevee_depth_of_field_resolve_frag, + "#define DOF_RESOLVE_PASS true\n" + "#define DOF_BOKEH_TEXTURE true\n" + "#define DOF_SLIGHT_FOCUS_DENSITY 4\n"); + SHADER_FULLSCREEN_DEFINES(DOF_RESOLVE, + eevee_depth_of_field_resolve_frag, + "#define DOF_RESOLVE_PASS true\n" + "#define DOF_BOKEH_TEXTURE false\n" + "#define DOF_SLIGHT_FOCUS_DENSITY 2\n"); + SHADER_FULLSCREEN_DEFINES(DOF_RESOLVE_HQ, + eevee_depth_of_field_resolve_frag, + "#define DOF_RESOLVE_PASS true\n" + "#define DOF_BOKEH_TEXTURE false\n" + "#define DOF_SLIGHT_FOCUS_DENSITY 4\n"); + SHADER(DOF_SCATTER_BACKGROUND_LUT, + eevee_depth_of_field_scatter_vert, + nullptr, + eevee_depth_of_field_scatter_frag, + "#define DOF_FOREGROUND_PASS false\n" + "#define DOF_BOKEH_TEXTURE true\n"); + SHADER(DOF_SCATTER_BACKGROUND, + eevee_depth_of_field_scatter_vert, + nullptr, + eevee_depth_of_field_scatter_frag, + "#define DOF_FOREGROUND_PASS false\n" + "#define DOF_BOKEH_TEXTURE false\n"); + SHADER(DOF_SCATTER_FOREGROUND_LUT, + eevee_depth_of_field_scatter_vert, + nullptr, + eevee_depth_of_field_scatter_frag, + "#define DOF_FOREGROUND_PASS true\n" + "#define DOF_BOKEH_TEXTURE true\n"); + SHADER(DOF_SCATTER_FOREGROUND, + eevee_depth_of_field_scatter_vert, + nullptr, + eevee_depth_of_field_scatter_frag, + "#define DOF_FOREGROUND_PASS true\n" + "#define DOF_BOKEH_TEXTURE false\n"); + SHADER_FULLSCREEN(DOF_SETUP, eevee_depth_of_field_setup_frag); + SHADER_FULLSCREEN_DEFINES(DOF_TILES_DILATE_MINABS, + eevee_depth_of_field_tiles_dilate_frag, + "#define DILATE_MODE_MIN_MAX false\n"); + SHADER_FULLSCREEN_DEFINES(DOF_TILES_DILATE_MINMAX, + eevee_depth_of_field_tiles_dilate_frag, + "#define DILATE_MODE_MIN_MAX true\n"); + SHADER_FULLSCREEN(DOF_TILES_FLATTEN, eevee_depth_of_field_tiles_flatten_frag); + SHADER(LIGHTPROBE_DISPLAY_CUBEMAP, + eevee_lightprobe_display_cubemap_vert, + nullptr, + eevee_lightprobe_display_cubemap_frag, + nullptr); + SHADER(LIGHTPROBE_DISPLAY_IRRADIANCE, + eevee_lightprobe_display_grid_vert, + nullptr, + eevee_lightprobe_display_grid_frag, + nullptr); + SHADER(LIGHTPROBE_FILTER_DOWNSAMPLE_CUBE, + eevee_lightprobe_filter_vert, + eevee_lightprobe_filter_geom, + eevee_lightprobe_filter_downsample_frag, + "#define CUBEMAP\n"); + SHADER(LIGHTPROBE_FILTER_GLOSSY, + eevee_lightprobe_filter_vert, + eevee_lightprobe_filter_geom, + eevee_lightprobe_filter_glossy_frag, + "#define CUBEMAP\n"); + SHADER(LIGHTPROBE_FILTER_DIFFUSE, + eevee_lightprobe_filter_vert, + eevee_lightprobe_filter_geom, + eevee_lightprobe_filter_diffuse_frag, + nullptr); + SHADER(LIGHTPROBE_FILTER_VISIBILITY, + eevee_lightprobe_filter_vert, + eevee_lightprobe_filter_geom, + eevee_lightprobe_filter_visibility_frag, + nullptr); + + SHADER_FULLSCREEN(LOOKDEV_BACKGROUND, eevee_lookdev_background_frag); + SHADER_FULLSCREEN(MOTION_BLUR_GATHER, eevee_motion_blur_gather_frag); + SHADER_FULLSCREEN(MOTION_BLUR_TILE_DILATE, eevee_motion_blur_tiles_dilate_frag); + SHADER_FULLSCREEN(MOTION_BLUR_TILE_FLATTEN, eevee_motion_blur_tiles_flatten_frag); + + SHADER_FULLSCREEN_DEFINES(RAYTRACE_DIFFUSE, eevee_raytrace_raygen_frag, "#define DIFFUSE\n"); + SHADER_FULLSCREEN_DEFINES(RAYTRACE_DIFFUSE_FALLBACK, + eevee_raytrace_raygen_frag, + "#define DIFFUSE\n" + "#define SKIP_TRACE\n"); + SHADER_FULLSCREEN_DEFINES( + RAYTRACE_REFLECTION, eevee_raytrace_raygen_frag, "#define REFLECTION\n"); + SHADER_FULLSCREEN_DEFINES(RAYTRACE_REFLECTION_FALLBACK, + eevee_raytrace_raygen_frag, + "#define REFLECTION\n" + "#define SKIP_TRACE\n"); + SHADER_FULLSCREEN_DEFINES( + RAYTRACE_REFRACTION, eevee_raytrace_raygen_frag, "#define REFRACTION\n"); + SHADER_FULLSCREEN_DEFINES(RAYTRACE_REFRACTION_FALLBACK, + eevee_raytrace_raygen_frag, + "#define REFRACTION\n" + "#define SKIP_TRACE\n"); + SHADER_COMPUTE(RAYTRACE_DENOISE_DIFFUSE, eevee_raytrace_denoise_comp, "#define DIFFUSE\n"); + SHADER_COMPUTE(RAYTRACE_DENOISE_REFLECTION, eevee_raytrace_denoise_comp, "#define REFLECTION\n"); + SHADER_COMPUTE(RAYTRACE_DENOISE_REFRACTION, eevee_raytrace_denoise_comp, "#define REFRACTION\n"); + SHADER_FULLSCREEN_DEFINES( + RAYTRACE_RESOLVE_DIFFUSE, eevee_raytrace_resolve_frag, "#define DIFFUSE\n"); + SHADER_FULLSCREEN_DEFINES( + RAYTRACE_RESOLVE_REFLECTION, eevee_raytrace_resolve_frag, "#define REFLECTION\n"); + SHADER_FULLSCREEN_DEFINES( + RAYTRACE_RESOLVE_REFRACTION, eevee_raytrace_resolve_frag, "#define REFRACTION\n"); + + SHADER_FULLSCREEN(SHADOW_DEBUG, eevee_shadow_debug_frag); + SHADER_COMPUTE(SHADOW_PAGE_ALLOC, eevee_shadow_page_alloc_comp, nullptr); + SHADER_COMPUTE(SHADOW_PAGE_COPY, eevee_shadow_page_copy_comp, nullptr); + SHADER_COMPUTE(SHADOW_PAGE_DEBUG, eevee_shadow_page_debug_comp, nullptr); + SHADER_COMPUTE(SHADOW_PAGE_DEFRAG, eevee_shadow_page_defrag_comp, nullptr); + SHADER_COMPUTE(SHADOW_PAGE_FREE, eevee_shadow_page_free_comp, nullptr); + SHADER_COMPUTE(SHADOW_PAGE_INIT, eevee_shadow_page_init_comp, nullptr); + SHADER(SHADOW_PAGE_MARK, eevee_shadow_page_mark_vert, nullptr, eevee_depth_clear_frag, nullptr); + SHADER_COMPUTE(SHADOW_TILE_DEPTH_SCAN, eevee_shadow_tilemap_depth_scan_comp, nullptr); + SHADER_COMPUTE(SHADOW_TILE_LOD_MASK, eevee_shadow_tilemap_lod_mask_comp, nullptr); + SHADER_COMPUTE(SHADOW_TILE_SETUP, eevee_shadow_tilemap_setup_comp, nullptr); + SHADER_COMPUTE(SHADOW_TILE_TAG_UPDATE, eevee_shadow_tilemap_tag_comp, "#define TAG_UPDATE\n"); + SHADER_COMPUTE(SHADOW_TILE_TAG_USAGE, eevee_shadow_tilemap_tag_comp, "#define TAG_USAGE\n"); + SHADER_COMPUTE(SHADOW_TILE_TAG_VISIBILITY, eevee_shadow_tilemap_visibility_comp, nullptr); + + SHADER_FULLSCREEN(SUBSURFACE_EVAL, eevee_subsurface_eval_frag); + + SHADER(VELOCITY_MESH, + eevee_surface_velocity_mesh_vert, + nullptr, + eevee_surface_velocity_frag, + nullptr); + SHADER_FULLSCREEN(VELOCITY_CAMERA, eevee_camera_velocity_frag); + +#undef SHADER +#undef SHADER_FULLSCREEN +#undef SHADER_FULLSCREEN_DEFINES +#undef SHADER_COMPUTE + +#ifdef DEBUG + /* Ensure all shader are described. */ + for (ShaderDescription &desc : shader_descriptions_) { + BLI_assert_msg(desc.name != nullptr, "EEVEE: Mising shader definition."); + if (desc.compute_shader_code == nullptr) { + BLI_assert(desc.vertex_shader_code != nullptr); + BLI_assert(desc.fragment_shader_code != nullptr); + } + } +#endif +} + +ShaderModule::~ShaderModule() +{ + for (GPUShader *&shader : shaders_) { + DRW_SHADER_FREE_SAFE(shader); + } + DRW_SHADER_LIB_FREE_SAFE(shader_lib_); +} + +GPUShader *ShaderModule::static_shader_get(eShaderType shader_type) +{ + if (shaders_[shader_type] == nullptr) { + ShaderDescription &desc = shader_descriptions_[shader_type]; + if (desc.compute_shader_code != nullptr) { + char *comp_with_lib = DRW_shader_library_create_shader_string(shader_lib_, + desc.compute_shader_code); + + shaders_[shader_type] = GPU_shader_create_compute( + comp_with_lib, nullptr, desc.defines_shader_code, desc.name); + + MEM_SAFE_FREE(comp_with_lib); + } + else { + shaders_[shader_type] = DRW_shader_create_with_shaderlib_ex(desc.vertex_shader_code, + desc.geometry_shader_code, + desc.fragment_shader_code, + shader_lib_, + desc.defines_shader_code, + desc.name); + } + if (shaders_[shader_type] == nullptr) { + fprintf(stderr, "EEVEE: error: Could not compile static shader \"%s\"\n", desc.name); + } + BLI_assert(shaders_[shader_type] != nullptr); + } + return shaders_[shader_type]; +} + +/* Run some custom preprocessor shader rewrite and returns a new string. */ +std::string ShaderModule::enum_preprocess(const char *input) +{ + std::string output = ""; + /* Not failure safe but this only runs on static data. */ + const char *cursor = input; + while ((cursor = strstr(cursor, "enum "))) { + output += StringRef(input, cursor - input); + + /* Skip "enum" keyword. */ + cursor = strstr(cursor, " "); + + const char *enum_name = cursor; + cursor = strstr(cursor, " :"); + + output += "#define " + StringRef(enum_name, cursor - enum_name) + " uint\n"; + output += "const uint "; + + const char *enum_values = strstr(cursor, "{") + 1; + cursor = strstr(cursor, "}"); + output += StringRef(enum_values, cursor - enum_values); + + if (cursor != nullptr) { + /* Skip the curly bracket but not the semicolon. */ + input = cursor + 1; + } + else { + input = nullptr; + } + } + if (input != nullptr) { + output += input; + } + return output; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name GPU Materials + * + * \{ */ + +char *ShaderModule::material_shader_code_defs_get(eMaterialGeometry geometry_type) +{ + std::string output = ""; + + switch (geometry_type) { + case MAT_GEOM_HAIR: + output += "#define MAT_GEOM_HAIR\n"; + break; + case MAT_GEOM_GPENCIL: + output += "#define MAT_GEOM_GPENCIL\n"; + output += "#define UNIFORM_RESOURCE_ID\n"; + break; + default: + break; + } + + return BLI_strdup(output.c_str()); +} + +char *ShaderModule::material_shader_code_vert_get(const GPUCodegenOutput *codegen, + GPUMaterial *mat, + eMaterialGeometry geometry_type) +{ + std::string output = "\n\n"; + + /* Might be needed by attr_load_orco. */ + if (GPU_material_flag_get(mat, GPU_MATFLAG_OBJECT_INFO)) { + output += "#pragma BLENDER_REQUIRE(common_obinfos_lib.glsl)\n"; + } + + if (codegen->attribs_interface) { + /* Declare inputs. */ + std::string delimiter = ";\n"; + std::string sub(codegen->attribs_declare); + size_t start, pos = 0; + while ((pos = sub.find(delimiter)) != std::string::npos) { + switch (geometry_type) { + case MAT_GEOM_MESH: + /* Example print: + * in vec2 u015684; */ + output += "in "; + output += sub.substr(0, pos + delimiter.length()); + break; + case MAT_GEOM_HAIR: + /* Example print: + * uniform samplerBuffer u015684; */ + output += "uniform samplerBuffer "; + start = sub.find(" ") + 1; + output += sub.substr(start, pos + delimiter.length() - start); + break; + case MAT_GEOM_GPENCIL: + /* Example print: + * vec2 u015684; + * These are not used and just here to make the attribs_load functions call valids. + * Only one uv and one color attribute layer is supported by gpencil objects. */ + output += sub.substr(0, pos + delimiter.length()); + break; + case MAT_GEOM_WORLD: + case MAT_GEOM_VOLUME: + case MAT_GEOM_LOOKDEV: + /* Not supported. */ + break; + } + sub.erase(0, pos + delimiter.length()); + } + output += "\n"; + + if (geometry_type != MAT_GEOM_WORLD) { + output += "IN_OUT AttributesInterface\n"; + output += "{\n"; + output += codegen->attribs_interface; + output += "};\n\n"; + } + } + + output += "void attrib_load(void)\n"; + output += "{\n"; + if (codegen->attribs_load && geometry_type != MAT_GEOM_WORLD) { + output += codegen->attribs_load; + } + output += "}\n\n"; + + if (ELEM(geometry_type, MAT_GEOM_MESH, MAT_GEOM_HAIR)) { + if (codegen->displacement) { + if (GPU_material_flag_get(mat, GPU_MATFLAG_UNIFORMS_ATTRIB)) { + output += datatoc_common_uniform_attribute_lib_glsl; + } + output += codegen->uniforms; + output += "\n"; + output += codegen->library; + output += "\n"; + } + + output += "vec3 nodetree_displacement(void)\n"; + output += "{\n"; + if (codegen->displacement) { + output += codegen->displacement; + } + else { + output += "return vec3(0);\n"; + } + output += "}\n\n"; + } + + switch (geometry_type) { + case MAT_GEOM_WORLD: + output += datatoc_eevee_surface_world_vert_glsl; + break; + case MAT_GEOM_VOLUME: + output += datatoc_eevee_volume_vert_glsl; + break; + case MAT_GEOM_GPENCIL: + output += datatoc_eevee_surface_gpencil_vert_glsl; + break; + case MAT_GEOM_LOOKDEV: + output += datatoc_eevee_surface_lookdev_vert_glsl; + break; + case MAT_GEOM_HAIR: + output += datatoc_eevee_surface_hair_vert_glsl; + break; + case MAT_GEOM_MESH: + default: + output += datatoc_eevee_surface_mesh_vert_glsl; + break; + } + + return DRW_shader_library_create_shader_string(shader_lib_, output.c_str()); +} + +char *ShaderModule::material_shader_code_geom_get(const GPUCodegenOutput *codegen, + GPUMaterial *mat, + eMaterialGeometry geometry_type) +{ + /* Force geometry usage if GPU_BARYCENTRIC_DIST is used. */ + if (!GPU_material_flag_get(mat, GPU_MATFLAG_BARYCENTRIC) || + !ELEM(geometry_type, MAT_GEOM_GPENCIL, MAT_GEOM_MESH)) { + return nullptr; + } + + StringRefNull interp_lib(datatoc_eevee_surface_lib_glsl); + int64_t start = interp_lib.find("SurfaceInterface"); + int64_t end = interp_lib.find("interp"); + StringRef interp_lib_stripped = interp_lib.substr(start, end - start); + std::string output = "\n\n"; + + if (codegen->attribs_interface) { + output += "in AttributesInterface\n"; + output += "{\n"; + output += codegen->attribs_interface; + output += "} attr_in[];\n\n"; + + output += "out AttributesInterface\n"; + output += "{\n"; + output += codegen->attribs_interface; + output += "} attr_out;\n\n"; + } + + output += "in "; + output += interp_lib_stripped; + output += "interp_in[];\n\n"; + + output += "out "; + output += interp_lib_stripped; + output += "interp_out;\n\n"; + + output += datatoc_eevee_surface_mesh_geom_glsl; + + output += "void main(void)\n"; + output += "{\n"; + output += "interp_out.barycentric_dists = calc_barycentric_distances("; + output += " interp_in[0].P, interp_in[1].P, interp_in[2].P);\n "; + + for (int i = 0; i < 3; i++) { + output += "{\n"; + output += "const int vert_id = " + std::to_string(i) + ";\n"; + output += "interp_out.barycentric_coords = calc_barycentric_co(vert_id);"; + output += "gl_Position = gl_in[vert_id].gl_Position;"; + if (codegen->attribs_passthrough) { + output += codegen->attribs_passthrough; + } + output += "EmitVertex();"; + output += "}\n"; + } + output += "}\n"; + + return DRW_shader_library_create_shader_string(shader_lib_, output.c_str()); +} + +char *ShaderModule::material_shader_code_frag_get(const GPUCodegenOutput *codegen, + GPUMaterial *gpumat, + eMaterialGeometry geometry_type, + eMaterialPipeline pipeline_type) +{ + std::string output = "\n\n"; + + /* World material loads attribs in fragment shader (only used for orco). */ + if (geometry_type == MAT_GEOM_WORLD) { + if (codegen->attribs_interface) { + /* Declare inputs. */ + std::string delimiter = ";\n"; + std::string sub(codegen->attribs_declare); + size_t pos = 0; + while ((pos = sub.find(delimiter)) != std::string::npos) { + /* Example print: + * vec2 u015684; + * These are not used and just here to make the attribs_load functions call valids. + * Only orco layer is supported by world. */ + output += sub.substr(0, pos + delimiter.length()); + sub.erase(0, pos + delimiter.length()); + } + output += "\n"; + + output += codegen->attribs_interface; + output += "\n"; + } + + output += "void attrib_load(void)\n"; + output += "{\n"; + if (codegen->attribs_interface) { + output += codegen->attribs_load; + } + output += "}\n\n"; + } + else { + if (codegen->attribs_interface) { + output += "IN_OUT AttributesInterface\n"; + output += "{\n"; + output += codegen->attribs_interface; + output += "};\n\n"; + } + } + + if (codegen->surface || codegen->volume) { + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_UNIFORMS_ATTRIB)) { + output += datatoc_common_uniform_attribute_lib_glsl; + } + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_OBJECT_INFO)) { + output += "#pragma BLENDER_REQUIRE(common_obinfos_lib.glsl)\n"; + } + output += codegen->uniforms; + output += "\n"; + output += codegen->library; + output += "\n"; + } + + output += "Closure nodetree_surface(void)\n"; + output += "{\n"; + if (codegen->surface) { + output += codegen->surface; + } + else { + output += "return CLOSURE_DEFAULT;\n"; + } + output += "}\n\n"; + + output += "Closure nodetree_volume(void)\n"; + output += "{\n"; + if (codegen->volume) { + output += codegen->volume; + } + else { + output += "return CLOSURE_DEFAULT;\n"; + } + output += "}\n\n"; + + output += "float nodetree_thickness(void)\n"; + output += "{\n"; + if (codegen->thickness) { + output += codegen->thickness; + } + else { + /* TODO(fclem): Better default. */ + output += "return 0.1;\n"; + } + output += "}\n\n"; + + switch (geometry_type) { + case MAT_GEOM_WORLD: + output += datatoc_eevee_surface_background_frag_glsl; + break; + case MAT_GEOM_VOLUME: + switch (pipeline_type) { + case MAT_PIPE_DEFERRED: + output += datatoc_eevee_volume_deferred_frag_glsl; + break; + default: + BLI_assert(0); + break; + } + break; + default: + switch (pipeline_type) { + case MAT_PIPE_FORWARD_PREPASS: + output += datatoc_eevee_surface_depth_simple_frag_glsl; + break; + case MAT_PIPE_DEFERRED_PREPASS: + case MAT_PIPE_SHADOW: + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) { + output += datatoc_eevee_surface_depth_frag_glsl; + } + else { + output += datatoc_eevee_surface_depth_simple_frag_glsl; + } + break; + case MAT_PIPE_DEFERRED: + output += datatoc_eevee_surface_deferred_frag_glsl; + break; + case MAT_PIPE_FORWARD: + output += datatoc_eevee_surface_forward_frag_glsl; + break; + default: + BLI_assert(0); + break; + } + break; + } + + return DRW_shader_library_create_shader_string(shader_lib_, output.c_str()); +} + +/* WATCH: This can be called from another thread! Needs to not touch the shader module in any + * thread unsafe manner. */ +GPUShaderSource ShaderModule::material_shader_code_generate(GPUMaterial *mat, + const GPUCodegenOutput *codegen) +{ + uint64_t shader_uuid = GPU_material_uuid_get(mat); + + eMaterialPipeline pipeline_type; + eMaterialGeometry geometry_type; + material_type_from_shader_uuid(shader_uuid, pipeline_type, geometry_type); + + GPUShaderSource source; + source.vertex = material_shader_code_vert_get(codegen, mat, geometry_type); + source.fragment = material_shader_code_frag_get(codegen, mat, geometry_type, pipeline_type); + source.geometry = material_shader_code_geom_get(codegen, mat, geometry_type); + source.defines = material_shader_code_defs_get(geometry_type); + return source; +} + +static GPUShaderSource codegen_callback(void *thunk, + GPUMaterial *mat, + const GPUCodegenOutput *codegen) +{ + return ((ShaderModule *)thunk)->material_shader_code_generate(mat, codegen); +} + +GPUMaterial *ShaderModule::material_shader_get(::Material *blender_mat, + struct bNodeTree *nodetree, + eMaterialPipeline pipeline_type, + eMaterialGeometry geometry_type, + bool deferred_compilation) +{ + uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type); + + bool is_volume = (pipeline_type == MAT_PIPE_VOLUME); + + return DRW_shader_from_material( + blender_mat, nodetree, shader_uuid, is_volume, deferred_compilation, codegen_callback, this); +} + +GPUMaterial *ShaderModule::world_shader_get(::World *blender_world, struct bNodeTree *nodetree) +{ + eMaterialPipeline pipeline_type = MAT_PIPE_DEFERRED; /* Unused. */ + eMaterialGeometry geometry_type = MAT_GEOM_WORLD; + + uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type); + + bool is_volume = (pipeline_type == MAT_PIPE_VOLUME); + bool deferred_compilation = false; + + return DRW_shader_from_world(blender_world, + nodetree, + shader_uuid, + is_volume, + deferred_compilation, + codegen_callback, + this); +} + +/* Variation to compile a material only with a nodetree. Caller needs to maintain the list of + * materials and call GPU_material_free on it to update the material. */ +GPUMaterial *ShaderModule::material_shader_get(const char *name, + ListBase &materials, + struct bNodeTree *nodetree, + eMaterialPipeline pipeline_type, + eMaterialGeometry geometry_type, + bool is_lookdev) +{ + uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type); + + bool is_volume = (pipeline_type == MAT_PIPE_VOLUME); + + GPUMaterial *gpumat = GPU_material_from_nodetree(nullptr, + nullptr, + nodetree, + &materials, + name, + shader_uuid, + is_volume, + is_lookdev, + codegen_callback, + this); + GPU_material_status_set(gpumat, GPU_MAT_QUEUED); + GPU_material_compile(gpumat); + return gpumat; +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_shader.hh b/source/blender/draw/engines/eevee/eevee_shader.hh new file mode 100644 index 00000000000..ec7601b97c9 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_shader.hh @@ -0,0 +1,191 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Shader module that manage shader libraries, deferred compilation, + * and static shader usage. + */ + +#pragma once + +#include <array> +#include <string> + +#include "BLI_string_ref.hh" +#include "DRW_render.h" +#include "GPU_material.h" +#include "GPU_shader.h" + +#include "eevee_id_map.hh" + +namespace blender::eevee { + +/* Keep alphabetical order and clean prefix. */ +enum eShaderType { + CULLING_DEBUG = 0, + CULLING_SELECT, + CULLING_SORT, + CULLING_TILE, + + DEFERRED_EVAL_DIRECT, + DEFERRED_EVAL_HOLDOUT, + DEFERRED_EVAL_TRANSPARENT, + DEFERRED_EVAL_VOLUME, + + DEFERRED_MESH, + DEFERRED_VOLUME, + + DOF_BOKEH_LUT, + DOF_GATHER_BACKGROUND_LUT, + DOF_GATHER_BACKGROUND, + DOF_FILTER, + DOF_GATHER_FOREGROUND_LUT, + DOF_GATHER_FOREGROUND, + DOF_GATHER_HOLEFILL, + DOF_REDUCE_COPY, + DOF_REDUCE_DOWNSAMPLE, + DOF_REDUCE_RECURSIVE, + DOF_RESOLVE, + DOF_RESOLVE_HQ, + DOF_RESOLVE_LUT, + DOF_RESOLVE_LUT_HQ, + DOF_SCATTER_BACKGROUND_LUT, + DOF_SCATTER_BACKGROUND, + DOF_SCATTER_FOREGROUND_LUT, + DOF_SCATTER_FOREGROUND, + DOF_SETUP, + DOF_TILES_DILATE_MINABS, + DOF_TILES_DILATE_MINMAX, + DOF_TILES_FLATTEN, + + FILM_FILTER, + FILM_RESOLVE, + FILM_RESOLVE_DEPTH, + + HIZ_COPY, + HIZ_DOWNSAMPLE, + + LIGHTPROBE_DISPLAY_CUBEMAP, + LIGHTPROBE_DISPLAY_IRRADIANCE, + + LIGHTPROBE_FILTER_DOWNSAMPLE_CUBE, + LIGHTPROBE_FILTER_GLOSSY, + LIGHTPROBE_FILTER_DIFFUSE, + LIGHTPROBE_FILTER_VISIBILITY, + + LOOKDEV_BACKGROUND, + + MOTION_BLUR_GATHER, + MOTION_BLUR_TILE_DILATE, + MOTION_BLUR_TILE_FLATTEN, + + RAYTRACE_DIFFUSE, + RAYTRACE_DIFFUSE_FALLBACK, + RAYTRACE_REFLECTION, + RAYTRACE_REFLECTION_FALLBACK, + RAYTRACE_REFRACTION, + RAYTRACE_REFRACTION_FALLBACK, + RAYTRACE_DENOISE_DIFFUSE, + RAYTRACE_DENOISE_REFLECTION, + RAYTRACE_DENOISE_REFRACTION, + RAYTRACE_RESOLVE_DIFFUSE, + RAYTRACE_RESOLVE_REFLECTION, + RAYTRACE_RESOLVE_REFRACTION, + + SHADOW_DEBUG, + SHADOW_PAGE_ALLOC, + SHADOW_PAGE_COPY, + SHADOW_PAGE_DEBUG, + SHADOW_PAGE_DEFRAG, + SHADOW_PAGE_FREE, + SHADOW_PAGE_INIT, + SHADOW_PAGE_MARK, + SHADOW_TILE_DEPTH_SCAN, + SHADOW_TILE_LOD_MASK, + SHADOW_TILE_SETUP, + SHADOW_TILE_TAG_UPDATE, + SHADOW_TILE_TAG_USAGE, + SHADOW_TILE_TAG_VISIBILITY, + + SUBSURFACE_EVAL, + + VELOCITY_CAMERA, + VELOCITY_MESH, + + MAX_SHADER_TYPE, +}; + +/** + * Shader module. shared between instances. + */ +class ShaderModule { + private: + struct ShaderDescription { + const char *name = nullptr; + const char *vertex_shader_code = nullptr; + const char *geometry_shader_code = nullptr; + const char *fragment_shader_code = nullptr; + const char *compute_shader_code = nullptr; + const char *defines_shader_code = nullptr; + }; + + DRWShaderLibrary *shader_lib_ = nullptr; + std::array<GPUShader *, MAX_SHADER_TYPE> shaders_; + std::array<ShaderDescription, MAX_SHADER_TYPE> shader_descriptions_; + std::string shared_lib_; + + public: + ShaderModule(); + ~ShaderModule(); + + GPUShader *static_shader_get(eShaderType shader_type); + GPUMaterial *material_shader_get(::Material *blender_mat, + struct bNodeTree *nodetree, + eMaterialPipeline pipeline_type, + eMaterialGeometry geometry_type, + bool deferred_compilation); + GPUMaterial *world_shader_get(::World *blender_world, struct bNodeTree *nodetree); + GPUMaterial *material_shader_get(const char *name, + ListBase &materials, + struct bNodeTree *nodetree, + eMaterialPipeline pipeline_type, + eMaterialGeometry geometry_type, + bool is_lookdev); + + GPUShaderSource material_shader_code_generate(GPUMaterial *mat, const GPUCodegenOutput *codegen); + + private: + /* Run some custom preprocessor shader rewrite and returns a new string. */ + std::string enum_preprocess(const char *input); + + char *material_shader_code_defs_get(eMaterialGeometry geometry_type); + char *material_shader_code_vert_get(const GPUCodegenOutput *codegen, + GPUMaterial *mat, + eMaterialGeometry geometry_type); + char *material_shader_code_geom_get(const GPUCodegenOutput *codegen, + GPUMaterial *mat, + eMaterialGeometry geometry_type); + char *material_shader_code_frag_get(const GPUCodegenOutput *codegen, + GPUMaterial *mat, + eMaterialGeometry geometry_type, + eMaterialPipeline pipeline_type); +}; + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_shader_shared.hh b/source/blender/draw/engines/eevee/eevee_shader_shared.hh new file mode 100644 index 00000000000..07690744367 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_shader_shared.hh @@ -0,0 +1,934 @@ + +/** + * Shared structures, enums & defines between C++ and GLSL. + * Can also include some math functions but they need to be simple enough to be valid in both + * language. + */ + +/** + * NOTE: Enum support is not part of GLSL. It is handled by our own pre-processor pass in + * EEVEE's shader module. + * + * IMPORTANT: + * - Don't add trailing comma at the end of the enum. Our custom pre-processor will noy trim it + * for GLSL. + * - Always use `u` suffix for values. GLSL do not support implicit cast. + * - Define all values. This is in order to simplify custom pre-processor code. + * - Always use uint32_t as underlying type. + * - Use float suffix by default for float literals to avoid double promotion in C++. + * - Pack one float or int after a vec3/ivec3 to fullfil alligment rules. + * + * NOTE: Due to alignment restriction and buggy drivers, do not try to use mat3 inside structs. + * Do not use arrays of float. They are padded to arrays of vec4 and are not worth it. + * + * IMPORTANT: Don't forget to align mat4 and vec4 to 16 bytes. + **/ + +#ifndef __cplusplus /* GLSL */ +# pragma BLENDER_REQUIRE(common_math_lib.glsl) +# define BLI_STATIC_ASSERT_ALIGN(type_, align_) +# define BLI_STATIC_ASSERT_SIZE(type_, size_) +# define static +# define inline +# define cosf cos +# define sinf sin +# define tanf tan +# define acosf acos +# define asinf asin +# define atanf atan +# define floorf floor +# define ceilf ceil +# define sqrtf sqrt + +#else /* C++ */ +# pragma once + +# include "BLI_float4x4.hh" + +typedef float mat4[4][4]; +using vec4 = blender::float4; +using vec3 = blender::float3; +using vec2 = blender::float2; +typedef int ivec4[4]; +using ivec3 = blender::int3; +using ivec2 = blender::int2; +typedef uint uvec4[4]; +typedef uint uvec3[3]; +typedef uint uvec2[2]; +typedef int bvec4[4]; +typedef int bvec2[2]; +/* Ugly but it does the job! */ +# define bool int + +# include "eevee_wrapper.hh" + +namespace blender::eevee { + +#endif + +#define UBO_MIN_MAX_SUPPORTED_SIZE 1 << 14 + +/* -------------------------------------------------------------------- */ +/** \name Sampling + * \{ */ + +enum eSamplingDimension : uint32_t { + SAMPLING_FILTER_U = 0u, + SAMPLING_FILTER_V = 1u, + SAMPLING_LENS_U = 2u, + SAMPLING_LENS_V = 3u, + SAMPLING_TIME = 4u, + SAMPLING_SHADOW_U = 5u, + SAMPLING_SHADOW_V = 6u, + SAMPLING_SHADOW_W = 7u, + SAMPLING_SHADOW_X = 8u, + SAMPLING_SHADOW_Y = 9u, + SAMPLING_CLOSURE = 10u, + SAMPLING_LIGHTPROBE = 11u, + SAMPLING_TRANSPARENCY = 12u, + SAMPLING_SSS_U = 13u, + SAMPLING_SSS_V = 14u, + SAMPLING_RAYTRACE_U = 15u, + SAMPLING_RAYTRACE_V = 16u, + SAMPLING_RAYTRACE_W = 17u, + SAMPLING_RAYTRACE_X = 18u +}; + +/** IMPORTANT: Make sure the array can contain all sampling dimensions. */ +#define SAMPLING_DIMENSION_COUNT 19 + +struct SamplingData { + /** Array containing random values from Low Discrepency Sequence in [0..1) range. */ + /** HACK: float arrays are padded to vec4 in GLSL. Using vec4 for now to get the same alignment + * but this is wasteful. */ + vec4 dimensions[SAMPLING_DIMENSION_COUNT]; +}; +BLI_STATIC_ASSERT_ALIGN(SamplingData, 16) + +/* Returns total sample count in a web pattern of the given size. */ +static inline int web_sample_count_get(int web_density, int ring_count) +{ + return ((ring_count * ring_count + ring_count) / 2) * web_density + 1; +} + +/* Returns lowest possible ring count that contains at least sample_count samples. */ +static inline int web_ring_count_get(int web_density, int sample_count) +{ + /* Inversion of web_sample_count_get(). */ + float x = 2.0f * (float(sample_count) - 1.0f) / float(web_density); + /* Solving polynomial. We only search positive solution. */ + float discriminant = 1.0f + 4.0f * x; + return int(ceilf(0.5f * (sqrtf(discriminant) - 1.0f))); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Camera + * \{ */ + +enum eCameraType : uint32_t { + CAMERA_PERSP = 0u, + CAMERA_ORTHO = 1u, + CAMERA_PANO_EQUIRECT = 2u, + CAMERA_PANO_EQUISOLID = 3u, + CAMERA_PANO_EQUIDISTANT = 4u, + CAMERA_PANO_MIRROR = 5u +}; + +static inline bool is_panoramic(eCameraType type) +{ + return type > CAMERA_ORTHO; +} + +struct CameraData { + /* View Matrices of the camera, not from any view! */ + mat4 persmat; + mat4 persinv; + mat4 viewmat; + mat4 viewinv; + mat4 winmat; + mat4 wininv; + /** Camera UV scale and bias. Also known as viewcamtexcofac. */ + vec2 uv_scale; + vec2 uv_bias; + /** Panorama parameters. */ + vec2 equirect_scale; + vec2 equirect_scale_inv; + vec2 equirect_bias; + float fisheye_fov; + float fisheye_lens; + /** Clipping distances. */ + float clip_near; + float clip_far; + /** Film pixel filter radius. */ + float filter_size; + eCameraType type; +}; +BLI_STATIC_ASSERT_ALIGN(CameraData, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Film + * \{ */ + +enum eDebugMode : uint32_t { + /* TODO(fclem) Rename shadow cases. */ + SHADOW_DEBUG_NONE = 0u, + /** + * Gradient showing light evaluation hotspots. + */ + DEBUG_LIGHT_CULLING = 4u, + /** + * Tilemaps to screen. Is also present in other modes. + * - Black pixels, no pages allocated. + * - Green pixels, pages cached. + * - Red pixels, pages allocated. + */ + SHADOW_DEBUG_TILEMAPS = 5u, + /** + * Random color per pages. Validates page density allocation and sampling. + */ + SHADOW_DEBUG_PAGES = 6u, + /** + * Outputs random color per tilemap (or tilemap level). Validates tilemaps coverage. + * Black means not covered by any tilemaps LOD of the shadow. + */ + SHADOW_DEBUG_LOD = 7u, + /** + * Outputs white pixels for pages allocated and black pixels for unused pages. + * This needs SHADOW_DEBUG_PAGE_ALLOCATION_ENABLED defined in order to work. + */ + SHADOW_DEBUG_PAGE_ALLOCATION = 8u, + /** + * Outputs the tilemap atlas. Default tilemap is too big for the usual screen resolution. + * Try lowering SHADOW_TILEMAP_PER_ROW and SHADOW_MAX_TILEMAP before using this option. + */ + SHADOW_DEBUG_TILE_ALLOCATION = 9u, + /** + * Visualize linear depth stored in the atlas regions of the active light. + * This way, one can check if the rendering, the copying and the shadow sampling functions works. + */ + SHADOW_DEBUG_SHADOW_DEPTH = 10u +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Film + * \{ */ + +enum eFilmDataType : uint32_t { + /** Color is accumulated using the pixel filter. No negative values. */ + FILM_DATA_COLOR = 0u, + /** Variant where we accumulate using pre-exposed values and log space. */ + FILM_DATA_COLOR_LOG = 1u, + /** Non-Color will be accumulated using nearest filter. All values are allowed. */ + FILM_DATA_FLOAT = 2u, + FILM_DATA_VEC2 = 3u, + /** No VEC3 because GPU_RGB16F is not a renderable format. */ + FILM_DATA_VEC4 = 4u, + FILM_DATA_NORMAL = 5u, + FILM_DATA_DEPTH = 6u, + FILM_DATA_MOTION = 7u +}; + +struct FilmData { + /** Size of the render target in pixels. */ + ivec2 extent; + /** Offset of the render target in the full-res frame, in pixels. */ + ivec2 offset; + /** Scale and bias to filter only a region of the render (aka. render_border). */ + vec2 uv_bias; + vec2 uv_scale; + vec2 uv_scale_inv; + /** Data type stored by this film. */ + eFilmDataType data_type; + /** Is true if history is valid and can be sampled. Bypassing history to resets accumulation. */ + bool use_history; + /** Used for fade-in effect. */ + float opacity; + /** Padding to sizeof(vec4). */ + int _pad0, _pad1, _pad2; +}; +BLI_STATIC_ASSERT_ALIGN(FilmData, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Depth of field + * \{ */ + +/* 5% error threshold. */ +#define DOF_FAST_GATHER_COC_ERROR 0.05 +#define DOF_GATHER_RING_COUNT 5 +#define DOF_DILATE_RING_COUNT 3 +#define DOF_TILE_DIVISOR 16 +#define DOF_BOKEH_LUT_SIZE 32 + +struct DepthOfFieldData { + /** Size of the render targets for gather & scatter passes. */ + ivec2 extent; + /** Size of a pixel in uv space (1.0 / extent). */ + vec2 texel_size; + /** Bokeh Scale factor. */ + vec2 bokeh_anisotropic_scale; + vec2 bokeh_anisotropic_scale_inv; + /* Correction factor to align main target pixels with the filtered mipmap chain texture. */ + vec2 gather_uv_fac; + /** Scatter parameters. */ + float scatter_coc_threshold; + float scatter_color_threshold; + float scatter_neighbor_max_color; + int scatter_sprite_per_row; + /** Downsampling paramters. */ + float denoise_factor; + /** Bokeh Shape parameters. */ + float bokeh_blades; + float bokeh_rotation; + float bokeh_aperture_ratio; + /** Circle of confusion (CoC) parameters. */ + float coc_mul; + float coc_bias; + float coc_abs_max; + /** Copy of camera type. */ + eCameraType camera_type; + int _pad0, _pad1; +}; +BLI_STATIC_ASSERT_ALIGN(DepthOfFieldData, 16) + +static inline float coc_radius_from_camera_depth(DepthOfFieldData dof, float depth) +{ + depth = (dof.camera_type != CAMERA_ORTHO) ? 1.0f / depth : depth; + return dof.coc_mul * depth + dof.coc_bias; +} + +static inline float regular_polygon_side_length(float sides_count) +{ + return 2.0f * sinf(M_PI / sides_count); +} + +/* Returns intersection ratio between the radius edge at theta and the regular polygon edge. + * Start first corners at theta == 0. */ +static inline float circle_to_polygon_radius(float sides_count, float theta) +{ + /* From Graphics Gems from CryENGINE 3 (Siggraph 2013) by Tiago Sousa (slide + * 36). */ + float side_angle = (2.0f * M_PI) / sides_count; + return cosf(side_angle * 0.5f) / + cosf(theta - side_angle * floorf((sides_count * theta + M_PI) / (2.0f * M_PI))); +} + +/* Remap input angle to have homogenous spacing of points along a polygon edge. + * Expects theta to be in [0..2pi] range. */ +static inline float circle_to_polygon_angle(float sides_count, float theta) +{ + float side_angle = (2.0f * M_PI) / sides_count; + float halfside_angle = side_angle * 0.5f; + float side = floorf(theta / side_angle); + /* Length of segment from center to the middle of polygon side. */ + float adjacent = circle_to_polygon_radius(sides_count, 0.0f); + + /* This is the relative position of the sample on the polygon half side. */ + float local_theta = theta - side * side_angle; + float ratio = (local_theta - halfside_angle) / halfside_angle; + + float halfside_len = regular_polygon_side_length(sides_count) * 0.5f; + float opposite = ratio * halfside_len; + + /* NOTE: atan(y_over_x) has output range [-M_PI_2..M_PI_2]. */ + float final_local_theta = atanf(opposite / adjacent); + + return side * side_angle + final_local_theta; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name VelocityModule + * \{ */ + +struct VelocityObjectData { + mat4 next_object_mat; + mat4 prev_object_mat; +}; +BLI_STATIC_ASSERT_ALIGN(VelocityObjectData, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Motion Blur + * \{ */ + +#define MB_TILE_DIVISOR 32 + +struct MotionBlurData { + /** Motion vector lengths are clamped to this maximum. A value of 0 means effect is bypassed. */ + float blur_max; + /** Depth scaling factor. Avoid bluring background behind moving objects. */ + float depth_scale; + /** As the name suggests. Used to avoid a division in the sampling. */ + vec2 target_size_inv; + /** Viewport motion blur only blurs using previous frame vectors. */ + bool is_viewport; + int _pad0, _pad1, _pad2; +}; +BLI_STATIC_ASSERT_ALIGN(MotionBlurData, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cullings + * \{ */ + +/* TODO(fclem) Rename this. Only used by probes now. */ +#define CULLING_ITEM_BATCH 128 +/* Number of items we can cull. Limited by how we store CullingZBin. */ +#define CULLING_MAX_ITEM 65536 +/* Number of items in a culling batch. Needs to be Power of 2. Must be <= to 65536. */ +/* Current limiting factor is the sorting phase which is single pass and only sort within a + * threadgroup which maximum size is 1024. */ +#define CULLING_BATCH_SIZE 1024 +/* Maximum number of 32 bit uint stored per tile. */ +#define CULLING_MAX_WORD (CULLING_BATCH_SIZE / 32) +/* Fine grained subdivision in the Z direction (Must be multiple of CULLING_BATCH_SIZE). */ +#define CULLING_ZBIN_COUNT 4096 + +struct CullingData { + /** Scale applied to tile pixel coordinates to get target UV coordinate. */ + vec2 tile_to_uv_fac; + /** Scale and bias applied to linear Z to get zbin. */ + float zbin_scale; + float zbin_bias; + /** Valid item count in the source data array. */ + uint items_count; + /** Items to skip that are not processed by the 2.5D culling. */ + uint items_no_cull_count; + /** Number of items that passes the first culling test. */ + uint visible_count; + /** Will disable specular during light data copy.. */ + bool enable_specular; + /** Extent of one square tile in pixels. */ + uint tile_size; + /** Number of tiles on the X/Y axis. */ + uint tile_x_len; + uint tile_y_len; + /** Number of word per tile. Depends on the maximum number of lights. */ + uint tile_word_len; +}; +BLI_STATIC_ASSERT_ALIGN(CullingData, 16) + +#define CullingZBin uint +#define CullingWord uint + +static inline int culling_z_to_zbin(CullingData data, float z) +{ + return int(z * data.zbin_scale + data.zbin_bias); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Shadows + * \{ */ + +/** + * Shadow data for either a directional shadow or a punctual shadow. + * + * A punctual shadow is composed of 1, 5 or 6 shadow regions. + * Regions are sorted in this order -Z, +X, -X, +Y, -Y, +Z. + * Face index is computed from light's object space coordinates. + * + * A directional light shadow is composed of multiple clipmaps with each level + * covering twice as much area as the previous one. + */ +struct ShadowData { + /** + * Point : Unused. + * Directional : Rotation matrix to local light coordinate. + * The scale is uniform for the Z axis. + * For the X & Y axes, it is scaled to be the size of a tile. + * Origin is the one of the largest clipmap. + * So after transformation, you are in the tilemap space [0..SHADOW_TILEMAP_RES] + * of the largest clipmap. + */ + mat4 mat; + /** NOTE: It is ok to use vec3 here. A float is declared right after it. + * vec3 is also aligned to 16 bytes. */ + /** Shadow offset caused by jittering projection origin (for soft shadows). */ + vec3 offset; + /** Shadow bias in world space. */ + float bias; + /** Near and far clipping distance to convert shadowmap to world space distances. */ + float clip_near; + float clip_far; + /** Index of the first tilemap. */ + int tilemap_index; + /** Index of the last tilemap. */ + int tilemap_last; + /** Directional : Clipmap lod range to avoid sampling outside of valid range. */ + int clipmap_lod_min, clipmap_lod_max; + /** Directional : Offset of the lod min in base units. */ + ivec2 base_offset; +}; +BLI_STATIC_ASSERT_ALIGN(ShadowData, 16) + +/** + * IMPORTANT: Some data packing are tweaked for these values. + * Be sure to update them accordingly. + * SHADOW_TILEMAP_RES max is 32 because of the shared bitmaps used for LOD tagging. + * It is also limited by the maximum thread group size (1024). + */ +#define SHADOW_TILEMAP_RES 16 +#define SHADOW_TILEMAP_LOD 4 /* LOG2(SHADOW_TILEMAP_RES) */ +#define SHADOW_TILEMAP_PER_ROW 64 +#define SHADOW_PAGE_COPY_GROUP_SIZE 32 +#define SHADOW_DEPTH_SCAN_GROUP_SIZE 32 +#define SHADOW_AABB_TAG_GROUP_SIZE 64 +#define SHADOW_MAX_TILEMAP 4096 +#define SHADOW_MAX_PAGE 4096 +#define SHADOW_PAGE_PER_ROW 64 + +#define SHADOW_DEBUG_PAGE_ALLOCATION_ENABLED +#define SHADOW_DEBUG_TILE_ALLOCATION_ENABLED +/** Debug shadow tile allocation. */ +// #define SHADOW_DEBUG_NO_CACHING +/* Debug: Comment to only use BBox tagging instead of depth scanning. */ +// #define SHADOW_DEBUG_NO_DEPTH_SCAN +/* Debug: Will freeze the camera used for shadow tagging if G.debug_value is >= 4. */ +// #define SHADOW_DEBUG_FREEZE_CAMERA + +#if defined(SHADOW_DEBUG_FREEZE_CAMERA) && defined(SHADOW_DEBUG_NO_DEPTH_SCAN) +# error Freeze camera debug option is incompatible with depth scanning. +#endif + +/* Given an input tile coordinate [0..SHADOW_TILEMAP_RES] returns the coordinate in NDC [-1..1]. */ +static inline vec2 shadow_tile_coord_to_ndc(ivec2 tile) +{ + vec2 co = vec2(tile.x, tile.y) / float(SHADOW_TILEMAP_RES); + return co * 2.0f - 1.0f; +} + +/** + * Small descriptor used for the tile update phase. + */ +struct ShadowTileMapData { + /** View Projection matrix used to tag tiles (World > UV Tile [0..SHADOW_TILEMAP_RES]). */ + mat4 tilemat; + /** Corners of the frustum. */ + vec4 corners[4]; + /** NDC depths to clip usage bbox. */ +#define _max_usage_depth corners[0].w +#define _min_usage_depth corners[1].w +#define _punctual_distance corners[2].w + /** Shift to apply to the tile grid in the setup phase. */ + ivec2 grid_shift; + /** True for punctual lights. */ + bool is_cubeface; + /** Index inside the tilemap allocator. */ + int index; + /** Cone direction for punctual shadows. */ + vec3 cone_direction; + /** Cosine of the max angle. Offset to take into acount the max tile angle. */ + float cone_angle_cos; +}; +BLI_STATIC_ASSERT_ALIGN(ShadowTileMapData, 16) + +struct ShadowPagesInfoData { + /** Index of the next free pages in the free page heap. */ + int page_free_next; + /** Last number of free pages at the begining of the redraw. */ + int page_free_next_prev; + /** Number of pages that needs to be rendered. */ + int page_updated_count; + int _pad0; +}; +BLI_STATIC_ASSERT_ALIGN(ShadowPagesInfoData, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Lights + * \{ */ + +#define LIGHT_NO_SHADOW -1 + +enum eLightType : uint32_t { + LIGHT_SUN = 0u, + LIGHT_POINT = 1u, + LIGHT_SPOT = 2u, + LIGHT_RECT = 3u, + LIGHT_ELLIPSE = 4u +}; + +static inline bool is_area_light(eLightType type) +{ + return type >= LIGHT_RECT; +} + +struct LightData { + /** Normalized obmat. Last column contains data accessible using the following macros. */ + mat4 object_mat; + /** Packed data in the last column of the object_mat. */ +#define _area_size_x object_mat[0][3] +#define _area_size_y object_mat[1][3] +#define _radius _area_size_x +#define _spot_mul object_mat[2][3] +#define _spot_bias object_mat[3][3] + /** Aliases for axes. */ +#ifdef __cplusplus +# define _right object_mat[0] +# define _up object_mat[1] +# define _back object_mat[2] +# define _position object_mat[3] +#else +# define _right object_mat[0].xyz +# define _up object_mat[1].xyz +# define _back object_mat[2].xyz +# define _position object_mat[3].xyz +#endif + /** Influence radius (inversed and squared) adjusted for Surface / Volume power. */ + float influence_radius_invsqr_surface; + float influence_radius_invsqr_volume; + /** Maximum influence radius. Used for culling. */ + float influence_radius_max; + /** Index of the shadow struct on CPU. -1 means no shadow. */ + int shadow_id; + /** NOTE: It is ok to use vec3 here. A float is declared right after it. + * vec3 is also aligned to 16 bytes. */ + vec3 color; + /** Power depending on shader type. */ + float diffuse_power; + float specular_power; + float volume_power; + float transmit_power; + /** Special radius factor for point lighting. */ + float radius_squared; + /** Light Type. */ + eLightType type; + /** Padding to sizeof(vec2). */ + float _pad1; + /** Spot size. Aligned to size of vec2. */ + vec2 spot_size_inv; + /** Associated shadow data. Only valid if shadow_id is not LIGHT_NO_SHADOW. */ + ShadowData shadow_data; +}; +BLI_STATIC_ASSERT_ALIGN(LightData, 16) + +/** + * Shadow data for debugging the active light shadow. + */ +struct ShadowDebugData { + LightData light; + ShadowData shadow; + vec3 camera_position; + eDebugMode type; + int tilemap_data_index; + int _pad1; + int _pad2; + int _pad3; +}; +BLI_STATIC_ASSERT_ALIGN(ShadowDebugData, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Light Probes + * \{ */ + +/** + * Data used when filtering the cubemap. + * NOTE(fclem): We might want to promote some of theses to push constants as they are changed + * very frequently (Vulkan). + */ +struct LightProbeFilterData { + /** For glossy filter. */ + float roughness; + /** Higher bias lowers the noise but increases blur and reduces quality. */ + float lod_bias; + /** Final intensity multiplicator. */ + float instensity_fac; + /** Luma maximum value. */ + float luma_max; + /** Sample count to take from the input cubemap. */ + float sample_count; + /** Visibility blur ratio [0..1]. Converted to angle in [0..PI/2] range. */ + float visibility_blur; + /** Depth range to encode in the resulting visibility map. */ + float visibility_range; + /** Target layer to render the fullscreen triangle to. */ + int target_layer; +}; +BLI_STATIC_ASSERT_ALIGN(LightProbeFilterData, 16) + +/** + * Common data to all irradiance grid. + */ +struct GridInfoData { + mat4 lookdev_rotation; + /** Total of visibility cells per row and layer. */ + int visibility_cells_per_row; + int visibility_cells_per_layer; + /** Size of visibility cell. */ + int visibility_size; + /** Number of irradiance cells per row. */ + int irradiance_cells_per_row; + /** Smooth irradiance sample interpolation but increases light leaks. */ + float irradiance_smooth; + /** Total number of active irradiance grid including world. */ + int grid_count; + /** Display size of sample spheres. */ + float display_size; + float _pad0; +}; +BLI_STATIC_ASSERT_ALIGN(GridInfoData, 16) + +/** + * Data for a single irradiance grid. + */ +struct GridData { + /** Object matrix inverse (World -> Local). */ + mat4 local_mat; + /** Resolution of the light grid. */ + ivec3 resolution; + /** Offset of the first cell of this grid in the grid texture. */ + int offset; + /** World space vector between 2 adjacent cells. */ + vec3 increment_x; + /** Attenuation Bias. */ + float attenuation_bias; + /** World space vector between 2 adjacent cells. */ + vec3 increment_y; + /** Attenuation scaling. */ + float attenuation_scale; + /** World space vector between 2 adjacent cells. */ + vec3 increment_z; + /** Number of grid levels not ready for display during baking. */ + int level_skip; + /** World space corner position. */ + vec3 corner; + /** Visibility test parameters. */ + float visibility_range; + float visibility_bleed; + float visibility_bias; + float _pad0; + float _pad1; +}; +BLI_STATIC_ASSERT_ALIGN(GridData, 16) + +static inline ivec3 grid_cell_index_to_coordinate(int cell_id, ivec3 resolution) +{ + ivec3 cell_coord; + cell_coord.z = cell_id % resolution.z; + cell_coord.y = (cell_id / resolution.z) % resolution.y; + cell_coord.x = cell_id / (resolution.z * resolution.y); + return cell_coord; +} + +/** + * Common data to all cubemaps. + */ +struct CubemapInfoData { + mat4 lookdev_rotation; + /** LOD containing data for roughness of 1. */ + float roughness_max_lod; + /** Total number of active cubemaps including world. */ + int cube_count; + /** Display size of sample spheres. */ + float display_size; + float _pad2; +}; +BLI_STATIC_ASSERT_ALIGN(CubemapInfoData, 16) + +#define CUBEMAP_SHAPE_SPHERE 0.0 +#define CUBEMAP_SHAPE_BOX 1.0 + +/** + * Data for a single reflection cubemap probe. + */ +struct CubemapData { + /** Influence shape matrix (World -> Local). */ + mat4 influence_mat; + /** Packed data in the last column of the influence_mat. */ +#define _attenuation_factor influence_mat[0][3] +#define _attenuation_type influence_mat[1][3] +#define _parallax_type influence_mat[2][3] + /** Layer of the cube array to sample. */ +#define _layer influence_mat[3][3] + /** Parallax shape matrix (World -> Local). */ + mat4 parallax_mat; + /** Packed data in the last column of the parallax_mat. */ +#define _world_position_x parallax_mat[0][3] +#define _world_position_y parallax_mat[1][3] +#define _world_position_z parallax_mat[2][3] +}; +BLI_STATIC_ASSERT_ALIGN(CubemapData, 16) + +struct LightProbeInfoData { + GridInfoData grids; + CubemapInfoData cubes; +}; +BLI_STATIC_ASSERT_ALIGN(LightProbeInfoData, 16) + +#define GRID_MAX 64 + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Hierarchical-Z Buffer + * \{ */ + +struct HiZData { + /** Scale factor to remove HiZBuffer padding. */ + vec2 uv_scale; + /** Scale factor to convert from pixel space to Normalized Device Coordinates [-1..1]. */ + vec2 pixel_to_ndc; +}; +BLI_STATIC_ASSERT_ALIGN(HiZData, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Raytracing + * \{ */ + +struct RaytraceData { + /** View space thickness the objects. */ + float thickness; + /** Determine how fast the sample steps are getting bigger. */ + float quality; + /** Importance sample bias. Lower values will make the render less noisy. */ + float bias; + /** Maximum brightness during lighting evaluation. */ + float brightness_clamp; + /** Maximum roughness for which we will trace a ray. */ + float max_roughness; + /** Resolve sample pool offset, based on scene current sample. */ + int pool_offset; + int _pad0; + int _pad1; +}; +BLI_STATIC_ASSERT_ALIGN(RaytraceData, 16) + +struct RaytraceBufferData { + /** ViewProjection matrix used to render the previous frame. */ + mat4 history_persmat; + /** False if the history buffer was just allocated and contains uninitialized data. */ + bool valid_history_diffuse; + bool valid_history_reflection; + bool valid_history_refraction; + int _pad0; +}; +BLI_STATIC_ASSERT_ALIGN(RaytraceData, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Subsurface + * \{ */ + +#define SSS_SAMPLE_MAX 64 +#define SSS_BURLEY_TRUNCATE 16.0 +#define SSS_BURLEY_TRUNCATE_CDF 0.9963790093708328 +#define SSS_TRANSMIT_LUT_SIZE 64.0 +#define SSS_TRANSMIT_LUT_RADIUS 1.218 +#define SSS_TRANSMIT_LUT_SCALE ((SSS_TRANSMIT_LUT_SIZE - 1.0) / float(SSS_TRANSMIT_LUT_SIZE)) +#define SSS_TRANSMIT_LUT_BIAS (0.5 / float(SSS_TRANSMIT_LUT_SIZE)) +#define SSS_TRANSMIT_LUT_STEP_RES 64.0 + +struct SubsurfaceData { + /** xy: 2D sample position [-1..1], zw: sample_bounds. */ + /* NOTE(fclem) Using vec4 for alignment. */ + vec4 samples[SSS_SAMPLE_MAX]; + /** Sample index after which samples are not randomly rotated anymore. */ + int jitter_threshold; + /** Number of samples precomputed in the set. */ + int sample_len; + int _pad0; + int _pad1; +}; +BLI_STATIC_ASSERT_ALIGN(SubsurfaceData, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Utility Texture + * \{ */ + +#define UTIL_TEX_SIZE 64 +#define UTIL_BTDF_LAYER_COUNT 16 +/* Scale and bias to avoid interpolation of the border pixel. + * Remap UVs to the border pixels centers. */ +#define UTIL_TEX_UV_SCALE ((UTIL_TEX_SIZE - 1.0f) / UTIL_TEX_SIZE) +#define UTIL_TEX_UV_BIAS (0.5f / UTIL_TEX_SIZE) + +#define UTIL_BLUE_NOISE_LAYER 0 +#define UTIL_LTC_MAT_LAYER 1 +#define UTIL_LTC_MAG_LAYER 2 +#define UTIL_BSDF_LAYER 2 +#define UTIL_BTDF_LAYER 3 +#define UTIL_DISK_INTEGRAL_LAYER 3 +#define UTIL_DISK_INTEGRAL_COMP 2 + +#ifndef __cplusplus +/* For codestyle reasons, we do not declare samplers in lib files. Use a prototype instead. */ +vec4 utility_tx_fetch(vec2 texel, float layer); +vec4 utility_tx_sample(vec2 uv, float layer); + +/* Fetch texel. Wrapping if above range. */ +# define utility_tx_fetch_define(utility_tx_) \ + vec4 utility_tx_fetch(vec2 texel, float layer) \ + { \ + return texelFetch(utility_tx_, ivec3(ivec2(texel) % UTIL_TEX_SIZE, layer), 0); \ + } + +/* Sample at uv position. Filtered & Wrapping enabled. */ +# define utility_tx_sample_define(utility_tx_) \ + vec4 utility_tx_sample(vec2 uv, float layer) \ + { \ + return textureLod(utility_tx_, vec3(uv, layer), 0.0); \ + } + +/* Stubs declarations if not using it. */ +# define utility_tx_fetch_define_stub(utility_tx_) \ + vec4 utility_tx_fetch(vec2 texel, float layer) \ + { \ + return vec4(0); \ + } +# define utility_tx_sample_define_stub(utility_tx_) \ + vec4 utility_tx_sample(vec2 uv, float layer) \ + { \ + return vec4(0); \ + } +#endif + +/** \} */ + +#ifdef __cplusplus +using CameraDataBuf = StructBuffer<CameraData>; +using CubemapDataBuf = StructArrayBuffer<CubemapData, CULLING_ITEM_BATCH>; +using CullingDataBuf = StorageBuffer<CullingData>; +using CullingKeyBuf = StorageArrayBuffer<uint, CULLING_BATCH_SIZE, true>; +using CullingLightBuf = StorageArrayBuffer<LightData, CULLING_BATCH_SIZE, true>; +using CullingTileBuf = StorageArrayBuffer<uint, 16 * 16 * CULLING_MAX_WORD, true>; +using CullingZbinBuf = StorageArrayBuffer<uint, CULLING_ZBIN_COUNT, true>; +using DepthOfFieldDataBuf = StructBuffer<DepthOfFieldData>; +using GridDataBuf = StructArrayBuffer<GridData, GRID_MAX>; +using HiZDataBuf = StructBuffer<HiZData>; +using LightDataBuf = StorageArrayBuffer<LightData, CULLING_BATCH_SIZE>; +using LightProbeFilterDataBuf = StructBuffer<LightProbeFilterData>; +using LightProbeInfoDataBuf = StructBuffer<LightProbeInfoData>; +using RaytraceBufferDataBuf = StructBuffer<RaytraceBufferData>; +using RaytraceDataBuf = StructBuffer<RaytraceData>; +using ShadowDataBuf = StorageArrayBuffer<ShadowData, CULLING_BATCH_SIZE>; +using ShadowDebugDataBuf = StructBuffer<ShadowDebugData>; +using ShadowPagesInfoDataBuf = StorageBuffer<ShadowPagesInfoData, true>; +using ShadowPageHeapBuf = StorageArrayBuffer<uint, SHADOW_MAX_PAGE, true>; +using ShadowTileMapDataBuf = StorageArrayBuffer<ShadowTileMapData, SHADOW_MAX_TILEMAP>; +using SubsurfaceDataBuf = StructBuffer<SubsurfaceData>; +using VelocityObjectBuf = StructBuffer<VelocityObjectData>; + +# undef bool +} // namespace blender::eevee +#endif diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c deleted file mode 100644 index c4108969c99..00000000000 --- a/source/blender/draw/engines/eevee/eevee_shaders.c +++ /dev/null @@ -1,1652 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - */ - -#include "DRW_render.h" - -#include "BKE_lib_id.h" -#include "BKE_node.h" - -#include "BLI_dynstr.h" -#include "BLI_string_utils.h" - -#include "DNA_world_types.h" - -#include "MEM_guardedalloc.h" - -#include "GPU_capabilities.h" -#include "GPU_material.h" -#include "GPU_shader.h" - -#include "NOD_shader.h" - -#include "eevee_engine.h" -#include "eevee_private.h" - -static const char *filter_defines = -#if defined(IRRADIANCE_SH_L2) - "#define IRRADIANCE_SH_L2\n"; -#elif defined(IRRADIANCE_HL2) - "#define IRRADIANCE_HL2\n"; -#endif - -static struct { - /* Lookdev */ - struct GPUShader *studiolight_probe_sh; - struct GPUShader *studiolight_background_sh; - - /* Probes */ - struct GPUShader *probe_grid_display_sh; - struct GPUShader *probe_cube_display_sh; - struct GPUShader *probe_planar_display_sh; - struct GPUShader *probe_filter_glossy_sh; - struct GPUShader *probe_filter_diffuse_sh; - struct GPUShader *probe_filter_visibility_sh; - struct GPUShader *probe_grid_fill_sh; - struct GPUShader *probe_planar_downsample_sh; - - /* Velocity Resolve */ - struct GPUShader *velocity_resolve_sh; - - /* Temporal Anti Aliasing */ - struct GPUShader *taa_resolve_sh; - struct GPUShader *taa_resolve_reproject_sh; - - /* Bloom */ - struct GPUShader *bloom_blit_sh[2]; - struct GPUShader *bloom_downsample_sh[2]; - struct GPUShader *bloom_upsample_sh[2]; - struct GPUShader *bloom_resolve_sh[2]; - - /* Depth Of Field */ - struct GPUShader *dof_bokeh_sh; - struct GPUShader *dof_setup_sh; - struct GPUShader *dof_flatten_tiles_sh; - struct GPUShader *dof_dilate_tiles_sh[2]; - struct GPUShader *dof_downsample_sh; - struct GPUShader *dof_reduce_sh[2]; - struct GPUShader *dof_gather_sh[DOF_GATHER_MAX_PASS][2]; - struct GPUShader *dof_filter_sh; - struct GPUShader *dof_scatter_sh[2][2]; - struct GPUShader *dof_resolve_sh[2][2]; - - /* General purpose Shaders. */ - struct GPUShader *lookdev_background; - struct GPUShader *update_noise_sh; - - /* Down-sample Depth */ - struct GPUShader *minz_downlevel_sh; - struct GPUShader *maxz_downlevel_sh; - struct GPUShader *minz_downdepth_sh; - struct GPUShader *maxz_downdepth_sh; - struct GPUShader *minz_downdepth_layer_sh; - struct GPUShader *maxz_downdepth_layer_sh; - struct GPUShader *maxz_copydepth_layer_sh; - struct GPUShader *minz_copydepth_sh; - struct GPUShader *maxz_copydepth_sh; - - /* Simple Down-sample. */ - struct GPUShader *color_copy_sh; - struct GPUShader *downsample_sh; - struct GPUShader *downsample_cube_sh; - - /* Mist */ - struct GPUShader *mist_sh; - - /* Motion Blur */ - struct GPUShader *motion_blur_sh; - struct GPUShader *motion_blur_object_sh; - struct GPUShader *motion_blur_hair_sh; - struct GPUShader *velocity_tiles_sh; - struct GPUShader *velocity_tiles_expand_sh; - - /* Ground Truth Ambient Occlusion */ - struct GPUShader *gtao_sh; - struct GPUShader *gtao_layer_sh; - struct GPUShader *gtao_debug_sh; - - /* GGX LUT */ - struct GPUShader *ggx_lut_sh; - struct GPUShader *ggx_refraction_lut_sh; - - /* Render Passes */ - struct GPUShader *postprocess_sh; - struct GPUShader *cryptomatte_sh[2]; - - /* Screen Space Reflection */ - struct GPUShader *reflection_trace; - struct GPUShader *reflection_resolve; - - /* Shadows */ - struct GPUShader *shadow_sh; - struct GPUShader *shadow_accum_sh; - - /* Subsurface */ - struct GPUShader *sss_sh[3]; - - /* Volume */ - struct GPUShader *volumetric_clear_sh; - struct GPUShader *scatter_sh; - struct GPUShader *scatter_with_lights_sh; - struct GPUShader *volumetric_integration_sh; - struct GPUShader *volumetric_resolve_sh[2]; - struct GPUShader *volumetric_accum_sh; - - /* Shader strings */ - char *surface_lit_frag; - char *surface_prepass_frag; - char *surface_geom_barycentric; - - DRWShaderLibrary *lib; - - /* LookDev Materials */ - Material *glossy_mat; - Material *diffuse_mat; - - Material *error_mat; - - World *default_world; - - /* Default Material */ - struct { - bNodeTree *ntree; - bNodeSocketValueRGBA *color_socket; - bNodeSocketValueFloat *metallic_socket; - bNodeSocketValueFloat *roughness_socket; - bNodeSocketValueFloat *specular_socket; - } surface; - - struct { - bNodeTree *ntree; - bNodeSocketValueRGBA *color_socket; - } world; -} e_data = {NULL}; /* Engine data */ - -extern char datatoc_common_hair_lib_glsl[]; -extern char datatoc_common_math_lib_glsl[]; -extern char datatoc_common_math_geom_lib_glsl[]; -extern char datatoc_common_view_lib_glsl[]; -extern char datatoc_gpu_shader_common_obinfos_lib_glsl[]; - -extern char datatoc_ambient_occlusion_lib_glsl[]; -extern char datatoc_background_vert_glsl[]; -extern char datatoc_bsdf_common_lib_glsl[]; -extern char datatoc_bsdf_lut_frag_glsl[]; -extern char datatoc_bsdf_sampling_lib_glsl[]; -extern char datatoc_btdf_lut_frag_glsl[]; -extern char datatoc_closure_type_lib_glsl[]; -extern char datatoc_common_uniforms_lib_glsl[]; -extern char datatoc_common_utiltex_lib_glsl[]; -extern char datatoc_cryptomatte_frag_glsl[]; -extern char datatoc_cubemap_lib_glsl[]; -extern char datatoc_default_frag_glsl[]; -extern char datatoc_lookdev_world_frag_glsl[]; -extern char datatoc_effect_bloom_frag_glsl[]; -extern char datatoc_effect_dof_bokeh_frag_glsl[]; -extern char datatoc_effect_dof_dilate_tiles_frag_glsl[]; -extern char datatoc_effect_dof_downsample_frag_glsl[]; -extern char datatoc_effect_dof_filter_frag_glsl[]; -extern char datatoc_effect_dof_flatten_tiles_frag_glsl[]; -extern char datatoc_effect_dof_gather_frag_glsl[]; -extern char datatoc_effect_dof_lib_glsl[]; -extern char datatoc_effect_dof_reduce_frag_glsl[]; -extern char datatoc_effect_dof_resolve_frag_glsl[]; -extern char datatoc_effect_dof_scatter_frag_glsl[]; -extern char datatoc_effect_dof_scatter_vert_glsl[]; -extern char datatoc_effect_dof_setup_frag_glsl[]; -extern char datatoc_effect_downsample_cube_frag_glsl[]; -extern char datatoc_effect_downsample_frag_glsl[]; -extern char datatoc_effect_gtao_frag_glsl[]; -extern char datatoc_effect_minmaxz_frag_glsl[]; -extern char datatoc_effect_mist_frag_glsl[]; -extern char datatoc_effect_motion_blur_frag_glsl[]; -extern char datatoc_effect_reflection_lib_glsl[]; -extern char datatoc_effect_reflection_resolve_frag_glsl[]; -extern char datatoc_effect_reflection_trace_frag_glsl[]; -extern char datatoc_effect_subsurface_frag_glsl[]; -extern char datatoc_effect_temporal_aa_glsl[]; -extern char datatoc_effect_translucency_frag_glsl[]; -extern char datatoc_effect_velocity_resolve_frag_glsl[]; -extern char datatoc_effect_velocity_tile_frag_glsl[]; -extern char datatoc_irradiance_lib_glsl[]; -extern char datatoc_lightprobe_cube_display_frag_glsl[]; -extern char datatoc_lightprobe_cube_display_vert_glsl[]; -extern char datatoc_lightprobe_filter_diffuse_frag_glsl[]; -extern char datatoc_lightprobe_filter_glossy_frag_glsl[]; -extern char datatoc_lightprobe_filter_visibility_frag_glsl[]; -extern char datatoc_lightprobe_geom_glsl[]; -extern char datatoc_lightprobe_grid_display_frag_glsl[]; -extern char datatoc_lightprobe_grid_display_vert_glsl[]; -extern char datatoc_lightprobe_grid_fill_frag_glsl[]; -extern char datatoc_lightprobe_lib_glsl[]; -extern char datatoc_lightprobe_planar_display_frag_glsl[]; -extern char datatoc_lightprobe_planar_display_vert_glsl[]; -extern char datatoc_lightprobe_planar_downsample_frag_glsl[]; -extern char datatoc_lightprobe_planar_downsample_geom_glsl[]; -extern char datatoc_lightprobe_planar_downsample_vert_glsl[]; -extern char datatoc_lightprobe_vert_glsl[]; -extern char datatoc_lights_lib_glsl[]; -extern char datatoc_closure_eval_lib_glsl[]; -extern char datatoc_closure_eval_diffuse_lib_glsl[]; -extern char datatoc_closure_eval_glossy_lib_glsl[]; -extern char datatoc_closure_eval_refraction_lib_glsl[]; -extern char datatoc_closure_eval_translucent_lib_glsl[]; -extern char datatoc_ltc_lib_glsl[]; -extern char datatoc_object_motion_frag_glsl[]; -extern char datatoc_object_motion_vert_glsl[]; -extern char datatoc_octahedron_lib_glsl[]; -extern char datatoc_prepass_frag_glsl[]; -extern char datatoc_prepass_vert_glsl[]; -extern char datatoc_random_lib_glsl[]; -extern char datatoc_raytrace_lib_glsl[]; -extern char datatoc_renderpass_lib_glsl[]; -extern char datatoc_renderpass_postprocess_frag_glsl[]; -extern char datatoc_shadow_accum_frag_glsl[]; -extern char datatoc_shadow_frag_glsl[]; -extern char datatoc_shadow_vert_glsl[]; -extern char datatoc_ssr_lib_glsl[]; -extern char datatoc_surface_frag_glsl[]; -extern char datatoc_surface_geom_glsl[]; -extern char datatoc_surface_lib_glsl[]; -extern char datatoc_surface_vert_glsl[]; -extern char datatoc_update_noise_frag_glsl[]; -extern char datatoc_volumetric_accum_frag_glsl[]; -extern char datatoc_volumetric_frag_glsl[]; -extern char datatoc_volumetric_geom_glsl[]; -extern char datatoc_volumetric_integration_frag_glsl[]; -extern char datatoc_volumetric_lib_glsl[]; -extern char datatoc_volumetric_resolve_frag_glsl[]; -extern char datatoc_volumetric_scatter_frag_glsl[]; -extern char datatoc_volumetric_vert_glsl[]; - -/* *********** FUNCTIONS *********** */ - -static void eevee_shader_library_ensure(void) -{ - if (e_data.lib == NULL) { - e_data.lib = DRW_shader_library_create(); - /* NOTE: These need to be ordered by dependencies. */ - DRW_SHADER_LIB_ADD(e_data.lib, common_math_lib); - DRW_SHADER_LIB_ADD(e_data.lib, common_math_geom_lib); - DRW_SHADER_LIB_ADD(e_data.lib, common_hair_lib); - DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib); - DRW_SHADER_LIB_ADD(e_data.lib, common_uniforms_lib); - DRW_SHADER_LIB_ADD(e_data.lib, gpu_shader_common_obinfos_lib); - DRW_SHADER_LIB_ADD(e_data.lib, random_lib); - DRW_SHADER_LIB_ADD(e_data.lib, renderpass_lib); - DRW_SHADER_LIB_ADD(e_data.lib, bsdf_common_lib); - DRW_SHADER_LIB_ADD(e_data.lib, common_utiltex_lib); - DRW_SHADER_LIB_ADD(e_data.lib, bsdf_sampling_lib); - DRW_SHADER_LIB_ADD(e_data.lib, cubemap_lib); - DRW_SHADER_LIB_ADD(e_data.lib, raytrace_lib); - DRW_SHADER_LIB_ADD(e_data.lib, ambient_occlusion_lib); - DRW_SHADER_LIB_ADD(e_data.lib, octahedron_lib); - DRW_SHADER_LIB_ADD(e_data.lib, irradiance_lib); - DRW_SHADER_LIB_ADD(e_data.lib, lightprobe_lib); - DRW_SHADER_LIB_ADD(e_data.lib, ltc_lib); - DRW_SHADER_LIB_ADD(e_data.lib, lights_lib); - DRW_SHADER_LIB_ADD(e_data.lib, surface_lib); - DRW_SHADER_LIB_ADD(e_data.lib, volumetric_lib); - DRW_SHADER_LIB_ADD(e_data.lib, ssr_lib); - DRW_SHADER_LIB_ADD(e_data.lib, effect_dof_lib); - DRW_SHADER_LIB_ADD(e_data.lib, effect_reflection_lib); - DRW_SHADER_LIB_ADD(e_data.lib, closure_type_lib); - DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_lib); - DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_diffuse_lib); - DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_glossy_lib); - DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_translucent_lib); - DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_refraction_lib); - - e_data.surface_lit_frag = DRW_shader_library_create_shader_string(e_data.lib, - datatoc_surface_frag_glsl); - - e_data.surface_prepass_frag = DRW_shader_library_create_shader_string( - e_data.lib, datatoc_prepass_frag_glsl); - - e_data.surface_geom_barycentric = DRW_shader_library_create_shader_string( - e_data.lib, datatoc_surface_geom_glsl); - } -} - -void EEVEE_shaders_material_shaders_init(void) -{ - eevee_shader_library_ensure(); -} - -DRWShaderLibrary *EEVEE_shader_lib_get(void) -{ - eevee_shader_library_ensure(); - return e_data.lib; -} - -GPUShader *EEVEE_shaders_probe_filter_glossy_sh_get(void) -{ - if (e_data.probe_filter_glossy_sh == NULL) { - e_data.probe_filter_glossy_sh = DRW_shader_create_with_shaderlib( - datatoc_lightprobe_vert_glsl, - datatoc_lightprobe_geom_glsl, - datatoc_lightprobe_filter_glossy_frag_glsl, - e_data.lib, - filter_defines); - } - return e_data.probe_filter_glossy_sh; -} - -GPUShader *EEVEE_shaders_probe_filter_diffuse_sh_get(void) -{ - if (e_data.probe_filter_diffuse_sh == NULL) { - e_data.probe_filter_diffuse_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_lightprobe_filter_diffuse_frag_glsl, e_data.lib, filter_defines); - } - return e_data.probe_filter_diffuse_sh; -} - -GPUShader *EEVEE_shaders_probe_filter_visibility_sh_get(void) -{ - if (e_data.probe_filter_visibility_sh == NULL) { - e_data.probe_filter_visibility_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_lightprobe_filter_visibility_frag_glsl, e_data.lib, filter_defines); - } - return e_data.probe_filter_visibility_sh; -} - -GPUShader *EEVEE_shaders_probe_grid_fill_sh_get(void) -{ - if (e_data.probe_grid_fill_sh == NULL) { - e_data.probe_grid_fill_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_lightprobe_grid_fill_frag_glsl, e_data.lib, filter_defines); - } - return e_data.probe_grid_fill_sh; -} - -GPUShader *EEVEE_shaders_probe_planar_downsample_sh_get(void) -{ - if (e_data.probe_planar_downsample_sh == NULL) { - e_data.probe_planar_downsample_sh = DRW_shader_create_with_shaderlib( - datatoc_lightprobe_planar_downsample_vert_glsl, - datatoc_lightprobe_planar_downsample_geom_glsl, - datatoc_lightprobe_planar_downsample_frag_glsl, - e_data.lib, - NULL); - } - return e_data.probe_planar_downsample_sh; -} - -GPUShader *EEVEE_shaders_studiolight_probe_sh_get(void) -{ - if (e_data.studiolight_probe_sh == NULL) { - e_data.studiolight_probe_sh = DRW_shader_create_with_shaderlib(datatoc_background_vert_glsl, - NULL, - datatoc_lookdev_world_frag_glsl, - e_data.lib, - SHADER_DEFINES); - } - return e_data.studiolight_probe_sh; -} - -GPUShader *EEVEE_shaders_studiolight_background_sh_get(void) -{ - if (e_data.studiolight_background_sh == NULL) { - e_data.studiolight_background_sh = DRW_shader_create_with_shaderlib( - datatoc_background_vert_glsl, - NULL, - datatoc_lookdev_world_frag_glsl, - e_data.lib, - "#define LOOKDEV_BG\n" SHADER_DEFINES); - } - return e_data.studiolight_background_sh; -} - -GPUShader *EEVEE_shaders_probe_cube_display_sh_get(void) -{ - if (e_data.probe_cube_display_sh == NULL) { - e_data.probe_cube_display_sh = DRW_shader_create_with_shaderlib( - datatoc_lightprobe_cube_display_vert_glsl, - NULL, - datatoc_lightprobe_cube_display_frag_glsl, - e_data.lib, - SHADER_DEFINES); - } - return e_data.probe_cube_display_sh; -} - -GPUShader *EEVEE_shaders_probe_grid_display_sh_get(void) -{ - if (e_data.probe_grid_display_sh == NULL) { - e_data.probe_grid_display_sh = DRW_shader_create_with_shaderlib( - datatoc_lightprobe_grid_display_vert_glsl, - NULL, - datatoc_lightprobe_grid_display_frag_glsl, - e_data.lib, - filter_defines); - } - return e_data.probe_grid_display_sh; -} - -GPUShader *EEVEE_shaders_probe_planar_display_sh_get(void) -{ - if (e_data.probe_planar_display_sh == NULL) { - e_data.probe_planar_display_sh = DRW_shader_create_with_shaderlib( - datatoc_lightprobe_planar_display_vert_glsl, - NULL, - datatoc_lightprobe_planar_display_frag_glsl, - e_data.lib, - NULL); - } - return e_data.probe_planar_display_sh; -} - -/* -------------------------------------------------------------------- */ -/** \name Down-sampling - * \{ */ - -GPUShader *EEVEE_shaders_effect_color_copy_sh_get(void) -{ - if (e_data.color_copy_sh == NULL) { - e_data.color_copy_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_downsample_frag_glsl, e_data.lib, "#define COPY_SRC\n"); - } - return e_data.color_copy_sh; -} - -GPUShader *EEVEE_shaders_effect_downsample_sh_get(void) -{ - if (e_data.downsample_sh == NULL) { - e_data.downsample_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_downsample_frag_glsl, e_data.lib, NULL); - } - return e_data.downsample_sh; -} - -GPUShader *EEVEE_shaders_effect_downsample_cube_sh_get(void) -{ - if (e_data.downsample_cube_sh == NULL) { - e_data.downsample_cube_sh = DRW_shader_create_with_shaderlib( - datatoc_lightprobe_vert_glsl, - datatoc_lightprobe_geom_glsl, - datatoc_effect_downsample_cube_frag_glsl, - e_data.lib, - NULL); - } - return e_data.downsample_cube_sh; -} - -GPUShader *EEVEE_shaders_effect_minz_downlevel_sh_get(void) -{ - if (e_data.minz_downlevel_sh == NULL) { - e_data.minz_downlevel_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl, - "#define MIN_PASS\n"); - } - return e_data.minz_downlevel_sh; -} - -GPUShader *EEVEE_shaders_effect_maxz_downlevel_sh_get(void) -{ - if (e_data.maxz_downlevel_sh == NULL) { - e_data.maxz_downlevel_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl, - "#define MAX_PASS\n"); - } - return e_data.maxz_downlevel_sh; -} - -GPUShader *EEVEE_shaders_effect_minz_downdepth_sh_get(void) -{ - if (e_data.minz_downdepth_sh == NULL) { - e_data.minz_downdepth_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl, - "#define MIN_PASS\n"); - } - return e_data.minz_downdepth_sh; -} - -GPUShader *EEVEE_shaders_effect_maxz_downdepth_sh_get(void) -{ - if (e_data.maxz_downdepth_sh == NULL) { - e_data.maxz_downdepth_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl, - "#define MAX_PASS\n"); - } - return e_data.maxz_downdepth_sh; -} - -GPUShader *EEVEE_shaders_effect_minz_downdepth_layer_sh_get(void) -{ - if (e_data.minz_downdepth_layer_sh == NULL) { - e_data.minz_downdepth_layer_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl, - "#define MIN_PASS\n" - "#define LAYERED\n"); - } - return e_data.minz_downdepth_layer_sh; -} - -GPUShader *EEVEE_shaders_effect_maxz_downdepth_layer_sh_get(void) -{ - if (e_data.maxz_downdepth_layer_sh == NULL) { - e_data.maxz_downdepth_layer_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl, - "#define MAX_PASS\n" - "#define LAYERED\n"); - } - return e_data.maxz_downdepth_layer_sh; -} - -GPUShader *EEVEE_shaders_effect_maxz_copydepth_layer_sh_get(void) -{ - if (e_data.maxz_copydepth_layer_sh == NULL) { - e_data.maxz_copydepth_layer_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl, - "#define MAX_PASS\n" - "#define COPY_DEPTH\n" - "#define LAYERED\n"); - } - return e_data.maxz_copydepth_layer_sh; -} - -GPUShader *EEVEE_shaders_effect_minz_copydepth_sh_get(void) -{ - if (e_data.minz_copydepth_sh == NULL) { - e_data.minz_copydepth_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl, - "#define MIN_PASS\n" - "#define COPY_DEPTH\n"); - } - return e_data.minz_copydepth_sh; -} - -GPUShader *EEVEE_shaders_effect_maxz_copydepth_sh_get(void) -{ - if (e_data.maxz_copydepth_sh == NULL) { - e_data.maxz_copydepth_sh = DRW_shader_create_fullscreen(datatoc_effect_minmaxz_frag_glsl, - "#define MAX_PASS\n" - "#define COPY_DEPTH\n"); - } - return e_data.maxz_copydepth_sh; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name GGX LUT - * \{ */ - -GPUShader *EEVEE_shaders_ggx_lut_sh_get(void) -{ - if (e_data.ggx_lut_sh == NULL) { - e_data.ggx_lut_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_bsdf_lut_frag_glsl, e_data.lib, NULL); - } - return e_data.ggx_lut_sh; -} - -GPUShader *EEVEE_shaders_ggx_refraction_lut_sh_get(void) -{ - if (e_data.ggx_refraction_lut_sh == NULL) { - e_data.ggx_refraction_lut_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_btdf_lut_frag_glsl, e_data.lib, NULL); - } - return e_data.ggx_refraction_lut_sh; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Mist - * \{ */ - -GPUShader *EEVEE_shaders_effect_mist_sh_get(void) -{ - if (e_data.mist_sh == NULL) { - e_data.mist_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_mist_frag_glsl, e_data.lib, "#define FIRST_PASS\n"); - } - return e_data.mist_sh; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Motion Blur - * \{ */ - -#define TILE_SIZE_STR "#define EEVEE_VELOCITY_TILE_SIZE " STRINGIFY(EEVEE_VELOCITY_TILE_SIZE) "\n" -GPUShader *EEVEE_shaders_effect_motion_blur_sh_get(void) -{ - if (e_data.motion_blur_sh == NULL) { - e_data.motion_blur_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_motion_blur_frag_glsl, e_data.lib, TILE_SIZE_STR); - } - return e_data.motion_blur_sh; -} - -GPUShader *EEVEE_shaders_effect_motion_blur_object_sh_get(void) -{ - if (e_data.motion_blur_object_sh == NULL) { - e_data.motion_blur_object_sh = DRW_shader_create_with_shaderlib( - datatoc_object_motion_vert_glsl, NULL, datatoc_object_motion_frag_glsl, e_data.lib, NULL); - } - return e_data.motion_blur_object_sh; -} - -GPUShader *EEVEE_shaders_effect_motion_blur_hair_sh_get(void) -{ - if (e_data.motion_blur_hair_sh == NULL) { - e_data.motion_blur_hair_sh = DRW_shader_create_with_shaderlib(datatoc_object_motion_vert_glsl, - NULL, - datatoc_object_motion_frag_glsl, - e_data.lib, - "#define HAIR\n"); - } - return e_data.motion_blur_hair_sh; -} - -GPUShader *EEVEE_shaders_effect_motion_blur_velocity_tiles_sh_get(void) -{ - if (e_data.velocity_tiles_sh == NULL) { - e_data.velocity_tiles_sh = DRW_shader_create_fullscreen(datatoc_effect_velocity_tile_frag_glsl, - "#define TILE_GATHER\n" TILE_SIZE_STR); - } - return e_data.velocity_tiles_sh; -} - -GPUShader *EEVEE_shaders_effect_motion_blur_velocity_tiles_expand_sh_get(void) -{ - if (e_data.velocity_tiles_expand_sh == NULL) { - e_data.velocity_tiles_expand_sh = DRW_shader_create_fullscreen( - datatoc_effect_velocity_tile_frag_glsl, "#define TILE_EXPANSION\n" TILE_SIZE_STR); - } - return e_data.velocity_tiles_expand_sh; -} - -#undef TILE_SIZE_STR - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Ambient Occlusion - * \{ */ - -GPUShader *EEVEE_shaders_effect_ambient_occlusion_sh_get(void) -{ - if (e_data.gtao_sh == NULL) { - e_data.gtao_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_gtao_frag_glsl, e_data.lib, NULL); - } - return e_data.gtao_sh; -} - -GPUShader *EEVEE_shaders_effect_ambient_occlusion_debug_sh_get(void) -{ - if (e_data.gtao_debug_sh == NULL) { - e_data.gtao_debug_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_gtao_frag_glsl, - e_data.lib, - "#define DEBUG_AO\n" - "#define ENABLE_DEFERED_AO"); - } - return e_data.gtao_debug_sh; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Render Passes - * \{ */ - -GPUShader *EEVEE_shaders_renderpasses_post_process_sh_get(void) -{ - if (e_data.postprocess_sh == NULL) { - e_data.postprocess_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_renderpass_postprocess_frag_glsl, e_data.lib, NULL); - } - return e_data.postprocess_sh; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Cryptomatte - * \{ */ - -GPUShader *EEVEE_shaders_cryptomatte_sh_get(bool is_hair) -{ - const int index = is_hair ? 1 : 0; - if (e_data.cryptomatte_sh[index] == NULL) { - DynStr *ds = BLI_dynstr_new(); - BLI_dynstr_append(ds, SHADER_DEFINES); - - if (is_hair) { - BLI_dynstr_append(ds, "#define HAIR_SHADER\n"); - } - else { - BLI_dynstr_append(ds, "#define MESH_SHADER\n"); - } - char *defines = BLI_dynstr_get_cstring(ds); - e_data.cryptomatte_sh[index] = DRW_shader_create_with_shaderlib( - datatoc_surface_vert_glsl, NULL, datatoc_cryptomatte_frag_glsl, e_data.lib, defines); - BLI_dynstr_free(ds); - MEM_freeN(defines); - } - return e_data.cryptomatte_sh[index]; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Raytraced Reflections - * \{ */ - -struct GPUShader *EEVEE_shaders_effect_reflection_trace_sh_get(void) -{ - if (e_data.reflection_trace == NULL) { - e_data.reflection_trace = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_reflection_trace_frag_glsl, - e_data.lib, - SHADER_DEFINES "#define STEP_RAYTRACE\n"); - } - return e_data.reflection_trace; -} - -struct GPUShader *EEVEE_shaders_effect_reflection_resolve_sh_get(void) -{ - if (e_data.reflection_resolve == NULL) { - e_data.reflection_resolve = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_reflection_resolve_frag_glsl, - e_data.lib, - SHADER_DEFINES "#define STEP_RESOLVE\n"); - } - return e_data.reflection_resolve; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Shadows - * \{ */ - -struct GPUShader *EEVEE_shaders_shadow_sh_get() -{ - if (e_data.shadow_sh == NULL) { - e_data.shadow_sh = DRW_shader_create_with_shaderlib( - datatoc_shadow_vert_glsl, NULL, datatoc_shadow_frag_glsl, e_data.lib, NULL); - } - return e_data.shadow_sh; -} - -struct GPUShader *EEVEE_shaders_shadow_accum_sh_get() -{ - if (e_data.shadow_accum_sh == NULL) { - e_data.shadow_accum_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_shadow_accum_frag_glsl, e_data.lib, SHADER_DEFINES); - } - return e_data.shadow_accum_sh; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Subsurface - * \{ */ - -struct GPUShader *EEVEE_shaders_subsurface_first_pass_sh_get() -{ - if (e_data.sss_sh[0] == NULL) { - e_data.sss_sh[0] = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_subsurface_frag_glsl, e_data.lib, "#define FIRST_PASS\n"); - } - return e_data.sss_sh[0]; -} - -struct GPUShader *EEVEE_shaders_subsurface_second_pass_sh_get() -{ - if (e_data.sss_sh[1] == NULL) { - e_data.sss_sh[1] = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_subsurface_frag_glsl, e_data.lib, "#define SECOND_PASS\n"); - } - return e_data.sss_sh[1]; -} - -struct GPUShader *EEVEE_shaders_subsurface_translucency_sh_get() -{ - if (e_data.sss_sh[2] == NULL) { - e_data.sss_sh[2] = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_translucency_frag_glsl, - e_data.lib, - "#define EEVEE_TRANSLUCENCY\n" SHADER_DEFINES); - } - return e_data.sss_sh[2]; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Volumes - * \{ */ - -struct GPUShader *EEVEE_shaders_volumes_clear_sh_get() -{ - if (e_data.volumetric_clear_sh == NULL) { - e_data.volumetric_clear_sh = DRW_shader_create_with_shaderlib(datatoc_volumetric_vert_glsl, - datatoc_volumetric_geom_glsl, - datatoc_volumetric_frag_glsl, - e_data.lib, - SHADER_DEFINES - "#define VOLUMETRICS\n" - "#define CLEAR\n"); - } - return e_data.volumetric_clear_sh; -} - -struct GPUShader *EEVEE_shaders_volumes_scatter_sh_get() -{ - if (e_data.scatter_sh == NULL) { - e_data.scatter_sh = DRW_shader_create_with_shaderlib(datatoc_volumetric_vert_glsl, - datatoc_volumetric_geom_glsl, - datatoc_volumetric_scatter_frag_glsl, - e_data.lib, - SHADER_DEFINES - "#define VOLUMETRICS\n" - "#define VOLUME_SHADOW\n"); - } - return e_data.scatter_sh; -} - -struct GPUShader *EEVEE_shaders_volumes_scatter_with_lights_sh_get() -{ - if (e_data.scatter_with_lights_sh == NULL) { - e_data.scatter_with_lights_sh = DRW_shader_create_with_shaderlib( - datatoc_volumetric_vert_glsl, - datatoc_volumetric_geom_glsl, - datatoc_volumetric_scatter_frag_glsl, - e_data.lib, - SHADER_DEFINES - "#define VOLUMETRICS\n" - "#define VOLUME_LIGHTING\n" - "#define VOLUME_SHADOW\n"); - } - return e_data.scatter_with_lights_sh; -} - -struct GPUShader *EEVEE_shaders_volumes_integration_sh_get() -{ - if (e_data.volumetric_integration_sh == NULL) { - e_data.volumetric_integration_sh = DRW_shader_create_with_shaderlib( - datatoc_volumetric_vert_glsl, - datatoc_volumetric_geom_glsl, - datatoc_volumetric_integration_frag_glsl, - e_data.lib, - USE_VOLUME_OPTI ? "#define USE_VOLUME_OPTI\n" SHADER_DEFINES : SHADER_DEFINES); - } - return e_data.volumetric_integration_sh; -} - -struct GPUShader *EEVEE_shaders_volumes_resolve_sh_get(bool accum) -{ - const int index = accum ? 1 : 0; - if (e_data.volumetric_resolve_sh[index] == NULL) { - e_data.volumetric_resolve_sh[index] = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_volumetric_resolve_frag_glsl, - e_data.lib, - accum ? "#define VOLUMETRICS_ACCUM\n" SHADER_DEFINES : SHADER_DEFINES); - } - return e_data.volumetric_resolve_sh[index]; -} - -struct GPUShader *EEVEE_shaders_volumes_accum_sh_get() -{ - if (e_data.volumetric_accum_sh == NULL) { - e_data.volumetric_accum_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_volumetric_accum_frag_glsl, e_data.lib, SHADER_DEFINES); - } - return e_data.volumetric_accum_sh; -} - -/** \} */ - -GPUShader *EEVEE_shaders_velocity_resolve_sh_get(void) -{ - if (e_data.velocity_resolve_sh == NULL) { - e_data.velocity_resolve_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_velocity_resolve_frag_glsl, e_data.lib, NULL); - } - return e_data.velocity_resolve_sh; -} - -GPUShader *EEVEE_shaders_update_noise_sh_get(void) -{ - if (e_data.update_noise_sh == NULL) { - e_data.update_noise_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_update_noise_frag_glsl, e_data.lib, NULL); - } - return e_data.update_noise_sh; -} - -GPUShader *EEVEE_shaders_taa_resolve_sh_get(EEVEE_EffectsFlag enabled_effects) -{ - GPUShader **sh; - const char *define = NULL; - if (enabled_effects & EFFECT_TAA_REPROJECT) { - sh = &e_data.taa_resolve_reproject_sh; - define = "#define USE_REPROJECTION\n"; - } - else { - sh = &e_data.taa_resolve_sh; - } - if (*sh == NULL) { - *sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_temporal_aa_glsl, e_data.lib, define); - } - - return *sh; -} - -/* -------------------------------------------------------------------- */ -/** \name Bloom - * \{ */ - -GPUShader *EEVEE_shaders_bloom_blit_get(bool high_quality) -{ - int index = high_quality ? 1 : 0; - - if (e_data.bloom_blit_sh[index] == NULL) { - const char *define = high_quality ? "#define STEP_BLIT\n" - "#define HIGH_QUALITY\n" : - "#define STEP_BLIT\n"; - e_data.bloom_blit_sh[index] = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_bloom_frag_glsl, e_data.lib, define); - } - return e_data.bloom_blit_sh[index]; -} - -GPUShader *EEVEE_shaders_bloom_downsample_get(bool high_quality) -{ - int index = high_quality ? 1 : 0; - - if (e_data.bloom_downsample_sh[index] == NULL) { - const char *define = high_quality ? "#define STEP_DOWNSAMPLE\n" - "#define HIGH_QUALITY\n" : - "#define STEP_DOWNSAMPLE\n"; - e_data.bloom_downsample_sh[index] = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_bloom_frag_glsl, e_data.lib, define); - } - return e_data.bloom_downsample_sh[index]; -} - -GPUShader *EEVEE_shaders_bloom_upsample_get(bool high_quality) -{ - int index = high_quality ? 1 : 0; - - if (e_data.bloom_upsample_sh[index] == NULL) { - const char *define = high_quality ? "#define STEP_UPSAMPLE\n" - "#define HIGH_QUALITY\n" : - "#define STEP_UPSAMPLE\n"; - e_data.bloom_upsample_sh[index] = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_bloom_frag_glsl, e_data.lib, define); - } - return e_data.bloom_upsample_sh[index]; -} - -GPUShader *EEVEE_shaders_bloom_resolve_get(bool high_quality) -{ - int index = high_quality ? 1 : 0; - - if (e_data.bloom_resolve_sh[index] == NULL) { - const char *define = high_quality ? "#define STEP_RESOLVE\n" - "#define HIGH_QUALITY\n" : - "#define STEP_RESOLVE\n"; - e_data.bloom_resolve_sh[index] = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_bloom_frag_glsl, e_data.lib, define); - } - return e_data.bloom_resolve_sh[index]; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Depth of field - * \{ */ - -GPUShader *EEVEE_shaders_depth_of_field_bokeh_get(void) -{ - if (e_data.dof_bokeh_sh == NULL) { - e_data.dof_bokeh_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_dof_bokeh_frag_glsl, e_data.lib, DOF_SHADER_DEFINES); - } - return e_data.dof_bokeh_sh; -} - -GPUShader *EEVEE_shaders_depth_of_field_setup_get(void) -{ - if (e_data.dof_setup_sh == NULL) { - e_data.dof_setup_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_dof_setup_frag_glsl, e_data.lib, DOF_SHADER_DEFINES); - } - return e_data.dof_setup_sh; -} - -GPUShader *EEVEE_shaders_depth_of_field_flatten_tiles_get(void) -{ - if (e_data.dof_flatten_tiles_sh == NULL) { - e_data.dof_flatten_tiles_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_dof_flatten_tiles_frag_glsl, e_data.lib, DOF_SHADER_DEFINES); - } - return e_data.dof_flatten_tiles_sh; -} - -GPUShader *EEVEE_shaders_depth_of_field_dilate_tiles_get(bool b_pass) -{ - int pass = b_pass; - if (e_data.dof_dilate_tiles_sh[pass] == NULL) { - e_data.dof_dilate_tiles_sh[pass] = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_dof_dilate_tiles_frag_glsl, - e_data.lib, - (pass == 0) ? DOF_SHADER_DEFINES "#define DILATE_MODE_MIN_MAX\n" : - DOF_SHADER_DEFINES "#define DILATE_MODE_MIN_ABS\n"); - } - return e_data.dof_dilate_tiles_sh[pass]; -} - -GPUShader *EEVEE_shaders_depth_of_field_downsample_get(void) -{ - if (e_data.dof_downsample_sh == NULL) { - e_data.dof_downsample_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_dof_downsample_frag_glsl, e_data.lib, DOF_SHADER_DEFINES); - } - return e_data.dof_downsample_sh; -} - -GPUShader *EEVEE_shaders_depth_of_field_reduce_get(bool b_is_copy_pass) -{ - int is_copy_pass = b_is_copy_pass; - if (e_data.dof_reduce_sh[is_copy_pass] == NULL) { - e_data.dof_reduce_sh[is_copy_pass] = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_dof_reduce_frag_glsl, - e_data.lib, - (is_copy_pass) ? DOF_SHADER_DEFINES "#define COPY_PASS\n" : - DOF_SHADER_DEFINES "#define REDUCE_PASS\n"); - } - return e_data.dof_reduce_sh[is_copy_pass]; -} - -GPUShader *EEVEE_shaders_depth_of_field_gather_get(EEVEE_DofGatherPass pass, bool b_use_bokeh_tx) -{ - int use_bokeh_tx = b_use_bokeh_tx; - if (e_data.dof_gather_sh[pass][use_bokeh_tx] == NULL) { - DynStr *ds = BLI_dynstr_new(); - - BLI_dynstr_append(ds, DOF_SHADER_DEFINES); - - switch (pass) { - case DOF_GATHER_FOREGROUND: - BLI_dynstr_append(ds, "#define DOF_FOREGROUND_PASS\n"); - break; - case DOF_GATHER_BACKGROUND: - BLI_dynstr_append(ds, "#define DOF_BACKGROUND_PASS\n"); - break; - case DOF_GATHER_HOLEFILL: - BLI_dynstr_append(ds, - "#define DOF_BACKGROUND_PASS\n" - "#define DOF_HOLEFILL_PASS\n"); - break; - default: - break; - } - - if (use_bokeh_tx) { - BLI_dynstr_append(ds, "#define DOF_BOKEH_TEXTURE\n"); - } - - char *define = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - - e_data.dof_gather_sh[pass][use_bokeh_tx] = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_dof_gather_frag_glsl, e_data.lib, define); - - MEM_freeN(define); - } - return e_data.dof_gather_sh[pass][use_bokeh_tx]; -} - -GPUShader *EEVEE_shaders_depth_of_field_filter_get(void) -{ - if (e_data.dof_filter_sh == NULL) { - e_data.dof_filter_sh = DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_dof_filter_frag_glsl, e_data.lib, DOF_SHADER_DEFINES); - } - return e_data.dof_filter_sh; -} - -GPUShader *EEVEE_shaders_depth_of_field_scatter_get(bool b_is_foreground, bool b_use_bokeh_tx) -{ - int is_foreground = b_is_foreground; - int use_bokeh_tx = b_use_bokeh_tx; - if (e_data.dof_scatter_sh[is_foreground][use_bokeh_tx] == NULL) { - DynStr *ds = BLI_dynstr_new(); - - BLI_dynstr_append(ds, DOF_SHADER_DEFINES); - BLI_dynstr_append( - ds, (is_foreground) ? "#define DOF_FOREGROUND_PASS\n" : "#define DOF_BACKGROUND_PASS\n"); - - if (use_bokeh_tx) { - BLI_dynstr_append(ds, "#define DOF_BOKEH_TEXTURE\n"); - } - - char *define = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - - e_data.dof_scatter_sh[is_foreground][use_bokeh_tx] = DRW_shader_create_with_shaderlib( - datatoc_effect_dof_scatter_vert_glsl, - NULL, - datatoc_effect_dof_scatter_frag_glsl, - e_data.lib, - define); - - MEM_freeN(define); - } - return e_data.dof_scatter_sh[is_foreground][use_bokeh_tx]; -} - -GPUShader *EEVEE_shaders_depth_of_field_resolve_get(bool b_use_bokeh_tx, bool b_use_hq_gather) -{ - int use_hq_gather = b_use_hq_gather; - int use_bokeh_tx = b_use_bokeh_tx; - if (e_data.dof_resolve_sh[use_bokeh_tx][use_hq_gather] == NULL) { - DynStr *ds = BLI_dynstr_new(); - - BLI_dynstr_append(ds, DOF_SHADER_DEFINES); - BLI_dynstr_append(ds, "#define DOF_RESOLVE_PASS\n"); - - if (use_bokeh_tx) { - BLI_dynstr_append(ds, "#define DOF_BOKEH_TEXTURE\n"); - } - - BLI_dynstr_appendf(ds, "#define DOF_SLIGHT_FOCUS_DENSITY %d\n", use_hq_gather ? 4 : 2); - - char *define = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - - e_data.dof_resolve_sh[use_bokeh_tx][use_hq_gather] = - DRW_shader_create_fullscreen_with_shaderlib( - datatoc_effect_dof_resolve_frag_glsl, e_data.lib, define); - - MEM_freeN(define); - } - return e_data.dof_resolve_sh[use_bokeh_tx][use_hq_gather]; -} - -/** \} */ - -Material *EEVEE_material_default_diffuse_get(void) -{ - if (!e_data.diffuse_mat) { - Material *ma = BKE_id_new_nomain(ID_MA, "EEVEEE default diffuse"); - - bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname); - ma->nodetree = ntree; - ma->use_nodes = true; - - bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_DIFFUSE); - bNodeSocket *base_color = nodeFindSocket(bsdf, SOCK_IN, "Color"); - copy_v3_fl(((bNodeSocketValueRGBA *)base_color->default_value)->value, 0.8f); - - bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_MATERIAL); - - nodeAddLink(ntree, - bsdf, - nodeFindSocket(bsdf, SOCK_OUT, "BSDF"), - output, - nodeFindSocket(output, SOCK_IN, "Surface")); - - nodeSetActive(ntree, output); - e_data.diffuse_mat = ma; - } - return e_data.diffuse_mat; -} - -Material *EEVEE_material_default_glossy_get(void) -{ - if (!e_data.glossy_mat) { - Material *ma = BKE_id_new_nomain(ID_MA, "EEVEEE default metal"); - - bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname); - ma->nodetree = ntree; - ma->use_nodes = true; - - bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_GLOSSY); - bNodeSocket *base_color = nodeFindSocket(bsdf, SOCK_IN, "Color"); - copy_v3_fl(((bNodeSocketValueRGBA *)base_color->default_value)->value, 1.0f); - bNodeSocket *roughness = nodeFindSocket(bsdf, SOCK_IN, "Roughness"); - ((bNodeSocketValueFloat *)roughness->default_value)->value = 0.0f; - - bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_MATERIAL); - - nodeAddLink(ntree, - bsdf, - nodeFindSocket(bsdf, SOCK_OUT, "BSDF"), - output, - nodeFindSocket(output, SOCK_IN, "Surface")); - - nodeSetActive(ntree, output); - e_data.glossy_mat = ma; - } - return e_data.glossy_mat; -} - -Material *EEVEE_material_default_error_get(void) -{ - if (!e_data.error_mat) { - Material *ma = BKE_id_new_nomain(ID_MA, "EEVEEE default metal"); - - bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname); - ma->nodetree = ntree; - ma->use_nodes = true; - - /* Use emission and output material to be compatible with both World and Material. */ - bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_EMISSION); - bNodeSocket *color = nodeFindSocket(bsdf, SOCK_IN, "Color"); - copy_v3_fl3(((bNodeSocketValueRGBA *)color->default_value)->value, 1.0f, 0.0f, 1.0f); - - bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_MATERIAL); - - nodeAddLink(ntree, - bsdf, - nodeFindSocket(bsdf, SOCK_OUT, "Emission"), - output, - nodeFindSocket(output, SOCK_IN, "Surface")); - - nodeSetActive(ntree, output); - e_data.error_mat = ma; - } - return e_data.error_mat; -} - -struct bNodeTree *EEVEE_shader_default_surface_nodetree(Material *ma) -{ - /* WARNING: This function is not threadsafe. Which is not a problem for the moment. */ - if (!e_data.surface.ntree) { - bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname); - bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_PRINCIPLED); - bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_MATERIAL); - bNodeSocket *bsdf_out = nodeFindSocket(bsdf, SOCK_OUT, "BSDF"); - bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface"); - nodeAddLink(ntree, bsdf, bsdf_out, output, output_in); - nodeSetActive(ntree, output); - - e_data.surface.color_socket = nodeFindSocket(bsdf, SOCK_IN, "Base Color")->default_value; - e_data.surface.metallic_socket = nodeFindSocket(bsdf, SOCK_IN, "Metallic")->default_value; - e_data.surface.roughness_socket = nodeFindSocket(bsdf, SOCK_IN, "Roughness")->default_value; - e_data.surface.specular_socket = nodeFindSocket(bsdf, SOCK_IN, "Specular")->default_value; - e_data.surface.ntree = ntree; - } - /* Update */ - copy_v3_fl3(e_data.surface.color_socket->value, ma->r, ma->g, ma->b); - e_data.surface.metallic_socket->value = ma->metallic; - e_data.surface.roughness_socket->value = ma->roughness; - e_data.surface.specular_socket->value = ma->spec; - - return e_data.surface.ntree; -} - -struct bNodeTree *EEVEE_shader_default_world_nodetree(World *wo) -{ - /* WARNING: This function is not threadsafe. Which is not a problem for the moment. */ - if (!e_data.world.ntree) { - bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname); - bNode *bg = nodeAddStaticNode(NULL, ntree, SH_NODE_BACKGROUND); - bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_WORLD); - bNodeSocket *bg_out = nodeFindSocket(bg, SOCK_OUT, "Background"); - bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface"); - nodeAddLink(ntree, bg, bg_out, output, output_in); - nodeSetActive(ntree, output); - - e_data.world.color_socket = nodeFindSocket(bg, SOCK_IN, "Color")->default_value; - e_data.world.ntree = ntree; - } - - copy_v3_fl3(e_data.world.color_socket->value, wo->horr, wo->horg, wo->horb); - - return e_data.world.ntree; -} - -World *EEVEE_world_default_get(void) -{ - if (e_data.default_world == NULL) { - e_data.default_world = BKE_id_new_nomain(ID_WO, "EEVEEE default world"); - copy_v3_fl(&e_data.default_world->horr, 0.0f); - e_data.default_world->use_nodes = 0; - e_data.default_world->nodetree = NULL; - BLI_listbase_clear(&e_data.default_world->gpumaterial); - } - return e_data.default_world; -} - -static char *eevee_get_defines(int options) -{ - char *str = NULL; - - DynStr *ds = BLI_dynstr_new(); - BLI_dynstr_append(ds, SHADER_DEFINES); - - if ((options & VAR_WORLD_BACKGROUND) != 0) { - BLI_dynstr_append(ds, "#define WORLD_BACKGROUND\n"); - } - if ((options & VAR_MAT_VOLUME) != 0) { - BLI_dynstr_append(ds, "#define VOLUMETRICS\n"); - } - if ((options & VAR_MAT_MESH) != 0) { - BLI_dynstr_append(ds, "#define MESH_SHADER\n"); - } - if ((options & VAR_MAT_DEPTH) != 0) { - BLI_dynstr_append(ds, "#define DEPTH_SHADER\n"); - } - if ((options & VAR_MAT_HAIR) != 0) { - BLI_dynstr_append(ds, "#define HAIR_SHADER\n"); - } - if ((options & VAR_MAT_POINTCLOUD) != 0) { - BLI_dynstr_append(ds, "#define POINTCLOUD_SHADER\n"); - } - if ((options & VAR_WORLD_PROBE) != 0) { - BLI_dynstr_append(ds, "#define PROBE_CAPTURE\n"); - } - if ((options & VAR_MAT_HASH) != 0) { - BLI_dynstr_append(ds, "#define USE_ALPHA_HASH\n"); - } - if ((options & VAR_MAT_BLEND) != 0) { - BLI_dynstr_append(ds, "#define USE_ALPHA_BLEND\n"); - } - if ((options & VAR_MAT_REFRACT) != 0) { - BLI_dynstr_append(ds, "#define USE_REFRACTION\n"); - } - if ((options & VAR_MAT_LOOKDEV) != 0) { - BLI_dynstr_append(ds, "#define LOOKDEV\n"); - } - if ((options & VAR_MAT_HOLDOUT) != 0) { - BLI_dynstr_append(ds, "#define HOLDOUT\n"); - } - - str = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - - return str; -} - -static char *eevee_get_vert(int options) -{ - char *str = NULL; - - if ((options & VAR_MAT_VOLUME) != 0) { - str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_volumetric_vert_glsl); - } - else if ((options & (VAR_WORLD_PROBE | VAR_WORLD_BACKGROUND)) != 0) { - str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_background_vert_glsl); - } - else { - str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_surface_vert_glsl); - } - - return str; -} - -static char *eevee_get_geom(int options) -{ - char *str = NULL; - - if ((options & VAR_MAT_VOLUME) != 0) { - str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_volumetric_geom_glsl); - } - - return str; -} - -static char *eevee_get_frag(int options) -{ - char *str = NULL; - - if ((options & VAR_MAT_VOLUME) != 0) { - str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_volumetric_frag_glsl); - } - else if ((options & VAR_MAT_DEPTH) != 0) { - str = BLI_strdup(e_data.surface_prepass_frag); - } - else { - str = BLI_strdup(e_data.surface_lit_frag); - } - - return str; -} - -static void eevee_material_post_eval(GPUMaterial *mat, - int options, - const char **UNUSED(vert_code), - const char **geom_code, - const char **UNUSED(frag_lib), - const char **UNUSED(defines)) -{ - const bool is_hair = (options & VAR_MAT_HAIR) != 0; - const bool is_mesh = (options & VAR_MAT_MESH) != 0; - - /* Force geometry usage if GPU_BARYCENTRIC_DIST or GPU_BARYCENTRIC_TEXCO are used. - * NOTE: GPU_BARYCENTRIC_TEXCO only requires it if the shader is not drawing hairs. */ - if (!is_hair && is_mesh && GPU_material_flag_get(mat, GPU_MATFLAG_BARYCENTRIC) && - *geom_code == NULL) { - *geom_code = e_data.surface_geom_barycentric; - } -} - -static struct GPUMaterial *eevee_material_get_ex( - struct Scene *scene, Material *ma, World *wo, int options, bool deferred) -{ - BLI_assert(ma || wo); - const bool is_volume = (options & VAR_MAT_VOLUME) != 0; - const bool is_default = (options & VAR_DEFAULT) != 0; - const void *engine = &DRW_engine_viewport_eevee_type; - - GPUMaterial *mat = NULL; - - if (ma) { - mat = DRW_shader_find_from_material(ma, engine, options, deferred); - } - else { - mat = DRW_shader_find_from_world(wo, engine, options, deferred); - } - - if (mat) { - return mat; - } - - char *defines = eevee_get_defines(options); - char *vert = eevee_get_vert(options); - char *geom = eevee_get_geom(options); - char *frag = eevee_get_frag(options); - - if (ma) { - GPUMaterialEvalCallbackFn cbfn = &eevee_material_post_eval; - - bNodeTree *ntree = !is_default ? ma->nodetree : EEVEE_shader_default_surface_nodetree(ma); - mat = DRW_shader_create_from_material( - scene, ma, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred, cbfn); - } - else { - bNodeTree *ntree = !is_default ? wo->nodetree : EEVEE_shader_default_world_nodetree(wo); - mat = DRW_shader_create_from_world( - scene, wo, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred, NULL); - } - - MEM_SAFE_FREE(defines); - MEM_SAFE_FREE(vert); - MEM_SAFE_FREE(geom); - MEM_SAFE_FREE(frag); - - return mat; -} - -struct GPUMaterial *EEVEE_material_default_get(struct Scene *scene, Material *ma, int options) -{ - Material *def_ma = (ma && (options & VAR_MAT_VOLUME)) ? BKE_material_default_volume() : - BKE_material_default_surface(); - BLI_assert(def_ma->use_nodes && def_ma->nodetree); - - return eevee_material_get_ex(scene, def_ma, NULL, options, false); -} - -struct GPUMaterial *EEVEE_material_get( - EEVEE_Data *vedata, struct Scene *scene, Material *ma, World *wo, int options) -{ - if ((ma && (!ma->use_nodes || !ma->nodetree)) || (wo && (!wo->use_nodes || !wo->nodetree))) { - options |= VAR_DEFAULT; - } - - /* Meh, implicit option. World probe cannot be deferred because they need - * to be rendered immediately. */ - const bool deferred = (options & VAR_WORLD_PROBE) == 0; - - GPUMaterial *mat = eevee_material_get_ex(scene, ma, wo, options, deferred); - - int status = GPU_material_status(mat); - switch (status) { - case GPU_MAT_SUCCESS: - break; - case GPU_MAT_QUEUED: - vedata->stl->g_data->queued_shaders_count++; - mat = EEVEE_material_default_get(scene, ma, options); - break; - case GPU_MAT_FAILED: - default: - ma = EEVEE_material_default_error_get(); - mat = eevee_material_get_ex(scene, ma, NULL, options, false); - break; - } - /* Returned material should be ready to be drawn. */ - BLI_assert(GPU_material_status(mat) == GPU_MAT_SUCCESS); - return mat; -} - -void EEVEE_shaders_free(void) -{ - MEM_SAFE_FREE(e_data.surface_prepass_frag); - MEM_SAFE_FREE(e_data.surface_lit_frag); - MEM_SAFE_FREE(e_data.surface_geom_barycentric); - DRW_SHADER_FREE_SAFE(e_data.lookdev_background); - DRW_SHADER_FREE_SAFE(e_data.update_noise_sh); - DRW_SHADER_FREE_SAFE(e_data.color_copy_sh); - DRW_SHADER_FREE_SAFE(e_data.downsample_sh); - DRW_SHADER_FREE_SAFE(e_data.downsample_cube_sh); - DRW_SHADER_FREE_SAFE(e_data.minz_downlevel_sh); - DRW_SHADER_FREE_SAFE(e_data.maxz_downlevel_sh); - DRW_SHADER_FREE_SAFE(e_data.minz_downdepth_sh); - DRW_SHADER_FREE_SAFE(e_data.maxz_downdepth_sh); - DRW_SHADER_FREE_SAFE(e_data.minz_downdepth_layer_sh); - DRW_SHADER_FREE_SAFE(e_data.maxz_downdepth_layer_sh); - DRW_SHADER_FREE_SAFE(e_data.maxz_copydepth_layer_sh); - DRW_SHADER_FREE_SAFE(e_data.minz_copydepth_sh); - DRW_SHADER_FREE_SAFE(e_data.maxz_copydepth_sh); - DRW_SHADER_FREE_SAFE(e_data.ggx_lut_sh); - DRW_SHADER_FREE_SAFE(e_data.ggx_refraction_lut_sh); - DRW_SHADER_FREE_SAFE(e_data.mist_sh); - DRW_SHADER_FREE_SAFE(e_data.motion_blur_sh); - DRW_SHADER_FREE_SAFE(e_data.motion_blur_object_sh); - DRW_SHADER_FREE_SAFE(e_data.motion_blur_hair_sh); - DRW_SHADER_FREE_SAFE(e_data.velocity_tiles_sh); - DRW_SHADER_FREE_SAFE(e_data.velocity_tiles_expand_sh); - DRW_SHADER_FREE_SAFE(e_data.gtao_sh); - DRW_SHADER_FREE_SAFE(e_data.gtao_layer_sh); - DRW_SHADER_FREE_SAFE(e_data.gtao_debug_sh); - DRW_SHADER_FREE_SAFE(e_data.velocity_resolve_sh); - DRW_SHADER_FREE_SAFE(e_data.postprocess_sh); - DRW_SHADER_FREE_SAFE(e_data.shadow_sh); - DRW_SHADER_FREE_SAFE(e_data.shadow_accum_sh); - DRW_SHADER_FREE_SAFE(e_data.sss_sh[0]); - DRW_SHADER_FREE_SAFE(e_data.sss_sh[1]); - DRW_SHADER_FREE_SAFE(e_data.sss_sh[2]); - DRW_SHADER_FREE_SAFE(e_data.volumetric_clear_sh); - DRW_SHADER_FREE_SAFE(e_data.scatter_sh); - DRW_SHADER_FREE_SAFE(e_data.scatter_with_lights_sh); - DRW_SHADER_FREE_SAFE(e_data.volumetric_integration_sh); - DRW_SHADER_FREE_SAFE(e_data.volumetric_resolve_sh[0]); - DRW_SHADER_FREE_SAFE(e_data.volumetric_resolve_sh[1]); - DRW_SHADER_FREE_SAFE(e_data.volumetric_accum_sh); - DRW_SHADER_FREE_SAFE(e_data.probe_filter_glossy_sh); - DRW_SHADER_FREE_SAFE(e_data.probe_filter_diffuse_sh); - DRW_SHADER_FREE_SAFE(e_data.probe_filter_visibility_sh); - DRW_SHADER_FREE_SAFE(e_data.probe_grid_fill_sh); - DRW_SHADER_FREE_SAFE(e_data.probe_planar_downsample_sh); - DRW_SHADER_FREE_SAFE(e_data.studiolight_probe_sh); - DRW_SHADER_FREE_SAFE(e_data.studiolight_background_sh); - DRW_SHADER_FREE_SAFE(e_data.probe_grid_display_sh); - DRW_SHADER_FREE_SAFE(e_data.probe_cube_display_sh); - DRW_SHADER_FREE_SAFE(e_data.probe_planar_display_sh); - DRW_SHADER_FREE_SAFE(e_data.velocity_resolve_sh); - DRW_SHADER_FREE_SAFE(e_data.taa_resolve_sh); - DRW_SHADER_FREE_SAFE(e_data.taa_resolve_reproject_sh); - DRW_SHADER_FREE_SAFE(e_data.dof_bokeh_sh); - DRW_SHADER_FREE_SAFE(e_data.dof_setup_sh); - DRW_SHADER_FREE_SAFE(e_data.dof_flatten_tiles_sh); - DRW_SHADER_FREE_SAFE(e_data.dof_dilate_tiles_sh[0]); - DRW_SHADER_FREE_SAFE(e_data.dof_dilate_tiles_sh[1]); - DRW_SHADER_FREE_SAFE(e_data.dof_downsample_sh); - DRW_SHADER_FREE_SAFE(e_data.dof_reduce_sh[0]); - DRW_SHADER_FREE_SAFE(e_data.dof_reduce_sh[1]); - for (int i = 0; i < DOF_GATHER_MAX_PASS; i++) { - DRW_SHADER_FREE_SAFE(e_data.dof_gather_sh[i][0]); - DRW_SHADER_FREE_SAFE(e_data.dof_gather_sh[i][1]); - } - DRW_SHADER_FREE_SAFE(e_data.dof_filter_sh); - DRW_SHADER_FREE_SAFE(e_data.dof_scatter_sh[0][0]); - DRW_SHADER_FREE_SAFE(e_data.dof_scatter_sh[0][1]); - DRW_SHADER_FREE_SAFE(e_data.dof_scatter_sh[1][0]); - DRW_SHADER_FREE_SAFE(e_data.dof_scatter_sh[1][1]); - DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh[0][0]); - DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh[0][1]); - DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh[1][0]); - DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh[1][1]); - DRW_SHADER_FREE_SAFE(e_data.cryptomatte_sh[0]); - DRW_SHADER_FREE_SAFE(e_data.cryptomatte_sh[1]); - for (int i = 0; i < 2; i++) { - DRW_SHADER_FREE_SAFE(e_data.bloom_blit_sh[i]); - DRW_SHADER_FREE_SAFE(e_data.bloom_downsample_sh[i]); - DRW_SHADER_FREE_SAFE(e_data.bloom_upsample_sh[i]); - DRW_SHADER_FREE_SAFE(e_data.bloom_resolve_sh[i]); - } - DRW_SHADER_FREE_SAFE(e_data.reflection_trace); - DRW_SHADER_FREE_SAFE(e_data.reflection_resolve); - DRW_SHADER_LIB_FREE_SAFE(e_data.lib); - - if (e_data.default_world) { - BKE_id_free(NULL, e_data.default_world); - e_data.default_world = NULL; - } - if (e_data.glossy_mat) { - BKE_id_free(NULL, e_data.glossy_mat); - e_data.glossy_mat = NULL; - } - if (e_data.diffuse_mat) { - BKE_id_free(NULL, e_data.diffuse_mat); - e_data.diffuse_mat = NULL; - } - if (e_data.error_mat) { - BKE_id_free(NULL, e_data.error_mat); - e_data.error_mat = NULL; - } - if (e_data.surface.ntree) { - ntreeFreeEmbeddedTree(e_data.surface.ntree); - MEM_freeN(e_data.surface.ntree); - e_data.surface.ntree = NULL; - } - if (e_data.world.ntree) { - ntreeFreeEmbeddedTree(e_data.world.ntree); - MEM_freeN(e_data.world.ntree); - e_data.world.ntree = NULL; - } -} diff --git a/source/blender/draw/engines/eevee/eevee_shading.cc b/source/blender/draw/engines/eevee/eevee_shading.cc new file mode 100644 index 00000000000..53567ad4611 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_shading.cc @@ -0,0 +1,599 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Shading passes contain drawcalls specific to shading pipelines. + * They are to be shared across views. + * This file is only for shading passes. Other passes are declared in their own module. + */ + +#include "eevee_instance.hh" + +#include "eevee_shading.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Background Pass + * + * \{ */ + +void BackgroundPass::sync(GPUMaterial *gpumat, GPUTexture *lookdev_tx) +{ + DRWState state = DRW_STATE_WRITE_COLOR; + background_ps_ = DRW_pass_create("Background", state); + + /* Push a matrix at the same location as the camera. */ + mat4 camera_mat; + unit_m4(camera_mat); + copy_v3_v3(camera_mat[3], inst_.camera.data_get().viewinv[3]); + + DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, background_ps_); + if (lookdev_tx != nullptr) { + /* HACK(fclem) This particular texture has been left without resource to be set here. */ + DRW_shgroup_uniform_texture(grp, "samp0", lookdev_tx); + } + DRW_shgroup_call_obmat(grp, DRW_cache_fullscreen_quad_get(), camera_mat); +} + +void BackgroundPass::render(void) +{ + DRW_draw_pass(background_ps_); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Forward Pass + * + * NPR materials (using Closure to RGBA) or material using ALPHA_BLEND. + * \{ */ + +void ForwardPass::sync(void) +{ + { + DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; + prepass_ps_ = DRW_pass_create("Forward.Opaque.Prepass", state); + + state |= DRW_STATE_CULL_BACK; + prepass_culled_ps_ = DRW_pass_create("Forward.Opaque.Prepass.Culled", state); + + DRW_pass_link(prepass_ps_, prepass_culled_ps_); + } + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL; + opaque_ps_ = DRW_pass_create("Forward.Opaque", state); + + state |= DRW_STATE_CULL_BACK; + opaque_culled_ps_ = DRW_pass_create("Forward.Opaque.Culled", state); + + DRW_pass_link(opaque_ps_, opaque_culled_ps_); + } + { + DRWState state = DRW_STATE_DEPTH_LESS_EQUAL; + transparent_ps_ = DRW_pass_create("Forward.Transparent", state); + } +} + +DRWShadingGroup *ForwardPass::material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat) +{ + DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? opaque_culled_ps_ : opaque_ps_; + LightModule &lights = inst_.lights; + LightProbeModule &lightprobes = inst_.lightprobes; + eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT; + DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass); + lights.shgroup_resources(grp); + DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get()); + DRW_shgroup_uniform_block(grp, "grids_block", lightprobes.grid_ubo_get()); + DRW_shgroup_uniform_block(grp, "cubes_block", lightprobes.cube_ubo_get()); + DRW_shgroup_uniform_block(grp, "lightprobes_info_block", lightprobes.info_ubo_get()); + DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get()); + DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get()); + DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx); + /* TODO(fclem): Make this only needed if material uses it ... somehow. */ + if (true) { + DRW_shgroup_uniform_texture_ref( + grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get()); + } + if (true) { + DRW_shgroup_uniform_block(grp, "rt_diffuse_block", inst_.raytracing.diffuse_ubo_get()); + DRW_shgroup_uniform_block(grp, "rt_reflection_block", inst_.raytracing.reflection_ubo_get()); + DRW_shgroup_uniform_block(grp, "rt_refraction_block", inst_.raytracing.refraction_ubo_get()); + DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_radiance_tx_, no_interp); + } + if (true) { + DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.ubo_get()); + DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_tx_); + } + return grp; +} + +DRWShadingGroup *ForwardPass::prepass_opaque_add(::Material *blender_mat, GPUMaterial *gpumat) +{ + DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? prepass_culled_ps_ : + prepass_ps_; + DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass); + return grp; +} + +DRWShadingGroup *ForwardPass::material_transparent_add(::Material *blender_mat, + GPUMaterial *gpumat) +{ + LightModule &lights = inst_.lights; + LightProbeModule &lightprobes = inst_.lightprobes; + eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT; + DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, transparent_ps_); + lights.shgroup_resources(grp); + DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get()); + DRW_shgroup_uniform_block(grp, "grids_block", lightprobes.grid_ubo_get()); + DRW_shgroup_uniform_block(grp, "cubes_block", lightprobes.cube_ubo_get()); + DRW_shgroup_uniform_block(grp, "lightprobes_info_block", lightprobes.info_ubo_get()); + DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get()); + DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get()); + DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx); + /* TODO(fclem): Make this only needed if material uses it ... somehow. */ + if (true) { + DRW_shgroup_uniform_texture_ref( + grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get()); + } + if (true) { + DRW_shgroup_uniform_block(grp, "rt_diffuse_block", inst_.raytracing.diffuse_ubo_get()); + DRW_shgroup_uniform_block(grp, "rt_reflection_block", inst_.raytracing.reflection_ubo_get()); + DRW_shgroup_uniform_block(grp, "rt_refraction_block", inst_.raytracing.refraction_ubo_get()); + DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_radiance_tx_, no_interp); + } + if (true) { + DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.ubo_get()); + DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_tx_); + } + + DRWState state_disable = DRW_STATE_WRITE_DEPTH; + DRWState state_enable = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM; + if (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) { + state_enable |= DRW_STATE_CULL_BACK; + } + DRW_shgroup_state_disable(grp, state_disable); + DRW_shgroup_state_enable(grp, state_enable); + return grp; +} + +DRWShadingGroup *ForwardPass::prepass_transparent_add(::Material *blender_mat, GPUMaterial *gpumat) +{ + if ((blender_mat->blend_flag & MA_BL_HIDE_BACKFACE) == 0) { + return nullptr; + } + + DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, transparent_ps_); + + DRWState state_disable = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM; + DRWState state_enable = DRW_STATE_WRITE_DEPTH; + if (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) { + state_enable |= DRW_STATE_CULL_BACK; + } + DRW_shgroup_state_disable(grp, state_disable); + DRW_shgroup_state_enable(grp, state_enable); + return grp; +} + +void ForwardPass::render(const DRWView *view, + GBuffer &gbuffer, + HiZBuffer &hiz, + GPUFrameBuffer *view_fb) +{ + if (inst_.raytracing.enabled()) { + ivec2 extent = {GPU_texture_width(gbuffer.depth_tx), GPU_texture_height(gbuffer.depth_tx)}; + /* Reuse texture. */ + gbuffer.ray_radiance_tx.acquire_tmp(UNPACK2(extent), GPU_RGBA16F, gbuffer.owner); + /* Copy combined buffer so we can sample from it. */ + GPU_texture_copy(gbuffer.ray_radiance_tx, gbuffer.combined_tx); + + input_radiance_tx_ = gbuffer.ray_radiance_tx; + + hiz.prepare(gbuffer.depth_tx); + /* TODO(fclem): Avoid this if possible. */ + hiz.update(gbuffer.depth_tx); + + input_hiz_tx_ = hiz.texture_get(); + + GPU_framebuffer_bind(view_fb); + } + + DRW_stats_group_start("ForwardOpaque"); + DRW_draw_pass(prepass_ps_); + inst_.shadows.set_view(view, gbuffer.depth_tx); + DRW_draw_pass(opaque_ps_); + DRW_stats_group_end(); + + DRW_stats_group_start("ForwardTransparent"); + /* TODO(fclem) This is suboptimal. We could sort during sync. */ + /* FIXME(fclem) This wont work for panoramic, where we need + * to sort by distance to camera, not by z. */ + DRW_pass_sort_shgroup_z(transparent_ps_); + DRW_draw_pass(transparent_ps_); + DRW_stats_group_end(); + + if (inst_.raytracing.enabled()) { + gbuffer.ray_radiance_tx.release_tmp(); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name DeferredLayer + * \{ */ + +void DeferredLayer::sync(void) +{ + { + DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; + prepass_ps_ = DRW_pass_create("Gbuffer.Prepass", state); + + state |= DRW_STATE_CULL_BACK; + prepass_culled_ps_ = DRW_pass_create("Gbuffer.Prepass.Culled", state); + + DRW_pass_link(prepass_ps_, prepass_culled_ps_); + } + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_STENCIL_ALWAYS | + DRW_STATE_WRITE_STENCIL; + gbuffer_ps_ = DRW_pass_create("Gbuffer", state); + + state |= DRW_STATE_CULL_BACK; + gbuffer_culled_ps_ = DRW_pass_create("Gbuffer.Culled", state); + + DRW_pass_link(gbuffer_ps_, gbuffer_culled_ps_); + } + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | + DRW_STATE_CULL_BACK | DRW_STATE_STENCIL_ALWAYS | DRW_STATE_WRITE_STENCIL; + volume_ps_ = DRW_pass_create("VolumesHeterogeneous", state); + } +} + +DRWShadingGroup *DeferredLayer::material_add(::Material *blender_mat, GPUMaterial *gpumat) +{ + /* TODO/OPTI(fclem) Set the right mask for each effect based on gpumat flags. */ + uint stencil_mask = CLOSURE_DIFFUSE | CLOSURE_SSS | CLOSURE_REFLECTION | CLOSURE_TRANSPARENCY | + CLOSURE_EMISSION | CLOSURE_REFRACTION; + DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? gbuffer_culled_ps_ : + gbuffer_ps_; + DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass); + DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get()); + DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx); + DRW_shgroup_stencil_set(grp, stencil_mask, 0xFF, 0xFF); + return grp; +} + +DRWShadingGroup *DeferredLayer::prepass_add(::Material *blender_mat, GPUMaterial *gpumat) +{ + DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? prepass_culled_ps_ : + prepass_ps_; + DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass); + DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get()); + return grp; +} + +void DeferredLayer::volume_add(Object *ob) +{ + LightModule &lights = inst_.lights; + DeferredPass &deferred_pass = inst_.shading_passes.deferred; + + GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_VOLUME); + DRWShadingGroup *grp = DRW_shgroup_create(sh, volume_ps_); + lights.shgroup_resources(grp); + DRW_shgroup_uniform_texture_ref(grp, "depth_max_tx", &deferred_pass.input_depth_behind_tx_); + DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx); + DRW_shgroup_stencil_set(grp, CLOSURE_VOLUME | CLOSURE_TRANSPARENCY, 0xFF, 0xFF); + DRW_shgroup_call(grp, DRW_cache_cube_get(), ob); +} + +void DeferredLayer::render(const DRWView *view, + GBuffer &gbuffer, + HiZBuffer &hiz_front, + HiZBuffer &hiz_back, + RaytraceBuffer &rt_buffer, + GPUFrameBuffer *view_fb) +{ + DeferredPass &deferred_pass = inst_.shading_passes.deferred; + + const bool no_surfaces = DRW_pass_is_empty(gbuffer_ps_); + const bool no_volumes = DRW_pass_is_empty(volume_ps_); + if (no_surfaces && no_volumes) { + return; + } + /* TODO(fclem): detect these cases. */ + const bool use_diffuse = true; + const bool use_subsurface = true; + const bool use_transparency = true; + const bool use_holdout = true; + const bool use_refraction = true; + const bool use_glossy = true; + const bool use_ao = false; + + gbuffer.prepare((eClosureBits)0xFFFFFFFFu); + if (use_ao || use_glossy || use_diffuse) { + hiz_front.prepare(gbuffer.depth_tx); + } + if (use_refraction) { + hiz_back.prepare(gbuffer.depth_tx); + } + + update_pass_inputs(gbuffer, hiz_front, hiz_back); + + if (use_refraction) { + /* TODO(fclem) Only update if needed. + * i.e: No need when SSR from previous layer has already updated hiz. */ + hiz_back.update(gbuffer.depth_tx); + } + + gbuffer.bind(); + + if (!no_surfaces) { + DRW_draw_pass(prepass_ps_); + + /* TODO(fclem): Ambient Occlusion texture node. */ + if (use_ao) { + hiz_front.update(gbuffer.depth_tx); + gbuffer.bind(); + } + + DRW_draw_pass(gbuffer_ps_); + } + + inst_.shadows.set_view(view, gbuffer.depth_tx); + + if (!no_volumes) { + // gbuffer.copy_depth_behind(); + // deferred_pass.input_depth_behind_tx_ = gbuffer.depth_behind_tx; + + gbuffer.bind_volume(); + DRW_draw_pass(volume_ps_); + } + + if (use_holdout) { + gbuffer.bind_holdout(); + DRW_draw_pass(deferred_pass.eval_holdout_ps_); + } + + /* TODO(fclem) We could bypass update if ao already updated it and if there is no volume. */ + hiz_front.update(gbuffer.depth_tx); + + if (!no_surfaces && use_refraction) { + /* Launch and shade refraction rays before transparency changes the combined pass. */ + rt_buffer.trace(CLOSURE_REFRACTION, gbuffer, hiz_back, hiz_front); + } + + GPU_framebuffer_bind(view_fb); + if (use_transparency) { + DRW_draw_pass(deferred_pass.eval_transparency_ps_); + } + + gbuffer.clear_radiance(); + + if (!no_surfaces && use_refraction) { + rt_buffer.denoise(CLOSURE_REFRACTION); + rt_buffer.resolve(CLOSURE_REFRACTION, gbuffer); + } + + if (!no_volumes) { + /* TODO(fclem) volume fb. */ + GPU_framebuffer_bind(view_fb); + DRW_draw_pass(deferred_pass.eval_volume_homogeneous_ps_); + } + + if (!no_surfaces) { + gbuffer.bind_radiance(); + DRW_draw_pass(deferred_pass.eval_direct_ps_); + + if (use_diffuse) { + rt_buffer.trace(CLOSURE_DIFFUSE, gbuffer, hiz_front, hiz_front); + rt_buffer.denoise(CLOSURE_DIFFUSE); + rt_buffer.resolve(CLOSURE_DIFFUSE, gbuffer); + } + + if (use_subsurface) { + GPU_framebuffer_bind(view_fb); + DRW_draw_pass(deferred_pass.eval_subsurface_ps_); + } + + if (use_glossy) { + rt_buffer.trace(CLOSURE_REFLECTION, gbuffer, hiz_front, hiz_front); + rt_buffer.denoise(CLOSURE_REFLECTION); + rt_buffer.resolve(CLOSURE_REFLECTION, gbuffer); + } + } +} + +void DeferredLayer::update_pass_inputs(GBuffer &gbuffer, HiZBuffer &hiz_front, HiZBuffer &hiz_back) +{ + DeferredPass &deferred_pass = inst_.shading_passes.deferred; + deferred_pass.input_combined_tx_ = gbuffer.combined_tx; + deferred_pass.input_emission_data_tx_ = gbuffer.emission_tx; + deferred_pass.input_transmit_color_tx_ = gbuffer.transmit_color_tx; + deferred_pass.input_transmit_normal_tx_ = gbuffer.transmit_normal_tx; + deferred_pass.input_transmit_data_tx_ = gbuffer.transmit_data_tx; + deferred_pass.input_reflect_color_tx_ = gbuffer.reflect_color_tx; + deferred_pass.input_reflect_normal_tx_ = gbuffer.reflect_normal_tx; + deferred_pass.input_diffuse_tx_ = gbuffer.diffuse_tx; + deferred_pass.input_transparency_data_tx_ = gbuffer.transparency_tx; + deferred_pass.input_volume_data_tx_ = gbuffer.volume_tx; + deferred_pass.input_hiz_front_tx_ = hiz_front.texture_get(); + deferred_pass.input_hiz_back_tx_ = hiz_back.texture_get(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name DeferredLayer + * \{ */ + +void DeferredPass::sync(void) +{ + opaque_layer_.sync(); + refraction_layer_.sync(); + volumetric_layer_.sync(); + + LightModule &lights = inst_.lights; + LightProbeModule &lightprobes = inst_.lightprobes; + + eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT; + + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_ADD_FULL; + eval_direct_ps_ = DRW_pass_create("DeferredDirect", state); + GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_EVAL_DIRECT); + DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_direct_ps_); + lights.shgroup_resources(grp); + DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get()); + DRW_shgroup_uniform_block(grp, "grids_block", lightprobes.grid_ubo_get()); + DRW_shgroup_uniform_block(grp, "cubes_block", lightprobes.cube_ubo_get()); + DRW_shgroup_uniform_block(grp, "lightprobes_info_block", lightprobes.info_ubo_get()); + DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get()); + DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get()); + DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx); + DRW_shgroup_uniform_texture_ref_ex( + grp, "emission_data_tx", &input_emission_data_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex( + grp, "transmit_color_tx", &input_transmit_color_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex( + grp, "transmit_normal_tx", &input_transmit_normal_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex( + grp, "transmit_data_tx", &input_transmit_data_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex( + grp, "reflect_color_tx", &input_reflect_color_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex( + grp, "reflect_normal_tx", &input_reflect_normal_tx_, no_interp); + DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_front_tx_); + DRW_shgroup_uniform_texture_ref( + grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get()); + DRW_shgroup_stencil_set( + grp, 0x0, 0x0, CLOSURE_DIFFUSE | CLOSURE_REFLECTION | CLOSURE_EMISSION); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + } + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL | DRW_STATE_BLEND_ADD_FULL; + eval_subsurface_ps_ = DRW_pass_create("DeferredSubsurface", state); + GPUShader *sh = inst_.shaders.static_shader_get(SUBSURFACE_EVAL); + DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_subsurface_ps_); + DRW_shgroup_uniform_block(grp, "subsurface_block", inst_.subsurface.ubo_get()); + DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get()); + DRW_shgroup_uniform_block(grp, "hiz_block", inst_.hiz.ubo_get()); + DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_front_tx_); + DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_diffuse_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex( + grp, "transmit_color_tx", &input_transmit_color_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex( + grp, "transmit_normal_tx", &input_transmit_normal_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex( + grp, "transmit_data_tx", &input_transmit_data_tx_, no_interp); + DRW_shgroup_stencil_set(grp, 0x0, 0xFF, CLOSURE_SSS); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + } + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_ADD_FULL; + eval_volume_homogeneous_ps_ = DRW_pass_create("DeferredVolume", state); + GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_EVAL_VOLUME); + DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_volume_homogeneous_ps_); + lights.shgroup_resources(grp); + DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.shading_passes.utility_tx); + DRW_shgroup_uniform_texture_ref_ex( + grp, "transparency_data_tx", &input_transparency_data_tx_, no_interp); + DRW_shgroup_uniform_texture_ref_ex(grp, "volume_data_tx", &input_volume_data_tx_, no_interp); + DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &input_hiz_front_tx_); + DRW_shgroup_stencil_set(grp, 0x0, 0x0, CLOSURE_VOLUME); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + } + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_MUL; + eval_transparency_ps_ = DRW_pass_create("DeferredTransparency", state); + GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_EVAL_TRANSPARENT); + DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_transparency_ps_); + DRW_shgroup_uniform_texture_ref(grp, "transparency_data_tx", &input_transparency_data_tx_); + DRW_shgroup_uniform_texture_ref_ex(grp, "volume_data_tx", &input_volume_data_tx_, no_interp); + DRW_shgroup_stencil_set(grp, 0x0, 0x0, CLOSURE_TRANSPARENCY); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + } + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL; + eval_holdout_ps_ = DRW_pass_create("DeferredHoldout", state); + GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_EVAL_HOLDOUT); + DRWShadingGroup *grp = DRW_shgroup_create(sh, eval_volume_homogeneous_ps_); + DRW_shgroup_uniform_texture_ref(grp, "combined_tx", &input_combined_tx_); + DRW_shgroup_uniform_texture_ref(grp, "transparency_data_tx", &input_transparency_data_tx_); + DRW_shgroup_stencil_set(grp, 0x0, 0x0, CLOSURE_TRANSPARENCY); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + } +} + +DRWShadingGroup *DeferredPass::material_add(::Material *material, GPUMaterial *gpumat) +{ + if (material->blend_flag & MA_BL_SS_REFRACTION) { + return refraction_layer_.material_add(material, gpumat); + } + else { + return opaque_layer_.material_add(material, gpumat); + } +} + +DRWShadingGroup *DeferredPass::prepass_add(::Material *material, GPUMaterial *gpumat) +{ + if (material->blend_flag & MA_BL_SS_REFRACTION) { + return refraction_layer_.prepass_add(material, gpumat); + } + else { + return opaque_layer_.prepass_add(material, gpumat); + } +} + +void DeferredPass::volume_add(Object *ob) +{ + volumetric_layer_.volume_add(ob); +} + +void DeferredPass::render(const DRWView *drw_view, + GBuffer &gbuffer, + HiZBuffer &hiz_front, + HiZBuffer &hiz_back, + RaytraceBuffer &rt_buffer_opaque_, + RaytraceBuffer &rt_buffer_refract_, + GPUFrameBuffer *view_fb) +{ + DRW_stats_group_start("OpaqueLayer"); + opaque_layer_.render(drw_view, gbuffer, hiz_front, hiz_back, rt_buffer_opaque_, view_fb); + DRW_stats_group_end(); + + DRW_stats_group_start("RefractionLayer"); + refraction_layer_.render(drw_view, gbuffer, hiz_front, hiz_back, rt_buffer_refract_, view_fb); + DRW_stats_group_end(); + + /* NOTE(fclem): Reuse the same rtbuffer as refraction but should not use it. */ + DRW_stats_group_start("VolumetricLayer"); + volumetric_layer_.render(drw_view, gbuffer, hiz_front, hiz_back, rt_buffer_refract_, view_fb); + DRW_stats_group_end(); + + gbuffer.render_end(); + rt_buffer_opaque_.render_end(drw_view); + rt_buffer_refract_.render_end(drw_view); +} + +/** \} */ + +} // namespace blender::eevee
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/eevee_shading.hh b/source/blender/draw/engines/eevee/eevee_shading.hh new file mode 100644 index 00000000000..132db6bd334 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_shading.hh @@ -0,0 +1,328 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Shading passes contain drawcalls specific to shading pipelines. + * They are to be shared across views. + * This file is only for shading passes. Other passes are declared in their own module. + */ + +#pragma once + +#include "DRW_render.h" + +#include "eevee_lut.h" + +#include "eevee_gbuffer.hh" +#include "eevee_raytracing.hh" +#include "eevee_shadow.hh" +#include "eevee_velocity.hh" + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name Background Pass + * + * Render world values. + * \{ */ + +class BackgroundPass { + private: + Instance &inst_; + + DRWPass *background_ps_ = nullptr; + + public: + BackgroundPass(Instance &inst) : inst_(inst){}; + + void sync(GPUMaterial *gpumat, GPUTexture *loodev_tx = nullptr); + void render(void); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Forward Pass + * + * Handles alpha blended surfaces and NPR materials (using Closure to RGBA). + * \{ */ + +class ForwardPass { + private: + Instance &inst_; + + DRWPass *prepass_ps_ = nullptr; + DRWPass *prepass_culled_ps_ = nullptr; + DRWPass *opaque_ps_ = nullptr; + DRWPass *opaque_culled_ps_ = nullptr; + DRWPass *transparent_ps_ = nullptr; + + GPUTexture *input_hiz_tx_ = nullptr; + GPUTexture *input_radiance_tx_ = nullptr; + + public: + ForwardPass(Instance &inst) : inst_(inst){}; + + void sync(void); + + DRWShadingGroup *material_add(::Material *blender_mat, GPUMaterial *gpumat) + { + return (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) ? + material_transparent_add(blender_mat, gpumat) : + material_opaque_add(blender_mat, gpumat); + } + + DRWShadingGroup *prepass_add(::Material *blender_mat, GPUMaterial *gpumat) + { + return (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) ? + prepass_transparent_add(blender_mat, gpumat) : + prepass_opaque_add(blender_mat, gpumat); + } + + DRWShadingGroup *material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat); + DRWShadingGroup *prepass_opaque_add(::Material *blender_mat, GPUMaterial *gpumat); + DRWShadingGroup *material_transparent_add(::Material *blender_mat, GPUMaterial *gpumat); + DRWShadingGroup *prepass_transparent_add(::Material *blender_mat, GPUMaterial *gpumat); + + void render(const DRWView *view, GBuffer &gbuffer, HiZBuffer &hiz, GPUFrameBuffer *view_fb); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Deferred lighting. + * \{ */ + +class DeferredLayer { + private: + Instance &inst_; + + /* TODO */ + // GPUTexture *input_emission_data_tx_ = nullptr; + // GPUTexture *input_diffuse_data_tx_ = nullptr; + // GPUTexture *input_depth_tx_ = nullptr; + + DRWPass *prepass_ps_ = nullptr; + DRWPass *prepass_culled_ps_ = nullptr; + DRWPass *gbuffer_ps_ = nullptr; + DRWPass *gbuffer_culled_ps_ = nullptr; + DRWPass *volume_ps_ = nullptr; + + public: + DeferredLayer(Instance &inst) : inst_(inst){}; + + void sync(void); + DRWShadingGroup *material_add(::Material *blender_mat, GPUMaterial *gpumat); + DRWShadingGroup *prepass_add(::Material *blender_mat, GPUMaterial *gpumat); + void volume_add(Object *ob); + void render(const DRWView *view, + GBuffer &gbuffer, + HiZBuffer &hiz_front, + HiZBuffer &hiz_back, + RaytraceBuffer &rtbuffer, + GPUFrameBuffer *view_fb); + + private: + void update_pass_inputs(GBuffer &gbuffer, HiZBuffer &hiz_front, HiZBuffer &hiz_back); +}; + +class DeferredPass { + friend DeferredLayer; + + private: + Instance &inst_; + + /* Gbuffer filling passes. We could have an arbitrary number of them but for now we just have + * a hardcoded number of them. */ + DeferredLayer opaque_layer_; + DeferredLayer refraction_layer_; + DeferredLayer volumetric_layer_; + + DRWPass *eval_direct_ps_ = nullptr; + DRWPass *eval_subsurface_ps_ = nullptr; + DRWPass *eval_transparency_ps_ = nullptr; + DRWPass *eval_holdout_ps_ = nullptr; + // DRWPass *eval_volume_heterogeneous_ps_ = nullptr; + DRWPass *eval_volume_homogeneous_ps_ = nullptr; + + /* References only. */ + GPUTexture *input_combined_tx_ = nullptr; + GPUTexture *input_depth_behind_tx_ = nullptr; + GPUTexture *input_diffuse_tx_ = nullptr; + GPUTexture *input_emission_data_tx_ = nullptr; + GPUTexture *input_hiz_front_tx_ = nullptr; + GPUTexture *input_hiz_back_tx_ = nullptr; + GPUTexture *input_reflect_color_tx_ = nullptr; + GPUTexture *input_reflect_normal_tx_ = nullptr; + GPUTexture *input_transmit_color_tx_ = nullptr; + GPUTexture *input_transmit_data_tx_ = nullptr; + GPUTexture *input_transmit_normal_tx_ = nullptr; + GPUTexture *input_transparency_data_tx_ = nullptr; + GPUTexture *input_volume_data_tx_ = nullptr; + // GPUTexture *input_volume_radiance_tx_ = nullptr; + // GPUTexture *input_volume_transmittance_tx_ = nullptr; + + public: + DeferredPass(Instance &inst) + : inst_(inst), opaque_layer_(inst), refraction_layer_(inst), volumetric_layer_(inst){}; + + void sync(void); + DRWShadingGroup *material_add(::Material *material, GPUMaterial *gpumat); + DRWShadingGroup *prepass_add(::Material *material, GPUMaterial *gpumat); + void volume_add(Object *ob); + void render(const DRWView *drw_view, + GBuffer &gbuffer, + HiZBuffer &hiz_front, + HiZBuffer &hiz_back, + RaytraceBuffer &rtbuffer_opaque, + RaytraceBuffer &rtbuffer_refract, + GPUFrameBuffer *view_fb); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Utility texture + * + * 64x64 2D array texture containing LUT tables and blue noises. + * \{ */ + +class UtilityTexture : public Texture { + struct Layer { + float data[UTIL_TEX_SIZE * UTIL_TEX_SIZE][4]; + }; + + static constexpr int lut_size = UTIL_TEX_SIZE; + static constexpr int lut_size_sqr = lut_size * lut_size; + static constexpr int layer_count = 4 + UTIL_BTDF_LAYER_COUNT; + + public: + UtilityTexture() + : Texture("UtilityTx", lut_size, lut_size, layer_count, 1, GPU_RGBA16F, nullptr, true) + { +#ifdef RUNTIME_LUT_CREATION + float *bsdf_ggx_lut = EEVEE_lut_update_ggx_brdf(lut_size); + float(*btdf_ggx_lut)[lut_size_sqr * 2] = (float(*)[lut_size_sqr * 2]) + EEVEE_lut_update_ggx_btdf(lut_size, UTIL_BTDF_LAYER_COUNT); +#else + const float *bsdf_ggx_lut = bsdf_split_sum_ggx; + const float(*btdf_ggx_lut)[lut_size_sqr * 2] = btdf_split_sum_ggx; +#endif + + Vector<Layer> data(layer_count); + { + Layer &layer = data[UTIL_BLUE_NOISE_LAYER]; + memcpy(layer.data, blue_noise, sizeof(layer)); + } + { + Layer &layer = data[UTIL_LTC_MAT_LAYER]; + memcpy(layer.data, ltc_mat_ggx, sizeof(layer)); + } + { + Layer &layer = data[UTIL_LTC_MAG_LAYER]; + for (auto i : IndexRange(lut_size_sqr)) { + layer.data[i][0] = bsdf_ggx_lut[i * 2 + 0]; + layer.data[i][1] = bsdf_ggx_lut[i * 2 + 1]; + layer.data[i][2] = ltc_mag_ggx[i * 2 + 0]; + layer.data[i][3] = ltc_mag_ggx[i * 2 + 1]; + } + BLI_assert(UTIL_LTC_MAG_LAYER == UTIL_BSDF_LAYER); + } + { + Layer &layer = data[UTIL_DISK_INTEGRAL_LAYER]; + for (auto i : IndexRange(lut_size_sqr)) { + layer.data[i][UTIL_DISK_INTEGRAL_COMP] = ltc_disk_integral[i]; + } + } + { + for (auto layer_id : IndexRange(16)) { + Layer &layer = data[3 + layer_id]; + for (auto i : IndexRange(lut_size_sqr)) { + layer.data[i][0] = btdf_ggx_lut[layer_id][i * 2 + 0]; + layer.data[i][1] = btdf_ggx_lut[layer_id][i * 2 + 1]; + } + } + } + GPU_texture_update_mipmap(*this, 0, GPU_DATA_FLOAT, data.data()); + } + + ~UtilityTexture(){}; +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name ShadingPasses + * + * \{ */ + +/** + * Shading passes. Shared between views. Objects will subscribe to one of them. + */ +class ShadingPasses { + public: + BackgroundPass background; + DeferredPass deferred; + ForwardPass forward; + ShadowPass shadow; + VelocityPass velocity; + + UtilityTexture utility_tx; + + public: + ShadingPasses(Instance &inst) + : background(inst), deferred(inst), forward(inst), shadow(inst), velocity(inst){}; + + void sync() + { + deferred.sync(); + forward.sync(); + shadow.sync(); + velocity.sync(); + } + + DRWShadingGroup *material_add(::Material *blender_mat, + GPUMaterial *gpumat, + eMaterialPipeline pipeline_type) + { + switch (pipeline_type) { + case MAT_PIPE_DEFERRED_PREPASS: + return deferred.prepass_add(blender_mat, gpumat); + case MAT_PIPE_FORWARD_PREPASS: + return forward.prepass_add(blender_mat, gpumat); + case MAT_PIPE_DEFERRED: + return deferred.material_add(blender_mat, gpumat); + case MAT_PIPE_FORWARD: + return forward.material_add(blender_mat, gpumat); + case MAT_PIPE_VOLUME: + /* TODO(fclem) volume pass. */ + return nullptr; + case MAT_PIPE_SHADOW: + return shadow.material_add(blender_mat, gpumat); + } + return nullptr; + } +}; + +/** \} */ + +} // namespace blender::eevee
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/eevee_shadow.cc b/source/blender/draw/engines/eevee/eevee_shadow.cc new file mode 100644 index 00000000000..06306ff770a --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_shadow.cc @@ -0,0 +1,1201 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * The shadow module manages shadow update tagging & shadow rendering. + */ + +#include "BKE_global.h" +#include "BLI_rect.h" + +#include "eevee_instance.hh" + +#include "draw_manager_text.h" + +#include <iostream> + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Tile map + * + * \{ */ + +void ShadowTileMap::sync_clipmap(const float3 &camera_position, + const float4x4 &object_mat_, + float near_, + float far_, + int2 new_offset, + int clipmap_level) +{ +#ifdef SHADOW_DEBUG_NO_CACHING + set_dirty(); +#endif + if (is_cubeface || (level != clipmap_level) || (near != near_) || (far != far_)) { + set_dirty(); + } + is_cubeface = false; + level = clipmap_level; + near = near_; + far = far_; + cone_direction = float3(1.0f); + cone_angle_cos = -2.0f; + + if (grid_shift == int2(0)) { + /* Only replace shift if it is not already dirty. */ + grid_shift = new_offset - grid_offset; + } + grid_offset = new_offset; + + if (!equals_m4m4(object_mat.ptr(), object_mat_.ptr())) { + object_mat = object_mat_; + set_dirty(); + } + + float half_size = tilemap_coverage_get() / 2.0f; + float tile_size = tile_size_get(); + float3 tilemap_center = object_mat * + float3(grid_offset.x * tile_size, grid_offset.y * tile_size, 0.0f); + + float4x4 viewinv = object_mat; + copy_v3_v3(viewinv.values[3], tilemap_center); + + float camera_distance_to_plane = math::dot(float3(object_mat.values[2]), camera_position); + float visible_near = camera_distance_to_plane - half_size; + float visible_far = camera_distance_to_plane + half_size; + + /* Update corners. Used for visibility test of each tile. */ + *(float3 *)(&corners[0]) = viewinv * float3(-half_size, -half_size, visible_near); + *(float3 *)(&corners[1]) = viewinv * float3(half_size, -half_size, visible_near); + *(float3 *)(&corners[2]) = viewinv * float3(-half_size, half_size, visible_near); + *(float3 *)(&corners[3]) = viewinv * float3(-half_size, -half_size, visible_far); + /* Store deltas. */ + corners[1] = (corners[1] - corners[0]) / float(SHADOW_TILEMAP_RES); + corners[2] = (corners[2] - corners[0]) / float(SHADOW_TILEMAP_RES); + corners[3] -= corners[0]; + + /* Usage depth range. Used for usage tagging. */ + float range = (far - near); + /* Need to be after the corners arithmetic because they are stored inside the last component. */ + _min_usage_depth = clamp_f((2.0f * (-visible_far - near) / range) - 1.0f, -1.0f, 1.0f); + _max_usage_depth = clamp_f((2.0f * (-visible_near - near) / range) - 1.0f, -1.0f, 1.0f); + + viewmat = viewinv.inverted_affine(); + winmat = winmat_get(nullptr); + mul_m4_m4m4(tilemat, tilemat_scale_bias_mat, winmat.ptr()); + mul_m4_m4m4(tilemat, tilemat, viewmat.ptr()); +} + +void ShadowTileMap::sync_cubeface( + const float4x4 &object_mat_, float near_, float far_, float cone_aperture, eCubeFace face) +{ +#ifdef SHADOW_DEBUG_NO_CACHING + set_dirty(); +#endif + if (!is_cubeface || (cubeface != face) || (near != near_) || (far != far_)) { + set_dirty(); + } + is_cubeface = true; + cubeface = face; + near = near_; + far = far_; + + if (cone_aperture > DEG2RADF(180.0f)) { + cone_angle_cos = -2.0; + } + else { + cone_angle_cos = cosf(min_ff((cone_aperture * 0.5f) + tile_cone_half_angle, M_PI_2)); + } + cone_direction = -float3(object_mat_.values[2]); + + if (!equals_m4m4(object_mat.ptr(), object_mat_.ptr())) { + object_mat = object_mat_; + set_dirty(); + } + + viewmat = float4x4(shadow_face_mat[cubeface]) * object_mat.inverted_affine(); + winmat = winmat_get(nullptr); + mul_m4_m4m4(tilemat, tilemat_scale_bias_mat, winmat.ptr()); + mul_m4_m4m4(tilemat, tilemat, viewmat.ptr()); + + /* Update corners. */ + float4x4 viewinv = viewmat.inverted(); + *reinterpret_cast<float3 *>(&corners[0]) = viewinv.translation(); + *reinterpret_cast<float3 *>(&corners[1]) = viewinv * float3(-far, -far, -far); + *reinterpret_cast<float3 *>(&corners[2]) = viewinv * float3(far, -far, -far); + *reinterpret_cast<float3 *>(&corners[3]) = viewinv * float3(-far, far, -far); + /* Store deltas. */ + corners[2] = (corners[2] - corners[1]) / float(SHADOW_TILEMAP_RES); + corners[3] = (corners[3] - corners[1]) / float(SHADOW_TILEMAP_RES); + /* Need to be after the corners arithmetic because they are stored inside the last component. */ + _min_usage_depth = -1.0f; + _max_usage_depth = 1.0f; + _punctual_distance = far_; +} + +float4x4 ShadowTileMap::winmat_get(const rcti *tile_minmax) const +{ + float2 min = float2(-1.0f); + float2 max = float2(1.0f); + + if (tile_minmax != nullptr) { + min = shadow_tile_coord_to_ndc(int2(tile_minmax->xmin, tile_minmax->ymin)); + max = shadow_tile_coord_to_ndc(int2(tile_minmax->xmax, tile_minmax->ymax)); + } + + float4x4 winmat; + if (is_cubeface) { + perspective_m4( + winmat.ptr(), near * min.x, near * max.x, near * min.y, near * max.y, near, far); + } + else { + float half_size = tilemap_coverage_get() / 2.0f; + orthographic_m4(winmat.ptr(), + half_size * min.x, + half_size * max.x, + half_size * min.y, + half_size * max.y, + near, + far); + } + return winmat; +} + +void ShadowTileMap::setup_view(const rcti &rect, DRWView *&view) const +{ + float4x4 culling_mat = winmat_get(&rect); + + if (view == nullptr) { + view = DRW_view_create(viewmat.ptr(), winmat.ptr(), nullptr, culling_mat.ptr(), nullptr); + } + else { + DRW_view_update(view, viewmat.ptr(), winmat.ptr(), nullptr, culling_mat.ptr()); + } + +#if 0 /* Debug. */ + float4 debug_color[6] = { + {1, .1, .1, 1}, {.1, 1, .1, 1}, {0, .2, 1, 1}, {1, 1, .3, 1}, {.1, .1, .1, 1}, {1, 1, 1, 1}}; + float4 color = debug_color[((is_cubeface ? cubeface : level) + 9999) % 6]; + float4x4 persinv_culling = (culling_mat * viewmat).inverted(); + DRW_debug_m4_as_bbox(persinv_culling.values, color, false); +#endif +} + +void ShadowTileMap::debug_draw(void) const +{ + /** Used for debug drawing. */ + float4 debug_color[6] = { + {1, .1, .1, 1}, {.1, 1, .1, 1}, {0, .2, 1, 1}, {1, 1, .3, 1}, {.1, .1, .1, 1}, {1, 1, 1, 1}}; + float4 color = debug_color[((is_cubeface ? cubeface : level) + 9999) % 6]; + + float4x4 winmat = winmat_get(nullptr); + float persinv[4][4]; + mul_m4_m4m4(persinv, winmat.ptr(), viewmat.ptr()); + invert_m4(persinv); + DRW_debug_m4_as_bbox(persinv, color, false); + + int64_t div = ShadowTileAllocator::maps_per_row; + std::stringstream ss; + ss << "[" << index % div << ":" << index / div << "]"; + std::string text = ss.str(); + + float3 pos = float3(0.0f, 0.0f, (is_cubeface) ? 1.0f : 0.0f); + mul_project_m4_v3(persinv, pos); + + uchar ucolor[4]; + rgba_float_to_uchar(ucolor, color); + struct DRWTextStore *dt = DRW_text_cache_ensure(); + DRW_text_cache_add(dt, pos, text.c_str(), text.size(), 0, 0, DRW_TEXT_CACHE_GLOBALSPACE, ucolor); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Tile map allocator + * + * \{ */ + +ShadowTileAllocator::ShadowTileAllocator() +{ + for (auto &bit : usage_bitmap_) { + bit = false; + } + + /* Try to not have a very long texture since it is + * potentially wasteful on most GPU using tiled memory. */ + int tilemap_res = ShadowTileMap::tile_map_resolution; + int2 extent; + extent.x = min_ii(size, maps_per_row) * tilemap_res; + extent.y = (size / maps_per_row) * tilemap_res; + /* Add half the height for LODs. */ + extent.y += extent.y / 2; + + tilemap_tx.ensure(UNPACK2(extent), 1, GPU_R32UI); + tilemap_tx.clear((uint)0); + + /* Allocate one pixel for each tilemap and each lod. */ + tilemap_rects_tx.ensure(SHADOW_TILEMAP_LOD + 1, size, 1, GPU_RGBA32I); + tilemap_rects_tx.clear((int)0); +} + +ShadowTileAllocator::~ShadowTileAllocator() +{ + for (ShadowTileMap *map : maps) { + delete map; + } +} + +/** Returns empty span on failure. */ +Span<ShadowTileMap *> ShadowTileAllocator::alloc(int64_t count) +{ + int64_t candidate = -1; + /* Iterate through the whole buffer starting from the last known alloc position. */ + for (int64_t j = 0; j < size; j++) { + int64_t i = (next_index + j) % size; + if (usage_bitmap_[i] == false) { + if (candidate == -1) { + candidate = i; + } + if (i - candidate + 1 == count) { + int64_t start = maps.size(); + for (auto j : IndexRange(candidate, count)) { + usage_bitmap_[j] = true; + maps.append(new ShadowTileMap(j)); + } + next_index = candidate + count; + return maps.as_span().slice(IndexRange(start, count)); + } + } + else { + candidate = -1; + } + } + return Span<ShadowTileMap *>(); +} + +void ShadowTileAllocator::free(Vector<ShadowTileMap *> &free_list) +{ + for (ShadowTileMap *map : free_list) { + maps.remove_first_occurrence_and_reorder(map); + usage_bitmap_[map->index] = false; + maps_deleted.append(map); + /* Actual deletion happens in end_sync(). */ + } + free_list.clear(); +} + +void ShadowTileAllocator::end_sync() +{ + active_maps_len = 0; + for (ShadowTileMap *map : maps) { + tilemaps_data[active_maps_len++] = *map; + } + + deleted_maps_len = 0; + for (ShadowTileMap *map : maps_deleted) { + /* Push to the ShadowTileMapsDataBuf in order to release the tiles. + * Only do that if the slot was not reused for another map. */ + if (usage_bitmap_[map->index] == false) { + /* This will effectively release all pages since they will be marked to update but not + * marked as visible. */ + map->set_dirty(); + tilemaps_data[active_maps_len + deleted_maps_len++] = *map; + } + delete map; + } + maps_deleted.clear(); + + tilemaps_data.push_update(); + + for (ShadowTileMap *map : maps) { + map->set_updated(); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Shadow Common + * + * \{ */ + +void ShadowCommon::free_resources() +{ + shadows_->tilemap_allocator.free(tilemaps); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Shadow Punctual + * + * \{ */ + +void ShadowPunctual::sync(eLightType light_type, + const mat4 &object_mat, + float cone_aperture, + float near_clip, + float far_clip, + float bias) +{ + bool is_wide_cone = cone_aperture > DEG2RADF(90.0f); + bool is_omni = cone_aperture > DEG2RADF(180.0f); + + far_ = max_ff(far_clip, 3e-4f); + near_ = min_ff(near_clip, far_clip - 1e-4f); + bias_ = bias; + light_type_ = light_type; + + /* Keep custom data. */ + size_x_ = _area_size_x; + size_y_ = _area_size_y; + + position_ = float3(object_mat[3]); + random_offset_ = float3(0.0f); + + int face_needed = is_omni ? 6 : (is_wide_cone ? 5 : 1); + if (tilemaps.size() != face_needed) { + shadows_->tilemap_allocator.free(tilemaps); + tilemaps = shadows_->tilemap_allocator.alloc(face_needed); + } + + /* Clear embedded custom data. */ + float4x4 obmat_tmp = float4x4(object_mat); + obmat_tmp.values[0][3] = obmat_tmp.values[1][3] = obmat_tmp.values[2][3] = 0.0f; + obmat_tmp.values[3][3] = 1.0f; + + tilemaps[Z_NEG]->sync_cubeface(obmat_tmp, near_, far_, cone_aperture, Z_NEG); + if (is_wide_cone) { + tilemaps[X_POS]->sync_cubeface(obmat_tmp, near_, far_, cone_aperture, X_POS); + tilemaps[X_NEG]->sync_cubeface(obmat_tmp, near_, far_, cone_aperture, X_NEG); + tilemaps[Y_POS]->sync_cubeface(obmat_tmp, near_, far_, cone_aperture, Y_POS); + tilemaps[Y_NEG]->sync_cubeface(obmat_tmp, near_, far_, cone_aperture, Y_NEG); + } + if (is_omni) { + tilemaps[Z_POS]->sync_cubeface(obmat_tmp, near_, far_, cone_aperture, Z_POS); + } +} + +ShadowPunctual::operator ShadowData() +{ + ShadowData data; + cubeface_winmat_get(data.mat, near_, far_); + invert_m4(data.mat); + data.offset = random_offset_; + data.bias = bias_; + data.clip_near = near_; + data.clip_far = far_; + data.tilemap_index = tilemaps.first()->index; + data.tilemap_last = data.tilemap_index + tilemaps.size() - 1; + return data; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Directional Shadow Maps + * + * \{ */ + +void ShadowDirectional::sync(const mat4 &object_mat, float bias, float min_resolution) +{ + object_mat_ = float4x4(object_mat); + /* Clear embedded custom data. */ + object_mat_.values[0][3] = object_mat_.values[1][3] = object_mat_.values[2][3] = 0.0f; + object_mat_.values[3][3] = 1.0f; + /* Remove translation. */ + zero_v3(object_mat_.values[3]); + + min_resolution_ = min_resolution; + bias_ = bias; +} + +void ShadowDirectional::end_sync(int min_level, + int max_level, + const float3 &camera_position, + const AABB &casters_bounds) +{ + int user_min_level = floorf(log2(min_resolution_)); + + /* FIXME(fclem): We center the clipmap around the camera position which is arbitrary and + * can affect lightprobe shadowing quality. To fix, just change camera position during bake and + * profit!!! */ + + float3 z_axis = float3(object_mat_.values[2]); + /* Near & far values used for rendering. Bounds the shadow casters. */ + near_ = 1.0e30f; + far_ = -1.0e30f; + BoundBox bbox = casters_bounds; + for (auto i : IndexRange(8)) { + float dist = -math::dot(z_axis, float3(bbox.vec[i])); + near_ = min_ff(near_, dist); + far_ = max_ff(far_, dist); + } + near_ -= 1e-8f; + far_ += 1e-8f; + + min_level = clamp_i(user_min_level, min_level, max_level); + int level_count = max_level - min_level + 1; + /* The maximum level count is bounded by the mantissa of a 32bit float. */ + if (level_count > 23) { + level_count = 23; + min_level = max_level - level_count + 1; + } + + if (tilemaps.size() != level_count) { + shadows_->tilemap_allocator.free(tilemaps); + tilemaps = shadows_->tilemap_allocator.alloc(level_count); + } + ShadowTileMap &first_clipmap = *tilemaps.first(); + /* Meh... in order to make tile_size_get() work properly. */ + first_clipmap.set_level(min_level); + first_clipmap.set_is_cubemap(false); + + /* Compute full offset from origin to the smallest clipmap tile size. */ + float tile_size = first_clipmap.tile_size_get(); + base_offset_ = int2( + roundf(math::dot(float3(object_mat_.values[0]), camera_position) / tile_size), + roundf(math::dot(float3(object_mat_.values[1]), camera_position) / tile_size)); + + int level = min_level; + int divisor = 1; + for (ShadowTileMap *tilemap : tilemaps) { + tilemap->sync_clipmap( + camera_position, object_mat_, near_, far_, base_offset_ / divisor, level++); + divisor <<= 1; + } + divisor >>= 1; + /* Save only the offset from the first clipmap level to the last. */ + base_offset_ = base_offset_ - (base_offset_ / divisor) * divisor; +} + +ShadowDirectional::operator ShadowData() +{ + ShadowData data; + ShadowTileMap &last_level = *tilemaps.last(); + mul_m4_m4m4(data.mat, shadow_clipmap_scale_mat, last_level.winmat.ptr()); + mul_m4_m4m4(data.mat, data.mat, last_level.viewmat.ptr()); + data.bias = bias_; + data.clip_near = near_; + data.clip_far = far_; + data.base_offset = base_offset_; + data.tilemap_index = tilemaps.first()->index; + data.tilemap_last = data.tilemap_index + tilemaps.size() - 1; + data.clipmap_lod_min = min_resolution_; + data.clipmap_lod_max = min_resolution_ + tilemaps.size() - 1; + return data; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Shadow Module + * + * \{ */ + +void ShadowModule::init(void) +{ + /* TODO(fclem) New resolution parameter. */ + // if (cube_shadow_res_ != inst_.scene->eevee.shadow_cube_size) { + // inst_.sampling.reset(); + // } + + eGPUTextureFormat shadow_format = (inst_.scene->eevee.flag & SCE_EEVEE_SHADOW_HIGH_BITDEPTH) ? + GPU_DEPTH_COMPONENT32F : + GPU_DEPTH_COMPONENT16; + + if (shadow_format_ != shadow_format) { + shadow_format_ = shadow_format; + inst_.sampling.reset(); + + int2 atlas_extent = int2(shadow_page_size_ * SHADOW_PAGE_PER_ROW); + int2 render_extent = int2(shadow_page_size_ * SHADOW_TILEMAP_RES); + GPUTexture *tex = atlas_tx_; + + /* Global update. */ + if ((tex == nullptr) || GPU_texture_format(atlas_tx_) != shadow_format_ || + GPU_texture_width(atlas_tx_) != atlas_extent.x || + GPU_texture_height(atlas_tx_) != atlas_extent.y) { + for (ShadowTileMap *tilemap : tilemap_allocator.maps) { + tilemap->set_dirty(); + } + } + + /* TODO(fclem) GPU_DEPTH_COMPONENT16 support in copy shader? */ + /* TODO(fclem) Make allocation safe. */ + atlas_tx_.ensure(UNPACK2(atlas_extent), 1, GPU_R32F); + atlas_tx_.filter_mode(false); +#if DEBUG + atlas_tx_.clear(0.0f); +#endif + /* Temporary render buffer. */ + render_tx_.ensure(UNPACK2(render_extent), 1, GPU_DEPTH_COMPONENT32F); + render_fb_.ensure(GPU_ATTACHMENT_TEXTURE(render_tx_)); + } + + const bool soft_shadow_enabled = (inst_.scene->eevee.flag & SCE_EEVEE_SHADOW_SOFT) != 0; + if (soft_shadows_enabled_ != soft_shadow_enabled) { + soft_shadows_enabled_ = soft_shadow_enabled; + inst_.sampling.reset(); + } + +#ifndef SHADOW_DEBUG_PAGE_ALLOCATION_ENABLED + if (inst_.debug_mode == SHADOW_DEBUG_PAGE_ALLOCATION) { + BLI_assert_msg(0, + "Error: EEVEE: SHADOW_DEBUG_PAGE_ALLOCATION used but " + "SHADOW_DEBUG_PAGE_ALLOCATION_ENABLED " + "is not defined"); + } +#endif + + tilemap_pixel_radius_ = M_SQRT2 * 2.0f / (SHADOW_TILEMAP_RES * shadow_page_size_); + + debug_data_.type = inst_.debug_mode; +} + +void ShadowModule::begin_sync(void) +{ + casters_bounds_.init_min_max(); + receivers_non_opaque_ = DRW_call_buffer_create(&aabb_format_); + casters_updated_ = DRW_call_buffer_create(&aabb_format_); + + for (int i = 0; i < ARRAY_SIZE(views_); i++) { + views_[i] = nullptr; + } +} + +void ShadowModule::sync_object(Object *ob, + const ObjectHandle &handle, + bool is_shadow_caster, + bool is_alpha_blend) +{ +#ifdef SHADOW_DEBUG_NO_DEPTH_SCAN + is_alpha_blend = true; +#endif + if (!is_shadow_caster && !is_alpha_blend) { + return; + } + + ShadowObject &shadow_ob = objects_.lookup_or_add_default(handle.object_key); + shadow_ob.used = true; + if (handle.recalc != 0 || !shadow_ob.initialized) { + if (is_shadow_caster && shadow_ob.initialized) { + DRW_buffer_add_entry_struct(casters_updated_, &shadow_ob.aabb); + } + shadow_ob.sync(ob); + if (is_shadow_caster) { + DRW_buffer_add_entry_struct(casters_updated_, &shadow_ob.aabb); + } + } + + if (is_shadow_caster) { + casters_bounds_.merge(shadow_ob.aabb); + } + + if (is_alpha_blend) { + printf("receivers_non_opaque_\n"); + DRW_buffer_add_entry_struct(receivers_non_opaque_, &shadow_ob.aabb); + } +} + +void ShadowModule::end_sync(void) +{ + /* Search for deleted or updated shadow casters */ + Vector<ObjectKey, 0> deleted_keys; + for (auto item : objects_.items()) { + ShadowObject &shadow_ob = item.value; + if (!shadow_ob.used) { + deleted_keys.append(item.key); + /* May not be a caster, but it does not matter, be conservative. */ + DRW_buffer_add_entry_struct(casters_updated_, &shadow_ob.aabb); + } + else { + /* Clear for next sync. */ + shadow_ob.used = false; + } + } + for (auto key : deleted_keys) { + objects_.remove(key); + } + if (deleted_keys.size() > 0) { + inst_.sampling.reset(); + } + + /* WARNING: Fragile, use same value as INIT_MINMAX. */ + bool no_casters = (casters_bounds_.min.x == 1.0e-30f); + if (no_casters) { + /* Avoid problems down the road. */ + casters_bounds_ = AABB(1.0f); + } + + /* Finish setting up the tilemaps. */ + punctuals.resize(); + directionals.resize(); + + { + /* Get the farthest point from camera to know what distance to cover. */ + float3 farthest_point = float3(1.0f, 1.0f, 1.0f); + mul_project_m4_v3(inst_.camera.data_get().wininv, farthest_point); + float far_dist = math::length(farthest_point); + float near_dist = fabsf(inst_.camera.data_get().clip_near); + float3 cam_position = inst_.camera.position(); + +#ifdef SHADOW_DEBUG_FREEZE_CAMERA + static bool valid = false; + static float far_dist_freezed; + static float near_dist_freezed; + static float3 cam_position_freezed; + if (G.debug_value < 4 || !valid) { + valid = true; + far_dist_freezed = far_dist; + near_dist_freezed = near_dist; + cam_position_freezed = cam_position; + debug_data_.camera_position = cam_position; + } + else { + far_dist = far_dist_freezed; + near_dist = near_dist_freezed; + cam_position = cam_position_freezed; + debug_data_.camera_position = cam_position_freezed; + } +#endif + + int min_level, max_level; + if (false /* is_ortho_camera */) { + /* TODO(fclem): To have the best resolution we need to find the smallest + * Clipmap that covers the intersection of the Camera Frustum with casters_bounds_. + * cam_position should be the center of this intersection. */ + } + else { + max_level = ceil(log2(far_dist)); + min_level = floor(log2(near_dist)); + } + + for (ShadowDirectional &directional : directionals) { + directional.end_sync(min_level, max_level, cam_position, casters_bounds_); + } + } + + tilemap_allocator.end_sync(); + + last_processed_view = nullptr; + + /** + * Tilemap Management + */ + + int64_t tilemaps_len = tilemap_allocator.active_maps_len; + { + tilemap_setup_ps_ = DRW_pass_create("ShadowTilemapSetup", (DRWState)0); + + GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_TILE_SETUP); + DRWShadingGroup *grp = DRW_shgroup_create(sh, tilemap_setup_ps_); + DRW_shgroup_vertex_buffer(grp, "pages_infos_buf", pages_infos_data_); + DRW_shgroup_vertex_buffer(grp, "pages_free_buf", pages_free_data_); + DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data); + DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx); + DRW_shgroup_uniform_bool(grp, "do_tilemap_setup", &do_tilemap_setup_, 1); + int64_t tilemaps_updated_len = tilemaps_len + tilemap_allocator.deleted_maps_len; + if (tilemaps_updated_len > 0) { + DRW_shgroup_call_compute(grp, 1, 1, tilemaps_updated_len); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_SHADER_STORAGE); + } + do_tilemap_setup_ = true; + + if (G.debug & G_DEBUG_GPU) { + debug_page_map_call(tilemap_setup_ps_); + } + } + { + tilemap_visibility_ps_ = DRW_pass_create("ShadowVisibilityTag", (DRWState)0); + + GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_TILE_TAG_VISIBILITY); + DRWShadingGroup *grp = DRW_shgroup_create(sh, tilemap_visibility_ps_); + DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data); + DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx); + DRW_shgroup_uniform_float(grp, "tilemap_pixel_radius", &tilemap_pixel_radius_, 1); + DRW_shgroup_uniform_float(grp, "screen_pixel_radius_inv", &screen_pixel_radius_inv_, 1); + if (tilemaps_len > 0) { + DRW_shgroup_call_compute(grp, 1, 1, tilemaps_len); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); + } + } + { + tilemap_usage_tag_ps_ = DRW_pass_create("ShadowUsageTag", (DRWState)0); + + GPUVertBuf *receivers_aabbs = DRW_call_buffer_as_vertbuf(receivers_non_opaque_); + uint aabb_len = GPU_vertbuf_get_vertex_len(receivers_aabbs); + + GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_TILE_TAG_USAGE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, tilemap_usage_tag_ps_); + DRW_shgroup_vertex_buffer(grp, "aabb_buf", receivers_aabbs); + DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data); + DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx); + DRW_shgroup_uniform_float(grp, "tilemap_pixel_radius", &tilemap_pixel_radius_, 1); + DRW_shgroup_uniform_float(grp, "screen_pixel_radius_inv", &screen_pixel_radius_inv_, 1); + DRW_shgroup_uniform_int_copy(grp, "aabb_len", aabb_len); + if (tilemaps_len > 0 && aabb_len > 0) { + uint group_len = divide_ceil_u(aabb_len, SHADOW_AABB_TAG_GROUP_SIZE); + DRW_shgroup_call_compute(grp, group_len, 1, tilemaps_len); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); + } + } + { + tilemap_depth_scan_ps_ = DRW_pass_create("ShadowDepthScan", (DRWState)0); + + GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_TILE_DEPTH_SCAN); + DRWShadingGroup *grp = DRW_shgroup_create(sh, tilemap_depth_scan_ps_); + DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_); + DRW_shgroup_vertex_buffer_ref(grp, "lights_buf", &inst_.lights.culling_light_buf); + DRW_shgroup_vertex_buffer_ref(grp, "lights_culling_buf", &inst_.lights.culling_data); + DRW_shgroup_vertex_buffer_ref(grp, "lights_zbins_buf", &inst_.lights.culling_zbin_buf); + DRW_shgroup_vertex_buffer_ref(grp, "lights_tile_buf", &inst_.lights.culling_tile_buf); + DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx); + DRW_shgroup_uniform_float(grp, "tilemap_pixel_radius", &tilemap_pixel_radius_, 1); + DRW_shgroup_uniform_float(grp, "screen_pixel_radius_inv", &screen_pixel_radius_inv_, 1); + DRW_shgroup_call_compute_ref(grp, scan_dispatch_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); + } + { + tilemap_update_tag_ps_ = DRW_pass_create("ShadowUpdateTag", (DRWState)0); + + GPUVertBuf *casters_aabbs = DRW_call_buffer_as_vertbuf(casters_updated_); + uint aabb_len = GPU_vertbuf_get_vertex_len(casters_aabbs); + + GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_TILE_TAG_UPDATE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, tilemap_update_tag_ps_); + DRW_shgroup_vertex_buffer(grp, "aabb_buf", casters_aabbs); + DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data); + DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx); + DRW_shgroup_uniform_int_copy(grp, "aabb_len", GPU_vertbuf_get_vertex_len(casters_aabbs)); + if (tilemaps_len > 0 && aabb_len > 0) { + uint group_len = divide_ceil_u(aabb_len, SHADOW_AABB_TAG_GROUP_SIZE); + DRW_shgroup_call_compute(grp, group_len, 1, tilemaps_len); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); + } + } + { + tilemap_lod_mask_ps_ = DRW_pass_create("ShadowLodMaskTag", (DRWState)0); + + GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_TILE_LOD_MASK); + DRWShadingGroup *grp = DRW_shgroup_create(sh, tilemap_lod_mask_ps_); + DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data); + DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx); + if (tilemaps_len > 0) { + DRW_shgroup_call_compute(grp, 1, 1, tilemaps_len); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); + } + } + + /** + * Page Management + */ + + { + page_init_ps_ = DRW_pass_create("ShadowPageInit", (DRWState)0); + + GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_PAGE_INIT); + DRWShadingGroup *grp = DRW_shgroup_create(sh, page_init_ps_); + DRW_shgroup_vertex_buffer(grp, "pages_infos_buf", pages_infos_data_); + DRW_shgroup_vertex_buffer(grp, "pages_free_buf", pages_free_data_); + DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx); + DRW_shgroup_call_compute(grp, SHADOW_MAX_PAGE / SHADOW_PAGE_PER_ROW, 1, 1); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_SHADER_STORAGE); + + if (G.debug & G_DEBUG_GPU) { + debug_page_map_call(page_init_ps_); + } + } + { + page_free_ps_ = DRW_pass_create("ShadowPageFree", (DRWState)0); + + GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_PAGE_FREE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, page_free_ps_); + DRW_shgroup_vertex_buffer(grp, "pages_infos_buf", pages_infos_data_); + DRW_shgroup_vertex_buffer(grp, "pages_free_buf", pages_free_data_); + DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data); + DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx); + int64_t tilemaps_updated_len = tilemaps_len + tilemap_allocator.deleted_maps_len; + if (tilemaps_updated_len > 0) { + DRW_shgroup_call_compute(grp, 1, 1, tilemaps_updated_len); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_SHADER_STORAGE); + } + + if (G.debug & G_DEBUG_GPU) { + debug_page_map_call(page_free_ps_); + } + } + { + page_defrag_ps_ = DRW_pass_create("ShadowPageDefrag", (DRWState)0); + + GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_PAGE_DEFRAG); + DRWShadingGroup *grp = DRW_shgroup_create(sh, page_defrag_ps_); + DRW_shgroup_vertex_buffer(grp, "pages_free_buf", pages_free_data_); + DRW_shgroup_vertex_buffer(grp, "pages_infos_buf", pages_infos_data_); + DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx); + DRW_shgroup_call_compute(grp, 1, 1, 1); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_SHADER_STORAGE); + } + { + page_alloc_ps_ = DRW_pass_create("ShadowPageAllocate", (DRWState)0); + + GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_PAGE_ALLOC); + DRWShadingGroup *grp = DRW_shgroup_create(sh, page_alloc_ps_); + DRW_shgroup_vertex_buffer(grp, "pages_infos_buf", pages_infos_data_); + DRW_shgroup_vertex_buffer(grp, "pages_free_buf", pages_free_data_); + DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data); + DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx); + DRW_shgroup_uniform_image(grp, "tilemap_rects_img", tilemap_allocator.tilemap_rects_tx); + if (tilemaps_len > 0) { + DRW_shgroup_call_compute(grp, 1, 1, tilemaps_len); + eGPUBarrier barrier = GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_SHADER_STORAGE; + barrier |= GPU_BARRIER_TEXTURE_FETCH; /* Needed for ShadowPageMark / ShadowPageCopy. */ + barrier |= GPU_BARRIER_TEXTURE_UPDATE; /* Needed for readback. */ + DRW_shgroup_barrier(grp, barrier); + } + + if (G.debug & G_DEBUG_GPU) { + debug_page_map_call(page_alloc_ps_); + } + } + { + DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS; + page_mark_ps_ = DRW_pass_create("ShadowPageMark", state); + + GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_PAGE_MARK); + DRWShadingGroup *grp = DRW_shgroup_create(sh, page_mark_ps_); + DRW_shgroup_uniform_texture(grp, "tilemaps_tx", tilemap_allocator.tilemap_tx); + DRW_shgroup_uniform_int(grp, "tilemap_lod", &rendering_lod_, 1); + DRW_shgroup_uniform_int(grp, "tilemap_index", &rendering_tilemap_, 1); + DRW_shgroup_clear_framebuffer(grp, GPU_DEPTH_BIT, 0, 0, 0, 0, 0.0f, 0x0); + DRW_shgroup_call_procedural_triangles(grp, nullptr, square_i(SHADOW_TILEMAP_RES) * 2); + } + { + page_copy_ps_ = DRW_pass_create("ShadowPageCopy", (DRWState)0); + + GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_PAGE_COPY); + DRWShadingGroup *grp = DRW_shgroup_create(sh, page_copy_ps_); + DRW_shgroup_uniform_texture(grp, "tilemaps_tx", tilemap_allocator.tilemap_tx); + DRW_shgroup_uniform_texture(grp, "render_tx", render_tx_); + DRW_shgroup_uniform_image(grp, "out_atlas_img", atlas_tx_); + DRW_shgroup_uniform_int(grp, "tilemap_lod", &rendering_lod_, 1); + DRW_shgroup_uniform_int(grp, "tilemap_index", &rendering_tilemap_, 1); + DRW_shgroup_call_compute_ref(grp, copy_dispatch_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); + } + + debug_end_sync(); +} + +void ShadowModule::debug_page_map_call(DRWPass *pass) +{ + if (debug_data_.type == SHADOW_DEBUG_NONE) { + return; + } + debug_page_tx_.ensure(SHADOW_PAGE_PER_ROW, SHADOW_PAGE_PER_ROW, 0, GPU_R32UI); + + GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_PAGE_DEBUG); + DRWShadingGroup *grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_vertex_buffer(grp, "pages_free_buf", pages_free_data_); + DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx); + DRW_shgroup_uniform_image(grp, "debug_img", debug_page_tx_); + DRW_shgroup_call_compute(grp, 1, 1, 1); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_TEXTURE_FETCH); +} + +void ShadowModule::debug_end_sync(void) +{ + debug_draw_ps_ = nullptr; + + if (debug_data_.type == SHADOW_DEBUG_NONE) { + return; + } + + const bool need_active_light_data = !ELEM( + debug_data_.type, SHADOW_DEBUG_TILE_ALLOCATION, SHADOW_DEBUG_PAGE_ALLOCATION); + + if (need_active_light_data) { + Object *obact = DRW_context_state_get()->obact; + if (obact && (obact->type == OB_LAMP)) { + /* Dangerous. But only used for debug. */ + debug_light_key = inst_.sync.sync_object(obact).object_key; + } + + if (debug_light_key.ob == nullptr) { + return; + } + + LightModule &light_module = inst_.lights; + if (light_module.lights_.contains(debug_light_key) == false) { + return; + } + Light &light = light_module.lights_.lookup(debug_light_key); + if (light.shadow_id == LIGHT_NO_SHADOW) { + return; + } + + debug_data_.light = light; + if (light.type == LIGHT_SUN) { + debug_data_.shadow = directionals[light.shadow_id]; + } + else { + debug_data_.shadow = punctuals[light.shadow_id]; + } + /* Find index of tilemap data. */ + for (auto index : IndexRange(tilemap_allocator.size)) { + if (debug_data_.shadow.tilemap_index == tilemap_allocator.tilemaps_data[index].index) { + debug_data_.tilemap_data_index = index; + break; + } + } + } + + debug_data_.push_update(); + + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | + DRW_STATE_BLEND_CUSTOM; + debug_draw_ps_ = DRW_pass_create("ShadowDebugDraw", state); + + if (debug_data_.type == SHADOW_DEBUG_PAGE_ALLOCATION) { + debug_page_map_call(debug_draw_ps_); + } + + GPUShader *sh = inst_.shaders.static_shader_get(SHADOW_DEBUG); + DRWShadingGroup *grp = DRW_shgroup_create(sh, debug_draw_ps_); + DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data); + DRW_shgroup_uniform_texture(grp, "tilemaps_tx", tilemap_allocator.tilemap_tx); + DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_); + DRW_shgroup_uniform_texture(grp, "atlas_tx", atlas_tx_); + DRW_shgroup_uniform_block(grp, "debug_block", debug_data_.ubo_get()); + if (debug_data_.type == SHADOW_DEBUG_PAGE_ALLOCATION) { + DRW_shgroup_uniform_texture(grp, "debug_page_tx", debug_page_tx_); + } + else { + DRW_shgroup_uniform_texture(grp, "debug_page_tx", tilemap_allocator.tilemap_tx); + } + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + } +} + +/* Update all shadow regions visible inside the view. + * If called multiple time for the same view, it will only do the depth buffer scanning + * to check any new opaque surfaces. + * Needs to be called after LightModule::set_view(); */ +void ShadowModule::set_view(const DRWView *view, GPUTexture *depth_tx) +{ + /* Only process each view once. */ + bool do_process_view = (view != last_processed_view); + last_processed_view = view; + +#if 0 /* TODO */ + bool force_update = false; + if (soft_shadows_enabled_ && (inst_.sampling.sample_get() != last_sample_)) { + force_update = true; + last_sample_ = inst_.sampling.sample_get(); + } + else { + last_sample_ = 0; + } +#endif + + ivec2 extent(GPU_texture_width(depth_tx), GPU_texture_height(depth_tx)); + input_depth_tx_ = depth_tx; + + scan_dispatch_size_.x = divide_ceil_u(extent.x, SHADOW_DEPTH_SCAN_GROUP_SIZE); + scan_dispatch_size_.y = divide_ceil_u(extent.y, SHADOW_DEPTH_SCAN_GROUP_SIZE); + scan_dispatch_size_.z = 1; + + DRW_view_set_active(view); + + float4x4 wininv; + DRW_view_winmat_get(view, wininv.values, true); + + { + /* Compute approximate screen pixel density (as world space radius). */ + float min_dim = float(min_ii(extent.x, extent.y)); + float3 p0 = float3(-1.0f, -1.0f, 0.0f); + float3 p1 = float3( + (min_dim / extent.x) * 2.0f - 1.0f, (min_dim / extent.y) * 2.0f - 1.0f, 0.0f); + mul_project_m4_v3(wininv.values, p0); + mul_project_m4_v3(wininv.values, p1); + /* Compute radius at unit plane from the camera. */ + if (DRW_view_is_persp_get(view)) { + p0 = p0 / p0.z; + p1 = p1 / p1.z; + } + screen_pixel_radius_inv_ = min_dim / math::distance(p0, p1); + } + +#ifdef SHADOW_DEBUG_FREEZE_CAMERA + static bool valid = false; + static float4x4 viewmat_freezed; + if (G.debug_value < 4 || !valid) { + valid = true; + DRW_view_viewmat_get(view, viewmat_freezed.values, false); + } + else { + float4x4 winmat; + DRW_view_winmat_get(view, winmat.values, false); + DRWView *debug_view = DRW_view_create( + viewmat_freezed.values, winmat.values, nullptr, nullptr, nullptr); + DRW_view_set_active(debug_view); + + float4 color(1.0f); + float4x4 persinv; + DRW_view_persmat_get(debug_view, persinv.values, true); + DRW_debug_m4_as_bbox(persinv.values, color, false); + } +#endif + + DRW_stats_group_start("ShadowUpdate"); + { + if (do_tilemap_setup_) { + if (do_page_init_) { +#ifndef SHADOW_DEBUG_NO_CACHING + do_page_init_ = false; +#endif + tilemap_allocator.tilemap_tx.clear((uint)0); + DRW_draw_pass(page_init_ps_); + } + } + /* Run every every time but only process tilemap update once. */ + DRW_draw_pass(tilemap_setup_ps_); + if (do_tilemap_setup_) { + DRW_draw_pass(tilemap_update_tag_ps_); + do_tilemap_setup_ = false; + } + if (do_process_view) { + DRW_draw_pass(tilemap_visibility_ps_); + DRW_draw_pass(tilemap_usage_tag_ps_); + } +#ifndef SHADOW_DEBUG_NO_DEPTH_SCAN + if (input_depth_tx_ != nullptr) { + DRW_draw_pass(tilemap_depth_scan_ps_); + } +#endif + DRW_draw_pass(tilemap_lod_mask_ps_); + DRW_draw_pass(page_free_ps_); + DRW_draw_pass(page_defrag_ps_); + DRW_draw_pass(page_alloc_ps_); + } + DRW_stats_group_end(); + + DRW_stats_group_start("ShadowRender"); + { + /* Readback update list. Ugly sync point. */ + rcti *rect = tilemap_allocator.tilemap_rects_tx.read<rcti>(GPU_DATA_INT); + + Span<rcti> regions(rect, tilemap_allocator.maps.size() * (SHADOW_TILEMAP_LOD + 1)); + Span<ShadowTileMap *> tilemaps = tilemap_allocator.maps.as_span(); + + int rect_idx = 0; + while (!regions.is_empty()) { + Vector<int, 6> regions_index; + Vector<int, 6> regions_lod; + + /* Group updates by pack of 6. This is to workaround the current DRWView limitation. + * Future goal is to have GPU culling and create the views on GPU. */ + while (!regions.is_empty() && regions_index.size() < 6) { + int lod = rect_idx % (SHADOW_TILEMAP_LOD + 1); + ShadowTileMap &tilemap = *tilemaps.first(); + if (!tilemap.is_cubeface && lod > 0) { + /* Do not process lod for clipmaps as they are undefined. */ + } + else if (!BLI_rcti_is_empty(®ions.first())) { + tilemap.setup_view(regions.first(), views_[regions_index.size()]); + regions_index.append(tilemap.index); + regions_lod.append(lod); + } + rect_idx++; + regions = regions.drop_front(1); + if (lod == SHADOW_TILEMAP_LOD) { + tilemaps = tilemaps.drop_front(1); + } + } + + for (auto i : regions_index.index_range()) { + rendering_tilemap_ = regions_index[i]; + rendering_lod_ = regions_lod[i]; + + DRW_view_set_active(views_[i]); + GPU_framebuffer_bind(render_fb_); + + copy_dispatch_size_.x = shadow_page_size_ / SHADOW_PAGE_COPY_GROUP_SIZE; + copy_dispatch_size_.y = copy_dispatch_size_.x; + copy_dispatch_size_.z = 1; + + int viewport_size = render_tx_.width() >> rendering_lod_; + GPU_framebuffer_viewport_set(render_fb_, 0, 0, viewport_size, viewport_size); + + DRW_draw_pass(page_mark_ps_); + inst_.shading_passes.shadow.render(); + DRW_draw_pass(page_copy_ps_); + } + } + + MEM_freeN(rect); + } + DRW_stats_group_end(); + + DRW_view_set_active(view); +} + +void ShadowModule::debug_draw(GPUFrameBuffer *view_fb, HiZBuffer &hiz) +{ + if (debug_draw_ps_ == nullptr) { + return; + } + input_depth_tx_ = hiz.texture_get(); + + GPU_framebuffer_bind(view_fb); + DRW_draw_pass(debug_draw_ps_); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Shadow Pass + * + * \{ */ + +void ShadowPass::sync(void) +{ + { + DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_SHADOW_OFFSET; + surface_ps_ = DRW_pass_create("ShadowSurface", state); + } +} + +DRWShadingGroup *ShadowPass::material_add(::Material *UNUSED(material), GPUMaterial *gpumat) +{ + DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, surface_ps_); + DRW_shgroup_uniform_block(grp, "sampling_block", inst_.sampling.ubo_get()); + return grp; +} + +void ShadowPass::render(void) +{ + DRW_draw_pass(surface_ps_); +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_shadow.hh b/source/blender/draw/engines/eevee/eevee_shadow.hh new file mode 100644 index 00000000000..a7db2f6dc0e --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_shadow.hh @@ -0,0 +1,611 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * The shadow module manages shadow update tagging & shadow rendering. + */ + +#pragma once + +#include "BLI_vector.hh" + +#include "GPU_batch.h" + +#include "eevee_allocator.hh" +#include "eevee_id_map.hh" +#include "eevee_material.hh" +#include "eevee_shader.hh" +#include "eevee_shader_shared.hh" + +namespace blender::eevee { + +/** + * TODO(fclem): Future plans + * The start of the implementation was done on CPU with the constraints of UBO limits and no + * compute capabilities in mind. + * But after removing this limit this left the door open for a full GPU driven pipeline of + * shadow and light management where the CPU would only push Objects updates and manage buffer + * grow/shrink behaviors. The GPU would then do what ShadowTileAllocator, ShadowPunctual and + * ShadowDirectional classes are doing. + * We still need to find a way to issue the shadow render passes at once and cull objects per view + * on GPU. + */ + +class Instance; +class ShadowModule; + +/** World space axis aligned bounding box. */ +struct AABB { + /** + * TODO(fclem) There is padding to match the std430 layout requirement inside shaders storage. + * The goal would be to send the Oriented Bound Box for better culling. + */ + float3 min; + float _pad0; + float3 max; + float _pad1; + + AABB() = default; + AABB(float val) : min(-val), max(val){}; + AABB(Object *ob) + { + init_min_max(); + BoundBox *bb = BKE_object_boundbox_get(ob); + for (int i = 0; i < 8; i++) { + float vec[3]; + copy_v3_v3(vec, bb->vec[i]); + mul_m4_v3(ob->obmat, vec); + minmax_v3v3_v3(min, max, vec); + } + } + + void debug_draw(void) + { + BoundBox bb = *this; + vec4 color = {1, 0, 0, 1}; + DRW_debug_bbox(&bb, color); + } + + float3 center(void) const + { + return (min + max) * 0.5; + } + + void init_min_max(void) + { + INIT_MINMAX(min, max); + } + + void merge(const AABB &a) + { + DO_MIN(a.min, min); + DO_MAX(a.max, max); + } + + void merge(const float3 &a) + { + DO_MIN(a, min); + DO_MAX(a, max); + } + + float radius(void) const + { + return math::length(max - min) / 2.0f; + } + + operator BoundBox() const + { + float3 middle = center(); + float3 halfdim = max - middle; + BoundBox bb; + *reinterpret_cast<float3 *>(bb.vec[0]) = middle + halfdim * vec3(1, 1, 1); + *reinterpret_cast<float3 *>(bb.vec[1]) = middle + halfdim * vec3(-1, 1, 1); + *reinterpret_cast<float3 *>(bb.vec[2]) = middle + halfdim * vec3(-1, -1, 1); + *reinterpret_cast<float3 *>(bb.vec[3]) = middle + halfdim * vec3(1, -1, 1); + *reinterpret_cast<float3 *>(bb.vec[4]) = middle + halfdim * vec3(1, 1, -1); + *reinterpret_cast<float3 *>(bb.vec[5]) = middle + halfdim * vec3(-1, 1, -1); + *reinterpret_cast<float3 *>(bb.vec[6]) = middle + halfdim * vec3(-1, -1, -1); + *reinterpret_cast<float3 *>(bb.vec[7]) = middle + halfdim * vec3(1, -1, -1); + return bb; + } +}; + +/* -------------------------------------------------------------------- */ +/** \name Shadow + * + * \{ */ + +/* To be applied after viewmatrix. */ +constexpr static const float shadow_face_mat[6][4][4] = { + {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}, /* Z_NEG */ + {{0, 0, -1, 0}, {-1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* X_POS */ + {{0, 0, 1, 0}, {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* X_NEG */ + {{1, 0, 0, 0}, {0, 0, -1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* Y_POS */ + {{-1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}, /* Y_NEG */ + {{1, 0, 0, 0}, {0, -1, 0, 0}, {0, 0, -1, 0}, {0, 0, 0, 1}}, /* Z_POS */ +}; + +/* Converts to [-SHADOW_TILEMAP_RES / 2..SHADOW_TILEMAP_RES / 2] for XY and [0..1] for Z. */ +constexpr static const float shadow_clipmap_scale_mat[4][4] = {{SHADOW_TILEMAP_RES / 2, 0, 0, 0}, + {0, SHADOW_TILEMAP_RES / 2, 0, 0}, + {0, 0, 0.5, 0}, + {0, 0, 0.5, 1}}; + +constexpr static const float tilemat_scale_bias_mat[4][4] = { + {SHADOW_TILEMAP_RES / 2, 0, 0, 0}, + {0, SHADOW_TILEMAP_RES / 2, 0, 0}, + {0, 0, 1, 0}, + {SHADOW_TILEMAP_RES / 2, SHADOW_TILEMAP_RES / 2, 0, 1}}; + +enum eCubeFace { + /* Ordering by culling order. If cone aperture is shallow, we cull the later view. */ + Z_NEG = 0, + X_POS, + X_NEG, + Y_POS, + Y_NEG, + Z_POS, +}; + +/** + * Stores indirection table and states of each tile of a virtual shadowmap clipmap level. + * One tilemap has the effective resolution of `pagesize * tile_map_resolution` . + * Each tilemap overhead is quite small if they do not have any pages allocated. + */ +struct ShadowTileMap : public ShadowTileMapData { + static constexpr int64_t tile_map_resolution = SHADOW_TILEMAP_RES; + static constexpr int64_t tiles_count = tile_map_resolution * tile_map_resolution; + /** + * Maximum "bounding" angle of a tile inside a cubemap. + * Half the diagonal of tile since we test using the tile center. + */ + static constexpr float tile_cone_half_angle = atan(0.5 * M_SQRT2 / (SHADOW_TILEMAP_RES / 2)); + + /** Level of detail for clipmap. */ + int level = INT_MAX; + /** Integer offset of the center of the 16x16 tiles from the origin of the tile space. */ + int2 grid_offset = int2(16); + /** Cube face index. */ + eCubeFace cubeface = Z_NEG; + /** Cached, used for rendering. */ + float4x4 viewmat, winmat; + /** Cached, used for detecting updates. */ + float4x4 object_mat; + /** Near and far clip distances. For clipmap they are updated after sync. */ + float near, far; + + public: + ShadowTileMap(int64_t _index) + { + index = _index; + }; + + void sync_clipmap(const float3 &camera_position, + const float4x4 &object_mat_, + float near_, + float far_, + int2 origin_offset, + int clipmap_level); + void sync_cubeface( + const float4x4 &object_mat, float near, float far, float cone_aperture, eCubeFace face); + + float tilemap_coverage_get(void) const + { + /* This function should be kept in sync with shadow_directional_clipmap_level(). */ + /* NOTE(fclem): If we would to introduce a global scaling option it would be here. */ + BLI_assert(!is_cubeface); + return powf(2.0f, level); + } + + float tile_size_get(void) const + { + return tilemap_coverage_get() / tile_map_resolution; + } + + float4x4 winmat_get(const rcti *tile_minmax) const; + void setup_view(const rcti &rect, DRWView *&view) const; + void debug_draw(void) const; + + /* For external callers. Use this in order to not miss an update. */ + void set_level(int clipmap_level) + { + if (level != clipmap_level) { + level = clipmap_level; + set_dirty(); + } + } + void set_is_cubemap(bool is_cubemap_) + { + if (is_cubeface != is_cubemap_) { + is_cubeface = is_cubemap_; + set_dirty(); + } + } + + void set_dirty() + { + grid_shift = int2(SHADOW_TILEMAP_RES); + } + + void set_updated() + { + grid_shift = int2(0); + } +}; + +struct ShadowCommon { + /** Tilemap for each cubeface needed (in eCubeFace order) or for each clipmap level. */ + Vector<ShadowTileMap *> tilemaps; + /** To have access to the tilemap allocator. */ + ShadowModule *shadows_; + + ShadowCommon(ShadowModule *shadows) : shadows_(shadows){}; + + void free_resources(); +}; + +class ShadowPunctual : public ShadowCommon { + private: + /** Area light size. */ + float size_x_, size_y_; + /** Shape type. */ + eLightType light_type_; + /** Random position on the light. In world space. */ + vec3 random_offset_; + /** Light position. */ + float3 position_; + /** Near and far clip distances. */ + float far_, near_; + /** View space offset to apply to the shadow. */ + float bias_; + + public: + ShadowPunctual(ShadowModule *shadows) : ShadowCommon(shadows){}; + + void sync(eLightType light_type, + const mat4 &object_mat, + float cone_aperture, + float near_clip, + float far_clip, + float bias); + + operator ShadowData(); +}; + +class ShadowDirectional : public ShadowCommon { + private: + /** User minimum resolution. */ + float min_resolution_; + /** View space offset to apply to the shadow. */ + float bias_; + /** Near and far clip distances. For clipmap, when they are updated after sync. */ + float near_, far_; + /** Offset of the lowest clipmap relative to the highest one. */ + ivec2 base_offset_; + /** Copy of object matrix. */ + float4x4 object_mat_; + + public: + ShadowDirectional(ShadowModule *shadows) : ShadowCommon(shadows){}; + + void sync(const mat4 &object_mat, float bias, float min_resolution); + void end_sync(int min_level, + int max_level, + const float3 &camera_position, + const AABB &casters_bounds); + + operator ShadowData(); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Shadow Casters & Receivers + * + * \{ */ + +/* Can be either a shadow caster or a shadow receiver. */ +struct ShadowObject { + AABB aabb; + + bool initialized = false; + bool used; + bool updated; + + void sync(Object *ob) + { + aabb = AABB(ob); + initialized = true; + updated = true; + } +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name ShadowModule + * + * Manages shadow atlas and shadow region datas. + * \{ */ + +/** + * Manages the tilemaps and allocates continuous regions to a shadow object. + * This way indexing is simple and fast inside the shaders. + * The tilemap atlas has a fixed 64x64 size. So it can contain 4096 tilemap or 16x16 pixels each. + * We allocate for many tilemaps because we don't want to reallocate the buffer as it would mean + * trashing the whole cache which it. + * In the future we could resize and copy old tilemap infos. But for now we KISS. + */ +struct ShadowTileAllocator { + static constexpr int64_t size = SHADOW_MAX_TILEMAP; + /** Limit the with of the texture. */ + static constexpr int64_t maps_per_row = SHADOW_TILEMAP_PER_ROW; + /* TODO(fclem): Do it for real... Use real bitmap. */ + Vector<bool> usage_bitmap_ = Vector<bool>(size); + /** Circular buffer allocation scheme. This is the last allocated index. */ + int64_t next_index = 0; + /** Vector containning the actual maps. Unordered. */ + Vector<ShadowTileMap *> maps; + /** Deleted maps go here to be freed after the next sync. */ + Vector<ShadowTileMap *> maps_deleted; + /** + * Tilemap atlas containing mapping to shadow pages inside the atlas texture. + * All shadow tilemaps are packed into one texture. + * Contains every clipmaps level of all directional light and each cubeface with mipmap. + */ + Texture tilemap_tx = Texture("tilemap_tx"); + /** Very small texture containing the result of the update pass. */ + /** FIXME(fclem): It would be nice to avoid GPU > CPU readback. */ + Texture tilemap_rects_tx = Texture("tilemap_rects_tx"); + /** UBO containing the description for every allocated tilemap. */ + ShadowTileMapDataBuf tilemaps_data; + /** Number of maps inside the tilemaps_data. */ + int64_t active_maps_len = 0; + /** Number of maps at the end of tilemaps_data that are being deleted and need clear. */ + int64_t deleted_maps_len = 0; + + ShadowTileAllocator(); + ~ShadowTileAllocator(); + + /** Returns empty span on failure. */ + Span<ShadowTileMap *> alloc(int64_t count); + + void free(Vector<ShadowTileMap *> &free_list); + + void end_sync(); +}; + +/** + * Simple struct here to group all things page related. + */ +struct ShadowVirtualPageManager { + ShadowVirtualPageManager(); + ~ShadowVirtualPageManager(); + + void end_sync(); +}; + +class ShadowModule { + friend ShadowPunctual; + friend ShadowDirectional; + + template<typename T> class ShadowAllocator : public IndexedAllocator<T> { + private: + ShadowModule &shadows_; + + public: + ShadowAllocator(ShadowModule &shadows) : shadows_(shadows){}; + + int64_t alloc(void) + { + return IndexedAllocator<T>::alloc(T(&shadows_)); + } + }; + + public: + /** Need to be first because of destructor order. */ + ShadowTileAllocator tilemap_allocator; + + ShadowAllocator<ShadowPunctual> punctuals; + ShadowAllocator<ShadowDirectional> directionals; + + private: + Instance &inst_; + + /** Map of shadow casters to track deletion & update of intersected shadows. */ + Map<ObjectKey, ShadowObject> objects_; + + /** Used to detect sample change for soft shadows. */ + uint64_t last_sample_ = 0; + + /** + * TODO(fclem) These should be stored inside the Shadow objects instead. + * The issues is that only 32 DRWView can have effective culling data with the current + * implementation. So we try to reduce the number of DRWView allocated to avoid the slow path. + **/ + DRWView *views_[6] = {nullptr}; + + /** + * Separate render buffer. This is meant to be replace by directly rendering inside the atlas. + */ + eevee::Texture render_tx_ = Texture("shadow_target_tx_"); + eevee::Framebuffer render_fb_ = Framebuffer("shadow_fb"); + + /* -------------------------------------------------------------------- */ + /** \name Tilemap Management + * \{ */ + + /** + * Clear the visibility, usage and request bits. + * Also shifts the whole tilemap for directional shadow clipmaps. + */ + DRWPass *tilemap_setup_ps_; + /** Update passes that will mark all shadow pages from a light to update or as unused. */ + DRWPass *tilemap_visibility_ps_; + /** Update passes that will mark all shadow pages touching an updated shadow caster. */ + DRWPass *tilemap_update_tag_ps_; + /** Tag each tile intersecting with a shadow receiver. */ + /* NOTE(fclem): Until we implement depth buffer scanning, we rely solely on this to tag + * needed tiles. */ + DRWPass *tilemap_usage_tag_ps_; + /** Use depth buffer to tag needed shadow pages. */ + DRWPass *tilemap_depth_scan_ps_; + /** Discard pages that are redundant in the mipmap chain. */ + DRWPass *tilemap_lod_mask_ps_; + + /** List of AABBs for tagging passes. */ + DRWCallBuffer *casters_updated_; + DRWCallBuffer *receivers_non_opaque_; + + int do_tilemap_setup_ = true; + const DRWView *last_processed_view = nullptr; + float tilemap_pixel_radius_; + float screen_pixel_radius_inv_; + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Page Management + * \{ */ + + eevee::Texture atlas_tx_ = Texture("shadow_atlas_tx_"); + + /** Pool of unallocated pages waiting to be assigned to specific tiles in the tilemap atlas. */ + ShadowPageHeapBuf pages_free_data_; + /** Infos for book keeping and debug. */ + ShadowPagesInfoDataBuf pages_infos_data_; + + /** Page buffer clear. This is only done if shadow atlas is reallocated. */ + DRWPass *page_init_ps_; + /** Defragment the page free array. */ + DRWPass *page_defrag_ps_; + /** Free pages of deleted tiles. You can think of a garbage collection. */ + DRWPass *page_free_ps_; + /** Allocate pages for new tiles. */ + DRWPass *page_alloc_ps_; + /** Clear depth of tiles to render to 1.0 and 0.0 for others. */ + DRWPass *page_mark_ps_; + /** Copy pages in the copy list. */ + DRWPass *page_copy_ps_; + + bool do_page_init_ = true; + int3 copy_dispatch_size_; + int3 scan_dispatch_size_; + int rendering_tilemap_; + int rendering_lod_; + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Debugging + * \{ */ + + /** Display informations about the virtual shadows. */ + DRWPass *debug_draw_ps_; + /** Depth input for debug drawing. Reference only. */ + GPUTexture *input_depth_tx_; + /** Object key used to retreive last active light. The debug info shown are from this light. */ + ObjectKey debug_light_key; + /** View used for the whole virtual shadow mapping setup. Used to debug culling. */ + DRWView *debug_view_; + /** Debug data sent to GPU. */ + ShadowDebugDataBuf debug_data_; + /** Debug texture to check page status. */ + Texture debug_page_tx_ = Texture("debug_page_tx_"); + + /** \} */ + + /** Scene immutable parameter. */ + int shadow_page_size_ = 256; + bool soft_shadows_enabled_ = false; + /** Default to invalid texture type. */ + eGPUTextureFormat shadow_format_ = GPU_RGBA8; + + /** Used for caster & receiver AABB lists. */ + GPUVertFormat aabb_format_; + /** Global bounds that contains all shadow casters. Used by directionnal for best fit. */ + AABB casters_bounds_; + + public: + ShadowModule(Instance &inst) : punctuals(*this), directionals(*this), inst_(inst) + { + GPU_vertformat_clear(&aabb_format_); + /* Must match the C++ AABB layout. */ + BLI_assert(sizeof(AABB) == sizeof(float) * 8); + GPU_vertformat_attr_add(&aabb_format_, "aabb_min", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&aabb_format_, "aabb_max", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + } + ~ShadowModule(){}; + + void init(void); + + void begin_sync(void); + void sync_object(Object *ob, + const ObjectHandle &handle, + bool is_shadow_caster, + bool is_alpha_blend); + void end_sync(void); + + void set_view(const DRWView *view, GPUTexture *depth_tx); + + void debug_end_sync(void); + void debug_draw(GPUFrameBuffer *view_fb, HiZBuffer &hiz); + + GPUTexture *atlas_tx_get(void) + { + return atlas_tx_; + } + GPUTexture *tilemap_tx_get(void) + { + return tilemap_allocator.tilemap_tx; + } + + private: + void remove_unused(void); + void debug_page_map_call(DRWPass *pass); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name ShadowPass + * + * A simple depth pass to which all shadow casters subscribe. + * \{ */ + +class ShadowPass { + private: + Instance &inst_; + + DRWPass *surface_ps_ = nullptr; + + public: + ShadowPass(Instance &inst) : inst_(inst){}; + + void sync(void); + + DRWShadingGroup *material_add(::Material *blender_mat, GPUMaterial *gpumat); + + void render(void); +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_shadows.c b/source/blender/draw/engines/eevee/eevee_shadows.c deleted file mode 100644 index d8a40fd13cc..00000000000 --- a/source/blender/draw/engines/eevee/eevee_shadows.c +++ /dev/null @@ -1,413 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2019, Blender Foundation. - */ - -/** \file - * \ingroup EEVEE - */ - -#include "BLI_string_utils.h" -#include "BLI_sys_types.h" /* bool */ - -#include "BKE_object.h" - -#include "DEG_depsgraph_query.h" - -#include "eevee_private.h" - -#define SH_CASTER_ALLOC_CHUNK 32 - -void eevee_contact_shadow_setup(const Light *la, EEVEE_Shadow *evsh) -{ - evsh->contact_dist = (la->mode & LA_SHAD_CONTACT) ? la->contact_dist : 0.0f; - evsh->contact_bias = 0.05f * la->contact_bias; - evsh->contact_thickness = la->contact_thickness; -} - -void EEVEE_shadows_init(EEVEE_ViewLayerData *sldata) -{ - const uint shadow_ubo_size = sizeof(EEVEE_Shadow) * MAX_SHADOW + - sizeof(EEVEE_ShadowCube) * MAX_SHADOW_CUBE + - sizeof(EEVEE_ShadowCascade) * MAX_SHADOW_CASCADE; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - - if (!sldata->lights) { - sldata->lights = MEM_callocN(sizeof(EEVEE_LightsInfo), "EEVEE_LightsInfo"); - sldata->light_ubo = GPU_uniformbuf_create_ex(sizeof(EEVEE_Light) * MAX_LIGHT, NULL, "evLight"); - sldata->shadow_ubo = GPU_uniformbuf_create_ex(shadow_ubo_size, NULL, "evShadow"); - - for (int i = 0; i < 2; i++) { - sldata->shcasters_buffers[i].bbox = MEM_mallocN( - sizeof(EEVEE_BoundBox) * SH_CASTER_ALLOC_CHUNK, __func__); - sldata->shcasters_buffers[i].update = BLI_BITMAP_NEW(SH_CASTER_ALLOC_CHUNK, __func__); - sldata->shcasters_buffers[i].alloc_count = SH_CASTER_ALLOC_CHUNK; - sldata->shcasters_buffers[i].count = 0; - } - sldata->lights->shcaster_frontbuffer = &sldata->shcasters_buffers[0]; - sldata->lights->shcaster_backbuffer = &sldata->shcasters_buffers[1]; - } - - /* Flip buffers */ - SWAP(EEVEE_ShadowCasterBuffer *, - sldata->lights->shcaster_frontbuffer, - sldata->lights->shcaster_backbuffer); - - int sh_cube_size = scene_eval->eevee.shadow_cube_size; - int sh_cascade_size = scene_eval->eevee.shadow_cascade_size; - const bool sh_high_bitdepth = (scene_eval->eevee.flag & SCE_EEVEE_SHADOW_HIGH_BITDEPTH) != 0; - sldata->lights->soft_shadows = (scene_eval->eevee.flag & SCE_EEVEE_SHADOW_SOFT) != 0; - - EEVEE_LightsInfo *linfo = sldata->lights; - if ((linfo->shadow_cube_size != sh_cube_size) || - (linfo->shadow_high_bitdepth != sh_high_bitdepth)) { - BLI_assert((sh_cube_size > 0) && (sh_cube_size <= 4096)); - DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_pool); - CLAMP(sh_cube_size, 1, 4096); - } - - if ((linfo->shadow_cascade_size != sh_cascade_size) || - (linfo->shadow_high_bitdepth != sh_high_bitdepth)) { - BLI_assert((sh_cascade_size > 0) && (sh_cascade_size <= 4096)); - DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_pool); - CLAMP(sh_cascade_size, 1, 4096); - } - - linfo->shadow_high_bitdepth = sh_high_bitdepth; - linfo->shadow_cube_size = sh_cube_size; - linfo->shadow_cascade_size = sh_cascade_size; -} - -void EEVEE_shadows_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_LightsInfo *linfo = sldata->lights; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_PassList *psl = vedata->psl; - - EEVEE_ShadowCasterBuffer *backbuffer = linfo->shcaster_backbuffer; - EEVEE_ShadowCasterBuffer *frontbuffer = linfo->shcaster_frontbuffer; - - frontbuffer->count = 0; - linfo->num_cube_layer = 0; - linfo->num_cascade_layer = 0; - linfo->cube_len = linfo->cascade_len = linfo->shadow_len = 0; - - /* Shadow Casters: Reset flags. */ - BLI_bitmap_set_all(backbuffer->update, true, backbuffer->alloc_count); - /* Is this one needed? */ - BLI_bitmap_set_all(frontbuffer->update, false, frontbuffer->alloc_count); - - INIT_MINMAX(linfo->shcaster_aabb.min, linfo->shcaster_aabb.max); - - { - DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_SHADOW_OFFSET; - DRW_PASS_CREATE(psl->shadow_pass, state); - - stl->g_data->shadow_shgrp = DRW_shgroup_create(EEVEE_shaders_shadow_sh_get(), - psl->shadow_pass); - } -} - -void EEVEE_shadows_caster_register(EEVEE_ViewLayerData *sldata, Object *ob) -{ - EEVEE_LightsInfo *linfo = sldata->lights; - EEVEE_ShadowCasterBuffer *backbuffer = linfo->shcaster_backbuffer; - EEVEE_ShadowCasterBuffer *frontbuffer = linfo->shcaster_frontbuffer; - bool update = true; - int id = frontbuffer->count; - - /* Make sure shadow_casters is big enough. */ - if (id >= frontbuffer->alloc_count) { - /* Double capacity to prevent exponential slowdown. */ - frontbuffer->alloc_count *= 2; - frontbuffer->bbox = MEM_reallocN(frontbuffer->bbox, - sizeof(EEVEE_BoundBox) * frontbuffer->alloc_count); - BLI_BITMAP_RESIZE(frontbuffer->update, frontbuffer->alloc_count); - } - - if (ob->base_flag & BASE_FROM_DUPLI) { - /* Duplis will always refresh the shadow-maps as if they were deleted each frame. */ - /* TODO(fclem): fix this. */ - update = true; - } - else { - EEVEE_ObjectEngineData *oedata = EEVEE_object_data_ensure(ob); - int past_id = oedata->shadow_caster_id; - oedata->shadow_caster_id = id; - /* Update flags in backbuffer. */ - if (past_id > -1 && past_id < backbuffer->count) { - BLI_BITMAP_SET(backbuffer->update, past_id, oedata->need_update); - } - update = oedata->need_update; - oedata->need_update = false; - } - - if (update) { - BLI_BITMAP_ENABLE(frontbuffer->update, id); - } - - /* Update World AABB in frontbuffer. */ - BoundBox *bb = BKE_object_boundbox_get(ob); - float min[3], max[3]; - INIT_MINMAX(min, max); - for (int i = 0; i < 8; i++) { - float vec[3]; - copy_v3_v3(vec, bb->vec[i]); - mul_m4_v3(ob->obmat, vec); - minmax_v3v3_v3(min, max, vec); - } - - EEVEE_BoundBox *aabb = &frontbuffer->bbox[id]; - /* Note that `*aabb` has not been initialized yet. */ - add_v3_v3v3(aabb->center, min, max); - mul_v3_fl(aabb->center, 0.5f); - sub_v3_v3v3(aabb->halfdim, aabb->center, max); - - aabb->halfdim[0] = fabsf(aabb->halfdim[0]); - aabb->halfdim[1] = fabsf(aabb->halfdim[1]); - aabb->halfdim[2] = fabsf(aabb->halfdim[2]); - - minmax_v3v3_v3(linfo->shcaster_aabb.min, linfo->shcaster_aabb.max, min); - minmax_v3v3_v3(linfo->shcaster_aabb.min, linfo->shcaster_aabb.max, max); - - frontbuffer->count++; -} - -/* Used for checking if object is inside the shadow volume. */ -static bool sphere_bbox_intersect(const BoundSphere *bs, const EEVEE_BoundBox *bb) -{ - /* We are testing using a rougher AABB vs AABB test instead of full AABB vs Sphere. */ - /* TODO: test speed with AABB vs Sphere. */ - bool x = fabsf(bb->center[0] - bs->center[0]) <= (bb->halfdim[0] + bs->radius); - bool y = fabsf(bb->center[1] - bs->center[1]) <= (bb->halfdim[1] + bs->radius); - bool z = fabsf(bb->center[2] - bs->center[2]) <= (bb->halfdim[2] + bs->radius); - - return x && y && z; -} - -void EEVEE_shadows_update(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - EEVEE_LightsInfo *linfo = sldata->lights; - EEVEE_ShadowCasterBuffer *backbuffer = linfo->shcaster_backbuffer; - EEVEE_ShadowCasterBuffer *frontbuffer = linfo->shcaster_frontbuffer; - - eGPUTextureFormat shadow_pool_format = (linfo->shadow_high_bitdepth) ? GPU_DEPTH_COMPONENT24 : - GPU_DEPTH_COMPONENT16; - /* Setup enough layers. */ - /* Free textures if number mismatch. */ - if (linfo->num_cube_layer != linfo->cache_num_cube_layer) { - DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_pool); - linfo->cache_num_cube_layer = linfo->num_cube_layer; - /* Update all lights. */ - BLI_bitmap_set_all(&linfo->sh_cube_update[0], true, MAX_LIGHT); - } - - if (linfo->num_cascade_layer != linfo->cache_num_cascade_layer) { - DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_pool); - linfo->cache_num_cascade_layer = linfo->num_cascade_layer; - } - - if (!sldata->shadow_cube_pool) { - sldata->shadow_cube_pool = DRW_texture_create_2d_array(linfo->shadow_cube_size, - linfo->shadow_cube_size, - max_ii(1, linfo->num_cube_layer * 6), - shadow_pool_format, - DRW_TEX_FILTER | DRW_TEX_COMPARE, - NULL); - } - - if (!sldata->shadow_cascade_pool) { - sldata->shadow_cascade_pool = DRW_texture_create_2d_array(linfo->shadow_cascade_size, - linfo->shadow_cascade_size, - max_ii(1, linfo->num_cascade_layer), - shadow_pool_format, - DRW_TEX_FILTER | DRW_TEX_COMPARE, - NULL); - } - - if (sldata->shadow_fb == NULL) { - sldata->shadow_fb = GPU_framebuffer_create("shadow_fb"); - } - - /* Gather all light own update bits. to avoid costly intersection check. */ - for (int j = 0; j < linfo->cube_len; j++) { - const EEVEE_Light *evli = linfo->light_data + linfo->shadow_cube_light_indices[j]; - /* Setup shadow cube in UBO and tag for update if necessary. */ - if (EEVEE_shadows_cube_setup(linfo, evli, effects->taa_current_sample - 1)) { - BLI_BITMAP_ENABLE(&linfo->sh_cube_update[0], j); - } - } - - /* TODO(fclem): This part can be slow, optimize it. */ - EEVEE_BoundBox *bbox = backbuffer->bbox; - BoundSphere *bsphere = linfo->shadow_bounds; - /* Search for deleted shadow casters or if shcaster WAS in shadow radius. */ - for (int i = 0; i < backbuffer->count; i++) { - /* If the shadow-caster has been deleted or updated. */ - if (BLI_BITMAP_TEST(backbuffer->update, i)) { - for (int j = 0; j < linfo->cube_len; j++) { - if (!BLI_BITMAP_TEST(&linfo->sh_cube_update[0], j)) { - if (sphere_bbox_intersect(&bsphere[j], &bbox[i])) { - BLI_BITMAP_ENABLE(&linfo->sh_cube_update[0], j); - } - } - } - } - } - /* Search for updates in current shadow casters. */ - bbox = frontbuffer->bbox; - for (int i = 0; i < frontbuffer->count; i++) { - /* If the shadow-caster has been updated. */ - if (BLI_BITMAP_TEST(frontbuffer->update, i)) { - for (int j = 0; j < linfo->cube_len; j++) { - if (!BLI_BITMAP_TEST(&linfo->sh_cube_update[0], j)) { - if (sphere_bbox_intersect(&bsphere[j], &bbox[i])) { - BLI_BITMAP_ENABLE(&linfo->sh_cube_update[0], j); - } - } - } - } - } - - /* Resize shcasters buffers if too big. */ - if (frontbuffer->alloc_count - frontbuffer->count > SH_CASTER_ALLOC_CHUNK) { - frontbuffer->alloc_count = (frontbuffer->count / SH_CASTER_ALLOC_CHUNK) * - SH_CASTER_ALLOC_CHUNK; - frontbuffer->alloc_count += (frontbuffer->count % SH_CASTER_ALLOC_CHUNK != 0) ? - SH_CASTER_ALLOC_CHUNK : - 0; - frontbuffer->bbox = MEM_reallocN(frontbuffer->bbox, - sizeof(EEVEE_BoundBox) * frontbuffer->alloc_count); - BLI_BITMAP_RESIZE(frontbuffer->update, frontbuffer->alloc_count); - } -} - -void EEVEE_shadows_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, DRWView *view) -{ - EEVEE_LightsInfo *linfo = sldata->lights; - - int saved_ray_type = sldata->common_data.ray_type; - - /* Precompute all shadow/view test before rendering and trashing the culling cache. */ - BLI_bitmap *cube_visible = BLI_BITMAP_NEW_ALLOCA(MAX_SHADOW_CUBE); - bool any_visible = linfo->cascade_len > 0; - for (int cube = 0; cube < linfo->cube_len; cube++) { - if (DRW_culling_sphere_test(view, linfo->shadow_bounds + cube)) { - BLI_BITMAP_ENABLE(cube_visible, cube); - any_visible = true; - } - } - - if (any_visible) { - sldata->common_data.ray_type = EEVEE_RAY_SHADOW; - GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); - } - - DRW_stats_group_start("Cube Shadow Maps"); - { - for (int cube = 0; cube < linfo->cube_len; cube++) { - if (BLI_BITMAP_TEST(cube_visible, cube) && BLI_BITMAP_TEST(linfo->sh_cube_update, cube)) { - EEVEE_shadows_draw_cubemap(sldata, vedata, cube); - } - } - } - DRW_stats_group_end(); - - DRW_stats_group_start("Cascaded Shadow Maps"); - { - for (int cascade = 0; cascade < linfo->cascade_len; cascade++) { - EEVEE_shadows_draw_cascades(sldata, vedata, view, cascade); - } - } - DRW_stats_group_end(); - - DRW_view_set_active(view); - - GPU_uniformbuf_update(sldata->shadow_ubo, &linfo->shadow_data); /* Update all data at once */ - - if (any_visible) { - sldata->common_data.ray_type = saved_ray_type; - GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); - } -} - -/* -------------------------------------------------------------------- */ -/** \name Render Passes - * \{ */ - -void EEVEE_shadow_output_init(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - uint UNUSED(tot_samples)) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_PassList *psl = vedata->psl; - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - - /* Create FrameBuffer. */ - const eGPUTextureFormat texture_format = GPU_R32F; - DRW_texture_ensure_fullscreen_2d(&txl->shadow_accum, texture_format, 0); - - GPU_framebuffer_ensure_config(&fbl->shadow_accum_fb, - {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->shadow_accum)}); - - /* Create Pass and shgroup. */ - DRW_PASS_CREATE(psl->shadow_accum_pass, - DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ADD_FULL); - DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_shadow_accum_sh_get(), - psl->shadow_accum_pass); - DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); - DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); - DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); - DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); - DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool); - DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool); - - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); -} - -void EEVEE_shadow_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_PassList *psl = vedata->psl; - EEVEE_EffectsInfo *effects = vedata->stl->effects; - - if (fbl->shadow_accum_fb != NULL) { - GPU_framebuffer_bind(fbl->shadow_accum_fb); - - /* Clear texture. */ - if (effects->taa_current_sample == 1) { - const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPU_framebuffer_clear_color(fbl->shadow_accum_fb, clear); - } - - DRW_draw_pass(psl->shadow_accum_pass); - - /* Restore */ - GPU_framebuffer_bind(fbl->main_fb); - } -} - -/** \} */ diff --git a/source/blender/draw/engines/eevee/eevee_shadows_cascade.c b/source/blender/draw/engines/eevee/eevee_shadows_cascade.c deleted file mode 100644 index 22ee821933c..00000000000 --- a/source/blender/draw/engines/eevee/eevee_shadows_cascade.c +++ /dev/null @@ -1,437 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2019, Blender Foundation. - */ - -/** \file - * \ingroup EEVEE - */ - -#include "BLI_rect.h" -#include "BLI_sys_types.h" /* bool */ - -#include "BKE_object.h" - -#include "eevee_private.h" - -#include "BLI_rand.h" /* needs to be after for some reason. */ - -void EEVEE_shadows_cascade_add(EEVEE_LightsInfo *linfo, EEVEE_Light *evli, Object *ob) -{ - if (linfo->cascade_len >= MAX_SHADOW_CASCADE) { - return; - } - - const Light *la = (Light *)ob->data; - EEVEE_Shadow *sh_data = linfo->shadow_data + linfo->shadow_len; - EEVEE_ShadowCascade *csm_data = linfo->shadow_cascade_data + linfo->cascade_len; - EEVEE_ShadowCascadeRender *csm_render = linfo->shadow_cascade_render + linfo->cascade_len; - - eevee_contact_shadow_setup(la, sh_data); - - linfo->shadow_cascade_light_indices[linfo->cascade_len] = linfo->num_light; - evli->shadow_id = linfo->shadow_len++; - sh_data->type_data_id = linfo->cascade_len++; - csm_data->tex_id = linfo->num_cascade_layer; - csm_render->cascade_fade = la->cascade_fade; - csm_render->cascade_count = la->cascade_count; - csm_render->cascade_exponent = la->cascade_exponent; - csm_render->cascade_max_dist = la->cascade_max_dist; - csm_render->original_bias = max_ff(la->bias, 0.0f); - - linfo->num_cascade_layer += la->cascade_count; -} - -static void shadow_cascade_random_matrix_set(float mat[4][4], float radius, int sample_ofs) -{ - float jitter[3]; -#ifndef DEBUG_SHADOW_DISTRIBUTION - EEVEE_sample_ellipse(sample_ofs, mat[0], mat[1], radius, radius, jitter); -#else - for (int i = 0; i <= sample_ofs; i++) { - EEVEE_sample_ellipse(i, mat[0], mat[1], radius, radius, jitter); - float p[3]; - add_v3_v3v3(p, jitter, mat[2]); - DRW_debug_sphere(p, 0.01f, (float[4]){1.0f, (sample_ofs == i) ? 1.0f : 0.0f, 0.0f, 1.0f}); - } -#endif - add_v3_v3(mat[2], jitter); - orthogonalize_m4(mat, 2); -} - -static double round_to_digits(double value, int digits) -{ - double factor = pow(10.0, digits - ceil(log10(fabs(value)))); - return round(value * factor) / factor; -} - -static void frustum_min_bounding_sphere(const float corners[8][3], - float r_center[3], - float *r_radius) -{ -#if 0 /* Simple solution but waste too much space. */ - float minvec[3], maxvec[3]; - - /* compute the bounding box */ - INIT_MINMAX(minvec, maxvec); - for (int i = 0; i < 8; i++) { - minmax_v3v3_v3(minvec, maxvec, corners[i]); - } - - /* compute the bounding sphere of this box */ - r_radius = len_v3v3(minvec, maxvec) * 0.5f; - add_v3_v3v3(r_center, minvec, maxvec); - mul_v3_fl(r_center, 0.5f); -#else - /* Find averaged center. */ - zero_v3(r_center); - for (int i = 0; i < 8; i++) { - add_v3_v3(r_center, corners[i]); - } - mul_v3_fl(r_center, 1.0f / 8.0f); - - /* Search the largest distance from the sphere center. */ - *r_radius = 0.0f; - for (int i = 0; i < 8; i++) { - float rad = len_squared_v3v3(corners[i], r_center); - if (rad > *r_radius) { - *r_radius = rad; - } - } - - /* TODO: try to reduce the radius further by moving the center. - * Remember we need a __stable__ solution! */ - - /* Try to reduce float imprecision leading to shimmering. */ - *r_radius = (float)round_to_digits(sqrtf(*r_radius), 3); -#endif -} - -static void eevee_shadow_cascade_setup(EEVEE_LightsInfo *linfo, - EEVEE_Light *evli, - DRWView *view, - float view_near, - float view_far, - int sample_ofs) -{ - EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id; - EEVEE_ShadowCascade *csm_data = linfo->shadow_cascade_data + (int)shdw_data->type_data_id; - EEVEE_ShadowCascadeRender *csm_render = linfo->shadow_cascade_render + - (int)shdw_data->type_data_id; - int cascade_nbr = csm_render->cascade_count; - float cascade_fade = csm_render->cascade_fade; - float cascade_max_dist = csm_render->cascade_max_dist; - float cascade_exponent = csm_render->cascade_exponent; - - float jitter_ofs[2]; - double ht_point[2]; - double ht_offset[2] = {0.0, 0.0}; - const uint ht_primes[2] = {2, 3}; - - BLI_halton_2d(ht_primes, ht_offset, sample_ofs, ht_point); - - /* Not really sure why we need 4.0 factor here. */ - jitter_ofs[0] = (ht_point[0] * 2.0 - 1.0) * 4.0 / linfo->shadow_cascade_size; - jitter_ofs[1] = (ht_point[1] * 2.0 - 1.0) * 4.0 / linfo->shadow_cascade_size; - - /* Camera Matrices */ - float persinv[4][4], vp_projmat[4][4]; - DRW_view_persmat_get(view, persinv, true); - DRW_view_winmat_get(view, vp_projmat, false); - bool is_persp = DRW_view_is_persp_get(view); - - /* obmat = Object Space > World Space */ - /* viewmat = World Space > View Space */ - float(*viewmat)[4] = csm_render->viewmat; - eevee_light_matrix_get(evli, viewmat); - /* At this point, viewmat == normalize_m4(obmat) */ - - if (linfo->soft_shadows) { - shadow_cascade_random_matrix_set(viewmat, evli->radius, sample_ofs); - } - - copy_m4_m4(csm_render->viewinv, viewmat); - invert_m4(viewmat); - - copy_v3_v3(csm_data->shadow_vec, csm_render->viewinv[2]); - - /* Compute near and far value based on all shadow casters cumulated AABBs. */ - float sh_near = -1.0e30f, sh_far = 1.0e30f; - BoundBox shcaster_bounds; - BKE_boundbox_init_from_minmax( - &shcaster_bounds, linfo->shcaster_aabb.min, linfo->shcaster_aabb.max); -#ifdef DEBUG_CSM - float dbg_col1[4] = {1.0f, 0.5f, 0.6f, 1.0f}; - DRW_debug_bbox(&shcaster_bounds, dbg_col1); -#endif - for (int i = 0; i < 8; i++) { - mul_m4_v3(viewmat, shcaster_bounds.vec[i]); - sh_near = max_ff(sh_near, shcaster_bounds.vec[i][2]); - sh_far = min_ff(sh_far, shcaster_bounds.vec[i][2]); - } -#ifdef DEBUG_CSM - float dbg_col2[4] = {0.5f, 1.0f, 0.6f, 1.0f}; - float pts[2][3] = {{0.0, 0.0, sh_near}, {0.0, 0.0, sh_far}}; - mul_m4_v3(csm_render->viewinv, pts[0]); - mul_m4_v3(csm_render->viewinv, pts[1]); - DRW_debug_sphere(pts[0], 1.0f, dbg_col1); - DRW_debug_sphere(pts[1], 1.0f, dbg_col2); -#endif - /* The rest of the function is assuming inverted Z. */ - /* Add a little bias to avoid invalid matrices. */ - sh_far = -(sh_far - 1e-3); - sh_near = -sh_near; - - /* The technique consists into splitting - * the view frustum into several sub-frustum - * that are individually receiving one shadow map */ - - float csm_start, csm_end; - - if (is_persp) { - csm_start = view_near; - csm_end = max_ff(view_far, -cascade_max_dist); - /* Avoid artifacts */ - csm_end = min_ff(view_near, csm_end); - } - else { - csm_start = -view_far; - csm_end = view_far; - } - - /* init near/far */ - for (int c = 0; c < MAX_CASCADE_NUM; c++) { - csm_data->split_start[c] = csm_end; - csm_data->split_end[c] = csm_end; - } - - /* Compute split planes */ - float splits_start_ndc[MAX_CASCADE_NUM]; - float splits_end_ndc[MAX_CASCADE_NUM]; - - { - /* Nearest plane */ - float p[4] = {1.0f, 1.0f, csm_start, 1.0f}; - /* TODO: we don't need full m4 multiply here */ - mul_m4_v4(vp_projmat, p); - splits_start_ndc[0] = p[2]; - if (is_persp) { - splits_start_ndc[0] /= p[3]; - } - } - - { - /* Farthest plane */ - float p[4] = {1.0f, 1.0f, csm_end, 1.0f}; - /* TODO: we don't need full m4 multiply here */ - mul_m4_v4(vp_projmat, p); - splits_end_ndc[cascade_nbr - 1] = p[2]; - if (is_persp) { - splits_end_ndc[cascade_nbr - 1] /= p[3]; - } - } - - csm_data->split_start[0] = csm_start; - csm_data->split_end[cascade_nbr - 1] = csm_end; - - for (int c = 1; c < cascade_nbr; c++) { - /* View Space */ - float linear_split = interpf(csm_end, csm_start, c / (float)cascade_nbr); - float exp_split = csm_start * powf(csm_end / csm_start, c / (float)cascade_nbr); - - if (is_persp) { - csm_data->split_start[c] = interpf(exp_split, linear_split, cascade_exponent); - } - else { - csm_data->split_start[c] = linear_split; - } - csm_data->split_end[c - 1] = csm_data->split_start[c]; - - /* Add some overlap for smooth transition */ - csm_data->split_start[c] = interpf((c > 1) ? csm_data->split_end[c - 2] : - csm_data->split_start[0], - csm_data->split_end[c - 1], - cascade_fade); - - /* NDC Space */ - { - float p[4] = {1.0f, 1.0f, csm_data->split_start[c], 1.0f}; - /* TODO: we don't need full m4 multiply here */ - mul_m4_v4(vp_projmat, p); - splits_start_ndc[c] = p[2]; - - if (is_persp) { - splits_start_ndc[c] /= p[3]; - } - } - - { - float p[4] = {1.0f, 1.0f, csm_data->split_end[c - 1], 1.0f}; - /* TODO: we don't need full m4 multiply here */ - mul_m4_v4(vp_projmat, p); - splits_end_ndc[c - 1] = p[2]; - - if (is_persp) { - splits_end_ndc[c - 1] /= p[3]; - } - } - } - - /* Set last cascade split fade distance into the first split_start. */ - float prev_split = (cascade_nbr > 1) ? csm_data->split_end[cascade_nbr - 2] : - csm_data->split_start[0]; - csm_data->split_start[0] = interpf( - prev_split, csm_data->split_end[cascade_nbr - 1], cascade_fade); - - /* For each cascade */ - for (int c = 0; c < cascade_nbr; c++) { - float(*projmat)[4] = csm_render->projmat[c]; - /* Given 8 frustum corners */ - float corners[8][3] = { - /* Near Cap */ - {1.0f, -1.0f, splits_start_ndc[c]}, - {-1.0f, -1.0f, splits_start_ndc[c]}, - {-1.0f, 1.0f, splits_start_ndc[c]}, - {1.0f, 1.0f, splits_start_ndc[c]}, - /* Far Cap */ - {1.0f, -1.0f, splits_end_ndc[c]}, - {-1.0f, -1.0f, splits_end_ndc[c]}, - {-1.0f, 1.0f, splits_end_ndc[c]}, - {1.0f, 1.0f, splits_end_ndc[c]}, - }; - - /* Transform them into world space */ - for (int i = 0; i < 8; i++) { - mul_project_m4_v3(persinv, corners[i]); - } - - float center[3]; - frustum_min_bounding_sphere(corners, center, &(csm_render->radius[c])); - -#ifdef DEBUG_CSM - float dbg_col[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - if (c < 3) { - dbg_col[c] = 1.0f; - } - DRW_debug_bbox((BoundBox *)&corners, dbg_col); - DRW_debug_sphere(center, csm_render->radius[c], dbg_col); -#endif - - /* Project into light-space. */ - mul_m4_v3(viewmat, center); - - /* Snap projection center to nearest texel to cancel shimmering. */ - float shadow_origin[2], shadow_texco[2]; - /* Light to texture space. */ - mul_v2_v2fl( - shadow_origin, center, linfo->shadow_cascade_size / (2.0f * csm_render->radius[c])); - - /* Find the nearest texel. */ - shadow_texco[0] = roundf(shadow_origin[0]); - shadow_texco[1] = roundf(shadow_origin[1]); - - /* Compute offset. */ - sub_v2_v2(shadow_texco, shadow_origin); - /* Texture to light space. */ - mul_v2_fl(shadow_texco, (2.0f * csm_render->radius[c]) / linfo->shadow_cascade_size); - - /* Apply offset. */ - add_v2_v2(center, shadow_texco); - - /* Expand the projection to cover frustum range */ - rctf rect_cascade; - BLI_rctf_init_pt_radius(&rect_cascade, center, csm_render->radius[c]); - orthographic_m4(projmat, - rect_cascade.xmin, - rect_cascade.xmax, - rect_cascade.ymin, - rect_cascade.ymax, - sh_near, - sh_far); - - /* Anti-Aliasing */ - if (linfo->soft_shadows) { - add_v2_v2(projmat[3], jitter_ofs); - } - - float viewprojmat[4][4]; - mul_m4_m4m4(viewprojmat, projmat, viewmat); - mul_m4_m4m4(csm_data->shadowmat[c], texcomat, viewprojmat); - -#ifdef DEBUG_CSM - DRW_debug_m4_as_bbox(viewprojmat, dbg_col, true); -#endif - } - - /* Bias is in clip-space, divide by range. */ - shdw_data->bias = csm_render->original_bias * 0.05f / fabsf(sh_far - sh_near); - shdw_data->near = sh_near; - shdw_data->far = sh_far; -} - -static void eevee_ensure_cascade_views(EEVEE_ShadowCascadeRender *csm_render, - DRWView *view[MAX_CASCADE_NUM]) -{ - for (int i = 0; i < csm_render->cascade_count; i++) { - if (view[i] == NULL) { - view[i] = DRW_view_create(csm_render->viewmat, csm_render->projmat[i], NULL, NULL, NULL); - } - else { - DRW_view_update(view[i], csm_render->viewmat, csm_render->projmat[i], NULL, NULL); - } - } -} - -void EEVEE_shadows_draw_cascades(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - DRWView *view, - int cascade_index) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - EEVEE_PrivateData *g_data = stl->g_data; - EEVEE_LightsInfo *linfo = sldata->lights; - - EEVEE_Light *evli = linfo->light_data + linfo->shadow_cascade_light_indices[cascade_index]; - EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id; - EEVEE_ShadowCascade *csm_data = linfo->shadow_cascade_data + (int)shdw_data->type_data_id; - EEVEE_ShadowCascadeRender *csm_render = linfo->shadow_cascade_render + - (int)shdw_data->type_data_id; - - float near = DRW_view_near_distance_get(view); - float far = DRW_view_far_distance_get(view); - - eevee_shadow_cascade_setup(linfo, evli, view, near, far, effects->taa_current_sample - 1); - - /* Meh, Reusing the cube views. */ - BLI_assert(MAX_CASCADE_NUM <= 6); - eevee_ensure_cascade_views(csm_render, g_data->cube_views); - - /* Render shadow cascades */ - /* Render cascade separately: seems to be faster for the general case. - * The only time it's more beneficial is when the CPU culling overhead - * outweigh the instancing overhead. which is rarely the case. */ - for (int j = 0; j < csm_render->cascade_count; j++) { - DRW_view_set_active(g_data->cube_views[j]); - int layer = csm_data->tex_id + j; - GPU_framebuffer_texture_layer_attach( - sldata->shadow_fb, sldata->shadow_cascade_pool, 0, layer, 0); - GPU_framebuffer_bind(sldata->shadow_fb); - GPU_framebuffer_clear_depth(sldata->shadow_fb, 1.0f); - DRW_draw_pass(psl->shadow_pass); - } -} diff --git a/source/blender/draw/engines/eevee/eevee_shadows_cube.c b/source/blender/draw/engines/eevee/eevee_shadows_cube.c deleted file mode 100644 index ed9f05df13c..00000000000 --- a/source/blender/draw/engines/eevee/eevee_shadows_cube.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2019, Blender Foundation. - */ - -/** \file - * \ingroup EEVEE - */ - -#include "eevee_private.h" - -void EEVEE_shadows_cube_add(EEVEE_LightsInfo *linfo, EEVEE_Light *evli, Object *ob) -{ - if (linfo->cube_len >= MAX_SHADOW_CUBE) { - return; - } - - const Light *la = (Light *)ob->data; - EEVEE_Shadow *sh_data = linfo->shadow_data + linfo->shadow_len; - - /* Always update dupli lights as EEVEE_LightEngineData is not saved. - * Same issue with dupli shadow casters. */ - bool update = (ob->base_flag & BASE_FROM_DUPLI) != 0; - if (!update) { - EEVEE_LightEngineData *led = EEVEE_light_data_ensure(ob); - if (led->need_update) { - update = true; - led->need_update = false; - } - } - - if (update) { - BLI_BITMAP_ENABLE(&linfo->sh_cube_update[0], linfo->cube_len); - } - - sh_data->near = max_ff(la->clipsta, 1e-8f); - sh_data->bias = max_ff(la->bias * 0.05f, 0.0f); - eevee_contact_shadow_setup(la, sh_data); - - /* Saving light bounds for later. */ - BoundSphere *cube_bound = linfo->shadow_bounds + linfo->cube_len; - copy_v3_v3(cube_bound->center, evli->position); - cube_bound->radius = sqrt(1.0f / evli->invsqrdist); - - linfo->shadow_cube_light_indices[linfo->cube_len] = linfo->num_light; - evli->shadow_id = linfo->shadow_len++; - sh_data->type_data_id = linfo->cube_len++; - - /* Same as linfo->cube_len, no need to save. */ - linfo->num_cube_layer++; -} - -static void shadow_cube_random_position_set(const EEVEE_Light *evli, - int sample_ofs, - float ws_sample_pos[3]) -{ - float jitter[3]; -#ifdef DEBUG_SHADOW_DISTRIBUTION - int i = 0; -start: -#else - int i = sample_ofs; -#endif - switch ((int)evli->light_type) { - case LA_AREA: - EEVEE_sample_rectangle(i, evli->rightvec, evli->upvec, evli->sizex, evli->sizey, jitter); - break; - case (int)LAMPTYPE_AREA_ELLIPSE: - EEVEE_sample_ellipse(i, evli->rightvec, evli->upvec, evli->sizex, evli->sizey, jitter); - break; - default: - EEVEE_sample_ball(i, evli->radius, jitter); - } -#ifdef DEBUG_SHADOW_DISTRIBUTION - float p[3]; - add_v3_v3v3(p, jitter, ws_sample_pos); - DRW_debug_sphere(p, 0.01f, (float[4]){1.0f, (sample_ofs == i) ? 1.0f : 0.0f, 0.0f, 1.0f}); - if (i++ < sample_ofs) { - goto start; - } -#endif - add_v3_v3(ws_sample_pos, jitter); -} - -bool EEVEE_shadows_cube_setup(EEVEE_LightsInfo *linfo, const EEVEE_Light *evli, int sample_ofs) -{ - EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id; - EEVEE_ShadowCube *cube_data = linfo->shadow_cube_data + (int)shdw_data->type_data_id; - - eevee_light_matrix_get(evli, cube_data->shadowmat); - - shdw_data->far = max_ff(sqrt(1.0f / evli->invsqrdist), 3e-4); - shdw_data->near = min_ff(shdw_data->near, shdw_data->far - 1e-4); - - bool update = false; - - if (linfo->soft_shadows) { - shadow_cube_random_position_set(evli, sample_ofs, cube_data->shadowmat[3]); - /* Update if position changes (avoid infinite update if soft shadows does not move). - * Other changes are caught by depsgraph tagging. This one is for update between samples. */ - update = !compare_v3v3(cube_data->shadowmat[3], cube_data->position, 1e-10f); - /** - * Anti-Aliasing jitter: Add random rotation. - * - * The 2.0 factor is because texel angular size is not even across the cube-map, - * so we make the rotation range a bit bigger. - * This will not blur the shadow even if the spread is too big since we are just - * rotating the shadow cube-map. - * Note that this may be a rough approximation an may not converge to a perfectly - * smooth shadow (because sample distribution is quite non-uniform) but is enough - * in practice. - */ - /* NOTE: this has implication for spotlight rendering optimization - * (see EEVEE_shadows_draw_cubemap). */ - float angular_texel_size = 2.0f * DEG2RADF(90) / (float)linfo->shadow_cube_size; - EEVEE_random_rotation_m4(sample_ofs, angular_texel_size, cube_data->shadowmat); - } - - copy_v3_v3(cube_data->position, cube_data->shadowmat[3]); - invert_m4(cube_data->shadowmat); - - return update; -} - -static void eevee_ensure_cube_views( - float near, float far, int cube_res, const float viewmat[4][4], DRWView *view[6]) -{ - float winmat[4][4]; - float side = near; - - /* TODO: shadow-cube array. */ - if (true) { - /* This half texel offset is used to ensure correct filtering between faces. */ - /* FIXME: This exhibit float precision issue with lower cube_res. - * But it seems to be caused by the perspective_m4. */ - side *= ((float)cube_res + 1.0f) / (float)(cube_res); - } - - perspective_m4(winmat, -side, side, -side, side, near, far); - - for (int i = 0; i < 6; i++) { - float tmp[4][4]; - mul_m4_m4m4(tmp, cubefacemat[i], viewmat); - - if (view[i] == NULL) { - view[i] = DRW_view_create(tmp, winmat, NULL, NULL, NULL); - } - else { - DRW_view_update(view[i], tmp, winmat, NULL, NULL); - } - } -} - -/* Does a spot angle fits a single cubeface. */ -static bool spot_angle_fit_single_face(const EEVEE_Light *evli) -{ - /* alpha = spot/cone half angle. */ - /* beta = scaled spot/cone half angle. */ - float cos_alpha = evli->spotsize; - float sin_alpha = sqrtf(max_ff(0.0f, 1.0f - cos_alpha * cos_alpha)); - float cos_beta = min_ff(cos_alpha / hypotf(cos_alpha, sin_alpha * evli->sizex), - cos_alpha / hypotf(cos_alpha, sin_alpha * evli->sizey)); - /* Don't use 45 degrees because AA jitter can offset the face. */ - return cos_beta > cosf(DEG2RADF(42.0f)); -} - -void EEVEE_shadows_draw_cubemap(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, int cube_index) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_PrivateData *g_data = stl->g_data; - EEVEE_LightsInfo *linfo = sldata->lights; - - EEVEE_Light *evli = linfo->light_data + linfo->shadow_cube_light_indices[cube_index]; - EEVEE_Shadow *shdw_data = linfo->shadow_data + (int)evli->shadow_id; - EEVEE_ShadowCube *cube_data = linfo->shadow_cube_data + (int)shdw_data->type_data_id; - - eevee_ensure_cube_views(shdw_data->near, - shdw_data->far, - linfo->shadow_cube_size, - cube_data->shadowmat, - g_data->cube_views); - - /* Render shadow cube */ - /* Render 6 faces separately: seems to be faster for the general case. - * The only time it's more beneficial is when the CPU culling overhead - * outweigh the instancing overhead. which is rarely the case. */ - for (int j = 0; j < 6; j++) { - /* Optimization: Only render the needed faces. */ - /* Skip all but -Z face. */ - if (evli->light_type == LA_SPOT && j != 5 && spot_angle_fit_single_face(evli)) { - continue; - } - /* Skip +Z face. */ - if (evli->light_type != LA_LOCAL && j == 4) { - continue; - } - /* TODO(fclem): some cube sides can be invisible in the main views. Cull them. */ - // if (frustum_intersect(g_data->cube_views[j], main_view)) - // continue; - - DRW_view_set_active(g_data->cube_views[j]); - int layer = cube_index * 6 + j; - GPU_framebuffer_texture_layer_attach(sldata->shadow_fb, sldata->shadow_cube_pool, 0, layer, 0); - GPU_framebuffer_bind(sldata->shadow_fb); - GPU_framebuffer_clear_depth(sldata->shadow_fb, 1.0f); - DRW_draw_pass(psl->shadow_pass); - } - - BLI_BITMAP_SET(&linfo->sh_cube_update[0], cube_index, false); -} diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.c b/source/blender/draw/engines/eevee/eevee_subsurface.c deleted file mode 100644 index 48c24d138e6..00000000000 --- a/source/blender/draw/engines/eevee/eevee_subsurface.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - * - * Screen space subsurface scattering technique. - */ - -#include "DRW_render.h" - -#include "BLI_string_utils.h" - -#include "DEG_depsgraph_query.h" - -#include "GPU_capabilities.h" -#include "GPU_material.h" -#include "GPU_texture.h" - -#include "eevee_private.h" - -void EEVEE_subsurface_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *UNUSED(vedata)) -{ -} - -void EEVEE_subsurface_draw_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_EffectsInfo *effects = vedata->stl->effects; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_TextureList *txl = vedata->txl; - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - const float *viewport_size = DRW_viewport_size_get(); - const int fs_size[2] = {(int)viewport_size[0], (int)viewport_size[1]}; - - if (effects->enabled_effects & EFFECT_SSS) { - /* NOTE : we need another stencil because the stencil buffer is on the same texture - * as the depth buffer we are sampling from. This could be avoided if the stencil is - * a separate texture but that needs OpenGL 4.4 or ARB_texture_stencil8. - * OR OpenGL 4.3 / ARB_ES3_compatibility if using a render-buffer instead. */ - effects->sss_stencil = DRW_texture_pool_query_2d( - fs_size[0], fs_size[1], GPU_DEPTH24_STENCIL8, &draw_engine_eevee_type); - effects->sss_blur = DRW_texture_pool_query_2d( - fs_size[0], fs_size[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type); - effects->sss_irradiance = DRW_texture_pool_query_2d( - fs_size[0], fs_size[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type); - effects->sss_radius = DRW_texture_pool_query_2d( - fs_size[0], fs_size[1], GPU_R16F, &draw_engine_eevee_type); - effects->sss_albedo = DRW_texture_pool_query_2d( - fs_size[0], fs_size[1], GPU_R11F_G11F_B10F, &draw_engine_eevee_type); - - GPUTexture *stencil_tex = effects->sss_stencil; - - if (GPU_depth_blitting_workaround()) { - /* Blitting stencil buffer does not work on macOS + Radeon Pro. - * Blit depth instead and use sss_stencil's depth as depth texture, - * and dtxl->depth as stencil mask. */ - GPU_framebuffer_ensure_config( - &fbl->sss_blit_fb, {GPU_ATTACHMENT_TEXTURE(effects->sss_stencil), GPU_ATTACHMENT_NONE}); - - stencil_tex = dtxl->depth; - } - - GPU_framebuffer_ensure_config( - &fbl->sss_blur_fb, - {GPU_ATTACHMENT_TEXTURE(stencil_tex), GPU_ATTACHMENT_TEXTURE(effects->sss_blur)}); - - GPU_framebuffer_ensure_config( - &fbl->sss_resolve_fb, - {GPU_ATTACHMENT_TEXTURE(stencil_tex), GPU_ATTACHMENT_TEXTURE(txl->color)}); - - GPU_framebuffer_ensure_config( - &fbl->sss_translucency_fb, - {GPU_ATTACHMENT_TEXTURE(stencil_tex), GPU_ATTACHMENT_TEXTURE(effects->sss_irradiance)}); - - GPU_framebuffer_ensure_config(&fbl->sss_clear_fb, - {GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(effects->sss_irradiance), - GPU_ATTACHMENT_TEXTURE(effects->sss_radius)}); - if ((stl->g_data->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) != 0) { - EEVEE_subsurface_output_init(sldata, vedata, 0); - } - else { - GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_accum_fb); - DRW_TEXTURE_FREE_SAFE(txl->sss_accum); - } - } - else { - /* Cleanup to release memory */ - GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_blur_fb); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_resolve_fb); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_clear_fb); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->sss_accum_fb); - DRW_TEXTURE_FREE_SAFE(txl->sss_accum); - effects->sss_stencil = NULL; - effects->sss_blur = NULL; - effects->sss_irradiance = NULL; - effects->sss_radius = NULL; - } -} - -void EEVEE_subsurface_output_init(EEVEE_ViewLayerData *UNUSED(sldata), - EEVEE_Data *vedata, - uint UNUSED(tot_samples)) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - - const eGPUTextureFormat texture_format_light = GPU_RGBA32F; - const bool texture_created = txl->sss_accum == NULL; - DRW_texture_ensure_fullscreen_2d(&txl->sss_accum, texture_format_light, 0); - - GPUTexture *stencil_tex = effects->sss_stencil; - - if (GPU_depth_blitting_workaround()) { - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - /* Blitting stencil buffer does not work on macOS + Radeon Pro. - * Blit depth instead and use sss_stencil's depth as depth texture, - * and dtxl->depth as stencil mask. */ - stencil_tex = dtxl->depth; - } - - GPU_framebuffer_ensure_config( - &fbl->sss_accum_fb, - {GPU_ATTACHMENT_TEXTURE(stencil_tex), GPU_ATTACHMENT_TEXTURE(txl->sss_accum)}); - - /* Clear texture. - * Due to the late initialization of the SSS it can happen that the `taa_current_sample` is - * already higher than one. This is noticeable when loading a file that has the diffuse light - * pass in look-dev mode active. `texture_created` will make sure that newly created textures - * are cleared. */ - if (effects->taa_current_sample == 1 || texture_created) { - const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPU_framebuffer_bind(fbl->sss_accum_fb); - GPU_framebuffer_clear_color(fbl->sss_accum_fb, clear); - } -} - -void EEVEE_subsurface_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - EEVEE_EffectsInfo *effects = vedata->stl->effects; - EEVEE_PassList *psl = vedata->psl; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - - effects->sss_sample_count = 1 + scene_eval->eevee.sss_samples * 2; - effects->sss_surface_count = 0; - common_data->sss_jitter_threshold = scene_eval->eevee.sss_jitter_threshold; - - /** Screen Space SubSurface Scattering overview - * TODO - */ - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL; - DRW_PASS_CREATE(psl->sss_blur_ps, state); - DRW_PASS_CREATE(psl->sss_resolve_ps, state | DRW_STATE_BLEND_ADD); - DRW_PASS_CREATE(psl->sss_translucency_ps, state | DRW_STATE_BLEND_ADD); -} - -void EEVEE_subsurface_add_pass(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - Material *ma, - DRWShadingGroup *shgrp, - struct GPUMaterial *gpumat) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - GPUTexture **depth_src = GPU_depth_blitting_workaround() ? &effects->sss_stencil : &dtxl->depth; - - struct GPUTexture *sss_tex_profile = NULL; - struct GPUUniformBuf *sss_profile = GPU_material_sss_profile_get( - gpumat, stl->effects->sss_sample_count, &sss_tex_profile); - - if (!sss_profile) { - BLI_assert_msg(0, "SSS pass requested but no SSS data was found"); - return; - } - - /* Limit of 8 bit stencil buffer. ID 255 is refraction. */ - if (effects->sss_surface_count >= 254) { - /* TODO: display message. */ - printf("Error: Too many different Subsurface shader in the scene.\n"); - return; - } - - int sss_id = ++(effects->sss_surface_count); - /* Make main pass output stencil mask. */ - DRW_shgroup_stencil_mask(shgrp, sss_id); - - { - eGPUSamplerState state = GPU_SAMPLER_DEFAULT; - - DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_subsurface_first_pass_sh_get(), - psl->sss_blur_ps); - DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); - DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", depth_src); - DRW_shgroup_uniform_texture_ref_ex(grp, "sssIrradiance", &effects->sss_irradiance, state); - DRW_shgroup_uniform_texture_ref_ex(grp, "sssRadius", &effects->sss_radius, state); - DRW_shgroup_uniform_block(grp, "sssProfile", sss_profile); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - DRW_shgroup_stencil_mask(grp, sss_id); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - grp = DRW_shgroup_create(EEVEE_shaders_subsurface_second_pass_sh_get(), psl->sss_resolve_ps); - DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); - DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", depth_src); - DRW_shgroup_uniform_texture_ref_ex(grp, "sssIrradiance", &effects->sss_blur, state); - DRW_shgroup_uniform_texture_ref_ex(grp, "sssAlbedo", &effects->sss_albedo, state); - DRW_shgroup_uniform_texture_ref_ex(grp, "sssRadius", &effects->sss_radius, state); - DRW_shgroup_uniform_block(grp, "sssProfile", sss_profile); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - DRW_shgroup_stencil_mask(grp, sss_id); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - } - - if (ma->blend_flag & MA_BL_TRANSLUCENCY) { - DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_subsurface_translucency_sh_get(), - psl->sss_translucency_ps); - DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); - DRW_shgroup_uniform_texture(grp, "sssTexProfile", sss_tex_profile); - DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", depth_src); - DRW_shgroup_uniform_texture_ref(grp, "sssRadius", &effects->sss_radius); - DRW_shgroup_uniform_texture_ref(grp, "sssShadowCubes", &sldata->shadow_cube_pool); - DRW_shgroup_uniform_texture_ref(grp, "sssShadowCascades", &sldata->shadow_cascade_pool); - DRW_shgroup_uniform_block(grp, "sssProfile", sss_profile); - DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); - DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - DRW_shgroup_stencil_mask(grp, sss_id); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - } -} - -void EEVEE_subsurface_data_render(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - - if ((effects->enabled_effects & EFFECT_SSS) != 0) { - const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - /* Clear sss_data texture only... can this be done in a more clever way? */ - GPU_framebuffer_bind(fbl->sss_clear_fb); - GPU_framebuffer_clear_color(fbl->sss_clear_fb, clear); - - GPU_framebuffer_ensure_config(&fbl->main_fb, - {GPU_ATTACHMENT_LEAVE, - GPU_ATTACHMENT_LEAVE, - GPU_ATTACHMENT_LEAVE, - GPU_ATTACHMENT_LEAVE, - GPU_ATTACHMENT_TEXTURE(effects->sss_irradiance), - GPU_ATTACHMENT_TEXTURE(effects->sss_radius), - GPU_ATTACHMENT_TEXTURE(effects->sss_albedo)}); - - GPU_framebuffer_bind(fbl->main_fb); - DRW_draw_pass(psl->material_sss_ps); - - /* Restore */ - GPU_framebuffer_ensure_config(&fbl->main_fb, - {GPU_ATTACHMENT_LEAVE, - GPU_ATTACHMENT_LEAVE, - GPU_ATTACHMENT_LEAVE, - GPU_ATTACHMENT_LEAVE, - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_NONE}); - } -} - -void EEVEE_subsurface_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_EffectsInfo *effects = stl->effects; - - if ((effects->enabled_effects & EFFECT_SSS) != 0) { - const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - - DRW_stats_group_start("SSS"); - - if (GPU_depth_blitting_workaround()) { - /* Copy depth channel */ - GPU_framebuffer_blit(fbl->main_fb, 0, fbl->sss_blit_fb, 0, GPU_DEPTH_BIT); - } - else { - /* Copy stencil channel, could be avoided (see EEVEE_subsurface_init) */ - GPU_framebuffer_blit(fbl->main_fb, 0, fbl->sss_blur_fb, 0, GPU_STENCIL_BIT); - } - - if (!DRW_pass_is_empty(psl->sss_translucency_ps)) { - /* We sample the shadow-maps using normal sampler. We need to disable Comparison mode. - * TODO(fclem): avoid this by using sampler objects. */ - GPU_texture_compare_mode(sldata->shadow_cube_pool, false); - GPU_texture_compare_mode(sldata->shadow_cascade_pool, false); - - GPU_framebuffer_bind(fbl->sss_translucency_fb); - DRW_draw_pass(psl->sss_translucency_ps); - - /* Reset original state. */ - GPU_texture_compare_mode(sldata->shadow_cube_pool, true); - GPU_texture_compare_mode(sldata->shadow_cascade_pool, true); - } - - /* 1. horizontal pass */ - GPU_framebuffer_bind(fbl->sss_blur_fb); - GPU_framebuffer_clear_color(fbl->sss_blur_fb, clear); - DRW_draw_pass(psl->sss_blur_ps); - - /* 2. vertical pass + Resolve */ - GPU_framebuffer_texture_attach(fbl->sss_resolve_fb, txl->color, 0, 0); - GPU_framebuffer_bind(fbl->sss_resolve_fb); - DRW_draw_pass(psl->sss_resolve_ps); - - GPU_framebuffer_bind(fbl->main_fb); - DRW_stats_group_end(); - } -} - -void EEVEE_subsurface_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - - if (((effects->enabled_effects & EFFECT_SSS) != 0) && (fbl->sss_accum_fb != NULL)) { - /* Copy stencil channel, could be avoided (see EEVEE_subsurface_init) */ - GPU_framebuffer_blit(fbl->main_fb, 0, fbl->sss_accum_fb, 0, GPU_STENCIL_BIT); - - /* Only do vertical pass + Resolve */ - GPU_framebuffer_bind(fbl->sss_accum_fb); - DRW_draw_pass(psl->sss_resolve_ps); - - /* Restore */ - GPU_framebuffer_bind(fbl->main_fb); - } -} diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.cc b/source/blender/draw/engines/eevee/eevee_subsurface.cc new file mode 100644 index 00000000000..83917bc4697 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_subsurface.cc @@ -0,0 +1,205 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + */ + +#include "BLI_vector.hh" + +#include "eevee_instance.hh" +#include "eevee_subsurface.hh" + +#include <iostream> + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Subsurface + * + * \{ */ + +/* TODO(fclem) Only enable this module if there is any SSS object in the scene. */ +void SubsurfaceModule::end_sync() +{ + data_.jitter_threshold = inst_.scene->eevee.sss_jitter_threshold; + if (data_.sample_len != inst_.scene->eevee.sss_samples) { + /* Convert sample count from old implementation which was using a separable filter. */ + /* TODO(fclem) better remapping. */ + // data_.sample_len = square_f(1 + 2 * inst_.scene->eevee.sss_samples); + data_.sample_len = 55; + } + + if (transmittance_tx == nullptr) { + precompute_transmittance_profile(); + } + + precompute_samples_location(); + + data_.push_update(); +} + +void SubsurfaceModule::precompute_samples_location() +{ + /* Precompute sample position with white albedo. */ + float d = burley_setup(1.0f, 1.0f); + + float rand_u = inst_.sampling.rng_get(SAMPLING_SSS_U); + float rand_v = inst_.sampling.rng_get(SAMPLING_SSS_V); + + double golden_angle = M_PI * (3.0 - sqrt(5.0)); + for (auto i : IndexRange(data_.sample_len)) { + float theta = golden_angle * i + M_PI * 2.0f * rand_u; + /* Scale using rand_v in order to keep first sample always at center. */ + float x = (1.0f + (rand_v / data_.sample_len)) * (i / (float)data_.sample_len); + float r = burley_sample(d, x); + data_.samples[i].x = cosf(theta) * r; + data_.samples[i].y = sinf(theta) * r; + data_.samples[i].z = 1.0f / burley_pdf(d, r); + } +} + +void SubsurfaceModule::precompute_transmittance_profile() +{ + Vector<float> profile(SSS_TRANSMIT_LUT_SIZE); + + /* Precompute sample position with white albedo. */ + float radius = 1.0f; + float d = burley_setup(radius, 1.0f); + + /* For each distance d we compute the radiance incoming from an hypothetical parallel plane. */ + for (auto i : IndexRange(SSS_TRANSMIT_LUT_SIZE)) { + /* Distance from the lit surface plane. + * Compute to a larger maximum distance to have a smoother falloff for all channels. */ + float lut_radius = SSS_TRANSMIT_LUT_RADIUS * radius; + float distance = lut_radius * (i + 1e-5f) / profile.size(); + /* Compute radius of the footprint on the hypothetical plane. */ + float r_fp = sqrtf(square_f(lut_radius) - square_f(distance)); + + profile[i] = 0.0f; + float area_accum = 0.0f; + for (auto j : IndexRange(SSS_TRANSMIT_LUT_STEP_RES)) { + /* Compute distance to the "shading" point through the medium. */ + float r = (r_fp * (j + 0.5f)) / SSS_TRANSMIT_LUT_STEP_RES; + float r_prev = (r_fp * (j + 0.0f)) / SSS_TRANSMIT_LUT_STEP_RES; + float r_next = (r_fp * (j + 1.0f)) / SSS_TRANSMIT_LUT_STEP_RES; + r = hypotf(r, distance); + float R = burley_eval(d, r); + /* Since the profile and configuration are radially symmetrical we + * can just evaluate it once and weight it accordingly */ + float disk_area = square_f(r_next) - square_f(r_prev); + + profile[i] += R * disk_area; + area_accum += disk_area; + } + /* Normalize over the disk. */ + profile[i] /= area_accum; + } + /* Make a smooth gradient from 1 to 0. */ + float range = profile.first() - profile.last(); + float offset = profile.last(); + for (float &value : profile) { + value = (value - offset) / range; + } + profile.first() = 1; + profile.last() = 0; + + transmittance_tx = GPU_texture_create_1d( + "SSSTransmittanceProfile", profile.size(), 1, GPU_R16F, profile.data()); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Christensen-Burley SSS model + * + * Based on: "Approximate Reflectance Profiles for Efficient Subsurface Scattering" + * by Per Christensen + * https://graphics.pixar.com/library/ApproxBSSRDF/approxbssrdfslides.pdf + * \{ */ + +float SubsurfaceModule::burley_setup(float radius, float albedo) +{ + float A = albedo; + /* Diffuse surface transmission, equation (6). */ + float s = 1.9f - A + 3.5f * square_f(A - 0.8f); + /* Mean free path length adapted to fit ancient Cubic and Gaussian models. */ + float l = 0.25 * M_1_PI * radius; + + return l / s; +} + +float SubsurfaceModule::burley_sample(float d, float x_rand) +{ + x_rand *= SSS_BURLEY_TRUNCATE_CDF; + + const float tolerance = 1e-6; + const int max_iteration_count = 10; + /* Do initial guess based on manual curve fitting, this allows us to reduce + * number of iterations to maximum 4 across the [0..1] range. We keep maximum + * number of iteration higher just to be sure we didn't miss root in some + * corner case. + */ + float r; + if (x_rand <= 0.9) { + r = exp(x_rand * x_rand * 2.4) - 1.0; + } + else { + /* TODO(sergey): Some nicer curve fit is possible here. */ + r = 15.0; + } + /* Solve against scaled radius. */ + for (int i = 0; i < max_iteration_count; i++) { + float exp_r_3 = exp(-r / 3.0); + float exp_r = exp_r_3 * exp_r_3 * exp_r_3; + float f = 1.0 - 0.25 * exp_r - 0.75 * exp_r_3 - x_rand; + float f_ = 0.25 * exp_r + 0.25 * exp_r_3; + + if (abs(f) < tolerance || f_ == 0.0) { + break; + } + + r = r - f / f_; + if (r < 0.0) { + r = 0.0; + } + } + + return r * d; +} + +float SubsurfaceModule::burley_eval(float d, float r) +{ + if (r >= SSS_BURLEY_TRUNCATE * d) { + return 0.0; + } + /* Slide 33. */ + float exp_r_3_d = expf(-r / (3.0f * d)); + float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d; + return (exp_r_d + exp_r_3_d) / (8.0f * (float)M_PI * d); +} + +float SubsurfaceModule::burley_pdf(float d, float r) +{ + return burley_eval(d, r) / SSS_BURLEY_TRUNCATE_CDF; +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.hh b/source/blender/draw/engines/eevee/eevee_subsurface.hh new file mode 100644 index 00000000000..c8b65fdfafb --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_subsurface.hh @@ -0,0 +1,84 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + */ + +#pragma once + +#include "eevee_shader.hh" +#include "eevee_shader_shared.hh" +#include "eevee_wrapper.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Subsurface + * + * \{ */ + +class Instance; + +struct SubsurfaceModule { + private: + Instance &inst_; + /** Contains samples locations. */ + SubsurfaceDataBuf data_; + /** Contains translucence profile for a single color channel. */ + GPUTexture *transmittance_tx = nullptr; + + public: + SubsurfaceModule(Instance &inst) : inst_(inst) + { + /* Force first update. */ + data_.sample_len = -1; + }; + + ~SubsurfaceModule() + { + GPU_TEXTURE_FREE_SAFE(transmittance_tx); + }; + + void end_sync(); + + const GPUUniformBuf *ubo_get(void) const + { + return data_.ubo_get(); + } + + GPUTexture **transmittance_ref_get(void) + { + return &transmittance_tx; + } + + private: + void precompute_samples_location(); + void precompute_transmittance_profile(); + + /** Christensen-Burley implementation. */ + static float burley_setup(float radius, float albedo); + static float burley_sample(float d, float x_rand); + static float burley_eval(float d, float r); + static float burley_pdf(float d, float r); +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c deleted file mode 100644 index 361fa2704c9..00000000000 --- a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c +++ /dev/null @@ -1,438 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - * - * Temporal super sampling technique - */ - -#include "DRW_render.h" - -#include "ED_screen.h" - -#include "BLI_rand.h" - -#include "DEG_depsgraph_query.h" - -#include "GPU_texture.h" -#include "eevee_private.h" - -#define FILTER_CDF_TABLE_SIZE 512 - -static struct { - /* Pixel filter table: Only blackman-harris for now. */ - bool inited; - float inverted_cdf[FILTER_CDF_TABLE_SIZE]; -} e_data = {false}; /* Engine data */ - -static float UNUSED_FUNCTION(filter_box)(float UNUSED(x)) -{ - return 1.0f; -} - -static float filter_blackman_harris(float x) -{ - /* Hardcoded 1px footprint [-0.5..0.5]. We resize later. */ - const float width = 1.0f; - x = 2.0f * M_PI * (x / width + 0.5f); - return 0.35875f - 0.48829f * cosf(x) + 0.14128f * cosf(2.0f * x) - 0.01168f * cosf(3.0f * x); -} - -/* Compute cumulative distribution function of a discrete function. */ -static void compute_cdf(float (*func)(float x), float cdf[FILTER_CDF_TABLE_SIZE]) -{ - cdf[0] = 0.0f; - /* Actual CDF evaluation. */ - for (int u = 0; u < FILTER_CDF_TABLE_SIZE - 1; u++) { - float x = (float)(u + 1) / (float)(FILTER_CDF_TABLE_SIZE - 1); - cdf[u + 1] = cdf[u] + func(x - 0.5f); /* [-0.5..0.5]. We resize later. */ - } - /* Normalize the CDF. */ - for (int u = 0; u < FILTER_CDF_TABLE_SIZE - 1; u++) { - cdf[u] /= cdf[FILTER_CDF_TABLE_SIZE - 1]; - } - /* Just to make sure. */ - cdf[FILTER_CDF_TABLE_SIZE - 1] = 1.0f; -} - -static void invert_cdf(const float cdf[FILTER_CDF_TABLE_SIZE], - float invert_cdf[FILTER_CDF_TABLE_SIZE]) -{ - for (int u = 0; u < FILTER_CDF_TABLE_SIZE; u++) { - float x = (float)u / (float)(FILTER_CDF_TABLE_SIZE - 1); - for (int i = 0; i < FILTER_CDF_TABLE_SIZE; i++) { - if (cdf[i] >= x) { - if (i == FILTER_CDF_TABLE_SIZE - 1) { - invert_cdf[u] = 1.0f; - } - else { - float t = (x - cdf[i]) / (cdf[i + 1] - cdf[i]); - invert_cdf[u] = ((float)i + t) / (float)(FILTER_CDF_TABLE_SIZE - 1); - } - break; - } - } - } -} - -/* Evaluate a discrete function table with linear interpolation. */ -static float eval_table(const float *table, float x) -{ - CLAMP(x, 0.0f, 1.0f); - x = x * (FILTER_CDF_TABLE_SIZE - 1); - - int index = min_ii((int)(x), FILTER_CDF_TABLE_SIZE - 1); - int nindex = min_ii(index + 1, FILTER_CDF_TABLE_SIZE - 1); - float t = x - index; - - return (1.0f - t) * table[index] + t * table[nindex]; -} - -static void eevee_create_cdf_table_temporal_sampling(void) -{ - float *cdf_table = MEM_mallocN(sizeof(float) * FILTER_CDF_TABLE_SIZE, "Eevee Filter CDF table"); - - float filter_width = 2.0f; /* Use a 2 pixel footprint by default. */ - - { - /* Use blackman-harris filter. */ - filter_width *= 2.0f; - compute_cdf(filter_blackman_harris, cdf_table); - } - - invert_cdf(cdf_table, e_data.inverted_cdf); - - /* Scale and offset table. */ - for (int i = 0; i < FILTER_CDF_TABLE_SIZE; i++) { - e_data.inverted_cdf[i] = (e_data.inverted_cdf[i] - 0.5f) * filter_width; - } - - MEM_freeN(cdf_table); - e_data.inited = true; -} - -void EEVEE_temporal_sampling_offset_calc(const double ht_point[2], - const float filter_size, - float r_offset[2]) -{ - r_offset[0] = eval_table(e_data.inverted_cdf, (float)(ht_point[0])) * filter_size; - r_offset[1] = eval_table(e_data.inverted_cdf, (float)(ht_point[1])) * filter_size; -} - -void EEVEE_temporal_sampling_matrices_calc(EEVEE_EffectsInfo *effects, const double ht_point[2]) -{ - const float *viewport_size = DRW_viewport_size_get(); - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - RenderData *rd = &scene->r; - - float persmat[4][4], viewmat[4][4], winmat[4][4], wininv[4][4]; - DRW_view_persmat_get(NULL, persmat, false); - DRW_view_viewmat_get(NULL, viewmat, false); - DRW_view_winmat_get(NULL, winmat, false); - DRW_view_winmat_get(NULL, wininv, true); - - float ofs[2]; - EEVEE_temporal_sampling_offset_calc(ht_point, rd->gauss, ofs); - - if (effects->taa_current_sample > 1) { - window_translate_m4(winmat, persmat, ofs[0] / viewport_size[0], ofs[1] / viewport_size[1]); - } - - /* Jitter is in pixel space. Focus distance in world space units. */ - float dof_jitter[2], focus_distance; - if (EEVEE_depth_of_field_jitter_get(effects, dof_jitter, &focus_distance)) { - /* Convert to NDC space [-1..1]. */ - dof_jitter[0] /= viewport_size[0] * 0.5f; - dof_jitter[1] /= viewport_size[1] * 0.5f; - - /* Skew the projection matrix in the ray direction and offset it to ray origin. - * Make it focus at focus_distance. */ - if (winmat[2][3] != -1.0f) { - /* Orthographic */ - add_v2_v2(winmat[2], dof_jitter); - - window_translate_m4( - winmat, persmat, dof_jitter[0] * focus_distance, dof_jitter[1] * focus_distance); - } - else { - /* Get focus distance in NDC. */ - float focus_pt[3] = {0.0f, 0.0f, -focus_distance}; - mul_project_m4_v3(winmat, focus_pt); - /* Get pixel footprint in view-space. */ - float jitter_scaled[3] = {dof_jitter[0], dof_jitter[1], focus_pt[2]}; - float center[3] = {0.0f, 0.0f, focus_pt[2]}; - mul_project_m4_v3(wininv, jitter_scaled); - mul_project_m4_v3(wininv, center); - - /* FIXME(fclem): The offset is noticeably large and the culling might make object pop out - * of the blurring radius. To fix this, use custom enlarged culling matrix. */ - sub_v2_v2v2(jitter_scaled, jitter_scaled, center); - add_v2_v2(viewmat[3], jitter_scaled); - - window_translate_m4(winmat, persmat, -dof_jitter[0], -dof_jitter[1]); - } - } - - BLI_assert(effects->taa_view != NULL); - - /* When rendering just update the view. This avoids recomputing the culling. */ - DRW_view_update_sub(effects->taa_view, viewmat, winmat); -} - -void EEVEE_temporal_sampling_update_matrices(EEVEE_Data *vedata) -{ - EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; - EEVEE_EffectsInfo *effects = stl->effects; - - double ht_point[2]; - double ht_offset[2] = {0.0, 0.0}; - const uint ht_primes[2] = {2, 3}; - - BLI_halton_2d(ht_primes, ht_offset, effects->taa_current_sample - 1, ht_point); - - EEVEE_temporal_sampling_matrices_calc(effects, ht_point); - - DRW_view_set_active(effects->taa_view); -} - -void EEVEE_temporal_sampling_reset(EEVEE_Data *vedata) -{ - vedata->stl->effects->taa_render_sample = 1; - vedata->stl->effects->taa_current_sample = 1; -} - -void EEVEE_temporal_sampling_create_view(EEVEE_Data *vedata) -{ - EEVEE_EffectsInfo *effects = vedata->stl->effects; - /* Create a sub view to disable clipping planes (if any). */ - const DRWView *default_view = DRW_view_default_get(); - float viewmat[4][4], winmat[4][4]; - DRW_view_viewmat_get(default_view, viewmat, false); - DRW_view_winmat_get(default_view, winmat, false); - effects->taa_view = DRW_view_create_sub(default_view, viewmat, winmat); - DRW_view_clip_planes_set(effects->taa_view, NULL, 0); -} - -int EEVEE_temporal_sampling_sample_count_get(const Scene *scene, const EEVEE_StorageList *stl) -{ - const bool is_render = DRW_state_is_image_render(); - int sample_count = is_render ? scene->eevee.taa_render_samples : scene->eevee.taa_samples; - int timesteps = is_render ? stl->g_data->render_timesteps : 1; - - sample_count = max_ii(0, sample_count); - sample_count = (sample_count == 0) ? TAA_MAX_SAMPLE : sample_count; - sample_count = divide_ceil_u(sample_count, timesteps); - - int dof_sample_count = EEVEE_depth_of_field_sample_count_get(stl->effects, sample_count, NULL); - sample_count = dof_sample_count * divide_ceil_u(sample_count, dof_sample_count); - return sample_count; -} - -int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - int repro_flag = 0; - - if (!e_data.inited) { - eevee_create_cdf_table_temporal_sampling(); - } - - /** - * Reset for each "redraw". When rendering using ogl render, - * we accumulate the redraw inside the drawing loop in eevee_draw_scene(). - */ - if (DRW_state_is_opengl_render()) { - effects->taa_render_sample = 1; - } - effects->bypass_drawing = false; - - EEVEE_temporal_sampling_create_view(vedata); - - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - - if ((scene_eval->eevee.taa_samples != 1) || DRW_state_is_image_render()) { - float persmat[4][4]; - - if (!DRW_state_is_image_render() && (scene_eval->eevee.flag & SCE_EEVEE_TAA_REPROJECTION)) { - repro_flag = EFFECT_TAA_REPROJECT | EFFECT_VELOCITY_BUFFER | EFFECT_DEPTH_DOUBLE_BUFFER | - EFFECT_DOUBLE_BUFFER | EFFECT_POST_BUFFER; - effects->taa_reproject_sample = ((effects->taa_reproject_sample + 1) % 16); - } - - /* Until we support reprojection, we need to make sure - * that the history buffer contains correct information. */ - bool view_is_valid = stl->g_data->valid_double_buffer; - - view_is_valid = view_is_valid && (stl->g_data->view_updated == false); - - if (draw_ctx->evil_C != NULL) { - struct wmWindowManager *wm = CTX_wm_manager(draw_ctx->evil_C); - view_is_valid = view_is_valid && (ED_screen_animation_no_scrub(wm) == NULL); - } - - effects->taa_total_sample = EEVEE_temporal_sampling_sample_count_get(scene_eval, stl); - - if (EEVEE_renderpasses_only_first_sample_pass_active(vedata)) { - view_is_valid = false; - effects->taa_total_sample = 1; - } - - /* Motion blur steps could reset the sampling when camera is animated (see T79970). */ - if (!DRW_state_is_scene_render()) { - DRW_view_persmat_get(NULL, persmat, false); - view_is_valid = view_is_valid && compare_m4m4(persmat, effects->prev_drw_persmat, FLT_MIN); - } - - /* Prevent ghosting from probe data. */ - view_is_valid = view_is_valid && (effects->prev_drw_support == DRW_state_draw_support()) && - (effects->prev_is_navigating == DRW_state_is_navigating()); - effects->prev_drw_support = DRW_state_draw_support(); - effects->prev_is_navigating = DRW_state_is_navigating(); - - if (((effects->taa_total_sample == 0) || - (effects->taa_current_sample < effects->taa_total_sample)) || - (!view_is_valid) || DRW_state_is_image_render()) { - if (view_is_valid) { - /* Viewport rendering updates the matrices in `eevee_draw_scene` */ - if (!DRW_state_is_image_render()) { - effects->taa_current_sample += 1; - repro_flag = 0; - } - } - else { - effects->taa_current_sample = 1; - } - } - else { - const bool all_shaders_compiled = stl->g_data->queued_shaders_count_prev == 0; - /* Fix Texture painting (see T79370) and shader compilation (see T78520). */ - if (DRW_state_is_navigating() || !all_shaders_compiled) { - effects->taa_current_sample = 1; - } - else { - effects->bypass_drawing = true; - } - } - - return repro_flag | EFFECT_TAA | EFFECT_DOUBLE_BUFFER | EFFECT_DEPTH_DOUBLE_BUFFER | - EFFECT_POST_BUFFER; - } - - effects->taa_current_sample = 1; - - return repro_flag; -} - -void EEVEE_temporal_sampling_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_EffectsInfo *effects = stl->effects; - - if (effects->enabled_effects & EFFECT_TAA) { - struct GPUShader *sh = EEVEE_shaders_taa_resolve_sh_get(effects->enabled_effects); - - DRW_PASS_CREATE(psl->taa_resolve, DRW_STATE_WRITE_COLOR); - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->taa_resolve); - - DRW_shgroup_uniform_texture_ref(grp, "colorHistoryBuffer", &txl->taa_history); - DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &effects->source_buffer); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - - if (effects->enabled_effects & EFFECT_TAA_REPROJECT) { - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); - DRW_shgroup_uniform_mat4(grp, "prevViewProjectionMatrix", effects->prev_drw_persmat); - } - else { - DRW_shgroup_uniform_float(grp, "alpha", &effects->taa_alpha, 1); - } - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); - } -} - -void EEVEE_temporal_sampling_draw(EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - - if ((effects->enabled_effects & (EFFECT_TAA | EFFECT_TAA_REPROJECT)) != 0) { - if ((effects->enabled_effects & EFFECT_TAA) != 0 && effects->taa_current_sample != 1) { - if (DRW_state_is_image_render()) { - /* See EEVEE_temporal_sampling_init() for more details. */ - effects->taa_alpha = 1.0f / (float)(effects->taa_render_sample); - } - else { - effects->taa_alpha = 1.0f / (float)(effects->taa_current_sample); - } - - GPU_framebuffer_bind(effects->target_buffer); - DRW_draw_pass(psl->taa_resolve); - - /* Restore the depth from sample 1. */ - GPU_framebuffer_blit(fbl->double_buffer_depth_fb, 0, fbl->main_fb, 0, GPU_DEPTH_BIT); - - SWAP_BUFFERS_TAA(); - } - else { - /* Save the depth buffer for the next frame. - * This saves us from doing anything special - * in the other mode engines. */ - GPU_framebuffer_blit(fbl->main_fb, 0, fbl->double_buffer_depth_fb, 0, GPU_DEPTH_BIT); - - /* Do reprojection for noise reduction */ - /* TODO: do AA jitter if in only render view. */ - if (!DRW_state_is_image_render() && (effects->enabled_effects & EFFECT_TAA_REPROJECT) != 0 && - stl->g_data->valid_taa_history) { - GPU_framebuffer_bind(effects->target_buffer); - DRW_draw_pass(psl->taa_resolve); - SWAP_BUFFERS_TAA(); - } - else { - struct GPUFrameBuffer *source_fb = (effects->target_buffer == fbl->main_color_fb) ? - fbl->effect_color_fb : - fbl->main_color_fb; - GPU_framebuffer_blit(source_fb, 0, fbl->taa_history_color_fb, 0, GPU_COLOR_BIT); - } - } - - /* Make each loop count when doing a render. */ - if (DRW_state_is_image_render()) { - effects->taa_render_sample += 1; - effects->taa_current_sample += 1; - } - else { - if (!DRW_state_is_playback() && - ((effects->taa_total_sample == 0) || - (effects->taa_current_sample < effects->taa_total_sample))) { - DRW_viewport_request_redraw(); - } - } - - DRW_view_persmat_get(NULL, effects->prev_drw_persmat, false); - } -} diff --git a/source/blender/draw/engines/eevee/eevee_velocity.cc b/source/blender/draw/engines/eevee/eevee_velocity.cc new file mode 100644 index 00000000000..6c221b66aec --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_velocity.cc @@ -0,0 +1,356 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * The velocity pass outputs motion vectors to use for either + * temporal re-projection or motion blur. + * + * It is the module that tracks the objects between frames updates. + */ + +#include "BKE_duplilist.h" +#include "BKE_object.h" +#include "BLI_map.hh" +#include "DEG_depsgraph_query.h" +#include "DNA_rigidbody_types.h" + +#include "eevee_instance.hh" +#include "eevee_renderpasses.hh" +#include "eevee_shader.hh" +#include "eevee_shader_shared.hh" +#include "eevee_velocity.hh" +#include "eevee_wrapper.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name VelocityModule + * + * \{ */ + +void VelocityModule::init(void) +{ + if (inst_.is_viewport()) { + /* For viewport we sync when object is evaluated and we swap at init time. + * Use next step to store the current position. This one will become the previous step after + * next swapping. */ + step_ = STEP_NEXT; + step_swap(); + } + + if (inst_.render && (inst_.render_passes.vector != nullptr)) { + /* No motion blur and the vector pass was requested. Do the step sync here. */ + const Scene *scene = inst_.scene; + float initial_time = scene->r.cfra + scene->r.subframe; + step_sync(STEP_PREVIOUS, initial_time - 1.0f); + step_sync(STEP_NEXT, initial_time + 1.0f); + inst_.set_time(initial_time); + } +} + +static void step_object_sync_render(void *velocity, + Object *ob, + RenderEngine *UNUSED(engine), + Depsgraph *UNUSED(depsgraph)) +{ + ObjectKey object_key(ob); + reinterpret_cast<VelocityModule *>(velocity)->step_object_sync(ob, object_key); +} + +void VelocityModule::step_sync(eStep step, float time) +{ + inst_.set_time(time); + step_ = step; + step_camera_sync(); + DRW_render_object_iter(this, inst_.render, inst_.depsgraph, step_object_sync_render); +} + +void VelocityModule::step_camera_sync() +{ + if (!inst_.is_viewport()) { + inst_.camera.sync(); + } + + if (step_ == STEP_NEXT) { + camera_step.next = inst_.camera.data_get(); + } + else if (step_ == STEP_PREVIOUS) { + camera_step.prev = inst_.camera.data_get(); + } +} + +/* Gather motion data from all objects in the scene. */ +void VelocityModule::step_object_sync(Object *ob, ObjectKey &object_key) +{ + if (!object_has_velocity(ob) && !object_is_deform(ob)) { + return; + } + + auto add_cb = [&]() { + inst_.sampling.reset(); + return new VelocityObjectBuf(); + }; + auto data = objects_steps.lookup_or_add_cb(object_key, add_cb); + + if (step_ == STEP_NEXT) { + copy_m4_m4(data->next_object_mat, ob->obmat); + } + else if (step_ == STEP_PREVIOUS) { + copy_m4_m4(data->prev_object_mat, ob->obmat); + } +} + +/* Moves next frame data to previous frame data. Nullify next frame data. */ +void VelocityModule::step_swap(void) +{ + for (VelocityObjectBuf *data : objects_steps.values()) { + copy_m4_m4(data->prev_object_mat, data->next_object_mat); + /* Important: This let us known if object is missing from the next time step. */ + zero_m4(data->next_object_mat); + } + camera_step.prev = static_cast<CameraData>(camera_step.next); +} + +void VelocityModule::begin_sync(void) +{ + if (inst_.is_viewport()) { + step_camera_sync(); + } +} + +/* This is the end of the current frame sync. Not the step_sync. */ +void VelocityModule::end_sync(void) +{ + Vector<ObjectKey, 1> deleted_keys; + + for (auto item : objects_steps.items()) { + /* Detect object deletion. Only do this on viewport as STEP_NEXT means current step. */ + if (inst_.is_viewport() && is_zero_m4(item.value->next_object_mat)) { + deleted_keys.append(item.key); + delete item.value; + } + else { + item.value->push_update(); + } + } + + if (deleted_keys.size() > 0) { + inst_.sampling.reset(); + } + + for (auto key : deleted_keys) { + objects_steps.remove(key); + } + + camera_step.prev.push_update(); + camera_step.next.push_update(); +} + +bool VelocityModule::object_has_velocity(const Object *ob) +{ +#if 0 + RigidBodyOb *rbo = ob->rigidbody_object; + /* Active rigidbody objects only, as only those are affected by sim. */ + const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE)); + /* For now we assume dupli objects are moving. */ + const bool is_dupli = (ob->base_flag & BASE_FROM_DUPLI) != 0; + const bool object_moves = is_dupli || has_rigidbody || BKE_object_moves_in_time(ob, true); +#else + UNUSED_VARS(ob); + /* BKE_object_moves_in_time does not work in some cases. + * Better detect non moving object after evaluation. */ + const bool object_moves = true; +#endif + return object_moves; +} + +bool VelocityModule::object_is_deform(const Object *ob) +{ + RigidBodyOb *rbo = ob->rigidbody_object; + /* Active rigidbody objects only, as only those are affected by sim. */ + const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE)); + const bool is_deform = BKE_object_is_deform_modified(inst_.scene, (Object *)ob) || + (has_rigidbody && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0); + + return is_deform; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name VelocityPass + * + * Draws velocity data from VelocityModule module to a framebuffer / texture. + * \{ */ + +void VelocityPass::sync(void) +{ + VelocityModule &velocity = inst_.velocity; + { + /* Outputs camera motion vector. */ + /* TODO(fclem) Ideally, we should run this only where the motion vectors were not written. + * But without imageLoadStore, we cannot do that without another buffer. */ + DRWState state = DRW_STATE_WRITE_COLOR; + DRW_PASS_CREATE(camera_ps_, state); + GPUShader *sh = inst_.shaders.static_shader_get(VELOCITY_CAMERA); + DRWShadingGroup *grp = DRW_shgroup_create(sh, camera_ps_); + DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_); + DRW_shgroup_uniform_block(grp, "camera_prev_block", velocity.camera_step.prev.ubo_get()); + DRW_shgroup_uniform_block(grp, "camera_next_block", velocity.camera_step.next.ubo_get()); + DRW_shgroup_uniform_block(grp, "camera_curr_block", inst_.camera.ubo_get()); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); + } + { + /* Animated objects are rendered and output the correct motion vector. */ + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL; + DRW_PASS_CREATE(object_ps_, state); + { + GPUShader *sh = inst_.shaders.static_shader_get(VELOCITY_MESH); + DRWShadingGroup *grp = mesh_grp_ = DRW_shgroup_create(sh, object_ps_); + DRW_shgroup_uniform_block(grp, "camera_prev_block", velocity.camera_step.prev.ubo_get()); + DRW_shgroup_uniform_block(grp, "camera_next_block", velocity.camera_step.next.ubo_get()); + DRW_shgroup_uniform_block(grp, "camera_curr_block", inst_.camera.ubo_get()); + } + } +} + +void VelocityPass::mesh_add(Object *ob, ObjectHandle &handle) +{ + if (inst_.is_viewport()) { + /* FIXME(fclem) As we are using original objects pointers, there is a chance the previous + * object key matches a totally different object if the scene was changed by user or python + * callback. In this case, we cannot correctly match objects between updates. + * What this means is that there will be incorrect motion vectors for these objects. + * We live with that until we have a correct way of identifying new objects. */ + if (handle.recalc & ID_RECALC_TRANSFORM) { + inst_.velocity.step_object_sync(ob, handle.object_key); + } + } + + VelocityObjectBuf **data_ptr = inst_.velocity.objects_steps.lookup_ptr(handle.object_key); + + if (data_ptr == nullptr) { + return; + } + + VelocityObjectBuf *data = *data_ptr; + + GPUBatch *geom = DRW_cache_object_surface_get(ob); + if (geom == NULL) { + return; + } + + if (!inst_.is_viewport()) { + /* Fill missing matrices if the object was hidden in previous or next frame. */ + if (is_zero_m4(data->prev_object_mat)) { + copy_m4_m4(data->prev_object_mat, ob->obmat); + } + + /* Avoid drawing object that has no motions since object_moves is always true. */ + if (/* !mb_geom->use_deform && */ /* Object deformation can happen without transform. */ + equals_m4m4(data->prev_object_mat, ob->obmat)) { + return; + } + } + else { + /* Fill missing matrices if the object was hidden in previous or next frame. */ + if (is_zero_m4(data->prev_object_mat)) { + copy_m4_m4(data->prev_object_mat, ob->obmat); + } + if (is_zero_m4(data->next_object_mat)) { + copy_m4_m4(data->next_object_mat, ob->obmat); + } + + // if (mb_geom->use_deform) { + // /* Keep to modify later (after init). */ + // mb_geom->batch = geom; + // } + + /* Avoid drawing object that has no motions since object_moves is always true. */ + if (/* !mb_geom->use_deform && */ /* Object deformation can happen without transform. */ + equals_m4m4(data->prev_object_mat, ob->obmat) && + equals_m4m4(data->next_object_mat, ob->obmat)) { + return; + } + } + + /* TODO(fclem) Use the same layout as modelBlock from draw so we can reuse the same offset + * and avoid the overhead of 1 shading group and one UBO per object. */ + DRWShadingGroup *grp = DRW_shgroup_create_sub(mesh_grp_); + DRW_shgroup_uniform_block(grp, "object_block", data->ubo_get()); + DRW_shgroup_call(grp, geom, ob); +} + +void VelocityPass::render_objects(void) +{ + DRW_draw_pass(object_ps_); +} + +void VelocityPass::resolve_camera_motion(GPUTexture *depth_tx) +{ + input_depth_tx_ = depth_tx; + DRW_draw_pass(camera_ps_); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name VelocityPass + * + * Draws velocity data from VelocityModule module to a framebuffer / texture. + * \{ */ + +void Velocity::sync(int extent[2]) +{ + /* HACK: View name should be unique and static. + * With this, we can reuse the same texture across views. */ + DrawEngineType *owner = (DrawEngineType *)view_name_.c_str(); + + /* TODO(fclem) Only allocate if needed. RG16F when only doing reprojection. */ + velocity_camera_tx_ = DRW_texture_pool_query_2d(UNPACK2(extent), GPU_RGBA16F, owner); + /* TODO(fclem) Only allocate if needed. RG16F when only doing motion blur post fx in + * panoramic camera. */ + velocity_view_tx_ = DRW_texture_pool_query_2d(UNPACK2(extent), GPU_RGBA16F, owner); + + velocity_only_fb_.ensure(GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(velocity_camera_tx_), + GPU_ATTACHMENT_TEXTURE(velocity_view_tx_)); +} + +void Velocity::render(GPUTexture *depth_tx) +{ + DRW_stats_group_start("Velocity"); + + GPU_framebuffer_bind(velocity_only_fb_); + inst_.shading_passes.velocity.resolve_camera_motion(depth_tx); + + velocity_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx), + GPU_ATTACHMENT_TEXTURE(velocity_camera_tx_), + GPU_ATTACHMENT_TEXTURE(velocity_view_tx_)); + + GPU_framebuffer_bind(velocity_fb_); + inst_.shading_passes.velocity.render_objects(); + + DRW_stats_group_end(); +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_velocity.hh b/source/blender/draw/engines/eevee/eevee_velocity.hh new file mode 100644 index 00000000000..5829737fc43 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_velocity.hh @@ -0,0 +1,171 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * The velocity pass outputs motion vectors to use for either + * temporal re-projection or motion blur. + * + * It is the module that tracks the objects between frames updates. + */ + +#pragma once + +#include "BLI_map.hh" + +#include "eevee_id_map.hh" +#include "eevee_renderpasses.hh" +#include "eevee_shader_shared.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name VelocityModule + * + * \{ */ + +/** Container for scene velocity data. */ +class VelocityModule { + public: + enum eStep { + STEP_PREVIOUS = 0, + STEP_NEXT = 1, + STEP_CURRENT = 2, + }; + + /** Map an object key to a velocity data. */ + Map<ObjectKey, VelocityObjectBuf *> objects_steps; + struct { + /** Copies of camera data. One for previous and one for next time step. */ + StructBuffer<CameraData> prev, next; + } camera_step; + + private: + Instance &inst_; + + eStep step_; + + public: + VelocityModule(Instance &inst) : inst_(inst){}; + + ~VelocityModule() + { + for (VelocityObjectBuf *data : objects_steps.values()) { + delete data; + } + } + + void init(void); + + void step_camera_sync(void); + void step_sync(eStep step, float time); + + /* Gather motion data from all objects in the scene. */ + void step_object_sync(Object *ob, ObjectKey &ob_key); + + /* Moves next frame data to previous frame data. Nullify next frame data. */ + void step_swap(void); + + void begin_sync(void); + void end_sync(void); + + private: + bool object_has_velocity(const Object *ob); + bool object_is_deform(const Object *ob); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name VelocityPass + * + * Draws velocity data from VelocityModule module to a framebuffer / texture. + * \{ */ + +class VelocityPass { + private: + Instance &inst_; + + DRWPass *object_ps_ = nullptr; + DRWPass *camera_ps_ = nullptr; + + /** Shading groups from object_ps_ */ + DRWShadingGroup *mesh_grp_; + + /** Reference only. Not owned. */ + GPUTexture *input_depth_tx_; + + public: + VelocityPass(Instance &inst) : inst_(inst){}; + + void sync(void); + + void mesh_add(Object *ob, ObjectHandle &handle); + + void render_objects(void); + void resolve_camera_motion(GPUTexture *depth_tx); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Velocity + * + * \{ */ + +/** + * Per view module. + */ +class Velocity { + private: + Instance &inst_; + + StringRefNull view_name_; + + /** Owned resources. */ + eevee::Framebuffer velocity_fb_; + eevee::Framebuffer velocity_only_fb_; + /** Draw resources. Not owned. */ + GPUTexture *velocity_camera_tx_ = nullptr; + GPUTexture *velocity_view_tx_ = nullptr; + + public: + Velocity(Instance &inst, const char *name) : inst_(inst), view_name_(name){}; + ~Velocity(){}; + + void sync(int extent[2]); + + void render(GPUTexture *depth_tx); + + /** + * Getters + **/ + GPUTexture *view_vectors_get(void) const + { + return (velocity_view_tx_ != nullptr) ? velocity_view_tx_ : velocity_camera_tx_; + } + GPUTexture *camera_vectors_get(void) const + { + return velocity_camera_tx_; + } +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_view.cc b/source/blender/draw/engines/eevee/eevee_view.cc new file mode 100644 index 00000000000..d147fabc3dc --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_view.cc @@ -0,0 +1,262 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * A view is either: + * - The entire main view. + * - A fragment of the main view (for panoramic projections). + * - A shadow map view. + * - A lightprobe view (either planar, cubemap, irradiance grid). + * + * A pass is a container for scene data. It is view agnostic but has specific logic depending on + * its type. Passes are shared between views. + */ + +#include "BKE_global.h" +#include "DRW_render.h" + +#include "eevee_instance.hh" + +#include "eevee_view.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name ShadingView + * \{ */ + +void ShadingView::init() +{ + dof_.init(); + mb_.init(); +} + +void ShadingView::sync(ivec2 render_extent_) +{ + if (inst_.camera.is_panoramic()) { + int64_t render_pixel_count = render_extent_.x * (int64_t)render_extent_.y; + /* Divide pixel count between the 6 views. Rendering to a square target. */ + extent_[0] = extent_[1] = ceilf(sqrtf(1 + (render_pixel_count / 6))); + /* TODO(fclem) Clip unused views heres. */ + is_enabled_ = true; + } + else { + extent_ = render_extent_; + /* Only enable -Z view. */ + is_enabled_ = (StringRefNull(name_) == "negZ_view"); + } + + if (!is_enabled_) { + return; + } + + /* Create views. */ + const CameraData &data = inst_.camera.data_get(); + + float viewmat[4][4], winmat[4][4]; + const float(*viewmat_p)[4] = viewmat, (*winmat_p)[4] = winmat; + if (inst_.camera.is_panoramic()) { + /* TODO(fclem) Overscans. */ + /* For now a mandatory 5% overscan for DoF. */ + float side = data.clip_near * 1.05f; + float near = data.clip_near; + float far = data.clip_far; + perspective_m4(winmat, -side, side, -side, side, near, far); + mul_m4_m4m4(viewmat, face_matrix_, data.viewmat); + } + else { + viewmat_p = data.viewmat; + winmat_p = data.winmat; + } + + main_view_ = DRW_view_create(viewmat_p, winmat_p, nullptr, nullptr, nullptr); + sub_view_ = DRW_view_create_sub(main_view_, viewmat_p, winmat_p); + render_view_ = DRW_view_create_sub(main_view_, viewmat_p, winmat_p); + + dof_.sync(winmat_p, extent_); + mb_.sync(extent_); + velocity_.sync(extent_); + rt_buffer_opaque_.sync(extent_); + rt_buffer_refract_.sync(extent_); + + { + /* Query temp textures and create framebuffers. */ + /* HACK: View name should be unique and static. + * With this, we can reuse the same texture across views. */ + DrawEngineType *owner = (DrawEngineType *)name_; + + depth_tx_ = DRW_texture_pool_query_2d(UNPACK2(extent_), GPU_DEPTH24_STENCIL8, owner); + combined_tx_ = DRW_texture_pool_query_2d(UNPACK2(extent_), GPU_RGBA16F, owner); + /* TODO(fclem) Only allocate if needed. */ + postfx_tx_ = DRW_texture_pool_query_2d(UNPACK2(extent_), GPU_RGBA16F, owner); + + view_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx_), GPU_ATTACHMENT_TEXTURE(combined_tx_)); + + gbuffer_.sync(depth_tx_, combined_tx_, owner); + } +} + +void ShadingView::render(void) +{ + if (!is_enabled_) { + return; + } + + float color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + + update_view(); + + DRW_stats_group_start(name_); + DRW_view_set_active(render_view_); + + GPU_framebuffer_bind(view_fb_); + GPU_framebuffer_clear_color_depth(view_fb_, color, 1.0f); + + if (inst_.lookdev.render_background() == false) { + inst_.shading_passes.background.render(); + } + + inst_.shading_passes.deferred.render(render_view_, + gbuffer_, + hiz_front_, + hiz_back_, + rt_buffer_opaque_, + rt_buffer_refract_, + view_fb_); + + inst_.lightprobes.draw_cache_display(); + + inst_.lookdev.render_overlay(view_fb_); + + inst_.shading_passes.forward.render(render_view_, gbuffer_, hiz_front_, view_fb_); + + inst_.lights.debug_draw(view_fb_, hiz_front_); + inst_.shadows.debug_draw(view_fb_, hiz_front_); + + velocity_.render(depth_tx_); + + if (inst_.render_passes.vector) { + inst_.render_passes.vector->accumulate(velocity_.camera_vectors_get(), sub_view_); + } + + GPUTexture *final_radiance_tx = render_post(combined_tx_); + + if (inst_.render_passes.combined) { + inst_.render_passes.combined->accumulate(final_radiance_tx, sub_view_); + } + + if (inst_.render_passes.depth) { + inst_.render_passes.depth->accumulate(depth_tx_, sub_view_); + } + + DRW_stats_group_end(); +} + +GPUTexture *ShadingView::render_post(GPUTexture *input_tx) +{ + GPUTexture *velocity_tx = velocity_.view_vectors_get(); + GPUTexture *output_tx = postfx_tx_; + /* Swapping is done internally. Actual output is set to the next input. */ + dof_.render(depth_tx_, &input_tx, &output_tx); + mb_.render(depth_tx_, velocity_tx, &input_tx, &output_tx); + return input_tx; +} + +void ShadingView::update_view(void) +{ + float viewmat[4][4], winmat[4][4]; + DRW_view_viewmat_get(main_view_, viewmat, false); + DRW_view_winmat_get(main_view_, winmat, false); + + /* Anti-Aliasing / Super-Sampling jitter. */ + float jitter_u = 2.0f * (inst_.sampling.rng_get(SAMPLING_FILTER_U) - 0.5f) / extent_[0]; + float jitter_v = 2.0f * (inst_.sampling.rng_get(SAMPLING_FILTER_V) - 0.5f) / extent_[1]; + + window_translate_m4(winmat, winmat, jitter_u, jitter_v); + DRW_view_update_sub(sub_view_, viewmat, winmat); + + /* FIXME(fclem): The offset may be is noticeably large and the culling might make object pop + * out of the blurring radius. To fix this, use custom enlarged culling matrix. */ + dof_.jitter_apply(winmat, viewmat); + DRW_view_update_sub(render_view_, viewmat, winmat); + + inst_.lightprobes.set_view(render_view_, extent_); + inst_.lights.set_view(render_view_, extent_); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name LightProbeView + * \{ */ + +void LightProbeView::sync(Texture &color_tx, + Texture &depth_tx, + const mat4 winmat, + const mat4 viewmat, + bool is_only_background) +{ + mat4 facemat; + mul_m4_m4m4(facemat, face_matrix_, viewmat); + + is_only_background_ = is_only_background; + extent_ = ivec2(color_tx.width()); + view_ = DRW_view_create(facemat, winmat, nullptr, nullptr, nullptr); + view_fb_.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx, layer_), + GPU_ATTACHMENT_TEXTURE_LAYER(color_tx, layer_)); + + if (!is_only_background_) { + /* Query temp textures and create framebuffers. */ + /* HACK: View name should be unique and static. + * With this, we can reuse the same texture across views. */ + DrawEngineType *owner = (DrawEngineType *)name_; + gbuffer_.sync(depth_tx, color_tx, owner, layer_); + rt_buffer_opaque_.sync(extent_); + rt_buffer_refract_.sync(extent_); + } +} + +void LightProbeView::render(void) +{ + if (!is_only_background_) { + inst_.lightprobes.set_view(view_, extent_); + inst_.lights.set_view(view_, extent_, false); + } + + DRW_stats_group_start(name_); + DRW_view_set_active(view_); + + GPU_framebuffer_bind(view_fb_); + + inst_.shading_passes.background.render(); + + if (!is_only_background_) { + GPU_framebuffer_clear_depth(view_fb_, 1.0f); + + inst_.shading_passes.deferred.render( + view_, gbuffer_, hiz_front_, hiz_back_, rt_buffer_opaque_, rt_buffer_refract_, view_fb_); + inst_.shading_passes.forward.render(view_, gbuffer_, hiz_front_, view_fb_); + } + DRW_stats_group_end(); +} + +/** \} */ + +} // namespace blender::eevee
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/eevee_view.hh b/source/blender/draw/engines/eevee/eevee_view.hh new file mode 100644 index 00000000000..4f1aae0d825 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_view.hh @@ -0,0 +1,239 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * A view is either: + * - The entire main view. + * - A fragment of the main view (for panoramic projections). + * - A shadow map view. + * - A lightprobe view (either planar, cubemap, irradiance grid). + * + * A pass is a container for scene data. It is view agnostic but has specific logic depending on + * its type. Passes are shared between views. + */ + +#pragma once + +#include "GPU_framebuffer.h" +#include "GPU_texture.h" + +#include "DRW_render.h" + +#include "eevee_depth_of_field.hh" +#include "eevee_hizbuffer.hh" +#include "eevee_motion_blur.hh" +#include "eevee_raytracing.hh" +#include "eevee_renderpasses.hh" +#include "eevee_shader.hh" +#include "eevee_shading.hh" +#include "eevee_velocity.hh" + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name ShadingView + * + * Render the scene and fill all render passes data. + * \{ */ + +class ShadingView { + private: + Instance &inst_; + /** Static string pointer. Used as debug name and as UUID for texture pool. */ + const char *name_; + /** Matrix to apply to the viewmat. */ + const float (*face_matrix_)[4]; + + /** Post-fx modules. */ + DepthOfField dof_; + MotionBlur mb_; + Velocity velocity_; + + /** GBuffer for deferred passes. */ + GBuffer gbuffer_; + /** HiZBuffer for screenspace effects. */ + HiZBuffer hiz_front_, hiz_back_; + /** Raytracing persistent buffers. Only opaque and refraction can have surface tracing. */ + RaytraceBuffer rt_buffer_opaque_; + RaytraceBuffer rt_buffer_refract_; + + /** Owned resources. */ + eevee::Framebuffer view_fb_; + /** Draw resources. Not owned. */ + GPUTexture *combined_tx_ = nullptr; + GPUTexture *depth_tx_ = nullptr; + GPUTexture *postfx_tx_ = nullptr; + + /** Main views is created from the camera (or is from the viewport). It is not jittered. */ + DRWView *main_view_ = nullptr; + /** Sub views is jittered versions or the main views. This allows jitter updates without trashing + * the visibility culling cache. */ + DRWView *sub_view_ = nullptr; + /** Same as sub_view_ but has Depth Of Field jitter applied. */ + DRWView *render_view_ = nullptr; + + /** Render size of the view. Can change between scene sample eval. */ + ivec2 extent_ = {-1, -1}; + + bool is_enabled_ = false; + + public: + ShadingView(Instance &inst, const char *name, const float (*face_matrix)[4]) + : inst_(inst), + name_(name), + face_matrix_(face_matrix), + dof_(inst, name), + mb_(inst, name), + velocity_(inst, name), + hiz_front_(inst), + hiz_back_(inst), + rt_buffer_opaque_(inst), + rt_buffer_refract_(inst){}; + + ~ShadingView(){}; + + void init(void); + + void sync(ivec2 render_extent_); + + void render(void); + + GPUTexture *render_post(GPUTexture *input_tx); + + private: + void update_view(void); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name LightProbeView + * + * Render the scene to a lightprobe target cubemap face. + * \{ */ + +class LightProbeView { + private: + Instance &inst_; + /** Static string pointer. Used as debug name and as UUID for texture pool. */ + const char *name_; + /** Matrix to apply to the viewmat. */ + const float (*face_matrix_)[4]; + /** GBuffer for deferred passes. */ + GBuffer gbuffer_; + /** HiZBuffer for screenspace effects. */ + HiZBuffer hiz_front_, hiz_back_; + /** Raytracing persistent buffers. Only opaque and refraction can have surface tracing. */ + RaytraceBuffer rt_buffer_opaque_; + RaytraceBuffer rt_buffer_refract_; + /** Owned resources. */ + Framebuffer view_fb_; + /** DRWView of this face. */ + DRWView *view_ = nullptr; + /** Render size of the view. */ + ivec2 extent_ = {-1, -1}; + + int layer_ = 0; + bool is_only_background_ = false; + + public: + LightProbeView(Instance &inst, const char *name, const float (*face_matrix)[4], int layer_) + : inst_(inst), + name_(name), + face_matrix_(face_matrix), + hiz_front_(inst), + hiz_back_(inst), + rt_buffer_opaque_(inst), + rt_buffer_refract_(inst), + layer_(layer_){}; + + ~LightProbeView(){}; + + void sync(Texture &color_tx, + Texture &depth_tx, + const mat4 winmat, + const mat4 viewmat, + bool is_only_background); + + void render(void); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Main View + * + * \{ */ + +/** + * Container for all views needed to render the final image. + * We might need up to 6 views for panoramic cameras. + */ +class MainView { + private: + ShadingView shading_views_[6]; + /** Internal render size. */ + int render_extent_[2]; + + public: + MainView(Instance &inst) + : shading_views_{{inst, "posX_view", cubeface_mat[0]}, + {inst, "negX_view", cubeface_mat[1]}, + {inst, "posY_view", cubeface_mat[2]}, + {inst, "negY_view", cubeface_mat[3]}, + {inst, "posZ_view", cubeface_mat[4]}, + {inst, "negZ_view", cubeface_mat[5]}} + { + } + + void init(const ivec2 full_extent_) + { + /* TODO(fclem) parameter hidden in experimental. We need to figure out mipmap bias to preserve + * texture crispiness. */ + float resolution_scale = 1.0f; + for (int i = 0; i < 2; i++) { + render_extent_[i] = max_ii(1, roundf(full_extent_[i] * resolution_scale)); + } + + for (auto i : IndexRange(ARRAY_SIZE(shading_views_))) { + shading_views_[i].init(); + } + } + + void sync(void) + { + for (auto i : IndexRange(ARRAY_SIZE(shading_views_))) { + shading_views_[i].sync(render_extent_); + } + } + + void render(void) + { + for (auto i : IndexRange(ARRAY_SIZE(shading_views_))) { + shading_views_[i].render(); + } + } +}; + +/** \} */ + +} // namespace blender::eevee
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c deleted file mode 100644 index ac9abcca16b..00000000000 --- a/source/blender/draw/engines/eevee/eevee_volumes.c +++ /dev/null @@ -1,856 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - * - * Volumetric effects rendering using frostbite approach. - */ - -#include "DRW_render.h" - -#include "BLI_listbase.h" -#include "BLI_rand.h" -#include "BLI_string_utils.h" - -#include "DNA_fluid_types.h" -#include "DNA_object_force_types.h" -#include "DNA_volume_types.h" -#include "DNA_world_types.h" - -#include "BKE_fluid.h" -#include "BKE_global.h" -#include "BKE_mesh.h" -#include "BKE_modifier.h" -#include "BKE_volume.h" -#include "BKE_volume_render.h" - -#include "ED_screen.h" - -#include "DEG_depsgraph_query.h" - -#include "GPU_capabilities.h" -#include "GPU_material.h" -#include "GPU_texture.h" -#include "eevee_private.h" - -static struct { - GPUTexture *depth_src; - - GPUTexture *dummy_zero; - GPUTexture *dummy_one; - GPUTexture *dummy_flame; - - GPUTexture *dummy_scatter; - GPUTexture *dummy_transmit; - - /* List of all fluid simulation / smoke domains rendered within this frame. */ - ListBase smoke_domains; -} e_data = {NULL}; /* Engine data */ - -static void eevee_create_textures_volumes(void) -{ - const float zero[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - e_data.dummy_zero = DRW_texture_create_3d(1, 1, 1, GPU_RGBA8, DRW_TEX_WRAP, zero); - - const float one[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - e_data.dummy_one = DRW_texture_create_3d(1, 1, 1, GPU_RGBA8, DRW_TEX_WRAP, one); - - const float flame = 0.0f; - e_data.dummy_flame = DRW_texture_create_3d(1, 1, 1, GPU_R8, DRW_TEX_WRAP, &flame); -} - -static GPUTexture *eevee_volume_default_texture(eGPUVolumeDefaultValue default_value) -{ - switch (default_value) { - case GPU_VOLUME_DEFAULT_0: - return e_data.dummy_zero; - case GPU_VOLUME_DEFAULT_1: - return e_data.dummy_one; - } - - return e_data.dummy_zero; -} - -void EEVEE_volumes_set_jitter(EEVEE_ViewLayerData *sldata, uint current_sample) -{ - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - - double ht_point[3]; - double ht_offset[3] = {0.0, 0.0}; - const uint ht_primes[3] = {3, 7, 2}; - - BLI_halton_3d(ht_primes, ht_offset, current_sample, ht_point); - - common_data->vol_jitter[0] = (float)ht_point[0]; - common_data->vol_jitter[1] = (float)ht_point[1]; - common_data->vol_jitter[2] = (float)ht_point[2]; -} - -void EEVEE_volumes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_StorageList *stl = vedata->stl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_EffectsInfo *effects = stl->effects; - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - - const float *viewport_size = DRW_viewport_size_get(); - - const int tile_size = scene_eval->eevee.volumetric_tile_size; - - /* Find Froxel Texture resolution. */ - int tex_size[3]; - - tex_size[0] = (int)ceilf(fmaxf(1.0f, viewport_size[0] / (float)tile_size)); - tex_size[1] = (int)ceilf(fmaxf(1.0f, viewport_size[1] / (float)tile_size)); - tex_size[2] = max_ii(scene_eval->eevee.volumetric_samples, 1); - - common_data->vol_coord_scale[0] = viewport_size[0] / (float)(tile_size * tex_size[0]); - common_data->vol_coord_scale[1] = viewport_size[1] / (float)(tile_size * tex_size[1]); - common_data->vol_coord_scale[2] = 1.0f / viewport_size[0]; - common_data->vol_coord_scale[3] = 1.0f / viewport_size[1]; - - /* TODO: compute snap to maxZBuffer for clustered rendering. */ - if ((common_data->vol_tex_size[0] != tex_size[0]) || - (common_data->vol_tex_size[1] != tex_size[1]) || - (common_data->vol_tex_size[2] != tex_size[2])) { - DRW_TEXTURE_FREE_SAFE(txl->volume_prop_scattering); - DRW_TEXTURE_FREE_SAFE(txl->volume_prop_extinction); - DRW_TEXTURE_FREE_SAFE(txl->volume_prop_emission); - DRW_TEXTURE_FREE_SAFE(txl->volume_prop_phase); - DRW_TEXTURE_FREE_SAFE(txl->volume_scatter); - DRW_TEXTURE_FREE_SAFE(txl->volume_transmit); - DRW_TEXTURE_FREE_SAFE(txl->volume_scatter_history); - DRW_TEXTURE_FREE_SAFE(txl->volume_transmit_history); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_fb); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_scat_fb); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_integ_fb); - copy_v3_v3_int(common_data->vol_tex_size, tex_size); - - common_data->vol_inv_tex_size[0] = 1.0f / (float)(tex_size[0]); - common_data->vol_inv_tex_size[1] = 1.0f / (float)(tex_size[1]); - common_data->vol_inv_tex_size[2] = 1.0f / (float)(tex_size[2]); - } - - /* Like frostbite's paper, 5% blend of the new frame. */ - common_data->vol_history_alpha = (txl->volume_prop_scattering == NULL) ? 0.0f : 0.95f; - - /* Temporal Super sampling jitter */ - uint ht_primes[3] = {3, 7, 2}; - uint current_sample = 0; - - /* If TAA is in use do not use the history buffer. */ - bool do_taa = ((effects->enabled_effects & EFFECT_TAA) != 0); - - if (draw_ctx->evil_C != NULL) { - struct wmWindowManager *wm = CTX_wm_manager(draw_ctx->evil_C); - do_taa = do_taa && (ED_screen_animation_no_scrub(wm) == NULL); - } - - if (do_taa) { - common_data->vol_history_alpha = 0.0f; - current_sample = effects->taa_current_sample - 1; - effects->volume_current_sample = -1; - } - else if (DRW_state_is_image_render()) { - const uint max_sample = (ht_primes[0] * ht_primes[1] * ht_primes[2]); - current_sample = effects->volume_current_sample = (effects->volume_current_sample + 1) % - max_sample; - if (current_sample != max_sample - 1) { - DRW_viewport_request_redraw(); - } - } - - EEVEE_volumes_set_jitter(sldata, current_sample); - - float integration_start = scene_eval->eevee.volumetric_start; - float integration_end = scene_eval->eevee.volumetric_end; - effects->volume_light_clamp = scene_eval->eevee.volumetric_light_clamp; - common_data->vol_shadow_steps = (float)scene_eval->eevee.volumetric_shadow_samples; - if ((scene_eval->eevee.flag & SCE_EEVEE_VOLUMETRIC_SHADOWS) == 0) { - common_data->vol_shadow_steps = 0; - } - - if (DRW_view_is_persp_get(NULL)) { - float sample_distribution = scene_eval->eevee.volumetric_sample_distribution; - sample_distribution = 4.0f * (max_ff(1.0f - sample_distribution, 1e-2f)); - - const float clip_start = DRW_view_near_distance_get(NULL); - /* Negate */ - float near = integration_start = min_ff(-integration_start, clip_start - 1e-4f); - float far = integration_end = min_ff(-integration_end, near - 1e-4f); - - common_data->vol_depth_param[0] = (far - near * exp2(1.0f / sample_distribution)) / - (far - near); - common_data->vol_depth_param[1] = (1.0f - common_data->vol_depth_param[0]) / near; - common_data->vol_depth_param[2] = sample_distribution; - } - else { - const float clip_start = DRW_view_near_distance_get(NULL); - const float clip_end = DRW_view_far_distance_get(NULL); - integration_start = min_ff(integration_end, clip_start); - integration_end = max_ff(-integration_end, clip_end); - - common_data->vol_depth_param[0] = integration_start; - common_data->vol_depth_param[1] = integration_end; - common_data->vol_depth_param[2] = 1.0f / (integration_end - integration_start); - } - - /* Disable clamp if equal to 0. */ - if (effects->volume_light_clamp == 0.0) { - effects->volume_light_clamp = FLT_MAX; - } - - common_data->vol_use_lights = (scene_eval->eevee.flag & SCE_EEVEE_VOLUMETRIC_LIGHTS) != 0; - common_data->vol_use_soft_shadows = (scene_eval->eevee.flag & SCE_EEVEE_SHADOW_SOFT) != 0; - - if (!e_data.dummy_scatter) { - const float scatter[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - const float transmit[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - e_data.dummy_scatter = DRW_texture_create_3d(1, 1, 1, GPU_RGBA8, DRW_TEX_WRAP, scatter); - e_data.dummy_transmit = DRW_texture_create_3d(1, 1, 1, GPU_RGBA8, DRW_TEX_WRAP, transmit); - } -} - -void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - DRWShadingGroup *grp = NULL; - - /* Textures */ - if (!e_data.dummy_zero) { - eevee_create_textures_volumes(); - } - - /* Quick breakdown of the Volumetric rendering: - * - * The rendering is separated in 4 stages: - * - * - Material Parameters : we collect volume properties of - * all participating media in the scene and store them in - * a 3D texture aligned with the 3D frustum. - * This is done in 2 passes, one that clear the texture - * and/or evaluate the world volumes, and the 2nd one that - * additively render object volumes. - * - * - Light Scattering : the volume properties then are sampled - * and light scattering is evaluated for each cell of the - * volume texture. Temporal super-sampling (if enabled) occurs here. - * - * - Volume Integration : the scattered light and extinction is - * integrated (accumulated) along the view-rays. The result is stored - * for every cell in another texture. - * - * - Full-screen Resolve : From the previous stage, we get two - * 3D textures that contains integrated scattered light and extinction - * for "every" positions in the frustum. We only need to sample - * them and blend the scene color with those factors. This also - * work for alpha blended materials. - */ - - /* World pass is not additive as it also clear the buffer. */ - DRW_PASS_CREATE(psl->volumetric_world_ps, DRW_STATE_WRITE_COLOR); - DRW_PASS_CREATE(psl->volumetric_objects_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD); - - /* World Volumetric */ - struct World *wo = scene->world; - if (wo != NULL && wo->use_nodes && wo->nodetree && - !LOOK_DEV_STUDIO_LIGHT_ENABLED(draw_ctx->v3d)) { - struct GPUMaterial *mat = EEVEE_material_get(vedata, scene, NULL, wo, VAR_MAT_VOLUME); - - if (GPU_material_has_volume_output(mat)) { - grp = DRW_shgroup_material_create(mat, psl->volumetric_world_ps); - } - - if (grp) { - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - /* TODO(fclem): remove those (need to clean the GLSL files). */ - DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); - DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); - DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - - /* Fix principle volumetric not working with world materials. */ - ListBase gpu_grids = GPU_material_volume_grids(mat); - LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, &gpu_grids) { - DRW_shgroup_uniform_texture( - grp, gpu_grid->sampler_name, eevee_volume_default_texture(gpu_grid->default_value)); - } - - DRW_shgroup_call_procedural_triangles(grp, NULL, common_data->vol_tex_size[2]); - - effects->enabled_effects |= (EFFECT_VOLUMETRIC | EFFECT_POST_BUFFER); - } - } - - if (grp == NULL) { - /* If no world or volume material is present just clear the buffer with this drawcall */ - grp = DRW_shgroup_create(EEVEE_shaders_volumes_clear_sh_get(), psl->volumetric_world_ps); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - - DRW_shgroup_call_procedural_triangles(grp, NULL, common_data->vol_tex_size[2]); - } -} - -static bool eevee_volume_object_grids_init(Object *ob, ListBase *gpu_grids, DRWShadingGroup *grp) -{ - Volume *volume = ob->data; - BKE_volume_load(volume, G.main); - - /* Test if we need to use multiple transforms. */ - DRWVolumeGrid *first_drw_grid = NULL; - bool multiple_transforms = true; - - LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) { - const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, gpu_grid->name); - DRWVolumeGrid *drw_grid = (volume_grid) ? - DRW_volume_batch_cache_get_grid(volume, volume_grid) : - NULL; - - if (drw_grid) { - if (first_drw_grid == NULL) { - first_drw_grid = drw_grid; - } - else if (drw_grid && - !equals_m4m4(drw_grid->object_to_texture, first_drw_grid->object_to_texture)) { - multiple_transforms = true; - break; - } - } - } - - /* Bail out of no grids to render. */ - if (first_drw_grid == NULL) { - return false; - } - - /* Set transform matrix for the volume as a whole. This one is also used for - * clipping so must map the entire bounding box to 0..1. */ - float bounds_to_object[4][4]; - - if (multiple_transforms) { - /* For multiple grids with different transform, we first transform from object space - * to bounds, then for each individual grid from bounds to texture. */ - BoundBox *bb = BKE_volume_boundbox_get(ob); - float bb_size[3]; - sub_v3_v3v3(bb_size, bb->vec[6], bb->vec[0]); - size_to_mat4(bounds_to_object, bb_size); - copy_v3_v3(bounds_to_object[3], bb->vec[0]); - - invert_m4_m4(first_drw_grid->object_to_bounds, bounds_to_object); - DRW_shgroup_uniform_mat4(grp, "volumeObjectToTexture", first_drw_grid->object_to_bounds); - } - else { - /* All grid transforms are equal, we can transform to texture space immediately. */ - DRW_shgroup_uniform_mat4(grp, "volumeObjectToTexture", first_drw_grid->object_to_texture); - } - - /* Don't use orco transform here, only matrix. */ - DRW_shgroup_uniform_vec3_copy(grp, "volumeOrcoLoc", (float[3]){0.5f, 0.5f, 0.5f}); - DRW_shgroup_uniform_vec3_copy(grp, "volumeOrcoSize", (float[3]){0.5f, 0.5f, 0.5f}); - - /* Set density scale. */ - const float density_scale = BKE_volume_density_scale(volume, ob->obmat); - DRW_shgroup_uniform_float_copy(grp, "volumeDensityScale", density_scale); - - /* Bind volume grid textures. */ - LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) { - const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, gpu_grid->name); - DRWVolumeGrid *drw_grid = (volume_grid) ? - DRW_volume_batch_cache_get_grid(volume, volume_grid) : - NULL; - - /* Handle 3 cases here: - * - Grid exists and texture was loaded -> use texture. - * - Grid exists but has zero size or failed to load -> use zero. - * - Grid does not exist -> use default value. */ - GPUTexture *grid_tex = (drw_grid) ? drw_grid->texture : - (volume_grid) ? e_data.dummy_zero : - eevee_volume_default_texture(gpu_grid->default_value); - - DRW_shgroup_uniform_texture(grp, gpu_grid->sampler_name, grid_tex); - - if (drw_grid && multiple_transforms) { - /* Specify per-volume transform matrix that is applied after the - * transform from object to bounds. */ - mul_m4_m4m4(drw_grid->bounds_to_texture, drw_grid->object_to_texture, bounds_to_object); - DRW_shgroup_uniform_mat4(grp, gpu_grid->transform_name, drw_grid->bounds_to_texture); - } - } - - return true; -} - -static bool eevee_volume_object_mesh_init(Scene *scene, - Object *ob, - ListBase *gpu_grids, - DRWShadingGroup *grp) -{ - static const float white[3] = {1.0f, 1.0f, 1.0f}; - ModifierData *md = NULL; - - /* Smoke Simulation */ - if ((md = BKE_modifiers_findby_type(ob, eModifierType_Fluid)) && - (BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) && - ((FluidModifierData *)md)->domain != NULL) { - FluidModifierData *fmd = (FluidModifierData *)md; - FluidDomainSettings *fds = fmd->domain; - - /* Don't try to show liquid domains here. */ - if (!fds->fluid || !(fds->type == FLUID_DOMAIN_TYPE_GAS)) { - return false; - } - - /* Don't show smoke before simulation starts, this could be made an option in the future. */ - /* (sebbas): Always show smoke for manta */ -#if 0 - const DRWContextState *draw_ctx = DRW_context_state_get(); - const bool show_smoke = ((int)DEG_get_ctime(draw_ctx->depsgraph) >= - *fds->point_cache[0]->startframe); -#endif - - if (fds->fluid && (fds->type == FLUID_DOMAIN_TYPE_GAS) /* && show_smoke */) { - DRW_smoke_ensure(fmd, fds->flags & FLUID_DOMAIN_USE_NOISE); - BLI_addtail(&e_data.smoke_domains, BLI_genericNodeN(fmd)); - } - - LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) { - if (STREQ(gpu_grid->name, "density")) { - DRW_shgroup_uniform_texture_ref( - grp, gpu_grid->sampler_name, fds->tex_density ? &fds->tex_density : &e_data.dummy_one); - } - else if (STREQ(gpu_grid->name, "color")) { - DRW_shgroup_uniform_texture_ref( - grp, gpu_grid->sampler_name, fds->tex_color ? &fds->tex_color : &e_data.dummy_one); - } - else if (STR_ELEM(gpu_grid->name, "flame", "temperature")) { - DRW_shgroup_uniform_texture_ref( - grp, gpu_grid->sampler_name, fds->tex_flame ? &fds->tex_flame : &e_data.dummy_flame); - } - else { - DRW_shgroup_uniform_texture( - grp, gpu_grid->sampler_name, eevee_volume_default_texture(gpu_grid->default_value)); - } - } - - /* Constant Volume color. */ - bool use_constant_color = ((fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) == 0 && - (fds->active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET) != 0); - - DRW_shgroup_uniform_vec3( - grp, "volumeColor", (use_constant_color) ? fds->active_color : white, 1); - - /* Output is such that 0..1 maps to 0..1000K */ - DRW_shgroup_uniform_vec2(grp, "volumeTemperature", &fds->flame_ignition, 1); - } - else { - LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) { - DRW_shgroup_uniform_texture( - grp, gpu_grid->sampler_name, eevee_volume_default_texture(gpu_grid->default_value)); - } - } - - /* Transform for mesh volumes. */ - static const float unit_mat[4][4] = {{1.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 1.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 1.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 1.0f}}; - float *texco_loc, *texco_size; - BKE_mesh_texspace_get_reference((struct Mesh *)ob->data, NULL, &texco_loc, &texco_size); - - DRW_shgroup_uniform_mat4(grp, "volumeObjectToTexture", unit_mat); - DRW_shgroup_uniform_vec3(grp, "volumeOrcoLoc", texco_loc, 1); - DRW_shgroup_uniform_vec3(grp, "volumeOrcoSize", texco_size, 1); - - return true; -} - -void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata, - EEVEE_Data *vedata, - Scene *scene, - Object *ob) -{ - Material *ma = BKE_object_material_get_eval(ob, 1); - - if (ma == NULL) { - if (ob->type == OB_VOLUME) { - ma = BKE_material_default_volume(); - } - else { - return; - } - } - - float size[3]; - mat4_to_size(size, ob->obmat); - /* Check if any of the axes have 0 length. (see T69070) */ - const float epsilon = 1e-8f; - if ((size[0] < epsilon) || (size[1] < epsilon) || (size[2] < epsilon)) { - return; - } - - int mat_options = VAR_MAT_VOLUME | VAR_MAT_MESH; - struct GPUMaterial *mat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options); - eGPUMaterialStatus status = GPU_material_status(mat); - - /* If shader failed to compile or is currently compiling. */ - if (status != GPU_MAT_SUCCESS) { - return; - } - - DRWShadingGroup *grp = DRW_shgroup_material_create(mat, vedata->psl->volumetric_objects_ps); - - /* TODO(fclem): remove those "unnecessary" UBOs */ - DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); - DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); - DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - - ListBase gpu_grids = GPU_material_volume_grids(mat); - - if (ob->type == OB_VOLUME) { - if (!eevee_volume_object_grids_init(ob, &gpu_grids, grp)) { - return; - } - } - else { - if (!eevee_volume_object_mesh_init(scene, ob, &gpu_grids, grp)) { - return; - } - } - - /* TODO: Reduce to number of slices intersecting. */ - /* TODO: Preemptive culling. */ - DRW_shgroup_call_procedural_triangles(grp, ob, sldata->common_data.vol_tex_size[2]); - - vedata->stl->effects->enabled_effects |= (EFFECT_VOLUMETRIC | EFFECT_POST_BUFFER); -} - -void EEVEE_volumes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_EffectsInfo *effects = vedata->stl->effects; - LightCache *lcache = vedata->stl->g_data->light_cache; - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - - if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) { - DRWShadingGroup *grp; - struct GPUShader *sh; - - DRW_PASS_CREATE(psl->volumetric_scatter_ps, DRW_STATE_WRITE_COLOR); - sh = (common_data->vol_use_lights) ? EEVEE_shaders_volumes_scatter_with_lights_sh_get() : - EEVEE_shaders_volumes_scatter_sh_get(); - grp = DRW_shgroup_create(sh, psl->volumetric_scatter_ps); - DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &lcache->grid_tx.tex); - DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool); - DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool); - DRW_shgroup_uniform_texture_ref(grp, "volumeScattering", &txl->volume_prop_scattering); - DRW_shgroup_uniform_texture_ref(grp, "volumeExtinction", &txl->volume_prop_extinction); - DRW_shgroup_uniform_texture_ref(grp, "volumeEmission", &txl->volume_prop_emission); - DRW_shgroup_uniform_texture_ref(grp, "volumePhase", &txl->volume_prop_phase); - DRW_shgroup_uniform_texture_ref(grp, "historyScattering", &txl->volume_scatter_history); - DRW_shgroup_uniform_texture_ref(grp, "historyTransmittance", &txl->volume_transmit_history); - DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); - DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - - DRW_shgroup_call_procedural_triangles(grp, NULL, common_data->vol_tex_size[2]); - - DRW_PASS_CREATE(psl->volumetric_integration_ps, DRW_STATE_WRITE_COLOR); - grp = DRW_shgroup_create(EEVEE_shaders_volumes_integration_sh_get(), - psl->volumetric_integration_ps); - DRW_shgroup_uniform_texture_ref(grp, "volumeScattering", &txl->volume_scatter); - DRW_shgroup_uniform_texture_ref(grp, "volumeExtinction", &txl->volume_transmit); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - if (USE_VOLUME_OPTI) { - DRW_shgroup_uniform_image_ref(grp, "finalScattering_img", &txl->volume_scatter_history); - DRW_shgroup_uniform_image_ref(grp, "finalTransmittance_img", &txl->volume_transmit_history); - } - - DRW_shgroup_call_procedural_triangles( - grp, NULL, USE_VOLUME_OPTI ? 1 : common_data->vol_tex_size[2]); - - DRW_PASS_CREATE(psl->volumetric_resolve_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM); - grp = DRW_shgroup_create(EEVEE_shaders_volumes_resolve_sh_get(false), - psl->volumetric_resolve_ps); - DRW_shgroup_uniform_texture_ref(grp, "inScattering", &txl->volume_scatter); - DRW_shgroup_uniform_texture_ref(grp, "inTransmittance", &txl->volume_transmit); - DRW_shgroup_uniform_texture_ref(grp, "inSceneDepth", &e_data.depth_src); - DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - } -} - -void EEVEE_volumes_draw_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_EffectsInfo *effects = vedata->stl->effects; - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - - if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) { - int *tex_size = common_data->vol_tex_size; - - if (txl->volume_prop_scattering == NULL) { - /* Volume properties: We evaluate all volumetric objects - * and store their final properties into each froxel */ - txl->volume_prop_scattering = DRW_texture_create_3d( - tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL); - txl->volume_prop_extinction = DRW_texture_create_3d( - tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL); - txl->volume_prop_emission = DRW_texture_create_3d( - tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL); - txl->volume_prop_phase = DRW_texture_create_3d( - tex_size[0], tex_size[1], tex_size[2], GPU_RG16F, DRW_TEX_FILTER, NULL); - - /* Volume scattering: We compute for each froxel the - * Scattered light towards the view. We also resolve temporal - * super sampling during this stage. */ - txl->volume_scatter = DRW_texture_create_3d( - tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL); - txl->volume_transmit = DRW_texture_create_3d( - tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL); - - /* Final integration: We compute for each froxel the - * amount of scattered light and extinction coef at this - * given depth. We use these textures as double buffer - * for the volumetric history. */ - txl->volume_scatter_history = DRW_texture_create_3d( - tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL); - txl->volume_transmit_history = DRW_texture_create_3d( - tex_size[0], tex_size[1], tex_size[2], GPU_R11F_G11F_B10F, DRW_TEX_FILTER, NULL); - } - - GPU_framebuffer_ensure_config(&fbl->volumetric_fb, - {GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(txl->volume_prop_scattering), - GPU_ATTACHMENT_TEXTURE(txl->volume_prop_extinction), - GPU_ATTACHMENT_TEXTURE(txl->volume_prop_emission), - GPU_ATTACHMENT_TEXTURE(txl->volume_prop_phase)}); - GPU_framebuffer_ensure_config(&fbl->volumetric_scat_fb, - {GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(txl->volume_scatter), - GPU_ATTACHMENT_TEXTURE(txl->volume_transmit)}); - GPU_framebuffer_ensure_config(&fbl->volumetric_integ_fb, - {GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(txl->volume_scatter_history), - GPU_ATTACHMENT_TEXTURE(txl->volume_transmit_history)}); - } - else { - DRW_TEXTURE_FREE_SAFE(txl->volume_prop_scattering); - DRW_TEXTURE_FREE_SAFE(txl->volume_prop_extinction); - DRW_TEXTURE_FREE_SAFE(txl->volume_prop_emission); - DRW_TEXTURE_FREE_SAFE(txl->volume_prop_phase); - DRW_TEXTURE_FREE_SAFE(txl->volume_scatter); - DRW_TEXTURE_FREE_SAFE(txl->volume_transmit); - DRW_TEXTURE_FREE_SAFE(txl->volume_scatter_history); - DRW_TEXTURE_FREE_SAFE(txl->volume_transmit_history); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_fb); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_scat_fb); - GPU_FRAMEBUFFER_FREE_SAFE(fbl->volumetric_integ_fb); - } - - effects->volume_scatter = e_data.dummy_scatter; - effects->volume_transmit = e_data.dummy_transmit; -} - -void EEVEE_volumes_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) { - DRW_stats_group_start("Volumetrics"); - - /* We sample the shadow-maps using shadow sampler. We need to enable Comparison mode. - * TODO(fclem): avoid this by using sampler objects. */ - GPU_texture_compare_mode(sldata->shadow_cube_pool, true); - GPU_texture_compare_mode(sldata->shadow_cascade_pool, true); - - GPU_framebuffer_bind(fbl->volumetric_fb); - DRW_draw_pass(psl->volumetric_world_ps); - DRW_draw_pass(psl->volumetric_objects_ps); - - GPU_framebuffer_bind(fbl->volumetric_scat_fb); - DRW_draw_pass(psl->volumetric_scatter_ps); - - if (USE_VOLUME_OPTI) { - /* Avoid feedback loop assert. */ - GPU_framebuffer_bind(fbl->volumetric_fb); - } - else { - GPU_framebuffer_bind(fbl->volumetric_integ_fb); - } - - DRW_draw_pass(psl->volumetric_integration_ps); - - SWAP(struct GPUFrameBuffer *, fbl->volumetric_scat_fb, fbl->volumetric_integ_fb); - SWAP(GPUTexture *, txl->volume_scatter, txl->volume_scatter_history); - SWAP(GPUTexture *, txl->volume_transmit, txl->volume_transmit_history); - - effects->volume_scatter = txl->volume_scatter; - effects->volume_transmit = txl->volume_transmit; - - /* Restore */ - GPU_framebuffer_bind(fbl->main_fb); - - DRW_stats_group_end(); - } -} - -void EEVEE_volumes_resolve(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_PassList *psl = vedata->psl; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_EffectsInfo *effects = stl->effects; - - if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) { - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - e_data.depth_src = dtxl->depth; - - if (USE_VOLUME_OPTI) { - GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); - } - - /* Apply for opaque geometry. */ - GPU_framebuffer_bind(fbl->main_color_fb); - DRW_draw_pass(psl->volumetric_resolve_ps); - - /* Restore. */ - GPU_framebuffer_bind(fbl->main_fb); - } -} - -void EEVEE_volumes_free_smoke_textures(void) -{ - /* Free Smoke Textures after rendering */ - LISTBASE_FOREACH (LinkData *, link, &e_data.smoke_domains) { - FluidModifierData *fmd = (FluidModifierData *)link->data; - DRW_smoke_free(fmd); - } - BLI_freelistN(&e_data.smoke_domains); -} - -void EEVEE_volumes_free(void) -{ - DRW_TEXTURE_FREE_SAFE(e_data.dummy_scatter); - DRW_TEXTURE_FREE_SAFE(e_data.dummy_transmit); - - DRW_TEXTURE_FREE_SAFE(e_data.dummy_zero); - DRW_TEXTURE_FREE_SAFE(e_data.dummy_one); - DRW_TEXTURE_FREE_SAFE(e_data.dummy_flame); -} - -/* -------------------------------------------------------------------- */ -/** \name Render Passes - * \{ */ - -void EEVEE_volumes_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_PassList *psl = vedata->psl; - EEVEE_EffectsInfo *effects = stl->effects; - - /* Create FrameBuffer. */ - - /* Should be enough precision for many samples. */ - const eGPUTextureFormat texture_format_accum = (tot_samples > 128) ? GPU_RGBA32F : GPU_RGBA16F; - DRW_texture_ensure_fullscreen_2d(&txl->volume_scatter_accum, texture_format_accum, 0); - DRW_texture_ensure_fullscreen_2d(&txl->volume_transmittance_accum, texture_format_accum, 0); - - GPU_framebuffer_ensure_config(&fbl->volumetric_accum_fb, - {GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(txl->volume_scatter_accum), - GPU_ATTACHMENT_TEXTURE(txl->volume_transmittance_accum)}); - - /* Create Pass and shgroup. */ - DRW_PASS_CREATE(psl->volumetric_accum_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL); - DRWShadingGroup *grp = NULL; - if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) { - grp = DRW_shgroup_create(EEVEE_shaders_volumes_resolve_sh_get(true), psl->volumetric_accum_ps); - DRW_shgroup_uniform_texture_ref(grp, "inScattering", &txl->volume_scatter); - DRW_shgroup_uniform_texture_ref(grp, "inTransmittance", &txl->volume_transmit); - DRW_shgroup_uniform_texture_ref(grp, "inSceneDepth", &e_data.depth_src); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - } - else { - /* There is no volumetrics in the scene. Use a shader to fill the accum textures with a default - * value. */ - grp = DRW_shgroup_create(EEVEE_shaders_volumes_accum_sh_get(), psl->volumetric_accum_ps); - } - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); -} - -void EEVEE_volumes_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) -{ - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_PassList *psl = vedata->psl; - EEVEE_EffectsInfo *effects = vedata->stl->effects; - - if (fbl->volumetric_accum_fb != NULL) { - /* Accum pass */ - GPU_framebuffer_bind(fbl->volumetric_accum_fb); - - /* Clear texture. */ - if (effects->taa_current_sample == 1) { - const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPU_framebuffer_clear_color(fbl->volumetric_accum_fb, clear); - } - - DRW_draw_pass(psl->volumetric_accum_ps); - - /* Restore */ - GPU_framebuffer_bind(fbl->main_fb); - } -} - -/** \} */ diff --git a/source/blender/draw/engines/eevee/eevee_world.cc b/source/blender/draw/engines/eevee/eevee_world.cc new file mode 100644 index 00000000000..04978d67fa1 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_world.cc @@ -0,0 +1,111 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +#include "NOD_shader.h" + +#include "BKE_lib_id.h" +#include "BKE_node.h" +#include "BKE_world.h" + +#include "eevee_instance.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Default Material + * + * \{ */ + +DefaultWorldNodeTree::DefaultWorldNodeTree() +{ + bNodeTree *ntree = ntreeAddTree(NULL, "World Nodetree", ntreeType_Shader->idname); + bNode *background = nodeAddStaticNode(NULL, ntree, SH_NODE_BACKGROUND); + bNode *output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_WORLD); + bNodeSocket *background_out = nodeFindSocket(background, SOCK_OUT, "Background"); + bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface"); + nodeAddLink(ntree, background, background_out, output, output_in); + nodeSetActive(ntree, output); + + color_socket_ = + (bNodeSocketValueRGBA *)nodeFindSocket(background, SOCK_IN, "Color")->default_value; + ntree_ = ntree; +} + +DefaultWorldNodeTree::~DefaultWorldNodeTree() +{ + ntreeFreeEmbeddedTree(ntree_); + MEM_SAFE_FREE(ntree_); +} + +/* Configure a default nodetree with the given world. */ +bNodeTree *DefaultWorldNodeTree::nodetree_get(::World *wo) +{ + /* WARNING: This function is not threadsafe. Which is not a problem for the moment. */ + copy_v3_fl3(color_socket_->value, wo->horr, wo->horg, wo->horb); + return ntree_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name World + * + * \{ */ + +void World::sync() +{ + if (inst_.lookdev.sync_world()) { + return; + } + + ::World *bl_world = inst_.scene->world; + + if (bl_world == nullptr) { + bl_world = BKE_world_default(); + } + else { + WorldHandle &wo_handle = inst_.sync.sync_world(bl_world); + + if (wo_handle.recalc != 0) { + inst_.lightprobes.set_world_dirty(); + } + wo_handle.reset_recalc_flag(); + } + + /* TODO(fclem) This should be detected to scene level. */ + ::World *orig_world = (::World *)DEG_get_original_id(&bl_world->id); + if (prev_original_world != orig_world) { + prev_original_world = orig_world; + inst_.sampling.reset(); + } + + bNodeTree *ntree = (bl_world->nodetree && bl_world->use_nodes) ? + bl_world->nodetree : + default_tree.nodetree_get(bl_world); + + GPUMaterial *gpumat = inst_.shaders.world_shader_get(bl_world, ntree); + inst_.shading_passes.background.sync(gpumat); +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_world.hh b/source/blender/draw/engines/eevee/eevee_world.hh new file mode 100644 index 00000000000..ecf4a8cb260 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_world.hh @@ -0,0 +1,78 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * World rendering with material handling. Also take care of lookdev + * HDRI and default material. + */ + +#pragma once + +#include "DNA_world_types.h" + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name Default World Nodetree + * + * In order to support worlds without nodetree we reuse and configure a standalone nodetree that + * we pass for shader generation. The GPUMaterial is still stored inside the World even if + * it does not use a nodetree. + * \{ */ + +class DefaultWorldNodeTree { + private: + bNodeTree *ntree_; + bNodeSocketValueRGBA *color_socket_; + + public: + DefaultWorldNodeTree(); + ~DefaultWorldNodeTree(); + + bNodeTree *nodetree_get(::World *world); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name World + * + * \{ */ + +class World { + private: + Instance &inst_; + + DefaultWorldNodeTree default_tree; + + /* Used to detect if world change. */ + ::World *prev_original_world = nullptr; + + public: + World(Instance &inst) : inst_(inst){}; + + void sync(void); +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/eevee_wrapper.hh b/source/blender/draw/engines/eevee/eevee_wrapper.hh new file mode 100644 index 00000000000..d5daa247d1c --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_wrapper.hh @@ -0,0 +1,682 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Templated wrappers to make it easier to use GPU objects in C++. + */ + +#pragma once + +#include "BLI_utildefines.h" +#include "BLI_utility_mixins.hh" +#include "GPU_framebuffer.h" +#include "GPU_texture.h" +#include "GPU_uniform_buffer.h" +#include "GPU_vertex_buffer.h" + +namespace blender::eevee { + +template< + /** Type of the values stored in this uniform buffer. */ + typename T, + /** The number of values that can be stored in this uniform buffer. */ + int64_t len> +class StructArrayBuffer : NonMovable, NonCopyable { + private: + T data_[len]; + GPUUniformBuf *ubo_; + +#ifdef DEBUG + const char *name_ = typeid(T).name(); +#else + constexpr static const char *name_ = "StructArrayBuffer"; +#endif + + public: + StructArrayBuffer() + { + ubo_ = GPU_uniformbuf_create_ex(sizeof(data_), nullptr, name_); + } + ~StructArrayBuffer() + { + GPU_uniformbuf_free(ubo_); + } + + void push_update(void) + { + GPU_uniformbuf_update(ubo_, data_); + } + + const GPUUniformBuf *ubo_get(void) const + { + return ubo_; + } + + /** + * Get the value at the given index. This invokes undefined behavior when the index is out of + * bounds. + */ + const T &operator[](int64_t index) const + { + BLI_assert(index >= 0); + BLI_assert(index < len); + return data_[index]; + } + + T &operator[](int64_t index) + { + BLI_assert(index >= 0); + BLI_assert(index < len); + return data_[index]; + } + + /** + * Get a pointer to the beginning of the array. + */ + const T *data() const + { + return data_; + } + T *data() + { + return data_; + } + + /** + * Iterator + */ + const T *begin() const + { + return data_; + } + const T *end() const + { + return data_ + len; + } + + T *begin() + { + return data_; + } + T *end() + { + return data_ + len; + } + + operator Span<T>() const + { + return Span<T>(data_, len); + } +}; + +template< + /** Type of the values stored in this uniform buffer. */ + typename T, + /** The number of values that can be stored in this uniform buffer. */ + int64_t len, + /** True if created on device and no memory host memory is allocated. */ + bool device_only = false> +/* Same thing as StructArrayBuffer but can be arbitrary large, and are writtable on GPU. */ +class StorageArrayBuffer : NonMovable, NonCopyable { + private: + T *data_ = nullptr; + /* Use vertex buffer for now. Until there is a complete GPUStorageBuf implementation. */ + GPUVertBuf *ssbo_; + /* Currently allocated size. */ + int64_t size; + +#ifdef DEBUG + const char *name_ = typeid(T).name(); +#else + constexpr static const char *name_ = "StorageArrayBuffer"; +#endif + + public: + StorageArrayBuffer() + { + init(len); + } + ~StorageArrayBuffer() + { + GPU_vertbuf_discard(ssbo_); + } + + void init(int64_t new_size) + { + size = new_size; + + GPUVertFormat format = {0}; + GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + + GPUUsageType usage = device_only ? GPU_USAGE_DEVICE_ONLY : GPU_USAGE_DYNAMIC; + ssbo_ = GPU_vertbuf_create_with_format_ex(&format, usage); + GPU_vertbuf_data_alloc(ssbo_, divide_ceil_u(sizeof(T) * size, 4)); + if (!device_only) { + data_ = (T *)GPU_vertbuf_get_data(ssbo_); + GPU_vertbuf_use(ssbo_); + } + } + + void resize(int64_t new_size) + { + BLI_assert(new_size > 0); + if (new_size != size) { + GPU_vertbuf_discard(ssbo_); + this->init(new_size); + } + } + + void push_update(void) + { + BLI_assert(!device_only); + /* Get the data again to tag for update. The actual pointer should not change. */ + data_ = (T *)GPU_vertbuf_get_data(ssbo_); + GPU_vertbuf_use(ssbo_); + } + + operator GPUVertBuf *() const + { + return ssbo_; + } + /* To be able to use it with DRW_shgroup_*_ref(). */ + GPUVertBuf **operator&() + { + return &ssbo_; + } + + /** + * Get the value at the given index. This invokes undefined behavior when the index is out of + * bounds. + */ + const T &operator[](int64_t index) const + { + BLI_assert(!device_only); + BLI_assert(index >= 0); + BLI_assert(index < size); + return data_[index]; + } + + T &operator[](int64_t index) + { + BLI_assert(!device_only); + BLI_assert(index >= 0); + BLI_assert(index < size); + return data_[index]; + } + + /** + * Get a pointer to the beginning of the array. + */ + const T *data() const + { + BLI_assert(!device_only); + return data_; + } + T *data() + { + BLI_assert(!device_only); + return data_; + } + + /** + * Iterator + */ + const T *begin() const + { + BLI_assert(!device_only); + return data_; + } + const T *end() const + { + BLI_assert(!device_only); + return data_ + len; + } + + T *begin() + { + BLI_assert(!device_only); + return data_; + } + T *end() + { + BLI_assert(!device_only); + return data_ + len; + } + + operator Span<T>() const + { + BLI_assert(!device_only); + return Span<T>(data_, len); + } +}; + +/** Simpler version where data is not an array. */ +template< + /** Type of the values stored in this uniform buffer. */ + typename T, + /** True if created on device and no memory host memory is allocated. */ + bool device_only = false> +class StorageBuffer : public T, NonMovable, NonCopyable { + private: + /* Use vertex buffer for now. Until there is a complete GPUStorageBuf implementation. */ + GPUVertBuf *ssbo_; + +#ifdef DEBUG + const char *name_ = typeid(T).name(); +#else + constexpr static const char *name_ = "StorageBuffer"; +#endif + + public: + StorageBuffer() + { + GPUVertFormat format = {0}; + GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + + GPUUsageType usage = device_only ? GPU_USAGE_DEVICE_ONLY : GPU_USAGE_DYNAMIC; + ssbo_ = GPU_vertbuf_create_with_format_ex(&format, usage); + GPU_vertbuf_data_alloc(ssbo_, divide_ceil_u(sizeof(T), 4)); + if (!device_only) { + GPU_vertbuf_use(ssbo_); + } + } + ~StorageBuffer() + { + GPU_vertbuf_discard(ssbo_); + } + + void push_update(void) + { + BLI_assert(!device_only); + /* TODO(fclem): Avoid a full copy. */ + T *data = (T *)GPU_vertbuf_get_data(ssbo_); + *data = *this; + + GPU_vertbuf_use(ssbo_); + } + + operator GPUVertBuf *() const + { + return ssbo_; + } + /* To be able to use it with DRW_shgroup_*_ref(). */ + GPUVertBuf **operator&() + { + return &ssbo_; + } + + StorageBuffer<T> &operator=(const T &other) + { + *static_cast<T *>(this) = other; + return *this; + } +}; + +/** Simpler version where data is not an array. */ +template<typename T> class StructBuffer : public T, NonMovable, NonCopyable { + private: + GPUUniformBuf *ubo_; + +#ifdef DEBUG + const char *name_ = typeid(T).name(); +#else + constexpr static const char *name_ = "StructBuffer"; +#endif + + public: + StructBuffer() + { + ubo_ = GPU_uniformbuf_create_ex(sizeof(T), nullptr, name_); + } + ~StructBuffer() + { + DRW_UBO_FREE_SAFE(ubo_); + } + + void push_update(void) + { + T *data = static_cast<T *>(this); + GPU_uniformbuf_update(ubo_, data); + } + + const GPUUniformBuf *ubo_get(void) const + { + return ubo_; + } + + StructBuffer<T> &operator=(const T &other) + { + *static_cast<T *>(this) = other; + return *this; + } +}; + +class Texture { + private: + GPUTexture *tx_ = nullptr; + GPUTexture *tx_tmp_saved_ = nullptr; + const char *name_; + + public: + Texture() : name_("eevee::Texture"){}; + Texture(const char *name) : name_(name){}; + + Texture(const char *name, + int w, + int h = 0, + int d = 0, + int mips = 1, + eGPUTextureFormat format = GPU_RGBA8, + float *data = nullptr, + bool layered = false, + bool cubemap = false) + : Texture(name) + { + if (h == 0) { + tx_ = GPU_texture_create_1d(name, w, mips, format, data); + } + else if (d == 0) { + if (layered) { + tx_ = GPU_texture_create_1d_array(name, w, h, mips, format, data); + } + else { + tx_ = GPU_texture_create_2d(name, w, h, mips, format, data); + } + } + else if (cubemap) { + if (layered) { + tx_ = GPU_texture_create_cube_array(name, w, d, mips, format, data); + } + else { + tx_ = GPU_texture_create_cube(name, w, mips, format, data); + } + } + else { + if (layered) { + tx_ = GPU_texture_create_2d_array(name, w, h, d, mips, format, data); + } + else { + tx_ = GPU_texture_create_3d(name, w, h, d, mips, format, GPU_DATA_FLOAT, data); + } + } + } + + ~Texture() + { + GPU_TEXTURE_FREE_SAFE(tx_); + } + + /* Use release_tmp after rendering or else mayhem will ensue. */ + void acquire_tmp(int w, int h, eGPUTextureFormat format, void *owner_) + { + if (tx_ == nullptr) { + if (tx_tmp_saved_ != nullptr) { + tx_ = tx_tmp_saved_; + return; + } + DrawEngineType *owner = (DrawEngineType *)owner_; + tx_ = DRW_texture_pool_query_2d(w, h, format, owner); + } + } + + /* Clears any reference. Workaround for pool texture not being releasable on demand. */ + void sync_tmp(void) + { + tx_tmp_saved_ = nullptr; + } + + void release_tmp(void) + { + tx_tmp_saved_ = tx_; + tx_ = nullptr; + } + + /* Return true is a texture has been created. */ + bool ensure(const char *name, + int w, + int h, + int d, + int mips, + eGPUTextureFormat format, + bool layered = false) + { + + /* TODO(fclem) In the future, we need to check if mip_count did not change. + * For now it's ok as we always define all mip level.*/ + if (tx_) { + int3 size = this->size(); + BLI_assert(GPU_texture_array(tx_) == layered); + if (size != int3(w, h, d) || GPU_texture_format(tx_) != format) { + GPU_TEXTURE_FREE_SAFE(tx_); + } + } + if (tx_ == nullptr) { + if (layered) { + tx_ = GPU_texture_create_2d_array(name, w, h, d, mips, format, nullptr); + } + else { + tx_ = GPU_texture_create_3d(name, w, h, d, mips, format, GPU_DATA_FLOAT, nullptr); + } + if (mips > 1) { + /* TODO(fclem) Remove once we have immutable storage or when mips are + * generated on creation. */ + GPU_texture_generate_mipmap(tx_); + } + return true; + } + return false; + } + + /* Return true is a texture has been created. */ + bool ensure(const char *name, int w, int h, int mips, eGPUTextureFormat format) + { + /* TODO(fclem) In the future, we need to check if mip_count did not change. + * For now it's ok as we always define all mip level.*/ + if (tx_ && (GPU_texture_width(tx_) != w || GPU_texture_height(tx_) != h || + GPU_texture_format(tx_) != format)) { + GPU_TEXTURE_FREE_SAFE(tx_); + } + if (tx_ == nullptr) { + tx_ = GPU_texture_create_2d(name, w, h, mips, format, nullptr); + if (mips > 1) { + /* TODO(fclem) Remove once we have immutable storage or when mips are + * generated on creation. */ + GPU_texture_generate_mipmap(tx_); + } + return true; + } + return false; + } + + /* Return true is a texture has been created. */ + bool ensure_cubemap(const char *name, int w, int mips, eGPUTextureFormat format) + { + /* TODO(fclem) In the future, we need to check if mip_count did not change. + * For now it's ok as we always define all mip level.*/ + if (tx_ && (GPU_texture_width(tx_) != w || GPU_texture_cube(tx_) != true || + GPU_texture_format(tx_) != format)) { + GPU_TEXTURE_FREE_SAFE(tx_); + } + if (tx_ == nullptr) { + tx_ = GPU_texture_create_cube(name, w, mips, format, nullptr); + if (mips > 1) { + /* TODO(fclem) Remove once we have immutable storage or when mips are + * generated on creation. */ + GPU_texture_generate_mipmap(tx_); + } + return true; + } + return false; + } + + /* Return true if a texture has been created. */ + bool ensure(int w, int h, int mips, eGPUTextureFormat format) + { + return ensure(name_, w, h, mips, format); + } + + void clear(vec4 color) + { + GPU_texture_clear(tx_, GPU_DATA_FLOAT, &color[0]); + } + void clear(float val) + { + float color[4] = {val, val, val, val}; + GPU_texture_clear(tx_, GPU_DATA_FLOAT, &color[0]); + } + void clear(uint val) + { + uint color[4] = {val, val, val, val}; + GPU_texture_clear(tx_, GPU_DATA_UINT, &color[0]); + } + void clear(uchar val) + { + uchar color[4] = {val, val, val, val}; + GPU_texture_clear(tx_, GPU_DATA_UBYTE, &color[0]); + } + void clear(int val) + { + int color[4] = {val, val, val, val}; + GPU_texture_clear(tx_, GPU_DATA_INT, &color[0]); + } + + void filter_mode(bool do_filter) + { + GPU_texture_filter_mode(tx_, do_filter); + } + + void release() + { + GPU_TEXTURE_FREE_SAFE(tx_); + } + + /* Returns a memory block that needs to be manually freed by MEM_freeN(). */ + template<typename T> T *read(eGPUDataFormat format, int miplvl = 0) + { + return reinterpret_cast<T *>(GPU_texture_read(tx_, format, miplvl)); + } + + Texture &operator=(Texture &a) + { + if (*this != a) { + this->tx_ = a.tx_; + this->name_ = a.name_; + a.tx_ = nullptr; + } + return *this; + } + /* To be able to use it with DRW_shgroup_uniform_texture(). */ + operator GPUTexture *() const + { + return tx_; + } + /* To be able to use it with DRW_shgroup_uniform_texture_ref(). */ + GPUTexture **operator&() + { + return &tx_; + } + + bool is_valid(void) const + { + return !!tx_; + } + int width(void) const + { + return GPU_texture_width(tx_); + } + int height(void) const + { + return GPU_texture_height(tx_); + } + int3 size(void) const + { + int3 size; + GPU_texture_get_mipmap_size(tx_, 0, size); + return size; + } +}; + +class Framebuffer { + private: + GPUFrameBuffer *fb_ = nullptr; + const char *name_; + + public: + Framebuffer() : name_(""){}; + Framebuffer(const char *name) : name_(name){}; + + ~Framebuffer() + { + GPU_FRAMEBUFFER_FREE_SAFE(fb_); + } + + void ensure(GPUAttachment depth = GPU_ATTACHMENT_NONE, + GPUAttachment color1 = GPU_ATTACHMENT_NONE, + GPUAttachment color2 = GPU_ATTACHMENT_NONE, + GPUAttachment color3 = GPU_ATTACHMENT_NONE, + GPUAttachment color4 = GPU_ATTACHMENT_NONE, + GPUAttachment color5 = GPU_ATTACHMENT_NONE, + GPUAttachment color6 = GPU_ATTACHMENT_NONE, + GPUAttachment color7 = GPU_ATTACHMENT_NONE, + GPUAttachment color8 = GPU_ATTACHMENT_NONE) + { + GPU_framebuffer_ensure_config( + &fb_, {depth, color1, color2, color3, color4, color5, color6, color7, color8}); + } + + Framebuffer &operator=(Framebuffer &a) + { + if (*this != a) { + this->fb_ = a.fb_; + this->name_ = a.name_; + a.fb_ = nullptr; + } + return *this; + } + + operator GPUFrameBuffer *() const + { + return fb_; + } +}; + +static inline void shgroup_geometry_call(DRWShadingGroup *grp, + Object *ob, + GPUBatch *geom, + int v_first = -1, + int v_count = -1, + bool use_instancing = false) +{ + if (grp == nullptr) { + return; + } + + if (v_first == -1) { + DRW_shgroup_call(grp, geom, ob); + } + else if (use_instancing) { + DRW_shgroup_call_instance_range(grp, ob, geom, v_first, v_count); + } + else { + DRW_shgroup_call_range(grp, ob, geom, v_first, v_count); + } +} + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl deleted file mode 100644 index 1061b2f91a2..00000000000 --- a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl +++ /dev/null @@ -1,417 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_math_lib.glsl) -#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) -#pragma BLENDER_REQUIRE(raytrace_lib.glsl) - -/* Based on Practical Realtime Strategies for Accurate Indirect Occlusion - * http://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf - * http://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pptx - */ - -#if defined(MESH_SHADER) -# if !defined(USE_ALPHA_HASH) -# if !defined(DEPTH_SHADER) -# if !defined(USE_ALPHA_BLEND) -# if !defined(USE_REFRACTION) -# define ENABLE_DEFERED_AO -# endif -# endif -# endif -# endif -#endif - -#ifndef ENABLE_DEFERED_AO -# if defined(STEP_RESOLVE) -# define ENABLE_DEFERED_AO -# endif -#endif - -uniform sampler2D horizonBuffer; - -/* aoSettings flags */ -#define USE_AO 1 -#define USE_BENT_NORMAL 2 -#define USE_DENOISE 4 - -#define NO_OCCLUSION_DATA OcclusionData(vec4(M_PI, -M_PI, M_PI, -M_PI), 1.0) - -struct OcclusionData { - /* 4 horizons angles, one in each direction around the view vector to form a cross pattern. */ - vec4 horizons; - /* Custom large scale occlusion. */ - float custom_occlusion; -}; - -vec4 pack_occlusion_data(OcclusionData data) -{ - return vec4(1.0 - data.horizons * vec4(1, -1, 1, -1) * M_1_PI); -} - -OcclusionData unpack_occlusion_data(vec4 v) -{ - return OcclusionData((1.0 - v) * vec4(1, -1, 1, -1) * M_PI, 0.0); -} - -vec2 get_ao_noise(void) -{ - vec2 noise = texelfetch_noise_tex(gl_FragCoord.xy).xy; - /* Decorrelate noise from AA. */ - /* TODO(fclem) we should use a more general approach for more random number dimensions. */ - noise = fract(noise * 6.1803402007); - return noise; -} - -vec2 get_ao_dir(float jitter) -{ - /* Only a quarter of a turn because we integrate using 2 slices. - * We use this instead of using utiltex circle noise to improve cache hits - * since all tracing direction will be in the same quadrant. */ - jitter *= M_PI_2; - return vec2(cos(jitter), sin(jitter)); -} - -/* Return horizon angle cosine. */ -float search_horizon(vec3 vI, - vec3 vP, - float noise, - ScreenSpaceRay ssray, - sampler2D depth_tx, - const float inverted, - float radius, - const float sample_count) -{ - /* Init at cos(M_PI). */ - float h = (inverted != 0.0) ? 1.0 : -1.0; - - ssray.max_time -= 1.0; - - if (ssray.max_time <= 2.0) { - /* Produces self shadowing under this threshold. */ - return fast_acos(h); - } - - float prev_time, time = 0.0; - for (float iter = 0.0; time < ssray.max_time && iter < sample_count; iter++) { - prev_time = time; - /* Gives us good precision at center and ensure we cross at least one pixel per iteration. */ - time = 1.0 + iter + sqr((iter + noise) / sample_count) * ssray.max_time; - float stride = time - prev_time; - float lod = (log2(stride) - noise) / (1.0 + aoQuality); - - vec2 uv = ssray.origin.xy + ssray.direction.xy * time; - float depth = textureLod(depth_tx, uv * hizUvScale.xy, floor(lod)).r; - - if (depth == 1.0 && inverted == 0.0) { - /* Skip background. Avoids making shadow on the geometry near the far plane. */ - continue; - } - - /* Bias depth a bit to avoid self shadowing issues. */ - const float bias = 2.0 * 2.4e-7; - depth += (inverted != 0.0) ? -bias : bias; - - vec3 s = get_view_space_from_depth(uv, depth); - vec3 omega_s = s - vP; - float len = length(omega_s); - /* Sample's horizon angle cosine. */ - float s_h = dot(vI, omega_s / len); - /* Blend weight to fade artifacts. */ - float dist_ratio = abs(len) / radius; - /* Sphere falloff. */ - float dist_fac = sqr(saturate(dist_ratio)); - /* Unbiased, gives too much hard cut behind objects */ - // float dist_fac = step(0.999, dist_ratio); - - if (inverted != 0.0) { - h = min(h, s_h); - } - else { - h = mix(max(h, s_h), h, dist_fac); - } - } - return fast_acos(h); -} - -OcclusionData occlusion_search( - vec3 vP, sampler2D depth_tx, float radius, const float inverted, const float dir_sample_count) -{ - if ((int(aoSettings) & USE_AO) == 0) { - return NO_OCCLUSION_DATA; - } - - vec2 noise = get_ao_noise(); - vec2 dir = get_ao_dir(noise.x); - vec2 uv = get_uvs_from_view(vP); - vec3 vI = ((ProjectionMatrix[3][3] == 0.0) ? normalize(-vP) : vec3(0.0, 0.0, 1.0)); - vec3 avg_dir = vec3(0.0); - float avg_apperture = 0.0; - - OcclusionData data = (inverted != 0.0) ? OcclusionData(vec4(0, 0, 0, 0), 1.0) : - NO_OCCLUSION_DATA; - - for (int i = 0; i < 2; i++) { - Ray ray; - ray.origin = vP; - ray.direction = vec3(dir * radius, 0.0); - - ScreenSpaceRay ssray; - - ssray = raytrace_screenspace_ray_create(ray); - data.horizons[0 + i * 2] = search_horizon( - vI, vP, noise.y, ssray, depth_tx, inverted, radius, dir_sample_count); - - ray.direction = -ray.direction; - - ssray = raytrace_screenspace_ray_create(ray); - data.horizons[1 + i * 2] = -search_horizon( - vI, vP, noise.y, ssray, depth_tx, inverted, radius, dir_sample_count); - - /* Rotate 90 degrees. */ - dir = vec2(-dir.y, dir.x); - } - - return data; -} - -vec2 clamp_horizons_to_hemisphere(vec2 horizons, float angle_N, const float inverted) -{ - /* Add a little bias to fight self shadowing. */ - const float max_angle = M_PI_2 - 0.05; - - if (inverted != 0.0) { - horizons.x = max(horizons.x, angle_N + max_angle); - horizons.y = min(horizons.y, angle_N - max_angle); - } - else { - horizons.x = min(horizons.x, angle_N + max_angle); - horizons.y = max(horizons.y, angle_N - max_angle); - } - return horizons; -} - -void occlusion_eval(OcclusionData data, - vec3 V, - vec3 N, - vec3 Ng, - const float inverted, - out float visibility, - out float visibility_error, - out vec3 bent_normal) -{ - /* No error by default. */ - visibility_error = 1.0; - - if ((int(aoSettings) & USE_AO) == 0) { - visibility = data.custom_occlusion; - bent_normal = N; - return; - } - - bool early_out = (inverted != 0.0) ? (max_v4(abs(data.horizons)) == 0.0) : - (min_v4(abs(data.horizons)) == M_PI); - if (early_out) { - visibility = saturate(dot(N, Ng) * 0.5 + 0.5); - visibility = min(visibility, data.custom_occlusion); - - if ((int(aoSettings) & USE_BENT_NORMAL) == 0) { - bent_normal = N; - } - else { - bent_normal = safe_normalize(N + Ng); - } - return; - } - - vec2 noise = get_ao_noise(); - vec2 dir = get_ao_dir(noise.x); - - visibility_error = 0.0; - visibility = 0.0; - bent_normal = N * 0.001; - - for (int i = 0; i < 2; i++) { - vec3 T = transform_direction(ViewMatrixInverse, vec3(dir, 0.0)); - /* Setup integration domain around V. */ - vec3 B = normalize(cross(V, T)); - T = normalize(cross(B, V)); - - float proj_N_len; - vec3 proj_N = normalize_len(N - B * dot(N, B), proj_N_len); - vec3 proj_Ng = normalize(Ng - B * dot(Ng, B)); - - vec2 h = (i == 0) ? data.horizons.xy : data.horizons.zw; - - float N_sin = dot(proj_N, T); - float Ng_sin = dot(proj_Ng, T); - float N_cos = saturate(dot(proj_N, V)); - float Ng_cos = saturate(dot(proj_Ng, V)); - /* Gamma, angle between normalized projected normal and view vector. */ - float angle_Ng = sign(Ng_sin) * fast_acos(Ng_cos); - float angle_N = sign(N_sin) * fast_acos(N_cos); - /* Clamp horizons to hemisphere around shading normal. */ - h = clamp_horizons_to_hemisphere(h, angle_N, inverted); - - float bent_angle = (h.x + h.y) * 0.5; - /* NOTE: here we multiply z by 0.5 as it shows less difference with the geometric normal. - * Also modulate by projected normal length to reduce issues with slanted surfaces. - * All of this is ad-hoc and not really grounded. */ - bent_normal += proj_N_len * (T * sin(bent_angle) + V * 0.5 * cos(bent_angle)); - - /* Clamp to geometric normal only for integral to keep smooth bent normal. */ - /* This is done to match Cycles ground truth but adds some computation. */ - h = clamp_horizons_to_hemisphere(h, angle_Ng, inverted); - - /* Inner integral (Eq. 7). */ - float a = dot(-cos(2.0 * h - angle_N) + N_cos + 2.0 * h * N_sin, vec2(0.25)); - /* Correct normal not on plane (Eq. 8). */ - visibility += proj_N_len * a; - /* Using a very low number of slices (2) leads to over-darkening of surfaces orthogonal to - * the view. This is particularly annoying for sharp reflections occlusion. So we compute how - * much the error is and correct the visibility later. */ - visibility_error += proj_N_len; - - /* Rotate 90 degrees. */ - dir = vec2(-dir.y, dir.x); - } - /* We integrated 2 directions. */ - visibility *= 0.5; - visibility_error *= 0.5; - - visibility = min(visibility, data.custom_occlusion); - - if ((int(aoSettings) & USE_BENT_NORMAL) == 0) { - bent_normal = N; - } - else { - /* NOTE: using pow(visibility, 6.0) produces NaN (see T87369). */ - float tmp = saturate(pow6(visibility)); - bent_normal = normalize(mix(bent_normal, N, tmp)); - } -} - -/* Multibounce approximation base on surface albedo. - * Page 78 in the .pdf version. */ -float gtao_multibounce(float visibility, vec3 albedo) -{ - if (aoBounceFac == 0.0) { - return visibility; - } - - /* Median luminance. Because Colored multibounce looks bad. */ - float lum = dot(albedo, vec3(0.3333)); - - float a = 2.0404 * lum - 0.3324; - float b = -4.7951 * lum + 0.6417; - float c = 2.7552 * lum + 0.6903; - - float x = visibility; - return max(x, ((x * a + b) * x + c) * x); -} - -float diffuse_occlusion(OcclusionData data, vec3 V, vec3 N, vec3 Ng) -{ - vec3 unused; - float unused_error; - float visibility; - occlusion_eval(data, V, N, Ng, 0.0, visibility, unused_error, unused); - /* Scale by user factor */ - visibility = pow(saturate(visibility), aoFactor); - return visibility; -} - -float diffuse_occlusion( - OcclusionData data, vec3 V, vec3 N, vec3 Ng, vec3 albedo, out vec3 bent_normal) -{ - float visibility; - float unused_error; - occlusion_eval(data, V, N, Ng, 0.0, visibility, unused_error, bent_normal); - - visibility = gtao_multibounce(visibility, albedo); - /* Scale by user factor */ - visibility = pow(saturate(visibility), aoFactor); - return visibility; -} - -/** - * Approximate the area of intersection of two spherical caps - * radius1 : First cap’s radius (arc length in radians) - * radius2 : Second caps’ radius (in radians) - * dist : Distance between caps (radians between centers of caps) - * NOTE: Result is divided by pi to save one multiply. - */ -float spherical_cap_intersection(float radius1, float radius2, float dist) -{ - /* From "Ambient Aperture Lighting" by Chris Oat - * Slide 15. */ - float max_radius = max(radius1, radius2); - float min_radius = min(radius1, radius2); - float sum_radius = radius1 + radius2; - float area; - if (dist <= max_radius - min_radius) { - /* One cap in completely inside the other */ - area = 1.0 - cos(min_radius); - } - else if (dist >= sum_radius) { - /* No intersection exists */ - area = 0; - } - else { - float diff = max_radius - min_radius; - area = smoothstep(0.0, 1.0, 1.0 - saturate((dist - diff) / (sum_radius - diff))); - area *= 1.0 - cos(min_radius); - } - return area; -} - -float specular_occlusion( - OcclusionData data, vec3 V, vec3 N, float roughness, inout vec3 specular_dir) -{ - vec3 visibility_dir; - float visibility_error; - float visibility; - occlusion_eval(data, V, N, N, 0.0, visibility, visibility_error, visibility_dir); - - /* Correct visibility error for very sharp surfaces. */ - visibility *= mix(safe_rcp(visibility_error), 1.0, roughness); - - specular_dir = normalize(mix(specular_dir, visibility_dir, roughness * (1.0 - visibility))); - - /* Visibility to cone angle (eq. 18). */ - float vis_angle = fast_acos(sqrt(1 - visibility)); - /* Roughness to cone angle (eq. 26). */ - float spec_angle = max(0.00990998744964599609375, fast_acos(cone_cosine(roughness))); - /* Angle between cone axes. */ - float cone_cone_dist = fast_acos(saturate(dot(visibility_dir, specular_dir))); - float cone_nor_dist = fast_acos(saturate(dot(N, specular_dir))); - - float isect_solid_angle = spherical_cap_intersection(vis_angle, spec_angle, cone_cone_dist); - float specular_solid_angle = spherical_cap_intersection(M_PI_2, spec_angle, cone_nor_dist); - float specular_occlusion = isect_solid_angle / specular_solid_angle; - /* Mix because it is unstable in unoccluded areas. */ - float tmp = saturate(pow8(visibility)); - visibility = mix(specular_occlusion, 1.0, tmp); - - /* Scale by user factor */ - visibility = pow(saturate(visibility), aoFactor); - return visibility; -} - -/* Use the right occlusion. */ -OcclusionData occlusion_load(vec3 vP, float custom_occlusion) -{ - /* Default to fully opened cone. */ - OcclusionData data = NO_OCCLUSION_DATA; - -#ifdef ENABLE_DEFERED_AO - if ((int(aoSettings) & USE_AO) != 0) { - data = unpack_occlusion_data(texelFetch(horizonBuffer, ivec2(gl_FragCoord.xy), 0)); - } -#else - /* For blended surfaces. */ - data = occlusion_search(vP, maxzBuffer, aoDistance, 0.0, 8.0); -#endif - - data.custom_occlusion = custom_occlusion; - - return data; -} diff --git a/source/blender/draw/engines/eevee/shaders/background_vert.glsl b/source/blender/draw/engines/eevee/shaders/background_vert.glsl deleted file mode 100644 index ab5d9a7ebe4..00000000000 --- a/source/blender/draw/engines/eevee/shaders/background_vert.glsl +++ /dev/null @@ -1,27 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(surface_lib.glsl) - -in vec2 pos; - -RESOURCE_ID_VARYING - -void main() -{ - GPU_INTEL_VERTEX_SHADER_WORKAROUND - - PASS_RESOURCE_ID - - gl_Position = vec4(pos, 1.0, 1.0); - viewPosition = vec3(pos, -1.0); - -#ifndef VOLUMETRICS - /* Not used in practice but needed to avoid compilation errors. */ - worldPosition = viewPosition; - worldNormal = viewNormal = normalize(-viewPosition); -#endif - -#ifdef USE_ATTR - pass_attr(viewPosition, NormalMatrix, ModelMatrixInverse); -#endif -} diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl deleted file mode 100644 index 4ee21cf8c2e..00000000000 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ /dev/null @@ -1,206 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_math_lib.glsl) - -vec3 diffuse_dominant_dir(vec3 bent_normal) -{ - return bent_normal; -} - -vec3 specular_dominant_dir(vec3 N, vec3 V, float roughness) -{ - vec3 R = -reflect(V, N); - float smoothness = 1.0 - roughness; - float fac = smoothness * (sqrt(smoothness) + roughness); - return normalize(mix(N, R, fac)); -} - -float ior_from_f0(float f0) -{ - float f = sqrt(f0); - return (-f - 1.0) / (f - 1.0); -} - -/* Simplified form of F_eta(eta, 1.0). */ -float f0_from_ior(float eta) -{ - float A = (eta - 1.0) / (eta + 1.0); - return A * A; -} - -vec3 refraction_dominant_dir(vec3 N, vec3 V, float roughness, float ior) -{ - /* TODO: This a bad approximation. Better approximation should fit - * the refracted vector and roughness into the best prefiltered reflection - * lobe. */ - /* Correct the IOR for ior < 1.0 to not see the abrupt delimitation or the TIR */ - ior = (ior < 1.0) ? mix(ior, 1.0, roughness) : ior; - float eta = 1.0 / ior; - - float NV = dot(N, -V); - - /* Custom Refraction. */ - float k = 1.0 - eta * eta * (1.0 - NV * NV); - k = max(0.0, k); /* Only this changes. */ - vec3 R = eta * -V - (eta * NV + sqrt(k)) * N; - - return R; -} - -/* Fresnel monochromatic, perfect mirror */ -float F_eta(float eta, float cos_theta) -{ - /* compute fresnel reflectance without explicitly computing - * the refracted direction */ - float c = abs(cos_theta); - float g = eta * eta - 1.0 + c * c; - if (g > 0.0) { - g = sqrt(g); - float A = (g - c) / (g + c); - float B = (c * (g + c) - 1.0) / (c * (g - c) + 1.0); - return 0.5 * A * A * (1.0 + B * B); - } - /* Total internal reflections. */ - return 1.0; -} - -/* Fresnel color blend base on fresnel factor */ -vec3 F_color_blend(float eta, float fresnel, vec3 f0_color) -{ - float f0 = f0_from_ior(eta); - float fac = saturate((fresnel - f0) / (1.0 - f0)); - return mix(f0_color, vec3(1.0), fac); -} - -/* Fresnel split-sum approximation. */ -vec3 F_brdf_single_scatter(vec3 f0, vec3 f90, vec2 lut) -{ - /* Unreal specular matching : if specular color is below 2% intensity, - * treat as shadowning */ - return lut.y * f90 + lut.x * f0; -} - -/* Multi-scattering brdf approximation from : - * "A Multiple-Scattering Microfacet Model for Real-Time Image-based Lighting" - * by Carmelo J. Fdez-Agüera. */ -vec3 F_brdf_multi_scatter(vec3 f0, vec3 f90, vec2 lut) -{ - vec3 FssEss = lut.y * f90 + lut.x * f0; - - float Ess = lut.x + lut.y; - float Ems = 1.0 - Ess; - vec3 Favg = f0 + (1.0 - f0) / 21.0; - vec3 Fms = FssEss * Favg / (1.0 - (1.0 - Ess) * Favg); - /* We don't do anything special for diffuse surfaces because the principle bsdf - * does not care about energy conservation of the specular layer for dielectrics. */ - return FssEss + Fms * Ems; -} - -/* GGX */ -float D_ggx_opti(float NH, float a2) -{ - float tmp = (NH * a2 - NH) * NH + 1.0; - return M_PI * tmp * tmp; /* Doing RCP and mul a2 at the end */ -} - -float G1_Smith_GGX_opti(float NX, float a2) -{ - /* Using Brian Karis approach and refactoring by NX/NX - * this way the (2*NL)*(2*NV) in G = G1(V) * G1(L) gets canceled by the brdf denominator 4*NL*NV - * Rcp is done on the whole G later - * Note that this is not convenient for the transmission formula */ - return NX + sqrt(NX * (NX - NX * a2) + a2); - /* return 2 / (1 + sqrt(1 + a2 * (1 - NX*NX) / (NX*NX) ) ); /* Reference function */ -} - -float bsdf_ggx(vec3 N, vec3 L, vec3 V, float roughness) -{ - float a = roughness; - float a2 = a * a; - - vec3 H = normalize(L + V); - float NH = max(dot(N, H), 1e-8); - float NL = max(dot(N, L), 1e-8); - float NV = max(dot(N, V), 1e-8); - - float G = G1_Smith_GGX_opti(NV, a2) * G1_Smith_GGX_opti(NL, a2); /* Doing RCP at the end */ - float D = D_ggx_opti(NH, a2); - - /* Denominator is canceled by G1_Smith */ - /* bsdf = D * G / (4.0 * NL * NV); /* Reference function */ - return NL * a2 / (D * G); /* NL to Fit cycles Equation : line. 345 in bsdf_microfacet.h */ -} - -void accumulate_light(vec3 light, float fac, inout vec4 accum) -{ - accum += vec4(light, 1.0) * min(fac, (1.0 - accum.a)); -} - -/* Same thing as Cycles without the comments to make it shorter. */ -vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N) -{ - vec3 R = -reflect(I, N); - - /* Reflection rays may always be at least as shallow as the incoming ray. */ - float threshold = min(0.9 * dot(Ng, I), 0.025); - if (dot(Ng, R) >= threshold) { - return N; - } - - float NdotNg = dot(N, Ng); - vec3 X = normalize(N - NdotNg * Ng); - - float Ix = dot(I, X), Iz = dot(I, Ng); - float Ix2 = sqr(Ix), Iz2 = sqr(Iz); - float a = Ix2 + Iz2; - - float b = sqrt(Ix2 * (a - sqr(threshold))); - float c = Iz * threshold + a; - - float fac = 0.5 / a; - float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c); - bool valid1 = (N1_z2 > 1e-5) && (N1_z2 <= (1.0 + 1e-5)); - bool valid2 = (N2_z2 > 1e-5) && (N2_z2 <= (1.0 + 1e-5)); - - vec2 N_new; - if (valid1 && valid2) { - /* If both are possible, do the expensive reflection-based check. */ - vec2 N1 = vec2(safe_sqrt(1.0 - N1_z2), safe_sqrt(N1_z2)); - vec2 N2 = vec2(safe_sqrt(1.0 - N2_z2), safe_sqrt(N2_z2)); - - float R1 = 2.0 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz; - float R2 = 2.0 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz; - - valid1 = (R1 >= 1e-5); - valid2 = (R2 >= 1e-5); - if (valid1 && valid2) { - N_new = (R1 < R2) ? N1 : N2; - } - else { - N_new = (R1 > R2) ? N1 : N2; - } - } - else if (valid1 || valid2) { - float Nz2 = valid1 ? N1_z2 : N2_z2; - N_new = vec2(safe_sqrt(1.0 - Nz2), safe_sqrt(Nz2)); - } - else { - return Ng; - } - return N_new.x * X + N_new.y * Ng; -} - -/* ----------- Cone angle Approximation --------- */ - -/* Return a fitted cone angle given the input roughness */ -float cone_cosine(float r) -{ - /* Using phong gloss - * roughness = sqrt(2/(gloss+2)) */ - float gloss = -2 + 2 / (r * r); - /* Drobot 2014 in GPUPro5 */ - // return cos(2.0 * sqrt(2.0 / (gloss + 2))); - /* Uludag 2014 in GPUPro5 */ - // return pow(0.244, 1 / (gloss + 1)); - /* Jimenez 2016 in Practical Realtime Strategies for Accurate Indirect Occlusion. */ - return exp2(-3.32193 * r * r); -} diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl deleted file mode 100644 index 4c1544654c1..00000000000 --- a/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl +++ /dev/null @@ -1,59 +0,0 @@ -#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) -#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) - -uniform float sampleCount; - -out vec2 FragColor; - -void main() -{ - /* Make sure coordinates are covering the whole [0..1] range at texel center. */ - float y = floor(gl_FragCoord.y) / (LUT_SIZE - 1); - float x = floor(gl_FragCoord.x) / (LUT_SIZE - 1); - - float NV = clamp(1.0 - y * y, 1e-4, 0.9999); - float a = x * x; - float a2 = clamp(a * a, 1e-4, 0.9999); - - vec3 V = vec3(sqrt(1.0 - NV * NV), 0.0, NV); - - /* Integrating BRDF */ - float brdf_accum = 0.0; - float fresnel_accum = 0.0; - for (float j = 0.0; j < sampleCount; j++) { - for (float i = 0.0; i < sampleCount; i++) { - vec3 Xi = (vec3(i, j, 0.0) + 0.5) / sampleCount; - Xi.yz = vec2(cos(Xi.y * M_2PI), sin(Xi.y * M_2PI)); - - /* Microfacet normal */ - vec3 H = sample_ggx(Xi, a, V); - vec3 L = -reflect(V, H); - float NL = L.z; - - if (NL > 0.0) { - float NH = max(H.z, 0.0); - float VH = max(dot(V, H), 0.0); - - float G1_v = G1_Smith_GGX_opti(NV, a2); - float G1_l = G1_Smith_GGX_opti(NL, a2); - /* See G1_Smith_GGX_opti for explanations. */ - float G_smith = 4.0 * NV * NL / (G1_v * G1_l); - - float brdf = (G_smith * VH) / (NH * NV); - - /* Follow maximum specular value for principled bsdf. */ - const float specular = 1.0; - const float eta = (2.0 / (1.0 - sqrt(0.08 * specular))) - 1.0; - float fresnel = F_eta(eta, VH); - float Fc = F_color_blend(eta, fresnel, vec3(0)).r; - - brdf_accum += (1.0 - Fc) * brdf; - fresnel_accum += Fc * brdf; - } - } - } - brdf_accum /= sampleCount * sampleCount; - fresnel_accum /= sampleCount * sampleCount; - - FragColor = vec2(brdf_accum, fresnel_accum); -} diff --git a/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl b/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl deleted file mode 100644 index c8c3fa548fc..00000000000 --- a/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl +++ /dev/null @@ -1,89 +0,0 @@ -#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) -#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) - -uniform float sampleCount; -uniform float z; - -out vec4 FragColor; - -void main() -{ - float x = floor(gl_FragCoord.x) / (LUT_SIZE - 1.0); - float y = floor(gl_FragCoord.y) / (LUT_SIZE - 1.0); - - float ior = clamp(sqrt(x), 0.05, 0.999); - /* ior is sin of critical angle. */ - float critical_cos = sqrt(1.0 - saturate(ior * ior)); - - y = y * 2.0 - 1.0; - /* Maximize texture usage on both sides of the critical angle. */ - y *= (y > 0.0) ? (1.0 - critical_cos) : critical_cos; - /* Center LUT around critical angle to avoid strange interpolation issues when the critical - * angle is changing. */ - y += critical_cos; - float NV = clamp(y, 1e-4, 0.9999); - - float a = z * z; - float a2 = clamp(a * a, 1e-8, 0.9999); - - vec3 V = vec3(sqrt(1.0 - NV * NV), 0.0, NV); - - /* Integrating BTDF */ - float btdf_accum = 0.0; - float fresnel_accum = 0.0; - for (float j = 0.0; j < sampleCount; j++) { - for (float i = 0.0; i < sampleCount; i++) { - vec3 Xi = (vec3(i, j, 0.0) + 0.5) / sampleCount; - Xi.yz = vec2(cos(Xi.y * M_2PI), sin(Xi.y * M_2PI)); - - /* Microfacet normal. */ - vec3 H = sample_ggx(Xi, a2, V); - - float VH = dot(V, H); - - /* Check if there is total internal reflections. */ - float fresnel = F_eta(ior, VH); - - fresnel_accum += fresnel; - - float eta = 1.0 / ior; - if (dot(H, V) < 0.0) { - H = -H; - eta = ior; - } - - vec3 L = refract(-V, H, eta); - float NL = -L.z; - - if ((NL > 0.0) && (fresnel < 0.999)) { - float LH = dot(L, H); - - /* Balancing the adjustments made in G1_Smith. */ - float G1_l = NL * 2.0 / G1_Smith_GGX_opti(NL, a2); - - /* btdf = abs(VH*LH) * (ior*ior) * D * G(V) * G(L) / (Ht2 * NV) - * pdf = (VH * abs(LH)) * (ior*ior) * D * G(V) / (Ht2 * NV) */ - float btdf = G1_l * abs(VH * LH) / (VH * abs(LH)); - - btdf_accum += btdf; - } - } - } - btdf_accum /= sampleCount * sampleCount; - fresnel_accum /= sampleCount * sampleCount; - - if (z == 0.0) { - /* Perfect mirror. Increased precision because the roughness is clamped. */ - fresnel_accum = F_eta(ior, NV); - } - - if (x == 0.0) { - /* Special case. */ - fresnel_accum = 1.0; - btdf_accum = 0.0; - } - - /* There is place to put multi-scatter result (which is a little bit different still) - * and / or lobe fitting for better sampling of. */ - FragColor = vec4(btdf_accum, fresnel_accum, 0.0, 1.0); -} diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl deleted file mode 100644 index 4f9791ac95f..00000000000 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl +++ /dev/null @@ -1,88 +0,0 @@ -#pragma BLENDER_REQUIRE(lights_lib.glsl) -#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) -#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) - -struct ClosureInputDiffuse { - vec3 N; /** Shading normal. */ - vec3 albedo; /** Used for multibounce GTAO approximation. Not applied to final radiance. */ -}; - -#define CLOSURE_INPUT_Diffuse_DEFAULT ClosureInputDiffuse(vec3(0.0), vec3(0.0)) - -struct ClosureEvalDiffuse { - vec3 probe_sampling_dir; /** Direction to sample probes from. */ - float ambient_occlusion; /** Final occlusion for distant lighting. */ -}; - -/* Stubs. */ -#define ClosureOutputDiffuse ClosureOutput -#define closure_Diffuse_planar_eval(cl_in, cl_eval, cl_common, data, cl_out) -#define closure_Diffuse_cubemap_eval(cl_in, cl_eval, cl_common, data, cl_out) - -ClosureEvalDiffuse closure_Diffuse_eval_init(inout ClosureInputDiffuse cl_in, - ClosureEvalCommon cl_common, - out ClosureOutputDiffuse cl_out) -{ - cl_in.N = safe_normalize(cl_in.N); - cl_out.radiance = vec3(0.0); - - ClosureEvalDiffuse cl_eval; - cl_eval.ambient_occlusion = diffuse_occlusion(cl_common.occlusion_data, - cl_common.V, - cl_in.N, - cl_common.Ng, - cl_in.albedo, - cl_eval.probe_sampling_dir); - return cl_eval; -} - -void closure_Diffuse_light_eval(ClosureInputDiffuse cl_in, - ClosureEvalDiffuse cl_eval, - ClosureEvalCommon cl_common, - ClosureLightData light, - inout ClosureOutputDiffuse cl_out) -{ - float radiance = light_diffuse(light.data, cl_in.N, cl_common.V, light.L); - /* TODO(fclem) We could try to shadow lights that are shadowless with the ambient_occlusion - * factor here. */ - cl_out.radiance += light.data.l_color * - (light.data.l_diff * light.vis * light.contact_shadow * radiance); -} - -void closure_Diffuse_grid_eval(ClosureInputDiffuse cl_in, - ClosureEvalDiffuse cl_eval, - ClosureEvalCommon cl_common, - ClosureGridData grid, - inout ClosureOutputDiffuse cl_out) -{ - vec3 probe_radiance = probe_evaluate_grid( - grid.data, cl_common.P, cl_eval.probe_sampling_dir, grid.local_pos); - cl_out.radiance += grid.attenuation * probe_radiance; -} - -void closure_Diffuse_indirect_end(ClosureInputDiffuse cl_in, - ClosureEvalDiffuse cl_eval, - ClosureEvalCommon cl_common, - inout ClosureOutputDiffuse cl_out) -{ - /* If not enough light has been accumulated from probes, use the world specular cubemap - * to fill the remaining energy needed. */ - if (cl_common.diffuse_accum > 0.0) { - vec3 probe_radiance = probe_evaluate_world_diff(cl_eval.probe_sampling_dir); - cl_out.radiance += cl_common.diffuse_accum * probe_radiance; - } - /* Apply occlusion on radiance before the light loop. */ - cl_out.radiance *= cl_eval.ambient_occlusion; -} - -void closure_Diffuse_eval_end(ClosureInputDiffuse cl_in, - ClosureEvalDiffuse cl_eval, - ClosureEvalCommon cl_common, - inout ClosureOutputDiffuse cl_out) -{ -#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND) - /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */ - cl_out.radiance = vec3(0.0); - return; -#endif -} diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl deleted file mode 100644 index 00d265a48b0..00000000000 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl +++ /dev/null @@ -1,145 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) -#pragma BLENDER_REQUIRE(lights_lib.glsl) -#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) -#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) -#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl) - -struct ClosureInputGlossy { - vec3 N; /** Shading normal. */ - float roughness; /** Input roughness, not squared. */ -}; - -#define CLOSURE_INPUT_Glossy_DEFAULT ClosureInputGlossy(vec3(0.0), 0.0) - -struct ClosureEvalGlossy { - vec4 ltc_mat; /** LTC matrix values. */ - float ltc_brdf_scale; /** LTC BRDF scaling. */ - vec3 probe_sampling_dir; /** Direction to sample probes from. */ - float spec_occlusion; /** Specular Occlusion. */ - vec3 raytrace_radiance; /** Raytrace reflection to be accumulated after occlusion. */ -}; - -/* Stubs. */ -#define ClosureOutputGlossy ClosureOutput -#define closure_Glossy_grid_eval(cl_in, cl_eval, cl_common, data, cl_out) - -#ifdef STEP_RESOLVE /* SSR */ -/* Prototype. */ -void raytrace_resolve(ClosureInputGlossy cl_in, - inout ClosureEvalGlossy cl_eval, - inout ClosureEvalCommon cl_common, - inout ClosureOutputGlossy cl_out); -#endif - -ClosureEvalGlossy closure_Glossy_eval_init(inout ClosureInputGlossy cl_in, - inout ClosureEvalCommon cl_common, - out ClosureOutputGlossy cl_out) -{ - cl_in.N = safe_normalize(cl_in.N); - cl_in.roughness = clamp(cl_in.roughness, 1e-8, 0.9999); - cl_out.radiance = vec3(0.0); - -#ifndef STEP_RESOLVE /* SSR */ - cl_in.N = ensure_valid_reflection(cl_common.Ng, cl_common.V, cl_in.N); -#endif - - float NV = dot(cl_in.N, cl_common.V); - vec2 lut_uv = lut_coords(NV, cl_in.roughness); - - ClosureEvalGlossy cl_eval; - cl_eval.ltc_mat = texture(utilTex, vec3(lut_uv, LTC_MAT_LAYER)); - cl_eval.probe_sampling_dir = specular_dominant_dir(cl_in.N, cl_common.V, sqr(cl_in.roughness)); - cl_eval.spec_occlusion = specular_occlusion(cl_common.occlusion_data, - cl_common.V, - cl_common.N, - cl_in.roughness, - cl_eval.probe_sampling_dir); - cl_eval.raytrace_radiance = vec3(0.0); - -#ifdef STEP_RESOLVE /* SSR */ - raytrace_resolve(cl_in, cl_eval, cl_common, cl_out); -#endif - - /* The brdf split sum LUT is applied after the radiance accumulation. - * Correct the LTC so that its energy is constant. */ - /* TODO(fclem) Optimize this so that only one scale factor is stored. */ - vec4 ltc_brdf = texture(utilTex, vec3(lut_uv, LTC_BRDF_LAYER)).barg; - vec2 split_sum_brdf = ltc_brdf.zw; - cl_eval.ltc_brdf_scale = (ltc_brdf.x + ltc_brdf.y) / (split_sum_brdf.x + split_sum_brdf.y); - return cl_eval; -} - -void closure_Glossy_light_eval(ClosureInputGlossy cl_in, - ClosureEvalGlossy cl_eval, - ClosureEvalCommon cl_common, - ClosureLightData light, - inout ClosureOutputGlossy cl_out) -{ - float radiance = light_specular(light.data, cl_eval.ltc_mat, cl_in.N, cl_common.V, light.L); - radiance *= cl_eval.ltc_brdf_scale; - cl_out.radiance += light.data.l_color * - (light.data.l_spec * light.vis * light.contact_shadow * radiance); -} - -void closure_Glossy_planar_eval(ClosureInputGlossy cl_in, - ClosureEvalGlossy cl_eval, - inout ClosureEvalCommon cl_common, - ClosurePlanarData planar, - inout ClosureOutputGlossy cl_out) -{ -#ifndef STEP_RESOLVE /* SSR already evaluates planar reflections. */ - float attenuation = planar.attenuation * probe_attenuation_planar_normal_roughness( - planar.data, cl_in.N, cl_in.roughness); - - vec3 probe_radiance = probe_evaluate_planar( - planar.id, planar.data, cl_common.P, cl_in.N, cl_common.V, cl_in.roughness); - - cl_out.radiance = mix(cl_out.radiance, probe_radiance, attenuation); -#endif -} - -void closure_Glossy_cubemap_eval(ClosureInputGlossy cl_in, - ClosureEvalGlossy cl_eval, - ClosureEvalCommon cl_common, - ClosureCubemapData cube, - inout ClosureOutputGlossy cl_out) -{ - vec3 probe_radiance = probe_evaluate_cube( - cube.id, cl_common.P, cl_eval.probe_sampling_dir, cl_in.roughness); - cl_out.radiance += cube.attenuation * probe_radiance; -} - -void closure_Glossy_indirect_end(ClosureInputGlossy cl_in, - ClosureEvalGlossy cl_eval, - ClosureEvalCommon cl_common, - inout ClosureOutputGlossy cl_out) -{ - /* If not enough light has been accumulated from probes, use the world specular cubemap - * to fill the remaining energy needed. */ - if (specToggle && cl_common.specular_accum > 0.0) { - vec3 probe_radiance = probe_evaluate_world_spec(cl_eval.probe_sampling_dir, cl_in.roughness); - cl_out.radiance += cl_common.specular_accum * probe_radiance; - } - - /* Apply occlusion on distant lighting. */ - cl_out.radiance *= cl_eval.spec_occlusion; - /* Apply Raytrace reflections after occlusion since they are direct, local reflections. */ - cl_out.radiance += cl_eval.raytrace_radiance; -} - -void closure_Glossy_eval_end(ClosureInputGlossy cl_in, - ClosureEvalGlossy cl_eval, - ClosureEvalCommon cl_common, - inout ClosureOutputGlossy cl_out) -{ -#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND) - /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */ - cl_out.radiance = vec3(0.0); - return; -#endif - - if (!specToggle) { - cl_out.radiance = vec3(0.0); - } -} diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl deleted file mode 100644 index 311887cf2f5..00000000000 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl +++ /dev/null @@ -1,324 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) -#pragma BLENDER_REQUIRE(lights_lib.glsl) -#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) - -/** - * Extensive use of Macros to be able to change the maximum amount of evaluated closure easily. - * NOTE: GLSL does not support variadic macros. - * - * Example - * // Declare the cl_eval function - * CLOSURE_EVAL_FUNCTION_DECLARE_3(name, Diffuse, Glossy, Refraction); - * // Declare the inputs & outputs - * CLOSURE_VARS_DECLARE(Diffuse, Glossy, Refraction); - * // Specify inputs - * in_Diffuse_0.N = N; - * ... - * // Call the cl_eval function - * CLOSURE_EVAL_FUNCTION_3(name, Diffuse, Glossy, Refraction); - * // Get the cl_out - * closure.radiance = out_Diffuse_0.radiance + out_Glossy_1.radiance + out_Refraction_2.radiance; - */ - -#define CLOSURE_VARS_DECLARE(t0, t1, t2, t3) \ - ClosureInputCommon in_common = CLOSURE_INPUT_COMMON_DEFAULT; \ - ClosureInput##t0 in_##t0##_0 = CLOSURE_INPUT_##t0##_DEFAULT; \ - ClosureInput##t1 in_##t1##_1 = CLOSURE_INPUT_##t1##_DEFAULT; \ - ClosureInput##t2 in_##t2##_2 = CLOSURE_INPUT_##t2##_DEFAULT; \ - ClosureInput##t3 in_##t3##_3 = CLOSURE_INPUT_##t3##_DEFAULT; \ - ClosureOutput##t0 out_##t0##_0; \ - ClosureOutput##t1 out_##t1##_1; \ - ClosureOutput##t2 out_##t2##_2; \ - ClosureOutput##t3 out_##t3##_3; - -#define CLOSURE_EVAL_DECLARE(t0, t1, t2, t3) \ - ClosureEvalCommon cl_common = closure_Common_eval_init(in_common); \ - ClosureEval##t0 eval_##t0##_0 = closure_##t0##_eval_init(in_##t0##_0, cl_common, out_##t0##_0); \ - ClosureEval##t1 eval_##t1##_1 = closure_##t1##_eval_init(in_##t1##_1, cl_common, out_##t1##_1); \ - ClosureEval##t2 eval_##t2##_2 = closure_##t2##_eval_init(in_##t2##_2, cl_common, out_##t2##_2); \ - ClosureEval##t3 eval_##t3##_3 = closure_##t3##_eval_init(in_##t3##_3, cl_common, out_##t3##_3); - -#define CLOSURE_META_SUBROUTINE(subroutine, t0, t1, t2, t3) \ - closure_##t0##_##subroutine(in_##t0##_0, eval_##t0##_0, cl_common, out_##t0##_0); \ - closure_##t1##_##subroutine(in_##t1##_1, eval_##t1##_1, cl_common, out_##t1##_1); \ - closure_##t2##_##subroutine(in_##t2##_2, eval_##t2##_2, cl_common, out_##t2##_2); \ - closure_##t3##_##subroutine(in_##t3##_3, eval_##t3##_3, cl_common, out_##t3##_3); - -#define CLOSURE_META_SUBROUTINE_DATA(subroutine, sub_data, t0, t1, t2, t3) \ - closure_##t0##_##subroutine(in_##t0##_0, eval_##t0##_0, cl_common, sub_data, out_##t0##_0); \ - closure_##t1##_##subroutine(in_##t1##_1, eval_##t1##_1, cl_common, sub_data, out_##t1##_1); \ - closure_##t2##_##subroutine(in_##t2##_2, eval_##t2##_2, cl_common, sub_data, out_##t2##_2); \ - closure_##t3##_##subroutine(in_##t3##_3, eval_##t3##_3, cl_common, sub_data, out_##t3##_3); - -#ifndef DEPTH_SHADER -/* Inputs are inout so that callers can get the final inputs used for evaluation. */ -# define CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3) \ - void closure_##name##_eval(ClosureInputCommon in_common, \ - inout ClosureInput##t0 in_##t0##_0, \ - inout ClosureInput##t1 in_##t1##_1, \ - inout ClosureInput##t2 in_##t2##_2, \ - inout ClosureInput##t3 in_##t3##_3, \ - out ClosureOutput##t0 out_##t0##_0, \ - out ClosureOutput##t1 out_##t1##_1, \ - out ClosureOutput##t2 out_##t2##_2, \ - out ClosureOutput##t3 out_##t3##_3) \ - { \ - CLOSURE_EVAL_DECLARE(t0, t1, t2, t3); \ -\ - /* Starts at 1 because 0 is world cubemap. */ \ - for (int i = 1; cl_common.specular_accum > 0.0 && i < prbNumRenderCube && i < MAX_PROBE; \ - i++) { \ - ClosureCubemapData cube = closure_cubemap_eval_init(i, cl_common); \ - if (cube.attenuation > 1e-8) { \ - CLOSURE_META_SUBROUTINE_DATA(cubemap_eval, cube, t0, t1, t2, t3); \ - } \ - } \ -\ - /* Starts at 1 because 0 is world irradiance. */ \ - for (int i = 1; cl_common.diffuse_accum > 0.0 && i < prbNumRenderGrid && i < MAX_GRID; \ - i++) { \ - ClosureGridData grid = closure_grid_eval_init(i, cl_common); \ - if (grid.attenuation > 1e-8) { \ - CLOSURE_META_SUBROUTINE_DATA(grid_eval, grid, t0, t1, t2, t3); \ - } \ - } \ -\ - CLOSURE_META_SUBROUTINE(indirect_end, t0, t1, t2, t3); \ -\ - ClosurePlanarData planar = closure_planar_eval_init(cl_common); \ - if (planar.attenuation > 1e-8) { \ - CLOSURE_META_SUBROUTINE_DATA(planar_eval, planar, t0, t1, t2, t3); \ - } \ -\ - for (int i = 0; i < laNumLight && i < MAX_LIGHT; i++) { \ - ClosureLightData light = closure_light_eval_init(cl_common, i); \ - if (light.vis > 1e-8) { \ - CLOSURE_META_SUBROUTINE_DATA(light_eval, light, t0, t1, t2, t3); \ - } \ - } \ -\ - CLOSURE_META_SUBROUTINE(eval_end, t0, t1, t2, t3); \ - } - -#else -/* Inputs are inout so that callers can get the final inputs used for evaluation. */ -# define CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3) \ - void closure_##name##_eval(ClosureInputCommon in_common, \ - inout ClosureInput##t0 in_##t0##_0, \ - inout ClosureInput##t1 in_##t1##_1, \ - inout ClosureInput##t2 in_##t2##_2, \ - inout ClosureInput##t3 in_##t3##_3, \ - out ClosureOutput##t0 out_##t0##_0, \ - out ClosureOutput##t1 out_##t1##_1, \ - out ClosureOutput##t2 out_##t2##_2, \ - out ClosureOutput##t3 out_##t3##_3) \ - { \ - CLOSURE_EVAL_DECLARE(t0, t1, t2, t3); \ - } -#endif - -#define CLOSURE_EVAL_FUNCTION(name, t0, t1, t2, t3) \ - closure_##name##_eval(in_common, \ - in_##t0##_0, \ - in_##t1##_1, \ - in_##t2##_2, \ - in_##t3##_3, \ - out_##t0##_0, \ - out_##t1##_1, \ - out_##t2##_2, \ - out_##t3##_3) - -#define CLOSURE_EVAL_FUNCTION_DECLARE_1(name, t0) \ - CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, Dummy, Dummy, Dummy) -#define CLOSURE_EVAL_FUNCTION_DECLARE_2(name, t0, t1) \ - CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, Dummy, Dummy) -#define CLOSURE_EVAL_FUNCTION_DECLARE_3(name, t0, t1, t2) \ - CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, Dummy) -#define CLOSURE_EVAL_FUNCTION_DECLARE_4(name, t0, t1, t2, t3) \ - CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3) - -#define CLOSURE_VARS_DECLARE_1(t0) CLOSURE_VARS_DECLARE(t0, Dummy, Dummy, Dummy) -#define CLOSURE_VARS_DECLARE_2(t0, t1) CLOSURE_VARS_DECLARE(t0, t1, Dummy, Dummy) -#define CLOSURE_VARS_DECLARE_3(t0, t1, t2) CLOSURE_VARS_DECLARE(t0, t1, t2, Dummy) -#define CLOSURE_VARS_DECLARE_4(t0, t1, t2, t3) CLOSURE_VARS_DECLARE(t0, t1, t2, t3) - -#define CLOSURE_EVAL_FUNCTION_1(name, t0) CLOSURE_EVAL_FUNCTION(name, t0, Dummy, Dummy, Dummy) -#define CLOSURE_EVAL_FUNCTION_2(name, t0, t1) CLOSURE_EVAL_FUNCTION(name, t0, t1, Dummy, Dummy) -#define CLOSURE_EVAL_FUNCTION_3(name, t0, t1, t2) CLOSURE_EVAL_FUNCTION(name, t0, t1, t2, Dummy) -#define CLOSURE_EVAL_FUNCTION_4(name, t0, t1, t2, t3) CLOSURE_EVAL_FUNCTION(name, t0, t1, t2, t3) - -/* -------------------------------------------------------------------- */ -/** \name Dummy Closure - * - * Dummy closure type that will be optimized out by the compiler. - * \{ */ - -#define ClosureInputDummy ClosureOutput -#define ClosureOutputDummy ClosureOutput -#define ClosureEvalDummy ClosureOutput -#define CLOSURE_EVAL_DUMMY ClosureOutput(vec3(0)) -#define CLOSURE_INPUT_Dummy_DEFAULT CLOSURE_EVAL_DUMMY -#define closure_Dummy_eval_init(cl_in, cl_common, cl_out) CLOSURE_EVAL_DUMMY -#define closure_Dummy_planar_eval(cl_in, cl_eval, cl_common, data, cl_out) -#define closure_Dummy_cubemap_eval(cl_in, cl_eval, cl_common, data, cl_out) -#define closure_Dummy_grid_eval(cl_in, cl_eval, cl_common, data, cl_out) -#define closure_Dummy_indirect_end(cl_in, cl_eval, cl_common, cl_out) -#define closure_Dummy_light_eval(cl_in, cl_eval, cl_common, data, cl_out) -#define closure_Dummy_eval_end(cl_in, cl_eval, cl_common, cl_out) - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Common cl_eval data - * - * Eval data not dependent on input parameters. All might not be used but unused ones - * will be optimized out. - * \{ */ - -struct ClosureInputCommon { - /** Custom occlusion value set by the user. */ - float occlusion; -}; - -#define CLOSURE_INPUT_COMMON_DEFAULT ClosureInputCommon(1.0) - -struct ClosureEvalCommon { - /** Result of SSAO. */ - OcclusionData occlusion_data; - /** View vector. */ - vec3 V; - /** Surface position. */ - vec3 P; - /** Normal vector, always facing camera. */ - vec3 N; - /** Normal vector, always facing camera. (viewspace) */ - vec3 vN; - /** Surface position. (viewspace) */ - vec3 vP; - /** Geometric normal, always facing camera. */ - vec3 Ng; - /** Geometric normal, always facing camera. (viewspace) */ - vec3 vNg; - /** Random numbers. 3 random sequences. zw is a random point on a circle. */ - vec4 rand; - /** Specular probe accumulator. Shared between planar and cubemap probe. */ - float specular_accum; - /** Diffuse probe accumulator. */ - float diffuse_accum; -}; - -/* Common cl_out struct used by most closures. */ -struct ClosureOutput { - vec3 radiance; -}; - -/* Workaround for screenspace shadows in SSR pass. */ -float FragDepth; - -ClosureEvalCommon closure_Common_eval_init(ClosureInputCommon cl_in) -{ - ClosureEvalCommon cl_eval; - cl_eval.rand = texelfetch_noise_tex(gl_FragCoord.xy); - cl_eval.V = cameraVec(worldPosition); - cl_eval.P = worldPosition; - cl_eval.N = safe_normalize(gl_FrontFacing ? worldNormal : -worldNormal); - cl_eval.vN = safe_normalize(gl_FrontFacing ? viewNormal : -viewNormal); - cl_eval.vP = viewPosition; - cl_eval.Ng = safe_normalize(cross(dFdx(cl_eval.P), dFdy(cl_eval.P))); - cl_eval.vNg = transform_direction(ViewMatrix, cl_eval.Ng); - - cl_eval.occlusion_data = occlusion_load(cl_eval.vP, cl_in.occlusion); - - cl_eval.specular_accum = 1.0; - cl_eval.diffuse_accum = 1.0; - return cl_eval; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Loop data - * - * Loop data is conveniently packed into struct to make it future proof. - * \{ */ - -struct ClosureLightData { - LightData data; /** Light Data. */ - vec4 L; /** Non-Normalized Light Vector (surface to light) with length in W component. */ - float vis; /** Light visibility. */ - float contact_shadow; /** Result of contact shadow tracing. */ -}; - -ClosureLightData closure_light_eval_init(ClosureEvalCommon cl_common, int light_id) -{ - ClosureLightData light; - light.data = lights_data[light_id]; - - light.L.xyz = light.data.l_position - cl_common.P; - light.L.w = length(light.L.xyz); - - light.vis = light_visibility(light.data, cl_common.P, light.L); - light.contact_shadow = light_contact_shadows( - light.data, cl_common.P, cl_common.vP, cl_common.vNg, cl_common.rand.x, light.vis); - - return light; -} - -struct ClosureCubemapData { - int id; /** Probe id. */ - float attenuation; /** Attenuation. */ -}; - -ClosureCubemapData closure_cubemap_eval_init(int cube_id, inout ClosureEvalCommon cl_common) -{ - ClosureCubemapData cube; - cube.id = cube_id; - cube.attenuation = probe_attenuation_cube(cube_id, cl_common.P); - cube.attenuation = min(cube.attenuation, cl_common.specular_accum); - cl_common.specular_accum -= cube.attenuation; - return cube; -} - -struct ClosurePlanarData { - int id; /** Probe id. */ - PlanarData data; /** planars_data[id]. */ - float attenuation; /** Attenuation. */ -}; - -ClosurePlanarData closure_planar_eval_init(inout ClosureEvalCommon cl_common) -{ - ClosurePlanarData planar; - planar.attenuation = 0.0; - - /* TODO(fclem): Find planar with the maximum weight. */ - for (int i = 0; i < prbNumPlanar && i < MAX_PLANAR; i++) { - float attenuation = probe_attenuation_planar(planars_data[i], cl_common.P); - if (attenuation > planar.attenuation) { - planar.id = i; - planar.attenuation = attenuation; - planar.data = planars_data[i]; - } - } - return planar; -} - -struct ClosureGridData { - int id; /** Grid id. */ - GridData data; /** grids_data[id] */ - float attenuation; /** Attenuation. */ - vec3 local_pos; /** Local position inside the grid. */ -}; - -ClosureGridData closure_grid_eval_init(int id, inout ClosureEvalCommon cl_common) -{ - ClosureGridData grid; - grid.id = id; - grid.data = grids_data[id]; - grid.attenuation = probe_attenuation_grid(grid.data, cl_common.P, grid.local_pos); - grid.attenuation = min(grid.attenuation, cl_common.diffuse_accum); - cl_common.diffuse_accum -= grid.attenuation; - return grid; -} - -/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl deleted file mode 100644 index 9011eea07c4..00000000000 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl +++ /dev/null @@ -1,128 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) -#pragma BLENDER_REQUIRE(lights_lib.glsl) -#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) -#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) -#pragma BLENDER_REQUIRE(ssr_lib.glsl) - -struct ClosureInputRefraction { - vec3 N; /** Shading normal. */ - float roughness; /** Input roughness, not squared. */ - float ior; /** Index of refraction ratio. */ -}; - -#define CLOSURE_INPUT_Refraction_DEFAULT ClosureInputRefraction(vec3(0.0), 0.0, 0.0) - -struct ClosureEvalRefraction { - vec3 P; /** LTC matrix values. */ - vec3 ltc_brdf; /** LTC BRDF values. */ - vec3 probe_sampling_dir; /** Direction to sample probes from. */ - float probes_weight; /** Factor to apply to probe radiance. */ -}; - -/* Stubs. */ -#define ClosureOutputRefraction ClosureOutput -#define closure_Refraction_grid_eval(cl_in, cl_eval, cl_common, data, cl_out) - -ClosureEvalRefraction closure_Refraction_eval_init(inout ClosureInputRefraction cl_in, - ClosureEvalCommon cl_common, - out ClosureOutputRefraction cl_out) -{ - cl_in.N = safe_normalize(cl_in.N); - cl_in.roughness = clamp(cl_in.roughness, 1e-8, 0.9999); - cl_in.ior = max(cl_in.ior, 1e-5); - cl_out.radiance = vec3(0.0); - - ClosureEvalRefraction cl_eval; - vec3 cl_V; - float eval_ior; - /* Refract the view vector using the depth heuristic. - * Then later Refract a second time the already refracted - * ray using the inverse ior. */ - if (refractionDepth > 0.0) { - eval_ior = 1.0 / cl_in.ior; - cl_V = -refract(-cl_common.V, cl_in.N, eval_ior); - vec3 plane_pos = cl_common.P - cl_in.N * refractionDepth; - cl_eval.P = line_plane_intersect(cl_common.P, cl_V, plane_pos, cl_in.N); - } - else { - eval_ior = cl_in.ior; - cl_V = cl_common.V; - cl_eval.P = cl_common.P; - } - - cl_eval.probe_sampling_dir = refraction_dominant_dir(cl_in.N, cl_V, cl_in.roughness, eval_ior); - cl_eval.probes_weight = 1.0; - -#ifdef USE_REFRACTION - if (ssrefractToggle && cl_in.roughness < ssrMaxRoughness + 0.2) { - /* Find approximated position of the 2nd refraction event. */ - vec3 vP = (refractionDepth > 0.0) ? transform_point(ViewMatrix, cl_eval.P) : cl_common.vP; - vec4 ssr_output = screen_space_refraction( - vP, cl_in.N, cl_V, eval_ior, sqr(cl_in.roughness), cl_common.rand); - ssr_output.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, cl_in.roughness); - cl_out.radiance += ssr_output.rgb * ssr_output.a; - cl_eval.probes_weight -= ssr_output.a; - } -#endif - return cl_eval; -} - -void closure_Refraction_light_eval(ClosureInputRefraction cl_in, - ClosureEvalRefraction cl_eval, - ClosureEvalCommon cl_common, - ClosureLightData light, - inout ClosureOutputRefraction cl_out) -{ - /* Not implemented yet. */ -} - -void closure_Refraction_planar_eval(ClosureInputRefraction cl_in, - ClosureEvalRefraction cl_eval, - ClosureEvalCommon cl_common, - ClosurePlanarData planar, - inout ClosureOutputRefraction cl_out) -{ - /* Not implemented yet. */ -} - -void closure_Refraction_cubemap_eval(ClosureInputRefraction cl_in, - ClosureEvalRefraction cl_eval, - ClosureEvalCommon cl_common, - ClosureCubemapData cube, - inout ClosureOutputRefraction cl_out) -{ - vec3 probe_radiance = probe_evaluate_cube( - cube.id, cl_eval.P, cl_eval.probe_sampling_dir, sqr(cl_in.roughness)); - cl_out.radiance += (cube.attenuation * cl_eval.probes_weight) * probe_radiance; -} - -void closure_Refraction_indirect_end(ClosureInputRefraction cl_in, - ClosureEvalRefraction cl_eval, - ClosureEvalCommon cl_common, - inout ClosureOutputRefraction cl_out) -{ - /* If not enough light has been accumulated from probes, use the world specular cubemap - * to fill the remaining energy needed. */ - if (specToggle && cl_common.specular_accum > 0.0) { - vec3 probe_radiance = probe_evaluate_world_spec(cl_eval.probe_sampling_dir, - sqr(cl_in.roughness)); - cl_out.radiance += (cl_common.specular_accum * cl_eval.probes_weight) * probe_radiance; - } -} - -void closure_Refraction_eval_end(ClosureInputRefraction cl_in, - ClosureEvalRefraction cl_eval, - ClosureEvalCommon cl_common, - inout ClosureOutputRefraction cl_out) -{ -#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND) - /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */ - cl_out.radiance = vec3(0.0); - return; -#endif - - if (!specToggle) { - cl_out.radiance = vec3(0.0); - } -} diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl deleted file mode 100644 index 183219c9088..00000000000 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl +++ /dev/null @@ -1,71 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) -#pragma BLENDER_REQUIRE(lights_lib.glsl) -#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) -#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) - -struct ClosureInputTranslucent { - vec3 N; /** Shading normal. */ -}; - -#define CLOSURE_INPUT_Translucent_DEFAULT ClosureInputTranslucent(vec3(0.0)) - -/* Stubs. */ -#define ClosureEvalTranslucent ClosureEvalDummy -#define ClosureOutputTranslucent ClosureOutput -#define closure_Translucent_planar_eval(cl_in, cl_eval, cl_common, data, cl_out) -#define closure_Translucent_cubemap_eval(cl_in, cl_eval, cl_common, data, cl_out) - -ClosureEvalTranslucent closure_Translucent_eval_init(inout ClosureInputTranslucent cl_in, - ClosureEvalCommon cl_common, - out ClosureOutputTranslucent cl_out) -{ - cl_in.N = safe_normalize(cl_in.N); - cl_out.radiance = vec3(0.0); - return CLOSURE_EVAL_DUMMY; -} - -void closure_Translucent_light_eval(ClosureInputTranslucent cl_in, - ClosureEvalTranslucent cl_eval, - ClosureEvalCommon cl_common, - ClosureLightData light, - inout ClosureOutputTranslucent cl_out) -{ - float radiance = light_diffuse(light.data, cl_in.N, cl_common.V, light.L); - cl_out.radiance += light.data.l_color * (light.data.l_diff * light.vis * radiance); -} - -void closure_Translucent_grid_eval(ClosureInputTranslucent cl_in, - ClosureEvalTranslucent cl_eval, - ClosureEvalCommon cl_common, - ClosureGridData grid, - inout ClosureOutputTranslucent cl_out) -{ - vec3 probe_radiance = probe_evaluate_grid(grid.data, cl_common.P, cl_in.N, grid.local_pos); - cl_out.radiance += grid.attenuation * probe_radiance; -} - -void closure_Translucent_indirect_end(ClosureInputTranslucent cl_in, - ClosureEvalTranslucent cl_eval, - ClosureEvalCommon cl_common, - inout ClosureOutputTranslucent cl_out) -{ - /* If not enough light has been accumulated from probes, use the world specular cubemap - * to fill the remaining energy needed. */ - if (cl_common.diffuse_accum > 0.0) { - vec3 probe_radiance = probe_evaluate_world_diff(cl_in.N); - cl_out.radiance += cl_common.diffuse_accum * probe_radiance; - } -} - -void closure_Translucent_eval_end(ClosureInputTranslucent cl_in, - ClosureEvalTranslucent cl_eval, - ClosureEvalCommon cl_common, - inout ClosureOutputTranslucent cl_out) -{ -#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND) - /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */ - cl_out.radiance = vec3(0.0); - return; -#endif -} diff --git a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl deleted file mode 100644 index f66f45635f4..00000000000 --- a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl +++ /dev/null @@ -1,189 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) -#pragma BLENDER_REQUIRE(renderpass_lib.glsl) - -#ifndef VOLUMETRICS - -uniform int outputSsrId = 1; -uniform int outputSssId = 1; - -#endif - -struct Closure { -#ifdef VOLUMETRICS - vec3 absorption; - vec3 scatter; - vec3 emission; - float anisotropy; - -#else /* SURFACE */ - vec3 radiance; - vec3 transmittance; - float holdout; - vec4 ssr_data; - vec2 ssr_normal; - int flag; -# ifdef USE_SSS - vec3 sss_irradiance; - vec3 sss_albedo; - float sss_radius; -# endif - -#endif -}; - -/* Prototype */ -Closure nodetree_exec(void); - -/* clang-format off */ -/* Avoid multi-line defines. */ -#ifdef VOLUMETRICS -# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), vec3(0), 0.0) -#elif !defined(USE_SSS) -# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), 0.0, vec4(0), vec2(0), 0) -#else -# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), 0.0, vec4(0), vec2(0), 0, vec3(0), vec3(0), 0.0) -#endif -/* clang-format on */ - -#define FLAG_TEST(flag, val) (((flag) & (val)) != 0) - -#define CLOSURE_SSR_FLAG 1 -#define CLOSURE_SSS_FLAG 2 -#define CLOSURE_HOLDOUT_FLAG 4 - -#ifdef VOLUMETRICS -Closure closure_mix(Closure cl1, Closure cl2, float fac) -{ - Closure cl; - cl.absorption = mix(cl1.absorption, cl2.absorption, fac); - cl.scatter = mix(cl1.scatter, cl2.scatter, fac); - cl.emission = mix(cl1.emission, cl2.emission, fac); - cl.anisotropy = mix(cl1.anisotropy, cl2.anisotropy, fac); - return cl; -} - -Closure closure_add(Closure cl1, Closure cl2) -{ - Closure cl; - cl.absorption = cl1.absorption + cl2.absorption; - cl.scatter = cl1.scatter + cl2.scatter; - cl.emission = cl1.emission + cl2.emission; - cl.anisotropy = (cl1.anisotropy + cl2.anisotropy) / 2.0; /* Average phase (no multi lobe) */ - return cl; -} - -Closure closure_emission(vec3 rgb) -{ - Closure cl = CLOSURE_DEFAULT; - cl.emission = rgb; - return cl; -} - -#else /* SURFACE */ - -Closure closure_mix(Closure cl1, Closure cl2, float fac) -{ - Closure cl; - cl.holdout = mix(cl1.holdout, cl2.holdout, fac); - - if (FLAG_TEST(cl1.flag, CLOSURE_HOLDOUT_FLAG)) { - fac = 1.0; - } - else if (FLAG_TEST(cl2.flag, CLOSURE_HOLDOUT_FLAG)) { - fac = 0.0; - } - - cl.transmittance = mix(cl1.transmittance, cl2.transmittance, fac); - cl.radiance = mix(cl1.radiance, cl2.radiance, fac); - cl.flag = cl1.flag | cl2.flag; - cl.ssr_data = mix(cl1.ssr_data, cl2.ssr_data, fac); - bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG); - /* When mixing SSR don't blend roughness and normals but only specular (ssr_data.xyz). */ - cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w; - cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal; - -# ifdef USE_SSS - cl.sss_albedo = mix(cl1.sss_albedo, cl2.sss_albedo, fac); - bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG); - /* It also does not make sense to mix SSS radius or irradiance. */ - cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius; - cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance; -# endif - return cl; -} - -Closure closure_add(Closure cl1, Closure cl2) -{ - Closure cl; - cl.transmittance = cl1.transmittance + cl2.transmittance; - cl.radiance = cl1.radiance + cl2.radiance; - cl.holdout = cl1.holdout + cl2.holdout; - cl.flag = cl1.flag | cl2.flag; - cl.ssr_data = cl1.ssr_data + cl2.ssr_data; - bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG); - /* When mixing SSR don't blend roughness and normals. */ - cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w; - cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal; - -# ifdef USE_SSS - cl.sss_albedo = cl1.sss_albedo + cl2.sss_albedo; - bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG); - /* It also does not make sense to mix SSS radius or irradiance. */ - cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius; - cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance; -# endif - return cl; -} - -Closure closure_emission(vec3 rgb) -{ - Closure cl = CLOSURE_DEFAULT; - cl.radiance = rgb; - return cl; -} - -#endif - -#ifndef VOLUMETRICS - -/* Let radiance passthrough or replace it to get the BRDF and color - * to applied to the SSR result. */ -vec3 closure_mask_ssr_radiance(vec3 radiance, float ssr_id) -{ - return (ssrToggle && int(ssr_id) == outputSsrId) ? vec3(1.0) : radiance; -} - -void closure_load_ssr_data( - vec3 ssr_radiance, float roughness, vec3 N, float ssr_id, inout Closure cl) -{ - /* Still encode to avoid artifacts in the SSR pass. */ - vec3 vN = normalize(mat3(ViewMatrix) * N); - cl.ssr_normal = normal_encode(vN, viewCameraVec(viewPosition)); - - if (ssrToggle && int(ssr_id) == outputSsrId) { - cl.ssr_data = vec4(ssr_radiance, roughness); - cl.flag |= CLOSURE_SSR_FLAG; - } - else { - cl.radiance += ssr_radiance; - } -} - -void closure_load_sss_data( - float radius, vec3 sss_irradiance, vec3 sss_albedo, int sss_id, inout Closure cl) -{ -# ifdef USE_SSS - if (sss_id == outputSssId) { - cl.sss_irradiance = sss_irradiance; - cl.sss_radius = radius; - cl.sss_albedo = sss_albedo; - cl.flag |= CLOSURE_SSS_FLAG; - /* Irradiance will be convolved by SSSS pass. Do not add to radiance. */ - sss_irradiance = vec3(0); - } -# endif - cl.radiance += render_pass_diffuse_mask(vec3(1), sss_irradiance) * sss_albedo; -} - -#endif diff --git a/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl b/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl deleted file mode 100644 index c935eca6a39..00000000000 --- a/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl +++ /dev/null @@ -1,73 +0,0 @@ - -layout(std140) uniform common_block -{ - mat4 pastViewProjectionMatrix; - vec4 hizUvScale; /* To correct mip level texel misalignment */ - /* Ambient Occlusion */ - vec4 aoParameters[2]; - /* Volumetric */ - ivec4 volTexSize; - vec4 volDepthParameters; /* Parameters to the volume Z equation */ - vec4 volInvTexSize; - vec4 volJitter; - vec4 volCoordScale; /* To convert volume uvs to screen uvs */ - float volHistoryAlpha; - float volShadowSteps; - bool volUseLights; - bool volUseSoftShadows; - /* Screen Space Reflections */ - vec4 ssrParameters; - float ssrBorderFac; - float ssrMaxRoughness; - float ssrFireflyFac; - float ssrBrdfBias; - bool ssrToggle; - bool ssrefractToggle; - /* SubSurface Scattering */ - float sssJitterThreshold; - bool sssToggle; - /* Specular */ - bool specToggle; - /* Lights */ - int laNumLight; - /* Probes */ - int prbNumPlanar; - int prbNumRenderCube; - int prbNumRenderGrid; - int prbIrradianceVisSize; - float prbIrradianceSmooth; - float prbLodCubeMax; - /* Misc */ - int rayType; - float rayDepth; - float alphaHashOffset; - float alphaHashScale; - float pad6; - float pad7; - float pad8; - float pad9; -}; - -/* rayType (keep in sync with ray_type) */ -#define EEVEE_RAY_CAMERA 0 -#define EEVEE_RAY_SHADOW 1 -#define EEVEE_RAY_DIFFUSE 2 -#define EEVEE_RAY_GLOSSY 3 - -/* aoParameters */ -#define aoDistance aoParameters[0].x -#define aoSamples aoParameters[0].y /* UNUSED */ -#define aoFactor aoParameters[0].z -#define aoInvSamples aoParameters[0].w /* UNUSED */ - -#define aoOffset aoParameters[1].x /* UNUSED */ -#define aoBounceFac aoParameters[1].y -#define aoQuality aoParameters[1].z -#define aoSettings aoParameters[1].w - -/* ssrParameters */ -#define ssrQuality ssrParameters.x -#define ssrThickness ssrParameters.y -#define ssrPixelSize ssrParameters.zw - -#define ssrUvScale hizUvScale.zw diff --git a/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl b/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl deleted file mode 100644 index 77a1560f3a7..00000000000 --- a/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl +++ /dev/null @@ -1,108 +0,0 @@ - -#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl) - -/* ---------------------------------------------------------------------- */ -/** \name Utiltex - * - * Utiltex is a sampler2DArray that stores a number of useful small utilitary textures and lookup - * tables. - * \{ */ - -uniform sampler2DArray utilTex; - -#define LUT_SIZE 64 - -#define LTC_MAT_LAYER 0 -#define LTC_BRDF_LAYER 1 -#define BRDF_LUT_LAYER 1 -#define NOISE_LAYER 2 -#define LTC_DISK_LAYER 3 /* UNUSED */ - -/* Layers 4 to 20 are for BTDF Lut. */ -const float lut_btdf_layer_first = 4.0; -const float lut_btdf_layer_count = 16.0; - -/** - * Reminder: The 4 noise values are based of 3 uncorrelated blue noises: - * x : Uniformly distributed value [0..1] (noise 1). - * y : Uniformly distributed value [0..1] (noise 2). - * z,w : Uniformly distributed point on the unit circle [-1..1] (noise 3). - */ -#define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) - -/* Return texture coordinates to sample Surface LUT. */ -vec2 lut_coords(float cos_theta, float roughness) -{ - vec2 coords = vec2(roughness, sqrt(1.0 - cos_theta)); - /* scale and bias coordinates, for correct filtered lookup */ - return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE; -} - -/* Returns the GGX split-sum precomputed in LUT. */ -vec2 brdf_lut(float cos_theta, float roughness) -{ - return textureLod(utilTex, vec3(lut_coords(cos_theta, roughness), BRDF_LUT_LAYER), 0.0).rg; -} - -/* Return texture coordinates to sample Surface LUT. */ -vec3 lut_coords_btdf(float cos_theta, float roughness, float ior) -{ - /* ior is sin of critical angle. */ - float critical_cos = sqrt(1.0 - ior * ior); - - vec3 coords; - coords.x = sqr(ior); - coords.y = cos_theta; - coords.y -= critical_cos; - coords.y /= (coords.y > 0.0) ? (1.0 - critical_cos) : critical_cos; - coords.y = coords.y * 0.5 + 0.5; - coords.z = roughness; - - coords = saturate(coords); - - /* scale and bias coordinates, for correct filtered lookup */ - coords.xy = coords.xy * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE; - - return coords; -} - -/* Returns GGX BTDF in first component and fresnel in second. */ -vec2 btdf_lut(float cos_theta, float roughness, float ior) -{ - if (ior <= 1e-5) { - return vec2(0.0); - } - - if (ior >= 1.0) { - vec2 split_sum = brdf_lut(cos_theta, roughness); - float f0 = f0_from_ior(ior); - /* Baked IOR for GGX BRDF. */ - const float specular = 1.0; - const float eta_brdf = (2.0 / (1.0 - sqrt(0.08 * specular))) - 1.0; - /* Avoid harsh transition coming from ior == 1. */ - float f90 = fast_sqrt(saturate(f0 / (f0_from_ior(eta_brdf) * 0.25))); - float fresnel = F_brdf_single_scatter(vec3(f0), vec3(f90), split_sum).r; - /* Setting the BTDF to one is not really important since it is only used for multiscatter - * and it's already quite close to ground truth. */ - float btdf = 1.0; - return vec2(btdf, fresnel); - } - - vec3 coords = lut_coords_btdf(cos_theta, roughness, ior); - - float layer = coords.z * lut_btdf_layer_count; - float layer_floored = floor(layer); - - coords.z = lut_btdf_layer_first + layer_floored; - vec2 btdf_low = textureLod(utilTex, coords, 0.0).rg; - - coords.z += 1.0; - vec2 btdf_high = textureLod(utilTex, coords, 0.0).rg; - - /* Manual trilinear interpolation. */ - vec2 btdf = mix(btdf_low, btdf_high, layer - layer_floored); - - return btdf; -} - -/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl b/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl deleted file mode 100644 index 9426b8e4a7b..00000000000 --- a/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl +++ /dev/null @@ -1,7 +0,0 @@ -uniform vec4 cryptohash; -out vec4 fragColor; - -void main() -{ - fragColor = cryptohash; -} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_bsdf_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_bsdf_lib.glsl new file mode 100644 index 00000000000..c015eaf09d7 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_bsdf_lib.glsl @@ -0,0 +1,242 @@ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +/* -------------------------------------------------------------------- */ +/** \name Utility functions to work with BSDFs + * \{ */ + +vec3 diffuse_dominant_dir(vec3 bent_normal) +{ + return bent_normal; +} + +vec3 specular_dominant_dir(vec3 N, vec3 V, float roughness) +{ + vec3 R = -reflect(V, N); + float smoothness = 1.0 - roughness; + float fac = smoothness * (sqrt(smoothness) + roughness); + return normalize(mix(N, R, fac)); +} + +vec3 refraction_dominant_dir(vec3 N, vec3 V, float roughness, float ior) +{ + /* TODO: This a bad approximation. Better approximation should fit + * the refracted vector and roughness into the best prefiltered reflection + * lobe. */ + /* Correct the IOR for ior < 1.0 to not see the abrupt delimitation or the TIR */ + ior = (ior < 1.0) ? mix(ior, 1.0, roughness) : ior; + float eta = 1.0 / ior; + + float NV = dot(N, -V); + + /* Custom Refraction. */ + float k = 1.0 - eta * eta * (1.0 - NV * NV); + k = max(0.0, k); /* Only this changes. */ + vec3 R = eta * -V - (eta * NV + sqrt(k)) * N; + + return R; +} + +float ior_from_f0(float f0) +{ + float f = sqrt(f0); + return (-f - 1.0) / (f - 1.0); +} + +/* Simplified form of F_eta(eta, 1.0). */ +float f0_from_ior(float eta) +{ + float A = (eta - 1.0) / (eta + 1.0); + return A * A; +} + +/* Fresnel monochromatic, perfect mirror. */ +float F_eta(float eta, float cos_theta) +{ + /* Compute fresnel reflectance without explicitly computing the refracted direction. */ + float c = abs(cos_theta); + float g = eta * eta - 1.0 + c * c; + if (g > 0.0) { + g = sqrt(g); + float A = (g - c) / (g + c); + float B = (c * (g + c) - 1.0) / (c * (g - c) + 1.0); + return 0.5 * A * A * (1.0 + B * B); + } + /* Total internal reflections. */ + return 1.0; +} + +/* Fresnel color blend base on fresnel factor. */ +vec3 F_color_blend(float eta, float fresnel, vec3 f0_color) +{ + float f0 = f0_from_ior(eta); + float fac = saturate((fresnel - f0) / (1.0 - f0)); + return mix(f0_color, vec3(1.0), fac); +} + +/* Fresnel split-sum approximation. */ +vec3 F_brdf_single_scatter(vec3 f0, vec3 f90, vec2 lut) +{ + /* Unreal specular matching : if specular color is below 2% intensity, treat as shadowning */ + return lut.y * f90 + lut.x * f0; +} + +/* Multi-scattering brdf approximation from : + * "A Multiple-Scattering Microfacet Model for Real-Time Image-based Lighting" + * by Carmelo J. Fdez-Agüera. */ +vec3 F_brdf_multi_scatter(vec3 f0, vec3 f90, vec2 lut) +{ + vec3 FssEss = lut.y * f90 + lut.x * f0; + + float Ess = lut.x + lut.y; + float Ems = 1.0 - Ess; + vec3 Favg = f0 + (1.0 - f0) / 21.0; + vec3 Fms = FssEss * Favg / (1.0 - (1.0 - Ess) * Favg); + /* We don't do anything special for diffuse surfaces because the principle bsdf + * does not care about energy conservation of the specular layer for dielectrics. */ + return FssEss + Fms * Ems; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Utility functions to work with BSDFs + * \{ */ + +/* Same thing as Cycles without the comments to make it shorter. */ +vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N) +{ + vec3 R; + float NI = dot(N, I); + float NgR, threshold; + /* Check if the incident ray is coming from behind normal N. */ + if (NI > 0.0) { + /* Normal reflection. */ + R = (2.0 * NI) * N - I; + NgR = dot(Ng, R); + /* Reflection rays may always be at least as shallow as the incoming ray. */ + threshold = min(0.9 * dot(Ng, I), 0.01); + if (NgR >= threshold) { + return N; + } + } + else { + /* Bad incident. */ + R = -I; + NgR = dot(Ng, R); + threshold = 0.01; + } + /* Lift the reflection above the threshold. */ + R = R + Ng * (threshold - NgR); + /* Find a bisector. */ + return safe_normalize(I * length(R) + R * length(I)); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cone angle approximation + * + * Returns a fitted cone angle given the input roughness + * \{ */ + +float cone_cosine(float r) +{ + /* Using phong gloss + * roughness = sqrt(2/(gloss+2)) */ + float gloss = -2 + 2 / (r * r); + /* Drobot 2014 in GPUPro5 */ + // return cos(2.0 * sqrt(2.0 / (gloss + 2))); + /* Uludag 2014 in GPUPro5 */ + // return pow(0.244, 1 / (gloss + 1)); + /* Jimenez 2016 in Practical Realtime Strategies for Accurate Indirect Occlusion*/ + return exp2(-3.32193 * r * r); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name BRDF & BTDF Look up table functions + * + * BRDFs are costly to evaluated at runtime. We precompute them by using the split-sum + * approximation method descibed by Brian Karis in "Real Shading in Unreal Engine 4" from + * Siggraph 2013. + * \{ */ + +/* Return texture coordinates to sample Surface LUT. */ +vec2 lut_coords(float cos_theta, float roughness) +{ + vec2 coords = vec2(roughness, sqrt(1.0 - cos_theta)); + /* scale and bias coordinates, for correct filtered lookup */ + return coords * UTIL_TEX_UV_SCALE + UTIL_TEX_UV_BIAS; +} + +/* Returns the GGX split-sum precomputed in LUT. */ +vec2 brdf_lut(float cos_theta, float roughness) +{ + return utility_tx_sample(lut_coords(cos_theta, roughness), UTIL_BSDF_LAYER).rg; +} + +/* Return texture coordinates to sample Surface LUT. */ +vec3 lut_coords_btdf(float cos_theta, float roughness, float ior) +{ + /* ior is sin of critical angle. */ + float critical_cos = sqrt(1.0 - ior * ior); + + vec3 coords; + coords.x = sqr(ior); + coords.y = cos_theta; + coords.y -= critical_cos; + coords.y /= (coords.y > 0.0) ? (1.0 - critical_cos) : critical_cos; + coords.y = coords.y * 0.5 + 0.5; + coords.z = roughness; + + coords = saturate(coords); + + /* Scale and bias coordinates, for correct filtered lookup. */ + coords.xy = coords.xy * UTIL_TEX_UV_SCALE + UTIL_TEX_UV_BIAS; + + return coords; +} + +/* Returns GGX BTDF in first component and fresnel in second. */ +vec2 btdf_lut(float cos_theta, float roughness, float ior) +{ + if (ior <= 1e-5) { + return vec2(0.0); + } + + if (ior >= 1.0) { + vec2 split_sum = brdf_lut(cos_theta, roughness); + float f0 = f0_from_ior(ior); + /* Baked IOR for GGX BRDF. */ + const float specular = 1.0; + const float eta_brdf = (2.0 / (1.0 - sqrt(0.08 * specular))) - 1.0; + /* Avoid harsh transition comming from ior == 1. */ + float f90 = fast_sqrt(saturate(f0 / (f0_from_ior(eta_brdf) * 0.25))); + float fresnel = F_brdf_single_scatter(vec3(f0), vec3(f90), split_sum).r; + /* Setting the BTDF to one is not really important since it is only used for multiscatter + * and it's already quite close to ground truth. */ + float btdf = 1.0; + return vec2(btdf, fresnel); + } + + vec3 coords = lut_coords_btdf(cos_theta, roughness, ior); + + float layer = coords.z * UTIL_BTDF_LAYER_COUNT; + float layer_floored = floor(layer); + + coords.z = UTIL_BTDF_LAYER + layer_floored; + vec2 btdf_low = utility_tx_sample(coords.xy, coords.z).rg; + + coords.z += 1.0; + vec2 btdf_high = utility_tx_sample(coords.xy, coords.z).rg; + + /* Manual trilinear interpolation. */ + vec2 btdf = mix(btdf_low, btdf_high, layer - layer_floored); + + return btdf; +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/eevee_bsdf_microfacet_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_bsdf_microfacet_lib.glsl new file mode 100644 index 00000000000..efb3c73797a --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_bsdf_microfacet_lib.glsl @@ -0,0 +1,59 @@ + +/* -------------------------------------------------------------------- */ +/** \name GGX + * + * \{ */ + +float D_ggx_opti(float NH, float a2) +{ + float tmp = (NH * a2 - NH) * NH + 1.0; + return M_PI * tmp * tmp; /* Doing RCP and mul a2 at the end. */ +} + +float G1_Smith_GGX_opti(float NX, float a2) +{ + /* Using Brian Karis approach and refactoring by NX/NX + * this way the (2*NL)*(2*NV) in G = G1(V) * G1(L) gets canceled by the brdf denominator 4*NL*NV + * Rcp is done on the whole G later. + * Note that this is not convenient for the transmission formula. */ + return NX + sqrt(NX * (NX - NX * a2) + a2); + /* return 2 / (1 + sqrt(1 + a2 * (1 - NX*NX) / (NX*NX) ) ); /* Reference function. */ +} + +float bsdf_ggx(vec3 N, vec3 L, vec3 V, float roughness) +{ + float a2 = sqr(roughness); + + vec3 H = normalize(L + V); + float NH = max(dot(N, H), 1e-8); + float NL = max(dot(N, L), 1e-8); + float NV = max(dot(N, V), 1e-8); + + float G = G1_Smith_GGX_opti(NV, a2) * G1_Smith_GGX_opti(NL, a2); /* Doing RCP at the end */ + float D = D_ggx_opti(NH, a2); + + /* Denominator is canceled by G1_Smith */ + /* bsdf = D * G / (4.0 * NL * NV); /* Reference function. */ + return NL * a2 / (D * G); /* NL to Fit cycles Equation : line. 345 in bsdf_microfacet.h */ +} + +float btdf_ggx(vec3 N, vec3 L, vec3 V, float roughness, float eta) +{ + float a2 = sqr(roughness); + + vec3 H = normalize(L + V); + float NH = max(dot(N, H), 1e-8); + float NL = max(dot(N, L), 1e-8); + float NV = max(dot(N, V), 1e-8); + float VH = max(dot(V, H), 1e-8); + float LH = max(dot(L, H), 1e-8); + float Ht2 = sqr(eta * LH + VH); + + float G = G1_Smith_GGX_opti(NV, a2) * G1_Smith_GGX_opti(NL, a2); /* Doing RCP at the end */ + float D = D_ggx_opti(NH, a2); + + /* btdf = abs(VH*LH) * (ior*ior) * D * G(V) * G(L) / (Ht2 * NV) */ + return abs(VH * LH) * sqr(eta) * 4.0 * a2 / (D * G * (Ht2 * NV)); +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_bsdf_sampling_lib.glsl index e0f52338914..7928fe76cb8 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/eevee_bsdf_sampling_lib.glsl @@ -4,7 +4,7 @@ */ #pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) -#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_bsdf_microfacet_lib.glsl) /* -------------------------------------------------------------------- */ /** \name Microfacet GGX distribution @@ -12,19 +12,27 @@ #define USE_VISIBLE_NORMAL 1 -float pdf_ggx_reflect(float NH, float NV, float VH, float alpha) +float sample_pdf_ggx_reflect(float NH, float NV, float VH, float G1, float alpha) { float a2 = sqr(alpha); #if USE_VISIBLE_NORMAL float D = a2 / D_ggx_opti(NH, a2); - float G1 = NV * 2.0 / G1_Smith_GGX_opti(NV, a2); return G1 * VH * D / NV; #else return NH * a2 / D_ggx_opti(NH, a2); #endif } -vec3 sample_ggx(vec3 rand, float alpha, vec3 Vt) +float sample_pdf_ggx_refract( + float NH, float NV, float VH, float LH, float G1, float alpha, float eta) +{ + float a2 = sqr(alpha); + float D = D_ggx_opti(NH, a2); + float Ht2 = sqr(eta * LH + VH); + return VH * abs(LH) * ((G1 * D) * sqr(eta) * a2 / (D * NV * Ht2)); +} + +vec3 sample_ggx(vec3 rand, float alpha, vec3 Vt, out float G1) { #if USE_VISIBLE_NORMAL /* From: @@ -41,6 +49,7 @@ vec3 sample_ggx(vec3 rand, float alpha, vec3 Vt) float x = r * rand.y; float y = r * rand.z; float s = 0.5 * (1.0 + Vh.z); + G1 = 1.0 / s; y = (1.0 - s) * sqrt(1.0 - x * x) + s * y; float z = sqrt(saturate(1.0 - x * x - y * y)); /* Compute normal. */ @@ -60,15 +69,49 @@ vec3 sample_ggx(vec3 rand, float alpha, vec3 Vt) #endif } -vec3 sample_ggx(vec3 rand, float alpha, vec3 V, vec3 N, vec3 T, vec3 B, out float pdf) +vec3 sample_ggx_reflect(vec3 rand, float alpha, vec3 V, vec3 N, vec3 T, vec3 B, out float pdf) { + float G1; vec3 Vt = world_to_tangent(V, N, T, B); - vec3 Ht = sample_ggx(rand, alpha, Vt); + vec3 Ht = sample_ggx(rand, alpha, Vt, G1); float NH = saturate(Ht.z); float NV = saturate(Vt.z); - float VH = saturate(dot(Vt, Ht)); - pdf = pdf_ggx_reflect(NH, NV, VH, alpha); - return tangent_to_world(Ht, N, T, B); + float VH = dot(Vt, Ht); + vec3 H = tangent_to_world(Ht, N, T, B); + + if (VH > 0.0) { + vec3 L = reflect(-V, H); + pdf = sample_pdf_ggx_reflect(NH, NV, VH, G1, alpha); + return L; + } + else { + pdf = 0.0; + return vec3(1.0, 0.0, 0.0); + } +} + +vec3 sample_ggx_refract( + vec3 rand, float alpha, float ior, vec3 V, vec3 N, vec3 T, vec3 B, out float pdf) +{ + float G1; + vec3 Vt = world_to_tangent(V, N, T, B); + vec3 Ht = sample_ggx(rand, alpha, Vt, G1); + float NH = saturate(Ht.z); + float NV = saturate(Vt.z); + float VH = dot(Vt, Ht); + vec3 H = tangent_to_world(Ht, N, T, B); + + if (VH > 0.0) { + /* NOTE: Ior is already inverted for front faces. */ + vec3 L = refract(-V, H, ior); + float LH = dot(L, H); + pdf = sample_pdf_ggx_refract(NH, NV, VH, LH, G1, alpha, ior); + return L; + } + else { + pdf = 0.0; + return vec3(1.0, 0.0, 0.0); + } } /** \} */ @@ -77,7 +120,7 @@ vec3 sample_ggx(vec3 rand, float alpha, vec3 V, vec3 N, vec3 T, vec3 B, out floa /** \name Uniform Hemisphere * \{ */ -float pdf_uniform_hemisphere() +float sample_pdf_uniform_hemisphere() { return 0.5 * M_1_PI; } @@ -94,7 +137,34 @@ vec3 sample_uniform_hemisphere(vec3 rand) vec3 sample_uniform_hemisphere(vec3 rand, vec3 N, vec3 T, vec3 B, out float pdf) { vec3 Ht = sample_uniform_hemisphere(rand); - pdf = pdf_uniform_hemisphere(); + pdf = sample_pdf_uniform_hemisphere(); + return tangent_to_world(Ht, N, T, B); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cosine Hemisphere + * \{ */ + +float sample_pdf_cosine_hemisphere(float cos_theta) +{ + return cos_theta * M_1_PI; +} + +vec3 sample_cosine_hemisphere(vec3 rand) +{ + float z = sqrt(max(1e-16, rand.x)); /* cos theta */ + float r = sqrt(max(0.0, 1.0 - z * z)); /* sin theta */ + float x = r * rand.y; + float y = r * rand.z; + return vec3(x, y, z); +} + +vec3 sample_cosine_hemisphere(vec3 rand, vec3 N, vec3 T, vec3 B, out float pdf) +{ + vec3 Ht = sample_cosine_hemisphere(rand); + pdf = sample_pdf_cosine_hemisphere(Ht.z); return tangent_to_world(Ht, N, T, B); } diff --git a/source/blender/draw/engines/eevee/shaders/eevee_bsdf_stubs_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_bsdf_stubs_lib.glsl new file mode 100644 index 00000000000..3d7b91bcc1e --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_bsdf_stubs_lib.glsl @@ -0,0 +1,41 @@ + +/* Lib file to include to evaluate a nodetree without evaluating BSDFs. */ + +/* -------------------------------------------------------------------- */ +/** \name Utility functions to work with BSDFs + * \{ */ + +vec3 diffuse_dominant_dir(vec3 bent_normal) +{ + return vec3(0); +} +vec3 specular_dominant_dir(vec3 N, vec3 V, float roughness) +{ + return vec3(0.0); +} +vec3 refraction_dominant_dir(vec3 N, vec3 V, float roughness, float ior) +{ + return vec3(0.0); +} +float F_eta(float eta, float cos_theta) +{ + return 0.0; +} +vec3 F_brdf_single_scatter(vec3 f0, vec3 f90, vec2 lut) +{ + return vec3(0); +} +vec3 F_brdf_multi_scatter(vec3 f0, vec3 f90, vec2 lut) +{ + return vec3(0); +} +vec2 brdf_lut(float cos_theta, float roughness) +{ + return vec2(0); +} +vec2 btdf_lut(float cos_theta, float roughness, float ior) +{ + return vec2(0); +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/eevee_camera_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_camera_lib.glsl new file mode 100644 index 00000000000..59806477654 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_camera_lib.glsl @@ -0,0 +1,167 @@ + +/** + * Camera projection / uv functions and utils. + **/ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +/* -------------------------------------------------------------------- */ +/** \name Panoramic Projections + * + * Adapted from Cycles to match EEVEE's coordinate system. + * \{ */ + +vec2 camera_equirectangular_from_direction(CameraData camera, vec3 dir) +{ + float phi = atan(-dir.z, dir.x); + float theta = acos(dir.y / length(dir)); + return (vec2(phi, theta) - camera.equirect_bias) * camera.equirect_scale_inv; +} + +vec3 camera_equirectangular_to_direction(CameraData camera, vec2 uv) +{ + uv = uv * camera.equirect_scale + camera.equirect_bias; + float phi = uv.x; + float theta = uv.y; + float sin_theta = sin(theta); + return vec3(sin_theta * cos(phi), cos(theta), -sin_theta * sin(phi)); +} + +vec2 camera_fisheye_from_direction(CameraData camera, vec3 dir) +{ + float r = atan(length(dir.xy), -dir.z) / camera.fisheye_fov; + float phi = atan(dir.y, dir.x); + vec2 uv = r * vec2(cos(phi), sin(phi)) + 0.5; + return (uv - camera.uv_bias) / camera.uv_scale; +} + +vec3 camera_fisheye_to_direction(CameraData camera, vec2 uv) +{ + uv = uv * camera.uv_scale + camera.uv_bias; + uv = (uv - 0.5) * 2.0; + float r = length(uv); + if (r > 1.0) { + return vec3(0.0); + } + float phi = safe_acos(uv.x * safe_rcp(r)); + float theta = r * camera.fisheye_fov * 0.5; + if (uv.y < 0.0) { + phi = -phi; + } + return vec3(cos(phi) * sin(theta), sin(phi) * sin(theta), -cos(theta)); +} + +vec2 camera_mirror_ball_from_direction(CameraData camera, vec3 dir) +{ + dir = normalize(dir); + dir.z -= 1.0; + dir *= safe_rcp(2.0 * safe_sqrt(-0.5 * dir.z)); + vec2 uv = 0.5 * dir.xy + 0.5; + return (uv - camera.uv_bias) / camera.uv_scale; +} + +vec3 camera_mirror_ball_to_direction(CameraData camera, vec2 uv) +{ + uv = uv * camera.uv_scale + camera.uv_bias; + vec3 dir; + dir.xy = uv * 2.0 - 1.0; + if (len_squared(dir.xy) > 1.0) { + return vec3(0.0); + } + dir.z = -safe_sqrt(1.0 - sqr(dir.x) - sqr(dir.y)); + const vec3 I = vec3(0.0, 0.0, 1.0); + return reflect(I, dir); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Regular projections + * \{ */ + +vec3 camera_view_from_uv(mat4 projmat, vec2 uv) +{ + return project_point(projmat, vec3(uv * 2.0 - 1.0, 0.0)); +} + +vec2 camera_uv_from_view(mat4 projmat, bool is_persp, vec3 vV) +{ + vec4 tmp = projmat * vec4(vV, 1.0); + if (is_persp && tmp.w <= 0.0) { + /* Return invalid coordinates for points behind the camera. + * This can happen with panoramic projections. */ + return vec2(-1.0); + } + return (tmp.xy / tmp.w) * 0.5 + 0.5; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name General functions handling all projections + * \{ */ + +vec3 camera_view_from_uv(CameraData camera, vec2 uv) +{ + vec3 vV; + switch (camera.type) { + default: + case CAMERA_ORTHO: + case CAMERA_PERSP: + return camera_view_from_uv(camera.wininv, uv); + case CAMERA_PANO_EQUIRECT: + vV = camera_equirectangular_to_direction(camera, uv); + break; + case CAMERA_PANO_EQUIDISTANT: + /* ATTR_FALLTHROUGH; */ + case CAMERA_PANO_EQUISOLID: + vV = camera_fisheye_to_direction(camera, uv); + break; + case CAMERA_PANO_MIRROR: + vV = camera_mirror_ball_to_direction(camera, uv); + break; + } + return vV; +} + +vec2 camera_uv_from_view(CameraData camera, vec3 vV) +{ + switch (camera.type) { + default: + case CAMERA_ORTHO: + return camera_uv_from_view(camera.winmat, false, vV); + case CAMERA_PERSP: + return camera_uv_from_view(camera.winmat, true, vV); + case CAMERA_PANO_EQUIRECT: + return camera_equirectangular_from_direction(camera, vV); + case CAMERA_PANO_EQUISOLID: + /* ATTR_FALLTHROUGH; */ + case CAMERA_PANO_EQUIDISTANT: + return camera_fisheye_from_direction(camera, vV); + case CAMERA_PANO_MIRROR: + return camera_mirror_ball_from_direction(camera, vV); + } +} + +vec2 camera_uv_from_world(CameraData camera, vec3 V) +{ + vec3 vV = transform_point(camera.viewmat, V); + switch (camera.type) { + default: + case CAMERA_ORTHO: + return camera_uv_from_view(camera.persmat, false, V); + case CAMERA_PERSP: + return camera_uv_from_view(camera.persmat, true, V); + case CAMERA_PANO_EQUIRECT: + return camera_equirectangular_from_direction(camera, vV); + case CAMERA_PANO_EQUISOLID: + /* ATTR_FALLTHROUGH; */ + case CAMERA_PANO_EQUIDISTANT: + return camera_fisheye_from_direction(camera, vV); + case CAMERA_PANO_MIRROR: + return camera_mirror_ball_from_direction(camera, vV); + } +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/eevee_camera_velocity_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_camera_velocity_frag.glsl new file mode 100644 index 00000000000..25158cf69c7 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_camera_velocity_frag.glsl @@ -0,0 +1,59 @@ + +/** + * Extract two 2D screen space velocity vector from depth buffer. + * Note that the offsets are in camera uv space, not view uv space. + * xy: Previous position > Current position + * zw: Current position > Next position + **/ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(std140) uniform camera_prev_block +{ + CameraData cam_prev; +}; + +layout(std140) uniform camera_curr_block +{ + CameraData cam_curr; +}; + +layout(std140) uniform camera_next_block +{ + CameraData cam_next; +}; + +uniform sampler2D depth_tx; + +in vec4 uvcoordsvar; + +layout(location = 0) out vec4 out_velocity_camera; +layout(location = 1) out vec4 out_velocity_view; + +void main(void) +{ + float depth = textureLod(depth_tx, uvcoordsvar.xy, 0.0).r; + + vec3 P = get_world_space_from_depth(uvcoordsvar.xy, depth); + vec3 P_prev = P, P_next = P; + + if (depth == 1.0) { + /* Background case. Only compute rotation velocity. */ + vec3 V = -cameraVec(P); + P_prev = cam_prev.viewinv[3].xyz + V; + P_next = cam_next.viewinv[3].xyz + V; + } + + if (depth == 1.0) { + /* Lookdev Sphere. Ouput no velocity. */ + out_velocity_camera = vec4(0); + out_velocity_view = vec4(0); + return; + } + + compute_velocity( + P_prev, P, P_next, cam_prev, cam_curr, cam_next, out_velocity_camera, out_velocity_view); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_closure_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_closure_lib.glsl new file mode 100644 index 00000000000..d255b9ad7a3 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_closure_lib.glsl @@ -0,0 +1,104 @@ + +struct ClosureDiffuse { + vec3 color; + vec3 N; + vec3 sss_radius; + uint sss_id; +}; + +struct ClosureReflection { + vec3 color; + vec3 N; + float roughness; +}; + +struct ClosureRefraction { + vec3 color; + vec3 N; + float roughness; + float ior; +}; + +struct ClosureVolume { + vec3 emission; + vec3 scattering; + vec3 transmittance; + float anisotropy; +}; + +struct ClosureEmission { + vec3 emission; +}; + +struct ClosureTransparency { + vec3 transmittance; + float holdout; +}; + +/* We use the weight tree pre-evaluation to weight the closures. + * There is no need for the Closure type. */ +struct Closure { + float dummy; +}; +#define CLOSURE_DEFAULT Closure(0.0) +#define closure_add(a, b) CLOSURE_DEFAULT +#define closure_mix(a, b, c) CLOSURE_DEFAULT + +/* Store weight in red channel. Store negative to differentiate with evaluated closure. */ +void closure_weight_add(inout ClosureDiffuse closure, float weight) +{ + closure.color.r -= weight; +} +void closure_weight_add(inout ClosureReflection closure, float weight) +{ + closure.color.r -= weight; +} +void closure_weight_add(inout ClosureRefraction closure, float weight) +{ + closure.color.r -= weight; +} + +/* Create a random threshold inside the weight range. */ +void closure_weight_randomize(inout ClosureDiffuse closure, float randu) +{ + closure.color.g = closure.color.r * randu; +} +void closure_weight_randomize(inout ClosureReflection closure, float randu) +{ + closure.color.g = closure.color.r * randu; +} +void closure_weight_randomize(inout ClosureRefraction closure, float randu) +{ + closure.color.g = closure.color.r * randu; +} + +bool closure_weight_threshold(inout ClosureDiffuse closure, inout float weight) +{ + /* Decrement weight from random threshold. */ + closure.color.g += weight; + /* Evaluate this closure if threshold reaches 0. */ + if (closure.color.g >= 0.0) { + /* Returns the sum of all weights. */ + weight = abs(closure.color.r); + return true; + } + return false; +} +bool closure_weight_threshold(inout ClosureReflection closure, inout float weight) +{ + closure.color.g += weight; + if (closure.color.g >= 0.0) { + weight = abs(closure.color.r); + return true; + } + return false; +} +bool closure_weight_threshold(inout ClosureRefraction closure, inout float weight) +{ + closure.color.g += weight; + if (closure.color.g >= 0.0) { + weight = abs(closure.color.r); + return true; + } + return false; +} diff --git a/source/blender/draw/engines/eevee/shaders/cubemap_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_cubemap_lib.glsl index 90272400915..3b6d6959183 100644 --- a/source/blender/draw/engines/eevee/shaders/cubemap_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/eevee_cubemap_lib.glsl @@ -1,12 +1,8 @@ -#ifdef GPU_ARB_texture_cube_map_array - -# define textureLod_cubemapArray(tex, co, lod) textureLod(tex, co, lod) - -#else - -/* Fallback implementation for hardware not supporting cubemap arrays. */ -# define samplerCubeArray sampler2DArray +/** + * Own implementation of cubemap sampling routines. Either used for custom cubemap + * storage (2D packing) or fallback for lack of samplerCubeArray support. + **/ float cubemap_face_index(vec3 P) { @@ -35,6 +31,35 @@ vec2 cubemap_face_coord(vec3 P, float face) } } +vec2 cubemap_face_coord(vec3 P, float face, float scale) +{ + if (face < 2.0) { + return (P.zy / P.x) * scale * vec2(-0.5, -sign(P.x) * 0.5) + 0.5; + } + else if (face < 4.0) { + return (P.xz / P.y) * scale * vec2(sign(P.y) * 0.5, 0.5) + 0.5; + } + else { + return (P.xy / P.z) * scale * vec2(0.5, -sign(P.z) * 0.5) + 0.5; + } +} + +vec2 cubemap_face_coord(vec3 P, float face, sampler2DArray tex) +{ + /* Scaling to compensate the 1px border around the face. */ + float cube_res = float(textureSize(tex, 0).x); + float scale = (cube_res) / (cube_res + 1.0); + return cubemap_face_coord(P, face, scale); +} + +vec2 cubemap_face_coord(vec3 P, float face, sampler2DArrayShadow tex) +{ + /* Scaling to compensate the 1px border around the face. */ + float cube_res = float(textureSize(tex, 0).x); + float scale = (cube_res) / (cube_res + 1.0); + return cubemap_face_coord(P, face, scale); +} + vec3 cubemap_adj_x(float face) { bool y_axis = (face == 2.0 || face == 3.0); @@ -60,7 +85,7 @@ vec3 cubemap_adj_xy(float face) } } -vec4 cubemap_seamless(sampler2DArray tex, vec4 cubevec, float lod) +vec4 cubemap_sample_lod_seamless(sampler2DArray tex, vec4 cubevec, float lod) { /* Manual Cube map Layer indexing. */ float face = cubemap_face_index(cubevec.xyz); @@ -116,15 +141,20 @@ vec4 cubemap_seamless(sampler2DArray tex, vec4 cubevec, float lod) } } -vec4 textureLod_cubemapArray(sampler2DArray tex, vec4 cubevec, float lod) +/* Fallback implementation for hardware not supporting cubemap arrays. */ +vec4 cubemap_array_sample(sampler2DArray tex, vec4 cubevec, float lod) { float lod1 = floor(lod); float lod2 = ceil(lod); - vec4 col_lod1 = cubemap_seamless(tex, cubevec, lod1); - vec4 col_lod2 = cubemap_seamless(tex, cubevec, lod2); + vec4 col_lod1 = cubemap_sample_lod_seamless(tex, cubevec, lod1); + vec4 col_lod2 = cubemap_sample_lod_seamless(tex, cubevec, lod2); return mix(col_lod1, col_lod2, lod - lod1); } +#ifdef GPU_ARB_texture_cube_map_array +# define cubemap_array_sample textureLod +#else +# define samplerCubeArray sampler2DArray #endif diff --git a/source/blender/draw/engines/eevee/shaders/eevee_culling_debug_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_culling_debug_frag.glsl new file mode 100644 index 00000000000..7b988a7b941 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_culling_debug_frag.glsl @@ -0,0 +1,72 @@ + +/** + * Debug Shader outputing a gradient of orange - white - blue to mark culling hotspots. + * Green pixels are error pixels that are missing lights from the culling pass (i.e: when culling + * pass is not conservative enough). + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_light_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_culling_iter_lib.glsl) + +layout(std430, binding = 0) readonly restrict buffer lights_buf +{ + LightData lights[]; +}; + +layout(std430, binding = 1) readonly restrict buffer lights_zbins_buf +{ + CullingZBin lights_zbins[]; +}; + +layout(std430, binding = 2) readonly restrict buffer lights_culling_buf +{ + CullingData light_culling; +}; + +layout(std430, binding = 3) readonly restrict buffer lights_tile_buf +{ + CullingWord lights_culling_words[]; +}; + +uniform sampler2D depth_tx; + +in vec4 uvcoordsvar; + +layout(location = 0) out vec4 out_debug_color; + +void main(void) +{ + float depth = texelFetch(depth_tx, ivec2(gl_FragCoord.xy), 0).r; + float vP_z = get_view_z_from_depth(depth); + + vec3 P = get_world_space_from_depth(uvcoordsvar.xy, depth); + + float lights_count = 0.0; + uint lights_cull = 0u; + LIGHT_FOREACH_BEGIN_LOCAL ( + light_culling, lights_zbins, lights_culling_words, gl_FragCoord.xy, vP_z, l_idx) { + LightData light = lights[l_idx]; + lights_cull |= 1u << l_idx; + lights_count += 1.0; + } + LIGHT_FOREACH_END + + uint lights_nocull = 0u; + LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL (light_culling, l_idx) { + LightData light = lights[l_idx]; + if (distance(light._position, P) < light.influence_radius_max) { + lights_nocull |= 1u << l_idx; + } + } + LIGHT_FOREACH_END + + if ((lights_cull & lights_nocull) != lights_nocull) { + /* ERROR. Some lights were culled incorrectly. */ + out_debug_color = vec4(0.0, 1.0, 0.0, 1.0); + } + else { + out_debug_color = vec4(heatmap_gradient(lights_count / 4.0), 1.0); + } +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_culling_iter_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_culling_iter_lib.glsl new file mode 100644 index 00000000000..05290be3e96 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_culling_iter_lib.glsl @@ -0,0 +1,73 @@ + +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +uint zbin_mask(uint word_index, uint zbin_min, uint zbin_max) +{ + uint word_start = word_index * 32u; + uint word_end = word_start + 31u; + uint local_min = max(zbin_min, word_start); + uint local_max = min(zbin_max, word_end); + uint mask_width = local_max - local_min + 1; + return bit_field_mask(mask_width, local_min); +} + +/* Waiting to implement extensions support. We need: + * - GL_KHR_shader_subgroup_ballot + * - GL_KHR_shader_subgroup_arithmetic + * or + * - Vulkan 1.1 + */ +#if 1 +# define subgroupMin(a) a +# define subgroupMax(a) a +# define subgroupOr(a) a +# define subgroupBroadcastFirst(a) a +#endif + +#define LIGHT_FOREACH_BEGIN_DIRECTIONAL(_culling, _item_index) \ + { \ + { \ + { \ + for (uint _item_index = 0u; _item_index < _culling.items_no_cull_count; _item_index++) { + +#define LIGHT_FOREACH_BEGIN_LOCAL(_culling, _zbins, _words, _pixel, _linearz, _item_index) \ + { \ + uint batch_count = divide_ceil_u(_culling.visible_count, CULLING_BATCH_SIZE); \ + uvec2 tile_co = uvec2(_pixel) / _culling.tile_size; \ + uint tile_word_offset = (tile_co.x + tile_co.y * _culling.tile_x_len) * \ + _culling.tile_word_len; \ + for (uint batch = 0; batch < batch_count; batch++) { \ + int zbin_index = culling_z_to_zbin(_culling, _linearz); \ + zbin_index = clamp(zbin_index, 0, CULLING_ZBIN_COUNT - 1); \ + uint zbin_data = _zbins[zbin_index + batch * CULLING_ZBIN_COUNT]; \ + uint min_index = zbin_data & 0xFFFFu; \ + uint max_index = zbin_data >> 16u; \ + /* Ensure all threads inside a subgroup get the same value to reduce VGPR usage. */ \ + min_index = subgroupBroadcastFirst(subgroupMin(min_index)); \ + max_index = subgroupBroadcastFirst(subgroupMax(max_index)); \ + uint word_min = min_index / 32u; \ + uint word_max = max_index / 32u; \ + for (uint word_idx = word_min; word_idx <= word_max; word_idx++) { \ + uint word = _words[tile_word_offset + word_idx]; \ + word &= zbin_mask(word_idx, min_index, max_index); \ + /* Ensure all threads inside a subgroup get the same value to reduce VGPR usage. */ \ + word = subgroupBroadcastFirst(subgroupOr(word)); \ + while (word != 0u) { \ + uint bit_index = uint(findLSB(word)); \ + word &= ~1u << bit_index; \ + uint _item_index = word_idx * 32u + bit_index; + +/* No culling. Iterate over all items. */ +#define LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL(_culling, _item_index) \ + { \ + { \ + { \ + for (uint _item_index = _culling.items_no_cull_count; \ + _item_index < _culling.visible_count; \ + _item_index++) { + +#define LIGHT_FOREACH_END \ + } \ + } \ + } \ + } diff --git a/source/blender/draw/engines/eevee/shaders/eevee_culling_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_culling_lib.glsl new file mode 100644 index 00000000000..27a39817140 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_culling_lib.glsl @@ -0,0 +1,175 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +/* ---------------------------------------------------------------------- */ +/** \name Intersection Tests + * \{ */ + +struct Cone { + vec3 direction; + float angle_cos; +}; + +struct Cylinder { + /* Assume Z axis as direction. */ + vec3 center; + float radius; +}; + +struct Frustum { + vec4 planes[4]; +}; + +struct CullingTile { + Frustum frustum; + Cone cone; +}; + +bool culling_sphere_cone_isect(Sphere sphere, Cone cone) +{ + /** + * Following "Improve Tile-based Light Culling with Spherical-sliced Cone" + * by Eric Zhang + * https://lxjk.github.io/2018/03/25/Improve-Tile-based-Light-Culling-with-Spherical-sliced-Cone.html + */ + float sphere_distance = length(sphere.center); + float sphere_sin = saturate(sphere.radius / sphere_distance); + float sphere_cos = sqrt(1.0 - sphere_sin * sphere_sin); + float cone_aperture_sin = sqrt(1.0 - cone.angle_cos * cone.angle_cos); + + float cone_sphere_center_cos = dot(sphere.center / sphere_distance, cone.direction); + /* cos(A+B) = cos(A) * cos(B) - sin(A) * sin(B). */ + float cone_sphere_angle_sum_cos = (sphere.radius > sphere_distance) ? + -1.0 : + (cone.angle_cos * sphere_cos - + cone_aperture_sin * sphere_sin); + + /* Comparing cosines instead of angles since we are interested + * only in the monotonic region [0 .. M_PI / 2]. This saves costly acos() calls. */ + return (cone_sphere_center_cos >= cone_sphere_angle_sum_cos); +} + +bool culling_sphere_cylinder_isect(Sphere sphere, Cylinder cylinder) +{ + float distance_squared = len_squared(sphere.center.xy - cylinder.center.xy); + return (distance_squared < sqr(cylinder.radius + sphere.radius)); +} + +bool culling_sphere_frustum_isect(Sphere sphere, Frustum frustum) +{ + if (dot(vec4(sphere.center, 1.0), frustum.planes[0]) > sphere.radius) { + return false; + } + if (dot(vec4(sphere.center, 1.0), frustum.planes[1]) > sphere.radius) { + return false; + } + if (dot(vec4(sphere.center, 1.0), frustum.planes[2]) > sphere.radius) { + return false; + } + if (dot(vec4(sphere.center, 1.0), frustum.planes[3]) > sphere.radius) { + return false; + } + return true; +} + +bool culling_sphere_tile_isect(Sphere sphere, CullingTile tile) +{ + /* Culling in view space for precision and simplicity. */ + sphere.center = transform_point(ViewMatrix, sphere.center); + bool isect; + /* Test tile intersection using bounding cone or bounding cylinder. + * This has less false positive cases when the sphere is large. */ + if (ProjectionMatrix[3][3] == 0.0) { + isect = culling_sphere_cone_isect(sphere, tile.cone); + } + else { + Cylinder cylinder = Cylinder(tile.cone.direction, tile.cone.angle_cos); + isect = culling_sphere_cylinder_isect(sphere, cylinder); + } + /* Refine using frustum test. If the sphere is small it avoids intersection + * with a neighbor tile. */ + if (isect) { + isect = culling_sphere_frustum_isect(sphere, tile.frustum); + } + return isect; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Culling shapes extraction + * \{ */ + +vec4 plane_from_quad(vec3 v0, vec3 v1, vec3 v2, vec3 v3) +{ + vec3 nor = normalize(cross(v2 - v1, v0 - v1) + cross(v0 - v3, v2 - v3)); + return vec4(nor, -dot(nor, v2)); +} + +/* Corners are expected to be in viewspace. */ +Cone cone_from_quad(vec3 corners[8]) +{ + for (int i = 0; i < 4; i++) { + corners[i] = normalize(corners[i]); + } + vec3 center = normalize(corners[0] + corners[1] + corners[2] + corners[3]); + + vec4 corners_cos; + for (int i = 0; i < 4; i++) { + corners_cos[i] = dot(center, corners[i]); + } + return Cone(center, max_v4(corners_cos)); +} + +/* Corners are expected to be in viewspace. Returns Z-aligned bounding cylinder. */ +Cone cylinder_from_quad(vec3 corners[8]) +{ + vec3 center = (corners[0] + corners[1] + corners[2] + corners[3]) * 0.25; + + vec4 corners_dist; + for (int i = 0; i < 4; i++) { + corners_dist[i] = distance_squared(center, corners[i]); + } + /* Return a cone. Later converted to cylinder. */ + return Cone(center, sqrt(max_v4(corners_dist))); +} + +vec2 tile_to_ndc(CullingData culling, vec2 tile_co, vec2 offset) +{ + /* Add a margin to prevent culling too much if the frustum becomes too much unstable. */ + tile_co += /* culling.tile_margin * */ offset; + return tile_co * culling.tile_to_uv_fac * 2.0 - 1.0; +} + +CullingTile culling_tile_get(CullingData culling, uvec2 tile_co) +{ + vec2 ftile = vec2(tile_co); + /* Culling frustum corners for this tile. */ + vec3 corners[8]; + corners[0].xy = corners[4].xy = tile_to_ndc(culling, ftile, vec2(1, 1)); + corners[1].xy = corners[5].xy = tile_to_ndc(culling, ftile, vec2(1, 0)); + corners[2].xy = corners[6].xy = tile_to_ndc(culling, ftile, vec2(0, 0)); + corners[3].xy = corners[7].xy = tile_to_ndc(culling, ftile, vec2(0, 1)); + /* The corners depth only matter for precision. Use a mix of not so close to clip plane to + * avoid small float imprecision if near clip is low. */ + corners[0].z = corners[1].z = corners[2].z = corners[3].z = -0.5; + corners[4].z = corners[5].z = corners[6].z = corners[7].z = 0.1; + + for (int i = 0; i < 8; i++) { + /* Culling in view space for precision. */ + corners[i] = project_point(ProjectionMatrixInverse, corners[i]); + } + + bool is_persp = ProjectionMatrix[3][3] == 0.0; + CullingTile tile; + tile.cone = (is_persp) ? cone_from_quad(corners) : cylinder_from_quad(corners); + tile.frustum.planes[0] = plane_from_quad(corners[0], corners[1], corners[5], corners[4]); + tile.frustum.planes[1] = plane_from_quad(corners[1], corners[2], corners[6], corners[5]); + tile.frustum.planes[2] = plane_from_quad(corners[2], corners[3], corners[7], corners[6]); + tile.frustum.planes[3] = plane_from_quad(corners[3], corners[0], corners[4], corners[7]); + return tile; +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/eevee_culling_select_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_culling_select_comp.glsl new file mode 100644 index 00000000000..f26e6621b58 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_culling_select_comp.glsl @@ -0,0 +1,60 @@ + +/** + * Select the visible items inside the active view and put them inside the sorting buffer. + */ + +#pragma BLENDER_REQUIRE(common_debug_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(common_intersection_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_light_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(local_size_x = CULLING_ITEM_BATCH) in; + +layout(std430, binding = 0) readonly restrict buffer lights_buf +{ + LightData lights[]; +}; + +layout(std430, binding = 1) restrict buffer culling_buf +{ + CullingData culling; +}; + +layout(std430, binding = 2) restrict buffer key_buf +{ + uint keys[]; +}; + +void main() +{ + uint l_idx = gl_GlobalInvocationID.x; + if (l_idx >= culling.items_count) { + return; + } + + LightData light = lights[l_idx]; + + /* Sun lights are packed at the start of the array. */ + if (light.type == LIGHT_SUN) { + keys[l_idx] = l_idx; + return; + } + + Sphere sphere; + switch (light.type) { + case LIGHT_SPOT: + /* TODO cone culling. */ + case LIGHT_RECT: + case LIGHT_ELLIPSE: + case LIGHT_POINT: + sphere = Sphere(light._position, light.influence_radius_max); + break; + } + + if (intersect_view(sphere)) { + uint index = culling.items_no_cull_count + atomicAdd(culling.visible_count, 1u); + keys[index] = l_idx; + } +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_culling_sort_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_culling_sort_comp.glsl new file mode 100644 index 00000000000..71307525b6d --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_culling_sort_comp.glsl @@ -0,0 +1,148 @@ + +/** + * Sort the lights by their Z distance to the camera. + * Outputs ordered light buffer and associated zbins. + * We split the work in CULLING_BATCH_SIZE and iterate to cover all zbins. + * One thread process one Light entity. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_light_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(local_size_x = CULLING_BATCH_SIZE) in; + +layout(std430, binding = 0) readonly restrict buffer lights_buf +{ + LightData lights[]; +}; + +layout(std430, binding = 1) restrict buffer culling_buf +{ + CullingData culling; +}; + +layout(std430, binding = 2) readonly restrict buffer key_buf +{ + uint keys[]; +}; + +layout(std430, binding = 3) writeonly restrict buffer out_zbins_buf +{ + CullingZBin out_zbins[]; +}; + +layout(std430, binding = 4) writeonly restrict buffer out_items_buf +{ + LightData out_lights[]; +}; + +void main() +{ + uint src_index = gl_GlobalInvocationID.x; + bool valid_thread = true; + + uint items_count_total = culling.items_no_cull_count + culling.visible_count; + + if (src_index >= items_count_total) { + /* Do not return because we use barriers later on (which need uniform control flow). + * Just process the same last item but avoid insertion. */ + src_index = items_count_total - 1; + valid_thread = false; + } + + uint key = keys[src_index]; + LightData light = lights[key]; + + if (light.type == LIGHT_SUN) { + valid_thread = false; + } + + if (!culling.enable_specular) { + light.specular_power = 0.0; + } + + int index = 0; + int contenders = 0; + + vec3 lP = light._position; + float radius = light.influence_radius_max; + float z_dist = dot(cameraForward, lP) - dot(cameraForward, cameraPos); + + int z_min = clamp(culling_z_to_zbin(culling, z_dist + radius), 0, CULLING_ZBIN_COUNT - 1); + int z_max = clamp(culling_z_to_zbin(culling, z_dist - radius), 0, CULLING_ZBIN_COUNT - 1); + + /* Fits the limit of 32KB. */ + shared int zbin_max[CULLING_ZBIN_COUNT]; + shared int zbin_min[CULLING_ZBIN_COUNT]; + /* Compilers do not release shared memory from early declaration. + * So we are forced to reuse the same variables in another form. */ +#define z_dists zbin_max +#define contender_table zbin_min + + /** + * Find how many values are before the local value. + * This finds the first possible destination index. + */ + z_dists[gl_LocalInvocationID.x] = floatBitsToInt(z_dist); + barrier(); + + int batch_start = int(gl_WorkGroupID.x) * CULLING_BATCH_SIZE; + int i_start = max(batch_start, int(culling.items_no_cull_count)); + int i_max = min(CULLING_BATCH_SIZE, int(items_count_total) - batch_start); + for (int i = i_start; i < i_max; i++) { + float ref = intBitsToFloat(z_dists[i]); + if (ref > z_dist) { + index++; + } + else if (ref == z_dist) { + contenders++; + } + } + + if (valid_thread) { + atomicExchange(contender_table[index], contenders); + } + barrier(); + + if (valid_thread) { + /** + * For each clashing index (where two lights have exactly the same z distances) + * we use an atomic counter to know how much to offset from the disputed index. + */ + index += atomicAdd(contender_table[index], -1) - 1; + index += i_start; + out_lights[index] = light; + } + else if (light.type == LIGHT_SUN) { + /* Directional lights are just copied to the same index. */ + out_lights[key] = light; + } + barrier(); + + const uint iter = uint(CULLING_ZBIN_COUNT / CULLING_BATCH_SIZE); + const uint zbin_local = gl_LocalInvocationID.x * iter; + const uint zbin_global = gl_WorkGroupID.x * CULLING_ZBIN_COUNT + zbin_local; + + for (uint i = 0u, l = zbin_local; i < iter; i++, l++) { + zbin_max[l] = 0x0000; + zbin_min[l] = 0xFFFF; + } + barrier(); + + /* Register to Z bins. */ + if (valid_thread) { + for (int z = z_min; z <= z_max; z++) { + atomicMin(zbin_min[z], index); + atomicMax(zbin_max[z], index); + } + } + barrier(); + + /* Write result to zbins buffer. */ + for (uint i = 0u, g = zbin_global, l = zbin_local; i < iter; i++, g++, l++) { + /* Pack min & max into 1 uint. */ + out_zbins[g] = (uint(zbin_max[l]) << 16u) | uint(zbin_min[l]); + } +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_culling_tile_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_culling_tile_comp.glsl new file mode 100644 index 00000000000..7c14b3c42ee --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_culling_tile_comp.glsl @@ -0,0 +1,70 @@ + +/** + * 2D Culling pass for lights. + * We iterate over all items and check if they intersect with the tile frustum. + * Dispatch one thread per word. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_light_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) +#pragma BLENDER_REQUIRE(eevee_culling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_culling_iter_lib.glsl) + +layout(local_size_x = 1024) in; + +layout(std430, binding = 0) readonly restrict buffer lights_buf +{ + LightData lights[]; +}; + +layout(std430, binding = 1) readonly restrict buffer culling_buf +{ + CullingData culling; +}; + +layout(std430, binding = 2) writeonly restrict buffer culling_tile_buf +{ + CullingWord culling_words[]; +}; + +void main(void) +{ + uint word_idx = gl_GlobalInvocationID.x % culling.tile_word_len; + uint tile_idx = gl_GlobalInvocationID.x / culling.tile_word_len; + uvec2 tile_co = uvec2(tile_idx % culling.tile_x_len, tile_idx / culling.tile_x_len); + + if (tile_co.y >= culling.tile_y_len) { + return; + } + + /* TODO(fclem): We could stop the tile at the HiZ depth. */ + CullingTile tile = culling_tile_get(culling, tile_co); + + uint l_idx = max(word_idx * 32u, culling.items_no_cull_count); + uint l_end = min(l_idx + 32u, culling.visible_count + culling.items_no_cull_count); + uint word = 0u; + + for (; l_idx < l_end; l_idx++) { + LightData light = lights[l_idx]; + + bool intersect_tile; + switch (light.type) { + case LIGHT_SPOT: + /* TODO cone culling. */ + case LIGHT_RECT: + case LIGHT_ELLIPSE: + case LIGHT_POINT: + Sphere sphere = Sphere(light._position, light.influence_radius_max); + intersect_tile = culling_sphere_tile_isect(sphere, tile); + break; + } + + if (intersect_tile) { + word |= 1u << (l_idx & 0x1Fu); + } + } + + culling_words[gl_GlobalInvocationID.x] = word; +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_deferred_direct_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_deferred_direct_frag.glsl new file mode 100644 index 00000000000..41c38efc4fd --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_deferred_direct_frag.glsl @@ -0,0 +1,136 @@ + +/** + * Direct lighting evaluation: Evaluate lights and light-probes contributions for all bsdfs. + **/ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_debug_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_culling_iter_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) +#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl) + +layout(std140) uniform sampling_block +{ + SamplingData sampling; +}; + +layout(std430, binding = 0) readonly restrict buffer lights_buf +{ + LightData lights[]; +}; + +layout(std430, binding = 1) readonly restrict buffer lights_zbins_buf +{ + CullingZBin lights_zbins[]; +}; + +layout(std430, binding = 2) readonly restrict buffer lights_culling_buf +{ + CullingData light_culling; +}; + +layout(std430, binding = 3) readonly restrict buffer lights_tile_buf +{ + CullingWord lights_culling_words[]; +}; + +layout(std140) uniform grids_block +{ + GridData grids[GRID_MAX]; +}; + +layout(std140) uniform cubes_block +{ + CubemapData cubes[CULLING_ITEM_BATCH]; +}; + +layout(std140) uniform lightprobes_info_block +{ + LightProbeInfoData probes_info; +}; + +uniform sampler2D hiz_tx; +uniform sampler2D emission_data_tx; +uniform sampler2D transmit_color_tx; +uniform sampler2D transmit_normal_tx; +uniform sampler2D transmit_data_tx; +uniform sampler2D reflect_color_tx; +uniform sampler2D reflect_normal_tx; +uniform sampler1D sss_transmittance_tx; +uniform sampler2DArray utility_tx; +uniform sampler2D shadow_atlas_tx; +uniform usampler2D shadow_tilemaps_tx; +uniform sampler2DArray lightprobe_grid_tx; +uniform samplerCubeArray lightprobe_cube_tx; + +utility_tx_fetch_define(utility_tx); +utility_tx_sample_define(utility_tx); + +in vec4 uvcoordsvar; + +layout(location = 0) out vec4 out_combined; +layout(location = 1) out vec4 out_diffuse; +layout(location = 2) out vec3 out_specular; + +/* Prototypes. */ +void light_eval(ClosureDiffuse diffuse, + ClosureReflection reflection, + vec3 P, + vec3 V, + float vP_z, + float thickness, + inout vec3 out_diffuse, + inout vec3 out_specular); +vec3 lightprobe_grid_eval(vec3 P, vec3 N, float random_threshold); +vec3 lightprobe_cubemap_eval(vec3 P, vec3 R, float roughness, float random_threshold); + +void main(void) +{ + vec2 uv = uvcoordsvar.xy; + float gbuffer_depth = texelFetch(hiz_tx, ivec2(gl_FragCoord.xy), 0).r; + vec3 vP = get_view_space_from_depth(uv, gbuffer_depth); + vec3 P = point_view_to_world(vP); + vec3 V = cameraVec(P); + + vec4 tra_col_in = texture(transmit_color_tx, uv); + vec4 tra_nor_in = texture(transmit_normal_tx, uv); + vec4 tra_dat_in = texture(transmit_data_tx, uv); + vec4 ref_col_in = texture(reflect_color_tx, uv); + vec4 ref_nor_in = texture(reflect_normal_tx, uv); + + ClosureEmission emission = gbuffer_load_emission_data(emission_data_tx, uv); + ClosureDiffuse diffuse = gbuffer_load_diffuse_data(tra_col_in, tra_nor_in, tra_dat_in); + ClosureReflection reflection = gbuffer_load_reflection_data(ref_col_in, ref_nor_in); + + float thickness; + gbuffer_load_global_data(tra_nor_in, thickness); + + float noise_offset = sampling_rng_1D_get(sampling, SAMPLING_LIGHTPROBE); + float noise = utility_tx_fetch(gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r; + float random_probe = fract(noise + noise_offset); + + vec3 radiance_diffuse = vec3(0); + vec3 radiance_reflection = vec3(0); + vec3 R = -reflect(V, reflection.N); + + light_eval(diffuse, reflection, P, V, vP.z, thickness, radiance_diffuse, radiance_reflection); + + out_combined = vec4(emission.emission, 0.0); + out_diffuse.rgb = radiance_diffuse; + /* FIXME(fclem): This won't work after the first light batch since we use additive blending. */ + out_diffuse.a = fract(float(diffuse.sss_id) / 1024.0) * 1024.0; + /* Do not apply color to diffuse term for SSS material. */ + if (diffuse.sss_id == 0u) { + out_diffuse.rgb *= diffuse.color; + out_combined.rgb += out_diffuse.rgb; + } + out_specular = radiance_reflection * reflection.color; + out_combined.rgb += out_specular; +} + +#pragma BLENDER_REQUIRE_POST(eevee_light_eval_lib.glsl) +#pragma BLENDER_REQUIRE_POST(eevee_lightprobe_eval_cubemap_lib.glsl) +#pragma BLENDER_REQUIRE_POST(eevee_lightprobe_eval_grid_lib.glsl) diff --git a/source/blender/draw/engines/eevee/shaders/eevee_deferred_holdout_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_deferred_holdout_frag.glsl new file mode 100644 index 00000000000..5078b97951f --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_deferred_holdout_frag.glsl @@ -0,0 +1,25 @@ + +/** + * Save radiance from main pass to subtract to final render. + * + * This way all screen space effects (SSS, SSR) are not altered by the presence of the holdout. + **/ + +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) + +uniform sampler2D combined_tx; +uniform sampler2D transparency_data_tx; + +in vec4 uvcoordsvar; + +layout(location = 5) out vec3 out_holdout; + +void main(void) +{ + vec3 combined_radiance = texture(combined_tx, uvcoordsvar.xy).rgb; + + ClosureTransparency transparency_data = gbuffer_load_transparency_data(transparency_data_tx, + uvcoordsvar.xy); + + out_holdout = combined_radiance * transparency_data.holdout; +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_deferred_transparent_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_deferred_transparent_frag.glsl new file mode 100644 index 00000000000..92bfa845e87 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_deferred_transparent_frag.glsl @@ -0,0 +1,59 @@ + +/** + * Apply transmittance to all radiance passes. + * + * We needs to evaluate the transmittance of homogeneous volumes if any is present. + * Hopefully, this has O(1) complexity as we do not need to raymarch the volume. + * + * Using blend mode multiply. + **/ + +#pragma BLENDER_REQUIRE(eevee_volume_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) + +uniform usampler2D volume_data_tx; +uniform sampler2D transparency_data_tx; + +in vec4 uvcoordsvar; + +layout(location = 0) out vec4 out_combined; +layout(location = 1) out vec3 out_diffuse; +layout(location = 2) out vec3 out_specular; +layout(location = 3) out vec3 out_volume; +layout(location = 4) out vec3 out_background; +layout(location = 5) out vec3 out_holdout; + +void main(void) +{ + ClosureVolume volume_data = gbuffer_load_volume_data(volume_data_tx, uvcoordsvar.xy); + + /* For volumes from solid objects. */ + // float depth_max = linear_z(texture(depth_max_tx, uv).r); + // float depth_min = linear_z(texture(depth_min_tx, uv).r); + + /* Refine bounds to skip empty areas. */ + // float dist_from_bbox = intersect_bbox_ray(P, V, bbox); + // depth_min = max(dist_from_bbox, depth_min); + + vec3 volume_transmittance; + if (volume_data.anisotropy == VOLUME_HETEROGENEOUS) { + volume_transmittance = volume_data.transmittance; + } + else { + // volume_eval_homogeneous(P, depth_min, depth_max, volume_transmittance); + volume_transmittance = vec3(0.0); + } + + vec3 surface_transmittance = + gbuffer_load_transparency_data(transparency_data_tx, uvcoordsvar.xy).transmittance; + + vec3 final_transmittance = volume_transmittance * surface_transmittance; + + /* Multiply transmittance all radiance buffers. Remember that blend mode is multiply. */ + out_combined = vec4(final_transmittance, avg(final_transmittance)); + out_diffuse = final_transmittance; + out_specular = final_transmittance; + out_volume = final_transmittance; + out_background = final_transmittance; + out_holdout = final_transmittance; +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_deferred_volume_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_deferred_volume_frag.glsl new file mode 100644 index 00000000000..7e83ea356ca --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_deferred_volume_frag.glsl @@ -0,0 +1,80 @@ + +/** + * Apply heterogeneous volume lighting and evaluates homogeneous volumetrics if needed. + * + * We read volume parameters from the gbuffer and consider them constant for the whole volume. + * This only applies to solid objects not volumes. + **/ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_bsdf_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_culling_iter_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_volume_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(std430, binding = 0) readonly restrict buffer lights_buf +{ + LightData lights[]; +}; + +layout(std430, binding = 1) readonly restrict buffer lights_zbins_buf +{ + CullingZBin lights_zbins[]; +}; + +layout(std430, binding = 2) readonly restrict buffer lights_culling_buf +{ + CullingData light_culling; +}; + +layout(std430, binding = 3) readonly restrict buffer lights_tile_buf +{ + CullingWord lights_culling_words[]; +}; + +uniform sampler2D transparency_data_tx; +uniform usampler2D volume_data_tx; +uniform sampler2DArray utility_tx; +uniform sampler2DShadow shadow_atlas_tx; +uniform usampler2D shadow_tilemaps_tx; + +utility_tx_fetch_define(utility_tx) utility_tx_sample_define(utility_tx) + + in vec4 uvcoordsvar; + +layout(location = 0) out vec4 out_combined; +layout(location = 1) out vec3 out_volume; + +void main(void) +{ + ClosureVolume volume_data = gbuffer_load_volume_data(volume_data_tx, uvcoordsvar.xy); + + /* For volumes from solid objects. */ + // float depth_max = linear_z(texture(depth_max_tx, uv).r); + // float depth_min = linear_z(texture(depth_min_tx, uv).r); + + /* Refine bounds to skip empty areas. */ + // float dist_from_bbox = intersect_bbox_ray(P, V, bbox); + // depth_min = max(dist_from_bbox, depth_min); + + vec3 volume_radiance; + if (volume_data.anisotropy == VOLUME_HETEROGENEOUS) { + volume_radiance = volume_data.scattering; + } + else { + // volume_eval_homogeneous(P, depth_min, depth_max, volume_radiance); + volume_radiance = vec3(0.0); + } + + /* Apply transmittance of surface on volumetric radiance because + * the volume is behind the surface. */ + ClosureTransparency transparency_data = gbuffer_load_transparency_data(transparency_data_tx, + uvcoordsvar.xy); + volume_radiance *= transparency_data.transmittance; + + out_combined = vec4(volume_radiance, 0.0); + out_volume = volume_radiance; +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_clear_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_clear_frag.glsl new file mode 100644 index 00000000000..8adad15baeb --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_clear_frag.glsl @@ -0,0 +1,6 @@ + +void main(void) +{ + /* No color output, only depth (line below is implicit). */ + /* gl_FragDepth = gl_FragCoord.z; */ +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_accumulator_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_accumulator_lib.glsl new file mode 100644 index 00000000000..f51807e71c6 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_accumulator_lib.glsl @@ -0,0 +1,685 @@ + +/** + * Depth of Field Gather accumulator. + * We currently have only 2 which are very similar. + * One is for the halfres gather passes and the other one for slight in focus regions. + **/ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) + +/* -------------------------------------------------------------------- */ +/** \name Options. + * \{ */ + +/* Quality options */ +#ifdef DOF_HOLEFILL_PASS +/* No need for very high density for holefill. */ +const int gather_ring_count = 3; +const int gather_ring_density = 3; +const int gather_max_density_change = 0; +const int gather_density_change_ring = 1; +#else +const int gather_ring_count = DOF_GATHER_RING_COUNT; +const int gather_ring_density = 3; +const int gather_max_density_change = 50; /* Dictates the maximum good quality blur. */ +const int gather_density_change_ring = 1; +#endif + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Constants. + * \{ */ + +const float unit_ring_radius = 1.0 / float(gather_ring_count); +const float unit_sample_radius = 1.0 / float(gather_ring_count + 0.5); +const float large_kernel_radius = 0.5 + float(gather_ring_count); +const float smaller_kernel_radius = 0.5 + float(gather_ring_count - gather_density_change_ring); +/* NOTE(fclem) the bias is reducing issues with density change visible transition. */ +const float radius_downscale_factor = smaller_kernel_radius / large_kernel_radius; +const int change_density_at_ring = (gather_ring_count - gather_density_change_ring + 1); +const float coc_radius_error = 2.0; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Gather common. + * \{ */ + +struct DofGatherData { + vec4 color; + float weight; + float dist; /* TODO remove */ + /* For scatter occlusion. */ + float coc; + float coc_sqr; + /* For ring bucket merging. */ + float transparency; + + float layer_opacity; +}; + +#define GATHER_DATA_INIT DofGatherData(vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) + +/* Intersection with the center of the kernel. */ +float dof_intersection_weight(float coc, float distance_from_center, float intersection_multiplier) +{ + if (no_smooth_intersection) { + return step(0.0, (abs(coc) - distance_from_center)); + } + else { + /* (Slide 64). */ + return saturate((abs(coc) - distance_from_center) * intersection_multiplier + 0.5); + } +} + +/* Returns weight of the sample for the outer bucket (containing previous + * rings). */ +float dof_gather_accum_weight(float coc, float bordering_radius, bool first_ring) +{ + /* First ring has nothing to be mixed against. */ + if (first_ring) { + return 0.0; + } + return saturate(coc - bordering_radius); +} + +void dof_gather_ammend_weight(inout DofGatherData sample_data, float weight) +{ + sample_data.color *= weight; + sample_data.coc *= weight; + sample_data.coc_sqr *= weight; + sample_data.weight *= weight; +} + +void dof_gather_accumulate_sample(DofGatherData sample_data, + float weight, + inout DofGatherData accum_data) +{ + accum_data.color += sample_data.color * weight; + accum_data.coc += sample_data.coc * weight; + accum_data.coc_sqr += sample_data.coc * (sample_data.coc * weight); + accum_data.weight += weight; +} + +void dof_gather_accumulate_sample_pair(DofGatherData pair_data[2], + float bordering_radius, + float intersection_multiplier, + bool first_ring, + const bool do_fast_gather, + const bool is_foreground, + inout DofGatherData ring_data, + inout DofGatherData accum_data) +{ + if (do_fast_gather) { + for (int i = 0; i < 2; i++) { + dof_gather_accumulate_sample(pair_data[i], 1.0, accum_data); + accum_data.layer_opacity += 1.0; + } + return; + } + +#if 0 + const float mirroring_threshold = -dof_layer_threshold - dof_layer_offset; + /* TODO(fclem) Promote to parameter? dither with Noise? */ + const float mirroring_min_distance = 15.0; + if (pair_data[0].coc < mirroring_threshold && + (pair_data[1].coc - mirroring_min_distance) > pair_data[0].coc) { + pair_data[1].coc = pair_data[0].coc; + } + else if (pair_data[1].coc < mirroring_threshold && + (pair_data[0].coc - mirroring_min_distance) > pair_data[1].coc) { + pair_data[0].coc = pair_data[1].coc; + } +#endif + + for (int i = 0; i < 2; i++) { + float sample_weight = dof_sample_weight(pair_data[i].coc); + float layer_weight = dof_layer_weight(pair_data[i].coc, is_foreground); + float inter_weight = dof_intersection_weight( + pair_data[i].coc, pair_data[i].dist, intersection_multiplier); + float weight = inter_weight * layer_weight * sample_weight; + + /** + * If a CoC is larger than bordering radius we accumulate it to the general accumulator. + * If not, we accumulate to the ring bucket. This is to have more consistent sample occlusion. + **/ + float accum_weight = dof_gather_accum_weight(pair_data[i].coc, bordering_radius, first_ring); + dof_gather_accumulate_sample(pair_data[i], weight * accum_weight, accum_data); + dof_gather_accumulate_sample(pair_data[i], weight * (1.0 - accum_weight), ring_data); + + accum_data.layer_opacity += layer_weight; + + if (is_foreground) { + ring_data.transparency += 1.0 - inter_weight * layer_weight; + } + else { + float coc = is_foreground ? -pair_data[i].coc : pair_data[i].coc; + ring_data.transparency += saturate(coc - bordering_radius); + } + } +} + +void dof_gather_accumulate_sample_ring(DofGatherData ring_data, + int sample_count, + bool first_ring, + const bool do_fast_gather, + /* accum_data occludes the ring_data if true. */ + const bool reversed_occlusion, + inout DofGatherData accum_data) +{ + if (do_fast_gather) { + /* Do nothing as ring_data contains nothing. All samples are already in + * accum_data. */ + return; + } + + if (first_ring) { + /* Layer opacity is directly accumulated into accum_data data. */ + accum_data.color = ring_data.color; + accum_data.coc = ring_data.coc; + accum_data.coc_sqr = ring_data.coc_sqr; + accum_data.weight = ring_data.weight; + + accum_data.transparency = ring_data.transparency / float(sample_count); + return; + } + + if (ring_data.weight == 0.0) { + return; + } + + float ring_avg_coc = ring_data.coc / ring_data.weight; + float accum_avg_coc = accum_data.coc / accum_data.weight; + + /* Smooth test to set opacity to see if the ring average coc occludes the + * accumulation. Test is reversed to be multiplied against opacity. */ + float ring_occlu = saturate(accum_avg_coc - ring_avg_coc); + /* The bias here is arbitrary. Seems to avoid weird looking foreground in most + * cases. We might need to make it a parameter or find a relative bias. */ + float accum_occlu = saturate((ring_avg_coc - accum_avg_coc) * 0.1 - 1.0); + + if (is_resolve) { + ring_occlu = accum_occlu = 0.0; + } + + if (no_gather_occlusion) { + ring_occlu = 0.0; + accum_occlu = 0.0; + } + + /* (Slide 40) */ + float ring_opacity = saturate(1.0 - ring_data.transparency / float(sample_count)); + float accum_opacity = 1.0 - accum_data.transparency; + + if (reversed_occlusion) { + /* Accum_data occludes the ring. */ + float alpha = (accum_data.weight == 0.0) ? 0.0 : accum_opacity * accum_occlu; + float one_minus_alpha = 1.0 - alpha; + + accum_data.color += ring_data.color * one_minus_alpha; + accum_data.coc += ring_data.coc * one_minus_alpha; + accum_data.coc_sqr += ring_data.coc_sqr * one_minus_alpha; + accum_data.weight += ring_data.weight * one_minus_alpha; + + accum_data.transparency *= 1.0 - ring_opacity; + } + else { + /* Ring occludes the accum_data (Same as reference). */ + float alpha = (accum_data.weight == 0.0) ? 1.0 : (ring_opacity * ring_occlu); + float one_minus_alpha = 1.0 - alpha; + + accum_data.color = accum_data.color * one_minus_alpha + ring_data.color; + accum_data.coc = accum_data.coc * one_minus_alpha + ring_data.coc; + accum_data.coc_sqr = accum_data.coc_sqr * one_minus_alpha + ring_data.coc_sqr; + accum_data.weight = accum_data.weight * one_minus_alpha + ring_data.weight; + } +} + +/* FIXME(fclem) Seems to be wrong since it needs ringcount+1 as input for + * slightfocus gather. */ +/* This should be replaced by web_sample_count_get() but doing so is breaking other things. */ +int dof_gather_total_sample_count(const int ring_count, const int ring_density) +{ + return (ring_count * ring_count - ring_count) * ring_density + 1; +} + +void dof_gather_accumulate_center_sample(DofGatherData center_data, + float bordering_radius, + int i_radius, + const bool do_fast_gather, + const bool is_foreground, + const bool is_resolve, + inout DofGatherData accum_data) +{ + float layer_weight = dof_layer_weight(center_data.coc, is_foreground); + float sample_weight = dof_sample_weight(center_data.coc); + float weight = layer_weight * sample_weight; + float accum_weight = dof_gather_accum_weight(center_data.coc, bordering_radius, false); + + if (do_fast_gather) { + /* Hope for the compiler to optimize the above. */ + layer_weight = 1.0; + sample_weight = 1.0; + accum_weight = 1.0; + weight = 1.0; + } + + center_data.transparency = 1.0 - weight; + + dof_gather_accumulate_sample(center_data, weight * accum_weight, accum_data); + + if (!do_fast_gather) { + if (is_resolve) { + /* NOTE(fclem): Hack to smooth transition to full in-focus opacity. */ + int total_sample_count = dof_gather_total_sample_count(i_radius + 1, + DOF_SLIGHT_FOCUS_DENSITY); + float fac = saturate(1.0 - abs(center_data.coc) / float(dof_layer_threshold)); + accum_data.layer_opacity += float(total_sample_count) * fac * fac; + } + accum_data.layer_opacity += layer_weight; + + /* Logic of dof_gather_accumulate_sample(). */ + weight *= (1.0 - accum_weight); + center_data.coc_sqr = center_data.coc * (center_data.coc * weight); + center_data.color *= weight; + center_data.coc *= weight; + center_data.weight = weight; + + if (is_foreground && !is_resolve) { + /* Reduce issue with closer foreground over distant foreground. */ + float ring_area = sqr(bordering_radius); + dof_gather_ammend_weight(center_data, ring_area); + } + + /* Accumulate center as its own ring. */ + dof_gather_accumulate_sample_ring( + center_data, 1, false, do_fast_gather, is_foreground, accum_data); + } +} + +int dof_gather_total_sample_count_with_density_change(const int ring_count, + const int ring_density, + int density_change) +{ + int sample_count_per_density_change = dof_gather_total_sample_count(ring_count, ring_density) - + dof_gather_total_sample_count( + ring_count - gather_density_change_ring, ring_density); + + return dof_gather_total_sample_count(ring_count, ring_density) + + sample_count_per_density_change * density_change; +} + +void dof_gather_accumulate_resolve(int total_sample_count, + DofGatherData accum_data, + out vec4 out_col, + out float out_weight, + out vec2 out_occlusion) +{ + float weight_inv = safe_rcp(accum_data.weight); + out_col = accum_data.color * weight_inv; + out_occlusion = vec2(abs(accum_data.coc), accum_data.coc_sqr) * weight_inv; + + if (is_foreground) { + out_weight = 1.0 - accum_data.transparency; + } + else if (accum_data.weight > 0.0) { + out_weight = accum_data.layer_opacity / float(total_sample_count); + } + else { + out_weight = 0.0; + } + /* Gathering may not accumulate to 1.0 alpha because of float precision. */ + if (out_weight > 0.99) { + out_weight = 1.0; + } + else if (out_weight < 0.01) { + out_weight = 0.0; + } + /* Same thing for alpha channel. */ + if (out_col.a > 0.99) { + out_col.a = 1.0; + } + else if (out_col.a < 0.01) { + out_col.a = 0.0; + } +} + +float dof_load_gather_coc(sampler2D gather_input_coc_tx, vec2 uv, float lod) +{ + float coc = textureLod(gather_input_coc_tx, uv, lod).r; + /* We gather at halfres. CoC must be divided by 2 to be compared against radii. */ + return coc * 0.5; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Common Gather accumulator. + * \{ */ + +/* Radii needs to be halfres CoC sizes. */ +bool dof_do_density_change(float base_radius, float min_intersectable_radius) +{ + /* Reduce artifact for very large blur. */ + min_intersectable_radius *= 0.1; + + bool need_new_density = (base_radius * unit_ring_radius > min_intersectable_radius); + bool larger_than_min_density = (base_radius * radius_downscale_factor > + float(gather_ring_count)); + + return need_new_density && larger_than_min_density; +} + +void dof_gather_init(DepthOfFieldData dof, + float base_radius, + vec2 noise, + out vec2 center_co, + out float lod, + out float intersection_multiplier) +{ + /* Jitter center half a ring to reduce undersampling. */ + vec2 jitter_ofs = 0.499 * sample_disk(noise); + if (DOF_BOKEH_TEXTURE) { + jitter_ofs *= dof.bokeh_anisotropic_scale; + } + center_co = gl_FragCoord.xy + jitter_ofs * base_radius * unit_sample_radius; + + /* TODO(fclem) Seems like the default lod selection is too big. Bias to avoid blocky moving out + * of focus shapes. */ + const float lod_bias = -2.0; + lod = max(floor(log2(base_radius * unit_sample_radius) + 0.5) + lod_bias, 0.0); + + if (no_gather_mipmaps) { + lod = 0.0; + } + /* (Slide 64). */ + intersection_multiplier = pow(0.5, lod); +} + +void dof_gather_accumulator(SamplingData sampling, + DepthOfFieldData dof, + sampler2D color_tx, + sampler2D color_bilinear_tx, + sampler2D coc_tx, + sampler2D bokeh_lut_tx, + float base_radius, + float min_intersectable_radius, + const bool do_fast_gather, + const bool do_density_change, + out vec4 out_color, + out float out_weight, + out vec2 out_occlusion) +{ + vec2 noise_offset = sampling_rng_2D_get(sampling, SAMPLING_LENS_U); + vec2 noise = no_gather_random ? + vec2(0.0, 0.0) : + vec2(interlieved_gradient_noise(gl_FragCoord.xy, 0, noise_offset.x), + interlieved_gradient_noise(gl_FragCoord.xy, 1, noise_offset.y)); + + if (!do_fast_gather) { + /* Jitter the radius to reduce noticeable density changes. */ + base_radius += noise.x * unit_ring_radius * base_radius; + } + else { + /* Jittering the radius more than we need means we are going to feather the bokeh shape half a + * ring. So we need to compensate for fast gather that does not check CoC intersection. */ + base_radius += (0.5 - noise.x) * 1.5 * unit_ring_radius * base_radius; + } + /* TODO(fclem) another seed? For now Cranly-Partterson rotation with golden ratio. */ + noise.x = fract(noise.x * 6.1803398875); + + float lod, isect_mul; + vec2 center_co; + dof_gather_init(dof, base_radius, noise, center_co, lod, isect_mul); + + bool first_ring = true; + + DofGatherData accum_data = GATHER_DATA_INIT; + + int density_change = 0; + for (int ring = gather_ring_count; ring > 0; ring--) { + int sample_pair_count = gather_ring_density * ring; + + float step_rot = M_PI / float(sample_pair_count); + mat2 step_rot_mat = rot2_from_angle(step_rot); + + float angle_offset = noise.y * step_rot; + vec2 offset = vec2(cos(angle_offset), sin(angle_offset)); + + float ring_radius = float(ring) * unit_sample_radius * base_radius; + + /* Slide 38. */ + float bordering_radius = ring_radius + + (0.5 + coc_radius_error) * base_radius * unit_sample_radius; + DofGatherData ring_data = GATHER_DATA_INIT; + for (int sample_pair = 0; sample_pair < sample_pair_count; sample_pair++) { + offset = step_rot_mat * offset; + + DofGatherData pair_data[2]; + for (int i = 0; i < 2; i++) { + vec2 offset_co = ((i == 0) ? offset : -offset); + if (DOF_BOKEH_TEXTURE) { + /* Scaling to 0.25 for speed. Improves texture cache hit. */ + offset_co = texture(bokeh_lut_tx, offset_co * 0.25 + 0.5).rg; + offset_co *= (is_foreground) ? -dof.bokeh_anisotropic_scale : + dof.bokeh_anisotropic_scale; + } + vec2 sample_co = center_co + offset_co * ring_radius; + vec2 sample_uv = sample_co * dof.gather_uv_fac; + if (do_fast_gather) { + pair_data[i].color = textureLod(color_bilinear_tx, sample_uv, lod); + } + else { + pair_data[i].color = textureLod(color_tx, sample_uv, lod); + } + pair_data[i].coc = dof_load_gather_coc(coc_tx, sample_uv, lod); + pair_data[i].dist = ring_radius; + } + + dof_gather_accumulate_sample_pair(pair_data, + bordering_radius, + isect_mul, + first_ring, + do_fast_gather, + is_foreground, + ring_data, + accum_data); + } + + if (is_foreground) { + /* Reduce issue with closer foreground over distant foreground. */ + /* TODO(fclem) this seems to not be completely correct as the issue remains. */ + float ring_area = (sqr(float(ring) + 0.5 + coc_radius_error) - + sqr(float(ring) - 0.5 + coc_radius_error)) * + sqr(base_radius * unit_sample_radius); + dof_gather_ammend_weight(ring_data, ring_area); + } + + dof_gather_accumulate_sample_ring( + ring_data, sample_pair_count * 2, first_ring, do_fast_gather, is_foreground, accum_data); + + first_ring = false; + + if (do_density_change && (ring == change_density_at_ring) && + (density_change < gather_max_density_change)) { + if (dof_do_density_change(base_radius, min_intersectable_radius)) { + base_radius *= radius_downscale_factor; + ring += gather_density_change_ring; + /* We need to account for the density change in the weights (slide 62). + * For that multiply old kernel data by its area divided by the new kernel area. */ + const float outer_rings_weight = 1.0 / (radius_downscale_factor * radius_downscale_factor); + /* Samples are already weighted per ring in foreground pass. */ + if (!is_foreground) { + dof_gather_ammend_weight(accum_data, outer_rings_weight); + } + /* Re-init kernel position & sampling parameters. */ + dof_gather_init(dof, base_radius, noise, center_co, lod, isect_mul); + density_change++; + } + } + } + + { + /* Center sample. */ + vec2 sample_uv = center_co * dof.gather_uv_fac; + DofGatherData center_data; + if (do_fast_gather) { + center_data.color = textureLod(color_bilinear_tx, sample_uv, lod); + } + else { + center_data.color = textureLod(color_tx, sample_uv, lod); + } + center_data.coc = dof_load_gather_coc(coc_tx, sample_uv, lod); + center_data.dist = 0.0; + + /* Slide 38. */ + float bordering_radius = (0.5 + coc_radius_error) * base_radius * unit_sample_radius; + + dof_gather_accumulate_center_sample( + center_data, bordering_radius, 0, do_fast_gather, is_foreground, false, accum_data); + } + + int total_sample_count = dof_gather_total_sample_count_with_density_change( + gather_ring_count, gather_ring_density, density_change); + dof_gather_accumulate_resolve( + total_sample_count, accum_data, out_color, out_weight, out_occlusion); + + if (debug_gather_perf && density_change > 0) { + float fac = saturate(float(density_change) / float(10.0)); + out_color.rgb = avg(out_color.rgb) * neon_gradient(fac); + } + if (debug_gather_perf && do_fast_gather) { + out_color.rgb = avg(out_color.rgb) * vec3(0.0, 1.0, 0.0); + } + if (debug_scatter_perf) { + out_color.rgb = avg(out_color.rgb) * vec3(0.0, 1.0, 0.0); + } + + /* Output premultiplied color so we can use bilinear sampler in resolve pass. */ + out_color *= out_weight; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Slight focus accumulator. + * + * The full pixel neighborhood is gathered. + * \{ */ + +void dof_slight_focus_gather(SamplingData sampling, + DepthOfFieldData dof, + sampler2D depth_tx, + sampler2D color_tx, + sampler2D bokeh_lut_tx, + float radius, + out vec4 out_color, + out float out_weight) +{ + float noise_offset = sampling_rng_1D_get(sampling, SAMPLING_LENS_U); + float noise = no_gather_random ? 0.0 : + interlieved_gradient_noise(gl_FragCoord.xy, 3, noise_offset); + + DofGatherData fg_accum = GATHER_DATA_INIT; + DofGatherData bg_accum = GATHER_DATA_INIT; + + int i_radius = clamp(int(radius), 0, int(dof_layer_threshold)); + const int resolve_ring_density = DOF_SLIGHT_FOCUS_DENSITY; + ivec2 texel = ivec2(gl_FragCoord.xy); + + bool first_ring = true; + + for (int ring = i_radius; ring > 0; ring--) { + DofGatherData fg_ring = GATHER_DATA_INIT; + DofGatherData bg_ring = GATHER_DATA_INIT; + + int ring_distance = ring; + int ring_sample_count = resolve_ring_density * ring_distance; + for (int sample_id = 0; sample_id < ring_sample_count; sample_id++) { + int s = sample_id * (4 / resolve_ring_density) + + int(noise * float((4 - resolve_ring_density) * ring_distance)); + + ivec2 offset = dof_square_ring_sample_offset(ring_distance, s); + float ring_dist = length(vec2(offset)); + + DofGatherData pair_data[2]; + for (int i = 0; i < 2; i++) { + ivec2 sample_offset = ((i == 0) ? offset : -offset); + ivec2 sample_texel = texel + sample_offset; + /* OPTI: could precompute the factor. */ + vec2 sample_uv = (vec2(sample_texel) + 0.5) / vec2(textureSize(depth_tx, 0)); + float depth = textureLod(depth_tx, sample_uv, 0.0).r; + pair_data[i].coc = dof_coc_from_depth(dof, sample_uv, depth); + pair_data[i].color = safe_color(textureLod(color_tx, sample_uv, 0.0)); + pair_data[i].dist = ring_dist; + if (DOF_BOKEH_TEXTURE) { + /* Contains subpixel distance to bokeh shape. */ + sample_offset += dof_max_slight_focus_radius; + pair_data[i].dist = texelFetch(bokeh_lut_tx, sample_offset, 0).r; + } + pair_data[i].coc = clamp(pair_data[i].coc, -dof.coc_abs_max, dof.coc_abs_max); + } + + float bordering_radius = ring_dist + 0.5; + const float isect_mul = 1.0; + dof_gather_accumulate_sample_pair( + pair_data, bordering_radius, isect_mul, first_ring, false, false, bg_ring, bg_accum); + + if (DOF_BOKEH_TEXTURE) { + /* Swap distances in order to flip bokeh shape for foreground. */ + float tmp = pair_data[0].dist; + pair_data[0].dist = pair_data[1].dist; + pair_data[1].dist = tmp; + } + dof_gather_accumulate_sample_pair( + pair_data, bordering_radius, isect_mul, first_ring, false, true, fg_ring, fg_accum); + } + + dof_gather_accumulate_sample_ring( + bg_ring, ring_sample_count * 2, first_ring, false, false, bg_accum); + dof_gather_accumulate_sample_ring( + fg_ring, ring_sample_count * 2, first_ring, false, true, fg_accum); + + first_ring = false; + } + + /* Center sample. */ + vec2 sample_uv = gl_FragCoord.xy / vec2(textureSize(depth_tx, 0)); + DofGatherData center_data; + center_data.color = safe_color(textureLod(color_tx, sample_uv, 0.0)); + center_data.coc = dof_coc_from_depth(dof, sample_uv, textureLod(depth_tx, sample_uv, 0.0).r); + center_data.coc = clamp(center_data.coc, -dof.coc_abs_max, dof.coc_abs_max); + center_data.dist = 0.0; + + /* Slide 38. */ + float bordering_radius = 0.5; + + dof_gather_accumulate_center_sample( + center_data, bordering_radius, i_radius, false, true, true, fg_accum); + dof_gather_accumulate_center_sample( + center_data, bordering_radius, i_radius, false, false, true, bg_accum); + + vec4 bg_col, fg_col; + float bg_weight, fg_weight; + vec2 unused_occlusion; + + int total_sample_count = dof_gather_total_sample_count(i_radius + 1, resolve_ring_density); + dof_gather_accumulate_resolve(total_sample_count, bg_accum, bg_col, bg_weight, unused_occlusion); + dof_gather_accumulate_resolve(total_sample_count, fg_accum, fg_col, fg_weight, unused_occlusion); + + /* Fix weighting issues on perfectly focus > slight focus transitionning areas. */ + if (abs(center_data.coc) < 0.5) { + bg_col = center_data.color; + bg_weight = 1.0; + } + + /* Alpha Over */ + float alpha = 1.0 - fg_weight; + out_weight = bg_weight * alpha + fg_weight; + out_color = bg_col * bg_weight * alpha + fg_col * fg_weight; +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_bokeh_lut_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_bokeh_lut_frag.glsl new file mode 100644 index 00000000000..9ab450064db --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_bokeh_lut_frag.glsl @@ -0,0 +1,63 @@ + +/** + * Bokeh Look Up Table: This outputs a radius multiplier to shape the sampling in gather pass or + * the scatter sprite appearance. This is only used if bokeh shape is either anamorphic or is not + * a perfect circle. + * We correct samples spacing for polygonal bokeh shapes. However, we do not for anamorphic bokeh + * as it is way more complex and expensive to do. + **/ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) + +layout(std140) uniform dof_block +{ + DepthOfFieldData dof; +}; + +in vec4 uvcoordsvar; + +layout(location = 0) out vec2 out_gather_lut; +layout(location = 1) out float out_scatter_Lut; +layout(location = 2) out float outResolveLut; + +void main() +{ + /* Center uv in range [-1..1]. */ + vec2 uv = uvcoordsvar.xy * 2.0 - 1.0; + + float radius = length(uv); + + vec2 texel = floor(gl_FragCoord.xy) - float(dof_max_slight_focus_radius); + + if (dof.bokeh_blades > 0.0) { + /* NOTE: atan(y,x) has output range [-M_PI..M_PI], so add 2pi to avoid negative angles. */ + float theta = atan(uv.y, uv.x) + M_2PI; + float r = length(uv); + + radius /= circle_to_polygon_radius(dof.bokeh_blades, theta - dof.bokeh_rotation); + + float theta_new = circle_to_polygon_angle(dof.bokeh_blades, theta); + float r_new = circle_to_polygon_radius(dof.bokeh_blades, theta_new); + + theta_new -= dof.bokeh_rotation; + + uv = r_new * vec2(-cos(theta_new), sin(theta_new)); + + { + /* Slight focus distance */ + texel *= dof.bokeh_anisotropic_scale_inv; + float theta = atan(texel.y, -texel.x) + M_2PI; + texel /= circle_to_polygon_radius(dof.bokeh_blades, theta + dof.bokeh_rotation); + } + } + else { + uv *= safe_rcp(length(uv)); + } + + /* For gather store the normalized UV. */ + out_gather_lut = uv; + /* For scatter store distance. */ + out_scatter_Lut = radius; + /* For slight focus gather store pixel perfect distance. */ + outResolveLut = length(texel); +} diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_filter_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_filter_frag.glsl index aa72cda4fb7..70f3a4b9aa7 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_filter_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_filter_frag.glsl @@ -6,15 +6,15 @@ * cheaper. */ -#pragma BLENDER_REQUIRE(effect_dof_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) -uniform sampler2D colorBuffer; -uniform sampler2D weightBuffer; +uniform sampler2D color_tx; +uniform sampler2D weight_tx; in vec4 uvcoordsvar; -layout(location = 0) out vec4 outColor; -layout(location = 1) out float outWeight; +layout(location = 0) out vec4 out_color; +layout(location = 1) out float out_weight; /* From: * Implementing Median Filters in XC4000E FPGAs @@ -88,6 +88,6 @@ void main() { /* OPTI(fclem) Could early return on some tiles. */ - outColor = median_filter(colorBuffer, uvcoordsvar.xy); - outWeight = median_filter(weightBuffer, uvcoordsvar.xy).r; + out_color = median_filter(color_tx, uvcoordsvar.xy); + out_weight = median_filter(weight_tx, uvcoordsvar.xy).r; } diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_gather_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_gather_frag.glsl new file mode 100644 index 00000000000..03768a62ab9 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_gather_frag.glsl @@ -0,0 +1,118 @@ + +/** + * Gather pass: Convolve foreground and background parts in separate passes. + * + * Using the min&max CoC tile buffer, we select the best apropriate method to blur the scene color. + * A fast gather path is taken if there is not many CoC variation inside the tile. + * + * We sample using an octaweb sampling pattern. We randomize the kernel center and each ring + * rotation to ensure maximum coverage. + * + * Outputs: + * - Color * Weight, Weight, Occlusion 'CoC' Depth (mean and variance) + **/ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_accumulator_lib.glsl) + +layout(std140) uniform sampling_block +{ + SamplingData sampling; +}; + +layout(std140) uniform dof_block +{ + DepthOfFieldData dof; +}; + +uniform sampler2D color_tx; +uniform sampler2D color_bilinear_tx; +uniform sampler2D coc_tx; +uniform sampler2D tiles_fg_tx; +uniform sampler2D tiles_bg_tx; +uniform sampler2D bokeh_lut_tx; + +layout(location = 0) out vec4 out_color; +layout(location = 1) out float out_weight; +layout(location = 2) out vec2 out_occlusion; + +void main() +{ + ivec2 tile_co = ivec2(gl_FragCoord.xy / float(DOF_TILE_DIVISOR / 2)); + CocTile coc_tile = dof_coc_tile_load(tiles_fg_tx, tiles_bg_tx, tile_co); + CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile); + + float base_radius, min_radius, min_intersectable_radius; + bool can_early_out; + if (is_foreground) { + base_radius = -coc_tile.fg_min_coc; + min_radius = -coc_tile.fg_max_coc; + min_intersectable_radius = -coc_tile.fg_max_intersectable_coc; + can_early_out = !prediction.do_foreground; + } + else { + base_radius = coc_tile.bg_max_coc; + min_radius = coc_tile.bg_min_coc; + min_intersectable_radius = coc_tile.bg_min_intersectable_coc; + can_early_out = !prediction.do_background; + } + + bool do_fast_gather = dof_do_fast_gather(base_radius, min_radius, is_foreground); + + /* Gather at half resolution. Divide CoC by 2. */ + base_radius *= 0.5; + min_intersectable_radius *= 0.5; + + bool do_density_change = dof_do_density_change(base_radius, min_intersectable_radius); + + if (can_early_out) { + /* Early out. */ + out_color = vec4(0.0); + out_weight = 0.0; + out_occlusion = vec2(0.0, 0.0); + } + else if (do_fast_gather) { + dof_gather_accumulator(sampling, + dof, + color_tx, + color_bilinear_tx, + coc_tx, + bokeh_lut_tx, + base_radius, + min_intersectable_radius, + true, + false, + out_color, + out_weight, + out_occlusion); + } + else if (do_density_change) { + dof_gather_accumulator(sampling, + dof, + color_tx, + color_bilinear_tx, + coc_tx, + bokeh_lut_tx, + base_radius, + min_intersectable_radius, + false, + true, + out_color, + out_weight, + out_occlusion); + } + else { + dof_gather_accumulator(sampling, + dof, + color_tx, + color_bilinear_tx, + coc_tx, + bokeh_lut_tx, + base_radius, + min_intersectable_radius, + false, + false, + out_color, + out_weight, + out_occlusion); + } +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_gather_holefill_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_gather_holefill_frag.glsl new file mode 100644 index 00000000000..df6ff9f6792 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_gather_holefill_frag.glsl @@ -0,0 +1,89 @@ + +/** + * Holefill pass: Gather background parts where foreground is present. + * + * Using the min&max CoC tile buffer, we select the best apropriate method to blur the scene color. + * A fast gather path is taken if there is not many CoC variation inside the tile. + * + * We sample using an octaweb sampling pattern. We randomize the kernel center and each ring + * rotation to ensure maximum coverage. + **/ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_accumulator_lib.glsl) + +layout(std140) uniform sampling_block +{ + SamplingData sampling; +}; + +layout(std140) uniform dof_block +{ + DepthOfFieldData dof; +}; + +uniform sampler2D color_tx; +uniform sampler2D color_bilinear_tx; +uniform sampler2D coc_tx; +uniform sampler2D tiles_fg_tx; +uniform sampler2D tiles_bg_tx; + +layout(location = 0) out vec4 out_color; +layout(location = 1) out float out_weight; + +void main() +{ + ivec2 tile_co = ivec2(gl_FragCoord.xy / float(DOF_TILE_DIVISOR / 2)); + CocTile coc_tile = dof_coc_tile_load(tiles_fg_tx, tiles_bg_tx, tile_co); + CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile); + + float base_radius = -coc_tile.fg_min_coc; + float min_radius = -coc_tile.fg_max_coc; + float min_intersectable_radius = dof_tile_large_coc; + bool can_early_out = !prediction.do_holefill; + + bool do_fast_gather = dof_do_fast_gather(base_radius, min_radius, is_foreground); + + /* Gather at half resolution. Divide CoC by 2. */ + base_radius *= 0.5; + min_intersectable_radius *= 0.5; + + bool do_density_change = dof_do_density_change(base_radius, min_intersectable_radius); + + if (can_early_out) { + /* Early out. */ + out_color = vec4(0.0); + out_weight = 0.0; + } + else if (do_fast_gather) { + vec2 unused_occlusion; + dof_gather_accumulator(sampling, + dof, + color_tx, + color_bilinear_tx, + coc_tx, + coc_tx, + base_radius, + min_intersectable_radius, + true, + false, + out_color, + out_weight, + unused_occlusion); + } + else { + vec2 unused_occlusion; + dof_gather_accumulator(sampling, + dof, + color_tx, + color_bilinear_tx, + coc_tx, + coc_tx, + base_radius, + min_intersectable_radius, + false, + false, + out_color, + out_weight, + unused_occlusion); + } +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_lib.glsl new file mode 100644 index 00000000000..0cc36dd0d03 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_lib.glsl @@ -0,0 +1,328 @@ +/** + * Depth of Field utils. + **/ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +/* -------------------------------------------------------------------- */ +/** \name Constants. + * \{ */ + +#ifndef DOF_SLIGHT_FOCUS_DENSITY +# define DOF_SLIGHT_FOCUS_DENSITY 2 +#endif + +#ifdef DOF_RESOLVE_PASS +const bool is_resolve = true; +#else +const bool is_resolve = false; +#endif +#ifdef DOF_FOREGROUND_PASS +const bool is_foreground = DOF_FOREGROUND_PASS; +#else +const bool is_foreground = false; +#endif +/* Debug options */ +const bool debug_gather_perf = false; +const bool debug_scatter_perf = false; +const bool debug_resolve_perf = false; + +const bool no_smooth_intersection = false; +const bool no_gather_occlusion = false; +const bool no_gather_mipmaps = false; +const bool no_gather_random = false; +const bool no_gather_filtering = false; +const bool no_scatter_occlusion = false; +const bool no_scatter_pass = false; +const bool no_foreground_pass = false; +const bool no_background_pass = false; +const bool no_slight_focus_pass = false; +const bool no_focus_pass = false; +const bool no_holefill_pass = false; + +/* Distribute weights between near/slightfocus/far fields (slide 117). */ +const float dof_layer_threshold = 4.0; +/* Make sure it overlaps. */ +const float dof_layer_offset_fg = 0.5 + 1.0; +/* Extra offset for convolution layers to avoid light leaking from background. */ +const float dof_layer_offset = 0.5 + 0.5; + +const int dof_max_slight_focus_radius = 5; + +const vec2 quad_offsets[4] = vec2[4]( + vec2(-0.5, 0.5), vec2(0.5, 0.5), vec2(0.5, -0.5), vec2(-0.5, -0.5)); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Weighting and downsampling utils. + * \{ */ + +float dof_hdr_color_weight(vec4 color) +{ + /* Very fast "luma" weighting. */ + float luma = (color.g * 2.0) + (color.r + color.b); + /* TODO(fclem) Pass correct exposure. */ + const float exposure = 1.0; + return 1.0 / (luma * exposure + 4.0); +} + +float dof_coc_select(vec4 cocs) +{ + /* Select biggest coc. */ + float selected_coc = cocs.x; + if (abs(cocs.y) > abs(selected_coc)) { + selected_coc = cocs.y; + } + if (abs(cocs.z) > abs(selected_coc)) { + selected_coc = cocs.z; + } + if (abs(cocs.w) > abs(selected_coc)) { + selected_coc = cocs.w; + } + return selected_coc; +} + +/* NOTE: Do not forget to normalize weights afterwards. */ +vec4 dof_bilateral_coc_weights(vec4 cocs) +{ + float chosen_coc = dof_coc_select(cocs); + + const float scale = 4.0; /* TODO(fclem) revisit. */ + /* NOTE: The difference between the cocs should be inside a abs() function, + * but we follow UE4 implementation to improve how dithered transparency looks (see slide 19). */ + return saturate(1.0 - (chosen_coc - cocs) * scale); +} + +/* NOTE: Do not forget to normalize weights afterwards. */ +vec4 dof_bilateral_color_weights(vec4 colors[4]) +{ + vec4 weights; + for (int i = 0; i < 4; i++) { + weights[i] = dof_hdr_color_weight(colors[i]); + } + return weights; +} + +/* Returns signed Circle of confusion radius (in pixel) based on depth buffer value [0..1]. */ +float dof_coc_from_depth(DepthOfFieldData dof, vec2 uv, float depth) +{ + if (is_panoramic(dof.camera_type)) { + /* Use radial depth. */ + depth = -length(get_view_space_from_depth(uv, depth)); + } + else { + depth = get_view_z_from_depth(depth); + } + return coc_radius_from_camera_depth(dof, depth); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Gather & Scatter Weighting + * \{ */ + +float dof_layer_weight(float coc, const bool is_foreground) +{ + /* NOTE: These are fullres pixel CoC value. */ + if (is_resolve) { + return saturate(-abs(coc) + dof_layer_threshold + dof_layer_offset) * + float(is_foreground ? (coc <= 0.5) : (coc > -0.5)); + } + else { + coc *= 2.0; /* Account for half pixel gather. */ + float threshold = dof_layer_threshold - + ((is_foreground) ? dof_layer_offset_fg : dof_layer_offset); + return saturate(((is_foreground) ? -coc : coc) - threshold); + } +} +vec4 dof_layer_weight(vec4 coc) +{ + /* NOTE: Used for scatter pass which already flipped the sign correctly. */ + coc *= 2.0; /* Account for half pixel gather. */ + return saturate(coc - dof_layer_threshold + dof_layer_offset); +} + +/* NOTE: This is halfres CoC radius. */ +float dof_sample_weight(float coc) +{ + /* Full intensity if CoC radius is below the pixel footprint. */ + const float min_coc = 1.0; + coc = max(min_coc, abs(coc)); + return (M_PI * min_coc * min_coc) / (M_PI * coc * coc); +} +vec4 dof_sample_weight(vec4 coc) +{ + /* Full intensity if CoC radius is below the pixel footprint. */ + const float min_coc = 1.0; + coc = max(vec4(min_coc), abs(coc)); + return (M_PI * min_coc * min_coc) / (M_PI * coc * coc); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Circle of Confusion tiles + * \{ */ + +struct CocTile { + float fg_min_coc; + float fg_max_coc; + float fg_max_intersectable_coc; + float fg_slight_focus_max_coc; + float bg_min_coc; + float bg_max_coc; + float bg_min_intersectable_coc; +}; + +struct CocTilePrediction { + bool do_foreground; + bool do_slight_focus; + bool do_focus; + bool do_background; + bool do_holefill; +}; + +/* WATCH: Might have to change depending on the texture format. */ +const float dof_tile_defocus = 0.25; +const float dof_tile_focus = 0.0; +const float dof_tile_mixed = 0.75; +const float dof_tile_large_coc = 1024.0; + +/* Init a CoC tile for reduction algorithms. */ +CocTile dof_coc_tile_init(void) +{ + CocTile tile; + tile.fg_min_coc = 0.0; + tile.fg_max_coc = -dof_tile_large_coc; + tile.fg_max_intersectable_coc = dof_tile_large_coc; + tile.fg_slight_focus_max_coc = -1.0; + tile.bg_min_coc = dof_tile_large_coc; + tile.bg_max_coc = 0.0; + tile.bg_min_intersectable_coc = dof_tile_large_coc; + return tile; +} + +CocTile dof_coc_tile_load(sampler2D fg_buffer, sampler2D bg_buffer, ivec2 tile_co) +{ + ivec2 tex_size = textureSize(fg_buffer, 0).xy; + tile_co = clamp(tile_co, ivec2(0), tex_size - 1); + + vec4 fg = texelFetch(fg_buffer, tile_co, 0); + vec3 bg = texelFetch(bg_buffer, tile_co, 0).xyz; + + CocTile tile; + tile.fg_min_coc = -fg.x; + tile.fg_max_coc = -fg.y; + tile.fg_max_intersectable_coc = -fg.z; + tile.fg_slight_focus_max_coc = fg.w; + tile.bg_min_coc = bg.x; + tile.bg_max_coc = bg.y; + tile.bg_min_intersectable_coc = bg.z; + return tile; +} + +void dof_coc_tile_store(CocTile tile, out vec4 out_fg, out vec3 out_bg) +{ + out_fg.x = -tile.fg_min_coc; + out_fg.y = -tile.fg_max_coc; + out_fg.z = -tile.fg_max_intersectable_coc; + out_fg.w = tile.fg_slight_focus_max_coc; + out_bg.x = tile.bg_min_coc; + out_bg.y = tile.bg_max_coc; + out_bg.z = tile.bg_min_intersectable_coc; +} + +bool dof_do_fast_gather(float max_absolute_coc, float min_absolute_coc, const bool is_foreground) +{ + float min_weight = dof_layer_weight((is_foreground) ? -min_absolute_coc : min_absolute_coc, + is_foreground); + if (min_weight < 1.0) { + return false; + } + /* FIXME(fclem): This is a workaround to fast gather triggering too early. Since we use custom + * opacity mask, the opacity is not given to be 100% even for after normal threshold. */ + if (is_foreground && min_absolute_coc < dof_layer_threshold) { + return false; + } + return (max_absolute_coc - min_absolute_coc) < (DOF_FAST_GATHER_COC_ERROR * max_absolute_coc); +} + +CocTilePrediction dof_coc_tile_prediction_get(CocTile tile) +{ + /* Based on tile value, predict what pass we need to load. */ + CocTilePrediction predict; + + predict.do_foreground = (-tile.fg_min_coc > dof_layer_threshold - dof_layer_offset_fg); + bool fg_fully_opaque = predict.do_foreground && + dof_do_fast_gather(-tile.fg_min_coc, -tile.fg_max_coc, true); + + predict.do_slight_focus = !fg_fully_opaque && (tile.fg_slight_focus_max_coc >= 0.5); + predict.do_focus = !fg_fully_opaque && (tile.fg_slight_focus_max_coc == dof_tile_focus); + + predict.do_background = !predict.do_focus && !fg_fully_opaque && + (tile.bg_max_coc > dof_layer_threshold - dof_layer_offset); + bool bg_fully_opaque = predict.do_background && + dof_do_fast_gather(-tile.bg_max_coc, tile.bg_min_coc, false); + predict.do_holefill = !predict.do_focus && !fg_fully_opaque && -tile.fg_max_coc > 0.0; + +#if 0 /* Debug */ + predict.do_foreground = predict.do_background = predict.do_holefill = true; +#endif + return predict; +} + +/* Special function to return the correct max value of 2 slight focus coc. */ +float dof_coc_max_slight_focus(float coc1, float coc2) +{ + /* Do not consider values below 0.5 for expansion as they are "encoded". + * See setup pass shader for more infos. */ + if ((coc1 == dof_tile_defocus && coc2 == dof_tile_focus) || + (coc1 == dof_tile_focus && coc2 == dof_tile_defocus)) { + /* Tile where completely out of focus and in focus are both present. + * Consider as very slightly out of focus. */ + return dof_tile_mixed; + } + return max(coc1, coc2); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Gathering + * \{ */ + +/** + * Generate samples in a square pattern with the ring radius. X is the center tile. + * + * Dist1 Dist2 + * 6 5 4 3 2 + * 3 2 1 7 1 + * . X 0 . X 0 + * . . . . . + * . . . . . + * + * Samples are expected to be mirrored to complete the pattern. + **/ +ivec2 dof_square_ring_sample_offset(int ring_distance, int sample_id) +{ + ivec2 offset; + if (sample_id < ring_distance) { + offset.x = ring_distance; + offset.y = sample_id; + } + else if (sample_id < ring_distance * 3) { + offset.x = ring_distance - sample_id + ring_distance; + offset.y = ring_distance; + } + else { + offset.x = -ring_distance; + offset.y = ring_distance - sample_id + 3 * ring_distance; + } + return offset; +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_copy_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_copy_frag.glsl new file mode 100644 index 00000000000..420476f6f37 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_copy_frag.glsl @@ -0,0 +1,144 @@ + +/** + * Reduce copy pass: filter fireflies and split color between scatter and gather input. + * + * NOTE: The texture can end up being too big because of the mipmap padding. We correct for + * that during the convolution phase. + * + * Inputs: + * - Output of setup pass (halfres) and reduce downsample pass (quarter res). + * Outputs: + * - Halfres padded to avoid mipmap mis-alignment (so possibly not matching input size). + * - Gather input color (mip 0), Scatter input color, Signed CoC. + **/ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) + +layout(std140) uniform dof_block +{ + DepthOfFieldData dof; +}; + +uniform sampler2D color_tx; +uniform sampler2D coc_tx; +uniform sampler2D downsampled_tx; + +layout(location = 0) out vec4 out_color_gather; +layout(location = 1) out float out_coc; +layout(location = 2) out vec3 out_color_scatter; + +/* NOTE: Do not compare alpha as it is not scattered by the scatter pass. */ +float dof_scatter_neighborhood_rejection(vec3 color) +{ + color = min(vec3(dof.scatter_neighbor_max_color), color); + + float validity = 0.0; + + /* Centered in the middle of 4 quarter res texel. */ + vec2 texel_size = 1.0 / vec2(textureSize(downsampled_tx, 0).xy); + vec2 uv = (gl_FragCoord.xy * 0.5) * texel_size; + + vec3 max_diff = vec3(0.0); + for (int i = 0; i < 4; i++) { + vec2 sample_uv = uv + quad_offsets[i] * texel_size; + vec3 ref = textureLod(downsampled_tx, sample_uv, 0.0).rgb; + + ref = min(vec3(dof.scatter_neighbor_max_color), ref); + float diff = max_v3(max(vec3(0.0), abs(ref - color))); + + const float rejection_threshold = 0.7; + diff = saturate(diff / rejection_threshold - 1.0); + validity = max(validity, diff); + } + + return validity; +} + +/* This avoids sprite popping in and out at the screen border and + * drawing sprites larger than the screen. */ +float dof_scatter_screen_border_rejection(float coc, vec2 uv, vec2 screen_size) +{ + vec2 screen_pos = uv * screen_size; + float min_screen_border_distance = min_v2(min(screen_pos, screen_size - screen_pos)); + /* Fullres to halfres CoC. */ + coc *= 0.5; + /* Allow 10px transition. */ + const float rejection_hardeness = 1.0 / 10.0; + return saturate((min_screen_border_distance - abs(coc)) * rejection_hardeness + 1.0); +} + +float dof_scatter_luminosity_rejection(vec3 color) +{ + const float rejection_hardness = 1.0; + return saturate(max_v3(color - dof.scatter_color_threshold) * rejection_hardness); +} + +float dof_scatter_coc_radius_rejection(float coc) +{ + const float rejection_hardness = 0.3; + return saturate((abs(coc) - dof.scatter_coc_threshold) * rejection_hardness); +} + +float fast_luma(vec3 color) +{ + return (2.0 * color.g) + color.r + color.b; +} + +/* Lightweight version of neighborhood clamping found in TAA. */ +vec3 dof_neighborhood_clamping(vec3 color) +{ + vec2 texel_size = 1.0 / vec2(textureSize(color_tx, 0)); + vec2 uv = gl_FragCoord.xy * texel_size; + vec4 ofs = vec4(-1, 1, -1, 1) * texel_size.xxyy; + + /* Luma clamping. 3x3 square neighborhood. */ + float c00 = fast_luma(textureLod(color_tx, uv + ofs.xz, 0.0).rgb); + float c01 = fast_luma(textureLod(color_tx, uv + ofs.xz * vec2(1.0, 0.0), 0.0).rgb); + float c02 = fast_luma(textureLod(color_tx, uv + ofs.xw, 0.0).rgb); + + float c10 = fast_luma(textureLod(color_tx, uv + ofs.xz * vec2(0.0, 1.0), 0.0).rgb); + float c11 = fast_luma(color); + float c12 = fast_luma(textureLod(color_tx, uv + ofs.xw * vec2(0.0, 1.0), 0.0).rgb); + + float c20 = fast_luma(textureLod(color_tx, uv + ofs.yz, 0.0).rgb); + float c21 = fast_luma(textureLod(color_tx, uv + ofs.yz * vec2(1.0, 0.0), 0.0).rgb); + float c22 = fast_luma(textureLod(color_tx, uv + ofs.yw, 0.0).rgb); + + float avg_luma = avg8(c00, c01, c02, c10, c12, c20, c21, c22); + float max_luma = max8(c00, c01, c02, c10, c12, c20, c21, c22); + + float upper_bound = mix(max_luma, avg_luma, dof.denoise_factor); + upper_bound = mix(c11, upper_bound, dof.denoise_factor); + + float clamped_luma = min(upper_bound, c11); + + return color * clamped_luma * safe_rcp(c11); +} + +void main() +{ + vec2 halfres = vec2(textureSize(color_tx, 0).xy); + vec2 uv = gl_FragCoord.xy / halfres; + + out_color_gather = textureLod(color_tx, uv, 0.0); + out_coc = textureLod(coc_tx, uv, 0.0).r; + + out_color_gather.rgb = dof_neighborhood_clamping(out_color_gather.rgb); + + /* Only scatter if luminous enough. */ + float do_scatter = dof_scatter_luminosity_rejection(out_color_gather.rgb); + /* Only scatter if CoC is big enough. */ + do_scatter *= dof_scatter_coc_radius_rejection(out_coc); + /* Only scatter if CoC is not too big to avoid performance issues. */ + do_scatter *= dof_scatter_screen_border_rejection(out_coc, uv, halfres); + /* Only scatter if neighborhood is different enough. */ + do_scatter *= dof_scatter_neighborhood_rejection(out_color_gather.rgb); + /* For debuging. */ + do_scatter *= float(!no_scatter_pass); + + out_color_scatter = mix(vec3(0.0), out_color_gather.rgb, do_scatter); + out_color_gather.rgb = mix(out_color_gather.rgb, vec3(0.0), do_scatter); + + /* Apply energy conservation to anamorphic scattered bokeh. */ + out_color_scatter *= max_v2(dof.bokeh_anisotropic_scale_inv); +} diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_downsample_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_downsample_frag.glsl index c33eda0acd2..f255400ac97 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_downsample_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_downsample_frag.glsl @@ -6,18 +6,16 @@ * weight luma for the bilateral weights. */ -#pragma BLENDER_REQUIRE(effect_dof_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) -/* Half resolution. */ -uniform sampler2D colorBuffer; -uniform sampler2D cocBuffer; +uniform sampler2D color_tx; +uniform sampler2D coc_tx; -/* Quarter resolution. */ -layout(location = 0) out vec4 outColor; +layout(location = 0) out vec4 out_color; void main() { - vec2 halfres_texel_size = 1.0 / vec2(textureSize(colorBuffer, 0).xy); + vec2 halfres_texel_size = 1.0 / vec2(textureSize(color_tx, 0).xy); /* Center uv around the 4 halfres pixels. */ vec2 quad_center = (floor(gl_FragCoord.xy) * 2.0 + 1.0) * halfres_texel_size; @@ -25,13 +23,13 @@ void main() vec4 cocs; for (int i = 0; i < 4; i++) { vec2 sample_uv = quad_center + quad_offsets[i] * halfres_texel_size; - colors[i] = textureLod(colorBuffer, sample_uv, 0.0); - cocs[i] = textureLod(cocBuffer, sample_uv, 0.0).r; + colors[i] = textureLod(color_tx, sample_uv, 0.0); + cocs[i] = textureLod(coc_tx, sample_uv, 0.0).r; } - vec4 weights = dof_downsample_bilateral_coc_weights(cocs); + vec4 weights = dof_bilateral_coc_weights(cocs); /* Normalize so that the sum is 1. */ weights *= safe_rcp(sum(weights)); - outColor = weighted_sum_array(colors, weights); + out_color = weighted_sum_array(colors, weights); } diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_recursive_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_recursive_frag.glsl new file mode 100644 index 00000000000..fc19d9cec29 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_reduce_recursive_frag.glsl @@ -0,0 +1,36 @@ + +/** + * Reduce recursive pass: Simple coc & luma aware downsampling pass to generate mipmaps. + **/ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) + +uniform sampler2D color_tx; +uniform sampler2D coc_tx; + +layout(location = 0) out vec4 out_color; +layout(location = 1) out float out_coc; + +/* Downsample pass done for each mip starting from mip1. */ +void main() +{ + vec2 input_texel_size = 1.0 / vec2(textureSize(color_tx, 0).xy); + /* Center uv around the 4 pixels of the previous mip. */ + vec2 quad_center = (floor(gl_FragCoord.xy) * 2.0 + 1.0) * input_texel_size; + + vec4 colors[4]; + vec4 cocs; + for (int i = 0; i < 4; i++) { + vec2 sample_uv = quad_center + quad_offsets[i] * input_texel_size; + colors[i] = textureLod(color_tx, sample_uv, 0.0); + cocs[i] = textureLod(coc_tx, sample_uv, 0.0).r; + } + + vec4 weights = dof_bilateral_coc_weights(cocs); + weights *= dof_bilateral_color_weights(colors); + /* Normalize so that the sum is 1. */ + weights *= safe_rcp(sum(weights)); + + out_color = weighted_sum_array(colors, weights); + out_coc = dot(cocs, weights); +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_resolve_frag.glsl new file mode 100644 index 00000000000..a6569d14090 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_resolve_frag.glsl @@ -0,0 +1,111 @@ + +/** + * Recombine Pass: Load separate convolution layer and composite with self + * slight defocus convolution and in-focus fields. + * + * The halfres gather methods are fast but lack precision for small CoC areas. + * To fix this we do a bruteforce gather to have a smooth transition between + * in-focus and defocus regions. + */ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_accumulator_lib.glsl) + +layout(std140) uniform sampling_block +{ + SamplingData sampling; +}; + +layout(std140) uniform dof_block +{ + DepthOfFieldData dof; +}; + +uniform sampler2D depth_tx; +uniform sampler2D color_tx; +uniform sampler2D color_bg_tx; +uniform sampler2D color_fg_tx; +uniform sampler2D color_holefill_tx; +uniform sampler2D tiles_bg_tx; +uniform sampler2D tiles_fg_tx; +uniform sampler2D weight_bg_tx; +uniform sampler2D weight_fg_tx; +uniform sampler2D weight_holefill_tx; +uniform sampler2D bokeh_lut_tx; + +in vec4 uvcoordsvar; + +layout(location = 0) out vec4 out_color; + +void main(void) +{ + ivec2 tile_co = ivec2(gl_FragCoord.xy / float(DOF_TILE_DIVISOR)); + CocTile coc_tile = dof_coc_tile_load(tiles_fg_tx, tiles_bg_tx, tile_co); + CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile); + + out_color = vec4(0.0); + float weight = 0.0; + + vec4 layer_color; + float layer_weight; + + vec2 uv_halfres = gl_FragCoord.xy / (2.0 * vec2(textureSize(color_bg_tx, 0))); + + if (!no_holefill_pass && prediction.do_holefill) { + layer_color = textureLod(color_holefill_tx, uv_halfres, 0.0); + layer_weight = textureLod(weight_holefill_tx, uv_halfres, 0.0).r; + out_color = layer_color * safe_rcp(layer_weight); + weight = float(layer_weight > 0.0); + } + + if (!no_background_pass && prediction.do_background) { + layer_color = textureLod(color_bg_tx, uv_halfres, 0.0); + layer_weight = textureLod(weight_bg_tx, uv_halfres, 0.0).r; + /* Always prefer background to holefill pass. */ + layer_color *= safe_rcp(layer_weight); + layer_weight = float(layer_weight > 0.0); + /* Composite background. */ + out_color = out_color * (1.0 - layer_weight) + layer_color; + weight = weight * (1.0 - layer_weight) + layer_weight; + /* Fill holes with the composited background. */ + out_color *= safe_rcp(weight); + weight = float(weight > 0.0); + } + + if (!no_slight_focus_pass && prediction.do_slight_focus) { + dof_slight_focus_gather(sampling, + dof, + depth_tx, + color_tx, + bokeh_lut_tx, + coc_tile.fg_slight_focus_max_coc, + layer_color, + layer_weight); + /* Composite slight defocus. */ + out_color = out_color * (1.0 - layer_weight) + layer_color; + weight = weight * (1.0 - layer_weight) + layer_weight; + } + + if (!no_focus_pass && prediction.do_focus) { + layer_color = safe_color(textureLod(color_tx, uvcoordsvar.xy, 0.0)); + layer_weight = 1.0; + /* Composite in focus. */ + out_color = out_color * (1.0 - layer_weight) + layer_color; + weight = weight * (1.0 - layer_weight) + layer_weight; + } + + if (!no_foreground_pass && prediction.do_foreground) { + layer_color = textureLod(color_fg_tx, uv_halfres, 0.0); + layer_weight = textureLod(weight_fg_tx, uv_halfres, 0.0).r; + /* Composite foreground. */ + out_color = out_color * (1.0 - layer_weight) + layer_color; + } + + /* Fix float precision issue in alpha compositing. */ + if (out_color.a > 0.99) { + out_color.a = 1.0; + } + + if (debug_resolve_perf && coc_tile.fg_slight_focus_max_coc >= 0.5) { + out_color.rgb *= vec3(1.0, 0.1, 0.1); + } +} diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_scatter_frag.glsl index 06dcbeaed66..e03b1903ece 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_scatter_frag.glsl @@ -6,38 +6,36 @@ * invocations and overdraw. */ -#pragma BLENDER_REQUIRE(effect_dof_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_depth_of_field_scatter_lib.glsl) -uniform sampler2D occlusionBuffer; -uniform sampler2D bokehLut; - -uniform vec2 bokehAnisotropyInv; +layout(std140) uniform dof_block +{ + DepthOfFieldData dof; +}; -flat in vec4 color1; -flat in vec4 color2; -flat in vec4 color3; -flat in vec4 color4; -flat in vec4 weights; -flat in vec4 cocs; -flat in vec2 spritepos; -flat in float spritesize; /* MaxCoC */ +uniform sampler2D occlusion_tx; +uniform sampler2D bokeh_lut_tx; layout(location = 0) out vec4 fragColor; float bokeh_shape(vec2 center) { vec2 co = gl_FragCoord.xy - center; - -#ifdef DOF_BOKEH_TEXTURE - co *= bokehAnisotropyInv; - float texture_size = float(textureSize(bokehLut, 0).x); - /* Bias scale to avoid sampling at the texture's border. */ - float scale_fac = spritesize * (float(DOF_BOKEH_LUT_SIZE) / float(DOF_BOKEH_LUT_SIZE - 1)); - float dist = scale_fac * textureLod(bokehLut, (co / scale_fac) * 0.5 + 0.5, 0.0).r; -#else - float dist = length(co); -#endif - + float dist; + if (DOF_BOKEH_TEXTURE) { + if (DOF_FOREGROUND_PASS) { + /* Negate to flip bokeh shape. Mimics optical phenomenon. */ + co = -co; + } + co *= dof.bokeh_anisotropic_scale_inv; + float texture_size = float(textureSize(bokeh_lut_tx, 0).x); + /* Bias scale to avoid sampling at the texture's border. */ + float scale_fac = spritesize * (float(DOF_BOKEH_LUT_SIZE) / float(DOF_BOKEH_LUT_SIZE - 1)); + dist = scale_fac * textureLod(bokeh_lut_tx, (co / scale_fac) * 0.5 + 0.5, 0.0).r; + } + else { + dist = length(co); + } return dist; } @@ -59,9 +57,9 @@ void main(void) } if (!no_scatter_occlusion) { - /* Works because target is the same size as occlusionBuffer. */ - vec2 uv = gl_FragCoord.xy / vec2(textureSize(occlusionBuffer, 0).xy); - vec2 occlusion_data = texture(occlusionBuffer, uv).rg; + /* Works because target is the same size as occlusion_tx. */ + vec2 uv = gl_FragCoord.xy / vec2(textureSize(occlusion_tx, 0).xy); + vec2 occlusion_data = texture(occlusion_tx, uv).rg; /* Fix tilling artifacts. (Slide 90) */ const float correction_fac = 1.0 - DOF_FAST_GATHER_COC_ERROR; /* Occlude the sprite with geometry from the same field @@ -79,7 +77,7 @@ void main(void) /* Do not accumulate alpha. This has already been accumulated by the gather pass. */ fragColor.a = 0.0; -#ifdef DOF_DEBUG_SCATTER_PERF - fragColor.rgb = avg(fragColor.rgb) * vec3(1.0, 0.0, 0.0); -#endif + if (debug_scatter_perf) { + fragColor.rgb = avg(fragColor.rgb) * vec3(1.0, 0.0, 0.0); + } } diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_scatter_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_scatter_lib.glsl new file mode 100644 index 00000000000..d31940f1968 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_scatter_lib.glsl @@ -0,0 +1,17 @@ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) + +IN_OUT DofScatterStageInterface +{ + /** Colors, weights, and Circle of confusion radii for the 4 pixels to scatter. */ + flat vec4 color1; + flat vec4 color2; + flat vec4 color3; + flat vec4 color4; + flat vec4 weights; + flat vec4 cocs; + /** Sprite center position. In pixels. */ + flat vec2 spritepos; + /* MaxCoC */ + flat float spritesize; +}; diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_scatter_vert.glsl index f349806d37e..0fcfbd3a8c5 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_scatter_vert.glsl @@ -1,26 +1,20 @@ -#pragma BLENDER_REQUIRE(effect_dof_lib.glsl) +/** + * Scatter pass: Use sprites to scatter the color of very bright pixel to have higher quality blur. + * + * We only scatter one triangle per sprite and one sprite per 4 pixels to reduce vertex shader + * invocations and overdraw. + **/ -uniform vec2 targetTexelSize; -uniform int spritePerRow; -uniform vec2 bokehAnisotropy; +#pragma BLENDER_REQUIRE(eevee_depth_of_field_scatter_lib.glsl) -uniform sampler2D colorBuffer; -uniform sampler2D cocBuffer; - -/* Scatter pass, calculate a triangle covering the CoC. - * We render to a half resolution target with double width so we can - * separate near and far fields. We also generate only one triangle per group of 4 pixels - * to limit overdraw. */ +layout(std140) uniform dof_block +{ + DepthOfFieldData dof; +}; -flat out vec4 color1; -flat out vec4 color2; -flat out vec4 color3; -flat out vec4 color4; -flat out vec4 weights; -flat out vec4 cocs; -flat out vec2 spritepos; -flat out float spritesize; +uniform sampler2D color_tx; +uniform sampler2D coc_tx; /* Load 4 Circle of confusion values. texel_co is centered around the 4 taps. */ vec4 fetch_cocs(vec2 texel_co) @@ -28,21 +22,21 @@ vec4 fetch_cocs(vec2 texel_co) /* TODO(fclem) The textureGather(sampler, co, comp) variant isn't here on some implementations. */ #if 0 // GPU_ARB_texture_gather - vec2 uvs = texel_co / vec2(textureSize(cocBuffer, 0)); + vec2 uvs = texel_co / vec2(textureSize(coc_tx, 0)); /* Reminder: Samples order is CW starting from top left. */ - cocs = textureGather(cocBuffer, uvs, isForegroundPass ? 0 : 1); + cocs = textureGather(coc_tx, uvs, isForegroundPass ? 0 : 1); #else ivec2 texel = ivec2(texel_co - 0.5); vec4 cocs; - cocs.x = texelFetchOffset(cocBuffer, texel, 0, ivec2(0, 1)).r; - cocs.y = texelFetchOffset(cocBuffer, texel, 0, ivec2(1, 1)).r; - cocs.z = texelFetchOffset(cocBuffer, texel, 0, ivec2(1, 0)).r; - cocs.w = texelFetchOffset(cocBuffer, texel, 0, ivec2(0, 0)).r; + cocs.x = texelFetchOffset(coc_tx, texel, 0, ivec2(0, 1)).r; + cocs.y = texelFetchOffset(coc_tx, texel, 0, ivec2(1, 1)).r; + cocs.z = texelFetchOffset(coc_tx, texel, 0, ivec2(1, 0)).r; + cocs.w = texelFetchOffset(coc_tx, texel, 0, ivec2(0, 0)).r; #endif -#ifdef DOF_FOREGROUND_PASS - cocs *= -1.0; -#endif + if (is_foreground) { + cocs *= -1.0; + } cocs = max(vec4(0.0), cocs); /* We are scattering at half resolution, so divide CoC by 2. */ @@ -57,12 +51,12 @@ void vertex_discard() void main() { - ivec2 tex_size = textureSize(cocBuffer, 0); + ivec2 tex_size = textureSize(coc_tx, 0); int t_id = gl_VertexID / 3; /* Triangle Id */ /* Some math to get the target pixel. */ - ivec2 texelco = ivec2(t_id % spritePerRow, t_id / spritePerRow) * 2; + ivec2 texelco = ivec2(t_id % dof.scatter_sprite_per_row, t_id / dof.scatter_sprite_per_row) * 2; /* Center sprite around the 4 texture taps. */ spritepos = vec2(texelco) + 1.0; @@ -82,7 +76,7 @@ void main() for (int i = 0; i < 4; i++) { vec2 sample_uv = quad_center + quad_offsets[i] * input_texel_size; - colors[i] = dof_load_scatter_color(colorBuffer, sample_uv, 0.0); + colors[i] = textureLod(color_tx, sample_uv, 0.0); no_color = no_color && all(equal(colors[i].rgb, vec3(0.0))); } @@ -128,11 +122,11 @@ void main() /* Add 2.5 to max_coc because the max_coc may not be centered on the sprite origin * and because we smooth the bokeh shape a bit in the pixel shader. */ - gl_Position.xy *= spritesize * bokehAnisotropy + 2.5; + gl_Position.xy *= spritesize * dof.bokeh_anisotropic_scale + 2.5; /* Position the sprite. */ gl_Position.xy += spritepos; /* NDC range [-1..1]. */ - gl_Position.xy = gl_Position.xy * targetTexelSize * 2.0 - 1.0; + gl_Position.xy = gl_Position.xy * dof.texel_size * 2.0 - 1.0; /* Add 2.5 for the same reason but without the ratio. */ spritesize += 2.5; diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_setup_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_setup_frag.glsl new file mode 100644 index 00000000000..aba91e9bd12 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_setup_frag.glsl @@ -0,0 +1,73 @@ + +/** + * Setup pass: CoC and luma aware downsample to half resolution of the input scene color buffer. + * + * An addition to the downsample CoC, we output the maximum slight out of focus CoC to be + * sure we don't miss a pixel. + * + * Input: + * Full-resolution color & depth buffer + * Output: + * Half-resolution Color, signed CoC (out_coc.x), and max slight focus abs CoC (out_coc.y). + **/ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) + +layout(std140) uniform dof_block +{ + DepthOfFieldData dof; +}; + +uniform sampler2D color_tx; +uniform sampler2D depth_tx; + +layout(location = 0) out vec4 out_color; +layout(location = 1) out vec2 out_coc; + +float dof_abs_max_slight_of_focus_coc(vec4 cocs) +{ + /* Clamp to 0.5 if full in defocus to differentiate full focus tiles with coc == 0.0. + * This enables an optimization in the resolve pass. */ + const vec4 threshold = vec4(dof_layer_threshold + dof_layer_offset); + cocs = abs(cocs); + bvec4 defocus = greaterThan(cocs, threshold); + bvec4 focus = lessThanEqual(cocs, vec4(0.5)); + if (any(defocus) && any(focus)) { + /* For the same reason as in the flatten pass. This is a case we cannot optimize for. */ + cocs = mix(cocs, vec4(dof_tile_mixed), focus); + cocs = mix(cocs, vec4(dof_tile_mixed), defocus); + } + else { + cocs = mix(cocs, vec4(dof_tile_focus), focus); + cocs = mix(cocs, vec4(dof_tile_defocus), defocus); + } + return max_v4(cocs); +} + +void main() +{ + vec2 fullres_texel_size = 1.0 / vec2(textureSize(color_tx, 0).xy); + /* Center uv around the 4 fullres pixels. */ + vec2 quad_center = (floor(gl_FragCoord.xy) * 2.0 + 1.0) * fullres_texel_size; + + vec4 colors[4]; + vec4 cocs; + for (int i = 0; i < 4; i++) { + vec2 sample_uv = quad_center + quad_offsets[i] * fullres_texel_size; + colors[i] = safe_color(textureLod(color_tx, sample_uv, 0.0)); + cocs[i] = dof_coc_from_depth(dof, sample_uv, textureLod(depth_tx, sample_uv, 0.0).r); + } + + cocs = clamp(cocs, -dof.coc_abs_max, dof.coc_abs_max); + + vec4 weights = dof_bilateral_coc_weights(cocs); + weights *= dof_bilateral_color_weights(colors); + /* Normalize so that the sum is 1. */ + weights *= safe_rcp(sum(weights)); + + out_color = weighted_sum_array(colors, weights); + out_coc.x = dot(cocs, weights); + out_coc.y = dof_abs_max_slight_of_focus_coc(cocs); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_tiles_dilate_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_tiles_dilate_frag.glsl new file mode 100644 index 00000000000..ced77716789 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_tiles_dilate_frag.glsl @@ -0,0 +1,117 @@ + +/** + * Tile dilate pass: Takes the 8x8 Tiles buffer and converts dilates the tiles with large CoC to + * their neighborhood. This pass is repeated multiple time until the maximum CoC can be covered. + * + * Input & Output: + * - Separated foreground and background CoC. 1/8th of half-res resolution. So 1/16th of full-res. + **/ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) + +uniform int ring_count; +uniform int ring_width_multiplier; +uniform bool dilate_slight_focus; + +uniform sampler2D tiles_fg_tx; +uniform sampler2D tiles_bg_tx; + +layout(location = 0) out vec4 out_tile_fg; +layout(location = 1) out vec3 out_tile_bg; + +/* Error introduced by the random offset of the gathering kernel's center. */ +const float bluring_radius_error = 1.0 + 1.0 / (float(DOF_GATHER_RING_COUNT) + 0.5); +const float tile_to_fullres_factor = float(DOF_TILE_DIVISOR); + +void main() +{ + ivec2 center_tile_pos = ivec2(gl_FragCoord.xy); + + CocTile ring_buckets[DOF_DILATE_RING_COUNT]; + + for (int ring = 0; ring < ring_count && ring < DOF_DILATE_RING_COUNT; ring++) { + ring_buckets[ring] = dof_coc_tile_init(); + + int ring_distance = ring + 1; + for (int sample_id = 0; sample_id < 4 * ring_distance; sample_id++) { + ivec2 offset = dof_square_ring_sample_offset(ring_distance, sample_id); + + offset *= ring_width_multiplier; + + for (int i = 0; i < 2; i++) { + ivec2 adj_tile_pos = center_tile_pos + ((i == 0) ? offset : -offset); + + CocTile adj_tile = dof_coc_tile_load(tiles_fg_tx, tiles_bg_tx, adj_tile_pos); + + if (DILATE_MODE_MIN_MAX) { + /* Actually gather the "absolute" biggest coc but keeping the sign. */ + ring_buckets[ring].fg_min_coc = min(ring_buckets[ring].fg_min_coc, adj_tile.fg_min_coc); + ring_buckets[ring].bg_max_coc = max(ring_buckets[ring].bg_max_coc, adj_tile.bg_max_coc); + + if (dilate_slight_focus) { + ring_buckets[ring].fg_slight_focus_max_coc = dof_coc_max_slight_focus( + ring_buckets[ring].fg_slight_focus_max_coc, adj_tile.fg_slight_focus_max_coc); + } + } + else { /* DILATE_MODE_MIN_ABS */ + ring_buckets[ring].fg_max_coc = max(ring_buckets[ring].fg_max_coc, adj_tile.fg_max_coc); + ring_buckets[ring].bg_min_coc = min(ring_buckets[ring].bg_min_coc, adj_tile.bg_min_coc); + + /* Should be tight as possible to reduce gather overhead (see slide 61). */ + float closest_neighbor_distance = length(max(abs(vec2(offset)) - 1.0, 0.0)) * + tile_to_fullres_factor; + + ring_buckets[ring].fg_max_intersectable_coc = max( + ring_buckets[ring].fg_max_intersectable_coc, + adj_tile.fg_max_intersectable_coc + closest_neighbor_distance); + ring_buckets[ring].bg_min_intersectable_coc = min( + ring_buckets[ring].bg_min_intersectable_coc, + adj_tile.bg_min_intersectable_coc + closest_neighbor_distance); + } + } + } + } + + /* Load center tile. */ + CocTile out_tile = dof_coc_tile_load(tiles_fg_tx, tiles_bg_tx, center_tile_pos); + + /* Dilate once. */ + if (dilate_slight_focus) { + out_tile.fg_slight_focus_max_coc = dof_coc_max_slight_focus( + out_tile.fg_slight_focus_max_coc, ring_buckets[0].fg_slight_focus_max_coc); + } + + for (int ring = 0; ring < ring_count && ring < DOF_DILATE_RING_COUNT; ring++) { + float ring_distance = float(ring + 1); + + ring_distance = (ring_distance * ring_width_multiplier - 1) * tile_to_fullres_factor; + + if (DILATE_MODE_MIN_MAX) { + /* NOTE(fclem): Unsure if both sides of the inequalities have the same unit. */ + if (-ring_buckets[ring].fg_min_coc * bluring_radius_error > ring_distance) { + out_tile.fg_min_coc = min(out_tile.fg_min_coc, ring_buckets[ring].fg_min_coc); + } + + if (ring_buckets[ring].bg_max_coc * bluring_radius_error > ring_distance) { + out_tile.bg_max_coc = max(out_tile.bg_max_coc, ring_buckets[ring].bg_max_coc); + } + } + else { /* DILATE_MODE_MIN_ABS */ + /* Find minimum absolute CoC radii that will be intersected for the previously + * computed maximum CoC values. */ + if (-out_tile.fg_min_coc * bluring_radius_error > ring_distance) { + out_tile.fg_max_coc = max(out_tile.fg_max_coc, ring_buckets[ring].fg_max_coc); + out_tile.fg_max_intersectable_coc = max(out_tile.fg_max_intersectable_coc, + ring_buckets[ring].fg_max_intersectable_coc); + } + + if (out_tile.bg_max_coc * bluring_radius_error > ring_distance) { + out_tile.bg_min_coc = min(out_tile.bg_min_coc, ring_buckets[ring].bg_min_coc); + out_tile.bg_min_intersectable_coc = min(out_tile.bg_min_intersectable_coc, + ring_buckets[ring].bg_min_intersectable_coc); + } + } + } + + dof_coc_tile_store(out_tile, out_tile_fg, out_tile_bg); +} diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_flatten_tiles_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_tiles_flatten_frag.glsl index 48195a1daea..7462a272fbd 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_flatten_tiles_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/eevee_depth_of_field_tiles_flatten_frag.glsl @@ -5,22 +5,25 @@ * Output min and max values for each tile and for both foreground & background. * Also outputs min intersectable CoC for the background, which is the minimum CoC * that comes from the background pixels. + * + * Input: + * - Half-resolution Circle of confusion. Out of setup pass. + * Output: + * - Separated foreground and background CoC. 1/8th of half-res resolution. So 1/16th of full-res. */ -#pragma BLENDER_REQUIRE(effect_dof_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) -/* Half resolution. */ -uniform sampler2D halfResCocBuffer; +uniform sampler2D coc_tx; -/* 1/8th of halfResCocBuffer resolution. So 1/16th of fullres. */ -layout(location = 0) out vec4 outFgCoc; -layout(location = 1) out vec3 outBgCoc; +layout(location = 0) out vec4 out_tile_fg; +layout(location = 1) out vec3 out_tile_bg; const int halfres_tile_divisor = DOF_TILE_DIVISOR / 2; void main() { - ivec2 halfres_bounds = textureSize(halfResCocBuffer, 0).xy - 1; + ivec2 halfres_bounds = textureSize(coc_tx, 0).xy - 1; ivec2 tile_co = ivec2(gl_FragCoord.xy); CocTile tile = dof_coc_tile_init(); @@ -29,7 +32,7 @@ void main() /* OPTI: Could be done in separate passes. */ for (int y = 0; y < halfres_tile_divisor; y++) { ivec2 sample_texel = tile_co * halfres_tile_divisor + ivec2(x, y); - vec2 sample_data = texelFetch(halfResCocBuffer, min(sample_texel, halfres_bounds), 0).rg; + vec2 sample_data = texelFetch(coc_tx, min(sample_texel, halfres_bounds), 0).rg; float sample_coc = sample_data.x; float sample_slight_focus_coc = sample_data.y; @@ -53,5 +56,5 @@ void main() } } - dof_coc_tile_store(tile, outFgCoc, outBgCoc); + dof_coc_tile_store(tile, out_tile_fg, out_tile_bg); } diff --git a/source/blender/draw/engines/eevee/shaders/eevee_film_filter_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_film_filter_frag.glsl new file mode 100644 index 00000000000..f924e321deb --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_film_filter_frag.glsl @@ -0,0 +1,114 @@ + +/** + * Accumulate input texture into the film accumulation buffer. + * + * All samples inside the filter radius are projected to the input texture. + * The nearest input sample is then projected back to the destination texture space + * to get an accurate filter weight. + * + * If using nearest filtering (for non-color data) only the closest sample is considered + * and the weight is use as a distance metric. + **/ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_film_lib.glsl) + +layout(std140) uniform camera_block +{ + CameraData camera; +}; + +layout(std140) uniform film_block +{ + FilmData film; +}; + +uniform sampler2D input_tx; +uniform sampler2D data_tx; +uniform sampler2D weight_tx; + +in vec4 uvcoordsvar; + +layout(location = 0) out vec4 out_data; +layout(location = 1) out float out_weight; + +/* clang-format off */ +const vec2 sample_offsets_plus[5] = vec2[5](vec2(0, 1), vec2(-1, 0), vec2(0, 0), vec2(1, 0), vec2(0, -1)); +const vec2 sample_offsets_3x3[9] = vec2[9](vec2(-1, 1), vec2(0, 1), vec2(1, 1), vec2(-1, 0), vec2(0, 0), vec2(1, 0), vec2(-1, -1), vec2(0, -1), vec2(1, -1)); +/* clang-format on */ + +void main(void) +{ + out_data = vec4(0.0); + out_weight = 0.0; + + /* TODO(fclem) Split into multiple shaders? Measure benefits. */ + if (camera.filter_size < 1.0 || !film_is_color_data(film)) { + film_process_sample(camera, + film, + ProjectionMatrix, + ProjectionMatrixInverse, + input_tx, + vec2(0.0), + out_data, + out_weight); + } + else if (camera.filter_size < M_SQRT2) { + for (int i = 0; i < 5; i++) { + film_process_sample(camera, + film, + ProjectionMatrix, + ProjectionMatrixInverse, + input_tx, + sample_offsets_plus[i], + out_data, + out_weight); + } + } + else if (camera.filter_size < 2.0) { + for (int i = 0; i < 9; i++) { + film_process_sample(camera, + film, + ProjectionMatrix, + ProjectionMatrixInverse, + input_tx, + sample_offsets_3x3[i], + out_data, + out_weight); + } + } + else { + /* This is slow but using large filter is not very common. */ + float extent = floor(camera.filter_size); + for (float x = -extent; x < extent; x++) { + for (float y = -extent; y < extent; y++) { + film_process_sample(camera, + film, + ProjectionMatrix, + ProjectionMatrixInverse, + input_tx, + vec2(x, y), + out_data, + out_weight); + } + } + } + + if (film.use_history) { + vec2 uv_history = film_uv_history_get(camera, camera, uvcoordsvar.xy); + vec4 history_data = textureLod(data_tx, uv_history, 0.0); + float history_weight = textureLod(weight_tx, uv_history, 0.0).r; + + if (film_is_color_data(film)) { + out_data += history_data; + out_weight += history_weight; + } + else { + /* Non-color data do not accumulates. It is replaced by nearest value. */ + if (history_weight > out_weight) { + out_weight = history_weight; + out_data = history_data; + } + } + } +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_film_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_film_lib.glsl new file mode 100644 index 00000000000..a990567800a --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_film_lib.glsl @@ -0,0 +1,149 @@ + +/** + * Film accumulation utils functions. + **/ + +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) +#pragma BLENDER_REQUIRE(eevee_camera_lib.glsl) + +bool film_is_color_data(FilmData film) +{ + return film.data_type < FILM_DATA_FLOAT; +} + +vec4 film_data_encode(FilmData film, vec4 data, float weight) +{ + if (film_is_color_data(film)) { + /* Could we assume safe color from earlier pass? */ + data = safe_color(data); + /* Convert transmittance to opacity. */ + data.a = saturate(1.0 - data.a); + } + + if (film.data_type == FILM_DATA_COLOR_LOG) { + /* TODO(fclem) Pre-expose. */ + data.rgb = log2(1.0 + data.rgb); + } + else if (film.data_type == FILM_DATA_DEPTH) { + /* TODO(fclem) Depth should be converted to radial depth in panoramic projection. */ + } + else if (film.data_type == FILM_DATA_MOTION) { + /* Motion vectors are in camera uv space. But final motion vectors are in pixel units. */ + data *= film.uv_scale_inv.xyxy; + } + + if (film_is_color_data(film)) { + data *= weight; + } + return data; +} + +vec4 film_data_decode(FilmData film, vec4 data, float weight) +{ + if (film_is_color_data(film)) { + data *= safe_rcp(weight); + } + + if (film.data_type == FILM_DATA_COLOR_LOG) { + /* TODO(fclem) undo Pre-expose. */ + data.rgb = exp2(data.rgb) - 1.0; + } + return data; +} + +/* Returns uv's position in the previous frame. */ +vec2 film_uv_history_get(CameraData camera, CameraData camera_history, vec2 uv) +{ +#if 0 /* TODO reproject history */ + vec3 V = camera_view_from_uv(camera, uv); + vec3 V_prev = transform_point(hitory_mat, V); + vec2 uv_history = camera_uv_from_view(camera_history, V_prev); + return uv_history; +#endif + return uv; +} + +/* -------------------------------------------------------------------- */ +/** \name Filter + * \{ */ + +float film_filter_weight(CameraData camera, vec2 offset) +{ +#if 1 /* Faster */ + /* Gaussian fitted to Blackman-Harris. */ + float r = len_squared(offset) / sqr(camera.filter_size); + const float sigma = 0.284; + const float fac = -0.5 / (sigma * sigma); + float weight = exp(fac * r); +#else + /* Blackman-Harris filter. */ + float r = M_2PI * saturate(0.5 + length(offset) / (2.0 * camera.filter_size)); + float weight = 0.35875 - 0.48829 * cos(r) + 0.14128 * cos(2.0 * r) - 0.01168 * cos(3.0 * r); +#endif + /* Always return a weight above 0 to avoid blind spots between samples. */ + return max(weight, 1e-6); +} + +/* Camera UV is the full-frame UV. Film uv is after cropping from render border. */ +vec2 film_sample_from_camera_uv(FilmData film, vec2 sample_uv) +{ + return (sample_uv - film.uv_bias) * film.uv_scale_inv; +} + +vec2 film_sample_to_camera_uv(FilmData film, vec2 sample_co) +{ + return sample_co * film.uv_scale + film.uv_bias; +} + +void film_process_sample(CameraData camera, + FilmData film, + mat4 input_persmat, + mat4 input_persinv, + sampler2D input_tx, + vec2 sample_offset, + inout vec4 data, + inout float weight) +{ + /* Project sample from destrination space to source texture. */ + vec2 sample_center = gl_FragCoord.xy; + vec2 sample_uv = film_sample_to_camera_uv(film, sample_center + sample_offset); + vec3 vV_dst = camera_view_from_uv(camera, sample_uv); + /* Pixels outside of projection range. */ + if (vV_dst == vec3(0.0)) { + return; + } + + bool is_persp = camera.type != CAMERA_ORTHO; + vec2 uv_src = camera_uv_from_view(input_persmat, is_persp, vV_dst); + /* Snap to sample actual location (pixel center). */ + vec2 input_size = vec2(textureSize(input_tx, 0)); + vec2 texel_center_src = floor(uv_src * input_size) + 0.5; + uv_src = texel_center_src / input_size; + /* Discard pixels outside of input range. */ + if (any(greaterThan(abs(uv_src - 0.5), vec2(0.5)))) { + return; + } + + /* Reproject sample location in destination space to have correct distance metric. */ + vec3 vV_src = camera_view_from_uv(input_persinv, uv_src); + vec2 uv_cam = camera_uv_from_view(camera, vV_src); + vec2 sample_dst = film_sample_from_camera_uv(film, uv_cam); + + /* Equirectangular projection might wrap and have more than one point mapping to the same + * original coordinate. We need to get the closest pixel center. + * NOTE: This is wrong for projection outside the main frame. */ + if (camera.type == CAMERA_PANO_EQUIRECT) { + sample_center = film_sample_to_camera_uv(film, sample_center); + vec3 vV_center = camera_view_from_uv(camera, sample_center); + sample_center = camera_uv_from_view(camera, vV_center); + sample_center = film_sample_from_camera_uv(film, sample_center); + } + /* Compute filter weight and add to weighted sum. */ + vec2 offset = sample_dst - sample_center; + float sample_weight = film_filter_weight(camera, offset); + vec4 sample_data = textureLod(input_tx, uv_src, 0.0); + data += film_data_encode(film, sample_data, sample_weight); + weight += sample_weight; +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/eevee_film_resolve_depth_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_film_resolve_depth_frag.glsl new file mode 100644 index 00000000000..85ec0186d7f --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_film_resolve_depth_frag.glsl @@ -0,0 +1,22 @@ + +#pragma BLENDER_REQUIRE(eevee_film_lib.glsl) + +layout(std140) uniform film_block +{ + FilmData film; +}; + +uniform sampler2D data_tx; +uniform sampler2D weight_tx; + +in vec4 uvcoordsvar; + +void main(void) +{ + vec2 uv = uvcoordsvar.xy; + + vec4 color = textureLod(data_tx, uv, 0.0); + float weight = textureLod(weight_tx, uv, 0.0).r; + + gl_FragDepth = film_data_decode(film, color, weight).r; +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_film_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_film_resolve_frag.glsl new file mode 100644 index 00000000000..bdf36d52e96 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_film_resolve_frag.glsl @@ -0,0 +1,31 @@ + +#pragma BLENDER_REQUIRE(eevee_film_lib.glsl) + +layout(std140) uniform film_block +{ + FilmData film; +}; + +uniform sampler2D data_tx; +uniform sampler2D weight_tx; +uniform sampler2D first_sample_tx; + +in vec4 uvcoordsvar; + +layout(location = 0) out vec4 out_color; + +void main(void) +{ + vec2 uv = uvcoordsvar.xy; + + vec4 color = textureLod(data_tx, uv, 0.0); + float weight = textureLod(weight_tx, uv, 0.0).r; + + out_color = film_data_decode(film, color, weight); + + /* First sample is stored in a fullscreen buffer. */ + vec2 uv_first_sample = ((uv * film.extent) + film.offset) / + vec2(textureSize(first_sample_tx, 0).xy); + vec4 first_sample = textureLod(first_sample_tx, uv_first_sample, 0.0); + out_color = mix(first_sample, out_color, film.opacity); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_gbuffer_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_gbuffer_lib.glsl new file mode 100644 index 00000000000..224a376bde4 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_gbuffer_lib.glsl @@ -0,0 +1,272 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_closure_lib.glsl) + +/* -------------------------------------------------------------------- */ +/** \name Encoding and decoding functions + * + * \{ */ + +uint gbuffer_encode_unit_float_to_uint(float scalar, const uint bit_size) +{ + float fac = float((1u << bit_size) - 1u); + return uint(saturate(scalar) * fac); +} + +float gbuffer_decode_unit_float_from_uint(uint packed_scalar, const uint bit_size) +{ + float fac = 1.0 / float((1u << bit_size) - 1u); + uint mask = ~(0xFFFFFFFFu << bit_size); + return float(packed_scalar & mask) * fac; +} + +/* Expects input to be normalized. */ +vec2 gbuffer_encode_normal(vec3 normal) +{ + vec3 vN = normal_world_to_view(normal); + bool neg = vN.z < 0.0; + if (neg) { + vN.z = -vN.z; + } + vec2 packed_normal = normal_encode(vN); + // return packed_normal; + return (neg) ? -packed_normal : packed_normal; +} + +vec3 gbuffer_decode_normal(vec2 packed_normal) +{ + bool neg = packed_normal.y < 0.0; + vec3 vN = normal_decode(abs(packed_normal)); + if (neg) { + vN.z = -vN.z; + } + return normal_view_to_world(vN); +} + +/* Note: does not handle negative colors. */ +uint gbuffer_encode_color(vec3 color) +{ + color *= 1.0; /* Test */ + float intensity = length(color); + /* Normalize to store it like a normal vector. */ + // color *= safe_rcp(intensity); + + uint encoded_color; + // encoded_color = gbuffer_encode_unit_float_to_uint(saturate(color.x), 10u) << 10u; + // encoded_color |= gbuffer_encode_unit_float_to_uint(saturate(color.y), 10u); + // encoded_color |= gbuffer_encode_unit_float_to_uint(saturate(intensity), 12u) << 20u; + + encoded_color = gbuffer_encode_unit_float_to_uint(saturate(color.x), 11u); + encoded_color |= gbuffer_encode_unit_float_to_uint(saturate(color.y), 11u) << 11u; + encoded_color |= gbuffer_encode_unit_float_to_uint(saturate(color.z), 10u) << 21u; + return encoded_color; +} + +vec3 gbuffer_decode_color(uint packed_data) +{ + vec3 color; + // color.x = gbuffer_decode_unit_float_from_uint(packed_data >> 10u, 10u); + // color.y = gbuffer_decode_unit_float_from_uint(packed_data, 10u); + // color.z = sqrt(1.0 - clamp(dot(color.xy, color.xy), 0.0, 1.0)); + // color *= gbuffer_decode_unit_float_from_uint(packed_data >> 20u, 12u); + + color.x = gbuffer_decode_unit_float_from_uint(packed_data, 11u); + color.y = gbuffer_decode_unit_float_from_uint(packed_data >> 11u, 11u); + color.z = gbuffer_decode_unit_float_from_uint(packed_data >> 21u, 10u); + color *= 1.0; /* Test */ + return color; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Global data + * + * \{ */ + +void gbuffer_load_global_data(vec4 transmit_normal_in, out float thickness) +{ + thickness = transmit_normal_in.w; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Diffuse data + * + * \{ */ + +ClosureDiffuse gbuffer_load_diffuse_data(vec4 color_in, vec4 normal_in, vec4 data_in) +{ + ClosureDiffuse data_out; + if (normal_in.z == -1.0) { + /* Transmission data is Refraction data. */ + data_out.color = vec3(0.0); + data_out.N = vec3(1.0); + data_out.sss_id = 0u; + data_out.sss_radius = vec3(-1.0); + } + else { + data_out.color = color_in.rgb; + data_out.N = gbuffer_decode_normal(normal_in.xy); + data_out.sss_id = uint(normal_in.z * 1024.0); + data_out.sss_radius = data_in.rgb; + } + return data_out; +} + +ClosureDiffuse gbuffer_load_diffuse_data(sampler2D transmit_color_tx, + sampler2D transmit_normal_tx, + sampler2D transmit_data_tx, + vec2 uv) +{ + vec4 tra_col_in = texture(transmit_color_tx, uv); + vec4 tra_nor_in = texture(transmit_normal_tx, uv); + vec4 tra_dat_in = texture(transmit_data_tx, uv); + return gbuffer_load_diffuse_data(tra_col_in, tra_nor_in, tra_dat_in); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Glossy data + * + * \{ */ + +ClosureReflection gbuffer_load_reflection_data(vec4 color_in, vec4 normal_in) +{ + ClosureReflection data_out; + data_out.color = color_in.rgb; + data_out.N = gbuffer_decode_normal(normal_in.xy); + data_out.roughness = normal_in.z; + return data_out; +} + +ClosureReflection gbuffer_load_reflection_data(sampler2D reflect_color_tx, + sampler2D reflect_normal_tx, + vec2 uv) +{ + vec4 ref_col_in = texture(reflect_color_tx, uv); + vec4 ref_nor_in = texture(reflect_normal_tx, uv); + return gbuffer_load_reflection_data(ref_col_in, ref_nor_in); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Refraction data + * + * \{ */ + +ClosureRefraction gbuffer_load_refraction_data(vec4 color_in, vec4 normal_in, vec4 data_in) +{ + ClosureRefraction data_out; + if (normal_in.z == -1.0) { + data_out.color = color_in.rgb; + data_out.N = gbuffer_decode_normal(normal_in.xy); + data_out.ior = data_in.x; + data_out.roughness = data_in.y; + } + else { + /* Transmission data is Diffuse/SSS data. */ + data_out.color = vec3(0.0); + data_out.N = vec3(1.0); + data_out.ior = -1.0; + data_out.roughness = 0.0; + } + return data_out; +} + +ClosureRefraction gbuffer_load_refraction_data(sampler2D transmit_color_tx, + sampler2D transmit_normal_tx, + sampler2D transmit_data_tx, + vec2 uv) +{ + vec4 tra_col_in = texture(transmit_color_tx, uv); + vec4 tra_nor_in = texture(transmit_normal_tx, uv); + vec4 tra_dat_in = texture(transmit_data_tx, uv); + return gbuffer_load_refraction_data(tra_col_in, tra_nor_in, tra_dat_in); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Volume data + * + * Pack all volumetric effects. + * + * \{ */ + +#define VOLUME_HETEROGENEOUS -2.0 + +uvec4 gbuffer_store_volume_data(ClosureVolume data_in) +{ + uvec4 data_out; + data_out.x = gbuffer_encode_color(data_in.emission); + data_out.y = gbuffer_encode_color(data_in.scattering); + data_out.z = gbuffer_encode_color(data_in.transmittance); + data_out.w = floatBitsToUint(data_in.anisotropy); + return data_out; +} + +ClosureVolume gbuffer_load_volume_data(usampler2D gbuffer_tx, vec2 uv) +{ + uvec4 data_in = texture(gbuffer_tx, uv); + + ClosureVolume data_out; + data_out.emission = gbuffer_decode_color(data_in.x); + data_out.scattering = gbuffer_decode_color(data_in.y); + data_out.transmittance = gbuffer_decode_color(data_in.z); + data_out.anisotropy = uintBitsToFloat(data_in.w); + return data_out; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Emission data + * + * \{ */ + +vec3 gbuffer_store_emission_data(ClosureEmission data_in) +{ + return data_in.emission; +} + +ClosureEmission gbuffer_load_emission_data(sampler2D gbuffer_tx, vec2 uv) +{ + vec4 data_in = texture(gbuffer_tx, uv); + + ClosureEmission data_out; + data_out.emission = data_in.xyz; + return data_out; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transparency data + * + * \{ */ + +vec4 gbuffer_store_transparency_data(ClosureTransparency data_in) +{ + vec4 data_out; + data_out.xyz = data_in.transmittance; + data_out.w = data_in.holdout; + return data_out; +} + +ClosureTransparency gbuffer_load_transparency_data(sampler2D gbuffer_tx, vec2 uv) +{ + vec4 data_in = texture(gbuffer_tx, uv); + + ClosureTransparency data_out; + data_out.transmittance = data_in.xyz; + data_out.holdout = data_in.w; + return data_out; +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/eevee_hiz_copy_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_hiz_copy_frag.glsl new file mode 100644 index 00000000000..f0ee9f515c9 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_hiz_copy_frag.glsl @@ -0,0 +1,17 @@ +/** + * Copy input depth texture to lower left corner of the destination, filling any padding with + * clamped texture extrapolation. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +uniform sampler2D depth_tx; + +layout(location = 0) out float out_depth; + +void main() +{ + vec2 uv = gl_FragCoord.xy / vec2(textureSize(depth_tx, 0).xy); + + out_depth = texture(depth_tx, uv).r; +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_hiz_downsample_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_hiz_downsample_frag.glsl new file mode 100644 index 00000000000..9a095d960b2 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_hiz_downsample_frag.glsl @@ -0,0 +1,40 @@ +/** + * Shader that down-sample depth buffer, creating a Hierarchical-Z buffer. + * Saves max value of each 2x2 texel in the mipmap above the one we are rendering to. + * Adapted from http://rastergrid.com/blog/2010/10/hierarchical-z-map-based-occlusion-culling/ + * + * Major simplification has been made since we pad the buffer to always be bigger than input to + * avoid mipmapping misalignement. + */ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +uniform sampler2D depth_tx; + +uniform vec2 texel_size; + +layout(location = 0) out float out_depth; + +#ifndef GPU_ARB_texture_gather +vec4 texGather(sampler2D tex, vec2 uv) +{ + vec4 ofs = vec2(0.5, 0.5, -0.5, -0.5) * texel_size.xyxy; + return vec4(texture(tex, uv + ofs.zw).r, + texture(tex, uv + ofs.zy).r, + texture(tex, uv + ofs.xw).r, + texture(tex, uv + ofs.xy).r); +} +#else +# define texGather(a, b) textureGather(a, b) +#endif + +void main() +{ + /* NOTE(@fclem): textureSize() does not work the same on all implementations + * when changing the min and max texture levels. Use uniform instead (see T87801). */ + vec2 uv = gl_FragCoord.xy * texel_size; + + vec4 samp = texGather(depth_tx, uv); + + out_depth = max_v4(samp); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_irradiance_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_irradiance_lib.glsl new file mode 100644 index 00000000000..143de5b0093 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_irradiance_lib.glsl @@ -0,0 +1,139 @@ + +/** + * Diffuse Irradiance encoding, decoding, loading, evaluation functions. + **/ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +/* ---------------------------------------------------------------------- */ +/** \name Visibility + * + * Irradiance grid is backed by a grid of small filtered distance map that reduces + * light leak by performing a chebishev test. + * + * The technique is similar to the one in the paper even if not using raytracing to update + * lightprobes. + * "Dynamic Diffuse Global Illumination with Ray-Traced Irradiance Fields" + * http://jcgt.org/published/0008/02/01/ + * \{ */ + +vec4 visibility_encode(vec2 accum, float range) +{ + accum /= range; + vec4 data; + data.x = fract(accum.x); + data.y = floor(accum.x) / 255.0; + data.z = fract(accum.y); + data.w = floor(accum.y) / 255.0; + return data; +} + +vec2 visibility_decode(vec4 data, float range) +{ + return (data.xz + data.yw * 255.0) * range; +} + +vec2 visibility_mapping_octahedron(vec3 cubevec, vec2 texel_size) +{ + /* Projection onto octahedron. */ + cubevec /= dot(vec3(1.0), abs(cubevec)); + /* Out-folding of the downward faces. */ + if (cubevec.z < 0.0) { + vec2 cubevec_sign = step(0.0, cubevec.xy) * 2.0 - 1.0; + cubevec.xy = (1.0 - abs(cubevec.yx)) * cubevec_sign; + } + /* Mapping to [0;1]ˆ2 texture space. */ + vec2 uvs = cubevec.xy * (0.5) + 0.5; + /* Edge filtering fix. */ + uvs = (1.0 - 2.0 * texel_size) * uvs + texel_size; + return uvs; +} + +/* Returns the cell weight using the visibility data and a smooth test. */ +float visibility_load_cell(GridInfoData info, + sampler2DArray irradiance_tx, + int cell, + vec3 L, + float dist, + float bias, + float bleed_bias, + float range) +{ + /* Keep in sync with diffuse_filter_probe(). */ + ivec2 cell_co = ivec2(info.visibility_size); + cell_co.x *= (cell % info.visibility_cells_per_row); + cell_co.y *= (cell % info.visibility_cells_per_layer) / info.visibility_cells_per_row; + float layer = 1.0 + float((cell / info.visibility_cells_per_layer)); + + vec2 texel_size = 1.0 / vec2(textureSize(irradiance_tx, 0).xy); + vec2 co = vec2(cell_co) * texel_size; + + vec2 uv = visibility_mapping_octahedron(-L, vec2(1.0 / float(info.visibility_size))); + uv *= vec2(info.visibility_size) * texel_size; + + vec4 data = texture(irradiance_tx, vec3(co + uv, layer)); + + /* Decoding compressed data. */ + vec2 moments = visibility_decode(data, range); + /* Doing chebishev test. */ + float variance = abs(moments.x * moments.x - moments.y); + variance = max(variance, bias / 10.0); + + float d = dist - moments.x; + float p_max = variance / (variance + d * d); + + /* Increase contrast in the weight by squaring it */ + p_max *= p_max; + /* Now reduce light-bleeding by removing the [0, x] tail and linearly rescaling (x, 1] */ + p_max = clamp((p_max - bleed_bias) / (1.0 - bleed_bias), 0.0, 1.0); + + return (dist <= moments.x) ? 1.0 : p_max; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Irradiance + * + * Using HalfLife2 ambient cube. We encode data using RGBE compression. + * https://cdn.cloudflare.steamstatic.com/apps/valve/2006/SIGGRAPH06_Course_ShadingInValvesSourceEngine.pdf + * \{ */ + +vec4 irradiance_encode(vec3 rgb) +{ + float maxRGB = max_v3(rgb); + float fexp = ceil(log2(maxRGB)); + return vec4(rgb / exp2(fexp), (fexp + 128.0) / 255.0); +} + +vec3 irradiance_decode(vec4 data) +{ + float fexp = data.a * 255.0 - 128.0; + return data.rgb * exp2(fexp); +} + +/* Samples an irradiance grid cell in the given direction. */ +vec3 irradiance_load_cell(GridInfoData info, sampler2DArray irradiance_tx, int cell, vec3 N) +{ + ivec2 cell_co = ivec2(3, 2); + cell_co.x *= cell % info.irradiance_cells_per_row; + cell_co.y *= cell / info.irradiance_cells_per_row; + + ivec3 is_negative = ivec3(step(0.0, -N)); + + /* Listing 1. */ + vec3 n_squared = N * N; + vec3 irradiance; + vec4 data; + data = texelFetch(irradiance_tx, ivec3(cell_co + ivec2(0, is_negative.x), 0), 0); + irradiance = n_squared.x * irradiance_decode(data); + + data = texelFetch(irradiance_tx, ivec3(cell_co + ivec2(1, is_negative.y), 0), 0); + irradiance += n_squared.y * irradiance_decode(data); + + data = texelFetch(irradiance_tx, ivec3(cell_co + ivec2(2, is_negative.z), 0), 0); + irradiance += n_squared.z * irradiance_decode(data); + return irradiance; +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/eevee_light_eval_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_light_eval_lib.glsl new file mode 100644 index 00000000000..433688b70c4 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_light_eval_lib.glsl @@ -0,0 +1,103 @@ + +/** + * This is an eval function that needs to be added after main fragment shader. + * A prototype needs to be declared before main in order to use it. + * + * The resources expected to be defined are: + * - lights + * - lights_zbins + * - light_culling + * - lights_culling_words + * - shadow_atlas_tx + * - shadow_tilemaps_tx + * - sss_transmittance_tx + * - utility_tx + * + * All of this is needed to avoid using macros and performance issues with large + * arrays as function arguments. + */ + +#pragma BLENDER_REQUIRE(eevee_light_lib.glsl) + +/* TODO(fclem): We could reduce register pressure by only having static branches for sun lights. */ +void light_eval_ex(ClosureDiffuse diffuse, + ClosureReflection reflection, + vec3 P, + vec3 V, + float vP_z, + float thickness, + vec4 ltc_mat, + uint l_idx, + inout vec3 out_diffuse, + inout vec3 out_specular) +{ + LightData light = lights[l_idx]; + vec3 L; + float dist; + light_vector_get(light, P, L, dist); + + float visibility = light_attenuation(light, L, dist); + + if ((light.shadow_id != LIGHT_NO_SHADOW) && (visibility > 0.0)) { + vec3 lL = light_world_to_local(light, -L) * dist; + + float shadow_delta = shadow_delta_get( + shadow_atlas_tx, shadow_tilemaps_tx, light, light.shadow_data, lL, dist, P); + + /* Transmittance evaluation first to use initial visibility. */ + if (diffuse.sss_id != 0u && light.diffuse_power > 0.0) { + float delta = max(thickness, shadow_delta); + + vec3 intensity = + visibility * light.transmit_power * + light_translucent( + sss_transmittance_tx, light, diffuse.N, L, dist, diffuse.sss_radius, delta); + out_diffuse += light.color * intensity; + } + + visibility *= float(shadow_delta - light.shadow_data.bias <= 0.0); + } + + if (visibility < 1e-6) { + return; + } + + if (light.diffuse_power > 0.0) { + float intensity = visibility * light.diffuse_power * + light_diffuse(utility_tx, light, diffuse.N, V, L, dist); + out_diffuse += light.color * intensity; + } + + if (light.specular_power > 0.0) { + float intensity = visibility * light.specular_power * + light_ltc(utility_tx, light, reflection.N, V, L, dist, ltc_mat); + out_specular += light.color * intensity; + } +} + +void light_eval(ClosureDiffuse diffuse, + ClosureReflection reflection, + vec3 P, + vec3 V, + float vP_z, + float thickness, + inout vec3 out_diffuse, + inout vec3 out_specular) +{ + vec2 uv = vec2(reflection.roughness, safe_sqrt(1.0 - dot(reflection.N, V))); + uv = uv * UTIL_TEX_UV_SCALE + UTIL_TEX_UV_BIAS; + vec4 ltc_mat = utility_tx_sample(uv, UTIL_LTC_MAT_LAYER); + + LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_culling, l_idx) { + light_eval_ex( + diffuse, reflection, P, V, vP_z, thickness, ltc_mat, l_idx, out_diffuse, out_specular); + } + LIGHT_FOREACH_END + + LIGHT_FOREACH_BEGIN_LOCAL ( + light_culling, lights_zbins, lights_culling_words, gl_FragCoord.xy, vP_z, l_idx) { + light_eval_ex( + diffuse, reflection, P, V, vP_z, thickness, ltc_mat, l_idx, out_diffuse, out_specular); + } + LIGHT_FOREACH_END +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_light_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_light_lib.glsl new file mode 100644 index 00000000000..a288224a2e3 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_light_lib.glsl @@ -0,0 +1,197 @@ + +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_ltc_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_culling_iter_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +/* ---------------------------------------------------------------------- */ +/** \name Light Functions + * \{ */ + +void light_vector_get(LightData ld, vec3 P, out vec3 L, out float dist) +{ + if (ld.type == LIGHT_SUN) { + L = ld._back; + dist = 1.0; + } + else { + L = ld._position - P; + dist = inversesqrt(len_squared(L)); + L *= dist; + dist = 1.0 / dist; + } +} + +/* Rotate vector to light's local space. Does not translate. */ +vec3 light_world_to_local(LightData ld, vec3 L) +{ + /* Avoid relying on compiler to optimize this. + * vec3 lL = transpose(mat3(ld.object_mat)) * L; */ + vec3 lL; + lL.x = dot(ld.object_mat[0].xyz, L); + lL.y = dot(ld.object_mat[1].xyz, L); + lL.z = dot(ld.object_mat[2].xyz, L); + return lL; +} + +/* From Frostbite PBR Course + * Distance based attenuation + * http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf */ +float light_influence_attenuation(float dist, float inv_sqr_influence) +{ + float factor = sqr(dist) * inv_sqr_influence; + float fac = saturate(1.0 - sqr(factor)); + return sqr(fac); +} + +float light_spot_attenuation(LightData ld, vec3 L) +{ + vec3 lL = light_world_to_local(ld, L); + float ellipse = inversesqrt(1.0 + len_squared(lL.xy * ld.spot_size_inv / lL.z)); + float spotmask = smoothstep(0.0, 1.0, ellipse * ld._spot_mul + ld._spot_bias); + return spotmask; +} + +float light_attenuation(LightData ld, vec3 L, float dist) +{ + float vis = 1.0; + if (ld.type == LIGHT_SPOT) { + vis *= light_spot_attenuation(ld, L); + } + if (ld.type >= LIGHT_SPOT) { + vis *= step(0.0, -dot(L, -ld._back)); + } + if (ld.type != LIGHT_SUN) { +#ifdef VOLUME_LIGHTING + vis *= light_influence_attenuation(dist, ld.influence_radius_invsqr_volume); +#else + vis *= light_influence_attenuation(dist, ld.influence_radius_invsqr_surface); +#endif + } + return vis; +} + +/* Cheaper alternative than evaluating the LTC. + * The result needs to be multiplied by BSDF or Phase Function. */ +float light_point_light(LightData ld, vec3 L, float dist) +{ + if (ld.type == LIGHT_SUN) { + return 1.0; + } + /** + * Using "Point Light Attenuation Without Singularity" from Cem Yuksel + * http://www.cemyuksel.com/research/pointlightattenuation/pointlightattenuation.pdf + * http://www.cemyuksel.com/research/pointlightattenuation/ + **/ + float d_sqr = sqr(dist); + float r_sqr = ld.radius_squared; + /* Using reformulation that has better numerical percision. */ + float power = 2.0 / (d_sqr + r_sqr + dist * sqrt(d_sqr + r_sqr)); + + if (is_area_light(ld.type)) { + /* Modulate by light plane orientation / solid angle. */ + power *= saturate(dot(ld._back, L)); + } + return power; +} + +float light_diffuse(sampler2DArray utility_tx, LightData ld, vec3 N, vec3 V, vec3 L, float dist) +{ + if (!is_area_light(ld.type)) { + float radius = ld._radius / dist; + return ltc_evaluate_disk_simple(utility_tx, radius, dot(N, L)); + } + else if (ld.type == LIGHT_RECT) { + vec3 corners[4]; + corners[0] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y; + corners[1] = ld._right * ld._area_size_x + ld._up * ld._area_size_y; + corners[2] = -corners[0]; + corners[3] = -corners[1]; + + corners[0] = normalize(L * dist + corners[0]); + corners[1] = normalize(L * dist + corners[1]); + corners[2] = normalize(L * dist + corners[2]); + corners[3] = normalize(L * dist + corners[3]); + + return ltc_evaluate_quad(utility_tx, corners, N); + } + else /* (ld.type == LIGHT_ELLIPSE) */ { + vec3 points[3]; + points[0] = ld._right * -ld._area_size_x + ld._up * -ld._area_size_y; + points[1] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y; + points[2] = -points[0]; + + points[0] += L * dist; + points[1] += L * dist; + points[2] += L * dist; + + return ltc_evaluate_disk(utility_tx, N, V, mat3(1.0), points); + } +} + +float light_ltc( + sampler2DArray utility_tx, LightData ld, vec3 N, vec3 V, vec3 L, float dist, vec4 ltc_mat) +{ + if (ld.type == LIGHT_RECT) { + vec3 corners[4]; + corners[0] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y; + corners[1] = ld._right * ld._area_size_x + ld._up * ld._area_size_y; + corners[2] = -corners[0]; + corners[3] = -corners[1]; + + corners[0] += L * dist; + corners[1] += L * dist; + corners[2] += L * dist; + corners[3] += L * dist; + + ltc_transform_quad(N, V, ltc_matrix(ltc_mat), corners); + + return ltc_evaluate_quad(utility_tx, corners, vec3(0.0, 0.0, 1.0)); + } + else { + vec3 Px = ld._right; + vec3 Py = ld._up; + + if (!is_area_light(ld.type)) { + make_orthonormal_basis(L, Px, Py); + } + + vec3 points[3]; + points[0] = Px * -ld._area_size_x + Py * -ld._area_size_y; + points[1] = Px * ld._area_size_x + Py * -ld._area_size_y; + points[2] = -points[0]; + + points[0] += L * dist; + points[1] += L * dist; + points[2] += L * dist; + + return ltc_evaluate_disk(utility_tx, N, V, ltc_matrix(ltc_mat), points); + } +} + +vec3 light_translucent(sampler1D transmittance_tx, + LightData ld, + vec3 N, + vec3 L, + float dist, + vec3 sss_radius, + float delta) +{ + /* TODO(fclem): We should compute the power at the entry point. */ + /* NOTE(fclem): we compute the light attenuation using the light vector but the transmittance + * using the shadow depth delta. */ + float power = light_point_light(ld, L, dist); + /* Do not add more energy on front faces. Also apply lambertian BSDF. */ + power *= max(0.0, dot(-N, L)) * M_1_PI; + + sss_radius *= SSS_TRANSMIT_LUT_RADIUS; + vec3 channels_co = saturate(delta / sss_radius) * SSS_TRANSMIT_LUT_SCALE + SSS_TRANSMIT_LUT_BIAS; + + vec3 translucency; + translucency.x = (sss_radius.x > 0.0) ? texture(transmittance_tx, channels_co.x).r : 0.0; + translucency.y = (sss_radius.y > 0.0) ? texture(transmittance_tx, channels_co.y).r : 0.0; + translucency.z = (sss_radius.z > 0.0) ? texture(transmittance_tx, channels_co.z).r : 0.0; + return translucency * power; +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_cubemap_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_cubemap_frag.glsl new file mode 100644 index 00000000000..e19063d562a --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_cubemap_frag.glsl @@ -0,0 +1,26 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_cubemap_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_lightprobe_display_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +uniform samplerCubeArray lightprobe_cube_tx; + +layout(location = 0) out vec4 out_color; + +void main() +{ + float len_sqr = len_squared(interp.coord); + if (len_sqr > 1.0) { + discard; + } + + vec3 vN = vec3(interp.coord, sqrt(1.0 - len_sqr)); + vec3 N = normal_view_to_world(vN); + + vec3 V = cameraVec(interp.P); + vec3 R = -reflect(V, N); + + out_color.rgb = cubemap_array_sample(lightprobe_cube_tx, vec4(R, interp.sample), 0.0).rgb; + out_color.a = 0.0; +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_cubemap_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_cubemap_vert.glsl new file mode 100644 index 00000000000..a1e8232908f --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_cubemap_vert.glsl @@ -0,0 +1,41 @@ + +/** + * Generate camera-facing quad procedurally for each reflection cubemap of the lightcache. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_lightprobe_display_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(std140) uniform cubes_block +{ + CubemapData cubes[CULLING_ITEM_BATCH]; +}; + +layout(std140) uniform lightprobes_info_block +{ + LightProbeInfoData probes_info; +}; + +const vec2 pos[6] = vec2[6](vec2(-1.0, -1.0), + vec2(1.0, -1.0), + vec2(-1.0, 1.0), + + vec2(1.0, -1.0), + vec2(1.0, 1.0), + vec2(-1.0, 1.0)); + +void main(void) +{ + interp.sample = 1 + (gl_VertexID / 6); + interp.coord = pos[gl_VertexID % 6]; + + CubemapData cube = cubes[interp.sample]; + + vec3 quad = vec3(interp.coord * probes_info.cubes.display_size * 0.5, 0.0); + + interp.P = vec3(cube._world_position_x, cube._world_position_y, cube._world_position_z); + interp.P += transform_direction(ViewMatrixInverse, quad); + + gl_Position = point_world_to_ndc(interp.P); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_grid_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_grid_frag.glsl new file mode 100644 index 00000000000..fd2322c9acd --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_grid_frag.glsl @@ -0,0 +1,29 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_irradiance_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_lightprobe_display_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(std140) uniform lightprobes_info_block +{ + LightProbeInfoData probes_info; +}; + +uniform sampler2DArray lightprobe_grid_tx; +uniform int grid_id; + +layout(location = 0) out vec4 out_color; + +void main() +{ + float len_sqr = len_squared(interp.coord); + if (len_sqr > 1.0) { + discard; + } + + vec3 vN = vec3(interp.coord, sqrt(1.0 - len_sqr)); + vec3 N = normal_view_to_world(vN); + + out_color.rgb = irradiance_load_cell(probes_info.grids, lightprobe_grid_tx, interp.sample, N); + out_color.a = 0.0; +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_grid_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_grid_vert.glsl new file mode 100644 index 00000000000..a04c280526e --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_grid_vert.glsl @@ -0,0 +1,53 @@ + +/** + * Generate camera-facing quad procedurally for each irradiance sample of the lightcache. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_lightprobe_display_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(std140) uniform grids_block +{ + GridData grids[GRID_MAX]; +}; + +layout(std140) uniform lightprobes_info_block +{ + LightProbeInfoData probes_info; +}; + +uniform int grid_id; + +const vec2 pos[6] = vec2[6](vec2(-1.0, -1.0), + vec2(1.0, -1.0), + vec2(-1.0, 1.0), + + vec2(1.0, -1.0), + vec2(1.0, 1.0), + vec2(-1.0, 1.0)); + +void main(void) +{ + + interp.sample = gl_VertexID / 6; + interp.coord = pos[gl_VertexID % 6]; + + GridData grid = grids[grid_id]; + + ivec3 cell_coord = grid_cell_index_to_coordinate(interp.sample, grid.resolution); + + interp.sample += grid.offset; + + mat4 cell_to_world = mat4(vec4(grid.increment_x, 0.0), + vec4(grid.increment_y, 0.0), + vec4(grid.increment_z, 0.0), + vec4(grid.corner, 1.0)); + + vec3 quad = vec3(interp.coord * probes_info.grids.display_size * 0.5, 0.0); + + interp.P = transform_point(cell_to_world, vec3(cell_coord)); + interp.P += transform_direction(ViewMatrixInverse, quad); + + gl_Position = point_world_to_ndc(interp.P); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_lib.glsl new file mode 100644 index 00000000000..9999ee45794 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_display_lib.glsl @@ -0,0 +1,8 @@ + +IN_OUT LightProbeDisplayInterface +{ + vec3 P; + vec2 coord; + flat int sample; +} +interp; diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_eval_cubemap_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_eval_cubemap_lib.glsl new file mode 100644 index 00000000000..16f5004308b --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_eval_cubemap_lib.glsl @@ -0,0 +1,89 @@ + +/** + * This is an eval function that needs to be added after main fragment shader. + * A prototype needs to be declared before main in order to use it. + * + * The resources expected to be defined are: + * - probes_info + * - lightprobe_cube_tx + * - cubes + * + * All of this is needed to avoid using macros and performance issues with large + * arrays as function arguments. + */ + +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_cubemap_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +float lightprobe_cubemap_weight(CubemapData cube, vec3 P) +{ + /* Composed transform to remove the packed data. */ + vec3 lP = transform_direction(cube.influence_mat, P) + cube.influence_mat[3].xyz; + float attenuation; + if (cube._attenuation_type == CUBEMAP_SHAPE_SPHERE) { + attenuation = saturate(cube._attenuation_factor * (1.0 - length(lP))); + } + else { + attenuation = min_v3(saturate(cube._attenuation_factor * (1.0 - abs(lP)))); + } + return attenuation; +} + +vec3 lightprobe_cubemap_evaluate(CubemapInfoData info, + samplerCubeArray cubemap_tx, + CubemapData cube, + vec3 P, + vec3 R, + float roughness) +{ + float linear_roughness = fast_sqrt(roughness); + if (cube._layer > 0.0) { + /* Correct reflection ray using parallax volume intersection. */ + vec3 lR = transform_direction(cube.parallax_mat, R); + /* Composed transform to remove the packed data. */ + vec3 lP = transform_direction(cube.parallax_mat, P) + cube.parallax_mat[3].xyz; + float dist; + if (cube._parallax_type == CUBEMAP_SHAPE_SPHERE) { + dist = line_unit_sphere_intersect_dist(lP, lR); + } + else { + dist = line_unit_box_intersect_dist(lP, lR); + } + vec3 cube_pos = vec3(cube._world_position_x, cube._world_position_y, cube._world_position_z); + /* Use Distance in WS directly to recover intersection. */ + vec3 intersection = (P + R * dist) - cube_pos; + /* Distance based roughness from Frostbite PBR Course. + * http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf + */ + float original_roughness = roughness; + float distance_roughness = saturate(dist * linear_roughness / length(intersection)); + linear_roughness = mix(distance_roughness, linear_roughness, linear_roughness); + roughness = linear_roughness * linear_roughness; + + float fac = saturate(original_roughness * 2.0 - 1.0); + R = mix(intersection, R, fac * fac); + } + else { + R = transform_direction(info.lookdev_rotation, R); + } + float lod = linear_roughness * info.roughness_max_lod; + return cubemap_array_sample(cubemap_tx, vec4(R, cube._layer), lod).rgb; +} + +vec3 lightprobe_cubemap_eval(vec3 P, vec3 R, float roughness, float random_threshold) +{ + /* Go through all cubemaps, computing and adding their weights for this pixel + * until reaching a random threshold. */ + float weight = 0.0; + int cube_index = probes_info.cubes.cube_count - 1; + for (; cube_index > 0; cube_index--) { + weight += lightprobe_cubemap_weight(cubes[cube_index], P); + if (weight >= random_threshold) { + break; + } + } + + return lightprobe_cubemap_evaluate( + probes_info.cubes, lightprobe_cube_tx, cubes[cube_index], P, R, roughness); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_eval_grid_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_eval_grid_lib.glsl new file mode 100644 index 00000000000..a66179979d4 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_eval_grid_lib.glsl @@ -0,0 +1,105 @@ + +/** + * This is an eval function that needs to be added after main fragment shader. + * A prototype needs to be declared before main in order to use it. + * + * The resources expected to be defined are: + * - probes_info + * - lightprobe_grid_tx + * - grids + * + * All of this is needed to avoid using macros and performance issues with large + * arrays as function arguments. + */ + +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_irradiance_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +float lightprobe_grid_weight(GridData grid, vec3 P) +{ + vec3 lP = transform_point(grid.local_mat, P); + vec3 pos_to_edge = max(vec3(0.0), abs(lP) - 1.0); + float fade = length(pos_to_edge); + return saturate(-fade * grid.attenuation_scale + grid.attenuation_bias); +} + +vec3 lightprobe_grid_evaluate( + GridInfoData info, sampler2DArray grid_tx, GridData grid, vec3 P, vec3 N) +{ + vec3 lP = transform_point(grid.local_mat, P) * 0.5 + 0.5; + lP = lP * vec3(grid.resolution) - 0.5; + + ivec3 lP_floored = ivec3(floor(lP)); + vec3 trilinear_weight = fract(lP); + + if (grid.offset == 0) { + N = transform_direction(info.lookdev_rotation, N); + } + + float weight_accum = 0.0; + vec3 irradiance_accum = vec3(0.0); + /* For each neighbor cells */ + for (int i = 0; i < 8; i++) { + ivec3 offset = ivec3(i, i >> 1, i >> 2) & ivec3(1); + ivec3 cell_coord = clamp(lP_floored + offset, ivec3(0), grid.resolution - 1); + /* Skip cells not yet rendered during baking. */ + cell_coord = (cell_coord / grid.level_skip) * grid.level_skip; + /* Keep in sync with update_irradiance_probe. */ + int cell_id = grid.offset + cell_coord.z + cell_coord.y * grid.resolution.z + + cell_coord.x * grid.resolution.z * grid.resolution.y; + + vec3 color = irradiance_load_cell(info, grid_tx, cell_id, N); + + /* We need this because we render probes in world space (so we need light vector in WS). + * And rendering them in local probe space is too much problem. */ + mat4 cell_to_world = mat4(vec4(grid.increment_x, 0.0), + vec4(grid.increment_y, 0.0), + vec4(grid.increment_z, 0.0), + vec4(grid.corner, 1.0)); + vec3 ws_cell_location = transform_point(cell_to_world, vec3(cell_coord)); + + vec3 ws_point_to_cell = ws_cell_location - P; + float ws_dist_point_to_cell = length(ws_point_to_cell); + vec3 ws_light = ws_point_to_cell / ws_dist_point_to_cell; + + /* Smooth backface test. */ + float weight = saturate(dot(ws_light, N)); + /* Precomputed visibility. */ + weight *= visibility_load_cell(info, + grid_tx, + cell_id, + ws_light, + ws_dist_point_to_cell, + grid.visibility_bias, + grid.visibility_bleed, + grid.visibility_range); + /* Smoother transition. */ + weight += info.irradiance_smooth; + /* Trilinear weights. */ + vec3 trilinear = mix(1.0 - trilinear_weight, trilinear_weight, offset); + weight *= trilinear.x * trilinear.y * trilinear.z; + /* Avoid zero weight. */ + weight = max(0.00001, weight); + + weight_accum += weight; + irradiance_accum += color * weight; + } + return irradiance_accum / weight_accum; +} + +vec3 lightprobe_grid_eval(vec3 P, vec3 N, float random_threshold) +{ + /* Go through all grids, computing and adding their weights for this pixel + * until reaching a random threshold. */ + float weight = 0.0; + int grid_index = probes_info.grids.grid_count - 1; + for (; grid_index > 0; grid_index--) { + weight += lightprobe_grid_weight(grids[grid_index], P); + if (weight >= random_threshold) { + break; + } + } + + return lightprobe_grid_evaluate(probes_info.grids, lightprobe_grid_tx, grids[grid_index], P, N); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_diffuse_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_diffuse_frag.glsl new file mode 100644 index 00000000000..2c044378ea6 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_diffuse_frag.glsl @@ -0,0 +1,73 @@ + +/** + * Outputs pre-convolved diffuse irradiance from an input radiance cubemap. + * Input radiance has its mipmaps updated so we can use filtered importance sampling. + * The output is a really small 6 directional basis similar to the ambient cube technique. + * Downside: very very low resolution (6 texels), bleed lighting because of interpolation. + * + * https://cdn.cloudflare.steamstatic.com/apps/valve/2006/SIGGRAPH06_Course_ShadingInValvesSourceEngine.pdf + */ + +#pragma BLENDER_REQUIRE(eevee_irradiance_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_bsdf_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_lightprobe_filter_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +uniform samplerCube radiance_tx; + +layout(std140) uniform filter_block +{ + LightProbeFilterData probe; +}; + +layout(location = 0) out vec4 out_irradiance; + +void main() +{ + int x = int(gl_FragCoord.x) % 3; + int y = int(gl_FragCoord.y) % 2; + + vec3 N, T, B; + switch (x) { + default: + N = vec3(1.0, 0.0, 0.0); + break; + case 1: + N = vec3(0.0, 1.0, 0.0); + break; + case 2: + N = vec3(0.0, 0.0, 1.0); + break; + } + + if (y == 1) { + N = -N; + } + + make_orthonormal_basis(N, T, B); + + /* Integrating Envmap. */ + float weight = 0.0; + vec3 radiance = vec3(0.0); + for (float i = 0; i < probe.sample_count; i++) { + vec3 Xi = sample_cylinder(hammersley_2d(i, probe.sample_count)); + + float pdf; + vec3 L = sample_uniform_hemisphere(Xi, N, T, B, pdf); + float NL = dot(N, L); + + if (NL > 0.0) { + /* Coarse Approximation of the mapping distortion. + * Unit Sphere -> Cubemap Face. */ + const float dist = 4.0 * M_PI / 6.0; + /* http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html : Equation 13 */ + float lod = probe.lod_bias - 0.5 * log2(pdf * dist); + + radiance += textureLod(radiance_tx, L, lod).rgb * NL; + weight += NL; + } + } + + out_irradiance = irradiance_encode(probe.instensity_fac * radiance / weight); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_downsample_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_downsample_frag.glsl new file mode 100644 index 00000000000..6db16afdbc2 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_downsample_frag.glsl @@ -0,0 +1,20 @@ + +/** + * Fullscreen pass that filter previous mipmap level using a 1 bilinear tap. + * This uses layered rendering to filter all cubeface / layers in one drawcall. + */ + +#pragma BLENDER_REQUIRE(eevee_lightprobe_filter_lib.glsl) + +#ifdef CUBEMAP +uniform samplerCube input_tx; +#else +uniform sampler2DArray input_tx; +#endif + +layout(location = 0) out vec4 out_color; + +void main(void) +{ + out_color = textureLod(input_tx, interp.coord, 0.0); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_geom.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_geom.glsl new file mode 100644 index 00000000000..bc4d4624551 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_geom.glsl @@ -0,0 +1,42 @@ + +/** + * Passthrough geometry shader to use layered rendering.. + */ + +/* TODO(fclem) Use vendor extensions to bypass geometry shader. */ + +layout(triangles) in; +layout(triangle_strip, max_vertices = 3) out; + +in FilterInterface +{ + vec3 coord; + flat int layer; +} +interp_in[]; + +out FilterInterface +{ + vec3 coord; + flat int layer; +} +interp_out; + +void main() +{ + gl_Layer = interp_in[0].layer; + + interp_out.coord = interp_in[0].coord; + gl_Position = gl_in[0].gl_Position; + EmitVertex(); + + interp_out.coord = interp_in[1].coord; + gl_Position = gl_in[1].gl_Position; + EmitVertex(); + + interp_out.coord = interp_in[2].coord; + gl_Position = gl_in[2].gl_Position; + EmitVertex(); + + EndPrimitive(); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_glossy_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_glossy_frag.glsl new file mode 100644 index 00000000000..7ad81605141 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_glossy_frag.glsl @@ -0,0 +1,67 @@ + +/** + * Outputs pre-convolved glossy BSDF irradiance from an input radiance cubemap. + * Input radiance has its mipmaps updated so we can use filtered importance sampling. + * + * Follows the principle of: + * https://developer.nvidia.com/gpugems/gpugems3/part-iii-rendering/chapter-20-gpu-based-importance-sampling + */ + +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_bsdf_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_lightprobe_filter_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +uniform samplerCube radiance_tx; + +layout(std140) uniform filter_block +{ + LightProbeFilterData probe; +}; + +layout(location = 0) out vec4 out_irradiance; + +void main() +{ + vec3 N, T, B, V; + vec3 R = normalize(interp.coord); + + /* Isotropic assumption. */ + N = V = R; + + make_orthonormal_basis(N, T, B); + + /* Integrating Envmap. */ + float weight = 0.0; + vec3 radiance = vec3(0.0); + for (float i = 0; i < probe.sample_count; i++) { + vec3 Xi = sample_cylinder(hammersley_2d(i, probe.sample_count)); + + float pdf; + vec3 L = sample_ggx_reflect(Xi, probe.roughness, V, N, T, B, pdf); + + if (pdf > 0.0) { + /* Microfacet normal. */ + vec3 H = normalize(V + L); + float NL = dot(N, L); + float NH = max(1e-8, dot(N, H)); + + /* Coarse Approximation of the mapping distortion. + * Unit Sphere -> Cubemap Face. */ + const float dist = 4.0 * M_PI / 6.0; + /* Equation 13. */ + float lod = probe.lod_bias - 0.5 * log2(pdf * dist); + + vec3 l_col = textureLod(radiance_tx, L, lod).rgb; + + /* Clamped brightness. */ + float luma = max(1e-8, max_v3(l_col)); + l_col *= 1.0 - max(0.0, luma - probe.luma_max) / luma; + + radiance += l_col * NL; + weight += NL; + } + } + + out_irradiance = vec4(probe.instensity_fac * radiance / weight, 1.0); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_lib.glsl new file mode 100644 index 00000000000..80335bcdbc6 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_lib.glsl @@ -0,0 +1,7 @@ + +IN_OUT FilterInterface +{ + vec3 coord; + flat int layer; +} +interp; diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_vert.glsl new file mode 100644 index 00000000000..9ea8e66e2a7 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_vert.glsl @@ -0,0 +1,49 @@ + +/** + * Fullscreen pass that outputs one triangle to a specific layer. + * This uses layered rendering to filter all cubeface / layers in one drawcall. + */ + +#pragma BLENDER_REQUIRE(eevee_lightprobe_filter_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(std140) uniform filter_block +{ + LightProbeFilterData probe; +}; + +void main(void) +{ + /* Fullscreen triangle. */ + int v = gl_VertexID % 3; + interp.coord.x = float((v & 1) << 2); + interp.coord.y = float((v & 2) << 1); + gl_Position = vec4(interp.coord.xy * 2.0 - 1.0, 1.0, 1.0); + + int cube_face = gl_VertexID / 3; + interp.layer = probe.target_layer + cube_face; + interp.coord.z = float(interp.layer); + +#ifdef CUBEMAP + switch (cube_face) { + case 0: + interp.coord = gl_Position.zyx * vec3(1, -1, -1); + break; + case 1: + interp.coord = gl_Position.zyx * vec3(-1, -1, 1); + break; + case 2: + interp.coord = gl_Position.xzy * vec3(1, 1, 1); + break; + case 3: + interp.coord = gl_Position.xzy * vec3(1, -1, -1); + break; + case 4: + interp.coord = gl_Position.xyz * vec3(1, -1, 1); + break; + default: + interp.coord = gl_Position.xyz * vec3(-1, -1, -1); + break; + } +#endif +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_visibility_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_visibility_frag.glsl new file mode 100644 index 00000000000..0fe784baeaf --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_lightprobe_filter_visibility_frag.glsl @@ -0,0 +1,78 @@ + +/** + * Outputs convolved visibility from an input depth cubemap. + * The output is an octahedral map which encodes depth in 4 components of 1 byte each. + */ + +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_bsdf_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_irradiance_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_lightprobe_filter_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +uniform samplerCube depth_tx; + +layout(std140) uniform filter_block +{ + LightProbeFilterData probe; +}; + +layout(location = 0) out vec4 out_irradiance; + +vec3 octahedral_to_cubemap_proj(vec2 co) +{ + co = co * 2.0 - 1.0; + + vec2 abs_co = abs(co); + vec3 v = vec3(co, 1.0 - (abs_co.x + abs_co.y)); + + if (abs_co.x + abs_co.y > 1.0) { + v.xy = (abs(co.yx) - 1.0) * -sign(co.xy); + } + + return v; +} + +float get_world_distance(float depth, vec3 coords) +{ + float is_background = step(1.0, depth); + depth = get_view_z_from_depth(depth); + depth += 1e1 * is_background; + coords = normalize(abs(coords)); + float cos_vec = max_v3(coords); + return depth / cos_vec; +} + +void main() +{ + vec2 uv = interp.coord.xy; + vec2 stored_texel_size = dFdx(uv); + /* Add a 1 pixel border all around the octahedral map to ensure filtering is correct. */ + uv = (uv - stored_texel_size) / (1.0 - 2.0 * stored_texel_size); + /* Edge mirroring : only mirror if directly adjacent (not diagonally adjacent). */ + vec2 m = abs(uv - 0.5) + 0.5; + vec2 f = floor(m); + if (f.x - f.y != 0.0) { + uv = 1.0 - uv; + } + uv = fract(uv); + + vec3 T, B, N; + N = normalize(octahedral_to_cubemap_proj(uv)); + make_orthonormal_basis(N, T, B); + + vec2 accum = vec2(0.0); + + for (float i = 0; i < probe.sample_count; i++) { + vec3 Xi = sample_cylinder(hammersley_2d(i, probe.sample_count)); + + vec3 dir = sample_uniform_cone(Xi, M_PI_2 * probe.visibility_blur, N, T, B); + float depth = texture(depth_tx, dir).r; + depth = get_world_distance(depth, dir); + accum += vec2(depth, depth * depth); + } + + out_irradiance = visibility_encode(abs(accum / probe.sample_count), probe.visibility_range); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_lookdev_background_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_lookdev_background_frag.glsl new file mode 100644 index 00000000000..0d8b76b5e99 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_lookdev_background_frag.glsl @@ -0,0 +1,38 @@ + +/** + * Special background shader for lookdev mode. + * It samples the world cubemap at specified blur level and can fade to fully transparent. + * + * Note: This might becode obsolete if using glossy bsdf becomes supported in world shaders. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_cubemap_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(std140) uniform lightprobes_info_block +{ + LightProbeInfoData probes_info; +}; + +uniform samplerCubeArray lightprobe_cube_tx; +uniform float opacity; +uniform float blur; + +in vec4 uvcoordsvar; + +layout(location = 0) out vec4 out_background; + +void main() +{ + vec3 vP = get_view_space_from_depth(uvcoordsvar.xy, 0.5); + vec3 vV = viewCameraVec(vP); + vec3 R = -normal_view_to_world(vV); + + R = transform_direction(probes_info.cubes.lookdev_rotation, R); + + float lod = blur * probes_info.cubes.roughness_max_lod; + out_background.rgb = cubemap_array_sample(lightprobe_cube_tx, vec4(R, 0), lod).rgb; + out_background.rgb *= opacity; + out_background.a = 1.0 - opacity; +} diff --git a/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_ltc_lib.glsl index 2750d42a74a..57e92b0b9b4 100644 --- a/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/eevee_ltc_lib.glsl @@ -1,3 +1,4 @@ + /** * Adapted from : * Real-Time Polygonal-Light Shading with Linearly Transformed Cosines. @@ -6,17 +7,15 @@ * Project page: https://eheitzresearch.wordpress.com/415-2/ */ -#define USE_LTC - /* Diffuse *clipped* sphere integral. */ -float diffuse_sphere_integral(float avg_dir_z, float form_factor) +float ltc_diffuse_sphere_integral(sampler2DArray utility_tx, float avg_dir_z, float form_factor) { #if 1 /* use tabulated horizon-clipped sphere */ vec2 uv = vec2(avg_dir_z * 0.5 + 0.5, form_factor); - uv = uv * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE; + uv = uv * UTIL_TEX_UV_SCALE + UTIL_TEX_UV_BIAS; - return texture(utilTex, vec3(uv, 3.0)).x; + return texture(utility_tx, vec3(uv, UTIL_DISK_INTEGRAL_LAYER))[UTIL_DISK_INTEGRAL_COMP]; #else /* Cheap approximation. Less smooth and have energy issues. */ return max((form_factor * form_factor + avg_dir_z) / (form_factor + 1.0), 0.0); @@ -28,7 +27,7 @@ float diffuse_sphere_integral(float avg_dir_z, float form_factor) * "How to solve a cubic equation, revisited" * http://momentsingraphics.de/?p=105 */ -vec3 solve_cubic(vec4 coefs) +vec3 ltc_solve_cubic(vec4 coefs) { /* Normalize the polynomial */ coefs.xyz /= coefs.w; @@ -119,7 +118,7 @@ vec3 solve_cubic(vec4 coefs) /* from Real-Time Area Lighting: a Journey from Research to Production * Stephen Hill and Eric Heitz */ -vec3 edge_integral_vec(vec3 v1, vec3 v2) +vec3 ltc_edge_integral_vec(vec3 v1, vec3 v2) { float x = dot(v1, v2); float y = abs(x); @@ -135,10 +134,8 @@ vec3 edge_integral_vec(vec3 v1, vec3 v2) mat3 ltc_matrix(vec4 lut) { - /* load inverse matrix */ - mat3 Minv = mat3(vec3(lut.x, 0, lut.y), vec3(0, 1, 0), vec3(lut.z, 0, lut.w)); - - return Minv; + /* Load inverse matrix. */ + return mat3(vec3(lut.x, 0, lut.y), vec3(0, 1, 0), vec3(lut.z, 0, lut.w)); } void ltc_transform_quad(vec3 N, vec3 V, mat3 Minv, inout vec3 corners[4]) @@ -146,12 +143,12 @@ void ltc_transform_quad(vec3 N, vec3 V, mat3 Minv, inout vec3 corners[4]) /* Avoid dot(N, V) == 1 in ortho mode, leading T1 normalize to fail. */ V = normalize(V + 1e-8); - /* construct orthonormal basis around N */ + /* Construct orthonormal basis around N. */ vec3 T1, T2; T1 = normalize(V - N * dot(N, V)); T2 = cross(N, T1); - /* rotate area light in (T1, T2, R) basis */ + /* Rotate area light in (T1, T2, R) basis. */ Minv = Minv * transpose(mat3(T1, T2, N)); /* Apply LTC inverse matrix. */ @@ -163,32 +160,32 @@ void ltc_transform_quad(vec3 N, vec3 V, mat3 Minv, inout vec3 corners[4]) /* If corners have already pass through ltc_transform_quad(), * then N **MUST** be vec3(0.0, 0.0, 1.0), corresponding to the Up axis of the shading basis. */ -float ltc_evaluate_quad(vec3 corners[4], vec3 N) +float ltc_evaluate_quad(sampler2DArray utility_tx, vec3 corners[4], vec3 N) { /* Approximation using a sphere of the same solid angle than the quad. * Finding the clipped sphere diffuse integral is easier than clipping the quad. */ vec3 avg_dir; - avg_dir = edge_integral_vec(corners[0], corners[1]); - avg_dir += edge_integral_vec(corners[1], corners[2]); - avg_dir += edge_integral_vec(corners[2], corners[3]); - avg_dir += edge_integral_vec(corners[3], corners[0]); + avg_dir = ltc_edge_integral_vec(corners[0], corners[1]); + avg_dir += ltc_edge_integral_vec(corners[1], corners[2]); + avg_dir += ltc_edge_integral_vec(corners[2], corners[3]); + avg_dir += ltc_edge_integral_vec(corners[3], corners[0]); float form_factor = length(avg_dir); float avg_dir_z = dot(N, avg_dir / form_factor); - return form_factor * diffuse_sphere_integral(avg_dir_z, form_factor); + return form_factor * ltc_diffuse_sphere_integral(utility_tx, avg_dir_z, form_factor); } /* If disk does not need to be transformed and is already front facing. */ -float ltc_evaluate_disk_simple(float disk_radius, float NL) +float ltc_evaluate_disk_simple(sampler2DArray utility_tx, float disk_radius, float NL) { float r_sqr = disk_radius * disk_radius; float one_r_sqr = 1.0 + r_sqr; float form_factor = r_sqr * inversesqrt(one_r_sqr * one_r_sqr); - return form_factor * diffuse_sphere_integral(NL, form_factor); + return form_factor * ltc_diffuse_sphere_integral(utility_tx, NL, form_factor); } /* disk_points are WS vectors from the shading point to the disk "bounding domain" */ -float ltc_evaluate_disk(vec3 N, vec3 V, mat3 Minv, vec3 disk_points[3]) +float ltc_evaluate_disk(sampler2DArray utility_tx, vec3 N, vec3 V, mat3 Minv, vec3 disk_points[3]) { /* Avoid dot(N, V) == 1 in ortho mode, leading T1 normalize to fail. */ V = normalize(V + 1e-8); @@ -280,7 +277,7 @@ float ltc_evaluate_disk(vec3 N, vec3 V, mat3 Minv, vec3 disk_points[3]) float c2 = (1.0 - a * t) - b * (1.0 + y0 * y0); float c3 = 1.0; - vec3 roots = solve_cubic(vec4(c0, c1, c2, c3)); + vec3 roots = ltc_solve_cubic(vec4(c0, c1, c2, c3)); float e1 = roots.x; float e2 = roots.y; float e3 = roots.z; @@ -298,5 +295,5 @@ float ltc_evaluate_disk(vec3 N, vec3 V, mat3 Minv, vec3 disk_points[3]) /* Find the sphere and compute lighting. */ float form_factor = max(0.0, L1 * L2 * inversesqrt((1.0 + L1 * L1) * (1.0 + L2 * L2))); - return form_factor * diffuse_sphere_integral(avg_dir.z, form_factor); + return form_factor * ltc_diffuse_sphere_integral(utility_tx, avg_dir.z, form_factor); } diff --git a/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_gather_frag.glsl index 0aa54715460..8aaf21201f8 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_gather_frag.glsl @@ -1,7 +1,6 @@ -#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) - -/* +/** + * Perform two gather blur in the 2 motion blur directions * Based on: * A Fast and Stable Feature-Aware Motion Blur Filter * by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai @@ -10,29 +9,33 @@ * Next Generation Post Processing in Call of Duty Advanced Warfare * by Jorge Jimenez */ -uniform sampler2D colorBuffer; -uniform sampler2D depthBuffer; -uniform sampler2D velocityBuffer; -uniform sampler2D tileMaxBuffer; -#define KERNEL 8 +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_motion_blur_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(std140) uniform sampling_block +{ + SamplingData sampling; +}; -uniform float depthScale; -uniform ivec2 tileBufferSize; -uniform vec2 viewportSize; -uniform vec2 viewportSizeInv; -uniform bool isPerspective; -uniform vec2 nearFar; /* Near & far view depths values */ +layout(std140) uniform motion_blur_block +{ + MotionBlurData mb; +}; -#define linear_depth(z) \ - ((isPerspective) ? (nearFar.x * nearFar.y) / (z * (nearFar.x - nearFar.y) + nearFar.y) : \ - z * (nearFar.y - nearFar.x) + nearFar.x) /* Only true for camera view! */ +uniform sampler2D color_tx; +uniform sampler2D depth_tx; +uniform sampler2D velocity_tx; +uniform sampler2D tiles_tx; in vec4 uvcoordsvar; -out vec4 fragColor; +layout(location = 0) out vec4 out_color; -#define saturate(a) clamp(a, 0.0, 1.0) +const int gather_sample_count = 8; vec2 spread_compare(float center_motion_length, float sample_motion_length, float offset_length) { @@ -41,7 +44,7 @@ vec2 spread_compare(float center_motion_length, float sample_motion_length, floa vec2 depth_compare(float center_depth, float sample_depth) { - return saturate(0.5 + vec2(depthScale, -depthScale) * (sample_depth - center_depth)); + return saturate(0.5 + vec2(-mb.depth_scale, mb.depth_scale) * (sample_depth - center_depth)); } /* Kill contribution if not going the same direction. */ @@ -67,29 +70,6 @@ vec2 sample_weights(float center_depth, return depth_weight * spread_weight; } -vec4 decode_velocity(vec4 velocity) -{ - velocity = velocity * 2.0 - 1.0; - /* Needed to match cycles. Can't find why... (fclem) */ - velocity *= 0.5; - /* Transpose to pixelspace. */ - velocity *= viewportSize.xyxy; - return velocity; -} - -vec4 sample_velocity(vec2 uv) -{ - vec4 data = texture(velocityBuffer, uv); - return decode_velocity(data); -} - -vec2 sample_velocity(vec2 uv, const bool next) -{ - vec4 data = sample_velocity(uv); - data.xy = (next ? data.zw : data.xy); - return data.xy; -} - void gather_sample(vec2 screen_uv, float center_depth, float center_motion_len, @@ -100,11 +80,16 @@ void gather_sample(vec2 screen_uv, inout vec4 accum_bg, inout vec3 w_accum) { - vec2 sample_uv = screen_uv - offset * viewportSizeInv; - vec2 sample_motion = sample_velocity(sample_uv, next); + vec2 sample_uv = screen_uv - offset * mb.target_size_inv; + vec2 sample_motion = sample_velocity(mb, velocity_tx, sample_uv, next); float sample_motion_len = length(sample_motion); - float sample_depth = linear_depth(texture(depthBuffer, sample_uv).r); - vec4 col = textureLod(colorBuffer, sample_uv, 0.0); + float sample_depth = texture(depth_tx, sample_uv).r; + vec4 sample_color = textureLod(color_tx, sample_uv, 0.0); + + /* Meh, a quirk of the motion vector pass... */ + sample_motion = (next) ? -sample_motion : sample_motion; + + sample_depth = get_view_z_from_depth(sample_depth); vec3 weights; weights.xy = sample_weights( @@ -112,8 +97,8 @@ void gather_sample(vec2 screen_uv, weights.z = dir_compare(offset, sample_motion, sample_motion_len); weights.xy *= weights.z; - accum += col * weights.y; - accum_bg += col * weights.x; + accum += sample_color * weights.y; + accum_bg += sample_color * weights.x; w_accum += weights; } @@ -142,8 +127,8 @@ void gather_blur(vec2 screen_uv, } int i; - float t, inc = 1.0 / float(KERNEL); - for (i = 0, t = ofs * inc; i < KERNEL; i++, t += inc) { + float t, inc = 1.0 / float(gather_sample_count); + for (i = 0, t = ofs * inc; i < gather_sample_count; i++, t += inc) { gather_sample(screen_uv, center_depth, center_motion_len, @@ -159,7 +144,7 @@ void gather_blur(vec2 screen_uv, return; } - for (i = 0, t = ofs * inc; i < KERNEL; i++, t += inc) { + for (i = 0, t = ofs * inc; i < gather_sample_count; i++, t += inc) { /* Also sample in center motion direction. * Allow recovering motion where there is conflicting * motion between foreground and background. */ @@ -180,19 +165,22 @@ void main() vec2 uv = uvcoordsvar.xy; /* Data of the center pixel of the gather (target). */ - float center_depth = linear_depth(texture(depthBuffer, uv).r); - vec4 center_motion = sample_velocity(uv); - vec4 center_color = textureLod(colorBuffer, uv, 0.0); + float center_depth = get_view_z_from_depth(texture(depth_tx, uv).r); + vec4 center_motion = sample_velocity(mb, velocity_tx, ivec2(gl_FragCoord.xy)); + + vec4 center_color = textureLod(color_tx, uv, 0.0); - vec2 rand = texelfetch_noise_tex(gl_FragCoord.xy).xy; + float noise_offset = sampling_rng_1D_get(sampling, SAMPLING_TIME); + /** TODO(fclem) Blue noise. */ + vec2 rand = vec2(interlieved_gradient_noise(gl_FragCoord.xy, 0, noise_offset), + interlieved_gradient_noise(gl_FragCoord.xy, 1, noise_offset)); /* Randomize tile boundary to avoid ugly discontinuities. Randomize 1/4th of the tile. * Note this randomize only in one direction but in practice it's enough. */ rand.x = rand.x * 2.0 - 1.0; - ivec2 tile = ivec2(gl_FragCoord.xy + rand.x * float(EEVEE_VELOCITY_TILE_SIZE) * 0.25) / - EEVEE_VELOCITY_TILE_SIZE; - tile = clamp(tile, ivec2(0), tileBufferSize - 1); - vec4 max_motion = decode_velocity(texelFetch(tileMaxBuffer, tile, 0)); + ivec2 tile = ivec2(gl_FragCoord.xy + rand.x * float(MB_TILE_DIVISOR) * 0.25) / MB_TILE_DIVISOR; + tile = clamp(tile, ivec2(0), textureSize(tiles_tx, 0) - 1); + vec4 max_motion = texelFetch(tiles_tx, tile, 0); /* First (center) sample: time = T */ /* x: Background, y: Foreground, z: dir. */ @@ -204,11 +192,11 @@ void main() uv, center_motion.xy, center_depth, max_motion.xy, rand.y, false, accum, accum_bg, w_accum); /* Second linear gather. time = [T, T + delta] */ gather_blur( - uv, center_motion.zw, center_depth, max_motion.zw, rand.y, true, accum, accum_bg, w_accum); + uv, -center_motion.zw, center_depth, -max_motion.zw, rand.y, true, accum, accum_bg, w_accum); -#if 1 +#if 1 /* Own addition. Not present in reference implementation. */ /* Avoid division by 0.0. */ - float w = 1.0 / (50.0 * float(KERNEL) * 4.0); + float w = 1.0 / (50.0 * float(gather_sample_count) * 4.0); accum_bg += center_color * w; w_accum.x += w; /* NOTE: In Jimenez's presentation, they used center sample. @@ -223,10 +211,10 @@ void main() /* Balance accumulation for failed samples. * We replace the missing foreground by the background. */ float blend_fac = saturate(1.0 - w_accum.y / w_accum.z); - fragColor = (accum / w_accum.z) + center_color * blend_fac; + out_color = (accum / w_accum.z) + center_color * blend_fac; #if 0 /* For debugging. */ - fragColor.rgb = fragColor.ggg; - fragColor.rg += max_motion.xy; + out_color.rgb = out_color.ggg; + out_color.rg += max_motion.xy; #endif } diff --git a/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_lib.glsl new file mode 100644 index 00000000000..f88b8dee5d7 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_lib.glsl @@ -0,0 +1,18 @@ + +#pragma BLENDER_REQUIRE(eevee_motion_blur_lib.glsl) + +/* Converts uv velocity into pixel space. Assumes velocity_tx is the same resolution as the + * target post-fx framebuffer. */ +vec4 sample_velocity(MotionBlurData mb, sampler2D velocity_tx, ivec2 texel) +{ + vec4 velocity = texelFetch(velocity_tx, texel, 0); + velocity *= vec2(textureSize(velocity_tx, 0)).xyxy; + velocity = (mb.is_viewport) ? velocity.xyxy : velocity; + return velocity; +} +vec2 sample_velocity(MotionBlurData mb, sampler2D velocity_tx, vec2 uv, const bool next) +{ + vec4 velocity = texture(velocity_tx, uv); + velocity *= vec2(textureSize(velocity_tx, 0)).xyxy; + return (next && !mb.is_viewport) ? velocity.zw : velocity.xy; +} diff --git a/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_tiles_dilate_frag.glsl index 300477570d0..786d6131eaa 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_tiles_dilate_frag.glsl @@ -1,5 +1,7 @@ + /** - * Shaders that down-sample velocity buffer, + * Samples a 3x3 tile neighborhood to find potentially intersecting motions. + * Outputs the largest intersecting motion vector in the neighboorhod. * * Based on: * A Fast and Stable Feature-Aware Motion Blur Filter @@ -8,57 +10,17 @@ * Adapted from G3D Innovation Engine implementation. */ -uniform sampler2D velocityBuffer; -uniform vec2 viewportSize; -uniform vec2 viewportSizeInv; -uniform ivec2 velocityBufferSize; - -out vec4 tileMaxVelocity; - -vec4 sample_velocity(ivec2 texel) -{ - texel = clamp(texel, ivec2(0), velocityBufferSize - 1); - vec4 data = texelFetch(velocityBuffer, texel, 0); - /* Decode data. */ - return (data * 2.0 - 1.0) * viewportSize.xyxy; -} +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) -vec4 encode_velocity(vec4 velocity) +layout(std140) uniform motion_blur_block { - return velocity * viewportSizeInv.xyxy * 0.5 + 0.5; -} + MotionBlurData mb; +}; -#ifdef TILE_GATHER +uniform sampler2D tiles_tx; -uniform ivec2 gatherStep; - -void main() -{ - vec4 max_motion = vec4(0.0); - float max_motion_len_sqr_prev = 0.0; - float max_motion_len_sqr_next = 0.0; - ivec2 texel = ivec2(gl_FragCoord.xy); - texel = texel * gatherStep.yx + texel * EEVEE_VELOCITY_TILE_SIZE * gatherStep; - - for (int i = 0; i < EEVEE_VELOCITY_TILE_SIZE; ++i) { - vec4 motion = sample_velocity(texel + i * gatherStep); - float motion_len_sqr_prev = dot(motion.xy, motion.xy); - float motion_len_sqr_next = dot(motion.zw, motion.zw); - - if (motion_len_sqr_prev > max_motion_len_sqr_prev) { - max_motion_len_sqr_prev = motion_len_sqr_prev; - max_motion.xy = motion.xy; - } - if (motion_len_sqr_next > max_motion_len_sqr_next) { - max_motion_len_sqr_next = motion_len_sqr_next; - max_motion.zw = motion.zw; - } - } - - tileMaxVelocity = encode_velocity(max_motion); -} - -#else /* TILE_EXPANSION */ +layout(location = 0) out vec4 out_max_motion; bool neighbor_affect_this_tile(ivec2 offset, vec2 velocity) { @@ -117,35 +79,35 @@ bool neighbor_affect_this_tile(ivec2 offset, vec2 velocity) */ void main() { - vec4 max_motion = vec4(0.0); + ivec2 tile = ivec2(gl_FragCoord.xy); + ivec2 texture_bounds = textureSize(tiles_tx, 0) - 1; + + out_max_motion = vec4(0.0); float max_motion_len_sqr_prev = -1.0; float max_motion_len_sqr_next = -1.0; - ivec2 tile = ivec2(gl_FragCoord.xy); ivec2 offset = ivec2(0); for (offset.y = -1; offset.y <= 1; ++offset.y) { for (offset.x = -1; offset.x <= 1; ++offset.x) { - vec4 motion = sample_velocity(tile + offset); - float motion_len_sqr_prev = dot(motion.xy, motion.xy); - float motion_len_sqr_next = dot(motion.zw, motion.zw); + ivec2 sample_tile = clamp(tile + offset, ivec2(0), texture_bounds); + vec4 motion = texelFetch(tiles_tx, sample_tile, 0); + + float motion_len_sqr_prev = len_squared(motion.xy); + float motion_len_sqr_next = len_squared(motion.zw); if (motion_len_sqr_prev > max_motion_len_sqr_prev) { if (neighbor_affect_this_tile(offset, motion.xy)) { max_motion_len_sqr_prev = motion_len_sqr_prev; - max_motion.xy = motion.xy; + out_max_motion.xy = motion.xy; } } if (motion_len_sqr_next > max_motion_len_sqr_next) { if (neighbor_affect_this_tile(offset, motion.zw)) { max_motion_len_sqr_next = motion_len_sqr_next; - max_motion.zw = motion.zw; + out_max_motion.zw = motion.zw; } } } } - - tileMaxVelocity = encode_velocity(max_motion); } - -#endif diff --git a/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_tiles_flatten_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_tiles_flatten_frag.glsl new file mode 100644 index 00000000000..0cf380d6191 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_motion_blur_tiles_flatten_frag.glsl @@ -0,0 +1,51 @@ + +/** + * Shaders that down-sample velocity buffer into squared tile of MB_TILE_DIVISOR pixels wide. + * Outputs the largest motion vector in the tile area. + * + * Based on: + * A Fast and Stable Feature-Aware Motion Blur Filter + * by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai + * + * Adapted from G3D Innovation Engine implementation. + */ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_motion_blur_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(std140) uniform motion_blur_block +{ + MotionBlurData mb; +}; + +uniform sampler2D velocity_tx; + +layout(location = 0) out vec4 out_max_motion; + +void main() +{ + ivec2 texture_bounds = textureSize(velocity_tx, 0) - 1; + ivec2 tile_co = ivec2(gl_FragCoord.xy); + + float max_motion_len_sqr_prev = -1.0; + float max_motion_len_sqr_next = -1.0; + for (int x = 0; x < MB_TILE_DIVISOR; x++) { + for (int y = 0; y < MB_TILE_DIVISOR; y++) { + ivec2 sample_texel = tile_co * MB_TILE_DIVISOR + ivec2(x, y); + vec4 motion = sample_velocity(mb, velocity_tx, min(sample_texel, texture_bounds)); + + float motion_len_sqr_prev = len_squared(motion.xy); + float motion_len_sqr_next = len_squared(motion.zw); + + if (motion_len_sqr_prev > max_motion_len_sqr_prev) { + max_motion_len_sqr_prev = motion_len_sqr_prev; + out_max_motion.xy = motion.xy; + } + if (motion_len_sqr_next > max_motion_len_sqr_next) { + max_motion_len_sqr_next = motion_len_sqr_next; + out_max_motion.zw = motion.zw; + } + } + } +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_nodetree_eval_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_nodetree_eval_lib.glsl new file mode 100644 index 00000000000..5bc3a1ecc0f --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_nodetree_eval_lib.glsl @@ -0,0 +1,97 @@ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_closure_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl) + +/* Globals used by closure. */ +ClosureDiffuse g_diffuse_data; +ClosureReflection g_reflection_data; +ClosureRefraction g_refraction_data; +ClosureVolume g_volume_data; +ClosureEmission g_emission_data; +ClosureTransparency g_transparency_data; + +struct GlobalData { + /** World position. */ + vec3 P; + /** Surface Normal. */ + vec3 N; + /** Geometric Normal. */ + vec3 Ng; + /** Barycentric coordinates. */ + vec2 barycentric_coords; + vec3 barycentric_dists; + /** Ray properties (approximation). */ + int ray_type; + float ray_depth; + float ray_length; + /** Random number to sample a closure. */ + float closure_rand; + float transmit_rand; + /** Hair time along hair length. 0 at base 1 at tip. */ + float hair_time; + /** Hair time along width of the hair. */ + float hair_time_width; + /** Hair thickness in world space. */ + float hair_thickness; + /** Index of the strand for per strand effects. */ + int hair_strand_id; + /** Is hair. */ + bool is_strand; +}; + +GlobalData g_data; + +void ntree_eval_init() +{ + g_diffuse_data.color = vec3(0.0); + g_diffuse_data.N = vec3(1.0, 0.0, 0.0); + g_diffuse_data.sss_radius = vec3(0); + g_diffuse_data.sss_id = 0u; + + g_reflection_data.color = vec3(0.0); + g_reflection_data.N = vec3(1.0, 0.0, 0.0); + g_reflection_data.roughness = 0.5; + + g_refraction_data.color = vec3(0.0); + g_refraction_data.N = vec3(1.0, 0.0, 0.0); + g_refraction_data.roughness = 0.5; + + g_volume_data.emission = vec3(0.0); + g_volume_data.scattering = vec3(0.0); + g_volume_data.transmittance = vec3(1.0); + g_volume_data.anisotropy = 0.0; + + g_emission_data.emission = vec3(0.0); + + g_transparency_data.transmittance = vec3(0.0); + g_transparency_data.holdout = 0.0; +} + +void ntree_eval_weights() +{ + float transmit_total = g_diffuse_data.color.r + g_refraction_data.color.r; + if (g_data.transmit_rand >= 0.0) { + float transmit_threshold = g_diffuse_data.color.r * safe_rcp(transmit_total); + if (g_data.transmit_rand > transmit_threshold) { + /* Signal that we will use the transmition closure. */ + g_data.transmit_rand = 0.0; + } + else { + /* Signal that we will use the diffuse closure. */ + g_data.transmit_rand = 1.0; + } + } + + closure_weight_randomize(g_diffuse_data, g_data.closure_rand); + closure_weight_randomize(g_reflection_data, g_data.closure_rand); + closure_weight_randomize(g_refraction_data, g_data.closure_rand); + + /* Amend total weight to avoid loosing energy. */ + if (g_data.transmit_rand > 0.0) { + g_diffuse_data.color.r += g_refraction_data.color.r; + } + else if (g_data.transmit_rand == 0.0) { + g_refraction_data.color.r += g_diffuse_data.color.r; + } +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_raytrace_denoise_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_denoise_comp.glsl new file mode 100644 index 00000000000..96718358f6a --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_denoise_comp.glsl @@ -0,0 +1,260 @@ + +/** + * Spatial ray reuse. Denoise raytrace result using ratio estimator. + * Also add in temporal reuse. + * + * Input: Ray direction * hit time, Ray radiance, Ray hit depth + * Ouput: Ray radiance reconstructed, Mean Ray hit depth, Radiance Variance + * + * Shader is specialized depending on the type of ray to denoise. + * + * Following "Stochastic All The Things: Raytracing in Hybrid Real-Time Rendering" + * by Tomasz Stachowiak + * https://www.ea.com/seed/news/seed-dd18-presentation-slides-raytracing + */ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_bsdf_microfacet_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_raytrace_resolve_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(local_size_x = 8, local_size_y = 8) in; + +layout(std140) uniform raytrace_block +{ + RaytraceData raytrace; +}; + +layout(std140) uniform rtbuffer_block +{ + RaytraceBufferData rtbuffer; +}; + +layout(std140) uniform hiz_block +{ + HiZData hiz; +}; + +uniform sampler2D hiz_tx; +uniform sampler2D ray_data_tx; +uniform sampler2D ray_radiance_tx; +uniform sampler2D cl_color_tx; +uniform sampler2D cl_normal_tx; +uniform sampler2D cl_data_tx; +uniform sampler2D ray_history_tx; +uniform sampler2D ray_variance_tx; + +layout(rgba16f) restrict uniform image2D out_history_img; +layout(r8) restrict uniform image2D out_variance_img; + +//#define USE_HISTORY + +void history_weigh_and_accumulate(vec3 rgb_history, + vec3 rgb_min, + vec3 rgb_max, + vec3 rgb_mean, + vec3 rgb_deviation, + inout vec3 rgb_accum, + inout float weight_accum) +{ + /* Basically tells us how much the given sample is inside the range of values + * we found during spatial reconstruction. */ + /* Slide 46. */ + vec3 dist = (rgb_history - rgb_mean) * safe_rcp(rgb_deviation); + float weight = exp2(-10.0 * max_v3(dist)); + /* Slide 47. */ + rgb_history = clamp(rgb_history, rgb_min, rgb_max); + +#ifdef USE_HISTORY + rgb_accum += rgb_history * weight; + weight_accum += weight; +#endif +} + +void main(void) +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + ivec2 img_size = textureSize(ray_data_tx, 0).xy; + + if (any(greaterThan(texel, img_size))) { + return; + } + + /* Skip pixels that have not been raytraced. */ + if (texelFetch(ray_data_tx, texel, 0).w == 0.0) { + imageStore(out_history_img, texel, vec4(0.0)); + imageStore(out_variance_img, texel, vec4(0.0)); + return; + } + + vec2 uv = vec2(texel) / vec2(img_size); + float gbuffer_depth = texelFetch(hiz_tx, texel, 0).r; + vec3 vP = get_view_space_from_depth(uv, gbuffer_depth); + vec3 vV = viewCameraVec(vP); + vec2 texel_size = hiz.pixel_to_ndc * 0.5; + + int sample_count = resolve_sample_max; +#if defined(DIFFUSE) + vec4 tra_col_in = texture(cl_color_tx, uv); + vec4 tra_nor_in = texture(cl_normal_tx, uv); + vec4 tra_dat_in = vec4(0.0); /* UNUSED */ + + ClosureDiffuse diffuse = gbuffer_load_diffuse_data(tra_col_in, tra_nor_in, tra_dat_in); + + if (diffuse.sss_radius.r < 0.0) { + /* Refraction pixel. */ + imageStore(out_history_img, texel, vec4(0.0)); + imageStore(out_variance_img, texel, vec4(0.0)); + return; + } + + vec3 vN = transform_direction(ViewMatrix, diffuse.N); + vec3 color = diffuse.color; + +#elif defined(REFRACTION) + vec4 tra_col_in = texture(cl_color_tx, uv); + vec4 tra_nor_in = texture(cl_normal_tx, uv); + vec4 tra_dat_in = texture(cl_data_tx, uv); + + ClosureRefraction refraction = gbuffer_load_refraction_data(tra_col_in, tra_nor_in, tra_dat_in); + + if (refraction.ior == -1.0) { + /* Diffuse/SSS pixel. */ + imageStore(out_history_img, texel, vec4(0.0)); + imageStore(out_variance_img, texel, vec4(0.0)); + return; + } + float thickness; + gbuffer_load_global_data(tra_nor_in, thickness); + + vec3 vN = transform_direction(ViewMatrix, refraction.N); + float roughness_sqr = max(1e-3, sqr(refraction.roughness)); + vec3 color = refraction.color; + + /* TODO(fclem): Unfortunately Refraction ray reuse does not work great for some reasons. + * To investigate. */ + sample_count = 1; +#else + ClosureReflection reflection = gbuffer_load_reflection_data(cl_color_tx, cl_normal_tx, uv); + + vec3 vN = transform_direction(ViewMatrix, reflection.N); + float roughness_sqr = max(1e-3, sqr(reflection.roughness)); + vec3 color = reflection.color; + + if (roughness_sqr == 1e-3) { + sample_count = 1; + } +#endif + + /* ----- SPATIAL DENOISE ----- */ + + /* Blue noise categorised into 4 sets of samples. + * See "Stochastic all the things" presentation slide 32-37. */ + int sample_pool = int((gl_GlobalInvocationID.x & 1u) + (gl_GlobalInvocationID.y & 1u) * 2u); + sample_pool = (sample_pool + raytrace.pool_offset) % 4; + int sample_id = sample_pool * resolve_sample_max; + + float hit_depth_mean = 0.0; + vec3 rgb_mean = vec3(0.0); + vec3 rgb_moment = vec3(0.0); + vec3 radiance_accum = vec3(0.0); + float weight_accum = 0.0; + for (int i = 0; i < sample_count; i++, sample_id++) { + vec2 sample_uv = uv + resolve_sample_offsets[sample_id] * texel_size; + + vec4 ray_data = texture(ray_data_tx, sample_uv); + vec4 ray_radiance = texture(ray_radiance_tx, sample_uv); + + vec3 vR = normalize(ray_data.xyz); + float ray_pdf_inv = ray_data.w; + /* Skip invalid pixels. */ + if (ray_pdf_inv == 0.0) { + continue; + } + + /* Slide 54. */ +#if defined(DIFFUSE) + float bsdf = saturate(dot(vN, vR)); +#elif defined(REFRACTION) + float bsdf = btdf_ggx(vN, vR, vV, roughness_sqr, refraction.ior); +#else + float bsdf = bsdf_ggx(vN, vR, vV, roughness_sqr); +#endif + float weight = bsdf * ray_pdf_inv; + +#ifdef REFRACTION + /* Transmit twice if thickness is set and ray is longer than thickness. */ + if (thickness > 0.0 && length(ray_data.xyz) > thickness) { + ray_radiance.rgb *= color; + } +#endif + radiance_accum += ray_radiance.rgb * weight; + weight_accum += weight; + + hit_depth_mean += ray_radiance.a; + rgb_mean += ray_radiance.rgb; + rgb_moment += sqr(ray_radiance.rgb); + } + + /* ----- TEMPORAL DENOISE ----- */ + + /* Local statistics. */ + float sample_count_inv = 1.0 / float(sample_count); + rgb_mean *= sample_count_inv; + rgb_moment *= sample_count_inv; + hit_depth_mean *= sample_count_inv; + vec3 rgb_variance = abs(rgb_moment - sqr(rgb_mean)); + vec3 rgb_deviation = sqrt(rgb_variance); + + float variance = max_v3(rgb_variance); + + radiance_accum *= safe_rcp(weight_accum); + weight_accum = 1.0; + +#if defined(DIFFUSE) + if (rtbuffer.valid_history_diffuse) { +#elif defined(REFRACTION) + if (rtbuffer.valid_history_refraction) { +#else + if (rtbuffer.valid_history_reflection) { +#endif + + vec3 rgb_min = rgb_mean - rgb_deviation; + vec3 rgb_max = rgb_mean + rgb_deviation; + + /* Surface reprojection. */ + vec3 P_surf = transform_point(ViewMatrixInverse, vP); + vec2 uv_surf = project_point(rtbuffer.history_persmat, P_surf).xy * 0.5 + 0.5; + ivec2 texel_surf = ivec2(uv_surf * vec2(img_size)); + if (all(lessThan(texel_surf, img_size)) && all(greaterThan(texel_surf, ivec2(0)))) { + vec3 radiance = texelFetch(ray_history_tx, texel_surf, 0).rgb; + history_weigh_and_accumulate( + radiance, rgb_min, rgb_max, rgb_mean, rgb_deviation, radiance_accum, weight_accum); + + /* Variance estimate (slide 41). */ + float variance_history = texelFetch(ray_variance_tx, texel_surf, 0).r; + variance = mix(variance, variance_history, 0.5); + } + +#if !defined(DIFFUSE) + /* Reflexion reprojection. */ + vec3 P_hit = get_world_space_from_depth(uv, hit_depth_mean); + vec2 uv_hit = project_point(rtbuffer.history_persmat, P_hit).xy * 0.5 + 0.5; + ivec2 texel_hit = ivec2(uv_hit * vec2(img_size)); + if (all(lessThan(texel_hit, img_size)) && all(greaterThan(texel_hit, ivec2(0)))) { + vec3 radiance = texelFetch(ray_history_tx, texel_hit, 0).rgb; + history_weigh_and_accumulate( + radiance, rgb_min, rgb_max, rgb_mean, rgb_deviation, radiance_accum, weight_accum); + } +#endif + + radiance_accum *= safe_rcp(weight_accum); + } + + /* Save linear depth in alpha to speed-up the bilateral filter. */ + imageStore(out_history_img, texel, vec4(radiance_accum, -vP.z)); + imageStore(out_variance_img, texel, vec4(variance, 0.0, 0.0, 0.0)); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_raytrace_raygen_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_raygen_frag.glsl new file mode 100644 index 00000000000..8f8c8457135 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_raygen_frag.glsl @@ -0,0 +1,183 @@ +/** + * Simple wrapper around the screen-space raytracing routine. + * The goal is to output the tracing result buffer that can be denoised. + * We fallback to reflection probe if the ray fails. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_raytrace_raygen_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_raytrace_trace_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) + +layout(std140) uniform sampling_block +{ + SamplingData sampling; +}; + +layout(std140) uniform raytrace_block +{ + RaytraceData raytrace; +}; + +layout(std140) uniform hiz_block +{ + HiZData hiz; +}; + +layout(std140) uniform cubes_block +{ + CubemapData cubes[CULLING_ITEM_BATCH]; +}; + +layout(std140) uniform lightprobes_info_block +{ + LightProbeInfoData probes_info; +}; + +uniform sampler2D hiz_tx; +uniform sampler2D hiz_front_tx; +uniform samplerCubeArray lightprobe_cube_tx; +uniform sampler2D radiance_tx; +uniform sampler2D combined_tx; +uniform sampler2D cl_color_tx; +uniform sampler2D cl_normal_tx; +uniform sampler2D cl_data_tx; +uniform sampler2DArray utility_tx; + +utility_tx_fetch_define(utility_tx); +utility_tx_sample_define(utility_tx); + +in vec4 uvcoordsvar; + +layout(location = 0) out vec4 out_ray_data; +layout(location = 1) out vec4 out_ray_radiance; + +/* Prototypes. */ +vec3 lightprobe_cubemap_eval(vec3 P, vec3 R, float roughness, float random_threshold); + +void main() +{ + vec2 uv = uvcoordsvar.xy; + float gbuffer_depth = texelFetch(hiz_front_tx, ivec2(gl_FragCoord.xy), 0).r; + vec3 P = get_world_space_from_depth(uv, gbuffer_depth); + vec3 V = cameraVec(P); + + vec4 noise = utility_tx_fetch(gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).gbar; + + out_ray_data = vec4(0.0); + out_ray_radiance = vec4(0.0); + +#if defined(DIFFUSE) + vec4 tra_col_in = vec4(0.0); /* UNUSED */ + vec4 tra_nor_in = texture(cl_normal_tx, uv); + vec4 tra_dat_in = vec4(0.0); /* UNUSED */ + + ClosureDiffuse diffuse = gbuffer_load_diffuse_data(tra_col_in, tra_nor_in, tra_dat_in); + + if (diffuse.sss_radius.r < 0.0) { + /* Refraction pixel. */ + return; + } + float roughness = 1.0; + +#elif defined(REFRACTION) + vec4 tra_col_in = vec4(0.0); /* UNUSED */ + vec4 tra_nor_in = texture(cl_normal_tx, uv); + vec4 tra_dat_in = texture(cl_data_tx, uv); + + ClosureRefraction refraction = gbuffer_load_refraction_data(tra_col_in, tra_nor_in, tra_dat_in); + + float thickness; + gbuffer_load_global_data(tra_nor_in, thickness); + + if (refraction.ior == -1.0) { + /* Diffuse/SSS pixel. */ + return; + } + float roughness = refraction.roughness; + +#else + ClosureReflection reflection = gbuffer_load_reflection_data(cl_color_tx, cl_normal_tx, uv); + + float roughness = reflection.roughness; +#endif + + /* Generate ray. */ + float pdf; +#if defined(DIFFUSE) + Ray ray = raytrace_create_diffuse_ray(sampling, noise.xy, diffuse, P, pdf); +#elif defined(REFRACTION) + Ray ray = raytrace_create_refraction_ray(sampling, noise.xy, refraction, V, P, pdf); +#else + Ray ray = raytrace_create_reflection_ray(sampling, noise.xy, reflection, V, P, pdf); +#endif + ray = raytrace_world_ray_to_view(ray); + + bool hit = false; + +#ifndef SKIP_TRACE + vec2 noise_offset = sampling_rng_2D_get(sampling, SAMPLING_RAYTRACE_W); + vec2 rand = fract(noise.zw + noise_offset.xy); + /* Extend the ray to cover the whole view. */ + ray.direction *= 1e16; + + if (roughness - rand.y * 0.2 < raytrace.max_roughness) { +# ifdef REFRACTION + const bool discard_backface = false; + const bool allow_self_intersection = true; + /* TODO(fclem): Take IOR into account in the roughness LOD bias. */ +# else + const bool discard_backface = true; + const bool allow_self_intersection = false; +# endif + hit = raytrace_screen( + raytrace, hiz, hiz_tx, rand.x, roughness, discard_backface, allow_self_intersection, ray); + } +#endif + + vec3 radiance; + if (hit) { + /* Evaluate radiance at hitpoint. */ + vec2 hit_uv = get_uvs_from_view(ray.origin + ray.direction); + + radiance = textureLod(radiance_tx, hit_uv, 0.0).rgb; + +#if defined(DIFFUSE) + /* For diffuse, the radiance is still split and SSS materials don't have the color applied. */ + vec4 tra_col_in = texture(cl_color_tx, hit_uv); + vec4 tra_nor_in = texture(cl_normal_tx, hit_uv); + vec4 tra_dat_in = vec4(0.0); /* UNUSED */ + ClosureDiffuse hit_diffuse = gbuffer_load_diffuse_data(tra_col_in, tra_nor_in, tra_dat_in); + + if (hit_diffuse.sss_id > 0u) { + /* Refraction pixel. */ + radiance *= hit_diffuse.color; + } + radiance += textureLod(combined_tx, hit_uv, 0.0).rgb; +#endif + } + else { + /* Evaluate fallback lightprobe. */ + float noise_offset = sampling_rng_1D_get(sampling, SAMPLING_LIGHTPROBE); + float random_probe = fract(noise.w + noise_offset); + + ray = raytrace_view_ray_to_world(ray); + /* TOOD(fclem): We could reduce noise by mapping ray pdf to roughness. */ + float roughness = 0.0; + + radiance = lightprobe_cubemap_eval(ray.origin, ray.direction, roughness, random_probe); + } + /* Apply brightness clamping. */ + float luma = max_v3(radiance); + radiance *= 1.0 - max(0.0, luma - raytrace.brightness_clamp) * safe_rcp(luma); + /* Limit to the smallest non-0 value that the format can encode. + * Strangely it does not correspond to the IEEE spec. */ + float inv_pdf = (pdf == 0.0) ? 0.0 : max(6e-8, 1.0 / pdf); + float hit_depth = saturate(get_depth_from_view_z(ray.origin.z + ray.direction.z)); + /* Output the ray. */ + out_ray_data = vec4(ray.direction, inv_pdf); + out_ray_radiance = vec4(radiance, hit_depth); +} + +#pragma BLENDER_REQUIRE_POST(eevee_lightprobe_eval_cubemap_lib.glsl) diff --git a/source/blender/draw/engines/eevee/shaders/eevee_raytrace_raygen_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_raygen_lib.glsl new file mode 100644 index 00000000000..8c1504f2e16 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_raygen_lib.glsl @@ -0,0 +1,83 @@ +/** + * Ray generation routines for each BSDF types. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_bsdf_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_raytrace_trace_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +/* Returns viewspace ray. */ +Ray raytrace_create_reflection_ray( + SamplingData sampling, vec2 noise, ClosureReflection reflection, vec3 V, vec3 P, out float pdf) +{ + vec2 noise_offset = sampling_rng_2D_get(sampling, SAMPLING_RAYTRACE_U); + vec3 Xi = sample_cylinder(fract(noise_offset + noise)); + + float roughness_sqr = max(1e-3, sqr(reflection.roughness)); + /* Gives *perfect* reflection for very small roughness. */ + if (reflection.roughness < 0.0016) { + Xi = vec3(0.0); + } + + vec3 T, B, N = reflection.N; + make_orthonormal_basis(N, T, B); + + Ray ray; + ray.origin = P; + ray.direction = sample_ggx_reflect(Xi, roughness_sqr, V, N, T, B, pdf); + return ray; +} + +Ray raytrace_create_refraction_ray( + SamplingData sampling, vec2 noise, ClosureRefraction refraction, vec3 V, vec3 P, out float pdf) +{ + vec2 noise_offset = sampling_rng_2D_get(sampling, SAMPLING_RAYTRACE_U); + vec3 Xi = sample_cylinder(fract(noise_offset + noise)); + + float roughness_sqr = max(1e-3, sqr(refraction.roughness)); + /* Gives *perfect* refraction for very small roughness. */ + if (refraction.roughness < 0.0016) { + Xi = vec3(0.0); + } + vec3 T, B, N = refraction.N; + make_orthonormal_basis(N, T, B); + + Ray ray; + ray.origin = P; + ray.direction = sample_ggx_refract(Xi, roughness_sqr, refraction.ior, V, N, T, B, pdf); + return ray; +} + +Ray raytrace_create_diffuse_ray( + SamplingData sampling, vec2 noise, ClosureDiffuse diffuse, vec3 P, out float pdf) +{ + vec2 noise_offset = sampling_rng_2D_get(sampling, SAMPLING_RAYTRACE_U); + vec3 Xi = sample_cylinder(fract(noise_offset + noise)); + + /* Bias the rays so we never get really high energy rays almost parallel to the surface. */ + Xi.x = Xi.x * 0.98 + 0.02; + + vec3 T, B, N = diffuse.N; + make_orthonormal_basis(N, T, B); + + Ray ray; + ray.origin = P; + ray.direction = sample_cosine_hemisphere(Xi, N, T, B, pdf); + return ray; +} + +Ray raytrace_world_ray_to_view(Ray ray) +{ + ray.origin = transform_point(ViewMatrix, ray.origin); + ray.direction = transform_direction(ViewMatrix, ray.direction); + return ray; +} + +Ray raytrace_view_ray_to_world(Ray ray) +{ + ray.origin = transform_point(ViewMatrixInverse, ray.origin); + ray.direction = transform_direction(ViewMatrixInverse, ray.direction); + return ray; +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_raytrace_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_resolve_frag.glsl new file mode 100644 index 00000000000..e532152d2f1 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_resolve_frag.glsl @@ -0,0 +1,124 @@ + +/** + * Final denoise step using a bilateral filter. Filter radius is controled by variance estimate. + * + * Inputs: Ray radiance (denoised), Mean hit depth, Extimated variance from ray reconstruction + * Outputs: Ray radiance (filtered). + * + * Linear depth is packed in ray_radiance for this step. + * Following "Stochastic All The Things: Raytracing in Hybrid Real-Time Rendering" + * by Tomasz Stachowiak + * https://www.ea.com/seed/news/seed-dd18-presentation-slides-raytracing + */ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_bsdf_microfacet_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(std140) uniform hiz_block +{ + HiZData hiz; +}; + +uniform sampler2D ray_radiance_tx; +uniform sampler2D ray_variance_tx; +uniform sampler2D cl_color_tx; +uniform sampler2D cl_normal_tx; +uniform sampler2D cl_data_tx; + +in vec4 uvcoordsvar; + +layout(location = 0) out vec4 out_combined; +layout(location = 1) out vec4 out_diffuse; +layout(location = 2) out vec3 out_specular; + +#if defined(DIFFUSE) +# define RADIUS 4 +#elif defined(REFRACTION) +# define RADIUS 1 +#else +# define RADIUS 1 +#endif + +float normal_pdf(float x_sqr, float sigma_inv, float sigma_inv_sqr) +{ + return exp(-0.5 * x_sqr * sigma_inv_sqr) * sigma_inv; +} + +void main(void) +{ + vec2 uv = uvcoordsvar.xy; + float ray_variance = texture(ray_variance_tx, uv).r; + vec4 ray_data = texture(ray_radiance_tx, uv); + float center_depth = ray_data.w; + vec2 texel_size = hiz.pixel_to_ndc * 0.5; + + out_combined = vec4(0.0); + out_diffuse = vec4(0.0); + out_specular = vec3(0.0); + +#if defined(DIFFUSE) + ClosureDiffuse closure = gbuffer_load_diffuse_data(cl_color_tx, cl_normal_tx, cl_data_tx, uv); + if (closure.sss_radius.r < 0.0) { + return; + } + float sigma_pixel = 3.0; +#elif defined(REFRACTION) + ClosureRefraction closure = gbuffer_load_refraction_data( + cl_color_tx, cl_normal_tx, cl_data_tx, uv); + if (closure.ior == -1.0) { + return; + } + float sigma_pixel = 1.0; +#else + ClosureReflection closure = gbuffer_load_reflection_data(cl_color_tx, cl_normal_tx, uv); + float sigma_pixel = 1.0; +#endif + /* TODO(fclem): Sigma based on variance. */ + float sigma_depth = 0.1; /* TODO user option? */ + + float px_sigma_inv = 1.0 / sigma_pixel; + float px_sigma_inv_sqr = sqr(px_sigma_inv); + float depth_sigma_inv = 1.0 / sigma_depth; + float depth_sigma_inv_sqr = sqr(depth_sigma_inv); + + float weight_accum = normal_pdf(0.0, px_sigma_inv, px_sigma_inv_sqr); + vec3 radiance_accum = ray_data.rgb * weight_accum; + for (int x = -RADIUS; x <= RADIUS; x++) { + for (int y = -RADIUS; y <= RADIUS; y++) { + /* Skip center pixels. */ + if (x == 0 && y == 0) { + continue; + } + vec2 sample_uv = uv + vec2(x, y) * texel_size; + vec4 ray_data = texture(ray_radiance_tx, sample_uv); + /* Skip unprocessed pixels. */ + if (ray_data.w == 0.0) { + continue; + } + float delta_pixel_sqr = len_squared(vec2(x, y)); + float delta_depth_sqr = sqr(abs(center_depth - ray_data.w)); + /* TODO(fclem): OPTI might be a good idea to compare view normal to avoid one matrix mult. */ + vec3 sample_N = gbuffer_decode_normal(texture(cl_normal_tx, sample_uv).xy); + /* Bilateral weight. */ + float weight = saturate(dot(sample_N, closure.N)) * + normal_pdf(delta_pixel_sqr, px_sigma_inv, px_sigma_inv_sqr) * + normal_pdf(delta_depth_sqr, depth_sigma_inv, depth_sigma_inv_sqr); + + radiance_accum += ray_data.rgb * weight; + weight_accum += weight; + } + } + radiance_accum *= safe_rcp(weight_accum); + radiance_accum *= closure.color; + + out_combined = vec4(radiance_accum, 0.0); +#if defined(DIFFUSE) + out_diffuse.rgb = radiance_accum; +#else + out_specular = radiance_accum; +#endif +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_raytrace_trace_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_trace_lib.glsl new file mode 100644 index 00000000000..e33968a36d5 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_raytrace_trace_lib.glsl @@ -0,0 +1,182 @@ +/** + * Screen-space raytracing routine. + * + * Based on "Efficient GPU Screen-Space Ray Tracing" + * by Morgan McGuire & Michael Mara + * https://jcgt.org/published/0003/04/04/paper.pdf + * + * Many modifications were made for our own usage. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +/** + * As input to the tracing function, direction is premultiplied by its maximum length. + * As output, direction is scaled to hit point or to latest step. + */ +struct Ray { + vec3 origin; + vec3 direction; +}; + +/** + * Screenspace ray ([0..1] "uv" range) where direction is normalize to be as small as one + * full-resolution pixel. The ray is also clipped to all frustum sides. + */ +struct ScreenSpaceRay { + vec4 origin; + vec4 direction; + float max_time; +}; + +/* Inputs expected to be in viewspace. */ +void raytrace_clip_ray_to_near_plane(inout Ray ray) +{ + float near_dist = get_view_z_from_depth(0.0); + if ((ray.origin.z + ray.direction.z) > near_dist) { + ray.direction *= abs((near_dist - ray.origin.z) / ray.direction.z); + } +} + +void raytrace_screenspace_ray_finalize(HiZData hiz, inout ScreenSpaceRay ray) +{ + /* Constant bias (due to depth buffer precision). Helps with self intersection. */ + /* Magic numbers for 24bits of precision. + * From http://terathon.com/gdc07_lengyel.pdf (slide 26) */ + const float bias = -2.4e-7 * 2.0; + ray.origin.zw += bias; + ray.direction.zw += bias; + + ray.direction -= ray.origin; + /* If the line is degenerate, make it cover at least one pixel + * to not have to handle zero-pixel extent as a special case later. */ + if (len_squared(ray.direction.xy) < 0.00001) { + ray.direction.xy = vec2(0.0, 0.00001); + } + float ray_len_sqr = len_squared(ray.direction.xyz); + /* Make ray.direction cover one pixel. */ + bool is_more_vertical = abs(ray.direction.x) < abs(ray.direction.y); + ray.direction /= (is_more_vertical) ? abs(ray.direction.y) : abs(ray.direction.x); + ray.direction *= (is_more_vertical) ? hiz.pixel_to_ndc.y : hiz.pixel_to_ndc.x; + /* Clip to segment's end. */ + ray.max_time = sqrt(ray_len_sqr * safe_rcp(len_squared(ray.direction.xyz))); + /* Clipping to frustum sides. */ + float clip_dist = line_unit_box_intersect_dist_safe(ray.origin.xyz, ray.direction.xyz); + ray.max_time = min(ray.max_time, clip_dist); + /* Convert to texture coords [0..1] range. */ + ray.origin = ray.origin * 0.5 + 0.5; + ray.direction *= 0.5; +} + +ScreenSpaceRay raytrace_screenspace_ray_create(HiZData hiz, Ray ray) +{ + ScreenSpaceRay ssray; + ssray.origin.xyz = project_point(ProjectionMatrix, ray.origin); + ssray.direction.xyz = project_point(ProjectionMatrix, ray.origin + ray.direction); + + raytrace_screenspace_ray_finalize(hiz, ssray); + return ssray; +} + +ScreenSpaceRay raytrace_screenspace_ray_create(HiZData hiz, Ray ray, float thickness) +{ + ScreenSpaceRay ssray; + ssray.origin.xyz = project_point(ProjectionMatrix, ray.origin); + ssray.direction.xyz = project_point(ProjectionMatrix, ray.origin + ray.direction); + /* Interpolate thickness in screen space. + * Calculate thickness further away to avoid near plane clipping issues. */ + ssray.origin.w = get_depth_from_view_z(ray.origin.z - thickness); + ssray.direction.w = get_depth_from_view_z(ray.origin.z + ray.direction.z - thickness); + ssray.origin.w = ssray.origin.w * 2.0 - 1.0; + ssray.direction.w = ssray.direction.w * 2.0 - 1.0; + + raytrace_screenspace_ray_finalize(hiz, ssray); + return ssray; +} + +/** + * Raytrace against the given hizbuffer heightfield. + * + * \param stride_rand: Random number in [0..1] range. Offset along the ray to avoid banding + * artifact when steps are too large. + * \param roughness: Determine how lower depth mipmaps are used to make the tracing faster. Lower + * roughness will use lower mipmaps. + * \param discard_backface: If true, raytrace will return false if we hit a surface from behind. + * \param allow_self_intersection: If false, raytrace will return false if the ray is not covering + * at least one pixel. + * \param ray: Viewspace ray. Direction premultiplied by maximum length. + * + * \return True if there is a valid intersection. + */ +bool raytrace_screen(RaytraceData raytrace, + HiZData hiz, + sampler2D hiz_tx, + float stride_rand, + float roughness, + const bool discard_backface, + const bool allow_self_intersection, + inout Ray ray) +{ + /* Clip to near plane for perspective view where there is a singularity at the camera origin. */ + if (ProjectionMatrix[3][3] == 0.0) { + raytrace_clip_ray_to_near_plane(ray); + } + + ScreenSpaceRay ssray = raytrace_screenspace_ray_create(hiz, ray, raytrace.thickness); + /* Avoid no iteration. */ + if (!allow_self_intersection && ssray.max_time < 1.1) { + /* Still output the clipped ray. */ + vec3 hit_ssP = ssray.origin.xyz + ssray.direction.xyz * ssray.max_time; + vec3 hit_P = get_view_space_from_depth(hit_ssP.xy, saturate(hit_ssP.z)); + ray.direction = hit_P - ray.origin; + return false; + } + + ssray.max_time = max(1.1, ssray.max_time); + + float prev_delta = 0.0, prev_time = 0.0; + float depth_sample = get_depth_from_view_z(ray.origin.z); + float delta = depth_sample - ssray.origin.z; + + float lod_fac = saturate(fast_sqrt(roughness) * 2.0 - 0.4); + + /* Cross at least one pixel. */ + float t = 1.001, time = 1.001; + bool hit = false; + const float max_steps = 255.0; + for (float iter = 1.0; !hit && (time < ssray.max_time) && (iter < max_steps); iter++) { + float stride = 1.0 + iter * raytrace.quality; + float lod = log2(stride) * lod_fac; + + prev_time = time; + prev_delta = delta; + + time = min(t + stride * stride_rand, ssray.max_time); + t += stride; + + vec4 ss_p = ssray.origin + ssray.direction * time; + depth_sample = textureLod(hiz_tx, ss_p.xy * hiz.uv_scale, floor(lod)).r; + + delta = depth_sample - ss_p.z; + /* Check if the ray is below the surface ... */ + hit = (delta < 0.0); + /* ... and above it with the added thickness. */ + hit = hit && (delta > ss_p.z - ss_p.w || abs(delta) < abs(ssray.direction.z * stride * 2.0)); + } + /* Discard backface hits. */ + hit = hit && !(discard_backface && prev_delta < 0.0); + /* Reject hit if background. */ + hit = hit && (depth_sample != 1.0); + /* Refine hit using intersection between the sampled heightfield and the ray. + * This simplifies nicely to this single line. */ + time = mix(prev_time, time, saturate(prev_delta / (prev_delta - delta))); + + vec3 hit_ssP = ssray.origin.xyz + ssray.direction.xyz * time; + /* Set ray to where tracing ended. */ + vec3 hit_P = get_view_space_from_depth(hit_ssP.xy, saturate(hit_ssP.z)); + ray.direction = hit_P - ray.origin; + + return hit; +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_sampling_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_sampling_lib.glsl new file mode 100644 index 00000000000..74fcf10e5f7 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_sampling_lib.glsl @@ -0,0 +1,96 @@ + +/** + * Sampling data accessors and random number generators. + * Also contains some sample mapping functions. + **/ + +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +/* -------------------------------------------------------------------- */ +/** \name Sampling data. + * + * Return a random values from Low Discrepency Sequence in [0..1) range. + * This value is uniform (constant) for the whole scene sample. + * You might want to couple it with a noise function. + * \{ */ + +float sampling_rng_1D_get(SamplingData data, const eSamplingDimension dimension) +{ + return data.dimensions[dimension].x; +} + +vec2 sampling_rng_2D_get(SamplingData data, const eSamplingDimension dimension) +{ + return vec2(data.dimensions[dimension].x, data.dimensions[dimension + 1u].x); +} + +vec3 sampling_rng_3D_get(SamplingData data, const eSamplingDimension dimension) +{ + return vec3(data.dimensions[dimension].x, + data.dimensions[dimension + 1u].x, + data.dimensions[dimension + 2u].x); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Random Number Generators. + * \{ */ + +/* Interlieved gradient noise by Jorge Jimenez + * http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare + * Seeding found by Epic Game. */ +float interlieved_gradient_noise(vec2 pixel, float seed, float offset) +{ + pixel += seed * (vec2(47, 17) * 0.695); + return fract(offset + 52.9829189 * fract(0.06711056 * pixel.x + 0.00583715 * pixel.y)); +} + +/* From: http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html */ +float van_der_corput_radical_inverse(uint bits) +{ + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + /* Same as dividing by 0x100000000. */ + return float(bits) * 2.3283064365386963e-10; +} + +vec2 hammersley_2d(float i, float sample_count) +{ + vec2 rand; + rand.x = i / sample_count; + rand.y = van_der_corput_radical_inverse(uint(i)); + return rand; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Distribution mapping. + * + * Functions mapping input random numbers to sampling shapes (i.e: hemisphere). + * \{ */ + +/* Given 2 randome number in [0..1] range, return a random unit disk sample. */ +vec2 sample_disk(vec2 noise) +{ + float angle = noise.x * M_2PI; + return vec2(cos(angle), sin(angle)) * sqrt(noise.y); +} + +/* This transform a 2d random sample (in [0..1] range) to a sample located on a cylinder of the + * same range. This is because the sampling functions expect such a random sample which is + * normally precomputed. */ +vec3 sample_cylinder(vec2 rand) +{ + float theta = rand.x; + float phi = (rand.y - 0.5) * M_2PI; + float cos_phi = cos(phi); + float sin_phi = sqrt(1.0 - sqr(cos_phi)) * sign(phi); + return vec3(theta, cos_phi, sin_phi); +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_debug_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_debug_frag.glsl new file mode 100644 index 00000000000..99dc6d5ee56 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_debug_frag.glsl @@ -0,0 +1,279 @@ + +/** + * Debug drawing for virtual shadowmaps. + * See eShadowDebug for more information. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_light_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_page_lib.glsl) + +/** Control the scaling of the tilemap splat. */ +const float pixel_scale = 4.0; + +layout(std140) uniform debug_block +{ + ShadowDebugData debug; +}; + +layout(std430, binding = 0) readonly buffer tilemaps_buf +{ + ShadowTileMapData tilemaps[]; +}; + +uniform usampler2D debug_page_tx; +uniform usampler2D tilemaps_tx; +uniform sampler2D depth_tx; +uniform sampler2D atlas_tx; + +in vec4 uvcoordsvar; + +layout(location = 0, index = 0) out vec4 out_color_add; +layout(location = 0, index = 1) out vec4 out_color_mul; + +vec3 debug_random_color(ivec2 v) +{ + float r = interlieved_gradient_noise(vec2(v), 0.0, 0.0); + return hue_gradient(r); +} + +vec3 debug_random_color(int v) +{ + return debug_random_color(ivec2(v, 0)); +} + +vec3 debug_tile_state_color(ShadowTileData tile) +{ + if (tile.lod > 0) { + return vec3(1, 0.5, 0) * float(tile.lod) / float(SHADOW_TILEMAP_LOD); + } + if (tile.do_update && tile.is_used && tile.is_visible) { + return vec3(1, 0, 0); + } + else if (tile.is_used && tile.is_visible) { + return vec3(0, 1, 0); + } + else if (tile.is_visible) { + return vec3(0, 0.2, 0.8); + } + return vec3(0); +} + +bool debug_tilemap() +{ + ivec2 tile = ivec2(gl_FragCoord.xy / pixel_scale); + int tilemap_lod = tile.y / (SHADOW_TILEMAP_RES + 2); + int tilemap_index = tile.x / (SHADOW_TILEMAP_RES + 2); + tile = (tile % (SHADOW_TILEMAP_RES + 2)) - 1; + tilemap_index += debug.shadow.tilemap_index; + int tilemap_lod_max = (debug.light.type != LIGHT_SUN) ? SHADOW_TILEMAP_LOD : 0; + + if ((tilemap_index >= debug.shadow.tilemap_index) && + (tilemap_index <= debug.shadow.tilemap_last) && (tilemap_lod >= 0) && + (tilemap_lod <= tilemap_lod_max) && + in_range_inclusive(tile, ivec2(0), ivec2(SHADOW_TILEMAP_RES - 1))) { + tile >>= tilemap_lod; + ShadowTileData tile_data = shadow_tile_load(tilemaps_tx, tile, tilemap_lod, tilemap_index); + /* Write depth to overlap overlays. */ + gl_FragDepth = 0.0; + out_color_add = vec4(debug_tile_state_color(tile_data), 0); + out_color_mul = vec4(0); + return true; + } + return false; +} + +bool debug_tilemap_point_is_inside(vec3 P, int tilemap_index) +{ + int tilemap_data_index = debug.tilemap_data_index + tilemap_index - debug.shadow.tilemap_index; + vec3 clipP = project_point(tilemaps[tilemap_data_index].tilemat, P); + return in_range_inclusive(clipP, vec3(0.0), vec3(SHADOW_TILEMAP_RES)); +} + +/** Unlike shadow_directional_tilemap_index, returns the first tilemap overlapping the position. */ +int debug_directional_tilemap_index(vec3 P) +{ + for (int tilemap_index = debug.shadow.tilemap_index; tilemap_index <= debug.shadow.tilemap_last; + tilemap_index++) { + if (debug_tilemap_point_is_inside(P, tilemap_index)) { + return tilemap_index; + } + } + return -1; +} + +int debug_punctual_tilemap_index(vec3 P) +{ + vec3 L; + float dist; + light_vector_get(debug.light, P, L, dist); + vec3 lL = light_world_to_local(debug.light, -L) * dist; + lL -= debug.shadow.offset; + int tilemap_index = debug.shadow.tilemap_index + shadow_punctual_face_index_get(lL); + if (tilemap_index > debug.shadow.tilemap_last) { + return -1; + } + if (debug_tilemap_point_is_inside(P, tilemap_index)) { + return tilemap_index; + } + return -1; +} + +void debug_pages(vec3 P) +{ + int tilemap_index = (debug.light.type == LIGHT_SUN) ? debug_directional_tilemap_index(P) : + debug_punctual_tilemap_index(P); + if (tilemap_index != -1) { + int tilemap_data_index = debug.tilemap_data_index + tilemap_index - debug.shadow.tilemap_index; + vec3 clipP = project_point(tilemaps[tilemap_data_index].tilemat, P); + ivec2 tile = ivec2(clipP.xy); + ShadowTileData tile_data = shadow_tile_load(tilemaps_tx, tile, 0, tilemap_index); + vec3 color = debug_random_color(ivec2(tile_data.page)); + out_color_add = vec4(color * 0.5, 0); + out_color_mul = out_color_add * 0.5 + 0.5; + } + else { + out_color_add = vec4(0.0); + out_color_mul = vec4(0.5); + } +} + +void debug_lod(vec3 P) +{ + int tilemap_index = (debug.light.type == LIGHT_SUN) ? debug_directional_tilemap_index(P) : + debug_punctual_tilemap_index(P); + if (tilemap_index != -1) { + vec3 color = debug_random_color(tilemap_index); + out_color_add = vec4(color * 0.5, 0.0); + out_color_mul = out_color_add * 0.5 + 0.5; + } + else { + out_color_add = vec4(0.0); + out_color_mul = vec4(0.5); + } +} + +void debug_tile_state(vec3 P) +{ + int tilemap_index = (debug.light.type == LIGHT_SUN) ? debug_directional_tilemap_index(P) : + debug_punctual_tilemap_index(P); + if (tilemap_index != -1) { + int tilemap_data_index = debug.tilemap_data_index + tilemap_index - debug.shadow.tilemap_index; + vec3 clipP = project_point(tilemaps[tilemap_data_index].tilemat, P); + ivec2 tile = ivec2(clipP.xy); + ShadowTileData tile_data = shadow_tile_load(tilemaps_tx, tile, 0, tilemap_index); + vec3 color = debug_tile_state_color(tile_data); + out_color_add = vec4(color * 0.5, 0); + out_color_mul = out_color_add * 0.5 + 0.5; + } + else { + out_color_add = vec4(0.0); + out_color_mul = vec4(0.5); + } +} + +void debug_page_allocation(void) +{ + ivec2 page = ivec2(gl_FragCoord.xy / pixel_scale) - 1; + + if (in_range_inclusive(page, ivec2(0), textureSize(debug_page_tx, 0).xy - 1)) { + uint page = texelFetch(debug_page_tx, page, 0).x; + + bool error = (page & 0xFFFFu) != 1u; + bool is_cached = (page & SHADOW_PAGE_IS_CACHED) != 0u; + bool is_needed = (page & SHADOW_PAGE_IS_NEEDED) != 0u; + bool in_heap = (page & SHADOW_PAGE_IN_FREE_HEAP) != 0u; + error = error || (is_cached && !in_heap); + error = error || (is_needed && is_cached); + + vec3 col = vec3(error, is_cached, is_needed); + + out_color_add = vec4(col, 0); + out_color_mul = vec4(0); + /* Write depth to overlap overlays. */ + gl_FragDepth = 0.0; + } +} + +void debug_tile_allocation(void) +{ + ivec2 tile_co = ivec2(gl_FragCoord.xy) - 32; + /* Assumes tilemap buffer is squared. */ + if (in_range_inclusive(tile_co, ivec2(0), textureSize(tilemaps_tx, 0).xy - 1)) { + ShadowTileData tile = shadow_tile_data_unpack(texelFetch(tilemaps_tx, tile_co, 0).x); + out_color_add = vec4(debug_tile_state_color(tile), 0); + out_color_mul = vec4(0); + /* Write depth to overlap overlays. */ + gl_FragDepth = 0.0; + } +} + +void debug_shadow_depth(vec3 P) +{ + vec3 L; + float dist; + light_vector_get(debug.light, P, L, dist); + vec3 lL = light_world_to_local(debug.light, -L) * dist; + lL -= debug.shadow.offset; + vec3 lP = transform_point(debug.shadow.mat, P); + float depth; + if (debug.light.type == LIGHT_SUN) { + shadow_directional_depth_get( + atlas_tx, tilemaps_tx, debug.light, debug.shadow, debug.camera_position, lP, P); + } + else { + shadow_punctual_depth_get(atlas_tx, tilemaps_tx, debug.light, debug.shadow, lL); + } + out_color_add = vec4(vec3(depth), 0); + out_color_mul = vec4(0); +} + +void main() +{ + /* Default to no output. */ + gl_FragDepth = 1.0; + out_color_add = vec4(0.0); + out_color_mul = vec4(1.0); + + if (debug.type == SHADOW_DEBUG_PAGE_ALLOCATION) { + debug_page_allocation(); + return; + } + + if (debug.type == SHADOW_DEBUG_TILE_ALLOCATION) { + debug_tile_allocation(); + return; + } + + if (debug_tilemap()) { + return; + } + + float depth = texelFetch(depth_tx, ivec2(gl_FragCoord.xy), 0).r; + vec3 P = get_world_space_from_depth(uvcoordsvar.xy, depth); + /* Make it pass the depth test. */ + gl_FragDepth = depth - 1e-6; + + if (depth != 1.0) { + switch (debug.type) { + case SHADOW_DEBUG_TILEMAPS: + debug_tile_state(P); + break; + case SHADOW_DEBUG_PAGES: + debug_pages(P); + break; + case SHADOW_DEBUG_LOD: + debug_lod(P); + break; + case SHADOW_DEBUG_SHADOW_DEPTH: + debug_shadow_depth(P); + break; + default: + discard; + } + } +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_lib.glsl new file mode 100644 index 00000000000..a1657a830ff --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_lib.glsl @@ -0,0 +1,128 @@ + +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) +#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_page_lib.glsl) + +/* ---------------------------------------------------------------------- */ +/** \name Shadow Sampling Functions + * \{ */ + +/* Turns local light coordinate into shadow region index. Matches eCubeFace order. */ +int shadow_punctual_face_index_get(vec3 lL) +{ + vec3 aP = abs(lL); + if (all(greaterThan(aP.xx, aP.yz))) { + return (lL.x > 0.0) ? 1 : 2; + } + else if (all(greaterThan(aP.yy, aP.xz))) { + return (lL.y > 0.0) ? 3 : 4; + } + else { + return (lL.z > 0.0) ? 5 : 0; + } +} + +/* Transform vector to face local coordinate. */ +vec3 shadow_punctual_local_position_to_face_local(int face_id, vec3 lL) +{ + switch (face_id) { + case 1: + return vec3(-lL.y, lL.z, -lL.x); + case 2: + return vec3(lL.y, lL.z, lL.x); + case 3: + return vec3(lL.x, lL.z, -lL.y); + case 4: + return vec3(-lL.x, lL.z, lL.y); + case 5: + return vec3(lL.x, -lL.y, -lL.z); + default: + return lL; + } +} + +float shadow_punctual_depth_get( + sampler2D atlas_tx, usampler2D tilemaps_tx, LightData light, ShadowData shadow, vec3 lL) +{ + lL -= shadow.offset; + int face_id = shadow_punctual_face_index_get(lL); + lL = shadow_punctual_local_position_to_face_local(face_id, lL); + /* UVs in [0..SHADOW_TILEMAP_RES] range. */ + const float lod0_res = float(SHADOW_TILEMAP_RES / 2); + vec2 uv = (lL.xy / abs(lL.z)) * lod0_res + lod0_res; + ivec2 tile_co = ivec2(floor(uv)); + int tilemap_index = shadow.tilemap_index + face_id; + ShadowTileData tile = shadow_tile_load(tilemaps_tx, tile_co, 0, tilemap_index); + + /* TODO(fclem): Remove this indirection. But this means having to store another indirection + * table for the pages. */ + if (tile.lod > 0u) { + tile_co >>= int(tile.lod); + tile.page = shadow_tile_load(tilemaps_tx, tile_co, int(tile.lod), tilemap_index).page; + } + + float depth = 1.0; + if ((tilemap_index <= shadow.tilemap_last) && (tile.is_allocated || tile.lod > 0)) { + vec2 shadow_uv = shadow_page_uv_transform(tile.page, tile.lod, uv); + depth = texture(atlas_tx, shadow_uv).r; + } + return depth; +} + +float shadow_directional_depth_get(sampler2D atlas_tx, + usampler2D tilemaps_tx, + LightData light, + ShadowData shadow, + vec3 camera_P, + vec3 lP, + vec3 P) +{ + int clipmap_lod = shadow_directional_clipmap_level(shadow, distance(P, camera_P)); + int clipmap_lod_relative = clipmap_lod - shadow.clipmap_lod_min; + int tilemap_index = clamp( + shadow.tilemap_index + clipmap_lod_relative, shadow.tilemap_index, shadow.tilemap_last); + /* Compute how many time we need to subdivide. */ + float clipmap_res_mul = float(1 << (shadow.clipmap_lod_max - clipmap_lod)); + /* Compute offset of the clipmap from the largest LOD. */ + vec2 clipmap_offset = vec2(abs(shadow.base_offset) >> clipmap_lod_relative) * + sign(shadow.base_offset); + + vec2 uv = (lP.xy * clipmap_res_mul - clipmap_offset) + float(SHADOW_TILEMAP_RES / 2); + ivec2 tile_co = ivec2(floor(uv)); + ShadowTileData tile = shadow_tile_load(tilemaps_tx, tile_co, 0, tilemap_index); + + float depth = 1.0; + if (tile.is_allocated) { + vec2 shadow_uv = shadow_page_uv_transform(tile.page, 0, uv); + depth = texture(atlas_tx, shadow_uv).r; + } + return depth; +} + +/* Returns world distance delta from light between shading point and first occluder. */ +float shadow_delta_get(sampler2D atlas_tx, + usampler2D tilemaps_tx, + LightData light, + ShadowData shadow, + vec3 lL, + float receiver_dist, + vec3 P) +{ + if (light.type == LIGHT_SUN) { + /* [-SHADOW_TILEMAP_RES/2..SHADOW_TILEMAP_RES/2] range for highest LOD. */ + vec3 lP = transform_point(shadow.mat, P); + float occluder_z = shadow_directional_depth_get( + atlas_tx, tilemaps_tx, light, shadow, cameraPos, lP, P); + /* Transform to world space distance. */ + return (lP.z - occluder_z) * abs(shadow.clip_far - shadow.clip_near); + } + else { + float occluder_z = shadow_punctual_depth_get(atlas_tx, tilemaps_tx, light, shadow, lL); + occluder_z = linear_depth(true, occluder_z, shadow.clip_far, shadow.clip_near); + /* Take into account the cubemap projection. We want the radial distance. */ + float occluder_dist = receiver_dist * occluder_z / max_v3(abs(lL)); + return receiver_dist - occluder_dist; + } +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_alloc_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_alloc_comp.glsl new file mode 100644 index 00000000000..3d217cdfc95 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_alloc_comp.glsl @@ -0,0 +1,132 @@ + +/** + * Virtual shadowmapping: Schedule phase for tilemaps. + * This is the most complex part in the entire shadow pipeline. + * This step will read each updated tilemap to see if any tile is both visible and to be + * updated. If that is the case, it computes the bounds of the tiles to update and write it + * in a texture to be read back by the CPU. This is a sync step that is the main performance + * bottleneck of the pipeline. + * + * Unused tile might be reallocated at this stage. + * + * For each unallocated tile it will reserve a new page in the atlas. If the tile is to be + * rendered, it will also write the tile copy coordinates required in another buffer. + * This is also a slow part and should be improved in the future by moving the least amount of + * tiles. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_page_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl) + +layout(local_size_x = SHADOW_TILEMAP_RES, local_size_y = SHADOW_TILEMAP_RES) in; + +layout(std430, binding = 0) restrict readonly buffer tilemaps_buf +{ + ShadowTileMapData tilemaps[]; +}; + +layout(std430, binding = 1) restrict buffer pages_free_buf +{ + uint free_page_owners[]; +}; + +layout(std430, binding = 3) restrict buffer pages_infos_buf +{ + ShadowPagesInfoData infos; +}; + +layout(r32ui) restrict uniform uimage2D tilemaps_img; +layout(r32i) writeonly restrict uniform iimage2D tilemap_rects_img; + +void main() +{ + ShadowTileMapData tilemap_data = tilemaps[gl_GlobalInvocationID.z]; + int tilemap_idx = tilemap_data.index; + int lod_max = tilemap_data.is_cubeface ? SHADOW_TILEMAP_LOD : 0; + + int lod_valid = 0; + for (int lod = lod_max; lod >= 0; lod--) { + ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy) >> lod; + int tile_index = (SHADOW_TILEMAP_RES / 2) * tile_co.y + tile_co.x; + uint stride = 1u << lod; + /* We load the same data for each thread covering the same LOD tile, but we avoid + * allocating the same tile twice. This is because we need uniform control flow for the + * barriers to be valid. */ + bool valid_thread = (gl_GlobalInvocationID.xy % stride) == uvec2(0); + + ivec2 texel = shadow_tile_coord_in_atlas(tile_co, tilemap_idx, lod); + ShadowTileData tile = shadow_tile_data_unpack(imageLoad(tilemaps_img, texel).x); + + if (valid_thread) { + if (tile.is_visible && tile.is_used && !tile.is_allocated) { + /** Tile allocation. */ + int free_index = atomicAdd(infos.page_free_next, -1); + if (free_index >= 0) { + ivec2 owner_texel = ivec2(unpackUvec2x16(free_page_owners[free_index])); + free_page_owners[free_index] = uint(-1); + + tile.page = shadow_tile_data_unpack(imageLoad(tilemaps_img, owner_texel).x).page; + tile.do_update = true; + tile.is_allocated = true; + tile.is_cached = false; + imageStore(tilemaps_img, texel, uvec4(shadow_tile_data_pack(tile))); + + const uint flag = SHADOW_TILE_IS_ALLOCATED | SHADOW_TILE_IS_CACHED; + imageAtomicAnd(tilemaps_img, owner_texel, ~flag); + } + else { + /* Well, hum ... you blew up your budget! */ + } + } + } + + barrier(); + + /* Save highest quality valid lod for this thread. */ + if (tile.is_visible && tile.is_used) { + lod_valid = lod; + } + else if (lod == 0) { + /* If the tile is not used, store the valid LOD level. */ + /* This is tricky because the tile might be processed by another thread doing an allocation. + * So we need to set the LOD using atomics. */ + uint lod_mask = 7u << 12u; + uint lod_store = uint(lod_valid) << 12u; + imageAtomicAnd(tilemaps_img, texel, ~lod_mask); + imageAtomicOr(tilemaps_img, texel, lod_store); + } + + /** Compute area to render and write to buffer for CPU to read. */ + { + shared ivec2 min_tile; + shared ivec2 max_tile; + ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy); + + if (gl_GlobalInvocationID.xy == uvec2(0)) { + min_tile = ivec2(SHADOW_TILEMAP_RES - 1); + max_tile = ivec2(0); + } + /* Makes initial value visible to other threads. */ + barrier(); + + if (valid_thread && tile.do_update && tile.is_visible && tile.is_used) { + atomicAdd(infos.page_updated_count, 1); + atomicMin(min_tile.x, tile_co.x); + atomicMin(min_tile.y, tile_co.y); + atomicMax(max_tile.x, tile_co.x); + atomicMax(max_tile.y, tile_co.y); + } + /* Makes final value visible to first threads. */ + barrier(); + + if (gl_GlobalInvocationID.xy == uvec2(0)) { + max_tile += 1; + /* Must match the rcti structure. */ + ivec4 out_data = ivec4(min_tile.x, max_tile.x, min_tile.y, max_tile.y); + imageStore(tilemap_rects_img, ivec2(lod, gl_GlobalInvocationID.z), out_data); + } + } + } +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_copy_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_copy_comp.glsl new file mode 100644 index 00000000000..6129428704e --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_copy_comp.glsl @@ -0,0 +1,45 @@ + +/** + * Virtual shadowmapping: Tile copy. + * + * This pass copies the pages rendered in the render target to the page atlas. + * This might not be the fastest way to blit shadow regions but at least it is fully GPU driven. + */ + +/* TODO(fclem): The goal would be to render on the atlas texture and only move pages if + * they overlap with the rendering. */ + +#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl) + +layout(local_size_x = SHADOW_PAGE_COPY_GROUP_SIZE, local_size_y = SHADOW_PAGE_COPY_GROUP_SIZE) in; + +uniform usampler2D tilemaps_tx; +uniform sampler2D render_tx; + +/* TODO(fclem): 16bit format. */ +layout(r32f) writeonly restrict uniform image2D out_atlas_img; + +uniform int tilemap_index; +uniform int tilemap_lod; + +void main() +{ + int page_size = textureSize(render_tx, 0).x / SHADOW_TILEMAP_RES; + int lod_size = SHADOW_TILEMAP_RES >> tilemap_lod; + /* TODO(fclem) Experiment with biggest dispatch instead of iterating. Or a list. */ + for (int y = 0; y < lod_size; y++) { + for (int x = 0; x < lod_size; x++) { + ivec2 tile_co = ivec2(x, y); + ShadowTileData tile = shadow_tile_load(tilemaps_tx, tile_co, tilemap_lod, tilemap_index); + if (tile.do_update && tile.is_used && tile.is_visible && tile.is_allocated) { + /* We dispatch enough group to cover one page. */ + ivec2 page_texel = ivec2(gl_GlobalInvocationID.xy); + ivec2 in_texel = page_texel + tile_co * page_size; + ivec2 out_texel = page_texel + ivec2(tile.page) * page_size; + + float depth = texelFetch(render_tx, in_texel, 0).r; + imageStore(out_atlas_img, out_texel, vec4(depth)); + } + } + } +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_debug_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_debug_comp.glsl new file mode 100644 index 00000000000..a2ef545248a --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_debug_comp.glsl @@ -0,0 +1,113 @@ + +/** + * Virtual shadowmapping: Debug pages. + * + * Since pages are only existing if they are attached to a tilemap or the free list, + * this shader will scan every possible position and create a debug map out of it. + * This is nice to inspect the state of the page allocation during the pipeline. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_page_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl) + +layout(local_size_x = 8, local_size_y = 8) in; + +layout(std430, binding = 1) readonly restrict buffer pages_free_buf +{ + uint free_page_owners[]; +}; + +layout(r32ui) restrict uniform uimage2D tilemaps_img; + +layout(r32ui) restrict uniform uimage2D debug_img; + +/* Use this as custom channel viewer in renderdoc to inspect debug_img. */ +#if 0 + +layout(binding = 2) uniform usampler2D texUInt2D; +in vec2 uv; +out vec4 color_out; + +void main() +{ + uint page = texture(texUInt2D, uv).x; + + bool error = (page & 0xFFFFu) != 1u; + bool is_cached = (page & SHADOW_PAGE_IS_CACHED) != 0u; + bool is_needed = (page & SHADOW_PAGE_IS_NEEDED) != 0u; + bool in_heap = (page & SHADOW_PAGE_IN_FREE_HEAP) != 0u; + error = error || (is_cached && !in_heap); + error = error || (is_needed && is_cached); + + color_out = vec4(error, is_cached, is_needed, in_heap); +} + +#endif + +void main() +{ + for (int y = 0; y < imageSize(debug_img).y / int(gl_WorkGroupSize.y); y++) { + for (int x = 0; x < imageSize(debug_img).x / int(gl_WorkGroupSize.x); x++) { + ivec2 co = ivec2(x, y) * ivec2(gl_WorkGroupSize.xy) + ivec2(gl_LocalInvocationID.xy); + imageStore(debug_img, co, uvec4(0)); + } + } + + barrier(); + + /* TODO(fclem): We only scan the first line of tilemap, otherwise it is too slow. + * Finish and do it properly one day... */ + for (int y = 0; y < imageSize(tilemaps_img).y / int(gl_WorkGroupSize.y); y++) { + for (int x = 0; x < imageSize(tilemaps_img).x / int(gl_WorkGroupSize.x); x++) { + ivec2 co = ivec2(x, y) * ivec2(gl_WorkGroupSize.xy) + ivec2(gl_LocalInvocationID.xy); + ShadowTileData tile = shadow_tile_data_unpack(imageLoad(tilemaps_img, co).x); + + if (tile.is_allocated) { + /* User count. */ + imageAtomicAdd(debug_img, ivec2(tile.page), 1u); + } + } + } + + barrier(); + + for (int y = 0; y < imageSize(tilemaps_img).y / int(gl_WorkGroupSize.y); y++) { + for (int x = 0; x < imageSize(tilemaps_img).x / int(gl_WorkGroupSize.x); x++) { + ivec2 co = ivec2(x, y) * ivec2(gl_WorkGroupSize.xy) + ivec2(gl_LocalInvocationID.xy); + ShadowTileData tile = shadow_tile_data_unpack(imageLoad(tilemaps_img, co).x); + + if (tile.is_allocated) { + imageAtomicOr(debug_img, ivec2(tile.page), SHADOW_PAGE_ALLOCATED); + if (tile.is_cached) { + imageAtomicOr(debug_img, ivec2(tile.page), SHADOW_PAGE_IS_CACHED); + /* Verify reference. */ + ivec2 ref = ivec2(unpackUvec2x16(free_page_owners[tile.free_page_owner_index])); + if (ref == co) { + imageAtomicOr(debug_img, ivec2(tile.page), SHADOW_PAGE_IN_FREE_HEAP); + } + } + if (tile.is_used && tile.is_visible) { + imageAtomicOr(debug_img, ivec2(tile.page), SHADOW_PAGE_IS_NEEDED); + } + if (tile.do_update) { + imageAtomicOr(debug_img, ivec2(tile.page), SHADOW_PAGE_DO_UPDATE); + } + } + } + } + +#if 0 + for (int x = 0; x < SHADOW_MAX_PAGE; x++) { + if (free_page_owners[x] != uint(-1)) { + uvec2 owner = unpackUvec2x16(free_page_owners[x]); + uvec2 page = shadow_tile_data_unpack(imageLoad(tilemaps_img, ivec2(owner)).x).page; + /* User count. */ + imageAtomicAdd(debug_img, ivec2(page), 1u); + + imageAtomicOr(debug_img, ivec2(page), SHADOW_PAGE_IN_FREE_HEAP); + } + } +#endif +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_defrag_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_defrag_comp.glsl new file mode 100644 index 00000000000..30f0f1e8925 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_defrag_comp.glsl @@ -0,0 +1,59 @@ + +/** + * Virtual shadowmapping: Defrag. + * + * Defragment the free page owner heap making one continuous array. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_page_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl) + +layout(local_size_x = 1) in; + +layout(std430, binding = 1) restrict buffer pages_free_buf +{ + uint free_page_owners[]; +}; + +layout(std430, binding = 3) restrict buffer pages_infos_buf +{ + ShadowPagesInfoData infos; +}; + +layout(r32ui) restrict uniform uimage2D tilemaps_img; + +void find_last_valid(inout uint last_valid) +{ + for (uint i = last_valid; i > 0u; i--) { + if (free_page_owners[i] != uint(-1)) { + last_valid = i; + break; + } + } +} + +void main() +{ + uint last_valid = uint(infos.page_free_next); + + find_last_valid(last_valid); + + for (uint i = 0u; i < last_valid; i++) { + if (free_page_owners[i] == uint(-1)) { + free_page_owners[i] = free_page_owners[last_valid]; + + /* Update corresponding reference in tile. */ + ivec2 texel = ivec2(unpackUvec2x16(free_page_owners[last_valid])); + ShadowTileData tile = shadow_tile_data_unpack(imageLoad(tilemaps_img, texel).x); + tile.free_page_owner_index = i; + imageStore(tilemaps_img, texel, uvec4(shadow_tile_data_pack(tile))); + + free_page_owners[last_valid] = uint(-1); + find_last_valid(last_valid); + } + } + + infos.page_free_next = int(last_valid); +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_free_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_free_comp.glsl new file mode 100644 index 00000000000..4c013ab526c --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_free_comp.glsl @@ -0,0 +1,73 @@ + +/** + * Virtual shadowmapping: Free pages. + * + * Scan all tilemaps and add all free pages inside the free page heap. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_page_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl) + +layout(local_size_x = SHADOW_TILEMAP_RES, local_size_y = SHADOW_TILEMAP_RES) in; + +layout(std430, binding = 0) restrict readonly buffer tilemaps_buf +{ + ShadowTileMapData tilemaps[]; +}; + +layout(std430, binding = 1) restrict buffer pages_free_buf +{ + uint free_page_owners[]; +}; + +layout(std430, binding = 3) restrict buffer pages_infos_buf +{ + ShadowPagesInfoData infos; +}; + +layout(r32ui) restrict uniform uimage2D tilemaps_img; + +void main() +{ + ShadowTileMapData tilemap_data = tilemaps[gl_GlobalInvocationID.z]; + ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy); + int tilemap_idx = tilemap_data.index; + + int lod_max = tilemap_data.is_cubeface ? SHADOW_TILEMAP_LOD : 0; + for (int lod = 0; lod <= lod_max; lod++) { + uint lod_size = SHADOW_TILEMAP_RES >> lod; + + if (any(greaterThanEqual(tile_co, ivec2(lod_size)))) { + continue; + } + + ivec2 texel = shadow_tile_coord_in_atlas(tile_co, tilemap_idx, lod); + ShadowTileData tile = shadow_tile_data_unpack(imageLoad(tilemaps_img, texel).x); + + if (tile.is_allocated) { + if (tile.is_visible && tile.is_used && tile.is_cached) { + /* Try to recover cached tiles. Update flag is kept untouched as content might be valid. */ + free_page_owners[tile.free_page_owner_index] = uint(-1); + tile.is_cached = false; + tile.free_page_owner_index = uint(-1); + imageStore(tilemaps_img, texel, uvec4(shadow_tile_data_pack(tile))); + } + else if ((!tile.is_visible || !tile.is_used) && !tile.is_cached) { + /* Push page to the free page heap. */ + int free_index = atomicAdd(infos.page_free_next, 1) + 1; + if (free_index < SHADOW_MAX_PAGE) { + free_page_owners[free_index] = packUvec2x16(uvec2(texel)); + tile.is_cached = true; + tile.free_page_owner_index = uint(free_index); + imageStore(tilemaps_img, texel, uvec4(shadow_tile_data_pack(tile))); + } + else { + /* Well, this should never happen. This would mean some pages were marked + * for deletion multiple times. */ + } + } + } + } +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_init_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_init_comp.glsl new file mode 100644 index 00000000000..adc00a18948 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_init_comp.glsl @@ -0,0 +1,49 @@ + +/** + * Virtual shadowmapping: Init page buffer. + * + * All pages are always owned by tiles. This step init all owners. + * This avoid mapping the buffer to host memory. + */ + +#pragma BLENDER_REQUIRE(eevee_shadow_page_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl) + +layout(local_size_x = SHADOW_PAGE_PER_ROW) in; + +layout(std430, binding = 1) restrict writeonly buffer pages_free_buf +{ + uint free_page_owners[]; +}; + +layout(std430, binding = 3) restrict writeonly buffer pages_infos_buf +{ + ShadowPagesInfoData infos; +}; + +layout(r32ui) restrict uniform uimage2D tilemaps_img; + +void main() +{ + if (gl_GlobalInvocationID == uvec3(0)) { + infos.page_free_next = SHADOW_MAX_PAGE - 1; + infos.page_free_next_prev = 0; + infos.page_updated_count = 0; + } + + uint page_index = gl_GlobalInvocationID.x; + + ivec2 texel = ivec2(page_index % (SHADOW_TILEMAP_PER_ROW * SHADOW_TILEMAP_RES), + page_index / (SHADOW_TILEMAP_PER_ROW * SHADOW_TILEMAP_RES)); + free_page_owners[page_index] = packUvec2x16(uvec2(texel)); + + /* Start with a blank tile. */ + ShadowTileData tile = shadow_tile_data_unpack(0u); + tile.page = uvec2(page_index % uint(SHADOW_PAGE_PER_ROW), + page_index / uint(SHADOW_PAGE_PER_ROW)); + tile.free_page_owner_index = page_index; + tile.is_allocated = true; + tile.is_cached = true; + tile.do_update = true; + imageStore(tilemaps_img, texel, uvec4(shadow_tile_data_pack(tile))); +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_lib.glsl new file mode 100644 index 00000000000..4dc95dfd011 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_lib.glsl @@ -0,0 +1,17 @@ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +#define SHADOW_PAGE_ALLOCATED (1u << 16u) +#define SHADOW_PAGE_IS_CACHED (1u << 17u) +#define SHADOW_PAGE_IS_NEEDED (1u << 18u) +#define SHADOW_PAGE_IN_FREE_HEAP (1u << 19u) +#define SHADOW_PAGE_DO_UPDATE (1u << 20u) + +/** \a unormalized_uv is the uv coordinates for the whole tilemap [0..SHADOW_TILEMAP_RES]. */ +vec2 shadow_page_uv_transform(uvec2 page, uint lod, vec2 unormalized_uv) +{ + vec2 page_texel = fract(unormalized_uv / float(1u << lod)); + /* Assumes atlas is squared. */ + return (vec2(page) + page_texel) / vec2(SHADOW_PAGE_PER_ROW); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_mark_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_mark_vert.glsl new file mode 100644 index 00000000000..20de208519f --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_mark_vert.glsl @@ -0,0 +1,40 @@ + +/** + * Virtual shadowmapping: Page marking / preparation + * + * This renders a series of quad to needed pages render locations. + * This is in order to clear the depth to 1.0 only where it is needed + * to occlude any potentially costly fragment shader invocation. + */ + +#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl) + +uniform usampler2D tilemaps_tx; + +uniform int tilemap_index; +uniform int tilemap_lod; + +void main() +{ + int tile_index = gl_VertexID / 6; + ivec2 tile_co = ivec2(tile_index % SHADOW_TILEMAP_RES, tile_index / SHADOW_TILEMAP_RES); + tile_co >>= tilemap_lod; + + ShadowTileData tile = shadow_tile_load(tilemaps_tx, tile_co, tilemap_lod, tilemap_index); + + if (!tile.is_visible || !tile.is_used || !tile.do_update) { + /* Don't draw anything as we already cleared the render target for these areas. */ + gl_Position = vec4(0.0); + return; + } + + int v = gl_VertexID % 3; + /* Triangle in lower left corner in [-1..1] square. */ + vec2 pos = -1.0 + vec2((v & 1) << 1, (v & 2) << 0); + /* NOTE: this only renders if backface cull is off. */ + pos = ((gl_VertexID % 6) > 2) ? -pos : pos; + + pos = ((pos * 0.5 + 0.5) + vec2(tile_co)) / float(SHADOW_TILEMAP_RES >> tilemap_lod); + + gl_Position = vec4(pos * 2.0 - 1.0, 1.0, 1.0); +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_depth_scan_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_depth_scan_comp.glsl new file mode 100644 index 00000000000..cdb9a1a4bad --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_depth_scan_comp.glsl @@ -0,0 +1,134 @@ + +/** + * Virtual shadowmapping: Depth buffer scanning. + * We iterate through the visible lights at each scene pixel depth in order to tag only the visible + * shadow pages. + */ + +#pragma BLENDER_REQUIRE(common_intersection_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_culling_iter_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) +#pragma BLENDER_REQUIRE(eevee_light_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl) + +layout(local_size_x = SHADOW_DEPTH_SCAN_GROUP_SIZE, + local_size_y = SHADOW_DEPTH_SCAN_GROUP_SIZE) in; + +layout(std430, binding = 0) readonly restrict buffer lights_buf +{ + LightData lights[]; +}; + +layout(std430, binding = 1) readonly restrict buffer lights_zbins_buf +{ + CullingZBin lights_zbins[]; +}; + +layout(std430, binding = 2) readonly restrict buffer lights_culling_buf +{ + CullingData light_culling; +}; + +layout(std430, binding = 3) readonly restrict buffer lights_tile_buf +{ + CullingWord lights_culling_words[]; +}; + +layout(r32ui) restrict uniform uimage2D tilemaps_img; + +uniform sampler2D depth_tx; + +uniform float tilemap_pixel_radius; +uniform float screen_pixel_radius_inv; + +void tag_tilemap(uint l_idx, vec3 P, float dist_to_cam, const bool is_directional) +{ + LightData light = lights[l_idx]; + ShadowData shadow = light.shadow_data; + + if (light.shadow_id == LIGHT_NO_SHADOW) { + return; + } + + int lod = 0; + ivec2 tile_co; + int tilemap_index = shadow.tilemap_index; + if (is_directional) { + int clipmap_lod = shadow_directional_clipmap_level(shadow, dist_to_cam); + int clipmap_lod_relative = clipmap_lod - shadow.clipmap_lod_min; + /* Compute how many time we need to subdivide. */ + float clipmap_res_mul = float(1 << (shadow.clipmap_lod_max - clipmap_lod)); + /* Compute offset of the clipmap from the largest LOD. */ + vec2 clipmap_offset = vec2(abs(shadow.base_offset) >> clipmap_lod_relative) * + sign(shadow.base_offset); + + /* [-SHADOW_TILEMAP_RES/2..SHADOW_TILEMAP_RES/2] range for highest LOD. */ + vec3 lP = transform_point(shadow.mat, P); + tile_co = ivec2(floor(lP.xy * clipmap_res_mul - clipmap_offset)) + SHADOW_TILEMAP_RES / 2; + tile_co = clamp(tile_co, ivec2(0), ivec2(SHADOW_TILEMAP_RES - 1)); + tilemap_index += clipmap_lod_relative; + tilemap_index = clamp(tilemap_index, shadow.tilemap_index, shadow.tilemap_last); + } + else { + vec3 lL = light_world_to_local(light, P - light._position); + float dist_to_light = length(lL); + if (dist_to_light > light.influence_radius_max) { + return; + } + /* How much a shadow map pixel covers a final image pixel. */ + float footprint_ratio = dist_to_light * (tilemap_pixel_radius * screen_pixel_radius_inv); + /* Project the radius to the screen. 1 unit away from the camera the same way + * pixel_world_radius_inv was computed. Not needed in orthographic mode. */ + bool is_persp = (ProjectionMatrix[3][3] == 0.0); + if (is_persp) { + footprint_ratio /= dist_to_cam; + } + lod = int(ceil(-log2(footprint_ratio))); + lod = clamp(lod, 0, SHADOW_TILEMAP_LOD); + + int face_id = shadow_punctual_face_index_get(lL); + lL = shadow_punctual_local_position_to_face_local(face_id, lL); + + uint lod_res = uint(SHADOW_TILEMAP_RES) >> uint(lod); + tile_co = ivec2(((lL.xy / abs(lL.z)) * 0.5 + 0.5) * float(lod_res)); + tile_co = clamp(tile_co, ivec2(0), ivec2(lod_res - 1)); + tilemap_index += face_id; + } + + const uint flag = SHADOW_TILE_IS_USED; + shadow_tile_set_flag(tilemaps_img, tile_co, lod, tilemap_index, flag); +} + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + ivec2 tex_size = textureSize(depth_tx, 0).xy; + + if (!in_range_inclusive(texel, ivec2(0), ivec2(tex_size - 1))) { + return; + } + + float depth = texelFetch(depth_tx, texel, 0).r; + vec2 uv = vec2(texel) / vec2(tex_size); + vec3 vP = get_view_space_from_depth(uv, depth); + vec3 P = transform_point(ViewMatrixInverse, vP); + + if (depth == 1.0) { + return; + } + + float dist_to_cam = length(vP); + + LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_culling, l_idx) { + tag_tilemap(l_idx, P, dist_to_cam, true); + } + LIGHT_FOREACH_END + + LIGHT_FOREACH_BEGIN_LOCAL ( + light_culling, lights_zbins, lights_culling_words, gl_GlobalInvocationID.xy, vP.z, l_idx) { + tag_tilemap(l_idx, P, dist_to_cam, false); + } + LIGHT_FOREACH_END +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lib.glsl new file mode 100644 index 00000000000..39553d5eb92 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lib.glsl @@ -0,0 +1,216 @@ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +/* ---------------------------------------------------------------------- */ +/** \name Tilemap data + * \{ */ + +/** Decoded tile data structure. */ +struct ShadowTileData { + /** Page inside the virtual shadow map atlas. */ + uvec2 page; + /** Page owner index inside free_page_owners heap. Only valid if is_cached is true. */ + uint free_page_owner_index; + /** Lod pointed to by LOD 0 tile page. (cubemap only) */ + uint lod; + /** Set to true during the setup phase if the tile is inside the view frustum. */ + bool is_visible; + /** If the tile is needed for rendering. */ + bool is_used; + /** True if this tile owns the page and if it points to a valid page. */ + bool is_allocated; + /** True if an update is needed. */ + bool do_update; + /** True if the tile is indexed inside the free_page_owners heap. */ + bool is_cached; +}; + +#define SHADOW_TILE_NO_DATA 0u +#define SHADOW_TILE_IS_CACHED (1u << 27u) +#define SHADOW_TILE_IS_ALLOCATED (1u << 28u) +#define SHADOW_TILE_DO_UPDATE (1u << 29u) +#define SHADOW_TILE_IS_VISIBLE (1u << 30u) +#define SHADOW_TILE_IS_USED (1u << 31u) + +ShadowTileData shadow_tile_data_unpack(uint data) +{ + ShadowTileData tile; + /* Tweaked for SHADOW_PAGE_PER_ROW = 64. */ + tile.page.x = data & 63u; + tile.page.y = (data >> 6u) & 63u; + /* Tweaked for SHADOW_TILEMAP_LOD < 8. */ + tile.lod = (data >> 12u) & 7u; + /* Tweaked for SHADOW_MAX_TILEMAP = 4096. */ + tile.free_page_owner_index = (data >> 15u) & 4095u; + tile.is_visible = flag_test(data, SHADOW_TILE_IS_VISIBLE); + tile.is_used = flag_test(data, SHADOW_TILE_IS_USED); + tile.is_cached = flag_test(data, SHADOW_TILE_IS_CACHED); + tile.is_allocated = flag_test(data, SHADOW_TILE_IS_ALLOCATED); + tile.do_update = flag_test(data, SHADOW_TILE_DO_UPDATE); + return tile; +} + +uint shadow_tile_data_pack(ShadowTileData tile) +{ + uint data; + data = tile.page.x; + data |= tile.page.y << 6u; + data |= tile.lod << 12u; + data |= tile.free_page_owner_index << 15u; + set_flag_from_test(data, tile.is_visible, SHADOW_TILE_IS_VISIBLE); + set_flag_from_test(data, tile.is_used, SHADOW_TILE_IS_USED); + set_flag_from_test(data, tile.is_allocated, SHADOW_TILE_IS_ALLOCATED); + set_flag_from_test(data, tile.is_cached, SHADOW_TILE_IS_CACHED); + set_flag_from_test(data, tile.do_update, SHADOW_TILE_DO_UPDATE); + return data; +} + +int shadow_tile_index(ivec2 tile) +{ + return tile.x + tile.y * SHADOW_TILEMAP_RES; +} + +ivec2 shadow_tile_coord(int tile_index) +{ + return ivec2(tile_index % SHADOW_TILEMAP_RES, tile_index / SHADOW_TILEMAP_RES); +} + +/* Return bottom left pixel position of the tilemap inside the tilemap atlas. */ +ivec2 shadow_tilemap_start(int tilemap_index) +{ + /* Assumes base map is squared. */ + ivec2 start = SHADOW_TILEMAP_RES * ivec2(tilemap_index % SHADOW_TILEMAP_PER_ROW, + tilemap_index / SHADOW_TILEMAP_PER_ROW); + return start; +} + +/* Return bottom left pixel position of the tilemap inside the tilemap atlas. */ +ivec2 shadow_tilemap_start(int tilemap_index, int lod) +{ + /* Assumes base map is squared. */ + ivec2 start = shadow_tilemap_start(tilemap_index) >> lod; + if (lod > 0) { + const int lod0_res = SHADOW_TILEMAP_RES * SHADOW_TILEMAP_PER_ROW; + start.y += lod0_res; + start.x += lod0_res - (lod0_res >> (lod - 1)); + } + return start; +} + +ivec2 shadow_tile_coord_in_atlas(ivec2 tile, int tilemap_index) +{ + return shadow_tilemap_start(tilemap_index) + tile; +} + +ivec2 shadow_tile_coord_in_atlas(ivec2 tile, int tilemap_index, int lod) +{ + return shadow_tilemap_start(tilemap_index, lod) + tile; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Load / Store functions. + * \{ */ + +void shadow_tile_store(restrict uimage2D tilemaps_img, + ivec2 tile_co, + int tilemap_index, + ShadowTileData data) +{ + uint tile_data = shadow_tile_data_pack(data); + imageStore(tilemaps_img, shadow_tile_coord_in_atlas(tile_co, tilemap_index), uvec4(tile_data)); +} + +void shadow_tile_store( + restrict uimage2D tilemaps_img, ivec2 tile_co, int lod, int tilemap_index, ShadowTileData data) +{ + uint tile_data = shadow_tile_data_pack(data); + imageStore( + tilemaps_img, shadow_tile_coord_in_atlas(tile_co, tilemap_index, lod), uvec4(tile_data)); +} +/* Ugly define because some compilers seems to not like the fact the imageAtomicOr is inside + * a function. */ +#define shadow_tile_set_flag(tilemaps_img, tile_co, lod, tilemap_index, flag) \ + imageAtomicOr(tilemaps_img, shadow_tile_coord_in_atlas(tile_co, tilemap_index, lod), flag) +#define shadow_tile_unset_flag(tilemaps_img, tile_co, lod, tilemap_index, flag) \ + imageAtomicAnd(tilemaps_img, shadow_tile_coord_in_atlas(tile_co, tilemap_index, lod), ~(flag)) + +ShadowTileData shadow_tile_load(restrict uimage2D tilemaps_img, + ivec2 tile_co, + int lod, + int tilemap_index) +{ + uint tile_data = SHADOW_TILE_NO_DATA; + if (in_range_inclusive(tile_co, ivec2(0), ivec2(SHADOW_TILEMAP_RES - 1))) { + tile_data = imageLoad(tilemaps_img, shadow_tile_coord_in_atlas(tile_co, tilemap_index, lod)).x; + } + return shadow_tile_data_unpack(tile_data); +} + +ShadowTileData shadow_tile_load(usampler2D tilemaps_tx, ivec2 tile_co, int lod, int tilemap_index) +{ + uint tile_data = SHADOW_TILE_NO_DATA; + if (in_range_inclusive(tile_co, ivec2(0), ivec2(SHADOW_TILEMAP_RES - 1))) { + tile_data = + texelFetch(tilemaps_tx, shadow_tile_coord_in_atlas(tile_co, tilemap_index, lod), 0).x; + } + return shadow_tile_data_unpack(tile_data); +} + +/* This function should be the inverse of ShadowTileMap::tilemap_coverage_get. */ +int shadow_directional_clipmap_level(ShadowData shadow, float distance_to_camera) +{ + /* Why do we need to bias by 2 here? I don't know... */ + int clipmap_lod = int(ceil(log2(distance_to_camera))) + 2; + return clamp(clipmap_lod, shadow.clipmap_lod_min, shadow.clipmap_lod_max); +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Frustum shapes. + * \{ */ + +vec3 shadow_tile_corner_persp(ShadowTileMapData tilemap, ivec2 tile) +{ + return tilemap.corners[1].xyz + tilemap.corners[2].xyz * float(tile.x) + + tilemap.corners[3].xyz * float(tile.y); +} + +Pyramid shadow_tilemap_cubeface_bounds(ShadowTileMapData tilemap, + ivec2 tile_start, + const ivec2 extent) +{ + Pyramid shape; + shape.corners[0] = tilemap.corners[0].xyz; + shape.corners[1] = shadow_tile_corner_persp(tilemap, tile_start + ivec2(0, 0)); + shape.corners[2] = shadow_tile_corner_persp(tilemap, tile_start + ivec2(extent.x, 0)); + shape.corners[3] = shadow_tile_corner_persp(tilemap, tile_start + extent); + shape.corners[4] = shadow_tile_corner_persp(tilemap, tile_start + ivec2(0, extent.y)); + return shape; +} + +vec3 shadow_tile_corner_ortho(ShadowTileMapData tilemap, ivec2 tile, const bool far) +{ + return tilemap.corners[0].xyz + tilemap.corners[1].xyz * float(tile.x) + + tilemap.corners[2].xyz * float(tile.y) + tilemap.corners[3].xyz * float(far); +} + +Box shadow_tilemap_clipmap_bounds(ShadowTileMapData tilemap, ivec2 tile_start, const ivec2 extent) +{ + Box shape; + shape.corners[0] = shadow_tile_corner_ortho(tilemap, tile_start + ivec2(0, 0), false); + shape.corners[1] = shadow_tile_corner_ortho(tilemap, tile_start + ivec2(extent.x, 0), false); + shape.corners[2] = shadow_tile_corner_ortho(tilemap, tile_start + extent, false); + shape.corners[3] = shadow_tile_corner_ortho(tilemap, tile_start + ivec2(0, extent.y), false); + shape.corners[4] = shadow_tile_corner_ortho(tilemap, tile_start + ivec2(0, 0), true); + shape.corners[5] = shadow_tile_corner_ortho(tilemap, tile_start + ivec2(extent.x, 0), true); + shape.corners[6] = shadow_tile_corner_ortho(tilemap, tile_start + extent, true); + shape.corners[7] = shadow_tile_corner_ortho(tilemap, tile_start + ivec2(0, extent.y), true); + return shape; +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lod_mask_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lod_mask_comp.glsl new file mode 100644 index 00000000000..3a6c1eb8128 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lod_mask_comp.glsl @@ -0,0 +1,70 @@ + +/** + * Virtual shadowmapping: LOD mask. + * + * Discard pages that are redundant in the mipmap chain. + * We mask tiles that are completely covered by higher LOD tiles. + */ + +#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl) + +layout(local_size_x = SHADOW_TILEMAP_RES, local_size_y = SHADOW_TILEMAP_RES) in; + +layout(std430, binding = 0) restrict readonly buffer tilemaps_buf +{ + ShadowTileMapData tilemaps[]; +}; + +layout(r32ui) restrict uniform uimage2D tilemaps_img; + +void main() +{ + ShadowTileMapData tilemap_data = tilemaps[gl_GlobalInvocationID.z]; + int tilemap_idx = tilemap_data.index; + int lod_max = tilemap_data.is_cubeface ? SHADOW_TILEMAP_LOD : 0; + + /* Bitmap of usage tests. Use one uint per tile. One bit per lod level. */ + shared uint lod_map[SHADOW_TILEMAP_RES * SHADOW_TILEMAP_RES]; + + /* For now there is nothing to do for directional shadows. */ + if (tilemap_data.is_cubeface) { + ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy); + + lod_map[SHADOW_TILEMAP_RES * tile_co.y + tile_co.x] = 0u; + + int map_index = tile_co.y * SHADOW_TILEMAP_RES + tile_co.x; + for (int lod = 0; lod <= lod_max; lod++) { + ivec2 tile_co_lod = ivec2(gl_GlobalInvocationID.xy) >> lod; + ShadowTileData tile = shadow_tile_load(tilemaps_img, tile_co_lod, lod, tilemap_idx); + + if (tile.is_used && tile.is_visible) { + lod_map[map_index] |= 1u << uint(lod); + } + } + + barrier(); + + /* We mask tiles from lower LOD that are completely covered by the lods above it. */ + for (int lod = 1; lod <= SHADOW_TILEMAP_LOD; lod++) { + uint stride = 1u << uint(lod); + if ((gl_GlobalInvocationID.xy % stride) != uvec2(0)) { + continue; + } + uint lod_mask = ~(~0x0u << uint(lod)); + bool tiles_covered = true; + for (int x = 0; x < stride && tiles_covered; x++) { + for (int y = 0; y < stride && tiles_covered; y++) { + ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy) + ivec2(x, y); + uint lod_bits = lod_map[tile_co.y * SHADOW_TILEMAP_RES + tile_co.x]; + if ((lod_bits & lod_mask) == 0u) { + tiles_covered = false; + } + } + } + if (tiles_covered) { + ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy >> uint(lod)); + shadow_tile_unset_flag(tilemaps_img, tile_co, lod, tilemap_idx, SHADOW_TILE_IS_USED); + } + } + } +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_setup_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_setup_comp.glsl new file mode 100644 index 00000000000..d6f01f6326e --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_setup_comp.glsl @@ -0,0 +1,90 @@ + +/** + * Virtual shadowmapping: Setup phase for tilemaps. + * During this phase we clear the visibility, usage and request bits. + * This is also where we shifts the whole tilemap for directional shadow clipmaps + */ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) +#pragma BLENDER_REQUIRE(eevee_shadow_page_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl) + +layout(local_size_x = SHADOW_TILEMAP_RES, local_size_y = SHADOW_TILEMAP_RES) in; + +layout(std430, binding = 0) readonly buffer tilemaps_buf +{ + ShadowTileMapData tilemaps[]; +}; + +layout(std430, binding = 1) restrict buffer pages_free_buf +{ + uint free_page_owners[]; +}; + +layout(std430, binding = 3) restrict buffer pages_infos_buf +{ + ShadowPagesInfoData infos; +}; + +layout(r32ui) restrict uniform uimage2D tilemaps_img; + +uniform bool do_tilemap_setup; + +void main() +{ + ShadowTileMapData tilemap = tilemaps[gl_GlobalInvocationID.z]; + ivec2 grid_shift = (do_tilemap_setup) ? tilemap.grid_shift : ivec2(0); + ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy); + ivec2 tile_shifted = tile_co + grid_shift; + /* Still load a valid tile after the shifting in order to not loose any page reference. + * This way the tile can even be reused if it is needed. Also avoid negative modulo. */ + ivec2 tile_wrapped = (tile_shifted + SHADOW_TILEMAP_RES) % SHADOW_TILEMAP_RES; + + ivec2 texel_out = shadow_tile_coord_in_atlas(tile_co, tilemap.index, 0); + ivec2 texel_in = shadow_tile_coord_in_atlas(tile_wrapped, tilemap.index, 0); + + ShadowTileData tile_data = shadow_tile_data_unpack(imageLoad(tilemaps_img, texel_in).x); + tile_data.is_visible = false; + tile_data.is_used = false; + tile_data.lod = 0; + + if (!in_range_inclusive(tile_shifted, ivec2(0), ivec2(SHADOW_TILEMAP_RES - 1))) { + /* This tile was shifted in. */ + tile_data.do_update = true; + } + + if (grid_shift != ivec2(0) && tile_data.is_cached) { + /* Update page location after shift. */ + free_page_owners[tile_data.free_page_owner_index] = packUvec2x16(uvec2(texel_out)); + } + + imageStore(tilemaps_img, texel_out, uvec4(shadow_tile_data_pack(tile_data))); + + if (tilemap.is_cubeface) { + /* Cubemap shift update is always all or nothing. */ + bool do_update = (grid_shift.x != 0); + + /* Number of lod0 tiles covered by the current lod level (in one dimension). */ + uint lod_stride = 1u << 1u; + uint lod_size = uint(SHADOW_TILEMAP_RES) >> 1u; + for (int lod = 1; lod <= SHADOW_TILEMAP_LOD; lod++, lod_size >>= 1u, lod_stride <<= 1u) { + if (all(lessThan(tile_co, ivec2(lod_size)))) { + ivec2 texel = shadow_tile_coord_in_atlas(tile_co, tilemap.index, lod); + + ShadowTileData tile_data = shadow_tile_data_unpack(imageLoad(tilemaps_img, texel).x); + tile_data.is_visible = false; + tile_data.is_used = false; + tile_data.do_update = do_update; + tile_data.lod = 0; + imageStore(tilemaps_img, texel, uvec4(shadow_tile_data_pack(tile_data))); + } + } + } + + if (gl_GlobalInvocationID == uvec3(0)) { + infos.page_free_next = max(-1, infos.page_free_next); + infos.page_free_next_prev = infos.page_free_next; + infos.page_updated_count = 0; + } +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_tag_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_tag_comp.glsl new file mode 100644 index 00000000000..72457880b73 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_tag_comp.glsl @@ -0,0 +1,221 @@ + +/** + * Virtual shadowmapping: AABB rendering + * Making entity checks on CPU can be an intensive task if scene is really complex. + * Some entities may need to tag certain shadow pages to be updated or needed. + */ + +/** + * TODO(fclem) : Future plans. Do not rely on rasterization and use a single compute shader for + * both visibility and usage. Une one thread group per tilemap and each thread compute one AABB. + * Use groupshared memory to hold a bitmap of the result. Each thread "rasterize" onto the bitmap + * using atomicOr. Iterate through all AABBs using this threagroup and can early out if all tiles + * are tagged. Could even compute AABB of each batch of 64 to skip an entire batch. + * + * This would avoid relying on arbitrary point size support and be more performant. + */ + +#pragma BLENDER_REQUIRE(common_intersection_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) +#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl) + +layout(local_size_x = SHADOW_AABB_TAG_GROUP_SIZE) in; + +layout(std430, binding = 0) readonly buffer tilemaps_buf +{ + ShadowTileMapData tilemaps[]; +}; + +layout(std430, binding = 1) readonly buffer aabb_buf +{ + AABB aabbs[]; +}; + +layout(r32ui) restrict uniform uimage2D tilemaps_img; + +uniform int aabb_len; +uniform float tilemap_pixel_radius; +uniform float screen_pixel_radius_inv; + +vec3 safe_project(ShadowTileMapData tilemap, inout int clipped, vec3 v) +{ + vec4 tmp = tilemap.tilemat * vec4(v, 1.0); + /* Detect case when point is behind the camera. */ + clipped += int(tmp.w < 0.0); + return tmp.xyz / tmp.w; +} + +void main() +{ + ShadowTileMapData tilemap = tilemaps[gl_GlobalInvocationID.z]; + + /* Bitmap of tile intersection tests. Use one uint per row for each LOD. */ + shared uint flag_map[SHADOW_TILEMAP_RES * 2]; + if (gl_LocalInvocationID.x == 0u) { + for (int i = 0; i < SHADOW_TILEMAP_RES * 2; i++) { + flag_map[i] = 0u; + } + } + barrier(); + + Pyramid frustum; + if (tilemap.is_cubeface) { + frustum = shadow_tilemap_cubeface_bounds(tilemap, ivec2(0), ivec2(SHADOW_TILEMAP_RES)); + } + + int iter = divide_ceil_i(aabb_len, int(gl_WorkGroupSize.x)); + for (int i = 0; i < iter; i++) { + int aabb_index = i * int(gl_WorkGroupSize.x) + int(gl_GlobalInvocationID.x); + if (aabb_index >= aabb_len) { + break; + } + AABB aabb = aabbs[aabb_index]; + /* Avoid completely flat object disapearing. */ + aabb.max += 1e-6; + aabb.min -= 1e-6; + + Box box = to_box(aabb); + +#ifdef TAG_USAGE + /* Skip non visible objects. */ + if (!intersect_view(box)) { + continue; + } +#endif + + int lod_min = 0; + int lod_max = 0; +#ifdef TAG_USAGE + if (tilemap.is_cubeface) { + /* TODO(fclem): This is imprecise as we only evaluate one point. + * Evaluate more point to get a range? but which ones? */ + vec3 nearest_receiver = (aabb.min + aabb.max) * 0.5; + float len = distance(nearest_receiver, frustum.corners[0]); + /* How much a shadow map pixel covers a final image pixel. */ + float footprint_ratio = len * (tilemap_pixel_radius * screen_pixel_radius_inv); + /* Project the radius to the screen. 1 unit away from the camera the same way + * pixel_world_radius_inv was computed. Not needed in orthographic mode. */ + bool is_persp = (ProjectionMatrix[3][3] == 0.0); + if (is_persp) { + footprint_ratio /= distance(nearest_receiver, cameraPos); + } + + lod_min = lod_max = int(ceil(-log2(footprint_ratio))); + +# if 0 /* DEBUG */ + vec4 green = vec4(0, 1, 0, 1); + vec4 yellow = vec4(1, 1, 0, 1); + vec4 red = vec4(1, 0, 0, 1); + float dist_fac = (is_persp) ? distance(nearest_receiver, cameraPos) : 1.0; + drw_debug_point(nearest_receiver, 128.0 * dist_fac / screen_pixel_radius_inv, green); + drw_debug_point(nearest_receiver, len * tilemap_pixel_radius * 128.0, yellow); +# endif + } + lod_max = clamp(lod_max, 0, SHADOW_TILEMAP_LOD); + lod_min = clamp(lod_min, 0, SHADOW_TILEMAP_LOD); +#endif + +#ifdef TAG_UPDATE + // drw_debug(aabb, vec4(0, 1, 0, 1)); +#else /* TAG_USAGE */ + // drw_debug(aabb, vec4(1, 1, 0, 1)); +#endif + + int clipped = 0; + /* TODO(fclem) project bbox instead of AABB. */ + /* NDC space post projection [-1..1] (unclamped). */ + AABB aabb_ndc = init_min_max(); + for (int v = 0; v < 8; v++) { + merge(aabb_ndc, safe_project(tilemap, clipped, box.corners[v])); + } + +#ifdef TAG_UPDATE + /* Update tag all LODs. */ + lod_max = SHADOW_TILEMAP_LOD; + lod_min = 0; +#endif + + if (tilemap.is_cubeface) { + if (clipped == 8) { + /* All verts are behind the camera. */ + continue; + } + else if (clipped > 0) { + /* Not all verts are behind the near clip plane. */ + if (intersect(frustum, box)) { + /* We cannot correctly handle this case so we fallback by covering the whole view. */ + aabb_ndc.max = vec3(vec2(SHADOW_TILEMAP_RES), 1.0); + aabb_ndc.min = vec3(0.0, 0.0, -1.0); + } + else { + /* Still out of the frustum. Ignore. */ + continue; + } + } + else { + /* Reject false positive when box is on the side of the frustum but fail other tests. */ + /* AABB of the entire light is enough for this case. */ + AABB aabb_light = AABB(frustum.corners[0] - vec3(tilemap._punctual_distance), + frustum.corners[0] + vec3(tilemap._punctual_distance)); + if (!intersect(aabb, aabb_light)) { + continue; + } + } + } + + /* Discard if the bbox does not touch the rendering frustum. */ +#ifdef TAG_UPDATE + const float min_depth = -1.0; + const float max_depth = 1.0; +#else /* TAG_USAGE */ + float max_depth = tilemap._max_usage_depth; + float min_depth = tilemap._min_usage_depth; +#endif + AABB aabb_tag; + const AABB aabb_map = AABB(vec3(0.0, 0.0, min_depth), + vec3(vec2(SHADOW_TILEMAP_RES) - 1e-6, max_depth)); + if (!intersect(aabb_map, aabb_ndc, aabb_tag)) { + continue; + } + + /* Raster the Box. */ + ivec2 range_min = ivec2(aabb_tag.min.xy) >> lod_min; + ivec2 range_max = ivec2(aabb_tag.max.xy) >> lod_min; + + for (int lod = lod_min; lod <= lod_max; lod++) { + for (int y = range_min.y; y <= range_max.y; y++) { + int flag_idx = (SHADOW_TILEMAP_RES >> lod) + y; + uint row_bits = bit_field_mask(range_max.x - range_min.x + 1, range_min.x); + atomicOr(flag_map[flag_idx], row_bits); + } + range_min >>= 1; + range_max >>= 1; + } + } + + barrier(); + + if (gl_LocalInvocationID.x == 0u) { +#ifdef TAG_UPDATE + const uint flag = SHADOW_TILE_DO_UPDATE; +#else /* TAG_USAGE */ + const uint flag = SHADOW_TILE_IS_USED; +#endif + int lod_max = tilemap.is_cubeface ? SHADOW_TILEMAP_LOD : 0; + /* Number of lod0 tiles covered by the current lod level (in one dimension). */ + uint lod_size = uint(SHADOW_TILEMAP_RES); + /* TODO(fclem): Could use multiple thread to set flag. */ + for (int lod = 0; lod <= lod_max; lod++, lod_size >>= 1) { + for (int y = 0; y < lod_size; y++) { + int flag_idx = (SHADOW_TILEMAP_RES >> lod) + y; + uint row_bits = flag_map[flag_idx]; + while (row_bits != 0u) { + int x = findLSB(row_bits); + row_bits &= ~1u << uint(x); + shadow_tile_set_flag(tilemaps_img, ivec2(x, y), lod, tilemap.index, flag); + } + } + } + } +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_visibility_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_visibility_comp.glsl new file mode 100644 index 00000000000..34d6b43ea05 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_visibility_comp.glsl @@ -0,0 +1,149 @@ + +/** + * Virtual shadowmapping: Visibility phase for tilemaps. + * During this phase we compute the visibility of each tile for the active view frustum. + * TODO(fclem) Could also test visibility against Z buffer (would help in interiors space). + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_intersection_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(local_size_x = SHADOW_TILEMAP_RES, local_size_y = SHADOW_TILEMAP_RES) in; + +layout(std430, binding = 0) readonly buffer tilemaps_buf +{ + ShadowTileMapData tilemaps[]; +}; + +uniform float tilemap_pixel_radius; +uniform float screen_pixel_radius_inv; + +layout(r32ui) restrict uniform uimage2D tilemaps_img; + +void main() +{ + ShadowTileMapData tilemap = tilemaps[gl_GlobalInvocationID.z]; + ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy); + + bool is_intersecting; + int lod_visible_min = 0; + int lod_visible_max = 0; + + if (tilemap.is_cubeface) { + Pyramid shape = shadow_tilemap_cubeface_bounds(tilemap, tile_co, ivec2(1)); + + is_intersecting = intersect_view(shape); + + if (is_intersecting && (tilemap.cone_angle_cos > -1.0)) { + /* Reject tile not in spot light cone angle. */ + vec3 tile_dir = normalize((shape.corners[3] - shape.corners[1]) * 0.5 + + (shape.corners[1] - shape.corners[0])); + /* cone_angle_cos is already biased to include the maximum tile cone half angle. */ + if (dot(tilemap.cone_direction, tile_dir) < tilemap.cone_angle_cos) { + is_intersecting = false; + } + } + + if (is_intersecting) { + /* Test minimum receiver distance and compute min and max visible LOD. */ + float len; + vec3 tile_center = (shape.corners[1] + shape.corners[3]) * 0.5; + vec3 tile_center_dir = normalize_len(tile_center - shape.corners[0], len); + /* Project the tile center to the frustum and compare the shadow texel density at this + * position since this is where the density ratio will be the lowest (meanning the highest + * LOD). NOTE: There is some inacuracy because we only project one point instead of + * projecting each individual pixels. */ + for (int p = 0; p < 6; p++) { + float facing = dot(tile_center_dir, -frustum_planes[p].xyz); + float d = line_plane_intersect_dist(shape.corners[0], tile_center_dir, frustum_planes[p]); + if (d > 0.0 && facing > 0.0) { + len = min(d, len); + } + } + vec3 nearest_receiver = shape.corners[0] + tile_center_dir * len; + /* How much a shadow map pixel covers a final image pixel. */ + float footprint_ratio = len * (tilemap_pixel_radius * screen_pixel_radius_inv); + /* Project the radius to the screen. 1 unit away from the camera the same way + * pixel_world_radius_inv was computed. Not needed in orthographic mode. */ + bool is_persp = (ProjectionMatrix[3][3] == 0.0); + if (is_persp) { + footprint_ratio /= distance(nearest_receiver, cameraPos); + } + +#if 0 /* DEBUG */ + if (gl_GlobalInvocationID.z == 0u) { + vec4 green = vec4(0, 1, 0, 1); + vec4 yellow = vec4(1, 1, 0, 1); + vec4 red = vec4(1, 0, 0, 1); + float dist_fac = (is_persp) ? distance(nearest_receiver, cameraPos) : 1.0; + drw_debug_point(nearest_receiver, 128.0 * dist_fac / screen_pixel_radius_inv, green); + drw_debug_point(shape.corners[0] + tile_center_dir, tilemap_pixel_radius * 128.0, red); + drw_debug_point(nearest_receiver, len * tilemap_pixel_radius * 128.0, yellow); + } +#endif + + lod_visible_min = int(ceil(-log2(footprint_ratio))); + /* FIXME(fclem): This should be computed using the farthest intersection with the view. */ + lod_visible_max = SHADOW_TILEMAP_LOD; + + lod_visible_max = clamp(lod_visible_max, 0, SHADOW_TILEMAP_LOD); + lod_visible_min = clamp(lod_visible_min, 0, SHADOW_TILEMAP_LOD); + } + + /* Bitmap of intersection tests. Use one uint per row. */ + shared uint intersect_map[SHADOW_TILEMAP_RES]; + + /* Number of lod0 tiles covered by the current lod level (in one dimension). */ + uint lod_stride = 1u; + uint lod_size = uint(SHADOW_TILEMAP_RES); + for (int lod = 1; lod <= SHADOW_TILEMAP_LOD; lod++) { + lod_size >>= 1; + lod_stride <<= 1; + + barrier(); + if (is_intersecting && lod >= lod_visible_min && lod <= lod_visible_max) { + atomicOr(intersect_map[tile_co.y], (1u << tile_co.x)); + } + else { + atomicAnd(intersect_map[tile_co.y], ~(1u << tile_co.x)); + } + /* Control flow is uniform inside a workgroup. */ + barrier(); + + if (all(lessThan(tile_co, ivec2(lod_size)))) { + uint col_mask = bit_field_mask(lod_stride, lod_stride * tile_co.x); + bool visible = false; + uint row = lod_stride * tile_co.y; + uint row_max = lod_stride + row; + for (; row < row_max && !visible; row++) { + visible = (intersect_map[row] & col_mask) != 0; + } + if (visible) { + shadow_tile_set_flag(tilemaps_img, tile_co, lod, tilemap.index, SHADOW_TILE_IS_VISIBLE); + } + } + } + } + else { + /* TODO(fclem): We can save a few tile more by shaping the BBoxes in depth based on their + * distance to the center. */ + Box shape = shadow_tilemap_clipmap_bounds(tilemap, tile_co, ivec2(1)); + + is_intersecting = intersect_view(shape); + + if (is_intersecting) { + /* Reject tiles not in view distance. */ + float tile_dist = length(vec2(tile_co - (SHADOW_TILEMAP_RES / 2)) + 0.5); + if (tile_dist > (SHADOW_TILEMAP_RES / 2) + M_SQRT2 * 0.5) { + is_intersecting = false; + } + } + } + + if (is_intersecting && lod_visible_min == 0) { + shadow_tile_set_flag(tilemaps_img, tile_co, 0, tilemap.index, SHADOW_TILE_IS_VISIBLE); + } +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_subsurface_eval_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_subsurface_eval_frag.glsl new file mode 100644 index 00000000000..eff25cc7cf8 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_subsurface_eval_frag.glsl @@ -0,0 +1,155 @@ + +/** + * Postprocess diffuse radiance output from the diffuse evaluation pass to mimic subsurface + * transmission. + * + * This implementation follows the technique described in the siggraph presentation: + * "Efficient screen space subsurface scattering Siggraph 2018" + * by Evgenii Golubev + * + * But, instead of having all the precomputed weights for all three color primaries, + * we precompute a weight profile texture to be able to support per pixel AND per channel radius. + **/ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_closure_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(std140) uniform subsurface_block +{ + SubsurfaceData sss; +}; + +layout(std140) uniform hiz_block +{ + HiZData hiz; +}; + +uniform sampler2D hiz_tx; +uniform sampler2D radiance_tx; +uniform sampler2D transmit_color_tx; +uniform sampler2D transmit_normal_tx; +uniform sampler2D transmit_data_tx; + +in vec4 uvcoordsvar; + +layout(location = 0) out vec4 out_combined; +/* TODO(fclem) Output to diffuse pass without feedback loop. */ + +vec3 burley_setup(vec3 radius, vec3 albedo) +{ + /* Scale albedo because we can have HDR value caused by BSDF sampling. */ + vec3 A = albedo / max(1e-6, max_v3(albedo)); + /* Diffuse surface transmission, equation (6). */ + vec3 s = 1.9 - A + 3.5 * sqr(A - 0.8); + /* Mean free path length adapted to fit ancient Cubic and Gaussian models. */ + vec3 l = 0.25 * M_1_PI * radius; + + return l / s; +} + +vec3 burley_eval(vec3 d, float r) +{ + /* Slide 33. */ + vec3 exp_r_3_d = exp(-r / (3.0 * d)); + vec3 exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d; + /** NOTE: + * - Surface albedo is applied at the end. + * - This is normalized diffuse model, so the equation is multiplied + * by 2*pi, which also matches cdf(). + */ + return (exp_r_d + exp_r_3_d) / (4.0 * d); +} + +void main(void) +{ + vec2 center_uv = uvcoordsvar.xy; + + float gbuffer_depth = texelFetch(hiz_tx, ivec2(gl_FragCoord.xy), 0).r; + vec3 vP = get_view_space_from_depth(center_uv, gbuffer_depth); + vec4 tra_col_in = texture(transmit_color_tx, center_uv); + vec4 tra_nor_in = texture(transmit_normal_tx, center_uv); + vec4 tra_dat_in = texture(transmit_data_tx, center_uv); + + ClosureDiffuse diffuse = gbuffer_load_diffuse_data(tra_col_in, tra_nor_in, tra_dat_in); + + if (diffuse.sss_id == 0u) { + /* Normal diffuse is already in combined pass. */ + /* Refraction also go into this case. */ + out_combined = vec4(0.0); + return; + } + + float max_radius = max_v3(diffuse.sss_radius); + + float homcoord = ProjectionMatrix[2][3] * vP.z + ProjectionMatrix[3][3]; + vec2 sample_scale = vec2(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) * + (0.5 * max_radius / homcoord); + + float pixel_footprint = sample_scale.x * float(textureSize(radiance_tx, 0).x); + if (pixel_footprint <= 1.0) { + /* Early out. */ + out_combined = vec4(texture(radiance_tx, center_uv).rgb * diffuse.color, 0.0); + return; + } + + diffuse.sss_radius = max(vec3(1e-4), diffuse.sss_radius / max_radius) * max_radius; + vec3 d = burley_setup(diffuse.sss_radius, diffuse.color); + + /* Do not rotate too much to avoid too much cache misses. */ + float golden_angle = M_PI * (3.0 - sqrt(5.0)); + float theta = interlieved_gradient_noise(gl_FragCoord.xy, 0, 0.0) * golden_angle; + float cos_theta = cos(theta); + float sin_theta = sqrt(1.0 - sqr(cos_theta)); + mat2 rot = mat2(cos_theta, sin_theta, -sin_theta, cos_theta); + + mat2 scale = mat2(sample_scale.x, 0.0, 0.0, sample_scale.y); + mat2 sample_space = scale * rot; + + vec3 accum_weight = vec3(0.0); + vec3 accum = vec3(0.0); + + /* TODO/OPTI(fclem) Make separate sample set for lower radius. */ + + for (int i = 0; i < sss.sample_len; i++) { + vec2 sample_uv = center_uv + sample_space * sss.samples[i].xy; + float pdf_inv = sss.samples[i].z; + + float sample_depth = textureLod(hiz_tx, sample_uv * hiz.uv_scale, 0.0).r; + vec3 sample_vP = get_view_space_from_depth(sample_uv, sample_depth); + + vec4 sample_data = texture(radiance_tx, sample_uv); + vec3 sample_radiance = sample_data.rgb; + uint sample_sss_id = uint(sample_data.a); + + if (sample_sss_id != diffuse.sss_id) { + continue; + } + + /* Discard out of bounds samples. */ + if (any(lessThan(sample_uv, vec2(0.0))) || any(greaterThan(sample_uv, vec2(1.0)))) { + continue; + } + + /* Slide 34. */ + float r = distance(sample_vP, vP); + vec3 weight = burley_eval(d, r) * pdf_inv; + + accum += sample_radiance * weight; + accum_weight += weight; + } + /* Normalize the sum (slide 34). */ + accum /= accum_weight; + /* Apply surface color on final radiance. */ + accum *= diffuse.color; + + /* Debug, detect NaNs. */ + if (any(isnan(accum))) { + accum = vec3(1.0, 0.0, 1.0); + } + + out_combined = vec4(accum, 0.0); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_background_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_background_frag.glsl new file mode 100644 index 00000000000..ddfcf8eaef8 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_background_frag.glsl @@ -0,0 +1,57 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_attribute_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_bsdf_stubs_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(location = 0) out vec4 out_background; + +void main(void) +{ + g_data = init_globals(); + /* View position is passed to keep accuracy. */ + g_data.N = normal_view_to_world(viewCameraVec(interp.P)); + g_data.Ng = g_data.N; + g_data.P = -g_data.N + cameraPos; + + attrib_load(); + + nodetree_surface(); + + out_background.rgb = safe_color(g_emission_data.emission); + out_background.a = saturate(avg(g_transparency_data.transmittance)); +} + +vec3 attr_load_orco(vec4 orco) +{ + return -g_data.N; +} + +/* Unsupported. */ +vec4 attr_load_tangent(vec4 tangent) +{ + return vec4(0); +} +vec4 attr_load_vec4(vec4 attr) +{ + return vec4(0); +} +vec3 attr_load_vec3(vec3 attr) +{ + return vec3(0); +} +vec2 attr_load_vec2(vec2 attr) +{ + return vec2(0); +} +vec4 attr_load_color(vec4 attr) +{ + return vec4(0); +} +vec3 attr_load_uv(vec3 attr) +{ + return vec3(0); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_deferred_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_deferred_frag.glsl new file mode 100644 index 00000000000..51e00ef43d3 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_deferred_frag.glsl @@ -0,0 +1,96 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_bsdf_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(std140) uniform sampling_block +{ + SamplingData sampling; +}; + +uniform sampler2DArray utility_tx; + +utility_tx_fetch_define(utility_tx); +utility_tx_sample_define(utility_tx); + +/* Diffuse or Transmission Color. */ +layout(location = 0) out vec3 out_transmit_color; +/* RG: Normal (negative if Tranmission), B: SSS ID, A: Min-Thickness */ +layout(location = 1) out vec4 out_transmit_normal; +/* RGB: SSS RGB Radius. + * or + * R: Transmission IOR, G: Transmission Roughness, B: Unused. */ +layout(location = 2) out vec3 out_transmit_data; +/* Reflection Color. */ +layout(location = 3) out vec3 out_reflection_color; +/* RG: Normal, B: Roughness X, A: Roughness Y. */ +layout(location = 4) out vec4 out_reflection_normal; +/* Volume Emission, Absorption, Scatter, Phase. */ +layout(location = 5) out uvec4 out_volume_data; +/* Emission. */ +layout(location = 6) out vec3 out_emission_data; +/* Transparent BSDF, Holdout. */ +layout(location = 7) out vec4 out_transparency_data; + +void main(void) +{ + g_data = init_globals(); + + float noise_offset = sampling_rng_1D_get(sampling, SAMPLING_CLOSURE); + float noise = utility_tx_fetch(gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r; + g_data.closure_rand = fract(noise + noise_offset); + /* TODO(fclem) other RNG. */ + g_data.transmit_rand = fract(g_data.closure_rand * 6.1803398875); + + float thickness = nodetree_thickness(); + + nodetree_surface(); + + float alpha = saturate(1.0 - avg(g_transparency_data.transmittance)); + vec3 V = cameraVec(g_data.P); + + if (alpha > 0.0) { + g_diffuse_data.color /= alpha; + g_reflection_data.color /= alpha; + g_refraction_data.color /= alpha; + g_emission_data.emission /= alpha; + } + + if (gl_FrontFacing) { + g_refraction_data.ior = safe_rcp(g_refraction_data.ior); + } + + g_reflection_data.N = ensure_valid_reflection(g_data.Ng, V, g_reflection_data.N); + + { + out_reflection_color = g_reflection_data.color; + out_reflection_normal.xy = gbuffer_encode_normal(g_reflection_data.N); + out_reflection_normal.z = max(1e-4, g_reflection_data.roughness); + } + + if (g_data.transmit_rand == 0.0) { + out_transmit_color = g_refraction_data.color; + out_transmit_normal.xy = gbuffer_encode_normal(g_refraction_data.N); + out_transmit_normal.z = -1.0; + out_transmit_normal.w = thickness; + out_transmit_data.x = g_refraction_data.ior; + out_transmit_data.y = g_refraction_data.roughness; + } + else { + /* Output diffuse / SSS in transmit data. */ + out_transmit_color = g_diffuse_data.color; + out_transmit_normal.xy = gbuffer_encode_normal(g_diffuse_data.N); + out_transmit_normal.z = fract(float(g_diffuse_data.sss_id) / 1024.0); + out_transmit_normal.w = thickness; + out_transmit_data = g_diffuse_data.sss_radius; + } + + out_volume_data = gbuffer_store_volume_data(g_volume_data); + out_emission_data = gbuffer_store_emission_data(g_emission_data); + out_transparency_data = gbuffer_store_transparency_data(g_transparency_data); +} diff --git a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_depth_frag.glsl index fd08dfda060..f4fe834d5e0 100644 --- a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_depth_frag.glsl @@ -1,21 +1,24 @@ -/* Required by some nodes. */ -#pragma BLENDER_REQUIRE(common_hair_lib.glsl) -#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +/** + * Complex depth shader that stochastically discard transparent pixel. + */ #pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl) -#pragma BLENDER_REQUIRE(closure_type_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_diffuse_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_translucent_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_refraction_lib.glsl) -#pragma BLENDER_REQUIRE(surface_lib.glsl) - -#ifdef USE_ALPHA_HASH - -/* From the paper "Hashed Alpha Testing" by Chris Wyman and Morgan McGuire */ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_bsdf_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) +#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl) + +layout(std140) uniform sampling_block +{ + SamplingData sampling; +}; + +utility_tx_sample_define_stub(utility_tx) + +/* From the paper "Hashed Alpha Testing" by Chris Wyman and Morgan McGuire. */ float hash(vec2 a) { return fract(1e4 * sin(17.0 * a.x + 0.1 * a.y) * (0.1 + abs(sin(13.0 * a.y + a.x)))); @@ -26,11 +29,11 @@ float hash3d(vec3 a) return hash(vec2(hash(a.xy), a.z)); } -float hashed_alpha_threshold(vec3 co) +float hashed_alpha_threshold(float hash_scale, float hash_offset, vec3 P) { /* Find the discretized derivatives of our coordinates. */ - float max_deriv = max(length(dFdx(co)), length(dFdy(co))); - float pix_scale = 1.0 / (alphaHashScale * max_deriv); + float max_deriv = max(length(dFdx(P)), length(dFdy(P))); + float pix_scale = 1.0 / (hash_scale * max_deriv); /* Find two nearest log-discretized noise scales. */ float pix_scale_log = log2(pix_scale); @@ -40,8 +43,8 @@ float hashed_alpha_threshold(vec3 co) /* Compute alpha thresholds at our two noise scales. */ vec2 alpha; - alpha.x = hash3d(floor(pix_scales.x * co)); - alpha.y = hash3d(floor(pix_scales.y * co)); + alpha.x = hash3d(floor(pix_scales.x * P)); + alpha.y = hash3d(floor(pix_scales.y * P)); /* Factor to interpolate lerp with. */ float fac = fract(log2(pix_scale)); @@ -60,7 +63,7 @@ float hashed_alpha_threshold(vec3 co) float threshold = (x < one_a) ? ((x < a) ? cases.x : cases.y) : cases.z; /* Jitter the threshold for TAA accumulation. */ - threshold = fract(threshold + alphaHashOffset); + threshold = fract(threshold + hash_offset); /* Avoids threshold == 0. */ threshold = clamp(threshold, 1.0e-6, 1.0); @@ -68,19 +71,17 @@ float hashed_alpha_threshold(vec3 co) return threshold; } -#endif - -void main() +void main(void) { -#if defined(USE_ALPHA_HASH) + g_data = init_globals(); - Closure cl = nodetree_exec(); + nodetree_surface(); - float opacity = saturate(1.0 - avg(cl.transmittance)); + float noise_offset = sampling_rng_1D_get(sampling, SAMPLING_TRANSPARENCY); + float random_threshold = hashed_alpha_threshold(1.0, noise_offset, g_data.P); - /* Hashed Alpha Testing */ - if (opacity < hashed_alpha_threshold(worldPosition)) { + float transparency = avg(g_transparency_data.transmittance); + if (transparency > random_threshold) { discard; } -#endif } diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_depth_simple_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_depth_simple_frag.glsl new file mode 100644 index 00000000000..e9ac4cfcfb0 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_depth_simple_frag.glsl @@ -0,0 +1,25 @@ + +/** + * Simple passthrough shader. Outputs depth without ammendment. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_bsdf_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) +#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl) + +layout(std140) uniform sampling_block +{ + SamplingData sampling; +}; + +utility_tx_sample_define_stub(utility_tx) + +void main(void) +{ + /* No color output, only depth (line below is implicit). */ + /* gl_FragDepth = gl_FragCoord.z; */ +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_forward_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_forward_frag.glsl new file mode 100644 index 00000000000..2a4f0176a08 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_forward_frag.glsl @@ -0,0 +1,237 @@ + +/** + * Forward lighting evaluation: Lighting is evaluated during the geometry rasterization. + * + * This is used by alpha blended materials and materials using Shader to RGB nodes. + **/ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_bsdf_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_culling_iter_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) +#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_raytrace_raygen_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_raytrace_trace_lib.glsl) + +/* TODO(fclem) Option. */ +#define USE_RAYTRACING + +layout(std140) uniform sampling_block +{ + SamplingData sampling; +}; + +layout(std430, binding = 0) readonly restrict buffer lights_buf +{ + LightData lights[]; +}; + +layout(std430, binding = 1) readonly restrict buffer lights_zbins_buf +{ + CullingZBin lights_zbins[]; +}; + +layout(std430, binding = 2) readonly restrict buffer lights_culling_buf +{ + CullingData light_culling; +}; + +layout(std430, binding = 3) readonly restrict buffer lights_tile_buf +{ + CullingWord lights_culling_words[]; +}; + +layout(std140) uniform grids_block +{ + GridData grids[GRID_MAX]; +}; + +layout(std140) uniform cubes_block +{ + CubemapData cubes[CULLING_ITEM_BATCH]; +}; + +layout(std140) uniform lightprobes_info_block +{ + LightProbeInfoData probes_info; +}; + +layout(std140) uniform rt_diffuse_block +{ + RaytraceData raytrace_diffuse; +}; + +layout(std140) uniform rt_reflection_block +{ + RaytraceData raytrace_reflection; +}; + +layout(std140) uniform rt_refraction_block +{ + RaytraceData raytrace_refraction; +}; + +layout(std140) uniform hiz_block +{ + HiZData hiz; +}; + +uniform sampler2DArray utility_tx; +uniform sampler2D shadow_atlas_tx; +uniform usampler2D shadow_tilemaps_tx; +uniform sampler1D sss_transmittance_tx; +uniform sampler2DArray lightprobe_grid_tx; +uniform samplerCubeArray lightprobe_cube_tx; +uniform sampler2D hiz_tx; +uniform sampler2D radiance_tx; + +utility_tx_fetch_define(utility_tx); +utility_tx_sample_define(utility_tx); + +layout(location = 0, index = 0) out vec4 out_radiance; +layout(location = 0, index = 1) out vec4 out_transmittance; + +/* Prototypes. */ +void light_eval(ClosureDiffuse diffuse, + ClosureReflection reflection, + vec3 P, + vec3 V, + float vP_z, + float thickness, + inout vec3 out_diffuse, + inout vec3 out_specular); +vec3 lightprobe_grid_eval(vec3 P, vec3 N, float random_threshold); +vec3 lightprobe_cubemap_eval(vec3 P, vec3 R, float roughness, float random_threshold); + +void main(void) +{ + g_data = init_globals(); + + float noise = utility_tx_fetch(gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r; + g_data.closure_rand = fract(noise + sampling_rng_1D_get(sampling, SAMPLING_CLOSURE)); + g_data.transmit_rand = -1.0; + + float thickness = nodetree_thickness(); + + nodetree_surface(); + + float vP_z = get_view_z_from_depth(gl_FragCoord.z); + vec3 V = cameraVec(g_data.P); + vec3 P = g_data.P; + + float noise_probe = utility_tx_fetch(gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).g; + float random_probe = fract(noise_probe + sampling_rng_1D_get(sampling, SAMPLING_LIGHTPROBE)); + + if (gl_FrontFacing) { + g_refraction_data.ior = safe_rcp(g_refraction_data.ior); + } + + g_reflection_data.N = ensure_valid_reflection(g_data.Ng, V, g_reflection_data.N); + + vec3 radiance_diffuse = vec3(0); + vec3 radiance_reflection = vec3(0); + vec3 radiance_refraction = vec3(0); + + light_eval(g_diffuse_data, + g_reflection_data, + P, + V, + vP_z, + thickness, + radiance_diffuse, + radiance_reflection); + + vec4 noise_rt = utility_tx_fetch(gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).rgba; + vec2 noise_offset = sampling_rng_2D_get(sampling, SAMPLING_RAYTRACE_W); + noise_rt.zw = fract(noise_rt.zw + noise_offset); + + { + float pdf; /* UNUSED */ + bool hit = false; + Ray ray = raytrace_create_diffuse_ray(sampling, noise_rt.xy, g_diffuse_data, P, pdf); + ray = raytrace_world_ray_to_view(ray); +#ifdef USE_RAYTRACING + /* Extend the ray to cover the whole view. */ + ray.direction *= 1e16; + hit = raytrace_screen(raytrace_diffuse, hiz, hiz_tx, noise_rt.w, 1.0, false, true, ray); +#endif + if (hit) { + vec2 hit_uv = get_uvs_from_view(ray.origin + ray.direction); + radiance_diffuse += textureLod(radiance_tx, hit_uv, 0.0).rgb; + } + else { + ray = raytrace_view_ray_to_world(ray); + radiance_diffuse += lightprobe_cubemap_eval(P, ray.direction, 1.0, random_probe); + } + } + + { + float pdf; /* UNUSED */ + bool hit = false; + float roughness = g_reflection_data.roughness; + Ray ray = raytrace_create_reflection_ray(sampling, noise_rt.xy, g_reflection_data, V, P, pdf); + ray = raytrace_world_ray_to_view(ray); +#ifdef USE_RAYTRACING + if (roughness - noise_rt.z * 0.2 < raytrace_reflection.max_roughness) { + /* Extend the ray to cover the whole view. */ + ray.direction *= 1e16; + hit = raytrace_screen( + raytrace_reflection, hiz, hiz_tx, noise_rt.w, roughness, false, true, ray); + } +#endif + if (hit) { + vec2 hit_uv = get_uvs_from_view(ray.origin + ray.direction); + radiance_reflection += textureLod(radiance_tx, hit_uv, 0.0).rgb; + } + else { + ray = raytrace_view_ray_to_world(ray); + radiance_reflection += lightprobe_cubemap_eval( + P, ray.direction, sqr(roughness), random_probe); + } + } + + { + float pdf; /* UNUSED */ + bool hit = false; + float roughness = g_refraction_data.roughness; + Ray ray = raytrace_create_refraction_ray(sampling, noise_rt.xy, g_refraction_data, V, P, pdf); + ray = raytrace_world_ray_to_view(ray); +#ifdef USE_RAYTRACING + if (roughness - noise_rt.z * 0.2 < raytrace_refraction.max_roughness) { + /* Extend the ray to cover the whole view. */ + ray.direction *= 1e16; + /* TODO(fclem): Take IOR into account in the roughness LOD bias. */ + hit = raytrace_screen( + raytrace_refraction, hiz, hiz_tx, noise_rt.w, roughness, false, true, ray); + } +#endif + if (hit) { + vec2 hit_uv = get_uvs_from_view(ray.origin + ray.direction); + radiance_refraction += textureLod(radiance_tx, hit_uv, 0.0).rgb; + } + else { + ray = raytrace_view_ray_to_world(ray); + radiance_refraction += lightprobe_cubemap_eval( + P, ray.direction, sqr(roughness), random_probe); + } + } + + // volume_eval(ray, volume_radiance, volume_transmittance, volume_depth); + + out_radiance.rgb = radiance_diffuse * g_diffuse_data.color; + out_radiance.rgb += radiance_reflection * g_reflection_data.color; + out_radiance.rgb += radiance_refraction * g_refraction_data.color; + out_radiance.rgb += g_emission_data.emission; + out_radiance.a = 0.0; + + out_transmittance.rgb = g_transparency_data.transmittance; + out_transmittance.a = saturate(avg(out_transmittance.rgb)); +} + +#pragma BLENDER_REQUIRE_POST(eevee_light_eval_lib.glsl) +#pragma BLENDER_REQUIRE_POST(eevee_lightprobe_eval_cubemap_lib.glsl) +#pragma BLENDER_REQUIRE_POST(eevee_lightprobe_eval_grid_lib.glsl) diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_gpencil_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_gpencil_vert.glsl new file mode 100644 index 00000000000..d69aa22b3e0 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_gpencil_vert.glsl @@ -0,0 +1,86 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_attribute_lib.glsl) +#pragma BLENDER_REQUIRE(common_gpencil_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl) + +in ivec4 ma, ma1, ma2, ma3; +in vec4 pos, pos1, pos2, pos3, uv1, uv2, col1, col2, fcol1; + +/* Globals to feed the load functions */ +vec2 uvs; +vec4 color; + +/* TODO(fclem) remove use of macro. use interface block instead. */ +RESOURCE_ID_VARYING + +void main(void) +{ + vec4 sspos; + vec2 aspect; + vec2 thickness; + + gl_Position = gpencil_vertex(ma, + ma1, + ma2, + ma3, + pos, + pos1, + pos2, + pos3, + uv1, + uv2, + col1, + col2, + fcol1, + vec4(ViewportSize, ViewportSizeInverse), + interp.P, + interp.N, + color, + uvs, + sspos, + aspect, + thickness); + + interp.barycentric_coords = vec2(0.0); + interp.barycentric_dists = vec3(0.0); + + PASS_RESOURCE_ID + attrib_load(); +} + +vec3 attr_load_orco(vec4 orco) +{ + vec3 lP = point_world_to_object(interp.P); + return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz; +} + +vec4 attr_load_tangent(vec4 tangent) +{ + /* TODO */ + return vec4(0.0, 0.0, 0.0, 1.0); +} + +/* Only have one uv and one color attribute layer. */ +vec3 attr_load_uv(vec3 dummy) +{ + return vec3(uvs, 0.0); +} +vec4 attr_load_color(vec4 dummy) +{ + return color; +} + +/* Not supported. */ +vec4 attr_load_vec4(vec4 attr) +{ + return vec4(0.0); +} +vec3 attr_load_vec3(vec3 attr) +{ + return vec3(0.0); +} +vec2 attr_load_vec2(vec2 attr) +{ + return vec2(0.0); +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_hair_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_hair_vert.glsl new file mode 100644 index 00000000000..ed749b33463 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_hair_vert.glsl @@ -0,0 +1,80 @@ + +#pragma BLENDER_REQUIRE(common_attribute_lib.glsl) +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl) + +/* TODO(fclem) remove use of macro. use interface block instead. */ +RESOURCE_ID_VARYING + +/* Globals to feed the load functions. */ +vec3 T; + +void main(void) +{ + bool is_persp = (ProjectionMatrix[3][3] == 0.0); + hair_get_pos_tan_binor_time(is_persp, + ModelMatrixInverse, + ViewMatrixInverse[3].xyz, + ViewMatrixInverse[2].xyz, + interp.P, + T, + interp.hair_binormal, + interp.hair_time, + interp.hair_thickness, + interp.hair_time_width); + + interp.N = cross(T, interp.hair_binormal); + interp.hair_strand_id = hair_get_strand_id(); + interp.barycentric_coords = hair_get_barycentric(); + + PASS_RESOURCE_ID + attrib_load(); + + g_data = init_globals(); + interp.P += nodetree_displacement(); + + gl_Position = point_world_to_ndc(interp.P); +} + +#ifdef OBINFO_LIB +vec3 attr_load_orco(samplerBuffer cd_buf) +{ + vec3 P = hair_get_strand_pos(); + vec3 lP = transform_point(ModelMatrixInverse, P); + return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz; +} +#endif + +vec4 attr_load_tangent(samplerBuffer cd_buf) +{ + /* Not supported. */ + return vec4(0.0, 0.0, 0.0, 1.0); +} + +vec3 attr_load_uv(samplerBuffer cd_buf) +{ + return texelFetch(cd_buf, interp.hair_strand_id).rgb; +} + +vec4 attr_load_color(samplerBuffer cd_buf) +{ + return texelFetch(cd_buf, interp.hair_strand_id).rgba; +} + +vec4 attr_load_vec4(samplerBuffer cd_buf) +{ + return texelFetch(cd_buf, interp.hair_strand_id).rgba; +} + +vec3 attr_load_vec3(samplerBuffer cd_buf) +{ + return texelFetch(cd_buf, interp.hair_strand_id).rgb; +} + +vec2 attr_load_vec2(samplerBuffer cd_buf) +{ + return texelFetch(cd_buf, interp.hair_strand_id).rg; +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_lib.glsl new file mode 100644 index 00000000000..d20e7860a38 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_lib.glsl @@ -0,0 +1,63 @@ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl) + +IN_OUT SurfaceInterface +{ + vec3 P; + vec3 N; + vec2 barycentric_coords; + flat vec3 barycentric_dists; + vec3 hair_binormal; + float hair_time; + float hair_time_width; + float hair_thickness; + flat int hair_strand_id; +} +interp; + +#if defined(GPU_FRAGMENT_SHADER) || defined(GPU_VERTEX_SHADER) +GlobalData init_globals(void) +{ + GlobalData surf; + surf.P = interp.P; + surf.N = normalize(interp.N); +# ifndef MAT_GEOM_GPENCIL + surf.N = (FrontFacing) ? surf.N : -surf.N; +# endif +# ifdef GPU_FRAGMENT_SHADER + surf.Ng = safe_normalize(cross(dFdx(surf.P), dFdy(surf.P))); +# else + surf.Ng = surf.N; +# endif + +# ifdef MAT_GEOM_HAIR + /* Shade as a cylinder. */ + float cos_theta = interp.hair_time_width / interp.hair_thickness; + float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta)); + surf.N = normalize(interp.N * sin_theta + interp.hair_binormal * cos_theta); +# endif + +# ifdef MAT_GEOM_HAIR + surf.is_strand = true; + surf.hair_time = interp.hair_time; + surf.hair_thickness = interp.hair_thickness; + surf.hair_strand_id = interp.hair_strand_id; + surf.barycentric_coords = hair_resolve_barycentric(interp.barycentric_coords); +# else + surf.is_strand = false; + surf.hair_time = 0.0; + surf.hair_thickness = 0.0; + surf.hair_strand_id = 0; + surf.barycentric_coords = interp.barycentric_coords; +# endif + surf.barycentric_dists = interp.barycentric_dists; + surf.ray_type = RAY_TYPE_CAMERA; + surf.ray_depth = 0.0; + surf.ray_length = distance(surf.P, cameraPos); + surf.closure_rand = 0.5; + return surf; +} +#endif diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_lookdev_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_lookdev_vert.glsl new file mode 100644 index 00000000000..1fc84f1f267 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_lookdev_vert.glsl @@ -0,0 +1,34 @@ + +/** + * Custom vertex shader for rendering the lookdev overlay (reference material spheres). + * The input mesh is a sphere. The output is a flattened version that will render at depth 0. + **/ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl) + +in vec3 pos; +in vec3 nor; + +/* TODO(fclem) remove use of macro. use interface block instead. */ +RESOURCE_ID_VARYING + +void main(void) +{ + interp.P = pos; + interp.N = nor; + interp.barycentric_coords = vec2(0.0); + interp.barycentric_dists = vec3(0.0); + + PASS_RESOURCE_ID + + /* Camera transform is passed via the model matrix. */ + gl_Position.xyz = transform_direction(ModelMatrix, interp.P); + /* Apply packed bias & scale. */ + gl_Position.xy *= ModelMatrix[3].xy; + gl_Position.xy += ModelMatrix[3].zw; + + /* Override depth. */ + gl_Position.z = -1.0; + gl_Position.w = 1.0; +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_mesh_geom.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_mesh_geom.glsl new file mode 100644 index 00000000000..29558a0958e --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_mesh_geom.glsl @@ -0,0 +1,33 @@ + +/** + * Optional geometry shader stage to compute barycentric coords + * Only needed / compatible with mesh or gpencil geometry. + * Main is generated in eevee_shader.cc to avoid compilation issue on some drivers. + */ + +layout(triangles) in; +layout(triangle_strip, max_vertices = 3) out; + +vec3 calc_barycentric_distances(vec3 pos0, vec3 pos1, vec3 pos2) +{ + vec3 edge21 = pos2 - pos1; + vec3 edge10 = pos1 - pos0; + vec3 edge02 = pos0 - pos2; + vec3 d21 = normalize(edge21); + vec3 d10 = normalize(edge10); + vec3 d02 = normalize(edge02); + + vec3 dists; + float d = dot(d21, edge02); + dists.x = sqrt(dot(edge02, edge02) - d * d); + d = dot(d02, edge10); + dists.y = sqrt(dot(edge10, edge10) - d * d); + d = dot(d10, edge21); + dists.z = sqrt(dot(edge21, edge21) - d * d); + return dists; +} + +vec2 calc_barycentric_co(int vertid) +{ + return vec2((vertid % 3) == 0, (vertid % 3) == 1); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_mesh_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_mesh_vert.glsl new file mode 100644 index 00000000000..f29cf57c1a8 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_mesh_vert.glsl @@ -0,0 +1,72 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_attribute_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl) + +in vec3 pos; +in vec3 nor; + +/* TODO(fclem) remove use of macro. use interface block instead. */ +RESOURCE_ID_VARYING + +void main(void) +{ + interp.P = point_object_to_world(pos); + interp.N = normal_object_to_world(nor); + interp.barycentric_coords = vec2(0.0); + interp.barycentric_dists = vec3(0.0); + + PASS_RESOURCE_ID + attrib_load(); + + g_data = init_globals(); + + interp.P += nodetree_displacement(); + + gl_Position = point_world_to_ndc(interp.P); +} + +#ifdef OBINFO_LIB +vec3 attr_load_orco(vec4 orco) +{ + /* We know when there is no orco layer when orco.w is 1.0 because it uses the generic vertex + * attrib (which is [0,0,0,1]). */ + if (orco.w == 0.0) { + return orco.xyz * 0.5 + 0.5; + } + else { + /* If the object does not have any deformation, the orco layer calculation is done on the fly + * using the orco_madd factors. */ + return OrcoTexCoFactors[0].xyz + pos * OrcoTexCoFactors[1].xyz; + } +} +#endif + +vec4 attr_load_tangent(vec4 tangent) +{ + tangent.xyz = safe_normalize(normal_object_to_world(tangent.xyz)); + return tangent; +} + +/* Simple passthrough. */ +vec4 attr_load_vec4(vec4 attr) +{ + return attr; +} +vec3 attr_load_vec3(vec3 attr) +{ + return attr; +} +vec2 attr_load_vec2(vec2 attr) +{ + return attr; +} +vec4 attr_load_color(vec4 attr) +{ + return attr; +} +vec3 attr_load_uv(vec3 attr) +{ + return attr; +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_frag.glsl new file mode 100644 index 00000000000..06e3eea9413 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_frag.glsl @@ -0,0 +1,43 @@ + +/** + * Output two 2D screen space velocity vector from object motion. + * There is a separate output for view and camera vectors. + * Camera vectors are used for reprojections and view vectors are used for motion blur fx. + * xy: Previous position > Current position + * zw: Current position > Next position + **/ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_surface_velocity_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(std140) uniform camera_prev_block +{ + CameraData cam_prev; +}; + +layout(std140) uniform camera_curr_block +{ + CameraData cam_curr; +}; + +layout(std140) uniform camera_next_block +{ + CameraData cam_next; +}; + +layout(location = 0) out vec4 out_velocity_camera; +layout(location = 1) out vec4 out_velocity_view; + +void main(void) +{ + compute_velocity(interp.P_prev, + interp.P, + interp.P_next, + cam_prev, + cam_curr, + cam_next, + out_velocity_camera, + out_velocity_view); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_lib.glsl new file mode 100644 index 00000000000..a1c1075b472 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_lib.glsl @@ -0,0 +1,8 @@ + +IN_OUT VelocityInterface +{ + vec3 P; + vec3 P_next; + vec3 P_prev; +} +interp; diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_mesh_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_mesh_vert.glsl new file mode 100644 index 00000000000..4abadc9f18d --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_velocity_mesh_vert.glsl @@ -0,0 +1,40 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_surface_velocity_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) + +layout(std140) uniform object_block +{ + VelocityObjectData velocity; +}; + +uniform int data_offset; + +in vec3 pos; +in vec3 prv; +in vec3 nxt; + +vec3 velocity_object_to_world_prev(VelocityObjectData data, vec3 prev_pos, vec3 current_pos) +{ + /* Encoded use_deform inside the matrix to save up space. */ + bool use_deform = data.next_object_mat[3][3] == 0.0; + return transform_point(data.prev_object_mat, use_deform ? prev_pos : current_pos); +} + +vec3 velocity_object_to_world_next(VelocityObjectData data, vec3 next_pos, vec3 current_pos) +{ + /* Encoded use_deform inside the matrix to save up space. */ + bool use_deform = data.next_object_mat[3][3] == 0.0; + mat4 obmat = data.next_object_mat; + obmat[3][3] = 1.0; + return transform_point(obmat, use_deform ? next_pos : current_pos); +} + +void main(void) +{ + interp.P = point_object_to_world(pos); + interp.P_prev = velocity_object_to_world_prev(velocity, prv, pos); + interp.P_next = velocity_object_to_world_next(velocity, nxt, pos); + + gl_Position = point_world_to_ndc(interp.P); +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/eevee_surface_world_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_surface_world_vert.glsl new file mode 100644 index 00000000000..bdff0582b45 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_surface_world_vert.glsl @@ -0,0 +1,26 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_surface_lib.glsl) + +/* TODO(fclem) remove use of macro. use interface block instead. */ +RESOURCE_ID_VARYING + +void main(void) +{ + /* Fullscreen triangle. */ + int v = gl_VertexID % 3; + float x = float((v & 1) << 2) - 1.0; + float y = float((v & 2) << 1) - 1.0; + gl_Position = vec4(x, y, 1.0, 1.0); + + /* Pass view position to keep accuracy. */ + interp.P = project_point(ProjectionMatrixInverse, gl_Position.xyz); + interp.N = vec3(1); + /* Unsupported. */ + interp.barycentric_coords = vec2(0.0); + interp.barycentric_dists = vec3(0.0); + + /* Used to pass the correct model matrix. */ + PASS_RESOURCE_ID +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_velocity_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_velocity_lib.glsl new file mode 100644 index 00000000000..766cb9b8cce --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_velocity_lib.glsl @@ -0,0 +1,38 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shader_shared.hh) +#pragma BLENDER_REQUIRE(eevee_camera_lib.glsl) + +void compute_velocity(vec3 P_prev, + vec3 P, + vec3 P_next, + CameraData cam_prev, + CameraData cam_curr, + CameraData cam_next, + out vec4 velocity_camera, + out vec4 velocity_view) +{ + vec2 prev_uv, curr_uv, next_uv; + prev_uv = camera_uv_from_world(cam_prev, P_prev); + curr_uv = camera_uv_from_world(cam_curr, P); + next_uv = camera_uv_from_world(cam_next, P_next); + + velocity_camera.xy = prev_uv - curr_uv; + velocity_camera.zw = curr_uv - next_uv; + + if (is_panoramic(cam_curr.type)) { + /* This path is only used if using using panoramic projections. Since the views always have + * the same 45° aperture angle, we can safely reuse the projection matrix. */ + prev_uv = transform_point(ProjectionMatrix, transform_point(cam_prev.viewmat, P_prev)).xy; + curr_uv = transform_point(ViewProjectionMatrix, P).xy; + next_uv = transform_point(ProjectionMatrix, transform_point(cam_next.viewmat, P_next)).xy; + + velocity_view.xy = prev_uv - curr_uv; + velocity_view.zw = curr_uv - next_uv; + /* Convert NDC velocity to UV velocity */ + velocity_view *= 0.5; + } + else { + velocity_view = velocity_camera; + } +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_volume_deferred_frag.glsl b/source/blender/draw/engines/eevee/shaders/eevee_volume_deferred_frag.glsl new file mode 100644 index 00000000000..359480e9e3c --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_volume_deferred_frag.glsl @@ -0,0 +1,66 @@ + +/** + * Renders heterogeneous volumes. + **/ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_volume_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_volume_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) + +uniform sampler2D depth_max_tx; + +layout(location = 0) out uvec4 out_volume_data; /* Volume Emission, Absorption, Scatter. */ +layout(location = 1) out vec4 out_transparency_data; /* Transparent BSDF, Holdout. */ + +void main(void) +{ + // g_volume = init_globals(); + + vec2 uv = gl_FragCoord.xy / vec2(textureSize(depth_max_tx, 0).xy); + + /* For volumes from solid objects. */ + vec3 vP_start = get_view_space_from_depth(uv, gl_FragCoord.z); + vec3 vP_end = get_view_space_from_depth(uv, texture(depth_max_tx, uv).r); + + Ray ray; + ray.origin = vP_start; + ray.direction = vP_end - vP_start; + ray.direction /= abs(ray.direction.z); + ray.max_time = max(vP_start.z - vP_end.z, 0.0); + + /* Refine bounds to skip empty areas. */ + // float dist = line_unit_box_intersect_dist(ls_ray_ori, ls_ray_dir); + // if (dist > 0.0) { + // ls_ray_ori = ls_ray_dir * dist + ls_ray_ori; + // } + + // vec3 ls_vol_isect = ls_ray_end - ls_ray_ori; + // if (dot(ls_ray_dir, ls_vol_isect) < 0.0) { + // /* Start is further away than the end. + // * That means no volume is intersected. */ + // discard; + // } + + vec3 out_depth_time; + vec3 out_radiance = vec3(0.0); + vec3 out_transmittance = vec3(1.0); + // volume_eval_scattering_transmittance( + // P, depth_min, depth_max, out_radiance, out_transmittance, gl_FragDepth); + + volume_eval_homogenous(ray, out_transmittance, out_depth_time); + + gl_FragDepth = get_depth_from_view_z(ray.origin.z - avg(out_depth_time)); + + g_volume_data.emission = vec3(0); + g_volume_data.scattering = out_radiance; + g_volume_data.transmittance = out_transmittance; + g_volume_data.anisotropy = VOLUME_HETEROGENEOUS; + + g_transparency_data.transmittance = vec3(1.0); + g_transparency_data.holdout = 0.0; + + out_volume_data = gbuffer_store_volume_data(g_volume_data); + out_transparency_data = gbuffer_store_transparency_data(g_transparency_data); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_volume_eval_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_volume_eval_lib.glsl new file mode 100644 index 00000000000..272b43047e9 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_volume_eval_lib.glsl @@ -0,0 +1,90 @@ + +#pragma BLENDER_REQUIRE(eevee_nodetree_eval_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) + +struct Ray { + vec3 origin; + vec3 direction; + float max_time; +}; + +vec3 volume_eval_light(vec3 P, float anisotropy) +{ + vec3 light_out = vec3(0.0); + // LIGHT_ITER(g_volume) + // { + // LightData ld = lights_data[i]; + + // if (ld.l_volume == 0.0) { + // continue; + // } + + // vec3 L; + // float dist; + // light_vector_get(light, P, L, dist); + + // float visibility = light_attenuation(light, L, dist); + + // if (light.shadow_id != LIGHT_NO_SHADOW && visibility > 0.0) { + // vec3 lL = light_world_to_local(light, -L) * dist; + // vec3 shadow_co = shadow_punctual_coordinates_get(shadows[l_idx], lL); + // visibility *= texture(shadow_atlas_tx, shadow_co); + // } + + // if (visibility > 1e-4) { + // float intensity = light_volume(utility_tx, light, V, L, dist, anisotropy); + // light_out += light.color * (intensity * visibility); + // } + // } + return light_out; +} + +void volume_eval(vec3 P, vec3 V, float max_time, out vec3 out_radiance, out vec3 out_transmittance) +{ + // out_radiance = vec3(0); + // out_transmittance = vec3(1); + + // VOLUME_ITER(g_volume) + // { + // nodetree_eval(); + + // vec3 scattered_light = g_volume_data.emission + + // g_volume_data.scattering * volume_eval_light(P, + // g_volume_data.anisotropy); + + // /* Emission does not work if there is no extinction because + // * Tr evaluates to 1.0 leading to Lscat = 0.0. (See T65771) */ + // /* s_extinction. */ + // g_volume_data.transmittance = max(vec3(1e-7) * step(1e-5, Lscat), + // g_volume_data.transmittance); + // /* Evaluate Scattering. */ + // float s_len = abs(ray_len - prev_ray_len); + // prev_ray_len = ray_len; + // vec3 Tr = exp(-g_volume_data.transmittance * s_len); + // /* Integrate along the current step segment. */ + // scattered_light = (scattered_light - scattered_light * Tr) * + // safe_rcp(g_volume_data.transmittance); + // /* Accumulate and also take into account the transmittance from previous steps. */ + // out_radiance += out_transmittance * scattered_light; + // out_transmittance *= Tr; + + // if (all(lessThan(out_transmittance, vec3(1e-4)))) { + // break; + // } + // } +} + +/* Simple version that compute transmittance only. */ +void volume_eval_homogenous(Ray ray, inout vec3 out_transmittance, out vec3 out_depth_time) +{ + // nodetree_eval(); + + float step_len = length(ray.direction); + + out_transmittance *= exp(-g_volume_data.transmittance * step_len * ray.max_time); + + float threshold = interlieved_gradient_noise(gl_FragCoord.xy, 0, 0.0); + /* Find depth at which we have threshold opacity. */ + out_depth_time = log(max(threshold, 1e-6)) * safe_rcp(-g_volume_data.transmittance * step_len); + out_depth_time = min(out_depth_time, vec3(ray.max_time)); +} diff --git a/source/blender/draw/engines/eevee/shaders/eevee_volume_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_volume_lib.glsl new file mode 100644 index 00000000000..da39c842579 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_volume_lib.glsl @@ -0,0 +1,16 @@ + +IN_OUT VolumeDataInterface +{ + vec3 P_start; + vec3 P_end; +} +interp; + +#ifdef GPU_FRAGMENT_SHADER +GlobalData init_globals(void) +{ + GlobalData volume; + volume.P = interp.P_start; + return volume; +} +#endif diff --git a/source/blender/draw/engines/eevee/shaders/eevee_volume_vert.glsl b/source/blender/draw/engines/eevee/shaders/eevee_volume_vert.glsl new file mode 100644 index 00000000000..c679870bc27 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_volume_vert.glsl @@ -0,0 +1,34 @@ + +/** + * Renders volume objects with no surfaces. + * + * The vertex shader outputs geometry at nearest depth. + **/ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_volume_lib.glsl) + +in vec3 pos; + +void main(void) +{ + /* TODO(fclem) Make the quad cover only the bounding box. */ + + // int v = gl_VertexID % 4; + // float x = -1.0 + float((v & 1) << 2); + // float y = -1.0 + float((v & 2) << 1); + // gl_Position = vec4(x, y, 1.0, 1.0); + + // vec3 aabb_min,; + + // uvcoordsvar = vec4((gl_Position.xy + 1.0) * 0.5, 0.0, 0.0); + + // interp.P_start = point_ndc_to_world(pos); + // interp.P_end = point_ndc_to_world(pos); + + interp.P_start = point_object_to_world(pos); + interp.P_end = interp.P_start; + + gl_Position = point_world_to_ndc(interp.P_start); +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/effect_bloom_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_bloom_frag.glsl deleted file mode 100644 index c6f61d1d443..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_bloom_frag.glsl +++ /dev/null @@ -1,218 +0,0 @@ -/* Original implementation by Keijiro Takahashi - * Blender integration by Clément Foucault - * - * Original License : - * - * Kino/Bloom v2 - Bloom filter for Unity - * - * Copyright (C) 2015, 2016 Keijiro Takahashi - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#pragma BLENDER_REQUIRE(common_math_lib.glsl) - -uniform sampler2D sourceBuffer; /* Buffer to filter */ -uniform vec2 sourceBufferTexelSize; - -/* Step Blit */ -uniform vec4 curveThreshold; -uniform float clampIntensity; - -/* Step Upsample */ -uniform sampler2D baseBuffer; /* Previous accumulation buffer */ -uniform vec2 baseBufferTexelSize; -uniform float sampleScale; - -/* Step Resolve */ -uniform vec3 bloomColor; -uniform bool bloomAddBase; - -in vec4 uvcoordsvar; - -out vec4 FragColor; - -/* -------------- Utils ------------- */ - -vec3 safe_color(vec3 c) -{ - /* Clamp to avoid black square artifacts if a pixel goes NaN. */ - return clamp(c, vec3(0.0), vec3(1e20)); /* 1e20 arbitrary. */ -} - -/* 3-tap median filter */ -vec3 median(vec3 a, vec3 b, vec3 c) -{ - return a + b + c - min(min(a, b), c) - max(max(a, b), c); -} - -/* ------------- Filters ------------ */ - -vec3 downsample_filter_high(sampler2D tex, vec2 uv, vec2 texelSize) -{ - /* Downsample with a 4x4 box filter + anti-flicker filter */ - vec4 d = texelSize.xyxy * vec4(-1, -1, +1, +1); - - vec3 s1 = textureLod(tex, uv + d.xy, 0.0).rgb; - vec3 s2 = textureLod(tex, uv + d.zy, 0.0).rgb; - vec3 s3 = textureLod(tex, uv + d.xw, 0.0).rgb; - vec3 s4 = textureLod(tex, uv + d.zw, 0.0).rgb; - - /* Karis's luma weighted average (using brightness instead of luma) */ - float s1w = 1.0 / (max_v3(s1) + 1.0); - float s2w = 1.0 / (max_v3(s2) + 1.0); - float s3w = 1.0 / (max_v3(s3) + 1.0); - float s4w = 1.0 / (max_v3(s4) + 1.0); - float one_div_wsum = 1.0 / (s1w + s2w + s3w + s4w); - - return (s1 * s1w + s2 * s2w + s3 * s3w + s4 * s4w) * one_div_wsum; -} - -vec3 downsample_filter(sampler2D tex, vec2 uv, vec2 texelSize) -{ - /* Downsample with a 4x4 box filter */ - vec4 d = texelSize.xyxy * vec4(-1, -1, +1, +1); - - vec3 s; - s = textureLod(tex, uv + d.xy, 0.0).rgb; - s += textureLod(tex, uv + d.zy, 0.0).rgb; - s += textureLod(tex, uv + d.xw, 0.0).rgb; - s += textureLod(tex, uv + d.zw, 0.0).rgb; - - return s * (1.0 / 4); -} - -vec3 upsample_filter_high(sampler2D tex, vec2 uv, vec2 texelSize) -{ - /* 9-tap bilinear upsampler (tent filter) */ - vec4 d = texelSize.xyxy * vec4(1, 1, -1, 0) * sampleScale; - - vec3 s; - s = textureLod(tex, uv - d.xy, 0.0).rgb; - s += textureLod(tex, uv - d.wy, 0.0).rgb * 2; - s += textureLod(tex, uv - d.zy, 0.0).rgb; - - s += textureLod(tex, uv + d.zw, 0.0).rgb * 2; - s += textureLod(tex, uv, 0.0).rgb * 4; - s += textureLod(tex, uv + d.xw, 0.0).rgb * 2; - - s += textureLod(tex, uv + d.zy, 0.0).rgb; - s += textureLod(tex, uv + d.wy, 0.0).rgb * 2; - s += textureLod(tex, uv + d.xy, 0.0).rgb; - - return s * (1.0 / 16.0); -} - -vec3 upsample_filter(sampler2D tex, vec2 uv, vec2 texelSize) -{ - /* 4-tap bilinear upsampler */ - vec4 d = texelSize.xyxy * vec4(-1, -1, +1, +1) * (sampleScale * 0.5); - - vec3 s; - s = textureLod(tex, uv + d.xy, 0.0).rgb; - s += textureLod(tex, uv + d.zy, 0.0).rgb; - s += textureLod(tex, uv + d.xw, 0.0).rgb; - s += textureLod(tex, uv + d.zw, 0.0).rgb; - - return s * (1.0 / 4.0); -} - -/* ----------- Steps ----------- */ - -vec4 step_blit(void) -{ - vec2 uv = uvcoordsvar.xy + sourceBufferTexelSize.xy * 0.5; - -#ifdef HIGH_QUALITY /* Anti flicker */ - vec3 d = sourceBufferTexelSize.xyx * vec3(1, 1, 0); - vec3 s0 = safe_color(textureLod(sourceBuffer, uvcoordsvar.xy, 0.0).rgb); - vec3 s1 = safe_color(textureLod(sourceBuffer, uvcoordsvar.xy - d.xz, 0.0).rgb); - vec3 s2 = safe_color(textureLod(sourceBuffer, uvcoordsvar.xy + d.xz, 0.0).rgb); - vec3 s3 = safe_color(textureLod(sourceBuffer, uvcoordsvar.xy - d.zy, 0.0).rgb); - vec3 s4 = safe_color(textureLod(sourceBuffer, uvcoordsvar.xy + d.zy, 0.0).rgb); - vec3 m = median(median(s0.rgb, s1, s2), s3, s4); -#else - vec3 s0 = safe_color(textureLod(sourceBuffer, uvcoordsvar.xy, 0.0).rgb); - vec3 m = s0.rgb; -#endif - - /* Pixel brightness */ - float br = max_v3(m); - - /* Under-threshold part: quadratic curve */ - float rq = clamp(br - curveThreshold.x, 0, curveThreshold.y); - rq = curveThreshold.z * rq * rq; - - /* Combine and apply the brightness response curve. */ - m *= max(rq, br - curveThreshold.w) / max(1e-5, br); - - /* Clamp pixel intensity if clamping enabled */ - if (clampIntensity > 0.0) { - br = max(1e-5, max_v3(m)); - m *= 1.0 - max(0.0, br - clampIntensity) / br; - } - - return vec4(m, 1.0); -} - -vec4 step_downsample(void) -{ -#ifdef HIGH_QUALITY /* Anti flicker */ - vec3 samp = downsample_filter_high(sourceBuffer, uvcoordsvar.xy, sourceBufferTexelSize); -#else - vec3 samp = downsample_filter(sourceBuffer, uvcoordsvar.xy, sourceBufferTexelSize); -#endif - return vec4(samp, 1.0); -} - -vec4 step_upsample(void) -{ -#ifdef HIGH_QUALITY - vec3 blur = upsample_filter_high(sourceBuffer, uvcoordsvar.xy, sourceBufferTexelSize); -#else - vec3 blur = upsample_filter(sourceBuffer, uvcoordsvar.xy, sourceBufferTexelSize); -#endif - vec3 base = textureLod(baseBuffer, uvcoordsvar.xy, 0.0).rgb; - return vec4(base + blur, 1.0); -} - -vec4 step_resolve(void) -{ -#ifdef HIGH_QUALITY - vec3 blur = upsample_filter_high(sourceBuffer, uvcoordsvar.xy, sourceBufferTexelSize); -#else - vec3 blur = upsample_filter(sourceBuffer, uvcoordsvar.xy, sourceBufferTexelSize); -#endif - vec4 base = bloomAddBase ? textureLod(baseBuffer, uvcoordsvar.xy, 0.0) : vec4(0.0); - vec3 cout = base.rgb + blur * bloomColor; - return vec4(cout, base.a); -} - -void main(void) -{ -#if defined(STEP_BLIT) - FragColor = step_blit(); -#elif defined(STEP_DOWNSAMPLE) - FragColor = step_downsample(); -#elif defined(STEP_UPSAMPLE) - FragColor = step_upsample(); -#elif defined(STEP_RESOLVE) - FragColor = step_resolve(); -#endif -} diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_bokeh_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_bokeh_frag.glsl deleted file mode 100644 index 051a08d25e6..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_bokeh_frag.glsl +++ /dev/null @@ -1,101 +0,0 @@ - -/** - * Bokeh Look Up Table: This outputs a radius multiplier to shape the sampling in gather pass or - * the scatter sprite appearance. This is only used if bokeh shape is either anamorphic or is not - * a perfect circle. - * We correct samples spacing for polygonal bokeh shapes. However, we do not for anamorphic bokeh - * as it is way more complex and expensive to do. - */ - -#pragma BLENDER_REQUIRE(effect_dof_lib.glsl) - -uniform float bokehSides; -uniform float bokehRotation; -uniform vec2 bokehAnisotropyInv; - -in vec4 uvcoordsvar; - -layout(location = 0) out vec2 outGatherLut; -layout(location = 1) out float outScatterLut; -layout(location = 2) out float outResolveLut; - -float polygon_sides_length(float sides_count) -{ - return 2.0 * sin(M_PI / sides_count); -} - -/* Returns intersection ratio between the radius edge at theta and the polygon edge. - * Start first corners at theta == 0. */ -float circle_to_polygon_radius(float sides_count, float theta) -{ - /* From Graphics Gems from CryENGINE 3 (Siggraph 2013) by Tiago Sousa (slide 36). */ - float side_angle = M_2PI / sides_count; - float halfside_angle = side_angle * 0.5; - return cos(side_angle * 0.5) / - cos(theta - side_angle * floor((sides_count * theta + M_PI) / M_2PI)); -} - -/* Remap input angle to have homogenous spacing of points along a polygon edge. - * Expect theta to be in [0..2pi] range. */ -float circle_to_polygon_angle(float sides_count, float theta) -{ - float side_angle = M_2PI / sides_count; - float halfside_angle = side_angle * 0.5; - float side = floor(theta / side_angle); - /* Length of segment from center to the middle of polygon side. */ - float adjacent = circle_to_polygon_radius(sides_count, 0.0); - - /* This is the relative position of the sample on the polygon half side. */ - float local_theta = theta - side * side_angle; - float ratio = (local_theta - halfside_angle) / halfside_angle; - - float halfside_len = polygon_sides_length(sides_count) * 0.5; - float opposite = ratio * halfside_len; - - /* NOTE: atan(y_over_x) has output range [-M_PI_2..M_PI_2]. */ - float final_local_theta = atan(opposite / adjacent); - - return side * side_angle + final_local_theta; -} - -void main() -{ - /* Center uv in range [-1..1]. */ - vec2 uv = uvcoordsvar.xy * 2.0 - 1.0; - - float radius = length(uv); - - vec2 texel = floor(gl_FragCoord.xy) - float(DOF_MAX_SLIGHT_FOCUS_RADIUS); - - if (bokehSides > 0.0) { - /* NOTE: atan(y,x) has output range [-M_PI..M_PI], so add 2pi to avoid negative angles. */ - float theta = atan(uv.y, uv.x) + M_2PI; - float r = length(uv); - - radius /= circle_to_polygon_radius(bokehSides, theta - bokehRotation); - - float theta_new = circle_to_polygon_angle(bokehSides, theta); - float r_new = circle_to_polygon_radius(bokehSides, theta_new); - - theta_new -= bokehRotation; - - uv = r_new * vec2(-cos(theta_new), sin(theta_new)); - - { - /* Slight focus distance */ - texel *= bokehAnisotropyInv; - float theta = atan(texel.y, -texel.x) + M_2PI; - texel /= circle_to_polygon_radius(bokehSides, theta + bokehRotation); - } - } - else { - uv *= safe_rcp(length(uv)); - } - - /* For gather store the normalized UV. */ - outGatherLut = uv; - /* For scatter store distance. */ - outScatterLut = radius; - /* For slight focus gather store pixel perfect distance. */ - outResolveLut = length(texel); -} diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_dilate_tiles_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_dilate_tiles_frag.glsl deleted file mode 100644 index 0cbf92466aa..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_dilate_tiles_frag.glsl +++ /dev/null @@ -1,117 +0,0 @@ - -/** - * Tile dilate pass: Takes the 8x8 Tiles buffer and converts dilates the tiles with large CoC to - * their neighborhood. This pass is repeated multiple time until the maximum CoC can be covered. - */ - -#pragma BLENDER_REQUIRE(effect_dof_lib.glsl) - -/* 1/16th of fullres. */ -uniform sampler2D cocTilesFgBuffer; -uniform sampler2D cocTilesBgBuffer; - -uniform int ringCount; -uniform int ringWidthMultiplier; -uniform bool dilateSlightFocus; - -/* 1/16th of fullres. Same format as input. */ -layout(location = 0) out vec4 outFgCoc; -layout(location = 1) out vec3 outBgCoc; - -const float tile_to_fullres_factor = float(DOF_TILE_DIVISOR); - -/* Error introduced by the random offset of the gathering kernel's center. */ -const float bluring_radius_error = 1.0 + 1.0 / (gather_ring_count + 0.5); - -void main() -{ - ivec2 center_tile_pos = ivec2(gl_FragCoord.xy); - - CocTile ring_buckets[DOF_DILATE_RING_COUNT]; - - for (int ring = 0; ring < ringCount && ring < DOF_DILATE_RING_COUNT; ring++) { - ring_buckets[ring] = dof_coc_tile_init(); - - int ring_distance = ring + 1; - for (int sample_id = 0; sample_id < 4 * ring_distance; sample_id++) { - ivec2 offset = dof_square_ring_sample_offset(ring_distance, sample_id); - - offset *= ringWidthMultiplier; - - for (int i = 0; i < 2; i++) { - ivec2 adj_tile_pos = center_tile_pos + ((i == 0) ? offset : -offset); - - CocTile adj_tile = dof_coc_tile_load(cocTilesFgBuffer, cocTilesBgBuffer, adj_tile_pos); - -#ifdef DILATE_MODE_MIN_MAX - /* Actually gather the "absolute" biggest coc but keeping the sign. */ - ring_buckets[ring].fg_min_coc = min(ring_buckets[ring].fg_min_coc, adj_tile.fg_min_coc); - ring_buckets[ring].bg_max_coc = max(ring_buckets[ring].bg_max_coc, adj_tile.bg_max_coc); - - if (dilateSlightFocus) { - ring_buckets[ring].fg_slight_focus_max_coc = dof_coc_max_slight_focus( - ring_buckets[ring].fg_slight_focus_max_coc, adj_tile.fg_slight_focus_max_coc); - } - -#else /* DILATE_MODE_MIN_ABS */ - ring_buckets[ring].fg_max_coc = max(ring_buckets[ring].fg_max_coc, adj_tile.fg_max_coc); - ring_buckets[ring].bg_min_coc = min(ring_buckets[ring].bg_min_coc, adj_tile.bg_min_coc); - - /* Should be tight as possible to reduce gather overhead (see slide 61). */ - float closest_neighbor_distance = length(max(abs(vec2(offset)) - 1.0, 0.0)) * - tile_to_fullres_factor; - - ring_buckets[ring].fg_max_intersectable_coc = max( - ring_buckets[ring].fg_max_intersectable_coc, - adj_tile.fg_max_intersectable_coc + closest_neighbor_distance); - ring_buckets[ring].bg_min_intersectable_coc = min( - ring_buckets[ring].bg_min_intersectable_coc, - adj_tile.bg_min_intersectable_coc + closest_neighbor_distance); -#endif - } - } - } - - /* Load center tile. */ - CocTile out_tile = dof_coc_tile_load(cocTilesFgBuffer, cocTilesBgBuffer, center_tile_pos); - - /* Dilate once. */ - if (dilateSlightFocus) { - out_tile.fg_slight_focus_max_coc = dof_coc_max_slight_focus( - out_tile.fg_slight_focus_max_coc, ring_buckets[0].fg_slight_focus_max_coc); - } - - for (int ring = 0; ring < ringCount && ring < DOF_DILATE_RING_COUNT; ring++) { - float ring_distance = float(ring + 1); - - ring_distance = (ring_distance * ringWidthMultiplier - 1) * tile_to_fullres_factor; - - /* NOTE(fclem): Unsure if both sides of the inequalities have the same unit. */ -#ifdef DILATE_MODE_MIN_MAX - if (-ring_buckets[ring].fg_min_coc * bluring_radius_error > ring_distance) { - out_tile.fg_min_coc = min(out_tile.fg_min_coc, ring_buckets[ring].fg_min_coc); - } - - if (ring_buckets[ring].bg_max_coc * bluring_radius_error > ring_distance) { - out_tile.bg_max_coc = max(out_tile.bg_max_coc, ring_buckets[ring].bg_max_coc); - } - -#else /* DILATE_MODE_MIN_ABS */ - /* Find minimum absolute CoC radii that will be intersected for the previously - * computed maximum CoC values. */ - if (-out_tile.fg_min_coc * bluring_radius_error > ring_distance) { - out_tile.fg_max_coc = max(out_tile.fg_max_coc, ring_buckets[ring].fg_max_coc); - out_tile.fg_max_intersectable_coc = max(out_tile.fg_max_intersectable_coc, - ring_buckets[ring].fg_max_intersectable_coc); - } - - if (out_tile.bg_max_coc * bluring_radius_error > ring_distance) { - out_tile.bg_min_coc = min(out_tile.bg_min_coc, ring_buckets[ring].bg_min_coc); - out_tile.bg_min_intersectable_coc = min(out_tile.bg_min_intersectable_coc, - ring_buckets[ring].bg_min_intersectable_coc); - } -#endif - } - - dof_coc_tile_store(out_tile, outFgCoc, outBgCoc); -} diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl deleted file mode 100644 index db9ae0f7034..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl +++ /dev/null @@ -1,293 +0,0 @@ - -/** - * Gather pass: Convolve foreground and background parts in separate passes. - * - * Using the min&max CoC tile buffer, we select the best appropriate method to blur the scene - * color. A fast gather path is taken if there is not many CoC variation inside the tile. - * - * We sample using an octaweb sampling pattern. We randomize the kernel center and each ring - * rotation to ensure maximum coverage. - */ - -#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) -#pragma BLENDER_REQUIRE(effect_dof_lib.glsl) - -/* Mipmapped input buffers, halfres but with padding to ensure mipmap alignement. */ -uniform sampler2D colorBuffer; -uniform sampler2D cocBuffer; - -/* Same input buffer but with a bilinear sampler object. */ -uniform sampler2D colorBufferBilinear; - -/* CoC Min&Max tile buffer at 1/16th of fullres. */ -uniform sampler2D cocTilesFgBuffer; -uniform sampler2D cocTilesBgBuffer; - -uniform sampler2D bokehLut; - -/* Used to correct the padding in the color and CoC buffers. */ -uniform vec2 gatherInputUvCorrection; - -uniform vec2 gatherOutputTexelSize; - -uniform vec2 bokehAnisotropy; - -layout(location = 0) out vec4 outColor; -layout(location = 1) out float outWeight; -#ifndef DOF_HOLEFILL_PASS -layout(location = 2) out vec2 outOcclusion; -#else - -/* Dirty global variable that isn't used. So it should get optimized out. */ -vec2 outOcclusion; -#endif - -#ifdef DOF_FOREGROUND_PASS -const bool is_foreground = true; -#else /* DOF_BACKGROUND_PASS */ -const bool is_foreground = false; -#endif - -const float unit_ring_radius = 1.0 / float(gather_ring_count); -const float unit_sample_radius = 1.0 / float(gather_ring_count + 0.5); -const float large_kernel_radius = 0.5 + float(gather_ring_count); -const float smaller_kernel_radius = 0.5 + float(gather_ring_count - gather_density_change_ring); -/* NOTE(fclem) the bias is reducing issues with density change visible transition. */ -const float radius_downscale_factor = smaller_kernel_radius / large_kernel_radius; -const int change_density_at_ring = (gather_ring_count - gather_density_change_ring + 1); -const float coc_radius_error = 2.0; - -/* Radii needs to be halfres CoC sizes. */ -bool dof_do_density_change(float base_radius, float min_intersectable_radius) -{ - /* Reduce artifact for very large blur. */ - min_intersectable_radius *= 0.1; - - bool need_new_density = (base_radius * unit_ring_radius > min_intersectable_radius); - bool larger_than_min_density = (base_radius * radius_downscale_factor > - float(gather_ring_count)); - - return need_new_density && larger_than_min_density; -} - -void dof_gather_init(float base_radius, - vec4 noise, - out vec2 center_co, - out float lod, - out float intersection_multiplier) -{ - /* Jitter center half a ring to reduce undersampling. */ - vec2 jitter_ofs = 0.499 * noise.zw * sqrt(noise.x); -#ifdef DOF_BOKEH_TEXTURE - jitter_ofs *= bokehAnisotropy; -#endif - center_co = gl_FragCoord.xy + jitter_ofs * base_radius * unit_sample_radius; - - /* TODO(fclem) Seems like the default lod selection is too big. Bias to avoid blocky moving - * out of focus shapes. */ - const float lod_bias = -2.0; - lod = max(floor(log2(base_radius * unit_sample_radius) + 0.5) + lod_bias, 0.0); - - if (no_gather_mipmaps) { - lod = 0.0; - } - /* (Slide 64). */ - intersection_multiplier = pow(0.5, lod); -} - -void dof_gather_accumulator(float base_radius, - float min_intersectable_radius, - const bool do_fast_gather, - const bool do_density_change) -{ - vec4 noise = no_gather_random ? vec4(0.0, 0.0, 0.0, 1.0) : texelfetch_noise_tex(gl_FragCoord.xy); - - if (!do_fast_gather) { - /* Jitter the radius to reduce noticeable density changes. */ - base_radius += noise.x * unit_ring_radius * base_radius; - } - else { - /* Jittering the radius more than we need means we are going to feather the bokeh shape half - * a ring. So we need to compensate for fast gather that does not check CoC intersection. */ - base_radius += (0.5 - noise.x) * 1.5 * unit_ring_radius * base_radius; - } - /* TODO(fclem) another seed? For now Cranly-Partterson rotation with golden ratio. */ - noise.x = fract(noise.x + 0.61803398875); - - float lod, isect_mul; - vec2 center_co; - dof_gather_init(base_radius, noise, center_co, lod, isect_mul); - - bool first_ring = true; - - DofGatherData accum_data = GATHER_DATA_INIT; - - int density_change = 0; - for (int ring = gather_ring_count; ring > 0; ring--) { - int sample_pair_count = gather_ring_density * ring; - - float step_rot = M_PI / float(sample_pair_count); - mat2 step_rot_mat = rot2_from_angle(step_rot); - - float angle_offset = noise.y * step_rot; - vec2 offset = vec2(cos(angle_offset), sin(angle_offset)); - - float ring_radius = float(ring) * unit_sample_radius * base_radius; - - /* Slide 38. */ - float bordering_radius = ring_radius + - (0.5 + coc_radius_error) * base_radius * unit_sample_radius; - DofGatherData ring_data = GATHER_DATA_INIT; - for (int sample_pair = 0; sample_pair < sample_pair_count; sample_pair++) { - offset = step_rot_mat * offset; - - DofGatherData pair_data[2]; - for (int i = 0; i < 2; i++) { - vec2 offset_co = ((i == 0) ? offset : -offset); -#ifdef DOF_BOKEH_TEXTURE - /* Scaling to 0.25 for speed. Improves texture cache hit. */ - offset_co = texture(bokehLut, offset_co * 0.25 + 0.5).rg; - offset_co *= bokehAnisotropy; -#endif - vec2 sample_co = center_co + offset_co * ring_radius; - vec2 sample_uv = sample_co * gatherOutputTexelSize * gatherInputUvCorrection; - if (do_fast_gather) { - pair_data[i].color = dof_load_gather_color(colorBufferBilinear, sample_uv, lod); - } - else { - pair_data[i].color = dof_load_gather_color(colorBuffer, sample_uv, lod); - } - pair_data[i].coc = dof_load_gather_coc(cocBuffer, sample_uv, lod); - pair_data[i].dist = ring_radius; - } - - dof_gather_accumulate_sample_pair(pair_data, - bordering_radius, - isect_mul, - first_ring, - do_fast_gather, - is_foreground, - ring_data, - accum_data); - } - -#ifdef DOF_FOREGROUND_PASS /* Reduce issue with closer foreground over distant foreground. */ - /* TODO(fclem) this seems to not be completely correct as the issue remains. */ - float ring_area = (sqr(float(ring) + 0.5 + coc_radius_error) - - sqr(float(ring) - 0.5 + coc_radius_error)) * - sqr(base_radius * unit_sample_radius); - dof_gather_ammend_weight(ring_data, ring_area); -#endif - - dof_gather_accumulate_sample_ring( - ring_data, sample_pair_count * 2, first_ring, do_fast_gather, is_foreground, accum_data); - - first_ring = false; - - if (do_density_change && (ring == change_density_at_ring) && - (density_change < gather_max_density_change)) { - if (dof_do_density_change(base_radius, min_intersectable_radius)) { - base_radius *= radius_downscale_factor; - ring += gather_density_change_ring; - /* We need to account for the density change in the weights (slide 62). - * For that multiply old kernel data by its area divided by the new kernel area. */ - const float outer_rings_weight = 1.0 / (radius_downscale_factor * radius_downscale_factor); -#ifndef DOF_FOREGROUND_PASS /* Samples are already weighted per ring in foreground pass. */ - dof_gather_ammend_weight(accum_data, outer_rings_weight); -#endif - /* Re-init kernel position & sampling parameters. */ - dof_gather_init(base_radius, noise, center_co, lod, isect_mul); - density_change++; - } - } - } - - { - /* Center sample. */ - vec2 sample_uv = center_co * gatherOutputTexelSize * gatherInputUvCorrection; - DofGatherData center_data; - if (do_fast_gather) { - center_data.color = dof_load_gather_color(colorBufferBilinear, sample_uv, lod); - } - else { - center_data.color = dof_load_gather_color(colorBuffer, sample_uv, lod); - } - center_data.coc = dof_load_gather_coc(cocBuffer, sample_uv, lod); - center_data.dist = 0.0; - - /* Slide 38. */ - float bordering_radius = (0.5 + coc_radius_error) * base_radius * unit_sample_radius; - - dof_gather_accumulate_center_sample( - center_data, bordering_radius, do_fast_gather, is_foreground, accum_data); - } - - int total_sample_count = dof_gather_total_sample_count_with_density_change( - gather_ring_count, gather_ring_density, density_change); - dof_gather_accumulate_resolve(total_sample_count, accum_data, outColor, outWeight, outOcclusion); - -#if defined(DOF_DEBUG_GATHER_PERF) - if (density_change > 0) { - float fac = saturate(float(density_change) / float(10.0)); - outColor.rgb = avg(outColor.rgb) * neon_gradient(fac); - } - if (do_fast_gather) { - outColor.rgb = avg(outColor.rgb) * vec3(0.0, 1.0, 0.0); - } -#elif defined(DOF_DEBUG_SCATTER_PERF) - outColor.rgb = avg(outColor.rgb) * vec3(0.0, 1.0, 0.0); -#endif - - /* Output premultiplied color so we can use bilinear sampler in resolve pass. */ - outColor *= outWeight; -} - -void main() -{ - ivec2 tile_co = ivec2(gl_FragCoord.xy / float(DOF_TILE_DIVISOR / 2)); - CocTile coc_tile = dof_coc_tile_load(cocTilesFgBuffer, cocTilesBgBuffer, tile_co); - CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile); - -#if defined(DOF_FOREGROUND_PASS) - float base_radius = -coc_tile.fg_min_coc; - float min_radius = -coc_tile.fg_max_coc; - float min_intersectable_radius = -coc_tile.fg_max_intersectable_coc; - bool can_early_out = !prediction.do_foreground; - -#elif defined(DOF_HOLEFILL_PASS) - float base_radius = -coc_tile.fg_min_coc; - float min_radius = -coc_tile.fg_max_coc; - float min_intersectable_radius = DOF_TILE_LARGE_COC; - bool can_early_out = !prediction.do_holefill; - -#else /* DOF_BACKGROUND_PASS */ - float base_radius = coc_tile.bg_max_coc; - float min_radius = coc_tile.bg_min_coc; - float min_intersectable_radius = coc_tile.bg_min_intersectable_coc; - bool can_early_out = !prediction.do_background; -#endif - - bool do_fast_gather = dof_do_fast_gather(base_radius, min_radius, is_foreground); - - /* Gather at half resolution. Divide CoC by 2. */ - base_radius *= 0.5; - min_intersectable_radius *= 0.5; - - bool do_density_change = dof_do_density_change(base_radius, min_intersectable_radius); - - if (can_early_out) { - /* Early out. */ - outColor = vec4(0.0); - outWeight = 0.0; - outOcclusion = vec2(0.0, 0.0); - } - else if (do_fast_gather) { - dof_gather_accumulator(base_radius, min_intersectable_radius, true, false); - } - else if (do_density_change) { - dof_gather_accumulator(base_radius, min_intersectable_radius, false, true); - } - else { - dof_gather_accumulator(base_radius, min_intersectable_radius, false, false); - } -} diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl deleted file mode 100644 index e5b68637563..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl +++ /dev/null @@ -1,625 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(common_math_lib.glsl) - -uniform vec4 cocParams; - -#define cocMul cocParams[0] /* distance * aperturesize * invsensorsize */ -#define cocBias cocParams[1] /* aperturesize * invsensorsize */ -#define cocNear cocParams[2] /* Near view depths value. */ -#define cocFar cocParams[3] /* Far view depths value. */ - -/* -------------- Debug Defines ------------- */ - -// #define DOF_DEBUG_GATHER_PERF -// #define DOF_DEBUG_SCATTER_PERF - -const bool no_smooth_intersection = false; -const bool no_gather_occlusion = false; -const bool no_gather_mipmaps = false; -const bool no_gather_random = false; -const bool no_gather_filtering = false; -const bool no_scatter_occlusion = false; -const bool no_scatter_pass = false; -const bool no_foreground_pass = false; -const bool no_background_pass = false; -const bool no_slight_focus_pass = false; -const bool no_focus_pass = false; -const bool no_holefill_pass = false; - -/* -------------- Quality Defines ------------- */ - -#ifdef DOF_HOLEFILL_PASS -/* No need for very high density for holefill. */ -const int gather_ring_count = 3; -const int gather_ring_density = 3; -const int gather_max_density_change = 0; -const int gather_density_change_ring = 1; -#else -const int gather_ring_count = DOF_GATHER_RING_COUNT; -const int gather_ring_density = 3; -const int gather_max_density_change = 50; /* Dictates the maximum good quality blur. */ -const int gather_density_change_ring = 1; -#endif - -/* -------------- Utils ------------- */ - -const vec2 quad_offsets[4] = vec2[4]( - vec2(-0.5, 0.5), vec2(0.5, 0.5), vec2(0.5, -0.5), vec2(-0.5, -0.5)); - -/* Divide by sensor size to get the normalized size. */ -#define calculate_coc_persp(zdepth) (cocMul / zdepth - cocBias) -#define calculate_coc_ortho(zdepth) ((zdepth + cocMul / cocBias) * cocMul) -#define calculate_coc(z) \ - (ProjectionMatrix[3][3] == 0.0) ? calculate_coc_persp(z) : calculate_coc_ortho(z) - -/* Ortho conversion is only true for camera view! */ -#define linear_depth_persp(d) ((cocNear * cocFar) / (d * (cocNear - cocFar) + cocFar)) -#define linear_depth_ortho(d) (d * (cocNear - cocFar) + cocNear) - -#define linear_depth(d) \ - ((ProjectionMatrix[3][3] == 0.0) ? linear_depth_persp(d) : linear_depth_ortho(d)) - -#define dof_coc_from_zdepth(d) calculate_coc(linear_depth(d)) - -float dof_hdr_color_weight(vec4 color) -{ - /* From UE4. Very fast "luma" weighting. */ - float luma = (color.g * 2.0) + (color.r + color.b); - /* TODO(fclem) Pass correct exposure. */ - const float exposure = 1.0; - return 1.0 / (luma * exposure + 4.0); -} - -float dof_coc_select(vec4 cocs) -{ - /* Select biggest coc. */ - float selected_coc = cocs.x; - if (abs(cocs.y) > abs(selected_coc)) { - selected_coc = cocs.y; - } - if (abs(cocs.z) > abs(selected_coc)) { - selected_coc = cocs.z; - } - if (abs(cocs.w) > abs(selected_coc)) { - selected_coc = cocs.w; - } - return selected_coc; -} - -/* NOTE: Do not forget to normalize weights afterwards. */ -vec4 dof_downsample_bilateral_coc_weights(vec4 cocs) -{ - float chosen_coc = dof_coc_select(cocs); - - const float scale = 4.0; /* TODO(fclem) revisit. */ - /* NOTE: The difference between the cocs should be inside a abs() function, - * but we follow UE4 implementation to improve how dithered transparency looks (see slide 19). */ - return saturate(1.0 - (chosen_coc - cocs) * scale); -} - -/* NOTE: Do not forget to normalize weights afterwards. */ -vec4 dof_downsample_bilateral_color_weights(vec4 colors[4]) -{ - vec4 weights; - for (int i = 0; i < 4; i++) { - weights[i] = dof_hdr_color_weight(colors[i]); - } - return weights; -} - -/* Makes sure the load functions distribute the energy correctly - * to both scatter and gather passes. */ -vec4 dof_load_gather_color(sampler2D gather_input_color_buffer, vec2 uv, float lod) -{ - vec4 color = textureLod(gather_input_color_buffer, uv, lod); - return color; -} - -vec4 dof_load_scatter_color(sampler2D scatter_input_color_buffer, vec2 uv, float lod) -{ - vec4 color = textureLod(scatter_input_color_buffer, uv, lod); - return color; -} - -float dof_load_gather_coc(sampler2D gather_input_coc_buffer, vec2 uv, float lod) -{ - float coc = textureLod(gather_input_coc_buffer, uv, lod).r; - /* We gather at halfres. CoC must be divided by 2 to be compared against radii. */ - return coc * 0.5; -} - -/* Distribute weights between near/slightfocus/far fields (slide 117). */ -const float layer_threshold = 4.0; -/* Make sure it overlaps. */ -const float layer_offset_fg = 0.5 + 1.0; -/* Extra offset for convolution layers to avoid light leaking from background. */ -const float layer_offset = 0.5 + 0.5; - -#define DOF_MAX_SLIGHT_FOCUS_RADIUS 16 - -float dof_layer_weight(float coc, const bool is_foreground) -{ -/* NOTE: These are fullres pixel CoC value. */ -#ifdef DOF_RESOLVE_PASS - return saturate(-abs(coc) + layer_threshold + layer_offset) * - float(is_foreground ? (coc <= 0.5) : (coc > -0.5)); -#else - coc *= 2.0; /* Account for half pixel gather. */ - float threshold = layer_threshold - ((is_foreground) ? layer_offset_fg : layer_offset); - return saturate(((is_foreground) ? -coc : coc) - threshold); -#endif -} -vec4 dof_layer_weight(vec4 coc) -{ - /* NOTE: Used for scatter pass which already flipped the sign correctly. */ - coc *= 2.0; /* Account for half pixel gather. */ - return saturate(coc - layer_threshold + layer_offset); -} - -/* NOTE: This is halfres CoC radius. */ -float dof_sample_weight(float coc) -{ - /* Full intensity if CoC radius is below the pixel footprint. */ - const float min_coc = 1.0; - coc = max(min_coc, abs(coc)); - return (M_PI * min_coc * min_coc) / (M_PI * coc * coc); -} -vec4 dof_sample_weight(vec4 coc) -{ - /* Full intensity if CoC radius is below the pixel footprint. */ - const float min_coc = 1.0; - coc = max(vec4(min_coc), abs(coc)); - return (M_PI * min_coc * min_coc) / (M_PI * coc * coc); -} - -/* Intersection with the center of the kernel. */ -float dof_intersection_weight(float coc, float distance_from_center, float intersection_multiplier) -{ - if (no_smooth_intersection) { - return step(0.0, (abs(coc) - distance_from_center)); - } - else { - /* (Slide 64). */ - return saturate((abs(coc) - distance_from_center) * intersection_multiplier + 0.5); - } -} - -/* Returns weight of the sample for the outer bucket (containing previous rings). */ -float dof_gather_accum_weight(float coc, float bordering_radius, bool first_ring) -{ - /* First ring has nothing to be mixed against. */ - if (first_ring) { - return 0.0; - } - return saturate(coc - bordering_radius); -} - -bool dof_do_fast_gather(float max_absolute_coc, float min_absolute_coc, const bool is_foreground) -{ - float min_weight = dof_layer_weight((is_foreground) ? -min_absolute_coc : min_absolute_coc, - is_foreground); - if (min_weight < 1.0) { - return false; - } - /* FIXME(fclem): This is a workaround to fast gather triggering too early. - * Since we use custom opacity mask, the opacity is not given to be 100% even for - * after normal threshold. */ - if (is_foreground && min_absolute_coc < layer_threshold) { - return false; - } - return (max_absolute_coc - min_absolute_coc) < (DOF_FAST_GATHER_COC_ERROR * max_absolute_coc); -} - -/* ------------------- COC TILES UTILS ------------------- */ - -struct CocTile { - float fg_min_coc; - float fg_max_coc; - float fg_max_intersectable_coc; - float fg_slight_focus_max_coc; - float bg_min_coc; - float bg_max_coc; - float bg_min_intersectable_coc; -}; - -struct CocTilePrediction { - bool do_foreground; - bool do_slight_focus; - bool do_focus; - bool do_background; - bool do_holefill; -}; - -/* WATCH: Might have to change depending on the texture format. */ -#define DOF_TILE_DEFOCUS 0.25 -#define DOF_TILE_FOCUS 0.0 -#define DOF_TILE_MIXED 0.75 -#define DOF_TILE_LARGE_COC 1024.0 - -/* Init a CoC tile for reduction algorithms. */ -CocTile dof_coc_tile_init(void) -{ - CocTile tile; - tile.fg_min_coc = 0.0; - tile.fg_max_coc = -DOF_TILE_LARGE_COC; - tile.fg_max_intersectable_coc = DOF_TILE_LARGE_COC; - tile.fg_slight_focus_max_coc = -1.0; - tile.bg_min_coc = DOF_TILE_LARGE_COC; - tile.bg_max_coc = 0.0; - tile.bg_min_intersectable_coc = DOF_TILE_LARGE_COC; - return tile; -} - -CocTile dof_coc_tile_load(sampler2D fg_buffer, sampler2D bg_buffer, ivec2 tile_co) -{ - ivec2 tex_size = textureSize(fg_buffer, 0).xy; - tile_co = clamp(tile_co, ivec2(0), tex_size - 1); - - vec4 fg = texelFetch(fg_buffer, tile_co, 0); - vec3 bg = texelFetch(bg_buffer, tile_co, 0).xyz; - - CocTile tile; - tile.fg_min_coc = -fg.x; - tile.fg_max_coc = -fg.y; - tile.fg_max_intersectable_coc = -fg.z; - tile.fg_slight_focus_max_coc = fg.w; - tile.bg_min_coc = bg.x; - tile.bg_max_coc = bg.y; - tile.bg_min_intersectable_coc = bg.z; - return tile; -} - -void dof_coc_tile_store(CocTile tile, out vec4 out_fg, out vec3 out_bg) -{ - out_fg.x = -tile.fg_min_coc; - out_fg.y = -tile.fg_max_coc; - out_fg.z = -tile.fg_max_intersectable_coc; - out_fg.w = tile.fg_slight_focus_max_coc; - out_bg.x = tile.bg_min_coc; - out_bg.y = tile.bg_max_coc; - out_bg.z = tile.bg_min_intersectable_coc; -} - -CocTilePrediction dof_coc_tile_prediction_get(CocTile tile) -{ - /* Based on tile value, predict what pass we need to load. */ - CocTilePrediction predict; - - predict.do_foreground = (-tile.fg_min_coc > layer_threshold - layer_offset_fg); - bool fg_fully_opaque = predict.do_foreground && - dof_do_fast_gather(-tile.fg_min_coc, -tile.fg_max_coc, true); - - predict.do_slight_focus = !fg_fully_opaque && (tile.fg_slight_focus_max_coc >= 0.5); - predict.do_focus = !fg_fully_opaque && (tile.fg_slight_focus_max_coc == DOF_TILE_FOCUS); - - predict.do_background = !predict.do_focus && !fg_fully_opaque && - (tile.bg_max_coc > layer_threshold - layer_offset); - bool bg_fully_opaque = predict.do_background && - dof_do_fast_gather(-tile.bg_max_coc, tile.bg_min_coc, false); - predict.do_holefill = !predict.do_focus && !fg_fully_opaque && -tile.fg_max_coc > 0.0; - -#if 0 /* Debug */ - predict.do_foreground = predict.do_background = predict.do_holefill = true; -#endif - return predict; -} - -/* Special function to return the correct max value of 2 slight focus coc. */ -float dof_coc_max_slight_focus(float coc1, float coc2) -{ - /* Do not consider values below 0.5 for expansion as they are "encoded". - * See setup pass shader for more infos. */ - if ((coc1 == DOF_TILE_DEFOCUS && coc2 == DOF_TILE_FOCUS) || - (coc1 == DOF_TILE_FOCUS && coc2 == DOF_TILE_DEFOCUS)) { - /* Tile where completely out of focus and in focus are both present. - * Consider as very slightly out of focus. */ - return DOF_TILE_MIXED; - } - return max(coc1, coc2); -} - -/* ------------------- GATHER UTILS ------------------- */ - -struct DofGatherData { - vec4 color; - float weight; - float dist; /* TODO: remove. */ - /* For scatter occlusion. */ - float coc; - float coc_sqr; - /* For ring bucket merging. */ - float transparency; - - float layer_opacity; -}; - -#define GATHER_DATA_INIT DofGatherData(vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) - -void dof_gather_ammend_weight(inout DofGatherData sample_data, float weight) -{ - sample_data.color *= weight; - sample_data.coc *= weight; - sample_data.coc_sqr *= weight; - sample_data.weight *= weight; -} - -void dof_gather_accumulate_sample(DofGatherData sample_data, - float weight, - inout DofGatherData accum_data) -{ - accum_data.color += sample_data.color * weight; - accum_data.coc += sample_data.coc * weight; - accum_data.coc_sqr += sample_data.coc * (sample_data.coc * weight); - accum_data.weight += weight; -} - -void dof_gather_accumulate_sample_pair(DofGatherData pair_data[2], - float bordering_radius, - float intersection_multiplier, - bool first_ring, - const bool do_fast_gather, - const bool is_foreground, - inout DofGatherData ring_data, - inout DofGatherData accum_data) -{ - if (do_fast_gather) { - for (int i = 0; i < 2; i++) { - dof_gather_accumulate_sample(pair_data[i], 1.0, accum_data); - accum_data.layer_opacity += 1.0; - } - return; - } - -#if 0 - const float mirroring_threshold = -layer_threshold - layer_offset; - /* TODO(fclem) Promote to parameter? dither with Noise? */ - const float mirroring_min_distance = 15.0; - if (pair_data[0].coc < mirroring_threshold && - (pair_data[1].coc - mirroring_min_distance) > pair_data[0].coc) { - pair_data[1].coc = pair_data[0].coc; - } - else if (pair_data[1].coc < mirroring_threshold && - (pair_data[0].coc - mirroring_min_distance) > pair_data[1].coc) { - pair_data[0].coc = pair_data[1].coc; - } -#endif - - for (int i = 0; i < 2; i++) { - float sample_weight = dof_sample_weight(pair_data[i].coc); - float layer_weight = dof_layer_weight(pair_data[i].coc, is_foreground); - float inter_weight = dof_intersection_weight( - pair_data[i].coc, pair_data[i].dist, intersection_multiplier); - float weight = inter_weight * layer_weight * sample_weight; - - /** - * If a CoC is larger than bordering radius we accumulate it to the general accumulator. - * If not, we accumulate to the ring bucket. This is to have more consistent sample occlusion. - */ - float accum_weight = dof_gather_accum_weight(pair_data[i].coc, bordering_radius, first_ring); - dof_gather_accumulate_sample(pair_data[i], weight * accum_weight, accum_data); - dof_gather_accumulate_sample(pair_data[i], weight * (1.0 - accum_weight), ring_data); - - accum_data.layer_opacity += layer_weight; - - if (is_foreground) { - ring_data.transparency += 1.0 - inter_weight * layer_weight; - } - else { - float coc = is_foreground ? -pair_data[i].coc : pair_data[i].coc; - ring_data.transparency += saturate(coc - bordering_radius); - } - } -} - -void dof_gather_accumulate_sample_ring(DofGatherData ring_data, - int sample_count, - bool first_ring, - const bool do_fast_gather, - /* accum_data occludes the ring_data if true. */ - const bool reversed_occlusion, - inout DofGatherData accum_data) -{ - if (do_fast_gather) { - /* Do nothing as ring_data contains nothing. All samples are already in accum_data. */ - return; - } - - if (first_ring) { - /* Layer opacity is directly accumulated into accum_data data. */ - accum_data.color = ring_data.color; - accum_data.coc = ring_data.coc; - accum_data.coc_sqr = ring_data.coc_sqr; - accum_data.weight = ring_data.weight; - - accum_data.transparency = ring_data.transparency / float(sample_count); - return; - } - - if (ring_data.weight == 0.0) { - return; - } - - float ring_avg_coc = ring_data.coc / ring_data.weight; - float accum_avg_coc = accum_data.coc / accum_data.weight; - - /* Smooth test to set opacity to see if the ring average coc occludes the accumulation. - * Test is reversed to be multiplied against opacity. */ - float ring_occlu = saturate(accum_avg_coc - ring_avg_coc); - /* The bias here is arbitrary. Seems to avoid weird looking foreground in most cases. - * We might need to make it a parameter or find a relative bias. */ - float accum_occlu = saturate((ring_avg_coc - accum_avg_coc) * 0.1 - 1.0); - -#ifdef DOF_RESOLVE_PASS - ring_occlu = accum_occlu = 0.0; -#endif - - if (no_gather_occlusion) { - ring_occlu = 0.0; - accum_occlu = 0.0; - } - - /* (Slide 40) */ - float ring_opacity = saturate(1.0 - ring_data.transparency / float(sample_count)); - float accum_opacity = 1.0 - accum_data.transparency; - - if (reversed_occlusion) { - /* Accum_data occludes the ring. */ - float alpha = (accum_data.weight == 0.0) ? 0.0 : accum_opacity * accum_occlu; - float one_minus_alpha = 1.0 - alpha; - - accum_data.color += ring_data.color * one_minus_alpha; - accum_data.coc += ring_data.coc * one_minus_alpha; - accum_data.coc_sqr += ring_data.coc_sqr * one_minus_alpha; - accum_data.weight += ring_data.weight * one_minus_alpha; - - accum_data.transparency *= 1.0 - ring_opacity; - } - else { - /* Ring occludes the accum_data (Same as reference). */ - float alpha = (accum_data.weight == 0.0) ? 1.0 : (ring_opacity * ring_occlu); - float one_minus_alpha = 1.0 - alpha; - - accum_data.color = accum_data.color * one_minus_alpha + ring_data.color; - accum_data.coc = accum_data.coc * one_minus_alpha + ring_data.coc; - accum_data.coc_sqr = accum_data.coc_sqr * one_minus_alpha + ring_data.coc_sqr; - accum_data.weight = accum_data.weight * one_minus_alpha + ring_data.weight; - } -} - -/* FIXME(fclem) Seems to be wrong since it needs ringcount+1 as input for slightfocus gather. */ -int dof_gather_total_sample_count(const int ring_count, const int ring_density) -{ - return (ring_count * ring_count - ring_count) * ring_density + 1; -} - -void dof_gather_accumulate_center_sample(DofGatherData center_data, - float bordering_radius, -#ifdef DOF_RESOLVE_PASS - int i_radius, -#endif - const bool do_fast_gather, - const bool is_foreground, - inout DofGatherData accum_data) -{ - float layer_weight = dof_layer_weight(center_data.coc, is_foreground); - float sample_weight = dof_sample_weight(center_data.coc); - float weight = layer_weight * sample_weight; - float accum_weight = dof_gather_accum_weight(center_data.coc, bordering_radius, false); - - if (do_fast_gather) { - /* Hope for the compiler to optimize the above. */ - layer_weight = 1.0; - sample_weight = 1.0; - accum_weight = 1.0; - weight = 1.0; - } - - center_data.transparency = 1.0 - weight; - - dof_gather_accumulate_sample(center_data, weight * accum_weight, accum_data); - - if (!do_fast_gather) { -#ifdef DOF_RESOLVE_PASS - /* NOTE(fclem): Hack to smooth transition to full in-focus opacity. */ - int total_sample_count = dof_gather_total_sample_count(i_radius + 1, DOF_SLIGHT_FOCUS_DENSITY); - float fac = saturate(1.0 - abs(center_data.coc) / float(layer_threshold)); - accum_data.layer_opacity += float(total_sample_count) * fac * fac; -#endif - accum_data.layer_opacity += layer_weight; - - /* Logic of dof_gather_accumulate_sample(). */ - weight *= (1.0 - accum_weight); - center_data.coc_sqr = center_data.coc * (center_data.coc * weight); - center_data.color *= weight; - center_data.coc *= weight; - center_data.weight = weight; - -#ifdef DOF_FOREGROUND_PASS /* Reduce issue with closer foreground over distant foreground. */ - float ring_area = sqr(bordering_radius); - dof_gather_ammend_weight(center_data, ring_area); -#endif - - /* Accumulate center as its own ring. */ - dof_gather_accumulate_sample_ring( - center_data, 1, false, do_fast_gather, is_foreground, accum_data); - } -} - -int dof_gather_total_sample_count_with_density_change(const int ring_count, - const int ring_density, - int density_change) -{ - int sample_count_per_density_change = dof_gather_total_sample_count(ring_count, ring_density) - - dof_gather_total_sample_count( - ring_count - gather_density_change_ring, ring_density); - - return dof_gather_total_sample_count(ring_count, ring_density) + - sample_count_per_density_change * density_change; -} - -void dof_gather_accumulate_resolve(int total_sample_count, - DofGatherData accum_data, - out vec4 out_col, - out float out_weight, - out vec2 out_occlusion) -{ - float weight_inv = safe_rcp(accum_data.weight); - out_col = accum_data.color * weight_inv; - out_occlusion = vec2(abs(accum_data.coc), accum_data.coc_sqr) * weight_inv; - -#ifdef DOF_FOREGROUND_PASS - out_weight = 1.0 - accum_data.transparency; -#else - if (accum_data.weight > 0.0) { - out_weight = accum_data.layer_opacity / float(total_sample_count); - } - else { - out_weight = 0.0; - } -#endif - /* Gathering may not accumulate to 1.0 alpha because of float precision. */ - if (out_weight > 0.99) { - out_weight = 1.0; - } - else if (out_weight < 0.01) { - out_weight = 0.0; - } - /* Same thing for alpha channel. */ - if (out_col.a > 0.99) { - out_col.a = 1.0; - } - else if (out_col.a < 0.01) { - out_col.a = 0.0; - } -} - -ivec2 dof_square_ring_sample_offset(int ring_distance, int sample_id) -{ - /** - * Generate samples in a square pattern with the ring radius. X is the center tile. - * - * Dist1 Dist2 - * 6 5 4 3 2 - * 3 2 1 7 1 - * . X 0 . X 0 - * . . . . . - * . . . . . - * - * Samples are expected to be mirrored to complete the pattern. - */ - ivec2 offset; - if (sample_id < ring_distance) { - offset.x = ring_distance; - offset.y = sample_id; - } - else if (sample_id < ring_distance * 3) { - offset.x = ring_distance - sample_id + ring_distance; - offset.y = ring_distance; - } - else { - offset.x = -ring_distance; - offset.y = ring_distance - sample_id + 3 * ring_distance; - } - return offset; -} diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_reduce_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_reduce_frag.glsl deleted file mode 100644 index d21254003f9..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_reduce_frag.glsl +++ /dev/null @@ -1,179 +0,0 @@ - -/** - * Reduce pass: Downsample the color buffer to generate mipmaps. - * Also decide if a pixel is to be convolved by scattering or gathering during the first pass. - */ - -#pragma BLENDER_REQUIRE(effect_dof_lib.glsl) - -/** Inputs: - * COPY_PASS: Is output of setup pass (halfres) and downsample pass (quarter res). - * REDUCE_PASS: Is previous Gather input miplvl (halfres >> miplvl). - */ -uniform sampler2D colorBuffer; -uniform sampler2D cocBuffer; -uniform sampler2D downsampledBuffer; - -uniform vec2 bokehAnisotropy; -uniform float scatterColorThreshold; -uniform float scatterCocThreshold; -uniform float scatterColorNeighborMax; -uniform float colorNeighborClamping; - -/** Outputs: - * COPY_PASS: Gather input mip0. - * REDUCE_PASS: Is next Gather input miplvl (halfres >> miplvl). - */ -layout(location = 0) out vec4 outColor; -layout(location = 1) out float outCoc; - -#ifdef COPY_PASS - -layout(location = 2) out vec3 outScatterColor; - -/* NOTE: Do not compare alpha as it is not scattered by the scatter pass. */ -float dof_scatter_neighborhood_rejection(vec3 color) -{ - color = min(vec3(scatterColorNeighborMax), color); - - float validity = 0.0; - - /* Centered in the middle of 4 quarter res texel. */ - vec2 texel_size = 1.0 / vec2(textureSize(downsampledBuffer, 0).xy); - vec2 uv = (gl_FragCoord.xy * 0.5) * texel_size; - - vec3 max_diff = vec3(0.0); - for (int i = 0; i < 4; i++) { - vec2 sample_uv = uv + quad_offsets[i] * texel_size; - vec3 ref = textureLod(downsampledBuffer, sample_uv, 0.0).rgb; - - ref = min(vec3(scatterColorNeighborMax), ref); - float diff = max_v3(max(vec3(0.0), abs(ref - color))); - - const float rejection_threshold = 0.7; - diff = saturate(diff / rejection_threshold - 1.0); - validity = max(validity, diff); - } - - return validity; -} - -/* This avoids sprite popping in and out at the screen border and - * drawing sprites larger than the screen. */ -float dof_scatter_screen_border_rejection(float coc, vec2 uv, vec2 screen_size) -{ - vec2 screen_pos = uv * screen_size; - float min_screen_border_distance = min_v2(min(screen_pos, screen_size - screen_pos)); - /* Fullres to halfres CoC. */ - coc *= 0.5; - /* Allow 10px transition. */ - const float rejection_hardeness = 1.0 / 10.0; - return saturate((min_screen_border_distance - abs(coc)) * rejection_hardeness + 1.0); -} - -float dof_scatter_luminosity_rejection(vec3 color) -{ - const float rejection_hardness = 1.0; - return saturate(max_v3(color - scatterColorThreshold) * rejection_hardness); -} - -float dof_scatter_coc_radius_rejection(float coc) -{ - const float rejection_hardness = 0.3; - return saturate((abs(coc) - scatterCocThreshold) * rejection_hardness); -} - -float fast_luma(vec3 color) -{ - return (2.0 * color.g) + color.r + color.b; -} - -/* Lightweight version of neighborhood clamping found in TAA. */ -vec3 dof_neighborhood_clamping(vec3 color) -{ - vec2 texel_size = 1.0 / vec2(textureSize(colorBuffer, 0)); - vec2 uv = gl_FragCoord.xy * texel_size; - vec4 ofs = vec4(-1, 1, -1, 1) * texel_size.xxyy; - - /* Luma clamping. 3x3 square neighborhood. */ - float c00 = fast_luma(textureLod(colorBuffer, uv + ofs.xz, 0.0).rgb); - float c01 = fast_luma(textureLod(colorBuffer, uv + ofs.xz * vec2(1.0, 0.0), 0.0).rgb); - float c02 = fast_luma(textureLod(colorBuffer, uv + ofs.xw, 0.0).rgb); - - float c10 = fast_luma(textureLod(colorBuffer, uv + ofs.xz * vec2(0.0, 1.0), 0.0).rgb); - float c11 = fast_luma(color); - float c12 = fast_luma(textureLod(colorBuffer, uv + ofs.xw * vec2(0.0, 1.0), 0.0).rgb); - - float c20 = fast_luma(textureLod(colorBuffer, uv + ofs.yz, 0.0).rgb); - float c21 = fast_luma(textureLod(colorBuffer, uv + ofs.yz * vec2(1.0, 0.0), 0.0).rgb); - float c22 = fast_luma(textureLod(colorBuffer, uv + ofs.yw, 0.0).rgb); - - float avg_luma = avg8(c00, c01, c02, c10, c12, c20, c21, c22); - float max_luma = max8(c00, c01, c02, c10, c12, c20, c21, c22); - - float upper_bound = mix(max_luma, avg_luma, colorNeighborClamping); - upper_bound = mix(c11, upper_bound, colorNeighborClamping); - - float clamped_luma = min(upper_bound, c11); - - return color * clamped_luma * safe_rcp(c11); -} - -/* Simple copy pass where we select what pixels to scatter. Also the resolution might change. - * NOTE: The texture can end up being too big because of the mipmap padding. We correct for - * that during the convolution phase. */ -void main() -{ - vec2 halfres = vec2(textureSize(colorBuffer, 0).xy); - vec2 uv = gl_FragCoord.xy / halfres; - - outColor = textureLod(colorBuffer, uv, 0.0); - outCoc = textureLod(cocBuffer, uv, 0.0).r; - - outColor.rgb = dof_neighborhood_clamping(outColor.rgb); - - /* Only scatter if luminous enough. */ - float do_scatter = dof_scatter_luminosity_rejection(outColor.rgb); - /* Only scatter if CoC is big enough. */ - do_scatter *= dof_scatter_coc_radius_rejection(outCoc); - /* Only scatter if CoC is not too big to avoid performance issues. */ - do_scatter *= dof_scatter_screen_border_rejection(outCoc, uv, halfres); - /* Only scatter if neighborhood is different enough. */ - do_scatter *= dof_scatter_neighborhood_rejection(outColor.rgb); - /* For debugging. */ - do_scatter *= float(!no_scatter_pass); - - outScatterColor = mix(vec3(0.0), outColor.rgb, do_scatter); - outColor.rgb = mix(outColor.rgb, vec3(0.0), do_scatter); - - /* Apply energy conservation to anamorphic scattered bokeh. */ - outScatterColor /= min_v2(bokehAnisotropy); -} - -#else /* REDUCE_PASS */ - -/* Downsample pass done for each mip starting from mip1. */ -void main() -{ - vec2 input_texel_size = 1.0 / vec2(textureSize(colorBuffer, 0).xy); - /* Center uv around the 4 pixels of the previous mip. */ - vec2 quad_center = (floor(gl_FragCoord.xy) * 2.0 + 1.0) * input_texel_size; - - vec4 colors[4]; - vec4 cocs; - for (int i = 0; i < 4; i++) { - vec2 sample_uv = quad_center + quad_offsets[i] * input_texel_size; - colors[i] = dof_load_gather_color(colorBuffer, sample_uv, 0.0); - cocs[i] = textureLod(cocBuffer, sample_uv, 0.0).r; - } - - vec4 weights = dof_downsample_bilateral_coc_weights(cocs); - weights *= dof_downsample_bilateral_color_weights(colors); - /* Normalize so that the sum is 1. */ - weights *= safe_rcp(sum(weights)); - - outColor = weighted_sum_array(colors, weights); - outCoc = dot(cocs, weights); -} - -#endif diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl deleted file mode 100644 index 57027c71156..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl +++ /dev/null @@ -1,212 +0,0 @@ - -/** - * Recombine Pass: Load separate convolution layer and composite with self slight defocus - * convolution and in-focus fields. - * - * The halfres gather methods are fast but lack precision for small CoC areas. To fix this we - * do a bruteforce gather to have a smooth transition between in-focus and defocus regions. - */ - -#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) -#pragma BLENDER_REQUIRE(effect_dof_lib.glsl) - -uniform sampler2D fullResColorBuffer; -uniform sampler2D fullResDepthBuffer; - -uniform sampler2D bgColorBuffer; -uniform sampler2D bgWeightBuffer; -uniform sampler2D bgTileBuffer; - -uniform sampler2D fgColorBuffer; -uniform sampler2D fgWeightBuffer; -uniform sampler2D fgTileBuffer; - -uniform sampler2D holefillColorBuffer; -uniform sampler2D holefillWeightBuffer; - -uniform sampler2D bokehLut; - -uniform float bokehMaxSize; - -in vec4 uvcoordsvar; - -out vec4 fragColor; - -void dof_slight_focus_gather(float radius, out vec4 out_color, out float out_weight) -{ - /* offset coord to avoid correlation with sampling pattern. */ - vec4 noise = texelfetch_noise_tex(gl_FragCoord.xy + 7.0); - - DofGatherData fg_accum = GATHER_DATA_INIT; - DofGatherData bg_accum = GATHER_DATA_INIT; - - int i_radius = clamp(int(radius + 0.5), 0, int(layer_threshold)); - const int resolve_ring_density = DOF_SLIGHT_FOCUS_DENSITY; - ivec2 texel = ivec2(gl_FragCoord.xy); - - bool first_ring = true; - - for (int ring = i_radius; ring > 0; ring--) { - DofGatherData fg_ring = GATHER_DATA_INIT; - DofGatherData bg_ring = GATHER_DATA_INIT; - - int ring_distance = ring; - int ring_sample_count = resolve_ring_density * ring_distance; - for (int sample_id = 0; sample_id < ring_sample_count; sample_id++) { - int s = sample_id * (4 / resolve_ring_density) + - int(noise.y * float((4 - resolve_ring_density) * ring_distance)); - - ivec2 offset = dof_square_ring_sample_offset(ring_distance, s); - float ring_dist = length(vec2(offset)); - - DofGatherData pair_data[2]; - for (int i = 0; i < 2; i++) { - ivec2 sample_offset = ((i == 0) ? offset : -offset); - ivec2 sample_texel = texel + sample_offset; - /* OPTI: could precompute the factor. */ - vec2 sample_uv = (vec2(sample_texel) + 0.5) / vec2(textureSize(fullResDepthBuffer, 0)); - float depth = textureLod(fullResDepthBuffer, sample_uv, 0.0).r; - pair_data[i].color = safe_color(textureLod(fullResColorBuffer, sample_uv, 0.0)); - pair_data[i].coc = dof_coc_from_zdepth(depth); - pair_data[i].dist = ring_dist; -#ifdef DOF_BOKEH_TEXTURE - /* Contains subpixel distance to bokeh shape. */ - pair_data[i].dist = texelFetch(bokehLut, sample_offset + DOF_MAX_SLIGHT_FOCUS_RADIUS, 0).r; -#endif - pair_data[i].coc = clamp(pair_data[i].coc, -bokehMaxSize, bokehMaxSize); - } - - float bordering_radius = ring_dist + 0.5; - const float isect_mul = 1.0; - dof_gather_accumulate_sample_pair( - pair_data, bordering_radius, isect_mul, first_ring, false, false, bg_ring, bg_accum); - -#ifdef DOF_BOKEH_TEXTURE - /* Swap distances in order to flip bokeh shape for foreground. */ - float tmp = pair_data[0].dist; - pair_data[0].dist = pair_data[1].dist; - pair_data[1].dist = tmp; -#endif - dof_gather_accumulate_sample_pair( - pair_data, bordering_radius, isect_mul, first_ring, false, true, fg_ring, fg_accum); - } - - dof_gather_accumulate_sample_ring( - bg_ring, ring_sample_count * 2, first_ring, false, false, bg_accum); - dof_gather_accumulate_sample_ring( - fg_ring, ring_sample_count * 2, first_ring, false, true, fg_accum); - - first_ring = false; - } - - /* Center sample. */ - vec2 sample_uv = uvcoordsvar.xy; - float depth = textureLod(fullResDepthBuffer, sample_uv, 0.0).r; - DofGatherData center_data; - center_data.color = safe_color(textureLod(fullResColorBuffer, sample_uv, 0.0)); - center_data.coc = dof_coc_from_zdepth(depth); - center_data.coc = clamp(center_data.coc, -bokehMaxSize, bokehMaxSize); - center_data.dist = 0.0; - - /* Slide 38. */ - float bordering_radius = 0.5; - - dof_gather_accumulate_center_sample( - center_data, bordering_radius, i_radius, false, true, fg_accum); - dof_gather_accumulate_center_sample( - center_data, bordering_radius, i_radius, false, false, bg_accum); - - vec4 bg_col, fg_col; - float bg_weight, fg_weight; - vec2 unused_occlusion; - - int total_sample_count = dof_gather_total_sample_count(i_radius + 1, resolve_ring_density); - dof_gather_accumulate_resolve(total_sample_count, bg_accum, bg_col, bg_weight, unused_occlusion); - dof_gather_accumulate_resolve(total_sample_count, fg_accum, fg_col, fg_weight, unused_occlusion); - - /* Fix weighting issues on perfectly focus > slight focus transitionning areas. */ - if (abs(center_data.coc) < 0.5) { - bg_col = center_data.color; - bg_weight = 1.0; - } - - /* Alpha Over */ - float alpha = 1.0 - fg_weight; - out_weight = bg_weight * alpha + fg_weight; - out_color = bg_col * bg_weight * alpha + fg_col * fg_weight; -} - -void dof_resolve_load_layer(sampler2D color_tex, - sampler2D weight_tex, - out vec4 out_color, - out float out_weight) -{ - vec2 pixel_co = gl_FragCoord.xy / 2.0; - vec2 uv = pixel_co / textureSize(color_tex, 0).xy; - out_color = textureLod(color_tex, uv, 0.0); - out_weight = textureLod(weight_tex, uv, 0.0).r; -} - -void main(void) -{ - ivec2 tile_co = ivec2(gl_FragCoord.xy / float(DOF_TILE_DIVISOR)); - CocTile coc_tile = dof_coc_tile_load(fgTileBuffer, bgTileBuffer, tile_co); - CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile); - - fragColor = vec4(0.0); - float weight = 0.0; - - vec4 layer_color; - float layer_weight; - - if (!no_holefill_pass && prediction.do_holefill) { - dof_resolve_load_layer(holefillColorBuffer, holefillWeightBuffer, layer_color, layer_weight); - fragColor = layer_color * safe_rcp(layer_weight); - weight = float(layer_weight > 0.0); - } - - if (!no_background_pass && prediction.do_background) { - dof_resolve_load_layer(bgColorBuffer, bgWeightBuffer, layer_color, layer_weight); - /* Always prefer background to holefill pass. */ - layer_color *= safe_rcp(layer_weight); - layer_weight = float(layer_weight > 0.0); - /* Composite background. */ - fragColor = fragColor * (1.0 - layer_weight) + layer_color; - weight = weight * (1.0 - layer_weight) + layer_weight; - /* Fill holes with the composited background. */ - fragColor *= safe_rcp(weight); - weight = float(weight > 0.0); - } - - if (!no_slight_focus_pass && prediction.do_slight_focus) { - dof_slight_focus_gather(coc_tile.fg_slight_focus_max_coc, layer_color, layer_weight); - /* Composite slight defocus. */ - fragColor = fragColor * (1.0 - layer_weight) + layer_color; - weight = weight * (1.0 - layer_weight) + layer_weight; - } - - if (!no_focus_pass && prediction.do_focus) { - layer_color = safe_color(textureLod(fullResColorBuffer, uvcoordsvar.xy, 0.0)); - layer_weight = 1.0; - /* Composite in focus. */ - fragColor = fragColor * (1.0 - layer_weight) + layer_color; - weight = weight * (1.0 - layer_weight) + layer_weight; - } - - if (!no_foreground_pass && prediction.do_foreground) { - dof_resolve_load_layer(fgColorBuffer, fgWeightBuffer, layer_color, layer_weight); - /* Composite foreground. */ - fragColor = fragColor * (1.0 - layer_weight) + layer_color; - } - - /* Fix float precision issue in alpha compositing. */ - if (fragColor.a > 0.99) { - fragColor.a = 1.0; - } - -#if 0 /* Debug */ - if (coc_tile.fg_slight_focus_max_coc >= 0.5) { - fragColor.rgb *= vec3(1.0, 0.1, 0.1); - } -#endif -} diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_setup_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_setup_frag.glsl deleted file mode 100644 index 235145b221b..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_setup_frag.glsl +++ /dev/null @@ -1,65 +0,0 @@ - -/** - * Setup pass: CoC and luma aware downsample to half resolution of the input scene color buffer. - * - * An addition to the downsample CoC, we output the maximum slight out of focus CoC to be - * sure we don't miss a pixel. - */ - -#pragma BLENDER_REQUIRE(effect_dof_lib.glsl) - -/* Full resolution. */ -uniform sampler2D colorBuffer; -uniform sampler2D depthBuffer; - -uniform float bokehMaxSize; - -/* Half resolution. */ -layout(location = 0) out vec4 outColor; -layout(location = 1) out vec2 outCoc; /* x: Downsample CoC, y: Max slight focus abs CoC */ - -void main() -{ - vec2 fullres_texel_size = 1.0 / vec2(textureSize(colorBuffer, 0).xy); - /* Center uv around the 4 fullres pixels. */ - vec2 quad_center = (floor(gl_FragCoord.xy) * 2.0 + 1.0) * fullres_texel_size; - - vec4 colors[4]; - vec4 depths; - for (int i = 0; i < 4; i++) { - vec2 sample_uv = quad_center + quad_offsets[i] * fullres_texel_size; - colors[i] = safe_color(textureLod(colorBuffer, sample_uv, 0.0)); - depths[i] = textureLod(depthBuffer, sample_uv, 0.0).r; - } - - vec4 cocs = dof_coc_from_zdepth(depths); - - cocs = clamp(cocs, -bokehMaxSize, bokehMaxSize); - - vec4 weights = dof_downsample_bilateral_coc_weights(cocs); - weights *= dof_downsample_bilateral_color_weights(colors); - /* Normalize so that the sum is 1. */ - weights *= safe_rcp(sum(weights)); - - outColor = weighted_sum_array(colors, weights); - outCoc.x = dot(cocs, weights); - - /* Max slight focus abs CoC. */ - - /* Clamp to 0.5 if full in defocus to differentiate full focus tiles with coc == 0.0. - * This enables an optimization in the resolve pass. */ - const vec4 threshold = vec4(layer_threshold + layer_offset); - cocs = abs(cocs); - bvec4 defocus = greaterThan(cocs, threshold); - bvec4 focus = lessThanEqual(cocs, vec4(0.5)); - if (any(defocus) && any(focus)) { - /* For the same reason as in the flatten pass. This is a case we cannot optimize for. */ - cocs = mix(cocs, vec4(DOF_TILE_MIXED), focus); - cocs = mix(cocs, vec4(DOF_TILE_MIXED), defocus); - } - else { - cocs = mix(cocs, vec4(DOF_TILE_FOCUS), focus); - cocs = mix(cocs, vec4(DOF_TILE_DEFOCUS), defocus); - } - outCoc.y = max_v4(cocs); -} diff --git a/source/blender/draw/engines/eevee/shaders/effect_downsample_cube_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_downsample_cube_frag.glsl deleted file mode 100644 index 05f16b866e0..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_downsample_cube_frag.glsl +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Simple down-sample shader. Takes the average of the 4 texels of lower mip. - */ - -#pragma BLENDER_REQUIRE(common_math_lib.glsl) - -uniform samplerCube source; -uniform float texelSize; - -flat in int fFace; - -out vec4 FragColor; - -const vec3 maj_axes[6] = vec3[6](vec3(1.0, 0.0, 0.0), - vec3(-1.0, 0.0, 0.0), - vec3(0.0, 1.0, 0.0), - vec3(0.0, -1.0, 0.0), - vec3(0.0, 0.0, 1.0), - vec3(0.0, 0.0, -1.0)); -const vec3 x_axis[6] = vec3[6](vec3(0.0, 0.0, -1.0), - vec3(0.0, 0.0, 1.0), - vec3(1.0, 0.0, 0.0), - vec3(1.0, 0.0, 0.0), - vec3(1.0, 0.0, 0.0), - vec3(-1.0, 0.0, 0.0)); -const vec3 y_axis[6] = vec3[6](vec3(0.0, -1.0, 0.0), - vec3(0.0, -1.0, 0.0), - vec3(0.0, 0.0, 1.0), - vec3(0.0, 0.0, -1.0), - vec3(0.0, -1.0, 0.0), - vec3(0.0, -1.0, 0.0)); - -void main() -{ - vec2 uvs = gl_FragCoord.xy * texelSize; - - uvs = 2.0 * uvs - 1.0; - - vec3 cubevec = x_axis[fFace] * uvs.x + y_axis[fFace] * uvs.y + maj_axes[fFace]; - - FragColor = textureLod(source, cubevec, 0.0); -} diff --git a/source/blender/draw/engines/eevee/shaders/effect_downsample_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_downsample_frag.glsl deleted file mode 100644 index 9fc258da185..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_downsample_frag.glsl +++ /dev/null @@ -1,42 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_math_lib.glsl) - -/** - * Simple down-sample shader. - * Do a gaussian filter using 4 bilinear texture samples. - */ - -uniform sampler2D source; -uniform float fireflyFactor; - -#ifndef COPY_SRC -uniform vec2 texelSize; -#endif - -out vec4 FragColor; - -void main() -{ -#ifdef COPY_SRC - vec2 uvs = gl_FragCoord.xy / vec2(textureSize(source, 0)); - FragColor = textureLod(source, uvs, 0.0); - FragColor = safe_color(FragColor); - - /* Clamped brightness. */ - float luma = max(1e-8, max_v3(FragColor.rgb)); - FragColor *= 1.0 - max(0.0, luma - fireflyFactor) / luma; - -#else - /* NOTE(@fclem): textureSize() does not work the same on all implementations - * when changing the min and max texture levels. Use uniform instead (see T87801). */ - vec2 uvs = gl_FragCoord.xy * texelSize; - vec4 ofs = texelSize.xyxy * vec4(0.75, 0.75, -0.75, -0.75); - uvs *= 2.0; - - FragColor = textureLod(source, uvs + ofs.xy, 0.0); - FragColor += textureLod(source, uvs + ofs.xw, 0.0); - FragColor += textureLod(source, uvs + ofs.zy, 0.0); - FragColor += textureLod(source, uvs + ofs.zw, 0.0); - FragColor *= 0.25; -#endif -} diff --git a/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl deleted file mode 100644 index 70f1e9f1e66..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl +++ /dev/null @@ -1,115 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(common_math_lib.glsl) -#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) -#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) -#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) - -/** - * This shader only compute maximum horizon angles for each directions. - * The final integration is done at the resolve stage with the shading normal. - */ - -in vec4 uvcoordsvar; - -out vec4 FragColor; - -uniform sampler2D normalBuffer; - -/* Similar to https://atyuwen.github.io/posts/normal-reconstruction/. - * This samples the depth buffer 4 time for each direction to get the most correct - * implicit normal reconstruction out of the depth buffer. */ -vec3 view_position_derivative_from_depth(vec2 uvs, vec2 ofs, vec3 vP, float depth_center) -{ - vec2 uv1 = uvs - ofs * 2.0; - vec2 uv2 = uvs - ofs; - vec2 uv3 = uvs + ofs; - vec2 uv4 = uvs + ofs * 2.0; - vec4 H; - H.x = textureLod(maxzBuffer, uv1, 0.0).r; - H.y = textureLod(maxzBuffer, uv2, 0.0).r; - H.z = textureLod(maxzBuffer, uv3, 0.0).r; - H.w = textureLod(maxzBuffer, uv4, 0.0).r; - /* Fix issue with depth precision. Take even larger diff. */ - vec4 diff = abs(vec4(depth_center, H.yzw) - H.x); - if (max_v4(diff) < 2.4e-7 && all(lessThan(diff.xyz, diff.www))) { - return 0.25 * (get_view_space_from_depth(uv3, H.w) - get_view_space_from_depth(uv1, H.x)); - } - /* Simplified (H.xw + 2.0 * (H.yz - H.xw)) - depth_center */ - vec2 deltas = abs((2.0 * H.yz - H.xw) - depth_center); - if (deltas.x < deltas.y) { - return vP - get_view_space_from_depth(uv2, H.y); - } - else { - return get_view_space_from_depth(uv3, H.z) - vP; - } -} - -/* TODO(fclem) port to a common place for other effects to use. */ -bool reconstruct_view_position_and_normal_from_depth(vec2 uvs, out vec3 vP, out vec3 vNg) -{ - vec2 texel_size = vec2(abs(dFdx(uvs.x)), abs(dFdy(uvs.y))); - float depth_center = textureLod(maxzBuffer, uvs, 0.0).r; - - vP = get_view_space_from_depth(uvs, depth_center); - - vec3 dPdx = view_position_derivative_from_depth(uvs, texel_size * vec2(1, 0), vP, depth_center); - vec3 dPdy = view_position_derivative_from_depth(uvs, texel_size * vec2(0, 1), vP, depth_center); - - vNg = safe_normalize(cross(dPdx, dPdy)); - - /* Background case. */ - if (depth_center == 1.0) { - return false; - } - - return true; -} - -#ifdef DEBUG_AO - -void main() -{ - vec3 vP, vNg; - vec2 uvs = uvcoordsvar.xy; - - if (!reconstruct_view_position_and_normal_from_depth(uvs * hizUvScale.xy, vP, vNg)) { - /* Handle Background case. Prevent artifact due to uncleared Horizon Render Target. */ - FragColor = vec4(0.0); - } - else { - vec3 P = transform_point(ViewMatrixInverse, vP); - vec3 V = cameraVec(P); - vec3 vV = viewCameraVec(vP); - vec3 vN = normal_decode(texture(normalBuffer, uvs).rg, vV); - vec3 N = transform_direction(ViewMatrixInverse, vN); - vec3 Ng = transform_direction(ViewMatrixInverse, vNg); - - OcclusionData data = occlusion_load(vP, 1.0); - - if (min_v4(abs(data.horizons)) != M_PI) { - FragColor = vec4(diffuse_occlusion(data, V, N, Ng)); - } - else { - FragColor = vec4(1.0); - } - } -} - -#else - -void main() -{ - vec2 uvs = uvcoordsvar.xy; - float depth = textureLod(maxzBuffer, uvs * hizUvScale.xy, 0.0).r; - vec3 vP = get_view_space_from_depth(uvs, depth); - - OcclusionData data = NO_OCCLUSION_DATA; - /* Do not trace for background */ - if (depth != 1.0) { - data = occlusion_search(vP, maxzBuffer, aoDistance, 0.0, 8.0); - } - - FragColor = pack_occlusion_data(data); -} -#endif diff --git a/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl deleted file mode 100644 index 8ef39a55921..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Shader that down-sample depth buffer, - * saving min and max value of each texel in the above mipmaps. - * Adapted from http://rastergrid.com/blog/2010/10/hierarchical-z-map-based-occlusion-culling/ - * - * Major simplification has been made since we pad the buffer to always be bigger than input to - * avoid mipmapping misalignement. - */ - -#ifdef LAYERED -uniform sampler2DArray depthBuffer; -uniform int depthLayer; -#else -uniform sampler2D depthBuffer; -#endif - -#ifndef COPY_DEPTH -uniform vec2 texelSize; -#endif - -#ifdef LAYERED -# define sampleLowerMip(t) texture(depthBuffer, vec3(t, depthLayer)).r -# define gatherLowerMip(t) textureGather(depthBuffer, vec3(t, depthLayer)) -#else -# define sampleLowerMip(t) texture(depthBuffer, t).r -# define gatherLowerMip(t) textureGather(depthBuffer, t) -#endif - -#ifdef MIN_PASS -# define minmax2(a, b) min(a, b) -# define minmax3(a, b, c) min(min(a, b), c) -# define minmax4(a, b, c, d) min(min(min(a, b), c), d) -#else /* MAX_PASS */ -# define minmax2(a, b) max(a, b) -# define minmax3(a, b, c) max(max(a, b), c) -# define minmax4(a, b, c, d) max(max(max(a, b), c), d) -#endif - -/* On some AMD card / driver combination, it is needed otherwise, - * the shader does not write anything. */ -#if defined(GPU_INTEL) || defined(GPU_ATI) -out vec4 fragColor; -#endif - -void main() -{ - vec2 texel = gl_FragCoord.xy; - -#ifdef COPY_DEPTH - vec2 uv = texel / vec2(textureSize(depthBuffer, 0).xy); - - float val = sampleLowerMip(uv); -#else - /* NOTE(@fclem): textureSize() does not work the same on all implementations - * when changing the min and max texture levels. Use uniform instead (see T87801). */ - vec2 uv = texel * 2.0 * texelSize; - - vec4 samp; -# ifdef GPU_ARB_texture_gather - samp = gatherLowerMip(uv); -# else - samp.x = sampleLowerMip(uv + vec2(-0.5, -0.5) * texelSize); - samp.y = sampleLowerMip(uv + vec2(-0.5, 0.5) * texelSize); - samp.z = sampleLowerMip(uv + vec2(0.5, -0.5) * texelSize); - samp.w = sampleLowerMip(uv + vec2(0.5, 0.5) * texelSize); -# endif - - float val = minmax4(samp.x, samp.y, samp.z, samp.w); -#endif - -#if defined(GPU_INTEL) || defined(GPU_ATI) - /* Use color format instead of 24bit depth texture */ - fragColor = vec4(val); -#endif - gl_FragDepth = val; -} diff --git a/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl deleted file mode 100644 index 7331f92ba6d..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl +++ /dev/null @@ -1,36 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_math_lib.glsl) -#pragma BLENDER_REQUIRE(common_view_lib.glsl) - -/* Convert depth to Mist factor */ -uniform vec3 mistSettings; -uniform sampler2D depthBuffer; - -#define mistStart mistSettings.x -#define mistInvDistance mistSettings.y -#define mistFalloff mistSettings.z - -out vec4 fragColor; - -void main() -{ - vec2 texel_size = 1.0 / vec2(textureSize(depthBuffer, 0)).xy; - vec2 uvs = gl_FragCoord.xy * texel_size; - - float depth = textureLod(depthBuffer, uvs, 0.0).r; - vec3 co = get_view_space_from_depth(uvs, depth); - - float zcor = (ProjectionMatrix[3][3] == 0.0) ? length(co) : -co.z; - - /* bring depth into 0..1 range */ - float mist = saturate((zcor - mistStart) * mistInvDistance); - - /* falloff */ - mist = pow(mist, mistFalloff); - - fragColor = vec4(mist); - - // if (mist > 0.999) fragColor = vec4(1.0); - // else if (mist > 0.0001) fragColor = vec4(0.5); - // else fragColor = vec4(0.0); -} diff --git a/source/blender/draw/engines/eevee/shaders/effect_reflection_lib.glsl b/source/blender/draw/engines/eevee/shaders/effect_reflection_lib.glsl deleted file mode 100644 index ed600a3be86..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_reflection_lib.glsl +++ /dev/null @@ -1,101 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) - -/* Based on: - * "Stochastic Screen Space Reflections" - * by Tomasz Stachowiak. - * https://www.ea.com/frostbite/news/stochastic-screen-space-reflections - * and - * "Stochastic all the things: raytracing in hybrid real-time rendering" - * by Tomasz Stachowiak. - * https://media.contentapi.ea.com/content/dam/ea/seed/presentations/dd18-seed-raytracing-in-hybrid-real-time-rendering.pdf - */ - -uniform ivec2 halfresOffset; - -struct HitData { - /** Hit direction scaled by intersection time. */ - vec3 hit_dir; - /** Screen space [0..1] depth of the reflection hit position, or -1.0 for planar reflections. */ - float hit_depth; - /** Inverse probability of ray spawning in this direction. */ - float ray_pdf_inv; - /** True if ray has hit valid geometry. */ - bool is_hit; - /** True if ray was generated from a planar reflection probe. */ - bool is_planar; -}; - -void encode_hit_data(HitData data, vec3 hit_sP, vec3 vP, out vec4 hit_data, out float hit_depth) -{ - vec3 hit_vP = get_view_space_from_depth(hit_sP.xy, hit_sP.z); - hit_data.xyz = hit_vP - vP; - hit_depth = data.is_planar ? -1.0 : hit_sP.z; - /* Record 1.0 / pdf to reduce the computation in the resolve phase. */ - /* Encode hit validity in sign. */ - hit_data.w = data.ray_pdf_inv * ((data.is_hit) ? 1.0 : -1.0); -} - -HitData decode_hit_data(vec4 hit_data, float hit_depth) -{ - HitData data; - data.hit_dir.xyz = hit_data.xyz; - data.hit_depth = hit_depth; - data.is_planar = (hit_depth == -1.0); - data.ray_pdf_inv = abs(hit_data.w); - data.is_hit = (hit_data.w > 0.0); - return data; -} - -/* Blue noise categorised into 4 sets of samples. - * See "Stochastic all the things" presentation slide 32-37. */ -const int resolve_samples_count = 9; -const vec2 resolve_sample_offsets[36] = vec2[36]( - /* Set 1. */ - /* First Ring (2x2). */ - vec2(0, 0), - /* Second Ring (6x6). */ - vec2(-1, 3), - vec2(1, 3), - vec2(-1, 1), - vec2(3, 1), - vec2(-2, 0), - vec2(3, 0), - vec2(2, -1), - vec2(1, -2), - /* Set 2. */ - /* First Ring (2x2). */ - vec2(1, 1), - /* Second Ring (6x6). */ - vec2(-2, 3), - vec2(3, 3), - vec2(0, 2), - vec2(2, 2), - vec2(-2, -1), - vec2(1, -1), - vec2(0, -2), - vec2(3, -2), - /* Set 3. */ - /* First Ring (2x2). */ - vec2(0, 1), - /* Second Ring (6x6). */ - vec2(0, 3), - vec2(3, 2), - vec2(-2, 1), - vec2(2, 1), - vec2(-1, 0), - vec2(-2, -2), - vec2(0, -1), - vec2(2, -2), - /* Set 4. */ - /* First Ring (2x2). */ - vec2(1, 0), - /* Second Ring (6x6). */ - vec2(2, 3), - vec2(-2, 2), - vec2(-1, 2), - vec2(1, 2), - vec2(2, 0), - vec2(-1, -1), - vec2(3, -1), - vec2(-1, -2)); diff --git a/source/blender/draw/engines/eevee/shaders/effect_reflection_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_reflection_resolve_frag.glsl deleted file mode 100644 index 7568d70bd14..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_reflection_resolve_frag.glsl +++ /dev/null @@ -1,220 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_math_lib.glsl) -#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_lib.glsl) -#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) -#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl) -#pragma BLENDER_REQUIRE(surface_lib.glsl) -#pragma BLENDER_REQUIRE(effect_reflection_lib.glsl) - -/* Based on: - * "Stochastic Screen Space Reflections" - * by Tomasz Stachowiak. - * https://www.ea.com/frostbite/news/stochastic-screen-space-reflections - * and - * "Stochastic all the things: raytracing in hybrid real-time rendering" - * by Tomasz Stachowiak. - * https://media.contentapi.ea.com/content/dam/ea/seed/presentations/dd18-seed-raytracing-in-hybrid-real-time-rendering.pdf - */ - -uniform sampler2D colorBuffer; -uniform sampler2D normalBuffer; -uniform sampler2D specroughBuffer; -uniform sampler2D hitBuffer; -uniform sampler2D hitDepth; - -uniform int samplePoolOffset; - -in vec4 uvcoordsvar; - -out vec4 fragColor; - -vec4 ssr_get_scene_color_and_mask(vec3 hit_vP, int planar_index, float mip) -{ - vec2 uv; - if (planar_index != -1) { - uv = get_uvs_from_view(hit_vP); - /* Planar X axis is flipped. */ - uv.x = 1.0 - uv.x; - } - else { - /* Find hit position in previous frame. */ - /* TODO: Combine matrices. */ - vec3 hit_P = transform_point(ViewMatrixInverse, hit_vP); - /* TODO: real reprojection with motion vectors, etc... */ - uv = project_point(pastViewProjectionMatrix, hit_P).xy * 0.5 + 0.5; - } - - vec3 color; - if (planar_index != -1) { - color = textureLod(probePlanars, vec3(uv, planar_index), mip).rgb; - } - else { - color = textureLod(colorBuffer, uv * hizUvScale.xy, mip).rgb; - } - - /* Clamped brightness. */ - float luma = max_v3(color); - color *= 1.0 - max(0.0, luma - ssrFireflyFac) * safe_rcp(luma); - - float mask = screen_border_mask(uv); - return vec4(color, mask); -} - -void resolve_reflection_sample(int planar_index, - vec2 sample_uv, - vec3 vP, - vec3 vN, - vec3 vV, - float roughness_squared, - float cone_tan, - inout float weight_accum, - inout vec4 ssr_accum) -{ - vec4 hit_data = texture(hitBuffer, sample_uv); - float hit_depth = texture(hitDepth, sample_uv).r; - HitData data = decode_hit_data(hit_data, hit_depth); - - float hit_dist = length(data.hit_dir); - - /* Slide 54. */ - float bsdf = bsdf_ggx(vN, data.hit_dir / hit_dist, vV, roughness_squared); - - float weight = bsdf * data.ray_pdf_inv; - - /* Do not reuse hitpoint from planar reflections for normal reflections and vice versa. */ - if ((planar_index == -1 && data.is_planar) || (planar_index != -1 && !data.is_planar)) { - return; - } - /* Do not add light if ray has failed but still weight it. */ - if (!data.is_hit) { - weight_accum += weight; - return; - } - - vec3 hit_vP = vP + data.hit_dir; - - /* Compute cone footprint in screen space. */ - float cone_footprint = hit_dist * cone_tan; - float homcoord = ProjectionMatrix[2][3] * hit_vP.z + ProjectionMatrix[3][3]; - cone_footprint *= max(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) / homcoord; - cone_footprint *= ssrBrdfBias * 0.5; - /* Estimate a cone footprint to sample a corresponding mipmap level. */ - float mip = log2(cone_footprint * max_v2(vec2(textureSize(specroughBuffer, 0)))); - - vec4 radiance_mask = ssr_get_scene_color_and_mask(hit_vP, planar_index, mip); - - ssr_accum += radiance_mask * weight; - weight_accum += weight; -} - -void raytrace_resolve(ClosureInputGlossy cl_in, - inout ClosureEvalGlossy cl_eval, - inout ClosureEvalCommon cl_common, - inout ClosureOutputGlossy cl_out) -{ - float roughness = cl_in.roughness; - - vec4 ssr_accum = vec4(0.0); - float weight_acc = 0.0; - - if (roughness < ssrMaxRoughness + 0.2) { - /* Find Planar Reflections affecting this pixel */ - int planar_index = -1; - for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) { - float fade = probe_attenuation_planar(planars_data[i], cl_common.P); - fade *= probe_attenuation_planar_normal_roughness(planars_data[i], cl_in.N, 0.0); - if (fade > 0.5) { - planar_index = i; - break; - } - } - - vec3 V, P, N; - if (planar_index != -1) { - PlanarData pd = planars_data[planar_index]; - /* Evaluate everything in reflected space. */ - P = line_plane_intersect(cl_common.P, cl_common.V, pd.pl_plane_eq); - V = reflect(cl_common.V, pd.pl_normal); - N = reflect(cl_in.N, pd.pl_normal); - } - else { - V = cl_common.V; - P = cl_common.P; - N = cl_in.N; - } - - /* Using view space */ - vec3 vV = transform_direction(ViewMatrix, cl_common.V); - vec3 vP = transform_point(ViewMatrix, cl_common.P); - vec3 vN = transform_direction(ViewMatrix, cl_in.N); - - float roughness_squared = max(1e-3, sqr(roughness)); - float cone_cos = cone_cosine(roughness_squared); - float cone_tan = sqrt(1.0 - cone_cos * cone_cos) / cone_cos; - cone_tan *= mix(saturate(dot(vN, -vV) * 2.0), 1.0, roughness); /* Elongation fit */ - - int sample_pool = int((uint(gl_FragCoord.x) & 1u) + (uint(gl_FragCoord.y) & 1u) * 2u); - sample_pool = (sample_pool + (samplePoolOffset / 5)) % 4; - - for (int i = 0; i < resolve_samples_count; i++) { - int sample_id = sample_pool * resolve_samples_count + i; - vec2 texture_size = vec2(textureSize(hitBuffer, 0)); - vec2 sample_texel = texture_size * uvcoordsvar.xy * ssrUvScale; - vec2 sample_uv = (sample_texel + resolve_sample_offsets[sample_id]) / texture_size; - - resolve_reflection_sample( - planar_index, sample_uv, vP, vN, vV, roughness_squared, cone_tan, weight_acc, ssr_accum); - } - } - - /* Compute SSR contribution */ - ssr_accum *= safe_rcp(weight_acc); - /* fade between 0.5 and 1.0 roughness */ - ssr_accum.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, roughness); - - cl_eval.raytrace_radiance = ssr_accum.rgb * ssr_accum.a; - cl_common.specular_accum -= ssr_accum.a; -} - -CLOSURE_EVAL_FUNCTION_DECLARE_1(ssr_resolve, Glossy) - -void main() -{ - float depth = textureLod(maxzBuffer, uvcoordsvar.xy * hizUvScale.xy, 0.0).r; - - if (depth == 1.0) { - discard; - } - - ivec2 texel = ivec2(gl_FragCoord.xy); - vec4 speccol_roughness = texelFetch(specroughBuffer, texel, 0).rgba; - vec3 brdf = speccol_roughness.rgb; - float roughness = speccol_roughness.a; - - if (max_v3(brdf) <= 0.0) { - discard; - } - - FragDepth = depth; - - viewPosition = get_view_space_from_depth(uvcoordsvar.xy, depth); - worldPosition = transform_point(ViewMatrixInverse, viewPosition); - - vec2 normal_encoded = texelFetch(normalBuffer, texel, 0).rg; - viewNormal = normal_decode(normal_encoded, viewCameraVec(viewPosition)); - worldNormal = transform_direction(ViewMatrixInverse, viewNormal); - - CLOSURE_VARS_DECLARE_1(Glossy); - - in_Glossy_0.N = worldNormal; - in_Glossy_0.roughness = roughness; - - /* Do a full deferred evaluation of the glossy BSDF. The only difference is that we inject the - * SSR resolve before the cubemap iter. BRDF term is already computed during main pass and is - * passed as specular color. */ - CLOSURE_EVAL_FUNCTION_1(ssr_resolve, Glossy); - - fragColor = vec4(out_Glossy_0.radiance * brdf, 1.0); -} diff --git a/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl deleted file mode 100644 index 2f1efd588f7..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_reflection_trace_frag.glsl +++ /dev/null @@ -1,149 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_math_lib.glsl) -#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) -#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) -#pragma BLENDER_REQUIRE(raytrace_lib.glsl) -#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) -#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) -#pragma BLENDER_REQUIRE(effect_reflection_lib.glsl) - -/* Based on: - * "Stochastic Screen Space Reflections" - * by Tomasz Stachowiak. - * https://www.ea.com/frostbite/news/stochastic-screen-space-reflections - * and - * "Stochastic all the things: raytracing in hybrid real-time rendering" - * by Tomasz Stachowiak. - * https://media.contentapi.ea.com/content/dam/ea/seed/presentations/dd18-seed-raytracing-in-hybrid-real-time-rendering.pdf - */ - -uniform sampler2D normalBuffer; -uniform sampler2D specroughBuffer; -uniform vec2 targetSize; -uniform float randomScale; - -in vec4 uvcoordsvar; - -layout(location = 0) out vec4 hitData; -layout(location = 1) out float hitDepth; - -void main() -{ - vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy); - /* Decorrelate from AA. */ - /* TODO(fclem) we should use a more general approach for more random number dimensions. */ - vec2 random_px = floor(fract(rand.xy * 2.2074408460575947536) * 1.99999) - 0.5; - rand.xy = fract(rand.xy * 3.2471795724474602596); - - /* Randomly choose the pixel to start the ray from when tracing at lower resolution. - * This method also make sure we always start from the center of a fullres texel. */ - vec2 uvs = (gl_FragCoord.xy + random_px * randomScale) / (targetSize * ssrUvScale); - - float depth = textureLod(maxzBuffer, uvs * hizUvScale.xy, 0.0).r; - - HitData data; - data.is_planar = false; - data.ray_pdf_inv = 0.0; - data.is_hit = false; - data.hit_dir = vec3(0.0, 0.0, 0.0); - /* Default: not hits. */ - encode_hit_data(data, data.hit_dir, data.hit_dir, hitData, hitDepth); - - /* Early out */ - /* We can't do discard because we don't clear the render target. */ - if (depth == 1.0) { - return; - } - - /* Using view space */ - vec3 vP = get_view_space_from_depth(uvs, depth); - vec3 P = transform_point(ViewMatrixInverse, vP); - vec3 vV = viewCameraVec(vP); - vec3 V = cameraVec(P); - vec3 vN = normal_decode(texture(normalBuffer, uvs, 0).rg, vV); - vec3 N = transform_direction(ViewMatrixInverse, vN); - - /* Retrieve pixel data */ - vec4 speccol_roughness = texture(specroughBuffer, uvs, 0).rgba; - - /* Early out */ - if (dot(speccol_roughness.rgb, vec3(1.0)) == 0.0) { - return; - } - - float roughness = speccol_roughness.a; - float alpha = max(1e-3, roughness * roughness); - - /* Early out */ - if (roughness > ssrMaxRoughness + 0.2) { - return; - } - - /* Planar Reflections */ - int planar_id = -1; - for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) { - PlanarData pd = planars_data[i]; - - float fade = probe_attenuation_planar(pd, P); - fade *= probe_attenuation_planar_normal_roughness(pd, N, 0.0); - - if (fade > 0.5) { - /* Find view vector / reflection plane intersection. */ - /* TODO: optimize, use view space for all. */ - vec3 P_plane = line_plane_intersect(P, V, pd.pl_plane_eq); - vP = transform_point(ViewMatrix, P_plane); - - planar_id = i; - data.is_planar = true; - break; - } - } - - /* Gives *perfect* reflection for very small roughness */ - if (roughness < 0.04) { - rand.xzw *= 0.0; - } - /* Importance sampling bias */ - rand.x = mix(rand.x, 0.0, ssrBrdfBias); - - vec3 vT, vB; - make_orthonormal_basis(vN, vT, vB); /* Generate tangent space */ - - float pdf; - vec3 vH = sample_ggx(rand.xzw, alpha, vV, vN, vT, vB, pdf); - vec3 vR = reflect(-vV, vH); - - if (isnan(pdf)) { - /* Seems that somethings went wrong. - * This only happens on extreme cases where the normal deformed too much to have any valid - * reflections. */ - return; - } - - if (data.is_planar) { - vec3 view_plane_normal = transform_direction(ViewMatrix, planars_data[planar_id].pl_normal); - /* For planar reflections, we trace inside the reflected view. */ - vR = reflect(vR, view_plane_normal); - } - - Ray ray; - ray.origin = vP; - ray.direction = vR * 1e16; - - RayTraceParameters params; - params.thickness = ssrThickness; - params.jitter = rand.y; - params.trace_quality = ssrQuality; - params.roughness = alpha * alpha; - - vec3 hit_sP; - if (data.is_planar) { - data.is_hit = raytrace_planar(ray, params, planar_id, hit_sP); - } - else { - data.is_hit = raytrace(ray, params, true, false, hit_sP); - } - data.ray_pdf_inv = safe_rcp(pdf); - - encode_hit_data(data, hit_sP, ray.origin, hitData, hitDepth); -} diff --git a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl deleted file mode 100644 index 58bbb825043..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl +++ /dev/null @@ -1,78 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(common_math_lib.glsl) -#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) -#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl) - -/* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */ - -#define MAX_SSS_SAMPLES 65 -layout(std140) uniform sssProfile -{ - vec4 kernel[MAX_SSS_SAMPLES]; - vec4 radii_max_radius; - int sss_samples; -}; - -uniform sampler2D depthBuffer; -uniform sampler2D sssIrradiance; -uniform sampler2D sssRadius; -uniform sampler2D sssAlbedo; - -layout(location = 0) out vec4 sssRadiance; - -void main(void) -{ - vec2 pixel_size = 1.0 / vec2(textureSize(depthBuffer, 0).xy); /* TODO: precompute. */ - vec2 uvs = gl_FragCoord.xy * pixel_size; - vec3 sss_irradiance = texture(sssIrradiance, uvs).rgb; - float sss_radius = texture(sssRadius, uvs).r * radii_max_radius.w; - float depth = texture(depthBuffer, uvs).r; - float depth_view = get_view_z_from_depth(depth); - - float rand = texelfetch_noise_tex(gl_FragCoord.xy).r; -#ifdef FIRST_PASS - float angle = M_2PI * rand + M_PI_2; - vec2 dir = vec2(1.0, 0.0); -#else /* SECOND_PASS */ - float angle = M_2PI * rand; - vec2 dir = vec2(0.0, 1.0); -#endif - vec2 dir_rand = vec2(cos(angle), sin(angle)); - - /* Compute kernel bounds in 2D. */ - float homcoord = ProjectionMatrix[2][3] * depth_view + ProjectionMatrix[3][3]; - vec2 scale = vec2(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) * sss_radius / homcoord; - vec2 finalStep = scale * 0.5; /* samples range -1..1 */ - - float sss_radius_inv = 1.0 / max(1e-8, sss_radius); - - /* Center sample */ - vec3 accum = sss_irradiance * kernel[0].rgb; - - for (int i = 1; i < sss_samples && i < MAX_SSS_SAMPLES; i++) { - vec2 sample_uv = uvs + kernel[i].a * finalStep * - ((abs(kernel[i].a) > sssJitterThreshold) ? dir : dir_rand); - vec3 color = texture(sssIrradiance, sample_uv).rgb; - float sample_depth = texture(depthBuffer, sample_uv).r; - sample_depth = get_view_z_from_depth(sample_depth); - /* Depth correction factor. See Real Time Realistic Skin Translucency 2010 - * by Jimenez, eqs. 2 and 9, and D9740. - * Coefficient -2 follows from gaussian_profile() from gpu_material.c and - * from the definition of finalStep. */ - float depth_delta = (depth_view - sample_depth) * sss_radius_inv; - float s = exp(-2.0 * sqr(depth_delta)); - /* Out of view samples. */ - if (any(lessThan(sample_uv, vec2(0.0))) || any(greaterThan(sample_uv, vec2(1.0)))) { - s = 0.0; - } - /* Mix with first sample in failure case and apply kernel color. */ - accum += kernel[i].rgb * mix(sss_irradiance, color, s); - } - -#if defined(FIRST_PASS) - sssRadiance = vec4(accum, 1.0); -#else /* SECOND_PASS */ - sssRadiance = vec4(accum * texture(sssAlbedo, uvs).rgb, 1.0); -#endif -} diff --git a/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl b/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl deleted file mode 100644 index c54621f123d..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl +++ /dev/null @@ -1,117 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_math_lib.glsl) -#pragma BLENDER_REQUIRE(common_view_lib.glsl) - -uniform sampler2D colorBuffer; -uniform sampler2D depthBuffer; -uniform sampler2D colorHistoryBuffer; - -uniform mat4 prevViewProjectionMatrix; - -out vec4 FragColor; - -#ifdef USE_REPROJECTION - -/** - * Adapted from https://casual-effects.com/g3d/G3D10/data-files/shader/Film/Film_temporalAA.pix - * which is adapted from - * https://github.com/gokselgoktas/temporal-anti-aliasing/blob/master/Assets/Resources/Shaders/TemporalAntiAliasing.cginc - * which is adapted from https://github.com/playdeadgames/temporal - * Optimization by Stubbesaurus and epsilon adjustment to avoid division by zero. - * - * This can cause 3x3 blocks of color when there is a thin edge of a similar color that - * is varying in intensity. - */ -vec3 clip_to_aabb(vec3 color, vec3 minimum, vec3 maximum, vec3 average) -{ - /* NOTE: only clips towards aabb center (but fast!) */ - vec3 center = 0.5 * (maximum + minimum); - vec3 extents = 0.5 * (maximum - minimum); - vec3 dist = color - center; - vec3 ts = abs(extents) / max(abs(dist), vec3(0.0001)); - float t = saturate(min_v3(ts)); - return center + dist * t; -} - -/** - * Vastly based on https://github.com/playdeadgames/temporal - */ -void main() -{ - vec2 screen_res = vec2(textureSize(colorBuffer, 0).xy); - vec2 uv = gl_FragCoord.xy / screen_res; - ivec2 texel = ivec2(gl_FragCoord.xy); - - /* Compute pixel position in previous frame. */ - float depth = textureLod(depthBuffer, uv, 0.0).r; - vec3 pos = get_world_space_from_depth(uv, depth); - vec2 uv_history = project_point(prevViewProjectionMatrix, pos).xy * 0.5 + 0.5; - - /* HACK: Reject lookdev spheres from TAA reprojection. */ - if (depth == 0.0) { - uv_history = uv; - } - - ivec2 texel_history = ivec2(uv_history * screen_res); - vec4 color_history = textureLod(colorHistoryBuffer, uv_history, 0.0); - - /* Color bounding box clamping. 3x3 neighborhood. */ - vec4 c02 = texelFetchOffset(colorBuffer, texel, 0, ivec2(-1, 1)); - vec4 c12 = texelFetchOffset(colorBuffer, texel, 0, ivec2(0, 1)); - vec4 c22 = texelFetchOffset(colorBuffer, texel, 0, ivec2(1, 1)); - vec4 c01 = texelFetchOffset(colorBuffer, texel, 0, ivec2(-1, 0)); - vec4 c11 = texelFetchOffset(colorBuffer, texel, 0, ivec2(0, 0)); - vec4 c21 = texelFetchOffset(colorBuffer, texel, 0, ivec2(1, 0)); - vec4 c00 = texelFetchOffset(colorBuffer, texel, 0, ivec2(-1, -1)); - vec4 c10 = texelFetchOffset(colorBuffer, texel, 0, ivec2(0, -1)); - vec4 c20 = texelFetchOffset(colorBuffer, texel, 0, ivec2(1, -1)); - - vec4 color = c11; - - /* AABB minmax */ - vec4 min_col = min9(c02, c12, c22, c01, c11, c21, c00, c10, c20); - vec4 max_col = max9(c02, c12, c22, c01, c11, c21, c00, c10, c20); - vec4 avg_col = avg9(c02, c12, c22, c01, c11, c21, c00, c10, c20); - - /* bias the color aabb toward the center (rounding the shape) */ - vec4 min_center = min5(c12, c01, c11, c21, c10); - vec4 max_center = max5(c12, c01, c11, c21, c10); - vec4 avg_center = avg5(c12, c01, c11, c21, c10); - min_col = (min_col + min_center) * 0.5; - max_col = (max_col + max_center) * 0.5; - avg_col = (avg_col + avg_center) * 0.5; - - /* Clip color toward the center of the neighborhood colors AABB box. */ - color_history.rgb = clip_to_aabb(color_history.rgb, min_col.rgb, max_col.rgb, avg_col.rgb); - - /* Luminance weighting. */ - /* TODO: correct luminance. */ - float lum0 = dot(color.rgb, vec3(0.333)); - float lum1 = dot(color_history.rgb, vec3(0.333)); - float diff = abs(lum0 - lum1) / max(lum0, max(lum1, 0.2)); - float weight = 1.0 - diff; - float alpha = mix(0.04, 0.12, weight * weight); - - color_history = mix(color_history, color, alpha); - - bool out_of_view = any(greaterThanEqual(abs(uv_history - 0.5), vec2(0.5))); - color_history = (out_of_view) ? color : color_history; - - FragColor = safe_color(color_history); - /* There is some ghost issue if we use the alpha - * in the viewport. Overwriting alpha fixes it. */ - FragColor.a = color.a; -} - -#else - -uniform float alpha; - -void main() -{ - ivec2 texel = ivec2(gl_FragCoord.xy); - vec4 color = texelFetch(colorBuffer, texel, 0); - vec4 color_history = texelFetch(colorHistoryBuffer, texel, 0); - FragColor = safe_color(mix(color_history, color, alpha)); -} -#endif diff --git a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl deleted file mode 100644 index ee48c468630..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl +++ /dev/null @@ -1,272 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) -#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) -#pragma BLENDER_REQUIRE(lights_lib.glsl) - -in vec4 uvcoordsvar; - -out vec4 FragColor; - -uniform sampler2D depthBuffer; -uniform sampler1D sssTexProfile; -uniform sampler2D sssRadius; -uniform sampler2DArray sssShadowCubes; -uniform sampler2DArray sssShadowCascades; - -#define MAX_SSS_SAMPLES 65 -#define SSS_LUT_SIZE 64.0 -#define SSS_LUT_SCALE ((SSS_LUT_SIZE - 1.0) / float(SSS_LUT_SIZE)) -#define SSS_LUT_BIAS (0.5 / float(SSS_LUT_SIZE)) - -layout(std140) uniform sssProfile -{ - vec4 kernel[MAX_SSS_SAMPLES]; - vec4 radii_max_radius; - int sss_samples; -}; - -vec3 sss_profile(float s) -{ - s /= radii_max_radius.w; - return texture(sssTexProfile, saturate(s) * SSS_LUT_SCALE + SSS_LUT_BIAS).rgb; -} - -float light_translucent_power_with_falloff(LightData ld, vec3 N, vec4 l_vector) -{ - float power, falloff; - /* XXX: Removing Area Power. */ - /* TODO: put this out of the shader. */ - if (ld.l_type >= AREA_RECT) { - power = (ld.l_sizex * ld.l_sizey * 4.0 * M_PI) * (1.0 / 80.0); - if (ld.l_type == AREA_ELLIPSE) { - power *= M_PI * 0.25; - } - power *= 0.3 * 20.0 * - max(0.0, dot(-ld.l_forward, l_vector.xyz / l_vector.w)); /* XXX ad hoc, empirical */ - power /= (l_vector.w * l_vector.w); - falloff = dot(N, l_vector.xyz / l_vector.w); - } - else if (ld.l_type == SUN) { - power = 1.0 / (1.0 + (ld.l_radius * ld.l_radius * 0.5)); - power *= ld.l_radius * ld.l_radius * M_PI; /* Removing area light power. */ - power *= M_2PI * 0.78; /* Matching cycles with point light. */ - power *= 0.082; /* XXX ad hoc, empirical */ - falloff = dot(N, -ld.l_forward); - } - else { - power = (4.0 * ld.l_radius * ld.l_radius) * (1.0 / 10.0); - power *= 1.5; /* XXX ad hoc, empirical */ - power /= (l_vector.w * l_vector.w); - falloff = dot(N, l_vector.xyz / l_vector.w); - } - /* No transmittance at grazing angle (hide artifacts) */ - return power * saturate(falloff * 2.0); -} - -/* Some driver poorly optimize this code. Use direct reference to matrices. */ -#define sd(x) shadows_data[x] -#define scube(x) shadows_cube_data[x] -#define scascade(x) shadows_cascade_data[x] - -float shadow_cube_radial_depth(vec3 cubevec, float tex_id, int shadow_id) -{ - float depth = sample_cube(sssShadowCubes, cubevec, tex_id).r; - /* To reverting the constant bias from shadow rendering. (Tweaked for 16bit shadowmaps) */ - const float depth_bias = 3.1e-5; - depth = saturate(depth - depth_bias); - - depth = linear_depth(true, depth, sd(shadow_id).sh_far, sd(shadow_id).sh_near); - depth *= length(cubevec / max_v3(abs(cubevec))); - return depth; -} - -vec3 light_translucent(LightData ld, vec3 P, vec3 N, vec4 l_vector, vec2 rand, float sss_scale) -{ - int shadow_id = int(ld.l_shadowid); - - vec4 L = (ld.l_type != SUN) ? l_vector : vec4(-ld.l_forward, 1.0); - - /* We use the full l_vector.xyz so that the spread is minimize - * if the shading point is further away from the light source */ - /* TODO(fclem): do something better than this. */ - vec3 T, B; - make_orthonormal_basis(L.xyz / L.w, T, B); - - vec3 n; - vec4 depths; - float d, dist; - int data_id = int(sd(shadow_id).sh_data_index); - if (ld.l_type == SUN) { - vec4 view_z = vec4(dot(P - cameraPos, cameraForward)); - - vec4 weights = step(scascade(data_id).split_end_distances, view_z); - float id = abs(4.0 - dot(weights, weights)); - if (id > 3.0) { - return vec3(0.0); - } - - /* Same factor as in get_cascade_world_distance(). */ - float range = abs(sd(shadow_id).sh_far - sd(shadow_id).sh_near); - - vec4 shpos = scascade(data_id).shadowmat[int(id)] * vec4(P, 1.0); - dist = shpos.z * range; - - if (shpos.z > 1.0 || shpos.z < 0.0) { - return vec3(0.0); - } - - float tex_id = scascade(data_id).sh_tex_index + id; - - /* Assume cascades have same height and width. */ - vec2 ofs = vec2(1.0, 0.0) / float(textureSize(sssShadowCascades, 0).x); - d = sample_cascade(sssShadowCascades, shpos.xy, tex_id).r; - depths.x = sample_cascade(sssShadowCascades, shpos.xy + ofs.xy, tex_id).r; - depths.y = sample_cascade(sssShadowCascades, shpos.xy + ofs.yx, tex_id).r; - depths.z = sample_cascade(sssShadowCascades, shpos.xy - ofs.xy, tex_id).r; - depths.w = sample_cascade(sssShadowCascades, shpos.xy - ofs.yx, tex_id).r; - - /* To reverting the constant bias from shadow rendering. (Tweaked for 16bit shadowmaps) */ - float depth_bias = 3.1e-5; - depths = saturate(depths - depth_bias); - d = saturate(d - depth_bias); - - /* Size of a texel in world space. - * FIXME This is only correct if l_right is the same right vector used for shadowmap creation. - * This won't work if the shadow matrix is rotated (soft shadows). - * TODO: precompute. */ - float unit_world_in_uv_space = length(mat3(scascade(data_id).shadowmat[int(id)]) * ld.l_right); - float dx_scale = 2.0 * ofs.x / unit_world_in_uv_space; - - d *= range; - depths *= range; - - /* This is the normal of the occluder in world space. */ - // vec3 T = ld.l_forward * dx + ld.l_right * dx_scale; - // vec3 B = ld.l_forward * dy + ld.l_up * dx_scale; - // n = normalize(cross(T, B)); - } - else { - float ofs = 1.0 / float(textureSize(sssShadowCubes, 0).x); - - vec3 cubevec = transform_point(scube(data_id).shadowmat, P); - dist = length(cubevec); - cubevec /= dist; - /* tex_id == data_id for cube shadowmap */ - float tex_id = float(data_id); - d = shadow_cube_radial_depth(cubevec, tex_id, shadow_id); - /* NOTE: The offset is irregular in respect to cubeface uvs. But it has - * a much more uniform behavior than biasing based on face derivatives. */ - depths.x = shadow_cube_radial_depth(cubevec + T * ofs, tex_id, shadow_id); - depths.y = shadow_cube_radial_depth(cubevec + B * ofs, tex_id, shadow_id); - depths.z = shadow_cube_radial_depth(cubevec - T * ofs, tex_id, shadow_id); - depths.w = shadow_cube_radial_depth(cubevec - B * ofs, tex_id, shadow_id); - } - - float dx = depths.x - depths.z; - float dy = depths.y - depths.w; - - float s = min(d, min_v4(depths)); - - /* To avoid light leak from depth discontinuity and shadowmap aliasing. */ - float slope_bias = (abs(dx) + abs(dy)) * 0.5; - s -= slope_bias; - - float delta = dist - s; - - float power = light_translucent_power_with_falloff(ld, N, l_vector); - - return power * sss_profile(abs(delta) / sss_scale); -} - -#undef sd -#undef scube -#undef scsmd - -/* Similar to https://atyuwen.github.io/posts/normal-reconstruction/. - * This samples the depth buffer 4 time for each direction to get the most correct - * implicit normal reconstruction out of the depth buffer. */ -vec3 view_position_derivative_from_depth(vec2 uvs, vec2 ofs, vec3 vP, float depth_center) -{ - vec2 uv1 = uvs - ofs * 2.0; - vec2 uv2 = uvs - ofs; - vec2 uv3 = uvs + ofs; - vec2 uv4 = uvs + ofs * 2.0; - vec4 H; - H.x = textureLod(depthBuffer, uv1, 0.0).r; - H.y = textureLod(depthBuffer, uv2, 0.0).r; - H.z = textureLod(depthBuffer, uv3, 0.0).r; - H.w = textureLod(depthBuffer, uv4, 0.0).r; - /* Fix issue with depth precision. Take even larger diff. */ - vec4 diff = abs(vec4(depth_center, H.yzw) - H.x); - if (max_v4(diff) < 2.4e-7 && all(lessThan(diff.xyz, diff.www))) { - return 0.25 * (get_view_space_from_depth(uv3, H.w) - get_view_space_from_depth(uv1, H.x)); - } - /* Simplified (H.xw + 2.0 * (H.yz - H.xw)) - depth_center */ - vec2 deltas = abs((2.0 * H.yz - H.xw) - depth_center); - if (deltas.x < deltas.y) { - return vP - get_view_space_from_depth(uv2, H.y); - } - else { - return get_view_space_from_depth(uv3, H.z) - vP; - } -} - -/* TODO(fclem) port to a common place for other effects to use. */ -bool reconstruct_view_position_and_normal_from_depth(vec2 uvs, out vec3 vP, out vec3 vNg) -{ - vec2 texel_size = vec2(abs(dFdx(uvs.x)), abs(dFdy(uvs.y))); - float depth_center = textureLod(depthBuffer, uvs, 0.0).r; - - vP = get_view_space_from_depth(uvs, depth_center); - - vec3 dPdx = view_position_derivative_from_depth(uvs, texel_size * vec2(1, 0), vP, depth_center); - vec3 dPdy = view_position_derivative_from_depth(uvs, texel_size * vec2(0, 1), vP, depth_center); - - vNg = safe_normalize(cross(dPdx, dPdy)); - - /* Background case. */ - if (depth_center == 1.0) { - return false; - } - - return true; -} - -void main(void) -{ - vec2 uvs = uvcoordsvar.xy; - float sss_scale = texture(sssRadius, uvs).r; - - vec3 rand = texelfetch_noise_tex(gl_FragCoord.xy).zwy; - rand.xy *= fast_sqrt(rand.z); - - vec3 vP, vNg; - reconstruct_view_position_and_normal_from_depth(uvs, vP, vNg); - - vec3 P = point_view_to_world(vP); - vec3 Ng = normal_view_to_world(vNg); - - vec3 accum = vec3(0.0); - for (int i = 0; i < MAX_LIGHT && i < laNumLight; i++) { - LightData ld = lights_data[i]; - - /* Only shadowed light can produce translucency */ - if (ld.l_shadowid < 0.0) { - continue; - } - - vec4 l_vector; /* Non-Normalized Light Vector with length in last component. */ - l_vector.xyz = ld.l_position - P; - l_vector.w = length(l_vector.xyz); - - float att = light_attenuation(ld, l_vector); - if (att < 1e-8) { - continue; - } - - accum += att * ld.l_color * light_translucent(ld, P, -Ng, l_vector, rand.xy, sss_scale); - } - - FragColor = vec4(accum, 1.0); -} diff --git a/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl deleted file mode 100644 index 9182171bab4..00000000000 --- a/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl +++ /dev/null @@ -1,32 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_math_lib.glsl) - -uniform sampler2D depthBuffer; - -uniform mat4 prevViewProjMatrix; -uniform mat4 currViewProjMatrixInv; -uniform mat4 nextViewProjMatrix; - -out vec4 outData; - -void main() -{ - /* Extract pixel motion vector from camera movement. */ - ivec2 texel = ivec2(gl_FragCoord.xy); - vec2 uv_curr = gl_FragCoord.xy / vec2(textureSize(depthBuffer, 0).xy); - - float depth = texelFetch(depthBuffer, texel, 0).r; - - uv_curr = uv_curr * 2.0 - 1.0; - depth = depth * 2.0 - 1.0; - - vec3 world_position = project_point(currViewProjMatrixInv, vec3(uv_curr, depth)); - vec2 uv_prev = project_point(prevViewProjMatrix, world_position).xy; - vec2 uv_next = project_point(nextViewProjMatrix, world_position).xy; - - outData.xy = uv_prev - uv_curr; - outData.zw = uv_next - uv_curr; - - /* Encode to unsigned normalized 16bit texture. */ - outData = outData * 0.5 + 0.5; -} diff --git a/source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl b/source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl deleted file mode 100644 index 2274bf8b950..00000000000 --- a/source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl +++ /dev/null @@ -1,212 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_math_lib.glsl) -#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl) -#pragma BLENDER_REQUIRE(octahedron_lib.glsl) - -#define IRRADIANCE_LIB - -/* ---------------------------------------------------------------------- */ -/** \name Structure - * \{ */ - -#if defined(IRRADIANCE_SH_L2) -struct IrradianceData { - vec3 shcoefs[9]; -}; - -#else /* defined(IRRADIANCE_HL2) */ -struct IrradianceData { - vec3 cubesides[3]; -}; - -#endif - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Resources - * \{ */ - -uniform sampler2DArray irradianceGrid; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Functions - * \{ */ - -vec4 irradiance_encode(vec3 rgb) -{ - float maxRGB = max_v3(rgb); - float fexp = ceil(log2(maxRGB)); - return vec4(rgb / exp2(fexp), (fexp + 128.0) / 255.0); -} - -vec3 irradiance_decode(vec4 data) -{ - float fexp = data.a * 255.0 - 128.0; - return data.rgb * exp2(fexp); -} - -vec4 visibility_encode(vec2 accum, float range) -{ - accum /= range; - - vec4 data; - data.x = fract(accum.x); - data.y = floor(accum.x) / 255.0; - data.z = fract(accum.y); - data.w = floor(accum.y) / 255.0; - - return data; -} - -vec2 visibility_decode(vec4 data, float range) -{ - return (data.xz + data.yw * 255.0) * range; -} - -IrradianceData load_irradiance_cell(int cell, vec3 N) -{ - /* Keep in sync with diffuse_filter_probe() */ - -#if defined(IRRADIANCE_SH_L2) - - ivec2 cell_co = ivec2(3, 3); - int cell_per_row = textureSize(irradianceGrid, 0).x / cell_co.x; - cell_co.x *= cell % cell_per_row; - cell_co.y *= cell / cell_per_row; - - ivec3 ofs = ivec3(0, 1, 2); - - IrradianceData ir; - ir.shcoefs[0] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.xx, 0), 0).rgb; - ir.shcoefs[1] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.yx, 0), 0).rgb; - ir.shcoefs[2] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.zx, 0), 0).rgb; - ir.shcoefs[3] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.xy, 0), 0).rgb; - ir.shcoefs[4] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.yy, 0), 0).rgb; - ir.shcoefs[5] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.zy, 0), 0).rgb; - ir.shcoefs[6] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.xz, 0), 0).rgb; - ir.shcoefs[7] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.yz, 0), 0).rgb; - ir.shcoefs[8] = texelFetch(irradianceGrid, ivec3(cell_co + ofs.zz, 0), 0).rgb; - -#else /* defined(IRRADIANCE_HL2) */ - - ivec2 cell_co = ivec2(3, 2); - int cell_per_row = textureSize(irradianceGrid, 0).x / cell_co.x; - cell_co.x *= cell % cell_per_row; - cell_co.y *= cell / cell_per_row; - - ivec3 is_negative = ivec3(step(0.0, -N)); - - IrradianceData ir; - ir.cubesides[0] = irradiance_decode( - texelFetch(irradianceGrid, ivec3(cell_co + ivec2(0, is_negative.x), 0), 0)); - ir.cubesides[1] = irradiance_decode( - texelFetch(irradianceGrid, ivec3(cell_co + ivec2(1, is_negative.y), 0), 0)); - ir.cubesides[2] = irradiance_decode( - texelFetch(irradianceGrid, ivec3(cell_co + ivec2(2, is_negative.z), 0), 0)); - -#endif - - return ir; -} - -float load_visibility_cell(int cell, vec3 L, float dist, float bias, float bleed_bias, float range) -{ - /* Keep in sync with diffuse_filter_probe() */ - ivec2 cell_co = ivec2(prbIrradianceVisSize); - ivec2 cell_per_row_col = textureSize(irradianceGrid, 0).xy / prbIrradianceVisSize; - cell_co.x *= (cell % cell_per_row_col.x); - cell_co.y *= (cell / cell_per_row_col.x) % cell_per_row_col.y; - float layer = 1.0 + float((cell / cell_per_row_col.x) / cell_per_row_col.y); - - vec2 texel_size = 1.0 / vec2(textureSize(irradianceGrid, 0).xy); - vec2 co = vec2(cell_co) * texel_size; - - vec2 uv = mapping_octahedron(-L, vec2(1.0 / float(prbIrradianceVisSize))); - uv *= vec2(prbIrradianceVisSize) * texel_size; - - vec4 data = texture(irradianceGrid, vec3(co + uv, layer)); - - /* Decoding compressed data */ - vec2 moments = visibility_decode(data, range); - - /* Doing chebishev test */ - float variance = abs(moments.x * moments.x - moments.y); - variance = max(variance, bias / 10.0); - - float d = dist - moments.x; - float p_max = variance / (variance + d * d); - - /* Increase contrast in the weight by squaring it */ - p_max *= p_max; - - /* Now reduce light-bleeding by removing the [0, x] tail and linearly rescaling (x, 1] */ - p_max = clamp((p_max - bleed_bias) / (1.0 - bleed_bias), 0.0, 1.0); - - return (dist <= moments.x) ? 1.0 : p_max; -} - -/* http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/ */ -vec3 spherical_harmonics_L1(vec3 N, vec3 shcoefs[4]) -{ - vec3 sh = vec3(0.0); - - sh += 0.282095 * shcoefs[0]; - - sh += -0.488603 * N.z * shcoefs[1]; - sh += 0.488603 * N.y * shcoefs[2]; - sh += -0.488603 * N.x * shcoefs[3]; - - return sh; -} - -vec3 spherical_harmonics_L2(vec3 N, vec3 shcoefs[9]) -{ - vec3 sh = vec3(0.0); - - sh += 0.282095 * shcoefs[0]; - - sh += -0.488603 * N.z * shcoefs[1]; - sh += 0.488603 * N.y * shcoefs[2]; - sh += -0.488603 * N.x * shcoefs[3]; - - sh += 1.092548 * N.x * N.z * shcoefs[4]; - sh += -1.092548 * N.z * N.y * shcoefs[5]; - sh += 0.315392 * (3.0 * N.y * N.y - 1.0) * shcoefs[6]; - sh += -1.092548 * N.x * N.y * shcoefs[7]; - sh += 0.546274 * (N.x * N.x - N.z * N.z) * shcoefs[8]; - - return sh; -} - -vec3 hl2_basis(vec3 N, vec3 cubesides[3]) -{ - vec3 irradiance = vec3(0.0); - - vec3 n_squared = N * N; - - irradiance += n_squared.x * cubesides[0]; - irradiance += n_squared.y * cubesides[1]; - irradiance += n_squared.z * cubesides[2]; - - return irradiance; -} - -vec3 compute_irradiance(vec3 N, IrradianceData ird) -{ -#if defined(IRRADIANCE_SH_L2) - return spherical_harmonics_L2(N, ird.shcoefs); -#else /* defined(IRRADIANCE_HL2) */ - return hl2_basis(N, ird.cubesides); -#endif -} - -vec3 irradiance_from_cell_get(int cell, vec3 ir_dir) -{ - IrradianceData ir_data = load_irradiance_cell(cell, ir_dir); - return compute_irradiance(ir_dir, ir_data); -} - -/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl deleted file mode 100644 index b0da4274a13..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl +++ /dev/null @@ -1,22 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) - -flat in int pid; -in vec2 quadCoord; - -out vec4 FragColor; - -void main() -{ - float dist_sqr = dot(quadCoord, quadCoord); - - /* Discard outside the circle. */ - if (dist_sqr > 1.0) { - discard; - } - - vec3 view_nor = vec3(quadCoord, sqrt(max(0.0, 1.0 - dist_sqr))); - vec3 world_ref = mat3(ViewMatrixInverse) * reflect(vec3(0.0, 0.0, -1.0), view_nor); - FragColor = vec4(textureLod_cubemapArray(probeCubes, vec4(world_ref, pid), 0.0).rgb, 1.0); -} diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl deleted file mode 100644 index c9af2753baa..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl +++ /dev/null @@ -1,44 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) - -/* XXX TODO: fix code duplication. */ -struct CubeData { - vec4 position_type; - vec4 attenuation_fac_type; - mat4 influencemat; - mat4 parallaxmat; -}; - -layout(std140) uniform probe_block -{ - CubeData probes_data[MAX_PROBE]; -}; - -uniform float sphere_size; -uniform vec3 screen_vecs[2]; - -flat out int pid; -out vec2 quadCoord; - -const vec2 pos[6] = vec2[6](vec2(-1.0, -1.0), - vec2(1.0, -1.0), - vec2(-1.0, 1.0), - - vec2(1.0, -1.0), - vec2(1.0, 1.0), - vec2(-1.0, 1.0)); - -void main() -{ - pid = 1 + (gl_VertexID / 6); /* +1 for the world */ - int vert_id = gl_VertexID % 6; - - quadCoord = pos[vert_id]; - - vec3 ws_location = probes_data[pid].position_type.xyz; - vec3 screen_pos = screen_vecs[0] * quadCoord.x + screen_vecs[1] * quadCoord.y; - ws_location += screen_pos * sphere_size; - - gl_Position = ViewProjectionMatrix * vec4(ws_location, 1.0); - gl_Position.z += 0.0001; /* Small bias to let the icon draw without zfighting */ -} diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl deleted file mode 100644 index 9ecc50d9df5..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl +++ /dev/null @@ -1,170 +0,0 @@ - -#pragma BLENDER_REQUIRE(random_lib.glsl) -#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) -#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) -#pragma BLENDER_REQUIRE(irradiance_lib.glsl) - -uniform samplerCube probeHdr; -uniform int probeSize; -uniform float lodFactor; -uniform float lodMax; -uniform float intensityFac; - -uniform float sampleCount; - -in vec3 worldPosition; - -out vec4 FragColor; - -#define M_4PI 12.5663706143591729 - -const mat3 CUBE_ROTATIONS[6] = mat3[]( - mat3(vec3(0.0, 0.0, -1.0), vec3(0.0, -1.0, 0.0), vec3(-1.0, 0.0, 0.0)), - mat3(vec3(0.0, 0.0, 1.0), vec3(0.0, -1.0, 0.0), vec3(1.0, 0.0, 0.0)), - mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, -1.0, 0.0)), - mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 0.0, -1.0), vec3(0.0, 1.0, 0.0)), - mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, -1.0)), - mat3(vec3(-1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0))); - -vec3 get_cubemap_vector(vec2 co, int face) -{ - return normalize(CUBE_ROTATIONS[face] * vec3(co * 2.0 - 1.0, 1.0)); -} - -float area_element(float x, float y) -{ - return atan(x * y, sqrt(x * x + y * y + 1)); -} - -float texel_solid_angle(vec2 co, float halfpix) -{ - vec2 v1 = (co - vec2(halfpix)) * 2.0 - 1.0; - vec2 v2 = (co + vec2(halfpix)) * 2.0 - 1.0; - - return area_element(v1.x, v1.y) - area_element(v1.x, v2.y) - area_element(v2.x, v1.y) + - area_element(v2.x, v2.y); -} - -vec3 octahedral_to_cubemap_proj(vec2 co) -{ - co = co * 2.0 - 1.0; - - vec2 abs_co = abs(co); - vec3 v = vec3(co, 1.0 - (abs_co.x + abs_co.y)); - - if (abs_co.x + abs_co.y > 1.0) { - v.xy = (abs(co.yx) - 1.0) * -sign(co.xy); - } - - return v; -} - -void main() -{ -#if defined(IRRADIANCE_SH_L2) - float pixstep = 1.0 / probeSize; - float halfpix = pixstep / 2.0; - - /* Downside: leaks negative values, very bandwidth consuming */ - int comp = int(gl_FragCoord.x) % 3 + (int(gl_FragCoord.y) % 3) * 3; - - float weight_accum = 0.0; - vec3 sh = vec3(0.0); - - for (int face = 0; face < 6; face++) { - for (float x = halfpix; x < 1.0; x += pixstep) { - for (float y = halfpix; y < 1.0; y += pixstep) { - float weight, coef; - vec2 facecoord = vec2(x, y); - vec3 cubevec = get_cubemap_vector(facecoord, face); - - if (comp == 0) { - coef = 0.282095; - } - else if (comp == 1) { - coef = -0.488603 * cubevec.z * 2.0 / 3.0; - } - else if (comp == 2) { - coef = 0.488603 * cubevec.y * 2.0 / 3.0; - } - else if (comp == 3) { - coef = -0.488603 * cubevec.x * 2.0 / 3.0; - } - else if (comp == 4) { - coef = 1.092548 * cubevec.x * cubevec.z * 1.0 / 4.0; - } - else if (comp == 5) { - coef = -1.092548 * cubevec.z * cubevec.y * 1.0 / 4.0; - } - else if (comp == 6) { - coef = 0.315392 * (3.0 * cubevec.y * cubevec.y - 1.0) * 1.0 / 4.0; - } - else if (comp == 7) { - coef = 1.092548 * cubevec.x * cubevec.y * 1.0 / 4.0; - } - else { /* (comp == 8) */ - coef = 0.546274 * (cubevec.x * cubevec.x - cubevec.z * cubevec.z) * 1.0 / 4.0; - } - - weight = texel_solid_angle(facecoord, halfpix); - - vec4 samp = textureLod(probeHdr, cubevec, lodMax); - sh += samp.rgb * coef * weight; - weight_accum += weight; - } - } - } - sh *= M_4PI / weight_accum; - - FragColor = vec4(sh, 1.0); -#else -# if defined(IRRADIANCE_HL2) - /* Downside: very very low resolution (6 texels), bleed lighting because of interpolation */ - int x = int(gl_FragCoord.x) % 3; - int y = int(gl_FragCoord.y) % 2; - - vec3 cubevec = vec3(1.0, 0.0, 0.0); - - if (x == 1) { - cubevec = cubevec.yxy; - } - else if (x == 2) { - cubevec = cubevec.yyx; - } - - if (y == 1) { - cubevec = -cubevec; - } -# endif - - vec3 N, T, B, V; - - N = normalize(cubevec); - - make_orthonormal_basis(N, T, B); /* Generate tangent space */ - - /* Integrating Envmap */ - float weight = 0.0; - vec3 out_radiance = vec3(0.0); - for (float i = 0; i < sampleCount; i++) { - vec3 Xi = rand2d_to_cylinder(hammersley_2d(i, sampleCount)); - - float pdf; - vec3 L = sample_uniform_hemisphere(Xi, N, T, B, pdf); - float NL = dot(N, L); - - if (NL > 0.0) { - /* Coarse Approximation of the mapping distortion - * Unit Sphere -> Cubemap Face */ - const float dist = 4.0 * M_PI / 6.0; - /* http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html : Equation 13 */ - float lod = clamp(lodFactor - 0.5 * log2(pdf * dist), 0.0, lodMax); - - out_radiance += textureLod(probeHdr, L, lod).rgb * NL; - weight += NL; - } - } - - FragColor = irradiance_encode(intensityFac * out_radiance / weight); -#endif -} diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl deleted file mode 100644 index a5d11f52a1d..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl +++ /dev/null @@ -1,79 +0,0 @@ - -#pragma BLENDER_REQUIRE(random_lib.glsl) -#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) -#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) - -uniform samplerCube probeHdr; -uniform float roughness; -uniform float texelSize; -uniform float lodFactor; -uniform float lodMax; -uniform float paddingSize; -uniform float intensityFac; -uniform float fireflyFactor; - -uniform float sampleCount; - -in vec3 worldPosition; - -out vec4 FragColor; - -vec3 octahedral_to_cubemap_proj(vec2 co) -{ - co = co * 2.0 - 1.0; - - vec2 abs_co = abs(co); - vec3 v = vec3(co, 1.0 - (abs_co.x + abs_co.y)); - - if (abs_co.x + abs_co.y > 1.0) { - v.xy = (abs(co.yx) - 1.0) * -sign(co.xy); - } - - return v; -} - -void main() -{ - vec3 N, T, B, V; - - vec3 R = normalize(worldPosition); - - /* Isotropic assumption */ - N = V = R; - - make_orthonormal_basis(N, T, B); /* Generate tangent space */ - - /* Integrating Envmap */ - float weight = 0.0; - vec3 out_radiance = vec3(0.0); - for (float i = 0; i < sampleCount; i++) { - vec3 Xi = rand2d_to_cylinder(hammersley_2d(i, sampleCount)); - - float pdf; - /* Microfacet normal */ - vec3 H = sample_ggx(Xi, roughness, V, N, T, B, pdf); - vec3 L = -reflect(V, H); - float NL = dot(N, L); - - if (NL > 0.0) { - float NH = max(1e-8, dot(N, H)); /* cosTheta */ - - /* Coarse Approximation of the mapping distortion - * Unit Sphere -> Cubemap Face */ - const float dist = 4.0 * M_PI / 6.0; - /* http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html : Equation 13 */ - float lod = clamp(lodFactor - 0.5 * log2(pdf * dist), 0.0, lodMax); - - vec3 l_col = textureLod(probeHdr, L, lod).rgb; - - /* Clamped brightness. */ - float luma = max(1e-8, max_v3(l_col)); - l_col *= 1.0 - max(0.0, luma - fireflyFactor) / luma; - - out_radiance += l_col * NL; - weight += NL; - } - } - - FragColor = vec4(intensityFac * out_radiance / weight, 1.0); -} diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl deleted file mode 100644 index d25ef23a706..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl +++ /dev/null @@ -1,97 +0,0 @@ - -#pragma BLENDER_REQUIRE(random_lib.glsl) -#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) -#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) -#pragma BLENDER_REQUIRE(irradiance_lib.glsl) - -uniform samplerCube probeDepth; -uniform int outputSize; -uniform float lodFactor; -uniform float storedTexelSize; -uniform float lodMax; -uniform float nearClip; -uniform float farClip; -uniform float visibilityRange; -uniform float visibilityBlur; - -uniform float sampleCount; -uniform float; - -out vec4 FragColor; - -vec3 octahedral_to_cubemap_proj(vec2 co) -{ - co = co * 2.0 - 1.0; - - vec2 abs_co = abs(co); - vec3 v = vec3(co, 1.0 - (abs_co.x + abs_co.y)); - - if (abs_co.x + abs_co.y > 1.0) { - v.xy = (abs(co.yx) - 1.0) * -sign(co.xy); - } - - return v; -} - -float linear_depth(float z) -{ - return (nearClip * farClip) / (z * (nearClip - farClip) + farClip); -} - -float get_world_distance(float depth, vec3 cos) -{ - float is_background = step(1.0, depth); - depth = linear_depth(depth); - depth += 1e1 * is_background; - cos = normalize(abs(cos)); - float cos_vec = max(cos.x, max(cos.y, cos.z)); - return depth / cos_vec; -} - -void main() -{ - ivec2 texel = ivec2(gl_FragCoord.xy) % ivec2(outputSize); - - vec3 cos; - - cos.xy = (vec2(texel) + 0.5) * storedTexelSize; - - /* add a 2 pixel border to ensure filtering is correct */ - cos.xy = (cos.xy - storedTexelSize) / (1.0 - 2.0 * storedTexelSize); - - float pattern = 1.0; - - /* edge mirroring : only mirror if directly adjacent - * (not diagonally adjacent) */ - vec2 m = abs(cos.xy - 0.5) + 0.5; - vec2 f = floor(m); - if (f.x - f.y != 0.0) { - cos.xy = 1.0 - cos.xy; - } - - /* clamp to [0-1] */ - cos.xy = fract(cos.xy); - - /* get cubemap vector */ - cos = normalize(octahedral_to_cubemap_proj(cos.xy)); - - vec3 T, B; - make_orthonormal_basis(cos, T, B); /* Generate tangent space */ - - vec2 accum = vec2(0.0); - - for (float i = 0; i < sampleCount; i++) { - vec3 Xi = rand2d_to_cylinder(hammersley_2d(i, sampleCount)); - - vec3 samp = sample_uniform_cone(Xi, M_PI_2 * visibilityBlur, cos, T, B); - float depth = texture(probeDepth, samp).r; - depth = get_world_distance(depth, samp); - accum += vec2(depth, depth * depth); - } - - accum /= sampleCount; - accum = abs(accum); - - /* Encode to normalized RGBA 8 */ - FragColor = visibility_encode(accum, visibilityRange); -} diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl deleted file mode 100644 index 009ccf6535e..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl +++ /dev/null @@ -1,47 +0,0 @@ - -layout(triangles) in; -layout(triangle_strip, max_vertices = 3) out; - -uniform int Layer; - -in vec4 vPos[]; -flat in int face[]; -flat out int fFace; - -out vec3 worldPosition; -out vec3 viewPosition; /* Required. otherwise generate linking error. */ -out vec3 worldNormal; /* Required. otherwise generate linking error. */ -out vec3 viewNormal; /* Required. otherwise generate linking error. */ - -const vec3 maj_axes[6] = vec3[6](vec3(1.0, 0.0, 0.0), - vec3(-1.0, 0.0, 0.0), - vec3(0.0, 1.0, 0.0), - vec3(0.0, -1.0, 0.0), - vec3(0.0, 0.0, 1.0), - vec3(0.0, 0.0, -1.0)); -const vec3 x_axis[6] = vec3[6](vec3(0.0, 0.0, -1.0), - vec3(0.0, 0.0, 1.0), - vec3(1.0, 0.0, 0.0), - vec3(1.0, 0.0, 0.0), - vec3(1.0, 0.0, 0.0), - vec3(-1.0, 0.0, 0.0)); -const vec3 y_axis[6] = vec3[6](vec3(0.0, -1.0, 0.0), - vec3(0.0, -1.0, 0.0), - vec3(0.0, 0.0, 1.0), - vec3(0.0, 0.0, -1.0), - vec3(0.0, -1.0, 0.0), - vec3(0.0, -1.0, 0.0)); - -void main() -{ - fFace = face[0]; - gl_Layer = Layer + fFace; - - for (int v = 0; v < 3; v++) { - gl_Position = vPos[v]; - worldPosition = x_axis[fFace] * vPos[v].x + y_axis[fFace] * vPos[v].y + maj_axes[fFace]; - EmitVertex(); - } - - EndPrimitive(); -} diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl deleted file mode 100644 index dc5ec1e40f5..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl +++ /dev/null @@ -1,22 +0,0 @@ -#pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(irradiance_lib.glsl) - -flat in int cellOffset; -in vec2 quadCoord; - -out vec4 FragColor; - -void main() -{ - float dist_sqr = dot(quadCoord, quadCoord); - - /* Discard outside the circle. */ - if (dist_sqr > 1.0) { - discard; - } - - vec3 view_nor = vec3(quadCoord, sqrt(max(0.0, 1.0 - dist_sqr))); - vec3 world_nor = mat3(ViewMatrixInverse) * view_nor; - IrradianceData ir_data = load_irradiance_cell(cellOffset, world_nor); - FragColor = vec4(compute_irradiance(world_nor, ir_data), 1.0); -} diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl deleted file mode 100644 index 6fefe1319bd..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl +++ /dev/null @@ -1,47 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) - -uniform float sphere_size; -uniform int offset; -uniform ivec3 grid_resolution; -uniform vec3 corner; -uniform vec3 increment_x; -uniform vec3 increment_y; -uniform vec3 increment_z; -uniform vec3 screen_vecs[2]; - -flat out int cellOffset; -out vec2 quadCoord; - -const vec2 pos[6] = vec2[6](vec2(-1.0, -1.0), - vec2(1.0, -1.0), - vec2(-1.0, 1.0), - - vec2(1.0, -1.0), - vec2(1.0, 1.0), - vec2(-1.0, 1.0)); - -void main() -{ - int cell_id = gl_VertexID / 6; - int vert_id = gl_VertexID % 6; - - vec3 ls_cell_location; - /* Keep in sync with update_irradiance_probe */ - ls_cell_location.z = float(cell_id % grid_resolution.z); - ls_cell_location.y = float((cell_id / grid_resolution.z) % grid_resolution.y); - ls_cell_location.x = float(cell_id / (grid_resolution.z * grid_resolution.y)); - - cellOffset = offset + cell_id; - - vec3 ws_cell_location = corner + - (increment_x * ls_cell_location.x + increment_y * ls_cell_location.y + - increment_z * ls_cell_location.z); - - quadCoord = pos[vert_id]; - vec3 screen_pos = screen_vecs[0] * quadCoord.x + screen_vecs[1] * quadCoord.y; - ws_cell_location += screen_pos * sphere_size; - - gl_Position = ViewProjectionMatrix * vec4(ws_cell_location, 1.0); - gl_Position.z += 0.0001; /* Small bias to let the icon draw without zfighting */ -} diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_fill_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_fill_frag.glsl deleted file mode 100644 index 71e4d6e2c4a..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_fill_frag.glsl +++ /dev/null @@ -1,18 +0,0 @@ -uniform sampler2DArray irradianceGrid; - -out vec4 FragColor; - -void main() -{ -#if defined(IRRADIANCE_SH_L2) - const ivec2 data_size = ivec2(3, 3); -#elif defined(IRRADIANCE_HL2) - const ivec2 data_size = ivec2(3, 2); -#endif - ivec2 coord = ivec2(gl_FragCoord.xy) % data_size; - FragColor = texelFetch(irradianceGrid, ivec3(coord, 0), 0); - - if (any(greaterThanEqual(ivec2(gl_FragCoord.xy), data_size))) { - FragColor = vec4(0.0, 0.0, 0.0, 1.0); - } -} diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl deleted file mode 100644 index 84626eac4cf..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl +++ /dev/null @@ -1,311 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) -#pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) -#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl) -#pragma BLENDER_REQUIRE(cubemap_lib.glsl) -#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) -#pragma BLENDER_REQUIRE(irradiance_lib.glsl) - -/* ----------- Uniforms --------- */ - -uniform sampler2DArray probePlanars; -uniform samplerCubeArray probeCubes; - -/* ----------- Structures --------- */ - -struct CubeData { - vec4 position_type; - vec4 attenuation_fac_type; - mat4 influencemat; - mat4 parallaxmat; -}; - -#define PROBE_PARALLAX_BOX 1.0 -#define PROBE_ATTENUATION_BOX 1.0 - -#define p_position position_type.xyz -#define p_parallax_type position_type.w -#define p_atten_fac attenuation_fac_type.x -#define p_atten_type attenuation_fac_type.y - -struct PlanarData { - vec4 plane_equation; - vec4 clip_vec_x_fade_scale; - vec4 clip_vec_y_fade_bias; - vec4 clip_edges; - vec4 facing_scale_bias; - mat4 reflectionmat; /* transform world space into reflection texture space */ - mat4 unused; -}; - -#define pl_plane_eq plane_equation -#define pl_normal plane_equation.xyz -#define pl_facing_scale facing_scale_bias.x -#define pl_facing_bias facing_scale_bias.y -#define pl_fade_scale clip_vec_x_fade_scale.w -#define pl_fade_bias clip_vec_y_fade_bias.w -#define pl_clip_pos_x clip_vec_x_fade_scale.xyz -#define pl_clip_pos_y clip_vec_y_fade_bias.xyz -#define pl_clip_edges clip_edges - -struct GridData { - mat4 localmat; - ivec4 resolution_offset; - vec4 ws_corner_atten_scale; /* world space corner position */ - vec4 ws_increment_x_atten_bias; /* world space vector between 2 opposite cells */ - vec4 ws_increment_y_lvl_bias; - vec4 ws_increment_z; - vec4 vis_bias_bleed_range; -}; - -#define g_corner ws_corner_atten_scale.xyz -#define g_atten_scale ws_corner_atten_scale.w -#define g_atten_bias ws_increment_x_atten_bias.w -#define g_level_bias ws_increment_y_lvl_bias.w -#define g_increment_x ws_increment_x_atten_bias.xyz -#define g_increment_y ws_increment_y_lvl_bias.xyz -#define g_increment_z ws_increment_z.xyz -#define g_resolution resolution_offset.xyz -#define g_offset resolution_offset.w -#define g_vis_bias vis_bias_bleed_range.x -#define g_vis_bleed vis_bias_bleed_range.y -#define g_vis_range vis_bias_bleed_range.z - -#ifndef MAX_PROBE -# define MAX_PROBE 1 -#endif -#ifndef MAX_GRID -# define MAX_GRID 1 -#endif -#ifndef MAX_PLANAR -# define MAX_PLANAR 1 -#endif - -layout(std140) uniform probe_block -{ - CubeData probes_data[MAX_PROBE]; -}; - -layout(std140) uniform grid_block -{ - GridData grids_data[MAX_GRID]; -}; - -layout(std140) uniform planar_block -{ - PlanarData planars_data[MAX_PLANAR]; -}; - -/* ----------- Functions --------- */ - -float probe_attenuation_cube(int pd_id, vec3 P) -{ - vec3 localpos = transform_point(probes_data[pd_id].influencemat, P); - - float probe_atten_fac = probes_data[pd_id].p_atten_fac; - float fac; - if (probes_data[pd_id].p_atten_type == PROBE_ATTENUATION_BOX) { - vec3 axes_fac = saturate(probe_atten_fac - probe_atten_fac * abs(localpos)); - fac = min_v3(axes_fac); - } - else { - fac = saturate(probe_atten_fac - probe_atten_fac * length(localpos)); - } - - return fac; -} - -float probe_attenuation_planar(PlanarData pd, vec3 P) -{ - /* Distance from plane */ - float fac = saturate(abs(dot(pd.pl_plane_eq, vec4(P, 1.0))) * pd.pl_fade_scale + - pd.pl_fade_bias); - /* Fancy fast clipping calculation */ - vec2 dist_to_clip; - dist_to_clip.x = dot(pd.pl_clip_pos_x, P); - dist_to_clip.y = dot(pd.pl_clip_pos_y, P); - /* compare and add all tests */ - fac *= step(2.0, dot(step(pd.pl_clip_edges, dist_to_clip.xxyy), vec2(-1.0, 1.0).xyxy)); - return fac; -} - -float probe_attenuation_planar_normal_roughness(PlanarData pd, vec3 N, float roughness) -{ - /* Normal Facing */ - float fac = saturate(dot(pd.pl_normal, N) * pd.pl_facing_scale + pd.pl_facing_bias); - /* Decrease influence for high roughness */ - return fac * saturate(1.0 - roughness * 10.0); -} - -float probe_attenuation_grid(GridData gd, vec3 P, out vec3 localpos) -{ - localpos = transform_point(gd.localmat, P); - vec3 pos_to_edge = max(vec3(0.0), abs(localpos) - 1.0); - float fade = length(pos_to_edge); - return saturate(-fade * gd.g_atten_scale + gd.g_atten_bias); -} - -vec3 probe_evaluate_cube(int pd_id, vec3 P, vec3 R, float roughness) -{ - /* Correct reflection ray using parallax volume intersection. */ - vec3 localpos = transform_point(probes_data[pd_id].parallaxmat, P); - vec3 localray = transform_direction(probes_data[pd_id].parallaxmat, R); - - float dist; - if (probes_data[pd_id].p_parallax_type == PROBE_PARALLAX_BOX) { - dist = line_unit_box_intersect_dist(localpos, localray); - } - else { - dist = line_unit_sphere_intersect_dist(localpos, localray); - } - - /* Use Distance in WS directly to recover intersection */ - vec3 intersection = P + R * dist - probes_data[pd_id].p_position; - - /* From Frostbite PBR Course - * Distance based roughness - * http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf - */ - float original_roughness = roughness; - float linear_roughness = fast_sqrt(roughness); - float distance_roughness = saturate(dist * linear_roughness / length(intersection)); - linear_roughness = mix(distance_roughness, linear_roughness, linear_roughness); - roughness = linear_roughness * linear_roughness; - - float fac = saturate(original_roughness * 2.0 - 1.0); - R = mix(intersection, R, fac * fac); - - float lod = linear_roughness * prbLodCubeMax; - return textureLod_cubemapArray(probeCubes, vec4(R, float(pd_id)), lod).rgb; -} - -vec3 probe_evaluate_world_spec(vec3 R, float roughness) -{ - float lod = fast_sqrt(roughness) * prbLodCubeMax; - return textureLod_cubemapArray(probeCubes, vec4(R, 0.0), lod).rgb; -} - -vec3 probe_evaluate_planar(int id, PlanarData pd, vec3 P, vec3 N, vec3 V, float roughness) -{ - /* Find view vector / reflection plane intersection. */ - vec3 point_on_plane = line_plane_intersect(P, V, pd.pl_plane_eq); - - /* How far the pixel is from the plane. */ - float ref_depth = 1.0; /* TODO: parameter. */ - - /* Compute distorded reflection vector based on the distance to the reflected object. - * In other words find intersection between reflection vector and the sphere center - * around point_on_plane. */ - vec3 proj_ref = reflect(reflect(-V, N) * ref_depth, pd.pl_normal); - - /* Final point in world space. */ - vec3 ref_pos = point_on_plane + proj_ref; - - /* Reproject to find texture coords. */ - vec4 refco = ViewProjectionMatrix * vec4(ref_pos, 1.0); - refco.xy /= refco.w; - - /* TODO: If we support non-ssr planar reflection, we should blur them with gaussian - * and chose the right mip depending on the cone footprint after projection */ - /* NOTE: X is inverted here to compensate inverted drawing. */ - vec3 radiance = textureLod(probePlanars, vec3(refco.xy * vec2(-0.5, 0.5) + 0.5, id), 0.0).rgb; - - return radiance; -} - -void fallback_cubemap(vec3 N, - vec3 V, - vec3 P, - vec3 vP, - float roughness, - float roughnessSquared, - inout vec4 spec_accum) -{ - /* Specular probes */ - vec3 spec_dir = specular_dominant_dir(N, V, roughnessSquared); - - OcclusionData occlusion_data = occlusion_load(vP, 1.0); - float final_ao = specular_occlusion(occlusion_data, V, N, roughness, spec_dir); - - /* Starts at 1 because 0 is world probe */ - for (int i = 1; i < MAX_PROBE && i < prbNumRenderCube && spec_accum.a < 0.999; i++) { - float fade = probe_attenuation_cube(i, P); - - if (fade > 0.0) { - vec3 spec = final_ao * probe_evaluate_cube(i, P, spec_dir, roughness); - accumulate_light(spec, fade, spec_accum); - } - } - - /* World Specular */ - if (spec_accum.a < 0.999) { - vec3 spec = final_ao * probe_evaluate_world_spec(spec_dir, roughness); - accumulate_light(spec, 1.0, spec_accum); - } -} - -vec3 probe_evaluate_grid(GridData gd, vec3 P, vec3 N, vec3 localpos) -{ - localpos = localpos * 0.5 + 0.5; - localpos = localpos * vec3(gd.g_resolution) - 0.5; - - vec3 localpos_floored = floor(localpos); - vec3 trilinear_weight = fract(localpos); - - float weight_accum = 0.0; - vec3 irradiance_accum = vec3(0.0); - - /* For each neighbor cells */ - for (int i = 0; i < 8; i++) { - ivec3 offset = ivec3(i, i >> 1, i >> 2) & ivec3(1); - vec3 cell_cos = clamp(localpos_floored + vec3(offset), vec3(0.0), vec3(gd.g_resolution) - 1.0); - - /* Keep in sync with update_irradiance_probe */ - ivec3 icell_cos = ivec3(gd.g_level_bias * floor(cell_cos / gd.g_level_bias)); - int cell = gd.g_offset + icell_cos.z + icell_cos.y * gd.g_resolution.z + - icell_cos.x * gd.g_resolution.z * gd.g_resolution.y; - - vec3 color = irradiance_from_cell_get(cell, N); - - /* We need this because we render probes in world space (so we need light vector in WS). - * And rendering them in local probe space is too much problem. */ - vec3 ws_cell_location = gd.g_corner + - (gd.g_increment_x * cell_cos.x + gd.g_increment_y * cell_cos.y + - gd.g_increment_z * cell_cos.z); - - vec3 ws_point_to_cell = ws_cell_location - P; - float ws_dist_point_to_cell = length(ws_point_to_cell); - vec3 ws_light = ws_point_to_cell / ws_dist_point_to_cell; - - /* Smooth back-face test. */ - float weight = saturate(dot(ws_light, N)); - - /* Precomputed visibility */ - weight *= load_visibility_cell( - cell, ws_light, ws_dist_point_to_cell, gd.g_vis_bias, gd.g_vis_bleed, gd.g_vis_range); - - /* Smoother transition */ - weight += prbIrradianceSmooth; - - /* Trilinear weights */ - vec3 trilinear = mix(1.0 - trilinear_weight, trilinear_weight, offset); - weight *= trilinear.x * trilinear.y * trilinear.z; - - /* Avoid zero weight */ - weight = max(0.00001, weight); - - weight_accum += weight; - irradiance_accum += color * weight; - } - - return irradiance_accum / weight_accum; -} - -vec3 probe_evaluate_world_diff(vec3 N) -{ - if (prbNumRenderGrid == 0) { - return vec3(0); - } - return irradiance_from_cell_get(0, N); -} diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl deleted file mode 100644 index 0a53abcb04a..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl +++ /dev/null @@ -1,18 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) - -uniform sampler2DArray probePlanars; - -in vec3 worldPosition; -flat in int probeIdx; - -out vec4 FragColor; - -void main() -{ - vec4 refco = ViewProjectionMatrix * vec4(worldPosition, 1.0); - refco.xy /= refco.w; - FragColor = vec4( - textureLod(probePlanars, vec3(refco.xy * vec2(-0.5, 0.5) + 0.5, float(probeIdx)), 0.0).rgb, - 1.0); -} diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl deleted file mode 100644 index 6759c060259..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl +++ /dev/null @@ -1,17 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) - -in vec3 pos; - -in int probe_id; -in mat4 probe_mat; - -out vec3 worldPosition; -flat out int probeIdx; - -void main() -{ - worldPosition = (probe_mat * vec4(-pos.x, pos.y, 0.0, 1.0)).xyz; - gl_Position = ViewProjectionMatrix * vec4(worldPosition, 1.0); - probeIdx = probe_id; -} diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl deleted file mode 100644 index cf44a04b707..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_frag.glsl +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Simple down-sample shader. Takes the average of the 4 texels of lower mip. - */ - -#pragma BLENDER_REQUIRE(common_math_lib.glsl) - -uniform sampler2DArray source; -uniform float fireflyFactor; - -in vec2 uvs; -flat in float layer; - -out vec4 FragColor; - -void main() -{ -#if 0 - /* Reconstructing Target uvs like this avoid missing pixels if NPO2 */ - vec2 uvs = gl_FragCoord.xy * 2.0 / vec2(textureSize(source, 0)); - - FragColor = textureLod(source, vec3(uvs, layer), 0.0); -#else - vec2 texel_size = 1.0 / vec2(textureSize(source, 0)); - vec2 uvs = gl_FragCoord.xy * 2.0 * texel_size; - vec4 ofs = texel_size.xyxy * vec4(0.75, 0.75, -0.75, -0.75); - - FragColor = textureLod(source, vec3(uvs + ofs.xy, layer), 0.0); - FragColor += textureLod(source, vec3(uvs + ofs.xw, layer), 0.0); - FragColor += textureLod(source, vec3(uvs + ofs.zy, layer), 0.0); - FragColor += textureLod(source, vec3(uvs + ofs.zw, layer), 0.0); - FragColor *= 0.25; - - /* Clamped brightness. */ - float luma = max(1e-8, max_v3(FragColor.rgb)); - FragColor *= 1.0 - max(0.0, luma - fireflyFactor) / luma; -#endif -} diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_geom.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_geom.glsl deleted file mode 100644 index 54847b612e2..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_geom.glsl +++ /dev/null @@ -1,25 +0,0 @@ - -layout(triangles) in; -layout(triangle_strip, max_vertices = 3) out; - -in int instance[]; -in vec2 vPos[]; - -flat out float layer; - -void main() -{ - gl_Layer = instance[0]; - layer = float(instance[0]); - - gl_Position = vec4(vPos[0], 0.0, 1.0); - EmitVertex(); - - gl_Position = vec4(vPos[1], 0.0, 1.0); - EmitVertex(); - - gl_Position = vec4(vPos[2], 0.0, 1.0); - EmitVertex(); - - EndPrimitive(); -} diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_vert.glsl deleted file mode 100644 index 588cd402bb3..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_downsample_vert.glsl +++ /dev/null @@ -1,12 +0,0 @@ - -out int instance; -out vec2 vPos; - -void main() -{ - int v = gl_VertexID % 3; - vPos.x = -1.0 + float((v & 1) << 2); - vPos.y = -1.0 + float((v & 2) << 1); - - instance = gl_VertexID / 3; -} diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_vert.glsl deleted file mode 100644 index 8639ca6fa1b..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_vert.glsl +++ /dev/null @@ -1,11 +0,0 @@ - -in vec3 pos; - -out vec4 vPos; -flat out int face; - -void main() -{ - vPos = vec4(pos, 1.0); - face = gl_InstanceID; -} diff --git a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl deleted file mode 100644 index dc98a00a1cd..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl +++ /dev/null @@ -1,387 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_math_lib.glsl) -#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) -#pragma BLENDER_REQUIRE(raytrace_lib.glsl) -#pragma BLENDER_REQUIRE(ltc_lib.glsl) - -#ifndef MAX_CASCADE_NUM -# define MAX_CASCADE_NUM 4 -#endif - -/* ---------------------------------------------------------------------- */ -/** \name Structure - * \{ */ - -struct LightData { - vec4 position_influence; /* w : InfluenceRadius (inversed and squared) */ - vec4 color_influence_volume; /* w : InfluenceRadius but for Volume power */ - vec4 spotdata_radius_shadow; /* x : spot size, y : spot blend, z : radius, w: shadow id */ - vec4 rightvec_sizex; /* xyz: Normalized up vector, w: area size X or spot scale X */ - vec4 upvec_sizey; /* xyz: Normalized right vector, w: area size Y or spot scale Y */ - vec4 forwardvec_type; /* xyz: Normalized forward vector, w: Light Type */ - vec4 diff_spec_volume; /* xyz: Diffuse/Spec/Volume power, w: radius for volumetric. */ -}; - -/* convenience aliases */ -#define l_color color_influence_volume.rgb -#define l_diff diff_spec_volume.x -#define l_spec diff_spec_volume.y -#define l_volume diff_spec_volume.z -#define l_volume_radius diff_spec_volume.w -#define l_position position_influence.xyz -#define l_influence position_influence.w -#define l_influence_volume color_influence_volume.w -#define l_sizex rightvec_sizex.w -#define l_sizey upvec_sizey.w -#define l_right rightvec_sizex.xyz -#define l_up upvec_sizey.xyz -#define l_forward forwardvec_type.xyz -#define l_type forwardvec_type.w -#define l_spot_size spotdata_radius_shadow.x -#define l_spot_blend spotdata_radius_shadow.y -#define l_radius spotdata_radius_shadow.z -#define l_shadowid spotdata_radius_shadow.w - -struct ShadowData { - vec4 near_far_bias_id; - vec4 contact_shadow_data; -}; - -struct ShadowCubeData { - mat4 shadowmat; - vec4 position; -}; - -struct ShadowCascadeData { - mat4 shadowmat[MAX_CASCADE_NUM]; - vec4 split_start_distances; - vec4 split_end_distances; - vec4 shadow_vec_id; -}; - -/* convenience aliases */ -#define sh_near near_far_bias_id.x -#define sh_far near_far_bias_id.y -#define sh_bias near_far_bias_id.z -#define sh_data_index near_far_bias_id.w -#define sh_contact_dist contact_shadow_data.x -#define sh_contact_offset contact_shadow_data.y -#define sh_contact_spread contact_shadow_data.z -#define sh_contact_thickness contact_shadow_data.w -#define sh_shadow_vec shadow_vec_id.xyz -#define sh_tex_index shadow_vec_id.w - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Resources - * \{ */ - -layout(std140) uniform shadow_block -{ - ShadowData shadows_data[MAX_SHADOW]; - ShadowCubeData shadows_cube_data[MAX_SHADOW_CUBE]; - ShadowCascadeData shadows_cascade_data[MAX_SHADOW_CASCADE]; -}; - -layout(std140) uniform light_block -{ - LightData lights_data[MAX_LIGHT]; -}; - -uniform sampler2DArrayShadow shadowCubeTexture; -uniform sampler2DArrayShadow shadowCascadeTexture; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Shadow Functions - * \{ */ - -/* type */ -#define POINT 0.0 -#define SUN 1.0 -#define SPOT 2.0 -#define AREA_RECT 4.0 -/* Used to define the area light shape, doesn't directly correspond to a Blender light type. */ -#define AREA_ELLIPSE 100.0 - -float cubeFaceIndexEEVEE(vec3 P) -{ - vec3 aP = abs(P); - if (all(greaterThan(aP.xx, aP.yz))) { - return (P.x > 0.0) ? 0.0 : 1.0; - } - else if (all(greaterThan(aP.yy, aP.xz))) { - return (P.y > 0.0) ? 2.0 : 3.0; - } - else { - return (P.z > 0.0) ? 4.0 : 5.0; - } -} - -vec2 cubeFaceCoordEEVEE(vec3 P, float face, float scale) -{ - if (face < 2.0) { - return (P.zy / P.x) * scale * vec2(-0.5, -sign(P.x) * 0.5) + 0.5; - } - else if (face < 4.0) { - return (P.xz / P.y) * scale * vec2(sign(P.y) * 0.5, 0.5) + 0.5; - } - else { - return (P.xy / P.z) * scale * vec2(0.5, -sign(P.z) * 0.5) + 0.5; - } -} - -vec2 cubeFaceCoordEEVEE(vec3 P, float face, sampler2DArray tex) -{ - /* Scaling to compensate the 1px border around the face. */ - float cube_res = float(textureSize(tex, 0).x); - float scale = (cube_res) / (cube_res + 1.0); - return cubeFaceCoordEEVEE(P, face, scale); -} - -vec2 cubeFaceCoordEEVEE(vec3 P, float face, sampler2DArrayShadow tex) -{ - /* Scaling to compensate the 1px border around the face. */ - float cube_res = float(textureSize(tex, 0).x); - float scale = (cube_res) / (cube_res + 1.0); - return cubeFaceCoordEEVEE(P, face, scale); -} - -vec4 sample_cube(sampler2DArray tex, vec3 cubevec, float cube) -{ - /* Manual Shadow Cube Layer indexing. */ - float face = cubeFaceIndexEEVEE(cubevec); - vec2 uv = cubeFaceCoordEEVEE(cubevec, face, tex); - - vec3 coord = vec3(uv, cube * 6.0 + face); - return texture(tex, coord); -} - -vec4 sample_cascade(sampler2DArray tex, vec2 co, float cascade_id) -{ - return texture(tex, vec3(co, cascade_id)); -} - -/* Some driver poorly optimize this code. Use direct reference to matrices. */ -#define sd(x) shadows_data[x] -#define scube(x) shadows_cube_data[x] -#define scascade(x) shadows_cascade_data[x] - -float sample_cube_shadow(int shadow_id, vec3 P) -{ - int data_id = int(sd(shadow_id).sh_data_index); - vec3 cubevec = transform_point(scube(data_id).shadowmat, P); - float dist = max(sd(shadow_id).sh_near, max_v3(abs(cubevec)) - sd(shadow_id).sh_bias); - dist = buffer_depth(true, dist, sd(shadow_id).sh_far, sd(shadow_id).sh_near); - /* Manual Shadow Cube Layer indexing. */ - /* TODO: Shadow Cube Array. */ - float face = cubeFaceIndexEEVEE(cubevec); - vec2 coord = cubeFaceCoordEEVEE(cubevec, face, shadowCubeTexture); - /* tex_id == data_id for cube shadowmap */ - float tex_id = float(data_id); - return texture(shadowCubeTexture, vec4(coord, tex_id * 6.0 + face, dist)); -} - -float sample_cascade_shadow(int shadow_id, vec3 P) -{ - int data_id = int(sd(shadow_id).sh_data_index); - float tex_id = scascade(data_id).sh_tex_index; - vec4 view_z = vec4(dot(P - cameraPos, cameraForward)); - vec4 weights = 1.0 - smoothstep(scascade(data_id).split_end_distances, - scascade(data_id).split_start_distances.yzwx, - view_z); - float tot_weight = dot(weights.xyz, vec3(1.0)); - - int cascade = int(clamp(tot_weight, 0.0, 3.0)); - float blend = fract(tot_weight); - float vis = weights.w; - vec4 coord, shpos; - /* Main cascade. */ - shpos = scascade(data_id).shadowmat[cascade] * vec4(P, 1.0); - coord = vec4(shpos.xy, tex_id + float(cascade), shpos.z - sd(shadow_id).sh_bias); - vis += texture(shadowCascadeTexture, coord) * (1.0 - blend); - - cascade = min(3, cascade + 1); - /* Second cascade. */ - shpos = scascade(data_id).shadowmat[cascade] * vec4(P, 1.0); - coord = vec4(shpos.xy, tex_id + float(cascade), shpos.z - sd(shadow_id).sh_bias); - vis += texture(shadowCascadeTexture, coord) * blend; - - return saturate(vis); -} -#undef sd -#undef scube -#undef scsmd - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Light Functions - * \{ */ - -/* From Frostbite PBR Course - * Distance based attenuation - * http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf */ -float distance_attenuation(float dist_sqr, float inv_sqr_influence) -{ - float factor = dist_sqr * inv_sqr_influence; - float fac = saturate(1.0 - factor * factor); - return fac * fac; -} - -float spot_attenuation(LightData ld, vec3 l_vector) -{ - float z = dot(ld.l_forward, l_vector.xyz); - vec3 lL = l_vector.xyz / z; - float x = dot(ld.l_right, lL) / ld.l_sizex; - float y = dot(ld.l_up, lL) / ld.l_sizey; - float ellipse = inversesqrt(1.0 + x * x + y * y); - float spotmask = smoothstep(0.0, 1.0, (ellipse - ld.l_spot_size) / ld.l_spot_blend); - return spotmask; -} - -float light_attenuation(LightData ld, vec4 l_vector) -{ - float vis = 1.0; - if (ld.l_type == SPOT) { - vis *= spot_attenuation(ld, l_vector.xyz); - } - if (ld.l_type >= SPOT) { - vis *= step(0.0, -dot(l_vector.xyz, ld.l_forward)); - } - if (ld.l_type != SUN) { -#ifdef VOLUME_LIGHTING - vis *= distance_attenuation(l_vector.w * l_vector.w, ld.l_influence_volume); -#else - vis *= distance_attenuation(l_vector.w * l_vector.w, ld.l_influence); -#endif - } - return vis; -} - -float light_shadowing(LightData ld, vec3 P, float vis) -{ -#if !defined(VOLUMETRICS) || defined(VOLUME_SHADOW) - if (ld.l_shadowid >= 0.0 && vis > 0.001) { - if (ld.l_type == SUN) { - vis *= sample_cascade_shadow(int(ld.l_shadowid), P); - } - else { - vis *= sample_cube_shadow(int(ld.l_shadowid), P); - } - } -#endif - return vis; -} - -#ifndef VOLUMETRICS -float light_contact_shadows(LightData ld, vec3 P, vec3 vP, vec3 vNg, float rand_x, float vis) -{ - if (ld.l_shadowid >= 0.0 && vis > 0.001) { - ShadowData sd = shadows_data[int(ld.l_shadowid)]; - /* Only compute if not already in shadow. */ - if (sd.sh_contact_dist > 0.0) { - /* Contact Shadows. */ - Ray ray; - - if (ld.l_type == SUN) { - ray.direction = shadows_cascade_data[int(sd.sh_data_index)].sh_shadow_vec * - sd.sh_contact_dist; - } - else { - ray.direction = shadows_cube_data[int(sd.sh_data_index)].position.xyz - P; - ray.direction *= saturate(sd.sh_contact_dist * safe_rcp(length(ray.direction))); - } - - ray.direction = transform_direction(ViewMatrix, ray.direction); - ray.origin = vP + vNg * sd.sh_contact_offset; - - RayTraceParameters params; - params.thickness = sd.sh_contact_thickness; - params.jitter = rand_x; - params.trace_quality = 0.1; - params.roughness = 0.001; - - vec3 hit_position_unused; - - if (raytrace(ray, params, false, false, hit_position_unused)) { - return 0.0; - } - } - } - return 1.0; -} -#endif /* VOLUMETRICS */ - -float light_visibility(LightData ld, vec3 P, vec4 l_vector) -{ - float l_atten = light_attenuation(ld, l_vector); - return light_shadowing(ld, P, l_atten); -} - -float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector) -{ - if (ld.l_type == AREA_RECT) { - vec3 corners[4]; - corners[0] = normalize((l_vector.xyz + ld.l_right * -ld.l_sizex) + ld.l_up * ld.l_sizey); - corners[1] = normalize((l_vector.xyz + ld.l_right * -ld.l_sizex) + ld.l_up * -ld.l_sizey); - corners[2] = normalize((l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * -ld.l_sizey); - corners[3] = normalize((l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * ld.l_sizey); - - return ltc_evaluate_quad(corners, N); - } - else if (ld.l_type == AREA_ELLIPSE) { - vec3 points[3]; - points[0] = (l_vector.xyz + ld.l_right * -ld.l_sizex) + ld.l_up * -ld.l_sizey; - points[1] = (l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * -ld.l_sizey; - points[2] = (l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * ld.l_sizey; - - return ltc_evaluate_disk(N, V, mat3(1.0), points); - } - else { - float radius = ld.l_radius; - radius /= (ld.l_type == SUN) ? 1.0 : l_vector.w; - vec3 L = (ld.l_type == SUN) ? -ld.l_forward : (l_vector.xyz / l_vector.w); - - return ltc_evaluate_disk_simple(radius, dot(N, L)); - } -} - -float light_specular(LightData ld, vec4 ltc_mat, vec3 N, vec3 V, vec4 l_vector) -{ - if (ld.l_type == AREA_RECT) { - vec3 corners[4]; - corners[0] = (l_vector.xyz + ld.l_right * -ld.l_sizex) + ld.l_up * ld.l_sizey; - corners[1] = (l_vector.xyz + ld.l_right * -ld.l_sizex) + ld.l_up * -ld.l_sizey; - corners[2] = (l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * -ld.l_sizey; - corners[3] = (l_vector.xyz + ld.l_right * ld.l_sizex) + ld.l_up * ld.l_sizey; - - ltc_transform_quad(N, V, ltc_matrix(ltc_mat), corners); - - return ltc_evaluate_quad(corners, vec3(0.0, 0.0, 1.0)); - } - else { - bool is_ellipse = (ld.l_type == AREA_ELLIPSE); - float radius_x = is_ellipse ? ld.l_sizex : ld.l_radius; - float radius_y = is_ellipse ? ld.l_sizey : ld.l_radius; - - vec3 L = (ld.l_type == SUN) ? -ld.l_forward : l_vector.xyz; - vec3 Px = ld.l_right; - vec3 Py = ld.l_up; - - if (ld.l_type == SPOT || ld.l_type == POINT) { - make_orthonormal_basis(l_vector.xyz / l_vector.w, Px, Py); - } - - vec3 points[3]; - points[0] = (L + Px * -radius_x) + Py * -radius_y; - points[1] = (L + Px * radius_x) + Py * -radius_y; - points[2] = (L + Px * radius_x) + Py * radius_y; - - return ltc_evaluate_disk(N, V, ltc_matrix(ltc_mat), points); - } -} - -/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl b/source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl deleted file mode 100644 index 9077b414026..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl +++ /dev/null @@ -1,52 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_math_lib.glsl) -#pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) -#pragma BLENDER_REQUIRE(surface_lib.glsl) - -uniform sampler2D studioLight; - -uniform float backgroundAlpha; -uniform mat3 StudioLightMatrix; -uniform float studioLightIntensity = 1.0; -uniform float studioLightBlur = 0.0; - -out vec4 FragColor; - -vec3 background_transform_to_world(vec3 viewvec) -{ - vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(viewvec, 1.0) : vec4(0.0, 0.0, 1.0, 1.0); - vec4 co_homogenous = (ProjectionMatrixInverse * v); - - vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0); - return (ViewMatrixInverse * co).xyz; -} - -float hypot(float x, float y) -{ - return sqrt(x * x + y * y); -} - -vec4 node_tex_environment_equirectangular(vec3 co, sampler2D ima) -{ - vec3 nco = normalize(co); - float u = -atan(nco.y, nco.x) / (2.0 * M_PI) + 0.5; - float v = atan(nco.z, hypot(nco.x, nco.y)) / M_PI + 0.5; - return textureLod(ima, vec2(u, v), 0.0); -} - -void main() -{ - vec3 worldvec = background_transform_to_world(viewPosition); - - vec3 background_color; -#if defined(LOOKDEV_BG) - background_color = probe_evaluate_world_spec(worldvec, studioLightBlur).rgb; -#else - worldvec = StudioLightMatrix * worldvec; - background_color = node_tex_environment_equirectangular(worldvec, studioLight).rgb; - background_color *= studioLightIntensity; -#endif - - FragColor = vec4(clamp(background_color, vec3(0.0), vec3(1e10)), 1.0) * backgroundAlpha; -} diff --git a/source/blender/draw/engines/eevee/shaders/object_motion_frag.glsl b/source/blender/draw/engines/eevee/shaders/object_motion_frag.glsl deleted file mode 100644 index 66b098ef6c5..00000000000 --- a/source/blender/draw/engines/eevee/shaders/object_motion_frag.glsl +++ /dev/null @@ -1,27 +0,0 @@ - -uniform mat4 prevViewProjMatrix; -uniform mat4 currViewProjMatrix; -uniform mat4 nextViewProjMatrix; - -in vec3 prevWorldPos; -in vec3 currWorldPos; -in vec3 nextWorldPos; - -out vec4 outData; - -void main() -{ - vec4 prev_wpos = prevViewProjMatrix * vec4(prevWorldPos, 1.0); - vec4 curr_wpos = currViewProjMatrix * vec4(currWorldPos, 1.0); - vec4 next_wpos = nextViewProjMatrix * vec4(nextWorldPos, 1.0); - - vec2 prev_uv = (prev_wpos.xy / prev_wpos.w); - vec2 curr_uv = (curr_wpos.xy / curr_wpos.w); - vec2 next_uv = (next_wpos.xy / next_wpos.w); - - outData.xy = prev_uv - curr_uv; - outData.zw = next_uv - curr_uv; - - /* Encode to unsigned normalized 16bit texture. */ - outData = outData * 0.5 + 0.5; -} diff --git a/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl b/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl deleted file mode 100644 index ef96bcbaedb..00000000000 --- a/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl +++ /dev/null @@ -1,59 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(common_hair_lib.glsl) - -uniform mat4 currModelMatrix; -uniform mat4 prevModelMatrix; -uniform mat4 nextModelMatrix; -uniform bool useDeform; - -#ifdef HAIR -uniform samplerBuffer prvBuffer; /* RGBA32F */ -uniform samplerBuffer nxtBuffer; /* RGBA32F */ -#else -in vec3 pos; -in vec3 prv; /* Previous frame position. */ -in vec3 nxt; /* Next frame position. */ -#endif - -out vec3 currWorldPos; -out vec3 prevWorldPos; -out vec3 nextWorldPos; - -void main() -{ - GPU_INTEL_VERTEX_SHADER_WORKAROUND - -#ifdef HAIR - bool is_persp = (ProjectionMatrix[3][3] == 0.0); - float time, thick_time, thickness; - vec3 tan, binor; - vec3 wpos; - - hair_get_pos_tan_binor_time(is_persp, - ModelMatrixInverse, - ViewMatrixInverse[3].xyz, - ViewMatrixInverse[2].xyz, - wpos, - tan, - binor, - time, - thickness, - thick_time); - - int id = hair_get_base_id(); - vec3 pos = texelFetch(hairPointBuffer, id).point_position; - vec3 prv = texelFetch(prvBuffer, id).point_position; - vec3 nxt = texelFetch(nxtBuffer, id).point_position; -#endif - prevWorldPos = (prevModelMatrix * vec4(useDeform ? prv : pos, 1.0)).xyz; - currWorldPos = (currModelMatrix * vec4(pos, 1.0)).xyz; - nextWorldPos = (nextModelMatrix * vec4(useDeform ? nxt : pos, 1.0)).xyz; - /* Use jittered projmatrix to be able to match exact sample depth (depth equal test). - * Note that currModelMatrix needs to also be equal to ModelMatrix for the samples to match. */ -#ifndef HAIR - gl_Position = ViewProjectionMatrix * vec4(currWorldPos, 1.0); -#else - gl_Position = ViewProjectionMatrix * vec4(wpos, 1.0); -#endif -} diff --git a/source/blender/draw/engines/eevee/shaders/octahedron_lib.glsl b/source/blender/draw/engines/eevee/shaders/octahedron_lib.glsl deleted file mode 100644 index e05cc2719fa..00000000000 --- a/source/blender/draw/engines/eevee/shaders/octahedron_lib.glsl +++ /dev/null @@ -1,20 +0,0 @@ - -vec2 mapping_octahedron(vec3 cubevec, vec2 texel_size) -{ - /* projection onto octahedron */ - cubevec /= dot(vec3(1.0), abs(cubevec)); - - /* out-folding of the downward faces */ - if (cubevec.z < 0.0) { - vec2 cubevec_sign = step(0.0, cubevec.xy) * 2.0 - 1.0; - cubevec.xy = (1.0 - abs(cubevec.yx)) * cubevec_sign; - } - - /* mapping to [0;1]ˆ2 texture space */ - vec2 uvs = cubevec.xy * (0.5) + 0.5; - - /* edge filtering fix */ - uvs = (1.0 - 2.0 * texel_size) * uvs + texel_size; - - return uvs; -} diff --git a/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl b/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl deleted file mode 100644 index f650e2eeb8c..00000000000 --- a/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl +++ /dev/null @@ -1,35 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_hair_lib.glsl) -#pragma BLENDER_REQUIRE(common_view_lib.glsl) - -#ifndef HAIR_SHADER -in vec3 pos; -#endif - -void main() -{ - GPU_INTEL_VERTEX_SHADER_WORKAROUND - -#ifdef HAIR_SHADER - float time, thick_time, thickness; - vec3 worldPosition, tan, binor; - hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0), - ModelMatrixInverse, - ViewMatrixInverse[3].xyz, - ViewMatrixInverse[2].xyz, - worldPosition, - tan, - binor, - time, - thickness, - thick_time); -#else - vec3 worldPosition = point_object_to_world(pos); -#endif - - gl_Position = point_world_to_ndc(worldPosition); - -#ifdef CLIP_PLANES - gl_ClipDistance[0] = dot(vec4(worldPosition.xyz, 1.0), clipPlanes[0]); -#endif -} diff --git a/source/blender/draw/engines/eevee/shaders/random_lib.glsl b/source/blender/draw/engines/eevee/shaders/random_lib.glsl deleted file mode 100644 index c2388f61346..00000000000 --- a/source/blender/draw/engines/eevee/shaders/random_lib.glsl +++ /dev/null @@ -1,38 +0,0 @@ - -/** - * Random numbers and low discrepancy sequences utilities. - */ - -#pragma BLENDER_REQUIRE(common_math_lib.glsl) - -/* From: http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html */ -float van_der_corput_radical_inverse(uint bits) -{ - bits = (bits << 16u) | (bits >> 16u); - bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); - bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); - bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); - bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); - /* Same as dividing by 0x100000000. */ - return float(bits) * 2.3283064365386963e-10; -} - -vec2 hammersley_2d(float i, float sample_count) -{ - vec2 rand; - rand.x = i / sample_count; - rand.y = van_der_corput_radical_inverse(uint(i)); - return rand; -} - -/* This transform a 2d random sample (in [0..1] range) to a sample located on a cylinder of the - * same range. This is because the sampling functions expect such a random sample which is - * normally precomputed. */ -vec3 rand2d_to_cylinder(vec2 rand) -{ - float theta = rand.x; - float phi = (rand.y - 0.5) * M_2PI; - float cos_phi = cos(phi); - float sin_phi = sqrt(1.0 - sqr(cos_phi)) * sign(phi); - return vec3(theta, cos_phi, sin_phi); -} diff --git a/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl b/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl deleted file mode 100644 index 73c4b521b05..00000000000 --- a/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl +++ /dev/null @@ -1,228 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(common_math_lib.glsl) -#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl) - -/** - * Screen-Space Raytracing functions. - */ - -uniform sampler2D maxzBuffer; -uniform sampler2DArray planarDepth; - -struct Ray { - vec3 origin; - /* Ray direction premultiplied by its maximum length. */ - vec3 direction; -}; - -/* Inputs expected to be in viewspace. */ -void raytrace_clip_ray_to_near_plane(inout Ray ray) -{ - float near_dist = get_view_z_from_depth(0.0); - if ((ray.origin.z + ray.direction.z) > near_dist) { - ray.direction *= abs((near_dist - ray.origin.z) / ray.direction.z); - } -} - -/* Screenspace ray ([0..1] "uv" range) where direction is normalize to be as small as one - * full-resolution pixel. The ray is also clipped to all frustum sides. - */ -struct ScreenSpaceRay { - vec4 origin; - vec4 direction; - float max_time; -}; - -void raytrace_screenspace_ray_finalize(inout ScreenSpaceRay ray) -{ - /* Constant bias (due to depth buffer precision). Helps with self intersection. */ - /* Magic numbers for 24bits of precision. - * From http://terathon.com/gdc07_lengyel.pdf (slide 26) */ - const float bias = -2.4e-7 * 2.0; - ray.origin.zw += bias; - ray.direction.zw += bias; - - ray.direction -= ray.origin; - /* If the line is degenerate, make it cover at least one pixel - * to not have to handle zero-pixel extent as a special case later */ - if (len_squared(ray.direction.xy) < 0.00001) { - ray.direction.xy = vec2(0.0, 0.00001); - } - float ray_len_sqr = len_squared(ray.direction.xyz); - /* Make ray.direction cover one pixel. */ - bool is_more_vertical = abs(ray.direction.x) < abs(ray.direction.y); - ray.direction /= (is_more_vertical) ? abs(ray.direction.y) : abs(ray.direction.x); - ray.direction *= (is_more_vertical) ? ssrPixelSize.y : ssrPixelSize.x; - /* Clip to segment's end. */ - ray.max_time = sqrt(ray_len_sqr * safe_rcp(len_squared(ray.direction.xyz))); - /* Clipping to frustum sides. */ - float clip_dist = line_unit_box_intersect_dist_safe(ray.origin.xyz, ray.direction.xyz); - ray.max_time = min(ray.max_time, clip_dist); - /* Convert to texture coords [0..1] range. */ - ray.origin = ray.origin * 0.5 + 0.5; - ray.direction *= 0.5; -} - -ScreenSpaceRay raytrace_screenspace_ray_create(Ray ray) -{ - ScreenSpaceRay ssray; - ssray.origin.xyz = project_point(ProjectionMatrix, ray.origin); - ssray.direction.xyz = project_point(ProjectionMatrix, ray.origin + ray.direction); - - raytrace_screenspace_ray_finalize(ssray); - return ssray; -} - -ScreenSpaceRay raytrace_screenspace_ray_create(Ray ray, float thickness) -{ - ScreenSpaceRay ssray; - ssray.origin.xyz = project_point(ProjectionMatrix, ray.origin); - ssray.direction.xyz = project_point(ProjectionMatrix, ray.origin + ray.direction); - /* Interpolate thickness in screen space. - * Calculate thickness further away to avoid near plane clipping issues. */ - ssray.origin.w = get_depth_from_view_z(ray.origin.z - thickness) * 2.0 - 1.0; - ssray.direction.w = get_depth_from_view_z(ray.origin.z + ray.direction.z - thickness) * 2.0 - - 1.0; - - raytrace_screenspace_ray_finalize(ssray); - return ssray; -} - -struct RayTraceParameters { - /** ViewSpace thickness the objects. */ - float thickness; - /** Jitter along the ray to avoid banding artifact when steps are too large. */ - float jitter; - /** Determine how fast the sample steps are getting bigger. */ - float trace_quality; - /** Determine how we can use lower depth mipmaps to make the tracing faster. */ - float roughness; -}; - -/* Returns true on hit. */ -/* TODO(fclem): remove the back-face check and do it the SSR resolve code. */ -bool raytrace(Ray ray, - RayTraceParameters params, - const bool discard_backface, - const bool allow_self_intersection, - out vec3 hit_position) -{ - /* Clip to near plane for perspective view where there is a singularity at the camera origin. */ - if (ProjectionMatrix[3][3] == 0.0) { - raytrace_clip_ray_to_near_plane(ray); - } - - ScreenSpaceRay ssray = raytrace_screenspace_ray_create(ray, params.thickness); - /* Avoid no iteration. */ - if (!allow_self_intersection && ssray.max_time < 1.1) { - hit_position = ssray.origin.xyz + ssray.direction.xyz; - return false; - } - - ssray.max_time = max(1.1, ssray.max_time); - - float prev_delta = 0.0, prev_time = 0.0; - float depth_sample = get_depth_from_view_z(ray.origin.z); - float delta = depth_sample - ssray.origin.z; - - float lod_fac = saturate(fast_sqrt(params.roughness) * 2.0 - 0.4); - - /* Cross at least one pixel. */ - float t = 1.001, time = 1.001; - bool hit = false; - const float max_steps = 255.0; - for (float iter = 1.0; !hit && (time < ssray.max_time) && (iter < max_steps); iter++) { - float stride = 1.0 + iter * params.trace_quality; - float lod = log2(stride) * lod_fac; - - prev_time = time; - prev_delta = delta; - - time = min(t + stride * params.jitter, ssray.max_time); - t += stride; - - vec4 ss_p = ssray.origin + ssray.direction * time; - depth_sample = textureLod(maxzBuffer, ss_p.xy * hizUvScale.xy, floor(lod)).r; - - delta = depth_sample - ss_p.z; - /* Check if the ray is below the surface ... */ - hit = (delta < 0.0); - /* ... and above it with the added thickness. */ - hit = hit && (delta > ss_p.z - ss_p.w || abs(delta) < abs(ssray.direction.z * stride * 2.0)); - } - /* Discard back-face hits. */ - hit = hit && !(discard_backface && prev_delta < 0.0); - /* Reject hit if background. */ - hit = hit && (depth_sample != 1.0); - /* Refine hit using intersection between the sampled heightfield and the ray. - * This simplifies nicely to this single line. */ - time = mix(prev_time, time, saturate(prev_delta / (prev_delta - delta))); - - hit_position = ssray.origin.xyz + ssray.direction.xyz * time; - - return hit; -} - -bool raytrace_planar(Ray ray, RayTraceParameters params, int planar_ref_id, out vec3 hit_position) -{ - /* Clip to near plane for perspective view where there is a singularity at the camera origin. */ - if (ProjectionMatrix[3][3] == 0.0) { - raytrace_clip_ray_to_near_plane(ray); - } - - ScreenSpaceRay ssray = raytrace_screenspace_ray_create(ray); - - /* Planar Reflections have X mirrored. */ - ssray.origin.x = 1.0 - ssray.origin.x; - ssray.direction.x = -ssray.direction.x; - - float prev_delta = 0.0, prev_time = 0.0; - float depth_sample = texture(planarDepth, vec3(ssray.origin.xy, planar_ref_id)).r; - float delta = depth_sample - ssray.origin.z; - - float t = 0.0, time = 0.0; - /* On very sharp reflections, the ray can be perfectly aligned with the view direction - * making the tracing useless. Bypass tracing in this case. */ - bool hit = false; - const float max_steps = 255.0; - for (float iter = 1.0; !hit && (time < ssray.max_time) && (iter < max_steps); iter++) { - float stride = 1.0 + iter * params.trace_quality; - - prev_time = time; - prev_delta = delta; - - time = min(t + stride * params.jitter, ssray.max_time); - t += stride; - - vec4 ss_ray = ssray.origin + ssray.direction * time; - - depth_sample = texture(planarDepth, vec3(ss_ray.xy, planar_ref_id)).r; - - delta = depth_sample - ss_ray.z; - /* Check if the ray is below the surface. */ - hit = (delta < 0.0); - } - /* Reject hit if background. */ - hit = hit && (depth_sample != 1.0); - /* Refine hit using intersection between the sampled heightfield and the ray. - * This simplifies nicely to this single line. */ - time = mix(prev_time, time, saturate(prev_delta / (prev_delta - delta))); - - hit_position = ssray.origin.xyz + ssray.direction.xyz * time; - /* Planar Reflections have X mirrored. */ - hit_position.x = 1.0 - hit_position.x; - - return hit; -} - -float screen_border_mask(vec2 hit_co) -{ - const float margin = 0.003; - float atten = ssrBorderFac + margin; /* Screen percentage */ - hit_co = smoothstep(0.0, atten, hit_co) * (1.0 - smoothstep(1.0 - atten, 1.0, hit_co)); - - float screenfade = hit_co.x * hit_co.y; - - return screenfade; -} diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl deleted file mode 100644 index 3e0a5e76d00..00000000000 --- a/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl +++ /dev/null @@ -1,56 +0,0 @@ -#define EEVEE_AOV_HASH_COLOR_TYPE_MASK 1 - -/* ---------------------------------------------------------------------- */ -/** \name Resources - * \{ */ - -layout(std140) uniform renderpass_block -{ - bool renderPassDiffuse; - bool renderPassDiffuseLight; - bool renderPassGlossy; - bool renderPassGlossyLight; - bool renderPassEmit; - bool renderPassSSSColor; - bool renderPassEnvironment; - bool renderPassAOV; - int renderPassAOVActive; -}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Functions - * \{ */ - -vec3 render_pass_diffuse_mask(vec3 diffuse_color, vec3 diffuse_light) -{ - return renderPassDiffuse ? (renderPassDiffuseLight ? diffuse_light : diffuse_color) : vec3(0.0); -} - -vec3 render_pass_sss_mask(vec3 sss_color) -{ - return renderPassSSSColor ? sss_color : vec3(0.0); -} - -vec3 render_pass_glossy_mask(vec3 specular_color, vec3 specular_light) -{ - return renderPassGlossy ? (renderPassGlossyLight ? specular_light : specular_color) : vec3(0.0); -} - -vec3 render_pass_emission_mask(vec3 emission_light) -{ - return renderPassEmit ? emission_light : vec3(0.0); -} - -bool render_pass_aov_is_color() -{ - return (renderPassAOVActive & EEVEE_AOV_HASH_COLOR_TYPE_MASK) != 0; -} - -int render_pass_aov_hash() -{ - return renderPassAOVActive & ~EEVEE_AOV_HASH_COLOR_TYPE_MASK; -} - -/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl deleted file mode 100644 index 363b5cb978a..00000000000 --- a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl +++ /dev/null @@ -1,126 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) - -#define PASS_POST_UNDEFINED 0 -#define PASS_POST_ACCUMULATED_COLOR 1 -#define PASS_POST_ACCUMULATED_COLOR_ALPHA 2 -#define PASS_POST_ACCUMULATED_LIGHT 3 -#define PASS_POST_ACCUMULATED_VALUE 4 -#define PASS_POST_DEPTH 5 -#define PASS_POST_AO 6 -#define PASS_POST_NORMAL 7 -#define PASS_POST_TWO_LIGHT_BUFFERS 8 -#define PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR 9 - -uniform int postProcessType; -uniform int currentSample; - -uniform sampler2D depthBuffer; -uniform sampler2D inputBuffer; -uniform sampler2D inputSecondLightBuffer; -uniform sampler2D inputColorBuffer; -uniform sampler2D inputTransmittanceBuffer; - -out vec4 fragColor; - -vec3 safe_divide_even_color(vec3 a, vec3 b) -{ - vec3 result = vec3((b.r != 0.0) ? a.r / b.r : 0.0, - (b.g != 0.0) ? a.g / b.g : 0.0, - (b.b != 0.0) ? a.b / b.b : 0.0); - /* try to get gray even if b is zero */ - if (b.r == 0.0) { - if (b.g == 0.0) { - result = result.bbb; - } - else if (b.b == 0.0) { - result = result.ggg; - } - else { - result.r = 0.5 * (result.g + result.b); - } - } - else if (b.g == 0.0) { - if (b.b == 0.0) { - result = result.rrr; - } - else { - result.g = 0.5 * (result.r + result.b); - } - } - else if (b.b == 0.0) { - result.b = 0.5 * (result.r + result.g); - } - - return result; -} - -void main() -{ - vec4 color = vec4(0.0, 0.0, 0.0, 1.0); - ivec2 texel = ivec2(gl_FragCoord.xy); - - if (postProcessType == PASS_POST_DEPTH) { - float depth = texelFetch(depthBuffer, texel, 0).r; - if (depth == 1.0f) { - depth = 1e10; - } - else { - depth = -get_view_z_from_depth(depth); - } - color.rgb = vec3(depth); - } - else if (postProcessType == PASS_POST_AO) { - float ao_accum = texelFetch(inputBuffer, texel, 0).r; - color.rgb = vec3(min(1.0, ao_accum / currentSample)); - } - else if (postProcessType == PASS_POST_NORMAL) { - float depth = texelFetch(depthBuffer, texel, 0).r; - vec2 encoded_normal = texelFetch(inputBuffer, texel, 0).rg; - /* decode the normals only when they are valid. otherwise the result buffer will be filled - * with NaN's */ - if (depth != 1.0 && any(notEqual(encoded_normal, vec2(0.0)))) { - vec3 decoded_normal = normal_decode(texelFetch(inputBuffer, texel, 0).rg, vec3(0.0)); - vec3 world_normal = transform_direction(ViewMatrixInverse, decoded_normal); - color.rgb = world_normal; - } - else { - color.rgb = vec3(0.0); - } - } - else if (postProcessType == PASS_POST_ACCUMULATED_VALUE) { - float accumulated_value = texelFetch(inputBuffer, texel, 0).r; - color.rgb = vec3(accumulated_value / currentSample); - } - else if (postProcessType == PASS_POST_ACCUMULATED_COLOR) { - vec3 accumulated_color = texelFetch(inputBuffer, texel, 0).rgb; - color.rgb = (accumulated_color / currentSample); - } - else if (postProcessType == PASS_POST_ACCUMULATED_COLOR_ALPHA) { - vec4 accumulated_color = texelFetch(inputBuffer, texel, 0); - color = (accumulated_color / currentSample); - } - else if (postProcessType == PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR) { - vec3 accumulated_color = texelFetch(inputBuffer, texel, 0).rgb; - vec3 transmittance = texelFetch(inputTransmittanceBuffer, texel, 0).rgb; - color.rgb = (accumulated_color / currentSample) * (transmittance / currentSample); - } - else if (postProcessType == PASS_POST_ACCUMULATED_LIGHT) { - vec3 accumulated_light = texelFetch(inputBuffer, texel, 0).rgb; - vec3 accumulated_color = texelFetch(inputColorBuffer, texel, 0).rgb; - color.rgb = safe_divide_even_color(accumulated_light, accumulated_color); - } - else if (postProcessType == PASS_POST_TWO_LIGHT_BUFFERS) { - vec3 accumulated_light = texelFetch(inputBuffer, texel, 0).rgb + - texelFetch(inputSecondLightBuffer, texel, 0).rgb; - vec3 accumulated_color = texelFetch(inputColorBuffer, texel, 0).rgb; - color.rgb = safe_divide_even_color(accumulated_light, accumulated_color); - } - else { - /* Output error color: Unknown how to post process this pass. */ - color.rgb = vec3(1.0, 0.0, 1.0); - } - - fragColor = color; -} diff --git a/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl b/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl deleted file mode 100644 index ad0d682dcf4..00000000000 --- a/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl +++ /dev/null @@ -1,52 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_math_lib.glsl) -#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) -#pragma BLENDER_REQUIRE(lights_lib.glsl) - -uniform sampler2D depthBuffer; - -out vec4 fragColor; - -void main() -{ - if (laNumLight == 0) { - /* Early exit: No lights in scene */ - fragColor.r = 1.0; - return; - } - - ivec2 texel = ivec2(gl_FragCoord.xy); - float depth = texelFetch(depthBuffer, texel, 0).r; - if (depth == 1.0f) { - /* Early exit background does not receive shadows */ - fragColor.r = 1.0; - return; - } - - vec2 texel_size = 1.0 / vec2(textureSize(depthBuffer, 0)).xy; - vec2 uvs = saturate(gl_FragCoord.xy * texel_size); - vec4 rand = texelfetch_noise_tex(texel); - - float accum_light = 0.0; - - vec3 vP = get_view_space_from_depth(uvs, depth); - vec3 P = transform_point(ViewMatrixInverse, vP); - - vec3 vNg = safe_normalize(cross(dFdx(vP), dFdy(vP))); - - for (int i = 0; i < MAX_LIGHT && i < laNumLight; i++) { - LightData ld = lights_data[i]; - - vec4 l_vector; /* Non-Normalized Light Vector with length in last component. */ - l_vector.xyz = ld.l_position - P; - l_vector.w = length(l_vector.xyz); - - float l_vis = light_shadowing(ld, P, 1.0); - - l_vis *= light_contact_shadows(ld, P, vP, vNg, rand.x, 1.0); - - accum_light += l_vis; - } - - fragColor.r = accum_light / float(laNumLight); -} diff --git a/source/blender/draw/engines/eevee/shaders/shadow_frag.glsl b/source/blender/draw/engines/eevee/shaders/shadow_frag.glsl deleted file mode 100644 index a394ce2b6f4..00000000000 --- a/source/blender/draw/engines/eevee/shaders/shadow_frag.glsl +++ /dev/null @@ -1,5 +0,0 @@ - -void main() -{ - /* Do nothing */ -} diff --git a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl deleted file mode 100644 index cbfa9737a84..00000000000 --- a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl +++ /dev/null @@ -1,54 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(common_hair_lib.glsl) -#pragma BLENDER_REQUIRE(surface_lib.glsl) - -in vec3 pos; -in vec3 nor; - -void main() -{ - GPU_INTEL_VERTEX_SHADER_WORKAROUND - -#ifdef HAIR_SHADER - hairStrandID = hair_get_strand_id(); - vec3 pos, binor; - hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0), - ModelMatrixInverse, - ViewMatrixInverse[3].xyz, - ViewMatrixInverse[2].xyz, - pos, - hairTangent, - binor, - hairTime, - hairThickness, - hairThickTime); - - worldNormal = cross(hairTangent, binor); - vec3 world_pos = pos; -#elif defined(POINTCLOUD_SHADER) - pointcloud_get_pos_and_radius(pointPosition, pointRadius); - pointID = gl_VertexID; -#else - vec3 world_pos = point_object_to_world(pos); -#endif - - gl_Position = point_world_to_ndc(world_pos); -#ifdef MESH_SHADER - worldPosition = world_pos; - viewPosition = point_world_to_view(worldPosition); - -# ifndef HAIR_SHADER - worldNormal = normalize(normal_object_to_world(nor)); -# endif - - /* No need to normalize since this is just a rotation. */ - viewNormal = normal_world_to_view(worldNormal); -# ifdef USE_ATTR -# ifdef HAIR_SHADER - pos = hair_get_strand_pos(); -# endif - pass_attr(pos, NormalMatrix, ModelMatrixInverse); -# endif -#endif -} diff --git a/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl b/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl deleted file mode 100644 index a563291bb90..00000000000 --- a/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl +++ /dev/null @@ -1,91 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) -#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl) -#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) -#pragma BLENDER_REQUIRE(raytrace_lib.glsl) -#pragma BLENDER_REQUIRE(surface_lib.glsl) - -/* ------------ Refraction ------------ */ - -#define BTDF_BIAS 0.85 - -uniform sampler2D refractColorBuffer; - -uniform float refractionDepth; - -vec4 screen_space_refraction(vec3 vP, vec3 N, vec3 V, float ior, float roughnessSquared, vec4 rand) -{ - float alpha = max(0.002, roughnessSquared); - - /* Importance sampling bias */ - rand.x = mix(rand.x, 0.0, BTDF_BIAS); - - vec3 T, B; - make_orthonormal_basis(N, T, B); - float pdf; - /* Microfacet normal */ - vec3 H = sample_ggx(rand.xzw, alpha, V, N, T, B, pdf); - - /* If ray is bad (i.e. going below the plane) regenerate. */ - if (F_eta(ior, dot(H, V)) < 1.0) { - H = sample_ggx(rand.xzw * vec3(1.0, -1.0, -1.0), alpha, V, N, T, B, pdf); - } - - vec3 vV = viewCameraVec(vP); - float eta = 1.0 / ior; - if (dot(H, V) < 0.0) { - H = -H; - eta = ior; - } - - vec3 R = refract(-V, H, 1.0 / ior); - - R = transform_direction(ViewMatrix, R); - - Ray ray; - ray.origin = vP; - ray.direction = R * 1e16; - - RayTraceParameters params; - params.thickness = ssrThickness; - params.jitter = rand.y; - params.trace_quality = ssrQuality; - params.roughness = roughnessSquared; - - vec3 hit_pos; - bool hit = raytrace(ray, params, false, true, hit_pos); - - if (hit && (F_eta(ior, dot(H, V)) < 1.0)) { - hit_pos = get_view_space_from_depth(hit_pos.xy, hit_pos.z); - float hit_dist = distance(hit_pos, vP); - - float cone_cos = cone_cosine(roughnessSquared); - float cone_tan = sqrt(1 - cone_cos * cone_cos) / cone_cos; - - /* Empirical fit for refraction. */ - /* TODO: find a better fit or precompute inside the LUT. */ - cone_tan *= 0.5 * fast_sqrt(f0_from_ior((ior < 1.0) ? 1.0 / ior : ior)); - - float cone_footprint = hit_dist * cone_tan; - - /* find the offset in screen space by multiplying a point - * in camera space at the depth of the point by the projection matrix. */ - float homcoord = ProjectionMatrix[2][3] * hit_pos.z + ProjectionMatrix[3][3]; - /* UV space footprint */ - cone_footprint = BTDF_BIAS * 0.5 * max(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) * - cone_footprint / homcoord; - - vec2 hit_uvs = project_point(ProjectionMatrix, hit_pos).xy * 0.5 + 0.5; - - /* Texel footprint */ - vec2 texture_size = vec2(textureSize(refractColorBuffer, 0).xy) / hizUvScale.xy; - float mip = clamp(log2(cone_footprint * max(texture_size.x, texture_size.y)), 0.0, 9.0); - - vec3 spec = textureLod(refractColorBuffer, hit_uvs * hizUvScale.xy, mip).xyz; - float mask = screen_border_mask(hit_uvs); - - return vec4(spec, mask); - } - - return vec4(0.0); -} diff --git a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl deleted file mode 100644 index 889bf439d5f..00000000000 --- a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl +++ /dev/null @@ -1,94 +0,0 @@ - -/* Required by some nodes. */ -#pragma BLENDER_REQUIRE(common_hair_lib.glsl) -#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) - -#pragma BLENDER_REQUIRE(closure_type_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_diffuse_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_translucent_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_refraction_lib.glsl) - -#pragma BLENDER_REQUIRE(surface_lib.glsl) -#pragma BLENDER_REQUIRE(volumetric_lib.glsl) - -#ifdef USE_ALPHA_BLEND -/* Use dual source blending to be able to make a whole range of effects. */ -layout(location = 0, index = 0) out vec4 outRadiance; -layout(location = 0, index = 1) out vec4 outTransmittance; - -#else /* OPAQUE */ -layout(location = 0) out vec4 outRadiance; -layout(location = 1) out vec2 ssrNormals; -layout(location = 2) out vec4 ssrData; -# ifdef USE_SSS -layout(location = 3) out vec3 sssIrradiance; -layout(location = 4) out float sssRadius; -layout(location = 5) out vec3 sssAlbedo; -# endif - -#endif - -void main() -{ - Closure cl = nodetree_exec(); - - float holdout = saturate(1.0 - cl.holdout); - float transmit = saturate(avg(cl.transmittance)); - float alpha = 1.0 - transmit; - -#ifdef USE_ALPHA_BLEND - vec2 uvs = gl_FragCoord.xy * volCoordScale.zw; - vec3 vol_transmit, vol_scatter; - volumetric_resolve(uvs, gl_FragCoord.z, vol_transmit, vol_scatter); - - /* Removes part of the volume scattering that have - * already been added to the destination pixels. - * Since we do that using the blending pipeline we need to account for material transmittance. */ - vol_scatter -= vol_scatter * cl.transmittance; - - cl.radiance = cl.radiance * holdout * vol_transmit + vol_scatter; - outRadiance = vec4(cl.radiance, alpha * holdout); - outTransmittance = vec4(cl.transmittance, transmit) * holdout; -#else - outRadiance = vec4(cl.radiance, holdout); - ssrNormals = cl.ssr_normal; - ssrData = cl.ssr_data; -# ifdef USE_SSS - sssIrradiance = cl.sss_irradiance; - sssRadius = cl.sss_radius; - sssAlbedo = cl.sss_albedo; -# endif -#endif - - /* For Probe capture */ -#ifdef USE_SSS - float fac = float(!sssToggle); - - /* TODO(fclem): we shouldn't need this. - * Just disable USE_SSS when USE_REFRACTION is enabled. */ -# ifdef USE_REFRACTION - /* SSRefraction pass is done after the SSS pass. - * In order to not lose the diffuse light totally we - * need to merge the SSS radiance to the main radiance. */ - fac = 1.0; -# endif - - outRadiance.rgb += cl.sss_irradiance.rgb * cl.sss_albedo.rgb * fac; -#endif - -#ifndef USE_ALPHA_BLEND - float alpha_div = safe_rcp(alpha); - outRadiance.rgb *= alpha_div; - ssrData.rgb *= alpha_div; -# ifdef USE_SSS - sssAlbedo.rgb *= alpha_div; -# endif -#endif - -#ifdef LOOKDEV - /* Lookdev spheres are rendered in front. */ - gl_FragDepth = 0.0; -#endif -} diff --git a/source/blender/draw/engines/eevee/shaders/surface_geom.glsl b/source/blender/draw/engines/eevee/shaders/surface_geom.glsl deleted file mode 100644 index ad437dca79a..00000000000 --- a/source/blender/draw/engines/eevee/shaders/surface_geom.glsl +++ /dev/null @@ -1,46 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(surface_lib.glsl) - -layout(triangles) in; -layout(triangle_strip, max_vertices = 3) out; - -RESOURCE_ID_VARYING - -/* Only used to compute barycentric coordinates. */ - -void main() -{ -#ifdef DO_BARYCENTRIC_DISTANCES - dataAttrOut.barycentricDist = calc_barycentric_distances( - dataIn[0].worldPosition, dataIn[1].worldPosition, dataIn[2].worldPosition); -#endif - - PASS_RESOURCE_ID - -#ifdef USE_ATTR - pass_attr(0); -#endif - PASS_SURFACE_INTERFACE(0); - gl_Position = gl_in[0].gl_Position; - gl_ClipDistance[0] = gl_in[0].gl_ClipDistance[0]; - EmitVertex(); - -#ifdef USE_ATTR - pass_attr(1); -#endif - PASS_SURFACE_INTERFACE(1); - gl_Position = gl_in[1].gl_Position; - gl_ClipDistance[0] = gl_in[1].gl_ClipDistance[0]; - EmitVertex(); - -#ifdef USE_ATTR - pass_attr(2); -#endif - PASS_SURFACE_INTERFACE(2); - gl_Position = gl_in[2].gl_Position; - gl_ClipDistance[0] = gl_in[2].gl_ClipDistance[0]; - EmitVertex(); - - EndPrimitive(); -} diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl deleted file mode 100644 index d7fc5e0b52a..00000000000 --- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl +++ /dev/null @@ -1,54 +0,0 @@ -/** This describe the entire interface of the shader. */ - -#define SURFACE_INTERFACE \ - vec3 worldPosition; \ - vec3 viewPosition; \ - vec3 worldNormal; \ - vec3 viewNormal; - -#if defined(STEP_RESOLVE) || defined(STEP_RAYTRACE) -/* SSR will set these global variables itself. - * Also make false positive compiler warnings disappear by setting values. */ -vec3 worldPosition = vec3(0); -vec3 viewPosition = vec3(0); -vec3 worldNormal = vec3(0); -vec3 viewNormal = vec3(0); - -#elif defined(GPU_GEOMETRY_SHADER) -in ShaderStageInterface{SURFACE_INTERFACE} dataIn[]; - -out ShaderStageInterface{SURFACE_INTERFACE} dataOut; - -# define PASS_SURFACE_INTERFACE(vert) \ - dataOut.worldPosition = dataIn[vert].worldPosition; \ - dataOut.viewPosition = dataIn[vert].viewPosition; \ - dataOut.worldNormal = dataIn[vert].worldNormal; \ - dataOut.viewNormal = dataIn[vert].viewNormal; - -#else /* GPU_VERTEX_SHADER || GPU_FRAGMENT_SHADER*/ - -IN_OUT ShaderStageInterface{SURFACE_INTERFACE}; - -#endif - -#ifdef HAIR_SHADER -IN_OUT ShaderHairInterface -{ - /* world space */ - vec3 hairTangent; - float hairThickTime; - float hairThickness; - float hairTime; - flat int hairStrandID; -}; -#endif - -#ifdef POINTCLOUD_SHADER -IN_OUT ShaderPointCloudInterface -{ - /* world space */ - float pointRadius; - float pointPosition; - flat int pointID; -}; -#endif diff --git a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl deleted file mode 100644 index 51e9eda6cc2..00000000000 --- a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl +++ /dev/null @@ -1,63 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_hair_lib.glsl) -#pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(surface_lib.glsl) - -#ifndef HAIR_SHADER -in vec3 pos; -in vec3 nor; -#endif - -RESOURCE_ID_VARYING - -void main() -{ - GPU_INTEL_VERTEX_SHADER_WORKAROUND - - PASS_RESOURCE_ID - -#ifdef HAIR_SHADER - hairStrandID = hair_get_strand_id(); - vec3 pos, binor; - hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0), - ModelMatrixInverse, - ViewMatrixInverse[3].xyz, - ViewMatrixInverse[2].xyz, - pos, - hairTangent, - binor, - hairTime, - hairThickness, - hairThickTime); - worldNormal = cross(hairTangent, binor); - vec3 world_pos = pos; -#elif defined(POINTCLOUD_SHADER) - pointcloud_get_pos_and_radius(pointPosition, pointRadius); - pointID = gl_VertexID; -#else - vec3 world_pos = point_object_to_world(pos); -#endif - - gl_Position = point_world_to_ndc(world_pos); - - /* Used for planar reflections */ - gl_ClipDistance[0] = dot(vec4(world_pos, 1.0), clipPlanes[0]); - -#ifdef MESH_SHADER - worldPosition = world_pos; - viewPosition = point_world_to_view(worldPosition); - -# ifndef HAIR_SHADER - worldNormal = normalize(normal_object_to_world(nor)); -# endif - - /* No need to normalize since this is just a rotation. */ - viewNormal = normal_world_to_view(worldNormal); -# ifdef USE_ATTR -# ifdef HAIR_SHADER - pos = hair_get_strand_pos(); -# endif - pass_attr(pos, NormalMatrix, ModelMatrixInverse); -# endif -#endif -} diff --git a/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl b/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl deleted file mode 100644 index 0c01c46c2ba..00000000000 --- a/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl +++ /dev/null @@ -1,18 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_math_lib.glsl) - -uniform sampler2D blueNoise; -uniform vec3 offsets; - -out vec4 FragColor; - -void main(void) -{ - vec3 blue_noise = texelFetch(blueNoise, ivec2(gl_FragCoord.xy), 0).xyz; - - float noise = fract(blue_noise.y + offsets.z); - FragColor.x = fract(blue_noise.x + offsets.x); - FragColor.y = fract(blue_noise.z + offsets.y); - FragColor.z = cos(noise * M_2PI); - FragColor.w = sin(noise * M_2PI); -} diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_accum_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_accum_frag.glsl deleted file mode 100644 index d8ce7905a1a..00000000000 --- a/source/blender/draw/engines/eevee/shaders/volumetric_accum_frag.glsl +++ /dev/null @@ -1,11 +0,0 @@ - -/* This shader is used to add default values to the volume accum textures. - * so it looks similar (transmittance = 1, scattering = 0) */ -layout(location = 0) out vec4 FragColor0; -layout(location = 1) out vec4 FragColor1; - -void main() -{ - FragColor0 = vec4(0.0); - FragColor1 = vec4(1.0); -} diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl deleted file mode 100644 index 25661a0d731..00000000000 --- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl +++ /dev/null @@ -1,74 +0,0 @@ - -#pragma BLENDER_REQUIRE(volumetric_lib.glsl) -#pragma BLENDER_REQUIRE(closure_type_lib.glsl) - -/* Based on Frosbite Unified Volumetric. - * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ - -#ifdef MESH_SHADER -uniform vec3 volumeOrcoLoc; -uniform vec3 volumeOrcoSize; -uniform mat4 volumeObjectToTexture; -uniform float volumeDensityScale = 1.0; -#endif - -flat in int slice; - -/* Warning: these are not attributes, these are global vars. */ -vec3 worldPosition = vec3(0.0); -vec3 viewPosition = vec3(0.0); -vec3 viewNormal = vec3(0.0); -#ifdef MESH_SHADER -vec3 volumeObjectLocalCoord = vec3(0.0); -#endif - -layout(location = 0) out vec4 volumeScattering; -layout(location = 1) out vec4 volumeExtinction; -layout(location = 2) out vec4 volumeEmissive; -layout(location = 3) out vec4 volumePhase; - -/* Store volumetric properties into the froxel textures. */ - -void main() -{ - ivec3 volume_cell = ivec3(gl_FragCoord.xy, slice); - vec3 ndc_cell = volume_to_ndc((vec3(volume_cell) + volJitter.xyz) * volInvTexSize.xyz); - - viewPosition = get_view_space_from_depth(ndc_cell.xy, ndc_cell.z); - worldPosition = point_view_to_world(viewPosition); -#ifdef MESH_SHADER - volumeObjectLocalCoord = point_world_to_object(worldPosition); - /* TODO: redundant transform */ - volumeObjectLocalCoord = (volumeObjectLocalCoord - volumeOrcoLoc + volumeOrcoSize) / - (volumeOrcoSize * 2.0); - volumeObjectLocalCoord = (volumeObjectToTexture * vec4(volumeObjectLocalCoord, 1.0)).xyz; - - if (any(lessThan(volumeObjectLocalCoord, vec3(0.0))) || - any(greaterThan(volumeObjectLocalCoord, vec3(1.0)))) - discard; -#endif - -#ifdef CLEAR - Closure cl = CLOSURE_DEFAULT; -#else - Closure cl = nodetree_exec(); -#endif - -#ifdef MESH_SHADER - cl.scatter *= volumeDensityScale; - cl.absorption *= volumeDensityScale; - cl.emission *= volumeDensityScale; -#endif - - volumeScattering = vec4(cl.scatter, 1.0); - volumeExtinction = vec4(cl.absorption + cl.scatter, 1.0); - volumeEmissive = vec4(cl.emission, 1.0); - - /* Do not add phase weight if no scattering. */ - if (all(equal(cl.scatter, vec3(0.0)))) { - volumePhase = vec4(0.0); - } - else { - volumePhase = vec4(cl.anisotropy, vec3(1.0)); - } -} diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl deleted file mode 100644 index 5226da57a06..00000000000 --- a/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl +++ /dev/null @@ -1,80 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) - -#ifdef MESH_SHADER -/* TODO: tight slices. */ -layout(triangles) in; -layout(triangle_strip, max_vertices = 3) out; -#else /* World */ -layout(triangles) in; -layout(triangle_strip, max_vertices = 3) out; -#endif - -in vec4 vPos[]; - -flat out int slice; - -RESOURCE_ID_VARYING - -#ifdef MESH_SHADER -/* TODO: tight slices. */ -void main() -{ - gl_Layer = slice = int(vPos[0].z); - - PASS_RESOURCE_ID - -# ifdef USE_ATTR - pass_attr(0); -# endif - gl_Position = vPos[0].xyww; - EmitVertex(); - -# ifdef USE_ATTR - pass_attr(1); -# endif - gl_Position = vPos[1].xyww; - EmitVertex(); - -# ifdef USE_ATTR - pass_attr(2); -# endif - gl_Position = vPos[2].xyww; - EmitVertex(); - - EndPrimitive(); -} - -#else /* World */ - -/* This is just a pass-through geometry shader that send the geometry - * to the layer corresponding to its depth. */ - -void main() -{ - gl_Layer = slice = int(vPos[0].z); - - PASS_RESOURCE_ID - -# ifdef USE_ATTR - pass_attr(0); -# endif - gl_Position = vPos[0].xyww; - EmitVertex(); - -# ifdef USE_ATTR - pass_attr(1); -# endif - gl_Position = vPos[1].xyww; - EmitVertex(); - -# ifdef USE_ATTR - pass_attr(2); -# endif - gl_Position = vPos[2].xyww; - EmitVertex(); - - EndPrimitive(); -} - -#endif diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl deleted file mode 100644 index 12b7d8acbea..00000000000 --- a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl +++ /dev/null @@ -1,85 +0,0 @@ - -#pragma BLENDER_REQUIRE(volumetric_lib.glsl) - -/* Based on Frosbite Unified Volumetric. - * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ - -/* Step 3 : Integrate for each froxel the final amount of light - * scattered back to the viewer and the amount of transmittance. */ - -uniform sampler3D volumeScattering; /* Result of the scatter step */ -uniform sampler3D volumeExtinction; - -#ifdef USE_VOLUME_OPTI -uniform layout(r11f_g11f_b10f) writeonly restrict image3D finalScattering_img; -uniform layout(r11f_g11f_b10f) writeonly restrict image3D finalTransmittance_img; - -vec3 finalScattering; -vec3 finalTransmittance; -#else - -flat in int slice; - -layout(location = 0) out vec3 finalScattering; -layout(location = 1) out vec3 finalTransmittance; -#endif - -void main() -{ - /* Start with full transmittance and no scattered light. */ - finalScattering = vec3(0.0); - finalTransmittance = vec3(1.0); - - vec3 tex_size = vec3(textureSize(volumeScattering, 0).xyz); - - /* Compute view ray. */ - vec2 uvs = gl_FragCoord.xy / tex_size.xy; - vec3 ndc_cell = volume_to_ndc(vec3(uvs, 1e-5)); - vec3 view_cell = get_view_space_from_depth(ndc_cell.xy, ndc_cell.z); - - /* Ortho */ - float prev_ray_len = view_cell.z; - float orig_ray_len = 1.0; - - /* Persp */ - if (ProjectionMatrix[3][3] == 0.0) { - prev_ray_len = length(view_cell); - orig_ray_len = prev_ray_len / view_cell.z; - } - -#ifdef USE_VOLUME_OPTI - int slice = textureSize(volumeScattering, 0).z; - ivec2 texco = ivec2(gl_FragCoord.xy); -#endif - for (int i = 0; i <= slice; i++) { - ivec3 volume_cell = ivec3(gl_FragCoord.xy, i); - - vec3 Lscat = texelFetch(volumeScattering, volume_cell, 0).rgb; - vec3 s_extinction = texelFetch(volumeExtinction, volume_cell, 0).rgb; - - float cell_depth = volume_z_to_view_z((float(i) + 1.0) / tex_size.z); - float ray_len = orig_ray_len * cell_depth; - - /* Emission does not work of there is no extinction because - * Tr evaluates to 1.0 leading to Lscat = 0.0. (See T65771) */ - s_extinction = max(vec3(1e-7) * step(1e-5, Lscat), s_extinction); - - /* Evaluate Scattering */ - float s_len = abs(ray_len - prev_ray_len); - prev_ray_len = ray_len; - vec3 Tr = exp(-s_extinction * s_len); - - /* integrate along the current step segment */ - Lscat = (Lscat - Lscat * Tr) / max(vec3(1e-8), s_extinction); - /* accumulate and also take into account the transmittance from previous steps */ - finalScattering += finalTransmittance * Lscat; - - finalTransmittance *= Tr; - -#ifdef USE_VOLUME_OPTI - ivec3 coord = ivec3(texco, i); - imageStore(finalScattering_img, coord, vec4(finalScattering, 0.0)); - imageStore(finalTransmittance_img, coord, vec4(finalTransmittance, 0.0)); -#endif - } -} diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl deleted file mode 100644 index 777e48fde34..00000000000 --- a/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl +++ /dev/null @@ -1,198 +0,0 @@ - -#pragma BLENDER_REQUIRE(lights_lib.glsl) -#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) -#pragma BLENDER_REQUIRE(irradiance_lib.glsl) - -/* Based on Frosbite Unified Volumetric. - * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ - -/* Volume slice to view space depth. */ -float volume_z_to_view_z(float z) -{ - if (ProjectionMatrix[3][3] == 0.0) { - /* Exponential distribution */ - return (exp2(z / volDepthParameters.z) - volDepthParameters.x) / volDepthParameters.y; - } - else { - /* Linear distribution */ - return mix(volDepthParameters.x, volDepthParameters.y, z); - } -} - -float view_z_to_volume_z(float depth) -{ - if (ProjectionMatrix[3][3] == 0.0) { - /* Exponential distribution */ - return volDepthParameters.z * log2(depth * volDepthParameters.y + volDepthParameters.x); - } - else { - /* Linear distribution */ - return (depth - volDepthParameters.x) * volDepthParameters.z; - } -} - -/* Volume texture normalized coordinates to NDC (special range [0, 1]). */ -vec3 volume_to_ndc(vec3 cos) -{ - cos.z = volume_z_to_view_z(cos.z); - cos.z = get_depth_from_view_z(cos.z); - cos.xy /= volCoordScale.xy; - return cos; -} - -vec3 ndc_to_volume(vec3 cos) -{ - cos.z = get_view_z_from_depth(cos.z); - cos.z = view_z_to_volume_z(cos.z); - cos.xy *= volCoordScale.xy; - return cos; -} - -float phase_function_isotropic() -{ - return 1.0 / (4.0 * M_PI); -} - -float phase_function(vec3 v, vec3 l, float g) -{ - /* Henyey-Greenstein */ - float cos_theta = dot(v, l); - g = clamp(g, -1.0 + 1e-3, 1.0 - 1e-3); - float sqr_g = g * g; - return (1 - sqr_g) / max(1e-8, 4.0 * M_PI * pow(1 + sqr_g - 2 * g * cos_theta, 3.0 / 2.0)); -} - -vec3 light_volume(LightData ld, vec4 l_vector) -{ - float power = 1.0; - if (ld.l_type != SUN) { - /** - * Using "Point Light Attenuation Without Singularity" from Cem Yuksel - * http://www.cemyuksel.com/research/pointlightattenuation/pointlightattenuation.pdf - * http://www.cemyuksel.com/research/pointlightattenuation/ - */ - float d = l_vector.w; - float d_sqr = sqr(d); - float r_sqr = ld.l_volume_radius; - /* Using reformulation that has better numerical precision. */ - power = 2.0 / (d_sqr + r_sqr + d * sqrt(d_sqr + r_sqr)); - - if (ld.l_type == AREA_RECT || ld.l_type == AREA_ELLIPSE) { - /* Modulate by light plane orientation / solid angle. */ - power *= saturate(dot(-ld.l_forward, l_vector.xyz / l_vector.w)); - } - } - return ld.l_color * ld.l_volume * power; -} - -vec3 light_volume_light_vector(LightData ld, vec3 P) -{ - if (ld.l_type == SUN) { - return -ld.l_forward; - } - else if (ld.l_type == AREA_RECT || ld.l_type == AREA_ELLIPSE) { - vec3 L = P - ld.l_position; - vec2 closest_point = vec2(dot(ld.l_right, L), dot(ld.l_up, L)); - vec2 max_pos = vec2(ld.l_sizex, ld.l_sizey); - closest_point /= max_pos; - - if (ld.l_type == AREA_ELLIPSE) { - closest_point /= max(1.0, length(closest_point)); - } - else { - closest_point = clamp(closest_point, -1.0, 1.0); - } - closest_point *= max_pos; - - vec3 L_prime = ld.l_right * closest_point.x + ld.l_up * closest_point.y; - return L_prime - L; - } - else { - return ld.l_position - P; - } -} - -#define VOLUMETRIC_SHADOW_MAX_STEP 128.0 - -vec3 participating_media_extinction(vec3 wpos, sampler3D volume_extinction) -{ - /* Waiting for proper volume shadowmaps and out of frustum shadow map. */ - vec3 ndc = project_point(ViewProjectionMatrix, wpos); - vec3 volume_co = ndc_to_volume(ndc * 0.5 + 0.5); - - /* Let the texture be clamped to edge. This reduce visual glitches. */ - return texture(volume_extinction, volume_co).rgb; -} - -vec3 light_volume_shadow(LightData ld, vec3 ray_wpos, vec4 l_vector, sampler3D volume_extinction) -{ -#if defined(VOLUME_SHADOW) - /* If light is shadowed, use the shadow vector, if not, reuse the light vector. */ - if (volUseSoftShadows && ld.l_shadowid >= 0.0) { - ShadowData sd = shadows_data[int(ld.l_shadowid)]; - - if (ld.l_type == SUN) { - l_vector.xyz = shadows_cascade_data[int(sd.sh_data_index)].sh_shadow_vec; - /* No need for length, it is recomputed later. */ - } - else { - l_vector.xyz = shadows_cube_data[int(sd.sh_data_index)].position.xyz - ray_wpos; - l_vector.w = length(l_vector.xyz); - } - } - - /* Heterogeneous volume shadows */ - float dd = l_vector.w / volShadowSteps; - vec3 L = l_vector.xyz / volShadowSteps; - - if (ld.l_type == SUN) { - /* For sun light we scan the whole frustum. So we need to get the correct endpoints. */ - vec3 ndcP = project_point(ViewProjectionMatrix, ray_wpos); - vec3 ndcL = project_point(ViewProjectionMatrix, ray_wpos + l_vector.xyz) - ndcP; - - vec3 frustum_isect = ndcP + ndcL * line_unit_box_intersect_dist_safe(ndcP, ndcL); - - L = project_point(ViewProjectionMatrixInverse, frustum_isect) - ray_wpos; - L /= volShadowSteps; - dd = length(L); - } - - vec3 shadow = vec3(1.0); - for (float s = 1.0; s < VOLUMETRIC_SHADOW_MAX_STEP && s <= volShadowSteps; s += 1.0) { - vec3 pos = ray_wpos + L * s; - vec3 s_extinction = participating_media_extinction(pos, volume_extinction); - shadow *= exp(-s_extinction * dd); - } - return shadow; -#else - return vec3(1.0); -#endif /* VOLUME_SHADOW */ -} - -vec3 irradiance_volumetric(vec3 wpos) -{ -#ifdef IRRADIANCE_HL2 - IrradianceData ir_data = load_irradiance_cell(0, vec3(1.0)); - vec3 irradiance = ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2]; - ir_data = load_irradiance_cell(0, vec3(-1.0)); - irradiance += ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2]; - irradiance *= 0.16666666; /* 1/6 */ - return irradiance; -#else - return vec3(0.0); -#endif -} - -uniform sampler3D inScattering; -uniform sampler3D inTransmittance; - -void volumetric_resolve(vec2 frag_uvs, - float frag_depth, - out vec3 transmittance, - out vec3 scattering) -{ - vec3 volume_cos = ndc_to_volume(vec3(frag_uvs, frag_depth)); - - scattering = texture(inScattering, volume_cos).rgb; - transmittance = texture(inTransmittance, volume_cos).rgb; -} diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl deleted file mode 100644 index 665a34741ca..00000000000 --- a/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl +++ /dev/null @@ -1,34 +0,0 @@ - -#pragma BLENDER_REQUIRE(volumetric_lib.glsl) - -/* Based on Frosbite Unified Volumetric. - * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ - -/* Step 4 : Apply final integration on top of the scene color. */ - -uniform sampler2D inSceneDepth; - -/* Blend equation is : FragColor0 + FragColor1 * DstColor */ -#ifdef VOLUMETRICS_ACCUM -layout(location = 0) out vec4 FragColor0; -layout(location = 1) out vec4 FragColor1; -#else -layout(location = 0, index = 0) out vec4 FragColor0; -layout(location = 0, index = 1) out vec4 FragColor1; -#endif - -void main() -{ - vec2 uvs = gl_FragCoord.xy / vec2(textureSize(inSceneDepth, 0)); - float scene_depth = texture(inSceneDepth, uvs).r; - - vec3 transmittance, scattering; - volumetric_resolve(uvs, scene_depth, transmittance, scattering); - - /* Approximate volume alpha by using a monochromatic transmittance - * and adding it to the scene alpha. */ - float alpha = dot(transmittance, vec3(1.0 / 3.0)); - - FragColor0 = vec4(scattering, 1.0 - alpha); - FragColor1 = vec4(transmittance, alpha); -} diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl deleted file mode 100644 index dc755aeab8b..00000000000 --- a/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl +++ /dev/null @@ -1,86 +0,0 @@ - -#pragma BLENDER_REQUIRE(volumetric_lib.glsl) - -/* Based on Frosbite Unified Volumetric. - * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ - -/* Step 2 : Evaluate all light scattering for each froxels. - * Also do the temporal reprojection to fight aliasing artifacts. */ - -uniform sampler3D volumeScattering; -uniform sampler3D volumeExtinction; -uniform sampler3D volumeEmission; -uniform sampler3D volumePhase; - -uniform sampler3D historyScattering; -uniform sampler3D historyTransmittance; - -flat in int slice; - -layout(location = 0) out vec4 outScattering; -layout(location = 1) out vec4 outTransmittance; - -void main() -{ - ivec3 volume_cell = ivec3(gl_FragCoord.xy, slice); - - /* Emission */ - outScattering = texelFetch(volumeEmission, volume_cell, 0); - outTransmittance = texelFetch(volumeExtinction, volume_cell, 0); - vec3 s_scattering = texelFetch(volumeScattering, volume_cell, 0).rgb; - vec3 volume_ndc = volume_to_ndc((vec3(volume_cell) + volJitter.xyz) * volInvTexSize.xyz); - vec3 P = get_world_space_from_depth(volume_ndc.xy, volume_ndc.z); - vec3 V = cameraVec(P); - - vec2 phase = texelFetch(volumePhase, volume_cell, 0).rg; - float s_anisotropy = phase.x / max(1.0, phase.y); - - /* Environment : Average color. */ - outScattering.rgb += irradiance_volumetric(P) * s_scattering * phase_function_isotropic(); - -#ifdef VOLUME_LIGHTING /* Lights */ - for (int i = 0; i < MAX_LIGHT && i < laNumLight; i++) { - LightData ld = lights_data[i]; - - if (ld.l_volume == 0.0) { - continue; - } - - vec4 l_vector; - l_vector.xyz = light_volume_light_vector(ld, P); - l_vector.w = length(l_vector.xyz); - - float vis = light_visibility(ld, P, l_vector); - - if (vis < 1e-4) { - continue; - } - - vec3 Li = light_volume(ld, l_vector) * light_volume_shadow(ld, P, l_vector, volumeExtinction); - - outScattering.rgb += Li * vis * s_scattering * - phase_function(-V, l_vector.xyz / l_vector.w, s_anisotropy); - } -#endif - - /* Temporal supersampling */ - /* Note : this uses the cell non-jittered position (texel center). */ - vec3 curr_ndc = volume_to_ndc(vec3(gl_FragCoord.xy, float(slice) + 0.5) * volInvTexSize.xyz); - vec3 wpos = get_world_space_from_depth(curr_ndc.xy, curr_ndc.z); - vec3 prev_ndc = project_point(pastViewProjectionMatrix, wpos); - vec3 prev_volume = ndc_to_volume(prev_ndc * 0.5 + 0.5); - - if ((volHistoryAlpha > 0.0) && all(greaterThan(prev_volume, vec3(0.0))) && - all(lessThan(prev_volume, vec3(1.0)))) { - vec4 h_Scattering = texture(historyScattering, prev_volume); - vec4 h_Transmittance = texture(historyTransmittance, prev_volume); - outScattering = mix(outScattering, h_Scattering, volHistoryAlpha); - outTransmittance = mix(outTransmittance, h_Transmittance, volHistoryAlpha); - } - - /* Catch NaNs */ - if (any(isnan(outScattering)) || any(isnan(outTransmittance))) { - outScattering = vec4(0.0); - outTransmittance = vec4(1.0); - } -} diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl deleted file mode 100644 index b70747ecec3..00000000000 --- a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl +++ /dev/null @@ -1,37 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) - -out vec4 vPos; - -RESOURCE_ID_VARYING - -void main() -{ - /* Generate Triangle : less memory fetches from a VBO */ - int v_id = gl_VertexID % 3; /* Vertex Id */ - int t_id = gl_VertexID / 3; /* Triangle Id */ - - /* Crappy diagram - * ex 1 - * | \ - * | \ - * 1 | \ - * | \ - * | \ - * 0 | \ - * | \ - * | \ - * -1 0 --------------- 2 - * -1 0 1 ex - */ - vPos.x = float(v_id / 2) * 4.0 - 1.0; /* int divisor round down */ - vPos.y = float(v_id % 2) * 4.0 - 1.0; - vPos.z = float(t_id); - vPos.w = 1.0; - - PASS_RESOURCE_ID - -#ifdef USE_ATTR - pass_attr(vec3(0.0), mat3(1), mat4(1)); -#endif -} diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index bf5dbac9f68..17a0084689b 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -46,9 +46,6 @@ struct RenderLayer; struct View3D; struct bGPDstroke; -/* used to convert pixel scale. */ -#define GPENCIL_PIXEL_FACTOR 2000.0f - /* used to expand VBOs. Size has a big impact in the speed */ #define GPENCIL_VBO_BLOCK_SIZE 128 diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.c index 4c09349c35d..c1a2b184c6b 100644 --- a/source/blender/draw/engines/overlay/overlay_shader.c +++ b/source/blender/draw/engines/overlay/overlay_shader.c @@ -130,7 +130,6 @@ extern char datatoc_gpu_shader_2D_smooth_color_frag_glsl[]; extern char datatoc_gpu_shader_uniform_color_frag_glsl[]; extern char datatoc_gpu_shader_flat_color_frag_glsl[]; extern char datatoc_gpu_shader_point_varying_color_varying_outline_aa_frag_glsl[]; -extern char datatoc_gpu_shader_common_obinfos_lib_glsl[]; extern char datatoc_gpencil_common_lib_glsl[]; @@ -142,6 +141,7 @@ extern char datatoc_common_smaa_lib_glsl[]; extern char datatoc_common_globals_lib_glsl[]; extern char datatoc_common_pointcloud_lib_glsl[]; extern char datatoc_common_view_lib_glsl[]; +extern char datatoc_common_obinfos_lib_glsl[]; typedef struct OVERLAY_Shaders { GPUShader *antialiasing; @@ -897,7 +897,7 @@ GPUShader *OVERLAY_shader_extra_grid(void) .vert = (const char *[]){sh_cfg->lib, datatoc_common_view_lib_glsl, datatoc_common_globals_lib_glsl, - datatoc_gpu_shader_common_obinfos_lib_glsl, + datatoc_common_obinfos_lib_glsl, datatoc_extra_lightprobe_grid_vert_glsl, NULL}, .frag = (const char *[]){datatoc_gpu_shader_point_varying_color_frag_glsl, NULL}, @@ -1181,7 +1181,7 @@ GPUShader *OVERLAY_shader_outline_prepass(bool use_wire) sh_data->outline_prepass_wire = GPU_shader_create_from_arrays({ .vert = (const char *[]){sh_cfg->lib, datatoc_common_view_lib_glsl, - datatoc_gpu_shader_common_obinfos_lib_glsl, + datatoc_common_obinfos_lib_glsl, datatoc_outline_prepass_vert_glsl, NULL}, .geom = (const char *[]){sh_cfg->lib, @@ -1196,7 +1196,7 @@ GPUShader *OVERLAY_shader_outline_prepass(bool use_wire) sh_data->outline_prepass = GPU_shader_create_from_arrays({ .vert = (const char *[]){sh_cfg->lib, datatoc_common_view_lib_glsl, - datatoc_gpu_shader_common_obinfos_lib_glsl, + datatoc_common_obinfos_lib_glsl, datatoc_outline_prepass_vert_glsl, NULL}, .frag = (const char *[]){datatoc_outline_prepass_frag_glsl, NULL}, @@ -1216,7 +1216,7 @@ GPUShader *OVERLAY_shader_outline_prepass_gpencil(void) .vert = (const char *[]){sh_cfg->lib, datatoc_common_view_lib_glsl, datatoc_gpencil_common_lib_glsl, - datatoc_gpu_shader_common_obinfos_lib_glsl, + datatoc_common_obinfos_lib_glsl, datatoc_outline_prepass_vert_glsl, NULL}, .frag = (const char *[]){datatoc_common_view_lib_glsl, @@ -1242,7 +1242,7 @@ GPUShader *OVERLAY_shader_outline_prepass_pointcloud(void) .vert = (const char *[]){sh_cfg->lib, datatoc_common_view_lib_glsl, datatoc_common_pointcloud_lib_glsl, - datatoc_gpu_shader_common_obinfos_lib_glsl, + datatoc_common_obinfos_lib_glsl, datatoc_outline_prepass_vert_glsl, NULL}, .frag = (const char *[]){datatoc_common_view_lib_glsl, @@ -1556,7 +1556,7 @@ GPUShader *OVERLAY_shader_wireframe_select(void) .vert = (const char *[]){sh_cfg->lib, datatoc_common_view_lib_glsl, datatoc_common_globals_lib_glsl, - datatoc_gpu_shader_common_obinfos_lib_glsl, + datatoc_common_obinfos_lib_glsl, datatoc_wireframe_vert_glsl, NULL}, .frag = (const char *[]){datatoc_wireframe_frag_glsl, NULL}, @@ -1576,7 +1576,7 @@ GPUShader *OVERLAY_shader_wireframe(bool custom_bias) .vert = (const char *[]){sh_cfg->lib, datatoc_common_view_lib_glsl, datatoc_common_globals_lib_glsl, - datatoc_gpu_shader_common_obinfos_lib_glsl, + datatoc_common_obinfos_lib_glsl, datatoc_wireframe_vert_glsl, NULL}, .frag = (const char *[]){datatoc_common_view_lib_glsl, diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index b16caf49209..ee5aa401c6e 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -43,6 +43,7 @@ #include "DNA_world_types.h" #include "GPU_framebuffer.h" +#include "GPU_material.h" #include "GPU_primitive.h" #include "GPU_shader.h" #include "GPU_texture.h" @@ -202,13 +203,6 @@ void DRW_texture_free(struct GPUTexture *tex); /* Shaders */ -typedef void (*GPUMaterialEvalCallbackFn)(struct GPUMaterial *mat, - int options, - const char **vert_code, - const char **geom_code, - const char **frag_lib, - const char **defines); - struct GPUShader *DRW_shader_create_ex( const char *vert, const char *geom, const char *frag, const char *defines, const char *name); struct GPUShader *DRW_shader_create_with_lib_ex(const char *vert, @@ -247,38 +241,20 @@ struct GPUShader *DRW_shader_create_fullscreen_with_shaderlib_ex(const char *fra #define DRW_shader_create_fullscreen_with_shaderlib(frag, lib, defines) \ DRW_shader_create_fullscreen_with_shaderlib_ex(frag, lib, defines, __func__) -struct GPUMaterial *DRW_shader_find_from_world(struct World *wo, - const void *engine_type, - int options, - bool deferred); -struct GPUMaterial *DRW_shader_find_from_material(struct Material *ma, - const void *engine_type, - int options, - bool deferred); -struct GPUMaterial *DRW_shader_create_from_world(struct Scene *scene, - struct World *wo, - struct bNodeTree *ntree, - const void *engine_type, - int options, - bool is_volume_shader, - const char *vert, - const char *geom, - const char *frag_lib, - const char *defines, - bool deferred, - GPUMaterialEvalCallbackFn callback); -struct GPUMaterial *DRW_shader_create_from_material(struct Scene *scene, - struct Material *ma, - struct bNodeTree *ntree, - const void *engine_type, - int options, - bool is_volume_shader, - const char *vert, - const char *geom, - const char *frag_lib, - const char *defines, - bool deferred, - GPUMaterialEvalCallbackFn callback); +struct GPUMaterial *DRW_shader_from_world(struct World *wo, + struct bNodeTree *ntree, + const uint64_t shader_id, + const bool is_volume_shader, + bool deferred, + GPUCodegenCallbackFn callback, + void *thunk); +struct GPUMaterial *DRW_shader_from_material(struct Material *ma, + struct bNodeTree *ntree, + const uint64_t shader_id, + const bool is_volume_shader, + bool deferred, + GPUCodegenCallbackFn callback, + void *thunk); void DRW_shader_free(struct GPUShader *shader); #define DRW_SHADER_FREE_SAFE(shader) \ do { \ @@ -370,6 +346,8 @@ typedef enum { DRW_STATE_PROGRAM_POINT_SIZE = (1u << 31), } DRWState; +ENUM_OPERATORS(DRWState, DRW_STATE_PROGRAM_POINT_SIZE) + #define DRW_STATE_DEFAULT \ (DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL) #define DRW_STATE_BLEND_ENABLED \ @@ -466,6 +444,9 @@ void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, int groups_x_len, int groups_y_len, int groups_z_len); +/* Warning this keeps the ref to groups until it actually draws. */ +void DRW_shgroup_call_compute_ref(DRWShadingGroup *shgroup, int groups_ref[3]); + void DRW_shgroup_call_procedural_points(DRWShadingGroup *sh, Object *ob, uint point_count); void DRW_shgroup_call_procedural_lines(DRWShadingGroup *sh, Object *ob, uint line_count); void DRW_shgroup_call_procedural_triangles(DRWShadingGroup *sh, Object *ob, uint tri_count); @@ -488,6 +469,11 @@ void DRW_shgroup_call_instances_with_attrs(DRWShadingGroup *shgroup, void DRW_shgroup_call_sculpt(DRWShadingGroup *sh, Object *ob, bool wire, bool mask); void DRW_shgroup_call_sculpt_with_materials(DRWShadingGroup **sh, int num_sh, Object *ob); +/* Lower level functions. Use DRW_shgroup_call_buffer(_instance) preferably. */ +DRWCallBuffer *DRW_call_buffer_create(struct GPUVertFormat *format); + +struct GPUVertBuf *DRW_call_buffer_as_vertbuf(DRWCallBuffer *callbuf); + DRWCallBuffer *DRW_shgroup_call_buffer(DRWShadingGroup *shgroup, struct GPUVertFormat *format, GPUPrimType prim_type); @@ -531,6 +517,8 @@ void DRW_shgroup_stencil_set(DRWShadingGroup *shgroup, */ void DRW_shgroup_stencil_mask(DRWShadingGroup *shgroup, uint mask); +void DRW_shgroup_barrier(DRWShadingGroup *shgroup, eGPUBarrier type); + /** * Issue a clear command. */ @@ -562,7 +550,7 @@ void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup, const struct GPUUniformBuf *ubo); void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup, const char *name, - struct GPUUniformBuf **ubo); + const struct GPUUniformBuf **ubo); void DRW_shgroup_uniform_float(DRWShadingGroup *shgroup, const char *name, const float *value, @@ -625,6 +613,9 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup, void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup, const char *name, struct GPUVertBuf *vertex_buffer); +void DRW_shgroup_vertex_buffer_ref(DRWShadingGroup *shgroup, + const char *name, + struct GPUVertBuf **vertex_buffer); bool DRW_shgroup_is_empty(DRWShadingGroup *shgroup); @@ -713,7 +704,8 @@ const DRWView *DRW_view_get_active(void); * \note planes must be in world space. */ void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len); -void DRW_view_camtexco_set(DRWView *view, float texco[4]); +void DRW_view_camtexco_set(DRWView *view, const float texco[4]); +void DRW_view_camtexco_get(const DRWView *view, float r_texco[4]); /* For all getters, if view is NULL, default view is assumed. */ @@ -730,6 +722,7 @@ void DRW_view_frustum_corners_get(const DRWView *view, BoundBox *corners); * See #draw_frustum_culling_planes_calc() for the plane order. */ void DRW_view_frustum_planes_get(const DRWView *view, float planes[6][4]); +BoundSphere DRW_view_frustum_bsphere_get(const DRWView *view); /** * These are in view-space, so negative if in perspective. diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index fdb7cfba580..f4be85d5116 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -22,6 +22,10 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + struct DRWShadingGroup; struct FluidModifierData; struct GPUMaterial; @@ -225,3 +229,7 @@ struct DRW_Global { struct GPUUniformBuf *view_ubo; }; extern struct DRW_Global G_draw; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_debug.c b/source/blender/draw/intern/draw_debug.c index e0114a6230e..d5183f4e368 100644 --- a/source/blender/draw/intern/draw_debug.c +++ b/source/blender/draw/intern/draw_debug.c @@ -122,6 +122,17 @@ void DRW_debug_m4_as_bbox(const float m[4][4], const float color[4], const bool DRW_debug_bbox(&bb, color); } +void DRW_debug_view(const DRWView *view, const float color[4]) +{ + float persinv[4][4], viewinv[4][4]; + DRW_view_persmat_get(view, persinv, true); + DRW_view_viewmat_get(view, viewinv, true); + + DRW_debug_modelmat_reset(); + DRW_debug_m4_as_bbox(persinv, color, false); + DRW_debug_m4(viewinv); +} + void DRW_debug_sphere(const float center[3], const float radius, const float color[4]) { float size_mat[4][4]; @@ -136,6 +147,44 @@ void DRW_debug_sphere(const float center[3], const float radius, const float col BLI_LINKS_PREPEND(DST.debug.spheres, sphere); } +/* --------- Indirect Rendering --------- */ + +/* Keep in sync with shader. */ +#define DEBUG_VERT_MAX 16 * 4096 + +static GPUVertFormat *debug_buf_format(void) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + } + return &format; +} + +GPUVertBuf *drw_debug_line_buffer_get() +{ + DRWDebugBuffer *buf = MEM_mallocN(sizeof(DRWDebugBuffer), "DRWDebugBuffer"); + + buf->verts = GPU_vertbuf_create_with_format(debug_buf_format()); + GPU_vertbuf_data_alloc(buf->verts, DEBUG_VERT_MAX); + uint(*data)[4] = GPU_vertbuf_get_data(buf->verts); + /* Set vertex count to 1 to skip the first 2 degenerate verts. + * This is because the first one is already being aliased in the shader definition. */ + data[0][3] = 1; + /* Fill positions to NaN to avoid rendering unused verts. */ + /* TODO(fclem): This could be done on GPU if that becomes a bottleneck. */ + float(*fdata)[4] = (float(*)[4])data; + for (int v = 0; v < DEBUG_VERT_MAX; v++) { + for (int i = 0; i < 3; i++) { + fdata[v][i] = 0.0f / 0.0f; + } + } + BLI_LINKS_PREPEND(DST.debug.line_buffers, buf); + + return buf->verts; +} + /* --------- Render --------- */ static void drw_debug_draw_lines(void) @@ -217,10 +266,38 @@ static void drw_debug_draw_spheres(void) GPU_batch_discard(draw_batch); } +static void drw_debug_draw_buffers(void) +{ + int count = BLI_linklist_count((LinkNode *)DST.debug.line_buffers); + + if (count == 0) { + return; + } + + while (DST.debug.line_buffers) { + void *next = DST.debug.line_buffers->next; + + GPUBatch *batch = GPU_batch_create_ex( + GPU_PRIM_LINES, DST.debug.line_buffers->verts, NULL, GPU_BATCH_OWNS_VBO); + GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_FLAT_COLOR); + + float persmat[4][4]; + DRW_view_persmat_get(NULL, persmat, false); + GPU_batch_uniform_mat4(batch, "ViewProjectionMatrix", persmat); + + GPU_batch_draw(batch); + GPU_batch_discard(batch); + + MEM_freeN(DST.debug.line_buffers); + DST.debug.line_buffers = next; + } +} + void drw_debug_draw(void) { drw_debug_draw_lines(); drw_debug_draw_spheres(); + drw_debug_draw_buffers(); } void drw_debug_init(void) diff --git a/source/blender/draw/intern/draw_debug.h b/source/blender/draw/intern/draw_debug.h index 2616bc0af97..3f3cfe91652 100644 --- a/source/blender/draw/intern/draw_debug.h +++ b/source/blender/draw/intern/draw_debug.h @@ -22,6 +22,11 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + +struct DRWView; struct BoundBox; void DRW_debug_modelmat_reset(void); @@ -34,5 +39,10 @@ void DRW_debug_polygon_v3(const float (*v)[3], int vert_len, const float color[4 */ void DRW_debug_m4(const float m[4][4]); void DRW_debug_m4_as_bbox(const float m[4][4], const float color[4], bool invert); -void DRW_debug_bbox(const BoundBox *bbox, const float color[4]); +void DRW_debug_view(const struct DRWView *view, const float color[4]); +void DRW_debug_bbox(const struct BoundBox *bbox, const float color[4]); void DRW_debug_sphere(const float center[3], float radius, const float color[4]); + +#ifdef __cplusplus +} +#endif
\ No newline at end of file diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index d27eb8be9e0..929fe9e2e1d 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -205,8 +205,10 @@ typedef enum { /* Compute Commands. */ DRW_CMD_COMPUTE = 8, + DRW_CMD_COMPUTE_REF = 9, /* Other Commands */ + DRW_CMD_BARRIER = 11, DRW_CMD_CLEAR = 12, DRW_CMD_DRWSTATE = 13, DRW_CMD_STENCIL = 14, @@ -249,6 +251,14 @@ typedef struct DRWCommandCompute { int groups_z_len; } DRWCommandCompute; +typedef struct DRWCommandComputeRef { + int *groups_ref; +} DRWCommandComputeRef; + +typedef struct DRWCommandBarrier { + eGPUBarrier type; +} DRWCommandBarrier; + typedef struct DRWCommandDrawProcedural { GPUBatch *batch; DRWResourceHandle handle; @@ -286,6 +296,8 @@ typedef union DRWCommand { DRWCommandDrawInstanceRange instance_range; DRWCommandDrawProcedural procedural; DRWCommandCompute compute; + DRWCommandComputeRef compute_ref; + DRWCommandBarrier barrier; DRWCommandSetMutableState state; DRWCommandSetStencil stencil; DRWCommandSetSelectID select_id; @@ -314,6 +326,7 @@ typedef enum { DRW_UNIFORM_BLOCK_REF, DRW_UNIFORM_TFEEDBACK_TARGET, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, + DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF, /** Per drawcall uniforms/UBO */ DRW_UNIFORM_BLOCK_OBMATS, DRW_UNIFORM_BLOCK_OBINFOS, @@ -345,6 +358,11 @@ struct DRWUniform { GPUUniformBuf *block; GPUUniformBuf **block_ref; }; + /* DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE */ + union { + GPUVertBuf *vertbuf; + GPUVertBuf **vertbuf_ref; + }; /* DRW_UNIFORM_FLOAT_COPY */ float fvalue[4]; /* DRW_UNIFORM_INT_COPY */ @@ -422,6 +440,13 @@ typedef struct DRWViewUboStorage { float viewvecs[2][4]; /* Should not be here. Not view dependent (only main view). */ float viewcamtexcofac[4]; + float viewport_size[2]; + float viewport_size_inv[2]; + + /** Frustum culling data. */ + /** NOTE: vec3 arrays are paded to vec4. */ + float frustum_corners[8][4]; + float frustum_planes[6][4]; } DRWViewUboStorage; BLI_STATIC_ASSERT_ALIGN(DRWViewUboStorage, 16) @@ -506,6 +531,11 @@ typedef struct DRWDebugSphere { float color[4]; } DRWDebugSphere; +typedef struct DRWDebugBuffer { + struct DRWDebugBuffer *next; /* linked list */ + struct GPUVertBuf *verts; +} DRWDebugBuffer; + /* ------------- Memory Pools ------------ */ /* Contains memory pools information */ @@ -643,6 +673,7 @@ typedef struct DRWManager { /* TODO(fclem): optimize: use chunks. */ DRWDebugLine *lines; DRWDebugSphere *spheres; + DRWDebugBuffer *line_buffers; } debug; } DRWManager; @@ -656,6 +687,7 @@ void *drw_viewport_engine_data_ensure(void *engine_type); void drw_state_set(DRWState state); +GPUVertBuf *drw_debug_line_buffer_get(void); void drw_debug_draw(void); void drw_debug_init(void); diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index ab570667a77..0a41e1a27c3 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -32,6 +32,7 @@ #include "BKE_pbvh.h" #include "DNA_curve_types.h" +#include "DNA_gpencil_types.h" #include "DNA_mesh_types.h" #include "DNA_meta_types.h" @@ -292,7 +293,9 @@ void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup, drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_BLOCK, ubo, 0, 0, 1); } -void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup, const char *name, GPUUniformBuf **ubo) +void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup, + const char *name, + const GPUUniformBuf **ubo) { BLI_assert(ubo != NULL); int loc = GPU_shader_get_uniform_block_binding(shgroup->shader, name); @@ -460,6 +463,19 @@ void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup, shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, vertex_buffer, 0, 0, 1); } +void DRW_shgroup_vertex_buffer_ref(DRWShadingGroup *shgroup, + const char *name, + GPUVertBuf **vertex_buffer) +{ + int location = GPU_shader_get_ssbo(shgroup->shader, name); + if (location == -1) { + BLI_assert_msg(0, "Unable to locate binding of shader storage buffer objects."); + return; + } + drw_shgroup_uniform_create_ex( + shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF, vertex_buffer, 0, 0, 1); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -489,6 +505,19 @@ static void drw_call_calc_orco(Object *ob, float (*r_orcofacs)[4]) texcosize = mb->size; break; } + case ID_GD: { + /* TODO(fclem) Put in the object eval somehow. */ + /* Note this is used for shading to get the average normal. + * A user modified texture space would not have this bounding property. */ + BoundBox *bbox = BKE_object_boundbox_get(ob); + /* TODO(fclem) Do real orco. */ + copy_v3_fl(r_orcofacs[0], 0.0f); + BKE_boundbox_calc_size_aabb(bbox, r_orcofacs[1]); + /* Avoid division by 0. */ + add_v3_fl(r_orcofacs[1], 1e-8f); + invert_v3(r_orcofacs[1]); + return; + } default: break; } @@ -528,7 +557,7 @@ static void drw_call_obinfos_init(DRWObjectInfos *ob_infos, Object *ob) drw_call_calc_orco(ob, ob_infos->orcotexfac); /* Random float value. */ uint random = (DST.dupli_source) ? - DST.dupli_source->random_id : + DST.dupli_source->random_id : /* TODO(fclem): this is rather costly to do at runtime. Maybe we can * put it in ob->runtime and make depsgraph ensure it is up to date. */ BLI_hash_int_2d(BLI_hash_string(ob->id.name + 2), 0); @@ -548,6 +577,15 @@ static void drw_call_obinfos_init(DRWObjectInfos *ob_infos, Object *ob) ob_infos->ob_flag *= (ob->transflag & OB_NEG_SCALE) ? -1.0f : 1.0f; /* Object Color. */ copy_v4_v4(ob_infos->ob_color, ob->color); + /* Grease Pencil object parameters. */ + if (ob->type == OB_GPENCIL) { + bGPdata *gpdata = ob->data; + float ob_scale = mat4_to_scale(ob->obmat); + ob_infos->orcotexfac[0][3] = (gpdata->flag & GP_DATA_STROKE_KEEPTHICKNESS) ? + -1.0 : + (gpdata->pixfactor / GPENCIL_PIXEL_FACTOR); + ob_infos->orcotexfac[1][3] = (gpdata->draw_mode == GP_DRAWMODE_2D) ? -ob_scale : ob_scale; + } } static void drw_call_culling_init(DRWCullingState *cull, Object *ob) @@ -730,6 +768,18 @@ static void drw_command_compute(DRWShadingGroup *shgroup, cmd->groups_z_len = groups_z_len; } +static void drw_command_compute_ref(DRWShadingGroup *shgroup, int groups_ref[3]) +{ + DRWCommandComputeRef *cmd = drw_command_create(shgroup, DRW_CMD_COMPUTE_REF); + cmd->groups_ref = groups_ref; +} + +static void drw_command_barrier(DRWShadingGroup *shgroup, eGPUBarrier type) +{ + DRWCommandBarrier *cmd = drw_command_create(shgroup, DRW_CMD_BARRIER); + cmd->type = type; +} + static void drw_command_draw_procedural(DRWShadingGroup *shgroup, GPUBatch *batch, DRWResourceHandle handle, @@ -855,6 +905,20 @@ void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, drw_command_compute(shgroup, groups_x_len, groups_y_len, groups_z_len); } +void DRW_shgroup_call_compute_ref(DRWShadingGroup *shgroup, int groups_ref[3]) +{ + BLI_assert(GPU_compute_shader_support()); + + drw_command_compute_ref(shgroup, groups_ref); +} + +void DRW_shgroup_barrier(DRWShadingGroup *shgroup, eGPUBarrier type) +{ + BLI_assert(GPU_compute_shader_support()); + + drw_command_barrier(shgroup, type); +} + static void drw_shgroup_call_procedural_add_ex(DRWShadingGroup *shgroup, GPUBatch *geom, Object *ob, @@ -1124,6 +1188,33 @@ void DRW_shgroup_call_sculpt_with_materials(DRWShadingGroup **shgroups, static GPUVertFormat inst_select_format = {0}; +DRWCallBuffer *DRW_call_buffer_create(struct GPUVertFormat *format) +{ + BLI_assert(format != NULL); + + DRWCallBuffer *callbuf = BLI_memblock_alloc(DST.vmempool->callbuffers); + callbuf->buf = DRW_temp_buffer_request(DST.vmempool->idatalist, format, &callbuf->count); + callbuf->buf_select = NULL; + callbuf->count = 0; + + if (G.f & G_FLAG_PICKSEL) { + /* Not actually used for rendering but alloced in one chunk. */ + if (inst_select_format.attr_len == 0) { + GPU_vertformat_attr_add(&inst_select_format, "selectId", GPU_COMP_I32, 1, GPU_FETCH_INT); + } + callbuf->buf_select = DRW_temp_buffer_request( + DST.vmempool->idatalist, &inst_select_format, &callbuf->count); + } + + return callbuf; +} + +GPUVertBuf *DRW_call_buffer_as_vertbuf(DRWCallBuffer *callbuf) +{ + GPU_vertbuf_data_len_set(callbuf->buf, callbuf->count); + return callbuf->buf; +} + DRWCallBuffer *DRW_shgroup_call_buffer(DRWShadingGroup *shgroup, struct GPUVertFormat *format, GPUPrimType prim_type) @@ -1292,6 +1383,15 @@ static void drw_shgroup_init(DRWShadingGroup *shgroup, GPUShader *shader) } } +#ifdef DEBUG + int debugbuf_location = GPU_shader_get_builtin_ssbo(shader, GPU_BUFFER_BLOCK_DEBUG); + if (debugbuf_location != -1) { + GPUVertBuf *vertbuf = drw_debug_line_buffer_get(); + drw_shgroup_uniform_create_ex( + shgroup, debugbuf_location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, vertbuf, 0, 0, 1); + } +#endif + if (info_ubo_location != -1) { drw_shgroup_uniform_create_ex( shgroup, info_ubo_location, DRW_UNIFORM_BLOCK_OBINFOS, NULL, 0, 0, 1); @@ -1883,6 +1983,14 @@ void DRW_view_update(DRWView *view, draw_frustum_bound_sphere_calc( &view->frustum_corners, viewinv, winmat, wininv, &view->frustum_bsphere); + /* TODO(fclem): Deduplicate. */ + for (int i = 0; i < 8; i++) { + copy_v3_v3(view->storage.frustum_corners[i], view->frustum_corners.vec[i]); + } + for (int i = 0; i < 6; i++) { + copy_v4_v4(view->storage.frustum_planes[i], view->frustum_planes[i]); + } + #ifdef DRW_DEBUG_CULLING if (G.debug_value != 0) { DRW_debug_sphere( @@ -1919,11 +2027,16 @@ void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len) } } -void DRW_view_camtexco_set(DRWView *view, float texco[4]) +void DRW_view_camtexco_set(DRWView *view, const float texco[4]) { copy_v4_v4(view->storage.viewcamtexcofac, texco); } +void DRW_view_camtexco_get(const DRWView *view, float r_texco[4]) +{ + copy_v4_v4(r_texco, view->storage.viewcamtexcofac); +} + void DRW_view_frustum_corners_get(const DRWView *view, BoundBox *corners) { memcpy(corners, &view->frustum_corners, sizeof(view->frustum_corners)); @@ -1934,6 +2047,12 @@ void DRW_view_frustum_planes_get(const DRWView *view, float planes[6][4]) memcpy(planes, &view->frustum_planes, sizeof(view->frustum_planes)); } +/* Return world space frustum bounding sphere. */ +BoundSphere DRW_view_frustum_bsphere_get(const DRWView *view) +{ + return view->frustum_bsphere; +} + bool DRW_view_is_persp_get(const DRWView *view) { view = (view) ? view : DST.view_default; diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 8dd24c01337..070b6d2eae8 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -356,7 +356,8 @@ static bool draw_call_is_culled(const DRWResourceHandle *handle, DRWView *view) void DRW_view_set_active(DRWView *view) { - DST.view_active = (view) ? view : DST.view_default; + /* TODO(fclem) DST.view_active should be const too. */ + DST.view_active = (view) ? (DRWView *)view : DST.view_default; } const DRWView *DRW_view_get_active(void) @@ -662,8 +663,11 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup, *use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader, ((GPUVertBuf *)uni->pvalue)); break; + case DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF: + GPU_vertbuf_bind_as_ssbo(*uni->vertbuf_ref, uni->location); + break; case DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE: - GPU_vertbuf_bind_as_ssbo((GPUVertBuf *)uni->pvalue, uni->location); + GPU_vertbuf_bind_as_ssbo(uni->vertbuf, uni->location); break; /* Legacy/Fallback support. */ case DRW_UNIFORM_BASE_INSTANCE: @@ -1049,6 +1053,15 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) cmd->compute.groups_y_len, cmd->compute.groups_z_len); break; + case DRW_CMD_COMPUTE_REF: + GPU_compute_dispatch(shgroup->shader, + cmd->compute_ref.groups_ref[0], + cmd->compute_ref.groups_ref[1], + cmd->compute_ref.groups_ref[2]); + break; + case DRW_CMD_BARRIER: + GPU_memory_barrier(cmd->barrier.type); + break; } } @@ -1060,8 +1073,13 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) } } -static void drw_update_view(void) +static void drw_update_view(const float viewport_size[2]) { + DRWViewUboStorage *storage = &DST.view_active->storage; + copy_v2_v2(storage->viewport_size, viewport_size); + copy_v2_v2(storage->viewport_size_inv, viewport_size); + invert_v2(storage->viewport_size_inv); + /* TODO(fclem): update a big UBO and only bind ranges here. */ GPU_uniformbuf_update(G_draw.view_ubo, &DST.view_active->storage); @@ -1089,8 +1107,11 @@ static void drw_draw_pass_ex(DRWPass *pass, BLI_assert(DST.buffer_finish_called && "DRW_render_instance_buffer_finish had not been called before drawing"); - if (DST.view_previous != DST.view_active || DST.view_active->is_dirty) { - drw_update_view(); + float viewport[4]; + GPU_viewport_size_get_f(viewport); + if (DST.view_previous != DST.view_active || DST.view_active->is_dirty || + !equals_v2v2(DST.view_active->storage.viewport_size, &viewport[2])) { + drw_update_view(&viewport[2]); DST.view_active->is_dirty = false; DST.view_previous = DST.view_active; } diff --git a/source/blender/draw/intern/draw_manager_profiling.h b/source/blender/draw/intern/draw_manager_profiling.h index e7c84491bdb..5626ac52049 100644 --- a/source/blender/draw/intern/draw_manager_profiling.h +++ b/source/blender/draw/intern/draw_manager_profiling.h @@ -22,6 +22,10 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + struct rcti; void DRW_stats_free(void); @@ -42,3 +46,7 @@ void DRW_stats_query_start(const char *name); void DRW_stats_query_end(void); void DRW_stats_draw(const rcti *rect); + +#ifdef __cplusplus +} +#endif
\ No newline at end of file diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c index 84440a8effe..ca8284af691 100644 --- a/source/blender/draw/intern/draw_manager_shader.c +++ b/source/blender/draw/intern/draw_manager_shader.c @@ -426,119 +426,81 @@ GPUShader *DRW_shader_create_fullscreen_with_shaderlib_ex(const char *frag, return sh; } -GPUMaterial *DRW_shader_find_from_world(World *wo, - const void *engine_type, - const int options, - bool deferred) +GPUMaterial *DRW_shader_from_world(World *wo, + struct bNodeTree *ntree, + const uint64_t shader_id, + const bool is_volume_shader, + bool deferred, + GPUCodegenCallbackFn callback, + void *thunk) { - GPUMaterial *mat = GPU_material_from_nodetree_find(&wo->gpumaterial, engine_type, options); - if (DRW_state_is_image_render() || !deferred) { - if (mat != NULL && GPU_material_status(mat) == GPU_MAT_QUEUED) { - /* XXX Hack : we return NULL so that the engine will call DRW_shader_create_from_XXX - * with the shader code and we will resume the compilation from there. */ - return NULL; - } - } - return mat; -} - -GPUMaterial *DRW_shader_find_from_material(Material *ma, - const void *engine_type, - const int options, - bool deferred) -{ - GPUMaterial *mat = GPU_material_from_nodetree_find(&ma->gpumaterial, engine_type, options); - if (DRW_state_is_image_render() || !deferred) { - if (mat != NULL && GPU_material_status(mat) == GPU_MAT_QUEUED) { - /* XXX Hack : we return NULL so that the engine will call DRW_shader_create_from_XXX - * with the shader code and we will resume the compilation from there. */ - return NULL; - } - } - return mat; -} - -GPUMaterial *DRW_shader_create_from_world(struct Scene *scene, - World *wo, - struct bNodeTree *ntree, - const void *engine_type, - const int options, - const bool is_volume_shader, - const char *vert, - const char *geom, - const char *frag_lib, - const char *defines, - bool deferred, - GPUMaterialEvalCallbackFn callback) -{ - GPUMaterial *mat = NULL; - if (DRW_state_is_image_render() || !deferred) { - mat = GPU_material_from_nodetree_find(&wo->gpumaterial, engine_type, options); - } - - if (mat == NULL) { - scene = (Scene *)DEG_get_original_id(&DST.draw_ctx.scene->id); - mat = GPU_material_from_nodetree(scene, - NULL, - ntree, - &wo->gpumaterial, - engine_type, - options, - is_volume_shader, - vert, - geom, - frag_lib, - defines, - wo->id.name, - callback); + Scene *scene = (Scene *)DEG_get_original_id(&DST.draw_ctx.scene->id); + GPUMaterial *mat = GPU_material_from_nodetree(scene, + NULL, + ntree, + &wo->gpumaterial, + wo->id.name, + shader_id, + is_volume_shader, + false, + callback, + thunk); + if (!DRW_state_is_image_render() && deferred && GPU_material_status(mat) == GPU_MAT_QUEUED) { + /* Shader has been already queued. */ + return mat; } - if (GPU_material_status(mat) == GPU_MAT_QUEUED) { + if (GPU_material_status(mat) == GPU_MAT_CREATED) { + GPU_material_status_set(mat, GPU_MAT_QUEUED); drw_deferred_shader_add(mat, deferred); } + if (!deferred && GPU_material_status(mat) == GPU_MAT_QUEUED) { + /* Force compilation for shaders already queued. */ + drw_deferred_shader_add(mat, false); + } return mat; } -GPUMaterial *DRW_shader_create_from_material(struct Scene *scene, - Material *ma, - struct bNodeTree *ntree, - const void *engine_type, - const int options, - const bool is_volume_shader, - const char *vert, - const char *geom, - const char *frag_lib, - const char *defines, - bool deferred, - GPUMaterialEvalCallbackFn callback) +GPUMaterial *DRW_shader_from_material(Material *ma, + struct bNodeTree *ntree, + const uint64_t shader_id, + const bool is_volume_shader, + bool deferred, + GPUCodegenCallbackFn callback, + void *thunk) { - GPUMaterial *mat = NULL; - if (DRW_state_is_image_render() || !deferred) { - mat = GPU_material_from_nodetree_find(&ma->gpumaterial, engine_type, options); + Scene *scene = (Scene *)DEG_get_original_id(&DST.draw_ctx.scene->id); + GPUMaterial *mat = GPU_material_from_nodetree(scene, + ma, + ntree, + &ma->gpumaterial, + ma->id.name, + shader_id, + is_volume_shader, + false, + callback, + thunk); + + if (DRW_state_is_image_render()) { + /* Do not deferred if doing render. */ + deferred = false; } - if (mat == NULL) { - scene = (Scene *)DEG_get_original_id(&DST.draw_ctx.scene->id); - mat = GPU_material_from_nodetree(scene, - ma, - ntree, - &ma->gpumaterial, - engine_type, - options, - is_volume_shader, - vert, - geom, - frag_lib, - defines, - ma->id.name, - callback); + if (deferred && GPU_material_status(mat) == GPU_MAT_QUEUED) { + /* Shader has been already queued. */ + return mat; } - if (GPU_material_status(mat) == GPU_MAT_QUEUED) { + if (GPU_material_status(mat) == GPU_MAT_CREATED) { + GPU_material_status_set(mat, GPU_MAT_QUEUED); drw_deferred_shader_add(mat, deferred); } + if (!deferred && GPU_material_status(mat) == GPU_MAT_QUEUED) { + /* Force compilation for shaders already queued. */ + drw_deferred_shader_add(mat, false); + } return mat; } @@ -561,15 +523,15 @@ void DRW_shader_free(GPUShader *shader) * contains the needed libraries for this shader. * \{ */ -/* 32 because we use a 32bit bitmap. */ -#define MAX_LIB 32 +/* 64 because we use a 64bit bitmap. */ +#define MAX_LIB 64 #define MAX_LIB_NAME 64 #define MAX_LIB_DEPS 8 struct DRWShaderLibrary { - char *libs[MAX_LIB]; + const char *libs[MAX_LIB]; char libs_name[MAX_LIB][MAX_LIB_NAME]; - uint32_t libs_deps[MAX_LIB]; + uint64_t libs_deps[MAX_LIB]; }; DRWShaderLibrary *DRW_shader_library_create(void) @@ -598,38 +560,43 @@ static int drw_shader_library_search(const DRWShaderLibrary *lib, const char *na } /* Return bitmap of dependencies. */ -static uint32_t drw_shader_dependencies_get(const DRWShaderLibrary *lib, const char *lib_code) +static uint64_t drw_shader_dependencies_get(const DRWShaderLibrary *lib, + const char *pragma_str, + const char *lib_code, + const char *lib_name) { /* Search dependencies. */ - uint32_t deps = 0; + uint pragma_len = strlen(pragma_str); + uint64_t deps = 0; const char *haystack = lib_code; - while ((haystack = strstr(haystack, "BLENDER_REQUIRE("))) { - haystack += 16; + while ((haystack = strstr(haystack, pragma_str))) { + haystack += pragma_len; int dep = drw_shader_library_search(lib, haystack); if (dep == -1) { - char dbg_name[33]; + char dbg_name[MAX_NAME]; int i = 0; while ((*haystack != ')') && (i < (sizeof(dbg_name) - 2))) { dbg_name[i] = *haystack; haystack++; i++; } - dbg_name[i + 1] = '\0'; + dbg_name[i] = '\0'; printf( - "Error: Dependency not found: %s\n" + "Error: Dependency %s not found for %s.\n" "This might be due to bad lib ordering.\n", - dbg_name); + dbg_name, + lib_name); BLI_assert(0); } else { - deps |= 1u << (uint32_t)dep; + deps |= 1llu << ((uint64_t)dep); } } return deps; } -void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const char *lib_name) +void DRW_shader_library_add_file(DRWShaderLibrary *lib, const char *lib_code, const char *lib_name) { int index = -1; for (int i = 0; i < MAX_LIB; i++) { @@ -642,7 +609,8 @@ void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const ch if (index > -1) { lib->libs[index] = lib_code; BLI_strncpy(lib->libs_name[index], lib_name, MAX_LIB_NAME); - lib->libs_deps[index] = drw_shader_dependencies_get(lib, lib_code); + lib->libs_deps[index] = drw_shader_dependencies_get( + lib, "BLENDER_REQUIRE(", lib_code, lib_name); } else { printf("Error: Too many libraries. Cannot add %s.\n", lib_name); @@ -652,25 +620,34 @@ void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const ch char *DRW_shader_library_create_shader_string(const DRWShaderLibrary *lib, const char *shader_code) { - uint32_t deps = drw_shader_dependencies_get(lib, shader_code); + /* TODO(fclem) Could be done in one pass. */ + uint64_t deps = drw_shader_dependencies_get(lib, "BLENDER_REQUIRE(", shader_code, "shader code"); + uint64_t deps_post = drw_shader_dependencies_get( + lib, "BLENDER_REQUIRE_POST(", shader_code, "shader code"); DynStr *ds = BLI_dynstr_new(); /* Add all dependencies recursively. */ for (int i = MAX_LIB - 1; i > -1; i--) { - if (lib->libs[i] && (deps & (1u << (uint32_t)i))) { + if (lib->libs[i] && ((deps & (1llu << (uint64_t)i)) || (deps_post & (1llu << (uint64_t)i)))) { deps |= lib->libs_deps[i]; } } /* Concatenate all needed libs into one string. */ - for (int i = 0; i < MAX_LIB; i++) { - if (deps & 1u) { + for (int i = 0; i < MAX_LIB && deps != 0llu; i++, deps >>= 1llu) { + if (deps & 1llu) { BLI_dynstr_append(ds, lib->libs[i]); } - deps = deps >> 1; } BLI_dynstr_append(ds, shader_code); + /* Concatenate all needed libs into one string. */ + for (int i = 0; i < MAX_LIB && deps_post != 0llu; i++, deps_post >>= 1llu) { + if (deps_post & 1llu) { + BLI_dynstr_append(ds, lib->libs[i]); + } + } + char *str = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); diff --git a/source/blender/draw/intern/draw_manager_texture.c b/source/blender/draw/intern/draw_manager_texture.c index 86242468e4a..5062c9c970e 100644 --- a/source/blender/draw/intern/draw_manager_texture.c +++ b/source/blender/draw/intern/draw_manager_texture.c @@ -43,13 +43,20 @@ static bool drw_texture_format_supports_framebuffer(eGPUTextureFormat format) case GPU_RG16: case GPU_RG16F: case GPU_RG16I: + case GPU_RG16UI: case GPU_RG32F: + case GPU_RG32I: + case GPU_RG32UI: case GPU_RGB10_A2: case GPU_R11F_G11F_B10F: case GPU_RGBA8: case GPU_RGBA16: case GPU_RGBA16F: + case GPU_RGBA16I: + case GPU_RGBA16UI: case GPU_RGBA32F: + case GPU_RGBA32I: + case GPU_RGBA32UI: case GPU_DEPTH_COMPONENT16: case GPU_DEPTH_COMPONENT24: case GPU_DEPTH24_STENCIL8: diff --git a/source/blender/draw/intern/shaders/common_attribute_lib.glsl b/source/blender/draw/intern/shaders/common_attribute_lib.glsl new file mode 100644 index 00000000000..1b693c2b01d --- /dev/null +++ b/source/blender/draw/intern/shaders/common_attribute_lib.glsl @@ -0,0 +1,19 @@ + +/* Prototype of functions to implement to load attributes data. + * Implementation changes based on object data type. */ + +vec3 attr_load_orco(vec4 orco); +vec4 attr_load_tangent(vec4 tangent); +vec3 attr_load_uv(vec3 uv); +vec4 attr_load_color(vec4 color); +vec4 attr_load_vec4(vec4 attr); +vec3 attr_load_vec3(vec3 attr); +vec2 attr_load_vec2(vec2 attr); + +vec3 attr_load_orco(samplerBuffer orco); +vec4 attr_load_tangent(samplerBuffer tangent); +vec3 attr_load_uv(samplerBuffer uv); +vec4 attr_load_color(samplerBuffer color); +vec4 attr_load_vec4(samplerBuffer attr); +vec3 attr_load_vec3(samplerBuffer attr); +vec2 attr_load_vec2(samplerBuffer attr); diff --git a/source/blender/draw/intern/shaders/common_debug_lib.glsl b/source/blender/draw/intern/shaders/common_debug_lib.glsl new file mode 100644 index 00000000000..396bbb759ec --- /dev/null +++ b/source/blender/draw/intern/shaders/common_debug_lib.glsl @@ -0,0 +1,131 @@ + +/** + * Debugging drawing library + * + * Quick way to draw debug geometry. All input should be in world space and + * will be rendered in the default view. No additional setup required. + **/ +#define DEBUG_DRAW + +/* Keep in sync with buffer creation. */ +#define DEBUG_VERT_MAX 16 * 4096 + +struct DebugVert { + vec3 pos; + uint color; +}; + +layout(std430, binding = 7) restrict buffer debugBuf +{ + /** Start the buffer with a degenerate vertice. */ + uint _pad0; + uint _pad1; + uint _pad2; + uint v_count; + DebugVert verts[]; +} +drw_debug_verts; + +bool drw_debug_draw_enable = true; + +uint drw_debug_color_pack(vec4 color) +{ + color = clamp(color, 0.0, 1.0); + uint result = 0; + result |= uint(color.x * 255.0) << 0u; + result |= uint(color.y * 255.0) << 8u; + result |= uint(color.z * 255.0) << 16u; + result |= uint(color.w * 255.0) << 24u; + return result; +} + +void drw_debug_line_do(inout uint vertid, vec3 v1, vec3 v2, uint color) +{ + drw_debug_verts.verts[vertid++] = DebugVert(v1, color); + drw_debug_verts.verts[vertid++] = DebugVert(v2, color); +} + +void drw_debug_line(vec3 v1, vec3 v2, vec4 color) +{ + if (!drw_debug_draw_enable) { + return; + } + const uint vneeded = 2; + uint vertid = atomicAdd(drw_debug_verts.v_count, vneeded); + if (vertid + vneeded < DEBUG_VERT_MAX + 1) { + drw_debug_line_do(vertid, v1, v2, drw_debug_color_pack(color)); + } +} + +void drw_debug_quad(vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec4 color) +{ + if (!drw_debug_draw_enable) { + return; + } + const uint vneeded = 8; + uint vertid = atomicAdd(drw_debug_verts.v_count, vneeded); + if (vertid + vneeded < DEBUG_VERT_MAX + 1) { + uint pcolor = drw_debug_color_pack(color); + drw_debug_line_do(vertid, v1, v2, pcolor); + drw_debug_line_do(vertid, v2, v3, pcolor); + drw_debug_line_do(vertid, v3, v4, pcolor); + drw_debug_line_do(vertid, v4, v1, pcolor); + } +} + +/* Draw an octahedron. */ +void drw_debug_point(vec3 p, float radius, vec4 color) +{ + if (!drw_debug_draw_enable) { + return; + } + vec3 c = vec3(radius, -radius, 0); + vec3 v1 = p + c.xzz; + vec3 v2 = p + c.zxz; + vec3 v3 = p + c.yzz; + vec3 v4 = p + c.zyz; + vec3 v5 = p + c.zzx; + vec3 v6 = p + c.zzy; + + const uint vneeded = 12 * 2; + uint vertid = atomicAdd(drw_debug_verts.v_count, vneeded); + if (vertid + vneeded < DEBUG_VERT_MAX + 1) { + uint pcolor = drw_debug_color_pack(color); + drw_debug_line_do(vertid, v1, v2, pcolor); + drw_debug_line_do(vertid, v2, v3, pcolor); + drw_debug_line_do(vertid, v3, v4, pcolor); + drw_debug_line_do(vertid, v4, v1, pcolor); + drw_debug_line_do(vertid, v1, v5, pcolor); + drw_debug_line_do(vertid, v2, v5, pcolor); + drw_debug_line_do(vertid, v3, v5, pcolor); + drw_debug_line_do(vertid, v4, v5, pcolor); + drw_debug_line_do(vertid, v1, v6, pcolor); + drw_debug_line_do(vertid, v2, v6, pcolor); + drw_debug_line_do(vertid, v3, v6, pcolor); + drw_debug_line_do(vertid, v4, v6, pcolor); + } +} + +void drw_debug_matrix(mat4 mat, vec4 color, const bool do_project) +{ + vec4 p[8] = vec4[8](vec4(-1, -1, -1, 1), + vec4(1, -1, -1, 1), + vec4(1, 1, -1, 1), + vec4(-1, 1, -1, 1), + vec4(-1, -1, 1, 1), + vec4(1, -1, 1, 1), + vec4(1, 1, 1, 1), + vec4(-1, 1, 1, 1)); + for (int i = 0; i < 8; i++) { + p[i] = mat * p[i]; + if (do_project) { + p[i].xyz /= p[i].w; + } + } + drw_debug_quad(p[0].xyz, p[1].xyz, p[2].xyz, p[3].xyz, color); + drw_debug_line(p[0].xyz, p[4].xyz, color); + drw_debug_line(p[1].xyz, p[5].xyz, color); + drw_debug_line(p[2].xyz, p[6].xyz, color); + drw_debug_line(p[3].xyz, p[7].xyz, color); + drw_debug_quad(p[4].xyz, p[5].xyz, p[6].xyz, p[7].xyz, color); +} diff --git a/source/blender/draw/intern/shaders/common_gpencil_lib.glsl b/source/blender/draw/intern/shaders/common_gpencil_lib.glsl new file mode 100644 index 00000000000..785c6948d8f --- /dev/null +++ b/source/blender/draw/intern/shaders/common_gpencil_lib.glsl @@ -0,0 +1,309 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_obinfos_lib.glsl) + +#ifdef GPU_FRAGMENT_SHADER +float gpencil_stroke_round_cap_mask(vec2 p1, vec2 p2, vec2 aspect, float thickness, float hardfac) +{ + /* We create our own uv space to avoid issues with triangulation and linear + * interpolation artifacts. */ + vec2 line = p2.xy - p1.xy; + vec2 pos = gl_FragCoord.xy - p1.xy; + float line_len = length(line); + float half_line_len = line_len * 0.5; + /* Normalize */ + line = (line_len > 0.0) ? (line / line_len) : vec2(1.0, 0.0); + /* Create a uv space that englobe the whole segment into a capsule. */ + vec2 uv_end; + uv_end.x = max(abs(dot(line, pos) - half_line_len) - half_line_len, 0.0); + uv_end.y = dot(vec2(-line.y, line.x), pos); + /* Divide by stroke radius. */ + uv_end /= thickness; + uv_end *= aspect; + + float dist = clamp(1.0 - length(uv_end) * 2.0, 0.0, 1.0); + if (hardfac > 0.999) { + return step(1e-8, dist); + } + else { + /* Modulate the falloff profile */ + float hardness = 1.0 - hardfac; + dist = pow(dist, mix(0.01, 10.0, hardness)); + return smoothstep(0.0, 1.0, dist); + } +} +#endif + +vec2 gpencil_decode_aspect(int packed_data) +{ + float asp = float(uint(packed_data) & 0x1FFu) * (1.0 / 255.0); + return (asp > 1.0) ? vec2(1.0, (asp - 1.0)) : vec2(asp, 1.0); +} + +float gpencil_decode_uvrot(int packed_data) +{ + uint udata = uint(packed_data); + float uvrot = 1e-8 + float((udata & 0x1FE00u) >> 9u) * (1.0 / 255.0); + return ((udata & 0x20000u) != 0u) ? -uvrot : uvrot; +} + +float gpencil_decode_hardness(int packed_data) +{ + return float((uint(packed_data) & 0x3FC0000u) >> 18u) * (1.0 / 255.0); +} + +vec2 gpencil_project_to_screenspace(vec4 v, vec4 viewport_size) +{ + return ((v.xy / v.w) * 0.5 + 0.5) * viewport_size.xy; +} + +float gpencil_stroke_thickness_modulate(float thickness, vec4 ndc_pos, vec4 viewport_size) +{ + /* Modify stroke thickness by object and layer factors. */ + /* TODO */ + thickness = max(1.0, thickness * ObjectGpencilThickness /* + thicknessOffset */); + + if (ObjectGpencilThicknessIsScreenSpace) { + /* Multiply offset by view Z so that offset is constant in screenspace. + * (e.i: does not change with the distance to camera) */ + thickness *= ndc_pos.w; + } + else { + /* World space point size. */ + thickness *= ObjectGpencilWorldScale * ProjectionMatrix[1][1] * viewport_size.y; + } + return thickness; +} + +float gpencil_clamp_small_stroke_thickness(float thickness, vec4 ndc_pos) +{ + /* To avoid aliasing artifacts, we clamp the line thickness and + * reduce its opacity in the fragment shader.*/ + float min_thickness = ndc_pos.w * 1.3; + thickness = max(min_thickness, thickness); + + return thickness; +} + +/** + * Returns value of gl_Position. + * + * To declare in vertex shader. + * in ivec4 ma, ma1, ma2, ma3; + * in vec4 pos, pos1, pos2, pos3, uv1, uv2, col1, col2, fcol1; + * + * All of these attributes are quad loaded the same way + * as GL_LINES_ADJACENCY would feed a geometry shader: + * - ma reference the previous adjacency point. + * - ma1 reference the current line first point. + * - ma2 reference the current line second point. + * - ma3 reference the next adjacency point. + * Note that we are rendering quad instances and not using any index buffer (except for fills). + * + * Material : x is material index, y is stroke_id, z is point_id, + * w is aspect & rotation & hardness packed. + * Position : contains thickness in 4th component. + * UV : xy is UV for fills, z is U of stroke, w is strength. + * + * + * WARNING: Max attribute count is actually 14 because OSX OpenGL implementation + * considers gl_VertexID and gl_InstanceID as vertex attribute. (see T74536) + **/ +vec4 gpencil_vertex(ivec4 ma, + ivec4 ma1, + ivec4 ma2, + ivec4 ma3, + vec4 pos, + vec4 pos1, + vec4 pos2, + vec4 pos3, + vec4 uv1, + vec4 uv2, + vec4 col1, + vec4 col2, + vec4 fcol1, + vec4 viewport_size, + out vec3 out_pos, + out vec3 out_nor, + out vec4 out_color, + out vec2 out_uv, + out vec4 out_sspos, + out vec2 out_aspect, + out vec2 out_thickness) +{ +#define stroke_id1 ma1.y +#define point_id1 ma1.z +#define thickness1 pos1.w +#define thickness2 pos2.w +#define strength1 uv1.w +#define strength2 uv2.w +/* Packed! need to be decoded. */ +#define hardness1 ma1.w +#define hardness2 ma2.w +#define uvrot1 ma1.w +#define aspect1 ma1.w + + /* Trick to detect if a drawcall is stroke or fill. + * This does mean that we need to draw an empty stroke segment before starting + * to draw the real stroke segments. */ + bool is_fill = (gl_InstanceID == 0); + + vec4 out_ndc; + + if (!is_fill) { + int m = ma1.x; + bool is_dot = false; + bool is_squares = false; + + /* Special Case. Stroke with single vert are rendered as dots. Do not discard them. */ + if (!is_dot && ma.x == -1 && ma2.x == -1) { + is_dot = true; + is_squares = false; + } + + /* Endpoints, we discard the vertices. */ + if (ma1.x == -1 || (!is_dot && ma2.x == -1)) { + /* We set the vertex at the camera origin to generate 0 fragments. */ + out_ndc = vec4(0.0, 0.0, -3e36, 0.0); + return out_ndc; + } + + /* Avoid using a vertex attribute for quad positioning. */ + float x = float(gl_VertexID & 1) * 2.0 - 1.0; /* [-1..1] */ + float y = float(gl_VertexID & 2) - 1.0; /* [-1..1] */ + + bool use_curr = is_dot || (x == -1.0); + + vec3 wpos_adj = transform_point(ModelMatrix, (use_curr) ? pos.xyz : pos3.xyz); + vec3 wpos1 = transform_point(ModelMatrix, pos1.xyz); + vec3 wpos2 = transform_point(ModelMatrix, pos2.xyz); + + vec3 T; + if (is_dot) { + /* Shade as facing billboards. */ + T = ViewMatrixInverse[0].xyz; + } + else if (use_curr && ma.x != -1) { + T = wpos1 - wpos_adj; + } + else { + T = wpos2 - wpos1; + } + T = safe_normalize(T); + + vec3 B = cross(T, ViewMatrixInverse[2].xyz); + out_nor = normalize(cross(B, T)); + + vec4 ndc_adj = point_world_to_ndc(wpos_adj); + vec4 ndc1 = point_world_to_ndc(wpos1); + vec4 ndc2 = point_world_to_ndc(wpos2); + + out_ndc = (use_curr) ? ndc1 : ndc2; + out_pos = (use_curr) ? wpos1 : wpos2; + + vec2 ss_adj = gpencil_project_to_screenspace(ndc_adj, viewport_size); + vec2 ss1 = gpencil_project_to_screenspace(ndc1, viewport_size); + vec2 ss2 = gpencil_project_to_screenspace(ndc2, viewport_size); + /* Screenspace Lines tangents. */ + float line_len; + vec2 line = safe_normalize_len(ss2 - ss1, line_len); + vec2 line_adj = safe_normalize((use_curr) ? (ss1 - ss_adj) : (ss_adj - ss2)); + + float thickness = abs((use_curr) ? thickness1 : thickness2); + thickness = gpencil_stroke_thickness_modulate(thickness, out_ndc, viewport_size); + float clamped_thickness = gpencil_clamp_small_stroke_thickness(thickness, out_ndc); + + out_uv = vec2(x, y) * 0.5 + 0.5; + + if (is_dot) { + vec2 x_axis; + { /* GP_STROKE_ALIGNMENT_OBJECT */ + vec4 ndc_x = point_world_to_ndc(wpos1 + ModelMatrix[0].xyz); + vec2 ss_x = gpencil_project_to_screenspace(ndc_x, viewport_size); + x_axis = safe_normalize(ss_x - ss1); + } + + /* Rotation: Encoded as Cos + Sin sign. */ + float uv_rot = gpencil_decode_uvrot(uvrot1); + float rot_sin = sqrt(max(0.0, 1.0 - uv_rot * uv_rot)) * sign(uv_rot); + float rot_cos = abs(uv_rot); + x_axis = mat2(rot_cos, -rot_sin, rot_sin, rot_cos) * x_axis; + /* Rotate 90° Counter-Clockwise. */ + vec2 y_axis = vec2(-x_axis.y, x_axis.x); + + out_aspect = gpencil_decode_aspect(aspect1); + + x *= out_aspect.x; + y *= out_aspect.y; + + /* Invert for vertex shader. */ + out_aspect = 1.0 / out_aspect; + + out_ndc.xy += (x * x_axis + y * y_axis) * viewport_size.zw * clamped_thickness; + + out_sspos.xy = ss1; + out_sspos.zw = ss1 + x_axis * 0.5; + out_thickness.x = (is_squares) ? 1e18 : (clamped_thickness / out_ndc.w); + out_thickness.y = (is_squares) ? 1e18 : (thickness / out_ndc.w); + } + else { + bool is_stroke_start = (ma.x == -1 && x == -1); + bool is_stroke_end = (ma3.x == -1 && x == 1); + + /* Mitter tangent vector. */ + vec2 miter_tan = safe_normalize(line_adj + line); + float miter_dot = dot(miter_tan, line_adj); + /* Break corners after a certain angle to avoid really thick corners. */ + const float miter_limit = 0.5; /* cos(60°) */ + bool miter_break = (miter_dot < miter_limit); + miter_tan = (miter_break || is_stroke_start || is_stroke_end) ? line : + (miter_tan / miter_dot); + /* Rotate 90° Counter-Clockwise. */ + vec2 miter = vec2(-miter_tan.y, miter_tan.x); + + out_sspos.xy = ss1; + out_sspos.zw = ss2; + out_thickness.x = clamped_thickness / out_ndc.w; + out_thickness.y = thickness / out_ndc.w; + out_aspect = vec2(1.0); + + vec2 screen_ofs = miter * y; + + /* Reminder: we packed the cap flag into the sign of strength and thickness sign. */ + if ((is_stroke_start && strength1 > 0.0) || (is_stroke_end && thickness1 > 0.0) || + (miter_break && !is_stroke_start && !is_stroke_end)) { + screen_ofs += line * x; + } + + out_ndc.xy += screen_ofs * viewport_size.zw * clamped_thickness; + + out_uv.x = (use_curr) ? uv1.z : uv2.z; + } + } + else { + /* Fill vertex. */ + out_pos = transform_point(ModelMatrix, pos1.xyz); + out_ndc = point_world_to_ndc(out_pos); + out_uv = uv1.xy; + out_thickness.x = 1e18; + out_thickness.y = 1e20; + out_aspect = vec2(1.0); + out_sspos = vec4(0.0); + + /* Flat normal following camera and object bounds. */ + vec3 V = cameraVec(ModelMatrix[3].xyz); + vec3 N = normal_world_to_object(V); + N *= OrcoTexCoFactors[1].xyz; + N = normal_object_to_world(N); + out_nor = safe_normalize(N); + + /* Decode fill opacity. */ + out_color = vec4(fcol1.rgb, floor(fcol1.a / 10.0)); + out_color.a /= 10000.0; + + /* We still offset the fills a little to avoid overlaps */ + out_ndc.z += 0.000002; + } + return out_ndc; +} diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl index ed8b8aeb849..25e4abc5ebc 100644 --- a/source/blender/draw/intern/shaders/common_hair_lib.glsl +++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl @@ -162,6 +162,14 @@ float hair_shaperadius(float shape, float root, float tip, float time) in float dummy; # endif +/* From http://libnoise.sourceforge.net/noisegen/index.html */ +float hair_integer_noise(int n) +{ + n = (n >> 13) ^ n; + int nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7FFFFFFF; + return (float(nn) / 1073741824.0); +} + void hair_get_pos_tan_binor_time(bool is_persp, mat4 invmodel_mat, vec3 camera_pos, @@ -213,6 +221,11 @@ void hair_get_pos_tan_binor_time(bool is_persp, wpos += wbinor * thick_time * scale; } + else { + /* Random hair time in V direction (binormal). Mimics cylinder shading. */ + thick_time = hair_integer_noise(hair_get_strand_id()); + thick_time = thickness * (thick_time * 2.0 - 1.0); + } } float hair_get_customdata_float(const samplerBuffer cd_buf) diff --git a/source/blender/draw/intern/shaders/common_intersection_lib.glsl b/source/blender/draw/intern/shaders/common_intersection_lib.glsl new file mode 100644 index 00000000000..c8a97808247 --- /dev/null +++ b/source/blender/draw/intern/shaders/common_intersection_lib.glsl @@ -0,0 +1,209 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) + +/** Require/include common_debug_lib.glsl before this file to debug draw intersections volumes. */ + +#if defined(DEBUG_DRAW_ISECT) && !defined(DEBUG_DRAW) +# error "You must include common_debug_lib.glsl before this file to enabled debug draw" +#endif + +/* ---------------------------------------------------------------------- */ +/** \name Plane extraction functions. + * \{ */ + +/** \a v1 and \a v2 are vectors on the plane. \a p is a point on the plane. */ +vec4 plane_setup(vec3 p, vec3 v1, vec3 v2) +{ + vec3 normal_to_plane = normalize(cross(v1, v2)); + return vec4(normal_to_plane, -dot(normal_to_plane, p)); +} + +void planes_setup(Pyramid shape, out vec4 planes[5]) +{ + vec3 A1 = shape.corners[1] - shape.corners[0]; + vec3 A2 = shape.corners[2] - shape.corners[0]; + vec3 A3 = shape.corners[3] - shape.corners[0]; + vec3 A4 = shape.corners[4] - shape.corners[0]; + vec3 S1 = shape.corners[4] - shape.corners[1]; + vec3 S2 = shape.corners[2] - shape.corners[1]; + + planes[0] = plane_setup(shape.corners[0], A2, A1); + planes[1] = plane_setup(shape.corners[0], A3, A2); + planes[2] = plane_setup(shape.corners[0], A4, A3); + planes[3] = plane_setup(shape.corners[0], A1, A4); + planes[4] = plane_setup(shape.corners[1], S2, S1); +} + +void planes_setup(Box shape, out vec4 planes[6]) +{ + vec3 A1 = shape.corners[1] - shape.corners[0]; + vec3 A3 = shape.corners[3] - shape.corners[0]; + vec3 A4 = shape.corners[4] - shape.corners[0]; + + planes[0] = plane_setup(shape.corners[0], A1, A3); + planes[1] = plane_setup(shape.corners[0], A3, A4); + planes[2] = plane_setup(shape.corners[0], A4, A1); + planes[3] = vec4(-planes[0].xyz, -dot(-planes[0].xyz, shape.corners[6])); + planes[4] = vec4(-planes[1].xyz, -dot(-planes[1].xyz, shape.corners[6])); + planes[5] = vec4(-planes[2].xyz, -dot(-planes[2].xyz, shape.corners[6])); +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Intersection functions. + * \{ */ + +#define TEST_ENABLED 1 +#define FALSE_POSITIVE_REJECTION 1 + +bool intersect_view(Pyramid pyramid) +{ + /** + * Frustum vs. Pyramid test from + * https://www.yosoygames.com.ar/wp/2016/12/frustum-vs-pyramid-intersection-also-frustum-vs-frustum/ + */ + bool intersects = true; + + /* Do Pyramid vertices vs Frustum planes. */ + for (int p = 0; p < 6 && intersects; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 5 && !is_any_vertex_on_positive_side; ++v) { + if (dot(frustum_planes[p], vec4(pyramid.corners[v], 1.0)) > 0.0) { + is_any_vertex_on_positive_side = true; + } + } + if (!is_any_vertex_on_positive_side) { + intersects = false; + } + } + + if (intersects) { + vec4 pyramid_planes[5]; + planes_setup(pyramid, pyramid_planes); + /* Now do Frustum vertices vs Pyramid planes. */ + for (int p = 0; p < 5 && intersects; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8 && !is_any_vertex_on_positive_side; ++v) { + if (dot(pyramid_planes[p], vec4(frustum_corners[v], 1.0)) > 0.0) { + is_any_vertex_on_positive_side = true; + } + } + if (!is_any_vertex_on_positive_side) { + intersects = false; + } + } + } + +#if defined(DEBUG_DRAW) && defined(DEBUG_DRAW_ISECT) + drw_debug(pyramid, intersects ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1)); +#endif + return intersects; +} + +bool intersect_view(Box box) +{ + bool intersects = true; + + /* Do Box vertices vs Frustum planes. */ + for (int p = 0; p < 6 && intersects; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8 && !is_any_vertex_on_positive_side; ++v) { + if (dot(frustum_planes[p], vec4(box.corners[v], 1.0)) > 0.0) { + is_any_vertex_on_positive_side = true; + } + } + if (!is_any_vertex_on_positive_side) { + intersects = false; + } + } + + if (intersects) { + vec4 box_planes[6]; + planes_setup(box, box_planes); + /* Now do Frustum vertices vs Box planes. */ + for (int p = 0; p < 6 && intersects; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8 && !is_any_vertex_on_positive_side; ++v) { + if (dot(box_planes[p], vec4(frustum_corners[v], 1.0)) > 0.0) { + is_any_vertex_on_positive_side = true; + } + } + if (!is_any_vertex_on_positive_side) { + intersects = false; + } + } + } + +#if defined(DEBUG_DRAW) && defined(DEBUG_DRAW_ISECT) + if (intersects) { + drw_debug(box, intersects ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1)); + } +#endif + return intersects; +} + +bool intersect_view(Sphere sph) +{ + bool intersects = true; + + for (int p = 0; p < 6 && intersects; ++p) { + float dist_to_plane = dot(frustum_planes[p], vec4(sph.center, 1.0)); + if (dist_to_plane < -sph.radius) { + intersects = false; + } + } + + /* TODO reject false positive. */ + +#if defined(DEBUG_DRAW) && defined(DEBUG_DRAW_ISECT) + if (intersects) { + drw_debug(sph, intersects ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1)); + } +#endif + return intersects; +} + +bool intersect(Pyramid pyramid, Box box) +{ + bool intersects = true; + + vec4 box_planes[6]; + planes_setup(box, box_planes); + /* Do Pyramid vertices vs Box planes. */ + for (int p = 0; p < 6 && intersects; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 5 && !is_any_vertex_on_positive_side; ++v) { + if (dot(box_planes[p], vec4(pyramid.corners[v], 1.0)) > 0.0) { + is_any_vertex_on_positive_side = true; + } + } + if (!is_any_vertex_on_positive_side) { + intersects = false; + } + } + + if (intersects) { + vec4 pyramid_planes[5]; + planes_setup(pyramid, pyramid_planes); + /* Now do Box vertices vs Pyramid planes. */ + for (int p = 0; p < 5 && intersects; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8 && !is_any_vertex_on_positive_side; ++v) { + if (dot(pyramid_planes[p], vec4(box.corners[v], 1.0)) > 0.0) { + is_any_vertex_on_positive_side = true; + } + } + if (!is_any_vertex_on_positive_side) { + intersects = false; + } + } + } + return intersects; +} + +#undef TEST_ENABLED +#undef FALSE_POSITIVE_REJECTION + +/** \} */ diff --git a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl index 7b701d1d81b..6c6ee599168 100644 --- a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl @@ -106,17 +106,17 @@ void make_orthonormal_basis(vec3 N, out vec3 T, out vec3 B) /* ---- Encode / Decode Normal buffer data ---- */ /* From http://aras-p.info/texts/CompactNormalStorage.html * Using Method #4: Spheremap Transform */ -vec2 normal_encode(vec3 n, vec3 view) +vec2 normal_encode(vec3 n) { - float p = sqrt(n.z * 8.0 + 8.0); + float p = safe_sqrt(n.z * 8.0 + 8.0); return n.xy / p + 0.5; } -vec3 normal_decode(vec2 enc, vec3 view) +vec3 normal_decode(vec2 enc) { vec2 fenc = enc * 4.0 - 2.0; float f = dot(fenc, fenc); - float g = sqrt(1.0 - f / 4.0); + float g = safe_sqrt(1.0 - f / 4.0); vec3 n; n.xy = fenc * g; n.z = 1 - f / 2; @@ -134,3 +134,114 @@ vec3 world_to_tangent(vec3 vector, vec3 N, vec3 T, vec3 B) } /** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Shapes + * \{ */ + +struct Sphere { + vec3 center; + float radius; +}; + +struct Box { + vec3 corners[8]; +}; + +struct Pyramid { + /* Apex is the first. Base vertices are in clockwise order from front view. */ + vec3 corners[5]; +}; + +#if defined(DEBUG_DRAW) + +void drw_debug(Box shape, vec4 color) +{ + drw_debug_quad(shape.corners[0], shape.corners[1], shape.corners[2], shape.corners[3], color); + drw_debug_line(shape.corners[0], shape.corners[4], color); + drw_debug_line(shape.corners[1], shape.corners[5], color); + drw_debug_line(shape.corners[2], shape.corners[6], color); + drw_debug_line(shape.corners[3], shape.corners[7], color); + drw_debug_quad(shape.corners[4], shape.corners[5], shape.corners[6], shape.corners[7], color); +} + +void drw_debug(Pyramid shape, vec4 color) +{ + drw_debug_line(shape.corners[0], shape.corners[1], color); + drw_debug_line(shape.corners[0], shape.corners[2], color); + drw_debug_line(shape.corners[0], shape.corners[3], color); + drw_debug_line(shape.corners[0], shape.corners[4], color); + drw_debug_quad(shape.corners[1], shape.corners[2], shape.corners[3], shape.corners[4], color); +} + +void drw_debug(Sphere shape, vec4 color) +{ + /* TODO(fclem): Counld be better. */ + drw_debug_point(shape.center, shape.radius, color); +} + +#endif + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Axis Aligned Bound Box + * \{ */ + +struct AABB { + vec3 min, max; +}; + +AABB init_min_max() +{ + AABB aabb; + aabb.min = vec3(1.0e30); + aabb.max = vec3(-1.0e30); + return aabb; +} + +void merge(inout AABB aabb, vec3 v) +{ + /* Fix for perspective where */ + aabb.min = min(aabb.min, v); + aabb.max = max(aabb.max, v); +} + +bool intersect(AABB a, AABB b) +{ + return all(greaterThanEqual(min(a.max, b.max), max(a.min, b.min))); +} + +bool intersect(AABB a, AABB b, out AABB c) +{ + bool isect = intersect(a, b); + c.min = max(a.min, b.min); + c.max = min(a.max, b.max); + return isect; +} + +Box to_box(AABB aabb) +{ + Box box; + box.corners[0] = aabb.min; + box.corners[1] = vec3(aabb.max.x, aabb.min.y, aabb.min.z); + box.corners[2] = vec3(aabb.max.x, aabb.max.y, aabb.min.z); + box.corners[3] = vec3(aabb.min.x, aabb.max.y, aabb.min.z); + box.corners[4] = vec3(aabb.min.x, aabb.min.y, aabb.max.z); + box.corners[5] = vec3(aabb.max.x, aabb.min.y, aabb.max.z); + box.corners[6] = aabb.max; + box.corners[7] = vec3(aabb.min.x, aabb.max.y, aabb.max.z); + return box; +} + +#if defined(DEBUG_DRAW) + +void drw_debug(AABB shape, vec4 color) +{ + Box box = to_box(shape); + drw_debug(box, color); +} + +#endif + +/** \} */ diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl index 479f9cd1827..e2941f1b049 100644 --- a/source/blender/draw/intern/shaders/common_math_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_lib.glsl @@ -84,10 +84,13 @@ float avg(vec4 v) { return dot(vec4(1.0 / 4.0), v); } float safe_rcp(float a) { return (a != 0.0) ? (1.0 / a) : 0.0; } vec2 safe_rcp(vec2 a) { return mix(vec2(0.0), (1.0 / a), notEqual(a, vec2(0.0))); } +vec3 safe_rcp(vec3 a) { return mix(vec3(0.0), (1.0 / a), notEqual(a, vec3(0.0))); } vec4 safe_rcp(vec4 a) { return mix(vec4(0.0), (1.0 / a), notEqual(a, vec4(0.0))); } float safe_sqrt(float a) { return sqrt(max(a, 0.0)); } +float safe_acos(float a) { return acos(clamp(a, -1.0, 1.0)); } + float sqr(float a) { return a * a; } vec2 sqr(vec2 a) { return a * a; } vec3 sqr(vec3 a) { return a * a; } @@ -101,6 +104,12 @@ float pow8(float x) { return sqr(sqr(sqr(x))); } float len_squared(vec3 a) { return dot(a, a); } float len_squared(vec2 a) { return dot(a, a); } +bool flag_test(uint flag, uint val) { return (flag & val) != 0u; } +bool flag_test(int flag, int val) { return (flag & val) != 0; } + +void set_flag_from_test(inout uint value, bool test, uint flag) { if (test) { value |= flag; } else { value &= ~flag; } } +void set_flag_from_test(inout int value, bool test, int flag) { if (test) { value |= flag; } else { value &= ~flag; } } + #define weighted_sum(val0, val1, val2, val3, weights) ((val0 * weights[0] + val1 * weights[1] + val2 * weights[2] + val3 * weights[3]) * safe_rcp(sum(weights))); #define weighted_sum_array(val, weights) ((val[0] * weights[0] + val[1] * weights[1] + val[2] * weights[2] + val[3] * weights[3]) * safe_rcp(sum(weights))); @@ -108,6 +117,38 @@ float len_squared(vec2 a) { return dot(a, a); } #define saturate(a) clamp(a, 0.0, 1.0) +#define in_range_inclusive(val, min_v, max_v) \ + (all(greaterThanEqual(val, min_v)) && all(lessThanEqual(val, max_v))) +#define in_range_exclusive(val, min_v, max_v) \ + (all(greaterThan(val, min_v)) && all(lessThan(val, max_v))) + +uint divide_ceil_u(uint visible_count, uint divisor) +{ + return (visible_count + (divisor - 1u)) / divisor; +} + +int divide_ceil_i(int visible_count, int divisor) +{ + return (visible_count + (divisor - 1)) / divisor; +} + +uint bit_field_mask(uint bit_width, uint bit_min) +{ + /* Cannot bit shift more than 31 positions. */ + uint mask = (bit_width > 31u) ? 0x0u : (0xFFFFFFFFu << bit_width); + return ~mask << bit_min; +} + +uvec2 unpackUvec2x16(uint data) +{ + return uvec2(data >> 16u, data & 0xFFFFu); +} + +uint packUvec2x16(uvec2 data) +{ + return (data.x << 16u) | (data.y & 0xFFFFu); +} + float distance_squared(vec2 a, vec2 b) { a -= b; @@ -129,6 +170,21 @@ vec3 safe_normalize(vec3 v) return v / len; } +vec2 safe_normalize_len(vec2 v, out float len) +{ + len = length(v); + if (isnan(len) || len == 0.0) { + return vec2(1.0, 0.0); + } + return v / len; +} + +vec2 safe_normalize(vec2 v) +{ + float len; + return safe_normalize_len(v, len); +} + vec3 normalize_len(vec3 v, out float len) { len = length(v); @@ -140,6 +196,11 @@ vec4 safe_color(vec4 c) /* Clamp to avoid black square artifacts if a pixel goes NaN. */ return clamp(c, vec4(0.0), vec4(1e20)); /* 1e20 arbitrary. */ } +vec3 safe_color(vec3 c) +{ + /* Clamp to avoid black square artifacts if a pixel goes NaN. */ + return clamp(c, vec3(0.0), vec3(1e20)); /* 1e20 arbitrary. */ +} /** \} */ @@ -187,3 +248,14 @@ vec3 neon_gradient(float t) { return clamp(vec3(t * 1.3 + 0.1, sqr(abs(0.43 - t) * 1.7), (1.0 - t) * 1.7), 0.0, 1.0); } +vec3 heatmap_gradient(float t) +{ + return saturate((pow(t, 1.5) * 0.8 + 0.2) * vec3(smoothstep(0.0, 0.35, t) + t * 0.5, + smoothstep(0.5, 1.0, t), + max(1.0 - t * 1.7, t * 7.0 - 6.0))); +} +vec3 hue_gradient(float t) +{ + vec3 p = abs(fract(t + vec3(1.0, 2.0 / 3.0, 1.0 / 3.0)) * 6.0 - 3.0); + return (clamp(p - 1.0, 0.0, 1.0)); +} diff --git a/source/blender/draw/intern/shaders/common_obinfos_lib.glsl b/source/blender/draw/intern/shaders/common_obinfos_lib.glsl new file mode 100644 index 00000000000..a56a4dae3c9 --- /dev/null +++ b/source/blender/draw/intern/shaders/common_obinfos_lib.glsl @@ -0,0 +1,25 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +struct ObjectInfos { + vec4 drw_OrcoTexCoFactors[2]; + vec4 drw_ObjectColor; + vec4 drw_Infos; +}; + +layout(std140) uniform infoBlock +{ + /* DRW_RESOURCE_CHUNK_LEN = 512 */ + ObjectInfos drw_infos[512]; +}; + +#define OrcoTexCoFactors (drw_infos[resource_id].drw_OrcoTexCoFactors) +#define ObjectInfo (drw_infos[resource_id].drw_Infos) +#define ObjectColor (drw_infos[resource_id].drw_ObjectColor) +#define ObjectGpencilWorldScale abs(drw_infos[resource_id].drw_OrcoTexCoFactors[0].w) +#define ObjectGpencilThicknessIsScreenSpace \ + (drw_infos[resource_id].drw_OrcoTexCoFactors[0].w < 0.0) +#define ObjectGpencilDepthOrder2D (drw_infos[resource_id].drw_OrcoTexCoFactors[1].w < 0.0) +#define ObjectGpencilThickness abs(drw_infos[resource_id].drw_OrcoTexCoFactors[1].w) + +#define OBINFO_LIB diff --git a/source/blender/draw/intern/shaders/common_uniform_attribute_lib.glsl b/source/blender/draw/intern/shaders/common_uniform_attribute_lib.glsl new file mode 100644 index 00000000000..b34182d77ab --- /dev/null +++ b/source/blender/draw/intern/shaders/common_uniform_attribute_lib.glsl @@ -0,0 +1,12 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +/* UniformAttributes is defined by codegen. */ + +layout(std140) uniform uniformAttrs +{ + /* DRW_RESOURCE_CHUNK_LEN = 512 */ + UniformAttributes uniform_attrs[512]; +}; + +#define UNIFORM_ATTR_UBO uniform_attrs[resource_id] diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl index d4d18b95238..573ad046ea9 100644 --- a/source/blender/draw/intern/shaders/common_view_lib.glsl +++ b/source/blender/draw/intern/shaders/common_view_lib.glsl @@ -22,6 +22,11 @@ layout(std140) uniform viewBlock /* TODO: move it elsewhere. */ vec4 CameraTexCoFactors; + vec2 ViewportSize; + vec2 ViewportSizeInverse; + + vec3 frustum_corners[8]; + vec4 frustum_planes[6]; }; #endif /* DRW_SHADER_SHARED_H */ @@ -258,23 +263,25 @@ uniform mat4 ModelMatrixInverse; /* ---- Opengl Depth conversion ---- */ +/* Expects positive near/far values. Returns positive value. */ float linear_depth(bool is_persp, float z, float zf, float zn) { if (is_persp) { return (zn * zf) / (z * (zn - zf) + zf); } else { - return (z * 2.0 - 1.0) * zf; + return z * (zf - zn) + zn; } } +/* Expects positive near/far values. Returns positive value. */ float buffer_depth(bool is_persp, float z, float zf, float zn) { if (is_persp) { return (zf * (zn - z)) / (z * (zn - zf)); } else { - return (z / (zf * 2.0)) + 0.5; + return (z - zn) / (zf - zn); } } diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 49b6b478148..5bca82b52e4 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -62,7 +62,7 @@ set(SRC intern/gpu_batch_utils.c intern/gpu_buffers.c intern/gpu_capabilities.cc - intern/gpu_codegen.c + intern/gpu_codegen.cc intern/gpu_compute.cc intern/gpu_context.cc intern/gpu_debug.cc @@ -373,8 +373,6 @@ set(GLSL_SRC shaders/gpu_shader_cfg_world_clip_lib.glsl shaders/gpu_shader_colorspace_lib.glsl - shaders/gpu_shader_common_obinfos_lib.glsl - intern/gpu_shader_shared_utils.h ) diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index 3555b957d67..f143730c5cb 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -29,6 +29,7 @@ #include "BLI_sys_types.h" /* for bool */ +#include "GPU_shader.h" /* for GPUShaderSource */ #include "GPU_texture.h" /* for eGPUSamplerState */ #ifdef __cplusplus @@ -84,35 +85,29 @@ typedef enum eGPUType { GPU_ATTR = 3001, } eGPUType; -typedef enum eGPUBuiltin { - GPU_VIEW_MATRIX = (1 << 0), - GPU_OBJECT_MATRIX = (1 << 1), - GPU_INVERSE_VIEW_MATRIX = (1 << 2), - GPU_INVERSE_OBJECT_MATRIX = (1 << 3), - GPU_VIEW_POSITION = (1 << 4), - GPU_VIEW_NORMAL = (1 << 5), - GPU_OBJECT_COLOR = (1 << 6), - GPU_AUTO_BUMPSCALE = (1 << 7), - GPU_CAMERA_TEXCO_FACTORS = (1 << 8), - GPU_PARTICLE_SCALAR_PROPS = (1 << 9), - GPU_PARTICLE_LOCATION = (1 << 10), - GPU_PARTICLE_VELOCITY = (1 << 11), - GPU_PARTICLE_ANG_VELOCITY = (1 << 12), - GPU_LOC_TO_VIEW_MATRIX = (1 << 13), - GPU_INVERSE_LOC_TO_VIEW_MATRIX = (1 << 14), - GPU_OBJECT_INFO = (1 << 15), - GPU_BARYCENTRIC_TEXCO = (1 << 16), - GPU_BARYCENTRIC_DIST = (1 << 17), - GPU_WORLD_NORMAL = (1 << 18), -} eGPUBuiltin; - -typedef enum eGPUMatFlag { +typedef enum eGPUMaterialFlag { GPU_MATFLAG_DIFFUSE = (1 << 0), - GPU_MATFLAG_GLOSSY = (1 << 1), - GPU_MATFLAG_REFRACT = (1 << 2), - GPU_MATFLAG_SSS = (1 << 3), - GPU_MATFLAG_BARYCENTRIC = (1 << 4), -} eGPUMatFlag; + GPU_MATFLAG_SUBSURFACE = (1 << 1), + GPU_MATFLAG_GLOSSY = (1 << 2), + GPU_MATFLAG_REFRACT = (1 << 3), + GPU_MATFLAG_EMISSION = (1 << 4), + GPU_MATFLAG_TRANSPARENT = (1 << 5), + GPU_MATFLAG_HOLDOUT = (1 << 6), + GPU_MATFLAG_SHADER_TO_RGBA = (1 << 7), + + GPU_MATFLAG_OBJECT_INFO = (1 << 10), + GPU_MATFLAG_UNIFORMS_ATTRIB = (1 << 11), + + GPU_MATFLAG_BARYCENTRIC = (1 << 20), + + /* Tells the render engine the material was just compiled or updated. */ + GPU_MATFLAG_UPDATED = (1 << 29), + + /* HACK(fclem) Tells the environment texture node to not bail out if empty. */ + GPU_MATFLAG_LOOKDEV_HACK = (1 << 30), +} eGPUMaterialFlag; + +ENUM_OPERATORS(eGPUMaterialFlag, GPU_MATFLAG_LOOKDEV_HACK); typedef struct GPUNodeStack { eGPUType type; @@ -126,6 +121,7 @@ typedef struct GPUNodeStack { typedef enum eGPUMaterialStatus { GPU_MAT_FAILED = 0, + GPU_MAT_CREATED, GPU_MAT_QUEUED, GPU_MAT_SUCCESS, } eGPUMaterialStatus; @@ -135,12 +131,24 @@ typedef enum eGPUVolumeDefaultValue { GPU_VOLUME_DEFAULT_1, } eGPUVolumeDefaultValue; -typedef void (*GPUMaterialEvalCallbackFn)(GPUMaterial *mat, - int options, - const char **vert_code, - const char **geom_code, - const char **frag_lib, - const char **defines); +typedef struct GPUCodegenOutput { + char *attribs_declare; + char *attribs_interface; + char *attribs_passthrough; + char *attribs_load; + + char *library; + char *uniforms; + /* Nodetree functions calls. */ + char *displacement; + char *surface; + char *volume; + char *thickness; +} GPUCodegenOutput; + +typedef GPUShaderSource (*GPUCodegenCallbackFn)(void *thunk, + GPUMaterial *mat, + const GPUCodegenOutput *codegen); GPUNodeLink *GPU_constant(const float *num); GPUNodeLink *GPU_uniform(const float *num); @@ -159,7 +167,6 @@ GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *ro GPUNodeLink *GPU_volume_grid(GPUMaterial *mat, const char *name, eGPUVolumeDefaultValue default_value); -GPUNodeLink *GPU_builtin(eGPUBuiltin builtin); bool GPU_link(GPUMaterial *mat, const char *name, ...); bool GPU_stack_link(GPUMaterial *mat, @@ -168,18 +175,31 @@ bool GPU_stack_link(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out, ...); +/** + * This is a special function to call the "*_eval" function of a BSDF node. + * \note This must be call right after GPU_stack_link() so that out[0] contains a valid link. + */ +bool GPU_stack_eval_link(GPUMaterial *material, + struct bNode *bnode, + const char *name, + GPUNodeStack *in, + GPUNodeStack *out, + ...); GPUNodeLink *GPU_uniformbuf_link_out(struct GPUMaterial *mat, struct bNode *node, struct GPUNodeStack *stack, int index); -void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link); +void GPU_material_output_surface(GPUMaterial *material, GPUNodeLink *link); +void GPU_material_output_volume(GPUMaterial *material, GPUNodeLink *link); +void GPU_material_output_displacement(GPUMaterial *material, GPUNodeLink *link); +void GPU_material_output_thickness(GPUMaterial *material, GPUNodeLink *link); + void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, int hash); -void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3]); -struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material, - int sample_len, - struct GPUTexture **tex_profile); +void GPU_material_add_closure_eval(GPUMaterial *material, + const GPUNodeLink *weight_link, + const GPUNodeLink *eval_link); /** * High level functions to create and use GPU materials. @@ -196,15 +216,13 @@ GPUMaterial *GPU_material_from_nodetree(struct Scene *scene, struct Material *ma, struct bNodeTree *ntree, struct ListBase *gpumaterials, - const void *engine_type, - int options, - bool is_volume_shader, - const char *vert_code, - const char *geom_code, - const char *frag_lib, - const char *defines, const char *name, - GPUMaterialEvalCallbackFn callback); + uint64_t shader_uuid, + bool is_volume_shader, + bool is_lookdev, + GPUCodegenCallbackFn callback, + void *thunk); + void GPU_material_compile(GPUMaterial *mat); void GPU_material_free(struct ListBase *gpumaterial); @@ -221,6 +239,7 @@ struct Material *GPU_material_get_material(GPUMaterial *material); * Return true if the material compilation has not yet begin or begin. */ eGPUMaterialStatus GPU_material_status(GPUMaterial *mat); +void GPU_material_status_set(GPUMaterial *mat, eGPUMaterialStatus status); struct GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material); /** @@ -231,13 +250,12 @@ struct GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material); void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs); struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void); -bool GPU_material_has_surface_output(GPUMaterial *mat); -bool GPU_material_has_volume_output(GPUMaterial *mat); - bool GPU_material_is_volume_shader(GPUMaterial *mat); -void GPU_material_flag_set(GPUMaterial *mat, eGPUMatFlag flag); -bool GPU_material_flag_get(GPUMaterial *mat, eGPUMatFlag flag); +void GPU_material_flag_set(GPUMaterial *mat, eGPUMaterialFlag flag); +bool GPU_material_flag_get(const GPUMaterial *mat, eGPUMaterialFlag flag); +bool GPU_material_recalc_flag_get(GPUMaterial *mat); +uint64_t GPU_material_uuid_get(GPUMaterial *mat); void GPU_pass_cache_init(void); void GPU_pass_cache_garbage_collect(void); diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 972758febd4..564a02bd068 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -42,6 +42,14 @@ typedef enum eGPUShaderTFBType { GPU_SHADER_TFB_TRIANGLES = 3, } eGPUShaderTFBType; +/* TODO(fclem) Only used by GPUPass but could be used by GPU_shader_create too. */ +typedef struct GPUShaderSource { + char *vertex; + char *geometry; + char *fragment; + char *defines; +} GPUShaderSource; + GPUShader *GPU_shader_create(const char *vertcode, const char *fragcode, const char *geomcode, @@ -161,11 +169,18 @@ typedef enum { GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */ } GPUUniformBlockBuiltin; +typedef enum { + GPU_BUFFER_BLOCK_DEBUG = 0, /* debugBuf */ + + GPU_NUM_BUFFER_BLOCKS, /* Special value, denotes number of builtin buffer blocks. */ +} GPUBufferBlockBuiltin; + void GPU_shader_set_srgb_uniform(GPUShader *shader); int GPU_shader_get_uniform(GPUShader *shader, const char *name); int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin); int GPU_shader_get_builtin_block(GPUShader *shader, int builtin); +int GPU_shader_get_builtin_ssbo(GPUShader *shader, int builtin); /** DEPRECATED: Kept only because of Python GPU API. */ int GPU_shader_get_uniform_block(GPUShader *shader, const char *name); int GPU_shader_get_ssbo(GPUShader *shader, const char *name); diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index 22cd7eab927..bd52b8321bb 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -37,11 +37,14 @@ ENUM_OPERATORS(eGPUWriteMask, GPU_WRITE_COLOR) typedef enum eGPUBarrier { GPU_BARRIER_NONE = 0, - GPU_BARRIER_SHADER_IMAGE_ACCESS = (1 << 0), - GPU_BARRIER_TEXTURE_FETCH = (1 << 1), - GPU_BARRIER_SHADER_STORAGE = (1 << 2), - GPU_BARRIER_VERTEX_ATTRIB_ARRAY = (1 << 3), - GPU_BARRIER_ELEMENT_ARRAY = (1 << 4), + GPU_BARRIER_COMMAND = (1 << 0), + GPU_BARRIER_FRAMEBUFFER = (1 << 1), + GPU_BARRIER_SHADER_IMAGE_ACCESS = (1 << 2), + GPU_BARRIER_SHADER_STORAGE = (1 << 3), + GPU_BARRIER_TEXTURE_FETCH = (1 << 4), + GPU_BARRIER_TEXTURE_UPDATE = (1 << 5), + GPU_BARRIER_VERTEX_ATTRIB_ARRAY = (1 << 6), + GPU_BARRIER_ELEMENT_ARRAY = (1 << 7), } eGPUBarrier; ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_ELEMENT_ARRAY) diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c deleted file mode 100644 index 568462ec2a6..00000000000 --- a/source/blender/gpu/intern/gpu_codegen.c +++ /dev/null @@ -1,1138 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup gpu - * - * Convert material node-trees to GLSL. - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_customdata_types.h" -#include "DNA_image_types.h" - -#include "BLI_blenlib.h" -#include "BLI_dynstr.h" -#include "BLI_ghash.h" -#include "BLI_hash_mm2a.h" -#include "BLI_link_utils.h" -#include "BLI_threads.h" -#include "BLI_utildefines.h" - -#include "PIL_time.h" - -#include "BKE_material.h" - -#include "GPU_capabilities.h" -#include "GPU_material.h" -#include "GPU_shader.h" -#include "GPU_uniform_buffer.h" -#include "GPU_vertex_format.h" - -#include "BLI_sys_types.h" /* for intptr_t support */ - -#include "gpu_codegen.h" -#include "gpu_material_library.h" -#include "gpu_node_graph.h" - -#include <stdarg.h> -#include <string.h> - -extern char datatoc_gpu_shader_codegen_lib_glsl[]; -extern char datatoc_gpu_shader_common_obinfos_lib_glsl[]; - -/* -------------------- GPUPass Cache ------------------ */ -/** - * Internal shader cache: This prevent the shader recompilation / stall when - * using undo/redo AND also allows for GPUPass reuse if the Shader code is the - * same for 2 different Materials. Unused GPUPasses are free by Garbage collection. - */ - -/* Only use one linked-list that contains the GPUPasses grouped by hash. */ -static GPUPass *pass_cache = NULL; -static SpinLock pass_cache_spin; - -static uint32_t gpu_pass_hash(const char *frag_gen, const char *defs, ListBase *attributes) -{ - BLI_HashMurmur2A hm2a; - BLI_hash_mm2a_init(&hm2a, 0); - BLI_hash_mm2a_add(&hm2a, (uchar *)frag_gen, strlen(frag_gen)); - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, attributes) { - BLI_hash_mm2a_add(&hm2a, (uchar *)attr->name, strlen(attr->name)); - } - if (defs) { - BLI_hash_mm2a_add(&hm2a, (uchar *)defs, strlen(defs)); - } - - return BLI_hash_mm2a_end(&hm2a); -} - -/* Search by hash only. Return first pass with the same hash. - * There is hash collision if (pass->next && pass->next->hash == hash) */ -static GPUPass *gpu_pass_cache_lookup(uint32_t hash) -{ - BLI_spin_lock(&pass_cache_spin); - /* Could be optimized with a Lookup table. */ - for (GPUPass *pass = pass_cache; pass; pass = pass->next) { - if (pass->hash == hash) { - BLI_spin_unlock(&pass_cache_spin); - return pass; - } - } - BLI_spin_unlock(&pass_cache_spin); - return NULL; -} - -/* Check all possible passes with the same hash. */ -static GPUPass *gpu_pass_cache_resolve_collision(GPUPass *pass, - const char *vert, - const char *geom, - const char *frag, - const char *defs, - uint32_t hash) -{ - BLI_spin_lock(&pass_cache_spin); - /* Collision, need to `strcmp` the whole shader. */ - for (; pass && (pass->hash == hash); pass = pass->next) { - if ((defs != NULL) && (!STREQ(pass->defines, defs))) { /* Pass */ - } - else if ((geom != NULL) && (!STREQ(pass->geometrycode, geom))) { /* Pass */ - } - else if ((!STREQ(pass->fragmentcode, frag) == 0) && (STREQ(pass->vertexcode, vert))) { - BLI_spin_unlock(&pass_cache_spin); - return pass; - } - } - BLI_spin_unlock(&pass_cache_spin); - return NULL; -} - -/* GLSL code generation */ - -static void codegen_convert_datatype(DynStr *ds, int from, int to, const char *tmp, int id) -{ - char name[1024]; - - BLI_snprintf(name, sizeof(name), "%s%d", tmp, id); - - if (from == to) { - BLI_dynstr_append(ds, name); - } - else if (to == GPU_FLOAT) { - if (from == GPU_VEC4) { - BLI_dynstr_appendf(ds, "dot(%s.rgb, vec3(0.2126, 0.7152, 0.0722))", name); - } - else if (from == GPU_VEC3) { - BLI_dynstr_appendf(ds, "(%s.r + %s.g + %s.b) / 3.0", name, name, name); - } - else if (from == GPU_VEC2) { - BLI_dynstr_appendf(ds, "%s.r", name); - } - } - else if (to == GPU_VEC2) { - if (from == GPU_VEC4) { - BLI_dynstr_appendf(ds, "vec2((%s.r + %s.g + %s.b) / 3.0, %s.a)", name, name, name, name); - } - else if (from == GPU_VEC3) { - BLI_dynstr_appendf(ds, "vec2((%s.r + %s.g + %s.b) / 3.0, 1.0)", name, name, name); - } - else if (from == GPU_FLOAT) { - BLI_dynstr_appendf(ds, "vec2(%s, 1.0)", name); - } - } - else if (to == GPU_VEC3) { - if (from == GPU_VEC4) { - BLI_dynstr_appendf(ds, "%s.rgb", name); - } - else if (from == GPU_VEC2) { - BLI_dynstr_appendf(ds, "vec3(%s.r, %s.r, %s.r)", name, name, name); - } - else if (from == GPU_FLOAT) { - BLI_dynstr_appendf(ds, "vec3(%s, %s, %s)", name, name, name); - } - } - else if (to == GPU_VEC4) { - if (from == GPU_VEC3) { - BLI_dynstr_appendf(ds, "vec4(%s, 1.0)", name); - } - else if (from == GPU_VEC2) { - BLI_dynstr_appendf(ds, "vec4(%s.r, %s.r, %s.r, %s.g)", name, name, name, name); - } - else if (from == GPU_FLOAT) { - BLI_dynstr_appendf(ds, "vec4(%s, %s, %s, 1.0)", name, name, name); - } - } - else if (to == GPU_CLOSURE) { - if (from == GPU_VEC4) { - BLI_dynstr_appendf(ds, "closure_emission(%s.rgb)", name); - } - else if (from == GPU_VEC3) { - BLI_dynstr_appendf(ds, "closure_emission(%s.rgb)", name); - } - else if (from == GPU_VEC2) { - BLI_dynstr_appendf(ds, "closure_emission(%s.rrr)", name); - } - else if (from == GPU_FLOAT) { - BLI_dynstr_appendf(ds, "closure_emission(vec3(%s, %s, %s))", name, name, name); - } - } - else { - BLI_dynstr_append(ds, name); - } -} - -static void codegen_print_datatype(DynStr *ds, const eGPUType type, float *data) -{ - int i; - - BLI_dynstr_appendf(ds, "%s(", gpu_data_type_to_string(type)); - - for (i = 0; i < type; i++) { - BLI_dynstr_appendf(ds, "%.12f", data[i]); - if (i == type - 1) { - BLI_dynstr_append(ds, ")"); - } - else { - BLI_dynstr_append(ds, ", "); - } - } -} - -static const char *gpu_builtin_name(eGPUBuiltin builtin) -{ - if (builtin == GPU_VIEW_MATRIX) { - return "unfviewmat"; - } - if (builtin == GPU_OBJECT_MATRIX) { - return "unfobmat"; - } - if (builtin == GPU_INVERSE_VIEW_MATRIX) { - return "unfinvviewmat"; - } - if (builtin == GPU_INVERSE_OBJECT_MATRIX) { - return "unfinvobmat"; - } - if (builtin == GPU_LOC_TO_VIEW_MATRIX) { - return "unflocaltoviewmat"; - } - if (builtin == GPU_INVERSE_LOC_TO_VIEW_MATRIX) { - return "unfinvlocaltoviewmat"; - } - if (builtin == GPU_VIEW_POSITION) { - return "varposition"; - } - if (builtin == GPU_WORLD_NORMAL) { - return "varwnormal"; - } - if (builtin == GPU_VIEW_NORMAL) { - return "varnormal"; - } - if (builtin == GPU_OBJECT_COLOR) { - return "unfobjectcolor"; - } - if (builtin == GPU_AUTO_BUMPSCALE) { - return "unfobautobumpscale"; - } - if (builtin == GPU_CAMERA_TEXCO_FACTORS) { - return "unfcameratexfactors"; - } - if (builtin == GPU_PARTICLE_SCALAR_PROPS) { - return "unfparticlescalarprops"; - } - if (builtin == GPU_PARTICLE_LOCATION) { - return "unfparticleco"; - } - if (builtin == GPU_PARTICLE_VELOCITY) { - return "unfparticlevel"; - } - if (builtin == GPU_PARTICLE_ANG_VELOCITY) { - return "unfparticleangvel"; - } - if (builtin == GPU_OBJECT_INFO) { - return "unfobjectinfo"; - } - if (builtin == GPU_BARYCENTRIC_TEXCO) { - return "unfbarycentrictex"; - } - if (builtin == GPU_BARYCENTRIC_DIST) { - return "unfbarycentricdist"; - } - return ""; -} - -static void codegen_set_unique_ids(GPUNodeGraph *graph) -{ - int id = 1; - - LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { - /* set id for unique names of uniform variables */ - input->id = id++; - } - - LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { - /* set id for unique names of tmp variables storing output */ - output->id = id++; - } - } -} - -/** - * It will create an UBO for GPUMaterial if there is any GPU_DYNAMIC_UBO. - */ -static int codegen_process_uniforms_functions(GPUMaterial *material, - DynStr *ds, - GPUNodeGraph *graph) -{ - const char *name; - int builtins = 0; - ListBase ubo_inputs = {NULL, NULL}; - - /* Textures */ - LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph->textures) { - if (tex->colorband) { - BLI_dynstr_appendf(ds, "uniform sampler1DArray %s;\n", tex->sampler_name); - } - else if (tex->tiled_mapping_name[0]) { - BLI_dynstr_appendf(ds, "uniform sampler2DArray %s;\n", tex->sampler_name); - BLI_dynstr_appendf(ds, "uniform sampler1DArray %s;\n", tex->tiled_mapping_name); - } - else { - BLI_dynstr_appendf(ds, "uniform sampler2D %s;\n", tex->sampler_name); - } - } - - /* Volume Grids */ - LISTBASE_FOREACH (GPUMaterialVolumeGrid *, grid, &graph->volume_grids) { - BLI_dynstr_appendf(ds, "uniform sampler3D %s;\n", grid->sampler_name); - BLI_dynstr_appendf(ds, "uniform mat4 %s = mat4(0.0);\n", grid->transform_name); - } - - /* Print other uniforms */ - - LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { - if (input->source == GPU_SOURCE_BUILTIN) { - /* only define each builtin uniform/varying once */ - if (!(builtins & input->builtin)) { - builtins |= input->builtin; - name = gpu_builtin_name(input->builtin); - - if (BLI_str_startswith(name, "unf")) { - BLI_dynstr_appendf(ds, "uniform %s %s;\n", gpu_data_type_to_string(input->type), name); - } - else { - BLI_dynstr_appendf(ds, "in %s %s;\n", gpu_data_type_to_string(input->type), name); - } - } - } - else if (input->source == GPU_SOURCE_STRUCT) { - /* Add other struct here if needed. */ - BLI_dynstr_appendf(ds, "Closure strct%d = CLOSURE_DEFAULT;\n", input->id); - } - else if (input->source == GPU_SOURCE_UNIFORM) { - if (!input->link) { - /* We handle the UBOuniforms separately. */ - BLI_addtail(&ubo_inputs, BLI_genericNodeN(input)); - } - } - else if (input->source == GPU_SOURCE_CONSTANT) { - BLI_dynstr_appendf( - ds, "const %s cons%d = ", gpu_data_type_to_string(input->type), input->id); - codegen_print_datatype(ds, input->type, input->vec); - BLI_dynstr_append(ds, ";\n"); - } - } - } - - /* Handle the UBO block separately. */ - if ((material != NULL) && !BLI_listbase_is_empty(&ubo_inputs)) { - GPU_material_uniform_buffer_create(material, &ubo_inputs); - - /* Inputs are sorted */ - BLI_dynstr_appendf(ds, "\nlayout (std140) uniform %s {\n", GPU_UBO_BLOCK_NAME); - - LISTBASE_FOREACH (LinkData *, link, &ubo_inputs) { - GPUInput *input = (GPUInput *)(link->data); - BLI_dynstr_appendf(ds, " %s unf%d;\n", gpu_data_type_to_string(input->type), input->id); - } - BLI_dynstr_append(ds, "};\n"); - BLI_freelistN(&ubo_inputs); - } - - /* Generate the uniform attribute UBO if necessary. */ - if (!BLI_listbase_is_empty(&graph->uniform_attrs.list)) { - BLI_dynstr_append(ds, "\nstruct UniformAttributes {\n"); - LISTBASE_FOREACH (GPUUniformAttr *, attr, &graph->uniform_attrs.list) { - BLI_dynstr_appendf(ds, " vec4 attr%d;\n", attr->id); - } - BLI_dynstr_append(ds, "};\n"); - BLI_dynstr_appendf(ds, "layout (std140) uniform %s {\n", GPU_ATTRIBUTE_UBO_BLOCK_NAME); - BLI_dynstr_append(ds, " UniformAttributes uniform_attrs[DRW_RESOURCE_CHUNK_LEN];\n"); - BLI_dynstr_append(ds, "};\n"); - BLI_dynstr_append(ds, "#define GET_UNIFORM_ATTR(name) (uniform_attrs[resource_id].name)\n"); - } - - BLI_dynstr_append(ds, "\n"); - - return builtins; -} - -static void codegen_declare_tmps(DynStr *ds, GPUNodeGraph *graph) -{ - LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - /* declare temporary variables for node output storage */ - LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { - if (output->type == GPU_CLOSURE) { - BLI_dynstr_appendf(ds, " Closure tmp%d;\n", output->id); - } - else { - BLI_dynstr_appendf(ds, " %s tmp%d;\n", gpu_data_type_to_string(output->type), output->id); - } - } - } - BLI_dynstr_append(ds, "\n"); -} - -static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph) -{ - LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - BLI_dynstr_appendf(ds, " %s(", node->name); - - LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { - if (input->source == GPU_SOURCE_TEX) { - BLI_dynstr_append(ds, input->texture->sampler_name); - } - else if (input->source == GPU_SOURCE_TEX_TILED_MAPPING) { - BLI_dynstr_append(ds, input->texture->tiled_mapping_name); - } - else if (input->source == GPU_SOURCE_VOLUME_GRID) { - BLI_dynstr_append(ds, input->volume_grid->sampler_name); - } - else if (input->source == GPU_SOURCE_VOLUME_GRID_TRANSFORM) { - BLI_dynstr_append(ds, input->volume_grid->transform_name); - } - else if (input->source == GPU_SOURCE_OUTPUT) { - codegen_convert_datatype( - ds, input->link->output->type, input->type, "tmp", input->link->output->id); - } - else if (input->source == GPU_SOURCE_BUILTIN) { - /* TODO(fclem): get rid of that. */ - if (input->builtin == GPU_INVERSE_VIEW_MATRIX) { - BLI_dynstr_append(ds, "viewinv"); - } - else if (input->builtin == GPU_VIEW_MATRIX) { - BLI_dynstr_append(ds, "viewmat"); - } - else if (input->builtin == GPU_CAMERA_TEXCO_FACTORS) { - BLI_dynstr_append(ds, "camtexfac"); - } - else if (input->builtin == GPU_LOC_TO_VIEW_MATRIX) { - BLI_dynstr_append(ds, "localtoviewmat"); - } - else if (input->builtin == GPU_INVERSE_LOC_TO_VIEW_MATRIX) { - BLI_dynstr_append(ds, "invlocaltoviewmat"); - } - else if (input->builtin == GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "barycentricDist"); - } - else if (input->builtin == GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "barytexco"); - } - else if (input->builtin == GPU_OBJECT_MATRIX) { - BLI_dynstr_append(ds, "objmat"); - } - else if (input->builtin == GPU_OBJECT_INFO) { - BLI_dynstr_append(ds, "ObjectInfo"); - } - else if (input->builtin == GPU_OBJECT_COLOR) { - BLI_dynstr_append(ds, "ObjectColor"); - } - else if (input->builtin == GPU_INVERSE_OBJECT_MATRIX) { - BLI_dynstr_append(ds, "objinv"); - } - else if (input->builtin == GPU_VIEW_POSITION) { - BLI_dynstr_append(ds, "viewposition"); - } - else if (input->builtin == GPU_VIEW_NORMAL) { - BLI_dynstr_append(ds, "facingnormal"); - } - else if (input->builtin == GPU_WORLD_NORMAL) { - BLI_dynstr_append(ds, "facingwnormal"); - } - else { - BLI_dynstr_append(ds, gpu_builtin_name(input->builtin)); - } - } - else if (input->source == GPU_SOURCE_STRUCT) { - BLI_dynstr_appendf(ds, "strct%d", input->id); - } - else if (input->source == GPU_SOURCE_UNIFORM) { - BLI_dynstr_appendf(ds, "unf%d", input->id); - } - else if (input->source == GPU_SOURCE_CONSTANT) { - BLI_dynstr_appendf(ds, "cons%d", input->id); - } - else if (input->source == GPU_SOURCE_ATTR) { - codegen_convert_datatype(ds, input->attr->gputype, input->type, "var", input->attr->id); - } - else if (input->source == GPU_SOURCE_UNIFORM_ATTR) { - BLI_dynstr_appendf(ds, "GET_UNIFORM_ATTR(attr%d)", input->uniform_attr->id); - } - - BLI_dynstr_append(ds, ", "); - } - - LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { - BLI_dynstr_appendf(ds, "tmp%d", output->id); - if (output->next) { - BLI_dynstr_append(ds, ", "); - } - } - - BLI_dynstr_append(ds, ");\n"); - } -} - -static void codegen_final_output(DynStr *ds, GPUOutput *finaloutput) -{ - BLI_dynstr_appendf(ds, "return tmp%d;\n", finaloutput->id); -} - -static char *code_generate_fragment(GPUMaterial *material, - GPUNodeGraph *graph, - const char *interface_str) -{ - DynStr *ds = BLI_dynstr_new(); - char *code; - int builtins; - - codegen_set_unique_ids(graph); - - /* Attributes, Shader stage interface. */ - if (interface_str) { - BLI_dynstr_appendf(ds, "in codegenInterface {%s};\n\n", interface_str); - } - - builtins = codegen_process_uniforms_functions(material, ds, graph); - - if (builtins & (GPU_OBJECT_INFO | GPU_OBJECT_COLOR)) { - BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); - } - - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); - } - - BLI_dynstr_append(ds, "Closure nodetree_exec(void)\n{\n"); - - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, " vec2 barytexco = barycentric_resolve(barycentricTexCo);\n"); - } - /* TODO(fclem): get rid of that. */ - if (builtins & GPU_VIEW_MATRIX) { - BLI_dynstr_append(ds, " #define viewmat ViewMatrix\n"); - } - if (builtins & GPU_CAMERA_TEXCO_FACTORS) { - BLI_dynstr_append(ds, " #define camtexfac CameraTexCoFactors\n"); - } - if (builtins & GPU_OBJECT_MATRIX) { - BLI_dynstr_append(ds, " #define objmat ModelMatrix\n"); - } - if (builtins & GPU_INVERSE_OBJECT_MATRIX) { - BLI_dynstr_append(ds, " #define objinv ModelMatrixInverse\n"); - } - if (builtins & GPU_INVERSE_VIEW_MATRIX) { - BLI_dynstr_append(ds, " #define viewinv ViewMatrixInverse\n"); - } - if (builtins & GPU_LOC_TO_VIEW_MATRIX) { - BLI_dynstr_append(ds, " #define localtoviewmat (ViewMatrix * ModelMatrix)\n"); - } - if (builtins & GPU_INVERSE_LOC_TO_VIEW_MATRIX) { - BLI_dynstr_append(ds, - " #define invlocaltoviewmat (ModelMatrixInverse * ViewMatrixInverse)\n"); - } - if (builtins & GPU_VIEW_NORMAL) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, " vec3 n;\n"); - BLI_dynstr_append(ds, " world_normals_get(n);\n"); - BLI_dynstr_append(ds, " vec3 facingnormal = transform_direction(ViewMatrix, n);\n"); - BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, " vec3 facingnormal = gl_FrontFacing ? viewNormal: -viewNormal;\n"); - BLI_dynstr_append(ds, "#endif\n"); - } - if (builtins & GPU_WORLD_NORMAL) { - BLI_dynstr_append(ds, " vec3 facingwnormal;\n"); - if (builtins & GPU_VIEW_NORMAL) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, " facingwnormal = n;\n"); - BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n"); - BLI_dynstr_append(ds, "#endif\n"); - } - else { - BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n"); - } - } - if (builtins & GPU_VIEW_POSITION) { - BLI_dynstr_append(ds, " #define viewposition viewPosition\n"); - } - - codegen_declare_tmps(ds, graph); - codegen_call_functions(ds, graph); - - BLI_dynstr_append(ds, " #ifndef VOLUMETRICS\n"); - BLI_dynstr_append(ds, " if (renderPassAOV) {\n"); - BLI_dynstr_append(ds, " switch (render_pass_aov_hash()) {\n"); - GSet *aovhashes_added = BLI_gset_int_new(__func__); - LISTBASE_FOREACH (GPUNodeGraphOutputLink *, aovlink, &graph->outlink_aovs) { - void *aov_key = POINTER_FROM_INT(aovlink->hash); - if (BLI_gset_haskey(aovhashes_added, aov_key)) { - continue; - } - BLI_dynstr_appendf(ds, " case %d: {\n ", aovlink->hash); - codegen_final_output(ds, aovlink->outlink->output); - BLI_dynstr_append(ds, " }\n"); - BLI_gset_add(aovhashes_added, aov_key); - } - BLI_gset_free(aovhashes_added, NULL); - BLI_dynstr_append(ds, " default: {\n"); - BLI_dynstr_append(ds, " Closure no_aov = CLOSURE_DEFAULT;\n"); - BLI_dynstr_append(ds, " no_aov.holdout = 1.0;\n"); - BLI_dynstr_append(ds, " return no_aov;\n"); - BLI_dynstr_append(ds, " }\n"); - BLI_dynstr_append(ds, " }\n"); - BLI_dynstr_append(ds, " } else {\n"); - BLI_dynstr_append(ds, " #else /* VOLUMETRICS */\n"); - BLI_dynstr_append(ds, " {\n"); - BLI_dynstr_append(ds, " #endif /* VOLUMETRICS */\n "); - codegen_final_output(ds, graph->outlink->output); - BLI_dynstr_append(ds, " }\n"); - - BLI_dynstr_append(ds, "}\n"); - - /* create shader */ - code = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - -#if 0 - if (G.debug & G_DEBUG) { - printf("%s\n", code); - } -#endif - - return code; -} - -static const char *attr_prefix_get(CustomDataType type) -{ - switch (type) { - case CD_ORCO: - return "orco"; - case CD_MTFACE: - return "u"; - case CD_TANGENT: - return "t"; - case CD_MCOL: - return "c"; - case CD_PROP_COLOR: - return "c"; - case CD_AUTO_FROM_NAME: - return "a"; - case CD_HAIRLENGTH: - return "hl"; - default: - BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!"); - return ""; - } -} - -/* We talk about shader stage interface, not to be mistaken with GPUShaderInterface. */ -static char *code_generate_interface(GPUNodeGraph *graph, int builtins) -{ - if (BLI_listbase_is_empty(&graph->attributes) && - (builtins & (GPU_BARYCENTRIC_DIST | GPU_BARYCENTRIC_TEXCO)) == 0) { - return NULL; - } - - DynStr *ds = BLI_dynstr_new(); - - BLI_dynstr_append(ds, "\n"); - - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - if (attr->type == CD_HAIRLENGTH) { - BLI_dynstr_appendf(ds, "float var%d;\n", attr->id); - } - else { - BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id); - } - } - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "vec2 barycentricTexCo;\n"); - } - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "vec3 barycentricDist;\n"); - } - - char *code = BLI_dynstr_get_cstring(ds); - - BLI_dynstr_free(ds); - - return code; -} - -static char *code_generate_vertex(GPUNodeGraph *graph, - const char *interface_str, - const char *vert_code, - int builtins) -{ - DynStr *ds = BLI_dynstr_new(); - - BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); - - /* Inputs */ - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - const char *type_str = gpu_data_type_to_string(attr->gputype); - const char *prefix = attr_prefix_get(attr->type); - /* XXX FIXME : see notes in mesh_render_data_create() */ - /* NOTE : Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */ - if (attr->type == CD_ORCO) { - /* OPTI : orco is computed from local positions, but only if no modifier is present. */ - BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); - BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n"); - } - else if (attr->type == CD_HAIRLENGTH) { - BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); - BLI_dynstr_append(ds, "DEFINE_ATTR(float, hairLen);\n"); - } - else if (attr->name[0] == '\0') { - BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s);\n", type_str, prefix); - BLI_dynstr_appendf(ds, "#define att%d %s\n", attr->id, prefix); - } - else { - char attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - GPU_vertformat_safe_attr_name(attr->name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s%s);\n", type_str, prefix, attr_safe_name); - BLI_dynstr_appendf(ds, "#define att%d %s%s\n", attr->id, prefix, attr_safe_name); - } - } - - /* Outputs interface */ - if (interface_str) { - BLI_dynstr_appendf(ds, "out codegenInterface {%s};\n\n", interface_str); - } - - /* Prototype. Needed for hair functions. */ - BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv);\n"); - BLI_dynstr_append(ds, "#define USE_ATTR\n\n"); - - BLI_dynstr_append(ds, vert_code); - BLI_dynstr_append(ds, "\n\n"); - - BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv) {\n"); - - /* GPU_BARYCENTRIC_TEXCO cannot be computed based on gl_VertexID - * for MESH_SHADER because of indexed drawing. In this case a - * geometry shader is needed. */ - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_appendf(ds, " barycentricTexCo = barycentric_get();\n"); - } - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_appendf(ds, " barycentricDist = vec3(0);\n"); - } - - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - if (attr->type == CD_TANGENT) { /* silly exception */ - BLI_dynstr_appendf(ds, " var%d = tangent_get(att%d, normalmat);\n", attr->id, attr->id); - } - else if (attr->type == CD_ORCO) { - BLI_dynstr_appendf( - ds, " var%d = orco_get(position, modelmatinv, OrcoTexCoFactors, orco);\n", attr->id); - } - else if (attr->type == CD_HAIRLENGTH) { - BLI_dynstr_appendf(ds, " var%d = hair_len_get(hair_get_strand_id(), hairLen);\n", attr->id); - } - else { - const char *type_str = gpu_data_type_to_string(attr->gputype); - BLI_dynstr_appendf(ds, " var%d = GET_ATTR(%s, att%d);\n", attr->id, type_str, attr->id); - } - } - - BLI_dynstr_append(ds, "}\n"); - - char *code = BLI_dynstr_get_cstring(ds); - - BLI_dynstr_free(ds); - -#if 0 - if (G.debug & G_DEBUG) { - printf("%s\n", code); - } -#endif - - return code; -} - -static char *code_generate_geometry(GPUNodeGraph *graph, - const char *interface_str, - const char *geom_code, - int builtins) -{ - if (!geom_code) { - return NULL; - } - - DynStr *ds = BLI_dynstr_new(); - - /* Attributes, Shader interface; */ - if (interface_str) { - BLI_dynstr_appendf(ds, "in codegenInterface {%s} dataAttrIn[];\n\n", interface_str); - BLI_dynstr_appendf(ds, "out codegenInterface {%s} dataAttrOut;\n\n", interface_str); - } - - BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); - - if (builtins & GPU_BARYCENTRIC_DIST) { - /* geom_code should do something with this, but may not. */ - BLI_dynstr_append(ds, "#define DO_BARYCENTRIC_DISTANCES\n"); - } - - /* Generate varying assignments. */ - BLI_dynstr_append(ds, "#define USE_ATTR\n"); - /* This needs to be a define. Some drivers don't like variable vert index inside dataAttrIn. */ - BLI_dynstr_append(ds, "#define pass_attr(vert) {\\\n"); - - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "dataAttrOut.barycentricTexCo = calc_barycentric_co(vert);\\\n"); - } - - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - /* TODO: let shader choose what to do depending on what the attribute is. */ - BLI_dynstr_appendf(ds, "dataAttrOut.var%d = dataAttrIn[vert].var%d;\\\n", attr->id, attr->id); - } - BLI_dynstr_append(ds, "}\n\n"); - - BLI_dynstr_append(ds, geom_code); - - char *code = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - - return code; -} - -GPUShader *GPU_pass_shader_get(GPUPass *pass) -{ - return pass->shader; -} - -/* Pass create/free */ - -static bool gpu_pass_is_valid(GPUPass *pass) -{ - /* Shader is not null if compilation is successful. */ - return (pass->compiled == false || pass->shader != NULL); -} - -GPUPass *GPU_generate_pass(GPUMaterial *material, - GPUNodeGraph *graph, - const char *vert_code, - const char *geom_code, - const char *frag_lib, - const char *defines) -{ - /* Prune the unused nodes and extract attributes before compiling so the - * generated VBOs are ready to accept the future shader. */ - gpu_node_graph_prune_unused(graph); - gpu_node_graph_finalize_uniform_attrs(graph); - - int builtins = 0; - LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { - if (input->source == GPU_SOURCE_BUILTIN) { - builtins |= input->builtin; - } - } - } - /* generate code */ - char *interface_str = code_generate_interface(graph, builtins); - char *fragmentgen = code_generate_fragment(material, graph, interface_str); - - /* Cache lookup: Reuse shaders already compiled */ - uint32_t hash = gpu_pass_hash(fragmentgen, defines, &graph->attributes); - GPUPass *pass_hash = gpu_pass_cache_lookup(hash); - - if (pass_hash && (pass_hash->next == NULL || pass_hash->next->hash != hash)) { - /* No collision, just return the pass. */ - MEM_SAFE_FREE(interface_str); - MEM_freeN(fragmentgen); - if (!gpu_pass_is_valid(pass_hash)) { - /* Shader has already been created but failed to compile. */ - return NULL; - } - pass_hash->refcount += 1; - return pass_hash; - } - - /* Either the shader is not compiled or there is a hash collision... - * continue generating the shader strings. */ - GSet *used_libraries = gpu_material_used_libraries(material); - char *tmp = gpu_material_library_generate_code(used_libraries, frag_lib); - - char *geometrycode = code_generate_geometry(graph, interface_str, geom_code, builtins); - char *vertexcode = code_generate_vertex(graph, interface_str, vert_code, builtins); - char *fragmentcode = BLI_strdupcat(tmp, fragmentgen); - - MEM_SAFE_FREE(interface_str); - MEM_freeN(fragmentgen); - MEM_freeN(tmp); - - GPUPass *pass = NULL; - if (pass_hash) { - /* Cache lookup: Reuse shaders already compiled */ - pass = gpu_pass_cache_resolve_collision( - pass_hash, vertexcode, geometrycode, fragmentcode, defines, hash); - } - - if (pass) { - MEM_SAFE_FREE(vertexcode); - MEM_SAFE_FREE(fragmentcode); - MEM_SAFE_FREE(geometrycode); - - /* Cache hit. Reuse the same GPUPass and GPUShader. */ - if (!gpu_pass_is_valid(pass)) { - /* Shader has already been created but failed to compile. */ - return NULL; - } - - pass->refcount += 1; - } - else { - /* We still create a pass even if shader compilation - * fails to avoid trying to compile again and again. */ - pass = MEM_callocN(sizeof(GPUPass), "GPUPass"); - pass->shader = NULL; - pass->refcount = 1; - pass->hash = hash; - pass->vertexcode = vertexcode; - pass->fragmentcode = fragmentcode; - pass->geometrycode = geometrycode; - pass->defines = (defines) ? BLI_strdup(defines) : NULL; - pass->compiled = false; - - BLI_spin_lock(&pass_cache_spin); - if (pass_hash != NULL) { - /* Add after the first pass having the same hash. */ - pass->next = pass_hash->next; - pass_hash->next = pass; - } - else { - /* No other pass have same hash, just prepend to the list. */ - BLI_LINKS_PREPEND(pass_cache, pass); - } - BLI_spin_unlock(&pass_cache_spin); - } - - return pass; -} - -static int count_active_texture_sampler(GPUShader *shader, const char *source) -{ - const char *code = source; - - /* Remember this is per stage. */ - GSet *sampler_ids = BLI_gset_int_new(__func__); - int num_samplers = 0; - - while ((code = strstr(code, "uniform "))) { - /* Move past "uniform". */ - code += 7; - /* Skip following spaces. */ - while (*code == ' ') { - code++; - } - /* Skip "i" from potential isamplers. */ - if (*code == 'i') { - code++; - } - /* Skip following spaces. */ - if (BLI_str_startswith(code, "sampler")) { - /* Move past "uniform". */ - code += 7; - /* Skip sampler type suffix. */ - while (!ELEM(*code, ' ', '\0')) { - code++; - } - /* Skip following spaces. */ - while (*code == ' ') { - code++; - } - - if (*code != '\0') { - char sampler_name[64]; - code = gpu_str_skip_token(code, sampler_name, sizeof(sampler_name)); - int id = GPU_shader_get_uniform(shader, sampler_name); - - if (id == -1) { - continue; - } - /* Catch duplicates. */ - if (BLI_gset_add(sampler_ids, POINTER_FROM_INT(id))) { - num_samplers++; - } - } - } - } - - BLI_gset_free(sampler_ids, NULL); - - return num_samplers; -} - -static bool gpu_pass_shader_validate(GPUPass *pass, GPUShader *shader) -{ - if (shader == NULL) { - return false; - } - - /* NOTE: The only drawback of this method is that it will count a sampler - * used in the fragment shader and only declared (but not used) in the vertex - * shader as used by both. But this corner case is not happening for now. */ - int vert_samplers_len = count_active_texture_sampler(shader, pass->vertexcode); - int frag_samplers_len = count_active_texture_sampler(shader, pass->fragmentcode); - - int total_samplers_len = vert_samplers_len + frag_samplers_len; - - /* Validate against opengl limit. */ - if ((frag_samplers_len > GPU_max_textures_frag()) || - (vert_samplers_len > GPU_max_textures_vert())) { - return false; - } - - if (pass->geometrycode) { - int geom_samplers_len = count_active_texture_sampler(shader, pass->geometrycode); - total_samplers_len += geom_samplers_len; - if (geom_samplers_len > GPU_max_textures_geom()) { - return false; - } - } - - return (total_samplers_len <= GPU_max_textures()); -} - -bool GPU_pass_compile(GPUPass *pass, const char *shname) -{ - bool success = true; - if (!pass->compiled) { - GPUShader *shader = GPU_shader_create( - pass->vertexcode, pass->fragmentcode, pass->geometrycode, NULL, pass->defines, shname); - - /* NOTE: Some drivers / gpu allows more active samplers than the opengl limit. - * We need to make sure to count active samplers to avoid undefined behavior. */ - if (!gpu_pass_shader_validate(pass, shader)) { - success = false; - if (shader != NULL) { - fprintf(stderr, "GPUShader: error: too many samplers in shader.\n"); - GPU_shader_free(shader); - shader = NULL; - } - } - pass->shader = shader; - pass->compiled = true; - } - - return success; -} - -void GPU_pass_release(GPUPass *pass) -{ - BLI_assert(pass->refcount > 0); - pass->refcount--; -} - -static void gpu_pass_free(GPUPass *pass) -{ - BLI_assert(pass->refcount == 0); - if (pass->shader) { - GPU_shader_free(pass->shader); - } - MEM_SAFE_FREE(pass->fragmentcode); - MEM_SAFE_FREE(pass->geometrycode); - MEM_SAFE_FREE(pass->vertexcode); - MEM_SAFE_FREE(pass->defines); - MEM_freeN(pass); -} - -void GPU_pass_cache_garbage_collect(void) -{ - static int lasttime = 0; - const int shadercollectrate = 60; /* hardcoded for now. */ - int ctime = (int)PIL_check_seconds_timer(); - - if (ctime < shadercollectrate + lasttime) { - return; - } - - lasttime = ctime; - - BLI_spin_lock(&pass_cache_spin); - GPUPass *next, **prev_pass = &pass_cache; - for (GPUPass *pass = pass_cache; pass; pass = next) { - next = pass->next; - if (pass->refcount == 0) { - /* Remove from list */ - *prev_pass = next; - gpu_pass_free(pass); - } - else { - prev_pass = &pass->next; - } - } - BLI_spin_unlock(&pass_cache_spin); -} - -void GPU_pass_cache_init(void) -{ - BLI_spin_init(&pass_cache_spin); -} - -void GPU_pass_cache_free(void) -{ - BLI_spin_lock(&pass_cache_spin); - while (pass_cache) { - GPUPass *next = pass_cache->next; - gpu_pass_free(pass_cache); - pass_cache = next; - } - BLI_spin_unlock(&pass_cache_spin); - - BLI_spin_end(&pass_cache_spin); -} - -/* Module */ - -void gpu_codegen_init(void) -{ -} - -void gpu_codegen_exit(void) -{ - BKE_material_defaults_free_gpu(); - GPU_shader_free_builtin_shaders(); -} diff --git a/source/blender/gpu/intern/gpu_codegen.cc b/source/blender/gpu/intern/gpu_codegen.cc new file mode 100644 index 00000000000..7777fc19997 --- /dev/null +++ b/source/blender/gpu/intern/gpu_codegen.cc @@ -0,0 +1,824 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Convert material node-trees to GLSL. + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_customdata_types.h" +#include "DNA_image_types.h" + +#include "BLI_blenlib.h" +#include "BLI_dynstr.h" +#include "BLI_ghash.h" +#include "BLI_hash_mm2a.h" +#include "BLI_link_utils.h" +#include "BLI_threads.h" +#include "BLI_utildefines.h" + +#include "PIL_time.h" + +#include "BKE_material.h" +#include "BKE_world.h" + +#include "GPU_capabilities.h" +#include "GPU_material.h" +#include "GPU_shader.h" +#include "GPU_uniform_buffer.h" +#include "GPU_vertex_format.h" + +#include "BLI_sys_types.h" /* for intptr_t support */ + +#include "gpu_codegen.h" +#include "gpu_material_library.h" +#include "gpu_node_graph.h" + +#include <stdarg.h> +#include <string.h> + +#include <sstream> +#include <string> + +extern "C" { +extern char datatoc_gpu_shader_codegen_lib_glsl[]; +} +/* -------------------------------------------------------------------- */ +/** \name GPUPass Cache + * + * Internal shader cache: This prevent the shader recompilation / stall when + * using undo/redo AND also allows for GPUPass reuse if the Shader code is the + * same for 2 different Materials. Unused GPUPasses are free by Garbage collection. + */ + +/* Only use one linklist that contains the GPUPasses grouped by hash. */ +static GPUPass *pass_cache = nullptr; +static SpinLock pass_cache_spin; + +/* Search by hash only. Return first pass with the same hash. + * There is hash collision if (pass->next && pass->next->hash == hash) */ +static GPUPass *gpu_pass_cache_lookup(uint32_t hash) +{ + BLI_spin_lock(&pass_cache_spin); + /* Could be optimized with a Lookup table. */ + for (GPUPass *pass = pass_cache; pass; pass = pass->next) { + if (pass->hash == hash) { + BLI_spin_unlock(&pass_cache_spin); + return pass; + } + } + BLI_spin_unlock(&pass_cache_spin); + return nullptr; +} + +static void gpu_pass_cache_insert_after(GPUPass *node, GPUPass *pass) +{ + BLI_spin_lock(&pass_cache_spin); + if (node != nullptr) { + /* Add after the first pass having the same hash. */ + pass->next = node->next; + node->next = pass; + } + else { + /* No other pass have same hash, just prepend to the list. */ + BLI_LINKS_PREPEND(pass_cache, pass); + } + BLI_spin_unlock(&pass_cache_spin); +} + +/* Check all possible passes with the same hash. */ +static GPUPass *gpu_pass_cache_resolve_collision(GPUPass *pass, + GPUShaderSource *source, + uint32_t hash) +{ + BLI_spin_lock(&pass_cache_spin); + /* Collision, need to `strcmp` the whole shader. */ + for (; pass && (pass->hash == hash); pass = pass->next) { + if ((source->defines != nullptr) && (!STREQ(pass->source.defines, source->defines))) { + /* Pass */ + } + else if ((source->geometry != nullptr) && (!STREQ(pass->source.geometry, source->geometry))) { + /* Pass */ + } + else if (STREQ(pass->source.fragment, source->fragment) && + STREQ(pass->source.vertex, source->vertex)) { + BLI_spin_unlock(&pass_cache_spin); + return pass; + } + } + BLI_spin_unlock(&pass_cache_spin); + return nullptr; +} + +static bool gpu_pass_is_valid(GPUPass *pass) +{ + /* Shader is not null if compilation is successful. */ + return (pass->compiled == false || pass->shader != nullptr); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Type > string conversion + * \{ */ + +static std::ostream &operator<<(std::ostream &stream, const GPUInput *input) +{ + switch (input->source) { + case GPU_SOURCE_OUTPUT: + return stream << "tmp" << input->id; + case GPU_SOURCE_CONSTANT: + return stream << "cons" << input->id; + case GPU_SOURCE_UNIFORM: + return stream << "unf" << input->id; + case GPU_SOURCE_ATTR: + return stream << "var" << input->attr->id; + case GPU_SOURCE_UNIFORM_ATTR: + return stream << "UNIFORM_ATTR_UBO.attr" << input->uniform_attr->id; + case GPU_SOURCE_STRUCT: + return stream << "strct" << input->id; + case GPU_SOURCE_TEX: + return stream << input->texture->sampler_name; + case GPU_SOURCE_TEX_TILED_MAPPING: + return stream << input->texture->tiled_mapping_name; + case GPU_SOURCE_VOLUME_GRID: + return stream << input->volume_grid->sampler_name; + case GPU_SOURCE_VOLUME_GRID_TRANSFORM: + return stream << input->volume_grid->transform_name; + default: + BLI_assert(0); + return stream; + } +} + +static std::ostream &operator<<(std::ostream &stream, const GPUOutput *output) +{ + return stream << "tmp" << output->id; +} + +static std::ostream &operator<<(std::ostream &stream, const eGPUType &type) +{ + return stream << gpu_data_type_to_string(type); +} + +/* Trick type to change overload and keep a somewhat nice syntax. */ +struct GPUConstant : public GPUInput { +}; + +/* Print data constructor (i.e: vec2(1.0f, 1.0f)). */ +static std::ostream &operator<<(std::ostream &stream, const GPUConstant *input) +{ + stream << input->type << "("; + for (int i = 0; i < input->type; i++) { + char formated_float[32]; + /* Print with the maximum precision for single precision float using scientific notation. + * See https://stackoverflow.com/questions/16839658/#answer-21162120 */ + SNPRINTF(formated_float, "%.9g", input->vec[i]); + stream << formated_float; + if (i < input->type - 1) { + stream << ", "; + } + } + stream << ")"; + return stream; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name GLSL code generation + * \{ */ + +class GPUCodegen { + public: + GPUMaterial &mat; + GPUNodeGraph &graph; + GPUCodegenOutput output = {}; + + private: + uint32_t hash_ = 0; + BLI_HashMurmur2A hm2a_; + ListBase ubo_inputs_ = {nullptr, nullptr}; + + public: + GPUCodegen(GPUMaterial *mat_, GPUNodeGraph *graph_) : mat(*mat_), graph(*graph_) + { + BLI_hash_mm2a_init(&hm2a_, GPU_material_uuid_get(&mat)); + } + + ~GPUCodegen() + { + MEM_SAFE_FREE(output.attribs_declare); + MEM_SAFE_FREE(output.attribs_interface); + MEM_SAFE_FREE(output.attribs_load); + MEM_SAFE_FREE(output.attribs_passthrough); + MEM_SAFE_FREE(output.surface); + MEM_SAFE_FREE(output.volume); + MEM_SAFE_FREE(output.thickness); + MEM_SAFE_FREE(output.displacement); + MEM_SAFE_FREE(output.uniforms); + MEM_SAFE_FREE(output.library); + BLI_freelistN(&ubo_inputs_); + }; + + void generate_graphs(); + void generate_uniform_buffer(); + void generate_attribs(); + void generate_resources(); + void generate_library(); + + uint32_t hash_get() const + { + return hash_; + } + + private: + void set_unique_ids(); + + void node_serialize(std::stringstream &eval_ss, const GPUNode *node); + char *graph_serialize(eGPUNodeTag tree_tag, GPUNodeLink *output_link); + + static char *extract_c_str(std::stringstream &stream) + { + auto string = stream.str(); + return BLI_strdup(string.c_str()); + } +}; + +static char attr_prefix_get(CustomDataType type) +{ + switch (type) { + case CD_MTFACE: + return 'u'; + case CD_TANGENT: + return 't'; + case CD_MCOL: + return 'c'; + case CD_PROP_COLOR: + return 'c'; + case CD_AUTO_FROM_NAME: + return 'a'; + case CD_HAIRLENGTH: + return 'l'; + default: + BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!"); + return '\0'; + } +} + +void GPUCodegen::generate_attribs() +{ + if (BLI_listbase_is_empty(&graph.attributes)) { + output.attribs_declare = nullptr; + output.attribs_interface = nullptr; + output.attribs_load = nullptr; + output.attribs_passthrough = nullptr; + return; + } + + /* Input declaration, loading / assignment to interface and geometry shader passthrough. */ + std::stringstream decl_ss, iface_ss, load_ss, pass_ss; + + LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph.attributes) { + eGPUType type = attr->gputype; + eGPUType in_type = attr->gputype; + char name[GPU_MAX_SAFE_ATTR_NAME + 1] = "orco"; + if (attr->type == CD_ORCO) { + /* OPTI : orco is computed from local positions, but only if no modifier is present. */ + GPU_material_flag_set(&mat, GPU_MATFLAG_OBJECT_INFO); + /* Need vec4 to detect usage of default attribute. */ + in_type = GPU_VEC4; + } + else { + name[0] = attr_prefix_get((CustomDataType)(attr->type)); + name[1] = '\0'; + } + + if (attr->name[0] != '\0') { + /* XXX FIXME : see notes in mesh_render_data_create() */ + GPU_vertformat_safe_attr_name(attr->name, &name[1], GPU_MAX_SAFE_ATTR_NAME); + } + /* NOTE : Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */ + decl_ss << in_type << " " << name << ";\n"; + + iface_ss << type << " var" << attr->id << ";\n"; + + load_ss << "var" << attr->id; + + switch (attr->type) { + case CD_ORCO: + load_ss << " = attr_load_orco(" << name << ");\n"; + break; + case CD_TANGENT: + load_ss << " = attr_load_tangent(" << name << ");\n"; + break; + case CD_MTFACE: + load_ss << " = attr_load_uv(" << name << ");\n"; + break; + case CD_MCOL: + load_ss << " = attr_load_color(" << name << ");\n"; + break; + default: + load_ss << " = attr_load_" << type << "(" << name << ");\n"; + break; + } + pass_ss << "attr_out.var" << attr->id << " = attr_in[vert_id].var" << attr->id << ";\n"; + } + + output.attribs_declare = extract_c_str(decl_ss); + output.attribs_interface = extract_c_str(iface_ss); + output.attribs_load = extract_c_str(load_ss); + output.attribs_passthrough = extract_c_str(pass_ss); +} + +void GPUCodegen::generate_resources() +{ + std::stringstream ss; + + /* Textures. */ + LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph.textures) { + if (tex->colorband) { + ss << "uniform sampler1DArray " << tex->sampler_name << ";\n"; + } + else if (tex->tiled_mapping_name[0] != '\0') { + ss << "uniform sampler2DArray " << tex->sampler_name << ";\n"; + ss << "uniform sampler1DArray " << tex->tiled_mapping_name << ";\n"; + } + else { + ss << "uniform sampler2D " << tex->sampler_name << ";\n"; + } + } + /* Volume Grids. */ + LISTBASE_FOREACH (GPUMaterialVolumeGrid *, grid, &graph.volume_grids) { + ss << "uniform sampler3D " << grid->sampler_name << ";\n"; + /* TODO(fclem) global uniform. To put in a UBO. */ + ss << "uniform mat4 " << grid->transform_name << " = mat4(0.0);\n"; + } + + if (!BLI_listbase_is_empty(&ubo_inputs_)) { + /* NOTE: generate_uniform_buffer() should have sorted the inputs before this. */ + ss << "layout(std140) uniform nodeTree {\n"; + LISTBASE_FOREACH (LinkData *, link, &ubo_inputs_) { + GPUInput *input = (GPUInput *)(link->data); + ss << input->type << " " << input << ";\n"; + } + ss << "};\n\n"; + } + + if (!BLI_listbase_is_empty(&graph.uniform_attrs.list)) { + GPU_material_flag_set(&mat, GPU_MATFLAG_UNIFORMS_ATTRIB); + + ss << "struct UniformAttributes {\n"; + LISTBASE_FOREACH (GPUUniformAttr *, attr, &graph.uniform_attrs.list) { + ss << "vec4 attr" << attr->id << ";\n"; + } + ss << "};\n\n"; + } + + output.uniforms = extract_c_str(ss); +} + +void GPUCodegen::generate_library() +{ + output.library = gpu_material_library_generate_code(graph.used_libraries); +} + +void GPUCodegen::node_serialize(std::stringstream &eval_ss, const GPUNode *node) +{ + /* Declare constants. */ + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + switch (input->source) { + case GPU_SOURCE_STRUCT: + eval_ss << input->type << " " << input << " = CLOSURE_DEFAULT;\n"; + break; + case GPU_SOURCE_CONSTANT: + eval_ss << input->type << " " << input << " = " << (GPUConstant *)input << ";\n"; + break; + default: + break; + } + } + /* Declare temporary variables for node output storage. */ + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { + eval_ss << output->type << " " << output << ";\n"; + } + + /* Function call. */ + eval_ss << node->name << "("; + /* Input arguments. */ + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + switch (input->source) { + case GPU_SOURCE_OUTPUT: + case GPU_SOURCE_ATTR: { + /* These inputs can have non matching types. Do conversion. */ + eGPUType to = input->type; + eGPUType from = (input->source == GPU_SOURCE_ATTR) ? input->attr->gputype : + input->link->output->type; + if (from != to) { + /* Use defines declared inside codegen_lib (i.e: vec4_from_float). */ + eval_ss << to << "_from_" << from << "("; + } + + if (input->source == GPU_SOURCE_ATTR) { + eval_ss << input; + } + else { + eval_ss << input->link->output; + } + + if (from != to) { + eval_ss << ")"; + } + break; + } + default: + eval_ss << input; + break; + } + eval_ss << ", "; + } + /* Output arguments. */ + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { + eval_ss << output; + if (output->next) { + eval_ss << ", "; + } + } + eval_ss << ");\n\n"; +} + +char *GPUCodegen::graph_serialize(eGPUNodeTag tree_tag, GPUNodeLink *output_link) +{ + if (output_link == nullptr) { + return nullptr; + } + + std::stringstream eval_ss; + /* Render engine implement this function if needed. */ + eval_ss << "ntree_eval_init();\n\n"; + /* NOTE: The node order is already top to bottom (or left to right in node editor) + * because of the evaluation order inside ntreeExecGPUNodes(). */ + LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) { + if ((node->tag & tree_tag) == 0 || (node->tag & GPU_NODE_TAG_EVAL) != 0) { + continue; + } + node_serialize(eval_ss, node); + } + /* Render engine implement this function if needed. */ + eval_ss << "ntree_eval_weights();\n\n"; + /* Output eval function at the end. */ + LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) { + if ((node->tag & tree_tag) == 0 || (node->tag & GPU_NODE_TAG_EVAL) == 0) { + continue; + } + node_serialize(eval_ss, node); + } + eval_ss << "return " << output_link->output << ";\n"; + + char *eval_c_str = extract_c_str(eval_ss); + BLI_hash_mm2a_add(&hm2a_, (uchar *)eval_c_str, eval_ss.str().size()); + return eval_c_str; +} + +void GPUCodegen::generate_uniform_buffer() +{ + /* Extract uniform inputs. */ + LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + if (input->source == GPU_SOURCE_UNIFORM && !input->link) { + /* We handle the UBO uniforms separately. */ + BLI_addtail(&ubo_inputs_, BLI_genericNodeN(input)); + } + } + } + if (!BLI_listbase_is_empty(&ubo_inputs_)) { + /* This sorts the inputs based on size. */ + GPU_material_uniform_buffer_create(&mat, &ubo_inputs_); + } +} + +/* Sets id for unique names for all inputs, resources and temp variables. */ +void GPUCodegen::set_unique_ids() +{ + int id = 1; + LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + input->id = id++; + } + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { + output->id = id++; + } + } +} + +void GPUCodegen::generate_graphs() +{ + set_unique_ids(); + + output.surface = graph_serialize(GPU_NODE_TAG_SURFACE, graph.outlink_surface); + output.volume = graph_serialize(GPU_NODE_TAG_VOLUME, graph.outlink_volume); + output.displacement = graph_serialize(GPU_NODE_TAG_DISPLACEMENT, graph.outlink_displacement); + output.thickness = graph_serialize(GPU_NODE_TAG_THICKNESS, graph.outlink_thickness); + + LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph.attributes) { + BLI_hash_mm2a_add(&hm2a_, (uchar *)attr->name, strlen(attr->name)); + } + + hash_ = BLI_hash_mm2a_end(&hm2a_); +} + +GPUPass *GPU_generate_pass(GPUMaterial *material, + GPUNodeGraph *graph, + GPUCodegenCallbackFn finalize_source_cb, + void *thunk) +{ + /* Prune the unused nodes and extract attributes before compiling so the + * generated VBOs are ready to accept the future shader. */ + gpu_node_graph_prune_unused(graph); + gpu_node_graph_finalize_uniform_attrs(graph); + + GPUCodegen codegen(material, graph); + codegen.generate_graphs(); + codegen.generate_uniform_buffer(); + + /* Cache lookup: Reuse shaders already compiled. */ + GPUPass *pass_hash = gpu_pass_cache_lookup(codegen.hash_get()); + + /* FIXME(fclem): This is broken. Since we only check for the hash and not the full source + * there is no way to have a collision currently. Some advocated to only use a bigger hash. */ + if (pass_hash && (pass_hash->next == nullptr || pass_hash->next->hash != codegen.hash_get())) { + if (!gpu_pass_is_valid(pass_hash)) { + /* Shader has already been created but failed to compile. */ + return nullptr; + } + /* No collision, just return the pass. */ + pass_hash->refcount += 1; + return pass_hash; + } + + /* Either the shader is not compiled or there is a hash collision... + * continue generating the shader strings. */ + codegen.generate_attribs(); + codegen.generate_resources(); + codegen.generate_library(); + + /* Make engine add its own code and implement the generated functions. */ + GPUShaderSource source = finalize_source_cb(thunk, material, &codegen.output); + + GPUPass *pass = nullptr; + if (pass_hash) { + /* Cache lookup: Reuse shaders already compiled. */ + pass = gpu_pass_cache_resolve_collision(pass_hash, &source, codegen.hash_get()); + } + + if (pass) { + MEM_SAFE_FREE(source.vertex); + MEM_SAFE_FREE(source.geometry); + MEM_SAFE_FREE(source.fragment); + MEM_SAFE_FREE(source.defines); + /* Cache hit. Reuse the same GPUPass and GPUShader. */ + if (!gpu_pass_is_valid(pass)) { + /* Shader has already been created but failed to compile. */ + return nullptr; + } + pass->refcount += 1; + } + else { + /* We still create a pass even if shader compilation + * fails to avoid trying to compile again and again. */ + pass = (GPUPass *)MEM_callocN(sizeof(GPUPass), "GPUPass"); + pass->shader = nullptr; + pass->refcount = 1; + pass->hash = codegen.hash_get(); + pass->source = source; + pass->compiled = false; + + gpu_pass_cache_insert_after(pass_hash, pass); + } + return pass; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Compilation + * \{ */ + +static int count_active_texture_sampler(GPUShader *shader, char *source) +{ + const char *code = source; + + /* Remember this is per stage. */ + GSet *sampler_ids = BLI_gset_int_new(__func__); + int num_samplers = 0; + + while ((code = strstr(code, "uniform "))) { + /* Move past "uniform". */ + code += 7; + /* Skip following spaces. */ + while (*code == ' ') { + code++; + } + /* Skip "i" from potential isamplers. */ + if (*code == 'i') { + code++; + } + /* Skip following spaces. */ + if (BLI_str_startswith(code, "sampler")) { + /* Move past "uniform". */ + code += 7; + /* Skip sampler type suffix. */ + while (!ELEM(*code, ' ', '\0')) { + code++; + } + /* Skip following spaces. */ + while (*code == ' ') { + code++; + } + + if (*code != '\0') { + char sampler_name[64]; + code = gpu_str_skip_token(code, sampler_name, sizeof(sampler_name)); + int id = GPU_shader_get_uniform(shader, sampler_name); + + if (id == -1) { + continue; + } + /* Catch duplicates. */ + if (BLI_gset_add(sampler_ids, POINTER_FROM_INT(id))) { + num_samplers++; + } + } + } + } + + BLI_gset_free(sampler_ids, nullptr); + + return num_samplers; +} + +static bool gpu_pass_shader_validate(GPUPass *pass, GPUShader *shader) +{ + if (shader == nullptr) { + return false; + } + + /* NOTE: The only drawback of this method is that it will count a sampler + * used in the fragment shader and only declared (but not used) in the vertex + * shader as used by both. But this corner case is not happening for now. */ + int vert_samplers_len = count_active_texture_sampler(shader, pass->source.vertex); + int frag_samplers_len = count_active_texture_sampler(shader, pass->source.fragment); + + int total_samplers_len = vert_samplers_len + frag_samplers_len; + + /* Validate against opengl limit. */ + if ((frag_samplers_len > GPU_max_textures_frag()) || + (vert_samplers_len > GPU_max_textures_vert())) { + return false; + } + + if (pass->source.geometry) { + int geom_samplers_len = count_active_texture_sampler(shader, pass->source.geometry); + total_samplers_len += geom_samplers_len; + if (geom_samplers_len > GPU_max_textures_geom()) { + return false; + } + } + + return (total_samplers_len <= GPU_max_textures()); +} + +bool GPU_pass_compile(GPUPass *pass, const char *shname) +{ + bool success = true; + if (!pass->compiled) { + GPUShader *shader = GPU_shader_create(pass->source.vertex, + pass->source.fragment, + pass->source.geometry, + nullptr, + pass->source.defines, + shname); + + /* NOTE: Some drivers / gpu allows more active samplers than the opengl limit. + * We need to make sure to count active samplers to avoid undefined behavior. */ + if (!gpu_pass_shader_validate(pass, shader)) { + success = false; + if (shader != nullptr) { + fprintf(stderr, "GPUShader: error: too many samplers in shader.\n"); + GPU_shader_free(shader); + shader = nullptr; + } + } + pass->shader = shader; + pass->compiled = true; + } + return success; +} + +GPUShader *GPU_pass_shader_get(GPUPass *pass) +{ + return pass->shader; +} + +void GPU_pass_release(GPUPass *pass) +{ + BLI_assert(pass->refcount > 0); + pass->refcount--; +} + +static void gpu_pass_free(GPUPass *pass) +{ + BLI_assert(pass->refcount == 0); + if (pass->shader) { + GPU_shader_free(pass->shader); + } + MEM_SAFE_FREE(pass->source.vertex); + MEM_SAFE_FREE(pass->source.fragment); + MEM_SAFE_FREE(pass->source.geometry); + MEM_SAFE_FREE(pass->source.defines); + MEM_freeN(pass); +} + +void GPU_pass_cache_garbage_collect(void) +{ + static int lasttime = 0; + const int shadercollectrate = 60; /* hardcoded for now. */ + int ctime = (int)PIL_check_seconds_timer(); + + if (ctime < shadercollectrate + lasttime) { + return; + } + + lasttime = ctime; + + BLI_spin_lock(&pass_cache_spin); + GPUPass *next, **prev_pass = &pass_cache; + for (GPUPass *pass = pass_cache; pass; pass = next) { + next = pass->next; + if (pass->refcount == 0) { + /* Remove from list */ + *prev_pass = next; + gpu_pass_free(pass); + } + else { + prev_pass = &pass->next; + } + } + BLI_spin_unlock(&pass_cache_spin); +} + +void GPU_pass_cache_init(void) +{ + BLI_spin_init(&pass_cache_spin); +} + +void GPU_pass_cache_free(void) +{ + BLI_spin_lock(&pass_cache_spin); + while (pass_cache) { + GPUPass *next = pass_cache->next; + gpu_pass_free(pass_cache); + pass_cache = next; + } + BLI_spin_unlock(&pass_cache_spin); + + BLI_spin_end(&pass_cache_spin); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Module + * \{ */ + +void gpu_codegen_init(void) +{ +} + +void gpu_codegen_exit(void) +{ + BKE_world_defaults_free_gpu(); + BKE_material_defaults_free_gpu(); + GPU_shader_free_builtin_shaders(); +} + +/** \} */
\ No newline at end of file diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h index f3d58a55814..cac92d1a3c3 100644 --- a/source/blender/gpu/intern/gpu_codegen.h +++ b/source/blender/gpu/intern/gpu_codegen.h @@ -25,22 +25,20 @@ #pragma once +#include "GPU_material.h" +#include "GPU_shader.h" + #ifdef __cplusplus extern "C" { #endif -struct GPUMaterial; struct GPUNodeGraph; -struct GPUShader; typedef struct GPUPass { struct GPUPass *next; - struct GPUShader *shader; - char *fragmentcode; - char *geometrycode; - char *vertexcode; - char *defines; + GPUShader *shader; + GPUShaderSource source; uint refcount; /* Orphaned GPUPasses gets freed by the garbage collector. */ uint32_t hash; /* Identity hash generated from all GLSL code. */ bool compiled; /* Did we already tried to compile the attached GPUShader. */ @@ -48,13 +46,11 @@ typedef struct GPUPass { /* Pass */ -GPUPass *GPU_generate_pass(struct GPUMaterial *material, +GPUPass *GPU_generate_pass(GPUMaterial *material, struct GPUNodeGraph *graph, - const char *vert_code, - const char *geom_code, - const char *frag_lib, - const char *defines); -struct GPUShader *GPU_pass_shader_get(GPUPass *pass); + GPUCodegenCallbackFn finalize_source_cb, + void *thunk); +GPUShader *GPU_pass_shader_get(GPUPass *pass); bool GPU_pass_compile(GPUPass *pass, const char *shname); void GPU_pass_release(GPUPass *pass); diff --git a/source/blender/gpu/intern/gpu_framebuffer_private.hh b/source/blender/gpu/intern/gpu_framebuffer_private.hh index a936e889e57..e7505258d2a 100644 --- a/source/blender/gpu/intern/gpu_framebuffer_private.hh +++ b/source/blender/gpu/intern/gpu_framebuffer_private.hh @@ -43,8 +43,9 @@ typedef enum GPUAttachmentType : int { GPU_FB_COLOR_ATTACHMENT3, GPU_FB_COLOR_ATTACHMENT4, GPU_FB_COLOR_ATTACHMENT5, - /* Number of maximum output slots. - * We support 6 outputs for now (usually we wouldn't need more to preserve fill rate). */ + GPU_FB_COLOR_ATTACHMENT6, + GPU_FB_COLOR_ATTACHMENT7, + /* Number of maximum output slots. */ /* Keep in mind that GL max is GL_MAX_DRAW_BUFFERS and is at least 8, corresponding to * the maximum number of COLOR attachments specified by glDrawBuffers. */ GPU_FB_MAX_ATTACHMENT, diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 11c61e4cf72..ca1f45f7238 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -32,7 +32,6 @@ #include "DNA_scene_types.h" #include "DNA_world_types.h" -#include "BLI_ghash.h" #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_string.h" @@ -43,6 +42,7 @@ #include "BKE_material.h" #include "BKE_node.h" #include "BKE_scene.h" +#include "BKE_world.h" #include "NOD_shader.h" @@ -65,45 +65,31 @@ typedef struct GPUColorBandBuilder { } GPUColorBandBuilder; struct GPUMaterial { - Scene *scene; /* DEPRECATED was only useful for lights. */ - Material *ma; - - eGPUMaterialStatus status; - - const void *engine_type; /* attached engine type */ - int options; /* to identify shader variations (shadow, probe, world background...) */ - bool is_volume_shader; /* is volumetric shader */ - - /* Nodes */ - GPUNodeGraph graph; - - /* for binding the material */ + /* Contains GPUShader and source code for deferred compilation. + * Can be shared between similar material (i.e: sharing same nodetree topology). */ GPUPass *pass; + /** UBOs for this material parameters. */ + GPUUniformBuf *ubo; + /** Compilation status. Do not use if shader is not GPU_MAT_SUCCESS. */ + eGPUMaterialStatus status; + /** Some flags about the nodetree & the needed resources. */ + eGPUMaterialFlag flag; + /* Identify shader variations (shadow, probe, world background...). + * Should be unique even across render engines. */ + uint64_t uuid; + /** Object type for attribute fetching. */ + bool is_volume_shader; + + /** DEPRECATED Currently only used for deferred compilation. */ + Scene *scene; + /** Source material, might be null. */ + Material *ma; + /** 1D Texture array containing all color bands. */ + GPUTexture *coba_tex; - /* XXX: Should be in Material. But it depends on the output node - * used and since the output selection is different for GPUMaterial... - */ - bool has_volume_output; - bool has_surface_output; - - /* Only used by Eevee to know which BSDF are used. */ - eGPUMatFlag flag; - - /* Used by 2.8 pipeline */ - GPUUniformBuf *ubo; /* UBOs for shader uniforms. */ - - /* Eevee SSS */ - GPUUniformBuf *sss_profile; /* UBO containing SSS profile. */ - GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */ - float sss_enabled; - float sss_radii[3]; - int sss_samples; - bool sss_dirty; - - GPUTexture *coba_tex; /* 1D Texture array containing all color bands. */ GPUColorBandBuilder *coba_builder; - - GSet *used_libraries; + /* Low level node graph(s). Also contains resources needed by the material. */ + GPUNodeGraph graph; #ifndef NDEBUG char name[64]; @@ -175,17 +161,9 @@ static void gpu_material_free_single(GPUMaterial *material) if (material->ubo != NULL) { GPU_uniformbuf_free(material->ubo); } - if (material->sss_tex_profile != NULL) { - GPU_texture_free(material->sss_tex_profile); - } - if (material->sss_profile != NULL) { - GPU_uniformbuf_free(material->sss_profile); - } if (material->coba_tex != NULL) { GPU_texture_free(material->coba_tex); } - - BLI_gset_free(material->used_libraries, NULL); } void GPU_material_free(ListBase *gpumaterial) @@ -233,292 +211,52 @@ void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs) material->ubo = GPU_uniformbuf_create_from_list(inputs, name); } -/* Eevee Subsurface scattering. */ -/* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */ - -#define SSS_SAMPLES 65 -#define SSS_EXPONENT 2.0f /* Importance sampling exponent */ - -typedef struct GPUSssKernelData { - float kernel[SSS_SAMPLES][4]; - float param[3], max_radius; - int samples; - int pad[3]; -} GPUSssKernelData; - -BLI_STATIC_ASSERT_ALIGN(GPUSssKernelData, 16) - -static void sss_calculate_offsets(GPUSssKernelData *kd, int count, float exponent) +ListBase GPU_material_attributes(GPUMaterial *material) { - float step = 2.0f / (float)(count - 1); - for (int i = 0; i < count; i++) { - float o = ((float)i) * step - 1.0f; - float sign = (o < 0.0f) ? -1.0f : 1.0f; - float ofs = sign * fabsf(powf(o, exponent)); - kd->kernel[i][3] = ofs; - } + return material->graph.attributes; } -#define BURLEY_TRUNCATE 16.0f -#define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE) -static float burley_profile(float r, float d) +ListBase GPU_material_textures(GPUMaterial *material) { - float exp_r_3_d = expf(-r / (3.0f * d)); - float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d; - return (exp_r_d + exp_r_3_d) / (4.0f * d); + return material->graph.textures; } -static float eval_profile(float r, float param) +ListBase GPU_material_volume_grids(GPUMaterial *material) { - r = fabsf(r); - return burley_profile(r, param) / BURLEY_TRUNCATE_CDF; + return material->graph.volume_grids; } -/* Resolution for each sample of the precomputed kernel profile */ -#define INTEGRAL_RESOLUTION 32 -static float eval_integral(float x0, float x1, float param) +GPUUniformAttrList *GPU_material_uniform_attributes(GPUMaterial *material) { - const float range = x1 - x0; - const float step = range / INTEGRAL_RESOLUTION; - float integral = 0.0f; - - for (int i = 0; i < INTEGRAL_RESOLUTION; i++) { - float x = x0 + range * ((float)i + 0.5f) / (float)INTEGRAL_RESOLUTION; - float y = eval_profile(x, param); - integral += y * step; - } - - return integral; + GPUUniformAttrList *attrs = &material->graph.uniform_attrs; + return attrs->count > 0 ? attrs : NULL; } -#undef INTEGRAL_RESOLUTION -static void compute_sss_kernel(GPUSssKernelData *kd, const float radii[3], int sample_len) +void GPU_material_output_surface(GPUMaterial *material, GPUNodeLink *link) { - float rad[3]; - /* Minimum radius */ - rad[0] = MAX2(radii[0], 1e-15f); - rad[1] = MAX2(radii[1], 1e-15f); - rad[2] = MAX2(radii[2], 1e-15f); - - /* Christensen-Burley fitting */ - float l[3], d[3]; - - mul_v3_v3fl(l, rad, 0.25f * M_1_PI); - const float A = 1.0f; - const float s = 1.9f - A + 3.5f * (A - 0.8f) * (A - 0.8f); - /* XXX 0.6f Out of nowhere to match cycles! Empirical! Can be tweak better. */ - mul_v3_v3fl(d, l, 0.6f / s); - mul_v3_v3fl(rad, d, BURLEY_TRUNCATE); - kd->max_radius = MAX3(rad[0], rad[1], rad[2]); - - copy_v3_v3(kd->param, d); - - /* Compute samples locations on the 1d kernel [-1..1] */ - sss_calculate_offsets(kd, sample_len, SSS_EXPONENT); - - /* Weights sum for normalization */ - float sum[3] = {0.0f, 0.0f, 0.0f}; - - /* Compute integral of each sample footprint */ - for (int i = 0; i < sample_len; i++) { - float x0, x1; - - if (i == 0) { - x0 = kd->kernel[0][3] - fabsf(kd->kernel[0][3] - kd->kernel[1][3]) / 2.0f; - } - else { - x0 = (kd->kernel[i - 1][3] + kd->kernel[i][3]) / 2.0f; - } - - if (i == sample_len - 1) { - x1 = kd->kernel[sample_len - 1][3] + - fabsf(kd->kernel[sample_len - 2][3] - kd->kernel[sample_len - 1][3]) / 2.0f; - } - else { - x1 = (kd->kernel[i][3] + kd->kernel[i + 1][3]) / 2.0f; - } - - x0 *= kd->max_radius; - x1 *= kd->max_radius; - - kd->kernel[i][0] = eval_integral(x0, x1, kd->param[0]); - kd->kernel[i][1] = eval_integral(x0, x1, kd->param[1]); - kd->kernel[i][2] = eval_integral(x0, x1, kd->param[2]); - - sum[0] += kd->kernel[i][0]; - sum[1] += kd->kernel[i][1]; - sum[2] += kd->kernel[i][2]; + if (!material->graph.outlink_surface) { + material->graph.outlink_surface = link; } - - for (int i = 0; i < 3; i++) { - if (sum[i] > 0.0f) { - /* Normalize */ - for (int j = 0; j < sample_len; j++) { - kd->kernel[j][i] /= sum[i]; - } - } - else { - /* Avoid 0 kernel sum. */ - kd->kernel[sample_len / 2][i] = 1.0f; - } - } - - /* Put center sample at the start of the array (to sample first) */ - float tmpv[4]; - copy_v4_v4(tmpv, kd->kernel[sample_len / 2]); - for (int i = sample_len / 2; i > 0; i--) { - copy_v4_v4(kd->kernel[i], kd->kernel[i - 1]); - } - copy_v4_v4(kd->kernel[0], tmpv); - - kd->samples = sample_len; } -#define INTEGRAL_RESOLUTION 512 -static void compute_sss_translucence_kernel(const GPUSssKernelData *kd, - int resolution, - float **output) +void GPU_material_output_volume(GPUMaterial *material, GPUNodeLink *link) { - float(*texels)[4]; - texels = MEM_callocN(sizeof(float[4]) * resolution, "compute_sss_translucence_kernel"); - *output = (float *)texels; - - /* Last texel should be black, hence the - 1. */ - for (int i = 0; i < resolution - 1; i++) { - /* Distance from surface. */ - float d = kd->max_radius * ((float)i + 0.00001f) / ((float)resolution); - - /* For each distance d we compute the radiance incoming from an hypothetical parallel plane. */ - /* Compute radius of the footprint on the hypothetical plane. */ - float r_fp = sqrtf(kd->max_radius * kd->max_radius - d * d); - float r_step = r_fp / INTEGRAL_RESOLUTION; - float area_accum = 0.0f; - for (float r = 0.0f; r < r_fp; r += r_step) { - /* Compute distance to the "shading" point through the medium. */ - /* r_step * 0.5f to put sample between the area borders */ - float dist = hypotf(r + r_step * 0.5f, d); - - float profile[3]; - profile[0] = eval_profile(dist, kd->param[0]); - profile[1] = eval_profile(dist, kd->param[1]); - profile[2] = eval_profile(dist, kd->param[2]); - - /* Since the profile and configuration are radially symmetrical we - * can just evaluate it once and weight it accordingly */ - float r_next = r + r_step; - float disk_area = (M_PI * r_next * r_next) - (M_PI * r * r); - - mul_v3_fl(profile, disk_area); - add_v3_v3(texels[i], profile); - area_accum += disk_area; - } - /* Normalize over the disk. */ - mul_v3_fl(texels[i], 1.0f / (area_accum)); - } - - /* Normalize */ - for (int j = resolution - 2; j > 0; j--) { - texels[j][0] /= (texels[0][0] > 0.0f) ? texels[0][0] : 1.0f; - texels[j][1] /= (texels[0][1] > 0.0f) ? texels[0][1] : 1.0f; - texels[j][2] /= (texels[0][2] > 0.0f) ? texels[0][2] : 1.0f; + if (!material->graph.outlink_volume) { + material->graph.outlink_volume = link; } - - /* First texel should be white */ - texels[0][0] = (texels[0][0] > 0.0f) ? 1.0f : 0.0f; - texels[0][1] = (texels[0][1] > 0.0f) ? 1.0f : 0.0f; - texels[0][2] = (texels[0][2] > 0.0f) ? 1.0f : 0.0f; - - /* dim the last few texels for smoother transition */ - mul_v3_fl(texels[resolution - 2], 0.25f); - mul_v3_fl(texels[resolution - 3], 0.5f); - mul_v3_fl(texels[resolution - 4], 0.75f); } -#undef INTEGRAL_RESOLUTION -void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3]) +void GPU_material_output_displacement(GPUMaterial *material, GPUNodeLink *link) { - copy_v3_v3(material->sss_radii, radii); - material->sss_dirty = true; - material->sss_enabled = true; - - /* Update / Create UBO */ - if (material->sss_profile == NULL) { - material->sss_profile = GPU_uniformbuf_create(sizeof(GPUSssKernelData)); + if (!material->graph.outlink_displacement) { + material->graph.outlink_displacement = link; } } -struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material, - int sample_len, - GPUTexture **tex_profile) +void GPU_material_output_thickness(GPUMaterial *material, GPUNodeLink *link) { - if (!material->sss_enabled) { - return NULL; - } - - if (material->sss_dirty || (material->sss_samples != sample_len)) { - GPUSssKernelData kd; - - compute_sss_kernel(&kd, material->sss_radii, sample_len); - - /* Update / Create UBO */ - GPU_uniformbuf_update(material->sss_profile, &kd); - - /* Update / Create Tex */ - float *translucence_profile; - compute_sss_translucence_kernel(&kd, 64, &translucence_profile); - - if (material->sss_tex_profile != NULL) { - GPU_texture_free(material->sss_tex_profile); - } - - material->sss_tex_profile = GPU_texture_create_1d( - "sss_tex_profile", 64, 1, GPU_RGBA16F, translucence_profile); - - MEM_freeN(translucence_profile); - - material->sss_samples = sample_len; - material->sss_dirty = false; - } - - if (tex_profile != NULL) { - *tex_profile = material->sss_tex_profile; - } - return material->sss_profile; -} - -struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void) -{ - return GPU_uniformbuf_create(sizeof(GPUSssKernelData)); -} - -#undef SSS_EXPONENT -#undef SSS_SAMPLES - -ListBase GPU_material_attributes(GPUMaterial *material) -{ - return material->graph.attributes; -} - -ListBase GPU_material_textures(GPUMaterial *material) -{ - return material->graph.textures; -} - -ListBase GPU_material_volume_grids(GPUMaterial *material) -{ - return material->graph.volume_grids; -} - -GPUUniformAttrList *GPU_material_uniform_attributes(GPUMaterial *material) -{ - GPUUniformAttrList *attrs = &material->graph.uniform_attrs; - return attrs->count > 0 ? attrs : NULL; -} - -void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link) -{ - if (!material->graph.outlink) { - material->graph.outlink = link; + if (!material->graph.outlink_thickness) { + material->graph.outlink_thickness = link; } } @@ -530,14 +268,19 @@ void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, BLI_addtail(&material->graph.outlink_aovs, aov_link); } -GPUNodeGraph *gpu_material_node_graph(GPUMaterial *material) +void GPU_material_add_closure_eval(GPUMaterial *material, + const GPUNodeLink *weight_link, + const GPUNodeLink *eval_link) { - return &material->graph; + GPUNodeGraphEvalNode *node = MEM_callocN(sizeof(GPUNodeGraphEvalNode), __func__); + node->weight_node = weight_link->output->node; + node->eval_node = eval_link->output->node; + BLI_addtail(&material->graph.eval_nodes, node); } -GSet *gpu_material_used_libraries(GPUMaterial *material) +GPUNodeGraph *gpu_material_node_graph(GPUMaterial *material) { - return material->used_libraries; + return &material->graph; } eGPUMaterialStatus GPU_material_status(GPUMaterial *mat) @@ -545,112 +288,86 @@ eGPUMaterialStatus GPU_material_status(GPUMaterial *mat) return mat->status; } -/* Code generation */ - -bool GPU_material_has_surface_output(GPUMaterial *mat) +void GPU_material_status_set(GPUMaterial *mat, eGPUMaterialStatus status) { - return mat->has_surface_output; + mat->status = status; } -bool GPU_material_has_volume_output(GPUMaterial *mat) -{ - return mat->has_volume_output; -} +/* Code generation */ bool GPU_material_is_volume_shader(GPUMaterial *mat) { return mat->is_volume_shader; } -void GPU_material_flag_set(GPUMaterial *mat, eGPUMatFlag flag) +void GPU_material_flag_set(GPUMaterial *mat, eGPUMaterialFlag flag) { mat->flag |= flag; } -bool GPU_material_flag_get(GPUMaterial *mat, eGPUMatFlag flag) +bool GPU_material_flag_get(const GPUMaterial *mat, eGPUMaterialFlag flag) { return (mat->flag & flag) != 0; } -GPUMaterial *GPU_material_from_nodetree_find(ListBase *gpumaterials, - const void *engine_type, - int options) +/* Note: Consumes the flags. */ +bool GPU_material_recalc_flag_get(GPUMaterial *mat) { - LISTBASE_FOREACH (LinkData *, link, gpumaterials) { - GPUMaterial *current_material = (GPUMaterial *)link->data; - if (current_material->engine_type == engine_type && current_material->options == options) { - return current_material; - } - } + bool updated = (mat->flag & GPU_MATFLAG_UPDATED) != 0; + mat->flag &= ~GPU_MATFLAG_UPDATED; + return updated; +} - return NULL; +uint64_t GPU_material_uuid_get(GPUMaterial *mat) +{ + return mat->uuid; } GPUMaterial *GPU_material_from_nodetree(Scene *scene, - struct Material *ma, - struct bNodeTree *ntree, + Material *ma, + bNodeTree *ntree, ListBase *gpumaterials, - const void *engine_type, - const int options, - const bool is_volume_shader, - const char *vert_code, - const char *geom_code, - const char *frag_lib, - const char *defines, const char *name, - GPUMaterialEvalCallbackFn callback) + uint64_t shader_uuid, + bool is_volume_shader, + bool is_lookdev, + GPUCodegenCallbackFn callback, + void *thunk) { - LinkData *link; - bool has_volume_output, has_surface_output; - - /* Caller must re-use materials. */ - BLI_assert(GPU_material_from_nodetree_find(gpumaterials, engine_type, options) == NULL); - - /* HACK: Eevee assume this to create #GHash keys. */ - BLI_assert(sizeof(GPUPass) > 16); + /* Search if this material is not already compiled. */ + LISTBASE_FOREACH (LinkData *, link, gpumaterials) { + GPUMaterial *mat = (GPUMaterial *)link->data; + if (mat->uuid == shader_uuid) { + return mat; + } + } - /* allocate material */ GPUMaterial *mat = MEM_callocN(sizeof(GPUMaterial), "GPUMaterial"); mat->ma = ma; mat->scene = scene; - mat->engine_type = engine_type; - mat->options = options; + mat->uuid = shader_uuid; + mat->flag = GPU_MATFLAG_UPDATED; mat->is_volume_shader = is_volume_shader; + mat->graph.used_libraries = BLI_gset_new( + BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "GPUNodeGraph.used_libraries"); #ifndef NDEBUG BLI_snprintf(mat->name, sizeof(mat->name), "%s", name); #else UNUSED_VARS(name); #endif + if (is_lookdev) { + mat->flag |= GPU_MATFLAG_LOOKDEV_HACK; + } - mat->used_libraries = BLI_gset_new( - BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "GPUMaterial.used_libraries"); - - /* localize tree to create links for reroute and mute */ + /* Localize tree to create links for reroute and mute. */ bNodeTree *localtree = ntreeLocalize(ntree); - ntreeGPUMaterialNodes(localtree, mat, &has_surface_output, &has_volume_output); + ntreeGPUMaterialNodes(localtree, mat); gpu_material_ramp_texture_build(mat); - mat->has_surface_output = has_surface_output; - mat->has_volume_output = has_volume_output; - - if (mat->graph.outlink) { - if (callback) { - callback(mat, options, &vert_code, &geom_code, &frag_lib, &defines); - } - /* HACK: this is only for eevee. We add the define here after the nodetree evaluation. */ - if (GPU_material_flag_get(mat, GPU_MATFLAG_SSS)) { - defines = BLI_string_joinN(defines, - "#ifndef USE_ALPHA_BLEND\n" - "# define USE_SSS\n" - "#endif\n"); - } + if (mat->graph.outlink_surface || mat->graph.outlink_volume) { /* Create source code and search pass cache for an already compiled version. */ - mat->pass = GPU_generate_pass(mat, &mat->graph, vert_code, geom_code, frag_lib, defines); - - if (GPU_material_flag_get(mat, GPU_MATFLAG_SSS)) { - MEM_freeN((char *)defines); - } + mat->pass = GPU_generate_pass(mat, &mat->graph, callback, thunk); if (mat->pass == NULL) { /* We had a cache hit and the shader has already failed to compile. */ @@ -665,7 +382,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, gpu_node_graph_free_nodes(&mat->graph); } else { - mat->status = GPU_MAT_QUEUED; + mat->status = GPU_MAT_CREATED; } } } @@ -674,17 +391,15 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, gpu_node_graph_free(&mat->graph); } - /* Only free after GPU_pass_shader_get where GPUUniformBuf - * read data from the local tree. */ + /* Only free after GPU_pass_shader_get where GPUUniformBuf read data from the local tree. */ ntreeFreeLocalTree(localtree); BLI_assert(!localtree->id.py_instance); /* Or call #BKE_libblock_free_data_py. */ MEM_freeN(localtree); - /* note that even if building the shader fails in some way, we still keep + /* Note that even if building the shader fails in some way, we still keep * it to avoid trying to compile again and again, and simply do not use - * the actual shader on drawing */ - - link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink"); + * the actual shader on drawing. */ + LinkData *link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink"); link->data = mat; BLI_addtail(gpumaterials, link); @@ -706,6 +421,8 @@ void GPU_material_compile(GPUMaterial *mat) success = GPU_pass_compile(mat->pass, __func__); #endif + mat->flag |= GPU_MATFLAG_UPDATED; + if (success) { GPUShader *sh = GPU_pass_shader_get(mat->pass); if (sh != NULL) { @@ -731,5 +448,6 @@ void GPU_materials_free(Main *bmain) GPU_material_free(&wo->gpumaterial); } + BKE_world_defaults_free_gpu(); BKE_material_defaults_free_gpu(); } diff --git a/source/blender/gpu/intern/gpu_material_library.c b/source/blender/gpu/intern/gpu_material_library.c index 712ab44adb3..6f68729eaae 100644 --- a/source/blender/gpu/intern/gpu_material_library.c +++ b/source/blender/gpu/intern/gpu_material_library.c @@ -757,7 +757,7 @@ static const char *GPU_DATATYPE_STR[17] = { const char *gpu_data_type_to_string(const eGPUType type) { - return GPU_DATATYPE_STR[type]; + return (type == GPU_CLOSURE) ? "Closure" : GPU_DATATYPE_STR[type]; } static void gpu_parse_material_library(GHash *hash, GPUMaterialLibrary *library) @@ -893,14 +893,10 @@ GPUFunction *gpu_material_library_use_function(GSet *used_libraries, const char return function; } -char *gpu_material_library_generate_code(GSet *used_libraries, const char *frag_lib) +char *gpu_material_library_generate_code(GSet *used_libraries) { DynStr *ds = BLI_dynstr_new(); - if (frag_lib) { - BLI_dynstr_append(ds, frag_lib); - } - /* Always include those because they may be needed by the execution function. */ gpu_material_use_library_with_dependencies(used_libraries, &gpu_shader_material_world_normals_library); diff --git a/source/blender/gpu/intern/gpu_material_library.h b/source/blender/gpu/intern/gpu_material_library.h index 7f9eeb8166a..d9c56ddd425 100644 --- a/source/blender/gpu/intern/gpu_material_library.h +++ b/source/blender/gpu/intern/gpu_material_library.h @@ -26,6 +26,10 @@ #include "GPU_material.h" +#ifdef __cplusplus +extern "C" { +#endif + #define MAX_FUNCTION_NAME 64 #define MAX_PARAMETER 36 @@ -58,9 +62,13 @@ void gpu_material_library_exit(void); /* Code Generation */ GPUFunction *gpu_material_library_use_function(struct GSet *used_libraries, const char *name); -char *gpu_material_library_generate_code(struct GSet *used_libraries, const char *frag_lib); +char *gpu_material_library_generate_code(struct GSet *used_libraries); /* Code Parsing */ const char *gpu_str_skip_token(const char *str, char *token, int max); const char *gpu_data_type_to_string(eGPUType type); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index e0d60aa5fda..65848620339 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -103,10 +103,6 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType input->type = type; switch (link->link_type) { - case GPU_NODE_LINK_BUILTIN: - input->source = GPU_SOURCE_BUILTIN; - input->builtin = link->builtin; - break; case GPU_NODE_LINK_OUTPUT: input->source = GPU_SOURCE_OUTPUT; input->link = link; @@ -619,26 +615,18 @@ GPUNodeLink *GPU_volume_grid(GPUMaterial *mat, return link; } -GPUNodeLink *GPU_builtin(eGPUBuiltin builtin) -{ - GPUNodeLink *link = gpu_node_link_create(); - link->link_type = GPU_NODE_LINK_BUILTIN; - link->builtin = builtin; - return link; -} - /* Creating Nodes */ bool GPU_link(GPUMaterial *mat, const char *name, ...) { - GSet *used_libraries = gpu_material_used_libraries(mat); + GPUNodeGraph *graph = gpu_material_node_graph(mat); GPUNode *node; GPUFunction *function; GPUNodeLink *link, **linkptr; va_list params; int i; - function = gpu_material_library_use_function(used_libraries, name); + function = gpu_material_library_use_function(graph->used_libraries, name); if (!function) { fprintf(stderr, "GPU failed to find function %s\n", name); return false; @@ -659,27 +647,25 @@ bool GPU_link(GPUMaterial *mat, const char *name, ...) } va_end(params); - GPUNodeGraph *graph = gpu_material_node_graph(mat); BLI_addtail(&graph->nodes, node); return true; } -bool GPU_stack_link(GPUMaterial *material, - bNode *bnode, - const char *name, - GPUNodeStack *in, - GPUNodeStack *out, - ...) +static bool gpu_stack_link_v(GPUMaterial *material, + bNode *bnode, + const char *name, + GPUNodeStack *in, + GPUNodeStack *out, + va_list params) { - GSet *used_libraries = gpu_material_used_libraries(material); + GPUNodeGraph *graph = gpu_material_node_graph(material); GPUNode *node; GPUFunction *function; GPUNodeLink *link, **linkptr; - va_list params; int i, totin, totout; - function = gpu_material_library_use_function(used_libraries, name); + function = gpu_material_library_use_function(graph->used_libraries, name); if (!function) { fprintf(stderr, "GPU failed to find function %s\n", name); return false; @@ -707,7 +693,6 @@ bool GPU_stack_link(GPUMaterial *material, } } - va_start(params, out); for (i = 0; i < function->totparam; i++) { if (function->paramqual[i] != FUNCTION_QUAL_IN) { if (totout == 0) { @@ -733,14 +718,51 @@ bool GPU_stack_link(GPUMaterial *material, } } } - va_end(params); - GPUNodeGraph *graph = gpu_material_node_graph(material); BLI_addtail(&graph->nodes, node); return true; } +bool GPU_stack_link(GPUMaterial *material, + bNode *bnode, + const char *name, + GPUNodeStack *in, + GPUNodeStack *out, + ...) +{ + va_list params; + va_start(params, out); + bool valid = gpu_stack_link_v(material, bnode, name, in, out, params); + va_end(params); + + return valid; +} + +bool GPU_stack_eval_link(GPUMaterial *material, + bNode *bnode, + const char *name, + GPUNodeStack *in, + GPUNodeStack *out, + ...) +{ + /* Save the closure link to replace the one created by the eval function call. Avoiding + * dependency to the eval call before the end of the graph. */ + GPUNodeLink *closure_out = out[0].link; + + va_list params; + va_start(params, out); + bool valid = gpu_stack_link_v(material, bnode, name, in, out, params); + va_end(params); + + /* Save both nodes for graph amendment. */ + GPU_material_add_closure_eval(material, closure_out, out[0].link); + /* Restore original link. */ + out[0].link = closure_out; + + return valid; +} + GPUNodeLink *GPU_uniformbuf_link_out(GPUMaterial *mat, bNode *node, GPUNodeStack *stack, @@ -802,7 +824,11 @@ void gpu_node_graph_free_nodes(GPUNodeGraph *graph) gpu_node_free(node); } - graph->outlink = NULL; + BLI_freelistN(&graph->eval_nodes); + graph->outlink_surface = NULL; + graph->outlink_volume = NULL; + graph->outlink_displacement = NULL; + graph->outlink_thickness = NULL; } void gpu_node_graph_free(GPUNodeGraph *graph) @@ -817,28 +843,32 @@ void gpu_node_graph_free(GPUNodeGraph *graph) BLI_freelistN(&graph->textures); BLI_freelistN(&graph->attributes); GPU_uniform_attr_list_free(&graph->uniform_attrs); + + if (graph->used_libraries) { + BLI_gset_free(graph->used_libraries, NULL); + graph->used_libraries = NULL; + } } /* Prune Unused Nodes */ -static void gpu_nodes_tag(GPUNodeLink *link) +static void gpu_nodes_tag(GPUNodeLink *link, eGPUNodeTag tag) { GPUNode *node; - GPUInput *input; - if (!link->output) { + if (!link || !link->output) { return; } node = link->output->node; - if (node->tag) { + if (node->tag & tag) { return; } - node->tag = true; - for (input = node->inputs.first; input; input = input->next) { + node->tag |= tag; + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { if (input->link) { - gpu_nodes_tag(input->link); + gpu_nodes_tag(input->link, tag); } } } @@ -846,18 +876,30 @@ static void gpu_nodes_tag(GPUNodeLink *link) void gpu_node_graph_prune_unused(GPUNodeGraph *graph) { LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - node->tag = false; + node->tag = GPU_NODE_TAG_NONE; } - gpu_nodes_tag(graph->outlink); + gpu_nodes_tag(graph->outlink_surface, GPU_NODE_TAG_SURFACE); + gpu_nodes_tag(graph->outlink_volume, GPU_NODE_TAG_VOLUME); + gpu_nodes_tag(graph->outlink_displacement, GPU_NODE_TAG_DISPLACEMENT); + gpu_nodes_tag(graph->outlink_thickness, GPU_NODE_TAG_THICKNESS); + LISTBASE_FOREACH (GPUNodeGraphOutputLink *, aovlink, &graph->outlink_aovs) { - gpu_nodes_tag(aovlink->outlink); + gpu_nodes_tag(aovlink->outlink, GPU_NODE_TAG_AOV); + } + + LISTBASE_FOREACH (GPUNodeGraphEvalNode *, node, &graph->eval_nodes) { + /* Copy weight node tag to avoid pruning of eval node since they are node connected to + * output. */ + if (node->weight_node->tag != GPU_NODE_TAG_NONE) { + node->eval_node->tag = GPU_NODE_TAG_EVAL | node->weight_node->tag; + } } for (GPUNode *node = graph->nodes.first, *next = NULL; node; node = next) { next = node->next; - if (!node->tag) { + if (node->tag == GPU_NODE_TAG_NONE) { BLI_remlink(&graph->nodes, node); gpu_node_free(node); } diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h index 8a112ac9c4d..9f1a729738b 100644 --- a/source/blender/gpu/intern/gpu_node_graph.h +++ b/source/blender/gpu/intern/gpu_node_graph.h @@ -28,9 +28,15 @@ #include "DNA_customdata_types.h" #include "DNA_listBase.h" +#include "BLI_ghash.h" + #include "GPU_material.h" #include "GPU_shader.h" +#ifdef __cplusplus +extern "C" { +#endif + struct GPUNode; struct GPUOutput; struct ListBase; @@ -41,7 +47,6 @@ typedef enum eGPUDataSource { GPU_SOURCE_UNIFORM, GPU_SOURCE_ATTR, GPU_SOURCE_UNIFORM_ATTR, - GPU_SOURCE_BUILTIN, GPU_SOURCE_STRUCT, GPU_SOURCE_TEX, GPU_SOURCE_TEX_TILED_MAPPING, @@ -53,7 +58,6 @@ typedef enum { GPU_NODE_LINK_NONE = 0, GPU_NODE_LINK_ATTR, GPU_NODE_LINK_UNIFORM_ATTR, - GPU_NODE_LINK_BUILTIN, GPU_NODE_LINK_COLORBAND, GPU_NODE_LINK_CONSTANT, GPU_NODE_LINK_IMAGE, @@ -65,13 +69,23 @@ typedef enum { GPU_NODE_LINK_UNIFORM, } GPUNodeLinkType; +typedef enum { + GPU_NODE_TAG_NONE = 0, + GPU_NODE_TAG_SURFACE = (1 << 0), + GPU_NODE_TAG_VOLUME = (1 << 1), + GPU_NODE_TAG_DISPLACEMENT = (1 << 2), + GPU_NODE_TAG_THICKNESS = (1 << 3), + GPU_NODE_TAG_AOV = (1 << 4), + GPU_NODE_TAG_EVAL = (1 << 5), +} eGPUNodeTag; + struct GPUNode { struct GPUNode *next, *prev; const char *name; /* Internal flag to mark nodes during pruning */ - bool tag; + eGPUNodeTag tag; ListBase inputs; ListBase outputs; @@ -86,8 +100,6 @@ struct GPUNodeLink { union { /* GPU_NODE_LINK_CONSTANT | GPU_NODE_LINK_UNIFORM */ const float *data; - /* GPU_NODE_LINK_BUILTIN */ - eGPUBuiltin builtin; /* GPU_NODE_LINK_COLORBAND */ struct GPUTexture **colorband; /* GPU_NODE_LINK_VOLUME_GRID */ @@ -126,8 +138,6 @@ typedef struct GPUInput { union { /* GPU_SOURCE_CONSTANT | GPU_SOURCE_UNIFORM */ float vec[16]; /* vector data */ - /* GPU_SOURCE_BUILTIN */ - eGPUBuiltin builtin; /* builtin uniform */ /* GPU_SOURCE_TEX | GPU_SOURCE_TEX_TILED_MAPPING */ struct GPUMaterialTexture *texture; /* GPU_SOURCE_ATTR */ @@ -145,14 +155,25 @@ typedef struct GPUNodeGraphOutputLink { GPUNodeLink *outlink; } GPUNodeGraphOutputLink; +typedef struct GPUNodeGraphEvalNode { + struct GPUNodeGraphEvalNode *next, *prev; + GPUNode *weight_node; + GPUNode *eval_node; +} GPUNodeGraphEvalNode; + typedef struct GPUNodeGraph { /* Nodes */ ListBase nodes; - /* Main Output. */ - GPUNodeLink *outlink; + /* Main Outputs. */ + GPUNodeLink *outlink_surface; + GPUNodeLink *outlink_volume; + GPUNodeLink *outlink_displacement; + GPUNodeLink *outlink_thickness; /* List of GPUNodeGraphOutputLink */ ListBase outlink_aovs; + /* List of GPUNodeGraphEvalNode. */ + ListBase eval_nodes; /* Requested attributes and textures. */ ListBase attributes; @@ -161,6 +182,9 @@ typedef struct GPUNodeGraph { /* The list of uniform attributes. */ GPUUniformAttrList uniform_attrs; + + /** Set of all the GLSL lib code blocks . */ + GSet *used_libraries; } GPUNodeGraph; /* Node Graph */ @@ -187,4 +211,6 @@ struct GPUTexture **gpu_material_ramp_texture_row_set(struct GPUMaterial *mat, float *pixels, float *row); -struct GSet *gpu_material_used_libraries(struct GPUMaterial *material); +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index ef800abc3c9..830e65f69d9 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -610,6 +610,12 @@ int GPU_shader_get_builtin_block(GPUShader *shader, int builtin) return interface->ubo_builtin((GPUUniformBlockBuiltin)builtin); } +int GPU_shader_get_builtin_ssbo(GPUShader *shader, int builtin) +{ + ShaderInterface *interface = unwrap(shader)->interface; + return interface->buffer_builtin((GPUBufferBlockBuiltin)builtin); +} + int GPU_shader_get_ssbo(GPUShader *shader, const char *name) { ShaderInterface *interface = unwrap(shader)->interface; diff --git a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc index 40e54ab4394..469085026b3 100644 --- a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc +++ b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc @@ -216,10 +216,7 @@ int BKE_subdiv_ccg_grid_to_face_index(const SubdivCCG *UNUSED(subdiv_ccg), /* -------------------------------------------------------------------- */ /** \name Stubs of BKE_node.h * \{ */ -void ntreeGPUMaterialNodes(struct bNodeTree *UNUSED(localtree), - struct GPUMaterial *UNUSED(mat), - bool *UNUSED(has_surface_output), - bool *UNUSED(has_volume_output)) +void ntreeGPUMaterialNodes(struct bNodeTree *UNUSED(localtree), struct GPUMaterial *UNUSED(mat)) { BLI_assert_unreachable(); } diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh index 8c6d6ede11b..f94006d1407 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.hh +++ b/source/blender/gpu/intern/gpu_shader_interface.hh @@ -71,6 +71,7 @@ class ShaderInterface { /** Location of builtin uniforms. Fast access, no lookup needed. */ int32_t builtins_[GPU_NUM_UNIFORMS]; int32_t builtin_blocks_[GPU_NUM_UNIFORM_BLOCKS]; + int32_t builtin_buffers_[GPU_NUM_BUFFER_BLOCKS]; public: ShaderInterface(); @@ -126,10 +127,17 @@ class ShaderInterface { BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORM_BLOCKS); return builtin_blocks_[builtin]; } + /* Returns binding position. */ + inline int32_t buffer_builtin(const GPUBufferBlockBuiltin builtin) const + { + BLI_assert(builtin >= 0 && builtin < GPU_NUM_BUFFER_BLOCKS); + return builtin_buffers_[builtin]; + } protected: static inline const char *builtin_uniform_name(GPUUniformBuiltin u); static inline const char *builtin_uniform_block_name(GPUUniformBlockBuiltin u); + static inline const char *builtin_buffer_block_name(GPUBufferBlockBuiltin u); inline uint32_t set_input_name(ShaderInput *input, char *name, uint32_t name_len) const; inline void copy_input_name(ShaderInput *input, @@ -223,6 +231,16 @@ inline const char *ShaderInterface::builtin_uniform_block_name(GPUUniformBlockBu } } +inline const char *ShaderInterface::builtin_buffer_block_name(GPUBufferBlockBuiltin u) +{ + switch (u) { + case GPU_BUFFER_BLOCK_DEBUG: + return "debugBuf"; + default: + return NULL; + } +} + /* Returns string length including '\0' terminator. */ inline uint32_t ShaderInterface::set_input_name(ShaderInput *input, char *name, diff --git a/source/blender/gpu/intern/gpu_shader_log.cc b/source/blender/gpu/intern/gpu_shader_log.cc index 12459b4b721..0c328876485 100644 --- a/source/blender/gpu/intern/gpu_shader_log.cc +++ b/source/blender/gpu/intern/gpu_shader_log.cc @@ -41,6 +41,9 @@ namespace blender::gpu { /** \name Debug functions * \{ */ +/* Number of lines before and after the error line to print for compilation errors. */ +#define DEBUG_CONTEXT_LINES 2 + void Shader::print_log(Span<const char *> sources, char *log, const char *stage, @@ -102,11 +105,10 @@ void Shader::print_log(Span<const char *> sources, found_line_id = true; break; } -/* TODO(fclem) Make this an option to display N lines before error. */ -#if 0 /* Uncomment to print shader file up to the error line to have more context. */ - BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index); - BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line); -#endif + if (src_line_index >= log_item.cursor.row - DEBUG_CONTEXT_LINES) { + BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index); + BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line); + } /* Continue to next line. */ src_line = src_line_end + 1; src_line_index++; @@ -129,6 +131,20 @@ void Shader::print_log(Span<const char *> sources, BLI_dynstr_appendf(dynstr, "^"); } BLI_dynstr_appendf(dynstr, "\n"); + + /* Skip the error line. */ + src_line = src_line_end + 1; + src_line_index++; + while ((src_line_end = strchr(src_line, '\n'))) { + if (src_line_index > log_item.cursor.row + DEBUG_CONTEXT_LINES) { + break; + } + BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index); + BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line); + /* Continue to next line. */ + src_line = src_line_end + 1; + src_line_index++; + } } } BLI_dynstr_appendf(dynstr, line_prefix); diff --git a/source/blender/gpu/opengl/gl_framebuffer.hh b/source/blender/gpu/opengl/gl_framebuffer.hh index 9ebe549efe7..00a7676dd3f 100644 --- a/source/blender/gpu/opengl/gl_framebuffer.hh +++ b/source/blender/gpu/opengl/gl_framebuffer.hh @@ -141,6 +141,8 @@ static inline GLenum to_gl(const GPUAttachmentType type) ATTACHMENT(COLOR_ATTACHMENT3); ATTACHMENT(COLOR_ATTACHMENT4); ATTACHMENT(COLOR_ATTACHMENT5); + ATTACHMENT(COLOR_ATTACHMENT6); + ATTACHMENT(COLOR_ATTACHMENT7); default: BLI_assert(0); return GL_COLOR_ATTACHMENT0; diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc index 299ea150408..467731f2889 100644 --- a/source/blender/gpu/opengl/gl_shader_interface.cc +++ b/source/blender/gpu/opengl/gl_shader_interface.cc @@ -117,7 +117,28 @@ static inline int image_binding(int32_t program, switch (type) { case GL_IMAGE_1D: case GL_IMAGE_2D: - case GL_IMAGE_3D: { + case GL_IMAGE_3D: + case GL_IMAGE_CUBE: + case GL_IMAGE_BUFFER: + case GL_IMAGE_1D_ARRAY: + case GL_IMAGE_2D_ARRAY: + case GL_IMAGE_CUBE_MAP_ARRAY: + case GL_INT_IMAGE_1D: + case GL_INT_IMAGE_2D: + case GL_INT_IMAGE_3D: + case GL_INT_IMAGE_CUBE: + case GL_INT_IMAGE_BUFFER: + case GL_INT_IMAGE_1D_ARRAY: + case GL_INT_IMAGE_2D_ARRAY: + case GL_INT_IMAGE_CUBE_MAP_ARRAY: + case GL_UNSIGNED_INT_IMAGE_1D: + case GL_UNSIGNED_INT_IMAGE_2D: + case GL_UNSIGNED_INT_IMAGE_3D: + case GL_UNSIGNED_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_IMAGE_BUFFER: + case GL_UNSIGNED_INT_IMAGE_1D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: { /* For now just assign a consecutive index. In the future, we should set it in * the shader using layout(binding = i) and query its value. */ int binding = *image_len; @@ -313,6 +334,12 @@ GLShaderInterface::GLShaderInterface(GLuint program) builtin_blocks_[u] = (block != nullptr) ? block->binding : -1; } + for (int32_t u_int = 0; u_int < GPU_NUM_BUFFER_BLOCKS; u_int++) { + GPUBufferBlockBuiltin u = static_cast<GPUBufferBlockBuiltin>(u_int); + const ShaderInput *buffer = this->ssbo_get(builtin_buffer_block_name(u)); + builtin_buffers_[u] = (buffer != nullptr) ? buffer->binding : -1; + } + MEM_freeN(uniforms_from_blocks); /* Resize name buffer to save some memory. */ diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh index 83ff3ffc9e9..5985f1583f2 100644 --- a/source/blender/gpu/opengl/gl_state.hh +++ b/source/blender/gpu/opengl/gl_state.hh @@ -124,11 +124,20 @@ static inline GLbitfield to_gl(eGPUBarrier barrier_bits) if (barrier_bits & GPU_BARRIER_SHADER_IMAGE_ACCESS) { barrier |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT; } + if (barrier_bits & GPU_BARRIER_SHADER_STORAGE) { + barrier |= GL_SHADER_STORAGE_BARRIER_BIT; + } if (barrier_bits & GPU_BARRIER_TEXTURE_FETCH) { barrier |= GL_TEXTURE_FETCH_BARRIER_BIT; } - if (barrier_bits & GPU_BARRIER_SHADER_STORAGE) { - barrier |= GL_SHADER_STORAGE_BARRIER_BIT; + if (barrier_bits & GPU_BARRIER_TEXTURE_UPDATE) { + barrier |= GL_TEXTURE_UPDATE_BARRIER_BIT; + } + if (barrier_bits & GPU_BARRIER_COMMAND) { + barrier |= GL_COMMAND_BARRIER_BIT; + } + if (barrier_bits & GPU_BARRIER_FRAMEBUFFER) { + barrier |= GL_FRAMEBUFFER_BARRIER_BIT; } if (barrier_bits & GPU_BARRIER_VERTEX_ATTRIB_ARRAY) { barrier |= GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT; diff --git a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl index 193a4190cbf..086ff5b0217 100644 --- a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl +++ b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl @@ -1,97 +1,28 @@ -vec3 calc_barycentric_distances(vec3 pos0, vec3 pos1, vec3 pos2) -{ - vec3 edge21 = pos2 - pos1; - vec3 edge10 = pos1 - pos0; - vec3 edge02 = pos0 - pos2; - vec3 d21 = normalize(edge21); - vec3 d10 = normalize(edge10); - vec3 d02 = normalize(edge02); - - vec3 dists; - float d = dot(d21, edge02); - dists.x = sqrt(dot(edge02, edge02) - d * d); - d = dot(d02, edge10); - dists.y = sqrt(dot(edge10, edge10) - d * d); - d = dot(d10, edge21); - dists.z = sqrt(dot(edge21, edge21) - d * d); - return dists; -} - -vec2 calc_barycentric_co(int vertid) -{ - vec2 bary; - bary.x = float((vertid % 3) == 0); - bary.y = float((vertid % 3) == 1); - return bary; -} - -#ifdef HAIR_SHADER - -/* Hairs uv and col attributes are passed by bufferTextures. */ -# define DEFINE_ATTR(type, attr) uniform samplerBuffer attr -# define GET_ATTR(type, attr) hair_get_customdata_##type(attr) - -# define barycentric_get() hair_get_barycentric() -# define barycentric_resolve(bary) hair_resolve_barycentric(bary) - -vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], const samplerBuffer orco_samp) -{ - /* TODO: fix ORCO with modifiers. */ - vec3 orco = (modelmatinv * vec4(local_pos, 1.0)).xyz; - return orco_madd[0].xyz + orco * orco_madd[1].xyz; -} - -float hair_len_get(int id, const samplerBuffer len) -{ - return texelFetch(len, id).x; -} - -vec4 tangent_get(const samplerBuffer attr, mat3 normalmat) -{ - /* Unsupported */ - return vec4(0.0); -} - -#else /* MESH_SHADER */ - -# define DEFINE_ATTR(type, attr) in type attr -# define GET_ATTR(type, attr) attr - -/* Calculated in geom shader later with calc_barycentric_co. */ -# define barycentric_get() vec2(0) -# define barycentric_resolve(bary) bary - -vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], vec4 orco) -{ - /* If the object does not have any deformation, the orco layer calculation is done on the fly - * using the orco_madd factors. - * We know when there is no orco layer when orco.w is 1.0 because it uses the generic vertex - * attrib (which is [0,0,0,1]). */ - if (orco.w == 0.0) { - return orco.xyz * 0.5 + 0.5; - } - else { - return orco_madd[0].xyz + local_pos * orco_madd[1].xyz; - } -} - -float hair_len_get(int id, const float len) -{ - return len; -} - -vec4 tangent_get(vec4 attr, mat3 normalmat) -{ - vec4 tangent; - tangent.xyz = normalmat * attr.xyz; - tangent.w = attr.w; - float len_sqr = dot(tangent.xyz, tangent.xyz); - /* Normalize only if vector is not null. */ - if (len_sqr > 0.0) { - tangent.xyz *= inversesqrt(len_sqr); - } - return tangent; -} - +/* Assumes GPU_VEC4 is color data. So converting to luminance like cycles. */ +#define float_from_vec4(v) dot(v.rgb, vec3(0.2126, 0.7152, 0.0722)) +#define float_from_vec3(v) avg(v.rgb) +#define float_from_vec2(v) v.r + +#define vec2_from_vec4(v) vec2(avg(v.rgb), v.a) +#define vec2_from_vec3(v) vec2(avg(v.rgb), 1.0) +#define vec2_from_float(v) vec2(v) + +#define vec3_from_vec4(v) v.rgb +#define vec3_from_vec2(v) v.rrr +#define vec3_from_float(v) vec3(v) + +#define vec4_from_vec3(v) vec4(v, 1.0) +#define vec4_from_vec2(v) v.rrrg +#define vec4_from_float(v) vec4(vec3(v), 1.0) + +#define RAY_TYPE_CAMERA 0 +#define RAY_TYPE_SHADOW 1 +#define RAY_TYPE_DIFFUSE 2 +#define RAY_TYPE_GLOSSY 3 + +#ifdef GPU_FRAGMENT_SHADER +# define FrontFacing gl_FrontFacing +#else +# define FrontFacing true #endif diff --git a/source/blender/gpu/shaders/gpu_shader_common_obinfos_lib.glsl b/source/blender/gpu/shaders/gpu_shader_common_obinfos_lib.glsl deleted file mode 100644 index f5b6de4899f..00000000000 --- a/source/blender/gpu/shaders/gpu_shader_common_obinfos_lib.glsl +++ /dev/null @@ -1,23 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_view_lib.glsl) - -#ifndef GPU_OBINFOS_UBO -# define GPU_OBINFOS_UBO -struct ObjectInfos { - vec4 drw_OrcoTexCoFactors[2]; - vec4 drw_ObjectColor; - vec4 drw_Infos; -}; - -# ifndef USE_GPU_SHADER_CREATE_INFO -layout(std140) uniform infoBlock -{ - /* DRW_RESOURCE_CHUNK_LEN = 512 */ - ObjectInfos drw_infos[512]; -}; -# endif - -# define OrcoTexCoFactors (drw_infos[resource_id].drw_OrcoTexCoFactors) -# define ObjectInfo (drw_infos[resource_id].drw_Infos) -# define ObjectColor (drw_infos[resource_id].drw_ObjectColor) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl index 12921a31b23..c2062be667b 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl @@ -1,4 +1,3 @@ -#ifndef VOLUMETRICS void node_ambient_occlusion(vec4 color, float dist, vec3 normal, @@ -11,16 +10,12 @@ void node_ambient_occlusion(vec4 color, vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy); OcclusionData data = occlusion_search(viewPosition, maxzBuffer, dist, inverted, sample_count); - vec3 V = cameraVec(worldPosition); + vec3 V = cameraVec(g_data.P); vec3 N = normalize(normal); - vec3 Ng = safe_normalize(cross(dFdx(worldPosition), dFdy(worldPosition))); + vec3 Ng = safe_normalize(cross(dFdx(g_data.P), dFdy(g_data.P))); float unused_error; vec3 unused; occlusion_eval(data, V, N, Ng, inverted, result_ao, unused_error, unused); result_color = result_ao * color; } -#else -/* Stub ambient occlusion because it is not compatible with volumetrics. */ -# define node_ambient_occlusion(a, b, c, d, e, f) (e = vec4(0); f = 0.0) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl index ec49cc86761..e70f62dfc59 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl @@ -1,17 +1,20 @@ -#ifndef VOLUMETRICS void node_bsdf_anisotropic(vec4 color, float roughness, float anisotropy, float rotation, vec3 N, vec3 T, - const float use_multiscatter, - const float ssr_id, + float weight, out Closure result) { - node_bsdf_glossy(color, roughness, N, use_multiscatter, ssr_id, result); + closure_weight_add(g_reflection_data, weight); +} + +void node_bsdf_anisotropic_eval( + vec4 color, float roughness, vec3 N, float weight, float use_multiscatter, out Closure result) +{ + if (closure_weight_threshold(g_reflection_data, weight)) { + g_reflection_data.color = color.rgb * weight; + g_reflection_data.N = N; + } } -#else -/* Stub anisotropic because it is not compatible with volumetrics. */ -# define node_bsdf_anisotropic(a, b, c, d, e, f, g, h, result) (result = CLOSURE_DEFAULT) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_background.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_background.glsl index 69ef4dcb7c7..7402e14da0a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_background.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_background.glsl @@ -1,11 +1,4 @@ -void node_background(vec4 color, float strength, out Closure result) +void node_background(vec4 color, float strength, float weight, out Closure result) { -#ifndef VOLUMETRICS - color *= strength; - result = CLOSURE_DEFAULT; - result.radiance = color.rgb; - result.transmittance = vec3(0.0); -#else - result = CLOSURE_DEFAULT; -#endif + g_emission_data.emission += color.rgb * strength * weight; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl index 9f73f654217..b9e5d26f7ed 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl @@ -14,15 +14,15 @@ void node_bump(float strength, float height_dx, float height_dy, vec3 N, - vec3 surf_pos, float invert, out vec3 result) { - N = mat3(ViewMatrix) * normalize(N); - dist *= gl_FrontFacing ? invert : -invert; + N = normalize(N); + dist *= FrontFacing ? invert : -invert; - vec3 dPdx = dFdx(surf_pos); - vec3 dPdy = dFdy(surf_pos); +#ifdef GPU_FRAGMENT_SHADER + vec3 dPdx = dFdx(g_data.P); + vec3 dPdy = dFdy(g_data.P); /* Get surface tangents from normal. */ vec3 Rx = cross(dPdy, N); @@ -39,6 +39,7 @@ void node_bump(float strength, result = normalize(abs(det) * N - dist * sign(det) * surfgrad); result = normalize(mix(N, result, strength)); - - result = mat3(ViewMatrixInverse) * result; +#else + result = N; +#endif } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl index 03e61e9f472..29319fe342a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl @@ -1,6 +1,6 @@ -void camera(vec3 co, out vec3 outview, out float outdepth, out float outdist) +void camera(out vec3 outview, out float outdepth, out float outdist) { - outdepth = abs(co.z); - outdist = length(co); - outview = normalize(co); + outdepth = abs(g_data.P.z); + outdist = length(g_data.P); + outview = normalize(-g_data.P); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl index 01a16e194ca..973c830c476 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl @@ -1,28 +1,13 @@ -#ifndef VOLUMETRICS -CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_diffuse, Diffuse) - -void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out Closure result) +void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, float weight, out Closure result) { - CLOSURE_VARS_DECLARE_1(Diffuse); - - in_Diffuse_0.N = N; /* Normalized during eval. */ - in_Diffuse_0.albedo = color.rgb; - - CLOSURE_EVAL_FUNCTION_1(node_bsdf_diffuse, Diffuse); - - result = CLOSURE_DEFAULT; - - out_Diffuse_0.radiance = render_pass_diffuse_mask(vec3(1.0), out_Diffuse_0.radiance); - out_Diffuse_0.radiance *= color.rgb; - - result.radiance = out_Diffuse_0.radiance; - - /* TODO(fclem) Try to not use this. */ - closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result); + closure_weight_add(g_diffuse_data, weight); } -#else -/* Stub diffuse because it is not compatible with volumetrics. */ -# define node_bsdf_diffuse(a, b, c, d) (d = CLOSURE_DEFAULT) -#endif +void node_bsdf_diffuse_eval(vec4 color, float roughness, vec3 N, float weight, out Closure result) +{ + if (closure_weight_threshold(g_diffuse_data, weight)) { + g_diffuse_data.color = color.rgb * weight; + g_diffuse_data.N = N; + } +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl index 0838b5c8b71..3bfd718df5d 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl @@ -1,9 +1,8 @@ -void node_displacement_object( - float height, float midlevel, float scale, vec3 N, mat4 obmat, out vec3 result) +void node_displacement_object(float height, float midlevel, float scale, vec3 N, out vec3 result) { - N = (vec4(N, 0.0) * obmat).xyz; + N = normal_world_to_object(N); result = (height - midlevel) * scale * normalize(N); - result = (obmat * vec4(result, 0.0)).xyz; + result = normal_object_to_world(result); } void node_displacement_world(float height, float midlevel, float scale, vec3 N, out vec3 result) diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl index 0941482df45..f5dbffdee67 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl @@ -1,6 +1,3 @@ -#ifndef VOLUMETRICS - -CLOSURE_EVAL_FUNCTION_DECLARE_3(node_eevee_specular, Diffuse, Glossy, Glossy) void node_eevee_specular(vec4 diffuse, vec4 specular, @@ -12,69 +9,65 @@ void node_eevee_specular(vec4 diffuse, float clearcoat_roughness, vec3 clearcoat_normal, float occlusion, - float ssr_id, + float weight, out Closure result) { - CLOSURE_VARS_DECLARE_3(Diffuse, Glossy, Glossy); - - in_common.occlusion = occlusion; - - in_Diffuse_0.N = normal; /* Normalized during eval. */ - in_Diffuse_0.albedo = diffuse.rgb; - in_Glossy_1.N = normal; /* Normalized during eval. */ - in_Glossy_1.roughness = roughness; + /* Evaluate non sampled closures. */ + g_emission_data.emission += emissive.rgb * weight; + g_transparency_data.transmittance += vec3(transp) * weight; - in_Glossy_2.N = clearcoat_normal; /* Normalized during eval. */ - in_Glossy_2.roughness = clearcoat_roughness; + float alpha = (1.0 - transp) * weight; - CLOSURE_EVAL_FUNCTION_3(node_eevee_specular, Diffuse, Glossy, Glossy); + closure_weight_add(g_diffuse_data, alpha); + closure_weight_add(g_reflection_data, alpha); + closure_weight_add(g_reflection_data, alpha * clearcoat * 0.25); +} - result = CLOSURE_DEFAULT; +void node_eevee_specular_eval(vec4 diffuse, + vec4 specular, + float roughness, + vec4 emissive, + float transp, + vec3 N, + float clearcoat, + float clearcoat_roughness, + vec3 CN, + float occlusion, + float weight, + out Closure result) +{ + N = safe_normalize(N); + vec3 V = cameraVec(g_data.P); - vec3 V = cameraVec(worldPosition); + float alpha = (1.0 - transp) * weight; - { - /* Diffuse. */ - out_Diffuse_0.radiance = render_pass_diffuse_mask(vec3(1), out_Diffuse_0.radiance); - out_Diffuse_0.radiance *= in_Diffuse_0.albedo; - result.radiance += out_Diffuse_0.radiance; + float diffuse_weight = alpha; + if (closure_weight_threshold(g_diffuse_data, diffuse_weight)) { + g_diffuse_data.color = diffuse.rgb * diffuse_weight; + g_diffuse_data.N = N; } - { - /* Glossy. */ - float NV = dot(in_Glossy_1.N, V); - vec2 split_sum = brdf_lut(NV, in_Glossy_1.roughness); + + float specular_weight = alpha; + if (closure_weight_threshold(g_reflection_data, specular_weight)) { + float NV = dot(N, V); + vec2 split_sum = brdf_lut(NV, roughness); vec3 brdf = F_brdf_single_scatter(specular.rgb, vec3(1.0), split_sum); - out_Glossy_1.radiance = closure_mask_ssr_radiance(out_Glossy_1.radiance, ssr_id); - out_Glossy_1.radiance *= brdf; - out_Glossy_1.radiance = render_pass_glossy_mask(specular.rgb, out_Glossy_1.radiance); - closure_load_ssr_data( - out_Glossy_1.radiance, in_Glossy_1.roughness, in_Glossy_1.N, ssr_id, result); + g_reflection_data.color = specular.rgb * brdf * specular_weight; + g_reflection_data.N = N; + g_reflection_data.roughness = roughness; } - { - /* Clearcoat. */ - float NV = dot(in_Glossy_2.N, V); - vec2 split_sum = brdf_lut(NV, in_Glossy_2.roughness); + + float clearcoat_weight = alpha * clearcoat * 0.25; + if (closure_weight_threshold(g_reflection_data, clearcoat_weight)) { + CN = safe_normalize(CN); + float NV = dot(CN, V); + vec2 split_sum = brdf_lut(NV, clearcoat_roughness); vec3 brdf = F_brdf_single_scatter(vec3(0.04), vec3(1.0), split_sum); - out_Glossy_2.radiance *= brdf * clearcoat * 0.25; - out_Glossy_2.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_2.radiance); - result.radiance += out_Glossy_2.radiance; - } - { - /* Emission. */ - vec3 out_emission_radiance = render_pass_emission_mask(emissive.rgb); - result.radiance += out_emission_radiance; + g_reflection_data.color = brdf * clearcoat_weight; + g_reflection_data.N = CN; + g_reflection_data.roughness = clearcoat_roughness; } - - float alpha = 1.0 - transp; - result.transmittance = vec3(transp); - result.radiance *= alpha; - result.ssr_data.rgb *= alpha; } - -#else -/* Stub specular because it is not compatible with volumetrics. */ -# define node_eevee_specular(a, b, c, d, e, f, g, h, i, j, k, result) (result = CLOSURE_DEFAULT) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl index f2de7c2da39..fb7c814a2b3 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl @@ -1,10 +1,4 @@ -void node_emission(vec4 color, float strength, vec3 vN, out Closure result) +void node_emission(vec4 color, float strength, float weight, out Closure result) { - result = CLOSURE_DEFAULT; -#ifndef VOLUMETRICS - result.radiance = render_pass_emission_mask(color.rgb) * strength; - result.ssr_normal = normal_encode(vN, viewCameraVec(viewPosition)); -#else - result.emission = color.rgb * strength; -#endif + g_emission_data.emission += color.rgb * strength * weight; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl index 7a4d28f2dd6..9fb98d598ab 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl @@ -26,12 +26,11 @@ float fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta) return fresnel_dielectric_cos(dot(Incoming, Normal), eta); } -void node_fresnel(float ior, vec3 N, vec3 I, out float result) +void node_fresnel(float ior, vec3 N, out float result) { N = normalize(N); - /* handle perspective/orthographic */ - vec3 I_view = (ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0); + vec3 V = cameraVec(g_data.P); float eta = max(ior, 0.00001); - result = fresnel_dielectric(I_view, N, (gl_FrontFacing) ? eta : 1.0 / eta); + result = fresnel_dielectric(V, N, (FrontFacing) ? eta : 1.0 / eta); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl index a14ff5021bf..9c40715db47 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl @@ -1,9 +1,4 @@ -void node_geometry(vec3 I, - vec3 N, - vec3 orco, - mat4 objmat, - mat4 toworld, - vec2 barycentric, +void node_geometry(vec3 orco, out vec3 position, out vec3 normal, out vec3 tangent, @@ -15,8 +10,7 @@ void node_geometry(vec3 I, out float random_per_island) { /* handle perspective/orthographic */ - vec3 I_view = (ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0); - incoming = -(toworld * vec4(I_view, 0.0)).xyz; + incoming = cameraVec(g_data.P); #if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE) position = -incoming; @@ -27,14 +21,18 @@ void node_geometry(vec3 I, pointiness = 0.0; #else - position = worldPosition; + position = g_data.P; # ifndef VOLUMETRICS - normal = normalize(N); - vec3 B = dFdx(worldPosition); - vec3 T = dFdy(worldPosition); + normal = normalize(g_data.N); +# ifdef GPU_FRAGMENT_SHADER + vec3 B = dFdx(g_data.P); + vec3 T = dFdy(g_data.P); true_normal = normalize(cross(B, T)); +# else /* GPU_VERTEX_SHADER */ + true_normal = normal; +# endif # else - normal = (toworld * vec4(N, 0.0)).xyz; + normal = (toworld * vec4(g_data.N, 0.0)).xyz; true_normal = normal; # endif @@ -42,11 +40,11 @@ void node_geometry(vec3 I, tangent = -hairTangent; # else tangent_orco_z(orco, orco); - node_tangent(N, orco, objmat, tangent); + node_tangent(orco, tangent); # endif - parametric = vec3(barycentric, 0.0); - backfacing = (gl_FrontFacing) ? 0.0 : 1.0; + parametric = vec3(g_data.barycentric_coords, 0.0); + backfacing = (FrontFacing) ? 0.0 : 1.0; pointiness = 0.5; random_per_island = 0.0; #endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl index aa0a8873596..72d74949940 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl @@ -1,57 +1,49 @@ -#ifndef VOLUMETRICS - -CLOSURE_EVAL_FUNCTION_DECLARE_2(node_bsdf_glass, Glossy, Refraction) void node_bsdf_glass(vec4 color, float roughness, float ior, vec3 N, - const float do_multiscatter, - const float ssr_id, - out Closure result) + float weight, + float do_multiscatter, + out Closure result, + out float reflection_weight, + out float refraction_weight) { - CLOSURE_VARS_DECLARE_2(Glossy, Refraction); - - in_Glossy_0.N = N; /* Normalized during eval. */ - in_Glossy_0.roughness = roughness; - - in_Refraction_1.N = N; /* Normalized during eval. */ - in_Refraction_1.roughness = roughness; - in_Refraction_1.ior = ior; - - CLOSURE_EVAL_FUNCTION_2(node_bsdf_glass, Glossy, Refraction); - - result = CLOSURE_DEFAULT; - - float NV = dot(in_Refraction_1.N, cameraVec(worldPosition)); - - float fresnel = (do_multiscatter != 0.0) ? - btdf_lut(NV, in_Refraction_1.roughness, in_Refraction_1.ior).y : - F_eta(in_Refraction_1.ior, NV); - - vec2 split_sum = brdf_lut(NV, in_Glossy_0.roughness); - vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(vec3(1.0), vec3(1.0), split_sum) : - F_brdf_single_scatter(vec3(1.0), vec3(1.0), split_sum); - - out_Glossy_0.radiance = closure_mask_ssr_radiance(out_Glossy_0.radiance, ssr_id); - out_Glossy_0.radiance *= brdf; - out_Glossy_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Glossy_0.radiance); - out_Glossy_0.radiance *= color.rgb * fresnel; - closure_load_ssr_data( - out_Glossy_0.radiance, in_Glossy_0.roughness, in_Glossy_0.N, ssr_id, result); - - float btdf = (do_multiscatter != 0.0) ? - 1.0 : - btdf_lut(NV, in_Refraction_1.roughness, in_Refraction_1.ior).x; - out_Refraction_1.radiance *= btdf; - out_Refraction_1.radiance = render_pass_glossy_mask(vec3(1.0), out_Refraction_1.radiance); - out_Refraction_1.radiance *= color.rgb * (1.0 - fresnel); - /* Simulate 2nd absorption event. */ - out_Refraction_1.radiance *= (refractionDepth > 0.0) ? color.rgb : vec3(1.0); - result.radiance += out_Refraction_1.radiance; + N = safe_normalize(N); + vec3 V = cameraVec(g_data.P); + float NV = dot(N, V); + + float fresnel = (do_multiscatter != 0.0) ? btdf_lut(NV, roughness, ior).y : F_eta(ior, NV); + reflection_weight = fresnel; + refraction_weight = 1.0 - fresnel; + closure_weight_add(g_reflection_data, reflection_weight); + closure_weight_add(g_refraction_data, refraction_weight); } -#else -/* Stub glass because it is not compatible with volumetrics. */ -# define node_bsdf_glass(a, b, c, d, e, f, result) (result = CLOSURE_DEFAULT) -#endif +void node_bsdf_glass_eval(vec4 color, + float roughness, + float ior, + vec3 N, + float weight, + float do_multiscatter, + float reflection_weight, + float refraction_weight, + out Closure result) +{ + N = safe_normalize(N); + if (closure_weight_threshold(g_reflection_data, reflection_weight)) { + g_reflection_data.color = color.rgb * reflection_weight; + g_reflection_data.N = N; + g_reflection_data.roughness = roughness; + } + if (closure_weight_threshold(g_refraction_data, refraction_weight)) { + vec3 V = cameraVec(g_data.P); + float NV = dot(N, V); + float btdf = (do_multiscatter != 0.0) ? 1.0 : btdf_lut(NV, roughness, ior).x; + + g_refraction_data.color = color.rgb * (refraction_weight * btdf); + g_refraction_data.N = N; + g_refraction_data.roughness = roughness; + g_refraction_data.ior = ior; + } +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl index fa83bfb6c7a..b73c6f9437f 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl @@ -1,33 +1,15 @@ -#ifndef VOLUMETRICS -CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_glossy, Glossy) - -void node_bsdf_glossy( - vec4 color, float roughness, vec3 N, float use_multiscatter, float ssr_id, out Closure result) +void node_bsdf_glossy(vec4 color, float roughness, vec3 N, float weight, out Closure result) { - bool do_ssr = (ssrToggle && int(ssr_id) == outputSsrId); - - CLOSURE_VARS_DECLARE_1(Glossy); - - in_Glossy_0.N = N; /* Normalized during eval. */ - in_Glossy_0.roughness = roughness; - - CLOSURE_EVAL_FUNCTION_1(node_bsdf_glossy, Glossy); - - result = CLOSURE_DEFAULT; - - vec2 split_sum = brdf_lut(dot(in_Glossy_0.N, cameraVec(worldPosition)), in_Glossy_0.roughness); - vec3 brdf = (use_multiscatter != 0.0) ? F_brdf_multi_scatter(vec3(1.0), vec3(1.0), split_sum) : - F_brdf_single_scatter(vec3(1.0), vec3(1.0), split_sum); - out_Glossy_0.radiance = closure_mask_ssr_radiance(out_Glossy_0.radiance, ssr_id); - out_Glossy_0.radiance *= brdf; - out_Glossy_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Glossy_0.radiance); - out_Glossy_0.radiance *= color.rgb; - closure_load_ssr_data( - out_Glossy_0.radiance, in_Glossy_0.roughness, in_Glossy_0.N, ssr_id, result); + closure_weight_add(g_reflection_data, weight); } -#else -/* Stub glossy because it is not compatible with volumetrics. */ -# define node_bsdf_glossy(a, b, c, d, e, result) (result = CLOSURE_DEFAULT) -#endif +void node_bsdf_glossy_eval( + vec4 color, float roughness, vec3 N, float weight, float use_multiscatter, out Closure result) +{ + if (closure_weight_threshold(g_reflection_data, weight)) { + g_reflection_data.color = color.rgb * weight; + g_reflection_data.N = N; + g_reflection_data.roughness = roughness; + } +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl index 59f0377869b..e74389f93e4 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl @@ -2,24 +2,16 @@ void node_hair_info(float hair_length, out float is_strand, out float intercept, - out float length, + out float out_length, out float thickness, out vec3 tangent, out float random) { - length = hair_length; -#ifdef HAIR_SHADER - is_strand = 1.0; - intercept = hairTime; - thickness = hairThickness; - tangent = normalize(worldNormal); - random = wang_hash_noise( - uint(hairStrandID)); /* TODO: could be precomputed per strand instead. */ -#else - is_strand = 0.0; - intercept = 0.0; - thickness = 0.0; - tangent = vec3(1.0); - random = 0.0; -#endif + is_strand = float(g_data.is_strand); + intercept = g_data.hair_time; + thickness = g_data.hair_thickness; + out_length = hair_length; + tangent = normalize(interp.N); + /* TODO: could be precomputed per strand instead. */ + random = wang_hash_noise(uint(g_data.hair_strand_id)); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl index 50ce2bf2ab8..075fd3ec835 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl @@ -1,8 +1,4 @@ -void node_holdout(out Closure result) +void node_holdout(float weight, out Closure result) { - result = CLOSURE_DEFAULT; -#ifndef VOLUMETRICS - result.holdout = 1.0; - result.flag = CLOSURE_HOLDOUT_FLAG; -#endif + g_transparency_data.holdout += weight; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl index 588d295bcc4..fe09b8bcd4c 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl @@ -1,15 +1,15 @@ -void node_layer_weight(float blend, vec3 N, vec3 I, out float fresnel, out float facing) +void node_layer_weight(float blend, vec3 N, out float fresnel, out float facing) { N = normalize(N); /* fresnel */ float eta = max(1.0 - blend, 0.00001); - vec3 I_view = (ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0); + vec3 V = cameraVec(g_data.P); - fresnel = fresnel_dielectric(I_view, N, (gl_FrontFacing) ? 1.0 / eta : eta); + fresnel = fresnel_dielectric(V, N, (FrontFacing) ? 1.0 / eta : eta); /* facing */ - facing = abs(dot(I_view, N)); + facing = abs(dot(V, N)); if (blend != 0.5) { blend = clamp(blend, 0.0, 0.99999); blend = (blend < 0.5) ? 2.0 * blend : 0.5 / (1.0 - blend); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl index 50c87e3f105..628a3d5e0e5 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl @@ -13,19 +13,19 @@ void node_light_path(out float is_camera_ray, out float transmission_depth) { /* Supported. */ - is_camera_ray = (rayType == EEVEE_RAY_CAMERA) ? 1.0 : 0.0; - is_shadow_ray = (rayType == EEVEE_RAY_SHADOW) ? 1.0 : 0.0; - is_diffuse_ray = (rayType == EEVEE_RAY_DIFFUSE) ? 1.0 : 0.0; - is_glossy_ray = (rayType == EEVEE_RAY_GLOSSY) ? 1.0 : 0.0; + is_camera_ray = float(g_data.ray_type == RAY_TYPE_CAMERA); + is_shadow_ray = float(g_data.ray_type == RAY_TYPE_SHADOW); + is_diffuse_ray = float(g_data.ray_type == RAY_TYPE_DIFFUSE); + is_glossy_ray = float(g_data.ray_type == RAY_TYPE_GLOSSY); /* Kind of supported. */ is_singular_ray = is_glossy_ray; is_reflection_ray = is_glossy_ray; is_transmission_ray = is_glossy_ray; - ray_depth = rayDepth; - diffuse_depth = (is_diffuse_ray == 1.0) ? rayDepth : 0.0; - glossy_depth = (is_glossy_ray == 1.0) ? rayDepth : 0.0; + ray_depth = g_data.ray_depth; + diffuse_depth = (is_diffuse_ray == 1.0) ? g_data.ray_depth : 0.0; + glossy_depth = (is_glossy_ray == 1.0) ? g_data.ray_depth : 0.0; transmission_depth = (is_transmission_ray == 1.0) ? glossy_depth : 0.0; + ray_length = g_data.ray_length; /* Not supported. */ - ray_length = 1.0; transparent_depth = 0.0; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl index c6203bc36ab..523c44fa36a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl @@ -135,17 +135,74 @@ mat3 euler_to_mat3(vec3 euler) return mat; } -void direction_transform_m4v3(vec3 vin, mat4 mat, out vec3 vout) +void normal_transform_object_to_world(vec3 vin, out vec3 vout) { - vout = (mat * vec4(vin, 0.0)).xyz; + vout = normal_object_to_world(vin); } -void normal_transform_transposed_m4v3(vec3 vin, mat4 mat, out vec3 vout) +void normal_transform_world_to_object(vec3 vin, out vec3 vout) { - vout = transpose(mat3(mat)) * vin; + vout = normal_world_to_object(vin); } -void point_transform_m4v3(vec3 vin, mat4 mat, out vec3 vout) +void direction_transform_object_to_world(vec3 vin, out vec3 vout) { - vout = (mat * vec4(vin, 1.0)).xyz; + vout = transform_direction(ModelMatrix, vin); +} + +void direction_transform_object_to_view(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ModelMatrix, vin); + vout = transform_direction(ViewMatrix, vout); +} + +void direction_transform_view_to_world(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ViewMatrixInverse, vin); +} + +void direction_transform_view_to_object(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ViewMatrixInverse, vin); + vout = transform_direction(ModelMatrixInverse, vout); +} + +void direction_transform_world_to_view(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ViewMatrix, vin); +} + +void direction_transform_world_to_object(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ModelMatrixInverse, vin); +} + +void point_transform_object_to_world(vec3 vin, out vec3 vout) +{ + vout = point_object_to_view(vin); +} + +void point_transform_object_to_view(vec3 vin, out vec3 vout) +{ + vout = point_object_to_world(vin); +} + +void point_transform_view_to_world(vec3 vin, out vec3 vout) +{ + vout = point_view_to_object(vin); +} + +void point_transform_view_to_object(vec3 vin, out vec3 vout) +{ + vout = point_view_to_world(vin); +} + +void point_transform_world_to_view(vec3 vin, out vec3 vout) +{ + vout = point_world_to_object(vin); +} + +void point_transform_world_to_object(vec3 vin, out vec3 vout) +{ + vout = point_world_to_view(vin); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl index 2b4a0204d97..e219e2b9bbe 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl @@ -1,13 +1,13 @@ -void node_normal_map(vec4 info, vec4 tangent, vec3 normal, vec3 texnormal, out vec3 outnormal) +void node_normal_map(vec4 tangent, vec3 texnormal, out vec3 outnormal) { if (all(equal(tangent, vec4(0.0, 0.0, 0.0, 1.0)))) { - outnormal = normal; + outnormal = g_data.N; return; } - tangent *= (gl_FrontFacing ? 1.0 : -1.0); - vec3 B = tangent.w * cross(normal, tangent.xyz) * sign(info.w); + tangent *= (FrontFacing ? 1.0 : -1.0); + vec3 B = tangent.w * cross(g_data.N, tangent.xyz) * sign(ObjectInfo.w); - outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * normal; + outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * g_data.N; outnormal = normalize(outnormal); } @@ -21,7 +21,7 @@ void color_to_blender_normal_new_shading(vec3 color, out vec3 normal) normal = vec3(2.0, -2.0, -2.0) * color - vec3(1.0); } -void node_normal_map_mix(float strength, vec3 newnormal, vec3 oldnormal, out vec3 outnormal) +void node_normal_map_mix(float strength, vec3 newnormal, out vec3 outnormal) { - outnormal = normalize(mix(oldnormal, newnormal, max(strength, 0.0))); + outnormal = normalize(mix(g_data.N, newnormal, max(strength, 0.0))); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl index ff77b0beea2..c932fa39dd8 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl @@ -1,16 +1,13 @@ -void node_object_info(mat4 obmat, - vec4 obcolor, - vec4 info, - float mat_index, +void node_object_info(float mat_index, out vec3 location, out vec4 color, out float object_index, out float material_index, out float random) { - location = obmat[3].xyz; - color = obcolor; - object_index = info.x; + location = ModelMatrix[3].xyz; + color = ObjectColor; + object_index = ObjectInfo.x; material_index = mat_index; - random = info.z; + random = ObjectInfo.z; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl index 14271f9d107..2c24f50264c 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl @@ -1,20 +1,20 @@ -void node_output_material(Closure surface, - Closure volume, - vec3 displacement, - float alpha_threshold, - float shadow_threshold, - out Closure result) + +void node_output_material_surface(Closure surface, out Closure out_surface) { -#ifdef VOLUMETRICS - result = volume; -#else - result = surface; -# if defined(USE_ALPHA_HASH) - /* Alpha clip emulation. */ - if ((rayType != EEVEE_RAY_SHADOW) ? (alpha_threshold >= 0.0) : (shadow_threshold >= 0.0)) { - float alpha = saturate(1.0 - avg(result.transmittance)); - result.transmittance = vec3(step(alpha, max(alpha_threshold, shadow_threshold))); - } -# endif -#endif + out_surface = surface; +} + +void node_output_material_volume(Closure volume, out Closure out_volume) +{ + out_volume = volume; +} + +void node_output_material_displacement(vec3 displacement, out vec3 out_displacement) +{ + out_displacement = displacement; +} + +void node_output_material_thickness(float thickness, out float out_thickness) +{ + out_thickness = thickness; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl index 5eb853a4c1a..37c34a4f0d7 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl @@ -1,14 +1,10 @@ -uniform float backgroundAlpha; -void node_output_world(Closure surface, Closure volume, out Closure result) +void node_output_world_surface(Closure surface, out Closure out_surface) { -#ifndef VOLUMETRICS - float alpha = renderPassEnvironment ? 1.0 : backgroundAlpha; - result = CLOSURE_DEFAULT; - result.radiance = surface.radiance * alpha; - result.transmittance = vec3(0.0); - result.holdout = (1.0 - alpha); -#else - result = volume; -#endif /* VOLUMETRICS */ + out_surface = surface; +} + +void node_output_world_volume(Closure volume, out Closure out_volume) +{ + out_volume = volume; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl index bdd60c20a81..5602345ea4a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl @@ -1,8 +1,4 @@ -void particle_info(vec4 sprops, - vec4 loc, - vec3 vel, - vec3 avel, - out float index, +void particle_info(out float index, out float random, out float age, out float life_time, @@ -11,13 +7,14 @@ void particle_info(vec4 sprops, out vec3 velocity, out vec3 angular_velocity) { - index = sprops.x; - random = loc.w; - age = sprops.y; - life_time = sprops.z; - size = sprops.w; + /* Unsupported for now. */ + index = 0.0; + random = 0.0; + age = 0.0; + life_time = 0.0; + size = 0.0; - location = loc.xyz; - velocity = vel; - angular_velocity = avel; + location = vec3(0.0); + velocity = vec3(0.0); + angular_velocity = vec3(0.0); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl index bba84c2be52..355046e1dba 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl @@ -1,4 +1,4 @@ -#ifndef VOLUMETRICS + vec3 tint_from_color(vec3 color) { float lum = dot(color, vec3(0.3, 0.6, 0.1)); /* luminance approx. */ @@ -13,8 +13,6 @@ float principled_sheen(float NV) return sheen; } -CLOSURE_EVAL_FUNCTION_DECLARE_4(node_bsdf_principled, Diffuse, Glossy, Glossy, Refraction) - void node_bsdf_principled(vec4 base_color, float subsurface, vec3 subsurface_radius, @@ -40,169 +38,152 @@ void node_bsdf_principled(vec4 base_color, vec3 N, vec3 CN, vec3 T, - const float do_diffuse, - const float do_clearcoat, - const float do_refraction, + float weight, const float do_multiscatter, - float ssr_id, - float sss_id, - vec3 sss_scale, - out Closure result) + out Closure result, + out float diffuse_weight, + out float specular_weight, + out float glass_reflection_weight, + out float glass_transmission_weight, + out float clearcoat_weight) { /* Match cycles. */ metallic = saturate(metallic); transmission = saturate(transmission); - float diffuse_weight = (1.0 - transmission) * (1.0 - metallic); + diffuse_weight = (1.0 - transmission) * (1.0 - metallic); transmission *= (1.0 - metallic); - float specular_weight = (1.0 - transmission); - clearcoat = max(clearcoat, 0.0); + specular_weight = (1.0 - transmission); + clearcoat_weight = max(clearcoat, 0.0) * 0.25; transmission_roughness = 1.0 - (1.0 - roughness) * (1.0 - transmission_roughness); specular = max(0.0, specular); - CLOSURE_VARS_DECLARE_4(Diffuse, Glossy, Glossy, Refraction); - - in_Diffuse_0.N = N; /* Normalized during eval. */ - in_Diffuse_0.albedo = mix(base_color.rgb, subsurface_color.rgb, subsurface); - - in_Glossy_1.N = N; /* Normalized during eval. */ - in_Glossy_1.roughness = roughness; - - in_Glossy_2.N = CN; /* Normalized during eval. */ - in_Glossy_2.roughness = clearcoat_roughness; - - in_Refraction_3.N = N; /* Normalized during eval. */ - in_Refraction_3.roughness = do_multiscatter != 0.0 ? roughness : transmission_roughness; - in_Refraction_3.ior = ior; - - CLOSURE_EVAL_FUNCTION_4(node_bsdf_principled, Diffuse, Glossy, Glossy, Refraction); - - result = CLOSURE_DEFAULT; - - /* This will tag the whole eval for optimisation. */ - if (do_diffuse == 0.0) { - out_Diffuse_0.radiance = vec3(0); - } - if (do_clearcoat == 0.0) { - out_Glossy_2.radiance = vec3(0); - } - if (do_refraction == 0.0) { - out_Refraction_3.radiance = vec3(0); - } - - vec3 V = cameraVec(worldPosition); - - /* Glossy_1 will always be evaluated. */ - float NV = dot(in_Glossy_1.N, V); + N = safe_normalize(N); + vec3 V = cameraVec(g_data.P); + float NV = dot(N, V); + + /* Evaluate non sampled closures. */ + g_emission_data.emission += emission.rgb * emission_strength * weight; + g_transparency_data.transmittance += vec3(1.0 - alpha) * weight; + /* Apply alpha and weight on sampled closures. */ + alpha *= weight; + diffuse_weight *= alpha; + specular_weight *= alpha; + transmission *= alpha; + clearcoat_weight *= alpha; + + float fresnel = (do_multiscatter != 0.0) ? btdf_lut(NV, roughness, ior).y : F_eta(ior, NV); + glass_reflection_weight = fresnel * transmission; + glass_transmission_weight = (1.0 - fresnel) * transmission; + + closure_weight_add(g_diffuse_data, diffuse_weight); + closure_weight_add(g_reflection_data, glass_reflection_weight); + closure_weight_add(g_reflection_data, specular_weight); + closure_weight_add(g_reflection_data, clearcoat_weight); + closure_weight_add(g_refraction_data, glass_transmission_weight); +} +void node_bsdf_principled_eval(vec4 base_color, + float subsurface, + vec3 subsurface_radius, + vec4 subsurface_color, + float subsurface_ior, + float subsurface_anisotropy, + float metallic, + float specular, + float specular_tint, + float roughness, + float anisotropic, + float anisotropic_rotation, + float sheen, + float sheen_tint, + float clearcoat, + float clearcoat_roughness, + float ior, + float transmission, + float transmission_roughness, + vec4 emission, + float emission_strength, + float alpha, + vec3 N, + vec3 CN, + vec3 T, + float weight, + const float do_multiscatter, + float diffuse_weight, + float specular_weight, + float glass_reflection_weight, + float glass_transmission_weight, + float clearcoat_weight, + out Closure result) +{ vec3 base_color_tint = tint_from_color(base_color.rgb); - float fresnel = (do_multiscatter != 0.0) ? - btdf_lut(NV, in_Glossy_1.roughness, in_Refraction_3.ior).y : - F_eta(in_Refraction_3.ior, NV); + N = safe_normalize(N); + vec3 V = cameraVec(g_data.P); + float NV = dot(N, V); - { - /* Glossy reflections. - * Separate Glass reflections and main specular reflections to match Cycles renderpasses. */ - out_Glossy_1.radiance = closure_mask_ssr_radiance(out_Glossy_1.radiance, ssr_id); - - vec2 split_sum = brdf_lut(NV, roughness); + vec2 split_sum = brdf_lut(NV, roughness); - vec3 glossy_radiance_final = vec3(0.0); - if (transmission > 1e-5) { - /* Glass Reflection: Reuse radiance from Glossy1. */ - vec3 out_glass_refl_radiance = out_Glossy_1.radiance; - - /* Poor approximation since we baked the LUT using a fixed IOR. */ - vec3 f0 = mix(vec3(1.0), base_color.rgb, specular_tint); - vec3 f90 = vec3(1); - - vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) : - F_brdf_single_scatter(f0, f90, split_sum); - - out_glass_refl_radiance *= brdf; - out_glass_refl_radiance = render_pass_glossy_mask(vec3(1), out_glass_refl_radiance); - out_glass_refl_radiance *= fresnel * transmission; - glossy_radiance_final += out_glass_refl_radiance; - } - if (specular_weight > 1e-5) { - vec3 dielectric_f0_color = mix(vec3(1.0), base_color_tint, specular_tint); - vec3 metallic_f0_color = base_color.rgb; - vec3 f0 = mix((0.08 * specular) * dielectric_f0_color, metallic_f0_color, metallic); - /* Cycles does this blending using the microfacet fresnel factor. However, our fresnel - * is already baked inside the split sum LUT. We approximate using by modifying the - * changing the f90 color directly in a non linear fashion. */ - vec3 f90 = mix(f0, vec3(1), fast_sqrt(specular)); - - vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) : - F_brdf_single_scatter(f0, f90, split_sum); - - out_Glossy_1.radiance *= brdf; - out_Glossy_1.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_1.radiance); - out_Glossy_1.radiance *= specular_weight; - glossy_radiance_final += out_Glossy_1.radiance; - } - - closure_load_ssr_data( - glossy_radiance_final, in_Glossy_1.roughness, in_Glossy_1.N, ssr_id, result); + /* Diffuse. */ + if (closure_weight_threshold(g_diffuse_data, diffuse_weight)) { + g_diffuse_data.color = mix(base_color.rgb, subsurface_color.rgb, subsurface); + /* Sheen Coarse approximation: We reuse the diffuse radiance and just scale it. */ + vec3 sheen_color = mix(vec3(1.0), base_color_tint, sheen_tint); + g_diffuse_data.color += sheen * sheen_color * principled_sheen(NV); + g_diffuse_data.color *= diffuse_weight; + g_diffuse_data.N = N; + g_diffuse_data.sss_radius = subsurface_radius * subsurface; + g_diffuse_data.sss_id = uint(resource_handle + 1); } - if (diffuse_weight > 1e-5) { - /* Mask over all diffuse radiance. */ - out_Diffuse_0.radiance *= diffuse_weight; + /* Reflection. */ + if (closure_weight_threshold(g_reflection_data, glass_reflection_weight)) { + /* Poor approximation since we baked the LUT using a fixed IOR. */ + vec3 f0 = mix(vec3(1.0), base_color.rgb, specular_tint); + vec3 f90 = vec3(1); - /* Sheen Coarse approximation: We reuse the diffuse radiance and just scale it. */ - vec3 sheen_color = mix(vec3(1), base_color_tint, sheen_tint); - vec3 out_sheen_radiance = out_Diffuse_0.radiance * principled_sheen(NV); - out_sheen_radiance = render_pass_diffuse_mask(vec3(1), out_sheen_radiance); - out_sheen_radiance *= sheen * sheen_color; - result.radiance += out_sheen_radiance; - - /* Diffuse / Subsurface. */ - float scale = avg(sss_scale) * subsurface; - closure_load_sss_data(scale, out_Diffuse_0.radiance, in_Diffuse_0.albedo, int(sss_id), result); - } + vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) : + F_brdf_single_scatter(f0, f90, split_sum); - if (transmission > 1e-5) { - float btdf = (do_multiscatter != 0.0) ? - 1.0 : - btdf_lut(NV, in_Refraction_3.roughness, in_Refraction_3.ior).x; - /* TODO(fclem) This could be going to a transmission render pass instead. */ - out_Refraction_3.radiance *= btdf; - out_Refraction_3.radiance = render_pass_glossy_mask(vec3(1), out_Refraction_3.radiance); - out_Refraction_3.radiance *= base_color.rgb; - /* Simulate 2nd transmission event. */ - out_Refraction_3.radiance *= (refractionDepth > 0.0) ? base_color.rgb : vec3(1); - out_Refraction_3.radiance *= (1.0 - fresnel) * transmission; - result.radiance += out_Refraction_3.radiance; + g_reflection_data.color = brdf * glass_reflection_weight; + g_reflection_data.N = N; + g_reflection_data.roughness = roughness; } - - if (clearcoat > 1e-5) { - float NV = dot(in_Glossy_2.N, V); - vec2 split_sum = brdf_lut(NV, in_Glossy_2.roughness); + else if (closure_weight_threshold(g_reflection_data, specular_weight)) { + vec3 dielectric_f0_color = mix(vec3(1.0), base_color_tint, specular_tint); + vec3 metallic_f0_color = base_color.rgb; + vec3 f0 = mix((0.08 * specular) * dielectric_f0_color, metallic_f0_color, metallic); + /* Cycles does this blending using the microfacet fresnel factor. However, our fresnel + * is already baked inside the split sum LUT. We approximate using by modifying the + * changing the f90 color directly in a non linear fashion. */ + vec3 f90 = mix(f0, vec3(1.0), fast_sqrt(specular)); + + vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) : + F_brdf_single_scatter(f0, f90, split_sum); + + g_reflection_data.color = brdf * specular_weight; + g_reflection_data.N = N; + g_reflection_data.roughness = roughness; + } + else if (closure_weight_threshold(g_reflection_data, clearcoat_weight)) { + N = safe_normalize(CN); + float NV = dot(CN, V); + vec2 split_sum = brdf_lut(NV, roughness); vec3 brdf = F_brdf_single_scatter(vec3(0.04), vec3(1.0), split_sum); - out_Glossy_2.radiance *= brdf * clearcoat * 0.25; - out_Glossy_2.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_2.radiance); - result.radiance += out_Glossy_2.radiance; + g_reflection_data.color = brdf * clearcoat_weight; + g_reflection_data.N = CN; + g_reflection_data.roughness = clearcoat_roughness; } - { - vec3 out_emission_radiance = render_pass_emission_mask(emission.rgb); - out_emission_radiance *= emission_strength; - result.radiance += out_emission_radiance; - } + /* Refraction. */ + if (closure_weight_threshold(g_refraction_data, glass_transmission_weight)) { + float btdf = (do_multiscatter != 0.0) ? 1.0 : btdf_lut(NV, roughness, ior).x; - result.transmittance = vec3(1.0 - alpha); - result.radiance *= alpha; - result.ssr_data.rgb *= alpha; -# ifdef USE_SSS - result.sss_albedo *= alpha; -# endif + g_refraction_data.color = base_color.rgb * (btdf * glass_transmission_weight); + g_refraction_data.N = N; + g_refraction_data.roughness = do_multiscatter != 0.0 ? roughness : + max(roughness, transmission_roughness); + g_refraction_data.ior = ior; + } } - -#else -/* clang-format off */ -/* Stub principled because it is not compatible with volumetrics. */ -# define node_bsdf_principled(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, cc, dd, ee, ff, result) (result = CLOSURE_DEFAULT) -/* clang-format on */ -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl index 7cbc7218f5c..f8a7b7a77b1 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl @@ -1,32 +1,17 @@ -#ifndef VOLUMETRICS -CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_refraction, Refraction) - -void node_bsdf_refraction(vec4 color, float roughness, float ior, vec3 N, out Closure result) +void node_bsdf_refraction( + vec4 color, float roughness, float ior, vec3 N, float weight, out Closure result) { - CLOSURE_VARS_DECLARE_1(Refraction); - - in_Refraction_0.N = N; /* Normalized during eval. */ - in_Refraction_0.roughness = roughness; - in_Refraction_0.ior = ior; - - CLOSURE_EVAL_FUNCTION_1(node_bsdf_refraction, Refraction); - - result = CLOSURE_DEFAULT; - - out_Refraction_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Refraction_0.radiance); - out_Refraction_0.radiance *= color.rgb; - /* Simulate 2nd absorption event. */ - out_Refraction_0.radiance *= (refractionDepth > 0.0) ? color.rgb : vec3(1.0); - - result.radiance = out_Refraction_0.radiance; - - /* TODO(fclem) Try to not use this. */ - result.ssr_normal = normal_encode(mat3(ViewMatrix) * in_Refraction_0.N, - viewCameraVec(viewPosition)); + closure_weight_add(g_refraction_data, weight); } -#else -/* Stub refraction because it is not compatible with volumetrics. */ -# define node_bsdf_refraction(a, b, c, d, e) (e = CLOSURE_DEFAULT) -#endif +void node_bsdf_refraction_eval( + vec4 color, float roughness, float ior, vec3 N, float weight, out Closure result) +{ + if (closure_weight_threshold(g_refraction_data, weight)) { + g_refraction_data.color = color.rgb * weight; + g_refraction_data.N = N; + g_refraction_data.roughness = roughness; + g_refraction_data.ior = ior; + } +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl index f0f2f79c60e..d446860e2c7 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl @@ -1,29 +1,4 @@ -#ifndef VOLUMETRICS - -CLOSURE_EVAL_FUNCTION_DECLARE_1(node_shader_to_rgba, Glossy) void node_shader_to_rgba(Closure cl, out vec4 outcol, out float outalpha) { - vec4 spec_accum = vec4(0.0); - if (ssrToggle && FLAG_TEST(cl.flag, CLOSURE_SSR_FLAG)) { - CLOSURE_VARS_DECLARE_1(Glossy); - - vec3 vN = normal_decode(cl.ssr_normal, viewCameraVec(viewPosition)); - vec3 N = transform_direction(ViewMatrixInverse, vN); - - in_Glossy_0.N = N; /* Normalized during eval. */ - in_Glossy_0.roughness = cl.ssr_data.a; - - CLOSURE_EVAL_FUNCTION_1(node_shader_to_rgba, Glossy); - - spec_accum.rgb = out_Glossy_0.radiance; - } - - outalpha = saturate(1.0 - avg(cl.transmittance)); - outcol = vec4((spec_accum.rgb * cl.ssr_data.rgb) + cl.radiance, 1.0); - -# ifdef USE_SSS - outcol.rgb += cl.sss_irradiance.rgb * cl.sss_albedo; -# endif } -#endif /* VOLUMETRICS */ diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl index d0c159cdf37..786775b5857 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl @@ -1,6 +1,3 @@ -#ifndef VOLUMETRICS - -CLOSURE_EVAL_FUNCTION_DECLARE_1(node_subsurface_scattering, Diffuse) void node_subsurface_scattering(vec4 color, float scale, @@ -8,25 +5,25 @@ void node_subsurface_scattering(vec4 color, float ior, float anisotropy, vec3 N, - float sss_id, + float weight, out Closure result) { - CLOSURE_VARS_DECLARE_1(Diffuse); - - in_Diffuse_0.N = N; /* Normalized during eval. */ - in_Diffuse_0.albedo = color.rgb; - - CLOSURE_EVAL_FUNCTION_1(node_subsurface_scattering, Diffuse); - - result = CLOSURE_DEFAULT; - - closure_load_sss_data(scale, out_Diffuse_0.radiance, color.rgb, int(sss_id), result); - - /* TODO(fclem) Try to not use this. */ - closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result); + closure_weight_add(g_diffuse_data, weight); } -#else -/* Stub subsurface scattering because it is not compatible with volumetrics. */ -# define node_subsurface_scattering(a, b, c, d, e, f, g, h) (h = CLOSURE_DEFAULT) -#endif +void node_subsurface_scattering_eval(vec4 color, + float scale, + vec3 radius, + float ior, + float anisotropy, + vec3 N, + float weight, + out Closure result) +{ + if (closure_weight_threshold(g_diffuse_data, weight)) { + g_diffuse_data.color = color.rgb * weight; + g_diffuse_data.N = N; + g_diffuse_data.sss_radius = radius * scale; + g_diffuse_data.sss_id = uint(resource_handle + 1); + } +}
\ No newline at end of file diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl index ff2dbc7ead3..4e4bf759ec9 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl @@ -18,8 +18,8 @@ void node_tangentmap(vec4 attr_tangent, out vec3 tangent) tangent = normalize(attr_tangent.xyz); } -void node_tangent(vec3 N, vec3 orco, mat4 objmat, out vec3 T) +void node_tangent(vec3 orco, out vec3 T) { - T = (objmat * vec4(orco, 0.0)).xyz; - T = cross(N, normalize(cross(T, N))); + T = transform_direction(ModelMatrix, orco); + T = cross(g_data.N, normalize(cross(T, g_data.N))); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl index 20a65f23c05..b6e9b3cab91 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl @@ -1,19 +1,3 @@ -void node_tex_environment_texco(vec3 viewvec, out vec3 worldvec) -{ -#ifdef MESH_SHADER - worldvec = worldPosition; -#else - vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(viewvec, 1.0) : vec4(0.0, 0.0, 1.0, 1.0); - vec4 co_homogenous = (ProjectionMatrixInverse * v); - - vec3 co = co_homogenous.xyz / co_homogenous.w; -# if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE) - worldvec = mat3(ViewMatrixInverse) * co; -# else - worldvec = mat3(ModelMatrixInverse) * (mat3(ViewMatrixInverse) * co); -# endif -#endif -} void node_tex_environment_equirectangular(vec3 co, out vec3 uv) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl index 08d566224bf..a9954fdb14a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl @@ -1,7 +1,3 @@ -vec3 mtex_2d_mapping(vec3 vec) -{ - return vec3(vec.xy * 0.5 + vec2(0.5), vec.z); -} void generated_from_orco(vec3 orco, out vec3 generated) { @@ -9,30 +5,19 @@ void generated_from_orco(vec3 orco, out vec3 generated) # ifdef MESH_SHADER generated = volumeObjectLocalCoord; # else - generated = worldPosition; + generated = g_data.P; # endif #else generated = orco; #endif } -void generated_texco(vec3 I, vec3 attr_orco, out vec3 generated) +void generated_texco(vec3 attr_orco, out vec3 generated) { - vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(I, 1.0) : vec4(0.0, 0.0, 1.0, 1.0); - vec4 co_homogenous = (ProjectionMatrixInverse * v); - vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0); - co.xyz = normalize(co.xyz); -#if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE) - generated = (ViewMatrixInverse * co).xyz; -#else generated_from_orco(attr_orco, generated); -#endif } -void node_tex_coord(vec3 I, - vec3 wN, - mat4 obmatinv, - vec4 camerafac, +void node_tex_coord(mat4 obmatinv, vec3 attr_orco, vec3 attr_uv, out vec3 generated, @@ -43,50 +28,20 @@ void node_tex_coord(vec3 I, out vec3 window, out vec3 reflection) { + vec3 N = safe_normalize(g_data.N); + generated = attr_orco; - normal = normalize(normal_world_to_object(wN)); + normal = normal_world_to_object(N); uv = attr_uv; - object = (obmatinv * (ViewMatrixInverse * vec4(I, 1.0))).xyz; - camera = vec3(I.xy, -I.z); - vec4 projvec = ProjectionMatrix * vec4(I, 1.0); - window = vec3(mtex_2d_mapping(projvec.xyz / projvec.w).xy * camerafac.xy + camerafac.zw, 0.0); - reflection = -reflect(cameraVec(worldPosition), normalize(wN)); -} -void node_tex_coord_background(vec3 I, - vec3 N, - mat4 obmatinv, - vec4 camerafac, - vec3 attr_orco, - vec3 attr_uv, - out vec3 generated, - out vec3 normal, - out vec3 uv, - out vec3 object, - out vec3 camera, - out vec3 window, - out vec3 reflection) -{ - vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(I, 1.0) : vec4(0.0, 0.0, 1.0, 1.0); - vec4 co_homogenous = (ProjectionMatrixInverse * v); + object = transform_point((obmatinv[3][3] == 0.0) ? ModelMatrixInverse : obmatinv, g_data.P); - vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0); + camera = transform_point(ViewMatrix, g_data.P); + camera.z = -camera.z; + /* TODO fix in panoramic view. */ + window.xy = project_point(ViewProjectionMatrix, g_data.P).xy * 0.5 + 0.5; + window.xy = window.xy * CameraTexCoFactors.xy + CameraTexCoFactors.zw; + window.z = 0.0; - co = normalize(co); - - vec3 coords = (ViewMatrixInverse * co).xyz; - - generated = coords; - normal = -coords; - uv = vec3(attr_uv.xy, 0.0); - object = (obmatinv * vec4(coords, 1.0)).xyz; - - camera = vec3(co.xy, -co.z); - window = vec3(mtex_2d_mapping(I).xy * camerafac.xy + camerafac.zw, 0.0); - - reflection = -coords; + reflection = -reflect(cameraVec(g_data.P), N); } - -#if defined(WORLD_BACKGROUND) || (defined(PROBE_CAPTURE) && !defined(MESH_SHADER)) -# define node_tex_coord node_tex_coord_background -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl index bbfc99ccc73..e46e16ba8c0 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl @@ -1,9 +1,13 @@ -#ifndef VOLUMETRICS -void node_bsdf_toon(vec4 color, float size, float tsmooth, vec3 N, out Closure result) +void node_bsdf_toon( + vec4 color, float size, float tsmooth, vec3 N, float weight, out Closure result) { - node_bsdf_diffuse(color, 0.0, N, result); + closure_weight_add(g_diffuse_data, weight); +} + +void node_bsdf_toon_eval(vec4 color, float roughness, vec3 N, float weight, out Closure result) +{ + if (closure_weight_threshold(g_diffuse_data, weight)) { + g_diffuse_data.color = color.rgb * weight; + g_diffuse_data.N = N; + } } -#else -/* Stub toon because it is not compatible with volumetrics. */ -# define node_bsdf_toon(a, b, c, d, e) (e = CLOSURE_DEFAULT) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl index 80bd3941b22..550afbb8b24 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl @@ -1,21 +1,13 @@ -#ifndef VOLUMETRICS -CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_translucent, Translucent) - -void node_bsdf_translucent(vec4 color, vec3 N, out Closure result) +void node_bsdf_translucent(vec4 color, vec3 N, float weight, out Closure result) { - CLOSURE_VARS_DECLARE_1(Translucent); - - in_Translucent_0.N = -N; /* Normalized during eval. */ - - CLOSURE_EVAL_FUNCTION_1(node_bsdf_translucent, Translucent); - - result = CLOSURE_DEFAULT; - closure_load_ssr_data(vec3(0.0), 0.0, -in_Translucent_0.N, -1.0, result); - result.radiance = render_pass_diffuse_mask(color.rgb, out_Translucent_0.radiance * color.rgb); + closure_weight_add(g_diffuse_data, weight); } -#else -/* Stub translucent because it is not compatible with volumetrics. */ -# define node_bsdf_translucent(a, b, c) (c = CLOSURE_DEFAULT) -#endif +void node_bsdf_translucent_eval(vec4 color, vec3 N, float weight, out Closure result) +{ + if (closure_weight_threshold(g_diffuse_data, weight)) { + g_diffuse_data.color = color.rgb * weight; + g_diffuse_data.N = -N; + } +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl index 9040f62bd3f..cfbd6178d95 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl @@ -1,11 +1,4 @@ -#ifndef VOLUMETRICS -void node_bsdf_transparent(vec4 color, out Closure result) +void node_bsdf_transparent(vec4 color, float weight, out Closure result) { - result = CLOSURE_DEFAULT; - result.radiance = vec3(0.0); - result.transmittance = abs(color.rgb); + g_transparency_data.transmittance += color.rgb * weight; } -#else -/* Stub transparent because it is not compatible with volumetrics. */ -# define node_bsdf_transparent(a, b) (b = CLOSURE_DEFAULT) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl index 4b5ed172081..7f1344d03fe 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl @@ -1,4 +1,4 @@ -void node_vector_displacement_tangent(vec4 vector, +void node_vector_displacement_tangent(vec3 vector, float midlevel, float scale, vec4 tangent, @@ -7,24 +7,23 @@ void node_vector_displacement_tangent(vec4 vector, mat4 viewmat, out vec3 result) { - /* TODO(fclem): this is broken. revisit latter. */ - vec3 N_object = normalize(((vec4(normal, 0.0) * viewmat) * obmat).xyz); - vec3 T_object = normalize(((vec4(tangent.xyz, 0.0) * viewmat) * obmat).xyz); - vec3 B_object = tangent.w * normalize(cross(N_object, T_object)); + vec3 oN = normalize(normal_world_to_object(normal)); + vec3 oT = normalize(normal_world_to_object(tangent.xyz)); + vec3 oB = tangent.w * normalize(cross(oN, oT)); - vec3 offset = (vector.xyz - vec3(midlevel)) * scale; - result = offset.x * T_object + offset.y * N_object + offset.z * B_object; - result = (obmat * vec4(result, 0.0)).xyz; + result = (vector - midlevel) * scale; + result = result.x * oT + result.y * oN + result.z * oB; + result = transform_point(ModelMatrix, result); } void node_vector_displacement_object( - vec4 vector, float midlevel, float scale, mat4 obmat, out vec3 result) + vec3 vector, float midlevel, float scale, mat4 obmat, out vec3 result) { - result = (vector.xyz - vec3(midlevel)) * scale; - result = (obmat * vec4(result, 0.0)).xyz; + result = (vector - midlevel) * scale; + result = transform_point(ModelMatrix, result); } -void node_vector_displacement_world(vec4 vector, float midlevel, float scale, out vec3 result) +void node_vector_displacement_world(vec3 vector, float midlevel, float scale, out vec3 result) { - result = (vector.xyz - vec3(midlevel)) * scale; + result = (vector - midlevel) * scale; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl index 989f18b881a..642112418c6 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl @@ -1,9 +1,12 @@ -#ifndef VOLUMETRICS -void node_bsdf_velvet(vec4 color, float sigma, vec3 N, out Closure result) +void node_bsdf_velvet(vec4 color, float sigma, vec3 N, float weight, out Closure result) { - node_bsdf_diffuse(color, 0.0, N, result); + closure_weight_add(g_diffuse_data, weight); +} + +void node_bsdf_velvet_eval(vec4 color, float roughness, vec3 N, float weight, out Closure result) +{ + if (closure_weight_threshold(g_diffuse_data, weight)) { + g_diffuse_data.color = color.rgb * weight; + g_diffuse_data.N = N; + } } -#else -/* Stub velvet because it is not compatible with volumetrics. */ -# define node_bsdf_velvet(a, b, c, d) (d = CLOSURE_DEFAULT) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl index e6c0880cd07..d11c29c089a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl @@ -1,8 +1,3 @@ -void node_volume_absorption(vec4 color, float density, out Closure result) +void node_volume_absorption(vec4 color, float density, float weight, out Closure result) { -#ifdef VOLUMETRICS - result = Closure((1.0 - color.rgb) * density, vec3(0.0), vec3(0.0), 0.0); -#else - result = CLOSURE_DEFAULT; -#endif } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl index 884d5415c51..471440c88b5 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl @@ -7,6 +7,7 @@ void node_volume_principled(vec4 color, float blackbody_intensity, vec4 blackbody_tint, float temperature, + float weight, float density_attribute, vec4 color_attribute, float temperature_attribute, diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl index 02c54658be5..cbaf5cb3a08 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl @@ -1,8 +1,4 @@ -void node_volume_scatter(vec4 color, float density, float anisotropy, out Closure result) +void node_volume_scatter( + vec4 color, float density, float anisotropy, float weight, out Closure result) { -#ifdef VOLUMETRICS - result = Closure(vec3(0.0), color.rgb * density, vec3(0.0), anisotropy); -#else - result = CLOSURE_DEFAULT; -#endif } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl index e2789e046e1..8881b99ddc0 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl @@ -1,20 +1,21 @@ -#ifndef VOLUMETRICS -void node_wireframe(float size, vec2 barycentric, vec3 barycentric_dist, out float fac) + +void node_wireframe(float size, out float fac) { - vec3 barys = barycentric.xyy; - barys.z = 1.0 - barycentric.x - barycentric.y; + vec3 barys = g_data.barycentric_coords.xyy; + barys.z = 1.0 - barys.x - barys.y; size *= 0.5; - vec3 s = step(-size, -barys * barycentric_dist); + vec3 s = step(-size, -barys * g_data.barycentric_dists); fac = max(s.x, max(s.y, s.z)); } -void node_wireframe_screenspace(float size, vec2 barycentric, out float fac) +void node_wireframe_screenspace(float size, out float fac) { - vec3 barys = barycentric.xyy; - barys.z = 1.0 - barycentric.x - barycentric.y; + vec3 barys = g_data.barycentric_coords.xyy; + barys.z = 1.0 - barys.x - barys.y; +#ifdef GPU_FRAGMENT_SHADER size *= (1.0 / 3.0); vec3 dx = dFdx(barys); vec3 dy = dFdy(barys); @@ -23,9 +24,7 @@ void node_wireframe_screenspace(float size, vec2 barycentric, out float fac) vec3 s = step(-deltas * size, -barys); fac = max(s.x, max(s.y, s.z)); -} #else -/* Stub wireframe because it is not compatible with volumetrics. */ -# define node_wireframe(a, b, c, d) (d = 0.0) -# define node_wireframe_screenspace(a, b, c) (c = 0.0) + fac = 1.0; #endif +}
\ No newline at end of file diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl index 40e46bc250c..5a0aeb2f932 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl @@ -1,25 +1,5 @@ -/* TODO: clean this `ifdef` mess. */ + void world_normals_get(out vec3 N) { -#ifndef VOLUMETRICS -# ifdef HAIR_SHADER - vec3 B = normalize(cross(worldNormal, hairTangent)); - float cos_theta; - if (hairThicknessRes == 1) { - vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy); - /* Random cosine normal distribution on the hair surface. */ - cos_theta = rand.x * 2.0 - 1.0; - } - else { - /* Shade as a cylinder. */ - cos_theta = hairThickTime / hairThickness; - } - float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta)); - N = normalize(worldNormal * sin_theta + B * cos_theta); -# else - N = gl_FrontFacing ? worldNormal : -worldNormal; -# endif -#else - generated_from_orco(vec3(0.0), N); -#endif + N = g_data.N; } diff --git a/source/blender/makesdna/DNA_camera_defaults.h b/source/blender/makesdna/DNA_camera_defaults.h index b0237caa544..6ad12137a47 100644 --- a/source/blender/makesdna/DNA_camera_defaults.h +++ b/source/blender/makesdna/DNA_camera_defaults.h @@ -52,6 +52,13 @@ .drawsize = 1.0f, \ .ortho_scale = 6.0, \ .flag = CAM_SHOWPASSEPARTOUT, \ + .panorama_type = CAM_PANO_FISHEYE_EQUISOLID, \ + .fisheye_fov = DEG2RADF(180.0f), \ + .fisheye_lens = 10.5f, \ + .latitude_min = DEG2RADF(-90.0f), \ + .latitude_max = DEG2RADF(90.0f), \ + .longitude_min = DEG2RADF(-180.0f), \ + .longitude_max = DEG2RADF(180.0f), \ .passepartalpha = 0.5f, \ \ .dof = _DNA_DEFAULT_CameraDOFSettings, \ diff --git a/source/blender/makesdna/DNA_camera_types.h b/source/blender/makesdna/DNA_camera_types.h index 32ebcade76d..9759fd8ad82 100644 --- a/source/blender/makesdna/DNA_camera_types.h +++ b/source/blender/makesdna/DNA_camera_types.h @@ -116,7 +116,15 @@ typedef struct Camera { struct ListBase bg_images; char sensor_fit; - char _pad[7]; + char panorama_type; + char _pad[6]; + /** Panoramic properties. */ + float fisheye_fov; + float fisheye_lens; + float latitude_min; + float latitude_max; + float longitude_min; + float longitude_max; /* Stereo settings */ struct CameraStereoSettings stereo; @@ -134,6 +142,14 @@ enum { CAM_PANO = 2, }; +/* panorama_type */ +enum { + CAM_PANO_EQUIRECTANGULAR = 0, + CAM_PANO_FISHEYE_EQUIDISTANT = 1, + CAM_PANO_FISHEYE_EQUISOLID = 2, + CAM_PANO_MIRRORBALL = 3, +}; + /* dtx */ enum { CAM_DTX_CENTER = (1 << 0), diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 0f570f8603d..b0ccb185ef6 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -49,6 +49,9 @@ struct MDeformVert; #define GPENCIL_MIN_FILL_FAC 0.05f #define GPENCIL_MAX_FILL_FAC 8.0f +/* Used to convert pixel scale. */ +#define GPENCIL_PIXEL_FACTOR 2000.0f + #define GPENCIL_MAX_THICKNESS 5000 /* ***************************************** */ diff --git a/source/blender/makesdna/DNA_lightprobe_types.h b/source/blender/makesdna/DNA_lightprobe_types.h index 1a3bda34a84..d8ab21727c1 100644 --- a/source/blender/makesdna/DNA_lightprobe_types.h +++ b/source/blender/makesdna/DNA_lightprobe_types.h @@ -123,7 +123,9 @@ typedef struct LightProbeCache { float position[3], parallax_type; float attenuation_fac; float attenuation_type; - float _pad3[2]; + /* Used during baking. */ + int probe_index; + int is_ready; float attenuationmat[4][4]; float parallaxmat[4][4]; } LightProbeCache; @@ -137,8 +139,10 @@ typedef struct LightGridCache { /** World space vector between 2 opposite cells. */ float increment_x[3], attenuation_bias; float increment_y[3], level_bias; - float increment_z[3], _pad4; - float visibility_bias, visibility_bleed, visibility_range, _pad5; + float increment_z[3], is_ready; + float visibility_bias, visibility_bleed, visibility_range; + /* Used during baking. */ + int probe_index; } LightGridCache; /* These are used as UBO data. They need to be aligned to size of vec4. */ @@ -207,6 +211,8 @@ enum { LIGHTCACHE_INVALID = (1 << 8), /** The data present in the cache is valid but unusable on this GPU. */ LIGHTCACHE_NOT_USABLE = (1 << 9), + /** Used by baking cache to keep reflections black. */ + LIGHTCACHE_NO_REFLECTION = (1 << 10), }; /* EEVEE_LightCacheTexture->data_type */ diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index 5d682d963ab..b91d681df0c 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -213,7 +213,7 @@ typedef struct Material { /* Transparency. */ float alpha_threshold; - float refract_depth; + char _pad4[4]; char blend_method; char blend_shadow; char blend_flag; @@ -331,7 +331,7 @@ enum { MA_BL_HIDE_BACKFACE = (1 << 0), MA_BL_SS_REFRACTION = (1 << 1), MA_BL_CULL_BACKFACE = (1 << 2), - MA_BL_TRANSLUCENCY = (1 << 3), + // MA_BL_TRANSLUCENCY = (1 << 3), /* deprecated */ }; /* blend_shadow */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 099e41d7ff0..0b24dfea0b0 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -340,17 +340,6 @@ typedef struct bNode { char iter_flag; /** - * XXX: eevee only, id of screen space reflection layer, - * needs to be a float to feed GPU_uniform. - */ - float ssr_id; - /** - * XXX: eevee only, id of screen subsurface scatter layer, - * needs to be a float to feed GPU_uniform. - */ - float sss_id; - - /** * Describes the desired interface of the node. This is run-time data only. * The actual interface of the node may deviate from the declaration temporarily. * It's possible to sync the actual state of the node to the desired state. Currently, this is diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 7ba054e3133..502abfbdf1d 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -236,7 +236,8 @@ \ .flag = SCE_EEVEE_VOLUMETRIC_LIGHTS | SCE_EEVEE_GTAO_BENT_NORMALS | \ SCE_EEVEE_GTAO_BOUNCE | SCE_EEVEE_TAA_REPROJECTION | \ - SCE_EEVEE_SSR_HALF_RESOLUTION | SCE_EEVEE_SHADOW_SOFT, \ + SCE_EEVEE_SHADOW_SOFT | \ + SCE_EEVEE_FILM_LOG_ENCODING, \ } #define _DNA_DEFAULT_Scene \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 864358e040c..36fef503efc 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1640,7 +1640,7 @@ typedef struct SceneEEVEE { float ssr_quality; float ssr_max_roughness; float ssr_thickness; - float ssr_border_fade; + float ssr_border_fade DNA_DEPRECATED; float ssr_firefly_fac; float volumetric_start; @@ -2440,16 +2440,16 @@ enum { SCE_EEVEE_GTAO_ENABLED = (1 << 4), SCE_EEVEE_GTAO_BENT_NORMALS = (1 << 5), SCE_EEVEE_GTAO_BOUNCE = (1 << 6), - // SCE_EEVEE_DOF_ENABLED = (1 << 7), /* Moved to camera->dof.flag */ + SCE_EEVEE_FILM_LOG_ENCODING = (1 << 7), SCE_EEVEE_BLOOM_ENABLED = (1 << 8), SCE_EEVEE_MOTION_BLUR_ENABLED = (1 << 9), SCE_EEVEE_SHADOW_HIGH_BITDEPTH = (1 << 10), SCE_EEVEE_TAA_REPROJECTION = (1 << 11), // SCE_EEVEE_SSS_ENABLED = (1 << 12), /* Unused */ // SCE_EEVEE_SSS_SEPARATE_ALBEDO = (1 << 13), /* Unused */ - SCE_EEVEE_SSR_ENABLED = (1 << 14), - SCE_EEVEE_SSR_REFRACTION = (1 << 15), - SCE_EEVEE_SSR_HALF_RESOLUTION = (1 << 16), + SCE_EEVEE_RAYTRACING_ENABLED = (1 << 14), + // SCE_EEVEE_SSR_REFRACTION = (1 << 15), + // SCE_EEVEE_SSR_HALF_RESOLUTION = (1 << 16), /* Unused */ SCE_EEVEE_SHOW_IRRADIANCE = (1 << 17), SCE_EEVEE_SHOW_CUBEMAPS = (1 << 18), SCE_EEVEE_GI_AUTOBAKE = (1 << 19), diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c index d218f92e639..2c955bfcbc4 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -513,6 +513,25 @@ void RNA_def_camera(BlenderRNA *brna) {CAMERA_SENSOR_FIT_VERT, "VERTICAL", 0, "Vertical", "Fit to the sensor height"}, {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem panorama_type_items[] = { + {CAM_PANO_EQUIRECTANGULAR, + "EQUIRECTANGULAR", + 0, + "Equirectangular", + "Render the scene with a spherical camera, also known as Lat Long panorama"}, + {CAM_PANO_FISHEYE_EQUIDISTANT, + "FISHEYE_EQUIDISTANT", + 0, + "Fisheye Equidistant", + "Ideal for fulldomes, ignore the sensor dimensions"}, + {CAM_PANO_FISHEYE_EQUISOLID, + "FISHEYE_EQUISOLID", + 0, + "Fisheye Equisolid", + "Similar to most fisheye modern lens, takes sensor dimensions into consideration"}, + {CAM_PANO_MIRRORBALL, "MIRRORBALL", 0, "Mirror Ball", "Uses the mirror ball mapping"}, + {0, NULL, 0, NULL, NULL}, + }; srna = RNA_def_struct(brna, "Camera", "ID"); RNA_def_struct_ui_text(srna, "Camera", "Camera data-block for storing camera settings"); @@ -526,6 +545,11 @@ void RNA_def_camera(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Type", "Camera types"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update"); + prop = RNA_def_property(srna, "panorama_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, panorama_type_items); + RNA_def_property_ui_text(prop, "Panorama Type", "Distortion to use for the calculation"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update"); + prop = RNA_def_property(srna, "sensor_fit", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "sensor_fit"); RNA_def_property_enum_items(prop, sensor_fit_items); @@ -581,6 +605,42 @@ void RNA_def_camera(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Focal Length", "Perspective Camera lens value in millimeters"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update"); + prop = RNA_def_property(srna, "fisheye_fov", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_range(prop, 0.1745f, DEG2RAD(360.0f)); + RNA_def_property_ui_range(prop, 0.1745f, DEG2RAD(360.0f * 5.0f), 10, 3); + RNA_def_property_ui_text(prop, "Field of View", "Field of view for the fisheye lens"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update"); + + prop = RNA_def_property(srna, "fisheye_lens", PROP_FLOAT, PROP_DISTANCE_CAMERA); + RNA_def_property_range(prop, 0.01f, 100.0f); + RNA_def_property_ui_range(prop, 0.01f, 15.0f, 100, 4); + RNA_def_property_ui_text(prop, "Fisheye Lens", "Lens focal length (mm)"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update"); + + prop = RNA_def_property(srna, "latitude_min", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_range(prop, DEG2RAD(-90.0f), DEG2RAD(90.0f)); + RNA_def_property_ui_text( + prop, "Min Latitude", "Minimum latitude (vertical angle) for the equirectangular lens"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update"); + + prop = RNA_def_property(srna, "latitude_max", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_range(prop, DEG2RAD(-90.0f), DEG2RAD(90.0f)); + RNA_def_property_ui_text( + prop, "Max Latitude", "Maximum latitude (vertical angle) for the equirectangular lens"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update"); + + prop = RNA_def_property(srna, "longitude_min", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_range(prop, DEG2RAD(-180.0f), DEG2RAD(180.0f)); + RNA_def_property_ui_text( + prop, "Min Longitude", "Minimum longitude (horizontal angle) for the equirectangular lens"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update"); + + prop = RNA_def_property(srna, "longitude_max", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_range(prop, DEG2RAD(-180.0f), DEG2RAD(180.0f)); + RNA_def_property_ui_text( + prop, "Max Longitude", "Maximum longitude (horizontal angle) for the equirectangular lens"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update"); + prop = RNA_def_property(srna, "sensor_width", PROP_FLOAT, PROP_DISTANCE_CAMERA); RNA_def_property_float_sdna(prop, NULL, "sensor_x"); RNA_def_property_range(prop, 1.0f, FLT_MAX); diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 22a75c0d992..9f3ee179ac7 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -807,21 +807,6 @@ void RNA_def_material(BlenderRNA *brna) prop, "Screen Space Refraction", "Use raytraced screen space refractions"); RNA_def_property_update(prop, 0, "rna_Material_draw_update"); - prop = RNA_def_property(srna, "use_sss_translucency", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "blend_flag", MA_BL_TRANSLUCENCY); - RNA_def_property_ui_text( - prop, "Subsurface Translucency", "Add translucency effect to subsurface"); - RNA_def_property_update(prop, 0, "rna_Material_draw_update"); - - prop = RNA_def_property(srna, "refraction_depth", PROP_FLOAT, PROP_DISTANCE); - RNA_def_property_float_sdna(prop, NULL, "refract_depth"); - RNA_def_property_range(prop, 0.0f, FLT_MAX); - RNA_def_property_ui_text(prop, - "Refraction Depth", - "Approximate the thickness of the object to compute two refraction " - "event (0 is disabled)"); - RNA_def_property_update(prop, 0, "rna_Material_draw_update"); - /* For Preview Render */ prop = RNA_def_property(srna, "preview_render_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "pr_type"); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 7f9890e492d..18e50be031b 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -7088,23 +7088,11 @@ static void rna_def_scene_eevee(BlenderRNA *brna) /* Screen Space Reflection */ prop = RNA_def_property(srna, "use_ssr", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_SSR_ENABLED); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_RAYTRACING_ENABLED); RNA_def_property_ui_text(prop, "Screen Space Reflections", "Enable screen space reflection"); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - prop = RNA_def_property(srna, "use_ssr_refraction", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_SSR_REFRACTION); - RNA_def_property_ui_text(prop, "Screen Space Refractions", "Enable screen space Refractions"); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - - prop = RNA_def_property(srna, "use_ssr_halfres", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_SSR_HALF_RESOLUTION); - RNA_def_property_ui_text(prop, "Half Res Trace", "Raytrace at a lower resolution"); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - prop = RNA_def_property(srna, "ssr_quality", PROP_FLOAT, PROP_FACTOR); RNA_def_property_ui_text(prop, "Trace Precision", "Precision of the screen space ray-tracing"); RNA_def_property_range(prop, 0.0f, 1.0f); @@ -7125,12 +7113,6 @@ static void rna_def_scene_eevee(BlenderRNA *brna) RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - prop = RNA_def_property(srna, "ssr_border_fade", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_ui_text(prop, "Edge Fading", "Screen percentage used to fade the SSR"); - RNA_def_property_range(prop, 0.0f, 0.5f); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - prop = RNA_def_property(srna, "ssr_firefly_fac", PROP_FLOAT, PROP_NONE); RNA_def_property_ui_text(prop, "Clamp", "Clamp pixel intensity to remove noise (0 to disabled)"); RNA_def_property_range(prop, 0.0f, FLT_MAX); @@ -7460,6 +7442,15 @@ static void rna_def_scene_eevee(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 50.0f); RNA_def_property_ui_range(prop, 0.0f, 10.0f, 1, 2); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + + prop = RNA_def_property(srna, "use_log_space", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_FILM_LOG_ENCODING); + RNA_def_property_ui_text(prop, + "Log Space", + "Use pre-exposed logarithmic space for Anti-Aliasing accumulation " + "(reduces aliasing of bright areas in color passes)"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); } static void rna_def_scene_gpencil(BlenderRNA *brna) diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h index 57740ce3ad9..3ebd3ef8a2a 100644 --- a/source/blender/nodes/NOD_shader.h +++ b/source/blender/nodes/NOD_shader.h @@ -160,10 +160,7 @@ struct bNode *ntreeShaderOutputNode(struct bNodeTree *ntree, int target); /** * This one needs to work on a local tree. */ -void ntreeGPUMaterialNodes(struct bNodeTree *localtree, - struct GPUMaterial *mat, - bool *has_surface_output, - bool *has_volume_output); +void ntreeGPUMaterialNodes(struct bNodeTree *localtree, struct GPUMaterial *mat); #ifdef __cplusplus } diff --git a/source/blender/nodes/shader/node_shader_tree.cc b/source/blender/nodes/shader/node_shader_tree.cc index 5c3c5889c98..8bf4f37a70b 100644 --- a/source/blender/nodes/shader/node_shader_tree.cc +++ b/source/blender/nodes/shader/node_shader_tree.cc @@ -62,12 +62,6 @@ #include "node_shader_util.hh" #include "node_util.h" -struct nTreeTags { - float ssr_id, sss_id; -}; - -static void ntree_shader_tag_nodes(bNodeTree *ntree, bNode *output_node, nTreeTags *tags); - static bool shader_tree_poll(const bContext *C, bNodeTreeType *UNUSED(treetype)) { Scene *scene = CTX_data_scene(C); @@ -265,6 +259,18 @@ static bNodeSocket *ntree_shader_node_find_output(bNode *node, const char *ident return ntree_shader_node_find_socket(&node->outputs, identifier); } +/* Find input socket at a specific position. */ +static bNodeSocket *ntree_shader_node_input_get(bNode *node, int n) +{ + return reinterpret_cast<bNodeSocket *>(BLI_findlink(&node->inputs, n)); +} + +/* Find output socket at a specific position. */ +static bNodeSocket *ntree_shader_node_output_get(bNode *node, int n) +{ + return reinterpret_cast<bNodeSocket *>(BLI_findlink(&node->outputs, n)); +} + /* Return true on success. */ static bool ntree_shader_expand_socket_default(bNodeTree *localtree, bNode *node, @@ -838,54 +844,309 @@ static bool ntree_shader_bump_branches(bNode *fromnode, bNode *UNUSED(tonode), v return true; } -static bool ntree_tag_bsdf_cb(bNode *fromnode, bNode *UNUSED(tonode), void *userdata) +/* Generate emission node to convert regular data to closure sockets. + * Returns validity of the tree. + */ +static bool ntree_shader_implicit_closure_cast(bNodeTree *ntree) { - switch (fromnode->type) { - case SH_NODE_BSDF_ANISOTROPIC: - case SH_NODE_EEVEE_SPECULAR: - case SH_NODE_BSDF_GLOSSY: - case SH_NODE_BSDF_GLASS: - fromnode->ssr_id = ((nTreeTags *)userdata)->ssr_id; - ((nTreeTags *)userdata)->ssr_id += 1; - break; - case SH_NODE_SUBSURFACE_SCATTERING: - fromnode->sss_id = ((nTreeTags *)userdata)->sss_id; - ((nTreeTags *)userdata)->sss_id += 1; - break; - case SH_NODE_BSDF_PRINCIPLED: - fromnode->ssr_id = ((nTreeTags *)userdata)->ssr_id; - fromnode->sss_id = ((nTreeTags *)userdata)->sss_id; - ((nTreeTags *)userdata)->sss_id += 1; - ((nTreeTags *)userdata)->ssr_id += 1; - break; - default: - /* We could return false here but since we - * allow the use of Closure as RGBA, we can have - * BSDF nodes linked to other BSDF nodes. */ - break; + bool modified = false; + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) { + if ((link->fromsock->type != SOCK_SHADER) && (link->tosock->type == SOCK_SHADER)) { + bNode *emission_node = nodeAddStaticNode(NULL, ntree, SH_NODE_EMISSION); + bNodeSocket *in_sock = ntree_shader_node_find_input(emission_node, "Color"); + bNodeSocket *out_sock = ntree_shader_node_find_output(emission_node, "Emission"); + nodeAddLink(ntree, link->fromnode, link->fromsock, emission_node, in_sock); + nodeAddLink(ntree, emission_node, out_sock, link->tonode, link->tosock); + nodeRemLink(ntree, link); + modified = true; + } + else if ((link->fromsock->type == SOCK_SHADER) && (link->tosock->type != SOCK_SHADER)) { + /* Meh. Not directly visible to the user. But better than nothing. */ + fprintf(stderr, "Shader Nodetree Error: Invalid implicit socket conversion\n"); + BKE_ntree_update_main_tree(G.main, ntree, nullptr); + return false; + } + } + if (modified) { + BKE_ntree_update_main_tree(G.main, ntree, nullptr); } - return true; } -/* EEVEE: Scan the ntree to set the Screen Space Reflection - * layer id of every specular node AND the Subsurface Scattering id of every SSS node. - */ -void ntree_shader_tag_nodes(bNodeTree *ntree, bNode *output_node, nTreeTags *tags) +/* Socket already has a link to it. Add weights together. */ +static void ntree_weight_tree_merge_weight(bNodeTree *ntree, + bNode *UNUSED(fromnode), + bNodeSocket *fromsock, + bNode **tonode, + bNodeSocket **tosock) { - if (output_node == nullptr) { - return; + bNode *addnode = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + addnode->custom1 = NODE_MATH_ADD; + addnode->tmp_flag = -2; /* Copy */ + bNodeSocket *addsock_out = ntree_shader_node_output_get(addnode, 0); + bNodeSocket *addsock_in0 = ntree_shader_node_input_get(addnode, 0); + bNodeSocket *addsock_in1 = ntree_shader_node_input_get(addnode, 1); + bNodeLink *oldlink = fromsock->link; + nodeAddLink(ntree, oldlink->fromnode, oldlink->fromsock, addnode, addsock_in0); + nodeAddLink(ntree, *tonode, *tosock, addnode, addsock_in1); + nodeRemLink(ntree, oldlink); + *tonode = addnode; + *tosock = addsock_out; +} + +static bool ntree_weight_tree_tag_nodes(bNode *fromnode, bNode *tonode, void *userdata) +{ + int *node_count = (int *)userdata; + bool to_node_from_weight_tree = ELEM(tonode->type, + SH_NODE_ADD_SHADER, + SH_NODE_MIX_SHADER, + SH_NODE_OUTPUT_WORLD, + SH_NODE_OUTPUT_MATERIAL); + if (tonode->tmp_flag == -1 && to_node_from_weight_tree) { + tonode->tmp_flag = *node_count; + *node_count += (tonode->type == SH_NODE_MIX_SHADER) ? 4 : 1; + } + if (fromnode->tmp_flag == -1 && ELEM(fromnode->type, SH_NODE_ADD_SHADER, SH_NODE_MIX_SHADER)) { + fromnode->tmp_flag = *node_count; + *node_count += (fromnode->type == SH_NODE_MIX_SHADER) ? 4 : 1; + } + return to_node_from_weight_tree; +} + +/* Invert evaluation order of the weight tree (add & mix closure nodes) to feed the closure nodes + * with their respective weights. */ +static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node) +{ + bNodeLink *displace_link = NULL; + bNodeSocket *displace_output = ntree_shader_node_find_input(output_node, "Displacement"); + if (displace_output && displace_output->link) { + /* Remove any displacement link to avoid tagging it later on. */ + displace_link = displace_output->link; + displace_output->link = NULL; + } + bNodeLink *thickness_link = NULL; + bNodeSocket *thickness_output = ntree_shader_node_find_input(output_node, "Thickness"); + if (thickness_output && thickness_output->link) { + /* Remove any thickness link to avoid tagging it later on. */ + thickness_link = thickness_output->link; + thickness_output->link = NULL; + } + /* Init tmp flag. */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + node->tmp_flag = -1; + } + /* Tag nodes from the weight tree. Only tag output node and mix/add shader nodes. */ + output_node->tmp_flag = 0; + int node_count = 1; + nodeChainIterBackwards(ntree, output_node, ntree_weight_tree_tag_nodes, &node_count, 0); + /* Make a mirror copy of the weight tree. */ + bNode **nodes_copy = static_cast<bNode **>(MEM_mallocN(sizeof(bNode *) * node_count, __func__)); + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->tmp_flag >= 0) { + int id = node->tmp_flag; + + switch (node->type) { + case SH_NODE_OUTPUT_WORLD: + case SH_NODE_OUTPUT_MATERIAL: { + /* Start the tree with full weight. */ + nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_VALUE); + nodes_copy[id]->tmp_flag = -2; /* Copy */ + ((bNodeSocketValueFloat *)ntree_shader_node_output_get(nodes_copy[id], 0)->default_value) + ->value = 1.0f; + break; + } + case SH_NODE_ADD_SHADER: { + /* Simple passthrough node. Each original inputs will get the same weight. */ + /* TODO(fclem) Better use some kind of reroute node? */ + nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + nodes_copy[id]->custom1 = NODE_MATH_ADD; + nodes_copy[id]->tmp_flag = -2; /* Copy */ + ((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value) + ->value = 0.0f; + break; + } + case SH_NODE_MIX_SHADER: { + /* We need multiple nodes to emulate the mix node in reverse. */ + bNode *fromnode, *tonode; + bNodeSocket *fromsock, *tosock; + int id_start = id; + /* output = (factor * input_weight) */ + nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + nodes_copy[id]->custom1 = NODE_MATH_MULTIPLY; + nodes_copy[id]->tmp_flag = -2; /* Copy */ + id++; + /* output = ((1.0 - factor) * input_weight) <=> (input_weight - factor * input_weight) */ + nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + nodes_copy[id]->custom1 = NODE_MATH_SUBTRACT; + nodes_copy[id]->tmp_flag = -2; /* Copy */ + id++; + /* Node sanitizes the input mix factor by clamping it. */ + nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + nodes_copy[id]->custom1 = NODE_MATH_ADD; + nodes_copy[id]->custom2 = SHD_MATH_CLAMP; + nodes_copy[id]->tmp_flag = -2; /* Copy */ + ((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value) + ->value = 0.0f; + /* Copy default value if no link present. */ + bNodeSocket *fac_sock = ntree_shader_node_find_input(node, "Fac"); + if (!fac_sock->link) { + float default_value = ((bNodeSocketValueFloat *)fac_sock->default_value)->value; + bNodeSocket *dst_sock = ntree_shader_node_input_get(nodes_copy[id], 1); + ((bNodeSocketValueFloat *)dst_sock->default_value)->value = default_value; + } + id++; + /* Reroute the weight input to the 3 processing nodes. Simplify linking later-on. */ + /* TODO(fclem) Better use some kind of reroute node? */ + nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + nodes_copy[id]->custom1 = NODE_MATH_ADD; + nodes_copy[id]->tmp_flag = -2; /* Copy */ + ((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value) + ->value = 0.0f; + id++; + /* Link between nodes for the substraction. */ + fromnode = nodes_copy[id_start]; + tonode = nodes_copy[id_start + 1]; + fromsock = ntree_shader_node_output_get(fromnode, 0); + tosock = ntree_shader_node_input_get(tonode, 1); + nodeAddLink(ntree, fromnode, fromsock, tonode, tosock); + /* Link mix input to first node. */ + fromnode = nodes_copy[id_start + 2]; + tonode = nodes_copy[id_start]; + fromsock = ntree_shader_node_output_get(fromnode, 0); + tosock = ntree_shader_node_input_get(tonode, 1); + nodeAddLink(ntree, fromnode, fromsock, tonode, tosock); + /* Link weight input to both multiply nodes. */ + fromnode = nodes_copy[id_start + 3]; + fromsock = ntree_shader_node_output_get(fromnode, 0); + tonode = nodes_copy[id_start]; + tosock = ntree_shader_node_input_get(tonode, 0); + nodeAddLink(ntree, fromnode, fromsock, tonode, tosock); + tonode = nodes_copy[id_start + 1]; + tosock = ntree_shader_node_input_get(tonode, 0); + nodeAddLink(ntree, fromnode, fromsock, tonode, tosock); + break; + } + default: + BLI_assert(0); + break; + } + } + } + /* Recreate links between copied nodes. */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->tmp_flag >= 0) { + /* Naming can be confusing here. We use original nodelink name for from/to prefix. + * The final link is in reversed order. */ + int socket_index; + LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->inputs, socket_index) { + bNodeSocket *tosock; + bNode *tonode; + + switch (node->type) { + case SH_NODE_OUTPUT_WORLD: + case SH_NODE_OUTPUT_MATERIAL: + case SH_NODE_ADD_SHADER: { + tonode = nodes_copy[node->tmp_flag]; + tosock = ntree_shader_node_output_get(tonode, 0); + break; + } + case SH_NODE_MIX_SHADER: { + if (socket_index == 0) { + /* Mix Factor. */ + tonode = nodes_copy[node->tmp_flag + 2]; + tosock = ntree_shader_node_input_get(tonode, 1); + } + else if (socket_index == 1) { + /* Shader 1. */ + tonode = nodes_copy[node->tmp_flag + 1]; + tosock = ntree_shader_node_output_get(tonode, 0); + } + else { + /* Shader 2. */ + tonode = nodes_copy[node->tmp_flag]; + tosock = ntree_shader_node_output_get(tonode, 0); + } + break; + } + default: + BLI_assert(0); + break; + } + + if (sock->link) { + bNodeSocket *fromsock; + bNode *fromnode = sock->link->fromnode; + + switch (fromnode->type) { + case SH_NODE_ADD_SHADER: { + fromnode = nodes_copy[fromnode->tmp_flag]; + fromsock = ntree_shader_node_input_get(fromnode, 1); + if (fromsock->link) { + ntree_weight_tree_merge_weight(ntree, fromnode, fromsock, &tonode, &tosock); + } + break; + } + case SH_NODE_MIX_SHADER: { + fromnode = nodes_copy[fromnode->tmp_flag + 3]; + fromsock = ntree_shader_node_input_get(fromnode, 1); + if (fromsock->link) { + ntree_weight_tree_merge_weight(ntree, fromnode, fromsock, &tonode, &tosock); + } + break; + } + case SH_NODE_BACKGROUND: + case SH_NODE_BSDF_ANISOTROPIC: + case SH_NODE_BSDF_DIFFUSE: + case SH_NODE_BSDF_GLASS: + case SH_NODE_BSDF_GLOSSY: + case SH_NODE_BSDF_HAIR_PRINCIPLED: + case SH_NODE_BSDF_HAIR: + case SH_NODE_BSDF_PRINCIPLED: + case SH_NODE_BSDF_REFRACTION: + case SH_NODE_BSDF_TOON: + case SH_NODE_BSDF_TRANSLUCENT: + case SH_NODE_BSDF_TRANSPARENT: + case SH_NODE_BSDF_VELVET: + case SH_NODE_EEVEE_SPECULAR: + case SH_NODE_EMISSION: + case SH_NODE_HOLDOUT: + case SH_NODE_SUBSURFACE_SCATTERING: + case SH_NODE_VOLUME_ABSORPTION: + case SH_NODE_VOLUME_PRINCIPLED: + case SH_NODE_VOLUME_SCATTER: + fromsock = ntree_shader_node_find_input(fromnode, "Weight"); + if (fromsock->link) { + ntree_weight_tree_merge_weight(ntree, fromnode, fromsock, &tonode, &tosock); + } + break; + default: + fromsock = sock->link->fromsock; + break; + } + + /* Manually add the link to the socket to avoid calling + * BKE_ntree_update_main_tree(G.main, oop, nullptr. */ + fromsock->link = nodeAddLink(ntree, fromnode, fromsock, tonode, tosock); + BLI_assert(fromsock->link); + } + } + } + } + /* Restore displacement & thickness link. */ + if (displace_link) { + nodeAddLink( + ntree, displace_link->fromnode, displace_link->fromsock, output_node, displace_output); + } + if (thickness_link) { + nodeAddLink( + ntree, thickness_link->fromnode, thickness_link->fromsock, output_node, thickness_output); } - /* Make sure sockets links pointers are correct. */ BKE_ntree_update_main_tree(G.main, ntree, nullptr); - nodeChainIterBackwards(ntree, output_node, ntree_tag_bsdf_cb, tags, 0); + MEM_freeN(nodes_copy); } -void ntreeGPUMaterialNodes(bNodeTree *localtree, - GPUMaterial *mat, - bool *has_surface_output, - bool *has_volume_output) +void ntreeGPUMaterialNodes(bNodeTree *localtree, GPUMaterial *mat) { bNodeTreeExec *exec; @@ -901,6 +1162,13 @@ void ntreeGPUMaterialNodes(bNodeTree *localtree, output = ntreeShaderOutputNode(localtree, SHD_OUTPUT_EEVEE); } + /* Tree is valid if it contains no undefined implicit socket type cast. */ + bool valid_tree = ntree_shader_implicit_closure_cast(localtree); + + if (valid_tree && output != NULL) { + ntree_shader_weight_tree_invert(localtree, output); + } + /* Perform all needed modifications on the tree in order to support * displacement/bump mapping. */ @@ -912,19 +1180,8 @@ void ntreeGPUMaterialNodes(bNodeTree *localtree, LISTBASE_FOREACH (bNode *, node, &localtree->nodes) { if (node->type == SH_NODE_OUTPUT_AOV) { nodeChainIterBackwards(localtree, node, ntree_shader_bump_branches, localtree, 0); - nTreeTags tags = {}; - tags.ssr_id = 1.0; - tags.sss_id = 1.0; - ntree_shader_tag_nodes(localtree, node, &tags); } } - - /* TODO(fclem): consider moving this to the gpu shader tree evaluation. */ - nTreeTags tags = {}; - tags.ssr_id = 1.0; - tags.sss_id = 1.0; - ntree_shader_tag_nodes(localtree, output, &tags); - exec = ntreeShaderBeginExecTree(localtree); ntreeExecGPUNodes(exec, mat, output); LISTBASE_FOREACH (bNode *, node, &localtree->nodes) { @@ -933,23 +1190,6 @@ void ntreeGPUMaterialNodes(bNodeTree *localtree, } } ntreeShaderEndExecTree(exec); - - /* EEVEE: Find which material domain was used (volume, surface ...). */ - *has_surface_output = false; - *has_volume_output = false; - - if (output != nullptr) { - bNodeSocket *surface_sock = ntree_shader_node_find_input(output, "Surface"); - bNodeSocket *volume_sock = ntree_shader_node_find_input(output, "Volume"); - - if (surface_sock != nullptr) { - *has_surface_output = (nodeCountSocketLinks(localtree, surface_sock) > 0); - } - - if (volume_sock != nullptr) { - *has_volume_output = (nodeCountSocketLinks(localtree, volume_sock) > 0); - } - } } bNodeTreeExec *ntreeShaderBeginExecTree_internal(bNodeExecContext *context, diff --git a/source/blender/nodes/shader/node_shader_util.cc b/source/blender/nodes/shader/node_shader_util.cc index bb377e2607a..8ea690a426f 100644 --- a/source/blender/nodes/shader/node_shader_util.cc +++ b/source/blender/nodes/shader/node_shader_util.cc @@ -296,7 +296,7 @@ void node_shader_gpu_default_tex_coord(GPUMaterial *mat, bNode *node, GPUNodeLin { if (!*link) { *link = GPU_attribute(mat, CD_ORCO, ""); - GPU_link(mat, "generated_texco", GPU_builtin(GPU_VIEW_POSITION), *link, link); + GPU_link(mat, "generated_texco", *link, link); node_shader_gpu_bump_tex_coord(mat, node, link); } } diff --git a/source/blender/nodes/shader/nodes/node_shader_bevel.cc b/source/blender/nodes/shader/nodes/node_shader_bevel.cc index c4fa61753f5..dfde18d8e2a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bevel.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bevel.cc @@ -48,11 +48,7 @@ static int gpu_shader_bevel(GPUMaterial *mat, GPUNodeStack *out) { if (!in[1].link) { - GPU_link(mat, - "direction_transform_m4v3", - GPU_builtin(GPU_VIEW_NORMAL), - GPU_builtin(GPU_INVERSE_VIEW_MATRIX), - &in[1].link); + GPU_link(mat, "world_normals_get", &in[1].link); } return GPU_stack_link(mat, node, "node_bevel", in, out); diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc index 3f0749ab2af..42f56a0b576 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc @@ -40,6 +40,7 @@ static void node_declare(NodeDeclarationBuilder &b) .subtype(PROP_FACTOR); b.add_input<decl::Vector>(N_("Normal")).hide_value(); b.add_input<decl::Vector>(N_("Tangent")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } @@ -67,13 +68,12 @@ static int node_shader_gpu_bsdf_anisotropic(GPUMaterial *mat, float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f; - return GPU_stack_link(mat, - node, - "node_bsdf_anisotropic", - in, - out, - GPU_constant(&use_multi_scatter), - GPU_constant(&node->ssr_id)); + GPU_stack_link(mat, node, "node_bsdf_anisotropic", in, out); + + GPU_stack_eval_link( + mat, node, "node_bsdf_anisotropic_eval", in, out, GPU_constant(&use_multi_scatter)); + + return true; } } // namespace blender::nodes::node_shader_bsdf_anisotropic_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc index 5848ca76cdd..9767da497ed 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc @@ -30,6 +30,7 @@ static void node_declare(NodeDeclarationBuilder &b) .max(1.0f) .subtype(PROP_FACTOR); b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } @@ -45,7 +46,8 @@ static int node_shader_gpu_bsdf_diffuse(GPUMaterial *mat, GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE); - return GPU_stack_link(mat, node, "node_bsdf_diffuse", in, out); + GPU_stack_link(mat, node, "node_bsdf_diffuse", in, out); + return GPU_stack_eval_link(mat, node, "node_bsdf_diffuse_eval", in, out); } } // namespace blender::nodes::node_shader_bsdf_diffuse_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc index 47d4b87198b..fbc35c23734 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc @@ -31,6 +31,7 @@ static void node_declare(NodeDeclarationBuilder &b) .subtype(PROP_FACTOR); b.add_input<decl::Float>(N_("IOR")).default_value(1.45f).min(0.0f).max(1000.0f); b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } @@ -53,17 +54,30 @@ static int node_shader_gpu_bsdf_glass(GPUMaterial *mat, GPU_link(mat, "set_value_zero", &in[1].link); } - GPU_material_flag_set(mat, (eGPUMatFlag)(GPU_MATFLAG_GLOSSY | GPU_MATFLAG_REFRACT)); + GPU_material_flag_set(mat, GPU_MATFLAG_GLOSSY | GPU_MATFLAG_REFRACT); float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f; - return GPU_stack_link(mat, - node, - "node_bsdf_glass", - in, - out, - GPU_constant(&use_multi_scatter), - GPU_constant(&node->ssr_id)); + GPUNodeLink *reflection_weight; + GPUNodeLink *refraction_weight; + + GPU_stack_link(mat, + node, + "node_bsdf_glass", + in, + out, + GPU_constant(&use_multi_scatter), + &reflection_weight, + &refraction_weight); + + return GPU_stack_eval_link(mat, + node, + "node_bsdf_glass_eval", + in, + out, + GPU_constant(&use_multi_scatter), + reflection_weight, + refraction_weight); } } // namespace blender::nodes::node_shader_bsdf_glass_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc index 03a3e634f56..879e00a6995 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc @@ -30,6 +30,7 @@ static void node_declare(NodeDeclarationBuilder &b) .max(1.0f) .subtype(PROP_FACTOR); b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } @@ -56,13 +57,9 @@ static int node_shader_gpu_bsdf_glossy(GPUMaterial *mat, float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f; - return GPU_stack_link(mat, - node, - "node_bsdf_glossy", - in, - out, - GPU_constant(&use_multi_scatter), - GPU_constant(&node->ssr_id)); + GPU_stack_link(mat, node, "node_bsdf_glossy", in, out); + return GPU_stack_eval_link( + mat, node, "node_bsdf_glossy_eval", in, out, GPU_constant(&use_multi_scatter)); } } // namespace blender::nodes::node_shader_bsdf_glossy_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc index 4c378d9bc09..e49a39b5a4a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc @@ -144,8 +144,6 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - GPUNodeLink *sss_scale; - /* Normals */ if (!in[22].link) { GPU_link(mat, "world_normals_get", &in[22].link); @@ -161,36 +159,17 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat, if (!in[24].link) { GPUNodeLink *orco = GPU_attribute(CD_ORCO, ""); GPU_link(mat, "tangent_orco_z", orco, &in[24].link); - GPU_link(mat, - "node_tangent", - GPU_builtin(GPU_WORLD_NORMAL), - in[24].link, - GPU_builtin(GPU_OBJECT_MATRIX), - &in[24].link); + GPU_link(mat, "node_tangent", in[24].link, &in[24].link); } #endif bool use_diffuse = socket_not_one(6) && socket_not_one(17); - bool use_subsurf = socket_not_zero(1) && use_diffuse && node->sss_id > 0; + bool use_subsurf = socket_not_zero(1) && use_diffuse; bool use_refract = socket_not_one(6) && socket_not_zero(17); - bool use_clear = socket_not_zero(14); + bool use_transparency = socket_not_one(21); + // bool use_clear = socket_not_zero(14); - /* SSS Profile */ - if (use_subsurf) { - bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2); - bNodeSocketValueRGBA *socket_data = (bNodeSocketValueRGBA *)socket->default_value; - /* For some reason it seems that the socket value is in ARGB format. */ - GPU_material_sss_profile_create(mat, &socket_data->value[1]); - } - - if (in[2].link) { - sss_scale = in[2].link; - } - else { - GPU_link(mat, "set_rgb_one", &sss_scale); - } - - uint flag = GPU_MATFLAG_GLOSSY; + eGPUMaterialFlag flag = GPU_MATFLAG_GLOSSY; if (use_diffuse) { flag |= GPU_MATFLAG_DIFFUSE; } @@ -198,28 +177,42 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat, flag |= GPU_MATFLAG_REFRACT; } if (use_subsurf) { - flag |= GPU_MATFLAG_SSS; + flag |= GPU_MATFLAG_SUBSURFACE; + } + if (use_transparency) { + flag |= GPU_MATFLAG_TRANSPARENT; } - float f_use_diffuse = use_diffuse ? 1.0f : 0.0f; - float f_use_clearcoat = use_clear ? 1.0f : 0.0f; - float f_use_refraction = use_refract ? 1.0f : 0.0f; float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f; - GPU_material_flag_set(mat, (eGPUMatFlag)flag); - - return GPU_stack_link(mat, - node, - "node_bsdf_principled", - in, - out, - GPU_constant(&f_use_diffuse), - GPU_constant(&f_use_clearcoat), - GPU_constant(&f_use_refraction), - GPU_constant(&use_multi_scatter), - GPU_constant(&node->ssr_id), - GPU_constant(&node->sss_id), - sss_scale); + GPU_material_flag_set(mat, flag); + + GPUNodeLink *diffuse_weight, *specular_weight, *glass_reflection_weight, + *glass_transmission_weight, *clearcoat_weight; + + GPU_stack_link(mat, + node, + "node_bsdf_principled", + in, + out, + GPU_constant(&use_multi_scatter), + &diffuse_weight, + &specular_weight, + &glass_reflection_weight, + &glass_transmission_weight, + &clearcoat_weight); + + return GPU_stack_eval_link(mat, + node, + "node_bsdf_principled_eval", + in, + out, + GPU_constant(&use_multi_scatter), + diffuse_weight, + specular_weight, + glass_reflection_weight, + glass_transmission_weight, + clearcoat_weight); } static void node_shader_update_principled(bNodeTree *ntree, bNode *node) diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc index 0d588c82869..b1320714c70 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc @@ -31,6 +31,7 @@ static void node_declare(NodeDeclarationBuilder &b) .subtype(PROP_FACTOR); b.add_input<decl::Float>(N_("IOR")).default_value(1.45f).min(0.0f).max(1000.0f); b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } @@ -55,7 +56,8 @@ static int node_shader_gpu_bsdf_refraction(GPUMaterial *mat, GPU_material_flag_set(mat, GPU_MATFLAG_REFRACT); - return GPU_stack_link(mat, node, "node_bsdf_refraction", in, out); + GPU_stack_link(mat, node, "node_bsdf_refraction", in, out); + return GPU_stack_eval_link(mat, node, "node_bsdf_refraction_eval", in, out); } } // namespace blender::nodes::node_shader_bsdf_refraction_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc index 5093b896764..4c9ca56eab2 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc @@ -38,6 +38,7 @@ static void node_declare(NodeDeclarationBuilder &b) .max(1.0f) .subtype(PROP_FACTOR); b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } @@ -58,7 +59,8 @@ static int node_shader_gpu_bsdf_toon(GPUMaterial *mat, GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE); - return GPU_stack_link(mat, node, "node_bsdf_toon", in, out); + GPU_stack_link(mat, node, "node_bsdf_toon", in, out); + return GPU_stack_eval_link(mat, node, "node_bsdf_toon_eval", in, out); } } // namespace blender::nodes::node_shader_bsdf_toon_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc index 22891738299..ec85b5357fa 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc @@ -25,6 +25,7 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } @@ -40,7 +41,8 @@ static int node_shader_gpu_bsdf_translucent(GPUMaterial *mat, GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE); - return GPU_stack_link(mat, node, "node_bsdf_translucent", in, out); + GPU_stack_link(mat, node, "node_bsdf_translucent", in, out); + return GPU_stack_eval_link(mat, node, "node_bsdf_translucent_eval", in, out); } } // namespace blender::nodes::node_shader_bsdf_translucent_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc index d764f4dd76b..d79f3884a4b 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc @@ -33,6 +33,9 @@ static int node_shader_gpu_bsdf_transparent(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { + if (in[0].link || !is_zero_v3(in[0].vec)) { + GPU_material_flag_set(mat, GPU_MATFLAG_TRANSPARENT); + } return GPU_stack_link(mat, node, "node_bsdf_transparent", in, out); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc index dd090236c08..7822c2f5961 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc @@ -30,6 +30,7 @@ static void node_declare(NodeDeclarationBuilder &b) .max(1.0f) .subtype(PROP_FACTOR); b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } @@ -45,7 +46,8 @@ static int node_shader_gpu_bsdf_velvet(GPUMaterial *mat, GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE); - return GPU_stack_link(mat, node, "node_bsdf_velvet", in, out); + GPU_stack_link(mat, node, "node_bsdf_velvet", in, out); + return GPU_stack_link(mat, node, "node_bsdf_velvet_eval", in, out); } } // namespace blender::nodes::node_shader_bsdf_velvet_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_bump.cc b/source/blender/nodes/shader/nodes/node_shader_bump.cc index 252abd02ad7..f0788c543c2 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bump.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bump.cc @@ -66,8 +66,7 @@ static int gpu_shader_bump(GPUMaterial *mat, float invert = (node->custom1) ? -1.0 : 1.0; - return GPU_stack_link( - mat, node, "node_bump", in, out, GPU_builtin(GPU_VIEW_POSITION), GPU_constant(&invert)); + return GPU_stack_link(mat, node, "node_bump", in, out, GPU_constant(&invert)); } } // namespace blender::nodes::node_shader_bump_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_camera.cc b/source/blender/nodes/shader/nodes/node_shader_camera.cc index b9c624faed9..90d2b55b0f7 100644 --- a/source/blender/nodes/shader/nodes/node_shader_camera.cc +++ b/source/blender/nodes/shader/nodes/node_shader_camera.cc @@ -38,11 +38,7 @@ static int gpu_shader_camera(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - GPUNodeLink *viewvec; - - viewvec = GPU_builtin(GPU_VIEW_POSITION); - GPU_link(mat, "invert_z", viewvec, &viewvec); - return GPU_stack_link(mat, node, "camera", in, out, viewvec); + return GPU_stack_link(mat, node, "camera", in, out); } } // namespace blender::nodes::node_shader_camera_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_displacement.cc b/source/blender/nodes/shader/nodes/node_shader_displacement.cc index c56218e795a..47af2447440 100644 --- a/source/blender/nodes/shader/nodes/node_shader_displacement.cc +++ b/source/blender/nodes/shader/nodes/node_shader_displacement.cc @@ -49,16 +49,11 @@ static int gpu_shader_displacement(GPUMaterial *mat, GPUNodeStack *out) { if (!in[3].link) { - GPU_link(mat, - "direction_transform_m4v3", - GPU_builtin(GPU_VIEW_NORMAL), - GPU_builtin(GPU_INVERSE_VIEW_MATRIX), - &in[3].link); + GPU_link(mat, "world_normals_get", &in[3].link); } if (node->custom1 == SHD_SPACE_OBJECT) { - return GPU_stack_link( - mat, node, "node_displacement_object", in, out, GPU_builtin(GPU_OBJECT_MATRIX)); + return GPU_stack_link(mat, node, "node_displacement_object", in, out); } return GPU_stack_link(mat, node, "node_displacement_world", in, out); diff --git a/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc b/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc index e4c80725c8e..8db56551236 100644 --- a/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc +++ b/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc @@ -49,6 +49,7 @@ static void node_declare(NodeDeclarationBuilder &b) .subtype(PROP_FACTOR); b.add_input<decl::Vector>(N_("Clear Coat Normal")).hide_value(); b.add_input<decl::Float>(N_("Ambient Occlusion")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } @@ -75,9 +76,10 @@ static int node_shader_gpu_eevee_specular(GPUMaterial *mat, GPU_link(mat, "set_value", GPU_constant(&one), &in[9].link); } - GPU_material_flag_set(mat, static_cast<eGPUMatFlag>(GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_GLOSSY)); + GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_GLOSSY); - return GPU_stack_link(mat, node, "node_eevee_specular", in, out, GPU_constant(&node->ssr_id)); + GPU_stack_link(mat, node, "node_eevee_specular", in, out); + return GPU_stack_eval_link(mat, node, "node_eevee_specular_eval", in, out); } } // namespace blender::nodes::node_shader_eevee_specular_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_emission.cc b/source/blender/nodes/shader/nodes/node_shader_emission.cc index ea36763578f..93dadf8e898 100644 --- a/source/blender/nodes/shader/nodes/node_shader_emission.cc +++ b/source/blender/nodes/shader/nodes/node_shader_emission.cc @@ -25,6 +25,7 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); b.add_input<decl::Float>(N_("Strength")).default_value(1.0f).min(0.0f).max(1000000.0f); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("Emission")); } @@ -34,7 +35,8 @@ static int node_shader_gpu_emission(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - return GPU_stack_link(mat, node, "node_emission", in, out, GPU_builtin(GPU_VIEW_NORMAL)); + GPU_material_flag_set(mat, GPU_MATFLAG_EMISSION); + return GPU_stack_link(mat, node, "node_emission", in, out); } } // namespace blender::nodes::node_shader_emission_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_fresnel.cc b/source/blender/nodes/shader/nodes/node_shader_fresnel.cc index 5dba42fcc30..19eb79e2138 100644 --- a/source/blender/nodes/shader/nodes/node_shader_fresnel.cc +++ b/source/blender/nodes/shader/nodes/node_shader_fresnel.cc @@ -35,14 +35,10 @@ static int node_shader_gpu_fresnel(GPUMaterial *mat, GPUNodeStack *out) { if (!in[1].link) { - in[1].link = GPU_builtin(GPU_VIEW_NORMAL); - } - else { - GPU_link( - mat, "direction_transform_m4v3", in[1].link, GPU_builtin(GPU_VIEW_MATRIX), &in[1].link); + GPU_link(mat, "world_normals_get", &in[1].link); } - return GPU_stack_link(mat, node, "node_fresnel", in, out, GPU_builtin(GPU_VIEW_POSITION)); + return GPU_stack_link(mat, node, "node_fresnel", in, out); } } // namespace blender::nodes::node_shader_fresnel_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_geometry.cc b/source/blender/nodes/shader/nodes/node_shader_geometry.cc index 2a5868333a9..35eef95adb5 100644 --- a/source/blender/nodes/shader/nodes/node_shader_geometry.cc +++ b/source/blender/nodes/shader/nodes/node_shader_geometry.cc @@ -42,27 +42,15 @@ static int node_shader_gpu_geometry(GPUMaterial *mat, { /* HACK: Don't request GPU_BARYCENTRIC_TEXCO if not used because it will * trigger the use of geometry shader (and the performance penalty it implies). */ - const float val[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPUNodeLink *bary_link = (!out[5].hasoutput) ? GPU_constant(val) : - GPU_builtin(GPU_BARYCENTRIC_TEXCO); if (out[5].hasoutput) { GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC); } /* Opti: don't request orco if not needed. */ + const float val[4] = {0.0f, 0.0f, 0.0f, 0.0f}; GPUNodeLink *orco_link = (!out[2].hasoutput) ? GPU_constant(val) : GPU_attribute(mat, CD_ORCO, ""); - const bool success = GPU_stack_link(mat, - node, - "node_geometry", - in, - out, - GPU_builtin(GPU_VIEW_POSITION), - GPU_builtin(GPU_WORLD_NORMAL), - orco_link, - GPU_builtin(GPU_OBJECT_MATRIX), - GPU_builtin(GPU_INVERSE_VIEW_MATRIX), - bary_link); + const bool success = GPU_stack_link(mat, node, "node_geometry", in, out, orco_link); int i; LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) { diff --git a/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc b/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc index b4210ce154c..0294a0bde07 100644 --- a/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc +++ b/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc @@ -36,14 +36,10 @@ static int node_shader_gpu_layer_weight(GPUMaterial *mat, GPUNodeStack *out) { if (!in[1].link) { - in[1].link = GPU_builtin(GPU_VIEW_NORMAL); - } - else { - GPU_link( - mat, "direction_transform_m4v3", in[1].link, GPU_builtin(GPU_VIEW_MATRIX), &in[1].link); + GPU_link(mat, "world_normals_get", &in[1].link); } - return GPU_stack_link(mat, node, "node_layer_weight", in, out, GPU_builtin(GPU_VIEW_POSITION)); + return GPU_stack_link(mat, node, "node_layer_weight", in, out); } } // namespace blender::nodes::node_shader_layer_weight_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_normal_map.cc b/source/blender/nodes/shader/nodes/node_shader_normal_map.cc index 338b4c62a3a..9eadb6ce014 100644 --- a/source/blender/nodes/shader/nodes/node_shader_normal_map.cc +++ b/source/blender/nodes/shader/nodes/node_shader_normal_map.cc @@ -99,18 +99,16 @@ static int gpu_shader_normal_map(GPUMaterial *mat, GPU_link(mat, color_to_normal_fnc_name, newnormal, &newnormal); switch (nm->space) { case SHD_SPACE_TANGENT: + GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO); GPU_link(mat, "node_normal_map", - GPU_builtin(GPU_OBJECT_INFO), GPU_attribute(mat, CD_TANGENT, nm->uv_map), - GPU_builtin(GPU_WORLD_NORMAL), newnormal, &newnormal); break; case SHD_SPACE_OBJECT: case SHD_SPACE_BLENDER_OBJECT: - GPU_link( - mat, "direction_transform_m4v3", newnormal, GPU_builtin(GPU_OBJECT_MATRIX), &newnormal); + GPU_link(mat, "normal_transform_object_to_world", newnormal, &newnormal); break; case SHD_SPACE_WORLD: case SHD_SPACE_BLENDER_WORLD: @@ -118,8 +116,7 @@ static int gpu_shader_normal_map(GPUMaterial *mat, break; } - GPUNodeLink *oldnormal = GPU_builtin(GPU_WORLD_NORMAL); - GPU_link(mat, "node_normal_map_mix", strength, newnormal, oldnormal, &out[0].link); + GPU_link(mat, "node_normal_map_mix", strength, newnormal, &out[0].link); return true; } diff --git a/source/blender/nodes/shader/nodes/node_shader_object_info.cc b/source/blender/nodes/shader/nodes/node_shader_object_info.cc index 7bc7039d11e..75c9c8f9206 100644 --- a/source/blender/nodes/shader/nodes/node_shader_object_info.cc +++ b/source/blender/nodes/shader/nodes/node_shader_object_info.cc @@ -38,15 +38,8 @@ static int node_shader_gpu_object_info(GPUMaterial *mat, { Material *ma = GPU_material_get_material(mat); float index = ma ? ma->index : 0.0f; - return GPU_stack_link(mat, - node, - "node_object_info", - in, - out, - GPU_builtin(GPU_OBJECT_MATRIX), - GPU_builtin(GPU_OBJECT_COLOR), - GPU_builtin(GPU_OBJECT_INFO), - GPU_constant(&index)); + GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO); + return GPU_stack_link(mat, node, "node_object_info", in, out, GPU_constant(&index)); } } // namespace blender::nodes::node_shader_object_info_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_output_aov.cc b/source/blender/nodes/shader/nodes/node_shader_output_aov.cc index 6ffd763b0d1..b5177014f3a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_output_aov.cc +++ b/source/blender/nodes/shader/nodes/node_shader_output_aov.cc @@ -47,16 +47,13 @@ static int node_shader_gpu_output_aov(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, - GPUNodeStack *out) + GPUNodeStack *UNUSED(out)) { - GPUNodeLink *outlink; NodeShaderOutputAOV *aov = (NodeShaderOutputAOV *)node->storage; /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash` and * `EEVEE_renderpasses_aov_hash`. */ unsigned int hash = BLI_hash_string(aov->name) << 1; - GPU_stack_link(mat, node, "node_output_aov", in, out, &outlink); - GPU_material_add_output_link_aov(mat, outlink, hash); - + GPU_material_add_output_link_aov(mat, in[0].link, hash); return true; } diff --git a/source/blender/nodes/shader/nodes/node_shader_output_material.cc b/source/blender/nodes/shader/nodes/node_shader_output_material.cc index 5fc95b92e3f..e96f55cf3cf 100644 --- a/source/blender/nodes/shader/nodes/node_shader_output_material.cc +++ b/source/blender/nodes/shader/nodes/node_shader_output_material.cc @@ -36,31 +36,24 @@ static int node_shader_gpu_output_material(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - GPUNodeLink *outlink, *alpha_threshold_link, *shadow_threshold_link; - Material *ma = GPU_material_get_material(mat); - - static float no_alpha_threshold = -1.0f; - if (ma) { - alpha_threshold_link = GPU_uniform((ma->blend_method == MA_BM_CLIP) ? &ma->alpha_threshold : - &no_alpha_threshold); - shadow_threshold_link = GPU_uniform((ma->blend_shadow == MA_BS_CLIP) ? &ma->alpha_threshold : - &no_alpha_threshold); + GPUNodeLink *outlink_surface, *outlink_volume, *outlink_displacement, *outlink_thickness; + /* Passthrough node in order to do the right socket conversions (important for displacement). */ + if (in[0].link) { + GPU_link(mat, "node_output_material_surface", in[0].link, &outlink_surface); + GPU_material_output_surface(mat, outlink_surface); } - else { - alpha_threshold_link = GPU_uniform(&no_alpha_threshold); - shadow_threshold_link = GPU_uniform(&no_alpha_threshold); + if (in[1].link) { + GPU_link(mat, "node_output_material_volume", in[1].link, &outlink_volume); + GPU_material_output_volume(mat, outlink_volume); + } + if (in[2].link) { + GPU_link(mat, "node_output_material_displacement", in[2].link, &outlink_displacement); + GPU_material_output_displacement(mat, outlink_displacement); + } + if (in[3].link) { + GPU_link(mat, "node_output_material_thickness", in[3].link, &outlink_thickness); + GPU_material_output_thickness(mat, outlink_thickness); } - - GPU_stack_link(mat, - node, - "node_output_material", - in, - out, - alpha_threshold_link, - shadow_threshold_link, - &outlink); - GPU_material_output_link(mat, outlink); - return true; } diff --git a/source/blender/nodes/shader/nodes/node_shader_output_world.cc b/source/blender/nodes/shader/nodes/node_shader_output_world.cc index 501dc088cbe..a3509fd3ff4 100644 --- a/source/blender/nodes/shader/nodes/node_shader_output_world.cc +++ b/source/blender/nodes/shader/nodes/node_shader_output_world.cc @@ -28,16 +28,20 @@ static void node_declare(NodeDeclarationBuilder &b) } static int node_shader_gpu_output_world(GPUMaterial *mat, - bNode *node, + bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, - GPUNodeStack *out) + GPUNodeStack *UNUSED(out)) { - GPUNodeLink *outlink; - - GPU_stack_link(mat, node, "node_output_world", in, out, &outlink); - GPU_material_output_link(mat, outlink); - + GPUNodeLink *outlink_surface, *outlink_volume; + if (in[0].link) { + GPU_link(mat, "node_output_world_surface", in[0].link, &outlink_surface); + GPU_material_output_surface(mat, outlink_surface); + } + if (in[1].link) { + GPU_link(mat, "node_output_world_volume", in[1].link, &outlink_volume); + GPU_material_output_volume(mat, outlink_volume); + } return true; } diff --git a/source/blender/nodes/shader/nodes/node_shader_particle_info.cc b/source/blender/nodes/shader/nodes/node_shader_particle_info.cc index d68d0fe0d72..5792282fa0f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_particle_info.cc +++ b/source/blender/nodes/shader/nodes/node_shader_particle_info.cc @@ -44,16 +44,9 @@ static int gpu_shader_particle_info(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - - return GPU_stack_link(mat, - node, - "particle_info", - in, - out, - GPU_builtin(GPU_PARTICLE_SCALAR_PROPS), - GPU_builtin(GPU_PARTICLE_LOCATION), - GPU_builtin(GPU_PARTICLE_VELOCITY), - GPU_builtin(GPU_PARTICLE_ANG_VELOCITY)); + GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO); + /* TODO(fclem) Pass particle data in obinfo. */ + return GPU_stack_link(mat, node, "particle_info", in, out); } } // namespace blender::nodes::node_shader_particle_info_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc index 153b1abfbca..d601f45b49f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc @@ -34,9 +34,7 @@ static int node_shader_gpu_shadertorgb(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - /* Because node_shader_to_rgba is using fallback_cubemap() - * we need to tag material as glossy. */ - GPU_material_flag_set(mat, GPU_MATFLAG_GLOSSY); + GPU_material_flag_set(mat, GPU_MATFLAG_SHADER_TO_RGBA); return GPU_stack_link(mat, node, "node_shader_to_rgba", in, out); } diff --git a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc index f60db81b4a9..a3e03d00a1f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc +++ b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc @@ -41,6 +41,7 @@ static void node_declare(NodeDeclarationBuilder &b) .max(1.0f) .subtype(PROP_FACTOR); b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSSRDF")); } @@ -65,19 +66,9 @@ static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat, GPU_link(mat, "world_normals_get", &in[5].link); } - if (node->sss_id > 0) { - bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2); - bNodeSocketValueRGBA *socket_data = (bNodeSocketValueRGBA *)socket->default_value; - /* For some reason it seems that the socket value is in ARGB format. */ - GPU_material_sss_profile_create(mat, &socket_data->value[1]); - - /* sss_id is 0 only the node is not connected to any output. - * In this case flagging the material would trigger a bug (see T68736). */ - GPU_material_flag_set(mat, (eGPUMatFlag)(GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_SSS)); - } - - return GPU_stack_link( - mat, node, "node_subsurface_scattering", in, out, GPU_constant(&node->sss_id)); + GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_SUBSURFACE); + GPU_stack_link(mat, node, "node_subsurface_scattering", in, out); + return GPU_stack_eval_link(mat, node, "node_subsurface_scattering_eval", in, out); } static void node_shader_update_subsurface_scattering(bNodeTree *ntree, bNode *node) diff --git a/source/blender/nodes/shader/nodes/node_shader_tangent.cc b/source/blender/nodes/shader/nodes/node_shader_tangent.cc index c4e5660b9f8..58625cacf9c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tangent.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tangent.cc @@ -89,14 +89,7 @@ static int node_shader_gpu_tangent(GPUMaterial *mat, GPU_link(mat, "tangent_orco_z", orco, &orco); } - return GPU_stack_link(mat, - node, - "node_tangent", - in, - out, - GPU_builtin(GPU_WORLD_NORMAL), - orco, - GPU_builtin(GPU_OBJECT_MATRIX)); + return GPU_stack_link(mat, node, "node_tangent", in, out, orco); } } // namespace blender::nodes::node_shader_tangent_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc index 1bbaed88ea5..d81316b0f8b 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc @@ -51,24 +51,22 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat, { Object *ob = (Object *)node->id; - GPUNodeLink *inv_obmat = (ob != nullptr) ? GPU_uniform(&ob->imat[0][0]) : - GPU_builtin(GPU_INVERSE_OBJECT_MATRIX); + /* Use special matrix to let the shader branch to using the render object's matrix. */ + float dummy_matrix[4][4]; + dummy_matrix[3][3] = 0.0f; + GPUNodeLink *inv_obmat = (ob != NULL) ? GPU_uniform(&ob->imat[0][0]) : + GPU_uniform(&dummy_matrix[0][0]); /* Opti: don't request orco if not needed. */ - const float default_coords[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPUNodeLink *orco = (!out[0].hasoutput) ? GPU_constant(default_coords) : - GPU_attribute(mat, CD_ORCO, ""); + float4 zero(0.0f); + GPUNodeLink *orco = (!out[0].hasoutput) ? GPU_constant(zero) : GPU_attribute(mat, CD_ORCO, ""); GPUNodeLink *mtface = GPU_attribute(mat, CD_MTFACE, ""); - GPUNodeLink *viewpos = GPU_builtin(GPU_VIEW_POSITION); - GPUNodeLink *worldnor = GPU_builtin(GPU_WORLD_NORMAL); - GPUNodeLink *texcofacs = GPU_builtin(GPU_CAMERA_TEXCO_FACTORS); if (out[0].hasoutput) { GPU_link(mat, "generated_from_orco", orco, &orco); } - GPU_stack_link( - mat, node, "node_tex_coord", in, out, viewpos, worldnor, inv_obmat, texcofacs, orco, mtface); + GPU_stack_link(mat, node, "node_tex_coord", in, out, inv_obmat, orco, mtface); int i; LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) { diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc index 7b947392084..24ecdee12d8 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc @@ -60,15 +60,13 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, GPUNodeLink *outalpha; - if (!ima) { + /* HACK(fclem): For lookdev mode: do not compile an empty environment and just create an empty + * texture entry point. We manually bind to it after DRW_shgroup_add_material_resources(). */ + if (!ima && !GPU_material_flag_get(mat, GPU_MATFLAG_LOOKDEV_HACK)) { return GPU_stack_link(mat, node, "node_tex_environment_empty", in, out); } - if (!in[0].link) { - GPU_link(mat, "node_tex_environment_texco", GPU_builtin(GPU_VIEW_POSITION), &in[0].link); - node_shader_gpu_bump_tex_coord(mat, node, &in[0].link); - } - + node_shader_gpu_default_tex_coord(mat, node, &in[0].link); node_shader_gpu_tex_mapping(mat, node, in, out); /* Compute texture coordinate. */ @@ -108,7 +106,7 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, /* Sample texture with correct interpolation. */ GPU_link(mat, gpu_fn, in[0].link, GPU_image(mat, ima, iuser, sampler), &out[0].link, &outalpha); - if (out[0].hasoutput) { + if (out[0].hasoutput && ima) { if (ELEM(ima->alpha_mode, IMA_ALPHA_IGNORE, IMA_ALPHA_CHANNEL_PACKED) || IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name)) { /* Don't let alpha affect color output in these cases. */ diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc index d5479f46a35..7c7154b8f7a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc @@ -104,13 +104,11 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, } case SHD_PROJ_BOX: { gpu_node_name = use_cubic ? "tex_box_sample_cubic" : "tex_box_sample_linear"; - GPUNodeLink *wnor, *col1, *col2, *col3; - GPUNodeLink *vnor = GPU_builtin(GPU_WORLD_NORMAL); - GPUNodeLink *ob_mat = GPU_builtin(GPU_OBJECT_MATRIX); + GPUNodeLink *vnor, *wnor, *col1, *col2, *col3; GPUNodeLink *blend = GPU_uniform(&tex->projection_blend); GPUNodeLink *gpu_image = GPU_image(mat, ima, iuser, sampler_state); - /* equivalent to normal_world_to_object */ - GPU_link(mat, "normal_transform_transposed_m4v3", vnor, ob_mat, &wnor); + GPU_link(mat, "world_normals_get", &vnor); + GPU_link(mat, "normal_transform_world_to_object", vnor, &wnor); GPU_link(mat, gpu_node_name, in[0].link, wnor, gpu_image, &col1, &col2, &col3); GPU_link(mat, "tex_box_blend", wnor, col1, col2, col3, blend, &out[0].link, &out[1].link); break; diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc b/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc index 3486eda1635..d34ef29c30a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc @@ -40,23 +40,20 @@ static int gpu_shader_vector_displacement(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - if (node->custom1 == SHD_SPACE_TANGENT) { - return GPU_stack_link(mat, - node, - "node_vector_displacement_tangent", - in, - out, - GPU_attribute(mat, CD_TANGENT, ""), - GPU_builtin(GPU_WORLD_NORMAL), - GPU_builtin(GPU_OBJECT_MATRIX), - GPU_builtin(GPU_VIEW_MATRIX)); + switch (node->custom1) { + case SHD_SPACE_TANGENT: + return GPU_stack_link(mat, + node, + "node_vector_displacement_tangent", + in, + out, + GPU_attribute(mat, CD_TANGENT, "")); + case SHD_SPACE_OBJECT: + return GPU_stack_link(mat, node, "node_vector_displacement_object", in, out); + case SHD_SPACE_WORLD: + default: + return GPU_stack_link(mat, node, "node_vector_displacement_world", in, out); } - if (node->custom1 == SHD_SPACE_OBJECT) { - return GPU_stack_link( - mat, node, "node_vector_displacement_object", in, out, GPU_builtin(GPU_OBJECT_MATRIX)); - } - - return GPU_stack_link(mat, node, "node_vector_displacement_world", in, out); } } // namespace blender::nodes::node_shader_vector_displacement_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc index a8a6902e30c..e38f67fb751 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc @@ -59,42 +59,43 @@ static void node_shader_init_vect_transform(bNodeTree *UNUSED(ntree), bNode *nod node->storage = vect; } -static GPUNodeLink *get_gpulink_matrix_from_to(short from, short to) +static const char *get_gpufn_name_from_to(short from, short to) { switch (from) { case SHD_VECT_TRANSFORM_SPACE_OBJECT: switch (to) { case SHD_VECT_TRANSFORM_SPACE_OBJECT: - return nullptr; + return NULL; case SHD_VECT_TRANSFORM_SPACE_WORLD: - return GPU_builtin(GPU_OBJECT_MATRIX); + return "object_to_world"; case SHD_VECT_TRANSFORM_SPACE_CAMERA: - return GPU_builtin(GPU_LOC_TO_VIEW_MATRIX); + return "object_to_view"; } break; case SHD_VECT_TRANSFORM_SPACE_WORLD: switch (to) { case SHD_VECT_TRANSFORM_SPACE_WORLD: - return nullptr; + return NULL; case SHD_VECT_TRANSFORM_SPACE_CAMERA: - return GPU_builtin(GPU_VIEW_MATRIX); + return "world_to_view"; case SHD_VECT_TRANSFORM_SPACE_OBJECT: - return GPU_builtin(GPU_INVERSE_OBJECT_MATRIX); + return "world_to_object"; } break; case SHD_VECT_TRANSFORM_SPACE_CAMERA: switch (to) { case SHD_VECT_TRANSFORM_SPACE_CAMERA: - return nullptr; + return NULL; case SHD_VECT_TRANSFORM_SPACE_WORLD: - return GPU_builtin(GPU_INVERSE_VIEW_MATRIX); + return "view_to_world"; case SHD_VECT_TRANSFORM_SPACE_OBJECT: - return GPU_builtin(GPU_INVERSE_LOC_TO_VIEW_MATRIX); + return "view_to_object"; } break; } - return nullptr; + return NULL; } + static int gpu_shader_vect_transform(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), @@ -102,11 +103,6 @@ static int gpu_shader_vect_transform(GPUMaterial *mat, GPUNodeStack *out) { struct GPUNodeLink *inputlink; - struct GPUNodeLink *fromto; - - const char *vtransform = "direction_transform_m4v3"; - const char *ptransform = "point_transform_m4v3"; - const char *func_name = nullptr; NodeShaderVectTransform *nodeprop = (NodeShaderVectTransform *)node->storage; @@ -117,9 +113,10 @@ static int gpu_shader_vect_transform(GPUMaterial *mat, inputlink = GPU_constant(in[0].vec); } - fromto = get_gpulink_matrix_from_to(nodeprop->convert_from, nodeprop->convert_to); + const char *xform = (nodeprop->type == SHD_VECT_TRANSFORM_TYPE_POINT) ? "point_transform_" : + "direction_transform_"; + const char *fromto = get_gpufn_name_from_to(nodeprop->convert_from, nodeprop->convert_to); - func_name = (nodeprop->type == SHD_VECT_TRANSFORM_TYPE_POINT) ? ptransform : vtransform; if (fromto) { /* For cycles we have inverted Z */ /* TODO: pass here the correct matrices */ @@ -127,7 +124,11 @@ static int gpu_shader_vect_transform(GPUMaterial *mat, nodeprop->convert_to != SHD_VECT_TRANSFORM_SPACE_CAMERA) { GPU_link(mat, "invert_z", inputlink, &inputlink); } - GPU_link(mat, func_name, inputlink, fromto, &out[0].link); + + char func_name[48]; + SNPRINTF(func_name, "%s%s", xform, fromto); + GPU_link(mat, func_name, inputlink, &out[0].link); + if (nodeprop->convert_to == SHD_VECT_TRANSFORM_SPACE_CAMERA && nodeprop->convert_from != SHD_VECT_TRANSFORM_SPACE_CAMERA) { GPU_link(mat, "invert_z", out[0].link, &out[0].link); diff --git a/source/blender/nodes/shader/nodes/node_shader_wireframe.cc b/source/blender/nodes/shader/nodes/node_shader_wireframe.cc index 1ba2391a09d..c4771cc13e9 100644 --- a/source/blender/nodes/shader/nodes/node_shader_wireframe.cc +++ b/source/blender/nodes/shader/nodes/node_shader_wireframe.cc @@ -44,17 +44,11 @@ static int node_shader_gpu_wireframe(GPUMaterial *mat, GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC); /* node->custom1 is use_pixel_size */ if (node->custom1) { - return GPU_stack_link( - mat, node, "node_wireframe_screenspace", in, out, GPU_builtin(GPU_BARYCENTRIC_TEXCO)); + return GPU_stack_link(mat, node, "node_wireframe_screenspace", in, out); + } + else { + return GPU_stack_link(mat, node, "node_wireframe", in, out); } - - return GPU_stack_link(mat, - node, - "node_wireframe", - in, - out, - GPU_builtin(GPU_BARYCENTRIC_TEXCO), - GPU_builtin(GPU_BARYCENTRIC_DIST)); } } // namespace blender::nodes::node_shader_wireframe_cc diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 6caac79c4d5..b7eafc6dfad 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -132,6 +132,7 @@ #include "BKE_sound.h" #include "BKE_subdiv.h" +#include "BKE_world.h" #include "COM_compositor.h" @@ -513,6 +514,7 @@ void WM_exit_ex(bContext *C, const bool do_python) BKE_addon_pref_type_free(); BKE_keyconfig_pref_type_free(); BKE_materials_exit(); + BKE_worlds_exit(); wm_operatortype_free(); wm_surfaces_free(); diff --git a/source/creator/creator.c b/source/creator/creator.c index d69e4d075fd..900071ec1b0 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -67,6 +67,7 @@ #include "BKE_sound.h" #include "BKE_vfont.h" #include "BKE_volume.h" +#include "BKE_world.h" #include "DEG_depsgraph.h" @@ -494,6 +495,7 @@ int main(int argc, BKE_sound_init_once(); BKE_materials_init(); + BKE_worlds_init(); #ifndef WITH_PYTHON_MODULE if (G.background == 0) { diff --git a/tests/python/eevee_render_tests.py b/tests/python/eevee_render_tests.py index 6a21afc7c9a..f52b37a28cb 100644 --- a/tests/python/eevee_render_tests.py +++ b/tests/python/eevee_render_tests.py @@ -30,12 +30,6 @@ def setup(): eevee.use_volumetric_shadows = True eevee.volumetric_tile_size = '2' - for mat in bpy.data.materials: - # This needs to be enabled case by case, - # otherwise we loose SSR and GTAO everywhere. - # mat.use_screen_refraction = True - mat.use_sss_translucency = True - cubemap = None grid = None # Does not work in edit mode |