有前面5章的基础,接下来我们可以以自己的方式去实现一些经典书籍的示例程序。大多数的示例都存在过度封装的倾向,对于初学者来说,要找到“真正起作用的那行代码”非常困难。先把红宝书,即《opengl programming guide》第8版第3章关于各种绘制方式的例子用第五章封装的oglwindow库和glew库实现一下。
具体的函数声明,参数说明通通省了,如果想了解,直接查红宝书吧,注意第8版电子书已经有了, OpenGL实在是变化很快的。
下面直接上代码,程序说明:
1)使用了oglwindow库、glew库
2)在VS中创建win32应用程序(不是控制台项目)
3)添加glew库和oglwindow头文件和库文件目录至VS环境
4)链接库需要:opengl32.lib glew32.lib oglwindow.lib
//
//该示例演示:
//1 带平移矩阵的顶点着色器
//2 缩小矩阵写在顶点着色器里了
//3 演示了glDrawArrays等多种绘图方法
//
//
///////////////////////////////////////////////////////////////////////
//
// triangles.cpp
//
///////////////////////////////////////////////////////////////////////
#include <iostream>
using namespace std;
#include <Windows.h>
#include <GL/glew.h>
#include <GL/wglew.h>
#include <oglWindow.h>
#pragma comment (lib, "opengl32.lib")
#pragma comment (lib , "oglwindow.lib")
#pragma comment (lib , "glew32.lib")
#define BUFFER_OFFSET(x) ((const void*) (x))
enum Buffer_IDs { ArrayBuffer, NumBuffers };
enum Attrib_IDs { vPosition = 0,vColor };
GLuint ebo[1];
GLuint vao[1], vbo[1];
GLuint shaderprogram;
GLuint CreateShader()
{
//顶点着色器的源程序
GLchar vertexshader[] ={
"#version 440\n"
"layout(location=0) in vec4 vPosition;\n"
"layout(location=1) in vec4 vColor;\n"
"out vec4 color;\n"
"uniform mat4 m;\n"
"void main()\n"
"{ mat4 m1 = mat4(0.2, 0.0, 0.0, 0.0,"
" 0.0, 0.2, 0.0, 0.0,"
" 0.0, 0.0, 0.2,0.0,"
" 0.0, 0.0, 0.0, 1.0); "
"gl_Position = m * m1 * vPosition; "
" color = vColor;\n }"
};
//片段着色器的源程序
GLchar fragshader[] ={
"#version 440\n"
"in vec4 color;\n"
"out vec4 fColor;\n"
"void main()\n"
"{ fColor = color; }"
};
//定义内部对象,使后续代码可以使用循环
struct Shader {
GLenum type;
GLchar* source;
} shaders[2] = {
{ GL_VERTEX_SHADER, vertexshader },
{ GL_FRAGMENT_SHADER, fragshader }
};
GLuint program = glCreateProgram();//GPU创建程序对象
for ( int i = 0; i < 2; ++i ) {
Shader& s = shaders[i];
GLuint shader = glCreateShader( s.type ); //GPU创建着色器对象
glShaderSource( shader, 1, (const GLchar**) &s.source, NULL );//着色器绑定对应源程序
glCompileShader( shader );//编译着色器
GLint status;
glGetShaderiv(shader,GL_COMPILE_STATUS,&status);
if(status == GL_FALSE) return 0;
glAttachShader( program, shader ); //将编译结果存入GPU程序
}
glLinkProgram(program); //将程序中的不同部分链接起来
GLint status;
glGetProgramiv(program,GL_LINK_STATUS,&status);
if(status == GL_FALSE) return 0;
return program;
}
//---------------------------------------------------------------------
//
// init
//
void init(void)
{
//有4个顶点的顶点数组
const GLfloat vertex_positions[] =
{
-1.0f, -1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
};
//每个顶点对应的颜色数组
const GLfloat vertex_colors[] =
{
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f
};
//使用glDrawElements等绘图方式必须有的索引数组
static const GLushort vertex_indices[] =
{
0,1,2,3,0,1
};
//创建并绑定顶点数组对象
glGenVertexArrays(1, vao);
glBindVertexArray(vao[0]);
//创建、绑定并对顶点缓冲对象赋值
glGenBuffers(1, vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER,sizeof(vertex_positions) + sizeof(vertex_colors),NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_positions), vertex_positions);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(vertex_positions), sizeof(vertex_colors), vertex_colors);
//创建、绑定并对索引数组对象赋值
glEnableClientState(GL_ELEMENT_ARRAY_BUFFER);
glGenBuffers(1, ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[0]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(vertex_indices), vertex_indices, GL_STATIC_DRAW);
//编译、链接着色器
GLuint program = CreateShader();
shaderprogram = program;
glUseProgram(program);
//设置着色器in类型变量指针
glVertexAttribPointer(vPosition, 4, GL_FLOAT,GL_FALSE, 0, BUFFER_OFFSET(0));
glEnableVertexAttribArray(vPosition);
glVertexAttribPointer(vColor, 4, GL_FLOAT,GL_FALSE, 0, BUFFER_OFFSET(sizeof(vertex_positions)));
glEnableVertexAttribArray(vColor);
}
//---------------------------------------------------------------------
//
// display
//
void display(void)
{
//清屏
glClear(GL_COLOR_BUFFER_BIT);
//绑定顶点数组对象
glBindVertexArray(vao[0]);
// glDrawArrays方法
// 平移矩阵,沿x方向左移0.8
GLfloat mat1[] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
-0.8f, 0.0f, 0.0f, 1.0f
};
GLint render_model_matrix_loc = glGetUniformLocation( shaderprogram, "m" );
glUniformMatrix4fv(render_model_matrix_loc, 1, GL_FALSE, mat1);
glDrawArrays(GL_TRIANGLES, 0, 3);
// glDrawElements
// 平移矩阵,沿x方向左移0.3
GLfloat mat2[] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
-0.3f, 0.0f, 0.0f, 1.0f
};
glUniformMatrix4fv(render_model_matrix_loc, 1, GL_FALSE, mat2);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, (GLvoid*)(0*sizeof(GLushort)));//最后一个参数是以字节为单位的偏移
// glDrawElementsBaseVertex
// 平移矩阵,沿x方向右移0.2
GLfloat mat3[] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.2f, 0.0f, 0.0f, 1.0f
};
glUniformMatrix4fv(render_model_matrix_loc, 1, GL_FALSE, mat3);
glDrawElementsBaseVertex(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, (GLvoid*)(0*sizeof(GLushort)),0);
// glDrawArraysInstanced
// 平移矩阵,沿x方向左移0.7
GLfloat mat4[] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.7f, 0.0f, 0.0f, 1.0f
};
glUniformMatrix4fv(render_model_matrix_loc, 1, GL_FALSE, mat4);
glDrawArraysInstanced(GL_TRIANGLES, 0, 3, 1);
glFlush();
SwapBuffers(wglGetCurrentDC());
}
///////////////////////////////////////////////////////////////////////////////
//窗口回调函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_PAINT:
display();
break;
case WM_SIZE:
int width;
int height;
height=lParam>>16;
width=lParam&0x0000FFFF;
glViewport(0,0,width,height);
UpdateWindow(hWnd);
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
return 0;
}
////////////////////////////////////////////////////////////////////
// 入口函数
int _stdcall WinMain(HINSTANCE hInst,HINSTANCE hPreInstance,LPSTR lpCmdLine,int nShowCmd)
{
//创建窗口
HWND hWnd = CreateDefaultOGLWindow(hInst,WndProc);
//初始化顶点数据
init();
//显示窗口
ShowOGLWindow(hWnd);
//进入主循环
EnterMainLoop();
return 0;
}
运行结果
图6-1 红宝书示例修改
对于着色器的一点说明:
//顶点着色器的源程序
GLchar vertexshader[] ={
"#version 440\n"
"layout(location=0) in vec4 vPosition;\n"
"layout(location=1) in vec4 vColor;\n"
"out vec4 color;\n"
"uniform mat4 m;\n"
"void main()\n"
"{ mat4 m1 = mat4(0.2, 0.0, 0.0, 0.0,"
" 0.0, 0.2, 0.0, 0.0,"
" 0.0, 0.0, 0.2,0.0,"
" 0.0, 0.0, 0.0, 1.0); "
"gl_Position = m * m1 * vPosition; "
" color = vColor;\n }"
};
//片段着色器的源程序
GLchar fragshader[] ={
"#version 440\n"
"in vec4 color;\n"
"out vec4 fColor;\n"
"void main()\n"
"{ fColor = color; }"
};
在顶点着色器中,m1是一个缩放矩阵,它把定义的三角形缩小到原大小的0.2倍,目的是为了能在一个窗口里放下4个三角形。
m是平移矩阵,由应用程序写入着色器。
片段着色器中的in修饰的变量color与顶点着色器中的out修饰变量color类型相同,名称也相同,glLinkProgram链接着色器程序时会把它们链接在一起,即顶点着色器输出的颜色值会作为片段着色器的颜色输入。
这里只给出了主程序全部代码,需要oglwindow库可以在第5章最后找到链接,也可以通过3.3节中给出的代码自行组织。