Ниже приведено несколько примеров пользовательских моделей освещения в поверхностных шейдерах. Основные примеры поверхностных шейдеров находятся на этой странице.
Так как отложенное освещение не очень хорошо работает с некоторыми пользовательскими моделями освещения для отдельных материалов, в большинстве примеров, приведённых ниже, мы создаём шейдеры только для упреждающего рендеринга.
Мы начнём с шейдера, который использует встроенную модель освещения Lambert:-
Shader "Example/Diffuse Texture" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float2 uv_MainTex;
};
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
}
Fallback "Diffuse"
}
Вот как он выглядит с текстурой и без неё (в сцене находится один направленный источник света):
Теперь давайте сделаем всё то же самое, но напишем свою собственную модель освещения вместо использования встроенной Lambert. Модели освещения поверхностного шейдера – это просто несколько функций, которые мы должны написать. Вот простой Lambert. Заметьте, что изменяется только секция CGPROGRAM, весь остальной код остаётся таким же:-
Shader "Example/Diffuse Texture" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf SimpleLambert
half4 LightingSimpleLambert (SurfaceOutput s, half3 lightDir, half atten) {
half NdotL = dot (s.Normal, lightDir);
half4 c;
c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten);
c.a = s.Alpha;
return c;
}
struct Input {
float2 uv_MainTex;
};
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
}
Fallback "Diffuse"
}
Итак, наша простая модель Diffuse (рассеянного) освещения – это функция LightingSimpleLambert
. Она рассчитывает освещение получая dot product между нормалью поверхности и направлением света, и затем применяет затухание и цвет источника освещения.
Вот огибающий Diffuse - модификация рассеянного освещения, где свечение “огибает” края объектов. Это полезно для имитации эффекта подповерхностного рассеивания (subsurface scattering). Так как меняется только секция CGPROGRAM, остальной код шейдера опущен:-
...ShaderLab code...
CGPROGRAM
#pragma surface surf WrapLambert
half4 LightingWrapLambert (SurfaceOutput s, half3 lightDir, half atten) {
half NdotL = dot (s.Normal, lightDir);
half diff = NdotL * 0.5 + 0.5;
half4 c;
c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten);
c.a = s.Alpha;
return c;
}
struct Input {
float2 uv_MainTex;
};
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
...ShaderLab code...
Вот модель освещения “Ramp” (наклонная), которая использует текстуру наклонов для определения того, как поверхность реагирует на угол между светом и нормалью. Это можно использовать для различных эффектов, включая toon освещение.
...ShaderLab code...
CGPROGRAM
#pragma surface surf Ramp
sampler2D _Ramp;
half4 LightingRamp (SurfaceOutput s, half3 lightDir, half atten) {
half NdotL = dot (s.Normal, lightDir);
half diff = NdotL * 0.5 + 0.5;
half3 ramp = tex2D (_Ramp, float2(diff)).rgb;
half4 c;
c.rgb = s.Albedo * _LightColor0.rgb * ramp * atten;
c.a = s.Alpha;
return c;
}
struct Input {
float2 uv_MainTex;
};
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
...ShaderLab code...
Вот простая зеркальная модель освещения. То что на самом деле делает встроенный BlinnPhong - довольно просто, мы просто оставим этот здесь для того, чтобы показать как он сделан.
...ShaderLab code...
CGPROGRAM
#pragma surface surf SimpleSpecular
half4 LightingSimpleSpecular (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {
half3 h = normalize (lightDir + viewDir);
half diff = max (0, dot (s.Normal, lightDir));
float nh = max (0, dot (s.Normal, h));
float spec = pow (nh, 48.0);
half4 c;
c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * spec) * atten;
c.a = s.Alpha;
return c;
}
struct Input {
float2 uv_MainTex;
};
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
...ShaderLab code...
We’ll start with a shader that mimics built-in lightmap decoding and uses Unity built-in function DecodeLightmap to decode information from data stored in lightmap textures and built-in UNITY_DIRBASIS macro defining basis vectors for Directional lightmaps:
Shader "Example/Standard Lightmap Decoding" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Standard
half4 LightingStandard (SurfaceOutput s, half3 lightDir, half atten) {
half NdotL = dot (s.Normal, lightDir);
half4 c; c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten);
c.a = s.Alpha;
return c;
}
inline fixed4 LightingStandard_SingleLightmap (SurfaceOutput s, fixed4 color) {
half3 lm = DecodeLightmap (color);
return fixed4(lm, 0);
}
inline fixed4 LightingStandard_DualLightmap (SurfaceOutput s, fixed4 totalColor, fixed4 indirectOnlyColor, half indirectFade) {
half3 lm = lerp (DecodeLightmap (indirectOnlyColor), DecodeLightmap (totalColor), indirectFade);
return fixed4(lm, 0);
}
inline fixed4 LightingStandard_DirLightmap (SurfaceOutput s, fixed4 color, fixed4 scale, bool surfFuncWritesNormal) {
UNITY_DIRBASIS
half3 lm = DecodeLightmap (color);
half3 scalePerBasisVector = DecodeLightmap (scale);
if (surfFuncWritesNormal)
{
half3 normalInRnmBasis = saturate (mul (unity_DirBasis, s.Normal));
lm *= dot (normalInRnmBasis, scalePerBasisVector);
}
return fixed4(lm, 0);
}
struct Input {
float2 uv_MainTex;
};
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
}
Fallback "Diffuse"
}
Теперь, давайте добавим немного карт тональности, для света, хранящегося в картах освещения:
Shader "Example/Tonemapped Lightmap Decoding" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
_Gain ("Lightmap tone-mapping Gain", Float) = 1
_Knee ("Lightmap tone-mapping Knee", Float) = 0.5
_Compress ("Lightmap tone-mapping Compress", Float) = 0.33
}
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Tonemapped
half4 LightingTonemapped (SurfaceOutput s, half3 lightDir, half atten) {
half NdotL = dot (s.Normal, lightDir);
half4 c; c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten); c.a = s.Alpha;
return c;
}
half _Gain;
half _Knee;
half _Compress;
inline half3 TonemapLight (half3 i) {
i *= _Gain;
return (i > _Knee)? (((i - _Knee)*_Compress)+_Knee): i;
}
inline fixed4 LightingTonemapped_SingleLightmap (SurfaceOutput s, fixed4 color) {
half3 lm = TonemapLight(DecodeLightmap (color));
return fixed4(lm, 0);
}
inline fixed4 LightingTonemapped_DualLightmap (SurfaceOutput s, fixed4 totalColor, fixed4 indirectOnlyColor, half indirectFade) {
half3 lm = TonemapLight(lerp (DecodeLightmap (indirectOnlyColor), DecodeLightmap (totalColor), indirectFade));
return fixed4(lm, 0);
}
inline fixed4 LightingTonemapped_DirLightmap (SurfaceOutput s, fixed4 color, fixed4 scale, bool surfFuncWritesNormal) {
UNITY_DIRBASIS
half3 lm = TonemapLight(DecodeLightmap (color));
half3 scalePerBasisVector = DecodeLightmap (scale);
if (surfFuncWritesNormal)
{
half3 normalInRnmBasis = saturate (mul (unity_DirBasis, s.Normal));
lm *= dot (normalInRnmBasis, scalePerBasisVector);
}
return fixed4(lm, 0);
}
struct Input {
float2 uv_MainTex;
};
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
}
Fallback "Diffuse"
}