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

ThreeJS启用depthWrite后Points出现黑色矩形背景问题求助

Three.js Points启用深度写入后出现黑色矩形背景的问题及解决

问题背景

我在Three.js场景中使用Points组件,核心代码如下:

<points ref={gPoints} position={position}>
    <bufferGeometry attach="geometry">
        <bufferAttribute
            attach="attributes-position"
            count={positions.length / 3}
            array={positions}
            itemSize={3}
            usage={THREE.DynamicDrawUsage}
        />
        // ... 其他属性
    </bufferGeometry>
    <shaderMaterial
        depthWrite={false}
        depthTest={false}
        fragmentShader={fragmentShader}
        vertexShader={vertexShader}
        uniforms={uniforms}
        blending={THREE.AdditiveBlending}
    />
</points>

通过片元着色器实现点的模糊效果:

uniform float uResolutionWidth;
uniform float uResolutionHeight;

varying vec3 vColor;
varying vec4 vRealPosition;
varying float vDistance;

void main() {
    float d = length(gl_PointCoord.xy - 0.5);
    float alpha = smoothstep(0.5, 0.15, d);
    gl_FragColor = vec4(vColor, alpha);
}

顶点着色器中通过以下逻辑设置vColor:

#define PI 3.14159265358979323846

uniform float uTime;
uniform float uRadius;

attribute float pointSizes;
attribute float randomX;
attribute float randomY;
attribute float randomZ;
attribute float randomW;

varying vec3 vColor;

void main() {
    // 设置顶点颜色
    float d = length(abs(position) / vec3(96.0, 22.0, 96.0));
    d = clamp(d, 0.0, 1.0);
    float d2 = abs(position.y);
    vColor = mix(vec3(244.0, 131.0, 12.0), vec3(82.0, 27.0, 255.0), d2/22.0) / 255.;
}

关闭depthWrite和depthTest时,点显示正常(呈现平滑的模糊圆形,无黑色背景);但启用这两个属性后,每个点出现黑色矩形背景。我尝试丢弃alpha值过低的片段:

uniform float uResolutionWidth;
uniform float uResolutionHeight;

varying vec3 vColor;
varying vec4 vRealPosition;
varying float vDistance;

void main() {
    float d = length(gl_PointCoord.xy - 0.5);
    float alpha = smoothstep(0.5, 0.15, d);

    // 丢弃完全透明的片段
    if (alpha < 0.001) {
        discard;
    }

    gl_FragColor = vec4(vColor * alpha, alpha);
}

但效果仍不理想(黑色矩形边缘仍可见)。

成因分析

  • 深度缓冲区写入逻辑:启用depthWrite时,WebGL会将每个片元的深度值写入深度缓冲区,无论该片元的alpha值是否为0。Points的每个点本质是矩形面片,即使通过alpha通道让边缘透明,这些透明片元的深度值依然会被写入,后续绘制的物体或场景背景会被这些深度值挡住,从而显示黑色矩形。
  • discard阈值过高:仅丢弃完全透明(alpha < 0.001)的片元,边缘半透明的片元仍会写入深度值,导致黑色痕迹残留。

解决方法

方法1:优化片元丢弃逻辑

降低丢弃阈值,确保所有非核心模糊区域的片元都被丢弃,避免无效深度写入:

uniform float uResolutionWidth;
uniform float uResolutionHeight;

varying vec3 vColor;
varying vec4 vRealPosition;
varying float vDistance;

void main() {
    float d = length(gl_PointCoord.xy - 0.5);
    float alpha = smoothstep(0.5, 0.15, d);

    // 丢弃半透明及完全透明的片元,阈值可根据效果调整
    if (alpha < 0.1) {
        discard;
    }

    gl_FragColor = vec4(vColor, alpha);
}

方法2:使用alphaTest替代手动丢弃

在ShaderMaterial中启用alphaTest,让WebGL自动丢弃alpha值低于指定阈值的片元,效率更高且能彻底避免深度写入问题:

<shaderMaterial
    depthWrite={true}
    depthTest={true}
    fragmentShader={fragmentShader}
    vertexShader={vertexShader}
    uniforms={uniforms}
    blending={THREE.AdditiveBlending}
    alphaTest={0.1} // 阈值可根据效果调整
/>

同时可移除片元着色器中的手动discard代码,保留原有alpha计算逻辑即可。

方法3:调整深度测试函数(补充方案)

如果需要保留AdditiveBlending和深度测试,可尝试将depthFunc设置为THREE.LessEqualDepth,缓解深度冲突问题:

<shaderMaterial
    depthWrite={true}
    depthTest={true}
    depthFunc={THREE.LessEqualDepth}
    fragmentShader={fragmentShader}
    vertexShader={vertexShader}
    uniforms={uniforms}
    blending={THREE.AdditiveBlending}
/>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 05:13:13