如何使用LWJGL正确绘制半透明/透明纹理?
OpenGL透明纹理渲染异常问题
初始问题描述
花费大量时间实现屏幕端透明纹理绘制,但代码运行效果始终不符合预期:仅完全不透明的纹理可正常绘制,透明区域会被红色填充,无法实现透明效果。
疑问:使用paint.net绘制透明区域,该操作是否正确?
问题更新(附更新后代码)
目前完全透明的区域已正常显示,但半透明像素位置存在红色轮廓,需要让半透明区域也正常实现透明渲染;另外存在疑问:为何设置glClearColor的alpha参数不会产生任何效果。
当前glClearColor设置为红色、alpha值设为0,代码如下:
GL11.glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
纹理加载代码(slick-util实现)
PNGDecoder decoder = null; try { decoder = new PNGDecoder(new FileInputStream(Main.projectPath + path)); } catch (FileNotFoundException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } ByteBuffer buffer = ByteBuffer.allocateDirect(4 * decoder.getWidth() * decoder.getHeight()); try { decoder.decode(buffer, decoder.getWidth() * 4, PNGDecoder.RGBA); } catch (IOException e) { e.printStackTrace(); } buffer.flip(); int id = GL13.glGenTextures(); GL13.glBindTexture(GL13.GL_TEXTURE_2D, id); GL13.glPixelStorei(GL13.GL_UNPACK_ALIGNMENT, 1); GL13.glTexParameterf(GL13.GL_TEXTURE_2D, GL13.GL_TEXTURE_MIN_FILTER, GL13.GL_NEAREST); GL13.glTexParameterf(GL13.GL_TEXTURE_2D, GL13.GL_TEXTURE_MAG_FILTER, GL13.GL_NEAREST); width = decoder.getWidth(); height = decoder.getHeight(); GL13.glTexImage2D(GL13.GL_TEXTURE_2D, 0, GL13.GL_RGBA, decoder.getWidth(), decoder.getHeight(), 0, GL13.GL_RGBA, GL13.GL_UNSIGNED_BYTE, buffer); GL30.glGenerateMipmap(GL13.GL_TEXTURE_2D); GL13.glTexParameteri(GL13.GL_TEXTURE_2D, GL13.GL_TEXTURE_WRAP_S, GL13.GL_RGBA); GL13.glTexParameteri(GL13.GL_TEXTURE_2D, GL13.GL_TEXTURE_WRAP_T, GL13.GL_RGBA); GL13.glTexParameteri(GL13.GL_TEXTURE_2D, GL13.GL_TEXTURE_MIN_FILTER, GL13.GL_NEAREST_MIPMAP_NEAREST); GL13.glTexParameteri(GL13.GL_TEXTURE_2D, GL13.GL_TEXTURE_MAG_FILTER,GL13.GL_NEAREST); this.texture = new Texture(id); this.textureID = id;
纹理渲染代码
public void renderMesh(Mesh mesh) { GL30.glBindVertexArray(mesh.getVAO()); GL30.glEnableVertexAttribArray(0); GL30.glEnableVertexAttribArray(1); GL30.glEnableVertexAttribArray(2); GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, mesh.getIBO()); GL13.glActiveTexture(GL13.GL_TEXTURE0); GL13.glBindTexture(GL11.GL_TEXTURE_2D, mesh.getMaterial().getTextureID()); shader.bind(); GL11.glDrawElements(GL11.GL_TRIANGLES, mesh.getIndices().length, GL11.GL_UNSIGNED_INT, 0); shader.unbind(); GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0); GL30.glDisableVertexAttribArray(0); GL30.glDisableVertexAttribArray(1); GL30.glDisableVertexAttribArray(2); GL30.glBindVertexArray(0); }
需求:定位上述代码中存在的错误,实现正常的透明/半透明纹理渲染效果。
补充初始化与着色器代码
初始化代码
window = GLFW.glfwCreateWindow(width, height, title, isFullscreen ? GLFW.glfwGetPrimaryMonitor() : 0, 0); if (window == 0) { System.err.println("ERROR: Window wasn't created"); return; } GLFWVidMode videoMode = GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor()); windowPosX[0] = (videoMode.width() - width) / 2; windowPosY[0] = (videoMode.height() - height) / 2; GLFW.glfwSetWindowPos(window, windowPosX[0], windowPosY[0]); GLFW.glfwMakeContextCurrent(window); GL.createCapabilities(); GL11.glEnable(GL11.GL_DEPTH_TEST); GL11.glMatrixMode(GL11.GL_TEXTURE); GL11.glEnable(GL11.GL_TEXTURE_2D); GL11.glEnable(GL11.GL_BLEND); GL11.glEnable(GL11.GL_ALPHA_TEST); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); GL11.glPushMatrix(); createCallbacks(); GLFW.glfwShowWindow(window); GLFW.glfwSwapInterval(1);
顶点着色器代码
#version 460 core in vec3 position; in vec4 color; in vec2 textureCoord; out vec4 passColor; out vec2 passTextureCoord; void main() { gl_Position = vec4(position, 1.0); passColor = color; passTextureCoord = textureCoord; }
片段着色器代码
#version 330 core out vec4 FragColor; in vec2 passTextureCoord; uniform sampler2D texture1; void main() { vec4 texColor = texture(texture1, passTextureCoord); if(texColor.a < 0.1) discard; FragColor = texColor; }
问题修复方案
- paint.net绘制透明区域的操作完全正确,只要导出为带alpha通道的PNG格式即可正常被OpenGL加载使用。
- 半透明区域红色轮廓核心原因是纹理环绕参数配置错误:代码中将
GL_TEXTURE_WRAP_S、GL_TEXTURE_WRAP_T的参数值错误传入了GL_RGBA,这是像素格式枚举值,不是合法的纹理环绕模式,会导致纹理边缘采样时取到错误像素,透出清除色红色。将这两行代码修改为:
GL13.glTexParameteri(GL13.GL_TEXTURE_2D, GL13.GL_TEXTURE_WRAP_S, GL13.GL_CLAMP_TO_EDGE); GL13.glTexParameteri(GL13.GL_TEXTURE_2D, GL13.GL_TEXTURE_WRAP_T, GL13.GL_CLAMP_TO_EDGE);
glClearColor的alpha参数不生效是正常现象:默认窗口的帧缓冲区直接输出RGB值到显示器,alpha通道不会参与桌面窗口的透明混合,只有渲染到自定义离屏FBO、需要做后期混合处理时,清除色的alpha值才会产生实际作用。- 冗余代码清理:初始化代码中
glMatrixMode、glEnable(GL_TEXTURE_2D)、glPushMatrix、glEnable(GL_ALPHA_TEST)都是旧版固定管线API,在核心模式OpenGL下没有任何作用,可以直接删除,避免产生不必要的状态干扰。 - 半透明渲染注意事项:开启深度测试时渲染半透明物体,需要按照物体离相机从远到近的顺序绘制,否则半透明面片会被深度测试拦截,出现显示异常。
内容的提问来源于stack exchange,提问作者instance
相关产品推荐
相关产品推荐

