WIP: eevee-next-world-irradiance #108304
|
@ -148,6 +148,7 @@ set(SRC
|
|||
engines/eevee_next/eevee_material.cc
|
||||
engines/eevee_next/eevee_motion_blur.cc
|
||||
engines/eevee_next/eevee_pipeline.cc
|
||||
engines/eevee_next/eevee_reflection_probes.cc
|
||||
engines/eevee_next/eevee_renderbuffers.cc
|
||||
engines/eevee_next/eevee_sampling.cc
|
||||
engines/eevee_next/eevee_shader.cc
|
||||
|
@ -286,6 +287,7 @@ set(SRC
|
|||
engines/eevee_next/eevee_material.hh
|
||||
engines/eevee_next/eevee_motion_blur.hh
|
||||
engines/eevee_next/eevee_pipeline.hh
|
||||
engines/eevee_next/eevee_reflection_probes.hh
|
||||
engines/eevee_next/eevee_renderbuffers.hh
|
||||
engines/eevee_next/eevee_sampling.hh
|
||||
engines/eevee_next/eevee_shader.hh
|
||||
|
@ -478,6 +480,7 @@ set(GLSL_SRC
|
|||
engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_motion_blur_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_nodetree_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_sampling_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_debug_frag.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_lib.glsl
|
||||
|
|
|
@ -95,6 +95,7 @@
|
|||
/* Only during shadow rendering. */
|
||||
#define SHADOW_RENDER_MAP_SLOT 13
|
||||
#define RBUFS_UTILITY_TEX_SLOT 14
|
||||
#define REFLECTION_PROBE_TEX_SLOT 16
|
||||
|
||||
/* Images. */
|
||||
#define RBUFS_NORMAL_SLOT 0
|
||||
|
|
|
@ -71,6 +71,7 @@ void Instance::init(const int2 &output_res,
|
|||
motion_blur.init();
|
||||
main_view.init();
|
||||
irradiance_cache.init();
|
||||
reflection_probes.init();
|
||||
}
|
||||
|
||||
void Instance::set_time(float time)
|
||||
|
@ -117,6 +118,7 @@ void Instance::begin_sync()
|
|||
hiz_buffer.sync();
|
||||
main_view.sync();
|
||||
world.sync();
|
||||
reflection_probes.sync();
|
||||
film.sync();
|
||||
irradiance_cache.sync();
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "eevee_material.hh"
|
||||
#include "eevee_motion_blur.hh"
|
||||
#include "eevee_pipeline.hh"
|
||||
#include "eevee_reflection_probes.hh"
|
||||
#include "eevee_renderbuffers.hh"
|
||||
#include "eevee_sampling.hh"
|
||||
#include "eevee_shader.hh"
|
||||
|
@ -51,6 +52,7 @@ class Instance {
|
|||
PipelineModule pipelines;
|
||||
ShadowModule shadows;
|
||||
LightModule lights;
|
||||
ReflectionProbeModule reflection_probes;
|
||||
VelocityModule velocity;
|
||||
MotionBlurModule motion_blur;
|
||||
DepthOfField depth_of_field;
|
||||
|
@ -97,6 +99,7 @@ class Instance {
|
|||
pipelines(*this),
|
||||
shadows(*this),
|
||||
lights(*this),
|
||||
reflection_probes(*this),
|
||||
velocity(*this),
|
||||
motion_blur(*this),
|
||||
depth_of_field(*this),
|
||||
|
|
|
@ -62,6 +62,75 @@ void WorldPipeline::render(View &view)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name World Probe Pipeline
|
||||
* \{ */
|
||||
|
||||
void WorldProbePipeline::sync()
|
||||
{
|
||||
for (int face : IndexRange(6)) {
|
||||
CubemapSide &side = sides_[face];
|
||||
/* View */
|
||||
float4x4 view_m4 = cubeface_mat(face);
|
||||
float4x4 win_m4;
|
||||
cubeface_winmat_get(win_m4, 1.0f, 10.0f);
|
||||
side.view.sync(view_m4, win_m4);
|
||||
|
||||
side.cubemap_face_ps.init();
|
||||
side.cubemap_face_ps.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldProbePipeline::sync(GPUMaterial *gpumat)
|
||||
{
|
||||
for (int face : IndexRange(6)) {
|
||||
sync(gpumat, face);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldProbePipeline::sync(GPUMaterial *gpumat, int face)
|
||||
{
|
||||
Manager &manager = *inst_.manager;
|
||||
|
||||
CubemapSide &side = sides_[face];
|
||||
|
||||
/* Framebuffer. */
|
||||
Texture &cubemap = inst_.reflection_probes.cubemaps_tx_;
|
||||
side.cubemap_face_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_CUBEFACE(cubemap, face));
|
||||
|
||||
ResourceHandle handle = manager.resource_handle(float4x4::identity());
|
||||
|
||||
side.cubemap_face_ps.framebuffer_set(&side.cubemap_face_fb);
|
||||
side.cubemap_face_ps.material_set(manager, gpumat);
|
||||
|
||||
side.cubemap_face_ps.draw(DRW_cache_fullscreen_quad_get(), handle);
|
||||
/* To allow opaque pass rendering over it. */
|
||||
side.cubemap_face_ps.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
|
||||
}
|
||||
|
||||
void WorldProbePipeline::render()
|
||||
{
|
||||
GPUFrameBuffer *previous_framebuffer = GPU_framebuffer_active_get();
|
||||
|
||||
GPU_debug_group_begin("World.Probe");
|
||||
for (int face : IndexRange(6)) {
|
||||
sides_[face].render(inst_);
|
||||
}
|
||||
GPU_debug_group_end();
|
||||
|
||||
GPU_texture_update_mipmap_chain(inst_.reflection_probes.cubemaps_tx_);
|
||||
if (previous_framebuffer) {
|
||||
GPU_framebuffer_bind(previous_framebuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldProbePipeline::CubemapSide::render(Instance &instance)
|
||||
{
|
||||
instance.manager->submit(cubemap_face_ps, view);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Shadow Pipeline
|
||||
*
|
||||
|
@ -387,6 +456,7 @@ void DeferredLayer::end_sync()
|
|||
inst_.shadows.bind_resources(&eval_light_ps_);
|
||||
inst_.sampling.bind_resources(&eval_light_ps_);
|
||||
inst_.hiz_buffer.bind_resources(&eval_light_ps_);
|
||||
inst_.reflection_probes.bind_resources(&eval_light_ps_);
|
||||
|
||||
eval_light_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS);
|
||||
eval_light_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
|
|
|
@ -43,6 +43,45 @@ class WorldPipeline {
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name World Probe Pipeline
|
||||
*
|
||||
* Render reflection probe of the world background.
|
||||
* \{ */
|
||||
|
||||
class WorldProbePipeline {
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
struct CubemapSide {
|
||||
PassSimple cubemap_face_ps;
|
||||
View view;
|
||||
Framebuffer cubemap_face_fb;
|
||||
void render(Instance &instance);
|
||||
};
|
||||
|
||||
CubemapSide sides_[6] = {
|
||||
{{"PosX"}, {"PosX"}},
|
||||
{{"NegX"}, {"NegX"}},
|
||||
{{"PosY"}, {"PosY"}},
|
||||
{{"NegY"}, {"NegY"}},
|
||||
{{"PosZ"}, {"PosZ"}},
|
||||
{{"NegZ"}, {"NegZ"}},
|
||||
};
|
||||
|
||||
public:
|
||||
WorldProbePipeline(Instance &inst) : inst_(inst){};
|
||||
|
||||
void sync();
|
||||
void sync(GPUMaterial *gpumat);
|
||||
void render();
|
||||
|
||||
private:
|
||||
void sync(GPUMaterial *gpumat, int face);
|
||||
}; // namespace blender::eevee
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Shadow Pass
|
||||
*
|
||||
|
@ -266,6 +305,7 @@ class UtilityTexture : public Texture {
|
|||
class PipelineModule {
|
||||
public:
|
||||
WorldPipeline world;
|
||||
WorldProbePipeline world_probe;
|
||||
DeferredPipeline deferred;
|
||||
ForwardPipeline forward;
|
||||
ShadowPipeline shadow;
|
||||
|
@ -273,13 +313,15 @@ class PipelineModule {
|
|||
UtilityTexture utility_tx;
|
||||
|
||||
public:
|
||||
PipelineModule(Instance &inst) : world(inst), deferred(inst), forward(inst), shadow(inst){};
|
||||
PipelineModule(Instance &inst)
|
||||
: world(inst), world_probe(inst), deferred(inst), forward(inst), shadow(inst){};
|
||||
|
||||
void begin_sync()
|
||||
{
|
||||
deferred.begin_sync();
|
||||
forward.sync();
|
||||
shadow.sync();
|
||||
world_probe.sync();
|
||||
}
|
||||
|
||||
void end_sync()
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
#include "eevee_reflection_probes.hh"
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
void ReflectionProbeModule::init()
|
||||
{
|
||||
if (cubemaps_.is_empty()) {
|
||||
cubemaps_.reserve(MAX_PROBES);
|
||||
|
||||
/* Initialize the world cubemap. */
|
||||
ReflectionProbe world_cubemap;
|
||||
world_cubemap.type = ReflectionProbe::Type::World;
|
||||
world_cubemap.is_dirty = true;
|
||||
cubemaps_.append(world_cubemap);
|
||||
|
||||
cubemaps_tx_.ensure_cube_array(GPU_RGBA16F,
|
||||
MAX_RESOLUTION,
|
||||
MAX_PROBES,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT,
|
||||
NULL,
|
||||
12);
|
||||
GPU_texture_mipmap_mode(cubemaps_tx_, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
void ReflectionProbeModule::sync()
|
||||
{
|
||||
for (int index : IndexRange(MAX_PROBES)) {
|
||||
ReflectionProbe &cubemap = cubemaps_[index];
|
||||
if (!cubemap.needs_update()) {
|
||||
continue;
|
||||
}
|
||||
sync(cubemap);
|
||||
cubemap.is_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ReflectionProbeModule::sync(const ReflectionProbe &cubemap)
|
||||
{
|
||||
if (cubemap.type == ReflectionProbe::Type::World) {
|
||||
GPUMaterial *world_material = instance_.world.get_world_material();
|
||||
instance_.pipelines.world_probe.sync(world_material);
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
void ReflectionProbeModule::set_world_dirty()
|
||||
{
|
||||
cubemaps_[WORLD_SLOT].is_dirty = true;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name World
|
||||
*
|
||||
* \{ */
|
||||
|
||||
bool ReflectionProbe::needs_update() const
|
||||
{
|
||||
return type != Type::Unused && is_dirty;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
|
@ -0,0 +1,89 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*
|
||||
* Cubemaps
|
||||
*
|
||||
* Cubemaps record light from different locations in the scene. These cubemaps are used to add
|
||||
* environment and indirect lighting from light probes.
|
||||
*
|
||||
* - Although we have Global illumination light probes can still be used for situations that
|
||||
* GI doesn't work. For example semi-transparent surfaces.
|
||||
* - The first cubemap is always reserved for the world shading.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "eevee_shader_shared.hh"
|
||||
|
||||
#include "BKE_cryptomatte.hh"
|
||||
|
||||
extern "C" {
|
||||
struct Material;
|
||||
}
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
class WorldProbePipeline;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Reflection Probes
|
||||
* \{ */
|
||||
class ReflectionProbe {
|
||||
public:
|
||||
enum Type { Unused, World };
|
||||
|
||||
Type type;
|
||||
bool is_dirty = false;
|
||||
|
||||
bool needs_update() const;
|
||||
};
|
||||
|
||||
class ReflectionProbeModule {
|
||||
private:
|
||||
/** The max number of probes to track. */
|
||||
static constexpr int MAX_PROBES = 1;
|
||||
|
||||
/**
|
||||
* The maximum resolution of a cubemap side.
|
||||
*
|
||||
* Must be a power of two; intension to be used as a cubemap atlas.
|
||||
*/
|
||||
static constexpr int MAX_RESOLUTION = 2048;
|
||||
|
||||
/**
|
||||
* Index of the probe that is used for world background.
|
||||
*
|
||||
* NOTE: First probe always contains the world probe.
|
||||
*/
|
||||
static constexpr int WORLD_SLOT = 0;
|
||||
|
||||
Instance &instance_;
|
||||
|
||||
Vector<ReflectionProbe> cubemaps_;
|
||||
Texture cubemaps_tx_ = {"Probes"};
|
||||
|
||||
public:
|
||||
ReflectionProbeModule(Instance &instance) : instance_(instance) {}
|
||||
|
||||
void init();
|
||||
void set_world_dirty();
|
||||
|
||||
void sync();
|
||||
|
||||
template<typename T> void bind_resources(draw::detail::PassBase<T> *pass)
|
||||
{
|
||||
pass->bind_texture(REFLECTION_PROBE_TEX_SLOT, cubemaps_tx_);
|
||||
}
|
||||
|
||||
private:
|
||||
void sync(const ReflectionProbe &cubemap);
|
||||
|
||||
friend class WorldProbePipeline;
|
||||
};
|
||||
|
||||
} // namespace blender::eevee
|
|
@ -119,14 +119,13 @@ void ShadingView::render()
|
|||
GPU_framebuffer_clear_color_depth(combined_fb_, clear_color, 1.0f);
|
||||
|
||||
inst_.pipelines.world.render(render_view_new_);
|
||||
inst_.pipelines.world_probe.render();
|
||||
|
||||
/* TODO(fclem): Move it after the first prepass (and hiz update) once pipeline is stabilized. */
|
||||
inst_.lights.set_view(render_view_new_, extent_);
|
||||
|
||||
inst_.pipelines.deferred.render(render_view_new_, prepass_fb_, combined_fb_, extent_);
|
||||
|
||||
// inst_.lightprobes.draw_cache_display();
|
||||
|
||||
// inst_.lookdev.render_overlay(view_fb_);
|
||||
|
||||
inst_.pipelines.forward.render(render_view_new_, prepass_fb_, combined_fb_, rbufs.combined_tx);
|
||||
|
|
|
@ -90,7 +90,7 @@ void World::sync()
|
|||
WorldHandle &wo_handle = inst_.sync.sync_world(bl_world);
|
||||
|
||||
if (wo_handle.recalc != 0) {
|
||||
// inst_.lightprobes.set_world_dirty();
|
||||
inst_.reflection_probes.set_world_dirty();
|
||||
}
|
||||
wo_handle.reset_recalc_flag();
|
||||
|
||||
|
@ -111,6 +111,21 @@ void World::sync()
|
|||
inst_.pipelines.world.sync(gpumat);
|
||||
}
|
||||
|
||||
GPUMaterial *World::get_world_material()
|
||||
{
|
||||
::World *bl_world = inst_.scene->world;
|
||||
if (bl_world == nullptr) {
|
||||
bl_world = default_world_get();
|
||||
}
|
||||
|
||||
bNodeTree *ntree = (bl_world->nodetree && bl_world->use_nodes) ?
|
||||
bl_world->nodetree :
|
||||
default_tree.nodetree_get(bl_world);
|
||||
|
||||
GPUMaterial *gpumat = inst_.shaders.world_shader_get(bl_world, ntree);
|
||||
return gpumat;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
|
|
|
@ -62,6 +62,13 @@ class World {
|
|||
~World();
|
||||
|
||||
void sync();
|
||||
|
||||
/**
|
||||
* Get the world material.
|
||||
*
|
||||
* NOTE: this function should only be called after World::sync has been executed.
|
||||
*/
|
||||
GPUMaterial *get_world_material();
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_reflection_probe_lib.glsl)
|
||||
|
||||
void main()
|
||||
{
|
||||
|
@ -52,6 +53,7 @@ void main()
|
|||
vec3 diffuse_light = vec3(0.0);
|
||||
vec3 reflection_light = vec3(0.0);
|
||||
|
||||
light_world_eval(diffuse_data, reflection_data, P, V, diffuse_light, reflection_light);
|
||||
light_eval(
|
||||
diffuse_data, reflection_data, P, Ng, V, vP_z, thickness, diffuse_light, reflection_light);
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
void light_world_eval(ClosureDiffuse diffuse,
|
||||
ClosureReflection reflection,
|
||||
vec3 P,
|
||||
vec3 V,
|
||||
inout vec3 out_diffuse,
|
||||
inout vec3 out_specular)
|
||||
{
|
||||
float linear_roughness = fast_sqrt(reflection.roughness);
|
||||
/* TODO: This should be based by actual LOD?.*/
|
||||
float lod_cube_max = 12.0;
|
||||
float lod = linear_roughness * lod_cube_max;
|
||||
|
||||
vec3 R = -reflect(V, reflection.N);
|
||||
vec3 world_light = textureLod_cubemapArray(reflectionProbes, vec4(R, 0.0), lod).rgb;
|
||||
out_specular += world_light;
|
||||
}
|
|
@ -37,6 +37,7 @@ GPU_SHADER_CREATE_INFO(eevee_deferred_light)
|
|||
.additional_info("eevee_shared",
|
||||
"eevee_utility_texture",
|
||||
"eevee_light_data",
|
||||
"eevee_reflection_probe_data",
|
||||
"eevee_shadow_data",
|
||||
"eevee_deferred_base",
|
||||
"eevee_hiz_data",
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#include "eevee_defines.hh"
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Shared
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_reflection_probe_data)
|
||||
.sampler(REFLECTION_PROBE_TEX_SLOT, ImageType::FLOAT_CUBE_ARRAY, "reflectionProbes");
|
||||
|
||||
/** \} */
|
|
@ -674,7 +674,7 @@ class Texture : NonCopyable {
|
|||
float *data = nullptr,
|
||||
int mip_len = 1)
|
||||
{
|
||||
return ensure_impl(extent, extent, layers, mip_len, format, usage, data, false, true);
|
||||
return ensure_impl(extent, extent, layers, mip_len, format, usage, data, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1033,8 +1033,7 @@ class TextureRef : public Texture {
|
|||
* Dummy type to bind texture as image.
|
||||
* It is just a GPUTexture in disguise.
|
||||
*/
|
||||
class Image {
|
||||
};
|
||||
class Image {};
|
||||
|
||||
static inline Image *as_image(GPUTexture *tex)
|
||||
{
|
||||
|
|
|
@ -648,6 +648,7 @@ set(SRC_SHADER_CREATE_INFOS
|
|||
../draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh
|
||||
../draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
|
||||
../draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh
|
||||
../draw/engines/eevee_next/shaders/infos/eevee_reflection_probe_info.hh
|
||||
../draw/engines/eevee_next/shaders/infos/eevee_shadow_info.hh
|
||||
../draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh
|
||||
../draw/engines/gpencil/shaders/infos/gpencil_info.hh
|
||||
|
|
Loading…
Reference in New Issue