首先,这是我第二次问这个问题。第一次,我得到了许多不同的技巧,我将它们应用于这个新问题。 目前,我正在开发 2D 图块渲染器。我发现切换 VAO 太消耗性能了。因此,我决定对瓷砖进行批处理。我已经能够以定义的网格大小渲染每个图块。我决定使用单个纹理图集,为了设置每个纹理,我只需更改它们的纹理坐标。我决定使用 SSBO 来完成此操作,并且在着色器中,我将使用 gl_InstanceId 来查找要使用的顶点或纹理坐标集。目前,我刚刚对所有这些应用了相同的纹理坐标,稍后我将更改它。
然而,问题在于,不是将纹理应用于每个图块,而是仅渲染一个纹理像素,从而产生一种纯色。
这是结果 非线框渲染
这是线框变体 线框渲染
最后,这是我的最小可重现示例; (注意,唯一的外部代码是纹理类。我之前测试过这个类,它有效。我把它剪掉了,因为我发现代码太长了。如果你想让我添加它,请告诉我。) 我知道它很长,但这就是所涉及的所有代码,并且我已经能够找到问题所在(也许?) 我认为这可能与纹理坐标有关。虽然,我不确定如何解决这个问题,甚至不知道问题是什么。
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;
}
}
现在上面的代码应该是问题所在。如何批量处理纹理坐标?
我找到答案了!我意识到,我可以只添加偏移量并重用纹理坐标,而不是传递所有纹理坐标数据!