颜色
一样先看代码:
Shader "Demo/Diffuse Color"{
Properties{
_Color("Color", Color) = (1,1,1,1)
}
SubShader{
Tags{"RenderType"="Opaque"}
LOD 200
CGPROGRAM
#pragma surface surf Lambert
float4 _Color;
struct Input{
float4 uv_Tex;
};
void surf(Input IN, inout SurfaceOutput o){
o.Albedo = _Color.xyz;
o.Alpha = _Color.w;
}
ENDCG
}
FallBack "Diffuse"
}
前一篇文章介绍过的不再重复,还是逐行说明:
_Color("Color", Color) = (0,0,0)
属性定义的一个关于Color(颜色)的属性,每条属性的定义语法如下:
_Name("Display Name", type) = defaultvalue{options}
_Name 属性的名字,代码调用的属性名。
Display Name 显示在unity的材质编辑器中作为可读的属性名(一般规范为可读属性名前加下划线即代码调用参数名)
type表示属性类型,可能表示的内容有以下几种:
1.Color 一种颜色,由RGBA(红绿蓝和透明度)四个量表示。
2.2D 一张2的阶数大小的贴图,在被采样后转为基于模型UV的每个像素的颜色,最终被显示出来。
3.Rect 一张非2的阶数大小的贴图。
4.Cube Cube Map Texture(立方体贴图),6张有联系的2D贴图组合,主要用于制作反射效果(天空盒和动态反射),也会被转换为对应点采样。
5.Range(min, max) 介于最小值和最大值的浮点数。
6.Float 浮点数
7.Vector 四维数
defaultvalue定义默认属性值,{options}只对2D,Rect,Cube贴图有关,输入时如果是贴图至少后边要跟一对什么都不含的{},需要打开特定选项时可以填入{}中,可能的选择有ObjectLinear, EyeLinear, SphereMap, CubeReflect, CubeNormal中的一个,这些都是OpenGL中TexGen(纹理坐标生成)的模式,以后再研究。
Tags{"RenderType"="Opaque"}
该标签向系统声明,渲染非透明物体时调用,相反的是Tags{"RenderType"="Transparent"},表示渲染透明物体时调用。
常用的标签还有如下:
1."IgnoreProjector"="True",不被Projectors(投影)影响
2."ForceNoShadowCasting"="True",从不产生阴影
3."Queue"="xxx",指定渲染顺序队列。
Queue指定了物体的渲染顺序,预定义的Queue有:
1.Background - 最早被调用的渲染,用来渲染天空盒或者背景
2.Geometry - 这是默认值,用来渲染非透明物体(普通情况下,场景中的绝大多数物体应该是非透明的)
3.AlphaTest - 用来渲染经过Alpha Test的像素,单独为AlphaTest设定一个Queue是出于对效率的考虑
4.Transparent - 以从后往前的顺序渲染透明物体
5.Overlay - 用来渲染叠加的效果,是渲染的最后阶段(比如镜头光晕等特效)
这些预定义的值本质上是一组整数,Background = 1000,Geometry = 2000,AlphaTest = 2450,Transparent = 3000,Overlay = 4000。在我们实际设置Queue值时,不仅能使用上面的几个预定义值,我们也可以指定自己的Queue值,写成类似这样:"Queue"="Transparent+100",表示一个在Transparent之后100的Queue上进行调用。通过调整Queue值,我们可以确保某些物体一定在另一些物体之前或者之后渲染,这个技巧有时候很有用处。
LOD 200
LOD是Level of Detail的缩写,在这里我们指定其为200(Unity的内建Diffuse着色器的设定值)。在Unity的Quality Settings中我们可以设定允许的最大LOD,当设定的LOD小于SubShader所指定的LOD时,这个SubShader将不可用。Unity内建Shader定义了一组LOD的数值,我们在实现自己的Shader的时候可以将其作为参考来设定自己的LOD数值,这样在之后调整根据设备图形性能来调整画质时可以进行比较精确的控制。
VertexLit及其系列 = 100
Decal, Reflective VertexLit = 150
Diffuse = 200
Diffuse Detail, Reflective Bumped Unlit, Reflective Bumped VertexLit = 250
Bumped, Specular = 300
Bumped Specular = 400
Parallax = 500
Parallax Specular = 600
float4 _Color;
对Properties里的_Color再次声明。因为shader是由两个相对独立的块组成的,外层的属性声明,回滚等等是Unity可以直接使用和编译的ShaderLab;而对于CGPROGRAM...ENDCG 中的CG程序,要想访问在Properties中定义的变量,必须使用和之前变量相同的名字进行声明。声明完毕后,surf方法中可以直接使用Properties中定义的_Color参数。
void surf(Input IN, inout SurfaceOutput o) {
o.Albedo = _Color.xyz;
o.Alpha = _Color.w;
}
下面解析Input和SurfaceOutput的结构:
struct Input {
float2 uv_MainTex; //纹理坐标
float3 viewDir; //入视角(计算视差、边缘光照等效果)
float4 screenPos; //裁剪空间位置(为了获得反射效果,需要包含屏幕坐标)
float3 worldPos; //世界空间位置
float3 worldRefl; //世界中的反射向量。如果表面着色写入o.Normal, 将包含世界反射向量。
float3 worldNormal; //世界中的法线向量。如果表面着色器写入法线(o.Normal)参数,将包含这个参数。
} ;
//默认的几种输出结构,可以自定义
struct SurfaceOutput {
fixed3 Albedo; // 像素颜色
fixed3 Normal; // 像素法向量值
fixed3 Emission; // 像素自发光颜色
half Specular; // 像素镜面高光强度,范围是0-1
fixed Gloss; // 镜面光滑程度
fixed Alpha; // 像素透明度
};
struct SurfaceOutputStandard {
fixed3 Albedo;
fixed3 Normal;
half3 Emission;
half Metallic; // 金属光泽度,0=非金属, 1=金属
half Smoothness; //光滑度 0=粗糙, 1=光滑
half Occlusion; // 遮挡(默认1)
fixed Alpha;
};
struct SurfaceOutputStandardSpecular {
fixed3 Albedo;
fixed3 Specular; // 镜面反射颜色
fixed3 Normal;
half3 Emission;
half Smoothness;
half Occlusion;
fixed Alpha;
};
看完上边的结构定义说明,应该明白surf中的方法所表达的意义了吧。
效果图
贴图
上代码:
Shader "Demo/Diffuse Texture"{
Properties{
_MainTex("MainTex",2D) = ""{}
}
SubShader{
Tags{"RenderType"="Opaque"}
LOD 200
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
struct Input{
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o){
half4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
sampler2D _MainTex;
sampler2D是一个数据容器接口。简单地理解,sampler2D就是GLSL中的2D贴图的类型,相应的,还有sampler1D,sampler3D,samplerCube等等格式。
half4 c = tex2D(_MainTex, IN.uv_MainTex);
tex2d函数是CG程序中用来在一张贴图中对一个点进行采样的方法,返回一个float4。这里对_MainTex在输入点上进行了采样,将其颜色的RGBA值赋予了输出。于是,着色器就明白了应当怎样工作:即找到贴图上对应的uv点,直接使用颜色信息来进行着色。half和float与double类似,表示浮点数,只是精度不一样。这里half指的是半精度浮点数,精度最低,运算性能相对比高精度浮点数高一些,因此被大量使用。
注:可能会有人发现照着例子调整透明度,并没有什么效果。这就对了,因为没有开启Alpha混合,最简单的实现是在编译定义后加alpha(要小写!),即:#pragma surface surf Lambert alpha
,但其实最后渲染效果还是会有问题,例如被不透明物体遮挡(渲染方式和顺序导致),过于透明效果的实现以后文章说明。