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:
parent
a9696f04a0
commit
f18067aa03
Notes:
blender-bot
2023-02-14 08:42:54 +01:00
Referenced by issue #99390, Eevee-next: Cryptomatte render pass
|
@ -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,
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -43,3 +43,5 @@
|
|||
|
||||
/* Minimum visibility size. */
|
||||
#define LIGHTPROBE_FILTER_VIS_GROUP_SIZE 16
|
||||
|
||||
#define FILM_GROUP_SIZE 16
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) &&
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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:
|
||||
/**
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) ?
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
|
@ -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
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
# define floorf floor
|
||||
# define ceilf ceil
|
||||
# define sqrtf sqrt
|
||||
# define expf exp
|
||||
|
||||
# define float2 vec2
|
||||
# define float3 vec3
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue