本文档主要是对Unity官方手册的个人理解与总结(其实以翻译记录为主:>)
仅作为个人学习使用,不得作为商业用途,欢迎转载,并请注明出处。
文章中涉及到的操作都是基于Unity2018.2版本
参考链接:https://docs.unity3d.com/Manual/SL-SurfaceShaderLighting.html
https://docs.unity3d.com/Manual/SL-SurfaceShaderLightingExamples.html
When writing Surface Shaders, you describe the properties of a surface (such as albedo color and normal), and a Lighting Model computes the lighting interaction.
当你在写表面着色器时,你描述了一个表面的属性(比如albedo颜色和法线),一个光照模型计算了灯光的相互作用。
There are two built-in lighting models: Lambert for diffuse lighting, and BlinnPhong for specular lighting. The Lighting.cginc file inside Unity defines these models (Windows: /Data/CGIncludes/Lighting.cginc; macOS: /Applications/Unity/Unity.app/Contents/CGIncludes/Lighting.cginc).
有两种内置的光照模式:用于漫射光照的兰伯特(Lambert ),以及用于高光光照的BlinnPhong光照。Lighting.cginc文件在Unity中定义了这些模型(Windows: /Data/CGIncludes/Lighting.cginc; macOS:/Applications/Unity/Unity.app/Contents/CGIncludes/Lighting.cginc).
Sometimes you might want to use a custom lighting model. You can do this with Surface Shaders. A lighting model is simply a couple of Cg/HLSL functions that match some conventions.
有时您可能想要使用自定义的光照模型。你可以用表面着色器来做。光照模型仅仅是一些与某些约定相匹配的Cg/HLSL函数。
Declaring lighting models 声明光照模型
A lighting model consists of regular functions with names that begin Lighting. You can declare them anywhere in your shader file, or one of the included files. The functions are:
光照模型名称是由Lighting开始的常规函数组成。您可以在shader文件的任何地方声明它们,也可以在其中一个包含的文件中声明它们。函数有:
Note that you don’t need to declare all functions. A lighting model either uses view direction or it does not. Similarly, if the lighting model only works in forward rendering, do not declare the _Deferred or _Prepass function. This ensures that Shaders that use it only compile to forward rendering.
注意,您不需要声明所有的函数。光照模型要么使用视图方向,要么不使用。类似地,如果光照模型只在前向渲染中工作,则不要声明延迟或预通道函数。这就确保了使用它的着色器只能编译前向渲染。
Custom GI 自定义GI
Declare the following function to customize the decoding lightmap data and probes:
声明以下函数来定制解码光照图数据和探针:
half4 Lighting_GI (SurfaceOutput s, UnityGIInput data, inout UnityGI gi);
Note that to decode standard Unity lightmaps and SH probes, you can use the built-in DecodeLightmap and ShadeSHPerPixel functions, as seen in UnityGI_Base in the UnityGlobalIllumination.cginc file inside Unity (Windows: /Data/CGIncludes/UnityGlobalIllumination.cginc; macOS: /Applications/Unity/Unity.app/Contents/CGIncludes/UnityGlobalIllumination.cginc_).
请注意,要解码标准的Unity lightmaps和SH探测,您可以使用内置的DecodeLightmap和ShadeSHPerPixel函数,就像UnityGlobalIllumination.cginc中的UnityGI_Base 中所看到的那样。
Surface Shader lighting examples
Because Deferred Lighting does not play well with some custom per-material lighting models, most of the examples below make the shaders compile to Forward Rendering only.
由于延迟光照与一些定制的每个材质的光照模型不太好,下面的大多数例子都使着色器只编译前向渲染。
Diffuse 漫反射
The following is an example of a shader that uses the built-in Lambert lighting model:
下面是一个使用内置的朗伯照明模型的着色器示例:
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"
}
Here’s how it looks like with a Texture and without a Texture, with one directional Light in the Scene:
这是它的效果,包括有纹理,没有纹理。在场景中有一个方向光:
The following example shows how to achieve the same result by writing a custom lighting model instead of using the built-in Lambert model.
下面的例子展示了如何通过编写一个定制的照明模型来实现相同的结果,而不是使用内置的Lambert模型。
To do this, you need to use a number of Surface Shader lighting model functions. Here’s a simple Lambert one. Note that only the CGPROGRAM section changes; the surrounding Shader code is exactly the same:
要做到这一点,您需要使用大量的表面着色器光照模型函数。这是一个简单的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"
}
This simple Diffuse lighting model uses the LightingSimpleLambert function. It computes lighting by calculating a dot product between surface normal and light direction, and then applying light attenuation and color.
这个简单的漫反射光照模型使用了LightingSimpleLambert函数。它通过计算表面法线和光照方向之间的点积来计算光照,然后应用光衰减和颜色。
Diffuse Wrap
The following example shows Wrapped Diffuse, a modification of Diffuse lighting where illumination “wraps around” the edges of objects. It’s useful for simulating subsurface scattering effects. Only the CGPROGRAM section changes, so once again, the surrounding Shader code is omitted:
下面的例子展示了Wrapped Diffuse,一种漫反射光照明的修改,在这里,光照“包裹”了物体的边缘。它对模拟次表面散射效果很有用。只有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...
Here’s how it looks like with a Texture and without a Texture, with one directional Light in the Scene:
Toon Ramp
The following example shows a “Ramp” lighting model that uses a Texture ramp to define how surfaces respond to the angles between the light and the normal. This can be used for a variety of effects, and is especially effective when used with Toon lighting.
下面的例子展示了一个“渐变(Ramp)”的光照模型,它使用一个纹理ramp来定义表面如何对光线和法线之间的角度做出反应。这可以用于多种效果,在使用卡通光照时尤其有效。
...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...
Here’s how it looks like with a Texture and without a Texture, with one directional Light in the Scene:
Simple Specular 简单高光
The following example shows a simple specular lighting model, similar to the built-in BlinnPhong lighting model.
下面的例子展示了一个简单的高光光照模型,类似于内置的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...
Here’s how it looks like with a Texture and without a Texture, with one directional Light in the Scene:
Custom GI
We’ll start with a Shader that mimics Unity’s built-in GI:
我们将从一个模拟Unity内置的GI的着色器开始:
Shader "Example/CustomGI_ToneMapped" {
Properties {
_MainTex ("Albedo (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
CGPROGRAM
#pragma surface surf StandardDefaultGI
#include "UnityPBSLighting.cginc"
sampler2D _MainTex;
inline half4 LightingStandardDefaultGI(SurfaceOutputStandard s, half3 viewDir, UnityGI gi)
{
return LightingStandard(s, viewDir, gi);
}
inline void LightingStandardDefaultGI_GI(
SurfaceOutputStandard s,
UnityGIInput data,
inout UnityGI gi)
{
LightingStandard_GI(s, data, gi);
}
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
o.Albedo = tex2D(_MainTex, IN.uv_MainTex);
}
ENDCG
}
FallBack "Diffuse"
}
Now, let’s add some tone mapping on top of the GI:
现在,让我们在GI的顶部添加一些色调映射:
Shader "Example/CustomGI_ToneMapped" {
Properties {
_MainTex ("Albedo (RGB)", 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 StandardToneMappedGI
#include "UnityPBSLighting.cginc"
half _Gain;
half _Knee;
half _Compress;
sampler2D _MainTex;
inline half3 TonemapLight(half3 i) {
i *= _Gain;
return (i > _Knee) ? (((i - _Knee)*_Compress) + _Knee) : i;
}
inline half4 LightingStandardToneMappedGI(SurfaceOutputStandard s, half3 viewDir, UnityGI gi)
{
return LightingStandard(s, viewDir, gi);
}
inline void LightingStandardToneMappedGI_GI(
SurfaceOutputStandard s,
UnityGIInput data,
inout UnityGI gi)
{
LightingStandard_GI(s, data, gi);
gi.light.color = TonemapLight(gi.light.color);
#ifdef DIRLIGHTMAP_SEPARATE
#ifdef LIGHTMAP_ON
gi.light2.color = TonemapLight(gi.light2.color);
#endif
#ifdef DYNAMICLIGHTMAP_ON
gi.light3.color = TonemapLight(gi.light3.color);
#endif
#endif
gi.indirect.diffuse = TonemapLight(gi.indirect.diffuse);
gi.indirect.specular = TonemapLight(gi.indirect.specular);
}
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
o.Albedo = tex2D(_MainTex, IN.uv_MainTex);
}
ENDCG
}
FallBack "Diffuse"
}