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.