第六天 开始Unity Shader的学习之Unity中的基础光照之漫反射光照模型

Unity Shader的学习笔记

第六天 开始Unity Shader的学习之Unity中的基础光照之漫反射光照模型



前言

提示:这里可以添加本文要记录的大概内容:

这几天忙了一些其他的东西,之后尽量做到每天更新,今天学的是漫反射光照模型中的逐像素光照以及半兰伯特光照模型.


提示:以下是本篇文章正文内容,下面案例可供参考

一、漫反射光照模型

1.逐像素光照

我们只需要对逐顶点的漫反射光照模型进行一些更改就可以得到逐像素的漫反射效果.
我们先来看看整体代码

Shader "Unity Shaders Book/Chapter 6/Diffuse Pixed-Level"
{
    
    
    Properties
    {
    
    
        //材质的漫反射系数
       _Diffuse ("Diffuse", Color) = (1.0 , 1.0 , 1.0 , 1.0)
    }
    SubShader
    {
    
    
        Pass
        {
    
    
            //标签:定义光照模式    只有定义正确的LightMode,才能得到一些Unity的内置光照变量,例如下面的_LightColor0
            Tags    {
    
    "LightMode"="ForwardBase"}     

            CGPROGRAM
            #pragma vertex vert             //定义顶点着色器名字
            #pragma fragment frag           //定义片元着色器名字

            #include "UnityCG.cginc"        //使用Unity内置变量
            #include "Lighting.cginc"       //使用Unity内置变量,后面的_LightColor0要用到
            //定义面板参数
            uniform fixed4 _Diffuse;

            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;
            }


            float4 frag(v2f i) : SV_Target										③
            {
    
    
                //得到环境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                //归一化法线
                fixed3 worldNormal = normalize(i.worldNormal);
                //得到光源的方向
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

                fixed3 diffuse = _LightColor0.rbg * _Diffuse.rbg * saturate(dot(worldNormal, worldLightDir));

                fixed3 color = diffuse + ambient;

                return fixed4(color, 1.0);
            }

            ENDCG
        }
    }
    Fallback "Diffuse"
}

① 更改v2f

由于我们编写的是逐像素光照,所以需要在片元着色器中计算,那么我们就需要将法线信息传递给片元着色器,所以更改v2f结构体:

struct v2f{
    
    
      float4 pos : SV_POSITION;
      float3 worldNormal : TEXCOORD0;
};

② 传递法线信息给片元着色器

由于计算逐像素的漫反射模型,所以需要将世界空间的法线信息存储到v2f的结构体中,从而传递给片元着色器.(将法线从模型空间转换到世界空间的代码要牢记,这里面的数学之后如果需要了解的话,之后会单独出一期)

o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);

③ 片元着色器计算漫反射光照模型

float4 frag(v2f i) : SV_Target
{
    
    
    //得到环境光
    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
    //归一化法线
    fixed3 worldNormal = normalize(i.worldNormal);
    //得到光源的方向
    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

    fixed3 diffuse = _LightColor0.rbg * _Diffuse.rbg * saturate(dot(worldNormal, worldLightDir));

    fixed3 color = diffuse + ambient;

    return fixed4(color, 1.0);
}

首先我们得到环境光ambient,然后对顶点着色器传来的世界空间下的法线和光照方向进行归一化处理,接着就可以带入漫反射光照模型的计算公式进行计算,返回color.
实现的结果如下:
漫反射光照之逐像素实现
相比于逐顶点光照,逐像素光照得到的光照结果更加平滑,但是我们会发现,在光照没办法到达的区域,模型的外观通常是全黑的,没有任何明暗变化,使得背光区域更像一个平面一样,失去了模型的细节表现,为了解决这个问题,半兰伯特光照模型被提出来了.

二.半兰伯特模型

我们使用的漫反射模型就是兰伯特光照模型,那么广义的半兰伯特模型的公式如下:
半兰伯特模型的公式
可以看出,相比于兰伯特模型,半兰伯特并没有使用max操作来防止法线和光照方向的点积为负值,而是对其结果做了一个0.5倍的放缩,在加上一个0.5倍的偏移,通过这种方式,我们也可以将[-1,1]的结果映射到[0,1]范围内,也就是说,本来模型的背光面都被映射为0,但是通过半兰伯特模型,背光面也会有明暗变化,不同的点积会映射到不同的值上.

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 6/HalfLambert Mat"
{
    
    
	Properties
	{
    
    
		_Diffuse ("Diffuse", Color) = (1.0, 1.0, 1.0, 1.0)
	}
	SubShader
	{
    
    
		Pass
		{
    
    
			Tags {
    
    "LightMode" = "ForwardBase"}

			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag

			#include "Lighting.cginc"

			fixed4 _Diffuse;

			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;
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * (0.5 + 0.5 * dot(worldNormal, worldLightDir));
				fixed3 color = ambient + diffuse;
				return fixed4(color, 1.0);
			}
			ENDCG
		}
	}
	FallBack "Diffuse"
}

半兰伯特模型效果


总结

今天学习了兰伯特模型的逐像素光照和半兰伯特模型,明天对Unity Shader中的高光反射模型.