Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 26 additions & 22 deletions drivers/gles3/shaders/scene.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -1493,6 +1493,11 @@ float SchlickFresnel(float u) {
return m2 * m2 * m; // pow(m,5)
}

float V_Kelemen(float LdotH) {
// Kelemen 2001, "A Microfacet Based Coupled Specular-Matte BRDF Model with Importance Sampling"
return 0.25 / (LdotH * LdotH + 1e-4);
}

void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_directional, float attenuation, vec3 f0, float roughness, float metallic, float specular_amount, vec3 albedo, inout float alpha, vec2 screen_uv,
#ifdef LIGHT_BACKLIGHT_USED
vec3 backlight,
Expand Down Expand Up @@ -1531,6 +1536,8 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_di
float NdotV = dot(N, V);
float cNdotV = max(NdotV, 1e-4);

float cc_attenuation = 1.0;

#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_CLEARCOAT_USED)
vec3 H = normalize(V + L);
#endif
Expand All @@ -1543,6 +1550,20 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_di
float cLdotH = clamp(A + dot(L, H), 0.0, 1.0);
#endif

#if defined(LIGHT_CLEARCOAT_USED)
// Clearcoat ignores normal_map, use vertex normal instead
float ccNdotH = clamp(A + dot(vertex_normal, H), 0.0, 1.0);
float cLdotH5 = SchlickFresnel(cLdotH);

float Dr = D_GGX(ccNdotH, mix(0.001, 0.1, clearcoat_roughness));
float Gr = V_Kelemen(cLdotH);
float Fr = mix(0.04, 1.0, cLdotH5) * clearcoat;
cc_attenuation = 1.0 - Fr;
float clearcoat_specular_brdf_NL = clearcoat * Gr * Fr * Dr * cNdotL;

specular_light += clearcoat_specular_brdf_NL * light_color * attenuation * specular_amount;
#endif // LIGHT_CLEARCOAT_USED

if (metallic < 1.0) {
float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance

Expand All @@ -1564,10 +1585,10 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_di
diffuse_brdf_NL = cNdotL * (1.0 / M_PI);
#endif

diffuse_light += light_color * diffuse_brdf_NL * attenuation;
diffuse_light += light_color * diffuse_brdf_NL * attenuation * cc_attenuation;

#if defined(LIGHT_BACKLIGHT_USED)
diffuse_light += light_color * (vec3(1.0 / M_PI) - diffuse_brdf_NL) * backlight * attenuation;
diffuse_light += light_color * (vec3(1.0 / M_PI) - diffuse_brdf_NL) * backlight * attenuation * cc_attenuation;
#endif

#if defined(LIGHT_RIM_USED)
Expand Down Expand Up @@ -1609,35 +1630,18 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_di
float G = V_GGX(cNdotL, cNdotV, alpha_ggx);
#endif // LIGHT_ANISOTROPY_USED
// F
#if !defined(LIGHT_CLEARCOAT_USED)
float cLdotH5 = SchlickFresnel(cLdotH);
#endif
// Calculate Fresnel using cheap approximate specular occlusion term from Filament:
// https://google.github.io/filament/Filament.html#lighting/occlusion/specularocclusion
float f90 = clamp(50.0 * f0.g, 0.0, 1.0);
vec3 F = f0 + (f90 - f0) * cLdotH5;

vec3 specular_brdf_NL = cNdotL * D * F * G;

specular_light += specular_brdf_NL * light_color * attenuation * specular_amount;
#endif

#if defined(LIGHT_CLEARCOAT_USED)
// Clearcoat ignores normal_map, use vertex normal instead
float ccNdotL = max(min(A + dot(vertex_normal, L), 1.0), 0.0);
float ccNdotH = clamp(A + dot(vertex_normal, H), 0.0, 1.0);
float ccNdotV = max(dot(vertex_normal, V), 1e-4);

#if !defined(SPECULAR_SCHLICK_GGX)
float cLdotH5 = SchlickFresnel(cLdotH);
specular_light += specular_brdf_NL * light_color * attenuation * cc_attenuation * specular_amount;
#endif
float Dr = D_GGX(ccNdotH, mix(0.001, 0.1, clearcoat_roughness));
float Gr = 0.25 / (cLdotH * cLdotH + 1e-4);
float Fr = mix(.04, 1.0, cLdotH5);
float clearcoat_specular_brdf_NL = clearcoat * Gr * Fr * Dr * cNdotL;

specular_light += clearcoat_specular_brdf_NL * light_color * attenuation * specular_amount;
// TODO: Clearcoat adds light to the scene right now (it is non-energy conserving), both diffuse and specular need to be scaled by (1.0 - FR)
// but to do so we need to rearrange this entire function
#endif // LIGHT_CLEARCOAT_USED
}

#ifdef USE_SHADOW_TO_OPACITY
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1680,32 +1680,28 @@ void fragment_shader(in SceneData scene_data) {
#endif

#ifdef LIGHT_CLEARCOAT_USED
vec3 cc_specular_light = vec3(0.0);
vec3 cc_ref_vec = vec3(0.0);

if (bool(scene_data.flags & SCENE_DATA_FLAGS_USE_REFLECTION_CUBEMAP)) {
float NoV = max(dot(geo_normal, view), 0.0001); // We want to use geometric normal, not normal_map
vec3 ref_vec = reflect(-view, geo_normal);
ref_vec = mix(ref_vec, geo_normal, clearcoat_roughness * clearcoat_roughness);
// The clear coat layer assumes an IOR of 1.5 (4% reflectance)
float Fc = clearcoat * (0.04 + 0.96 * SchlickFresnel(NoV));
float attenuation = 1.0 - Fc;
ambient_light *= attenuation;
indirect_specular_light *= attenuation;

float horizon = min(1.0 + dot(ref_vec, indirect_normal), 1.0);
ref_vec = scene_data.radiance_inverse_xform * ref_vec;
float roughness_lod = mix(0.001, 0.1, sqrt(clearcoat_roughness)) * MAX_ROUGHNESS_LOD;
// We want to use geometric normal, not normal_map
cc_ref_vec = reflect(-view, geo_normal);
cc_ref_vec = mix(cc_ref_vec, geo_normal, mix(0.001, 0.1, clearcoat_roughness));

vec3 cc_radiance_ref_vec = scene_data.radiance_inverse_xform * cc_ref_vec;
float roughness_lod = sqrt(mix(0.001, 0.1, clearcoat_roughness)) * MAX_ROUGHNESS_LOD;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY

float lod, blend;
blend = modf(roughness_lod, lod);
vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb;
clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend);
vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cc_radiance_ref_vec, lod)).rgb;
clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cc_radiance_ref_vec, lod + 1)).rgb, blend);

#else
vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb;
vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cc_radiance_ref_vec, roughness_lod).rgb;

#endif //USE_RADIANCE_CUBEMAP_ARRAY
indirect_specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a;
cc_specular_light += clearcoat_light * scene_data.IBL_exposure_normalization * scene_data.ambient_light_color_energy.a;
}
#endif // LIGHT_CLEARCOAT_USED
#endif // !AMBIENT_LIGHT_DISABLED
Expand Down Expand Up @@ -1957,6 +1953,9 @@ void fragment_shader(in SceneData scene_data) {

vec4 reflection_accum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 ambient_accum = vec4(0.0, 0.0, 0.0, 0.0);
#ifdef LIGHT_CLEARCOAT_USED
vec3 cc_reflection_accum = vec3(0.0, 0.0, 0.0);
#endif

uint cluster_reflection_offset = cluster_offset + implementation_data.cluster_type_size * 3;

Expand Down Expand Up @@ -2006,7 +2005,11 @@ void fragment_shader(in SceneData scene_data) {
break;
}

reflection_process(reflection_index, vertex, ref_vec, normal, roughness, ambient_light, indirect_specular_light, ambient_accum, reflection_accum);
reflection_process(reflection_index, vertex, ref_vec, normal, roughness, ambient_light, indirect_specular_light,
#ifdef LIGHT_CLEARCOAT_USED
cc_specular_light, cc_ref_vec, mix(0.001, 0.1, clearcoat_roughness), cc_reflection_accum,
#endif
ambient_accum, reflection_accum);
}
}

Expand All @@ -2020,6 +2023,9 @@ void fragment_shader(in SceneData scene_data) {

if (reflection_accum.a > 0.0) {
indirect_specular_light = reflection_accum.rgb;
#ifdef LIGHT_CLEARCOAT_USED
cc_specular_light = cc_reflection_accum.rgb;
#endif
}

#if !defined(USE_LIGHTMAP)
Expand Down Expand Up @@ -2094,6 +2100,15 @@ void fragment_shader(in SceneData scene_data) {

//this saves some VGPRs
vec3 f0 = F0(metallic, specular, albedo);

#ifdef LIGHT_CLEARCOAT_USED
// The base layer's f0 is computed assuming an interface from air to an IOR
// of 1.5, but the clear coat layer forms an interface from IOR 1.5 to IOR
// 1.5. We recompute f0 by first computing its IOR, then reconverting to f0
// by using the correct interface
f0 = mix(f0, f0_Clear_Coat_To_Surface(f0), clearcoat);
#endif

#ifndef AMBIENT_LIGHT_DISABLED
{
#if defined(DIFFUSE_TOON)
Expand All @@ -2109,7 +2124,24 @@ void fragment_shader(in SceneData scene_data) {
// cheap luminance approximation
float f90 = clamp(50.0 * f0.g, metallic, 1.0);
indirect_specular_light *= energy_compensation * ((f90 - f0) * envBRDF.x + f0 * envBRDF.y);

#ifdef LIGHT_CLEARCOAT_USED
float geo_NdotV = max(dot(geo_normal, view), 0.0001); // We want to use geometric normal, not normal_map
// The clearcoat layer assumes an IOR of 1.5 (4% reflectance).
// Attenuate underlying diffuse/specular by clearcoat fresnel (ONLY fresnel, hence we don't just invert the BRDF below).
float NdotV5 = SchlickFresnel(geo_NdotV);
float F = mix(0.04, 1.0, NdotV5) * clearcoat;
float cc_attenuation = 1.0 - F;

ambient_light *= cc_attenuation;
indirect_specular_light *= cc_attenuation;

// Clearcoat Layer
// We don't need DFG for clearcoat, so we can use the fresnel directly.
indirect_specular_light += cc_specular_light * (F * clearcoat);
#endif

#endif // DIFFUSE_TOON
}

#endif // !AMBIENT_LIGHT_DISABLED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1596,33 +1596,28 @@ void main() {
ambient_light = mix(ambient_light, custom_irradiance.rgb, custom_irradiance.a);
#endif // CUSTOM_IRRADIANCE_USED
#ifdef LIGHT_CLEARCOAT_USED
hvec3 cc_specular_light = hvec3(0.0);
hvec3 cc_ref_vec = hvec3(0.0);

if (sc_scene_use_reflection_cubemap()) {
half NoV = max(dot(geo_normal, view), half(0.0001));
hvec3 ref_vec = reflect(-view, geo_normal);
ref_vec = mix(ref_vec, geo_normal, clearcoat_roughness * clearcoat_roughness);
// The clear coat layer assumes an IOR of 1.5 (4% reflectance)
half Fc = clearcoat * (half(0.04) + half(0.96) * SchlickFresnel(NoV));
half attenuation = half(1.0) - Fc;
ambient_light *= attenuation;
indirect_specular_light *= attenuation;
cc_ref_vec = reflect(-view, geo_normal);
cc_ref_vec = mix(cc_ref_vec, geo_normal, mix(0.001, 0.1, clearcoat_roughness));

half horizon = min(half(1.0) + dot(ref_vec, indirect_normal), half(1.0));
ref_vec = hvec3(scene_data.radiance_inverse_xform * vec3(ref_vec));
float roughness_lod = mix(0.001, 0.1, sqrt(float(clearcoat_roughness))) * MAX_ROUGHNESS_LOD;
hvec3 cc_radiance_ref_vec = hvec3(scene_data.radiance_inverse_xform * vec3(cc_ref_vec));
float roughness_lod = sqrt(mix(0.001, 0.1, clearcoat_roughness)) * MAX_ROUGHNESS_LOD;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY

float lod;
half blend = half(modf(roughness_lod, lod));
hvec3 clearcoat_sample_a = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb);
hvec3 clearcoat_sample_b = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb);
hvec3 clearcoat_sample_a = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cc_radiance_ref_vec, lod)).rgb);
hvec3 clearcoat_sample_b = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cc_radiance_ref_vec, lod + 1)).rgb);
hvec3 clearcoat_light = mix(clearcoat_sample_a, clearcoat_sample_b, blend);

#else
hvec3 clearcoat_light = hvec3(textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_vec), roughness_lod).rgb);
hvec3 clearcoat_light = hvec3(textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(cc_radiance_ref_vec), roughness_lod).rgb);

#endif //USE_RADIANCE_CUBEMAP_ARRAY
indirect_specular_light += clearcoat_light * horizon * horizon * Fc * half(scene_data.ambient_light_color_energy.a);
cc_specular_light += clearcoat_light * half(scene_data.IBL_exposure_normalization) * half(scene_data.ambient_light_color_energy.a);
}
#endif // LIGHT_CLEARCOAT_USED
#endif // !AMBIENT_LIGHT_DISABLED
Expand Down Expand Up @@ -1714,6 +1709,9 @@ void main() {
if (reflection_probe_count > 0) {
hvec4 reflection_accum = hvec4(0.0);
hvec4 ambient_accum = hvec4(0.0);
#ifdef LIGHT_CLEARCOAT_USED
hvec3 cc_reflection_accum = hvec3(0.0, 0.0, 0.0);
#endif

#ifdef LIGHT_ANISOTROPY_USED
// https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy
Expand All @@ -1739,7 +1737,11 @@ void main() {
break;
}

reflection_process(reflection_index, vertex, ref_vec, normal, roughness, ambient_light, indirect_specular_light, ambient_accum, reflection_accum);
reflection_process(reflection_index, vertex, ref_vec, normal, roughness, ambient_light, indirect_specular_light,
#ifdef LIGHT_CLEARCOAT_USED
cc_specular_light, cc_ref_vec, mix(0.001, 0.1, clearcoat_roughness), cc_reflection_accum,
#endif
ambient_accum, reflection_accum);
}

if (ambient_accum.a < half(1.0)) {
Expand All @@ -1752,6 +1754,9 @@ void main() {

if (reflection_accum.a > half(0.0)) {
indirect_specular_light = reflection_accum.rgb;
#ifdef LIGHT_CLEARCOAT_USED
cc_specular_light = cc_reflection_accum.rgb;
#endif
}

#if !defined(USE_LIGHTMAP)
Expand Down Expand Up @@ -1791,6 +1796,14 @@ void main() {
//this saves some VGPRs
hvec3 f0 = F0(metallic, specular, albedo);

#ifdef LIGHT_CLEARCOAT_USED
// The base layer's f0 is computed assuming an interface from air to an IOR
// of 1.5, but the clear coat layer forms an interface from IOR 1.5 to IOR
// 1.5. We recompute f0 by first computing its IOR, then reconverting to f0
// by using the correct interface
f0 = mix(f0, f0_Clear_Coat_To_Surface(f0), clearcoat);
#endif

#ifndef AMBIENT_LIGHT_DISABLED
{
#if defined(DIFFUSE_TOON)
Expand All @@ -1810,7 +1823,23 @@ void main() {
hvec2 env = hvec2(-1.04, 1.04) * a004 + r.zw;

indirect_specular_light *= env.x * f0 + env.y * clamp(half(50.0) * f0.g, metallic, half(1.0));

#ifdef LIGHT_CLEARCOAT_USED
half geo_NdotV = max(dot(geo_normal, view), half(0.0001)); // We want to use geometric normal, not normal_map
// The clearcoat layer assumes an IOR of 1.5 (4% reflectance).
// Attenuate underlying diffuse/specular by clearcoat fresnel (ONLY fresnel, hence we don't just invert the BRDF below).
half NdotV5 = SchlickFresnel(geo_NdotV);
half F = mix(half(0.04), half(1.0), NdotV5) * clearcoat;
half cc_attenuation = half(1.0) - F;

ambient_light *= cc_attenuation;
indirect_specular_light *= cc_attenuation;

// We don't need a BRDF approximation for clearcoat, so we can use the fresnel directly.
indirect_specular_light += cc_specular_light * (F * clearcoat);
#endif

#endif // DIFFUSE_TOON
}

#endif // !AMBIENT_LIGHT_DISABLED
Expand Down
Loading