前面我们已经熟悉OpenGL中一些常有的名词,现在开始用OpenGL来写一个简单的demo,即画三角形。(这里关于OpenGL的环境搭建不再描述,请参考...)
1.GLUT初始化
首先苹果提供一个可视化窗口框架来允许我们来操作OpenGL,这里我们第一步需要完成GLUT框架的初始化工作:
glutInit(&argc, argv);
这里GLUT 提供了一个glutInit 方法给我们,该方法有两个参数分别为C标准main函数入参,分别是调用main函数传参个数和参数数组
2.初始化显示模式
初始化缓冲区显示模式
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
该函数参数为缓冲区枚举类型,大概有下面一些常见类型:GLUT_DOUBLE表示双缓冲区,GLUT_RGBA表示颜色缓冲区,GLUT_DEPTH表示深度缓冲区,GLUT_STENCIL表示模板缓冲区。当我们不知道传哪个参数的情况下,一般都带上
3.设置可视化窗口大小
设置显示窗口大小:
glutInitWindowSize(375, 680);
第一个参数表示窗口宽度,第二个参数表示窗口高度。注意:这里该方法非必须,系统会给一个默认值,比如300*300
4.给窗口程序设置一个Title
glutCreateWindow("Hello OpenGL!");
这里仅仅是给窗口设置一个标题,必须设置
5.注册窗口回调函数
注册窗口大小改变函数(Mac下用户可以手动拖拉窗口大小)
void ChangeSize(int w, int h)
{
printf("width:%d, height:%d\n",w, h);
glViewport(0, 0, w, h);
}
glutReshapeFunc(ChangeSize);
注册窗口渲染绘制函数:
void RenderScene(void)
{
//清除窗口缓存,参数为缓存位,有三个缓冲位:颜色缓冲位、深度缓冲位、模板缓冲位
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
//设置一组浮点数来表示红色
GLfloat vRed[] = {1.0f,0.0f,0.0f,1.0f};
//使用存储着色器渲染颜色,用的是单元着色器。 这个着色器只是使用指定颜色以默认笛卡尔坐标第在屏幕上渲染几何图形,是存储着色器中的一种。
shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vRed);
//提交着色器
triangleBatch.Draw();
//将在后台缓冲区进行渲染,然后在结束时交换到前台
glutSwapBuffers();
}
glutDisplayFunc(RenderScene);
1.这里说明一下,GPU渲染管线中有下面这些固定着色器:
单元着色器:
/**
param1: 存储着⾊器种类-单元着⾊器
param2: 颜⾊值
*/
GLShaderManager::UserStockShader(GLT_SHADER_IDENTITY,
GLfloat vColor[4]);
平⾯着⾊器:
/**
brief 在绘制图形时, 可以应⽤变换(模型/投影变化)。
param1: 存储着⾊器种类-平⾯着⾊器
param2: 允许变化的4*4矩阵
param3: 颜⾊色值
*/
GLShaderManager::UserStockShader(GLT_SHADER_FLAT,
GLfloat mvp[16],
GLfloat vColor[4]);
上⾊着⾊器:
/**
brief 在绘制图形时, 可以应⽤变换(模型/投影变化)。颜色将会平滑地插入到顶点之间,称为平滑着色。
param1: 存储着⾊器种类-上⾊着⾊器
param2: 允许变化的4*4矩阵
*/
GLShaderManager::UserStockShader(GLT_SHADER_SHADED,
GLfloat mvp[16]);
默认光源着⾊器:
/**
brief 在绘制图形时, 可以应⽤变换(模型/投影变化)。这种着⾊器会使绘制的图形产生阴影和光照的效果.
param1: 存储着⾊器种类-默认光源着⾊器
param2: 模型4*4矩阵
param3: 投影4*4矩阵
param4: 颜⾊值
*/
GLShaderManager::UserStockShader(GLT_SHADER_DEFAULT_LIGHT,
GLfloat mvMatrix[16],
GLfloat pMatrix[16],
GLfloat vColor[4]);
点光源着⾊器:
/**
使⽤场景:在绘制图形时, 可以应用变换(模型/投影变化)
param1: 存储着⾊器种类-点光源着⾊器
param2: 模型4*4矩阵
param3: 投影4*4矩阵
param4: 点光源的位置
param5: 漫反射颜⾊值
*/
GLShaderManager::UserStockShader(GLT_SHADER_POINT_LIGHT_DIEF,
GLfloat mvMatrix[16],
GLfloat pMatrix[16],
GLfloat vLightPos[3],
GLfloat vColor[4]);
纹理替换矩阵着⾊器:
/**
使⽤场景:在绘制图形时, 可以应⽤变换(模型/投影变化)。这种着⾊器通过给定的模型视图投影矩阵,使⽤纹理单元来进⾏颜⾊填充。其中每个像素点的颜⾊是从纹理中获取。
参数1: 存储着⾊器种类-纹理替换矩阵着⾊器
参数2: 模型4*4矩阵
参数3: 纹理单元
*/
GLShaderManager::UserStockShader(GLT_SHADER_TEXTURE_REPLACE,
GLfloat mvMatrix[16],
GLint nTextureUnit);
纹理调整着⾊器:
/**
使⽤场景:在绘制图形时, 可以应⽤变换(模型/投影变化)。这种着⾊器通过给定的模型视图投影矩阵。着⾊器将⼀个基本⾊乘以⼀个取⾃纹理单元nTextureUnit 的纹理,将颜⾊与纹理进⾏颜⾊混合后才填充到⽚段中
参数1: 存储着⾊器种类-纹理调整着⾊器
参数2: 模型4*4矩阵
参数3: 颜⾊值
参数4: 纹理单元
*/
GLShaderManager::UserStockShader(GLT_SHADER_TEXTURE_MODULATE,
GLfloat mvMatrix[16],
GLfloat vColor[4],
GLint nTextureUnit);
纹理光源着⾊器:
/**
//8.纹理光源着⾊器:
使⽤用场景:在绘制图形时, 可以应⽤变换(模型/投影变化)。这种着⾊器通过给定的模型视图投影矩阵,着⾊器将⼀个纹理通过漫反射照明计算进⾏调整(相乘)。
参数1: 存储着⾊器种类-纹理光源着⾊器
参数2: 模型4*4矩阵
参数3: 投影4*4矩阵
参数4: 点光源位置
参数5: 颜⾊值
参数6: 纹理单元
*/
GLShaderManager::UserStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIEF,
GLfloat mvMatrix[16],
GLfloat pMatrix[16],
GLfloat vLightPos[3],
GLfloat vBaseColor[4],
GLint nTextureUnit);
2.这里解释一下为什么在渲染完成后需要调用glutSwapBuffers函数,因为我们之前有了解到iOS的双缓存渲染机制。所以,在计算缓冲区处理完毕后需要将数据提交到显示缓冲区
6.初始化状态机
OpenGL本身就是一个巨大的状态机,这里需要glewInit函数来初始化这个状态机。
GLenum errorCode = glewInit();
if (errorCode != GLEW_OK) {//0 表示成功,否则表示失败
fprintf(stderr, "glew error :%s \n",glewGetErrorString(errorCode));
return 1;
}
这里glewGetErrorString 表示获取错误信息,参数是对应的返回状态码
7.初始化准备工作完成
一般我们会定义一个SetupRC函数来完成三角形绘制的内容初始化
void SetupRC() {
// 初始化两个变量
GLBatch triangleBatch;
GLShaderManager shaderManager;
//设置窗口(画布)背影颜色
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
// 初始化固定着色器
shaderManager.shaderManagerInitializeStockShaders();
// 设置三角形顶点数据(这里顶点随便取,按照NDC规则即可)
GLfloat vertex[] = {
-1.0f,0.0f,0.0f,
0.0f, 1.0,0.0f,
1.0f,0.f,0.0f,
};
// 设置图元装配类型,因为我们需要渲染的是三角形,所以这里采用GL_TRIANGLES,且三角形有3个顶点
triangleBatch.Begin(GL_TRIANGLES, 3);
// 设置3维顶点数据
triangleBatch.CopyVertexData3f(vertex);
// 结束提交
triangleBatch.End();
}
这里GLBatch 和GLShaderManager 开源代码include文件夹下两个工具类,批次处理类和着色器管理类。另外补充一下:这里图元装配类型有多种:
GL_POINTS //点
GL_LINES //线段
GL_LINE_STRIP //多段线
GL_LINE_LOOP //线圈
GL_TRIANGLES //三角形
GL_TRIANGLE_STRIP //三角形条带
GL_TRIANGLE_FAN //三角形扇
GL_QUADS //四边形
GL_QUAD_STRIP //四边形条带
GL_POLYGON //多边形(凸)
8.启动循环渲染
如果我们不启用循环渲染
,那么程序在渲染一次结束后立马退出。然后我们并不想要这种效果,而是希望程序一直运行下去。
glutMainLoop()
9.完整main如下:
int main(int argc, char * argv[])
{
printf("hello world");
// 1.初始化GLUT库
glutInit(&argc, argv);
// 2.初始化双缓冲窗口,其中GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分别指的是:双缓冲窗口,RGBA颜色模式,深度测试,模板缓冲区
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
// 3.设置GLUT窗口大小,非必须设置,默认300x300
glutInitWindowSize(375, 650);
// 4.给window窗口起个别名,必须写,不然跑步起来
glutCreateWindow("hello OpenGL");
// 5.注册窗口回调函数-窗口大小改变
glutReshapeFunc(ChangeSize);
// 6.注册窗口回调函数-渲染视图
glutDisplayFunc(RenderScene);
// 7.初始化状态机
GLenum errorCode = glewInit();
if (errorCode != GLEW_OK) {//0 表示成功,否则表示失败
fprintf(stderr, "glew error :%s \n",glewGetErrorString(errorCode));
return 1;
}
// 9.调用setupRC初始化准备工作
SetupRC();
// 10.开启runloop函数
glutMainLoop();
return 0;
}