在绘制之前,我们先来介绍下一下几个头文件的作用。
// GLTool.h头⽂文件包含了大部分GLTool中类似C语言的独立函数
#include "GLTools.h"
// 着色器管理器
#include "GLShaderManager.h"
// 矩阵的⼯具类:可以利于GLMatrixStack 加载单元矩阵/矩阵/矩阵相乘/压栈/出栈/缩放/平移/旋转
#include "GLMatrixStack.h"
// 矩阵工具类,表示观察者的位置.通过设置vOrigin,vForward,vUps等
#include "GLFrame.h"
// 矩阵⼯具类,⽤来快速设置正/透视投影矩阵.完成坐标从3D->2D的映射过程
#include "GLFrustum.h"
// 三⻆形批次类,帮助类,利用它可以传输顶点/光照/纹理/颜⾊数据到存储着⾊器中
#include "GLBatch.h"
// 变换管道类,⽤来快速在代码中传输视图矩阵/投影矩阵/视图投影变换矩阵等
#include "GLGeometryTransform.h"
接下来我们需要在外部声明几个全局变量。
// 着色器管理器
GLShaderManager shaderManager;
// 模型视图矩阵
GLMatrixStack modelViewMatrix;
// 投影矩阵
GLMatrixStack projectionMatrix;
// 视景体, 用来完成物体从3D到2D的映射
GLFrustum viewFrustum;
// 几何图形变换管道
GLGeometryTransform transformPipeline;
// 大球
GLTriangleBatch torusBatch;
// 小球
GLTriangleBatch sphereBatch;
//地板
GLBatch floorBatch;
// 角色帧 照相机角色帧
GLFrame cameraFrame;
// 添加附加随机球
#define NUM_SPHERES 50
GLFrame spheres[NUM_SPHERES];
绘制地板
main函数工作流程
int main(int argc, char* argv[])
{
// 设置工作目录
gltSetWorkingDirectory(argv[0]);
// 初始化GLUT环境
glutInit(&argc, argv);
// 设置显示模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
// 设置初始化窗口的大小
glutInitWindowSize(800,600);
// 创建一个名为OpenGL SphereWorld的窗口
glutCreateWindow("OpenGL SphereWorld");
// 注册窗口改变时的回调函数
glutReshapeFunc(ChangeSize);
// 注册一个绘图的函数
glutDisplayFunc(RenderScene);
// 初始化一个GLEW库,确保OpenGL API对程序完全可用。在试图做任何渲染之前,要检查确定驱动程序的初始化过程中没有任何问题
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
// 自定义函数,做一些变量的初始化
SetupRC();
// 开启mainloop,相当于OC中runloop,是一个运行循环
glutMainLoop();
return 0;
}
glutInitDisplayMode
函数功能为设置初始显示模式。mode可取以下值或其组合:
-w638
SetupRC
void SetupRC()
{ //清理背景颜色
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
//1.初始化
shaderManager.InitializeStockShaders();
//2.开启深度测试
glEnable(GL_DEPTH_TEST);
//3.设置地板顶点数据
floorBatch.Begin(GL_LINES, 324);
for(GLfloat x = -20.0; x <= 20.0f; x+= 0.5) {
floorBatch.Vertex3f(x, -0.55f, 20.0f);
floorBatch.Vertex3f(x, -0.55f, -20.0f);
floorBatch.Vertex3f(20.0f, -0.55f, x);
floorBatch.Vertex3f(-20.0f, -0.55f, x);
}
floorBatch.End();
}
ChangeSize
void ChangeSize(int nWidth, int nHeight)
{
//1.设置视口
glViewport(0, 0, nWidth, nHeight);
//2.创建投影矩阵,。
viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
//viewFrustum.GetProjectionMatrix() 获取viewFrustum投影矩阵,并将其加载到投影矩阵堆栈上
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//3.设置变换管道使用的两个矩阵堆栈(变换矩阵modelViewMatrix ,投影矩阵projectionMatrix)
//初始化GLGeometryTransform 的实例transformPipeline.通过将它的内部指针设置为模型视图矩阵堆栈 和投影矩阵堆栈实例,来完成初始化。
//当然这个操作也可以在SetupRC函数中完成,但是在窗口大小改变时或者窗口创建时设置它们并没有坏处。而且这样可以一次性完成矩阵和管线的设置。
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
RenderScene
void RenderScene(void)
{
//1.颜色值
GLfloat vFloorColor[] = { 0.0f, 1.0f, 0.0f, 1.0f};
//2.清除颜色缓存区和深度缓冲区中的数据
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//3.使用平面着色器,绘制地面。
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vFloorColor);
floorBatch.Draw();
//4.执行缓存区交换
glutSwapBuffers();
}
绘制大球
SetupRC
在SetupRC
中初始化球体的数据。
// 初始化球体的三角形批次,后面参数依次是,球半径,片段数,堆叠数
gltMakeSphere(torusBatch, 0.5f, 300, 80);
RenderScene
在RenderScene
函数中对大球进行绘制。
// 定义球体的颜色
static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };
// 定义一个计时器,让球体旋转起来。
static CStopWatch rotTimer;
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
// 绘制球体
// 获取光源位置
M3DVector4f vLightPos = {0.0f,10.0f,5.0f,1.0f};
// 将大球位置平移(3.0)向屏幕里面。
modelViewMatrix.Translate(0.0f, 0.0f, -3.0f);
// 向模型视图矩阵中压入一个栈,用来处理球体的旋转
modelViewMatrix.PushMatrix();
// 让球体转起来
modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
// 使用点光源着色器
shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(), vLightPos, vTorusColor);
// 绘制
torusBatch.Draw();
// 绘制完毕后出栈
modelViewMatrix.PopMatrix();
添加以上代码,此时球体已经被渲染出来了,但是此时它并没有动起来。我们需要提交重新渲染,让它动起来。
glutPostRedisplay();
RenderScene
的最终代码
void RenderScene(void)
{
//1.颜色值(地板,大球,小球颜色)
static GLfloat vFloorColor[] = { 0.0f, 1.0f, 0.0f, 1.0f};
static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };
//2.基于时间动画
static CStopWatch rotTimer;
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
//2.清除颜色缓存区和深度缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//**// 压栈
modelViewMatrix.PushMatrix();
//3.绘制地面
shaderManager.UseStockShader(GLT_SHADER_FLAT,
transformPipeline.GetModelViewProjectionMatrix(),
vFloorColor);
floorBatch.Draw();
//4.获取光源位置
M3DVector4f vLightPos = {0.0f,10.0f,5.0f,1.0f};
//5.使得大球位置平移(3.0)向屏幕里面
modelViewMatrix.Translate(0.0f, 0.0f, -3.0f);
//6.压栈(复制栈顶)
modelViewMatrix.PushMatrix();
//7.大球自转
modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
//8.指定合适的着色器(点光源着色器)
shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(), vLightPos, vTorusColor);
torusBatch.Draw();
//9.绘制完毕则Pop
modelViewMatrix.PopMatrix();
modelViewMatrix.PopMatrix();
//10.执行缓存区交换
glutSwapBuffers();
glutPostRedisplay();
}
绘制小球
SetupRC
在SetupRC
函数中设置小球的模型
// 设置小球球模型
gltMakeSphere(sphereBatch, 0.1f, 13, 26);
// 随机位置放置小球球
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);
}
RenderScene
在RenderScene
函数中绘制小球
// 画小球
for (int i = 0; i < NUM_SPHERES; i++) {
modelViewMatrix.PushMatrix();
modelViewMatrix.MultMatrix(spheres[i]);
shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(), vLightPos, vSphereColor);
sphereBatch.Draw();
modelViewMatrix.PopMatrix();
}
接下来我们让一个小球围绕着大球转起来
我们需要在RenderScene
函数中添加以下代码。
modelViewMatrix.Rotate(yRot * -2.0f, 0.0f, 1.0f, 0.0f);
modelViewMatrix.Translate(0.8f, 0.0f, 0.0f);
shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,transformPipeline.GetModelViewMatrix(),transformPipeline.GetProjectionMatrix(),vLightPos,vSphereColor);
sphereBatch.Draw();
移动观察者
首先在模型视图矩阵堆栈中加入一个观察者矩阵,在RenderScene
函数中添加如下代码。
// 创建一个观察者矩阵
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
// 将观察者矩阵压入模型视图堆栈中。
modelViewMatrix.PushMatrix(mCamera);
。。。。
// 在最后需要pop
modelViewMatrix.PopMatrix();
接下来我们需要实现SpeacialKeys
函数。实现键盘的控制
void SpeacialKeys(int key,int x,int y){
float linear = 0.1f;
float angular = float(m3dDegToRad(5.0f));
if (key == GLUT_KEY_UP) {
cameraFrame.MoveForward(linear);
}
if (key == GLUT_KEY_DOWN) {
cameraFrame.MoveForward(-linear);
}
if (key == GLUT_KEY_LEFT) {
cameraFrame.RotateWorld(angular, 0, 1, 0);
}
if (key == GLUT_KEY_RIGHT) {
cameraFrame.RotateWorld(-angular, 0, 1, 0);
}
}
网友评论