Files
XCEngine/docs/深入方向规划.md

29 KiB
Raw Blame History

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) 相函数

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 相函数

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 相函数

// 查表实现,更精确但复杂
// 通常用HG近似
  • 物理最正确
  • 计算复杂
  • 通常用双HG近似

4. Rayleigh 相函数

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天
  • 参数UI1天

1.3 体积自发光 (Volumetric Emission)

背景

火焰、熔岩、霓虹气体等发光体积。除了散射外部光源,自身也发出光。

实现方法

// 在着色器中添加自发光项
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)

// 计算当前像素在上一帧的位置
float2 motionVector = currentPos - previousPos;
float2 prevUV = currentUV - motionVector;

2. 历史帧采样

float3 historyColor = historyTexture.Sample(sampler, prevUV);

3. 颜色约束 (Clamping)

// 防止鬼影
float3 minColor = min(neighbors);
float3 maxColor = max(neighbors);
historyColor = clamp(historyColor, minColor, maxColor);

4. 混合

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. 线程组共享内存

groupshared float sharedDensity[8][8];
// 相邻像素可共享中间计算结果

2. 自适应工作分配

// 可以跳过空像素,让活跃线程处理更多

3. 更好的缓存利用

// 可以手动控制数据加载,优化缓存

实现架构

Compute Shader Dispatch (32x32 线程组)
    ↓
每个线程处理一个像素
    ↓
Ray Marching
    ↓
输出到 UAV 纹理

代码示例

[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基于密度

float density = SampleDensity(pos);
float stepSize = baseStep * (1 + (1 - density) * maxScale);

方法B基于梯度

float gradient = length(SampleGradient(pos));
float stepSize = baseStep / (1 + gradient * gradientScale);

方法C预计算重要性场

// 离线预计算每个区域的重要性
// 运行时根据重要性调整步长

注意事项

  • 步长变化过大可能导致伪影
  • 需要注意光线一致性

工作量估计

  • 基础实现2-3天
  • 调优1周

三、功能扩展

3.1 多光源支持

背景

当前只支持单一方向光。真实场景可能有多个光源(太阳 + 天光 + 点光源)。

实现内容

1. 多方向光

struct DirectionalLight {
    float3 direction;
    float3 color;
    float intensity;
};

#define MAX_DIR_LIGHTS 4
DirectionalLight dirLights[MAX_DIR_LIGHTS];

2. 点光源

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. 聚光灯

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 散射

// 空气分子散射,产生蓝天
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 散射

// 气溶胶散射,产生雾和光晕
float3 MieScattering(float cosTheta, float g) {
    float3 betaM = float3(21e-6, 21e-6, 21e-6); // Mie系数
    return betaM * HGPhase(cosTheta, g);
}

3. 单次散射积分

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. 深度重建世界坐标

float depth = depthTexture.Sample(sampler, uv);
float4 worldPos = mul(invViewProj, float4(ndc, depth, 1));
worldPos /= worldPos.w;

2. 沿视线积分

float3 cameraPos = ...;
float3 rayDir = normalize(worldPos - cameraPos);
float dist = length(worldPos - cameraPos);

float3 color = IntegrateVolume(cameraPos, rayDir, dist);

3. 与场景混合

float3 sceneColor = sceneTexture.Sample(sampler, uv);
float3 finalColor = lerp(sceneColor, volumeColor, volumeAlpha);

工作量估计

  • 基础实现3-4天

四、交互与控制

4.1 相机交互

功能需求

  • 鼠标左键拖动:旋转视角
  • 鼠标右键拖动:平移
  • 鼠标滚轮:缩放
  • WASD自由移动

实现方法

1. 弧球相机 (Arcball Camera)

// 围绕目标点旋转
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)

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);
}

输入处理

// 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 库选择

选项ADear ImGui (推荐)

#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();
}

选项BWin32 原生控件

  • 不需要额外库
  • 实现复杂
  • 样式受限

集成步骤

  1. 添加 ImGui 到项目
  2. 实现 D3D12 后端
  3. 创建参数面板
  4. 绑定参数到常量缓冲区

工作量估计

  • ImGui 集成2-3天
  • 参数面板1-2天

4.3 体积数据编辑

功能需求

  • 实时修改体积密度
  • 添加/删除体素
  • 画笔工具

实现思路

1. CPU 端编辑

// 读取当前体积数据
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. 画笔工具

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

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)

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. 云密度函数

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 (细胞噪声)

// 用于云的细节和孔洞
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 (卷云)

  • 高空,纤维状
  • 高频噪声
  • 方向性

动态演化

// 随时间演化
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 Noise2天
  • 云密度函数3-4天
  • 参数调优1周

5.2 离线流体模拟器

背景

实现 CPU 端流体模拟,导出 OpenVDB/NanoVDB 格式。

核心算法Navier-Stokes 方程

∂u/∂t = -(u·∇)u - ∇p/ρ + ν∇²u + f
∇·u = 0

其中:

  • u速度场
  • p压力场
  • ρ:密度
  • ν:粘度
  • f外力

求解步骤

1. 对流 (Advection)

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)

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)

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)

void addForce(VelocityField& u, Vec3 forcePos, Vec3 forceDir, float strength) {
    for each cell near forcePos:
        u(i, j, k) += forceDir * strength * falloff(distance);
}

烟雾模拟扩展

密度场

// 对流密度
advect(u, density, dt);

// 添加烟雾源
for each source cell:
    density(i, j, k) = sourceStrength;

// 衰减
density *= (1 - decay * dt);

温度场

// 浮力
for each cell:
    float buoyancy = temperature(i,j,k) * buoyancyStrength;
    u(i,j,k).y += buoyancy * dt;

涡度约束 (Vorticity Confinement)

// 增加细节
Vec3 curl = computeCurl(u);
Vec3 N = normalize(curl);
Vec3 omega = curl;
u += dt * vorticityStrength * N * omega;

OpenVDB 集成

#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 转换

#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 体素数据 自定义格式 手动解析

实现思路

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. 基于密度的重要性采样

// 根据密度调整采样概率
float pdf = density / totalDensity;
float step = baseStep / pdf;

2. 分层采样

// 将光线分成段,每段内均匀采样
for each segment:
    float randomOffset = rand();
    float samplePos = segmentStart + randomOffset * segmentLength;

3. 多重要性采样

// 结合多种采样策略
float sampleByDensity = ...;
float sampleByLight = ...;
float weight = misWeight(densityWeight, lightWeight);

工作量估计

  • 基础实现3-4天
  • 优化1周

6.2 屏幕空间降噪

背景

低采样率导致的噪点,通过屏幕空间滤波器降噪。

算法

1. 双边滤波 (Bilateral Filter)

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天
  • SVGF2-3周

七、场景融合

7.1 体积与场景遮挡

问题

当前体积渲染不考虑场景几何体的遮挡。

解决方案

1. 深度测试

// 从深度缓冲重建世界坐标
float depth = depthTexture.Sample(sampler, uv);
float3 sceneWorldPos = reconstructWorldPos(uv, depth);

// 计算到场景的距离
float sceneDist = length(sceneWorldPos - cameraPos);

// Ray Marching 时限制最大距离
float tmax = min(rayTmax, sceneDist);

2. 阴影遮挡

// 场景几何体遮挡体积
float sceneShadow = 1.0;
if (isOccludedByScene(pos)) {
    sceneShadow = 0.0;
}

工作量估计

  • 深度测试2-3天
  • 完整遮挡1周

7.2 阴影接收

问题

体积应该接收场景几何体的阴影。

解决方案

float sceneShadow = SampleSceneShadowMap(pos);
float3 S = sigmaS * phase_function() * shadow * sceneShadow;

工作量估计

  • 基础实现2-3天

八、应用场景

8.1 动态天气系统

功能

  • 云层动态演化
  • 日出日落光照变化
  • 雨雾效果

实现

// 云层演化
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 火焰/爆炸效果

需求

  • 温度场驱动
  • 黑体辐射颜色
  • 自发光

实现

// 温度到颜色 (黑体辐射)
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月