如何实现仅给半透明容器盒贴纹理、内部物品保持不透明?
问题描述
我想要绘制一个内部包含不透明物品的半透明容器盒,已制作用于半透明容器的纹理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
相关产品推荐
相关产品推荐

