| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- /* env.glsl -- Contains everything you need to manage environment
- *
- * Copyright (c) 2025-2026 Le Juez Victor
- *
- * This software is provided 'as-is', without any express or implied warranty.
- * For conditions of distribution and use, see accompanying LICENSE file.
- */
- /* === Includes === */
- #include "../math.glsl"
- /* === Structures === */
- struct EnvAmbient {
- vec4 rotation;
- vec4 color;
- float energy;
- int irradiance;
- int prefilter;
- };
- struct EnvProbe {
- vec3 position;
- float falloff;
- float range;
- int irradiance;
- int prefilter;
- };
- /* === Uniform Block === */
- layout(std140) uniform EnvBlock {
- EnvProbe uProbes[NUM_PROBES];
- EnvAmbient uAmbient;
- int uNumPrefilterLevels;
- int uNumProbes;
- };
- /* === IBL Functions === */
- float IBL_GetSpecularOcclusion(float NdotV, float ao, float roughness)
- {
- // Lagarde method: https://seblagarde.wordpress.com/wp-content/uploads/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
- return clamp(pow(NdotV + ao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ao, 0.0, 1.0);
- }
- vec3 IBL_SampleIrradiance(samplerCubeArray irradiance, int index, vec3 N)
- {
- return texture(irradiance, vec4(N, float(index))).rgb;
- }
- vec3 IBL_SampleIrradiance(samplerCubeArray irradiance, int index, vec3 N, vec4 rotation)
- {
- return texture(irradiance, vec4(M_Rotate3D(N, rotation), float(index))).rgb;
- }
- vec3 IBL_SamplePrefilter(samplerCubeArray prefilter, int index, vec3 V, vec3 N, float roughness)
- {
- float mipLevel = roughness * float(uNumPrefilterLevels - 1);
- return textureLod(prefilter, vec4(reflect(-V, N), float(index)), mipLevel).rgb;
- }
- vec3 IBL_SamplePrefilter(samplerCubeArray prefilter, int index, vec3 V, vec3 N, vec4 rotation, float roughness)
- {
- float mipLevel = roughness * float(uNumPrefilterLevels - 1);
- return textureLod(prefilter, vec4(M_Rotate3D(reflect(-V, N), rotation), float(index)), mipLevel).rgb;
- }
- void IBL_MultiScattering(inout vec3 irradiance, inout vec3 radiance, vec3 diffuse, vec3 F0, vec2 brdf, float NdotV, float roughness)
- {
- // Adapted from Fdez-Aguera method without the roughness-dependent Fresnel
- // See: https://jcgt.org/published/0008/01/03/paper.pdf
- // Single scattering with standard Fresnel
- vec3 FssEss = F0 * brdf.x + brdf.y;
- // Multiple scattering, from Fdez-Aguera
- float Ess = brdf.x + brdf.y;
- float Ems = 1.0 - Ess;
- vec3 Favg = F0 + (1.0 - F0) / 21.0;
- vec3 FmsD = max(1.0 - (1.0 - Ess) * Favg, vec3(1e-6)); //< avoids division by zero in extreme F0/low roughness cases
- vec3 Fms = FssEss * Favg / FmsD;
- vec3 kD = diffuse * (1.0 - FssEss);
- // Compute final irradiance / radiance
- irradiance *= (Fms * Ems + kD);
- radiance *= FssEss;
- }
- void IBL_SampleProbe(inout vec3 irr, inout vec3 rad, inout float wIrr, inout float wRad, int probeIndex, float roughness, vec3 P, vec3 N, vec3 V)
- {
- EnvProbe probe = uProbes[probeIndex];
- float dist = length(P - probe.position);
- float weight = pow(clamp(1.0 - dist / probe.range, 0.0, 1.0), probe.falloff);
- if (weight < 1e-6) return;
- if (probe.irradiance >= 0) {
- vec3 probeIrr = IBL_SampleIrradiance(uIrradianceTex, probe.irradiance, N);
- irr += probeIrr.rgb * weight;
- wIrr += weight;
- }
- if (probe.prefilter >= 0) {
- vec3 probeRad = IBL_SamplePrefilter(uPrefilterTex, probe.prefilter, V, N, roughness);
- rad += probeRad.rgb * weight;
- wRad += weight;
- }
- }
- /* === Environment Functions === */
- void E_ComputeAmbientAndProbes(inout vec3 diffuse, inout vec3 specular, vec3 kD, vec3 orm, vec3 F0, vec3 P, vec3 N, vec3 V, float NdotV)
- {
- float occlusion = orm.x;
- float roughness = orm.y;
- float metalness = orm.z;
- vec3 irradiance = vec3(0.0);
- float wIrradiance = 0.0;
- vec3 radiance = vec3(0.0);
- float wRadiance = 0.0;
- for (int i = 0; i < uNumProbes; ++i) {
- IBL_SampleProbe(irradiance, radiance, wIrradiance, wRadiance, i, roughness, P, N, V);
- }
- if (wIrradiance > 1.0) {
- float invTotalWeight = 1.0 / wIrradiance;
- irradiance *= invTotalWeight;
- wIrradiance = 1.0;
- }
- if (wRadiance > 1.0) {
- float invTotalWeight = 1.0 / wRadiance;
- radiance *= invTotalWeight;
- wRadiance = 1.0;
- }
- if (wIrradiance < 1.0) {
- vec3 ambientIrr = vec3(0.0);
- if (uAmbient.irradiance < 0) ambientIrr = uAmbient.color.rgb;
- else ambientIrr = IBL_SampleIrradiance(uIrradianceTex, uAmbient.irradiance, N, uAmbient.rotation);
- irradiance += ambientIrr * (1.0 - wIrradiance);
- }
- if (wRadiance < 1.0 && uAmbient.prefilter >= 0) {
- vec3 ambientRad = IBL_SamplePrefilter(uPrefilterTex, uAmbient.prefilter, V, N, uAmbient.rotation, roughness);
- radiance += ambientRad * (1.0 - wRadiance);
- }
- irradiance *= occlusion * uAmbient.energy;
- radiance *= IBL_GetSpecularOcclusion(NdotV, occlusion, roughness);
- vec2 brdf = texture(uBrdfLutTex, vec2(NdotV, roughness)).xy;
- IBL_MultiScattering(irradiance, radiance, kD, F0, brdf, NdotV, roughness);
- diffuse += irradiance;
- specular += radiance;
- }
- void E_ComputeAmbientOnly(inout vec3 diffuse, inout vec3 specular, vec3 kD, vec3 orm, vec3 F0, vec3 P, vec3 N, vec3 V, float NdotV)
- {
- float occlusion = orm.x;
- float roughness = orm.y;
- float metalness = orm.z;
- vec3 irradiance = (uAmbient.irradiance >= 0)
- ? IBL_SampleIrradiance(uIrradianceTex, uAmbient.irradiance, N, uAmbient.rotation).rgb
- : uAmbient.color.rgb;
- irradiance *= occlusion * uAmbient.energy;
- vec3 radiance = vec3(0.0);
- if (uAmbient.prefilter >= 0) {
- radiance = IBL_SamplePrefilter(uPrefilterTex, uAmbient.prefilter, V, N, uAmbient.rotation, roughness).rgb;
- radiance *= IBL_GetSpecularOcclusion(NdotV, occlusion, roughness);
- }
- vec2 brdf = texture(uBrdfLutTex, vec2(NdotV, roughness)).xy;
- IBL_MultiScattering(irradiance, radiance, kD, F0, brdf, NdotV, roughness);
- diffuse += irradiance;
- specular += radiance;
- }
- void E_ComputeAmbientColor(inout vec3 diffuse, vec3 kD, float occlusion)
- {
- diffuse += kD * uAmbient.color.rgb * uAmbient.energy * occlusion;
- }
|