1309 lines
29 KiB
Markdown
1309 lines
29 KiB
Markdown
|
|
# XCVolumeRenderer 深入方向规划
|
|||
|
|
|
|||
|
|
本文档详细列出本科毕业设计可深入的所有方向,供选择和规划。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 一、渲染效果提升
|
|||
|
|
|
|||
|
|
### 1.1 多散射 (Multiple Scattering)
|
|||
|
|
|
|||
|
|
#### 背景
|
|||
|
|
当前实现的是单散射(Single Scattering),光线只从光源到体素再到相机弹射一次。真实云层中,光线会在体积内多次弹射,产生柔和的边缘和通透的内部效果。
|
|||
|
|
|
|||
|
|
#### 当前问题
|
|||
|
|
- 云内部过暗,缺乏体积感
|
|||
|
|
- 边缘过于锐利,不自然
|
|||
|
|
- 逆光效果不明显
|
|||
|
|
|
|||
|
|
#### 解决方案
|
|||
|
|
|
|||
|
|
**方法A:迭代多散射**
|
|||
|
|
```
|
|||
|
|
for (散射次数) {
|
|||
|
|
for (每个采样点) {
|
|||
|
|
计算该点的入射光(来自光源 + 其他体素的散射光)
|
|||
|
|
累积散射光
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
- 优点:物理正确
|
|||
|
|
- 缺点:性能开销大,O(n²) 或更高
|
|||
|
|
|
|||
|
|
**方法B:近似多散射**
|
|||
|
|
```
|
|||
|
|
// 使用扩散近似(Diffusion Approximation)
|
|||
|
|
// 参考: Jensen et al. "A Practical Model for Subsurface Light Transport"
|
|||
|
|
float multiScatter = exp(-sigmaT * d) * diffusionTerm;
|
|||
|
|
```
|
|||
|
|
- 优点:性能好
|
|||
|
|
- 缺点:近似,不够精确
|
|||
|
|
|
|||
|
|
**方法C:预计算多散射**
|
|||
|
|
```
|
|||
|
|
// 离线预计算散射LUT
|
|||
|
|
// 运行时查表
|
|||
|
|
Texture3D scatteringLUT;
|
|||
|
|
float multiScatter = scatteringLUT.Sample(sampler, uvw);
|
|||
|
|
```
|
|||
|
|
- 优点:运行时快
|
|||
|
|
- 缺点:需要额外存储
|
|||
|
|
|
|||
|
|
#### 实现步骤
|
|||
|
|
1. 实现简单的双散射(Double Scattering)
|
|||
|
|
2. 观察效果变化
|
|||
|
|
3. 优化性能(重要性采样)
|
|||
|
|
4. 扩展到多阶散射
|
|||
|
|
|
|||
|
|
#### 参考资料
|
|||
|
|
- Jensen, H. W., et al. "A Practical Model for Subsurface Light Transport" (SIGGRAPH 2001)
|
|||
|
|
- Hachisuka, T., et al. "Progressive Photon Mapping" (SIGGRAPH 2008)
|
|||
|
|
- Kutz, P., et al. "Spectral and Decomposition Tracking for Rendering Heterogeneous Volumes" (SIGGRAPH 2017)
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- 双散射基础实现:1周
|
|||
|
|
- 完整多散射:2-3周
|
|||
|
|
- 性能优化:1周
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 1.2 相函数 (Phase Function)
|
|||
|
|
|
|||
|
|
#### 背景
|
|||
|
|
相函数描述光线在介质中散射的方向分布。当前使用的是均匀相函数 `return 1.0`,即各向同性散射。真实云层的相函数是各向异性的,具有强烈的前向散射特性。
|
|||
|
|
|
|||
|
|
#### 当前问题
|
|||
|
|
- 光照效果过于均匀
|
|||
|
|
- 缺乏真实云层的光晕效果
|
|||
|
|
- 逆光时的"银边"效果不明显
|
|||
|
|
|
|||
|
|
#### 相函数类型
|
|||
|
|
|
|||
|
|
**1. Henyey-Greenstein (HG) 相函数**
|
|||
|
|
```hlsl
|
|||
|
|
float hg_phase(float cosTheta, float g) {
|
|||
|
|
float g2 = g * g;
|
|||
|
|
return (1 - g2) / pow(1 + g2 - 2 * g * cosTheta, 1.5) / (4 * PI);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
- `g = 0`: 各向同性
|
|||
|
|
- `g > 0`: 前向散射(云层特性)
|
|||
|
|
- `g < 0`: 后向散射
|
|||
|
|
- 参数 `g` 范围:(-1, 1)
|
|||
|
|
|
|||
|
|
**2. 双 Henyey-Greenstein 相函数**
|
|||
|
|
```hlsl
|
|||
|
|
float double_hg_phase(float cosTheta, float g1, float g2, float w) {
|
|||
|
|
return w * hg_phase(cosTheta, g1) + (1 - w) * hg_phase(cosTheta, g2);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
- 结合两个HG相函数
|
|||
|
|
- 更好地模拟真实云层
|
|||
|
|
- `g1 ≈ 0.8`(强前向),`g2 ≈ -0.2`(弱后向)
|
|||
|
|
|
|||
|
|
**3. Mie 相函数**
|
|||
|
|
```hlsl
|
|||
|
|
// 查表实现,更精确但复杂
|
|||
|
|
// 通常用HG近似
|
|||
|
|
```
|
|||
|
|
- 物理最正确
|
|||
|
|
- 计算复杂
|
|||
|
|
- 通常用双HG近似
|
|||
|
|
|
|||
|
|
**4. Rayleigh 相函数**
|
|||
|
|
```hlsl
|
|||
|
|
float rayleigh_phase(float cosTheta) {
|
|||
|
|
return (3 / (16 * PI)) * (1 + cosTheta * cosTheta);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
- 适用于大气散射
|
|||
|
|
- 小粒子(空气分子)
|
|||
|
|
|
|||
|
|
#### 实现步骤
|
|||
|
|
1. 实现 HG 相函数
|
|||
|
|
2. 添加参数 UI 控制 `g` 值
|
|||
|
|
3. 观察不同 `g` 值的效果
|
|||
|
|
4. 实现双 HG 相函数
|
|||
|
|
5. 对比效果
|
|||
|
|
|
|||
|
|
#### 参数建议
|
|||
|
|
| 场景 | g 值 | 说明 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| 云层 | 0.7 ~ 0.9 | 强前向散射 |
|
|||
|
|
| 雾 | 0.1 ~ 0.3 | 弱前向散射 |
|
|||
|
|
| 烟雾 | -0.2 ~ 0.2 | 接近各向同性 |
|
|||
|
|
|
|||
|
|
#### 参考资料
|
|||
|
|
- Henyey, L. G., & Greenstein, J. L. "Diffuse radiation in the galaxy" (1941)
|
|||
|
|
- Nishita, T., et al. "Display of clouds taking into account multiple anisotropic scattering and sky light" (SIGGRAPH 1996)
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- HG相函数实现:1-2天
|
|||
|
|
- 双HG相函数:2-3天
|
|||
|
|
- 参数UI:1天
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 1.3 体积自发光 (Volumetric Emission)
|
|||
|
|
|
|||
|
|
#### 背景
|
|||
|
|
火焰、熔岩、霓虹气体等发光体积。除了散射外部光源,自身也发出光。
|
|||
|
|
|
|||
|
|
#### 实现方法
|
|||
|
|
```hlsl
|
|||
|
|
// 在着色器中添加自发光项
|
|||
|
|
float3 emission = emissionColor * emissionIntensity * emissionMask;
|
|||
|
|
float3 S = sigmaS * phase_function() * shadow * float3(1, 1, 1) + emission;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 扩展
|
|||
|
|
- 温度场驱动发光颜色(黑体辐射)
|
|||
|
|
- 发光影响周围体积(需多散射支持)
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- 基础实现:1-2天
|
|||
|
|
- 温度场驱动:3-4天
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 二、性能优化
|
|||
|
|
|
|||
|
|
### 2.1 Temporal Reprojection
|
|||
|
|
|
|||
|
|
#### 背景
|
|||
|
|
当前渲染存在噪点,尤其是阴影采样。Temporal Reprojection 利用历史帧信息进行降噪。
|
|||
|
|
|
|||
|
|
#### 原理
|
|||
|
|
```
|
|||
|
|
当前帧颜色 = α * 当前帧渲染 + (1-α) * 历史帧重投影颜色
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 核心步骤
|
|||
|
|
|
|||
|
|
**1. 速度缓冲 (Motion Vector)**
|
|||
|
|
```hlsl
|
|||
|
|
// 计算当前像素在上一帧的位置
|
|||
|
|
float2 motionVector = currentPos - previousPos;
|
|||
|
|
float2 prevUV = currentUV - motionVector;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**2. 历史帧采样**
|
|||
|
|
```hlsl
|
|||
|
|
float3 historyColor = historyTexture.Sample(sampler, prevUV);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**3. 颜色约束 (Clamping)**
|
|||
|
|
```hlsl
|
|||
|
|
// 防止鬼影
|
|||
|
|
float3 minColor = min(neighbors);
|
|||
|
|
float3 maxColor = max(neighbors);
|
|||
|
|
historyColor = clamp(historyColor, minColor, maxColor);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**4. 混合**
|
|||
|
|
```hlsl
|
|||
|
|
float3 finalColor = lerp(currentColor, historyColor, 0.9);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 需要的资源
|
|||
|
|
- 历史帧颜色缓冲
|
|||
|
|
- 深度缓冲(当前帧 + 历史帧)
|
|||
|
|
- Motion Vector 缓冲
|
|||
|
|
|
|||
|
|
#### 实现步骤
|
|||
|
|
1. 创建历史帧缓冲
|
|||
|
|
2. 实现 Motion Vector 计算
|
|||
|
|
3. 实现重投影逻辑
|
|||
|
|
4. 实现颜色约束
|
|||
|
|
5. 调试和参数调优
|
|||
|
|
|
|||
|
|
#### 注意事项
|
|||
|
|
- 相机快速移动时会产生鬼影
|
|||
|
|
- 需要处理物体移动(动态场景)
|
|||
|
|
- 需要处理体积自身的旋转/移动
|
|||
|
|
|
|||
|
|
#### 参考资料
|
|||
|
|
- Karis, B. "High Quality Temporal Supersampling" (SIGGRAPH 2014)
|
|||
|
|
- Schied, C., et al. "Temporal Sample Anti-Aliasing" (EGSR 2020)
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- 基础实现:1周
|
|||
|
|
- 调优和调试:1周
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2.2 Compute Shader 重构
|
|||
|
|
|
|||
|
|
#### 背景
|
|||
|
|
当前使用 Pixel Shader 进行 Ray Marching,每个像素独立计算。Compute Shader 可以更灵活地控制线程,实现更好的优化。
|
|||
|
|
|
|||
|
|
#### 优势
|
|||
|
|
|
|||
|
|
**1. 线程组共享内存**
|
|||
|
|
```hlsl
|
|||
|
|
groupshared float sharedDensity[8][8];
|
|||
|
|
// 相邻像素可共享中间计算结果
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**2. 自适应工作分配**
|
|||
|
|
```hlsl
|
|||
|
|
// 可以跳过空像素,让活跃线程处理更多
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**3. 更好的缓存利用**
|
|||
|
|
```hlsl
|
|||
|
|
// 可以手动控制数据加载,优化缓存
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 实现架构
|
|||
|
|
```
|
|||
|
|
Compute Shader Dispatch (32x32 线程组)
|
|||
|
|
↓
|
|||
|
|
每个线程处理一个像素
|
|||
|
|
↓
|
|||
|
|
Ray Marching
|
|||
|
|
↓
|
|||
|
|
输出到 UAV 纹理
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 代码示例
|
|||
|
|
```hlsl
|
|||
|
|
[numthreads(8, 8, 1)]
|
|||
|
|
void MainCS(uint3 DTid : SV_DispatchThreadID)
|
|||
|
|
{
|
|||
|
|
float2 uv = (float2(DTid.xy) + 0.5) / Resolution;
|
|||
|
|
float3 color = RayMarch(uv);
|
|||
|
|
OutputTexture[DTid.xy] = float4(color, 1);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 可实现的高级优化
|
|||
|
|
- 波前追踪 (Wavefront Tracing)
|
|||
|
|
- 空间跳跃优化
|
|||
|
|
- 自适应采样
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- 基础重构:1-2周
|
|||
|
|
- 高级优化:2-3周
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2.3 自适应步长
|
|||
|
|
|
|||
|
|
#### 背景
|
|||
|
|
当前使用固定步长,但在低密度区域可以加大步长,高密度区域减小步长。
|
|||
|
|
|
|||
|
|
#### 实现方法
|
|||
|
|
|
|||
|
|
**方法A:基于密度**
|
|||
|
|
```hlsl
|
|||
|
|
float density = SampleDensity(pos);
|
|||
|
|
float stepSize = baseStep * (1 + (1 - density) * maxScale);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**方法B:基于梯度**
|
|||
|
|
```hlsl
|
|||
|
|
float gradient = length(SampleGradient(pos));
|
|||
|
|
float stepSize = baseStep / (1 + gradient * gradientScale);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**方法C:预计算重要性场**
|
|||
|
|
```hlsl
|
|||
|
|
// 离线预计算每个区域的重要性
|
|||
|
|
// 运行时根据重要性调整步长
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 注意事项
|
|||
|
|
- 步长变化过大可能导致伪影
|
|||
|
|
- 需要注意光线一致性
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- 基础实现:2-3天
|
|||
|
|
- 调优:1周
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 三、功能扩展
|
|||
|
|
|
|||
|
|
### 3.1 多光源支持
|
|||
|
|
|
|||
|
|
#### 背景
|
|||
|
|
当前只支持单一方向光。真实场景可能有多个光源(太阳 + 天光 + 点光源)。
|
|||
|
|
|
|||
|
|
#### 实现内容
|
|||
|
|
|
|||
|
|
**1. 多方向光**
|
|||
|
|
```hlsl
|
|||
|
|
struct DirectionalLight {
|
|||
|
|
float3 direction;
|
|||
|
|
float3 color;
|
|||
|
|
float intensity;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
#define MAX_DIR_LIGHTS 4
|
|||
|
|
DirectionalLight dirLights[MAX_DIR_LIGHTS];
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**2. 点光源**
|
|||
|
|
```hlsl
|
|||
|
|
struct PointLight {
|
|||
|
|
float3 position;
|
|||
|
|
float3 color;
|
|||
|
|
float intensity;
|
|||
|
|
float radius;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
float3 SamplePointLight(PointLight light, float3 pos) {
|
|||
|
|
float3 dir = light.position - pos;
|
|||
|
|
float dist = length(dir);
|
|||
|
|
dir /= dist;
|
|||
|
|
|
|||
|
|
// 衰减
|
|||
|
|
float attenuation = 1.0 / (1.0 + dist * dist);
|
|||
|
|
|
|||
|
|
// 阴影采样
|
|||
|
|
float shadow = VolumetricShadow(pos, dir, dist);
|
|||
|
|
|
|||
|
|
return light.color * light.intensity * attenuation * shadow;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**3. 聚光灯**
|
|||
|
|
```hlsl
|
|||
|
|
struct SpotLight {
|
|||
|
|
float3 position;
|
|||
|
|
float3 direction;
|
|||
|
|
float3 color;
|
|||
|
|
float intensity;
|
|||
|
|
float innerAngle;
|
|||
|
|
float outerAngle;
|
|||
|
|
float range;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 实现步骤
|
|||
|
|
1. 设计光源数据结构
|
|||
|
|
2. 实现多方向光循环
|
|||
|
|
3. 实现点光源阴影采样
|
|||
|
|
4. 实现聚光灯锥形衰减
|
|||
|
|
5. 添加参数 UI
|
|||
|
|
|
|||
|
|
#### 性能考虑
|
|||
|
|
- 每个光源都需要额外的阴影采样
|
|||
|
|
- 限制光源数量或使用延迟渲染思路
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- 多方向光:2-3天
|
|||
|
|
- 点光源:3-4天
|
|||
|
|
- 聚光灯:2-3天
|
|||
|
|
- UI和参数:1-2天
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 3.2 大气散射
|
|||
|
|
|
|||
|
|
#### 背景
|
|||
|
|
将体积渲染与天空大气结合,实现更真实的室外场景。
|
|||
|
|
|
|||
|
|
#### 核心算法
|
|||
|
|
|
|||
|
|
**1. Rayleigh 散射**
|
|||
|
|
```hlsl
|
|||
|
|
// 空气分子散射,产生蓝天
|
|||
|
|
float3 RayleighScattering(float cosTheta, float height) {
|
|||
|
|
float3 betaR = float3(5.8e-6, 13.5e-6, 33.1e-6); // Rayleigh系数
|
|||
|
|
float density = exp(-height / 8500); // 大气密度衰减
|
|||
|
|
return betaR * density * (3 / (16 * PI)) * (1 + cosTheta * cosTheta);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**2. Mie 散射**
|
|||
|
|
```hlsl
|
|||
|
|
// 气溶胶散射,产生雾和光晕
|
|||
|
|
float3 MieScattering(float cosTheta, float g) {
|
|||
|
|
float3 betaM = float3(21e-6, 21e-6, 21e-6); // Mie系数
|
|||
|
|
return betaM * HGPhase(cosTheta, g);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**3. 单次散射积分**
|
|||
|
|
```hlsl
|
|||
|
|
float3 IntegrateAtmosphere(float3 rayOrigin, float3 rayDir, float3 sunDir) {
|
|||
|
|
float3 color = 0;
|
|||
|
|
float transmittance = 1;
|
|||
|
|
|
|||
|
|
for (int i = 0; i < steps; i++) {
|
|||
|
|
float3 pos = rayOrigin + rayDir * t;
|
|||
|
|
float height = length(pos) - earthRadius;
|
|||
|
|
|
|||
|
|
// Rayleigh
|
|||
|
|
float densityR = exp(-height / 8500);
|
|||
|
|
float3 scatterR = RayleighScattering(dot(rayDir, sunDir), height);
|
|||
|
|
|
|||
|
|
// Mie
|
|||
|
|
float densityM = exp(-height / 1200);
|
|||
|
|
float3 scatterM = MieScattering(dot(rayDir, sunDir), 0.76);
|
|||
|
|
|
|||
|
|
// 光学深度
|
|||
|
|
float3 opticalDepth = (scatterR + scatterM) * stepSize;
|
|||
|
|
transmittance *= exp(-opticalDepth);
|
|||
|
|
color += transmittance * (scatterR + scatterM) * stepSize;
|
|||
|
|
|
|||
|
|
t += stepSize;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return color;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 效果
|
|||
|
|
- 蓝天
|
|||
|
|
- 日落/日出
|
|||
|
|
- 远山大气透视
|
|||
|
|
- 太阳光晕
|
|||
|
|
|
|||
|
|
#### 参考资料
|
|||
|
|
- Nishita, T., et al. "Display of The Earth Taking into Account Atmospheric Scattering" (SIGGRAPH 1993)
|
|||
|
|
- Bruneton, E., & Neyret, F. "Precomputed Atmospheric Scattering" (EGSR 2008)
|
|||
|
|
- Hillaire, S. "A Scalable and Production Ready Sky and Atmosphere Rendering Technique" (SIGGRAPH 2020)
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- 基础实现:1周
|
|||
|
|
- 优化和调参:1周
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 3.3 体积雾
|
|||
|
|
|
|||
|
|
#### 背景
|
|||
|
|
将体积渲染作为后处理效果,与场景几何体融合。
|
|||
|
|
|
|||
|
|
#### 实现方法
|
|||
|
|
|
|||
|
|
**1. 深度重建世界坐标**
|
|||
|
|
```hlsl
|
|||
|
|
float depth = depthTexture.Sample(sampler, uv);
|
|||
|
|
float4 worldPos = mul(invViewProj, float4(ndc, depth, 1));
|
|||
|
|
worldPos /= worldPos.w;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**2. 沿视线积分**
|
|||
|
|
```hlsl
|
|||
|
|
float3 cameraPos = ...;
|
|||
|
|
float3 rayDir = normalize(worldPos - cameraPos);
|
|||
|
|
float dist = length(worldPos - cameraPos);
|
|||
|
|
|
|||
|
|
float3 color = IntegrateVolume(cameraPos, rayDir, dist);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**3. 与场景混合**
|
|||
|
|
```hlsl
|
|||
|
|
float3 sceneColor = sceneTexture.Sample(sampler, uv);
|
|||
|
|
float3 finalColor = lerp(sceneColor, volumeColor, volumeAlpha);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- 基础实现:3-4天
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 四、交互与控制
|
|||
|
|
|
|||
|
|
### 4.1 相机交互
|
|||
|
|
|
|||
|
|
#### 功能需求
|
|||
|
|
- 鼠标左键拖动:旋转视角
|
|||
|
|
- 鼠标右键拖动:平移
|
|||
|
|
- 鼠标滚轮:缩放
|
|||
|
|
- WASD:自由移动
|
|||
|
|
|
|||
|
|
#### 实现方法
|
|||
|
|
|
|||
|
|
**1. 弧球相机 (Arcball Camera)**
|
|||
|
|
```cpp
|
|||
|
|
// 围绕目标点旋转
|
|||
|
|
XMVECTOR target = XMVectorSet(0, 0, 0, 1);
|
|||
|
|
float distance = 500.0f;
|
|||
|
|
float yaw = 0, pitch = 0;
|
|||
|
|
|
|||
|
|
void UpdateCamera() {
|
|||
|
|
XMVECTOR eye = XMVectorSet(
|
|||
|
|
sin(yaw) * cos(pitch) * distance,
|
|||
|
|
sin(pitch) * distance,
|
|||
|
|
cos(yaw) * cos(pitch) * distance,
|
|||
|
|
1.0f
|
|||
|
|
);
|
|||
|
|
viewMatrix = XMMatrixLookAtLH(eye, target, XMVectorSet(0, 1, 0, 0));
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**2. 自由相机 (Free Camera)**
|
|||
|
|
```cpp
|
|||
|
|
XMVECTOR position;
|
|||
|
|
float yaw = 0, pitch = 0;
|
|||
|
|
|
|||
|
|
void Move(float dx, float dy, float dz) {
|
|||
|
|
XMVECTOR forward = XMVectorSet(sin(yaw), 0, cos(yaw), 0);
|
|||
|
|
XMVECTOR right = XMVectorSet(cos(yaw), 0, -sin(yaw), 0);
|
|||
|
|
XMVECTOR up = XMVectorSet(0, 1, 0, 0);
|
|||
|
|
|
|||
|
|
position += forward * dz + right * dx + up * dy;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void Rotate(float dYaw, float dPitch) {
|
|||
|
|
yaw += dYaw;
|
|||
|
|
pitch += dPitch;
|
|||
|
|
pitch = clamp(pitch, -PI/2 + 0.01f, PI/2 - 0.01f);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 输入处理
|
|||
|
|
```cpp
|
|||
|
|
// Windows 消息处理
|
|||
|
|
case WM_LBUTTONDOWN:
|
|||
|
|
isDragging = true;
|
|||
|
|
lastMousePos = GET_MOUSE_POS(lParam);
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case WM_MOUSEMOVE:
|
|||
|
|
if (isDragging) {
|
|||
|
|
POINT currentPos = GET_MOUSE_POS(lParam);
|
|||
|
|
float dx = (currentPos.x - lastMousePos.x) * sensitivity;
|
|||
|
|
float dy = (currentPos.y - lastMousePos.y) * sensitivity;
|
|||
|
|
camera.Rotate(dx, dy);
|
|||
|
|
lastMousePos = currentPos;
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- 弧球相机:2-3天
|
|||
|
|
- 自由相机:2-3天
|
|||
|
|
- 输入处理:1-2天
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4.2 实时参数 UI
|
|||
|
|
|
|||
|
|
#### 功能需求
|
|||
|
|
- 密度缩放 (Density Scale)
|
|||
|
|
- 步长 (Step Size)
|
|||
|
|
- 最大步数 (Max Steps)
|
|||
|
|
- 光照方向 (Light Direction)
|
|||
|
|
- 光照强度 (Light Intensity)
|
|||
|
|
- 相函数参数 (Phase Function G)
|
|||
|
|
- 阴影采样数 (Shadow Samples)
|
|||
|
|
|
|||
|
|
#### UI 库选择
|
|||
|
|
|
|||
|
|
**选项A:Dear ImGui (推荐)**
|
|||
|
|
```cpp
|
|||
|
|
#include "imgui.h"
|
|||
|
|
|
|||
|
|
void RenderUI() {
|
|||
|
|
ImGui::Begin("Volume Parameters");
|
|||
|
|
|
|||
|
|
ImGui::SliderFloat("Density Scale", &densityScale, 0.0f, 1.0f);
|
|||
|
|
ImGui::SliderFloat("Step Size", &stepSize, 0.1f, 5.0f);
|
|||
|
|
ImGui::SliderInt("Max Steps", &maxSteps, 100, 5000);
|
|||
|
|
ImGui::SliderFloat3("Light Dir", lightDir, -1.0f, 1.0f);
|
|||
|
|
ImGui::SliderFloat("Phase G", &phaseG, -0.99f, 0.99f);
|
|||
|
|
|
|||
|
|
ImGui::End();
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**选项B:Win32 原生控件**
|
|||
|
|
- 不需要额外库
|
|||
|
|
- 实现复杂
|
|||
|
|
- 样式受限
|
|||
|
|
|
|||
|
|
#### 集成步骤
|
|||
|
|
1. 添加 ImGui 到项目
|
|||
|
|
2. 实现 D3D12 后端
|
|||
|
|
3. 创建参数面板
|
|||
|
|
4. 绑定参数到常量缓冲区
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- ImGui 集成:2-3天
|
|||
|
|
- 参数面板:1-2天
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4.3 体积数据编辑
|
|||
|
|
|
|||
|
|
#### 功能需求
|
|||
|
|
- 实时修改体积密度
|
|||
|
|
- 添加/删除体素
|
|||
|
|
- 画笔工具
|
|||
|
|
|
|||
|
|
#### 实现思路
|
|||
|
|
|
|||
|
|
**1. CPU 端编辑**
|
|||
|
|
```cpp
|
|||
|
|
// 读取当前体积数据
|
|||
|
|
std::vector<float> volumeData = ReadVolumeData();
|
|||
|
|
|
|||
|
|
// 修改
|
|||
|
|
int x = floor(pos.x / voxelSize);
|
|||
|
|
int y = floor(pos.y / voxelSize);
|
|||
|
|
int z = floor(pos.z / voxelSize);
|
|||
|
|
volumeData[index(x,y,z)] += strength;
|
|||
|
|
|
|||
|
|
// 重新上传 GPU
|
|||
|
|
UpdateGPUBuffer(volumeData);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**2. 画笔工具**
|
|||
|
|
```cpp
|
|||
|
|
void PaintVolume(float3 center, float radius, float strength) {
|
|||
|
|
for each voxel in range:
|
|||
|
|
float dist = length(voxelPos - center);
|
|||
|
|
if (dist < radius) {
|
|||
|
|
float falloff = 1 - dist / radius;
|
|||
|
|
voxel.density += strength * falloff;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 挑战
|
|||
|
|
- NanoVDB 是只读结构,需要重建
|
|||
|
|
- 或改用 3D Texture
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- 基础编辑:1周
|
|||
|
|
- 画笔工具:3-4天
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 五、数据生成
|
|||
|
|
|
|||
|
|
### 5.1 程序化云生成
|
|||
|
|
|
|||
|
|
#### 背景
|
|||
|
|
使用 Noise 函数程序化生成云层,无需外部数据文件。
|
|||
|
|
|
|||
|
|
#### 核心算法
|
|||
|
|
|
|||
|
|
**1. Perlin Noise**
|
|||
|
|
```hlsl
|
|||
|
|
float perlinNoise(float3 p) {
|
|||
|
|
// 梯度噪声实现
|
|||
|
|
float3 i = floor(p);
|
|||
|
|
float3 f = frac(p);
|
|||
|
|
float3 u = f * f * (3 - 2 * f); // smoothstep
|
|||
|
|
|
|||
|
|
return lerp(
|
|||
|
|
lerp(lerp(hash(i + float3(0,0,0)), hash(i + float3(1,0,0)), u.x),
|
|||
|
|
lerp(hash(i + float3(0,1,0)), hash(i + float3(1,1,0)), u.x), u.y),
|
|||
|
|
lerp(lerp(hash(i + float3(0,0,1)), hash(i + float3(1,0,1)), u.x),
|
|||
|
|
lerp(hash(i + float3(0,1,1)), hash(i + float3(1,1,1)), u.x), u.y),
|
|||
|
|
u.z
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**2. Fractal Brownian Motion (FBM)**
|
|||
|
|
```hlsl
|
|||
|
|
float fbm(float3 p, int octaves) {
|
|||
|
|
float value = 0;
|
|||
|
|
float amplitude = 0.5;
|
|||
|
|
float frequency = 1;
|
|||
|
|
|
|||
|
|
for (int i = 0; i < octaves; i++) {
|
|||
|
|
value += amplitude * perlinNoise(p * frequency);
|
|||
|
|
amplitude *= 0.5;
|
|||
|
|
frequency *= 2;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return value;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**3. 云密度函数**
|
|||
|
|
```hlsl
|
|||
|
|
float cloudDensity(float3 pos) {
|
|||
|
|
// 基础形状(球体或盒子)
|
|||
|
|
float shape = sphere(pos, center, radius);
|
|||
|
|
|
|||
|
|
// 噪声扰动
|
|||
|
|
float noise = fbm(pos * scale, 5);
|
|||
|
|
|
|||
|
|
// 密度
|
|||
|
|
float density = shape * noise;
|
|||
|
|
|
|||
|
|
// 阈值和锐化
|
|||
|
|
density = smoothstep(threshold, threshold + edgeSoftness, density);
|
|||
|
|
|
|||
|
|
return density;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**4. Worley Noise (细胞噪声)**
|
|||
|
|
```hlsl
|
|||
|
|
// 用于云的细节和孔洞
|
|||
|
|
float worleyNoise(float3 p) {
|
|||
|
|
float minDist = 1e10;
|
|||
|
|
int3 cell = floor(p);
|
|||
|
|
|
|||
|
|
for (int x = -1; x <= 1; x++)
|
|||
|
|
for (int y = -1; y <= 1; y++)
|
|||
|
|
for (int z = -1; z <= 1; z++) {
|
|||
|
|
int3 neighbor = cell + int3(x, y, z);
|
|||
|
|
float3 point = neighbor + hash3(neighbor);
|
|||
|
|
float dist = distance(p, point);
|
|||
|
|
minDist = min(minDist, dist);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return minDist;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 云层模型
|
|||
|
|
|
|||
|
|
**Cumulus (积云)**
|
|||
|
|
- 底部平坦,顶部蓬松
|
|||
|
|
- 使用多个 FBM 叠加
|
|||
|
|
- 高度衰减
|
|||
|
|
|
|||
|
|
**Stratus (层云)**
|
|||
|
|
- 薄层状
|
|||
|
|
- 低频噪声
|
|||
|
|
- 均匀分布
|
|||
|
|
|
|||
|
|
**Cirrus (卷云)**
|
|||
|
|
- 高空,纤维状
|
|||
|
|
- 高频噪声
|
|||
|
|
- 方向性
|
|||
|
|
|
|||
|
|
#### 动态演化
|
|||
|
|
```hlsl
|
|||
|
|
// 随时间演化
|
|||
|
|
float density = cloudDensity(pos + wind * time);
|
|||
|
|
|
|||
|
|
// 形态变化
|
|||
|
|
float growth = sin(time * growthSpeed);
|
|||
|
|
density *= 1 + growth * 0.2;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 参考资料
|
|||
|
|
- Ebert, D. S., et al. "Texturing & Modeling: A Procedural Approach"
|
|||
|
|
- Gili, A. "The Real-Time Volumetric Cloudscapes of Horizon Zero Dawn" (SIGGRAPH 2015)
|
|||
|
|
- Hillaire, S. "Physically Based Sky, Atmosphere and Cloud Rendering in Frostbite" (SIGGRAPH 2016)
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- Perlin/FBM 实现:2-3天
|
|||
|
|
- Worley Noise:2天
|
|||
|
|
- 云密度函数:3-4天
|
|||
|
|
- 参数调优:1周
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 5.2 离线流体模拟器
|
|||
|
|
|
|||
|
|
#### 背景
|
|||
|
|
实现 CPU 端流体模拟,导出 OpenVDB/NanoVDB 格式。
|
|||
|
|
|
|||
|
|
#### 核心算法:Navier-Stokes 方程
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
∂u/∂t = -(u·∇)u - ∇p/ρ + ν∇²u + f
|
|||
|
|
∇·u = 0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
其中:
|
|||
|
|
- u:速度场
|
|||
|
|
- p:压力场
|
|||
|
|
- ρ:密度
|
|||
|
|
- ν:粘度
|
|||
|
|
- f:外力
|
|||
|
|
|
|||
|
|
#### 求解步骤
|
|||
|
|
|
|||
|
|
**1. 对流 (Advection)**
|
|||
|
|
```cpp
|
|||
|
|
void advect(VelocityField& u, ScalarField& q, float dt) {
|
|||
|
|
for each cell (i, j, k):
|
|||
|
|
// 回溯粒子位置
|
|||
|
|
Vec3 pos = Vec3(i, j, k) - u(i, j, k) * dt;
|
|||
|
|
// 插值采样
|
|||
|
|
q_new(i, j, k) = interpolate(q, pos);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**2. 扩散 (Diffusion)**
|
|||
|
|
```cpp
|
|||
|
|
void diffuse(ScalarField& q, float viscosity, float dt) {
|
|||
|
|
// Jacobi 迭代
|
|||
|
|
for (int iter = 0; iter < iterations; iter++) {
|
|||
|
|
for each cell (i, j, k):
|
|||
|
|
q_new(i, j, k) = (q(i-1,j,k) + q(i+1,j,k)
|
|||
|
|
+ q(i,j-1,k) + q(i,j+1,k)
|
|||
|
|
+ q(i,j,k-1) + q(i,j,k+1)
|
|||
|
|
+ q_old(i,j,k) * alpha) / (6 + alpha);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**3. 压力求解 (Pressure Solve)**
|
|||
|
|
```cpp
|
|||
|
|
void project(VelocityField& u) {
|
|||
|
|
// 计算散度
|
|||
|
|
for each cell:
|
|||
|
|
divergence(i, j, k) = -0.5 * (u(i+1,j,k).x - u(i-1,j,k).x
|
|||
|
|
+ u(i,j+1,k).y - u(i,j-1,k).y
|
|||
|
|
+ u(i,j,k+1).z - u(i,j,k-1).z);
|
|||
|
|
|
|||
|
|
// Jacobi 迭代求解压力
|
|||
|
|
for (int iter = 0; iter < iterations; iter++) {
|
|||
|
|
for each cell:
|
|||
|
|
p_new(i, j, k) = (p(i-1,j,k) + p(i+1,j,k)
|
|||
|
|
+ p(i,j-1,k) + p(i,j+1,k)
|
|||
|
|
+ p(i,j,k-1) + p(i,j,k+1)
|
|||
|
|
- divergence(i,j,k)) / 6;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 速度校正
|
|||
|
|
for each cell:
|
|||
|
|
u(i, j, k).x -= 0.5 * (p(i+1,j,k) - p(i-1,j,k));
|
|||
|
|
u(i, j, k).y -= 0.5 * (p(i,j+1,k) - p(i,j-1,k));
|
|||
|
|
u(i, j, k).z -= 0.5 * (p(i,j,k+1) - p(i,j,k-1));
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**4. 外力 (External Forces)**
|
|||
|
|
```cpp
|
|||
|
|
void addForce(VelocityField& u, Vec3 forcePos, Vec3 forceDir, float strength) {
|
|||
|
|
for each cell near forcePos:
|
|||
|
|
u(i, j, k) += forceDir * strength * falloff(distance);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 烟雾模拟扩展
|
|||
|
|
|
|||
|
|
**密度场**
|
|||
|
|
```cpp
|
|||
|
|
// 对流密度
|
|||
|
|
advect(u, density, dt);
|
|||
|
|
|
|||
|
|
// 添加烟雾源
|
|||
|
|
for each source cell:
|
|||
|
|
density(i, j, k) = sourceStrength;
|
|||
|
|
|
|||
|
|
// 衰减
|
|||
|
|
density *= (1 - decay * dt);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**温度场**
|
|||
|
|
```cpp
|
|||
|
|
// 浮力
|
|||
|
|
for each cell:
|
|||
|
|
float buoyancy = temperature(i,j,k) * buoyancyStrength;
|
|||
|
|
u(i,j,k).y += buoyancy * dt;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**涡度约束 (Vorticity Confinement)**
|
|||
|
|
```cpp
|
|||
|
|
// 增加细节
|
|||
|
|
Vec3 curl = computeCurl(u);
|
|||
|
|
Vec3 N = normalize(curl);
|
|||
|
|
Vec3 omega = curl;
|
|||
|
|
u += dt * vorticityStrength * N * omega;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### OpenVDB 集成
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
#include <openvdb/openvdb.h>
|
|||
|
|
#include <openvdb/tools/Composite.h>
|
|||
|
|
|
|||
|
|
void exportToVDB(const ScalarField& density, const std::string& filename) {
|
|||
|
|
openvdb::initialize();
|
|||
|
|
|
|||
|
|
// 创建网格
|
|||
|
|
openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create();
|
|||
|
|
openvdb::FloatGrid::Accessor accessor = grid->getAccessor();
|
|||
|
|
|
|||
|
|
// 填充数据
|
|||
|
|
for each active voxel (i, j, k):
|
|||
|
|
openvdb::Coord coord(i, j, k);
|
|||
|
|
accessor.setValue(coord, density(i, j, k));
|
|||
|
|
|
|||
|
|
// 保存
|
|||
|
|
openvdb::io::File file(filename);
|
|||
|
|
file.write({grid});
|
|||
|
|
file.close();
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### NanoVDB 转换
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
#include <nanovdb/util/OpenToNanoVDB.h>
|
|||
|
|
|
|||
|
|
void convertToNanoVDB(const std::string& openvdbFile, const std::string& nanovdbFile) {
|
|||
|
|
// 读取 OpenVDB
|
|||
|
|
openvdb::io::File file(openvdbFile);
|
|||
|
|
openvdb::GridBase::Ptr baseGrid = file.readGrid("density");
|
|||
|
|
|
|||
|
|
// 转换
|
|||
|
|
auto handle = nanovdb::openToNanoVDB(baseGrid);
|
|||
|
|
|
|||
|
|
// 保存
|
|||
|
|
nanovdb::io::writeToFile(nanovdbFile, handle);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 工作流程
|
|||
|
|
```
|
|||
|
|
1. 初始化网格和场
|
|||
|
|
2. 循环帧:
|
|||
|
|
a. 对流速度场
|
|||
|
|
b. 添加外力
|
|||
|
|
c. 扩散(粘度)
|
|||
|
|
d. 压力求解
|
|||
|
|
e. 对流密度/温度
|
|||
|
|
f. 导出当前帧
|
|||
|
|
3. 批量转换为 NanoVDB
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 参考资料
|
|||
|
|
- Bridson, R. "Fluid Simulation for Computer Graphics" (书籍)
|
|||
|
|
- Stam, J. "Stable Fluids" (SIGGRAPH 1999)
|
|||
|
|
- Fedkiw, R., et al. "Visual Simulation of Smoke" (SIGGRAPH 2001)
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- 基础流体求解器:2周
|
|||
|
|
- 烟雾扩展:1周
|
|||
|
|
- OpenVDB 集成:1周
|
|||
|
|
- NanoVDB 导出:3-4天
|
|||
|
|
- UI 和参数:1周
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 5.3 多格式导入
|
|||
|
|
|
|||
|
|
#### 支持格式
|
|||
|
|
|
|||
|
|
| 格式 | 描述 | 库 |
|
|||
|
|
|------|------|-----|
|
|||
|
|
| OpenVDB (.vdb) | 行业标准 | openvdb 库 |
|
|||
|
|
| DDS 体积纹理 | DirectX 格式 | DirectX |
|
|||
|
|
| Raw 体素数据 | 自定义格式 | 手动解析 |
|
|||
|
|
|
|||
|
|
#### 实现思路
|
|||
|
|
```cpp
|
|||
|
|
class VolumeLoader {
|
|||
|
|
public:
|
|||
|
|
virtual bool load(const std::string& path) = 0;
|
|||
|
|
virtual float sample(int x, int y, int z) const = 0;
|
|||
|
|
virtual Vec3 size() const = 0;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
class OpenVDBLoader : public VolumeLoader { ... };
|
|||
|
|
class DDSLoader : public VolumeLoader { ... };
|
|||
|
|
class RawLoader : public VolumeLoader { ... };
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- OpenVDB 支持:3-4天
|
|||
|
|
- DDS 支持:2-3天
|
|||
|
|
- 统一接口:1-2天
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 六、高级算法
|
|||
|
|
|
|||
|
|
### 6.1 重要性采样
|
|||
|
|
|
|||
|
|
#### 背景
|
|||
|
|
根据体积密度分布,在重要区域增加采样密度,在空区域减少采样。
|
|||
|
|
|
|||
|
|
#### 方法
|
|||
|
|
|
|||
|
|
**1. 基于密度的重要性采样**
|
|||
|
|
```hlsl
|
|||
|
|
// 根据密度调整采样概率
|
|||
|
|
float pdf = density / totalDensity;
|
|||
|
|
float step = baseStep / pdf;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**2. 分层采样**
|
|||
|
|
```hlsl
|
|||
|
|
// 将光线分成段,每段内均匀采样
|
|||
|
|
for each segment:
|
|||
|
|
float randomOffset = rand();
|
|||
|
|
float samplePos = segmentStart + randomOffset * segmentLength;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**3. 多重要性采样**
|
|||
|
|
```hlsl
|
|||
|
|
// 结合多种采样策略
|
|||
|
|
float sampleByDensity = ...;
|
|||
|
|
float sampleByLight = ...;
|
|||
|
|
float weight = misWeight(densityWeight, lightWeight);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- 基础实现:3-4天
|
|||
|
|
- 优化:1周
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 6.2 屏幕空间降噪
|
|||
|
|
|
|||
|
|
#### 背景
|
|||
|
|
低采样率导致的噪点,通过屏幕空间滤波器降噪。
|
|||
|
|
|
|||
|
|
#### 算法
|
|||
|
|
|
|||
|
|
**1. 双边滤波 (Bilateral Filter)**
|
|||
|
|
```hlsl
|
|||
|
|
float3 bilateralFilter(float2 uv, float3 centerColor, float centerDepth) {
|
|||
|
|
float3 sum = 0;
|
|||
|
|
float weightSum = 0;
|
|||
|
|
|
|||
|
|
for each neighbor:
|
|||
|
|
float3 neighborColor = sample(neighborUV);
|
|||
|
|
float neighborDepth = sampleDepth(neighborUV);
|
|||
|
|
|
|||
|
|
float spatialWeight = gaussian(distance);
|
|||
|
|
float rangeWeight = gaussian(length(neighborColor - centerColor));
|
|||
|
|
float depthWeight = gaussian(abs(neighborDepth - centerDepth));
|
|||
|
|
|
|||
|
|
float weight = spatialWeight * rangeWeight * depthWeight;
|
|||
|
|
sum += neighborColor * weight;
|
|||
|
|
weightSum += weight;
|
|||
|
|
|
|||
|
|
return sum / weightSum;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**2. SVGF (Spatiotemporal Variance-Guided Filtering)**
|
|||
|
|
- Temporal 累积
|
|||
|
|
- 空间滤波
|
|||
|
|
- 方差估计
|
|||
|
|
|
|||
|
|
**3. BMFR (Blockwise Multi-Order Feature Regression)**
|
|||
|
|
- 块处理
|
|||
|
|
- 特征回归
|
|||
|
|
|
|||
|
|
#### 参考资料
|
|||
|
|
- Schied, C., et al. "Spatiotemporal Variance-Guided Filtering" (EGSR 2017)
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- 双边滤波:2-3天
|
|||
|
|
- SVGF:2-3周
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 七、场景融合
|
|||
|
|
|
|||
|
|
### 7.1 体积与场景遮挡
|
|||
|
|
|
|||
|
|
#### 问题
|
|||
|
|
当前体积渲染不考虑场景几何体的遮挡。
|
|||
|
|
|
|||
|
|
#### 解决方案
|
|||
|
|
|
|||
|
|
**1. 深度测试**
|
|||
|
|
```hlsl
|
|||
|
|
// 从深度缓冲重建世界坐标
|
|||
|
|
float depth = depthTexture.Sample(sampler, uv);
|
|||
|
|
float3 sceneWorldPos = reconstructWorldPos(uv, depth);
|
|||
|
|
|
|||
|
|
// 计算到场景的距离
|
|||
|
|
float sceneDist = length(sceneWorldPos - cameraPos);
|
|||
|
|
|
|||
|
|
// Ray Marching 时限制最大距离
|
|||
|
|
float tmax = min(rayTmax, sceneDist);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**2. 阴影遮挡**
|
|||
|
|
```hlsl
|
|||
|
|
// 场景几何体遮挡体积
|
|||
|
|
float sceneShadow = 1.0;
|
|||
|
|
if (isOccludedByScene(pos)) {
|
|||
|
|
sceneShadow = 0.0;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- 深度测试:2-3天
|
|||
|
|
- 完整遮挡:1周
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 7.2 阴影接收
|
|||
|
|
|
|||
|
|
#### 问题
|
|||
|
|
体积应该接收场景几何体的阴影。
|
|||
|
|
|
|||
|
|
#### 解决方案
|
|||
|
|
```hlsl
|
|||
|
|
float sceneShadow = SampleSceneShadowMap(pos);
|
|||
|
|
float3 S = sigmaS * phase_function() * shadow * sceneShadow;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- 基础实现:2-3天
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 八、应用场景
|
|||
|
|
|
|||
|
|
### 8.1 动态天气系统
|
|||
|
|
|
|||
|
|
#### 功能
|
|||
|
|
- 云层动态演化
|
|||
|
|
- 日出日落光照变化
|
|||
|
|
- 雨雾效果
|
|||
|
|
|
|||
|
|
#### 实现
|
|||
|
|
```hlsl
|
|||
|
|
// 云层演化
|
|||
|
|
float timeOfDay = sin(time * dayCycleSpeed);
|
|||
|
|
float cloudDensity = baseDensity * (0.5 + 0.5 * timeOfDay);
|
|||
|
|
|
|||
|
|
// 光照变化
|
|||
|
|
float3 sunColor = lerp(sunriseColor, noonColor, timeOfDay);
|
|||
|
|
float3 ambientColor = lerp(nightAmbient, dayAmbient, timeOfDay);
|
|||
|
|
|
|||
|
|
// 雾气
|
|||
|
|
float fogDensity = fogBase + fogVariation * sin(time);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- 1-2周
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 8.2 火焰/爆炸效果
|
|||
|
|
|
|||
|
|
#### 需求
|
|||
|
|
- 温度场驱动
|
|||
|
|
- 黑体辐射颜色
|
|||
|
|
- 自发光
|
|||
|
|
|
|||
|
|
#### 实现
|
|||
|
|
```hlsl
|
|||
|
|
// 温度到颜色 (黑体辐射)
|
|||
|
|
float3 temperatureToColor(float T) {
|
|||
|
|
// Planck 定律近似
|
|||
|
|
float x = T / 10000.0;
|
|||
|
|
float3 color;
|
|||
|
|
color.r = 1.0;
|
|||
|
|
color.g = clamp(1.0 - exp(-5.0 * x), 0, 1);
|
|||
|
|
color.b = clamp(1.0 - exp(-10.0 * x), 0, 1);
|
|||
|
|
return color;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 发光
|
|||
|
|
float3 emission = temperatureToColor(temperature) * emissionIntensity;
|
|||
|
|
float3 S = sigmaS * phase_function() * shadow + emission;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 工作量估计
|
|||
|
|
- 1周
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 九、推荐方案总结
|
|||
|
|
|
|||
|
|
### 方案A:渲染深化(学术向)
|
|||
|
|
```
|
|||
|
|
多散射 + 相函数 + Temporal Reprojection
|
|||
|
|
```
|
|||
|
|
- 难度:⭐⭐⭐⭐
|
|||
|
|
- 工作量:4-5周
|
|||
|
|
- 论文价值:高
|
|||
|
|
- 适合:想发论文/读研
|
|||
|
|
|
|||
|
|
### 方案B:工程完整(展示向)
|
|||
|
|
```
|
|||
|
|
相机交互 + 参数UI + 多光源 + 大气散射
|
|||
|
|
```
|
|||
|
|
- 难度:⭐⭐⭐
|
|||
|
|
- 工作量:3-4周
|
|||
|
|
- 展示效果:好
|
|||
|
|
- 适合:答辩演示
|
|||
|
|
|
|||
|
|
### 方案C:离线工具链(创新向)
|
|||
|
|
```
|
|||
|
|
离线流体模拟器 + OpenVDB集成 + NanoVDB导出
|
|||
|
|
```
|
|||
|
|
- 难度:⭐⭐⭐⭐⭐
|
|||
|
|
- 工作量:6-8周
|
|||
|
|
- 创新性:强
|
|||
|
|
- 适合:想深入图形学
|
|||
|
|
|
|||
|
|
### 方案D:程序化生成(创意向)
|
|||
|
|
```
|
|||
|
|
程序化云生成 + 参数控制 + 动态天气
|
|||
|
|
```
|
|||
|
|
- 难度:⭐⭐⭐⭐
|
|||
|
|
- 工作量:3-4周
|
|||
|
|
- 展示性:强
|
|||
|
|
- 适合:想展示效果
|
|||
|
|
|
|||
|
|
### 方案E:平衡方案(推荐)
|
|||
|
|
```
|
|||
|
|
多散射 + Temporal Reprojection + 相函数 + 程序化扰动动画
|
|||
|
|
```
|
|||
|
|
- 难度:⭐⭐⭐⭐
|
|||
|
|
- 工作量:4-5周
|
|||
|
|
- 效果提升明显
|
|||
|
|
- 有技术深度
|
|||
|
|
- 工作量可控
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 十、时间规划建议
|
|||
|
|
|
|||
|
|
### 单方向深入(4-5周)
|
|||
|
|
|
|||
|
|
| 阶段 | 内容 | 时间 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| 第1周 | 多散射基础实现 | 7天 |
|
|||
|
|
| 第2周 | 多散射优化 + 相函数 | 7天 |
|
|||
|
|
| 第3周 | Temporal Reprojection | 7天 |
|
|||
|
|
| 第4周 | 程序化扰动 + 效果调优 | 7天 |
|
|||
|
|
| 第5周 | 文档 + 测试 | 7天 |
|
|||
|
|
|
|||
|
|
### 多方向扩展(6-8周)
|
|||
|
|
|
|||
|
|
| 阶段 | 内容 | 时间 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| 第1-2周 | 渲染效果提升 | 14天 |
|
|||
|
|
| 第3-4周 | 性能优化 | 14天 |
|
|||
|
|
| 第5-6周 | 交互与控制 | 14天 |
|
|||
|
|
| 第7周 | 效果调优 | 7天 |
|
|||
|
|
| 第8周 | 文档 + 测试 | 7天 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 十一、参考资料汇总
|
|||
|
|
|
|||
|
|
### 论文
|
|||
|
|
1. Jensen, H. W., et al. "A Practical Model for Subsurface Light Transport" (SIGGRAPH 2001)
|
|||
|
|
2. Kutz, P., et al. "Spectral and Decomposition Tracking for Rendering Heterogeneous Volumes" (SIGGRAPH 2017)
|
|||
|
|
3. Stam, J. "Stable Fluids" (SIGGRAPH 1999)
|
|||
|
|
4. Fedkiw, R., et al. "Visual Simulation of Smoke" (SIGGRAPH 2001)
|
|||
|
|
5. Karis, B. "High Quality Temporal Supersampling" (SIGGRAPH 2014)
|
|||
|
|
6. Schied, C., et al. "Spatiotemporal Variance-Guided Filtering" (EGSR 2017)
|
|||
|
|
|
|||
|
|
### 书籍
|
|||
|
|
1. Bridson, R. "Fluid Simulation for Computer Graphics"
|
|||
|
|
2. Ebert, D. S., et al. "Texturing & Modeling: A Procedural Approach"
|
|||
|
|
3. Pharr, M., et al. "Physically Based Rendering: From Theory to Implementation"
|
|||
|
|
|
|||
|
|
### 开源项目
|
|||
|
|
1. OpenVDB: https://github.com/AcademySoftwareFoundation/openvdb
|
|||
|
|
2. NanoVDB: https://github.com/AcademySoftwareFoundation/nanovdb
|
|||
|
|
3. Mantaflow: https://mantaflow.uni-mainz.de/
|
|||
|
|
4. Dear ImGui: https://github.com/ocornut/imgui
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
*文档版本: 1.0*
|
|||
|
|
*更新日期: 2026年3月*
|