UnityShader源码2017---学习笔记与自我拓展028-1

这篇集中梳理一下关于顶点光源的话题。这也是上篇留下的ShadeSH9等。

先从API和Doc下手。


简单翻译一下:(如果有什么特殊的疑问请自己在unity里测试一下吧,比如这种问题:我所有的light都是not Important,那么最亮的那个直线光还是per-pixel么?等等。。)

1.只要被设置成Not Important 的light一律按照per-vertex和SH光照处理

2.最亮的直线光按照per-pixel处理

3.只要被设置长Important的light一律按照per-pixel处理

4.如果像素光的数量少于QualitytSetting里设定的数量的话,按照亮度的强弱在per-pixel里依次参与渲染计算。

Forward RenderingMode中

1.在Base Pass中处理一个像素直线光(最亮的那个,如果要问,我弄两了两个参数完全一样的直线光,并且都是Important的话,并且距离物体的距离也是一样的,那个参与BasePass里的计算呢?我想给出的回答是:“胸弟,不要为难自己,自己测试一下吧”)和所有的per-vertex/SH光源。

2.其他的像素光在additional passes里参与渲染计算,每个像素光都会产生一个pass。

接下来进入正题

首先看一下Shade4PointLights

这个方法的意图很明显,就是计算4个点光源。

这个方法的注释还是要看一下的

// Used in ForwardBase pass: Calculates diffuse lighting from 4 point lights, with data packed in a special way.

这个方法使用与Base pass中,用最多4个点光源于计算漫反射光之中,这最多4个的点光源的数据是用一种特殊的方式pack了一下,“unity粑粑果然黑盒”,意思很明确,unity的意思就是“粑粑已经把最多4个的点光源(如果不够四个,那估计就用黑色轻度为0的点光源填充)的数据给你准备好了,不要问粑粑怎么准备的,你拿去basepass里用就好了”,unity粑粑强硬又不失典雅。

这里多说一句,为啥是最多4个point light而不是5个或者6个?其实这个也不难猜测,float4嘛,我们处理的是三维空间的数据,引入齐次之后,。。。。,巴拉巴拉一堆,我也不知道怎说,索性就不说了吧。


看一下这个方法的声明

float3 Shade4PointLights (
float4 lightPosX, float4 lightPosY, float4 lightPosZ,
float3 lightColor0, float3 lightColor1, float3 lightColor2, float3 lightColor3,
float4 lightAttenSq,
float3 pos, float3 normal){}

这个简单的说一下,如果想深入了解。请看一下untiy3d ShaderLab实战详解》,建议购买正版支持一下作者(他是大前辈,他的书我拜读了至少3遍)。听说最近群里的管理员们一起商榷,打算出第三版。额。。。。扯远了

lightPosX这个是个float4,一下子就知道了它存储的是4个pointlight的position.x,同理,lightPosYlightPosZ

lightColor0,lightColor1,lightColor2,lightColor3这四个无疑就是light的颜色的rgb值了。

lightAttenSq这个存储的就是平方衰减的值了

pos和normal就是物体的worldSpace下的顶点position和normal了

接下来可以一点一点吃方法体

float4 toLightX = lightPosX - pos.x;
float4 toLightY = lightPosY - pos.y;
float4 toLightZ = lightPosZ - pos.z;

因为每一个点关于的坐标是分开XYZ单独存储的

也就是说,float3(toLightX.x,toLightY.x,toLightZ.x)是第一盏点光源得到pos的direction vector

float4 lengthSq = 0 ;
lengthSq += toLightX * toLightX;
lengthSq += toLightY * toLightY;
lengthSq += toLightZ * toLightZ;
// don't produce NaNs if some vertex position overlaps with the light
lengthSq = max (lengthSq, 0.000001 );

这里求的就是point light与vertex 的worldspace的距离的平方值。

接下来就是一起计算4个point light的NdotL

// NdotL
float4 ndotl = 0 ;
ndotl += toLightX * normal.x;
ndotl += toLightY * normal.y;
ndotl += toLightZ * normal.z;
// correct NdotL
float4 corr = rsqrt (lengthSq);
ndotl = max ( float4 ( 0 , 0 , 0 , 0 ), ndotl * corr);

注释:rsqrt(x)      返回1/sqrt(x)

这里解释一下之前所有的骚气操作。

首先大家都直道 NdotL的计算  

NdotL = dot(normalize(Normal),normalize(LightDir))

normalize(vec3) = vec3/(length(vec3))

length(vec3) = sqrt(dot(vec3,vec3))

dot(vec3,vec3) = vec3.x*vec3.x + vec3.y*vec3.y + vec3.z*vec3.z

然后我们折回来看一下源码,这里我们只看一个pointlight的计算

为了方便表述这里简单的定义下

float3 toLight = float3(toLightX.x,toLightY.x,toLightZ.x);

float lengthSq_Single= lengthSq.x;

float ndotl_single = ndotl.x;

首先我们拿到了未单位化(标准化)的light direction vector------toLight,然后拿到了距离的平方------lengthSq_Single,

然后我们只需要   toLight*rsqrt(lengthSq_Single)就拿到了单位化的light direction vector。然后在计算ndotl,计算之前要单位化normal

但是源码并不是这么做的

它先做了ndotl = toLight.x * normal.x + toLight.y*normal.y + tolight.z*normal.z;

然后

ndotl*rsqrt(ndotl_single)

= toLight.x*rsqrt(ndotl_single) * normal.x + toLight.y*rsqrt(ndotl_single) *normal.y + tolight.z*rsqrt(ndotl_single) *normal.z

= dot(normalize(toLight),normal)

最后的结果也是一样的。为啥要滞后计算这个单位化的操作

其实也很好理解,toLightX ,toLightY,toLightZ这三个都是个float4,如果单位化的话,这12个分量都要去与对应的length的倒数做乘法

所以官方采取了最后做,那么只需要对一个float4做一次就好。

然后,接着看后面的源码

// attenuation
float4 atten = 1.0 / ( 1.0 + lengthSq * lightAttenSq);
float4 diff = ndotl * atten;

这个就是简单的平方衰减的计算和diff的计算。从这里就知道了这四个point light采用的是lambert光照模型。

// final color
float3 col = 0 ;
col += lightColor0 * diff.x;
col += lightColor1 * diff.y;
col += lightColor2 * diff.z;
col += lightColor3 * diff.w;

最后这里就是把灯光的颜色算进去而已

到这里就把Shade4PointLights 这个方法简单的剖析了一下,本来还想继续写写ShadeSH9这个。但是感觉还是重开一篇做做记录吧


下来就是简单的应用Shade4PointLights 的shader源码。

Shader "ShaderStore/Templates/New Unlit NoFog"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" "LightMode"="ForwardBase"}
		LOD 100

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
				float3 normal : NORMAL;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
				float3 shade4pointlights: TEXCOORD1;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			
			v2f vert (appdata v)
			{
				v2f o;
				float4 posWorld = mul(unity_ObjectToWorld,v.vertex);
				o.vertex = mul(UNITY_MATRIX_VP,posWorld);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				float3 normWorld = mul(v.normal,(float3x3)unity_WorldToObject);

				o.shade4pointlights = Shade4PointLights(
					unity_4LightPosX0,unity_4LightPosY0,unity_4LightPosZ0,
					unity_LightColor[0].rgb,unity_LightColor[1].rgb,unity_LightColor[2].rgb,unity_LightColor[3].rgb,
					unity_4LightAtten0,
					posWorld.xyz,normWorld
				);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 col = tex2D(_MainTex, i.uv);
				col.rgb *= i.shade4pointlights;
				return col;
			}
			ENDCG
		}
	}
}












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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值