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

如何实现仅给半透明容器盒贴纹理、内部物品保持不透明?

问题描述

我想要绘制一个内部包含不透明物品的半透明容器盒,已制作用于半透明容器的纹理whitebg.jpg,但当前代码实现后纹理同时应用到了容器盒和内部物品,希望实现仅给容器盒贴纹理,内部物品保持原本的不透明颜色。

当前实现代码

主函数

private static void CreateItems(List<Items> packedItems)
{
    Glut.glutInit();
    Glut.glutInitDisplayMode(Glut.GLUT_DOUBLE | Glut.GLUT_DEPTH);
    Glut.glutInitWindowSize(width, height);
    Glut.glutCreateWindow("pack");

    Glut.glutIdleFunc(OnRenderFrame);
    Glut.glutDisplayFunc(OnDisplay);

    Glut.glutKeyboardFunc(OnKeyboardDown);
    Glut.glutKeyboardUpFunc(OnKeyboardUp);
    Glut.glutMouseWheelFunc(OnMouseWheel);

    Glut.glutCloseFunc(OnClose);
    Glut.glutReshapeFunc(OnReshape);

    Gl.Disable(EnableCap.DepthTest);
    Gl.Enable(EnableCap.Blend);
    Gl.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);

    program = new ShaderProgram(VertexShader, FragmentShader);

    program.Use();
    program["projection_matrix"].SetValue(Matrix4.CreatePerspectiveFieldOfView(0.45f, (float)width / height, 0.1f, 1000f));
    program["view_matrix"].SetValue(Matrix4.LookAt(new Vector3(0, 0, 10), Vector3.Zero, new Vector3(0, 1, 0)));

    program["light_direction"].SetValue(new Vector3(0, 0, 1));
    program["enable_lighting"].SetValue(lighting);

    bgTexture = new Texture("whitebg.jpg"); 
}

OnRenderFrame实现

private static void OnRenderFrame()
{
    watch.Stop();
    float deltaTime = (float)watch.ElapsedTicks / System.Diagnostics.Stopwatch.Frequency;
    watch.Restart();

    // 旋转控制
    if (autoRotate)
    {
        xangle += deltaTime / 2;
        yangle += deltaTime;
    }
    if (right) yangle += deltaTime;
    if (left) yangle -= deltaTime;
    if (up) xangle -= deltaTime;
    if (down) xangle += deltaTime;

    // 视口与缓冲区清理
    Gl.Viewport(0, 0, width, height);
    Gl.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

    // 绑定着色器与纹理
    Gl.UseProgram(program);
    Gl.BindTexture(bgTexture);

    // 模型矩阵设置
    program["model_matrix"].SetValue(Matrix4.CreateRotationY(yangle) * Matrix4.CreateRotationX(xangle));
    program["enable_lighting"].SetValue(lighting);

    // 绘制内部物品
    for (int i = 0; i < virtual_items.Count; i++)
    {
        Gl.BindBufferToShaderAttribute(virtual_items[i], program, "vertexPosition");
        Gl.BindBufferToShaderAttribute(virtual_items_color[i], program, "vertexColor");
        Gl.BindBuffer(virtual_items_quads[i]);

        Gl.DrawElements(BeginMode.Quads, virtual_items_quads[i].Count, DrawElementsType.UnsignedInt, IntPtr.Zero);
    }

    // 绘制容器
    Gl.BindBufferToShaderAttribute(virtual_container, program, "vertexPosition");
    Gl.BindBufferToShaderAttribute(virtual_container_normals, program, "vertexNormal");
    Gl.BindBufferToShaderAttribute(virtual_container_UV, program, "vertexUV");
    Gl.BindBuffer(virtual_container_Quads);

    Gl.DrawElements(BeginMode.Quads, virtual_container_Quads.Count, DrawElementsType.UnsignedInt, IntPtr.Zero);

    Glut.glutSwapBuffers();
}
解决方案

核心问题是你在绘制所有物体前就绑定了纹理,且着色器没有区分是否启用纹理的逻辑,导致所有物体都采样了该纹理。按以下步骤修复:

1. 修改着色器,添加纹理控制开关

在顶点着色器和片段着色器中新增use_texture的uniform变量,用于区分物体是否使用纹理:

片段着色器示例

uniform bool use_texture;
uniform sampler2D bgTexture;
in vec4 vertexColor;
in vec2 vertexUV;
out vec4 fragColor;

void main() {
    if(use_texture) {
        // 容器使用纹理,并保持半透明
        fragColor = texture(bgTexture, vertexUV);
        fragColor.a = 0.5; // 可根据需求调整透明度
    } else {
        // 内部物品使用顶点颜色,强制不透明
        fragColor = vertexColor;
        fragColor.a = 1.0;
    }
}

2. 调整绘制逻辑,区分纹理使用场景

在OnRenderFrame中,先绘制内部物品(关闭纹理),再绘制容器(开启纹理并绑定):

private static void OnRenderFrame()
{
    // ... 原有旋转、视口清理等代码 ...

    Gl.UseProgram(program);
    program["model_matrix"].SetValue(Matrix4.CreateRotationY(yangle) * Matrix4.CreateRotationX(xangle));
    program["enable_lighting"].SetValue(lighting);

    // 1. 绘制内部物品:关闭纹理,使用顶点颜色
    program["use_texture"].SetValue(false);
    for (int i = 0; i < virtual_items.Count; i++)
    {
        Gl.BindBufferToShaderAttribute(virtual_items[i], program, "vertexPosition");
        Gl.BindBufferToShaderAttribute(virtual_items_color[i], program, "vertexColor");
        Gl.BindBuffer(virtual_items_quads[i]);

        Gl.DrawElements(BeginMode.Quads, virtual_items_quads[i].Count, DrawElementsType.UnsignedInt, IntPtr.Zero);
    }

    // 2. 绘制容器:开启纹理,绑定容器纹理
    program["use_texture"].SetValue(true);
    Gl.BindTexture(bgTexture);
    Gl.BindBufferToShaderAttribute(virtual_container, program, "vertexPosition");
    Gl.BindBufferToShaderAttribute(virtual_container_normals, program, "vertexNormal");
    Gl.BindBufferToShaderAttribute(virtual_container_UV, program, "vertexUV");
    Gl.BindBuffer(virtual_container_Quads);

    // 半透明物体绘制时关闭深度写入,避免遮挡内部物品
    Gl.DepthMask(false);
    Gl.DrawElements(BeginMode.Quads, virtual_container_Quads.Count, DrawElementsType.UnsignedInt, IntPtr.Zero);
    Gl.DepthMask(true);

    Glut.glutSwapBuffers();
}

3. 修复深度测试配置

原有代码禁用了深度测试,会导致半透明容器的绘制顺序错误,调整初始化代码:

// 启用深度测试,保证不透明物品正确遮挡
Gl.Enable(EnableCap.DepthTest);
Gl.Enable(EnableCap.Blend);
Gl.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 08:30:35