1.显示到屏幕
这里先简单介绍一下缓冲区,缓冲区存在于图形卡的显存中,OpenGL在绘制图元时,先是在一个缓冲区中完成渲染,然后再把渲染结果交换到屏幕上。
void display() { //指定OpenGL清理屏幕时将要使用的颜色,这里为黑色。 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //开始清理屏幕,GL_COLOR_BUFFER_BIT表示清理将影响颜色缓冲区,清理时使用上面指定的颜色。 glClear(GL_COLOR_BUFFER_BIT); //告知OpenGL渲染的时候需要调用应用程序对象。 glUseProgram(theProgram); //下面三行设置三角形的坐标,它们告知OpenGL三角形在缓冲区中的位置 //获取已经初始化的缓冲区对象。 glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject); //启用缓冲区对象中的数据,参数指定要修改的顶点属性的索引值 glEnableVertexAttribArray(0); //尽管这个函数包含“Pointer”,但是它不处理指针,它用来对缓冲区对象中的顶点属性进行设定。 //参数1指定要修改的顶点属性的索引值;参数2指定每个顶点向量的维数;参数3指定每个顶点向量的数据类型; //参数4指定是否归一化;参数5指定各顶点向量间是否有空隙,0表示紧密排列;参数6指定顶点数组起始位置与缓冲区 //对象起始位置的偏移量,0表示无偏移。后三个参数通常取默认值。 glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); //渲染函数,从顶点数组索引0开始,读取3个顶点,然后将它们连接成一个三角形。 glDrawArrays(GL_TRIANGLES, 0, 3); //下面两行是清理工作,释放为了实现渲染所做的一些设置。 glDisableVertexAttribArray(0); glUseProgram(0); }
2.顶点传递
以下是我们希望传递的数据,一个顶点数组。
const float vertexPositions[] = {
0.75f, 0.75f, 0.0f, 1.0f,
0.75f, -0.75f, 0.0f, 1.0f,
-0.75f, -0.75f, 0.0f, 1.0f,
};
每行表示一个顶点的四维向量,向量的前三个数字表示顶点的x、y、z坐标,第四个顶点w是缩放因子,通常为1,当w为1是,可以忽略它。尽管我们定义了这些数据,但是OpenGL不能直接使用。所以我们需要分配一些OpenGL可见的缓冲区,然后将我们的数据添加到这些缓冲区。这可以通过缓冲区对象(buffer object)来实现。缓冲区对象可以看成GPU内存中的线性数组,GPU可以快速访问这些数组。缓冲区对象在初始化时创建,代码如下所示。
void InitializeVertexBuffer() { //创建一个缓冲区对象,并将positionBufferObject指向该缓存区对象。 //此时缓存区对象已经创建,但并未给它分配存储空间。 glGenBuffers(1, &positionBufferObject); //将缓冲区对象绑定到绑定目标GL_ARRAY_GUFFER,OpenGL中的所有对象在进行操作前都需要绑定到上下文,缓冲区对象也不例外。 glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject); //一旦绑定了一个缓冲区对象,就需要给该对象分配足够的GPU内存来存储顶点数据,分配的大小为 sizeof(vertexPositions)。 //然后该函数将我们定义的数据拷贝到缓冲区对象,第四个参数是数据的使用方式。 glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW); //简单的清理。将0绑定到绑定目标GL_ARRAY_BUFFER后,之前的绑定自动解除。这里的0类似NULL指针。 glBindBuffer(GL_ARRAY_BUFFER, 0); }
3.顶点着色器
#version 330 layout(location = 0) in vec4 position; void main() { gl_Position = position; }第一行是着色器语言的版本声明,这里对应的是OpenGL 3.3版本。
4.片段着色器
#version 330 out vec4 outputColor; void main() { outputColor = vec4(1.0f, 1.0f, 1.0f, 1.0f); }片段做色器用于计算片段的输出颜色。
5.开始着色
void InitializeProgram() { //创建着色器对象列表,这些着色器对象将通过链接生成应用程序对象 std::vector<GLuint> shaderList; //下面两行分别编译顶点着色器和片段着色器,着色器的编译和链接与c语言类似,也是先编译后链接。更重要的是,编译还引入了出错检查。 shaderList.push_back(CreateShader(GL_VERTEX_SHADER, strVertexShader)); shaderList.push_back(CreateShader(GL_FRAGMENT_SHADER, strFragmentShader)); theProgram = CreateProgram(shaderList); //删除着色器对象 std::vector<GLuint>::iterator it; for(it=shaderList.begin();it!=shaderList.end();it++) glDeleteShader(*it); }
GLuint CreateShader(GLenum eShaderType, const std::string &strShaderFile) { //创建着色器对象 GLuint shader = glCreateShader(eShaderType); const char *strFileData = strShaderFile.c_str(); //将着色器对象与着色器程序文件关联,参数2为着色器程序文件数量,当需要将多个文件编译到一个着色器对象,可以多个文件组成数组。最后 //一个参数指定文件的长度,这里用NULL,告知OpenGL假设该文件中不含null字符,除非需要在文件中使用null字 符,否则该参数都可以设置为 //NULL glShaderSource(shader, 1, &strFileData, NULL); //编译着色器对象 glCompileShader(shader); GLint status; //获取编译状态、 glGetShaderiv(shader, GL_COMPILE_STATUS, &status); //如果编译失败,打印日志信息 if (status == GL_FALSE) { GLint infoLogLength; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); GLchar *strInfoLog = new GLchar[infoLogLength + 1]; glGetShaderInfoLog(shader, infoLogLength, NULL, strInfoLog); const char *strShaderType = NULL; switch(eShaderType) { case GL_VERTEX_SHADER: strShaderType = "vertex"; break; case GL_GEOMETRY_SHADER: strShaderType = "geometry"; break; case GL_FRAGMENT_SHADER: strShaderType = "fragment"; break; } fprintf(stderr, "Compile failure in %s shader:\n%s\n", strShaderType, strInfoLog); delete[] strInfoLog; } return shader; }接着可以把编译的着色器对象传给 CreateProgram函数
GLuint CreateProgram(const std::vector<GLuint> &shaderList) { //创建应用程序对象 GLuint program = glCreateProgram(); //将着色器对象附加到应用程序对象 for(size_t iLoop = 0; iLoop < shaderList.size(); iLoop++) glAttachShader(program, shaderList[iLoop]); //链接应用程序对象 glLinkProgram(program); GLint status; //获取链接状态 glGetProgramiv (program, GL_LINK_STATUS, &status); //如果链接失败,打印日志信息 if (status == GL_FALSE) { GLint infoLogLength; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength); GLchar *strInfoLog = new GLchar[infoLogLength + 1]; glGetProgramInfoLog(program, infoLogLength, NULL, strInfoLog); fprintf(stderr, "Linker failure: %s\n", strInfoLog); delete[] strInfoLog; } //取消着色器对象的附加 for(size_t iLoop = 0; iLoop < shaderList.size(); iLoop++) glDetachShader(program, shaderList[iLoop]); return program; }
参考链接:http://alfonse.bitbucket.org/oldtut/Basics/Tut01%20Dissecting%20Display.html
源码需要用Qt5.6.0编译
源码链接:http://download.csdn.net/detail/caoshangpa/9471466