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

翻译 2016年01月08日 21:19:16

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

OpenGL的Draw函数

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

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

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

Win32 OpenGL编程(5)顶点数组详细介绍

Win32 OpenGL编程(5) 顶点数组详细介绍write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie讨论新闻组及文件Technorati 标签: 顶点...
  • vagrxie
  • vagrxie
  • 2009年10月20日 14:39
  • 17143

【Android开发学习16】Android OpenGL ES 关于glDrawArrays和glDrawElements

引用一段网上的话: For both, you pass OpenGL some buffers containing vertex data. glDrawArrays is basically...
  • ypist
  • ypist
  • 2013年02月22日 16:21
  • 3807

OpenGL简介-基于VBO绘制三角形

现在第一个例子:绘制三角形 Initial OpenGL CanvasLoad 绘制所需的資料 / 建立儲存資料的資料結構Initial OpenGL state 與 各種 OpenGL buffe...
  • jaccen
  • jaccen
  • 2017年04月21日 08:31
  • 227

学会利用“配置文件”优化我们的代码结构

之前我们讲过某些时候可以利用数组来代替if/else来优化代码, 在本文中, 我们再次来复习一下, 顺便学学利用配置文件来优化代码结构, 好, 开始吧。        假设有这样一个场景: 某系统...
  • stpeace
  • stpeace
  • 2015年03月13日 23:40
  • 1171

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

原文:http://wiki.lwjgl.org/wiki/The_Quad_with_DrawArraysIntroduction 介绍随着3.0发布,“弃用”机制被引入。所有被标记为弃用的函数,在...

【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篇】用投影、视图、模型矩阵画方形

原文:http://wiki.lwjgl.org/wiki/The_Quad_with_Projection,_View_and_Model_matricesIntroduction 介绍在OpenG...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【LWJGL2 WIKI】【现代OpenGL篇】用DrawElements画方形
举报原因:
原因补充:

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