573 字
3 分钟
极简 RenderFeature 案例
极简 RenderFeature 案例
Shader 部分
TIP可以查看博主之前的帖子,使用 Rider 实时模板来提升开发效率。RiderSkill
Shader "Hidden/GrayScale"{ SubShader { Tags { "RenderPipeline" = "UniversalPipeline" "RenderType" = "Opaque" }
Pass { Name "GrayScale"
HLSLPROGRAM
#pragma vertex vert #pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct Attributes { // 直接告诉 Shader:你现在正在处理第几个顶点 uint vertexID : SV_VertexID; };
struct Varyings { float4 positionHCS : SV_POSITION; float2 uv : TEXCOORD0; };
TEXTURE2D_X(_BlitTexture); SAMPLER(sampler_BlitTexture);
Varyings vert(Attributes input) { Varyings output;
output.positionHCS = GetFullScreenTriangleVertexPosition(input.vertexID); output.uv = GetFullScreenTriangleTexCoord(input.vertexID);
return output; }
half4 frag(Varyings input) : SV_Target { half4 color = SAMPLE_TEXTURE2D_X(_BlitTexture,sampler_BlitTexture,input.uv); // 把 RGB 三个颜色值,按照不同的重要程度加权,计算出一个灰度值。 half gray = dot(color.rgb,half3(0.299,0.587,0.114));
return half4(gray,gray,gray,color.a); }
ENDHLSL } }}half gray = dot(color.rgb,half3(0.299,0.587,0.114));这行代码把 RGB 三个颜色值,按照不同的重要程度加权,计算出一个灰度值。
NOTE普通 Shader 和 Blit 全屏 Shader 的区别。
POSITION是”从模型顶点里拿位置”。
SV_VertexID是”直接告诉 Shader:你现在正在处理第几个顶点”。我们的 Blit Shader 根本没有真正的模型顶点数据拿,所以用的是
SV_VertexID
RT 部分
using UnityEngine;using UnityEngine.Rendering;using UnityEngine.Rendering.Universal;
public class GrayScaleRF : ScriptableRendererFeature{ class GrayScalePass : ScriptableRenderPass { private Material material;
private RTHandle cameraColorTarget; private RTHandle temporaryRT;
public GrayScalePass(Material material) { this.material = material; }
// 使用专门的函数进行初始化,让代码更加规整 public void SetTarget(RTHandle target) { cameraColorTarget = target; }
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) { RenderTextureDescriptor descriptor = renderingData.cameraData.cameraTargetDescriptor; descriptor.depthBufferBits = 0;
RenderingUtils.ReAllocateIfNeeded(ref temporaryRT, descriptor, FilterMode.Point, TextureWrapMode.Clamp, name: "_GrayScaleTemp"); }
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { if(material == null) return;
CommandBuffer cmd = CommandBufferPool.Get("GrayScale");
// Camera -> 临时 RT Blitter.BlitCameraTexture(cmd,cameraColorTarget,temporaryRT); // 临时RT -> Camera Blitter.BlitCameraTexture(cmd,temporaryRT,cameraColorTarget,material,0); context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd); }
// 实际开发中实际不太会用 OnCameraCleanup,而是使用 Dispose,与 RF 的 Dispose 一起调用。 public void Dispose() { temporaryRT?.Release(); temporaryRT = null; } }
[SerializeField] private Material material;
private GrayScalePass pass;
public override void Create() { pass = new GrayScalePass(material);
pass.renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; }
// 在 AddRenderPasses 之前执行,SetTarget 应该在此方法中进行配置 public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData) { if(material == null) return;
if(renderingData.cameraData.cameraType != CameraType.Game) return;
pass.SetTarget(renderer.cameraColorTargetHandle); }
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { if(material == null) return;
if(renderingData.cameraData.cameraType != CameraType.Game) return;
renderer.EnqueuePass(pass); }
protected override void Dispose(bool disposing) { pass?.Dispose(); }}应用效果

找到当前使用的 RenderAsset 点击 Add Renderer Feature 将我们自定义的 GrayScaleRF 插入。
新建 Material 使用 GrayScale Shader,将材质拖入对应的材质槽位。可以在 Game 窗口看到最后的效果。