入门图形学:光照模型(三)

       紧接上一篇:https://blog.csdn.net/yinhun2012/article/details/80912620

       之前两篇博客,我们学习了一般光照模型对物体颜色的作用公式,如下:

       surfaceColor = emissive + ambient + diffuse + specular;

         这次我们就来实际在图形引擎中用shader cg实现一下,看下具体效果,效果图如下:

        

        这里我做了一个放射光为鲜红色的高亮材质球,其中fragment函数是自己实现的通用光照模型计算,cg代码如下:

        


 

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

Shader "Unlit/FragLightUnlitShader"
{

	Properties
	{
		_EmissiveColor("emissive",Color) = (1,1,1,1)
		_AmbientColor("embient",Color) = (1,1,1,1)
		_AmbientFactor("e_factor",Color) = (1,1,1,1)
		_LightColor("light",Color) = (1,1,1,1)
		_DiffuseFactor("d_factor",Color) = (1,1,1,1)
		_SpecularFactor("s_factor",Color) = (1,1,1,1)
		_SpecShininess("s_shininess",Range(10,100)) = 10
	}

	SubShader
	{
		//LightMode标注问ForwardBase是为了告诉unity引擎我们开始使用光照计算了
		//需要为我们开发准备好光照计算所有数据
		Tags{ "RenderType" = "Opaque" "LightMode" = "ForwardBase" }
		LOD 100

		Pass
		{
			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"
			#include "Lighting.cginc"

			struct app2vert
			{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};

			struct vert2frag
			{
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
			};

			float3 _EmissiveColor;		/*自己定义的放射颜色*/
			float3 _AmbientColor;		/*自己定义的环境光颜色,一般我们都是用unityLighting中的自带字段*/
			float4 _AmbientFactor;		/*自己定义的环境反射系数,为什么用一个color字段储存呢?博客中我会谈到*/
			float3 _LightColor;			/*自己定义的太阳光颜色,当然这个我们也是一般使用unityLighting.cginc中自带的场景中directionlight的颜色了*/
			float4 _DiffuseFactor;		/*自己定义的Diffuse漫反射系数,float4值*/
			float4 _SpecularFactor;		/*自己定义的Sepcular镜面反射系数,float4值*/
			float _SpecShininess;    	/*自己定义的specular的强度指数*/

			vert2frag vert(app2vert av)
			{
				vert2frag vf;
				//使用mvp矩阵将顶点位置变换到投影空间位置
				vf.pos = UnityObjectToClipPos(av.vertex);
				//使用M矩阵将法线位置变换到模型空间法线位置的单位向量
				vf.worldNormal = normalize(mul(UNITY_MATRIX_M,av.normal));
				//使用M矩阵将顶点位置变换到模型空间位置的单位向量
				vf.worldPos = normalize(mul(UNITY_MATRIX_M,av.vertex).xyz);
				return vf;
			}

			fixed4 frag(vert2frag vf) : SV_Target
			{
				//根据lighting.cginc中的字段准备一些参数
				float3 unity_buildin_ambient_light_color = UNITY_LIGHTMODEL_AMBIENT.xyz;									/*unity自带的环境光颜色*/
				
				float3 unity_buildin_world_p2sun_dir = normalize(WorldSpaceLightDir(float4(vf.worldPos,1)));				/*unity自己的光线入射方向计算相反的的p点到太阳的朝向单位向量*/
				
				float3 unity_buildin_light_color = _LightColor0.rgb;														/*unity自带的入射光颜色*/
				
				//float3 unity_buildin_light_color = _LightColor;/*或者自己指定lightColor*/
				float3 unity_buildin_p2eye_dir = normalize(_WorldSpaceCameraPos.xyz - vf.worldPos.xyz);						/*unity自带的eye眼睛到p点朝向单位向量*/
				
				float3 h_dir_or_call_VplusL_dir = normalize(unity_buildin_p2eye_dir + unity_buildin_world_p2sun_dir);		/*计算出的H向量的单位向量*/
				/*surfaceColor = emissive + ambient + diffuse + specular根据这个公式一步一步来计算*/

				//先计算emissive
				float3 emissive = _EmissiveColor;
				//再计算ambient
				float3 ambient = _AmbientFactor.rgb * unity_buildin_ambient_light_color;
				/* 或者float3 ambient = _AmbientFactor.rgb * _AmbientColor; */
				//再计算diffuse
				float3 diffuse = _DiffuseFactor.rgb * unity_buildin_light_color * max(dot(vf.worldNormal,unity_buildin_world_p2sun_dir),0);
				//再计算specular
				float3 specular = _SpecularFactor.rgb * unity_buildin_light_color * pow(max(dot(vf.worldNormal,h_dir_or_call_VplusL_dir),0),_SpecShininess);
				//最终计算surfaceColor
				fixed4 sCol = fixed4(emissive + ambient + diffuse + specular,1.0);
				return sCol;
			}
			ENDCG
		}
	}
}

        这里我来着重解释一下cg代码中可能导致小伙伴们看不懂的地方。

        一.properties定义字段讲解。

        我依次将放射颜色(_EmissiveColor),环境光颜色(_AmbientColor),材质环境反射系数(_AmbientFactor),场景光源颜色(_LightColor),漫反射系数(_DiffuseFactor),镜面反射系数(_SpecularFactor),镜面反射强度指数(_SpecShininess)人为定义完毕。

        这里需要特别注意的是,我将系数都定义为了Color的float4类型,这是为什么呢?这些系数难道不应该是一个float类型么?

        其实是这样考虑的,我们获取颜色rgb(float3)*系数(float4或者float)后分两种情况:

        ①.rgb(float3) * 系数(float4).xyz这种乘法得到的结果是float3(rgb.x*系数.x,rgb.x*系数.y,rgb.x*系数.z)

        ②.rgb(float3) * 系数(float) = float3(rgb.x*系数,rgb.x*系数,rgb.x*系数)

        第一种细节更加丰富,因为rgb颜色的每个分量分别乘上了不同的系数分量值,并不像第二种一样分量的系数不变,第二种就类似于一个等比例系数,太固定死板了。

        二.vert顶点函数讲解

        vert顶点函数中的输入参数app2vert结构体,这个就是UnityCG.cginc文件中提供的unitybuildin字段,我们可以直接定义成输入参数结构体拿来用,这些都是unity帮我计算储存好的,此时我就使用了vertex顶点坐标和normal顶点法向量(当然注意,这是最原始的数据)

        随后,在顶点函数中,我依次用矩阵处理了

       ①.UNITY_MATRIX_MVP去处理顶点变换到裁剪空间

      ②.UNITY_MATRIX_M去处理法向量到3D模型空间(这个法向量无所谓平移或者不平移,你可以用matrix3x3或者4x4的M矩阵去处理都行)并且单位化它,以便后续使用

       ③.UNITY_MATRIX_M去处理顶点坐标到3D模型空间坐标,后面也需要用

        ps:如果现在还是不懂这些矩阵运算意义的同学,建议从我博客第一篇看起。

       三.frag片段函数讲解

       frag片段函数就开始我们最关键的光照模型数学计算讲解了。

       首先我们准备一些参数,如下:

       ①.float3 unity_buildin_ambient_light_color = UNITY_LIGHTMODEL_AMBIENT.xyz;

         这个字段是unity buildin定义字段,记录的是当前环境光颜色,可以通过editor - window-lighting-settings中的Environment Lighting手动设置。

        ②.float3 unity_buildin_world_p2sun_dir = normalize(WorldSpaceLightDir(float4(vf.worldPos,1)));

        根据unity buildin的获取光线射线朝向向量的函数WorldSpaceLightDir函数获取顶点到光源朝向向量L,同时单位化。

        ③.float3 unity_buildin_light_color = _LightColor0.rgb;

        一目了然,这就是unity buildin的入射光线颜色值

        ④.float3 unity_buildin_eye2p_dir = normalize(_WorldSpaceCameraPos.xyz - vf.worldPos.xyz);

        这个就是计算的观察者眼睛到照射点P的朝向向量,根据场景中camera在3D建模空间xyz坐标-顶点在3D建模空间中xyz坐标。

        ⑤.float3 h_dir_or_call_VplusL_dir = normalize(unity_buildin_eye2p_dir + unity_buildin_world_p2sun_dir);

        计算出H单位向量的值,就是V+L。这里不懂原因的同学可以返回上一篇看我写的镜面反射数学计算。

        接下来就简单了,依次计算出:emissive、ambient、diffuse、specular,然后计算出surfaceColor。

        这些基本是相当详细的讲解了cg代码的每一个步骤的作用,我相信小伙伴能看懂,demo我就不放了csdn要分,这个cg代码就是最核心的东西,有兴趣自己动手实现看看。

        下一篇我们学习一种自定义通用光照模型的cg写法。

 

        

 

        

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值