您的位置:首页 > 其它

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

2016-10-21 11:06 357 查看
球形阴影

在平面阴影的讲述中,接收阴影的物体结构比较简单,因此我们可以有一个简单有效的算法来解决投射在平面上的阴影问题。同理,如果阴影投射物体的结构很简单,我们也可以设计出一个简单有效的算法来计算阴影,比如球体投影的阴影。其实不只是球体只要是能用能用一固定方程式表达的几何体,都适用于此概念。

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:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐