main #133

Merged
LeenkxTeam merged 4 commits from Onek8/LNXSDK:main into main 2026-08-10 17:45:09 +00:00
56 changed files with 3148 additions and 910 deletions

View File

@ -6,6 +6,7 @@
uniform sampler2D tex; uniform sampler2D tex;
uniform sampler2D gbuffer0; // Roughness uniform sampler2D gbuffer0; // Roughness
uniform sampler2D gbufferD; // Depth
uniform vec2 dirInv; uniform vec2 dirInv;
@ -14,19 +15,46 @@ out vec4 fragColor;
void main() { void main() {
float roughness = textureLod(gbuffer0, texCoord, 0.0).b; float roughness = textureLod(gbuffer0, texCoord, 0.0).b;
// if (roughness == 0.0) { // Always blur for now, non blured output can produce noise if (roughness >= 0.8) {
// fragColor.rgb = textureLod(tex, texCoord).rgb; fragColor.rgb = textureLod(tex, texCoord, 0.0).rgb;
// return; return;
// } }
if (roughness >= 0.8) { // No reflections if (roughness < 0.01) {
fragColor.rgb = textureLod(tex, texCoord, 0.0).rgb; fragColor.rgb = textureLod(tex, texCoord, 0.0).rgb;
return; return;
} }
fragColor.rgb = textureLod(tex, texCoord + dirInv * 2.5, 0.0).rgb; float blurRadius = 1.0 + roughness * 4.0;
fragColor.rgb += textureLod(tex, texCoord + dirInv * 1.5, 0.0).rgb; vec3 center = textureLod(tex, texCoord, 0.0).rgb;
fragColor.rgb += textureLod(tex, texCoord, 0.0).rgb;
fragColor.rgb += textureLod(tex, texCoord - dirInv * 1.5, 0.0).rgb; float centerDepth = textureLod(gbufferD, texCoord, 0.0).r;
fragColor.rgb += textureLod(tex, texCoord - dirInv * 2.5, 0.0).rgb;
fragColor.rgb /= vec3(5.0); 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);
} }

View File

@ -17,10 +17,12 @@ in vec3 wnormal;
+-------------------+-----------------++--------------+--------------+-----------------+--------------------+ +-------------------+-----------------++--------------+--------------+-----------------+--------------------+
| GBUF_IDX_1 | || base color (RGB) | occlusion/specular | | 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_EMISSION | _EmissionShaded || emission color (RGB) | 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". The indices as well as the GBUF_SIZE define are defined in "compiled.inc".
*/ */
@ -52,6 +54,10 @@ void main() {
#endif #endif
#ifdef _SSRefraction #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_2].a = -1.0;
#endif #endif
} }

View File

@ -7,7 +7,11 @@ out vec4 fragColor[GBUF_SIZE];
void main() { void main() {
fragColor[GBUF_IDX_0] = vec4(1.0, 1.0, 0.0, 1.0); 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); fragColor[GBUF_IDX_1] = vec4(color, 1.0);
#else
fragColor[GBUF_IDX_0] = vec4(color, 1.0);
#endif
#ifdef _EmissionShaded #ifdef _EmissionShaded
fragColor[GBUF_IDX_EMISSION] = vec4(0.0); fragColor[GBUF_IDX_EMISSION] = vec4(0.0);

View File

@ -8,12 +8,10 @@
#ifdef _Irr #ifdef _Irr
#include "std/shirr.glsl" #include "std/shirr.glsl"
#endif #endif
#ifdef _SSS
#include "std/sss.glsl"
#endif
#ifdef _SSRS #ifdef _SSRS
#include "std/ssrs.glsl" #include "std/ssrs.glsl"
#endif #endif
#include "std/brdf.glsl"
uniform sampler2D gbufferD; uniform sampler2D gbufferD;
uniform sampler2D gbuffer0; uniform sampler2D gbuffer0;
@ -25,6 +23,9 @@ uniform sampler2D gbuffer1;
#ifdef _EmissionShaded #ifdef _EmissionShaded
uniform sampler2D gbufferEmission; uniform sampler2D gbufferEmission;
#endif #endif
#ifdef _ClearCoat
uniform sampler2D gbufferCoatNormal;
#endif
#ifdef _VoxelGI #ifdef _VoxelGI
uniform sampler2D voxels_diffuse; uniform sampler2D voxels_diffuse;
@ -91,7 +92,7 @@ uniform mat4 invVP;
#ifdef _SinglePoint #ifdef _SinglePoint
//!uniform sampler2DShadow shadowMapSpot[1]; //!uniform sampler2DShadow shadowMapSpot[1];
//!uniform sampler2D shadowMapSpotTransparent[1]; //!uniform sampler2D shadowMapSpotTransparent[1];
//!uniform mat4 LWVPSpot[1]; //!uniform mat4 LWVPSpotArray[1];
#endif #endif
#ifdef _Clusters #ifdef _Clusters
//!uniform sampler2DShadow shadowMapSpot[4]; //!uniform sampler2DShadow shadowMapSpot[4];
@ -136,7 +137,7 @@ uniform vec2 cameraPlane;
#ifdef _ShadowMapTransparent #ifdef _ShadowMapTransparent
//!uniform sampler2D shadowMapSpotTransparent[1]; //!uniform sampler2D shadowMapSpotTransparent[1];
#endif #endif
//!uniform mat4 LWVPSpot[1]; //!uniform mat4 LWVPSpotArray[1];
#else #else
//!uniform samplerCubeShadow shadowMapPoint[1]; //!uniform samplerCubeShadow shadowMapPoint[1];
#ifdef _ShadowMapTransparent #ifdef _ShadowMapTransparent
@ -199,6 +200,7 @@ uniform vec3 sunCol;
uniform sampler2D shadowMapAtlasSunTransparent; uniform sampler2D shadowMapAtlasSunTransparent;
#endif #endif
#endif #endif
//!uniform vec4 tileBoundsSunArray[maxLights * shadowmapCascades];
#else #else
uniform sampler2DShadow shadowMap; uniform sampler2DShadow shadowMap;
#ifdef _ShadowMapTransparent #ifdef _ShadowMapTransparent
@ -235,6 +237,9 @@ uniform float time;
#endif #endif
#include "std/light.glsl" #include "std/light.glsl"
#ifdef _SSS
#include "std/sss.glsl"
#endif
in vec2 texCoord; in vec2 texCoord;
in vec3 viewRay; in vec3 viewRay;
@ -254,12 +259,40 @@ void main() {
float metallic; float metallic;
uint matid; uint matid;
unpackFloatInt16(g0.a, metallic, matid); unpackFloatInt16(g0.a, metallic, matid);
#ifdef _ExtBRDF
matid = min(matid, uint(MAX_MATERIALS - 1));
//!uniform vec4 materialParams[MAX_MATERIALS * 8];
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); vec2 occspec = unpackFloat2(g1.a);
// re-investigate clamp basecolor to prevent extreme values causing glitches // re-investigate clamp basecolor to prevent extreme values causing glitches
vec3 basecolor = min(g1.rgb, vec3(2.0)); vec3 basecolor = min(g1.rgb, vec3(2.0));
vec3 albedo = surfaceAlbedo(basecolor, metallic); vec3 albedo = surfaceAlbedo(basecolor, metallic);
vec3 f0 = surfaceF0(basecolor, metallic); vec3 f0 = surfaceF0(basecolor, metallic);
#ifdef _ExtBRDF
f0 = mix(f0, basecolor, vec3(matp6.y, matp6.z, matp6.w));
#endif
#ifdef _VRStereo #ifdef _VRStereo
bool isLeftEye = texCoord.x < 0.5; bool isLeftEye = texCoord.x < 0.5;
@ -279,10 +312,26 @@ void main() {
#endif #endif
float dotNV = max(dot(n, v), 0.0); 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 #ifdef _gbuffer2
vec4 g2 = textureLod(gbuffer2, texCoord, 0.0); vec4 g2 = textureLod(gbuffer2, texCoord, 0.0);
#endif #endif
#ifdef _Anisotropy
#ifdef _gbuffer2
vec3 wTangent = decodeTangent(g2.a, n);
#else
vec3 wTangent = vec3(0.0);
#endif
#endif
#ifdef _MicroShadowing #ifdef _MicroShadowing
occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel
@ -295,6 +344,44 @@ void main() {
vec3 F = f0; vec3 F = f0;
#endif #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 _VoxelAOvar
#ifndef _VoxelGI #ifndef _VoxelGI
// Envmap // Envmap
@ -302,9 +389,7 @@ void main() {
vec3 envl = shIrradiance(n, shirr); vec3 envl = shIrradiance(n, shirr);
#ifdef _gbuffer2 #ifdef _gbuffer2
if (g2.b < 0.5) { if (g2.b >= 0.5) {
envl = envl;
} else {
envl = vec3(0.0); envl = vec3(0.0);
} }
#endif #endif
@ -317,16 +402,21 @@ void main() {
#endif #endif
#ifdef _Rad #ifdef _Rad
#ifdef _Anisotropy
vec3 reflectionWorld = anisotropicIBLDirection(n, v, wTangent,
matp0.x, roughness);
#else
vec3 reflectionWorld = reflect(-v, n); vec3 reflectionWorld = reflect(-v, n);
#endif
float lod = getMipFromRoughness(roughness, envmapNumMipmaps); float lod = getMipFromRoughness(roughness, envmapNumMipmaps);
vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb; vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;
prefilteredColor = min(prefilteredColor, vec3(20.0)); prefilteredColor = min(prefilteredColor, vec3(20.0));
#endif #endif
#ifdef _EnvLDR #ifdef _EnvLDR
envl.rgb = pow(envl.rgb, vec3(2.2)); envl.rgb = srgbToLinear(envl.rgb);
#ifdef _Rad #ifdef _Rad
prefilteredColor = pow(prefilteredColor, vec3(2.2)); prefilteredColor = srgbToLinear(prefilteredColor);
#endif #endif
#endif #endif
@ -344,6 +434,68 @@ void main() {
#endif #endif
#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; envl.rgb *= envmapStrength * occspec.x;
fragColor.rgb = envl; fragColor.rgb = envl;
@ -352,11 +504,30 @@ void main() {
#ifdef _VoxelGI #ifdef _VoxelGI
fragColor.rgb = textureLod(voxels_diffuse, texCoord, 0.0).rgb * voxelgiDiff; fragColor.rgb = textureLod(voxels_diffuse, texCoord, 0.0).rgb * voxelgiDiff;
if(roughness < 1.0 && occspec.y > 0.0) if(roughness < 1.0) {
fragColor.rgb += textureLod(voxels_specular, texCoord, 0.0).rgb * occspec.y * voxelgiRefl; 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 #else
#ifdef _VoxelAOvar #ifdef _VoxelAOvar
fragColor.rgb = textureLod(voxels_ao, texCoord, 0.0).rgb * voxelgiOcc; fragColor.rgb = textureLod(voxels_ao, texCoord, 0.0).rgb;
#endif #endif
#endif #endif
@ -406,10 +577,42 @@ void main() {
float sdotVH = max(0.0, dot(v, sh)); float sdotVH = max(0.0, dot(v, sh));
float sdotNL = max(0.0, dot(n, sunDir)); float sdotNL = max(0.0, dot(n, sunDir));
vec3 svisibility = vec3(1.0); vec3 svisibility = vec3(1.0);
#ifdef _Anisotropy
vec3 sdirect;
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,
wTangent, sbitangent, n, sunDir, v, sdotNL, dotNV) * occspec.y;
} else {
sdirect = lambertDiffuseBRDF(albedo, sdotNL) +
specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * occspec.y;
}
#else
vec3 sdirect = lambertDiffuseBRDF(albedo, sdotNL) + vec3 sdirect = lambertDiffuseBRDF(albedo, sdotNL) +
specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * occspec.y; specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * occspec.y;
#endif
#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 _ShadowMap
#ifdef _ShadowMapAtlas
tileBounds = tileBoundsSunArray[0];
#endif
#ifdef _CSM #ifdef _CSM
svisibility = shadowTestCascade( svisibility = shadowTestCascade(
#ifdef _ShadowMapAtlas #ifdef _ShadowMapAtlas
@ -494,23 +697,17 @@ void main() {
fragColor.rgb += sdirect * sunCol * svisibility; 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 #ifdef _SSS
if (matid == 2) { #ifdef _ExtBRDF
if (matid >= 3u && matp3.z > 0.0) {
#ifdef _CSM #ifdef _CSM
int casi, casindex; int casi, casindex;
mat4 LWVP = getCascadeMat(distance(eye, p), casi, casindex); mat4 LWVP = getCascadeMat(distance(eye, p), casi, casindex);
#endif #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, LWVP, p, n, sunDir, lightPlane.y,
#ifdef _ShadowMapAtlas #ifdef _ShadowMapAtlas
#ifndef _SingleAtlas #ifndef _SingleAtlas
@ -521,12 +718,26 @@ void main() {
#else #else
shadowMap shadowMap
#endif #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
#endif // _Sun #endif // _Sun
#ifdef _ShadowMapAtlas
tileBounds = vec4(0.0, 0.0, 1.0, 1.0);
#endif
#ifdef _SinglePoint #ifdef _SinglePoint
#ifdef _VRStereo #ifdef _VRStereo
@ -555,12 +766,53 @@ void main() {
#ifdef _SSRS #ifdef _SSRS
, gbufferD, invVP, eye , gbufferD, invVP, eye
#endif #endif
#ifdef _ClearCoat
, matp1.x, matp1.y, matp1.z, coatTintCol, nCoat
#endif
#ifdef _Sheen
, matp0.z, matp0.w, sheenTintCol
#endif
#ifdef _Anisotropy
, matp0.x, matp0.y, wTangent
#endif
#ifdef _SSS
, matp3.z, sssColorVal, sssRadiusBase * matp7.x, matp3.w
#endif
#ifdef _Transmission
, matp2.z, matp2.w, matp3.x, matp3.y
#endif
); );
#ifdef _Spot #ifdef _Spot
#ifdef _SSS #ifdef _SSS
#ifdef _ShadowMap #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 #endif
#endif #endif
@ -618,7 +870,96 @@ void main() {
#ifdef _SSRS #ifdef _SSRS
, gbufferD, invVP, eye , gbufferD, invVP, eye
#endif #endif
#ifdef _ClearCoat
, matp1.x, matp1.y, matp1.z, coatTintCol, nCoat
#endif
#ifdef _Sheen
, matp0.z, matp0.w, sheenTintCol
#endif
#ifdef _Anisotropy
, matp0.x, matp0.y, wTangent
#endif
#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 #endif // _Clusters

View File

@ -138,6 +138,16 @@
"link": "_cascadeData", "link": "_cascadeData",
"ifdef": ["_Sun", "_ShadowMap", "_CSM"] "ifdef": ["_Sun", "_ShadowMap", "_CSM"]
}, },
{
"name": "tileBoundsSunArray",
"link": "_tileBoundsSunArray",
"ifdef": ["_Sun", "_ShadowMap", "_ShadowMapAtlas"]
},
{
"name": "tileBoundsSpotArray",
"link": "_tileBoundsSpotArray",
"ifdef": ["_Clusters", "_Spot", "_ShadowMap", "_ShadowMapAtlas"]
},
{ {
"name": "lightPlane", "name": "lightPlane",
"link": "_lightPlane", "link": "_lightPlane",
@ -277,8 +287,15 @@
"link": "_biasLightWorldViewProjectionMatrixSpot3", "link": "_biasLightWorldViewProjectionMatrixSpot3",
"ifndef": ["_ShadowMapAtlas"], "ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_LTC", "_ShadowMap"] "ifdef": ["_LTC", "_ShadowMap"]
},
{
"name": "materialParams",
"link": "_materialParams",
"type": "floats",
"ifdef": ["_ExtBRDF"]
} }
], ],
"texture_units": [],
"vertex_shader": "../include/pass_viewray.vert.glsl", "vertex_shader": "../include/pass_viewray.vert.glsl",
"fragment_shader": "deferred_light.frag.glsl", "fragment_shader": "deferred_light.frag.glsl",
"color_attachments": ["RGBA64"] "color_attachments": ["RGBA64"]

View File

@ -3,6 +3,7 @@
#include "compiled.inc" #include "compiled.inc"
#include "std/gbuffer.glsl" #include "std/gbuffer.glsl"
#include "std/math.glsl" #include "std/math.glsl"
#include "std/brdf.glsl"
#ifdef _Clusters #ifdef _Clusters
#include "std/clusters.glsl" #include "std/clusters.glsl"
#endif #endif
@ -13,6 +14,12 @@
uniform sampler2D gbufferD; uniform sampler2D gbufferD;
uniform sampler2D gbuffer0; uniform sampler2D gbuffer0;
uniform sampler2D gbuffer1; uniform sampler2D gbuffer1;
#ifdef _gbuffer2
uniform sampler2D gbuffer2;
#endif
#ifdef _ClearCoat
uniform sampler2D gbufferCoatNormal;
#endif
uniform float envmapStrength; uniform float envmapStrength;
#ifdef _Irr #ifdef _Irr
@ -49,7 +56,7 @@ uniform vec2 cameraPlane;
#ifdef _SinglePoint #ifdef _SinglePoint
#ifdef _Spot #ifdef _Spot
//!uniform sampler2DShadow shadowMapSpot[1]; //!uniform sampler2DShadow shadowMapSpot[1];
//!uniform mat4 LWVPSpot[1]; //!uniform mat4 LWVPSpotArray[1];
#else #else
//!uniform samplerCubeShadow shadowMapPoint[1]; //!uniform samplerCubeShadow shadowMapPoint[1];
//!uniform vec2 lightProj; //!uniform vec2 lightProj;
@ -91,6 +98,7 @@ uniform vec3 sunCol;
#ifndef _SingleAtlas #ifndef _SingleAtlas
uniform sampler2DShadow shadowMapAtlasSun; uniform sampler2DShadow shadowMapAtlasSun;
#endif #endif
//!uniform vec4 tileBoundsSunArray[maxLights * shadowmapCascades];
#else #else
uniform sampler2DShadow shadowMap; uniform sampler2DShadow shadowMap;
#endif #endif
@ -132,17 +140,61 @@ void main() {
float metallic; float metallic;
uint matid; uint matid;
unpackFloatInt16(g0.a, metallic, matid); unpackFloatInt16(g0.a, metallic, matid);
#ifdef _ExtBRDF
matid = min(matid, uint(MAX_MATERIALS - 1));
//!uniform vec4 materialParams[MAX_MATERIALS * 8];
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 vec4 g1 = textureLod(gbuffer1, texCoord, 0.0); // Basecolor.rgb, spec/occ
vec2 occspec = unpackFloat2(g1.a); vec2 occspec = unpackFloat2(g1.a);
vec3 albedo = surfaceAlbedo(g1.rgb, metallic); // g1.rgb - basecolor vec3 albedo = surfaceAlbedo(g1.rgb, metallic); // g1.rgb - basecolor
vec3 f0 = surfaceF0(g1.rgb, metallic); 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; float depth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
vec3 p = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj); vec3 p = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj);
vec3 v = normalize(eye - p); vec3 v = normalize(eye - p);
float dotNV = max(dot(n, v), 0.0); 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
#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 #ifdef _Brdf
vec2 envBRDF = texelFetch(senvmapBrdf, ivec2(vec2(dotNV, 1.0 - roughness) * 256.0), 0).xy; vec2 envBRDF = texelFetch(senvmapBrdf, ivec2(vec2(dotNV, 1.0 - roughness) * 256.0), 0).xy;
#endif #endif
@ -158,15 +210,20 @@ void main() {
#endif #endif
#ifdef _Rad #ifdef _Rad
#ifdef _Anisotropy
vec3 reflectionWorld = anisotropicIBLDirection(n, v, wTangent,
matp0.x, roughness);
#else
vec3 reflectionWorld = reflect(-v, n); vec3 reflectionWorld = reflect(-v, n);
#endif
float lod = getMipFromRoughness(roughness, envmapNumMipmaps); float lod = getMipFromRoughness(roughness, envmapNumMipmaps);
vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb; vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;
#endif #endif
#ifdef _EnvLDR #ifdef _EnvLDR
envl.rgb = pow(envl.rgb, vec3(2.2)); envl.rgb = srgbToLinear(envl.rgb);
#ifdef _Rad #ifdef _Rad
prefilteredColor = pow(prefilteredColor, vec3(2.2)); prefilteredColor = srgbToLinear(prefilteredColor);
#endif #endif
#endif #endif
@ -180,6 +237,98 @@ void main() {
#endif #endif
#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; envl.rgb *= envmapStrength * occspec.x;
fragColor.rgb = envl; fragColor.rgb = envl;
@ -189,10 +338,50 @@ void main() {
float sdotVH = max(0.0, dot(v, sh)); float sdotVH = max(0.0, dot(v, sh));
float sdotNL = max(0.0, dot(n, sunDir)); float sdotNL = max(0.0, dot(n, sunDir));
float svisibility = 1.0; float svisibility = 1.0;
#ifdef _Anisotropy
vec3 sdirect;
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,
wTangent, sbitangent, n, sunDir, v, sdotNL, dotNV) * occspec.y;
} else {
sdirect = lambertDiffuseBRDF(albedo, sdotNL) +
specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * occspec.y;
}
#else
vec3 sdirect = lambertDiffuseBRDF(albedo, sdotNL) + vec3 sdirect = lambertDiffuseBRDF(albedo, sdotNL) +
specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * occspec.y; specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * occspec.y;
#endif
float sunSheenWeight = brdf_sheenWeight;
float sunCoatWeight = brdf_coatWeight;
#ifdef _Sheen
vec3 sunSheen = sheenBRDF(matp0.z, matp0.w, sheenTintCol, sdotNL, sdotNH, dotNV);
#endif
#ifdef _ClearCoat
vec3 sunCoat = clearcoatBRDF(matp1.x, matp1.y, matp1.z, nCoat, sunDir, v, sh);
#endif
float sunLayerWeight = sunSheenWeight * sunCoatWeight;
sdirect *= sunLayerWeight;
#ifdef _Transmission
sdirect += transmissionBRDF(albedo, matp2.z, matp2.w, matp3.x, matp3.y, sdotNL, dotNV, sdotVH) * sunLayerWeight;
#endif
#ifdef _ClearCoat
sdirect *= brdf_coatTintAbsorb;
sdirect += sunCoat * sunSheenWeight;
#endif
#ifdef _Sheen
sdirect += sunSheen;
#endif
#ifdef _ShadowMap #ifdef _ShadowMap
#ifdef _ShadowMapAtlas
tileBounds = tileBoundsSunArray[0];
#endif
#ifdef _CSM #ifdef _CSM
svisibility = shadowTestCascade( svisibility = shadowTestCascade(
#ifdef _ShadowMapAtlas #ifdef _ShadowMapAtlas
@ -235,6 +424,21 @@ void main() {
#ifdef _Spot #ifdef _Spot
, true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight // TODO: Test! , true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight // TODO: Test!
#endif #endif
#ifdef _ClearCoat
, matp1.x, matp1.y, matp1.z, coatTintCol, nCoat
#endif
#ifdef _Sheen
, matp0.z, matp0.w, sheenTintCol
#endif
#ifdef _Anisotropy
, matp0.x, matp0.y, wTangent
#endif
#ifdef _SSS
, matp3.z, sssColorVal, sssRadiusScaled, matp3.w
#endif
#ifdef _Transmission
, matp2.z, matp2.w, matp3.x, matp3.y
#endif
); );
#endif #endif
@ -277,7 +481,29 @@ void main() {
, vec2(lightsArray[li * 3].w, lightsArray[li * 3 + 1].w) // scale , vec2(lightsArray[li * 3].w, lightsArray[li * 3 + 1].w) // scale
, lightsArraySpot[li * 2 + 1].xyz // right , lightsArraySpot[li * 2 + 1].xyz // right
#endif #endif
#ifdef _ClearCoat
, matp1.x, matp1.y, matp1.z, coatTintCol, nCoat
#endif
#ifdef _Sheen
, matp0.z, matp0.w, sheenTintCol
#endif
#ifdef _Anisotropy
, matp0.x, matp0.y, wTangent
#endif
#ifdef _SSS
, matp3.z, sssColorVal, sssRadiusScaled, matp3.w
#endif
#ifdef _Transmission
, matp2.z, matp2.w, matp3.x, matp3.y
#endif
); );
} }
#endif // _Clusters #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
} }

View File

@ -97,6 +97,16 @@
"link": "_cascadeData", "link": "_cascadeData",
"ifdef": ["_Sun", "_ShadowMap", "_CSM"] "ifdef": ["_Sun", "_ShadowMap", "_CSM"]
}, },
{
"name": "tileBoundsSunArray",
"link": "_tileBoundsSunArray",
"ifdef": ["_Sun", "_ShadowMap", "_ShadowMapAtlas"]
},
{
"name": "tileBoundsSpotArray",
"link": "_tileBoundsSpotArray",
"ifdef": ["_Clusters", "_Spot", "_ShadowMap", "_ShadowMapAtlas"]
},
{ {
"name": "eyeLookRight", "name": "eyeLookRight",
"link": "_eyeLookRight", "link": "_eyeLookRight",
@ -214,6 +224,12 @@
"link": "_biasLightWorldViewProjectionMatrixSpot3", "link": "_biasLightWorldViewProjectionMatrixSpot3",
"ifndef": ["_ShadowMapAtlas"], "ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_LTC", "_ShadowMap"] "ifdef": ["_LTC", "_ShadowMap"]
},
{
"name": "materialParams",
"link": "_materialParams",
"type": "floats",
"ifdef": ["_ExtBRDF"]
} }
], ],
"vertex_shader": "../include/pass_viewray.vert.glsl", "vertex_shader": "../include/pass_viewray.vert.glsl",

View File

@ -11,6 +11,7 @@ uniform sampler2D gbuffer1; // basecol, spec
uniform mat4 P; uniform mat4 P;
uniform mat3 V3; uniform mat3 V3;
uniform vec2 cameraProj; uniform vec2 cameraProj;
uniform vec2 screenSize;
#ifdef _CPostprocess #ifdef _CPostprocess
uniform vec3 PPComp9; uniform vec3 PPComp9;
@ -24,8 +25,8 @@ out vec4 fragColor;
vec3 hitCoord; vec3 hitCoord;
float depth; float depth;
const int numBinarySearchSteps = 7; const int numBinarySearchSteps = 8;
const int maxSteps = int(ceil(1.0 / ssrRayStep) * ssrSearchDist); const int maxSteps = 50;
vec2 getProjectedCoord(const vec3 hit) { vec2 getProjectedCoord(const vec3 hit) {
vec4 projectedCoord = P * vec4(hit, 1.0); vec4 projectedCoord = P * vec4(hit, 1.0);
@ -38,44 +39,58 @@ vec2 getProjectedCoord(const vec3 hit) {
} }
float getDeltaDepth(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); vec3 viewPos = getPosView(viewRay, depth, cameraProj);
return viewPos.z - hit.z; return viewPos.z - hit.z;
} }
vec4 binarySearch(vec3 dir) { vec4 binarySearch(vec3 dir, float stepSize) {
float ddepth; float ddepth;
for (int i = 0; i < numBinarySearchSteps; i++) { for (int i = 0; i < numBinarySearchSteps; i++) {
dir *= 0.5; stepSize *= 0.5;
hitCoord -= dir; hitCoord -= dir * stepSize;
ddepth = getDeltaDepth(hitCoord); 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 #ifdef _CPostprocess
if (abs(ddepth) > PPComp9.z / 500) return vec4(0.0); float maxDist = PPComp9.z;
#else #else
if (abs(ddepth) > ssrSearchDist / 500) return vec4(0.0); float maxDist = ssrSearchDist;
#endif #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) { vec4 rayCast(vec3 dir) {
#ifdef _CPostprocess #ifdef _CPostprocess
dir *= PPComp9.x; float baseStep = PPComp9.x;
float maxDist = PPComp9.z;
#else #else
dir *= ssrRayStep; float baseStep = ssrRayStep;
float maxDist = ssrSearchDist;
#endif #endif
float stepSize = baseStep * max(1.0, -viewRay.z * 0.1);
vec3 startPos = hitCoord;
for (int i = 0; i < maxSteps; i++) { for (int i = 0; i < maxSteps; i++) {
hitCoord += dir; hitCoord += dir * stepSize;
if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir); 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); return vec4(0.0);
} }
void main() { void main() {
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0); 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; } if (roughness == 1.0) { fragColor.rgb = vec3(0.0); return; }
float spec = fract(textureLod(gbuffer1, texCoord, 0.0).a); float spec = fract(textureLod(gbuffer1, texCoord, 0.0).a);
@ -92,30 +107,54 @@ void main() {
vec3 viewNormal = V3 * n; vec3 viewNormal = V3 * n;
vec3 viewPos = getPosView(viewRay, d, cameraProj); 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; hitCoord = viewPos;
#ifdef _CPostprocess vec3 dir = reflected;
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
// * max(ssrMinRayStep, -viewPos.z)
vec4 coords = rayCast(dir); vec4 coords = rayCast(dir);
if (coords.w <= 0.0) {
fragColor.rgb = vec3(0.0);
return;
}
vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy); vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy);
float screenEdgeFactor = clamp(1.0 - (deltaCoords.x + deltaCoords.y), 0.0, 1.0); 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; float reflectivity = 1.0 - roughness;
#ifdef _CPostprocess #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 #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 #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); intensity = clamp(intensity, 0.0, 1.0);
vec3 reflCol = textureLod(tex, coords.xy, 0.0).rgb; vec3 reflCol = textureLod(tex, coords.xy, 0.0).rgb;
reflCol = clamp(reflCol, 0.0, 1.0); reflCol = clamp(reflCol, 0.0, 1.0);
fragColor.rgb = reflCol * intensity * 0.5; fragColor.rgb = reflCol * intensity * mix(0.5, 1.0, fresnel);
} }

View File

@ -22,6 +22,10 @@
"name": "cameraProj", "name": "cameraProj",
"link": "_cameraPlaneProj" "link": "_cameraPlaneProj"
}, },
{
"name": "screenSize",
"link": "_screenSize"
},
{ {
"name": "PPComp9", "name": "PPComp9",
"link": "_PPComp9", "link": "_PPComp9",

View File

@ -12,6 +12,7 @@ uniform sampler2D tex1;
uniform sampler2D gbufferD; uniform sampler2D gbufferD;
uniform sampler2D gbuffer0; uniform sampler2D gbuffer0;
uniform sampler2D gbufferD1; uniform sampler2D gbufferD1;
uniform sampler2D gbuffer1;
uniform sampler2D gbuffer_refraction; // ior\opacity uniform sampler2D gbuffer_refraction; // ior\opacity
uniform mat4 P; uniform mat4 P;
@ -26,7 +27,7 @@ vec3 hitCoord;
float depth; float depth;
const int numBinarySearchSteps = 7; const int numBinarySearchSteps = 7;
const int maxSteps = int(ceil(1.0 / ss_refractionRayStep) * ss_refractionSearchDist); const int maxSteps = 50;
vec2 getProjectedCoord(const vec3 hit) { vec2 getProjectedCoord(const vec3 hit) {
vec4 projectedCoord = P * vec4(hit, 1.0); vec4 projectedCoord = P * vec4(hit, 1.0);
@ -39,45 +40,60 @@ vec2 getProjectedCoord(const vec3 hit) {
} }
float getDeltaDepth(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); vec3 viewPos = getPosView(viewRay, depth, cameraProj);
return viewPos.z - hit.z; return viewPos.z - hit.z;
} }
vec4 binarySearch(vec3 dir) { vec4 binarySearch(vec3 dir, float stepSize) {
float ddepth; float ddepth;
for (int i = 0; i < numBinarySearchSteps; i++) { for (int i = 0; i < numBinarySearchSteps; i++) {
dir *= 0.5; stepSize *= 0.5;
hitCoord -= dir; hitCoord -= dir * stepSize;
ddepth = getDeltaDepth(hitCoord); 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); if (abs(ddepth) > ss_refractionSearchDist * 0.005) return vec4(0.0);
return vec4(getProjectedCoord(hitCoord), 0.0, 1.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) { vec4 rayCast(vec3 dir) {
float ddepth; float stepSize = ss_refractionRayStep * max(1.0, -viewRay.z * 0.1);
dir *= ss_refractionRayStep; vec3 startPos = hitCoord;
for (int i = 0; i < maxSteps; i++) { for (int i = 0; i < maxSteps; i++) {
hitCoord += dir; hitCoord += dir * stepSize;
ddepth = getDeltaDepth(hitCoord); float dist = length(hitCoord - startPos);
if (ddepth > 0.0) return binarySearch(dir); 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); return vec4(texCoord, 0.0, 0.0);
} }
void main() { void main() {
vec4 gr = textureLod(gbuffer_refraction, texCoord, 0.0); vec4 gr = textureLod(gbuffer_refraction, texCoord, 0.0);
float ior = gr.x; float ior = unpackIOR(gr.x);
float transmittance = gr.y; float transmittance = gr.y;
float surfaceDepth = gr.z; float surfaceDepth = gr.z;
float d = surfaceDepth * 2.0 - 1.0; float d = surfaceDepth * 2.0 - 1.0;
vec4 sceneSample = textureLod(tex, texCoord, 0.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; 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; fragColor.a = 1.0;
return; return;
} }
@ -96,18 +112,18 @@ void main() {
vec3 refracted = refract(incident, viewNormal, 1.0 / ior); vec3 refracted = refract(incident, viewNormal, 1.0 / ior);
if (length(refracted) < 0.001) { if (length(refracted) < 0.001) {
vec3 background = textureLod(tex1, texCoord, 0.0).rgb; 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; fragColor.a = 1.0;
return; return;
} }
hitCoord = viewPos; hitCoord = viewPos;
vec3 dir = refracted * (1.0 - rand(texCoord) * ss_refractionJitter * roughness) * 2.0; vec3 dir = normalize(refracted);
vec4 coords = rayCast(dir); vec4 coords = rayCast(dir);
vec2 screenEdge = smoothstep(0.0, 0.1, coords.xy) * smoothstep(0.0, 0.1, 1.0 - coords.xy); vec2 screenEdge = smoothstep(0.0, 0.05, coords.xy) * smoothstep(0.0, 0.05, 1.0 - coords.xy);
float screenEdgeFactor = screenEdge.x * screenEdge.y; float screenEdgeFactor = max(screenEdge.x * screenEdge.y, 0.05);
float refractivity = 1.0 - roughness; float refractivity = 1.0 - roughness;
float intensity = pow(refractivity, ss_refractionFalloffExp) * screenEdgeFactor * coords.w; float intensity = pow(refractivity, ss_refractionFalloffExp) * screenEdgeFactor * coords.w;
@ -118,6 +134,6 @@ void main() {
vec3 behindColor = mix(straightBackground, refractedBackground, intensity); 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; fragColor.a = 1.0;
} }

View File

@ -40,100 +40,148 @@
uniform sampler2D gbufferD; uniform sampler2D gbufferD;
uniform sampler2D gbuffer0; uniform sampler2D gbuffer0;
uniform sampler2D gbuffer1;
uniform sampler2D tex; uniform sampler2D tex;
uniform vec2 dir; uniform vec2 dir;
uniform vec2 cameraProj; uniform vec2 cameraProj;
uniform mat4 projectionMatrix;
#ifdef _ExtBRDF
//!uniform vec4 materialParams[MAX_MATERIALS * 8];
#endif
in vec2 texCoord; in vec2 texCoord;
out vec4 fragColor; out vec4 fragColor;
const vec3 SKIN_SSS_RADIUS = vec3(4.8, 2.4, 1.5); // TODO: finish the SSS
const float SSS_DISTANCE_SCALE = 0.001; const float SSS_SCALE = 0.05;
const float DEPTH_THRESHOLD = 0.05;
// Temp hash func -
float hash13(vec3 p3) { float hash13(vec3 p3) {
p3 = fract(p3 * vec3(0.1031, 0.1030, 0.0973)); p3 = fract(p3 * vec3(0.1031, 0.1030, 0.0973));
p3 += dot(p3, p3.yzx + 33.33); p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z); return fract((p3.x + p3.y) * p3.z);
} }
vec4 SSSSBlur() { vec4 SSSSBlur(vec3 sssRadius, float sssWeight) {
const int SSSS_N_SAMPLES = 15; const int SSSS_N_SAMPLES = 11;
vec4 kernel[SSSS_N_SAMPLES]; vec4 kernel[SSSS_N_SAMPLES];
kernel[0] = vec4(0.233, 0.455, 0.649, 0.0); // Center sample 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[1] = vec4(0.100, 0.336, 0.344, 0.37); // +0.37
kernel[2] = vec4(0.118, 0.198, 0.0, 0.97); // +0.97mm 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.93mm 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.87mm 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.53mm (red only) kernel[5] = vec4(0.078, 0.0, 0.0, 6.53); // +6.53 (red only)
kernel[6] = vec4(0.0, 0.0, 0.0, 0.0); // Unused kernel[6] = vec4(0.100, 0.336, 0.344, -0.37); // -0.37
kernel[7] = vec4(0.0, 0.0, 0.0, 0.0); // Unused kernel[7] = vec4(0.118, 0.198, 0.0, -0.97); // -0.97
kernel[8] = vec4(0.100, 0.336, 0.344, -0.37); // -0.37mm kernel[8] = vec4(0.113, 0.007, 0.007, -1.93); // -1.93
kernel[9] = vec4(0.118, 0.198, 0.0, -0.97); // -0.97mm kernel[9] = vec4(0.358, 0.004, 0.0, -3.87); // -3.87
kernel[10] = vec4(0.113, 0.007, 0.007, -1.93); // -1.93mm kernel[10] = vec4(0.078, 0.0, 0.0, -6.53); // -6.53 (red only)
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); vec2 texSize = vec2(textureSize(tex, 0));
ivec2 texelCoord = ivec2(texCoord * texSize);
float depth = textureLod(gbufferD, texCoord, 0.0).r; 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 depthM = cameraProj.y / (depth - cameraProj.x);
float distanceScale = 1.0 / max(depthM, 0.1); float blurWidth = max(max(sssRadius.r, sssRadius.g), sssRadius.b);
float projScale = dot(dir, vec2(projectionMatrix[0][0], projectionMatrix[1][1]));
vec2 finalStep = sssWidth * distanceScale * dir * SSS_DISTANCE_SCALE; vec2 finalStep = blurWidth * (1.0 / depthM) * dir * projScale * SSS_SCALE;
vec3 jitterSeed = vec3(texCoord.xy * 1000.0, fract(cameraProj.x * 0.0001)); vec3 jitterSeed = vec3(texCoord.xy * 1000.0, fract(cameraProj.x * 0.0001));
float jitterOffset = (hash13(jitterSeed) * 2.0 - 1.0) * 0.15; float jitterOffset = (hash13(jitterSeed) * 2.0 - 1.0) * 0.15;
finalStep *= (1.0 + jitterOffset); finalStep *= (1.0 + jitterOffset);
vec3 colorBlurred = vec3(0.0);
vec3 weightSum = vec3(0.0); vec3 colorBlurred = irradianceM * kernel[0].rgb;
colorBlurred += colorM.rgb * kernel[0].rgb; vec3 weightSum = kernel[0].rgb;
weightSum += kernel[0].rgb;
for (int i = 1; i < SSSS_N_SAMPLES; i++) { 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; float sampleJitter = hash13(vec3(texCoord.xy * 720.0, float(i) * 37.45)) * 0.1 - 0.05;
vec2 offset = texCoord + (kernel[i].a + sampleJitter) * finalStep; vec2 offset = texCoord + (kernel[i].a + sampleJitter) * finalStep;
vec4 color = textureLod(tex, offset, 0.0);
const float DEPTH_THRESHOLD = 0.05; vec3 irradiance = irradianceM;
float sampleDepth = textureLod(gbufferD, offset, 0.0).r; 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 sampleDepthM = cameraProj.y / (sampleDepth - cameraProj.x);
float depthDiff = abs(depthM - sampleDepthM); float depthDiff = abs(depthM - sampleDepthM);
float depthWeight = exp(-depthDiff * 10.0);
if (depthDiff > DEPTH_THRESHOLD) { if (depthDiff < 1.0) {
color.rgb = mix(colorM.rgb, color.rgb, depthWeight); 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));
} }
colorBlurred += color.rgb * kernel[i].rgb; if (depthDiff <= DEPTH_THRESHOLD) {
s = 1.0;
} else {
s = exp(-depthDiff * 10.0);
}
}
}
colorBlurred += kernel[i].rgb * mix(irradianceM, irradiance, s);
weightSum += kernel[i].rgb; 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; float dither = hash13(vec3(texCoord * 1333.0, 0.0)) * 0.003 - 0.0015;
normalizedColor = max(normalizedColor + vec3(dither), vec3(0.0)); normalizedIrradiance = max(normalizedIrradiance + vec3(dither), vec3(0.0));
return vec4(normalizedColor, colorM.a); vec3 blurredColor = normalizedIrradiance * albedo;
vec3 result = mix(colorM.rgb, blurredColor, sssWeight);
return vec4(result, colorM.a);
} }
void main() { 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; float metallic;
uint matid; uint matid;
unpackFloatInt16(g0.a, metallic, matid); unpackFloatInt16(g0.a, metallic, matid);
if (matid == 2u) { bool applySSS = false;
vec4 originalColor = textureLod(tex, texCoord, 0.0); vec3 sssRadius = vec3(1.0);
vec4 blurredColor = SSSSBlur(); float sssWeight = 1.0;
vec4 sssContribution = blurredColor - originalColor; vec4 matp0, matp1, matp2, matp3, matp4, matp5, matp6, matp7;
vec4 combined = originalColor + max(vec4(0.0), sssContribution) * 0.8; #ifdef _ExtBRDF
fragColor = max(vec4(0.0), min(combined, vec4(10.0))); 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 { } else {
fragColor = textureLod(tex, texCoord, 0.0); vec2 texSizeMain = vec2(textureSize(tex, 0));
ivec2 texelCoordMain = ivec2(texCoord * texSizeMain);
fragColor = texelFetch(tex, texelCoordMain, 0);
} }
} }

View File

@ -13,6 +13,16 @@
{ {
"name": "cameraProj", "name": "cameraProj",
"link": "_cameraPlaneProj" "link": "_cameraPlaneProj"
},
{
"name": "projectionMatrix",
"link": "_projectionMatrix"
},
{
"name": "materialParams",
"link": "_materialParams",
"type": "floats",
"ifdef": ["_ExtBRDF"]
} }
], ],
"texture_params": [], "texture_params": [],
@ -32,6 +42,16 @@
{ {
"name": "cameraProj", "name": "cameraProj",
"link": "_cameraPlaneProj" "link": "_cameraPlaneProj"
},
{
"name": "projectionMatrix",
"link": "_projectionMatrix"
},
{
"name": "materialParams",
"link": "_materialParams",
"type": "floats",
"ifdef": ["_ExtBRDF"]
} }
], ],
"texture_params": [], "texture_params": [],

View File

@ -1,10 +1,30 @@
#ifndef _BRDF_GLSL_ #ifndef _BRDF_GLSL_
#define _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://xlgames-inc.github.io/posts/improvedibl/
// http://blog.selfshadow.com/publications/s2013-shading-course/ // http://blog.selfshadow.com/publications/s2013-shading-course/
vec3 f_schlick(const vec3 f0, const float vh) { 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) { 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 a2 = a * a;
float denom = nh * nh * (a2 - 1.0) + 1.0; 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 */); 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) { 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/ // 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) { vec3 specularBRDFb(const vec3 f0, const float roughness, const float dotNL, const float dotNH, const float dotLH) {
// D // D
const float pi = 3.1415926535;
float alpha = roughness * roughness; float alpha = roughness * roughness;
float alphaSqr = alpha * alpha; float alphaSqr = alpha * alpha;
float denom = dotNH * dotNH * (alphaSqr - 1.0) + 1.0; float denom = dotNH * dotNH * (alphaSqr - 1.0) + 1.0;
float D = alphaSqr / (pi * denom * denom); float D = alphaSqr / (PI * denom * denom);
// F // F
const float F_a = 1.0; const float F_a = 1.0;
float F_b = pow(1.0 - dotLH, 5.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 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) { 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) { vec3 surfaceAlbedo(const vec3 baseColor, const float metalness) {
@ -95,24 +101,6 @@ float getMipFromRoughness(const float roughness, const float numMipmaps) {
return roughness * 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 // https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile
// vec3 EnvBRDFApprox(vec3 SpecularColor, float Roughness, float NoV) { // vec3 EnvBRDFApprox(vec3 SpecularColor, float Roughness, float NoV) {
// const vec4 c0 = { -1, -0.0275, -0.572, 0.022 }; // const vec4 c0 = { -1, -0.0275, -0.572, 0.022 };
@ -138,4 +126,207 @@ float D_Approx(const float Roughness, const float RoL) {
return rcp_a2 * exp2( c * RoL - c ); return rcp_a2 * exp2( c * RoL - c );
} }
#ifdef _ClearCoat
float brdf_coatF0;
vec3 clearcoatBRDF(const float clearcoat, const float clearcoat_rough,
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;
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 vec3 coatN, const vec3 v) {
if (clearcoat <= 0.0) return 1.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 vec3 coatN, const vec3 v) {
if (clearcoat <= 0.0) return vec3(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) {
if (sheen <= 0.0) return vec3(0.0);
float rough = clamp(sheen_rough, 1e-3, 1.0);
float a = rough * rough;
float sinNH2 = 1.0 - dotNH * dotNH;
float a2 = a * a;
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);
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 maxComp = sheen * max(max(sheen_tint.r, sheen_tint.g), sheen_tint.b) * brdf_sheenAlbedo;
return max(1.0 - maxComp, 0.0);
}
#endif
#ifdef _Anisotropy
// anisotropic GGX Burley 2012
vec3 anisotropicBRDF(const vec3 f0, const float roughness, const float anisotropy,
const float aniso_rot, const vec3 tangent, const vec3 bitangent,
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 * PI * 2.0;
float cr = cos(rot);
float sr = sin(rot);
vec3 t = normalize(tangent * cr + bitangent * sr);
vec3 b = normalize(bitangent * cr - tangent * sr);
float aniso_abs = abs(anisotropy);
float at = max(roughness * (1.0 + aniso_abs), 1e-5);
float ab = max(roughness * (1.0 - aniso_abs), 1e-5);
if (anisotropy < 0.0) { vec3 tmp = t; t = b; b = tmp; }
float at2 = at * at;
float ab2 = ab * ab;
vec3 h = normalize(l + v);
float dotTH = dot(t, h);
float dotBH = dot(b, h);
float dotTV = dot(t, v);
float dotBV = dot(b, v);
float dotTL = dot(t, l);
float dotBL = dot(b, l);
float denom = max(dotTH * dotTH / at2 + dotBH * dotBH / ab2, 1e-7);
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);
return D * V * F / max(4.0 * dotNV, 1e-5);
}
#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 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 = 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 #endif

View File

@ -34,17 +34,19 @@ THE SOFTWARE.
// https://research.nvidia.com/sites/default/files/publications/GIVoxels-pg2011-authors.pdf // https://research.nvidia.com/sites/default/files/publications/GIVoxels-pg2011-authors.pdf
const float MAX_DISTANCE = voxelgiRange; const float MAX_DISTANCE = voxelgiRange;
const int MAX_CONE_STEPS = 32;
#ifdef _VoxelGI #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 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); 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; vec3 half_texel = vec3(0.5) / voxelgiResolution;
tc = tc * 0.5 + 0.5; tc = tc * 0.5 + 0.5;
tc = clamp(tc, half_texel, 1.0 - half_texel); 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; tc.y = (tc.y + clipmap_index) / voxelgiClipmapCount;
if (precomputed_direction == 0) { if (precomputed_direction == 0) {
@ -55,7 +57,7 @@ vec4 sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapCo
else else
col = textureLod(voxels, tc, 0); col = textureLod(voxels, tc, 0);
col *= step_dist / float(clipmaps[int(clipmap_index * 10)]); col *= step_dist / voxelSize;
return col; return col;
} }
@ -64,11 +66,13 @@ vec4 sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapCo
#ifdef _VoxelAOvar #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 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; 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; vec3 half_texel = vec3(0.5) / voxelgiResolution;
tc = tc * 0.5 + 0.5; tc = tc * 0.5 + 0.5;
tc = clamp(tc, half_texel, 1.0 - half_texel); 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; tc.y = (tc.y + clipmap_index) / voxelgiClipmapCount;
if (precomputed_direction == 0) { if (precomputed_direction == 0) {
@ -79,7 +83,7 @@ float sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapC
else else
opac = textureLod(voxels, tc, 0).r; opac = textureLod(voxels, tc, 0).r;
opac *= step_dist / float(clipmaps[int(clipmap_index * 10)]); opac *= step_dist / voxelSize;
return opac; return opac;
} }
@ -92,7 +96,7 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori
float dist = voxelSize0; float dist = voxelSize0;
float step_dist = dist; float step_dist = dist;
vec3 samplePos; vec3 samplePos;
vec3 start_pos = origin + n * voxelSize0; vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
int clipmap_index0 = 0; int clipmap_index0 = 0;
vec3 aniso_direction = -dir; 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.x > 0.0 ? 0.0 : 1.0,
aniso_direction.y > 0.0 ? 2.0 : 3.0, aniso_direction.y > 0.0 ? 2.0 : 3.0,
aniso_direction.z > 0.0 ? 4.0 : 5.0 aniso_direction.z > 0.0 ? 4.0 : 5.0
) / (6 + DIFFUSE_CONE_COUNT); ) / (6 + diffuseConeCount);
vec3 direction_weight = abs(dir); vec3 direction_weight = abs(dir);
float coneCoefficient = 2.0 * tan(aperture * 0.5); 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); vec4 mipSample = vec4(0.0);
float diam = max(voxelSize0, dist * coneCoefficient); float diam = max(voxelSize0, dist * coneCoefficient);
float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1); 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)); float clipmap_blend = smoothstep(0.0, 1.0, fract(lod));
vec3 p0 = start_pos + dir * dist; 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; samplePos = samplePos * 0.5 + 0.5;
if (any(notEqual(samplePos, clamp(samplePos, 0.0, 1.0)))) { 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); 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); 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); mipSample = mix(mipSample, mipSampleNext, totalBlend);
} }
@ -138,8 +149,6 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori
float stepSizeCurrent = step_size; float stepSizeCurrent = step_size;
if (use_sdf) { 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); vec3 tc0 = clamp(samplePos, half_texel, 1 - half_texel);
tc0.y = (tc0.y + clipmap_index) / voxelgiClipmapCount; // remap into clipmap tc0.y = (tc0.y + clipmap_index) / voxelgiClipmapCount; // remap into clipmap
float sdf = textureLod(voxelsSDF, tc0, 0).r; 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; step_dist = diam * stepSizeCurrent;
dist += step_dist; dist += step_dist;
steps++;
} }
return sampleCol; 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]) { vec4 traceDiffuse(const vec3 origin, const vec3 normal, const sampler3D voxels, const float clipmaps[voxelgiClipmapCount * 10]) {
float sum = 0.0; float sum = 0.0;
vec4 amount = vec4(0.0); vec4 amount = vec4(0.0);
for (int i = 0; i < DIFFUSE_CONE_COUNT; ++i) { for (int i = 0; i < diffuseConeCount; ++i) {
vec3 coneDir = DIFFUSE_CONE_DIRECTIONS[i]; vec3 coneDir = diffuseConeDirections[i];
const float cosTheta = dot(normal, coneDir); const float cosTheta = dot(normal, coneDir);
if (cosTheta <= 0) if (cosTheta <= 0)
continue; continue;
int precomputed_direction = 6 + i; 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; 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.rgb = max(vec3(0.0), amount.rgb);
amount.a = clamp(amount.a, 0.0, 1.0); amount.a = clamp(amount.a, 0.0, 1.0);
return amount * voxelgiOcc; return amount * voxelgiOcc * voxelgiRefr;
} }
#endif #endif
@ -202,7 +212,7 @@ float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const
float dist = voxelSize0; float dist = voxelSize0;
float step_dist = dist; float step_dist = dist;
vec3 samplePos; vec3 samplePos;
vec3 start_pos = origin + n * voxelSize0; vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
int clipmap_index0 = 0; int clipmap_index0 = 0;
vec3 aniso_direction = -dir; 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.x > 0.0 ? 0.0 : 1.0,
aniso_direction.y > 0.0 ? 2.0 : 3.0, aniso_direction.y > 0.0 ? 2.0 : 3.0,
aniso_direction.z > 0.0 ? 4.0 : 5.0 aniso_direction.z > 0.0 ? 4.0 : 5.0
) / (6 + DIFFUSE_CONE_COUNT); ) / (6 + diffuseConeCount);
vec3 direction_weight = abs(dir); vec3 direction_weight = abs(dir);
float coneCoefficient = 2.0 * tan(aperture * 0.5); 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 mipSample = 0.0;
float diam = max(voxelSize0, dist * coneCoefficient); float diam = max(voxelSize0, dist * coneCoefficient);
float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1); 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)); float clipmap_blend = smoothstep(0.0, 1.0, fract(lod));
vec3 p0 = start_pos + dir * dist; 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; samplePos = samplePos * 0.5 + 0.5;
if ((any(notEqual(clamp(samplePos, 0.0, 1.0), samplePos)))) { 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); 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); 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); 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; step_dist = diam * step_size;
dist += step_dist; dist += step_dist;
steps++;
} }
return sampleCol; 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 traceAO(const vec3 origin, const vec3 normal, const sampler3D voxels, const float clipmaps[voxelgiClipmapCount * 10]) {
float sum = 0.0; float sum = 0.0;
float amount = 0.0; float amount = 0.0;
for (int i = 0; i < DIFFUSE_CONE_COUNT; i++) { for (int i = 0; i < diffuseConeCount; i++) {
vec3 coneDir = DIFFUSE_CONE_DIRECTIONS[i]; vec3 coneDir = diffuseConeDirections[i];
int precomputed_direction = 6 + i; int precomputed_direction = 6 + i;
const float cosTheta = dot(normal, coneDir); const float cosTheta = dot(normal, coneDir);
if (cosTheta <= 0) if (cosTheta <= 0)
continue; 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; sum += cosTheta;
} }
amount /= max(sum, 0.0001); amount /= max(sum, 0.0001);
amount = clamp(amount, 0.0, 1.0); amount = clamp(amount, 0.0, 1.0);
return amount * voxelgiOcc; return amount;
} }
#endif #endif
@ -278,7 +295,7 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v
float dist = voxelSize0; float dist = voxelSize0;
float step_dist = dist; float step_dist = dist;
vec3 samplePos; vec3 samplePos;
vec3 start_pos = origin + n * voxelSize0; vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
int clipmap_index0 = 0; int clipmap_index0 = 0;
vec3 aniso_direction = -dir; 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.x > 0.0 ? 0.0 : 1.0,
aniso_direction.y > 0.0 ? 2.0 : 3.0, aniso_direction.y > 0.0 ? 2.0 : 3.0,
aniso_direction.z > 0.0 ? 4.0 : 5.0 aniso_direction.z > 0.0 ? 4.0 : 5.0
) / (6 + DIFFUSE_CONE_COUNT); ) / (6 + diffuseConeCount);
vec3 direction_weight = abs(dir); vec3 direction_weight = abs(dir);
float coneCoefficient = 2.0 * tan(aperture * 0.5); 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 mipSample = 0.0;
float diam = max(voxelSize0, dist * coneCoefficient); float diam = max(voxelSize0, dist * coneCoefficient);
float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1); 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)); float clipmap_blend = smoothstep(0.0, 1.0, fract(lod));
vec3 p0 = start_pos + dir * dist; 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; samplePos = samplePos * 0.5 + 0.5;
if ((any(notEqual(samplePos, clamp(samplePos, 0.0, 1.0))))) { 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; mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, 0, face_offset, direction_weight).a;
#endif #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 #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 #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 #endif
mipSample = mix(mipSample, mipSampleNext, totalBlend); mipSample = mix(mipSample, mipSampleNext, totalBlend);
} }
@ -331,8 +355,6 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v
float stepSizeCurrent = step_size; 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); vec3 tc0 = clamp(samplePos, half_texel, 1 - half_texel);
tc0.y = (tc0.y + clipmap_index) / voxelgiClipmapCount; // remap into clipmap tc0.y = (tc0.y + clipmap_index) / voxelgiClipmapCount; // remap into clipmap
float sdf = textureLod(voxelsSDF, tc0, 0.0).r; 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; step_dist = diam * stepSizeCurrent;
dist += step_dist; dist += step_dist;
steps++;
} }
return sampleCol; 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) { 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; 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); amount = clamp(amount, 0.0, 1.0);
return amount * voxelgiOcc; return amount * voxelgiOcc;
} }

View File

@ -20,13 +20,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
*/ */
const int DIFFUSE_CONE_COUNT = 16; const float diffuseConeAperture = radians(39.0);
const float SHADOW_CONE_APERTURE = radians(15.0); const vec3 diffuseConeDirections[16] = vec3[](
const float DIFFUSE_CONE_APERTURE = 1.0;
const vec3 DIFFUSE_CONE_DIRECTIONS[16] = vec3[](
vec3( 0.3480, 0.0000, 0.9375), vec3( 0.3480, 0.0000, 0.9375),
vec3(-0.4299, 0.3938, 0.8125), vec3(-0.4299, 0.3938, 0.8125),
vec3( 0.0635, -0.7234, 0.6875), vec3( 0.0635, -0.7234, 0.6875),

View File

@ -170,4 +170,88 @@ void unpackFloatInt16(float val, out float f, out uint i) {
f = (bitsValue & ~(0xF << numBitFloat)) / maxValFloat; f = (bitsValue & ~(0xF << numBitFloat)) / maxValFloat;
} }
#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 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 #endif

View File

@ -3,7 +3,9 @@ uniform sampler2D texIES;
float iesAttenuation(vec3 l) { 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 // https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
// Sample direction into light space // Sample direction into light space
// vec3 iesSampleDirection = mul(light.worldToLight , -L); // vec3 iesSampleDirection = mul(light.worldToLight , -L);

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@
#ifdef _SinglePoint #ifdef _SinglePoint
#ifdef _Spot #ifdef _Spot
uniform sampler2DShadow shadowMapSpot[1]; uniform sampler2DShadow shadowMapSpot[1];
uniform mat4 LWVPSpot[1]; uniform mat4 LWVPSpotArray[1];
#else #else
uniform samplerCubeShadow shadowMapPoint[1]; uniform samplerCubeShadow shadowMapPoint[1];
uniform vec2 lightProj; uniform vec2 lightProj;
@ -24,7 +24,9 @@
#ifdef _SingleAtlas #ifdef _SingleAtlas
//!uniform sampler2DShadow shadowMapAtlas; //!uniform sampler2DShadow shadowMapAtlas;
#endif #endif
#ifndef _SinglePoint
uniform vec2 lightProj; uniform vec2 lightProj;
#endif
#ifdef _ShadowMapAtlas #ifdef _ShadowMapAtlas
#ifndef _SingleAtlas #ifndef _SingleAtlas
uniform sampler2DShadow shadowMapAtlasPoint; uniform sampler2DShadow shadowMapAtlasPoint;
@ -53,19 +55,64 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
#ifdef _Spot #ifdef _Spot
, bool isSpot, float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right , bool isSpot, float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right
#endif #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 ld = lp - p; vec3 ld = lp - p;
vec3 l = normalize(ld); float dist = length(ld);
vec3 l = ld / dist;
vec3 h = normalize(v + l); vec3 h = normalize(v + l);
float dotNH = max(0.0, dot(n, h)); float dotNH = max(0.0, dot(n, h));
float dotVH = max(0.0, dot(v, h)); float dotVH = max(0.0, dot(v, h));
float dotNL = max(0.0, dot(n, l)); float dotNL = max(0.0, dot(n, l));
#ifdef _Anisotropy
vec3 direct;
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,
tangent, bitangent, n, l, v, dotNL, dotNV) * spec;
} else {
direct = lambertDiffuseBRDF(albedo, dotNL) +
specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec;
}
#else
vec3 direct = lambertDiffuseBRDF(albedo, dotNL) + vec3 direct = lambertDiffuseBRDF(albedo, dotNL) +
specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec; specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec;
#endif
#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 *= lightCol;
direct *= attenuate(distance(p, lp)); direct *= attenuate(dist);
#ifdef _Spot #ifdef _Spot
if (isSpot) { if (isSpot) {
@ -74,12 +121,13 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
#ifdef _ShadowMap #ifdef _ShadowMap
if (receiveShadow) { if (receiveShadow) {
#ifdef _SinglePoint #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); direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
#endif #endif
#ifdef _Clusters #ifdef _Clusters
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0); vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0);
#ifdef _ShadowMapAtlas #ifdef _ShadowMapAtlas
tileBounds = tileBoundsSpotArray[index];
direct *= shadowTest( direct *= shadowTest(
#ifndef _SingleAtlas #ifndef _SingleAtlas
shadowMapAtlasSpot shadowMapAtlasSpot
@ -132,4 +180,41 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
return direct; 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 #endif

View File

@ -8,8 +8,12 @@ float hash(const vec2 p) {
} }
vec2 envMapEquirect(const vec3 normal) { vec2 envMapEquirect(const vec3 normal) {
const float PI = 3.1415926535; #ifndef PI
const float PI2 = PI * 2.0; #define PI 3.1415926535
#endif
#ifndef PI2
#define PI2 6.2831853071
#endif
float phi = acos(normal.z); float phi = acos(normal.z);
float theta = atan(-normal.y, normal.x) + PI; float theta = atan(-normal.y, normal.x) + PI;
return vec2(theta / PI2, phi / PI); return vec2(theta / PI2, phi / PI);

View File

@ -22,6 +22,14 @@ uniform vec2 smSizeUniform;
#endif #endif
#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 #ifdef _ShadowMapAtlas
// PCF that clamps samples to tile boundaries to prevent bleeding // PCF that clamps samples to tile boundaries to prevent bleeding
vec3 PCFTileAware(sampler2DShadow shadowMap, vec3 PCFTileAware(sampler2DShadow shadowMap,
@ -291,13 +299,13 @@ vec3 PCFFakeCube(sampler2DShadow shadowMap,
, const bool transparent , const bool transparent
#endif #endif
) { ) {
const vec2 smSize = smSizeUniform; // TODO: incorrect...
const float compare = lpToDepth(lp, lightProj) - bias * 1.5; const float compare = lpToDepth(lp, lightProj) - bias * 1.5;
ml = ml + n * bias * 20; ml = ml + n * bias * 20;
int faceIndex = 0; int faceIndex = 0;
const int lightIndex = index * 6; const int lightIndex = index * 6;
const vec2 uv = sampleCube(ml, faceIndex); 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 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; vec2 uvtiled = pointLightTile.z * uv + pointLightTile.xy;
#ifdef _FlipY #ifdef _FlipY
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
@ -387,10 +395,6 @@ vec3 PCFFakeCube(sampler2DShadow shadowMap,
} }
#endif #endif
#ifdef _ShadowMapAtlas
uniform vec4 tileBounds;
#endif
vec3 shadowTest(sampler2DShadow shadowMap, vec3 shadowTest(sampler2DShadow shadowMap,
#ifdef _ShadowMapTransparent #ifdef _ShadowMapTransparent
sampler2D shadowMapTransparent, sampler2D shadowMapTransparent,
@ -405,9 +409,9 @@ vec3 shadowTest(sampler2DShadow shadowMap,
#ifdef _ShadowMapAtlas #ifdef _ShadowMapAtlas
// use tile PCF // use tile PCF
#ifdef _SMSizeUniform #ifdef _SMSizeUniform
vec2 smSizeAtlas = smSizeUniform; vec2 smSizeAtlas = smSizeUniform * (tileBounds.zw - tileBounds.xy);
#else #else
const vec2 smSizeAtlas = shadowmapSize; vec2 smSizeAtlas = shadowmapSize * (tileBounds.zw - tileBounds.xy);
#endif #endif
return PCFTileAware(shadowMap, return PCFTileAware(shadowMap,
#ifdef _ShadowMapTransparent #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].y),
float(d > casData[c * 4].z), float(d > casData[c * 4].z),
float(d > casData[c * 4].w)); 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 // Get cascade mat
casIndex = casi * 4; casIndex = casi * 4;
return mat4( return mat4(
@ -479,8 +483,12 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap,
#ifdef _SMSizeUniform #ifdef _SMSizeUniform
vec2 smSize = smSizeUniform; vec2 smSize = smSizeUniform;
#else #else
#ifdef _ShadowMapAtlas
vec2 smSize = shadowmapSize * (tileBoundsSunArray[0].zw - tileBoundsSunArray[0].xy);
#else
const vec2 smSize = shadowmapSize * vec2(shadowmapCascades, 1.0); const vec2 smSize = shadowmapSize * vec2(shadowmapCascades, 1.0);
#endif #endif
#endif
const int c = shadowmapCascades; const int c = shadowmapCascades;
float d = distance(eye, p); float d = distance(eye, p);
int casi; int casi;
@ -489,8 +497,25 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap,
vec4 lPos = LWVP * vec4(p, 1.0); vec4 lPos = LWVP * vec4(p, 1.0);
lPos.xyz /= lPos.w; lPos.xyz /= lPos.w;
#ifdef _ShadowMapAtlas
tileBounds = tileBoundsSunArray[casi];
#endif
vec3 visibility = vec3(1.0); vec3 visibility = vec3(1.0);
if (lPos.w > 0.0) visibility = PCF(shadowMap, 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 #ifdef _ShadowMapTransparent
shadowMapTransparent, shadowMapTransparent,
#endif #endif
@ -499,6 +524,8 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap,
, transparent , transparent
#endif #endif
); );
#endif
}
// Blend cascade // Blend cascade
// https://github.com/TheRealMJP/Shadows // https://github.com/TheRealMJP/Shadows
@ -518,7 +545,23 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap,
lPos2.xyz /= lPos2.w; lPos2.xyz /= lPos2.w;
vec3 visibility2 = vec3(1.0); vec3 visibility2 = vec3(1.0);
// use lPos2 coordinates for second cascade, not lPos // use lPos2 coordinates for second cascade, not lPos
if (lPos2.w > 0.0) visibility2 = PCF(shadowMap, #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 #ifdef _ShadowMapTransparent
shadowMapTransparent, shadowMapTransparent,
#endif #endif
@ -527,6 +570,8 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap,
, transparent , transparent
#endif #endif
); );
#endif
}
float lerpAmt = smoothstep(0.0, blendThres, splitDist); float lerpAmt = smoothstep(0.0, blendThres, splitDist);
return mix(visibility2, visibility, lerpAmt); return mix(visibility2, visibility, lerpAmt);

View File

@ -26,7 +26,7 @@ uniform sampler2D singleScatterLUT;
uniform vec2 skyDensity; uniform vec2 skyDensity;
#ifndef PI #ifndef PI
#define PI 3.141592 #define PI 3.1415926535
#endif #endif
#ifndef HALF_PI #ifndef HALF_PI
#define HALF_PI 1.570796 #define HALF_PI 1.570796

View File

@ -1,15 +1,25 @@
// Separable SSS Transmittance Function, ref to sss_pass // Separable SSS Transmittance Function, ref to sss_pass
vec3 SSSSTransmittance(mat4 LWVP, vec3 p, vec3 n, vec3 l, float lightFar, sampler2DShadow shadowMap) { vec3 SSSSTransmittance(mat4 LWVP, vec3 p, vec3 n, vec3 l, float lightFar, sampler2DShadow shadowMap, vec3 sssColor, float sssRadius
const float translucency = 1.0; #ifdef _ShadowMapAtlas
, vec4 tileBounds
#endif
) {
const float translucency = 0.85;
vec4 shrinkedPos = vec4(p - 0.005 * n, 1.0); vec4 shrinkedPos = vec4(p - 0.005 * n, 1.0);
vec4 shadowPos = LWVP * shrinkedPos; vec4 shadowPos = LWVP * shrinkedPos;
float scale = 8.25 * (1.0 - translucency) / (sssWidth / 10.0); vec2 shadowUV = shadowPos.xy / shadowPos.w;
float d1 = texture(shadowMap, vec3(shadowPos.xy / shadowPos.w, shadowPos.z)).r; // 'd1' has a range of 0..1 #ifdef _ShadowMapAtlas
float d2 = shadowPos.z; // 'd2' has a range of 0..'lightFarPlane' shadowUV = clamp(shadowUV, tileBounds.xy, tileBounds.zw);
d1 *= lightFar; // So we scale 'd1' accordingly: #endif
float d = scale * abs(d1 - d2); 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; float dd = -d * d;
vec3 profile = vec3(0.233, 0.455, 0.649) * exp(dd / 0.0064) + 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.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.113, 0.007, 0.007) * exp(dd / 0.567) +
vec3(0.358, 0.004, 0.0) * exp(dd / 1.99) + vec3(0.358, 0.004, 0.0) * exp(dd / 1.99) +
vec3(0.078, 0.0, 0.0) * exp(dd / 7.41); 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) { #ifdef _ShadowMapAtlas
// TODO vec3 SSSSTransmittanceCubeAtlas(sampler2DShadow shadowMap, vec3 lightPos, vec3 p, vec3 n, vec3 l, float lightFar, vec2 lightProj, int index, vec3 sssColor, float sssRadius) {
return vec3(0.0); 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);
} }

View File

@ -38,7 +38,6 @@ uniform layout(r8) image3D voxelsOut;
#endif #endif
uniform int clipmapLevel; uniform int clipmapLevel;
uniform float voxelBlend;
uniform float clipmaps[voxelgiClipmapCount * 10]; uniform float clipmaps[voxelgiClipmapCount * 10];
@ -47,7 +46,7 @@ void main() {
ivec3 src = ivec3(gl_GlobalInvocationID.xyz); ivec3 src = ivec3(gl_GlobalInvocationID.xyz);
src.y += clipmapLevel * res; 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); vec4 col = vec4(0.0);

View File

@ -165,7 +165,7 @@ void main() {
#endif #endif
#endif #endif
envl.rgb *= envmapStrength * occspec.x; envl.rgb *= envmapStrength * voxelgiEnv * occspec.x;
vec3 occ = envl * (1.0 - traceAO(P, n, voxels, clipmaps)); vec3 occ = envl * (1.0 - traceAO(P, n, voxels, clipmaps));

View File

@ -53,13 +53,6 @@ uniform float shirr[7 * 4];
#ifdef _Brdf #ifdef _Brdf
uniform sampler2D senvmapBrdf; uniform sampler2D senvmapBrdf;
#endif #endif
#ifdef _Rad
uniform sampler2D senvmapRadiance;
uniform int envmapNumMipmaps;
#endif
#ifdef _EnvCol
uniform vec3 backgroundCol;
#endif
void main() { void main() {
const vec2 pixel = gl_GlobalInvocationID.xy; const vec2 pixel = gl_GlobalInvocationID.xy;
@ -140,17 +133,8 @@ void main() {
vec3 envl = vec3(0.0); vec3 envl = vec3(0.0);
#endif #endif
#ifdef _Rad
vec3 reflectionWorld = reflect(-v, n);
float lod = getMipFromRoughness(roughness, envmapNumMipmaps);
vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;
#endif
#ifdef _EnvLDR #ifdef _EnvLDR
envl.rgb = pow(envl.rgb, vec3(2.2)); envl.rgb = pow(envl.rgb, vec3(2.2));
#ifdef _Rad
prefilteredColor = pow(prefilteredColor, vec3(2.2));
#endif
#endif #endif
envl.rgb *= albedo; envl.rgb *= albedo;
@ -159,15 +143,7 @@ void main() {
envl.rgb *= 1.0 - F; //LV: We should take refracted light into account envl.rgb *= 1.0 - F; //LV: We should take refracted light into account
#endif #endif
#ifdef _Rad // Indirect specular envl.rgb *= envmapStrength * voxelgiEnv * occspec.x;
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;
vec4 trace = traceDiffuse(P, n, voxels, clipmaps); vec4 trace = traceDiffuse(P, n, voxels, clipmaps);
vec3 color = trace.rgb * albedo * (1.0 - F); vec3 color = trace.rgb * albedo * (1.0 - F);

View File

@ -48,13 +48,15 @@ void main() {
const vec2 pixel = gl_GlobalInvocationID.xy; const vec2 pixel = gl_GlobalInvocationID.xy;
vec2 uv = (pixel + 0.5) / postprocess_resolution; vec2 uv = (pixel + 0.5) / postprocess_resolution;
#ifdef _InvY #ifdef _InvY
uv.y = 1.0 - uv.y uv.y = 1.0 - uv.y;
#endif #endif
float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0; float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0;
if (depth == 0) return; if (depth == 0) return;
vec2 ior_opac = textureLod(gbuffer_refraction, uv, 0.0).xy; 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 x = uv.x * 2 - 1;
float y = uv.y * 2 - 1; float y = uv.y * 2 - 1;
@ -72,8 +74,8 @@ void main() {
n = normalize(n); n = normalize(n);
vec3 color = vec3(0.0); vec3 color = vec3(0.0);
if(ior_opac.y < 1.0) if(opacity < 1.0)
color = traceRefraction(P, n, voxels, voxelsSDF, normalize(eye - P), ior_opac.x, g0.b, clipmaps, pixel).rgb; color = traceRefraction(P, n, voxels, voxelsSDF, normalize(eye - P), ior, g0.b, clipmaps, pixel).rgb;
imageStore(voxels_refraction, ivec2(pixel), vec4(color, 1.0)); imageStore(voxels_refraction, ivec2(pixel), vec4(color, 1.0));
} }

View File

@ -66,9 +66,13 @@ void main() {
n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy); n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy);
n = normalize(n); n = normalize(n);
float roughness = g0.b;
vec3 v = normalize(eye - P);
vec2 velocity = -textureLod(sveloc, uv, 0.0).rg; 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)); imageStore(voxels_specular, ivec2(pixel), vec4(color, 1.0));
} }

View File

@ -79,7 +79,7 @@ void main() {
float aniso_colors[6]; float aniso_colors[6];
#endif #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); ivec3 src = ivec3(gl_GlobalInvocationID.xyz);
src.x += i * res; src.x += i * res;
@ -136,7 +136,7 @@ void main() {
radiance = basecol; radiance = basecol;
vec4 trace = traceDiffuse(wposition, wnormal, voxelsSampler, clipmaps); vec4 trace = traceDiffuse(wposition, wnormal, voxelsSampler, clipmaps);
vec3 indirect = trace.rgb + envl.rgb * (1.0 - trace.a); 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; radiance.rgb += emission.rgb;
} }
@ -195,7 +195,7 @@ void main() {
} }
else { else {
// precompute cone sampling: // precompute cone sampling:
vec3 coneDirection = DIFFUSE_CONE_DIRECTIONS[i - 6]; vec3 coneDirection = diffuseConeDirections[i - 6];
vec3 aniso_direction = -coneDirection; vec3 aniso_direction = -coneDirection;
uvec3 face_offsets = uvec3( uvec3 face_offsets = uvec3(
aniso_direction.x > 0 ? 0 : 1, aniso_direction.x > 0 ? 0 : 1,

View File

@ -304,10 +304,13 @@ class RenderPath {
currentD = 1; currentD = 1;
currentFace = -1; currentFace = -1;
meshesSorted = false; meshesSorted = false;
sun = null;
for (l in Scene.active.lights) { for (l in Scene.active.lights) {
if (l.visible) l.buildMatrix(Scene.active.camera); 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; else point = l;
} }
light = Scene.active.lights[0]; light = Scene.active.lights[0];
@ -500,7 +503,7 @@ class RenderPath {
} }
public function drawMeshes(context: String) { public function drawMeshes(context: String) {
var isShadows = context == "shadowmap"; var isShadows = context == "shadowmap" || context == "shadowmap_transparent";
if (isShadows) { if (isShadows) {
// Disabled shadow casting for this light // Disabled shadow casting for this light
if (light == null || !light.data.raw.cast_shadow || !light.visible || light.data.raw.strength == 0) return; if (light == null || !light.data.raw.cast_shadow || !light.visible || light.data.raw.strength == 0) return;

View File

@ -71,6 +71,13 @@ class Scene {
public var embedded: Map<String, kha.Image>; public var embedded: Map<String, kha.Image>;
#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
public var ready: Bool; // Async in progress public var ready: Bool; // Async in progress
public var traitInits: Array<Void->Void> = []; public var traitInits: Array<Void->Void> = [];
@ -107,6 +114,9 @@ class Scene {
armatures = []; armatures = [];
#end #end
embedded = new Map(); embedded = new Map();
#if (rp_renderer == "Deferred")
materialParamsBuffer = new kha.arrays.Float32Array(MAX_MATERIALS * FLOATS_PER_MATERIAL_PARAM);
#end
root = new Object(); root = new Object();
root.name = "Root"; root.name = "Root";
traitInits = []; traitInits = [];
@ -204,6 +214,89 @@ class Scene {
root.remove(); root.remove();
} }
#if (rp_renderer == "Deferred")
public function markMaterialParamsDirty() {
materialParamsDirty = true;
}
public function updateMaterialParams() {
if (!materialParamsDirty) return;
materialParamsDirty = false;
var buf = materialParamsBuffer;
for (m in meshes) {
if (m.materials == null) continue;
for (mat in m.materials) {
if (mat == null) continue;
if (mat.contexts == null) continue;
var slot = -1;
var bc = null;
for (ctx in mat.contexts) {
if (ctx == null || ctx.raw == null) continue;
if (ctx.raw.name == "mesh" && ctx.raw.bind_constants != null) {
bc = ctx.raw.bind_constants;
for (c in bc) {
if (c != null && c.name == "materialID" && c.intValue != null) {
slot = c.intValue;
}
}
break;
}
}
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 || c.floatValue == null) continue;
switch (c.name) {
// matp0: vec4(anisotropy, anisoRot, sheen, sheenRough)
case "anisotropy": buf[base + 0] = c.floatValue;
case "anisoRot": buf[base + 1] = c.floatValue;
case "sheen": buf[base + 2] = c.floatValue;
case "sheenRough": buf[base + 3] = c.floatValue;
// matp1: vec4(clearcoat, clearcoatRough, coatIOR, coatTintR)
case "clearcoat": buf[base + 4] = c.floatValue;
case "clearcoatRough": buf[base + 5] = c.floatValue;
case "coatIOR": buf[base + 6] = c.floatValue;
case "coatTintR": buf[base + 7] = c.floatValue;
// matp2: vec4(coatTintG, coatTintB, transmission, transRough)
case "coatTintG": buf[base + 8] = c.floatValue;
case "coatTintB": buf[base + 9] = c.floatValue;
case "transmission": buf[base + 10] = c.floatValue;
case "transmissionRough": buf[base + 11] = c.floatValue;
// matp3: vec4(ior, thinWall, subsurface, subsurfaceAnisotropy)
case "ior": buf[base + 12] = c.floatValue;
case "thinWall": buf[base + 13] = c.floatValue;
case "subsurface": buf[base + 14] = c.floatValue;
case "subsurfaceAnisotropy": buf[base + 15] = c.floatValue;
// matp4: vec4(subsurfaceRadiusR, subsurfaceRadiusG, subsurfaceRadiusB, subsurfaceColorR)
case "subsurfaceRadiusR": buf[base + 16] = c.floatValue;
case "subsurfaceRadiusG": buf[base + 17] = c.floatValue;
case "subsurfaceRadiusB": buf[base + 18] = c.floatValue;
case "subsurfaceColorR": buf[base + 19] = c.floatValue;
// matp5: vec4(subsurfaceColorG, subsurfaceColorB, sheenTintR, sheenTintG)
case "subsurfaceColorG": buf[base + 20] = c.floatValue;
case "subsurfaceColorB": buf[base + 21] = c.floatValue;
case "sheenTintR": buf[base + 22] = c.floatValue;
case "sheenTintG": buf[base + 23] = c.floatValue;
// 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;
}
}
}
}
}
#end
static var framePassed = true; static var framePassed = true;
public static function setActive(sceneName: String, done: Object->Void = null) { public static function setActive(sceneName: String, done: Object->Void = null) {
if (!framePassed) return; if (!framePassed) return;
@ -351,6 +444,9 @@ class Scene {
public function addMeshObject(data: MeshData, materials: Vector<MaterialData>, parent: Object = null): MeshObject { public function addMeshObject(data: MeshData, materials: Vector<MaterialData>, parent: Object = null): MeshObject {
var object = new MeshObject(data, materials); var object = new MeshObject(data, materials);
parent != null ? object.setParent(parent) : object.setParent(root); parent != null ? object.setParent(parent) : object.setParent(root);
#if (rp_renderer == "Deferred")
markMaterialParamsDirty();
#end
return object; return object;
} }

View File

@ -245,7 +245,12 @@ class LightObject extends Object {
// Snap to texel coords - fix translation swim // Snap to texel coords - fix translation swim
var smsize = data.raw.shadowmap_size; var smsize = data.raw.shadowmap_size;
#if lnx_csm // Cascades #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 #end
var worldPerTexelX = (maxx - minx) / smsize; var worldPerTexelX = (maxx - minx) / smsize;
var worldPerTexelY = (maxy - miny) / smsize; var worldPerTexelY = (maxy - miny) / smsize;
@ -685,22 +690,6 @@ class LightObject extends Object {
return LWVPMatrixArray; 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 { public static inline function getMaxLightsCluster(): Int {
#if (rp_max_lights_cluster == 8) #if (rp_max_lights_cluster == 8)
return 8; return 8;
@ -718,6 +707,90 @@ class LightObject extends Object {
} }
#end // lnx_clusters #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 { public inline function right(): Vec4 {
return new Vec4(V._00, V._10, V._20); return new Vec4(V._00, V._10, V._20);
} }

View File

@ -89,6 +89,9 @@ class MeshObject extends Object {
#end #end
if (tilesheet != null) tilesheet.remove(); if (tilesheet != null) tilesheet.remove();
if (Scene.active != null) Scene.active.meshes.remove(this); if (Scene.active != null) Scene.active.meshes.remove(this);
#if (rp_renderer == "Deferred")
if (Scene.active != null) Scene.active.markMaterialParamsDirty();
#end
data.refcount--; data.refcount--;
super.remove(); super.remove();
} }

View File

@ -827,9 +827,20 @@ class Uniforms {
} }
} }
case "_shadowMapSize": { 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 = 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: default:
@ -887,6 +898,11 @@ class Uniforms {
case "_envmapIrradiance": { case "_envmapIrradiance": {
fa = Scene.active.world == null ? WorldData.getEmptyIrradiance() : Scene.active.world.probe.irradiance; fa = Scene.active.world == null ? WorldData.getEmptyIrradiance() : Scene.active.world.probe.irradiance;
} }
#if (rp_renderer == "Deferred")
case "_materialParams": {
fa = Scene.active.materialParamsBuffer;
}
#end
#if lnx_clusters #if lnx_clusters
case "_lightsArray": { case "_lightsArray": {
fa = LightObject.lightsArray; fa = LightObject.lightsArray;
@ -904,12 +920,15 @@ class Uniforms {
#end // lnx_clusters #end // lnx_clusters
#if lnx_csm #if lnx_csm
case "_cascadeData": { case "_cascadeData": {
for (l in Scene.active.lights) { var sun = RenderPath.active.sun;
if (l.data.raw.type == "sun") { if (sun != null && sun.data.raw.type == "sun") {
fa = l.getCascadeData(); fa = sun.getCascadeData();
break;
} }
} }
#end
#if lnx_shadowmap_atlas
case "_tileBoundsSunArray": {
fa = LightObject.updateSunTileBoundsArray();
} }
#end #end
} }
@ -1064,21 +1083,21 @@ class Uniforms {
} }
} }
case "_biasLightWorldViewProjectionMatrixSun": { case "_biasLightWorldViewProjectionMatrixSun": {
for (l in iron.Scene.active.lights) { var sun = RenderPath.active.sun;
if (l.data.raw.type == "sun") { if (sun != null && sun.data.raw.type == "sun") {
// object is null for DrawQuad // object is null for DrawQuad
object == null ? helpMat.setIdentity() : helpMat.setFrom(object.transform.worldUnpack); object == null ? helpMat.setIdentity() : helpMat.setFrom(object.transform.worldUnpack);
helpMat.multmat(l.VP); helpMat.multmat(sun.VP);
helpMat.multmat(biasMat); helpMat.multmat(biasMat);
#if lnx_shadowmap_atlas #if lnx_shadowmap_atlas
// tile matrix // tile matrix
helpMat2.setIdentity(); helpMat2.setIdentity();
// scale [0-1] coords to [0-tilescale] // scale [0-1] coords to [0-tilescale]
helpMat2._00 = l.tileScale[0]; helpMat2._00 = sun.tileScale[0];
helpMat2._11 = l.tileScale[0]; helpMat2._11 = sun.tileScale[0];
// offset coordinate start from [0, 0] to [tile-start-x, tile-start-y] // offset coordinate start from [0, 0] to [tile-start-x, tile-start-y]
helpMat2._30 = l.tileOffsetX[0]; helpMat2._30 = sun.tileOffsetX[0];
helpMat2._31 = l.tileOffsetY[0]; helpMat2._31 = sun.tileOffsetY[0];
helpMat.multmat(helpMat2); helpMat.multmat(helpMat2);
#if (!kha_opengl) #if (!kha_opengl)
helpMat2.setIdentity(); helpMat2.setIdentity();
@ -1088,8 +1107,6 @@ class Uniforms {
#end #end
#end #end
m = helpMat; m = helpMat;
break;
}
} }
} }
#if rp_probes #if rp_probes
@ -1373,7 +1390,7 @@ class Uniforms {
if (fa == null) return; if (fa == null) return;
g.setFloats(location, fa); g.setFloats(location, fa);
} }
else if (c.type == "int") { else if (c.type == "int" || c.type == "uint") {
var i: Null<Int> = null; var i: Null<Int> = null;
switch (c.link) { switch (c.link) {
case "_uid": { case "_uid": {

View File

@ -39,7 +39,6 @@ class Inc {
#if (rp_voxels == "Voxel GI") #if (rp_voxels == "Voxel GI")
static var voxel_td1:kha.graphics4.TextureUnit; static var voxel_td1:kha.graphics4.TextureUnit;
static var voxel_te1:kha.graphics4.TextureUnit; static var voxel_te1:kha.graphics4.TextureUnit;
static var voxel_cc1:kha.graphics4.ConstantLocation;
#else #else
#if lnx_voxelgi_shadows #if lnx_voxelgi_shadows
static var voxel_te1:kha.graphics4.TextureUnit; static var voxel_te1:kha.graphics4.TextureUnit;
@ -137,10 +136,7 @@ class Inc {
break; break;
if (LightObject.discardLightCulled(light)) continue; if (LightObject.discardLightCulled(light)) continue;
if (light.data.raw.type == "point") { if (light.data.raw.type == "point") {
if (!light.data.raw.cast_shadow) { if (light.data.raw.cast_shadow) {
j += 4 * 6;
continue;
}
for(k in 0...6) { for(k in 0...6) {
LightObject.pointLightsData[j ] = light.tileOffsetX[k]; // posx LightObject.pointLightsData[j ] = light.tileOffsetX[k]; // posx
LightObject.pointLightsData[j + 1] = light.tileOffsetY[k]; // posy LightObject.pointLightsData[j + 1] = light.tileOffsetY[k]; // posy
@ -149,6 +145,13 @@ class Inc {
j += 4; j += 4;
} }
} }
else {
j += 4 * 6;
}
}
else {
j += 4 * 6; // Reserve slot for non-point lights to keep index aligned
}
i++; i++;
} }
} }
@ -214,11 +217,13 @@ class Inc {
if (!light.lightInAtlas && !light.culledLight && light.visible && light.shadowMapScale > 0.0 if (!light.lightInAtlas && !light.culledLight && light.visible && light.shadowMapScale > 0.0
&& light.data.raw.strength > 0.0 && light.data.raw.cast_shadow) { && light.data.raw.strength > 0.0 && light.data.raw.cast_shadow) {
ShadowMapAtlas.addLight(light, false); ShadowMapAtlas.addLight(light, false);
LightObject.tileBoundsDirty = true;
} }
#if rp_shadowmap_transparent #if rp_shadowmap_transparent
if (!light.lightInAtlasTransparent && !light.culledLight && light.visible && light.shadowMapScale > 0.0 if (!light.lightInAtlasTransparent && !light.culledLight && light.visible && light.shadowMapScale > 0.0
&& light.data.raw.strength > 0.0 && light.data.raw.cast_shadow) { && light.data.raw.strength > 0.0 && light.data.raw.cast_shadow) {
ShadowMapAtlas.addLight(light, true); ShadowMapAtlas.addLight(light, true);
LightObject.tileBoundsDirty = true;
} }
#end #end
} }
@ -308,6 +313,7 @@ class Inc {
var newTile = ShadowMapTile.assignTiles(tile.light, atlas, tile); var newTile = ShadowMapTile.assignTiles(tile.light, atlas, tile);
if (newTile != null) if (newTile != null)
atlas.activeTiles.push(newTile); atlas.activeTiles.push(newTile);
LightObject.tileBoundsDirty = true;
} }
updatePointLightAtlasData(false); updatePointLightAtlasData(false);
#end #end
@ -315,6 +321,7 @@ class Inc {
for (tile in tilesToRemove) { for (tile in tilesToRemove) {
atlas.activeTiles.remove(tile); atlas.activeTiles.remove(tile);
tile.freeTile(); tile.freeTile();
LightObject.tileBoundsDirty = true;
} }
} }
@ -405,6 +412,7 @@ class Inc {
var newTile = ShadowMapTile.assignTiles(tile.light, atlas, tile); var newTile = ShadowMapTile.assignTiles(tile.light, atlas, tile);
if (newTile != null) if (newTile != null)
atlas.activeTiles.push(newTile); atlas.activeTiles.push(newTile);
LightObject.tileBoundsDirty = true;
} }
updatePointLightAtlasData(true); updatePointLightAtlasData(true);
#end #end
@ -412,6 +420,7 @@ class Inc {
for (tile in tilesToRemove) { for (tile in tilesToRemove) {
atlas.activeTiles.remove(tile); atlas.activeTiles.remove(tile);
tile.freeTile(); tile.freeTile();
LightObject.tileBoundsDirty = true;
} }
} }
#end #end
@ -419,25 +428,30 @@ class Inc {
} }
#else #else
public static function bindShadowMap() { public static function bindShadowMap() {
for (l in iron.Scene.active.lights) { var sun = RenderPath.active.sun;
if (!l.visible || l.data.raw.type != "sun") continue; if (sun != null && sun.visible && sun.data.raw.type == "sun") {
var n = "shadowMap"; var n = "shadowMap";
path.bindTarget(n, n); path.bindTarget(n, n);
#if rp_shadowmap_transparent
var n = "shadowMapTransparent"; var n = "shadowMapTransparent";
path.bindTarget(n, n); path.bindTarget(n, n);
break; #end
} }
for (i in 0...pointIndex) { for (i in 0...pointIndex) {
var n = "shadowMapPoint[" + i + "]"; var n = "shadowMapPoint[" + i + "]";
path.bindTarget(n, n); path.bindTarget(n, n);
#if rp_shadowmap_transparent
var n = "shadowMapPointTransparent[" + i + "]"; var n = "shadowMapPointTransparent[" + i + "]";
path.bindTarget(n, n); path.bindTarget(n, n);
#end
} }
for (i in 0...spotIndex) { for (i in 0...spotIndex) {
var n = "shadowMapSpot[" + i + "]"; var n = "shadowMapSpot[" + i + "]";
path.bindTarget(n, n); path.bindTarget(n, n);
#if rp_shadowmap_transparent
var n = "shadowMapSpotTransparent[" + i + "]"; var n = "shadowMapSpotTransparent[" + i + "]";
path.bindTarget(n, n); path.bindTarget(n, n);
#end
} }
} }
@ -500,6 +514,7 @@ class Inc {
spotIndex = 0; spotIndex = 0;
for (l in iron.Scene.active.lights) { for (l in iron.Scene.active.lights) {
if (!l.visible) continue; if (!l.visible) continue;
if (l.data.raw.type == "sun" && l != RenderPath.active.sun) continue;
path.light = l; path.light = l;
var shadowmap = Inc.getShadowMap(l, false); var shadowmap = Inc.getShadowMap(l, false);
@ -523,6 +538,7 @@ class Inc {
spotIndex = 0; spotIndex = 0;
for (l in iron.Scene.active.lights) { for (l in iron.Scene.active.lights) {
if (!l.visible) continue; if (!l.visible) continue;
if (l.data.raw.type == "sun" && l != RenderPath.active.sun) continue;
path.light = l; path.light = l;
var shadowmap_transparent = Inc.getShadowMap(l, true); var shadowmap_transparent = Inc.getShadowMap(l, true);
@ -719,7 +735,7 @@ class Inc {
#else #else
t.format = "RGBA32"; t.format = "RGBA32";
#end #end
t.width = res * (6 + 16); t.width = res * (6 + Main.diffuseConeCount);
t.height = res * Main.voxelgiClipmapCount; t.height = res * Main.voxelgiClipmapCount;
t.depth = res; t.depth = res;
} }
@ -840,12 +856,10 @@ class Inc {
voxel_ca1 = voxel_sh1.getConstantLocation("clipmaps"); voxel_ca1 = voxel_sh1.getConstantLocation("clipmaps");
voxel_cb1 = voxel_sh1.getConstantLocation("clipmapLevel"); voxel_cb1 = voxel_sh1.getConstantLocation("clipmapLevel");
voxel_cc1 = voxel_sh1.getConstantLocation("envmapStrength");
#if (rp_voxels == "Voxel GI") #if (rp_voxels == "Voxel GI")
voxel_td1 = voxel_sh1.getTextureUnit("voxelsSampler"); voxel_td1 = voxel_sh1.getTextureUnit("voxelsSampler");
voxel_te1 = voxel_sh1.getTextureUnit("SDF"); voxel_te1 = voxel_sh1.getTextureUnit("SDF");
voxel_cc1 = voxel_sh1.getConstantLocation("envmapStrength");
#else #else
#if lnx_voxelgi_shadows #if lnx_voxelgi_shadows
voxel_te1 = voxel_sh1.getTextureUnit("SDF"); voxel_te1 = voxel_sh1.getTextureUnit("SDF");
@ -884,9 +898,11 @@ class Inc {
#if lnx_brdf #if lnx_brdf
voxel_tg3 = voxel_sh3.getTextureUnit("senvmapBrdf"); voxel_tg3 = voxel_sh3.getTextureUnit("senvmapBrdf");
#end #end
#if (rp_voxels == "Voxel AO")
#if lnx_radiance #if lnx_radiance
voxel_th3 = voxel_sh3.getTextureUnit("senvmapRadiance"); voxel_th3 = voxel_sh3.getTextureUnit("senvmapRadiance");
#end #end
#end
voxel_ca3 = voxel_sh3.getConstantLocation("clipmaps"); voxel_ca3 = voxel_sh3.getConstantLocation("clipmaps");
voxel_cb3 = voxel_sh3.getConstantLocation("InvVP"); voxel_cb3 = voxel_sh3.getConstantLocation("InvVP");
voxel_cc3 = voxel_sh3.getConstantLocation("eye"); voxel_cc3 = voxel_sh3.getConstantLocation("eye");
@ -895,12 +911,14 @@ class Inc {
#if lnx_irradiance #if lnx_irradiance
voxel_cf3 = voxel_sh3.getConstantLocation("shirr"); voxel_cf3 = voxel_sh3.getConstantLocation("shirr");
#end #end
#if (rp_voxels == "Voxel AO")
#if lnx_radiance #if lnx_radiance
voxel_cg3 = voxel_sh3.getConstantLocation("envmapNumMipmaps"); voxel_cg3 = voxel_sh3.getConstantLocation("envmapNumMipmaps");
#end #end
#if lnx_envcol #if lnx_envcol
voxel_ch3 = voxel_sh3.getConstantLocation("backgroundCol"); voxel_ch3 = voxel_sh3.getConstantLocation("backgroundCol");
#end #end
#end
} }
#if (rp_voxels == "Voxel GI") #if (rp_voxels == "Voxel GI")
if (voxel_sh4 == null) if (voxel_sh4 == null)
@ -988,7 +1006,6 @@ class Inc {
#if (rp_voxels == "Voxel GI") #if (rp_voxels == "Voxel GI")
g.setTexture(voxel_td1, rts.get("voxelsOutB").image); g.setTexture(voxel_td1, rts.get("voxelsOutB").image);
g.setImageTexture(voxel_te1, rts.get("voxelsSDF").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 #else
#if lnx_voxelgi_shadows #if lnx_voxelgi_shadows
g.setImageTexture(voxel_te1, rts.get("voxelsSDF").image); g.setImageTexture(voxel_te1, rts.get("voxelsSDF").image);
@ -1001,8 +1018,6 @@ class Inc {
g.setInt(voxel_cb1, iron.RenderPath.clipmapLevel); 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)); g.compute(Std.int(res / 8), Std.int(res / 8), Std.int(res / 8));
} }
@ -1163,9 +1178,6 @@ class Inc {
#if lnx_brdf #if lnx_brdf
g.setTexture(voxel_tg3, iron.Scene.active.embedded.get("brdf.png")); g.setTexture(voxel_tg3, iron.Scene.active.embedded.get("brdf.png"));
#end #end
#if lnx_radiance
g.setTexture(voxel_th3, iron.Scene.active.world.probe.radiance);
#end
var fa = fillVoxelClipmapsArray(clipmaps); var fa = fillVoxelClipmapsArray(clipmaps);
@ -1205,28 +1217,12 @@ class Inc {
iron.Scene.active.world.probe.irradiance; iron.Scene.active.world.probe.irradiance;
g.setFloats(voxel_cf3, irradiance); g.setFloats(voxel_cf3, irradiance);
#end #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); g.compute(Std.int((width + 7) / 8), Std.int((height + 7) / 8), 1);
} }
#end #end
#if (rp_voxels == "Voxel GI")
public static function resolveSpecular(g: kha.graphics4.Graphics) { public static function resolveSpecular(g: kha.graphics4.Graphics) {
var rts = path.renderTargets; var rts = path.renderTargets;
var res = iron.RenderPath.getVoxelRes(); var res = iron.RenderPath.getVoxelRes();
@ -1278,6 +1274,7 @@ class Inc {
g.compute(Std.int((width + 7) / 8), Std.int((height + 7) / 8), 1); g.compute(Std.int((width + 7) / 8), Std.int((height + 7) / 8), 1);
} }
#end
#if (rp_voxels == "Voxel GI") #if (rp_voxels == "Voxel GI")
#end // GI #end // GI

View File

@ -26,7 +26,8 @@ class RenderPathDeferred {
"gbuffer1", "gbuffer1",
#if rp_gbuffer2 "gbuffer2", #end #if rp_gbuffer2 "gbuffer2", #end
#if rp_gbuffer_emission "gbuffer_emission", #end #if rp_gbuffer_emission "gbuffer_emission", #end
#if (rp_ssrefr || lnx_voxelgi_refract) "gbuffer_refraction" #end #if (rp_ssrefr || lnx_voxelgi_refract) "gbuffer_refraction", #end
#if rp_clearcoat "gbuffer_coat_normal", #end
]); ]);
} }
@ -154,7 +155,11 @@ class RenderPathDeferred {
t.width = 0; t.width = 0;
t.height = 0; t.height = 0;
t.displayp = Inc.getDisplayp(); t.displayp = Inc.getDisplayp();
#if rp_hdr
t.format = "RGBA64"; t.format = "RGBA64";
#else
t.format = "RGBA32";
#end
t.scale = Inc.getSuperSampling(); t.scale = Inc.getSuperSampling();
path.createRenderTarget(t); path.createRenderTarget(t);
} }
@ -281,7 +286,7 @@ class RenderPathDeferred {
} }
#end #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(); var t = new RenderTargetRaw();
t.name = "bufa"; t.name = "bufa";
@ -408,6 +413,19 @@ class RenderPathDeferred {
} }
#end #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) #if (rp_ssrefr || lnx_voxelgi_refract)
{ {
var t = new RenderTargetRaw(); var t = new RenderTargetRaw();
@ -415,7 +433,7 @@ class RenderPathDeferred {
t.width = 0; t.width = 0;
t.height = 0; t.height = 0;
t.displayp = Inc.getDisplayp(); t.displayp = Inc.getDisplayp();
t.format = "RGBA64"; t.format = "RGBA32";
t.scale = Inc.getSuperSampling(); t.scale = Inc.getSuperSampling();
path.createRenderTarget(t); path.createRenderTarget(t);
} }
@ -555,7 +573,14 @@ class RenderPathDeferred {
#if (rp_ssrefr || lnx_voxelgi_refract) #if (rp_ssrefr || lnx_voxelgi_refract)
{ {
path.setTarget("gbuffer_refraction"); path.setTarget("gbuffer_refraction");
path.clearTarget(0xffff00ff); path.clearTarget(0x000000ff);
}
#end
#if rp_clearcoat
{
path.setTarget("gbuffer_coat_normal");
path.clearTarget(0x00000000);
} }
#end #end
@ -566,6 +591,7 @@ class RenderPathDeferred {
} }
#end #end
RenderPathCreator.setTargetMeshes(); RenderPathCreator.setTargetMeshes();
#if rp_dynres #if rp_dynres
@ -761,6 +787,12 @@ class RenderPathDeferred {
} }
#end #end
#if rp_clearcoat
{
path.bindTarget("gbuffer_coat_normal", "gbufferCoatNormal");
}
#end
#if rp_ssao #if rp_ssao
{ {
if (leenkx.data.Config.raw.rp_ssao != false) { if (leenkx.data.Config.raw.rp_ssao != false) {
@ -824,10 +856,13 @@ class RenderPathDeferred {
#end #end
#if rp_material_solid #if rp_material_solid
Scene.active.updateMaterialParams();
path.drawShader("shader_datas/deferred_light_solid/deferred_light"); path.drawShader("shader_datas/deferred_light_solid/deferred_light");
#elseif rp_material_mobile #elseif rp_material_mobile
Scene.active.updateMaterialParams();
path.drawShader("shader_datas/deferred_light_mobile/deferred_light"); path.drawShader("shader_datas/deferred_light_mobile/deferred_light");
#else #else
Scene.active.updateMaterialParams();
voxelao_pass ? voxelao_pass ?
path.drawShader("shader_datas/deferred_light/deferred_light_VoxelAOvar") : path.drawShader("shader_datas/deferred_light/deferred_light_VoxelAOvar") :
path.drawShader("shader_datas/deferred_light/deferred_light"); path.drawShader("shader_datas/deferred_light/deferred_light");
@ -883,7 +918,10 @@ class RenderPathDeferred {
#if rp_water #if rp_water
{ {
#if (!kha_opengl)
path.setDepthFrom("tex", "gbuffer1"); path.setDepthFrom("tex", "gbuffer1");
#end
path.setTarget("buf"); path.setTarget("buf");
path.bindTarget("tex", "tex"); path.bindTarget("tex", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass"); path.drawShader("shader_datas/copy_pass/copy_pass");
@ -891,7 +929,10 @@ class RenderPathDeferred {
path.bindTarget("_main", "gbufferD"); path.bindTarget("_main", "gbufferD");
path.bindTarget("buf", "tex"); path.bindTarget("buf", "tex");
path.drawShader("shader_datas/water_pass/water_pass"); path.drawShader("shader_datas/water_pass/water_pass");
#if (!kha_opengl)
path.setDepthFrom("tex", "gbuffer0"); path.setDepthFrom("tex", "gbuffer0");
#end
} }
#end #end
@ -907,7 +948,7 @@ class RenderPathDeferred {
var targetb = "ssrb"; var targetb = "ssrb";
#else #else
var targeta = "buf"; var targeta = "buf";
var targetb = "gbuffer1"; var targetb = "bufa";
#end #end
path.setTarget(targeta); path.setTarget(targeta);
@ -925,11 +966,21 @@ class RenderPathDeferred {
path.setTarget(targetb); path.setTarget(targetb);
path.bindTarget(targeta, "tex"); path.bindTarget(targeta, "tex");
path.bindTarget("gbuffer0", "gbuffer0"); 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.drawShader("shader_datas/blur_adaptive_pass/blur_adaptive_pass_x");
path.setTarget("tex"); path.setTarget("tex");
path.bindTarget(targetb, "tex"); path.bindTarget(targetb, "tex");
path.bindTarget("gbuffer0", "gbuffer0"); 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"); path.drawShader("shader_datas/blur_adaptive_pass/blur_adaptive_pass_y3_blend");
#if (!kha_opengl) #if (!kha_opengl)
@ -949,12 +1000,14 @@ class RenderPathDeferred {
path.bindTarget("tex", "tex"); path.bindTarget("tex", "tex");
path.bindTarget("_main", "gbufferD"); path.bindTarget("_main", "gbufferD");
path.bindTarget("gbuffer0", "gbuffer0"); path.bindTarget("gbuffer0", "gbuffer0");
path.bindTarget("gbuffer1", "gbuffer1");
path.drawShader("shader_datas/sss_pass/sss_pass_x"); path.drawShader("shader_datas/sss_pass/sss_pass_x");
path.setTarget("tex"); path.setTarget("tex");
path.bindTarget("buf", "tex"); path.bindTarget("buf", "tex");
path.bindTarget("_main", "gbufferD"); path.bindTarget("_main", "gbufferD");
path.bindTarget("gbuffer0", "gbuffer0"); path.bindTarget("gbuffer0", "gbuffer0");
path.bindTarget("gbuffer1", "gbuffer1");
path.drawShader("shader_datas/sss_pass/sss_pass_y"); path.drawShader("shader_datas/sss_pass/sss_pass_y");
#if (!kha_opengl) #if (!kha_opengl)
@ -967,20 +1020,20 @@ class RenderPathDeferred {
{ {
if (leenkx.data.Config.raw.rp_ssrefr != false) if (leenkx.data.Config.raw.rp_ssrefr != false)
{ {
//#if (!kha_opengl) #if (!kha_opengl)
//path.setDepthFrom("gbuffer0", "gbuffer1"); // Unbind depth so we can read it path.setDepthFrom("gbuffer0", "gbuffer1"); // Unbind depth so we can read it
//path.depthToRenderTarget.set("main", path.renderTargets.get("tex")); path.depthToRenderTarget.set("main", path.renderTargets.get("tex"));
//#end #end
//save depth //save depth
path.setTarget("gbufferD1"); path.setTarget("gbufferD1");
path.bindTarget("_main", "tex"); path.bindTarget("_main", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass"); path.drawShader("shader_datas/copy_pass/copy_pass");
//#if (!kha_opengl) #if (!kha_opengl)
//path.setDepthFrom("gbuffer0", "tex"); // Re-bind depth path.setDepthFrom("gbuffer0", "tex"); // Re-bind depth
//path.depthToRenderTarget.set("main", path.renderTargets.get("gbuffer0")); path.depthToRenderTarget.set("main", path.renderTargets.get("gbuffer0"));
//#end #end
//save background color //save background color
path.setTarget("refr"); path.setTarget("refr");
@ -1016,6 +1069,7 @@ class RenderPathDeferred {
path.bindTarget("tex", "tex"); path.bindTarget("tex", "tex");
path.bindTarget("gbufferD1", "gbufferD1"); path.bindTarget("gbufferD1", "gbufferD1");
path.bindTarget("gbuffer0", "gbuffer0"); path.bindTarget("gbuffer0", "gbuffer0");
path.bindTarget("gbuffer1", "gbuffer1");
path.bindTarget("refr", "tex1"); path.bindTarget("refr", "tex1");
path.bindTarget("_main", "gbufferD"); path.bindTarget("_main", "gbufferD");
path.bindTarget("gbuffer_refraction", "gbuffer_refraction"); path.bindTarget("gbuffer_refraction", "gbuffer_refraction");

View File

@ -130,7 +130,7 @@ class RenderPathForward {
t.width = 0; t.width = 0;
t.height = 0; t.height = 0;
t.displayp = Inc.getDisplayp(); t.displayp = Inc.getDisplayp();
t.format = "RGBA64"; t.format = "RGBA32";
t.scale = Inc.getSuperSampling(); t.scale = Inc.getSuperSampling();
path.createRenderTarget(t); path.createRenderTarget(t);
} }
@ -489,7 +489,7 @@ class RenderPathForward {
#if (rp_ssrefr || lnx_voxelgi_refract) #if (rp_ssrefr || lnx_voxelgi_refract)
{ {
path.setTarget("gbuffer_refraction"); path.setTarget("gbuffer_refraction");
path.clearTarget(0xffffff00); path.clearTarget(0x00000000);
} }
#end #end
@ -645,11 +645,21 @@ class RenderPathForward {
path.setTarget(targetb); path.setTarget(targetb);
path.bindTarget(targeta, "tex"); path.bindTarget(targeta, "tex");
path.bindTarget("lbuffer1", "gbuffer0"); 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.drawShader("shader_datas/blur_adaptive_pass/blur_adaptive_pass_x");
path.setTarget("lbuffer0"); path.setTarget("lbuffer0");
path.bindTarget(targetb, "tex"); path.bindTarget(targetb, "tex");
path.bindTarget("lbuffer1", "gbuffer0"); 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"); path.drawShader("shader_datas/blur_adaptive_pass/blur_adaptive_pass_y3_blend");
} }
} }

View File

@ -22,7 +22,7 @@ class UniformsManager extends Trait{
static var texturesRegistered = false; static var texturesRegistered = false;
static var texturesMap = new Map<Object, Map<MaterialData, Map<String, kha.Image>>>(); static var texturesMap = new Map<Object, Map<MaterialData, Map<String, kha.Image>>>();
static var sceneRemoveInitalized = false; static var sceneRemoveInitialized = false;
public var uniformExists = false; public var uniformExists = false;
@ -32,7 +32,8 @@ class UniformsManager extends Trait{
notifyOnAdd(init); notifyOnAdd(init);
notifyOnRemove(removeObject); notifyOnRemove(removeObject);
if (!sceneRemoveInitalized) { if (!sceneRemoveInitialized) {
sceneRemoveInitialized = true;
Scene.active.notifyOnRemove(removeScene); Scene.active.notifyOnRemove(removeScene);
} }
} }
@ -166,7 +167,12 @@ class UniformsManager extends Trait{
matMap.set(material, entry); 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 // Method to set map Object -> Material -> Link -> Vec3
@ -188,7 +194,12 @@ class UniformsManager extends Trait{
matMap.set(material, entry); 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 // Method to set map Object -> Material -> Link -> Texture
@ -210,7 +221,12 @@ class UniformsManager extends Trait{
matMap.set(material, entry); 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 // Method to get object specific material parameter float value
@ -330,10 +346,15 @@ class UniformsManager extends Trait{
var entry = material.get(mat); var entry = material.get(mat);
if (entry == null) return; if (entry == null) return;
var existed = entry.exists(link);
entry.remove(link); entry.remove(link);
if (!entry.keys().hasNext()) material.remove(mat); if (!entry.keys().hasNext()) material.remove(mat);
if (!material.keys().hasNext()) floatsMap.remove(object); 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) { public static function removeVectorValue(object: Object, mat:MaterialData, link: String) {
@ -344,10 +365,15 @@ class UniformsManager extends Trait{
var entry = material.get(mat); var entry = material.get(mat);
if (entry == null) return; if (entry == null) return;
var existed = entry.exists(link);
entry.remove(link); entry.remove(link);
if (!entry.keys().hasNext()) material.remove(mat); if (!entry.keys().hasNext()) material.remove(mat);
if (!material.keys().hasNext()) vectorsMap.remove(object); 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) { public static function removeTextureValue(object: Object, mat:MaterialData, link: String) {
@ -358,10 +384,15 @@ class UniformsManager extends Trait{
var entry = material.get(mat); var entry = material.get(mat);
if (entry == null) return; if (entry == null) return;
var existed = entry.exists(link);
entry.remove(link); entry.remove(link);
if (!entry.keys().hasNext()) material.remove(mat); if (!entry.keys().hasNext()) material.remove(mat);
if (!material.keys().hasNext()) texturesMap.remove(object); if (!material.keys().hasNext()) texturesMap.remove(object);
#if (rp_renderer == "Deferred")
if (existed && Scene.active != null) Scene.active.markMaterialParamsDirty();
#end
} }
} }

View File

@ -2514,6 +2514,9 @@ class LeenkxExporter:
# Ensure the same order for merging materials # Ensure the same order for merging materials
self.material_array.sort(key=lambda x: x.name) 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: if wrd.lnx_batch_materials:
mat_users = self.material_to_object_dict mat_users = self.material_to_object_dict
mat_lnxusers = self.material_to_lnx_object_dict mat_lnxusers = self.material_to_lnx_object_dict

View File

@ -247,7 +247,7 @@ def check_link(
included based on the given defines (`defs`). If that is the case, included based on the given defines (`defs`). If that is the case,
the found link is written to the `out` dictionary. 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: if link["name"] == cid:
valid_link = True valid_link = True

View File

@ -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_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_temporal/voxel_temporal.comp.glsl')
assets.add_shader_external(lnx.utils.get_sdk_path() + '/leenkx/Shaders/voxel_sdf_jumpflood/voxel_sdf_jumpflood.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): if rpdat.lnx_voxelgi_shadows and (point_lights > 0 or '_Sun' in wrd.world_defs):
wrd.world_defs += '_VoxelShadow' wrd.world_defs += '_VoxelShadow'
assets.add_khafile_def('lnx_voxelgi_shadows') assets.add_khafile_def('lnx_voxelgi_shadows')
@ -234,7 +233,7 @@ def build():
assets.add_khafile_def('rp_compositornodes') assets.add_khafile_def('rp_compositornodes')
compo_depth = False compo_depth = False
# wrd.compo_defs += '' # 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 wrd.compo_defs += '_CTone' + rpdat.lnx_tonemap
if rpdat.lnx_dithering != 'Off': if rpdat.lnx_dithering != 'Off':
wrd.compo_defs += '_CDithering' + rpdat.lnx_dithering wrd.compo_defs += '_CDithering' + rpdat.lnx_dithering
@ -441,6 +440,7 @@ def build():
if rpdat.rp_sss: if rpdat.rp_sss:
assets.add_khafile_def('rp_sss') assets.add_khafile_def('rp_sss')
if '_SSS' not in wrd.world_defs:
wrd.world_defs += '_SSS' wrd.world_defs += '_SSS'
assets.add_shader_pass('sss_pass') assets.add_shader_pass('sss_pass')
@ -484,13 +484,55 @@ def build():
if ignoreIrr: if ignoreIrr:
wrd.world_defs += '_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: if gbuffer2:
assets.add_khafile_def('rp_gbuffer2') assets.add_khafile_def('rp_gbuffer2')
wrd.world_defs += '_gbuffer2' wrd.world_defs += '_gbuffer2'
if '_ClearCoat' in wrd.world_defs:
assets.add_khafile_def('rp_clearcoat')
if callback is not None: if callback is not None:
callback() callback()
@ -503,11 +545,12 @@ def get_num_gbuffer_rts_deferred()-> int:
refraction_flags = {'_SSRefraction', '_VoxelRefract'} refraction_flags = {'_SSRefraction', '_VoxelRefract'}
found_refraction_flag = False 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 wrd.world_defs:
if flag in refraction_flags and not found_refraction_flag: if flag in refraction_flags and not found_refraction_flag:
num += 1 num += 1
found_refraction_flag = True found_refraction_flag = True
else: else:
num += 1 num += 1
return num return num

View File

@ -188,6 +188,78 @@ def parse(nodes, con: ShaderContext,
# Make sure that individual functions in this module aren't called with an incorrect/old parser state, set it to # Make sure that individual functions in this module aren't called with an incorrect/old parser state, set it to
# None so that it will raise exceptions when not set # None so that it will raise exceptions when not set
# store extended BRDF feature flags before destroying parser state
import re as _re
def _extract_vec3(s):
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):
try:
return float(val_str)
except (ValueError, TypeError):
return default
_sss_r, _sss_g, _sss_b = _extract_vec3(state.out_subsurface_radius)
_sss_scale = state.out_subsurface_scale
_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 = {
'clearcoat': state.out_clearcoat,
'clearcoatRough': state.out_clearcoat_rough,
'coatIOR': state.out_coat_ior,
'coatTintR': _coat_tint_r,
'coatTintG': _coat_tint_g,
'coatTintB': _coat_tint_b,
'sheen': state.out_sheen,
'sheenRough': state.out_sheen_rough,
'sheenTintR': _sheen_tint_r,
'sheenTintG': _sheen_tint_g,
'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,
'subsurfaceColorR': _sss_color_r,
'subsurfaceColorG': _sss_color_g,
'subsurfaceColorB': _sss_color_b,
'anisotropy': state.out_anisotropy,
'anisoRot': state.out_aniso_rot,
'transmission': state.out_transmission,
'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 state = None
@ -219,7 +291,15 @@ def parse_material_output(node: bpy.types.Node, custom_particle_node: bpy.types.
curshader = state.frag curshader = state.frag
state.curshader = curshader state.curshader = curshader
out_basecol, out_roughness, out_metallic, out_occlusion, out_specular, out_opacity, out_ior, out_emission_col = parse_shader_input(node.inputs[0]) outs = parse_shader_input(node.inputs[0])
out_basecol = outs[0]
out_roughness = outs[1]
out_metallic = outs[2]
out_occlusion = outs[3]
out_specular = outs[4]
out_opacity = outs[5]
out_ior = outs[6]
out_emission_col = outs[7]
if parse_surface: if parse_surface:
curshader.write(f'basecol = {out_basecol};') curshader.write(f'basecol = {out_basecol};')
curshader.write(f'roughness = {out_roughness};') curshader.write(f'roughness = {out_roughness};')
@ -227,6 +307,25 @@ def parse_material_output(node: bpy.types.Node, custom_particle_node: bpy.types.
curshader.write(f'occlusion = {out_occlusion};') curshader.write(f'occlusion = {out_occlusion};')
curshader.write(f'specular = {out_specular};') curshader.write(f'specular = {out_specular};')
curshader.write(f'emissionCol = {out_emission_col};') curshader.write(f'emissionCol = {out_emission_col};')
curshader.write(f'subsurface = {outs[8]};')
curshader.write(f'subsurfaceRadius = {outs[9]};')
curshader.write(f'subsurfaceColor = {outs[10]};')
curshader.write(f'specularTint = {outs[11]};')
curshader.write(f'anisotropy = {outs[12]};')
curshader.write(f'anisoRot = {outs[13]};')
curshader.write(f'sheen = {outs[14]};')
curshader.write(f'sheenRough = {outs[15]};')
curshader.write(f'sheenTint = {outs[16]};')
curshader.write(f'clearcoat = {outs[17]};')
curshader.write(f'clearcoatRough = {outs[18]};')
curshader.write(f'transmission = {outs[19]};')
curshader.write(f'transmissionRough = {outs[20]};')
curshader.write(f'thinWall = {outs[21]};')
curshader.write(f'tangent = {outs[22]};')
curshader.write(f'subsurfaceScale = {outs[23]};')
curshader.write(f'subsurfaceAnisotropy = {outs[24]};')
curshader.write(f'coatIOR = {outs[25]};')
curshader.write(f'coatTint = {outs[26]};')
if mat_state.emission_type == mat_state.EmissionType.SHADELESS: if mat_state.emission_type == mat_state.EmissionType.SHADELESS:
if '_EmissionShadeless' not in wrd.world_defs: if '_EmissionShadeless' not in wrd.world_defs:
@ -330,6 +429,10 @@ def parse_shader(node: bpy.types.Node, socket: bpy.types.NodeSocket) -> Tuple[st
'BSDF_GLASS', 'BSDF_GLASS',
'HOLDOUT', 'HOLDOUT',
'SUBSURFACE_SCATTERING', 'SUBSURFACE_SCATTERING',
'BSDF_REFRACTION',
'BSDF_TOON',
'BSDF_HAIR',
'BSDF_HAIR_PRINCIPLED',
'BSDF_TRANSLUCENT', 'BSDF_TRANSLUCENT',
'BSDF_TRANSPARENT', 'BSDF_TRANSPARENT',
'BSDF_VELVET', 'BSDF_VELVET',
@ -662,11 +765,11 @@ def vector_curve(name, fac, points):
# Map vector # 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) 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': if inp.is_linked and inp.links[0].from_node.type != 'GROUP_INPUT':
normal_res = parse_vector_input(inp) normal_res = parse_vector_input(inp)
if normal_res != None: 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): def is_parsed(node_store_name: str):

View File

@ -20,11 +20,43 @@ else:
if bpy.app.version < (4, 0, 0): if bpy.app.version < (4, 0, 0):
EMISSION_COLOR = 'Emission' EMISSION_COLOR = 'Emission'
SUBSURFACE = 'Subsurface' SUBSURFACE = 'Subsurface'
SUBSURFACE_RADIUS = 'Subsurface Radius'
SUBSURFACE_COLOR = 'Subsurface Color'
SPECULAR = 'Specular' SPECULAR = 'Specular'
SPECULAR_TINT = 'Specular Tint'
ANISOTROPIC = 'Anisotropic'
ANISOTROPIC_ROT = 'Anisotropic Rotation'
SHEEN = 'Sheen'
SHEEN_TINT = 'Sheen Tint'
CLEARCOAT = 'Clearcoat'
CLEARCOAT_ROUGHNESS = 'Clearcoat Roughness'
CLEARCOAT_NORMAL = 'Clearcoat Normal'
TRANSMISSION = 'Transmission'
TRANSMISSION_ROUGHNESS = 'Transmission Roughness'
TANGENT = 'Tangent'
else: else:
EMISSION_COLOR = 'Emission Color' EMISSION_COLOR = 'Emission Color'
SUBSURFACE = 'Subsurface Weight' SUBSURFACE = 'Subsurface Weight'
SUBSURFACE_RADIUS = 'Subsurface Radius'
SUBSURFACE_COLOR = 'Subsurface Color'
SPECULAR = 'Specular IOR Level' SPECULAR = 'Specular IOR Level'
SPECULAR_TINT = 'Specular Tint'
ANISOTROPIC = 'Anisotropic'
ANISOTROPIC_ROT = 'Anisotropic Rotation'
SHEEN = 'Sheen Weight'
SHEEN_TINT = 'Sheen Tint'
SHEEN_ROUGHNESS = 'Sheen Roughness'
CLEARCOAT = 'Coat Weight'
CLEARCOAT_ROUGHNESS = 'Coat Roughness'
CLEARCOAT_NORMAL = 'Coat Normal'
COAT_IOR = 'Coat IOR'
COAT_TINT = 'Coat Tint'
TRANSMISSION = 'Transmission Weight'
TRANSMISSION_ROUGHNESS = 'Transmission Roughness'
TANGENT = 'Tangent'
THIN_WALL = 'Thin Wall'
SUBSURFACE_SCALE = 'Subsurface Scale'
SUBSURFACE_ANISOTROPY = 'Subsurface Anisotropy'
def parse_mixshader(node: bpy.types.ShaderNodeMixShader, out_socket: NodeSocket, state: ParserState) -> None: def parse_mixshader(node: bpy.types.ShaderNodeMixShader, out_socket: NodeSocket, state: ParserState) -> None:
@ -45,55 +77,89 @@ 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)) 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 mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
sss_before_1 = mat_state.needs_sss o1 = c.parse_shader_input(node.inputs[1])
bc1, rough1, met1, occ1, spec1, opac1, ior1, emi1 = c.parse_shader_input(node.inputs[1])
sss_1 = mat_state.needs_sss
ek1 = mat_state.emission_type ek1 = mat_state.emission_type
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION 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])
bc2, rough2, met2, occ2, spec2, opac2, ior2, emi2 = c.parse_shader_input(node.inputs[2])
sss_2 = mat_state.needs_sss
ek2 = mat_state.emission_type ek2 = mat_state.emission_type
mat_state.needs_sss = sss_1 or sss_2
if state.parse_surface: if state.parse_surface:
state.out_basecol = '({0} * {3} + {1} * {2})'.format(bc1, bc2, fac_var, fac_inv_var) fm = '{0} * {3} + {1} * {2}' # fac mix template: a*fac_inv + b*fac
state.out_roughness = '({0} * {3} + {1} * {2})'.format(rough1, rough2, fac_var, fac_inv_var) fv = (fac_var, fac_inv_var)
state.out_metallic = '({0} * {3} + {1} * {2})'.format(met1, met2, fac_var, fac_inv_var) state.out_basecol = fm.format(o1[0], o2[0], fv[0], fv[1])
state.out_occlusion = '({0} * {3} + {1} * {2})'.format(occ1, occ2, fac_var, fac_inv_var) state.out_roughness = fm.format(o1[1], o2[1], fv[0], fv[1])
state.out_specular = '({0} * {3} + {1} * {2})'.format(spec1, spec2, fac_var, fac_inv_var) state.out_metallic = fm.format(o1[2], o2[2], fv[0], fv[1])
state.out_emission_col = '({0} * {3} + {1} * {2})'.format(emi1, emi2, fac_var, fac_inv_var) state.out_occlusion = fm.format(o1[3], o2[3], fv[0], fv[1])
state.out_specular = fm.format(o1[4], o2[4], fv[0], fv[1])
state.out_emission_col = fm.format(o1[7], o2[7], fv[0], fv[1])
state.out_subsurface = fm.format(o1[8], o2[8], fv[0], fv[1])
state.out_subsurface_radius = fm.format(o1[9], o2[9], fv[0], fv[1])
state.out_subsurface_color = fm.format(o1[10], o2[10], fv[0], fv[1])
state.out_specular_tint = fm.format(o1[11], o2[11], fv[0], fv[1])
state.out_anisotropy = fm.format(o1[12], o2[12], fv[0], fv[1])
state.out_aniso_rot = fm.format(o1[13], o2[13], fv[0], fv[1])
state.out_sheen = fm.format(o1[14], o2[14], fv[0], fv[1])
state.out_sheen_rough = fm.format(o1[15], o2[15], fv[0], fv[1])
state.out_sheen_tint = fm.format(o1[16], o2[16], fv[0], fv[1])
state.out_clearcoat = fm.format(o1[17], o2[17], fv[0], fv[1])
state.out_clearcoat_rough = fm.format(o1[18], o2[18], fv[0], fv[1])
state.out_transmission = fm.format(o1[19], o2[19], fv[0], fv[1])
state.out_transmission_rough = fm.format(o1[20], o2[20], fv[0], fv[1])
state.out_thin_wall = fm.format(o1[21], o2[21], fv[0], fv[1])
state.out_tangent = fm.format(o1[22], o2[22], fv[0], fv[1])
state.out_subsurface_scale = fm.format(o1[23], o2[23], fv[0], fv[1])
state.out_subsurface_anisotropy = fm.format(o1[24], o2[24], fv[0], fv[1])
state.out_coat_ior = fm.format(o1[25], o2[25], fv[0], fv[1])
state.out_coat_tint = fm.format(o1[26], o2[26], fv[0], fv[1])
mat_state.emission_type = mat_state.EmissionType.get_effective_combination(ek1, ek2) mat_state.emission_type = mat_state.EmissionType.get_effective_combination(ek1, ek2)
if state.parse_opacity: if state.parse_opacity:
state.out_opacity = '({0} * {3} + {1} * {2})'.format(opac1, opac2, fac_var, fac_inv_var) fm = '{0} * {3} + {1} * {2}'
state.out_ior = '({0} * {3} + {1} * {2})'.format(ior1, ior2, fac_var, fac_inv_var) fv = (fac_var, fac_inv_var)
state.out_opacity = fm.format(o1[5], o2[5], fv[0], fv[1])
state.out_ior = fm.format(o1[6], o2[6], fv[0], fv[1])
def parse_addshader(node: bpy.types.ShaderNodeAddShader, out_socket: NodeSocket, state: ParserState) -> None: def parse_addshader(node: bpy.types.ShaderNodeAddShader, out_socket: NodeSocket, state: ParserState) -> None:
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
sss_before_1 = mat_state.needs_sss o1 = c.parse_shader_input(node.inputs[0])
bc1, rough1, met1, occ1, spec1, opac1, ior1, emi1 = c.parse_shader_input(node.inputs[0])
sss_1 = mat_state.needs_sss
ek1 = mat_state.emission_type ek1 = mat_state.emission_type
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION 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])
bc2, rough2, met2, occ2, spec2, opac2, ior2, emi2 = c.parse_shader_input(node.inputs[1])
sss_2 = mat_state.needs_sss
ek2 = mat_state.emission_type ek2 = mat_state.emission_type
mat_state.needs_sss = sss_1 or sss_2
if state.parse_surface: if state.parse_surface:
state.out_basecol = '({0} + {1})'.format(bc1, bc2) am = '{0} * 0.5 + {1} * 0.5' # add mix template (average)
state.out_roughness = '({0} * 0.5 + {1} * 0.5)'.format(rough1, rough2) state.out_basecol = '({0} + {1})'.format(o1[0], o2[0])
state.out_metallic = '({0} * 0.5 + {1} * 0.5)'.format(met1, met2) state.out_roughness = am.format(o1[1], o2[1])
state.out_occlusion = '({0} * 0.5 + {1} * 0.5)'.format(occ1, occ2) state.out_metallic = am.format(o1[2], o2[2])
state.out_specular = '({0} * 0.5 + {1} * 0.5)'.format(spec1, spec2) state.out_occlusion = am.format(o1[3], o2[3])
state.out_emission_col = '({0} + {1})'.format(emi1, emi2) state.out_specular = am.format(o1[4], o2[4])
state.out_emission_col = '({0} + {1})'.format(o1[7], o2[7])
state.out_subsurface = am.format(o1[8], o2[8])
state.out_subsurface_radius = am.format(o1[9], o2[9])
state.out_subsurface_color = am.format(o1[10], o2[10])
state.out_specular_tint = am.format(o1[11], o2[11])
state.out_anisotropy = am.format(o1[12], o2[12])
state.out_aniso_rot = am.format(o1[13], o2[13])
state.out_sheen = am.format(o1[14], o2[14])
state.out_sheen_rough = am.format(o1[15], o2[15])
state.out_sheen_tint = am.format(o1[16], o2[16])
state.out_clearcoat = am.format(o1[17], o2[17])
state.out_clearcoat_rough = am.format(o1[18], o2[18])
state.out_transmission = am.format(o1[19], o2[19])
state.out_transmission_rough = am.format(o1[20], o2[20])
state.out_thin_wall = am.format(o1[21], o2[21])
state.out_tangent = am.format(o1[22], o2[22])
state.out_subsurface_scale = am.format(o1[23], o2[23])
state.out_subsurface_anisotropy = am.format(o1[24], o2[24])
state.out_coat_ior = am.format(o1[25], o2[25])
state.out_coat_tint = am.format(o1[26], o2[26])
mat_state.emission_type = mat_state.EmissionType.get_effective_combination(ek1, ek2) mat_state.emission_type = mat_state.EmissionType.get_effective_combination(ek1, ek2)
if state.parse_opacity: if state.parse_opacity:
state.out_opacity = '({0} * 0.5 + {1} * 0.5)'.format(opac1, opac2) am = '{0} * 0.5 + {1} * 0.5'
state.out_ior = '({0} * 0.5 + {1} * 0.5)'.format(ior1, ior2) state.out_opacity = am.format(o1[5], o2[5])
state.out_ior = am.format(o1[6], o2[6])
# TODO: Refactor using c.get_*_input() # TODO: Refactor using c.get_*_input()
@ -103,11 +169,27 @@ if bpy.app.version < (2, 91, 0):
if state.parse_surface: if state.parse_surface:
c.write_normal(node.inputs['Normal']) c.write_normal(node.inputs['Normal'])
state.out_basecol = c.parse_vector_input(node.inputs['Base Color']) 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: state.out_subsurface = c.parse_value_input(node.inputs[SUBSURFACE])
mat_state.needs_sss = True state.out_subsurface_radius = c.parse_vector_input(node.inputs[SUBSURFACE_RADIUS])
state.out_subsurface_color = c.parse_vector_input(node.inputs[SUBSURFACE_COLOR])
state.out_metallic = c.parse_value_input(node.inputs['Metallic']) state.out_metallic = c.parse_value_input(node.inputs['Metallic'])
state.out_specular = c.parse_value_input(node.inputs[SPECULAR]) state.out_specular = c.parse_value_input(node.inputs[SPECULAR])
state.out_specular_tint = 'vec3({})'.format(c.parse_value_input(node.inputs[SPECULAR_TINT]))
state.out_roughness = c.parse_value_input(node.inputs['Roughness']) state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
state.out_anisotropy = c.parse_value_input(node.inputs[ANISOTROPIC])
state.out_aniso_rot = c.parse_value_input(node.inputs[ANISOTROPIC_ROT])
state.out_sheen = c.parse_value_input(node.inputs[SHEEN])
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:
state.out_transmission_rough = c.parse_value_input(trans_rough_socket)
state.out_tangent = c.parse_vector_input(node.inputs[TANGENT])
if node.inputs[EMISSION_COLOR].is_linked or not mat_utils.equals_color_socket(node.inputs[EMISSION_COLOR], (0.0, 0.0, 0.0), comp_alpha=False): if node.inputs[EMISSION_COLOR].is_linked or not mat_utils.equals_color_socket(node.inputs[EMISSION_COLOR], (0.0, 0.0, 0.0), comp_alpha=False):
emission_col = c.parse_vector_input(node.inputs[EMISSION_COLOR]) emission_col = c.parse_vector_input(node.inputs[EMISSION_COLOR])
state.out_emission_col = emission_col state.out_emission_col = emission_col
@ -116,7 +198,6 @@ if bpy.app.version < (2, 91, 0):
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
if state.parse_opacity: if state.parse_opacity:
state.out_ior = c.parse_value_input(node.inputs['IOR']) state.out_ior = c.parse_value_input(node.inputs['IOR'])
# In Blender 2.83, Alpha socket is at index 18, not 19
if 'Alpha' in node.inputs: if 'Alpha' in node.inputs:
state.out_opacity = c.parse_value_input(node.inputs['Alpha']) state.out_opacity = c.parse_value_input(node.inputs['Alpha'])
else: else:
@ -126,23 +207,25 @@ if bpy.app.version >= (2, 91, 0) and bpy.app.version < (4, 0, 0):
if state.parse_surface: if state.parse_surface:
c.write_normal(node.inputs['Normal']) c.write_normal(node.inputs['Normal'])
state.out_basecol = c.parse_vector_input(node.inputs['Base Color']) 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: state.out_subsurface = c.parse_value_input(node.inputs[SUBSURFACE])
mat_state.needs_sss = True state.out_subsurface_radius = c.parse_vector_input(node.inputs[SUBSURFACE_RADIUS])
# subsurface_radius = c.parse_vector_input(node.inputs[2]) state.out_subsurface_color = c.parse_vector_input(node.inputs[SUBSURFACE_COLOR])
# subsurface_color = c.parse_vector_input(node.inputs[3])
state.out_metallic = c.parse_value_input(node.inputs['Metallic']) state.out_metallic = c.parse_value_input(node.inputs['Metallic'])
state.out_specular = c.parse_value_input(node.inputs[SPECULAR]) state.out_specular = c.parse_value_input(node.inputs[SPECULAR])
# specular_tint = c.parse_vector_input(node.inputs[6]) state.out_specular_tint = 'vec3({})'.format(c.parse_value_input(node.inputs[SPECULAR_TINT]))
state.out_roughness = c.parse_value_input(node.inputs['Roughness']) state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
# aniso = c.parse_vector_input(node.inputs[8]) state.out_anisotropy = c.parse_value_input(node.inputs[ANISOTROPIC])
# aniso_rot = c.parse_vector_input(node.inputs[9]) state.out_aniso_rot = c.parse_value_input(node.inputs[ANISOTROPIC_ROT])
# sheen = c.parse_vector_input(node.inputs[10]) state.out_sheen = c.parse_value_input(node.inputs[SHEEN])
# sheen_tint = c.parse_vector_input(node.inputs[11]) state.out_sheen_tint = 'vec3({})'.format(c.parse_value_input(node.inputs[SHEEN_TINT]))
# clearcoat = c.parse_vector_input(node.inputs[12]) state.out_clearcoat = c.parse_value_input(node.inputs[CLEARCOAT])
# clearcoat_rough = c.parse_vector_input(node.inputs[13]) state.out_clearcoat_rough = c.parse_value_input(node.inputs[CLEARCOAT_ROUGHNESS])
# ior = c.parse_vector_input(node.inputs[14]) coat_normal_socket = node.inputs.get(CLEARCOAT_NORMAL)
# transmission = c.parse_vector_input(node.inputs[15]) if coat_normal_socket is not None:
# transmission_roughness = c.parse_vector_input(node.inputs[16]) 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])
if (node.inputs['Emission Strength'].is_linked or node.inputs['Emission Strength'].default_value != 0.0)\ if (node.inputs['Emission Strength'].is_linked or node.inputs['Emission Strength'].default_value != 0.0)\
and (node.inputs[EMISSION_COLOR].is_linked or not mat_utils.equals_color_socket(node.inputs[EMISSION_COLOR], (0.0, 0.0, 0.0), comp_alpha=False)): and (node.inputs[EMISSION_COLOR].is_linked or not mat_utils.equals_color_socket(node.inputs[EMISSION_COLOR], (0.0, 0.0, 0.0), comp_alpha=False)):
emission_col = c.parse_vector_input(node.inputs[EMISSION_COLOR]) emission_col = c.parse_vector_input(node.inputs[EMISSION_COLOR])
@ -151,8 +234,6 @@ if bpy.app.version >= (2, 91, 0) and bpy.app.version < (4, 0, 0):
mat_state.emission_type = mat_state.EmissionType.SHADED mat_state.emission_type = mat_state.EmissionType.SHADED
else: else:
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
# clearcoar_normal = c.parse_vector_input(node.inputs[21])
# tangent = c.parse_vector_input(node.inputs[22])
if state.parse_opacity: if state.parse_opacity:
state.out_ior = c.parse_value_input(node.inputs['IOR']) state.out_ior = c.parse_value_input(node.inputs['IOR'])
if 'Alpha' in node.inputs: if 'Alpha' in node.inputs:
@ -167,16 +248,26 @@ if bpy.app.version >= (4, 0, 0):
sss_input = node.inputs.get(SUBSURFACE) sss_input = node.inputs.get(SUBSURFACE)
if sss_input is not None: if sss_input is not None:
if sss_input.is_linked or sss_input.default_value > 0.0: state.out_subsurface = c.parse_value_input(sss_input)
mat_state.needs_sss = True
subsurface = c.parse_value_input(node.inputs[SUBSURFACE]) sss_radius_input = node.inputs.get(SUBSURFACE_RADIUS)
subsurface_radius = c.parse_vector_input(node.inputs['Subsurface Radius']) if sss_radius_input is not None:
subsurface_color_input = node.inputs.get('Subsurface Color') state.out_subsurface_radius = c.parse_vector_input(sss_radius_input)
if subsurface_color_input is not None:
subsurface_color = c.parse_vector_input(subsurface_color_input) sss_scale_input = node.inputs.get(SUBSURFACE_SCALE)
if sss_scale_input is not None:
state.out_subsurface_scale = c.parse_value_input(sss_scale_input)
sss_aniso_input = node.inputs.get(SUBSURFACE_ANISOTROPY)
if sss_aniso_input is not None:
state.out_subsurface_anisotropy = c.parse_value_input(sss_aniso_input)
sss_color_input = node.inputs.get(SUBSURFACE_COLOR)
if sss_color_input is not None:
state.out_subsurface_color = c.parse_vector_input(sss_color_input)
else: else:
subsurface_color = c.parse_vector_input(node.inputs['Base Color']) state.out_subsurface_color = c.parse_vector_input(node.inputs['Base Color'])
state.out_metallic = c.parse_value_input(node.inputs['Metallic']) state.out_metallic = c.parse_value_input(node.inputs['Metallic'])
specular_socket = node.inputs.get(SPECULAR) specular_socket = node.inputs.get(SPECULAR)
@ -189,6 +280,10 @@ if bpy.app.version >= (4, 0, 0):
else: else:
state.out_specular = '1.0' state.out_specular = '1.0'
spec_tint_socket = node.inputs.get(SPECULAR_TINT)
if spec_tint_socket is not None:
state.out_specular_tint = c.parse_vector_input(spec_tint_socket)
state.out_roughness = c.parse_value_input(node.inputs['Roughness']) state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
# Prevent black material when metal = 1.0 and roughness = 0.0 # Prevent black material when metal = 1.0 and roughness = 0.0
try: try:
@ -196,6 +291,55 @@ if bpy.app.version >= (4, 0, 0):
state.out_roughness = '0.001' state.out_roughness = '0.001'
except ValueError: except ValueError:
pass pass
aniso_socket = node.inputs.get(ANISOTROPIC)
if aniso_socket is not None:
state.out_anisotropy = c.parse_value_input(aniso_socket)
aniso_rot_socket = node.inputs.get(ANISOTROPIC_ROT)
if aniso_rot_socket is not None:
state.out_aniso_rot = c.parse_value_input(aniso_rot_socket)
sheen_socket = node.inputs.get(SHEEN)
if sheen_socket is not None:
state.out_sheen = c.parse_value_input(sheen_socket)
sheen_rough_socket = node.inputs.get(SHEEN_ROUGHNESS)
if sheen_rough_socket is not None:
state.out_sheen_rough = c.parse_value_input(sheen_rough_socket)
sheen_tint_socket = node.inputs.get(SHEEN_TINT)
if sheen_tint_socket is not None:
state.out_sheen_tint = c.parse_vector_input(sheen_tint_socket)
coat_socket = node.inputs.get(CLEARCOAT)
if coat_socket is not None:
state.out_clearcoat = c.parse_value_input(coat_socket)
coat_rough_socket = node.inputs.get(CLEARCOAT_ROUGHNESS)
if coat_rough_socket is not None:
state.out_clearcoat_rough = c.parse_value_input(coat_rough_socket)
coat_ior_socket = node.inputs.get(COAT_IOR)
if coat_ior_socket is not None:
state.out_coat_ior = c.parse_value_input(coat_ior_socket)
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:
state.out_transmission = c.parse_value_input(trans_socket)
trans_rough_socket = node.inputs.get(TRANSMISSION_ROUGHNESS)
if trans_rough_socket is not None:
state.out_transmission_rough = c.parse_value_input(trans_rough_socket)
thin_wall_socket = node.inputs.get(THIN_WALL)
if thin_wall_socket is not None:
state.out_thin_wall = c.parse_value_input(thin_wall_socket)
tangent_socket = node.inputs.get(TANGENT)
if tangent_socket is not None:
state.out_tangent = c.parse_vector_input(tangent_socket)
if (node.inputs['Emission Strength'].is_linked or node.inputs['Emission Strength'].default_value != 0.0)\ if (node.inputs['Emission Strength'].is_linked or node.inputs['Emission Strength'].default_value != 0.0)\
and (node.inputs[EMISSION_COLOR].is_linked or not mat_utils.equals_color_socket(node.inputs[EMISSION_COLOR], (0.0, 0.0, 0.0), comp_alpha=False)): and (node.inputs[EMISSION_COLOR].is_linked or not mat_utils.equals_color_socket(node.inputs[EMISSION_COLOR], (0.0, 0.0, 0.0), comp_alpha=False)):
emission_col = c.parse_vector_input(node.inputs[EMISSION_COLOR]) emission_col = c.parse_vector_input(node.inputs[EMISSION_COLOR])
@ -204,16 +348,6 @@ if bpy.app.version >= (4, 0, 0):
mat_state.emission_type = mat_state.EmissionType.SHADED mat_state.emission_type = mat_state.EmissionType.SHADED
else: else:
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
#state.out_occlusion = state.out_roughness
#state.out_aniso = c.parse_vector_input(node.inputs[14])
#state.out_aniso_rot = c.parse_vector_input(node.inputs[15])
#state.out_sheen = c.parse_vector_input(node.inputs[23])
#state.out_sheen_tint = c.parse_vector_input(node.inputs[25])
#state.out_clearcoat = c.parse_vector_input(node.inputs[18])
#state.out_clearcoat_rough = c.parse_vector_input(node.inputs[19])
#state.out_ior = c.parse_value_input(node.inputs[3])
#state.out_transmission = c.parse_vector_input(node.inputs[17])
#state.out_transmission_roughness = state.out_roughness
if state.parse_opacity: if state.parse_opacity:
state.out_ior = c.parse_value_input(node.inputs['IOR']) state.out_ior = c.parse_value_input(node.inputs['IOR'])
state.out_opacity = c.parse_value_input(node.inputs['Alpha']) state.out_opacity = c.parse_value_input(node.inputs['Alpha'])
@ -285,7 +419,68 @@ def parse_bsdfglass(node: bpy.types.ShaderNodeBsdfGlass, out_socket: NodeSocket,
def parse_bsdfhair(node: bpy.types.ShaderNodeBsdfHair, out_socket: NodeSocket, state: ParserState) -> None: def parse_bsdfhair(node: bpy.types.ShaderNodeBsdfHair, out_socket: NodeSocket, state: ParserState) -> None:
pass if state.parse_surface:
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
state.out_roughness = c.parse_value_input(node.inputs['RoughnessU'])
state.out_specular = '0.0'
rough_v_socket = node.inputs.get('RoughnessV')
if rough_v_socket is not None:
state.out_aniso_rot = c.parse_value_input(rough_v_socket)
tangent_socket = node.inputs.get('Tangent')
if tangent_socket is not None:
state.out_tangent = c.parse_vector_input(tangent_socket)
if bpy.app.version >= (2, 83, 0):
def parse_bsdfhairprincipled(node: 'bpy.types.ShaderNodeBsdfHairPrincipled', out_socket: NodeSocket, state: ParserState) -> None:
if state.parse_surface:
c.write_normal(node.inputs['Normal'])
parametrization = getattr(node, 'parametrization', 'COLOR')
if parametrization == 'COLOR':
color_socket = node.inputs.get('Color')
if color_socket is not None:
state.out_basecol = c.parse_vector_input(color_socket)
elif parametrization == 'MELANIN':
melanin_socket = node.inputs.get('Melanin')
if melanin_socket is not None:
state.out_subsurface = c.parse_value_input(melanin_socket)
melanin_red_socket = node.inputs.get('Melanin Redness')
if melanin_red_socket is not None:
state.out_specular_tint = 'vec3({})'.format(c.parse_value_input(melanin_red_socket))
elif parametrization == 'ABSORPTION':
absorption_socket = node.inputs.get('Absorption Coefficient')
if absorption_socket is not None:
state.out_specular_tint = c.parse_vector_input(absorption_socket)
tint_socket = node.inputs.get('Tint')
if tint_socket is not None:
state.out_subsurface_color = c.parse_vector_input(tint_socket)
rough_socket = node.inputs.get('Roughness')
if rough_socket is not None:
state.out_roughness = c.parse_value_input(rough_socket)
model = getattr(node, 'model', 'CHIANG')
if model == 'CHIANG':
radial_rough_socket = node.inputs.get('Radial Roughness')
if radial_rough_socket is not None:
state.out_aniso_rot = c.parse_value_input(radial_rough_socket)
coat_socket = node.inputs.get('Coat')
if coat_socket is not None:
state.out_clearcoat = c.parse_value_input(coat_socket)
random_color_socket = node.inputs.get('Random Color')
if random_color_socket is not None:
state.out_sheen = c.parse_value_input(random_color_socket)
random_rough_socket = node.inputs.get('Random Roughness')
if random_rough_socket is not None:
state.out_sheen_rough = c.parse_value_input(random_rough_socket)
offset_socket = node.inputs.get('Offset')
if offset_socket is not None:
state.out_anisotropy = c.parse_value_input(offset_socket)
state.out_specular = '0.0'
state.out_metallic = '0.0'
if state.parse_opacity:
ior_socket = node.inputs.get('IOR')
if ior_socket is not None:
state.out_ior = c.parse_value_input(ior_socket)
state.out_opacity = '1.0'
def parse_holdout(node: bpy.types.ShaderNodeHoldout, out_socket: NodeSocket, state: ParserState) -> None: def parse_holdout(node: bpy.types.ShaderNodeHoldout, out_socket: NodeSocket, state: ParserState) -> None:
@ -305,15 +500,20 @@ def parse_bsdfrefraction(node: bpy.types.ShaderNodeBsdfRefraction, out_socket: N
def parse_subsurfacescattering(node: bpy.types.ShaderNodeSubsurfaceScattering, out_socket: NodeSocket, state: ParserState) -> None: def parse_subsurfacescattering(node: bpy.types.ShaderNodeSubsurfaceScattering, out_socket: NodeSocket, state: ParserState) -> None:
if state.parse_surface: if state.parse_surface:
# Mark that this material needs SSS
mat_state.needs_sss = True
c.write_normal(node.inputs['Normal']) c.write_normal(node.inputs['Normal'])
state.out_basecol = c.parse_vector_input(node.inputs['Color']) state.out_basecol = c.parse_vector_input(node.inputs['Color'])
state.out_subsurface = c.parse_value_input(node.inputs['Scale'])
state.out_subsurface_radius = c.parse_vector_input(node.inputs['Radius'])
state.out_subsurface_color = c.parse_vector_input(node.inputs['Color'])
state.out_specular = '0.0'
def parse_bsdftoon(node: bpy.types.ShaderNodeBsdfToon, out_socket: NodeSocket, state: ParserState) -> None: def parse_bsdftoon(node: bpy.types.ShaderNodeBsdfToon, out_socket: NodeSocket, state: ParserState) -> None:
# c.write_normal(node.inputs[3]) if state.parse_surface:
pass c.write_normal(node.inputs['Normal'])
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
state.out_roughness = c.parse_value_input(node.inputs['Size'])
state.out_specular = '0.0'
def parse_bsdftranslucent(node: bpy.types.ShaderNodeBsdfTranslucent, out_socket: NodeSocket, state: ParserState) -> None: def parse_bsdftranslucent(node: bpy.types.ShaderNodeBsdfTranslucent, out_socket: NodeSocket, state: ParserState) -> None:

View File

@ -588,7 +588,7 @@ def parse_tex_voronoi(node: bpy.types.ShaderNodeTexVoronoi, out_socket: bpy.type
co = 'bposition' co = 'bposition'
scale = c.get_value_input(node, ['Scale']) 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']) randomness = c.get_value_input(node, ['Randomness'])
# Color or Position # Color or Position

View File

@ -40,6 +40,8 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]],
wrd = bpy.data.worlds['Lnx'] wrd = bpy.data.worlds['Lnx']
rpdat = lnx.utils.get_rp() rpdat = lnx.utils.get_rp()
mat_state.features = {}
# Texture caching for material batching # Texture caching for material batching
batch_cached_textures = [] batch_cached_textures = []
@ -62,8 +64,6 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]],
shader_data_name = material.lnx_custom_material shader_data_name = material.lnx_custom_material
bind_constants = {'mesh': []} bind_constants = {'mesh': []}
bind_textures = {'mesh': []} bind_textures = {'mesh': []}
mat_uses_sss = False
make_shader.make_instancing_and_skinning(material, mat_users) make_shader.make_instancing_and_skinning(material, mat_users)
for idx, item in enumerate(material.lnx_bind_textures_list): for idx, item in enumerate(material.lnx_bind_textures_list):
@ -80,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') 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'): 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 sd = shader_data.sd
else: else:
result = mat_batch.get(material) 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 sd = shader_data.sd
sss_used = False sss_used = False
@ -105,20 +105,170 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]],
if material.lnx_material_id != 0: if material.lnx_material_id != 0:
c['bind_constants'].append({'name': 'materialID', 'intValue': material.lnx_material_id}) c['bind_constants'].append({'name': 'materialID', 'intValue': material.lnx_material_id})
if material.lnx_material_id == 2: # extended BRDF parameters as bind_constants
wrd.world_defs += '_Hair' if rpdat.rp_renderer == 'Deferred':
feats = mat_state.features
elif rpdat.rp_sss_state != 'Off': import re as _re_mix
const = {'name': 'materialID'} _mix_re = _re_mix.compile(r'^\s*([+-]?\d*\.?\d+(?:[eE][+-]?\d+)?)\s*\*\s*\w+\s*([+-])\s*([+-]?\d*\.?\d+(?:[eE][+-]?\d+)?)\s*\*\s*\w+\s*$')
# Use per-material SSS flag from shader build
if mat_uses_sss: _term_re = _re_mix.compile(r'([+-]?\d*\.?\d+(?:[eE][+-]?\d+)?)\s*\*\s*[^\s*]+(?:\s*\*\s*[^\s*]+)*')
const['intValue'] = 2 _term_pair_re = _re_mix.compile(r'([+-]?\d*\.?\d+(?:[eE][+-]?\d+)?)\s*\*\s*([^\s*]+(?:\s*\*\s*[^\s*]+)*)')
sss_used = True
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):
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 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 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 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: if '_SSS' not in wrd.world_defs:
wrd.world_defs += '_SSS' wrd.world_defs += '_SSS'
has_ext_brdf = True
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 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
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:
if mat_state.next_ext_mat_id <= 15:
ext_id = mat_state.next_ext_mat_id
mat_state.next_ext_mat_id += 1
c['bind_constants'].append({'name': 'materialID', 'intValue': ext_id})
else: else:
const['intValue'] = 0 log.warn(f'material "{material.name}": extended BRDF material limit (15) exceeded, extended BRDF will be disabled for this material')
c['bind_constants'].append(const) 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 # TODO: Mesh only material batching
if wrd.lnx_batch_materials: if wrd.lnx_batch_materials:

View File

@ -77,6 +77,8 @@ def write(vert: shader.Shader, frag: shader.Shader):
if is_transparent_shadows: if is_transparent_shadows:
frag.add_uniform('sampler2D shadowMapSpotTransparent[4]', included=True) frag.add_uniform('sampler2D shadowMapSpotTransparent[4]', included=True)
frag.add_uniform('mat4 LWVPSpotArray[maxLightsCluster]', link='_biasLightWorldViewProjectionMatrixSpotArray', 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('for (int i = 0; i < min(numLights, maxLightsCluster); i++) {')
frag.write('int li = int(texelFetch(clustersData, ivec2(clusterI, i + 1), 0).r * 255);') frag.write('int li = int(texelFetch(clustersData, ivec2(clusterI, i + 1), 0).r * 255);')
@ -111,6 +113,16 @@ def write(vert: shader.Shader, frag: shader.Shader):
frag.add_uniform('mat4 invVP', '_inverseViewProjectionMatrix') frag.add_uniform('mat4 invVP', '_inverseViewProjectionMatrix')
frag.add_uniform('vec3 eye', '_cameraPosition') frag.add_uniform('vec3 eye', '_cameraPosition')
frag.write(', gbufferD, invVP, eye') frag.write(', gbufferD, invVP, eye')
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 '_Anisotropy' in wrd.world_defs:
frag.write(', anisotropy, anisoRot, tangent')
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(');') frag.write(');')
frag.write('}') # for numLights frag.write('}') # for numLights

View File

@ -154,6 +154,8 @@ def make_base(con_mesh, parse_opacity):
vert.add_out('vec3 wnormal') vert.add_out('vec3 wnormal')
make_attrib.write_norpos(con_mesh, vert) make_attrib.write_norpos(con_mesh, vert)
frag.write_attrib('vec3 n = normalize(wnormal);') 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: if mat_state.material.lnx_two_sided:
frag.write('if (!gl_FrontFacing) n *= -1;') # Flip normal when drawing back-face frag.write('if (!gl_FrontFacing) n *= -1;') # Flip normal when drawing back-face
@ -252,14 +254,16 @@ def make_deferred(con_mesh, rpasses):
frag.write('n.xy = n.z >= 0.0 ? n.xy : octahedronWrap(n.xy);') frag.write('n.xy = n.z >= 0.0 ? n.xy : octahedronWrap(n.xy);')
is_shadeless = mat_state.emission_type == mat_state.EmissionType.SHADELESS 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: 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 has_ext_brdf:
frag.write('uint matid = 0;') frag.write('uint matid = 0;')
if is_shadeless: if is_shadeless:
frag.write('matid = 1;') frag.write('matid = 1;')
frag.write('basecol = emissionCol;') frag.write('basecol = emissionCol;')
if '_SSS' in wrd.world_defs or '_Hair' in wrd.world_defs: if has_ext_brdf:
frag.add_uniform('int materialID') frag.add_uniform('int materialID')
frag.write('if (materialID == 2) matid = 2;') frag.write('if (materialID >= 3) matid = uint(materialID);')
else: else:
frag.write('const uint matid = 0;') frag.write('const uint matid = 0;')
@ -276,6 +280,14 @@ def make_deferred(con_mesh, rpasses):
if mat_state.material.lnx_ignore_irradiance: if mat_state.material.lnx_ignore_irradiance:
frag.write('fragColor[GBUF_IDX_2].b = 1.0;') 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 # Even if the material doesn't use emission we need to write to the
# emission buffer (if used) to prevent undefined behaviour # emission buffer (if used) to prevent undefined behaviour
frag.write('#ifdef _EmissionShaded') frag.write('#ifdef _EmissionShaded')
@ -283,7 +295,22 @@ def make_deferred(con_mesh, rpasses):
frag.write('#endif') frag.write('#endif')
if '_SSRefraction' in wrd.world_defs or '_VoxelRefract' in wrd.world_defs: 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 '_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 return con_mesh
@ -388,6 +415,9 @@ def make_forward_mobile(con_mesh):
frag.add_include('std/shadows.glsl') frag.add_include('std/shadows.glsl')
frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True) frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True)
frag.add_uniform('vec3 eye', '_cameraPosition') 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);') frag.write(f'svisibility = shadowTestCascade({shadowmap_sun}, eye, wposition + n * shadowsBias * 10, shadowsBias, opacity != 1.0);')
else: else:
frag.write('if (lightPosition.w > 0.0) {') frag.write('if (lightPosition.w > 0.0) {')
@ -567,7 +597,7 @@ def make_forward(con_mesh):
frag.write('fragColor[0] = vec4(direct + indirect, packFloat2(occlusion, specular));') frag.write('fragColor[0] = vec4(direct + indirect, packFloat2(occlusion, specular));')
frag.write('fragColor[1] = vec4(n.xy, roughness, metallic);') frag.write('fragColor[1] = vec4(n.xy, roughness, metallic);')
if rpdat.rp_ss_refraction or rpdat.lnx_voxelgi_refract: 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: else:
frag.add_out('vec4 fragColor[1]') frag.add_out('vec4 fragColor[1]')
@ -668,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 albedo = surfaceAlbedo(basecol, metallic);')
frag.write('vec3 f0 = surfaceF0(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: if '_Brdf' in wrd.world_defs:
frag.add_uniform('sampler2D senvmapBrdf', link='$brdf.png') frag.add_uniform('sampler2D senvmapBrdf', link='$brdf.png')
@ -737,6 +769,18 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
frag.write('}') frag.write('}')
frag.write('vec3 direct = vec3(0.0);') 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: if '_Sun' in wrd.world_defs:
frag.add_uniform('vec3 sunCol', '_sunColor') frag.add_uniform('vec3 sunCol', '_sunColor')
frag.add_uniform('vec3 sunDir', '_sunDirection') frag.add_uniform('vec3 sunDir', '_sunDirection')
@ -756,6 +800,9 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
frag.add_include('std/shadows.glsl') frag.add_include('std/shadows.glsl')
frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True) frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True)
frag.add_uniform('vec3 eye', '_cameraPosition') 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},') frag.write(f'svisibility = shadowTestCascade({shadowmap_sun},')
if is_transparent_shadows: if is_transparent_shadows:
frag.write(f'{shadowmap_sun_tr},') frag.write(f'{shadowmap_sun_tr},')
@ -789,6 +836,20 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
if '_VoxelShadow' in wrd.world_defs: 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('svisibility *= (1.0 - traceShadow(wposition, n, voxels, voxelsSDF, sunDir, clipmaps, gl_FragCoord.xy, velocity).r) * voxelgiShad;')
frag.write('}') # receiveShadow frag.write('}') # receiveShadow
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;') frag.write('direct += (lambertDiffuseBRDF(albedo, sdotNL) + specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * specular) * sunCol * svisibility;')
# sun # sun
@ -830,6 +891,16 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
frag.add_uniform('mat4 invVP', '_inverseViewProjectionMatrix') frag.add_uniform('mat4 invVP', '_inverseViewProjectionMatrix')
frag.add_uniform('vec3 eye', '_cameraPosition') frag.add_uniform('vec3 eye', '_cameraPosition')
frag.write(', gbufferD, invVP, eye') frag.write(', gbufferD, invVP, eye')
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 '_Anisotropy' in wrd.world_defs:
frag.write(', anisotropy, anisoRot, tangent')
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(');') frag.write(');')
if '_Clusters' in wrd.world_defs: if '_Clusters' in wrd.world_defs:
@ -842,7 +913,7 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
if '_VoxelRefract' in wrd.world_defs and parse_opacity: if '_VoxelRefract' in wrd.world_defs and parse_opacity:
frag.write('if (opacity < 1.0) {') 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(' indirect = mix(refraction, indirect, opacity);')
frag.write(' direct = mix(refraction, direct, opacity);') frag.write(' direct = mix(refraction, direct, opacity);')
frag.write('}') frag.write('}')
@ -856,6 +927,28 @@ def _write_material_attribs_default(frag: shader.Shader, parse_opacity: bool):
# We may not use emission, but the attribute will then be removed # We may not use emission, but the attribute will then be removed
# by the shader compiler # by the shader compiler
frag.write('vec3 emissionCol;') frag.write('vec3 emissionCol;')
# Extended BRDF parameters
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: if parse_opacity:
frag.write('float opacity;') frag.write('float opacity;')
frag.write('float ior = 1.45;') else:
frag.write('float opacity = 1.0;')

View File

@ -40,34 +40,29 @@ def make(context_id):
# Remove fragColor = ...; # Remove fragColor = ...;
frag.main = frag.main[:frag.main.rfind('fragColor')] frag.main = frag.main[:frag.main.rfind('fragColor')]
frag.write('\n') frag.write('\n')
frag.write('if (opacity <= 0.0) discard;')
wrd = bpy.data.worlds['Lnx']
frag.write('n /= (abs(n.x) + abs(n.y) + abs(n.z));') 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);') frag.write('n.xy = n.z >= 0.0 ? n.xy : octahedronWrap(n.xy);')
is_shadeless = mat_state.emission_type == mat_state.EmissionType.SHADELESS 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:
frag.write('uint matid = 0;')
if is_shadeless: if is_shadeless:
frag.write('uint matid = 0;')
frag.write('matid = 1;') frag.write('matid = 1;')
frag.write('basecol = emissionCol;') 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;')
else: else:
frag.write('const uint matid = 0;') frag.write('const uint matid = 0;')
if rpdat.rp_renderer == 'Deferred': if rpdat.rp_renderer == 'Deferred':
frag.write('fragColor[0] = vec4(n.xy, roughness, 1.0);') frag.write('fragColor[0] = vec4(n.xy, roughness, 1.0);')
frag.write('vec3 finalColor = direct + indirect;') frag.write('vec3 finalColor = direct + indirect;')
frag.write('fragColor[1] = vec4(finalColor * opacity, opacity);') frag.write('fragColor[1] = vec4(finalColor * opacity, 1.0);')
else: else:
frag.write('vec3 finalColor = direct + indirect;') 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[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);') # frag.write('fragColor[2] = vec4(ior, 1.0 - opacity, packFloat2(basecol.r, basecol.g), basecol.b);')
make_finalize.make(con_refract) make_finalize.make(con_refract)

View File

@ -57,9 +57,6 @@ def build(material: Material, mat_users: Dict[Material, List[Object]], mat_lnxus
# Place empty material output to keep compiler happy.. # Place empty material output to keep compiler happy..
mat_state.output_node = mat_state.nodes.new('ShaderNodeOutputMaterial') mat_state.output_node = mat_state.nodes.new('ShaderNodeOutputMaterial')
# reset for each material
mat_state.needs_sss = False
wrd = bpy.data.worlds['Lnx'] wrd = bpy.data.worlds['Lnx']
rpdat = lnx.utils.get_rp() rpdat = lnx.utils.get_rp()
rpasses = mat_utils.get_rpasses(material) 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' shader_data_path = lnx.utils.get_fp_build() + '/compiled/Shaders/' + shader_data_name + '.lnx'
assets.add_shader_data(shader_data_path) assets.add_shader_data(shader_data_path)
# Store SSS state in the return tuple so it's preserved per-material return rpasses, mat_state.data, shader_data_name, bind_constants, bind_textures
needs_sss_result = mat_state.needs_sss
return rpasses, mat_state.data, shader_data_name, bind_constants, bind_textures, needs_sss_result
def write_shaders(rel_path: str, con: ShaderContext, rpass: str, matname: str) -> None: def write_shaders(rel_path: str, con: ShaderContext, rpass: str, matname: str) -> None:

View File

@ -69,23 +69,44 @@ def make_gi(context_id):
frag.add_include('std/gbuffer.glsl') frag.add_include('std/gbuffer.glsl')
frag.add_include('std/brdf.glsl') frag.add_include('std/brdf.glsl')
frag.add_include('std/aabb.glsl') frag.add_include('std/aabb.glsl')
frag.write_header('#define _VoxelPass')
rpdat = lnx.utils.get_rp() rpdat = lnx.utils.get_rp()
frag.add_uniform('layout(r32ui) uimage3D voxels') frag.add_uniform('layout(r32ui) uimage3D voxels')
frag.write('vec3 n;') frag.write('vec3 n;')
frag.write('vec3 nCoat = vec3(0.0, 0.0, 0.0);')
frag.write('vec3 wposition;') frag.write('vec3 wposition;')
frag.write('vec3 basecol;') frag.write('vec3 basecol;')
frag.write('float roughness;') # frag.write('float roughness;') #
frag.write('float metallic;') # frag.write('float metallic;') #
frag.write('float occlusion;') # frag.write('float occlusion;') #
frag.write('float specular;') # frag.write('float specular;') #
frag.write('vec3 emissionCol = vec3(0.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 blend = mat_state.material.lnx_blending
parse_opacity = blend or mat_utils.is_transluc(mat_state.material) parse_opacity = blend or mat_utils.is_transluc(mat_state.material)
frag.write('float ior = 1.45;')
if parse_opacity: if parse_opacity:
frag.write('float opacity;') frag.write('float opacity;')
frag.write('float ior;')
else: else:
frag.write('float opacity = 1.0;') frag.write('float opacity = 1.0;')
@ -252,10 +273,6 @@ def make_gi(context_id):
frag.write_attrib('vec3 vVec = normalize(eyeDir);') frag.write_attrib('vec3 vVec = normalize(eyeDir);')
frag.write_attrib('float dotNV = max(dot(N, vVec), 0.0);') 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: if '_Irr' in wrd.world_defs:
frag.add_include('std/shirr.glsl') frag.add_include('std/shirr.glsl')
frag.add_uniform('vec4 shirr[7]', link='_envmapIrradiance') frag.add_uniform('vec4 shirr[7]', link='_envmapIrradiance')
@ -265,31 +282,12 @@ def make_gi(context_id):
else: else:
frag.write('vec3 envl = vec3(0.0);') 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: if '_EnvLDR' in wrd.world_defs:
frag.write('envl = pow(envl, vec3(2.2));') 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.add_uniform('float envmapStrength', link='_envmapStrength')
frag.write('envl *= envmapStrength * occlusion;') frag.write('envl *= envmapStrength * voxelgiEnv * occlusion;')
frag.add_include('std/light.glsl') frag.add_include('std/light.glsl')
is_shadows = '_ShadowMap' in wrd.world_defs is_shadows = '_ShadowMap' in wrd.world_defs
@ -324,6 +322,9 @@ def make_gi(context_id):
frag.add_include('std/shadows.glsl') frag.add_include('std/shadows.glsl')
frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True) frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True)
frag.add_uniform('vec3 eye', '_cameraPosition') 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},') frag.write(f'svisibility = shadowTestCascade({shadowmap_sun},')
if is_transparent_shadows: if is_transparent_shadows:
frag.write(f'{shadowmap_sun_tr},') frag.write(f'{shadowmap_sun_tr},')
@ -345,7 +346,7 @@ def make_gi(context_id):
frag.write(', false') frag.write(', false')
frag.write(');') frag.write(');')
frag.write('}') # receiveShadow 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 # sun
if '_SinglePoint' in wrd.world_defs: if '_SinglePoint' in wrd.world_defs:
@ -377,6 +378,16 @@ def make_gi(context_id):
frag.write(', opacity != 1.0') frag.write(', opacity != 1.0')
if '_Spot' in wrd.world_defs: if '_Spot' in wrd.world_defs:
frag.write(', true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight') frag.write(', true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight')
if '_ClearCoat' in wrd.world_defs:
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 '_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(');') frag.write(');')
if '_Clusters' in wrd.world_defs: if '_Clusters' in wrd.world_defs:
@ -433,6 +444,8 @@ def make_gi(context_id):
if is_transparent_shadows: if is_transparent_shadows:
frag.add_uniform('sampler2D shadowMapSpotTransparent[4]', included=True) frag.add_uniform('sampler2D shadowMapSpotTransparent[4]', included=True)
frag.add_uniform('mat4 LWVPSpotArray[maxLightsCluster]', link='_biasLightWorldViewProjectionMatrixSpotArray', 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('for (int i = 0; i < min(numLights, maxLightsCluster); i++) {')
frag.write('int li = int(texelFetch(clustersData, ivec2(clusterI, i + 1), 0).r * 255);') frag.write('int li = int(texelFetch(clustersData, ivec2(clusterI, i + 1), 0).r * 255);')
@ -458,6 +471,16 @@ def make_gi(context_id):
frag.write('\t, lightsArraySpot[li * 2].xyz') # spotDir frag.write('\t, lightsArraySpot[li * 2].xyz') # spotDir
frag.write('\t, vec2(lightsArray[li * 3].w, lightsArray[li * 3 + 1].w)') # scale frag.write('\t, vec2(lightsArray[li * 3].w, lightsArray[li * 3 + 1].w)') # scale
frag.write('\t, lightsArraySpot[li * 2 + 1].xyz') # right frag.write('\t, lightsArraySpot[li * 2 + 1].xyz') # right
if '_ClearCoat' in wrd.world_defs:
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 '_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(' );') frag.write(' );')
frag.write('}') frag.write('}')

View File

@ -38,4 +38,5 @@ texture_grad = False # Sample textures using textureGrad()
con_mesh = None # Mesh context con_mesh = None # Mesh context
uses_instancing = False # Whether the current material has at least one user with instancing enabled uses_instancing = False # Whether the current material has at least one user with instancing enabled
emission_type = EmissionType.NO_EMISSION 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

View File

@ -165,6 +165,10 @@ ALL_NODES: Dict[str, MaterialNodeMeta] = {
'TRANSPARENT_BSDF': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdftransparent), 'TRANSPARENT_BSDF': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdftransparent),
'BSDF_REFRACTION': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfrefraction), 'BSDF_REFRACTION': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfrefraction),
'REFRACTION_BSDF': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfrefraction), 'REFRACTION_BSDF': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfrefraction),
'BSDF_TOON': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdftoon),
'TOON_BSDF': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdftoon),
'BSDF_HAIR': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfhair),
'HAIR_BSDF': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfhair),
'EMISSION': MaterialNodeMeta(parse_func=nodes_shader.parse_emission), 'EMISSION': MaterialNodeMeta(parse_func=nodes_shader.parse_emission),
'HOLDOUT': MaterialNodeMeta( 'HOLDOUT': MaterialNodeMeta(
parse_func=nodes_shader.parse_holdout, parse_func=nodes_shader.parse_holdout,
@ -211,6 +215,8 @@ if bpy.app.version < (4, 1, 0):
ALL_NODES['TEX_MUSGRAVE'] = MaterialNodeMeta(parse_func=nodes_texture.parse_tex_musgrave) ALL_NODES['TEX_MUSGRAVE'] = MaterialNodeMeta(parse_func=nodes_texture.parse_tex_musgrave)
if bpy.app.version >= (4, 0, 0): if bpy.app.version >= (4, 0, 0):
ALL_NODES['BSDF_SHEEN'] = MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfsheen) ALL_NODES['BSDF_SHEEN'] = MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfsheen)
if bpy.app.version >= (2, 83, 0):
ALL_NODES['BSDF_HAIR_PRINCIPLED'] = MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfhairprincipled)
if bpy.app.version < (5, 0, 0): if bpy.app.version < (5, 0, 0):
ALL_NODES['TEX_POINTDENSITY'] = MaterialNodeMeta(parse_func=nodes_texture.parse_tex_pointdensity, compute_dxdy_variants=ComputeDXDYVariant.NEVER) ALL_NODES['TEX_POINTDENSITY'] = MaterialNodeMeta(parse_func=nodes_texture.parse_tex_pointdensity, compute_dxdy_variants=ComputeDXDYVariant.NEVER)

View File

@ -99,6 +99,26 @@ class ParserState:
self.out_opacity: floatstr = '1.0' self.out_opacity: floatstr = '1.0'
self.out_ior: floatstr = '1.450' self.out_ior: floatstr = '1.450'
self.out_emission_col: vec3str = 'vec3(0.0)' self.out_emission_col: vec3str = 'vec3(0.0)'
self.out_subsurface: floatstr = '0.0'
self.out_subsurface_radius: vec3str = 'vec3(0.0)'
self.out_subsurface_color: vec3str = 'vec3(0.8)'
self.out_specular_tint: vec3str = 'vec3(1.0)'
self.out_anisotropy: floatstr = '0.0'
self.out_aniso_rot: floatstr = '0.0'
self.out_sheen: floatstr = '0.0'
self.out_sheen_rough: floatstr = '0.5'
self.out_sheen_tint: vec3str = 'vec3(1.0)'
self.out_clearcoat: floatstr = '0.0'
self.out_clearcoat_rough: floatstr = '0.03'
self.out_transmission: floatstr = '0.0'
self.out_transmission_rough: floatstr = '0.0'
self.out_thin_wall: floatstr = '0.0'
self.out_tangent: vec3str = 'vec3(0.0)'
self.out_subsurface_scale: floatstr = '0.05'
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): def reset_outs(self):
"""Reset the shader output values to their default values.""" """Reset the shader output values to their default values."""
@ -110,11 +130,41 @@ class ParserState:
self.out_opacity = '1.0' self.out_opacity = '1.0'
self.out_ior = '1.450' self.out_ior = '1.450'
self.out_emission_col = 'vec3(0.0)' self.out_emission_col = 'vec3(0.0)'
self.out_subsurface = '0.0'
self.out_subsurface_radius = 'vec3(0.0)'
self.out_subsurface_color = 'vec3(0.8)'
self.out_specular_tint = 'vec3(1.0)'
self.out_anisotropy = '0.0'
self.out_aniso_rot = '0.0'
self.out_sheen = '0.0'
self.out_sheen_rough = '0.5'
self.out_sheen_tint = 'vec3(1.0)'
self.out_clearcoat = '0.0'
self.out_clearcoat_rough = '0.03'
self.out_transmission = '0.0'
self.out_transmission_rough = '0.0'
self.out_thin_wall = '0.0'
self.out_tangent = 'vec3(0.0)'
self.out_subsurface_scale = '0.05'
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[vec3str, floatstr, floatstr, floatstr, floatstr, floatstr, floatstr, vec3str]: def get_outs(self) -> Tuple:
"""Return the shader output values as a tuple.""" """Return the shader output values as a tuple."""
return (self.out_basecol, self.out_roughness, self.out_metallic, self.out_occlusion, self.out_specular, return (self.out_basecol, self.out_roughness, self.out_metallic,
self.out_opacity, self.out_ior, self.out_emission_col) self.out_occlusion, self.out_specular, self.out_opacity,
self.out_ior, self.out_emission_col,
self.out_subsurface, self.out_subsurface_radius,
self.out_subsurface_color, self.out_specular_tint,
self.out_anisotropy, self.out_aniso_rot,
self.out_sheen, self.out_sheen_rough, self.out_sheen_tint,
self.out_clearcoat, self.out_clearcoat_rough,
self.out_transmission, self.out_transmission_rough,
self.out_thin_wall, self.out_tangent,
self.out_subsurface_scale, self.out_subsurface_anisotropy,
self.out_coat_ior, self.out_coat_tint)
def get_parser_pass_suffix(self) -> str: def get_parser_pass_suffix(self) -> str:

View File

@ -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') name="MSAA", description="Samples per pixel usable for render paths drawing directly to framebuffer", default='1')
lnx_voxelgi_cones: EnumProperty( lnx_voxelgi_cones: EnumProperty(
items=[('9', '9', '9'), items=[('16', '16', '16'),
('9', '9', '9'),
('5', '5', '5'), ('5', '5', '5'),
('3', '3', '3'), ('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_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_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) 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_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_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_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_voxelgi_aperture: FloatProperty(name="Aperture", description="Cone aperture for shadow trace", default=0.26, update=assets.invalidate_shader_cache)
lnx_sss_width: FloatProperty(name="Width", description="SSS blur strength", default=1.0, 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_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_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) lnx_water_displace: FloatProperty(name="Displace", default=1.0, update=assets.invalidate_shader_cache)

View File

@ -1646,9 +1646,6 @@ class LNX_PT_RenderPathRendererPanel(bpy.types.Panel):
layout.prop(rpdat, 'lnx_samples_per_pixel') layout.prop(rpdat, 'lnx_samples_per_pixel')
layout.prop(rpdat, 'lnx_texture_filter') layout.prop(rpdat, 'lnx_texture_filter')
layout.prop(rpdat, 'rp_sss_state') 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') layout.prop(rpdat, 'lnx_rp_displacement')
if rpdat.lnx_rp_displacement == 'Tessellation': if rpdat.lnx_rp_displacement == 'Tessellation':
layout.label(text='Mesh') layout.label(text='Mesh')
@ -1890,7 +1887,7 @@ class LNX_PT_RenderPathVoxelsPanel(bpy.types.Panel):
col.prop(rpdat, 'lnx_voxelgi_shadows', text='Shadows') col.prop(rpdat, 'lnx_voxelgi_shadows', text='Shadows')
col2.prop(rpdat, 'lnx_voxelgi_refract', text='Refraction') col2.prop(rpdat, 'lnx_voxelgi_refract', text='Refraction')
#col.prop(rpdat, 'lnx_voxelgi_clipmap_count') #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, 'rp_voxelgi_resolution')
col.prop(rpdat, 'lnx_voxelgi_size') col.prop(rpdat, 'lnx_voxelgi_size')
#col.prop(rpdat, 'rp_voxelgi_resolution_z') #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_env')
col.prop(rpdat, 'lnx_voxelgi_occ') col.prop(rpdat, 'lnx_voxelgi_occ')
col.label(text="Ray") 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_step')
col.prop(rpdat, 'lnx_voxelgi_range') 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): class LNX_PT_RenderPathWorldPanel(bpy.types.Panel):
bl_label = "World" bl_label = "World"

View File

@ -665,7 +665,8 @@ class Main {
if rpdat.rp_voxels == 'Voxel GI' or rpdat.rp_voxels == 'Voxel AO': if rpdat.rp_voxels == 'Voxel GI' or rpdat.rp_voxels == 'Voxel AO':
f.write(""" f.write("""
public static inline var voxelgiClipmapCount = """ + str(rpdat.lnx_voxelgi_clipmap_count) + """; 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: if rpdat.rp_bloom:
follow_blender = rpdat.lnx_bloom_follow_blender if bpy.app.version < (4, 3, 0) else False follow_blender = rpdat.lnx_bloom_follow_blender if bpy.app.version < (4, 3, 0) else False
@ -834,6 +835,18 @@ def write_compiledglsl(defs, make_variants):
if '_SSRefraction' in wrd.world_defs or '_VoxelRefract' in wrd.world_defs: if '_SSRefraction' in wrd.world_defs or '_VoxelRefract' in wrd.world_defs:
f.write(f'#define GBUF_IDX_REFRACTION {idx_refraction}\n') f.write(f'#define GBUF_IDX_REFRACTION {idx_refraction}\n')
idx_refraction += 1
if '_ClearCoat' in wrd.world_defs:
f.write(f'#define GBUF_IDX_COAT_NORMAL {idx_refraction}\n')
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')
f.write('#endif\n')
f.write('#define MAX_MATERIALS 16\n')
f.write('uniform vec4 materialParams[MAX_MATERIALS * 8];\n')
f.write("""#if defined(HLSL) || defined(METAL) f.write("""#if defined(HLSL) || defined(METAL)
#define _InvY #define _InvY
@ -843,8 +856,12 @@ def write_compiledglsl(defs, make_variants):
if state.target == 'html5' or lnx.utils.get_gapi() == 'direct3d11': if state.target == 'html5' or lnx.utils.get_gapi() == 'direct3d11':
f.write("#define _FlipY\n") f.write("#define _FlipY\n")
f.write("""const float PI = 3.1415926535; f.write("""#ifndef PI
const float PI2 = PI * 2.0; #define PI 3.1415926535
#endif
#ifndef PI2
#define PI2 6.2831853071
#endif
const vec2 shadowmapSize = vec2(""" + str(shadowmap_size) + """, """ + str(shadowmap_size) + """); const vec2 shadowmapSize = vec2(""" + str(shadowmap_size) + """, """ + str(shadowmap_size) + """);
const float shadowmapCubePcfSize = """ + str((round(rpdat.lnx_pcfsize * 100) / 100) / 1000) + """; const float shadowmapCubePcfSize = """ + str((round(rpdat.lnx_pcfsize * 100) / 100) / 1000) + """;
const int shadowmapCascades = """ + str(rpdat.rp_shadowmap_cascades) + """; const int shadowmapCascades = """ + str(rpdat.rp_shadowmap_cascades) + """;
@ -1015,6 +1032,7 @@ const float compoDOFLength = """ + str(round(lens * 100) / 100) +""";
if rpdat.rp_voxels != 'Off': 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) + """); 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 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 voxelgiOcc = """ + str(round(rpdat.lnx_voxelgi_occ * 100) / 100) + """;
const float voxelgiVoxelSize = """ + str(round(rpdat.lnx_voxelgi_size * 1000) / 1000) + """; const float voxelgiVoxelSize = """ + str(round(rpdat.lnx_voxelgi_size * 1000) / 1000) + """;
const float voxelgiStep = """ + str(round(rpdat.lnx_voxelgi_step * 1000) / 1000) + """; const float voxelgiStep = """ + str(round(rpdat.lnx_voxelgi_step * 1000) / 1000) + """;
@ -1030,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 voxelgiRefl = """ + str(round(rpdat.lnx_voxelgi_spec * 100) / 100) + """;
const float voxelgiRefr = """ + str(round(rpdat.lnx_voxelgi_refr * 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 # Skinning
if rpdat.lnx_skin == 'On': if rpdat.lnx_skin == 'On':
f.write( f.write(