本文为L_Ares个人写作,包括图片皆为个人亲自操作,以任何形式转载请表明原文出处。
本节作为前几节知识的综合训练。只放代码,里面有非常详细的注释了,效果图如文章末尾"效果图"所示。
建议电脑配置不高的同学们不要把球的数据绘制的太多,数据可以随意减少的,2020款最新MacPro32+512G的电脑,跑时间长一点还是会发热的。
//
// main.cpp
// 08综合
//
// Created by EasonLi on 2020/8/30.
// Copyright © 2020 EasonLi. All rights reserved.
//
#include <stdio.h>
#pragma mark - 引用类
//GLTools工具类。包含了绝大多数GLTool中类似C语言的函数
#include "GLTools.h"
//着色器管理器
#include "GLShaderManager.h"
//参考帧。表示世界坐标系中任意物体的位置与方向
#include "GLFrame.h"
//三角形批次类。容器类
#include "GLBatch.h"
//矩阵堆栈类
#include "GLMatrixStack.h"
//管道类。管理模型视图投影矩阵堆栈的
#include "GLGeometryTransform.h"
//矩阵投影工具类
#include "GLFrustum.h"
//一个计时器
#include "StopWatch.h"
//3d数学库
#include "math3d.h"
//宏定义根据不同的系统引入不同的GLUT
//Windows用静态的,MacOS用glut/glut.h
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
#pragma mark - 公共变量
//着色器管理器
GLShaderManager shaderManager;
//容器类
//地板线条的三角形批次类容器
GLBatch floorBatch;
//大球容器类
GLTriangleBatch bigSphereBatch;
//小球容器类
GLTriangleBatch smallSphereBatch;
//参考帧
//观察者参考帧
GLFrame cameraFrame;
//矩阵堆栈类
//模型视图矩阵堆栈
GLMatrixStack modelViewMatrixStack;
//投影视图矩阵堆栈
GLMatrixStack projectionMatrixStack;
//矩阵投影工具类
GLFrustum viewFrustum;
//矩阵堆栈管道
GLGeometryTransform transformPipeline;
//常规属性
//地板颜色
GLfloat vGreen[] = {0.f,1.f,0.f,1.f};
//大球颜色
GLfloat vRed[] = {1.f,0.f,0.f,1.f};
//小球颜色
GLfloat vBlue[] = {0.f,0.f,1.f,1.f};
//小球的数量
#define SMALL_SPHERE_NUM 50
//定义一个小球数组
GLFrame spheresNum[SMALL_SPHERE_NUM];
#pragma mark - 函数
//设置窗口尺寸等关联信息
void ChangeSize(int w,int h)
{
//设置视口
glViewport(0, 0, w, h);
//创建投影矩阵
viewFrustum.SetPerspective(36.f, float(w)/float(h), 1.f, 500.f);
//将投影矩阵加载到投影矩阵堆栈中
projectionMatrixStack.LoadMatrix(viewFrustum.GetProjectionMatrix());
//设置矩阵堆栈变换管道来管理矩阵堆栈
//GLGeometryTransform的初始化:
//通过将GLGeometryTransform对象的内部指针分别设置成modelViewMatrixStack和projectionMatrixStack来完成初始化。
transformPipeline.SetMatrixStacks(modelViewMatrixStack, projectionMatrixStack);
}
//设置渲染环境
void SetUpRC()
{
/**********************公共**********************/
//设置清屏颜色,避免残留的屏幕颜色对渲染造成影响
glClearColor(0.f, 0.f, 0.f, 1.f);
//初始化着色器管理器
shaderManager.InitializeStockShaders();
//因为要用到立体图形了,需要立体的效果,所以开启深度测试
glEnable(GL_DEPTH_TEST);
/***********************************************/
/********************设置地板********************/
//设置地板的图元和顶点数量
floorBatch.Begin(GL_LINES, 324);
//利用for循环设置坐标
//x坐标的范围随意去定,每次+0.5控制的是没两条平行线之间的距离
//值越小,平行线密度越大
for (GLfloat x = -20.f; x <= 20.f; x += 0.5f) {
//设置纵向的线
//因为我们要的是地板的效果,所以发生变化的是x和z,y是不变的,这才是一个地板
//平面的效果
floorBatch.Vertex3f(x, -0.5f, 20.f);
floorBatch.Vertex3f(x, -0.5f, -20.f);
//设置横向的线
floorBatch.Vertex3f(20.f, -0.5f, x);
floorBatch.Vertex3f(-20.f, -0.5f, x);
}
//完成地板批次类的设置
floorBatch.End();
/***********************************************/
/********************设置大球*********************/
//直接用系统给的就行
gltMakeSphere(bigSphereBatch, 0.5f, 50, 100);
/***********************************************/
/********************设置小球*********************/
//直接用系统给的就行
gltMakeSphere(smallSphereBatch, 0.1f, 13, 26);
//利用for循环随机放置小球位置
for (int i = 0; i < SMALL_SPHERE_NUM; i++) {
//因为还是同一平面的问题,从我们的角度来看,就是Y轴不变,那么X,Z轴随机坐标
GLfloat x = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
GLfloat z = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
//对数组中的每一个顶点都设置顶点数据
//Y就给0,看起来效果像是飘起来的
spheresNum[i].SetOrigin(x, 0.f, z);
}
/***********************************************/
}
//渲染
void RenderScene()
{
/**********************公共**********************/
//清空缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//定义一个矩阵用来存储观察者矩阵
M3DMatrix44f mCamera;
//获取观察者矩阵并且放入上面定义的观察者矩阵
cameraFrame.GetCameraMatrix(mCamera);
//把观察者矩阵压入模型视图矩阵堆栈,做好乘法,矩阵变换,放在栈顶
modelViewMatrixStack.PushMatrix(mCamera);
/***********************************************/
/********************绘制地板********************/
//使用着色器
shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vGreen);
//利用地板的三角形批次类绘制
floorBatch.Draw();
/***********************************************/
/********************绘制大球********************/
//定义一个静态的定时器,全局都要用。
static CStopWatch timer;
//设置每秒的旋转角度
float yRot = timer.GetElapsedSeconds() * 60.f;
//给定一个光源位置,因为光源是向量,所以如下定义
M3DVector4f lightPos = {0.f,10.f,5.f,1.f};
//给大球一个平移,这样保证大球不在原点位置,不然和观察者重叠了
modelViewMatrixStack.Translate(0.f, 0.f, -3.f);
/**现在模型视图矩阵堆栈里面从栈底到栈顶有如下几个矩阵:*/
//观察者mv平移矩阵
//压栈。因为后面要绘制其他的东西,大球就画自己的,画完就出栈,不要影响其他的绘制
modelViewMatrixStack.PushMatrix();
/**现在模型视图矩阵堆栈里面从栈底到栈顶有如下几个矩阵:*/
//观察者mv平移矩阵-->观察者mv平移矩阵
//设置大球自转
modelViewMatrixStack.Rotate(yRot, 0.f, 1.f, 0.f);
//设置成点光源着色器
shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,transformPipeline.GetModelViewMatrix(),transformPipeline.GetProjectionMatrix(),lightPos,vRed);
//利用大球的三角形批次类绘制
bigSphereBatch.Draw();
/**现在模型视图矩阵堆栈里面从栈底到栈顶有如下几个矩阵:*/
//观察者mv平移矩阵
modelViewMatrixStack.PopMatrix();
/***********************************************/
/********************绘制随机小球******************/
//还是利用for循环把随机出来的小球绘制出来
for (int i = 0; i < SMALL_SPHERE_NUM; i++) {
//压栈完成后
/**现在模型视图矩阵堆栈里面从栈底到栈顶有如下几个矩阵:*/
//观察者mv平移矩阵-->观察者mv平移矩阵
modelViewMatrixStack.PushMatrix();
//直接给数组中的每一个小球参考帧矩阵都做矩阵变换
/**完成后,现在模型视图矩阵堆栈里面从栈底到栈顶有如下几个矩阵:*/
//观察者mv平移矩阵-->观察者mv平移和小球位置矩阵
modelViewMatrixStack.MultMatrix(spheresNum[i]);
//设置着色器
shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,transformPipeline.GetModelViewMatrix(),transformPipeline.GetProjectionMatrix(),lightPos,vBlue);
//利用小球的三角形批次类绘制
smallSphereBatch.Draw();
//出栈
/**完成后,现在模型视图矩阵堆栈里面从栈底到栈顶有如下几个矩阵:*/
//观察者mv平移矩阵
modelViewMatrixStack.PopMatrix();
}
/***********************************************/
/********************绘制公转小球******************/
//设置绕大球公转(其实就是绕着Y轴公转,因为大球中心线和Y轴是同平面中的平行关系)
//现在的mv矩阵堆栈中只有观察者mv矩阵,旋转后就变成了观察者mv旋转矩阵
modelViewMatrixStack.Rotate(yRot * 2.f, 0.f, 1.f, 0.f);
//再把小球拉出来点,拉出的距离必须比大球大,不然就不是绕着大球公转了
//平移后变成观察者mv旋转平移矩阵
modelViewMatrixStack.Translate(1.f, 0.f, 0.f);
//着色器设置
shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,transformPipeline.GetModelViewMatrix(),transformPipeline.GetProjectionMatrix(),lightPos,vBlue);
//小球三角形批次类绘制
smallSphereBatch.Draw();
/***********************************************/
/**********************公共**********************/
//出栈。回复到最初的原始堆栈
modelViewMatrixStack.PopMatrix();
//交换缓冲区
glutSwapBuffers();
glutPostRedisplay();
/***********************************************/
}
//特殊键位
void SpecialKeys(int key,int x,int y)
{
//设置前后是移动,左右是旋转
//前后每次移动的步长
float nStep = 0.1f;
//左右每次旋转的角度
float nRot = float(m3dDegToRad(5.f));
//判断键位,这里用观察者视角来做变化,也可以用物体做变化
//如果用物体的话,定义一个物体的参考帧objFrame,RenderScene中直接把objFrame
//压栈到模型视图矩阵堆栈就行了,就不要把观察者矩阵加进去了,一样能实现效果
switch (key) {
case GLUT_KEY_UP:
cameraFrame.MoveForward(nStep);
break;
case GLUT_KEY_DOWN:
cameraFrame.MoveForward(-nStep);
break;
case GLUT_KEY_RIGHT:
cameraFrame.RotateWorld(-nRot, 0.f, 1.f, 0.f);
break;
case GLUT_KEY_LEFT:
cameraFrame.RotateWorld(nRot, 0.f, 1.f, 0.f);
break;
}
//这里就不用发送重新渲染的信号了,因为RenderScene中有个定时器,已经在
//RenderScene中一直的重新渲染了,加上也无所谓
glutPostRedisplay();
}
#pragma mark - main
int main(int argc,char *argv[])
{
//设置工作目录和项目目录都到/Resouce下面
//GLUT优先级已经设置过,手动保证安全
gltSetWorkingDirectory(argv[0]);
//GLUT初始化
glutInit(&argc, argv);
//初始化显示模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
//设置窗口尺寸
glutInitWindowSize(600, 600);
//创建窗口并命名
glutCreateWindow("综合");
//注册相关函数
//重塑函数
glutReshapeFunc(ChangeSize);
//渲染函数
glutDisplayFunc(RenderScene);
//特殊键位函数
glutSpecialFunc(SpecialKeys);
//初始化Glew库
GLenum status = glewInit();
if (status != GLEW_OK) {
printf("glew init error : %s \n",glewGetErrorString(status));
return 1;
}
//设置渲染环境
SetUpRC();
//构建本地循环
glutMainLoop();
return 0;
}
效果图如下,大球是自转的,就是看的不清楚,小球是围绕大球公转的,大球的自转后面加入纹理后可以看的很清楚。
效果图.png
网友评论