目录
平移、旋转、缩放
下面只贴出了关键代码,仅供参考
static void SetupRC(){
//1.初始化
glClearColor(0.0f, 0.0f, 1.0f, 1.0f );
shaderManager.InitializeStockShaders();
//2.加载三角形
squareBatch.Begin(GL_TRIANGLE_FAN, 4);
squareBatch.CopyVertexData3f(vVerts);
squareBatch.End();
}
static void RenderScene(void){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
M3DMatrix44f mFinalTransform, mTranslationMatrix, mRotationMatrix,mScaleMatrix;
//平移 xPos,yPos
m3dTranslationMatrix44(mTranslationMatrix, xPos, yPos, 0.0f);
//旋转 rotatePos
m3dRotationMatrix44(mRotationMatrix, m3dDegToRad(rotatePos), 0.0f, 0.0f, 1.0f);
//缩放 scalePos
m3dScaleMatrix44(mScaleMatrix, scalePos, scalePos, scalePos);
//将旋转和移动的结果合并到mFinalTransform 中
m3dMatrixMultiply44(mFinalTransform, mTranslationMatrix, mRotationMatrix);
m3dMatrixMultiply44(mFinalTransform, mFinalTransform, mScaleMatrix);
//将矩阵结果提交到固定着色器(平面着色器)中。
shaderManager.UseStockShader(GLT_SHADER_FLAT, mFinalTransform, vRed);
squareBatch.Draw();
// 执行缓冲区交换
glutSwapBuffers();
}
理解变换
变换 | 应用 |
视图 | 指定观察者位置 |
模型 | 在场景中移动物体 |
模型视图 | 描述视图/模型变换的二元性 |
投影 | 改变视景体大小和设置它的投影方式 |
视口 | 伪变化,对窗口上最终输出进行缩放 |
一、看一个案例
1、代码如下
#include "GLTools.h" // OpenGL toolkit
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLGeometryTransform.h"
#include "StopWatch.h"
#include <math.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 GLFrame cameraFrame;
//世界坐标位置
static GLFrame objectFrame;
//视景体,用来构造投影矩阵
static GLFrustum viewFrustum;
//三角形批次类
static GLTriangleBatch CC_Triangle;
//球
static GLTriangleBatch sphereBatch;
//环
static GLTriangleBatch torusBatch;
//圆柱
static GLTriangleBatch cylinderBatch;
//锥
static GLTriangleBatch coneBatch;
//磁盘
static GLTriangleBatch diskBatch;
static GLGeometryTransform transformPipeline;
static M3DMatrix44f shadowMatrix;
static GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
static GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
static int nStep = 0;
// 将上下文中,进行必要的初始化
static void SetupRC()
{
//1.
glClearColor(0.7f, 0.7f, 0.7f, 1.0f );
shaderManager.InitializeStockShaders();
//2.开启深度测试
glEnable(GL_DEPTH_TEST);
//3.将观察者坐标位置Z移动往屏幕里移动15个单位位置
//表示离屏幕之间的距离 负数,是往屏幕后面移动;正数,往屏幕前面移动
cameraFrame.MoveForward(-15.0f);
//4.利用三角形批次类构造图形对象
// 球
/*
gltMakeSphere(GLTriangleBatch& sphereBatch, GLfloat fRadius, GLint iSlices, GLint iStacks);
参数1:sphereBatch,三角形批次类对象
参数2:fRadius,球体半径
参数3:iSlices,从球体底部堆叠到顶部的三角形带的数量;其实球体是一圈一圈三角形带组成
参数4:iStacks,围绕球体一圈排列的三角形对数
建议:一个对称性较好的球体的片段数量是堆叠数量的2倍,就是iStacks = 2 * iSlices;
绘制球体都是围绕Z轴,这样+z就是球体的顶点,-z就是球体的底部。
*/
gltMakeSphere(sphereBatch, 3.0, 10, 20);
// 环面
/*
gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
参数1:torusBatch,三角形批次类对象
参数2:majorRadius,甜甜圈中心到外边缘的半径
参数3:minorRadius,甜甜圈中心到内边缘的半径
参数4:numMajor,沿着主半径的三角形数量
参数5:numMinor,沿着内部较小半径的三角形数量
*/
gltMakeTorus(torusBatch, 3.0f, 0.75f, 15, 15);
// 圆柱
/*
void gltMakeCylinder(GLTriangleBatch& cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks);
参数1:cylinderBatch,三角形批次类对象
参数2:baseRadius,底部半径
参数3:topRadius,头部半径
参数4:fLength,圆形长度
参数5:numSlices,围绕Z轴的三角形对的数量
参数6:numStacks,圆柱底部堆叠到顶部圆环的三角形数量
*/
gltMakeCylinder(cylinderBatch, 2.0f, 2.0f, 3.0f, 15, 2);
//锥
/*
void gltMakeCylinder(GLTriangleBatch& cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks);
参数1:cylinderBatch,三角形批次类对象
参数2:baseRadius,底部半径
参数3:topRadius,头部半径
参数4:fLength,圆形长度
参数5:numSlices,围绕Z轴的三角形对的数量
参数6:numStacks,圆柱底部堆叠到顶部圆环的三角形数量
*/
//圆柱体,从0开始向Z轴正方向延伸。
//圆锥体,是一端的半径为0,另一端半径可指定。
gltMakeCylinder(coneBatch, 2.0f, 0.0f, 3.0f, 13, 2);
// 磁盘
/*
void gltMakeDisk(GLTriangleBatch& diskBatch, GLfloat innerRadius, GLfloat outerRadius, GLint nSlices, GLint nStacks);
参数1:diskBatch,三角形批次类对象
参数2:innerRadius,内圆半径
参数3:outerRadius,外圆半径
参数4:nSlices,圆盘围绕Z轴的三角形对的数量
参数5:nStacks,圆盘外网到内围的三角形数量
*/
gltMakeDisk(diskBatch, 1.5f, 3.0f, 13, 3);
}
static void DrawWireFramedBatch(GLTriangleBatch* pBatch)
{
//----绘制图形----
//1.平面着色器,绘制三角形
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
//传过来的参数,对应不同的图形Batch
pBatch->Draw();
//---画出黑色轮廓---
//2.开启多边形偏移
glEnable(GL_POLYGON_OFFSET_LINE);
//多边形模型(背面、线) 将多边形背面设为线框模式
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//开启多边形偏移(设置偏移数量)
glPolygonOffset(-1.0f, -1.0f);
//线条宽度
glLineWidth(2.5f);
//3.开启混合功能(颜色混合&抗锯齿功能)
glEnable(GL_BLEND);
//开启处理线段抗锯齿功能
glEnable(GL_LINE_SMOOTH);
//设置颜色混合因子
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//4.平面着色器绘制线条
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
pBatch->Draw();
//5.恢复多边形模式和深度测试
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDisable(GL_POLYGON_OFFSET_LINE);
glLineWidth(1.0f);
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
}
//召唤场景
static void RenderScene(void)
{
//1.用当前清除颜色清除窗口背景
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//2.模型视图矩阵栈堆,压栈
modelViewMatrix.PushMatrix();
//3.获取摄像头矩阵
M3DMatrix44f mCamera;
//从camereaFrame中获取矩阵到mCamera
cameraFrame.GetCameraMatrix(mCamera);
//模型视图堆栈的 矩阵与mCamera矩阵 相乘之后,存储到modelViewMatrix矩阵堆栈中
modelViewMatrix.MultMatrix(mCamera);
//4.创建矩阵mObjectFrame
M3DMatrix44f mObjectFrame;
//从ObjectFrame 获取矩阵到mOjectFrame中
objectFrame.GetMatrix(mObjectFrame);
//将modelViewMatrix 的堆栈中的矩阵 与 mOjbectFrame 矩阵相乘,存储到modelViewMatrix矩阵堆栈中
modelViewMatrix.MultMatrix(mObjectFrame);
//5.判断你目前是绘制第几个图形
switch(nStep) {
case 0:
DrawWireFramedBatch(&sphereBatch);
break;
case 1:
DrawWireFramedBatch(&torusBatch);
break;
case 2:
DrawWireFramedBatch(&cylinderBatch);
break;
case 3:
DrawWireFramedBatch(&coneBatch);
break;
case 4:
DrawWireFramedBatch(&diskBatch);
break;
}
//6. pop
modelViewMatrix.PopMatrix();
//7.
glutSwapBuffers();
}
//上下左右,移动图形
static void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP)
//移动世界坐标系,而不是去移动物体。
//将世界坐标系在X方向移动-5.0
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_DOWN)
objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_LEFT)
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
if(key == GLUT_KEY_RIGHT)
objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
glutPostRedisplay();
}
//点击空格,切换渲染图形
static void KeyPressFunc(unsigned char key, int x, int y)
{
if(key == 32)
{
nStep++;
if(nStep > 4)
nStep = 0;
}
switch(nStep)
{
case 0:
glutSetWindowTitle("Sphere");
break;
case 1:
glutSetWindowTitle("Torus");
break;
case 2:
glutSetWindowTitle("Cylinder");
break;
case 3:
glutSetWindowTitle("Cone");
break;
case 4:
glutSetWindowTitle("Disk");
break;
}
glutPostRedisplay();
}
static 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);
//projectionMatrix 矩阵堆栈 加载透视投影矩阵
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//3.modelViewMatrix 矩阵堆栈 加载单元矩阵
modelViewMatrix.LoadIdentity();
//4.通过GLGeometryTransform管理矩阵堆栈
//使用transformPipeline 管道管理模型视图矩阵堆栈 和 投影矩阵堆栈
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
int forth_two(int argc, char* argv[])
{
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
glutInitWindowSize(800, 600);
glutCreateWindow("Sphere");
glutReshapeFunc(ChangeSize);
glutKeyboardFunc(KeyPressFunc);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
return 0;
}
2、关于代码中进出栈的解释
(1)压栈 PushMatrix();
modelViewMatrix.PushMatrix();
这句代码的意思是压栈,如果 PushMatix() 括号里是空的,就代表是把栈顶的矩阵复制一份,再压栈到它的顶部。如果不是空的,比如是括号里是单元矩阵,那么就代表压入一个单元矩阵到栈顶了。
(2)矩阵相乘 MultMatrix(mObjectFrame)
将modelViewMatrix 的堆栈中的矩阵 与 mOjbectFrame 矩阵相乘,存储到modelViewMatrix矩阵堆栈中modelViewMatrix.MultMatrix(mObjectFrame); 这句代码的意思是把 模型视图矩阵堆栈 的 栈顶 的矩阵copy出一份来和新矩阵进行矩阵相乘,然后再将相乘的结果赋值给栈顶的矩阵。
(3)出栈PopMatrix();
modelViewMatrix.PopMatrix();
把栈顶的矩阵出栈,恢复为原始的矩阵堆栈,这样就不会影响后续的操作了。 下面是我自己总结的一个《矩阵入栈、相乘、出栈》的流程图:
二、在看一个球体自动旋转的案例
1、代码如下
#include "GLTools.h"
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLGeometryTransform.h"
#include "GLBatch.h"
#include "StopWatch.h"
#include <math.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
static GLFrustum viewFrustum;
static GLShaderManager shaderManager;
static GLTriangleBatch torusBatch;
static GLGeometryTransform transformPipeline;
// 设置视口和投影矩阵
static void ChangeSize(int w, int h)
{
//防止除以零
if(h == 0)
h = 1;
//将视口设置为窗口尺寸
glViewport(0, 0, w, h);
//设置透视投影
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 1000.0f);
}
//召唤场景
static void RenderScene(void)
{
//清除屏幕、深度缓存区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//1.建立基于时间变化的动画
static CStopWatch rotTimer;
//当前时间 * 60s
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
//2.矩阵变量
/*
mTranslate: 平移
mRotate: 旋转
mModelview: 模型视图
mModelViewProjection: 模型视图投影MVP
*/
M3DMatrix44f mTranslate, mRotate, mModelview, mModelViewProjection;
//创建一个4*4矩阵变量,将花托沿着Z轴负方向移动2.5个单位长度
m3dTranslationMatrix44(mTranslate, 0.0f, 0.0f, -2.5f);
//创建一个4*4矩阵变量,将花托在Y轴上渲染yRot度,yRot根据经过时间设置动画帧率
m3dRotationMatrix44(mRotate, m3dDegToRad(yRot), 0.0f, 1.0f, 0.0f);
//为mModerView 通过矩阵旋转矩阵、移动矩阵相乘,将结果添加到mModerView上
m3dMatrixMultiply44(mModelview, mTranslate, mRotate);
// 将投影矩阵乘以模型视图矩阵,将变化结果通过矩阵乘法应用到mModelViewProjection矩阵上
//注意顺序: 投影 * 模型 != 模型 * 投影
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();
}
static void SetupRC()
{
//1.
glClearColor(0.8f, 0.8f, 0.8f, 1.0f );
shaderManager.InitializeStockShaders();
glEnable(GL_DEPTH_TEST);
//2.形成一个球
gltMakeSphere(torusBatch, 0.4f, 10, 20);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
int forth_three(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;
}
2、对变换的总结
行者常至,为者常成!