using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Unity.Mathematics; using UnityEngine; using UnityEditor; using UnityEngine.SceneManagement; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering.Universal; using System.IO; using Unity.Collections; using SLZ.SLZEditorTools; using UnityEngine.Rendering; using System.Diagnostics; using UnityEditor.SceneManagement; #if UNITY_EDITOR_WIN using Microsoft.Win32; #endif using Debug = UnityEngine.Debug; public class VolumetricBaking : EditorWindow { // Add menu item named "My Window" to the Window menu [MenuItem("Stress Level Zero/Volumetric Baking")] public static void ShowWindow() { //Show existing window instance. If one doesn't exist, make one. GetWindow(typeof(VolumetricBaking)); SetGPUTimeout(); // BuildComboList(); // BuildSelectionGrid(); } static void SetGPUTimeout() { #if UNITY_EDITOR_WIN const string key = "HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\GraphicsDrivers"; const string value = "TdrDelay"; object currentValueBoxed = Registry.GetValue(key, value, null); int currentValue = currentValueBoxed != null ? (int)currentValueBoxed : 2; //EditorPrefs.SetBool("VolBakeDontShowGPUTimeoutWarning", false); if (!EditorPrefs.GetBool("VolBakeDontShowGPUTimeoutWarning", false) && currentValue < 30) { int allowKey = EditorUtility.DisplayDialogComplex("Increase GPU timeout", "This tool needs to set a Windows registry key to increase the time the GPU is allowed to take " + "processing graphics jobs in a single frame. Otherwise Windows will consider the GPU to be stalled out and will kill " + "Unity mid-bake.\n\n Key: " + key + "\\" + value + "\n\nIncrease the timeout from the default (2 seconds) to 30s?\n", "Proceed", "Cancel", "Cancel - Don't Show Again" ); bool proceed = allowKey == 0; //if (allowKey == 1) //{ // proceed = !EditorUtility.DisplayDialog("Don't increase timeout", "Are you sure? If the timeout period isn't increased, unity is almost guaranteed to crash when baking volumetrics." + // " Increasing the value of this key will not cause any issues, it only means that Windows will wait 30 seconds before killing applications that have actually experienced a GPU crash.", // "Don't increase timeout", // "Increase timeout"); // //} if (allowKey == 2) { int choice2 = EditorUtility.DisplayDialogComplex("Don't increase timeout", "Are you sure? If the timeout period isn't increased, unity is almost guaranteed to crash when baking volumetrics." + "\n\nIncreasing the value of this key should not cause any issues, it only means that Windows will wait 30 seconds before killing applications that have actually experienced a GPU crash. " + "\n\nThis will never ask you again! Only do this if you know what you're doing!", "Don't increase timeout and never ask again", "Don't increase timeout", "Increase timeout"); proceed = choice2 == 2; if (choice2 == 0) { EditorPrefs.SetBool("VolBakeDontShowGPUTimeoutWarning", true); } } if (proceed) { // Registry.SetValue(key, value, 30, RegistryValueKind.DWord); System.Diagnostics.Process process = new System.Diagnostics.Process(); System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo(); startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal; startInfo.FileName = "cmd.exe"; startInfo.Arguments = string.Format("/C reg add {0} /v {1} /t REG_DWORD /d 30 /f", key, value); //Debug.Log(startInfo.Arguments); startInfo.Verb = "runas"; process.StartInfo = startInfo; process.Start(); process.WaitForExit(); if (process.ExitCode != 0) { Debug.LogError("Setting registry key failed"); } //Debug.Log("Volumetric Baking: Set GPU Timeout Registry Key to 30 seconds (" + key + "\\" + value + ")"); } } Debug.Log("Volumetric Baking: GPU Timeout Registry Key value is " + currentValue + " seconds (" + key + "\\" + value + ")"); #endif } //TODO: Save to scene asset file //Public variables [Range(1, 2048)] public int AreaLightSamples = 256; [Range(4, 128), Tooltip("Size of the render buckets.")] public int BucketSize = 32; public bool DXRAcceletration = true; public bool SkyboxContribution = false; public Cubemap CustomEnvorment; public int EnvLightSamples = 2048; public int RayChunkSize = 2048; public float VolExposure = 0.05f; bool checkedD3D12 = false; bool isD3D12 = false; //interal bool saveWarning = false; bool Running = false; private void OnGUI() { DisplayProgress(); EditorGUILayout.LabelField("System Settings", EditorStyles.boldLabel); GUI.enabled = !DXRAcceletration; BucketSize = EditorGUILayout.IntSlider("Bucket Size", BucketSize, 4, 256); GUI.enabled = true; VolExposure = EditorGUILayout.Slider("Debug Exposure", RefreshExposure(VolExposure), 0, 2); DXRAcceletration = EditorGUILayout.Toggle("DXR Acceletration", DXRAcceletration); RayChunkSize = EditorGUILayout.IntSlider("DXR Ray Chunk Size", RayChunkSize, 256, 8192); EditorGUILayout.Space(); EditorGUILayout.LabelField("Light Settings", EditorStyles.boldLabel); AreaLightSamples = EditorGUILayout.IntSlider("Area Samples", AreaLightSamples, 1, 1024); EditorGUILayout.Space(); GUI.enabled = DXRAcceletration; EditorGUILayout.LabelField("Enviorment Settings", EditorStyles.boldLabel); SkyboxContribution = EditorGUILayout.Toggle("Skybox Contribution", SkyboxContribution); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Custom Skybox", EditorStyles.label); CustomEnvorment = (Cubemap)EditorGUILayout.ObjectField(CustomEnvorment, typeof(Cubemap), true); EditorGUILayout.EndHorizontal(); EnvLightSamples = EditorGUILayout.IntSlider("Environmental Samples", EnvLightSamples, 1, 8192 ); ; GUI.enabled = true; // EditorGUILayout.IntField(AreaLightSamples, "Area light samples" ); if (GUILayout.Button("Bake Volumetrics")) { ClearWarning(); if (VerifySettings() == false) return; //Check settings and return if something is wrong if (DXRAcceletration) { BakeDXR(); } else { RebuildMeshObjectBuffers(); BakeLights(); ReleaseBuffers(); } }; EditorGUILayout.LabelField(BakingStatus); WarningGUI(); if (saveWarning) if (GUILayout.Button("Save scene(s)")) SaveScenes(); if (!checkedD3D12) { checkedD3D12 = true; isD3D12 = SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Direct3D12; } if (!isD3D12) { if (GUILayout.Button("Restart Editor in DX12")) { if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) { Process unity = new Process(); string projectPath = Path.GetDirectoryName(Application.dataPath); unity.StartInfo.FileName = EditorApplication.applicationPath; unity.StartInfo.Arguments = "-projectPath \"" + projectPath + "\" -force-d3d12"; unity.Start(); EditorApplication.Exit(0); } } } // EditorGUILayout.HelpBox("Some warning text", MessageType.Warning); //Todo: add warning box to window } float RefreshExposure(float Exposure) { Shader.SetGlobalFloat("_VolExposure", Exposure); return Exposure; } struct WarningStatus { public bool Display; public string Text; } WarningStatus warningStatus; void WarningGUI() { if (warningStatus.Display == false) return; EditorGUILayout.HelpBox(warningStatus.Text, MessageType.Warning); } struct Progress{ public string title; public string info; public float percent; } Progress progress; double ProgressTimeStart; void DisplayProgress() { if (Running) { EditorUtility.DisplayProgressBar(progress.title + EditorApplication.timeSinceStartup, progress.info, progress.percent); } } private void UpdateProgress(string title, string info, float percent) { progress.title = title; progress.info = info; progress.percent = percent; DisplayProgress(); } string BakingStatus; void UpdateStatus(string Status) { BakingStatus = Status; Debug.Log(Status); Repaint(); } void UpdateWarning(string text) { warningStatus.Display = true; warningStatus.Text = text; Debug.LogWarning(warningStatus.Text); } void ClearWarning() { warningStatus.Display = false; warningStatus.Text = null; saveWarning = false; } bool VerifySettings() { if (DXRAcceletration && SystemInfo.graphicsDeviceType != UnityEngine.Rendering.GraphicsDeviceType.Direct3D12) { UpdateWarning("DirectX 12 not in use. DXR Acceletration requires it."); return false; } if (VolumetricRegisters.volumetricAreas.Count < 1) //Checking volumes { UpdateWarning("No volumetric areas in the scene. Nothing to bake."); return false; } if (AreScenesDirty() == true) //Checking if scenes are dirty then ask to save { bool notCancelled = UnityEditor.SceneManagement.EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo(); if (notCancelled) { if (AreScenesDirty()) //checking AGAIN because only sets to false when cancelled { UpdateWarning("Save your scene before baking."); //This is not techincally required, but it helps save progress incase of crash saveWarning = true; return false; } } else { return false; //cancelled } } if (SceneManager.sceneCount > 1) //Checking scene count { UpdateWarning("More than one scene open. Saving to active scene."); } return true; //everything checks out } bool AreScenesDirty() { for (int i = 0; i < SceneManager.sceneCount; i++) { if (SceneManager.GetSceneAt(i).isDirty) return true; } return false; } void SaveScene() { Scene scene = SceneManager.GetActiveScene(); UnityEditor.SceneManagement.EditorSceneManager.SaveScene(scene); ClearWarning(); } void SaveScenes() { for (int i = 0; i < SceneManager.sceneCount; i++) { Scene currentS = SceneManager.GetSceneAt(i); if (currentS.isDirty) { UnityEditor.SceneManagement.EditorSceneManager.SaveScene(currentS); Debug.Log("Saved " + currentS.name); } } AssetDatabase.SaveAssets(); ClearWarning(); } // public int tex1Res = 64; // ComputeShader BakingShader; // Baking shader // ComputeShader slicer; //Slicer shader for saving //public Light[] BakeLights; // public Vector3 DirectionalLight = new Vector3(0,1,0); public Vector3 Size = Vector3.one; public float AmbientMedia = 0; public GameObject[] Spheres; //Data Lists public struct LightStruct { public Color[] color; public float[] extinction; public Vector3[] Position; } /// /// Triangle Object /// struct MeshObject { public Matrix4x4 localToWorldMatrix; public int indices_offset; public int indices_count; } private static List _meshObjects = new List(); private static List _vertices = new List(); private static List _indices = new List(); private ComputeBuffer _meshObjectBuffer; private ComputeBuffer _vertexBuffer; private ComputeBuffer _indexBuffer; struct Debuger { public int DebugCcounter; } /////////////// Unused public void BakeVolumetrics() { System.DateTime startTime = System.DateTime.Now; UpdateStatus("Baking " + SceneManager.GetActiveScene().name); //Make RT ComputeShader BakingShader = AssetDatabase.LoadAssetAtPath("Assets/VolumetricFog/Shaders/VolumetricBaking.compute"); //float displayProgress = 0; //EditorUtility.DisplayProgressBar("Baking Volumetrics: ", "Shows a progress bar for the given seconds", displayProgress); for (int j = 0; j < VolumetricRegisters.volumetricAreas.Count; j++) { // EditorUtility.DisplayProgressBar("Baking Volumetrics... ", VolumetricRegisters.volumetricAreas[j].name, (float)j / (float)VolumetricRegisters.volumetricAreas.Count ); Vector3Int Texels = VolumetricRegisters.volumetricAreas[j].NormalizedTexelDensity; RenderTextureDescriptor rtdiscrpt = new RenderTextureDescriptor(); rtdiscrpt.enableRandomWrite = true; rtdiscrpt.dimension = UnityEngine.Rendering.TextureDimension.Tex3D; rtdiscrpt.width = Texels.x; rtdiscrpt.height = Texels.y; rtdiscrpt.volumeDepth = Texels.z; rtdiscrpt.graphicsFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.R16G16B16A16_SFloat; //R32G32B32A32_SFloat is excessive, the compressed formats won't befefit and even lightmaps are saved as 16 bit EXRs rtdiscrpt.msaaSamples = 1; RenderTexture RT3d = new RenderTexture(rtdiscrpt); RT3d.Create(); // Color[] colorstring = new Color[RT3d.width * RT3d.height * RT3d.depth]; //for (int c = 0; c < colorstring.Length; c++) colorstring[c] = Color.cyan; //Setup light data Light[] PointLights = GatherBakedLights(LightType.Point); Light[] DirectionalLights = GatherBakedLights(LightType.Directional); Vector4[] lightColors = new Vector4[PointLights.Length]; Vector4[] lightPos = new Vector4[PointLights.Length]; for (int i = 0; i < PointLights.Length; i++) { lightColors[i] = PointLights[i].color * PointLights[i].intensity; lightPos[i] = PointLights[i].transform.position; } //participatingMediaEntities = FindObjectsOfType(); Vector4[] mediaPos = new Vector4[VolumetricRegisters.VolumetricMediaEntities.Count]; Vector4[] mediaAbs = new Vector4[VolumetricRegisters.VolumetricMediaEntities.Count]; for (int i = 0; i < mediaPos.Length; i++) { mediaPos[i] = VolumetricRegisters.VolumetricMediaEntities[i].transform.position; // mediaAbs[i] = new Vector4(VolumetricRegisters.VolumetricMediaEntities[i].Absorption, 0, 0, 0); //Only Absorption for now } //Setup and dispatch Baking compute shader // Debug.Log("Baking " + PointLights.Length + " lights"); // Debug.Log("Baking " + mediaPos.Length + " Media"); //Setting shader variables int shaderKernel = BakingShader.FindKernel("VolumetricAreaBake"); BakingShader.SetTexture(shaderKernel, "AccumulatedLights", RT3d); BakingShader.SetVectorArray("LightColor", lightColors); BakingShader.SetVectorArray("LightPosition", lightPos); BakingShader.SetInt("LightCount", lightColors.Length); BakingShader.SetVectorArray("MediaSphere", mediaPos); BakingShader.SetVectorArray("MediaSphereAbsorption", mediaAbs); BakingShader.SetFloat("AmbientMedia", AmbientMedia); BakingShader.SetInt("MediaSphereCount", mediaPos.Length); BakingShader.SetVector("Size", VolumetricRegisters.volumetricAreas[j].NormalizedScale); BakingShader.SetVector("Position", VolumetricRegisters.volumetricAreas[j].Corner); //Only select the first directional light. Should only have one in a scene, but will add support for more later for artists BakingShader.SetVector("DirectionalLightDirection", DirectionalLights[0].transform.rotation.eulerAngles); BakingShader.SetVector("DirectionalLightColor", DirectionalLights[0].color * DirectionalLights[0].intensity); //Temp Shadow spheres //Vector4[] SpherePos = new Vector4[Spheres.Length]; Vector4[] SpherePos = new Vector4[0]; //for (int i = 0; i < SpherePos.Length; i++) //{ // SpherePos[i] = new Vector4( // Spheres[i].transform.position.x, // Spheres[i].transform.position.y, // Spheres[i].transform.position.z, // Spheres[i].transform.lossyScale.z * 0.5f); //} // SetComputeBuffer("_Spheres", _sphereBuffer); SetComputeBuffer("_MeshObjects", BakingShader, shaderKernel, _meshObjectBuffer); SetComputeBuffer("_Vertices", BakingShader, shaderKernel, _vertexBuffer); SetComputeBuffer("_Indices", BakingShader, shaderKernel, _indexBuffer); BakingShader.SetVectorArray("OpaqueSphere", SpherePos); BakingShader.SetInt("SphereCount", SpherePos.Length); Vector3 ThreadsToDispatch = new Vector3( Mathf.CeilToInt((float)Texels.x / 4.0f), Mathf.CeilToInt((float)Texels.y / 4.0f), Mathf.CeilToInt((float)Texels.z / 4.0f) ); ///GPU Baking /// /// ///Sending data to the GPU //Debuger[] debuger = new Debuger[1]; //debuger[0].DebugCcounter = 0; //int DebugCountStride = sizeof(int); //Size of debug strut //int DebugID = Shader.PropertyToID("DebugBuffer"); //ComputeBuffer BakeBuffer = new ComputeBuffer(1, DebugCountStride); //BakeBuffer.SetData(debuger); //BakingShader.SetBuffer(shaderKernel, DebugID, BakeBuffer); /// // Graphics.CreateGraphicsFence ? //DISPATCHING BakingShader.Dispatch(shaderKernel, (int)ThreadsToDispatch.x, (int)ThreadsToDispatch.y, (int)ThreadsToDispatch.z); // while //Sending data back to the CPU to check if work is done before writing to disk // BakeBuffer.GetData(debuger); // Debug.Log("Shot " + debuger[0].DebugCcounter + " rays"); //BakeBuffer.Release(); //Avoiding memory leak /// //Define path and save 3d texture string path = CheckDirectoryAndReturnPath() + j; //RT3d.SaveToTexture3D(path); RenderTexture[] mipChain = Get3DMips(RT3d); Texture3D ReadBackTex = ReadRT2Tex3D(mipChain); for (int i = 0; i < mipChain.Length; i++) { mipChain[i].Release(); DestroyImmediate(mipChain[i]); } Vol3d.WriteTex3DToVol3D(ReadBackTex, path + Vol3d.fileExtension); AssetDatabase.ImportAsset(path + Vol3d.fileExtension); Texture3D volTex = (Texture3D)AssetDatabase.LoadAssetAtPath(path + Vol3d.fileExtension, typeof(Texture3D)); VolumetricRegisters.volumetricAreas[j].bakedTexture = volTex; EditorUtility.UnloadUnusedAssetsImmediate(); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true); //MaterialPropertyBlock propertyBlock = new MaterialPropertyBlock(); //propertyBlock.SetTexture("_3dTexture", VolumetricRegisters.volumetricAreas[j].bakedTexture); // VolumetricRegisters.volumetricAreas[j].DebugCube.SetPropertyBlock(propertyBlock); //Repaint(); } EditorUtility.ClearProgressBar(); System.DateTime endTime = System.DateTime.Now; UpdateStatus(" Volumetric bake took " + (endTime.Minute - startTime.Minute) + " Minutes and " + (endTime.Second - startTime.Second) + " Seconds. Baked "+ VolumetricRegisters.volumetricAreas.Count + " areas." ); UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene()); } static int prop_OutputDim = Shader.PropertyToID("_OutputDim"); static int prop_Input = Shader.PropertyToID("_Input"); static int prop_Output = Shader.PropertyToID("_Output"); const string Mip3DTextureGUID = "a7b6f45f3454c3345a78c989cedd7229"; static ComputeShader s_mip3DCompute; static ComputeShader Mip3DCompute { get { if (s_mip3DCompute == null) { string mip3dComputePath = AssetDatabase.GUIDToAssetPath(Mip3DTextureGUID); if (string.IsNullOrEmpty(mip3dComputePath)) { throw new FileNotFoundException("Volumetric Baking: Failed to load Mip3DTexture compute shader by hard-coded GUID (" + Mip3DTextureGUID + "). Either the shader is missing or its meta file got regenerated"); } ComputeShader t_mip3DCompute = AssetDatabase.LoadAssetAtPath(mip3dComputePath); if (t_mip3DCompute == null) { throw new FileNotFoundException("Volumetric Baking: Failed to load Mip3DTexture compute shader by hard-coded GUID (" + Mip3DTextureGUID + "). No compute shader was found at the path of the GUID, meaning another asset has the same GUID!"); } s_mip3DCompute = t_mip3DCompute; } return s_mip3DCompute; } } RenderTexture[] Get3DMips(RenderTexture mip0) { int numMips = (int)math.floor(math.log2(math.max(mip0.width, math.max(mip0.height, mip0.volumeDepth)))) + 1; RenderTexture[] mips = new RenderTexture[numMips]; RenderTextureDescriptor rtDesc = mip0.descriptor; ComputeShader mip3DCompute = Mip3DCompute; int kernelMip = mip3DCompute.FindKernel("CalculateMip"); mips[0] = mip0; for (int level = 1; level < numMips; level++) { rtDesc.width = math.max(rtDesc.width / 2, 1); rtDesc.height = math.max(rtDesc.height / 2, 1); rtDesc.volumeDepth = math.max(rtDesc.volumeDepth / 2, 1); mips[level] = new RenderTexture(rtDesc); mips[level].Create(); mip3DCompute.SetTexture(kernelMip, prop_Input, mips[level-1]); mip3DCompute.SetTexture(kernelMip, prop_Output, mips[level]); mip3DCompute.SetInts(prop_OutputDim, new int[] { rtDesc.width, rtDesc.height, rtDesc.volumeDepth, 0 }); mip3DCompute.Dispatch(kernelMip, (rtDesc.width + 3) / 4, (rtDesc.height + 3) / 4, (rtDesc.volumeDepth + 3) / 4); } return mips; } Texture3D ReadRT2Tex3D(RenderTexture[] rtAndMips) { GraphicsFormat gfmt = rtAndMips[0].graphicsFormat; TextureFormat tfmt = GraphicsFormatUtility.GetTextureFormat(gfmt); int width = rtAndMips[0].width; int height = rtAndMips[0].height; int depth = rtAndMips[0].volumeDepth; Texture3D output = new Texture3D(width, height, depth, gfmt, rtAndMips.Length > 1 ? TextureCreationFlags.MipChain : TextureCreationFlags.None); AsyncGPUReadbackRequest[] request = new AsyncGPUReadbackRequest[rtAndMips.Length]; for (int i = 0; i < rtAndMips.Length; i++) { request[i] = AsyncGPUReadback.Request(rtAndMips[i], 0); } for (int mip = 0; mip < output.mipmapCount; mip++) { int currPtr = 0; NativeArray outputRaw = output.GetPixelData(mip); request[mip].WaitForCompletion(); int layers = request[mip].layerCount; for (int slice = 0; slice < layers; slice++) { NativeArray readBackRaw = request[mip].GetData(slice); NativeArray.Copy(readBackRaw, 0, outputRaw, currPtr, readBackRaw.Length); currPtr += readBackRaw.Length; readBackRaw.Dispose(); } } //Texture2D temp = new Texture2D(width, height, gfmt, TextureCreationFlags.None); //Rect sliceRect = new Rect(0, 0, width, height); //int sliceSize = width * height; //RenderTexture oldActive = RenderTexture.active; //RenderTexture.active = rt; //int outputRawPtr = 0; //for (int depthSlice = 0; depthSlice < depth; depthSlice++) //{ // Graphics.SetRenderTarget(rt, 0, CubemapFace.Unknown, depthSlice); // temp.ReadPixels(sliceRect,0,0); // temp.Apply(false); // NativeArray tempNative = temp.GetPixelData(0); // NativeArray.Copy(tempNative, 0, outputRaw, outputRawPtr, tempNative.Length); // outputRawPtr += tempNative.Length; //} //Graphics.SetRenderTarget(oldActive, 0, CubemapFace.Unknown, 0); //RenderTexture.active = oldActive; return output; } static int prop_PrevMipDimOffset = Shader.PropertyToID("_PrevMipDimOffset"); static int prop_MipDimOffset = Shader.PropertyToID("_MipDimOffset"); static int prop_Buffer = Shader.PropertyToID("_Buffer"); ComputeBuffer Get3DMipsBuffer(RenderTexture mip0) { int numMips = (int)math.floor(math.log2(math.max(mip0.width, math.max(mip0.height, mip0.volumeDepth)))) + 1; RenderTextureDescriptor rtDesc = mip0.descriptor; int3 textureDim = new int3(rtDesc.width, rtDesc.height, rtDesc.volumeDepth); int bufferCount = textureDim.x * textureDim.y * textureDim.z; for (int i = 0; i < numMips; i++) { textureDim = math.max(textureDim / 2, 1); bufferCount += textureDim.x * textureDim.y * textureDim.z; } ComputeBuffer mips = new ComputeBuffer(bufferCount, 4 * sizeof(ushort), ComputeBufferType.Structured); ComputeShader mip3DCompute = Mip3DCompute; int initKernel = mip3DCompute.FindKernel("CopyTexToBuffer"); int mipKernel = mip3DCompute.FindKernel("CalculateMipBuffer"); int3 mipDim = new int3(rtDesc.width, rtDesc.height, rtDesc.volumeDepth); mip3DCompute.SetInts(prop_MipDimOffset, new int[] { mipDim.x, mipDim.y, mipDim.z, 0 }); mip3DCompute.SetBuffer(initKernel, prop_Buffer, mips); mip3DCompute.SetTexture(initKernel, prop_Input, mip0); mip3DCompute.Dispatch(initKernel, (mipDim.x + 3) / 4, (mipDim.y + 3) / 4, (mipDim.z + 3) / 4); int3 prevMipDim = mipDim; int mipPtr = 0; int prevMipPtr = 0; mip3DCompute.SetBuffer(mipKernel, prop_Buffer, mips); for (int level = 1; level < numMips; level++) { prevMipDim = mipDim; prevMipPtr = mipPtr; mipDim = math.max(mipDim / 2, new int3(1,1,1)); mipPtr += prevMipDim.x * prevMipDim.y * prevMipDim.z; mip3DCompute.SetInts(prop_PrevMipDimOffset, new int[] { prevMipDim.x, prevMipDim.y, prevMipDim.z, prevMipPtr }); mip3DCompute.SetInts(prop_MipDimOffset, new int[] { mipDim.x, mipDim.y, mipDim.z, mipPtr }); mip3DCompute.Dispatch(mipKernel, (mipDim.x + 3) / 4, (mipDim.y + 3) / 4, (mipDim.z + 3) / 4); } return mips; } Texture3D ReadBufferToTex3D(ComputeBuffer rtAndMips, int width, int height, int depth) { GraphicsFormat gfmt = GraphicsFormat.R16G16B16A16_SFloat; Texture3D output = new Texture3D(width, height, depth, gfmt, TextureCreationFlags.MipChain); int stride = rtAndMips.stride / sizeof(ushort); ushort[] bufferReadBack = new ushort[rtAndMips.count * stride]; rtAndMips.GetData(bufferReadBack); int ptr = 0; for (int mip = 0; mip < output.mipmapCount; mip++) { NativeArray outputRaw = output.GetPixelData(mip); int copyCount = width * height * depth * stride; NativeArray.Copy(bufferReadBack, ptr, outputRaw, 0, width * height * depth * stride); //if (mip == output.mipmapCount - 1) //{ // half4 lastColor = new half4( // new half() { value = bufferReadBack[ptr - 4] }, // new half() { value = bufferReadBack[ptr - 3] }, // new half() { value = bufferReadBack[ptr - 2] }, // new half() { value = bufferReadBack[ptr - 1] }); // Debug.Log("Last Color: " + lastColor.ToString()); //} ptr += copyCount; width = math.max(1, width / 2); height = math.max(1, height / 2); depth = math.max(1, depth / 2); } return output; } ////// ComputeShader BakingShader; void BakeLights() { System.DateTime startTime = System.DateTime.Now; BakingShader = AssetDatabase.LoadAssetAtPath("Packages/com.unity.render-pipelines.universal/Shaders/Volumetrics/VolumetricBaking.compute"); UpdateStatus("Baking " + SceneManager.GetActiveScene().name); ComputeShader BlitShader = AssetDatabase.LoadAssetAtPath("Packages/com.unity.render-pipelines.universal/Shaders/Volumetrics/3dBlit.compute"); int BlitBucketKernal = BlitShader.FindKernel("BlitBucket"); Running = true; for (int j = 0; j < VolumetricRegisters.volumetricAreas.Count; j++) { // EditorUtility.DisplayProgressBar("Baking Volumetrics... ", VolumetricRegisters.volumetricAreas[j].name, (float)j / (float)VolumetricRegisters.volumetricAreas.Count); Vector3Int Texels = VolumetricRegisters.volumetricAreas[j].NormalizedTexelDensity; RenderTextureDescriptor rtdiscrpt = new RenderTextureDescriptor(); rtdiscrpt.enableRandomWrite = true; rtdiscrpt.dimension = UnityEngine.Rendering.TextureDimension.Tex3D; rtdiscrpt.width = Texels.x; rtdiscrpt.height = Texels.y; rtdiscrpt.volumeDepth = Texels.z; rtdiscrpt.graphicsFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.R16G16B16A16_SFloat; rtdiscrpt.msaaSamples = 1; //Target buffer RenderTexture RT3d = new RenderTexture(rtdiscrpt); RT3d.Create(); //Bucket rtdiscrpt.width = BucketSize; rtdiscrpt.height = BucketSize; rtdiscrpt.volumeDepth = BucketSize; RenderTexture bucketBuffer = new RenderTexture(rtdiscrpt); bucketBuffer.Create(); Light[] PointLights = GatherBakedLights(LightType.Point); //Figuring out the buckets per dimension int bx = Mathf.CeilToInt((float)Texels.x / BucketSize); int by = Mathf.CeilToInt((float)Texels.y / BucketSize); int bz = Mathf.CeilToInt((float)Texels.z / BucketSize); //Total number of buckets int BucketCount = bx * by * bz; Debug.Log(BucketCount + " buckets"); BlitShader.SetTexture(BlitBucketKernal, "BucketBuffer", bucketBuffer); BlitShader.SetTexture(BlitBucketKernal, "Result", RT3d); //Bucket Loop for (int b = 0; b < BucketCount; b++) { Vector3 ThreadsToDispatch = new Vector3( Mathf.CeilToInt((float)Texels.x / 4.0f), Mathf.CeilToInt((float)Texels.y / 4.0f), Mathf.CeilToInt((float)Texels.z / 4.0f) ); Vector3 BucketThreads = new Vector3( Mathf.CeilToInt((float)BucketSize / 4.0f), Mathf.CeilToInt((float)BucketSize / 4.0f), Mathf.CeilToInt((float)BucketSize / 4.0f)); //Generate cell offset int x = b % bx; int y = (b / bx) % by; int z = b / (by * bx); Vector3Int CellOffset = new Vector3Int(x, y, z); Vector3Int TextileOffset = CellOffset * BucketSize; // Debug.Log(TextileOffset + ", size:" + BucketSize); /// BlitShader.set /// //Clear buffer BakingShader.SetTexture(BakingShader.FindKernel("ClearBuffer"), "AccumulatedLights", bucketBuffer); BakingShader.Dispatch(BakingShader.FindKernel("ClearBuffer"), (int)BucketThreads.x, (int)BucketThreads.y, (int)BucketThreads.z); //Light Loop for (int i = 0; i < PointLights.Length; i++) { UpdateProgress("Baking " + (j + 1) + "/" + VolumetricRegisters.volumetricAreas.Count + " " + VolumetricRegisters.volumetricAreas[j].name + " " + (System.DateTime.Now.Minute - startTime.Minute) + ":" + (System.DateTime.Now.Second - startTime.Second) //TODO: Format correctly , PointLights[i].name, (float)i / PointLights.Length); //TODO: Do some checking to only render lights affecting the area. AABB? //Render to bucket buffer DispatchLight(PointLights[i], bucketBuffer, new Vector3Int(BucketSize, BucketSize, BucketSize), j, TextileOffset); } //Blit from bucket to larger buffer BlitShader.SetTexture(BlitBucketKernal, "BucketBuffer", bucketBuffer); //bucket buffer BlitShader.SetTexture(BlitBucketKernal, "Result", RT3d); //target buffer BlitShader.SetVector( "BucketOffset", (Vector3)TextileOffset) ; BlitShader.SetInt( "BucketSize", BucketSize) ; BlitShader.Dispatch(BlitBucketKernal, (int)ThreadsToDispatch.x, (int)ThreadsToDispatch.y, (int)ThreadsToDispatch.z); // RT3d } //do //{ //} while (i < 0); //bool dothething = true; //while (dothething) //{ // UpdateProgress("Baking " + (j + 1) + "/" + VolumetricRegisters.volumetricAreas.Count + " " // + VolumetricRegisters.volumetricAreas[j].name + " " // + (System.DateTime.Now.Minute - startTime.Minute) + ":" + (System.DateTime.Now.Second - startTime.Second) //TODO: Format correctly // , PointLights[i].name, (float)i / PointLights.Length); // //TODO: Do some checking to only render lights affecting the area. AABB? // DispatchLight(PointLights[i], RT3d, Texels, j); // Debug.Log(""); // return; //} //Define path and save 3d texture string path = CheckDirectoryAndReturnPath() + j; RenderTexture[] mipChain = Get3DMips(RT3d); Texture3D ReadBackTex = ReadRT2Tex3D(mipChain); for (int i = 0; i < mipChain.Length; i++) { mipChain[i].Release(); DestroyImmediate(mipChain[i]); } Vol3d.WriteTex3DToVol3D(ReadBackTex, path + Vol3d.fileExtension); bucketBuffer.Release(); DestroyImmediate(bucketBuffer); AssetDatabase.ImportAsset(path + Vol3d.fileExtension); VolumetricRegisters.volumetricAreas[j].bakedTexture = (Texture3D)AssetDatabase.LoadAssetAtPath(path + Vol3d.fileExtension, typeof(Texture3D)); EditorUtility.UnloadUnusedAssetsImmediate(); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true); // Debug.Log(VolumetricRegisters.volumetricAreas[j].gameObject.scene.name + VolumetricRegisters.volumetricAreas[j].name); UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(VolumetricRegisters.volumetricAreas[j].gameObject.scene); } Running = false; EditorUtility.ClearProgressBar(); System.DateTime endTime = System.DateTime.Now; UpdateStatus(" Volumetric bake took " + (endTime.Minute - startTime.Minute) + " Minutes and " + (endTime.Second - startTime.Second) + " Seconds. Baked " + VolumetricRegisters.volumetricAreas.Count + " areas." ); // UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene()); } string CheckDirectoryAndReturnPath() { //Define path string path = SceneManager.GetActiveScene().path; path = path.Replace(".unity", ""); if (!Directory.Exists(path)) //Check if path exists { Directory.CreateDirectory(path); //if it doesn't, create it Debug.Log("Made Directory " + path); AssetDatabase.Refresh(); } return path + "/" + "Volumemap-"; } RenderTexture initializeVolume(int i) { Vector3Int Texels = VolumetricRegisters.volumetricAreas[i].NormalizedTexelDensity; RenderTextureDescriptor rtdiscrpt = new RenderTextureDescriptor(); rtdiscrpt.enableRandomWrite = true; rtdiscrpt.dimension = UnityEngine.Rendering.TextureDimension.Tex3D; rtdiscrpt.width = Texels.x; rtdiscrpt.height = Texels.y; rtdiscrpt.volumeDepth = Texels.z; rtdiscrpt.graphicsFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.R16G16B16A16_SFloat; rtdiscrpt.msaaSamples = 1; //Target buffer RenderTexture RT3d = new RenderTexture(rtdiscrpt); RT3d.Create(); return RT3d; } Texture MakeEnvironmentalCubemap() { //Black Background if (SkyboxContribution != true){ RenderTexture cubetex = new RenderTexture(32, 32, 1, RenderTextureFormat.DefaultHDR); cubetex.enableRandomWrite = true; cubetex.dimension = UnityEngine.Rendering.TextureDimension.Cube; cubetex.Create(); Camera renderCam = new GameObject().AddComponent(); renderCam.cullingMask = 0; renderCam.backgroundColor = Color.black; renderCam.clearFlags = CameraClearFlags.Color; renderCam.RenderToCubemap(cubetex); DestroyImmediate(renderCam.gameObject); return cubetex; } //Generate Skybox if (CustomEnvorment == null) { RenderTexture cubetex = new RenderTexture(256, 256, 1, RenderTextureFormat.DefaultHDR); cubetex.enableRandomWrite = true; cubetex.dimension = UnityEngine.Rendering.TextureDimension.Cube; cubetex.Create(); Camera renderCam = new GameObject().AddComponent(); renderCam.cullingMask = 0; renderCam.backgroundColor = Color.black; renderCam.clearFlags = CameraClearFlags.Skybox; renderCam.RenderToCubemap(cubetex); DestroyImmediate(renderCam.gameObject); return cubetex; } //Provided skybox else { return CustomEnvorment; } } void RefreshDebuggers() { for (int i = 0; i < VolumetricRegisters.volumetricAreas.Count; i++) { if (VolumetricRegisters.volumetricAreas[i].DEBUG) VolumetricRegisters.volumetricAreas[i].RefreshDebugMesh(); } } Color ColorExtraction(Light light) { Color colorModulation = light.color.linear; if (light.useColorTemperature) colorModulation *= Mathf.CorrelatedColorTemperatureToRGB(light.colorTemperature); colorModulation *= light.intensity; colorModulation *= light.gameObject.GetComponent().volumetricDimmer; return colorModulation; } void BakeDXR() { System.DateTime startTime = System.DateTime.Now; UpdateStatus("Baking " + SceneManager.GetActiveScene().name); //ComputeShader BlitShader = AssetDatabase.LoadAssetAtPath("Packages/com.unity.render-pipelines.universal/Shaders/Volumetrics/3dBlit.compute"); RayTracingShader rtshader = AssetDatabase.LoadAssetAtPath("Packages/com.unity.render-pipelines.universal/Shaders/Volumetrics/DXR-Volumebaker.raytrace"); rtshader.SetShaderPass("BakedRaytrace"); RayTracingAccelerationStructure accelerationStructure = new RayTracingAccelerationStructure(); ; Renderer[] renderers = GatherStaticRenderers(); for (int i = 0; i < renderers.Length; i++) accelerationStructure.AddInstance(renderers[i]); accelerationStructure.Build(); rtshader.SetAccelerationStructure("g_SceneAccelStruct", accelerationStructure); Running = true; Vector3Int threads = new Vector3Int(); List PointLights, ConeLights, DirectionalLights, AreaLights; Light[] Lights = GatherBakedLights(); PointLights = new List(); ConeLights = new List(); DirectionalLights = new List(); AreaLights = new List(); for (int i = 0; i < Lights.Length; i++) { switch (Lights[i].type) { case LightType.Point: PointLights.Add(Lights[i]); break; case LightType.Spot: ConeLights.Add(Lights[i]); break; case LightType.Directional: DirectionalLights.Add(Lights[i]); break; case LightType.Area: AreaLights.Add(Lights[i]); break; case LightType.Disc: AreaLights.Add(Lights[i]); //Stacking area and disc break; default: break; } } Debug.Log(PointLights.Count + "Point Lights, " + ConeLights.Count + " Cone Lights, " + DirectionalLights.Count + " Dir Lights, " + AreaLights.Count + " area lights."); //Set up buffers with data stride. Keeping a min count of 1 to keep buffer valid. Get's skipped in shader. ComputeBuffer pointBuffer = new ComputeBuffer(Mathf.Max(PointLights.Count, 1), (3 + 4) * 4); ComputeBuffer coneBuffer = new ComputeBuffer(Mathf.Max(ConeLights.Count, 1), (3 + 4 + 3 + 2) * 4); ComputeBuffer dirBuffer = new ComputeBuffer(Mathf.Max(DirectionalLights.Count, 1), (3 + 4) * 4); ComputeBuffer areaBuffer = new ComputeBuffer(Mathf.Max(AreaLights.Count, 1), (4 * 4 + 4 * 4 + 3 + 4 + 3) * 4); PointLightData[] PointLDatas = new PointLightData[PointLights.Count]; ConeLightData[] ConeLDatas = new ConeLightData[ConeLights.Count]; DirLightData[] DirLDatas = new DirLightData[DirectionalLights.Count]; AreaLightData[] AreaLDatas = new AreaLightData[AreaLights.Count]; for (int i = 0; i < PointLights.Count; i++) { PointLDatas[i].PointLightsPos = PointLights[i].transform.position; PointLDatas[i].PointLightsColors = ColorExtraction(PointLights[i]); } for (int i = 0; i < ConeLights.Count; i++) { ConeLDatas[i].ConeLightsWS = ConeLights[i].transform.position; ConeLDatas[i].ConeLightsColors = ColorExtraction(ConeLights[i]); ConeLDatas[i].ConeLightsDir = ConeLights[i].transform.forward; float flPhiDot = Mathf.Clamp01(Mathf.Cos(ConeLights[i].spotAngle * 0.5f * Mathf.Deg2Rad)); // outer cone float flThetaDot = Mathf.Clamp01(Mathf.Cos(ConeLights[i].innerSpotAngle * 0.5f * Mathf.Deg2Rad)); // inner cone ConeLDatas[i].ConeLightsPram = new Vector4(flPhiDot, 1.0f / Mathf.Max(0.01f, flThetaDot - flPhiDot), 0, 0); } for (int i = 0; i < DirectionalLights.Count; i++) { DirLDatas[i].DirLightsDir = DirectionalLights[i].transform.forward; DirLDatas[i].DirLightsColors = ColorExtraction(DirectionalLights[i]); } for (int i = 0; i < AreaLights.Count; i++) { AreaLDatas[i].AreaLightsPos = AreaLights[i].transform.position; AreaLDatas[i].AreaLightsMatrix = Matrix4x4.TRS(AreaLights[i].transform.position, AreaLights[i].transform.rotation, Vector3.one); AreaLDatas[i].AreaLightsMatrixInv = AreaLDatas[i].AreaLightsMatrix.inverse; AreaLDatas[i].AreaLightsColors = ColorExtraction(AreaLights[i]); AreaLDatas[i].AreaLightsSize = new Vector3(AreaLights[i].areaSize.x, AreaLights[i].areaSize.y, AreaLights[i].type == LightType.Disc ? 1 : 0); //Packing for area or disc logic } pointBuffer.SetData(PointLDatas); coneBuffer.SetData(ConeLDatas); dirBuffer.SetData(DirLDatas); areaBuffer.SetData(AreaLDatas); Texture skyTex = MakeEnvironmentalCubemap(); CommandBuffer cmd = CommandBufferPool.Get(); cmd.SetRayTracingIntParam(rtshader, "PointLightCount", PointLDatas.Length); //Add a stack overflow loop or computebuffer cmd.SetRayTracingBufferParam(rtshader, "PLD", pointBuffer); //Cone cmd.SetRayTracingIntParam(rtshader, "ConeLightCount", ConeLDatas.Length); //Add a stack overflow loop or computebuffer cmd.SetRayTracingBufferParam(rtshader, "CLD", coneBuffer); //Directional cmd.SetRayTracingIntParam(rtshader, "DirLightCount", DirLDatas.Length); //Add a stack overflow loop or computebuffer cmd.SetRayTracingBufferParam(rtshader, "DLD", dirBuffer); //Area cmd.SetRayTracingIntParam(rtshader, "AreaLightCount", AreaLDatas.Length); //Add a stack overflow loop or computebuffer cmd.SetRayTracingIntParam(rtshader, "AreaLightSamples", System.Convert.ToInt32(AreaLightSamples)); cmd.SetRayTracingBufferParam(rtshader, "ALD", areaBuffer); //Env rtshader.SetTexture("_SkyTexture", skyTex); cmd.SetRayTracingIntParam(rtshader, "EnvLightSamples", System.Convert.ToInt32(EnvLightSamples)); int id_startIdx = Shader.PropertyToID("StartRayIdx"); cmd.SetRayTracingIntParam(rtshader, "PerDispatchRayCount", RayChunkSize); for (int j = 0; j < VolumetricRegisters.volumetricAreas.Count; j++) { Vector3Int resolution = VolumetricRegisters.volumetricAreas[j].NormalizedTexelDensity; threads = resolution; Vector3 boxSize = VolumetricRegisters.volumetricAreas[j].BoxScale; float maxVoxelSize = math.max(boxSize.x / (float)resolution.x, math.max(boxSize.y / (float)resolution.y, boxSize.z / (float)resolution.z)); RenderTexture RT3d = initializeVolume(j); cmd.SetRayTracingTextureParam(rtshader, "g_Output", RT3d); cmd.SetRayTracingVectorParam(rtshader, "Size", VolumetricRegisters.volumetricAreas[j].NormalizedScale); cmd.SetRayTracingVectorParam(rtshader, "WPosition", VolumetricRegisters.volumetricAreas[j].Corner); cmd.SetRayTracingFloatParam(rtshader, "_Seed", (UnityEngine.Random.Range(0.0f, 64.0f))); cmd.SetRayTracingFloatParam(rtshader, "HalfVoxelSize", maxVoxelSize * 0.5f); //Point //Dispatching int maxRays = PointLDatas.Length + ConeLDatas.Length + DirLDatas.Length + EnvLightSamples + (AreaLDatas.Length * AreaLightSamples); Debug.Log("Max Rays: " + maxRays); for (int i = 0; i < maxRays; i += RayChunkSize) { cmd.SetRayTracingIntParam(rtshader, id_startIdx, i); cmd.DispatchRays(rtshader, "MainRayGenShader", (uint)threads.x, (uint)threads.y, (uint)threads.z); // Execute the command buffer once per volume, if we add all the volumes to the buffer then execute the gpu will time out! Graphics.ExecuteCommandBuffer(cmd); cmd.Clear(); } /// /// /// string path = CheckDirectoryAndReturnPath() + j; ComputeBuffer mipChain = Get3DMipsBuffer(RT3d); Texture3D ReadBackTex = ReadBufferToTex3D(mipChain, RT3d.width, RT3d.height, RT3d.volumeDepth); mipChain.Release(); Debug.Log("Used Buffer Readback"); //for (int i = 0; i < mipChain.Length; i++) //{ // mipChain[i].Release(); // DestroyImmediate(mipChain[i]); //} Vol3d.WriteTex3DToVol3D(ReadBackTex, path + Vol3d.fileExtension); AssetDatabase.ImportAsset(path + Vol3d.fileExtension); VolumetricRegisters.volumetricAreas[j].bakedTexture = (Texture3D)AssetDatabase.LoadAssetAtPath(path + Vol3d.fileExtension, typeof(Texture3D)); // Debug.Log(VolumetricRegisters.volumetricAreas[j].gameObject.scene.name + VolumetricRegisters.volumetricAreas[j].name); UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(VolumetricRegisters.volumetricAreas[j].gameObject.scene); } pointBuffer.Release(); coneBuffer.Release(); dirBuffer.Release(); areaBuffer.Release(); Running = false; EditorUtility.ClearProgressBar(); if (skyTex != null && skyTex.GetType() == typeof(RenderTexture)) { RenderTexture rtSky = skyTex as RenderTexture; rtSky.DiscardContents(true, true); rtSky.Release(); DestroyImmediate(rtSky); } accelerationStructure.Dispose(); System.DateTime endTime = System.DateTime.Now; UpdateStatus(" Volumetric bake took " + (endTime.Minute - startTime.Minute) + " Minutes and " + (endTime.Second - startTime.Second) + " Seconds. Baked " + VolumetricRegisters.volumetricAreas.Count + " areas." ); RefreshDebuggers(); EditorUtility.UnloadUnusedAssetsImmediate(); GC.Collect(); } void DispatchLight(Light light, RenderTexture RT3d, Vector3Int Texels, int AreaID, Vector3Int TextileOffset ) //Used to render each light indavidually. TODO Generalize into pure pathtracing { //Setup light data Vector4 lightColor = light.color * light.intensity; Vector4 lightPos = light.transform.position; int shaderKernel; //Setup and dispatch Baking compute shader switch (light.type) { case LightType.Point : shaderKernel = BakingShader.FindKernel("PointLight"); break; case LightType.Spot : shaderKernel = BakingShader.FindKernel("SpotLight"); float flPhiDot = Mathf.Clamp01(Mathf.Cos(light.spotAngle * 0.5f * Mathf.Deg2Rad)); // outer cone float flThetaDot = Mathf.Clamp01(Mathf.Cos(light.innerSpotAngle * 0.5f * Mathf.Deg2Rad)); // inner cone BakingShader.SetFloat("SpotPram", flPhiDot); BakingShader.SetFloat("InnerSpotPram", 1.0f / Mathf.Max(0.01f, flThetaDot - flPhiDot)); break; case LightType.Directional: shaderKernel = BakingShader.FindKernel("DirectionalLight"); break; case LightType.Rectangle: // Debug.Log("baked " + light.name); shaderKernel = BakingShader.FindKernel("RectangleLight"); BakingShader.SetVector("AreaSize", light.areaSize); BakingShader.SetFloat("AreaLightSamples", AreaLightSamples); BakingShader.SetMatrix("AreaMatrix", Matrix4x4.Rotate(light.transform.rotation)); BakingShader.SetFloat("_Seed", UnityEngine.Random.value); break; case LightType.Disc: shaderKernel = BakingShader.FindKernel("DiscLight"); BakingShader.SetVector("AreaSize", light.areaSize); //Only the first float is used for radius BakingShader.SetFloat("AreaLightSamples", AreaLightSamples); BakingShader.SetMatrix("AreaMatrix", Matrix4x4.Rotate(light.transform.rotation)); BakingShader.SetFloat("_Seed", UnityEngine.Random.value); break; default: return; } BakingShader.SetTexture(shaderKernel, "AccumulatedLights", RT3d); BakingShader.SetVector("LightColor", lightColor); BakingShader.SetVector("LightPosition", lightPos); BakingShader.SetVector("LightDirection", light.transform.rotation * Vector3.forward); // BakingShader.SetVector("Size", VolumetricRegisters.volumetricAreas[AreaID].NormalizedScale); Vector3 BucketScaler = (Vector3)VolumetricRegisters.volumetricAreas[AreaID].NormalizedTexelDensity / (float)BucketSize ; // BucketScaler = Vector3.one * 1.5f; //Debug.Log("Bucket Scaler" + BucketScaler+ // "NormalizedTexelDensity" + (VolumetricRegisters.volumetricAreas[AreaID].NormalizedTexelDensity)+ // "BucketSize" + BucketSize ); //Offset each cell in world space based on the textiles Vector3 D = VolumetricRegisters.volumetricAreas[AreaID].NormalizedScale; Vector3 T = (Vector3)VolumetricRegisters.volumetricAreas[AreaID].NormalizedTexelDensity; Vector3 PositionOffset = new Vector3( (D.x / T.x) * TextileOffset.x, (D.y / T.y) * TextileOffset.y, (D.z / T.z) * TextileOffset.z); BakingShader.SetVector("Size", new Vector3(VolumetricRegisters.volumetricAreas[AreaID].NormalizedScale.x / BucketScaler.x, VolumetricRegisters.volumetricAreas[AreaID].NormalizedScale.y / BucketScaler.y, VolumetricRegisters.volumetricAreas[AreaID].NormalizedScale.z / BucketScaler.z) ); // BakingShader.SetVector("Size", (Vector3)(Vector3Int.one * BucketSize) ); BakingShader.SetVector("Position", VolumetricRegisters.volumetricAreas[AreaID].Corner + PositionOffset); ///Temp Shadow spheres Vector4[] SpherePos = new Vector4[0]; SetComputeBuffer("_MeshObjects", BakingShader, shaderKernel, _meshObjectBuffer); SetComputeBuffer("_Vertices", BakingShader, shaderKernel, _vertexBuffer); SetComputeBuffer("_Indices", BakingShader, shaderKernel, _indexBuffer); BakingShader.SetVectorArray("OpaqueSphere", SpherePos); BakingShader.SetInt("SphereCount", SpherePos.Length); /// Vector3 ThreadsToDispatch = new Vector3( Mathf.CeilToInt(Texels.x / 4.0f), Mathf.CeilToInt(Texels.y / 4.0f), Mathf.CeilToInt(Texels.z / 4.0f) ); //int DebugCountStride = sizeof(int); //Size of debug strut //int DebugID = Shader.PropertyToID("DebugBuffer"); //Debuger[] debuger = new Debuger[1]; //debuger[0].DebugCcounter = 0; //ComputeBuffer BakeBuffer = new ComputeBuffer(1, DebugCountStride); //BakingShader.SetBuffer(shaderKernel, DebugID, BakeBuffer); BakingShader.Dispatch(shaderKernel, (int)ThreadsToDispatch.x, (int)ThreadsToDispatch.y, (int)ThreadsToDispatch.z); //while (debuger[0].DebugCcounter == 0) //{ // //BakeBuffer.GetData(debuger); // // Debug.Log("Counter " + debuger[0].DebugCcounter); //} //BakeBuffer.Release(); //Avoiding memory leak // return RT3d; } void SetComputeBuffer(string name, ComputeShader shader, int kernel, ComputeBuffer buffer) { // Debug.Log("Setting buffer"); if (buffer != null) { shader.SetBuffer(kernel, name, buffer); // Debug.Log(name + " set"); } } Light[] GatherBakedLights(LightType lightType) { Light[] lights = FindObjectsOfType(); //TODO: Make it smarter to find only baked lights affecting zone. List FilteredLights = new List(); for (int i = 0; i < lights.Length; i++) { //TODO: Handle mixed lights differently in the future if (lights[i].lightmapBakeType == LightmapBakeType.Baked || lights[i].lightmapBakeType == LightmapBakeType.Mixed) { if (lights[i].enabled) FilteredLights.Add(lights[i]); } } return FilteredLights.ToArray(); } Light[] GatherBakedLights() { Light[] lights = FindObjectsOfType(); //TODO: Make it smarter to find only baked lights affecting zone. List FilteredLights = new List(); for (int i = 0; i < lights.Length; i++) { //TODO: Handle mixed lights differently in the future if (lights[i].lightmapBakeType == LightmapBakeType.Baked || lights[i].lightmapBakeType == LightmapBakeType.Mixed) { if (lights[i].enabled) FilteredLights.Add(lights[i]); } } return FilteredLights.ToArray(); } GameObject[] GatherStaticObjects() { List StatcGameobject = new List(); Renderer[] AllRenderers = FindObjectsOfType(); StaticEditorFlags staticFlag = StaticEditorFlags.ContributeGI; //Loop through GO's and see if the correct static flag is enabled. If it is, then add it to the list; for (int i = 0; i < AllRenderers.Length; i++){ if (GameObjectUtility.AreStaticEditorFlagsSet(AllRenderers[i].gameObject, staticFlag) && AllRenderers[i].shadowCastingMode != UnityEngine.Rendering.ShadowCastingMode.Off) { StatcGameobject.Add(AllRenderers[i].gameObject); } } return StatcGameobject.ToArray(); } Renderer[] GatherStaticRenderers() { List StatcRenderer = new List(); Renderer[] AllRenderers = FindObjectsOfType(); StaticEditorFlags staticFlag = StaticEditorFlags.ContributeGI; //Loop through GO's and see if the correct static flag is enabled. If it is, then add it to the list; for (int i = 0; i < AllRenderers.Length; i++) { if (GameObjectUtility.AreStaticEditorFlagsSet(AllRenderers[i].gameObject, staticFlag) && AllRenderers[i].shadowCastingMode != UnityEngine.Rendering.ShadowCastingMode.Off) { StatcRenderer.Add(AllRenderers[i]); } } return StatcRenderer.ToArray(); } public void RebuildMeshObjectBuffers() { //if (!VolumetricRegisters._meshObjectsNeedRebuilding) //{ // return; //} // VolumetricBakingRegisters._meshObjectsNeedRebuilding = false; // Clear all lists _meshObjects.Clear(); _vertices.Clear(); _indices.Clear(); GameObject[] staticGOs = GatherStaticObjects(); // Loop over all objects and gather their data for (int i=0; i< staticGOs.Length; i++) { Mesh mesh = staticGOs[i].GetComponent().sharedMesh; if (mesh == null) return; // Add vertex data int firstVertex = _vertices.Count; // if (_vertices.Count == 0) return; _vertices.AddRange(mesh.vertices); // Add index data - if the vertex buffer wasn't empty before, the // indices need to be offset // // Debug.Log(_indices.Count + " index"); int firstIndex = _indices.Count; var indices = mesh.GetIndices(0); //Extend to support submeshes // _indices.AddRange(indices.Select(index => index + firstVertex)) _indices.AddRange( indices.Select(index => index + firstVertex) ); // Add the object itself _meshObjects.Add(new MeshObject() { localToWorldMatrix = staticGOs[i].transform.localToWorldMatrix, indices_offset = firstIndex, indices_count = indices.Length }); } //TODO: Covert terrain data into an ingestable format!! //for (int i = 0; i < staticGOs.Length; i++) //{ // Mesh mesh = staticGOs[i].GetComponent().sharedMesh; // if (mesh == null) return; // // Add vertex data // int firstVertex = _vertices.Count; // // if (_vertices.Count == 0) return; // _vertices.AddRange(mesh.vertices); // // Add index data - if the vertex buffer wasn't empty before, the // // indices need to be offset // // // // Debug.Log(_indices.Count + " index"); // int firstIndex = _indices.Count; // var indices = mesh.GetIndices(0); //Extend to support submeshes // // _indices.AddRange(indices.Select(index => index + firstVertex)) // _indices.AddRange(indices.Select(index => index + firstVertex)); // // Add the object itself // _meshObjects.Add(new MeshObject() // { // localToWorldMatrix = staticGOs[i].transform.localToWorldMatrix, // indices_offset = firstIndex, // indices_count = indices.Length // }); //} // Debug.Log(_meshObjects.Count); CreateComputeBuffer(ref _meshObjectBuffer, _meshObjects, 72); CreateComputeBuffer(ref _vertexBuffer, _vertices, 12); CreateComputeBuffer(ref _indexBuffer, _indices, 4); } void ReleaseBuffers() { _meshObjectBuffer.Release(); _meshObjectBuffer = null; _vertexBuffer.Release(); _vertexBuffer = null; _indexBuffer.Release(); _indexBuffer = null; } private static void CreateComputeBuffer(ref ComputeBuffer buffer, List data, int stride) where T : struct { //Debug.Log("Making computebuffer "); //buffer = new ComputeBuffer(data.Count, stride); // Do we already have a compute buffer? if (buffer != null && data != null && stride != 0) { // If no data or buffer doesn't match the given criteria, release it if (data.Count == 0 || buffer.count != data.Count || buffer.stride != stride) { // Debug.Log("Buffer count = " + buffer.count); buffer.Release(); buffer = null; } } if (data.Count != 0) { // If the buffer has been released or wasn't there to // begin with, create it if (buffer == null) { buffer = new ComputeBuffer(data.Count, stride); // Debug.Log("Buffer count = " + buffer.count); } // Set data on the buffer buffer.SetData(data); } } struct PointLightData { //Point public Vector3 PointLightsPos; public Vector4 PointLightsColors; } struct ConeLightData { public Vector3 ConeLightsWS; public Vector4 ConeLightsColors; public Vector3 ConeLightsDir; public Vector2 ConeLightsPram; } struct DirLightData { public Vector3 DirLightsDir; public Vector4 DirLightsColors; } struct AreaLightData { public Matrix4x4 AreaLightsMatrix; public Matrix4x4 AreaLightsMatrixInv; public Vector3 AreaLightsPos; public Vector4 AreaLightsColors; public Vector3 AreaLightsSize; } //public static void RepaintInspector(System.Type t) //{ // Editor[] ed = (Editor[])Resources.FindObjectsOfTypeAll(); // for (int i = 0; i < ed.Length; i++) // { // if (ed[i].GetType() == t) // { // ed[i].Repaint(); // return; // } // } //} }