关于Unity Shader的学习笔记(十一)

本文详细介绍了在Unity中实现逐像素光照和高光反射的过程,包括代码修改和效果对比,探讨了不同光照模型如半兰伯特、片元高光及Blinn光照模型的实现与视觉效果。
在上一篇笔记中我们完成了逐顶点光照的效果,接下来完成逐像素光照,代码部分改动不大,只是将漫反射计算部分从顶点函数移动到片元函数中。新建一个StandardSurface shader,打开清空从Properties开始的代码,之后将上一个Shader的从Properties开始的内容全部复制进来,这里要修改顶点着色器的输出结构体v2f: struct v2f { float4 pos : SV_POSITION; float3 worldNormal:TEXCOORD0; };
顶点函数部分不需要计算光照模型,只需要将世界空间下的法线传递给片元着色器即可: v2f vert (a2v v){ v2f o; o.pos=UnityObjectToClipPos(v.vertex); o.worldNormal=mul(v.normal,(float3x3)unity_WorldToObject); return o; } 片元函数需要计算光照: fixed4 frag(v2f i):SV_Target { fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldNormal=normalize(i.worldNormal); fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz); fixed3 diffuse=_LightColor0.rgb_Diffuse.rgbsaturate(dot(worldNormal,worldLightDir)); fixed3 color=ambient+diffuse; return fixed4(color,1.0); } 完整代码如下: 之后在Unity场景中新建一个材质,新建一个胶囊体,将写好的shader拖给材质,材质拖给胶囊体,可以看到如下效果,发现明暗交界线部分比较流畅: 可以对比发现逐像素光照比逐顶点光照的效果更加平滑,但是这样还是能发现问题,暗面没有层次感,没有渐变效果,学美术的肯定不能忍了。针对于这样的需求改进,半兰伯特光照模型被提了出来。 Valve公司在开发游戏《半条命》时提出了一种技术,由于该技术是在漫反射光照模型也就是兰伯特光照模型的基础上进行的一个简单修改,因此被称为半兰伯特光照模型。 半兰伯特光照模型的计算公式如下: diffuse=(c*m)(α(n·I)+β);相比于之前的漫反射光照计算公式可以发现没有使用max操作来防止物体法线与光方向矢量的点积为负值,而是将点积结果进行了α倍的缩放之后再加上β大小的偏移。大多数情况下α与β的值为0.5,半兰伯特是没有任何物理依据的,只是一个视觉加强技术。突然想起书上说的图形学第一定律:如果它看起来是对的,那么它就是对的。呵呵... 了解了半兰伯特光照模型的计算公式后,我们将上面写的逐像素光照的代码改写一下,只需更改片元函数部分,完整代码如下: 可以发现胶囊体的光影效果更加微妙了,似乎有点白皙通透的感觉,我们截图对比一下三个效果下的胶囊体:
漫反射光照部分梳理完后,我们接着梳理高光反射光照模型,高光反射部分的计算公式如下: specular=(c*m)max(0,v·r)^m.gloss; 其中,c为入射光线的颜色和强度,m为材质的高光反射系数,^代表次幂,m.gloss代表材质光泽度,v为视角方向,r为反射方向。反射方向r可以由表面法线n和光源方向I计算得出,在CG中提供了计算反射方向的函数reflect(i,n);i代表入射方向,n代表法线方向,另外CG中也提供了幂函数pow(x,y);
在了解了高光反射计算公式后,我们来动手写写代码感受一下。首先我们还是在顶点函数中计算高光,在上一个Unity工程中,新建一个材质,一个Standard Surface Shader,一个球体。将shader拖给材质,材质拖给球体,打开shader,之后在Properties块中声明三个属性: Properties { _Diffuse("Diffuse",Color)=(1,1,1,1) //固有色 _Specular("Specular",Color)=(1,1,1,1) //控制高光反射颜色 _Gloss("Gloss",Range(8.0,256))=20 //控制高光区域大小 }
之后在SubShader块中写入Pass块,之后定义LightMode标签为ForwardBase,随后以CGPROGRAM,ENDCG作为开始和结束部分以开始CG代码部分。用#pragma分别定义顶点和片元函数,另外将Properties块中的三个属性用CG属性重新声明,之后定义顶点函数输入a2v、输出v2f结构体: SubShader { Pass { Tags{"LightMode"="ForwardBase"} CGPROGRAM
#pragma vertex vert #pragma fragment frag #include "Lighting.cginc" fixed4 _Diffuse; fixed4 _Specular; float _Gloss; struct a2v { float4 vertex : POSITION; float4 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; fixed3 color : COLOR; }; ENDCG } }
之后开始顶点函数计算部分,看过上一篇计算漫反射部分笔记后应该对这个计算部分不难理解,只是根据计算公式替换了一些变量,这里主要用到了CG中的reflect函数和pow函数: 最后的片元函数直接返回顶点函数计算的结果即可: fixed4 frag(v2f i):SV_Target { return fixed4(i.color,1.0); } 完整代码如下: 最后小球的效果: 可以看到高光部分并不是那么让人满意,很明显的像素块感,接下来我们在片元函数中计算高光,看看效果,代码改动不大,就是将计算高光的部分移到片元函数中,完整代码如下: 最后小球的高光效果: 还有一种Blinn光照模型,是对片元高光计算模型的加强版,其没有使用反射方向,是新引入了一个矢量,这个矢量是通过对视角方向和光照方向相加后再归一化得到的,听文字解释就知道是加强版,相较于片元高光计算代码改动不大,完整代码如下: 小球的高光效果: 可以看出,的确比前两种效果要更强一些。下面为三种高光效果对比图: 未完待续
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值