概述
主要内容
本文主要内容:实现《OpenGL编程指南(原书第8版)》第一章中渲染两个蓝色三角形示例。通过该示例,介绍GLSL编程的一般步骤。为了便于学习,没有新建用来保存着色器代码的文件triangles.vert和triangles.frag,而是用常量字符串直接表示;同时没有使用书中提到的LoadShaders类,而是将其中用到的代码在MyQGLWidget 类中实现。自己第一次看红宝书第一章时很吃力,代码也分散在几个文件里,看起来更吃力了。所以自己做笔记时,编程示例就把代码集中一起,至少自己一目了然,希望对大家也有帮助。
示例演示
头文件myQGLWidget .h
#ifndef MY_QGLWIDGET_H
#define MY_QGLWIDGET_H
#include <QOpenGLWidget>
#include <QtOpenGL>
#include <QOpenGLFunctions_4_3_Compatibility>
class MyQGLWidget : public QOpenGLWidget , protected QOpenGLFunctions_4_3_Compatibility
{
Q_OBJECT
public:
MyQGLWidget(QWidget *parent = 0);
protected:
void initializeGL();
void resizeGL(int width, int height);
void paintGL();
private:
enum VAO_IDs {Triangles, NumVAOs};
enum Buffer_IDs {ArrayBuffer, NumBuffers};
enum Attrib_IDs {vPosition = 0};
GLuint VAOs[NumVAOs];
GLuint Buffers[NumBuffers];
const GLuint NumVertices;
void initShader();
};
#endif
源文件myQGLWidget .cpp
#include <QtGui>
#include "myQGLWidget.h"
MyQGLWidget::MyQGLWidget(QWidget *parent)
: QOpenGLWidget(parent),
NumVertices(6)
{
}
void MyQGLWidget::initializeGL()
{
initializeOpenGLFunctions();
glEnable(GL_DEPTH_TEST);
initShader();
}
void MyQGLWidget::resizeGL(int width, int height)
{
glViewport(0, 0, width, height);
}
void MyQGLWidget::paintGL()
{
glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
glClearColor(0.0, 0.0, 0.0, 1.0);
glBindVertexArray(VAOs[Triangles]);
glDrawArrays(GL_TRIANGLES, 0, NumVertices);
}
void MyQGLWidget::initShader()
{
glGenVertexArrays(NumVAOs, VAOs);
glBindVertexArray(VAOs[Triangles]);
GLfloat vertices[6][2] = {
{-0.90, -0.90},
{0.85, -0.90},
{-0.90, 0.85},
{0.90, -0.85},
{0.90, 0.90},
{-0.85, 0.90}
};
glGenBuffers(NumBuffers, Buffers);
glBindBuffer(GL_ARRAY_BUFFER, Buffers[ArrayBuffer]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(vPosition);
glVertexAttribPointer(vPosition, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
const char* vertexShaderCode =
"#version 430 n"
""
"layout(location=0) in vec4 vPosition;"
""
"void main()"
"{"
" gl_Position = vPosition;"
"}";
const char* fragmentShaderCode =
"#version 430 r n"
""
"out vec4 fColor;"
""
"void main()"
"{"
" fColor = vec4(0.0, 0.0, 1.0, 1.0);"
"}";
GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
const char* adapter[1];
adapter[0] = vertexShaderCode;
glShaderSource(vertexShaderID, 1, adapter, 0);
adapter[0] = fragmentShaderCode;
glShaderSource(fragmentShaderID, 1, adapter, 0);
glCompileShader(vertexShaderID);
glCompileShader(fragmentShaderID);
GLuint programID = glCreateProgram();
glAttachShader(programID, vertexShaderID);
glAttachShader(programID, fragmentShaderID);
glLinkProgram(programID);
glUseProgram(programID);
}
运行结果:
代码分析
initShader函数实现了OpenGL的初始化过程。
初始化顶点数组对象VAO
调用glGenVertexArrays分配顶点数组对象(vertex-array object)。glGenVertexArrays(NumVAOs, VAOs)表示OpenGL分配了NumVAOs个顶点数组对象,保存到数组VAOs中。glGenVertexArrays函数的完整解释如下:
void glGenVertexArrays(GLsizei n, GLuint *arrays);
返回n个未使用的对象名到数组arrays中,用作顶点数组对象。返回的名字可以用来分配更多的缓存对象,并且它们已经使用未初
始化的顶点数组集合的默认状态进行了数值的初始化。
很多OpenGL命令都是glGen*的形式,它们负责分配不同类型的OpenGL对象的名称。这里的名称类似C语言中的指针变量,必须分配内存并且用名称引用它之后,名称才有意义。在OpenGL中,这个分配的机制叫做绑定对象(bind an object)。这通过一系列glBind形式的OpenGL函数集合去实现,本例中使用glBindVertexArray。glBindVertexArray(VAOs[Triangles])表示创建一个顶点数组对象,并与其名称(VAOs[Triangles])关联起来。
void glBindVertexArray(GLuint array);
1、如果array非0,并且是glGenVertexArrays()所返回的,将创建一个新的顶点数组对象并且与其名称关联起来。
2、如果绑定到一个已经创建的顶点数组对象,将会激活这个VAO,并且直接影响对象中所保存的顶点数组状态。
3、如果array为0,OpenGL将不再使用程序所分配的任何VAO,并将渲染状态重设为顶点数组的默认状态。
总体上来说,在两种情况下我们需要绑定一个对象:创建对象并初始化它所对应的数据时;以及每次我们准备使用这个对象而它并不是当前绑定的对象时。
分配顶点缓存对象VBO
顶点数组对象VAO负责保存一系列顶点的数据。这些数据保存到缓存对象当中,并且由当前绑定的顶点数组对象VAO管理。我们只有一种顶点数组对象VAO类型,但是却又很多种类型的对象,并且其中一部分对象并不负责处理顶点数据。缓存对象就是OpenGL服务端(比如GPU)分配和管理的一块内存区域,并且几乎所有传入OpenGL的数据都存储在缓存对象当中。
顶点缓存对象(Vertex Buffer Objects, VBO)的初始化过程与顶点数组对象VAO的创建过程类似,不过需要有向缓存中添加数据的一个过程。首先,glGenBuffers(NumBuffers, Buffers)表示分配NumBuffers个VBO到数组Buffer中。
void glGenBuffers(GLsizei n, GLuint * buffers);
返回n个当前未使用的缓存对象名称,并保存到buffers数组中。返回到buffers中的名称不一定是连续的整型数据。
当分配缓存的名称之后,就可以调用glBindBuffer()来绑定它们了。由于OpenGL中有很多种不同类型的缓存对象,因此绑定一个缓存时,需要指定它所对应的类型。此例中由于是将顶点数据保存到缓存中(故为顶点缓存对象VBO),因此使用GL_ARRAY_BUFFER类型。缓存对象的类型现在共有8中,分别用于不同的OpenGL功能实现,具体以后介绍。
void glBindBuffer(GLenum target, GLuint buffer);
1、如果是第一次绑定buffer,且它是一个非零的无符号整数,那么将创建一个与该名称相对应的新缓存对象。
2、如果绑定到一个已经创建的缓存对象,那么它将称为当前被激活的缓存对象。
3、如果绑定的buffer值为0,那么OpenGL将不再对当前target应用任何缓存对象。
初始化顶点缓存对象之后,我们需要把顶点数据从对象传输到缓存对象中,这一步通过glBufferData ()完成的。它主要有两个任务:分配顶点数据所需的存储空间,然后将数据从应用程序的数组中拷贝到OpenGL服务器的内存中。
void glBufferData(GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage);
在OpenGL服务端内存中分配size个存储单元(通常为byte),用于存储数据或索引。如果当前绑定的对象已经存在了关联的数据,
那么会首先删除这些数据。target设置数据类型。size表示存储数据的总数量。data要么是一个客户端内存的指针,以便初始化缓
存对象,要么是NULL,将保留size大小的未初始化的数据,以备后用。usage用于设置分配数据后的读取和写入方式。
glEnableVertexAttribArray和glVertexAttribPointer指定了顶点着色器的变量与我们存储在缓存对象中数据的关系。这也就是我们所说的着色管线装配的过程,即将应用程序与着色器之间,以及不同着色阶段之间的数据通道连接起来。
为了输入顶点着色器的数据,也就是OpenGL将要处理的所有顶点数据,需要在着色器中声明一个in 变量,然后使用glVertexAttribPointer()将它关联到一个顶点属性数组。
void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei
stride, const GLvoid * pointer);
设置index(着色器中的数学位置)位置对应的数据值。pointer表示缓存对象中,从起始位置开始计算的数组数据的偏移值(假设
起始位置为0),使用基本的系统单位(byte)。size表示每个顶点需要更新的分量数目。type指定了数组中每个元素的数据类型
(比如GL_FLOAT)。normalized设置顶点数据在存储前是否需要进一步归一化。stride是数组中每两个元素之间的大小偏移
量(byte)。如果stride为0,那么数据应该紧密地封装在一起。
glVertexAttribPointer(vPosition, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0)中各个参数的设置及其意义如下图所示。
后面是在OpenGL中使用可编程着色器(shader),我们会在下一篇详细介绍。
使用OpenGL进行渲染
渲染的工作我们在paintGL函数中进行。首先我们要清除帧缓存的数据再进行渲染。清除的工作由glClear()完成。如果要改变清除颜色的数值,可以使用glClearColor()。后面两行代码的工作就是选择我们准备绘制的顶点数据,然后请求进行绘制。首先调用glBindVertexArray()来选择作为顶点数据使用的顶点数组。然后调用glDrawArrays()来实现顶点数据向OpenGL管线的传输。
void glDrawArrays(GLenum mode, GLint first, GLsizei count);
使用当前绑定的顶点数组元素来建立一系列的几何图元。mode设置构建图元的类型(比如GL_TRIANGLES),起始位置为first,而
结束位置为first+count-1。
其他
MyQGLWidget 继承自QOpenGLFunctions_4_3_Compatibility,其实也可以继承自QOpenGLFunctions_4_3_Core。QOpenGLFunctions_4_3_Core使用的OpenGL 43.版本的核心模式(core profile),这个模式可以确保使用的只是OpenGL的最新特性。QOpenGLFunctions_4_3_Compatibility使用的是兼容模式,这种模式自OpenGL 1.0版本以来的所有特性都可以使用。
代码下载
OpenGL学习系列导航
最后
以上就是清新铅笔为你收集整理的s2.GLSL学习之绘制三角形主要内容示例演示代码分析其他的全部内容,希望文章能够帮你解决s2.GLSL学习之绘制三角形主要内容示例演示代码分析其他所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复