本文最终效果
文章目录
- 一、前言
- 二、环境准备
-
- 二、方案一:写Shader实现
- 1、Shader脚本:UrpDecal.shader
- 2、材质球
- 3、创建Cube
- 4、地面场景
- 5、添加Renderer Feature: Decal
- 6、移动DecalCube,与地面交叉
- 7、运行效果
- 三、方案二:使用URP Decal Projector
- 1、添加Renderer Feature: Decal
- 2、创建Decal Shader Graph
- 3、材质球
- 4、空物体挂 URPDecalProjector组件
- 4、设置组件参数
- 5、运行效果
- 四、工程源码
- 五、完毕
一、前言
嗨,大家好,我是新发。
我平时偶尔会打打Dota2
,在Dota
里面,技能释放前会有一个地面贴花效果来作为范围的显示,比如深渊领主的这个技能,如下
我们可以看到技能范围特效是可以投射到场景地面和物体上(比如树木),这个在Unity
中如何去实现呢?本文我以URP
渲染管线为例,讲一下这个技能地面贴花效果的制作过程吧~
注:本文我使用的Unity
版本为2021.3.1f1c1
,Universal RP
版本为12.1.6
二、环境准备
1、URP环境准备
首先通过PackageManager
安装Universal RP
,在Project
窗口中右键鼠标,点击菜单Create / Rendering / URP Asset (with Universal Renderer)
,
此时会创建出两个文件,如下
打开Project Settings
窗口,点击Graphics
,把Uinversal Render Pipeline Asset
文件拖给Scriptable Render Pipeline Settings
,如下
2、技能范围图案
接着我们去找一张技能范围的图案,我找了一张这样的,
接下来就是具体的实现过程了。有两个方案,下面我分别给大家讲解一下。
二、方案一:写Shader实现
1、Shader脚本:UrpDecal.shader
我在GitHub
上看到有个外国小哥写了个URP
渲染管线的贴花Shader
,
GitHub
地址:https://github.com/ColinLeung-NiloCat/UnityURPUnlitScreenSpaceDecalShader
我给他的Shader
添加了中文注释,方便大家阅读,如下
Shader "Universal Render Pipeline/NiloCat Extension/Screen Space Decal/Unlit"
{
Properties
{
[Header(Basic)]
_MainTex("Texture", 2D) = "white" {}
[HDR]_Color("_Color (default = 1,1,1,1)", color) = (1,1,1,1)
[Header(Blending)]
[Enum(UnityEngine.Rendering.BlendMode)]_SrcBlend("_SrcBlend (default = SrcAlpha)", Float) = 5
[Enum(UnityEngine.Rendering.BlendMode)]_DstBlend("_DstBlend (default = OneMinusSrcAlpha)", Float) = 10
[Header(Alpha remap(extra alpha control))]
_AlphaRemap("_AlphaRemap (default = 1,0,0,0) _____alpha will first mul x, then add y (zw unused)", vector) = (1,0,0,0)
[Header(Prevent Side Stretching(Compare projection direction with scene normal and Discard if needed))]
[Toggle(_ProjectionAngleDiscardEnable)] _ProjectionAngleDiscardEnable("_ProjectionAngleDiscardEnable (default = off)", float) = 0
_ProjectionAngleDiscardThreshold("_ProjectionAngleDiscardThreshold (default = 0)", range(-1,1)) = 0
[Header(Mul alpha to rgb)]
[Toggle]_MulAlphaToRGB("_MulAlphaToRGB (default = off)", Float) = 0
[Header(Ignore texture wrap mode setting)]
[Toggle(_FracUVEnable)] _FracUVEnable("_FracUVEnable (default = off)", Float) = 0
[Header(Stencil Masking)]
_StencilRef("_StencilRef", Float) = 0
[Enum(UnityEngine.Rendering.CompareFunction)]_StencilComp("_StencilComp (default = Disable) _____Set to NotEqual if you want to mask by specific _StencilRef value, else set to Disable", Float) = 0
[Header(ZTest)]
[Enum(UnityEngine.Rendering.CompareFunction)]_ZTest("_ZTest (default = Disable) _____to improve GPU performance, Set to LessEqual if camera never goes into cube volume, else set to Disable", Float) = 0
[Header(Cull)]
[Enum(UnityEngine.Rendering.CullMode)]_Cull("_Cull (default = Front) _____to improve GPU performance, Set to Back if camera never goes into cube volume, else set to Front", Float) = 1
[Header(Unity Fog)]
[Toggle(_UnityFogEnable)] _UnityFogEnable("_UnityFogEnable (default = on)", Float) = 1
[Header(Support Orthographic camera)]
[Toggle(_SupportOrthographicCamera)] _SupportOrthographicCamera("_SupportOrthographicCamera (default = off)", Float) = 0
}
SubShader
{
Tags { "RenderType" = "Overlay" "Queue" = "Transparent-499" "DisableBatching" = "True" }
Pass
{
Stencil
{
Ref[_StencilRef]
Comp[_StencilComp]
}
Cull[_Cull]
ZTest[_ZTest]
ZWrite off
Blend[_SrcBlend][_DstBlend]
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#pragma target 3.0
#pragma shader_feature_local_fragment _ProjectionAngleDiscardEnable
#pragma shader_feature_local _UnityFogEnable
#pragma shader_feature_local_fragment _FracUVEnable
#pragma shader_feature_local_fragment _SupportOrthographicCamera
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct appdata
{
float3 positionOS : POSITION;
};
struct v2f
{
float4 positionCS : SV_POSITION;
float4 screenPos : TEXCOORD0;
float4 viewRayOS : TEXCOORD1;
float4 cameraPosOSAndFogFactor : TEXCOORD2;
};
sampler2D _MainTex;
sampler2D _CameraDepthTexture;
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_ST;
float _ProjectionAngleDiscardThreshold;
half4 _Color;
half2 _AlphaRemap;
half _MulAlphaToRGB;
CBUFFER_END
v2f vert(appdata input)
{
v2f o;
VertexPositionInputs vertexPositionInput = GetVertexPositionInputs(input.positionOS);
o.positionCS = vertexPositionInput.positionCS;
#if _UnityFogEnable
o.cameraPosOSAndFogFactor.a = ComputeFogFactor(o.positionCS.z);
#else
o.cameraPosOSAndFogFactor.a = 0;
#endif
o.screenPos = ComputeScreenPos(o.positionCS);
float3 viewRay = vertexPositionInput.positionVS;
o.viewRayOS.w = viewRay.z;
viewRay *= -1;
float4x4 ViewToObjectMatrix = mul(UNITY_MATRIX_I_M, UNITY_MATRIX_I_V);
o.viewRayOS.xyz = mul((float3x3)ViewToObjectMatrix, viewRay);
o.cameraPosOSAndFogFactor.xyz = mul(ViewToObjectMatrix, float4(0,0,0,1)).xyz;
return o;
}
half4 frag(v2f i) : SV_Target
{
i.viewRayOS.xyz /= i.viewRayOS.w;
float2 screenSpaceUV = i.screenPos.xy / i.screenPos.w;
float sceneRawDepth = tex2D(_CameraDepthTexture, screenSpaceUV).r;
float3 decalSpaceScenePos;
#if _SupportOrthographicCamera
if(unity_OrthoParams.w)
{
#if defined(UNITY_REVERSED_Z)
sceneRawDepth = 1-sceneRawDepth;
#endif
float sceneDepthVS = lerp(_ProjectionParams.y, _ProjectionParams.z, sceneRawDepth);
float2 viewRayEndPosVS_xy = float2(unity_OrthoParams.xy * (i.screenPos.xy - 0.5) * 2 );
float4 vposOrtho = float4(viewRayEndPosVS_xy, -sceneDepthVS, 1);
float3 wposOrtho = mul(UNITY_MATRIX_I_V, vposOrtho).xyz;
decalSpaceScenePos = mul(GetWorldToObjectMatrix(), float4(wposOrtho, 1)).xyz;
}
else
{
#endif
float sceneDepthVS = LinearEyeDepth(sceneRawDepth, _ZBufferParams);
decalSpaceScenePos = i.cameraPosOSAndFogFactor.xyz + i.viewRayOS.xyz * sceneDepthVS;
#if _SupportOrthographicCamera
}
#endif
float2 decalSpaceUV = decalSpaceScenePos.xy + 0.5;
float shouldClip = 0;
#if _ProjectionAngleDiscardEnable
float3 decalSpaceHardNormal = normalize(cross(ddx(decalSpaceScenePos), ddy(decalSpaceScenePos)));
shouldClip = decalSpaceHardNormal.z > _ProjectionAngleDiscardThreshold ? 0 : 1;
#endif
clip(0.5 - abs(decalSpaceScenePos) - shouldClip);
float2 uv = decalSpaceUV.xy * _MainTex_ST.xy + _MainTex_ST.zw;
#if _FracUVEnable
uv = frac(uv);
#endif
half4 col = tex2D(_MainTex, uv);
col *= _Color;
col.a = saturate(col.a * _AlphaRemap.x + _AlphaRemap.y);
col.rgb *= lerp(1, col.a, _MulAlphaToRGB);
#if _UnityFogEnable
col.rgb = MixFog(col.rgb, i.cameraPosOSAndFogFactor.a);
#endif
return col;
}
ENDHLSL
}
}
}
首先有一个前提,就是模型必须使用Cube
。
最核心的一步就是通过深度信息还原世界空间坐标,再转模型空间坐标(也就是贴花空间坐标),计算出贴花UV
,对贴花图案采样输出。
其中关于如何通过深度纹理重建世界坐标,大家可以阅读 冯乐乐 写的 《Unity Shader 入门精要》 这本书第13章的13.3.1
小结,她讲得很好,建议大家多看书学习
我们把上面的Shader
保存为UrpDecal.shader
,如下
2、材质球
我们创建一个材质球,重命名为UrpDecal
,并使用刚刚的shader
,如下
设置一下材质球参数,如下
3、创建Cube
创建一个Cube
,重命名为DecalCube
,
把上面的材质球赋给这个Cube
,
4、地面场景
简单搭建一下地面场景,
5、添加Renderer Feature: Decal
点击Universal Render Pipeline Asset_Renderer
,点击Add Renderer Feature
,然后点击Decal
,
如下
6、移动DecalCube,与地面交叉
选中DecalCube
,调整下角度和缩放,
然后移动DecalCube
,让它与地面交叉,此时我们就可以看到想要的贴花效果了
7、运行效果
运行效果如下
三、方案二:使用URP Decal Projector
在默认渲染管线中,我们可以使用Projector
来实现贴花效果,比较常见的是假阴影的实现。
在URP
渲染管线中,我们可以使用URP Decal Projector
。
1、添加Renderer Feature: Decal
跟上面一样,也得添加Decal
,
2、创建Decal Shader Graph
点击菜单Create / Shader Graph / URP / Decal Shader Graph
,如下
双击打开它ShaderGraph
,
连线图如下
3、材质球
我们创建一个材质球并重命名为DecalShaderGraph
,把上面的ShaderGraph
赋给它,如下
设置一下材质球参数
4、空物体挂 URPDecalProjector组件
创建一个空物体,重命名为URPDecalProjector
,
给它挂上URPDecalProjector
组件
4、设置组件参数
设置一下组件参数,如下
5、运行效果
调整一下URPDecalProjector
的角度和缩放,
移动URPDecalProjector
使其与地面交叉,效果如下
我们还可以在ShaderGraph
中给加个UV
旋转,让图案转起来,
效果如下
四、工程源码
以上两种方案放在一起,效果如下,
本文工程源码我已上传到GitCode
,地址:https://gitcode.net/linxinfa/unityurpdecaldemo
感兴趣的同学可自行下载学习
五、完毕
好了,夜深了,收工睡觉。
我是林新发,https://blog.csdn.net/linxinfa
一个在小公司默默奋斗的Unity
开发者,希望可以帮助更多想学Unity
的人,共勉~
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)