You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Three.js中访问像素深度数据以实现边缘描边Shader?

在Three.js中访问深度缓冲区实现边缘轮廓Shader

嘿,我来帮你理清Three.js里怎么访问深度缓冲区,实现你想要的那种基于深度差的动态轮廓效果~

首先得先搞懂你提到的“绘图缓冲区拥有深度缓冲区”是什么意思:其实深度缓冲区是GPU渲染时偷偷维护的一个隐藏缓冲区,它不会直接显示在屏幕上,但每个像素位置都存储了该点距离相机的远近信息——这正是你用来判断边缘、生成轮廓的核心数据。咱们平时看到的物体遮挡效果,本质上就是GPU通过深度缓冲区来判断哪个像素该显示、哪个该被遮挡的。

接下来具体说怎么在Three.js里拿到这些深度数据,主要有两种常用方法:

一、创建深度纹理(最常用的方式)

这种方法适合把整个场景的深度信息做成一张纹理,然后在自定义Shader里直接采样使用:

  1. 先创建一个支持深度纹理的渲染目标:
const depthRenderTarget = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, {
  depthTexture: new THREE.DepthTexture(),
  depthBuffer: true // 确保开启深度缓冲区
});
  1. 渲染时先把场景渲染到这个目标上,这样深度纹理就会被填充好数据:
// 先渲染到深度目标
renderer.setRenderTarget(depthRenderTarget);
renderer.render(scene, camera);
// 恢复到默认的屏幕渲染目标
renderer.setRenderTarget(null);
  1. 之后把depthRenderTarget.depthTexture作为uniform变量传给你的轮廓Shader,就能在片元着色器里读取每个像素的深度值了。

二、在Shader里直接读取内置深度值

如果你不想单独创建深度纹理,也可以在自定义Shader里直接访问Three.js提供的内置变量:

  • 片元Shader里的gl_FragCoord.z可以拿到当前像素的归一化深度值(范围0到1),但这个值是非线性的,需要转换成线性的相机空间深度(也就是实际的相机到物体表面的距离),转换代码如下:
// 记得把相机的near和far值作为uniform传入Shader
uniform float cameraNear;
uniform float cameraFar;

void main() {
  // 先把归一化深度转成NDC空间(-1到1)
  float z = gl_FragCoord.z * 2.0 - 1.0;
  // 计算线性深度
  float linearDepth = (2.0 * cameraNear * cameraFar) / (cameraFar + cameraNear - z * (cameraFar - cameraNear));
}

三、实现动态轮廓的核心逻辑

拿到深度值后,就可以像你看到的示例那样,通过对比当前像素和相邻像素的深度差来生成轮廓:

  • 在片元Shader里采样当前像素上下左右四个方向的深度值,计算它们和当前像素的深度差异;
  • 如果差异超过你设定的阈值,就把这个像素渲染成轮廓颜色;深度差越小,就越不显示轮廓,或者让轮廓更细。

给你一个简单的片元Shader示例片段:

uniform sampler2D depthTexture;
uniform vec2 resolution; // 屏幕分辨率
uniform float edgeThreshold; // 深度差阈值

void main() {
  vec2 uv = gl_FragCoord.xy / resolution;
  float currentDepth = texture(depthTexture, uv).r;
  
  // 采样相邻四个方向的深度
  float leftDepth = texture(depthTexture, uv + vec2(-1.0/resolution.x, 0.0)).r;
  float rightDepth = texture(depthTexture, uv + vec2(1.0/resolution.x, 0.0)).r;
  float topDepth = texture(depthTexture, uv + vec2(0.0, 1.0/resolution.y)).r;
  float bottomDepth = texture(depthTexture, uv + vec2(0.0, -1.0/resolution.y)).r;
  
  // 计算总深度差异
  float depthDiff = abs(currentDepth - leftDepth) + abs(currentDepth - rightDepth) + 
                    abs(currentDepth - topDepth) + abs(currentDepth - bottomDepth);
  
  // 根据阈值判断是否渲染轮廓
  if(depthDiff > edgeThreshold) {
    gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); // 黑色轮廓
  } else {
    gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); // 透明,显示原场景
  }
}

另外,如果你用Three.js的后处理套件EffectComposer,可以把这个轮廓Shader做成一个后处理通道,集成到渲染流程里,会更方便哦~

内容的提问来源于stack exchange,提问作者john doe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 06:48:22