溶解着色器和世界探索

第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 TextureAmount (将指示整个执行过程)。 与其他属性和变量一样,您可以随意调用它们。

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我们的纹理以灰度存储,即RGB的值相等,您可以选择任意一个 。 例如, 白色(1,1,1)黑色(0,0,0)

在我的示例中,我使用了表面着色器:

 void surf (Input IN, inout SurfaceOutputStandard o) { half dissolve_value = tex2D(_DissolveTexture, IN.uv_MainTex).r; //Get how much we have to dissolve based on our dissolve texture clip(dissolve_value - _Amount); //Dissolve! //Your shader body, you can set the Albedo etc. //[...] } 

就是这样! 我们可以将此过程应用于任何现有的着色器,并将其转变为溶解着色器

这是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参数,该参数控制对象的消失/消失,因此我们需要用对象与播放器之间的距离替换它。

为此,我们需要选择PlayerObject的位置。

球员位置


Unity Shader GraphHLSL 的过程都相似:我们需要在代码中转移玩家的位置。

 private void Update() { //Updates the _PlayerPos variable in all the shaders //Be aware that the parameter name has to match the one in your shaders or it wont' work Shader.SetGlobalVector("_PlayerPos", transform.position); //"transform" is the transform of the Player } 



着色器图


物体的位置和距离


使用明暗器图,我们可以使用位置和距离节点。



PS若要使该系统与Sprite Renderers一起使用,您需要添加_MainTex属性,对其进行采样并将其连接到反照率。 您可以阅读我以前的Sprites漫反射着色器教程(使用着色器图)。



HLSL(表面)


对象位置


在HLSL中,我们可以将worldPos变量添加到Input结构中以获取对象顶点的位置。

 struct Input { float2 uv_MainTex; float3 worldPos; //add this and Unity will set it automatically }; 

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); //"6" is the maximum distance where your object will start showing //Set albedo, alpha, smoothness etc[...] } 

结果(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; //I modified the worldPos to reduce the texture size 

着色器图



结果(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)。

Source: https://habr.com/ru/post/zh-CN427401/


All Articles