Unity shader学习笔记(三)光照模型
发表于2018-04-28
光照模型
光照模型是一个公式,使用这个公式计算物体某个坐标点上的光照效果。
光照模型有很多种,例如:标准光照模型,半兰伯特光照模型,Blinn光照模型,Phong光照模型和Blinn-Phong光照模型
标准光照模型里面,会进入到视野中的的光主要有下面四种光
1.自发光 emissive(像火把)
2.高光反射 specular(像镜子的光反射)
3.漫反射 diffuse(像木头的光反射)
4.环境光 ambient
光照效果可以放在顶点函数里面处理,即逐顶点关照处理,也可以放在片元函数中处理,叫做逐片元(像素)光照处理
所有的物体光照计算都会与物体的发现相关,法线是指始终垂直于某平面的虚线或者某曲线上一点的切线的直线
法线如图:
漫反射光的计算方式:
直射光颜色 * max(0,cos(x))(x代表光和法线的夹角)
max的作用是使得光与物体法线夹角的值始终大于0,0的时候物体表面颜色即为黑色。
向量的夹角的cos值也可以通过向量进行点积得到 所以计算方式也可以写成 直射光颜色 * max(0,表面法线方向单位向量*直射光方向单位向量)
SubShader{ Pass{ Tags{"LightMode" = "ForwardBase"} CGPROGRAM #include "Lighting.cginc" //unity系统定义好的类 可以使用一些内置的变量来简化计算操作 //_LightColor0 该变量表示第一个直射光的颜色 //_WorldSpaceLightPos0 该变量表示第一个直射光的位置(世界坐标下) //UNITY_LIGHTMODEL_AMBIENT 该变量表示系统的环境光 #pragma vertex vert #pragma fragment frag struct a2v { float4 vertex : POSITION; //系统自动将顶点坐标传入vertex float3 normal : NORMAL; //系统自动将法线传入 变量normal }; struct v2f { float4 position:SV_POSITION; fixed3 color : COLOR; }; v2f vert(a2v x) { v2f f; //UNITY_MATRIX_MVP 这个矩阵用来把一个坐标从模型空间转换到剪裁空间 f.position = mul(UNITY_MATRIX_MVP, x.vertex); //对于每个顶点来说,光的位置其实就是光的方向 //normalize 该方法是用来将一个向量转换成单位向量 当前直射光的空间是在世界空间 fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb; //目前的normal变量还是模型空间下的坐标。需要将其转换为和直射光相同空间下的坐标 //_World2Object 这个矩阵用来把一个方向从世界空间转换到模型空间 方法为mul(_World2Object,x.vertex) //如果方法写成 mul(x.vertex,_World2Object) 则将一个方向从模型空间转换到世界空间 //_World2Object是一个4x4的矩阵,但是当前我们需要使用的是3x3的矩阵,因此使用float3x3来对矩阵进行强制转换 fixed3 normalDir = normalize(mul(x.normal, (float3x3)unity_WorldToObject)); //暂时没有考虑alpha属性 //max 该方法是用来取得参数中最大的一个 //dot 该方法是用来获取两个向量的点积 fixed3 diffuse = _LightColor0.rgb*max(dot(lightDir, normalDir),0)*_Diffuse.rgb;//取得漫反射的颜色 f.color = diffuse; return f;//系统会自动根据结构体中变量的语义来进行取值 } fixed4 frag(v2f f) :SV_TARGET{ //将计算得到的颜色值输出到视野中的物体上 return fixed4(f.color, 1); } ENDCG } }
如果想在inspector面板进行调控
那么在属性中定义变量
Properties{ _Diffuse("Diffuse Color",Color) = (1,1,1,1) }
在pass中重新声明变量
fixed4 _Diffuse;
最后应用的结果如图
unity中两种颜色叠加 如果是光亮进行融合的话 直接两种颜色相乘即可
颜色亮度基本无变化
只要将
fixed3 diffuse = _LightColor0.rgb*max(dot(lightDir, normalDir),0);更改为fixed3 diffuse = _LightColor0.rgb*max(dot(lightDir,ormalDir),0)*_Diffuse.rgb; 即可
如果是两种光互相叠加 那么将两种颜色相加获得新的颜色 会使得颜色亮度增加 相加过程中颜色值会趋近于(1,1,1,1)
例如:如果加上环境光的话f.color = diffuse; 更改为 f.color = diffuse + ambient;
上面的渲染方法是逐顶点渲染,如果要将代码修改为逐像素渲染,那么只需要将计算方法的位置进行修改。
代码如下:
SubShader{ Pass{ Tags{"LightMode" = "ForwardBase"} CGPROGRAM #include "Lighting.cginc" //unity系统定义好的类 可以使用一些内置的变量来简化计算操作 //_LightColor0 该变量表示第一个直射光的颜色 //_WorldSpaceLightPos0 该变量表示第一个直射光的位置(世界坐标下) //UNITY_LIGHTMODEL_AMBIENT 该变量表示系统的环境光 #pragma vertex vert #pragma fragment frag fixed4 _Diffuse; struct a2v { float4 vertex : POSITION; //系统自动将顶点坐标传入vertex float3 normal : NORMAL; //系统自动将法线传入 变量normal }; struct v2f { float4 position:SV_POSITION; fixed3 worldNormalDir : COLOR; }; v2f vert(a2v x) { v2f f; //UNITY_MATRIX_MVP 这个矩阵用来把一个坐标从模型空间转换到剪裁空间 f.position = mul(UNITY_MATRIX_MVP, x.vertex); //法线的计算仍然在顶点函数中计算 ,将结果传递给片元函数 f.worldNormalDir = normalize(mul(x.normal, (float3x3)unity_WorldToObject)); //系统会自动根据结构体中变量的语义来进行取值 return f; } fixed4 frag(v2f f) :SV_TARGET{ fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb; fixed3 normalDir = f.worldNormalDir; //暂时没有考虑alpha属性 fixed3 diffuse = _LightColor0.rgb*max(dot(lightDir, normalDir),0)*_Diffuse.rgb; fixed3 tempColor = diffuse + ambient; //将计算得到的颜色值输出到视野中的物体上 return fixed4(tempColor, 1); } ENDCG } }
最后的效果如图
但是我们会发现物体的背光面是个全黑的颜色,现实生活中物体的背光面并不是全黑的,而是可以看到物体的大概形状。要实现此种现象,那么就需要别的光照模型来实现。
来自:https://blog.csdn.net/u013106366/article/details/53313141