Eevee-Next: World Reflective Light #108149
|
@ -154,6 +154,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
|
||||
|
@ -296,6 +297,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
|
||||
|
@ -511,6 +513,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
|
||||
|
|
|
@ -108,6 +108,7 @@
|
|||
#define SHADOW_ATLAS_TEX_SLOT 5
|
||||
#define SSS_TRANSMITTANCE_TEX_SLOT 6
|
||||
#define IRRADIANCE_ATLAS_TEX_SLOT 7
|
||||
#define REFLECTION_PROBE_TEX_SLOT 8
|
||||
/* Only during shadow rendering. */
|
||||
#define SHADOW_RENDER_MAP_SLOT 4
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@ void Instance::init(const int2 &output_res,
|
|||
motion_blur.init();
|
||||
main_view.init();
|
||||
irradiance_cache.init();
|
||||
reflection_probes.init();
|
||||
}
|
||||
|
||||
void Instance::init_light_bake(Depsgraph *depsgraph, draw::Manager *manager)
|
||||
|
@ -290,6 +291,7 @@ void Instance::render_sample()
|
|||
|
||||
sampling.step();
|
||||
|
||||
capture_view.render();
|
||||
main_view.render();
|
||||
|
||||
motion_blur.step();
|
||||
|
|
|
@ -27,6 +27,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"
|
||||
|
@ -54,6 +55,7 @@ class Instance {
|
|||
PipelineModule pipelines;
|
||||
ShadowModule shadows;
|
||||
LightModule lights;
|
||||
ReflectionProbeModule reflection_probes;
|
||||
VelocityModule velocity;
|
||||
MotionBlurModule motion_blur;
|
||||
DepthOfField depth_of_field;
|
||||
|
@ -65,6 +67,7 @@ class Instance {
|
|||
Film film;
|
||||
RenderBuffers render_buffers;
|
||||
MainView main_view;
|
||||
CaptureView capture_view;
|
||||
World world;
|
||||
LightProbeModule light_probes;
|
||||
IrradianceCache irradiance_cache;
|
||||
|
@ -105,6 +108,7 @@ class Instance {
|
|||
pipelines(*this),
|
||||
shadows(*this),
|
||||
lights(*this),
|
||||
reflection_probes(*this),
|
||||
velocity(*this),
|
||||
motion_blur(*this),
|
||||
depth_of_field(*this),
|
||||
|
@ -115,6 +119,7 @@ class Instance {
|
|||
film(*this),
|
||||
render_buffers(*this),
|
||||
main_view(*this),
|
||||
capture_view(*this),
|
||||
world(*this),
|
||||
light_probes(*this),
|
||||
irradiance_cache(*this){};
|
||||
|
|
|
@ -56,6 +56,56 @@ void BackgroundPipeline::render(View &view)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name World Probe Pipeline
|
||||
* \{ */
|
||||
|
||||
void WorldPipeline::sync(GPUMaterial *gpumat)
|
||||
{
|
||||
const int2 extent(1);
|
||||
constexpr eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_WRITE;
|
||||
dummy_cryptomatte_tx_.ensure_2d(GPU_RGBA32F, extent, usage);
|
||||
dummy_renderpass_tx_.ensure_2d(GPU_RGBA16F, extent, usage);
|
||||
dummy_aov_color_tx_.ensure_2d_array(GPU_RGBA16F, extent, 1, usage);
|
||||
dummy_aov_value_tx_.ensure_2d_array(GPU_R16F, extent, 1, usage);
|
||||
|
||||
PassSimple &pass = cubemap_face_ps_;
|
||||
pass.init();
|
||||
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS);
|
||||
|
||||
Manager &manager = *inst_.manager;
|
||||
ResourceHandle handle = manager.resource_handle(float4x4::identity());
|
||||
pass.material_set(manager, gpumat);
|
||||
pass.push_constant("world_opacity_fade", 1.0f);
|
||||
|
||||
pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
|
||||
pass.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get());
|
||||
pass.bind_ubo(RBUFS_BUF_SLOT, &inst_.render_buffers.data);
|
||||
pass.bind_image("rp_normal_img", dummy_renderpass_tx_);
|
||||
pass.bind_image("rp_light_img", dummy_renderpass_tx_);
|
||||
pass.bind_image("rp_diffuse_color_img", dummy_renderpass_tx_);
|
||||
pass.bind_image("rp_specular_color_img", dummy_renderpass_tx_);
|
||||
pass.bind_image("rp_emission_img", dummy_renderpass_tx_);
|
||||
pass.bind_image("rp_cryptomatte_img", dummy_cryptomatte_tx_);
|
||||
pass.bind_image("rp_color_img", dummy_aov_color_tx_);
|
||||
pass.bind_image("rp_value_img", dummy_aov_value_tx_);
|
||||
/* Required by validation layers. */
|
||||
inst_.cryptomatte.bind_resources(&pass);
|
||||
|
||||
pass.bind_image("aov_color_img", dummy_aov_color_tx_);
|
||||
pass.bind_image("aov_value_img", dummy_aov_value_tx_);
|
||||
pass.bind_ssbo("aov_buf", &inst_.film.aovs_info);
|
||||
|
||||
pass.draw(DRW_cache_fullscreen_quad_get(), handle);
|
||||
}
|
||||
|
||||
void WorldPipeline::render(View &view)
|
||||
{
|
||||
inst_.manager->submit(cubemap_face_ps_, view);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Shadow Pipeline
|
||||
*
|
||||
|
@ -371,6 +421,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_);
|
||||
inst_.irradiance_cache.bind_resources(&eval_light_ps_);
|
||||
|
||||
eval_light_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS);
|
||||
|
|
|
@ -43,6 +43,34 @@ class BackgroundPipeline {
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name World Probe Pipeline
|
||||
*
|
||||
* Renders a single side for the world reflection probe.
|
||||
* \{ */
|
||||
|
||||
class WorldPipeline {
|
||||
private:
|
||||
Instance &inst_;
|
||||
|
||||
/* Dummy textures: required to reuse background shader and avoid another shader variation. */
|
||||
Texture dummy_renderpass_tx_;
|
||||
Texture dummy_cryptomatte_tx_;
|
||||
Texture dummy_aov_color_tx_;
|
||||
Texture dummy_aov_value_tx_;
|
||||
|
||||
PassSimple cubemap_face_ps_ = {"World.Probe"};
|
||||
|
||||
public:
|
||||
WorldPipeline(Instance &inst) : inst_(inst){};
|
||||
|
||||
void sync(GPUMaterial *gpumat);
|
||||
void render(View &view);
|
||||
|
||||
}; // namespace blender::eevee
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Shadow Pass
|
||||
*
|
||||
|
@ -288,6 +316,7 @@ class UtilityTexture : public Texture {
|
|||
class PipelineModule {
|
||||
public:
|
||||
BackgroundPipeline background;
|
||||
WorldPipeline world;
|
||||
DeferredPipeline deferred;
|
||||
ForwardPipeline forward;
|
||||
ShadowPipeline shadow;
|
||||
|
@ -297,7 +326,12 @@ class PipelineModule {
|
|||
|
||||
public:
|
||||
PipelineModule(Instance &inst)
|
||||
: background(inst), deferred(inst), forward(inst), shadow(inst), capture(inst){};
|
||||
: background(inst),
|
||||
world(inst),
|
||||
deferred(inst),
|
||||
forward(inst),
|
||||
shadow(inst),
|
||||
capture(inst){};
|
||||
|
||||
void begin_sync()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/* 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 (!initialized_) {
|
||||
cubemaps_tx_.ensure_cube_array(GPU_RGBA16F,
|
||||
max_resolution_,
|
||||
max_probes_,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT,
|
||||
NULL,
|
||||
max_mipmap_levels_);
|
||||
GPU_texture_mipmap_mode(cubemaps_tx_, true, true);
|
||||
initialized_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::eevee
|
|
@ -0,0 +1,73 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. */
|
||||
|
||||
/** \file
|
||||
* \ingroup eevee
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "eevee_shader_shared.hh"
|
||||
|
||||
#include "BKE_cryptomatte.hh"
|
||||
|
||||
extern "C" {
|
||||
struct Material;
|
||||
}
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
class Instance;
|
||||
class CaptureView;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Reflection Probes
|
||||
* \{ */
|
||||
|
||||
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;
|
||||
static constexpr int max_mipmap_levels_ = log(max_resolution_) + 1;
|
||||
|
||||
Instance &instance_;
|
||||
|
||||
Texture cubemaps_tx_ = {"Probes"};
|
||||
|
||||
bool initialized_ = false;
|
||||
|
||||
bool do_world_update_ = false;
|
||||
|
||||
public:
|
||||
ReflectionProbeModule(Instance &instance) : instance_(instance) {}
|
||||
|
||||
void init();
|
||||
|
||||
template<typename T> void bind_resources(draw::detail::PassBase<T> *pass)
|
||||
{
|
||||
pass->bind_texture(REFLECTION_PROBE_TEX_SLOT, cubemaps_tx_);
|
||||
}
|
||||
|
||||
void do_world_update_set(bool value)
|
||||
{
|
||||
do_world_update_ = value;
|
||||
}
|
||||
|
||||
private:
|
||||
bool do_world_update_get() const
|
||||
{
|
||||
return do_world_update_;
|
||||
}
|
||||
|
||||
/* Capture View requires access to the cubemaps texture for framebuffer configuration. */
|
||||
friend class CaptureView;
|
||||
};
|
||||
|
||||
} // namespace blender::eevee
|
|
@ -125,8 +125,6 @@ void ShadingView::render()
|
|||
|
||||
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);
|
||||
|
@ -191,4 +189,35 @@ void ShadingView::update_view()
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Capture View
|
||||
* \{ */
|
||||
|
||||
void CaptureView::render()
|
||||
{
|
||||
if (!inst_.reflection_probes.do_world_update_get()) {
|
||||
return;
|
||||
}
|
||||
inst_.reflection_probes.do_world_update_set(false);
|
||||
|
||||
GPU_debug_group_begin("World.Capture");
|
||||
View view = {"World.Capture.View"};
|
||||
|
||||
for (int face : IndexRange(6)) {
|
||||
capture_fb_.ensure(
|
||||
GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE_CUBEFACE(inst_.reflection_probes.cubemaps_tx_, face));
|
||||
GPU_framebuffer_bind(capture_fb_);
|
||||
|
||||
float4x4 view_m4 = cubeface_mat(face);
|
||||
float4x4 win_m4 = math::projection::perspective(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 10.0f);
|
||||
view.sync(view_m4, win_m4);
|
||||
inst_.pipelines.world.render(view);
|
||||
}
|
||||
GPU_texture_update_mipmap_chain(inst_.reflection_probes.cubemaps_tx_);
|
||||
GPU_debug_group_end();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
|
|
|
@ -142,4 +142,22 @@ class MainView {
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Capture View
|
||||
*
|
||||
* View for capturing cubemap renders outside a ShadingView.
|
||||
* \{ */
|
||||
|
||||
class CaptureView {
|
||||
private:
|
||||
Instance &inst_;
|
||||
Framebuffer capture_fb_ = {"World.Capture"};
|
||||
|
||||
public:
|
||||
CaptureView(Instance &inst) : inst_(inst) {}
|
||||
void render();
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::eevee
|
||||
|
|
|
@ -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.do_world_update_set(true);
|
||||
}
|
||||
wo_handle.reset_recalc_flag();
|
||||
|
||||
|
@ -109,6 +109,7 @@ void World::sync()
|
|||
inst_.manager->register_layer_attributes(gpumat);
|
||||
|
||||
inst_.pipelines.background.sync(gpumat);
|
||||
inst_.pipelines.world.sync(gpumat);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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)
|
||||
#pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl)
|
||||
|
||||
void main()
|
||||
|
@ -54,6 +55,7 @@ void main()
|
|||
vec3 reflection_light = vec3(0.0);
|
||||
float shadow = 1.0;
|
||||
|
||||
light_world_eval(reflection_data, P, V, reflection_light);
|
||||
lightprobe_eval(diffuse_data, reflection_data, P, Ng, V, diffuse_light, reflection_light);
|
||||
|
||||
light_eval(diffuse_data,
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
|
||||
|
||||
void light_world_eval(ClosureReflection reflection, vec3 P, vec3 V, inout vec3 out_specular)
|
||||
{
|
||||
ivec3 texture_size = textureSize(reflectionProbes, 0);
|
||||
/* TODO: This should be based by actual resolution. Currently the resolution is fixed but
|
||||
* eventually this should based on a user setting and part of the reflection probe data that will
|
||||
* be introduced by the reflection probe patch. */
|
||||
float lod_cube_max = 12.0;
|
||||
|
||||
/* Pow2f to distributed across lod more evenly */
|
||||
float roughness = clamp(pow2f(reflection.roughness), 1e-4f, 0.9999f);
|
||||
|
||||
#if defined(GPU_COMPUTE_SHADER)
|
||||
vec2 frag_coord = vec2(gl_GlobalInvocationID.xy) + 0.5;
|
||||
#else
|
||||
vec2 frag_coord = gl_FragCoord.xy;
|
||||
#endif
|
||||
vec2 noise = utility_tx_fetch(utility_tx, frag_coord, UTIL_BLUE_NOISE_LAYER).gb;
|
||||
vec2 rand = fract(noise + sampling_rng_2D_get(SAMPLING_RAYTRACE_U));
|
||||
|
||||
vec3 Xi = sample_cylinder(rand);
|
||||
|
||||
/* Microfacet normal */
|
||||
vec3 T, B;
|
||||
make_orthonormal_basis(reflection.N, T, B);
|
||||
float pdf;
|
||||
vec3 H = sample_ggx(Xi, roughness, V, reflection.N, T, B, pdf);
|
||||
|
||||
vec3 L = -reflect(V, H);
|
||||
float NL = dot(reflection.N, L);
|
||||
|
||||
if (NL > 0.0) {
|
||||
/* Coarse Approximation of the mapping distortion
|
||||
* Unit Sphere -> Cubemap Face */
|
||||
const float dist = 4.0 * M_PI / 6.0;
|
||||
|
||||
/* http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html : Equation 13 */
|
||||
/* TODO: lod_factor should be precalculated and stored inside the reflection probe data. */
|
||||
const float bias = 0;
|
||||
const float lod_factor = bias + 0.5 * log(float(square_i(texture_size.x))) / log(2);
|
||||
/* -2: Don't use LOD levels that are smaller than 4x4 pixels. */
|
||||
float lod = clamp(lod_factor - 0.5 * log2(pdf * dist), 0.0, lod_cube_max - 2.0);
|
||||
|
||||
vec3 l_col = textureLod_cubemapArray(reflectionProbes, vec4(L, 0.0), lod).rgb;
|
||||
|
||||
/* Clamped brightness. */
|
||||
/* For artistic freedom this should be read from the scene/reflection probe.
|
||||
* Note: Eevee-legacy read the firefly_factor from gi_glossy_clamp.
|
||||
* Note: Firefly removal should be moved to a different shader and also take SSR into
|
||||
* account.*/
|
||||
float luma = max(1e-8, max_v3(l_col));
|
||||
const float firefly_factor = 1e16;
|
||||
l_col *= 1.0 - max(0.0, luma - firefly_factor) / luma;
|
||||
|
||||
/* TODO: for artistic freedom want to read this from the reflection probe. That will be part of
|
||||
* the reflection probe patch. */
|
||||
const float intensity_factor = 1.0;
|
||||
out_specular += vec3(intensity_factor * l_col);
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
**/
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Sampling data.
|
||||
|
@ -102,3 +103,86 @@ vec3 sample_cylinder(vec2 rand)
|
|||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Microfacet GGX distribution
|
||||
* \{ */
|
||||
|
||||
#define USE_VISIBLE_NORMAL 1
|
||||
|
||||
float D_ggx_opti(float NH, float a2)
|
||||
{
|
||||
float tmp = (NH * a2 - NH) * NH + 1.0;
|
||||
return M_PI * tmp * tmp; /* Doing RCP and mul a2 at the end */
|
||||
}
|
||||
|
||||
float G1_Smith_GGX_opti(float NX, float a2)
|
||||
{
|
||||
/* Using Brian Karis approach and refactoring by NX/NX
|
||||
* this way the (2*NL)*(2*NV) in G = G1(V) * G1(L) gets canceled by the brdf denominator 4*NL*NV
|
||||
* Rcp is done on the whole G later
|
||||
* Note that this is not convenient for the transmission formula */
|
||||
return NX + sqrt(NX * (NX - NX * a2) + a2);
|
||||
/* return 2 / (1 + sqrt(1 + a2 * (1 - NX*NX) / (NX*NX) ) ); /* Reference function */
|
||||
}
|
||||
|
||||
float pdf_ggx_reflect(float NH, float NV, float VH, float alpha)
|
||||
{
|
||||
float a2 = sqr(alpha);
|
||||
#if USE_VISIBLE_NORMAL
|
||||
float D = a2 / D_ggx_opti(NH, a2);
|
||||
float G1 = NV * 2.0 / G1_Smith_GGX_opti(NV, a2);
|
||||
return G1 * VH * D / NV;
|
||||
#else
|
||||
return NH * a2 / D_ggx_opti(NH, a2);
|
||||
#endif
|
||||
}
|
||||
|
||||
vec3 sample_ggx(vec3 rand, float alpha, vec3 Vt)
|
||||
{
|
||||
#if USE_VISIBLE_NORMAL
|
||||
/* From:
|
||||
* "A Simpler and Exact Sampling Routine for the GGXDistribution of Visible Normals"
|
||||
* by Eric Heitz.
|
||||
* http://jcgt.org/published/0007/04/01/slides.pdf
|
||||
* View vector is expected to be in tangent space. */
|
||||
|
||||
/* Stretch view. */
|
||||
vec3 Th, Bh, Vh = normalize(vec3(alpha * Vt.xy, Vt.z));
|
||||
make_orthonormal_basis(Vh, Th, Bh);
|
||||
/* Sample point with polar coordinates (r, phi). */
|
||||
float r = sqrt(rand.x);
|
||||
float x = r * rand.y;
|
||||
float y = r * rand.z;
|
||||
float s = 0.5 * (1.0 + Vh.z);
|
||||
y = (1.0 - s) * sqrt(1.0 - x * x) + s * y;
|
||||
float z = sqrt(saturate(1.0 - x * x - y * y));
|
||||
/* Compute normal. */
|
||||
vec3 Hh = x * Th + y * Bh + z * Vh;
|
||||
/* Unstretch. */
|
||||
vec3 Ht = normalize(vec3(alpha * Hh.xy, saturate(Hh.z)));
|
||||
/* Microfacet Normal. */
|
||||
return Ht;
|
||||
#else
|
||||
/* Theta is the cone angle. */
|
||||
float z = sqrt((1.0 - rand.x) / (1.0 + sqr(alpha) * rand.x - rand.x)); /* cos theta */
|
||||
float r = sqrt(max(0.0, 1.0 - z * z)); /* sin theta */
|
||||
float x = r * rand.y;
|
||||
float y = r * rand.z;
|
||||
/* Microfacet Normal */
|
||||
return vec3(x, y, z);
|
||||
#endif
|
||||
}
|
||||
|
||||
vec3 sample_ggx(vec3 rand, float alpha, vec3 V, vec3 N, vec3 T, vec3 B, out float pdf)
|
||||
{
|
||||
vec3 Vt = world_to_tangent(V, N, T, B);
|
||||
vec3 Ht = sample_ggx(rand, alpha, Vt);
|
||||
float NH = saturate(Ht.z);
|
||||
float NV = saturate(Vt.z);
|
||||
float VH = saturate(dot(Vt, Ht));
|
||||
pdf = pdf_ggx_reflect(NH, NV, VH, alpha);
|
||||
return tangent_to_world(Ht, N, T, B);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -36,7 +36,9 @@ GPU_SHADER_CREATE_INFO(eevee_deferred_light)
|
|||
.sampler(1, ImageType::FLOAT_2D_ARRAY, "gbuffer_color_tx")
|
||||
.additional_info("eevee_shared",
|
||||
"eevee_utility_texture",
|
||||
"eevee_sampling_data",
|
||||
"eevee_light_data",
|
||||
"eevee_reflection_probe_data",
|
||||
"eevee_lightprobe_data",
|
||||
"eevee_shadow_data",
|
||||
"eevee_deferred_base",
|
||||
|
|
|
@ -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");
|
||||
|
||||
/** \} */
|
|
@ -651,6 +651,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_subsurface_info.hh
|
||||
../draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh
|
||||
|
|
Loading…
Reference in New Issue