(此文为易于理解的演讲配合文档,设计到专业词汇和解释可能不当)
1.光源发射光
2.物体与光交互发生“散射”以及“吸收”两个结果
散射:不改变光本身的物理属性(颜色,密度等),只改变光的方向。但是散射根据最终结果的不同方向又分为两类,到物体内部,这种现象称之为“折射”或“透射”,另一种方向为为散射到物体外部,即反射。
吸收:只改变光的本身物理属性,不改变方向。光被吸收后通常转为其他形式的能量,比如热。
下图反应了实际光与物体表面交互的过程和结果,对于不透明物体,折射到内部的光线还会继续交互,最终部分光线重新从表面出射。
3.最终离开物的光到部分到达摄像机,摄像机形成影像
光照模型概念:即根据物体表面属性,光源信息,使用某一个数学公式计算,最终得到沿某个观察方向出射的光的集合。
标准光照模型最终到传到摄像机的光线,分为自发光,高光反射,漫反射,环境光四个部分即
漫反射(diffuse)即光经过表面交互(折射吸收等)后,会向每个方向出射多少量的光,因此在任意方向观察,漫反射结果都是一样的,计算公式(由符合兰伯特定律推出):出射光线强度与表面法线和光源方向之间夹角成正比,即
其中,n是法线方向,I是指向光源的单位矢量,
此外,还有一种计算公式,即半兰伯特模型,通过缩放和偏移映射原来的值域到新值域,例如 [-1,1] 映射到 [0,1],这种模型计算的漫反射看起来整体更亮,有些领域也在使用,如下图效果对比
计算公式为:
高光反射用于计算光跟表面交互后,会在完全镜面反射方向出射多少光,这些镜面反射的光投影到我们的观察方向,也就形成了高光区域
这一部分计算结果使用以下公式(Phong模型)
r=2(ncdot I)n-I
其中
两者都是经验模型,并不是正确的,他们分别在某些情况计算效率会高于另有一种,对比效果如下,Blinn-Phong计算出来的高光区域更大,边缘更硬一些
自发光描述了物体本身会向观察方向发射什么样的光,如果没有全局关照技术,这些自发光并不会真正照亮周围物体,而只是本身看起来更亮了
描述其他所有的间接光照
unity内置渲染流程中提供了两个地方进行自定义计算,即顶点着色器,片元着色器
顶点:逐定点计算,模型有n个顶点则计算n次
片元:逐像素计算,整个模型画到屏幕上时如果占了m个像素,则计算m次,一般情况m远大于n
顶点着色器中的数据,会经过插值得到到偏远着色器中使用的数据,如两个顶点颜色分别为白色(1,1,1)和黑色(0,0,0),到了片元着色器里边对应的一系列像素颜色是(1,1,1)(0.9,0.9,0.9)(0.8,0,0.8,)...(0,0,0)。着色计算即可以在定点中着色阶段进行,也可以在片元着色阶段进行,在前者进行更快但是可能由于插值而出现明显边际,在后者计算量大但是效果最好。
这里只展示主要在片元着色器中进行计算
环境光:Unityshader中可以直接使用系统定义的“UNITY_LIGHTMODEL_AMBIENT”获得环境光
自发光:大多数物体并没有自发光,如许添加,只要在最后的运算结果加上自定义的自发光颜色即可
漫反射效果:
UnityShader逐像素计算面反射代码段
v2f vert(a2v v){
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
//保存世界空间的法线
o.WorldNormal=UnityObjectToWorldNormal(v.normal);
return o;
}
fixed4 frag(v2f i):SV_TARGET{
//环境光
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
//光源方向
fixed3 WorldSpaceLightDir=normalize(_WorldSpaceLightPos0.xyz);
//diffuse=C(light) * m(diffuse) * max(0,I.n)
fixed3 diffuse=_LightColor0.xyz * _Diffuse.xyz *saturate(dot(WorldSpaceLightDir,i.WorldNormal))
return fixed4(diffuse,1);
}
UnityASE编辑器节点
漫反射的半兰伯特模型
shader修改只要修改最后部分
//兰伯特diffuse=C(light) * m(diffuse) * max(0,I.n)
//fixed3 diffuse=_LightColor0.xyz * _Diffuse.xyz * saturate(dot(WorldSpaceLightDir,i.WorldNormal));
//半兰伯特diffuse=C(light) * m(diffuse) * (,0.5*I.n+0.5)
fixed3 diffuse=_LightColor0.xyz * _Diffuse.xyz * (0.5h* dot(WorldSpaceLightDir,i.WorldNormal)+0.5h);
ASE编辑器也只需按公式做修改即可
高光反射
Unity效果(Phong模型):
Unityshader代码片段:
v2f vert(a2v i){
v2f o;
o.pos=UnityObjectToClipPos(i.vertex);
//保存世界法线
o.wNormal=UnityObjectToWorldNormal(i.normal);
//保存世界位置
o.wPos=mul((float3x3)unity_ObjectToWorld,i.vertex);
return o;
}
fixed4 frag(v2f i):SV_TARGET{
fixed4 finialColor;
finialColor.w=1;
//光源方向
fixed3 wLightDir=normalize(_WorldSpaceLightPos0.xyz);
//环境光
fixed3 GI=UNITY_LIGHTMODEL_AMBIENT.xyz;
//漫反射
fixed3 diffuse=_LightColor0.xyz*_Diffuse.xyz*saturate(dot(i.wNormal,wLightDir));
//反射方向
fixed3 refDic=normalize(reflect(-wLightDir,i.wNormal));
//观察方向
fixed3 vDic=normalize(_WorldSpaceCameraPos.xyz-i.wPos);
//高光=C(light) * m(Specular) * max(0,r.v)^m(gloss)
fixed3 specular=_LightColor0.xyz*_Specular.xyz*pow(saturate(dot(refDic,vDic)),_Gloss);
finialColor.xyz=diffuse+GI+specular;
return finialColor;
}
UnityASE编辑器节点
优化的高光计算模型(Blinn-Phong模型)
代码修改片段:
fixed3 vDic=normalize(UnityWorldSpaceViewDir(i.wPos));
fixed3 hlfVerc=normalize(wLightDir+vDic);
fixed3 specular=_LightColor0.xyz*_Specular.xyz*pow(saturate(dot(i.wNormal,hlfVerc)),_Gloss);
ASE编辑器修改,代替原点乘部分即可
参考文档:
《UnityShader入门精要》