Текстуры OpenGL появляются в неправильных местах при соединении
Я новичок в 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. Репозиторий, содержащий этот файл, также создается с помощью 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();
}
}
1 ответ
Вы неправильно настраиваете свои вершинные массивы, связывая буфер вершин в неправильное время.
Когда вы используете Vertex Array Objects (VAO) для рендеринга [1], единственный раз, когда GL_ARRAY_BUFFER
привязка читается это glVertexAttribPointer
вызов. призвание glVertexAttribPointer
принимает имя привязанного в данный момент GL_ARRAY_BUFFER
возражать и связывать его с атрибутом VAO; после этого GL_ARRAY_BUFFER
Привязка не имеет значения вообще, а привязка другого буфера массива никак не изменит VAO.
В своем коде вы звоните specifyVertexAttributes
настроить VAO перед звонком glBindBuffer
, Это означает, что буфер массива, который glVertexAttribPointer
saves - предыдущий использованный В ваших первых двух примерах, когда вы когда-либо связываете только один буфер массива в любой момент времени, он "работает", потому что связанный буфер из предыдущего кадра сохраняется и читается в следующем кадре; Если вы приостановили свою программу на самом первом кадре, она, вероятно, будет черной.
Решение в вашем случае простое; переместить glBindBuffer
позвонить выше specifyVertexAttributes
позвоните, чтобы ваш glVertexAttribPointer
вызовы читают правильный буфер.
Обратите внимание, что это не относится к GL_ELEMENT_ARRAY_BUFFER
связывание; привязка сохраняется в VAO всякий раз, когда вы связываете новую.
[1] Технически вы используете стандарт VAO по умолчанию, который поддерживается только в контексте совместимости, хотя его легко создавать и связывать с глобальным VAO, который используется постоянно.