绘制在glDrawArrays上工作正常,但使用glDrawElements失败



我的着色器工作正常,我可以使用glDrawArrays进行绘制,但是很难让glDrawElements工作。我已经添加了注释,其中 lwjgl 函数调用与标准 openGL 不同。法典:

import org.lwjgl.Sys
import org.lwjgl.glfw._
import org.lwjgl.opengl._
import java.nio.ByteBuffer
import java.nio.FloatBuffer
import  org.lwjgl.glfw.Callbacks._
import  org.lwjgl.glfw.GLFW._
import  org.lwjgl.opengl.GL11._
import  org.lwjgl.opengl.GL15._
import  org.lwjgl.opengl.GL20._
import  org.lwjgl.opengl.GL30._
import  org.lwjgl.system.MemoryUtil._
import  org.lwjgl.BufferUtils._
import hands._
import javafx.scene.shape.CullFace

class Test {
    val vertex_positions:  Array[Float] = Array(
     -1.0f, -1.0f, 0.0f,  
     1.0f, -1.0f, 0.0f,
     -1.0f, 1.0f, 0.0f
    )
    val vertex_indices: Array[Int] = Array(
      0, 1, 2    
    )
    // We need to strongly reference callback instances.
    val errorCallback: GLFWErrorCallback = Callbacks.errorCallbackPrint();
    val keyCallback: GLFWKeyCallback = new GLFWKeyCallback() {
      @Override
      def invoke(window: Long , key: Int, scancode: Int , action: Int , mods: Int) {
        if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
          glfwSetWindowShouldClose(window, GL_TRUE) // We will detect this in our rendering loop
      }
    }
    val WIDTH = 800
    val HEIGHT = 600 
    def run(): Unit = {
            System.out.println("Hello LWJGL " + Sys.getVersion() + "!")  
            try {
                val vertBuffer = hands.createFlippedBuffer(vertex_positions)
                val indexBuffer = hands.createFlippedBuffer(vertex_indices)
                // Setup an error callback. The default implementation
                // will print the error message in System.err.
                glfwSetErrorCallback(errorCallback)
                // Initialize GLFW. Most GLFW functions will not work before doing this.
                if ( glfwInit() != GL_TRUE )
                    throw new IllegalStateException("Unable to initialize GLFW")
                // Configure our window
                glfwDefaultWindowHints() // optional, the current window hints are already the default
                glfwWindowHint(GLFW_VISIBLE, GL_FALSE) // the window will stay hidden after creation
                glfwWindowHint(GLFW_RESIZABLE, GL_TRUE) // the window will be resizable
                glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3)
                glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3)
                glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE)
                // Create the window
                val window = glfwCreateWindow(WIDTH, HEIGHT, "Hello World!", 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, keyCallback)
                // Get the resolution of the primary monitor
                val vidmode: ByteBuffer = glfwGetVideoMode(glfwGetPrimaryMonitor())
                // Center our window
                glfwSetWindowPos(
                        window,
                        (GLFWvidmode.width(vidmode) - WIDTH) / 2,
                        (GLFWvidmode.height(vidmode) - HEIGHT) / 2)
                // Make the OpenGL context current
                glfwMakeContextCurrent(window)
                // Enable v-sync
                glfwSwapInterval(1)
                // Make the window visible
                glfwShowWindow(window)
                // This line is critical for LWJGL's interoperation with GLFW's
                // OpenGL context, or any context that is managed externally.
                // LWJGL detects the context that is current in the current thread,
                // creates the ContextCapabilities instance and makes the OpenGL
                // bindings available for use.
                GLContext.createFromCurrent()
                //create shader, and use it as program
                val shader = new Shader()
                glUseProgram(shader.program)
                val vao = glGenVertexArrays()
                glBindVertexArray(vao)
                val vbo = glGenBuffers()
                glBindBuffer(GL_ARRAY_BUFFER, vbo)
                //http://javadoc.lwjgl.org/org/lwjgl/opengl/GL15.html#glBufferData%28int,%20java.nio.FloatBuffer,%20int%29
                glBufferData(GL_ARRAY_BUFFER,  vertBuffer, GL_STATIC_DRAW)
                //this function accepts false instead of GL_FALSE
                glVertexAttribPointer(0, 3, GL_FLOAT, false , 0, 0)
                glEnableVertexAttribArray(0)
                val ebo = glGenBuffers()
                glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo)
                //http://javadoc.lwjgl.org/org/lwjgl/opengl/GL15.html#glBufferData%28int,%20java.nio.ShortBuffer,%20int%29
                glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBuffer, GL_STATIC_DRAW)

                // Set the clear color
                glClearColor(0, 0, 0.4f, 1)
                // Run the rendering loop until the user has attempted to close
                // the window or has pressed the ESCAPE key.
                //glfwWindowShouldClose produces a GL_FALSE, instead of a boolean value
                while ( glfwWindowShouldClose(window) == GL_FALSE ) {
                glClear(GL_COLOR_BUFFER_BIT) // clear the framebuffer
                glBindVertexArray(vao)
                glEnableVertexAttribArray(0)
                glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo)
                //http://javadoc.lwjgl.org/org/lwjgl/opengl/GL11.html#glDrawElements%28int,%20java.nio.ShortBuffer%29
                glDrawElements(GL_TRIANGLES, indexBuffer)
                //glDrawArrays(GL_TRIANGLES, 0, 9)
                glfwSwapBuffers(window); // swap the color buffers
                // Poll for window events. The key callback above will only be
                // invoked during this call.
                glfwPollEvents();
            }
            // Release window and window callbacks            
            glfwDestroyWindow(window)
            keyCallback.release()
        } finally {
            // Terminate GLFW and release the GLFWerrorfun
            glfwTerminate()
            errorCallback.release()
        }
    }
}
object main{
      def main( args: Array[String] ) = {
          new Test().run();
      }
    }

glDrawElements()有两种变体,它们在定义和使用最后一个参数的方式上有很大不同:

  1. 一种以"数据"为论据。参数的确切类型取决于语言绑定,但它通常是 C/C++ 中的指针、Java 中的缓冲区等。

  2. 一个将整数偏移量作为参数。

如果缓冲区当前绑定到 GL_ELEMENT_ARRAY_BUFFER,则使用选项 2。作为最后一个参数给出的整数是相对于绑定缓冲区开头的偏移量(以字节为单位)。

如果没有缓冲区绑定到 GL_ELEMENT_ARRAY_BUFFER则使用选项 1。在这种情况下,最后一个参数直接指定索引数据。

代码中的问题是将两者混合在一起。您有一个缓冲区绑定,因此需要使用选项 2。但您使用的是选项 1。在您的情况下,正确的调用是:

glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0)

请注意,计数(第二个参数)以顶点为单位给出,而偏移量(最后一个参数)以字节为单位给出。由于您使用的是整个缓冲区,并且偏移量为 0,因此此处没有区别。但它是错误的常见来源。

在弱类型语言(如 C/C++)中,实际上只有一个glDrawElements()调用,选项 2 的偏移量被强制转换为指针。这也是如果您错误地使用来自选项 1 的调用而不是 OpenGL 错误,则会出现崩溃的原因。根据记录 C/C++ 绑定的规范,只有一个调用,因此在这种情况下没有调用"错误"入口点这样的事情。它们只是更强类型语言中的不同入口点。

最新更新