OpenGL 学习 07 - 向量/矩阵变换/投影

学习书籍: OpenGL 超级宝典(中文第五版) 密码:fu4w

书籍源码:OpenGL 超级宝典第五版源代码 密码:oyb4

环境搭建:OpenGL 学习 01 - Mac 搭建 OpenGL 环境

PS:因为以后 Demo 源码代码量会越来越多,不太可能全部复制在这里了,我就解释下 Demo 的核心源码,全部源码请前往我的 github/OpenGLDemo 下载,源码里会有详细注释。

基本概念

一、向量

一个顶点是 XYZ 空间坐标系的一个位置(x,y,z),这一个坐标也能表示一个向量,是从坐标原点指向这个位置点的一个向量(带箭头的线段)。

在 OpenGL 里,对应数据类型为 M3DVector3f(3维浮点向量)M3DVector4f(4维浮点向量)

向量之间计算的几何意义看下图:

二、矩阵

矩阵是一种功能非常强大的数学工具,大大简化了求解变量之间有复杂关系的方程或方程组的过程。

在 OpenGL 里,主要运用于坐标变换,对应数据类型有M3DMatrix33f(3x3浮点矩阵)M3DMatrix44f(4x4浮点矩阵)等。

注意:矩阵的乘法是不满足交换律的,即 AB != BA

三、变换

1. 视图变换

视图变换是应用到场景的第一种变换,用于确定场景中的有利位置,即观察者的位置,就像在场景中放置照相机并让它指向某个方向。

2. 模型变换

物体本身的坐标变换,有旋转、平移、缩放3种基础模型变换。

3. 模型视图变换

实际上就是视图变换和模型变换的结合,因为视图变换和模型变换实质上是一样的,只是为了程序员方便而区分出来,因为你移动物体向前10m,和观察者向后移动10m,最终效果是一样的。

4. 投影变换

投影变换有正投影和透视投影:

  • 正投影(平行投影):无论物体有多远,都会按照同样大小进行绘制。
  • 透视投影:远处的物体看起来会比近处同样大小的物体更小一些。
5. 视口变换

将逻辑窗口坐标映射到物理窗口坐标的变换,称为视口变换,通常我们不需要为这事操心,图形硬件已经为我们做好了这些。

源码解析

一、矩阵变换

// 加载单位矩阵
void m3dLoadIdentity44(M3DMatrix44f m);
// 沿着 x/y/z 轴平移
void m3dTranslationMatrix44(M3DMatrix44f m, float x, float y, float z);
// 沿着 x/y/z 轴旋转 angle(弧度)
void m3dRotationMatrix44(M3DMatrix44f m, float angle, float x, float y, float z);
// 在 x/y/z 轴上进行缩放
void m3dScaleMatrix44(M3DMatrix44f m, float xScale, float yScale, float zScale);
// 矩阵 a 和矩阵 b 相乘得到矩阵 product
void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const  M3DMatrix44f b);

08-MoveByMatrix 核心源码如下,全部源码下载:08-MoveByMatrix

// 创建3个4x4矩阵,分别是合成矩阵、平移矩阵、旋转矩阵
M3DMatrix44f mFinalTransform, mTranslationMatrix, mRotationMatrix;
// 平移(xPos, yPos, 0)的矩阵表示
m3dTranslationMatrix44(mTranslationMatrix, xPos, yPos, 0.0f);
// 绕z轴旋转的矩阵,每次旋转角度加 5 度,m3dDegToRad = 角度 -> 弧度
static float zRot = 0.0f;
zRot += 5.0f;
m3dRotationMatrix44(mRotationMatrix, m3dDegToRad(zRot), 0.0f, 0.0f, 1.0f);
// 矩阵相乘,参数顺序很重要,先平移,后旋转
m3dMatrixMultiply44(mFinalTransform, mTranslationMatrix, mRotationMatrix);

二、投影矩阵

设置投影矩阵是通过视景体(GLFrustum)来设置的

[GLFrustum] { // 仅仅表示以下方法是 GLFrustum 的方法
    // 设置正投影矩阵参数,(x, y, z)最小和最大值
    void SetOrthographic(GLfloat xMin, GLfloat xMax, 
                         GLfloat yMin, GLfloat yMax, 
                         GLfloat zMin, GLfloat zMax);
    // 设置透视投影矩阵参数,分别为:透视角,宽高比,近距,远距
    void SetPerspective(float fFov, float fAspect, float fNear, float fFar);
    // 获取投影矩阵
    const M3DMatrix44f& GetProjectionMatrix(void);
}

为了方便管理各个矩阵,GLTools 提供了矩阵堆栈 GLMatrixStack,默认堆栈最大深度为 64,有压栈、出栈等操作。

[GLMatrixStack] { // 仅仅表示以下方法是 GLMatrixStack 的方法
    // -------- 矩阵加载 -----------
    // 在栈顶载入单元矩阵,载入即覆盖
    void LoadIdentity(void);
    // 在栈顶载入任何矩阵
    void LoadMatrix(const M3DMatrix44f m);
    // 用栈顶矩阵乘以某个矩阵,得到的结果矩阵覆盖原来的栈顶矩阵
    void MultMatrix(const M3DMatrix44f mMatrix);
    // 获取栈顶矩阵
    const M3DMatrix44f& GetMatrix(void);
    // -------- 出栈压栈 -----------
    // 压栈,在栈顶压入一个矩阵
    void PushMatrix(const M3DMatrix44f mMatrix);
    // 出栈,把栈顶矩阵移除矩阵堆栈
    void PopMatrix(void);
    // -------- 仿射变换 -----------
    // 栈顶矩阵进行缩放变换
    void Scale(GLfloat x, GLfloat y, GLfloat z);
    // 栈顶矩阵进行平移变换
    void Translate(GLfloat x, GLfloat y, GLfloat z);
    // 栈顶矩阵进行旋转变换
    void Rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
}

而为了方便管理模型视图矩阵堆栈和投影矩阵堆栈,GLTools 为我们提供了管理管线 GLGeometryTransform,帮助我们跟踪记录这两种矩阵堆栈,并快速检索模型视图矩阵堆栈顶部或投影矩阵堆栈顶部。

[GLGeometryTransform] { // 仅仅表示以下方法是 GLGeometryTransform 的方法
    // ----------- 设置矩阵堆栈 ---------
    // 单独设置模型视图矩阵堆栈
    void SetModelViewMatrixStack(GLMatrixStack& mModelView);
    // 单独设置投影矩阵堆栈
    void SetProjectionMatrixStack(GLMatrixStack& mProjection);
    // 同时设置模型视图矩阵堆栈和投影矩阵堆栈
    void SetMatrixStacks(GLMatrixStack& mModelView, GLMatrixStack& mProjection);
    // ----------- 获取堆栈顶部 ---------
    // 获取模型视图矩阵堆栈的栈顶矩阵
    const M3DMatrix44f& GetModelViewMatrix(void);
    // 获取投影矩阵堆栈的栈顶矩阵
    const M3DMatrix44f& GetProjectionMatrix(void);
    // 获取2个矩阵堆栈的栈顶矩阵相乘的结果矩阵
    const M3DMatrix44f& GetModelViewProjectionMatrix(void);
}

10-Orthographic 核心源码如下,全部源码下载:10-Orthographic

// 设置正投影参数,(xMin, xMax, yMin, yMax, zMin, zMax)
viewFrustum.SetOrthographic(-130.0f, 130.0f, -130.0f, 130.0f, -130.0f, 130.0f);
// 获得到的正投影矩阵载入堆栈中
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
// 变换管线,管理2个堆栈
transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);

11-Perspective 核心源码如下,全部源码下载:11-Perspective

// 设置透视投影矩阵参数,分别为:透视角,宽高比,近距,远距
viewFrustum.SetPerspective(35.0f, float(width)/float(height), 1.0f, 1000.0f);
// 载入透视投影矩阵
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
// 利用变换管线,管理2个堆栈
transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);

12-ModelViewProjection 核心源码如下,全部源码下载:12-ModelViewProjection

// 窗口渲染回调
void RenderScene(void) {
    // 定义一个测试运行时间
    static CStopWatch rotTimer;
    // 获取到上一个时间点到当前的时间间隔(单位是每秒刻度数,即  1/60s)
    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轴旋转的矩阵变换
    m3dRotationMatrix44(mRotate, m3dDegToRad(yRot), 0.0f, 1.0f, 0.0f);
    // 矩阵变换的合并矩阵
    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();
}

上面的 Demo 源码全部都放在我的 github/OpenGLDemo 上,大家可以去下载和调试。

有什么问题可以在下方评论区提出,写得不好可以提出你的意见,我会合理采纳的,O(∩_∩)O哈哈~,求关注求赞

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容