Spherical Area Lights (#1901)

I still need to simplify and optimize the code, but here's a preliminary working version of Spherical Area Lights. See the example image below from a modified version of my [cubism-demo-rs](https://github.com/Josh015/cubism-demo-rs) app, which you can also clone and run to see them in action.

![Spherical Area Lights v1](https://user-images.githubusercontent.com/8846132/114491862-60df6000-9be5-11eb-8950-f039b74e1e96.jpg)
This commit is contained in:
Joshua Ols 2021-04-22 18:49:02 +00:00
parent b9640243c6
commit 19f467ebd0
2 changed files with 33 additions and 10 deletions

View file

@ -11,6 +11,7 @@ pub struct PointLight {
pub color: Color,
pub intensity: f32,
pub range: f32,
pub radius: f32,
}
impl Default for PointLight {
@ -19,6 +20,7 @@ impl Default for PointLight {
color: Color::rgb(1.0, 1.0, 1.0),
intensity: 200.0,
range: 20.0,
radius: 0.0,
}
}
}
@ -29,7 +31,7 @@ pub(crate) struct PointLightUniform {
pub pos: [f32; 4],
pub color: [f32; 4],
// storing as a `[f32; 4]` for memory alignement
pub inverse_range_squared: [f32; 4],
pub light_params: [f32; 4],
}
unsafe impl Byteable for PointLightUniform {}
@ -41,10 +43,11 @@ impl PointLightUniform {
// premultiply color by intensity
// we don't use the alpha at all, so no reason to multiply only [0..3]
let color: [f32; 4] = (light.color * light.intensity).into();
PointLightUniform {
pos: [x, y, z, 1.0],
color,
inverse_range_squared: [1.0 / (light.range * light.range), 0., 0., 0.],
light_params: [1.0 / (light.range * light.range), light.radius, 0.0, 0.0],
}
}
}

View file

@ -39,7 +39,7 @@ const int MAX_LIGHTS = 10;
struct PointLight {
vec4 pos;
vec4 color;
float inverseRangeSquared;
vec4 lightParams;
};
layout(location = 0) in vec3 v_WorldPosition;
@ -193,12 +193,12 @@ vec3 fresnel(vec3 f0, float LoH) {
// Cook-Torrance approximation of the microfacet model integration using Fresnel law F to model f_m
// f_r(v,l) = { D(h,α) G(v,l,α) F(v,h,f0) } / { 4 (n⋅v) (n⋅l) }
vec3 specular(vec3 f0, float roughness, const vec3 h, float NoV, float NoL,
float NoH, float LoH) {
float NoH, float LoH, float specularIntensity) {
float D = D_GGX(roughness, NoH, h);
float V = V_SmithGGXCorrelated(roughness, NoV, NoL);
vec3 F = fresnel(f0, LoH);
return (D * V) * F;
return (specularIntensity * D * V) * F;
}
// Diffuse BRDF
@ -339,24 +339,44 @@ void main() {
// Diffuse strength inversely related to metallicity
vec3 diffuseColor = output_color.rgb * (1.0 - metallic);
vec3 R = reflect(-V, N);
// accumulate color
vec3 light_accum = vec3(0.0);
for (int i = 0; i < int(NumLights.x) && i < MAX_LIGHTS; ++i) {
PointLight light = PointLights[i];
vec3 light_to_frag = light.pos.xyz - v_WorldPosition.xyz;
vec3 L = normalize(light_to_frag);
float distance_square = dot(light_to_frag, light_to_frag);
float rangeAttenuation =
getDistanceAttenuation(distance_square, light.inverseRangeSquared);
getDistanceAttenuation(distance_square, light.lightParams.r);
// Specular.
// Representative Point Area Lights.
// see http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf p14-16
float a = roughness;
float radius = light.lightParams.g;
vec3 centerToRay = dot(light_to_frag, R) * R - light_to_frag;
vec3 closestPoint = light_to_frag + centerToRay * saturate(radius * inversesqrt(dot(centerToRay, centerToRay)));
float LspecLengthInverse = inversesqrt(dot(closestPoint, closestPoint));
float normalizationFactor = a / saturate(a + (radius * 0.5 * LspecLengthInverse));
float specularIntensity = normalizationFactor * normalizationFactor;
vec3 L = closestPoint * LspecLengthInverse; // normalize() equivalent?
vec3 H = normalize(L + V);
float NoL = saturate(dot(N, L));
float NoH = saturate(dot(N, H));
float LoH = saturate(dot(L, H));
vec3 specular = specular(F0, roughness, H, NdotV, NoL, NoH, LoH);
vec3 specular = specular(F0, roughness, H, NdotV, NoL, NoH, LoH, specularIntensity);
// Diffuse.
// Comes after specular since its NoL is used in the lighting equation.
L = normalize(light_to_frag);
H = normalize(L + V);
NoL = saturate(dot(N, L));
NoH = saturate(dot(N, H));
LoH = saturate(dot(L, H));
vec3 diffuse = diffuseColor * Fd_Burley(roughness, NdotV, NoL, LoH);
// Lout = f(v,l) Φ / { 4 π d^2 }⟨n⋅l⟩