Unity-URP

1. 简单使用

1. 简介

  1. SRP:可编程渲染管线
    剔除 culling
    渲染 rendering
    后处理 postprocessing
  2. URP 通用可编程渲染管线

2. 安装

1. 新工程

可直接选择LWRP(URP)【通用渲染管线】创建工程.

2. 升级已有工程
  1. 在包管理器中找到LWRP(URP)【通用渲染管线】。
  2. 右键创建->渲染(Rendering)->创建Render Pipeline Asset(渲染管线 with U Renderer),并赋给项目设置->图形中的渲染管线设置。
    此外,还需为Quality设置渲染管线。
  3. 这时,项目原本的材质均会出错丢失。需在编辑->渲染管线中升级已有材质。路径如下
    在这里插入图片描述
    但这只能修复少量unity自带的材质。
3. URP注意事项

URP想要副光源也能够产生阴影,需要先在URP管线的Inspector面板下为Additional Lights勾选上Cast Shadows。(接受阴影)

2. URP运行流程

1. 创建RenderPipelineAsset和RendererData

  1. 如果使用SRP,需要创建RenderPipelineAsset的子类, 在URP中即UniversalRenderPipelineAsset,将其指定到项目Graphics设定里
  2. URP框架中有ScriptableRenderData类型用于创建Renderer,目前可用的是UniversalRenderData (用于替代之前版本的ForwardRenderData )和Render2DData
  3. 资源创建UniversalRenderPipelineAsset时会自动创建一个UniversalRenderData或者Render2DData并矢联_上,RenderPipelineAsset至少要有一个RenderData,多个RenderPipelineAsset可以使用同一个RenderData,例如做高中低画质配置(使用不同RenderPipelineAsset,而不是RenderData)
  4. 运行时可以通过GraphicsSettings.renderPipelineAsset动态修改使用的RenderPipelineAsset
    在这里插入图片描述

2. 创建Pipeline和Renderer

  1. 引擎调用CreatePipeline
  2. UniversalRenderPipelineAsset -> UniversalRenderPipeline
  3. UniversalRenderData -> UniversalRenderer
  4. RenderPipelineAsset & RenderPipelineÆ5 I 是引擎c++类,RenderData Renderer是URР расkаgе里的c#脚本类
    在这里插入图片描述
    在这里插入图片描述

3. 摄像机与灯光的变化

1. 摄像机
  1. 渲染器改到URP之后,Camera会被自动带上UniversalAdditionalCameraData脚本,并且Camera参数和Built-in 。RP不同
  2. 摄像机可选择Overlay类型,这时将其添加到主摄像机的Stack中,即可实现Overlay摄像机对主摄像机的覆盖。
2. 灯光
  1. Light也会被自动添加UniversalAddiitionalLightData组件,且参数变了。

4. 原理

引擎每帧会调用会这些接口,以便在渲染的不同阶段执行你想要的操作
RenderPipeline:Render
protected override void Render( renderContext, List cameras)

  1. BeginContextRendering
  2. render Cameras (RenderCameraStack)
  3. EndContextRendering (EndFrameRendering)

5. 行为列表

1. 场景剔除Cull
  1. 执行 context.Cull() 得到 cullResults,初始化RenderingData(本帧Camera用的数据容器)
2. Renderer. Setup
  1. 决定本帧有哪些RenderPass ( m_ ActiveRenderPassQueue ), forward deferred管线pass不同
  2. RenderPass渲染步骤。简单地说,渲染pass是渲染管道的单个执行。渲染pass将输出图像渲染到内存中的一组帧缓冲区附件中。
  3. 根据RenderingData决定是否需要每个pass
    在这里插入图片描述
    在这里插入图片描述
3. Renderer. Execute
  1. 按照RenderPassEvent(渲染队列)对pass列表排序
  2. 分阶段执行RenderBlock ( BeforeRendering, MainRenderingOpaque,MainRenderingTransparent, AfterRendering),每 个RenderBlock含有这个阶段的所有RenderPass
  3. 阶段之间执行一些shader uniform ( light, camera, time )设置工作
  4. 执行每个RenderPass的Execute,内部调用Context的各种底层接口
  5. Execute完成后 , 执行Context.Submit()提交渲染命令
4. 总结
  1. 使用SRP之后,由RenderPipelineAsset创建RenderPipeline,每帧走RenderPipeline.Render()
  2. URP内部RenderData创建Renderer,Camera可以指定使用的Renderer
  3. URP中,RenderPipeline.Render()主要对每个Camera执行RenderCameraStack,其中对stack里的每个Camera执行RenderSingleCamera
  4. RenderSingleCamera创建RenderPass队列,并逐个Execute
  5. RenderPass内部调用ScriptableRenderContext的底层接口,实现制定渲染任务

3. 贴画效果

1. 实现方法

  1. 为当前使用的URP RendererData添加组件Decal renderer feature ( 否则无法使用Projector)
  2. 为空对象添加Decal Projector
  3. 为Decal Projector添加Decal材质
    在这里插入图片描述
    在这里插入图片描述

2. Decal设置 Technique(贴花渲染技术)

1. DBuffer
  1. 用于贴花的几个buffer,需要支持MRT,先将贴花信息(base color, normal, MAOS金属度AO光滑度)信息输出到3个
    buffer上,之后渲染场景时将DBuffer信息考虑进去,一并画到物体上(先算贴花,再画物体)
  2. 对于DBuffer,添加三个pass:
    • CopyDepthPass
    • DBufferRenderPass
    • ForwardEmissivePass
  3. DBufferRenderPass内部有DecalDrawSystem,每个Decal Projector对应一个entity,逐个Draw,实际上就是画Cube
2. Screen Space
  1. 不用MRT,先画物体得到屏幕空间深度,再画贴花,因为缺乏一些其他信息,效果可能比DBuffer差
3. GBuffer
  1. 延迟管线下使用

3. 实现原理

在这里插入图片描述

Shader "Decal/DepthDecal"
{
    
    
	Properties
	{
    
    
		_MainTex ("Texture", 2D) = "white" {
    
    }
	}
	
	SubShader
	{
    
    
		Tags {
    
    "Queue"="Transparent+100"}
		Pass
		{
    
    
			Blend SrcAlpha OneMinusSrcAlpha
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"
 
			struct appdata
			{
    
    
				float4 vertex : POSITION;
			};
 
			struct v2f
			{
    
    
				float4 vertex : SV_POSITION;
				float4 screenPos : TEXCOORD1;
				float3 ray : TEXCOORD2;
			};
 
			sampler2D _MainTex;
			sampler2D_float _CameraDepthTexture;
			
			v2f vert (appdata v)
			{
    
    
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				// 屏幕坐标
				o.screenPos = ComputeScreenPos(o.vertex);
				// 从屏幕发出的射线
				o.ray =UnityObjectToViewPos(v.vertex) * float3(-1,-1,1);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
    
    
				//深度重建视空间坐标
				float2 screenuv = i.screenPos.xy / i.screenPos.w;
				float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, screenuv);
				// 深度
				float viewDepth = Linear01Depth(depth) * _ProjectionParams.z;
				// 射线方向✖️深度得到表面坐标
				float3 viewPos = i.ray * viewDepth / i.ray.z;
				//转化到世界空间坐标
				float4 worldPos = mul(unity_CameraToWorld, float4(viewPos, 1.0));
				//转化为物体空间坐标
				float3 objectPos = mul(unity_WorldToObject, worldPos);
				//剔除掉在立方体外面的内容
				clip(float3(0.5, 0.5, 0.5) - abs(objectPos));
				//使用物体空间坐标的xz坐标作为采样uv
				float2 uv = objectPos.xz + 0.5;
				fixed4 col = tex2D(_MainTex, uv);
				return col;
			}
			ENDCG
		}
		
	}
}

4. URP的pass通道

1. 基础pass

在这里插入图片描述

2. 插入额外pass

  1. URP内置RenderObjects Decal ScreenSpaceAmbientOcclusion ScreenSpaceShadows,可自行添加 ScriptableRendererFeature 子类并插入Renderer
  2. 每帧Setup时走RendererFeature的AddRenderPasses添加pass
  3. 对所有pass排序,逐个执行
    在这里插入图片描述

5. Renderer Feature

1. 使用

在和上文贴花的相同位置添加,用于在特定渲染后执行渲染

2. 参数

01 Name:首先是这个Feature的名字;
02 Event (事件):当Unity执行这个Renderer Feature 的时候,这个事件Event在通用渲染管线中的执行顺序;
03 Filters:这个设置允许我们给这个Renderer Feature 去配置要渲染哪些对象;这里面有两个参数,一个是Queue,一个是Layer Mask。
Queue:这个Feature选择渲染透明物体还是不透明物体;
Layer Mask:这个Feature选择渲染哪个图层中的对象;
04 Shader Passes Name:如果shader中的一个pass具有 LightMode Pass 这个标记的话,那我们的Renderer Feature仅处理 LightMode Pass Tag 等于这个Shader Passes Name的Pass。
05 Overrides:使用这个Renderer Feature 进行渲染时,这部分的设置可让我们配置某些属性进行重写覆盖。
Material:渲染对象时,Unity会使用该材质替换分配给它的材质。
Depth:选择此选项可以指定这个Renderer Feature如何影响或使用深度缓冲区。此选项包含以下各项:
Write Depth:写入深度,此选项定义渲染对象时这个Renderer Feature是否更新深度缓冲区。
Depth Test:深度测试,决定renderer feature是否渲染object的片元。
Stencil:选中此复选框后,Renderer将处理模板缓冲区值。
Camera:选择此选项可让您覆盖以下“摄像机”属性:
Field of View:渲染对象时,渲染器功能使用此Field of View(视场),而不是在相机上指定的值。
Position Offset:渲染对象时,Renderer Feature将其移动此偏移量。
Restore:选中此选项,在此Renderer Feature中执行渲染过程后,Renderer Feature将还原原始相机矩阵。
在这里插入图片描述

6. URP的shader

1. 声明为URP shader

需要在SubShader的tag中注明

Tags{
    
    "RenderPipeline"="UniversalPipeline"}

2. 定义不同的Pass通道

  1. 这个是urp的默认渲染pass,里面可以处理多光源,自发光,以及环境光和雾等等。
Tags{
    
    "LightMode"="UniversalForward"}
  1. 这个是用来渲染物体阴影投射的
Tags{
    
    "LightMode" = "ShadowCaster"}
  1. 这个是渲染深度图的,如果你要在屏幕空间做一些东西,需要渲染此pass渲染。
Tags{
    
    "LightMode" = "DepthOnly"}
  1. 这个是渲染一张带有深度和法向的纹理图的
Tags{
    
    "LightMode" = "DepthNormals"}
  1. 这个pass是用来烘焙光照贴图的
Tags{
    
    "LightMode" = "Meta"}
  1. 这个是用作2d渲染中的,一般我们用unity都是3d,所以这个pass我是直接忽略了。
Tags{
    
     "LightMode" = "Universal2D" }

3. HlSL

  1. ShaderLab是一个包含Shader语言的代码声明块。在Built-In管线中我们常使用CG语言来写Shader,但是在URP中,一般使用HLSL。CG语言和HLSL语言都是C风格的语言,写法上没有太大差别。
  2. 由于使用语言不同,用于包含CG语言的CGPROGRAM和ENDCG需要改成包含HLSL的HLSLPROGRAM和ENDHLSL。
HLSLPROGRAM
    ……
ENDHLSL

4. 头文件

使用的底层代码发生了变化,默认包含的头文件需要从CG的 #include "UnityCG.cginc"改为#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

除了核心的工具库外,其他如光照、阴影等要包含的头文件也需要修改。

使用了不同的头文件后,自然一些使用各种工具函数也不一样,比如顶点着色器中转换空间的方法UnityObjectToClipPos(v.vertex);在URP中就使用GetVertexPositionInputs(IN.positionOS.xyz);来获取一个存储了各个空间Position坐标的结构体,再从中获取裁剪空间中的位置坐标。

VertexPositionInputs positionInputs = GetVertexPositionInputs(IN.positionOS.xyz);
OUT.positionCS = positionInputs.positionCS;

5. CBUFFER

为了支持SRP Batcher,Shader中要将所有暴露出的参数(贴图除外)给包含到CBUFFER_START(UnityPerMaterial)与CBUFFER_END之间。并且为了保证之后的每个Pass都能拥有一样的CBUFFER,这一段代码需要写在SubShader之内,其它Pass之前。

CBUFFER_START(UnityPerMaterial)
    float4 _BaseMap_ST;
    float _Color;
CBUFFER_END

6. 贴图采样

在CG中我们一般使用sampler2D _Texture来采样贴图。在HLSL中,贴图采样需要声明为Sampler。具体的采样方法也从原来的tex2D(Texture tex,float uv);变为SAMPLE_TEXTURE2D(Texture tex, Sampler sampler,float uv);

TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
……
float4 frag(Varings IN):SV_Target
{
    
    
    ……
    half4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);     
    ……
}

7. 示例

外描边效果

Shader "Unlit/outline"
{
    
    
    Properties
    {
    
    
        _BaseMap ("Base Texture", 2D) = "white"{
    
    }
        _BaseColor ("Color", color) = (0, 0, 0, 0)
        _Outline ("Outline", Range(0, 1)) = 0.1
    }
    SubShader
    {
    
    
        Tags
        {
    
    
            // 表明这是一个URP Shader
            "RenderPipeline"="UniversalPipeline"
        }
        Cull Front
        
        HLSLINCLUDE
         // 引入头文件,类似于 CG中核心代码库 "UnityCG.cginc"
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
       
        // 除了贴图外,要暴露在Inspector面板上的变量都需要缓存到CBUFFER中
        CBUFFER_START(UnityPerMaterial)
        // 在CG中会写成sampler2D _MainTex;
        TEXTURE2D(_BaseMap);
        SAMPLER(sampler_BaseMap);
        float4 _BaseMap_ST;
        float4 _BaseColor;
        float _Outline;
        CBUFFER_END
        
        ENDHLSL
 
        Pass
        {
    
    
            // 这个是urp的默认渲染pass,里面可以处理多光源,自发光,以及环境光和雾等等。
            Tags{
    
    "LightMode"="UniversalForward"}

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
 
            struct Attributes
            {
    
    
                float4 positionOS : POSITION;
                float2 uv : TEXCOORD;
                float3 normal : NORMAL;
            };
            struct Varings
            {
    
    
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD;
            };
 
 
            Varings vert(Attributes IN)
            {
    
    
                Varings OUT;
                IN.positionOS.xyz += IN.normal * _Outline;
                //在CG里面,我们这样转换空间坐标 o.vertex = UnityObjectToClipPos(v.vertex);
                VertexPositionInputs positionInputs = GetVertexPositionInputs(IN.positionOS.xyz);
                OUT.positionCS = positionInputs.positionCS;
 
                OUT.uv=TRANSFORM_TEX(IN.uv,_BaseMap);
                return OUT;
            }
 
            float4 frag(Varings IN):SV_Target
            {
    
    
                //在CG里,我们这样对贴图采样 fixed4 col = tex2D(_MainTex, i.uv);
                half4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);                
                return baseMap * _BaseColor;
            }
            ENDHLSL
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_50682713/article/details/126828917