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

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

Introduction 介绍

本教程介绍颜色。每个方形的角将被分配一个不同的颜色,为此我们必须使用shader。跟之前教程一样,本教程还要用glDrawElements画方形。还要怎样给顶点定义颜色,怎样在shader里使用它。开始吧。

4 components instead of 3? 用4个组件代替3个

OpenGL内部用的是四个值的向量,当定义位置时我们只需要声明 X、Y、Z三个组件,但是实际上还有一个叫W的第四组件。颜色也是同理,我们用R、G、B组件描述每个象素,另外还有一A(alpha)值,shader实际上将处理四组件向量,因此在代码里也得这么用。

Vertex position and color definitions 顶点位置和颜色定义

顶点中含有两层信息:位置和颜色。之前都只在VBO里存位置信息和顶点序号信息,现在需要再建一个VBO存颜色信息。
由于位置和颜色用的都是四组件,所以按如下定义它们,记住位置是XYZW,颜色是RGBA:

// Vertices, the order is not important. XYZW instead of XYZ
float[] vertices = {
        -0.5f, 0.5f, 0f, 1f,
        -0.5f, -0.5f, 0f, 1f,
        0.5f, -0.5f, 0f, 1f,
        0.5f, 0.5f, 0f, 1f
};
FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length);
verticesBuffer.put(vertices);
verticesBuffer.flip();

float[] colors = {
        1f, 0f, 0f, 1f,
        0f, 1f, 0f, 1f,
        0f, 0f, 1f, 1f,
        1f, 1f, 1f, 1f,
};
FloatBuffer colorsBuffer = BufferUtils.createFloatBuffer(colors.length);
colorsBuffer.put(colors);
colorsBuffer.flip();

有16个属性列表,已经用一个在位置信息上了(0号属性列表),现在再用一个新的属性列表给颜色(1号属性列表)

// Create a new VBO for the indices and select it (bind) - COLORS
vbocId = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbocId);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, colorsBuffer, GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(1, 4, GL11.GL_FLOAT, false, 0, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

仍然要用GL_ARRAY_BUFFER,但是不是3元素而是4元素:RGBA。需要在glVertextAttribPointer里告诉OpenGL。类型仍然用浮点,VBO和之前位置信息的一样,只不会里面的数据是4个值而不是3个值了。

Shaders

Shader是程序中运行的最小单位程序。有各种各样的Shader,我们将使用顶点Shader和片段Shader。顶点shader将在每个顶点上运行,片段Shader则在每个象素上运行,片段shader在全部顶点shader运行完毕后运行。我们可以从顶点shader将数据推送到片段shader。
shader使用的编程语言被称为GLSL(OpenGL Shading Language),我们用OpenGL3.2,对应的GLSL版本是1.5.0,因为我们使用的是核心profile,所以GLSL也将是核心版本。
文件名扩展名怎样都无所谓,不过最好都用glsl当扩展名。

Vertex Shader 顶点Shader

在顶点shader里,我们做两件事,定义shader位置并给它一个颜色。(我的顶点shader命名为vertex.glsl)为了做这两件事,我们需要将顶点信息从主程序里推送到shader中。通过定义in变量可以做到这一点。(注意,它们是有四个元素的向量类型)

in vec4 in_Position;
in vec4 in_Color;

还有out变量,这些变量将被推送至片段shader。我们的顶点shader什么也不做,直接使用程序里传来的位置,并将颜色直接传递给片段shader。以下是完整的顶点shader:

#version 150 core

in vec4 in_Position;
in vec4 in_Color;

out vec4 pass_Color;

void main(void) {
    gl_Position = in_Position;
    pass_Color = in_Color;
}

Fragment shader 片段shader

如果想要接收到pass_Color,必须在片段shader中也将它定义成in变量:

in vec4 pass_Color;

我将我的片段shader命名为fragment.glsl,以下是完整内容:

#version 150 core

in vec4 pass_Color;

out vec4 out_Color;

void main(void) {
    out_Color = pass_Color;
}

仍然是直接使用它的颜色。

Shader program shader程序

为了使用这两个shader,并使它们可以相互关互,我们得设置shader程序。这程序将由不同的shader组成,并使它们共同工作。我们还要编译shader(驱动就可以编译,所以不需要再下载其他的工具),还要将shader绑定在程序上并连结在一起。
读取shader很简单,只需要一行一行地读取shader源代码。创建一个shader的ID(就跟创建其他对象的ID差不多),把源代码放入定义好的shader中,然后编译。通常shader读取方法如下:

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

    return shaderID;
}

方案需要一个类型(区分是顶点还是片段或者是其他什么shader)和文件名。它将返回shader的ID,因为之后编译的时候需要使用到。
两个shader的ID和一个程序的ID,全局声明为vsID, fsId和pId。读取shader很简单,一般方法如下:

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

别忘了你读的是哪种shader,我们必须得给它一个正确的shader类型。下一步就是创建程序并且将shader绑定了。

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

还记得我们声明了两个in类型在顶点shader里吧,OpenGL必须知道得从哪里得到它们。因此要将VAO的属性列表和这些变量绑定。这些列表是数组,但是因为我们已经告诉OpenGL它需要从每个顶点定义中读取多少个项目了,所以仅提供一个关系信息给每个顶点shader实例即可(每个顶点shader通常会被同时执行)

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

shader设置完毕,开始渲染。

Rendering with shaders 用shader渲染

用shader渲染并不难,只需要在画顶点将绑定shader。我们调用glUseProgram绑定一个具体shader,然后给它程序ID。要解绑的话只需要将程序ID设为0.

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

Cleaning up our memory 清理内存

退出程序时多清理一些东西。颜色VBO、shader和shader程序。

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

GL20.glDeleteShader(vsId);
GL20.glDeleteShader(fsId);
GL20.glDeleteProgram(pId);
// Delete the color VBO
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL15.glDeleteBuffers(vbocId);

The result 结果

颜色方形如下:
这里写图片描述

Complete source code 完整源代码

Vertex shader

#version 150 core

in vec4 in_Position;
in vec4 in_Color;

out vec4 pass_Color;

void main(void) {
    gl_Position = in_Position;
    pass_Color = in_Color;
}

Fragment shader

#version 150 core

in vec4 pass_Color;

out vec4 out_Color;

void main(void) {
    out_Color = pass_Color;
}

程序

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 TheQuadExampleColored {
    // Entry point for the application
    public static void main(String[] args) {
        new TheQuadExampleColored();
    }

    // Setup variables
    private final String WINDOW_TITLE = "The Quad: colored";
    private final int WIDTH = 320;
    private final int HEIGHT = 240;
    // Quad variables
    private int vaoId = 0;
    private int vboId = 0;
    private int vbocId = 0;
    private int vboiId = 0;
    private int indicesCount = 0;
    // Shader variables
    private int vsId = 0;
    private int fsId = 0;
    private int pId = 0;

    public TheQuadExampleColored() {
        // 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() {
        // Vertices, the order is not important. XYZW instead of XYZ
        float[] vertices = {
                -0.5f, 0.5f, 0f, 1f,
                -0.5f, -0.5f, 0f, 1f,
                0.5f, -0.5f, 0f, 1f,
                0.5f, 0.5f, 0f, 1f
        };
        FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length);
        verticesBuffer.put(vertices);
        verticesBuffer.flip();

        float[] colors = {
                1f, 0f, 0f, 1f,
                0f, 1f, 0f, 1f,
                0f, 0f, 1f, 1f,
                1f, 1f, 1f, 1f,
        };
        FloatBuffer colorsBuffer = BufferUtils.createFloatBuffer(colors.length);
        colorsBuffer.put(colors);
        colorsBuffer.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) - VERTICES
        vboId = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verticesBuffer, GL15.GL_STATIC_DRAW);
        GL20.glVertexAttribPointer(0, 4, GL11.GL_FLOAT, false, 0, 0);
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

        // Create a new VBO for the indices and select it (bind) - COLORS
        vbocId = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbocId);
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, colorsBuffer, GL15.GL_STATIC_DRAW);
        GL20.glVertexAttribPointer(1, 4, GL11.GL_FLOAT, false, 0, 0);
        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 color VBO
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
        GL15.glDeleteBuffers(vbocId);

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

        return shaderID;
    }
}

Credit

Mathias Verboven (moci)

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值