使用SSBO批量渲染2D Tile时仅渲染单个纹素的问题
2D Tile批量渲染纹理坐标错误排查与修复
我正在开发2D Tile渲染器,因切换VAO性能开销过高,采用批量渲染方案:使用单张纹理图集,通过修改纹理坐标为每个Tile分配对应纹理,借助SSBO存储数据,在着色器中通过gl_InstanceId索引对应数据。这是我第二次提问,已应用上次建议修正了Tile位置偏移问题,但当前所有Tile无法正确应用纹理,仅显示单个纹素的纯色效果。通过统一纹理坐标测试确认纹理本身可用,问题定位在纹理坐标的批量处理逻辑,现寻求正确的纹理坐标批量处理方法。
最小复现代码
package test; import gfx.Render; import gfx.ShaderProgram; import gfx.Texture; import org.joml.Matrix4f; import org.lwjgl.glfw.GLFWKeyCallback; import org.lwjgl.opengl.GL; import org.lwjgl.system.MemoryUtil; import java.nio.FloatBuffer; import java.nio.IntBuffer; import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL15.*; import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW; import static org.lwjgl.opengl.GL20.glEnableVertexAttribArray; import static org.lwjgl.opengl.GL20.glVertexAttribPointer; import static org.lwjgl.opengl.GL30.*; import static org.lwjgl.opengl.GL31.glDrawElementsInstanced; import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER; import static org.lwjgl.system.MemoryUtil.memFree; public class MinimalReproducibleExample { private int mapWidth = 50, mapHeight = 50; private float tileSize = 0.1f; private int vaoId; private int iboId; public float camX = 0.0f, camY = 0.0f; public float scale = 1.0f; private int usboId; Texture tt; ShaderProgram shaderProgram; private float[] vertices; private final int[] indices = new int[]{ 0, 1, 3, 3, 1, 2, }; private FloatBuffer offsetBuffer; private FloatBuffer tpBuffer; long windowId; private Matrix4f projectionMatrix; static float safeZoneX, safeZoneY; private void setupOrthographicProjection() { float orthoHeight = 10.0f; // Adjust this based on your scene's needs float orthoWidth = orthoHeight * 1080/920; safeZoneX = orthoWidth; safeZoneY = orthoHeight; projectionMatrix = new Matrix4f().setOrtho2D(-orthoWidth / 2, orthoWidth / 2, -orthoHeight / 2, orthoHeight / 2); } public MinimalReproducibleExample() { if(!glfwInit()) System.exit(1); windowId = glfwCreateWindow(1080, 920, "TEST WINDOW", 0, 0); if(windowId == 0) System.exit(1); glfwMakeContextCurrent(windowId); glfwShowWindow(windowId); GL.createCapabilities(); setupOrthographicProjection(); ShaderProgram.loadShaderPrograms(); tt = Texture.loadTexture("texture_atlases/atlas-1.png"); float[] offsets = new float[mapWidth * mapHeight * 2]; float[] texPos = new float[mapWidth * mapHeight * 8]; vertices = new float[]{ -tileSize, tileSize, 0.0f, -tileSize, -tileSize, 0.0f, tileSize, -tileSize, 0.0f, tileSize, tileSize, 0.0f, }; float[] test = new float[]{ 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f }; int index = 0; for (int x = 0; x < mapWidth; x++) { for (int y = 0; y < mapHeight; y++) { float xPos = (float) x * tileSize; float yPos = (float) y * tileSize; offsets[index++] = xPos; offsets[index++] = yPos; } } index = 0; for(int i = 0; i < mapWidth * mapHeight * 8; i++) { texPos[i] = test[index]; index++; if(index >= 8) { index = 0; } } offsetBuffer = MemoryUtil.memAllocFloat(offsets.length); offsetBuffer.put(offsets).flip(); tpBuffer = MemoryUtil.memAllocFloat(texPos.length); tpBuffer.put(texPos).flip(); shaderProgram = ShaderProgram.getShaderProgram("test_program"); ShaderProgram.useProgram("test_program"); vaoId = glGenVertexArrays(); glBindVertexArray(vaoId); // Store vertex data off-heap FloatBuffer vertexData = MemoryUtil.memAllocFloat(vertices.length); vertexData.put(vertices).flip(); // Create and bind VBO for vertices int vboId = glGenBuffers(); glBindBuffer(GL_ARRAY_BUFFER, vboId); glBufferData(GL_ARRAY_BUFFER, vertexData, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); glEnableVertexAttribArray(0); memFree(vertexData); // Store index data off-heap IntBuffer indexData = MemoryUtil.memAllocInt(indices.length); indexData.put(indices).flip(); // Create and bind IBO for indices iboId = glGenBuffers(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboId); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexData, GL_STATIC_DRAW); memFree(indexData); // Create and bind the SSBO for offsets int ssboId = glGenBuffers(); glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboId); glBufferData(GL_SHADER_STORAGE_BUFFER, offsetBuffer, GL_STATIC_DRAW); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssboId); // Create and bind the SSBO for texture coordinates usboId = glGenBuffers(); glBindBuffer(GL_SHADER_STORAGE_BUFFER, usboId); glBufferData(GL_SHADER_STORAGE_BUFFER, tpBuffer, GL_STATIC_DRAW); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, usboId); // Unbind VAO glBindVertexArray(0); glfwSetKeyCallback(windowId, new GLFWKeyCallback() { @Override public void invoke(long l, int i, int i1, int i2, int i3) { if(i == GLFW_KEY_F1) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glDisable(GL_TEXTURE_2D); } if(i == GLFW_KEY_F2) { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glEnable(GL_TEXTURE_2D); } } }); while(!glfwWindowShouldClose(windowId)) { glfwPollEvents(); //handle camera movement; if(glfwGetKey(windowId, GLFW_KEY_D) == GLFW_TRUE) { camX -= 0.01f; } if(glfwGetKey(windowId, GLFW_KEY_A) == GLFW_TRUE) { camX += 0.01f; } if(glfwGetKey(windowId, GLFW_KEY_W) == GLFW_TRUE) { camY -= 0.01f; } if(glfwGetKey(windowId, GLFW_KEY_S) == GLFW_TRUE) { camY += 0.01f; } if(glfwGetKey(windowId, GLFW_KEY_E) == GLFW_TRUE) { scale += 0.001f; } if(glfwGetKey(windowId, GLFW_KEY_Q) == GLFW_TRUE) { scale -= 0.001f; } if(scale >=10.0f) { scale = 10.0f; } if(scale<=0.01f) { scale = 0.01f; } render(); } glfwDestroyWindow(windowId); glfwTerminate(); } float tick = 0; public void render() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); tick += 0.01f; ShaderProgram.useProgram("test_program"); glBindVertexArray(vaoId); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboId); shaderProgram.setUniform("delta", tick); shaderProgram.setUniform("projectionMatrix", projectionMatrix); shaderProgram.setUniform("texture_sampler", 0); // Ensure correct texture unit shaderProgram.setUniform("scale", scale); shaderProgram.setUniform("camPos", camX, camY); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, tt.getId()); glDrawElementsInstanced(GL_TRIANGLES, indices.length, GL_UNSIGNED_INT, 0, mapWidth * mapHeight); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindVertexArray(0); ShaderProgram.unbindProgram(); glfwSwapBuffers(windowId); } public static void main(String[] args) { new MinimalReproducibleExample(); } }
着色器代码
片段着色器
#version 460 out vec4 fragColor; uniform sampler2D texture_sampler; in vec2 outTP; void main() { fragColor = texture(texture_sampler, outTP); }
顶点着色器
#version 460 layout(location = 0) in vec3 position; layout(std430, binding = 0) buffer OffsetPositions { vec2 offsets[]; }; layout(std430, binding = 1) buffer TexturePositions { vec2 textCoords[]; }; uniform float delta; uniform mat4 projectionMatrix; uniform vec2 camPos; uniform float scale; out vec2 outTP; void main() { int index = gl_InstanceID; vec2 offset = offsets[index]; outTP = textCoords[index]; gl_Position = projectionMatrix * vec4( position.xy * scale + offset * scale + camPos * scale, position.z, 1.0 ); }
问题代码片段
index = 0; for(int i = 0; i < mapWidth * mapHeight * 8; i++) { texPos[i] = test[index]; index++; if(index >= 8) { index = 0; } }
内容的提问来源于stack exchange,提问作者Austin Romney
相关产品推荐
相关产品推荐

