This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/draw/engines/eevee/eevee_temporal_sampling.c

368 lines
12 KiB
C
Raw Normal View History

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
2019-01-23 11:29:18 +11:00
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Temporal super sampling technique
*/
#include "DRW_render.h"
#include "ED_screen.h"
#include "BLI_rand.h"
#include "DEG_depsgraph_query.h"
#include "eevee_private.h"
#include "GPU_texture.h"
#define FILTER_CDF_TABLE_SIZE 512
static struct {
/* Pixel filter table: Only blackman-harris for now. */
bool inited;
float inverted_cdf[FILTER_CDF_TABLE_SIZE];
} e_data = {false}; /* Engine data */
extern char datatoc_common_uniforms_lib_glsl[];
extern char datatoc_common_view_lib_glsl[];
extern char datatoc_bsdf_common_lib_glsl[];
static float UNUSED_FUNCTION(filter_box)(float UNUSED(x))
{
return 1.0f;
}
static float filter_blackman_harris(float x)
{
/* Hardcoded 1px footprint [-0.5..0.5]. We resize later. */
const float width = 1.0f;
x = 2.0f * M_PI * (x / width + 0.5f);
return 0.35875f - 0.48829f * cosf(x) + 0.14128f * cosf(2.0f * x) - 0.01168f * cosf(3.0f * x);
}
/* Compute cumulative distribution function of a discrete function. */
static void compute_cdf(float (*func)(float x), float cdf[FILTER_CDF_TABLE_SIZE])
{
cdf[0] = 0.0f;
/* Actual CDF evaluation. */
for (int u = 0; u < FILTER_CDF_TABLE_SIZE - 1; u++) {
float x = (float)(u + 1) / (float)(FILTER_CDF_TABLE_SIZE - 1);
cdf[u + 1] = cdf[u] + func(x - 0.5f); /* [-0.5..0.5]. We resize later. */
}
/* Normalize the CDF. */
for (int u = 0; u < FILTER_CDF_TABLE_SIZE - 1; u++) {
cdf[u] /= cdf[FILTER_CDF_TABLE_SIZE - 1];
}
/* Just to make sure. */
cdf[FILTER_CDF_TABLE_SIZE - 1] = 1.0f;
}
static void invert_cdf(const float cdf[FILTER_CDF_TABLE_SIZE],
float invert_cdf[FILTER_CDF_TABLE_SIZE])
{
for (int u = 0; u < FILTER_CDF_TABLE_SIZE; u++) {
float x = (float)u / (float)(FILTER_CDF_TABLE_SIZE - 1);
for (int i = 0; i < FILTER_CDF_TABLE_SIZE; i++) {
if (cdf[i] >= x) {
if (i == FILTER_CDF_TABLE_SIZE - 1) {
invert_cdf[u] = 1.0f;
}
else {
float t = (x - cdf[i]) / (cdf[i + 1] - cdf[i]);
invert_cdf[u] = ((float)i + t) / (float)(FILTER_CDF_TABLE_SIZE - 1);
}
break;
}
}
}
}
/* Evaluate a discrete function table with linear interpolation. */
static float eval_table(float *table, float x)
{
CLAMP(x, 0.0f, 1.0f);
x = x * (FILTER_CDF_TABLE_SIZE - 1);
int index = min_ii((int)(x), FILTER_CDF_TABLE_SIZE - 1);
int nindex = min_ii(index + 1, FILTER_CDF_TABLE_SIZE - 1);
float t = x - index;
return (1.0f - t) * table[index] + t * table[nindex];
}
static void eevee_create_cdf_table_temporal_sampling(void)
{
float *cdf_table = MEM_mallocN(sizeof(float) * FILTER_CDF_TABLE_SIZE, "Eevee Filter CDF table");
float filter_width = 2.0f; /* Use a 2 pixel footprint by default. */
{
/* Use blackman-harris filter. */
filter_width *= 2.0f;
compute_cdf(filter_blackman_harris, cdf_table);
}
invert_cdf(cdf_table, e_data.inverted_cdf);
/* Scale and offset table. */
for (int i = 0; i < FILTER_CDF_TABLE_SIZE; i++) {
e_data.inverted_cdf[i] = (e_data.inverted_cdf[i] - 0.5f) * filter_width;
}
MEM_freeN(cdf_table);
e_data.inited = true;
}
void EEVEE_temporal_sampling_offset_calc(const double ht_point[2],
const float filter_size,
float r_offset[2])
{
r_offset[0] = eval_table(e_data.inverted_cdf, (float)(ht_point[0])) * filter_size;
r_offset[1] = eval_table(e_data.inverted_cdf, (float)(ht_point[1])) * filter_size;
}
void EEVEE_temporal_sampling_matrices_calc(EEVEE_EffectsInfo *effects, const double ht_point[2])
{
const float *viewport_size = DRW_viewport_size_get();
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
RenderData *rd = &scene->r;
float persmat[4][4], viewmat[4][4], winmat[4][4];
DRW_view_persmat_get(NULL, persmat, false);
DRW_view_viewmat_get(NULL, viewmat, false);
DRW_view_winmat_get(NULL, winmat, false);
float ofs[2];
EEVEE_temporal_sampling_offset_calc(ht_point, rd->gauss, ofs);
window_translate_m4(winmat, persmat, ofs[0] / viewport_size[0], ofs[1] / viewport_size[1]);
BLI_assert(effects->taa_view != NULL);
/* When rendering just update the view. This avoids recomputing the culling. */
DRW_view_update_sub(effects->taa_view, viewmat, winmat);
}
/* Update the matrices based on the current sample.
* Note: `DRW_MAT_PERS` and `DRW_MAT_VIEW` needs to read the original matrices. */
void EEVEE_temporal_sampling_update_matrices(EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
EEVEE_EffectsInfo *effects = stl->effects;
double ht_point[2];
double ht_offset[2] = {0.0, 0.0};
uint ht_primes[2] = {2, 3};
BLI_halton_2d(ht_primes, ht_offset, effects->taa_current_sample - 1, ht_point);
EEVEE_temporal_sampling_matrices_calc(effects, ht_point);
DRW_view_set_active(effects->taa_view);
}
void EEVEE_temporal_sampling_reset(EEVEE_Data *vedata)
{
vedata->stl->effects->taa_render_sample = 1;
vedata->stl->effects->taa_current_sample = 1;
}
int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
int repro_flag = 0;
if (!e_data.inited) {
eevee_create_cdf_table_temporal_sampling();
}
/**
* Reset for each "redraw". When rendering using ogl render,
* we accumulate the redraw inside the drawing loop in eevee_draw_scene().
**/
effects->taa_render_sample = 1;
effects->taa_view = NULL;
/* Create a sub view to disable clipping planes (if any). */
const DRWView *default_view = DRW_view_default_get();
float viewmat[4][4], winmat[4][4];
DRW_view_viewmat_get(default_view, viewmat, false);
DRW_view_winmat_get(default_view, winmat, false);
effects->taa_view = DRW_view_create_sub(default_view, viewmat, winmat);
DRW_view_clip_planes_set(effects->taa_view, NULL, 0);
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
if ((scene_eval->eevee.taa_samples != 1) || DRW_state_is_image_render()) {
float persmat[4][4];
if (!DRW_state_is_image_render() && (scene_eval->eevee.flag & SCE_EEVEE_TAA_REPROJECTION)) {
repro_flag = EFFECT_TAA_REPROJECT | EFFECT_VELOCITY_BUFFER | EFFECT_DEPTH_DOUBLE_BUFFER |
EFFECT_DOUBLE_BUFFER | EFFECT_POST_BUFFER;
effects->taa_reproject_sample = ((effects->taa_reproject_sample + 1) % 16);
}
/* Until we support reprojection, we need to make sure
* that the history buffer contains correct information. */
bool view_is_valid = stl->g_data->valid_double_buffer;
view_is_valid = view_is_valid && (stl->g_data->view_updated == false);
if (draw_ctx->evil_C != NULL) {
struct wmWindowManager *wm = CTX_wm_manager(draw_ctx->evil_C);
view_is_valid = view_is_valid && (ED_screen_animation_no_scrub(wm) == NULL);
}
effects->taa_total_sample = EEVEE_renderpasses_only_first_sample_pass_active(vedata) ?
1 :
scene_eval->eevee.taa_samples;
MAX2(effects->taa_total_sample, 0);
DRW_view_persmat_get(NULL, persmat, false);
view_is_valid = view_is_valid && compare_m4m4(persmat, effects->prev_drw_persmat, FLT_MIN);
copy_m4_m4(effects->prev_drw_persmat, persmat);
/* Prevent ghosting from probe data. */
view_is_valid = view_is_valid && (effects->prev_drw_support == DRW_state_draw_support());
effects->prev_drw_support = DRW_state_draw_support();
if (((effects->taa_total_sample == 0) ||
(effects->taa_current_sample < effects->taa_total_sample)) ||
DRW_state_is_image_render()) {
if (view_is_valid) {
/* Viewport rendering updates the matrices in `eevee_draw_scene` */
if (!DRW_state_is_image_render()) {
effects->taa_current_sample += 1;
repro_flag = 0;
}
}
else {
effects->taa_current_sample = 1;
}
}
else {
effects->taa_current_sample = 1;
}
return repro_flag | EFFECT_TAA | EFFECT_DOUBLE_BUFFER | EFFECT_DEPTH_DOUBLE_BUFFER |
EFFECT_POST_BUFFER;
}
effects->taa_current_sample = 1;
return repro_flag;
}
void EEVEE_temporal_sampling_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_EffectsInfo *effects = stl->effects;
if ((effects->enabled_effects & (EFFECT_TAA | EFFECT_TAA_REPROJECT)) != 0) {
struct GPUShader *sh = EEVEE_shaders_taa_resolve_sh_get(effects->enabled_effects);
DRW_PASS_CREATE(psl->taa_resolve, DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->taa_resolve);
DRW_shgroup_uniform_texture_ref(grp, "colorHistoryBuffer", &txl->taa_history);
DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &effects->source_buffer);
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
EEVEE: Render Passes This patch adds new render passes to EEVEE. These passes include: * Emission * Diffuse Light * Diffuse Color * Glossy Light * Glossy Color * Environment * Volume Scattering * Volume Transmission * Bloom * Shadow With these passes it will be possible to use EEVEE effectively for compositing. During development we kept a close eye on how to get similar results compared to cycles render passes there are some differences that are related to how EEVEE works. For EEVEE we combined the passes to `Diffuse` and `Specular`. There are no transmittance or sss passes anymore. Cycles will be changed accordingly. Cycles volume transmittance is added to multiple surface col passes. For EEVEE we left the volume transmittance as a separate pass. Known Limitations * All materials that use alpha blending will not be rendered in the render passes. Other transparency modes are supported. * More GPU memory is required to store the render passes. When rendering a HD image with all render passes enabled at max extra 570MB GPU memory is required. Implementation Details An overview of render passes have been described in https://wiki.blender.org/wiki/Source/Render/EEVEE/RenderPasses Future Developments * In this implementation the materials are re-rendered for Diffuse/Glossy and Emission passes. We could use multi target rendering to improve the render speed. * Other passes can be added later * Don't render material based passes when only requesting AO or Shadow. * Add more passes to the system. These could include Cryptomatte, AOV's, Vector, ObjectID, MaterialID, UV. Reviewed By: Clément Foucault Differential Revision: https://developer.blender.org/D6331
2020-02-20 14:53:53 +01:00
DRW_shgroup_uniform_block(
grp, "renderpass_block", EEVEE_material_default_render_pass_ubo_get(sldata));
if (effects->enabled_effects & EFFECT_TAA_REPROJECT) {
// DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
DRW_shgroup_uniform_texture_ref(grp, "velocityBuffer", &effects->velocity_tx);
}
else {
DRW_shgroup_uniform_float(grp, "alpha", &effects->taa_alpha, 1);
}
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
}
}
void EEVEE_temporal_sampling_draw(EEVEE_Data *vedata)
{
EEVEE_PassList *psl = vedata->psl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
if ((effects->enabled_effects & (EFFECT_TAA | EFFECT_TAA_REPROJECT)) != 0) {
if ((effects->enabled_effects & EFFECT_TAA) != 0 && effects->taa_current_sample != 1) {
if (DRW_state_is_image_render()) {
/* See EEVEE_temporal_sampling_init() for more details. */
effects->taa_alpha = 1.0f / (float)(effects->taa_render_sample);
}
else {
effects->taa_alpha = 1.0f / (float)(effects->taa_current_sample);
}
GPU_framebuffer_bind(effects->target_buffer);
DRW_draw_pass(psl->taa_resolve);
/* Restore the depth from sample 1. */
GPU_framebuffer_blit(fbl->double_buffer_depth_fb, 0, fbl->main_fb, 0, GPU_DEPTH_BIT);
SWAP_BUFFERS_TAA();
}
else {
/* Save the depth buffer for the next frame.
* This saves us from doing anything special
* in the other mode engines. */
GPU_framebuffer_blit(fbl->main_fb, 0, fbl->double_buffer_depth_fb, 0, GPU_DEPTH_BIT);
/* Do reprojection for noise reduction */
/* TODO : do AA jitter if in only render view. */
if (!DRW_state_is_image_render() && (effects->enabled_effects & EFFECT_TAA_REPROJECT) != 0 &&
stl->g_data->valid_taa_history) {
GPU_framebuffer_bind(effects->target_buffer);
DRW_draw_pass(psl->taa_resolve);
SWAP_BUFFERS_TAA();
}
else {
struct GPUFrameBuffer *source_fb = (effects->target_buffer == fbl->main_color_fb) ?
fbl->effect_color_fb :
fbl->main_color_fb;
GPU_framebuffer_blit(source_fb, 0, fbl->taa_history_color_fb, 0, GPU_COLOR_BIT);
}
}
/* Make each loop count when doing a render. */
if (DRW_state_is_image_render()) {
effects->taa_render_sample += 1;
effects->taa_current_sample += 1;
}
else {
if ((effects->taa_total_sample == 0) ||
(effects->taa_current_sample < effects->taa_total_sample)) {
DRW_viewport_request_redraw();
}
}
}
}