|
前言:
已经进入“高级篇”啦,希望大家多多支持,多多关注,这将对我产生非常愉悦的正反馈~
“《Unity Shader 入门精要》从Bulit-in 到URP”是一个帮助Unity Shader学习者以冯乐乐女神《Unity Shader 入门精要》为基础学习用HLSL语言编写URP着色器的案例教学系列。
作者自学能力有限,抛砖引玉,如有建议和问题请各位大佬和同仁交流指正。
如何在URP中实现后处理参见:
《Unity Shader 入门精要》从Bulit-in 到URP (HLSL)之后处理(Post-processing : RenderFeature + VolumeComponent)
Bulit-in版:
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shaders Book/Chapter 12/Gaussian Blur" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_BlurSize ("Blur Size", Float) = 1.0
}
SubShader {
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
half4 _MainTex_TexelSize;
float _BlurSize;
struct v2f {
float4 pos : SV_POSITION;
half2 uv[5]: TEXCOORD0;
};
v2f vertBlurVertical(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.texcoord;
o.uv[0] = uv;
o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
return o;
}
v2f vertBlurHorizontal(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.texcoord;
o.uv[0] = uv;
o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
return o;
}
fixed4 fragBlur(v2f i) : SV_Target {
float weight[3] = {0.4026, 0.2442, 0.0545};
fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
for (int it = 1; it < 3; it++) {
sum += tex2D(_MainTex, i.uv[it*2-1]).rgb * weight[it];
sum += tex2D(_MainTex, i.uv[it*2]).rgb * weight[it];
}
return fixed4(sum, 1.0);
}
ENDCG
ZTest Always Cull Off ZWrite Off
Pass {
NAME &#34;GAUSSIAN_BLUR_VERTICAL&#34;
CGPROGRAM
#pragma vertex vertBlurVertical
#pragma fragment fragBlur
ENDCG
}
Pass {
NAME &#34;GAUSSIAN_BLUR_HORIZONTAL&#34;
CGPROGRAM
#pragma vertex vertBlurHorizontal
#pragma fragment fragBlur
ENDCG
}
}
FallBack &#34;Diffuse&#34;
}
URP版:
Shader &#34;Unlit/Chapter12-GaussianBlur&#34;
{
Properties {
_MainTex (&#34;Base (RGB)&#34;, 2D) = &#34;white&#34; {}
_BlurSize (&#34;Blur Size&#34;, Float) = 1.0
}
SubShader
{
Tags { &#34;RenderPipeline&#34; = &#34;UniversalPipeline&#34; }
HLSLINCLUDE
#include &#34;Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl&#34;
#include &#34;Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl&#34;
CBUFFER_START(UnityPerMaterial)
half4 _MainTex_TexelSize;
float _BlurSize;
CBUFFER_END
TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex);
struct appdata{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
half2 uv[5] : TEXCOORD0;
};
v2f vertBlurVertical(appdata v) {
v2f o;
o.pos = TransformObjectToHClip(v.vertex);
half2 uv = v.texcoord;
o.uv[0] = uv;
o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
//和属性_BlurSize相乘来控制采样距离。在高斯核维数不变的情况下,_BlurSize越大,模糊程度越高
return o;
}
v2f vertBlurHorizontal(appdata v) {
v2f o;
o.pos = TransformObjectToHClip(v.vertex);
half2 uv = v.texcoord;
o.uv[0] = uv;
o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
return o;
}
half4 fragBlur(v2f i) : SV_Target {
float weight[3] = {0.4026, 0.2442, 0.0545};
//高斯权重
half3 sum = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv[0]).rgb * weight[0];
for (int it = 1; it < 3; it++) {
sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv[it*2-1]).rgb * weight[it];
sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv[it*2]).rgb * weight[it];
}
return half4(sum, 1.0);
}
ENDHLSL
ZTest Always Cull Off ZWrite Off
Pass {
NAME &#34;GAUSSIAN_BLUR_VERTICAL&#34;
HLSLPROGRAM
#pragma vertex vertBlurVertical
#pragma fragment fragBlur
ENDHLSL
}
Pass {
NAME &#34;GAUSSIAN_BLUR_HORIZONTAL&#34;
HLSLPROGRAM
#pragma vertex vertBlurHorizontal
#pragma fragment fragBlur
ENDHLSL
}
}
FallBack &#34;Packages/com.unity.render-pipelines.universal/FallbackError&#34;
}
URP后处理脚本
因为和上面提供的(Post-processing : RenderFeature + VolumeComponent)模板有些许差异(降采样、两个Pass分别渲染、迭代处理),因此在此提供对应源码。
using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class GaussianBlur : ScriptableRendererFeature
{
[Serializable, VolumeComponentMenu(&#34;Ding Post-processing/12.4 GaussianBlur&#34;)]
public class CustomVolumeComponent : DingVolumeComponentBase
{
public ClampedFloatParameter BlurSpread = new ClampedFloatParameter(0.5f, 0.1f, 3f);
public ClampedIntParameter Iterations = new ClampedIntParameter(3, 1, 4);
public ClampedIntParameter DownSample = new ClampedIntParameter(2, 1, 8);
public override bool IsActive() => isRender.value;
public override bool IsTileCompatible() => false;
}
class CustomRenderPass : ScriptableRenderPass
{
public Material material;
//RT的滤波模式
public FilterMode filterMode {get; set;}
//当前渲染阶段的colorRT
//RenderTargetIdentifier、RenderTargetHandle都可以理解为RT,Identifier为camera提供的需要被应用的texture,Handle为被shader处理渲染过的RT
private RenderTargetIdentifier source {get; set;}
private RenderTargetHandle destination {get; set;}
//辅助RT
private RenderTargetHandle tempTexture0;
private RenderTargetHandle tempTexture1;
string m_ProfilerTag;
//Profiling上显示
public CustomVolumeComponent volume;
ProfilingSampler m_ProfilingSampler = new ProfilingSampler(&#34;URPDing&#34;);
public CustomRenderPass(RenderPassEvent renderPassEvent, Shader shader, CustomVolumeComponent volume, string tag){
//确定在哪个阶段插入渲染
this.renderPassEvent = renderPassEvent;
this.volume = volume;
if(shader == null){return;}
this.material = CoreUtils.CreateEngineMaterial(shader);
m_ProfilerTag = tag;
//初始化辅助RT的名字
tempTexture0.Init(&#34;_TempRTexture0&#34;);
tempTexture1.Init(&#34;_TempRTexture1&#34;);
}
public void Setup(RenderTargetIdentifier source, RenderTargetHandle destination){
this.source = source;
this.destination = destination;
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
if (!volume.IsActive()) {
return;
}
CommandBuffer cmd = CommandBufferPool.Get(&#34;m_ProfilerTag&#34;);
//using 方法可以实现在FrameDebug上查看渲染过程
using(new ProfilingScope(cmd, m_ProfilingSampler)){
ref var cameraData = ref renderingData.cameraData;
int w = cameraData.camera.scaledPixelWidth / volume.DownSample.value;
int h = cameraData.camera.scaledPixelHeight / volume.DownSample.value;
cmd.GetTemporaryRT(tempTexture0.id, w, h, 0, filterMode);
Blit(cmd, source, tempTexture0.Identifier());
for(int i = 0; i < volume.Iterations.value; i++){
material.SetFloat(&#34;_BlurSize&#34;, 1.0f + i * volume.BlurSpread.value);
cmd.GetTemporaryRT(tempTexture1.id, w, h, 0, filterMode);
Blit(cmd, tempTexture0.Identifier(), tempTexture1.Identifier(), material, 0);
cmd.ReleaseTemporaryRT(tempTexture0.id);
tempTexture0 = tempTexture1;
cmd.GetTemporaryRT(tempTexture1.id, w, h, 0, filterMode);
Blit(cmd, tempTexture0.Identifier(), tempTexture1.Identifier(), material, 1);
cmd.ReleaseTemporaryRT(tempTexture0.id);
tempTexture0 = tempTexture1;
}
Blit(cmd, tempTexture0.Identifier(), source);
}
//执行渲染
context.ExecuteCommandBuffer(cmd);
//释放回收
CommandBufferPool.Release(cmd);
}
public override void FrameCleanup(CommandBuffer cmd){
base.FrameCleanup(cmd);
cmd.ReleaseTemporaryRT(tempTexture0.id);
cmd.ReleaseTemporaryRT(tempTexture1.id);
}
}
[System.Serializable]
public class Settings{
public RenderPassEvent Event = RenderPassEvent.AfterRenderingTransparents;
public Shader shader;
}
public Settings settings = new Settings();
CustomVolumeComponent volume;
CustomRenderPass m_ScriptablePass;
/// <inheritdoc/>
//feature被创建时调用
public override void Create()
{
var stack = VolumeManager.instance.stack;
volume = stack.GetComponent<CustomVolumeComponent>();
if (volume == null) {
CoreUtils.Destroy(m_ScriptablePass.material);
return;
}
m_ScriptablePass = new CustomRenderPass(settings.Event, settings.shader, volume, name);
}
// Here you can inject one or multiple render passes in the renderer.
// This method is called when setting up the renderer once per-camera.
//每一帧都会调用
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
var src = renderer.cameraColorTarget;
var dest = RenderTargetHandle.CameraTarget;
if(settings.shader == null){
Debug.LogWarningFormat(&#34;shader丢失&#34;,GetType().Name);
return;
}
//将当前渲染的colorRT传到Pass中
m_ScriptablePass.Setup(src, dest);
//将Pass添加到渲染队列中
renderer.EnqueuePass(m_ScriptablePass);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
}
效果图:
如有收获,请留下“关注”和“赞同”~ |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|