diff --git a/examples/custompass_ssr.html b/examples/custompass_ssr.html
index 6562a16..aee7cbf 100644
--- a/examples/custompass_ssr.html
+++ b/examples/custompass_ssr.html
@@ -127,8 +127,6 @@
const shadowMapPass = new t3d.ShadowMapPass();
const ssrPass = new t3d.ShaderPostPass(SSRShader);
- ssrPass.uniforms.maxRayDistance = 10;
- ssrPass.uniforms.pixelStrideZCutoff = 50;
const tempRenderTarget = new t3d.RenderTarget2D(width, height);
tempRenderTarget.texture.minFilter = t3d.TEXTURE_FILTER.LINEAR;
@@ -158,6 +156,7 @@
ssrPass.uniforms["gBufferTexture2"] = gBuffer.getDepthTexture();
ssrPass.uniforms["viewportSize"][0] = width;
ssrPass.uniforms["viewportSize"][1] = height;
+ ssrPass.uniforms["nearZ"] = 1;
const blurPass = new t3d.ShaderPostPass(BlurShader);
blurPass.uniforms["projection"] = projection.elements;
@@ -178,7 +177,6 @@
const blendPass = new t3d.ShaderPostPass(BlendShader);
blendPass.uniforms["tDiffuse1"] = tempRenderTarget.texture;
blendPass.uniforms["tDiffuse2"] = tempRenderTarget3.texture;
- blendPass.uniforms["opacity2"] = 1;
const gui = new GUI();
gui.add(material, 'roughness', 0, 1, 0.01);
diff --git a/examples/jsm/shaders/SSRShader.js b/examples/jsm/shaders/SSRShader.js
index ddae1d1..ffe8409 100644
--- a/examples/jsm/shaders/SSRShader.js
+++ b/examples/jsm/shaders/SSRShader.js
@@ -3,119 +3,108 @@
* https://github.com/pissang/claygl/blob/master/example/shader/ssr.glsl
* http://casual-effects.blogspot.jp/2014/08/screen-space-ray-tracing.html
*/
-
-
-
-var SSRShader = {
-
+const SSRShader = {
+ name: 'effect_ssr',
defines: {
-
+ MAX_ITERATION: 50,
+ MAX_BINARY_SEARCH_ITERATION: 5
},
-
uniforms: {
- 'colorTex': null,
- 'gBufferTexture1': null,
- 'gBufferTexture2': null,
+ colorTex: null,
+ gBufferTexture1: null,
+ gBufferTexture2: null,
- 'projection': new Float32Array(16),
- 'projectionInv': new Float32Array(16),
- 'viewInverseTranspose': new Float32Array(16),
+ projection: new Float32Array(16),
+ projectionInv: new Float32Array(16),
+ viewInverseTranspose: new Float32Array(16),
- 'maxRayDistance': 4,
+ pixelStride: 8,
+ maxRayDistance: 200,
- 'pixelStride': 16,
- 'pixelStrideZCutoff': 10,
+ enablePixelStrideZCutoff: 1.0,
+ pixelStrideZCutoff: 50,
- 'screenEdgeFadeStart': 0.9,
+ screenEdgeFadeStart: 0.9,
- 'eyeFadeStart': 0.4,
- 'eyeFadeEnd': 0.8,
+ eyeFadeStart: 0.99,
+ eyeFadeEnd: 1,
- 'minGlossiness': 0.2,
- 'zThicknessThreshold': 0.1,
- 'jitterOffset': 0,
-
- 'nearZ': 0,
- 'viewportSize': [512, 512],
-
- 'maxMipmapLevel': 5,
+ minGlossiness: 0.2,
+ nearZ: 0.1,
+ zThicknessThreshold: 0.1,
+ jitterOffset: 0,
+ viewportSize: [512, 512]
},
+ vertexShader: `
+ attribute vec3 a_Position;
+ attribute vec2 a_Uv;
- vertexShader: [
- "attribute vec3 a_Position;",
- "attribute vec2 a_Uv;",
-
- "uniform mat4 u_ProjectionView;",
- "uniform mat4 u_Model;",
-
- "varying vec2 v_Uv;",
-
- "void main() {",
-
- " v_Uv = a_Uv;",
- " gl_Position = u_ProjectionView * u_Model * vec4( a_Position, 1.0 );",
+ uniform mat4 u_ProjectionView;
+ uniform mat4 u_Model;
- "}"
- ].join("\n"),
+ varying vec2 v_Uv;
- fragmentShader: [
- "#define MAX_ITERATION 20;",
- "#define MAX_BINARY_SEARCH_ITERATION 5;",
+ void main() {
+ v_Uv = a_Uv;
+ gl_Position = u_ProjectionView * u_Model * vec4(a_Position, 1.0);
+ }
+ `,
+ fragmentShader: `
+ varying vec2 v_Uv;
- "uniform sampler2D colorTex;",
- "uniform sampler2D gBufferTexture1;",
- "uniform sampler2D gBufferTexture2;",
+ uniform sampler2D colorTex;
+ uniform sampler2D gBufferTexture1;
+ uniform sampler2D gBufferTexture2;
- "uniform mat4 projection;",
- "uniform mat4 projectionInv;",
- "uniform mat4 viewInverseTranspose;",
+ uniform mat4 projection;
+ uniform mat4 projectionInv;
+ uniform mat4 viewInverseTranspose;
+ uniform float maxRayDistance;
- "uniform float maxRayDistance;",
-
- 'uniform float pixelStride;',
+ uniform float pixelStride;
// ray origin Z at this distance will have a pixel stride of 1.0
- 'uniform float pixelStrideZCutoff;',
+ uniform float pixelStrideZCutoff;
// distance to screen edge that ray hits will start to fade (0.0 -> 1.0)
- 'uniform float screenEdgeFadeStart;',
+ uniform float screenEdgeFadeStart;
// ray direction's Z that ray hits will start to fade (0.0 -> 1.0)
- 'uniform float eyeFadeStart;',
+ uniform float eyeFadeStart;
// ray direction's Z that ray hits will be cut (0.0 -> 1.0)
- 'uniform float eyeFadeEnd;',
+ uniform float eyeFadeEnd;
// Object larger than minGlossiness will have ssr effect
- 'uniform float minGlossiness;',
- 'uniform float zThicknessThreshold;',
- 'uniform float jitterOffset;',
+ uniform float minGlossiness;
+ uniform float zThicknessThreshold;
+ uniform float jitterOffset;
- 'uniform float nearZ;',
- 'uniform vec2 viewportSize;',
+ uniform float nearZ;
+ uniform vec2 viewportSize;
- 'uniform float maxMipmapLevel;',
+ uniform float maxMipmapLevel;
- "varying vec2 v_Uv;",
+ uniform float enablePixelStrideZCutoff;
- 'float fetchDepth(sampler2D depthTexture, vec2 uv) {',
- ' vec4 depthTexel = texture2D(depthTexture, uv);',
- ' return depthTexel.r * 2.0 - 1.0;',
- '}',
+ float fetchDepth(sampler2D depthTexture, vec2 uv) {
+ vec4 depthTexel = texture2D(depthTexture, uv);
+ return depthTexel.r * 2.0 - 1.0;
+ }
- 'float linearDepth(float depth) {',
- ' return projection[3][2] / (depth * projection[2][3] - projection[2][2]);',
- '}',
+ float linearDepth(float depth) {
+ return projection[3][2] / (depth * projection[2][3] - projection[2][2]);
+ }
- 'bool rayIntersectDepth(float rayZNear, float rayZFar, vec2 hitPixel) {',
- // Swap if bigger
- ' if (rayZFar > rayZNear) {',
- ' float t = rayZFar; rayZFar = rayZNear; rayZNear = t;',
- ' }',
- ' float cameraZ = linearDepth(fetchDepth(gBufferTexture2, hitPixel));',
- // float cameraBackZ = linearDepth(fetchDepth(backDepthTex, hitPixel));
- // Cross z
- ' return rayZFar <= cameraZ && rayZNear >= cameraZ - zThicknessThreshold;',
- '}',
+ bool rayIntersectDepth(float rayZNear, float rayZFar, vec2 hitPixel) {
+ // Swap if bigger
+ if (rayZFar > rayZNear) {
+ float t = rayZFar; rayZFar = rayZNear; rayZNear = t;
+ }
+ float cameraZ = linearDepth(fetchDepth(gBufferTexture2, hitPixel));
+ // float cameraBackZ = linearDepth(fetchDepth(backDepthTex, hitPixel));
+ // Cross z
+ return rayZFar <= cameraZ && rayZNear >= cameraZ - zThicknessThreshold;
+ }
// Trace a ray in screenspace from rayOrigin (in camera space) pointing in rayDir (in camera space)
//
@@ -127,217 +116,211 @@ var SSRShader = {
//
// Based on Morgan McGuire & Mike Mara's GLSL implementation:
// http://casual-effects.blogspot.com/2014/08/screen-space-ray-tracing.html
- 'bool traceScreenSpaceRay(vec3 rayOrigin, vec3 rayDir, float jitter, out vec2 hitPixel, out vec3 hitPoint, out float iterationCount) {',
- // Clip to the near plane
- ' float rayLength = ((rayOrigin.z + rayDir.z * maxRayDistance) > -nearZ) ? (-nearZ - rayOrigin.z) / rayDir.z : maxRayDistance;',
-
- ' vec3 rayEnd = rayOrigin + rayDir * rayLength;',
-
- // Project into homogeneous clip space
- ' vec4 H0 = projection * vec4(rayOrigin, 1.0);',
- ' vec4 H1 = projection * vec4(rayEnd, 1.0);',
-
- ' float k0 = 1.0 / H0.w, k1 = 1.0 / H1.w;',
-
- // The interpolated homogeneous version of the camera space points
- ' vec3 Q0 = rayOrigin * k0, Q1 = rayEnd * k1;',
-
- // Screen space endpoints
- // PENDING viewportSize ?
- ' vec2 P0 = (H0.xy * k0 * 0.5 + 0.5) * viewportSize;',
- ' vec2 P1 = (H1.xy * k1 * 0.5 + 0.5) * viewportSize;',
-
- // If the line is degenerate, make it cover at least one pixel to avoid handling
- // zero-pixel extent as a special case later
- ' P1 += dot(P1 - P0, P1 - P0) < 0.0001 ? 0.01 : 0.0;',
- ' vec2 delta = P1 - P0;',
-
- // Permute so that the primary iteration is in x to collapse
- // all quadrant-specific DDA case later
- ' bool permute = false;',
- ' if (abs(delta.x) < abs(delta.y)) {',
- // More vertical line
- ' permute = true;',
- ' delta = delta.yx;',
- ' P0 = P0.yx;',
- ' P1 = P1.yx;',
- ' }',
- ' float stepDir = sign(delta.x);',
- ' float invdx = stepDir / delta.x;',
-
- // Track the derivatives of Q and K
- ' vec3 dQ = (Q1 - Q0) * invdx;',
- ' float dk = (k1 - k0) * invdx;',
-
- ' vec2 dP = vec2(stepDir, delta.y * invdx);',
-
- // Calculate pixel stride based on distance of ray origin from camera.
- // Since perspective means distant objects will be smaller in screen space
- // we can use this to have higher quality reflections for far away objects
- // while still using a large pixel stride for near objects (and increase performance)
- // this also helps mitigate artifacts on distant reflections when we use a large
- // pixel stride.
- ' float strideScaler = 1.0 - min(1.0, -rayOrigin.z / pixelStrideZCutoff);',
- ' float pixStride = 1.0 + strideScaler * pixelStride;',
-
- // Scale derivatives by the desired pixel stride and the offset the starting values by the jitter fraction
- ' dP *= pixStride; dQ *= pixStride; dk *= pixStride;',
-
- // Track ray step and derivatives in a vec4 to parallelize
- ' vec4 pqk = vec4(P0, Q0.z, k0);',
- ' vec4 dPQK = vec4(dP, dQ.z, dk);',
-
- ' pqk += dPQK * jitter;',
- ' float rayZFar = (dPQK.z * 0.5 + pqk.z) / (dPQK.w * 0.5 + pqk.w);',
- ' float rayZNear;',
-
- ' bool intersect = false;',
-
- ' vec2 texelSize = 1.0 / viewportSize;',
-
- ' iterationCount = 0.0;',
-
- ' for (int i = 0; i < 20; i++) {',
- ' pqk += dPQK;',
-
- ' rayZNear = rayZFar;',
- ' rayZFar = (dPQK.z * 0.5 + pqk.z) / (dPQK.w * 0.5 + pqk.w);',
-
- ' hitPixel = permute ? pqk.yx : pqk.xy;',
- ' hitPixel *= texelSize;',
-
- ' intersect = rayIntersectDepth(rayZNear, rayZFar, hitPixel);',
-
- ' iterationCount += 1.0;',
-
- // PENDING Right on all platforms?
- ' if (intersect) {',
- ' break;',
- ' }',
- ' }',
-
- // Binary search refinement
- // FIXME If intersect in first iteration binary search may easily lead to the pixel of reflect object it self
- ' if (pixStride > 1.0 && intersect && iterationCount > 1.0) {',
- // Roll back
- ' pqk -= dPQK;',
- ' dPQK /= pixStride;',
-
- ' float originalStride = pixStride * 0.5;',
- ' float stride = originalStride;',
-
- ' rayZNear = pqk.z / pqk.w;',
- ' rayZFar = rayZNear;',
-
- ' for (int j = 0; j < 5; j++) {',
- ' pqk += dPQK * stride;',
- ' rayZNear = rayZFar;',
- ' rayZFar = (dPQK.z * -0.5 + pqk.z) / (dPQK.w * -0.5 + pqk.w);',
- ' hitPixel = permute ? pqk.yx : pqk.xy;',
- ' hitPixel *= texelSize;',
-
- ' originalStride *= 0.5;',
- ' stride = rayIntersectDepth(rayZNear, rayZFar, hitPixel) ? -originalStride : originalStride;',
- ' }',
- ' }',
-
- ' Q0.xy += dQ.xy * iterationCount;',
- ' Q0.z = pqk.z;',
- ' hitPoint = Q0 / pqk.w;',
-
- ' return intersect;',
- '}',
-
- 'float calculateAlpha(float iterationCount, float reflectivity, vec2 hitPixel, vec3 hitPoint, float dist, vec3 rayDir) {',
- ' float alpha = clamp(reflectivity, 0.0, 1.0);',
- // Fade ray hits that approach the maximum iterations
- ' alpha *= 1.0 - (iterationCount / float(20));',
- // Fade ray hits that approach the screen edge
- ' vec2 hitPixelNDC = hitPixel * 2.0 - 1.0;',
- ' float maxDimension = min(1.0, max(abs(hitPixelNDC.x), abs(hitPixelNDC.y)));',
- ' alpha *= 1.0 - max(0.0, maxDimension - screenEdgeFadeStart) / (1.0 - screenEdgeFadeStart);',
-
- // Fade ray hits base on how much they face the camera
- ' float _eyeFadeStart = eyeFadeStart;',
- ' float _eyeFadeEnd = eyeFadeEnd;',
- ' if (_eyeFadeStart > _eyeFadeEnd) {',
- ' float tmp = _eyeFadeEnd;',
- ' _eyeFadeEnd = _eyeFadeStart;',
- ' _eyeFadeStart = tmp;',
- ' }',
-
- ' float eyeDir = clamp(rayDir.z, _eyeFadeStart, _eyeFadeEnd);',
- ' alpha *= 1.0 - (eyeDir - _eyeFadeStart) / (_eyeFadeEnd - _eyeFadeStart);',
-
- // Fade ray hits based on distance from ray origin
- ' alpha *= 1.0 - clamp(dist / maxRayDistance, 0.0, 1.0);',
-
- ' return alpha;',
- '}',
-
- 'void main() {',
- ' vec4 normalAndGloss = texture2D(gBufferTexture1, v_Uv);',
-
- // Is empty
- ' if (dot(normalAndGloss.rgb, vec3(1.0)) == 0.0) {',
- ' discard;',
- ' }',
-
- ' float g = normalAndGloss.a;',
- ' if (g <= minGlossiness) {',
- ' discard;',
- ' }',
-
- ' float reflectivity = (g - minGlossiness) / (1.0 - minGlossiness);',
-
- ' vec3 N = normalAndGloss.rgb * 2.0 - 1.0;',
- ' N = normalize((viewInverseTranspose * vec4(N, 0.0)).xyz);',
-
- // Position in view
- ' vec4 projectedPos = vec4(v_Uv * 2.0 - 1.0, fetchDepth(gBufferTexture2, v_Uv), 1.0);',
- ' vec4 pos = projectionInv * projectedPos;',
- ' vec3 rayOrigin = pos.xyz / pos.w;',
-
- ' vec3 rayDir = normalize(reflect(normalize(rayOrigin), N));',
- ' vec2 hitPixel;',
- ' vec3 hitPoint;',
- ' float iterationCount;',
-
- // Get jitter
- ' vec2 uv2 = v_Uv * viewportSize;',
- ' float jitter = fract((uv2.x + uv2.y) * 0.25);',
-
- ' bool intersect = traceScreenSpaceRay(rayOrigin, rayDir, jitter, hitPixel, hitPoint, iterationCount);',
-
- ' float dist = distance(rayOrigin, hitPoint);',
-
- ' float alpha = calculateAlpha(iterationCount, reflectivity, hitPixel, hitPoint, dist, rayDir) * float(intersect);',
-
- ' vec3 hitNormal = texture2D(gBufferTexture1, hitPixel).rgb * 2.0 - 1.0;',
- ' hitNormal = normalize((viewInverseTranspose * vec4(hitNormal, 0.0)).xyz);',
-
- // Ignore the pixel not face the ray
- // TODO fadeout ?
- // PENDING Can be configured?
- ' if (dot(hitNormal, rayDir) >= 0.0) {',
- ' discard;',
- ' }',
-
- // vec4 color = decodeHDR(texture2DLodEXT(colorTex, hitPixel, clamp(dist / maxRayDistance, 0.0, 1.0) * maxMipmapLevel));
-
- ' if (!intersect) {',
- ' discard;',
- ' }',
-
- ' vec4 color = texture2D(colorTex, hitPixel);',
- ' gl_FragColor = vec4(color.rgb * alpha, color.a);',
-
- // gl_FragColor = vec4(vec3(iterationCount / 2.0), 1.0);
-
- '}'
-
- ].join("\n")
-
+ bool traceScreenSpaceRay(vec3 rayOrigin, vec3 rayDir, float jitter, out vec2 hitPixel, out vec3 hitPoint, out float iterationCount) {
+ // Clip to the near plane
+ float rayLength = ((rayOrigin.z + rayDir.z * maxRayDistance) > -nearZ) ? (-nearZ - rayOrigin.z) / rayDir.z : maxRayDistance;
+
+ vec3 rayEnd = rayOrigin + rayDir * rayLength;
+
+ // Project into homogeneous clip space
+ vec4 H0 = projection * vec4(rayOrigin, 1.0);
+ vec4 H1 = projection * vec4(rayEnd, 1.0);
+
+ float k0 = 1.0 / H0.w, k1 = 1.0 / H1.w;
+
+ // The interpolated homogeneous version of the camera space points
+ vec3 Q0 = rayOrigin * k0, Q1 = rayEnd * k1;
+
+ // Screen space endpoints
+ // PENDING viewportSize ?
+ vec2 P0 = (H0.xy * k0 * 0.5 + 0.5) * viewportSize;
+ vec2 P1 = (H1.xy * k1 * 0.5 + 0.5) * viewportSize;
+
+ // If the line is degenerate, make it cover at least one pixel to avoid handling
+ // zero-pixel extent as a special case later
+ P1 += dot(P1 - P0, P1 - P0) < 0.0001 ? 0.01 : 0.0;
+ vec2 delta = P1 - P0;
+
+ // Permute so that the primary iteration is in x to collapse
+ // all quadrant-specific DDA case later
+ bool permute = false;
+ if (abs(delta.x) < abs(delta.y)) {
+ // More vertical line
+ permute = true;
+ delta = delta.yx;
+ P0 = P0.yx;
+ P1 = P1.yx;
+ }
+ float stepDir = sign(delta.x);
+ float invdx = stepDir / delta.x;
+
+ // Track the derivatives of Q and K
+ vec3 dQ = (Q1 - Q0) * invdx;
+ float dk = (k1 - k0) * invdx;
+
+ vec2 dP = vec2(stepDir, delta.y * invdx);
+
+ // Calculate pixel stride based on distance of ray origin from camera.
+ // Since perspective means distant objects will be smaller in screen space
+ // we can use this to have higher quality reflections for far away objects
+ // while still using a large pixel stride for near objects (and increase performance)
+ // this also helps mitigate artifacts on distant reflections when we use a large
+ // pixel stride.
+ float strideScaler = 1.0 - min(1.0, -rayOrigin.z / pixelStrideZCutoff);
+ float pixStride = mix(pixelStride, 1.0 + strideScaler * pixelStride, enablePixelStrideZCutoff);
+
+ // Scale derivatives by the desired pixel stride and the offset the starting values by the jitter fraction
+ dP *= pixStride; dQ *= pixStride; dk *= pixStride;
+
+ // Track ray step and derivatives in a vec4 to parallelize
+ vec4 pqk = vec4(P0, Q0.z, k0);
+ vec4 dPQK = vec4(dP, dQ.z, dk);
+
+ pqk += dPQK * jitter;
+ float rayZFar = (dPQK.z * 0.5 + pqk.z) / (dPQK.w * 0.5 + pqk.w);
+ float rayZNear;
+
+ bool intersect = false;
+
+ vec2 texelSize = 1.0 / viewportSize;
+
+ iterationCount = 0.0;
+ float end = P1.x * stepDir;
+ for (int i = 0; i < MAX_ITERATION; i++) {
+ pqk += dPQK;
+ if ((pqk.x * stepDir) >= end) {
+ break;
+ }
+
+ rayZNear = rayZFar;
+ rayZFar = (dPQK.z * 0.5 + pqk.z) / (dPQK.w * 0.5 + pqk.w);
+
+ hitPixel = permute ? pqk.yx : pqk.xy;
+ hitPixel *= texelSize;
+
+ intersect = rayIntersectDepth(rayZNear, rayZFar, hitPixel);
+
+ iterationCount += 1.0;
+
+ // PENDING Right on all platforms?
+ if (intersect) {
+ break;
+ }
+ }
+
+ // Binary search refinement
+ // FIXME If intersect in first iteration binary search may easily lead to the pixel of reflect object it self
+ if (pixStride > 1.0 && intersect && iterationCount > 1.0) {
+ // Roll back
+ pqk -= dPQK;
+ dPQK /= pixStride;
+
+ float originalStride = pixStride * 0.5;
+ float stride = originalStride;
+
+ rayZNear = pqk.z / pqk.w;
+ rayZFar = rayZNear;
+
+ for (int j = 0; j < MAX_BINARY_SEARCH_ITERATION; j++) {
+ pqk += dPQK * stride;
+ rayZNear = rayZFar;
+ rayZFar = (dPQK.z * -0.5 + pqk.z) / (dPQK.w * -0.5 + pqk.w);
+ hitPixel = permute ? pqk.yx : pqk.xy;
+ hitPixel *= texelSize;
+
+ originalStride *= 0.5;
+ stride = rayIntersectDepth(rayZNear, rayZFar, hitPixel) ? -originalStride : originalStride;
+ }
+ }
+
+ Q0.xy += dQ.xy * iterationCount;
+ Q0.z = pqk.z;
+ hitPoint = Q0 / pqk.w;
+
+ return intersect;
+ }
+
+ float calculateAlpha(float iterationCount, float reflectivity, vec2 hitPixel, vec3 hitPoint, float dist, vec3 rayDir) {
+ float alpha = clamp(reflectivity, 0.0, 1.0);
+
+ // Fade ray hits that approach the maximum iterations
+ alpha *= 1.0 - (iterationCount / float(MAX_ITERATION));
+
+ // Fade ray hits that approach the screen edge
+ vec2 hitPixelNDC = hitPixel * 2.0 - 1.0;
+ float maxDimension = min(1.0, max(abs(hitPixelNDC.x), abs(hitPixelNDC.y)));
+ alpha *= 1.0 - max(0.0, maxDimension - screenEdgeFadeStart) / (1.0 - screenEdgeFadeStart);
+
+ // Fade ray hits base on how much they face the camera
+ float _eyeFadeStart = eyeFadeStart;
+ float _eyeFadeEnd = eyeFadeEnd;
+ if (_eyeFadeStart > _eyeFadeEnd) {
+ float tmp = _eyeFadeEnd;
+ _eyeFadeEnd = _eyeFadeStart;
+ _eyeFadeStart = tmp;
+ }
+ float eyeDir = clamp(rayDir.z, _eyeFadeStart, _eyeFadeEnd);
+ alpha *= 1.0 - (eyeDir - _eyeFadeStart) / (_eyeFadeEnd - _eyeFadeStart);
+
+ // Fade ray hits based on distance from ray origin
+ alpha *= 1.0 - clamp(dist / maxRayDistance, 0.0, 1.0);
+
+ return alpha;
+ }
+ void main() {
+ vec4 normalAndGloss = texture2D(gBufferTexture1, v_Uv);
+
+ if (dot(normalAndGloss.rgb, vec3(1.0)) == 0.0) {
+ discard;
+ }
+
+ float g = normalAndGloss.a;
+ if (g <= minGlossiness) {
+ discard;
+ }
+
+ float reflectivity = g;
+
+ vec3 N = normalAndGloss.rgb * 2.0 - 1.0;
+ N = normalize((viewInverseTranspose * vec4(N, 0.0)).xyz);
+
+ // Position in view
+ vec4 projectedPos = vec4(v_Uv * 2.0 - 1.0, fetchDepth(gBufferTexture2, v_Uv), 1.0);
+ vec4 pos = projectionInv * projectedPos;
+ vec3 rayOrigin = pos.xyz / pos.w;
+
+ vec3 rayDir = normalize(reflect(normalize(rayOrigin), N));
+ vec2 hitPixel;
+ vec3 hitPoint;
+ float iterationCount;
+
+ // Get jitter
+ vec2 uv2 = v_Uv * viewportSize;
+ float jitter = fract((uv2.x + uv2.y) * 0.25);
+
+ bool intersect = traceScreenSpaceRay(rayOrigin, rayDir, jitter, hitPixel, hitPoint, iterationCount);
+ // Is empty
+ if (!intersect) {
+ discard;
+ }
+ float dist = distance(rayOrigin, hitPoint);
+
+ float alpha = calculateAlpha(iterationCount, reflectivity, hitPixel, hitPoint, dist, rayDir) * float(intersect);
+
+ vec3 hitNormal = texture2D(gBufferTexture1, hitPixel).rgb * 2.0 - 1.0;
+ hitNormal = normalize((viewInverseTranspose * vec4(hitNormal, 0.0)).xyz);
+
+ // Ignore the pixel not face the ray
+ // TODO fadeout ?
+ // PENDING Can be configured?
+ if (dot(hitNormal, rayDir) >= 0.0) {
+ discard;
+ }
+
+ vec4 color = texture2D(colorTex, hitPixel);
+ gl_FragColor = vec4(color.rgb * alpha, color.a);
+ }
+ `
};
export { SSRShader };
\ No newline at end of file