awalter7
awalter7

Reputation: 1

Reconstruct world Space from Depth Texture THREE.js Shader Pass

I am creating an Atmosphere shader in three.js. The Shader is passed to a ShaderPass, and the ShaderPass to the effectComposer. I am currently having a problem with utilizing the DepthTexture. The problem with using the ShaderMaterial as a post processing effect is that you are not provided the worldPosition. I am trying to reconstruct the world position.

Ideally I would pass the depthTexture to the reconstructWorldPosition() function to reconstruct the depth. I have tried doing this, and I am lost.

vec3 reconstructWorldPosition() { 
    float depth = 0.90;  

    vec4 clipSpacePosition = vec4(vUv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);


    vec4 viewSpacePosition = uInverseProjection * clipSpacePosition;
    viewSpacePosition /= viewSpacePosition.w;


    vec4 worldSpacePosition = uInverseView * viewSpacePosition;
    return worldSpacePosition.xyz;
}

`

The problem with this current implementation is 1) the depth is constant and 2) the atmosphere z fights with the sphere mesh at the same position.

Close to Atmosphere: Atmosphere Close Far from Atmosphere: Atmosphere Far Away

The specific atmosphere shader I am trying to implement can be found in this video by Sebastian Lague:

https://www.youtube.com/watch?v=DxfEbulyFcY&t=145s

And the alternative worldPosition function I was trying to implement can be found in this video by SimonDev:

https://www.youtube.com/watch?v=8bRS9RRWfSs&t=299s

The rest of the shader: ` import * as THREE from "three";

const AtmosphereShader = new THREE.ShaderMaterial(
    { 
    uniforms: { 
    tDiffuse: { value: null }, 
    tDepth: { value: null },
    sRadius: { value: 0.0 },
    sPosition: { value: new THREE.Vector3(0.0, 0.0, 0.0) },
    aRadius: { value: 0.0 },
    
    nCamera: { value: 0.0 },
    fCamera: { value: 0.0 },
    
    numOpticalDepthPoints: { value: 10.0 },
    numInScatteringPoints: { value: 30.0 },
    
    uInverseProjection: { value: new THREE.Matrix4() },
    uInverseView: { value: new THREE.Matrix4() },
    
    sunDist: { value: 0.0 },
    dirToSun: { value: new THREE.Vector3(0.0, 0.5, 0.5).normalize() },
    
    densityFallOff: { value: 2.0 },
    
    cR: { value: 0.0 },
    cG: { value: 0.0 },
    cB: { value: 0.0 },
    }, vertexShader:` 
        varying vec2 vUv; 
        varying vec3 vWorldPosition;

        void main() {
          vUv = uv;
          vec4 worldPosition = modelMatrix * vec4(position, 1.0);
          vWorldPosition = worldPosition.xyz;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
    ,   fragmentShader: `
            uniform sampler2D tDiffuse; 
            uniform sampler2D tDepth;
            uniform float sRadius;
            uniform vec3 sPosition;
            uniform float aRadius;
            
            uniform float nCamera;
            uniform float fCamera;
            
            uniform float numOpticalDepthPoints;
            uniform float numInScatteringPoints;
            uniform float densityFallOff;
            
            uniform float cR;
            uniform float cG;
            uniform float cB;
            
            uniform float sunDist;
            uniform vec3 dirToSun;
            
            varying vec2 vUv;
            varying vec3 vWorldPosition;
            
            uniform mat4 uInverseProjection;
            uniform mat4 uInverseView;
            float linearEyeDepth(float d) {
              float zNDC = 2.0 * d - 1.0;
              return (2.0 * nCamera * fCamera) / (fCamera + nCamera - zNDC * (fCamera - nCamera));
            }
            
            vec3 reconstructWorldPosition() {
              // Use a constant normalized depth value (between 0.0 and 1.0)
              float depth = 0.90;
            
              // Convert UV coordinates [0,1] -> clip space [-1,1]
              vec4 clipSpacePosition = vec4(vUv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
            
              // Transform from clip space to view space
              vec4 viewSpacePosition = uInverseProjection * clipSpacePosition;
              viewSpacePosition /= viewSpacePosition.w;
            
              // Transform from view space to world space
              vec4 worldSpacePosition = uInverseView * viewSpacePosition;
              return worldSpacePosition.xyz;
            }
            
            float densityAtPoint(vec3 densitySamplePoint) {
              float heightAboveSurface = length(densitySamplePoint - sPosition) - sRadius;
              float height01 = heightAboveSurface / (aRadius - sRadius);
              float localDensity = exp(-height01 * densityFallOff) * (1.0 - height01);
              return localDensity;
            }
            
            vec2 raySphere(vec3 sphereCenter, float radius, vec3 rayOrigin, vec3 rayDir) {
              vec3 offset = rayOrigin - sphereCenter;
              float a = 1.0;
              float b = 2.0 * dot(offset, rayDir);
              float c = dot(offset, offset) - radius * radius;
              float d = b * b - 4.0 * a * c;
            
              if (d > 0.0) {
                float s = sqrt(d);
                float dstToSphereNear = max(0.0, (-b - s) / (2.0 * a));
                float dstToSphereFar = (-b + s) / (2.0 * a);
            
                if(dstToSphereFar >= 0.0){
                  return vec2(dstToSphereNear, dstToSphereFar - dstToSphereNear);
                }
              }
              return vec2(1.0 / 0.0, 0.0); // No intersection
            }
            
            float opticalDepth(vec3 rayOrigin, vec3 rayDir, float rayLength) {
              vec3 densitySamplePoint = rayOrigin;
              float stepSize = rayLength / (numOpticalDepthPoints - 1.0);
              float accumDepth = 0.0;
            
              for(float i = 0.0; i < numOpticalDepthPoints; i++) {
                float localDensity = densityAtPoint(densitySamplePoint);
                accumDepth += localDensity * stepSize;
                densitySamplePoint += rayDir * stepSize;
              }
              return accumDepth;
            }
            
            vec3 calculateLight(vec3 rayOrigin, vec3 rayDir, float rayLength, vec3 originalCol) {
              vec3 inScatterPoint = rayOrigin;
              float stepSize = rayLength / (numInScatteringPoints - 1.0);
              vec3 inScatteredLight = vec3(0.0);
              float sunRayOpticalDepth = 0.0;
              float viewRayOpticalDepth = 0.0;
            
              vec3 scatteringCoef = vec3(cR, cG, cB);
            
              for(float i = 0.0; i < numInScatteringPoints; i++) {
                float sunRayLength = raySphere(sPosition, aRadius, inScatterPoint, dirToSun).y * sunDist;
                sunRayOpticalDepth = opticalDepth(inScatterPoint, dirToSun, sunRayLength);
                viewRayOpticalDepth = opticalDepth(inScatterPoint, -rayDir, stepSize * i);
            
                vec3 transmittance = exp(-(sunRayOpticalDepth + viewRayOpticalDepth) * scatteringCoef);
                float localDensity = densityAtPoint(inScatterPoint);
            
                inScatteredLight += localDensity * transmittance * scatteringCoef * stepSize;
                inScatterPoint += rayDir * stepSize;
              }
            
              float originalColTransmittance = exp(-viewRayOpticalDepth);
              return originalCol * originalColTransmittance + inScatteredLight;
            }
            
            vec4 atmosphere() {
              vec3 worldPosition = reconstructWorldPosition();
              vec3 viewVector = worldPosition - cameraPosition;
              float vVlength = length(viewVector);
            
              vec4 originalCol = texture2D(tDiffuse, vUv);
            
              float sceneDepthNonLinear = texture2D(tDepth, vUv).r;
              float sceneDepth = linearEyeDepth(sceneDepthNonLinear) * vVlength;
            
              vec3 rayOrigin = worldPosition;
              vec3 rayDir = normalize(viewVector);
            
              vec2 surfaceHitInfo = raySphere(sPosition, sRadius, rayOrigin, rayDir);
              float dstToSurface = min(sceneDepth, surfaceHitInfo.x);
            
              vec2 sphereHitInfo = raySphere(sPosition, aRadius, rayOrigin, rayDir);
              float dstToAtmosphere = sphereHitInfo.x;
              float dstThroughAtmosphere = min(sphereHitInfo.y, dstToSurface - dstToAtmosphere);
            
              if (dstThroughAtmosphere > 0.0) {
                const float epsilon = 0.0001;
                vec3 pointInAtmosphere = rayOrigin + rayDir * (dstToAtmosphere + epsilon);
                vec3 light = calculateLight(pointInAtmosphere, rayDir, dstThroughAtmosphere - epsilon * 2.0, originalCol.rgb);
            
                if(dstToAtmosphere < sceneDepth) {
                  return vec4(light, 0.9);
                }
              }
            
              return originalCol;
            }
            
            void main() {
              gl_FragColor = atmosphere();
            }
        ` , 
        transparent: true, 
        depthWrite: false, 
        depthTest: true, 
        blending: null, 
    }
);

export default AtmosphereShader; 

I am rather new to Three.js Post Processing, so please let me know if you happen across any other problem areas in my code that may be the cause the issue, or if I left out any important details.

Upvotes: 0

Views: 35

Answers (0)

Related Questions