miduo_client/Assets/LuaFramework/Scripts/Utility/FastShadow/ShadowUtils.cs

184 lines
9.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
public static class ShadowUtils
{
private static List<Vector4> _vList = new List<Vector4>();
/// <summary>
/// 根据主相机,设置光照相机
/// </summary>
/// <param name="mainCamera"></param>
/// <param name="lightCamera"></param>
public static void SetLightCamera(Camera mainCamera, Camera lightCamera)
{
//1、 求视锥8顶点 (主相机空间中) n平面aspect * y, tan(r/2)* n,n f平面aspect*y, tan(r/2) * f, f
float r = (mainCamera.fieldOfView / 180f) * Mathf.PI;
//n平面
Vector4 nLeftUp = new Vector4(-mainCamera.aspect * Mathf.Tan(r / 2) * mainCamera.nearClipPlane, Mathf.Tan(r / 2) * mainCamera.nearClipPlane, mainCamera.nearClipPlane, 1);
Vector4 nRightUp = new Vector4(mainCamera.aspect * Mathf.Tan(r / 2) * mainCamera.nearClipPlane, Mathf.Tan(r / 2) * mainCamera.nearClipPlane, mainCamera.nearClipPlane, 1);
Vector4 nLeftDonw = new Vector4(-mainCamera.aspect * Mathf.Tan(r / 2) * mainCamera.nearClipPlane, -Mathf.Tan(r / 2) * mainCamera.nearClipPlane, mainCamera.nearClipPlane, 1);
Vector4 nRightDonw = new Vector4(mainCamera.aspect * Mathf.Tan(r / 2) * mainCamera.nearClipPlane, -Mathf.Tan(r / 2) * mainCamera.nearClipPlane, mainCamera.nearClipPlane, 1);
//f平面
Vector4 fLeftUp = new Vector4(-mainCamera.aspect * Mathf.Tan(r / 2) * mainCamera.farClipPlane, Mathf.Tan(r / 2) * mainCamera.farClipPlane, mainCamera.farClipPlane, 1);
Vector4 fRightUp = new Vector4(mainCamera.aspect * Mathf.Tan(r / 2) * mainCamera.farClipPlane, Mathf.Tan(r / 2) * mainCamera.farClipPlane, mainCamera.farClipPlane, 1);
Vector4 fLeftDonw = new Vector4(-mainCamera.aspect * Mathf.Tan(r / 2) * mainCamera.farClipPlane, -Mathf.Tan(r / 2) * mainCamera.farClipPlane, mainCamera.farClipPlane, 1);
Vector4 fRightDonw = new Vector4(mainCamera.aspect * Mathf.Tan(r / 2) * mainCamera.farClipPlane, -Mathf.Tan(r / 2) * mainCamera.farClipPlane, mainCamera.farClipPlane, 1);
//2、将8个顶点变换到世界空间
Matrix4x4 mainv2w = mainCamera.transform.localToWorldMatrix;//本来这里的矩阵使用mainCamera.cameraToWorldMatrix,但是请看http://docs.unity3d.com/ScriptReference/Camera-cameraToWorldMatrix.html cameraToWorldMatrix返回的是GL风格的camera空间的矩阵z是负的跟untiy编辑器中的不对应也是坑爹的很就不能统一吗所以我们直接使用localToWorldMatrix
Vector4 wnLeftUp = mainv2w * nLeftUp;
Vector4 wnRightUp = mainv2w * nRightUp;
Vector4 wnLeftDonw = mainv2w * nLeftDonw;
Vector4 wnRightDonw = mainv2w * nRightDonw;
//
Vector4 wfLeftUp = mainv2w * fLeftUp;
Vector4 wfRightUp = mainv2w * fRightUp;
Vector4 wfLeftDonw = mainv2w * fLeftDonw;
Vector4 wfRightDonw = mainv2w * fRightDonw;
//将灯光相机设置在mainCamera视锥中心
Vector4 nCenter = (wnLeftUp + wnRightUp + wnLeftDonw + wnRightDonw) / 4f;
Vector4 fCenter = (wfLeftUp + wfRightUp + wfLeftDonw + wfRightDonw) / 4f;
lightCamera.transform.position = (nCenter + fCenter) / 2f;
//3、 求光view矩阵
Matrix4x4 lgihtw2v = lightCamera.transform.worldToLocalMatrix;//本来这里使用lightCamera.worldToCameraMatrix,但是同上面不使用mainCamera.cameraToWorldMatrix的原因一样我们直接使用worldToLocalMatrix
//4、 把顶点从世界空间变换到光view空间
Vector4 vnLeftUp = lgihtw2v * wnLeftUp;
Vector4 vnRightUp = lgihtw2v * wnRightUp;
Vector4 vnLeftDonw = lgihtw2v * wnLeftDonw;
Vector4 vnRightDonw = lgihtw2v * wnLeftDonw;
//
Vector4 vfLeftUp = lgihtw2v * wfLeftUp;
Vector4 vfRightUp = lgihtw2v * wfRightUp;
Vector4 vfLeftDonw = lgihtw2v * wfLeftDonw;
Vector4 vfRightDonw = lgihtw2v * wfRightDonw;
_vList.Clear();
_vList.Add(vnLeftUp);
_vList.Add(vnRightUp);
_vList.Add(vnLeftDonw);
_vList.Add(vnRightDonw);
_vList.Add(vfLeftUp);
_vList.Add(vfRightUp);
_vList.Add(vfLeftDonw);
_vList.Add(vfRightDonw);
//5、 求包围盒 (由于光锥xy轴的对称性这里求最大包围盒就好不是严格意义的AABB)
float maxX = -float.MaxValue;
float maxY = -float.MaxValue;
float maxZ = -float.MaxValue;
float minZ = float.MaxValue;
for (int i = 0; i < _vList.Count; i++)
{
Vector4 v = _vList[i];
if (Mathf.Abs(v.x) > maxX)
{
maxX = Mathf.Abs(v.x);
}
if (Mathf.Abs(v.y) > maxY)
{
maxY = Mathf.Abs(v.y);
}
if (v.z > maxZ)
{
maxZ = v.z;
}
else if (v.z < minZ)
{
minZ = v.z;
}
}
//5.5 优化如果8个顶点在光锥view空间中的z<0,那么如果n=0就可能出现应该被渲染depthmap的物体被光锥近裁面剪裁掉的情况所以z < 0 的情况下要延光照负方向移动光源位置以避免这种情况
if (minZ < 0)
{
lightCamera.transform.position += -lightCamera.transform.forward.normalized * Mathf.Abs(minZ);
maxZ = maxZ - minZ;
}
//6、 根据包围盒确定投影矩阵 包围盒的最大z就是fCamera.orthographicSize由y max决定 还要设置Camera.aspect
lightCamera.orthographic = true;
lightCamera.aspect = maxX / maxY;
lightCamera.orthographicSize = maxY;
lightCamera.nearClipPlane = 0.0f;
lightCamera.farClipPlane = Mathf.Abs(maxZ);
}
/// <summary>
/// 根据场景包围盒来设置光锥
/// </summary>
/// <param name="b"></param>
/// <param name="lightCamera"></param>
public static void SetLightCamera(Bounds b, Camera lightCamera)
{
//1、将lightCamera放在包围盒中心
lightCamera.transform.position = b.center;
//2、 求光view矩阵
Matrix4x4 lgihtw2v = lightCamera.transform.worldToLocalMatrix;//本来这里使用lightCamera.worldToCameraMatrix,但是同上面不使用mainCamera.cameraToWorldMatrix的原因一样我们直接使用worldToLocalMatrix
//3、 把顶点从世界空间变换到光view空间
Vector4 vnLeftUp = lgihtw2v * new Vector3(b.max.x, b.max.y, b.max.z);
Vector4 vnRightUp = lgihtw2v * new Vector3(b.max.x, b.min.y, b.max.z);
Vector4 vnLeftDonw = lgihtw2v * new Vector3(b.max.x, b.max.y, b.min.z);
Vector4 vnRightDonw = lgihtw2v * new Vector3(b.min.x, b.max.y, b.max.z);
//
Vector4 vfLeftUp = lgihtw2v * new Vector3(b.min.x, b.min.y, b.min.z); ;
Vector4 vfRightUp = lgihtw2v * new Vector3(b.min.x, b.max.y, b.min.z); ;
Vector4 vfLeftDonw = lgihtw2v * new Vector3(b.min.x, b.min.y, b.max.z); ;
Vector4 vfRightDonw = lgihtw2v * new Vector3(b.max.x, b.min.y, b.min.z); ;
_vList.Clear();
_vList.Add(vnLeftUp);
_vList.Add(vnRightUp);
_vList.Add(vnLeftDonw);
_vList.Add(vnRightDonw);
_vList.Add(vfLeftUp);
_vList.Add(vfRightUp);
_vList.Add(vfLeftDonw);
_vList.Add(vfRightDonw);
//4、 求包围盒 (由于光锥xy轴的对称性这里求最大包围盒就好不是严格意义的AABB)
float maxX = -float.MaxValue;
float maxY = -float.MaxValue;
float maxZ = -float.MaxValue;
float minZ = float.MaxValue;
for (int i = 0; i < _vList.Count; i++)
{
Vector4 v = _vList[i];
if (Mathf.Abs(v.x) > maxX)
{
maxX = Mathf.Abs(v.x);
}
if (Mathf.Abs(v.y) > maxY)
{
maxY = Mathf.Abs(v.y);
}
if (v.z > maxZ)
{
maxZ = v.z;
}
else if (v.z < minZ)
{
minZ = v.z;
}
}
//4.5 优化如果8个顶点在光锥view空间中的z<0,那么如果n=0就可能出现应该被渲染depthmap的物体被光锥近裁面剪裁掉的情况所以z < 0 的情况下要延光照负方向移动光源位置以避免这种情况
if (minZ < 0)
{
lightCamera.transform.position += -lightCamera.transform.forward.normalized * Mathf.Abs(minZ);
maxZ = maxZ - minZ;
}
//5、 根据包围盒确定投影矩阵 包围盒的最大z就是fCamera.orthographicSize由y max决定 还要设置Camera.aspect
lightCamera.orthographic = true;
lightCamera.aspect = maxX / maxY;
lightCamera.orthographicSize = maxY;
lightCamera.nearClipPlane = 0.0f;
lightCamera.farClipPlane = Mathf.Abs(maxZ);
}
}