本人新人一枚,想要将自己所学分享给大家,如有错误或不足请大家毫不犹豫的指出,谢谢大家的支持!!!那么开始吧:
一个物体我们能看见是通过被光线照亮后经过反射进入我们的眼睛后大脑成像。计算机(GPU)就是计算进入眼睛之前的各个步骤得到的从而给我们反馈一个结果。
计算公式:SurfaceColor=emissive+ambient+diffuse+specular+...;
SurfaceColor-物体最终渲染结果;
emissive-放射;
ambient-环境放射;
diffuse-漫反射;
specular-镜面反射;
...-其他数据或自定义数据;
注:一个简单的光照渲染中emissive(放射)和specular(镜面反射)和...(其他数据或自定义数据)不是必须的。
上面的公式看着复杂,其实都是一些很简单的运算,接下来我们一个个的分析探讨:
1>emissive 放射
放射简单来讲就是物体发光,可以当做自发光,比如灯泡之类的。
emissive=Ka ——ka表示材质的放射光颜色
2>ambient 环境反射
环境反射简单讲就是反射的从四面八方来的环境中的光
ambient=Kb*globleAmbient——Kb是材质环境反射系数,globleAmbient是入射环境光颜色。
3>diffuse 漫反射
漫反射就是对于表面凹凸不平的物体对于光的反射,比如沥青路面,黑板表面等等。
diffuse=Kc*lightColor*max(N*L,0)——Kc是材质漫反射颜色,lightColor是入射光的颜色,N是归一化的表面法向量,L是归一化的指向光源的向量。其中N*L是N向量点乘L向量。
注:归一化是将向量变成单位向量,也就是说此向量我们不需要其大小仅仅需要其方向进行计算。归一化也有叫规范化。
4>specular 镜面反射
这个好理解,就是类似于镜子中的光反射,但是看到镜子中物体到底有什么取决于观察者的位置,位置不同看到镜子中物体也不同,可以简单理解为美术中的高光。多用于光泽度比较大的物体,例如抛光金属等。
specular=Kd*lightColor*facing*(max(N*H),0)^Mglass——Kd是材质的镜面反射颜色,lightColor是入射镜面反射光颜色,N是归一化法向量,V是指向视点的归一化向量,L是指向光源的归一化向量,H是V向量和L向量的中间向量,Mglass是材质的光泽度。其中N*H是N向量点乘H向量。
好了基本原理讲解完了剩下就是码cg代码了;
cg语言中有两种着色器,顶点着色器和片元着色器。顶点和片元着色器中的代码搞明白其中一个就行,另一个就是粘贴复制,一样的原理,两者就是着色效果会有一点点区别,一般如果物体顶点少的话用顶点着色器会看出物体的三角面,采用片元着色器会好一点。顶点越多线性插值越丰富颜色过度越好,但相对消耗性能。
代码基于Unity3d 2017.3.of3 版本渲染:
Shader "Custom/DiffuseVertexLevelMat"//逐顶点光照
{
Properties
{
_DiffuseColor("DiffuseColor",Color)=(1,1,1,1)
_DiffuseValueRange("_DiffuseValueRange",Range(-5,5))=0.0
_SpecularColor("SpecularColor",Color)= (1,1,1,1)
_Gloss("GlossValue",Range(0.0,256))=20
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Tags{ "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed3 normal : NORMAL;
fixed3 color : COLOR;
};
fixed4 _DiffuseColor;
float _DiffuseValueRange;
fixed4 _SpecularColor;
float _Gloss;
v2f vert (appdata v)
{
v2f o;
//o.vertex = mul(UNITY_MARIX_MVP,v.vertex);//这种写法和下面的写法效果和功能一样,下面的UnityObjectToClipPos()是对mul()的封装
o.vertex = UnityObjectToClipPos(v.vertex);
/*
*计算环境反射
*/
o.normal= (fixed3)normalize(mul(v.normal, (float3x3)unity_WorldToObject));
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;//环境反射
/*
*计算漫反射
*/
fixed3 worldNormal =(fixed3) normalize(mul(v.normal, (float3x3)unity_WorldToObject));//世界坐标系下的法线
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);//世界坐标系下的灯光向量
fixed3 diffuse = _LightColor0.rgb*_DiffuseColor.rgb*max(dot(worldNormal, worldLight),_DiffuseValueRange);//漫发射,其中_DiffuseValueRange可以替换成0
/*
*计算镜面反射
*/
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz-mul(unity_ObjectToWorld,v.vertex).xyz);
fixed3 reflectDir = normalize(reflect(-worldLight,worldNormal));
fixed3 specular = _LightColor0.rgb*_SpecularColor.rgb*pow(saturate(dot(reflectDir, viewDir)), _Gloss);//镜面反射
o.color = ambient + diffuse+ specular;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 color = (fixed4)(i.color,1.0);
return fixed4(i.color, 1.0);
}
ENDCG
}
}
}
注:如果这里添加自发光只是会显示颜色,并不会真正意义上变亮,只是简单地光照渲染所以没有给出。
参考文献:Cg教程——可编程实时图形权威指南 Randima Fernando&Mark J.Kilgard 人民邮电出版社 2004年9月第1版
unityShader入门精要 冯乐乐 人民邮电出版社 2016年6月第1版