190 lines
7.0 KiB
GLSL
190 lines
7.0 KiB
GLSL
vec3 PBRLight(
|
|
vec3 normal,
|
|
vec3 eyedir,
|
|
vec3 lightdir,
|
|
vec3 color,
|
|
float perceptual_roughness,
|
|
float metallic)
|
|
{
|
|
float NdotV = max(dot(normal, eyedir), 0.0001);
|
|
float NdotL = clamp(dot(normal, lightdir), 0.0, 1.0);
|
|
|
|
vec2 F_ab = F_AB(perceptual_roughness, NdotV);
|
|
|
|
vec3 H = normalize(eyedir + lightdir);
|
|
float NdotH = clamp(dot(normal, H), 0.0, 1.0);
|
|
float LdotH = clamp(dot(lightdir, H), 0.0, 1.0);
|
|
|
|
vec3 diffuse_color = color * (1.0 - metallic);
|
|
vec3 F0 = mix(vec3(0.04), color, metallic);
|
|
// No real world material has specular values under 0.02, so we use this range as a
|
|
// "pre-baked specular occlusion" that extinguishes the fresnel term, for artistic control.
|
|
// See: https://google.github.io/filament/Filament.html#specularocclusion
|
|
float F90 = clamp(dot(F0, vec3(50.0 * 0.33)), 0.0, 1.0);
|
|
|
|
float roughness = perceptualRoughnessToRoughness(perceptual_roughness);
|
|
|
|
vec3 diffuse = diffuse_color * Fd_Burley(roughness, NdotV, NdotL, NdotH);
|
|
|
|
float D = D_GGX(roughness, NdotH);
|
|
float V = V_Smith_GGX_Correlated(roughness, NdotV, NdotL);
|
|
vec3 F = fresnel(F0, F90, LdotH);
|
|
vec3 specular = D * V * F * (1.0 + F0 * (1.0 / F_ab.x - 1.0));
|
|
|
|
return NdotL * (diffuse + specular);
|
|
}
|
|
|
|
vec3 PBRSunAmbientEmitLight(
|
|
vec3 normal,
|
|
vec3 eyedir,
|
|
vec3 sundir,
|
|
vec3 color,
|
|
vec3 irradiance,
|
|
vec3 radiance,
|
|
vec3 sun_color,
|
|
vec3 ambient_color,
|
|
float perceptual_roughness,
|
|
float metallic,
|
|
float emissive)
|
|
{
|
|
// Copied from PBRLight to use F_ab and F90 again
|
|
float NdotV = max(dot(normal, eyedir), 0.0001);
|
|
float NdotL = clamp(dot(normal, sundir), 0.0, 1.0);
|
|
|
|
vec2 F_ab = F_AB(perceptual_roughness, NdotV);
|
|
|
|
vec3 H = normalize(eyedir + sundir);
|
|
float NdotH = clamp(dot(normal, H), 0.0, 1.0);
|
|
float LdotH = clamp(dot(sundir, H), 0.0, 1.0);
|
|
|
|
vec3 diffuse_color = color * (1.0 - metallic);
|
|
vec3 F0 = mix(vec3(0.04), color, metallic);
|
|
// No real world material has specular values under 0.02, so we use this range as a
|
|
// "pre-baked specular occlusion" that extinguishes the fresnel term, for artistic control.
|
|
// See: https://google.github.io/filament/Filament.html#specularocclusion
|
|
float F90 = clamp(dot(F0, vec3(50.0 * 0.33)), 0.0, 1.0);
|
|
|
|
float roughness = perceptualRoughnessToRoughness(perceptual_roughness);
|
|
|
|
vec3 diffuse = diffuse_color * Fd_Burley(roughness, NdotV, NdotL, NdotH);
|
|
|
|
float D = D_GGX(roughness, NdotH);
|
|
float V = V_Smith_GGX_Correlated(roughness, NdotV, NdotL);
|
|
vec3 F = fresnel(F0, F90, LdotH);
|
|
vec3 specular = D * V * F * (1.0 + F0 * (1.0 / F_ab.x - 1.0));
|
|
|
|
vec3 sunlight = NdotL * (diffuse + specular);
|
|
|
|
vec3 diffuse_ambient = envBRDFApprox(diffuse_color, F_AB(1.0, NdotV));
|
|
|
|
vec3 specular_ambient = F90 * envBRDFApprox(F0, F_ab);
|
|
|
|
// Other 0.6 comes from skybox
|
|
ambient_color *= 0.4;
|
|
vec3 environment;
|
|
if (u_ibl)
|
|
{
|
|
environment = environmentLight(irradiance, radiance, roughness,
|
|
diffuse_color, F_ab, F0, F90, NdotV);
|
|
}
|
|
else
|
|
{
|
|
environment = u_global_light.m_skytop_color * ambient_color *
|
|
diffuse_color;
|
|
}
|
|
|
|
vec3 emit = emissive * color * 4.0;
|
|
|
|
return sun_color * sunlight
|
|
+ environment + emit
|
|
+ (diffuse_ambient + specular_ambient) * ambient_color;
|
|
}
|
|
|
|
vec3 accumulateLights(int light_count, vec3 diffuse_color, vec3 normal,
|
|
vec3 xpos, vec3 eyedir, float perceptual_roughness,
|
|
float metallic)
|
|
{
|
|
vec3 accumulated_color = vec3(0.0);
|
|
for (int i = 0; i < light_count; i++)
|
|
{
|
|
vec3 light_to_frag = (u_camera.m_view_matrix *
|
|
vec4(u_global_light.m_lights[i].m_position_radius.xyz,
|
|
1.0)).xyz - xpos;
|
|
float invrange = u_global_light.m_lights[i].m_color_inverse_square_range.w;
|
|
float distance_sq = dot(light_to_frag, light_to_frag);
|
|
if (distance_sq * invrange > 1.)
|
|
continue;
|
|
// SpotLight
|
|
float sattenuation = 1.;
|
|
float sscale = u_global_light.m_lights[i].m_direction_scale_offset.z;
|
|
float distance = sqrt(distance_sq);
|
|
float distance_inverse = 1. / distance;
|
|
vec3 L = light_to_frag * distance_inverse;
|
|
if (sscale != 0.)
|
|
{
|
|
vec3 sdir =
|
|
vec3(u_global_light.m_lights[i].m_direction_scale_offset.xy, 0.);
|
|
sdir.z = sqrt(1. - dot(sdir, sdir)) * sign(sscale);
|
|
sdir = (u_camera.m_view_matrix * vec4(sdir, 0.0)).xyz;
|
|
sattenuation = clamp(dot(-sdir, L) *
|
|
abs(sscale) +
|
|
u_global_light.m_lights[i].m_direction_scale_offset.w, 0.0, 1.0);
|
|
#ifndef TILED_GPU
|
|
// Reduce branching in tiled GPU
|
|
if (sattenuation == 0.)
|
|
continue;
|
|
#endif
|
|
}
|
|
vec3 diffuse_specular = PBRLight(normal, eyedir, L, diffuse_color,
|
|
perceptual_roughness, metallic);
|
|
float attenuation = 20. / (1. + distance_sq);
|
|
float radius = u_global_light.m_lights[i].m_position_radius.w;
|
|
attenuation *= (radius - distance) / radius;
|
|
attenuation *= sattenuation * sattenuation;
|
|
vec3 light_color =
|
|
u_global_light.m_lights[i].m_color_inverse_square_range.xyz;
|
|
accumulated_color += light_color * attenuation * diffuse_specular;
|
|
}
|
|
return accumulated_color;
|
|
}
|
|
|
|
// Copied because reusing in a loop will be slower
|
|
vec3 calculateLight(int i, vec3 diffuse_color, vec3 normal, vec3 xpos,
|
|
vec3 eyedir, float perceptual_roughness, float metallic)
|
|
{
|
|
vec3 light_to_frag = (u_camera.m_view_matrix *
|
|
vec4(u_global_light.m_lights[i].m_position_radius.xyz,
|
|
1.0)).xyz - xpos;
|
|
float invrange = u_global_light.m_lights[i].m_color_inverse_square_range.w;
|
|
float distance_sq = dot(light_to_frag, light_to_frag);
|
|
if (distance_sq * invrange > 1.)
|
|
return vec3(0.0);
|
|
// SpotLight
|
|
float sattenuation = 1.;
|
|
float sscale = u_global_light.m_lights[i].m_direction_scale_offset.z;
|
|
float distance = sqrt(distance_sq);
|
|
float distance_inverse = 1. / distance;
|
|
vec3 L = light_to_frag * distance_inverse;
|
|
if (sscale != 0.)
|
|
{
|
|
vec3 sdir =
|
|
vec3(u_global_light.m_lights[i].m_direction_scale_offset.xy, 0.);
|
|
sdir.z = sqrt(1. - dot(sdir, sdir)) * sign(sscale);
|
|
sdir = (u_camera.m_view_matrix * vec4(sdir, 0.0)).xyz;
|
|
sattenuation = clamp(dot(-sdir, L) *
|
|
abs(sscale) +
|
|
u_global_light.m_lights[i].m_direction_scale_offset.w, 0.0, 1.0);
|
|
if (sattenuation == 0.)
|
|
return vec3(0.0);
|
|
}
|
|
vec3 diffuse_specular = PBRLight(normal, eyedir, L, diffuse_color,
|
|
perceptual_roughness, metallic);
|
|
float attenuation = 20. / (1. + distance_sq);
|
|
float radius = u_global_light.m_lights[i].m_position_radius.w;
|
|
attenuation *= (radius - distance) / radius;
|
|
attenuation *= sattenuation * sattenuation;
|
|
vec3 light_color =
|
|
u_global_light.m_lights[i].m_color_inverse_square_range.xyz;
|
|
return light_color * attenuation * diffuse_specular;
|
|
}
|