Unity Surface Shader 示例分析
对于Unity中的表面着色器(Surface Shader),它的代码整体结构如下所示:
Properties {
_MainTex ("Texture", 2D) = "white" {}
_BumpMap ("Bumpmap", 2D) = "bump" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
LOD 200
CGPROGRAM#pragma surface surf Lambert
sampler2D _MainTex;
sampler2D _BumpMap;
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
};
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
}
ENDCG
}
Fallback "Diffuse"
}
Shader "name" {
Properties {
// 第一部分
}
SubShader {
// 第二部分
}
Fallback "Diffuse" // 第三部分
}
第一部分 Properties 数据块
它的作用是充当数据的接口,将外部的数据(资源)引入进来,以供着色器内部使用。在这里,我们可以定义的数据类型如下所示:
(1) _MainTex ( "Base (RGB)", 2D ) = "white" {}
2D类型,主要指我们所使用的纹理贴图。
_MainTex是自定义的变量名称,我们在编写shader的代码时将使用它,它会显示在shader的属性面板上,下同;
"Base (RGB)"也是一个名称,它会在绑定该shader的材质属性面板上显示。
(2) _CubeTex ( "3D Map", Cube ) = "white" {}
Cube类型,表示三维纹理。三维纹理具有宽度、高度以及深度三个维度的信息。
(3) _Color ( "Main Color", Color ) = (1,1,1,1)
Color类型,表示一种颜色,包含( R, G, B, A )4个颜色值成分。
_Color是自定义的变量名称,在编写shader的代码时使用。
(4) _Float ( "Float Value", float ) = 0.0
float类型,表示一个浮点数值。
_Float是自定义的变量名称,在编写shader的代码时使用。
(5) _RimPower ( "Rim Power", Range( 0.5, 1.0 ) ) = 1.0
float类型,数值取自[min, max]这段范围。
_RimPower是自定义的变量名称,在编写shader的代码时使用。
(6) _Vector( "Vector4", Vector ) = ( 1, 1, 1, 1 )
Vector类型,表示4个元素的向量,包含( X, Y, Z, W )4个值。
_Vector是自定义的变量名称,在编写shader的代码时使用。
第二部分 SubShader 子着色器代码块
这一部分是着色器的代码主体,是我们编写代码的主阵地,在下面的示例分析中会着重写到。
第三部分 Fallback "Diffuse" 回滚块
回滚作为一种替代方案,当我们的图形卡没有找到合适的SubShader时将被调用,具备较为简单的着色效果。
接下来,我们从示例入手,来分析一下着色器的代码。我们的示例程序是Unity Surface Examples 中的Normal mapping,实现了法线贴图的基础着色器(链接地址http://docs.unity3d.com/Manual/SL-SurfaceShaderExamples.html),完整代码如下所示:
Shader "Example/Diffuse Bump" {
_MainTex ("Texture", 2D) = "white" {}
_BumpMap ("Bumpmap", 2D) = "bump" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
LOD 200
CGPROGRAM#pragma surface surf Lambert
sampler2D _MainTex;
sampler2D _BumpMap;
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
};
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
}
ENDCG
}
Fallback "Diffuse"
}
首先,我们在Properties块中定义了两个2D类型的变量:_MainTex和_BumpMap。_MainTex作为纹理贴图数据,_BumpMap作为法线贴图数据。
进入到我们的SubShader内部,首先看到的是一个Tags(标签)。Tags用来对表面着色器进行修饰,我们的硬件通过判断这些Tags,来决定何时应该调用该子着色器。常用的一些Tags如下所示:
"RenderType" = "Opaque" // 在绘制不透明图形时调用
"RenderType" = "Transparent" // 在绘制透明图形时调用
"IgnoreProjector" = "True" // 不受投影的影响
"Queue" = "XXX" // 指定渲染队列
LOD 200
LOD是Level of Detail 的缩写,这个数值的大小决定了我们的SubShader是否可以被调用。当系统设定的最大LOD小于该SubShader所指定的LOD时,该SubShader将不可用。
接下来的部分,CGPROGRAM - ENDCG 表示这是一段CG程序。#pragma
surface surf Lambert 是一个编译指令,surface表明这是一个表面着色器,surf是着色器调用的方法名字,Lambert则是使用的光照模型,这也是一个Unity自带的光照模型。需要特别注意的是,想要在CG程序中访问在Properties中所定义的变量的话,必须在CG程序中对这些变量使用相同的名字再次进行声明。只不过这时,变量的类型要使用CG程序中相对应的类型。Input是一个需要我们自定义的结构体,它的对象作为输入(IN)参数,以供我们在surf方法中使用。在上述示例中,我们定义了uv_MainTex和uv_BumpMap,它们分别表示了纹理贴图和法线贴图的uv坐标。最后的surf函数部分,是着色器的方法主体。在这里我们完成了着色和法线的处理。最终效果如下所示:
来源:CSDN
作者:杜晓萌
链接:https://blog.csdn.net/Haohan_Meng/article/details/43345033