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

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

Introduction 介绍

我们之前把顶点数据分别存在不同的VBO之内。其实也可以交叉或混合保存数值,将所有的数据保存在同一个VBO中。按之前的教程,你可能会想,那样是不是我们只需要用一个属性列表了。不,在属性列表的部分,仍然需要把它们分开,只是用同一个VBO来保存数据。教程开始。

Splitting VBO’s 分割VBO

作为前情回顾,仍然做些重复的工作,但是这次要把VBO分割开。建两个FloatBuffer,一个给颜色一个给位置,颜色定义为4组件,位置是3组件。

float[] positions = new float[] {1, 1, 1};
float[] colors = new float[] {1, 1, 1, 1};

// Put our data in the proper format: byte buffers
FloatBuffer positionBuffer = BufferUtils.createFloatBuffer(positions.length);
positionBuffer.put(positions); positionBuffer.flip();
FloatBuffer colorBuffer = BufferUtils.createFloatBuffer(colors.length);
colorBuffer.put(colors); colorBuffer.flip();

// Create a new VAO
int vaoID = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vaoID);

// Create a new VBO for our positions and link it to attribute list 0
int posVboID = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, posVboID);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, positionBuffer, GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

// Create a new VBO for our colors and link it to attribute list 1
int colVboID = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, colVboID);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, positionBuffer, GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(1, 4, GL11.GL_FLOAT, false, 0, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

GL30.glBindVertexArray(0);

对那些已经看过之前教程的朋友提问:当我用glDrawElements的时候,我忘了什么东西?

Interleaving VBO’s 交叉保存VBO

现在把颜色和位置VBO组合成一个VBO。仍然按上面的例子走,进一步实际应用的例子稍后再讲。

float[] positions = new float[] {1, 1, 1};
float[] colors = new float[] {1, 1, 1, 1};

// Interleave the data in the proper format: byte buffer
FloatBuffer interleavedBuffer = BufferUtils.createFloatBuffer(positions.length +
        colors.length);
interleavedBuffer.put(positions);   // Buffer contents: X, Y, Z
interleavedBuffer.put(colors);      // Buffer contents: X, Y, Z, R, G, B, A
interleavedBuffer.flip();

// Create a new VAO
int vaoID = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vaoID);

// Create a new VBO for our interleaved data
int interVboID = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, interVboID);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, interleavedBuffer, GL15.GL_STATIC_DRAW);

// -- We'll need to know some numbers beforehand, I'm using variables for these
// -- so it's easier to see where they come from
// There are 4 bytes in a float
int floatByteSize = 4;
// We use 3 floats for our position
int positionFloatCount = 3;
// We use 4 floats for our color
int colorFloatCount = 4;
// So the total amount of floats used is ...
int floatsPerVertex = positionFloatCount + colorFloatCount;
// So the total amount of bytes per vertex used is (this is the 'stride') ...
int vertexFloatSizeInBytes = floatByteSize * floatsPerVertex;

// -- Now we can split our interleaved data over 2 attribute lists
// First up is our positional information in list 0
GL20.glVertexAttribPointer(0, positionFloatCount, GL11.GL_FLOAT, false,
        vertexFloatSizeInBytes, 0);
// Second is our color information in list 1, for this we also need the offset
int byteOffset = floatByteSize * positionFloatCount;
GL20.glVertexAttribPointer(1, colorFloatCount, GL11.GL_FLOAT, false,
        vertexFloatSizeInBytes, byteOffset);

GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);

我们要做的就是把所有的信息放在一个大数组内,然后告诉OpenGL每个顶点的信息是几个字节跨度的,还要再告诉它一个顶点所有的字节也可以分割,0号属性列表只需要看前X个字节即可,而1号属性列表只看X字节之后的Y字节。我们通过设置stride和offset来实现。

  • stride,步进,告诉OpenGL多少字节算是一组。实际上就是将我们的缓冲给分割成几个小缓冲区,小缓冲区的长度就是步进的值。
  • offset,偏移值,当被分割成许多个列表(如上面提到的位置列表、颜色列表)时,还需要告诉OpenGL在其内部偏移值是多少。偏移值也是以字节为单位的,所以如果前一个列表有三个元素,并且每个元素占4个字节,那么到下一列表的偏移值应是12字节。

图例如下:

整个缓冲区被分割成数个小区域,它们的大小就是步进。小区域可以再被分割为不同的字节组,不同组代表顶点的不同属性。上面的图片就是之前例子里用的缓冲区,位置属性存三个浮点数(一个浮点数占4个字节),颜色属性存四个浮点数。

Practical management of vertices 实际的顶点管理

当把不同的属性加到一个顶点上时,我们很快就能得到许多不同的浮点数组和其他变量。为了避免必须一个个地管理所有的缓冲区和数组,最好以面向对象的形式处理它,毕竟我们用的是Java嘛。因此首先的改进方案,我们创建一个顶点类,它将保存我们不同的属性,即位置和颜色属性。这样比较明确简便,当设置属性列表时也更加方便。

public class Vertex {
    // Vertex data
    private float[] xyzw = new float[] {0f, 0f, 0f, 1f};
    private float[] rgba = new float[] {1f, 1f, 1f, 1f};

    // The amount of elements that a vertex has
    public static final int elementCount = 8;  
    // The amount of bytes an element has
    public static final int elementBytes = 4;
    // The size of a vertex in bytes, like in C/C++: sizeof(Vertex)
    public static final int sizeInBytes = elementBytes * elementCount;

    // Setters
    public void setXYZ(float x, float y, float z) {
        this.setXYZW(x, y, z, 1f);
    }

    public void setRGB(float r, float g, float b) {
        this.setRGBA(r, g, b, 1f);
    }

    public void setXYZW(float x, float y, float z, float w) {
        this.xyzw = new float[] {x, y, z, w};
    }

    public void setRGBA(float r, float g, float b, float a) {
        this.rgba = new float[] {r, g, b, 1f};
    }

    // Getters 
    public float[] getXYZW() {
        return new float[] {this.xyzw[0], this.xyzw[1], this.xyzw[2], this.xyzw[3]};
    }

    public float[] getRGBA() {
        return new float[] {this.rgba[0], this.rgba[1], this.rgba[2], this.rgba[3]};
    }
}

现在已经有了一个Vertex类了,分配颜色和位置更加容易,现在看看怎样用它创建方形。

// We'll define our quad using 4 vertices of the custom 'Vertex' class
Vertex v0 = new Vertex(); v0.setXYZ(-0.5f, 0.5f, 0f); v0.setRGB(1, 0, 0);
Vertex v1 = new Vertex(); v1.setXYZ(-0.5f, -0.5f, 0f); v1.setRGB(0, 1, 0);
Vertex v2 = new Vertex(); v2.setXYZ(0.5f, -0.5f, 0f); v2.setRGB(0, 0, 1);
Vertex v3 = new Vertex(); v3.setXYZ(0.5f, 0.5f, 0f); v3.setRGB(1, 1, 1);

现在只需创建一个新的FloatBuffer,将每个Vertex的数据放入其中。

Vertex[] vertices = new Vertex[] {v0, v1, v2, v3};
// Put each 'Vertex' in one FloatBuffer
FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length *
        Vertex.elementCount);
for (int i = 0; i < vertices.length; i++) {
    verticesBuffer.put(vertices[i].getXYZW());
    verticesBuffer.put(vertices[i].getRGBA());
}
verticesBuffer.flip();

还记得之前我的提问吧,答案就是在用glDrawElements之前,我们琮必须创建一个新的VBO把序号放进去。这部分跟交叉数据什么的没关系,所以跟之前一样。

// OpenGL expects to draw vertices in counter clockwise order by default
byte[] indices = {
        0, 1, 2,
        2, 3, 0
};

现在该做VAO和VBO的工作了。

// Create a new Vertex Array Object in memory and select it (bind)
vaoId = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vaoId);

// Create a new Vertex Buffer Object in memory and select it (bind)
vboId = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verticesBuffer, GL15.GL_STATIC_DRAW);
// Put the positions in attribute list 0
GL20.glVertexAttribPointer(0, 4, GL11.GL_FLOAT, false, Vertex.sizeInBytes, 0);
// Put the colors in attribute list 1
GL20.glVertexAttribPointer(1, 4, GL11.GL_FLOAT, false, Vertex.sizeInBytes,
        Vertex.elementBytes * 4);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

// Deselect (bind to 0) the VAO
GL30.glBindVertexArray(0);

不需要再改别的东西了,仍然要用两个属性列表,只是这两个属性列表将只对应一个VBO。

The result 结果

看起来一样。

Complete source code 完整代码

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
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;
import org.lwjgl.util.glu.GLU;

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

    // Setup variables
    private final String WINDOW_TITLE = "The Quad: Interleaved";
    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;
    // Shader variables
    private int vsId = 0;
    private int fsId = 0;
    private int pId = 0;

    public TheQuadExampleInterleaved() {
        // Initialize OpenGL (Display)
        this.setupOpenGL();

        this.setupQuad();
        this.setupShaders();

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

    public void setupQuad() {
        // We'll define our quad using 4 vertices of the custom 'Vertex' class
        Vertex v0 = new Vertex(); v0.setXYZ(-0.5f, 0.5f, 0f); v0.setRGB(1, 0, 0);
        Vertex v1 = new Vertex(); v1.setXYZ(-0.5f, -0.5f, 0f); v1.setRGB(0, 1, 0);
        Vertex v2 = new Vertex(); v2.setXYZ(0.5f, -0.5f, 0f); v2.setRGB(0, 0, 1);
        Vertex v3 = new Vertex(); v3.setXYZ(0.5f, 0.5f, 0f); v3.setRGB(1, 1, 1);

        Vertex[] vertices = new Vertex[] {v0, v1, v2, v3};
        // Put each 'Vertex' in one FloatBuffer
        FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length *
                Vertex.elementCount);
        for (int i = 0; i < vertices.length; i++) {
            verticesBuffer.put(vertices[i].getXYZW());
            verticesBuffer.put(vertices[i].getRGBA());
        }
        verticesBuffer.flip();

        // OpenGL expects to draw vertices in counter clockwise order by default
        byte[] indices = {
                0, 1, 2,
                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)
        vaoId = GL30.glGenVertexArrays();
        GL30.glBindVertexArray(vaoId);

        // Create a new Vertex Buffer Object in memory and select it (bind)
        vboId = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verticesBuffer, GL15.GL_STATIC_DRAW);
        // Put the positions in attribute list 0
        GL20.glVertexAttribPointer(0, 4, GL11.GL_FLOAT, false, Vertex.sizeInBytes, 0);
        // Put the colors in attribute list 1
        GL20.glVertexAttribPointer(1, 4, GL11.GL_FLOAT, false, Vertex.sizeInBytes,
                Vertex.elementBytes * 4);
        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) - INDICES
        vboiId = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiId);
        GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL15.GL_STATIC_DRAW);
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
    }

    private void setupShaders() {
        int errorCheckValue = GL11.glGetError();

        // Load the vertex shader
        vsId = this.loadShader("src/thequad/vertex.glsl", GL20.GL_VERTEX_SHADER);
        // Load the fragment shader
        fsId = this.loadShader("src/thequad/fragment.glsl", GL20.GL_FRAGMENT_SHADER);

        // Create a new shader program that links both shaders
        pId = GL20.glCreateProgram();
        GL20.glAttachShader(pId, vsId);
        GL20.glAttachShader(pId, fsId);

        // Position information will be attribute 0
        GL20.glBindAttribLocation(pId, 0, "in_Position");
        // Color information will be attribute 1
        GL20.glBindAttribLocation(pId, 1, "in_Color");

        GL20.glLinkProgram(pId);
        GL20.glValidateProgram(pId);

        errorCheckValue = GL11.glGetError();
        if (errorCheckValue != GL11.GL_NO_ERROR) {
            System.out.println("ERROR - Could not create the shaders:" + GLU.gluErrorString(errorCheckValue));
            System.exit(-1);
        }
    }

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

        GL20.glUseProgram(pId);

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

        // 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);
        GL20.glDisableVertexAttribArray(1);
        GL30.glBindVertexArray(0);
        GL20.glUseProgram(0);
    }

    public void destroyOpenGL() {
        // Delete the shaders
        GL20.glUseProgram(0);
        GL20.glDetachShader(pId, vsId);
        GL20.glDetachShader(pId, fsId);

        GL20.glDeleteShader(vsId);
        GL20.glDeleteShader(fsId);
        GL20.glDeleteProgram(pId);

        // Select the VAO
        GL30.glBindVertexArray(vaoId);

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

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

    public int loadShader(String filename, int type) {
        StringBuilder shaderSource = new StringBuilder();
        int shaderID = 0;

        try {
            BufferedReader reader = new BufferedReader(new FileReader(filename));
            String line;
            while ((line = reader.readLine()) != null) {
                shaderSource.append(line).append("\n");
            }
            reader.close();
        } catch (IOException e) {
            System.err.println("Could not read file.");
            e.printStackTrace();
            System.exit(-1);
        }

        shaderID = GL20.glCreateShader(type);
        GL20.glShaderSource(shaderID, shaderSource);
        GL20.glCompileShader(shaderID);

        if (GL20.glGetShader(shaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("Could not compile shader.");
            System.exit(-1);
        }

        return shaderID;
    }
}

Credit

Mathias Verboven (moci)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值