我是OpenGL的新手,我正在尝试绘制两个不同纹理的正方形。我使用lwjgl 3作为OpenGL的接口,但我相信对于在其他语言中使用OpenGL的人来说,OpenGL调用看起来应该很熟悉。我的主循环看起来像这样:
while (glfwWindowShouldClose(windowId) == GLFW_FALSE) {
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgramId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
// DRAW TEXTURE 1
specifyVertexAttributes(shaderProgramId);
glBindTexture(GL_TEXTURE_2D, texture1.getId());
glBindBuffer(GL_ARRAY_BUFFER, vbo1);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// DRAW TEXTURE 2
specifyVertexAttributes(shaderProgramId);
glBindTexture(GL_TEXTURE_2D, texture2.getId());
glBindBuffer(GL_ARRAY_BUFFER, vbo2);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(windowId);
glfwPollEvents();
}
当我注释掉绘制纹理2的代码时,纹理1绘制在正确的位置:
当我注释掉绘制纹理1的代码时,纹理2绘制在正确的位置:
但是当我尝试绘制两个纹理时,它们会切换位置:
我意识到上面的代码段可能不足以诊断此问题。我创建了一个独立的java类,其中包含我正在制作的所有OpenGL调用以绘制这些纹理:StandaloneMultiTextureExample。包含该文件的repo也使用gradle构建。对于愿意帮助检查回购并运行该示例类的任何人来说应该很容易。
编辑:StandaloneMultiTextureExample.java的副本(没有导入)
public class StandaloneMultiTextureExample {
private final GLFWErrorCallback errorCallback = new LoggingErrorCallback();
private final GLFWKeyCallback keyCallback = new ApplicationClosingKeyCallback();
public void run() {
if ( glfwInit() != GLFW_TRUE ) {
throw new IllegalStateException("Unable to initialize GLFW");
}
glfwSetErrorCallback(errorCallback);
int width = 225;
int height = 200;
long windowId = createWindow(width, height);
glfwSetKeyCallback(windowId, keyCallback);
glfwShowWindow(windowId);
GL.createCapabilities();
Texture texture1 = createTexture("multiTextureExample/texture1.png");
Texture texture2 = createTexture("multiTextureExample/texture2.png");
int shaderProgramId = createShaderProgram(
"multiTextureExample/textureShader.vert",
"multiTextureExample/textureShader.frag");
IntBuffer elements = BufferUtils.createIntBuffer(2 * 3);
elements.put(0).put(1).put(2);
elements.put(2).put(3).put(0);
elements.flip();
int ebo = glGenBuffers();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, elements, GL_STATIC_DRAW);
float x1 = 0;
float y1 = 0;
float x2 = x1 + texture1.getWidth();
float y2 = y1 + texture1.getHeight();
float x3 = 25;
float x4 = x3 + texture2.getWidth();
FloatBuffer texture1Vertices = BufferUtils.createFloatBuffer(4 * 7);
texture1Vertices.put(x1).put(y1).put(1).put(1).put(1).put(0).put(0);
texture1Vertices.put(x2).put(y1).put(1).put(1).put(1).put(1).put(0);
texture1Vertices.put(x2).put(y2).put(1).put(1).put(1).put(1).put(1);
texture1Vertices.put(x1).put(y2).put(1).put(1).put(1).put(0).put(1);
texture1Vertices.flip();
FloatBuffer texture2Vertices = BufferUtils.createFloatBuffer(4 * 7);
texture2Vertices.put(x3).put(y1).put(1).put(1).put(1).put(0).put(0);
texture2Vertices.put(x4).put(y1).put(1).put(1).put(1).put(1).put(0);
texture2Vertices.put(x4).put(y2).put(1).put(1).put(1).put(1).put(1);
texture2Vertices.put(x3).put(y2).put(1).put(1).put(1).put(0).put(1);
texture2Vertices.flip();
int vbo1 = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vbo1);
glBufferData(GL_ARRAY_BUFFER, texture1Vertices, GL_STATIC_DRAW);
int vbo2 = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vbo2);
glBufferData(GL_ARRAY_BUFFER, texture2Vertices, GL_STATIC_DRAW);
specifyUniformVariables(windowId, shaderProgramId);
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
while (glfwWindowShouldClose(windowId) == GLFW_FALSE) {
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgramId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
specifyVertexAttributes(shaderProgramId);
glBindTexture(GL_TEXTURE_2D, texture1.getId());
glBindBuffer(GL_ARRAY_BUFFER, vbo1);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
specifyVertexAttributes(shaderProgramId);
glBindTexture(GL_TEXTURE_2D, texture2.getId());
glBindBuffer(GL_ARRAY_BUFFER, vbo2);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(windowId);
glfwPollEvents();
}
}
private void specifyUniformVariables(long windowId, int shaderProgramId) {
int uniModel = getUniform(shaderProgramId, "model");
FloatBuffer model = BufferUtils.createFloatBuffer(16);
new Matrix4f().get(model);
glUniformMatrix4fv(uniModel, false, model);
FloatBuffer view = BufferUtils.createFloatBuffer(16);
new Matrix4f().get(view);
int uniView = getUniform(shaderProgramId, "view");
glUniformMatrix4fv(uniView, false, view);
WindowSize windowSize = getWindowSize(windowId);
int uniProjection = getUniform(shaderProgramId, "projection");
FloatBuffer projection = BufferUtils.createFloatBuffer(16);
new Matrix4f().ortho2D(0, windowSize.getWidth(), 0, windowSize.getHeight()).get(projection);
glUniformMatrix4fv(uniProjection, false, projection);
}
private void specifyVertexAttributes(int shaderProgramId) {
int stride = 7 * Float.BYTES;
int posAttrib = getAttribute(shaderProgramId, "position");
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, false, stride, 0);
int colAttrib = getAttribute(shaderProgramId, "color");
glEnableVertexAttribArray(colAttrib);
glVertexAttribPointer(colAttrib, 3, GL_FLOAT, false, stride, 2 * Float.BYTES);
int texAttrib = getAttribute(shaderProgramId, "texcoord");
glEnableVertexAttribArray(texAttrib);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, false, stride, 5 * Float.BYTES);
}
public long createWindow(int width, int height) {
glfwDefaultWindowHints();
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
long windowId = glfwCreateWindow(width, height, "Hello World!", NULL, NULL);
if (windowId == NULL) {
throw new RuntimeException("Failed to create the GLFW window");
}
// Get the resolution of the primary monitor
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
// Center our window
glfwSetWindowPos(
windowId,
(vidmode.width() - width) / 2,
(vidmode.height() - height) / 2
);
// Make the OpenGL context current
glfwMakeContextCurrent(windowId);
// Enable v-sync
glfwSwapInterval(1);
return windowId;
}
public WindowSize getWindowSize(long windowId) {
IntBuffer width = BufferUtils.createIntBuffer(1);
IntBuffer height = BufferUtils.createIntBuffer(1);
GLFW.glfwGetFramebufferSize(windowId, width, height);
return new WindowSize(width.get(), height.get());
}
public Texture createTexture(String textureResource) {
int textureId = glGenTextures();
glBindTexture(GL_TEXTURE_2D, textureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
ARGBImage image = new ImageService().loadClasspathImage(textureResource);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA8,
image.getWidth(),
image.getHeight(),
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
image.getContents());
return new Texture(textureId, image.getWidth(), image.getHeight());
}
public int createShaderProgram(String vertexResource, String fragmentResource) {
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
String vertexSource = getClasspathResource(vertexResource);
glShaderSource(vertexShader, vertexSource);
glCompileShader(vertexShader);
validateShaderCompilation(vertexShader);
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
String fragmentSource = getClasspathResource(fragmentResource);
glShaderSource(fragmentShader, fragmentSource);
glCompileShader(fragmentShader);
validateShaderCompilation(fragmentShader);
int shaderProgramId = glCreateProgram();
glAttachShader(shaderProgramId, vertexShader);
glAttachShader(shaderProgramId, fragmentShader);
glLinkProgram(shaderProgramId);
validateShaderProgram(shaderProgramId);
glUseProgram(shaderProgramId);
return shaderProgramId;
}
private static String getClasspathResource(String resourceName) {
URL url = Resources.getResource(resourceName);
try {
return Resources.toString(url, Charsets.UTF_8);
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
private static void validateShaderCompilation(int shader) {
int status = glGetShaderi(shader, GL_COMPILE_STATUS);
if (status != GL_TRUE) {
throw new RuntimeException(glGetShaderInfoLog(shader));
}
}
private static void validateShaderProgram(int shaderProgram) {
int status = glGetProgrami(shaderProgram, GL_LINK_STATUS);
if (status != GL_TRUE) {
throw new RuntimeException(glGetProgramInfoLog(shaderProgram));
}
}
public int getUniform(int shaderProgramId, String uniformName) {
int location = glGetUniformLocation(shaderProgramId, uniformName);
if (location == -1) {
throw new IllegalArgumentException("Could not find uniform: "
+ uniformName + " for shaderProgramId: " + shaderProgramId);
} else {
return location;
}
}
public int getAttribute(int shaderProgramId, String attribute) {
int location = glGetAttribLocation(shaderProgramId, attribute);
if (location == -1) {
throw new IllegalArgumentException("Could not find attribute: "
+ attribute + " for shaderProgramId: " + shaderProgramId);
} else {
return location;
}
}
public static void main(String[] args) {
new StandaloneMultiTextureExample().run();
}
}
答案 0 :(得分:1)
您通过在错误的时间绑定顶点缓冲区来错误地设置顶点数组。
当您使用顶点数组对象(VAO)渲染[1]时,GL_ARRAY_BUFFER
绑定的唯一时间是glVertexAttribPointer
调用。调用glVertexAttribPointer
获取当前绑定的GL_ARRAY_BUFFER
对象的名称,并将其与属性和VAO关联;之后,GL_ARRAY_BUFFER
绑定根本不重要,绑定另一个数组缓冲区不会以任何方式修改VAO。
在您的代码中,在致电specifyVertexAttributes
之前,请致电glBindBuffer
设置您的VAO 。这意味着glVertexAttribPointer
保存的数组缓冲区是前一个使用的缓冲区。在前两个示例中,您只需要在任何时间点绑定一个数组缓冲区,它就可以正常运行"因为前一帧的绑定缓冲区仍然存在并在下一帧中被读取;如果你在第一帧暂停你的程序,它可能会是黑色的。
您案例中的解决方案很简单;将glBindBuffer
调用移到specifyVertexAttributes
调用之上,以便glVertexAttribPointer
次调用读取正确的缓冲区。
请注意,不适用于GL_ELEMENT_ARRAY_BUFFER
绑定;每当你绑定一个新的绑定时,绑定都会保存在VAO中。
[1]您在技术上使用默认VAO,它仅在兼容性上下文中受支持,尽管它很容易创建和绑定一直使用的全局VAO。