【《Unity Shader入门精要》 提炼总结】(七)第七章·Unity中的基础光照介绍&漫反射光照Shader编写&逐顶点光照&逐像素光照&半兰伯特光照

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/iceSony/article/details/84334899

本文由@唐三十胖子出品,转载请注明出处。  
文章链接:https://blog.csdn.net/iceSony/article/details/84334899

这篇文章将总结和提炼《Unity Shader入门精要》的第六章“Unity中的基础光照”的内容。

通过这篇文章,你可以知道

1)标准光照模型对光线的处理

2)漫反射的逐顶点光照

3)漫反射的逐像素光照

4)漫反射的半兰伯特光照

一.标准光照模型对光线的处理

下面四种都是模型对光的处理

自发光emissive 不会改变周围物体颜色

模型表现颜色为自发光颜色

 

高光反射 specular 完全反射光

phong模型:高光反射颜色 = 光线颜色*材质高光反射系数*max(0,法线在发射光线的投影)^反光度

漫反射 duffuse 吸收波长,反射颜色波长

兰伯特定律:漫反射颜色 = 光线颜色*材质漫反射系数*max(0,法线在光源方向的投影)

环境光 ambient 所有物体颜色受环境光影响

模型表现颜色为环境光 (Unity设置,Window->Lighting->Ambient Source->Ambient Color->Ambient Intensity)

接下来我们将重点介绍最常见的漫反射与高光反射光照Shader编写。

二.漫反射光照Shader编写

对漫反射光照的处理有两种方式

逐顶点光照 or 逐像素光照

逐顶点光照:单位为顶点 通过顶点进行计算 计算量小 图元内部顶点着色可能产生棱角

逐像素光照:单位为像素 通过法线进行计算 计算量大

逐顶点光照

注意这里我们需要设置tag,获取光照颜色与光照方向

_LightColor0与_WorldSpaceLightPos0(注意这里只有一个光源)☞                Tags {“LightMode”=”ForwardBase”}

Tags具体使用:https://blog.csdn.net/iceSony/article/details/84258594

代码中首先设置一个属性_Diffuse,作为模型漫反射的系数

Properties
{
    _Diffuse("Color", Color) = (1.0,1.0,1.0,1.0)
}

之后就是常见的Pass语义定义

SubShader
{
	Tags { "RenderType" = "ForwardBase" }
	Pass
	{
		CGPROGRAM
		#pragma vertex vert
		#pragma fragment frag
		#include "Lighting.cginc"
		fixed4 _Diffuse;

在这之后就是结构体

struct a2v
{
	float4 position:POSITION;
	float3 normal:NORMAL;
};
struct v2f
{
	float4 pos : SV_POSITION;
	fixed3 color : COLOR;
};

最重要的着色器函数_LightColor0代表光照颜色,_WorldSpaceLightPos0代表世界坐标中光照方向

v2f vert (a2v v)
{
    v2f o;
    o.pos = UnityObjectToClipPos(v.position);
    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
    fixed3 worldNormal = normalize(mul(v.normal, (float3x3)_World2Object));
    fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
    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

重点解说顶点着色器

首先将o.pos从模型空间坐标转换到屏幕坐标

ambient是环境光的方向的获取

这里进行了两步操作v.normal代表的是模型空间坐标下的顶点法线

我们可以获取到光照方向,diffuse下进行光照在法线上的强度计算。

World2Object是什么

这是计算方法,Unity提供的变换矩阵

可以通过mul(v.normal, (float3x3)_World2Object)方法转化法线到空间坐标

其中因为法线类型为float3 所以取float4x4矩阵的一部分就够了

 

Saturate函数目的是获取[0-1]内的参数

最后在和diffuse颜色相乘得到最终漫反射结果

果然边缘看起来很凹凸不平

 

这里你可能奇怪

1.为什么要和光照颜色进行相乘?

如果你调整光照颜色为红色且不和光照颜色相乘,这时候你的物体颜色不会受到影响,还是白色。

2.那为什么不是加呢?

左加右乘,高下立盼,这时候你调整属性会发现右边的类似黄+蓝=绿的效果

完整代码如下

Shader "sony/Shader144"
{
    Properties
    {
	_Diffuse("Color", Color) = (1.0,1.0,1.0,1.0)
    }
    SubShader
    {
	Tags { "RenderType" = "ForwardBase" }
	Pass
	{
	    CGPROGRAM
	    #pragma vertex vert
	    #pragma fragment frag
	    #include "Lighting.cginc"
	    fixed4 _Diffuse;
	    struct a2v
	    {
		float4 position:POSITION;
		float3 normal:NORMAL;
	    };
	    struct v2f
	    {
		float4 pos : SV_POSITION;
		fixed3 color : COLOR;
            };	
	    v2f vert (a2v v)
	    {
		v2f o;
		o.pos = UnityObjectToClipPos(v.position);
		fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
		fixed3 worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject));
		fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
		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"
}

三.逐像素光照

计算方式与逐顶点光照类似,这里不重复介绍了。

Shader "sony/Shader147"
{
	Properties
	{
		_Diffuse("Color", Color) = (1.0,1.0,1.0,1.0)
	}
		SubShader
	{
		Tags{ "RenderType" = "ForwardBase" }
		Pass
	{
		CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
		fixed4 _Diffuse;
	struct a2v
	{
		float4 position:POSITION;
		float3 normal:NORMAL;
	}
	;
	struct v2f
	{
		float4 pos : SV_POSITION;
		float3 texcoord : TEXCOORD0;
	};

	v2f vert(a2v v)
	{
		v2f o;
		o.pos = UnityObjectToClipPos(v.position);
		o.texcoord = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
		return o;
	}

	fixed4 frag(v2f i) : SV_Target
	{
		fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
		fixed3 worldNormal = normalize(i.texcoord);
		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 "Diffuse"
}

具体效果比逐顶点光照shader好。

四.半兰伯特光照

虽然我们可以通过添加环境光让角色背面亮起来,但是那种背面的阴影是一个深度的

在半条命中提出了这样的一种光照模式,具体操作是原本反射的光在光照法线上的曲线

这样就解决了判断0-1的问题:)

好处也很明显,背面的光照显得很真实。

这是背面对比图

贴个代码

Shader "sony/Shader148"
{
	Properties
	{
		_Diffuse("Color", Color) = (1.0,1.0,1.0,1.0)
	}
		SubShader
	{
		Tags{ "RenderType" = "ForwardBase" }
		Pass
	{
		CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
		fixed4 _Diffuse;
	struct a2v
	{
		float4 position:POSITION;
		float3 normal:NORMAL;
	}
	;
	struct v2f
	{
		float4 pos : SV_POSITION;
		float3 texcoord : TEXCOORD0;
	};

	v2f vert(a2v v)
	{
		v2f o;
		o.pos = UnityObjectToClipPos(v.position);
		o.texcoord = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
		return o;
	}

	fixed4 frag(v2f i) : SV_Target
	{
		fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
	fixed3 worldNormal = normalize(i.texcoord);
	fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
	fixed3 halfLambert = dot(worldNormal, worldLightDir)*0.5 + 0.5;
	fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*halfLambert;
	fixed3 color = ambient + diffuse;

	return fixed4(color,1.0);
	}
		ENDCG
	}
	}
		Fallback "Diffuse"
}

其实在日常开发中这是最常用的一种光照计算方式。

下一章节,我们将介绍高光反射。

猜你喜欢

转载自blog.csdn.net/iceSony/article/details/84334899