Quack — 一款用 C++ 编写的轻量级 3D 游戏引擎,致敬 id Software 的传奇引擎 Quake
项目地址: https://github.com/Oh-noodles/Quack
项目简介 #
Quack 是一款基于 OpenGL 3.3 Core Profile 的 C++ 游戏引擎,其命名和精神内核致敬了 id Software 由约翰·卡马克(John Carmack)主导开发的革命性 3D 引擎 Quake。如同 Quake 在 1996 年将 3D 游戏带入全新时代,Quack 旨在以精简而现代的方式,探索实时渲染、物理碰撞、骨骼动画等游戏引擎核心技术。
本引擎并非对 Quake 的复刻,而是受其启发的独立实现,展现了现代 OpenGL 图形管线与经典游戏引擎架构的融合。
技术栈 #
| 类别 | 技术 | 说明 |
|---|---|---|
| 编程语言 | C++14 | 核心开发语言,使用 g++(Linux)或 clang++(macOS)编译 |
| 图形 API | OpenGL 3.3 Core Profile | 跨平台 3D 图形渲染接口 |
| 窗口与输入 | GLFW 3.3 | 跨平台窗口管理、键盘、鼠标输入处理 |
| OpenGL 加载 | GLAD | 轻量级 OpenGL 函数加载器 |
| 数学库 | GLM 0.9.9 | OpenGL Mathematics,提供向量、矩阵、四元数等数学运算 |
| 模型加载 | Assimp 5.x | 支持多种 3D 模型格式(.obj, .gltf, .glb, .dae 等) |
| 图片加载 | STB Image | 轻量级单头文件图片库,加载纹理资源 |
| 构建系统 | GNU Make | 跨平台 Makefile,支持 Linux 和 macOS |
核心特性 #
渲染系统 #
- Blinn-Phong 着色器:实现了环境光(Ambient)、漫反射(Diffuse)和镜面反射(Specular)三阶段光照计算,支持法线贴图和纹理映射
- 多光源支持:单个着色器可同时处理 4 个点光源、1 个方向光和 1 个聚光灯
- 纹理系统:支持漫反射贴图、镜面反射贴图、法线贴图、粗糙度贴图等 PBR 材质属性
- 着色器管理:自研
Shader类,支持从文件加载顶点/片段着色器,提供类型安全的 uniform 接口
场景管理 #
- 多场景架构:
Engine单例管理多个Scene实例,支持场景切换 - 游戏对象树:每个场景维护
GameObject映射表,游戏对象通过唯一 ID 管理 - 生命周期回调:每个对象可注册
renderFrameCallback,在每一帧执行更新逻辑 - 动态对象创建/销毁:支持运行时添加和标记销毁游戏对象
光照系统 #
Light (基类)
├── DirectionalLight — 方向光,模拟太阳光等无限远光源
├── PointLight — 点光源,带距离衰减(constant / linear / quadratic)
└── SpotLight — 聚光灯,带内外锥角 cutoff / outerCutOff光照计算采用 Blinn-Phong 模型,结合 attenuation 实现物理正确的距离衰减。
碰撞检测 #
引擎实现了自定义 包围盒层次树(Bounding Volume Hierarchy, BVH) 碰撞检测系统:
- Node:表示一个 AABB(轴对齐包围盒)节点,支持叶节点和内部节点
- DTree:基于红黑树思想的 BVH 数据结构,支持高效的空间查询
- 动态维护:支持叶节点的插入、删除和位置更新,父节点自动重新适配(refit)
- 碰撞查询:
getCollidedNodes()方法返回所有与给定碰撞体相交的节点 - 可视化调试:支持将 BVH 树结构导出为 SVG 图像(front / top / left 三视图)
游戏对象与实体 #
GameObject (基类)
├── Tank::Tank — 坦克基类
│ ├── Player — 玩家控制的坦克(绑定相机)
│ └── Enemy — AI 敌方坦克(自动行驶)
└── Cannon::Cannon — 发射的炮弹(带重力抛物线轨迹)- 变换系统:每个游戏对象拥有
position、rotation、scaling三元组,支持独立的位移、旋转、缩放 - 碰撞体组件:
Collider以偏移量方式挂载到GameObject,动态计算 AABB - 可扩展设计:通过继承
GameObject和覆写renderFrameCallback创建自定义实体
骨骼动画 #
引擎支持完整的骨骼动画系统:
- Assimp 集成:从模型文件中提取骨骼层级、动画通道(位置/旋转/缩放关键帧)
- 骨骼矩阵:每帧递归计算骨骼的全局变换矩阵,传递到着色器
- 顶点蒙皮:每个顶点支持最多 4 骨骼加权(
MX_NR_BONE_INFLUENCE) - 支持格式:
.gltf/.glb/.dae(带动画的模型)
相机系统 #
- Euler 角控制:支持 Yaw / Pitch 旋转,Pitch 限幅防止翻转
- 多种移动方式:键盘 WASD + Space(上)+ Shift(下)六自由度移动
- 鼠标视角:鼠标控制相机朝向,支持 FOV 缩放
- 坐标系:右手坐标系,
front/right/up向量自动更新
架构设计 #
┌─────────────────────────────────────────────────┐
│ Engine (单例) │
│ ┌───────────┐ ┌───────────────┐ ┌─────────┐ │
│ │ Window │ │ Shader 管理 │ │ 场景树 │ │
│ └───────────┘ └───────────────┘ └────┬────┘ │
└─────────────────────────────────────────┼───────┘
│
┌─────────────────────▼─────────────────────┐
│ Scene │
│ ┌─────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Camera │ │ Lights │ │ GameObjects│ │
│ └─────────┘ └──────────┘ └─────┬────┘ │
└──────────────────────────────────┼─────────┘
│
┌────────────────────────▼──────────────┐
│ GameObject │
│ ┌──────────┐ ┌────────────────────┐ │
│ │ Model │ │ Collider (BVH) │ │
│ └──────────┘ └────────────────────┘ │
└─────────────────────────────────────────┘核心数据流:
- 每一帧,
Engine::run()驱动主循环 - 计算
deltaTime并调用全局输入处理 - 清空帧缓冲,调用
renderObjects()遍历活动场景 - 对每个
GameObject调用renderFrameCallback(deltaTime)更新逻辑 - 按模型矩阵渲染,应用光照 uniform
- 触发全局
renderFrameCallback(用于碰撞检测等游戏逻辑) - 交换缓冲区、轮询事件
目录结构 #
Quack/
├── include/ # 头文件
│ ├── engine/ # 引擎核心
│ │ ├── engine.hpp # 引擎单例,主循环驱动
│ │ ├── scene.hpp # 场景管理,光照定义
│ │ ├── gameObject.hpp # 游戏对象基类
│ │ ├── bvh.hpp # BVH 碰撞检测数据结构
│ │ ├── collider.hpp # 碰撞体组件
│ │ └── utils.hpp # 工具函数(UUID 生成)
│ ├── game/ # 游戏层
│ │ ├── player.hpp # 玩家坦克
│ │ ├── enemy.hpp # AI 敌方坦克
│ │ ├── tank.hpp # 坦克基类
│ │ └── cannon.hpp # 炮弹
│ ├── learn/ # LearnOpenGL 教程框架
│ │ ├── camera.hpp # 相机类
│ │ ├── model.hpp # 模型加载器
│ │ ├── mesh.hpp # 网格封装
│ │ ├── shader_s.hpp # 着色器管理
│ │ ├── shader.hpp # 基础着色器
│ │ ├── animation.hpp # 动画系统
│ │ ├── animator.hpp # 动画控制器
│ │ ├── bone.hpp # 骨骼数据
│ │ ├── animdata.hpp # 动画数据结构
│ │ ├── MyAnimation.hpp # 自定义动画实现
│ │ ├── model_animation.hpp # 模型动画
│ │ ├── assimp_glm_helpers.hpp # Assimp-GLM 转换工具
│ │ └── stb_image.h # STB 图片加载
│ ├── glm/ # GLM 数学库
│ ├── assimp/ # Assimp 导入器
│ ├── GLFW/ # GLFW 声明
│ ├── KHR/ # OpenGL 平台头
│ └── glad/ # GLAD 加载器
├── src/ # 源文件
│ ├── engine/ # 引擎实现
│ │ ├── engine.cpp
│ │ ├── scene.cpp
│ │ ├── gameObject.cpp
│ │ ├── bvh.cpp
│ │ ├── collider.cpp
│ │ └── utils.cpp
│ ├── game/ # 游戏实现
│ │ ├── main.cpp # 游戏入口
│ │ ├── player.cpp
│ │ ├── enemy.cpp
│ │ ├── tank.cpp
│ │ └── cannon.cpp
│ ├── phong_shading/ # Phong 着色演示
│ ├── model_loading/ # 模型加载演示
│ ├── skeletal_animation/ # 骨骼动画演示
│ ├── shader_showcase/ # 自定义着色器演示
│ ├── test/ # BVH 测试
│ ├── stb/ # STB 实现
│ └── glad/ # GLAD 实现
├── resources/ # 资源资产
│ ├── objects/ # 3D 模型
│ │ ├── backpack/ # 背包模型 (.obj)
│ │ ├── simple_tank/ # 坦克模型 (.gltf)
│ │ ├── concept_tank/ # 概念坦克 (.glb)
│ │ ├── pixel_tank/ # 像素风坦克 (.glb)
│ │ ├── tank/ # 基础坦克 (.glb)
│ │ ├── cannon/ # 炮弹模型 (.glb)
│ │ ├── knight/ # 骑士模型 (.obj)
│ │ ├── guardian/ # 守护者模型 (.obj)
│ │ ├── Horse/ # 马模型 (.glb)
│ │ ├── LittlestTokyo/ # 东京夜景 (.glb)
│ │ ├── Television/ # 电视机 (.gltf)
│ │ ├── shapes/ # 基础几何体 (.glb)
│ │ ├── anim_zombie/ # 动画僵尸 (.gltf)
│ │ ├── anim_male/ # 动画男性 (.gltf)
│ │ ├── anim_fish/ # 动画鱼 (.gltf)
│ │ ├── anim_car/ # 动画汽车 (.gltf)
│ │ ├── vampire/ # 吸血鬼动画 (.dae)
│ │ └── blender_cube/ # Blender 立方体 (.gltf)
│ └── textures/ # 纹理贴图
├── Makefile # 构建配置
├── compile_flags.txt # 编译标志
└── README.md # 本文件演示项目 #
phong_shading #
基础着色演示,展示 Phong / Blinn-Phong 光照模型:
- 10 个旋转立方体环绕点光源
- 环境光、漫反射、镜面反射三段着色
- 镜面高光指数 32
model_loading #
模型加载演示,完整展示 Assimp 模型加载管线:
- 加载
backpack.obj等复杂模型 - 多材质、多纹理支持
- 方向光 + 4 点光源 + 聚光灯复合光照
skeletal_animation #
骨骼动画演示:
- 加载带动画的
.gltf模型(僵尸、鱼、男性) - 实时播放模型内置动画
- 骨骼矩阵传递到片段着色器进行顶点蒙皮
shader_showcase #
自定义着色器效果展示:
- 实时水位/水波动画效果
- 基于时间的动态着色
- 支持高分辨率渲染(1920×1080)
test #
BVH 碰撞检测系统单元测试:
- 创建 6 个顺序排列的 AABB 节点
- 测试插入、碰撞查询、删除、更新操作
- 导出三视图 SVG 可视化文件
game #
主游戏项目 — 坦克对战:
- 玩家控制一辆坦克(WASD 移动,鼠标转向,Space 开炮)
- AI 敌方坦克自动行驶与碰撞避让
- 炮弹带重力抛物线轨迹
- 实时碰撞检测与坦克朝向判定
- 方向光 + 4 点光源 + 聚光灯完整光照
构建与运行 #
依赖项 #
Linux:
sudo apt-get install libglfw3-dev libassimp-devmacOS:
brew install glfw assimp系统还需安装 OpenGL 3.3 兼容的显卡驱动。
编译 #
项目使用 GNU Make 构建系统,Makefile 支持 Linux(g++)和 macOS(clang++):
# 编译主游戏
make
# 编译后会生成 ./main 可执行文件编译参数说明:
- 编译器:
g++(Linux)/clang++(macOS) - 标准:
-std=c++14(Linux)/-std=c++11(macOS,建议升级至 C++14 以获得最佳兼容性) - 警告:
-Wall - 调试符号:
-g - 包含路径:
-I include - 链接库:
-l glfw -l assimp -lm
编译其他演示项目:
目前 Makefile 默认仅编译 src/game/main.cpp。如需编译其他演示,可修改 ENTRY 变量或手动编译:
# 编译 Phong 着色演示
g++ -Wall -std=c++14 -g -I include src/glad/glad.c src/stb/stb_image.cpp src/phong_shading/main.cpp -l glfw -l assimp -o phong_demo
# 编译模型加载演示
g++ -Wall -std=c++14 -g -I include src/glad/glad.c src/stb/stb_image.cpp src/model_loading/main.cpp -l glfw -l assimp -o model_demo
# 编译骨骼动画演示
g++ -Wall -std=c++14 -g -I include src/glad/glad.c src/stb/stb_image.cpp src/skeletal_animation/main.cpp -l glfw -l assimp -o anim_demo
# 编译着色器演示
g++ -Wall -std=c++14 -g -I include src/glad/glad.c src/stb/stb_image.cpp src/shader_showcase/main.cpp -l glfw -l assimp -o shader_demo
# 编译 BVH 测试
g++ -Wall -std=c++14 -g -I include src/engine/bvh.cpp src/test/main.cpp -o bvh_test运行 #
# 运行主游戏
./main
# 运行其他演示
./phong_demo
./model_demo
./anim_demo
./shader_demo
./bvh_test注意:部分演示程序的工作目录需要定位到项目根目录下,以便正确加载资源文件。
输入控制 #
| 操作 | 按键 |
|---|---|
| 前进 | W |
| 后退 | S |
| 左移 | A |
| 右移 | D |
| 向上 | Space |
| 向下 | Left Shift |
| 开火 | Space(主游戏) |
| 鼠标视角 | 鼠标移动 |
| 缩放 | 鼠标滚轮 |
| 退出 | ESC |
资源资产 #
项目 resources/objects/ 目录中包含多种格式的 3D 模型资产:
| 资产 | 格式 | 说明 |
|---|---|---|
| Backpack | .obj |
带漫反射/法线/高光贴图的背包 |
| Simple Tank | .gltf |
PBR 材质坦克模型 |
| Concept Tank | .glb |
概念设计坦克 |
| Pixel Tank | .glb |
像素风格坦克 |
| Knight | .obj |
中世纪骑士模型 |
| Guardian | .obj |
守护者模型 |
| Horse | .glb |
低多边形马 |
| LittlestTokyo | .glb |
东京夜景场景 |
| Zombie / Male / Fish / Car | .gltf |
带动画的骨骼模型 |
| Vampire | .dae |
带动画的吸血鬼 |
| Cannon | .glb |
炮弹模型 |
部分资源采用 CC BY 等许可证发布,详细授权请参见各子目录下的 license.txt 文件。
设计哲学 #
- 精简而不简陋:Quack 采用扁平化的 C++ 架构,没有过度设计的抽象层,每个组件职责清晰
- 教学友好:代码结构参考了 LearnOpenGL 教程的组织方式,适合学习 3D 图形编程
- 可扩展:通过
GameObject继承体系和回调机制,可方便地添加新的游戏实体 - 跨平台:在 Linux 和 macOS 上均可编译运行
- 致敬经典:引擎命名 “Quack” 致敬 id Software 的 Quake 引擎,表达对传奇游戏引擎开发者约翰·卡马克的敬意
致谢 #
- id Software / John Carmack — Quake 引擎的开创者,无数游戏开发者的灵感源泉
- LearnOpenGL — 本项目的图形编程教学基础框架源自 LearnOpenGL 教程
- Assimp — 强大的跨平台 3D 模型导入库
- GLFW — 简洁易用的跨平台窗口与输入库
- GLM — 符合 OpenGL 标准的数学库
- STB — 高效的单头文件图片加载库