第1部分:溶出度着色器
溶解着色器返回漂亮的效果,而且易于创建和理解; 今天,我们将在
Unity Shader Graph中进行制作 ,并在
HLSL上进行编写。
这是我们将创建的示例:
如何运作
要创建
溶解着色器,我们将必须使用“ Shader Graph”着色器中的
AlphaClipThreshold值,或使用称为
clip的HLSL函数。
本质上,我们告诉着色器
不要基于传递的
纹理和
值 来渲染像素 。 我们需要了解以下内容:
白色部分溶解得更快 。
我们将使用以下纹理:
您可以创建自己的-直线,三角形,但是任何东西! 请记住,
白色部分溶解得更快 。
我使用“云”滤镜在Photoshop中创建了此纹理。
即使您只对Shader Graph感兴趣,而对HLSL也不了解,我仍然建议您阅读本部分,因为这对于了解Unity Shader Graph如何在内部工作非常有用。
ls
在HLSL中,我们使用
clip(x)函数。
clip(x)函数丢弃所有值小于
零的像素。 因此,如果我们调用
clip(-1) ,我们将确保着色器永远不会渲染此像素。 您可以在
Microsoft Docs中了解有关
剪辑的更多信息。
属性
着色器需要两个属性,
Dissolve Texture和
Amount (将指示整个执行过程)。 与其他属性和变量一样,您可以随意调用它们。
Properties { //Your other properties //[...] //Dissolve shader properties _DissolveTexture("Dissolve Texture", 2D) = "white" {} _Amount("Amount", Range(0,1)) = 0 }
确保在CGPROGRAM SubShader之后添加以下内容(换句话说,声明变量):
sampler2D _DissolveTexture; half _Amount;
另外,不要忘记。 它们的名称必须与“属性”部分中的名称匹配。
功能介绍
我们通过采样
溶解的
纹理并获取
红色的
值来开始“
表面”或“
片段”功能。 PS我们的纹理以
灰度存储,即
R ,
G和
B的值相等,您可以
选择任意一个 。 例如,
白色为
(1,1,1) ,
黑色为
(0,0,0) 。
在我的示例中,我使用了表面着色器:
void surf (Input IN, inout SurfaceOutputStandard o) { half dissolve_value = tex2D(_DissolveTexture, IN.uv_MainTex).r;
就是这样! 我们可以将此过程应用于任何现有的着色器,并将其转变为
溶解着色器 !
这是Unity引擎的标准Surface Shader,已变成
双面 溶解着色器: Shader "Custom/DissolveSurface" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1)) = 0.0 //Dissolve properties _DissolveTexture("Dissolve Texutre", 2D) = "white" {} _Amount("Amount", Range(0,1)) = 0 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 Cull Off //Fast way to turn your material double-sided CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; }; half _Glossiness; half _Metallic; fixed4 _Color; //Dissolve properties sampler2D _DissolveTexture; half _Amount; void surf (Input IN, inout SurfaceOutputStandard o) { //Dissolve function half dissolve_value = tex2D(_DissolveTexture, IN.uv_MainTex).r; clip(dissolve_value - _Amount); //Basic shader function fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = ca; } ENDCG } FallBack "Diffuse" }
着色器图
如果需要使用Unity
Shader Graph创建此效果,则必须使用
AlphaClipThreshold值(其工作原理与HLSL中的
clip(x)不同)。 在此示例中,我创建了一个PBR着色器。
AlphaClipThreshold函数指示着色器丢弃其值小于其
Alpha值的所有像素。 例如,如果值为
0.3f ,而我们的alpha值为
0.2f ,则着色器
将不会渲染此像素。 可以在
Unity文档中找到
AlphaClipThreshold函数:
PBR主节点和
未点亮的主节点 。
这是我们完成的着色器:
我们对
溶解纹理进行采样并获得
红色的
值 ,然后将其添加到
Amount值(这是我添加以指示整个执行过程的属性,该值1表示完全溶解)并将其连接到
AlphaClipThreshold 。
做完了!如果要将其应用于任何现有的着色器,则只需
将 节点连接 复制到
AlphaClipThreshold (不要错过必要的属性!)。 您也可以使其
双边,并获得更漂亮的效果!
轮廓溶解着色器
如果您尝试为其添加
轮廓 ? 开始吧!
我们无法处理已经溶解的像素,因为放下像素后
它们会永远消失 。 相反,我们可以使用“几乎已分解”的值!
在
HLSL中,这非常简单,只需在计算
剪辑后添加几行代码即可:
void surf (Input IN, inout SurfaceOutputStandard o) { //[...] //After our clip calculations if (dissolve_value - _Amount < .05f) //outline width = .05f o.Emission = fixed3(1, 1, 1); //emits white color //Your shader body, you can set the Albedo etc. //[...] }
做完了!使用
Shader Graph时,逻辑略有不同。 这是完成的着色器:
我们可以使用简单的
溶解着色器创建非常
酷的效果 ; 您可以尝试
不同的纹理和
值 ,以及其他一些东西!
第2部分:世界探索着色器
世界探索着色器(或“
世界溶出度着色器 ,或
全局溶出度 ”)使我们能够根据对象到位置的距离相等地隐藏场景中的所有对象;现在,我们将在
Unity Shader Graph中创建这样的着色
器并将其写入
HLSL 。
这是我们将创建的示例:
距离作为参数
假设如果场景中
的物体 离播放器太远,我们需要将其
溶解 。 我们已经宣布了
_Amount参数,该参数控制对象的消失/消失,因此我们需要用对象与播放器之间的距离替换它。
为此,我们需要选择
Player和
Object的位置。
球员位置
Unity Shader Graph和
HLSL 的过程都相似:我们需要在代码中转移玩家的位置。
private void Update() {
着色器图
物体的位置和距离
使用明暗器图,我们可以使用位置和距离节点。
PS若要使该系统与Sprite Renderers一起使用,您需要添加_MainTex属性,对其进行采样并将其连接到反照率。 您可以阅读我以前的
Sprites漫反射着色器教程(使用着色器图)。
HLSL(表面)
对象位置
在HLSL中,我们可以将
worldPos变量添加到
Input结构中以获取对象顶点的位置。
struct Input { float2 uv_MainTex; float3 worldPos;
在
Unity文档页面上,您可以找到允许添加到Input结构中的其他内置参数。
应用距离
我们需要使用物体与玩家之间的距离作为溶解量。 为此,您可以使用内置的
距离功能(
Microsoft文档 )。
void surf (Input IN, inout SurfaceOutputStandard o) { half dissolve_value = tex2D(_DissolveTexture, IN.uv_MainTex).x; float dist = distance(_PlayerPos, IN.worldPos); clip(dissolve_value - dist/ 6f);
结果(3D)
结果(2D)
如您所见,对象“局部溶解”,我们没有得到均质效果,因为我们从使用每个对象的UV采样的纹理中获得“溶解值”。 (在2D中,这不太明显)。
HLSL上的3D LocalUV Dissolve Shader
Shader "Custom/GlobalDissolveSurface" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Glossiness("Smoothness", Range(0,1)) = 0.5 _Metallic("Metallic", Range(0,1)) = 0.0 _DissolveTexture("Dissolve texture", 2D) = "white" {} _Radius("Distance", Float) = 1 //distance where we start to reveal the objects } SubShader{ Tags { "RenderType" = "Opaque" } LOD 200 Cull off //material is two sided CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0 sampler2D _MainTex; sampler2D _DissolveTexture; //texture where we get the dissolve value struct Input { float2 uv_MainTex; float3 worldPos; //Built-in world position }; half _Glossiness; half _Metallic; fixed4 _Color; float3 _PlayerPos; //"Global Shader Variable", contains the Player Position float _Radius; void surf (Input IN, inout SurfaceOutputStandard o) { half dissolve_value = tex2D(_DissolveTexture, IN.uv_MainTex).x; float dist = distance(_PlayerPos, IN.worldPos); clip(dissolve_value - dist/ _Radius); fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = ca; } ENDCG } FallBack "Diffuse" }
Sprites漫反射-HLSL上的LocalUV Dissolve Shader
Shader "Custom/GlobalDissolveSprites" { Properties { [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {} _Color("Tint", Color) = (1,1,1,1) [MaterialToggle] PixelSnap("Pixel snap", Float) = 0 [HideInInspector] _RendererColor("RendererColor", Color) = (1,1,1,1) [HideInInspector] _Flip("Flip", Vector) = (1,1,1,1) [PerRendererData] _AlphaTex("External Alpha", 2D) = "white" {} [PerRendererData] _EnableExternalAlpha("Enable External Alpha", Float) = 0 _DissolveTexture("Dissolve texture", 2D) = "white" {} _Radius("Distance", Float) = 1 //distance where we start to reveal the objects } SubShader { Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "PreviewType" = "Plane" "CanUseSpriteAtlas" = "True" } Cull Off Lighting Off ZWrite Off Blend One OneMinusSrcAlpha CGPROGRAM #pragma surface surf Lambert vertex:vert nofog nolightmap nodynlightmap keepalpha noinstancing #pragma multi_compile _ PIXELSNAP_ON #pragma multi_compile _ ETC1_EXTERNAL_ALPHA #include "UnitySprites.cginc" struct Input { float2 uv_MainTex; fixed4 color; float3 worldPos; //Built-in world position }; sampler2D _DissolveTexture; //texture where we get the dissolve value float3 _PlayerPos; //"Global Shader Variable", contains the Player Position float _Radius; void vert(inout appdata_full v, out Input o) { v.vertex = UnityFlipSprite(v.vertex, _Flip); #if defined(PIXELSNAP_ON) v.vertex = UnityPixelSnap(v.vertex); #endif UNITY_INITIALIZE_OUTPUT(Input, o); o.color = v.color * _Color * _RendererColor; } void surf(Input IN, inout SurfaceOutput o) { half dissolve_value = tex2D(_DissolveTexture, IN.uv_MainTex).x; float dist = distance(_PlayerPos, IN.worldPos); clip(dissolve_value - dist / _Radius); fixed4 c = SampleSpriteTexture(IN.uv_MainTex) * IN.color; o.Albedo = c.rgb * ca; o.Alpha = ca; } ENDCG } Fallback "Transparent/VertexLit" }
PS为了创建最后一个着色器,我复制了标准的Unity Sprites-Diffuse着色器,并添加了本文此部分前面所述的“溶解”部分。 所有标准着色器都可以在
这里找到。
使效果均匀
为了使效果均匀,我们可以使用全局坐标(在世界上的位置)作为溶解纹理的UV坐标。 在溶出度纹理参数中设置
Wrap = Repeat也是很重要的,这样我们就可以在不注意到纹理的情况下重复纹理(确保纹理是无缝的并且可以重复!
HLSL(表面)
half dissolve_value = tex2D(_DissolveTexture, IN.worldPos / 4).x;
着色器图
结果(2D)
结果就是:我们可以注意到,整个世界的溶解质地现在是一致的。
该着色器已经
非常适合2D游戏 ,但对于
3D对象则需要
改进 。
3D对象的问题
如您所见,着色器不适用于“非垂直”面,并极大地扭曲了纹理。 这就是为什么 UV坐标需要值float2,如果我们通过worldPos,则它仅接收X和Y。
如果通过应用计算以在所有面上显示纹理来消除此问题,则会遇到一个新问题:变暗时,对象将彼此相交,并且将不会保持均匀。
对于初学者而言,解决方案将很难理解:必须消除纹理,在世界上产生三维噪声,并从中获得“溶出值”。 在这篇文章中,我不会解释3D噪声的产生,但是您可以找到一堆可以使用的功能!
这是一个噪声着色器示例:
https :
//github.com/keijiro/NoiseShader 。 您还可以在以下位置了解如何产生噪音:
https :
//thebookofshaders.com/11/和此处:
https :
//catlikecoding.com/unity/tutorials/noise/我将以这种方式设置表面功能(假设您已经编写了噪波部分):
void surf (Input IN, inout SurfaceOutputStandard o) { float dist = distance(_PlayerPos, IN.worldPos); //"abs" because you have to make sure that the noise is between the range [0,1] //you can remove "abs" if your noise function returns a value between [0,1] //also, replace "NOISE_FUNCTION_HERE" with your 3D noise function. half dissolve_value = abs(NOISE_FUNCTION_HERE(IN.worldPos)); if (dist > _Radius) { float clip_value = dissolve_value - ((dist - _Radius) / _Radius); clip(clip_value); if (clip_value < 0.05f) o.Emission = float3(1, 1, 1); } fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = ca; }
HLSL的简要提醒:在使用/调用函数之前,必须先编写/声明函数。
PS如果要使用Unity Shader Graph创建着色器,则需要使用“自定义节点”(并通过在其中写入HLSL代码来产生噪声)。 我将在以后的教程中讨论自定义节点。
结果(3D)
添加轮廓
要添加轮廓,您需要从教程的上一部分开始重复该过程。
反作用
如果我们想扭转这种影响? (如果玩家在附近,则物体应消失)
对于我们来说,更改一行就足够了:
float dist = _Radius - distance(_PlayerPos, IN.worldPos);
(相同的过程适用于Shader Graph)。