using System; using System.Collections.Generic; using System.Reflection; using System.Threading; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.XR; namespace Assets.ThoMagic.Renderer { public class CameraRenderer : IDisposable { internal static int nextCameraRendererId = 0; int cameraRendererId; public static IInstanceRenderSettings ReflectionProbeSettings; public readonly Camera Camera; public IInstanceRenderSettings cameraSettings; private readonly bool _singlePassInstanced = false; private int cachedBufferHash; private Vector3 _origin; private Light _mainLight; List visibleStreams = new List(); List prefabData = new List(); List indirectDrawIndexedArgs = new List(); Dictionary objRenderDistanceCache = new Dictionary(); Dictionary appliedSettings = new Dictionary(); List cullableIndexes = new List(); public Camera ReferenceCamera { get; private set; } private CommandBuffer commandBuffer; private ComputeShader instancingShader; private ComputeBuffer prefabBuffer; private GraphicsBuffer indirectDrawIndexedArgsBuffer; private ComputeBuffer metaBuffer; private ComputeBuffer cullableIndexesBuffer; private ComputeBuffer visibleIndexesBuffer; private ComputeBuffer visibleShadowIndexesBuffer; private float cachedFov; private float cachedBias; private bool lodInvalid; SceneRenderSettings sceneSettings = new SceneRenderSettings(); private Plane[] _cachedFrustumPlanes; private Vector4[] _cachedShaderFrustumPlanes; internal bool instanceCountChanged = true; internal bool rebuildPrefabs = true; private int cullShaderId = 0, clearShaderId = 0; private uint maxInstancesCount = 0; private uint indexBufferOffset = 0; private int lastCamSettingsHash = 0; public CameraRenderer(Camera camera) { commandBuffer = new CommandBuffer(); cameraRendererId = Interlocked.Increment(ref nextCameraRendererId); instancingShader = UnityEngine.Object.Instantiate(Resources.Load("ThoMagic Renderer Instancing")); instancingShader.hideFlags = HideFlags.HideAndDontSave; cullShaderId = instancingShader.FindKernel("Cull_64"); clearShaderId = instancingShader.FindKernel("Clear_64"); instancingShader.name = $"ThoMagic Renderer Instancing - {camera.name}"; Camera = camera != null ? camera : throw new ArgumentNullException(nameof(camera)); cameraSettings = camera.GetComponent(); ReferenceCamera = camera; if (camera.cameraType == CameraType.Game || camera.cameraType == CameraType.VR) { _singlePassInstanced = XRSettings.enabled && (XRSettings.stereoRenderingMode == XRSettings.StereoRenderingMode.SinglePassInstanced || XRSettings.stereoRenderingMode == XRSettings.StereoRenderingMode.SinglePassMultiview); if (Application.isEditor && XRSettings.enabled && !_singlePassInstanced && XRSettings.loadedDeviceName == "MockHMD Display" && this.LoadMockHmdSetting() == "SinglePassInstanced") _singlePassInstanced = true; } } public void SetFloatingOrigin(in Vector3 origin) { _origin = origin; } public void SetReferenceCamera(Camera camera) { ReferenceCamera = camera != null ? camera : Camera; cameraSettings = ReferenceCamera.GetComponent(); } public void SetMainLight(Light light) => _mainLight = light; private void CalculateFrustumPlanes() { if (_cachedFrustumPlanes == null) _cachedFrustumPlanes = new Plane[6]; GeometryUtility.CalculateFrustumPlanes(ReferenceCamera, _cachedFrustumPlanes); if (_cachedShaderFrustumPlanes == null) _cachedShaderFrustumPlanes = new Vector4[6]; for (int index = 0; index < 6; ++index) _cachedShaderFrustumPlanes[index] = new Vector4(_cachedFrustumPlanes[index].normal.x, _cachedFrustumPlanes[index].normal.y, _cachedFrustumPlanes[index].normal.z, _cachedFrustumPlanes[index].distance); } public void Render() { //Used in editor if selected camera is not editor scene camera. Then only the culled instances of the selected camera is rendered. if (ReferenceCamera == null) SetReferenceCamera(Camera); if (_mainLight == null) _mainLight = FindMainLight(); if (_mainLight != null) { sceneSettings.HasMainLight = true; sceneSettings.HasMainLightShadows = _mainLight.shadows > 0; sceneSettings.MainLightDirection = _mainLight.transform.forward; } else { sceneSettings.HasMainLight = false; sceneSettings.HasMainLightShadows = false; } var finalSettings = InstanceRenderSettings.Default(ReferenceCamera); if (ReferenceCamera.cameraType == CameraType.Reflection && ReflectionProbeSettings != null) finalSettings.Merge(ReflectionProbeSettings.Settings); if (cameraSettings == null && Application.isEditor && !Application.isPlaying) cameraSettings = ReferenceCamera.GetComponent(); if (cameraSettings != null) finalSettings.Merge(cameraSettings.Settings); if(finalSettings.GetHashCode() != lastCamSettingsHash) { lastCamSettingsHash = finalSettings.GetHashCode(); rebuildPrefabs = true; } if (!rebuildPrefabs && Application.isEditor && !Application.isPlaying) { foreach (var renderObject in RendererPool.objectsList) { var settingsEditor = finalSettings; var renderObjectSettingsNew = renderObject.GameObject.GetComponent(); if (renderObjectSettingsNew != null) settingsEditor.Merge(renderObjectSettingsNew.Settings); if(!appliedSettings.ContainsKey(renderObject.prefabId) || settingsEditor.GetHashCode() != appliedSettings[renderObject.prefabId].GetHashCode()) { renderObject.Settings = renderObjectSettingsNew; rebuildPrefabs = true; } } } CalculateFrustumPlanes(); //Find visible streams for this frame visibleStreams.Clear(); foreach (var streamer in RendererPool.streamers.Values) if (streamer.IsInRange(ReferenceCamera, _cachedFrustumPlanes)) { streamer.UpdateForCamera(ReferenceCamera, _cachedFrustumPlanes); visibleStreams.Add(streamer); } bool prefabDataChanged = false; bool noNeedToUpdateIndirectIndexOffsets = false; //A prefab (object) has been added, removed or changed if (rebuildPrefabs) { noNeedToUpdateIndirectIndexOffsets = true; rebuildPrefabs = false; lodInvalid = false; cachedFov = ReferenceCamera.fieldOfView; cachedBias = QualitySettings.lodBias; appliedSettings.Clear(); indirectDrawIndexedArgs.Clear(); prefabData.Clear(); prefabData.AddRange(RendererPool.prefabData); maxInstancesCount = 0; indexBufferOffset = 0; uint batchIndex = 0; uint prefabCount = 0; uint indexArgsSize = 0; int totalIndirectArgsIndex = 0; foreach (var renderObject in RendererPool.objectsList) { var localPrefabData = prefabData[(int)renderObject.prefabId]; var finalObjSettings = finalSettings; if (renderObject.Settings != null) { finalObjSettings.Merge(renderObject.Settings.Settings); ValidateSettings(ref finalObjSettings); } CalculateCullingDistances(renderObject, finalObjSettings, cachedFov, cachedBias, renderObject.LodTransitions, renderObject.LodSizes, renderObject.lods); float distance1 = RelativeHeightToDistance(finalObjSettings.DensityInDistanceFalloff.x, renderObject.LodSizes[0], cachedFov); float distance2 = RelativeHeightToDistance(finalObjSettings.DensityInDistanceFalloff.y, renderObject.LodSizes[0], cachedFov); var densityInDistance = new Vector4(finalObjSettings.DensityInDistance, distance1, distance2 - distance1, finalObjSettings.ShadowDistance); localPrefabData.lodCount = renderObject.lodCount; localPrefabData.fadeLods = renderObject.fadeLods; localPrefabData.batchIndex = batchIndex; localPrefabData.indexBufferStartOffset = indexBufferOffset; localPrefabData.maxCount = renderObject.count; localPrefabData.densityInDistance = densityInDistance; renderObject.indirectArgsPerSubmeshOffsets.Clear(); indirectDrawIndexedArgs.AddRange(renderObject.indirectDrawIndexedArgs); for (int i = 0; i < renderObject.lodCount; i++) { renderObject.indirectArgsPerLodOffsets[i] = indexArgsSize; //All indirect arguments for this lod including shadows for (int z = 0; z < renderObject.indirectArgsPerLodWithShadowsCounts[i]; z++) { //Set batchoffset var idia = indirectDrawIndexedArgs[totalIndirectArgsIndex]; idia.startInstance = indexBufferOffset + (uint)(i * renderObject.count); indirectDrawIndexedArgs[totalIndirectArgsIndex] = idia; //** renderObject.indirectArgsPerSubmeshOffsets.Add(totalIndirectArgsIndex); totalIndirectArgsIndex++; indexArgsSize += 5; } } localPrefabData.SetLods(renderObject.lods, renderObject.indirectArgsPerLodOffsets, renderObject.indirectArgsPerLodCounts); prefabData[(int)renderObject.prefabId] = localPrefabData; appliedSettings[renderObject.prefabId] = finalObjSettings; batchIndex += renderObject.lodCount; indexBufferOffset += renderObject.count * renderObject.lodCount; maxInstancesCount += renderObject.count; prefabCount++; } //Nothing to draw yet if (maxInstancesCount == 0) { rebuildPrefabs = true; return; } if (metaBuffer == null || metaBuffer.count < maxInstancesCount) { metaBuffer?.Dispose(); int nextCount = RendererPool.RESIZE_COUNT * (((int)maxInstancesCount - 1) / RendererPool.RESIZE_COUNT + 1); metaBuffer = new ComputeBuffer(nextCount, sizeof(uint) * 4, ComputeBufferType.Structured); metaBuffer.name = $"ThoMagic metaBuffer ({Camera.name})"; cullableIndexesBuffer?.Dispose(); cullableIndexesBuffer = new ComputeBuffer(nextCount, sizeof(uint), ComputeBufferType.Structured); cullableIndexesBuffer.name = $"ThoMagic cullingIndexesBuffer ({Camera.name})"; } if (indirectDrawIndexedArgsBuffer == null || indirectDrawIndexedArgsBuffer.count < indirectDrawIndexedArgs.Count) { indirectDrawIndexedArgsBuffer?.Dispose(); int nextCount = RendererPool.RESIZE_COUNT * (((int)indirectDrawIndexedArgs.Count - 1) / RendererPool.RESIZE_COUNT + 1); indirectDrawIndexedArgsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, nextCount, GraphicsBuffer.IndirectDrawIndexedArgs.size); indirectDrawIndexedArgsBuffer.name = $"ThoMagic indirectDrawIndexedArgsBuffer ({Camera.name})"; } indirectDrawIndexedArgsBuffer.SetData(indirectDrawIndexedArgs); if (prefabBuffer == null || prefabBuffer.count < prefabCount) { prefabBuffer?.Dispose(); int nextCount = RendererPool.RESIZE_COUNT * (((int)prefabCount - 1) / RendererPool.RESIZE_COUNT + 1); prefabBuffer = new ComputeBuffer(nextCount, PrefabData.Stride, ComputeBufferType.Structured); prefabBuffer.name = $"ThoMagic prefabBuffer ({Camera.name})"; } if (visibleIndexesBuffer == null || visibleIndexesBuffer.count < indexBufferOffset) { visibleIndexesBuffer?.Dispose(); int nextCount = RendererPool.RESIZE_COUNT * (((int)indexBufferOffset - 1) / RendererPool.RESIZE_COUNT + 1); visibleIndexesBuffer = new ComputeBuffer(nextCount, sizeof(uint), ComputeBufferType.Structured); visibleIndexesBuffer.name = $"ThoMagic visibleIndexesBuffer ({Camera.name})"; visibleShadowIndexesBuffer?.Dispose(); visibleShadowIndexesBuffer = new ComputeBuffer(nextCount, sizeof(uint), ComputeBufferType.Structured); visibleShadowIndexesBuffer.name = $"ThoMagic visibleShadowIndexesBuffer ({Camera.name})"; }; prefabDataChanged = true; instanceCountChanged = true; } //Nothing to draw yet if (maxInstancesCount == 0) return; //Either instances have been added/removed or the visible streams have changed. //We need to rebuild the visible instances index buffer if (instanceCountChanged || BuffersChanged(visibleStreams, ref cachedBufferHash)) { if (instanceCountChanged) { if (!noNeedToUpdateIndirectIndexOffsets)//If already rebuild by prefab update, this is not needed { maxInstancesCount = 0; indexBufferOffset = 0; uint indexArgsSize = 0; int totalIndirectArgsIndex = 0; foreach (var renderObject in RendererPool.objectsList) { for (int i = 0; i < renderObject.lodCount; i++) { //All indirect arguments for this lod including shadows for (int z = 0; z < renderObject.indirectArgsPerLodWithShadowsCounts[i]; z++) { //Set batchoffset var idia = indirectDrawIndexedArgs[totalIndirectArgsIndex]; idia.startInstance = indexBufferOffset + (uint)(i * renderObject.count); indirectDrawIndexedArgs[totalIndirectArgsIndex] = idia; //** totalIndirectArgsIndex++; indexArgsSize += 5; } } var pd = prefabData[(int)renderObject.prefabId]; if (pd.maxCount != renderObject.count) prefabDataChanged = true; pd.indexBufferStartOffset = indexBufferOffset; pd.maxCount = renderObject.count; prefabData[(int)renderObject.prefabId] = pd; indexBufferOffset += renderObject.count * renderObject.lodCount; maxInstancesCount += renderObject.count; } if (metaBuffer == null || metaBuffer.count < maxInstancesCount) { metaBuffer?.Dispose(); int nextCount = RendererPool.RESIZE_COUNT * (((int)maxInstancesCount - 1) / RendererPool.RESIZE_COUNT + 1); metaBuffer = new ComputeBuffer(nextCount, sizeof(uint) * 4, ComputeBufferType.Structured); metaBuffer.name = $"ThoMagic metaBuffer ({Camera.name})"; cullableIndexesBuffer?.Dispose(); cullableIndexesBuffer = new ComputeBuffer(nextCount, sizeof(uint), ComputeBufferType.Structured); cullableIndexesBuffer.name = $"ThoMagic cullingIndexesBuffer ({Camera.name})"; } if (visibleIndexesBuffer == null || visibleIndexesBuffer.count < indexBufferOffset) { visibleIndexesBuffer?.Dispose(); int nextCount = RendererPool.RESIZE_COUNT * (((int)indexBufferOffset - 1) / RendererPool.RESIZE_COUNT + 1); visibleIndexesBuffer = new ComputeBuffer(nextCount, sizeof(uint), ComputeBufferType.Structured); visibleIndexesBuffer.name = $"ThoMagic visibleIndexesBuffer ({Camera.name})"; visibleShadowIndexesBuffer?.Dispose(); visibleShadowIndexesBuffer = new ComputeBuffer(nextCount, sizeof(uint), ComputeBufferType.Structured); visibleShadowIndexesBuffer.name = $"ThoMagic visibleShadowIndexesBuffer ({Camera.name})"; }; indirectDrawIndexedArgsBuffer.SetData(indirectDrawIndexedArgs); } } instanceCountChanged = false; cullableIndexes.Clear(); foreach (var stream in visibleStreams) { foreach (var objectInstanceIds in stream.objectInstanceIds.Values) { cullableIndexes.AddRange(objectInstanceIds); } } cullableIndexesBuffer.SetData(cullableIndexes); } //Nothing to render if (cullableIndexes.Count == 0 || RendererPool.globalInstanceBuffer == null) return; //Lod settings changed, update prefab lod data if (cachedFov != ReferenceCamera.fieldOfView || cachedBias != QualitySettings.lodBias || lodInvalid) { lodInvalid = false; cachedFov = ReferenceCamera.fieldOfView; cachedBias = QualitySettings.lodBias; foreach (var renderObject in RendererPool.objectsList) { CalculateCullingDistances(renderObject, appliedSettings[renderObject.prefabId], cachedFov, cachedBias, renderObject.LodTransitions, renderObject.LodSizes, renderObject.lods); float distance1 = RelativeHeightToDistance(appliedSettings[renderObject.prefabId].DensityInDistanceFalloff.x, renderObject.LodSizes[0], cachedFov); float distance2 = RelativeHeightToDistance(appliedSettings[renderObject.prefabId].DensityInDistanceFalloff.y, renderObject.LodSizes[0], cachedFov); var densityInDistance = new Vector4(appliedSettings[renderObject.prefabId].DensityInDistance, distance1, distance2 - distance1, appliedSettings[renderObject.prefabId].ShadowDistance); var pd = prefabData[(int)renderObject.prefabId]; pd.SetLods(renderObject.lods, renderObject.indirectArgsPerLodOffsets, renderObject.indirectArgsPerLodCounts); pd.densityInDistance = densityInDistance; prefabData[(int)renderObject.prefabId] = pd; } prefabDataChanged = true; } if(prefabDataChanged) prefabBuffer.SetData(prefabData); //Compute culling //Reset visible count of instances to zero /*instancingShader.SetInt("_Count", indirectDrawIndexedArgs.Count); instancingShader.SetBuffer(clearShaderId, "perCamIndirectArgumentsBuffer", indirectDrawIndexedArgsBuffer); instancingShader.Dispatch(clearShaderId, Mathf.CeilToInt(indirectDrawIndexedArgs.Count / 64.0f), 1, 1); //Cull instancingShader.SetBuffer(cullShaderId, "globalInstances", RendererPool.globalInstanceBuffer); instancingShader.SetBuffer(cullShaderId, "perCamPrefabs", prefabBuffer); instancingShader.SetBuffer(cullShaderId, "perCamMeta", metaBuffer); instancingShader.SetBuffer(cullShaderId, "perCamIndirectArgumentsBuffer", indirectDrawIndexedArgsBuffer); instancingShader.SetBuffer(cullShaderId, "perCamCullableIndexesBuffer", cullableIndexesBuffer); instancingShader.SetBuffer(cullShaderId, "perCamVisibleIndexesBuffer", visibleIndexesBuffer); instancingShader.SetBuffer(cullShaderId, "perCamShadowVisibleIndexesBuffer", visibleShadowIndexesBuffer); instancingShader.SetVector("_CameraPosition", ReferenceCamera.transform.position); instancingShader.SetVectorArray("_FrustumPlanes", _cachedShaderFrustumPlanes); instancingShader.SetVector("_ShadowDirection", sceneSettings.MainLightDirection); instancingShader.SetInt("_Count", cullableIndexes.Count); instancingShader.Dispatch(cullShaderId, Mathf.CeilToInt(cullableIndexes.Count / 64.0f), 1, 1);*/ commandBuffer.Clear(); commandBuffer.SetComputeIntParam(instancingShader, "_CountClear", indirectDrawIndexedArgs.Count); commandBuffer.SetComputeBufferParam(instancingShader, clearShaderId, "perCamIndirectArgumentsBuffer", indirectDrawIndexedArgsBuffer); commandBuffer.DispatchCompute(instancingShader, clearShaderId, Mathf.CeilToInt(indirectDrawIndexedArgs.Count / 64.0f), 1, 1); commandBuffer.SetComputeBufferParam(instancingShader, cullShaderId, "globalInstances", RendererPool.globalInstanceBuffer); commandBuffer.SetComputeBufferParam(instancingShader, cullShaderId, "perCamPrefabs", prefabBuffer); commandBuffer.SetComputeBufferParam(instancingShader, cullShaderId, "perCamMeta", metaBuffer); commandBuffer.SetComputeBufferParam(instancingShader, cullShaderId, "perCamIndirectArgumentsBuffer", indirectDrawIndexedArgsBuffer); commandBuffer.SetComputeBufferParam(instancingShader, cullShaderId, "perCamCullableIndexesBuffer", cullableIndexesBuffer); commandBuffer.SetComputeBufferParam(instancingShader, cullShaderId, "perCamVisibleIndexesBuffer", visibleIndexesBuffer); commandBuffer.SetComputeBufferParam(instancingShader, cullShaderId, "perCamShadowVisibleIndexesBuffer", visibleShadowIndexesBuffer); commandBuffer.SetComputeVectorParam(instancingShader, "_CameraPosition", ReferenceCamera.transform.position); commandBuffer.SetComputeVectorArrayParam(instancingShader, "_FrustumPlanes", _cachedShaderFrustumPlanes); commandBuffer.SetComputeVectorParam(instancingShader, "_ShadowDirection", sceneSettings.MainLightDirection); commandBuffer.SetComputeIntParam(instancingShader, "_CountCull", cullableIndexes.Count); commandBuffer.DispatchCompute(instancingShader, cullShaderId, Mathf.CeilToInt(cullableIndexes.Count / 64.0f), 1, 1); Graphics.ExecuteCommandBuffer(commandBuffer); var fence = Graphics.CreateGraphicsFence(GraphicsFenceType.AsyncQueueSynchronisation, SynchronisationStageFlags.ComputeProcessing); Graphics.WaitOnAsyncGraphicsFence(fence); //Render all prefabs (objects) foreach (var renderObject in RendererPool.objectsList) if (renderObject.count > 0)//Ignore if no instance is registered RenderObject(renderObject, appliedSettings[renderObject.prefabId]); } private void CalculateCullingDistances( ObjectData renderObject, InstanceRenderSettings finalSettings, float fieldOfView, float bias, float[] relativeTransitionHeight, float[] detailSize, Vector4[] lodData) { for (int index = 0; index < relativeTransitionHeight.Length; ++index) lodData[index] = new Vector4(index > 0 ? lodData[index - 1].y : 0.0f, RelativeHeightToDistance(relativeTransitionHeight[index], detailSize[index], fieldOfView) * bias, detailSize[index], 0.0f); var renderDistance = Mathf.Min(finalSettings.RenderDistance, RelativeHeightToDistance(renderObject.LodTransitions[renderObject.LodTransitions.Length - 1], renderObject.LodSizes[renderObject.LodTransitions.Length - 1], this.ReferenceCamera.fieldOfView, QualitySettings.lodBias)); objRenderDistanceCache[renderObject.GetHashCode()] = renderDistance; for (int index = 0; index < renderObject.LodSizes.Length; ++index) { renderObject.lods[index].x = Mathf.Min(renderObject.lods[index].x, renderDistance); renderObject.lods[index].y = Mathf.Min(renderObject.lods[index].y, renderDistance); } } public static float RelativeHeightToDistance( float relativeHeight, float size, float fieldOfView) { if (relativeHeight <= 0.0f || relativeHeight == float.MaxValue) return float.MaxValue; float num = Mathf.Tan((float)(Math.PI / 180.0 * (double)fieldOfView * 0.5)); return size * 0.5f / relativeHeight / num; } private void RenderObject( ObjectData obj, InstanceRenderSettings renderSettings) { if (LayerIsCulled(ReferenceCamera, obj.GameObject.layer) || (/*(!Application.isEditor || Application.isPlaying) &&*/ SceneIsCulled(ReferenceCamera, obj.GameObject))) return; if (!renderSettings.Render) return; foreach(var drawGroup in obj.drawGroups) { if (drawGroup == null) continue; drawGroup.SetInstances(RendererPool.globalInstanceBuffer, visibleIndexesBuffer, metaBuffer); drawGroup.Draw(Camera, obj, indirectDrawIndexedArgsBuffer); } if (!renderSettings.Shadows) return; foreach (var drawGroup in obj.drawShadowGroups) { if (drawGroup == null) continue; drawGroup.SetInstances(RendererPool.globalInstanceBuffer, visibleShadowIndexesBuffer, metaBuffer); drawGroup.Draw(Camera, obj, indirectDrawIndexedArgsBuffer); } } private void ValidateSettings(ref InstanceRenderSettings settings) { if (settings.RenderDistance <= 0.0f) settings.RenderDistance = this.ReferenceCamera.farClipPlane; if (settings.ShadowDistance > 0.0f) return; settings.ShadowDistance = settings.RenderDistance; } private bool LayerIsCulled(Camera camera, int layer) { if (camera.cullingMask != -1) { int num = layer; if ((camera.cullingMask & 1 << num) == 0) return true; } return false; } private bool SceneIsCulled(Camera camera, GameObject gameObject) { if (camera.scene != null && camera.scene.handle != 0 && camera.scene != gameObject.scene) return true; else return false; } private string LoadMockHmdSetting() { try { foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { Type type = assembly.GetType("Unity.XR.MockHMD.MockHMDBuildSettings", false); if (!(type == (Type)null)) { object obj = type.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue((object)null); return type.GetField("renderMode").GetValue(obj).ToString(); } } } catch { } return null; } private Light FindMainLight() { Light currentLight = null; foreach (Light otherLight in UnityEngine.Object.FindObjectsOfType()) { if (otherLight.type == LightType.Directional) { if (otherLight.shadows > 0) { currentLight = otherLight; break; } if (currentLight == null) currentLight = otherLight; else if (otherLight.intensity > currentLight.intensity) currentLight = otherLight; } } return currentLight; } private float RelativeHeightToDistance( float relativeHeight, float size, float fieldOfView, float bias) { if ((double)relativeHeight <= 0.0 || (double)relativeHeight == 3.40282346638529E+38) return float.MaxValue; float num = Mathf.Tan((float)(Math.PI / 180.0 * (double)fieldOfView * 0.5)); return size * 0.5f / relativeHeight / num * bias; } private bool BuffersChanged(List buffers, ref int cachedHash) { int hash = ComputeHash(buffers); if (hash == cachedHash) return false; cachedHash = hash; return true; } private int ComputeHash(List buffers) { if (buffers == null || buffers.Count == 0) return 0; int num = buffers[0].GetHashCode(); for (int index = 1; index < buffers.Count; ++index) num = HashCode.Combine(num, buffers[index].GetHashCode()); return num; } public void Dispose() { UnityEngine.Object.DestroyImmediate(instancingShader); prefabBuffer?.Dispose(); indirectDrawIndexedArgsBuffer?.Dispose(); metaBuffer?.Dispose(); cullableIndexesBuffer?.Dispose(); visibleIndexesBuffer?.Dispose(); visibleShadowIndexesBuffer?.Dispose(); commandBuffer?.Dispose(); prefabBuffer = null; indirectDrawIndexedArgsBuffer = null; metaBuffer = null; visibleIndexesBuffer = null; visibleShadowIndexesBuffer = null; } } }