【URP】Unity[后处理]胶片颗粒FilmGrain

【从UnityURP开始探索游戏渲染】专栏-直达

Film Grain的定义与作用

Film Grain是一种模拟传统摄影胶片颗粒感的后期处理效果,通过添加随机噪点纹理增强画面的艺术表现力。其核心用途包括:

  • 复古风格模拟:重现胶片摄影的颗粒质感,增强怀旧氛围
  • 画面细节强化:掩盖低分辨率纹理的瑕疵,提升视觉丰富度
  • 电影感塑造:配合色调映射、色差等效果构建电影级视觉风格

发展历史

  • 传统胶片时代‌:物理银盐颗粒形成的自然噪点
  • 数字时代初期‌:通过简单噪声算法模拟(如Perlin噪声)
  • 现代游戏引擎‌:HDRP/URP等管线集成预设化系统,支持物理准确的颗粒分布模型(如Kodak系列预设)

原理

Unity URP中的Film Grain效果通过噪声纹理叠加和亮度响应曲线实现胶片颗粒模拟.

噪声生成机制

  • 预设纹理采样‌:内置Kodak/Agfa等胶片颗粒的预烘焙LUT纹理(64x64分辨率),通过屏幕UV坐标进行双线性采样
  • 动态噪声合成‌:当选择"Custom"模式时,使用Simplex噪声算法实时生成3D噪声场,通过时间参数实现动态流动效果
  • 色彩空间转换‌:噪声值在YCoCg色彩空间进行混合,避免RGB通道直接叠加导致的色偏问题

亮度响应系统

c 复制代码
hlsl
float grainIntensity = intensity * (1 - smoothstep(0.5, 1.0, luminance));

该公式根据像素亮度动态调节颗粒强度,使暗部保留更多噪点(响应曲线参数控制过渡斜率)

实现示例

该Shader实现包含噪声纹理平铺、亮度自适应调节和色彩安全混合三个关键技术点

  • Filmgrain.shader

    c 复制代码
    Shader "PostProcessing/FilmGrain"
    {
        Properties {
            _GrainTex ("Noise Texture", 2D) = "white" {}
            _Intensity ("Intensity", Range(0,1)) = 0.5
            _Response ("Response", Range(0,1)) = 0.8
        }
        SubShader {
            Pass {
                HLSLPROGRAM
                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
    
                TEXTURE2D(_GrainTex);
                SAMPLER(sampler_GrainTex);
                float _Intensity;
                float _Response;
    
                float4 Frag(Varyings input) : SV_Target {
                    float2 uv = input.uv * float2(80,45); // 平铺噪声
                    float3 grain = SAMPLE_TEXTURE2D(_GrainTex,sampler_GrainTex,uv).rgb;
    
                    float luminance = Luminance(SceneColor.rgb);
                    float adaptive = lerp(1.0, 1.0-luminance, _Response);
    
                    return SceneColor * (1.0 + grain * _Intensity * adaptive);
                }
                ENDHLSL
            }
        }
    }

管线集成流程

  • 渲染阶段‌:在URP的PostProcessingStack中插入FilmGrainPass,执行顺序在Tonemapping之后、FXAA之前
  • 性能优化‌:采用1/4分辨率渲染噪声纹理,通过硬件线性滤波降低带宽消耗
  • 移动端适配‌:使用ARM NEON指令集加速噪声计算,在GPU Tile-Based架构下减少内存访问次数

URP实现流程

  • FilmGrainExample.cs

    csharp 复制代码
    using UnityEngine;
    using UnityEngine.Rendering;
    using UnityEngine.Rendering.Universal;
    
    [VolumeComponentMenu("Post-processing/Film Grain")]
    public class CustomFilmGrain : VolumeComponent, IPostProcessComponent {
        public FilmGrainLookupParameter type = new FilmGrainLookupParameter(FilmGrainLookup.Kodak_200);
        public ClampedFloatParameter intensity = new ClampedFloatParameter(0f, 0f, 1f);
        public ClampedFloatParameter response = new ClampedFloatParameter(0.8f, 0f, 1f);
    
        public bool IsActive() => intensity.value > 0f;
        public bool IsTileCompatible() => false;
    }
  • FilmGrainRenderer.cs

    csharp 复制代码
    using UnityEngine;
    using UnityEngine.Rendering;
    using UnityEngine.Rendering.Universal;
    
    public class FilmGrainRenderer : ScriptableRendererFeature {
        class CustomPass : ScriptableRenderPass {
            // 渲染逻辑实现...
        }
        public override void Create() {
            // 初始化代码...
        }
    }

参数详解与用例

参数 类型 说明 典型用例
Type Enum 预设颗粒类型(Kodak200/400等) 选择Agfa400模拟16mm胶片
Intensity 0-1 颗粒可见度 0.3-0.5用于复古RPG游戏
Response 0-1 亮度响应曲线 0.7使亮部颗粒减弱
Texture 2D 自定义噪点贴图 制作数字故障艺术效果

操作步骤(URP 2022.1+)

  • 创建Volume对象:GameObject > Volume

  • 添加Film Grain覆盖:Add Override > Film Grain

  • 配置参数示例:

    Type = Kodak500T

    Intensity = 0.4

    Response = 0.65

性能优化建议

  • 移动端使用Quarter分辨率
  • 动态调整Intensity(剧情过场时增强)
  • 禁用非必要时的Volume更新

【从UnityURP开始探索游戏渲染】专栏-直达

(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)