——我在URP基础的Lit.shader上添加了描边属性(描边颜色和宽度),并且重新定义了Priority区间(从±50扩展为±1000),显示材质球RenderQueue的大小(省去了你切换到Debug模式查看CustomRenderQueue的功夫)
(对于shader我刚刚入门,写这个就想整理下这三天捣鼓的一点东西,可能有愚蠢错误的地方,还望指教)
描边Pass可以参考这篇教程,非常详实https://blog.csdn.net/puppet_master/article/details/54000951
第一个Pass实现描边效果,第二个Pass实现基础着色。第二个Pass不想自己写的话,也可以用UsePass来借用别的shader。
但是在URP/LWRP里默认只走一个Pass,如果想要运行多个Pass,可以使用Tags:
1 2 3 | Tags{"LightMode" = "UniversalForward"} //LWRP不可用此Tag Tags{"LightMode" = "LightweightForward" } Tags{"LightMode" = "SRPDefaultUnlit"} |
一个Pass里放一个Tags,这样看的话似乎URP-shader最多3个Pass,LWRP-shader最多2个Pass?
“To do a multi pass shader in the Lightweight pipeline you have to have one pass have no lightmode defined (or be using the default, which is “LightMode” = “SRPDefaultUnlit”), and one pass use “LightMode” = “LightweightForward”. The lightweight pipeline appears to only use the first pass it finds of each tag, so no more 3+ pass shaders.”——来自 https://forum.unity.com/threads/transparency-using-the-lwrp.550711/
先来看看URP基础的Lit.shader:
- 把它的Properties全部复制黏贴进我们的描边shader
- 使用UsePass调用它的5个Pass(只用第一个Pass好像也够了)(?)
- 它第一个Pass已经使用了Tags{“LightMode” = “UniversalForward”} ,所以如果我们想再加一个描边Pass,就必须在Pass里加上其他Tags,或者什么都不加也可以(什么都不加就是默认"LightMode" = “SRPDefaultUnlit” )(?)
- 为了获得和原Lit.shader一致的GUI布局,注意最后一行【CustomEditor “UnityEditor . Rendering . Universal . ShaderGUI . LitOutlineShaderAddQueue”】,当然这也不是必须的,就是写一个shaderGUI脚本会让面板好看点罢了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | //这是一个在URP/Lit基础上加入了描边Pass的shader //如果看不到描边效果,是因为被后渲染的天空盒覆盖了,需要设置Render Queue=Transparent //请配合使用脚本 LitOutlineShaderAddQueue.cs 以获得和原shader一致的GUI布局 Shader "Universal Render Pipeline/LitOutline" { Properties { _OutlineCol("OutlineCol", Color) = (1,1,0,1) _OutlineFactor("OutlineFactor", Range(0, 1)) = 0.1 _ShowRenderQueue("ShowRenderQueue",Int)= -1 //... //下面照搬Lit.shader的Properties //复制粘贴大段代码页面会崩溃,我这里就……懒一下 } SubShader { Tags{"RenderType" = "Transparent" "RenderPipeline" = "UniversalPipeline" "IgnoreProjector" = "True"} LOD 300 UsePass "Universal Render Pipeline/Lit/ForwardLit" //我也不知道后面四个pass具体什么作用,这样引用是否能完整拷贝Lit-shader? UsePass "Universal Render Pipeline/Lit/ShadowCaster" UsePass "Universal Render Pipeline/Lit/DepthOnly" UsePass "Universal Render Pipeline/Lit/Meta" UsePass "Universal Render Pipeline/Lit/Universal2D" Pass { Name "Outline" Cull Front Zwrite Off CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fog #include "UnityCG.cginc" struct v2f { UNITY_FOG_COORDS(0) float4 vertex : SV_POSITION; } fixed4 _OutlineCol; float _OutlineFactor; v2f vert (appdata_base v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); float3 vnormal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);//将法线方向转换到视空间 vnormal = normalize(vnormal);//为了_OutlineFactor不受物体scale影响 float2 offset = TransformViewToProjection(vnormal.xy);//将视空间法线xy坐标转化到投影空间 o.vertex.xy += offset * _OutlineFactor;//在最终投影阶段输出进行偏移操作 UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { return _OutlineCol; } ENDCG } } FallBack "Hidden/Universal Render Pipeline/FallbackError" CustomEditor "UnityEditor.Rendering.Universal.ShaderGUI.LitOutlineShaderAddQueue" } |
现在,我们写完了LitOutline.shader后还要再仿照原来的LitShader.cs写一个shaderGUI脚本
——直接复制一份LitShader.cs然后重命名为LitOutlineShaderAddQueue.cs开始修改:
- 添加MaterialProperty类型的变量,
- 然后用FindProperty()函数将变量与shader中的属性对应,
- 最后在OnGUI()函数里用 materialEditor.ShaderProperty()将变量显示到面板上
- 为了修改priority的区间,在BaseShaderGUI脚本里查找了一下它是在DrawAdvancedOptions()函数里诞生的,所以重写了这个函数
- 为了能时刻显示当前的RenderQueue,在BaseShaderGUI脚本里发现它是由MaterialChanged()函数更新的,所以也修改了下这个函数
1 2 3 4 5 6 7 8 9 10 11 | using System; using UnityEngine; using UnityEngine.Rendering; using UnityEditor.Rendering.Universal; //这个脚本一定要保存在Editor文件夹下,负责提供 LitOutline.shader的GUI布局 //这个脚本以LitShader.cs为基础,将priority区间扩大至±1000,并且显示RenderQueue数值(RenderQueue不可编辑) //添加了变量【outlineCol, outlineFactor】 变量【showRenderQueue】 //修改了FindProperties()函数 //修改了MaterialChanged()函数 //修改了DrawAdvancedOptions()函数(使queueOffsetRange = 1000) //添加了public override void OnGUI()函数 |
这里就复制黏贴一下我修改和添加的代码行,其他照抄的就点点点表示了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | namespace UnityEditor.Rendering.Universal.ShaderGUI { internal class LitOutlineShaderAddQueue : BaseShaderGUI { // Properties private LitGUI.LitProperties litProperties; //添加了变量【outlineCol, outlineFactor】 变量【showRenderQueue】 private MaterialProperty outlineCol, outlineFactor; protected MaterialProperty showRenderQueue { get; set; } // collect properties from the material properties public override void FindProperties(MaterialProperty[] properties) { base.FindProperties(properties); litProperties = new LitGUI.LitProperties(properties); //修改了FindProperties()函数 showRenderQueue = FindProperty("_ShowRenderQueue", properties, false); outlineCol = FindProperty("_OutlineCol", properties); outlineFactor = FindProperty("_OutlineFactor", properties); } // material changed check public override void MaterialChanged(Material material) { if (material == null) throw new ArgumentNullException("material"); SetMaterialKeywords(material, LitGUI.SetMaterialKeywords); //MaterialChanged()函数触发SetMaterialKeywords()函数触发SetupMaterialBlendMode(),从而更新了 material.renderQueue //修改了MaterialChanged()函数 showRenderQueue.floatValue = material.renderQueue; } public override void DrawSurfaceOptions(Material material){...} public override void DrawSurfaceInputs(Material material){...} public override void DrawAdvancedOptions(Material material) { if (litProperties.reflections != null && litProperties.highlights != null) { EditorGUI.BeginChangeCheck(); materialEditor.ShaderProperty(litProperties.highlights, LitGUI.Styles.highlightsText); materialEditor.ShaderProperty(litProperties.reflections, LitGUI.Styles.reflectionsText); if (EditorGUI.EndChangeCheck()) { MaterialChanged(material); } } //修改了DrawAdvancedOptions()函数(使queueOffsetRange = 1000) //base.DrawAdvancedOptions(material); materialEditor.EnableInstancingField(); if (queueOffsetProp != null) { EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = queueOffsetProp.hasMixedValue; int queueOffsetRange = 1000; //和原先相比就添加了这一行 var queue = EditorGUILayout.IntSlider(Styles.queueSlider, (int)queueOffsetProp.floatValue, -queueOffsetRange, queueOffsetRange); if (EditorGUI.EndChangeCheck()) { queueOffsetProp.floatValue = queue; } EditorGUI.showMixedValue = false; } } public override void AssignNewShaderToMaterial(...){...} //添加了public override void OnGUI()函数 public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props) { // render the default gui base.OnGUI(materialEditor, props); materialEditor.ShaderProperty(showRenderQueue, new GUIContent("RenderQueue(uneditable,controlled by Priority)")); materialEditor.ShaderProperty(outlineCol, new GUIContent("OutlineColor")); materialEditor.ShaderProperty(outlineFactor, new GUIContent("OutlineFactor")); } } } |
最后附一个鼠标悬停触发物体高亮的脚本:
1 | 在这里插入代码片 |
LWRP与URP的shader/shaderGUI脚本都是通用的,只需要把出现Lightweight Render Pipeline的地方替换Universal Render Pipeline即可。
待学习:
https://unity.cn/projects/unity-hdrp-custom-pass-post-processing-hou-chu-li-te-xiao-xue-xi-yi-zong-jie
Unity HDRP Custom Pass (Post processing) 后处理特效学习(一)总结,文字版,
主要介绍如何获取各种延迟渲染的缓冲数据。
比起之前发的文字版,进行了更新,补充了很多内容和图,如果对这个感兴趣的务必重新看一下。
https://unity.cn/projects/unity-hdrp-custom-pass-post-processing-hou-chu-li-te-xiao-xue-xi-er-zong-jie
Unity HDRP Custom Pass (Post processing) 后处理特效学习(二)总结,文字版,
主要介绍如何使用ddx、ddy进行勾边,,如何手动计算进行勾边。
比起视频,补充了很多内容和图。比如说补充了为什么ddx、ddy计算出来的勾边会断线,是GPU是如何计算的。
URP也可以做后处理,用RendererFeature,但是URP的后处理,你就没有GBuffer的数据了,那你只能从别的方面着手。就像官方的RendererFeature例子里的就有一个SobelFilter的勾边处理。
https://github.com/Unity-Technologies/UniversalRenderingExamples