URP-RenderFramework

本文详细介绍了如何在Unity的UniversalRenderPipeline中使用RenderFeature创建并配置PostProcessPass,包括VolumeComponent的集成和后处理参数的设置。作者展示了BloomSRF和PostProcessRenderPass的实现,以及如何在渲染过程中进行纹理上下采样和模糊效果的处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

前言

P1

P2


前言

写一个使用RenderFeature添加PostProcessPass的框架

P1

P2

包含

  • VC设置后处理参数
  • RenderFeature添加Pass
  • 纹理上下采样
using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

// 通用渲染管线程序集
namespace UnityEngine.Rendering.Universal
{
    //添加到Volume组件菜单中
    [Serializable, VolumeComponentMenu("CustomVC/PostProcessVC")]
    public class PostProcessVC : VolumeComponent
    {
        /*
            ClampedXXXParameter(default, min, max)
            ColorParameter(Color.white)
            BoolParameter(false)
        */
        public IntParameter IntParame2 = new ClampedIntParameter(4, 1, 8);
        public FloatParameter FloatParame = new ClampedFloatParameter(0.5f, 0.0f, 1.0f);
        public ColorParameter ColorParame = new ColorParameter(Color.white);
        public BoolParameter BoolParame = new BoolParameter(false);
    }
}
public class BloomSRF : ScriptableRendererFeature
{
    [System.Serializable]
    public class BloomSettings
    {
        public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
        public Shader shader; // 设置后处理Shader
    }

    public BloomSettings settings = new BloomSettings();
    PostProcessRenderPass postProcessScriptablePass;

    public override void Create()
    {
        this.name = "postProcess"; // 外部显示名字
        postProcessScriptablePass =
            new PostProcessRenderPass(RenderPassEvent.BeforeRenderingPostProcessing, settings.shader); // 初始化Pass
    }

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        postProcessScriptablePass.Setup(renderingData.cameraData.renderer.cameraColorTarget); // 初始化Pass里的属性
        renderer.EnqueuePass(postProcessScriptablePass);
    }
}

public class PostProcessRenderPass : ScriptableRenderPass
{
    static readonly string k_RenderTag = "PostProcessRender"; // 设置渲染 Tags
    static readonly int TempTargetId = Shader.PropertyToID("_存储阈值的临时贴图"); // 设置储存图像信息
    PostProcessVC postProcessVC; // 传递到volume
    Material postProcessMaterial; // 后处理使用材质
    RenderTargetIdentifier cameraColorTexture; // 设置当前渲染目标

    Level[] m_Pyramid;
    const int k_MaxPyramidSize = 16;


    struct Level
    {
        internal int down;
        internal int up;
    }

    static class ShaderIDs
    {
        internal static readonly int ShaderID1 = Shader.PropertyToID("ShaderID1");
        internal static readonly int ShaderID3 = Shader.PropertyToID("ShaderID2");
        internal static readonly int ShaderID2 = Shader.PropertyToID("ShaderID3");

    }

    public PostProcessRenderPass(RenderPassEvent evt, Shader postProcessShader)
    {
        renderPassEvent = evt; // 设置渲染事件的位置
        var shader = postProcessShader; // 输入Shader信息
        
        // 判断如果不存在Shader
        if (shader = null) // Shader如果为空提示
        {
            Debug.LogError("PostProcessPass没有指定Shader");
            return;
        }

        //如果存在新建材质
        postProcessMaterial = CoreUtils.CreateEngineMaterial(postProcessShader);

        m_Pyramid = new Level[k_MaxPyramidSize];

        for (int i = 0; i < k_MaxPyramidSize; i++)
        {
            m_Pyramid[i] = new Level
            {
                down = Shader.PropertyToID("_BlurMipDown" + i),
                up = Shader.PropertyToID("_BlurMipUp" + i)
            };
        }
    }
    //提交渲染信息
    public void Setup(in RenderTargetIdentifier currentTarget)
    {
        this.cameraColorTexture = currentTarget;
    }

    //后处理的逻辑和渲染核心函数,基本相当于内置管线的OnRenderImage函数
    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        // 判断材质是否为空
        if (postProcessMaterial == null)
        {
            Debug.LogError("材质初始化失败");
            return;
        }

        // 判断是否开启后处理
        if (!renderingData.cameraData.postProcessEnabled)
        {
            return;
        }

        // 渲染设置
        var stack = VolumeManager.instance.stack; // 传入volume
        postProcessVC = stack.GetComponent<PostProcessVC>(); // 拿到我们的volume
        if (postProcessVC == null)
        {
            Debug.LogError(" Volume组件获取失败 ");
            return;
        }
        var cmd = CommandBufferPool.Get(k_RenderTag); // 设置渲染标签
        Render(cmd, ref renderingData); // 设置渲染函数
        context.ExecuteCommandBuffer(cmd); // 执行函数
        CommandBufferPool.Release(cmd); // 释放
    }

    void Render(CommandBuffer cmd, ref RenderingData renderingData)
    {
        ref var cameraData = ref renderingData.cameraData; // 获取摄像机属性
        var camera = cameraData.camera; // 传入摄像机
        var source = cameraColorTexture; // 获取渲染图片
        int buffer0 = TempTargetId; // 渲染结果图片


        int tw = (int)(camera.scaledPixelWidth / postProcessVC.降采样.value);
        int th = (int)(camera.scaledPixelHeight / postProcessVC.降采样.value);

       
        Vector4 vec4 = new Vector4(postProcessVC.模糊范围.value / (float)Screen.width,
            postProcessVC.模糊范围.value / (float)Screen.height, 0, 0);
        
        postProcessMaterial.SetVector(ShaderIDs.vec4, vec4);
        postProcessMaterial.SetFloat(ShaderIDs.ShaderID1, postProcessVC.FloatParame.value);
        postProcessMaterial.SetFloat(ShaderIDs.ShaderID2, postProcessVC.IntParame2.value);
        postProcessMaterial.SetFloat(ShaderIDs.ShaderID3, postProcessVC.BoolParame.value);


        //取阈值
        cmd.GetTemporaryRT(buffer0, 
            camera.scaledPixelWidth, camera.scaledPixelHeight, 0,
            FilterMode.Trilinear, RenderTextureFormat.Default);
        
        //混合计算
        cmd.Blit(source, buffer0); 
        cmd.Blit(buffer0, source, postProcessMaterial, 0); 

        // 降采样
        RenderTargetIdentifier lastDown = source; //备份

        for (int i = 0; i < postProcessVC.迭代次数.value; i++)
        {
            int mipDown = m_Pyramid[i].down;
            int mipUp = m_Pyramid[i].up;
            cmd.GetTemporaryRT(mipDown, tw, th, 0, FilterMode.Bilinear);
            cmd.GetTemporaryRT(mipUp, tw, th, 0, FilterMode.Bilinear);

            cmd.Blit(lastDown, mipDown, postProcessMaterial, 1);

            lastDown = mipDown;
            tw = Mathf.Max(tw / 2, 1);
            th = Mathf.Max(th / 2, 1);
        }

        // 升采样
        int lastUp = m_Pyramid[postProcessVC.迭代次数.value - 1].down;

        for (int i = postProcessVC.迭代次数.value - 2; i >= 0; i--)
        {
            int mipUp = m_Pyramid[i].up;
            cmd.Blit(lastUp, mipUp, 
                postProcessMaterial, 2);
            lastUp = mipUp;
        }

        //合并
        if (postProcessVC.Debug.value)
        {
            cmd.Blit(lastUp, source, postProcessMaterial, 4);
        }
        else
        {
            cmd.SetGlobalTexture("_SourceTex", buffer0);
            cmd.Blit(lastUp, source, postProcessMaterial, 3);
        }

        // Cleanup
        for (int i = 0; i < postProcessVC.迭代次数.value; i++)
        {
            if (m_Pyramid[i].down != lastUp)
                cmd.ReleaseTemporaryRT(m_Pyramid[i].down);
            if (m_Pyramid[i].up != lastUp)
                cmd.ReleaseTemporaryRT(m_Pyramid[i].up);
        }
    }
}

### 关于Unity URP PBR Water Shader Implementation Tutorial 在 Unity 的 Universal Render Pipeline (URP) 中实现基于物理渲染(Physically Based Rendering, PBR)的水面着色器是一项复杂的任务,它涉及到多个方面的技术细节。以下是关于此主题的关键知识点以及其实现方法: #### 1. **纹理采样与系统变换矩阵** 为了构建一个高质量的水面材质,首先需要掌握如何在 URP 下进行纹理采样和获取系统的变换矩阵。这一步骤对于后续计算法线映射、反射方向以及其他光照参数至关重要[^1]。 ```csharp // 示例代码:获取世界空间位置并应用变换矩阵 float4 worldPos = mul(unity_ObjectToWorld, v.vertex); ``` #### 2. **简单光照模型的基础** 在实现更高级别的 PBR 效果前,通常先从简单的光照模型入手,比如 Lambert 或 Blinn-Phong 模型。这些模型可以帮助理解光线与表面之间的交互方式,并为进一步扩展奠定基础[^1]。 ```hlsl half NdotL = saturate(dot(normalDirection, lightDirection)); fixed3 diffuseReflection = _LightColor0.rgb * surfaceAlbedo * max(NdotL, 0.0f); ``` #### 3. **利用 GPU Instancing 和 SRP Batcher 提升性能** 为了让场景中的大量水体实例高效运行,应考虑启用 GPU Instancing 功能以及 URP 自带的 SRP Batcher 支持。这样可以在减少绘制调用的同时保持良好的视觉质量[^1]。 #### 4. **单 Pass 实现多光源 PBR 效果** 通过优化着色器逻辑,在单一通道内完成对多个光源的支持是一种常见的做法。这种方法不仅提高了效率,还简化了后期处理流程。 #### 5. **深度贴图与颜色贴图的应用** 借助 URP 自动生成的深度贴图和颜色贴图资源,开发者能够模拟逼真的水中倒影现象。这一过程可能需要用到屏幕空间反射或者立方体贴图投影等技术手段[^1]。 ```hlsl // 使用 GrabPass 获取背景图像数据 sampler2D _GrabTexture; float4 frag(v2f i) : SV_Target { float4 screenPos = ComputeScreenPos(i.pos); float2 uv = screenPos.xy / screenPos.w; fixed4 col = tex2D(_GrabTexture, uv); } ``` #### 6. **曲面细分增强几何结构** 如果希望让水面看起来更加细腻,则可以引入 Direct3D 11 所支持的 Tessellation 阶段。具体来说,可以通过配置 Hull Shader 和 Domain Shader 来动态调整顶点密度,从而生成更为平滑的波浪形态[^3]。 #### 7. **Shader Graph 工具辅助开发** 除了手动编写 HLSL 脚本外,还可以充分利用 Unity 内置的 Shader Graph 编辑器快速搭建原型。例如设置 Properties 属性节点定义输入变量,组合 Noise 函数制造随机波动等等[^4]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值