На самом деле это довольно сложный процесс, хотя я рекомендую начать с загрузки исходного кода Unity Shader с их сайта. Большая часть фактического затенения выполняется в UnityStandardBRDF.cginc, UnityPBSLighting.cginc и UnityStandardCore.cginc. Вы также можете найти эти файлы на GitHub, если вы их Google.
По сути, базовая структура шейдера с добавлением вперед выглядит следующим образом:
Shader "MyForwardAddShader"
{
Properties
{
_MainTex("Some texture", 2D) = "white"{}
}
SubShader
{
Tags {"RenderType"="Geometry" "Queue" = "Geometry"}
Pass
{
// Forward Base pass - this applies main directional light and ambient
Name "FORWARD"
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma target 3.5
#pragma multi_compile_fwdbase
#pragma multi_compile_fog
#pragma multi_compile_instancing
#pragma vertex vert
#pragma fragment frag
// I Like to keep these in separate include files
#include "MyForwardBase.cginc"
ENDCG
}
Pass
{
// Forward Add pass - this is added once per extra light source
Name "FORWARD_DELTA"
Tags { "LightMode" = "ForwardAdd" }
Blend SrcAlpha One
Fog { Color (0,0,0,0) } // in additive pass fog should be black
ZWrite Off
ZTest LEqual
CGPROGRAM
#pragma target 3.5
#pragma multi_compile_fwdadd_fullshadows
#pragma multi_compile_fog
#pragma vertex vert
#pragma fragment frag
#include "MyForwardAdd.cginc"
ENDCG
}
}
}
Обратите внимание, что вам также понадобится проход для заклинателя теней для отбрасывания теней (вы можете просто скопировать проход, использованный в исходном коде стандартного шейдера), а также проход Мета, если вы хотите использовать запеченное освещение.
Файл, подобный «MyForwardBase.cginc», должен содержать определения для структур ввода и вывода вершины, а также функции вершин и фрагментов для прохода. При такой настройке вам также необходимо определить униформу (переменные свойства). Некоторые переменные, которые могут вам понадобиться для легких расчетов:
// Calculate this in the vertex shader and normalize per fragment
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
float3 viewDir = _WorldSpaceCameraPos.xyz - o.worldPos;
// In the ForwardBase pass, _WorldSPaceLightPos0.xyz stores the direction of the main directional light, instead of position.
// Use this in the vertex function:
half3 mainLightColor = _LightColor0.rgb;
half3 mainLightDir = _WorldSpaceLightPos0.xyz;
Для прохода ForwardBase вам необходимо включить «AutoLight.cginc» и использовать этот макрос в функции вершины:
COMPUTE_LIGHT_COORDS(o);
Что, в свою очередь, требует наличия этого макроса в структуре VertexOutput:
UNITY_LIGHTING_COORDS(6,7) // 6, 7 can be any texture interpolator IDs, they will translate to TEXCOORD6 and TEXCOORD7 in this case
ForwardAdd, ослабление света работает следующим образом:
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos.xyz); // Here, worldPos is stored in the vertex output struct and calculated per vertex
half3 lightColor = _LightColor0.rgb * atten;
Существуют также макросы для определения, находится ли пиксель в тени, вы можете найти их в AutoLight.cginc. В проходе ForwardAdd направление света рассчитывается для каждой вершины следующим образом:
o.lightDir = _WorldSpaceLightPos0.xyz - o.worldPos.xyz * _WorldSpaceLightPos0.w;
Лично мне нравится создавать небольшие CGInclude для каждой функции, такой как отражение, преломление, трипланарное отображение и т. Д., И просто включать их всякий раз, когда они мне нужны. Я также склонен хранить отдельную функцию под названием «Освещение» в своем собственном включаемом файле, поскольку расчеты освещения делятся между проходами. Эта функция принимает в качестве аргументов все переменные, такие как lightDir, viewDir, albedo, шероховатость и т. Д., И возвращает заштрихованный цвет фрагмента.
Как я уже сказал, это довольно сложный процесс, и это всего лишь учебник для начинающих. Реализация функции освещения - это отдельная история, хотя в этом может помочь просмотр UnityStandardBRDF.cginc. Я рекомендую вам начать с базовой модели blinn-phong для отладки и улучшить ее.