Shader的简单结构
//声明shader所在的层级目录
Shader "CookbookShaders/BasicDiffuse"
{
//输入参数
Properties
{
//变量名(面板显示名,变量类型)=初始化值
_EmissiveColor("Emissive Color",Color)=(1,1,1,1)
_AmbientColor("Ambient Color",Color) = (1,1,1,1)
_MySliderValue("This is a slider",Range(0,10)) = 2.5
}
//实际语义模块,可以有多个来对应不同的显卡配置
SubShader
{
//标签栏目,这里表明渲染的类型是不透明的物体
Tags { "RenderType"="Opaque" }
//层次细节 200
LOD 200
//CG代码的开始声明
CGPROGRAM
//预处理指令,表明这是surface类型的shader,其函数名伪surf。此外指定了其光照模型为Lambert模型
#pragma surface surf Lambert
//声明输入变量
float4 _EmissiveColor;
float4 _AmbientColor;
float _MySliderValue;
//输入结构体
struct Input
{
//主纹理的uv
float2 uv_MainTex;
};
//着色函数
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c;
//将两个颜色进行混合,并进行幕运算
c = pow((_EmissiveColor+_AmbientColor),_MySliderValue);
//将对应的颜色数据进行输出
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
//将阴影渲染相关的代码插入当前位置
FallBack "Diffuse"
}
上面是一个简单的混色shader,简单的将两个颜色进行输入,最后将混合的颜色根据MySliderValue的调节来输出,具体效果如下:
cg中的数据类型
- float,32位浮点数据,一个符号位。浮点数据类型被所有的profile支持(但是directX8 pixel profiles 在一些操作种降低了浮点数的精度和范围);
- half,16位浮点数据;
- int,32位整形数据,有些profile会将int类型作为float类型使用;
- fixed,12位定点数,被所有的fragment profiles所支持;
- bool,被所有profiles支持。
- sampler*,纹理对象的句柄(the handle to a texture object),分为6类:sampler,sampler1d,sampler2d,sampler3d,samplerCUBE,和samplerRECT。
其中如float2、float3之类的则是二维和三维的数组
自定义漫反射光照模型
在subshader中我们曾经执行了一句预处理命令,声明我们要使用Lambert来处理光照效果,而现在我们要使用自己的光照方案来处理光照问题,对代码中的subshader部分进行修改,修改后应该是这样:
Shader "CookbookShaders/BasicDiffuse"
{
Properties
{
_EmissiveColor("Emissive Color",Color)=(1,1,1,1)
_AmbientColor("Ambient Color",Color) = (1,1,1,1)
_MySliderValue("This is a slider",Range(0,10)) = 2.5
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
//使用BasicDiffuse的光照系统来进行光照计算
#pragma surface surf BasicDiffuse
float4 _EmissiveColor;
float4 _AmbientColor;
float _MySliderValue;
struct Input
{
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c;
c = pow((_EmissiveColor+_AmbientColor),_MySliderValue);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
//以Lignting+Name开头的就是所指定的光照计算方法
//s:在surf中计算出的颜色
//lightDir:顶点到光源的方向
//atten:光源到顶点的衰变值
inline float4 LightingBasicDiffuse(SurfaceOutput s,fixed3 lightDir,fixed atten)
{
//点积函数(dot)会将两个方向之间的夹角计算出来,取值在-1到1之间,如果是-1是背离,1是同向,0是垂直
//因为如果是负数会导致出现严重的黑块出现,所以这里必须取值限制在0到1之间
float difLight = max(0,dot(s.Normal,lightDir));
float4 col;
//将surf算出的颜色乘以_LightColor0中所获得的光源颜色,最后乘以倾斜角度和衰败对颜色的影响得出最后的颜色
col.rgb = s.Albedo * _LightColor0.rgb * (difLight * atten * 2);
col.a = s.Alpha;
return col;
}
ENDCG
}
FallBack "Diffuse"
}
效果如下:
可以看出来相对于之前的模型,对于高光的反馈更明显一些。
Half Lambert光照模型
由Valve提出的改进方法,避免了低光区缺乏层次感,其实原理就是在计算明亮度时候,将0.5作为基准值,而变动则被限制在了-0.5到0.5之间,这也就意味着光线夹角的作用将会被削弱一半,但是会带来更好的视觉增强。具体修改如下:
inline float4 LightingBasicDiffuse(SurfaceOutput s,fixed3 lightDir,fixed atten)
{
//计算出夹角度,注意这里没有max,因为现在负值并不会导致负数,可以拿来使用
float difLight = dot(s.Normal,lightDir);
//将被缩小一半的夹角变量和0.5相加,得出优化后的夹角权重
float hLambert = difLight * 0.5 + 0.5;
float4 col;
col.rgb = s.Albedo * _LightColor0.rgb * (hLambert * atten * 2);
col.a = s.Alpha;
return col;
}
效果如下:
加了max的结果:(个人感觉max没必要加,不如都0.5起步拟真性不高,反而反向缺乏层次感了)
渐变纹理控制的漫反射
准备一张如下所示的色图:
然后修改代码成为如下样式,将色图应用到夹角计算中:
Shader "CookbookShaders/BasicDiffuse"
{
Properties
{
_EmissiveColor("Emissive Color",Color)=(1,1,1,1)
_AmbientColor("Ambient Color",Color) = (1,1,1,1)
_MySliderValue("This is a slider",Range(0,10)) = 2.5
//声明一个2d图像输入量
_RampTex("Ramp Texture",2D) = ""
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf BasicDiffuse
float4 _EmissiveColor;
float4 _AmbientColor;
float _MySliderValue;
//声明数据类型
sampler2D _RampTex;
struct Input
{
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c;
c = pow((_EmissiveColor+_AmbientColor),_MySliderValue);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
inline float4 LightingBasicDiffuse(SurfaceOutput s,fixed3 lightDir,fixed atten)
{
float difLight = dot(s.Normal, lightDir);
float hLambert = difLight * 0.5 + 0.5;
//使用tex2D读取在色图中指定2d坐标的颜色,第二个参数就是要读取的颜色
float3 ramp = tex2D(_RampTex, float2(hLambert, hLambert)).rgb;
float4 col;
//将原来的物理因素全部排除,由色图来决定光照效果
col.rgb = s.Albedo * _LightColor0.rgb * (ramp);
col.a = s.Alpha;
return col;
}
ENDCG
}
FallBack "Diffuse"
}
最后实现的效果:
有趣的一点是,要注意色图的分辨率问题,tex2D是依据像素点来读取rgb的,假如你像素过高,则都会显示为一个色域,看起来就跟实现失败一样。此外我看的参考消息中float只有一个参数,而实际上unity中的语法里,float必须两个参数。