Eevee-Next: World Reflective Light #108149
|
@ -1,12 +1,71 @@
|
|||
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
|
||||
Jeroen-Bakker marked this conversation as resolved
Outdated
|
||||
|
||||
void light_world_eval(ClosureReflection reflection, vec3 P, vec3 V, inout vec3 out_specular)
|
||||
{
|
||||
float linear_roughness = fast_sqrt(reflection.roughness);
|
||||
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. */
|
||||
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;
|
||||
vec3 T, B;
|
||||
Jeroen-Bakker marked this conversation as resolved
Outdated
Jeroen Bakker
commented
Check Check `lightprobe_filter_glossy_frag`
|
||||
make_orthonormal_basis(reflection.N, T, B);
|
||||
|
||||
float weight = 0.0;
|
||||
vec3 out_radiance = vec3(0.0);
|
||||
/* Note: this was dynamic based on the mipmap level that was read from. */
|
||||
/* lod 0 => 1
|
||||
* 1 => 32
|
||||
* 2 => 40
|
||||
* 3 => 64
|
||||
* else => 128
|
||||
* multiplied by the filter quality. */
|
||||
const int max_sample_count = 32;
|
||||
/* Note this is dynamically based on the mip map level.
|
||||
pinfo->lodfactor = bias + 0.5f * log(square_f(target_size) / pinfo->samples_len) / log(2);
|
||||
*/
|
||||
/* Pow4f for Disney Roughness distributed across lod more evenly */
|
||||
float roughness = clamp(pow4f(reflection.roughness), 1e-4f, 0.9999f); /* Avoid artifacts */
|
||||
|
||||
const float lod_factor = 1.0 +
|
||||
0.5 * log(float(square_i(texture_size.x)) / float(max_sample_count)) /
|
||||
log(2);
|
||||
/* We should find a math formular that would approx max_sample_count and bias. */
|
||||
for (int i = 0; i < max_sample_count; i++) {
|
||||
vec3 Xi = sample_cylinder(hammersley_2d(float(i), max_sample_count));
|
||||
|
||||
/* Microfacet normal */
|
||||
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) {
|
||||
float NH = max(1e-8, dot(reflection.N, H)); /*cosTheta */
|
||||
|
||||
/* 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 */
|
||||
float lod = clamp(lod_factor - 0.5 * log2(pdf * dist), 0.0, lod_cube_max);
|
||||
|
||||
vec3 l_col = textureLod_cubemapArray(reflectionProbes, vec4(L, 0.0), lod).rgb;
|
||||
|
||||
/* Clamped brightness. */
|
||||
float luma = max(1e-8, max_v3(l_col));
|
||||
|
||||
/* For artistic freedom this should be read from the scene/reflection probe.
|
||||
* Note: Eevee-legacy read the firefly_factor from gi_glossy_clamp. */
|
||||
const float firefly_factor = 1e16;
|
||||
l_col *= 1.0 - max(0.0, luma - firefly_factor) / luma;
|
||||
|
||||
out_radiance += l_col * NL;
|
||||
weight += NL;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: for artistic freedom want to read this from the reflection probe. That can be added as
|
||||
* part of the reflection probe patch. */
|
||||
const float intensity_factor = 1.0;
|
||||
out_specular += vec3(intensity_factor * out_radiance / weight);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
Loading…
Reference in New Issue
Do we also need to update out_diffuse