结合前面学到的知识,做一个球公转自转的综合程序,代码如下:
#include "GLTools.h"
#include "GLShaderManager.h"
#include "GLMatrixStack.h"
#include "GLFrustum.h"
#include "GLGeometryTransform.h"
#include "StopWatch.h"
//在Mac 系统下,`#include<glut/glut.h>` 在Windows 和 Linux上,我们使⽤freeglut的静态库版本并且需要添加⼀个宏
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
GLShaderManager shaderManager; //着色器管理器
GLMatrixStack modelViewMatrix; //模型视图矩阵
GLMatrixStack projectionMatrix; //投影矩阵
GLFrustum viewFrustum; //视景体
GLGeometryTransform transformPipeline; //几何图形变换管道
GLBatch floorBatch; //地板
GLTriangleBatch torusBatch; //大球
GLTriangleBatch sphereBatch; //小球
GLFrame cameraFrame; //照相机角色帧
//球随机数
#define NUM_SPHERES 50
GLFrame spheres[NUM_SPHERES];
//ChangeSize 函数:⾃定义函数.通过glutReshaperFunc(函数名)注册为重塑函数.当屏幕⼤⼩发⽣变化/或者第⼀次创建窗⼝时,会调⽤该函数调整窗⼝⼤⼩/视⼝⼤⼩.
void ChangeSize(int w ,int h)
{
//设置视口窗口尺寸
glViewport(0, 0, w, h);
//创建投影矩阵,
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f);
//加载到投影矩阵堆栈上
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//设置变换管道以使用两个矩阵堆栈
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
void SetupRC()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
shaderManager.InitializeStockShaders();
//开启深度测试
glEnable(GL_DEPTH_TEST);
//设置地板顶点数据
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();
//设置大球模型
gltMakeSphere(torusBatch, 0.4f, 40, 80);
//设置小球模型
gltMakeSphere(sphereBatch, 0.1f, 26, 13);
//随机位置放置小球
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);
}
}
//上下左右键位控制移动
void SpecialKeys(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.0f, 1.0f, 0.0f);
}
if (key == GLUT_KEY_RIGHT)
{
cameraFrame.RotateWorld(-angular, 0.0f, 1.0f, 0.0f);
}
}
//RenderScene 函数:⾃定义函数.通过glutDisplayFunc(函数名)注册为显示渲染函数.当屏幕发⽣变化/或者开发者主动渲染会调⽤此函数,⽤来实现数据->渲染过程
void RenderScene(void)
{
//清理缓存区(颜⾊,深度,模板缓存区等)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//颜色值(地板,大球,小球颜色)
static GLfloat vFloorColor[] = { 0.0f, 1.0f, 0.0f, 1.0f};
static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f};
static GLfloat vSphereColor[] = { 0.0f, 0.0f, 1.0f, 1.0f};
//基于时间动画
static CStopWatch rotTimer;
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
//加入观察者
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.PushMatrix(mCamera);
//获取光源位置
M3DVector4f vLightPos = {0.0f,10.0f,5.0f,1.0f};
//绘制地板
shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vFloorColor);
floorBatch.Draw();
//大球默认位置(0,0,0)Z深度(3.0) 正负指的是方向, 数字指的移动距离
//使得大球位置平移(3.0)向屏幕里面 只移动1次
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();
//画小球
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();
}
modelViewMatrix.Rotate(yRot * -2.0f, 0.0f, 1.0f, 0.0f);
modelViewMatrix.Translate(0.8f, 0.0f, 0.0f);
shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vSphereColor);
sphereBatch.Draw();
modelViewMatrix.PopMatrix();
//同步绘制命令
glutSwapBuffers();
glutPostRedisplay();
}
//main 函数: 程序⼊⼝.OpenGL 是⾯向过程编程.所以你会发现利⽤OpenGL处理图形/图像都是链式形式.以及基于OpenGL封装的图像处理框架也是链式编程
int main(int argc, char* argv[])
{
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
//申请一个颜色缓存区、双缓存区、深度缓存区
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
//设置窗口的尺寸
glutInitWindowSize(800, 800);
//设置窗口的名称
glutCreateWindow("球的公转自转");
//注册回调函数(改变尺寸)
glutReshapeFunc(ChangeSize);
//注册显示函数
glutDisplayFunc(RenderScene);
glutSpecialFunc(SpecialKeys);
GLenum err = glewInit();
if (GLEW_OK != err)
{
fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
return 1;
}
SetupRC();
//runloop运行循环
glutMainLoop();
return 0;
}
运行后效果图
1.png
网友评论