Unity Shader 学习记录(5) —— 实现漫反射光照模型

文章详细介绍了如何在Unity中实现漫反射光照模型,包括顶点着色器和片元着色器的使用,以及CG编程语言中的saturate函数来处理负值。文章还讨论了兰伯特光照模型和半兰伯特光照模型的区别,后者能提供更平滑的背光区域明暗变化,提高模型细节的表现。
摘要由CSDN通过智能技术生成

1 公式计算在这里插入图片描述

从公式可以看出,要计算漫反射需要知道4 个参数:入射光线的颜色和强度 cgh”材质的漫反射系数mdiuse,表面法线n以及光源方向I。

为了防止点积结果为负值,我们需要使用max操作,而CG提供了这样的函数。在本例中使用CG的另一个函数可以达到同样的目的,即saturate函数。

2 顶点着色器实现

Shader "MyShader/Chapter6-DiffuseVertexLevel"{
   //顶点着色器采用插值渲染,速度更快,但是效果有限
   //声明反射color属性
   Properties{
      _Diffuse ("Diffuse",  Color) =(1,1,1,1)
   }
   
   SubShader{
      pass{
         //LightMode标签是Pass标签中的一种,它用于定义该Pass在 Unity 的光照流水线中的角色
         Tags{"LightMode"="ForwardBase"}
         CGPROGRAM
            //CG代码
            //声明顶点着色器和片元着色器  无分号
            #pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
            
            //从属性中获取材质漫反射颜色
            fixed4 _Diffuse;

            //顶点着色器输入结构体
            struct a2v{
               fixed4 vertex : POSITION;
               //NORMAL语义告诉unity法线存到normal
               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;

               //unity_WorldToObject 模型空间转换到世界空间
               //mul矩阵点积
               //获取当前顶点的世界坐标下法线
               fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
               //获取当前光源向量
               fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
               //saturate截取参数0-1防止负数
               //计算光源颜色和漫反射颜色
               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
   Fallback "diffuse"
}

3 片元着色器实现

Shader "MyShader/Chapter6-Chapter6-DiffusePixelLevel"{
    //片元着色器逐像素渲染,速度慢,但是精度高

   //声明反射color属性
   Properties{
      _Diffuse ("Diffuse",  Color) =(1,1,1,1)
   }
   
   SubShader{
      pass{
         //LightMode标签是Pass标签中的一种,它用于定义该Pass在 Unity 的光照流水线中的角色
         Tags{"LightMode"="ForwardBase"}
         CGPROGRAM
            //CG代码
            //声明顶点着色器和片元着色器  无分号
            #pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
            
            //从属性中获取材质漫反射颜色
            fixed4 _Diffuse;

            //顶点着色器输入结构体
            struct a2v{
               fixed4 vertex : POSITION;
               //NORMAL语义告诉unity法线存到normal
               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 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

                //计算反射
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
                fixed3 color = ambient + diffuse;
				return fixed4(color, 1.0);
			}
         ENDCG
      }
   }

   //fallback
   Fallback "diffuse"
}

从实现结果上可以看出两种着色器效果差别
遗留问题:
逐像素光照可以得到更加平滑的光照效果。但是,即便使用了逐像素漫反射光照,有一个问题仍然存在。在光照无法到达的区域,模型的外观通常是全黑的,没有任何明暗变化,这会使模型的背光区域看起来就像一个平面一样,失去了模型细节表现。

半兰伯特光照模型

在6.4.1小节中,我们使用的漫反射光照模型也被称为兰伯特光照模型,因为它符合兰伯特定律一一在平面某点漫反射光的光强与该反射点的法向量和入射光角度的余弦值成正比。

Valve 公司在开发游戏《半条命》时提出了一种技术,由于该技术是在原兰伯特光照模型的基础上进行了一个简单的修改,因此被称为半兰伯特光照模型。

通过这样的方式,我们可以把分·I的结果范围从[-1,1]映射到[1]范围内。也就是说,对于模型的背光面,在原兰伯特光照模型中点积结果将映射到同一个值,即0值处;而在半兰伯特模型中,背光面也可以有明暗变化,不同的点积结果会映射到不同的值上。在这里插入图片描述

  //片元着色器计算漫反射光照模型
            fixed4 frag(v2f i) : SV_Target {
                //获取环境光色
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                //获取世界法线
                fixed3 worldNormal = normalize(i.worldNormal);
                //获取光源的世界法线
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                //半兰伯特参数
                fixed halfLambert = dot(worldNormal ,worldLightDir) * 0.5 + 0.5;

                //计算反射
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * halfLambert;
                fixed3 color = ambient + diffuse;
				return fixed4(color, 1.0);
			}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值