赞
踩
光线从光源发射出来,通常把光源当作一个没有体积的点,用l表示方向,用辐照度量化光,辐照度与cosθ成正比,cosθ可以通过光源方向l与表面发现n的点积来得到(games101中有详细解释光照模型)。
散射只改变光的方向,不改变光线的密度与颜色,而吸收只改变光线的密度与颜色。散射到物体内部:折射或投射;散射到物体外部:反射。
为了区分不同的反射方向,高光反射表示物体表面如何反射光线,漫反射表示多少光线会被折射、吸收、散射出表面。出射度描述出射光线的数量与方向,辐照度和出射度满足线性关系。
着色指的是根据材质属性(如漫反射属性)、光源信息、计算某个观察方向出射度的过程。
自发光:描述当给定一个方向时,一个表面本身会向该方向发射多少辐射量,如果没有使用全局光照,自发光的表面并不会真的照亮周围的物体。
高光反射:描述当光线从光源照射到模型表面时,该表面会在完全镜面反射方向散射多少辐射量。
漫反射:用于描述当光线从光源照射到漫反射模型表面时,该表面会向每个方向散射多少辐射量。
环境光:描述其他所有的间接光照。
布林冯模型此处不详细记录,后续上传games101笔记时有详细解释。Blinn-Phong模型是各向同性的,而有些物体表面是各向异性反射材质的,如拉丝金属毛发等,需要基于物理的光照模型。
在片元着色器中计算,以每个像素为基础得到他的法线,然后进行光照计算,在面片间对顶点发现插值得技术称为Phong着色。
Gouraud着色,在顶点着色器中计算,依赖线性插值来得到像素光照,因此当光照模型中有非线性的计算会出现问题。
计算漫反射需要知道四个参数:入射光线的颜色与强度Clight,材质的漫反射系数mdiffuse,表面法线n以及光源方向l。
为了防止点积结果为负值,需要进行max操作,CG的函数saturate(x)提供了这个功能,用于将x截取在[0,1]范围内,如果x是矢量,会对每一个分量进行同样的操作。
代码如下,说明在注释中
- // Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
- // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
-
- Shader "Shaders/DiffuseVertexLevel"
- {
- Properties
- {
- _Diffuse("Diffuse",Color) = (1,1,1,1) //得到并控制材质的漫反射颜色 初始值为白色
- }
- SubShader
- {
- Pass
- {
- Tags{"LigtheMode" = "ForwardBase"}//定义该pass在unity光照流水线中的角色
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #include "Lighting.cginc"
- fixed4 _Diffuse;//使用properties中声明的属性
- struct a2v {
- float4 vertex :POSITION;
- float3 normal:Normal;//获取法线(位于模型空间下)后续使用需要转换到世界空间
- };
- struct v2f {
- float4 pos :SV_POSITION;
- fixed3 color : COLOR;
- };
- v2f vert(a2v v){
- v2f o;//定义返回值
- o.pos=UnityObjectToClipPos(v.vertex);//顶点着色器的功能,将顶点坐标从模型空间转换到裁剪空间
- fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;//获得环境光
- fixed3 worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));//得到模型空间到世界空间的变换矩阵的逆矩阵_World2Object,调换他在
- //mul函数中的位置,得到和转置矩阵相同的矩阵乘法,由于法线是三维向量,只需要截取_World2Object的前三行三列
- fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);//要知道光源的颜色与强度信息及光源方向, 可以以通过内置变量——LightColor()得到,前提是定义了
- //合适的lightmode,光源方向可以有_WorldSpaceLightPos0得到(单一光源平行光)
- //计算光源方向和法线方向的点积的时候,只有两者处于同一坐标空间才有意义,这里选择世界坐标
- fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLight));
- o.color=ambient+diffuse;
- return o;
- }
- fixed4 frag(v2f i):SV_Target{
- return fixed4(i.color,1.0);//所有的计算在顶点着色器中完成了,片元着色器中只需将顶点颜色输出
- }
- ENDCG
- }
- }
- FallBack "Diffuse"
- }
代码如下,逐像素光照可以得到更加平滑的光照效果,但是在光照无法到达的区域,模型的外观通常是全黑的,没有明暗变化,像一个平面一样失去模型细节表现,改善方法--半兰伯特(half-lambert)光照模型。
- // Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
-
- // Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
- // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
-
- Shader "Shaders/DiffuseVertexLevel"
- {
- Properties
- {
- _Diffuse("Diffuse",Color) = (1,1,1,1) //得到并控制材质的漫反射颜色 初始值为白色
- }
- SubShader
- {
- Pass
- {
- Tags{"LigtheMode" = "ForwardBase"}//定义该pass在unity光照流水线中的角色
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #include "Lighting.cginc"
- fixed4 _Diffuse;//使用properties中声明的属性
- struct a2v {
- float4 vertex :POSITION;
- float3 normal:Normal;//获取法线(位于模型空间下)后续使用需要转换到世界空间
- };
- 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 worldLight=normalize(_WorldSpaceLightPos0.xyz);
- fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLight));
- fixed3 color=ambient+diffuse;
- return fixed4(color,1.0);
- }
- ENDCG
- }
- }
- FallBack "Diffuse"
- }
在前面使用的漫反射光照模型为兰伯特光照模型,为了解决光照无法到达区域模型外观为全黑无明暗变化的问题,对公式进行修正,没有用max操作来防止法线n与光源方向I的点积为负值,而是用α倍的缩放加上β大小的偏移,绝大多数下用0.5 。没有物理依据,仅仅是视觉加强技术。
- 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 worldLight=normalize(_WorldSpaceLightPos0.xyz);
- fixed3 halfLambert=dot(worldNormal,worldLight)*0.5+0.5;
- fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*halfLambert;
- fixed3 color=ambient+diffuse;
- return fixed4(color,1.0);
- }
计算高光反射需要知道四个参数:入射光线颜色和强度Clight,材质的高光反射系数,视角的方向与反射方向,反射方向可以有法线与光源方向计算得到。CG提供函数reflect(i,n),给定入射方向i与法线方向n时,返回反射方向。Gloss越大,高光范围越小。
- // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
- // Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
- // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
-
- Shader "Shaders/SpecularVertexLevel"
- {
- Properties
- {
- _Diffuse("Diffuse",Color)=(1,1,1,1)
- _Specular("Specular",Color)=(1,1,1,1) //控制高光反射颜色
- _Gloss("Gloss",Range(8.0,256))=20 //控制高光区域的大小
- }
- SubShader
- {
- Pass
- {
- Tags { "RenderType"="ForwardBase" }
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- // make fog work
- #include "Lighting.cginc"
-
- fixed4 _Diffuse;
- fixed4 _Specular;
- float _Gloss;
-
- struct a2v{
- float4 vertex :POSITION;
- float3 normal: NORMAL;
- };
-
- struct v2f{
- float4 pos:SV_POSITION;
- fixed3 color:COLOR;
- };
- v2f vert(a2v v ){
- v2f o;
- o.pos =UnityObjectToClipPos(v.vertex);
- fixed3 ambient =UNITY_LIGHTMODEL_AMBIENT.xyz;
- fixed3 worldNormal =normalize(mul(v.normal,(float3x3)unity_WorldToObject));
- fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);
- fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLightDir));
- fixed3 reflectDir = normalize(reflect(-worldLightDir,worldNormal));
- fixed3 viewDir =normalize(_WorldSpaceCameraPos.xyz-mul(unity_ObjectToWorld,v.vertex).xyz);
- fixed3 specular=_LightColor0.rgb*_Specular.rgb*pow(saturate(dot(reflectDir,viewDir)),_Gloss);
- o.color =ambient+diffuse+specular;
- return o;
- };
- fixed4 frag(v2f i):SV_Target {
- return fixed4 (i.color,1.0);
- }
-
-
- ENDCG
- }
- }
- FallBack "Diffuse"
- }
- // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
-
- // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
- // Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
- // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
-
- Shader "Shaders/SpecularPixelLevel"
- {
- Properties
- {
- _Diffuse("Diffuse",Color)=(1,1,1,1)
- _Specular("Specular",Color)=(1,1,1,1) //控制高光反射颜色
- _Gloss("Gloss",Range(8.0,256))=20 //控制高光区域的大小
- }
- SubShader
- {
- Pass
- {
- Tags { "RenderType"="ForwardBase" }
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- // make fog work
- #include "Lighting.cginc"
-
- fixed4 _Diffuse;
- fixed4 _Specular;
- float _Gloss;
-
- struct a2v{
- float4 vertex :POSITION;
- float3 normal: NORMAL;
- };
-
- struct v2f{
- float4 pos:SV_POSITION;
- float3 worldNormal:TEXCOORD0;
- float3 worldPos:TEXCOORD1;
- };
- v2f vert(a2v v ){
- v2f o;
- o.pos =UnityObjectToClipPos(v.vertex);
-
- o.worldNormal =normalize(mul(v.normal,(float3x3)unity_WorldToObject));
-
- o.worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;
- return o;
- };
- fixed4 frag(v2f i):SV_Target {
- fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
- fixed3 worldNormal=i.worldNormal;
- fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);
- fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLightDir));
- fixed3 reflectDir=normalize(reflect(-worldLightDir,worldNormal));
- fixed3 viewDir=normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);
- fixed3 specular=_LightColor0.rgb*_Specular.rgb*pow(saturate(dot(reflectDir,viewDir)),_Gloss);
- return fixed4 (ambient+diffuse+specular,1.0);
- }
-
-
- ENDCG
- }
- }
- FallBack "Diffuse"
- }
Blinn模型没有使用反射方向,而是引入了新的矢量h半程向量,描述视角方向与光照方向的中间向量,由视角方向与光照方向相加后再归一化得到。实现代码与上面类似,仅呈现不同的部分。
- fixed4 frag(v2f i):SV_Target {
- fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
- fixed3 worldNormal=i.worldNormal;
- fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);
- fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLightDir));
- fixed3 reflectDir=normalize(reflect(-worldLightDir,worldNormal));
- fixed3 viewDir=normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);
- fixed3 halfDir=normalize(worldLightDir+viewDir);
- fixed3 specular=_LightColor0.rgb*_Specular.rgb*pow(max(0,dot(worldNormal,halfDir)),_Gloss);
- // BlinnPhong与phone的区别就在于高光反射部分
- return fixed4 (ambient+diffuse+specular,1.0);
- }
unity中一些内置函数可以辅助计算。
内置函数得到的方向是没有归一化的,仍需要使用normalize函数归一化
- v2f vert(a2v v ){
- v2f o;
- o.pos =UnityObjectToClipPos(v.vertex);
- o.worldNormal=UnityObjectToWorldNormal(v.normal);
- // o.worldNormal =normalize(mul(v.normal,(float3x3)unity_WorldToObject));
- o.worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;
- return o;
- };
- fixed4 frag(v2f i):SV_Target {
- fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
- fixed3 worldNormal=normalize(i.worldNormal);
- fixed3 worldLightDir=normalize(UnityWorldSpaceLightDir(i.worldPos));
- fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLightDir));
- fixed3 reflectDir=normalize(reflect(-worldLightDir,worldNormal));
- fixed3 viewDir=normalize(UnityWorldSpaceViewDir(i.worldPos));
- fixed3 halfDir=normalize(worldLightDir+viewDir);
- fixed3 specular=_LightColor0.rgb*_Specular.rgb*pow(max(0,dot(worldNormal,halfDir)),_Gloss);
- // BlinnPhong与phone的区别就在于高光反射部分
- return fixed4 (ambient+diffuse+specular,1.0);
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。