Bootstrap

Surface Shader(可编程渲染管线 表面着色器)

1、Unity 3d官方文档上对WorldNormalVector的解释

float3 worldNormal INTERNAL_DATA -will contain world normal vector if surface shader writes to o.Normal. Toget the normal vector based on per-pixel normal map, use WorldNormalVector(IN, o.Normal).

float3 worldNormalINTERNAL_DATA 是U3D提供的内置数据,如果表面shader为SurfaceOutput结构中的Normal赋值了,此它会包含世界空间坐标系中法线的向量,通过调用方法WorldNormalVector(IN,o.Normal) 即可得到此值。

使用方法:

struct Input
{
  float3 worldNormal;INTERNAL_DATA
};
void surf(Input IN, inout SurfaceOutput o)
{
  o.Normal = UnpackNormal(tex2D(_NormalMap, IN.NormalMap)).rgb;
  float3 diffuseVal = texCUBE(_CubeMap, WorldNormalVector(IN, o.Normal)).rgb;
}

2、自定义光照模型

inline float4 LightingCustomDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten) {  
    float difLight = max(0, dot (s.Normal, lightDir));
    float hLambert = difLight * 0.5 + 0.5;
    float4 col;
    col.rgb = s.Albedo * _LightColor0.rgb * (hLambert * atten * 2);
    col.a = s.Alpha;
    return col;
}

inline表示内联的。

CustomDiffuse 表示光照模型,我们自定义的光照模型,unity自动光照模型有lambert和BlinnPhong两种光照模式,由于unity中有严格的书写格式,所以在使用时,其前要有Lighting的注明。

光照具体命名和作用区别

【形式一】

half4 LightingName(SurfaceOutput s, half3lightDir, half atten);

此种形式的函数可以表示在正向渲染路径(forward rendering path)中的光照模式,且此函数不取决于视图方向(view direction)。例如:漫反射(diffuse)。

【形式二】

half4 LightingName (SurfaceOutputs, half3lightDir, half3 viewDir, half atten);

此种形式的函数可以表示在正向渲染路径(forward rendering path)中使用的光照模式,且此函数包含了视图方向(view direction)。

【形式三】

half4LightingName_PrePass (SurfaceOutputs, half4 light);

此种形式的函数可以在延时光照路径(deferred lighting path)中使用的。

【形式四】

half4LightingName_DirLightmap(SurfaceOutput s, fixed4 color, fixed4 scale, boolsurfFuncWritesNormal);

这种形式也是不依赖于视图方向(viewdirection)的光照模式。例如:漫反射(diffuse)。

【形式五】

half4LightingName_DirLightmap(SurfaceOutput s, fixed4 color, fixed4 scale, half3viewDir, bool surfFuncWritesNormal,out half3 specColor);这是使用的依赖于视图方向(view direction)的光照模式(light model)。

比如,一个光照模式(lightingmodel)要么使用视图方向(viewdirection)要么不使用。同样的,如果光照模式(lightingmodel)在延时光照(deferred lighting)中不工作,只要不声明成_PrePass(第三种形式),就是行的。

另外,对于形式四和形式五的选择,主要取决于我们的光照模式(light model)是否依赖视图方向(view direction)。需要注意的是,这两个函数将自动处理正向和延时光照路径(forwardand deferred lighting rendering paths)

PS: Unity在移动平台中暂时不支持延迟光照渲染。

 

函数命名:LightingCustomDiffuse

shader中对于方法的名称有着比较严格的约定,想要创建一个光照模型,首先要做的是按照规则声明一个光照计算的函数名字,即Lighting<Your Chosen Name>。对于我们的光照模型CustomDiffuse,其计算函数的名称自然就是LightingCustomDiffuse了。光照模型的计算是在surf方法的表面颜色之后,根据输入的光照条件来对原来的颜色在这种光照下的表现进行计算,最后输出新的颜色值给渲染单元完成在屏幕的绘制

函数参数:(SurfaceOutputs, fixed3 lightDir, fixed atten

SurfaceOutput s 表示由surf方法计算出的表面颜色信息

fixed3 lightDir  表示光照方向,由unity内部确定

atten 光的衰减系数,由unity内部确定

函数内部变量:_LightColor0 _SpecColor

_LightColor0 表示光照的颜色,由unity内部确定

_SpecColor 表示镜面反射的颜色,由unity内部确定

 

3、更改顶点模型

void vert (inout appdata_full v) {  
    float4 sn = mul(transpose(_Object2World) , _SnowDirection);
    if(dot(v.normal, sn.xyz) >= lerp(1,-1, (_Snow * 2) / 3)) {
        v.vertex.xyz += (sn.xyz + v.normal) * _SnowDepth * _Snow;
    }
}

如何定义片段方法、光照模型和顶点方法

一般格式为 #pragma surface surfaceFunction lightModel[optionalparams]

#pragma surface surf CustomDiffusevertex:vert

//执行顺序 顶点处理函数vert->表面处理函数surf ->光照模型函数CustomDiffuse ->颜色值

surface表示片段着色器其后紧跟片段方法名surf

CustomDiffuse 表示光照模型,我们自定义的光照模型,unity自动光照模型有lambert和BlinnPhong两种光照模式,由于unity中有严格的书写格式,所以在使用时,其前要有Lighting的注明,如LightingLambertLightingCustomDiffuse

vertex表示顶点着色器其后使用:紧跟顶点方法vert。

函数内部方法:transpose(矩阵)  _Object2World()

transpose()输出原矩阵的逆矩阵,比如模型空间转世界空间,逆矩阵即为世界空间转模型空间

_Object2World()变换矩阵,表示将当前模型空间转换到世界空间中的矩阵

 

4、surface shader的基本属性和写法

Shader "Custom/Diffuse Texture" {  
    Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Lambert

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        v
;