diff --git a/leenkx/Shaders/blur_adaptive_pass/blur_adaptive_pass.frag.glsl b/leenkx/Shaders/blur_adaptive_pass/blur_adaptive_pass.frag.glsl index 2afa8b2e..96a7dd54 100644 --- a/leenkx/Shaders/blur_adaptive_pass/blur_adaptive_pass.frag.glsl +++ b/leenkx/Shaders/blur_adaptive_pass/blur_adaptive_pass.frag.glsl @@ -6,6 +6,7 @@ uniform sampler2D tex; uniform sampler2D gbuffer0; // Roughness +uniform sampler2D gbufferD; // Depth uniform vec2 dirInv; @@ -14,19 +15,46 @@ out vec4 fragColor; void main() { float roughness = textureLod(gbuffer0, texCoord, 0.0).b; - // if (roughness == 0.0) { // Always blur for now, non blured output can produce noise - // fragColor.rgb = textureLod(tex, texCoord).rgb; - // return; - // } - if (roughness >= 0.8) { // No reflections + if (roughness >= 0.8) { fragColor.rgb = textureLod(tex, texCoord, 0.0).rgb; return; } - - fragColor.rgb = textureLod(tex, texCoord + dirInv * 2.5, 0.0).rgb; - fragColor.rgb += textureLod(tex, texCoord + dirInv * 1.5, 0.0).rgb; - fragColor.rgb += textureLod(tex, texCoord, 0.0).rgb; - fragColor.rgb += textureLod(tex, texCoord - dirInv * 1.5, 0.0).rgb; - fragColor.rgb += textureLod(tex, texCoord - dirInv * 2.5, 0.0).rgb; - fragColor.rgb /= vec3(5.0); + if (roughness < 0.01) { + fragColor.rgb = textureLod(tex, texCoord, 0.0).rgb; + return; + } + + float blurRadius = 1.0 + roughness * 4.0; + vec3 center = textureLod(tex, texCoord, 0.0).rgb; + + float centerDepth = textureLod(gbufferD, texCoord, 0.0).r; + + float w0 = 1.0 / (1.0 + roughness * 2.0); + float w1 = 1.0 / (1.0 + roughness); + float w2 = 1.0 / (1.0 + roughness * 0.5); + float totalW = w0; + + fragColor.rgb = center * w0; + + vec2 offsets[4]; + offsets[0] = dirInv * blurRadius * 2.5; + offsets[1] = dirInv * blurRadius * 1.5; + offsets[2] = -dirInv * blurRadius * 1.5; + offsets[3] = -dirInv * blurRadius * 2.5; + float weights[4]; + weights[0] = w2; + weights[1] = w1; + weights[2] = w1; + weights[3] = w2; + + for (int i = 0; i < 4; i++) { + vec2 sampleTC = texCoord + offsets[i]; + float sampleDepth = textureLod(gbufferD, sampleTC, 0.0).r; + float depthWeight = exp(-abs(centerDepth - sampleDepth) * 100.0); + float w = weights[i] * depthWeight; + fragColor.rgb += textureLod(tex, sampleTC, 0.0).rgb * w; + totalW += w; + } + + fragColor.rgb /= vec3(totalW); } diff --git a/leenkx/Shaders/custom_mat_presets/custom_mat_deferred.frag.glsl b/leenkx/Shaders/custom_mat_presets/custom_mat_deferred.frag.glsl index 49f94393..04fb5ec4 100644 --- a/leenkx/Shaders/custom_mat_presets/custom_mat_deferred.frag.glsl +++ b/leenkx/Shaders/custom_mat_presets/custom_mat_deferred.frag.glsl @@ -17,12 +17,12 @@ in vec3 wnormal; +-------------------+-----------------++--------------+--------------+-----------------+--------------------+ | GBUF_IDX_1 | || base color (RGB) | occlusion/specular | +-------------------+-----------------++--------------+--------------+-----------------+--------------------+ - | GBUF_IDX_2 | _gbuffer2 || velocity (XY) | ignore radiance | unused | + | GBUF_IDX_2 | _gbuffer2 || velocity (XY) | ignore radiance | tangent angle | +-------------------+-----------------++--------------+--------------+-----------------+--------------------+ | GBUF_IDX_EMISSION | _EmissionShaded || emission color (RGB) | unused | +-------------------+-----------------++--------------+--------------+-----------------+--------------------+ - | GBUF_IDX_3 | _Anisotropy || world tangent (XYZ) | unused | - +-------------------+-----------------++--------------+--------------+-----------------+--------------------+ + | GBUF_IDX_REFRACTION | _SSRefraction || packed IOR | transmittance | surfaceDepth | unused | + | | _VoxelRefract || (0-1 range) | | | | The indices as well as the GBUF_SIZE define are defined in "compiled.inc". */ @@ -54,10 +54,10 @@ void main() { #endif #ifdef _SSRefraction - fragColor[GBUF_IDX_REFRACTION] = vec4(ior, opacity, 0.0, 0.0); + fragColor[GBUF_IDX_REFRACTION] = vec4(packIOR(ior), opacity, 0.0, 1.0); #endif #ifdef _Anisotropy - fragColor[GBUF_IDX_3] = vec4(0.0, 0.0, 0.0, 0.0); + fragColor[GBUF_IDX_2].a = -1.0; #endif } diff --git a/leenkx/Shaders/debug_draw/line_deferred.frag.glsl b/leenkx/Shaders/debug_draw/line_deferred.frag.glsl index a96a603f..717c6875 100644 --- a/leenkx/Shaders/debug_draw/line_deferred.frag.glsl +++ b/leenkx/Shaders/debug_draw/line_deferred.frag.glsl @@ -7,7 +7,11 @@ out vec4 fragColor[GBUF_SIZE]; void main() { fragColor[GBUF_IDX_0] = vec4(1.0, 1.0, 0.0, 1.0); + #if GBUF_SIZE > 1 fragColor[GBUF_IDX_1] = vec4(color, 1.0); + #else + fragColor[GBUF_IDX_0] = vec4(color, 1.0); + #endif #ifdef _EmissionShaded fragColor[GBUF_IDX_EMISSION] = vec4(0.0); diff --git a/leenkx/Shaders/deferred_light/deferred_light.frag.glsl b/leenkx/Shaders/deferred_light/deferred_light.frag.glsl index 78b98aed..6420a4e0 100644 --- a/leenkx/Shaders/deferred_light/deferred_light.frag.glsl +++ b/leenkx/Shaders/deferred_light/deferred_light.frag.glsl @@ -8,9 +8,6 @@ #ifdef _Irr #include "std/shirr.glsl" #endif -#ifdef _SSS -#include "std/sss.glsl" -#endif #ifdef _SSRS #include "std/ssrs.glsl" #endif @@ -23,12 +20,12 @@ uniform sampler2D gbuffer1; #ifdef _gbuffer2 uniform sampler2D gbuffer2; #endif -#ifdef _Anisotropy - uniform sampler2D gbuffer3; -#endif #ifdef _EmissionShaded uniform sampler2D gbufferEmission; #endif +#ifdef _ClearCoat + uniform sampler2D gbufferCoatNormal; +#endif #ifdef _VoxelGI uniform sampler2D voxels_diffuse; @@ -95,7 +92,7 @@ uniform mat4 invVP; #ifdef _SinglePoint //!uniform sampler2DShadow shadowMapSpot[1]; //!uniform sampler2D shadowMapSpotTransparent[1]; - //!uniform mat4 LWVPSpot[1]; + //!uniform mat4 LWVPSpotArray[1]; #endif #ifdef _Clusters //!uniform sampler2DShadow shadowMapSpot[4]; @@ -140,7 +137,7 @@ uniform vec2 cameraPlane; #ifdef _ShadowMapTransparent //!uniform sampler2D shadowMapSpotTransparent[1]; #endif - //!uniform mat4 LWVPSpot[1]; + //!uniform mat4 LWVPSpotArray[1]; #else //!uniform samplerCubeShadow shadowMapPoint[1]; #ifdef _ShadowMapTransparent @@ -203,6 +200,7 @@ uniform vec3 sunCol; uniform sampler2D shadowMapAtlasSunTransparent; #endif #endif + //!uniform vec4 tileBoundsSunArray[maxLights * shadowmapCascades]; #else uniform sampler2DShadow shadowMap; #ifdef _ShadowMapTransparent @@ -239,6 +237,9 @@ uniform float time; #endif #include "std/light.glsl" +#ifdef _SSS +#include "std/sss.glsl" +#endif in vec2 texCoord; in vec3 viewRay; @@ -262,8 +263,26 @@ void main() { matid = min(matid, uint(MAX_MATERIALS - 1)); //!uniform vec4 materialParams[MAX_MATERIALS * 8]; - vec4 matp0, matp1, matp2, matp3, matp4, matp5, matp6; - getMaterialParams(matid, matp0, matp1, matp2, matp3, matp4, matp5, matp6); + vec4 matp0 = vec4(0.0), matp1 = vec4(0.0), matp2 = vec4(0.0), matp3 = vec4(0.0); + vec4 matp4 = vec4(0.0), matp5 = vec4(0.0), matp6 = vec4(0.0), matp7 = vec4(0.0); + // TODO: coatIOR=1.5, ior=1.45, thinWall=1.0 move to python make files + matp1.z = 1.5; + matp3.x = 1.45; + matp3.y = 1.0; + if (matid >= 3u) { + getMaterialParams(matid, matp0, matp1, matp2, matp3, matp4, matp5, matp6, matp7); + } + #ifdef _ClearCoat + vec3 coatTintCol = vec3(matp1.w, matp2.x, matp2.y); + #endif + #ifdef _Sheen + vec3 sheenTintCol = vec3(matp5.z, matp5.w, matp6.x); + #endif + #ifdef _SSS + vec3 sssColorVal = vec3(matp4.w, matp5.x, matp5.y); + vec3 sssRadiusBase = vec3(matp4.x, matp4.y, matp4.z); + float sssRadiusScalar = max(max(matp4.x, matp4.y), matp4.z) * matp7.x; + #endif #endif vec2 occspec = unpackFloat2(g1.a); @@ -271,7 +290,10 @@ void main() { vec3 basecolor = min(g1.rgb, vec3(2.0)); vec3 albedo = surfaceAlbedo(basecolor, metallic); vec3 f0 = surfaceF0(basecolor, metallic); - + #ifdef _ExtBRDF + f0 = mix(f0, basecolor, vec3(matp6.y, matp6.z, matp6.w)); + #endif + #ifdef _VRStereo bool isLeftEye = texCoord.x < 0.5; vec3 eyePos = isLeftEye ? eyeLeft : eyeRight; @@ -290,13 +312,24 @@ void main() { #endif float dotNV = max(dot(n, v), 0.0); +#ifdef _ClearCoat + vec4 gCoat = textureLod(gbufferCoatNormal, texCoord, 0.0); + vec3 nCoat; + nCoat.z = 1.0 - abs(gCoat.x) - abs(gCoat.y); + nCoat.xy = nCoat.z >= 0.0 ? gCoat.xy : octahedronWrap(gCoat.xy); + nCoat = normalize(nCoat); +#endif + #ifdef _gbuffer2 vec4 g2 = textureLod(gbuffer2, texCoord, 0.0); #endif #ifdef _Anisotropy - vec4 g3 = textureLod(gbuffer3, texCoord, 0.0); - vec3 wTangent = length(g3.xyz) > 0.0 ? normalize(g3.xyz) : vec3(1.0, 0.0, 0.0); + #ifdef _gbuffer2 + vec3 wTangent = decodeTangent(g2.a, n); + #else + vec3 wTangent = vec3(0.0); + #endif #endif @@ -311,6 +344,44 @@ void main() { vec3 F = f0; #endif +#ifdef _ExtBRDF + float iblSheenWeight = 1.0; + float iblCoatWeight = 1.0; + float iblLayerWeight = 1.0; + vec3 coatTintAbsorb = vec3(1.0); + + #ifdef _Sheen + float sheenAlb = sheenIBLAlbedo(matp0.z, matp0.w, dotNV); + iblSheenWeight = max(1.0 - sheenAlb * + max(max(sheenTintCol.r, sheenTintCol.g), sheenTintCol.b), 0.0); + #endif + + #ifdef _ClearCoat + float dotNVCoat = max(dot(nCoat, v), 0.0); + float coatF = coatIBLFresnel(matp1.x, matp1.z, dotNVCoat); + iblCoatWeight = max(1.0 - coatF, 0.0); + coatTintAbsorb = mix(vec3(1.0), clamp(coatTintCol, 0.0, 1.0), + clamp(1.0 / max(dotNVCoat, 0.3) * 0.2, 0.0, 1.0)); + #endif + + iblLayerWeight = iblSheenWeight * iblCoatWeight; + + brdf_sheenWeight = iblSheenWeight; + brdf_coatWeight = iblCoatWeight; + brdf_coatTintAbsorb = coatTintAbsorb; + #ifdef _Sheen + brdf_sheenAlbedo = sheenAlb; + #endif + #ifdef _ClearCoat + brdf_coatF0 = (matp1.z - 1.0) / (matp1.z + 1.0); + brdf_coatF0 = brdf_coatF0 * brdf_coatF0; + #endif + #ifdef _Transmission + brdf_transmissionF0 = (matp3.x - 1.0) / (matp3.x + 1.0); + brdf_transmissionF0 = brdf_transmissionF0 * brdf_transmissionF0; + #endif +#endif // _ExtBRDF + #ifndef _VoxelAOvar #ifndef _VoxelGI // Envmap @@ -318,9 +389,7 @@ void main() { vec3 envl = shIrradiance(n, shirr); #ifdef _gbuffer2 - if (g2.b < 0.5) { - envl = envl; - } else { + if (g2.b >= 0.5) { envl = vec3(0.0); } #endif @@ -333,16 +402,21 @@ void main() { #endif #ifdef _Rad + #ifdef _Anisotropy + vec3 reflectionWorld = anisotropicIBLDirection(n, v, wTangent, + matp0.x, roughness); + #else vec3 reflectionWorld = reflect(-v, n); + #endif float lod = getMipFromRoughness(roughness, envmapNumMipmaps); vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb; prefilteredColor = min(prefilteredColor, vec3(20.0)); #endif #ifdef _EnvLDR - envl.rgb = pow(envl.rgb, vec3(2.2)); + envl.rgb = srgbToLinear(envl.rgb); #ifdef _Rad - prefilteredColor = pow(prefilteredColor, vec3(2.2)); + prefilteredColor = srgbToLinear(prefilteredColor); #endif #endif @@ -360,6 +434,68 @@ void main() { #endif #endif +#ifdef _ExtBRDF + envl.rgb *= iblLayerWeight; + + + #ifdef _Transmission + float transF = transmissionIBLFresnel(matp3.x, dotNV); + float transmittance = 1.0 - transF; + #ifdef _Rad + if (matp2.z > 0.0 && transmittance > 0.0) { + vec3 refrDir; + if (matp3.y > 0.5) { + refrDir = reflect(-v, n); + } else { + refrDir = transmissionIBLDirection(n, v, matp3.x); + } + float transLod = getMipFromRoughness(matp2.w, envmapNumMipmaps); + vec3 transColor = textureLod(senvmapRadiance, + envMapEquirect(refrDir), transLod).rgb; + transColor = min(transColor, vec3(20.0)); + #ifdef _EnvLDR + transColor = srgbToLinear(transColor); + #endif + envl.rgb += albedo * matp2.z * transmittance * transColor * dotNV + * iblLayerWeight; + } + #endif + #endif + + #ifdef _ClearCoat + envl.rgb *= coatTintAbsorb; + #ifdef _Rad + if (coatF > 0.0) { + float coatLod = getMipFromRoughness(matp1.y, envmapNumMipmaps); + vec3 coatRefl = reflect(-v, nCoat); + vec3 coatColor = textureLod(senvmapRadiance, + envMapEquirect(coatRefl), coatLod).rgb; + coatColor = min(coatColor, vec3(20.0)); + #ifdef _EnvLDR + coatColor = srgbToLinear(coatColor); + #endif + envl.rgb += coatColor * coatF * iblSheenWeight; + } + #endif + #endif + + #ifdef _Sheen + #ifdef _Rad + if (sheenAlb > 0.0) { + float sheenLod = getMipFromRoughness(matp0.w, envmapNumMipmaps); + vec3 sheenRefl = reflect(-v, n); + vec3 sheenColor = textureLod(senvmapRadiance, + envMapEquirect(sheenRefl), sheenLod).rgb; + sheenColor = min(sheenColor, vec3(20.0)); + #ifdef _EnvLDR + sheenColor = srgbToLinear(sheenColor); + #endif + envl.rgb += sheenColor * sheenTintCol * sheenAlb; + } + #endif + #endif +#endif // _ExtBRDF + envl.rgb *= envmapStrength * occspec.x; fragColor.rgb = envl; @@ -368,11 +504,30 @@ void main() { #ifdef _VoxelGI fragColor.rgb = textureLod(voxels_diffuse, texCoord, 0.0).rgb * voxelgiDiff; - if(roughness < 1.0 && occspec.y > 0.0) - fragColor.rgb += textureLod(voxels_specular, texCoord, 0.0).rgb * occspec.y * voxelgiRefl; + if(roughness < 1.0) { + fragColor.rgb += textureLod(voxels_specular, texCoord, 0.0).rgb * F * voxelgiRefl * occspec.y; + } + #ifdef _Rad + vec3 iblReflection = reflect(-v, n); + float iblLod = getMipFromRoughness(roughness, envmapNumMipmaps); + vec3 iblPrefiltered = textureLod(senvmapRadiance, envMapEquirect(iblReflection), iblLod).rgb; + iblPrefiltered = min(iblPrefiltered, vec3(20.0)); + #ifdef _EnvLDR + iblPrefiltered = srgbToLinear(iblPrefiltered); + #endif + #ifdef _ExtBRDF + iblPrefiltered *= iblLayerWeight; + iblPrefiltered *= coatTintAbsorb; + #endif + fragColor.rgb += iblPrefiltered * F * envmapStrength * occspec.x; + #else + #ifdef _EnvCol + fragColor.rgb += backgroundCol * F * envmapStrength * occspec.x; + #endif + #endif #else #ifdef _VoxelAOvar - fragColor.rgb = textureLod(voxels_ao, texCoord, 0.0).rgb * voxelgiOcc; + fragColor.rgb = textureLod(voxels_ao, texCoord, 0.0).rgb; #endif #endif @@ -424,7 +579,7 @@ void main() { vec3 svisibility = vec3(1.0); #ifdef _Anisotropy vec3 sdirect; - if (abs(matp0.x) > 0.001) { + if (abs(matp0.x) > 0.001 && dot(wTangent, wTangent) > 0.001) { vec3 sbitangent = normalize(cross(n, wTangent)); sdirect = lambertDiffuseBRDF(albedo, sdotNL) + anisotropicBRDF(f0, roughness, matp0.x, matp0.y, @@ -438,36 +593,26 @@ void main() { specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * occspec.y; #endif - float sunSheenWeight = 1.0; - float sunCoatWeight = 1.0; - - #ifdef _Sheen - vec3 sunSheen = sheenBRDF(matp0.z, matp0.w, vec3(matp5.z, matp5.w, matp6.x), sdotNL, sdotNH, dotNV); - sunSheenWeight = sheenAttenuation(matp0.z, matp0.w, vec3(matp5.z, matp5.w, matp6.x), dotNV); - #endif - - #ifdef _ClearCoat - vec3 sunCoat = clearcoatBRDF(matp1.x, matp1.y, matp1.z, sdotNL, sdotNH, dotNV, sdotVH); - sunCoatWeight = coatAttenuation(matp1.x, matp1.z, dotNV); - #endif - - float sunLayerWeight = sunSheenWeight * sunCoatWeight; - sdirect *= sunLayerWeight; - #ifdef _Subsurface - sdirect += subsurfaceBRDF(albedo, vec3(matp4.w, matp5.x, matp5.y), vec3(matp4.x, matp4.y, matp4.z), matp3.z, matp3.w, sdotNL) * sunLayerWeight; - #endif - #ifdef _Transmission - sdirect += transmissionBRDF(albedo, matp2.z, matp2.w, matp3.x, matp3.y, sdotNL, dotNV, sdotVH) * sunLayerWeight; - #endif - #ifdef _ClearCoat - sdirect *= coatTintAttenuation(matp1.x, vec3(matp1.w, matp2.x, matp2.y), dotNV); - sdirect += sunCoat * sunSheenWeight; - #endif - #ifdef _Sheen - sdirect += sunSheen; + #ifdef _ExtBRDF + float sunLayerWeight; + sdirect = applyExtBRDFLayers(sdirect, albedo, f0, roughness, + sdotNL, dotNV, sdotNH, sdotVH, n, sunDir, v, sh + #ifdef _ClearCoat + , matp1.x, matp1.y, matp1.z, coatTintCol, nCoat + #endif + #ifdef _Sheen + , matp0.z, matp0.w, sheenTintCol + #endif + #ifdef _Transmission + , matp2.z, matp2.w, matp3.x, matp3.y + #endif + , sunLayerWeight); #endif #ifdef _ShadowMap + #ifdef _ShadowMapAtlas + tileBounds = tileBoundsSunArray[0]; + #endif #ifdef _CSM svisibility = shadowTestCascade( #ifdef _ShadowMapAtlas @@ -552,23 +697,17 @@ void main() { fragColor.rgb += sdirect * sunCol * svisibility; -// #ifdef _Hair // Aniso -// if (matid == 2) { -// const float shinyParallel = roughness; -// const float shinyPerpendicular = 0.1; -// const vec3 v = vec3(0.99146, 0.11664, 0.05832); -// vec3 T = abs(dot(n, v)) > 0.99999 ? cross(n, vec3(0.0, 1.0, 0.0)) : cross(n, v); -// fragColor.rgb = orenNayarDiffuseBRDF(albedo, roughness, dotNV, dotNL, dotVH) + wardSpecular(n, h, dotNL, dotNV, dotNH, T, shinyParallel, shinyPerpendicular) * spec; -// } -// #endif - #ifdef _SSS - if (matid == 2) { + #ifdef _ExtBRDF + if (matid >= 3u && matp3.z > 0.0) { #ifdef _CSM int casi, casindex; mat4 LWVP = getCascadeMat(distance(eye, p), casi, casindex); #endif - fragColor.rgb += fragColor.rgb * SSSSTransmittance( + vec3 sssColor = sssColorVal; + float sssRadius = sssRadiusScalar; + float sssStrength = matp3.z; + vec3 sssResult = SSSSTransmittance( LWVP, p, n, sunDir, lightPlane.y, #ifdef _ShadowMapAtlas #ifndef _SingleAtlas @@ -579,12 +718,26 @@ void main() { #else shadowMap #endif - );//TODO implement transparent shadowmaps into the SSSSTransmittance() + , sssColor, sssRadius + #ifdef _ShadowMapAtlas + #ifdef _CSM + , tileBoundsSunArray[casi] + #else + , tileBoundsSunArray[0] + #endif + #endif + ); + fragColor.rgb += sunCol * sssStrength * sssResult; } #endif + #endif #endif // _Sun +#ifdef _ShadowMapAtlas +tileBounds = vec4(0.0, 0.0, 1.0, 1.0); +#endif + #ifdef _SinglePoint #ifdef _VRStereo @@ -614,16 +767,16 @@ void main() { , gbufferD, invVP, eye #endif #ifdef _ClearCoat - , matp1.x, matp1.y, matp1.z, vec3(matp1.w, matp2.x, matp2.y) + , matp1.x, matp1.y, matp1.z, coatTintCol, nCoat #endif #ifdef _Sheen - , matp0.z, matp0.w, vec3(matp5.z, matp5.w, matp6.x) + , matp0.z, matp0.w, sheenTintCol #endif #ifdef _Anisotropy , matp0.x, matp0.y, wTangent #endif - #ifdef _Subsurface - , matp3.z, vec3(matp4.w, matp5.x, matp5.y), vec3(matp4.x, matp4.y, matp4.z), matp3.w + #ifdef _SSS + , matp3.z, sssColorVal, sssRadiusBase * matp7.x, matp3.w #endif #ifdef _Transmission , matp2.z, matp2.w, matp3.x, matp3.y @@ -633,7 +786,33 @@ void main() { #ifdef _Spot #ifdef _SSS #ifdef _ShadowMap - if (matid == 2) fragColor.rgb += fragColor.rgb * SSSSTransmittance(LWVPSpot[0], p, n, normalize(lightPos - p), lightPlane.y, shadowMapSpot[0]);//TODO implement transparent shadowmaps into the SSSSTransmittance() + #ifdef _ExtBRDF + if (matid >= 3u && matp3.z > 0.0) { + vec3 sssColorSpot = sssColorVal; + float sssRadiusSpot = sssRadiusScalar; + float sssStrengthSpot = matp3.z; + fragColor.rgb += pointCol * sssStrengthSpot * SSSSTransmittance(LWVPSpotArray[0], p, n, normalize(lightPos - p), lightPlane.y, shadowMapSpot[0], sssColorSpot, sssRadiusSpot + #ifdef _ShadowMapAtlas + , vec4(0.0, 0.0, 1.0, 1.0) + #endif + );//TODO implement transparent shadowmaps into the SSSSTransmittance() + } + #endif + #endif + #endif + #endif + + #ifndef _Spot + #ifdef _SSS + #ifdef _ShadowMap + #ifdef _ExtBRDF + if (matid >= 3u && matp3.z > 0.0) { + vec3 sssColorPoint = sssColorVal; + float sssRadiusPoint = sssRadiusScalar; + float sssStrengthPoint = matp3.z; + fragColor.rgb += pointCol * sssStrengthPoint * SSSSTransmittanceCube(shadowMapPoint[0], lightPos, p, n, normalize(lightPos - p), lightPlane.y, lightProj, sssColorPoint, sssRadiusPoint); + } + #endif #endif #endif #endif @@ -692,21 +871,95 @@ void main() { , gbufferD, invVP, eye #endif #ifdef _ClearCoat - , matp1.x, matp1.y, matp1.z, vec3(matp1.w, matp2.x, matp2.y) + , matp1.x, matp1.y, matp1.z, coatTintCol, nCoat #endif #ifdef _Sheen - , matp0.z, matp0.w, vec3(matp5.z, matp5.w, matp6.x) + , matp0.z, matp0.w, sheenTintCol #endif #ifdef _Anisotropy , matp0.x, matp0.y, wTangent #endif - #ifdef _Subsurface - , matp3.z, vec3(matp4.w, matp5.x, matp5.y), vec3(matp4.x, matp4.y, matp4.z), matp3.w + #ifdef _SSS + , matp3.z, sssColorVal, sssRadiusBase * matp7.x, matp3.w #endif #ifdef _Transmission , matp2.z, matp2.w, matp3.x, matp3.y #endif ); + + #ifdef _SSS + #ifdef _ShadowMap + #ifdef _ExtBRDF + if (matid >= 3u && matp3.z > 0.0) { + vec3 sssColorCL = sssColorVal; + float sssRadiusCL = sssRadiusScalar; + float sssStrengthCL = matp3.z; + vec3 cLightPos = lightsArray[li * 3].xyz; + vec3 cLightCol = lightsArray[li * 3 + 1].xyz; + vec3 cLightDir = normalize(cLightPos - p); + #ifdef _Spot + bool isSpotLight = lightsArray[li * 3 + 2].y != 0.0; + if (isSpotLight) { + #ifdef _ShadowMapAtlas + #ifndef _SingleAtlas + fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittance(LWVPSpotArray[li], p, n, cLightDir, lightPlane.y, shadowMapAtlasSpot, sssColorCL, sssRadiusCL, tileBoundsSpotArray[li]); + #else + fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittance(LWVPSpotArray[li], p, n, cLightDir, lightPlane.y, shadowMapAtlas, sssColorCL, sssRadiusCL, tileBoundsSpotArray[li]); + #endif + #else + if (li == 0) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittance(LWVPSpotArray[0], p, n, cLightDir, lightPlane.y, shadowMapSpot[0], sssColorCL, sssRadiusCL + #ifdef _ShadowMapAtlas + , vec4(0.0, 0.0, 1.0, 1.0) + #endif + ); + else if (li == 1) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittance(LWVPSpotArray[1], p, n, cLightDir, lightPlane.y, shadowMapSpot[1], sssColorCL, sssRadiusCL + #ifdef _ShadowMapAtlas + , vec4(0.0, 0.0, 1.0, 1.0) + #endif + ); + else if (li == 2) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittance(LWVPSpotArray[2], p, n, cLightDir, lightPlane.y, shadowMapSpot[2], sssColorCL, sssRadiusCL + #ifdef _ShadowMapAtlas + , vec4(0.0, 0.0, 1.0, 1.0) + #endif + ); + else if (li == 3) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittance(LWVPSpotArray[3], p, n, cLightDir, lightPlane.y, shadowMapSpot[3], sssColorCL, sssRadiusCL + #ifdef _ShadowMapAtlas + , vec4(0.0, 0.0, 1.0, 1.0) + #endif + ); + #endif + } else { + #ifdef _ShadowMapAtlas + #ifndef _SingleAtlas + fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCubeAtlas(shadowMapAtlasPoint, cLightPos, p, n, cLightDir, lightPlane.y, lightProj, li, sssColorCL, sssRadiusCL); + #else + fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCubeAtlas(shadowMapAtlas, cLightPos, p, n, cLightDir, lightPlane.y, lightProj, li, sssColorCL, sssRadiusCL); + #endif + #else + if (li == 0) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCube(shadowMapPoint[0], cLightPos, p, n, cLightDir, lightPlane.y, lightProj, sssColorCL, sssRadiusCL); + else if (li == 1) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCube(shadowMapPoint[1], cLightPos, p, n, cLightDir, lightPlane.y, lightProj, sssColorCL, sssRadiusCL); + else if (li == 2) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCube(shadowMapPoint[2], cLightPos, p, n, cLightDir, lightPlane.y, lightProj, sssColorCL, sssRadiusCL); + else if (li == 3) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCube(shadowMapPoint[3], cLightPos, p, n, cLightDir, lightPlane.y, lightProj, sssColorCL, sssRadiusCL); + #endif + } + #else + #ifdef _ShadowMapAtlas + #ifndef _SingleAtlas + fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCubeAtlas(shadowMapAtlasPoint, cLightPos, p, n, cLightDir, lightPlane.y, lightProj, li, sssColorCL, sssRadiusCL); + #else + fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCubeAtlas(shadowMapAtlas, cLightPos, p, n, cLightDir, lightPlane.y, lightProj, li, sssColorCL, sssRadiusCL); + #endif + #else + if (li == 0) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCube(shadowMapPoint[0], cLightPos, p, n, cLightDir, lightPlane.y, lightProj, sssColorCL, sssRadiusCL); + else if (li == 1) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCube(shadowMapPoint[1], cLightPos, p, n, cLightDir, lightPlane.y, lightProj, sssColorCL, sssRadiusCL); + else if (li == 2) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCube(shadowMapPoint[2], cLightPos, p, n, cLightDir, lightPlane.y, lightProj, sssColorCL, sssRadiusCL); + else if (li == 3) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCube(shadowMapPoint[3], cLightPos, p, n, cLightDir, lightPlane.y, lightProj, sssColorCL, sssRadiusCL); + #endif + #endif + } + #endif + #endif + #endif } #endif // _Clusters diff --git a/leenkx/Shaders/deferred_light/deferred_light.json b/leenkx/Shaders/deferred_light/deferred_light.json index db3c2d94..f2df396b 100644 --- a/leenkx/Shaders/deferred_light/deferred_light.json +++ b/leenkx/Shaders/deferred_light/deferred_light.json @@ -138,6 +138,16 @@ "link": "_cascadeData", "ifdef": ["_Sun", "_ShadowMap", "_CSM"] }, + { + "name": "tileBoundsSunArray", + "link": "_tileBoundsSunArray", + "ifdef": ["_Sun", "_ShadowMap", "_ShadowMapAtlas"] + }, + { + "name": "tileBoundsSpotArray", + "link": "_tileBoundsSpotArray", + "ifdef": ["_Clusters", "_Spot", "_ShadowMap", "_ShadowMapAtlas"] + }, { "name": "lightPlane", "link": "_lightPlane", @@ -281,9 +291,11 @@ { "name": "materialParams", "link": "_materialParams", - "type": "floats" + "type": "floats", + "ifdef": ["_ExtBRDF"] } ], + "texture_units": [], "vertex_shader": "../include/pass_viewray.vert.glsl", "fragment_shader": "deferred_light.frag.glsl", "color_attachments": ["RGBA64"] diff --git a/leenkx/Shaders/deferred_light_mobile/deferred_light.frag.glsl b/leenkx/Shaders/deferred_light_mobile/deferred_light.frag.glsl index 659de0da..27e6958b 100644 --- a/leenkx/Shaders/deferred_light_mobile/deferred_light.frag.glsl +++ b/leenkx/Shaders/deferred_light_mobile/deferred_light.frag.glsl @@ -14,8 +14,11 @@ uniform sampler2D gbufferD; uniform sampler2D gbuffer0; uniform sampler2D gbuffer1; -#ifdef _Anisotropy -uniform sampler2D gbuffer3; +#ifdef _gbuffer2 +uniform sampler2D gbuffer2; +#endif +#ifdef _ClearCoat +uniform sampler2D gbufferCoatNormal; #endif uniform float envmapStrength; @@ -53,7 +56,7 @@ uniform vec2 cameraPlane; #ifdef _SinglePoint #ifdef _Spot //!uniform sampler2DShadow shadowMapSpot[1]; - //!uniform mat4 LWVPSpot[1]; + //!uniform mat4 LWVPSpotArray[1]; #else //!uniform samplerCubeShadow shadowMapPoint[1]; //!uniform vec2 lightProj; @@ -95,6 +98,7 @@ uniform vec3 sunCol; #ifndef _SingleAtlas uniform sampler2DShadow shadowMapAtlasSun; #endif + //!uniform vec4 tileBoundsSunArray[maxLights * shadowmapCascades]; #else uniform sampler2DShadow shadowMap; #endif @@ -140,23 +144,55 @@ void main() { matid = min(matid, uint(MAX_MATERIALS - 1)); //!uniform vec4 materialParams[MAX_MATERIALS * 8]; - vec4 matp0, matp1, matp2, matp3, matp4, matp5, matp6; - getMaterialParams(matid, matp0, matp1, matp2, matp3, matp4, matp5, matp6); + vec4 matp0 = vec4(0.0), matp1 = vec4(0.0), matp2 = vec4(0.0), matp3 = vec4(0.0); + vec4 matp4 = vec4(0.0), matp5 = vec4(0.0), matp6 = vec4(0.0), matp7 = vec4(0.0); + // TODO: coatIOR=1.5, ior=1.45, thinWall=1.0 move to python make files + matp1.z = 1.5; + matp3.x = 1.45; + matp3.y = 1.0; + if (matid >= 3u) { + getMaterialParams(matid, matp0, matp1, matp2, matp3, matp4, matp5, matp6, matp7); + } + #ifdef _ClearCoat + vec3 coatTintCol = vec3(matp1.w, matp2.x, matp2.y); + #endif + #ifdef _Sheen + vec3 sheenTintCol = vec3(matp5.z, matp5.w, matp6.x); + #endif + #ifdef _SSS + vec3 sssColorVal = vec3(matp4.w, matp5.x, matp5.y); + vec3 sssRadiusScaled = vec3(matp4.x, matp4.y, matp4.z) * matp7.x; + #endif #endif vec4 g1 = textureLod(gbuffer1, texCoord, 0.0); // Basecolor.rgb, spec/occ vec2 occspec = unpackFloat2(g1.a); vec3 albedo = surfaceAlbedo(g1.rgb, metallic); // g1.rgb - basecolor vec3 f0 = surfaceF0(g1.rgb, metallic); + #ifdef _ExtBRDF + f0 = mix(f0, min(g1.rgb, vec3(2.0)), vec3(matp6.y, matp6.z, matp6.w)); + #endif float depth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0; vec3 p = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj); vec3 v = normalize(eye - p); float dotNV = max(dot(n, v), 0.0); +#ifdef _ClearCoat + vec4 gCoat = textureLod(gbufferCoatNormal, texCoord, 0.0); + vec3 nCoat; + nCoat.z = 1.0 - abs(gCoat.x) - abs(gCoat.y); + nCoat.xy = nCoat.z >= 0.0 ? gCoat.xy : octahedronWrap(gCoat.xy); + nCoat = normalize(nCoat); +#endif + #ifdef _Anisotropy - vec4 g3 = textureLod(gbuffer3, texCoord, 0.0); - vec3 wTangent = length(g3.xyz) > 0.0 ? normalize(g3.xyz) : vec3(1.0, 0.0, 0.0); + #ifdef _gbuffer2 + vec4 g2 = textureLod(gbuffer2, texCoord, 0.0); + vec3 wTangent = decodeTangent(g2.a, n); + #else + vec3 wTangent = vec3(0.0); + #endif #endif #ifdef _Brdf @@ -174,15 +210,20 @@ void main() { #endif #ifdef _Rad + #ifdef _Anisotropy + vec3 reflectionWorld = anisotropicIBLDirection(n, v, wTangent, + matp0.x, roughness); + #else vec3 reflectionWorld = reflect(-v, n); + #endif float lod = getMipFromRoughness(roughness, envmapNumMipmaps); vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb; #endif #ifdef _EnvLDR - envl.rgb = pow(envl.rgb, vec3(2.2)); + envl.rgb = srgbToLinear(envl.rgb); #ifdef _Rad - prefilteredColor = pow(prefilteredColor, vec3(2.2)); + prefilteredColor = srgbToLinear(prefilteredColor); #endif #endif @@ -196,6 +237,98 @@ void main() { #endif #endif +#ifdef _ExtBRDF + float iblSheenWeight = 1.0; + float iblCoatWeight = 1.0; + vec3 coatTintAbsorb = vec3(1.0); + + #ifdef _Sheen + float sheenAlb = sheenIBLAlbedo(matp0.z, matp0.w, dotNV); + iblSheenWeight = max(1.0 - sheenAlb * + max(max(sheenTintCol.r, sheenTintCol.g), sheenTintCol.b), 0.0); + #endif + + #ifdef _ClearCoat + float dotNVCoat = max(dot(nCoat, v), 0.0); + float coatF = coatIBLFresnel(matp1.x, matp1.z, dotNVCoat); + iblCoatWeight = max(1.0 - coatF, 0.0); + if (matp1.x > 0.0) { + coatTintAbsorb = mix(vec3(1.0), clamp(coatTintCol, 0.0, 1.0), + clamp(1.0 / max(dotNVCoat, 0.3) * 0.2, 0.0, 1.0)); + } + #endif + + float iblLayerWeight = iblSheenWeight * iblCoatWeight; + envl.rgb *= iblLayerWeight; + + brdf_sheenWeight = iblSheenWeight; + brdf_coatWeight = iblCoatWeight; + brdf_coatTintAbsorb = coatTintAbsorb; + #ifdef _Sheen + brdf_sheenAlbedo = sheenAlb; + #endif + #ifdef _ClearCoat + brdf_coatF0 = (matp1.z - 1.0) / (matp1.z + 1.0); + brdf_coatF0 = brdf_coatF0 * brdf_coatF0; + #endif + #ifdef _Transmission + brdf_transmissionF0 = (matp3.x - 1.0) / (matp3.x + 1.0); + brdf_transmissionF0 = brdf_transmissionF0 * brdf_transmissionF0; + #endif + + #ifdef _Transmission + float transF = transmissionIBLFresnel(matp3.x, dotNV); + float transmittance = 1.0 - transF; + #ifdef _Rad + if (matp2.z > 0.0 && transmittance > 0.0) { + vec3 refrDir = transmissionIBLDirection(n, v, matp3.x); + float transLod = getMipFromRoughness(matp2.w, envmapNumMipmaps); + vec3 transColor = textureLod(senvmapRadiance, + envMapEquirect(refrDir), transLod).rgb; + transColor = min(transColor, vec3(20.0)); + #ifdef _EnvLDR + transColor = srgbToLinear(transColor); + #endif + envl.rgb += albedo * matp2.z * transmittance * transColor * dotNV + * iblLayerWeight; + } + #endif + #endif + + #ifdef _ClearCoat + envl.rgb *= coatTintAbsorb; + #ifdef _Rad + if (coatF > 0.0) { + float coatLod = getMipFromRoughness(matp1.y, envmapNumMipmaps); + vec3 coatRefl = reflect(-v, nCoat); + vec3 coatColor = textureLod(senvmapRadiance, + envMapEquirect(coatRefl), coatLod).rgb; + coatColor = min(coatColor, vec3(20.0)); + #ifdef _EnvLDR + coatColor = srgbToLinear(coatColor); + #endif + envl.rgb += coatColor * coatF * iblSheenWeight; + } + #endif + #endif + + #ifdef _Sheen + #ifdef _Rad + if (sheenAlb > 0.0) { + float sheenLod = getMipFromRoughness(matp0.w, envmapNumMipmaps); + vec3 sheenRefl = reflect(-v, n); + vec3 sheenColor = textureLod(senvmapRadiance, + envMapEquirect(sheenRefl), sheenLod).rgb; + sheenColor = min(sheenColor, vec3(20.0)); + #ifdef _EnvLDR + sheenColor = srgbToLinear(sheenColor); + #endif + envl.rgb += sheenColor * sheenTintCol * sheenAlb; + } + #endif + #endif +#endif // _ExtBRDF + envl.rgb *= envmapStrength * occspec.x; fragColor.rgb = envl; @@ -207,7 +340,7 @@ void main() { float svisibility = 1.0; #ifdef _Anisotropy vec3 sdirect; - if (abs(matp0.x) > 0.001) { + if (abs(matp0.x) > 0.001 && dot(wTangent, wTangent) > 0.001) { vec3 sbitangent = normalize(cross(n, wTangent)); sdirect = lambertDiffuseBRDF(albedo, sdotNL) + anisotropicBRDF(f0, roughness, matp0.x, matp0.y, @@ -221,29 +354,24 @@ void main() { specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * occspec.y; #endif - float sunSheenWeight = 1.0; - float sunCoatWeight = 1.0; + float sunSheenWeight = brdf_sheenWeight; + float sunCoatWeight = brdf_coatWeight; #ifdef _Sheen - vec3 sunSheen = sheenBRDF(matp0.z, matp0.w, vec3(matp5.z, matp5.w, matp6.x), sdotNL, sdotNH, dotNV); - sunSheenWeight = sheenAttenuation(matp0.z, matp0.w, vec3(matp5.z, matp5.w, matp6.x), dotNV); + vec3 sunSheen = sheenBRDF(matp0.z, matp0.w, sheenTintCol, sdotNL, sdotNH, dotNV); #endif #ifdef _ClearCoat - vec3 sunCoat = clearcoatBRDF(matp1.x, matp1.y, matp1.z, sdotNL, sdotNH, dotNV, sdotVH); - sunCoatWeight = coatAttenuation(matp1.x, matp1.z, dotNV); + vec3 sunCoat = clearcoatBRDF(matp1.x, matp1.y, matp1.z, nCoat, sunDir, v, sh); #endif float sunLayerWeight = sunSheenWeight * sunCoatWeight; sdirect *= sunLayerWeight; - #ifdef _Subsurface - sdirect += subsurfaceBRDF(albedo, vec3(matp4.w, matp5.x, matp5.y), vec3(matp4.x, matp4.y, matp4.z), matp3.z, matp3.w, sdotNL) * sunLayerWeight; - #endif #ifdef _Transmission sdirect += transmissionBRDF(albedo, matp2.z, matp2.w, matp3.x, matp3.y, sdotNL, dotNV, sdotVH) * sunLayerWeight; #endif #ifdef _ClearCoat - sdirect *= coatTintAttenuation(matp1.x, vec3(matp1.w, matp2.x, matp2.y), dotNV); + sdirect *= brdf_coatTintAbsorb; sdirect += sunCoat * sunSheenWeight; #endif #ifdef _Sheen @@ -251,6 +379,9 @@ void main() { #endif #ifdef _ShadowMap + #ifdef _ShadowMapAtlas + tileBounds = tileBoundsSunArray[0]; + #endif #ifdef _CSM svisibility = shadowTestCascade( #ifdef _ShadowMapAtlas @@ -294,16 +425,16 @@ void main() { , true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight // TODO: Test! #endif #ifdef _ClearCoat - , matp1.x, matp1.y, matp1.z, vec3(matp1.w, matp2.x, matp2.y) + , matp1.x, matp1.y, matp1.z, coatTintCol, nCoat #endif #ifdef _Sheen - , matp0.z, matp0.w, vec3(matp5.z, matp5.w, matp6.x) + , matp0.z, matp0.w, sheenTintCol #endif #ifdef _Anisotropy , matp0.x, matp0.y, wTangent #endif - #ifdef _Subsurface - , matp3.z, vec3(matp4.w, matp5.x, matp5.y), vec3(matp4.x, matp4.y, matp4.z), matp3.w + #ifdef _SSS + , matp3.z, sssColorVal, sssRadiusScaled, matp3.w #endif #ifdef _Transmission , matp2.z, matp2.w, matp3.x, matp3.y @@ -351,16 +482,16 @@ void main() { , lightsArraySpot[li * 2 + 1].xyz // right #endif #ifdef _ClearCoat - , matp1.x, matp1.y, matp1.z, vec3(matp1.w, matp2.x, matp2.y) + , matp1.x, matp1.y, matp1.z, coatTintCol, nCoat #endif #ifdef _Sheen - , matp0.z, matp0.w, vec3(matp5.z, matp5.w, matp6.x) + , matp0.z, matp0.w, sheenTintCol #endif #ifdef _Anisotropy , matp0.x, matp0.y, wTangent #endif - #ifdef _Subsurface - , matp3.z, vec3(matp4.w, matp5.x, matp5.y), vec3(matp4.x, matp4.y, matp4.z), matp3.w + #ifdef _SSS + , matp3.z, sssColorVal, sssRadiusScaled, matp3.w #endif #ifdef _Transmission , matp2.z, matp2.w, matp3.x, matp3.y @@ -368,4 +499,11 @@ void main() { ); } #endif // _Clusters + + fragColor.rgb = clamp(fragColor.rgb, vec3(0.0), vec3(65504.0)); + if (any(isnan(fragColor.rgb)) || any(isinf(fragColor.rgb))) { + fragColor.rgb = vec3(0.0); + } + + fragColor.a = 1.0; // Mark as opaque } diff --git a/leenkx/Shaders/deferred_light_mobile/deferred_light_mobile.json b/leenkx/Shaders/deferred_light_mobile/deferred_light_mobile.json index c3989154..efa0f9ce 100644 --- a/leenkx/Shaders/deferred_light_mobile/deferred_light_mobile.json +++ b/leenkx/Shaders/deferred_light_mobile/deferred_light_mobile.json @@ -97,6 +97,16 @@ "link": "_cascadeData", "ifdef": ["_Sun", "_ShadowMap", "_CSM"] }, + { + "name": "tileBoundsSunArray", + "link": "_tileBoundsSunArray", + "ifdef": ["_Sun", "_ShadowMap", "_ShadowMapAtlas"] + }, + { + "name": "tileBoundsSpotArray", + "link": "_tileBoundsSpotArray", + "ifdef": ["_Clusters", "_Spot", "_ShadowMap", "_ShadowMapAtlas"] + }, { "name": "eyeLookRight", "link": "_eyeLookRight", @@ -218,7 +228,8 @@ { "name": "materialParams", "link": "_materialParams", - "type": "floats" + "type": "floats", + "ifdef": ["_ExtBRDF"] } ], "vertex_shader": "../include/pass_viewray.vert.glsl", diff --git a/leenkx/Shaders/ssr_pass/ssr_pass.frag.glsl b/leenkx/Shaders/ssr_pass/ssr_pass.frag.glsl index 84aa2fab..7c9ffa89 100644 --- a/leenkx/Shaders/ssr_pass/ssr_pass.frag.glsl +++ b/leenkx/Shaders/ssr_pass/ssr_pass.frag.glsl @@ -11,6 +11,7 @@ uniform sampler2D gbuffer1; // basecol, spec uniform mat4 P; uniform mat3 V3; uniform vec2 cameraProj; +uniform vec2 screenSize; #ifdef _CPostprocess uniform vec3 PPComp9; @@ -24,8 +25,8 @@ out vec4 fragColor; vec3 hitCoord; float depth; -const int numBinarySearchSteps = 7; -const int maxSteps = int(ceil(1.0 / ssrRayStep) * ssrSearchDist); +const int numBinarySearchSteps = 8; +const int maxSteps = 50; vec2 getProjectedCoord(const vec3 hit) { vec4 projectedCoord = P * vec4(hit, 1.0); @@ -38,44 +39,58 @@ vec2 getProjectedCoord(const vec3 hit) { } float getDeltaDepth(const vec3 hit) { - depth = textureLod(gbufferD, getProjectedCoord(hit), 0.0).r * 2.0 - 1.0; + vec2 tc = getProjectedCoord(hit); + if (tc.x < 0.0 || tc.x > 1.0 || tc.y < 0.0 || tc.y > 1.0) + return -1.0; + depth = textureLod(gbufferD, tc, 0.0).r * 2.0 - 1.0; vec3 viewPos = getPosView(viewRay, depth, cameraProj); return viewPos.z - hit.z; } -vec4 binarySearch(vec3 dir) { +vec4 binarySearch(vec3 dir, float stepSize) { float ddepth; for (int i = 0; i < numBinarySearchSteps; i++) { - dir *= 0.5; - hitCoord -= dir; + stepSize *= 0.5; + hitCoord -= dir * stepSize; ddepth = getDeltaDepth(hitCoord); - if (ddepth < 0.0) hitCoord += dir; + if (ddepth < 0.0) hitCoord += dir * stepSize; } - // Ugly discard of hits too far away #ifdef _CPostprocess - if (abs(ddepth) > PPComp9.z / 500) return vec4(0.0); + float maxDist = PPComp9.z; #else - if (abs(ddepth) > ssrSearchDist / 500) return vec4(0.0); + float maxDist = ssrSearchDist; #endif - return vec4(getProjectedCoord(hitCoord), 0.0, 1.0); + if (abs(ddepth) > maxDist * 0.005) return vec4(0.0); + vec2 hitTC = getProjectedCoord(hitCoord); + if (hitTC.x < 0.0 || hitTC.x > 1.0 || hitTC.y < 0.0 || hitTC.y > 1.0) + return vec4(0.0); + return vec4(hitTC, 0.0, 1.0); } vec4 rayCast(vec3 dir) { #ifdef _CPostprocess - dir *= PPComp9.x; + float baseStep = PPComp9.x; + float maxDist = PPComp9.z; #else - dir *= ssrRayStep; + float baseStep = ssrRayStep; + float maxDist = ssrSearchDist; #endif + float stepSize = baseStep * max(1.0, -viewRay.z * 0.1); + vec3 startPos = hitCoord; for (int i = 0; i < maxSteps; i++) { - hitCoord += dir; - if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); + hitCoord += dir * stepSize; + float dist = length(hitCoord - startPos); + if (dist > maxDist) break; + float ddepth = getDeltaDepth(hitCoord); + if (ddepth > 0.0) return binarySearch(dir, stepSize); + stepSize *= 1.03; } return vec4(0.0); } void main() { vec4 g0 = textureLod(gbuffer0, texCoord, 0.0); - float roughness = unpackFloat(g0.b).y; + float roughness = g0.b; if (roughness == 1.0) { fragColor.rgb = vec3(0.0); return; } float spec = fract(textureLod(gbuffer1, texCoord, 0.0).a); @@ -92,30 +107,54 @@ void main() { vec3 viewNormal = V3 * n; vec3 viewPos = getPosView(viewRay, d, cameraProj); - vec3 reflected = reflect(viewPos, viewNormal); + float NdotV = clamp(dot(viewNormal, -normalize(viewPos)), 0.0, 1.0); + vec3 reflected = reflect(normalize(viewPos), viewNormal); hitCoord = viewPos; - #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 + vec3 dir = reflected; - // * max(ssrMinRayStep, -viewPos.z) 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); + if (coords.w <= 0.0) { + fragColor.rgb = vec3(0.0); + return; + } + + vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy); + float screenEdgeFactor = smoothstep(0.5, 0.15, deltaCoords.x) + * smoothstep(0.5, 0.15, deltaCoords.y); + screenEdgeFactor = max(screenEdgeFactor, 0.15); + + float hitDepth = textureLod(gbufferD, coords.xy, 0.0).r * 2.0 - 1.0; + vec3 hitViewPos = getPosView(viewRay, hitDepth, cameraProj); + vec3 hitDir = normalize(hitViewPos - viewPos); + float hitNdotV = clamp(dot(viewNormal, -hitDir), 0.0, 1.0); + float hitBackFace = smoothstep(-0.15, 0.3, hitNdotV); 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; + float falloffExp = PPComp10.x; + float maxDist = PPComp9.z; #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; + float falloffExp = ssrFalloffExp; + float maxDist = ssrSearchDist; #endif + float distAttenuation = 1.0 - clamp(length(viewPos - hitCoord) / maxDist, 0.0, 1.0); + distAttenuation = pow(distAttenuation, 1.5); + + float fresnel = pow(1.0 - NdotV, 5.0); + fresnel = mix(0.04, 1.0, fresnel); + + float intensity = pow(reflectivity, falloffExp) * screenEdgeFactor + * smoothstep(0.0, 0.1, -reflected.z) + * distAttenuation + * hitBackFace + * coords.w; + 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; + fragColor.rgb = reflCol * intensity * mix(0.5, 1.0, fresnel); } diff --git a/leenkx/Shaders/ssr_pass/ssr_pass.json b/leenkx/Shaders/ssr_pass/ssr_pass.json index 7bdc8379..5a34062a 100644 --- a/leenkx/Shaders/ssr_pass/ssr_pass.json +++ b/leenkx/Shaders/ssr_pass/ssr_pass.json @@ -22,6 +22,10 @@ "name": "cameraProj", "link": "_cameraPlaneProj" }, + { + "name": "screenSize", + "link": "_screenSize" + }, { "name": "PPComp9", "link": "_PPComp9", diff --git a/leenkx/Shaders/ssrefr_pass/ssrefr_pass.frag.glsl b/leenkx/Shaders/ssrefr_pass/ssrefr_pass.frag.glsl index 11d8b5d3..c8f71976 100644 --- a/leenkx/Shaders/ssrefr_pass/ssrefr_pass.frag.glsl +++ b/leenkx/Shaders/ssrefr_pass/ssrefr_pass.frag.glsl @@ -12,6 +12,7 @@ uniform sampler2D tex1; uniform sampler2D gbufferD; uniform sampler2D gbuffer0; uniform sampler2D gbufferD1; +uniform sampler2D gbuffer1; uniform sampler2D gbuffer_refraction; // ior\opacity uniform mat4 P; @@ -26,7 +27,7 @@ vec3 hitCoord; float depth; const int numBinarySearchSteps = 7; -const int maxSteps = int(ceil(1.0 / ss_refractionRayStep) * ss_refractionSearchDist); +const int maxSteps = 50; vec2 getProjectedCoord(const vec3 hit) { vec4 projectedCoord = P * vec4(hit, 1.0); @@ -39,45 +40,60 @@ vec2 getProjectedCoord(const vec3 hit) { } float getDeltaDepth(const vec3 hit) { - depth = textureLod(gbufferD1, getProjectedCoord(hit), 0.0).r * 2.0 - 1.0; + vec2 tc = getProjectedCoord(hit); + if (tc.x < 0.0 || tc.x > 1.0 || tc.y < 0.0 || tc.y > 1.0) + return -1.0; + depth = textureLod(gbufferD1, tc, 0.0).r * 2.0 - 1.0; vec3 viewPos = getPosView(viewRay, depth, cameraProj); return viewPos.z - hit.z; } -vec4 binarySearch(vec3 dir) { +vec4 binarySearch(vec3 dir, float stepSize) { float ddepth; for (int i = 0; i < numBinarySearchSteps; i++) { - dir *= 0.5; - hitCoord -= dir; + stepSize *= 0.5; + hitCoord -= dir * stepSize; ddepth = getDeltaDepth(hitCoord); - if (ddepth < 0.0) hitCoord += dir; + if (ddepth < 0.0) hitCoord += dir * stepSize; } - if (abs(ddepth) > ss_refractionSearchDist) return vec4(0.0); - return vec4(getProjectedCoord(hitCoord), 0.0, 1.0); + if (abs(ddepth) > ss_refractionSearchDist * 0.005) return vec4(0.0); + vec2 hitTC = getProjectedCoord(hitCoord); + if (hitTC.x < 0.0 || hitTC.x > 1.0 || hitTC.y < 0.0 || hitTC.y > 1.0) + return vec4(0.0); + return vec4(hitTC, 0.0, 1.0); } vec4 rayCast(vec3 dir) { - float ddepth; - dir *= ss_refractionRayStep; + float stepSize = ss_refractionRayStep * max(1.0, -viewRay.z * 0.1); + vec3 startPos = hitCoord; for (int i = 0; i < maxSteps; i++) { - hitCoord += dir; - ddepth = getDeltaDepth(hitCoord); - if (ddepth > 0.0) return binarySearch(dir); + hitCoord += dir * stepSize; + float dist = length(hitCoord - startPos); + if (dist > ss_refractionSearchDist) break; + float ddepth = getDeltaDepth(hitCoord); + if (ddepth > 0.0) return binarySearch(dir, stepSize); + stepSize *= 1.03; } return vec4(texCoord, 0.0, 0.0); } void main() { vec4 gr = textureLod(gbuffer_refraction, texCoord, 0.0); - float ior = gr.x; + float ior = unpackIOR(gr.x); float transmittance = gr.y; float surfaceDepth = gr.z; float d = surfaceDepth * 2.0 - 1.0; vec4 sceneSample = textureLod(tex, texCoord, 0.0); - if (surfaceDepth == 0.0 || transmittance == 0.0 || ior == 1.0) { + if (surfaceDepth == 0.0 || surfaceDepth == 1.0) { + fragColor = sceneSample; + return; + } + + vec4 g1 = textureLod(gbuffer1, texCoord, 0.0); + if (transmittance == 0.0 || ior == 1.0) { vec3 background = textureLod(tex1, texCoord, 0.0).rgb; - fragColor.rgb = sceneSample.rgb + background * (1.0 - sceneSample.a); + fragColor.rgb = g1.rgb + background * transmittance; fragColor.a = 1.0; return; } @@ -96,18 +112,18 @@ void main() { vec3 refracted = refract(incident, viewNormal, 1.0 / ior); if (length(refracted) < 0.001) { vec3 background = textureLod(tex1, texCoord, 0.0).rgb; - fragColor.rgb = sceneSample.rgb + background * (1.0 - sceneSample.a); + fragColor.rgb = g1.rgb + background * transmittance; fragColor.a = 1.0; return; } hitCoord = viewPos; - vec3 dir = refracted * (1.0 - rand(texCoord) * ss_refractionJitter * roughness) * 2.0; + vec3 dir = normalize(refracted); vec4 coords = rayCast(dir); - vec2 screenEdge = smoothstep(0.0, 0.1, coords.xy) * smoothstep(0.0, 0.1, 1.0 - coords.xy); - float screenEdgeFactor = screenEdge.x * screenEdge.y; + vec2 screenEdge = smoothstep(0.0, 0.05, coords.xy) * smoothstep(0.0, 0.05, 1.0 - coords.xy); + float screenEdgeFactor = max(screenEdge.x * screenEdge.y, 0.05); float refractivity = 1.0 - roughness; float intensity = pow(refractivity, ss_refractionFalloffExp) * screenEdgeFactor * coords.w; @@ -118,6 +134,6 @@ void main() { vec3 behindColor = mix(straightBackground, refractedBackground, intensity); - fragColor.rgb = sceneSample.rgb + behindColor * (1.0 - sceneSample.a); + fragColor.rgb = g1.rgb + behindColor * transmittance; fragColor.a = 1.0; } diff --git a/leenkx/Shaders/sss_pass/sss_pass.frag.glsl b/leenkx/Shaders/sss_pass/sss_pass.frag.glsl index f839df09..1410e9d7 100644 --- a/leenkx/Shaders/sss_pass/sss_pass.frag.glsl +++ b/leenkx/Shaders/sss_pass/sss_pass.frag.glsl @@ -40,100 +40,148 @@ uniform sampler2D gbufferD; uniform sampler2D gbuffer0; +uniform sampler2D gbuffer1; uniform sampler2D tex; uniform vec2 dir; uniform vec2 cameraProj; +uniform mat4 projectionMatrix; + +#ifdef _ExtBRDF +//!uniform vec4 materialParams[MAX_MATERIALS * 8]; +#endif in vec2 texCoord; out vec4 fragColor; -const vec3 SKIN_SSS_RADIUS = vec3(4.8, 2.4, 1.5); -const float SSS_DISTANCE_SCALE = 0.001; +// TODO: finish the SSS +const float SSS_SCALE = 0.05; +const float DEPTH_THRESHOLD = 0.05; -// Temp hash func - float hash13(vec3 p3) { p3 = fract(p3 * vec3(0.1031, 0.1030, 0.0973)); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.x + p3.y) * p3.z); } -vec4 SSSSBlur() { - const int SSSS_N_SAMPLES = 15; +vec4 SSSSBlur(vec3 sssRadius, float sssWeight) { + const int SSSS_N_SAMPLES = 11; vec4 kernel[SSSS_N_SAMPLES]; - - kernel[0] = vec4(0.233, 0.455, 0.649, 0.0); // Center sample - kernel[1] = vec4(0.100, 0.336, 0.344, 0.37); // +0.37mm - kernel[2] = vec4(0.118, 0.198, 0.0, 0.97); // +0.97mm - kernel[3] = vec4(0.113, 0.007, 0.007, 1.93); // +1.93mm - kernel[4] = vec4(0.358, 0.004, 0.0, 3.87); // +3.87mm - kernel[5] = vec4(0.078, 0.0, 0.0, 6.53); // +6.53mm (red only) - kernel[6] = vec4(0.0, 0.0, 0.0, 0.0); // Unused - kernel[7] = vec4(0.0, 0.0, 0.0, 0.0); // Unused - kernel[8] = vec4(0.100, 0.336, 0.344, -0.37); // -0.37mm - kernel[9] = vec4(0.118, 0.198, 0.0, -0.97); // -0.97mm - kernel[10] = vec4(0.113, 0.007, 0.007, -1.93); // -1.93mm - kernel[11] = vec4(0.358, 0.004, 0.0, -3.87); // -3.87mm - kernel[12] = vec4(0.078, 0.0, 0.0, -6.53); // -6.53mm (red only) - kernel[13] = vec4(0.0, 0.0, 0.0, 0.0); // Unused - kernel[14] = vec4(0.0, 0.0, 0.0, 0.0); // Unused - - vec4 colorM = textureLod(tex, texCoord, 0.0); - float depth = textureLod(gbufferD, texCoord, 0.0).r; + kernel[0] = vec4(0.233, 0.455, 0.649, 0.0); // Center sample + kernel[1] = vec4(0.100, 0.336, 0.344, 0.37); // +0.37 + kernel[2] = vec4(0.118, 0.198, 0.0, 0.97); // +0.97 + kernel[3] = vec4(0.113, 0.007, 0.007, 1.93); // +1.93 + kernel[4] = vec4(0.358, 0.004, 0.0, 3.87); // +3.87 + kernel[5] = vec4(0.078, 0.0, 0.0, 6.53); // +6.53 (red only) + kernel[6] = vec4(0.100, 0.336, 0.344, -0.37); // -0.37 + kernel[7] = vec4(0.118, 0.198, 0.0, -0.97); // -0.97 + kernel[8] = vec4(0.113, 0.007, 0.007, -1.93); // -1.93 + kernel[9] = vec4(0.358, 0.004, 0.0, -3.87); // -3.87 + kernel[10] = vec4(0.078, 0.0, 0.0, -6.53); // -6.53 (red only) + + vec2 texSize = vec2(textureSize(tex, 0)); + ivec2 texelCoord = ivec2(texCoord * texSize); + + vec4 colorM = texelFetch(tex, texelCoord, 0); + vec3 albedo = texelFetch(gbuffer1, texelCoord, 0).rgb; + + vec3 irradianceM = colorM.rgb / max(albedo, vec3(0.00001)); + + float depth = texelFetch(gbufferD, texelCoord, 0).r; float depthM = cameraProj.y / (depth - cameraProj.x); - float distanceScale = 1.0 / max(depthM, 0.1); - - vec2 finalStep = sssWidth * distanceScale * dir * SSS_DISTANCE_SCALE; - + float blurWidth = max(max(sssRadius.r, sssRadius.g), sssRadius.b); + float projScale = dot(dir, vec2(projectionMatrix[0][0], projectionMatrix[1][1])); + vec2 finalStep = blurWidth * (1.0 / depthM) * dir * projScale * SSS_SCALE; vec3 jitterSeed = vec3(texCoord.xy * 1000.0, fract(cameraProj.x * 0.0001)); float jitterOffset = (hash13(jitterSeed) * 2.0 - 1.0) * 0.15; - finalStep *= (1.0 + jitterOffset); - vec3 colorBlurred = vec3(0.0); - vec3 weightSum = vec3(0.0); - colorBlurred += colorM.rgb * kernel[0].rgb; - weightSum += kernel[0].rgb; - + + vec3 colorBlurred = irradianceM * kernel[0].rgb; + vec3 weightSum = kernel[0].rgb; + for (int i = 1; i < SSSS_N_SAMPLES; i++) { float sampleJitter = hash13(vec3(texCoord.xy * 720.0, float(i) * 37.45)) * 0.1 - 0.05; vec2 offset = texCoord + (kernel[i].a + sampleJitter) * finalStep; - vec4 color = textureLod(tex, offset, 0.0); - const float DEPTH_THRESHOLD = 0.05; - float sampleDepth = textureLod(gbufferD, offset, 0.0).r; - float sampleDepthM = cameraProj.y / (sampleDepth - cameraProj.x); - - float depthDiff = abs(depthM - sampleDepthM); - float depthWeight = exp(-depthDiff * 10.0); - - if (depthDiff > DEPTH_THRESHOLD) { - color.rgb = mix(colorM.rgb, color.rgb, depthWeight); + + vec3 irradiance = irradianceM; + float s = 0.0; + if (all(greaterThanEqual(offset, vec2(0.0))) && all(lessThan(offset, vec2(1.0)))) { + ivec2 sampleTexel = ivec2(offset * texSize); + + float sampleDepth = texelFetch(gbufferD, sampleTexel, 0).r; + float sampleDepthM = cameraProj.y / (sampleDepth - cameraProj.x); + float depthDiff = abs(depthM - sampleDepthM); + + if (depthDiff < 1.0) { + vec4 sampleG0 = texelFetch(gbuffer0, sampleTexel, 0); + float sampleMetallic; + uint sampleMatid; + unpackFloatInt16(sampleG0.a, sampleMetallic, sampleMatid); + bool sampleIsSSS = false; + #ifdef _ExtBRDF + if (sampleMatid >= 3u && sampleMatid < uint(MAX_MATERIALS)) { + if (materialParams[sampleMatid * 8u + 3u].z > 0.0) sampleIsSSS = true; + } + #endif + + if (sampleIsSSS) { + vec3 sampleColor = texelFetch(tex, sampleTexel, 0).rgb; + vec3 sampleAlbedo = texelFetch(gbuffer1, sampleTexel, 0).rgb; + irradiance = sampleColor / max(sampleAlbedo, vec3(0.00001)); + } + + if (depthDiff <= DEPTH_THRESHOLD) { + s = 1.0; + } else { + s = exp(-depthDiff * 10.0); + } + } } - - colorBlurred += color.rgb * kernel[i].rgb; + + colorBlurred += kernel[i].rgb * mix(irradianceM, irradiance, s); weightSum += kernel[i].rgb; } - vec3 normalizedColor = colorBlurred / max(weightSum, vec3(0.00001)); + + vec3 normalizedIrradiance = colorBlurred / max(weightSum, vec3(0.00001)); float dither = hash13(vec3(texCoord * 1333.0, 0.0)) * 0.003 - 0.0015; - normalizedColor = max(normalizedColor + vec3(dither), vec3(0.0)); - return vec4(normalizedColor, colorM.a); + normalizedIrradiance = max(normalizedIrradiance + vec3(dither), vec3(0.0)); + vec3 blurredColor = normalizedIrradiance * albedo; + vec3 result = mix(colorM.rgb, blurredColor, sssWeight); + return vec4(result, colorM.a); } void main() { - vec4 g0 = textureLod(gbuffer0, texCoord, 0.0); + vec2 texSize0 = vec2(textureSize(gbuffer0, 0)); + ivec2 texelCoord0 = ivec2(texCoord * texSize0); + vec4 g0 = texelFetch(gbuffer0, texelCoord0, 0); float metallic; uint matid; unpackFloatInt16(g0.a, metallic, matid); - if (matid == 2u) { - vec4 originalColor = textureLod(tex, texCoord, 0.0); - vec4 blurredColor = SSSSBlur(); - vec4 sssContribution = blurredColor - originalColor; - vec4 combined = originalColor + max(vec4(0.0), sssContribution) * 0.8; - fragColor = max(vec4(0.0), min(combined, vec4(10.0))); + bool applySSS = false; + vec3 sssRadius = vec3(1.0); + float sssWeight = 1.0; + vec4 matp0, matp1, matp2, matp3, matp4, matp5, matp6, matp7; + #ifdef _ExtBRDF + if (matid >= 3u && matid < uint(MAX_MATERIALS)) { + getMaterialParams(matid, matp0, matp1, matp2, matp3, matp4, matp5, matp6, matp7); + // matp3.z = subsurface, matp4.xyz = subsurfaceRadiusRGB, matp7.x = subsurfaceScale + if (matp3.z > 0.0) { + applySSS = true; + sssRadius = matp4.xyz * matp7.x; + sssWeight = matp3.z; + } + } + #endif + + if (applySSS) { + fragColor = SSSSBlur(sssRadius, sssWeight); } else { - fragColor = textureLod(tex, texCoord, 0.0); + vec2 texSizeMain = vec2(textureSize(tex, 0)); + ivec2 texelCoordMain = ivec2(texCoord * texSizeMain); + fragColor = texelFetch(tex, texelCoordMain, 0); } } diff --git a/leenkx/Shaders/sss_pass/sss_pass.json b/leenkx/Shaders/sss_pass/sss_pass.json index a304a91b..316cd2ef 100644 --- a/leenkx/Shaders/sss_pass/sss_pass.json +++ b/leenkx/Shaders/sss_pass/sss_pass.json @@ -13,6 +13,16 @@ { "name": "cameraProj", "link": "_cameraPlaneProj" + }, + { + "name": "projectionMatrix", + "link": "_projectionMatrix" + }, + { + "name": "materialParams", + "link": "_materialParams", + "type": "floats", + "ifdef": ["_ExtBRDF"] } ], "texture_params": [], @@ -32,6 +42,16 @@ { "name": "cameraProj", "link": "_cameraPlaneProj" + }, + { + "name": "projectionMatrix", + "link": "_projectionMatrix" + }, + { + "name": "materialParams", + "link": "_materialParams", + "type": "floats", + "ifdef": ["_ExtBRDF"] } ], "texture_params": [], diff --git a/leenkx/Shaders/std/brdf.glsl b/leenkx/Shaders/std/brdf.glsl index a0d76b4a..d7c1aa54 100644 --- a/leenkx/Shaders/std/brdf.glsl +++ b/leenkx/Shaders/std/brdf.glsl @@ -1,10 +1,30 @@ #ifndef _BRDF_GLSL_ #define _BRDF_GLSL_ +#ifndef PI +#define PI 3.1415926535 +#endif +#ifndef INV_PI +#define INV_PI 0.3183098861 +#endif +#ifndef INV_TWO_PI +#define INV_TWO_PI 0.1591549430 +#endif +#ifndef SCHLICK_A +#define SCHLICK_A -5.55473 +#endif +#ifndef SCHLICK_B +#define SCHLICK_B -6.98316 +#endif +#ifndef SRGB_GAMMA +#define SRGB_GAMMA 2.2 +#endif +#define srgbToLinear(x) pow(x, vec3(SRGB_GAMMA)) + // http://xlgames-inc.github.io/posts/improvedibl/ // http://blog.selfshadow.com/publications/s2013-shading-course/ vec3 f_schlick(const vec3 f0, const float vh) { - return f0 + (1.0 - f0) * exp2((-5.55473 * vh - 6.98316) * vh); + return f0 + (1.0 - f0) * exp2((SCHLICK_A * vh + SCHLICK_B) * vh); } float v_smithschlick(const float nl, const float nv, const float a) { @@ -31,7 +51,7 @@ 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; + return a2 * INV_PI / denom; } vec3 specularBRDF(const vec3 f0, const float roughness, const float nl, const float nh, const float nv, const float vh) { @@ -44,11 +64,10 @@ vec3 specularBRDF(const vec3 f0, const float roughness, const float nl, const fl // 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); + float D = alphaSqr / (PI * denom * denom); // F const float F_a = 1.0; float F_b = pow(1.0 - dotLH, 5.0); @@ -65,21 +84,8 @@ vec3 specularBRDFb(const vec3 f0, const float roughness, const float dotNL, cons 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 * (1.0 / 3.1415926535) * nl; + return albedo * INV_PI * nl; } vec3 surfaceAlbedo(const vec3 baseColor, const float metalness) { @@ -95,24 +101,6 @@ 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 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); -} - // 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 }; @@ -139,38 +127,39 @@ float D_Approx(const float Roughness, const float RoL) { } #ifdef _ClearCoat +float brdf_coatF0; vec3 clearcoatBRDF(const float clearcoat, const float clearcoat_rough, - const float coat_ior, - const float dotNL, const float dotNH, const float dotNV, const float dotVH) { + const float coat_ior, const vec3 coatN, const vec3 l, const vec3 v, const vec3 h) { if (clearcoat <= 0.0) return vec3(0.0); + float cdotNL = max(0.0, dot(coatN, l)); + float cdotNH = max(0.0, dot(coatN, h)); + float cdotNV = max(0.0, dot(coatN, v)); + float cdotVH = max(0.0, dot(v, h)); float a = clearcoat_rough * clearcoat_rough; - // F0 from Fresnel equation for dielectric - float ccF0 = (coat_ior - 1.0) / (coat_ior + 1.0); - ccF0 = ccF0 * ccF0; - float F = ccF0 + (1.0 - ccF0) * pow(1.0 - dotVH, 5.0); - float D = d_ggx(dotNH, a); - float G = g2_approx(dotNL, dotNV, a); - return vec3(clearcoat * D * G * F / max(4.0 * dotNV, 1e-5)); + float F = brdf_coatF0 + (1.0 - brdf_coatF0) * exp2((SCHLICK_A * cdotVH + SCHLICK_B) * cdotVH); + float D = d_ggx(cdotNH, a); + float G = g2_approx(cdotNL, cdotNV, a); + return vec3(clearcoat * D * G * F / max(4.0 * cdotNV, 1e-5)); } float coatAttenuation(const float clearcoat, - const float coat_ior, const float dotNV) { + const float coat_ior, const vec3 coatN, const vec3 v) { if (clearcoat <= 0.0) return 1.0; - float ccF0 = (coat_ior - 1.0) / (coat_ior + 1.0); - ccF0 = ccF0 * ccF0; - // Schlick with pow(.,5) - cheaper than exp2 - float F = ccF0 + (1.0 - ccF0) * pow(1.0 - dotNV, 5.0); + float cdotNV = max(0.0, dot(coatN, v)); + float F = brdf_coatF0 + (1.0 - brdf_coatF0) * exp2((SCHLICK_A * cdotNV + SCHLICK_B) * cdotNV); return max(1.0 - F * clearcoat, 0.0); } -vec3 coatTintAttenuation(const float clearcoat, const vec3 coat_tint, const float dotNV) { +vec3 coatTintAttenuation(const float clearcoat, const vec3 coat_tint, const vec3 coatN, const vec3 v) { if (clearcoat <= 0.0) return vec3(1.0); - float absorption = 1.0 / max(dotNV, 0.3); - return mix(vec3(1.0), coat_tint, clamp(absorption * 0.2, 0.0, 1.0)); + float cdotNV = max(0.0, dot(coatN, v)); + float absorption = 1.0 / max(cdotNV, 0.3); + return mix(vec3(1.0), clamp(coat_tint, 0.0, 1.0), clamp(absorption * 0.2, 0.0, 1.0)); } #endif #ifdef _Sheen +float brdf_sheenAlbedo; // based on Blender sheen model/Frostbite PBR vec3 sheenBRDF(const float sheen, const float sheen_rough, const vec3 sheen_tint, const float dotNL, const float dotNH, const float dotNV) { @@ -179,18 +168,16 @@ vec3 sheenBRDF(const float sheen, const float sheen_rough, float a = rough * rough; float sinNH2 = 1.0 - dotNH * dotNH; float a2 = a * a; - float D = (2.0 + a2) * sinNH2 / (2.0 * 3.1415926535 * pow(1.0 + a2 * sinNH2, 2.0)); + float denom = 1.0 + a2 * sinNH2; + float D = (2.0 + a2) * sinNH2 * INV_TWO_PI / (denom * denom); float V = 1.0 / (4.0 * dotNL * dotNV + 1e-5); - float sheenAlbedo = (1.0 - 0.5 * rough) * mix(1.0, dotNV, 0.5); - return sheen_tint * sheen * D * V * dotNL * sheenAlbedo; + return sheen_tint * sheen * D * V * dotNL * brdf_sheenAlbedo; } float sheenAttenuation(const float sheen, const float sheen_rough, const vec3 sheen_tint, const float dotNV) { if (sheen <= 0.0) return 1.0; - float rough = clamp(sheen_rough, 1e-3, 1.0); - float sheenAlbedo = (1.0 - 0.5 * rough) * mix(1.0, dotNV, 0.5); - float maxComp = sheen * max(max(sheen_tint.r, sheen_tint.g), sheen_tint.b) * sheenAlbedo; + float maxComp = sheen * max(max(sheen_tint.r, sheen_tint.g), sheen_tint.b) * brdf_sheenAlbedo; return max(1.0 - maxComp, 0.0); } #endif @@ -202,7 +189,7 @@ vec3 anisotropicBRDF(const vec3 f0, const float roughness, const float anisotrop const vec3 n, const vec3 l, const vec3 v, const float dotNL, const float dotNV) { if (abs(anisotropy) <= 0.001) return vec3(0.0); - float rot = aniso_rot * 3.1415926535 * 2.0; + float rot = aniso_rot * PI * 2.0; float cr = cos(rot); float sr = sin(rot); vec3 t = normalize(tangent * cr + bitangent * sr); @@ -221,7 +208,7 @@ vec3 anisotropicBRDF(const vec3 f0, const float roughness, const float anisotrop float dotTL = dot(t, l); float dotBL = dot(b, l); float denom = max(dotTH * dotTH / at2 + dotBH * dotBH / ab2, 1e-7); - float D = 1.0 / (3.1415926535 * at * ab * denom * denom); + float D = INV_PI / (at * ab * denom * denom); float V = 1.0 / max(dotNL * (dotTL / at + dotBL / ab) * (dotTV / at + dotBV / ab), 1e-5); float dotVH = max(dot(v, h), 0.0); vec3 F = f_schlick(f0, dotVH); @@ -229,46 +216,117 @@ vec3 anisotropicBRDF(const vec3 f0, const float roughness, const float anisotrop } #endif -#ifdef _Subsurface -// Blenders bssrdf_burley implementation -vec3 subsurfaceBRDF(const vec3 albedo, const vec3 sss_color, - const vec3 sss_radius, const float subsurface, const float sss_anisotropy, - const float dotNL) { - if (subsurface <= 0.0) return vec3(0.0); - vec3 mfp = sss_radius * (0.25 / 3.1415926535); - vec3 A = clamp(albedo, 0.0, 1.0); - vec3 d = 1.9 - A + 3.5 * (A - 0.8) * (A - 0.8); - d = mfp / max(d, 1e-5); - float aniso = clamp(sss_anisotropy, 0.0, 0.9); - float scatter = subsurface * (1.0 / 3.1415926535); - vec3 sssDiffuse = sss_color * scatter * dotNL; - float backScatter = max(0.0, 1.0 - dotNL) * (1.0 - aniso) * 0.5; - vec3 sssBack = sss_color * subsurface * backScatter; - float dist = max(0.0, 1.0 - dotNL); - vec3 rcp_d = 1.0 / max(d, vec3(1e-5)); - vec3 x = vec3(dist) * rcp_d; - vec3 extinction = 1.0 / (1.0 + x + 0.5 * x * x); - return sssDiffuse * extinction + sssBack * (vec3(1.0) - extinction); -} -#endif - #ifdef _Transmission +float brdf_transmissionF0; // Blenders microfacet glass/refraction model vec3 transmissionBRDF(const vec3 albedo, const float transmission, const float trans_rough, const float ior, const float thin_wall, const float dotNL, const float dotNV, const float dotVH) { if (transmission <= 0.0) return vec3(0.0); - float F0 = (ior - 1.0) / (ior + 1.0); - F0 = F0 * F0; - float F = F0 + (1.0 - F0) * pow(1.0 - dotVH, 5.0); + float F = brdf_transmissionF0 + (1.0 - brdf_transmissionF0) * exp2((SCHLICK_A * dotVH + SCHLICK_B) * dotVH); float transmittance = 1.0 - F; if (thin_wall > 0.5) { return albedo * transmission * transmittance * dotNL; } float a = trans_rough * trans_rough; - float rough_atten = mix(1.0, 1.0 / max(dotNV, 0.1), a); + float rough_atten = min(mix(1.0, 1.0 / max(dotNV, 0.1), a), 4.0); return albedo * transmission * transmittance * rough_atten * dotNL; } #endif +#ifdef _ExtBRDF +float brdf_sheenWeight = 1.0; +float brdf_coatWeight = 1.0; +vec3 brdf_coatTintAbsorb = vec3(1.0); + +vec3 applyExtBRDFLayers( + const vec3 direct, + const vec3 albedo, + const vec3 f0, + const float roughness, + const float dotNL, const float dotNV, const float dotNH, const float dotVH, + const vec3 n, const vec3 l, const vec3 v, const vec3 h, + #ifdef _ClearCoat + const float clearcoat, const float clearcoatRough, const float coatIOR, + const vec3 coatTint, const vec3 coatN, + #endif + #ifdef _Sheen + const float sheen, const float sheenRough, const vec3 sheenTint, + #endif + #ifdef _Transmission + const float transmission, const float transRough, const float ior, const float thinWall, + #endif + out float layerWeight +) { + float sheenWeight = brdf_sheenWeight; + float coatWeight = brdf_coatWeight; + #ifdef _Sheen + vec3 sheenContrib = sheenBRDF(sheen, sheenRough, sheenTint, dotNL, dotNH, dotNV); + #endif + #ifdef _ClearCoat + vec3 coatContrib = clearcoatBRDF(clearcoat, clearcoatRough, coatIOR, coatN, l, v, h); + #endif + layerWeight = sheenWeight * coatWeight; + vec3 result = direct * layerWeight; + #ifdef _Transmission + result += transmissionBRDF(albedo, transmission, transRough, ior, thinWall, dotNL, dotNV, dotVH) * layerWeight; + #endif + #ifdef _ClearCoat + result *= brdf_coatTintAbsorb; + result += coatContrib * sheenWeight; + #endif + #ifdef _Sheen + result += sheenContrib; + #endif + return result; +} +#endif + +#ifdef _ClearCoat +float coatIBLFresnel(const float clearcoat, const float coat_ior, + const float dotNV_coat) { + if (clearcoat <= 0.0) return 0.0; + float F = brdf_coatF0 + (1.0 - brdf_coatF0) * exp2((SCHLICK_A * dotNV_coat + SCHLICK_B) * dotNV_coat); + return F * clearcoat; +} +#endif + +#ifdef _Sheen +float sheenIBLAlbedo(const float sheen, const float sheen_rough, + const float dotNV) { + if (sheen <= 0.0) return 0.0; + float rough = clamp(sheen_rough, 1e-3, 1.0); + return sheen * (1.0 - 0.5 * rough) * mix(1.0, dotNV, 0.5); +} +#endif + +#ifdef _Anisotropy +vec3 anisotropicIBLDirection(const vec3 n, const vec3 v, const vec3 tangent, + const float anisotropy, const float roughness) { + if (abs(anisotropy) <= 0.001 || dot(tangent, tangent) < 0.001) + return reflect(-v, n); + vec3 bitangent = normalize(cross(n, tangent)); + vec3 r = reflect(-v, n); + float aniso_abs = abs(anisotropy); + vec3 stretchDir = anisotropy > 0.0 ? tangent : bitangent; + float stretchAmt = aniso_abs * roughness; + return normalize(r + stretchDir * stretchAmt * dot(r, stretchDir) * 0.5); +} +#endif + +#ifdef _Transmission +float transmissionIBLFresnel(const float ior, const float dotNV) { + return brdf_transmissionF0 + (1.0 - brdf_transmissionF0) * exp2((SCHLICK_A * dotNV + SCHLICK_B) * dotNV); +} + +vec3 transmissionIBLDirection(const vec3 n, const vec3 v, const float ior) { + float eta = 1.0 / ior; + vec3 refrDir = refract(-v, n, eta); + if (dot(refrDir, refrDir) < 0.001) { + refrDir = reflect(-v, n); + } + return refrDir; +} +#endif + #endif diff --git a/leenkx/Shaders/std/conetrace.glsl b/leenkx/Shaders/std/conetrace.glsl index a80ff047..fef95310 100644 --- a/leenkx/Shaders/std/conetrace.glsl +++ b/leenkx/Shaders/std/conetrace.glsl @@ -34,17 +34,19 @@ THE SOFTWARE. // https://research.nvidia.com/sites/default/files/publications/GIVoxels-pg2011-authors.pdf const float MAX_DISTANCE = voxelgiRange; +const int MAX_CONE_STEPS = 32; #ifdef _VoxelGI -uniform sampler3D dummy; vec4 sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapCount * 10], const float clipmap_index, const float step_dist, const int precomputed_direction, const vec3 face_offset, const vec3 direction_weight) { vec4 col = vec4(0.0); - vec3 tc = (P - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution); + int base = int(clipmap_index * 10); + float voxelSize = float(clipmaps[base]); + vec3 tc = (P - vec3(clipmaps[base + 4], clipmaps[base + 5], clipmaps[base + 6])) / (voxelSize * voxelgiResolution); vec3 half_texel = vec3(0.5) / voxelgiResolution; tc = tc * 0.5 + 0.5; tc = clamp(tc, half_texel, 1.0 - half_texel); - tc.x = (tc.x + precomputed_direction) / (6 + DIFFUSE_CONE_COUNT); + tc.x = (tc.x + precomputed_direction) / (6 + diffuseConeCount); tc.y = (tc.y + clipmap_index) / voxelgiClipmapCount; if (precomputed_direction == 0) { @@ -55,7 +57,7 @@ vec4 sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapCo else col = textureLod(voxels, tc, 0); - col *= step_dist / float(clipmaps[int(clipmap_index * 10)]); + col *= step_dist / voxelSize; return col; } @@ -64,11 +66,13 @@ vec4 sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapCo #ifdef _VoxelAOvar float sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapCount * 10], const float clipmap_index, const float step_dist, const int precomputed_direction, const vec3 face_offset, const vec3 direction_weight) { float opac = 0.0; - vec3 tc = (P - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution); + int base = int(clipmap_index * 10); + float voxelSize = float(clipmaps[base]); + vec3 tc = (P - vec3(clipmaps[base + 4], clipmaps[base + 5], clipmaps[base + 6])) / (voxelSize * voxelgiResolution); vec3 half_texel = vec3(0.5) / voxelgiResolution; tc = tc * 0.5 + 0.5; tc = clamp(tc, half_texel, 1.0 - half_texel); - tc.x = (tc.x + precomputed_direction) / (6 + DIFFUSE_CONE_COUNT); + tc.x = (tc.x + precomputed_direction) / (6 + diffuseConeCount); tc.y = (tc.y + clipmap_index) / voxelgiClipmapCount; if (precomputed_direction == 0) { @@ -79,7 +83,7 @@ float sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapC else opac = textureLod(voxels, tc, 0).r; - opac *= step_dist / float(clipmaps[int(clipmap_index * 10)]); + opac *= step_dist / voxelSize; return opac; } @@ -92,7 +96,7 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori float dist = voxelSize0; float step_dist = dist; vec3 samplePos; - vec3 start_pos = origin + n * voxelSize0; + vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset; int clipmap_index0 = 0; vec3 aniso_direction = -dir; @@ -100,12 +104,14 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori aniso_direction.x > 0.0 ? 0.0 : 1.0, aniso_direction.y > 0.0 ? 2.0 : 3.0, aniso_direction.z > 0.0 ? 4.0 : 5.0 - ) / (6 + DIFFUSE_CONE_COUNT); + ) / (6 + diffuseConeCount); vec3 direction_weight = abs(dir); float coneCoefficient = 2.0 * tan(aperture * 0.5); - while (sampleCol.a < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount) { + const vec3 half_texel = vec3(0.5) / voxelgiResolution; + int steps = 0; + while (sampleCol.a < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount && steps < MAX_CONE_STEPS) { vec4 mipSample = vec4(0.0); float diam = max(voxelSize0, dist * coneCoefficient); float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1); @@ -113,7 +119,9 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori float clipmap_blend = smoothstep(0.0, 1.0, fract(lod)); vec3 p0 = start_pos + dir * dist; - samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution); + int base = int(clipmap_index * 10); + float voxelSize = float(clipmaps[base]); + samplePos = (p0 - vec3(clipmaps[base + 4], clipmaps[base + 5], clipmaps[base + 6])) / (voxelSize * voxelgiResolution); samplePos = samplePos * 0.5 + 0.5; if (any(notEqual(samplePos, clamp(samplePos, 0.0, 1.0)))) { @@ -129,8 +137,11 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, precomputed_direction, face_offset, direction_weight); - if(totalBlend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) { + if(totalBlend > 0.05 && clipmap_index < voxelgiClipmapCount - 1) { vec4 mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, precomputed_direction, face_offset, direction_weight); + int baseNext = int((clipmap_index + 1.0) * 10); + float voxelSizeCoarse = float(clipmaps[baseNext]); + mipSampleNext *= voxelSizeCoarse / voxelSize; mipSample = mix(mipSample, mipSampleNext, totalBlend); } @@ -138,8 +149,6 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori float stepSizeCurrent = step_size; if (use_sdf) { - // half texel correction is applied to avoid sampling over current clipmap: - const vec3 half_texel = vec3(0.5) / voxelgiResolution; vec3 tc0 = clamp(samplePos, half_texel, 1 - half_texel); tc0.y = (tc0.y + clipmap_index) / voxelgiClipmapCount; // remap into clipmap float sdf = textureLod(voxelsSDF, tc0, 0).r; @@ -147,6 +156,7 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori } step_dist = diam * stepSizeCurrent; dist += step_dist; + steps++; } return sampleCol; } @@ -154,13 +164,13 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori vec4 traceDiffuse(const vec3 origin, const vec3 normal, const sampler3D voxels, const float clipmaps[voxelgiClipmapCount * 10]) { float sum = 0.0; vec4 amount = vec4(0.0); - for (int i = 0; i < DIFFUSE_CONE_COUNT; ++i) { - vec3 coneDir = DIFFUSE_CONE_DIRECTIONS[i]; + for (int i = 0; i < diffuseConeCount; ++i) { + vec3 coneDir = diffuseConeDirections[i]; const float cosTheta = dot(normal, coneDir); if (cosTheta <= 0) continue; int precomputed_direction = 6 + i; - amount += traceCone(voxels, dummy, origin, normal, coneDir, precomputed_direction, false, DIFFUSE_CONE_APERTURE, 1.0, clipmaps) * cosTheta; + amount += traceCone(voxels, voxels, origin, normal, coneDir, precomputed_direction, false, diffuseConeAperture, 1.0, clipmaps) * cosTheta; sum += cosTheta; } @@ -191,7 +201,7 @@ vec4 traceRefraction(const vec3 origin, const vec3 normal, sampler3D voxels, sam amount.rgb = max(vec3(0.0), amount.rgb); amount.a = clamp(amount.a, 0.0, 1.0); - return amount * voxelgiOcc; + return amount * voxelgiOcc * voxelgiRefr; } #endif @@ -202,7 +212,7 @@ float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const float dist = voxelSize0; float step_dist = dist; vec3 samplePos; - vec3 start_pos = origin + n * voxelSize0; + vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset; int clipmap_index0 = 0; vec3 aniso_direction = -dir; @@ -210,12 +220,13 @@ float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const aniso_direction.x > 0.0 ? 0.0 : 1.0, aniso_direction.y > 0.0 ? 2.0 : 3.0, aniso_direction.z > 0.0 ? 4.0 : 5.0 - ) / (6 + DIFFUSE_CONE_COUNT); + ) / (6 + diffuseConeCount); vec3 direction_weight = abs(dir); float coneCoefficient = 2.0 * tan(aperture * 0.5); - while (sampleCol < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount) { + int steps = 0; + while (sampleCol < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount && steps < MAX_CONE_STEPS) { float mipSample = 0.0; float diam = max(voxelSize0, dist * coneCoefficient); float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1); @@ -223,7 +234,9 @@ float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const float clipmap_blend = smoothstep(0.0, 1.0, fract(lod)); vec3 p0 = start_pos + dir * dist; - samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution); + int base = int(clipmap_index * 10); + float voxelSize = float(clipmaps[base]); + samplePos = (p0 - vec3(clipmaps[base + 4], clipmaps[base + 5], clipmaps[base + 6])) / (voxelSize * voxelgiResolution); samplePos = samplePos * 0.5 + 0.5; if ((any(notEqual(clamp(samplePos, 0.0, 1.0), samplePos)))) { @@ -239,8 +252,11 @@ float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, precomputed_direction, face_offset, direction_weight); - if(totalBlend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) { + if(totalBlend > 0.05 && clipmap_index < voxelgiClipmapCount - 1) { float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, precomputed_direction, face_offset, direction_weight); + int baseNext = int((clipmap_index + 1.0) * 10); + float voxelSizeCoarse = float(clipmaps[baseNext]); + mipSampleNext *= voxelSizeCoarse / voxelSize; mipSample = mix(mipSample, mipSampleNext, totalBlend); } @@ -248,6 +264,7 @@ float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const step_dist = diam * step_size; dist += step_dist; + steps++; } return sampleCol; } @@ -256,18 +273,18 @@ float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const float traceAO(const vec3 origin, const vec3 normal, const sampler3D voxels, const float clipmaps[voxelgiClipmapCount * 10]) { float sum = 0.0; float amount = 0.0; - for (int i = 0; i < DIFFUSE_CONE_COUNT; i++) { - vec3 coneDir = DIFFUSE_CONE_DIRECTIONS[i]; + for (int i = 0; i < diffuseConeCount; i++) { + vec3 coneDir = diffuseConeDirections[i]; int precomputed_direction = 6 + i; const float cosTheta = dot(normal, coneDir); if (cosTheta <= 0) continue; - amount += traceConeAO(voxels, origin, normal, coneDir, precomputed_direction, DIFFUSE_CONE_APERTURE, 1.0, clipmaps) * cosTheta; + amount += traceConeAO(voxels, origin, normal, coneDir, precomputed_direction, diffuseConeAperture, 1.0, clipmaps) * cosTheta; sum += cosTheta; } amount /= max(sum, 0.0001); amount = clamp(amount, 0.0, 1.0); - return amount * voxelgiOcc; + return amount; } #endif @@ -278,7 +295,7 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v float dist = voxelSize0; float step_dist = dist; vec3 samplePos; - vec3 start_pos = origin + n * voxelSize0; + vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset; int clipmap_index0 = 0; vec3 aniso_direction = -dir; @@ -286,11 +303,13 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v aniso_direction.x > 0.0 ? 0.0 : 1.0, aniso_direction.y > 0.0 ? 2.0 : 3.0, aniso_direction.z > 0.0 ? 4.0 : 5.0 - ) / (6 + DIFFUSE_CONE_COUNT); + ) / (6 + diffuseConeCount); vec3 direction_weight = abs(dir); float coneCoefficient = 2.0 * tan(aperture * 0.5); - while (sampleCol < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount) { + const vec3 half_texel = vec3(0.5) / voxelgiResolution; + int steps = 0; + while (sampleCol < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount && steps < MAX_CONE_STEPS) { float mipSample = 0.0; float diam = max(voxelSize0, dist * coneCoefficient); float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1); @@ -298,7 +317,9 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v float clipmap_blend = smoothstep(0.0, 1.0, fract(lod)); vec3 p0 = start_pos + dir * dist; - samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution); + int base = int(clipmap_index * 10); + float voxelSize = float(clipmaps[base]); + samplePos = (p0 - vec3(clipmaps[base + 4], clipmaps[base + 5], clipmaps[base + 6])) / (voxelSize * voxelgiResolution); samplePos = samplePos * 0.5 + 0.5; if ((any(notEqual(samplePos, clamp(samplePos, 0.0, 1.0))))) { @@ -318,11 +339,14 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, 0, face_offset, direction_weight).a; #endif - if(totalBlend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) { + if(totalBlend > 0.05 && clipmap_index < voxelgiClipmapCount - 1) { + int baseNext = int((clipmap_index + 1.0) * 10); + float voxelSizeCoarse = float(clipmaps[baseNext]); + float scaleRatio = voxelSizeCoarse / voxelSize; #ifdef _VoxelAOvar - float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, 0, face_offset, direction_weight); + float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, 0, face_offset, direction_weight) * scaleRatio; #else - float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, 0, face_offset, direction_weight).a; + float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, 0, face_offset, direction_weight).a * scaleRatio; #endif mipSample = mix(mipSample, mipSampleNext, totalBlend); } @@ -331,8 +355,6 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v float stepSizeCurrent = step_size; - // half texel correction is applied to avoid sampling over current clipmap: - const vec3 half_texel = vec3(0.5) / voxelgiResolution; vec3 tc0 = clamp(samplePos, half_texel, 1 - half_texel); tc0.y = (tc0.y + clipmap_index) / voxelgiClipmapCount; // remap into clipmap float sdf = textureLod(voxelsSDF, tc0, 0.0).r; @@ -340,6 +362,7 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v step_dist = diam * stepSizeCurrent; dist += step_dist; + steps++; } return sampleCol; } @@ -347,7 +370,7 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v float traceShadow(const vec3 origin, const vec3 normal, const sampler3D voxels, const sampler3D voxelsSDF, const vec3 dir, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel, const vec2 velocity) { vec3 P = origin + dir * (BayerMatrix8[int(pixel.x + velocity.x) % 8][int(pixel.y + velocity.y) % 8] - 0.5) * voxelgiStep; - float amount = traceConeShadow(voxels, voxelsSDF, P, normal, dir, SHADOW_CONE_APERTURE, voxelgiStep, clipmaps); + float amount = traceConeShadow(voxels, voxelsSDF, P, normal, dir, voxelgiAperture, voxelgiStep, clipmaps); amount = clamp(amount, 0.0, 1.0); return amount * voxelgiOcc; } diff --git a/leenkx/Shaders/std/constants.glsl b/leenkx/Shaders/std/constants.glsl index 994d17ce..3980529e 100644 --- a/leenkx/Shaders/std/constants.glsl +++ b/leenkx/Shaders/std/constants.glsl @@ -20,13 +20,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -const int DIFFUSE_CONE_COUNT = 16; +const float diffuseConeAperture = radians(39.0); -const float SHADOW_CONE_APERTURE = radians(15.0); - -const float DIFFUSE_CONE_APERTURE = 1.0; - -const vec3 DIFFUSE_CONE_DIRECTIONS[16] = vec3[]( +const vec3 diffuseConeDirections[16] = vec3[]( vec3( 0.3480, 0.0000, 0.9375), vec3(-0.4299, 0.3938, 0.8125), vec3( 0.0635, -0.7234, 0.6875), diff --git a/leenkx/Shaders/std/gbuffer.glsl b/leenkx/Shaders/std/gbuffer.glsl index 58e140fb..1d0cd00e 100644 --- a/leenkx/Shaders/std/gbuffer.glsl +++ b/leenkx/Shaders/std/gbuffer.glsl @@ -173,16 +173,85 @@ void unpackFloatInt16(float val, out float f, out uint i) { #ifdef _ExtBRDF // extended material parameters by material slot ID returns vec4s (28 floats) of extended BRDF parameters void getMaterialParams(uint matid, out vec4 p0, out vec4 p1, out vec4 p2, out vec4 p3, - out vec4 p4, out vec4 p5, out vec4 p6) { + out vec4 p4, out vec4 p5, out vec4 p6, out vec4 p7) { uint base = matid * 8u; + #if defined(_Anisotropy) || defined(_Sheen) p0 = materialParams[base]; + #else + p0 = vec4(0.0); + #endif + #if defined(_ClearCoat) p1 = materialParams[base + 1u]; + #else + p1 = vec4(0.0); + #endif + #if defined(_ClearCoat) || defined(_Transmission) p2 = materialParams[base + 2u]; + #else + p2 = vec4(0.0); + #endif + #if defined(_Transmission) || defined(_SSS) p3 = materialParams[base + 3u]; + #else + p3 = vec4(0.0); + #endif + #if defined(_SSS) p4 = materialParams[base + 4u]; + #else + p4 = vec4(0.0); + #endif + #if defined(_Sheen) || defined(_SSS) p5 = materialParams[base + 5u]; + #else + p5 = vec4(0.0); + #endif + #if defined(_ExtBRDF) p6 = materialParams[base + 6u]; + #else + p6 = vec4(0.0); + #endif + #if defined(_SSS) + p7 = materialParams[base + 7u]; + #else + p7 = vec4(0.0); + #endif } #endif +float packIOR(float ior) { + return clamp((ior - 1.0) / 1.5, 0.0, 1.0); +} + +float unpackIOR(float packed) { + return packed * 1.5 + 1.0; +} + +#ifndef PI +#define PI 3.1415926535 +#endif +#ifndef PI2 +#define PI2 6.2831853071 +#endif + +float encodeTangent(vec3 tangent, vec3 normal) { + if (length(tangent) < 0.5) return -1.0; + vec3 t = normalize(tangent); + vec3 n = normalize(normal); + vec3 ref = abs(n.y) < 0.999 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0); + vec3 r = normalize(ref - n * dot(ref, n)); + vec3 b = cross(n, r); + float angle = atan(dot(t, b), dot(t, r)); + return (angle / (2.0 * PI) + 0.5); +} + +vec3 decodeTangent(float enc, vec3 normal) { + if (enc < 0.0) return vec3(1.0, 0.0, 0.0); + vec3 n = normalize(normal); + vec3 ref = abs(n.y) < 0.999 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0); + vec3 r = normalize(ref - n * dot(ref, n)); + vec3 b = cross(n, r); + float angle = (enc - 0.5) * 2.0 * PI; + return normalize(r * cos(angle) + b * sin(angle)); +} + #endif diff --git a/leenkx/Shaders/std/ies.glsl b/leenkx/Shaders/std/ies.glsl index a7e9f124..ae4e801f 100644 --- a/leenkx/Shaders/std/ies.glsl +++ b/leenkx/Shaders/std/ies.glsl @@ -3,7 +3,9 @@ uniform sampler2D texIES; float iesAttenuation(vec3 l) { - const float PI = 3.1415926535; + #ifndef PI + #define PI 3.1415926535 + #endif // https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf // Sample direction into light space // vec3 iesSampleDirection = mul(light.worldToLight , -L); diff --git a/leenkx/Shaders/std/light.glsl b/leenkx/Shaders/std/light.glsl index 35f1276b..b093a3cb 100644 --- a/leenkx/Shaders/std/light.glsl +++ b/leenkx/Shaders/std/light.glsl @@ -51,7 +51,9 @@ //!uniform sampler2D shadowMapAtlasTransparent; #endif #endif + #ifndef _SinglePoint uniform vec2 lightProj; + #endif #ifdef _ShadowMapAtlas #ifndef _SingleAtlas uniform sampler2DShadow shadowMapAtlasPoint; @@ -92,7 +94,6 @@ uniform vec3 lightArea3; uniform sampler2D sltcMat; uniform sampler2D sltcMag; #ifdef _ShadowMap -#ifndef _Spot #ifdef _SinglePoint uniform sampler2DShadow shadowMapSpot[1]; #ifdef _ShadowMapTransparent @@ -100,18 +101,10 @@ uniform sampler2D sltcMag; #endif uniform mat4 LWVPSpotArray[1]; #endif - #ifdef _Clusters - uniform sampler2DShadow shadowMapSpot[maxLightsCluster]; - #ifdef _ShadowMapTransparent - uniform sampler2D shadowMapSpotTransparent[maxLightsCluster]; - #endif - 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, +vec3 sampleLightCore(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 @@ -122,17 +115,8 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co #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], vec2 velocity - #endif - #ifdef _MicroShadowing - , float occ - #endif - #ifdef _SSRS - , sampler2D gbufferD, mat4 invVP, vec3 eye - #endif #ifdef _ClearCoat - , float clearcoat, float clearcoatRough, float coatIOR, vec3 coatTint + , float clearcoat, float clearcoatRough, float coatIOR, vec3 coatTint, vec3 coatN #endif #ifdef _Sheen , float sheen, float sheenRough, vec3 sheenTint @@ -140,20 +124,25 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co #ifdef _Anisotropy , float anisotropy, float anisoRot, vec3 tangent #endif - #ifdef _Subsurface + #ifdef _SSS , float subsurface, vec3 sssColor, vec3 sssRadius, float sssAnisotropy #endif #ifdef _Transmission , float transmission, float transRough, float ior, float thinWall #endif + , out vec3 l_out ) { vec3 ld = lp - p; - vec3 l = normalize(ld); + float dist = length(ld); + vec3 l = ld / dist; 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 _VoxelPass + vec3 direct = vec3(dotNL); + #else #ifdef _LTC float theta = acos(dotNV); vec2 tuv = vec2(rough, theta / (0.5 * PI)); @@ -170,7 +159,7 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co #else #ifdef _Anisotropy vec3 direct; - if (abs(anisotropy) > 0.001) { + if (abs(anisotropy) > 0.001 && dot(tangent, tangent) > 0.001) { vec3 bitangent = normalize(cross(n, tangent)); direct = lambertDiffuseBRDF(albedo, dotNL) + anisotropicBRDF(f0, rough, anisotropy, anisoRot, @@ -181,62 +170,36 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co } #else vec3 direct = lambertDiffuseBRDF(albedo, dotNL) + - specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec; + specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec; #endif #endif // before attenuate/shadow so everything is properly shadowed in one pass - float sheenWeight = 1.0; - float coatWeight = 1.0; - #ifdef _Sheen - vec3 sheenContrib = sheenBRDF(sheen, sheenRough, sheenTint, dotNL, dotNH, dotNV); - sheenWeight = sheenAttenuation(sheen, sheenRough, sheenTint, dotNV); + #ifdef _ExtBRDF + float layerWeight; + direct = applyExtBRDFLayers(direct, albedo, f0, rough, + dotNL, dotNV, dotNH, dotVH, n, l, v, h + #ifdef _ClearCoat + , clearcoat, clearcoatRough, coatIOR, coatTint, coatN + #endif + #ifdef _Sheen + , sheen, sheenRough, sheenTint + #endif + #ifdef _Transmission + , transmission, transRough, ior, thinWall + #endif + , layerWeight); #endif - #ifdef _ClearCoat - vec3 coatContrib = clearcoatBRDF(clearcoat, clearcoatRough, coatIOR, dotNL, dotNH, dotNV, dotVH); - coatWeight = coatAttenuation(clearcoat, coatIOR, dotNV); - #endif - float layerWeight = sheenWeight * coatWeight; - direct *= layerWeight; - #ifdef _Subsurface - direct += subsurfaceBRDF(albedo, sssColor, sssRadius, subsurface, sssAnisotropy, dotNL) * layerWeight; - #endif - #ifdef _Transmission - direct += transmissionBRDF(albedo, transmission, transRough, ior, thinWall, dotNL, dotNV, dotVH) * layerWeight; - #endif - #ifdef _ClearCoat - direct *= coatTintAttenuation(clearcoat, coatTint, dotNV); - direct += coatContrib * sheenWeight; - #endif - #ifdef _Sheen - direct += sheenContrib; #endif - direct *= attenuate(distance(p, lp)); + direct *= attenuate(dist); direct *= min(lightCol, vec3(100.0)); - #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 - vec3 lightDir = l; - #ifdef _Spot - if (isSpot) - lightDir = spotDir; - #endif - direct *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, lightDir, clipmaps, gl_FragCoord.xy, velocity).r) * voxelgiShad; - #endif - #ifdef _LTC #ifdef _ShadowMap if (receiveShadow) { #ifdef _SinglePoint - vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0); + vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0); direct *= shadowTest(shadowMapSpot[0], #ifdef _ShadowMapTransparent shadowMapSpotTransparent[0], @@ -248,7 +211,29 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co ); #endif #ifdef _Clusters - vec4 lPos = LWVPSpot[index] * vec4(p + n * bias * 10, 1.0); + vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); + #ifdef _ShadowMapAtlas + tileBounds = tileBoundsSpotArray[index]; + direct *= shadowTest( + #ifdef _ShadowMapTransparent + #ifndef _SingleAtlas + shadowMapAtlasSpot, shadowMapAtlasSpotTransparent + #else + shadowMapAtlas, shadowMapAtlasTransparent + #endif + #else + #ifndef _SingleAtlas + shadowMapAtlasSpot + #else + shadowMapAtlas + #endif + #endif + , lPos.xyz / lPos.w, bias + #ifdef _ShadowMapTransparent + , transparent + #endif + ); + #else if (index == 0) direct *= shadowTest(shadowMapSpot[0], #ifdef _ShadowMapTransparent shadowMapSpotTransparent[0], @@ -286,8 +271,10 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co #endif ); #endif + #endif } #endif + l_out = l; return direct; #endif @@ -312,25 +299,26 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co #ifdef _Clusters vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); #ifdef _ShadowMapAtlas - direct *= shadowTest( - #ifdef _ShadowMapTransparent - #ifndef _SingleAtlas - shadowMapAtlasSpot, shadowMapAtlasSpotTransparent - #else - shadowMapAtlas, shadowMapAtlasTransparent - #endif - #else - #ifndef _SingleAtlas - shadowMapAtlasSpot - #else - shadowMapAtlas - #endif - #endif - , lPos.xyz / lPos.w, bias - #ifdef _ShadowMapTransparent - , transparent - #endif - ); + tileBounds = tileBoundsSpotArray[index]; + direct *= shadowTest( + #ifdef _ShadowMapTransparent + #ifndef _SingleAtlas + shadowMapAtlasSpot, shadowMapAtlasSpotTransparent + #else + shadowMapAtlas, shadowMapAtlasTransparent + #endif + #else + #ifndef _SingleAtlas + shadowMapAtlasSpot + #else + shadowMapAtlas + #endif + #endif + , lPos.xyz / lPos.w, bias + #ifdef _ShadowMapTransparent + , transparent + #endif + ); #else if (index == 0) direct *= shadowTest(shadowMapSpot[0], #ifdef _ShadowMapTransparent @@ -372,6 +360,7 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co #endif } #endif + l_out = l; return direct; } #endif @@ -458,9 +447,157 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co } #endif + l_out = l; return direct; } +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 + #ifdef _ShadowMapTransparent + , bool transparent + #endif + #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], vec2 velocity + #endif + #ifdef _MicroShadowing + , float occ + #endif + #ifdef _SSRS + , sampler2D gbufferD, mat4 invVP, vec3 eye + #endif + #ifdef _ClearCoat + , float clearcoat, float clearcoatRough, float coatIOR, vec3 coatTint, vec3 coatN + #endif + #ifdef _Sheen + , float sheen, float sheenRough, vec3 sheenTint + #endif + #ifdef _Anisotropy + , float anisotropy, float anisoRot, vec3 tangent + #endif + #ifdef _SSS + , float subsurface, vec3 sssColor, vec3 sssRadius, float sssAnisotropy + #endif + #ifdef _Transmission + , float transmission, float transRough, float ior, float thinWall + #endif +) { + vec3 l; + vec3 direct = sampleLightCore(p, n, v, dotNV, lp, lightCol, albedo, rough, spec, f0 + #ifdef _ShadowMap + , index, bias, receiveShadow + #ifdef _ShadowMapTransparent + , transparent + #endif + #endif + #ifdef _Spot + , isSpot, spotSize, spotBlend, spotDir, scale, right + #endif + #ifdef _ClearCoat + , clearcoat, clearcoatRough, coatIOR, coatTint, coatN + #endif + #ifdef _Sheen + , sheen, sheenRough, sheenTint + #endif + #ifdef _Anisotropy + , anisotropy, anisoRot, tangent + #endif + #ifdef _SSS + , subsurface, sssColor, sssRadius, sssAnisotropy + #endif + #ifdef _Transmission + , transmission, transRough, ior, thinWall + #endif + , l); + + float dotNL = max(0.0, dot(n, l)); + + #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 + vec3 lightDir = l; + #ifdef _Spot + if (isSpot) + lightDir = spotDir; + #endif + direct *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, lightDir, clipmaps, gl_FragCoord.xy, velocity).r) * voxelgiShad; + #endif + + return direct; +} + +// Backward-compatible overload for generated shaders that don't pass extended BRDF params +#ifdef _ExtBRDF +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 + #ifdef _ShadowMapTransparent + , bool transparent + #endif + #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], vec2 velocity + #endif + #ifdef _MicroShadowing + , float occ + #endif + #ifdef _SSRS + , sampler2D gbufferD, mat4 invVP, vec3 eye + #endif +) { + return sampleLight(p, n, v, dotNV, lp, lightCol, albedo, rough, spec, f0 + #ifdef _ShadowMap + , index, bias, receiveShadow + #ifdef _ShadowMapTransparent + , transparent + #endif + #endif + #ifdef _Spot + , isSpot, spotSize, spotBlend, spotDir, scale, right + #endif + #ifdef _VoxelShadow + , voxels, voxelsSDF, clipmaps, velocity + #endif + #ifdef _MicroShadowing + , occ + #endif + #ifdef _SSRS + , gbufferD, invVP, eye + #endif + #ifdef _ClearCoat + , 0.0, 0.0, 1.5, vec3(1.0), n + #endif + #ifdef _Sheen + , 0.0, 0.0, vec3(1.0) + #endif + #ifdef _Anisotropy + , 0.0, 0.0, vec3(0.0) + #endif + #ifdef _SSS + , 0.0, vec3(0.0), vec3(0.0), 0.0 + #endif + #ifdef _Transmission + , 0.0, 0.0, 1.45, 1.0 + #endif + ); +} +#endif // _ExtBRDF + #ifdef _VoxelGI vec3 sampleLightVoxels(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 @@ -474,7 +611,7 @@ vec3 sampleLightVoxels(const vec3 p, const vec3 n, const vec3 v, const float dot , const bool isSpot, const float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right #endif #ifdef _ClearCoat - , float clearcoat, float clearcoatRough, float coatIOR, vec3 coatTint + , float clearcoat, float clearcoatRough, float coatIOR, vec3 coatTint, vec3 coatN #endif #ifdef _Sheen , float sheen, float sheenRough, vec3 sheenTint @@ -482,295 +619,85 @@ vec3 sampleLightVoxels(const vec3 p, const vec3 n, const vec3 v, const float dot #ifdef _Anisotropy , float anisotropy, float anisoRot, vec3 tangent #endif - #ifdef _Subsurface + #ifdef _SSS , float subsurface, vec3 sssColor, vec3 sssRadius, float sssAnisotropy #endif #ifdef _Transmission , float transmission, float transRough, float ior, float thinWall #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 - - float sheenWeight = 1.0; - float coatWeight = 1.0; - #ifdef _Sheen - vec3 sheenContrib = sheenBRDF(sheen, sheenRough, sheenTint, dotNL, dotNH, dotNV); - sheenWeight = sheenAttenuation(sheen, sheenRough, sheenTint, dotNV); - #endif - #ifdef _ClearCoat - vec3 coatContrib = clearcoatBRDF(clearcoat, clearcoatRough, coatIOR, dotNL, dotNH, dotNV, dotVH); - coatWeight = coatAttenuation(clearcoat, coatIOR, dotNV); - #endif - float layerWeight = sheenWeight * coatWeight; - direct *= layerWeight; - #ifdef _Subsurface - direct += subsurfaceBRDF(albedo, sssColor, sssRadius, subsurface, sssAnisotropy, dotNL) * layerWeight; - #endif - #ifdef _Transmission - direct += transmissionBRDF(albedo, transmission, transRough, ior, thinWall, dotNL, dotNV, dotVH) * layerWeight; - #endif - #ifdef _ClearCoat - direct *= coatTintAttenuation(clearcoat, coatTint, dotNV); - direct += coatContrib * sheenWeight; - #endif - #ifdef _Sheen - direct += sheenContrib; - #endif - - direct *= attenuate(distance(p, lp)); - // CRITICAL: Clamp light color to prevent extreme HDR values causing white sphere artifacts - direct *= min(lightCol, vec3(100.0)); - - #ifdef _LTC - #ifdef _ShadowMap - if (receiveShadow) { - #ifdef _SinglePoint - vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0); - direct *= shadowTest(shadowMapSpot[0], - #ifdef _ShadowMapTransparent - shadowMapSpotTransparent[0], - #endif - lPos.xyz / lPos.w, bias - #ifdef _ShadowMapTransparent - , transparent - #endif - ); - #endif - #ifdef _Clusters - vec4 lPos = LWVPSpot[index] * vec4(p + n * bias * 10, 1.0); - if (index == 0) direct *= shadowTest(shadowMapSpot[0], - #ifdef _ShadowMapTransparent - shadowMapSpotTransparent[0], - #endif - lPos.xyz / lPos.w, bias - #ifdef _ShadowMapTransparent - , transparent - #endif - ); - else if (index == 1) direct *= shadowTest(shadowMapSpot[1], - #ifdef _ShadowMapTransparent - shadowMapSpotTransparent[1], - #endif - lPos.xyz / lPos.w, bias - #ifdef _ShadowMapTransparent - , transparent - #endif - ); - else if (index == 2) direct *= shadowTest(shadowMapSpot[2], - #ifdef _ShadowMapTransparent - shadowMapSpotTransparent[2], - #endif - lPos.xyz / lPos.w, bias - #ifdef _ShadowMapTransparent - , transparent - #endif - ); - else if (index == 3) direct *= shadowTest(shadowMapSpot[3], - #ifdef _ShadowMapTransparent - shadowMapSpotTransparent[3], - #endif - lPos.xyz / lPos.w, bias - #ifdef _ShadowMapTransparent - , transparent - #endif - ); - #endif - } - #endif - return direct; - #endif - - #ifdef _Spot - if (isSpot) { - direct *= spotlightMask(l, spotDir, right, scale, spotSize, spotBlend); - +) { + vec3 l; + return sampleLightCore(p, n, v, dotNV, lp, lightCol, albedo, rough, spec, f0 #ifdef _ShadowMap - if (receiveShadow) { - #ifdef _SinglePoint - vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0); - direct *= shadowTest(shadowMapSpot[0], - #ifdef _ShadowMapTransparent - shadowMapSpotTransparent[0], - #endif - lPos.xyz / lPos.w, bias - #ifdef _ShadowMapTransparent - , transparent - #endif - ); - #endif - #ifdef _Clusters - vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); - #ifdef _ShadowMapAtlas - direct *= shadowTest( - #ifdef _ShadowMapTransparent - #ifndef _SingleAtlas - shadowMapAtlasSpot, shadowMapAtlasSpotTransparent - #else - shadowMapAtlas, shadowMapAtlasTransparent - #endif - #else - #ifndef _SingleAtlas - shadowMapAtlasSpot - #else - shadowMapAtlas - #endif - #endif - , lPos.xyz / lPos.w, bias - #ifdef _ShadowMapTransparent - , transparent - #endif - ); - #else - if (index == 0) direct *= shadowTest(shadowMapSpot[0], - #ifdef _ShadowMapTransparent - shadowMapSpotTransparent[0], - #endif - lPos.xyz / lPos.w, bias - #ifdef _ShadowMapTransparent - , transparent - #endif - ); - else if (index == 1) direct *= shadowTest(shadowMapSpot[1], - #ifdef _ShadowMapTransparent - shadowMapSpotTransparent[1], - #endif - lPos.xyz / lPos.w, bias - #ifdef _ShadowMapTransparent - , transparent - #endif - ); - else if (index == 2) direct *= shadowTest(shadowMapSpot[2], - #ifdef _ShadowMapTransparent - shadowMapSpotTransparent[2], - #endif - lPos.xyz / lPos.w, bias - #ifdef _ShadowMapTransparent - , transparent - #endif - ); - else if (index == 3) direct *= shadowTest(shadowMapSpot[3], - #ifdef _ShadowMapTransparent - shadowMapSpotTransparent[3], - #endif - lPos.xyz / lPos.w, bias - #ifdef _ShadowMapTransparent - , transparent - #endif - ); - #endif - #endif - } + , index, bias, receiveShadow + #ifdef _ShadowMapTransparent + , transparent #endif - return direct; - } - #endif - - #ifdef _LightIES - direct *= iesAttenuation(-l); - #endif - - #ifdef _ShadowMap - if (receiveShadow) { - #ifdef _SinglePoint - #ifndef _Spot - direct *= PCFCube(shadowMapPoint[0], - #ifdef _ShadowMapTransparent - shadowMapPointTransparent[0], - #endif - ld, -l, bias, lightProj, n - #ifdef _ShadowMapTransparent - , transparent - #endif - ); - #endif - #endif - #ifdef _Clusters - #ifdef _ShadowMapAtlas - direct *= PCFFakeCube( - #ifdef _ShadowMapTransparent - #ifndef _SingleAtlas - shadowMapAtlasPoint, shadowMapAtlasPointTransparent - #else - shadowMapAtlas, shadowMapAtlasTransparent - #endif - #else - #ifndef _SingleAtlas - shadowMapAtlasPoint - #else - shadowMapAtlas - #endif - #endif - , ld, -l, bias, lightProj, n, index - #ifdef _ShadowMapTransparent - , transparent - #endif - ); - #else - if (index == 0) direct *= PCFCube(shadowMapPoint[0], - #ifdef _ShadowMapTransparent - shadowMapPointTransparent[0], - #endif - ld, -l, bias, lightProj, n - #ifdef _ShadowMapTransparent - , transparent - #endif - ); - else if (index == 1) direct *= PCFCube(shadowMapPoint[1], - #ifdef _ShadowMapTransparent - shadowMapPointTransparent[1], - #endif - ld, -l, bias, lightProj, n - #ifdef _ShadowMapTransparent - , transparent - #endif - ); - else if (index == 2) direct *= PCFCube(shadowMapPoint[2], - #ifdef _ShadowMapTransparent - shadowMapPointTransparent[2], - #endif - ld, -l, bias, lightProj, n - #ifdef _ShadowMapTransparent - , transparent - #endif - ); - else if (index == 3) direct *= PCFCube(shadowMapPoint[3], - #ifdef _ShadowMapTransparent - shadowMapPointTransparent[3], - #endif - ld, -l, bias, lightProj, n - #ifdef _ShadowMapTransparent - , transparent - #endif - ); - #endif - #endif - } - #endif - - return direct; + #endif + #ifdef _Spot + , isSpot, spotSize, spotBlend, spotDir, scale, right + #endif + #ifdef _ClearCoat + , clearcoat, clearcoatRough, coatIOR, coatTint, coatN + #endif + #ifdef _Sheen + , sheen, sheenRough, sheenTint + #endif + #ifdef _Anisotropy + , anisotropy, anisoRot, tangent + #endif + #ifdef _SSS + , subsurface, sssColor, sssRadius, sssAnisotropy + #endif + #ifdef _Transmission + , transmission, transRough, ior, thinWall + #endif + , l); } + +// Backward-compatible overload for generated shaders that don't pass extended BRDF params +#ifdef _ExtBRDF +vec3 sampleLightVoxels(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 + #ifdef _ShadowMapTransparent + , bool transparent + #endif + #endif + #ifdef _Spot + , const bool isSpot, const float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right + #endif +) { + vec3 l; + return sampleLightCore(p, n, v, dotNV, lp, lightCol, albedo, rough, spec, f0 + #ifdef _ShadowMap + , index, bias, receiveShadow + #ifdef _ShadowMapTransparent + , transparent + #endif + #endif + #ifdef _Spot + , isSpot, spotSize, spotBlend, spotDir, scale, right + #endif + #ifdef _ClearCoat + , 0.0, 0.0, 1.5, vec3(1.0), n + #endif + #ifdef _Sheen + , 0.0, 0.0, vec3(1.0) + #endif + #ifdef _Anisotropy + , 0.0, 0.0, vec3(0.0) + #endif + #ifdef _SSS + , 0.0, vec3(0.0), vec3(0.0), 0.0 + #endif + #ifdef _Transmission + , 0.0, 0.0, 1.45, 1.0 + #endif + , l); +} +#endif // _ExtBRDF + #endif #endif diff --git a/leenkx/Shaders/std/light_mobile.glsl b/leenkx/Shaders/std/light_mobile.glsl index 2b8f828c..851c0505 100644 --- a/leenkx/Shaders/std/light_mobile.glsl +++ b/leenkx/Shaders/std/light_mobile.glsl @@ -14,7 +14,7 @@ #ifdef _SinglePoint #ifdef _Spot uniform sampler2DShadow shadowMapSpot[1]; - uniform mat4 LWVPSpot[1]; + uniform mat4 LWVPSpotArray[1]; #else uniform samplerCubeShadow shadowMapPoint[1]; uniform vec2 lightProj; @@ -24,7 +24,9 @@ #ifdef _SingleAtlas //!uniform sampler2DShadow shadowMapAtlas; #endif + #ifndef _SinglePoint uniform vec2 lightProj; + #endif #ifdef _ShadowMapAtlas #ifndef _SingleAtlas uniform sampler2DShadow shadowMapAtlasPoint; @@ -54,7 +56,7 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co , bool isSpot, float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right #endif #ifdef _ClearCoat - , float clearcoat, float clearcoatRough, float coatIOR, vec3 coatTint + , float clearcoat, float clearcoatRough, float coatIOR, vec3 coatTint, vec3 coatN #endif #ifdef _Sheen , float sheen, float sheenRough, vec3 sheenTint @@ -62,7 +64,7 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co #ifdef _Anisotropy , float anisotropy, float anisoRot, vec3 tangent #endif - #ifdef _Subsurface + #ifdef _SSS , float subsurface, vec3 sssColor, vec3 sssRadius, float sssAnisotropy #endif #ifdef _Transmission @@ -70,7 +72,8 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co #endif ) { vec3 ld = lp - p; - vec3 l = normalize(ld); + float dist = length(ld); + vec3 l = ld / dist; vec3 h = normalize(v + l); float dotNH = max(0.0, dot(n, h)); float dotVH = max(0.0, dot(v, h)); @@ -78,7 +81,7 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co #ifdef _Anisotropy vec3 direct; - if (abs(anisotropy) > 0.001) { + if (abs(anisotropy) > 0.001 && dot(tangent, tangent) > 0.001) { vec3 bitangent = normalize(cross(n, tangent)); direct = lambertDiffuseBRDF(albedo, dotNL) + anisotropicBRDF(f0, rough, anisotropy, anisoRot, @@ -92,34 +95,24 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec; #endif - float sheenWeight = 1.0; - float coatWeight = 1.0; - #ifdef _Sheen - vec3 sheenContrib = sheenBRDF(sheen, sheenRough, sheenTint, dotNL, dotNH, dotNV); - sheenWeight = sheenAttenuation(sheen, sheenRough, sheenTint, dotNV); - #endif - #ifdef _ClearCoat - vec3 coatContrib = clearcoatBRDF(clearcoat, clearcoatRough, coatIOR, dotNL, dotNH, dotNV, dotVH); - coatWeight = coatAttenuation(clearcoat, coatIOR, dotNV); - #endif - float layerWeight = sheenWeight * coatWeight; - direct *= layerWeight; - #ifdef _Subsurface - direct += subsurfaceBRDF(albedo, sssColor, sssRadius, subsurface, sssAnisotropy, dotNL) * layerWeight; - #endif - #ifdef _Transmission - direct += transmissionBRDF(albedo, transmission, transRough, ior, thinWall, dotNL, dotNV, dotVH) * layerWeight; - #endif - #ifdef _ClearCoat - direct *= coatTintAttenuation(clearcoat, coatTint, dotNV); - direct += coatContrib * sheenWeight; - #endif - #ifdef _Sheen - direct += sheenContrib; + #ifdef _ExtBRDF + float layerWeight; + direct = applyExtBRDFLayers(direct, albedo, f0, rough, dotNL, dotNV, dotNH, dotVH, n, l, v, h + #ifdef _ClearCoat + , clearcoat, clearcoatRough, coatIOR, coatTint, coatN + #endif + #ifdef _Sheen + , sheen, sheenRough, sheenTint + #endif + #ifdef _Transmission + , transmission, transRough, ior, thinWall + #endif + , layerWeight + ); #endif direct *= lightCol; - direct *= attenuate(distance(p, lp)); + direct *= attenuate(dist); #ifdef _Spot if (isSpot) { @@ -128,12 +121,13 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co #ifdef _ShadowMap if (receiveShadow) { #ifdef _SinglePoint - vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0); + vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0); direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias); #endif #ifdef _Clusters vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); #ifdef _ShadowMapAtlas + tileBounds = tileBoundsSpotArray[index]; direct *= shadowTest( #ifndef _SingleAtlas shadowMapAtlasSpot @@ -186,4 +180,41 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co return direct; } +// Backward-compatible overload for generated shaders that don't pass extended BRDF params +#ifdef _ExtBRDF +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 + #endif + #ifdef _Spot + , bool isSpot, float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right + #endif +) { + return sampleLight(p, n, v, dotNV, lp, lightCol, albedo, rough, spec, f0 + #ifdef _ShadowMap + , index, bias, receiveShadow + #endif + #ifdef _Spot + , isSpot, spotSize, spotBlend, spotDir, scale, right + #endif + #ifdef _ClearCoat + , 0.0, 0.0, 1.5, vec3(1.0), n + #endif + #ifdef _Sheen + , 0.0, 0.0, vec3(1.0) + #endif + #ifdef _Anisotropy + , 0.0, 0.0, vec3(0.0) + #endif + #ifdef _SSS + , 0.0, vec3(0.0), vec3(0.0), 0.0 + #endif + #ifdef _Transmission + , 0.0, 0.0, 1.45, 1.0 + #endif + ); +} +#endif // _ExtBRDF + #endif diff --git a/leenkx/Shaders/std/math.glsl b/leenkx/Shaders/std/math.glsl index a4a64138..3f0587f0 100644 --- a/leenkx/Shaders/std/math.glsl +++ b/leenkx/Shaders/std/math.glsl @@ -8,8 +8,12 @@ float hash(const vec2 p) { } vec2 envMapEquirect(const vec3 normal) { - const float PI = 3.1415926535; - const float PI2 = PI * 2.0; + #ifndef PI + #define PI 3.1415926535 + #endif + #ifndef PI2 + #define PI2 6.2831853071 + #endif float phi = acos(normal.z); float theta = atan(-normal.y, normal.x) + PI; return vec2(theta / PI2, phi / PI); diff --git a/leenkx/Shaders/std/shadows.glsl b/leenkx/Shaders/std/shadows.glsl index b186309c..ff02a734 100644 --- a/leenkx/Shaders/std/shadows.glsl +++ b/leenkx/Shaders/std/shadows.glsl @@ -22,6 +22,14 @@ uniform vec2 smSizeUniform; #endif #endif +#ifdef _ShadowMapAtlas +uniform vec4 tileBoundsSunArray[maxLights * shadowmapCascades]; +#if defined(_Clusters) && defined(_Spot) && defined(_ShadowMap) +uniform vec4 tileBoundsSpotArray[maxLightsCluster]; +#endif +vec4 tileBounds = vec4(0.0, 0.0, 1.0, 1.0); +#endif + #ifdef _ShadowMapAtlas // PCF that clamps samples to tile boundaries to prevent bleeding vec3 PCFTileAware(sampler2DShadow shadowMap, @@ -291,13 +299,13 @@ vec3 PCFFakeCube(sampler2DShadow shadowMap, , const bool transparent #endif ) { - const vec2 smSize = smSizeUniform; // TODO: incorrect... const float compare = lpToDepth(lp, lightProj) - bias * 1.5; ml = ml + n * bias * 20; int faceIndex = 0; const int lightIndex = index * 6; const vec2 uv = sampleCube(ml, faceIndex); vec4 pointLightTile = pointLightDataArray[lightIndex + faceIndex]; // x: tile X offset, y: tile Y offset, z: tile size relative to atlas + const vec2 smSize = smSizeUniform; // TODO: incorrect... vec2 uvtiled = pointLightTile.z * uv + pointLightTile.xy; #ifdef _FlipY uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system @@ -387,10 +395,6 @@ vec3 PCFFakeCube(sampler2DShadow shadowMap, } #endif -#ifdef _ShadowMapAtlas -uniform vec4 tileBounds; -#endif - vec3 shadowTest(sampler2DShadow shadowMap, #ifdef _ShadowMapTransparent sampler2D shadowMapTransparent, @@ -405,9 +409,9 @@ vec3 shadowTest(sampler2DShadow shadowMap, #ifdef _ShadowMapAtlas // use tile PCF #ifdef _SMSizeUniform - vec2 smSizeAtlas = smSizeUniform; + vec2 smSizeAtlas = smSizeUniform * (tileBounds.zw - tileBounds.xy); #else - const vec2 smSizeAtlas = shadowmapSize; + vec2 smSizeAtlas = shadowmapSize * (tileBounds.zw - tileBounds.xy); #endif return PCFTileAware(shadowMap, #ifdef _ShadowMapTransparent @@ -455,7 +459,7 @@ mat4 getCascadeMat(const float d, out int casi, out int casIndex) { float(d > casData[c * 4].y), float(d > casData[c * 4].z), float(d > casData[c * 4].w)); - casi = int(min(dot(ci, comp), c)); + casi = int(min(dot(ci, comp), float(c - 1))); // Get cascade mat casIndex = casi * 4; return mat4( @@ -479,8 +483,12 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap, #ifdef _SMSizeUniform vec2 smSize = smSizeUniform; #else + #ifdef _ShadowMapAtlas + vec2 smSize = shadowmapSize * (tileBoundsSunArray[0].zw - tileBoundsSunArray[0].xy); + #else const vec2 smSize = shadowmapSize * vec2(shadowmapCascades, 1.0); #endif + #endif const int c = shadowmapCascades; float d = distance(eye, p); int casi; @@ -489,16 +497,35 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap, vec4 lPos = LWVP * vec4(p, 1.0); lPos.xyz /= lPos.w; + #ifdef _ShadowMapAtlas + tileBounds = tileBoundsSunArray[casi]; + #endif + vec3 visibility = vec3(1.0); - if (lPos.w > 0.0) visibility = PCF(shadowMap, - #ifdef _ShadowMapTransparent - shadowMapTransparent, - #endif - lPos.xy, lPos.z - shadowsBias, smSize - #ifdef _ShadowMapTransparent - , transparent - #endif - ); + if (lPos.w > 0.0) { + #ifdef _ShadowMapAtlas + visibility = PCFTileAware(shadowMap, + #ifdef _ShadowMapTransparent + shadowMapTransparent, + #endif + lPos.xy, lPos.z - shadowsBias, smSize, + tileBounds.xy, tileBounds.zw + #ifdef _ShadowMapTransparent + , transparent + #endif + ); + #else + visibility = PCF(shadowMap, + #ifdef _ShadowMapTransparent + shadowMapTransparent, + #endif + lPos.xy, lPos.z - shadowsBias, smSize + #ifdef _ShadowMapTransparent + , transparent + #endif + ); + #endif + } // Blend cascade // https://github.com/TheRealMJP/Shadows @@ -518,15 +545,33 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap, lPos2.xyz /= lPos2.w; vec3 visibility2 = vec3(1.0); // use lPos2 coordinates for second cascade, not lPos - if (lPos2.w > 0.0) visibility2 = PCF(shadowMap, - #ifdef _ShadowMapTransparent - shadowMapTransparent, - #endif - lPos2.xy, lPos2.z - shadowsBias, smSize - #ifdef _ShadowMapTransparent - , transparent - #endif - ); + #ifdef _ShadowMapAtlas + tileBounds = tileBoundsSunArray[casi + 1]; + #endif + if (lPos2.w > 0.0) { + #ifdef _ShadowMapAtlas + visibility2 = PCFTileAware(shadowMap, + #ifdef _ShadowMapTransparent + shadowMapTransparent, + #endif + lPos2.xy, lPos2.z - shadowsBias, smSize, + tileBounds.xy, tileBounds.zw + #ifdef _ShadowMapTransparent + , transparent + #endif + ); + #else + visibility2 = PCF(shadowMap, + #ifdef _ShadowMapTransparent + shadowMapTransparent, + #endif + lPos2.xy, lPos2.z - shadowsBias, smSize + #ifdef _ShadowMapTransparent + , transparent + #endif + ); + #endif + } float lerpAmt = smoothstep(0.0, blendThres, splitDist); return mix(visibility2, visibility, lerpAmt); diff --git a/leenkx/Shaders/std/sky.glsl b/leenkx/Shaders/std/sky.glsl index 5243b3a3..f4540a45 100644 --- a/leenkx/Shaders/std/sky.glsl +++ b/leenkx/Shaders/std/sky.glsl @@ -26,7 +26,7 @@ uniform sampler2D singleScatterLUT; uniform vec2 skyDensity; #ifndef PI - #define PI 3.141592 + #define PI 3.1415926535 #endif #ifndef HALF_PI #define HALF_PI 1.570796 diff --git a/leenkx/Shaders/std/sss.glsl b/leenkx/Shaders/std/sss.glsl index 7af08789..ef30a63c 100644 --- a/leenkx/Shaders/std/sss.glsl +++ b/leenkx/Shaders/std/sss.glsl @@ -1,15 +1,25 @@ // Separable SSS Transmittance Function, ref to sss_pass -vec3 SSSSTransmittance(mat4 LWVP, vec3 p, vec3 n, vec3 l, float lightFar, sampler2DShadow shadowMap) { - const float translucency = 1.0; +vec3 SSSSTransmittance(mat4 LWVP, vec3 p, vec3 n, vec3 l, float lightFar, sampler2DShadow shadowMap, vec3 sssColor, float sssRadius + #ifdef _ShadowMapAtlas + , vec4 tileBounds + #endif + ) { + const float translucency = 0.85; vec4 shrinkedPos = vec4(p - 0.005 * n, 1.0); vec4 shadowPos = LWVP * shrinkedPos; - float scale = 8.25 * (1.0 - translucency) / (sssWidth / 10.0); - float d1 = texture(shadowMap, vec3(shadowPos.xy / shadowPos.w, shadowPos.z)).r; // 'd1' has a range of 0..1 - float d2 = shadowPos.z; // 'd2' has a range of 0..'lightFarPlane' - d1 *= lightFar; // So we scale 'd1' accordingly: - float d = scale * abs(d1 - d2); + vec2 shadowUV = shadowPos.xy / shadowPos.w; + #ifdef _ShadowMapAtlas + shadowUV = clamp(shadowUV, tileBounds.xy, tileBounds.zw); + #endif + float scale = 2.5 * (1.0 - translucency) / max(sssRadius, 0.001); + float d1 = texture(shadowMap, vec3(shadowUV, shadowPos.z)).r; + float d2 = shadowPos.z; + d1 *= lightFar; + d2 *= lightFar; + float d = scale * abs(d1 - d2) * 1000.0; + if (d > 10.0) return vec3(0.0); float dd = -d * d; vec3 profile = vec3(0.233, 0.455, 0.649) * exp(dd / 0.0064) + vec3(0.1, 0.336, 0.344) * exp(dd / 0.0484) + @@ -17,10 +27,70 @@ vec3 SSSSTransmittance(mat4 LWVP, vec3 p, vec3 n, vec3 l, float lightFar, sample vec3(0.113, 0.007, 0.007) * exp(dd / 0.567) + vec3(0.358, 0.004, 0.0) * exp(dd / 1.99) + vec3(0.078, 0.0, 0.0) * exp(dd / 7.41); - return profile * clamp(0.3 + dot(l, -n), 0.0, 1.0); + profile *= mix(vec3(1.0), sssColor, 0.8); + return profile * clamp(0.5 + dot(l, -n), 0.0, 1.0); } -vec3 SSSSTransmittanceCube(float translucency, vec4 shadowPos, vec3 n, vec3 l, float lightFar) { - // TODO - return vec3(0.0); +#ifdef _ShadowMapAtlas +vec3 SSSSTransmittanceCubeAtlas(sampler2DShadow shadowMap, vec3 lightPos, vec3 p, vec3 n, vec3 l, float lightFar, vec2 lightProj, int index, vec3 sssColor, float sssRadius) { + const float translucency = 0.85; + vec3 shrinkedPos = p - 0.005 * n; + vec3 ld = normalize(shrinkedPos - lightPos); + #ifdef _InvY + ld.y = -ld.y; + #endif + float d2 = lpToDepth(ld, lightProj); + int faceIndex = 0; + int lightIndex = index * 6; + vec2 uv = sampleCube(ld, faceIndex); + vec4 pointLightTile = pointLightDataArray[lightIndex + faceIndex]; + vec2 uvtiled = pointLightTile.z * uv + pointLightTile.xy; + #ifdef _FlipY + uvtiled.y = 1.0 - uvtiled.y; + #endif + float d1 = texture(shadowMap, vec3(uvtiled, d2)).r; + d1 *= lightFar; + d2 *= lightFar; + float scale = 2.5 * (1.0 - translucency) / max(sssRadius, 0.001); + // d1/d2 are in meters, sssRadius is in mm, exponential constants are in mm^2 + float d = scale * abs(d1 - d2) * 1000.0; // Convert distance to mm + + if (d > 10.0) return vec3(0.0); + float dd = -d * d; + vec3 profile = vec3(0.233, 0.455, 0.649) * exp(dd / 0.0064) + + vec3(0.1, 0.336, 0.344) * exp(dd / 0.0484) + + vec3(0.118, 0.198, 0.0) * exp(dd / 0.187) + + vec3(0.113, 0.007, 0.007) * exp(dd / 0.567) + + vec3(0.358, 0.004, 0.0) * exp(dd / 1.99) + + vec3(0.078, 0.0, 0.0) * exp(dd / 7.41); + profile *= mix(vec3(1.0), sssColor, 0.8); + return profile * clamp(0.5 + dot(l, -n), 0.0, 1.0); +} +#endif + +vec3 SSSSTransmittanceCube(samplerCubeShadow shadowMapCube, vec3 lightPos, vec3 p, vec3 n, vec3 l, float lightFar, vec2 lightProj, vec3 sssColor, float sssRadius) { + const float translucency = 0.85; + vec3 shrinkedPos = p - 0.005 * n; + vec3 ld = normalize(shrinkedPos - lightPos); + #ifdef _InvY + ld.y = -ld.y; + #endif + float d2 = lpToDepth(ld, lightProj); + float d1 = texture(shadowMapCube, vec4(ld, d2)).r; + d1 *= lightFar; + d2 *= lightFar; + float scale = 2.5 * (1.0 - translucency) / max(sssRadius, 0.001); + // d1/d2 are in meters, sssRadius is in mm, exponential constants are in mm^2 + float d = scale * abs(d1 - d2) * 1000.0; // Convert distance to mm + + if (d > 10.0) return vec3(0.0); + float dd = -d * d; + vec3 profile = vec3(0.233, 0.455, 0.649) * exp(dd / 0.0064) + + vec3(0.1, 0.336, 0.344) * exp(dd / 0.0484) + + vec3(0.118, 0.198, 0.0) * exp(dd / 0.187) + + vec3(0.113, 0.007, 0.007) * exp(dd / 0.567) + + vec3(0.358, 0.004, 0.0) * exp(dd / 1.99) + + vec3(0.078, 0.0, 0.0) * exp(dd / 7.41); + profile *= mix(vec3(1.0), sssColor, 0.8); + return profile * clamp(0.5 + dot(l, -n), 0.0, 1.0); } diff --git a/leenkx/Shaders/voxel_offsetprev/voxel_offsetprev.comp.glsl b/leenkx/Shaders/voxel_offsetprev/voxel_offsetprev.comp.glsl index 3b5fe1cc..19784977 100644 --- a/leenkx/Shaders/voxel_offsetprev/voxel_offsetprev.comp.glsl +++ b/leenkx/Shaders/voxel_offsetprev/voxel_offsetprev.comp.glsl @@ -38,7 +38,6 @@ uniform layout(r8) image3D voxelsOut; #endif uniform int clipmapLevel; -uniform float voxelBlend; uniform float clipmaps[voxelgiClipmapCount * 10]; @@ -47,7 +46,7 @@ void main() { ivec3 src = ivec3(gl_GlobalInvocationID.xyz); src.y += clipmapLevel * res; - for (int i = 0; i < 6 + DIFFUSE_CONE_COUNT; i++) + for (int i = 0; i < 6 + diffuseConeCount; i++) { vec4 col = vec4(0.0); diff --git a/leenkx/Shaders/voxel_resolve_ao/voxel_resolve_ao.comp.glsl b/leenkx/Shaders/voxel_resolve_ao/voxel_resolve_ao.comp.glsl index 7123f271..cae825cb 100644 --- a/leenkx/Shaders/voxel_resolve_ao/voxel_resolve_ao.comp.glsl +++ b/leenkx/Shaders/voxel_resolve_ao/voxel_resolve_ao.comp.glsl @@ -165,7 +165,7 @@ void main() { #endif #endif - envl.rgb *= envmapStrength * occspec.x; + envl.rgb *= envmapStrength * voxelgiEnv * occspec.x; vec3 occ = envl * (1.0 - traceAO(P, n, voxels, clipmaps)); diff --git a/leenkx/Shaders/voxel_resolve_diffuse/voxel_resolve_diffuse.comp.glsl b/leenkx/Shaders/voxel_resolve_diffuse/voxel_resolve_diffuse.comp.glsl index a605d793..76121726 100644 --- a/leenkx/Shaders/voxel_resolve_diffuse/voxel_resolve_diffuse.comp.glsl +++ b/leenkx/Shaders/voxel_resolve_diffuse/voxel_resolve_diffuse.comp.glsl @@ -53,13 +53,6 @@ uniform float shirr[7 * 4]; #ifdef _Brdf uniform sampler2D senvmapBrdf; #endif -#ifdef _Rad -uniform sampler2D senvmapRadiance; -uniform int envmapNumMipmaps; -#endif -#ifdef _EnvCol -uniform vec3 backgroundCol; -#endif void main() { const vec2 pixel = gl_GlobalInvocationID.xy; @@ -140,17 +133,8 @@ void main() { vec3 envl = vec3(0.0); #endif -#ifdef _Rad - vec3 reflectionWorld = reflect(-v, n); - float lod = getMipFromRoughness(roughness, envmapNumMipmaps); - vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb; -#endif - #ifdef _EnvLDR envl.rgb = pow(envl.rgb, vec3(2.2)); - #ifdef _Rad - prefilteredColor = pow(prefilteredColor, vec3(2.2)); - #endif #endif envl.rgb *= albedo; @@ -159,15 +143,7 @@ void main() { envl.rgb *= 1.0 - F; //LV: We should take refracted light into account #endif -#ifdef _Rad // Indirect specular - envl.rgb += prefilteredColor * F; //LV: Removed "1.5 * occspec.y". Specular should be weighted only by FV LUT -#else - #ifdef _EnvCol - envl.rgb += backgroundCol * F; //LV: Eh, what's the point of weighting it only by F0? - #endif -#endif - - envl.rgb *= envmapStrength * occspec.x; + envl.rgb *= envmapStrength * voxelgiEnv * occspec.x; vec4 trace = traceDiffuse(P, n, voxels, clipmaps); vec3 color = trace.rgb * albedo * (1.0 - F); diff --git a/leenkx/Shaders/voxel_resolve_refraction/voxel_resolve_refraction.comp.glsl b/leenkx/Shaders/voxel_resolve_refraction/voxel_resolve_refraction.comp.glsl index c74ebaeb..a65f8085 100644 --- a/leenkx/Shaders/voxel_resolve_refraction/voxel_resolve_refraction.comp.glsl +++ b/leenkx/Shaders/voxel_resolve_refraction/voxel_resolve_refraction.comp.glsl @@ -48,13 +48,15 @@ void main() { const vec2 pixel = gl_GlobalInvocationID.xy; vec2 uv = (pixel + 0.5) / postprocess_resolution; #ifdef _InvY - uv.y = 1.0 - uv.y + uv.y = 1.0 - uv.y; #endif float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0; if (depth == 0) return; vec2 ior_opac = textureLod(gbuffer_refraction, uv, 0.0).xy; + float ior = unpackIOR(ior_opac.x); + float opacity = ior_opac.y; float x = uv.x * 2 - 1; float y = uv.y * 2 - 1; @@ -72,8 +74,8 @@ void main() { n = normalize(n); vec3 color = vec3(0.0); - if(ior_opac.y < 1.0) - color = traceRefraction(P, n, voxels, voxelsSDF, normalize(eye - P), ior_opac.x, g0.b, clipmaps, pixel).rgb; + if(opacity < 1.0) + color = traceRefraction(P, n, voxels, voxelsSDF, normalize(eye - P), ior, g0.b, clipmaps, pixel).rgb; imageStore(voxels_refraction, ivec2(pixel), vec4(color, 1.0)); } diff --git a/leenkx/Shaders/voxel_resolve_specular/voxel_resolve_specular.comp.glsl b/leenkx/Shaders/voxel_resolve_specular/voxel_resolve_specular.comp.glsl index 8cad6a15..f121a111 100644 --- a/leenkx/Shaders/voxel_resolve_specular/voxel_resolve_specular.comp.glsl +++ b/leenkx/Shaders/voxel_resolve_specular/voxel_resolve_specular.comp.glsl @@ -66,9 +66,13 @@ void main() { n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy); n = normalize(n); + float roughness = g0.b; + + vec3 v = normalize(eye - P); + vec2 velocity = -textureLod(sveloc, uv, 0.0).rg; - vec3 color = traceSpecular(P, n, voxels, voxelsSDF, normalize(eye - P), g0.z * g0.z, clipmaps, pixel, velocity).rgb; + vec3 color = traceSpecular(P, n, voxels, voxelsSDF, v, roughness * roughness, clipmaps, pixel, velocity).rgb; imageStore(voxels_specular, ivec2(pixel), vec4(color, 1.0)); } diff --git a/leenkx/Shaders/voxel_temporal/voxel_temporal.comp.glsl b/leenkx/Shaders/voxel_temporal/voxel_temporal.comp.glsl index 584446ef..a20aafe6 100644 --- a/leenkx/Shaders/voxel_temporal/voxel_temporal.comp.glsl +++ b/leenkx/Shaders/voxel_temporal/voxel_temporal.comp.glsl @@ -79,7 +79,7 @@ void main() { float aniso_colors[6]; #endif - for (int i = 0; i < 6 + DIFFUSE_CONE_COUNT; i++) + for (int i = 0; i < 6 + diffuseConeCount; i++) { ivec3 src = ivec3(gl_GlobalInvocationID.xyz); src.x += i * res; @@ -136,7 +136,7 @@ void main() { radiance = basecol; vec4 trace = traceDiffuse(wposition, wnormal, voxelsSampler, clipmaps); vec3 indirect = trace.rgb + envl.rgb * (1.0 - trace.a); - radiance.rgb *= light.rgb + indirect.rgb; + radiance.rgb *= light.rgb * INV_PI + indirect.rgb; radiance.rgb += emission.rgb; } @@ -195,7 +195,7 @@ void main() { } else { // precompute cone sampling: - vec3 coneDirection = DIFFUSE_CONE_DIRECTIONS[i - 6]; + vec3 coneDirection = diffuseConeDirections[i - 6]; vec3 aniso_direction = -coneDirection; uvec3 face_offsets = uvec3( aniso_direction.x > 0 ? 0 : 1, diff --git a/leenkx/Sources/iron/RenderPath.hx b/leenkx/Sources/iron/RenderPath.hx index e79e6e41..d8673579 100644 --- a/leenkx/Sources/iron/RenderPath.hx +++ b/leenkx/Sources/iron/RenderPath.hx @@ -304,10 +304,13 @@ class RenderPath { currentD = 1; currentFace = -1; meshesSorted = false; + sun = null; for (l in Scene.active.lights) { if (l.visible) l.buildMatrix(Scene.active.camera); - if (l.data.raw.type == "sun") sun = l; + if (l.data.raw.type == "sun") { + if (sun == null || (!sun.data.raw.cast_shadow && l.data.raw.cast_shadow)) sun = l; + } else point = l; } light = Scene.active.lights[0]; @@ -500,7 +503,7 @@ class RenderPath { } public function drawMeshes(context: String) { - var isShadows = context == "shadowmap"; + var isShadows = context == "shadowmap" || context == "shadowmap_transparent"; if (isShadows) { // Disabled shadow casting for this light if (light == null || !light.data.raw.cast_shadow || !light.visible || light.data.raw.strength == 0) return; diff --git a/leenkx/Sources/iron/Scene.hx b/leenkx/Sources/iron/Scene.hx index 49b88f98..7c17b671 100644 --- a/leenkx/Sources/iron/Scene.hx +++ b/leenkx/Sources/iron/Scene.hx @@ -72,6 +72,8 @@ class Scene { public var embedded: Map; #if (rp_renderer == "Deferred") + public static inline var MAX_MATERIALS = 16; + public static inline var FLOATS_PER_MATERIAL_PARAM = 32; // 8 vec4s per material public var materialParamsBuffer: kha.arrays.Float32Array; public var materialParamsDirty: Bool = true; #end @@ -113,7 +115,7 @@ class Scene { #end embedded = new Map(); #if (rp_renderer == "Deferred") - materialParamsBuffer = new kha.arrays.Float32Array(512); + materialParamsBuffer = new kha.arrays.Float32Array(MAX_MATERIALS * FLOATS_PER_MATERIAL_PARAM); #end root = new Object(); root.name = "Root"; @@ -213,11 +215,14 @@ class Scene { } #if (rp_renderer == "Deferred") + public function markMaterialParamsDirty() { + materialParamsDirty = true; + } + public function updateMaterialParams() { if (!materialParamsDirty) return; materialParamsDirty = false; var buf = materialParamsBuffer; - for (i in 0...buf.length) buf[i] = 0.0; for (m in meshes) { if (m.materials == null) continue; for (mat in m.materials) { @@ -237,12 +242,16 @@ class Scene { break; } } - if (slot <= 0 || slot >= 16) continue; - var base = slot * 32; - if (base + 31 >= buf.length) continue; + if (slot < 0 || slot >= MAX_MATERIALS) continue; + var base = slot * FLOATS_PER_MATERIAL_PARAM; + if (base + FLOATS_PER_MATERIAL_PARAM - 1 >= buf.length) continue; if (bc == null) continue; + for (i in 0...FLOATS_PER_MATERIAL_PARAM) buf[base + i] = 0.0; + buf[base + 6] = 1.5; // coatIOR + buf[base + 12] = 1.45; // ior + buf[base + 13] = 1.0; // thinWall for (c in bc) { - if (c == null || c.name == null) continue; + if (c == null || c.name == null || c.floatValue == null) continue; switch (c.name) { // matp0: vec4(anisotropy, anisoRot, sheen, sheenRough) case "anisotropy": buf[base + 0] = c.floatValue; @@ -274,8 +283,13 @@ class Scene { case "subsurfaceColorB": buf[base + 21] = c.floatValue; case "sheenTintR": buf[base + 22] = c.floatValue; case "sheenTintG": buf[base + 23] = c.floatValue; - // matp6: vec4(sheenTintB, 0, 0, 0) + // matp6: vec4(sheenTintB, specularTintR, specularTintG, specularTintB) case "sheenTintB": buf[base + 24] = c.floatValue; + case "specularTintR": buf[base + 25] = c.floatValue; + case "specularTintG": buf[base + 26] = c.floatValue; + case "specularTintB": buf[base + 27] = c.floatValue; + // matp7: vec4(subsurfaceScale, 0, 0, 0) + case "subsurfaceScale": buf[base + 28] = c.floatValue; } } } @@ -431,7 +445,7 @@ class Scene { var object = new MeshObject(data, materials); parent != null ? object.setParent(parent) : object.setParent(root); #if (rp_renderer == "Deferred") - materialParamsDirty = true; + markMaterialParamsDirty(); #end return object; } diff --git a/leenkx/Sources/iron/object/LightObject.hx b/leenkx/Sources/iron/object/LightObject.hx index 01b4a6a1..b90c2571 100644 --- a/leenkx/Sources/iron/object/LightObject.hx +++ b/leenkx/Sources/iron/object/LightObject.hx @@ -245,7 +245,12 @@ class LightObject extends Object { // Snap to texel coords - fix translation swim var smsize = data.raw.shadowmap_size; #if lnx_csm // Cascades - smsize = Std.int(smsize / 4); + #if lnx_shadowmap_atlas + var ts = cascade < tileScale.length ? tileScale[cascade] : 1.0; + smsize = Std.int(Math.max(ts * data.raw.shadowmap_size, 1.0)); + #else + smsize = Std.int(smsize / cascadeCount); + #end #end var worldPerTexelX = (maxx - minx) / smsize; var worldPerTexelY = (maxy - miny) / smsize; @@ -685,22 +690,6 @@ class LightObject extends Object { return LWVPMatrixArray; } - public static inline function getMaxLights(): Int { - #if (rp_max_lights == 8) - return 8; - #elseif (rp_max_lights == 16) - return 16; - #elseif (rp_max_lights == 24) - return 24; - #elseif (rp_max_lights == 32) - return 32; - #elseif (rp_max_lights == 64) - return 64; - #else - return 4; - #end - } - public static inline function getMaxLightsCluster(): Int { #if (rp_max_lights_cluster == 8) return 8; @@ -718,6 +707,90 @@ class LightObject extends Object { } #end // lnx_clusters + public static inline function getMaxLights(): Int { + #if (rp_max_lights == 8) + return 8; + #elseif (rp_max_lights == 16) + return 16; + #elseif (rp_max_lights == 24) + return 24; + #elseif (rp_max_lights == 32) + return 32; + #elseif (rp_max_lights == 64) + return 64; + #else + return 4; + #end + } + + #if (rp_shadowmap && lnx_shadowmap_atlas) + public static var sunTileBoundsArray: Float32Array = null; + public static var tileBoundsDirty: Bool = true; + + public static function updateSunTileBoundsArray(): Float32Array { + if (!tileBoundsDirty && sunTileBoundsArray != null) return sunTileBoundsArray; + tileBoundsDirty = false; + var maxLights = getMaxLights(); + var numTiles = maxLights * cascadeCount; + if (sunTileBoundsArray == null) { + sunTileBoundsArray = new Float32Array(numTiles * 4); + } + for (k in 0...sunTileBoundsArray.length) sunTileBoundsArray[k] = 0; + var i = 0; + for (light in Scene.active.lights) { + if (i >= maxLights) break; + if (!light.visible || light.data.raw.type != "sun") continue; + if (!light.data.raw.cast_shadow || light.data.raw.strength == 0.0) continue; + for (c in 0...cascadeCount) { + var idx = (i * cascadeCount + c) * 4; + sunTileBoundsArray[idx ] = light.tileOffsetX[c]; + sunTileBoundsArray[idx + 2] = light.tileOffsetX[c] + light.tileScale[c]; + #if (!kha_opengl) + // flip bounds to match + sunTileBoundsArray[idx + 1] = 1.0 - (light.tileOffsetY[c] + light.tileScale[c]); + sunTileBoundsArray[idx + 3] = 1.0 - light.tileOffsetY[c]; + #else + sunTileBoundsArray[idx + 1] = light.tileOffsetY[c]; + sunTileBoundsArray[idx + 3] = light.tileOffsetY[c] + light.tileScale[c]; + #end + } + i++; + } + return sunTileBoundsArray; + } + #end + + #if (rp_shadowmap && lnx_shadowmap_atlas) + public static var spotTileBoundsArray: Float32Array = null; + + public static function updateSpotTileBoundsArray(): Float32Array { + var maxLights = getMaxLights(); + if (spotTileBoundsArray == null) { + spotTileBoundsArray = new Float32Array(maxLights * 4); + } + for (k in 0...spotTileBoundsArray.length) spotTileBoundsArray[k] = 0; + var i = 0; + for (light in Scene.active.lights) { + if (i >= maxLights) break; + if (!light.visible || light.data.raw.type != "spot") continue; + if (!light.data.raw.cast_shadow || light.data.raw.strength == 0.0) continue; + var idx = i * 4; + spotTileBoundsArray[idx ] = light.tileOffsetX[0]; + spotTileBoundsArray[idx + 2] = light.tileOffsetX[0] + light.tileScale[0]; + #if (!kha_opengl) + // flip bounds + spotTileBoundsArray[idx + 1] = 1.0 - (light.tileOffsetY[0] + light.tileScale[0]); + spotTileBoundsArray[idx + 3] = 1.0 - light.tileOffsetY[0]; + #else + spotTileBoundsArray[idx + 1] = light.tileOffsetY[0]; + spotTileBoundsArray[idx + 3] = light.tileOffsetY[0] + light.tileScale[0]; + #end + i++; + } + return spotTileBoundsArray; + } + #end + public inline function right(): Vec4 { return new Vec4(V._00, V._10, V._20); } diff --git a/leenkx/Sources/iron/object/MeshObject.hx b/leenkx/Sources/iron/object/MeshObject.hx index 5b290cfe..472abea3 100644 --- a/leenkx/Sources/iron/object/MeshObject.hx +++ b/leenkx/Sources/iron/object/MeshObject.hx @@ -90,7 +90,7 @@ class MeshObject extends Object { if (tilesheet != null) tilesheet.remove(); if (Scene.active != null) Scene.active.meshes.remove(this); #if (rp_renderer == "Deferred") - if (Scene.active != null) Scene.active.materialParamsDirty = true; + if (Scene.active != null) Scene.active.markMaterialParamsDirty(); #end data.refcount--; super.remove(); diff --git a/leenkx/Sources/iron/object/Uniforms.hx b/leenkx/Sources/iron/object/Uniforms.hx index 686be5ba..d12f7a67 100644 --- a/leenkx/Sources/iron/object/Uniforms.hx +++ b/leenkx/Sources/iron/object/Uniforms.hx @@ -827,9 +827,20 @@ class Uniforms { } } case "_shadowMapSize": { - if (light != null && light.data.raw.cast_shadow) { + var shadowLight = light; + if (shadowLight == null || !shadowLight.data.raw.cast_shadow) { + shadowLight = RenderPath.active.sun; + } + if (shadowLight != null && shadowLight.data.raw.cast_shadow) { v = helpVec; - v.x = v.y = light.data.raw.shadowmap_size; + v.x = v.y = shadowLight.data.raw.shadowmap_size; + #if lnx_csm + #if (!lnx_shadowmap_atlas) + if (shadowLight.data.raw.type == "sun") { + v.x = shadowLight.data.raw.shadowmap_size * LightObject.cascadeCount; + } + #end + #end } } default: @@ -887,9 +898,11 @@ class Uniforms { case "_envmapIrradiance": { fa = Scene.active.world == null ? WorldData.getEmptyIrradiance() : Scene.active.world.probe.irradiance; } - case "_materialParams": { + #if (rp_renderer == "Deferred") + case "_materialParams": { fa = Scene.active.materialParamsBuffer; } + #end #if lnx_clusters case "_lightsArray": { fa = LightObject.lightsArray; @@ -906,15 +919,18 @@ class Uniforms { #end #end // lnx_clusters #if lnx_csm - case "_cascadeData": { - for (l in Scene.active.lights) { - if (l.data.raw.type == "sun") { - fa = l.getCascadeData(); - break; - } - } + case "_cascadeData": { + var sun = RenderPath.active.sun; + if (sun != null && sun.data.raw.type == "sun") { + fa = sun.getCascadeData(); } - #end + } + #end + #if lnx_shadowmap_atlas + case "_tileBoundsSunArray": { + fa = LightObject.updateSunTileBoundsArray(); + } + #end } if (fa != null) { @@ -1067,32 +1083,30 @@ class Uniforms { } } case "_biasLightWorldViewProjectionMatrixSun": { - for (l in iron.Scene.active.lights) { - if (l.data.raw.type == "sun") { - // object is null for DrawQuad - object == null ? helpMat.setIdentity() : helpMat.setFrom(object.transform.worldUnpack); - helpMat.multmat(l.VP); - helpMat.multmat(biasMat); - #if lnx_shadowmap_atlas - // tile matrix - helpMat2.setIdentity(); - // scale [0-1] coords to [0-tilescale] - helpMat2._00 = l.tileScale[0]; - helpMat2._11 = l.tileScale[0]; - // offset coordinate start from [0, 0] to [tile-start-x, tile-start-y] - helpMat2._30 = l.tileOffsetX[0]; - helpMat2._31 = l.tileOffsetY[0]; - helpMat.multmat(helpMat2); - #if (!kha_opengl) - helpMat2.setIdentity(); - helpMat2._11 = -1.0; - helpMat2._31 = 1.0; - helpMat.multmat(helpMat2); - #end - #end - m = helpMat; - break; - } + var sun = RenderPath.active.sun; + if (sun != null && sun.data.raw.type == "sun") { + // object is null for DrawQuad + object == null ? helpMat.setIdentity() : helpMat.setFrom(object.transform.worldUnpack); + helpMat.multmat(sun.VP); + helpMat.multmat(biasMat); + #if lnx_shadowmap_atlas + // tile matrix + helpMat2.setIdentity(); + // scale [0-1] coords to [0-tilescale] + helpMat2._00 = sun.tileScale[0]; + helpMat2._11 = sun.tileScale[0]; + // offset coordinate start from [0, 0] to [tile-start-x, tile-start-y] + helpMat2._30 = sun.tileOffsetX[0]; + helpMat2._31 = sun.tileOffsetY[0]; + helpMat.multmat(helpMat2); + #if (!kha_opengl) + helpMat2.setIdentity(); + helpMat2._11 = -1.0; + helpMat2._31 = 1.0; + helpMat.multmat(helpMat2); + #end + #end + m = helpMat; } } #if rp_probes @@ -1376,7 +1390,7 @@ class Uniforms { if (fa == null) return; g.setFloats(location, fa); } - else if (c.type == "int") { + else if (c.type == "int" || c.type == "uint") { var i: Null = null; switch (c.link) { case "_uid": { diff --git a/leenkx/Sources/leenkx/renderpath/Inc.hx b/leenkx/Sources/leenkx/renderpath/Inc.hx index 52b12594..02e9433d 100644 --- a/leenkx/Sources/leenkx/renderpath/Inc.hx +++ b/leenkx/Sources/leenkx/renderpath/Inc.hx @@ -39,7 +39,6 @@ class Inc { #if (rp_voxels == "Voxel GI") static var voxel_td1:kha.graphics4.TextureUnit; static var voxel_te1:kha.graphics4.TextureUnit; - static var voxel_cc1:kha.graphics4.ConstantLocation; #else #if lnx_voxelgi_shadows static var voxel_te1:kha.graphics4.TextureUnit; @@ -137,18 +136,22 @@ class Inc { break; if (LightObject.discardLightCulled(light)) continue; if (light.data.raw.type == "point") { - if (!light.data.raw.cast_shadow) { + if (light.data.raw.cast_shadow) { + for(k in 0...6) { + LightObject.pointLightsData[j ] = light.tileOffsetX[k]; // posx + LightObject.pointLightsData[j + 1] = light.tileOffsetY[k]; // posy + LightObject.pointLightsData[j + 2] = light.tileScale[k]; // tile scale factor relative to atlas + LightObject.pointLightsData[j + 3] = 0; // padding + j += 4; + } + } + else { j += 4 * 6; - continue; - } - for(k in 0...6) { - LightObject.pointLightsData[j ] = light.tileOffsetX[k]; // posx - LightObject.pointLightsData[j + 1] = light.tileOffsetY[k]; // posy - LightObject.pointLightsData[j + 2] = light.tileScale[k]; // tile scale factor relative to atlas - LightObject.pointLightsData[j + 3] = 0; // padding - j += 4; } } + else { + j += 4 * 6; // Reserve slot for non-point lights to keep index aligned + } i++; } } @@ -214,11 +217,13 @@ class Inc { if (!light.lightInAtlas && !light.culledLight && light.visible && light.shadowMapScale > 0.0 && light.data.raw.strength > 0.0 && light.data.raw.cast_shadow) { ShadowMapAtlas.addLight(light, false); + LightObject.tileBoundsDirty = true; } #if rp_shadowmap_transparent if (!light.lightInAtlasTransparent && !light.culledLight && light.visible && light.shadowMapScale > 0.0 && light.data.raw.strength > 0.0 && light.data.raw.cast_shadow) { ShadowMapAtlas.addLight(light, true); + LightObject.tileBoundsDirty = true; } #end } @@ -308,6 +313,7 @@ class Inc { var newTile = ShadowMapTile.assignTiles(tile.light, atlas, tile); if (newTile != null) atlas.activeTiles.push(newTile); + LightObject.tileBoundsDirty = true; } updatePointLightAtlasData(false); #end @@ -315,6 +321,7 @@ class Inc { for (tile in tilesToRemove) { atlas.activeTiles.remove(tile); tile.freeTile(); + LightObject.tileBoundsDirty = true; } } @@ -405,6 +412,7 @@ class Inc { var newTile = ShadowMapTile.assignTiles(tile.light, atlas, tile); if (newTile != null) atlas.activeTiles.push(newTile); + LightObject.tileBoundsDirty = true; } updatePointLightAtlasData(true); #end @@ -412,6 +420,7 @@ class Inc { for (tile in tilesToRemove) { atlas.activeTiles.remove(tile); tile.freeTile(); + LightObject.tileBoundsDirty = true; } } #end @@ -419,25 +428,30 @@ class Inc { } #else public static function bindShadowMap() { - for (l in iron.Scene.active.lights) { - if (!l.visible || l.data.raw.type != "sun") continue; + var sun = RenderPath.active.sun; + if (sun != null && sun.visible && sun.data.raw.type == "sun") { var n = "shadowMap"; path.bindTarget(n, n); + #if rp_shadowmap_transparent var n = "shadowMapTransparent"; path.bindTarget(n, n); - break; + #end } for (i in 0...pointIndex) { var n = "shadowMapPoint[" + i + "]"; path.bindTarget(n, n); + #if rp_shadowmap_transparent var n = "shadowMapPointTransparent[" + i + "]"; path.bindTarget(n, n); + #end } for (i in 0...spotIndex) { var n = "shadowMapSpot[" + i + "]"; path.bindTarget(n, n); + #if rp_shadowmap_transparent var n = "shadowMapSpotTransparent[" + i + "]"; path.bindTarget(n, n); + #end } } @@ -500,6 +514,7 @@ class Inc { spotIndex = 0; for (l in iron.Scene.active.lights) { if (!l.visible) continue; + if (l.data.raw.type == "sun" && l != RenderPath.active.sun) continue; path.light = l; var shadowmap = Inc.getShadowMap(l, false); @@ -523,6 +538,7 @@ class Inc { spotIndex = 0; for (l in iron.Scene.active.lights) { if (!l.visible) continue; + if (l.data.raw.type == "sun" && l != RenderPath.active.sun) continue; path.light = l; var shadowmap_transparent = Inc.getShadowMap(l, true); @@ -719,7 +735,7 @@ class Inc { #else t.format = "RGBA32"; #end - t.width = res * (6 + 16); + t.width = res * (6 + Main.diffuseConeCount); t.height = res * Main.voxelgiClipmapCount; t.depth = res; } @@ -840,12 +856,10 @@ class Inc { voxel_ca1 = voxel_sh1.getConstantLocation("clipmaps"); voxel_cb1 = voxel_sh1.getConstantLocation("clipmapLevel"); - voxel_cc1 = voxel_sh1.getConstantLocation("envmapStrength"); #if (rp_voxels == "Voxel GI") voxel_td1 = voxel_sh1.getTextureUnit("voxelsSampler"); voxel_te1 = voxel_sh1.getTextureUnit("SDF"); - voxel_cc1 = voxel_sh1.getConstantLocation("envmapStrength"); #else #if lnx_voxelgi_shadows voxel_te1 = voxel_sh1.getTextureUnit("SDF"); @@ -884,9 +898,11 @@ class Inc { #if lnx_brdf voxel_tg3 = voxel_sh3.getTextureUnit("senvmapBrdf"); #end + #if (rp_voxels == "Voxel AO") #if lnx_radiance voxel_th3 = voxel_sh3.getTextureUnit("senvmapRadiance"); #end + #end voxel_ca3 = voxel_sh3.getConstantLocation("clipmaps"); voxel_cb3 = voxel_sh3.getConstantLocation("InvVP"); voxel_cc3 = voxel_sh3.getConstantLocation("eye"); @@ -895,12 +911,14 @@ class Inc { #if lnx_irradiance voxel_cf3 = voxel_sh3.getConstantLocation("shirr"); #end + #if (rp_voxels == "Voxel AO") #if lnx_radiance voxel_cg3 = voxel_sh3.getConstantLocation("envmapNumMipmaps"); #end #if lnx_envcol voxel_ch3 = voxel_sh3.getConstantLocation("backgroundCol"); - #end + #end + #end } #if (rp_voxels == "Voxel GI") if (voxel_sh4 == null) @@ -988,7 +1006,6 @@ class Inc { #if (rp_voxels == "Voxel GI") g.setTexture(voxel_td1, rts.get("voxelsOutB").image); g.setImageTexture(voxel_te1, rts.get("voxelsSDF").image); - g.setFloat(voxel_cc1, iron.Scene.active.world == null ? 0.0 : iron.Scene.active.world.probe.raw.strength); #else #if lnx_voxelgi_shadows g.setImageTexture(voxel_te1, rts.get("voxelsSDF").image); @@ -1001,8 +1018,6 @@ class Inc { g.setInt(voxel_cb1, iron.RenderPath.clipmapLevel); - g.setFloat(voxel_cc1, iron.Scene.active.world == null ? 0.0 : iron.Scene.active.world.probe.raw.strength); - g.compute(Std.int(res / 8), Std.int(res / 8), Std.int(res / 8)); } @@ -1163,9 +1178,6 @@ class Inc { #if lnx_brdf g.setTexture(voxel_tg3, iron.Scene.active.embedded.get("brdf.png")); #end - #if lnx_radiance - g.setTexture(voxel_th3, iron.Scene.active.world.probe.radiance); - #end var fa = fillVoxelClipmapsArray(clipmaps); @@ -1205,28 +1217,12 @@ class Inc { iron.Scene.active.world.probe.irradiance; g.setFloats(voxel_cf3, irradiance); #end - #if lnx_radiance - g.setFloat(voxel_cg3, iron.Scene.active.world != null ? iron.Scene.active.world.probe.raw.radiance_mipmaps + 1 - 2 : 1); - #end - - #if lnx_envcol - var x: kha.FastFloat = 0.0; - var y: kha.FastFloat = 0.0; - var z: kha.FastFloat = 0.0; - - if (camera.data.raw.clear_color != null) { - x = camera.data.raw.clear_color[0]; - y = camera.data.raw.clear_color[1]; - z = camera.data.raw.clear_color[2]; - } - - g.setFloat3(voxel_ch3, x, y, z); - #end g.compute(Std.int((width + 7) / 8), Std.int((height + 7) / 8), 1); } #end + #if (rp_voxels == "Voxel GI") public static function resolveSpecular(g: kha.graphics4.Graphics) { var rts = path.renderTargets; var res = iron.RenderPath.getVoxelRes(); @@ -1278,6 +1274,7 @@ class Inc { g.compute(Std.int((width + 7) / 8), Std.int((height + 7) / 8), 1); } + #end #if (rp_voxels == "Voxel GI") #end // GI diff --git a/leenkx/Sources/leenkx/renderpath/RenderPathDeferred.hx b/leenkx/Sources/leenkx/renderpath/RenderPathDeferred.hx index c2b3fd89..580c30d0 100644 --- a/leenkx/Sources/leenkx/renderpath/RenderPathDeferred.hx +++ b/leenkx/Sources/leenkx/renderpath/RenderPathDeferred.hx @@ -27,7 +27,7 @@ class RenderPathDeferred { #if rp_gbuffer2 "gbuffer2", #end #if rp_gbuffer_emission "gbuffer_emission", #end #if (rp_ssrefr || lnx_voxelgi_refract) "gbuffer_refraction", #end - #if lnx_anisotropy "gbuffer3" #end + #if rp_clearcoat "gbuffer_coat_normal", #end ]); } @@ -155,7 +155,11 @@ class RenderPathDeferred { t.width = 0; t.height = 0; t.displayp = Inc.getDisplayp(); + #if rp_hdr t.format = "RGBA64"; + #else + t.format = "RGBA32"; + #end t.scale = Inc.getSuperSampling(); path.createRenderTarget(t); } @@ -174,19 +178,6 @@ class RenderPathDeferred { } #end - #if lnx_anisotropy - { - var t = new RenderTargetRaw(); - t.name = "gbuffer3"; - t.width = 0; - t.height = 0; - t.displayp = Inc.getDisplayp(); - t.format = "RGBA64"; - t.scale = Inc.getSuperSampling(); - path.createRenderTarget(t); - } - #end - #if rp_material_solid path.loadShader("shader_datas/deferred_light_solid/deferred_light"); #elseif rp_material_mobile @@ -295,7 +286,7 @@ class RenderPathDeferred { } #end - #if ((rp_antialiasing == "SMAA") || (rp_antialiasing == "TAA") || rp_fsr1) + #if ((rp_antialiasing == "SMAA") || (rp_antialiasing == "TAA") || rp_fsr1 || (rp_ssr && !rp_ssr_half)) { var t = new RenderTargetRaw(); t.name = "bufa"; @@ -422,6 +413,19 @@ class RenderPathDeferred { } #end + #if rp_clearcoat + { + var t = new RenderTargetRaw(); + t.name = "gbuffer_coat_normal"; + t.width = 0; + t.height = 0; + t.displayp = Inc.getDisplayp(); + t.format = "RGBA64"; + t.scale = Inc.getSuperSampling(); + path.createRenderTarget(t); + } + #end + #if (rp_ssrefr || lnx_voxelgi_refract) { var t = new RenderTargetRaw(); @@ -429,7 +433,7 @@ class RenderPathDeferred { t.width = 0; t.height = 0; t.displayp = Inc.getDisplayp(); - t.format = "RGBA64"; + t.format = "RGBA32"; t.scale = Inc.getSuperSampling(); path.createRenderTarget(t); } @@ -569,7 +573,14 @@ class RenderPathDeferred { #if (rp_ssrefr || lnx_voxelgi_refract) { path.setTarget("gbuffer_refraction"); - path.clearTarget(0xffff00ff); + path.clearTarget(0x000000ff); + } + #end + + #if rp_clearcoat + { + path.setTarget("gbuffer_coat_normal"); + path.clearTarget(0x00000000); } #end @@ -580,12 +591,6 @@ class RenderPathDeferred { } #end - #if lnx_anisotropy - { - path.setTarget("gbuffer3"); - path.clearTarget(0x00000000); - } - #end RenderPathCreator.setTargetMeshes(); @@ -782,8 +787,10 @@ class RenderPathDeferred { } #end - #if lnx_anisotropy - path.bindTarget("gbuffer3", "gbuffer3"); + #if rp_clearcoat + { + path.bindTarget("gbuffer_coat_normal", "gbufferCoatNormal"); + } #end #if rp_ssao @@ -849,6 +856,7 @@ class RenderPathDeferred { #end #if rp_material_solid + Scene.active.updateMaterialParams(); path.drawShader("shader_datas/deferred_light_solid/deferred_light"); #elseif rp_material_mobile Scene.active.updateMaterialParams(); @@ -910,7 +918,10 @@ class RenderPathDeferred { #if rp_water { + #if (!kha_opengl) path.setDepthFrom("tex", "gbuffer1"); + #end + path.setTarget("buf"); path.bindTarget("tex", "tex"); path.drawShader("shader_datas/copy_pass/copy_pass"); @@ -918,7 +929,10 @@ class RenderPathDeferred { path.bindTarget("_main", "gbufferD"); path.bindTarget("buf", "tex"); path.drawShader("shader_datas/water_pass/water_pass"); + + #if (!kha_opengl) path.setDepthFrom("tex", "gbuffer0"); + #end } #end @@ -934,7 +948,7 @@ class RenderPathDeferred { var targetb = "ssrb"; #else var targeta = "buf"; - var targetb = "gbuffer1"; + var targetb = "bufa"; #end path.setTarget(targeta); @@ -952,11 +966,21 @@ class RenderPathDeferred { path.setTarget(targetb); path.bindTarget(targeta, "tex"); path.bindTarget("gbuffer0", "gbuffer0"); + #if rp_ssr_half + path.bindTarget("half", "gbufferD"); + #else + path.bindTarget("_main", "gbufferD"); + #end path.drawShader("shader_datas/blur_adaptive_pass/blur_adaptive_pass_x"); path.setTarget("tex"); path.bindTarget(targetb, "tex"); path.bindTarget("gbuffer0", "gbuffer0"); + #if rp_ssr_half + path.bindTarget("half", "gbufferD"); + #else + path.bindTarget("_main", "gbufferD"); + #end path.drawShader("shader_datas/blur_adaptive_pass/blur_adaptive_pass_y3_blend"); #if (!kha_opengl) @@ -976,12 +1000,14 @@ class RenderPathDeferred { path.bindTarget("tex", "tex"); path.bindTarget("_main", "gbufferD"); path.bindTarget("gbuffer0", "gbuffer0"); + path.bindTarget("gbuffer1", "gbuffer1"); path.drawShader("shader_datas/sss_pass/sss_pass_x"); path.setTarget("tex"); path.bindTarget("buf", "tex"); path.bindTarget("_main", "gbufferD"); path.bindTarget("gbuffer0", "gbuffer0"); + path.bindTarget("gbuffer1", "gbuffer1"); path.drawShader("shader_datas/sss_pass/sss_pass_y"); #if (!kha_opengl) @@ -994,20 +1020,20 @@ class RenderPathDeferred { { if (leenkx.data.Config.raw.rp_ssrefr != false) { - //#if (!kha_opengl) - //path.setDepthFrom("gbuffer0", "gbuffer1"); // Unbind depth so we can read it - //path.depthToRenderTarget.set("main", path.renderTargets.get("tex")); - //#end + #if (!kha_opengl) + path.setDepthFrom("gbuffer0", "gbuffer1"); // Unbind depth so we can read it + path.depthToRenderTarget.set("main", path.renderTargets.get("tex")); + #end //save depth path.setTarget("gbufferD1"); path.bindTarget("_main", "tex"); path.drawShader("shader_datas/copy_pass/copy_pass"); - //#if (!kha_opengl) - //path.setDepthFrom("gbuffer0", "tex"); // Re-bind depth - //path.depthToRenderTarget.set("main", path.renderTargets.get("gbuffer0")); - //#end + #if (!kha_opengl) + path.setDepthFrom("gbuffer0", "tex"); // Re-bind depth + path.depthToRenderTarget.set("main", path.renderTargets.get("gbuffer0")); + #end //save background color path.setTarget("refr"); @@ -1043,6 +1069,7 @@ class RenderPathDeferred { path.bindTarget("tex", "tex"); path.bindTarget("gbufferD1", "gbufferD1"); path.bindTarget("gbuffer0", "gbuffer0"); + path.bindTarget("gbuffer1", "gbuffer1"); path.bindTarget("refr", "tex1"); path.bindTarget("_main", "gbufferD"); path.bindTarget("gbuffer_refraction", "gbuffer_refraction"); diff --git a/leenkx/Sources/leenkx/renderpath/RenderPathForward.hx b/leenkx/Sources/leenkx/renderpath/RenderPathForward.hx index ea61677b..bcddc2b0 100644 --- a/leenkx/Sources/leenkx/renderpath/RenderPathForward.hx +++ b/leenkx/Sources/leenkx/renderpath/RenderPathForward.hx @@ -130,7 +130,7 @@ class RenderPathForward { t.width = 0; t.height = 0; t.displayp = Inc.getDisplayp(); - t.format = "RGBA64"; + t.format = "RGBA32"; t.scale = Inc.getSuperSampling(); path.createRenderTarget(t); } @@ -489,7 +489,7 @@ class RenderPathForward { #if (rp_ssrefr || lnx_voxelgi_refract) { path.setTarget("gbuffer_refraction"); - path.clearTarget(0xffffff00); + path.clearTarget(0x00000000); } #end @@ -645,11 +645,21 @@ class RenderPathForward { path.setTarget(targetb); path.bindTarget(targeta, "tex"); path.bindTarget("lbuffer1", "gbuffer0"); + #if rp_ssr_half + path.bindTarget("half", "gbufferD"); + #else + path.bindTarget("_main", "gbufferD"); + #end path.drawShader("shader_datas/blur_adaptive_pass/blur_adaptive_pass_x"); path.setTarget("lbuffer0"); path.bindTarget(targetb, "tex"); path.bindTarget("lbuffer1", "gbuffer0"); + #if rp_ssr_half + path.bindTarget("half", "gbufferD"); + #else + path.bindTarget("_main", "gbufferD"); + #end path.drawShader("shader_datas/blur_adaptive_pass/blur_adaptive_pass_y3_blend"); } } diff --git a/leenkx/Sources/leenkx/trait/internal/UniformsManager.hx b/leenkx/Sources/leenkx/trait/internal/UniformsManager.hx index eeb7505a..42865a72 100644 --- a/leenkx/Sources/leenkx/trait/internal/UniformsManager.hx +++ b/leenkx/Sources/leenkx/trait/internal/UniformsManager.hx @@ -22,7 +22,7 @@ class UniformsManager extends Trait{ static var texturesRegistered = false; static var texturesMap = new Map>>(); - static var sceneRemoveInitalized = false; + static var sceneRemoveInitialized = false; public var uniformExists = false; @@ -32,7 +32,8 @@ class UniformsManager extends Trait{ notifyOnAdd(init); notifyOnRemove(removeObject); - if (!sceneRemoveInitalized) { + if (!sceneRemoveInitialized) { + sceneRemoveInitialized = true; Scene.active.notifyOnRemove(removeScene); } } @@ -166,7 +167,12 @@ class UniformsManager extends Trait{ matMap.set(material, entry); } - entry.set(link, value); // parameter name, value + if (entry.get(link) != value) { + entry.set(link, value); + #if (rp_renderer == "Deferred") + if (Scene.active != null) Scene.active.markMaterialParamsDirty(); + #end + } } // Method to set map Object -> Material -> Link -> Vec3 @@ -188,7 +194,12 @@ class UniformsManager extends Trait{ matMap.set(material, entry); } - entry.set(link, value); // parameter name, value + if (entry.get(link) != value) { + entry.set(link, value); + #if (rp_renderer == "Deferred") + if (Scene.active != null) Scene.active.markMaterialParamsDirty(); + #end + } } // Method to set map Object -> Material -> Link -> Texture @@ -210,7 +221,12 @@ class UniformsManager extends Trait{ matMap.set(material, entry); } - entry.set(link, value); // parameter name, value + if (entry.get(link) != value) { + entry.set(link, value); + #if (rp_renderer == "Deferred") + if (Scene.active != null) Scene.active.markMaterialParamsDirty(); + #end + } } // Method to get object specific material parameter float value @@ -330,10 +346,15 @@ class UniformsManager extends Trait{ var entry = material.get(mat); if (entry == null) return; + var existed = entry.exists(link); entry.remove(link); if (!entry.keys().hasNext()) material.remove(mat); if (!material.keys().hasNext()) floatsMap.remove(object); + + #if (rp_renderer == "Deferred") + if (existed && Scene.active != null) Scene.active.markMaterialParamsDirty(); + #end } public static function removeVectorValue(object: Object, mat:MaterialData, link: String) { @@ -344,10 +365,15 @@ class UniformsManager extends Trait{ var entry = material.get(mat); if (entry == null) return; + var existed = entry.exists(link); entry.remove(link); if (!entry.keys().hasNext()) material.remove(mat); if (!material.keys().hasNext()) vectorsMap.remove(object); + + #if (rp_renderer == "Deferred") + if (existed && Scene.active != null) Scene.active.markMaterialParamsDirty(); + #end } public static function removeTextureValue(object: Object, mat:MaterialData, link: String) { @@ -358,10 +384,15 @@ class UniformsManager extends Trait{ var entry = material.get(mat); if (entry == null) return; + var existed = entry.exists(link); entry.remove(link); if (!entry.keys().hasNext()) material.remove(mat); if (!material.keys().hasNext()) texturesMap.remove(object); + + #if (rp_renderer == "Deferred") + if (existed && Scene.active != null) Scene.active.markMaterialParamsDirty(); + #end } } diff --git a/leenkx/blender/lnx/exporter.py b/leenkx/blender/lnx/exporter.py index aa125713..f7d52b52 100644 --- a/leenkx/blender/lnx/exporter.py +++ b/leenkx/blender/lnx/exporter.py @@ -2514,6 +2514,9 @@ class LeenkxExporter: # Ensure the same order for merging materials self.material_array.sort(key=lambda x: x.name) + from lnx.material import mat_state + mat_state.next_ext_mat_id = 3 + if wrd.lnx_batch_materials: mat_users = self.material_to_object_dict mat_lnxusers = self.material_to_lnx_object_dict @@ -2526,9 +2529,6 @@ class LeenkxExporter: decals_used = False sss_used = False - from lnx.material import mat_state - mat_state.next_ext_mat_id = 3 - for material in self.material_array: # If the material is unlinked, material becomes None if material is None: diff --git a/leenkx/blender/lnx/lib/make_datas.py b/leenkx/blender/lnx/lib/make_datas.py index a47a8390..5daa1f56 100644 --- a/leenkx/blender/lnx/lib/make_datas.py +++ b/leenkx/blender/lnx/lib/make_datas.py @@ -247,7 +247,7 @@ def check_link( included based on the given defines (`defs`). If that is the case, the found link is written to the `out` dictionary. """ - for link in source_context["links"]: + for link in source_context.get("links", []): if link["name"] == cid: valid_link = True diff --git a/leenkx/blender/lnx/make_renderpath.py b/leenkx/blender/lnx/make_renderpath.py index 84a4f589..e7362538 100644 --- a/leenkx/blender/lnx/make_renderpath.py +++ b/leenkx/blender/lnx/make_renderpath.py @@ -133,7 +133,6 @@ def add_world_defs(): assets.add_shader_external(lnx.utils.get_sdk_path() + '/leenkx/Shaders/voxel_offsetprev/voxel_offsetprev.comp.glsl') assets.add_shader_external(lnx.utils.get_sdk_path() + '/leenkx/Shaders/voxel_temporal/voxel_temporal.comp.glsl') assets.add_shader_external(lnx.utils.get_sdk_path() + '/leenkx/Shaders/voxel_sdf_jumpflood/voxel_sdf_jumpflood.comp.glsl') - #wrd.world_defs += "_VoxelCones" + rpdat.lnx_voxelgi_cones if rpdat.lnx_voxelgi_shadows and (point_lights > 0 or '_Sun' in wrd.world_defs): wrd.world_defs += '_VoxelShadow' assets.add_khafile_def('lnx_voxelgi_shadows') @@ -173,9 +172,6 @@ def add_world_defs(): wrd.world_defs += '_Brdf' assets.add_khafile_def("lnx_brdf") - if '_Anisotropy' in wrd.world_defs: - assets.add_khafile_def('lnx_anisotropy') - def build(): rpdat = lnx.utils.get_rp() project_path = lnx.utils.get_fp() @@ -237,7 +233,7 @@ def build(): assets.add_khafile_def('rp_compositornodes') compo_depth = False # wrd.compo_defs += '' - if rpdat.lnx_tonemap != 'Off': + if rpdat.lnx_tonemap != 'Off' and not state.is_viewport: wrd.compo_defs += '_CTone' + rpdat.lnx_tonemap if rpdat.lnx_dithering != 'Off': wrd.compo_defs += '_CDithering' + rpdat.lnx_dithering @@ -444,7 +440,8 @@ def build(): if rpdat.rp_sss: assets.add_khafile_def('rp_sss') - wrd.world_defs += '_SSS' + if '_SSS' not in wrd.world_defs: + wrd.world_defs += '_SSS' assets.add_shader_pass('sss_pass') if (rpdat.rp_ssr and rpdat.lnx_ssr_half_res) or (rpdat.rp_ssao and rpdat.lnx_ssao_half_res) or (rpdat.rp_ssgi and rpdat.lnx_ssgi_half_res) or rpdat.rp_voxels != "Off": @@ -487,13 +484,55 @@ def build(): if ignoreIrr: wrd.world_defs += '_IgnoreIrr' + # TODO: avoid the prescan correctly + if bpy.app.version >= (4, 0, 0): + coat_name = 'Coat Weight' + sheen_name = 'Sheen Weight' + subsurf_name = 'Subsurface Weight' + trans_name = 'Transmission Weight' + else: + coat_name = 'Clearcoat' + sheen_name = 'Sheen' + subsurf_name = 'Subsurface' + trans_name = 'Transmission' + for mat in bpy.data.materials: + if mat.node_tree is None: + continue + for node in mat.node_tree.nodes: + if node.type == 'BSDF_PRINCIPLED': + coat_socket = node.inputs.get(coat_name) + if coat_socket is not None and (coat_socket.is_linked or coat_socket.default_value > 0.0): + if '_ClearCoat' not in wrd.world_defs: + wrd.world_defs += '_ClearCoat' + aniso_socket = node.inputs.get('Anisotropic') + if aniso_socket is not None and (aniso_socket.is_linked or aniso_socket.default_value > 0.0): + if '_Anisotropy' not in wrd.world_defs: + wrd.world_defs += '_Anisotropy' + sheen_socket = node.inputs.get(sheen_name) + if sheen_socket is not None and (sheen_socket.is_linked or sheen_socket.default_value > 0.0): + if '_Sheen' not in wrd.world_defs: + wrd.world_defs += '_Sheen' + subsurf_socket = node.inputs.get(subsurf_name) + if subsurf_socket is not None and (subsurf_socket.is_linked or subsurf_socket.default_value > 0.0): + if '_SSS' not in wrd.world_defs: + wrd.world_defs += '_SSS' + trans_socket = node.inputs.get(trans_name) + if trans_socket is not None and (trans_socket.is_linked or trans_socket.default_value > 0.0): + if '_Transmission' not in wrd.world_defs: + wrd.world_defs += '_Transmission' + elif node.type == 'SUBSURFACE_SCATTERING': + if '_SSS' not in wrd.world_defs: + wrd.world_defs += '_SSS' - gbuffer2 = '_Veloc' in wrd.world_defs or '_IgnoreIrr' in wrd.world_defs or '_VoxelGI' in wrd.world_defs or '_VoxelShadow' in wrd.world_defs or '_SSGI' in wrd.world_defs + gbuffer2 = '_Veloc' in wrd.world_defs or '_IgnoreIrr' in wrd.world_defs or '_VoxelGI' in wrd.world_defs or '_VoxelShadow' in wrd.world_defs or '_SSGI' in wrd.world_defs or '_Anisotropy' in wrd.world_defs if gbuffer2: assets.add_khafile_def('rp_gbuffer2') wrd.world_defs += '_gbuffer2' + if '_ClearCoat' in wrd.world_defs: + assets.add_khafile_def('rp_clearcoat') + if callback is not None: callback() @@ -506,7 +545,7 @@ def get_num_gbuffer_rts_deferred()-> int: refraction_flags = {'_SSRefraction', '_VoxelRefract'} found_refraction_flag = False - for flag in ('_gbuffer2', '_EmissionShaded', '_SSRefraction', '_VoxelRefract'): + for flag in ('_gbuffer2', '_EmissionShaded', '_SSRefraction', '_VoxelRefract', '_ClearCoat'): if flag in wrd.world_defs: if flag in refraction_flags and not found_refraction_flag: num += 1 @@ -514,7 +553,4 @@ def get_num_gbuffer_rts_deferred()-> int: else: num += 1 - if '_Anisotropy' in wrd.world_defs: - num += 1 - return num diff --git a/leenkx/blender/lnx/material/cycles.py b/leenkx/blender/lnx/material/cycles.py index a6e5cd51..c22a1677 100644 --- a/leenkx/blender/lnx/material/cycles.py +++ b/leenkx/blender/lnx/material/cycles.py @@ -192,13 +192,27 @@ def parse(nodes, con: ShaderContext, import re as _re def _extract_vec3(s): - m = _re.match(r'vec3\s*\(\s*([^,\s\)]+)\s*\)', s) - if m and ',' not in m.group(1): - v = m.group(1) - return v, v, v - m = _re.match(r'vec3\s*\(\s*([^,\s]+)\s*,\s*([^,\s]+)\s*,\s*([^,\s\)]+)', s) - if m: - return m.group(1), m.group(2), m.group(3) + s = s.strip() + m = _re.match(r'vec3\s*\((.*)\)\s*$', s) + if not m: + return '1.0', '1.0', '1.0' + inner = m.group(1).strip() + parts = [] + depth = 0 + start = 0 + for i, ch in enumerate(inner): + if ch == '(': + depth += 1 + elif ch == ')': + depth -= 1 + elif ch == ',' and depth == 0: + parts.append(inner[start:i].strip()) + start = i + 1 + parts.append(inner[start:].strip()) + if len(parts) == 1: + return parts[0], parts[0], parts[0] + if len(parts) == 3: + return parts[0], parts[1], parts[2] return '1.0', '1.0', '1.0' def _try_float(val_str, default=0.0): @@ -209,14 +223,10 @@ def parse(nodes, con: ShaderContext, _sss_r, _sss_g, _sss_b = _extract_vec3(state.out_subsurface_radius) _sss_scale = state.out_subsurface_scale - _sss_scale_f = _try_float(_sss_scale, 0.05) - if _sss_scale_f != 1.0: - _sss_r = str(_try_float(_sss_r, 1.0) * _sss_scale_f) - _sss_g = str(_try_float(_sss_g, 0.2) * _sss_scale_f) - _sss_b = str(_try_float(_sss_b, 0.1) * _sss_scale_f) _sheen_tint_r, _sheen_tint_g, _sheen_tint_b = _extract_vec3(state.out_sheen_tint) _coat_tint_r, _coat_tint_g, _coat_tint_b = _extract_vec3(state.out_coat_tint) + _spec_tint_r, _spec_tint_g, _spec_tint_b = _extract_vec3(state.out_specular_tint) _sss_color_r, _sss_color_g, _sss_color_b = _extract_vec3(state.out_subsurface_color) mat_state.features = { @@ -233,6 +243,7 @@ def parse(nodes, con: ShaderContext, 'sheenTintB': _sheen_tint_b, 'subsurface': state.out_subsurface, 'subsurfaceAnisotropy': state.out_subsurface_anisotropy, + 'subsurfaceScale': _sss_scale, 'subsurfaceRadiusR': _sss_r, 'subsurfaceRadiusG': _sss_g, 'subsurfaceRadiusB': _sss_b, @@ -245,6 +256,9 @@ def parse(nodes, con: ShaderContext, 'transmissionRough': state.out_transmission_rough, 'ior': state.out_ior, 'thinWall': state.out_thin_wall, + 'specularTintR': _spec_tint_r, + 'specularTintG': _spec_tint_g, + 'specularTintB': _spec_tint_b, } state = None @@ -751,11 +765,11 @@ def vector_curve(name, fac, points): # Map vector return 'mix({0}[{1}], {0}[{1} + 1], ({2} - {3}[{1}]) * (1.0 / ({3}[{1} + 1] - {3}[{1}]) ))'.format(ys_var, index_var, fac_var, facs_var) -def write_normal(inp): +def write_normal(inp, target_var='n'): if inp.is_linked and inp.links[0].from_node.type != 'GROUP_INPUT': normal_res = parse_vector_input(inp) if normal_res != None: - state.curshader.write('n = {0};'.format(normal_res)) + state.curshader.write('{0} = {1};'.format(target_var, normal_res)) def is_parsed(node_store_name: str): diff --git a/leenkx/blender/lnx/material/cycles_nodes/nodes_shader.py b/leenkx/blender/lnx/material/cycles_nodes/nodes_shader.py index 36470dd3..9d7a1cac 100644 --- a/leenkx/blender/lnx/material/cycles_nodes/nodes_shader.py +++ b/leenkx/blender/lnx/material/cycles_nodes/nodes_shader.py @@ -77,17 +77,12 @@ def parse_mixshader(node: bpy.types.ShaderNodeMixShader, out_socket: NodeSocket, state.curshader.write('{0}float {1} = 1.0 - {2};'.format(prefix, fac_inv_var, fac_var)) mat_state.emission_type = mat_state.EmissionType.NO_EMISSION - sss_before_1 = mat_state.needs_sss o1 = c.parse_shader_input(node.inputs[1]) - sss_1 = mat_state.needs_sss ek1 = mat_state.emission_type mat_state.emission_type = mat_state.EmissionType.NO_EMISSION - mat_state.needs_sss = sss_before_1 # Reset to state before parsing input 1 o2 = c.parse_shader_input(node.inputs[2]) - sss_2 = mat_state.needs_sss ek2 = mat_state.emission_type - mat_state.needs_sss = sss_1 or sss_2 if state.parse_surface: fm = '{0} * {3} + {1} * {2}' # fac mix template: a*fac_inv + b*fac @@ -126,17 +121,12 @@ def parse_mixshader(node: bpy.types.ShaderNodeMixShader, out_socket: NodeSocket, def parse_addshader(node: bpy.types.ShaderNodeAddShader, out_socket: NodeSocket, state: ParserState) -> None: mat_state.emission_type = mat_state.EmissionType.NO_EMISSION - sss_before_1 = mat_state.needs_sss o1 = c.parse_shader_input(node.inputs[0]) - sss_1 = mat_state.needs_sss ek1 = mat_state.emission_type mat_state.emission_type = mat_state.EmissionType.NO_EMISSION - mat_state.needs_sss = sss_before_1 # Reset to state before parsing input 0 o2 = c.parse_shader_input(node.inputs[1]) - sss_2 = mat_state.needs_sss ek2 = mat_state.emission_type - mat_state.needs_sss = sss_1 or sss_2 if state.parse_surface: am = '{0} * 0.5 + {1} * 0.5' # add mix template (average) @@ -179,8 +169,6 @@ if bpy.app.version < (2, 91, 0): if state.parse_surface: c.write_normal(node.inputs['Normal']) state.out_basecol = c.parse_vector_input(node.inputs['Base Color']) - if node.inputs[SUBSURFACE].is_linked or node.inputs[SUBSURFACE].default_value > 0.0: - mat_state.needs_sss = True state.out_subsurface = c.parse_value_input(node.inputs[SUBSURFACE]) state.out_subsurface_radius = c.parse_vector_input(node.inputs[SUBSURFACE_RADIUS]) state.out_subsurface_color = c.parse_vector_input(node.inputs[SUBSURFACE_COLOR]) @@ -194,6 +182,9 @@ if bpy.app.version < (2, 91, 0): state.out_sheen_tint = 'vec3({})'.format(c.parse_value_input(node.inputs[SHEEN_TINT])) state.out_clearcoat = c.parse_value_input(node.inputs[CLEARCOAT]) state.out_clearcoat_rough = c.parse_value_input(node.inputs[CLEARCOAT_ROUGHNESS]) + coat_normal_socket = node.inputs.get(CLEARCOAT_NORMAL) + if coat_normal_socket is not None: + c.write_normal(coat_normal_socket, target_var='nCoat') state.out_transmission = c.parse_value_input(node.inputs[TRANSMISSION]) trans_rough_socket = node.inputs.get(TRANSMISSION_ROUGHNESS) if trans_rough_socket is not None: @@ -216,8 +207,6 @@ if bpy.app.version >= (2, 91, 0) and bpy.app.version < (4, 0, 0): if state.parse_surface: c.write_normal(node.inputs['Normal']) state.out_basecol = c.parse_vector_input(node.inputs['Base Color']) - if node.inputs[SUBSURFACE].is_linked or node.inputs[SUBSURFACE].default_value > 0.0: - mat_state.needs_sss = True state.out_subsurface = c.parse_value_input(node.inputs[SUBSURFACE]) state.out_subsurface_radius = c.parse_vector_input(node.inputs[SUBSURFACE_RADIUS]) state.out_subsurface_color = c.parse_vector_input(node.inputs[SUBSURFACE_COLOR]) @@ -231,6 +220,9 @@ if bpy.app.version >= (2, 91, 0) and bpy.app.version < (4, 0, 0): state.out_sheen_tint = 'vec3({})'.format(c.parse_value_input(node.inputs[SHEEN_TINT])) state.out_clearcoat = c.parse_value_input(node.inputs[CLEARCOAT]) state.out_clearcoat_rough = c.parse_value_input(node.inputs[CLEARCOAT_ROUGHNESS]) + coat_normal_socket = node.inputs.get(CLEARCOAT_NORMAL) + if coat_normal_socket is not None: + c.write_normal(coat_normal_socket, target_var='nCoat') state.out_transmission = c.parse_value_input(node.inputs[TRANSMISSION]) state.out_transmission_rough = c.parse_value_input(node.inputs[TRANSMISSION_ROUGHNESS]) state.out_tangent = c.parse_vector_input(node.inputs[TANGENT]) @@ -256,8 +248,6 @@ if bpy.app.version >= (4, 0, 0): sss_input = node.inputs.get(SUBSURFACE) if sss_input is not None: - if sss_input.is_linked or sss_input.default_value > 0.0: - mat_state.needs_sss = True state.out_subsurface = c.parse_value_input(sss_input) sss_radius_input = node.inputs.get(SUBSURFACE_RADIUS) @@ -331,6 +321,9 @@ if bpy.app.version >= (4, 0, 0): coat_tint_socket = node.inputs.get(COAT_TINT) if coat_tint_socket is not None: state.out_coat_tint = c.parse_vector_input(coat_tint_socket) + coat_normal_socket = node.inputs.get(CLEARCOAT_NORMAL) + if coat_normal_socket is not None: + c.write_normal(coat_normal_socket, target_var='nCoat') trans_socket = node.inputs.get(TRANSMISSION) if trans_socket is not None: @@ -507,8 +500,6 @@ def parse_bsdfrefraction(node: bpy.types.ShaderNodeBsdfRefraction, out_socket: N def parse_subsurfacescattering(node: bpy.types.ShaderNodeSubsurfaceScattering, out_socket: NodeSocket, state: ParserState) -> None: if state.parse_surface: - # Mark that this material needs SSS - mat_state.needs_sss = True c.write_normal(node.inputs['Normal']) state.out_basecol = c.parse_vector_input(node.inputs['Color']) state.out_subsurface = c.parse_value_input(node.inputs['Scale']) diff --git a/leenkx/blender/lnx/material/cycles_nodes/nodes_texture.py b/leenkx/blender/lnx/material/cycles_nodes/nodes_texture.py index 3c27a48c..989842f7 100644 --- a/leenkx/blender/lnx/material/cycles_nodes/nodes_texture.py +++ b/leenkx/blender/lnx/material/cycles_nodes/nodes_texture.py @@ -588,7 +588,7 @@ def parse_tex_voronoi(node: bpy.types.ShaderNodeTexVoronoi, out_socket: bpy.type co = 'bposition' scale = c.get_value_input(node, ['Scale']) - exp = c.get_value_input(node, ['Exponent']) + exp = c.get_value_input(node, ['Exponent']) if m == 3 else '1.0' randomness = c.get_value_input(node, ['Randomness']) # Color or Position diff --git a/leenkx/blender/lnx/material/make.py b/leenkx/blender/lnx/material/make.py index 04d6f3ea..e13a49fa 100644 --- a/leenkx/blender/lnx/material/make.py +++ b/leenkx/blender/lnx/material/make.py @@ -64,8 +64,6 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]], shader_data_name = material.lnx_custom_material bind_constants = {'mesh': []} bind_textures = {'mesh': []} - mat_uses_sss = False - make_shader.make_instancing_and_skinning(material, mat_users) for idx, item in enumerate(material.lnx_bind_textures_list): @@ -82,11 +80,11 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]], log.warn(f'Material "{material.name}": skipping export of bind texture at slot {idx + 1} ("{item.uniform_name}") with no image selected') elif not wrd.lnx_batch_materials or material.name.startswith('lnxdefault'): - rpasses, shader_data, shader_data_name, bind_constants, bind_textures, mat_uses_sss = make_shader.build(material, mat_users, mat_lnxusers) + rpasses, shader_data, shader_data_name, bind_constants, bind_textures = make_shader.build(material, mat_users, mat_lnxusers) sd = shader_data.sd else: result = mat_batch.get(material) - rpasses, shader_data, shader_data_name, bind_constants, bind_textures, mat_uses_sss = result + rpasses, shader_data, shader_data_name, bind_constants, bind_textures = result sd = shader_data.sd sss_used = False @@ -107,74 +105,144 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]], if material.lnx_material_id != 0: c['bind_constants'].append({'name': 'materialID', 'intValue': material.lnx_material_id}) - if material.lnx_material_id == 2: - wrd.world_defs += '_Hair' - - elif rpdat.rp_sss_state != 'Off': - const = {'name': 'materialID'} - # Use per-material SSS flag from shader build - if mat_uses_sss: - const['intValue'] = 2 - sss_used = True - if '_SSS' not in wrd.world_defs: - wrd.world_defs += '_SSS' - else: - const['intValue'] = 0 - c['bind_constants'].append(const) - # extended BRDF parameters as bind_constants if rpdat.rp_renderer == 'Deferred': feats = mat_state.features - def try_float(val_str, default=1.0): + import re as _re_mix + _mix_re = _re_mix.compile(r'^\s*([+-]?\d*\.?\d+(?:[eE][+-]?\d+)?)\s*\*\s*\w+\s*([+-])\s*([+-]?\d*\.?\d+(?:[eE][+-]?\d+)?)\s*\*\s*\w+\s*$') + + _term_re = _re_mix.compile(r'([+-]?\d*\.?\d+(?:[eE][+-]?\d+)?)\s*\*\s*[^\s*]+(?:\s*\*\s*[^\s*]+)*') + _term_pair_re = _re_mix.compile(r'([+-]?\d*\.?\d+(?:[eE][+-]?\d+)?)\s*\*\s*([^\s*]+(?:\s*\*\s*[^\s*]+)*)') + + def _parse_terms(val_str): + val_str = str(val_str).strip() + pairs = _term_pair_re.findall(val_str) + return [(float(c), v.replace(' ', '')) for c, v in pairs] + + def eval_filtered_expr(val_str, filter_str): + if val_str is None or filter_str is None: + return None + filter_terms = _parse_terms(filter_str) + if len(filter_terms) < 2: + return None + filter_map = {} + for coeff, var_prod in filter_terms: + filter_map[var_prod] = coeff + val_terms = _parse_terms(val_str) + if len(val_terms) < 2: + return None + filtered_coeffs = [] + for coeff, var_prod in val_terms: + fc = filter_map.get(var_prod) + if fc is not None and fc != 0.0: + filtered_coeffs.append(coeff) + if not filtered_coeffs: + return None + if all(c == filtered_coeffs[0] for c in filtered_coeffs): + return filtered_coeffs[0] + return None + + def eval_mix_expr(val_str): + if val_str is None: + return None + val_str = str(val_str).strip() try: return float(val_str) except (ValueError, TypeError): - return default + pass + m = _mix_re.match(val_str) + if m: + a = float(m.group(1)) + op = m.group(2) + b = float(m.group(3)) + if op == '+' and a == b: + return a + if op == '-' and a == b: + return 0.0 + terms = _term_re.findall(val_str) + if terms and len(terms) >= 2: + coeffs = [float(t) for t in terms] + nonzero = [c for c in coeffs if c != 0.0] + if not nonzero: + return 0.0 + if all(c == nonzero[0] for c in nonzero): + return nonzero[0] + return None + + def feat_is_nonzero(val_str): + v = eval_mix_expr(val_str) + if v is None: + return True + return v != 0.0 + + def try_float(val_str, default=1.0): + v = eval_mix_expr(val_str) + return v if v is not None else default + + def is_constant(val_str): + return eval_mix_expr(val_str) is not None + + warned_nonconst = set() + + def ext_bc(name, val_str, default): + const = is_constant(val_str) + if not const and name not in warned_nonconst: + warned_nonconst.add(name) + log.warn(f'material "{material.name}": deferred ext BRDF param "{name}" has non-constant expression "{val_str}", using default {default} - per-pixel texture-driven params require deferred texturing (Phase 4)') + c['bind_constants'].append({'name': name, 'floatValue': try_float(val_str, default), 'is_constant': const}) has_ext_brdf = False - if feats.get('clearcoat', '0.0') not in ('0.0', '0', ''): - c['bind_constants'].append({'name': 'clearcoat', 'floatValue': try_float(feats.get('clearcoat', '1.0'))}) - c['bind_constants'].append({'name': 'clearcoatRough', 'floatValue': try_float(feats.get('clearcoatRough', '0.03'), 0.03)}) - c['bind_constants'].append({'name': 'coatIOR', 'floatValue': try_float(feats.get('coatIOR', '1.5'), 1.5)}) - c['bind_constants'].append({'name': 'coatTintR', 'floatValue': try_float(feats.get('coatTintR', '1.0'), 1.0)}) - c['bind_constants'].append({'name': 'coatTintG', 'floatValue': try_float(feats.get('coatTintG', '1.0'), 1.0)}) - c['bind_constants'].append({'name': 'coatTintB', 'floatValue': try_float(feats.get('coatTintB', '1.0'), 1.0)}) + if feat_is_nonzero(feats.get('clearcoat', '0.0')): + ext_bc('clearcoat', feats.get('clearcoat', '1.0'), 1.0) + ext_bc('clearcoatRough', feats.get('clearcoatRough', '0.03'), 0.03) + ext_bc('coatIOR', feats.get('coatIOR', '1.5'), 1.5) + ext_bc('coatTintR', feats.get('coatTintR', '1.0'), 1.0) + ext_bc('coatTintG', feats.get('coatTintG', '1.0'), 1.0) + ext_bc('coatTintB', feats.get('coatTintB', '1.0'), 1.0) if '_ClearCoat' not in wrd.world_defs: wrd.world_defs += '_ClearCoat' has_ext_brdf = True - if feats.get('sheen', '0.0') not in ('0.0', '0', ''): - c['bind_constants'].append({'name': 'sheen', 'floatValue': try_float(feats.get('sheen', '1.0'))}) - c['bind_constants'].append({'name': 'sheenRough', 'floatValue': try_float(feats.get('sheenRough', '0.5'), 0.5)}) - c['bind_constants'].append({'name': 'sheenTintR', 'floatValue': try_float(feats.get('sheenTintR', '1.0'), 1.0)}) - c['bind_constants'].append({'name': 'sheenTintG', 'floatValue': try_float(feats.get('sheenTintG', '1.0'), 1.0)}) - c['bind_constants'].append({'name': 'sheenTintB', 'floatValue': try_float(feats.get('sheenTintB', '1.0'), 1.0)}) + if feat_is_nonzero(feats.get('sheen', '0.0')): + ext_bc('sheen', feats.get('sheen', '1.0'), 1.0) + ext_bc('sheenRough', feats.get('sheenRough', '0.5'), 0.5) + ext_bc('sheenTintR', feats.get('sheenTintR', '1.0'), 1.0) + ext_bc('sheenTintG', feats.get('sheenTintG', '1.0'), 1.0) + ext_bc('sheenTintB', feats.get('sheenTintB', '1.0'), 1.0) if '_Sheen' not in wrd.world_defs: wrd.world_defs += '_Sheen' has_ext_brdf = True - if feats.get('subsurface', '0.0') not in ('0.0', '0', '') and rpdat.rp_sss_state != 'Off' and not mat_uses_sss: - c['bind_constants'].append({'name': 'subsurface', 'floatValue': try_float(feats.get('subsurface', '1.0'))}) - c['bind_constants'].append({'name': 'subsurfaceAnisotropy', 'floatValue': try_float(feats.get('subsurfaceAnisotropy', '0.0'), 0.0)}) - c['bind_constants'].append({'name': 'subsurfaceRadiusR', 'floatValue': try_float(feats.get('subsurfaceRadiusR', '1.0'), 1.0)}) - c['bind_constants'].append({'name': 'subsurfaceRadiusG', 'floatValue': try_float(feats.get('subsurfaceRadiusG', '0.2'), 0.2)}) - c['bind_constants'].append({'name': 'subsurfaceRadiusB', 'floatValue': try_float(feats.get('subsurfaceRadiusB', '0.1'), 0.1)}) - c['bind_constants'].append({'name': 'subsurfaceColorR', 'floatValue': try_float(feats.get('subsurfaceColorR', '0.8'), 0.8)}) - c['bind_constants'].append({'name': 'subsurfaceColorG', 'floatValue': try_float(feats.get('subsurfaceColorG', '0.8'), 0.8)}) - c['bind_constants'].append({'name': 'subsurfaceColorB', 'floatValue': try_float(feats.get('subsurfaceColorB', '0.8'), 0.8)}) - if '_Subsurface' not in wrd.world_defs: - wrd.world_defs += '_Subsurface' + if feat_is_nonzero(feats.get('subsurface', '0.0')) and rpdat.rp_sss_state != 'Off': + ext_bc('subsurface', feats.get('subsurface', '1.0'), 1.0) + ext_bc('subsurfaceAnisotropy', feats.get('subsurfaceAnisotropy', '0.0'), 0.0) + ext_bc('subsurfaceScale', feats.get('subsurfaceScale', '0.05'), 0.05) + ext_bc('subsurfaceRadiusR', feats.get('subsurfaceRadiusR', '1.0'), 1.0) + ext_bc('subsurfaceRadiusG', feats.get('subsurfaceRadiusG', '0.2'), 0.2) + ext_bc('subsurfaceRadiusB', feats.get('subsurfaceRadiusB', '0.1'), 0.1) + ext_bc('subsurfaceColorR', feats.get('subsurfaceColorR', '0.8'), 0.8) + ext_bc('subsurfaceColorG', feats.get('subsurfaceColorG', '0.8'), 0.8) + ext_bc('subsurfaceColorB', feats.get('subsurfaceColorB', '0.8'), 0.8) + if '_SSS' not in wrd.world_defs: + wrd.world_defs += '_SSS' has_ext_brdf = True - if feats.get('anisotropy', '0.0') not in ('0.0', '0', ''): - c['bind_constants'].append({'name': 'anisotropy', 'floatValue': try_float(feats.get('anisotropy', '1.0'))}) - c['bind_constants'].append({'name': 'anisoRot', 'floatValue': try_float(feats.get('anisoRot', '0.0'), 0.0)}) + sss_used = True + if feat_is_nonzero(feats.get('anisotropy', '0.0')): + ext_bc('anisotropy', feats.get('anisotropy', '1.0'), 1.0) + ext_bc('anisoRot', feats.get('anisoRot', '0.0'), 0.0) if '_Anisotropy' not in wrd.world_defs: wrd.world_defs += '_Anisotropy' has_ext_brdf = True - if feats.get('transmission', '0.0') not in ('0.0', '0', ''): - c['bind_constants'].append({'name': 'transmission', 'floatValue': try_float(feats.get('transmission', '1.0'))}) - c['bind_constants'].append({'name': 'transmissionRough', 'floatValue': try_float(feats.get('transmissionRough', '0.0'), 0.0)}) - c['bind_constants'].append({'name': 'ior', 'floatValue': try_float(feats.get('ior', '1.45'), 1.45)}) - c['bind_constants'].append({'name': 'thinWall', 'floatValue': try_float(feats.get('thinWall', '0.0'), 0.0)}) + if feat_is_nonzero(feats.get('transmission', '0.0')): + transm_str = feats.get('transmission', '1.0') + ext_bc('transmission', transm_str, 1.0) + ext_bc('transmissionRough', feats.get('transmissionRough', '0.0'), 0.0) + ior_str = feats.get('ior', '1.45') + if not is_constant(ior_str): + filtered = eval_filtered_expr(ior_str, transm_str) + if filtered is not None: + ior_str = str(filtered) + ext_bc('ior', ior_str, 1.45) + ext_bc('thinWall', feats.get('thinWall', '0.0'), 0.0) if '_Transmission' not in wrd.world_defs: wrd.world_defs += '_Transmission' has_ext_brdf = True @@ -182,6 +250,11 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]], if has_ext_brdf and '_ExtBRDF' not in wrd.world_defs: wrd.world_defs += '_ExtBRDF' + if has_ext_brdf: + ext_bc('specularTintR', feats.get('specularTintR', '1.0'), 1.0) + ext_bc('specularTintG', feats.get('specularTintG', '1.0'), 1.0) + ext_bc('specularTintB', feats.get('specularTintB', '1.0'), 1.0) + # assign materialID for extended BRDF if not already set # coexist with Scene.hx for last materialID in bind_constants if has_ext_brdf and material.lnx_material_id == 0: @@ -191,6 +264,11 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]], c['bind_constants'].append({'name': 'materialID', 'intValue': ext_id}) else: log.warn(f'material "{material.name}": extended BRDF material limit (15) exceeded, extended BRDF will be disabled for this material') + has_ext_brdf = False + + has_matid = any(bc.get('name') == 'materialID' for bc in c['bind_constants'] if isinstance(bc, dict)) + if not has_matid: + c['bind_constants'].append({'name': 'materialID', 'intValue': 0}) # TODO: Mesh only material batching if wrd.lnx_batch_materials: diff --git a/leenkx/blender/lnx/material/make_cluster.py b/leenkx/blender/lnx/material/make_cluster.py index ae896c52..d46148b1 100644 --- a/leenkx/blender/lnx/material/make_cluster.py +++ b/leenkx/blender/lnx/material/make_cluster.py @@ -77,6 +77,8 @@ def write(vert: shader.Shader, frag: shader.Shader): if is_transparent_shadows: frag.add_uniform('sampler2D shadowMapSpotTransparent[4]', included=True) frag.add_uniform('mat4 LWVPSpotArray[maxLightsCluster]', link='_biasLightWorldViewProjectionMatrixSpotArray', included=True) + if is_shadows_atlas: + frag.add_uniform('vec4 tileBoundsSpotArray[maxLightsCluster]', link='_tileBoundsSpotArray', included=True) frag.write('for (int i = 0; i < min(numLights, maxLightsCluster); i++) {') frag.write('int li = int(texelFetch(clustersData, ivec2(clusterI, i + 1), 0).r * 255);') @@ -112,13 +114,13 @@ def write(vert: shader.Shader, frag: shader.Shader): frag.add_uniform('vec3 eye', '_cameraPosition') frag.write(', gbufferD, invVP, eye') if '_ClearCoat' in wrd.world_defs: - frag.write(', clearcoat, clearcoatRough, coatIOR, coatTint') + frag.write(', clearcoat, clearcoatRough, coatIOR, coatTint, nCoat') if '_Sheen' in wrd.world_defs: frag.write(', sheen, sheenRough, sheenTint') if '_Anisotropy' in wrd.world_defs: frag.write(', anisotropy, anisoRot, tangent') - if '_Subsurface' in wrd.world_defs: - frag.write(', subsurface, subsurfaceColor, subsurfaceRadius, subsurfaceAnisotropy') + if '_SSS' in wrd.world_defs: + frag.write(', subsurface, subsurfaceColor, subsurfaceRadius * subsurfaceScale, subsurfaceAnisotropy') if '_Transmission' in wrd.world_defs: frag.write(', transmission, transmissionRough, ior, thinWall') frag.write(');') diff --git a/leenkx/blender/lnx/material/make_mesh.py b/leenkx/blender/lnx/material/make_mesh.py index 65af6032..811338ba 100644 --- a/leenkx/blender/lnx/material/make_mesh.py +++ b/leenkx/blender/lnx/material/make_mesh.py @@ -74,8 +74,6 @@ def make(context_id, rpasses): con['color_attachments'] = [attachment_format, attachment_format] if '_gbuffer2' in wrd.world_defs: con['color_attachments'].append(attachment_format) - if '_Anisotropy' in wrd.world_defs: - con['color_attachments'].append('RGBA64') con_mesh = mat_state.data.add_context(con) mat_state.con_mesh = con_mesh @@ -156,6 +154,8 @@ def make_base(con_mesh, parse_opacity): vert.add_out('vec3 wnormal') make_attrib.write_norpos(con_mesh, vert) frag.write_attrib('vec3 n = normalize(wnormal);') + if '_ClearCoat' in wrd.world_defs: + frag.write_attrib('vec3 nCoat = vec3(0.0, 0.0, 0.0);') if mat_state.material.lnx_two_sided: frag.write('if (!gl_FrontFacing) n *= -1;') # Flip normal when drawing back-face @@ -254,16 +254,13 @@ def make_deferred(con_mesh, rpasses): frag.write('n.xy = n.z >= 0.0 ? n.xy : octahedronWrap(n.xy);') is_shadeless = mat_state.emission_type == mat_state.EmissionType.SHADELESS - ext_brdf_defs = ('_ClearCoat', '_Sheen', '_Anisotropy', '_Subsurface', '_Transmission') + ext_brdf_defs = ('_ClearCoat', '_Sheen', '_Anisotropy', '_SSS', '_Transmission') has_ext_brdf = any(d in wrd.world_defs for d in ext_brdf_defs) - if is_shadeless or '_SSS' in wrd.world_defs or '_Hair' in wrd.world_defs or has_ext_brdf: + if is_shadeless or has_ext_brdf: frag.write('uint matid = 0;') if is_shadeless: frag.write('matid = 1;') frag.write('basecol = emissionCol;') - if '_SSS' in wrd.world_defs or '_Hair' in wrd.world_defs: - frag.add_uniform('int materialID') - frag.write('if (materialID == 2) matid = 2;') if has_ext_brdf: frag.add_uniform('int materialID') frag.write('if (materialID >= 3) matid = uint(materialID);') @@ -283,6 +280,14 @@ def make_deferred(con_mesh, rpasses): if mat_state.material.lnx_ignore_irradiance: frag.write('fragColor[GBUF_IDX_2].b = 1.0;') + if '_Anisotropy' in wrd.world_defs: + frag.write('#ifdef _Anisotropy') + if con_mesh.is_elem('tang'): + frag.write('fragColor[GBUF_IDX_2].a = encodeTangent(TBN[0], normalize(wnormal));') + else: + frag.write('fragColor[GBUF_IDX_2].a = -1.0;') + frag.write('#endif') + # Even if the material doesn't use emission we need to write to the # emission buffer (if used) to prevent undefined behaviour frag.write('#ifdef _EmissionShaded') @@ -290,14 +295,21 @@ def make_deferred(con_mesh, rpasses): frag.write('#endif') if '_SSRefraction' in wrd.world_defs or '_VoxelRefract' in wrd.world_defs: - frag.write('fragColor[GBUF_IDX_REFRACTION] = vec4(1.0, 0.0, 0.0, 1.0);') + frag.write('fragColor[GBUF_IDX_REFRACTION] = vec4(packIOR(1.0), 0.0, 0.0, 1.0);') - if '_Anisotropy' in wrd.world_defs: - frag.write('#ifdef _Anisotropy') - if con_mesh.is_elem('tang'): - frag.write('fragColor[GBUF_IDX_3] = vec4(normalize(TBN[0]), 0.0);') - else: - frag.write('fragColor[GBUF_IDX_3] = vec4(0.0, 0.0, 0.0, 0.0);') + if '_ClearCoat' in wrd.world_defs: + frag.write('#ifdef _ClearCoat') + frag.write('if (dot(nCoat, nCoat) > 0.0) {') + frag.write(' vec3 nCoatNorm = normalize(nCoat);') + frag.write(' nCoatNorm /= (abs(nCoatNorm.x) + abs(nCoatNorm.y) + abs(nCoatNorm.z));') + frag.write(' nCoatNorm.xy = nCoatNorm.z >= 0.0 ? nCoatNorm.xy : octahedronWrap(nCoatNorm.xy);') + frag.write(' fragColor[GBUF_IDX_COAT_NORMAL] = vec4(nCoatNorm.xy, 0.0, 0.0);') + frag.write('} else {') + frag.write(' vec3 nNorm = normalize(n);') + frag.write(' nNorm /= (abs(nNorm.x) + abs(nNorm.y) + abs(nNorm.z));') + frag.write(' nNorm.xy = nNorm.z >= 0.0 ? nNorm.xy : octahedronWrap(nNorm.xy);') + frag.write(' fragColor[GBUF_IDX_COAT_NORMAL] = vec4(nNorm.xy, 0.0, 0.0);') + frag.write('}') frag.write('#endif') return con_mesh @@ -403,6 +415,9 @@ def make_forward_mobile(con_mesh): frag.add_include('std/shadows.glsl') frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True) frag.add_uniform('vec3 eye', '_cameraPosition') + if is_shadows_atlas: + frag.add_uniform('vec4 tileBoundsSunArray[maxLights * shadowmapCascades]', '_tileBoundsSunArray', included=True) + frag.write('tileBounds = tileBoundsSunArray[0];') frag.write(f'svisibility = shadowTestCascade({shadowmap_sun}, eye, wposition + n * shadowsBias * 10, shadowsBias, opacity != 1.0);') else: frag.write('if (lightPosition.w > 0.0) {') @@ -582,7 +597,7 @@ def make_forward(con_mesh): frag.write('fragColor[0] = vec4(direct + indirect, packFloat2(occlusion, specular));') frag.write('fragColor[1] = vec4(n.xy, roughness, metallic);') if rpdat.rp_ss_refraction or rpdat.lnx_voxelgi_refract: - frag.write(f'fragColor[2] = vec4(1.0, 0.0, 0.0, 1.0);') + frag.write(f'fragColor[2] = vec4(packIOR(1.0), 0.0, 0.0, 1.0);') else: frag.add_out('vec4 fragColor[1]') @@ -683,6 +698,8 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False): frag.write('vec3 albedo = surfaceAlbedo(basecol, metallic);') frag.write('vec3 f0 = surfaceF0(basecol, metallic);') + if '_ExtBRDF' in wrd.world_defs: + frag.write('f0 = mix(f0, basecol, specularTint);') if '_Brdf' in wrd.world_defs: frag.add_uniform('sampler2D senvmapBrdf', link='$brdf.png') @@ -752,6 +769,18 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False): frag.write('}') frag.write('vec3 direct = vec3(0.0);') + if '_ExtBRDF' in wrd.world_defs: + frag.write('#ifdef _ExtBRDF') + if '_Sheen' in wrd.world_defs: + frag.write('brdf_sheenWeight = sheenAttenuation(sheen, sheenRough, sheenTint, dotNV);') + if '_ClearCoat' in wrd.world_defs: + frag.write('brdf_coatWeight = coatAttenuation(clearcoat, coatIOR, nCoat, vVec);') + frag.write('brdf_coatTintAbsorb = coatTintAttenuation(clearcoat, coatTint, nCoat, vVec);') + frag.write('brdf_coatF0 = (coatIOR - 1.0) / (coatIOR + 1.0); brdf_coatF0 = brdf_coatF0 * brdf_coatF0;') + if '_Transmission' in wrd.world_defs: + frag.write('brdf_transmissionF0 = (ior - 1.0) / (ior + 1.0); brdf_transmissionF0 = brdf_transmissionF0 * brdf_transmissionF0;') + frag.write('#endif') + if '_Sun' in wrd.world_defs: frag.add_uniform('vec3 sunCol', '_sunColor') frag.add_uniform('vec3 sunDir', '_sunDirection') @@ -771,6 +800,9 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False): frag.add_include('std/shadows.glsl') frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True) frag.add_uniform('vec3 eye', '_cameraPosition') + if is_shadows_atlas: + frag.add_uniform('vec4 tileBoundsSunArray[maxLights * shadowmapCascades]', '_tileBoundsSunArray', included=True) + frag.write('tileBounds = tileBoundsSunArray[0];') frag.write(f'svisibility = shadowTestCascade({shadowmap_sun},') if is_transparent_shadows: frag.write(f'{shadowmap_sun_tr},') @@ -804,7 +836,21 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False): if '_VoxelShadow' in wrd.world_defs: frag.write('svisibility *= (1.0 - traceShadow(wposition, n, voxels, voxelsSDF, sunDir, clipmaps, gl_FragCoord.xy, velocity).r) * voxelgiShad;') frag.write('}') # receiveShadow - frag.write('direct += (lambertDiffuseBRDF(albedo, sdotNL) + specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * specular) * sunCol * svisibility;') + if '_ExtBRDF' in wrd.world_defs: + frag.write('float sunLayerWeight;') + frag.write('vec3 sdirect = applyExtBRDFLayers(') + frag.write(' lambertDiffuseBRDF(albedo, sdotNL) + specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * specular,') + frag.write(' albedo, f0, roughness, sdotNL, dotNV, sdotNH, sdotVH, n, sunDir, vVec, sh') + if '_ClearCoat' in wrd.world_defs: + frag.write(', clearcoat, clearcoatRough, coatIOR, coatTint, nCoat') + if '_Sheen' in wrd.world_defs: + frag.write(', sheen, sheenRough, sheenTint') + if '_Transmission' in wrd.world_defs: + frag.write(', transmission, transmissionRough, ior, thinWall') + frag.write(', sunLayerWeight);') + frag.write('direct += sdirect * sunCol * svisibility;') + else: + frag.write('direct += (lambertDiffuseBRDF(albedo, sdotNL) + specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * specular) * sunCol * svisibility;') # sun if '_SinglePoint' in wrd.world_defs: @@ -846,13 +892,13 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False): frag.add_uniform('vec3 eye', '_cameraPosition') frag.write(', gbufferD, invVP, eye') if '_ClearCoat' in wrd.world_defs: - frag.write(', clearcoat, clearcoatRough, coatIOR, coatTint') + frag.write(', clearcoat, clearcoatRough, coatIOR, coatTint, nCoat') if '_Sheen' in wrd.world_defs: frag.write(', sheen, sheenRough, sheenTint') if '_Anisotropy' in wrd.world_defs: frag.write(', anisotropy, anisoRot, tangent') - if '_Subsurface' in wrd.world_defs: - frag.write(', subsurface, subsurfaceColor, subsurfaceRadius, subsurfaceAnisotropy') + if '_SSS' in wrd.world_defs: + frag.write(', subsurface, subsurfaceColor, subsurfaceRadius * subsurfaceScale, subsurfaceAnisotropy') if '_Transmission' in wrd.world_defs: frag.write(', transmission, transmissionRough, ior, thinWall') frag.write(');') @@ -867,7 +913,7 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False): if '_VoxelRefract' in wrd.world_defs and parse_opacity: frag.write('if (opacity < 1.0) {') - frag.write(' vec3 refraction = traceRefraction(wposition, n, voxels, voxelsSDF, vVec, ior, roughness * roughness, clipmaps, gl_FragCoord.xy, velocity, opacity).rgb * (1.0 - F) * voxelgiRefr;') + frag.write(' vec3 refraction = traceRefraction(wposition, n, voxels, voxelsSDF, vVec, ior, roughness * roughness, clipmaps, gl_FragCoord.xy, velocity, opacity).rgb * (1.0 - F);') frag.write(' indirect = mix(refraction, indirect, opacity);') frag.write(' direct = mix(refraction, direct, opacity);') frag.write('}') @@ -882,25 +928,27 @@ def _write_material_attribs_default(frag: shader.Shader, parse_opacity: bool): # by the shader compiler frag.write('vec3 emissionCol;') # Extended BRDF parameters - frag.write('float subsurface = 0.0;') - frag.write('vec3 subsurfaceRadius = vec3(0.0);') - frag.write('vec3 subsurfaceColor = vec3(0.8);') - frag.write('vec3 specularTint = vec3(1.0);') - frag.write('float anisotropy = 0.0;') - frag.write('float anisoRot = 0.0;') - frag.write('float sheen = 0.0;') - frag.write('float sheenRough = 0.5;') - frag.write('vec3 sheenTint = vec3(1.0);') - frag.write('float clearcoat = 0.0;') - frag.write('float clearcoatRough = 0.03;') - frag.write('float transmission = 0.0;') - frag.write('float transmissionRough = 0.0;') - frag.write('float thinWall = 0.0;') - frag.write('vec3 tangent = vec3(0.0);') - frag.write('float subsurfaceScale = 0.05;') - frag.write('float subsurfaceAnisotropy = 0.0;') - frag.write('float coatIOR = 1.5;') - frag.write('vec3 coatTint = vec3(1.0);') + frag.write('float subsurface;') + frag.write('vec3 subsurfaceRadius;') + frag.write('vec3 subsurfaceColor;') + frag.write('vec3 specularTint;') + frag.write('float anisotropy;') + frag.write('float anisoRot;') + frag.write('float sheen;') + frag.write('float sheenRough;') + frag.write('vec3 sheenTint;') + frag.write('float clearcoat;') + frag.write('float clearcoatRough;') + frag.write('float transmission;') + frag.write('float transmissionRough;') + frag.write('float thinWall;') + frag.write('vec3 tangent;') + frag.write('float subsurfaceScale;') + frag.write('float subsurfaceAnisotropy;') + frag.write('float coatIOR;') + frag.write('vec3 coatTint;') + frag.write('float ior = 1.45;') if parse_opacity: frag.write('float opacity;') - frag.write('float ior = 1.45;') + else: + frag.write('float opacity = 1.0;') diff --git a/leenkx/blender/lnx/material/make_refract.py b/leenkx/blender/lnx/material/make_refract.py index 6b00856a..c2ca8187 100644 --- a/leenkx/blender/lnx/material/make_refract.py +++ b/leenkx/blender/lnx/material/make_refract.py @@ -40,34 +40,29 @@ def make(context_id): # Remove fragColor = ...; frag.main = frag.main[:frag.main.rfind('fragColor')] frag.write('\n') - - wrd = bpy.data.worlds['Lnx'] + frag.write('if (opacity <= 0.0) discard;') frag.write('n /= (abs(n.x) + abs(n.y) + abs(n.z));') frag.write('n.xy = n.z >= 0.0 ? n.xy : octahedronWrap(n.xy);') is_shadeless = mat_state.emission_type == mat_state.EmissionType.SHADELESS - if is_shadeless or '_SSS' in wrd.world_defs or '_Hair' in wrd.world_defs: + if is_shadeless: frag.write('uint matid = 0;') - if is_shadeless: - frag.write('matid = 1;') - frag.write('basecol = emissionCol;') - if '_SSS' in wrd.world_defs or '_Hair' in wrd.world_defs: - frag.add_uniform('int materialID') - frag.write('if (materialID == 2) matid = 2;') + frag.write('matid = 1;') + frag.write('basecol = emissionCol;') else: frag.write('const uint matid = 0;') if rpdat.rp_renderer == 'Deferred': frag.write('fragColor[0] = vec4(n.xy, roughness, 1.0);') frag.write('vec3 finalColor = direct + indirect;') - frag.write('fragColor[1] = vec4(finalColor * opacity, opacity);') + frag.write('fragColor[1] = vec4(finalColor * opacity, 1.0);') else: frag.write('vec3 finalColor = direct + indirect;') - frag.write('fragColor[0] = vec4(finalColor * opacity, opacity);') + frag.write('fragColor[0] = vec4(finalColor * opacity, 1.0);') frag.write('fragColor[1] = vec4(n.xy, roughness, 1.0);') - frag.write('fragColor[2] = vec4(ior, 1.0 - opacity, gl_FragCoord.z, 1.0);') + frag.write('fragColor[2] = vec4(packIOR(ior), 1.0 - opacity, gl_FragCoord.z, 1.0);') # frag.write('fragColor[2] = vec4(ior, 1.0 - opacity, packFloat2(basecol.r, basecol.g), basecol.b);') make_finalize.make(con_refract) diff --git a/leenkx/blender/lnx/material/make_shader.py b/leenkx/blender/lnx/material/make_shader.py index 6b13a0b8..2348dc86 100644 --- a/leenkx/blender/lnx/material/make_shader.py +++ b/leenkx/blender/lnx/material/make_shader.py @@ -57,9 +57,6 @@ def build(material: Material, mat_users: Dict[Material, List[Object]], mat_lnxus # Place empty material output to keep compiler happy.. mat_state.output_node = mat_state.nodes.new('ShaderNodeOutputMaterial') - # reset for each material - mat_state.needs_sss = False - wrd = bpy.data.worlds['Lnx'] rpdat = lnx.utils.get_rp() rpasses = mat_utils.get_rpasses(material) @@ -133,9 +130,7 @@ def build(material: Material, mat_users: Dict[Material, List[Object]], mat_lnxus shader_data_path = lnx.utils.get_fp_build() + '/compiled/Shaders/' + shader_data_name + '.lnx' assets.add_shader_data(shader_data_path) - # Store SSS state in the return tuple so it's preserved per-material - needs_sss_result = mat_state.needs_sss - return rpasses, mat_state.data, shader_data_name, bind_constants, bind_textures, needs_sss_result + return rpasses, mat_state.data, shader_data_name, bind_constants, bind_textures def write_shaders(rel_path: str, con: ShaderContext, rpass: str, matname: str) -> None: diff --git a/leenkx/blender/lnx/material/make_voxel.py b/leenkx/blender/lnx/material/make_voxel.py index 0b532933..c533ca78 100644 --- a/leenkx/blender/lnx/material/make_voxel.py +++ b/leenkx/blender/lnx/material/make_voxel.py @@ -69,42 +69,44 @@ def make_gi(context_id): frag.add_include('std/gbuffer.glsl') frag.add_include('std/brdf.glsl') frag.add_include('std/aabb.glsl') + frag.write_header('#define _VoxelPass') rpdat = lnx.utils.get_rp() frag.add_uniform('layout(r32ui) uimage3D voxels') frag.write('vec3 n;') + frag.write('vec3 nCoat = vec3(0.0, 0.0, 0.0);') frag.write('vec3 wposition;') frag.write('vec3 basecol;') frag.write('float roughness;') # frag.write('float metallic;') # frag.write('float occlusion;') # frag.write('float specular;') # - frag.write('vec3 emissionCol = vec3(0.0);') - frag.write('vec3 specularTint = vec3(1.0);') - frag.write('float subsurface = 0.0;') - frag.write('vec3 subsurfaceRadius = vec3(0.0);') - frag.write('vec3 subsurfaceColor = vec3(0.8);') - frag.write('float anisotropy = 0.0;') - frag.write('float anisoRot = 0.0;') - frag.write('float sheen = 0.0;') - frag.write('float sheenRough = 0.5;') - frag.write('vec3 sheenTint = vec3(1.0);') - frag.write('float clearcoat = 0.0;') - frag.write('float clearcoatRough = 0.03;') - frag.write('float transmission = 0.0;') - frag.write('float transmissionRough = 0.0;') - frag.write('float thinWall = 0.0;') - frag.write('vec3 tangent = vec3(0.0);') - frag.write('float subsurfaceScale = 0.05;') - frag.write('float subsurfaceAnisotropy = 0.0;') - frag.write('float coatIOR = 1.5;') - frag.write('vec3 coatTint = vec3(1.0);') + frag.write('vec3 emissionCol;') + frag.write('vec3 specularTint;') + frag.write('float subsurface;') + frag.write('vec3 subsurfaceRadius;') + frag.write('vec3 subsurfaceColor;') + frag.write('float anisotropy;') + frag.write('float anisoRot;') + frag.write('float sheen;') + frag.write('float sheenRough;') + frag.write('vec3 sheenTint;') + frag.write('float clearcoat;') + frag.write('float clearcoatRough;') + frag.write('float transmission;') + frag.write('float transmissionRough;') + frag.write('float thinWall;') + frag.write('vec3 tangent;') + frag.write('float subsurfaceScale;') + frag.write('float subsurfaceAnisotropy;') + frag.write('float coatIOR;') + frag.write('vec3 coatTint;') blend = mat_state.material.lnx_blending parse_opacity = blend or mat_utils.is_transluc(mat_state.material) + frag.write('float ior = 1.45;') if parse_opacity: frag.write('float opacity;') - frag.write('float ior;') else: frag.write('float opacity = 1.0;') @@ -271,10 +273,6 @@ def make_gi(context_id): frag.write_attrib('vec3 vVec = normalize(eyeDir);') frag.write_attrib('float dotNV = max(dot(N, vVec), 0.0);') - if '_Brdf' in wrd.world_defs: - frag.add_uniform('sampler2D senvmapBrdf', link='$brdf.png') - frag.write('vec2 envBRDF = texelFetch(senvmapBrdf, ivec2(vec2(dotNV, 1.0 - roughness) * 256.0), 0).xy;') - if '_Irr' in wrd.world_defs: frag.add_include('std/shirr.glsl') frag.add_uniform('vec4 shirr[7]', link='_envmapIrradiance') @@ -284,31 +282,12 @@ def make_gi(context_id): else: frag.write('vec3 envl = vec3(0.0);') - if '_Rad' in wrd.world_defs: - frag.add_uniform('sampler2D senvmapRadiance', link='_envmapRadiance') - frag.add_uniform('int envmapNumMipmaps', link='_envmapNumMipmaps') - frag.write('vec3 reflectionWorld = reflect(-vVec, N);') - frag.write('float lod = getMipFromRoughness(roughness, envmapNumMipmaps);') - frag.write('vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;') - if '_EnvLDR' in wrd.world_defs: frag.write('envl = pow(envl, vec3(2.2));') - if '_Rad' in wrd.world_defs: - frag.write('prefilteredColor = pow(prefilteredColor, vec3(2.2));') - frag.write('envl *= albedo;') - - if '_Brdf' in wrd.world_defs: - frag.write('vec3 F = f0 + (vec3(1.0) - f0) * pow(1.0 - abs(dot(N, vVec)), 5.0);') - frag.write('envl.rgb *= 1.0 - F;') - if '_Rad' in wrd.world_defs: - frag.write('envl += prefilteredColor * F;') - elif '_EnvCol' in wrd.world_defs: - frag.add_uniform('vec3 backgroundCol', link='_backgroundCol') - frag.write('envl += backgroundCol * F;') frag.add_uniform('float envmapStrength', link='_envmapStrength') - frag.write('envl *= envmapStrength * occlusion;') + frag.write('envl *= envmapStrength * voxelgiEnv * occlusion;') frag.add_include('std/light.glsl') is_shadows = '_ShadowMap' in wrd.world_defs @@ -343,6 +322,9 @@ def make_gi(context_id): frag.add_include('std/shadows.glsl') frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True) frag.add_uniform('vec3 eye', '_cameraPosition') + if is_shadows_atlas: + frag.add_uniform('vec4 tileBoundsSunArray[maxLights * shadowmapCascades]', '_tileBoundsSunArray', included=True) + frag.write('tileBounds = tileBoundsSunArray[0];') frag.write(f'svisibility = shadowTestCascade({shadowmap_sun},') if is_transparent_shadows: frag.write(f'{shadowmap_sun_tr},') @@ -364,7 +346,7 @@ def make_gi(context_id): frag.write(', false') frag.write(');') frag.write('}') # receiveShadow - frag.write('direct += (lambertDiffuseBRDF(albedo, sdotNL) + specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * specular) * sunCol * svisibility;') + frag.write('direct += vec3(sdotNL) * sunCol * svisibility;') # sun if '_SinglePoint' in wrd.world_defs: @@ -397,13 +379,13 @@ def make_gi(context_id): if '_Spot' in wrd.world_defs: frag.write(', true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight') if '_ClearCoat' in wrd.world_defs: - frag.write(', clearcoat, clearcoatRough, coatIOR, coatTint') + frag.write(', clearcoat, clearcoatRough, coatIOR, coatTint, N') if '_Sheen' in wrd.world_defs: frag.write(', sheen, sheenRough, sheenTint') if '_Anisotropy' in wrd.world_defs: frag.write(', anisotropy, anisoRot, tangent') - if '_Subsurface' in wrd.world_defs: - frag.write(', subsurface, subsurfaceColor, subsurfaceRadius, subsurfaceAnisotropy') + if '_SSS' in wrd.world_defs: + frag.write(', subsurface, subsurfaceColor, subsurfaceRadius * subsurfaceScale, subsurfaceAnisotropy') if '_Transmission' in wrd.world_defs: frag.write(', transmission, transmissionRough, ior, thinWall') frag.write(');') @@ -462,6 +444,8 @@ def make_gi(context_id): if is_transparent_shadows: frag.add_uniform('sampler2D shadowMapSpotTransparent[4]', included=True) frag.add_uniform('mat4 LWVPSpotArray[maxLightsCluster]', link='_biasLightWorldViewProjectionMatrixSpotArray', included=True) + if is_shadows_atlas: + frag.add_uniform('vec4 tileBoundsSpotArray[maxLightsCluster]', link='_tileBoundsSpotArray', included=True) frag.write('for (int i = 0; i < min(numLights, maxLightsCluster); i++) {') frag.write('int li = int(texelFetch(clustersData, ivec2(clusterI, i + 1), 0).r * 255);') @@ -488,13 +472,13 @@ def make_gi(context_id): frag.write('\t, vec2(lightsArray[li * 3].w, lightsArray[li * 3 + 1].w)') # scale frag.write('\t, lightsArraySpot[li * 2 + 1].xyz') # right if '_ClearCoat' in wrd.world_defs: - frag.write('\t, clearcoat, clearcoatRough, coatIOR, coatTint') + frag.write('\t, clearcoat, clearcoatRough, coatIOR, coatTint, N') if '_Sheen' in wrd.world_defs: frag.write('\t, sheen, sheenRough, sheenTint') if '_Anisotropy' in wrd.world_defs: frag.write('\t, anisotropy, anisoRot, tangent') - if '_Subsurface' in wrd.world_defs: - frag.write('\t, subsurface, subsurfaceColor, subsurfaceRadius, subsurfaceAnisotropy') + if '_SSS' in wrd.world_defs: + frag.write('\t, subsurface, subsurfaceColor, subsurfaceRadius * subsurfaceScale, subsurfaceAnisotropy') if '_Transmission' in wrd.world_defs: frag.write('\t, transmission, transmissionRough, ior, thinWall') frag.write(' );') diff --git a/leenkx/blender/lnx/material/mat_state.py b/leenkx/blender/lnx/material/mat_state.py index 63c2b344..3de76f86 100644 --- a/leenkx/blender/lnx/material/mat_state.py +++ b/leenkx/blender/lnx/material/mat_state.py @@ -38,6 +38,5 @@ texture_grad = False # Sample textures using textureGrad() con_mesh = None # Mesh context uses_instancing = False # Whether the current material has at least one user with instancing enabled emission_type = EmissionType.NO_EMISSION -needs_sss = False features = {} # tracks extended BRDF features used by current material next_ext_mat_id = 3 # auto assigned materialID for extended BRDF diff --git a/leenkx/blender/lnx/material/parser_state.py b/leenkx/blender/lnx/material/parser_state.py index c4574c72..2a990b93 100644 --- a/leenkx/blender/lnx/material/parser_state.py +++ b/leenkx/blender/lnx/material/parser_state.py @@ -118,6 +118,7 @@ class ParserState: self.out_subsurface_anisotropy: floatstr = '0.0' self.out_coat_ior: floatstr = '1.5' self.out_coat_tint: vec3str = 'vec3(1.0)' + self.out_coat_normal: vec3str = 'vec3(0.0)' def reset_outs(self): """Reset the shader output values to their default values.""" @@ -148,6 +149,7 @@ class ParserState: self.out_subsurface_anisotropy = '0.0' self.out_coat_ior = '1.5' self.out_coat_tint = 'vec3(1.0)' + self.out_coat_normal = 'vec3(0.0)' def get_outs(self) -> Tuple: """Return the shader output values as a tuple.""" diff --git a/leenkx/blender/lnx/props_renderpath.py b/leenkx/blender/lnx/props_renderpath.py index 05cbb9ea..ed10debf 100644 --- a/leenkx/blender/lnx/props_renderpath.py +++ b/leenkx/blender/lnx/props_renderpath.py @@ -543,12 +543,12 @@ class LnxRPListItem(bpy.types.PropertyGroup): name="MSAA", description="Samples per pixel usable for render paths drawing directly to framebuffer", default='1') lnx_voxelgi_cones: EnumProperty( - items=[('9', '9', '9'), + items=[('16', '16', '16'), + ('9', '9', '9'), ('5', '5', '5'), ('3', '3', '3'), - ('1', '1', '1'), ], - name="Cones", description="Number of cones to trace", default='5', update=assets.invalidate_shader_cache) + name="Cones", description="Number of cones to trace", default='9', update=assets.invalidate_shader_cache) lnx_voxelgi_diff: FloatProperty(name="Diffuse", description="", default=1.0, update=assets.invalidate_shader_cache) lnx_voxelgi_spec: FloatProperty(name="Reflection", description="", default=1.0, update=assets.invalidate_shader_cache) lnx_voxelgi_refr: FloatProperty(name="Refraction", description="", default=1.0, update=assets.invalidate_shader_cache) @@ -559,8 +559,7 @@ class LnxRPListItem(bpy.types.PropertyGroup): lnx_voxelgi_step: FloatProperty(name="Step", description="Step size", default=1.0, update=assets.invalidate_shader_cache) lnx_voxelgi_range: FloatProperty(name="Range", description="Maximum range", default=100.0, update=assets.invalidate_shader_cache) lnx_voxelgi_offset: FloatProperty(name="Offset", description="Multiplicative Offset for dealing with self occlusion", default=1.0, update=assets.invalidate_shader_cache) - lnx_voxelgi_aperture: FloatProperty(name="Aperture", description="Cone aperture for shadow trace", default=0.0, update=assets.invalidate_shader_cache) - lnx_sss_width: FloatProperty(name="Width", description="SSS blur strength", default=1.0, update=assets.invalidate_shader_cache) + lnx_voxelgi_aperture: FloatProperty(name="Aperture", description="Cone aperture for shadow trace", default=0.26, update=assets.invalidate_shader_cache) lnx_water_color: FloatVectorProperty(name="Color", size=3, default=[1, 1, 1], subtype='COLOR', min=0, max=1, update=assets.invalidate_shader_cache) lnx_water_level: FloatProperty(name="Level", default=0.0, update=assets.invalidate_shader_cache) lnx_water_displace: FloatProperty(name="Displace", default=1.0, update=assets.invalidate_shader_cache) diff --git a/leenkx/blender/lnx/props_ui.py b/leenkx/blender/lnx/props_ui.py index bdaed3c3..789ecde3 100644 --- a/leenkx/blender/lnx/props_ui.py +++ b/leenkx/blender/lnx/props_ui.py @@ -1646,9 +1646,6 @@ class LNX_PT_RenderPathRendererPanel(bpy.types.Panel): layout.prop(rpdat, 'lnx_samples_per_pixel') layout.prop(rpdat, 'lnx_texture_filter') layout.prop(rpdat, 'rp_sss_state') - col = layout.column() - col.enabled = rpdat.rp_sss_state != 'Off' - col.prop(rpdat, 'lnx_sss_width') layout.prop(rpdat, 'lnx_rp_displacement') if rpdat.lnx_rp_displacement == 'Tessellation': layout.label(text='Mesh') @@ -1890,7 +1887,7 @@ class LNX_PT_RenderPathVoxelsPanel(bpy.types.Panel): col.prop(rpdat, 'lnx_voxelgi_shadows', text='Shadows') col2.prop(rpdat, 'lnx_voxelgi_refract', text='Refraction') #col.prop(rpdat, 'lnx_voxelgi_clipmap_count') - #col.prop(rpdat, 'lnx_voxelgi_cones') + col.prop(rpdat, 'lnx_voxelgi_cones') col.prop(rpdat, 'rp_voxelgi_resolution') col.prop(rpdat, 'lnx_voxelgi_size') #col.prop(rpdat, 'rp_voxelgi_resolution_z') @@ -1906,10 +1903,10 @@ class LNX_PT_RenderPathVoxelsPanel(bpy.types.Panel): col.prop(rpdat, 'lnx_voxelgi_env') col.prop(rpdat, 'lnx_voxelgi_occ') col.label(text="Ray") - #col.prop(rpdat, 'lnx_voxelgi_offset') + col.prop(rpdat, 'lnx_voxelgi_offset') col.prop(rpdat, 'lnx_voxelgi_step') col.prop(rpdat, 'lnx_voxelgi_range') - #col.prop(rpdat, 'lnx_voxelgi_aperture') + col.prop(rpdat, 'lnx_voxelgi_aperture') class LNX_PT_RenderPathWorldPanel(bpy.types.Panel): bl_label = "World" diff --git a/leenkx/blender/lnx/write_data.py b/leenkx/blender/lnx/write_data.py index 9db6c343..8c672e80 100644 --- a/leenkx/blender/lnx/write_data.py +++ b/leenkx/blender/lnx/write_data.py @@ -665,7 +665,8 @@ class Main { if rpdat.rp_voxels == 'Voxel GI' or rpdat.rp_voxels == 'Voxel AO': f.write(""" public static inline var voxelgiClipmapCount = """ + str(rpdat.lnx_voxelgi_clipmap_count) + """; - public static inline var voxelgiVoxelSize = """ + str(round(rpdat.lnx_voxelgi_size * 100) / 100) + """;""") + public static inline var voxelgiVoxelSize = """ + str(round(rpdat.lnx_voxelgi_size * 100) / 100) + """; + public static inline var diffuseConeCount = """ + str(rpdat.lnx_voxelgi_cones) + """;""") if rpdat.rp_bloom: follow_blender = rpdat.lnx_bloom_follow_blender if bpy.app.version < (4, 3, 0) else False @@ -834,12 +835,12 @@ def write_compiledglsl(defs, make_variants): if '_SSRefraction' in wrd.world_defs or '_VoxelRefract' in wrd.world_defs: f.write(f'#define GBUF_IDX_REFRACTION {idx_refraction}\n') + idx_refraction += 1 - idx_anisotropy = idx_refraction - if '_Anisotropy' in wrd.world_defs: - f.write(f'#define GBUF_IDX_3 {idx_anisotropy}\n') + if '_ClearCoat' in wrd.world_defs: + f.write(f'#define GBUF_IDX_COAT_NORMAL {idx_refraction}\n') - ext_brdf_defs = ('_ClearCoat', '_Sheen', '_Anisotropy', '_Subsurface', '_Transmission') + ext_brdf_defs = ('_ClearCoat', '_Sheen', '_Anisotropy', '_SSS', '_Transmission') if any(d in wrd.world_defs for d in ext_brdf_defs): f.write('#ifndef _ExtBRDF\n') f.write('#define _ExtBRDF\n') @@ -855,8 +856,12 @@ def write_compiledglsl(defs, make_variants): if state.target == 'html5' or lnx.utils.get_gapi() == 'direct3d11': f.write("#define _FlipY\n") - f.write("""const float PI = 3.1415926535; -const float PI2 = PI * 2.0; + f.write("""#ifndef PI +#define PI 3.1415926535 +#endif +#ifndef PI2 +#define PI2 6.2831853071 +#endif const vec2 shadowmapSize = vec2(""" + str(shadowmap_size) + """, """ + str(shadowmap_size) + """); const float shadowmapCubePcfSize = """ + str((round(rpdat.lnx_pcfsize * 100) / 100) / 1000) + """; const int shadowmapCascades = """ + str(rpdat.rp_shadowmap_cascades) + """; @@ -1027,6 +1032,7 @@ const float compoDOFLength = """ + str(round(lens * 100) / 100) +"""; if rpdat.rp_voxels != 'Off': f.write("""const ivec3 voxelgiResolution = ivec3(""" + str(rpdat.rp_voxelgi_resolution) + """, """ + str(rpdat.rp_voxelgi_resolution) + """, """ + str(rpdat.rp_voxelgi_resolution) + """); const int voxelgiClipmapCount = """ + str(rpdat.lnx_voxelgi_clipmap_count) + """; +const int diffuseConeCount = """ + str(rpdat.lnx_voxelgi_cones) + """; const float voxelgiOcc = """ + str(round(rpdat.lnx_voxelgi_occ * 100) / 100) + """; const float voxelgiVoxelSize = """ + str(round(rpdat.lnx_voxelgi_size * 1000) / 1000) + """; const float voxelgiStep = """ + str(round(rpdat.lnx_voxelgi_step * 1000) / 1000) + """; @@ -1042,9 +1048,6 @@ const float voxelgiDiff = """ + str(round(rpdat.lnx_voxelgi_diff * 100) / 100) + const float voxelgiRefl = """ + str(round(rpdat.lnx_voxelgi_spec * 100) / 100) + """; const float voxelgiRefr = """ + str(round(rpdat.lnx_voxelgi_refr * 100) / 100) + """; """) - if rpdat.rp_sss or '_SSS' in wrd.world_defs: - f.write(f"const float sssWidth = {rpdat.lnx_sss_width / 10.0};\n") - # Skinning if rpdat.lnx_skin == 'On': f.write(