抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

Unity URP RenderFeature

大部分Unity图形开发者的效果需求其实可以用URP的RenderFeature实现,而非完整用SRP重写一个管线

替换全局材质

这是一个替换全局物体材质的Feature的示例,可以用于各种Debug

Global Volume

在这里面添加开关

public enum DisplayMode
{
None = 0,
Generate = 1,
PlayAnim = 2
}

// 自定义一个序列化数据,可以将一个枚举暴露在UI上
[Serializable]
public sealed class DisplayModeParameter : VolumeParameter<DisplayMode> { public DisplayModeParameter(DisplayMode value, bool overrideState = false) : base(value, overrideState) { } }


public class DisplayOption : VolumeComponent, IPostProcessComponent
{
// 将一个bool暴露在UI上
public BoolParameter featureEnable = new BoolParameter(false);
public DisplayModeParameter mode = new DisplayModeParameter(DisplayMode.None);

public bool IsActive()
{
return (mode.value != DisplayMode.None)&&featureEnable.value;
}

public bool IsTileCompatible()
{
return false;
}
// 这是一个工具函数,用于访问Volume参数
public int GetMaterialIndex()
{
if (mode == DisplayMode.Generate)
{
return 0;
}
else if (mode == DisplayMode.PlayAnim)
{
return 1;
}

return -1;
}
}

RenderFeature

public class DisplayRenderFeature : ScriptableRendererFeature
{
private DisplayRenderPass _pass;
[SerializeField] private Shader generateShader = null;
[SerializeField] private Shader playAnimShader = null;
public override void Create()
{
generateShader = Shader.Find("Hidden/Generate");
playAnimShader = Shader.Find("Hidden/Display");
if (!generateShader || !playAnimShader)
{
return;
}
List<Shader> shaders = new List<Shader>();
shaders.Add(generateShader);
shaders.Add(playAnimShader);
_pass = new DisplayRenderPass("Display", RenderQueueRange.opaque,
RenderPassEvent.AfterRenderingTransparents, shaders, true);
}

public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
renderer.EnqueuePass(_pass);
}
}


RenderPass

class DisplayRenderPass : ScriptableRenderPass
{
// Global Volume上的参数
private DisplayOption _displayOption;
private string _passCmdName;
private bool _isOpaque;
private List<ShaderTagId> _tagIdList = new List<ShaderTagId>();
private FilteringSettings _filteringSettings;
private List<Material> _materials;
public DisplayRenderPass(string passCmdName, RenderQueueRange renderQueueRange, RenderPassEvent evt, List<Shader> shaders, bool isOpaque)
{
_passCmdName = passCmdName;
_isOpaque = isOpaque;
// Shader Tag
_tagIdList.Add(new ShaderTagId("UniversalForward"));
_tagIdList.Add(new ShaderTagId("LightweightForward"));
_tagIdList.Add(new ShaderTagId("SRPDefaultUnlit"));
_filteringSettings = new FilteringSettings(renderQueueRange, LayerMask.NameToLayer("Everything"));
// 创建复写材质(这个RenderFeature本质是在一个平面上重绘RT)
_materials = new List<Material>();
for (int i = 0; i < shaders.Count; i++)
{
_materials.Add(CoreUtils.CreateEngineMaterial(shaders[i]));
}
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
// 从Global Volume中获取 DisplayOption
var stack = VolumeManager.instance.stack;
_displayOption = stack.GetComponent<DisplayOption>();
if (_displayOption == null || !_displayOption.IsActive() || _displayOption.GetMaterialIndex() < 0)
{
return;
}
// 绘制命令(这里自由度非常高)
var cmd = CommandBufferPool.Get(_passCmdName);
#region Render

var camera = renderingData.cameraData.camera;
if (_isOpaque)
{
if (renderingData.cameraData.isSceneViewCamera ||
(camera.TryGetComponent(out UniversalAdditionalCameraData urpCameraData) &&
urpCameraData.renderType == CameraRenderType.Base))
{
cmd.ClearRenderTarget(true, true, Color.black);
}
}
context.ExecuteCommandBuffer(cmd);
cmd.Clear();

var sortFlags = _isOpaque
? renderingData.cameraData.defaultOpaqueSortFlags
: SortingCriteria.CommonTransparent;
var drawSettings = CreateDrawingSettings(_tagIdList, ref renderingData, sortFlags);
drawSettings.overrideMaterial = _materials[_displayOption.GetMaterialIndex()];
context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref _filteringSettings);
#endregion
// 执行、释放资源
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
}

Shader

Shader你随便写

Shader "Hidden/Display"
{
SubShader
{
Tags { "RenderType"="Opaque" "RenderPipeline" = "UniversalPipeline" "Queue"="Overlay" }

Pass
{
Cull Back

HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float4 tangentOS : TANGENT;
float2 texcoord : TEXCOORD0;
float2 staticLightmapUV : TEXCOORD1;
float2 dynamicLightmapUV : TEXCOORD2;
UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct Varyings
{
float4 pos: SV_POSITION; //齐次裁剪空间顶点坐标
float2 uv: TEXCOORD0; //纹理坐标
float3 normalWS: TEXCOORD1; //世界空间法线
float3 viewDirWS: TEXCOORD2; //世界空间视线方向

#if defined(REQUIRES_WORLD_SPACE_POS_INTERPOLATOR)
float3 posWS: TEXCOORD3; //世界空间顶点位置
#endif

float2 lightmapUV: TEXCOORD4;

#ifdef _NORMALMAP
float4 tangentWS: TEXCOORD5; //xyz是世界空间切向量,w是方向
#endif

half4 fogFactorAndVertexLight: TEXCOORD6; //x是雾系数,yzw为顶点光照

#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
float4 shadowCoord: TEXCOORD7; //阴影坐标
#endif

float4 screenPos: TEXCOORD8;
};

// vert没什么特殊的
Varyings vert(Attributes v)
{
Varyings o = (Varyings)0;
VertexPositionInputs vertexInput = GetVertexPositionInputs(v.positionOS.xyz);
VertexNormalInputs normalInput = GetVertexNormalInputs(v.normalOS, v.tangentOS);
float3 viewDirWS = GetCameraPositionWS() - vertexInput.positionWS;
half3 vertexLight = VertexLighting(vertexInput.positionWS, normalInput.normalWS);
half fogFactor = ComputeFogFactor(vertexInput.positionCS.z);

o.uv = v.texcoord;
o.normalWS = normalInput.normalWS;
o.viewDirWS = viewDirWS;

#ifdef _NORMALMAP
real sign = v.tangent.w * GetOddNegativeScale();
o.tangentWS = half4(normalInput.tangentWS.xyz, sign);
#endif

o.lightmapUV = v.staticLightmapUV;

o.fogFactorAndVertexLight = half4(fogFactor, vertexLight);

#if defined(REQUIRES_WORLD_SPACE_POS_INTERPOLATOR)
o.posWS = vertexInput.positionWS;
#endif

#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
o.shadowCoord = GetShadowCoord(vertexInput);
#endif

o.pos = vertexInput.positionCS; //齐次裁剪空间顶点坐标
o.screenPos = ComputeScreenPos(o.pos);

return o;
}

TEXTURE2D(_GlobalUVTexture); SAMPLER(sampler_GlobalUVTexture);

float4 frag(Varyings i) : SV_Target
{
float3 color = SAMPLE_TEXTURE2D(_GlobalUVTexture, sampler_GlobalUVTexture, i.lightmapUV).rgb;
return float4(color, 1);
}
ENDHLSL
}
}
}

评论