lingo3d
Version:
Lingo3D is a React/Vue 3d game development framework that ships with a complete visual editor
290 lines (214 loc) • 9.19 kB
JavaScript
export default `varying vec2 vUv;
uniform sampler2D inputTexture;
uniform sampler2D accumulatedTexture;
uniform sampler2D normalTexture;
uniform sampler2D depthTexture;
uniform sampler2D envMap;
uniform mat4 _projectionMatrix;
uniform mat4 _inverseProjectionMatrix;
uniform mat4 cameraMatrixWorld;
uniform float cameraNear;
uniform float cameraFar;
uniform float rayDistance;
uniform float intensity;
uniform float maxDepthDifference;
uniform float roughnessFade;
uniform float maxRoughness;
uniform float fade;
uniform float thickness;
uniform float ior;
uniform float jitter;
uniform float jitterRoughness;
float nearMinusFar;
float nearMulFar;
float farMinusNear;
// helper functions
vec2 RayMarch(vec3 dir, inout vec3 hitPos, inout float rayHitDepthDifference);
vec2 BinarySearch(in vec3 dir, inout vec3 hitPos, inout float rayHitDepthDifference);
float fastGetViewZ(const in float depth);
vec3 getIBLRadiance(const in vec3 viewDir, const in vec3 normal, const in float roughness);
void main() {
vec4 depthTexel = textureLod(depthTexture, vUv, 0.0);
// filter out sky
if (dot(depthTexel.rgb, depthTexel.rgb) < FLOAT_EPSILON) {
gl_FragColor = EARLY_OUT_COLOR;
return;
}
float unpackedDepth = unpackRGBAToDepth(depthTexel);
vec4 normalTexel = textureLod(normalTexture, vUv, 0.0);
float roughness = normalTexel.a;
float specular = 1.0 - roughness;
// pre-calculated variables for the "fastGetViewZ" function
nearMinusFar = cameraNear - cameraFar;
nearMulFar = cameraNear * cameraFar;
farMinusNear = cameraFar - cameraNear;
normalTexel.rgb = unpackRGBToNormal(normalTexel.rgb);
// view-space depth
float depth = fastGetViewZ(unpackedDepth);
// view-space position of the current texel
vec3 viewPos = getViewPosition(depth);
vec3 viewDir = normalize(viewPos);
vec3 viewNormal = normalTexel.xyz;
// world-space position of the current texel
vec3 worldPos = screenSpaceToWorldSpace(vUv, unpackedDepth);
// jitteriing
vec3 jitt = vec3(0.0);
if (jitterRoughness != 0.0 || jitter != 0.0) {
vec3 randomJitter = hash(50.0 * worldPos) - 0.5;
float spread = ((2.0 - specular) + roughness * jitterRoughness);
float jitterMix = jitter * 0.25 + jitterRoughness * roughness;
if (jitterMix > 1.0) jitterMix = 1.0;
jitt = mix(vec3(0.0), randomJitter * spread, jitterMix);
}
viewNormal += jitt;
float fresnelFactor = fresnel_dielectric(viewDir, viewNormal, ior);
vec3 iblRadiance = getIBLRadiance(-viewDir, viewNormal, 0.) * fresnelFactor;
float lastFrameAlpha = textureLod(accumulatedTexture, vUv, 0.0).a;
if (roughness > maxRoughness || (roughness > 1.0 - FLOAT_EPSILON && roughnessFade > 1.0 - FLOAT_EPSILON)) {
gl_FragColor = vec4(iblRadiance, lastFrameAlpha);
return;
}
// view-space reflected ray
vec3 reflected = reflect(viewDir, viewNormal);
vec3 rayDir = reflected * -viewPos.z;
vec3 hitPos = viewPos;
float rayHitDepthDifference;
vec2 coords = RayMarch(rayDir, hitPos, rayHitDepthDifference);
if (coords.x == -1.0) {
gl_FragColor = vec4(iblRadiance, lastFrameAlpha);
return;
}
vec4 SSRTexel = textureLod(inputTexture, coords.xy, 0.0);
vec4 SSRTexelReflected = textureLod(accumulatedTexture, coords.xy, 0.0);
vec3 SSR = SSRTexel.rgb + SSRTexelReflected.rgb;
float roughnessFactor = mix(specular, 1.0, max(0.0, 1.0 - roughnessFade));
vec2 coordsNDC = (coords.xy * 2.0 - 1.0);
float screenFade = 0.1;
float maxDimension = min(1.0, max(abs(coordsNDC.x), abs(coordsNDC.y)));
float reflectionIntensity = 1.0 - (max(0.0, maxDimension - screenFade) / (1.0 - screenFade));
reflectionIntensity = max(0., reflectionIntensity);
vec3 finalSSR = mix(iblRadiance, SSR, reflectionIntensity) * roughnessFactor;
// vec2 dCoords = smoothstep(0.2, 0.6, abs(vec2(0.5, 0.5) - coords.xy));
// float screenEdgefactor = clamp(1.0 - (dCoords.x + dCoords.y), 0.0, 1.0);
// vec3 finalSSR = mix(iblRadiance, SSR * screenEdgefactor, screenEdgefactor) * roughnessFactor;
if (fade != 0.0) {
vec3 hitWorldPos = screenSpaceToWorldSpace(coords, rayHitDepthDifference);
// distance from the reflection point to what it's reflecting
float reflectionDistance = distance(hitWorldPos, worldPos) + 1.0;
float opacity = 1.0 / (reflectionDistance * fade * 0.1);
if (opacity > 1.0) opacity = 1.0;
finalSSR *= opacity;
}
finalSSR *= fresnelFactor * intensity;
finalSSR = min(vec3(1.0), finalSSR);
float alpha = hitPos.z == 1.0 ? 1.0 : SSRTexelReflected.a;
alpha = min(lastFrameAlpha, alpha);
gl_FragColor = vec4(finalSSR, alpha);
}
vec2 RayMarch(vec3 dir, inout vec3 hitPos, inout float rayHitDepthDifference) {
dir = normalize(dir);
dir *= rayDistance / float(steps);
float depth;
vec4 projectedCoord;
vec4 lastProjectedCoord;
float unpackedDepth;
vec4 depthTexel;
for (int i = 0; i < steps; i++) {
hitPos += dir;
projectedCoord = _projectionMatrix * vec4(hitPos, 1.0);
projectedCoord.xy /= projectedCoord.w;
// [-1, 1] --> [0, 1] (NDC to screen position)
projectedCoord.xy = projectedCoord.xy * 0.5 + 0.5;
// the ray is outside the camera's frustum
if (projectedCoord.x < 0.0 || projectedCoord.x > 1.0 || projectedCoord.y < 0.0 || projectedCoord.y > 1.0) {
return INVALID_RAY_COORDS;
}
depthTexel = textureLod(depthTexture, projectedCoord.xy, 0.0);
unpackedDepth = unpackRGBAToDepth(depthTexel);
depth = fastGetViewZ(unpackedDepth);
rayHitDepthDifference = depth - hitPos.z;
if (rayHitDepthDifference >= 0.0 && rayHitDepthDifference < thickness) {
// filter out sky
if (dot(depthTexel.rgb, depthTexel.rgb) < FLOAT_EPSILON) return INVALID_RAY_COORDS;
return BinarySearch(dir, hitPos, rayHitDepthDifference);
}
// the ray is behind the camera
if (hitPos.z > 0.0) {
return INVALID_RAY_COORDS;
}
lastProjectedCoord = projectedCoord;
}
// since hitPos isn't used anywhere we can use it to mark that this reflection would have been invalid
hitPos.z = 1.0;
return INVALID_RAY_COORDS;
rayHitDepthDifference = unpackedDepth;
return projectedCoord.xy;
}
vec2 BinarySearch(in vec3 dir, inout vec3 hitPos, inout float rayHitDepthDifference) {
float depth;
vec4 projectedCoord;
vec2 lastMinProjectedCoordXY;
float unpackedDepth;
vec4 depthTexel;
for (int i = 0; i < refineSteps; i++) {
projectedCoord = _projectionMatrix * vec4(hitPos, 1.0);
projectedCoord.xy /= projectedCoord.w;
projectedCoord.xy = projectedCoord.xy * 0.5 + 0.5;
depthTexel = textureLod(depthTexture, projectedCoord.xy, 0.0);
unpackedDepth = unpackRGBAToDepth(depthTexel);
depth = fastGetViewZ(unpackedDepth);
rayHitDepthDifference = depth - hitPos.z;
dir *= 0.5;
if (rayHitDepthDifference > 0.0) {
hitPos -= dir;
} else {
hitPos += dir;
}
}
// filter out sky
if (dot(depthTexel.rgb, depthTexel.rgb) < FLOAT_EPSILON) return INVALID_RAY_COORDS;
if (abs(rayHitDepthDifference) > maxDepthDifference) return INVALID_RAY_COORDS;
projectedCoord = _projectionMatrix * vec4(hitPos, 1.0);
projectedCoord.xy /= projectedCoord.w;
projectedCoord.xy = projectedCoord.xy * 0.5 + 0.5;
rayHitDepthDifference = unpackedDepth;
return projectedCoord.xy;
}
// source: https://github.com/mrdoob/three.js/blob/342946c8392639028da439b6dc0597e58209c696/examples/js/shaders/SAOShader.js#L123
float fastGetViewZ(const in float depth) {
return nearMulFar / (farMinusNear * depth - cameraFar);
return depth * nearMinusFar - cameraNear;
}
// from: https://github.com/mrdoob/three.js/blob/d5b82d2ca410e2e24ca2f7320212dfbee0fe8e89/src/renderers/shaders/ShaderChunk/envmap_physical_pars_fragment.glsl.js#L22
vec3 getIBLRadiance(const in vec3 viewDir, const in vec3 normal, const in float roughness) {
vec3 reflectVec = reflect(-viewDir, normal);
// Mixing the reflection with the normal is more accurate and keeps rough objects from gathering light from behind their tangent plane.
reflectVec = normalize(mix(reflectVec, normal, roughness * roughness));
reflectVec = inverseTransformDirection(reflectVec, viewMatrix);
vec4 envMapColor = textureCubeUV(envMap, reflectVec, roughness);
return envMapColor.rgb * intensity;
return vec3(0.0);
}`;
//# sourceMappingURL=reflectionsShader.js.map