// SphereWorld.cpp
// OpenGL SuperBible
// New and improved (performance) sphere world
// Program by Richard S. Wright Jr.
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef __APPLE__
#include
#else
#define FREEGLUT_STATIC
#include
#endif
#define NUM_SPHERES 50
GLFrame spheres[NUM_SPHERES];
GLShaderManager shaderManager; // Shader Manager
GLMatrixStack modelViewMatrix; // Modelview Matrix
GLMatrixStack projectionMatrix; // Projection Matrix
GLFrustum viewFrustum; // View Frustum
GLGeometryTransform transformPipeline; // Geometry Transform Pipeline
GLTriangleBatch torusBatch;
GLBatch floorBatch;
GLTriangleBatch sphereBatch;
GLFrame cameraFrame;
//////////////////////////////////////////////////////////////////
// This function does any needed initialization on the rendering
// context.
void SetupRC()
{
// Initialze Shader Manager
shaderManager.InitializeStockShaders();
glEnable(GL_DEPTH_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// This makes a torus
gltMakeTorus(torusBatch, 0.4f, 0.15f, 30, 30);
// This make a sphere
gltMakeSphere(sphereBatch, 0.1f, 26, 13);
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();
// Randomly place the spheres
for(int i = 0; i < NUM_SPHERES; i++) {
GLfloat x = ((GLfloat)((rand() % 400) - 200) * 0.1f);
GLfloat z = ((GLfloat)((rand() % 400) - 200) * 0.1f);
spheres[i].SetOrigin(x, 0.0f, z);
}
}
///////////////////////////////////////////////////
// Screen changes size or is initialized
void ChangeSize(int nWidth, int nHeight)
{
glViewport(0, 0, nWidth, nHeight);
// Create the projection matrix, and load it on the projection matrix stack
viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
// Set the transformation pipeline to use the two matrix stacks
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
// Called to draw scene
void RenderScene(void)
{
// Color values
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 };
// Time Based animation
static CStopWatch rotTimer;
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
// Clear the color and depth buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Save the current modelview matrix (the identity matrix)
modelViewMatrix.PushMatrix();
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.PushMatrix(mCamera);
// Transform the light position into eye coordinates
M3DVector4f vLightPos = { 0.0f, 10.0f, 5.0f, 1.0f };
M3DVector4f vLightEyePos;
m3dTransformVector4(vLightEyePos, vLightPos, mCamera);
// Draw the ground
shaderManager.UseStockShader(GLT_SHADER_FLAT,
transformPipeline.GetModelViewProjectionMatrix(),
vFloorColor);
floorBatch.Draw();
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(), vLightEyePos, vSphereColor);
sphereBatch.Draw();
modelViewMatrix.PopMatrix();
}
// Draw the spinning Torus
modelViewMatrix.Translate(0.0f, 0.0f, -2.5f);
// Save the Translation
modelViewMatrix.PushMatrix();
// Apply a rotation and draw the torus
modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(), vLightEyePos, vTorusColor);
torusBatch.Draw();
modelViewMatrix.PopMatrix(); // "Erase" the Rotation from before
// Apply another rotation, followed by a translation, then draw the sphere
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(), vLightEyePos, vSphereColor);
sphereBatch.Draw();
// Restore the previous modleview matrix (the identity matrix)
modelViewMatrix.PopMatrix();
modelViewMatrix.PopMatrix();
// Do the buffer Swap
glutSwapBuffers();
// Tell GLUT to do it again
glutPostRedisplay();
}
// Respond to arrow keys by moving the camera frame of reference
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);
}
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");
glutSpecialFunc(SpecialKeys);
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
return 0;
}
上面是源码,我分析下各个函数各行代码的作用,帮助自己理解下OpenGL的大致结构。
首先是一些头文件的包含,因为会用到OpenGL的一些API。
GLShaderManager是着色管理器,没有着色管理器,就没法着色;作色存储器还提供存储着色器,能够进行一些初步和基本的渲染操作。
在Mac和Windows|Linux上,我们将会采用不同的处理器宏定义
#ifdef __APPLE__
#include
#else
#define FREEGLUT_STATIC
#include
#endif
main()函数作为程序的入口函数,得先从这边来分析。
gltSetWorkingDirectory(argv[0]);
上函数是用来设置当前工作目录。
//传递命令行参数并初始化GLUT库
glutInit(&argc, argv);
//告诉GLUT库,在创建窗口时要使用哪种类型的显示模式
//GLUT_DOUBLE表示使用双缓冲窗口,一般用来生成动画效果
//GLUT_RGB表示使用RGB颜色模式
//GLUT_DEPTH表示我们可以进行深度测试
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
//指明GLUT窗口的大小,并以OpenGL SphereWorld为标题创建窗口。
glutInitWindowSize(800,600);
glutCreateWindow("OpenGL SphereWorld");
GLUT内部运行一个本地消息循环,拦截适当的消息,然后调用相应的回调函数。我们必须为窗口改变大小而设置一个回调函数,以便能够设置视点,还要注册一个函数来包含OpenGL渲染代码。如下:
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
初始化OpenGL驱动程序,并检查初始化是否出现问题
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
在任何OpenGL函数起作用之前都得创建一个渲染环境,在这个函数里面我们会进行预加载纹理,建立几何图形、渲染器等工作。
SetupRC();
开始上面讲过的本地消息循环,并结束main函数。
glutMainLoop();
return 0;
glutMainLoop函数被调用之后,在主窗口被关闭之前都不会返回,一个应用程序只需要调用一次。这个函数负责处理所有操作系统特定的消息、按键动作等,直到我们关闭程序为止。它还能确保我们的这些回调函数被正确地调用。
ChangeSize函数中:
glViewport(x, y, w, h)
定义窗口与视口之间的关系,x参数和y参数代表窗口中视口的左下角坐标,w和h代表视口的宽带和高度,如下图所示:
glViewport(0, 0, nWidth, nHeight);
SetupRC中:
glClearColor是用来设置背景颜色,该函数不会立即清除背景,而是设置在以后颜色缓冲区被清除时(调用glClear函数)使用的颜色。
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
当设置OpenGL窗口时,我们制定要一个双缓冲渲染环境。这就意味着将在后台缓冲区进行渲染,然后结束的时候交换到前台缓冲区。这种形式能够防止观察者可能看到伴随着动画帧与动画帧之间闪烁的渲染过程。OpenGL有一个特定的函数来完成上述工作:
glutSwapBuffers();
刷新显示:告诉窗口需要更新内容
glutPostRedisplay
该函数来告诉GLUT发生了某些变化,应该对场景重新进行渲染