124 lines
5.0 KiB
GLSL
124 lines
5.0 KiB
GLSL
/*
|
|
Implementation for the Cook-Torrance specular BRDF. Consists of a Distribution function, a
|
|
Fresnel equation, and a Geometry function.
|
|
*/
|
|
|
|
/*
|
|
Fresnel equation: describes the ratio of surface reflection at different surface angles.
|
|
*/
|
|
highp vec3 fresnel_schlick(highp float cosTheta, highp vec3 F0) {
|
|
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
|
|
}
|
|
|
|
highp vec3 fresnel_schlick_roughness(highp float cosTheta, highp vec3 F0, highp float roughness) {
|
|
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
|
|
}
|
|
|
|
/*
|
|
Trowbridge-Reitz GGX normal distribution function: approximates the amount the surface's
|
|
microfacets are aligned to the halfway vector influenced by the roughness of the surface.
|
|
Primary function approximating microfacets.
|
|
*/
|
|
highp float distribution_ggx(highp vec3 N, highp vec3 H, highp float roughness) {
|
|
highp float a = roughness * roughness;
|
|
highp float a2 = a * a;
|
|
highp float NdotH = max(dot(N, H), 0.0);
|
|
highp float NdotH2 = NdotH * NdotH;
|
|
|
|
highp float nom = a2;
|
|
highp float denom = (NdotH2 * (a2 - 1.0) + 1.0);
|
|
denom = PI * denom * denom;
|
|
|
|
return nom / denom;
|
|
}
|
|
|
|
/*
|
|
Schlick GGX geometry function: describes the self-shadowing property of the microfacets. When
|
|
a surface is relatively rough the surface's microfacets can overshadow other microfacets, reducing
|
|
the light the surface reflects.
|
|
*/
|
|
highp float geometry_schlick_ggx(highp float NdotV, highp float roughness) {
|
|
highp float r = (roughness + 1.0);
|
|
highp float k = (r * r) / 8.0;
|
|
highp float nom = NdotV;
|
|
highp float denom = NdotV * (1.0 - k) + k;
|
|
|
|
return nom / denom;
|
|
}
|
|
|
|
/*
|
|
Smith method combines the obstruction from the view direction with the shadowing from the light
|
|
direction vector. Each is computed with Schlick and combined through Smith.
|
|
*/
|
|
highp float geometry_smith(highp vec3 N, highp vec3 V, highp vec3 L, highp float roughness) {
|
|
highp float NdotV = max(dot(N, V), 0.0);
|
|
highp float NdotL = max(dot(N, L), 0.0);
|
|
highp float ggx2 = geometry_schlick_ggx(NdotV, roughness);
|
|
highp float ggx1 = geometry_schlick_ggx(NdotL, roughness);
|
|
|
|
return ggx1 * ggx2;
|
|
}
|
|
|
|
highp float smooth_distance_attenuation(highp float squared_distance, highp float inv_squared_attenuation_radius) {
|
|
highp float factor = squared_distance * inv_squared_attenuation_radius;
|
|
highp float smooth_factor = clamp(1.0f - factor * factor, 0.0, 1.0);
|
|
return smooth_factor * smooth_factor;
|
|
}
|
|
|
|
highp float compute_distance_attenuation(highp vec3 unnormalized_light_vector, highp float inv_squared_attenuation_radius) {
|
|
highp float squared_distance = dot(unnormalized_light_vector, unnormalized_light_vector);
|
|
highp float attenuation = 1.0 / (max(squared_distance, 0.01 * 0.01));
|
|
attenuation *= smooth_distance_attenuation(squared_distance, inv_squared_attenuation_radius);
|
|
|
|
return attenuation;
|
|
}
|
|
|
|
highp float compute_attenuation_pbr(const VROLightUniforms light, highp vec3 surface_pos,
|
|
out highp vec3 L) {
|
|
|
|
highp float attenuation = 1.0;
|
|
|
|
// Directional light
|
|
if (light.type == 1) {
|
|
L = -normalize(light.direction.xyz);
|
|
|
|
// For directional light luminous intensity is simply a scalar (as if we're
|
|
// using Lambert)
|
|
highp float luminous_intensity = light.intensity / (1000.0);
|
|
attenuation = luminous_intensity;
|
|
}
|
|
|
|
// Omni Light
|
|
else if (light.type == 2) {
|
|
highp vec3 unnormalized_light_vector = light.position.xyz - surface_pos;
|
|
L = normalize(unnormalized_light_vector);
|
|
|
|
// Intensity is integrated over the solid angle to get power (luminous flux)
|
|
// For a point light this simplifies to I = phi / (4.0 * PI). We use PI
|
|
// becuase we don't want spotlight intensity to differ from omni intensity
|
|
highp float luminous_intensity = light.intensity / PI;
|
|
|
|
highp float inverse_square_attenuation_radius = 1.0 / (light.attenuation_end_distance * light.attenuation_end_distance);
|
|
attenuation *= compute_distance_attenuation(unnormalized_light_vector, inverse_square_attenuation_radius);
|
|
attenuation *= luminous_intensity;
|
|
}
|
|
|
|
// Spotlight
|
|
else if (light.type == 3) {
|
|
highp vec3 unnormalized_light_vector = light.position.xyz - surface_pos;
|
|
L = normalize(unnormalized_light_vector);
|
|
|
|
// For a spotlight we use a simple equation for intensity since we don't want
|
|
// intensity to be stronger for smaller cones given the same power (luminous flux)
|
|
highp float luminous_intensity = light.intensity / PI;
|
|
|
|
highp float inverse_square_attenuation_radius = 1.0 / (light.attenuation_end_distance * light.attenuation_end_distance);
|
|
attenuation *= compute_distance_attenuation(unnormalized_light_vector, inverse_square_attenuation_radius);
|
|
attenuation *= compute_angle_attenuation(L, light.direction.xyz, light.spot_inner_angle, light.spot_outer_angle);
|
|
attenuation *= luminous_intensity;
|
|
}
|
|
|
|
return attenuation;
|
|
}
|
|
|