<!// ShadowCore.slh// libraries/render-utils/src//// Created by Olivier Prat on 11/13/17.// Copyright 2017 High Fidelity, Inc.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html!><@if not SHADOW_CORE_SLH@><@def SHADOW_CORE_SLH@> <@include Shadows_shared.slh@> UNIFORM_BUFFER(RENDER_UTILS_BUFFER_SHADOW_PARAMS, shadowTransformBuffer) { ShadowParameters shadow;}; int getShadowCascadeCount() { return shadow.cascadeCount;} float getShadowCascadeInvBlendWidth() { return shadow.invCascadeBlendWidth;} float evalShadowFalloff(float depth) { return clamp((shadow.maxDistance-depth) * shadow.invFalloffDistance, 0.0, 1.0);} mat4 getShadowReprojection(int cascadeIndex) { return shadow.cascades[cascadeIndex].reprojection;} float getShadowScale() { return shadow.invMapSize;} float getShadowFixedBias(int cascadeIndex) { return shadow.cascades[cascadeIndex].fixedBias;} float getShadowSlopeBias(int cascadeIndex) { return shadow.cascades[cascadeIndex].slopeBias;} // Compute the texture coordinates from world coordinatesvec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { vec4 shadowCoord = getShadowReprojection(cascadeIndex) * position; return vec4(shadowCoord.xyz, 1.0);} bool isShadowCascadeProjectedOnPixel(vec4 cascadeTexCoords) { bvec2 greaterThanZero = greaterThan(cascadeTexCoords.xy, vec2(0)); bvec2 lessThanOne = lessThan(cascadeTexCoords.xy, vec2(1)); return all(greaterThanZero) && all(lessThanOne);} int getFirstShadowCascadeOnPixel(int startCascadeIndex, vec4 worldPosition, out vec4 cascadeShadowCoords) { int cascadeIndex; startCascadeIndex = min(startCascadeIndex, getShadowCascadeCount()-1); for (cascadeIndex=startCascadeIndex ; cascadeIndex<getShadowCascadeCount() ; cascadeIndex++) { cascadeShadowCoords = evalShadowTexcoord(cascadeIndex, worldPosition); if (isShadowCascadeProjectedOnPixel(cascadeShadowCoords)) { return cascadeIndex; } } return cascadeIndex;} float evalShadowCascadeWeight(vec4 cascadeTexCoords) { // Inspired by DirectX CascadeShadowMaps11 example vec2 distanceToOne = vec2(1.0) - cascadeTexCoords.xy; float blend1 = min( cascadeTexCoords.x, cascadeTexCoords.y ); float blend2 = min( distanceToOne.x, distanceToOne.y ); float blend = min( blend1, blend2 ); return clamp(blend * getShadowCascadeInvBlendWidth(), 0.0, 1.0);} float evalCascadeMix(float firstCascadeWeight, float secondCascadeWeight) { return ((1.0-firstCascadeWeight) * secondCascadeWeight) / (firstCascadeWeight + secondCascadeWeight);} float determineShadowCascadesOnPixel(vec4 worldPosition, float viewDepth, out vec4 cascadeShadowCoords[2], out ivec2 cascadeIndices) { cascadeIndices.x = getFirstShadowCascadeOnPixel(0, worldPosition, cascadeShadowCoords[0]); cascadeIndices.y = cascadeIndices.x+1; float firstCascadeWeight = evalShadowCascadeWeight(cascadeShadowCoords[0]); if (firstCascadeWeight<1.0 && cascadeIndices.x < (getShadowCascadeCount()-1)) { cascadeIndices.y = getFirstShadowCascadeOnPixel(cascadeIndices.y, worldPosition, cascadeShadowCoords[1]); float secondCascadeWeight = evalShadowCascadeWeight(cascadeShadowCoords[1]); // Returns the mix amount between first and second cascade. return evalCascadeMix(firstCascadeWeight, secondCascadeWeight); } else { return 0.0; }} <@endif@>
<!
// ShadowCore.slh
// libraries/render-utils/src
//
// Created by Olivier Prat on 11/13/17.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
!>
<@if not SHADOW_CORE_SLH@>
<@def SHADOW_CORE_SLH@>
<@include Shadows_shared.slh@>
UNIFORM_BUFFER(RENDER_UTILS_BUFFER_SHADOW_PARAMS, shadowTransformBuffer) {
ShadowParameters shadow;
};
int getShadowCascadeCount() {
return shadow.cascadeCount;
}
float getShadowCascadeInvBlendWidth() {
return shadow.invCascadeBlendWidth;
}
float evalShadowFalloff(float depth) {
return clamp((shadow.maxDistance-depth) * shadow.invFalloffDistance, 0.0, 1.0);
}
mat4 getShadowReprojection(int cascadeIndex) {
return shadow.cascades[cascadeIndex].reprojection;
}
float getShadowScale() {
return shadow.invMapSize;
}
float getShadowFixedBias(int cascadeIndex) {
return shadow.cascades[cascadeIndex].fixedBias;
}
float getShadowSlopeBias(int cascadeIndex) {
return shadow.cascades[cascadeIndex].slopeBias;
}
// Compute the texture coordinates from world coordinates
vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) {
vec4 shadowCoord = getShadowReprojection(cascadeIndex) * position;
return vec4(shadowCoord.xyz, 1.0);
}
bool isShadowCascadeProjectedOnPixel(vec4 cascadeTexCoords) {
bvec2 greaterThanZero = greaterThan(cascadeTexCoords.xy, vec2(0));
bvec2 lessThanOne = lessThan(cascadeTexCoords.xy, vec2(1));
return all(greaterThanZero) && all(lessThanOne);
}
int getFirstShadowCascadeOnPixel(int startCascadeIndex, vec4 worldPosition, out vec4 cascadeShadowCoords) {
int cascadeIndex;
startCascadeIndex = min(startCascadeIndex, getShadowCascadeCount()-1);
for (cascadeIndex=startCascadeIndex ; cascadeIndex<getShadowCascadeCount() ; cascadeIndex++) {
cascadeShadowCoords = evalShadowTexcoord(cascadeIndex, worldPosition);
if (isShadowCascadeProjectedOnPixel(cascadeShadowCoords)) {
return cascadeIndex;
}
}
return cascadeIndex;
}
float evalShadowCascadeWeight(vec4 cascadeTexCoords) {
// Inspired by DirectX CascadeShadowMaps11 example
vec2 distanceToOne = vec2(1.0) - cascadeTexCoords.xy;
float blend1 = min( cascadeTexCoords.x, cascadeTexCoords.y );
float blend2 = min( distanceToOne.x, distanceToOne.y );
float blend = min( blend1, blend2 );
return clamp(blend * getShadowCascadeInvBlendWidth(), 0.0, 1.0);
}
float evalCascadeMix(float firstCascadeWeight, float secondCascadeWeight) {
return ((1.0-firstCascadeWeight) * secondCascadeWeight) / (firstCascadeWeight + secondCascadeWeight);
}
float determineShadowCascadesOnPixel(vec4 worldPosition, float viewDepth, out vec4 cascadeShadowCoords[2], out ivec2 cascadeIndices) {
cascadeIndices.x = getFirstShadowCascadeOnPixel(0, worldPosition, cascadeShadowCoords[0]);
cascadeIndices.y = cascadeIndices.x+1;
float firstCascadeWeight = evalShadowCascadeWeight(cascadeShadowCoords[0]);
if (firstCascadeWeight<1.0 && cascadeIndices.x < (getShadowCascadeCount()-1)) {
cascadeIndices.y = getFirstShadowCascadeOnPixel(cascadeIndices.y, worldPosition, cascadeShadowCoords[1]);
float secondCascadeWeight = evalShadowCascadeWeight(cascadeShadowCoords[1]);
// Returns the mix amount between first and second cascade.
return evalCascadeMix(firstCascadeWeight, secondCascadeWeight);
} else {
return 0.0;
}
}
<@endif@>