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

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

Introduction 介紹

隨着3.0發佈,“棄用”機制被引入。所有被標記爲棄用的函數,在未來的版本會被移除。因此開發者應避免使用它們並把已經用到的儘量重構出去。許多輔助類被移除,比如matrix/stack操作和默認光照。但是更重要的是,定義了基元,無需再用glVertex了,取而代之使用頂點數組對象VAO(Vertex Array Object)和頂點緩衝對象VBO(Vertex Buffer Object)。可以用它們來在屏幕上畫方形。

“Vertex Array Object” and “Vertex Buffer Object”

一種渲染提速算法是把數據放在GPU裏而不是每次都從CPU往GPU發,這就是VAO和VBO做的事情,你將可以使用一定量的顯存。
把VAO想象成一個完整的對象定義,它由VBO組成。每個VBO可以保存特殊的定義的數據(混合或交插保存都是可行的),VBO可以用來保存:頂點位置、顏色、法線、紋理座標……。VAO保存各種各樣的VBO在它的屬性列表裏,默認將在一個VAO裏有從0到15共16個屬性可用。
設置VAO或者其他對象,都要按下面的規則:

  • 請求一個內存空間(返回一個整數,作爲對象的ID)
  • 用ID綁定對象
  • 操作對象(此對象是指OpenGL目前綁定上的對象)
  • 用ID解綁對象

如果是VAO,用代碼寫出來就是:

int vaoId = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vaoId);
// Do something with it
GL30.glBindVertexArray(0);

如果是VBO,用代碼寫出來就是:

int vboId = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
// Do something with it
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

Setting up the quad VBO and VAO 用VBO和VAO設置方形

在把VBO放進VAO的屬性列表之前,應該先調用glVertextAttributePointer綁定VAO,此舉需要列表ID(默認是從0到15)。在本例中,我們要用VAO和VBO設置一個方形。VBO含有頂點座標數據,VAO將作爲主物體(即方形)定義而活動。
首先需聲明頂點,OpenGL以三角形的方式畫所有的東西。三角形頂點默認是按逆時針方向描畫的。這意味着,既然你要按逆時針順序指定頂點,需要先定義哪邊是前哪邊是後。這很有用,OpenGL可以由此在渲染管線中以最優方式剔除那些看不到的面。你可以改掉這種默認的行爲,但是那通常是自找麻煩,所以這裏我們不再深入討論。
回到頂點定義,一個方形不是一個三角形而是兩個三角形,因此定義方形實際需要的是6個頂點而不是4個(這種低效的方式下次我們再解決):
六個頂點
我們默認的OpenGL座標系所有軸都是從-1到1,這與我們整個屏幕是相對應的,像這樣:
座標系
我們會用三個數字定義頂點,每座標軸一個數字(X, Y, Z)。實際上OpenGL用的是4個數字,(X, Y, Z, W)。先不管W,把它設成1。數字是浮點型,在將它們用在OpenGL方法前,需要在bytebuffer(或者FloatBuffer)裏交換它們。

// OpenGL expects vertices to be defined counter clockwise by default
float[] vertices = {
        // Left bottom triangle
        -0.5f, 0.5f, 0f,
        -0.5f, -0.5f, 0f,
        0.5f, -0.5f, 0f,
        // Right top triangle
        0.5f, -0.5f, 0f,
        0.5f, 0.5f, 0f,
        -0.5f, 0.5f, 0f
};
// Sending data to OpenGL requires the usage of (flipped) byte buffers
FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length);
verticesBuffer.put(vertices);
verticesBuffer.flip();

vertexCount = 6;

需要記錄頂點數目,因此我們需要讓OpenGL稍候用glDrawArrays畫圖時知道有多少個頂點需要畫。
現在我們的頂點是可用格式,可以開始定義VAO和VBO了。在VBO裏放位置數據,在VAO裏把VBO放在屬性列表的0位置上。連接VBO和VAO的屬性列表很容易(當不需要使用交叉數據的時候)。只需要用glVertextAttribPointer即可,它的參數如下:

  • 屬性列表序號(在本例中,就是0)
  • 一個數據定義裏含了幾個值(在本例中,3個浮點數算是一個位置數據定義)
  • 值的數據類型(在本例中,是浮點數)
  • 步進和偏移量(暫時還不需要用,在交叉數據裏才用)

用glBufferData將數據放入VBO中,這方法的參數如下:

  • 類型(我們用的是GL_ARRAY_BUFFER, 默認就是這個)
  • 緩衝區(我們的那個帶有頂點數據的FloatBuffer)
  • 用途(我們的頂點不動也不變,所以就用簡單的GL_STATIC_DRAW)

別忘了解綁我們的對象,因爲不需要再操作它們了。綜合以上所有步驟,代碼如下:

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

vaoID和vboId(還有vertexCount)是全局定義的整數值。

Rendering with glDrawArrays 用glDrawArrays渲染

爲了實際畫出方形,前面提過,用glDrawArrays方法,它的參數:

  • 怎樣畫頂點(我們用簡單的GL_TRIANGLES)
  • 第一個序號(我們從頭開始,就是0)
  • 頂點數目(這值我們存在vertextCount變量裏)

OpenGL還必須有VAO(連着VBO的那一個)激活在內存中,因此必須在畫前綁定好它們。VBO已經和VAO屬性列表0號位連好了,因此只需要啓用此列表。當畫完後,我們要解綁並禁用一切。渲染代碼如下:

GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

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

// Draw the vertices
GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, vertexCount);

// Put everything back to default (deselect)
GL20.glDisableVertexAttribArray(0);
GL30.glBindVertexArray(0);

Cleaning up our memory 清除內存

最後,退出程序之前,需要做內存管理:

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

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

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

The result 結果

最後畫出我們的方形:

畫出的方形

Complete source code 完整代碼

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;
import org.lwjgl.util.glu.GLU;

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

    // Setup variables
    private final String WINDOW_TITLE = "The Quad: glDrawArrays";
    private final int WIDTH = 320;
    private final int HEIGHT = 240;
    // Quad variables
    private int vaoId = 0;
    private int vboId = 0;
    private int vertexCount = 0;

    public TheQuadExampleDrawArrays() {
        // 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)
                .withForwardCompatible(true)
                .withProfileCore(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);

        this.exitOnGLError("Error in setupOpenGL");
    }

    public void setupQuad() {      
        // OpenGL expects vertices to be defined counter clockwise by default
        float[] vertices = {
                // Left bottom triangle
                -0.5f, 0.5f, 0f,
                -0.5f, -0.5f, 0f,
                0.5f, -0.5f, 0f,
                // Right top triangle
                0.5f, -0.5f, 0f,
                0.5f, 0.5f, 0f,
                -0.5f, 0.5f, 0f
        };
        // Sending data to OpenGL requires the usage of (flipped) byte buffers
        FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length);
        verticesBuffer.put(vertices);
        verticesBuffer.flip();

        vertexCount = 6;

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

        this.exitOnGLError("Error in setupQuad");
    }

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

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

        // Draw the vertices
        GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, vertexCount);

        // Put everything back to default (deselect)
        GL20.glDisableVertexAttribArray(0);
        GL30.glBindVertexArray(0);

        this.exitOnGLError("Error in loopCycle");
    }

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

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

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

        Display.destroy();
    }

    public void exitOnGLError(String errorMessage) {
        int errorValue = GL11.glGetError();

        if (errorValue != GL11.GL_NO_ERROR) {
            String errorString = GLU.gluErrorString(errorValue);
            System.err.println("ERROR - " + errorMessage + ": " + errorString);

            if (Display.isCreated()) Display.destroy();
            System.exit(-1);
        }
    }
}

Credit

Mathias Verboven (moci)

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