Using Depth Textures in threeJS

Introduction

Depth textures are essential in 3D rendering to store depth information for advanced effects like depth-based shading, screen-space effects, and shadow mapping. This article explains how to utilize depth textures in Three.js, focusing on render targets, shaders, and post-processing.

Creating a Depth Texture Render Target

To store depth information, we need a WebGLRenderTarget with a depth texture. This allows us to capture the scene’s depth buffer.

const target = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight);
target.texture.minFilter = THREE.NearestFilter;
target.texture.magFilter = THREE.NearestFilter;
target.texture.generateMipmaps = false;
target.depthTexture = new THREE.DepthTexture();
target.depthTexture.format = THREE.DepthFormat;
target.depthTexture.type = THREE.UnsignedShortType;

Rendering the Scene into the Depth Texture

The main scene is rendered into this depth texture using a render target before applying post-processing effects.

renderer.setRenderTarget(target);
renderer.render(scene, camera);

Reading Depth Values in a Shader

A post-processing step is used to visualize or process depth data. This is done using a shader that reads depth values from the texture.

Vertex Shader:

varying vec2 vUv;
void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

Fragment Shader:

#include <packing>
varying vec2 vUv;
uniform sampler2D tDepth;
uniform float cameraNear;
uniform float cameraFar;

float readDepth(sampler2D depthSampler, vec2 coord) {
    float fragCoordZ = texture2D(depthSampler, coord).x;
    float viewZ = perspectiveDepthToViewZ(fragCoordZ, cameraNear, cameraFar);
    return viewZToOrthographicDepth(viewZ, cameraNear, cameraFar);
}

void main() {
    float depth = readDepth(tDepth, vUv);
    gl_FragColor = vec4(vec3(1.0 - depth), 1.0);
}

Applying Post-Processing

A fullscreen quad is used to apply the shader for depth visualization:

const postCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
const postMaterial = new THREE.ShaderMaterial({
    vertexShader: document.querySelector('#post-vert').textContent.trim(),
    fragmentShader: document.querySelector('#post-frag').textContent.trim(),
    uniforms: {
        cameraNear: { value: camera.near },
        cameraFar: { value: camera.far },
        tDepth: { value: target.depthTexture },
    }
});
const postScene = new THREE.Scene();
postScene.add(new THREE.Mesh(new THREE.PlaneGeometry(2, 2), postMaterial));

Final Render Pass

After rendering the depth data, it is applied as a fullscreen post-processing effect:

renderer.setRenderTarget(null);
renderer.render(postScene, postCamera);

Conclusion

This method efficiently captures and processes depth information, enabling effects like depth-based shading and visualization. By using depth textures in Three.js, developers can implement various rendering techniques for enhanced visual fidelity.

Leave a comment

Your email address will not be published. Required fields are marked *