【LWJGL2 WIKI】【現代OpenGL篇】用DrawElements畫方形

原文:http://wiki.lwjgl.org/wiki/The_Quad_with_DrawElements

Introduction 介紹

之前教程裏用glDrawArrays畫方形,缺點是你必須指定所有三角形的每一個頂點。一個方形其實只有四個頂點,但是分成兩個三角形就變成了六個!用glDrawElements的話,就可以在定義頂點時把重複的點去掉了。取而代之的是,我們必須通過指定序號告訴OpenGL每個三角形用的是哪三個頂點。接下來教程開始。

Do you remember the following… 知識回顧

之前的DrawArrays教程將作爲本教程的基礎,也就是讀本篇前你得先了解什麼是AVO和VBO並且瞭解怎樣設置它們。你還需要知道VAO有許多屬性列表(默認是16個),我們可以將VBO連在上面。VBO裏含有數據,還需要綁定和解綁對象(比如VBO就是一個對象,頂點緩衝對象)。

Defining our vertices 定義頂點

定義僅含有方形四頂點的頂點數組。順序無所謂,因爲之後要具體在另一個對象裏指定順序。

// 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();

如你所見,只定義了四個頂點,就是方形的四個頂點。數組數目由0開始到3結束,每個頂點有三個值,代表位置。現在在我們的腦海裏可以給它們定一個序號(見代碼註釋),如果直接將四點給OpenGL,它將不知所措。所以我們還需要給OpenGL一個指引,讓它用四點畫兩個三角形,因此我們還需要建另一個VBO。

Adding indices 增加序號

方形依舊是由兩個三角形組成的,在新的VBO裏,我們用序號來定義之前頂點的順序。如下:

// 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();

如你所見,我們用序號來定義了兩個三角形。關鍵在於,我們雖然定義了四個頂點,但是其中兩個頂點用了兩次。這樣內存佔用減小了,從6降到了4,但是又增加了一個序號VBO,看起來好像得不償失,但是實際遊戲的模型可不僅僅是一個方形,頂點數量巨大,就值了。
後建的這個VBO類型略有不同,並非是一個普通的數組緩衝(GL_ARRAY_BUFFER),而是元素數組(GL_ELEMENT_ARRAY_BUFFER)。爲了它,還需要再申請內存ID,綁定,填充數據到緩衝(別忘了最後解綁)。

// 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);

vboiId是一個全局整數變量。還要注意我們保存了序號的數目(就和之前glDrawArrays教程裏保存頂點數目一樣)

Rendering with “glDrawElements” 用glDrawElements渲染

用glDrawElements渲染跟之前的glDrawArrays差不多,渲染前需要綁定序號VBO。

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);

Cleaning up our memory 清除內存

做內存管理的時候別忘了也刪除序號VBO。

// Delete the index VBO
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL15.glDeleteBuffers(vboiId);
The rest of our cleaning code looks exactly the same, here’s the complete cleanup code:

其他的清除代碼也一樣,以下是完整的清除代碼:

// 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);

Display.destroy();

The result 結果

以下是畫出的方形(和之前一樣):
方形

Complete source code 完整代碼

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.ContextAttribs;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.PixelFormat;

public class TheQuadExampleDrawElements {
    // Entry point for the application
    public static void main(String[] args) {
        new TheQuadExampleDrawElements();
    }

    // Setup variables
    private final String WINDOW_TITLE = "The Quad: glDrawElements";
    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 TheQuadExampleDrawElements() {
        // Initialize OpenGL (Display)
        this.setupOpenGL();

        this.setupQuad();

        while (!Display.isCloseRequested()) {
            // Do a single loop (logic/render)
            this.loopCycle();

            // Force a maximum FPS of about 60
            Display.sync(60);
            // Let the CPU synchronize with the GPU if GPU is tagging behind
            Display.update();
        }

        // Destroy OpenGL (Display)
        this.destroyOpenGL();
    }

    public void setupOpenGL() {
        // Setup an OpenGL context with API version 3.2
        try {
            PixelFormat pixelFormat = new PixelFormat();
            ContextAttribs contextAtrributes = new ContextAttribs(3, 2)
                .withProfileCore(true)
                .withForwardCompatible(true);

            Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT));
            Display.setTitle(WINDOW_TITLE);
            Display.create(pixelFormat, contextAtrributes);

            GL11.glViewport(0, 0, WIDTH, HEIGHT);
        } catch (LWJGLException e) {
            e.printStackTrace();
            System.exit(-1);
        }

        // Setup an XNA like background color
        GL11.glClearColor(0.4f, 0.6f, 0.9f, 0f);

        // Map the internal OpenGL coordinate system to the entire screen
        GL11.glViewport(0, 0, WIDTH, HEIGHT);
    }

    public void setupQuad() {
        // 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 (VBO's) 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);

        Display.destroy();
    }
}

Credit

Mathias Verboven (moci)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章