中级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 三大核心变换变量
位置变量:m_position
记录物体在世界坐标系中的坐标,用glm::vec3存储,初始化为(0.0f, 0.0f, 0.0f),代表物体默认位于世界原点。旋转变量:m_angle_x /m_angle_y/m_angle_z
采用欧拉角旋转方案,分别记录物体绕 X、Y、Z 本地轴的旋转角度,这是游戏 / 引擎中最通用的旋转方式:Pitch(绕 X 轴):上下俯仰(类比「点头」)
Yaw(绕 Y 轴):左右转向(类比「摇头」)
Roll(绕 Z 轴):滚筒旋转(类比「滚床单」)
缩放变量: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 核心价值
统一抽象:将所有 3D 物体的变换逻辑收敛到一个基类,减少重复代码
标准输出:输出合规模型矩阵,无缝对接渲染管线
易于继承:子类可快速扩展(如 Model 类添加网格数据、Light 类添加光照参数)
6.2 扩展方向
新增
resetTransform()重置所有变换添加欧拉角转四元数,解决万向锁问题
支持父物体 / 子物体的父子坐标系变换
结语
Object 类是 3D 渲染框架的「最小可用单元」,它没有复杂的逻辑,却承载了物体空间变换的核心能力。从变量权限设计、旋转规则制定,到模型矩阵计算,每一步都遵循工业级引擎标准,代码简洁、可直接落地到 OpenGL/ES 3D 项目中。
掌握 Object 类,你就掌握了 3D 场景中物体运动的底层逻辑,后续再扩展相机、光照、材质等模块,都会变得水到渠成🌊。
