You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求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缓存命中问题:

  1. 矩阵类:world → viewProj
  2. 相机参数:cameraPos
  3. 雾核心参数:fogDensity → fogHeightMin → fogHeightMax
  4. 噪声与动画: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 04:52:31