untiy 3d ShaderLab_第9章_2_球体阴影(一) 平行光对球体的投影

本文介绍了一种用于计算球体在平行光下投影阴影的方法。通过脚本和着色器实现,利用向量运算和相似三角形原理判断物体是否位于阴影区域内。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

球形阴影
        在平面阴影的讲述中,接收阴影的物体结构比较简单,因此我们可以有一个简单有效的算法来解决投射在平面上的阴影问题。同理,如果阴影投射物体的结构很简单,我们也可以设计出一个简单有效的算法来计算阴影,比如球体投影的阴影。其实不只是球体只要是能用能用一固定方程式表达的几何体,都适用于此概念。
9.1平行光对球体的投影
    当我们知道一个球体的位置以及其半径的时候,就已经确切地掌握了这个球体在空间的儿何信息,此时就很容易算出这个球体的投影。首先是脚本SphereShadow.cs,这是一个设定球体位置和半径信息的脚本,需要附加到使用此材质接受球体阴影的脚本上,其代码如下:
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class SphereShadow : MonoBehaviour {
	public GameObject sphere;


	void Update () {
		Vector3 pos=sphere.transform.position;
		GetComponent<Renderer>().sharedMaterial.SetVector("_spPos",new Vector4(pos.x,pos.y,pos.z,1f));
		GetComponent<Renderer>().sharedMaterial.SetFloat("_spR",sphere.transform.localScale.x/2);
	}
}

9.1.2 使用相似三角形计算投影
    然后,当我们需要计算一个点是否在球体的投影区域时,我们需要知道这个点到灯光的方向到球体中心的方向以及距离,然后根据这两个方向的夹角,通过相似三角形的计算考察透过这个方向在球体的此半径内能不能看到光源,如果可以,此点就不处于阴影之中,否则此点将被标记在阴影中。这个完整操作是在SphereShadow_l.shader中完成的,其完整代码如下:
Shader "Tut/Shadow/SphereShadow_1" {
	Properties {
		_spPos ("Sphere Position", vector) = (0,0,0,1)//投影球体位置
		_spR ("Sphere Radius", float) = 1//投影球体半径
		_Intensity("Intensity Of Shadow",range(0,1))=0.5//阴影浓度
	}
	SubShader {
		pass{
		Tags{"LightMode"="ForwardBase"}
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"
			float4 _spPos;
			float _spR;
			float _Intensity;
			float4 _LightColor0;
			struct v2f{
				float4 pos:SV_POSITION;
				float3 litDir:TEXCOORD0;//在世界坐标中的灯光方向矢量
				float3 spDir:TEXCOORD1;//在世界坐标中的投影球体方向矢量
				float4 vc:TEXCOORD2;//逐顶点计算的光照
			};
		v2f vert(appdata_base v)
		{
			v2f o;
			o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
			o.litDir=WorldSpaceLightDir(v.vertex);
			o.spDir=(_spPos-mul(_Object2World,v.vertex)).xyz;
			//下面是对顶点光照的计算,与阴影的计算无关

			float3 ldir=ObjSpaceLightDir(v.vertex);
			ldir=normalize(ldir);
			o.vc=_LightColor0*max(0,dot(ldir,v.normal));
			return o;
		}
		float4 frag(v2f i):COLOR
		{
			float3 litDir=normalize(i.litDir);
			float3 spDir=i.spDir;
			float spDistance=length(spDir);//到球体的距离
			spDir=normalize(spDir);

			float cosV=dot(spDir,litDir);//两个方向的夹角余弦值
			float sinV=sin(acos(max(0,cosV)));
			float D=sinV*spDistance;//解三角形
			float shadow=step(_spR,D);//spR>D 0,else 1 判断是否能透过此方向看到光源
			float c=lerp(1-_Intensity,1,shadow);//0 is dark  //*step(0,dot(i.N,litDir))
			return i.vc*c;
		}
		ENDCG
		}//endpass
	} 
}

        这个Shader在vertex函数中计算了到光源和投影球体的矢量,在fragment函数中,把插值后的矢量进行normalize,同时计算到投影球体的距离,这个距离在解此点、球体中心以及到光源方向矢量的垂线所构造的三角形时要用到。通过这两个方向矢量的点积得出角度,然后解上面那个三角形,通过到光源矢量方向的垂线距离与球体半径的大小判定在此点能否看到光源。打开SphereShadow文件夹下面的SphereShadow-1场景,效果如图所示。

Scene:


Game:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值