如何在three.js中使用深度缓冲区屏蔽对象

How do I mask objects using the depth buffer in three.js?

本文关键字:深度 缓冲区 屏蔽 对象 three js      更新时间:2023-09-26

我正在用three.js编写一个2D游戏。首先,我想渲染背景场景。然后,我想渲染一个透明四边形(opacity === 0),即使您看不到它,它仍然会将值写入深度缓冲区。然后,我想渲染后面有z的对象,z将它们的片段放在透明四边形后面,这样任何与透明四边形具有相同屏幕空间位置的片段都会因为深度测试失败而被丢弃。

根据我对OpenGL的理解,这是可能的,但当透明四边形的碎片着色器的输出具有gl_FragColor.a === 0时,three.js似乎没有为透明四边形写入深度值。

我已经设置了renderer.sortObjects = false。我在z = 0处有背景,在z = 1处有透明四边形,并且在z = 0.5处有要被深度缓冲区屏蔽的对象。

对于透明四边形,我设置了material.transparent = false,但它显示的不是透明四边形上的背景景色,而是清晰的颜色。

以下是我如何创建透明四边形:

let uniforms = {
    color: {
        type: 'v4',
        value: new three.Vector4(0.2, 0.8, 0.8, 1),
    },
    opacity: {
        type: 'f',
        value: 1,
    },
    region_left: {
        type: 'f',
        value: -1,
    },
    region_right: {
        type: 'f',
        value: -1,
    },
    region_top: {
        type: 'f',
        value: -1,
    },
    region_bottom: {
        type: 'f',
        value: -1,
    },
}
let vertex_shader =
`varying vec2 screen_space_position;
void main()
{
    vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
    screen_space_position = mvPosition.xy;
    gl_Position = projectionMatrix * mvPosition;
}`
let fragment_shader =
`uniform vec4 color;
uniform float opacity;
uniform float region_left;
uniform float region_right;
uniform float region_top;
uniform float region_bottom;
varying vec2 screen_space_position;
void main()
{
    // The following just checks a 2D box I've defined in 'screen space'
    // (really it's camera space, but I'm using an ortho camera where units
    // are pixels) and sets the opacity to zero if the fragment is inside
    // it.
    float final_opacity = opacity;
    vec2 position = screen_space_position;
    if (position.x >= region_left && position.x <= region_right &&
        position.y >= region_top && position.y <= region_bottom)
    {
        final_opacity = 0.0;
    }
    gl_FragColor = vec4(color.rgb, color.a * final_opacity);
    // I also tried just bypassing the above code and rendering 0 alpha
    // across the entire quad
    //gl_FragColor = vec4(1, 1, 1, 0);
}`
// Create geometry that represents a quad
let geometry = new three.PlaneGeometry(1, 1)
// PlaneGeometry assumes the lower left corner is (0, 0). This makes it
// so the upper left corner is (0, 0).
geometry.scale(1, -1, 1)
let material = new three.ShaderMaterial({
    uniforms,
    vertexShader: vertex_shader,
    fragmentShader: fragment_shader,
    transparent: true,
})
let mesh = new three.Mesh(geometry, material)
// Center the mesh
mesh.position.set(0.5, 0.5, 0)
let root = new three.Object3D()
root.add(mesh)

以下从r73开始工作。小心控制three.MeshrenderOrderposition属性是解决方案。

首先,我通过删除renderer.sortObjects = false行重新打开排序。其次,我确保要遮罩的对象的z位置将它们放置在透明四边形后面。

最后,我为我想要遮罩的每个场景对象的three.Mesh对象设置了mesh.renderOrder = 1(即,在使用renderOrder < 1的任何其他网格之后渲染该网格),为透明四边形设置了mesh.renderOrder = 0.5renderOrder的默认值为0,因此在透明四边形和遮罩对象之前,任何网格的renderOrder I都没有明确地将渲染设置为正常。

请注意,必须为每个网格设置renderOrder。此属性不会传播到子Object3D对象。