一个基于OpenGL 3.3的轻量级3D渲染引擎,专注OBJ模型加载、实例化渲染与风格化着色。采用现代C++17模块化架构,适合3D图形学习与实时渲染演示。
Lumenaris - 源自拉丁语 lumen(光)+ aris(带来...的),意为"带来光明的",象征这个OpenGL学习项目为3D图形学之路带来启蒙与指引。
- 高性能实例化渲染:单次绘制调用渲染1000+物体
- 完整OBJ工作流:支持多材质/纹理/变换,自动解析.mtl材质文件
- 8种风格化着色器:卡通、玻璃、墨水、霓虹、像素噪点、素描
- 天空盒系统:轻量级环境光照(IBL),与Phong光照系统集成
- 智能资源管理:
shared_ptr自动管理MeshBuffer/纹理生命周期,零内存泄漏 - 异步日志系统:后台线程写入,Release编译零开销
🎪✨ 超级宇宙迪斯科舞台 🕺💃
📦 1600立方体 + 🍩 5圆环 + 🎯 39平台 + 🌐 9球体
💡48动态光源 + 🌌 宇宙天空盒
🐇 狂舞兔子 + 🚗 漂移跑车
🎉 Enjoy the chaos! 🎆
📦 1600立方体 + 🍩 5圆环 + 🎯 39平台 + 🌐 9球体
💡48动态光源 + 🌌 宇宙天空盒
🐇 狂舞兔子 + 🚗 漂移跑车
🎉 Enjoy the chaos! 🎆
| 技术 | 版本 | 用途 |
|---|---|---|
| OpenGL | 3.3 Core | 渲染API |
| C++ | 17 | 开发语言 |
| GLFW | 3.4 | 窗口与输入管理 |
| GLM | 3D数学运算 | |
| GLAD | 0.1.36 | OpenGL函数加载 |
| TinyOBJLoader | 1.0.6 | OBJ模型解析 |
| STB Image | 2.30 | 纹理加载 |
- CMake 3.15+
- 支持OpenGL 3.3的显卡
- C++17编译器(GCC 7+, Clang 5+, MSVC 2017+)
git clone <repository-url>
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)#include "Renderer/Factory/MeshDataFactory.hpp"
#include "Renderer/Renderer/InstancedRenderer.hpp"
#include "Renderer/Data/InstanceData.hpp"
// 创建立方体缓冲区(工厂模式,已上传到GPU)
auto cubeBuffer = Renderer::MeshDataFactory::CreateCubeBuffer();
// 准备实例数据(100个立方体)
auto instances = std::make_shared<Renderer::InstanceData>();
for (int x = 0; x < 10; ++x) {
for (int z = 0; z < 10; ++z) {
instances->AddInstance({
glm::vec3(x * 2.0f, 0.0f, z * 2.0f), // 位置
glm::vec3(0.0f), // 旋转
glm::vec3(1.0f), // 缩放
glm::vec3(1.0f, 0.5f, 0.3f) // 颜色
);
}
}
// 创建渲染器并初始化
Renderer::InstancedRenderer renderer;
renderer.SetMesh(cubeBuffer);
renderer.SetInstances(instances);
renderer.Initialize();
// 渲染循环
shader.Use();
renderer.Render(); // 一次绘制调用渲染100个立方体// 加载跑车模型(38个材质)
std::string carPath = "assets/models/cars/sportsCar.obj";
auto carInstances = std::make_shared<Renderer::InstanceData>();
// ... 添加12辆车实例 ...
// 从OBJ创建渲染器(每个材质独立)
auto [carRenderers, carMeshBuffers, carInstanceData] =
Renderer::InstancedRenderer::CreateForOBJ(carPath, carInstances);
// 渲染(38次DrawCall替代传统456次)
for (auto& renderer : carRenderers) {
shader.SetBool("useTexture", renderer.HasTexture());
renderer.Render();
}// 每帧更新实例变换
float time = glfwGetTime();
auto& matrices = instances->GetModelMatrices();
for (size_t i = 0; i < matrices.size(); ++i) {
float angle = time + i * 0.1f;
matrices[i] = glm::translate(glm::mat4(1.0f), positions[i]) *
glm::rotate(glm::mat4(1.0f), angle, glm::vec3(0, 1, 0));
}
// 高效上传到GPU(不重新分配内存)
renderer.UpdateInstanceData();
renderer.Render();Lumenaris/
├── assets/ # 资源文件
│ ├── models/ # 3D模型(跑车、云朵、康奈尔盒)
│ ├── shader/ # 着色器(8种风格 + 天空盒 + 环境光照)
│ ├── textures/ # 纹理资源(含天空盒)
│ └── picture/ # 纹理资源
├── include/ # 头文件
│ ├── Core/ # 核心系统(窗口、摄像机、输入)
│ └── Renderer/ # 渲染系统
│ ├── Core/ # IRenderer接口
│ ├── Data/ # MeshData/InstanceData
│ ├── Environment/ # 环境渲染(天空盒、环境光照)
│ ├── Factory/ # 网格工厂
│ ├── Geometry/ # 几何体实现
│ ├── Lighting/ # 光照系统
│ ├── Renderer/ # 渲染器实现
│ └── Resources/ # 资源加载器
├── src/ # 源代码
│ ├── Core/ # 核心系统实现
│ └── Renderer/ # 渲染系统实现
│ ├── Data/ # 数据容器
│ ├── Environment/ # 环境渲染实现
│ ├── Factory/ # 工厂实现
│ ├── Geometry/ # 几何体实现
│ ├── Lighting/ # 光照系统实现
│ ├── Renderer/ # 渲染器实现
│ └── Resources/ # 资源实现
├── vendor/ # 第三方库
├── docs/ # 文档
│ ├── ARCHITECTURE.md # 架构详解
│ └── INTERFACES.md # 接口文档
├── CMakeLists.txt # 构建配置
└── README.md # 本文件
| 风格 | 文件名 | 效果描述 |
|---|---|---|
| 基础 | basic.frag |
Phong光照 |
| 卡通 | toon.frag |
色阶着色 |
| 玻璃 | glass.frag |
折射+透明 |
| 墨水 | ink.frag |
轮廓描边 |
| 霓虹 | neon.frag |
发光边缘 |
| 像素噪点 | pixelnoise.frag |
像素化 |
| 素描 | sketch.frag |
手绘质感 |
切换方式:
std::vector<Renderer::Shader> shaders;
for (const auto& path : stylePaths) {
Renderer::Shader shader;
shader.Load("basic.vert", path);
shaders.push_back(std::move(shader));
}
// 运行时切换:shaders[styleIndex].Use();- OpenGL:right, left, top, bottom, back, front
- DirectX:left, right, top, bottom, front, back
- Maya/Corona:rt, lf, up, dn, bk, ft
- HDR Lab:px, nx, py, ny, pz, nz
// 方式1:完整自定义文件名
auto config = SkyboxLoader::CreateCustomConfig(
"assets/textures/skybox",
{"corona_rt.png", "corona_lf.png", "corona_up.png",
"corona_dn.png", "corona_bk.png", "corona_ft.png"},
CubemapConvention::OPENGL
);
// 方式2:基于约定和命名模式
auto config = SkyboxLoader::CreateFromPattern(
"assets/textures/skybox",
"corona_{face}",
CubemapConvention::MAYA,
".png"
);
// 方式3:完全自定义面名称后缀
FaceNamingScheme custom("rt", "lf", "up", "dn", "bk", "ft");
auto config = SkyboxLoader::CreateFromCustomScheme(
"assets/textures/skybox",
"corona_{face}",
custom,
CubemapConvention::OPENGL,
".png"
);- SOLID_COLOR:传统Phong固定颜色环境光
- SKYBOX_SAMPLE:从天空盒采样IBL环境光
- HEMISPHERE:半球渐变环境光(天空/地面插值)
- ❌ 天空盒:存在状态泄露的风险(当有异常产生状态未恢复),需要 RAII管理 OpenGLContext,拓展imgui时需要关注Context的恢复!!!
- ❌ 场景图系统:
SceneNode层级管理 - ❌ PBR材质系统:金属度/粗糙度工作流
- ❌ 资源管理器:自动缓存+异步加载
- ❌ IBL:环境光照
- ❌ Framebuffer
- ❌ 渲染管线抽象:前向/延迟渲染切换与Bloom
- ❌ 阴影系统:强制性多Pass,否则性能损失严重
- ❌ 粒子系统:与实例化架构冲突
- ❌ 骨骼动画:超出学习范围
- ❌ PBR真实光照:项目定位风格化渲染
- ❌ 音频/网络:非渲染核心
- ❌ GPU 上传优化:glBufferStorage(OpenGL4.4+)
- ❌ 视锥剔除:Frustum Culling
- ❌ 渲染状态批处理:State Batching
- ❌ 纹理压缩与MipMap优化
- ❌ Uniform Buffer Object (UBO)
- ❌ InstanceData 无锁快照机制:多线程数据竞争优化
- 问题:
std::vector::push_back()非原子操作,扩容时可能导致渲染线程读取已释放内存 - 不开发原因:需要引入 GSL 库或 C++20
std::span,增加依赖复杂度;当前单线程架构已满足需求
- 问题:
- ❌ Mesh and Texture Cache: 最佳实践为多线程异步,但是代码基础是单线程 GL 渲染循环。
- 问题曾经尝试开发单线程版本,会有20%的性能提升(面对多重复物体的情况),但是代码维护复杂
- 不要直接运行任何archive里面的文件,项目经过重构数次,早已不适配。
- 代码规范:遵循C++17标准,使用现代智能指针
- 架构约束:新增功能需符合
IRenderer接口,保持职责分离 - 性能优先:渲染循环避免堆分配,优先使用实例化
- 文档同步:修改接口需更新
docs/INTERFACES.md
MIT License - 详见 LICENSE 文件
这是一个学习导向的渲染引擎项目,欢迎提交Issue讨论架构设计、性能优化与渲染技术。
