跳过正文
  1. 计算机图形学/

3D 游戏引擎 Quack

·1177 字·6 分钟
目录

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     — 发射的炮弹(带重力抛物线轨迹)
  • 变换系统:每个游戏对象拥有 positionrotationscaling 三元组,支持独立的位移、旋转、缩放
  • 碰撞体组件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)    │  │
                              │  └──────────┘  └────────────────────┘  │
                              └─────────────────────────────────────────┘

核心数据流

  1. 每一帧,Engine::run() 驱动主循环
  2. 计算 deltaTime 并调用全局输入处理
  3. 清空帧缓冲,调用 renderObjects() 遍历活动场景
  4. 对每个 GameObject 调用 renderFrameCallback(deltaTime) 更新逻辑
  5. 按模型矩阵渲染,应用光照 uniform
  6. 触发全局 renderFrameCallback(用于碰撞检测等游戏逻辑)
  7. 交换缓冲区、轮询事件

目录结构
#

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-dev

macOS:

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 文件。

设计哲学
#

  1. 精简而不简陋:Quack 采用扁平化的 C++ 架构,没有过度设计的抽象层,每个组件职责清晰
  2. 教学友好:代码结构参考了 LearnOpenGL 教程的组织方式,适合学习 3D 图形编程
  3. 可扩展:通过 GameObject 继承体系和回调机制,可方便地添加新的游戏实体
  4. 跨平台:在 Linux 和 macOS 上均可编译运行
  5. 致敬经典:引擎命名 “Quack” 致敬 id Software 的 Quake 引擎,表达对传奇游戏引擎开发者约翰·卡马克的敬意

致谢
#

  • id Software / John Carmack — Quake 引擎的开创者,无数游戏开发者的灵感源泉
  • LearnOpenGL — 本项目的图形编程教学基础框架源自 LearnOpenGL 教程
  • Assimp — 强大的跨平台 3D 模型导入库
  • GLFW — 简洁易用的跨平台窗口与输入库
  • GLM — 符合 OpenGL 标准的数学库
  • STB — 高效的单头文件图片加载库