mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 05:23:09 +02:00
First step for HBAO. Not working of course
This commit is contained in:
parent
5dcb7f622a
commit
5e9355235c
3 changed files with 151 additions and 74 deletions
|
@ -11,11 +11,13 @@
|
||||||
<@if not SSAO_SLH@>
|
<@if not SSAO_SLH@>
|
||||||
<@def SSAO_SLH@>
|
<@def SSAO_SLH@>
|
||||||
|
|
||||||
|
#define SSAO_USE_HORIZON_BASED 1
|
||||||
|
|
||||||
<@func declarePackOcclusionDepth()@>
|
<@func declarePackOcclusionDepth()@>
|
||||||
|
|
||||||
const float FAR_PLANE_Z = -300.0;
|
const float FAR_PLANE_Z = -300.0;
|
||||||
|
|
||||||
float CSZToDephtKey(float z) {
|
float CSZToDepthKey(float z) {
|
||||||
return clamp(z * (1.0 / FAR_PLANE_Z), 0.0, 1.0);
|
return clamp(z * (1.0 / FAR_PLANE_Z), 0.0, 1.0);
|
||||||
}
|
}
|
||||||
vec3 packOcclusionDepth(float occlusion, float depth) {
|
vec3 packOcclusionDepth(float occlusion, float depth) {
|
||||||
|
@ -156,24 +158,25 @@ float evalDiskRadius(float Zeye, vec2 imageSize) {
|
||||||
return ssDiskRadius;
|
return ssDiskRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float TWO_PI = 6.28;
|
const float PI = 3.1415926;
|
||||||
|
const float TWO_PI = 6.2831852;
|
||||||
|
|
||||||
vec3 getUnitTapLocation(int sampleNumber, float spinAngle){
|
vec3 getUnitTapLocation(int sampleNumber, float spiralTurns, float spinAngle, float angleRange){
|
||||||
// Radius relative to ssR
|
// Radius relative to ssR
|
||||||
float alpha = float(sampleNumber + 0.5) * getInvNumSamples();
|
float alpha = float(sampleNumber + 0.5) * getInvNumSamples();
|
||||||
float angle = alpha * (getNumSpiralTurns() * TWO_PI) + spinAngle;
|
float angle = alpha * (spiralTurns * angleRange) + spinAngle;
|
||||||
return vec3(cos(angle), sin(angle), alpha);
|
return vec3(cos(angle), sin(angle), alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 getTapLocation(int sampleNumber, float spinAngle, float outerRadius) {
|
vec3 getTapLocationSSAO(int sampleNumber, float spinAngle, float outerRadius) {
|
||||||
vec3 tap = getUnitTapLocation(sampleNumber, spinAngle);
|
vec3 tap = getUnitTapLocation(sampleNumber, getNumSpiralTurns(), spinAngle, TWO_PI);
|
||||||
tap.xy *= tap.z;
|
tap.xy *= tap.z;
|
||||||
tap *= outerRadius;
|
tap *= outerRadius;
|
||||||
return tap;
|
return tap;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 getTapLocationClamped(int sampleNumber, float spinAngle, float outerRadius, vec2 pixelPos, vec2 imageSize) {
|
vec3 getTapLocationClampedSSAO(int sampleNumber, float spinAngle, float outerRadius, vec2 pixelPos, vec2 imageSize) {
|
||||||
vec3 tap = getTapLocation(sampleNumber, spinAngle, outerRadius);
|
vec3 tap = getTapLocationSSAO(sampleNumber, spinAngle, outerRadius);
|
||||||
vec2 tapPos = pixelPos + tap.xy;
|
vec2 tapPos = pixelPos + tap.xy;
|
||||||
|
|
||||||
if (!(isBorderingEnabled() > 0.0)) {
|
if (!(isBorderingEnabled() > 0.0)) {
|
||||||
|
@ -196,24 +199,7 @@ vec3 getTapLocationClamped(int sampleNumber, float spinAngle, float outerRadius,
|
||||||
tapPos.y -= (imageSize.y - tapPos.y);
|
tapPos.y -= (imageSize.y - tapPos.y);
|
||||||
redoTap = true;
|
redoTap = true;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
if ((tapPos.x < 0.5)) {
|
|
||||||
tapPos.x = 0.5;
|
|
||||||
redoTap = true;
|
|
||||||
} else if ((tapPos.x > imageSize.x - 0.5)) {
|
|
||||||
tapPos.x = imageSize.x - 0.5;
|
|
||||||
redoTap = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((tapPos.y < 0.5)) {
|
|
||||||
tapPos.y = 0.5;
|
|
||||||
redoTap = true;
|
|
||||||
} else if ((tapPos.y > imageSize.y - 0.5)) {
|
|
||||||
tapPos.y = imageSize.y - 0.5;
|
|
||||||
redoTap = true;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (redoTap) {
|
if (redoTap) {
|
||||||
tap.xy = tapPos - pixelPos;
|
tap.xy = tapPos - pixelPos;
|
||||||
tap.z = length(tap.xy);
|
tap.z = length(tap.xy);
|
||||||
|
@ -228,14 +214,17 @@ vec3 getTapLocationClamped(int sampleNumber, float spinAngle, float outerRadius,
|
||||||
|
|
||||||
<@func declareFetchDepthPyramidMap()@>
|
<@func declareFetchDepthPyramidMap()@>
|
||||||
|
|
||||||
|
|
||||||
// the depth pyramid texture
|
// the depth pyramid texture
|
||||||
uniform sampler2D pyramidMap;
|
uniform sampler2D pyramidMap;
|
||||||
|
|
||||||
float getZEye(ivec2 pixel, int level) {
|
float getZEyeAtPixel(ivec2 pixel, int level) {
|
||||||
return -texelFetch(pyramidMap, pixel, level).x;
|
return -texelFetch(pyramidMap, pixel, level).x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float getZEyeAtUV(vec2 texCoord, int level) {
|
||||||
|
return -texture(pyramidMap, texCoord, level).x;
|
||||||
|
}
|
||||||
|
|
||||||
const int LOG_MAX_OFFSET = 3;
|
const int LOG_MAX_OFFSET = 3;
|
||||||
const int MAX_MIP_LEVEL = 5;
|
const int MAX_MIP_LEVEL = 5;
|
||||||
int evalMipFromRadius(float radius) {
|
int evalMipFromRadius(float radius) {
|
||||||
|
@ -243,7 +232,6 @@ int evalMipFromRadius(float radius) {
|
||||||
return clamp(findMSB(int(radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL);
|
return clamp(findMSB(int(radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
vec3 fetchTapUnfiltered(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) {
|
vec3 fetchTapUnfiltered(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) {
|
||||||
ivec2 ssP = ivec2(tap.xy) + ssC;
|
ivec2 ssP = ivec2(tap.xy) + ssC;
|
||||||
ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y);
|
ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y);
|
||||||
|
@ -292,10 +280,18 @@ vec3 fetchTap(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) {
|
||||||
|
|
||||||
<@func declareEvalObscurance()@>
|
<@func declareEvalObscurance()@>
|
||||||
|
|
||||||
float evalAO(in vec3 C, in vec3 n_C, in vec3 Q) {
|
vec3 fastAcos(vec3 x) {
|
||||||
vec3 v = Q - C;
|
// [Eberly2014] GPGPU Programming for Games and Science
|
||||||
|
vec3 absX = abs(x);
|
||||||
|
vec3 res = absX * (-0.156583) + vec3(PI / 2.0);
|
||||||
|
res *= sqrt(vec3(1.0) - absX);
|
||||||
|
return mix(res, vec3(PI) - res, greaterThanEqual(x, vec3(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 tapPosition) {
|
||||||
|
vec3 v = tapPosition - centerPosition;
|
||||||
float vv = dot(v, v);
|
float vv = dot(v, v);
|
||||||
float vn = dot(v, n_C);
|
float vn = dot(v, centerNormal);
|
||||||
|
|
||||||
// Fall off function as recommended in SAO paper
|
// Fall off function as recommended in SAO paper
|
||||||
const float epsilon = 0.01;
|
const float epsilon = 0.01;
|
||||||
|
@ -303,6 +299,95 @@ float evalAO(in vec3 C, in vec3 n_C, in vec3 Q) {
|
||||||
return f * f * f * max((vn - getFalloffBias()) / (epsilon + vv), 0.0);
|
return f * f * f * max((vn - getFalloffBias()) / (epsilon + vv), 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec2 searchHorizons(int side, float centerDepth, vec2 centerPixelUV, vec2 imageSize, vec2 deltaTap, float ssDiskRadius, vec3 fragNormalES) {
|
||||||
|
vec2 searchVec = deltaTap * ssDiskRadius;
|
||||||
|
vec2 absSearchVec = abs(searchVec);
|
||||||
|
vec2 horizons = vec2(-PI/2.0, -PI/2.0);
|
||||||
|
vec3 centerPoint = vec3(centerPixelUV, centerDepth);
|
||||||
|
int stepIndex;
|
||||||
|
vec2 tapPixelUV;
|
||||||
|
|
||||||
|
if (absSearchVec.x > absSearchVec.y) {
|
||||||
|
int stepCount = int(ceil(absSearchVec.x));
|
||||||
|
|
||||||
|
// Positive search for h2
|
||||||
|
tapPixelUV = centerPixelUV;
|
||||||
|
deltaTap = (searchVec / searchVec.x) / imageSize;
|
||||||
|
stepIndex = stepCount;
|
||||||
|
while (stepIndex>0 && tapPixelUV.x < 1.0) {
|
||||||
|
tapPixelUV += deltaTap;
|
||||||
|
// Don't clamp for the moment
|
||||||
|
|
||||||
|
float tapDepth = getZEyeAtUV(tapPixelUV, 0);
|
||||||
|
vec3 deltaVec = vec3(tapPixelUV, tapDepth) - centerPoint;
|
||||||
|
|
||||||
|
horizons.y = max(horizons.y, normalize(deltaVec).z);
|
||||||
|
--stepIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negative search for h1
|
||||||
|
tapPixelUV = centerPixelUV;
|
||||||
|
stepIndex = stepCount;
|
||||||
|
while (stepIndex>0 && tapPixelUV.x > 0.0) {
|
||||||
|
tapPixelUV -= deltaTap;
|
||||||
|
// Don't clamp for the moment
|
||||||
|
|
||||||
|
float tapDepth = getZEyeAtUV(tapPixelUV, 0);
|
||||||
|
vec3 deltaVec = vec3(tapPixelUV, tapDepth) - centerPoint;
|
||||||
|
|
||||||
|
horizons.x = max(horizons.x, normalize(deltaVec).z);
|
||||||
|
--stepIndex;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int stepCount = int(ceil(absSearchVec.y));
|
||||||
|
|
||||||
|
// Positive search for h2
|
||||||
|
tapPixelUV = centerPixelUV;
|
||||||
|
deltaTap = (searchVec / searchVec.y) / imageSize;
|
||||||
|
stepIndex = stepCount;
|
||||||
|
while (stepIndex>0 && tapPixelUV.y < 1.0) {
|
||||||
|
tapPixelUV += deltaTap;
|
||||||
|
// Don't clamp for the moment
|
||||||
|
|
||||||
|
float tapDepth = getZEyeAtUV(tapPixelUV, 0);
|
||||||
|
vec3 deltaVec = vec3(tapPixelUV, tapDepth) - centerPoint;
|
||||||
|
|
||||||
|
horizons.y = max(horizons.y, normalize(deltaVec).z);
|
||||||
|
--stepIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negative search for h1
|
||||||
|
tapPixelUV = centerPixelUV;
|
||||||
|
stepIndex = stepCount;
|
||||||
|
while (stepIndex>0 && tapPixelUV.y > 0.0) {
|
||||||
|
tapPixelUV -= deltaTap;
|
||||||
|
// Don't clamp for the moment
|
||||||
|
|
||||||
|
float tapDepth = getZEyeAtUV(tapPixelUV, 0);
|
||||||
|
vec3 deltaVec = vec3(tapPixelUV, tapDepth) - centerPoint;
|
||||||
|
|
||||||
|
horizons.x = max(horizons.x, normalize(deltaVec).z);
|
||||||
|
--stepIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 angles = acos(vec3(horizons, fragNormalES.z))-PI/2.0;
|
||||||
|
angles.x = -angles.x;
|
||||||
|
// Clamp to limit horizon defined by normal plane
|
||||||
|
horizons.xy = angles.zz + max(angles.xy - angles.zz, vec2(-PI/2.0, PI/2.0));
|
||||||
|
return horizons;
|
||||||
|
}
|
||||||
|
|
||||||
|
float integrateArc(float h1, float h2) {
|
||||||
|
vec2 cosh = cos(vec2(h1, h2));
|
||||||
|
return 2.0 - cosh.x - cosh.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
float evalVisibilityHBAO(int side, float centerDepth, vec2 centerPixelUV, vec2 imageSize, vec2 deltaTap, float ssDiskRadius, vec3 fragNormalES) {
|
||||||
|
vec2 horizonAngles = searchHorizons(side, centerDepth, centerPixelUV, imageSize, deltaTap, ssDiskRadius, fragNormalES);
|
||||||
|
return integrateArc(horizonAngles.x, horizonAngles.y);
|
||||||
|
}
|
||||||
|
|
||||||
<@endfunc@>
|
<@endfunc@>
|
||||||
|
|
||||||
<@func declareBlurPass(axis)@>
|
<@func declareBlurPass(axis)@>
|
||||||
|
|
|
@ -50,7 +50,7 @@ void main(void) {
|
||||||
ivec2 ssC = ivec2(cursorPixelPos);
|
ivec2 ssC = ivec2(cursorPixelPos);
|
||||||
|
|
||||||
// Fetch the z under the pixel (stereo or not)
|
// Fetch the z under the pixel (stereo or not)
|
||||||
float Zeye = getZEye(ssC, 0);
|
float Zeye = getZEyeAtPixel(ssC, 0);
|
||||||
|
|
||||||
// Stereo side info
|
// Stereo side info
|
||||||
ivec4 side = getStereoSideInfo(ssC.x, getResolutionLevel());
|
ivec4 side = getStereoSideInfo(ssC.x, getResolutionLevel());
|
||||||
|
@ -84,7 +84,7 @@ void main(void) {
|
||||||
bool keep = false;
|
bool keep = false;
|
||||||
|
|
||||||
for (int i = 0; i < getNumSamples(); ++i) {
|
for (int i = 0; i < getNumSamples(); ++i) {
|
||||||
vec3 tap = getTapLocationClamped(i, randomPatternRotationAngle, ssDiskRadius, cursorPixelPos, imageSize);
|
vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, ssDiskRadius, cursorPixelPos, imageSize);
|
||||||
|
|
||||||
// The occluding point in camera space
|
// The occluding point in camera space
|
||||||
vec2 fragToTap = vec2(ssC) + tap.xy - fragCoord.xy;
|
vec2 fragToTap = vec2(ssC) + tap.xy - fragCoord.xy;
|
||||||
|
@ -97,7 +97,7 @@ void main(void) {
|
||||||
|
|
||||||
vec3 Q = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy);
|
vec3 Q = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy);
|
||||||
|
|
||||||
sum += float(tap.z > 0.0) * evalAO(Cp, Cn, Q);
|
sum += float(tap.z > 0.0) * evalVisibilitySSAO(Cp, Cn, Q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ void main(void) {
|
||||||
}
|
}
|
||||||
!>
|
!>
|
||||||
|
|
||||||
outFragColor = vec4(packOcclusionDepth(A, CSZToDephtKey(Cp.z)), 1.0);
|
outFragColor = vec4(packOcclusionDepth(A, CSZToDepthKey(Cp.z)), 1.0);
|
||||||
|
|
||||||
if ((dot(fragToCursor,fragToCursor) < (100.0 * keepTapRadius * keepTapRadius) )) {
|
if ((dot(fragToCursor,fragToCursor) < (100.0 * keepTapRadius * keepTapRadius) )) {
|
||||||
// outFragColor = vec4(vec3(A), 1.0);
|
// outFragColor = vec4(vec3(A), 1.0);
|
||||||
|
|
|
@ -24,63 +24,55 @@ void main(void) {
|
||||||
|
|
||||||
// Pixel being shaded
|
// Pixel being shaded
|
||||||
vec2 fragCoord = gl_FragCoord.xy;
|
vec2 fragCoord = gl_FragCoord.xy;
|
||||||
ivec2 ssC = ivec2(fragCoord.xy);
|
ivec2 centerPixelPos = ivec2(fragCoord.xy);
|
||||||
|
|
||||||
// Fetch the z under the pixel (stereo or not)
|
// Fetch the z under the pixel (stereo or not)
|
||||||
float Zeye = getZEye(ssC, 0);
|
float Zeye = getZEyeAtPixel(centerPixelPos, 0);
|
||||||
|
|
||||||
// Stereo side info
|
// Stereo side info
|
||||||
ivec4 side = getStereoSideInfo(ssC.x, getResolutionLevel());
|
ivec4 side = getStereoSideInfo(centerPixelPos.x, getResolutionLevel());
|
||||||
|
|
||||||
// From now on, ssC is the pixel pos in the side
|
// From now on, centerPixelPos is the pixel pos in the side
|
||||||
ssC.x -= side.y;
|
centerPixelPos.x -= side.y;
|
||||||
vec2 fragPos = (vec2(ssC) + vec2(0.5)) / imageSize;
|
vec2 fragUVPos = (vec2(centerPixelPos) + vec2(0.5)) / imageSize;
|
||||||
|
|
||||||
// The position and normal of the pixel fragment in Eye space
|
// The position and normal of the pixel fragment in Eye space
|
||||||
vec3 Cp = evalEyePositionFromZeye(side.x, Zeye, fragPos);
|
vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos);
|
||||||
vec3 Cn = evalEyeNormal(Cp);
|
vec3 fragNormalES = evalEyeNormal(fragPositionES);
|
||||||
|
|
||||||
// Choose the screen-space sample radius
|
// Choose the screen-space sample radius
|
||||||
float ssDiskRadius = evalDiskRadius(Cp.z, imageSize);
|
float ssDiskRadius = evalDiskRadius(fragPositionES.z, imageSize);
|
||||||
|
#if SSAO_USE_HORIZON_BASED
|
||||||
|
ssDiskRadius = min(ssDiskRadius, 3.0);
|
||||||
|
#endif
|
||||||
// Let's make noise
|
// Let's make noise
|
||||||
float randomPatternRotationAngle = getAngleDithering(ssC);
|
float randomPatternRotationAngle = getAngleDithering(centerPixelPos);
|
||||||
//vec3 wCp = (getViewInverse() * vec4(Cp, 1.0)).xyz;
|
|
||||||
//float randomPatternRotationAngle = getAngleDitheringWorldPos(wCp);
|
|
||||||
|
|
||||||
// Accumulate the Obscurance for each samples
|
// Accumulate the visibility for each samples
|
||||||
float sum = 0.0;
|
float visibilitySum = 0.0;
|
||||||
for (int i = 0; i < getNumSamples(); ++i) {
|
for (int i = 0; i < getNumSamples(); ++i) {
|
||||||
vec3 tap = getTapLocationClamped(i, randomPatternRotationAngle, ssDiskRadius, ssC, imageSize);
|
#if SSAO_USE_HORIZON_BASED
|
||||||
|
vec3 deltaTap = getUnitTapLocation(i, 1, randomPatternRotationAngle, PI);
|
||||||
vec3 tapUVZ = fetchTap(side, ssC, tap, imageSize);
|
visibilitySum += evalVisibilityHBAO(side.x, Zeye, fragUVPos, imageSize, deltaTap.xy, ssDiskRadius, fragNormalES);
|
||||||
|
#else
|
||||||
vec3 Q = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy);
|
vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, ssDiskRadius, centerPixelPos, imageSize);
|
||||||
|
vec3 tapUVZ = fetchTap(side, centerPixelPos, tap, imageSize);
|
||||||
sum += float(tap.z > 0.0) * evalAO(Cp, Cn, Q);
|
vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy);
|
||||||
|
visibilitySum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
float A = max(0.0, 1.0 - sum * getObscuranceScaling() * 5.0 * getInvNumSamples());
|
float occlusion = max(0.0, 1.0 - visibilitySum * getObscuranceScaling() * 5.0 * getInvNumSamples());
|
||||||
|
|
||||||
// KEEP IT for Debugging
|
// KEEP IT for Debugging
|
||||||
// Bilateral box-filter over a quad for free, respecting depth edges
|
// Bilateral box-filter over a quad for free, respecting depth edges
|
||||||
// (the difference that this makes is subtle)
|
// (the difference that this makes is subtle)
|
||||||
if (abs(dFdx(Cp.z)) < 0.02) {
|
/* if (abs(dFdx(fragPositionES.z)) < 0.02) {
|
||||||
A -= dFdx(A) * ((ssC.x & 1) - 0.5);
|
occlusion -= dFdx(occlusion) * ((centerPixelPos.x & 1) - 0.5);
|
||||||
}
|
}
|
||||||
if (abs(dFdy(Cp.z)) < 0.02) {
|
if (abs(dFdy(fragPositionES.z)) < 0.02) {
|
||||||
A -= dFdy(A) * ((ssC.y & 1) - 0.5);
|
occlusion -= dFdy(occlusion) * ((centerPixelPos.y & 1) - 0.5);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
outFragColor = vec4(packOcclusionDepth(A, CSZToDephtKey(Cp.z)), 1.0);
|
|
||||||
|
|
||||||
/* {
|
|
||||||
vec3 tap = getTapLocationClamped(2, randomPatternRotationAngle, ssDiskRadius, ssC, imageSize);
|
|
||||||
vec3 tapUVZ = fetchTap(side, ssC, tap, imageSize);
|
|
||||||
vec2 fetchUV = vec2(tapUVZ.x + side.w * 0.5 * (side.x - tapUVZ.x), tapUVZ.y);
|
|
||||||
vec3 Q = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy);
|
|
||||||
outFragColor = vec4(fetchUV, 0.0, 1.0);
|
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
outFragColor = vec4(packOcclusionDepth(occlusion, CSZToDepthKey(fragPositionES.z)), 1.0);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue