Почему LWJGL (OpenGL) drawElements не рисует? - PullRequest
2 голосов
/ 02 февраля 2020

Я попытался следовать учебнику LWJGL 3.2+ на drawElements и заставить мое приложение LWJGL нарисовать квад. Мой код работает успешно, но ничего не рисует (кроме окна basi c), независимо от того, где я запускаю свой метод loopCycle, который должен рисовать квад. Я предполагаю, что это связано с переходом с Display (Tutorial) на GLFW (мой код)? Я видел некоторые посты, в которых говорится о матрицах проекции, вида и модели, которые я не использую (afaik). В этом ли проблема, почему она не отображается?

package org.tempest.game;

import org.lwjgl.*;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*;
import org.lwjgl.system.*;

import java.nio.*;

import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryStack.*;
import static org.lwjgl.system.MemoryUtil.*;

public class Graphics {

    // The window handle
    private long window;

    // Window setup
    private final String WINDOW_TITLE = "Test";
    // 1920x1080, 1600x900 and 1200x675 are all 16:9 ratios
    private final int WIDTH = 320;
    private final int HEIGHT = 240;

    // Quad variables
    private int vaoId = 0;
    private int vboId = 0;
    private int vboiId = 0;
    private int indicesCount = 0;

    public static void main(String[] args) {
        new Graphics().run();
    }

    public void run() {
        System.out.println("Hello LWJGL " + Version.getVersion() + "!");
        init();

        setupQuad();
        loop();
        destroyOpenGL();

        // Free the window callbacks and destroy the window
        glfwFreeCallbacks(window);
        glfwDestroyWindow(window);

        // Terminate GLFW and free the error callback
        glfwTerminate();
        glfwSetErrorCallback(null).free();
    }

    private void init() {
        // Setup an error callback. The default implementation
        // will print the error message in System.err.
        GLFWErrorCallback.createPrint(System.err).set();

        // Initialize GLFW. Most GLFW functions will not work before doing this.
        if ( !glfwInit() )
            throw new IllegalStateException("Unable to initialize GLFW");

        // Configure GLFW
        glfwDefaultWindowHints(); // optional, the current window hints are already the default
        glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
        glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);

        // Create the window
        window = glfwCreateWindow(WIDTH, HEIGHT, WINDOW_TITLE, NULL, NULL);
        if ( window == NULL )
            throw new RuntimeException("Failed to create the GLFW window");

        // Setup a key callback. It will be called every time a key is pressed, repeated or released.
        glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
            if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
                glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop
        });

        // Get the thread stack and push a new frame
        try ( MemoryStack stack = stackPush() ) {
            IntBuffer pWidth = stack.mallocInt(1); // int*
            IntBuffer pHeight = stack.mallocInt(1); // int*

            // Get the window size passed to glfwCreateWindow
            glfwGetWindowSize(window, pWidth, pHeight);

            // Get the resolution of the primary monitor
            GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());

            // Center the window
            glfwSetWindowPos(
                    window,
                    (vidmode.width() - pWidth.get(0)) / 2,
                    (vidmode.height() - pHeight.get(0)) / 2
            );
        } // the stack frame is popped automatically

        // Make the OpenGL context current
        glfwMakeContextCurrent(window);
        // Enable v-sync with 1
        glfwSwapInterval(0);

        // Make the window visible
        glfwShowWindow(window);
    }

    private void loop() {
        // Initialize variables for fps calculation
        long time_start = System.nanoTime();
        int frames = 0;
        final double check_fps_time = 1d;

        // Set the clear color
        glClearColor(0.2f, 0.2f, 0.2f, 0.0f);
        // TODO Where to initialize this?
        //GL11.glViewport(0, 0, WIDTH, HEIGHT);

        // Run the rendering loop until the user has attempted to close
        // the window or has pressed the ESCAPE key.
        while ( !glfwWindowShouldClose(window) ) {
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer

            glfwSwapBuffers(window); // swap the color buffers

            // Count, calculate and display fps
            frames++;
            long time_now = System.nanoTime();
            if ((double)(time_now - time_start)/1000000000 > check_fps_time) {
                int fps_prediction = (int)(frames/check_fps_time);
                System.out.println("FPS: " + fps_prediction);
                frames = 0;
                time_start = time_now;
            }

            // Poll for window events. The key callback above will only be
            // invoked during this call.
            glfwPollEvents();

            loopCycle();
        }
    }

    public void setupQuad() {
        GL.createCapabilities();
        // Vertices, the order is not important.
        float[] vertices = {
                -0.5f, 0.5f, 0f,    // Left top         ID: 0
                -0.5f, -0.5f, 0f,   // Left bottom      ID: 1
                0.5f, -0.5f, 0f,    // Right bottom     ID: 2
                0.5f, 0.5f, 0f      // Right left       ID: 3
        };
        // Sending data to OpenGL requires the usage of (flipped) byte buffers
        FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length);
        verticesBuffer.put(vertices);
        verticesBuffer.flip();

        // OpenGL expects to draw vertices in counter clockwise order by default
        byte[] indices = {
                // Left bottom triangle
                0, 1, 2,
                // Right top triangle
                2, 3, 0
        };
        indicesCount = indices.length;
        ByteBuffer indicesBuffer = BufferUtils.createByteBuffer(indicesCount);
        indicesBuffer.put(indices);
        indicesBuffer.flip();

        // Create a new Vertex Array Object in memory and select it (bind)
        // A VAO can have up to 16 attributes (VBOs) assigned to it by default
        vaoId = GL30.glGenVertexArrays();
        GL30.glBindVertexArray(vaoId);

        // Create a new Vertex Buffer Object in memory and select it (bind)
        // A VBO is a collection of Vectors which in this case resemble the location of each vertex.
        vboId = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verticesBuffer, GL15.GL_STATIC_DRAW);
        // Put the VBO in the attributes list at index 0
        GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0);
        // Deselect (bind to 0) the VBO
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
        // Deselect (bind to 0) the VAO
        GL30.glBindVertexArray(0);

        // Create a new VBO for the indices and select it (bind)
        vboiId = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiId);
        GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL15.GL_STATIC_DRAW);
        // Deselect (bind to 0) the VBO
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
    }

    public void loopCycle() {
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

        // Bind to the VAO that has all the information about the vertices
        GL30.glBindVertexArray(vaoId);
        GL20.glEnableVertexAttribArray(0);

        // Bind to the index VBO that has all the information about the order of the vertices
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiId);

        // Draw the vertices
        GL11.glDrawElements(GL11.GL_TRIANGLES, indicesCount, GL11.GL_UNSIGNED_BYTE, 0);

        // Put everything back to default (deselect)
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
        GL20.glDisableVertexAttribArray(0);
        GL30.glBindVertexArray(0);
    }
    public void destroyOpenGL() {
        // Disable the VBO index from the VAO attributes list
        GL20.glDisableVertexAttribArray(0);

        // Delete the vertex VBO
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
        GL15.glDeleteBuffers(vboId);

        // Delete the index VBO
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
        GL15.glDeleteBuffers(vboiId);

        // Delete the VAO
        GL30.glBindVertexArray(0);
        GL30.glDeleteVertexArrays(vaoId);
    }

    public int getWIDTH() {
        return WIDTH;
    }

    public int getHEIGHT() {
        return HEIGHT;
    }
}

Я новичок и, вероятно, есть количество вещей, которые мне нужно изучить, чтобы сделать эту работу. Мне бы очень хотелось услышать несколько советов о том, на что обратить внимание, чтобы заставить мое приложение что-то сделать, чтобы я мог оттуда что-то сделать. Спасибо огромное! :)

1 Ответ

3 голосов
/ 02 февраля 2020

Существует как минимум одна проблема с этим кодом - он вызывает сброс / отрисовку / обмен в неправильном порядке. В основном с OpenGL, основной l oop должен сначала вызвать clear(), нарисовать некоторые вещи, а затем вызвать swapBuffers() для отображения содержимого буфера.

Пример вместо этого: звонки clear (хорошо, очистить буфер), поменять местами буферы (здесь показано пустое окно, поскольку буфер очищен), и затем dr aws куча вещей в буфер. Но содержимое буфера никогда не отображается (поскольку в следующем цикле первая операция снова очищается ()).

Ниже слегка измененного кода; это dr aws белый прямоугольник - я не совсем уверен насчет использования glBindBuffer (в прошлом я использовал drawLine и drawTriangle), но это начало.

package sample;

import org.lwjgl.*;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*;
import org.lwjgl.system.*;
import java.nio.*;
import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryStack.*;
import static org.lwjgl.system.MemoryUtil.*;

public class DrawExample {

  // The window handle
  private long window;

  // Window setup
  private final String WINDOW_TITLE = "Test";
  // 1920x1080, 1600x900 and 1200x675 are all 16:9 ratios
  private final int WIDTH = 320;
  private final int HEIGHT = 240;

  // Quad variables
  private int vaoId = 0;
  private int vboId = 0;
  private int vboiId = 0;
  private int indicesCount = 0;

  public static void main(String[] args) {
    new DrawExample().run();
  }

  public void run() {
    System.out.println("Hello LWJGL " + Version.getVersion() + "!");
    init();

    setupQuad();
    loop();
    destroyOpenGL();

    // Free the window callbacks and destroy the window
    glfwFreeCallbacks(window);
    glfwDestroyWindow(window);

    // Terminate GLFW and free the error callback
    glfwTerminate();
    glfwSetErrorCallback(null).free();
  }

  private void init() {
    // Setup an error callback. The default implementation
    // will print the error message in System.err.
    GLFWErrorCallback.createPrint(System.err).set();

    // Initialize GLFW. Most GLFW functions will not work before doing this.
    if (!glfwInit())
      throw new IllegalStateException("Unable to initialize GLFW");

    // Configure GLFW
    glfwDefaultWindowHints(); // optional, the current window hints are already the default
    glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
    glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);

    // Create the window
    window = glfwCreateWindow(WIDTH, HEIGHT, WINDOW_TITLE, NULL, NULL);
    if (window == NULL)
      throw new RuntimeException("Failed to create the GLFW window");

    // Setup a key callback. It will be called every time a key is pressed, repeated or released.
    glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
      if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE)
        glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop
    });


    // Get the resolution of the primary monitor
    GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
    // Get the thread stack and push a new frame
    try (MemoryStack stack = stackPush()) {
      IntBuffer pWidth = stack.mallocInt(1); // int*
      IntBuffer pHeight = stack.mallocInt(1); // int*

      // Get the window size passed to glfwCreateWindow
      glfwGetWindowSize(window, pWidth, pHeight);



      // Center the window
      glfwSetWindowPos(window, (vidmode.width() - pWidth.get(0)) / 2,
          (vidmode.height() - pHeight.get(0)) / 2);
    } // the stack frame is popped automatically

    // Make the OpenGL context current
    glfwMakeContextCurrent(window);
    // Enable v-sync with 1
    glfwSwapInterval(1);

    // Make the window visible
    glfwShowWindow(window);
  }

  private void loop() {
    // Initialize variables for fps calculation
    long time_start = System.nanoTime();
    int frames = 0;
    final double check_fps_time = 1d;

    // Set the clear color
    glClearColor(0.2f, 0.2f, 0.2f, 0.0f);
    // TODO Where to initialize this?
    // GL11.glViewport(0, 0, WIDTH, HEIGHT);

    // Run the rendering loop until the user has attempted to close
    // the window or has pressed the ESCAPE key.
    while (!glfwWindowShouldClose(window)) {
      // Count, calculate and display fps
      frames++;
      long time_now = System.nanoTime();
      if ((double) (time_now - time_start) / 1000000000 > check_fps_time) {
        int fps_prediction = (int) (frames / check_fps_time);
        System.out.println("FPS: " + fps_prediction);
        frames = 0;
        time_start = time_now;
      }

      // Poll for window events. The key callback above will only be
      // invoked during this call.
      glfwPollEvents();

      loopCycle();
      glfwSwapBuffers(window); // swap the color buffers

    }
  }

  public void setupQuad() {
    GL.createCapabilities();
    // Vertices, the order is not important.
    float[] vertices = {-0.5f, 0.5f, 0f, // Left top ID: 0
        -0.5f, -0.5f, 0f, // Left bottom ID: 1
        0.5f, -0.5f, 0f, // Right bottom ID: 2
        0.5f, 0.5f, 0f // Right left ID: 3
    };
    // Sending data to OpenGL requires the usage of (flipped) byte buffers
    FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length);
    verticesBuffer.put(vertices);
    verticesBuffer.flip();

    // OpenGL expects to draw vertices in counter clockwise order by default
    byte[] indices = {
        // Left bottom triangle
        0, 1, 2,
        // Right top triangle
        2, 3, 0};
    indicesCount = indices.length;
    ByteBuffer indicesBuffer = BufferUtils.createByteBuffer(indicesCount);
    indicesBuffer.put(indices);
    indicesBuffer.flip();

    // Create a new Vertex Array Object in memory and select it (bind)
    // A VAO can have up to 16 attributes (VBOs) assigned to it by default
    vaoId = GL30.glGenVertexArrays();
    GL30.glBindVertexArray(vaoId);

    // Create a new Vertex Buffer Object in memory and select it (bind)
    // A VBO is a collection of Vectors which in this case resemble the location of each vertex.
    vboId = GL15.glGenBuffers();
    GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
    GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verticesBuffer, GL15.GL_STATIC_DRAW);
    // Put the VBO in the attributes list at index 0
    GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0);
    // Deselect (bind to 0) the VBO
    GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
    // Deselect (bind to 0) the VAO
    GL30.glBindVertexArray(0);

    // Create a new VBO for the indices and select it (bind)
    vboiId = GL15.glGenBuffers();
    GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiId);
    GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL15.GL_STATIC_DRAW);
    // Deselect (bind to 0) the VBO
    GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
  }

  public void loopCycle() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Bind to the VAO that has all the information about the vertices
    GL30.glBindVertexArray(vaoId);
    GL20.glEnableVertexAttribArray(0);

    // Bind to the index VBO that has all the information about the order of the vertices
    GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiId);

    // Draw the vertices
    GL11.glDrawElements(GL11.GL_TRIANGLES, indicesCount, GL11.GL_UNSIGNED_BYTE, 0);

    // Put everything back to default (deselect)
    GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
    GL20.glDisableVertexAttribArray(0);
    GL30.glBindVertexArray(0);
  }

  public void destroyOpenGL() {
    // Disable the VBO index from the VAO attributes list
    GL20.glDisableVertexAttribArray(0);

    // Delete the vertex VBO
    GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
    GL15.glDeleteBuffers(vboId);

    // Delete the index VBO
    GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
    GL15.glDeleteBuffers(vboiId);

    // Delete the VAO
    GL30.glBindVertexArray(0);
    GL30.glDeleteVertexArrays(vaoId);
  }

  public int getWIDTH() {
    return WIDTH;
  }

  public int getHEIGHT() {
    return HEIGHT;
  }
}
...