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.
This commit is contained in:
Clément Foucault 2022-06-28 18:33:25 +02:00
parent a9696f04a0
commit f18067aa03
Notes: blender-bot 2023-02-14 08:42:54 +01:00
Referenced by issue #99390, Eevee-next: Cryptomatte render pass
37 changed files with 2396 additions and 118 deletions

View File

@ -390,6 +390,27 @@ class RENDER_PT_eevee_sampling(RenderButtonsPanel, Panel):
col.prop(props, "use_taa_reprojection")
class RENDER_PT_eevee_next_sampling(RenderButtonsPanel, Panel):
bl_label = "Sampling"
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
@classmethod
def poll(cls, context):
return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
scene = context.scene
props = scene.eevee
col = layout.column(align=True)
col.prop(props, "taa_render_samples", text="Render")
col.prop(props, "taa_samples", text="Viewport")
class RENDER_PT_eevee_indirect_lighting(RenderButtonsPanel, Panel):
bl_label = "Indirect Lighting"
bl_options = {'DEFAULT_CLOSED'}
@ -482,6 +503,27 @@ class RENDER_PT_eevee_film(RenderButtonsPanel, Panel):
sub.prop(props, "overscan_size", text="")
class RENDER_PT_eevee_next_film(RenderButtonsPanel, Panel):
bl_label = "Film"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
@classmethod
def poll(cls, context):
return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
scene = context.scene
rd = scene.render
props = scene.eevee
col = layout.column()
col.prop(rd, "filter_size")
def draw_curves_settings(self, context):
layout = self.layout
scene = context.scene
@ -702,6 +744,9 @@ classes = (
RENDER_PT_eevee_indirect_lighting_display,
RENDER_PT_eevee_film,
RENDER_PT_eevee_next_sampling,
RENDER_PT_eevee_next_film,
RENDER_PT_gpencil,
RENDER_PT_opengl_sampling,
RENDER_PT_opengl_lighting,

View File

@ -19,7 +19,7 @@ class WorldButtonsPanel:
class WORLD_PT_context_world(WorldButtonsPanel, Panel):
bl_label = ""
bl_options = {'HIDE_HEADER'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
@ -41,7 +41,7 @@ class WORLD_PT_context_world(WorldButtonsPanel, Panel):
class EEVEE_WORLD_PT_mist(WorldButtonsPanel, Panel):
bl_label = "Mist Pass"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_EEVEE'}
COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT'}
@classmethod
def poll(cls, context):
@ -63,14 +63,14 @@ class EEVEE_WORLD_PT_mist(WorldButtonsPanel, Panel):
class WORLD_PT_custom_props(WorldButtonsPanel, PropertyPanel, Panel):
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'}
_context_path = "world"
_property_type = bpy.types.World
class EEVEE_WORLD_PT_surface(WorldButtonsPanel, Panel):
bl_label = "Surface"
COMPAT_ENGINES = {'BLENDER_EEVEE'}
COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT'}
@classmethod
def poll(cls, context):

View File

@ -136,9 +136,12 @@ set(SRC
engines/eevee/eevee_volumes.c
engines/eevee_next/eevee_camera.cc
engines/eevee_next/eevee_engine.cc
engines/eevee_next/eevee_film.cc
engines/eevee_next/eevee_instance.cc
engines/eevee_next/eevee_material.cc
engines/eevee_next/eevee_pipeline.cc
engines/eevee_next/eevee_renderbuffers.cc
engines/eevee_next/eevee_sampling.cc
engines/eevee_next/eevee_shader.cc
engines/eevee_next/eevee_sync.cc
engines/eevee_next/eevee_velocity.cc
@ -357,6 +360,9 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_attributes_lib.glsl
engines/eevee_next/shaders/eevee_camera_lib.glsl
engines/eevee_next/shaders/eevee_film_comp.glsl
engines/eevee_next/shaders/eevee_film_frag.glsl
engines/eevee_next/shaders/eevee_film_lib.glsl
engines/eevee_next/shaders/eevee_geom_curves_vert.glsl
engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl
engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl

View File

@ -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();
}
}

View File

@ -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)

View File

@ -43,3 +43,5 @@
/* Minimum visibility size. */
#define LIGHTPROBE_FILTER_VIS_GROUP_SIZE 16
#define FILM_GROUP_SIZE 16

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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 !DRW_state_is_scene_render();
return render == nullptr;
}
bool use_scene_lights(void) const
bool overlays_enabled() const
{
return (!v3d) || ((v3d->flag & V3D_HIDE_OVERLAYS) == 0);
}
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);

View File

@ -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) &&

View File

@ -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(

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:
/**

View File

@ -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,
};

View File

@ -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"
@ -27,6 +27,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,14 +122,136 @@ 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

View File

@ -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)

View File

@ -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) {

View File

@ -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);
/* 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];
/* 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. */
// window_translate_m4(winmat.ptr(), winmat.ptr(), jitter_u, jitter_v);
/* Anti-Aliasing / Super-Sampling jitter. */
float2 jitter = inst_.film.pixel_jitter_get() / float2(extent_);
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

View File

@ -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();
}
}

View File

@ -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) ?

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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");

View File

@ -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
/** \} */

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"

View File

@ -25,6 +25,9 @@ set(INC
# For theme color access.
../editors/include
# For *_info.hh includes.
../draw/engines/eevee_next
# For node muting stuff.
../nodes
@ -445,6 +448,7 @@ list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR})
set(SRC_SHADER_CREATE_INFOS
../draw/engines/basic/shaders/infos/basic_depth_info.hh
../draw/engines/eevee_next/shaders/infos/eevee_film_info.hh
../draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
../draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh
../draw/engines/gpencil/shaders/infos/gpencil_info.hh

View File

@ -41,6 +41,7 @@
# define floorf floor
# define ceilf ceil
# define sqrtf sqrt
# define expf exp
# define float2 vec2
# define float3 vec3

View File

@ -35,6 +35,7 @@ typedef enum eViewLayerEEVEEPassType {
EEVEE_RENDER_PASS_BLOOM = (1 << 14),
EEVEE_RENDER_PASS_AOV = (1 << 15),
EEVEE_RENDER_PASS_CRYPTOMATTE = (1 << 16),
EEVEE_RENDER_PASS_VECTOR = (1 << 17),
} eViewLayerEEVEEPassType;
#define EEVEE_RENDER_PASS_MAX_BIT 17