The fundamental of Unity Shader

Created by miccall (转载请注明出处 miccall.tech)

宏定义
条件编译
预定义着色器预处理器宏
预编译
Unity Lighting Model
自定义光照模型
其余的一些重要内置文件

MacroDefinition 宏定义 :

define命令是C类语言中的一个宏定义命令,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本

    // #define <宏名> <字符串>  例:
    #define PI 3.1415926
    // #define <宏名> (<参数表>) <宏体>  例: 
    #define A(x) x

解出定义:


    #undef <宏名> 

条件编译 :

当标识符已经被定义过(一般是用#define命令定义),
则对程序段1进行编译,否则编译程序段2。:


    #ifdef 标识符
            程序段1
    #else
            程序段2
    #endif

其中#else部分也可以没有,即:

    #ifdef
            程序段1
    #denif

这里的“程序段”可以是语句组,也可以是命令行。

例子:


    // 我们有一个数据类型,在Windows平台中,应该使用long类型表示,而在其他平台应该使用float表示
    #ifdef WINDOWS
            #define MYTYPE long
    #else
            #define MYTYPE float
    #endif

    // 调试模式下才输出一些 
    #ifdef DEBUG
            print ("device_open(%p)\n", file);
    #endif

还有另一种方式:

    #ifndef 标识符
            程序段1
    #else
            程序段2
    #endif

将“ifdef”改为“ifndef”。它的作用是:若标识符未被定义则编译程序段1,
否则编译程序段2。这种形式与第一种形式的作用相反。

还有这种:

    #if 表达式
            程序段1
    #else
            程序段2
    #endif

当指定的表达式值为真(非零)时就编译程序段1,否则编译程序段2。
可以事先给定一定条件,使程序在不同的条件下执行不同的功能。

复杂表达式用法:


    #if defined (表达式1) || defined (表达式2) ||  !defined(表达式3)

else if 判断 :

    #if 条件1
       代码段1
    #elif 条件2
       代码段2
        ...
    #elif 条件n
     代码段n

    #else
     代码段 n+1
    #endif

shader 预定义着色器预处理器宏

  1. 目标平台

    SHADER_API_D3D11    // Direct3D 11
    SHADER_API_GLCORE    // 桌面OpenGL“核心”(GL 3/4)
    SHADER_API_GLES        // OpenGL ES 2.0
    SHADER_API_GLES3    // OpenGL ES 3.0 / 3.1
    SHADER_API_METAL    // iOS / Mac Metal
    SHADER_API_VULKAN    // 福尔康
    SHADER_API_D3D11_9X    // 通用Windows平台 Direct3D 11“功能级别9.x”目标
    SHADER_API_PS4        // PlayStation 4. SHADER_API_PSSL也是定义的。
    SHADER_API_XBOXONE    // Xbox One
    SHADER_API_PSP2        // PlayStation Vita

  1. 目标模型

    #if SHADER_TARGET < 30
        // less than Shader model 3.0:
        // very limited Shader capabilities, do some approximation
    #else
        // decent capabilities, do a better thing
    #endif

  1. Unity版本

    UNITY_VERSION

包含Unity版本的数值。例如,UNITY_VERSION适用501于Unity 5.0.1。
如果您需要编写使用不同内置着色器功能的着色器,则可以将其用于版本比较。
例如,


    #if UNITY_VERSION >= 500

预处理程序检查仅传递版本5.0.0或更高版本。

  1. 正在编译着色器阶段

预处理宏


    SHADER_STAGE_VERTEX,
    SHADER_STAGE_FRAGMENT,
    SHADER_STAGE_DOMAIN,
    SHADER_STAGE_HULL,
    SHADER_STAGE_GEOMETRY,
    SHADER_STAGE_COMPUTE

正在编制每个着色阶段时定义。通常,在像素
之间共享着色器代码时,它们很有用着色器和计算着色器,以处理某些事情必须略有不同的情况。

  1. 阴影贴图宏
  2. 常量缓冲区宏
  3. 纹理/采样器声明宏

预编译:

#pragma vertex name    
// 编译name函数为顶点着色器

#pragma fragment name    
// 编译name函数为片元着色器

#pragma geometry name    
// 编译name函数为DX10的几何着色器
// 注:会自动开启#pragma target 4.0

#pragma hull name    
// 编译name函数为DX10的壳着色器
// 注:会自动开启#pragma target 5.0

#pragma domain name    
// 编译name函数为DX10的域着色器
// 注:会自动开启#pragma target 5.0

#pragma target name    
// 表明编译目标

#pragma only_renderers space_separated_names    
// 只为指定的渲染平台渲染着色器
/*    
    包括下列值:
    d3d9:Direct3D 9
    d3d11:Direct3D 11/12
    glcore:OpenGL 3.x/4.x
    gles:OpenGL ES 2.0
    gles:OpenGL ES 3.x
    metal:IOS&Mac Metal
    d3d11_9x:Direct3D 11 9.x特性等级一般用于WSA平台
    xbox360:Xbox 360
    xboxone:Xbox One
    ps4:PlayStation 4
    psp2:PlayStation Vita
    n3ds:Nintendo 3DS
    wiiu:Nintendo Wii U
*/ 


#pragma exclude_renderers space_separated_names    
// 排除指定的渲染平台 
/*    
    参数同上
*/ 


#pragma multi_compile ...    
// 为shader创建多个稍微有点区别的shader变体。这个Shader被称为宏着色器(mega shader)或者超着色器(uber shader)。实现原理:根据不同的情况,使用不同的预处理器指令,来多次编译Shader代码
// https://blog.csdn.net/ecidevilin/article/details/52882400


#pragma enable_d3d11_debug_symbols    
// 生成d3d11的调试信息
// 可以在VS2012(或以上)使用图形调试器调试 shader


#pragma hardware_tier_variants renderer_name    
// 针对所选渲染器的每个硬件层级
// 生成每个已编译的Shader的多重Shader硬件变体
// Unity官方文档:多重着色器变体[https://docs.unity3d.com/Manual/SL-MultipleProgramVariants.html]

Unity Lighting Model

form : Lighting.cginc

该文件包含了一个 SurfaceOutput 结构体 ,并给出了 Lambert 和 BlinnPhong 光照模型 在 前向渲染 和 延迟渲染中的渲染 ,以及在GI作用下,传统路径下的渲染

Diffuse :


inline fixed4 UnityLambertLight (SurfaceOutput s, UnityLight light)
{
    fixed diff = max (0, dot (s.Normal, light.dir));

    fixed4 c;
    c.rgb = s.Albedo * light.color * diff;
    c.a = s.Alpha;
    return c;
}

Specular :


inline fixed4 UnityBlinnPhongLight (SurfaceOutput s, half3 viewDir, UnityLight light)
{
    half3 h = normalize (light.dir + viewDir);

    fixed diff = max (0, dot (s.Normal, light.dir));

    float nh = max (0, dot (s.Normal, h));
    float spec = pow (nh, s.Specular*128.0) * s.Gloss;

    fixed4 c;
    c.rgb = s.Albedo * light.color * diff + light.color * _SpecColor.rgb * spec;
    c.a = s.Alpha;

    return c;
}

自定义光照模型:


    half4 Lighting<Name> (SurfaceOutput s, UnityGI gi);
    // 在前向渲染中使用此路径,了解不依赖于视图方向的灯光模型。

    half4 Lighting<Name> (SurfaceOutput s, half3 viewDir, UnityGI gi); 
    // 在前向渲染中使用此选项用于光模式,路径是依赖于观察方向。

    half4 Lighting<Name>_Deferred (SurfaceOutput s, UnityGI gi, out half4 outDiffuseOcclusion, out half4 outSpecSmoothness, out half4 outNormal); 
    // 在延迟光照路径中使用它。

    half4 Lighting<Name>_PrePass (SurfaceOutput s, half4 light); 
    // 在光预通过(传统延迟)光照路径中使用此功能。

    half4 Lighting<Name>_GI (SurfaceOutput s, UnityGIInput data, inout UnityGI gi);
    // 自定义解码光照贴图数据和探针


其余的一些重要内置文件

HLSLSupport.cginc- (自动包含)帮助程序宏和跨平台着色器的定义编译。
UnityShaderVariables.cginc- (自动包含)常用的全局变量。
UnityCG.cginc- 常用的辅助函数。
AutoLight.cginc- 照明和阴影功能,例如表面着色器内部使用此文件。
Lighting.cginc- 标准表面着色器照明模型; 在您编写表面着色器时自动包含。
TerrainEngine.cginc- 地形辅助功能和植被着色器。