diff --git a/leenkx/Shaders/ssr_pass/ssr_pass.frag.glsl b/leenkx/Shaders/ssr_pass/ssr_pass.frag.glsl index 84aa2fab..e21a16a7 100644 --- a/leenkx/Shaders/ssr_pass/ssr_pass.frag.glsl +++ b/leenkx/Shaders/ssr_pass/ssr_pass.frag.glsl @@ -4,6 +4,7 @@ #include "std/math.glsl" #include "std/gbuffer.glsl" +uniform samplerCube probeTex; uniform sampler2D tex; uniform sampler2D gbufferD; uniform sampler2D gbuffer0; // Normal, roughness @@ -27,95 +28,106 @@ float depth; const int numBinarySearchSteps = 7; const int maxSteps = int(ceil(1.0 / ssrRayStep) * ssrSearchDist); +// Project a view-space hit coordinate into screen UVs vec2 getProjectedCoord(const vec3 hit) { - vec4 projectedCoord = P * vec4(hit, 1.0); - projectedCoord.xy /= projectedCoord.w; - projectedCoord.xy = projectedCoord.xy * 0.5 + 0.5; - #ifdef _InvY - projectedCoord.y = 1.0 - projectedCoord.y; - #endif - return projectedCoord.xy; + vec4 projectedCoord = P * vec4(hit, 1.0); + projectedCoord.xy /= projectedCoord.w; + projectedCoord.xy = projectedCoord.xy * 0.5 + 0.5; + #ifdef _InvY + projectedCoord.y = 1.0 - projectedCoord.y; + #endif + return projectedCoord.xy; } +// Compute depth difference between current ray hit and gbuffer depth float getDeltaDepth(const vec3 hit) { - depth = textureLod(gbufferD, getProjectedCoord(hit), 0.0).r * 2.0 - 1.0; - vec3 viewPos = getPosView(viewRay, depth, cameraProj); - return viewPos.z - hit.z; + depth = textureLod(gbufferD, getProjectedCoord(hit), 0.0).r * 2.0 - 1.0; + vec3 viewPos = getPosView(viewRay, depth, cameraProj); + return viewPos.z - hit.z; } +// Refine hit using binary search vec4 binarySearch(vec3 dir) { - float ddepth; - for (int i = 0; i < numBinarySearchSteps; i++) { - dir *= 0.5; - hitCoord -= dir; - ddepth = getDeltaDepth(hitCoord); - if (ddepth < 0.0) hitCoord += dir; - } - // Ugly discard of hits too far away - #ifdef _CPostprocess - if (abs(ddepth) > PPComp9.z / 500) return vec4(0.0); - #else - if (abs(ddepth) > ssrSearchDist / 500) return vec4(0.0); - #endif - return vec4(getProjectedCoord(hitCoord), 0.0, 1.0); + float ddepth; + for (int i = 0; i < numBinarySearchSteps; i++) { + dir *= 0.5; + hitCoord -= dir; + ddepth = getDeltaDepth(hitCoord); + if (ddepth < 0.0) hitCoord += dir; + } + #ifdef _CPostprocess + if (abs(ddepth) > PPComp9.z / 500) return vec4(0.0); + #else + if (abs(ddepth) > ssrSearchDist / 500) return vec4(0.0); + #endif + return vec4(getProjectedCoord(hitCoord), 0.0, 1.0); } +// Perform raymarching using view-space direction vec4 rayCast(vec3 dir) { - #ifdef _CPostprocess - dir *= PPComp9.x; - #else - dir *= ssrRayStep; - #endif - for (int i = 0; i < maxSteps; i++) { - hitCoord += dir; - if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); - } - return vec4(0.0); + #ifdef _CPostprocess + dir *= PPComp9.x; + #else + dir *= ssrRayStep; + #endif + for (int i = 0; i < maxSteps; i++) { + hitCoord += dir; + if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + } + return vec4(0.0); } void main() { - vec4 g0 = textureLod(gbuffer0, texCoord, 0.0); - float roughness = unpackFloat(g0.b).y; - if (roughness == 1.0) { fragColor.rgb = vec3(0.0); return; } + vec4 g0 = textureLod(gbuffer0, texCoord, 0.0); + float roughness = unpackFloat(g0.b).y; + if (roughness >= 1.0) { fragColor.rgb = vec3(0.0); return; } - float spec = fract(textureLod(gbuffer1, texCoord, 0.0).a); - if (spec == 0.0) { fragColor.rgb = vec3(0.0); return; } + float spec = fract(textureLod(gbuffer1, texCoord, 0.0).a); + if (spec == 0.0) { fragColor.rgb = vec3(0.0); return; } - float d = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0; - if (d == 1.0) { fragColor.rgb = vec3(0.0); return; } + float d = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0; + if (d == 1.0) { fragColor.rgb = vec3(0.0); return; } - vec2 enc = g0.rg; - vec3 n; - n.z = 1.0 - abs(enc.x) - abs(enc.y); - n.xy = n.z >= 0.0 ? enc.xy : octahedronWrap(enc.xy); - n = normalize(n); + // Decode octahedral normal + vec2 enc = g0.rg; + vec3 n; + n.z = 1.0 - abs(enc.x) - abs(enc.y); + n.xy = n.z >= 0.0 ? enc.xy : octahedronWrap(enc.xy); + n = normalize(n); + vec3 viewNormal = normalize(V3 * n); - vec3 viewNormal = V3 * n; - vec3 viewPos = getPosView(viewRay, d, cameraProj); - vec3 reflected = reflect(viewPos, viewNormal); - hitCoord = viewPos; + // View-space position and reflection + vec3 viewPos = getPosView(viewRay, d, cameraProj); + vec3 viewDir = normalize(viewPos); + vec3 reflected = normalize(reflect(viewDir, viewNormal)); - #ifdef _CPostprocess - vec3 dir = reflected * (1.0 - rand(texCoord) * PPComp10.y * roughness) * 2.0; - #else - vec3 dir = reflected * (1.0 - rand(texCoord) * ssrJitter * roughness) * 2.0; - #endif + hitCoord = viewPos; - // * max(ssrMinRayStep, -viewPos.z) - vec4 coords = rayCast(dir); + // Importance sampling jitter based on roughness + float jitterStrength = roughness * roughness; + vec3 randVec = normalize(vec3(rand(texCoord), rand(texCoord * 1.3), rand(texCoord * 2.7)) * 2.0 - 1.0); + vec3 dir = normalize(mix(reflected, randVec, jitterStrength)); - vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy); - float screenEdgeFactor = clamp(1.0 - (deltaCoords.x + deltaCoords.y), 0.0, 1.0); + vec4 coords = rayCast(dir); + vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy); + float screenEdgeFactor = clamp(1.0 - (deltaCoords.x + deltaCoords.y), 0.0, 1.0); - float reflectivity = 1.0 - roughness; - #ifdef _CPostprocess - float intensity = pow(reflectivity, PPComp10.x) * screenEdgeFactor * clamp(-reflected.z, 0.0, 1.0) * clamp((PPComp9.z - length(viewPos - hitCoord)) * (1.0 / PPComp9.z), 0.0, 1.0) * coords.w; - #else - float intensity = pow(reflectivity, ssrFalloffExp) * screenEdgeFactor * clamp(-reflected.z, 0.0, 1.0) * clamp((ssrSearchDist - length(viewPos - hitCoord)) * (1.0 / ssrSearchDist), 0.0, 1.0) * coords.w; - #endif + float reflectivity = 1.0 - roughness; + #ifdef _CPostprocess + float distAtten = clamp((PPComp9.z - length(viewPos - hitCoord)) / PPComp9.z, 0.0, 1.0); + float intensity = pow(reflectivity, PPComp10.x) * screenEdgeFactor * clamp(-reflected.z, 0.0, 1.0) * distAtten * coords.w; + #else + float distAtten = clamp((ssrSearchDist - length(viewPos - hitCoord)) / ssrSearchDist, 0.0, 1.0); + float intensity = pow(reflectivity, ssrFalloffExp) * screenEdgeFactor * clamp(-reflected.z, 0.0, 1.0) * distAtten * coords.w; + #endif - intensity = clamp(intensity, 0.0, 1.0); - vec3 reflCol = textureLod(tex, coords.xy, 0.0).rgb; - reflCol = clamp(reflCol, 0.0, 1.0); - fragColor.rgb = reflCol * intensity * 0.5; + intensity = clamp(intensity, 0.0, 1.0); + vec3 ssrColor = textureLod(tex, coords.xy, roughness * 4.0).rgb; + ssrColor = clamp(ssrColor, 0.0, 1.0); + + // Cubemap fallback with roughness-aware LOD sampling + vec3 cubemapColor = textureLod(probeTex, reflected, roughness * 6.0).rgb; + + // Additively blend SSR with cubemap fallback + fragColor.rgb = mix(cubemapColor, ssrColor, intensity) * spec; } diff --git a/leenkx/Shaders/std/brdf.glsl b/leenkx/Shaders/std/brdf.glsl index cce227e8..95807bcc 100644 --- a/leenkx/Shaders/std/brdf.glsl +++ b/leenkx/Shaders/std/brdf.glsl @@ -1,8 +1,10 @@ #ifndef _BRDF_GLSL_ #define _BRDF_GLSL_ -// http://xlgames-inc.github.io/posts/improvedibl/ -// http://blog.selfshadow.com/publications/s2013-shading-course/ +// Constants +//const float PI = 3.1415926535; + +// Fresnel-Schlick with optimized exponential approximation vec3 f_schlick(const vec3 f0, const float vh) { return f0 + (1.0 - f0) * exp2((-5.55473 * vh - 6.98316) * vh); } @@ -11,82 +13,11 @@ float v_smithschlick(const float nl, const float nv, const float a) { return 1.0 / ((nl * (1.0 - a) + a) * (nv * (1.0 - a) + a)); } -//Uncorrelated masking/shadowing (info below) function -//Because it is uncorrelated, G1(NdotL, a) gives us shadowing, and G1(NdotV, a) gives us masking function. -//Approximation from: https://ubm-twvideo01.s3.amazonaws.com/o1/vault/gdc2017/Presentations/Hammon_Earl_PBR_Diffuse_Lighting.pdf -float g1_approx(const float NdotX, const float alpha) -{ - return (2.0 * NdotX) * (1.0 / (NdotX * (2.0 - alpha) + alpha)); -} - -//Uncorrelated masking-shadowing function -//Approximation from: https://ubm-twvideo01.s3.amazonaws.com/o1/vault/gdc2017/Presentations/Hammon_Earl_PBR_Diffuse_Lighting.pdf -float g2_approx(const float NdotL, const float NdotV, const float alpha) -{ - vec2 helper = (2.0 * vec2(NdotL, NdotV)) * (1.0 / (vec2(NdotL, NdotV) * (2.0 - alpha) + alpha)); - return max(helper.x * helper.y, 0.0); //This can go negative, let's fix that -} - float d_ggx(const float nh, const float a) { float a2 = a * a; float denom = nh * nh * (a2 - 1.0) + 1.0; - denom = max(denom * denom, 0.00006103515625 /* 2^-14 = smallest possible half float value, prevent div by zero */); - return a2 * (1.0 / 3.1415926535) / denom; -} - -vec3 specularBRDF(const vec3 f0, const float roughness, const float nl, const float nh, const float nv, const float vh) { - float a = roughness * roughness; - return d_ggx(nh, a) * g2_approx(nl, nv, a) * f_schlick(f0, vh) / max(4.0 * nv, 1e-5); //NdotL cancels out later -} - -// John Hable - Optimizing GGX Shaders -// http://filmicworlds.com/blog/optimizing-ggx-shaders-with-dotlh/ -vec3 specularBRDFb(const vec3 f0, const float roughness, const float dotNL, const float dotNH, const float dotLH) { - // D - const float pi = 3.1415926535; - float alpha = roughness * roughness; - float alphaSqr = alpha * alpha; - float denom = dotNH * dotNH * (alphaSqr - 1.0) + 1.0; - float D = alphaSqr / (pi * denom * denom); - // F - const float F_a = 1.0; - float F_b = pow(1.0 - dotLH, 5.0); - // V - float vis; - float k = alpha / 2.0; - float k2 = k * k; - float invK2 = 1.0 - k2; - vis = 1.0 / (dotLH * dotLH * invK2 + k2); - vec2 FV_helper = vec2((F_a - F_b) * vis, F_b * vis); - - vec3 FV = f0 * FV_helper.x + FV_helper.y; - vec3 specular = clamp(dotNL, 0.0, 1.0) * D * FV; - return specular / 4.0; // TODO: get rid of / 4.0 -} - -vec3 orenNayarDiffuseBRDF(const vec3 albedo, const float roughness, const float nv, const float nl, const float vh) { - float a = roughness * roughness; - float s = a; - float s2 = s * s; - float vl = 2.0 * vh * vh - 1.0; // Double angle identity - float Cosri = vl - nv * nl; - float C1 = 1.0 - 0.5 * s2 / (s2 + 0.33); - float test = 1.0; - if (Cosri >= 0.0) test = (1.0 / (max(nl, nv))); - float C2 = 0.45 * s2 / (s2 + 0.09) * Cosri * test; - return albedo * max(0.0, nl) * (C1 + C2) * (1.0 + roughness * 0.5); -} - -vec3 lambertDiffuseBRDF(const vec3 albedo, const float nl) { - return albedo * nl; -} - -vec3 surfaceAlbedo(const vec3 baseColor, const float metalness) { - return mix(baseColor, vec3(0.0), metalness); -} - -vec3 surfaceF0(const vec3 baseColor, const float metalness) { - return mix(vec3(0.04), baseColor, metalness); + denom = max(denom * denom, 2e-6); + return a2 / (PI * denom); } float getMipFromRoughness(const float roughness, const float numMipmaps) { @@ -94,47 +25,64 @@ float getMipFromRoughness(const float roughness, const float numMipmaps) { return roughness * numMipmaps; } -float wardSpecular(vec3 N, vec3 H, float dotNL, float dotNV, float dotNH, vec3 fiberDirection, float shinyParallel, float shinyPerpendicular) { - if(dotNL < 0.0 || dotNV < 0.0) { - return 0.0; - } - // fiberDirection - parse from rotation - // shinyParallel - roughness - // shinyPerpendicular - anisotropy +vec3 specularBRDF(vec3 N, vec3 V, vec3 L, vec3 F0, float roughness) +{ + vec3 H = normalize(V + L); - vec3 fiberParallel = normalize(fiberDirection); - vec3 fiberPerpendicular = normalize(cross(N, fiberDirection)); - float dotXH = dot(fiberParallel, H); - float dotYH = dot(fiberPerpendicular, H); - const float PI = 3.1415926535; - float coeff = sqrt(dotNL/dotNV) / (4.0 * PI * shinyParallel * shinyPerpendicular); - float theta = (pow(dotXH/shinyParallel, 2.0) + pow(dotYH/shinyPerpendicular, 2.0)) / (1.0 + dotNH); - return clamp(coeff * exp(-2.0 * theta), 0.0, 1.0); + float NdotL = max(dot(N, L), 0.0); + float NdotV = max(dot(N, V), 0.0); + float NdotH = max(dot(N, H), 0.0); + float VdotH = max(dot(V, H), 0.0); + + float alpha = roughness * roughness; + + float D = d_ggx(NdotH, alpha); + float k = alpha / 2.0; + float G_V = NdotV / (NdotV * (1.0 - k) + k + 1e-5); + float G_L = NdotL / (NdotL * (1.0 - k) + k + 1e-5); + float G = G_V * G_L; + + vec3 F = f_schlick(F0, VdotH); + + return F * D * G / max(4.0 * NdotL * NdotV, 1e-5); } -// https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile -// vec3 EnvBRDFApprox(vec3 SpecularColor, float Roughness, float NoV) { -// const vec4 c0 = { -1, -0.0275, -0.572, 0.022 }; -// const vec4 c1 = { 1, 0.0425, 1.04, -0.04 }; -// vec4 r = Roughness * c0 + c1; -// float a004 = min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y; -// vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw; -// return SpecularColor * AB.x + AB.y; -// } -// float EnvBRDFApproxNonmetal(float Roughness, float NoV) { -// // Same as EnvBRDFApprox( 0.04, Roughness, NoV ) -// const vec2 c0 = { -1, -0.0275 }; -// const vec2 c1 = { 1, 0.0425 }; -// vec2 r = Roughness * c0 + c1; -// return min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y; -// } -float D_Approx(const float Roughness, const float RoL) { - float a = Roughness * Roughness; - float a2 = a * a; - float rcp_a2 = 1.0 / a2;//rcp(a2); - // 0.5 / ln(2), 0.275 / ln(2) - float c = 0.72134752 * rcp_a2 + 0.39674113; - return rcp_a2 * exp2( c * RoL - c ); +vec3 burleyDiffuseBRDF(vec3 N, vec3 V, vec3 L, vec3 albedo, float roughness) +{ + float NdotL = max(dot(N, L), 0.0); + float NdotV = max(dot(N, V), 0.0); + float LdotH = max(dot(L, normalize(L + V)), 0.0); + + float FD90 = 0.5 + 2.0 * LdotH * LdotH * roughness; + float FL = pow(1.0 - NdotL, 5.0); + float FV = pow(1.0 - NdotV, 5.0); + + float diffuse = (1.0 + (FD90 - 1.0) * FL) * (1.0 + (FD90 - 1.0) * FV); + return albedo * (1.0 / PI) * diffuse * NdotL; +} + +// Energy-conserving material albedo (fades out for metals) +vec3 surfaceAlbedo(const vec3 baseColor, const float metalness) { + return baseColor * (1.0 - metalness); +} + +// Metal-aware F0 blending +vec3 surfaceF0(vec3 baseColor, float metalness) { + return mix(vec3(0.04), baseColor, metalness); +} + +// LUT-based approximation of IBL BRDF (Unreal-style) +vec2 EnvBRDFApprox(float Roughness, float NoV) { + const vec4 c0 = vec4(-1, -0.0275, -0.572, 0.022); + const vec4 c1 = vec4(1, 0.0425, 1.04, -0.04); + vec4 r = Roughness * c0 + c1; + float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y; + return vec2(-1.04, 1.04) * a004 + r.zw; +} + +vec3 IBLSpecularApprox(vec3 specColor, float roughness, float NoV) { + vec2 brdf = EnvBRDFApprox(roughness, NoV); + return specColor * brdf.x + brdf.y; } #endif diff --git a/leenkx/Shaders/std/gbuffer.glsl b/leenkx/Shaders/std/gbuffer.glsl index 0ded1461..1ce3a4d2 100644 --- a/leenkx/Shaders/std/gbuffer.glsl +++ b/leenkx/Shaders/std/gbuffer.glsl @@ -15,12 +15,10 @@ vec3 getNor(const vec2 enc) { vec3 getPosView(const vec3 viewRay, const float depth, const vec2 cameraProj) { float linearDepth = cameraProj.y / (cameraProj.x - depth); - //float linearDepth = cameraProj.y / ((depth * 0.5 + 0.5) - cameraProj.x); return viewRay * linearDepth; } vec3 getPos(const vec3 eye, const vec3 eyeLook, const vec3 viewRay, const float depth, const vec2 cameraProj) { - // eyeLook, viewRay should be normalized float linearDepth = cameraProj.y / ((depth * 0.5 + 0.5) - cameraProj.x); float viewZDist = dot(eyeLook, viewRay); vec3 wposition = eye + viewRay * (linearDepth / viewZDist); @@ -28,7 +26,6 @@ vec3 getPos(const vec3 eye, const vec3 eyeLook, const vec3 viewRay, const float } vec3 getPosNoEye(const vec3 eyeLook, const vec3 viewRay, const float depth, const vec2 cameraProj) { - // eyeLook, viewRay should be normalized float linearDepth = cameraProj.y / ((depth * 0.5 + 0.5) - cameraProj.x); float viewZDist = dot(eyeLook, viewRay); vec3 wposition = viewRay * (linearDepth / viewZDist); @@ -71,6 +68,7 @@ vec3 getPos2NoEye(const vec3 eye, const mat4 invVP, const float depth, const vec return pos.xyz - eye; } +// Updated to support separate roughness/metalness storage float packFloat(const float f1, const float f2) { return floor(f1 * 100.0) + min(f2, 1.0 - 1.0 / 100.0); } @@ -80,7 +78,6 @@ vec2 unpackFloat(const float f) { } float packFloat2(const float f1, const float f2) { - // Higher f1 = less precise f2 return floor(f1 * 255.0) + min(f2, 1.0 - 1.0 / 100.0); } @@ -101,21 +98,16 @@ vec3 decodeRGBM(const vec4 rgbm) { return rgbm.rgb * rgbm.a * maxRange; } -vec2 signNotZero(vec2 v) -{ +vec2 signNotZero(vec2 v) { return vec2((v.x >= 0.0) ? +1.0 : -1.0, (v.y >= 0.0) ? +1.0 : -1.0); } -vec2 encode_oct(vec3 v) -{ - // Project the sphere onto the octahedron, and then onto the xy plane +vec2 encode_oct(vec3 v) { vec2 p = v.xy * (1.0 / (abs(v.x) + abs(v.y) + abs(v.z))); - // Reflect the folds of the lower hemisphere over the diagonals return (v.z <= 0.0) ? ((1.0 - abs(p.yx)) * signNotZero(p)) : p; } -vec3 decode_oct(vec2 e) -{ +vec3 decode_oct(vec2 e) { vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y)); if (v.z < 0) v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy); return normalize(v); @@ -147,9 +139,6 @@ vec3 decNor(uint val) { return normal; } -/** - Packs a float in [0, 1] and an integer in [0..15] into a single 16 bit float value. -**/ float packFloatInt16(const float f, const uint i) { const uint numBitFloat = 12; const float maxValFloat = float((1 << numBitFloat) - 1); @@ -165,9 +154,8 @@ void unpackFloatInt16(const float val, out float f, out uint i) { const float maxValFloat = float((1 << numBitFloat) - 1); const uint bitsValue = uint(val); - i = bitsValue >> numBitFloat; f = (bitsValue & ~(0xF << numBitFloat)) / maxValFloat; } -#endif +#endif \ No newline at end of file diff --git a/leenkx/Shaders/std/light.glsl b/leenkx/Shaders/std/light.glsl index 9e1182e2..870aef58 100644 --- a/leenkx/Shaders/std/light.glsl +++ b/leenkx/Shaders/std/light.glsl @@ -1,239 +1,241 @@ -#ifndef _LIGHT_GLSL_ -#define _LIGHT_GLSL_ - -#include "compiled.inc" -#include "std/brdf.glsl" -#include "std/math.glsl" -#ifdef _ShadowMap -#include "std/shadows.glsl" -#endif -#ifdef _VoxelShadow -#include "std/conetrace.glsl" -//!uniform sampler2D voxels_shadows; -#endif -#ifdef _LTC -#include "std/ltc.glsl" -#endif -#ifdef _LightIES -#include "std/ies.glsl" -#endif -#ifdef _SSRS -#include "std/ssrs.glsl" -#endif -#ifdef _Spot -#include "std/light_common.glsl" -#endif - -#ifdef _ShadowMap - #ifdef _SinglePoint - #ifdef _Spot - #ifndef _LTC - uniform sampler2DShadow shadowMapSpot[1]; - uniform sampler2D shadowMapSpotTransparent[1]; - uniform mat4 LWVPSpot[1]; - #endif - #else - uniform samplerCubeShadow shadowMapPoint[1]; - uniform samplerCube shadowMapPointTransparent[1]; - uniform vec2 lightProj; - #endif - #endif - #ifdef _Clusters - #ifdef _SingleAtlas - //!uniform sampler2DShadow shadowMapAtlas; - //!uniform sampler2D shadowMapAtlasTransparent; - #endif - uniform vec2 lightProj; - #ifdef _ShadowMapAtlas - #ifndef _SingleAtlas - uniform sampler2DShadow shadowMapAtlasPoint; - uniform sampler2D shadowMapAtlasPointTransparent; - #endif - #else - uniform samplerCubeShadow shadowMapPoint[4]; - uniform samplerCube shadowMapPointTransparent[4]; - #endif - #ifdef _Spot - #ifdef _ShadowMapAtlas - #ifndef _SingleAtlas - uniform sampler2DShadow shadowMapAtlasSpot; - uniform sampler2D shadowMapAtlasSpotTransparent; - #endif - #else - uniform sampler2DShadow shadowMapSpot[4]; - uniform sampler2D shadowMapSpotTransparent[4]; - #endif - uniform mat4 LWVPSpotArray[maxLightsCluster]; - #endif - #endif -#endif - -#ifdef _LTC -uniform vec3 lightArea0; -uniform vec3 lightArea1; -uniform vec3 lightArea2; -uniform vec3 lightArea3; -uniform sampler2D sltcMat; -uniform sampler2D sltcMag; -#ifdef _ShadowMap -#ifndef _Spot - #ifdef _SinglePoint - uniform sampler2DShadow shadowMapSpot[1]; - uniform sampler2D shadowMapSpotTransparent[1]; - uniform mat4 LWVPSpot[1]; - #endif - #ifdef _Clusters - uniform sampler2DShadow shadowMapSpot[maxLightsCluster]; - uniform sampler2D shadowMapSpotTransparent[maxLightsCluster]; - uniform mat4 LWVPSpotArray[maxLightsCluster]; - #endif - #endif -#endif -#endif - -vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol, - const vec3 albedo, const float rough, const float spec, const vec3 f0 - #ifdef _ShadowMap - , int index, float bias, bool receiveShadow, bool transparent - #endif - #ifdef _Spot - , const bool isSpot, const float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right - #endif - #ifdef _VoxelShadow - , sampler3D voxels, sampler3D voxelsSDF, float clipmaps[10 * voxelgiClipmapCount] - #endif - #ifdef _MicroShadowing - , float occ - #endif - #ifdef _SSRS - , sampler2D gbufferD, mat4 invVP, vec3 eye - #endif - ) { - vec3 ld = lp - p; - vec3 l = normalize(ld); - vec3 h = normalize(v + l); - float dotNH = max(0.0, dot(n, h)); - float dotVH = max(0.0, dot(v, h)); - float dotNL = max(0.0, dot(n, l)); - - #ifdef _LTC - float theta = acos(dotNV); - vec2 tuv = vec2(rough, theta / (0.5 * PI)); - tuv = tuv * LUT_SCALE + LUT_BIAS; - vec4 t = textureLod(sltcMat, tuv, 0.0); - mat3 invM = mat3( - vec3(1.0, 0.0, t.y), - vec3(0.0, t.z, 0.0), - vec3(t.w, 0.0, t.x)); - float ltcspec = ltcEvaluate(n, v, dotNV, p, invM, lightArea0, lightArea1, lightArea2, lightArea3); - ltcspec *= textureLod(sltcMag, tuv, 0.0).a; - float ltcdiff = ltcEvaluate(n, v, dotNV, p, mat3(1.0), lightArea0, lightArea1, lightArea2, lightArea3); - vec3 direct = albedo * ltcdiff + ltcspec * spec * 0.05; - #else - vec3 direct = lambertDiffuseBRDF(albedo, dotNL) + - specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec; - #endif - - direct *= attenuate(distance(p, lp)); - direct *= lightCol; - - #ifdef _MicroShadowing - direct *= clamp(dotNL + 2.0 * occ * occ - 1.0, 0.0, 1.0); - #endif - - #ifdef _SSRS - direct *= traceShadowSS(l, p, gbufferD, invVP, eye); - #endif - - #ifdef _VoxelShadow - direct *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, l, clipmaps, gl_FragCoord.xy).r) * voxelgiShad; - #endif - - #ifdef _LTC - #ifdef _ShadowMap - if (receiveShadow) { - #ifdef _SinglePoint - vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0); - direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); - #endif - #ifdef _Clusters - vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); - if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); - else if (index == 1) direct *= shadowTest(shadowMapSpot[1], shadowMapSpotTransparent[1], lPos.xyz / lPos.w, bias, transparent); - else if (index == 2) direct *= shadowTest(shadowMapSpot[2], shadowMapSpotTransparent[2], lPos.xyz / lPos.w, bias, transparent); - else if (index == 3) direct *= shadowTest(shadowMapSpot[3], shadowMapSpotTransparent[3], lPos.xyz / lPos.w, bias, transparent); - #endif - } - #endif - return direct; - #endif - - #ifdef _Spot - if (isSpot) { - direct *= spotlightMask(l, spotDir, right, scale, spotSize, spotBlend); - - #ifdef _ShadowMap - if (receiveShadow) { - #ifdef _SinglePoint - vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0); - direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); - #endif - #ifdef _Clusters - vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); - #ifdef _ShadowMapAtlas - direct *= shadowTest( - #ifndef _SingleAtlas - shadowMapAtlasSpot, shadowMapAtlasSpotTransparent - #else - shadowMapAtlas, shadowMapAtlasTransparent - #endif - , lPos.xyz / lPos.w, bias, transparent - ); - #else - if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); - else if (index == 1) direct *= shadowTest(shadowMapSpot[1], shadowMapSpotTransparent[1], lPos.xyz / lPos.w, bias, transparent); - else if (index == 2) direct *= shadowTest(shadowMapSpot[2], shadowMapSpotTransparent[2], lPos.xyz / lPos.w, bias, transparent); - else if (index == 3) direct *= shadowTest(shadowMapSpot[3], shadowMapSpotTransparent[3], lPos.xyz / lPos.w, bias, transparent); - #endif - #endif - } - #endif - return direct; - } - #endif - - #ifdef _LightIES - direct *= iesAttenuation(-l); - #endif - - #ifdef _ShadowMap - if (receiveShadow) { - #ifdef _SinglePoint - #ifndef _Spot - direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent); - #endif - #endif - #ifdef _Clusters - #ifdef _ShadowMapAtlas - direct *= PCFFakeCube( - #ifndef _SingleAtlas - shadowMapAtlasPoint, shadowMapAtlasPointTransparent - #else - shadowMapAtlas, shadowMapAtlasTransparent - #endif - , ld, -l, bias, lightProj, n, index, transparent - ); - #else - if (index == 0) direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent); - else if (index == 1) direct *= PCFCube(shadowMapPoint[1], shadowMapPointTransparent[1], ld, -l, bias, lightProj, n, transparent); - else if (index == 2) direct *= PCFCube(shadowMapPoint[2], shadowMapPointTransparent[2], ld, -l, bias, lightProj, n, transparent); - else if (index == 3) direct *= PCFCube(shadowMapPoint[3], shadowMapPointTransparent[3], ld, -l, bias, lightProj, n, transparent); - #endif - #endif - } - #endif - - return direct; -} - -#endif +#ifndef _LIGHT_GLSL_ +#define _LIGHT_GLSL_ + +#include "compiled.inc" +#include "std/brdf.glsl" +#include "std/math.glsl" +#ifdef _ShadowMap +#include "std/shadows.glsl" +#endif +#ifdef _VoxelShadow +#include "std/conetrace.glsl" +//!uniform sampler2D voxels_shadows; +#endif +#ifdef _LTC +#include "std/ltc.glsl" +#endif +#ifdef _LightIES +#include "std/ies.glsl" +#endif +#ifdef _SSRS +#include "std/ssrs.glsl" +#endif +#ifdef _Spot +#include "std/light_common.glsl" +#endif + +#ifdef _ShadowMap + #ifdef _SinglePoint + #ifdef _Spot + #ifndef _LTC + uniform sampler2DShadow shadowMapSpot[1]; + uniform sampler2D shadowMapSpotTransparent[1]; + uniform mat4 LWVPSpot[1]; + #endif + #else + uniform samplerCubeShadow shadowMapPoint[1]; + uniform samplerCube shadowMapPointTransparent[1]; + uniform vec2 lightProj; + #endif + #endif + #ifdef _Clusters + #ifdef _SingleAtlas + //!uniform sampler2DShadow shadowMapAtlas; + //!uniform sampler2D shadowMapAtlasTransparent; + #endif + uniform vec2 lightProj; + #ifdef _ShadowMapAtlas + #ifndef _SingleAtlas + uniform sampler2DShadow shadowMapAtlasPoint; + uniform sampler2D shadowMapAtlasPointTransparent; + #endif + #else + uniform samplerCubeShadow shadowMapPoint[4]; + uniform samplerCube shadowMapPointTransparent[4]; + #endif + #ifdef _Spot + #ifdef _ShadowMapAtlas + #ifndef _SingleAtlas + uniform sampler2DShadow shadowMapAtlasSpot; + uniform sampler2D shadowMapAtlasSpotTransparent; + #endif + #else + uniform sampler2DShadow shadowMapSpot[4]; + uniform sampler2D shadowMapSpotTransparent[4]; + #endif + uniform mat4 LWVPSpotArray[maxLightsCluster]; + #endif + #endif +#endif + +#ifdef _LTC +uniform vec3 lightArea0; +uniform vec3 lightArea1; +uniform vec3 lightArea2; +uniform vec3 lightArea3; +uniform sampler2D sltcMat; +uniform sampler2D sltcMag; +#ifdef _ShadowMap +#ifndef _Spot + #ifdef _SinglePoint + uniform sampler2DShadow shadowMapSpot[1]; + uniform sampler2D shadowMapSpotTransparent[1]; + uniform mat4 LWVPSpot[1]; + #endif + #ifdef _Clusters + uniform sampler2DShadow shadowMapSpot[maxLightsCluster]; + uniform sampler2D shadowMapSpotTransparent[maxLightsCluster]; + uniform mat4 LWVPSpotArray[maxLightsCluster]; + #endif + #endif +#endif +#endif + +vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol, + const vec3 albedo, const float rough, const float spec, const vec3 f0 + #ifdef _ShadowMap + , int index, float bias, bool receiveShadow, bool transparent + #endif + #ifdef _Spot + , const bool isSpot, const float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right + #endif + #ifdef _VoxelShadow + , sampler3D voxels, sampler3D voxelsSDF, float clipmaps[10 * voxelgiClipmapCount] + #endif + #ifdef _MicroShadowing + , float occ + #endif + #ifdef _SSRS + , sampler2D gbufferD, mat4 invVP, vec3 eye + #endif + ) { + vec3 ld = lp - p; + vec3 l = normalize(ld); + vec3 h = normalize(v + l); + float dotNH = max(0.0, dot(n, h)); + float dotVH = max(0.0, dot(v, h)); + float dotNL = max(0.0, dot(n, l)); + + #ifdef _LTC + float theta = acos(dotNV); + vec2 tuv = vec2(rough, theta / (0.5 * PI)); + tuv = tuv * LUT_SCALE + LUT_BIAS; + vec4 t = textureLod(sltcMat, tuv, 0.0); + mat3 invM = mat3( + vec3(1.0, 0.0, t.y), + vec3(0.0, t.z, 0.0), + vec3(t.w, 0.0, t.x)); + float ltcspec = ltcEvaluate(n, v, dotNV, p, invM, lightArea0, lightArea1, lightArea2, lightArea3); + ltcspec *= textureLod(sltcMag, tuv, 0.0).a; + float ltcdiff = ltcEvaluate(n, v, dotNV, p, mat3(1.0), lightArea0, lightArea1, lightArea2, lightArea3); + vec3 direct = albedo * ltcdiff + ltcspec * spec * 0.05; + #else + vec3 F0 = surfaceF0(albedo, spec); // spec used as the metalness value + + vec3 direct = burleyDiffuseBRDF(n, v, l, albedo, rough) * (1.0 - spec) + + specularBRDF(n, v, l, f0, rough) * spec; + #endif + + direct *= attenuate(distance(p, lp)); + direct *= lightCol; + + #ifdef _MicroShadowing + direct *= clamp(dotNL + 2.0 * occ * occ - 1.0, 0.0, 1.0); + #endif + + #ifdef _SSRS + direct *= traceShadowSS(l, p, gbufferD, invVP, eye); + #endif + + #ifdef _VoxelShadow + direct *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, l, clipmaps, gl_FragCoord.xy).r) * voxelgiShad; + #endif + + #ifdef _LTC + #ifdef _ShadowMap + if (receiveShadow) { + #ifdef _SinglePoint + vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0); + direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); + #endif + #ifdef _Clusters + vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); + if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); + else if (index == 1) direct *= shadowTest(shadowMapSpot[1], shadowMapSpotTransparent[1], lPos.xyz / lPos.w, bias, transparent); + else if (index == 2) direct *= shadowTest(shadowMapSpot[2], shadowMapSpotTransparent[2], lPos.xyz / lPos.w, bias, transparent); + else if (index == 3) direct *= shadowTest(shadowMapSpot[3], shadowMapSpotTransparent[3], lPos.xyz / lPos.w, bias, transparent); + #endif + } + #endif + return direct; + #endif + + #ifdef _Spot + if (isSpot) { + direct *= spotlightMask(l, spotDir, right, scale, spotSize, spotBlend); + + #ifdef _ShadowMap + if (receiveShadow) { + #ifdef _SinglePoint + vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0); + direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); + #endif + #ifdef _Clusters + vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); + #ifdef _ShadowMapAtlas + direct *= shadowTest( + #ifndef _SingleAtlas + shadowMapAtlasSpot, shadowMapAtlasSpotTransparent + #else + shadowMapAtlas, shadowMapAtlasTransparent + #endif + , lPos.xyz / lPos.w, bias, transparent + ); + #else + if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent); + else if (index == 1) direct *= shadowTest(shadowMapSpot[1], shadowMapSpotTransparent[1], lPos.xyz / lPos.w, bias, transparent); + else if (index == 2) direct *= shadowTest(shadowMapSpot[2], shadowMapSpotTransparent[2], lPos.xyz / lPos.w, bias, transparent); + else if (index == 3) direct *= shadowTest(shadowMapSpot[3], shadowMapSpotTransparent[3], lPos.xyz / lPos.w, bias, transparent); + #endif + #endif + } + #endif + return direct; + } + #endif + + #ifdef _LightIES + direct *= iesAttenuation(-l); + #endif + + #ifdef _ShadowMap + if (receiveShadow) { + #ifdef _SinglePoint + #ifndef _Spot + direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent); + #endif + #endif + #ifdef _Clusters + #ifdef _ShadowMapAtlas + direct *= PCFFakeCube( + #ifndef _SingleAtlas + shadowMapAtlasPoint, shadowMapAtlasPointTransparent + #else + shadowMapAtlas, shadowMapAtlasTransparent + #endif + , ld, -l, bias, lightProj, n, index, transparent + ); + #else + if (index == 0) direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent); + else if (index == 1) direct *= PCFCube(shadowMapPoint[1], shadowMapPointTransparent[1], ld, -l, bias, lightProj, n, transparent); + else if (index == 2) direct *= PCFCube(shadowMapPoint[2], shadowMapPointTransparent[2], ld, -l, bias, lightProj, n, transparent); + else if (index == 3) direct *= PCFCube(shadowMapPoint[3], shadowMapPointTransparent[3], ld, -l, bias, lightProj, n, transparent); + #endif + #endif + } + #endif + + return direct; +} + +#endif