Merge pull request #2102 from N8n5h/light-fix-2

Add support for shadow map atlasing
This commit is contained in:
Lubos Lenco 2021-02-14 16:32:59 +01:00 committed by GitHub
commit 49a599dc66
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 1447 additions and 193 deletions

View file

@ -2,7 +2,6 @@
#include "compiled.inc"
#include "std/gbuffer.glsl"
#include "std/light.glsl"
#ifdef _Clusters
#include "std/clusters.glsl"
#endif
@ -80,14 +79,11 @@ uniform mat4 invVP;
#ifdef _ShadowMap
#ifdef _SinglePoint
//!uniform sampler2DShadow shadowMapSpot[1];
//!uniform mat4 LWVPSpot0;
//!uniform mat4 LWVPSpot[1];
#endif
#ifdef _Clusters
//!uniform sampler2DShadow shadowMapSpot[4];
//!uniform mat4 LWVPSpot0;
//!uniform mat4 LWVPSpot1;
//!uniform mat4 LWVPSpot2;
//!uniform mat4 LWVPSpot3;
//!uniform mat4 LWVPSpotArray[4];
#endif
#endif
#endif
@ -109,21 +105,36 @@ uniform vec2 cameraPlane;
#ifdef _SinglePoint
#ifdef _Spot
//!uniform sampler2DShadow shadowMapSpot[1];
//!uniform mat4 LWVPSpot0;
//!uniform mat4 LWVPSpot[1];
#else
//!uniform samplerCubeShadow shadowMapPoint[1];
//!uniform vec2 lightProj;
#endif
#endif
#ifdef _Clusters
//!uniform samplerCubeShadow shadowMapPoint[4];
#ifdef _ShadowMapAtlas
#ifdef _SingleAtlas
uniform sampler2DShadow shadowMapAtlas;
#endif
#endif
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
//!uniform sampler2DShadow shadowMapAtlasPoint;
#endif
//!uniform vec4 pointLightDataArray[4];
#else
//!uniform samplerCubeShadow shadowMapPoint[4];
#endif
//!uniform vec2 lightProj;
#ifdef _Spot
//!uniform sampler2DShadow shadowMapSpot[4];
//!uniform mat4 LWVPSpot0;
//!uniform mat4 LWVPSpot1;
//!uniform mat4 LWVPSpot2;
//!uniform mat4 LWVPSpot3;
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
//!uniform sampler2DShadow shadowMapAtlasSpot;
#endif
#else
//!uniform sampler2DShadow shadowMapSpot[4];
#endif
//!uniform mat4 LWVPSpotArray[4];
#endif
#endif
#endif
@ -132,7 +143,13 @@ uniform vec2 cameraPlane;
uniform vec3 sunDir;
uniform vec3 sunCol;
#ifdef _ShadowMap
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
uniform sampler2DShadow shadowMapAtlasSun;
#endif
#else
uniform sampler2DShadow shadowMap;
#endif
uniform float shadowsBias;
#ifdef _CSM
//!uniform vec4 casData[shadowmapCascades * 4 + 4];
@ -159,6 +176,8 @@ uniform sampler2D texClouds;
uniform float time;
#endif
#include "std/light.glsl"
in vec2 texCoord;
in vec3 viewRay;
out vec4 fragColor;
@ -289,10 +308,32 @@ void main() {
#ifdef _ShadowMap
#ifdef _CSM
svisibility = shadowTestCascade(shadowMap, eye, p + n * shadowsBias * 10, shadowsBias);
svisibility = shadowTestCascade(
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
shadowMapAtlasSun
#else
shadowMapAtlas
#endif
#else
shadowMap
#endif
, eye, p + n * shadowsBias * 10, shadowsBias
);
#else
vec4 lPos = LWVP * vec4(p + n * shadowsBias * 100, 1.0);
if (lPos.w > 0.0) svisibility = shadowTest(shadowMap, lPos.xyz / lPos.w, shadowsBias);
vec4 lPos = LWVP * vec4(p + n * shadowsBias * 100, 1.0);
if (lPos.w > 0.0) svisibility = shadowTest(
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
shadowMapAtlasSun
#else
shadowMapAtlas
#endif
#else
shadowMap
#endif
, lPos.xyz / lPos.w, shadowsBias
);
#endif
#endif
@ -335,7 +376,18 @@ void main() {
int casi, casindex;
mat4 LWVP = getCascadeMat(distance(eye, p), casi, casindex);
#endif
fragColor.rgb += fragColor.rgb * SSSSTransmittance(LWVP, p, n, sunDir, lightPlane.y, shadowMap);
fragColor.rgb += fragColor.rgb * SSSSTransmittance(
LWVP, p, n, sunDir, lightPlane.y,
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
shadowMapAtlasSun
#else
shadowMapAtlas
#endif
#else
shadowMap
#endif
);
}
#endif

View file

@ -199,43 +199,37 @@
"ifdef": ["_SinglePoint", "_Spot"]
},
{
"name": "LWVPSpot0",
"name": "LWVPSpotArray",
"link": "_biasLightWorldViewProjectionMatrixSpotArray",
"ifdef": ["_Clusters", "_ShadowMap", "_Spot"]
},
{
"name": "pointLightDataArray",
"link": "_pointLightsAtlasArray",
"ifdef": ["_Clusters", "_ShadowMap", "_ShadowMapAtlas"]
},
{
"name": "LWVPSpot[0]",
"link": "_biasLightWorldViewProjectionMatrixSpot0",
"ifdef": ["_Spot", "_ShadowMap"]
"ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_LTC", "_ShadowMap"]
},
{
"name": "LWVPSpot1",
"name": "LWVPSpot[1]",
"link": "_biasLightWorldViewProjectionMatrixSpot1",
"ifdef": ["_Spot", "_ShadowMap"]
"ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_LTC", "_ShadowMap"]
},
{
"name": "LWVPSpot2",
"name": "LWVPSpot[2]",
"link": "_biasLightWorldViewProjectionMatrixSpot2",
"ifdef": ["_Spot", "_ShadowMap"]
"ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_LTC", "_ShadowMap"]
},
{
"name": "LWVPSpot3",
"link": "_biasLightWorldViewProjectionMatrixSpot3",
"ifdef": ["_Spot", "_ShadowMap"]
},
{
"name": "LWVPSpot0",
"link": "_biasLightWorldViewProjectionMatrixSpot0",
"ifdef": ["_LTC", "_ShadowMap"]
},
{
"name": "LWVPSpot1",
"link": "_biasLightWorldViewProjectionMatrixSpot1",
"ifdef": ["_LTC", "_ShadowMap"]
},
{
"name": "LWVPSpot2",
"link": "_biasLightWorldViewProjectionMatrixSpot2",
"ifdef": ["_LTC", "_ShadowMap"]
},
{
"name": "LWVPSpot3",
"name": "LWVPSpot[3]",
"link": "_biasLightWorldViewProjectionMatrixSpot3",
"ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_LTC", "_ShadowMap"]
}
],

View file

@ -3,7 +3,6 @@
#include "compiled.inc"
#include "std/gbuffer.glsl"
#include "std/math.glsl"
#include "std/light_mobile.glsl"
#ifdef _Clusters
#include "std/clusters.glsl"
#endif
@ -47,21 +46,36 @@ uniform vec2 cameraPlane;
#ifdef _SinglePoint
#ifdef _Spot
//!uniform sampler2DShadow shadowMapSpot[1];
//!uniform mat4 LWVPSpot0;
//!uniform mat4 LWVPSpot[1];
#else
//!uniform samplerCubeShadow shadowMapPoint[1];
//!uniform vec2 lightProj;
#endif
#endif
#ifdef _Clusters
//!uniform samplerCubeShadow shadowMapPoint[4];
#ifdef _ShadowMapAtlas
#ifdef _SingleAtlas
uniform sampler2DShadow shadowMapAtlas;
#endif
#endif
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
//!uniform sampler2DShadow shadowMapAtlasPoint;
#endif
//!uniform vec4 pointLightDataArray[4];
#else
//!uniform samplerCubeShadow shadowMapPoint[4];
#endif
//!uniform vec2 lightProj;
#ifdef _Spot
//!uniform sampler2DShadow shadowMapSpot[4];
//!uniform mat4 LWVPSpot0;
//!uniform mat4 LWVPSpot1;
//!uniform mat4 LWVPSpot2;
//!uniform mat4 LWVPSpot3;
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
//!uniform sampler2DShadow shadowMapAtlasSpot;
#endif
#else
//!uniform sampler2DShadow shadowMapSpot[4];
#endif
//!uniform mat4 LWVPSpotArray[4];
#endif
#endif
#endif
@ -70,7 +84,13 @@ uniform vec2 cameraPlane;
uniform vec3 sunDir;
uniform vec3 sunCol;
#ifdef _ShadowMap
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
uniform sampler2DShadow shadowMapAtlasSun;
#endif
#else
uniform sampler2DShadow shadowMap;
#endif
uniform float shadowsBias;
#ifdef _CSM
//!uniform vec4 casData[shadowmapCascades * 4 + 4];
@ -90,6 +110,8 @@ uniform float pointBias;
#endif
#endif
#include "std/light_mobile.glsl"
in vec2 texCoord;
in vec3 viewRay;
out vec4 fragColor;
@ -168,10 +190,32 @@ void main() {
#ifdef _ShadowMap
#ifdef _CSM
svisibility = shadowTestCascade(shadowMap, eye, p + n * shadowsBias * 10, shadowsBias, shadowmapSize * vec2(shadowmapCascades, 1.0));
svisibility = shadowTestCascade(
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
shadowMapAtlasSun
#else
shadowMapAtlas
#endif
#else
shadowMap
#endif
, eye, p + n * shadowsBias * 10, shadowsBias
);
#else
vec4 lPos = LWVP * vec4(p + n * shadowsBias * 100, 1.0);
if (lPos.w > 0.0) svisibility = shadowTest(shadowMap, lPos.xyz / lPos.w, shadowsBias, shadowmapSize);
vec4 lPos = LWVP * vec4(p + n * shadowsBias * 100, 1.0);
if (lPos.w > 0.0) svisibility = shadowTest(
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
shadowMapAtlasSun
#else
shadowMapAtlas
#endif
#else
shadowMap
#endif
, lPos.xyz / lPos.w, shadowsBias
);
#endif
#endif

View file

@ -138,24 +138,38 @@
"ifdef": ["_SinglePoint", "_Spot"]
},
{
"name": "LWVPSpot0",
"name": "LWVPSpotArray",
"link": "_biasLightWorldViewProjectionMatrixSpotArray",
"ifdef": ["_Clusters", "_ShadowMap", "_Spot"]
},
{
"name": "pointLightDataArray",
"link": "_pointLightsAtlasArray",
"ifdef": ["_Clusters", "_ShadowMap", "_ShadowMapAtlas"]
},
{
"name": "LWVPSpot[0]",
"link": "_biasLightWorldViewProjectionMatrixSpot0",
"ifdef": ["_Spot", "_ShadowMap"]
"ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_LTC", "_ShadowMap"]
},
{
"name": "LWVPSpot1",
"name": "LWVPSpot[1]",
"link": "_biasLightWorldViewProjectionMatrixSpot1",
"ifdef": ["_Spot", "_ShadowMap"]
"ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_LTC", "_ShadowMap"]
},
{
"name": "LWVPSpot2",
"name": "LWVPSpot[2]",
"link": "_biasLightWorldViewProjectionMatrixSpot2",
"ifdef": ["_Spot", "_ShadowMap"]
"ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_LTC", "_ShadowMap"]
},
{
"name": "LWVPSpot3",
"name": "LWVPSpot[3]",
"link": "_biasLightWorldViewProjectionMatrixSpot3",
"ifdef": ["_Spot", "_ShadowMap"]
"ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_LTC", "_ShadowMap"]
}
],
"vertex_shader": "../include/pass_viewray.vert.glsl",

View file

@ -1,7 +1,4 @@
const int maxLights = 16;
const int maxLightsCluster = 4; // Ensure fast loop unroll before going higher
const float clusterNear = 3.0;
const vec3 clusterSlices = vec3(16, 16, 16);
int getClusterI(vec2 tc, float viewz, vec2 cameraPlane) {

View file

@ -21,27 +21,41 @@
#endif
#ifdef _ShadowMap
#ifdef _SinglePoint
#ifdef _Spot
uniform sampler2DShadow shadowMapSpot[1];
uniform mat4 LWVPSpot0;
#else
uniform samplerCubeShadow shadowMapPoint[1];
uniform vec2 lightProj;
#ifdef _SinglePoint
#ifdef _Spot
#ifndef _LTC
uniform sampler2DShadow shadowMapSpot[1];
uniform mat4 LWVPSpot[1];
#endif
#else
uniform samplerCubeShadow shadowMapPoint[1];
uniform vec2 lightProj;
#endif
#endif
#endif
#ifdef _Clusters
uniform samplerCubeShadow shadowMapPoint[4];
uniform vec2 lightProj;
#ifdef _Spot
uniform sampler2DShadow shadowMapSpot[4];
uniform mat4 LWVPSpot0;
uniform mat4 LWVPSpot1;
uniform mat4 LWVPSpot2;
uniform mat4 LWVPSpot3;
#ifdef _Clusters
#ifdef _SingleAtlas
//!uniform sampler2DShadow shadowMapAtlas;
#endif
uniform vec2 lightProj;
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
uniform sampler2DShadow shadowMapAtlasPoint;
#endif
#else
uniform samplerCubeShadow shadowMapPoint[4];
#endif
#ifdef _Spot
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
uniform sampler2DShadow shadowMapAtlasSpot;
#endif
#else
uniform sampler2DShadow shadowMapSpot[maxLightsCluster];
#endif
uniform mat4 LWVPSpotArray[maxLightsCluster];
#endif
#endif
#endif
#endif
#ifdef _LTC
uniform vec3 lightArea0;
@ -51,17 +65,14 @@ uniform vec3 lightArea3;
uniform sampler2D sltcMat;
uniform sampler2D sltcMag;
#ifdef _ShadowMap
#ifndef _Spot
#ifndef _Spot
#ifdef _SinglePoint
uniform sampler2DShadow shadowMapSpot[1];
uniform mat4 LWVPSpot0;
uniform sampler2DShadow shadowMapSpot[1];
uniform mat4 LWVPSpot[1];
#endif
#ifdef _Clusters
uniform sampler2DShadow shadowMapSpot[4];
uniform mat4 LWVPSpot0;
uniform mat4 LWVPSpot1;
uniform mat4 LWVPSpot2;
uniform mat4 LWVPSpot3;
uniform sampler2DShadow shadowMapSpot[maxLightsCluster];
uniform mat4 LWVPSpotArray[maxLightsCluster];
#endif
#endif
#endif
@ -132,24 +143,24 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
#ifdef _ShadowMap
if (receiveShadow) {
#ifdef _SinglePoint
vec4 lPos = LWVPSpot0 * vec4(p + n * bias * 10, 1.0);
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0);
direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
#endif
#ifdef _Clusters
if (index == 0) {
vec4 lPos = LWVPSpot0 * vec4(p + n * bias * 10, 1.0);
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0);
direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
}
else if (index == 1) {
vec4 lPos = LWVPSpot1 * vec4(p + n * bias * 10, 1.0);
vec4 lPos = LWVPSpot[1] * vec4(p + n * bias * 10, 1.0);
direct *= shadowTest(shadowMapSpot[1], lPos.xyz / lPos.w, bias);
}
else if (index == 2) {
vec4 lPos = LWVPSpot2 * vec4(p + n * bias * 10, 1.0);
vec4 lPos = LWVPSpot[2] * vec4(p + n * bias * 10, 1.0);
direct *= shadowTest(shadowMapSpot[2], lPos.xyz / lPos.w, bias);
}
else if (index == 3) {
vec4 lPos = LWVPSpot3 * vec4(p + n * bias * 10, 1.0);
vec4 lPos = LWVPSpot[3] * vec4(p + n * bias * 10, 1.0);
direct *= shadowTest(shadowMapSpot[3], lPos.xyz / lPos.w, bias);
}
#endif
@ -168,26 +179,30 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
#ifdef _ShadowMap
if (receiveShadow) {
#ifdef _SinglePoint
vec4 lPos = LWVPSpot0 * vec4(p + n * bias * 10, 1.0);
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0);
direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
#endif
#ifdef _Clusters
if (index == 0) {
vec4 lPos = LWVPSpot0 * vec4(p + n * bias * 10, 1.0);
direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
}
else if (index == 1) {
vec4 lPos = LWVPSpot1 * vec4(p + n * bias * 10, 1.0);
direct *= shadowTest(shadowMapSpot[1], lPos.xyz / lPos.w, bias);
}
else if (index == 2) {
vec4 lPos = LWVPSpot2 * vec4(p + n * bias * 10, 1.0);
direct *= shadowTest(shadowMapSpot[2], lPos.xyz / lPos.w, bias);
}
else if (index == 3) {
vec4 lPos = LWVPSpot3 * vec4(p + n * bias * 10, 1.0);
direct *= shadowTest(shadowMapSpot[3], lPos.xyz / lPos.w, bias);
}
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0);
#ifdef _ShadowMapAtlas
vec3 uv = lPos.xyz / lPos.w;
#ifdef _InvY
uv.y = 1.0 - uv.y; // invert Y coordinates for direct3d coordinate system
#endif
direct *= shadowTest(
#ifndef _SingleAtlas
shadowMapAtlasSpot
#else
shadowMapAtlas
#endif
, uv, bias
);
#else
if (index == 0) direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
else if (index == 1) direct *= shadowTest(shadowMapSpot[1], lPos.xyz / lPos.w, bias);
else if (index == 2) direct *= shadowTest(shadowMapSpot[2], lPos.xyz / lPos.w, bias);
else if (index == 3) direct *= shadowTest(shadowMapSpot[3], lPos.xyz / lPos.w, bias);
#endif
#endif
}
#endif
@ -207,10 +222,21 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
#endif
#endif
#ifdef _Clusters
if (index == 0) direct *= PCFCube(shadowMapPoint[0], ld, -l, bias, lightProj, n);
else if (index == 1) direct *= PCFCube(shadowMapPoint[1], ld, -l, bias, lightProj, n);
else if (index == 2) direct *= PCFCube(shadowMapPoint[2], ld, -l, bias, lightProj, n);
else if (index == 3) direct *= PCFCube(shadowMapPoint[3], ld, -l, bias, lightProj, n);
#ifdef _ShadowMapAtlas
direct *= PCFFakeCube(
#ifndef _SingleAtlas
shadowMapAtlasPoint
#else
shadowMapAtlas
#endif
, ld, -l, bias, lightProj, n, index
);
#else
if (index == 0) direct *= PCFCube(shadowMapPoint[0], ld, -l, bias, lightProj, n);
else if (index == 1) direct *= PCFCube(shadowMapPoint[1], ld, -l, bias, lightProj, n);
else if (index == 2) direct *= PCFCube(shadowMapPoint[2], ld, -l, bias, lightProj, n);
else if (index == 3) direct *= PCFCube(shadowMapPoint[3], ld, -l, bias, lightProj, n);
#endif
#endif
}
#endif

View file

@ -11,21 +11,33 @@
#ifdef _SinglePoint
#ifdef _Spot
uniform sampler2DShadow shadowMapSpot[1];
uniform mat4 LWVPSpot0;
uniform mat4 LWVPSpot[1];
#else
uniform samplerCubeShadow shadowMapPoint[1];
uniform vec2 lightProj;
#endif
#endif
#ifdef _Clusters
uniform samplerCubeShadow shadowMapPoint[4];
#ifdef _SingleAtlas
//!uniform sampler2DShadow shadowMapAtlas;
#endif
uniform vec2 lightProj;
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
uniform sampler2DShadow shadowMapAtlasPoint;
#endif
#else
uniform samplerCubeShadow shadowMapPoint[4];
#endif
#ifdef _Spot
uniform sampler2DShadow shadowMapSpot[4];
uniform mat4 LWVPSpot0;
uniform mat4 LWVPSpot1;
uniform mat4 LWVPSpot2;
uniform mat4 LWVPSpot3;
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
uniform sampler2DShadow shadowMapAtlasSpot;
#endif
#else
uniform sampler2DShadow shadowMapSpot[maxLightsCluster];
#endif
uniform mat4 LWVPSpotArray[maxLightsCluster];
#endif
#endif
#endif
@ -62,26 +74,30 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
#ifdef _ShadowMap
if (receiveShadow) {
#ifdef _SinglePoint
vec4 lPos = LWVPSpot0 * vec4(p + n * bias * 10, 1.0);
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0);
direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
#endif
#ifdef _Clusters
if (index == 0) {
vec4 lPos = LWVPSpot0 * vec4(p + n * bias * 10, 1.0);
direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
}
else if (index == 1) {
vec4 lPos = LWVPSpot1 * vec4(p + n * bias * 10, 1.0);
direct *= shadowTest(shadowMapSpot[1], lPos.xyz / lPos.w, bias);
}
else if (index == 2) {
vec4 lPos = LWVPSpot2 * vec4(p + n * bias * 10, 1.0);
direct *= shadowTest(shadowMapSpot[2], lPos.xyz / lPos.w, bias);
}
else if (index == 3) {
vec4 lPos = LWVPSpot3 * vec4(p + n * bias * 10, 1.0);
direct *= shadowTest(shadowMapSpot[3], lPos.xyz / lPos.w, bias);
}
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0);
#ifdef _ShadowMapAtlas
vec3 uv = lPos.xyz / lPos.w;
#ifdef _InvY
uv.y = 1.0 - uv.y; // invert Y coordinates for direct3d coordinate system
#endif
direct *= shadowTest(
#ifndef _SingleAtlas
shadowMapAtlasSpot
#else
shadowMapAtlas
#endif
, uv, bias
);
#else
if (index == 0) direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
else if (index == 1) direct *= shadowTest(shadowMapSpot[1], lPos.xyz / lPos.w, bias);
else if (index == 2) direct *= shadowTest(shadowMapSpot[2], lPos.xyz / lPos.w, bias);
else if (index == 3) direct *= shadowTest(shadowMapSpot[3], lPos.xyz / lPos.w, bias);
#endif
#endif
}
#endif
@ -96,10 +112,21 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
direct *= PCFCube(shadowMapPoint[0], ld, -l, bias, lightProj, n);
#endif
#ifdef _Clusters
if (index == 0) direct *= PCFCube(shadowMapPoint[0], ld, -l, bias, lightProj, n);
else if (index == 1) direct *= PCFCube(shadowMapPoint[1], ld, -l, bias, lightProj, n);
else if (index == 2) direct *= PCFCube(shadowMapPoint[2], ld, -l, bias, lightProj, n);
else if (index == 3) direct *= PCFCube(shadowMapPoint[3], ld, -l, bias, lightProj, n);
#ifdef _ShadowMapAtlas
direct *= PCFFakeCube(
#ifndef _SingleAtlas
shadowMapAtlasPoint
#else
shadowMapAtlas
#endif
, ld, -l, bias, lightProj, n, index
);
#else
if (index == 0) direct *= PCFCube(shadowMapPoint[0], ld, -l, bias, lightProj, n);
else if (index == 1) direct *= PCFCube(shadowMapPoint[1], ld, -l, bias, lightProj, n);
else if (index == 2) direct *= PCFCube(shadowMapPoint[2], ld, -l, bias, lightProj, n);
else if (index == 3) direct *= PCFCube(shadowMapPoint[3], ld, -l, bias, lightProj, n);
#endif
#endif
}
#endif

View file

@ -11,6 +11,41 @@ uniform vec4 casData[shadowmapCascades * 4 + 4];
uniform vec2 smSizeUniform;
#endif
#ifdef _ShadowMap
#ifdef _Clusters
#ifdef _ShadowMapAtlas
uniform vec4 pointLightDataArray[maxLightsCluster * 6];
#endif
#endif
#endif
#ifdef _ShadowMapAtlas
// https://www.khronos.org/registry/OpenGL/specs/gl/glspec20.pdf // p:168
// https://www.gamedev.net/forums/topic/687535-implementing-a-cube-map-lookup-function/5337472/
vec2 sampleCube(vec3 dir, out int faceIndex) {
vec3 dirAbs = abs(dir);
float ma;
vec2 uv;
if(dirAbs.z >= dirAbs.x && dirAbs.z >= dirAbs.y) {
faceIndex = dir.z < 0.0 ? 5 : 4;
ma = 0.5 / dirAbs.z;
uv = vec2(dir.z < 0.0 ? -dir.x : dir.x, -dir.y);
}
else if(dirAbs.y >= dirAbs.x) {
faceIndex = dir.y < 0.0 ? 3 : 2;
ma = 0.5 / dirAbs.y;
uv = vec2(dir.x, dir.y < 0.0 ? -dir.z : dir.z);
}
else {
faceIndex = dir.x < 0.0 ? 1 : 0;
ma = 0.5 / dirAbs.x;
uv = vec2(dir.x < 0.0 ? dir.z : -dir.z, -dir.y);
}
// downscale uv a little to hide seams
return uv * 0.9976 * ma + 0.5;
}
#endif
float PCF(sampler2DShadow shadowMap, const vec2 uv, const float compare, const vec2 smSize) {
float result = texture(shadowMap, vec3(uv + (vec2(-1.0, -1.0) / smSize), compare));
result += texture(shadowMap, vec3(uv + (vec2(-1.0, 0.0) / smSize), compare));
@ -50,6 +85,185 @@ float PCFCube(samplerCubeShadow shadowMapCube, const vec3 lp, vec3 ml, const flo
return result / 9.0;
}
#ifdef _ShadowMapAtlas
// transform "out-of-bounds" coordinates to the correct face/coordinate system
vec2 transformOffsetedUV(const int faceIndex, out int newFaceIndex, vec2 uv) {
if (uv.x < 0.0) {
if (faceIndex == 0) { // X+
newFaceIndex = 4; // Z+
}
else if (faceIndex == 1) { // X-
newFaceIndex = 5; // Z-
}
else if (faceIndex == 2) { // Y+
newFaceIndex = 1; // X-
}
else if (faceIndex == 3) { // Y-
newFaceIndex = 1; // X-
}
else if (faceIndex == 4) { // Z+
newFaceIndex = 1; // X-
}
else { // Z-
newFaceIndex = 0; // X+
}
uv = vec2(1.0 + uv.x, uv.y);
}
else if (uv.x > 1.0) {
if (faceIndex == 0) { // X+
newFaceIndex = 5; // Z-
}
else if (faceIndex == 1) { // X-
newFaceIndex = 4; // Z+
}
else if (faceIndex == 2) { // Y+
newFaceIndex = 0; // X+
}
else if (faceIndex == 3) { // Y-
newFaceIndex = 0; // X+
}
else if (faceIndex == 4) { // Z+
newFaceIndex = 0; // X+
}
else { // Z-
newFaceIndex = 1; // X-
}
uv = vec2(1.0 - uv.x, uv.y);
}
else if (uv.y < 0.0) {
if (faceIndex == 0) { // X+
newFaceIndex = 2; // Y+
}
else if (faceIndex == 1) { // X-
newFaceIndex = 2; // Y+
}
else if (faceIndex == 2) { // Y+
newFaceIndex = 5; // Z-
}
else if (faceIndex == 3) { // Y-
newFaceIndex = 4; // Z+
}
else if (faceIndex == 4) { // Z+
newFaceIndex = 2; // Y+
}
else { // Z-
newFaceIndex = 2; // Y+
}
uv = vec2(uv.x, 1.0 + uv.y);
}
else if (uv.y > 1.0) {
if (faceIndex == 0) { // X+
newFaceIndex = 3; // Y-
}
else if (faceIndex == 1) { // X-
newFaceIndex = 3; // Y-
}
else if (faceIndex == 2) { // Y+
newFaceIndex = 4; // Z+
}
else if (faceIndex == 3) { // Y-
newFaceIndex = 5; // Z-
}
else if (faceIndex == 4) { // Z+
newFaceIndex = 3; // Y-
}
else { // Z-
newFaceIndex = 3; // Y-
}
uv = vec2(uv.x, 1.0 - uv.y);
} else {
newFaceIndex = faceIndex;
}
// cover corner cases too
return uv;
}
float PCFFakeCube(sampler2DShadow shadowMap, const vec3 lp, vec3 ml, const float bias, const vec2 lightProj, const vec3 n, const int index) {
const vec2 smSize = smSizeUniform; // TODO: incorrect...
const float compare = lpToDepth(lp, lightProj) - bias * 1.5;
ml = ml + n * bias * 20;
int faceIndex = 0;
const int lightIndex = index * 6;
const vec2 uv = sampleCube(ml, faceIndex);
vec4 pointLightTile = pointLightDataArray[lightIndex + faceIndex]; // x: tile X offset, y: tile Y offset, z: tile size relative to atlas
vec2 uvtiled = pointLightTile.z * uv + pointLightTile.xy;
#ifdef _InvY
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
#endif
float result = texture(shadowMap, vec3(uvtiled, compare));
// soft shadowing
int newFaceIndex = 0;
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(-1.0, 0.0) / smSize)));
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
#ifdef _InvY
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
#endif
result += texture(shadowMap, vec3(pointLightTile.z * uvtiled + pointLightTile.xy, compare));
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(-1.0, 1.0) / smSize)));
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
#ifdef _InvY
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
#endif
result += texture(shadowMap, vec3(uvtiled, compare));
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(0.0, -1.0) / smSize)));
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
#ifdef _InvY
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
#endif
result += texture(shadowMap, vec3(uvtiled, compare));
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(-1.0, -1.0) / smSize)));
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
#ifdef _InvY
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
#endif
result += texture(shadowMap, vec3(uvtiled, compare));
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(0.0, 1.0) / smSize)));
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
#ifdef _InvY
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
#endif
result += texture(shadowMap, vec3(uvtiled, compare));
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(1.0, -1.0) / smSize)));
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
#ifdef _InvY
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
#endif
result += texture(shadowMap, vec3(uvtiled, compare));
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(1.0, 0.0) / smSize)));
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
#ifdef _InvY
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
#endif
result += texture(shadowMap, vec3(uvtiled, compare));
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(1.0, 1.0) / smSize)));
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
#ifdef _InvY
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
#endif
result += texture(shadowMap, vec3(uvtiled, compare));
return result / 9.0;
}
#endif
float shadowTest(sampler2DShadow shadowMap, const vec3 lPos, const float shadowsBias) {
#ifdef _SMSizeUniform
vec2 smSize = smSizeUniform;
@ -95,7 +309,7 @@ mat4 getCascadeMat(const float d, out int casi, out int casIndex) {
float shadowTestCascade(sampler2DShadow shadowMap, const vec3 eye, const vec3 p, const float shadowsBias) {
#ifdef _SMSizeUniform
vec2 smSize = smSizeUniform * vec2(shadowmapCascades, 1.0);
vec2 smSize = smSizeUniform;
#else
const vec2 smSize = shadowmapSize * vec2(shadowmapCascades, 1.0);
#endif

View file

@ -24,7 +24,7 @@ uniform vec2 cameraPlane;
#ifdef _SinglePoint
#ifdef _Spot
uniform sampler2DShadow shadowMapSpot[1];
uniform mat4 LWVPSpot0;
uniform mat4 LWVPSpot[1];
#else
uniform samplerCubeShadow shadowMapPoint[1];
uniform vec2 lightProj;
@ -35,10 +35,7 @@ uniform vec2 cameraPlane;
uniform vec2 lightProj;
#ifdef _Spot
uniform sampler2DShadow shadowMapSpot[4];
uniform mat4 LWVPSpot0;
uniform mat4 LWVPSpot1;
uniform mat4 LWVPSpot2;
uniform mat4 LWVPSpot3;
uniform mat4 LWVPSpot[maxLightsCluster];
#endif
#endif
#endif
@ -103,7 +100,7 @@ void rayStep(inout vec3 curPos, inout float curOpticalDepth, inout float scatter
#ifdef _SinglePoint
#ifdef _Spot
vec4 lPos = LWVPSpot0 * vec4(curPos, 1.0);
vec4 lPos = LWVPSpot[0] * vec4(curPos, 1.0);
visibility = shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, pointBias);
float spotEffect = dot(spotDir, normalize(pointPos - curPos)); // lightDir
if (spotEffect < spotData.x) { // x - cutoff, y - cutoff - exponent

View file

@ -108,23 +108,32 @@
"ifdef": ["_SinglePoint", "_Spot"]
},
{
"name": "LWVPSpot0",
"name": "LWVPSpotArray",
"link": "_biasLightWorldViewProjectionMatrixSpotArray",
"ifdef": ["_Clusters", "_ShadowMap", "_Spot"]
},
{
"name": "LWVPSpot[0]",
"link": "_biasLightWorldViewProjectionMatrixSpot0",
"ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_Spot", "_ShadowMap"]
},
{
"name": "LWVPSpot1",
"name": "LWVPSpot[1]",
"link": "_biasLightWorldViewProjectionMatrixSpot1",
"ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_Spot", "_ShadowMap"]
},
{
"name": "LWVPSpot2",
"name": "LWVPSpot[2]",
"link": "_biasLightWorldViewProjectionMatrixSpot2",
"ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_Spot", "_ShadowMap"]
},
{
"name": "LWVPSpot3",
"name": "LWVPSpot[3]",
"link": "_biasLightWorldViewProjectionMatrixSpot3",
"ifndef": ["_ShadowMapAtlas"],
"ifdef": ["_Spot", "_ShadowMap"]
}
],

View file

@ -1,6 +1,7 @@
package armory.renderpath;
import iron.RenderPath;
import iron.object.LightObject;
class Inc {
@ -39,6 +40,171 @@ class Inc {
#end
}
#if arm_shadowmap_atlas
public static function updatePointLightAtlasData(): Void {
var atlas = ShadowMapAtlas.shadowMapAtlases.get(ShadowMapAtlas.shadowMapAtlasName("point"));
if (atlas != null) {
if(LightObject.pointLightsData == null) {
LightObject.pointLightsData = new kha.arrays.Float32Array(
LightObject.maxLightsCluster * ShadowMapTile.tilesLightType("point") * 4 ); // max possible visible lights * 6 or 2 (faces) * 4 (xyzw)
}
var n = iron.Scene.active.lights.length > LightObject.maxLightsCluster ? LightObject.maxLightsCluster : iron.Scene.active.lights.length;
var i = 0;
var j = 0;
for (light in iron.Scene.active.lights) {
if (i >= n)
break;
if (LightObject.discardLightCulled(light)) continue;
if (light.data.raw.type == "point") {
for(k in 0...light.tileOffsetX.length) {
LightObject.pointLightsData[j ] = light.tileOffsetX[k]; // posx
LightObject.pointLightsData[j + 1] = light.tileOffsetY[k]; // posy
LightObject.pointLightsData[j + 2] = light.tileScale[k]; // tile scale factor relative to atlas
LightObject.pointLightsData[j + 3] = 0; // padding
j += 4;
}
}
i++;
}
}
}
public static function bindShadowMapAtlas() {
for (atlas in ShadowMapAtlas.shadowMapAtlases) {
path.bindTarget(atlas.target, atlas.target);
}
}
static function getShadowMapAtlas(atlas:ShadowMapAtlas):String {
inline function createDepthTarget(name: String, size: Int) {
var t = new RenderTargetRaw();
t.name = name;
t.width = t.height = size;
t.format = "DEPTH16";
return path.createRenderTarget(t);
}
var rt = path.renderTargets.get(atlas.target);
// Create shadowmap atlas texture on the fly and replace existing on size change
if (rt == null) {
rt = createDepthTarget(atlas.target, atlas.sizew);
}
else if (atlas.updateRenderTarget) {
atlas.updateRenderTarget = false;
// Resize shadow map
rt.unload();
rt = createDepthTarget(atlas.target, atlas.sizew);
}
return atlas.target;
}
public static function drawShadowMapAtlas() {
#if rp_shadowmap
#if rp_probes
// Share shadow map with probe
if (lastFrame == RenderPath.active.frame)
return;
lastFrame = RenderPath.active.frame;
#end
// add new lights to the atlases
for (light in iron.Scene.active.lights) {
if (!light.lightInAtlas && !light.culledLight && light.visible && light.shadowMapScale > 0.0
&& light.data.raw.strength > 0.0 && light.data.raw.cast_shadow) {
light.lightInAtlas = ShadowMapAtlas.addLight(light);
}
}
// update point light data before rendering
updatePointLightAtlasData();
for (atlas in ShadowMapAtlas.shadowMapAtlases) {
var tilesToRemove = [];
#if arm_shadowmap_atlas_lod
var tilesToChangeSize = [];
#end
var shadowmap = getShadowMapAtlas(atlas);
path.setTargetStream(shadowmap);
path.clearTarget(null, 1.0);
for (tile in atlas.activeTiles) {
if (tile.light == null || !tile.light.visible || tile.light.culledLight
|| !tile.light.data.raw.cast_shadow || tile.light.data.raw.strength == 0) {
tile.unlockLight = true;
tilesToRemove.push(tile);
continue;
}
#if arm_shadowmap_atlas_lod
var newTileSize = atlas.getTileSize(tile.light.shadowMapScale);
if (newTileSize != tile.size) {
if (newTileSize == 0) {
tile.unlockLight = true;
tilesToRemove.push(tile);
continue;
}
// queue for size change
tile.newTileSize = newTileSize;
tilesToChangeSize.push(tile);
}
#end
// set the tile offset for this tile and every linked tile to this one
var j = 0;
tile.forEachTileLinked(function (lTile) {
tile.light.tileOffsetX[j] = (lTile.coordsX == 0) ? 0.0 : lTile.coordsX / atlas.sizew;
tile.light.tileOffsetY[j] = (lTile.coordsY == 0) ? 0.0 : lTile.coordsY / atlas.sizew;
tile.light.tileScale[j] = lTile.size / atlas.sizew;
j++;
});
// set shadowmap size for uniform
tile.light.data.raw.shadowmap_size = atlas.sizew;
path.light = tile.light;
var face = 0;
var faces = ShadowMapTile.tilesLightType(tile.light.data.raw.type);
tile.forEachTileLinked(function (lTile) {
if (faces > 1) {
#if arm_csm
switch (tile.light.data.raw.type) {
case "sun": tile.light.setCascade(iron.Scene.active.camera, face);
case "point": path.currentFace = face;
}
#else
path.currentFace = face;
#end
face++;
}
path.setCurrentViewportWithOffset(lTile.size, lTile.size, lTile.coordsX, lTile.coordsY);
path.drawMeshesStream("shadowmap");
});
path.currentFace = -1;
}
path.endStream();
#if arm_shadowmap_atlas_lod
for (tile in tilesToChangeSize) {
tilesToRemove.push(tile);
var newTile = ShadowMapTile.assignTiles(tile.light, atlas, tile);
if (newTile != null)
atlas.activeTiles.push(newTile);
}
// update point light data after changing size of tiles to avoid render issues
updatePointLightAtlasData();
#end
for (tile in tilesToRemove) {
atlas.activeTiles.remove(tile);
tile.freeTile();
}
}
#end // rp_shadowmap
}
#else
public static function bindShadowMap() {
for (l in iron.Scene.active.lights) {
if (!l.visible || l.data.raw.type != "sun") continue;
@ -56,10 +222,15 @@ class Inc {
}
}
static function shadowMapName(l: iron.object.LightObject): String {
if (l.data.raw.type == "sun") return "shadowMap";
if (l.data.raw.type == "point") return "shadowMapPoint[" + pointIndex + "]";
else return "shadowMapSpot[" + spotIndex + "]";
static function shadowMapName(light: LightObject): String {
switch (light.data.raw.type) {
case "sun":
return "shadowMap";
case "point":
return "shadowMapPoint[" + pointIndex + "]";
default:
return "shadowMapSpot[" + spotIndex + "]";
}
}
static function getShadowMap(l: iron.object.LightObject): String {
@ -130,6 +301,7 @@ class Inc {
#end // rp_shadowmap
}
#end
public static function applyConfig() {
#if arm_config
@ -203,7 +375,11 @@ class Inc {
path.setTarget("accum", ["revealage"]);
#if rp_shadowmap
{
#if arm_shadowmap_atlas
bindShadowMapAtlas();
#else
bindShadowMap();
#end
}
#end
path.drawMeshes("translucent");
@ -342,3 +518,490 @@ class Inc {
#end
}
}
#if arm_shadowmap_atlas
class ShadowMapAtlas {
public var target: String;
public var baseTileSizeConst: Int;
public var maxAtlasSizeConst: Int;
public var sizew: Int;
public var sizeh: Int;
public var currTileOffset = 0;
public var tiles: Array<ShadowMapTile> = [];
public var activeTiles: Array<ShadowMapTile> = [];
public var depth = 1;
#if arm_shadowmap_atlas_lod
static var tileSizes: Array<Int> = [];
static var tileSizeFactor: Array<Float> = [];
#end
public var updateRenderTarget = false;
public static var shadowMapAtlases:Map<String, ShadowMapAtlas> = new Map(); // map a shadowmap atlas to their light type
function new(light: LightObject) {
var maxTileSize = shadowMapAtlasSize(light);
this.target = shadowMapAtlasName(light.data.raw.type);
this.sizew = this.sizeh = this.baseTileSizeConst = maxTileSize;
this.depth = getSubdivisions();
this.maxAtlasSizeConst = getMaxAtlasSize(light.data.raw.type);
#if arm_shadowmap_atlas_lod
if (tileSizes.length == 0)
computeTileSizes(maxTileSize, depth);
#end
}
/**
* Adds a light to an atlas. The atlas is decided based on the type of the light
* @param light of type LightObject to be added to an yatlas
* @return if the light was added succesfully
*/
public static function addLight(light: LightObject): Bool {
// check if light can be added based on culling
if (light.culledLight || light.shadowMapScale == 0.0)
return false;
var atlasName = shadowMapAtlasName(light.data.raw.type);
var atlas = shadowMapAtlases.get(atlasName);
if (atlas == null) {
// create a new atlas
atlas = new ShadowMapAtlas(light);
shadowMapAtlases.set(atlasName, atlas);
}
// find a free tile for this light
var mainTile = ShadowMapTile.assignTiles(light, atlas, null);
if (mainTile == null)
return false;
// push main tile to active tiles
atlas.activeTiles.push(mainTile);
return true;
}
static inline function shadowMapAtlasSize(light:LightObject):Int {
// TODO: this can break because we are changing shadowmap_size elsewhere.
return light.data.raw.shadowmap_size;
}
public function getTileSize(shadowMapScale: Float): Int {
#if arm_shadowmap_atlas_lod
// find the first scale factor that is smaller to the shadowmap scale, and then return the previous one.
var i = 0;
for (sizeFactor in tileSizeFactor) {
if (sizeFactor < shadowMapScale) break;
i++;
}
return tileSizes[i - 1];
#else
return this.baseTileSizeConst;
#end
}
#if arm_shadowmap_atlas_lod
static function computeTileSizes(maxTileSize: Int, depth: Int): Void {
// find the highest value based on the calculation done in the cluster code
var base = LightObject.zToShadowMapScale(0, 16);
var subdiv = base / depth;
for(i in 0...depth){
tileSizes.push(Std.int(maxTileSize / Math.pow(2, i)));
tileSizeFactor.push(base);
base -= subdiv;
}
tileSizes.push(0);
tileSizeFactor.push(0.0);
}
#end
public inline function atlasLimitReached() {
// asume square atlas
return (currTileOffset + 1) * baseTileSizeConst > maxAtlasSizeConst;
}
public static inline function shadowMapAtlasName(type: String): String {
#if arm_shadowmap_atlas_single_map
return "shadowMapAtlas";
#else
switch (type) {
case "point":
return "shadowMapAtlasPoint";
case "sun":
return "shadowMapAtlasSun";
default:
return "shadowMapAtlasSpot";
}
#end
}
public static inline function getSubdivisions(): Int {
#if (rp_shadowmap_atlas_lod_subdivisions == 2)
return 2;
#elseif (rp_shadowmap_atlas_lod_subdivisions == 3)
return 3;
#elseif (rp_shadowmap_atlas_lod_subdivisions == 4)
return 4;
#elseif (rp_shadowmap_atlas_lod_subdivisions == 5)
return 5;
#elseif (rp_shadowmap_atlas_lod_subdivisions == 6)
return 6;
#elseif (rp_shadowmap_atlas_lod_subdivisions == 7)
return 7;
#elseif (rp_shadowmap_atlas_lod_subdivisions == 8)
return 8;
#elseif (!arm_shadowmap_atlas_lod)
return 1;
#end
}
public static inline function getMaxAtlasSize(type: String): Int {
#if arm_shadowmap_atlas_single_map
#if (rp_shadowmap_atlas_max_size == 1024)
return 1024;
#elseif (rp_shadowmap_atlas_max_size == 2048)
return 2048;
#elseif (rp_shadowmap_atlas_max_size == 4096)
return 4096;
#elseif (rp_shadowmap_atlas_max_size == 8192)
return 8192;
#elseif (rp_shadowmap_atlas_max_size == 16384)
return 16384;
#end
#else
switch (type) {
case "point": {
#if (rp_shadowmap_atlas_max_size_point == 1024)
return 1024;
#elseif (rp_shadowmap_atlas_max_size_point == 2048)
return 2048;
#elseif (rp_shadowmap_atlas_max_size_point == 4096)
return 4096;
#elseif (rp_shadowmap_atlas_max_size_point == 8192)
return 8192;
#elseif (rp_shadowmap_atlas_max_size_point == 16384)
return 16384;
#end
}
case "spot": {
#if (rp_shadowmap_atlas_max_size_spot == 1024)
return 1024;
#elseif (rp_shadowmap_atlas_max_size_spot == 2048)
return 2048;
#elseif (rp_shadowmap_atlas_max_size_spot == 4096)
return 4096;
#elseif (rp_shadowmap_atlas_max_size_spot == 8192)
return 8192;
#elseif (rp_shadowmap_atlas_max_size_spot == 16384)
return 16384;
#end
}
case "sun": {
#if (rp_shadowmap_atlas_max_size_sun == 1024)
return 1024;
#elseif (rp_shadowmap_atlas_max_size_sun == 2048)
return 2048;
#elseif (rp_shadowmap_atlas_max_size_sun == 4096)
return 4096;
#elseif (rp_shadowmap_atlas_max_size_sun == 8192)
return 8192;
#elseif (rp_shadowmap_atlas_max_size_sun == 16384)
return 16384;
#end
}
default: {
#if (rp_shadowmap_atlas_max_size == 1024)
return 1024;
#elseif (rp_shadowmap_atlas_max_size == 2048)
return 2048;
#elseif (rp_shadowmap_atlas_max_size == 4096)
return 4096;
#elseif (rp_shadowmap_atlas_max_size == 8192)
return 8192;
#elseif (rp_shadowmap_atlas_max_size == 16384)
return 16384;
#end
}
}
#end
}
}
class ShadowMapTile {
public var light:Null<LightObject> = null;
public var coordsX:Int;
public var coordsY:Int;
public var size:Int;
public var tiles:Array<ShadowMapTile> = [];
public var linkedTile:ShadowMapTile = null;
#if arm_shadowmap_atlas_lod
public var parentTile: ShadowMapTile = null;
public var activeSubTiles: Int = 0;
public var newTileSize: Int = -1;
static var tilePattern = [[0, 0], [1, 0], [0, 1], [1, 1]];
#end
function new(coordsX: Int, coordsY: Int, size: Int) {
this.coordsX = coordsX;
this.coordsY = coordsY;
this.size = size;
}
public static function assignTiles(light: LightObject, atlas: ShadowMapAtlas, oldTile: ShadowMapTile): ShadowMapTile {
var tileSize = 0;
#if arm_shadowmap_atlas_lod
if (oldTile != null && oldTile.newTileSize != -1) {
// reuse tilesize instead of computing it again
tileSize = oldTile.newTileSize;
oldTile.newTileSize = -1;
}
else
#end
tileSize = atlas.getTileSize(light.shadowMapScale);
if (tileSize == 0)
return null;
var tiles = [];
tiles = findCreateTiles(light, oldTile, atlas, tilesLightType(light.data.raw.type), tileSize);
// lock new tiles with light
for (tile in tiles)
tile.lockTile(light);
return linkTiles(tiles);
}
static inline function linkTiles(tiles: Array<ShadowMapTile>): ShadowMapTile {
if (tiles.length > 1) {
var linkedTile = tiles[0];
for (i in 1...tiles.length) {
linkedTile.linkedTile = tiles[i];
linkedTile = tiles[i];
}
}
return tiles[0];
}
static inline function findCreateTiles(light: LightObject, oldTile: ShadowMapTile, atlas: ShadowMapAtlas, tilesPerLightType: Int, tileSize: Int): Array<ShadowMapTile> {
var tilesFound: Array<ShadowMapTile> = [];
var updateAtlas = false;
while (tilesFound.length < tilesPerLightType) {
findTiles(light, oldTile, atlas.tiles, tileSize, tilesPerLightType, tilesFound);
if (tilesFound.length < tilesPerLightType) {
tilesFound = []; // empty tilesFound
// skip creating more tiles if limit has been reached
if (atlas.atlasLimitReached())
break;
createTiles(atlas.tiles, atlas.baseTileSizeConst, atlas.depth, atlas.currTileOffset, atlas.currTileOffset);
atlas.currTileOffset++;
// update texture to accomodate new size
atlas.updateRenderTarget = true;
atlas.sizew = atlas.sizeh = atlas.currTileOffset * atlas.baseTileSizeConst;
}
}
return tilesFound;
}
inline static function findTiles(light:LightObject, oldTile: ShadowMapTile,
tiles: Array<ShadowMapTile>, size: Int, tilesCount: Int, tilesFound: Array<ShadowMapTile>): Void {
#if arm_shadowmap_atlas_lod
if (oldTile != null) {
// reuse children tiles
if (size < oldTile.size) {
oldTile.forEachTileLinked(function(lTile) {
var childTile = findFreeChildTile(lTile, size);
tilesFound.push(childTile);
});
}
// reuse parent tiles
else {
oldTile.forEachTileLinked(function(lTile) {
// find out if parents tiles are not occupied
var parentTile = findFreeParentTile(lTile, size);
// if parent is free, add it to found tiles
if (parentTile != null)
tilesFound.push(parentTile);
});
if (tilesFound.length < tilesCount) {
// find naively the rest of the tiles that couldn't be reused
findTilesNaive(light, tiles, size, tilesCount, tilesFound);
}
}
}
else
#end
findTilesNaive(light, tiles, size, tilesCount, tilesFound);
}
#if arm_shadowmap_atlas_lod
static inline function findFreeChildTile(tile: ShadowMapTile, size: Int): ShadowMapTile {
var childrenTile = tile;
while (size < childrenTile.size) {
childrenTile = childrenTile.tiles[0];
}
return childrenTile;
}
static inline function findFreeParentTile(tile: ShadowMapTile, size: Int): ShadowMapTile {
var parentTile = tile;
while (size > parentTile.size) {
parentTile = parentTile.parentTile;
// stop if parent tile is occupied
if (parentTile.activeSubTiles > 1) {
parentTile = null;
break;
}
}
return parentTile;
}
#end
static function findTilesNaive(light:LightObject, tiles: Array<ShadowMapTile>, size: Int, tilesCount: Int, tilesFound: Array<ShadowMapTile>): Void {
for (tile in tiles) {
if (tile.size == size) {
if (tile.light == null #if arm_shadowmap_atlas_lod && tile.activeSubTiles == 0 #end) {
tilesFound.push(tile);
// stop after finding enough tiles
if (tilesFound.length == tilesCount)
return;
}
}
else {
// skip over if end of the tree or tile is occupied
if (tile.tiles.length == 0 || tile.light != null)
continue;
findTilesNaive(light, tile.tiles, size, tilesCount, tilesFound);
// skip iterating over the rest of the tiles if found enough
if (tilesFound.length == tilesCount)
return;
}
}
}
// create a basic tile and subdivide it if needed
public static function createTiles(tiles:Array<ShadowMapTile>, size:Int, depth: Int, baseX:Int, baseY:Int) {
var i = baseX;
var j = 0;
var lastTile = tiles.length;
// assume occupied tiles start from 1 line before the base x
var occupiedTiles = baseX - 1;
while (i >= 0) {
if (i <= occupiedTiles) { // avoid overriding tiles
j = baseY;
}
while (j <= baseY) {
// create base tile of max-size
tiles.push(new ShadowMapTile(size * i, size * j, size));
#if arm_shadowmap_atlas_lod
tiles[lastTile].tiles = subDivTile(tiles[lastTile], size, size * i, size * j, depth - 1);
#end
lastTile++;
j++;
}
i--;
}
}
#if arm_shadowmap_atlas_lod
static function subDivTile(parent: ShadowMapTile, size: Int, baseCoordsX: Int, baseCoordsY: Int, depth: Int): Array<ShadowMapTile> {
var tileSize = Std.int(size / 2);
var tiles = [];
for (i in 0...4) {
var coordsX = baseCoordsX + tilePattern[i][0] * tileSize;
var coordsY = baseCoordsY + tilePattern[i][1] * tileSize;
var tile = new ShadowMapTile(coordsX, coordsY, tileSize);
tile.parentTile = parent;
if (depth > 1)
tile.tiles = subDivTile(tile, tileSize, coordsX, coordsY, depth - 1);
tiles.push(tile);
}
return tiles;
}
#end
public static inline function tilesLightType(type: String): Int {
switch (type) {
case "sun":
return LightObject.cascadeCount;
case "point":
return 6;
default:
return 1;
}
}
inline function lockTile(light: LightObject): Void {
if (this.light != null)
return;
this.light = light;
#if arm_shadowmap_atlas_lod
// update the count of used tiles for parents
this.forEachParentTile(function (pTile) {
pTile.activeSubTiles++;
});
#end
}
public var unlockLight: Bool = false;
public function freeTile(): Void {
// prevent duplicates
if (light != null && unlockLight) {
light.lightInAtlas = false;
unlockLight = false;
}
var linkedTile = this;
var tempTile = this;
while (linkedTile != null) {
linkedTile.light = null;
#if arm_shadowmap_atlas_lod
// update the count of used tiles for parents
linkedTile.forEachParentTile(function (pTile) {
if (pTile.activeSubTiles > 0)
pTile.activeSubTiles--;
});
#end
linkedTile = linkedTile.linkedTile;
// unlink linked tiles
tempTile.linkedTile = null;
tempTile = linkedTile;
}
}
public inline function forEachTileLinked(action: ShadowMapTile->Void): Void {
var linkedTile = this;
while (linkedTile != null) {
action(linkedTile);
linkedTile = linkedTile.linkedTile;
}
}
#if arm_shadowmap_atlas_lod
public inline function forEachParentTile(action: ShadowMapTile->Void): Void {
var parentTile = this.parentTile;
while (parentTile != null) {
action(parentTile);
parentTile = parentTile.parentTile;
}
}
#end
}
#end

View file

@ -7,6 +7,8 @@ class RenderPathCreator {
public static var path: RenderPath;
public static var commands: Void->Void = function() {};
#if (rp_renderer == "Forward")
public static var setTargetMeshes: Void->Void = RenderPathForward.setTargetMeshes;
public static var drawMeshes: Void->Void = RenderPathForward.drawMeshes;
@ -33,13 +35,22 @@ class RenderPathCreator {
#if (rp_renderer == "Forward")
RenderPathForward.init(path);
path.commands = RenderPathForward.commands;
path.commands = function() {
RenderPathForward.commands();
commands();
}
#elseif (rp_renderer == "Deferred")
RenderPathDeferred.init(path);
path.commands = RenderPathDeferred.commands;
path.commands = function() {
RenderPathDeferred.commands();
commands();
}
#elseif (rp_renderer == "Raytracer")
RenderPathRaytracer.init(path);
path.commands = RenderPathRaytracer.commands;
path.commands = function() {
RenderPathRaytracer.commands();
commands();
}
#end
return path;
}

View file

@ -502,8 +502,13 @@ class RenderPathDeferred {
#end
#if (rp_shadowmap)
// atlasing is exclusive for now
#if arm_shadowmap_atlas
Inc.drawShadowMapAtlas();
#else
Inc.drawShadowMap();
#end
#end
// Voxels
#if rp_voxelao
@ -572,7 +577,11 @@ class RenderPathDeferred {
#if rp_shadowmap
{
#if arm_shadowmap_atlas
Inc.bindShadowMapAtlas();
#else
Inc.bindShadowMap();
#end
}
#end
@ -624,7 +633,11 @@ class RenderPathDeferred {
{
path.setTarget("singlea");
path.bindTarget("_main", "gbufferD");
#if arm_shadowmap_atlas
Inc.bindShadowMapAtlas();
#else
Inc.bindShadowMap();
#end
path.drawShader("shader_datas/volumetric_light/volumetric_light");
path.setTarget("singleb");

View file

@ -300,7 +300,11 @@ class RenderPathForward {
#if rp_shadowmap
{
#if arm_shadowmap_atlas
Inc.drawShadowMapAtlas();
#else
Inc.drawShadowMap();
#end
}
#end
@ -352,7 +356,11 @@ class RenderPathForward {
#if rp_shadowmap
{
#if arm_shadowmap_atlas
Inc.bindShadowMapAtlas();
#else
Inc.bindShadowMap();
#end
}
#end
@ -466,7 +474,11 @@ class RenderPathForward {
{
path.setTarget("singlea");
path.bindTarget("_main", "gbufferD");
#if arm_shadowmap_atlas
Inc.bindShadowMapAtlas();
#else
Inc.bindShadowMap();
#end
path.drawShader("shader_datas/volumetric_light/volumetric_light");
path.setTarget("singleb");

View file

@ -421,6 +421,9 @@ class DebugConsole extends Trait {
var lightHandle = Id.handle();
lightHandle.value = light.data.raw.strength / 10;
light.data.raw.strength = ui.slider(lightHandle, "Strength", 0.0, 5.0, true) * 10;
#if arm_shadowmap_atlas
ui.text("status: " + (light.culledLight ? "culled" : "rendered"));
#end
}
else if (Std.is(selectedObject, iron.object.CameraObject)) {
selectedType = "(Camera)";

View file

@ -43,6 +43,22 @@ def add_world_defs():
if rpdat.rp_shadowmap_cascades != '1':
wrd.world_defs += '_CSM'
assets.add_khafile_def('arm_csm')
if rpdat.rp_shadowmap_atlas:
assets.add_khafile_def('arm_shadowmap_atlas')
wrd.world_defs += '_ShadowMapAtlas'
if rpdat.rp_shadowmap_atlas_single_map:
assets.add_khafile_def('arm_shadowmap_atlas_single_map')
wrd.world_defs += '_SingleAtlas'
assets.add_khafile_def('rp_shadowmap_atlas_max_size_point={0}'.format(int(rpdat.rp_shadowmap_atlas_max_size_point)))
assets.add_khafile_def('rp_shadowmap_atlas_max_size_spot={0}'.format(int(rpdat.rp_shadowmap_atlas_max_size_spot)))
assets.add_khafile_def('rp_shadowmap_atlas_max_size_sun={0}'.format(int(rpdat.rp_shadowmap_atlas_max_size_sun)))
assets.add_khafile_def('rp_shadowmap_atlas_max_size={0}'.format(int(rpdat.rp_shadowmap_atlas_max_size)))
assets.add_khafile_def('rp_max_lights_cluster={0}'.format(int(rpdat.rp_max_lights_cluster)))
assets.add_khafile_def('rp_max_lights={0}'.format(int(rpdat.rp_max_lights)))
if rpdat.rp_shadowmap_atlas_lod:
assets.add_khafile_def('arm_shadowmap_atlas_lod')
assets.add_khafile_def('rp_shadowmap_atlas_lod_subdivisions={0}'.format(int(rpdat.rp_shadowmap_atlas_lod_subdivisions)))
# SS
if rpdat.rp_ssgi == 'RTGI' or rpdat.rp_ssgi == 'RTAO':
if rpdat.rp_ssgi == 'RTGI':
@ -89,9 +105,14 @@ def add_world_defs():
wrd.world_defs += '_Spot'
assets.add_khafile_def('arm_spot')
if point_lights == 1:
wrd.world_defs += '_SinglePoint'
elif point_lights > 1:
if not rpdat.rp_shadowmap_atlas:
if point_lights == 1:
wrd.world_defs += '_SinglePoint'
elif point_lights > 1:
wrd.world_defs += '_Clusters'
assets.add_khafile_def('arm_clusters')
else:
wrd.world_defs += '_SMSizeUniform'
wrd.world_defs += '_Clusters'
assets.add_khafile_def('arm_clusters')

View file

@ -3,8 +3,10 @@ import bpy
def write(vert, frag):
wrd = bpy.data.worlds['Arm']
is_shadows = '_ShadowMap' in wrd.world_defs
is_shadows_atlas = '_ShadowMapAtlas' in wrd.world_defs
is_single_atlas = is_shadows_atlas and '_SingleAtlas' in wrd.world_defs
frag.add_include('std/clusters.glsl')
frag.add_include_front('std/clusters.glsl')
frag.add_uniform('vec2 cameraProj', link='_cameraPlaneProj')
frag.add_uniform('vec2 cameraPlane', link='_cameraPlane')
frag.add_uniform('vec4 lightsArray[maxLights * 2]', link='_lightsArray')
@ -12,7 +14,12 @@ def write(vert, frag):
if is_shadows:
frag.add_uniform('bool receiveShadow')
frag.add_uniform('vec2 lightProj', link='_lightPlaneProj', included=True)
frag.add_uniform('samplerCubeShadow shadowMapPoint[4]', included=True)
if is_shadows_atlas:
if not is_single_atlas:
frag.add_uniform('sampler2DShadow shadowMapAtlasPoint', included=True)
frag.add_uniform('vec4 pointLightDataArray[maxLightsCluster]', link='_pointLightsAtlasArray', included=True)
else:
frag.add_uniform('samplerCubeShadow shadowMapPoint[4]', included=True)
vert.add_out('vec4 wvpposition')
vert.write('wvpposition = gl_Position;')
# wvpposition.z / wvpposition.w
@ -29,11 +36,12 @@ def write(vert, frag):
frag.write('int numSpots = int(texelFetch(clustersData, ivec2(clusterI, 1 + maxLightsCluster), 0).r * 255);')
frag.write('int numPoints = numLights - numSpots;')
if is_shadows:
frag.add_uniform('mat4 LWVPSpot0', link='_biasLightWorldViewProjectionMatrixSpot0', included=True)
frag.add_uniform('mat4 LWVPSpot1', link='_biasLightWorldViewProjectionMatrixSpot1', included=True)
frag.add_uniform('mat4 LWVPSpot2', link='_biasLightWorldViewProjectionMatrixSpot2', included=True)
frag.add_uniform('mat4 LWVPSpot3', link='_biasLightWorldViewProjectionMatrixSpot3', included=True)
frag.add_uniform('sampler2DShadow shadowMapSpot[4]', included=True)
if is_shadows_atlas and not is_single_atlas:
frag.add_uniform(f'sampler2DShadow shadowMapAtlasSpot', included=True)
elif not is_shadows_atlas:
frag.add_uniform('sampler2DShadow shadowMapSpot[4]', included=True)
# FIXME: type is actually mat4, but otherwise it will not be set as floats when writing the shaders' json files
frag.add_uniform('vec4 LWVPSpotArray[maxLightsCluster]', link='_biasLightWorldViewProjectionMatrixSpotArray', included=True)
frag.write('for (int i = 0; i < min(numLights, maxLightsCluster); i++) {')
frag.write('int li = int(texelFetch(clustersData, ivec2(clusterI, i + 1), 0).r * 255);')
@ -52,7 +60,7 @@ def write(vert, frag):
if is_shadows:
frag.write(' , li, lightsArray[li * 2].w, receiveShadow') # bias
if '_Spot' in wrd.world_defs:
frag.write(' , li > numPoints - 1')
frag.write(' , lightsArray[li * 2 + 1].w != 0.0')
frag.write(' , lightsArray[li * 2 + 1].w') # cutoff
frag.write(' , lightsArraySpot[li].w') # cutoff - exponent
frag.write(' , lightsArraySpot[li].xyz') # spotDir

View file

@ -354,6 +354,12 @@ def make_forward_mobile(con_mesh):
return
is_shadows = '_ShadowMap' in wrd.world_defs
is_shadows_atlas = '_ShadowMapAtlas' in wrd.world_defs
is_single_atlas = is_shadows_atlas and '_SingleAtlas' in wrd.world_defs
shadowmap_sun = 'shadowMap'
if is_shadows_atlas:
shadowmap_sun = 'shadowMapAtlasSun' if not is_single_atlas else 'shadowMapAtlas'
frag.add_uniform('vec2 smSizeUniform', '_shadowMapSize', included=True)
frag.write('vec3 direct = vec3(0.0);')
if '_Sun' in wrd.world_defs:
@ -366,7 +372,7 @@ def make_forward_mobile(con_mesh):
vert.add_uniform('mat4 LWVP', '_biasLightWorldViewProjectionMatrix')
vert.write('lightPosition = LWVP * spos;')
frag.add_uniform('bool receiveShadow')
frag.add_uniform('sampler2DShadow shadowMap')
frag.add_uniform(f'sampler2DShadow {shadowmap_sun}')
frag.add_uniform('float shadowsBias', '_sunShadowsBias')
frag.write('if (receiveShadow) {')
@ -374,14 +380,14 @@ def make_forward_mobile(con_mesh):
frag.add_include('std/shadows.glsl')
frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True)
frag.add_uniform('vec3 eye', '_cameraPosition')
frag.write('svisibility = shadowTestCascade(shadowMap, eye, wposition + n * shadowsBias * 10, shadowsBias);')
frag.write(f'svisibility = shadowTestCascade({shadowmap_sun}, eye, wposition + n * shadowsBias * 10, shadowsBias);')
else:
frag.write('if (lightPosition.w > 0.0) {')
frag.write(' vec3 lPos = lightPosition.xyz / lightPosition.w;')
if '_Legacy' in wrd.world_defs:
frag.write(' svisibility = float(texture(shadowMap, vec2(lPos.xy)).r > lPos.z - shadowsBias);')
frag.write(f' svisibility = float(texture({shadowmap_sun}, vec2(lPos.xy)).r > lPos.z - shadowsBias);')
else:
frag.write(' svisibility = texture(shadowMap, vec3(lPos.xy, lPos.z - shadowsBias)).r;')
frag.write(f' svisibility = texture({shadowmap_sun}, vec3(lPos.xy, lPos.z - shadowsBias)).r;')
frag.write('}')
frag.write('}') # receiveShadow
frag.write('direct += basecol * sdotNL * sunCol * svisibility;')
@ -404,8 +410,8 @@ def make_forward_mobile(con_mesh):
frag.write('if (receiveShadow) {')
if '_Spot' in wrd.world_defs:
vert.add_out('vec4 spotPosition')
vert.add_uniform('mat4 LWVPSpot0', link='_biasLightWorldViewProjectionMatrixSpot0')
vert.write('spotPosition = LWVPSpot0 * spos;')
vert.add_uniform('mat4 LWVPSpotArray[1]', link='_biasLightWorldViewProjectionMatrixSpotArray')
vert.write('spotPosition = LWVPSpotArray[0] * spos;')
frag.add_uniform('sampler2DShadow shadowMapSpot[1]')
frag.write('if (spotPosition.w > 0.0) {')
frag.write(' vec3 lPos = spotPosition.xyz / spotPosition.w;')
@ -532,13 +538,10 @@ def make_forward(con_mesh):
frag.add_uniform('sampler2D sltcMag', '_ltcMag', included=True)
if '_ShadowMap' in wrd.world_defs:
if '_SinglePoint' in wrd.world_defs:
frag.add_uniform('mat4 LWVPSpot0', link='_biasLightViewProjectionMatrixSpot0', included=True)
frag.add_uniform('mat4 LWVPSpot[0]', link='_biasLightViewProjectionMatrixSpot0', included=True)
frag.add_uniform('sampler2DShadow shadowMapSpot[1]', included=True)
if '_Clusters' in wrd.world_defs:
frag.add_uniform('mat4 LWVPSpot0', link='_biasLightWorldViewProjectionMatrixSpot0', included=True)
frag.add_uniform('mat4 LWVPSpot1', link='_biasLightWorldViewProjectionMatrixSpot1', included=True)
frag.add_uniform('mat4 LWVPSpot2', link='_biasLightWorldViewProjectionMatrixSpot2', included=True)
frag.add_uniform('mat4 LWVPSpot3', link='_biasLightWorldViewProjectionMatrixSpot3', included=True)
frag.add_uniform('mat4 LWVPSpotArray[4]', link='_biasLightWorldViewProjectionMatrixSpotArray', included=True)
frag.add_uniform('sampler2DShadow shadowMapSpot[4]', included=True)
if not blend:
@ -605,6 +608,12 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
frag.add_include('std/light.glsl')
is_shadows = '_ShadowMap' in wrd.world_defs
is_shadows_atlas = '_ShadowMapAtlas' in wrd.world_defs
is_single_atlas = is_shadows_atlas and '_SingleAtlas' in wrd.world_defs
shadowmap_sun = 'shadowMap'
if is_shadows_atlas:
shadowmap_sun = 'shadowMapAtlasSun' if not is_single_atlas else 'shadowMapAtlas'
frag.add_uniform('vec2 smSizeUniform', '_shadowMapSize', included=True)
frag.write('vec3 albedo = surfaceAlbedo(basecol, metallic);')
frag.write('vec3 f0 = surfaceF0(basecol, metallic);')
@ -661,14 +670,14 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
frag.write('float sdotVH = dot(vVec, sh);')
if is_shadows:
frag.add_uniform('bool receiveShadow')
frag.add_uniform('sampler2DShadow shadowMap')
frag.add_uniform(f'sampler2DShadow {shadowmap_sun}', top=True)
frag.add_uniform('float shadowsBias', '_sunShadowsBias')
frag.write('if (receiveShadow) {')
if '_CSM' in wrd.world_defs:
frag.add_include('std/shadows.glsl')
frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True)
frag.add_uniform('vec3 eye', '_cameraPosition')
frag.write('svisibility = shadowTestCascade(shadowMap, eye, wposition + n * shadowsBias * 10, shadowsBias);')
frag.write(f'svisibility = shadowTestCascade({shadowmap_sun}, eye, wposition + n * shadowsBias * 10, shadowsBias);')
else:
if tese is not None:
tese.add_out('vec4 lightPosition')
@ -685,7 +694,7 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
vert.write('lightPosition = LWVP * spos;')
frag.write('vec3 lPos = lightPosition.xyz / lightPosition.w;')
frag.write('const vec2 smSize = shadowmapSize;')
frag.write('svisibility = PCF(shadowMap, lPos.xy, lPos.z - shadowsBias, smSize);')
frag.write(f'svisibility = PCF({shadowmap_sun}, lPos.xy, lPos.z - shadowsBias, smSize);')
frag.write('}') # receiveShadow
if '_VoxelShadow' in wrd.world_defs and '_VoxelAOvar' in wrd.world_defs:
frag.write('svisibility *= 1.0 - traceShadow(voxels, voxpos, sunDir);')
@ -703,7 +712,7 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
frag.add_uniform('float pointBias', link='_pointShadowsBias')
if '_Spot' in wrd.world_defs:
# Skip world matrix, already in world-space
frag.add_uniform('mat4 LWVPSpot0', link='_biasLightViewProjectionMatrixSpot0', included=True)
frag.add_uniform('mat4 LWVPSpot[1]', link='_biasLightViewProjectionMatrixSpotArray', included=True)
frag.add_uniform('sampler2DShadow shadowMapSpot[1]', included=True)
else:
frag.add_uniform('vec2 lightProj', link='_lightPlaneProj', included=True)

View file

@ -179,6 +179,7 @@ class Shader:
self.includes = []
self.ins = []
self.outs = []
self.uniforms_top = []
self.uniforms = []
self.constants = []
self.functions = {}
@ -205,6 +206,10 @@ class Shader:
if not self.has_include(s):
self.includes.append(s)
def add_include_front(self, s):
if not self.has_include(s):
self.includes.insert(0, s)
def add_in(self, s):
if s not in self.ins:
self.ins.append(s)
@ -213,7 +218,7 @@ class Shader:
if s not in self.outs:
self.outs.append(s)
def add_uniform(self, s, link=None, included=False):
def add_uniform(self, s, link=None, included=False, top=False):
ar = s.split(' ')
# layout(RGBA8) image3D voxels
utype = ar[-2]
@ -236,8 +241,12 @@ class Shader:
ar[0] = 'floats'
ar[1] = ar[1].split('[', 1)[0]
self.context.add_constant(ar[0], ar[1], link=link)
if not included and s not in self.uniforms:
self.uniforms.append(s)
if top:
if not included and s not in self.uniforms_top:
self.uniforms_top.append(s)
else:
if not included and s not in self.uniforms:
self.uniforms.append(s)
def add_const(self, type_str: str, name: str, value_str: str, array_size: int = 0):
"""
@ -375,6 +384,8 @@ class Shader:
s += 'layout(triangle_strip) out;\n'
s += 'layout(max_vertices=3) out;\n'
for a in self.uniforms_top:
s += 'uniform ' + a + ';\n'
for a in self.includes:
s += '#include "' + a + '"\n'
if self.geom_passthrough:

View file

@ -242,6 +242,62 @@ class ArmRPListItem(bpy.types.PropertyGroup):
rp_autoexposure: BoolProperty(name="Auto Exposure", description="Adjust exposure based on luminance", default=False, update=update_renderpath)
rp_compositornodes: BoolProperty(name="Compositor", description="Draw compositor nodes", default=True, update=update_renderpath)
rp_shadows: BoolProperty(name="Shadows", description="Enable shadow casting", default=True, update=update_renderpath)
rp_max_lights: EnumProperty(
items=[('4', '4', '4'),
('8', '8', '8'),
('16', '16', '16'),
('24', '24', '24'),
('32', '32', '32'),
('64', '64', '64'),],
name="Max Lights", description="Max number of lights that can be visible in the screen", default='16')
rp_max_lights_cluster: EnumProperty(
items=[('4', '4', '4'),
('8', '8', '8'),
('16', '16', '16'),
('24', '24', '24'),
('32', '32', '32'),
('64', '64', '64'),],
name="Max Lights Shadows", description="Max number of rendered shadow maps that can be visible in the screen. Always equal or lower than Max Lights", default='16')
rp_shadowmap_atlas: BoolProperty(name="Shadow Map Atlasing", description="Group shadow maps of lights of the same type in the same texture", default=False, update=update_renderpath)
rp_shadowmap_atlas_single_map: BoolProperty(name="Shadow Map Atlas single map", description="Use a single texture for all different light types.", default=False, update=update_renderpath)
rp_shadowmap_atlas_lod: BoolProperty(name="Shadow Map Atlas LOD (Experimental)", description="When enabled, the size of the shadow map will be determined on runtime based on the distance of the light to the camera", default=False, update=update_renderpath)
rp_shadowmap_atlas_lod_subdivisions: EnumProperty(
items=[('2', '2', '2'),
('3', '3', '3'),
('4', '4', '4'),
('5', '5', '5'),
('6', '6', '6'),
('7', '7', '7'),
('8', '8', '8'),],
name="LOD Subdivisions", description="Number of subdivisions of the default tile size for LOD", default='2', update=update_renderpath)
rp_shadowmap_atlas_max_size_point: EnumProperty(
items=[('1024', '1024', '1024'),
('2048', '2048', '2048'),
('4096', '4096', '4096'),
('8192', '8192', '8192'),
('16384', '16384', '16384')],
name="Max Atlas Texture Size Points", description="Sets the limit of the size of the texture.", default='8192', update=update_renderpath)
rp_shadowmap_atlas_max_size_spot: EnumProperty(
items=[('1024', '1024', '1024'),
('2048', '2048', '2048'),
('4096', '4096', '4096'),
('8192', '8192', '8192'),
('16384', '16384', '16384')],
name="Max Atlas Texture Size Spots", description="Sets the limit of the size of the texture.", default='8192', update=update_renderpath)
rp_shadowmap_atlas_max_size_sun: EnumProperty(
items=[('1024', '1024', '1024'),
('2048', '2048', '2048'),
('4096', '4096', '4096'),
('8192', '8192', '8192'),
('16384', '16384', '16384')],
name="Max Atlas Texture Size Sun", description="Sets the limit of the size of the texture.", default='8192', update=update_renderpath)
rp_shadowmap_atlas_max_size: EnumProperty(
items=[('1024', '1024', '1024'),
('2048', '2048', '2048'),
('4096', '4096', '4096'),
('8192', '8192', '8192'),
('16384', '16384', '16384')],
name="Max Atlas Texture Size", description="Sets the limit of the size of the texture.", default='8192', update=update_renderpath)
rp_shadowmap_cube: EnumProperty(
items=[('256', '256', '256'),
('512', '512', '512'),

View file

@ -1268,6 +1268,13 @@ class ARM_PT_RenderPathShadowsPanel(bpy.types.Panel):
rpdat = wrd.arm_rplist[wrd.arm_rplist_index]
self.layout.prop(rpdat, "rp_shadows", text="")
def compute_subdivs(self, max, subdivs):
l = [max]
for i in range(subdivs - 1):
l.append(int(max / 2))
max = max / 2
return l
def draw(self, context):
layout = self.layout
layout.use_property_split = True
@ -1287,6 +1294,55 @@ class ARM_PT_RenderPathShadowsPanel(bpy.types.Panel):
col2.prop(rpdat, 'arm_shadowmap_split')
col.prop(rpdat, 'arm_shadowmap_bounds')
col.prop(rpdat, 'arm_pcfsize')
layout.separator()
layout.prop(rpdat, 'rp_shadowmap_atlas')
colatlas = layout.column()
colatlas.enabled = rpdat.rp_shadowmap_atlas
colatlas.prop(rpdat, 'rp_max_lights')
colatlas.prop(rpdat, 'rp_max_lights_cluster')
colatlas.prop(rpdat, 'rp_shadowmap_atlas_lod')
colatlas_lod = colatlas.column()
colatlas_lod.enabled = rpdat.rp_shadowmap_atlas_lod
colatlas_lod.prop(rpdat, 'rp_shadowmap_atlas_lod_subdivisions')
colatlas_lod_info = colatlas_lod.row()
colatlas_lod_info.alignment = 'RIGHT'
subdivs_list = self.compute_subdivs(int(rpdat.rp_shadowmap_cascade), int(rpdat.rp_shadowmap_atlas_lod_subdivisions))
subdiv_text = "Subdivisions: " + ', '.join(map(str, subdivs_list))
colatlas_lod_info.label(text=subdiv_text, icon="IMAGE_ZDEPTH")
colatlas.prop(rpdat, 'rp_shadowmap_atlas_single_map')
# show size for single texture
if rpdat.rp_shadowmap_atlas_single_map:
colatlas_single = colatlas.column()
colatlas_single.prop(rpdat, 'rp_shadowmap_atlas_max_size')
if int(rpdat.rp_shadowmap_cascade) >= int(rpdat.rp_shadowmap_atlas_max_size):
print(rpdat.rp_shadowmap_atlas_max_size)
colatlas_warning = colatlas_single.row()
colatlas_warning.alignment = 'RIGHT'
colatlas_warning.label(text=f'Warning: {rpdat.rp_shadowmap_atlas_max_size} is too small for the shadowmap size: {rpdat.rp_shadowmap_cascade}', icon="ERROR")
else:
# show size for all types
colatlas_mixed = colatlas.column()
colatlas_mixed.prop(rpdat, 'rp_shadowmap_atlas_max_size_spot')
if int(rpdat.rp_shadowmap_cascade) > int(rpdat.rp_shadowmap_atlas_max_size_spot):
colatlas_warning = colatlas_mixed.row()
colatlas_warning.alignment = 'RIGHT'
colatlas_warning.label(text=f'Warning: {rpdat.rp_shadowmap_atlas_max_size_spot} is too small for the shadowmap size: {rpdat.rp_shadowmap_cascade}', icon="ERROR")
colatlas_mixed.prop(rpdat, 'rp_shadowmap_atlas_max_size_point')
if int(rpdat.rp_shadowmap_cascade) >= int(rpdat.rp_shadowmap_atlas_max_size_point):
colatlas_warning = colatlas_mixed.row()
colatlas_warning.alignment = 'RIGHT'
colatlas_warning.label(text=f'Warning: {rpdat.rp_shadowmap_atlas_max_size_point} is too small for the shadowmap size: {rpdat.rp_shadowmap_cube}', icon="ERROR")
colatlas_mixed.prop(rpdat, 'rp_shadowmap_atlas_max_size_sun')
if int(rpdat.rp_shadowmap_cascade) >= int(rpdat.rp_shadowmap_atlas_max_size_sun):
colatlas_warning = colatlas_mixed.row()
colatlas_warning.alignment = 'RIGHT'
colatlas_warning.label(text=f'Warning: {rpdat.rp_shadowmap_atlas_max_size_sun} is too small for the shadowmap size: {rpdat.rp_shadowmap_cascade}', icon="ERROR")
class ARM_PT_RenderPathVoxelsPanel(bpy.types.Panel):
bl_label = "Voxel AO"

View file

@ -517,6 +517,7 @@ def write_indexhtml(w, h, is_publish):
add_compiledglsl = ''
def write_compiledglsl(defs, make_variants):
rpdat = arm.utils.get_rp()
wrd = bpy.data.worlds['Arm']
shadowmap_size = arm.utils.get_cascade_size(rpdat) if rpdat.rp_shadows else 0
with open(arm.utils.build_dir() + '/compiled/Shaders/compiled.inc', 'w') as f:
f.write(
@ -689,6 +690,22 @@ const float voxelgiAperture = """ + str(round(rpdat.arm_voxelgi_aperture * 100)
if rpdat.arm_skin == 'On':
f.write(
"""const int skinMaxBones = """ + str(rpdat.arm_skin_max_bones) + """;
""")
if '_Clusters' in wrd.world_defs:
max_lights = "4"
max_lights_clusters = "4"
if rpdat.rp_shadowmap_atlas:
max_lights = str(rpdat.rp_max_lights)
max_lights_clusters = str(rpdat.rp_max_lights_cluster)
# prevent max lights cluster being higher than max lights
if (int(max_lights_clusters) > int(max_lights)):
max_lights_clusters = max_lights
f.write(
"""const int maxLights = """ + max_lights + """;
const int maxLightsCluster = """ + max_lights_clusters + """;
const float clusterNear = 4.0;
""")
f.write(add_compiledglsl + '\n') # External defined constants