#include "GLShaderManager.h"
#include "GLTools.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
GLShaderManager shaderManager; //着色管理器
GLMatrixStack modelViewMatrix; //模型视图矩阵
GLMatrixStack projectionMatrix; //投影矩阵
GLFrustum viewFrustum; //视景体
GLGeometryTransform transformPipeline; //几何图形变换管道
GLTriangleBatch torusBatch; //打球
GLTriangleBatch sphereBatch; //小球
GLBatch floorBatch; //地板
GLFrame cameraFrame; //照相机角色帧
//添加附加随机球
#define NUM_SPHERES 50
GLFrame spheres[NUM_SPHERES];
//为程序做一次性的设置
void setupRC() {
//1.初始化
glClearColor(0, 0, 0, 1);
shaderManager.InitializeStockShaders();
//2.开启深度测试
glEnable(GL_DEPTH_TEST);
//3.设置地板顶点数据
floorBatch.Begin(GL_LINES, 324);
for (GLfloat x = -20; x <= 20; x += 0.5) {
floorBatch.Vertex3f(x, -0.55, 20);
floorBatch.Vertex3f(x, -0.55, -20);
floorBatch.Vertex3f(20, -0.55, x);
floorBatch.Vertex3f(-20, -0.55, x);
}
floorBatch.End();
//4.设置打球模型
gltMakeSphere(torusBatch, 0.4, 40, 80);
//5.设置小球模型
gltMakeSphere(sphereBatch, 0.1, 26, 13);
//6.随机位置放置小球
for (int i = 0; i < NUM_SPHERES; i ++) {
//y轴不变,X,Z产生随机值
GLfloat x = ((GLfloat)(rand() % 400 - 200)) * 0.1;
GLfloat z = ((GLfloat)(rand() % 400 - 200)) * 0.1;
//在y方向,将球体设置为0,0的位置,这使得它们看起来是漂浮在眼镜的高度
//对spheres数组中的每一个顶点,设置顶点数据
spheres[i].SetOrigin(x, 0, z);
}
}
//绘制场景
void renderScene(void) {
//1.颜色值
static GLfloat vFloorColor[] = { 0, 1, 0, 1 };
static GLfloat vTorusColor[] = { 1, 0, 0, 1 };
static GLfloat vSphereColor[] = { 0, 0, 1, 1 };
//2.基于时间动画
static CStopWatch rotTimer;
float yRot = rotTimer.GetElapsedSeconds() * 60;
//3.清除时间缓冲区和深度缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//4.加入观察者平移10步(地板,打球,小球,小小球)
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.PushMatrix(mCamera);
//5.获取光源位置
M3DVector4f vLightPos = { 0, 10, 5, 1 };
//6.绘制地面
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vFloorColor);
floorBatch.Draw();
//打球默认位置是(0,0,0)Z深度(3)正负指的是方向,数字指的是移动距离
//7.使得大球位置平移(3)向屏幕里面,只移动一次
modelViewMatrix.Translate(0, 0, -3);
//8.压栈(复制栈顶)
modelViewMatrix.PushMatrix();
//9.大球自转
modelViewMatrix.Rotate(yRot, 0, 1, 0);
//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, 0, 1, 0);
modelViewMatrix.Translate(0.8, 0, 0);
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vSphereColor);
sphereBatch.Draw();
modelViewMatrix.PopMatrix();
modelViewMatrix.PopMatrix();
glutSwapBuffers();
glutPostRedisplay();
}
//窗口改变时接受新的宽度和高度,0,0代表窗口中视口的左下角坐标,w,h代表像素
void ChangeSize(int w, int h) {
//1.设置视口
glViewport(0, 0, w, h);
//2.创建投影矩阵并加载到投影矩阵堆栈中
viewFrustum.SetPerspective(35, float(w) / float(h), 1, 100);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//3.这个操作也可以在SetupRC 函数中完成,但是在窗口大小改变时或者窗口创建时设置它们并没有坏处。而且这样可以一次性完成矩阵和管线的设置。
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
void specialKeys(int key, int x, int y) {
//移动步长
float linear = 0.1;
//旋转度数
float angular = float(m3dDegToRad(5));
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);
}
}
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(specialKeys);
GLenum err = glewInit();
if (err != GLEW_OK) {
fprintf(stderr, "GLEW Error:%s\n",glewGetErrorString(err));
return 1;
}
setupRC();
glutMainLoop();
return 0;
}
网友评论