OpenGL渲染图像
OpenGL渲染图像需要执行的操作
- 从OpenGL的几何图元中设置数据,用于构建形状。
- 使用不同的着色器(shader)对输入的图元数据执行计算操作,判断他们的位置、颜色、以及其他渲染属性。
- 将输入图元的数学描述转化为与屏幕位置对应的像素片元(shader)。这一步也称为光栅化(rasterization)。
- 最后,针对光栅化过程产生的每个片元,执行片元着色器(fragment shader),从而决定这个片元的最终颜色和位置。
- 如果有必要,还需要对每个片元执行一些额外的操作,例如判断片元对应的对象是否可见,或者将片元的颜色与当前屏幕位置的颜色进行融合。
绘制图形需要做哪些准备呢?
导入框架
#include
头文件包含了大部分GLTool中类似C语言的独立函数GLTool.h #include
移入了GLTool 着色器管理器(shader Mananger)类。没有着色器,我们就不能在OpenGL(核心框架)进行着色。着色器管理器不仅允许我们创建并管理着色器,还提供一组“存储着色器”,他们能够进行一些初步䄦基本的渲染操作- 在Mac系统下
#include
启动GLUT
程序的总是“main”函数开始处理
GLTools函数glSetWorkingDrectory用来设置当前工作目录。实际上在Windows中是不必要的,因为工作目录默认就是与程序可执行执行程序相同的目录。但是在Mac OS X中,这个程序将当前工作文件夹改为应用程序捆绑包中的/Resource文件夹。GLUT的优先设定自动进行了这个中设置,但是这样中方法更加安全。创建窗口并设置显示模式
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
GLUT_DOUBLE:双缓存窗口,是指绘图命令实际上是离屏缓存区执行的,然后迅速转换成窗口视图,这种方式,经常用来生成动画效果;
GLUT_DEPTH:标志将一个深度缓存区分配为显示的一部分,因此我们能够执行深度测试;
GLUT_STENCIL:确保我们也会有一个可用的模板缓存区。
深度、模板测试后面会细致讲到。初始化GLEW库
重新调用GLEW库初始化OpenGL 驱动程序中所有丢失的入口点,以确保OpenGL API对开发者完全可用。
调用glewInit()函数一次就能完成这一步。在试图做任何渲染之前,还要检查确定驱动程序的初始化过程中没有出现任何问题。SetupRC()
实际上这个函数对GLUT 没有什么影响,但是在实际开始渲染之前,我们这里进行任何OpenGL 初始化都非常方便。这里的RC代表渲染环境,这是一个运行中的OpenGL状态机的句柄。在任何OpenGL 函数起作用之前必须创建一个渲染环境。而GLUT在我们第一次创建窗口时就完成了这项工作。int main(int argc, char* argv[]) { gltSetWorkingDirectory(argv[0]); glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(800,600); glutCreateWindow("OpenGL SphereWorld"); glutReshapeFunc(ChangeSize); glutDisplayFunc(RenderScene); glutSpecialFunc(SpeacialKeys); GLenum err = glewInit(); if (GLEW_OK != err) { fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err)); return 1; } SetupRC(); glutMainLoop(); ShutdownRC(); return 0; }
看不懂?没关系!
main函数中常用函数含义解析
glutInit() 负责初始化GLUT库。它会处理向程序输入的命令行参数,并且移除其中与控制GLUT如何操作相关的部分。它必须是应用程序第一个GLUT函数,负责设置其他GLUT例程必需的数据结构。 glutInitDisplayMode() 设置了程序所使用的窗口类型。窗口设置更多的OpenGL 特性,例如RAGA颜色空间,使用深度缓存或动画效果。 glutInitWindowsSize() 设置所需的窗口大小,如果不想在这个设置一个固定值,也可以先查询显示设备的尺寸,然后根据计算机的屏幕动态设置窗口的大小。 glutCreateWindow(),它的功能和它的名字一样,如果当前的系统环境可以满足glutInitDisplayMode()的显示模式要求,这里就会创建一个窗口(此时会调用计算机窗口系统的接口)。只有GLUT创建了一个窗口之后(其中包含创建创建OpenGL环境的过程),我们才可以使用OpenGL相关的函数 glewInit()函数,属于另一个辅助库GLEW(OpenGL Extention Wrangler)。GLEW可以简化获取函数地址的过程,并且包含了可以跨平台使用的其他一些OpenGL编程方法。 glutDisplayFunc(),它设置了一个显示回调(diplay callback),即GLUT在每次更新窗口内容的时候回自动调用该例程 glutMainLoop(),这是一个无限执行的循环,它会负责一直处理窗口和操作系统的用户输入等操作。(注意:不会执行在glutMainLoop()之后的所有命令。)
这里要注意最核心的三个方法
changeSize
RenderScene
setupRC
changeSize 触发条件:
- 新建窗口
- 窗口尺寸发生调整
处理业务:- 设置OpenGL 视口
- 设置OpenGL 投影方式等
RenderScene 触发条件:
- 系统自动触发
- 开发者手动调用函数触发
处理业务:- 清理缓存区(颜色, 深度, 模板缓存区等)
- 使用存储着色器
- 绘制图形
setupRC 触发条件:
- 手动main函数触发
处理业务:- 设置窗口背景颜色
- 初始化存储着色器shaderManager
- 设置图形顶点数据
- 利用GLBatch 三角形批次类, 将数据传递到着色器
星图Demo示例:
setupRC函数中的代码
void setupRC() {
// 设置清屏颜色到颜色缓冲区
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// 初始化着色器管理器
shaderManager.InitializeStockShaders();
// 开启深度测试/背面剔除
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
// 设置大球
gltMakeSphere(torusBatch, 0.4f, 40, 80);
// 设置小球
gltMakeSphere(sphereBatch, 0.1f, 26, 13);
// 设置地板顶点数据&地板纹理
GLfloat texSize = 10.0f;
floorBatch.Begin(GL_TRIANGLE_FAN, 4, 1);
floorBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
floorBatch.Vertex3f(-20.0f, -0.41f, 20.0f);
floorBatch.MultiTexCoord2f(0, texSize, 0.0f);
floorBatch.Vertex3f(20.0f, -0.41f, 20.f);
floorBatch.MultiTexCoord2f(0, texSize, texSize);
floorBatch.Vertex3f(20.0f, -0.41f, -20.0f);
floorBatch.MultiTexCoord2f(0, 0.0f, texSize);
floorBatch.Vertex3f(-20.0f, -0.41f, -20.0f);
floorBatch.End();
// 随机小球顶点坐标数据
for (int i = 0; i < NUM_SPHERES; i++) {
//y轴不变,X,Z产生随机值
GLfloat x = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
GLfloat z = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
//在y方向,将球体设置为0.0的位置,这使得它们看起来是飘浮在眼睛的高度
//对spheres数组中的每一个顶点,设置顶点数据
spheres[i].SetOrigin(x, 0.0f, z);
}
// 命名纹理对象
glGenTextures(3, uiTextures);
// 将TGA文件加载为2D纹理。
// 参数1:纹理文件名称
// 参数2&参数3:需要缩小&放大的过滤器
// 参数4:纹理坐标环绕模式
glBindTexture(GL_TEXTURE_2D, uiTextures[0]);
loadTGATexture("marble.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_REPEAT);
glBindTexture(GL_TEXTURE_2D, uiTextures[1]);
loadTGATexture("marslike.tga", GL_LINEAR_MIPMAP_LINEAR,
GL_LINEAR, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, uiTextures[2]);
loadTGATexture("moonlike.tga", GL_LINEAR_MIPMAP_LINEAR,
GL_LINEAR, GL_CLAMP_TO_EDGE);
}
changeSize函数中的代码
// 屏幕更改大小或已初始化
void changeSize(int nWidth, int nHeight) {
// 设置视口
glViewport(0, 0, nWidth, nHeight);
// 设置投影方式
viewFrusrum.SetPerspective(35.0f, float(nWidth) / float(nHeight), 1.0f, 100.0f);
// 将投影矩阵加载到投影矩阵堆栈
projectionMatrix.LoadMatrix(viewFrusrum.GetProjectionMatrix());
modelViewMatrix.LoadIdentity();
// 将投影矩阵堆栈和模型视图矩阵对象设置到管道中
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
renderScene函数中的代码
// 进行调用绘制场景
void renderScene(void) {
// 地板颜色值
static GLfloat vFloorColor[] = {1.0f, 1.0f, 0.0f, 0.75f};
// 时间动画
static CStopWatch rotTimer;
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
// 清除颜色缓存区和深度缓存区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 压栈
modelViewMatrix.PushMatrix();
// 设置观察者矩阵
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.MultMatrix(mCamera);
// 压栈
modelViewMatrix.PushMatrix();
// 翻转Y轴
modelViewMatrix.Scale(1.0f, -1.0f, 1.0f);
modelViewMatrix.Translate(0.0f, 0.8f, 0.0f);
// 指定顺时针为正面
glFrontFace(GL_CW);
// 绘制地面以外其他部分
drawSomething(yRot);
// 恢复逆时针为正面
glFrontFace(GL_CCW);
// 绘制镜面, 恢复矩阵
modelViewMatrix.PopMatrix();
// 开启混合功能
glEnable(GL_BLEND);
// 指定glBlendFunc 颜色混合方程式
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 绑定地面纹理
glBindTexture(GL_TEXTURE_2D, uiTextures[0]);
// 纹理调整着色器
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_MODULATE,
transformPipeline.GetModelViewProjectionMatrix(),
vFloorColor,
0);
// 开始绘制
floorBatch.Draw();
// 取消混合
glDisable(GL_BLEND);
// 绘制地面以外其他部分
drawSomething(yRot);
// 绘制完 恢复矩阵
modelViewMatrix.PopMatrix();
// 交换缓存区
glutSwapBuffers();
// 提交重新渲染
glutPostRedisplay();
}
main函数中的代码
int main(int argc, char *argv[]) {
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(800, 600);
glutCreateWindow("OpenGL SphereWorld");
glutReshapeFunc(changeSize);
glutDisplayFunc(renderScene);
glutSpecialFunc(SpeacialKeys);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
setupRC();
glutMainLoop();
shutdownRC();
return 0;
}
让我们来看一下效果
当我们需要移动视角 也就是改变观察者的位置与角度
//**3.移动照相机参考帧,来对方向键作出响应
void SpeacialKeys(int key,int x,int y)
{
float linear = 0.1f;
float angular = float(m3dDegToRad(5.0f));
if (key == GLUT_KEY_UP) {
//MoveForward 平移
cameraFrame.MoveForward(linear);
}
if (key == GLUT_KEY_DOWN) {
cameraFrame.MoveForward(-linear);
}
if (key == GLUT_KEY_LEFT) {
//RotateWorld 旋转
cameraFrame.RotateWorld(angular, 0.0f, 1.0f, 0.0f);
}
if (key == GLUT_KEY_RIGHT) {
cameraFrame.RotateWorld(-angular, 0.0f, 1.0f, 0.0f);
}
}
按下键盘方向键我们就可以移动观察者的位置与角度
Demo地址: https://github.com/daolelidong/OpenGLUniverse