求DirectX 11下HLSL实现Unreal Engine风格Volumetric Fog的方案与资料
DirectX 11 HLSL实现Unreal风格体积雾指南
一、核心实现代码与变量说明
Unreal风格体积雾依赖**光线步进(Ray Marching)**实现3D空间雾效,以下是可直接复用的HLSL代码,包含顶点/像素着色器及关键参数:
顶点着色器
struct VS_INPUT { float3 pos : POSITION; }; struct PS_INPUT { float4 pos : SV_POSITION; float3 worldPos : TEXCOORD0; float3 viewDir : TEXCOORD1; }; cbuffer ConstantBuffer : register(b0) { float4x4 world; // 模型世界矩阵 float4x4 viewProj; // 视图投影矩阵 float3 cameraPos; // 相机世界位置 float fogDensity; // 雾的基础密度 float fogHeightMin; // 雾的最低高度(低于此高度无雾) float fogHeightMax; // 雾的最高高度(高于此高度无雾) float noiseScale; // 噪声纹理缩放系数 float time; // 时间变量,用于雾的动画效果 }; PS_INPUT VS(VS_INPUT input) { PS_INPUT output; output.pos = mul(float4(input.pos, 1.0), mul(world, viewProj)); output.worldPos = mul(float4(input.pos, 1.0), world).xyz; output.viewDir = normalize(output.worldPos - cameraPos); return output; }
像素着色器(含Perlin噪声与光线步进)
// 内置Perlin噪声实现(可直接复制到着色器中) float permute(float x) { return ((x*34.0)+1.0)*x; } float taylorInvSqrt(float r) { return 1.79284291400159 - 0.85373472095314 * r; } float fade(float t) { return t*t*t*(t*(t*6.0-15.0)+10.0); } float pnoise(float3 p, float3 rep) { float3 pi = mod(floor(p), rep); float3 pf = frac(p); float3 w = fade(pf); float3 x0 = pi + float3(0.0, 0.0, 0.0); float3 x1 = pi + float3(1.0, 0.0, 0.0); float3 x2 = pi + float3(0.0, 1.0, 0.0); float3 x3 = pi + float3(1.0, 1.0, 0.0); float3 x4 = pi + float3(0.0, 0.0, 1.0); float3 x5 = pi + float3(1.0, 0.0, 1.0); float3 x6 = pi + float3(0.0, 1.0, 1.0); float3 x7 = pi + float3(1.0, 1.0, 1.0); float3 g0 = normalize(frac(sin(x0*float3(127.1,311.7, 74.7))*43758.5453) * 2.0 - 1.0); float3 g1 = normalize(frac(sin(x1*float3(127.1,311.7, 74.7))*43758.5453) * 2.0 - 1.0); float3 g2 = normalize(frac(sin(x2*float3(127.1,311.7, 74.7))*43758.5453) * 2.0 - 1.0); float3 g3 = normalize(frac(sin(x3*float3(127.1,311.7, 74.7))*43758.5453) * 2.0 - 1.0); float3 g4 = normalize(frac(sin(x4*float3(127.1,311.7, 74.7))*43758.5453) * 2.0 - 1.0); float3 g5 = normalize(frac(sin(x5*float3(127.1,311.7, 74.7))*43758.5453) * 2.0 - 1.0); float3 g6 = normalize(frac(sin(x6*float3(127.1,311.7, 74.7))*43758.5453) * 2.0 - 1.0); float3 g7 = normalize(frac(sin(x7*float3(127.1,311.7, 74.7))*43758.5453) * 2.0 - 1.0); float n0 = dot(g0, pf - float3(0.0, 0.0, 0.0)); float n1 = dot(g1, pf - float3(1.0, 0.0, 0.0)); float n2 = dot(g2, pf - float3(0.0, 1.0, 0.0)); float n3 = dot(g3, pf - float3(1.0, 1.0, 0.0)); float n4 = dot(g4, pf - float3(0.0, 0.0, 1.0)); float n5 = dot(g5, pf - float3(1.0, 0.0, 1.0)); float n6 = dot(g6, pf - float3(0.0, 1.0, 1.0)); float n7 = dot(g7, pf - float3(1.0, 1.0, 1.0)); float x1Interp = lerp(n0, n1, w.x); float x2Interp = lerp(n2, n3, w.x); float yInterp = lerp(x1Interp, x2Interp, w.y); float x3Interp = lerp(n4, n5, w.x); float x4Interp = lerp(n6, n7, w.x); float y2Interp = lerp(x3Interp, x4Interp, w.y); return lerp(yInterp, y2Interp, w.z); } // 计算采样点的雾密度 float GetFogDensity(float3 worldPos) { // 高度衰减:限制雾在指定高度范围内 float heightAtten = saturate((worldPos.y - fogHeightMin) / (fogHeightMax - fogHeightMin)); heightAtten = 1.0 - heightAtten; // 底部雾更浓,可反向调整 // 噪声扰动:模拟不均匀的雾团 float noise = pnoise(worldPos * noiseScale + float3(0, time * 0.1, 0), float3(100, 100, 100)); noise = (noise + 1.0) * 0.5; // 噪声值映射到0-1区间 return fogDensity * heightAtten * noise; } float4 PS(PS_INPUT input) : SV_TARGET { float3 rayStart = cameraPos; float3 rayEnd = input.worldPos; float rayLength = distance(rayStart, rayEnd); float3 rayDir = normalize(rayEnd - rayStart); const int sampleCount = 32; // 采样次数,平衡性能与效果 float stepSize = rayLength / sampleCount; float3 currentPos = rayStart; float totalFog = 0.0; // 光线步进采样雾密度 for (int i = 0; i < sampleCount; i++) { float density = GetFogDensity(currentPos); totalFog += density * stepSize; currentPos += rayDir * stepSize; // 提前终止:雾浓度足够高时停止采样,节省性能 if (totalFog > 0.95) break; } totalFog = saturate(totalFog); // 替换为实际场景颜色采样(比如从GBuffer获取) float4 sceneColor = float4(0.8, 0.9, 1.0, 1.0); float4 fogColor = float4(0.7, 0.75, 0.8, 1.0); // 雾与场景颜色混合 return lerp(sceneColor, fogColor, totalFog); }
变量传递顺序
常量缓冲区按以下顺序传递参数,避免GPU缓存命中问题:
- 矩阵类:
world→viewProj - 相机参数:
cameraPos - 雾核心参数:
fogDensity→fogHeightMin→fogHeightMax - 噪声与动画:
noiseScale→time
二、3D雾效实现误区解答
不是仅用带Perlin Noise的平面就能实现3D雾效。平面噪声雾属于2.5D效果,只能模拟地面雾的“平面分布”,无法表现光线穿过雾时的空间浓度变化、雾的立体形态。真正的3D体积雾必须对相机到像素的光线进行步进采样,在每个空间点计算雾密度并累积,才能呈现出真实的3D空间感。
三、相关文献与书籍推荐
HLSL/DirectX方向
- 《Real-Time Rendering》(第四版):第14章深入讲解体积雾、大气散射的数学模型与实时实现,是实时渲染领域的权威教材。
- 《HLSL Development Cookbook》:包含体积雾的HLSL实战章节,覆盖光线步进优化、噪声应用等实用技巧。
- NVIDIA技术文档《Volumetric Fog Techniques》:详细讲解DirectX环境下体积雾的性能优化方案,适合工业级项目开发。
OpenGL方向
- 《OpenGL Programming Guide》(红宝书):后期章节涉及体积渲染、高级雾效的OpenGL着色器实现。
- 《Real-Time Volume Graphics》:专注实时体积图形学,包含雾、云等效果的数学推导与OpenGL代码示例。
内容的提问来源于stack exchange,提问作者HardCoder
相关产品推荐
相关产品推荐

