详细代码参见Demo
Demo地址 -> OpenGLDemos -> 2.pyramid
代码解析:
#include
#include "GLTools.h"
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLGeometryTransform.h"
#include
#ifdef __APPLE__
#include
#else
#define FREEGLUT_STATIC
#include
#endif
//着色器管理类
GLShaderManager shaderManager;
//模型视图矩阵堆栈
GLMatrixStack modelViewMatrix;
//投影矩阵堆栈
GLMatrixStack projectionMatrix;
//照相机 参数帧
GLFrame cameraFrame;
//模型 参考帧
GLFrame objectFrame;
//投影矩阵
GLFrustum viewFrustum;
//容器类 对应GL_TRIANGLES(三角形) 图元类型
GLBatch triangleBatch;
//几何变换的管道
GLGeometryTransform transformPipeline;
GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
//跟踪效果步骤
int nStep = 0;
ChangeSize 为重塑方法,当第一次创建窗口或者窗口改变时候系统调整,主要在该方法中使用窗口维度设置视口和投影矩阵。
GLFrustum类通过setPerspective ⽅方法为我们构建⼀个平截头体。
CLFrustum::SetPerspective(float fFov,float fAspect,float fNear ,float fFar);
参数:
fFov:垂直⽅向上的视场⻆度
fAspect:窗口的宽度与高度的纵横比
fNear:近裁剪⾯距离 (视角到近裁剪面距离为fNear)
fFar:远裁剪面距离(视角到远裁剪面距离为fFar)
纵横比 = 宽(w)/⾼(h)
//设置视口的大小等
void ChangeSize(int w, int h)
{
// 1、设置视口大小
glViewport(0, 0, w, h);
// 2、创建投影矩阵,并将它载入投影矩阵堆栈中
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 500.0f);
// 3、将投影矩阵载入投影堆栈
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
// 将单元矩阵载入模型视图矩阵堆栈
modelViewMatrix.LoadIdentity();
}
此函数在呈现上下文中进行任何必要的初始化
这是第一次或任何与openGL 相关的任务
SetupRC 是在 main 方法中手动调用,且只执行一次,主要是做着色器初始化、窗口背景初始化、顶点数据的准备等工作,在这个案例中,此方法主要是初始化了绘制金字塔需要的顶点数据。
//此函数在呈现上下文中进行任何必要的初始化
//这是第一次或任何与openGL 相关的任务
void SetupRC()
{
// 1、灰色的背景
glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
// 2、初始化着色器
shaderManager.InitializeStockShaders();
// 3、启动深度测试,不开启的话看到的黑色边线会透视过来(如图)
glEnable(GL_DEPTH_TEST);
// 4、设置变换管线以使用两个矩阵堆栈
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
// 5、将相机向前移动15个单元
cameraFrame.MoveForward(-15.0f);
/*
常见函数
void GLBatch::Begin(GLenum primitive,GLuint nVerts,GLuint nTextureUnits = 0);
参数1:表示使用的图元
参数2:顶点数
参数3:纹理坐标(可选)
//负责顶点坐标
void GLBatch::CopyVertexData3f(GLFloat *vNorms);
//结束,表示已经完成数据复制工作
void GLBatch::End(void);
*/
// 6、通过三角形创建金字塔
GLfloat vPyramid[12][3] = {
-2.0f, 0.0f, -2.0f,
2.0f, 0.0f, -2.0f,
0.0f, 4.0f, 0.0f,
2.0f, 0.0f, -2.0f,
2.0f, 0.0f, 2.0f,
0.0f, 4.0f, 0.0f,
2.0f, 0.0f, 2.0f,
-2.0f, 0.0f, 2.0f,
0.0f, 4.0f, 0.0f,
-2.0f, 0.0f, 2.0f,
-2.0f, 0.0f, -2.0f,
0.0f, 4.0f, 0.0f
};
// GL_TRIANGLES 每3个顶点定义一个新的三角形
triangleBatch.Begin(GL_TRIANGLES, 12);
triangleBatch.CopyVertexData3f(vPyramid);
triangleBatch.End();
}
一定要记得开启深度测试,要黑色线条会透过去的。效果如下
glEnable(GL_DEPTH_TEST);
未开启深度测试
开启深度测试
RenderScene 同 ChangeSize 一样,需要再main 函数里注册通知,当屏幕发生变化或者通过调用glutPostRedisplay 方法,从而让系统调用。
在此方法中,主要通过模型视图堆栈,做了一系列的矩阵计算,从而得到一个新的模型视图堆栈。最后通过渲染管线获取到模型视图矩阵以及投影矩阵,作为固定着色器中几何图形变换的变换矩阵。
void RenderScene(void)
{
// 用当前清除颜色清除窗口
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// 压栈
modelViewMatrix.PushMatrix();
// 相机矩阵
M3DMatrix44f mCanera;
cameraFrame.GetCameraMatrix(mCanera);
// 矩阵乘以矩阵堆栈的顶部矩阵,相乘的结果随后存入栈顶
modelViewMatrix.MultMatrix(mCanera);
M3DMatrix44f mObjectFrame;
// 只要使用 GetMatrix 函数就可以获取矩阵堆栈栈顶的值,这个函数可以进行2次重载用来使用GLShaderManager 的使用。或者是获取顶部矩阵的顶点副本数据
objectFrame.GetMatrix(mObjectFrame);
// 矩阵乘以矩阵堆栈栈顶的矩阵,相乘的结果存储在栈顶
modelViewMatrix.MultMatrix(mObjectFrame);
/* GLShaderManager 中的Uniform 值——平面着色器
参数1:平面着色器
参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
--transformPipeline.GetModelViewProjectionMatrix() 获取的
GetMatrix函数就可以获得矩阵堆栈顶部的值
参数3:颜色值(黑色)
*/
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
DrawWireFramedBatch(&triangleBatch);
// 还原到以前的模型视图矩阵(单位矩阵)
modelViewMatrix.PopMatrix();
// 进行缓冲区交换
glutSwapBuffers();
}
//通过上下左右键控制翻转
GLUT_KEY_UP(上)
GLUT_KEY_DOWN(下)
GLUT_KEY_LEFT(左)
GLUT_KEY_RIGHT(右)
m3dDegToRad(x)每次翻转x/180度
#define m3dDegToRad(x) ((x)*M3D_PI_DIV_180)
设置X Y Z 值,去乘以fAngle 得到的是相应变换后的值
void RotateWorld(float fAngle, float x, float y, float z)
例如 objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f); 每次调用向上翻转5/180 度
看一下效果
在上述的RenderScene()方法中
modelVIewMatrix.PushMatrix()
modelViewMatrix.PopMatrix()
这样的代码存在,这就是压栈与出栈,
矩阵压栈出栈的就是利用栈特性,先进后出。从栈顶入栈,从栈顶出栈。也就是说我们的矩阵是临时存入栈中,在用完需要移出。
1、PushMatrix()
压栈:和数据结构中的栈类似,调用这个方法的时候,若传入一个矩阵,则将该矩阵压入栈顶;若不传入参数,则默认 copy 一份当前栈顶矩阵,压入栈顶,主要作用就是记录状态,保存当时的临时结果。
2、MultMatrix(x)
矩阵相乘,将栈顶元素copy 一份,并于 矩阵相乘,得到结果赋值给矩阵堆栈的栈顶矩阵。
3、PopMatrix()
矩阵出栈:将栈顶矩阵出栈,恢复为原始的矩阵堆栈
在使用完该栈之后,这个方法必须调用。原因就是,之前有调大,OpenGL上下文本身就是一个巨大的状态机,若在这里不将栈顶矩阵出栈,对于在其他使用到该矩阵堆栈的地方来说,矩阵数据就会错乱。
参阅
1、案例 03:金字塔、六边形、圆环的绘制