中级OpenGL教程 010:Object 类设计与模型矩阵完全实现

中级OpenGL教程 010:Object 类设计与模型矩阵完全实现

  • Bilibili 同步视频
  • 一、Object 类:3D 物体的「空间管理者」
    • 1.1 文件结构与基础配置
  • 二、成员变量设计:protected 权限的精妙选择
    • 2.1 三大核心变换变量
  • 三、旋转逻辑:本地坐标系 + Unity 标准顺序
    • 3.1 本地坐标系旋转
    • 3.2 旋转顺序:严格遵循 Unity 引擎标准
  • 四、接口封装:简洁易用的变换 API
    • 4.1 核心接口声明
    • 4.2 接口实现(object.cpp)
  • 五、模型矩阵计算:渲染管线的「核心输出」
    • 5.1 矩阵计算完整实现
    • 5.2 关键细节勘误(必看)
  • 六、Object 类的价值与扩展方向
    • 6.1 核心价值
    • 6.2 扩展方向
  • 结语

Bilibili 同步视频

中级OpenGL教程 010:Object 类设计与模型矩阵完全实现

在 3D 图形渲染的世界里,一切可见元素都可被抽象为「物体」—— 无论是场景中的模型、角色、道具,还是粒子特效,都需要一套统一的框架来管理其空间变换。Object 类正是这套框架的核心,它封装了物体的位移、旋转、缩放三大基础变换,最终输出渲染管线必需的模型矩阵(Model Matrix),为后续 MVP 矩阵计算与片元着色打下坚实基础。

本文将从零拆解 Object 类的设计思路、成员变量定义、接口封装与模型矩阵计算逻辑,搭配可直接落地的 C++ 代码实现,帮你快速搭建 3D 引擎的物体管理模块✨。


一、Object 类:3D 物体的「空间管理者」

Object 类的核心使命只有一个:管理物体的空间变换,输出标准模型矩阵
它是所有可渲染物体的基类,场景中的模型、相机、灯光等实体,都可继承此类复用变换逻辑。因此在设计时,权限控制、变量封装、接口易用性是三大关键原则。

1.1 文件结构与基础配置

我们将 Object 类置于GL framework核心模块,方便全工程复用,创建两个核心文件:

  • object.h:类定义、变量声明、接口声明

  • object.cpp:函数实现、矩阵计算逻辑

头文件必须添加#pragma once预编译指令,杜绝头文件二次编译,避免重复定义报错;同时引入核心依赖库(GLM 数学库、框架核心文件),为矩阵运算、向量操作提供支撑。

// object.h 基础配置#pragmaonce#include"core.h"// 包含 GLM 数学库、框架核心工具

二、成员变量设计:protected 权限的精妙选择

物体的空间变换,本质是位置、旋转、缩放三组数据的组合。
在权限设计上,我们放弃private,选择protected修饰所有成员变量 —— 这是为了让子类能直接访问父类变换数据,保证继承体系的灵活性(比如模型类、角色类可直接修改位置、旋转角度)。

2.1 三大核心变换变量

  1. 位置变量:m_position
    记录物体在世界坐标系中的坐标,用glm::vec3存储,初始化为(0.0f, 0.0f, 0.0f),代表物体默认位于世界原点。

  2. 旋转变量:m_angle_x /m_angle_y/m_angle_z
    采用欧拉角旋转方案,分别记录物体绕 X、Y、Z 本地轴的旋转角度,这是游戏 / 引擎中最通用的旋转方式:

    • Pitch(绕 X 轴):上下俯仰(类比「点头」)

    • Yaw(绕 Y 轴):左右转向(类比「摇头」)

    • Roll(绕 Z 轴):滚筒旋转(类比「滚床单」)

  3. 缩放变量:m_scale
    glm::vec3存储物体在 X、Y、Z 轴的缩放比例,默认值必须为 1.0f(代表无缩放),若设为 0 会导致物体完全消失。

// object.h 成员变量定义classObject{protected:// 1. 位置:世界坐标系坐标glm::vec3 m_position{0.0f,0.0f,0.0f};// 2. 旋转:欧拉角(绕本地X/Y/Z轴)floatm_angle_x{0.0f};floatm_angle_y{0.0f};floatm_angle_z{0.0f};// 3. 缩放:三轴缩放比例glm::vec3 m_scale{1.0f,1.0f,1.0f};public:// 后续接口声明...};

三、旋转逻辑:本地坐标系 + Unity 标准顺序

旋转是 3D 变换中最易出错的环节,核心要解决两个问题:基于什么坐标系旋转?旋转的先后顺序是什么?

3.1 本地坐标系旋转

我们采用本地坐标系(局部坐标系)旋转规则:
物体的旋转始终围绕自身当前的坐标轴,而非世界坐标系坐标轴。
比如物体先绕 X 轴旋转 30°,再绕 Z 轴旋转时,Z 轴是物体旋转后的本地 Z 轴,而非世界 Z 轴 —— 这符合现实中物体的运动直觉(如飞机、汽车的运动)。

3.2 旋转顺序:严格遵循 Unity 引擎标准

为保证旋转结果可预期,固定旋转顺序至关重要,我们直接沿用工业级标准(Unity 引擎):
先 Pitch(X 轴)→ 再 Yaw(Y 轴)→ 最后 Roll(Z 轴)
此顺序能最大程度避免万向锁问题,适配绝大多数 3D 场景需求。


四、接口封装:简洁易用的变换 API

Object 类对外提供极简接口,支持设置位置、增量旋转、设置缩放三大操作,区分「赋值式设置」与「增量式修改」,符合引擎开发习惯。

4.1 核心接口声明

// object.h 公共接口public:Object()=default;~Object()=default;// 1. 设置位置:赋值式(直接覆盖世界坐标)voidsetPosition(constglm::vec3&position);// 2. 增量旋转:在原有角度基础上累加voidrotateX(floatangle);voidrotateY(floatangle);voidrotateZ(floatangle);// 3. 设置缩放:赋值式(直接覆盖三轴缩放)voidsetScale(constglm::vec3&scale);// 4. 核心:计算并返回模型矩阵glm::mat4getModelMatrix();

4.2 接口实现(object.cpp)

旋转接口采用增量累加逻辑,每调用一次rotateX,就在原有角度上叠加新角度,适配帧更新的旋转逻辑;位置与缩放为直接赋值,保证精准控制。

// object.cpp 接口实现#include"object.h"// 设置位置voidObject::setPosition(constglm::vec3&position){m_position=position;}// 增量旋转 X 轴voidObject::rotateX(floatangle){m_angle_x+=angle;}// 增量旋转 Y 轴voidObject::rotateY(floatangle){m_angle_y+=angle;}// 增量旋转 Z 轴voidObject::rotateZ(floatangle){m_angle_z+=angle;}// 设置缩放voidObject::setScale(constglm::vec3&scale){m_scale=scale;}

五、模型矩阵计算:渲染管线的「核心输出」

getModelMatrix()是 Object 类的灵魂函数,它将位移、旋转、缩放三组离散数据,组合为渲染管线必需的模型矩阵,变换顺序严格遵循 Unity 标准:
先缩放 → 再旋转 → 最后平移
⚠️ 关键规则:平移必须在最后,且基于世界坐标系执行,保证物体最终定位到指定世界坐标。

5.1 矩阵计算完整实现

// object.cpp 模型矩阵计算glm::mat4Object::getModelMatrix(){// 1. 初始化单位矩阵glm::mat4 transform=glm::mat4(1.0f);// 2. 第一步:缩放变换(本地坐标系)transform=glm::scale(transform,m_scale);// 3. 第二步:旋转变换(本地坐标系,严格按 X→Y→Z 顺序)// 旋转需转弧度:GLM 仅支持弧度运算,角度必须转换!transform=glm::rotate(transform,glm::radians(m_angle_x),glm::vec3(1.0f,0.0f,0.0f));transform=glm::rotate(transform,glm::radians(m_angle_y),glm::vec3(0.0f,1.0f,0.0f));transform=glm::rotate(transform,glm::radians(m_angle_z),glm::vec3(0.0f,0.0f,1.0f));// 4. 第三步:平移变换(世界坐标系,单独计算后相乘)transform=glm::translate(glm::mat4(1.0f),m_position)*transform;// 返回最终模型矩阵returntransform;}

5.2 关键细节勘误(必看)

GLM 数学库的旋转函数仅支持弧度值,但我们设计的m_angle_x/y/z是角度值,必须用glm::radians()转换,否则旋转结果完全错误,这是 3D 开发高频踩坑点❗


六、Object 类的价值与扩展方向

6.1 核心价值

  1. 统一抽象:将所有 3D 物体的变换逻辑收敛到一个基类,减少重复代码

  2. 标准输出:输出合规模型矩阵,无缝对接渲染管线

  3. 易于继承:子类可快速扩展(如 Model 类添加网格数据、Light 类添加光照参数)

6.2 扩展方向

  • 新增resetTransform()重置所有变换

  • 添加欧拉角转四元数,解决万向锁问题

  • 支持父物体 / 子物体的父子坐标系变换


结语

Object 类是 3D 渲染框架的「最小可用单元」,它没有复杂的逻辑,却承载了物体空间变换的核心能力。从变量权限设计、旋转规则制定,到模型矩阵计算,每一步都遵循工业级引擎标准,代码简洁、可直接落地到 OpenGL/ES 3D 项目中。

掌握 Object 类,你就掌握了 3D 场景中物体运动的底层逻辑,后续再扩展相机、光照、材质等模块,都会变得水到渠成🌊。