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

WebGL中如何让透明元素正确显示后方元素?

解决WebGL透明立方体无法显示后方物体的问题

你的问题出在透明物体的渲染逻辑上,核心是渲染顺序和深度缓冲设置错误,导致透明立方体只显示画布背景,而看不到后方的立方体。以下是具体问题分析和修复方案:

问题根源

  1. 渲染顺序混乱:当前代码把所有立方体(透明+不透明)一次性绘制,没有区分先后顺序。透明物体需要从离相机最远到最近的顺序绘制,才能让后方物体的颜色透过透明层叠加显示。
  2. 深度缓冲处理错误:代码里先禁用了DEPTH_TEST,之后又重新启用,但没有针对透明物体关闭深度写入(gl.depthMask(false))。透明物体如果写入深度缓冲,会直接挡住后方所有物体,包括其他透明或不透明的立方体。

修复步骤

1. 拆分渲染流程:先画不透明物体,再画透明物体

把立方体数据分成两组:不透明立方体的顶点/颜色数据,透明立方体的顶点/颜色数据。

2. 正确设置深度和混合参数

  • 绘制不透明物体时:启用深度测试和深度写入,正常渲染(不需要混合)。
  • 绘制透明物体时:启用混合、关闭深度写入,并且按距离相机从远到近排序后绘制。

3. 修改后的代码示例

function draw() {
    let opaqueColorData = [];
    let transparentColorData = [];
    let opaqueVertices = [];
    let transparentVertices = [];
    let transparentObjects = [];

    // 拆分不透明和透明立方体的数据
    for (let i = 0; i < (N + extra) * N**2; i++){
        // 替换成你实际获取单立方体顶点和颜色的逻辑
        const cubeVertices = flu.getCubeVertices(i);
        const cubeColor = flu.dyeIdx(i);
        const cubeCenter = flu.getCubeCenter(i); // 获取立方体中心坐标用于排序

        if (cubeColor[3] < 1.0) {
            transparentObjects.push({
                vertices: cubeVertices,
                color: cubeColor,
                center: cubeCenter
            });
        } else {
            opaqueVertices.push(...cubeVertices);
            opaqueColorData.push(...cubeColor);
        }
    }

    // --- 着色器/程序/缓冲区初始化(建议抽离到draw外,只执行一次)---
    // 顶点着色器、片元着色器、程序创建逻辑和你原代码一致,此处省略重复内容

    // --- 渲染开始 ---
    gl.clearColor(0.0, 0.0, 0.0, 0.0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    gl.useProgram(program);
    // 矩阵计算(和你原代码一致)
    let matrix = mat4.create();
    mat4.translate(matrix, matrix, [0, .75, -4]);
    let projectionMatrix = mat4.create();
    mat4.perspective(projectionMatrix, 50 * Math.PI / 180, canvas.width / canvas.height, 1e-4, 1e4);
    mat4.rotateX(matrix, matrix, Math.PI/5);
    mat4.rotateY(matrix, matrix, Math.PI);
    let outMatrix = mat4.create();
    mat4.multiply(outMatrix, projectionMatrix, matrix);
    gl.uniformMatrix4fv(uniformLocation.matrix, false, outMatrix);

    // 1. 绘制不透明物体
    gl.enable(gl.DEPTH_TEST);
    gl.depthMask(true); // 开启深度写入
    gl.disable(gl.BLEND);

    // 绑定不透明物体缓冲区并绘制
    gl.bindBuffer(gl.ARRAY_BUFFER, opaquePositionBuffer);
    gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
    gl.bindBuffer(gl.ARRAY_BUFFER, opaqueColorBuffer);
    gl.vertexAttribPointer(colorLocation, 4, gl.FLOAT, false, 0, 0);
    gl.drawArrays(gl.TRIANGLES, 0, opaqueVertices.length / 3);

    // 2. 透明物体按距离相机从远到近排序
    transparentObjects.sort((a, b) => {
        // 计算物体中心在视图空间的深度值,z值越大离相机越远
        const aViewPos = vec4.transformMat4(vec4.create(), a.center, outMatrix);
        const bViewPos = vec4.transformMat4(vec4.create(), b.center, outMatrix);
        return bViewPos[2] - aViewPos[2];
    });

    // 3. 绘制透明物体
    gl.enable(gl.BLEND);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    gl.depthMask(false); // 关闭深度写入,避免挡住后方物体
    gl.enable(gl.DEPTH_TEST); // 保留深度测试,防止透明物体穿过前方不透明物体

    // 逐个绘制排序后的透明物体
    for (const obj of transparentObjects) {
        // 动态创建缓冲区或复用缓冲区,此处简化处理
        const tempPosBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, tempPosBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(obj.vertices), gl.STATIC_DRAW);
        gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);

        const tempColorBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, tempColorBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(obj.color), gl.STATIC_DRAW);
        gl.vertexAttribPointer(colorLocation, 4, gl.FLOAT, false, 0, 0);

        gl.drawArrays(gl.TRIANGLES, 0, obj.vertices.length / 3);
    }

    // 恢复默认状态(可选)
    gl.depthMask(true);
    gl.disable(gl.BLEND);
}

额外优化建议

  • 复用着色器和缓冲区:当前代码每次draw都重新创建着色器、程序和缓冲区,会严重拖慢性能。建议把初始化逻辑抽离到draw函数外部,只执行一次。
  • 批量处理透明物体:示例中为每个透明物体创建临时缓冲区是简化写法,实际可以把排序后的透明物体数据合并到统一缓冲区,减少绘制调用次数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 22:17:02