refactor: rename ui_editor to editor for consistency
458
docs/plan/end/编辑器与运行时分层架构设计.md
Normal file
@@ -0,0 +1,458 @@
|
|||||||
|
# 编辑器与运行时分层架构设计
|
||||||
|
|
||||||
|
## 1. 背景与目的
|
||||||
|
|
||||||
|
### 1.1 参考案例:Unity 的 Editor/Player 分离
|
||||||
|
|
||||||
|
Unity 是目前最成熟的游戏引擎之一,其架构设计经过多年迭代验证。Unity 的核心设计理念是**将游戏开发工具(编辑器)与游戏运行载体(播放器)清晰分离**,同时共享同一套引擎核心。
|
||||||
|
|
||||||
|
Unity 的整体架构如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ Unity Hub │
|
||||||
|
│ (项目浏览器、启动器) │
|
||||||
|
└─────────────────────────┬───────────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌─────────────────────────┴───────────────────────────────────┐
|
||||||
|
│ Unity Editor │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────────────┐ │
|
||||||
|
│ │Hierarchy │ │Inspector │ │ Scene │ │ Project │ │
|
||||||
|
│ │ Panel │ │ Panel │ │ View │ │ Browser │ │
|
||||||
|
│ └──────────┘ └──────────┘ └──────────┘ └─────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Unity Engine Core (C++) │ │
|
||||||
|
│ │ Scene System, ECS, Renderer, Physics, Audio, etc. │ │
|
||||||
|
│ └────────────────────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
│ (Build)
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ Unity Player │
|
||||||
|
│ (独立可执行文件,不含编辑器组件) │
|
||||||
|
│ - IL2CPP 虚拟机运行 C# 脚本 │
|
||||||
|
│ - 裁剪过的引擎子集 │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
这一架构的核心思想是:
|
||||||
|
|
||||||
|
- **Editor 是开发工具**,面向游戏开发者,提供场景编辑、参数调整、资源管理等功能
|
||||||
|
- **Player 是运行载体**,面向最终用户,运行游戏逻辑,不包含任何编辑器 UI
|
||||||
|
- **Engine Core 是共享层**,Editor 和 Player 都依赖同一套核心逻辑
|
||||||
|
|
||||||
|
### 1.2 分层架构的目标与收益
|
||||||
|
|
||||||
|
采用分层架构设计编辑器与运行时,带来以下收益:
|
||||||
|
|
||||||
|
| 目标 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **职责分离** | 编辑器和运行时各司其职,代码边界清晰 |
|
||||||
|
| **核心复用** | 场景系统、渲染器、物理等核心逻辑只需维护一份 |
|
||||||
|
| **按需发布** | 发行游戏时只需包含运行时,体积更小 |
|
||||||
|
| **独立迭代** | 编辑器改进不影响运行时稳定性 |
|
||||||
|
| **团队协作** | 程序员专注核心,设计师专注编辑器操作 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 整体架构设计
|
||||||
|
|
||||||
|
### 2.1 分层原则
|
||||||
|
|
||||||
|
编辑器与运行时的分层应遵循以下原则:
|
||||||
|
|
||||||
|
1. **核心不可依赖编辑器**:Engine Core 不应包含任何 UI 相关的代码,确保可以在无界面环境下运行
|
||||||
|
2. **运行时独立最小化**:运行时只包含游戏运行必需的组件,不包含编辑器特有功能
|
||||||
|
3. **接口抽象分离**:通过接口隔离变化,便于未来替换实现
|
||||||
|
4. **数据驱动**:场景、配置等数据与代码逻辑分离
|
||||||
|
|
||||||
|
### 2.2 模块划分
|
||||||
|
|
||||||
|
整体架构分为五个主要模块:
|
||||||
|
|
||||||
|
| 模块 | 英文名 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| 引擎核心 | Engine Core | 游戏运行时逻辑的核心库 |
|
||||||
|
| 编辑器应用 | Editor Application | 游戏开发工具 |
|
||||||
|
| 运行时应用 | Runtime Application | 游戏运行载体 |
|
||||||
|
| 启动器 | Launcher | 项目浏览器与启动器 |
|
||||||
|
| 脚本系统 | Script System | 游戏逻辑脚本 |
|
||||||
|
|
||||||
|
### 2.3 整体架构图
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────┐
|
||||||
|
│ <Launcher> │
|
||||||
|
│ (启动器) │
|
||||||
|
│ 扫描项目,启动应用 │
|
||||||
|
└─────────────┬───────────────┘
|
||||||
|
│
|
||||||
|
┌──────────────────┴──────────────────┐
|
||||||
|
│ │
|
||||||
|
┌─────────▼─────────┐ ┌──────────▼──────────┐
|
||||||
|
│ <Editor>.exe │ │ <Runtime>.exe │
|
||||||
|
│ (编辑器) │ │ (运行时) │
|
||||||
|
│ │ │ │
|
||||||
|
│ ┌─────────────┐ │ │ ┌───────────────┐ │
|
||||||
|
│ │ <Editor>Layer │ │ │ │ <Runtime>Layer │ │
|
||||||
|
│ │ (UI面板) │ │ │ │ (游戏逻辑) │ │
|
||||||
|
│ └──────┬──────┘ │ │ └───────┬───────┘ │
|
||||||
|
└─────────┼─────────┘ └──────────┼──────────┘
|
||||||
|
│ │
|
||||||
|
┌──────────┴───────────────────────────────────┴──────────┐
|
||||||
|
│ │
|
||||||
|
│ <Core> │
|
||||||
|
│ (引擎核心静态库) │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐ │
|
||||||
|
│ │ Scene │ │ Renderer │ │ Physics │ │ Script │ │
|
||||||
|
│ │ (ECS) │ │ │ │ 2D/3D │ │ (C# CLR) │ │
|
||||||
|
│ └──────────┘ └──────────┘ └──────────┘ └────────────┘ │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ ┌──────────────────────────┐ │
|
||||||
|
│ │ Asset │ │ Event │ │ Project/Settings │ │
|
||||||
|
│ │ Manager │ │ System │ │ │ │
|
||||||
|
│ └──────────┘ └──────────┘ └──────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.4 模块间依赖关系
|
||||||
|
|
||||||
|
```
|
||||||
|
<Launcher> ──启动──> <Editor> / <Runtime>
|
||||||
|
|
||||||
|
<Editor> ──链接──> Engine Core
|
||||||
|
<Runtime> ──链接──> Engine Core
|
||||||
|
|
||||||
|
Engine Core(不依赖任何其他模块)
|
||||||
|
│
|
||||||
|
├── Scene System
|
||||||
|
├── Renderer
|
||||||
|
├── Physics
|
||||||
|
├── Script Engine
|
||||||
|
├── Asset Manager
|
||||||
|
└── Event System
|
||||||
|
|
||||||
|
<Script>(C#项目)──编译输出──> .dll 供 <Core> 加载
|
||||||
|
```
|
||||||
|
|
||||||
|
依赖规则:
|
||||||
|
- **Engine Core 是底层**,不被任何其他模块依赖(它依赖谁?)
|
||||||
|
- **Editor 和 Runtime 都依赖 Engine Core**,不直接依赖彼此
|
||||||
|
- **Launcher 只依赖文件系统**,不依赖 Engine Core
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 核心模块设计
|
||||||
|
|
||||||
|
### 3.1 Engine Core(引擎核心)
|
||||||
|
|
||||||
|
#### 职责
|
||||||
|
|
||||||
|
引擎核心是整个架构的基石,负责游戏运行时的一切逻辑:
|
||||||
|
|
||||||
|
- **场景管理**:Entity-Component-System 架构,场景的创建、销毁、序列化
|
||||||
|
- **渲染系统**:渲染管线、材质、光照、后处理
|
||||||
|
- **物理系统**:2D/3D 物理模拟
|
||||||
|
- **脚本系统**:脚本引擎、脚本绑定
|
||||||
|
- **资源管理**:资源加载、缓存、卸载
|
||||||
|
- **事件系统**:事件分发与响应
|
||||||
|
- **输入系统**:输入事件捕获与分发
|
||||||
|
- **音频系统**:音频播放、音效处理
|
||||||
|
|
||||||
|
#### 边界
|
||||||
|
|
||||||
|
Engine Core **不包含**:
|
||||||
|
- 任何 UI 逻辑(ImGui 代码、面板布局等)
|
||||||
|
- 编辑器特有功能(场景大纲、Gizmo 选择等)
|
||||||
|
- 启动器相关逻辑
|
||||||
|
|
||||||
|
#### 设计要点
|
||||||
|
|
||||||
|
Engine Core 编译为**静态库**(`.lib`),被 Editor 和 Runtime 两个可执行文件链接。这种方式确保:
|
||||||
|
- 代码真正共享,而非运行时 DLL 加载
|
||||||
|
- 两个应用使用完全相同的核心逻辑版本
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.2 Editor Application(编辑器应用)
|
||||||
|
|
||||||
|
#### 职责
|
||||||
|
|
||||||
|
编辑器是游戏开发者的主要工具,提供:
|
||||||
|
|
||||||
|
- **场景编辑**:场景视图、层级面板、检视面板
|
||||||
|
- **资源管理**:资源导入、浏览、配置
|
||||||
|
- **游戏预览**:Play/Simulate 模式切换
|
||||||
|
- **项目配置**:项目设置、构建选项
|
||||||
|
|
||||||
|
#### 与 Engine Core 的关系
|
||||||
|
|
||||||
|
编辑器**链接** Engine Core,并在此基础上添加编辑器的 UI 层:
|
||||||
|
|
||||||
|
```
|
||||||
|
Editor Application
|
||||||
|
│
|
||||||
|
├── Editor UI Layer(ImGui 面板)
|
||||||
|
│ │
|
||||||
|
│ ├── ViewportPanel
|
||||||
|
│ ├── SceneHierarchyPanel
|
||||||
|
│ ├── InspectorPanel
|
||||||
|
│ ├── ContentBrowserPanel
|
||||||
|
│ └── MenuBarPanel
|
||||||
|
│
|
||||||
|
└── Engine Core(场景管理、渲染、物理等)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 核心机制:Scene 复制
|
||||||
|
|
||||||
|
编辑器在 Play/Simulate 时,会将当前编辑的场景**完整复制**一份交给运行时:
|
||||||
|
|
||||||
|
```
|
||||||
|
m_activeScene = Scene::copy(m_editorScene);
|
||||||
|
m_activeScene->onRuntimeStart();
|
||||||
|
```
|
||||||
|
|
||||||
|
这样设计的好处是:
|
||||||
|
- 编辑器场景保持不变,方便运行时修改调试
|
||||||
|
- 运行时可以自由修改场景对象,不影响编辑器状态
|
||||||
|
- Stop 时只需切回编辑器场景即可
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.3 Runtime Application(运行时应用)
|
||||||
|
|
||||||
|
#### 职责
|
||||||
|
|
||||||
|
运行时是游戏的运行载体,面向最终用户:
|
||||||
|
|
||||||
|
- **加载游戏项目**:读取项目配置、启动场景
|
||||||
|
- **运行游戏循环**:持续执行 Scene 的更新逻辑
|
||||||
|
- **执行脚本**:运行 C# 脚本游戏逻辑
|
||||||
|
- **渲染画面**:调用渲染器输出图像
|
||||||
|
|
||||||
|
#### 与 Engine Core 的关系
|
||||||
|
|
||||||
|
运行时同样**链接** Engine Core,但不包含任何编辑器 UI:
|
||||||
|
|
||||||
|
```
|
||||||
|
Runtime Application
|
||||||
|
│
|
||||||
|
├── Runtime Layer(游戏入口、更新循环)
|
||||||
|
│
|
||||||
|
└── Engine Core(与编辑器相同)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 特点
|
||||||
|
|
||||||
|
- **无 UI**:运行时没有任何编辑器界面
|
||||||
|
- **最小化**:只包含游戏运行必需的组件
|
||||||
|
- **独立发行**:可打包为独立的游戏可执行文件
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.4 Launcher(启动器)
|
||||||
|
|
||||||
|
#### 职责
|
||||||
|
|
||||||
|
启动器是用户进入游戏的第一个界面:
|
||||||
|
|
||||||
|
- **项目浏览**:扫描并显示本地的游戏项目列表
|
||||||
|
- **项目管理**:创建、打开、删除项目
|
||||||
|
- **启动选择**:启动编辑器或直接运行游戏
|
||||||
|
|
||||||
|
#### 设计要点
|
||||||
|
|
||||||
|
启动器**不链接** Engine Core,而是作为一个独立的小型程序:
|
||||||
|
|
||||||
|
```
|
||||||
|
Launcher Application
|
||||||
|
│
|
||||||
|
├── 项目浏览器 UI
|
||||||
|
├── 项目文件系统扫描
|
||||||
|
└── 进程启动(调用 <Editor>.exe 或 <Runtime>.exe)
|
||||||
|
```
|
||||||
|
|
||||||
|
这种设计使得:
|
||||||
|
- 启动器可以独立编译、独立发布
|
||||||
|
- 即使引擎编译失败,启动器也可能正常工作
|
||||||
|
- 未来可以单独改进启动器的 UI/UX
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.5 Script System(脚本系统)
|
||||||
|
|
||||||
|
#### 定位
|
||||||
|
|
||||||
|
脚本系统是连接游戏逻辑与引擎的桥梁。游戏开发者编写脚本(当前采用 C#),脚本调用引擎提供的 API。
|
||||||
|
|
||||||
|
#### 隔离方式
|
||||||
|
|
||||||
|
脚本系统采用**独立编译 + 运行时加载**的模式:
|
||||||
|
|
||||||
|
```
|
||||||
|
<Script>(C# 项目)
|
||||||
|
│
|
||||||
|
├── 游戏脚本源码(.cs)
|
||||||
|
│
|
||||||
|
└── 编译输出 ──> GameScripts.dll
|
||||||
|
│
|
||||||
|
↓
|
||||||
|
<Core>(运行时加载)
|
||||||
|
│
|
||||||
|
└── Mono Runtime 执行
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 设计要点
|
||||||
|
|
||||||
|
- **脚本独立编译**:不与引擎一起编译,便于快速迭代
|
||||||
|
- **运行时加载**:Engine Core 通过 Mono Runtime 加载编译好的脚本
|
||||||
|
- **引擎 API 绑定**:引擎提供 C++ 函数,脚本通过 InternalCall 调用
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 编辑器与运行时交互机制
|
||||||
|
|
||||||
|
### 4.1 Scene 复制机制
|
||||||
|
|
||||||
|
编辑器维护两种 Scene:
|
||||||
|
|
||||||
|
| Scene | 用途 | 修改权限 |
|
||||||
|
|-------|------|----------|
|
||||||
|
| `m_editorScene` | 编辑器中显示的场景 | 编辑器随时可改 |
|
||||||
|
| `m_runtimeScene` | Play/Simulate 时复制的场景 | 运行时逻辑可改 |
|
||||||
|
|
||||||
|
复制过程:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::shared_ptr<Scene> Scene::copy(std::shared_ptr<Scene> other) {
|
||||||
|
std::shared_ptr<Scene> newScene = std::make_shared<Scene>();
|
||||||
|
|
||||||
|
// 1. 复制所有 Entity(保留 UUID)
|
||||||
|
auto idView = srcSceneRegistry.view<IDComponent>();
|
||||||
|
for (auto e : idView) {
|
||||||
|
UUID uuid = srcSceneRegistry.get<IDComponent>(e).ID;
|
||||||
|
Entity newEntity = newScene->createEntityWithUUID(uuid, name);
|
||||||
|
enttMap[uuid] = (entt::entity)newEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 复制所有 Component
|
||||||
|
copyComponent(AllComponents{}, dstSceneRegistry, srcSceneRegistry, enttMap);
|
||||||
|
|
||||||
|
return newScene;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
复制确保:
|
||||||
|
- Entity UUID 不变,便于调试和序列化
|
||||||
|
- 所有 Component 数据完整复制
|
||||||
|
- 编辑器场景不受影响
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.2 状态机(Edit / Play / Simulate)
|
||||||
|
|
||||||
|
编辑器有三种状态:
|
||||||
|
|
||||||
|
| 状态 | Scene | 物理 | 脚本 | 相机 | 层级可编辑 |
|
||||||
|
|------|-------|------|------|------|------------|
|
||||||
|
| **Edit** | `m_editorScene` | ❌ | ❌ | EditorCamera | ✅ |
|
||||||
|
| **Play** | `m_runtimeScene` | ✅ | ✅ | MainCamera | ❌ |
|
||||||
|
| **Simulate** | `m_runtimeScene` | ✅ | ✅ | EditorCamera | ❌ |
|
||||||
|
|
||||||
|
状态转换:
|
||||||
|
|
||||||
|
```
|
||||||
|
[Edit Mode]
|
||||||
|
│
|
||||||
|
┌─────────┼─────────┐
|
||||||
|
│ │ │
|
||||||
|
[Play] [Simulate] [New/Open Scene]
|
||||||
|
│ │ │
|
||||||
|
└────┬────┴────┬────┘
|
||||||
|
│ │
|
||||||
|
[Stop] ←────┘
|
||||||
|
│
|
||||||
|
↓
|
||||||
|
[Edit Mode](切回编辑器场景)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Edit → Play**:
|
||||||
|
1. `m_editorScene` 深复制为 `m_runtimeScene`
|
||||||
|
2. `m_runtimeScene->onRuntimeStart()` 初始化运行时
|
||||||
|
3. 层级面板禁用编辑
|
||||||
|
|
||||||
|
**Edit → Simulate**:
|
||||||
|
1. 与 Play 相同,但使用 EditorCamera
|
||||||
|
2. 物理运行,方便调试物理效果
|
||||||
|
|
||||||
|
**Stop**:
|
||||||
|
1. 运行时场景 `onRuntimeStop()` 清理
|
||||||
|
2. 切回 `m_editorScene`
|
||||||
|
3. 层级面板恢复编辑
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.3 资源管理差异
|
||||||
|
|
||||||
|
编辑器与运行时对资源的访问权限不同:
|
||||||
|
|
||||||
|
| 操作 | EditorAssetManager | RuntimeAssetManager |
|
||||||
|
|------|-------------------|---------------------|
|
||||||
|
| 加载资源 | ✅ | ✅ |
|
||||||
|
| 导入资源 | ✅ | ❌ |
|
||||||
|
| 创建资源 | ✅ | ❌ |
|
||||||
|
| 删除资源 | ✅ | ❌ |
|
||||||
|
|
||||||
|
这种设计确保:
|
||||||
|
- **运行时安全**:玩家无法导入新资源,防止作弊
|
||||||
|
- **编辑器便捷**:开发者可以直接在编辑器内导入、管理资源
|
||||||
|
- **职责清晰**:资源导入是开发流程的一部分,不是运行时必需的
|
||||||
|
|
||||||
|
实现上,两个 AssetManager 继承自同一基类:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class AssetManagerBase { ... };
|
||||||
|
|
||||||
|
class EditorAssetManager : public AssetManagerBase {
|
||||||
|
void importAsset(const std::filesystem::path&) { /* 实现 */ }
|
||||||
|
};
|
||||||
|
|
||||||
|
class RuntimeAssetManager : public AssetManagerBase {
|
||||||
|
void importAsset(const std::filesystem::path&) = delete; // 禁止
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 总结
|
||||||
|
|
||||||
|
### 5.1 分层架构的核心价值
|
||||||
|
|
||||||
|
| 价值 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **职责清晰** | 编辑器管开发,运行时管执行,各司其职 |
|
||||||
|
| **代码复用** | Engine Core 被两方共享,维护一份代码 |
|
||||||
|
| **灵活发布** | 按需构建,只发行运行时 |
|
||||||
|
| **易于测试** | 核心逻辑可脱离编辑器独立测试 |
|
||||||
|
|
||||||
|
### 5.2 关键设计决策
|
||||||
|
|
||||||
|
| 决策 | 选择 | 理由 |
|
||||||
|
|------|------|------|
|
||||||
|
| 核心库形式 | 静态库 | 编译时链接,确保版本一致 |
|
||||||
|
| 脚本隔离 | 独立 C# 项目 | 快速编译迭代,与引擎解耦 |
|
||||||
|
| 场景复制 | 深复制 | 确保编辑/运行时隔离 |
|
||||||
|
| 状态机 | Edit/Play/Simulate 三态 | 支持物理调试 |
|
||||||
|
|
||||||
|
### 5.3 架构优势
|
||||||
|
|
||||||
|
这种分层架构让引擎具备:
|
||||||
|
- 类似 Unity 的开发体验
|
||||||
|
- 可独立迭代的模块
|
||||||
|
- 可裁剪的运行时体积
|
||||||
|
- 清晰的代码边界
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*文档版本:1.0*
|
||||||
|
*参考:Unity Editor/Player Architecture*
|
||||||
15
参考/Fermion/.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
.vscode
|
||||||
|
build
|
||||||
|
bin/*
|
||||||
|
!bin/
|
||||||
|
!bin/imgui.ini
|
||||||
|
*.log
|
||||||
|
*.ini
|
||||||
|
.vs
|
||||||
|
out
|
||||||
|
cmake-build-debug
|
||||||
|
cmake-build-release
|
||||||
|
.idea
|
||||||
|
docs
|
||||||
|
.cache
|
||||||
|
.claude
|
||||||
40
参考/Fermion/.gitmodules
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[submodule "Fermion/external/spdlog"]
|
||||||
|
path = Fermion/external/spdlog
|
||||||
|
url = https://github.com/gabime/spdlog.git
|
||||||
|
[submodule "Fermion/external/entt"]
|
||||||
|
path = Fermion/external/entt
|
||||||
|
url = https://github.com/skypjack/entt.git
|
||||||
|
[submodule "Fermion/external/glm"]
|
||||||
|
path = Fermion/external/glm
|
||||||
|
url = https://github.com/g-truc/glm.git
|
||||||
|
[submodule "Fermion/external/imgui"]
|
||||||
|
path = Fermion/external/imgui
|
||||||
|
url = https://github.com/ocornut/imgui.git
|
||||||
|
[submodule "Fermion/external/GLFW"]
|
||||||
|
path = Fermion/external/GLFW
|
||||||
|
url = https://github.com/glfw/glfw.git
|
||||||
|
[submodule "Fermion/external/yaml-cpp"]
|
||||||
|
path = Fermion/external/yaml-cpp
|
||||||
|
url = https://github.com/jbeder/yaml-cpp.git
|
||||||
|
[submodule "Fermion/external/ImGuizmo"]
|
||||||
|
path = Fermion/external/ImGuizmo
|
||||||
|
url = https://github.com/CedricGuillemet/ImGuizmo.git
|
||||||
|
[submodule "Fermion/external/box2d"]
|
||||||
|
path = Fermion/external/box2d
|
||||||
|
url = https://github.com/erincatto/box2d.git
|
||||||
|
[submodule "Fermion/external/msdf-atlas-gen"]
|
||||||
|
path = Fermion/external/msdf-atlas-gen
|
||||||
|
url = https://github.com/Chlumsky/msdf-atlas-gen.git
|
||||||
|
[submodule "Fermion/external/freetype"]
|
||||||
|
path = Fermion/external/freetype
|
||||||
|
url = https://github.com/freetype/freetype.git
|
||||||
|
[submodule "Fermion/external/assimp"]
|
||||||
|
path = Fermion/external/assimp
|
||||||
|
url = https://github.com/assimp/assimp.git
|
||||||
|
[submodule "Fermion/external/JoltPhysics"]
|
||||||
|
path = Fermion/external/JoltPhysics
|
||||||
|
url = https://github.com/jrouwe/JoltPhysics.git
|
||||||
|
[submodule "Fermion/external/ImguiNodeEditor"]
|
||||||
|
path = Fermion/external/ImguiNodeEditor
|
||||||
|
url = https://github.com/thedmd/imgui-node-editor.git
|
||||||
|
branch = develop
|
||||||
35
参考/Fermion/Boson/CMakeLists.txt
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
|
||||||
|
set(BOSON_SOUCES
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/BosonEditor.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/BosonLayer.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/BosonLayer_Project.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/BosonLayer_Scene.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/Panels/SceneHierarchyPanel.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/Panels/ContentBrowserPanel.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/Panels/AssetManagerPanel.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/Panels/InspectorPanel.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/Panels/MenuBarPanel.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/Panels/MaterialEditorPanel.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/Panels/TextureConfigPanel.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/Panels/ViewportPanel.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/Panels/SettingsPanel.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/Panels/OverlayRenderPanel.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(BosonEditor ${BOSON_SOUCES})
|
||||||
|
|
||||||
|
set_target_properties(BosonEditor PROPERTIES
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${BIN_DIR}
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(BosonEditor PRIVATE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
|
${CMAKE_SOURCE_DIR}/Fermion/external/ImGuizmo
|
||||||
|
${CMAKE_SOURCE_DIR}/HilbertSpace/)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
target_link_libraries(BosonEditor PRIVATE engine)
|
||||||
|
|
||||||
BIN
参考/Fermion/Boson/Resources/Icons/Camera.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
参考/Fermion/Boson/Resources/Icons/Close.png
Normal file
|
After Width: | Height: | Size: 176 B |
|
After Width: | Height: | Size: 5.1 KiB |
BIN
参考/Fermion/Boson/Resources/Icons/ContentBrowser/FileIcon.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
BIN
参考/Fermion/Boson/Resources/Icons/ContentBrowser/MeshFileIcon.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 64 KiB |
BIN
参考/Fermion/Boson/Resources/Icons/Maximize.png
Normal file
|
After Width: | Height: | Size: 100 B |
BIN
参考/Fermion/Boson/Resources/Icons/Minimize.png
Normal file
|
After Width: | Height: | Size: 80 B |
BIN
参考/Fermion/Boson/Resources/Icons/PauseButton.png
Normal file
|
After Width: | Height: | Size: 119 B |
BIN
参考/Fermion/Boson/Resources/Icons/PlayButton.png
Normal file
|
After Width: | Height: | Size: 357 B |
BIN
参考/Fermion/Boson/Resources/Icons/Restore.png
Normal file
|
After Width: | Height: | Size: 176 B |
BIN
参考/Fermion/Boson/Resources/Icons/SimulateButton.png
Normal file
|
After Width: | Height: | Size: 667 B |
BIN
参考/Fermion/Boson/Resources/Icons/StepButton.png
Normal file
|
After Width: | Height: | Size: 438 B |
BIN
参考/Fermion/Boson/Resources/Icons/StopButton.png
Normal file
|
After Width: | Height: | Size: 115 B |
BIN
参考/Fermion/Boson/Resources/Icons/light.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
120
参考/Fermion/Boson/Resources/Shaders/BRDFLUT.glsl
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
// ============================================================================
|
||||||
|
// BRDF积分查找表生成
|
||||||
|
// ============================================================================
|
||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
layout(location = 1) in vec2 a_TexCoords;
|
||||||
|
|
||||||
|
out vec2 v_TexCoords;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
v_TexCoords = a_TexCoords;
|
||||||
|
gl_Position = vec4(a_Position, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
out vec2 FragColor;
|
||||||
|
in vec2 v_TexCoords;
|
||||||
|
|
||||||
|
const float PI = 3.14159265359;
|
||||||
|
|
||||||
|
// 优化的 pow5
|
||||||
|
float pow5(float x) {
|
||||||
|
float x2 = x * x;
|
||||||
|
return x2 * x2 * x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 低差异序列采样
|
||||||
|
float RadicalInverse_VdC(uint bits) {
|
||||||
|
bits = (bits << 16u) | (bits >> 16u);
|
||||||
|
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
|
||||||
|
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
|
||||||
|
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
|
||||||
|
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
|
||||||
|
return float(bits) * 2.3283064365386963e-10;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 Hammersley(uint i, uint N) {
|
||||||
|
return vec2(float(i)/float(N), RadicalInverse_VdC(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) {
|
||||||
|
float a = roughness * roughness;
|
||||||
|
|
||||||
|
float phi = 2.0 * PI * Xi.x;
|
||||||
|
float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
|
||||||
|
float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
|
||||||
|
|
||||||
|
vec3 H;
|
||||||
|
H.x = cos(phi) * sinTheta;
|
||||||
|
H.y = sin(phi) * sinTheta;
|
||||||
|
H.z = cosTheta;
|
||||||
|
|
||||||
|
vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
|
||||||
|
vec3 tangent = normalize(cross(up, N));
|
||||||
|
vec3 bitangent = cross(N, tangent);
|
||||||
|
|
||||||
|
vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
|
||||||
|
return normalize(sampleVec);
|
||||||
|
}
|
||||||
|
|
||||||
|
float GeometrySchlickGGX(float NdotV, float roughness) {
|
||||||
|
float a = roughness;
|
||||||
|
float k = (a * a) / 2.0;
|
||||||
|
|
||||||
|
float nom = NdotV;
|
||||||
|
float denom = NdotV * (1.0 - k) + k;
|
||||||
|
|
||||||
|
return nom / denom;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {
|
||||||
|
float NdotV = max(dot(N, V), 0.0);
|
||||||
|
float NdotL = max(dot(N, L), 0.0);
|
||||||
|
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
|
||||||
|
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
|
||||||
|
|
||||||
|
return ggx1 * ggx2;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 IntegrateBRDF(float NdotV, float roughness) {
|
||||||
|
vec3 V;
|
||||||
|
V.x = sqrt(1.0 - NdotV*NdotV);
|
||||||
|
V.y = 0.0;
|
||||||
|
V.z = NdotV;
|
||||||
|
|
||||||
|
float A = 0.0;
|
||||||
|
float B = 0.0;
|
||||||
|
|
||||||
|
vec3 N = vec3(0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
const uint SAMPLE_COUNT = 1024u;
|
||||||
|
for(uint i = 0u; i < SAMPLE_COUNT; ++i) {
|
||||||
|
vec2 Xi = Hammersley(i, SAMPLE_COUNT);
|
||||||
|
vec3 H = ImportanceSampleGGX(Xi, N, roughness);
|
||||||
|
vec3 L = normalize(2.0 * dot(V, H) * H - V);
|
||||||
|
|
||||||
|
float NdotL = max(L.z, 0.0);
|
||||||
|
float NdotH = max(H.z, 0.0);
|
||||||
|
float VdotH = max(dot(V, H), 0.0);
|
||||||
|
|
||||||
|
if(NdotL > 0.0) {
|
||||||
|
float G = GeometrySmith(N, V, L, roughness);
|
||||||
|
float G_Vis = (G * VdotH) / max(NdotH * NdotV, 0.001);
|
||||||
|
float Fc = pow5(1.0 - VdotH);
|
||||||
|
|
||||||
|
A += (1.0 - Fc) * G_Vis;
|
||||||
|
B += Fc * G_Vis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
A /= float(SAMPLE_COUNT);
|
||||||
|
B /= float(SAMPLE_COUNT);
|
||||||
|
return vec2(A, B);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 integratedBRDF = IntegrateBRDF(v_TexCoords.x, v_TexCoords.y);
|
||||||
|
FragColor = integratedBRDF;
|
||||||
|
}
|
||||||
76
参考/Fermion/Boson/Resources/Shaders/Circle.glsl
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
|
||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_WorldPosition;
|
||||||
|
layout(location = 1) in vec3 a_LocalPosition;
|
||||||
|
layout(location = 2) in vec4 a_Color;
|
||||||
|
layout(location = 3) in float a_Thickness;
|
||||||
|
layout(location = 4) in float a_Fade;
|
||||||
|
layout(location = 5) in int a_ObjectID;
|
||||||
|
|
||||||
|
// Camera uniform buffer (binding = 0)
|
||||||
|
layout(std140, binding = 0) uniform CameraData
|
||||||
|
{
|
||||||
|
mat4 u_ViewProjection;
|
||||||
|
mat4 u_View;
|
||||||
|
mat4 u_Projection;
|
||||||
|
vec3 u_CameraPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VertexOutput
|
||||||
|
{
|
||||||
|
vec3 LocalPosition;
|
||||||
|
vec4 Color;
|
||||||
|
float Thickness;
|
||||||
|
float Fade;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout (location = 0) out VertexOutput Output;
|
||||||
|
layout (location = 4) out flat int v_ObjectID;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
Output.LocalPosition = a_LocalPosition;
|
||||||
|
Output.Color = a_Color;
|
||||||
|
Output.Thickness = a_Thickness;
|
||||||
|
Output.Fade = a_Fade;
|
||||||
|
|
||||||
|
v_ObjectID = a_ObjectID;
|
||||||
|
|
||||||
|
gl_Position = u_ViewProjection * vec4(a_WorldPosition, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Color;
|
||||||
|
layout(location = 1) out int o_ObjectID;
|
||||||
|
|
||||||
|
struct VertexOutput
|
||||||
|
{
|
||||||
|
vec3 LocalPosition;
|
||||||
|
vec4 Color;
|
||||||
|
float Thickness;
|
||||||
|
float Fade;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout (location = 0) in VertexOutput Input;
|
||||||
|
layout (location = 4) in flat int v_ObjectID;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Calculate distance and fill circle with white
|
||||||
|
float distance = 1.0 - length(Input.LocalPosition);
|
||||||
|
float circle = smoothstep(0.0, Input.Fade, distance);
|
||||||
|
circle *= smoothstep(Input.Thickness + Input.Fade, Input.Thickness, distance);
|
||||||
|
|
||||||
|
if (circle == 0.0)
|
||||||
|
discard;
|
||||||
|
|
||||||
|
// Set output color
|
||||||
|
o_Color = Input.Color;
|
||||||
|
o_Color.a *= circle;
|
||||||
|
|
||||||
|
o_ObjectID = v_ObjectID;
|
||||||
|
}
|
||||||
32
参考/Fermion/Boson/Resources/Shaders/Cube.glsl
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
layout(location = 1) in vec3 a_Normal;
|
||||||
|
layout(location = 2) in vec4 a_Color;
|
||||||
|
layout(location = 3) in int a_ObjectID;
|
||||||
|
|
||||||
|
uniform mat4 u_ViewProjection;
|
||||||
|
|
||||||
|
out vec3 v_Normal;
|
||||||
|
out vec4 v_Color;
|
||||||
|
flat out int v_ObjectID;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
v_Normal = a_Normal;
|
||||||
|
v_Color = a_Color;
|
||||||
|
v_ObjectID = a_ObjectID;
|
||||||
|
gl_Position = u_ViewProjection * vec4(a_Position, 1.0);
|
||||||
|
}
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
in vec3 v_Normal;
|
||||||
|
in vec4 v_Color;
|
||||||
|
flat in int v_ObjectID;
|
||||||
|
|
||||||
|
out vec4 color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 lightDir = normalize(vec3(0.5, 1.0, 0.3));
|
||||||
|
float diff = max(dot(normalize(v_Normal), lightDir), 0.2);
|
||||||
|
color = vec4(v_Color.rgb * diff, v_Color.a);
|
||||||
|
}
|
||||||
357
参考/Fermion/Boson/Resources/Shaders/DeferredLighting.glsl
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
layout(location = 1) in vec2 a_TexCoords;
|
||||||
|
|
||||||
|
out vec2 v_TexCoords;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
v_TexCoords = a_TexCoords;
|
||||||
|
gl_Position = vec4(a_Position, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Color;
|
||||||
|
|
||||||
|
in vec2 v_TexCoords;
|
||||||
|
|
||||||
|
const float PI = 3.14159265359;
|
||||||
|
const float F0_NON_METAL = 0.04;
|
||||||
|
const float MIN_ROUGHNESS = 0.045;
|
||||||
|
|
||||||
|
#define MAX_DIR_LIGHTS 4
|
||||||
|
#define MAX_POINT_LIGHTS 16
|
||||||
|
#define MAX_SPOT_LIGHTS 16
|
||||||
|
|
||||||
|
// 优化的数学函数
|
||||||
|
float pow5(float x) {
|
||||||
|
float x2 = x * x;
|
||||||
|
return x2 * x2 * x;
|
||||||
|
}
|
||||||
|
|
||||||
|
float sq(float x) {
|
||||||
|
return x * x;
|
||||||
|
}
|
||||||
|
|
||||||
|
float saturate(float x) {
|
||||||
|
return clamp(x, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 saturate(vec3 x) {
|
||||||
|
return clamp(x, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DirectionalLight {
|
||||||
|
vec3 direction;
|
||||||
|
vec3 color;
|
||||||
|
float intensity;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PointLight {
|
||||||
|
vec3 position;
|
||||||
|
vec3 color;
|
||||||
|
float intensity;
|
||||||
|
float range;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SpotLight {
|
||||||
|
vec3 position;
|
||||||
|
vec3 direction;
|
||||||
|
|
||||||
|
vec3 color;
|
||||||
|
float intensity;
|
||||||
|
|
||||||
|
float range;
|
||||||
|
float innerConeAngle;
|
||||||
|
float outerConeAngle;
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform int u_DirLightCount;
|
||||||
|
uniform DirectionalLight u_DirLights[MAX_DIR_LIGHTS];
|
||||||
|
|
||||||
|
uniform int u_PointLightCount;
|
||||||
|
uniform PointLight u_PointLights[MAX_POINT_LIGHTS];
|
||||||
|
|
||||||
|
uniform int u_SpotLightCount;
|
||||||
|
uniform SpotLight u_SpotLights[MAX_SPOT_LIGHTS];
|
||||||
|
|
||||||
|
// Camera uniform buffer (binding = 0)
|
||||||
|
layout(std140, binding = 0) uniform CameraData
|
||||||
|
{
|
||||||
|
mat4 u_ViewProjection;
|
||||||
|
mat4 u_View;
|
||||||
|
mat4 u_Projection;
|
||||||
|
vec3 u_CameraPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Light uniform buffer (binding = 2)
|
||||||
|
layout(std140, binding = 2) uniform LightData
|
||||||
|
{
|
||||||
|
mat4 u_LightSpaceMatrix;
|
||||||
|
vec3 u_DirLightDirection;
|
||||||
|
float u_DirLightIntensity;
|
||||||
|
vec3 u_DirLightColor;
|
||||||
|
float _lightPadding0;
|
||||||
|
float u_ShadowBias;
|
||||||
|
float u_ShadowSoftness;
|
||||||
|
int u_EnableShadows;
|
||||||
|
int u_NumDirLights;
|
||||||
|
float u_AmbientIntensity;
|
||||||
|
int u_NumPointLights;
|
||||||
|
int u_NumSpotLights;
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform sampler2D u_GBufferAlbedo;
|
||||||
|
uniform sampler2D u_GBufferNormal;
|
||||||
|
uniform sampler2D u_GBufferMaterial;
|
||||||
|
uniform sampler2D u_GBufferEmissive;
|
||||||
|
uniform sampler2D u_GBufferDepth;
|
||||||
|
|
||||||
|
uniform mat4 u_InverseViewProjection;
|
||||||
|
|
||||||
|
// Shadow mapping
|
||||||
|
uniform sampler2D u_ShadowMap;
|
||||||
|
|
||||||
|
// IBL
|
||||||
|
uniform bool u_UseIBL;
|
||||||
|
uniform samplerCube u_IrradianceMap;
|
||||||
|
uniform samplerCube u_PrefilterMap;
|
||||||
|
uniform sampler2D u_BRDFLT;
|
||||||
|
uniform float u_PrefilterMaxLOD;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// BRDF 函数
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
float D_GGX(float roughness, float NoH) {
|
||||||
|
float a = roughness * roughness;
|
||||||
|
float a2 = a * a;
|
||||||
|
float NoH2 = NoH * NoH;
|
||||||
|
|
||||||
|
float nom = a2;
|
||||||
|
float denom = (NoH2 * (a2 - 1.0) + 1.0);
|
||||||
|
denom = PI * denom * denom;
|
||||||
|
|
||||||
|
return nom / max(denom, 1e-7);
|
||||||
|
}
|
||||||
|
|
||||||
|
float V_SmithGGXCorrelated(float roughness, float NoV, float NoL) {
|
||||||
|
float a = roughness * roughness;
|
||||||
|
float a2 = a * a;
|
||||||
|
float lambdaV = NoL * sqrt(NoV * NoV * (1.0 - a2) + a2);
|
||||||
|
float lambdaL = NoV * sqrt(NoL * NoL * (1.0 - a2) + a2);
|
||||||
|
return 0.5 / max(lambdaV + lambdaL, 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
|
||||||
|
return F0 + (1.0 - F0) * pow5(saturate(1.0 - cosTheta));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) {
|
||||||
|
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow5(saturate(1.0 - cosTheta));
|
||||||
|
}
|
||||||
|
|
||||||
|
float Fd_Burley(float roughness, float NoV, float NoL, float LoH) {
|
||||||
|
float f90 = 0.5 + 2.0 * roughness * LoH * LoH;
|
||||||
|
float lightScatter = 1.0 + (f90 - 1.0) * pow5(1.0 - NoL);
|
||||||
|
float viewScatter = 1.0 + (f90 - 1.0) * pow5(1.0 - NoV);
|
||||||
|
return lightScatter * viewScatter * (1.0 / PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 ACESFilm(vec3 x) {
|
||||||
|
float a = 2.51;
|
||||||
|
float b = 0.03;
|
||||||
|
float c = 2.43;
|
||||||
|
float d = 0.59;
|
||||||
|
float e = 0.14;
|
||||||
|
return saturate((x * (a * x + b)) / (x * (c * x + d) + e));
|
||||||
|
}
|
||||||
|
|
||||||
|
float computeSpecularOcclusion(float NoV, float ao, float roughness) {
|
||||||
|
return clamp(pow(NoV + ao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ao, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) {
|
||||||
|
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
||||||
|
projCoords = projCoords * 0.5 + 0.5;
|
||||||
|
|
||||||
|
if (projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 ||
|
||||||
|
projCoords.y < 0.0 || projCoords.y > 1.0)
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1);
|
||||||
|
|
||||||
|
float shadow = 0.0;
|
||||||
|
vec2 texelSize = 1.0 / textureSize(u_ShadowMap, 0);
|
||||||
|
int pcfRange = int(u_ShadowSoftness);
|
||||||
|
int sampleCount = 0;
|
||||||
|
|
||||||
|
for (int x = -pcfRange; x <= pcfRange; ++x) {
|
||||||
|
for (int y = -pcfRange; y <= pcfRange; ++y) {
|
||||||
|
float pcfDepth = texture(u_ShadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
|
||||||
|
shadow += projCoords.z - bias > pcfDepth ? 1.0 : 0.0;
|
||||||
|
sampleCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shadow /= float(sampleCount);
|
||||||
|
|
||||||
|
return shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 reconstructWorldPosition(vec2 uv, float depth) {
|
||||||
|
vec4 clip = vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
|
||||||
|
vec4 world = u_InverseViewProjection * clip;
|
||||||
|
return world.xyz / max(world.w, 0.0001);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 综合光照评估
|
||||||
|
// ============================================================================
|
||||||
|
vec3 evaluateLighting(vec3 N, vec3 V, vec3 L, vec3 albedo, float roughness,
|
||||||
|
float metallic, vec3 F0, float NoV, vec3 radiance, float occlusion) {
|
||||||
|
|
||||||
|
vec3 H = normalize(V + L);
|
||||||
|
float NoL = max(dot(N, L), 0.0);
|
||||||
|
float NoH = saturate(dot(N, H));
|
||||||
|
float LoH = saturate(dot(L, H));
|
||||||
|
|
||||||
|
// 标准 BRDF
|
||||||
|
float D = D_GGX(roughness, NoH);
|
||||||
|
float V_term = V_SmithGGXCorrelated(roughness, NoV, NoL);
|
||||||
|
vec3 kS = fresnelSchlick(LoH, F0);
|
||||||
|
vec3 specularBRDF = (D * V_term) * kS;
|
||||||
|
|
||||||
|
// Burley 漫反射
|
||||||
|
vec3 kD = (vec3(1.0) - kS) * (1.0 - metallic);
|
||||||
|
vec3 diffuseBRDF = kD * albedo * Fd_Burley(roughness, NoV, NoL, LoH);
|
||||||
|
|
||||||
|
vec3 color = (diffuseBRDF + specularBRDF) * NoL;
|
||||||
|
|
||||||
|
return color * radiance * occlusion;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 主渲染函数
|
||||||
|
// ============================================================================
|
||||||
|
void main() {
|
||||||
|
float depth = texture(u_GBufferDepth, v_TexCoords).r;
|
||||||
|
if (depth >= 1.0)
|
||||||
|
discard;
|
||||||
|
|
||||||
|
vec3 albedo = texture(u_GBufferAlbedo, v_TexCoords).rgb;
|
||||||
|
vec3 N = normalize(texture(u_GBufferNormal, v_TexCoords).rgb);
|
||||||
|
vec3 material = texture(u_GBufferMaterial, v_TexCoords).rgb;
|
||||||
|
vec3 emissive = texture(u_GBufferEmissive, v_TexCoords).rgb;
|
||||||
|
|
||||||
|
float roughness = max(material.r, MIN_ROUGHNESS);
|
||||||
|
float metallic = saturate(material.g);
|
||||||
|
float ao = saturate(material.b);
|
||||||
|
|
||||||
|
vec3 worldPos = reconstructWorldPosition(v_TexCoords, depth);
|
||||||
|
vec3 V = normalize(u_CameraPosition - worldPos);
|
||||||
|
float NoV = max(dot(N, V), 1e-4);
|
||||||
|
|
||||||
|
vec3 F0 = mix(vec3(F0_NON_METAL), albedo, metallic);
|
||||||
|
|
||||||
|
vec3 Lo = vec3(0.0);
|
||||||
|
|
||||||
|
// Main directional light (with shadow)
|
||||||
|
{
|
||||||
|
vec3 L = normalize(-u_DirLightDirection);
|
||||||
|
vec3 radiance = u_DirLightColor * u_DirLightIntensity;
|
||||||
|
|
||||||
|
float shadow = 0.0;
|
||||||
|
if (u_EnableShadows != 0) {
|
||||||
|
vec4 fragPosLightSpace = u_LightSpaceMatrix * vec4(worldPos, 1.0);
|
||||||
|
shadow = calculateShadow(fragPosLightSpace, N, L);
|
||||||
|
}
|
||||||
|
|
||||||
|
Lo += evaluateLighting(N, V, L, albedo, roughness, metallic, F0, NoV, radiance, 1.0 - shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional directional lights
|
||||||
|
for (int i = 0; i < u_DirLightCount; i++) {
|
||||||
|
DirectionalLight light = u_DirLights[i];
|
||||||
|
vec3 L = normalize(-light.direction);
|
||||||
|
vec3 radiance = light.color * light.intensity;
|
||||||
|
Lo += evaluateLighting(N, V, L, albedo, roughness, metallic, F0, NoV, radiance, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Point lights
|
||||||
|
for (int i = 0; i < u_PointLightCount; i++) {
|
||||||
|
PointLight light = u_PointLights[i];
|
||||||
|
vec3 L = light.position - worldPos;
|
||||||
|
float dist = length(L);
|
||||||
|
if (dist > light.range)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
L = normalize(L);
|
||||||
|
float distanceAttenuation = 1.0 / (dist * dist + 1.0);
|
||||||
|
float windowFactor = sq(saturate(1.0 - sq(sq(dist / light.range))));
|
||||||
|
float attenuation = distanceAttenuation * windowFactor;
|
||||||
|
vec3 radiance = light.color * light.intensity * attenuation;
|
||||||
|
|
||||||
|
Lo += evaluateLighting(N, V, L, albedo, roughness, metallic, F0, NoV, radiance, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spot lights
|
||||||
|
for (int i = 0; i < u_SpotLightCount; i++) {
|
||||||
|
SpotLight light = u_SpotLights[i];
|
||||||
|
vec3 L = light.position - worldPos;
|
||||||
|
float dist = length(L);
|
||||||
|
if (dist > light.range)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
L = normalize(L);
|
||||||
|
float theta = dot(L, normalize(light.direction));
|
||||||
|
float epsilon = light.innerConeAngle - light.outerConeAngle;
|
||||||
|
float spotIntensity = saturate((theta - light.outerConeAngle) / epsilon);
|
||||||
|
if (spotIntensity <= 0.0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float distanceAttenuation = 1.0 / (dist * dist + 1.0);
|
||||||
|
float windowFactor = sq(saturate(1.0 - sq(sq(dist / light.range))));
|
||||||
|
float attenuation = distanceAttenuation * windowFactor;
|
||||||
|
vec3 radiance = light.color * light.intensity * attenuation * spotIntensity;
|
||||||
|
|
||||||
|
Lo += evaluateLighting(N, V, L, albedo, roughness, metallic, F0, NoV, radiance, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IBL
|
||||||
|
vec3 ambient = vec3(0.0);
|
||||||
|
if (u_UseIBL) {
|
||||||
|
vec3 F = fresnelSchlickRoughness(NoV, F0, roughness);
|
||||||
|
vec3 kS = F;
|
||||||
|
vec3 kD = (1.0 - kS) * (1.0 - metallic);
|
||||||
|
|
||||||
|
vec3 irradiance = texture(u_IrradianceMap, N).rgb;
|
||||||
|
if (dot(irradiance, irradiance) < 0.000001)
|
||||||
|
irradiance = vec3(0.03);
|
||||||
|
vec3 diffuse = irradiance * albedo;
|
||||||
|
|
||||||
|
vec3 R = reflect(-V, N);
|
||||||
|
float lod = roughness * u_PrefilterMaxLOD;
|
||||||
|
vec3 prefilteredColor = textureLod(u_PrefilterMap, R, lod).rgb;
|
||||||
|
if (dot(prefilteredColor, prefilteredColor) < 0.000001)
|
||||||
|
prefilteredColor = vec3(0.03);
|
||||||
|
|
||||||
|
vec2 brdf = texture(u_BRDFLT, vec2(NoV, roughness)).rg;
|
||||||
|
float specOcclusion = computeSpecularOcclusion(NoV, ao, roughness);
|
||||||
|
vec3 specular = prefilteredColor * (F * brdf.x + brdf.y) * specOcclusion;
|
||||||
|
|
||||||
|
ambient = (kD * diffuse + specular) * ao;
|
||||||
|
} else {
|
||||||
|
ambient = vec3(u_AmbientIntensity) * albedo * ao;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 color = ambient + Lo + emissive;
|
||||||
|
|
||||||
|
color = ACESFilm(color);
|
||||||
|
color = pow(color, vec3(1.0 / 2.2));
|
||||||
|
|
||||||
|
o_Color = vec4(color, 1.0);
|
||||||
|
}
|
||||||
33
参考/Fermion/Boson/Resources/Shaders/DepthView.glsl
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
layout(location = 0) in vec2 aPos;
|
||||||
|
layout(location = 1) in vec2 aUV;
|
||||||
|
out vec2 vUV;
|
||||||
|
void main() {
|
||||||
|
vUV = aUV;
|
||||||
|
gl_Position = vec4(aPos, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
out vec4 FragColor;
|
||||||
|
in vec2 vUV;
|
||||||
|
|
||||||
|
uniform sampler2D u_Depth;
|
||||||
|
uniform float u_Near;
|
||||||
|
uniform float u_Far;
|
||||||
|
uniform int u_IsPerspective;
|
||||||
|
uniform float u_Power;
|
||||||
|
|
||||||
|
float LinearizeDepth(float d) {
|
||||||
|
float z = d * 2.0 - 1.0;
|
||||||
|
return (2.0 * u_Near * u_Far) / (u_Far + u_Near - z * (u_Far - u_Near));
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float depth = texture(u_Depth, vUV).r;
|
||||||
|
float depth01 = (u_IsPerspective != 0) ? (LinearizeDepth(depth) / u_Far) : depth;
|
||||||
|
depth01 =1.0 - clamp(depth01, 0.0, 1.0);
|
||||||
|
depth01 = pow(depth01, max(u_Power*10.0, 0.0001));
|
||||||
|
FragColor = vec4(vec3(depth01), 1.0);
|
||||||
|
}
|
||||||
43
参考/Fermion/Boson/Resources/Shaders/EquirectToCube.glsl
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
|
||||||
|
out vec3 v_LocalPos;
|
||||||
|
|
||||||
|
uniform mat4 u_Projection;
|
||||||
|
uniform mat4 u_View;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
v_LocalPos = a_Position;
|
||||||
|
// 只使用view的旋转部分,移除平移(与skybox一致)
|
||||||
|
mat4 rotView = mat4(mat3(u_View));
|
||||||
|
vec4 clipPos = u_Projection * rotView * vec4(a_Position, 1.0);
|
||||||
|
gl_Position = clipPos.xyww; // 防止从立方体内部渲染时被近平面裁剪
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
in vec3 v_LocalPos;
|
||||||
|
|
||||||
|
uniform sampler2D u_EquirectangularMap;
|
||||||
|
|
||||||
|
const vec2 invAtan = vec2(0.1591, 0.3183);
|
||||||
|
|
||||||
|
vec2 SampleSphericalMap(vec3 v) {
|
||||||
|
vec2 uv = vec2(atan(v.z, v.x), asin(v.y));
|
||||||
|
uv *= invAtan;
|
||||||
|
uv += 0.5;
|
||||||
|
return uv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 uv = SampleSphericalMap(normalize(v_LocalPos));
|
||||||
|
vec3 color = texture(u_EquirectangularMap, uv).rgb;
|
||||||
|
|
||||||
|
// 限制极端HDR值,防止后续IBL生成时出现问题
|
||||||
|
color = min(color, vec3(500.0));
|
||||||
|
|
||||||
|
FragColor = vec4(color, 1.0);
|
||||||
|
}
|
||||||
20
参考/Fermion/Boson/Resources/Shaders/FlatColor.glsl
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
|
||||||
|
uniform mat4 u_ViewProjection;
|
||||||
|
uniform mat4 u_Transform;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
out vec4 FragColor;
|
||||||
|
uniform vec4 u_Color;
|
||||||
|
void main() {
|
||||||
|
FragColor = u_Color;
|
||||||
|
}
|
||||||
104
参考/Fermion/Boson/Resources/Shaders/GBufferDebug.glsl
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
layout(location = 1) in vec2 a_TexCoords;
|
||||||
|
|
||||||
|
out vec2 v_TexCoords;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
v_TexCoords = a_TexCoords;
|
||||||
|
gl_Position = vec4(a_Position, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Color;
|
||||||
|
|
||||||
|
in vec2 v_TexCoords;
|
||||||
|
|
||||||
|
uniform sampler2D u_GBufferAlbedo;
|
||||||
|
uniform sampler2D u_GBufferNormal;
|
||||||
|
uniform sampler2D u_GBufferMaterial;
|
||||||
|
uniform sampler2D u_GBufferEmissive;
|
||||||
|
uniform sampler2D u_GBufferDepth;
|
||||||
|
uniform isampler2D u_GBufferObjectID;
|
||||||
|
|
||||||
|
uniform int u_Mode;
|
||||||
|
uniform float u_Near;
|
||||||
|
uniform float u_Far;
|
||||||
|
uniform float u_DepthPower;
|
||||||
|
|
||||||
|
float LinearizeDepth(float d)
|
||||||
|
{
|
||||||
|
float z = d * 2.0 - 1.0;
|
||||||
|
return (2.0 * u_Near * u_Far) / (u_Far + u_Near - z * (u_Far - u_Near));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 EncodeObjectID(int id)
|
||||||
|
{
|
||||||
|
if (id < 0)
|
||||||
|
return vec3(0.0);
|
||||||
|
|
||||||
|
int r = (id >> 0) & 0xFF;
|
||||||
|
int g = (id >> 8) & 0xFF;
|
||||||
|
int b = (id >> 16) & 0xFF;
|
||||||
|
return vec3(float(r), float(g), float(b)) / 255.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec3 result = vec3(0.0);
|
||||||
|
|
||||||
|
if (u_Mode == 1)
|
||||||
|
{
|
||||||
|
vec3 albedo = texture(u_GBufferAlbedo, v_TexCoords).rgb;
|
||||||
|
result = pow(albedo, vec3(1.0 / 2.2));
|
||||||
|
}
|
||||||
|
else if (u_Mode == 2)
|
||||||
|
{
|
||||||
|
vec3 normal = normalize(texture(u_GBufferNormal, v_TexCoords).rgb);
|
||||||
|
result = normal * 0.5 + 0.5;
|
||||||
|
}
|
||||||
|
else if (u_Mode == 3)
|
||||||
|
{
|
||||||
|
result = texture(u_GBufferMaterial, v_TexCoords).rgb;
|
||||||
|
}
|
||||||
|
else if (u_Mode == 4)
|
||||||
|
{
|
||||||
|
float roughness = texture(u_GBufferMaterial, v_TexCoords).r;
|
||||||
|
result = vec3(roughness);
|
||||||
|
}
|
||||||
|
else if (u_Mode == 5)
|
||||||
|
{
|
||||||
|
float metallic = texture(u_GBufferMaterial, v_TexCoords).g;
|
||||||
|
result = vec3(metallic);
|
||||||
|
}
|
||||||
|
else if (u_Mode == 6)
|
||||||
|
{
|
||||||
|
float ao = texture(u_GBufferMaterial, v_TexCoords).b;
|
||||||
|
result = vec3(ao);
|
||||||
|
}
|
||||||
|
else if (u_Mode == 7)
|
||||||
|
{
|
||||||
|
vec3 emissive = texture(u_GBufferEmissive, v_TexCoords).rgb;
|
||||||
|
result = pow(emissive, vec3(1.0 / 2.2));
|
||||||
|
}
|
||||||
|
else if (u_Mode == 8)
|
||||||
|
{
|
||||||
|
float depth = texture(u_GBufferDepth, v_TexCoords).r;
|
||||||
|
float depth01 = LinearizeDepth(depth) / max(u_Far, 0.0001);
|
||||||
|
depth01 = 1.0 - clamp(depth01, 0.0, 1.0);
|
||||||
|
depth01 = pow(depth01, max(u_DepthPower * 10.0, 0.0001));
|
||||||
|
result = vec3(depth01);
|
||||||
|
}
|
||||||
|
else if (u_Mode == 9)
|
||||||
|
{
|
||||||
|
int id = texture(u_GBufferObjectID, v_TexCoords).r;
|
||||||
|
result = EncodeObjectID(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
o_Color = vec4(result, 1.0);
|
||||||
|
}
|
||||||
71
参考/Fermion/Boson/Resources/Shaders/GBufferMesh.glsl
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
layout(location = 1) in vec3 a_Normal;
|
||||||
|
layout(location = 2) in vec4 a_Color;
|
||||||
|
layout(location = 3) in vec2 a_TexCoords;
|
||||||
|
|
||||||
|
// Camera uniform buffer (binding = 0)
|
||||||
|
layout(std140, binding = 0) uniform CameraData
|
||||||
|
{
|
||||||
|
mat4 u_ViewProjection;
|
||||||
|
mat4 u_View;
|
||||||
|
mat4 u_Projection;
|
||||||
|
vec3 u_CameraPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Model uniform buffer (binding = 1)
|
||||||
|
layout(std140, binding = 1) uniform ModelData
|
||||||
|
{
|
||||||
|
mat4 u_Model;
|
||||||
|
mat4 u_NormalMatrix;
|
||||||
|
int u_ObjectID;
|
||||||
|
};
|
||||||
|
|
||||||
|
out vec3 v_Normal;
|
||||||
|
out vec2 v_TexCoords;
|
||||||
|
flat out int v_ObjectID;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 worldPos = u_Model * vec4(a_Position, 1.0);
|
||||||
|
v_Normal = mat3(u_NormalMatrix) * a_Normal;
|
||||||
|
v_TexCoords = a_TexCoords;
|
||||||
|
v_ObjectID = u_ObjectID;
|
||||||
|
|
||||||
|
gl_Position = u_ViewProjection * worldPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Albedo;
|
||||||
|
layout(location = 1) out vec4 o_Normal;
|
||||||
|
layout(location = 2) out vec4 o_Material;
|
||||||
|
layout(location = 3) out vec4 o_Emissive;
|
||||||
|
layout(location = 4) out int o_ObjectID;
|
||||||
|
|
||||||
|
in vec3 v_Normal;
|
||||||
|
in vec2 v_TexCoords;
|
||||||
|
flat in int v_ObjectID;
|
||||||
|
|
||||||
|
uniform bool u_UseTexture;
|
||||||
|
uniform sampler2D u_Texture;
|
||||||
|
uniform vec4 u_Kd;
|
||||||
|
uniform bool u_FlipUV;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec2 uv = v_TexCoords;
|
||||||
|
if (u_FlipUV)
|
||||||
|
uv.y = 1.0 - uv.y;
|
||||||
|
|
||||||
|
vec4 baseColor = u_UseTexture ? texture(u_Texture, uv) : u_Kd;
|
||||||
|
|
||||||
|
o_Albedo = vec4(baseColor.rgb, baseColor.a);
|
||||||
|
o_Normal = vec4(normalize(v_Normal), 1.0);
|
||||||
|
o_Material = vec4(1.0, 0.0, 1.0, 1.0);
|
||||||
|
o_Emissive = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
o_ObjectID = v_ObjectID;
|
||||||
|
}
|
||||||
152
参考/Fermion/Boson/Resources/Shaders/GBufferPBRMesh.glsl
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
layout(location = 1) in vec3 a_Normal;
|
||||||
|
layout(location = 2) in vec4 a_Color;
|
||||||
|
layout(location = 3) in vec2 a_TexCoords;
|
||||||
|
layout(location = 4) in vec3 a_Tangent;
|
||||||
|
layout(location = 5) in vec3 a_Bitangent;
|
||||||
|
|
||||||
|
// Camera uniform buffer (binding = 0)
|
||||||
|
layout(std140, binding = 0) uniform CameraData
|
||||||
|
{
|
||||||
|
mat4 u_ViewProjection;
|
||||||
|
mat4 u_View;
|
||||||
|
mat4 u_Projection;
|
||||||
|
vec3 u_CameraPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Model uniform buffer (binding = 1)
|
||||||
|
layout(std140, binding = 1) uniform ModelData
|
||||||
|
{
|
||||||
|
mat4 u_Model;
|
||||||
|
mat4 u_NormalMatrix;
|
||||||
|
int u_ObjectID;
|
||||||
|
};
|
||||||
|
|
||||||
|
out vec3 v_WorldPos;
|
||||||
|
out vec3 v_Normal;
|
||||||
|
out vec2 v_TexCoords;
|
||||||
|
flat out int v_ObjectID;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 worldPos = u_Model * vec4(a_Position, 1.0);
|
||||||
|
v_WorldPos = worldPos.xyz;
|
||||||
|
|
||||||
|
// Use precomputed normal matrix from UBO
|
||||||
|
mat3 normalMatrix = mat3(u_NormalMatrix);
|
||||||
|
v_Normal = normalize(normalMatrix * a_Normal);
|
||||||
|
|
||||||
|
v_TexCoords = a_TexCoords;
|
||||||
|
v_ObjectID = u_ObjectID;
|
||||||
|
|
||||||
|
gl_Position = u_ViewProjection * worldPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Albedo;
|
||||||
|
layout(location = 1) out vec4 o_Normal;
|
||||||
|
layout(location = 2) out vec4 o_Material;
|
||||||
|
layout(location = 3) out vec4 o_Emissive;
|
||||||
|
layout(location = 4) out int o_ObjectID;
|
||||||
|
|
||||||
|
in vec3 v_WorldPos;
|
||||||
|
in vec3 v_Normal;
|
||||||
|
in vec2 v_TexCoords;
|
||||||
|
flat in int v_ObjectID;
|
||||||
|
|
||||||
|
struct Material
|
||||||
|
{
|
||||||
|
vec3 albedo;
|
||||||
|
float metallic;
|
||||||
|
float roughness;
|
||||||
|
float ao;
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform Material u_Material;
|
||||||
|
|
||||||
|
uniform bool u_UseAlbedoMap;
|
||||||
|
uniform sampler2D u_AlbedoMap;
|
||||||
|
|
||||||
|
uniform bool u_UseNormalMap;
|
||||||
|
uniform sampler2D u_NormalMap;
|
||||||
|
uniform float u_NormalStrength;
|
||||||
|
uniform float u_ToksvigStrength;
|
||||||
|
|
||||||
|
uniform bool u_UseMetallicMap;
|
||||||
|
uniform sampler2D u_MetallicMap;
|
||||||
|
|
||||||
|
uniform bool u_UseRoughnessMap;
|
||||||
|
uniform sampler2D u_RoughnessMap;
|
||||||
|
|
||||||
|
uniform bool u_UseAOMap;
|
||||||
|
uniform sampler2D u_AOMap;
|
||||||
|
|
||||||
|
uniform bool u_FlipUV;
|
||||||
|
|
||||||
|
vec3 getNormalFromMap(vec2 uv, out float normalVariance, out float normalLength)
|
||||||
|
{
|
||||||
|
normalVariance = 0.0;
|
||||||
|
normalLength = 1.0;
|
||||||
|
|
||||||
|
if (!u_UseNormalMap)
|
||||||
|
return normalize(v_Normal);
|
||||||
|
|
||||||
|
vec3 tangentNormal = texture(u_NormalMap, uv).xyz * 2.0 - 1.0;
|
||||||
|
normalLength = length(tangentNormal);
|
||||||
|
|
||||||
|
vec3 Q1 = dFdx(v_WorldPos);
|
||||||
|
vec3 Q2 = dFdy(v_WorldPos);
|
||||||
|
vec2 st1 = dFdx(uv);
|
||||||
|
vec2 st2 = dFdy(uv);
|
||||||
|
|
||||||
|
vec3 N = normalize(v_Normal);
|
||||||
|
vec3 T = normalize(Q1 * st2.t - Q2 * st1.t);
|
||||||
|
vec3 B = normalize(cross(N, T));
|
||||||
|
mat3 TBN = mat3(T, B, N);
|
||||||
|
|
||||||
|
vec3 worldNormal = normalize(TBN * tangentNormal);
|
||||||
|
worldNormal = normalize(mix(N, worldNormal, u_NormalStrength));
|
||||||
|
|
||||||
|
vec3 dndx = dFdx(worldNormal);
|
||||||
|
vec3 dndy = dFdy(worldNormal);
|
||||||
|
normalVariance = max(0.0, dot(dndx, dndx) + dot(dndy, dndy));
|
||||||
|
return worldNormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec2 uv = v_TexCoords;
|
||||||
|
if (u_FlipUV)
|
||||||
|
uv.y = 1.0 - uv.y;
|
||||||
|
|
||||||
|
vec3 baseColorLinear = pow(u_Material.albedo, vec3(2.2));
|
||||||
|
vec3 texColorLinear = u_UseAlbedoMap ? pow(texture(u_AlbedoMap, uv).rgb, vec3(2.2)) : vec3(1.0);
|
||||||
|
vec3 albedo = texColorLinear * baseColorLinear;
|
||||||
|
|
||||||
|
float metallic = u_UseMetallicMap ? texture(u_MetallicMap, uv).r : u_Material.metallic;
|
||||||
|
float roughness = u_UseRoughnessMap ? texture(u_RoughnessMap, uv).r : u_Material.roughness;
|
||||||
|
float ao = u_UseAOMap ? texture(u_AOMap, uv).r : u_Material.ao;
|
||||||
|
|
||||||
|
float normalVariance;
|
||||||
|
float normalLength;
|
||||||
|
vec3 normal = getNormalFromMap(uv, normalVariance, normalLength);
|
||||||
|
|
||||||
|
float roughnessAA = clamp(roughness, 0.04, 1.0);
|
||||||
|
if (u_UseNormalMap)
|
||||||
|
{
|
||||||
|
float kernelRoughness = min(2.0 * normalVariance, 1.0);
|
||||||
|
float tokvsig = clamp(1.0 - normalLength, 0.0, 1.0);
|
||||||
|
roughnessAA = clamp(sqrt(roughnessAA * roughnessAA + kernelRoughness + tokvsig * 0.5 * u_ToksvigStrength), 0.04, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
o_Albedo = vec4(albedo, 1.0);
|
||||||
|
o_Normal = vec4(normalize(normal), 1.0);
|
||||||
|
o_Material = vec4(roughnessAA, metallic, ao, 1.0);
|
||||||
|
o_Emissive = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
o_ObjectID = v_ObjectID;
|
||||||
|
}
|
||||||
119
参考/Fermion/Boson/Resources/Shaders/IBLPrefilter.glsl
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
// ============================================================================
|
||||||
|
// 预过滤环境贴图生成 - 用于镜面反射IBL
|
||||||
|
// ============================================================================
|
||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
|
||||||
|
out vec3 v_WorldPos;
|
||||||
|
|
||||||
|
uniform mat4 u_Projection;
|
||||||
|
uniform mat4 u_View;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
v_WorldPos = a_Position;
|
||||||
|
// 只使用view的旋转部分,移除平移(与skybox一致)
|
||||||
|
mat4 rotView = mat4(mat3(u_View));
|
||||||
|
vec4 clipPos = u_Projection * rotView * vec4(v_WorldPos, 1.0);
|
||||||
|
gl_Position = clipPos.xyww; // 确保深度为最远
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
out vec4 FragColor;
|
||||||
|
in vec3 v_WorldPos;
|
||||||
|
|
||||||
|
uniform samplerCube u_EnvironmentMap;
|
||||||
|
uniform float u_Roughness;
|
||||||
|
|
||||||
|
const float PI = 3.14159265359;
|
||||||
|
|
||||||
|
// 低差异序列采样
|
||||||
|
float RadicalInverse_VdC(uint bits) {
|
||||||
|
bits = (bits << 16u) | (bits >> 16u);
|
||||||
|
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
|
||||||
|
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
|
||||||
|
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
|
||||||
|
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
|
||||||
|
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 Hammersley(uint i, uint N) {
|
||||||
|
return vec2(float(i) / float(N), RadicalInverse_VdC(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
float DistributionGGX(vec3 N, vec3 H, float roughness) {
|
||||||
|
float a = roughness * roughness;
|
||||||
|
float a2 = a * a;
|
||||||
|
float NdotH = max(dot(N, H), 0.0);
|
||||||
|
float NdotH2 = NdotH * NdotH;
|
||||||
|
|
||||||
|
float nom = a2;
|
||||||
|
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
|
||||||
|
denom = PI * denom * denom;
|
||||||
|
|
||||||
|
return nom / denom;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 基于粗糙度的重要性采样
|
||||||
|
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) {
|
||||||
|
float a = roughness * roughness;
|
||||||
|
|
||||||
|
float phi = 2.0 * PI * Xi.x;
|
||||||
|
float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y));
|
||||||
|
float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
|
||||||
|
|
||||||
|
// 切线空间的半向量
|
||||||
|
vec3 H;
|
||||||
|
H.x = cos(phi) * sinTheta;
|
||||||
|
H.y = sin(phi) * sinTheta;
|
||||||
|
H.z = cosTheta;
|
||||||
|
|
||||||
|
// 切线空间转世界空间
|
||||||
|
vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
|
||||||
|
vec3 tangent = normalize(cross(up, N));
|
||||||
|
vec3 bitangent = cross(N, tangent);
|
||||||
|
|
||||||
|
vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
|
||||||
|
return normalize(sampleVec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 N = normalize(v_WorldPos);
|
||||||
|
|
||||||
|
// 假设视角方向等于反射方向
|
||||||
|
vec3 R = N;
|
||||||
|
vec3 V = R;
|
||||||
|
|
||||||
|
const uint SAMPLE_COUNT = 1024u;
|
||||||
|
vec3 prefilteredColor = vec3(0.0);
|
||||||
|
float totalWeight = 0.0;
|
||||||
|
|
||||||
|
for(uint i = 0u; i < SAMPLE_COUNT; ++i) {
|
||||||
|
vec2 Xi = Hammersley(i, SAMPLE_COUNT);
|
||||||
|
vec3 H = ImportanceSampleGGX(Xi, N, u_Roughness);
|
||||||
|
vec3 L = normalize(2.0 * dot(V, H) * H - V);
|
||||||
|
|
||||||
|
float NdotL = max(dot(N, L), 0.0);
|
||||||
|
if(NdotL > 0.0) {
|
||||||
|
|
||||||
|
float NdotH = max(dot(N, H), 0.0);
|
||||||
|
float HdotV = max(dot(H, V), 0.0);
|
||||||
|
float D = DistributionGGX(N, H, u_Roughness);
|
||||||
|
|
||||||
|
float pdf = (D * NdotH / (4.0 * HdotV)) + 0.0001;
|
||||||
|
float resolution = 512.0;
|
||||||
|
float saTexel = 4.0 * PI / (6.0 * resolution * resolution);
|
||||||
|
float saSample = 1.0 / (float(SAMPLE_COUNT) * pdf + 0.0001);
|
||||||
|
|
||||||
|
float mipLevel = u_Roughness == 0.0 ? 0.0 : 0.5 * log2(saSample / saTexel);
|
||||||
|
prefilteredColor += textureLod(u_EnvironmentMap, L, mipLevel).rgb;
|
||||||
|
// prefilteredColor += texture(u_EnvironmentMap, L).rgb;
|
||||||
|
totalWeight += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prefilteredColor = prefilteredColor / totalWeight;
|
||||||
|
|
||||||
|
FragColor = vec4(prefilteredColor, 1.0);
|
||||||
|
}
|
||||||
55
参考/Fermion/Boson/Resources/Shaders/IBLPreprocess.glsl
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// ============================================================================
|
||||||
|
// 辐照度贴图生成 - 用于漫反射IBL
|
||||||
|
// ============================================================================
|
||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
|
||||||
|
out vec3 v_WorldPos;
|
||||||
|
|
||||||
|
uniform mat4 u_Projection;
|
||||||
|
uniform mat4 u_View;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
v_WorldPos = a_Position;
|
||||||
|
gl_Position = u_Projection * u_View * vec4(a_Position, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
out vec4 FragColor;
|
||||||
|
in vec3 v_WorldPos;
|
||||||
|
|
||||||
|
uniform samplerCube u_EnvironmentMap;
|
||||||
|
|
||||||
|
const float PI = 3.14159265359;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 N = normalize(v_WorldPos);
|
||||||
|
|
||||||
|
vec3 irradiance = vec3(0.0);
|
||||||
|
|
||||||
|
vec3 up = vec3(0.0, 1.0, 0.0);
|
||||||
|
if(abs(N.y) > 0.999) {
|
||||||
|
up = vec3(0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
vec3 right = normalize(cross(up, N));
|
||||||
|
up = normalize(cross(N, right));
|
||||||
|
|
||||||
|
// 在半球上采样
|
||||||
|
float sampleDelta = 0.015;
|
||||||
|
float nrSamples = 0.0;
|
||||||
|
|
||||||
|
for(float phi = 0.0; phi < 2.0 * PI; phi += sampleDelta) {
|
||||||
|
for(float theta = 0.0; theta < 0.5 * PI; theta += sampleDelta) {
|
||||||
|
vec3 tangentSample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
|
||||||
|
vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N;
|
||||||
|
irradiance += texture(u_EnvironmentMap, sampleVec).rgb * cos(theta) * sin(theta);
|
||||||
|
nrSamples++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
irradiance = PI * irradiance * (1.0 / float(nrSamples));
|
||||||
|
|
||||||
|
FragColor = vec4(irradiance, 1.0);
|
||||||
|
}
|
||||||
220
参考/Fermion/Boson/Resources/Shaders/InfiniteGrid.glsl
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
// Infinite Grid Shader
|
||||||
|
// Based on "The Best Darn Grid Shader (Yet)" by Ben Golus
|
||||||
|
|
||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform CameraData
|
||||||
|
{
|
||||||
|
mat4 u_ViewProjection;
|
||||||
|
mat4 u_View;
|
||||||
|
mat4 u_Projection;
|
||||||
|
vec3 u_CameraPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
out vec3 v_NearPoint;
|
||||||
|
out vec3 v_FarPoint;
|
||||||
|
|
||||||
|
vec3 unprojectPoint(float x, float y, float z) {
|
||||||
|
mat4 viewInv = inverse(u_View);
|
||||||
|
mat4 projInv = inverse(u_Projection);
|
||||||
|
vec4 unprojectedPoint = viewInv * projInv * vec4(x, y, z, 1.0);
|
||||||
|
return unprojectedPoint.xyz / unprojectedPoint.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
v_NearPoint = unprojectPoint(a_Position.x, a_Position.y, 0.0);
|
||||||
|
v_FarPoint = unprojectPoint(a_Position.x, a_Position.y, 1.0);
|
||||||
|
gl_Position = vec4(a_Position, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Color;
|
||||||
|
layout(location = 1) out int o_ObjectID;
|
||||||
|
|
||||||
|
in vec3 v_NearPoint;
|
||||||
|
in vec3 v_FarPoint;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform CameraData
|
||||||
|
{
|
||||||
|
mat4 u_ViewProjection;
|
||||||
|
mat4 u_View;
|
||||||
|
mat4 u_Projection;
|
||||||
|
vec3 u_CameraPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Grid plane: 0 = XZ (Y up), 1 = XY (Z forward), 2 = YZ (X right)
|
||||||
|
uniform int u_GridPlane = 0;
|
||||||
|
uniform float u_GridScale = 1.0;
|
||||||
|
uniform vec4 u_GridColorThin = vec4(0.5, 0.5, 0.5, 0.4);
|
||||||
|
uniform vec4 u_GridColorThick = vec4(0.5, 0.5, 0.5, 0.6);
|
||||||
|
uniform vec4 u_AxisColorX = vec4(0.9, 0.2, 0.2, 1.0);
|
||||||
|
uniform vec4 u_AxisColorZ = vec4(0.2, 0.2, 0.9, 1.0);
|
||||||
|
uniform float u_FadeDistance = 500.0;
|
||||||
|
|
||||||
|
float computeDepth(vec3 pos) {
|
||||||
|
vec4 clipSpacePos = u_ViewProjection * vec4(pos, 1.0);
|
||||||
|
return clipSpacePos.z / clipSpacePos.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the plane normal based on grid plane type
|
||||||
|
vec3 getPlaneNormal() {
|
||||||
|
if (u_GridPlane == 1) return vec3(0.0, 0.0, 1.0); // XY plane, Z normal
|
||||||
|
if (u_GridPlane == 2) return vec3(1.0, 0.0, 0.0); // YZ plane, X normal
|
||||||
|
return vec3(0.0, 1.0, 0.0); // XZ plane, Y normal (default)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get 2D coordinates on the plane
|
||||||
|
vec2 getPlaneCoords(vec3 pos) {
|
||||||
|
if (u_GridPlane == 1) return pos.xy; // XY plane
|
||||||
|
if (u_GridPlane == 2) return pos.yz; // YZ plane
|
||||||
|
return pos.xz; // XZ plane (default)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the component perpendicular to the plane (for axis drawing)
|
||||||
|
vec2 getAxisCoords(vec3 pos) {
|
||||||
|
// Returns the two coordinates used for drawing axis lines
|
||||||
|
// First component -> first axis color, Second component -> second axis color
|
||||||
|
if (u_GridPlane == 1) return vec2(pos.x, pos.y); // XY: X-axis and Y-axis
|
||||||
|
if (u_GridPlane == 2) return vec2(pos.y, pos.z); // YZ: Y-axis and Z-axis
|
||||||
|
return vec2(pos.x, pos.z); // XZ: X-axis and Z-axis
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate t for ray-plane intersection
|
||||||
|
float rayPlaneIntersection(vec3 nearPoint, vec3 farPoint) {
|
||||||
|
vec3 rayDir = farPoint - nearPoint;
|
||||||
|
|
||||||
|
if (u_GridPlane == 1) {
|
||||||
|
// XY plane (z = 0)
|
||||||
|
if (abs(rayDir.z) < 0.0001) return -1.0;
|
||||||
|
return -nearPoint.z / rayDir.z;
|
||||||
|
}
|
||||||
|
if (u_GridPlane == 2) {
|
||||||
|
// YZ plane (x = 0)
|
||||||
|
if (abs(rayDir.x) < 0.0001) return -1.0;
|
||||||
|
return -nearPoint.x / rayDir.x;
|
||||||
|
}
|
||||||
|
// XZ plane (y = 0) - default
|
||||||
|
if (abs(rayDir.y) < 0.0001) return -1.0;
|
||||||
|
return -nearPoint.y / rayDir.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get view angle component for normal fade
|
||||||
|
float getViewAngleComponent(vec3 viewDir) {
|
||||||
|
if (u_GridPlane == 1) return abs(viewDir.z); // XY plane
|
||||||
|
if (u_GridPlane == 2) return abs(viewDir.x); // YZ plane
|
||||||
|
return abs(viewDir.y); // XZ plane
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pristine grid - single pixel line with proper AA
|
||||||
|
float pristineGridLine(vec2 uv) {
|
||||||
|
vec2 dudv = fwidth(uv);
|
||||||
|
vec2 uvMod = fract(uv);
|
||||||
|
vec2 uvDist = min(uvMod, 1.0 - uvMod);
|
||||||
|
vec2 distInPixels = uvDist / dudv;
|
||||||
|
vec2 lineAlpha = 1.0 - smoothstep(0.0, 1.0, distInPixels);
|
||||||
|
float alpha = max(lineAlpha.x, lineAlpha.y);
|
||||||
|
float density = max(dudv.x, dudv.y);
|
||||||
|
float densityFade = 1.0 - smoothstep(0.5, 1.0, density);
|
||||||
|
return alpha * densityFade;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Axis line - single pixel wide
|
||||||
|
float axisLineAA(float coord, float dudv) {
|
||||||
|
float distInPixels = abs(coord) / dudv;
|
||||||
|
return 1.0 - smoothstep(0.0, 1.5, distInPixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float t = rayPlaneIntersection(v_NearPoint, v_FarPoint);
|
||||||
|
|
||||||
|
if (t < 0.0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 fragPos3D = v_NearPoint + t * (v_FarPoint - v_NearPoint);
|
||||||
|
float depth = computeDepth(fragPos3D);
|
||||||
|
|
||||||
|
if (depth > 1.0 || depth < -1.0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 worldPos = getPlaneCoords(fragPos3D);
|
||||||
|
|
||||||
|
// === Fading ===
|
||||||
|
|
||||||
|
// Radial fade
|
||||||
|
float dist = length(fragPos3D - u_CameraPosition);
|
||||||
|
float radialFade = 1.0 - smoothstep(u_FadeDistance * 0.3, u_FadeDistance, dist);
|
||||||
|
|
||||||
|
// Normal fade (view angle)
|
||||||
|
vec3 viewDir = normalize(fragPos3D - u_CameraPosition);
|
||||||
|
float viewAngle = getViewAngleComponent(viewDir);
|
||||||
|
float normalFade = smoothstep(0.0, 0.15, viewAngle);
|
||||||
|
|
||||||
|
float fadeFactor = radialFade * normalFade;
|
||||||
|
|
||||||
|
if (fadeFactor < 0.001) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Grid calculation ===
|
||||||
|
|
||||||
|
vec2 gridCoord1 = worldPos / u_GridScale;
|
||||||
|
vec2 gridCoord10 = worldPos / (u_GridScale * 10.0);
|
||||||
|
|
||||||
|
float grid1 = pristineGridLine(gridCoord1);
|
||||||
|
float grid10 = pristineGridLine(gridCoord10);
|
||||||
|
|
||||||
|
// LOD blend
|
||||||
|
vec2 deriv1 = fwidth(gridCoord1);
|
||||||
|
float lodFactor = smoothstep(0.3, 0.6, max(deriv1.x, deriv1.y));
|
||||||
|
|
||||||
|
// Combine grids
|
||||||
|
float gridIntensity = mix(max(grid1, grid10 * 0.7), grid10, lodFactor);
|
||||||
|
|
||||||
|
// Grid color
|
||||||
|
vec3 gridColor = mix(u_GridColorThin.rgb, u_GridColorThick.rgb, lodFactor);
|
||||||
|
float baseAlpha = mix(u_GridColorThin.a, u_GridColorThick.a, lodFactor);
|
||||||
|
float gridAlpha = baseAlpha * gridIntensity * fadeFactor;
|
||||||
|
|
||||||
|
// === Axis lines ===
|
||||||
|
|
||||||
|
vec2 axisCoords = getAxisCoords(fragPos3D);
|
||||||
|
vec2 worldDeriv = fwidth(worldPos);
|
||||||
|
|
||||||
|
// First axis (uses AxisColorX - typically red)
|
||||||
|
float axis1Alpha = axisLineAA(axisCoords.y, worldDeriv.y) * fadeFactor;
|
||||||
|
// Second axis (uses AxisColorZ - typically blue)
|
||||||
|
float axis2Alpha = axisLineAA(axisCoords.x, worldDeriv.x) * fadeFactor;
|
||||||
|
|
||||||
|
// === Final composition ===
|
||||||
|
|
||||||
|
vec3 finalColor = gridColor;
|
||||||
|
float finalAlpha = gridAlpha;
|
||||||
|
|
||||||
|
// Blend axis colors
|
||||||
|
if (axis2Alpha > 0.001) {
|
||||||
|
float blend = axis2Alpha * u_AxisColorZ.a;
|
||||||
|
finalColor = mix(finalColor, u_AxisColorZ.rgb, blend);
|
||||||
|
finalAlpha = max(finalAlpha, blend);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (axis1Alpha > 0.001) {
|
||||||
|
float blend = axis1Alpha * u_AxisColorX.a;
|
||||||
|
finalColor = mix(finalColor, u_AxisColorX.rgb, blend);
|
||||||
|
finalAlpha = max(finalAlpha, blend);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finalAlpha < 0.001) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragDepth = depth * 0.5 + 0.5;
|
||||||
|
o_Color = vec4(finalColor, finalAlpha);
|
||||||
|
o_ObjectID = -1;
|
||||||
|
}
|
||||||
48
参考/Fermion/Boson/Resources/Shaders/Line.glsl
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
|
||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
layout(location = 1) in vec4 a_Color;
|
||||||
|
layout(location = 2) in int a_ObjectID;
|
||||||
|
|
||||||
|
// Camera uniform buffer (binding = 0)
|
||||||
|
layout(std140, binding = 0) uniform CameraData
|
||||||
|
{
|
||||||
|
mat4 u_ViewProjection;
|
||||||
|
mat4 u_View;
|
||||||
|
mat4 u_Projection;
|
||||||
|
vec3 u_CameraPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VertexOutput {
|
||||||
|
vec4 Color;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(location = 0) out VertexOutput Output;
|
||||||
|
layout(location = 1) out flat int v_ObjectID;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
Output.Color = a_Color;
|
||||||
|
v_ObjectID = a_ObjectID;
|
||||||
|
|
||||||
|
gl_Position = u_ViewProjection * vec4(a_Position, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Color;
|
||||||
|
layout(location = 1) out int o_ObjectID;
|
||||||
|
|
||||||
|
struct VertexOutput {
|
||||||
|
vec4 Color;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(location = 0) in VertexOutput Input;
|
||||||
|
layout(location = 1) in flat int v_ObjectID;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
o_Color = Input.Color;
|
||||||
|
o_ObjectID = v_ObjectID;
|
||||||
|
}
|
||||||
225
参考/Fermion/Boson/Resources/Shaders/MaterialPreview.glsl
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
layout(location = 1) in vec3 a_Normal;
|
||||||
|
layout(location = 2) in vec4 a_Color;
|
||||||
|
layout(location = 3) in vec2 a_TexCoords;
|
||||||
|
|
||||||
|
uniform mat4 u_ViewProjection;
|
||||||
|
uniform mat4 u_Model;
|
||||||
|
uniform mat4 u_NormalMatrix;
|
||||||
|
|
||||||
|
out vec3 v_WorldPos;
|
||||||
|
out vec3 v_Normal;
|
||||||
|
out vec2 v_TexCoords;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 worldPos = u_Model * vec4(a_Position, 1.0);
|
||||||
|
v_WorldPos = worldPos.xyz;
|
||||||
|
|
||||||
|
mat3 normalMatrix = mat3(u_NormalMatrix);
|
||||||
|
v_Normal = normalize(normalMatrix * a_Normal);
|
||||||
|
|
||||||
|
v_TexCoords = a_TexCoords;
|
||||||
|
|
||||||
|
gl_Position = u_ViewProjection * worldPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Color;
|
||||||
|
|
||||||
|
in vec3 v_WorldPos;
|
||||||
|
in vec3 v_Normal;
|
||||||
|
in vec2 v_TexCoords;
|
||||||
|
|
||||||
|
const float PI = 3.14159265359;
|
||||||
|
const float F0_NON_METAL = 0.04;
|
||||||
|
const float MIN_ROUGHNESS = 0.045;
|
||||||
|
|
||||||
|
// Camera
|
||||||
|
uniform vec3 u_CameraPosition;
|
||||||
|
|
||||||
|
// Light
|
||||||
|
uniform vec3 u_LightDirection;
|
||||||
|
uniform vec3 u_LightColor;
|
||||||
|
uniform float u_LightIntensity;
|
||||||
|
uniform float u_AmbientIntensity;
|
||||||
|
|
||||||
|
// PBR Material
|
||||||
|
struct Material {
|
||||||
|
vec3 albedo;
|
||||||
|
float metallic;
|
||||||
|
float roughness;
|
||||||
|
float ao;
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform Material u_Material;
|
||||||
|
|
||||||
|
// Textures
|
||||||
|
uniform bool u_UseAlbedoMap;
|
||||||
|
uniform sampler2D u_AlbedoMap;
|
||||||
|
|
||||||
|
uniform bool u_UseNormalMap;
|
||||||
|
uniform sampler2D u_NormalMap;
|
||||||
|
|
||||||
|
uniform bool u_UseMetallicMap;
|
||||||
|
uniform sampler2D u_MetallicMap;
|
||||||
|
|
||||||
|
uniform bool u_UseRoughnessMap;
|
||||||
|
uniform sampler2D u_RoughnessMap;
|
||||||
|
|
||||||
|
uniform bool u_UseAOMap;
|
||||||
|
uniform sampler2D u_AOMap;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Utility Functions
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
float pow5(float x) {
|
||||||
|
float x2 = x * x;
|
||||||
|
return x2 * x2 * x;
|
||||||
|
}
|
||||||
|
|
||||||
|
float saturate(float x) {
|
||||||
|
return clamp(x, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 saturate(vec3 x) {
|
||||||
|
return clamp(x, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// PBR Functions
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Normal Distribution Function (GGX)
|
||||||
|
float D_GGX(float roughness, float NoH) {
|
||||||
|
float a = roughness * roughness;
|
||||||
|
float a2 = a * a;
|
||||||
|
float NoH2 = NoH * NoH;
|
||||||
|
|
||||||
|
float nom = a2;
|
||||||
|
float denom = (NoH2 * (a2 - 1.0) + 1.0);
|
||||||
|
denom = PI * denom * denom;
|
||||||
|
|
||||||
|
return nom / max(denom, 1e-7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smith-GGX Correlated Visibility
|
||||||
|
float V_SmithGGXCorrelated(float roughness, float NoV, float NoL) {
|
||||||
|
float a = roughness * roughness;
|
||||||
|
float a2 = a * a;
|
||||||
|
float lambdaV = NoL * sqrt(NoV * NoV * (1.0 - a2) + a2);
|
||||||
|
float lambdaL = NoV * sqrt(NoL * NoL * (1.0 - a2) + a2);
|
||||||
|
return 0.5 / max(lambdaV + lambdaL, 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fresnel-Schlick
|
||||||
|
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
|
||||||
|
return F0 + (1.0 - F0) * pow5(saturate(1.0 - cosTheta));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Burley Diffuse
|
||||||
|
float Fd_Burley(float roughness, float NoV, float NoL, float LoH) {
|
||||||
|
float f90 = 0.5 + 2.0 * roughness * LoH * LoH;
|
||||||
|
float lightScatter = 1.0 + (f90 - 1.0) * pow5(1.0 - NoL);
|
||||||
|
float viewScatter = 1.0 + (f90 - 1.0) * pow5(1.0 - NoV);
|
||||||
|
return lightScatter * viewScatter * (1.0 / PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get normal from normal map using screen-space derivatives
|
||||||
|
vec3 getNormalFromMap() {
|
||||||
|
if (!u_UseNormalMap) {
|
||||||
|
return normalize(v_Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 tangentNormal = texture(u_NormalMap, v_TexCoords).xyz * 2.0 - 1.0;
|
||||||
|
|
||||||
|
vec3 Q1 = dFdx(v_WorldPos);
|
||||||
|
vec3 Q2 = dFdy(v_WorldPos);
|
||||||
|
vec2 st1 = dFdx(v_TexCoords);
|
||||||
|
vec2 st2 = dFdy(v_TexCoords);
|
||||||
|
|
||||||
|
vec3 N = normalize(v_Normal);
|
||||||
|
vec3 T = normalize(Q1 * st2.t - Q2 * st1.t);
|
||||||
|
vec3 B = normalize(cross(N, T));
|
||||||
|
mat3 TBN = mat3(T, B, N);
|
||||||
|
|
||||||
|
return normalize(TBN * tangentNormal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACES Filmic Tone Mapping
|
||||||
|
vec3 ACESFilm(vec3 x) {
|
||||||
|
float a = 2.51;
|
||||||
|
float b = 0.03;
|
||||||
|
float c = 2.43;
|
||||||
|
float d = 0.59;
|
||||||
|
float e = 0.14;
|
||||||
|
return saturate((x * (a * x + b)) / (x * (c * x + d) + e));
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Get material properties
|
||||||
|
vec3 baseColorLinear = pow(u_Material.albedo, vec3(2.2));
|
||||||
|
vec3 texColorLinear = u_UseAlbedoMap ? pow(texture(u_AlbedoMap, v_TexCoords).rgb, vec3(2.2)) : vec3(1.0);
|
||||||
|
vec3 albedo = texColorLinear * baseColorLinear;
|
||||||
|
|
||||||
|
float metallic = u_UseMetallicMap ? texture(u_MetallicMap, v_TexCoords).r : u_Material.metallic;
|
||||||
|
float roughness = u_UseRoughnessMap ? texture(u_RoughnessMap, v_TexCoords).r : u_Material.roughness;
|
||||||
|
float ao = u_UseAOMap ? texture(u_AOMap, v_TexCoords).r : u_Material.ao;
|
||||||
|
|
||||||
|
// Clamp roughness
|
||||||
|
roughness = max(roughness, MIN_ROUGHNESS);
|
||||||
|
|
||||||
|
// Get normal
|
||||||
|
vec3 N = getNormalFromMap();
|
||||||
|
vec3 V = normalize(u_CameraPosition - v_WorldPos);
|
||||||
|
float NoV = max(dot(N, V), 1e-4);
|
||||||
|
|
||||||
|
// Calculate F0
|
||||||
|
vec3 F0 = mix(vec3(F0_NON_METAL), albedo, metallic);
|
||||||
|
|
||||||
|
// Reflectance equation
|
||||||
|
vec3 Lo = vec3(0.0);
|
||||||
|
|
||||||
|
// Main directional light
|
||||||
|
{
|
||||||
|
vec3 L = normalize(-u_LightDirection);
|
||||||
|
vec3 H = normalize(V + L);
|
||||||
|
|
||||||
|
float NoL = max(dot(N, L), 0.0);
|
||||||
|
float NoH = saturate(dot(N, H));
|
||||||
|
float LoH = saturate(dot(L, H));
|
||||||
|
|
||||||
|
// Cook-Torrance BRDF
|
||||||
|
float D = D_GGX(roughness, NoH);
|
||||||
|
float V_term = V_SmithGGXCorrelated(roughness, NoV, NoL);
|
||||||
|
vec3 F = fresnelSchlick(LoH, F0);
|
||||||
|
|
||||||
|
vec3 specularBRDF = (D * V_term) * F;
|
||||||
|
|
||||||
|
// Burley diffuse
|
||||||
|
vec3 kS = F;
|
||||||
|
vec3 kD = (vec3(1.0) - kS) * (1.0 - metallic);
|
||||||
|
vec3 diffuseBRDF = kD * albedo * Fd_Burley(roughness, NoV, NoL, LoH);
|
||||||
|
|
||||||
|
vec3 radiance = u_LightColor * u_LightIntensity;
|
||||||
|
Lo += (diffuseBRDF + specularBRDF) * radiance * NoL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ambient lighting
|
||||||
|
vec3 ambient = vec3(u_AmbientIntensity) * albedo * ao;
|
||||||
|
|
||||||
|
vec3 color = ambient + Lo;
|
||||||
|
|
||||||
|
// HDR tone mapping (ACES Filmic)
|
||||||
|
color = ACESFilm(color);
|
||||||
|
|
||||||
|
// Gamma correction
|
||||||
|
color = pow(color, vec3(1.0 / 2.2));
|
||||||
|
|
||||||
|
o_Color = vec4(color, 1.0);
|
||||||
|
}
|
||||||
272
参考/Fermion/Boson/Resources/Shaders/Mesh.glsl
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
layout(location = 1) in vec3 a_Normal;
|
||||||
|
layout(location = 2) in vec4 a_Color;
|
||||||
|
layout(location = 3) in vec2 a_TexCoords;
|
||||||
|
|
||||||
|
// Camera uniform buffer (binding = 0)
|
||||||
|
layout(std140, binding = 0) uniform CameraData
|
||||||
|
{
|
||||||
|
mat4 u_ViewProjection;
|
||||||
|
mat4 u_View;
|
||||||
|
mat4 u_Projection;
|
||||||
|
vec3 u_CameraPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Model uniform buffer (binding = 1)
|
||||||
|
layout(std140, binding = 1) uniform ModelData
|
||||||
|
{
|
||||||
|
mat4 u_Model;
|
||||||
|
mat4 u_NormalMatrix;
|
||||||
|
int u_ObjectID;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Light uniform buffer (binding = 2)
|
||||||
|
layout(std140, binding = 2) uniform LightData
|
||||||
|
{
|
||||||
|
mat4 u_LightSpaceMatrix;
|
||||||
|
vec3 u_DirLightDirection;
|
||||||
|
float u_DirLightIntensity;
|
||||||
|
vec3 u_DirLightColor;
|
||||||
|
float _lightPadding0;
|
||||||
|
float u_ShadowBias;
|
||||||
|
float u_ShadowSoftness;
|
||||||
|
int u_EnableShadows;
|
||||||
|
int u_NumDirLights;
|
||||||
|
float u_AmbientIntensity;
|
||||||
|
int u_NumPointLights;
|
||||||
|
int u_NumSpotLights;
|
||||||
|
};
|
||||||
|
|
||||||
|
out vec3 v_WorldPos;
|
||||||
|
out vec3 v_Normal;
|
||||||
|
out vec4 v_Color;
|
||||||
|
out vec2 v_TexCoords;
|
||||||
|
out vec4 v_FragPosLightSpace;
|
||||||
|
flat out int v_ObjectID;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 worldPos = u_Model * vec4(a_Position, 1.0);
|
||||||
|
v_WorldPos = worldPos.xyz;
|
||||||
|
|
||||||
|
// Use precomputed normal matrix from UBO
|
||||||
|
v_Normal = mat3(u_NormalMatrix) * a_Normal;
|
||||||
|
v_Color = a_Color;
|
||||||
|
v_TexCoords = a_TexCoords;
|
||||||
|
v_FragPosLightSpace = u_LightSpaceMatrix * worldPos;
|
||||||
|
v_ObjectID = u_ObjectID;
|
||||||
|
|
||||||
|
gl_Position = u_ViewProjection * worldPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Color;
|
||||||
|
layout(location = 1) out int o_ObjectID;
|
||||||
|
|
||||||
|
in vec3 v_WorldPos;
|
||||||
|
in vec3 v_Normal;
|
||||||
|
in vec4 v_Color;
|
||||||
|
in vec2 v_TexCoords;
|
||||||
|
in vec4 v_FragPosLightSpace;
|
||||||
|
flat in int v_ObjectID;
|
||||||
|
|
||||||
|
#define MAX_DIR_LIGHTS 4
|
||||||
|
#define MAX_POINT_LIGHTS 16
|
||||||
|
#define MAX_SPOT_LIGHTS 16
|
||||||
|
|
||||||
|
// Light uniform buffer (binding = 2)
|
||||||
|
layout(std140, binding = 2) uniform LightData
|
||||||
|
{
|
||||||
|
mat4 u_LightSpaceMatrix;
|
||||||
|
vec3 u_DirLightDirection;
|
||||||
|
float u_DirLightIntensity;
|
||||||
|
vec3 u_DirLightColor;
|
||||||
|
float _lightPadding0;
|
||||||
|
float u_ShadowBias;
|
||||||
|
float u_ShadowSoftness;
|
||||||
|
int u_EnableShadows;
|
||||||
|
int u_NumDirLights;
|
||||||
|
float u_AmbientIntensity;
|
||||||
|
int u_NumPointLights;
|
||||||
|
int u_NumSpotLights;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DirectionalLight {
|
||||||
|
vec3 direction;
|
||||||
|
vec3 color;
|
||||||
|
float intensity;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PointLight {
|
||||||
|
vec3 position;
|
||||||
|
vec3 color;
|
||||||
|
float intensity;
|
||||||
|
float range;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SpotLight {
|
||||||
|
vec3 position;
|
||||||
|
vec3 direction;
|
||||||
|
|
||||||
|
vec3 color;
|
||||||
|
float intensity;
|
||||||
|
|
||||||
|
float range;
|
||||||
|
float innerConeAngle;
|
||||||
|
float outerConeAngle;
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform int u_DirLightCount;
|
||||||
|
uniform DirectionalLight u_DirLights[MAX_DIR_LIGHTS];
|
||||||
|
|
||||||
|
uniform int u_PointLightCount;
|
||||||
|
uniform PointLight u_PointLights[MAX_POINT_LIGHTS];
|
||||||
|
|
||||||
|
uniform int u_SpotLightCount;
|
||||||
|
uniform SpotLight u_SpotLights[MAX_SPOT_LIGHTS];
|
||||||
|
|
||||||
|
uniform bool u_UseTexture;
|
||||||
|
uniform sampler2D u_Texture;
|
||||||
|
|
||||||
|
uniform vec4 u_Kd; // diffuse
|
||||||
|
uniform vec4 u_Ka; // ambient
|
||||||
|
uniform bool u_FlipUV;
|
||||||
|
|
||||||
|
// Shadow mapping
|
||||||
|
uniform sampler2D u_ShadowMap;
|
||||||
|
|
||||||
|
float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir)
|
||||||
|
{
|
||||||
|
// Perspective divide
|
||||||
|
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
||||||
|
|
||||||
|
// Transform to [0,1] range
|
||||||
|
projCoords = projCoords * 0.5 + 0.5;
|
||||||
|
|
||||||
|
// If outside shadow map bounds, assume no shadow
|
||||||
|
if(projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 || projCoords.y < 0.0 || projCoords.y > 1.0)
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
// Get closest depth value from light's perspective
|
||||||
|
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
|
||||||
|
float currentDepth = projCoords.z;
|
||||||
|
|
||||||
|
// Calculate bias based on surface angle
|
||||||
|
float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1);
|
||||||
|
|
||||||
|
// PCF (Percentage Closer Filtering) for soft shadows
|
||||||
|
float shadow = 0.0;
|
||||||
|
vec2 texelSize = 1.0 / textureSize(u_ShadowMap, 0);
|
||||||
|
int pcfRange = int(u_ShadowSoftness);
|
||||||
|
int sampleCount = 0;
|
||||||
|
|
||||||
|
for(int x = -pcfRange; x <= pcfRange; ++x)
|
||||||
|
{
|
||||||
|
for(int y = -pcfRange; y <= pcfRange; ++y)
|
||||||
|
{
|
||||||
|
float pcfDepth = texture(u_ShadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
|
||||||
|
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
|
||||||
|
sampleCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shadow /= float(sampleCount);
|
||||||
|
|
||||||
|
return shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec3 normal = normalize(v_Normal);
|
||||||
|
|
||||||
|
vec2 uv = v_TexCoords;
|
||||||
|
if(u_FlipUV)
|
||||||
|
uv.y = 1.0 - uv.y;
|
||||||
|
|
||||||
|
vec3 baseColor;
|
||||||
|
if(u_UseTexture)
|
||||||
|
baseColor = texture(u_Texture, uv).rgb;
|
||||||
|
else
|
||||||
|
baseColor = u_Kd.rgb;
|
||||||
|
|
||||||
|
// Ambient
|
||||||
|
vec3 result = u_Ka.rgb * baseColor;
|
||||||
|
|
||||||
|
// Main directional light with shadow
|
||||||
|
vec3 dirLightDir = normalize(-u_DirLightDirection);
|
||||||
|
float NdotL = max(dot(normal, dirLightDir), 0.0);
|
||||||
|
float shadow = 0.0;
|
||||||
|
if (u_EnableShadows != 0) {
|
||||||
|
shadow = calculateShadow(v_FragPosLightSpace, normal, dirLightDir);
|
||||||
|
}
|
||||||
|
result += u_DirLightColor * u_DirLightIntensity * NdotL * (1.0 - shadow) * baseColor;
|
||||||
|
|
||||||
|
// Additional directional lights (without shadow)
|
||||||
|
for(int i = 0; i < u_DirLightCount; i++)
|
||||||
|
{
|
||||||
|
DirectionalLight light = u_DirLights[i];
|
||||||
|
vec3 lightDir = normalize(-light.direction);
|
||||||
|
float NdotL = max(dot(normal, lightDir), 0.0);
|
||||||
|
result += light.color * light.intensity * NdotL * baseColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Point lights
|
||||||
|
for(int i = 0; i < u_PointLightCount; i++)
|
||||||
|
{
|
||||||
|
PointLight light = u_PointLights[i];
|
||||||
|
|
||||||
|
vec3 L = light.position - v_WorldPos;
|
||||||
|
float distance = length(L);
|
||||||
|
if(distance > light.range)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
vec3 lightDir = normalize(L);
|
||||||
|
float NdotL = max(dot(normal, lightDir), 0.0);
|
||||||
|
|
||||||
|
float attenuation = 1.0 - distance / light.range;
|
||||||
|
|
||||||
|
result += light.color *
|
||||||
|
light.intensity *
|
||||||
|
NdotL *
|
||||||
|
attenuation *
|
||||||
|
baseColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spot lights
|
||||||
|
for(int i = 0; i < u_SpotLightCount; i++)
|
||||||
|
{
|
||||||
|
SpotLight light = u_SpotLights[i];
|
||||||
|
|
||||||
|
vec3 L = light.position - v_WorldPos;
|
||||||
|
float distance = length(L);
|
||||||
|
if(distance > light.range)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
vec3 lightDir = normalize(L);
|
||||||
|
|
||||||
|
float theta = dot(lightDir, normalize(light.direction));
|
||||||
|
float innerCos = light.innerConeAngle;
|
||||||
|
float outerCos = light.outerConeAngle;
|
||||||
|
|
||||||
|
if(theta < outerCos)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float spot = clamp((theta - outerCos) / (innerCos - outerCos), 0.0, 1.0);
|
||||||
|
|
||||||
|
float NdotL = max(dot(normal, lightDir), 0.0);
|
||||||
|
float attenuation = 1.0 - distance / light.range;
|
||||||
|
|
||||||
|
result += light.color *
|
||||||
|
light.intensity *
|
||||||
|
NdotL *
|
||||||
|
attenuation *
|
||||||
|
spot *
|
||||||
|
baseColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
o_Color = vec4(result, u_Kd.a);
|
||||||
|
o_ObjectID = v_ObjectID;
|
||||||
|
}
|
||||||
45
参考/Fermion/Boson/Resources/Shaders/Outline.glsl
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
layout(location = 1) in vec3 a_Normal;
|
||||||
|
|
||||||
|
uniform mat4 u_Model;
|
||||||
|
uniform mat4 u_View;
|
||||||
|
uniform mat4 u_Projection;
|
||||||
|
|
||||||
|
uniform float u_OutlineWidth;
|
||||||
|
uniform float u_Epsilon;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
vec4 viewPos = u_View * u_Model * vec4(a_Position, 1.0);
|
||||||
|
|
||||||
|
mat3 normalMatrix = mat3(transpose(inverse(u_View * u_Model)));
|
||||||
|
vec3 viewNormal = normalize(normalMatrix * a_Normal);
|
||||||
|
|
||||||
|
float ndotv = abs(dot(viewNormal, vec3(0.0, 0.0, -1.0)));
|
||||||
|
float outlineFactor = 1.0 - smoothstep(0.0, u_Epsilon, ndotv);
|
||||||
|
|
||||||
|
vec3 expandNormal = normalize(vec3(viewNormal.xy, 0.0));
|
||||||
|
float depth = -viewPos.z;
|
||||||
|
float depthScale = clamp(depth * 0.02, 0.5, 2.0);
|
||||||
|
|
||||||
|
viewPos.xyz += expandNormal * u_OutlineWidth * depthScale * outlineFactor;
|
||||||
|
|
||||||
|
gl_Position = u_Projection * viewPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Color;
|
||||||
|
layout(location = 1) out int o_ObjectID;
|
||||||
|
|
||||||
|
uniform vec4 u_OutlineColor = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
uniform int u_ObjectID = -1;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
o_Color = u_OutlineColor;
|
||||||
|
o_ObjectID = u_ObjectID;
|
||||||
|
}
|
||||||
768
参考/Fermion/Boson/Resources/Shaders/PBRMesh.glsl
Normal file
@@ -0,0 +1,768 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
layout(location = 1) in vec3 a_Normal;
|
||||||
|
layout(location = 2) in vec4 a_Color;
|
||||||
|
layout(location = 3) in vec2 a_TexCoords;
|
||||||
|
layout(location = 4) in vec3 a_Tangent;
|
||||||
|
layout(location = 5) in vec3 a_Bitangent;
|
||||||
|
|
||||||
|
// Camera uniform buffer (binding = 0)
|
||||||
|
layout(std140, binding = 0) uniform CameraData
|
||||||
|
{
|
||||||
|
mat4 u_ViewProjection;
|
||||||
|
mat4 u_View;
|
||||||
|
mat4 u_Projection;
|
||||||
|
vec3 u_CameraPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Model uniform buffer (binding = 1)
|
||||||
|
layout(std140, binding = 1) uniform ModelData
|
||||||
|
{
|
||||||
|
mat4 u_Model;
|
||||||
|
mat4 u_NormalMatrix;
|
||||||
|
int u_ObjectID;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Light uniform buffer (binding = 2)
|
||||||
|
layout(std140, binding = 2) uniform LightData
|
||||||
|
{
|
||||||
|
mat4 u_LightSpaceMatrix;
|
||||||
|
vec3 u_DirLightDirection;
|
||||||
|
float u_DirLightIntensity;
|
||||||
|
vec3 u_DirLightColor;
|
||||||
|
float _lightPadding0;
|
||||||
|
float u_ShadowBias;
|
||||||
|
float u_ShadowSoftness;
|
||||||
|
int u_EnableShadows;
|
||||||
|
int u_NumDirLights;
|
||||||
|
float u_AmbientIntensity;
|
||||||
|
int u_NumPointLights;
|
||||||
|
int u_NumSpotLights;
|
||||||
|
};
|
||||||
|
|
||||||
|
out vec3 v_WorldPos;
|
||||||
|
out vec3 v_Normal;
|
||||||
|
out vec4 v_Color;
|
||||||
|
out vec2 v_TexCoords;
|
||||||
|
out vec4 v_FragPosLightSpace;
|
||||||
|
out mat3 v_TBN;
|
||||||
|
flat out int v_ObjectID;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 worldPos = u_Model * vec4(a_Position, 1.0);
|
||||||
|
v_WorldPos = worldPos.xyz;
|
||||||
|
|
||||||
|
// Use precomputed normal matrix from UBO
|
||||||
|
mat3 normalMatrix = mat3(u_NormalMatrix);
|
||||||
|
v_Normal = normalize(normalMatrix * a_Normal);
|
||||||
|
|
||||||
|
// Build TBN matrix for normal mapping
|
||||||
|
vec3 T = normalize(normalMatrix * a_Tangent);
|
||||||
|
vec3 N = v_Normal;
|
||||||
|
// Re-orthogonalize T with respect to N
|
||||||
|
T = normalize(T - dot(T, N) * N);
|
||||||
|
vec3 B = cross(N, T);
|
||||||
|
v_TBN = mat3(T, B, N);
|
||||||
|
|
||||||
|
v_Color = a_Color;
|
||||||
|
v_TexCoords = a_TexCoords;
|
||||||
|
v_FragPosLightSpace = u_LightSpaceMatrix * worldPos;
|
||||||
|
v_ObjectID = u_ObjectID;
|
||||||
|
|
||||||
|
gl_Position = u_ViewProjection * worldPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Color;
|
||||||
|
layout(location = 1) out int o_ObjectID;
|
||||||
|
|
||||||
|
in vec3 v_WorldPos;
|
||||||
|
in vec3 v_Normal;
|
||||||
|
in vec4 v_Color;
|
||||||
|
in vec2 v_TexCoords;
|
||||||
|
in vec4 v_FragPosLightSpace;
|
||||||
|
in mat3 v_TBN;
|
||||||
|
flat in int v_ObjectID;
|
||||||
|
|
||||||
|
const float PI = 3.14159265359;
|
||||||
|
const float HALF_PI = 1.570796327;
|
||||||
|
const float F0_NON_METAL = 0.04;
|
||||||
|
const float MIN_ROUGHNESS = 0.045; // 避免高光过于尖锐
|
||||||
|
|
||||||
|
#define MAX_DIR_LIGHTS 4
|
||||||
|
#define MAX_POINT_LIGHTS 16
|
||||||
|
#define MAX_SPOT_LIGHTS 16
|
||||||
|
|
||||||
|
// 优化的数学函数
|
||||||
|
float pow5(float x) {
|
||||||
|
float x2 = x * x;
|
||||||
|
return x2 * x2 * x;
|
||||||
|
}
|
||||||
|
|
||||||
|
float sq(float x) {
|
||||||
|
return x * x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 饱和函数
|
||||||
|
float saturate(float x) {
|
||||||
|
return clamp(x, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 saturate(vec3 x) {
|
||||||
|
return clamp(x, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Camera uniform buffer (binding = 0)
|
||||||
|
layout(std140, binding = 0) uniform CameraData
|
||||||
|
{
|
||||||
|
mat4 u_ViewProjection;
|
||||||
|
mat4 u_View;
|
||||||
|
mat4 u_Projection;
|
||||||
|
vec3 u_CameraPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Light uniform buffer (binding = 2)
|
||||||
|
layout(std140, binding = 2) uniform LightData
|
||||||
|
{
|
||||||
|
mat4 u_LightSpaceMatrix;
|
||||||
|
vec3 u_DirLightDirection;
|
||||||
|
float u_DirLightIntensity;
|
||||||
|
vec3 u_DirLightColor;
|
||||||
|
float _lightPadding0;
|
||||||
|
float u_ShadowBias;
|
||||||
|
float u_ShadowSoftness;
|
||||||
|
int u_EnableShadows;
|
||||||
|
int u_NumDirLights;
|
||||||
|
float u_AmbientIntensity;
|
||||||
|
int u_NumPointLights;
|
||||||
|
int u_NumSpotLights;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 光源结构
|
||||||
|
struct DirectionalLight {
|
||||||
|
vec3 direction;
|
||||||
|
vec3 color;
|
||||||
|
float intensity;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PointLight {
|
||||||
|
vec3 position;
|
||||||
|
vec3 color;
|
||||||
|
float intensity;
|
||||||
|
float range;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SpotLight {
|
||||||
|
vec3 position;
|
||||||
|
vec3 direction;
|
||||||
|
|
||||||
|
vec3 color;
|
||||||
|
float intensity;
|
||||||
|
|
||||||
|
float range;
|
||||||
|
float innerConeAngle;
|
||||||
|
float outerConeAngle;
|
||||||
|
};
|
||||||
|
|
||||||
|
// PBR材质参数
|
||||||
|
struct Material {
|
||||||
|
vec3 albedo;
|
||||||
|
float metallic;
|
||||||
|
float roughness;
|
||||||
|
float ao;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 高级材质参数
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Clear Coat (透明涂层) - 用于汽车漆、涂漆表面等
|
||||||
|
uniform bool u_UseClearCoat;
|
||||||
|
uniform float u_ClearCoat; // 涂层强度 [0, 1]
|
||||||
|
uniform float u_ClearCoatRoughness; // 涂层粗糙度 [0, 1]
|
||||||
|
|
||||||
|
// Anisotropic (各向异性) - 用于拉丝金属、头发等
|
||||||
|
uniform bool u_UseAnisotropic;
|
||||||
|
uniform float u_Anisotropy; // 各向异性强度 [-1, 1],负值为垂直方向
|
||||||
|
uniform vec3 u_AnisotropyDirection; // 各向异性方向(切线空间)
|
||||||
|
|
||||||
|
// Subsurface Scattering (次表面散射) - 用于皮肤、蜡、玉石等
|
||||||
|
uniform bool u_UseSubsurface;
|
||||||
|
uniform vec3 u_SubsurfaceColor; // 次表面散射颜色
|
||||||
|
uniform float u_SubsurfacePower; // 散射指数
|
||||||
|
uniform float u_Thickness; // 材质厚度 [0, 1],0=完全不透光
|
||||||
|
|
||||||
|
// Uniforms
|
||||||
|
uniform int u_DirLightCount;
|
||||||
|
uniform DirectionalLight u_DirLights[MAX_DIR_LIGHTS];
|
||||||
|
|
||||||
|
uniform int u_PointLightCount;
|
||||||
|
uniform PointLight u_PointLights[MAX_POINT_LIGHTS];
|
||||||
|
|
||||||
|
uniform int u_SpotLightCount;
|
||||||
|
uniform SpotLight u_SpotLights[MAX_SPOT_LIGHTS];
|
||||||
|
|
||||||
|
uniform Material u_Material;
|
||||||
|
|
||||||
|
// 纹理
|
||||||
|
uniform bool u_UseAlbedoMap;
|
||||||
|
uniform sampler2D u_AlbedoMap;
|
||||||
|
|
||||||
|
uniform bool u_UseNormalMap;
|
||||||
|
uniform sampler2D u_NormalMap;
|
||||||
|
uniform float u_NormalStrength;
|
||||||
|
uniform float u_ToksvigStrength;
|
||||||
|
|
||||||
|
uniform bool u_UseMetallicMap;
|
||||||
|
uniform sampler2D u_MetallicMap;
|
||||||
|
|
||||||
|
uniform bool u_UseRoughnessMap;
|
||||||
|
uniform sampler2D u_RoughnessMap;
|
||||||
|
|
||||||
|
uniform bool u_UseAOMap;
|
||||||
|
uniform sampler2D u_AOMap;
|
||||||
|
|
||||||
|
uniform bool u_FlipUV;
|
||||||
|
|
||||||
|
// Shadow mapping
|
||||||
|
uniform sampler2D u_ShadowMap;
|
||||||
|
|
||||||
|
// IBL
|
||||||
|
uniform bool u_UseIBL;
|
||||||
|
uniform samplerCube u_IrradianceMap;
|
||||||
|
uniform samplerCube u_PrefilterMap;
|
||||||
|
uniform sampler2D u_BRDFLT;
|
||||||
|
uniform float u_PrefilterMaxLOD;
|
||||||
|
|
||||||
|
|
||||||
|
// GGX 法线分布函数 - 标准实现,使用 roughness² 参数化
|
||||||
|
// Walter et al. 2007, "Microfacet Models for Refraction through Rough Surfaces"
|
||||||
|
float D_GGX(float roughness, float NoH) {
|
||||||
|
float a = roughness * roughness;
|
||||||
|
float a2 = a * a;
|
||||||
|
float NoH2 = NoH * NoH;
|
||||||
|
|
||||||
|
float nom = a2;
|
||||||
|
float denom = (NoH2 * (a2 - 1.0) + 1.0);
|
||||||
|
denom = PI * denom * denom;
|
||||||
|
|
||||||
|
return nom / max(denom, 1e-7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smith-GGX Correlated Visibility 函数 - 比 Schlick 近似更精确
|
||||||
|
// Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs"
|
||||||
|
float V_SmithGGXCorrelated(float roughness, float NoV, float NoL) {
|
||||||
|
float a = roughness * roughness;
|
||||||
|
float a2 = a * a;
|
||||||
|
float lambdaV = NoL * sqrt(NoV * NoV * (1.0 - a2) + a2);
|
||||||
|
float lambdaL = NoV * sqrt(NoL * NoL * (1.0 - a2) + a2);
|
||||||
|
return 0.5 / max(lambdaV + lambdaL, 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 快速近似版本 - 性能更好,质量略低
|
||||||
|
// Hammon 2017, "PBR Diffuse Lighting for GGX+Smith Microsurfaces"
|
||||||
|
float V_SmithGGXCorrelated_Fast(float roughness, float NoV, float NoL) {
|
||||||
|
float a = roughness * roughness;
|
||||||
|
float v = 0.5 / max(mix(2.0 * NoL * NoV, NoL + NoV, a), 1e-5);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schlick Fresnel - 使用优化的 pow5
|
||||||
|
vec3 F_Schlick(const vec3 f0, float f90, float VoH) {
|
||||||
|
return f0 + (vec3(f90) - f0) * pow5(1.0 - VoH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fresnel方程 - 标准版本,f90 = 1.0
|
||||||
|
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
|
||||||
|
return F0 + (1.0 - F0) * pow5(saturate(1.0 - cosTheta));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fresnel方程带粗糙度 - 用于IBL
|
||||||
|
vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) {
|
||||||
|
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow5(saturate(1.0 - cosTheta));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lambert 漫反射
|
||||||
|
float Fd_Lambert() {
|
||||||
|
return 1.0 / PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Burley 漫反射 - 比 Lambert 更真实,考虑粗糙度
|
||||||
|
// Burley 2012, "Physically-Based Shading at Disney"
|
||||||
|
float Fd_Burley(float roughness, float NoV, float NoL, float LoH) {
|
||||||
|
float f90 = 0.5 + 2.0 * roughness * LoH * LoH;
|
||||||
|
float lightScatter = 1.0 + (f90 - 1.0) * pow5(1.0 - NoL);
|
||||||
|
float viewScatter = 1.0 + (f90 - 1.0) * pow5(1.0 - NoV);
|
||||||
|
return lightScatter * viewScatter * (1.0 / PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACES Filmic Tone Mapping - 更真实的色调映射
|
||||||
|
// Krzysztof Narkowicz 2015
|
||||||
|
vec3 ACESFilm(vec3 x) {
|
||||||
|
float a = 2.51;
|
||||||
|
float b = 0.03;
|
||||||
|
float c = 2.43;
|
||||||
|
float d = 0.59;
|
||||||
|
float e = 0.14;
|
||||||
|
return saturate((x * (a * x + b)) / (x * (c * x + d) + e));
|
||||||
|
}
|
||||||
|
|
||||||
|
float computeSpecularOcclusion(float NoV, float ao, float roughness) {
|
||||||
|
return clamp(pow(NoV + ao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ao, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Clear Coat BRDF 函数
|
||||||
|
// 透明涂层使用简化的 Cook-Torrance,IOR 固定为 1.5 (聚氨酯)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Clear Coat 专用的 Kelemen Visibility 函数 - 更高效
|
||||||
|
float V_Kelemen(float LoH) {
|
||||||
|
return 0.25 / max(LoH * LoH, 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear Coat Fresnel - 固定 F0 = 0.04 (IOR 1.5)
|
||||||
|
float F_Schlick_ClearCoat(float VoH) {
|
||||||
|
float f0 = 0.04;
|
||||||
|
return f0 + (1.0 - f0) * pow5(1.0 - VoH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear Coat Lobe
|
||||||
|
float clearCoatLobe(float clearCoatRoughness, float NoH, float LoH, float clearCoat, out float Fcc) {
|
||||||
|
float D = D_GGX(clearCoatRoughness, NoH);
|
||||||
|
float V = V_Kelemen(LoH);
|
||||||
|
float F = F_Schlick_ClearCoat(LoH) * clearCoat;
|
||||||
|
|
||||||
|
Fcc = F;
|
||||||
|
return D * V * F;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Anisotropic BRDF 函数
|
||||||
|
// 各向异性高光 - 用于拉丝金属、头发、唱片等
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Anisotropic GGX NDF
|
||||||
|
// Burley 2012, "Physically-Based Shading at Disney"
|
||||||
|
float D_GGX_Anisotropic(float at, float ab, float ToH, float BoH, float NoH) {
|
||||||
|
float a2 = at * ab;
|
||||||
|
vec3 d = vec3(ab * ToH, at * BoH, a2 * NoH);
|
||||||
|
float d2 = dot(d, d);
|
||||||
|
float b2 = a2 / max(d2, 1e-7);
|
||||||
|
return a2 * b2 * b2 * (1.0 / PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anisotropic Smith-GGX Visibility
|
||||||
|
float V_SmithGGXCorrelated_Anisotropic(float at, float ab, float ToV, float BoV,
|
||||||
|
float ToL, float BoL, float NoV, float NoL) {
|
||||||
|
float lambdaV = NoL * length(vec3(at * ToV, ab * BoV, NoV));
|
||||||
|
float lambdaL = NoV * length(vec3(at * ToL, ab * BoL, NoL));
|
||||||
|
return 0.5 / max(lambdaV + lambdaL, 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anisotropic Specular Lobe
|
||||||
|
vec3 anisotropicLobe(vec3 N, vec3 V, vec3 L, vec3 H, vec3 T, vec3 B,
|
||||||
|
float roughness, float anisotropy, vec3 F0, out vec3 kS, out float NoL_out) {
|
||||||
|
|
||||||
|
float NoV = max(dot(N, V), 1e-4);
|
||||||
|
float NoL = max(dot(N, L), 0.0);
|
||||||
|
float NoH = saturate(dot(N, H));
|
||||||
|
float LoH = saturate(dot(L, H));
|
||||||
|
|
||||||
|
float ToV = dot(T, V);
|
||||||
|
float BoV = dot(B, V);
|
||||||
|
float ToL = dot(T, L);
|
||||||
|
float BoL = dot(B, L);
|
||||||
|
float ToH = dot(T, H);
|
||||||
|
float BoH = dot(B, H);
|
||||||
|
|
||||||
|
// 计算各向异性粗糙度
|
||||||
|
// Kulla 2017, "Revisiting Physically Based Shading at Imageworks"
|
||||||
|
float at = max(roughness * (1.0 + anisotropy), MIN_ROUGHNESS);
|
||||||
|
float ab = max(roughness * (1.0 - anisotropy), MIN_ROUGHNESS);
|
||||||
|
|
||||||
|
float D = D_GGX_Anisotropic(at, ab, ToH, BoH, NoH);
|
||||||
|
float V_term = V_SmithGGXCorrelated_Anisotropic(at, ab, ToV, BoV, ToL, BoL, NoV, NoL);
|
||||||
|
vec3 F = fresnelSchlick(LoH, F0);
|
||||||
|
|
||||||
|
kS = F;
|
||||||
|
NoL_out = NoL;
|
||||||
|
|
||||||
|
return (D * V_term) * F;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Subsurface Scattering BRDF 函数
|
||||||
|
// 次表面散射 - 用于皮肤、蜡、玉石、树叶等半透明材质
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Wrap Diffuse - 能量守恒的包裹漫反射,模拟次表面散射
|
||||||
|
float Fd_Wrap(float NoL, float w) {
|
||||||
|
return saturate((NoL + w) / sq(1.0 + w));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subsurface Scattering Lobe
|
||||||
|
// 基于 Filament 的简化 BTDF 实现
|
||||||
|
vec3 subsurfaceLobe(vec3 N, vec3 V, vec3 L, vec3 subsurfaceColor,
|
||||||
|
float subsurfacePower, float thickness, vec3 diffuseColor) {
|
||||||
|
|
||||||
|
float NoL = dot(N, L);
|
||||||
|
|
||||||
|
// Forward scattering - 使用球面高斯近似
|
||||||
|
// 模拟光线穿透材质后从背面散射出来
|
||||||
|
float scatterVoH = saturate(dot(V, -L));
|
||||||
|
float forwardScatter = exp2(scatterVoH * subsurfacePower - subsurfacePower);
|
||||||
|
|
||||||
|
// Back scatter - 简化的背面散射
|
||||||
|
float backScatter = saturate(NoL * thickness + (1.0 - thickness)) * 0.5;
|
||||||
|
|
||||||
|
// 组合散射
|
||||||
|
float subsurface = mix(backScatter, 1.0, forwardScatter) * (1.0 - thickness);
|
||||||
|
|
||||||
|
return subsurfaceColor * subsurface * diffuseColor * (1.0 / PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 阴影计算
|
||||||
|
float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) {
|
||||||
|
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
||||||
|
projCoords = projCoords * 0.5 + 0.5;
|
||||||
|
|
||||||
|
if(projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 ||
|
||||||
|
projCoords.y < 0.0 || projCoords.y > 1.0)
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
|
||||||
|
float currentDepth = projCoords.z;
|
||||||
|
|
||||||
|
float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1);
|
||||||
|
|
||||||
|
float shadow = 0.0;
|
||||||
|
vec2 texelSize = 1.0 / textureSize(u_ShadowMap, 0);
|
||||||
|
int pcfRange = int(u_ShadowSoftness);
|
||||||
|
int sampleCount = 0;
|
||||||
|
|
||||||
|
for(int x = -pcfRange; x <= pcfRange; ++x) {
|
||||||
|
for(int y = -pcfRange; y <= pcfRange; ++y) {
|
||||||
|
float pcfDepth = texture(u_ShadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
|
||||||
|
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
|
||||||
|
sampleCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shadow /= float(sampleCount);
|
||||||
|
|
||||||
|
return shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取法线使用导数计算TBN
|
||||||
|
vec3 getNormalFromMap(out float normalVariance) {
|
||||||
|
if(!u_UseNormalMap) {
|
||||||
|
normalVariance = 0.0;
|
||||||
|
return normalize(v_Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 采样切线空间法线
|
||||||
|
vec3 tangentNormal = texture(u_NormalMap, v_TexCoords).xyz * 2.0 - 1.0;
|
||||||
|
|
||||||
|
// 构建 TBN(屏幕导数方式,避免切线错误)
|
||||||
|
vec3 Q1 = dFdx(v_WorldPos);
|
||||||
|
vec3 Q2 = dFdy(v_WorldPos);
|
||||||
|
vec2 st1 = dFdx(v_TexCoords);
|
||||||
|
vec2 st2 = dFdy(v_TexCoords);
|
||||||
|
|
||||||
|
vec3 N = normalize(v_Normal);
|
||||||
|
vec3 T = normalize(Q1 * st2.t - Q2 * st1.t);
|
||||||
|
vec3 B = normalize(cross(N, T));
|
||||||
|
mat3 TBN = mat3(T, B, N);
|
||||||
|
|
||||||
|
// 世界空间法线
|
||||||
|
vec3 worldNormal = normalize(TBN * tangentNormal);
|
||||||
|
|
||||||
|
// 计算法线变化率(用于 Specular AA)
|
||||||
|
vec3 dndx = dFdx(worldNormal);
|
||||||
|
vec3 dndy = dFdy(worldNormal);
|
||||||
|
normalVariance = max(0.0, dot(dndx, dndx) + dot(dndy, dndy));
|
||||||
|
|
||||||
|
return worldNormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lambert diffuse - 使用优化版本
|
||||||
|
vec3 LambertDiffuse(vec3 kS, vec3 albedo, float metallic) {
|
||||||
|
vec3 kD = (vec3(1.0) - kS) * (1.0 - metallic);
|
||||||
|
return kD * albedo * Fd_Lambert();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Burley diffuse - 更真实的漫反射
|
||||||
|
vec3 BurleyDiffuse(vec3 kS, vec3 albedo, float metallic, float roughness, float NoV, float NoL, float LoH) {
|
||||||
|
vec3 kD = (vec3(1.0) - kS) * (1.0 - metallic);
|
||||||
|
return kD * albedo * Fd_Burley(roughness, NoV, NoL, LoH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 综合着色函数 - 支持 Clear Coat, Anisotropic, Subsurface
|
||||||
|
// ============================================================================
|
||||||
|
vec3 evaluateLighting(vec3 N, vec3 V, vec3 L, vec3 T, vec3 B,
|
||||||
|
vec3 albedo, float roughness, float metallic, vec3 F0,
|
||||||
|
float NoV, vec3 radiance, float occlusion) {
|
||||||
|
|
||||||
|
vec3 H = normalize(V + L);
|
||||||
|
float NoL = max(dot(N, L), 0.0);
|
||||||
|
float NoH = saturate(dot(N, H));
|
||||||
|
float LoH = saturate(dot(L, H));
|
||||||
|
|
||||||
|
vec3 color = vec3(0.0);
|
||||||
|
|
||||||
|
// ==================== 基础 BRDF ====================
|
||||||
|
vec3 kS;
|
||||||
|
vec3 specularBRDF;
|
||||||
|
|
||||||
|
if(u_UseAnisotropic && abs(u_Anisotropy) > 0.01) {
|
||||||
|
// 各向异性高光
|
||||||
|
float dummy;
|
||||||
|
specularBRDF = anisotropicLobe(N, V, L, H, T, B, roughness, u_Anisotropy, F0, kS, dummy);
|
||||||
|
} else {
|
||||||
|
// 标准各向同性高光
|
||||||
|
float D = D_GGX(roughness, NoH);
|
||||||
|
float V_term = V_SmithGGXCorrelated(roughness, max(dot(N, V), 1e-4), NoL);
|
||||||
|
kS = fresnelSchlick(LoH, F0);
|
||||||
|
specularBRDF = (D * V_term) * kS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 漫反射
|
||||||
|
vec3 diffuseBRDF = BurleyDiffuse(kS, albedo, metallic, roughness, NoV, NoL, LoH);
|
||||||
|
|
||||||
|
// 次表面散射修正漫反射
|
||||||
|
if(u_UseSubsurface) {
|
||||||
|
// 使用 Wrap Diffuse 模拟次表面散射对漫反射的影响
|
||||||
|
float wrapNoL = Fd_Wrap(dot(N, L), 0.5);
|
||||||
|
diffuseBRDF *= saturate(u_SubsurfaceColor + wrapNoL);
|
||||||
|
}
|
||||||
|
|
||||||
|
color = (diffuseBRDF + specularBRDF) * NoL;
|
||||||
|
|
||||||
|
// ==================== 次表面散射 ====================
|
||||||
|
if(u_UseSubsurface) {
|
||||||
|
vec3 sss = subsurfaceLobe(N, V, L, u_SubsurfaceColor,
|
||||||
|
u_SubsurfacePower, u_Thickness, albedo);
|
||||||
|
color += sss;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== Clear Coat ====================
|
||||||
|
if(u_UseClearCoat && u_ClearCoat > 0.01) {
|
||||||
|
// Clear Coat 使用几何法线,避免底层法线贴图细节影响涂层
|
||||||
|
vec3 clearCoatNormal = normalize(v_Normal);
|
||||||
|
float clearCoatNoH = saturate(dot(clearCoatNormal, H));
|
||||||
|
float clearCoatNoL = saturate(dot(clearCoatNormal, L));
|
||||||
|
|
||||||
|
float Fcc;
|
||||||
|
float clearCoatSpecular = clearCoatLobe(
|
||||||
|
max(u_ClearCoatRoughness, MIN_ROUGHNESS),
|
||||||
|
clearCoatNoH, LoH, u_ClearCoat, Fcc);
|
||||||
|
|
||||||
|
// Clear Coat 吸收底层能量
|
||||||
|
float attenuation = 1.0 - Fcc;
|
||||||
|
color *= attenuation;
|
||||||
|
|
||||||
|
// 添加 Clear Coat 高光
|
||||||
|
color += clearCoatSpecular * clearCoatNoL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return color * radiance * occlusion;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 主渲染函数
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// 处理UV
|
||||||
|
vec2 uv = v_TexCoords;
|
||||||
|
if(u_FlipUV)
|
||||||
|
uv.y = 1.0 - uv.y;
|
||||||
|
|
||||||
|
// 获取材质属性
|
||||||
|
vec3 baseColorLinear = pow(u_Material.albedo, vec3(2.2));
|
||||||
|
vec3 texColorLinear = u_UseAlbedoMap ? pow(texture(u_AlbedoMap, uv).rgb, vec3(2.2)) : vec3(1.0);
|
||||||
|
vec3 albedo = texColorLinear * baseColorLinear;
|
||||||
|
float metallic = u_UseMetallicMap ? texture(u_MetallicMap, uv).r : u_Material.metallic;
|
||||||
|
float roughness = u_UseRoughnessMap ? texture(u_RoughnessMap, uv).r : u_Material.roughness;
|
||||||
|
float ao = u_UseAOMap ? texture(u_AOMap, uv).r : u_Material.ao;
|
||||||
|
|
||||||
|
// 获取法线
|
||||||
|
float normalVariance;
|
||||||
|
vec3 N = getNormalFromMap(normalVariance);
|
||||||
|
vec3 V = normalize(u_CameraPosition - v_WorldPos);
|
||||||
|
float NoV = max(dot(N, V), 1e-4);
|
||||||
|
|
||||||
|
// 构建 TBN 矩阵用于各向异性计算
|
||||||
|
vec3 T = normalize(v_TBN[0]);
|
||||||
|
vec3 B = normalize(v_TBN[1]);
|
||||||
|
|
||||||
|
// 如果有自定义各向异性方向,应用它
|
||||||
|
if(u_UseAnisotropic) {
|
||||||
|
vec3 anisotropyDir = normalize(u_AnisotropyDirection);
|
||||||
|
T = normalize(v_TBN * anisotropyDir);
|
||||||
|
B = normalize(cross(N, T));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 法线强度混合
|
||||||
|
vec3 N_geom = normalize(v_Normal);
|
||||||
|
N = normalize(mix(N_geom, N, u_NormalStrength));
|
||||||
|
NoV = max(dot(N, V), 1e-4);
|
||||||
|
|
||||||
|
// ================= Specular Anti-Aliasing =================
|
||||||
|
float roughnessAA = clamp(roughness, 0.04, 1.0);
|
||||||
|
if(u_UseNormalMap) {
|
||||||
|
float variance = normalVariance;
|
||||||
|
float kernelRoughness = min(2.0 * variance, 1.0);
|
||||||
|
float normalLength = length(texture(u_NormalMap, uv).xyz * 2.0 - 1.0);
|
||||||
|
float tokvsig = clamp((1.0 - normalLength), 0.0, 1.0);
|
||||||
|
roughnessAA = clamp(sqrt(roughnessAA * roughnessAA + kernelRoughness + tokvsig * 0.5 * u_ToksvigStrength), 0.04, 1.0);
|
||||||
|
}
|
||||||
|
roughness = max(roughnessAA, MIN_ROUGHNESS);
|
||||||
|
|
||||||
|
// 计算F0
|
||||||
|
vec3 F0 = mix(vec3(F0_NON_METAL), albedo, metallic);
|
||||||
|
|
||||||
|
// 反射方程
|
||||||
|
vec3 Lo = vec3(0.0);
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// 主方向光(带阴影)
|
||||||
|
// ========================================================================
|
||||||
|
{
|
||||||
|
vec3 L = normalize(-u_DirLightDirection);
|
||||||
|
vec3 radiance = u_DirLightColor * u_DirLightIntensity;
|
||||||
|
|
||||||
|
float shadow = 0.0;
|
||||||
|
if(u_EnableShadows != 0) {
|
||||||
|
shadow = calculateShadow(v_FragPosLightSpace, N, L);
|
||||||
|
}
|
||||||
|
|
||||||
|
Lo += evaluateLighting(N, V, L, T, B, albedo, roughness, metallic, F0, NoV, radiance, 1.0 - shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// 额外方向光(无阴影)
|
||||||
|
// ========================================================================
|
||||||
|
for(int i = 0; i < u_DirLightCount; i++) {
|
||||||
|
DirectionalLight light = u_DirLights[i];
|
||||||
|
vec3 L = normalize(-light.direction);
|
||||||
|
vec3 radiance = light.color * light.intensity;
|
||||||
|
|
||||||
|
Lo += evaluateLighting(N, V, L, T, B, albedo, roughness, metallic, F0, NoV, radiance, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// 点光源
|
||||||
|
// ========================================================================
|
||||||
|
for(int i = 0; i < u_PointLightCount; i++) {
|
||||||
|
PointLight light = u_PointLights[i];
|
||||||
|
|
||||||
|
vec3 L = light.position - v_WorldPos;
|
||||||
|
float lightDist = length(L);
|
||||||
|
|
||||||
|
if(lightDist > light.range)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
L = normalize(L);
|
||||||
|
|
||||||
|
float distanceAttenuation = 1.0 / (lightDist * lightDist + 1.0);
|
||||||
|
float windowFactor = sq(saturate(1.0 - sq(sq(lightDist / light.range))));
|
||||||
|
float attenuation = distanceAttenuation * windowFactor;
|
||||||
|
|
||||||
|
vec3 radiance = light.color * light.intensity * attenuation;
|
||||||
|
|
||||||
|
Lo += evaluateLighting(N, V, L, T, B, albedo, roughness, metallic, F0, NoV, radiance, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// 聚光灯
|
||||||
|
// ========================================================================
|
||||||
|
for(int i = 0; i < u_SpotLightCount; i++) {
|
||||||
|
SpotLight light = u_SpotLights[i];
|
||||||
|
|
||||||
|
vec3 L = light.position - v_WorldPos;
|
||||||
|
float lightDist = length(L);
|
||||||
|
|
||||||
|
if(lightDist > light.range)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
L = normalize(L);
|
||||||
|
|
||||||
|
float theta = dot(L, normalize(light.direction));
|
||||||
|
float epsilon = light.innerConeAngle - light.outerConeAngle;
|
||||||
|
float spotIntensity = saturate((theta - light.outerConeAngle) / epsilon);
|
||||||
|
|
||||||
|
if(spotIntensity <= 0.0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float distanceAttenuation = 1.0 / (lightDist * lightDist + 1.0);
|
||||||
|
float windowFactor = sq(saturate(1.0 - sq(sq(lightDist / light.range))));
|
||||||
|
float attenuation = distanceAttenuation * windowFactor;
|
||||||
|
|
||||||
|
vec3 radiance = light.color * light.intensity * attenuation * spotIntensity;
|
||||||
|
|
||||||
|
Lo += evaluateLighting(N, V, L, T, B, albedo, roughness, metallic, F0, NoV, radiance, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// 环境光 (IBL)
|
||||||
|
// ========================================================================
|
||||||
|
vec3 ambient = vec3(0.0);
|
||||||
|
|
||||||
|
if(u_UseIBL) {
|
||||||
|
float NoV_ibl = max(dot(N, V), 1e-4);
|
||||||
|
vec3 F = fresnelSchlickRoughness(NoV_ibl, F0, roughness);
|
||||||
|
|
||||||
|
vec3 kS = F;
|
||||||
|
vec3 kD = (1.0 - kS) * (1.0 - metallic);
|
||||||
|
|
||||||
|
// 漫反射 IBL
|
||||||
|
vec3 irradiance = texture(u_IrradianceMap, N).rgb;
|
||||||
|
if(dot(irradiance, irradiance) < 0.000001)
|
||||||
|
irradiance = vec3(0.03);
|
||||||
|
|
||||||
|
vec3 diffuse = irradiance * albedo;
|
||||||
|
|
||||||
|
// 镜面反射 IBL
|
||||||
|
vec3 R = reflect(-V, N);
|
||||||
|
float lod = roughness * u_PrefilterMaxLOD;
|
||||||
|
|
||||||
|
vec3 prefilteredColor = textureLod(u_PrefilterMap, R, lod).rgb;
|
||||||
|
if(dot(prefilteredColor, prefilteredColor) < 0.000001)
|
||||||
|
prefilteredColor = vec3(0.03);
|
||||||
|
|
||||||
|
vec2 brdf = texture(u_BRDFLT, vec2(NoV_ibl, roughness)).rg;
|
||||||
|
float specOcclusion = computeSpecularOcclusion(NoV_ibl, ao, roughness);
|
||||||
|
vec3 specular = prefilteredColor * (F * brdf.x + brdf.y) * specOcclusion;
|
||||||
|
|
||||||
|
ambient = (kD * diffuse + specular) * ao;
|
||||||
|
|
||||||
|
// Clear Coat IBL
|
||||||
|
if(u_UseClearCoat && u_ClearCoat > 0.01) {
|
||||||
|
vec3 clearCoatR = reflect(-V, normalize(v_Normal));
|
||||||
|
float clearCoatLod = u_ClearCoatRoughness * u_PrefilterMaxLOD;
|
||||||
|
vec3 clearCoatPrefilteredColor = textureLod(u_PrefilterMap, clearCoatR, clearCoatLod).rgb;
|
||||||
|
|
||||||
|
float Fc = F_Schlick_ClearCoat(NoV_ibl) * u_ClearCoat;
|
||||||
|
ambient *= (1.0 - Fc);
|
||||||
|
ambient += clearCoatPrefilteredColor * Fc;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ambient = vec3(u_AmbientIntensity) * albedo * ao;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 color = ambient + Lo;
|
||||||
|
|
||||||
|
// HDR色调映射 (ACES Filmic)
|
||||||
|
color = ACESFilm(color);
|
||||||
|
// Gamma校正
|
||||||
|
color = pow(color, vec3(1.0 / 2.2));
|
||||||
|
|
||||||
|
o_Color = vec4(color, 1.0);
|
||||||
|
o_ObjectID = v_ObjectID;
|
||||||
|
}
|
||||||
142
参考/Fermion/Boson/Resources/Shaders/ProceduralSky.glsl
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
|
||||||
|
uniform mat4 u_Projection;
|
||||||
|
uniform mat4 u_View;
|
||||||
|
|
||||||
|
out vec3 v_LocalPos;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
v_LocalPos = a_Position;
|
||||||
|
|
||||||
|
// Remove translation from view matrix
|
||||||
|
mat4 rotView = mat4(mat3(u_View));
|
||||||
|
vec4 clipPos = u_Projection * rotView * vec4(a_Position, 1.0);
|
||||||
|
|
||||||
|
gl_Position = clipPos.xyww;
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 FragColor;
|
||||||
|
|
||||||
|
in vec3 v_LocalPos;
|
||||||
|
|
||||||
|
// Sky parameters
|
||||||
|
uniform vec3 u_SunDirection; // Normalized sun direction
|
||||||
|
uniform float u_SunIntensity; // HDR sun brightness
|
||||||
|
uniform float u_SunAngularRadius; // Sun angular radius in radians
|
||||||
|
uniform vec3 u_SkyColorZenith; // Zenith color (linear)
|
||||||
|
uniform vec3 u_SkyColorHorizon; // Horizon color (linear)
|
||||||
|
uniform vec3 u_GroundColor; // Ground color (linear)
|
||||||
|
uniform float u_SkyExposure; // Sky exposure multiplier
|
||||||
|
|
||||||
|
const float PI = 3.14159265359;
|
||||||
|
|
||||||
|
// Rayleigh scattering approximation
|
||||||
|
// Simulates how shorter wavelengths (blue) scatter more at zenith
|
||||||
|
vec3 computeRayleighScattering(vec3 direction, vec3 sunDir)
|
||||||
|
{
|
||||||
|
// Height gradient: zenith (y=1) to horizon (y=0)
|
||||||
|
float height = max(direction.y, 0.0);
|
||||||
|
|
||||||
|
// Exponential falloff for more natural sky gradient
|
||||||
|
float zenithFactor = pow(height, 0.5);
|
||||||
|
|
||||||
|
// Blend between horizon and zenith colors
|
||||||
|
vec3 skyColor = mix(u_SkyColorHorizon, u_SkyColorZenith, zenithFactor);
|
||||||
|
|
||||||
|
// Add slight brightening near horizon (atmospheric haze)
|
||||||
|
float horizonGlow = exp(-height * 4.0) * 0.3;
|
||||||
|
skyColor += u_SkyColorHorizon * horizonGlow;
|
||||||
|
|
||||||
|
return skyColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mie scattering - creates glow around the sun
|
||||||
|
vec3 computeMieScattering(vec3 direction, vec3 sunDir)
|
||||||
|
{
|
||||||
|
float cosTheta = dot(direction, sunDir);
|
||||||
|
|
||||||
|
// Henyey-Greenstein phase function approximation
|
||||||
|
// g controls asymmetry (0.76 gives forward scattering)
|
||||||
|
float g = 0.76;
|
||||||
|
float g2 = g * g;
|
||||||
|
|
||||||
|
float phase = (1.0 - g2) / (4.0 * PI * pow(1.0 + g2 - 2.0 * g * cosTheta, 1.5));
|
||||||
|
|
||||||
|
// Scale and color the Mie scattering
|
||||||
|
// Warm color for sunset-like glow
|
||||||
|
vec3 mieColor = vec3(1.0, 0.9, 0.7);
|
||||||
|
|
||||||
|
return mieColor * phase * 0.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sun disc with soft edge
|
||||||
|
vec3 computeSunDisc(vec3 direction, vec3 sunDir)
|
||||||
|
{
|
||||||
|
float cosTheta = dot(direction, sunDir);
|
||||||
|
float sunCosAngle = cos(u_SunAngularRadius);
|
||||||
|
|
||||||
|
// Soft edge using smoothstep
|
||||||
|
float edgeSoftness = u_SunAngularRadius * 0.3;
|
||||||
|
float sunDisc = smoothstep(
|
||||||
|
cos(u_SunAngularRadius + edgeSoftness),
|
||||||
|
sunCosAngle,
|
||||||
|
cosTheta
|
||||||
|
);
|
||||||
|
|
||||||
|
// HDR sun color (slightly warm)
|
||||||
|
vec3 sunColor = vec3(1.0, 0.95, 0.9) * u_SunIntensity;
|
||||||
|
|
||||||
|
return sunColor * sunDisc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec3 direction = normalize(v_LocalPos);
|
||||||
|
vec3 sunDir = normalize(u_SunDirection);
|
||||||
|
|
||||||
|
vec3 color = vec3(0.0);
|
||||||
|
|
||||||
|
// Check if we're looking at sky or ground
|
||||||
|
if (direction.y > -0.02)
|
||||||
|
{
|
||||||
|
// Sky hemisphere
|
||||||
|
|
||||||
|
// Base sky color with Rayleigh scattering
|
||||||
|
vec3 skyColor = computeRayleighScattering(direction, sunDir);
|
||||||
|
|
||||||
|
// Add Mie scattering (sun glow)
|
||||||
|
vec3 mie = computeMieScattering(direction, sunDir);
|
||||||
|
skyColor += mie;
|
||||||
|
|
||||||
|
// Add sun disc (only if sun is above horizon)
|
||||||
|
if (sunDir.y > 0.0)
|
||||||
|
{
|
||||||
|
vec3 sunDisc = computeSunDisc(direction, sunDir);
|
||||||
|
skyColor += sunDisc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply exposure
|
||||||
|
color = skyColor * u_SkyExposure;
|
||||||
|
|
||||||
|
// Soft transition to ground at horizon
|
||||||
|
float horizonBlend = smoothstep(-0.02, 0.05, direction.y);
|
||||||
|
color = mix(u_GroundColor * u_SkyExposure * 0.5, color, horizonBlend);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Ground hemisphere
|
||||||
|
// Darken towards nadir for depth
|
||||||
|
float groundFade = smoothstep(-1.0, -0.1, direction.y);
|
||||||
|
color = u_GroundColor * u_SkyExposure * 0.5 * groundFade;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output linear HDR (no tonemapping - IBL pipeline will process this)
|
||||||
|
FragColor = vec4(color, 1.0);
|
||||||
|
}
|
||||||
100
参考/Fermion/Boson/Resources/Shaders/Quad.glsl
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// Basic Texture Shader
|
||||||
|
|
||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
layout(location = 1) in vec4 a_Color;
|
||||||
|
layout(location = 2) in vec2 a_TexCoord;
|
||||||
|
layout(location = 3) in float a_TexIndex;
|
||||||
|
layout(location = 4) in float a_TilingFactor;
|
||||||
|
layout(location = 5) in int a_ObjectID;
|
||||||
|
|
||||||
|
// Camera uniform buffer (binding = 0)
|
||||||
|
layout(std140, binding = 0) uniform CameraData
|
||||||
|
{
|
||||||
|
mat4 u_ViewProjection;
|
||||||
|
mat4 u_View;
|
||||||
|
mat4 u_Projection;
|
||||||
|
vec3 u_CameraPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
out vec4 v_Color;
|
||||||
|
out vec2 v_TexCoord;
|
||||||
|
out float v_TilingFactor;
|
||||||
|
flat out float v_TexIndex;
|
||||||
|
flat out int v_ObjectID;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
v_Color = a_Color;
|
||||||
|
v_TexCoord = a_TexCoord;
|
||||||
|
v_TilingFactor = a_TilingFactor;
|
||||||
|
v_TexIndex = a_TexIndex;
|
||||||
|
v_ObjectID = a_ObjectID;
|
||||||
|
|
||||||
|
gl_Position = u_ViewProjection * vec4(a_Position, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Color;
|
||||||
|
layout(location = 1) out int o_ObjectID;
|
||||||
|
|
||||||
|
in vec4 v_Color;
|
||||||
|
in vec2 v_TexCoord;
|
||||||
|
in float v_TilingFactor;
|
||||||
|
flat in float v_TexIndex;
|
||||||
|
flat in int v_ObjectID;
|
||||||
|
|
||||||
|
uniform sampler2D u_Textures[32];
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 texColor = v_Color;
|
||||||
|
|
||||||
|
switch (int(v_TexIndex))
|
||||||
|
{
|
||||||
|
case 0: texColor *= texture(u_Textures[ 0], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 1: texColor *= texture(u_Textures[ 1], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 2: texColor *= texture(u_Textures[ 2], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 3: texColor *= texture(u_Textures[ 3], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 4: texColor *= texture(u_Textures[ 4], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 5: texColor *= texture(u_Textures[ 5], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 6: texColor *= texture(u_Textures[ 6], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 7: texColor *= texture(u_Textures[ 7], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 8: texColor *= texture(u_Textures[ 8], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 9: texColor *= texture(u_Textures[ 9], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 10: texColor *= texture(u_Textures[10], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 11: texColor *= texture(u_Textures[11], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 12: texColor *= texture(u_Textures[12], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 13: texColor *= texture(u_Textures[13], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 14: texColor *= texture(u_Textures[14], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 15: texColor *= texture(u_Textures[15], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 16: texColor *= texture(u_Textures[16], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 17: texColor *= texture(u_Textures[17], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 18: texColor *= texture(u_Textures[18], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 19: texColor *= texture(u_Textures[19], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 20: texColor *= texture(u_Textures[20], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 21: texColor *= texture(u_Textures[21], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 22: texColor *= texture(u_Textures[22], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 23: texColor *= texture(u_Textures[23], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 24: texColor *= texture(u_Textures[24], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 25: texColor *= texture(u_Textures[25], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 26: texColor *= texture(u_Textures[26], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 27: texColor *= texture(u_Textures[27], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 28: texColor *= texture(u_Textures[28], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 29: texColor *= texture(u_Textures[29], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 30: texColor *= texture(u_Textures[30], v_TexCoord * v_TilingFactor); break;
|
||||||
|
case 31: texColor *= texture(u_Textures[31], v_TexCoord * v_TilingFactor); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texColor.a == 0.0)
|
||||||
|
discard;
|
||||||
|
|
||||||
|
o_Color = texColor;
|
||||||
|
o_ObjectID = v_ObjectID;
|
||||||
|
}
|
||||||
|
|
||||||
178
参考/Fermion/Boson/Resources/Shaders/QuadInstance.glsl
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
// Instanced quad shader
|
||||||
|
|
||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in mat4 a_Transform;
|
||||||
|
layout(location = 4) in vec4 a_Color;
|
||||||
|
layout(location = 5) in float a_TexIndex;
|
||||||
|
layout(location = 6) in float a_TilingFactor;
|
||||||
|
layout(location = 7) in int a_ObjectID;
|
||||||
|
|
||||||
|
// Camera uniform buffer (binding = 0)
|
||||||
|
layout(std140, binding = 0) uniform CameraData
|
||||||
|
{
|
||||||
|
mat4 u_ViewProjection;
|
||||||
|
mat4 u_View;
|
||||||
|
mat4 u_Projection;
|
||||||
|
vec3 u_CameraPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
out vec4 v_Color;
|
||||||
|
out vec2 v_TexCoord;
|
||||||
|
out float v_TilingFactor;
|
||||||
|
flat out float v_TexIndex;
|
||||||
|
flat out int v_ObjectID;
|
||||||
|
|
||||||
|
const vec3 s_Positions[4] = vec3[](
|
||||||
|
vec3(-0.5, -0.5, 0.0),
|
||||||
|
vec3(0.5, -0.5, 0.0),
|
||||||
|
vec3(0.5, 0.5, 0.0),
|
||||||
|
vec3(-0.5, 0.5, 0.0));
|
||||||
|
|
||||||
|
const vec2 s_TexCoords[4] = vec2[](
|
||||||
|
vec2(0.0, 0.0),
|
||||||
|
vec2(1.0, 0.0),
|
||||||
|
vec2(1.0, 1.0),
|
||||||
|
vec2(0.0, 1.0));
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
int vertexIndex = gl_VertexID % 4;
|
||||||
|
vec4 worldPosition = a_Transform * vec4(s_Positions[vertexIndex], 1.0);
|
||||||
|
|
||||||
|
v_Color = a_Color;
|
||||||
|
v_TexCoord = s_TexCoords[vertexIndex];
|
||||||
|
v_TilingFactor = a_TilingFactor;
|
||||||
|
v_TexIndex = a_TexIndex;
|
||||||
|
v_ObjectID = a_ObjectID;
|
||||||
|
|
||||||
|
gl_Position = u_ViewProjection * worldPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Color;
|
||||||
|
layout(location = 1) out int o_ObjectID;
|
||||||
|
|
||||||
|
in vec4 v_Color;
|
||||||
|
in vec2 v_TexCoord;
|
||||||
|
in float v_TilingFactor;
|
||||||
|
flat in float v_TexIndex;
|
||||||
|
flat in int v_ObjectID;
|
||||||
|
|
||||||
|
uniform sampler2D u_Textures[32];
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 texColor = v_Color;
|
||||||
|
|
||||||
|
switch (int(v_TexIndex))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
texColor *= texture(u_Textures[0], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
texColor *= texture(u_Textures[1], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
texColor *= texture(u_Textures[2], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
texColor *= texture(u_Textures[3], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
texColor *= texture(u_Textures[4], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
texColor *= texture(u_Textures[5], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
texColor *= texture(u_Textures[6], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
texColor *= texture(u_Textures[7], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
texColor *= texture(u_Textures[8], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
texColor *= texture(u_Textures[9], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
texColor *= texture(u_Textures[10], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
texColor *= texture(u_Textures[11], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
texColor *= texture(u_Textures[12], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
texColor *= texture(u_Textures[13], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
texColor *= texture(u_Textures[14], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
texColor *= texture(u_Textures[15], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
texColor *= texture(u_Textures[16], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 17:
|
||||||
|
texColor *= texture(u_Textures[17], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 18:
|
||||||
|
texColor *= texture(u_Textures[18], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 19:
|
||||||
|
texColor *= texture(u_Textures[19], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
texColor *= texture(u_Textures[20], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 21:
|
||||||
|
texColor *= texture(u_Textures[21], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 22:
|
||||||
|
texColor *= texture(u_Textures[22], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 23:
|
||||||
|
texColor *= texture(u_Textures[23], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 24:
|
||||||
|
texColor *= texture(u_Textures[24], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 25:
|
||||||
|
texColor *= texture(u_Textures[25], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 26:
|
||||||
|
texColor *= texture(u_Textures[26], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 27:
|
||||||
|
texColor *= texture(u_Textures[27], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 28:
|
||||||
|
texColor *= texture(u_Textures[28], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 29:
|
||||||
|
texColor *= texture(u_Textures[29], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 30:
|
||||||
|
texColor *= texture(u_Textures[30], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
case 31:
|
||||||
|
texColor *= texture(u_Textures[31], v_TexCoord * v_TilingFactor);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texColor.a == 0.0)
|
||||||
|
discard;
|
||||||
|
|
||||||
|
o_Color = texColor;
|
||||||
|
o_ObjectID = v_ObjectID;
|
||||||
|
}
|
||||||
40
参考/Fermion/Boson/Resources/Shaders/Shadow.glsl
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
|
||||||
|
// Model uniform buffer (binding = 1)
|
||||||
|
layout(std140, binding = 1) uniform ModelData
|
||||||
|
{
|
||||||
|
mat4 u_Model;
|
||||||
|
mat4 u_NormalMatrix;
|
||||||
|
int u_ObjectID;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Light uniform buffer (binding = 2)
|
||||||
|
layout(std140, binding = 2) uniform LightData
|
||||||
|
{
|
||||||
|
mat4 u_LightSpaceMatrix;
|
||||||
|
vec3 u_DirLightDirection;
|
||||||
|
float u_DirLightIntensity;
|
||||||
|
vec3 u_DirLightColor;
|
||||||
|
float _lightPadding0;
|
||||||
|
float u_ShadowBias;
|
||||||
|
float u_ShadowSoftness;
|
||||||
|
int u_EnableShadows;
|
||||||
|
float _lightPadding1;
|
||||||
|
float u_AmbientIntensity;
|
||||||
|
int u_NumPointLights;
|
||||||
|
int u_NumSpotLights;
|
||||||
|
};
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = u_LightSpaceMatrix * u_Model * vec4(a_Position, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
}
|
||||||
174
参考/Fermion/Boson/Resources/Shaders/SkinnedGBufferPBRMesh.glsl
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
layout(location = 1) in vec3 a_Normal;
|
||||||
|
layout(location = 2) in vec4 a_Color;
|
||||||
|
layout(location = 3) in vec2 a_TexCoords;
|
||||||
|
layout(location = 4) in ivec4 a_BoneIDs;
|
||||||
|
layout(location = 5) in vec4 a_BoneWeights;
|
||||||
|
|
||||||
|
// Camera uniform buffer (binding = 0)
|
||||||
|
layout(std140, binding = 0) uniform CameraData
|
||||||
|
{
|
||||||
|
mat4 u_ViewProjection;
|
||||||
|
mat4 u_View;
|
||||||
|
mat4 u_Projection;
|
||||||
|
vec3 u_CameraPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Model uniform buffer (binding = 1)
|
||||||
|
layout(std140, binding = 1) uniform ModelData
|
||||||
|
{
|
||||||
|
mat4 u_Model;
|
||||||
|
mat4 u_NormalMatrix;
|
||||||
|
int u_ObjectID;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bone uniform buffer (binding = 4)
|
||||||
|
layout(std140, binding = 4) uniform BoneData
|
||||||
|
{
|
||||||
|
mat4 u_BoneMatrices[128];
|
||||||
|
};
|
||||||
|
|
||||||
|
out vec3 v_WorldPos;
|
||||||
|
out vec3 v_Normal;
|
||||||
|
out vec2 v_TexCoords;
|
||||||
|
flat out int v_ObjectID;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Compute skin matrix from bone influences
|
||||||
|
mat4 skinMatrix = mat4(0.0);
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (a_BoneIDs[i] >= 0) {
|
||||||
|
skinMatrix += a_BoneWeights[i] * u_BoneMatrices[a_BoneIDs[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no bones affect this vertex, use identity
|
||||||
|
if (a_BoneWeights[0] == 0.0 && a_BoneWeights[1] == 0.0 &&
|
||||||
|
a_BoneWeights[2] == 0.0 && a_BoneWeights[3] == 0.0) {
|
||||||
|
skinMatrix = mat4(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 skinnedPos = skinMatrix * vec4(a_Position, 1.0);
|
||||||
|
vec4 worldPos = u_Model * skinnedPos;
|
||||||
|
v_WorldPos = worldPos.xyz;
|
||||||
|
|
||||||
|
// Skin the normal
|
||||||
|
mat3 skinNormalMatrix = mat3(skinMatrix);
|
||||||
|
mat3 normalMatrix = mat3(u_NormalMatrix);
|
||||||
|
v_Normal = normalize(normalMatrix * skinNormalMatrix * a_Normal);
|
||||||
|
|
||||||
|
v_TexCoords = a_TexCoords;
|
||||||
|
v_ObjectID = u_ObjectID;
|
||||||
|
|
||||||
|
gl_Position = u_ViewProjection * worldPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Albedo;
|
||||||
|
layout(location = 1) out vec4 o_Normal;
|
||||||
|
layout(location = 2) out vec4 o_Material;
|
||||||
|
layout(location = 3) out vec4 o_Emissive;
|
||||||
|
layout(location = 4) out int o_ObjectID;
|
||||||
|
|
||||||
|
in vec3 v_WorldPos;
|
||||||
|
in vec3 v_Normal;
|
||||||
|
in vec2 v_TexCoords;
|
||||||
|
flat in int v_ObjectID;
|
||||||
|
|
||||||
|
struct Material
|
||||||
|
{
|
||||||
|
vec3 albedo;
|
||||||
|
float metallic;
|
||||||
|
float roughness;
|
||||||
|
float ao;
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform Material u_Material;
|
||||||
|
|
||||||
|
uniform bool u_UseAlbedoMap;
|
||||||
|
uniform sampler2D u_AlbedoMap;
|
||||||
|
|
||||||
|
uniform bool u_UseNormalMap;
|
||||||
|
uniform sampler2D u_NormalMap;
|
||||||
|
uniform float u_NormalStrength;
|
||||||
|
uniform float u_ToksvigStrength;
|
||||||
|
|
||||||
|
uniform bool u_UseMetallicMap;
|
||||||
|
uniform sampler2D u_MetallicMap;
|
||||||
|
|
||||||
|
uniform bool u_UseRoughnessMap;
|
||||||
|
uniform sampler2D u_RoughnessMap;
|
||||||
|
|
||||||
|
uniform bool u_UseAOMap;
|
||||||
|
uniform sampler2D u_AOMap;
|
||||||
|
|
||||||
|
uniform bool u_FlipUV;
|
||||||
|
|
||||||
|
vec3 getNormalFromMap(vec2 uv, out float normalVariance, out float normalLength)
|
||||||
|
{
|
||||||
|
normalVariance = 0.0;
|
||||||
|
normalLength = 1.0;
|
||||||
|
|
||||||
|
if (!u_UseNormalMap)
|
||||||
|
return normalize(v_Normal);
|
||||||
|
|
||||||
|
vec3 tangentNormal = texture(u_NormalMap, uv).xyz * 2.0 - 1.0;
|
||||||
|
normalLength = length(tangentNormal);
|
||||||
|
|
||||||
|
vec3 Q1 = dFdx(v_WorldPos);
|
||||||
|
vec3 Q2 = dFdy(v_WorldPos);
|
||||||
|
vec2 st1 = dFdx(uv);
|
||||||
|
vec2 st2 = dFdy(uv);
|
||||||
|
|
||||||
|
vec3 N = normalize(v_Normal);
|
||||||
|
vec3 T = normalize(Q1 * st2.t - Q2 * st1.t);
|
||||||
|
vec3 B = normalize(cross(N, T));
|
||||||
|
mat3 TBN = mat3(T, B, N);
|
||||||
|
|
||||||
|
vec3 worldNormal = normalize(TBN * tangentNormal);
|
||||||
|
worldNormal = normalize(mix(N, worldNormal, u_NormalStrength));
|
||||||
|
|
||||||
|
vec3 dndx = dFdx(worldNormal);
|
||||||
|
vec3 dndy = dFdy(worldNormal);
|
||||||
|
normalVariance = max(0.0, dot(dndx, dndx) + dot(dndy, dndy));
|
||||||
|
return worldNormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec2 uv = v_TexCoords;
|
||||||
|
if (u_FlipUV)
|
||||||
|
uv.y = 1.0 - uv.y;
|
||||||
|
|
||||||
|
vec3 baseColorLinear = pow(u_Material.albedo, vec3(2.2));
|
||||||
|
vec3 texColorLinear = u_UseAlbedoMap ? pow(texture(u_AlbedoMap, uv).rgb, vec3(2.2)) : vec3(1.0);
|
||||||
|
vec3 albedo = texColorLinear * baseColorLinear;
|
||||||
|
|
||||||
|
float metallic = u_UseMetallicMap ? texture(u_MetallicMap, uv).r : u_Material.metallic;
|
||||||
|
float roughness = u_UseRoughnessMap ? texture(u_RoughnessMap, uv).r : u_Material.roughness;
|
||||||
|
float ao = u_UseAOMap ? texture(u_AOMap, uv).r : u_Material.ao;
|
||||||
|
|
||||||
|
float normalVariance;
|
||||||
|
float normalLength;
|
||||||
|
vec3 normal = getNormalFromMap(uv, normalVariance, normalLength);
|
||||||
|
|
||||||
|
float roughnessAA = clamp(roughness, 0.04, 1.0);
|
||||||
|
if (u_UseNormalMap)
|
||||||
|
{
|
||||||
|
float kernelRoughness = min(2.0 * normalVariance, 1.0);
|
||||||
|
float tokvsig = clamp(1.0 - normalLength, 0.0, 1.0);
|
||||||
|
roughnessAA = clamp(sqrt(roughnessAA * roughnessAA + kernelRoughness + tokvsig * 0.5 * u_ToksvigStrength), 0.04, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
o_Albedo = vec4(albedo, 1.0);
|
||||||
|
o_Normal = vec4(normalize(normal), 1.0);
|
||||||
|
o_Material = vec4(roughnessAA, metallic, ao, 1.0);
|
||||||
|
o_Emissive = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
o_ObjectID = v_ObjectID;
|
||||||
|
}
|
||||||
577
参考/Fermion/Boson/Resources/Shaders/SkinnedPBRMesh.glsl
Normal file
@@ -0,0 +1,577 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
layout(location = 1) in vec3 a_Normal;
|
||||||
|
layout(location = 2) in vec4 a_Color;
|
||||||
|
layout(location = 3) in vec2 a_TexCoords;
|
||||||
|
layout(location = 4) in ivec4 a_BoneIDs;
|
||||||
|
layout(location = 5) in vec4 a_BoneWeights;
|
||||||
|
|
||||||
|
// Camera uniform buffer (binding = 0)
|
||||||
|
layout(std140, binding = 0) uniform CameraData
|
||||||
|
{
|
||||||
|
mat4 u_ViewProjection;
|
||||||
|
mat4 u_View;
|
||||||
|
mat4 u_Projection;
|
||||||
|
vec3 u_CameraPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Model uniform buffer (binding = 1)
|
||||||
|
layout(std140, binding = 1) uniform ModelData
|
||||||
|
{
|
||||||
|
mat4 u_Model;
|
||||||
|
mat4 u_NormalMatrix;
|
||||||
|
int u_ObjectID;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Light uniform buffer (binding = 2)
|
||||||
|
layout(std140, binding = 2) uniform LightData
|
||||||
|
{
|
||||||
|
mat4 u_LightSpaceMatrix;
|
||||||
|
vec3 u_DirLightDirection;
|
||||||
|
float u_DirLightIntensity;
|
||||||
|
vec3 u_DirLightColor;
|
||||||
|
float _lightPadding0;
|
||||||
|
float u_ShadowBias;
|
||||||
|
float u_ShadowSoftness;
|
||||||
|
int u_EnableShadows;
|
||||||
|
int u_NumDirLights;
|
||||||
|
float u_AmbientIntensity;
|
||||||
|
int u_NumPointLights;
|
||||||
|
int u_NumSpotLights;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bone uniform buffer (binding = 4)
|
||||||
|
layout(std140, binding = 4) uniform BoneData
|
||||||
|
{
|
||||||
|
mat4 u_BoneMatrices[128];
|
||||||
|
};
|
||||||
|
|
||||||
|
out vec3 v_WorldPos;
|
||||||
|
out vec3 v_Normal;
|
||||||
|
out vec4 v_Color;
|
||||||
|
out vec2 v_TexCoords;
|
||||||
|
out vec4 v_FragPosLightSpace;
|
||||||
|
flat out int v_ObjectID;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Compute skin matrix from bone influences
|
||||||
|
mat4 skinMatrix = mat4(0.0);
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (a_BoneIDs[i] >= 0) {
|
||||||
|
skinMatrix += a_BoneWeights[i] * u_BoneMatrices[a_BoneIDs[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no bones affect this vertex, use identity
|
||||||
|
if (a_BoneWeights[0] == 0.0 && a_BoneWeights[1] == 0.0 &&
|
||||||
|
a_BoneWeights[2] == 0.0 && a_BoneWeights[3] == 0.0) {
|
||||||
|
skinMatrix = mat4(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 skinnedPos = skinMatrix * vec4(a_Position, 1.0);
|
||||||
|
vec4 worldPos = u_Model * skinnedPos;
|
||||||
|
v_WorldPos = worldPos.xyz;
|
||||||
|
|
||||||
|
// Skin the normal
|
||||||
|
mat3 skinNormalMatrix = mat3(skinMatrix);
|
||||||
|
mat3 normalMatrix = mat3(u_NormalMatrix);
|
||||||
|
v_Normal = normalize(normalMatrix * skinNormalMatrix * a_Normal);
|
||||||
|
|
||||||
|
v_Color = a_Color;
|
||||||
|
v_TexCoords = a_TexCoords;
|
||||||
|
v_FragPosLightSpace = u_LightSpaceMatrix * worldPos;
|
||||||
|
v_ObjectID = u_ObjectID;
|
||||||
|
|
||||||
|
gl_Position = u_ViewProjection * worldPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Color;
|
||||||
|
layout(location = 1) out int o_ObjectID;
|
||||||
|
|
||||||
|
in vec3 v_WorldPos;
|
||||||
|
in vec3 v_Normal;
|
||||||
|
in vec4 v_Color;
|
||||||
|
in vec2 v_TexCoords;
|
||||||
|
in vec4 v_FragPosLightSpace;
|
||||||
|
flat in int v_ObjectID;
|
||||||
|
|
||||||
|
const float PI = 3.14159265359;
|
||||||
|
const float HALF_PI = 1.570796327;
|
||||||
|
const float F0_NON_METAL = 0.04;
|
||||||
|
const float MIN_ROUGHNESS = 0.045;
|
||||||
|
|
||||||
|
float pow5(float x) {
|
||||||
|
float x2 = x * x;
|
||||||
|
return x2 * x2 * x;
|
||||||
|
}
|
||||||
|
|
||||||
|
float sq(float x) {
|
||||||
|
return x * x;
|
||||||
|
}
|
||||||
|
|
||||||
|
float saturate(float x) {
|
||||||
|
return clamp(x, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 saturate(vec3 x) {
|
||||||
|
return clamp(x, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Camera uniform buffer (binding = 0)
|
||||||
|
layout(std140, binding = 0) uniform CameraData
|
||||||
|
{
|
||||||
|
mat4 u_ViewProjection;
|
||||||
|
mat4 u_View;
|
||||||
|
mat4 u_Projection;
|
||||||
|
vec3 u_CameraPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Light uniform buffer (binding = 2)
|
||||||
|
layout(std140, binding = 2) uniform LightData
|
||||||
|
{
|
||||||
|
mat4 u_LightSpaceMatrix;
|
||||||
|
vec3 u_DirLightDirection;
|
||||||
|
float u_DirLightIntensity;
|
||||||
|
vec3 u_DirLightColor;
|
||||||
|
float _lightPadding0;
|
||||||
|
float u_ShadowBias;
|
||||||
|
float u_ShadowSoftness;
|
||||||
|
int u_EnableShadows;
|
||||||
|
int u_NumDirLights;
|
||||||
|
float u_AmbientIntensity;
|
||||||
|
int u_NumPointLights;
|
||||||
|
int u_NumSpotLights;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX_DIR_LIGHTS 4
|
||||||
|
#define MAX_POINT_LIGHTS 16
|
||||||
|
#define MAX_SPOT_LIGHTS 16
|
||||||
|
|
||||||
|
struct DirectionalLight {
|
||||||
|
vec3 direction;
|
||||||
|
vec3 color;
|
||||||
|
float intensity;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PointLight {
|
||||||
|
vec3 position;
|
||||||
|
vec3 color;
|
||||||
|
float intensity;
|
||||||
|
float range;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SpotLight {
|
||||||
|
vec3 position;
|
||||||
|
vec3 direction;
|
||||||
|
vec3 color;
|
||||||
|
float intensity;
|
||||||
|
float range;
|
||||||
|
float innerConeAngle;
|
||||||
|
float outerConeAngle;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Material {
|
||||||
|
vec3 albedo;
|
||||||
|
float metallic;
|
||||||
|
float roughness;
|
||||||
|
float ao;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Clear Coat
|
||||||
|
uniform bool u_UseClearCoat;
|
||||||
|
uniform float u_ClearCoat;
|
||||||
|
uniform float u_ClearCoatRoughness;
|
||||||
|
|
||||||
|
// Anisotropic
|
||||||
|
uniform bool u_UseAnisotropic;
|
||||||
|
uniform float u_Anisotropy;
|
||||||
|
uniform vec3 u_AnisotropyDirection;
|
||||||
|
|
||||||
|
// Subsurface Scattering
|
||||||
|
uniform bool u_UseSubsurface;
|
||||||
|
uniform vec3 u_SubsurfaceColor;
|
||||||
|
uniform float u_SubsurfacePower;
|
||||||
|
uniform float u_Thickness;
|
||||||
|
|
||||||
|
uniform int u_DirLightCount;
|
||||||
|
uniform DirectionalLight u_DirLights[MAX_DIR_LIGHTS];
|
||||||
|
uniform int u_PointLightCount;
|
||||||
|
uniform PointLight u_PointLights[MAX_POINT_LIGHTS];
|
||||||
|
uniform int u_SpotLightCount;
|
||||||
|
uniform SpotLight u_SpotLights[MAX_SPOT_LIGHTS];
|
||||||
|
|
||||||
|
uniform Material u_Material;
|
||||||
|
|
||||||
|
uniform bool u_UseAlbedoMap;
|
||||||
|
uniform sampler2D u_AlbedoMap;
|
||||||
|
uniform bool u_UseNormalMap;
|
||||||
|
uniform sampler2D u_NormalMap;
|
||||||
|
uniform float u_NormalStrength;
|
||||||
|
uniform float u_ToksvigStrength;
|
||||||
|
uniform bool u_UseMetallicMap;
|
||||||
|
uniform sampler2D u_MetallicMap;
|
||||||
|
uniform bool u_UseRoughnessMap;
|
||||||
|
uniform sampler2D u_RoughnessMap;
|
||||||
|
uniform bool u_UseAOMap;
|
||||||
|
uniform sampler2D u_AOMap;
|
||||||
|
uniform bool u_FlipUV;
|
||||||
|
uniform sampler2D u_ShadowMap;
|
||||||
|
|
||||||
|
// IBL
|
||||||
|
uniform bool u_UseIBL;
|
||||||
|
uniform samplerCube u_IrradianceMap;
|
||||||
|
uniform samplerCube u_PrefilterMap;
|
||||||
|
uniform sampler2D u_BRDFLT;
|
||||||
|
uniform float u_PrefilterMaxLOD;
|
||||||
|
|
||||||
|
// GGX NDF
|
||||||
|
float D_GGX(float roughness, float NoH) {
|
||||||
|
float a = roughness * roughness;
|
||||||
|
float a2 = a * a;
|
||||||
|
float NoH2 = NoH * NoH;
|
||||||
|
float nom = a2;
|
||||||
|
float denom = (NoH2 * (a2 - 1.0) + 1.0);
|
||||||
|
denom = PI * denom * denom;
|
||||||
|
return nom / max(denom, 1e-7);
|
||||||
|
}
|
||||||
|
|
||||||
|
float V_SmithGGXCorrelated(float roughness, float NoV, float NoL) {
|
||||||
|
float a = roughness * roughness;
|
||||||
|
float a2 = a * a;
|
||||||
|
float lambdaV = NoL * sqrt(NoV * NoV * (1.0 - a2) + a2);
|
||||||
|
float lambdaL = NoV * sqrt(NoL * NoL * (1.0 - a2) + a2);
|
||||||
|
return 0.5 / max(lambdaV + lambdaL, 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 F_Schlick(const vec3 f0, float f90, float VoH) {
|
||||||
|
return f0 + (vec3(f90) - f0) * pow5(1.0 - VoH);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
|
||||||
|
return F0 + (1.0 - F0) * pow5(saturate(1.0 - cosTheta));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) {
|
||||||
|
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow5(saturate(1.0 - cosTheta));
|
||||||
|
}
|
||||||
|
|
||||||
|
float Fd_Lambert() {
|
||||||
|
return 1.0 / PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Fd_Burley(float roughness, float NoV, float NoL, float LoH) {
|
||||||
|
float f90 = 0.5 + 2.0 * roughness * LoH * LoH;
|
||||||
|
float lightScatter = 1.0 + (f90 - 1.0) * pow5(1.0 - NoL);
|
||||||
|
float viewScatter = 1.0 + (f90 - 1.0) * pow5(1.0 - NoV);
|
||||||
|
return lightScatter * viewScatter * (1.0 / PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 ACESFilm(vec3 x) {
|
||||||
|
float a = 2.51;
|
||||||
|
float b = 0.03;
|
||||||
|
float c = 2.43;
|
||||||
|
float d = 0.59;
|
||||||
|
float e = 0.14;
|
||||||
|
return saturate((x * (a * x + b)) / (x * (c * x + d) + e));
|
||||||
|
}
|
||||||
|
|
||||||
|
float computeSpecularOcclusion(float NoV, float ao, float roughness) {
|
||||||
|
return clamp(pow(NoV + ao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ao, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
float V_Kelemen(float LoH) {
|
||||||
|
return 0.25 / max(LoH * LoH, 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
float F_Schlick_ClearCoat(float VoH) {
|
||||||
|
float f0 = 0.04;
|
||||||
|
return f0 + (1.0 - f0) * pow5(1.0 - VoH);
|
||||||
|
}
|
||||||
|
|
||||||
|
float clearCoatLobe(float clearCoatRoughness, float NoH, float LoH, float clearCoat, out float Fcc) {
|
||||||
|
float D = D_GGX(clearCoatRoughness, NoH);
|
||||||
|
float V = V_Kelemen(LoH);
|
||||||
|
float F = F_Schlick_ClearCoat(LoH) * clearCoat;
|
||||||
|
Fcc = F;
|
||||||
|
return D * V * F;
|
||||||
|
}
|
||||||
|
|
||||||
|
float D_GGX_Anisotropic(float at, float ab, float ToH, float BoH, float NoH) {
|
||||||
|
float a2 = at * ab;
|
||||||
|
vec3 d_vec = vec3(ab * ToH, at * BoH, a2 * NoH);
|
||||||
|
float d2 = dot(d_vec, d_vec);
|
||||||
|
float b2 = a2 / max(d2, 1e-7);
|
||||||
|
return a2 * b2 * b2 * (1.0 / PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
float V_SmithGGXCorrelated_Anisotropic(float at, float ab, float ToV, float BoV,
|
||||||
|
float ToL, float BoL, float NoV, float NoL) {
|
||||||
|
float lambdaV = NoL * length(vec3(at * ToV, ab * BoV, NoV));
|
||||||
|
float lambdaL = NoV * length(vec3(at * ToL, ab * BoL, NoL));
|
||||||
|
return 0.5 / max(lambdaV + lambdaL, 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 anisotropicLobe(vec3 N, vec3 V, vec3 L, vec3 H, vec3 T, vec3 B,
|
||||||
|
float roughness, float anisotropy, vec3 F0, out vec3 kS, out float NoL_out) {
|
||||||
|
float NoV = max(dot(N, V), 1e-4);
|
||||||
|
float NoL = max(dot(N, L), 0.0);
|
||||||
|
float NoH = saturate(dot(N, H));
|
||||||
|
float LoH = saturate(dot(L, H));
|
||||||
|
float ToV = dot(T, V);
|
||||||
|
float BoV = dot(B, V);
|
||||||
|
float ToL = dot(T, L);
|
||||||
|
float BoL = dot(B, L);
|
||||||
|
float ToH = dot(T, H);
|
||||||
|
float BoH = dot(B, H);
|
||||||
|
float at = max(roughness * (1.0 + anisotropy), MIN_ROUGHNESS);
|
||||||
|
float ab = max(roughness * (1.0 - anisotropy), MIN_ROUGHNESS);
|
||||||
|
float D = D_GGX_Anisotropic(at, ab, ToH, BoH, NoH);
|
||||||
|
float V_term = V_SmithGGXCorrelated_Anisotropic(at, ab, ToV, BoV, ToL, BoL, NoV, NoL);
|
||||||
|
vec3 F = fresnelSchlick(LoH, F0);
|
||||||
|
kS = F;
|
||||||
|
NoL_out = NoL;
|
||||||
|
return (D * V_term) * F;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Fd_Wrap(float NoL, float w) {
|
||||||
|
return saturate((NoL + w) / sq(1.0 + w));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 subsurfaceLobe(vec3 N, vec3 V, vec3 L, vec3 subsurfaceColor,
|
||||||
|
float subsurfacePower, float thickness, vec3 diffuseColor) {
|
||||||
|
float NoL = dot(N, L);
|
||||||
|
float scatterVoH = saturate(dot(V, -L));
|
||||||
|
float forwardScatter = exp2(scatterVoH * subsurfacePower - subsurfacePower);
|
||||||
|
float backScatter = saturate(NoL * thickness + (1.0 - thickness)) * 0.5;
|
||||||
|
float subsurface = mix(backScatter, 1.0, forwardScatter) * (1.0 - thickness);
|
||||||
|
return subsurfaceColor * subsurface * diffuseColor * (1.0 / PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) {
|
||||||
|
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
||||||
|
projCoords = projCoords * 0.5 + 0.5;
|
||||||
|
if(projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 ||
|
||||||
|
projCoords.y < 0.0 || projCoords.y > 1.0)
|
||||||
|
return 0.0;
|
||||||
|
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
|
||||||
|
float currentDepth = projCoords.z;
|
||||||
|
float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1);
|
||||||
|
float shadow = 0.0;
|
||||||
|
vec2 texelSize = 1.0 / textureSize(u_ShadowMap, 0);
|
||||||
|
int pcfRange = int(u_ShadowSoftness);
|
||||||
|
int sampleCount = 0;
|
||||||
|
for(int x = -pcfRange; x <= pcfRange; ++x) {
|
||||||
|
for(int y = -pcfRange; y <= pcfRange; ++y) {
|
||||||
|
float pcfDepth = texture(u_ShadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
|
||||||
|
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
|
||||||
|
sampleCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shadow /= float(sampleCount);
|
||||||
|
return shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 getNormalFromMap(out float normalVariance) {
|
||||||
|
if(!u_UseNormalMap) {
|
||||||
|
normalVariance = 0.0;
|
||||||
|
return normalize(v_Normal);
|
||||||
|
}
|
||||||
|
vec3 tangentNormal = texture(u_NormalMap, v_TexCoords).xyz * 2.0 - 1.0;
|
||||||
|
vec3 Q1 = dFdx(v_WorldPos);
|
||||||
|
vec3 Q2 = dFdy(v_WorldPos);
|
||||||
|
vec2 st1 = dFdx(v_TexCoords);
|
||||||
|
vec2 st2 = dFdy(v_TexCoords);
|
||||||
|
vec3 N = normalize(v_Normal);
|
||||||
|
vec3 T = normalize(Q1 * st2.t - Q2 * st1.t);
|
||||||
|
vec3 B = normalize(cross(N, T));
|
||||||
|
mat3 TBN = mat3(T, B, N);
|
||||||
|
vec3 worldNormal = normalize(TBN * tangentNormal);
|
||||||
|
vec3 dndx = dFdx(worldNormal);
|
||||||
|
vec3 dndy = dFdy(worldNormal);
|
||||||
|
normalVariance = max(0.0, dot(dndx, dndx) + dot(dndy, dndy));
|
||||||
|
return worldNormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 BurleyDiffuse(vec3 kS, vec3 albedo, float metallic, float roughness, float NoV, float NoL, float LoH) {
|
||||||
|
vec3 kD = (vec3(1.0) - kS) * (1.0 - metallic);
|
||||||
|
return kD * albedo * Fd_Burley(roughness, NoV, NoL, LoH);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 evaluateLighting(vec3 N, vec3 V, vec3 L, vec3 T, vec3 B,
|
||||||
|
vec3 albedo, float roughness, float metallic, vec3 F0,
|
||||||
|
float NoV, vec3 radiance, float occlusion) {
|
||||||
|
vec3 H = normalize(V + L);
|
||||||
|
float NoL = max(dot(N, L), 0.0);
|
||||||
|
float NoH = saturate(dot(N, H));
|
||||||
|
float LoH = saturate(dot(L, H));
|
||||||
|
vec3 color = vec3(0.0);
|
||||||
|
vec3 kS;
|
||||||
|
vec3 specularBRDF;
|
||||||
|
if(u_UseAnisotropic && abs(u_Anisotropy) > 0.01) {
|
||||||
|
float dummy;
|
||||||
|
specularBRDF = anisotropicLobe(N, V, L, H, T, B, roughness, u_Anisotropy, F0, kS, dummy);
|
||||||
|
} else {
|
||||||
|
float D = D_GGX(roughness, NoH);
|
||||||
|
float V_term = V_SmithGGXCorrelated(roughness, max(dot(N, V), 1e-4), NoL);
|
||||||
|
kS = fresnelSchlick(LoH, F0);
|
||||||
|
specularBRDF = (D * V_term) * kS;
|
||||||
|
}
|
||||||
|
vec3 diffuseBRDF = BurleyDiffuse(kS, albedo, metallic, roughness, NoV, NoL, LoH);
|
||||||
|
if(u_UseSubsurface) {
|
||||||
|
float wrapNoL = Fd_Wrap(dot(N, L), 0.5);
|
||||||
|
diffuseBRDF *= saturate(u_SubsurfaceColor + wrapNoL);
|
||||||
|
}
|
||||||
|
color = (diffuseBRDF + specularBRDF) * NoL;
|
||||||
|
if(u_UseSubsurface) {
|
||||||
|
vec3 sss = subsurfaceLobe(N, V, L, u_SubsurfaceColor,
|
||||||
|
u_SubsurfacePower, u_Thickness, albedo);
|
||||||
|
color += sss;
|
||||||
|
}
|
||||||
|
if(u_UseClearCoat && u_ClearCoat > 0.01) {
|
||||||
|
vec3 clearCoatNormal = normalize(v_Normal);
|
||||||
|
float clearCoatNoH = saturate(dot(clearCoatNormal, H));
|
||||||
|
float clearCoatNoL = saturate(dot(clearCoatNormal, L));
|
||||||
|
float Fcc;
|
||||||
|
float clearCoatSpecular = clearCoatLobe(
|
||||||
|
max(u_ClearCoatRoughness, MIN_ROUGHNESS),
|
||||||
|
clearCoatNoH, LoH, u_ClearCoat, Fcc);
|
||||||
|
float attenuation = 1.0 - Fcc;
|
||||||
|
color *= attenuation;
|
||||||
|
color += clearCoatSpecular * clearCoatNoL;
|
||||||
|
}
|
||||||
|
return color * radiance * occlusion;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 uv = v_TexCoords;
|
||||||
|
if(u_FlipUV)
|
||||||
|
uv.y = 1.0 - uv.y;
|
||||||
|
vec3 baseColorLinear = pow(u_Material.albedo, vec3(2.2));
|
||||||
|
vec3 texColorLinear = u_UseAlbedoMap ? pow(texture(u_AlbedoMap, uv).rgb, vec3(2.2)) : vec3(1.0);
|
||||||
|
vec3 albedo = texColorLinear * baseColorLinear;
|
||||||
|
float metallic = u_UseMetallicMap ? texture(u_MetallicMap, uv).r : u_Material.metallic;
|
||||||
|
float roughness = u_UseRoughnessMap ? texture(u_RoughnessMap, uv).r : u_Material.roughness;
|
||||||
|
float ao = u_UseAOMap ? texture(u_AOMap, uv).r : u_Material.ao;
|
||||||
|
float normalVariance;
|
||||||
|
vec3 N = getNormalFromMap(normalVariance);
|
||||||
|
vec3 V = normalize(u_CameraPosition - v_WorldPos);
|
||||||
|
float NoV = max(dot(N, V), 1e-4);
|
||||||
|
|
||||||
|
// Compute T and B using screen-space derivatives (same as getNormalFromMap)
|
||||||
|
vec3 Q1 = dFdx(v_WorldPos);
|
||||||
|
vec3 Q2 = dFdy(v_WorldPos);
|
||||||
|
vec2 st1 = dFdx(uv);
|
||||||
|
vec2 st2 = dFdy(uv);
|
||||||
|
vec3 T = normalize(Q1 * st2.t - Q2 * st1.t);
|
||||||
|
vec3 B = normalize(cross(N, T));
|
||||||
|
|
||||||
|
if(u_UseAnisotropic) {
|
||||||
|
vec3 anisotropyDir = normalize(u_AnisotropyDirection);
|
||||||
|
vec3 N_geom = normalize(v_Normal);
|
||||||
|
mat3 TBN = mat3(T, B, N_geom);
|
||||||
|
T = normalize(TBN * anisotropyDir);
|
||||||
|
B = normalize(cross(N, T));
|
||||||
|
}
|
||||||
|
vec3 N_geom = normalize(v_Normal);
|
||||||
|
float roughnessWeight = smoothstep(0.15, 0.75, 1.0 - roughness);
|
||||||
|
float grazingAttenuation = smoothstep(0.0, 0.4, NoV);
|
||||||
|
float normalWeight = roughnessWeight * grazingAttenuation;
|
||||||
|
normalWeight *= u_NormalStrength;
|
||||||
|
float dist = length(u_CameraPosition - v_WorldPos);
|
||||||
|
float distanceBoost = clamp(1.5 - dist * 0.15, 0.7, 1.5);
|
||||||
|
normalWeight *= distanceBoost;
|
||||||
|
N = normalize(mix(N_geom, N, normalWeight));
|
||||||
|
NoV = max(dot(N, V), 1e-4);
|
||||||
|
float roughnessAA = clamp(roughness, 0.04, 1.0);
|
||||||
|
if(u_UseNormalMap) {
|
||||||
|
float variance = normalVariance;
|
||||||
|
float kernelRoughness = min(2.0 * variance, 1.0);
|
||||||
|
float normalLength = length(texture(u_NormalMap, uv).xyz * 2.0 - 1.0);
|
||||||
|
float tokvsig = clamp((1.0 - normalLength), 0.0, 1.0);
|
||||||
|
roughnessAA = clamp(sqrt(roughnessAA * roughnessAA + kernelRoughness + tokvsig * 0.5 * u_ToksvigStrength), 0.04, 1.0);
|
||||||
|
}
|
||||||
|
roughness = max(roughnessAA, MIN_ROUGHNESS);
|
||||||
|
vec3 F0 = mix(vec3(F0_NON_METAL), albedo, metallic);
|
||||||
|
vec3 Lo = vec3(0.0);
|
||||||
|
{
|
||||||
|
vec3 L = normalize(-u_DirLightDirection);
|
||||||
|
vec3 radiance = u_DirLightColor * u_DirLightIntensity;
|
||||||
|
float shadow = 0.0;
|
||||||
|
if(u_EnableShadows != 0) {
|
||||||
|
shadow = calculateShadow(v_FragPosLightSpace, N, L);
|
||||||
|
}
|
||||||
|
Lo += evaluateLighting(N, V, L, T, B, albedo, roughness, metallic, F0, NoV, radiance, 1.0 - shadow);
|
||||||
|
}
|
||||||
|
for(int i = 0; i < u_DirLightCount; i++) {
|
||||||
|
DirectionalLight light = u_DirLights[i];
|
||||||
|
vec3 L = normalize(-light.direction);
|
||||||
|
vec3 radiance = light.color * light.intensity;
|
||||||
|
Lo += evaluateLighting(N, V, L, T, B, albedo, roughness, metallic, F0, NoV, radiance, 1.0);
|
||||||
|
}
|
||||||
|
for(int i = 0; i < u_PointLightCount; i++) {
|
||||||
|
PointLight light = u_PointLights[i];
|
||||||
|
vec3 L = light.position - v_WorldPos;
|
||||||
|
float lightDist = length(L);
|
||||||
|
if(lightDist > light.range) continue;
|
||||||
|
L = normalize(L);
|
||||||
|
float distanceAttenuation = 1.0 / (lightDist * lightDist + 1.0);
|
||||||
|
float windowFactor = sq(saturate(1.0 - sq(sq(lightDist / light.range))));
|
||||||
|
float attenuation = distanceAttenuation * windowFactor;
|
||||||
|
vec3 radiance = light.color * light.intensity * attenuation;
|
||||||
|
Lo += evaluateLighting(N, V, L, T, B, albedo, roughness, metallic, F0, NoV, radiance, 1.0);
|
||||||
|
}
|
||||||
|
for(int i = 0; i < u_SpotLightCount; i++) {
|
||||||
|
SpotLight light = u_SpotLights[i];
|
||||||
|
vec3 L = light.position - v_WorldPos;
|
||||||
|
float lightDist = length(L);
|
||||||
|
if(lightDist > light.range) continue;
|
||||||
|
L = normalize(L);
|
||||||
|
float theta = dot(L, normalize(light.direction));
|
||||||
|
float epsilon = light.innerConeAngle - light.outerConeAngle;
|
||||||
|
float spotIntensity = saturate((theta - light.outerConeAngle) / epsilon);
|
||||||
|
if(spotIntensity <= 0.0) continue;
|
||||||
|
float distanceAttenuation = 1.0 / (lightDist * lightDist + 1.0);
|
||||||
|
float windowFactor = sq(saturate(1.0 - sq(sq(lightDist / light.range))));
|
||||||
|
float attenuation = distanceAttenuation * windowFactor;
|
||||||
|
vec3 radiance = light.color * light.intensity * attenuation * spotIntensity;
|
||||||
|
Lo += evaluateLighting(N, V, L, T, B, albedo, roughness, metallic, F0, NoV, radiance, 1.0);
|
||||||
|
}
|
||||||
|
vec3 ambient = vec3(0.0);
|
||||||
|
if(u_UseIBL) {
|
||||||
|
float NoV_ibl = max(dot(N, V), 1e-4);
|
||||||
|
vec3 F = fresnelSchlickRoughness(NoV_ibl, F0, roughness);
|
||||||
|
vec3 kS = F;
|
||||||
|
vec3 kD = (1.0 - kS) * (1.0 - metallic);
|
||||||
|
vec3 irradiance = texture(u_IrradianceMap, N).rgb;
|
||||||
|
if(dot(irradiance, irradiance) < 0.000001) irradiance = vec3(0.03);
|
||||||
|
vec3 diffuse = irradiance * albedo;
|
||||||
|
vec3 R = reflect(-V, N);
|
||||||
|
float lod = roughness * u_PrefilterMaxLOD;
|
||||||
|
vec3 prefilteredColor = textureLod(u_PrefilterMap, R, lod).rgb;
|
||||||
|
if(dot(prefilteredColor, prefilteredColor) < 0.000001) prefilteredColor = vec3(0.03);
|
||||||
|
vec2 brdf = texture(u_BRDFLT, vec2(NoV_ibl, roughness)).rg;
|
||||||
|
float specOcclusion = computeSpecularOcclusion(NoV_ibl, ao, roughness);
|
||||||
|
vec3 specular = prefilteredColor * (F * brdf.x + brdf.y) * specOcclusion;
|
||||||
|
ambient = (kD * diffuse + specular) * ao;
|
||||||
|
if(u_UseClearCoat && u_ClearCoat > 0.01) {
|
||||||
|
vec3 clearCoatR = reflect(-V, normalize(v_Normal));
|
||||||
|
float clearCoatLod = u_ClearCoatRoughness * u_PrefilterMaxLOD;
|
||||||
|
vec3 clearCoatPrefilteredColor = textureLod(u_PrefilterMap, clearCoatR, clearCoatLod).rgb;
|
||||||
|
float Fc = F_Schlick_ClearCoat(NoV_ibl) * u_ClearCoat;
|
||||||
|
ambient *= (1.0 - Fc);
|
||||||
|
ambient += clearCoatPrefilteredColor * Fc;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ambient = vec3(u_AmbientIntensity) * albedo * ao;
|
||||||
|
}
|
||||||
|
vec3 color = ambient + Lo;
|
||||||
|
color = ACESFilm(color);
|
||||||
|
color = pow(color, vec3(1.0 / 2.2));
|
||||||
|
o_Color = vec4(color, 1.0);
|
||||||
|
o_ObjectID = v_ObjectID;
|
||||||
|
}
|
||||||
63
参考/Fermion/Boson/Resources/Shaders/SkinnedShadow.glsl
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
layout(location = 4) in ivec4 a_BoneIDs;
|
||||||
|
layout(location = 5) in vec4 a_BoneWeights;
|
||||||
|
|
||||||
|
// Model uniform buffer (binding = 1)
|
||||||
|
layout(std140, binding = 1) uniform ModelData
|
||||||
|
{
|
||||||
|
mat4 u_Model;
|
||||||
|
mat4 u_NormalMatrix;
|
||||||
|
int u_ObjectID;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Light uniform buffer (binding = 2)
|
||||||
|
layout(std140, binding = 2) uniform LightData
|
||||||
|
{
|
||||||
|
mat4 u_LightSpaceMatrix;
|
||||||
|
vec3 u_DirLightDirection;
|
||||||
|
float u_DirLightIntensity;
|
||||||
|
vec3 u_DirLightColor;
|
||||||
|
float _lightPadding0;
|
||||||
|
float u_ShadowBias;
|
||||||
|
float u_ShadowSoftness;
|
||||||
|
int u_EnableShadows;
|
||||||
|
float _lightPadding1;
|
||||||
|
float u_AmbientIntensity;
|
||||||
|
int u_NumPointLights;
|
||||||
|
int u_NumSpotLights;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bone uniform buffer (binding = 4)
|
||||||
|
layout(std140, binding = 4) uniform BoneData
|
||||||
|
{
|
||||||
|
mat4 u_BoneMatrices[128];
|
||||||
|
};
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Compute skin matrix from bone influences
|
||||||
|
mat4 skinMatrix = mat4(0.0);
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (a_BoneIDs[i] >= 0) {
|
||||||
|
skinMatrix += a_BoneWeights[i] * u_BoneMatrices[a_BoneIDs[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no bones affect this vertex, use identity
|
||||||
|
if (a_BoneWeights[0] == 0.0 && a_BoneWeights[1] == 0.0 &&
|
||||||
|
a_BoneWeights[2] == 0.0 && a_BoneWeights[3] == 0.0) {
|
||||||
|
skinMatrix = mat4(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 skinnedPos = skinMatrix * vec4(a_Position, 1.0);
|
||||||
|
gl_Position = u_LightSpaceMatrix * u_Model * skinnedPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
}
|
||||||
35
参考/Fermion/Boson/Resources/Shaders/Skybox.glsl
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
|
||||||
|
out vec3 v_TexCoords;
|
||||||
|
|
||||||
|
uniform mat4 u_View;
|
||||||
|
uniform mat4 u_Projection;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
v_TexCoords = a_Position;
|
||||||
|
|
||||||
|
mat4 view = mat4(mat3(u_View));
|
||||||
|
vec4 clipPos = u_Projection * view * vec4(a_Position, 1.0);
|
||||||
|
gl_Position = vec4(clipPos.xy, clipPos.w, clipPos.w); // Force depth to far plane to avoid occluding scene geometry
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
in vec3 v_TexCoords;
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
uniform samplerCube u_Cubemap;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 color = texture(u_Cubemap, v_TexCoords).rgb;
|
||||||
|
|
||||||
|
// HDR色调映射 (Reinhard)
|
||||||
|
color = color / (color + vec3(1.0));
|
||||||
|
|
||||||
|
// Gamma校正
|
||||||
|
color = pow(color, vec3(1.0/2.2));
|
||||||
|
|
||||||
|
FragColor = vec4(color, 1.0);
|
||||||
|
}
|
||||||
61
参考/Fermion/Boson/Resources/Shaders/Text.glsl
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#type vertex
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_Position;
|
||||||
|
layout(location = 1) in vec4 a_Color;
|
||||||
|
layout(location = 2) in vec2 a_TexCoord;
|
||||||
|
layout(location = 3) in int a_ObjectID;
|
||||||
|
|
||||||
|
// Camera uniform buffer (binding = 0)
|
||||||
|
layout(std140, binding = 0) uniform CameraData
|
||||||
|
{
|
||||||
|
mat4 u_ViewProjection;
|
||||||
|
mat4 u_View;
|
||||||
|
mat4 u_Projection;
|
||||||
|
vec3 u_CameraPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
out vec4 v_Color;
|
||||||
|
out vec2 v_TexCoord;
|
||||||
|
flat out int v_ObjectID;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
v_Color = a_Color;
|
||||||
|
v_TexCoord = a_TexCoord;
|
||||||
|
v_ObjectID = a_ObjectID;
|
||||||
|
|
||||||
|
gl_Position = u_ViewProjection * vec4(a_Position, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#type fragment
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Color;
|
||||||
|
layout(location = 1) out int o_ObjectID;
|
||||||
|
|
||||||
|
in vec4 v_Color;
|
||||||
|
in vec2 v_TexCoord;
|
||||||
|
flat in int v_ObjectID;
|
||||||
|
|
||||||
|
uniform sampler2D u_Atlas;
|
||||||
|
|
||||||
|
float median(float r, float g, float b)
|
||||||
|
{
|
||||||
|
return max(min(r, g), min(max(r, g), b));
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec3 msd = texture(u_Atlas, v_TexCoord).rgb;
|
||||||
|
float sd = median(msd.r, msd.g, msd.b);
|
||||||
|
|
||||||
|
// Smooth threshold around 0.5 (MSDF center)
|
||||||
|
float alpha = smoothstep(0.5 - 0.1, 0.5 + 0.1, sd);
|
||||||
|
if (alpha <= 0.0)
|
||||||
|
discard;
|
||||||
|
|
||||||
|
o_Color = vec4(v_Color.rgb, v_Color.a * alpha);
|
||||||
|
o_ObjectID = v_ObjectID;
|
||||||
|
}
|
||||||
|
|
||||||
BIN
参考/Fermion/Boson/Resources/assets/fonts/Cuprum-Bold.ttf
Normal file
93
参考/Fermion/Boson/Resources/assets/fonts/Cuprum-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
Copyright 2010 The Cuprum Project Authors (lemonad@jovanny.ru), with Reserved Font Name "Cuprum".
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
93
参考/Fermion/Boson/Resources/assets/fonts/Play-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
Copyright (c) 2011, Jonas Hecksher, Playtypes, e-types AS (lasse@e-types.com), with Reserved Font Name 'Play', 'Playtype', 'Playtype Sans'.
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
BIN
参考/Fermion/Boson/Resources/assets/fonts/Play-Regular.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
参考/Fermion/Boson/Resources/assets/fonts/Play-Regular.ttf
Normal file
65
参考/Fermion/Boson/projects/Assets/Materials/Material_MR.fmat
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
Material:
|
||||||
|
Type: 2
|
||||||
|
Name: Material_MR
|
||||||
|
Albedo: [1, 1, 1]
|
||||||
|
Metallic: 1
|
||||||
|
Roughness: 1
|
||||||
|
AO: 1
|
||||||
|
Emissive: [0, 0, 0]
|
||||||
|
EmissiveIntensity: 1
|
||||||
|
AlbedoMap: 2493715504153704453
|
||||||
|
NormalMap: 7553078236290796322
|
||||||
|
MetallicMap: 16026405867881152170
|
||||||
|
RoughnessMap: 16026405867881152170
|
||||||
|
AOMap: 5345533721503086083
|
||||||
|
EmissiveMap: 10970895745636314528
|
||||||
|
Editor:
|
||||||
|
NextNodeID: 8
|
||||||
|
NextLinkID: 7
|
||||||
|
Nodes:
|
||||||
|
- ID: 1
|
||||||
|
Type: 0
|
||||||
|
Position: [400, 100]
|
||||||
|
- ID: 2
|
||||||
|
Type: 1
|
||||||
|
Position: [50, 0]
|
||||||
|
TextureHandle: 2493715504153704453
|
||||||
|
- ID: 3
|
||||||
|
Type: 1
|
||||||
|
Position: [50, 150]
|
||||||
|
TextureHandle: 7553078236290796322
|
||||||
|
- ID: 4
|
||||||
|
Type: 1
|
||||||
|
Position: [50, 300]
|
||||||
|
TextureHandle: 16026405867881152170
|
||||||
|
- ID: 5
|
||||||
|
Type: 1
|
||||||
|
Position: [50, 450]
|
||||||
|
TextureHandle: 16026405867881152170
|
||||||
|
- ID: 6
|
||||||
|
Type: 1
|
||||||
|
Position: [50, 600]
|
||||||
|
TextureHandle: 5345533721503086083
|
||||||
|
- ID: 7
|
||||||
|
Type: 1
|
||||||
|
Position: [39, 770]
|
||||||
|
TextureHandle: 10970895745636314528
|
||||||
|
Links:
|
||||||
|
- ID: 1
|
||||||
|
StartPin: 201
|
||||||
|
EndPin: 101
|
||||||
|
- ID: 2
|
||||||
|
StartPin: 301
|
||||||
|
EndPin: 102
|
||||||
|
- ID: 3
|
||||||
|
StartPin: 401
|
||||||
|
EndPin: 103
|
||||||
|
- ID: 4
|
||||||
|
StartPin: 501
|
||||||
|
EndPin: 104
|
||||||
|
- ID: 5
|
||||||
|
StartPin: 601
|
||||||
|
EndPin: 105
|
||||||
|
- ID: 6
|
||||||
|
StartPin: 701
|
||||||
|
EndPin: 106
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Asset:
|
||||||
|
Handle: 12539830590652719771
|
||||||
|
Type: Material
|
||||||
|
Name: Material_MR
|
||||||
|
FilePath: Assets/Materials/Material_MR.fmat
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
TextureAsset:
|
||||||
|
SourcePath: Assets/Materials/Plastic/albedo.png
|
||||||
|
GenerateMipmaps: true
|
||||||
|
MinFilter: Linear
|
||||||
|
MagFilter: Linear
|
||||||
|
WrapS: Repeat
|
||||||
|
WrapT: Repeat
|
||||||
|
Anisotropy: 1
|
||||||
|
sRGB: false
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Asset:
|
||||||
|
Handle: 10678462134903437624
|
||||||
|
Type: Texture
|
||||||
|
Name: albedo
|
||||||
|
FilePath: Assets/Materials/Plastic/albedo.ftex
|
||||||
BIN
参考/Fermion/Boson/projects/Assets/Materials/Plastic/albedo.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
@@ -0,0 +1,9 @@
|
|||||||
|
TextureAsset:
|
||||||
|
SourcePath: Assets/Materials/Plastic/ao.png
|
||||||
|
GenerateMipmaps: true
|
||||||
|
MinFilter: Linear
|
||||||
|
MagFilter: Linear
|
||||||
|
WrapS: Repeat
|
||||||
|
WrapT: Repeat
|
||||||
|
Anisotropy: 1
|
||||||
|
sRGB: false
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Asset:
|
||||||
|
Handle: 4645923213129979568
|
||||||
|
Type: Texture
|
||||||
|
Name: ao
|
||||||
|
FilePath: Assets/Materials/Plastic/ao.ftex
|
||||||
BIN
参考/Fermion/Boson/projects/Assets/Materials/Plastic/ao.png
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
@@ -0,0 +1,9 @@
|
|||||||
|
TextureAsset:
|
||||||
|
SourcePath: Assets/Materials/Plastic/normal.png
|
||||||
|
GenerateMipmaps: true
|
||||||
|
MinFilter: Linear
|
||||||
|
MagFilter: Linear
|
||||||
|
WrapS: Repeat
|
||||||
|
WrapT: Repeat
|
||||||
|
Anisotropy: 1
|
||||||
|
sRGB: false
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Asset:
|
||||||
|
Handle: 12443521726984459823
|
||||||
|
Type: Texture
|
||||||
|
Name: normal
|
||||||
|
FilePath: Assets/Materials/Plastic/normal.ftex
|
||||||
BIN
参考/Fermion/Boson/projects/Assets/Materials/Plastic/normal.png
Normal file
|
After Width: | Height: | Size: 4.4 MiB |
@@ -0,0 +1,9 @@
|
|||||||
|
TextureAsset:
|
||||||
|
SourcePath: Assets/Materials/Plastic/roughness.png
|
||||||
|
GenerateMipmaps: true
|
||||||
|
MinFilter: Linear
|
||||||
|
MagFilter: Linear
|
||||||
|
WrapS: Repeat
|
||||||
|
WrapT: Repeat
|
||||||
|
Anisotropy: 1
|
||||||
|
sRGB: false
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Asset:
|
||||||
|
Handle: 13216614171579863546
|
||||||
|
Type: Texture
|
||||||
|
Name: roughness
|
||||||
|
FilePath: Assets/Materials/Plastic/roughness.ftex
|
||||||
BIN
参考/Fermion/Boson/projects/Assets/Materials/Plastic/roughness.png
Normal file
|
After Width: | Height: | Size: 4.5 MiB |