void AddBounds(CachedShadowMapLight a_light) { var bounding_sphere = BoundsUtility.ComputeLightBoundingSphere(a_light: a_light); if (this._LightBoundingSpheres.ContainsKey(key: a_light.LightComponent)) { this._LightBoundingSpheres[key : a_light.LightComponent] = bounding_sphere; } else { this._LightBoundingSpheres.Add(key: a_light.LightComponent, value: bounding_sphere); } var light_bounds = BoundsUtility.ComputeLightBounds(light: a_light); if (this._LightBounds.ContainsKey(key: a_light.LightComponent)) { this._LightBounds[key : a_light.LightComponent] = light_bounds; } else { this._LightBounds.Add(key: a_light.LightComponent, value: light_bounds); } this.ReceiverCamera?.RefreshCullingGroup(); }
internal static bool DidDynamicObjectsChange(CachedShadowMapLight a_light, ref List <DynamicObject> dynamic_objects) { var changed = false; if (a_light && dynamic_objects.Count > 0) { for (var index = 0; index < dynamic_objects.Count; index++) { var o = dynamic_objects[index : index]; var dynamic_object = o; if (dynamic_object == null) { continue; } if (dynamic_object.ChangeEnableState || (dynamic_object.Moved && dynamic_object.isActiveAndEnabled && dynamic_object.gameObject.activeInHierarchy)) { changed = true; } } } return(changed); }
void EnsureCachedTextureExist(CachedShadowMapLight a_light) { if (this._CachedShadowMapTextures.ContainsKey(key: a_light.LightComponent)) { if (this._CachedShadowMapTextures[key : a_light.LightComponent].width == a_light.LightComponent.shadowCustomResolution && this._CachedShadowMapTextures[key : a_light.LightComponent].height == a_light.LightComponent.shadowCustomResolution && this.TextureTypeFits(a_light : a_light) && this._CachedShadowMapTextures[key : a_light.LightComponent].depth == (int)CachedShadowMapLight.MShadowMapBitDepth && this._CachedShadowMapTextures[key : a_light.LightComponent].IsCreated()) { return; } this._CachedShadowMapTextures[key : a_light.LightComponent].DiscardContents(); this._CachedShadowMapTextures[key : a_light.LightComponent].Release(); RenderTexture.DestroyImmediate(obj: this._CachedShadowMapTextures[key: a_light.LightComponent]); this._CachedShadowMapTextures.Remove(key: a_light.LightComponent); //this.CachedShadowMapTextures[aLight.LightComponent].MarkRestoreExpected(); } ; var shadow_custom_resolution = a_light.LightComponent.shadowCustomResolution; var cached_shadow_map = new RenderTexture(width: shadow_custom_resolution, height: shadow_custom_resolution, depth: (int)CachedShadowMapLight.MShadowMapBitDepth, format: RenderTextureFormat.Shadowmap) { antiAliasing = (int)AntiAliasingEnum.None_, filterMode = CachedShadowMapLight._MFilterMode, useMipMap = false, autoGenerateMips = false, wrapMode = TextureWrapMode.Clamp, memorylessMode = RenderTextureMemoryless.None, vrUsage = VRTextureUsage.None, useDynamicScale = false, name = $"Cached Shadow Map for #{a_light.GetInstanceID()}", anisoLevel = CachedShadowMapLight ._MAnisoLevel }; if (a_light.LightComponent.type == LightType.Point) { cached_shadow_map.dimension = TextureDimension.Cube; } cached_shadow_map.Create(); this._CachedShadowMapTextures[key : a_light.LightComponent] = cached_shadow_map; }
bool TextureTypeFits(CachedShadowMapLight a_light) { if (a_light.LightComponent.type == LightType.Point) { return(this._CachedShadowMapTextures[key : a_light.LightComponent].dimension == TextureDimension.Cube); } return(this._CachedShadowMapTextures[key : a_light.LightComponent].dimension == TextureDimension.Tex2D); }
/// <summary> /// Compute bounds of a point light, add the same(inflation) percentage to the range as when creating the light geometry. /// </summary> /// <param name="light"></param> /// <param name="inflation"></param> /// <returns></returns> public static Bounds ComputePointLightBounds(CachedShadowMapLight light, float inflation = _default_inflation) { var range = light.LightComponent.range; var dif = new Vector3(x: range, y: range, z: range) * inflation; var min_bounds = light.CachedPosition - dif; var max_bounds = light.CachedPosition + dif; var bounds = new Bounds(); bounds.SetMinMax(min: min_bounds, max: max_bounds); return(bounds); }
/// <summary> /// Compute bounds of a spot light /// </summary> /// <param name="light"></param> /// <param name="inflation"></param> /// <returns></returns> public static Bounds ComputeSpotLightBounds(CachedShadowMapLight light, float inflation = _default_inflation) { var transform = light.CachedTransform; var forward1 = transform.forward; var pos = light.CachedPosition + -forward1 * (inflation - 1.0f); var forward = forward1 * inflation; var right = transform.right * inflation; var up = transform.up * inflation; var far_center = pos + forward * light.LightComponent.range; var far_height = Mathf.Tan(f: light.LightComponent.spotAngle * 0.5f * Mathf.Deg2Rad) * light.LightComponent.range; var far_top_left = far_center + up * (far_height) - right * (far_height); var far_top_right = far_center + up * (far_height) + right * (far_height); var far_bottom_left = far_center - up * (far_height) - right * (far_height); var far_bottom_right = far_center - up * (far_height) + right * (far_height); var min_bounds = new Vector3(x: Mathf.Min(a: far_top_left.x, b: Mathf.Min(a: far_top_right.x, b: Mathf.Min(a: far_bottom_left.x, b: Mathf.Min(a: far_bottom_right.x, b: pos.x)))), y: Mathf.Min(a: far_top_left.y, b: Mathf.Min(a: far_top_right.y, b: Mathf.Min(a: far_bottom_left.y, b: Mathf.Min(a: far_bottom_right.y, b: pos.y)))), z: Mathf.Min(a: far_top_left.z, b: Mathf.Min(a: far_top_right.z, b: Mathf.Min(a: far_bottom_left.z, b: Mathf.Min(a: far_bottom_right.z, b: pos.z))))); var max_bounds = new Vector3(x: Mathf.Max(a: far_top_left.x, b: Mathf.Max(a: far_top_right.x, b: Mathf.Max(a: far_bottom_left.x, b: Mathf.Max(a: far_bottom_right.x, b: pos.x)))), y: Mathf.Max(a: far_top_left.y, b: Mathf.Max(a: far_top_right.y, b: Mathf.Max(a: far_bottom_left.y, b: Mathf.Max(a: far_bottom_right.y, b: pos.y)))), z: Mathf.Max(a: far_top_left.z, b: Mathf.Max(a: far_top_right.z, b: Mathf.Max(a: far_bottom_left.z, b: Mathf.Max(a: far_bottom_right.z, b: pos.z))))); var bounds = new Bounds(); bounds.SetMinMax(min: min_bounds, max: max_bounds); return(bounds); }
/// <summary> /// Disables the cached shadow map functionality for a single CachedShadowMapLight in system /// </summary> /// <param name="a_light"></param> protected internal virtual void RevertToDynamicLighting(CachedShadowMapLight a_light) { if (a_light && a_light.LightComponent) { a_light.LightComponent.enabled = true; } #if REMOVE_COMMANDBUFFERS_WHILE_NOT_ACTIVE if (this._captureCmdBuffers.ContainsKey(aLight.LightComponent)) { aLight.LightComponent.RemoveCommandBuffer(captureEvent, this._captureCmdBuffers[a_light.LightComponent]); this._captureCmdBuffers[a_light.LightComponent].Clear(); } if (this.BlitCmdBuffers.ContainsKey(aLight.LightComponent)) { if (mainCamera?._camera) { this.mainCamera._camera.RemoveCommandBuffer(blitEvent, this.BlitCmdBuffers[a_light.LightComponent]); } this.BlitCmdBuffers[a_light.LightComponent].Clear(); } #endif if (this._CachedShadowMapTextures.ContainsKey(key: a_light.LightComponent)) { this._CachedShadowMapTextures[key : a_light.LightComponent].DiscardContents(); this._CachedShadowMapTextures[key : a_light.LightComponent].Release(); //this.CachedShadowMapTextures[aLight.LightComponent].MarkRestoreExpected(); } if (this._ActivelyCachedTextures.ContainsKey(key: a_light.LightComponent)) { this._ActivelyCachedTextures.Remove(key: a_light.LightComponent); } if (this._ActivelyCachedLights.Contains(item: a_light.LightComponent)) { this._ActivelyCachedLights.Remove(item: a_light.LightComponent); } if (this._ActivelyCachedShadowMaps.Contains(item: a_light)) { this._ActivelyCachedShadowMaps.Remove(item: a_light); } #if UNITY_EDITOR && CACHED_SHADOW_MAP_DEBUG this.UpdateEditorDebugInfo(); #endif }
void RemoveBounds(CachedShadowMapLight a_light) { if (this._LightBoundingSpheres.ContainsKey(key: a_light.LightComponent)) { this._LightBoundingSpheres.Remove(key: a_light.LightComponent); } if (this._LightBounds.ContainsKey(key: a_light.LightComponent)) { this._LightBounds.Remove(key: a_light.LightComponent); } this.ReceiverCamera?.RefreshCullingGroup(); }
internal static bool DidIntersectingDynamicsObjectChange(CachedShadowMapLight a_light, ref List <DynamicObject> dynamic_objects, bool only_when_intersecting = true) { var changed = false; if (a_light) { for (var index = 0; index < dynamic_objects.Count; index++) { var dynamic_object = dynamic_objects[index : index]; var intersects = false; if (dynamic_object) { switch (a_light.LightComponent.type) { case LightType.Spot: intersects = ConeSphereIntersection(spot_light: a_light, dynamic_object: dynamic_object); break; case LightType.Point: intersects = SphereSphereIntersection(point_light: a_light, dynamic_object: dynamic_object); break; case LightType.Directional: case LightType.Area: case LightType.Disc: break; default: throw new ArgumentOutOfRangeException(); } if (intersects || !only_when_intersecting) { if (dynamic_object.ChangeEnableState || (dynamic_object.Moved && dynamic_object.isActiveAndEnabled && dynamic_object.gameObject.activeInHierarchy)) { changed = true; } } } } } return(changed); }
/// <summary> /// see https://www.geometrictools.com/Documentation/IntersectionSphereCone.pdf /// </summary> /// <param name="spot_light"></param> /// <param name="dynamic_object"></param> /// <returns></returns> public static bool ConeSphereIntersection(CachedShadowMapLight spot_light, DynamicObject dynamic_object) { // FIXME Optimize computations of boundingSphereRadius^2, 1.0f / Mathf.Sin(angle * 0.5f), Mathf.Sin(angle * 0.5f)^2 and Mathf.Cos(angle * 0.5f)^2 var sin = Mathf.Sin(f: spot_light.LightComponent.spotAngle * 0.5f * Mathf.Deg2Rad); var cos = Mathf.Cos(f: spot_light.LightComponent.spotAngle * 0.5f * Mathf.Deg2Rad); var offset = (dynamic_object.boundingSphereRadius / sin); var u = spot_light.CachedPosition - offset * spot_light.CachedForward; // Assumes unit length forward. var d = dynamic_object.CachedPosition + dynamic_object.offset - u; var distance = Vector3.Dot(lhs: spot_light.CachedForward, rhs: d); // Assumes unit length forward. return(distance >= d.magnitude * cos && distance <= offset + spot_light.LightComponent.range + dynamic_object.boundingSphereRadius); }
/// <summary> /// Removes a cachedShadowMapLight from the system, removes, disposes, releases CommandBuffers and RenderTextures for the light /// </summary> /// <param name="a_light"></param> public void RemoveLightFromSystem(CachedShadowMapLight a_light) { #if !REMOVE_COMMANDBUFFERS_WHILE_NOT_ACTIVE if (this._CaptureCmdBuffers.ContainsKey(key: a_light.LightComponent)) { a_light.LightComponent.RemoveCommandBuffer(evt: this._capture_event, buffer: this._CaptureCmdBuffers[key: a_light .LightComponent]); } if (this._BlitCmdBuffers.ContainsKey(key: a_light.LightComponent)) { this.ReceiverCamera?._camera.RemoveCommandBuffer(evt: this._blit_event, buffer: this._BlitCmdBuffers[key: a_light .LightComponent]); } #endif this.RevertToDynamicLighting(a_light: a_light); this.RemoveBounds(a_light: a_light); this._AllCachedLightComponents.Remove(item: a_light); // Remove and dispose command buffers. if (this._CaptureCmdBuffers.ContainsKey(key: a_light.LightComponent)) { var shadow_map_cache_buffer = this._CaptureCmdBuffers[key : a_light.LightComponent]; this._CaptureCmdBuffers.Remove(key: a_light.LightComponent); shadow_map_cache_buffer.Dispose(); } if (this._BlitCmdBuffers.ContainsKey(key: a_light.LightComponent)) { var lighting_buffer = this._BlitCmdBuffers[key : a_light.LightComponent]; this._BlitCmdBuffers.Remove(key: a_light.LightComponent); lighting_buffer.Dispose(); } // Remove and release render texture. if (this._CachedShadowMapTextures.ContainsKey(key: a_light.LightComponent)) { this._CachedShadowMapTextures[key : a_light.LightComponent].Release(); RenderTexture.DestroyImmediate(obj: this._CachedShadowMapTextures[key: a_light.LightComponent]); this._CachedShadowMapTextures.Remove(key: a_light.LightComponent); } }
/// <summary> /// Compute bounding sphere of a spot light /// See illustration at https://www.mathalino.com/sites/default/files/users/Mathalino/differential-calculus/063-cone-inscribed-in-sphere.jpg /// </summary> /// <param name="light"></param> /// <param name="inflation"></param> /// <returns></returns> public static BoundingSphere ComputeSpotLightBoundingSphere(CachedShadowMapLight light, float inflation = _default_inflation) { if (light.LightComponent.spotAngle < 90.0f) { var r = Mathf.Tan(f: light.LightComponent.spotAngle * 0.5f * Mathf.Deg2Rad) * light.LightComponent.range; var h = light.LightComponent.range; var a = (r * r + h * h) / (2.0f * h); return(new BoundingSphere(pos: light.CachedPosition + light.CachedForward * a, rad: a * inflation)); } else { var r = Mathf.Tan(f: light.LightComponent.spotAngle * 0.5f * Mathf.Deg2Rad) * light.LightComponent.range; return(new BoundingSphere(pos: light.CachedPosition + light.CachedForward * light.LightComponent.range, rad: r * inflation)); } }
internal static void RemoveNonOverlapping(CachedShadowMapLight a_light, ref List <DynamicObject> dynamic_objects) { if (dynamic_objects.Count < 1 || !a_light) { return; } for (var i = dynamic_objects.Count - 1; i >= 0; i--) { if (NotIntersecting(a_light : a_light, dynamic_object : dynamic_objects[index : i])) { dynamic_objects.RemoveAt(index: i); } } // dynamic_objects.RemoveAll(o=>NotIntersecting(aLight,o)); // GENERATES GARBAGE! }
/// <summary> /// Compute bounds of light /// </summary> /// <param name="light"></param> /// <param name="inflation"></param> /// <returns></returns> public static Bounds ComputeLightBounds(CachedShadowMapLight light, float inflation = _default_inflation) { if (light) { switch (light.LightComponent.type) { case LightType.Spot: { return(ComputeSpotLightBounds(light: light, inflation: inflation)); } case LightType.Point: { return(ComputePointLightBounds(light: light, inflation: inflation)); } } } return(new Bounds()); }
/// <summary> /// Compute bounding sphere of light /// </summary> /// <param name="a_light"></param> /// <param name="inflation"></param> /// <returns></returns> public static BoundingSphere ComputeLightBoundingSphere(CachedShadowMapLight a_light, float inflation = _default_inflation) { if (a_light.LightComponent) { switch (a_light.LightComponent.type) { case LightType.Spot: { return(ComputeSpotLightBoundingSphere(light: a_light, inflation: inflation)); } case LightType.Point: { return(new BoundingSphere(pos: a_light.CachedPosition, rad: a_light.LightComponent.range * inflation)); } } } return(new BoundingSphere()); }
void EnsureCaptureCmdBufferExist(CachedShadowMapLight a_light) { if (!this._CaptureCmdBuffers.ContainsKey(key: a_light.LightComponent)) { var s = new CommandBuffer { name = $"Shadow Map Cache #{unchecked(this._capture_id++)} #CID{a_light.GetInstanceID()}" }; //unchecked wraps integer instead of throwing exception a_light.LightComponent.AddCommandBuffer(evt: this._capture_event, buffer: s); this._CaptureCmdBuffers.Add(key: a_light.LightComponent, value: s); } a_light.LightComponent.RemoveCommandBuffer(evt: this._capture_event, buffer: this._CaptureCmdBuffers [key: a_light.LightComponent]); a_light.LightComponent.AddCommandBuffer(evt: this._capture_event, buffer: this._CaptureCmdBuffers[key: a_light.LightComponent]); }
void EnsureBlitCmdBufferExist(CachedShadowMapLight a_light) { if (!this._BlitCmdBuffers.ContainsKey(key: a_light.LightComponent)) { var lighting_buffer = new CommandBuffer { name = $"Cached Shadow Map #{unchecked(this._blit_id++)} #CID{a_light.LightComponent.GetInstanceID()}" }; //unchecked wraps integer instead of throwing exception this._BlitCmdBuffers.Add(key: a_light.LightComponent, value: lighting_buffer); } this.ReceiverCamera?._camera.RemoveCommandBuffer(evt: this._blit_event, buffer: this._BlitCmdBuffers[key: a_light .LightComponent]); this.ReceiverCamera?._camera.AddCommandBuffer(evt: this._blit_event, buffer: this._BlitCmdBuffers [key: a_light.LightComponent]); }
/// <summary> /// Enqueue a CachedShadowMapLight to be cached /// </summary> /// <param name="a_light"></param> public void EnqueueDirtyCandidate(CachedShadowMapLight a_light) { if (this.DirtyCachedLights.Contains(item: a_light)) { return; } if (a_light && a_light.LightComponent) { #if CACHED_SHADOW_MAP_DEBUG if (!aLight.isActiveAndEnabled) { Debug.LogError("Light was not isActiveAndEnabled", aLight); } if (this.DirtyCachedLights.Contains(aLight)) { Debug.LogError("Light was already enqueued", aLight); } #endif this.DirtyCachedLights.Enqueue(item: a_light); } }
static bool NotIntersecting(CachedShadowMapLight a_light, DynamicObject dynamic_object) { if (dynamic_object && dynamic_object.isActiveAndEnabled && dynamic_object.gameObject.activeInHierarchy) { switch (a_light.LightComponent.type) { case LightType.Spot: return(!ConeSphereIntersection(spot_light: a_light, dynamic_object: dynamic_object)); case LightType.Point: return(!SphereSphereIntersection(point_light: a_light, dynamic_object: dynamic_object)); case LightType.Directional: case LightType.Area: case LightType.Disc: break; default: throw new ArgumentOutOfRangeException(); } } return(true); }
/// <summary> /// /// </summary> /// <param name="point_light"></param> /// <param name="dynamic_object"></param> /// <returns></returns> public static bool SphereSphereIntersection(CachedShadowMapLight point_light, DynamicObject dynamic_object) { var diff = dynamic_object.CachedPosition + dynamic_object.offset - point_light.CachedPosition; return(diff.magnitude - dynamic_object.boundingSphereRadius <= point_light.LightComponent.range); }
/// <summary> /// Adds a cachedShadowMapLight to the system, sets up a renderTexture to be rendered into by the light /// </summary> /// <param name="a_light"></param> public void AddLightToSystem(CachedShadowMapLight a_light) { this._AllCachedLightComponents.Add(item: a_light); this.EnsureCachedTextureExist(a_light: a_light); }
protected internal override void RevertToDynamicLighting(CachedShadowMapLight aLight) { base.RevertToDynamicLighting(aLight); HxVolumetricCamera.SetCachedShadowMaps(ActivelyCachedLights, ActivelyCachedTextures); }
private protected override void ComputeCachedShadowMap(CachedShadowMapLight light, float margin = 2) { base.ComputeCachedShadowMap(light, margin); HxVolumetricCamera.SetCachedShadowMaps(ActivelyCachedLights, ActivelyCachedTextures); }
private protected virtual void ComputeCachedShadowMap(CachedShadowMapLight a_light, float margin = 2f) { if (!a_light || !a_light.isActiveAndEnabled || !a_light.LightComponent) { #if CACHED_SHADOW_MAP_DEBUG Debug.LogWarning($"light was null!!!", aLight); #endif this.RevertToDynamicLighting(a_light: a_light); return; } //Debug.Log("Updating CSM " + aLight.name, aLight); this.EnsureCaptureCmdBufferExist(a_light: a_light); this.EnsureCachedTextureExist(a_light: a_light); var capture_cmd_buffer = this._CaptureCmdBuffers[key : a_light.LightComponent]; a_light.LightComponent.enabled = true; // Make sure the light source is on while we render using the caching camera var cull_d = a_light.LightComponent.layerShadowCullDistances; a_light.LightComponent.layerShadowCullDistances = null; var lrm = a_light.LightComponent.renderMode; a_light.LightComponent.renderMode = LightRenderMode.ForcePixel; var lscm = a_light.LightComponent.lightShadowCasterMode; a_light.LightComponent.lightShadowCasterMode = LightShadowCasterMode.Everything; #if PRERENDER_CONTEXT #if PRECLEAR_SHADOW_MAP captureCmdBuffer.SetRenderTarget(this.CachedShadowMapTextures[aLight.LightComponent]); captureCmdBuffer.ClearRenderTarget(true, false, Color.black); #endif this.camera.CacheShadowMapOfLight(aLight, margin); captureCmdBuffer.Clear(); #endif #if SHADOW_MAP_UPDATE_CHECK var last_update_count = this._CachedShadowMapTextures[key : a_light.LightComponent].updateCount; capture_cmd_buffer.IncrementUpdateCount(dest: this._CachedShadowMapTextures [key: a_light.LightComponent]); #endif capture_cmd_buffer.CopyTexture(src: BuiltinRenderTextureType.CurrentActive, dst: this._CachedShadowMapTextures[key: a_light.LightComponent]); this.cachingCamera.CacheShadowMapOfLight(a_light: a_light, margin: margin); // Reset camera and set light specific parameters. capture_cmd_buffer.Clear(); a_light.LightComponent.renderMode = lrm; a_light.LightComponent.layerShadowCullDistances = cull_d; a_light.LightComponent.lightShadowCasterMode = lscm; #if SHADOW_MAP_UPDATE_CHECK if (last_update_count == this._CachedShadowMapTextures[key : a_light.LightComponent].updateCount) { Debug.LogError("CachedShadowMap was not updated!"); this.RevertToDynamicLighting(a_light: a_light); this.EnqueueDirtyCandidate(a_light: a_light); return; } #endif #if SHADOW_MAP_SANITY_CHECK if (ShadowMapChecking.SanityCheck(CachedShadowMapTextures[aLight.LightComponent]) && CachedShadowMapTextures[aLight.LightComponent].IsCreated()) { RevertToDynamicLighting(aLight); EnqueueDirtyCandidate(aLight); return; } #endif this.AddBounds(a_light: a_light); this.EnsureBlitCmdBufferExist(a_light: a_light); if (!this._ActivelyCachedLights.Contains(item: a_light.LightComponent)) { this._ActivelyCachedLights.Add(item: a_light.LightComponent); } if (!this._ActivelyCachedShadowMaps.Contains(item: a_light)) { this._ActivelyCachedShadowMaps.Add(item: a_light); } if (!this._ActivelyCachedTextures.ContainsKey(key: a_light.LightComponent)) { this._ActivelyCachedTextures.Add(key: a_light.LightComponent, value: this._CachedShadowMapTextures[key: a_light.LightComponent]); } a_light.LightComponent.enabled = false; #if UNITY_EDITOR && CACHED_SHADOW_MAP_DEBUG this.UpdateEditorDebugInfo(); #endif }