This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/draw/engines/eevee/eevee_materials.c
Clément Foucault 9957096f35 EEVEE: ScreenSpaceReflections: Improve hit quality
This changes the hitBuffer to store `ReflectionDir * HitTime, invPdf`
just as the reference presentation.

This avoids issues when the hit refinement produce a coordinate that
does not land on the correct surface.

We now store the pdf in the same texture and store it inversed so we can
remove some ALU from the resolve shader.

This also rewrite the resolve shader to not be vectorized to improve
readability and scalability.
2021-03-10 17:57:09 +01:00

1125 lines
42 KiB
C

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*/
#include "DRW_render.h"
#include "BLI_alloca.h"
#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "BLI_math_bits.h"
#include "BLI_memblock.h"
#include "BLI_rand.h"
#include "BLI_string_utils.h"
#include "BKE_paint.h"
#include "BKE_particle.h"
#include "DNA_hair_types.h"
#include "DNA_modifier_types.h"
#include "DNA_view3d_types.h"
#include "DNA_world_types.h"
#include "GPU_material.h"
#include "DEG_depsgraph_query.h"
#include "eevee_engine.h"
#include "eevee_lut.h"
#include "eevee_private.h"
/* *********** STATIC *********** */
static struct {
/* 64*64 array texture containing all LUTs and other utilitarian arrays.
* Packing enables us to same precious textures slots. */
struct GPUTexture *util_tex;
struct GPUTexture *noise_tex;
float noise_offsets[3];
} e_data = {NULL}; /* Engine data */
typedef struct EeveeMaterialCache {
struct DRWShadingGroup *depth_grp;
struct DRWShadingGroup *shading_grp;
struct DRWShadingGroup *shadow_grp;
struct GPUMaterial *shading_gpumat;
/* Meh, Used by hair to ensure draw order when calling DRW_shgroup_create_sub.
* Pointers to ghash values. */
struct DRWShadingGroup **depth_grp_p;
struct DRWShadingGroup **shading_grp_p;
struct DRWShadingGroup **shadow_grp_p;
} EeveeMaterialCache;
/* *********** FUNCTIONS *********** */
/* XXX TODO define all shared resources in a shared place without duplication */
struct GPUTexture *EEVEE_materials_get_util_tex(void)
{
return e_data.util_tex;
}
/**
* ssr_id can be null to disable ssr contribution.
*/
void EEVEE_material_bind_resources(DRWShadingGroup *shgrp,
GPUMaterial *gpumat,
EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
const int *ssr_id,
const float *refract_depth,
bool use_ssrefraction,
bool use_alpha_blend)
{
bool use_diffuse = GPU_material_flag_get(gpumat, GPU_MATFLAG_DIFFUSE);
bool use_glossy = GPU_material_flag_get(gpumat, GPU_MATFLAG_GLOSSY);
bool use_refract = GPU_material_flag_get(gpumat, GPU_MATFLAG_REFRACT);
LightCache *lcache = vedata->stl->g_data->light_cache;
EEVEE_EffectsInfo *effects = vedata->stl->effects;
EEVEE_PrivateData *pd = vedata->stl->g_data;
DRW_shgroup_uniform_block(shgrp, "probe_block", sldata->probe_ubo);
DRW_shgroup_uniform_block(shgrp, "grid_block", sldata->grid_ubo);
DRW_shgroup_uniform_block(shgrp, "planar_block", sldata->planar_ubo);
DRW_shgroup_uniform_block(shgrp, "light_block", sldata->light_ubo);
DRW_shgroup_uniform_block(shgrp, "shadow_block", sldata->shadow_ubo);
DRW_shgroup_uniform_block(shgrp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block_ref(shgrp, "renderpass_block", &pd->renderpass_ubo);
DRW_shgroup_uniform_int_copy(shgrp, "outputSssId", 1);
DRW_shgroup_uniform_texture(shgrp, "utilTex", e_data.util_tex);
if (use_diffuse || use_glossy || use_refract) {
DRW_shgroup_uniform_texture_ref(shgrp, "shadowCubeTexture", &sldata->shadow_cube_pool);
DRW_shgroup_uniform_texture_ref(shgrp, "shadowCascadeTexture", &sldata->shadow_cascade_pool);
DRW_shgroup_uniform_texture_ref(shgrp, "maxzBuffer", &vedata->txl->maxzbuffer);
}
if ((use_diffuse || use_glossy) && !use_ssrefraction) {
DRW_shgroup_uniform_texture_ref(shgrp, "horizonBuffer", &effects->gtao_horizons);
}
if (use_diffuse) {
DRW_shgroup_uniform_texture_ref(shgrp, "irradianceGrid", &lcache->grid_tx.tex);
}
if (use_glossy || use_refract) {
DRW_shgroup_uniform_texture_ref(shgrp, "probeCubes", &lcache->cube_tx.tex);
}
if (use_glossy) {
DRW_shgroup_uniform_texture_ref(shgrp, "probePlanars", &vedata->txl->planar_pool);
DRW_shgroup_uniform_int_copy(shgrp, "outputSsrId", ssr_id ? *ssr_id : 0);
}
if (use_refract) {
DRW_shgroup_uniform_float_copy(
shgrp, "refractionDepth", (refract_depth) ? *refract_depth : 0.0);
if (use_ssrefraction) {
DRW_shgroup_uniform_texture_ref(
shgrp, "refractColorBuffer", &vedata->txl->filtered_radiance);
}
}
if (use_alpha_blend) {
DRW_shgroup_uniform_texture_ref(shgrp, "inScattering", &effects->volume_scatter);
DRW_shgroup_uniform_texture_ref(shgrp, "inTransmittance", &effects->volume_transmit);
}
}
static void eevee_init_noise_texture(void)
{
e_data.noise_tex = DRW_texture_create_2d(64, 64, GPU_RGBA16F, 0, (float *)blue_noise);
}
#define RUNTIME_LUT_CREATION 0
static void eevee_init_util_texture(void)
{
const int layers = 4 + 16;
float(*texels)[4] = MEM_mallocN(sizeof(float[4]) * 64 * 64 * layers, "utils texels");
float(*texels_layer)[4] = texels;
#if RUNTIME_LUT_CREATION
float *bsdf_ggx_lut = EEVEE_lut_update_ggx_brdf(64);
float(*btdf_ggx_lut)[64 * 64 * 2] = (float(*)[64 * 64 * 2]) EEVEE_lut_update_ggx_btdf(64, 16);
#else
const float *bsdf_ggx_lut = bsdf_split_sum_ggx;
const float(*btdf_ggx_lut)[64 * 64 * 2] = btdf_split_sum_ggx;
#endif
/* Copy ltc_mat_ggx into 1st layer */
memcpy(texels_layer, ltc_mat_ggx, sizeof(float[4]) * 64 * 64);
texels_layer += 64 * 64;
/* Copy bsdf_split_sum_ggx into 2nd layer red and green channels.
* Copy ltc_mag_ggx into 2nd layer blue and alpha channel. */
for (int i = 0; i < 64 * 64; i++) {
texels_layer[i][0] = bsdf_ggx_lut[i * 2 + 0];
texels_layer[i][1] = bsdf_ggx_lut[i * 2 + 1];
texels_layer[i][2] = ltc_mag_ggx[i * 2 + 0];
texels_layer[i][3] = ltc_mag_ggx[i * 2 + 1];
}
texels_layer += 64 * 64;
/* Copy blue noise in 3rd layer */
for (int i = 0; i < 64 * 64; i++) {
texels_layer[i][0] = blue_noise[i][0];
texels_layer[i][1] = blue_noise[i][2];
texels_layer[i][2] = cosf(blue_noise[i][1] * 2.0f * M_PI);
texels_layer[i][3] = sinf(blue_noise[i][1] * 2.0f * M_PI);
}
texels_layer += 64 * 64;
/* Copy ltc_disk_integral in 4th layer */
for (int i = 0; i < 64 * 64; i++) {
texels_layer[i][0] = ltc_disk_integral[i];
texels_layer[i][1] = 0.0; /* UNUSED */
texels_layer[i][2] = 0.0; /* UNUSED */
texels_layer[i][3] = 0.0; /* UNUSED */
}
texels_layer += 64 * 64;
/* Copy Refraction GGX LUT in layer 5 - 21 */
for (int j = 0; j < 16; j++) {
for (int i = 0; i < 64 * 64; i++) {
texels_layer[i][0] = btdf_ggx_lut[j][i * 2 + 0];
texels_layer[i][1] = btdf_ggx_lut[j][i * 2 + 1];
texels_layer[i][2] = 0.0; /* UNUSED */
texels_layer[i][3] = 0.0; /* UNUSED */
}
texels_layer += 64 * 64;
}
e_data.util_tex = DRW_texture_create_2d_array(
64, 64, layers, GPU_RGBA16F, DRW_TEX_FILTER | DRW_TEX_WRAP, (float *)texels);
MEM_freeN(texels);
#if RUNTIME_LUT_CREATION
MEM_freeN(bsdf_ggx_lut);
MEM_freeN(btdf_ggx_lut);
#endif
}
void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const double offsets[3])
{
e_data.noise_offsets[0] = offsets[0];
e_data.noise_offsets[1] = offsets[1];
e_data.noise_offsets[2] = offsets[2];
GPU_framebuffer_bind(fbl->update_noise_fb);
DRW_draw_pass(psl->update_noise_pass);
}
void EEVEE_materials_init(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
EEVEE_StorageList *stl,
EEVEE_FramebufferList *fbl)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
EEVEE_PrivateData *g_data = stl->g_data;
if (!e_data.util_tex) {
EEVEE_shaders_material_shaders_init();
eevee_init_util_texture();
eevee_init_noise_texture();
}
if (!DRW_state_is_image_render() && ((stl->effects->enabled_effects & EFFECT_TAA) == 0)) {
sldata->common_data.alpha_hash_offset = 0.0f;
sldata->common_data.alpha_hash_scale = 1.0f;
}
else {
double r;
BLI_halton_1d(5, 0.0, stl->effects->taa_current_sample - 1, &r);
sldata->common_data.alpha_hash_offset = (float)r;
sldata->common_data.alpha_hash_scale = 0.01f;
}
{
/* Update noise Frame-buffer. */
GPU_framebuffer_ensure_config(
&fbl->update_noise_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_LAYER(e_data.util_tex, 2)});
}
{
/* Create RenderPass UBO */
if (sldata->renderpass_ubo.combined == NULL) {
EEVEE_RenderPassData data;
data = (EEVEE_RenderPassData){true, true, true, true, true, false, false, false, 0};
sldata->renderpass_ubo.combined = GPU_uniformbuf_create_ex(
sizeof(data), &data, "renderpass_ubo.combined");
data = (EEVEE_RenderPassData){true, false, false, false, false, true, false, false, 0};
sldata->renderpass_ubo.diff_color = GPU_uniformbuf_create_ex(
sizeof(data), &data, "renderpass_ubo.diff_color");
data = (EEVEE_RenderPassData){true, true, false, false, false, false, false, false, 0};
sldata->renderpass_ubo.diff_light = GPU_uniformbuf_create_ex(
sizeof(data), &data, "renderpass_ubo.diff_light");
data = (EEVEE_RenderPassData){false, false, true, false, false, false, false, false, 0};
sldata->renderpass_ubo.spec_color = GPU_uniformbuf_create_ex(
sizeof(data), &data, "renderpass_ubo.spec_color");
data = (EEVEE_RenderPassData){false, false, true, true, false, false, false, false, 0};
sldata->renderpass_ubo.spec_light = GPU_uniformbuf_create_ex(
sizeof(data), &data, "renderpass_ubo.spec_light");
data = (EEVEE_RenderPassData){false, false, false, false, true, false, false, false, 0};
sldata->renderpass_ubo.emit = GPU_uniformbuf_create_ex(
sizeof(data), &data, "renderpass_ubo.emit");
data = (EEVEE_RenderPassData){true, true, true, true, true, false, true, false, 0};
sldata->renderpass_ubo.environment = GPU_uniformbuf_create_ex(
sizeof(data), &data, "renderpass_ubo.environment");
}
/* Used combined pass by default. */
g_data->renderpass_ubo = sldata->renderpass_ubo.combined;
{
g_data->num_aovs_used = 0;
if ((stl->g_data->render_passes & EEVEE_RENDER_PASS_AOV) != 0) {
EEVEE_RenderPassData data = {true, true, true, true, true, false, false, true, 0};
if (stl->g_data->aov_hash == EEVEE_AOV_HASH_ALL) {
ViewLayer *view_layer = draw_ctx->view_layer;
int aov_index = 0;
LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
if ((aov->flag & AOV_CONFLICT) != 0) {
continue;
}
if (aov_index == MAX_AOVS) {
break;
}
data.renderPassAOVActive = EEVEE_renderpasses_aov_hash(aov);
if (sldata->renderpass_ubo.aovs[aov_index]) {
GPU_uniformbuf_update(sldata->renderpass_ubo.aovs[aov_index], &data);
}
else {
sldata->renderpass_ubo.aovs[aov_index] = GPU_uniformbuf_create_ex(
sizeof(data), &data, "renderpass_ubo.aovs");
}
aov_index++;
}
g_data->num_aovs_used = aov_index;
}
else {
/* Rendering a single AOV in the 3d viewport */
data.renderPassAOVActive = stl->g_data->aov_hash;
if (sldata->renderpass_ubo.aovs[0]) {
GPU_uniformbuf_update(sldata->renderpass_ubo.aovs[0], &data);
}
else {
sldata->renderpass_ubo.aovs[0] = GPU_uniformbuf_create_ex(
sizeof(data), &data, "renderpass_ubo.aovs");
}
g_data->num_aovs_used = 1;
}
}
/* Free AOV UBO's that are not in use. */
for (int aov_index = g_data->num_aovs_used; aov_index < MAX_AOVS; aov_index++) {
DRW_UBO_FREE_SAFE(sldata->renderpass_ubo.aovs[aov_index]);
}
}
/* HACK: EEVEE_material_get can create a new context. This can only be
* done when there is no active framebuffer. We do this here otherwise
* `EEVEE_renderpasses_output_init` will fail. It cannot be done in
* `EEVEE_renderpasses_init` as the `e_data.vertcode` can be uninitialized.
*/
if (g_data->render_passes & EEVEE_RENDER_PASS_ENVIRONMENT) {
struct Scene *scene = draw_ctx->scene;
struct World *wo = scene->world;
if (wo && wo->use_nodes) {
EEVEE_material_get(vedata, scene, NULL, wo, VAR_WORLD_BACKGROUND);
}
}
}
}
void EEVEE_materials_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
const DRWContextState *draw_ctx = DRW_context_state_get();
/* Create Material Ghash */
{
stl->g_data->material_hash = BLI_ghash_ptr_new("Eevee_material ghash");
if (sldata->material_cache == NULL) {
sldata->material_cache = BLI_memblock_create(sizeof(EeveeMaterialCache));
}
else {
BLI_memblock_clear(sldata->material_cache, NULL);
}
}
{
DRW_PASS_CREATE(psl->background_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
DRWShadingGroup *grp = NULL;
EEVEE_lookdev_cache_init(vedata, sldata, psl->background_ps, NULL, &grp);
if (grp == NULL) {
Scene *scene = draw_ctx->scene;
World *world = (scene->world) ? scene->world : EEVEE_world_default_get();
const int options = VAR_WORLD_BACKGROUND;
struct GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, NULL, world, options);
grp = DRW_shgroup_material_create(gpumat, psl->background_ps);
DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1);
}
DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo);
DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo);
DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo);
DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &stl->g_data->renderpass_ubo);
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
}
#define EEVEE_PASS_CREATE(pass, state) \
do { \
DRW_PASS_CREATE(psl->pass##_ps, state); \
DRW_PASS_CREATE(psl->pass##_cull_ps, state | DRW_STATE_CULL_BACK); \
DRW_pass_link(psl->pass##_ps, psl->pass##_cull_ps); \
} while (0)
#define EEVEE_CLIP_PASS_CREATE(pass, state) \
do { \
DRWState st = state | DRW_STATE_CLIP_PLANES; \
DRW_PASS_INSTANCE_CREATE(psl->pass##_clip_ps, psl->pass##_ps, st); \
DRW_PASS_INSTANCE_CREATE( \
psl->pass##_clip_cull_ps, psl->pass##_cull_ps, st | DRW_STATE_CULL_BACK); \
DRW_pass_link(psl->pass##_clip_ps, psl->pass##_clip_cull_ps); \
} while (0)
{
DRWState state_depth = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
DRWState state_shading = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_CLIP_PLANES;
DRWState state_sss = DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_ALWAYS;
EEVEE_PASS_CREATE(depth, state_depth);
EEVEE_CLIP_PASS_CREATE(depth, state_depth);
EEVEE_PASS_CREATE(depth_refract, state_depth);
EEVEE_CLIP_PASS_CREATE(depth_refract, state_depth);
EEVEE_PASS_CREATE(material, state_shading);
EEVEE_PASS_CREATE(material_refract, state_shading);
EEVEE_PASS_CREATE(material_sss, state_shading | state_sss);
}
{
/* Renderpass accumulation. */
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ADD_FULL;
/* Create an instance of each of these passes and link them together. */
DRWPass *passes[] = {
psl->material_ps,
psl->material_cull_ps,
psl->material_sss_ps,
psl->material_sss_cull_ps,
};
DRWPass *first = NULL, *last = NULL;
for (int i = 0; i < ARRAY_SIZE(passes); i++) {
DRWPass *pass = DRW_pass_create_instance("Renderpass Accumulation", passes[i], state);
if (first == NULL) {
first = last = pass;
}
else {
DRW_pass_link(last, pass);
last = pass;
}
}
psl->material_accum_ps = first;
/* Same for background */
DRW_PASS_INSTANCE_CREATE(psl->background_accum_ps, psl->background_ps, state);
}
{
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CLIP_PLANES;
DRW_PASS_CREATE(psl->transparent_pass, state);
}
{
DRW_PASS_CREATE(psl->update_noise_pass, DRW_STATE_WRITE_COLOR);
GPUShader *sh = EEVEE_shaders_update_noise_sh_get();
DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->update_noise_pass);
DRW_shgroup_uniform_texture(grp, "blueNoise", e_data.noise_tex);
DRW_shgroup_uniform_vec3(grp, "offsets", e_data.noise_offsets, 1);
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
}
}
BLI_INLINE void material_shadow(EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
Material *ma,
bool is_hair,
EeveeMaterialCache *emc)
{
EEVEE_PrivateData *pd = vedata->stl->g_data;
EEVEE_PassList *psl = vedata->psl;
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
if (ma->blend_shadow != MA_BS_NONE) {
/* Shadow Pass */
const bool use_shadow_shader = ma->use_nodes && ma->nodetree &&
ELEM(ma->blend_shadow, MA_BS_CLIP, MA_BS_HASHED);
int mat_options = VAR_MAT_MESH | VAR_MAT_DEPTH;
SET_FLAG_FROM_TEST(mat_options, use_shadow_shader, VAR_MAT_HASH);
SET_FLAG_FROM_TEST(mat_options, is_hair, VAR_MAT_HAIR);
GPUMaterial *gpumat = (use_shadow_shader) ?
EEVEE_material_get(vedata, scene, ma, NULL, mat_options) :
EEVEE_material_default_get(scene, ma, mat_options);
/* Avoid possible confusion with depth pre-pass options. */
int option = KEY_SHADOW;
SET_FLAG_FROM_TEST(option, is_hair, KEY_HAIR);
/* Search for the same shaders usage in the pass. */
struct GPUShader *sh = GPU_material_get_shader(gpumat);
void *cache_key = (char *)sh + option;
DRWShadingGroup *grp, **grp_p;
if (BLI_ghash_ensure_p(pd->material_hash, cache_key, (void ***)&grp_p)) {
/* This GPUShader has already been used by another material.
* Add new shading group just after to avoid shader switching cost. */
grp = DRW_shgroup_create_sub(*grp_p);
}
else {
*grp_p = grp = DRW_shgroup_create(sh, psl->shadow_pass);
EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false);
}
DRW_shgroup_add_material_resources(grp, gpumat);
emc->shadow_grp = grp;
emc->shadow_grp_p = grp_p;
}
else {
emc->shadow_grp = NULL;
emc->shadow_grp_p = NULL;
}
}
static EeveeMaterialCache material_opaque(EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
Material *ma,
const bool is_hair)
{
EEVEE_EffectsInfo *effects = vedata->stl->effects;
EEVEE_PrivateData *pd = vedata->stl->g_data;
EEVEE_PassList *psl = vedata->psl;
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
const bool do_cull = !is_hair && (ma->blend_flag & MA_BL_CULL_BACKFACE) != 0;
const bool use_gpumat = (ma->use_nodes && ma->nodetree);
const bool use_ssrefract = use_gpumat && ((ma->blend_flag & MA_BL_SS_REFRACTION) != 0) &&
((effects->enabled_effects & EFFECT_REFRACT) != 0);
const bool use_depth_shader = use_gpumat && ELEM(ma->blend_method, MA_BM_CLIP, MA_BM_HASHED);
/* HACK: Assume the struct will never be smaller than our variations.
* This allow us to only keep one ghash and avoid bigger keys comparisons/hashing. */
void *key = (char *)ma + is_hair;
/* Search for other material instances (sharing the same Material data-block). */
EeveeMaterialCache **emc_p, *emc;
if (BLI_ghash_ensure_p(pd->material_hash, key, (void ***)&emc_p)) {
return **emc_p;
}
*emc_p = emc = BLI_memblock_alloc(sldata->material_cache);
material_shadow(vedata, sldata, ma, is_hair, emc);
{
/* Depth Pass */
int mat_options = VAR_MAT_MESH | VAR_MAT_DEPTH;
SET_FLAG_FROM_TEST(mat_options, use_ssrefract, VAR_MAT_REFRACT);
SET_FLAG_FROM_TEST(mat_options, use_depth_shader, VAR_MAT_HASH);
SET_FLAG_FROM_TEST(mat_options, is_hair, VAR_MAT_HAIR);
GPUMaterial *gpumat = (use_depth_shader) ?
EEVEE_material_get(vedata, scene, ma, NULL, mat_options) :
EEVEE_material_default_get(scene, ma, mat_options);
int option = 0;
SET_FLAG_FROM_TEST(option, do_cull, KEY_CULL);
SET_FLAG_FROM_TEST(option, use_ssrefract, KEY_REFRACT);
DRWPass *depth_ps = (DRWPass *[]){
psl->depth_ps,
psl->depth_cull_ps,
psl->depth_refract_ps,
psl->depth_refract_cull_ps,
}[option];
/* Hair are rendered inside the non-cull pass but needs to have a separate cache key. */
SET_FLAG_FROM_TEST(option, is_hair, KEY_HAIR);
/* Search for the same shaders usage in the pass. */
struct GPUShader *sh = GPU_material_get_shader(gpumat);
void *cache_key = (char *)sh + option;
DRWShadingGroup *grp, **grp_p;
if (BLI_ghash_ensure_p(pd->material_hash, cache_key, (void ***)&grp_p)) {
/* This GPUShader has already been used by another material.
* Add new shading group just after to avoid shader switching cost. */
grp = DRW_shgroup_create_sub(*grp_p);
}
else {
*grp_p = grp = DRW_shgroup_create(sh, depth_ps);
EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false);
}
DRW_shgroup_add_material_resources(grp, gpumat);
emc->depth_grp = grp;
emc->depth_grp_p = grp_p;
}
{
/* Shading Pass */
int mat_options = VAR_MAT_MESH;
SET_FLAG_FROM_TEST(mat_options, use_ssrefract, VAR_MAT_REFRACT);
SET_FLAG_FROM_TEST(mat_options, is_hair, VAR_MAT_HAIR);
GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options);
const bool use_sss = GPU_material_flag_get(gpumat, GPU_MATFLAG_SSS);
int ssr_id = (((effects->enabled_effects & EFFECT_SSR) != 0) && !use_ssrefract) ? 1 : 0;
int option = (use_ssrefract ? 0 : (use_sss ? 1 : 2)) * 2 + do_cull;
DRWPass *shading_pass = (DRWPass *[]){
psl->material_refract_ps,
psl->material_refract_cull_ps,
psl->material_sss_ps,
psl->material_sss_cull_ps,
psl->material_ps,
psl->material_cull_ps,
}[option];
/* Hair are rendered inside the non-cull pass but needs to have a separate cache key */
option = option * 2 + is_hair;
/* Search for the same shaders usage in the pass. */
/* HACK: Assume the struct will never be smaller than our variations.
* This allow us to only keep one ghash and avoid bigger keys comparisons/hashing. */
BLI_assert(option <= 16);
struct GPUShader *sh = GPU_material_get_shader(gpumat);
void *cache_key = (char *)sh + option;
DRWShadingGroup *grp, **grp_p;
if (BLI_ghash_ensure_p(pd->material_hash, cache_key, (void ***)&grp_p)) {
/* This GPUShader has already been used by another material.
* Add new shading group just after to avoid shader switching cost. */
grp = DRW_shgroup_create_sub(*grp_p);
}
else {
*grp_p = grp = DRW_shgroup_create(sh, shading_pass);
EEVEE_material_bind_resources(
grp, gpumat, sldata, vedata, &ssr_id, &ma->refract_depth, use_ssrefract, false);
}
DRW_shgroup_add_material_resources(grp, gpumat);
if (use_sss) {
EEVEE_subsurface_add_pass(sldata, vedata, ma, grp, gpumat);
}
emc->shading_grp = grp;
emc->shading_grp_p = grp_p;
emc->shading_gpumat = gpumat;
}
return *emc;
}
static EeveeMaterialCache material_transparent(EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
Material *ma)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
EEVEE_PassList *psl = vedata->psl;
EEVEE_EffectsInfo *effects = vedata->stl->effects;
EeveeMaterialCache emc = {0};
const bool do_cull = (ma->blend_flag & MA_BL_CULL_BACKFACE) != 0;
const bool use_gpumat = ma->use_nodes && ma->nodetree;
const bool use_ssrefract = use_gpumat && ((ma->blend_flag & MA_BL_SS_REFRACTION) != 0) &&
((effects->enabled_effects & EFFECT_REFRACT) != 0);
const bool use_prepass = ((ma->blend_flag & MA_BL_HIDE_BACKFACE) != 0);
DRWState cur_state;
DRWState all_state = (DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_CULL_BACK |
DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_DEPTH_EQUAL |
DRW_STATE_BLEND_CUSTOM);
material_shadow(vedata, sldata, ma, false, &emc);
if (use_prepass) {
/* Depth prepass */
int mat_options = VAR_MAT_MESH | VAR_MAT_DEPTH;
GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options);
struct GPUShader *sh = GPU_material_get_shader(gpumat);
DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->transparent_pass);
EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, true);
DRW_shgroup_add_material_resources(grp, gpumat);
cur_state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
cur_state |= (do_cull) ? DRW_STATE_CULL_BACK : 0;
DRW_shgroup_state_disable(grp, all_state);
DRW_shgroup_state_enable(grp, cur_state);
emc.depth_grp = grp;
}
{
/* Shading */
int ssr_id = -1; /* TODO transparent SSR */
int mat_options = VAR_MAT_MESH | VAR_MAT_BLEND;
SET_FLAG_FROM_TEST(mat_options, use_ssrefract, VAR_MAT_REFRACT);
GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options);
DRWShadingGroup *grp = DRW_shgroup_create(GPU_material_get_shader(gpumat),
psl->transparent_pass);
EEVEE_material_bind_resources(
grp, gpumat, sldata, vedata, &ssr_id, &ma->refract_depth, use_ssrefract, true);
DRW_shgroup_add_material_resources(grp, gpumat);
cur_state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM;
cur_state |= (use_prepass) ? DRW_STATE_DEPTH_EQUAL : DRW_STATE_DEPTH_LESS_EQUAL;
cur_state |= (do_cull) ? DRW_STATE_CULL_BACK : 0;
/* Disable other blend modes and use the one we want. */
DRW_shgroup_state_disable(grp, all_state);
DRW_shgroup_state_enable(grp, cur_state);
emc.shading_grp = grp;
emc.shading_gpumat = gpumat;
}
return emc;
}
/* Return correct material or empty default material if slot is empty. */
BLI_INLINE Material *eevee_object_material_get(Object *ob, int slot, bool holdout)
{
if (holdout) {
return BKE_material_default_holdout();
}
Material *ma = BKE_object_material_get(ob, slot + 1);
if (ma == NULL) {
if (ob->type == OB_VOLUME) {
ma = BKE_material_default_volume();
}
else {
ma = BKE_material_default_surface();
}
}
return ma;
}
BLI_INLINE EeveeMaterialCache eevee_material_cache_get(
EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob, int slot, bool is_hair)
{
const bool holdout = (ob->base_flag & BASE_HOLDOUT) != 0;
EeveeMaterialCache matcache;
Material *ma = eevee_object_material_get(ob, slot, holdout);
switch (ma->blend_method) {
case MA_BM_BLEND:
if (!is_hair) {
matcache = material_transparent(vedata, sldata, ma);
break;
}
ATTR_FALLTHROUGH;
case MA_BM_SOLID:
case MA_BM_CLIP:
case MA_BM_HASHED:
default:
matcache = material_opaque(vedata, sldata, ma, is_hair);
break;
}
return matcache;
}
static void eevee_hair_cache_populate(EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
Object *ob,
ParticleSystem *psys,
ModifierData *md,
int matnr,
bool *cast_shadow)
{
EeveeMaterialCache matcache = eevee_material_cache_get(vedata, sldata, ob, matnr - 1, true);
if (matcache.depth_grp) {
*matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp);
}
if (matcache.shading_grp) {
*matcache.shading_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shading_grp);
}
if (matcache.shadow_grp) {
*matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp);
*cast_shadow = true;
}
EEVEE_motion_blur_hair_cache_populate(sldata, vedata, ob, psys, md);
}
#define ADD_SHGROUP_CALL(shgrp, ob, geom, oedata) \
do { \
if (oedata) { \
DRW_shgroup_call_with_callback(shgrp, geom, ob, oedata); \
} \
else { \
DRW_shgroup_call(shgrp, geom, ob); \
} \
} while (0)
#define ADD_SHGROUP_CALL_SAFE(shgrp, ob, geom, oedata) \
do { \
if (shgrp) { \
ADD_SHGROUP_CALL(shgrp, ob, geom, oedata); \
} \
} while (0)
#define MATCACHE_AS_ARRAY(matcache, member, materials_len, output_array) \
for (int i = 0; i < materials_len; i++) { \
output_array[i] = matcache[i].member; \
}
void EEVEE_materials_cache_populate(EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
Object *ob,
bool *cast_shadow)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) &&
!DRW_state_is_image_render();
/* First get materials for this mesh. */
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) {
const int materials_len = DRW_cache_object_material_count_get(ob);
EeveeMaterialCache *matcache = BLI_array_alloca(matcache, materials_len);
for (int i = 0; i < materials_len; i++) {
matcache[i] = eevee_material_cache_get(vedata, sldata, ob, i, false);
}
/* Only support single volume material for now. */
/* XXX We rely on the previously compiled surface shader
* to know if the material has a "volume nodetree".
*/
bool use_volume_material = (matcache[0].shading_gpumat &&
GPU_material_has_volume_output(matcache[0].shading_gpumat));
if ((ob->dt >= OB_SOLID) || DRW_state_is_scene_render()) {
if (use_sculpt_pbvh) {
struct DRWShadingGroup **shgrps_array = BLI_array_alloca(shgrps_array, materials_len);
MATCACHE_AS_ARRAY(matcache, shading_grp, materials_len, shgrps_array);
DRW_shgroup_call_sculpt_with_materials(shgrps_array, materials_len, ob);
MATCACHE_AS_ARRAY(matcache, depth_grp, materials_len, shgrps_array);
DRW_shgroup_call_sculpt_with_materials(shgrps_array, materials_len, ob);
MATCACHE_AS_ARRAY(matcache, shadow_grp, materials_len, shgrps_array);
DRW_shgroup_call_sculpt_with_materials(shgrps_array, materials_len, ob);
}
else {
struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len);
MATCACHE_AS_ARRAY(matcache, shading_gpumat, materials_len, gpumat_array);
/* Get per-material split surface */
struct GPUBatch **mat_geom = DRW_cache_object_surface_material_get(
ob, gpumat_array, materials_len);
if (mat_geom) {
for (int i = 0; i < materials_len; i++) {
if (mat_geom[i] == NULL) {
continue;
}
/* Do not render surface if we are rendering a volume object
* and do not have a surface closure. */
if (use_volume_material &&
(gpumat_array[i] && !GPU_material_has_surface_output(gpumat_array[i]))) {
continue;
}
/* XXX TODO rewrite this to include the dupli objects.
* This means we cannot exclude dupli objects from reflections!!! */
EEVEE_ObjectEngineData *oedata = NULL;
if ((ob->base_flag & BASE_FROM_DUPLI) == 0) {
oedata = EEVEE_object_data_ensure(ob);
oedata->ob = ob;
oedata->test_data = &sldata->probes->vis_data;
}
ADD_SHGROUP_CALL(matcache[i].shading_grp, ob, mat_geom[i], oedata);
ADD_SHGROUP_CALL_SAFE(matcache[i].depth_grp, ob, mat_geom[i], oedata);
ADD_SHGROUP_CALL_SAFE(matcache[i].shadow_grp, ob, mat_geom[i], oedata);
*cast_shadow = *cast_shadow || (matcache[i].shadow_grp != NULL);
}
}
}
/* Motion Blur Vectors. */
EEVEE_motion_blur_cache_populate(sldata, vedata, ob);
}
/* Volumetrics */
if (use_volume_material) {
EEVEE_volumes_cache_object_add(sldata, vedata, scene, ob);
}
}
}
void EEVEE_particle_hair_cache_populate(EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
Object *ob,
bool *cast_shadow)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
if (ob->type == OB_MESH) {
if (ob != draw_ctx->object_edit) {
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type != eModifierType_ParticleSystem) {
continue;
}
ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) {
continue;
}
ParticleSettings *part = psys->part;
const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
if (draw_as != PART_DRAW_PATH) {
continue;
}
eevee_hair_cache_populate(vedata, sldata, ob, psys, md, part->omat, cast_shadow);
}
}
}
}
void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
Object *ob,
bool *cast_shadow)
{
eevee_hair_cache_populate(vedata, sldata, ob, NULL, NULL, HAIR_MATERIAL_NR, cast_shadow);
}
void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
{
EEVEE_PrivateData *pd = vedata->stl->g_data;
EEVEE_EffectsInfo *effects = vedata->stl->effects;
BLI_ghash_free(pd->material_hash, NULL, NULL);
pd->material_hash = NULL;
SET_FLAG_FROM_TEST(effects->enabled_effects, effects->sss_surface_count > 0, EFFECT_SSS);
}
void EEVEE_materials_free(void)
{
DRW_TEXTURE_FREE_SAFE(e_data.util_tex);
DRW_TEXTURE_FREE_SAFE(e_data.noise_tex);
}
/* -------------------------------------------------------------------- */
/** \name Render Passes
* \{ */
void EEVEE_material_renderpasses_init(EEVEE_Data *vedata)
{
EEVEE_PrivateData *pd = vedata->stl->g_data;
/* For diffuse and glossy we calculate the final light + color buffer where we extract the
* light from by dividing by the color buffer. When one the light is requested we also tag
* the color buffer to do the extraction. */
if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) {
pd->render_passes |= EEVEE_RENDER_PASS_DIFFUSE_COLOR;
}
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) {
pd->render_passes |= EEVEE_RENDER_PASS_SPECULAR_COLOR;
}
}
static void material_renderpass_init(EEVEE_FramebufferList *fbl,
GPUTexture **output_tx,
const eGPUTextureFormat format,
const bool do_clear)
{
DRW_texture_ensure_fullscreen_2d(output_tx, format, 0);
/* Clear texture. */
if (do_clear) {
const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
/* TODO(fclem): replace by GPU_texture_clear once it is fast. */
GPU_framebuffer_texture_attach(fbl->material_accum_fb, *output_tx, 0, 0);
GPU_framebuffer_bind(fbl->material_accum_fb);
GPU_framebuffer_clear_color(fbl->material_accum_fb, clear);
GPU_framebuffer_bind(fbl->main_fb);
GPU_framebuffer_texture_detach(fbl->material_accum_fb, *output_tx);
}
}
void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
EEVEE_TextureList *txl = vedata->txl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_PrivateData *pd = stl->g_data;
/* Should be enough precision for many samples. */
const eGPUTextureFormat texture_format = (tot_samples > 128) ? GPU_RGBA32F : GPU_RGBA16F;
const bool do_clear = (effects->taa_current_sample == 1);
/* Create FrameBuffer. */
GPU_framebuffer_ensure_config(&fbl->material_accum_fb,
{GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_LEAVE});
if (pd->render_passes & EEVEE_RENDER_PASS_ENVIRONMENT) {
material_renderpass_init(fbl, &txl->env_accum, texture_format, do_clear);
}
if (pd->render_passes & EEVEE_RENDER_PASS_EMIT) {
material_renderpass_init(fbl, &txl->emit_accum, texture_format, do_clear);
}
if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_COLOR) {
material_renderpass_init(fbl, &txl->diff_color_accum, texture_format, do_clear);
}
if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) {
material_renderpass_init(fbl, &txl->diff_light_accum, texture_format, do_clear);
}
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_COLOR) {
material_renderpass_init(fbl, &txl->spec_color_accum, texture_format, do_clear);
}
if (pd->render_passes & EEVEE_RENDER_PASS_AOV) {
for (int aov_index = 0; aov_index < pd->num_aovs_used; aov_index++) {
material_renderpass_init(fbl, &txl->aov_surface_accum[aov_index], texture_format, do_clear);
}
}
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) {
material_renderpass_init(fbl, &txl->spec_light_accum, texture_format, do_clear);
if (effects->enabled_effects & EFFECT_SSR) {
EEVEE_reflection_output_init(sldata, vedata, tot_samples);
}
}
}
static void material_renderpass_accumulate(EEVEE_FramebufferList *fbl,
DRWPass *renderpass,
DRWPass *renderpass2,
EEVEE_PrivateData *pd,
GPUTexture *output_tx,
struct GPUUniformBuf *renderpass_option_ubo)
{
GPU_framebuffer_texture_attach(fbl->material_accum_fb, output_tx, 0, 0);
GPU_framebuffer_bind(fbl->material_accum_fb);
pd->renderpass_ubo = renderpass_option_ubo;
DRW_draw_pass(renderpass);
if (renderpass2) {
DRW_draw_pass(renderpass2);
}
GPU_framebuffer_texture_detach(fbl->material_accum_fb, output_tx);
}
void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_PassList *psl = vedata->psl;
EEVEE_PrivateData *pd = vedata->stl->g_data;
EEVEE_EffectsInfo *effects = vedata->stl->effects;
EEVEE_TextureList *txl = vedata->txl;
if (fbl->material_accum_fb != NULL) {
DRWPass *material_accum_ps = psl->material_accum_ps;
DRWPass *background_accum_ps = psl->background_accum_ps;
if (pd->render_passes & EEVEE_RENDER_PASS_ENVIRONMENT) {
material_renderpass_accumulate(
fbl, background_accum_ps, NULL, pd, txl->env_accum, sldata->renderpass_ubo.environment);
}
if (pd->render_passes & EEVEE_RENDER_PASS_EMIT) {
material_renderpass_accumulate(
fbl, material_accum_ps, NULL, pd, txl->emit_accum, sldata->renderpass_ubo.emit);
}
if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_COLOR) {
material_renderpass_accumulate(fbl,
material_accum_ps,
NULL,
pd,
txl->diff_color_accum,
sldata->renderpass_ubo.diff_color);
}
if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) {
material_renderpass_accumulate(fbl,
material_accum_ps,
NULL,
pd,
txl->diff_light_accum,
sldata->renderpass_ubo.diff_light);
if (effects->enabled_effects & EFFECT_SSS) {
EEVEE_subsurface_output_accumulate(sldata, vedata);
}
}
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_COLOR) {
material_renderpass_accumulate(fbl,
material_accum_ps,
NULL,
pd,
txl->spec_color_accum,
sldata->renderpass_ubo.spec_color);
}
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) {
material_renderpass_accumulate(fbl,
material_accum_ps,
NULL,
pd,
txl->spec_light_accum,
sldata->renderpass_ubo.spec_light);
if (effects->enabled_effects & EFFECT_SSR) {
EEVEE_reflection_output_accumulate(sldata, vedata);
}
}
if (pd->render_passes & EEVEE_RENDER_PASS_AOV) {
for (int aov_index = 0; aov_index < pd->num_aovs_used; aov_index++) {
material_renderpass_accumulate(fbl,
material_accum_ps,
background_accum_ps,
pd,
txl->aov_surface_accum[aov_index],
sldata->renderpass_ubo.aovs[aov_index]);
}
}
/* Free unused aov textures. */
for (int aov_index = pd->num_aovs_used; aov_index < MAX_AOVS; aov_index++) {
DRW_TEXTURE_FREE_SAFE(txl->aov_surface_accum[aov_index]);
}
/* Restore default. */
pd->renderpass_ubo = sldata->renderpass_ubo.combined;
GPU_framebuffer_bind(fbl->main_fb);
}
}
/** \} */