2018-12-10 09:46:46 +01:00
|
|
|
#version 450
|
|
|
|
|
|
|
|
#include "compiled.inc"
|
|
|
|
#include "std/gbuffer.glsl"
|
|
|
|
#include "std/math.glsl"
|
|
|
|
#ifdef _Clusters
|
|
|
|
#include "std/clusters.glsl"
|
|
|
|
#endif
|
|
|
|
#ifdef _Irr
|
|
|
|
#include "std/shirr.glsl"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
uniform sampler2D gbufferD;
|
|
|
|
uniform sampler2D gbuffer0;
|
|
|
|
uniform sampler2D gbuffer1;
|
|
|
|
|
|
|
|
uniform float envmapStrength;
|
|
|
|
#ifdef _Irr
|
2020-05-10 19:43:02 +02:00
|
|
|
uniform vec4 shirr[7];
|
2018-12-10 09:46:46 +01:00
|
|
|
#endif
|
|
|
|
#ifdef _Brdf
|
|
|
|
uniform sampler2D senvmapBrdf;
|
|
|
|
#endif
|
|
|
|
#ifdef _Rad
|
|
|
|
uniform sampler2D senvmapRadiance;
|
|
|
|
uniform int envmapNumMipmaps;
|
|
|
|
#endif
|
|
|
|
#ifdef _EnvCol
|
|
|
|
uniform vec3 backgroundCol;
|
|
|
|
#endif
|
|
|
|
|
2021-03-15 01:51:36 +01:00
|
|
|
#ifdef _SMSizeUniform
|
|
|
|
//!uniform vec2 smSizeUniform;
|
|
|
|
#endif
|
2018-12-10 09:46:46 +01:00
|
|
|
uniform vec2 cameraProj;
|
|
|
|
uniform vec3 eye;
|
|
|
|
uniform vec3 eyeLook;
|
|
|
|
|
|
|
|
#ifdef _Clusters
|
2021-03-14 14:35:25 +01:00
|
|
|
uniform vec4 lightsArray[maxLights * 3];
|
2018-12-10 09:46:46 +01:00
|
|
|
#ifdef _Spot
|
|
|
|
uniform vec4 lightsArraySpot[maxLights];
|
|
|
|
#endif
|
|
|
|
uniform sampler2D clustersData;
|
|
|
|
uniform vec2 cameraPlane;
|
2018-12-10 17:25:29 +01:00
|
|
|
#endif
|
|
|
|
|
2018-12-10 09:46:46 +01:00
|
|
|
#ifdef _ShadowMap
|
2018-12-15 19:03:11 +01:00
|
|
|
#ifdef _SinglePoint
|
|
|
|
#ifdef _Spot
|
|
|
|
//!uniform sampler2DShadow shadowMapSpot[1];
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
//!uniform mat4 LWVPSpot[1];
|
2018-12-15 19:03:11 +01:00
|
|
|
#else
|
|
|
|
//!uniform samplerCubeShadow shadowMapPoint[1];
|
2018-12-10 17:25:29 +01:00
|
|
|
//!uniform vec2 lightProj;
|
2018-12-15 19:03:11 +01:00
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#ifdef _Clusters
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
#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
|
2018-12-15 19:03:11 +01:00
|
|
|
//!uniform vec2 lightProj;
|
2018-12-10 09:46:46 +01:00
|
|
|
#ifdef _Spot
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
#ifdef _ShadowMapAtlas
|
|
|
|
#ifndef _SingleAtlas
|
|
|
|
//!uniform sampler2DShadow shadowMapAtlasSpot;
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
//!uniform sampler2DShadow shadowMapSpot[4];
|
|
|
|
#endif
|
|
|
|
//!uniform mat4 LWVPSpotArray[4];
|
2018-12-10 09:46:46 +01:00
|
|
|
#endif
|
2018-12-10 17:25:29 +01:00
|
|
|
#endif
|
2018-12-15 19:03:11 +01:00
|
|
|
#endif
|
2018-12-10 09:46:46 +01:00
|
|
|
|
|
|
|
#ifdef _Sun
|
|
|
|
uniform vec3 sunDir;
|
|
|
|
uniform vec3 sunCol;
|
|
|
|
#ifdef _ShadowMap
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
#ifdef _ShadowMapAtlas
|
|
|
|
#ifndef _SingleAtlas
|
|
|
|
uniform sampler2DShadow shadowMapAtlasSun;
|
|
|
|
#endif
|
|
|
|
#else
|
2018-12-15 15:07:30 +01:00
|
|
|
uniform sampler2DShadow shadowMap;
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
#endif
|
2018-12-10 09:46:46 +01:00
|
|
|
uniform float shadowsBias;
|
|
|
|
#ifdef _CSM
|
|
|
|
//!uniform vec4 casData[shadowmapCascades * 4 + 4];
|
|
|
|
#else
|
|
|
|
uniform mat4 LWVP;
|
|
|
|
#endif
|
|
|
|
#endif // _ShadowMap
|
|
|
|
#endif
|
|
|
|
|
2018-12-10 17:25:29 +01:00
|
|
|
#ifdef _SinglePoint // Fast path for single light
|
|
|
|
uniform vec3 pointPos;
|
|
|
|
uniform vec3 pointCol;
|
|
|
|
uniform float pointBias;
|
|
|
|
#ifdef _Spot
|
|
|
|
uniform vec3 spotDir;
|
|
|
|
uniform vec2 spotData;
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
#include "std/light_mobile.glsl"
|
|
|
|
|
2018-12-10 09:46:46 +01:00
|
|
|
in vec2 texCoord;
|
|
|
|
in vec3 viewRay;
|
|
|
|
out vec4 fragColor;
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0); // Normal.xy, metallic/roughness, depth
|
2020-05-10 19:43:02 +02:00
|
|
|
|
2018-12-10 09:46:46 +01:00
|
|
|
vec3 n;
|
|
|
|
n.z = 1.0 - abs(g0.x) - abs(g0.y);
|
|
|
|
n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy);
|
|
|
|
n = normalize(n);
|
|
|
|
|
2019-07-07 22:02:07 +02:00
|
|
|
float roughness = g0.b;
|
|
|
|
float metallic;
|
|
|
|
uint matid;
|
2019-07-14 16:45:34 +02:00
|
|
|
unpackFloatInt16(g0.a, metallic, matid);
|
2019-07-07 22:02:07 +02:00
|
|
|
|
2018-12-10 09:46:46 +01:00
|
|
|
vec4 g1 = textureLod(gbuffer1, texCoord, 0.0); // Basecolor.rgb, spec/occ
|
|
|
|
vec2 occspec = unpackFloat2(g1.a);
|
2019-07-07 22:02:07 +02:00
|
|
|
vec3 albedo = surfaceAlbedo(g1.rgb, metallic); // g1.rgb - basecolor
|
|
|
|
vec3 f0 = surfaceF0(g1.rgb, metallic);
|
2018-12-10 09:46:46 +01:00
|
|
|
|
|
|
|
float depth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
|
|
|
|
vec3 p = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj);
|
|
|
|
vec3 v = normalize(eye - p);
|
|
|
|
float dotNV = max(dot(n, v), 0.0);
|
|
|
|
|
|
|
|
#ifdef _Brdf
|
2019-07-07 22:02:07 +02:00
|
|
|
vec2 envBRDF = textureLod(senvmapBrdf, vec2(roughness, 1.0 - dotNV), 0.0).xy;
|
2018-12-10 09:46:46 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// Envmap
|
|
|
|
#ifdef _Irr
|
2020-05-10 19:43:02 +02:00
|
|
|
vec3 envl = shIrradiance(n, shirr);
|
2018-12-10 09:46:46 +01:00
|
|
|
#ifdef _EnvTex
|
|
|
|
envl /= PI;
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
vec3 envl = vec3(1.0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef _Rad
|
|
|
|
vec3 reflectionWorld = reflect(-v, n);
|
2019-07-07 22:02:07 +02:00
|
|
|
float lod = getMipFromRoughness(roughness, envmapNumMipmaps);
|
2018-12-10 09:46:46 +01:00
|
|
|
vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef _EnvLDR
|
|
|
|
envl.rgb = pow(envl.rgb, vec3(2.2));
|
|
|
|
#ifdef _Rad
|
|
|
|
prefilteredColor = pow(prefilteredColor, vec3(2.2));
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
envl.rgb *= albedo;
|
2020-05-10 19:43:02 +02:00
|
|
|
|
2018-12-10 09:46:46 +01:00
|
|
|
#ifdef _Rad // Indirect specular
|
|
|
|
envl.rgb += prefilteredColor * (f0 * envBRDF.x + envBRDF.y) * 1.5 * occspec.y;
|
|
|
|
#else
|
|
|
|
#ifdef _EnvCol
|
2019-07-07 22:02:07 +02:00
|
|
|
envl.rgb += backgroundCol * surfaceF0(g1.rgb, metallic); // f0
|
2018-12-10 09:46:46 +01:00
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
envl.rgb *= envmapStrength * occspec.x;
|
|
|
|
fragColor.rgb = envl;
|
|
|
|
|
|
|
|
#ifdef _Sun
|
|
|
|
vec3 sh = normalize(v + sunDir);
|
|
|
|
float sdotNH = dot(n, sh);
|
|
|
|
float sdotVH = dot(v, sh);
|
|
|
|
float sdotNL = dot(n, sunDir);
|
|
|
|
float svisibility = 1.0;
|
2018-12-10 17:25:29 +01:00
|
|
|
vec3 sdirect = lambertDiffuseBRDF(albedo, sdotNL) +
|
2019-07-07 22:02:07 +02:00
|
|
|
specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * occspec.y;
|
2018-12-10 09:46:46 +01:00
|
|
|
|
|
|
|
#ifdef _ShadowMap
|
|
|
|
#ifdef _CSM
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
svisibility = shadowTestCascade(
|
|
|
|
#ifdef _ShadowMapAtlas
|
|
|
|
#ifndef _SingleAtlas
|
|
|
|
shadowMapAtlasSun
|
|
|
|
#else
|
|
|
|
shadowMapAtlas
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
shadowMap
|
|
|
|
#endif
|
|
|
|
, eye, p + n * shadowsBias * 10, shadowsBias
|
|
|
|
);
|
2018-12-10 09:46:46 +01:00
|
|
|
#else
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
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
|
|
|
|
);
|
2018-12-10 09:46:46 +01:00
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
fragColor.rgb += sdirect * svisibility * sunCol;
|
|
|
|
#endif
|
|
|
|
|
2018-12-10 17:25:29 +01:00
|
|
|
#ifdef _SinglePoint
|
|
|
|
fragColor.rgb += sampleLight(
|
2019-07-07 22:02:07 +02:00
|
|
|
p, n, v, dotNV, pointPos, pointCol, albedo, roughness, occspec.y, f0
|
2018-12-10 17:25:29 +01:00
|
|
|
#ifdef _ShadowMap
|
2020-07-13 23:28:43 +02:00
|
|
|
, 0, pointBias, true
|
2018-12-10 17:25:29 +01:00
|
|
|
#endif
|
|
|
|
#ifdef _Spot
|
|
|
|
, true, spotData.x, spotData.y, spotDir
|
|
|
|
#endif
|
|
|
|
);
|
|
|
|
#endif
|
|
|
|
|
2018-12-10 09:46:46 +01:00
|
|
|
#ifdef _Clusters
|
|
|
|
float viewz = linearize(depth * 0.5 + 0.5, cameraProj);
|
|
|
|
int clusterI = getClusterI(texCoord, viewz, cameraPlane);
|
|
|
|
int numLights = int(texelFetch(clustersData, ivec2(clusterI, 0), 0).r * 255);
|
|
|
|
|
|
|
|
#ifdef HLSL
|
|
|
|
viewz += textureLod(clustersData, vec2(0.0), 0.0).r * 1e-9; // TODO: krafix bug, needs to generate sampler
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef _Spot
|
|
|
|
int numSpots = int(texelFetch(clustersData, ivec2(clusterI, 1 + maxLightsCluster), 0).r * 255);
|
|
|
|
int numPoints = numLights - numSpots;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
for (int i = 0; i < min(numLights, maxLightsCluster); i++) {
|
|
|
|
int li = int(texelFetch(clustersData, ivec2(clusterI, i + 1), 0).r * 255);
|
2018-12-10 17:25:29 +01:00
|
|
|
fragColor.rgb += sampleLight(
|
|
|
|
p,
|
|
|
|
n,
|
|
|
|
v,
|
|
|
|
dotNV,
|
2021-03-14 14:35:25 +01:00
|
|
|
lightsArray[li * 3].xyz, // lp
|
|
|
|
lightsArray[li * 3 + 1].xyz, // lightCol
|
2018-12-10 17:25:29 +01:00
|
|
|
albedo,
|
2019-07-07 22:02:07 +02:00
|
|
|
roughness,
|
2018-12-10 17:25:29 +01:00
|
|
|
occspec.y,
|
|
|
|
f0
|
|
|
|
#ifdef _ShadowMap
|
2021-03-14 14:35:25 +01:00
|
|
|
// light index, shadow bias, cast_shadows
|
|
|
|
, li, lightsArray[li * 3 + 2].x, lightsArray[li * 3 + 2].z != 0.0
|
2018-12-10 09:46:46 +01:00
|
|
|
#endif
|
2018-12-10 17:25:29 +01:00
|
|
|
#ifdef _Spot
|
2021-03-14 14:35:25 +01:00
|
|
|
, lightsArray[li * 3 + 2].y != 0.0
|
|
|
|
, lightsArray[li * 3 + 2].y // cutoff
|
2018-12-10 17:25:29 +01:00
|
|
|
, lightsArraySpot[li].w // cutoff - exponent
|
2018-12-10 18:18:32 +01:00
|
|
|
, lightsArraySpot[li].xyz // spotDir
|
2018-12-10 17:25:29 +01:00
|
|
|
#endif
|
|
|
|
);
|
2018-12-10 09:46:46 +01:00
|
|
|
}
|
2018-12-10 17:25:29 +01:00
|
|
|
#endif // _Clusters
|
2018-12-10 09:46:46 +01:00
|
|
|
}
|