模型视图投影源码示例
// ModelviewProjection.cpp
// OpenGL SuperBible
// Demonstrates OpenGL the ModelviewProjection matrix
// Program by Richard S. Wright Jr.
#include
// OpenGL toolkit
#include
#include
#include
#include
#include
#include
#include
#ifdef __APPLE__ #include
#else #define FREEGLUT_STATIC #include
#endif // Global view frustum class GLFrustum viewFrustum; // The shader manager GLShaderManager shaderManager; // The torus GLTriangleBatch torusBatch; // Set up the viewport and the projection matrix void ChangeSize(int w, int h) { // Prevent a divide by zero if(h == 0) h = 1; // Set Viewport to window dimensions glViewport(0, 0, w, h); viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 1000.0f); } // Called to draw scene void RenderScene(void) { // Set up time based animation static CStopWatch rotTimer; float yRot = rotTimer.GetElapsedSeconds() * 60.0f; // Clear the window and the depth buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Matrix Variables M3DMatrix44f mTranslate, mRotate, mModelview, mModelViewProjection; // Create a translation matrix to move the torus back and into sight m3dTranslationMatrix44(mTranslate, 0.0f, 0.0f, -2.5f); // Create a rotation matrix based on the current value of yRot m3dRotationMatrix44(mRotate, m3dDegToRad(yRot), 0.0f, 1.0f, 0.0f); // Add the rotation to the translation, store the result in mModelView m3dMatrixMultiply44(mModelview, mTranslate, mRotate); // Add the modelview matrix to the projection matrix, // the final matrix is the ModelViewProjection matrix. m3dMatrixMultiply44(mModelViewProjection, viewFrustum.GetProjectionMatrix(),mModelview); // Pass this completed matrix to the shader, and render the torus GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f }; shaderManager.UseStockShader(GLT_SHADER_FLAT, mModelViewProjection, vBlack); torusBatch.Draw(); // Swap buffers, and immediately refresh glutSwapBuffers(); glutPostRedisplay(); } // This function does any needed initialization on the rendering // context. void SetupRC() { // Black background glClearColor(0.8f, 0.8f, 0.8f, 1.0f ); glEnable(GL_DEPTH_TEST); shaderManager.InitializeStockShaders(); // This makes a torus gltMakeTorus(torusBatch, 0.4f, 0.15f, 30, 30); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); } /// // Main entry point for GLUT based programs int main(int argc, char* argv[]) { gltSetWorkingDirectory(argv[0]); glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL); glutInitWindowSize(800, 600); glutCreateWindow("ModelViewProjection Example"); 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; }
此源码示例可以帮助我们理解模型、视图和投影,相应的坐标系及变换等。同时需要学习的是一些向量、矩阵等一些数学知识。
前述:
此示例作为蓝宝书第四章的第一个源码示例,第三章的Primitives.cpp示例暂时先跳过,由于Primitives.cpp里面涉及了第四章的知识点,所以在后面的学习在着重解析一下,可以更好的理解模型视图投影的变换和照相机和角色的移动等知识。并且在GeoTest.cpp示例中已经大概地学习了一些模型视图投影的知识,忘记了可以回顾一下
http://blog.youkuaiyun.com/perseverancep/article/details/72639777。但是此次来加深学习理解3D图形的数学知识。
一、3D图形数学基础
在GeoTest.cpp示例中粗略地学习了一下角色参考帧(GLFrame)、平截头体(GLFrustum)、矩阵堆栈(GLMatrixStack)等类型概念,然后这些重要的知识点都与最基本的数学知识——矩阵分不开。
1、向量
math3d库有两个数据类型,能够表示一个三维或四维向量:M3DVector3f表示一个三维向量(X,Y,Z),M3DVector4可以表示一个(X,Y,Z,W)。典型情况下W坐标设为1.0。X、Y、Z值通过除以W来进行缩放,而除以1.0则本质上不改变XYZ的值。要将它们定义成数组,只需如下操作。
typedef float M3DVector3f[3]; typedef float M3DVector4f[4];
三分量声明:
M3DVector3f vVector;
四分量声明并初始化:
M3DVector4f vVertex = {0.0f,0.0f,1.0f,1.0f}
点乘:
float m3dDotProduct3(contst M3DVector3f u,const M3DVector3f v); 参数是两个3分量的单位向量,结果是一个标量(一个值)
两个单位向量之间的点乘的结果是两个向量之间的夹角的余弦值,返回的结果将在-1.0和+1.0范围之内。在后面的学习中,表面法向量和指向光源的向量之间会大量进行这种运算。
叉乘:
void m3dCrossProduct3(M3DVector3f result,const M3DVector3f u, const M3DVector3f v);参数1是叉乘的结果,参数2、3是进行叉乘的两个向量
两个向量之间叉乘所得结果是另一个向量,新向量与原来两个向量定义的平面垂直。要进行叉乘,这两个向量都不必为单位向量。叉乘的顺序非常的重要。从寻找三角形便面法线到构造变换矩阵,叉乘的应用数不胜数。
2、矩阵
在数学上,矩阵只不过是一组排列在统一的行和列中的数字而已——用程序设计的语言来说就是一个2维数组。在math3d库中也有3x3和4x4矩阵类型:
typedef float M3DMatrix33f[9];
typedef float M3DMatrix44[16];
可以声明:M3DMatrix33 matrix1;M3DMatrix44 matrix2;
二、理解变换
大多数3D图形其实并不是真正的3D的。只是使用3D概念和术语来描述物体,将3D数据被“压扁”到一个2D的计算机屏幕上。这种将3D数据被“压扁”成2D数据的处理过程叫投影。
1、视觉坐标是相对于观察者视角而言的,无论可能进行何种变换,我们都可以将它们视为“绝对的”屏幕坐标。
2、视图变换是应用到场景中的第一种变换。它用来确定场景中的有利位置。视图变换允许我们把观察点放在所希望的任何位置,并允许在任何方向上观察场景。确定视图 就像在场景中防止照相机并让它指向某个方向。从大局考虑,在应用任何其他模型变换之前,必须先应用视图变换。这样做是因为,视图变换移动了当前的工作坐标系。
3、模型变换用于操纵模型和其中的特定对象。这些变换将对象移动到需要的位置,然后再对它进行旋转和缩放。
4、视图的二元性。实际上,视图和模型变换按照它们的内部效果和对场景的最终外观来说是一样的。将两者区分开纯粹是为了程序员的方便。将对象向后移动和将参考系
向前移动在视觉上没有区别。视觉变换和模型变换一样,都应用在整个场景中,在场景中的对象常常在进行视图变换后单独进行模型变换。术语“模型视图”是指这两种
变换在变换管线中进行组合,成为一个单独的矩阵,及模型视图矩阵。
5、投影变换将在模型视图变换之后应用到顶点上。这种投影实际上定义了视景体并创建了裁剪平面。
6、视口变换是把二维投影的图形映射到屏幕上某处的窗口上。这种到物理窗口的映射是我们最后要做的变换,成为视口变换。视口变换会将“规范化”设备坐标重新映射到窗口坐标上。
三、模型视图矩阵
模型视图矩阵是一个4x4矩阵,它表示变换后的坐标系,我们可以用来设置对象和确定对象的方向。为图元提供的顶点将作为一个单列矩阵的形式来使用,并乘以一个模型
视图矩阵来获得一个相对于视觉坐标的经过变换的新坐标。
1、单位矩阵
快捷创建方式:void m3dLoadIdentity44(M3DMatrix44f m)
2、创建平移矩阵
void m3dTranslationMatrix44(M3DMatrix44f m,float x,float y,float z);
一个平移矩阵仅仅是将我们的顶点沿着3个坐标轴的一个或多个进行平移。
3、创建旋转矩阵
void m3dRotationMatrix44(M3DMatrix44f m,float angle,float x,float y,float z)
围绕一个x、y和z变量指定的向量来进行旋转。旋转的角度沿逆时针方向按照弧度计算,由变量angle指定。可以围绕一个轴进行旋转,也可以围绕任意一个由x、y、和z变量指定的向量来进行旋转。
4、创建缩放矩阵
void m3dScaleMatrix44(M3DMatrix44f m,float xScale,float yScale,float zScale)
缩放矩阵可以沿着3个坐标轴方向按照指定因子放大或缩小所有顶顶啊,以改变对象的大小。
5、综合变换
将两种变换加在加在一起就是综合变换,只需将两个矩阵相乘
void m3dMatrixMultiply44(M3DMatrix44f product,const M3DMatrix44f a,const M3DMatrix b)
四、部分源码解析
1、void RenderScene(void)
//声明一个测量运行时间的对象,并根据时间来设定旋转的角度值
static CStopWatch rotTimer;
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
//清除颜色和深度缓冲器
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//声明矩阵局部变量
M3DMatrix44f mTranslate, mRotate, mModelview, mModelViewProjection;
//创建一个平移矩阵(沿z轴向面向观察者方向移动)
m3dTranslationMatrix44(mTranslate, 0.0f, 0.0f, -2.5f);
//创建一个旋转矩阵(按时间的运行时间设定弧度值,绕y轴旋转)
//将平移矩阵和旋转矩阵相乘得到一个综合矩阵(模型矩阵)
m3dMatrixMultiply44(mModelview, mTranslate, mRotate);
//将投影矩阵和模型视图矩阵相乘得到模型视图投影矩阵
m3dMatrixMultiply44(mModelViewProjection, viewFrustum.GetProjectionMatrix(),mModelview);
//把模型视图投影矩阵提交给着色器,并且把图形批次图元也提交给着色器进行渲染
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
shaderManager.UseStockShader(GLT_SHADER_FLAT, mModelViewProjection, vBlack);
torusBatch.Draw();
//交换缓冲区,刷新显示
glutSwapBuffers();
glutPostRedisplay();
五、小结
以上剪短的源码示例,展示了矩阵变换的应用,应该多结合源码理解模式视图投影的变换及应用。可以结合GeoTest.cpp源码示例理解记忆。
http://blog.youkuaiyun.com/perseverancep/article/details/72639777