【LWJGL2 WIKI】【现代OpenGL篇】用DrawArrays画方形

翻译 2016年01月07日 23:00:44

原文: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)

相关文章推荐

WebGL中gl.drawArrays()与gl.drawElements()对比

在我们学习WebGL的过程中,会常常用到drawArray和drawElements方法以及TRIANGLES图元和TRIANGLE_STRIP图元,但是都没有对这些组合加以详细的对比,以分析性能差异...

OpenGL的Draw函数

OpenGL的Draw函数前言初学OpenGL时会发现各种各样的Draw*函数,每种Draw*的功能和适合使用场景是什么,在这里做一下整理。...

WebGL中gl.drawArrays()与gl.drawElements()对比

本次我们来绘制一个这样的网格(5*10),我们采用程序实现。 为什么绘制网格?!因为网格可以绘制很多不同的对象,比如球、圆环……,对于我们地理信息科学专业来说,地球就是我们关注的重点…… ...

【LWJGL2 WIKI】【现代OpenGL篇】用投影、视图、模型矩阵画方形

原文:http://wiki.lwjgl.org/wiki/The_Quad_with_Projection,_View_and_Model_matricesIntroduction 介绍在OpenG...

【LWJGL2 WIKI】【现代OpenGL篇】用纹理画方形

原文:http://wiki.lwjgl.org/wiki/The_Quad_texturedIntroduction 介绍本教程将讲怎样对模型贴纹理,其他方形的部分就和之前教程中讲的差不多。后面可以...

【LWJGL2 WIKI】【现代OpenGL篇】画颜色方形

原文:http://wiki.lwjgl.org/wiki/The_Quad_coloredIntroduction 介绍本教程介绍颜色。每个方形的角将被分配一个不同的颜色,为此我们必须使用shade...

【LWJGL2 WIKI】【现代OpenGL篇】交叉数据画方形

原文:http://wiki.lwjgl.org/wiki/The_Quad_interleavedIntroduction 介绍我们之前把顶点数据分别存在不同的VBO之内。其实也可以交叉或混合保存数...

【LWJGL2 WIKI】【现代OpenGL篇】用BufferSubData更新VBO方形

原文:http://wiki.lwjgl.org/wiki/The_Quad_updating_a_VBO_with_BufferSubDataIntroduction 介绍VBO一旦设置好,我们可以...

【LWJGL2 WIKI】【现代OpenGL篇】版本选择

原文:http://wiki.lwjgl.org/wiki/Version_selectionIntroduction 介绍OpenGL有许多版本可以用。写教程的时候,最新版是4.2,开发期间决定逐渐...

【LWJGL2 WIKI】【基础篇】基础3:方形

原文:http://wiki.lwjgl.org/wiki/LWJGL_Basics_3_%28The_Quad%29Introduction 介绍本教程教你怎样在LWJGL里使用OpenGL,但是并...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【LWJGL2 WIKI】【现代OpenGL篇】用DrawArrays画方形
举报原因:
原因补充:

(最多只允许输入30个字)