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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2022-06-28 19:33:25 +0300
committerClément Foucault <foucault.clem@gmail.com>2022-06-30 23:45:42 +0300
commitf18067aa032a35e8a6cc990b5c250567d0f4d78f (patch)
treed7521c5a487952d2f9855ba7492943a6713b0516 /source/blender/draw/engines
parenta9696f04a012ce23ef419afe489378d1ce840638 (diff)
EEVEE-Next: Add Film and RenderBuffers module
This modules handles renderpasses allocation and filling. Also handles blitting to viewport framebuffer and render result reading. Changes against the old implementation: - the filling of the renderpasses happens all at once requiring only 1 geometry pass. - The filtering is optimized with weights precomputed on CPU and reuse of neighboor pixels. - Only one accumulation buffer for renderpasses (no ping-pong). - Accumulation happens in one pass for every passes using a single dispatch or fullscreen triangle pass. TAA and history reprojection is not yet implemented. AOVs support is present but with a 16 AOV limit for now. Cryptomatte is not yet implemented.
Diffstat (limited to 'source/blender/draw/engines')
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_camera.cc11
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_camera.hh3
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_defines.hh2
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_engine.cc22
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_film.cc579
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_film.hh187
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_instance.cc83
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_instance.hh29
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_material.cc2
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_pipeline.cc28
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc96
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh57
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_sampling.cc264
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_sampling.hh172
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader.cc5
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader.hh5
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader_shared.hh190
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_sync.cc16
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_velocity.cc4
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_view.cc76
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_view.hh19
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_world.cc2
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_film_comp.glsl13
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl29
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl387
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl14
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl44
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl8
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh44
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh55
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh1
31 files changed, 2334 insertions, 113 deletions
diff --git a/source/blender/draw/engines/eevee_next/eevee_camera.cc b/source/blender/draw/engines/eevee_next/eevee_camera.cc
index e6d2e2db764..1f65f887d46 100644
--- a/source/blender/draw/engines/eevee_next/eevee_camera.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_camera.cc
@@ -77,9 +77,10 @@ void Camera::init()
void Camera::sync()
{
const Object *camera_eval = inst_.camera_eval_object;
- CameraData &data = data_.current();
- data.filter_size = inst_.scene->r.gauss;
+ data_.swap();
+
+ CameraData &data = data_.current();
if (inst_.drw_view) {
DRW_view_viewmat_get(inst_.drw_view, data.viewmat.ptr(), false);
@@ -127,6 +128,10 @@ void Camera::sync()
data.equirect_scale *= data.uv_scale;
data.equirect_scale_inv = 1.0f / data.equirect_scale;
+#else
+ data.fisheye_fov = data.fisheye_lens = -1.0f;
+ data.equirect_bias = float2(0.0f);
+ data.equirect_scale = float2(0.0f);
#endif
}
else if (inst_.drw_view) {
@@ -143,7 +148,7 @@ void Camera::sync()
/* Detect changes in parameters. */
if (data_.current() != data_.previous()) {
- // inst_.sampling.reset();
+ inst_.sampling.reset();
}
}
diff --git a/source/blender/draw/engines/eevee_next/eevee_camera.hh b/source/blender/draw/engines/eevee_next/eevee_camera.hh
index dfec738b1f3..3b3586190c4 100644
--- a/source/blender/draw/engines/eevee_next/eevee_camera.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_camera.hh
@@ -61,8 +61,7 @@ inline bool operator==(const CameraData &a, const CameraData &b)
return compare_m4m4(a.persmat.ptr(), b.persmat.ptr(), FLT_MIN) && (a.uv_scale == b.uv_scale) &&
(a.uv_bias == b.uv_bias) && (a.equirect_scale == b.equirect_scale) &&
(a.equirect_bias == b.equirect_bias) && (a.fisheye_fov == b.fisheye_fov) &&
- (a.fisheye_lens == b.fisheye_lens) && (a.filter_size == b.filter_size) &&
- (a.type == b.type);
+ (a.fisheye_lens == b.fisheye_lens) && (a.type == b.type);
}
inline bool operator!=(const CameraData &a, const CameraData &b)
diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh
index f75ebd2bd13..1e7979b594e 100644
--- a/source/blender/draw/engines/eevee_next/eevee_defines.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh
@@ -43,3 +43,5 @@
/* Minimum visibility size. */
#define LIGHTPROBE_FILTER_VIS_GROUP_SIZE 16
+
+#define FILM_GROUP_SIZE 16
diff --git a/source/blender/draw/engines/eevee_next/eevee_engine.cc b/source/blender/draw/engines/eevee_next/eevee_engine.cc
index be0adfad568..37b4bde324e 100644
--- a/source/blender/draw/engines/eevee_next/eevee_engine.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_engine.cc
@@ -12,6 +12,8 @@
#include "DRW_render.h"
+#include "RE_pipeline.h"
+
#include "eevee_engine.h" /* Own include. */
#include "eevee_instance.hh"
@@ -97,6 +99,8 @@ static void eevee_draw_scene(void *vedata)
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
ved->instance->draw_viewport(dfbl);
STRNCPY(ved->info, ved->instance->info.c_str());
+ /* Reset view for other following engines. */
+ DRW_view_set_active(nullptr);
}
static void eevee_cache_init(void *vedata)
@@ -144,7 +148,23 @@ static void eevee_render_to_image(void *UNUSED(vedata),
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
- UNUSED_VARS(engine, layer);
+
+ eevee::Instance *instance = new eevee::Instance();
+
+ Render *render = engine->re;
+ Depsgraph *depsgraph = DRW_context_state_get()->depsgraph;
+ Object *camera_original_ob = RE_GetCamera(engine->re);
+ const char *viewname = RE_GetActiveRenderView(engine->re);
+ int size[2] = {engine->resolution_x, engine->resolution_y};
+
+ rctf view_rect;
+ rcti rect;
+ RE_GetViewPlane(render, &view_rect, &rect);
+
+ instance->init(size, &rect, engine, depsgraph, nullptr, camera_original_ob, layer);
+ instance->render_frame(layer, viewname);
+
+ delete instance;
}
static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
diff --git a/source/blender/draw/engines/eevee_next/eevee_film.cc b/source/blender/draw/engines/eevee_next/eevee_film.cc
new file mode 100644
index 00000000000..44737018c62
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_film.cc
@@ -0,0 +1,579 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * 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_hash.h"
+#include "BLI_rect.h"
+
+#include "GPU_framebuffer.h"
+#include "GPU_texture.h"
+
+#include "DRW_render.h"
+#include "RE_pipeline.h"
+
+#include "eevee_film.hh"
+#include "eevee_instance.hh"
+
+namespace blender::eevee {
+
+ENUM_OPERATORS(eViewLayerEEVEEPassType, 1 << EEVEE_RENDER_PASS_MAX_BIT)
+
+/* -------------------------------------------------------------------- */
+/** \name Arbitrary Output Variables
+ * \{ */
+
+void Film::init_aovs()
+{
+ Vector<ViewLayerAOV *> aovs;
+
+ aovs_info.display_id = -1;
+ aovs_info.display_is_value = false;
+ aovs_info.value_len = aovs_info.color_len = 0;
+
+ if (inst_.is_viewport()) {
+ /* Viewport case. */
+ if (inst_.v3d->shading.render_pass == EEVEE_RENDER_PASS_AOV) {
+ /* AOV display, request only a single AOV. */
+ ViewLayerAOV *aov = (ViewLayerAOV *)BLI_findstring(
+ &inst_.view_layer->aovs, inst_.v3d->shading.aov_name, offsetof(ViewLayerAOV, name));
+
+ if (aov == nullptr) {
+ /* AOV not found in view layer. */
+ return;
+ }
+
+ aovs.append(aov);
+ aovs_info.display_id = 0;
+ aovs_info.display_is_value = (aov->type == AOV_TYPE_VALUE);
+ }
+ else {
+ /* TODO(fclem): The realtime compositor could ask for several AOVs. */
+ }
+ }
+ else {
+ /* Render case. */
+ LISTBASE_FOREACH (ViewLayerAOV *, aov, &inst_.view_layer->aovs) {
+ aovs.append(aov);
+ }
+ }
+
+ if (aovs.size() > AOV_MAX) {
+ inst_.info = "Error: Too many AOVs";
+ return;
+ }
+
+ for (ViewLayerAOV *aov : aovs) {
+ bool is_value = (aov->type == AOV_TYPE_VALUE);
+ uint &index = is_value ? aovs_info.value_len : aovs_info.color_len;
+ uint &hash = is_value ? aovs_info.hash_value[index] : aovs_info.hash_color[index];
+ hash = BLI_hash_string(aov->name);
+ index++;
+ }
+}
+
+float *Film::read_aov(ViewLayerAOV *aov)
+{
+ bool is_value = (aov->type == AOV_TYPE_VALUE);
+ Texture &accum_tx = is_value ? value_accum_tx_ : color_accum_tx_;
+
+ Span<uint> aovs_hash(is_value ? aovs_info.hash_value : aovs_info.hash_color,
+ is_value ? aovs_info.value_len : aovs_info.color_len);
+ /* Find AOV index. */
+ uint hash = BLI_hash_string(aov->name);
+ int aov_index = -1;
+ int i = 0;
+ for (uint candidate_hash : aovs_hash) {
+ if (candidate_hash == hash) {
+ aov_index = i;
+ break;
+ }
+ i++;
+ }
+
+ accum_tx.ensure_layer_views();
+
+ int index = aov_index + (is_value ? data_.aov_value_id : data_.aov_color_id);
+ GPUTexture *pass_tx = accum_tx.layer_view(index);
+
+ return (float *)GPU_texture_read(pass_tx, GPU_DATA_FLOAT, 0);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mist Pass
+ * \{ */
+
+void Film::sync_mist()
+{
+ const CameraData &cam = inst_.camera.data_get();
+ const ::World *world = inst_.scene->world;
+ float mist_start = world ? world->miststa : cam.clip_near;
+ float mist_distance = world ? world->mistdist : fabsf(cam.clip_far - cam.clip_near);
+ int mist_type = world ? world->mistype : WO_MIST_LINEAR;
+
+ switch (mist_type) {
+ case WO_MIST_QUADRATIC:
+ data_.mist_exponent = 2.0f;
+ break;
+ case WO_MIST_LINEAR:
+ data_.mist_exponent = 1.0f;
+ break;
+ case WO_MIST_INVERSE_QUADRATIC:
+ data_.mist_exponent = 0.5f;
+ break;
+ }
+
+ data_.mist_scale = 1.0 / mist_distance;
+ data_.mist_bias = -mist_start / mist_distance;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name FilmData
+ * \{ */
+
+inline bool operator==(const FilmData &a, const FilmData &b)
+{
+ return (a.extent == b.extent) && (a.offset == b.offset) && (a.filter_size == b.filter_size) &&
+ (a.scaling_factor == b.scaling_factor);
+}
+
+inline bool operator!=(const FilmData &a, const FilmData &b)
+{
+ return !(a == b);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Film
+ * \{ */
+
+void Film::init(const int2 &extent, const rcti *output_rect)
+{
+ init_aovs();
+
+ {
+ /* Enable passes that need to be rendered. */
+ eViewLayerEEVEEPassType render_passes;
+
+ if (inst_.is_viewport()) {
+ /* Viewport Case. */
+ render_passes = eViewLayerEEVEEPassType(inst_.v3d->shading.render_pass);
+
+ if (inst_.overlays_enabled() || inst_.gpencil_engine_enabled) {
+ /* Overlays and Grease Pencil needs the depth for correct compositing.
+ * Using the render pass ensure we store the center depth. */
+ render_passes |= EEVEE_RENDER_PASS_Z;
+ }
+ }
+ else {
+ /* Render Case. */
+ render_passes = eViewLayerEEVEEPassType(inst_.view_layer->eevee.render_passes);
+
+ render_passes |= EEVEE_RENDER_PASS_COMBINED;
+
+#define ENABLE_FROM_LEGACY(name_legacy, name_eevee) \
+ SET_FLAG_FROM_TEST(render_passes, \
+ (inst_.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
+ }
+
+ /* Filter obsolete passes. */
+ render_passes &= ~(EEVEE_RENDER_PASS_UNUSED_8 | EEVEE_RENDER_PASS_BLOOM);
+
+ /* TODO(@fclem): Can't we rely on depsgraph update notification? */
+ if (assign_if_different(enabled_passes_, render_passes)) {
+ inst_.sampling.reset();
+ }
+ }
+ {
+ 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;
+ }
+
+ FilmData data = data_;
+ data.extent = int2(BLI_rcti_size_x(output_rect), BLI_rcti_size_y(output_rect));
+ data.offset = int2(output_rect->xmin, output_rect->ymin);
+ data.filter_size = clamp_f(inst_.scene->r.gauss, 0.0f, 100.0f);
+ /* TODO(fclem): parameter hidden in experimental.
+ * We need to figure out LOD bias first in order to preserve texture crispiness. */
+ data.scaling_factor = 1;
+
+ FilmData &data_prev_ = data_;
+ if (assign_if_different(data_prev_, data)) {
+ inst_.sampling.reset();
+ }
+
+ const eViewLayerEEVEEPassType data_passes = EEVEE_RENDER_PASS_Z | EEVEE_RENDER_PASS_NORMAL |
+ EEVEE_RENDER_PASS_VECTOR;
+ const eViewLayerEEVEEPassType color_passes_1 = EEVEE_RENDER_PASS_DIFFUSE_LIGHT |
+ EEVEE_RENDER_PASS_SPECULAR_LIGHT |
+ EEVEE_RENDER_PASS_VOLUME_LIGHT |
+ EEVEE_RENDER_PASS_EMIT;
+ const eViewLayerEEVEEPassType color_passes_2 = EEVEE_RENDER_PASS_DIFFUSE_COLOR |
+ EEVEE_RENDER_PASS_SPECULAR_COLOR |
+ EEVEE_RENDER_PASS_ENVIRONMENT |
+ EEVEE_RENDER_PASS_MIST |
+ EEVEE_RENDER_PASS_SHADOW | EEVEE_RENDER_PASS_AO;
+
+ data_.exposure = 1.0f /* TODO */;
+ data_.has_data = (enabled_passes_ & data_passes) != 0;
+ data_.any_render_pass_1 = (enabled_passes_ & color_passes_1) != 0;
+ data_.any_render_pass_2 = (enabled_passes_ & color_passes_2) != 0;
+ }
+ {
+ /* Set pass offsets. */
+
+ data_.display_id = aovs_info.display_id;
+ data_.display_is_value = aovs_info.display_is_value;
+
+ /* Combined is in a separate buffer. */
+ data_.combined_id = (enabled_passes_ & EEVEE_RENDER_PASS_COMBINED) ? 0 : -1;
+ /* Depth is in a separate buffer. */
+ data_.depth_id = (enabled_passes_ & EEVEE_RENDER_PASS_Z) ? 0 : -1;
+
+ data_.color_len = 0;
+ data_.value_len = 0;
+
+ auto pass_index_get = [&](eViewLayerEEVEEPassType pass_type) {
+ bool is_value = pass_is_value(pass_type);
+ int index = (enabled_passes_ & pass_type) ?
+ (is_value ? data_.value_len : data_.color_len)++ :
+ -1;
+ if (inst_.is_viewport() && inst_.v3d->shading.render_pass == pass_type) {
+ data_.display_id = index;
+ data_.display_is_value = is_value;
+ }
+ return index;
+ };
+
+ data_.mist_id = pass_index_get(EEVEE_RENDER_PASS_MIST);
+ data_.normal_id = pass_index_get(EEVEE_RENDER_PASS_NORMAL);
+ data_.vector_id = pass_index_get(EEVEE_RENDER_PASS_VECTOR);
+ data_.diffuse_light_id = pass_index_get(EEVEE_RENDER_PASS_DIFFUSE_LIGHT);
+ data_.diffuse_color_id = pass_index_get(EEVEE_RENDER_PASS_DIFFUSE_COLOR);
+ data_.specular_light_id = pass_index_get(EEVEE_RENDER_PASS_SPECULAR_LIGHT);
+ data_.specular_color_id = pass_index_get(EEVEE_RENDER_PASS_SPECULAR_COLOR);
+ data_.volume_light_id = pass_index_get(EEVEE_RENDER_PASS_VOLUME_LIGHT);
+ data_.emission_id = pass_index_get(EEVEE_RENDER_PASS_EMIT);
+ data_.environment_id = pass_index_get(EEVEE_RENDER_PASS_ENVIRONMENT);
+ data_.shadow_id = pass_index_get(EEVEE_RENDER_PASS_SHADOW);
+ data_.ambient_occlusion_id = pass_index_get(EEVEE_RENDER_PASS_AO);
+
+ data_.aov_color_id = data_.color_len;
+ data_.aov_value_id = data_.value_len;
+
+ data_.aov_color_len = aovs_info.color_len;
+ data_.aov_value_len = aovs_info.value_len;
+
+ data_.color_len += data_.aov_color_len;
+ data_.value_len += data_.aov_value_len;
+ }
+ {
+ /* TODO(fclem): Overscans. */
+
+ render_extent_ = math::divide_ceil(extent, int2(data_.scaling_factor));
+ int2 weight_extent = inst_.camera.is_panoramic() ? data_.extent : int2(data_.scaling_factor);
+
+ eGPUTextureFormat color_format = GPU_RGBA16F;
+ eGPUTextureFormat float_format = GPU_R16F;
+ eGPUTextureFormat weight_format = GPU_R32F;
+ eGPUTextureFormat depth_format = GPU_R32F;
+
+ int reset = 0;
+ reset += depth_tx_.ensure_2d(depth_format, data_.extent);
+ reset += combined_tx_.current().ensure_2d(color_format, data_.extent);
+ reset += combined_tx_.next().ensure_2d(color_format, data_.extent);
+ /* Two layers, one for nearest sample weight and one for weight accumulation. */
+ reset += weight_tx_.current().ensure_2d_array(weight_format, weight_extent, 2);
+ reset += weight_tx_.next().ensure_2d_array(weight_format, weight_extent, 2);
+ reset += color_accum_tx_.ensure_2d_array(color_format,
+ (data_.color_len > 0) ? data_.extent : int2(1),
+ (data_.color_len > 0) ? data_.color_len : 1);
+ reset += value_accum_tx_.ensure_2d_array(float_format,
+ (data_.value_len > 0) ? data_.extent : int2(1),
+ (data_.value_len > 0) ? data_.value_len : 1);
+
+ if (reset > 0) {
+ inst_.sampling.reset();
+ data_.use_history = 0;
+ data_.use_reprojection = 0;
+
+ /* Avoid NaN in uninitialized texture memory making history blending dangerous. */
+ color_accum_tx_.clear(float4(0.0f));
+ value_accum_tx_.clear(float4(0.0f));
+ combined_tx_.current().clear(float4(0.0f));
+ weight_tx_.current().clear(float4(0.0f));
+ depth_tx_.clear(float4(0.0f));
+ }
+ }
+}
+
+void Film::sync()
+{
+ /* We use a fragment shader for viewport because we need to output the depth. */
+ bool use_compute = (inst_.is_viewport() == false);
+
+ eShaderType shader = use_compute ? FILM_COMP : FILM_FRAG;
+
+ /* TODO(fclem): Shader variation for panoramic & scaled resolution. */
+
+ RenderBuffers &rbuffers = inst_.render_buffers;
+
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS;
+ accumulate_ps_ = DRW_pass_create("Film.Accumulate", state);
+ GPUShader *sh = inst_.shaders.static_shader_get(shader);
+ DRWShadingGroup *grp = DRW_shgroup_create(sh, accumulate_ps_);
+ DRW_shgroup_uniform_block_ref(grp, "film_buf", &data_);
+ DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &rbuffers.depth_tx);
+ DRW_shgroup_uniform_texture_ref(grp, "combined_tx", &rbuffers.combined_tx);
+ DRW_shgroup_uniform_texture_ref(grp, "normal_tx", &rbuffers.normal_tx);
+ DRW_shgroup_uniform_texture_ref(grp, "vector_tx", &rbuffers.vector_tx);
+ DRW_shgroup_uniform_texture_ref(grp, "diffuse_light_tx", &rbuffers.diffuse_light_tx);
+ DRW_shgroup_uniform_texture_ref(grp, "diffuse_color_tx", &rbuffers.diffuse_color_tx);
+ DRW_shgroup_uniform_texture_ref(grp, "specular_light_tx", &rbuffers.specular_light_tx);
+ DRW_shgroup_uniform_texture_ref(grp, "specular_color_tx", &rbuffers.specular_color_tx);
+ DRW_shgroup_uniform_texture_ref(grp, "volume_light_tx", &rbuffers.volume_light_tx);
+ DRW_shgroup_uniform_texture_ref(grp, "emission_tx", &rbuffers.emission_tx);
+ DRW_shgroup_uniform_texture_ref(grp, "environment_tx", &rbuffers.environment_tx);
+ DRW_shgroup_uniform_texture_ref(grp, "shadow_tx", &rbuffers.shadow_tx);
+ DRW_shgroup_uniform_texture_ref(grp, "ambient_occlusion_tx", &rbuffers.ambient_occlusion_tx);
+ DRW_shgroup_uniform_texture_ref(grp, "aov_color_tx", &rbuffers.aov_color_tx);
+ DRW_shgroup_uniform_texture_ref(grp, "aov_value_tx", &rbuffers.aov_value_tx);
+ /* NOTE(@fclem): 16 is the max number of sampled texture in many implementations.
+ * If we need more, we need to pack more of the similar passes in the same textures as arrays or
+ * use image binding instead. */
+ DRW_shgroup_uniform_image_ref(grp, "in_weight_img", &weight_tx_.current());
+ DRW_shgroup_uniform_image_ref(grp, "out_weight_img", &weight_tx_.next());
+ DRW_shgroup_uniform_image_ref(grp, "in_combined_img", &combined_tx_.current());
+ DRW_shgroup_uniform_image_ref(grp, "out_combined_img", &combined_tx_.next());
+ DRW_shgroup_uniform_image_ref(grp, "depth_img", &depth_tx_);
+ DRW_shgroup_uniform_image_ref(grp, "color_accum_img", &color_accum_tx_);
+ DRW_shgroup_uniform_image_ref(grp, "value_accum_img", &value_accum_tx_);
+ /* Sync with rendering passes. */
+ DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH);
+ /* Sync with rendering passes. */
+ DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS);
+ if (use_compute) {
+ int2 dispatch_size = math::divide_ceil(data_.extent, int2(FILM_GROUP_SIZE));
+ DRW_shgroup_call_compute(grp, UNPACK2(dispatch_size), 1);
+ }
+ else {
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+ }
+}
+
+void Film::end_sync()
+{
+ if (inst_.sampling.is_reset()) {
+ data_.use_history = 0;
+ }
+
+ // if (camera.changed_type) {
+ // data_.use_reprojection = false;
+ // }
+
+ aovs_info.push_update();
+
+ sync_mist();
+}
+
+float2 Film::pixel_jitter_get() const
+{
+ float2 jitter = inst_.sampling.rng_2d_get(SAMPLING_FILTER_U);
+
+ if (data_.filter_size < M_SQRT1_2 && !inst_.camera.is_panoramic()) {
+ /* For filter size less than a pixel, change sampling strategy and use a uniform disk
+ * distribution covering the filter shape. This avoids putting samples in areas without any
+ * weights. */
+ /* TODO(fclem): Importance sampling could be a better option here. */
+ jitter = Sampling::sample_disk(jitter) * data_.filter_size;
+ }
+ else {
+ /* Jitter the size of a whole pixel. */
+ jitter = jitter * 2.0f - 1.0f;
+ }
+ /* TODO(fclem): Mixed-resolution rendering: We need to offset to each of the target pixel covered
+ * by a render pixel, ideally, by choosing one randomly using another sampling dimension, or by
+ * repeating the same sample RNG sequence for each pixel offset. */
+ return jitter;
+}
+
+void Film::update_sample_table()
+{
+ data_.subpixel_offset = pixel_jitter_get();
+
+ int filter_size_ceil = ceilf(data_.filter_size);
+ float filter_size_sqr = square_f(data_.filter_size);
+
+ data_.samples_len = 0;
+ if (data_.filter_size < 0.01f) {
+ /* Disable filtering. */
+ data_.samples[0].texel = int2(0, 0);
+ data_.samples[0].weight = 1.0f;
+ data_.samples_weight_total = 1.0f;
+ data_.samples_len = 1;
+ }
+ /* NOTE: Threshold determined by hand until we don't hit the assert bellow. */
+ else if (data_.filter_size < 2.20f) {
+ /* Small filter Size. */
+ int closest_index = 0;
+ float closest_distance = FLT_MAX;
+ data_.samples_weight_total = 0.0f;
+ /* TODO(fclem): For optimization, could try Z-tile ordering. */
+ for (int y = -filter_size_ceil; y <= filter_size_ceil; y++) {
+ for (int x = -filter_size_ceil; x <= filter_size_ceil; x++) {
+ float2 pixel_offset = float2(x, y) - data_.subpixel_offset;
+ float distance_sqr = math::length_squared(pixel_offset);
+ if (distance_sqr < filter_size_sqr) {
+ if (data_.samples_len >= FILM_PRECOMP_SAMPLE_MAX) {
+ BLI_assert_msg(0, "Precomputed sample table is too small.");
+ break;
+ }
+ FilmSample &sample = data_.samples[data_.samples_len];
+ sample.texel = int2(x, y);
+ sample.weight = film_filter_weight(data_.filter_size, distance_sqr);
+ data_.samples_weight_total += sample.weight;
+
+ if (distance_sqr < closest_distance) {
+ closest_distance = distance_sqr;
+ closest_index = data_.samples_len;
+ }
+ data_.samples_len++;
+ }
+ }
+ }
+ /* Put the closest one in first position. */
+ if (closest_index != 0) {
+ SWAP(FilmSample, data_.samples[closest_index], data_.samples[0]);
+ }
+ }
+ else {
+ /* Large Filter Size. */
+ MutableSpan<FilmSample> sample_table(data_.samples, FILM_PRECOMP_SAMPLE_MAX);
+ /* To avoid hitting driver TDR and slowing rendering too much we use random sampling. */
+ /* TODO(fclem): This case needs more work. We could distribute the samples better to avoid
+ * loading the same pixel twice. */
+ data_.samples_len = sample_table.size();
+ data_.samples_weight_total = 0.0f;
+
+ int i = 0;
+ for (FilmSample &sample : sample_table) {
+ /* TODO(fclem): Own RNG. */
+ float2 random_2d = inst_.sampling.rng_2d_get(SAMPLING_FILTER_U);
+ /* This randomization makes sure we converge to the right result but also makes nearest
+ * neighbor filtering not converging rapidly. */
+ random_2d.x = (random_2d.x + i) / float(FILM_PRECOMP_SAMPLE_MAX);
+
+ float2 pixel_offset = math::floor(Sampling::sample_spiral(random_2d) * data_.filter_size);
+ sample.texel = int2(pixel_offset);
+
+ float distance_sqr = math::length_squared(pixel_offset - data_.subpixel_offset);
+ sample.weight = film_filter_weight(data_.filter_size, distance_sqr);
+ data_.samples_weight_total += sample.weight;
+ i++;
+ }
+ }
+}
+
+void Film::accumulate(const DRWView *view)
+{
+ if (inst_.is_viewport()) {
+ DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
+ GPU_framebuffer_bind(dfbl->default_fb);
+ GPU_framebuffer_viewport_set(dfbl->default_fb, UNPACK2(data_.offset), UNPACK2(data_.extent));
+ }
+
+ update_sample_table();
+
+ data_.display_only = false;
+ data_.push_update();
+
+ DRW_view_set_active(view);
+ DRW_draw_pass(accumulate_ps_);
+
+ combined_tx_.swap();
+ weight_tx_.swap();
+
+ /* Use history after first sample. */
+ if (data_.use_history == 0) {
+ data_.use_history = 1;
+ data_.use_reprojection = 1;
+ }
+}
+
+void Film::display()
+{
+ BLI_assert(inst_.is_viewport());
+
+ /* Acquire dummy render buffers for correct binding. They will not be used. */
+ inst_.render_buffers.acquire(int2(1), (void *)this);
+
+ DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
+ GPU_framebuffer_bind(dfbl->default_fb);
+ GPU_framebuffer_viewport_set(dfbl->default_fb, UNPACK2(data_.offset), UNPACK2(data_.extent));
+
+ data_.display_only = true;
+ data_.push_update();
+
+ DRW_view_set_active(nullptr);
+ DRW_draw_pass(accumulate_ps_);
+
+ inst_.render_buffers.release();
+
+ /* IMPORTANT: Do not swap! No accumulation has happened. */
+}
+
+float *Film::read_pass(eViewLayerEEVEEPassType pass_type)
+{
+
+ bool is_value = pass_is_value(pass_type);
+ Texture &accum_tx = (pass_type == EEVEE_RENDER_PASS_COMBINED) ?
+ combined_tx_.current() :
+ (pass_type == EEVEE_RENDER_PASS_Z) ?
+ depth_tx_ :
+ (is_value ? value_accum_tx_ : color_accum_tx_);
+
+ accum_tx.ensure_layer_views();
+
+ int index = pass_id_get(pass_type);
+ GPUTexture *pass_tx = accum_tx.layer_view(index);
+
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
+
+ return (float *)GPU_texture_read(pass_tx, GPU_DATA_FLOAT, 0);
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_film.hh b/source/blender/draw/engines/eevee_next/eevee_film.hh
new file mode 100644
index 00000000000..7ffbd4e45c6
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_film.hh
@@ -0,0 +1,187 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * The film class handles accumulation of samples with any distorted camera_type
+ * using a pixel filter. Inputs needs to be jittered so that the filter converges to the right
+ * result.
+ */
+
+#pragma once
+
+#include "DRW_render.h"
+
+#include "eevee_shader_shared.hh"
+
+namespace blender::eevee {
+
+class Instance;
+
+/* -------------------------------------------------------------------- */
+/** \name Film
+ * \{ */
+
+class Film {
+ public:
+ /** Stores indirection table of AOVs based on their name hash and their type. */
+ AOVsInfoDataBuf aovs_info;
+
+ private:
+ Instance &inst_;
+
+ /** Main accumulation textures containing every render-pass except depth and combined. */
+ Texture color_accum_tx_;
+ Texture value_accum_tx_;
+ /** Depth accumulation texture. Separated because using a different format. */
+ Texture depth_tx_;
+ /** Combined "Color" buffer. Double buffered to allow re-projection. */
+ SwapChain<Texture, 2> combined_tx_;
+ /** Weight buffers. Double buffered to allow updating it during accumulation. */
+ SwapChain<Texture, 2> weight_tx_;
+ /** Extent used by the render buffers when rendering the main views. */
+ int2 render_extent_ = int2(-1);
+
+ DRWPass *accumulate_ps_ = nullptr;
+
+ FilmDataBuf data_;
+
+ eViewLayerEEVEEPassType enabled_passes_ = eViewLayerEEVEEPassType(0);
+
+ public:
+ Film(Instance &inst) : inst_(inst){};
+ ~Film(){};
+
+ void init(const int2 &full_extent, const rcti *output_rect);
+
+ void sync();
+ void end_sync();
+
+ /** Accumulate the newly rendered sample contained in #RenderBuffers and blit to display. */
+ void accumulate(const DRWView *view);
+
+ /** Blit to display. No rendered sample needed. */
+ void display();
+
+ float *read_pass(eViewLayerEEVEEPassType pass_type);
+ float *read_aov(ViewLayerAOV *aov);
+
+ int2 render_extent_get() const
+ {
+ return render_extent_;
+ }
+
+ float2 pixel_jitter_get() const;
+
+ eViewLayerEEVEEPassType enabled_passes_get() const
+ {
+ return enabled_passes_;
+ }
+
+ static bool pass_is_value(eViewLayerEEVEEPassType pass_type)
+ {
+ switch (pass_type) {
+ case EEVEE_RENDER_PASS_Z:
+ case EEVEE_RENDER_PASS_MIST:
+ case EEVEE_RENDER_PASS_SHADOW:
+ case EEVEE_RENDER_PASS_AO:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /* Returns layer offset in the accumulation texture. -1 if the pass is not enabled. */
+ int pass_id_get(eViewLayerEEVEEPassType pass_type) const
+ {
+ switch (pass_type) {
+ case EEVEE_RENDER_PASS_COMBINED:
+ return data_.combined_id;
+ case EEVEE_RENDER_PASS_Z:
+ return data_.depth_id;
+ case EEVEE_RENDER_PASS_MIST:
+ return data_.mist_id;
+ case EEVEE_RENDER_PASS_NORMAL:
+ return data_.normal_id;
+ case EEVEE_RENDER_PASS_DIFFUSE_LIGHT:
+ return data_.diffuse_light_id;
+ case EEVEE_RENDER_PASS_DIFFUSE_COLOR:
+ return data_.diffuse_color_id;
+ case EEVEE_RENDER_PASS_SPECULAR_LIGHT:
+ return data_.specular_light_id;
+ case EEVEE_RENDER_PASS_SPECULAR_COLOR:
+ return data_.specular_color_id;
+ case EEVEE_RENDER_PASS_VOLUME_LIGHT:
+ return data_.volume_light_id;
+ case EEVEE_RENDER_PASS_EMIT:
+ return data_.emission_id;
+ case EEVEE_RENDER_PASS_ENVIRONMENT:
+ return data_.environment_id;
+ case EEVEE_RENDER_PASS_SHADOW:
+ return data_.shadow_id;
+ case EEVEE_RENDER_PASS_AO:
+ return data_.ambient_occlusion_id;
+ case EEVEE_RENDER_PASS_CRYPTOMATTE:
+ return -1; /* TODO */
+ case EEVEE_RENDER_PASS_VECTOR:
+ return data_.vector_id;
+ default:
+ return -1;
+ }
+ }
+
+ static const char *pass_to_render_pass_name(eViewLayerEEVEEPassType pass_type)
+ {
+ switch (pass_type) {
+ case EEVEE_RENDER_PASS_COMBINED:
+ return RE_PASSNAME_COMBINED;
+ case EEVEE_RENDER_PASS_Z:
+ return RE_PASSNAME_Z;
+ case EEVEE_RENDER_PASS_MIST:
+ return RE_PASSNAME_MIST;
+ case EEVEE_RENDER_PASS_NORMAL:
+ return RE_PASSNAME_NORMAL;
+ case EEVEE_RENDER_PASS_DIFFUSE_LIGHT:
+ return RE_PASSNAME_DIFFUSE_DIRECT;
+ case EEVEE_RENDER_PASS_DIFFUSE_COLOR:
+ return RE_PASSNAME_DIFFUSE_COLOR;
+ case EEVEE_RENDER_PASS_SPECULAR_LIGHT:
+ return RE_PASSNAME_GLOSSY_DIRECT;
+ case EEVEE_RENDER_PASS_SPECULAR_COLOR:
+ return RE_PASSNAME_GLOSSY_COLOR;
+ case EEVEE_RENDER_PASS_VOLUME_LIGHT:
+ return RE_PASSNAME_VOLUME_LIGHT;
+ case EEVEE_RENDER_PASS_EMIT:
+ return RE_PASSNAME_EMIT;
+ case EEVEE_RENDER_PASS_ENVIRONMENT:
+ return RE_PASSNAME_ENVIRONMENT;
+ case EEVEE_RENDER_PASS_SHADOW:
+ return RE_PASSNAME_SHADOW;
+ case EEVEE_RENDER_PASS_AO:
+ return RE_PASSNAME_AO;
+ case EEVEE_RENDER_PASS_CRYPTOMATTE:
+ BLI_assert_msg(0, "Cryptomatte is not implemented yet.");
+ return ""; /* TODO */
+ case EEVEE_RENDER_PASS_VECTOR:
+ return RE_PASSNAME_VECTOR;
+ default:
+ BLI_assert(0);
+ return "";
+ }
+ }
+
+ private:
+ void init_aovs();
+ void sync_mist();
+
+ /**
+ * Precompute sample weights if they are uniform across the whole film extent.
+ */
+ void update_sample_table();
+};
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc
index 606630bcdef..5fa5628515f 100644
--- a/source/blender/draw/engines/eevee_next/eevee_instance.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc
@@ -17,6 +17,7 @@
#include "DNA_ID.h"
#include "DNA_lightprobe_types.h"
#include "DNA_modifier_types.h"
+#include "RE_pipeline.h"
#include "eevee_instance.hh"
@@ -43,7 +44,7 @@ void Instance::init(const int2 &output_res,
const View3D *v3d_,
const RegionView3D *rv3d_)
{
- UNUSED_VARS(light_probe_, output_rect);
+ UNUSED_VARS(light_probe_);
render = render_;
depsgraph = depsgraph_;
camera_orig_object = camera_object_;
@@ -56,7 +57,10 @@ void Instance::init(const int2 &output_res,
update_eval_members();
- main_view.init(output_res);
+ sampling.init(scene);
+ camera.init();
+ film.init(output_res, output_rect);
+ main_view.init();
}
void Instance::set_time(float time)
@@ -90,9 +94,14 @@ void Instance::begin_sync()
materials.begin_sync();
velocity.begin_sync();
+ gpencil_engine_enabled = false;
+
+ render_buffers.sync();
pipelines.sync();
main_view.sync();
world.sync();
+ camera.sync();
+ film.sync();
}
void Instance::object_sync(Object *ob)
@@ -146,13 +155,36 @@ void Instance::object_sync(Object *ob)
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_);
+ inst.object_sync(ob);
+}
+
void Instance::end_sync()
{
velocity.end_sync();
+ sampling.end_sync();
+ film.end_sync();
}
void Instance::render_sync()
{
+ DRW_cache_restart();
+
+ begin_sync();
+ DRW_render_object_iter(this, render, depsgraph, object_sync_render);
+ 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();
}
/** \} */
@@ -167,6 +199,18 @@ void Instance::render_sync()
**/
void Instance::render_sample()
{
+ if (sampling.finished()) {
+ film.display();
+ return;
+ }
+
+ /* Motion blur may need to do re-sync after a certain number of sample. */
+ if (!is_viewport() && sampling.do_render_sync()) {
+ render_sync();
+ }
+
+ sampling.step();
+
main_view.render();
}
@@ -178,7 +222,36 @@ void Instance::render_sample()
void Instance::render_frame(RenderLayer *render_layer, const char *view_name)
{
- UNUSED_VARS(render_layer, view_name);
+ while (!sampling.finished()) {
+ this->render_sample();
+ /* TODO(fclem) print progression. */
+ }
+
+ /* Read Results. */
+ eViewLayerEEVEEPassType pass_bits = film.enabled_passes_get();
+ for (auto i : IndexRange(EEVEE_RENDER_PASS_MAX_BIT)) {
+ eViewLayerEEVEEPassType pass_type = eViewLayerEEVEEPassType(pass_bits & (1 << i));
+ if (pass_type == 0) {
+ continue;
+ }
+
+ const char *pass_name = Film::pass_to_render_pass_name(pass_type);
+ RenderPass *rp = RE_pass_find_by_name(render_layer, pass_name, view_name);
+ if (rp) {
+ float *result = film.read_pass(pass_type);
+ if (result) {
+ std::cout << "read " << pass_name << std::endl;
+ BLI_mutex_lock(&render->update_render_passes_mutex);
+ /* WORKAROUND: We use texture read to avoid using a framebuffer to get the render result.
+ * However, on some implementation, we need a buffer with a few extra bytes for the read to
+ * happen correctly (see GLTexture::read()). So we need a custom memory allocation. */
+ /* Avoid memcpy(), replace the pointer directly. */
+ MEM_SAFE_FREE(rp->rect);
+ rp->rect = result;
+ BLI_mutex_unlock(&render->update_render_passes_mutex);
+ }
+ }
+ }
}
void Instance::draw_viewport(DefaultFramebufferList *dfbl)
@@ -187,6 +260,10 @@ void Instance::draw_viewport(DefaultFramebufferList *dfbl)
render_sample();
velocity.step_swap();
+ if (!sampling.finished_viewport()) {
+ DRW_viewport_request_redraw();
+ }
+
if (materials.queued_shaders_count > 0) {
std::stringstream ss;
ss << "Compiling Shaders " << materials.queued_shaders_count;
diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh
index 84be59fc5f0..f47d4f20363 100644
--- a/source/blender/draw/engines/eevee_next/eevee_instance.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh
@@ -16,8 +16,11 @@
#include "DRW_render.h"
#include "eevee_camera.hh"
+#include "eevee_film.hh"
#include "eevee_material.hh"
#include "eevee_pipeline.hh"
+#include "eevee_renderbuffers.hh"
+#include "eevee_sampling.hh"
#include "eevee_shader.hh"
#include "eevee_sync.hh"
#include "eevee_view.hh"
@@ -38,7 +41,10 @@ class Instance {
MaterialModule materials;
PipelineModule pipelines;
VelocityModule velocity;
+ Sampling sampling;
Camera camera;
+ Film film;
+ RenderBuffers render_buffers;
MainView main_view;
World world;
@@ -57,6 +63,9 @@ class Instance {
const View3D *v3d;
const RegionView3D *rv3d;
+ /** True if the grease pencil engine might be running. */
+ bool gpencil_engine_enabled;
+
/* Info string displayed at the top of the render / viewport. */
std::string info = "";
@@ -67,7 +76,10 @@ class Instance {
materials(*this),
pipelines(*this),
velocity(*this),
+ sampling(*this),
camera(*this),
+ film(*this),
+ render_buffers(*this),
main_view(*this),
world(*this){};
~Instance(){};
@@ -92,12 +104,17 @@ class Instance {
void draw_viewport(DefaultFramebufferList *dfbl);
- bool is_viewport(void)
+ bool is_viewport() const
+ {
+ return render == nullptr;
+ }
+
+ bool overlays_enabled() const
{
- return !DRW_state_is_scene_render();
+ return (!v3d) || ((v3d->flag & V3D_HIDE_OVERLAYS) == 0);
}
- bool use_scene_lights(void) const
+ bool use_scene_lights() const
{
return (!v3d) ||
((v3d->shading.type == OB_MATERIAL) &&
@@ -107,7 +124,7 @@ class Instance {
}
/* Light the scene using the selected HDRI in the viewport shading pop-over. */
- bool use_studio_light(void) const
+ bool use_studio_light() const
{
return (v3d) && (((v3d->shading.type == OB_MATERIAL) &&
((v3d->shading.flag & V3D_SHADING_SCENE_WORLD) == 0)) ||
@@ -116,6 +133,10 @@ class Instance {
}
private:
+ static void object_sync_render(void *instance_,
+ Object *ob,
+ RenderEngine *engine,
+ Depsgraph *depsgraph);
void render_sample();
void mesh_sync(Object *ob, ObjectHandle &ob_handle);
diff --git a/source/blender/draw/engines/eevee_next/eevee_material.cc b/source/blender/draw/engines/eevee_next/eevee_material.cc
index 1676c89d679..b3161a67092 100644
--- a/source/blender/draw/engines/eevee_next/eevee_material.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_material.cc
@@ -195,7 +195,7 @@ MaterialPass MaterialModule::material_pass_get(::Material *blender_mat,
BLI_assert(GPU_material_status(matpass.gpumat) == GPU_MAT_SUCCESS);
if (GPU_material_recalc_flag_get(matpass.gpumat)) {
- // inst_.sampling.reset();
+ inst_.sampling.reset();
}
if ((pipeline_type == MAT_PIPE_DEFERRED) &&
diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc
index 33853eba06c..7b3cfbf5899 100644
--- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc
@@ -24,6 +24,8 @@ namespace blender::eevee {
void WorldPipeline::sync(GPUMaterial *gpumat)
{
+ RenderBuffers &rbufs = inst_.render_buffers;
+
DRWState state = DRW_STATE_WRITE_COLOR;
world_ps_ = DRW_pass_create("World", state);
@@ -34,6 +36,19 @@ void WorldPipeline::sync(GPUMaterial *gpumat)
DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, world_ps_);
DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.pipelines.utility_tx);
DRW_shgroup_call_obmat(grp, DRW_cache_fullscreen_quad_get(), camera_mat.ptr());
+ /* AOVs. */
+ DRW_shgroup_uniform_image_ref(grp, "aov_color_img", &rbufs.aov_color_tx);
+ DRW_shgroup_uniform_image_ref(grp, "aov_value_img", &rbufs.aov_value_tx);
+ DRW_shgroup_storage_block_ref(grp, "aov_buf", &inst_.film.aovs_info);
+ /* RenderPasses. Cleared by background (even if bad practice). */
+ DRW_shgroup_uniform_image_ref(grp, "rp_normal_img", &rbufs.normal_tx);
+ DRW_shgroup_uniform_image_ref(grp, "rp_diffuse_light_img", &rbufs.diffuse_light_tx);
+ DRW_shgroup_uniform_image_ref(grp, "rp_diffuse_color_img", &rbufs.diffuse_color_tx);
+ DRW_shgroup_uniform_image_ref(grp, "rp_specular_light_img", &rbufs.specular_light_tx);
+ DRW_shgroup_uniform_image_ref(grp, "rp_specular_color_img", &rbufs.specular_color_tx);
+ DRW_shgroup_uniform_image_ref(grp, "rp_emission_img", &rbufs.emission_tx);
+ /* To allow opaque pass rendering over it. */
+ DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS);
}
void WorldPipeline::render()
@@ -83,6 +98,7 @@ void ForwardPipeline::sync()
DRWShadingGroup *ForwardPipeline::material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat)
{
+ RenderBuffers &rbufs = inst_.render_buffers;
DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? opaque_culled_ps_ : opaque_ps_;
// LightModule &lights = inst_.lights;
// LightProbeModule &lightprobes = inst_.lightprobes;
@@ -97,6 +113,18 @@ DRWShadingGroup *ForwardPipeline::material_opaque_add(::Material *blender_mat, G
// 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_.pipelines.utility_tx);
+ /* AOVs. */
+ DRW_shgroup_uniform_image_ref(grp, "aov_color_img", &rbufs.aov_color_tx);
+ DRW_shgroup_uniform_image_ref(grp, "aov_value_img", &rbufs.aov_value_tx);
+ DRW_shgroup_storage_block_ref(grp, "aov_buf", &inst_.film.aovs_info);
+ /* RenderPasses. */
+ DRW_shgroup_uniform_image_ref(grp, "rp_normal_img", &rbufs.normal_tx);
+ DRW_shgroup_uniform_image_ref(grp, "rp_diffuse_light_img", &rbufs.diffuse_light_tx);
+ DRW_shgroup_uniform_image_ref(grp, "rp_diffuse_color_img", &rbufs.diffuse_color_tx);
+ DRW_shgroup_uniform_image_ref(grp, "rp_specular_light_img", &rbufs.specular_light_tx);
+ DRW_shgroup_uniform_image_ref(grp, "rp_specular_color_img", &rbufs.specular_color_tx);
+ DRW_shgroup_uniform_image_ref(grp, "rp_emission_img", &rbufs.emission_tx);
+
/* TODO(fclem): Make this only needed if material uses it ... somehow. */
// if (true) {
// DRW_shgroup_uniform_texture_ref(
diff --git a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc
new file mode 100644
index 00000000000..6e30ba989df
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * 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 {
+
+void RenderBuffers::sync()
+{
+ depth_tx.sync();
+ combined_tx.sync();
+
+ normal_tx.sync();
+ vector_tx.sync();
+ diffuse_light_tx.sync();
+ diffuse_color_tx.sync();
+ specular_light_tx.sync();
+ specular_color_tx.sync();
+ volume_light_tx.sync();
+ emission_tx.sync();
+ environment_tx.sync();
+ shadow_tx.sync();
+ ambient_occlusion_tx.sync();
+}
+
+void RenderBuffers::acquire(int2 extent, void *owner)
+{
+ auto pass_extent = [&](eViewLayerEEVEEPassType pass_bit) -> int2 {
+ /* Use dummy texture for disabled passes. Allows correct bindings. */
+ return (inst_.film.enabled_passes_get() & pass_bit) ? extent : int2(1);
+ };
+
+ eGPUTextureFormat color_format = GPU_RGBA16F;
+ eGPUTextureFormat float_format = GPU_R16F;
+
+ /* Depth and combined are always needed. */
+ depth_tx.acquire(extent, GPU_DEPTH24_STENCIL8, owner);
+ combined_tx.acquire(extent, color_format, owner);
+
+ normal_tx.acquire(pass_extent(EEVEE_RENDER_PASS_NORMAL), color_format, owner);
+ vector_tx.acquire(pass_extent(EEVEE_RENDER_PASS_VECTOR), color_format, owner);
+ diffuse_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_LIGHT), color_format, owner);
+ diffuse_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_COLOR), color_format, owner);
+ specular_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SPECULAR_LIGHT), color_format, owner);
+ specular_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SPECULAR_COLOR), color_format, owner);
+ volume_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_VOLUME_LIGHT), color_format, owner);
+ emission_tx.acquire(pass_extent(EEVEE_RENDER_PASS_EMIT), color_format, owner);
+ environment_tx.acquire(pass_extent(EEVEE_RENDER_PASS_ENVIRONMENT), color_format, owner);
+ shadow_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SHADOW), float_format, owner);
+ ambient_occlusion_tx.acquire(pass_extent(EEVEE_RENDER_PASS_AO), float_format, owner);
+
+ const AOVsInfoData &aovs = inst_.film.aovs_info;
+ aov_color_tx.ensure_2d_array(
+ color_format, (aovs.color_len > 0) ? extent : int2(1), max_ii(1, aovs.color_len));
+ aov_value_tx.ensure_2d_array(
+ float_format, (aovs.value_len > 0) ? extent : int2(1), max_ii(1, aovs.value_len));
+}
+
+void RenderBuffers::release()
+{
+ depth_tx.release();
+ combined_tx.release();
+
+ normal_tx.release();
+ vector_tx.release();
+ diffuse_light_tx.release();
+ diffuse_color_tx.release();
+ specular_light_tx.release();
+ specular_color_tx.release();
+ volume_light_tx.release();
+ emission_tx.release();
+ environment_tx.release();
+ shadow_tx.release();
+ ambient_occlusion_tx.release();
+}
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh
new file mode 100644
index 00000000000..8c91fed2f0f
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Render buffers are textures that are filled during a view rendering.
+ * Their content is then added to the accumulation buffers of the film class.
+ * They are short lived and can be reused when doing multi view rendering.
+ */
+
+#pragma once
+
+#include "DRW_render.h"
+
+#include "eevee_shader_shared.hh"
+
+namespace blender::eevee {
+
+class Instance;
+
+class RenderBuffers {
+ public:
+ TextureFromPool depth_tx;
+ TextureFromPool combined_tx;
+
+ // TextureFromPool mist_tx; /* Derived from depth_tx during accumulation. */
+ TextureFromPool normal_tx;
+ TextureFromPool vector_tx;
+ TextureFromPool diffuse_light_tx;
+ TextureFromPool diffuse_color_tx;
+ TextureFromPool specular_light_tx;
+ TextureFromPool specular_color_tx;
+ TextureFromPool volume_light_tx;
+ TextureFromPool emission_tx;
+ TextureFromPool environment_tx;
+ TextureFromPool shadow_tx;
+ TextureFromPool ambient_occlusion_tx;
+ // TextureFromPool cryptomatte_tx; /* TODO */
+ /* TODO(fclem): Use texture from pool once they support texture array. */
+ Texture aov_color_tx;
+ Texture aov_value_tx;
+
+ private:
+ Instance &inst_;
+
+ public:
+ RenderBuffers(Instance &inst) : inst_(inst){};
+
+ void sync();
+ /* Acquires (also ensures) the render buffer before rendering to them. */
+ void acquire(int2 extent, void *owner);
+ void release();
+};
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.cc b/source/blender/draw/engines/eevee_next/eevee_sampling.cc
new file mode 100644
index 00000000000..493fff53919
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_sampling.cc
@@ -0,0 +1,264 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Random number generator, contains persistent state and sample count logic.
+ */
+
+#include "BLI_rand.h"
+
+#include "eevee_instance.hh"
+#include "eevee_sampling.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name Sampling
+ * \{ */
+
+void Sampling::init(const Scene *scene)
+{
+ sample_count_ = inst_.is_viewport() ? scene->eevee.taa_samples : scene->eevee.taa_render_samples;
+
+ if (sample_count_ == 0) {
+ BLI_assert(inst_.is_viewport());
+ sample_count_ = infinite_sample_count_;
+ }
+
+ motion_blur_steps_ = !inst_.is_viewport() ? 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_ = sampling_web_ring_count_get(dof_web_density_, sample_count_);
+ }
+ dof_sample_count_ = sampling_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_;
+}
+
+void Sampling::end_sync()
+{
+ if (reset_) {
+ viewport_sample_ = 0;
+ if (inst_.is_viewport()) {
+ interactive_mode_ = true;
+ }
+ }
+
+ if (interactive_mode_) {
+ int interactive_sample_count = min_ii(interactive_sample_max_, sample_count_);
+
+ if (viewport_sample_ < interactive_sample_count) {
+ /* Loop over the same starting samples. */
+ sample_ = sample_ % interactive_sample_count;
+ }
+ else {
+ /* Break out of the loop and resume normal pattern. */
+ sample_ = interactive_sample_count;
+ interactive_mode_ = false;
+ }
+ }
+}
+
+void Sampling::step()
+{
+ {
+ /* TODO(fclem) we could use some persistent states to speedup the computation. */
+ double2 r, offset = {0, 0};
+ /* Using 2,3 primes as per UE4 Temporal AA presentation.
+ * advances.realtimerendering.com/s2014/epic/TemporalAA.pptx (slide 14) */
+ uint2 primes = {2, 3};
+ BLI_halton_2d(primes, offset, sample_ + 1, r);
+ /* WORKAROUND: We offset the distribution to make the first sample (0,0). This way, we are
+ * assured that at least one of the samples inside the TAA rotation will match the one from the
+ * draw manager. This makes sure overlays are correctly composited in static scene. */
+ data_.dimensions[SAMPLING_FILTER_U] = fractf(r[0] + (1.0 / 2.0));
+ data_.dimensions[SAMPLING_FILTER_V] = fractf(r[1] + (2.0 / 3.0));
+ /* TODO de-correlate. */
+ data_.dimensions[SAMPLING_TIME] = r[0];
+ data_.dimensions[SAMPLING_CLOSURE] = r[1];
+ data_.dimensions[SAMPLING_RAYTRACE_X] = r[0];
+ }
+ {
+ double2 r, offset = {0, 0};
+ uint2 primes = {5, 7};
+ BLI_halton_2d(primes, offset, sample_ + 1, r);
+ data_.dimensions[SAMPLING_LENS_U] = r[0];
+ data_.dimensions[SAMPLING_LENS_V] = r[1];
+ /* TODO de-correlate. */
+ data_.dimensions[SAMPLING_LIGHTPROBE] = r[0];
+ data_.dimensions[SAMPLING_TRANSPARENCY] = r[1];
+ }
+ {
+ /* Using leaped Halton sequence so we can reused the same primes as lens. */
+ double3 r, offset = {0, 0, 0};
+ uint64_t leap = 11;
+ uint3 primes = {5, 4, 7};
+ BLI_halton_3d(primes, offset, sample_ * leap, r);
+ data_.dimensions[SAMPLING_SHADOW_U] = r[0];
+ data_.dimensions[SAMPLING_SHADOW_V] = r[1];
+ data_.dimensions[SAMPLING_SHADOW_W] = r[2];
+ /* TODO de-correlate. */
+ data_.dimensions[SAMPLING_RAYTRACE_U] = r[0];
+ data_.dimensions[SAMPLING_RAYTRACE_V] = r[1];
+ data_.dimensions[SAMPLING_RAYTRACE_W] = r[2];
+ }
+ {
+ /* Using leaped Halton sequence so we can reused the same primes. */
+ double2 r, offset = {0, 0};
+ uint64_t leap = 5;
+ uint2 primes = {2, 3};
+ BLI_halton_2d(primes, offset, sample_ * leap, r);
+ data_.dimensions[SAMPLING_SHADOW_X] = r[0];
+ data_.dimensions[SAMPLING_SHADOW_Y] = r[1];
+ /* TODO de-correlate. */
+ data_.dimensions[SAMPLING_SSS_U] = r[0];
+ data_.dimensions[SAMPLING_SSS_V] = r[1];
+ }
+
+ data_.push_update();
+
+ viewport_sample_++;
+ sample_++;
+
+ std::cout << sample_ << " " << viewport_sample_ << std::endl;
+
+ reset_ = false;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sampling patterns
+ * \{ */
+
+float3 Sampling::sample_ball(const float3 &rand)
+{
+ float3 sample;
+ sample.z = rand.x * 2.0f - 1.0f; /* cos theta */
+
+ float r = sqrtf(fmaxf(0.0f, 1.0f - square_f(sample.z))); /* sin theta */
+
+ float omega = rand.y * 2.0f * M_PI;
+ sample.x = r * cosf(omega);
+ sample.y = r * sinf(omega);
+
+ sample *= sqrtf(sqrtf(rand.z));
+ return sample;
+}
+
+float2 Sampling::sample_disk(const float2 &rand)
+{
+ float omega = rand.y * 2.0f * M_PI;
+ return sqrtf(rand.x) * float2(cosf(omega), sinf(omega));
+}
+
+float2 Sampling::sample_spiral(const float2 &rand)
+{
+ /* Fibonacci spiral. */
+ float omega = M_PI * (1.0f + sqrtf(5.0f)) * rand.x;
+ float r = sqrtf(rand.x);
+ /* Random rotation. */
+ omega += rand.y * 2.0f * M_PI;
+ return r * float2(cosf(omega), sinf(omega));
+}
+
+void Sampling::dof_disk_sample_get(float *r_radius, float *r_theta) const
+{
+ 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;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sampling patterns
+ * \{ */
+
+/* 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. */
+void Sampling::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. */
+void Sampling::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;
+ }
+ }
+ }
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.hh b/source/blender/draw/engines/eevee_next/eevee_sampling.hh
new file mode 100644
index 00000000000..d956c61f2b2
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_sampling.hh
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Random number generator, contains persistent state and sample count logic.
+ */
+
+#pragma once
+
+#include "BKE_colortools.h"
+#include "BLI_system.h"
+#include "BLI_vector.hh"
+#include "DNA_scene_types.h"
+#include "DRW_render.h"
+
+#include "eevee_shader_shared.hh"
+
+namespace blender::eevee {
+
+class Instance;
+
+class Sampling {
+ private:
+ Instance &inst_;
+
+ /* 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;
+ /* During interactive rendering, loop over the first few samples. */
+ constexpr static uint64_t interactive_sample_max_ = 8;
+
+ /** 0 based current sample. */
+ uint64_t sample_ = 0;
+ /** 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;
+ /** Increases if the view and the scene is static. */
+ int64_t viewport_sample_ = 0;
+ /** Tag to reset sampling for the next sample. */
+ bool reset_ = false;
+ /**
+ * Switch between interactive and static accumulation.
+ * In interactive mode, image stability is prioritized over quality.
+ */
+ bool interactive_mode_ = false;
+
+ SamplingDataBuf data_;
+
+ public:
+ Sampling(Instance &inst) : inst_(inst){};
+ ~Sampling(){};
+
+ void init(const Scene *scene);
+ void end_sync();
+ void step();
+
+ /* 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()
+ {
+ reset_ = true;
+ }
+
+ /* Viewport Only: true if an update happened in the scene and accumulation needs reset. */
+ bool is_reset() const
+ {
+ return reset_;
+ }
+
+ void bind_resources(DRWShadingGroup *grp)
+ {
+ DRW_shgroup_storage_block_ref(grp, "sampling_buf", &data_);
+ }
+
+ /* Returns a pseudo random number in [0..1] range. Each dimension are de-correlated. */
+ float rng_get(eSamplingDimension dimension) const
+ {
+ return data_.dimensions[dimension];
+ }
+
+ /* Returns a pseudo random number in [0..1] range. Each dimension are de-correlated. */
+ float2 rng_2d_get(eSamplingDimension starting_dimension) const
+ {
+ return *reinterpret_cast<const float2 *>(&data_.dimensions[starting_dimension]);
+ }
+
+ /* Returns a pseudo random number in [0..1] range. Each dimension are de-correlated. */
+ float3 rng_3d_get(eSamplingDimension starting_dimension) const
+ {
+ return *reinterpret_cast<const float3 *>(&data_.dimensions[starting_dimension]);
+ }
+
+ /* Returns true if rendering has finished. */
+ bool finished() const
+ {
+ return (sample_ >= sample_count_ - 1);
+ }
+
+ /* Returns true if viewport smoothing and sampling has finished. */
+ bool finished_viewport() const
+ {
+ return finished() && (viewport_sample_ >= interactive_sample_max_);
+ }
+
+ /* Return true if we are starting a new motion blur step. We need to run sync again since
+ * depsgraph was updated by MotionBlur::step(). */
+ bool do_render_sync() const
+ {
+ return ((sample_ % (sample_count_ / motion_blur_steps_)) == 0);
+ }
+
+ /**
+ * 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.
+ * \a rand is 3 random float in the [0..1] range.
+ * Returns point in a ball of radius 1 and centered on the origin.
+ */
+ static float3 sample_ball(const float3 &rand);
+
+ /**
+ * Uniform disc distribution.
+ * \a rand is 2 random float in the [0..1] range.
+ * Returns point in a disk of radius 1 and centered on the origin.
+ */
+ static float2 sample_disk(const float2 &rand);
+
+ /**
+ * Uniform disc distribution using fibonacci spiral sampling.
+ * \a rand is 2 random float in the [0..1] range.
+ * Returns point in a disk of radius 1 and centered on the origin.
+ */
+ static float2 sample_spiral(const float2 &rand);
+
+ /**
+ * Special RNG for depth of field.
+ * Returns \a radius and \a theta angle offset to apply to the web sampling pattern.
+ */
+ void dof_disk_sample_get(float *r_radius, float *r_theta) const;
+
+ /**
+ * Returns sample count inside the jittered depth of field web pattern.
+ */
+ uint64_t dof_ring_count_get() const
+ {
+ return dof_ring_count_;
+ }
+
+ /**
+ * Returns sample count inside the jittered depth of field web pattern.
+ */
+ uint64_t dof_sample_count_get() const
+ {
+ return dof_sample_count_;
+ }
+
+ /* Cumulative Distribution Function Utils. */
+ static void cdf_from_curvemapping(const CurveMapping &curve, Vector<float> &cdf);
+ static void cdf_invert(Vector<float> &cdf, Vector<float> &inverted_cdf);
+};
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc
index 09aa97e49e9..f5d4af2914e 100644
--- a/source/blender/draw/engines/eevee_next/eevee_shader.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc
@@ -78,6 +78,10 @@ ShaderModule::~ShaderModule()
const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_type)
{
switch (shader_type) {
+ case FILM_FRAG:
+ return "eevee_film_frag";
+ case FILM_COMP:
+ return "eevee_film_comp";
case VELOCITY_RESOLVE:
return "eevee_velocity_resolve";
/* To avoid compiler warning about missing case. */
@@ -161,7 +165,6 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
}
}
info.vertex_inputs_.clear();
- info.additional_info("draw_curves_infos");
break;
case MAT_GEOM_WORLD:
/**
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh
index 0f42e880a10..7a0867820af 100644
--- a/source/blender/draw/engines/eevee_next/eevee_shader.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh
@@ -26,7 +26,10 @@ namespace blender::eevee {
/* Keep alphabetical order and clean prefix. */
enum eShaderType {
- VELOCITY_RESOLVE = 0,
+ FILM_FRAG = 0,
+ FILM_COMP,
+
+ VELOCITY_RESOLVE,
MAX_SHADER_TYPE,
};
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh
index eb409f076f3..4168171ab07 100644
--- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh
@@ -12,7 +12,7 @@
# include "BLI_memory_utils.hh"
# include "DRW_gpu_wrapper.hh"
-// # include "eevee_defines.hh"
+# include "eevee_defines.hh"
# include "GPU_shader_shared.h"
@@ -28,6 +28,63 @@ using draw::TextureFromPool;
#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.
+ * Also note that it needs to be multiple of 4.
+ */
+#define SAMPLING_DIMENSION_COUNT 20
+
+/* NOTE(fclem): Needs to be used in StorageBuffer because of arrays of scalar. */
+struct SamplingData {
+ /** Array containing random values from Low Discrepency Sequence in [0..1) range. */
+ float 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 sampling_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 sampling_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
* \{ */
@@ -65,15 +122,137 @@ struct CameraData {
/** Clipping distances. */
float clip_near;
float clip_far;
- /** Film pixel filter radius. */
- float filter_size;
eCameraType type;
+
+ int _pad0;
};
BLI_STATIC_ASSERT_ALIGN(CameraData, 16)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Film
+ * \{ */
+
+#define FILM_PRECOMP_SAMPLE_MAX 16
+
+struct FilmSample {
+ int2 texel;
+ float weight;
+ /** Used for accumulation. */
+ float weight_sum_inv;
+};
+BLI_STATIC_ASSERT_ALIGN(FilmSample, 16)
+
+struct FilmData {
+ /** Size of the film in pixels. */
+ int2 extent;
+ /** Offset of the film in the full-res frame, in pixels. */
+ int2 offset;
+ /** Subpixel offset applied to the window matrix.
+ * NOTE: In final film pixel unit.
+ * NOTE: Positive values makes the view translate in the negative axes direction.
+ * NOTE: The origin is the center of the lower left film pixel of the area covered by a render
+ * pixel if using scaled resolution rendering.
+ */
+ float2 subpixel_offset;
+ /** Is true if history is valid and can be sampled. Bypass history to resets accumulation. */
+ bool1 use_history;
+ /** Is true if combined buffer is valid and can be re-projected to reduce variance. */
+ bool1 use_reprojection;
+ /** Is true if accumulation of non-filtered passes is needed. */
+ bool1 has_data;
+ /** Is true if accumulation of filtered passes is needed. */
+ bool1 any_render_pass_1;
+ bool1 any_render_pass_2;
+ int _pad0, _pad1;
+ /** Output counts per type. */
+ int color_len, value_len;
+ /** Index in color_accum_img or value_accum_img of each pass. -1 if pass is not enabled. */
+ int mist_id;
+ int normal_id;
+ int vector_id;
+ int diffuse_light_id;
+ int diffuse_color_id;
+ int specular_light_id;
+ int specular_color_id;
+ int volume_light_id;
+ int emission_id;
+ int environment_id;
+ int shadow_id;
+ int ambient_occlusion_id;
+ /** Not indexed but still not -1 if enabled. */
+ int depth_id;
+ int combined_id;
+ /** Id of the render-pass to be displayed. -1 for combined. */
+ int display_id;
+ /** True if the render-pass to be displayed is from the value accum buffer. */
+ bool1 display_is_value;
+ /** True if we bypass the accumulation and directly output the accumulation buffer. */
+ bool1 display_only;
+ /** Start of AOVs and number of aov. */
+ int aov_color_id, aov_color_len;
+ int aov_value_id, aov_value_len;
+ /** Settings to render mist pass */
+ float mist_scale, mist_bias, mist_exponent;
+ /** Scene exposure used for better noise reduction. */
+ float exposure;
+ /** Scaling factor for scaled resolution rendering. */
+ int scaling_factor;
+ /** Film pixel filter radius. */
+ float filter_size;
+ /** Precomputed samples. First in the table is the closest one. The rest is unordered. */
+ int samples_len;
+ /** Sum of the weights of all samples in the sample table. */
+ float samples_weight_total;
+ FilmSample samples[FILM_PRECOMP_SAMPLE_MAX];
+};
+BLI_STATIC_ASSERT_ALIGN(FilmData, 16)
+
+static inline float film_filter_weight(float filter_size, float sample_distance_sqr)
+{
+#if 1 /* Faster */
+ /* Gaussian fitted to Blackman-Harris. */
+ float r = sample_distance_sqr / (filter_size * filter_size);
+ const float sigma = 0.284;
+ const float fac = -0.5 / (sigma * sigma);
+ float weight = expf(fac * r);
+#else
+ /* Blackman-Harris filter. */
+ float r = M_2PI * saturate(0.5 + sqrtf(sample_distance_sqr) / (2.0 * filter_size));
+ float weight = 0.35875 - 0.48829 * cosf(r) + 0.14128 * cosf(2.0 * r) - 0.01168 * cosf(3.0 * r);
+#endif
+ return weight;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Arbitrary Output Variables
+ * \{ */
+
+/* Theoretical max is 128 as we are using texture array and VRAM usage.
+ * However, the output_aov() function perform a linear search inside all the hashes.
+ * If we find a way to avoid this we could bump this number up. */
+#define AOV_MAX 16
+
+/* NOTE(fclem): Needs to be used in StorageBuffer because of arrays of scalar. */
+struct AOVsInfoData {
+ uint hash_value[AOV_MAX];
+ uint hash_color[AOV_MAX];
+ /* Length of used data. */
+ uint color_len;
+ uint value_len;
+ /** Id of the AOV to be displayed (from the start of the AOV array). -1 for combined. */
+ int display_id;
+ /** True if the AOV to be displayed is from the value accum buffer. */
+ bool1 display_is_value;
+};
+BLI_STATIC_ASSERT_ALIGN(AOVsInfoData, 16)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name VelocityModule
* \{ */
@@ -178,10 +357,13 @@ float4 utility_tx_sample(sampler2DArray util_tx, float2 uv, float layer)
#ifdef __cplusplus
+using AOVsInfoDataBuf = draw::StorageBuffer<AOVsInfoData>;
using CameraDataBuf = draw::UniformBuffer<CameraData>;
+using FilmDataBuf = draw::UniformBuffer<FilmData>;
+using SamplingDataBuf = draw::StorageBuffer<SamplingData>;
+using VelocityGeometryBuf = draw::StorageArrayBuffer<float4, 16, true>;
using VelocityIndexBuf = draw::StorageArrayBuffer<VelocityIndex, 16>;
using VelocityObjectBuf = draw::StorageArrayBuffer<float4x4, 16>;
-using VelocityGeometryBuf = draw::StorageArrayBuffer<float4, 16, true>;
} // namespace blender::eevee
#endif
diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.cc b/source/blender/draw/engines/eevee_next/eevee_sync.cc
index 42af251d770..e2d4b0ac1c2 100644
--- a/source/blender/draw/engines/eevee_next/eevee_sync.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_sync.cc
@@ -47,7 +47,7 @@ ObjectHandle &SyncModule::sync_object(Object *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();
+ inst_.sampling.reset();
UNUSED_VARS(inst_);
}
@@ -63,7 +63,7 @@ WorldHandle &SyncModule::sync_world(::World *world)
const int recalc_flags = ID_RECALC_ALL;
if ((eevee_dd.recalc & recalc_flags) != 0) {
- // inst_.sampling.reset();
+ inst_.sampling.reset();
}
return eevee_dd;
}
@@ -253,7 +253,10 @@ static void gpencil_stroke_sync(bGPDlayer *UNUSED(gpl),
void SyncModule::sync_gpencil(Object *ob, ObjectHandle &ob_handle)
{
/* TODO(fclem): Waiting for a user option to use the render engine instead of gpencil engine. */
- return;
+ if (true) {
+ inst_.gpencil_engine_enabled = true;
+ return;
+ }
gpIterData iter(inst_, ob, ob_handle);
@@ -280,7 +283,12 @@ static void shgroup_curves_call(MaterialPass &matpass,
if (matpass.shgrp == nullptr) {
return;
}
- DRW_shgroup_hair_create_sub(ob, part_sys, modifier_data, matpass.shgrp, matpass.gpumat);
+ if (part_sys != nullptr) {
+ DRW_shgroup_hair_create_sub(ob, part_sys, modifier_data, matpass.shgrp, matpass.gpumat);
+ }
+ else {
+ DRW_shgroup_curves_create_sub(ob, matpass.shgrp, matpass.gpumat);
+ }
}
void SyncModule::sync_curves(Object *ob, ObjectHandle &ob_handle, ModifierData *modifier_data)
diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.cc b/source/blender/draw/engines/eevee_next/eevee_velocity.cc
index ceae9df44d0..4bd0af8204e 100644
--- a/source/blender/draw/engines/eevee_next/eevee_velocity.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_velocity.cc
@@ -162,7 +162,7 @@ bool VelocityModule::step_object_sync(Object *ob,
}
/* TODO(@fclem): Reset sampling here? Should ultimately be covered by depsgraph update tags. */
- // inst_.sampling.reset();
+ inst_.sampling.reset();
return true;
}
@@ -264,7 +264,7 @@ void VelocityModule::end_sync()
}
if (deleted_obj.size() > 0) {
- // inst_.sampling.reset();
+ inst_.sampling.reset();
}
for (auto key : deleted_obj) {
diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc
index e21342c5ef6..f4dba47721d 100644
--- a/source/blender/draw/engines/eevee_next/eevee_view.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_view.cc
@@ -34,17 +34,19 @@ void ShadingView::init()
// mb_.init();
}
-void ShadingView::sync(int2 render_extent_)
+void ShadingView::sync()
{
+ int2 render_extent = inst_.film.render_extent_get();
+
if (false /* inst_.camera.is_panoramic() */) {
- int64_t render_pixel_count = render_extent_.x * (int64_t)render_extent_.y;
+ 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 here. */
is_enabled_ = true;
}
else {
- extent_ = render_extent_;
+ extent_ = render_extent;
/* Only enable -Z view. */
is_enabled_ = (StringRefNull(name_) == "negZ_view");
}
@@ -54,31 +56,23 @@ void ShadingView::sync(int2 render_extent_)
}
/* Create views. */
- // const CameraData &data = inst_.camera.data_get();
+ const CameraData &cam = inst_.camera.data_get();
float4x4 viewmat, winmat;
const float(*viewmat_p)[4] = viewmat.ptr(), (*winmat_p)[4] = winmat.ptr();
-#if 0
if (false /* inst_.camera.is_panoramic() */) {
/* TODO(@fclem) Over-scans. */
/* For now a mandatory 5% over-scan for DoF. */
- float side = data.clip_near * 1.05f;
- float near = data.clip_near;
- float far = data.clip_far;
+ float side = cam.clip_near * 1.05f;
+ float near = cam.clip_near;
+ float far = cam.clip_far;
perspective_m4(winmat.ptr(), -side, side, -side, side, near, far);
- viewmat = face_matrix_ * data.viewmat;
+ viewmat = face_matrix_ * cam.viewmat;
}
else {
- viewmat_p = data.viewmat.ptr();
- winmat_p = data.winmat.ptr();
+ viewmat_p = cam.viewmat.ptr();
+ winmat_p = cam.winmat.ptr();
}
-#else
- /* TEMP */
- UNUSED_VARS(face_matrix_);
- const DRWView *default_view = DRW_view_default_get();
- DRW_view_winmat_get(default_view, winmat.ptr(), false);
- DRW_view_viewmat_get(default_view, viewmat.ptr(), false);
-#endif
main_view_ = DRW_view_create(viewmat_p, winmat_p, nullptr, nullptr, nullptr);
sub_view_ = DRW_view_create_sub(main_view_, viewmat_p, winmat_p);
@@ -93,7 +87,6 @@ void ShadingView::sync(int2 render_extent_)
// inst_.hiz_front.view_sync(extent_);
// inst_.gbuffer.view_sync(extent_);
- combined_tx_.sync();
postfx_tx_.sync();
}
@@ -108,22 +101,18 @@ void ShadingView::render()
* With this, we can reuse the same texture across views. */
DrawEngineType *owner = (DrawEngineType *)name_;
- DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
-
- depth_tx_.ensure_2d(GPU_DEPTH24_STENCIL8, extent_);
- combined_tx_.acquire(extent_, GPU_RGBA16F, owner);
+ RenderBuffers &rbufs = inst_.render_buffers;
+ rbufs.acquire(extent_, owner);
velocity_.acquire(extent_);
- // combined_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx_), GPU_ATTACHMENT_TEXTURE(combined_tx_));
- // prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx_),
- // GPU_ATTACHMENT_TEXTURE(velocity_.view_vectors_get()));
- combined_fb_.ensure(GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(dtxl->color));
- prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(dtxl->depth),
+ combined_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx),
+ GPU_ATTACHMENT_TEXTURE(rbufs.combined_tx));
+ prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx),
GPU_ATTACHMENT_TEXTURE(velocity_.view_vectors_get()));
update_view();
DRW_stats_group_start(name_);
- // DRW_view_set_active(render_view_);
+ DRW_view_set_active(render_view_);
float4 clear_velocity(VELOCITY_INVALID);
GPU_framebuffer_bind(prepass_fb_);
@@ -142,31 +131,22 @@ void ShadingView::render()
// inst_.lookdev.render_overlay(view_fb_);
- inst_.pipelines.forward.render(render_view_, prepass_fb_, combined_fb_, depth_tx_, combined_tx_);
+ inst_.pipelines.forward.render(
+ render_view_, prepass_fb_, combined_fb_, rbufs.depth_tx, rbufs.combined_tx);
// inst_.lights.debug_draw(view_fb_);
// inst_.shadows.debug_draw(view_fb_);
- // velocity_.resolve(depth_tx_);
- velocity_.resolve(dtxl->depth);
-
- // if (inst_.render_passes.vector) {
- // inst_.render_passes.vector->accumulate(velocity_.camera_vectors_get(), sub_view_);
- // }
+ velocity_.resolve(rbufs.depth_tx);
// GPUTexture *final_radiance_tx = render_post(combined_tx_);
- // if (inst_.render_passes.combined) {
- // inst_.render_passes.combined->accumulate(final_radiance_tx, sub_view_);
- // }
+ inst_.film.accumulate(sub_view_);
- // if (inst_.render_passes.depth) {
- // inst_.render_passes.depth->accumulate(depth_tx_, sub_view_);
- // }
+ rbufs.release();
DRW_stats_group_end();
- combined_tx_.release();
postfx_tx_.release();
velocity_.release();
}
@@ -197,11 +177,15 @@ void ShadingView::update_view()
DRW_view_viewmat_get(main_view_, viewmat.ptr(), false);
DRW_view_winmat_get(main_view_, winmat.ptr(), false);
+ /* TODO(fclem): Mixed-resolution rendering: We need to make sure we render with exactly the same
+ * distances between pixels to line up render samples and target pixels.
+ * So if the target resolution is not a multiple of the resolution divisor, we need to make the
+ * projection window bigger in the +X and +Y directions. */
+
/* 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];
+ float2 jitter = inst_.film.pixel_jitter_get() / float2(extent_);
- // window_translate_m4(winmat.ptr(), winmat.ptr(), jitter_u, jitter_v);
+ window_translate_m4(winmat.ptr(), winmat.ptr(), UNPACK2(jitter));
DRW_view_update_sub(sub_view_, viewmat.ptr(), winmat.ptr());
/* FIXME(fclem): The offset may be is noticeably large and the culling might make object pop
diff --git a/source/blender/draw/engines/eevee_next/eevee_view.hh b/source/blender/draw/engines/eevee_next/eevee_view.hh
index fb74412f557..30e06df9716 100644
--- a/source/blender/draw/engines/eevee_next/eevee_view.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_view.hh
@@ -52,8 +52,6 @@ class ShadingView {
Framebuffer prepass_fb_;
Framebuffer combined_fb_;
- Texture depth_tx_;
- TextureFromPool combined_tx_;
TextureFromPool postfx_tx_;
/** Main views is created from the camera (or is from the viewport). It is not jittered. */
@@ -77,7 +75,7 @@ class ShadingView {
void init();
- void sync(int2 render_extent_);
+ void sync();
void render();
@@ -94,7 +92,7 @@ class ShadingView {
*
* Container for all views needed to render the final image.
* We might need up to 6 views for panoramic cameras.
- * All views are always available but only enabled for if need.
+ * All views are always available but only enabled for if needed.
* \{ */
class MainView {
@@ -109,8 +107,6 @@ class MainView {
ShadingView shading_views_4;
ShadingView shading_views_5;
#define shading_views_ (&shading_views_0)
- /** Internal render size. */
- int render_extent_[2];
public:
MainView(Instance &inst)
@@ -123,15 +119,8 @@ class MainView {
{
}
- void init(const int2 full_extent_)
+ void init()
{
- /* 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(6)) {
shading_views_[i].init();
}
@@ -140,7 +129,7 @@ class MainView {
void sync()
{
for (auto i : IndexRange(6)) {
- shading_views_[i].sync(render_extent_);
+ shading_views_[i].sync();
}
}
diff --git a/source/blender/draw/engines/eevee_next/eevee_world.cc b/source/blender/draw/engines/eevee_next/eevee_world.cc
index b9cb24fe30a..56cb0f127db 100644
--- a/source/blender/draw/engines/eevee_next/eevee_world.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_world.cc
@@ -79,7 +79,7 @@ void World::sync()
/* TODO(fclem) This should be detected to scene level. */
::World *orig_world = (::World *)DEG_get_original_id(&bl_world->id);
if (assign_if_different(prev_original_world, orig_world)) {
- // inst_.sampling.reset();
+ inst_.sampling.reset();
}
bNodeTree *ntree = (bl_world->nodetree && bl_world->use_nodes) ?
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_film_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_film_comp.glsl
new file mode 100644
index 00000000000..ce1f19edf53
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_film_comp.glsl
@@ -0,0 +1,13 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_film_lib.glsl)
+
+void main()
+{
+ ivec2 texel_film = ivec2(gl_GlobalInvocationID.xy);
+ /* Not used. */
+ vec4 out_color;
+ float out_depth;
+
+ film_process_data(texel_film, out_color, out_depth);
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl
new file mode 100644
index 00000000000..6716c0f126e
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl
@@ -0,0 +1,29 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_film_lib.glsl)
+
+void main()
+{
+ ivec2 texel_film = ivec2(gl_FragCoord.xy);
+ float out_depth;
+
+ if (film_buf.display_only) {
+ out_depth = imageLoad(depth_img, texel_film).r;
+
+ if (film_buf.display_id == -1) {
+ out_color = imageLoad(in_combined_img, texel_film);
+ }
+ else if (film_buf.display_is_value) {
+ out_color.rgb = imageLoad(value_accum_img, ivec3(texel_film, film_buf.display_id)).rrr;
+ out_color.a = 1.0;
+ }
+ else {
+ out_color = imageLoad(color_accum_img, ivec3(texel_film, film_buf.display_id));
+ }
+ }
+ else {
+ film_process_data(texel_film, out_color, out_depth);
+ }
+
+ gl_FragDepth = get_depth_from_view_z(-out_depth);
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl
new file mode 100644
index 00000000000..03af34f27ef
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl
@@ -0,0 +1,387 @@
+
+/**
+ * Film accumulation utils functions.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_camera_lib.glsl)
+
+/* Return scene linear Z depth from the camera or radial depth for panoramic cameras. */
+float film_depth_convert_to_scene(float depth)
+{
+ if (false /* Panoramic */) {
+ /* TODO */
+ return 1.0;
+ }
+ return abs(get_view_z_from_depth(depth));
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Filter
+ * \{ */
+
+FilmSample film_sample_get(int sample_n, ivec2 texel_film)
+{
+#ifdef PANORAMIC
+ /* TODO(fclem): Panoramic projection will be more complex. The samples will have to be retrieve
+ * at runtime, maybe by scanning a whole region. Offset and weight will have to be computed by
+ * reprojecting the incoming pixel data into film pixel space. */
+#else
+
+# ifdef SCALED_RENDERING
+ texel_film /= film_buf.scaling_factor;
+# endif
+
+ FilmSample film_sample = film_buf.samples[sample_n];
+ film_sample.texel += texel_film;
+ /* Use extend on borders. */
+ film_sample.texel = clamp(film_sample.texel, ivec2(0, 0), film_buf.extent - 1);
+
+ /* TODO(fclem): Panoramic projection will need to compute the sample weight in the shader
+ * instead of precomputing it on CPU. */
+# ifdef SCALED_RENDERING
+ /* We need to compute the real distance and weight since a sample
+ * can be used by many final pixel. */
+ vec2 offset = film_buf.subpixel_offset - vec2(texel_film % film_buf.scaling_factor);
+ film_sample.weight = film_filter_weight(film_buf.filter_size, len_squared(offset));
+# endif
+
+#endif /* PANORAMIC */
+
+ /* Always return a weight above 0 to avoid blind spots between samples. */
+ film_sample.weight = max(film_sample.weight, 1e-6);
+
+ return film_sample;
+}
+
+/* Returns the combined weights of all samples affecting this film pixel. */
+float film_weight_accumulation(ivec2 texel_film)
+{
+#if 0 /* TODO(fclem): Reference implementation, also needed for panoramic cameras. */
+ float weight = 0.0;
+ for (int i = 0; i < film_buf.samples_len; i++) {
+ weight += film_sample_get(i, texel_film).weight;
+ }
+ return weight;
+#endif
+ return film_buf.samples_weight_total;
+}
+
+void film_sample_accum(FilmSample samp, int pass_id, sampler2D tex, inout vec4 accum)
+{
+ if (pass_id == -1) {
+ return;
+ }
+ accum += texelFetch(tex, samp.texel, 0) * samp.weight;
+}
+
+void film_sample_accum(FilmSample samp, int pass_id, sampler2D tex, inout float accum)
+{
+ if (pass_id == -1) {
+ return;
+ }
+ accum += texelFetch(tex, samp.texel, 0).x * samp.weight;
+}
+
+void film_sample_accum(FilmSample samp, int pass_id, sampler2DArray tex, inout vec4 accum)
+{
+ if (pass_id == -1) {
+ return;
+ }
+ accum += texelFetch(tex, ivec3(samp.texel, pass_id), 0) * samp.weight;
+}
+
+void film_sample_accum(FilmSample samp, int pass_id, sampler2DArray tex, inout float accum)
+{
+ if (pass_id == -1) {
+ return;
+ }
+ accum += texelFetch(tex, ivec3(samp.texel, pass_id), 0).x * samp.weight;
+}
+
+void film_sample_accum_mist(FilmSample samp, inout float accum)
+{
+ if (film_buf.mist_id == -1) {
+ return;
+ }
+ float depth = texelFetch(depth_tx, samp.texel, 0).x;
+ vec2 uv = (vec2(samp.texel) + 0.5) / textureSize(depth_tx, 0).xy;
+ vec3 vP = get_view_space_from_depth(uv, depth);
+ bool is_persp = ProjectionMatrix[3][3] == 0.0;
+ float mist = (is_persp) ? length(vP) : abs(vP.z);
+ /* Remap to 0..1 range. */
+ mist = saturate(mist * film_buf.mist_scale + film_buf.mist_bias);
+ /* Falloff. */
+ mist = pow(mist, film_buf.mist_exponent);
+ accum += mist * samp.weight;
+}
+
+void film_sample_accum_combined(FilmSample samp, inout vec4 accum)
+{
+ if (film_buf.combined_id == -1) {
+ return;
+ }
+ vec4 color = texelFetch(combined_tx, samp.texel, 0);
+ /* Convert transmittance to opacity. */
+ color.a = saturate(1.0 - color.a);
+ /* TODO(fclem) Pre-expose. */
+ color.rgb = log2(1.0 + color.rgb);
+
+ accum += color * samp.weight;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Load/Store Data
+ * \{ */
+
+#define WEIGHT_lAYER_ACCUMULATION 0
+#define WEIGHT_lAYER_DISTANCE 1
+
+/* Returns the distance used to store nearest interpolation data. */
+float film_distance_load(ivec2 texel)
+{
+ /* Repeat texture coordinates as the weight can be optimized to a small portion of the film. */
+ texel = texel % imageSize(in_weight_img).xy;
+
+ if (film_buf.use_history == false) {
+ return 1.0e16;
+ }
+ return imageLoad(in_weight_img, ivec3(texel, WEIGHT_lAYER_DISTANCE)).x;
+}
+
+float film_weight_load(ivec2 texel)
+{
+ /* Repeat texture coordinates as the weight can be optimized to a small portion of the film. */
+ texel = texel % imageSize(in_weight_img).xy;
+
+ if (film_buf.use_history == false) {
+ return 0.0;
+ }
+ return imageLoad(in_weight_img, ivec3(texel, WEIGHT_lAYER_ACCUMULATION)).x;
+}
+
+/* Return the motion in pixels. */
+void film_motion_load()
+{
+ // ivec2 texel_sample = film_sample_get(0, texel_film, distance_sample);
+ // vec4 vector = texelFetch(vector_tx, texel_sample);
+
+ // vector.xy *= film_buf.extent;
+}
+
+/* Returns resolved final color. */
+void film_store_combined(FilmSample dst, vec4 color, inout vec4 display)
+{
+ if (film_buf.combined_id == -1) {
+ return;
+ }
+
+ /* Could we assume safe color from earlier pass? */
+ color = safe_color(color);
+ if (false) {
+ /* Re-projection using motion vectors. */
+ // ivec2 texel_combined = texel_film + film_motion_load(texel_film);
+ // float weight_combined = film_weight_load(texel_combined);
+ }
+#ifdef USE_NEIGHBORHOOD_CLAMPING
+ /* Only do that for combined pass as it has a non-negligeable performance impact. */
+ // color = clamp_bbox(color, min, max);
+#endif
+
+ vec4 dst_color = imageLoad(in_combined_img, dst.texel);
+
+ color = (dst_color * dst.weight + color) * dst.weight_sum_inv;
+
+ /* TODO(fclem) undo Pre-expose. */
+ // color.rgb = exp2(color.rgb) - 1.0;
+
+ if (film_buf.display_id == -1) {
+ display = color;
+ }
+ imageStore(out_combined_img, dst.texel, color);
+}
+
+void film_store_color(FilmSample dst, int pass_id, vec4 color, inout vec4 display)
+{
+ if (pass_id == -1) {
+ return;
+ }
+
+ vec4 data_film = imageLoad(color_accum_img, ivec3(dst.texel, pass_id));
+
+ color = (data_film * dst.weight + color) * dst.weight_sum_inv;
+
+ if (film_buf.display_id == pass_id) {
+ display = color;
+ }
+ imageStore(color_accum_img, ivec3(dst.texel, pass_id), color);
+}
+
+void film_store_value(FilmSample dst, int pass_id, float value, inout vec4 display)
+{
+ if (pass_id == -1) {
+ return;
+ }
+
+ float data_film = imageLoad(value_accum_img, ivec3(dst.texel, pass_id)).x;
+
+ value = (data_film * dst.weight + value) * dst.weight_sum_inv;
+
+ if (film_buf.display_id == pass_id) {
+ display = vec4(value, value, value, 1.0);
+ }
+ imageStore(value_accum_img, ivec3(dst.texel, pass_id), vec4(value));
+}
+
+/* Nearest sample variant. Always stores the data. */
+void film_store_data(ivec2 texel_film, int pass_id, vec4 data_sample, inout vec4 display)
+{
+ if (pass_id == -1) {
+ return;
+ }
+
+ if (film_buf.display_id == pass_id) {
+ display = data_sample;
+ }
+ imageStore(color_accum_img, ivec3(texel_film, pass_id), data_sample);
+}
+
+void film_store_depth(ivec2 texel_film, float value, out float out_depth)
+{
+ if (film_buf.depth_id == -1) {
+ return;
+ }
+
+ out_depth = film_depth_convert_to_scene(value);
+
+ imageStore(depth_img, texel_film, vec4(out_depth));
+}
+
+void film_store_distance(ivec2 texel, float value)
+{
+ imageStore(out_weight_img, ivec3(texel, WEIGHT_lAYER_DISTANCE), vec4(value));
+}
+
+void film_store_weight(ivec2 texel, float value)
+{
+ imageStore(out_weight_img, ivec3(texel, WEIGHT_lAYER_ACCUMULATION), vec4(value));
+}
+
+/** \} */
+
+/** NOTE: out_depth is scene linear depth from the camera origin. */
+void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth)
+{
+ out_color = vec4(0.0);
+ out_depth = 0.0;
+
+ float weight_accum = film_weight_accumulation(texel_film);
+ float film_weight = film_weight_load(texel_film);
+ float weight_sum = film_weight + weight_accum;
+ film_store_weight(texel_film, weight_sum);
+
+ FilmSample dst;
+ dst.texel = texel_film;
+ dst.weight = film_weight;
+ dst.weight_sum_inv = 1.0 / weight_sum;
+
+ /* NOTE: We split the accumulations into separate loops to avoid using too much registers and
+ * maximize occupancy. */
+
+ if (film_buf.has_data) {
+ float film_weight = film_distance_load(texel_film);
+
+ /* Get sample closest to target texel. It is always sample 0. */
+ FilmSample film_sample = film_sample_get(0, texel_film);
+
+ if (film_sample.weight < film_weight) {
+ float depth = texelFetch(depth_tx, film_sample.texel, 0).x;
+ vec4 normal = texelFetch(normal_tx, film_sample.texel, 0);
+ vec4 vector = texelFetch(vector_tx, film_sample.texel, 0);
+
+ film_store_depth(texel_film, depth, out_depth);
+ film_store_data(texel_film, film_buf.normal_id, normal, out_color);
+ film_store_data(texel_film, film_buf.vector_id, vector, out_color);
+ film_store_distance(texel_film, film_sample.weight);
+ }
+ else {
+ out_depth = imageLoad(depth_img, texel_film).r;
+ }
+ }
+
+ if (film_buf.combined_id != -1) {
+ vec4 combined_accum = vec4(0.0);
+
+ for (int i = 0; i < film_buf.samples_len; i++) {
+ FilmSample src = film_sample_get(i, texel_film);
+ film_sample_accum_combined(src, combined_accum);
+ }
+ film_store_combined(dst, combined_accum, out_color);
+ }
+
+ if (film_buf.any_render_pass_1) {
+ vec4 diffuse_light_accum = vec4(0.0);
+ vec4 specular_light_accum = vec4(0.0);
+ vec4 volume_light_accum = vec4(0.0);
+ vec4 emission_accum = vec4(0.0);
+
+ for (int i = 0; i < film_buf.samples_len; i++) {
+ FilmSample src = film_sample_get(i, texel_film);
+ film_sample_accum(src, film_buf.diffuse_light_id, diffuse_light_tx, diffuse_light_accum);
+ film_sample_accum(src, film_buf.specular_light_id, specular_light_tx, specular_light_accum);
+ film_sample_accum(src, film_buf.volume_light_id, volume_light_tx, volume_light_accum);
+ film_sample_accum(src, film_buf.emission_id, emission_tx, emission_accum);
+ }
+ film_store_color(dst, film_buf.diffuse_light_id, diffuse_light_accum, out_color);
+ film_store_color(dst, film_buf.specular_light_id, specular_light_accum, out_color);
+ film_store_color(dst, film_buf.volume_light_id, volume_light_accum, out_color);
+ film_store_color(dst, film_buf.emission_id, emission_accum, out_color);
+ }
+
+ if (film_buf.any_render_pass_2) {
+ vec4 diffuse_color_accum = vec4(0.0);
+ vec4 specular_color_accum = vec4(0.0);
+ vec4 environment_accum = vec4(0.0);
+ float mist_accum = 0.0;
+ float shadow_accum = 0.0;
+ float ao_accum = 0.0;
+
+ for (int i = 0; i < film_buf.samples_len; i++) {
+ FilmSample src = film_sample_get(i, texel_film);
+ film_sample_accum(src, film_buf.diffuse_color_id, diffuse_color_tx, diffuse_color_accum);
+ film_sample_accum(src, film_buf.specular_color_id, specular_color_tx, specular_color_accum);
+ film_sample_accum(src, film_buf.environment_id, environment_tx, environment_accum);
+ film_sample_accum(src, film_buf.shadow_id, shadow_tx, shadow_accum);
+ film_sample_accum(src, film_buf.ambient_occlusion_id, ambient_occlusion_tx, ao_accum);
+ film_sample_accum_mist(src, mist_accum);
+ }
+ film_store_color(dst, film_buf.diffuse_color_id, diffuse_color_accum, out_color);
+ film_store_color(dst, film_buf.specular_color_id, specular_color_accum, out_color);
+ film_store_color(dst, film_buf.environment_id, environment_accum, out_color);
+ film_store_value(dst, film_buf.shadow_id, shadow_accum, out_color);
+ film_store_value(dst, film_buf.ambient_occlusion_id, ao_accum, out_color);
+ film_store_value(dst, film_buf.mist_id, mist_accum, out_color);
+ }
+
+ for (int aov = 0; aov < film_buf.aov_color_len; aov++) {
+ vec4 aov_accum = vec4(0.0);
+
+ for (int i = 0; i < film_buf.samples_len; i++) {
+ FilmSample src = film_sample_get(i, texel_film);
+ film_sample_accum(src, aov, aov_color_tx, aov_accum);
+ }
+ film_store_color(dst, film_buf.aov_color_id + aov, aov_accum, out_color);
+ }
+
+ for (int aov = 0; aov < film_buf.aov_value_len; aov++) {
+ float aov_accum = 0.0;
+
+ for (int i = 0; i < film_buf.samples_len; i++) {
+ FilmSample src = film_sample_get(i, texel_film);
+ film_sample_accum(src, aov, aov_value_tx, aov_accum);
+ }
+ film_store_value(dst, film_buf.aov_value_id + aov, aov_accum, out_color);
+ }
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl
index 0ccf06a9e14..71921d0477a 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl
@@ -245,6 +245,20 @@ float F_eta(float a, float b)
}
void output_aov(vec4 color, float value, uint hash)
{
+#if defined(MAT_AOV_SUPPORT) && defined(GPU_FRAGMENT_SHADER)
+ for (int i = 0; i < AOV_MAX && i < aov_buf.color_len; i++) {
+ if (aov_buf.hash_color[i] == hash) {
+ imageStore(aov_color_img, ivec3(gl_FragCoord.xy, i), color);
+ return;
+ }
+ }
+ for (int i = 0; i < AOV_MAX && i < aov_buf.value_len; i++) {
+ if (aov_buf.hash_value[i] == hash) {
+ imageStore(aov_value_img, ivec3(gl_FragCoord.xy, i), vec4(value));
+ return;
+ }
+ }
+#endif
}
#ifdef EEVEE_MATERIAL_STUBS
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl
index 143e88dbe68..48ced4e5374 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl
@@ -53,21 +53,45 @@ void main()
g_holdout = saturate(g_holdout);
+ vec3 diffuse_light = vec3(saturate(g_diffuse_data.N.z * 0.5 + 0.5));
+ vec3 reflection_light = vec3(spec_light(g_reflection_data));
+ vec3 refraction_light = vec3(saturate(g_refraction_data.N.z * 0.5 + 0.5));
+
+ g_diffuse_data.color *= g_diffuse_data.weight;
+ g_reflection_data.color *= g_reflection_data.weight;
+ g_refraction_data.color *= g_refraction_data.weight;
+ diffuse_light *= step(1e-5, g_diffuse_data.weight);
+ reflection_light *= step(1e-5, g_reflection_data.weight);
+ refraction_light *= step(1e-5, g_refraction_data.weight);
+
out_radiance.rgb = g_emission;
- out_radiance.rgb += g_diffuse_data.color * g_diffuse_data.weight *
- saturate(g_diffuse_data.N.z * 0.5 + 0.5);
- out_radiance.rgb += g_reflection_data.color * g_reflection_data.weight *
- spec_light(g_reflection_data);
- out_radiance.rgb += g_refraction_data.color * g_refraction_data.weight *
- saturate(g_refraction_data.N.z * 0.5 + 0.5);
+ out_radiance.rgb += g_diffuse_data.color * diffuse_light;
+ out_radiance.rgb += g_reflection_data.color * reflection_light;
+ out_radiance.rgb += g_refraction_data.color * refraction_light;
out_radiance.a = 0.0;
+ vec3 specular_light = reflection_light + refraction_light;
+ vec3 specular_color = g_reflection_data.color + g_refraction_data.color;
+
+ /* TODO(fclem): This feels way too complex for what is it. */
+ bool has_any_bsdf_weight = g_diffuse_data.weight != 0.0 || g_reflection_data.weight != 0.0 ||
+ g_refraction_data.weight != 0.0;
+ vec3 out_normal = has_any_bsdf_weight ? vec3(0.0) : g_data.N;
+ out_normal += g_diffuse_data.N * g_diffuse_data.weight;
+ out_normal += g_reflection_data.N * g_reflection_data.weight;
+ out_normal += g_refraction_data.N * g_refraction_data.weight;
+ out_normal = safe_normalize(out_normal);
+
+ ivec2 out_texel = ivec2(gl_FragCoord.xy);
+ imageStore(rp_normal_img, out_texel, vec4(out_normal, 1.0));
+ imageStore(rp_diffuse_light_img, out_texel, vec4(diffuse_light, 1.0));
+ imageStore(rp_diffuse_color_img, out_texel, vec4(g_diffuse_data.color, 1.0));
+ imageStore(rp_specular_light_img, out_texel, vec4(specular_light, 1.0));
+ imageStore(rp_specular_color_img, out_texel, vec4(specular_color, 1.0));
+ imageStore(rp_emission_img, out_texel, vec4(g_emission, 1.0));
+
out_radiance.rgb *= 1.0 - g_holdout;
out_transmittance.rgb = g_transmittance;
out_transmittance.a = saturate(avg(g_transmittance));
-
- /* Test */
- out_transmittance.a = 1.0 - out_transmittance.a;
- out_radiance.a = 1.0 - out_radiance.a;
}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl
index ac657afc922..b32c3c1c4eb 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl
@@ -24,6 +24,14 @@ void main()
g_holdout = saturate(g_holdout);
+ ivec2 out_texel = ivec2(gl_FragCoord.xy);
+ imageStore(rp_normal_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0));
+ imageStore(rp_diffuse_light_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0));
+ imageStore(rp_diffuse_color_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0));
+ imageStore(rp_specular_light_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0));
+ imageStore(rp_specular_color_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0));
+ imageStore(rp_emission_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0));
+
out_background.rgb = safe_color(g_emission) * (1.0 - g_holdout);
out_background.a = saturate(avg(g_transmittance)) * g_holdout;
}
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh
new file mode 100644
index 00000000000..eec7b8ae615
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "eevee_defines.hh"
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(eevee_film)
+ .uniform_buf(1, "FilmData", "film_buf")
+ .sampler(0, ImageType::DEPTH_2D, "depth_tx")
+ .sampler(1, ImageType::FLOAT_2D, "combined_tx")
+ .sampler(2, ImageType::FLOAT_2D, "normal_tx")
+ .sampler(3, ImageType::FLOAT_2D, "vector_tx")
+ .sampler(4, ImageType::FLOAT_2D, "diffuse_light_tx")
+ .sampler(5, ImageType::FLOAT_2D, "diffuse_color_tx")
+ .sampler(6, ImageType::FLOAT_2D, "specular_light_tx")
+ .sampler(7, ImageType::FLOAT_2D, "specular_color_tx")
+ .sampler(8, ImageType::FLOAT_2D, "volume_light_tx")
+ .sampler(9, ImageType::FLOAT_2D, "emission_tx")
+ .sampler(10, ImageType::FLOAT_2D, "environment_tx")
+ .sampler(11, ImageType::FLOAT_2D, "shadow_tx")
+ .sampler(12, ImageType::FLOAT_2D, "ambient_occlusion_tx")
+ .sampler(13, ImageType::FLOAT_2D_ARRAY, "aov_color_tx")
+ .sampler(14, ImageType::FLOAT_2D_ARRAY, "aov_value_tx")
+ // .sampler(15, ImageType::FLOAT_2D, "cryptomatte_tx") /* TODO */
+ .image(0, GPU_R32F, Qualifier::READ, ImageType::FLOAT_2D_ARRAY, "in_weight_img")
+ .image(1, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "out_weight_img")
+ .image(2, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D, "in_combined_img")
+ .image(3, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_combined_img")
+ .image(4, GPU_R32F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "depth_img")
+ .image(5, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "color_accum_img")
+ .image(6, GPU_R16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "value_accum_img")
+ .additional_info("eevee_shared")
+ .additional_info("draw_view");
+
+GPU_SHADER_CREATE_INFO(eevee_film_frag)
+ .do_static_compilation(true)
+ .fragment_out(0, Type::VEC4, "out_color")
+ .fragment_source("eevee_film_frag.glsl")
+ .additional_info("draw_fullscreen", "eevee_film");
+
+GPU_SHADER_CREATE_INFO(eevee_film_comp)
+ .do_static_compilation(true)
+ .local_group_size(FILM_GROUP_SIZE, FILM_GROUP_SIZE)
+ .compute_source("eevee_film_comp.glsl")
+ .additional_info("eevee_film");
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
index d9a6b6efd0c..950164f5b86 100644
--- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
@@ -70,6 +70,14 @@ GPU_SHADER_INTERFACE_INFO(eevee_surf_iface, "interp")
#define image_out(slot, qualifier, format, name) \
image(slot, format, qualifier, ImageType::FLOAT_2D, name, Frequency::PASS)
+#define image_array_out(slot, qualifier, format, name) \
+ image(slot, format, qualifier, ImageType::FLOAT_2D_ARRAY, name, Frequency::PASS)
+
+GPU_SHADER_CREATE_INFO(eevee_aov_out)
+ .define("MAT_AOV_SUPPORT")
+ .image_array_out(6, Qualifier::WRITE, GPU_RGBA16F, "aov_color_img")
+ .image_array_out(7, Qualifier::WRITE, GPU_R16F, "aov_value_img")
+ .storage_buf(7, Qualifier::READ, "AOVsInfoData", "aov_buf");
GPU_SHADER_CREATE_INFO(eevee_surf_deferred)
.vertex_out(eevee_surf_iface)
@@ -89,27 +97,34 @@ GPU_SHADER_CREATE_INFO(eevee_surf_deferred)
// .image_out(6, Qualifier::READ_WRITE, GPU_RGBA16F, "rpass_volume_light")
/* TODO: AOVs maybe? */
.fragment_source("eevee_surf_deferred_frag.glsl")
- // .additional_info("eevee_sampling_data", "eevee_utility_texture")
+ // .additional_info("eevee_aov_out", "eevee_sampling_data", "eevee_utility_texture")
;
-#undef image_out
-
GPU_SHADER_CREATE_INFO(eevee_surf_forward)
.auto_resource_location(true)
.vertex_out(eevee_surf_iface)
+ /* Early fragment test is needed for render passes support for forward surfaces. */
+ /* NOTE: This removes the possibility of using gl_FragDepth. */
+ .early_fragment_test(true)
.fragment_out(0, Type::VEC4, "out_radiance", DualBlend::SRC_0)
.fragment_out(0, Type::VEC4, "out_transmittance", DualBlend::SRC_1)
.fragment_source("eevee_surf_forward_frag.glsl")
- // .additional_info("eevee_sampling_data",
- // "eevee_lightprobe_data",
- /* Optionally added depending on the material. */
- // "eevee_raytrace_data",
- // "eevee_transmittance_data",
- // "eevee_utility_texture",
- // "eevee_light_data",
- // "eevee_shadow_data"
- // )
- ;
+ .image_out(0, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_normal_img")
+ .image_out(1, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_diffuse_light_img")
+ .image_out(2, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_diffuse_color_img")
+ .image_out(3, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_light_img")
+ .image_out(4, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_color_img")
+ .image_out(5, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_emission_img")
+ .additional_info("eevee_aov_out"
+ // "eevee_sampling_data",
+ // "eevee_lightprobe_data",
+ /* Optionally added depending on the material. */
+ // "eevee_raytrace_data",
+ // "eevee_transmittance_data",
+ // "eevee_utility_texture",
+ // "eevee_light_data",
+ // "eevee_shadow_data"
+ );
GPU_SHADER_CREATE_INFO(eevee_surf_depth)
.vertex_out(eevee_surf_iface)
@@ -119,10 +134,20 @@ GPU_SHADER_CREATE_INFO(eevee_surf_depth)
GPU_SHADER_CREATE_INFO(eevee_surf_world)
.vertex_out(eevee_surf_iface)
+ .image_out(0, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_normal_img")
+ .image_out(1, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_diffuse_light_img")
+ .image_out(2, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_diffuse_color_img")
+ .image_out(3, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_light_img")
+ .image_out(4, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_color_img")
+ .image_out(5, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_emission_img")
.fragment_out(0, Type::VEC4, "out_background")
.fragment_source("eevee_surf_world_frag.glsl")
- // .additional_info("eevee_utility_texture")
- ;
+ .additional_info("eevee_aov_out"
+ //"eevee_utility_texture"
+ );
+
+#undef image_out
+#undef image_array_out
/** \} */
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh
index a5f16363466..c6cbf9b1456 100644
--- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"