TaiZhouCangChu_VRanime/Assets/FGC/Volund-Morph3D/UniqueShadow/UniqueShadow.cs

369 lines
12 KiB
C#

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class UniqueShadow : MonoBehaviour {
[System.Serializable] public class FocusSetup {
public bool autoFocus;
public float autoFocusRadiusBias;
public Transform target;
public Vector3 offset;
public float radius = 1f;
public float depthBias = 0.0005f;
public float sceneCaptureDistance = 50f;
}
[HideInInspector] public Shader uniqueShadowDepthShader;
public enum Dimension {
x256 = 256,
x512 = 512,
x1024 = 1024,
x2048 = 2048,
x4096 = 4096,
x8192 = 8192,
}
public Dimension shadowMapSize = Dimension.x2048;
public float cullingDistance = 15f;
public LayerMask inclusionMask;
public bool useSceneCapture = true;
public float blockerSearchDistance = 24f;
public float blockerDistanceScale = 1f;
public float lightNearSize = 4f;
public float lightFarSize = 22f;
public float fallbackFilterWidth = 6f;
public int startFocus;
public FocusSetup[] shadowFoci;
int m_downscale = 0;
int m_activeFocus = -1;
Light m_lightSource;
List<Material> m_materialInstances;
static Texture2D ms_shadowTextureFakePoint;
static int ms_shadowMatrixID, ms_shadowTextureID;
//static Plane[] ms_cameraPlanes = new Plane[6];
RenderTexture m_shadowTexture;
Matrix4x4 m_shadowMatrix;
Camera m_shadowCamera;
Matrix4x4 m_shadowSpaceMatrix;
public void SetDownscale(int downscale) {
m_downscale = downscale;
if(m_shadowTexture) {
ReleaseTarget();
AllocateTarget();
}
}
void Awake() {
if(!ms_shadowTextureFakePoint) {
ms_shadowTextureFakePoint = new Texture2D(1, 1, TextureFormat.Alpha8, false, true);
ms_shadowTextureFakePoint.filterMode = FilterMode.Point;
ms_shadowTextureFakePoint.SetPixel(0, 0, new Color(0f, 0f, 0f, 0f));
ms_shadowTextureFakePoint.Apply(false, true);
ms_shadowMatrixID = Shader.PropertyToID("u_UniqueShadowMatrix");
ms_shadowTextureID = Shader.PropertyToID("u_UniqueShadowTexture");
}
EnsureLightSource();
m_shadowMatrix = Matrix4x4.identity;
var shadowCameraGO = new GameObject("#> _Shadow Camera < " + this.name);
shadowCameraGO.hideFlags = HideFlags.DontSave | HideFlags.NotEditable | HideFlags.HideInHierarchy;
m_shadowCamera = shadowCameraGO.AddComponent<Camera>();
m_shadowCamera.renderingPath = RenderingPath.Forward;
m_shadowCamera.clearFlags = CameraClearFlags.Depth;
m_shadowCamera.depthTextureMode = DepthTextureMode.None;
m_shadowCamera.useOcclusionCulling = false;
m_shadowCamera.cullingMask = useSceneCapture ? (LayerMask)~0 : inclusionMask;
m_shadowCamera.orthographic = true;
m_shadowCamera.depth = -100;
m_shadowCamera.aspect = 1f;
m_shadowCamera.SetReplacementShader(uniqueShadowDepthShader, "RenderType");
m_shadowCamera.enabled = false;
SetFocus(startFocus);
m_materialInstances = new List<Material>();
var materialMap = new Dictionary<Material, Material>();
foreach(var r in GetComponentsInChildren<Renderer>()) {
if(!r.receiveShadows)
continue;
bool hadMaterials = false;
var sharedMaterials = r.sharedMaterials;
for(int i = 0, n = sharedMaterials.Length; i < n; ++i) {
var m = sharedMaterials[i];
Material mi = null;
if(!materialMap.TryGetValue(m, out mi)) {
materialMap[m] = mi = new Material(m);
mi.name = m.name + " (uniq)";
mi.shaderKeywords = m.shaderKeywords;
mi.renderQueue = m.renderQueue;
SetStaticShaderUniforms(mi);
m_materialInstances.Add(mi);
}
sharedMaterials[i] = mi;
hadMaterials = true;
}
if(hadMaterials)
r.sharedMaterials = sharedMaterials;
}
if(m_materialInstances.Count > 0) {
var mesh = new Mesh();
mesh.bounds = new Bounds(Vector3.zero, Vector3.one * 1000f);
mesh.hideFlags = HideFlags.HideAndDontSave;
var mf = gameObject.AddComponent<MeshFilter>();
mf.sharedMesh = mesh;
var mr = gameObject.AddComponent<MeshRenderer>();
mr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
mr.reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off;
mr.useLightProbes = false;
}
}
void OnEnable() {
AllocateTarget();
ToggleUniqueVariant();
}
void OnDisable() {
ReleaseTarget();
ToggleUniqueVariant();
}
void OnDestroy() {
var mf = GetComponent<MeshFilter>();
if(mf)
Object.DestroyImmediate(mf.sharedMesh);
if(m_shadowCamera)
Object.DestroyImmediate(m_shadowCamera.gameObject);
}
void OnValidate() {
if(!Application.isPlaying || !m_shadowCamera)
return;
ReleaseTarget();
AllocateTarget();
if(m_materialInstances != null)
for(int i = 0, n = m_materialInstances.Count; i < n; ++i)
SetStaticShaderUniforms(m_materialInstances[i]);
m_shadowCamera.cullingMask = useSceneCapture ? (LayerMask)~0 : inclusionMask;
SetFocus(m_activeFocus >= 0 ? m_activeFocus : startFocus);
ToggleUniqueVariant();
}
void AllocateTarget() {
m_shadowTexture = new RenderTexture((int)shadowMapSize >> m_downscale, (int)shadowMapSize >> m_downscale, 16, RenderTextureFormat.Shadowmap, RenderTextureReadWrite.Linear);
m_shadowTexture.filterMode = FilterMode.Bilinear;
m_shadowTexture.useMipMap = false;
m_shadowTexture.autoGenerateMips = false;
m_shadowCamera.targetTexture = m_shadowTexture;
}
void ReleaseTarget() {
m_shadowCamera.targetTexture = null;
Object.DestroyImmediate(m_shadowTexture);
m_shadowTexture = null;
}
void ToggleUniqueVariant() {
bool isEnable = m_lightSource;
for(int i = 0, n = m_materialInstances.Count; i < n; ++i) {
var m = m_materialInstances[i];
m.DisableKeyword("UNIQUE_SHADOW");
m.DisableKeyword("UNIQUE_SHADOW_LIGHT_COOKIE");
if(isEnable && m_shadowTexture) {
if(m_lightSource.cookie)
m.EnableKeyword("UNIQUE_SHADOW_LIGHT_COOKIE");
else
m.EnableKeyword("UNIQUE_SHADOW");
}
}
}
void SetStaticShaderUniforms(Material m) {
m.SetTexture("u_UniqueShadowTextureFakePoint", ms_shadowTextureFakePoint);
// We want the same 'softness' regardless of texture resolution.
var texelsInMap = (float)(int)shadowMapSize;
var relativeTexelSize = texelsInMap / 2048f;
m.SetVector("u_UniqueShadowFilterWidth", new Vector2(1f / (float)(int)shadowMapSize, 1f / (float)(int)shadowMapSize) * fallbackFilterWidth * relativeTexelSize);
var uniqueShadowBlockerWidth = relativeTexelSize * blockerSearchDistance / texelsInMap;
m.SetVector("u_UniqueShadowBlockerWidth", Vector4.one * uniqueShadowBlockerWidth);
// This needs to run each frame if we start using multiple foci.
var focus = shadowFoci[m_activeFocus];
var uniqueShadowBlockerDistanceScale = blockerDistanceScale * focus.radius * 0.5f / 10f; // 10 samples in shader
m.SetFloat("u_UniqueShadowBlockerDistanceScale", uniqueShadowBlockerDistanceScale);
var uniqueShadowLightWidth = new Vector2(lightNearSize, lightFarSize) * relativeTexelSize / texelsInMap;
m.SetVector("u_UniqueShadowLightWidth", uniqueShadowLightWidth);
}
bool EnsureLightSource() {
bool hadValidLight = m_lightSource;
bool hadCookie = m_lightSource && m_lightSource.cookie;
m_lightSource = UniqueShadowSun.instance;
// Only capture shadows from the light's culling mask.
if(useSceneCapture && m_lightSource && m_shadowCamera)
m_shadowCamera.cullingMask = m_lightSource.cullingMask;
return hadValidLight != m_lightSource || hadCookie != (m_lightSource && m_lightSource.cookie);
}
void UpdateAutoFocus(FocusSetup focus) {
if(!focus.autoFocus)
return;
var targetPos = focus.target.position + focus.target.right * focus.offset.x
+ focus.target.up * focus.offset.y + focus.target.forward * focus.offset.z;
var self = GetComponent<Renderer>();
var bounds = new Bounds(targetPos, Vector3.one * 0.1f);
foreach(var r in GetComponentsInChildren<Renderer>())
if(r != self)
bounds.Encapsulate(r.bounds);
focus.offset = bounds.center - focus.target.position;
focus.radius = focus.autoFocusRadiusBias + bounds.extents.magnitude;
}
void SetFocus(int idx) {
if(idx < 0 || idx >= shadowFoci.Length) {
Debug.LogError("Invalid active focus: " + m_activeFocus);
return;
}
m_activeFocus = idx;
var focus = shadowFoci[m_activeFocus];
UpdateAutoFocus(focus);
m_shadowCamera.orthographicSize = focus.radius;
m_shadowCamera.nearClipPlane = useSceneCapture ? -focus.sceneCaptureDistance : 0f;
m_shadowCamera.farClipPlane = focus.radius * 2f;
m_shadowCamera.projectionMatrix
= GL.GetGPUProjectionMatrix(Matrix4x4.Ortho(-focus.radius, focus.radius, -focus.radius, focus.radius, 0f, focus.radius * 2f), false);
var isD3D9 = SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Direct3D9;
var isD3D = isD3D9 || SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Direct3D11;
float to = isD3D9 ? 0.5f / (float)(int)shadowMapSize : 0f;
float zs = isD3D ? 1f : 0.5f, zo = isD3D ? 0f : 0.5f;
float db = -focus.depthBias;
m_shadowSpaceMatrix.SetRow(0, new Vector4(0.5f, 0.0f, 0.0f, 0.5f + to));
m_shadowSpaceMatrix.SetRow(1, new Vector4(0.0f, 0.5f, 0.0f, 0.5f + to));
m_shadowSpaceMatrix.SetRow(2, new Vector4(0.0f, 0.0f, zs, zo + db));
m_shadowSpaceMatrix.SetRow(3, new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
}
void UpdateFocus() {
var focus = shadowFoci[m_activeFocus];
var targetPos = focus.target.position + focus.target.right * focus.offset.x
+ focus.target.up * focus.offset.y + focus.target.forward * focus.offset.z;
var lightDir = m_lightSource.transform.forward;
var lightOri = m_lightSource.transform.rotation;
m_shadowCamera.transform.position = targetPos - lightDir * focus.radius;
m_shadowCamera.transform.rotation = lightOri;
//TODO: Texel snap? (probably doesn't matter too much since the targets are always animated)
var shadowViewMat = m_shadowCamera.worldToCameraMatrix;
var shadowProjMat = GL.GetGPUProjectionMatrix(m_shadowCamera.projectionMatrix, false);
m_shadowMatrix = m_shadowSpaceMatrix * shadowProjMat * shadowViewMat;
}
bool CheckVisibility(Camera cam) {
var focus = shadowFoci[m_activeFocus];
UpdateAutoFocus(focus);
var targetPos = focus.target.position + focus.target.right * focus.offset.x
+ focus.target.up * focus.offset.y + focus.target.forward * focus.offset.z;
var bounds = new Bounds(targetPos, Vector3.one * focus.radius * 2f);
return (targetPos - cam.transform.position).sqrMagnitude < (cullingDistance * cullingDistance)
&& GeometryUtility.TestPlanesAABB(GeometryUtility.CalculateFrustumPlanes(/*ms_cameraPlanes,*/ cam), bounds);
}
bool CheckCamera(Camera cam) {
if(cam == Camera.main)
return true;
#if UNITY_EDITOR
if(UnityEditor.SceneView.currentDrawingSceneView)
if(UnityEditor.SceneView.currentDrawingSceneView.camera == cam)
return true;
#endif
return false;
}
void OnWillRenderObject() {
if(EnsureLightSource())
ToggleUniqueVariant();
if(!m_lightSource)
return;
var cam = Camera.current;
if(!CheckCamera(cam))
return;
if(!CheckVisibility(cam))
return;
UpdateFocus();
var shadowDistance = QualitySettings.shadowDistance;
QualitySettings.shadowDistance = 0f;
m_shadowCamera.Render();
QualitySettings.shadowDistance = shadowDistance;
for(int i = 0, n = m_materialInstances.Count; i < n; ++i) {
var m = m_materialInstances[i];
m.SetTexture(ms_shadowTextureID, m_shadowTexture);
m.SetMatrix(ms_shadowMatrixID, m_shadowMatrix);
}
}
void OnDrawGizmosSelected() {
if(shadowFoci == null)
return;
foreach(var f in shadowFoci) {
if(f.target == null)
continue;
Gizmos.color = f.autoFocus ? Color.cyan : Color.green;
var p = f.target.position + f.target.right * f.offset.x + f.target.up * f.offset.y + f.target.forward * f.offset.z;
Gizmos.DrawWireSphere(p, f.radius + (f.autoFocus ? f.autoFocusRadiusBias : 0f));
}
}
}