您需要 登录 才可以下载或查看,没有账号?注册
x
四、写Shader实战
ps:这一篇我早就已经看完了。并且把里面的代码全部手打一遍,虽然感觉没什么卵用,但是对从没接触过编程的我还是有一些的,最起码让我知道了写代码的规矩。
下一篇我也已经看了,这个有个大神已经转过了,本身内容也是晦涩难懂,讲的是unity内置源码,
其实浅墨大神后面还有好多帖子,但是我不打算继续转下去了,因为感觉现在的学习确实如空中楼阁一般,看上去很美,但是经不起推敲。后续我会找一些巩固我基础的东西,还得找前进的方向,自己学习就是这点不好,方向不太好找,没人领路。
后面内容也许会有点碎,也许我又找到另外一个大神,说不太好。暂时很碎是肯定的,东拼西凑。哈哈,与君共勉,,,,
下面的东西也许会卖钱了啊,哈哈哈,毕竟都是我自己辛苦的找出的经验,值不值就看各人了,,,,,
下面是正文
四、写Shader实战
上文已经提到过了,: Unity在移动平台中暂时不支持延迟光照(Deferred lighting)渲染。因为延时光照不能与一些自定义per-material 光照模式很好的共同运行,所以在下面的例子中我们只在着色器正向渲染(ForwardRendering)中进行实现。
0.内置的漫反射光照
首先,我们根据上一节所学,写一个依靠内置的兰伯特光照模式的漫反射光的Surface Shader:
[cpp] view plain copy
- Shader "浅墨Shader编程/Volume7/33.内置的漫反射"
- {
- //--------------------------------【属性】----------------------------------
- Properties
- {
- _MainTex ("【主纹理】Texture", 2D) = "white" {}
- }
- //--------------------------------【子着色器】----------------------------------
- SubShader
- {
- //-----------子着色器标签----------
- Tags { "RenderType" = "Opaque" }
- //-------------------开始CG着色器编程语言段-----------------
- CGPROGRAM
- //【1】光照模式声明:使用兰伯特光照模式
- #pragma surface surf Lambert
- //【2】输入结构
- struct Input
- {
- float2 uv_MainTex;
- };
- //变量声明
- sampler2D _MainTex;
- //【3】表面着色函数的编写
- void surf (Input IN, inout SurfaceOutput o)
- {
- //从主纹理获取rgb颜色值
- o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
- }
- //-------------------结束CG着色器编程语言段------------------
- ENDCG
- }
- Fallback "Diffuse"
- }
实现效果:
1.简单的高光光照模型
下面是一个简单的高光光照模式(specular lighting model)Shader。实际上他就是Unity内置的Blinn-Phong光照模型,实际上做起来并不困难。这边我们将它单独拿出来实现一下:
[cpp] view plain copy
- Shader "浅墨Shader编程/Volume7/34.自定义高光"
- {
- //--------------------------------【属性】----------------------------------
- Properties
- {
- _MainTex ("【主纹理】Texture", 2D) = "white" {}
- }
- //--------------------------------【子着色器】----------------------------------
- SubShader
- {
- //-----------子着色器标签----------
- Tags { "RenderType" = "Opaque" }
- //-------------------开始CG着色器编程语言段-----------------
- CGPROGRAM
- //【1】光照模式声明:使用自定义的光照模式
- #pragma surface surf SimpleSpecular
- //【2】实现自定义的光照模式SimpleSpecular
- half4 LightingSimpleSpecular (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten)
- {
- half3 h = normalize (lightDir + viewDir);
- half diff = max (0, dot (s.Normal, lightDir));
- float nh = max (0, dot (s.Normal, h));
- float spec = pow (nh, 48.0);
- half4 c;
- c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * spec) * (atten * 2);
- c.a = s.Alpha;
- return c;
- }
- //【3】输入结构
- struct Input
- {
- float2 uv_MainTex;
- };
- //变量声明
- sampler2D _MainTex;
- //【4】表面着色函数的编写
- void surf (Input IN, inout SurfaceOutput o)
- {
- //从主纹理获取rgb颜色值
- o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
- }
- //-------------------结束CG着色器编程语言段------------------
- ENDCG
- }
- //“备胎”为普通漫反射
- Fallback "Diffuse"
- }
实现效果:
2.自制简单的Lambert光照
对应于Unity内建的Lambert光照,我们可以自定义原理类似的光照模式,实现自己Lambert光照:
[cpp] view plain copy
- Shader "浅墨Shader编程/Volume7/35.自制简单的Lambert光照"
- {
- //--------------------------------【属性】----------------------------------------
- Properties
- {
- _MainTex ("【主纹理】Texture", 2D) = "white" {}
- }
- //--------------------------------【子着色器】----------------------------------
- SubShader
- {
- //-----------子着色器标签----------
- Tags { "RenderType" = "Opaque" }
- //-------------------开始CG着色器编程语言段-----------------
- CGPROGRAM
- //【1】光照模式声明:使用自制的兰伯特光照模式
- #pragma surface surf QianMoLambert
- //【2】实现自定义的兰伯特光照模式
- half4 LightingQianMoLambert (SurfaceOutput s, half3 lightDir, half atten)
- {
- half NdotL =max(0, dot (s.Normal, lightDir));
- half4 color;
- color.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten * 2);
- color.a = s.Alpha;
- return color;
- }
- //【3】输入结构
- struct Input
- {
- float2 uv_MainTex;
- };
- //变量声明
- sampler2D _MainTex;
- //【4】表面着色函数的编写
- void surf (Input IN, inout SurfaceOutput o)
- {
- //从主纹理获取rgb颜色值
- o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
- }
- //-------------------结束CG着色器编程语言段------------------
- ENDCG
- }
- Fallback "Diffuse"
- }
实现效果如下:
3.自定义的半Lambert光照
接下来,让我们自制一个半Lambert光照。
Lambert定律认为,在平面某点漫反射光的光强与该反射点的法向量和入射光角度的余弦值成正比(即我们之前使用dot函数得到的结果)。Half Lambert最初是由Valve(大V社)提出来的,用于提高物体在一些光线无法照射到的区域的亮度的。
简单说来,半Lambert光照提高了漫反射光照的亮度,使得漫反射光线可以看起来照射到一个物体的各个表面。
而半Lambert最初也是被用于《半条命2》的画面渲染,为了防止某个物体的背光面丢失形状并且显得太过平面化。这个技术是完全没有基于任何物理原理的,而仅仅是一种感性的视觉增强。
遮蔽的漫反射-漫反射光照的一种改进。照明"环绕(wraps around)"在物体的边缘。它对于假冒子表面(subsurface)散射效果(scattering effect)非常有用。
半Lambert光照Shader的代码如下:
[cpp] view plain copy
- Shader "浅墨Shader编程/Volume7/36.自制半Lambert光照"
- {
- //--------------------------------【属性】----------------------------------------
- Properties
- {
- _MainTex ("【主纹理】Texture", 2D) = "white" {}
- }
- //--------------------------------【子着色器】----------------------------------
- SubShader
- {
- //-----------子着色器标签----------
- Tags { "RenderType" = "Opaque" }
- //-------------------开始CG着色器编程语言段-----------------
- CGPROGRAM
- //【1】光照模式声明:使用自制的半兰伯特光照模式
- #pragma surface surf QianMoHalfLambert
- //【2】实现自定义的半兰伯特光照模式
- half4 LightingQianMoHalfLambert (SurfaceOutput s, half3 lightDir, half atten)
- {
- half NdotL =max(0, dot (s.Normal, lightDir));
- //在兰伯特光照的基础上加上这句,增加光强
- float hLambert = NdotL * 0.5 + 0.5;
- half4 color;
- //修改这句中的相关参数
- color.rgb = s.Albedo * _LightColor0.rgb * (hLambert * atten * 2);
- color.a = s.Alpha;
- return color;
- }
- //【3】输入结构
- struct Input
- {
- float2 uv_MainTex;
- };
- //变量声明
- sampler2D _MainTex;
- //【4】表面着色函数的编写
- void surf (Input IN, inout SurfaceOutput o)
- {
- //从主纹理获取rgb颜色值
- o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
- }
- //-------------------结束CG着色器编程语言段------------------
- ENDCG
- }
- Fallback "Diffuse"
- }
实现效果如下:
4.自定义卡通渐变光照
下面,我们一起实现一个自定义卡通渐变光照,通过一个不同的渐变纹理(渐变纹理可由PS制作),实现各种不同的渐变效果。
自定义卡通渐变光照Shader代码如下:
[cpp] view plain copy
- Shader "浅墨Shader编程/Volume7/37.自定义卡通渐变光照"
- {
- //--------------------------------【属性】----------------------------------------
- Properties
- {
- _MainTex ("【主纹理】Texture", 2D) = "white" {}
- _Ramp ("【渐变纹理】Shading Ramp", 2D) = "gray" {}
- }
- //--------------------------------【子着色器】----------------------------------
- SubShader
- {
- //-----------子着色器标签----------
- Tags { "RenderType" = "Opaque" }
- //-------------------开始CG着色器编程语言段-----------------
- CGPROGRAM
- //【1】光照模式声明:使用自制的卡通渐变光照模式
- #pragma surface surf Ramp
- //变量声明
- sampler2D _Ramp;
- //【2】实现自制的卡通渐变光照模式
- half4 LightingRamp (SurfaceOutput s, half3 lightDir, half atten)
- {
- //点乘反射光线法线和光线方向
- half NdotL = dot (s.Normal, lightDir);
- //增强光强
- half diff = NdotL * 0.5 + 0.5;
- //从纹理中定义渐变效果
- half3 ramp = tex2D (_Ramp, float2(diff,diff)).rgb;
- //计算出最终结果
- half4 color;
- color.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2);
- color.a = s.Alpha;
- return color;
- }
- //【3】输入结构
- struct Input
- {
- float2 uv_MainTex;
- };
- //变量声明
- sampler2D _MainTex;
- //【4】表面着色函数的编写
- void surf (Input IN, inout SurfaceOutput o)
- {
- //从主纹理获取rgb颜色值
- o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
- }
- //-------------------结束CG着色器编程语言段------------------
- ENDCG
- }
- Fallback "Diffuse"
- }
我们取不同的渐变纹理,可得到不同的效果。以下是五种不同渐变纹理和对应的效果图。
第一组:
第二组:
第三组:
第四组:
第五组:
5.自定义卡通渐变光照v2
让我们在上面这个Shader的基础上,加入更多可选的属性,成为一个功能完备的渐变光照Shader:
[cpp] view plain copy
- Shader "浅墨Shader编程/Volume7/38.自定义卡通渐变光照v2"
- {
- //--------------------------------【属性】----------------------------------------
- Properties
- {
- _MainTex ("【主纹理】Texture", 2D) = "white" {}
- _Ramp ("【渐变纹理】Ramp Texture", 2D) = "white"{}
- _BumpMap ("【凹凸纹理】Bumpmap", 2D) = "bump" {}
- _Detail ("【细节纹理】Detail", 2D) = "gray" {}
- _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" }
- LOD 200
- //-------------------开始CG着色器编程语言段-----------------
- CGPROGRAM
- //【1】光照模式声明:使用自制的卡通渐变光照模式
- #pragma surface surf QianMoCartoonShader
- //变量声明
- sampler2D _MainTex;
- sampler2D _Ramp;
- sampler2D _BumpMap;
- sampler2D _Detail;
- float4 _RimColor;
- float _RimPower;
- //【2】实现自制的卡通渐变光照模式
- inline float4 LightingQianMoCartoonShader(SurfaceOutput s, fixed3 lightDir, fixed atten)
- {
- //点乘反射光线法线和光线方向
- half NdotL = dot (s.Normal, lightDir);
- //增强光强
- half diff = NdotL * 0.5 + 0.5;
- //从纹理中定义渐变效果
- half3 ramp = tex2D (_Ramp, float2(diff,diff)).rgb;
- //计算出最终结果
- half4 color;
- color.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2);
- color.a = s.Alpha;
- return color;
- }
- //【3】输入结构
- struct Input
- {
- //主纹理的uv值
- float2 uv_MainTex;
- //凹凸纹理的uv值
- float2 uv_BumpMap;
- //细节纹理的uv值
- float2 uv_Detail;
- //当前坐标的视角方向
- float3 viewDir;
- };
- //【4】表面着色函数的编写
- void surf (Input IN, inout SurfaceOutput o)
- {
- //先从主纹理获取rgb颜色值
- o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
- //设置细节纹理
- o.Albedo *= tex2D (_Detail, IN.uv_Detail).rgb * 2;
- //从凹凸纹理获取法线值
- o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
- //从_RimColor参数获取自发光颜色
- half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
- o.Emission = _RimColor.rgb * pow (rim, _RimPower);
- }
- //-------------------结束CG着色器编程语言段------------------
- ENDCG
- }
- FallBack "Diffuse"
- }
我们将此Shader编译后赋给材质,得到如下效果:
可供调节的属性非常多,稍微放几张效果图,剩下的大家可以下载工程源代码,或者拷贝Shader代码,自己回去调着玩吧~
布料细节纹理+灰白渐变纹理+红色边缘光:
布料细节纹理+灰白渐变纹理+浅绿色边缘光:
布料细节纹理+灰白渐变纹理+白色边缘光:
布料细节纹理+灰白渐变纹理+无边缘光(黑色):
五、场景搭建
以大师级美工鬼斧神工的场景作品为基础,浅墨调整了场景布局,加入了音乐,并加入了更多高级特效,于是便得到了如此这次比较唯美的静谧之秋场景。
运行游戏,树影摇曳,我们来到金黄色的丰收之秋。
最后,放一张本篇文章中实现的Shader全家福:
OK,美图就放这么多。游戏场景可运行的exe可以在文章开头中提供的链接下载。而以下是源工程的下载链接。
本篇文章的示例程序源工程请点击此处下载:
【浅墨Unity3D Shader编程】之七 静谧之秋篇配套Unity工程下载
Unity Shader系列文章到目前已经更新了7篇,一共实现了38个详细注释、循序渐进、功能各异的Shader,对Unity中的固定功能Shader、Surface Shader都已经有了比较详细、系统的讲解和实现。而可编程Shader按学习计划来说是以后的计划,目前还是未涉及,有机会在以后的文章中一起和大家一起探讨。
而大家如果仔细参考和阅读这七篇文章,会发现Unity中Shader的书写其实是非常容易和有章可循的。这大概就是浅墨写这个系列文章的初衷吧。
天下没有不散的宴席。
浅墨接下来的一段时间有一些大的游戏项目要接触,所以,Unity Shader系列文章每周周一的固定更新只能改为不定期的更新了。以后浅墨有了空余时间,会继续写博文来与大家交流分享。
OK,于未来某天更新的下篇文章中,我们后会有期。:)
|