الجزء 1: حل شادر
إن تظليل الانحلال يعيد تأثيرًا جميلًا ، علاوة على ذلك ، من السهل إنشاء وفهم. اليوم
سنجعلها في
Unity Shader Graph ،
ونكتب أيضًا على
HLSL .
فيما يلي مثال على ما سنقوم بإنشائه:
كيف يعمل
لإنشاء أداة تظليل
مذابة ، سيتعين علينا
العمل مع قيمة
AlphaClipThreshold في أداة تظليل "Shader Graph" أو استخدام وظيفة HLSL تسمى
مشبك .
بشكل أساسي ، نقول للتظليل
عدم تقديم البكسل بناءً على
الملمس والقيمة التي تم تمريرها. نحتاج إلى معرفة ما يلي:
الأجزاء البيضاء تذوب بشكل أسرع .
سنستخدم الملمس التالي:
يمكنك إنشاء خطوطك الخاصة - مثل المثلثات المستقيمة ، ولكن أي شيء! فقط تذكر أن
الأجزاء البيضاء تذوب بشكل أسرع .
لقد أنشأت هذا النسيج في Photoshop باستخدام مرشح Clouds.
حتى إذا كنت مهتمًا فقط بـ Shader Graph ولا تعرف أي شيء عن HLSL ، ما زلت أوصي بقراءة هذا الجزء ، لأنه من المفيد فهم كيفية عمل Unity Shader Graph في الداخل.
Hlsl
في HLSL ، نستخدم وظيفة
المقطع (x) . تتجاهل وظيفة
المقطع (x) جميع وحدات البكسل بقيمة أقل من
الصفر . لذلك ، إذا اتصلنا
بالمقطع (-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;
أيضا ، لا تنسى. أن أسمائهم يجب أن تتطابق مع الأسماء في قسم الخصائص.
الوظيفة
نبدأ وظيفة
Surface أو
Fragment عن طريق أخذ عينات من
نسيج الانحلال والحصول على
قيمة اللون الأحمر . ملاحظة: يتم تخزين
قوامنا بتدرج الرمادي ، أي أن قيمه
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;
هذا كل ما في الأمر! يمكننا تطبيق هذه العملية على أي تظليل موجود وتحويله إلى
تظليل الانحلال !
في ما يلي جهاز تظليل السطح القياسي لمحرك الوحدة ، والذي تم تحويله إلى جهاز
تظليل ذو اتجاهين : 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 (التي تعمل بشكل مختلف عن
المقطع (x) من HLSL). في هذا المثال ، قمت بإنشاء تظليل PBR.
تُرشد الدالة
AlphaClipThreshold جهاز التظليل
بتجاهل جميع وحدات البكسل التي تقل قيمتها عن قيمة
Alpha . على سبيل المثال ، إذا كان
0.3f ، وكانت قيمة ألفا
0.2f ، فلن يقوم جهاز التظليل
بعرض هذه البكسل. يمكن العثور على وظيفة
AlphaClipThreshold في
وثائق الوحدة :
PBR Master Node و
Unlit Master Node .
هنا لدينا تظليل الانتهاء:
نحن نأخذ عينات
نسيج الانحلال ونحصل
على القيمة الحمراء ، ثم نضيفها إلى قيمة
المبلغ (وهي خاصية أضفتها للإشارة إلى عملية التنفيذ الإجمالية ، والقيمة 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 ، يختلف المنطق قليلاً. هنا هو التظليل النهائي:
يمكننا إنشاء
تأثيرات رائعة جدًا باستخدام
تظليل حل بسيط ؛ يمكنك تجربة مواد
وقيم مختلفة ، بالإضافة إلى التوصل إلى شيء آخر!
الجزء الثاني: تظليل الاستكشاف العالمي
يسمح لنا تظليل
الاستكشاف العالمي (أو "
تظليل الانحلال العالمي ، أو
الانحلال العالمي ") بإخفاء جميع الأشياء في المشهد على قدم المساواة بناءً على بعدها عن الموقع ؛ والآن سنقوم بإنشاء مثل هذا التظليل في
Unity Shader Graph ونكتبه في
HLSL .
فيما يلي مثال على ما سنقوم بإنشائه:
المسافة كمعلمة
افترض أننا بحاجة إلى
حل كائن في مشهد إذا كان
بعيدًا جدًا عن اللاعب . لقد أعلنا بالفعل عن معلمة
_Amount ، التي تتحكم في اختفاء / انحلال الكائن ، لذلك نحتاج إلى استبداله بالمسافة بين الكائن واللاعب.
للقيام بذلك ، نحن بحاجة إلى اتخاذ مواقف
لاعب وشيء .
موقف اللاعب
ستكون
العملية مماثلة لكل من
Unity Shader Graph و
HLSL : نحتاج إلى نقل مركز اللاعب في الشفرة.
private void Update() {
شادر الرسم البياني
موضع الكائن والمسافة إليه
باستخدام Shader Graph ، يمكننا استخدام عقد الموقع والمسافة.
ملاحظة: لكي يعمل هذا النظام مع Sprite Renderers ، تحتاج إلى إضافة خاصية _MainTex ، وتجربتها وتوصيلها بالبيدو. يمكنك قراءة برنامج
Sprites التعليمي
المنتشر التظليل السابق (الذي يستخدم الرسم البياني للتظليل).
HLSL (سطح)
موضع الكائن
في HLSL ، يمكننا إضافة متغير
worldPos إلى هيكل
الإدخال الخاص بنا للحصول على مواقع رؤوس الأشياء.
struct Input { float2 uv_MainTex; float3 worldPos;
في
صفحة وثائق الوحدة ، يمكنك معرفة المعلمات الأخرى المضمنة التي يمكنك إضافتها إلى بنية الإدخال.
تطبيق المسافة
نحن بحاجة إلى استخدام المسافة بين الأشياء واللاعب كمبلغ الذوبان. للقيام بذلك ، يمكنك استخدام وظيفة
المسافة المضمنة (
وثائق 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 ، هذا أقل ملحوظة).
3D LocalUV Dissolve Shader على HLSL
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 Diffuse - LocalUV Dissolve Shader على HLSL
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" }
سكرتير خاص لإنشاء تظليل آخر ، قمت بنسخ تظليل Unity Sprites-Diffuse القياسي وأضفت الجزء "حل" الموصوف سابقًا في هذا الجزء من المقالة. يمكن العثور على جميع تظليل القياسية
هنا .
جعل التأثير متجانسًا
لجعل التأثير متجانسًا ، يمكننا استخدام الإحداثيات العالمية (الموقع في العالم) كإحداثيات الأشعة فوق البنفسجية لنسيج الانحلال. من المهم أيضًا ضبط
Wrap = Repeat في معلمات نسيج الذوبان حتى نتمكن من تكرار النسيج دون ملاحظته (تأكد من أن النسيج سلس ويتكرر جيدًا!)
HLSL (سطح)
half dissolve_value = tex2D(_DissolveTexture, IN.worldPos / 4).x;
شادر الرسم البياني
النتيجة (ثنائي الأبعاد)
هذه هي النتيجة: يمكننا أن نلاحظ أن نسيج الذوبان أصبح موحدًا للعالم كله.
يعد هذا التظليل
مثاليًا للألعاب ثنائية الأبعاد ، ولكن بالنسبة إلى
الكائنات ثلاثية الأبعاد ، يجب
تحسينه .
المشكلة مع الكائنات ثلاثية الأبعاد
كما ترى ، لا يعمل التظليل للوجوه "غير العمودية" ، ويشوه إلى حد كبير النسيج. لهذا اتضح أن إحداثيات الأشعة فوق البنفسجية تحتاج إلى القيمة float2 ، وإذا تجاوزنا worldPos ، فستتلقى X و Y فقط.
إذا قضينا على هذه المشكلة من خلال تطبيق الحسابات لعرض النسيج على جميع الوجوه ، فسوف نصل إلى مشكلة جديدة: عند التعتيم ، ستتقاطع الكائنات مع بعضها البعض ، ولن تظل متجانسة.
سيكون من الصعب على المبتدئين فهم الحل: من الضروري التخلص من الملمس ، وتوليد ضوضاء ثلاثية الأبعاد في العالم والحصول على "قيمة الذوبان" منه. في هذا المنشور ، لن أشرح توليد الضجيج ثلاثي الأبعاد ، ولكن يمكنك العثور على مجموعة من الوظائف الجاهزة للاستخدام!
فيما يلي مثال لتظليل الضوضاء:
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 ، تحتاج إلى استخدام العقد المخصصة (وتوليد ضوضاء من خلال كتابة كود HLSL فيها). سأتحدث عن العقد المخصصة في البرنامج التعليمي في المستقبل.
النتيجة (3D)
إضافة الخطوط
لإضافة الخطوط ، تحتاج إلى تكرار العملية من الجزء السابق من البرنامج التعليمي.
تأثير مقلوب
وإذا أردنا عكس هذا التأثير؟ (يجب أن تختفي الأشياء إذا كان اللاعب قريبًا)
يكفي أن نغير سطر واحد:
float dist = _Radius - distance(_PlayerPos, IN.worldPos);
(نفس العملية تنطبق على Shader Graph).