提要
今天要学习的是一些Shader 的例子,从简单到难。Let's go.
一大波例子来袭
还是用上一篇用到的工程。点我下载
红色的螃蟹
Test1.shader
- Shader "Custom/Test1" {
- SubShader {
- Tags { "RenderType" = "Opaque" }
- CGPROGRAM
- #pragma surface surf Lambert
- struct Input {
- float4 color : COLOR;
- };
- void surf (Input IN, inout SurfaceOutput o) {
- o.Albedo = 1;
- }
- ENDCG
- }
- Fallback "Diffuse"
- }
o.Albedo = 1;表示输出颜色是白色,将方向光调成红色,最后经过lambert光照模型计算后,得到
带法线贴图的螃蟹
- Shader "Custom/Test2" {
- Properties {
- _MainTex ("Texture", 2D) = "white" {}
- _BumpMap ("Bumpmap", 2D) = "bump" {}
- }
- SubShader {
- Tags { "RenderType" = "Opaque" }
- CGPROGRAM
- #pragma surface surf Lambert
- struct Input {
- float2 uv_MainTex;
- float2 uv_BumpMap;
- };
- sampler2D _MainTex;
- sampler2D _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"
- }
对比感受一下,左边是不带法线贴图的,右边是带法线贴图的。
从剑灵里面跑出来的螃蟹
先看下啥是剑灵风
感觉就是很多高光有木有(不要瞎瞅,喂!),这还有个专业名词,叫Rim Lighting。我们的螃蟹也可以,哼~
- Shader "Custom/Test3" {
- Properties {
- _MainTex ("Texture", 2D) = "white" {}
- _BumpMap ("Bumpmap", 2D) = "bump" {}
- _RimColor ("Rim Color", Color) = (0.26,0.19,0.16,0.0)
- _RimPower ("Rim Power", Range(0.5,8.0)) = 3.0
- }
- SubShader {
- Tags { "RenderType" = "Opaque" }
- CGPROGRAM
- #pragma surface surf Lambert
- struct Input {
- float2 uv_MainTex;
- float2 uv_BumpMap;
- float3 viewDir;
- };
- sampler2D _MainTex;
- sampler2D _BumpMap;
- float4 _RimColor;
- float _RimPower;
- void surf (Input IN, inout SurfaceOutput o) {
- o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
- o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
- half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
- o.Emission = _RimColor.rgb * pow (rim, _RimPower);
- }
- ENDCG
- }
- Fallback "Diffuse"
- }
渲染结果,(模型精度有点低,凑合着看吧)
原理简单说一下,主要是用来计算边缘光照的,首先通过视线与法线的夹角来找到模型的边缘,然后再根据距离的远近来控制发射光的强度。
- half rim = 1.0 - saturate(dot (normalize(IN.viewDir), IN.worldNormal));
- o.Emission = _RimColor.rgb * pow (rim, _RimPower);
IN.viewDir是当前视角向量,IN.worldNormal是物体的法线。dot是计算视角和法线的点积,等于视角和法线夹角的cos值,Cos的值域是1-0,1-cos就成了0-1,在夹角90度时达到最大值,正好用来模拟侧光的强度(与视角成90度的部分光线最强,就是边缘光了)
把这个值的变化率用一个pow函数(rim的_rimPower次方)进行放大,就能强化边缘发亮的效果。
胖胖的螃蟹
这个效果原理很简单,就是将顶点位置沿着法线方向移动一定的距离。
- Shader "Custom/Test4" {
- Properties {
- _MainTex ("Texture", 2D) = "white" {}
- _Amount ("Extrusion Amount", Range(-1,1)) = 0.5
- }
- SubShader {
- Tags { "RenderType" = "Opaque" }
- CGPROGRAM
- #pragma surface surf Lambert vertex:vert
- struct Input {
- float2 uv_MainTex;
- };
- float _Amount;
- void vert (inout appdata_full v) {
- v.vertex.xyz += v.normal * _Amount;
- }
- sampler2D _MainTex;
- void surf (Input IN, inout SurfaceOutput o) {
- o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
- }
- ENDCG
- }
- Fallback "Diffuse"
- }
确实胖了,但是..怎么画框框的地方怎么有点怪怪的? 其实是mesh的问题,这个从游戏里面提取的模型,三角面可能并不好,比如这个提取的mesh网格九没有封闭。那就换一个好了!
从Dota2的官网下一个旱地神牛的模型下来,导入进来,给他同样的shader,结果如下。
高富帅瞬间变蠢萌娃有木有!0成本把写实风格的模型变成Q版风格。
Vertex modifier function
在surface shader中也可以加入 vetex shader.
方法就是在声明的时候添加一个字段 vertex:xxx,比如
- #pragma surface surf Lambert vertex:vert
然后定义好vert函数就可以了。vertex shading的过程会在surface函数之前进行。下面的例子是将法线渲染出来(叠加在原来的颜色上)。
- Shader "Custom/test5" {
- Properties {
- _MainTex ("Texture", 2D) = "white" {}
- }
- SubShader {
- Tags { "RenderType" = "Opaque" }
- CGPROGRAM
- #pragma surface surf Lambert vertex:vert
- struct Input {
- float2 uv_MainTex;
- float3 customColor;
- };
- void vert (inout appdata_full v, out Input o) {
- UNITY_INITIALIZE_OUTPUT(Input,o);
- o.customColor = abs(v.normal);
- }
- sampler2D _MainTex;
- void surf (Input IN, inout SurfaceOutput o) {
- o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
- o.Albedo *= IN.customColor;
- }
- ENDCG
- }
- Fallback "Diffuse"
- }
渲染结果
之前的Rim lighting在可以在vertex shading中计算。
Final Color Modifier
这个就很像fregment shader了,属于pipeline的最后一个阶段。
定义的方式和vertex 的类似,
- #pragma surface surf Lambert finalcolor:mycolor
接着定义mycolor函数就可以了。mycolor函数会在surf函数执行之后再执行。
- Shader "Custom/test6" {
- Properties {
- _MainTex ("Texture", 2D) = "white" {}
- _ColorTint ("Tint", Color) = (1.0, 0.6, 0.6, 1.0)
- }
- SubShader {
- Tags { "RenderType" = "Opaque" }
- CGPROGRAM
- #pragma surface surf Lambert finalcolor:mycolor
- struct Input {
- float2 uv_MainTex;
- };
- fixed4 _ColorTint;
- void mycolor (Input IN, SurfaceOutput o, inout fixed4 color)
- {
- color *= _ColorTint;
- }
- sampler2D _MainTex;
- void surf (Input IN, inout SurfaceOutput o) {
- o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
- }
- ENDCG
- }
- Fallback "Diffuse"
- }
渲染效果
参考
Surface Shader Examples - http://docs.unity3d.com/Manual/SL-SurfaceShaderExamples.html