溶解シェーダーと世界探検

パート1:溶解シェーダー



溶解シェーダーは美しい効果を返します。さらに、作成と理解が簡単です。 今日はUnityシェーダーグラフで作成し、 HLSLで作成します。



作成するものの例を次に示します。











仕組み



ディゾルブシェーダーを作成するには、「Shader Graph」シェーダーのAlphaClipThreshold値を操作するか、 clipというHLSL関数を使用する必要があります



基本的に、渡されたテクスチャ値に基づいてピクセルをレンダリングしないようにシェーダーに指示します 。 次のことを知る必要があります白い部分はより速く溶解します



次のテクスチャを使用します。









あなたはあなた自身を作成することができます-直線、三角形、しかし何でも! 白い部分がより速く溶解することを覚えておいてください。



「クラウド」フィルターを使用して、Photoshopでこのテクスチャを作成しました。



シェーダーグラフにのみ興味があり、HLSLについて何も知らない場合でも、Unityシェーダーグラフが内部でどのように機能するかを理解するのに役立つため、この部分を読むことをお勧めします。






Hlsl



HLSLでは、 clip(x)関数を使用しますclip(x)関数は、 ゼロより小さい値を持つすべてのピクセルを破棄します 。 したがって、 clip(-1)を呼び出すと、シェーダーがこのピクセルをレンダリングしないことが確実になります。 クリップの詳細については、 Microsoft Docsをご覧ください。



プロパティ



シェーダーには、2つのプロパティ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;
      
      





また、忘れないでください。 それらの名前は[プロパティ]セクションの名前と一致する必要があります。



機能



溶解テクスチャをサンプリングし、赤の値を取得することにより、 SurfaceまたはFragment関数を開始します 。 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のクリップ(x)とは異なる動作)を使用する必要があります。 この例では、PBRシェーダーを作成しました。



AlphaClipThreshold関数は、シェーダーに、 Alpha値より小さい値を持つすべてのピクセルを破棄するよう指示します。 たとえば、 0.3fでアルファ値が0.2fの場合、シェーダーこのピクセルをレンダリングしませんAlphaClipThreshold関数は、 Unityのドキュメント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このシステムをスプライトレンダラーと連携させるには、_MainTexプロパティを追加し、サンプリングして、アルベドに接続する必要があります。 以前のSprites拡散シェーダーチュートリアル(シェーダーグラフを使用)を読むことができます。






HLSL(表面)



オブジェクトの位置



HLSLでは、 入力構造にworldPos変数を追加して、オブジェクトの頂点の位置を取得できます。



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





Unityのドキュメントページで 、入力構造に追加できるその他の組み込みパラメーターを確認できます。



距離を適用



分解の量として、オブジェクトとプレイヤーの間の距離を使用する必要があります。 これを行うには、組み込みの距離関数を使用できます( 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" }
      
      





スプライト拡散-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のみを受け取ります。



計算を適用してすべての面にテクスチャを表示することでこの問題を修正すると、新しい問題が発生します。暗くなると、オブジェクトは互いに交差し、均質に保たれなくなります。



初心者が解決策を理解することは困難です。テクスチャを取り除き、世界で3次元ノイズを生成し、そこから「溶解の価値」を取得する必要があります。 この投稿では、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ユニティシェーダーグラフを使用してシェーダーを作成する場合は、カスタムノードを使用する必要があります(また、HLSLコードを記述してノイズを生成します)。 カスタムノードについては、今後のチュートリアルで説明します。



結果(3D)












輪郭の追加



輪郭を追加するには、チュートリアルの前の部分のプロセスを繰り返す必要があります。












反転効果



そして、この効果を逆にしたい場合は? (プレイヤーが近くにいる場合、オブジェクトは消えなければなりません)



1行変更するだけで十分です。



 float dist = _Radius - distance(_PlayerPos, IN.worldPos);
      
      





(同じプロセスがシェーダーグラフに適用されます)。










All Articles