目录
案例训练
案例代码如下
/**
cameraFrame 移动的公转自转
物体移动:
物体本身移动
物体坐标系移动
观察者移动
*/
#include "LCDay5.hpp"
#include "GLTools.h"
#include "GLShaderManager.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLMatrixStack.h"
#include "GLGeometryTransform.h"
#include "StopWatch.h"
#include <math.h>
#include <stdio.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
static GLShaderManager shaderManager; // 着色器管理器
static GLMatrixStack modelViewMatrix; // 模型视图矩阵
static GLMatrixStack projectionMatrix; // 投影矩阵
static GLFrustum viewFrustum; // 视景体
static GLGeometryTransform transformPipeline; // 几何图形变换管道
static GLTriangleBatch torusBatch; //大球
static GLTriangleBatch sphereBatch; //小球
static GLBatch floorBatch; //地板
//角色帧 照相机角色帧
static GLFrame cameraFrame;
//**4、添加附加随机球
#define NUM_SPHERES 50
static GLFrame spheres[NUM_SPHERES];
static void SetupRC()
{
//1.初始化
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
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();
//4.设置大球模型
gltMakeSphere(torusBatch, 0.4f, 40, 80);
//5. 设置小球球模型
gltMakeSphere(sphereBatch, 0.1f, 26, 13);
//6. 随机位置放置小球球
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);
}
}
//进行调用以绘制场景
static 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};
static GLfloat vSphereColor[] = { 0.0f, 0.0f, 1.0f, 1.0f};
//2.基于时间动画
static CStopWatch rotTimer;
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
//3.清除颜色缓存区和深度缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//4.加入观察者 平移10步(地板,大球,小球,小小球)
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.PushMatrix(mCamera);
//5.获取光源位置
M3DVector4f vLightPos = {0.0f,10.0f,5.0f,1.0f};
//6.绘制地面
shaderManager.UseStockShader(GLT_SHADER_FLAT,
transformPipeline.GetModelViewProjectionMatrix(),
vFloorColor);
floorBatch.Draw();
//大球默认位置(0,0,0)Z深度(3.0) 正负指的是方向, 数字指的移动距离
//7.使得大球位置平移(3.0)向屏幕里面 只移动1次
modelViewMatrix.PushMatrix();
modelViewMatrix.Translate(0.0f, 0.0f, -3.0f);
//8.压栈(复制栈顶)
modelViewMatrix.PushMatrix();
//9.大球自转
modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
//10.指定合适的着色器(点光源着色器)
shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(), vLightPos, vTorusColor);
torusBatch.Draw();
//11.绘制完毕则Pop
modelViewMatrix.PopMatrix();
//12.画小球
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();
}
//13. 让一个小篮球围绕大球公众自转
//学员提问: 为什么这里老师没有加PushMatrix/PopMatrix ,是可以加的.但是因为是最后的绘图因为没有不会影响就么有添加. 这边给大家添加一组.
modelViewMatrix.PushMatrix();
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();
//移除z轴的平移
modelViewMatrix.PopMatrix();
//移除观察者矩阵
modelViewMatrix.PopMatrix();
//14.执行缓存区交换
glutSwapBuffers();
//15
glutPostRedisplay();
}
//屏幕更改大小或已初始化
static 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);
}
static 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);
}
}
int fifth(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();
return 0;
}
重点说明
名称 | 解释及作用 |
cameraFrame | 观察者位置矩阵,默认位置在原点。 |
objectFrame | 物体位置,默认位置在原点 |
矩阵 | 主要用于记录变换 |
矩阵堆栈 | 为了更好的管理矩阵。矩阵堆栈一定要保持平衡。在绘制到最后一个图形时大部分情况其实不需要push和pop了,但最好进行一次push和pop操作,避免引起一些其它问题(比如递归调用),养成良好的习惯 |
变换管线 | 为了更好的管理矩阵堆栈,方便从矩阵堆栈中取出矩阵 |
所以最重要的还是矩阵,因为矩阵代表变化。使用不同的矩阵绘制,效果就会不同。比如如果绘制图形的矩阵中包含了观察者矩阵,那么我们就可以通过移动观察者(改变观察者矩阵)来实现类似游戏中移动的场景,如上边案例中的效果。
行者常至,为者常成!