مرحبا بالجميع! اسمي Grigory Dyadichenko ، وأنا مؤسس شركة CTO لشركة Foxsys Studios. اليوم سنتحدث عن تظليل قمة الرأس. سوف تدرس المقالة الممارسة من وجهة نظر الوحدة ، أمثلة بسيطة للغاية ، بالإضافة إلى العديد من الروابط لدراسة المعلومات حول التظليل في الوحدة. إذا كنت جيدًا في كتابة الظلال ، فلن تجد شيئًا جديدًا لنفسك. أي شخص يريد أن يبدأ كتابة تظليل في الوحدة ، مرحبا بكم في القط.

قليلا من الناحية النظرية
لفهم أفضل لعملية التظليل ، دعونا نلقي نظرة على نظرية صغيرة. تظليل قمة الرأس أو تظليل قمة الرأس هو مرحلة تظليل قابلة للبرمجة تعمل مع رؤوس فردية. تقوم Vertexes بدورها بتخزين العديد من السمات التي تتم معالجتها بواسطة هذا الجزء من التظليل للحصول على سمات محولة في الإخراج.
أمثلة حيث يتم استخدام تظليل قمة الرأس
تشوه الأجسام - الأمواج الواقعية ، وتأثير التموجات من المطر ، والتشوه عند اصطدام رصاصة ، كل هذا يمكن القيام به باستخدام تظليل الرأس ، وستبدو أكثر واقعية من نفس الشيء الذي يتم من خلال Bump Mapping في الجزء المبتسم من التظليل. لأن هذا هو التغيير في الهندسة. تظليل المستوى 3.0 حول هذا الموضوع له تقنية تسمى "خريطة التعيين" ، حيث أصبح بإمكانهم الآن الوصول إلى مواد في الجزء العلوي من التظليل.
الرسوم المتحركة من الكائنات. تبدو الألعاب أكثر حيوية وإثارة للاهتمام عندما تتفاعل النباتات مع شخصية أو أشجار تتأرجح في مهب الريح. لهذا ، يتم استخدام تظليل قمة الرأس أيضًا.
إضاءة الكرتون أو منمق. في العديد من الألعاب ، من وجهة نظر الأناقة ، تبدو الإضاءة pbr بدلاً من التصميم أكثر إثارة للاهتمام. في الوقت نفسه ، لا معنى لحساب أي شيء في الجزء المجزأة.
السلخ. في اللحظة المعينة في محركات اللعبة ، يتم حل هذه المشكلة ، ولكن من المفيد فهم تظليل قمة الرأس من أجل فهم كيفية عملها.
أمثلة بسيطة للعمل مع القمم

لا أريد أن يحدث ذلك ، كما في الدروس القديمة حول كيفية رسم البومة ، لذلك دعونا نذهب على مراحل. إنشاء تظليل السطح القياسية. يمكن القيام بذلك باستخدام زر الماوس الأيمن في Project View أو في اللوحة العلوية في علامة التبويب Assets. إنشاء-> شادر-> شادر سطح قياسي.
ونحن نحصل على مثل هذا المعيار فارغ.
تظليل السطحShader "Custom/SimpleVertexExtrusionShader"
{
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
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = ca;
}
ENDCG
}
FallBack "Diffuse"
}
كيف يعمل بشكل عام وسنقوم بتحليله بالتفصيل في المقالة بعد الممارسة الأساسية ، بالإضافة إلى أننا سوف نفهم جزئياً أثناء تنفيذ التظليل. في الوقت الحالي ، دع بعض الأشياء تبقى كما هي. باختصار ، لا يوجد سحر (من حيث كيفية اختيار المعلمات وما إلى ذلك) فقط لبعض الكلمات الرئيسية ، تقوم الوحدة بإنشاء رمز لك حتى لا تكتبه من نقطة الصفر. لذلك ، هذه العملية ليست واضحة بما فيه الكفاية. يمكنك قراءة المزيد حول تظليل السطح وخصائصه في الوحدة هنا.
docs.unity3d.com/Manual/SL-SurfaceShaders.htmlسنقوم بإزالة كل شيء لا لزوم له منه بحيث لا يصرف انتباهك ، لأنه في الوقت المحدد لا حاجة إليه. والحصول على مثل هذا تظليل قصير.
تظليل مبسطShader "Custom/SimpleVertexExtrusionShader"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
#pragma target 3.0
struct Input
{
float4 color : COLOR;
};
fixed4 _Color;
void surf (Input IN, inout SurfaceOutputStandard o)
{
fixed4 c = _Color;
o.Albedo = c.rgb;
}
ENDCG
}
FallBack "Diffuse"
}

فقط اللون على النموذج مع الإضاءة. في هذه الحالة ، تعد الوحدة مسؤولة عن حساب الإضاءة.
أولاً ، أضف التأثير الأبسط من أمثلة الوحدة. البثق أمر طبيعي ، وسنقوم على مثاله بتحليل كيفية عمله.
للقيام بذلك ، قم بإضافة
vertex: معدل
vert إلى
# pragma surface surf line
fullforwardshadows line. إذا قمنا بتمرير
inout appdata_full v كمعلمة إلى دالة ، فعندئذ تكون هذه الوظيفة في جوهرها معدّل قمة الرأس. في جوهره ، هو جزء من تظليل قمة الرأس ، الذي تم إنشاؤه بواسطة وحدة توليد الشفرة ، التي تقوم بمعالجة أولية للرؤوس. أيضًا في كتلة
الخصائص ، أضف حقل قبول
_Amount من 0 إلى 1. لاستخدام حقل
_Amount في التظليل ، نحتاج أيضًا إلى تحديده هناك. في هذه الوظيفة ،
سننتقل ببساطة إلى الوضع الطبيعي اعتمادًا على
_Amount ، حيث 0 هي موضع قمة الرأس القياسي (صفر تحول) ، و 1 هو التحول إلى الوضع الطبيعي تمامًا.
SimpleVertexExtrusionShaderShader "Custom/SimpleVertexExtrusionShader"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_Amount ("Extrusion Amount", Range(0,1)) = 0.5
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows vertex:vert
#pragma target 3.0
struct Input
{
float4 color : COLOR;
};
fixed4 _Color;
float _Amount;
void vert (inout appdata_full v)
{
v.vertex.xyz += v.normal * _Amount;
}
void surf (Input IN, inout SurfaceOutputStandard o)
{
fixed4 c = _Color;
o.Albedo = c.rgb;
}
ENDCG
}
FallBack "Diffuse"
}
يمكنك ملاحظة سمة مهمة من تظليل. على الرغم من تنفيذ التظليل في كل إطار ، فإن النتيجة التي يتم الحصول عليها أثناء تشغيل التظليل لا يتم تخزينها في الشبكة ، ولكن يتم استخدامها فقط للتجسيد. لذلك ، من المستحيل ربط وظائف التظليل ، وكذلك
التحديث في البرامج النصية. يتم تطبيقها كل إطار دون تغيير بيانات الشبكة ، ولكن ببساطة تعديل الشبكة لمزيد من التقديم.
على سبيل المثال ، تتمثل إحدى أسهل الطرق لإنشاء رسوم متحركة في استخدام الوقت لتغيير السعة. تحتوي الوحدة على متغيرات
مضمّنة ، يمكن العثور على قائمة كاملة بها هنا
docs.unity3d.com/Manual/SL-UnityShaderVariables.html في هذه الحالة ،
سنكتب تظليلًا جديدًا استنادًا إلى
تظليلنا السابق. بدلاً من
_Amount ، دعونا نجعل قيمة التعويم
_Amplitude واستخدام المتغير Unity
المدمج _SinTime .
_SinTime هو جيب الوقت ، وبالتالي فإنه يأخذ قيمًا من -1 إلى 1. ومع ذلك ، لا تنس أن جميع متغيرات الوقت المضمنة في تظليل الوحدة عبارة عن متجهات
تعويم . على سبيل المثال ،
يتم تعريف
_SinTime على أنه
(sin (t / 8) ، sin (t / 4) ، sin (t / 2) ، sin (t)) ، حيث t هو الوقت. لذلك ، نأخذ المكون z بحيث تكون الرسوم المتحركة أسرع. ونحن نحصل على:
SimpleVertexExtrusionWithTimeShader "Custom/SimpleVertexExtrusionWithTime"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_Amplitude ("Extrusion Amplitude", float) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows vertex:vert
#pragma target 3.0
struct Input
{
float4 color : COLOR;
};
fixed4 _Color;
float _Amplitude;
void vert (inout appdata_full v)
{
v.vertex.xyz += v.normal * _Amplitude * (1 - _SinTime.z);
}
void surf (Input IN, inout SurfaceOutputStandard o)
{
fixed4 c = _Color;
o.Albedo = c.rgb;
}
ENDCG
}
FallBack "Diffuse"
}
لذلك كانت هذه أمثلة بسيطة. حان الوقت لرسم البومة!
تشوه الأشياء
لقد كتبت بالفعل مقالة كاملة حول موضوع تأثير تشوه واحد مع تحليل مفصل لرياضيات العملية ومنطق التفكير عند تطوير مثل هذا التأثير
habr.com/en/post/435828 سيكون هذا بومة لدينا.
تتم كتابة جميع تظليل في المقالة في hlsl. تحتوي هذه اللغة بالفعل على وثائق ضخمة خاصة بها ، والتي ينسى الكثيرون ويتساءلون من أين تأتي نصف الوظائف السلكية ، على الرغم من أنها محددة في
docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl- وظائف الجوهريةولكن في الواقع ، فإن التظليل السطحي في الوحدة هو موضوع كبير ومضخم في حد ذاته. بالإضافة إلى ذلك ، لا تريد دائمًا العبث بإضاءة الوحدة. في بعض الأحيان تحتاج إلى الغش وكتابة أسرع تظليل يحتوي فقط على المجموعة الصحيحة من التأثيرات المحددة مسبقًا. في وحدة ، يمكنك كتابة تظليل المستوى الأدنى.
تظليل مستوى منخفض

وفقًا للتقاليد القديمة الجيدة للعمل مع تظليل ، سنقوم فيما بعد بمعذبة أرنب ستانفورد.
بشكل عام ، ما يسمى Unity ShaderLab هو في الأساس تصور لمفتش له حقول في المواد وبعض التبسيط لتظليل الكتابة.
خذ الهيكل العام لتظليل شادرلاب:
هيكل تظليل عامShader "MyShaderName"
{
Properties
{
//
}
SubShader // ( )
{
Pass
{
//
}
//
}
//
FallBack "VertexLit" // ,
}
تجميع التوجيهات مثل
#pragma vert vert# براغ جزء شظيةتحديد وظائف التظليل التي سيتم تجميعها كتظليل رأس وتظليل ، على التوالي.
لنفترض أننا نأخذ أحد الأمثلة الأكثر شيوعًا - تظليل لعرض لون الحالة الطبيعية:
SimpleNormalVisualizationShader "Custom/SimpleNormalVisualization"
{
Properties
{
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
fixed3 color : COLOR0;
};
v2f vert (appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.color = v.normal * 0.5 + 0.5;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return fixed4 (i.color, 1);
}
ENDCG
}
}
FallBack "VertexLit"
}

في هذه الحالة ، في الجزء العلوي ، نكتب القيمة المعتادة المحولة إلى لون الرأس ، وفي الجزء بكسل ، نستخدم هذا اللون لونًا للنموذج.
وظيفة
UnityObjectToClipPos هي وظيفة مساعدة Unity (من ملف
UnityCG.cginc ) تقوم بترجمة رؤوس الكائن إلى الموضع المقترن بالكاميرا. بدونها ، سيتم رسم كائن ، عندما يدخل رؤية الكاميرا (إحباط) ، في إحداثيات الشاشة ، بغض النظر عن موضع التحويل. منذ البداية يتم عرض مواضع القمم في إحداثيات الكائن. مجرد قيم نسبة إلى محوره.
هذه الكتلة.
struct v2f {
float4 pos : SV_POSITION;
fixed3 color : COLOR0;
};
هذا هو تعريف الهيكل الذي سيتم معالجته في الجزء العلوي ويتم نقله إلى الجزء. في هذه الحالة ، يتم تحديد أن معلمتين مأخوذة من الشبكة - موضع الرأس ولون الرأس. يمكنك قراءة المزيد حول البيانات التي يمكن طرحها في وحدة على هذا الرابط
docs.unity3d.com/Manual/SL-VertexProgramInputs.htmlتوضيح مهم. أسماء سمات شبكة لا يهم. وهذا هو ، دعنا نقول في سمة اللون أنه يمكنك كتابة متجه الانحراف عن الموضع الأصلي (وبهذه الطريقة يكون لها تأثير في بعض الأحيان عندما تتحول الشخصية بحيث "تتصدى" العشب منها). كيف ستتم معالجة هذه السمة تعتمد كليا على تظليلك.
استنتاج
شكرا لاهتمامكم! من الصعب كتابة بعض الآثار المعقدة بدون جزء مجزأ ، ولهذا السبب سنناقش نفس الشيء في مقالات منفصلة. آمل خلال هذه المقالة أن يصبح الأمر أكثر وضوحًا قليلاً في كيفية كتابة رمز تظليل قمة الرأس بشكل عام ، وحيث يمكنك العثور على معلومات للدراسة ، نظرًا لأن التظليل موضوع عميق للغاية.
في المقالات المستقبلية ، سنقوم بتحليل الأنواع الأخرى من التظليل ، والمؤثرات الفردية ، وسأحاول وصف منطق تفكيري عند إنشاء تأثيرات جديدة أو معقدة.
تم إنشاء مستودع أيضًا حيث ستتم إضافة جميع نتائج هذه السلسلة من المقالات
github.com/Nox7atra/ShaderExamples ، وآمل أن تكون هذه المعلومات مفيدة للمبتدئين الذين بدأوا للتو رحلتهم في دراسة هذا الموضوع.
بعض الروابط المفيدة (بما في ذلك المصادر):
www.khronos.org/opengl/wiki/Vertex_Shaderdocs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-referencedocs.unity3d.com/ru/current/Manual/SL-Reference.htmldocs.unity3d.com/Manual/GraphicsTutorials.htmlwww.malbred.com/3d-grafika-3d-redaktory/sovremennaya-terminologiya-3d-grafiki/vertex-shader-vershinnyy-sheyder.html3dpapa.ru/accurate-displacement-workflow