WuhuIslandTesting/Library/PackageCache/com.unity.render-pipelines.universal@8148.0.7-4/Editor/VolumetricBakingWindow.cs
2025-01-07 02:06:59 +01:00

1613 lines
64 KiB
C#

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;
}
/// <summary>
/// Triangle Object
/// </summary>
struct MeshObject
{
public Matrix4x4 localToWorldMatrix;
public int indices_offset;
public int indices_count;
}
private static List<MeshObject> _meshObjects = new List<MeshObject>();
private static List<Vector3> _vertices = new List<Vector3>();
private static List<int> _indices = new List<int>();
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<ComputeShader>("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<ParticipatingMediaEntity>();
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<ComputeShader>(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<byte> outputRaw = output.GetPixelData<byte>(mip);
request[mip].WaitForCompletion();
int layers = request[mip].layerCount;
for (int slice = 0; slice < layers; slice++)
{
NativeArray<byte> readBackRaw = request[mip].GetData<byte>(slice);
NativeArray<byte>.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<byte> tempNative = temp.GetPixelData<byte>(0);
// NativeArray<byte>.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<ushort> outputRaw = output.GetPixelData<ushort>(mip);
int copyCount = width * height * depth * stride;
NativeArray<ushort>.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<ComputeShader>("Packages/com.unity.render-pipelines.universal/Shaders/Volumetrics/VolumetricBaking.compute");
UpdateStatus("Baking " + SceneManager.GetActiveScene().name);
ComputeShader BlitShader = AssetDatabase.LoadAssetAtPath<ComputeShader>("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<Camera>();
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<Camera>();
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<UniversalAdditionalLightData>().volumetricDimmer;
return colorModulation;
}
void BakeDXR()
{
System.DateTime startTime = System.DateTime.Now;
UpdateStatus("Baking " + SceneManager.GetActiveScene().name);
//ComputeShader BlitShader = AssetDatabase.LoadAssetAtPath<ComputeShader>("Packages/com.unity.render-pipelines.universal/Shaders/Volumetrics/3dBlit.compute");
RayTracingShader rtshader = AssetDatabase.LoadAssetAtPath<RayTracingShader>("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<Light> PointLights, ConeLights, DirectionalLights, AreaLights;
Light[] Lights = GatherBakedLights();
PointLights = new List<Light>();
ConeLights = new List<Light>();
DirectionalLights = new List<Light>();
AreaLights = new List<Light>();
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<Light>(); //TODO: Make it smarter to find only baked lights affecting zone.
List<Light> FilteredLights = new List<Light>();
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<Light>(); //TODO: Make it smarter to find only baked lights affecting zone.
List<Light> FilteredLights = new List<Light>();
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<GameObject> StatcGameobject = new List<GameObject>();
Renderer[] AllRenderers = FindObjectsOfType<Renderer>();
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<Renderer> StatcRenderer = new List<Renderer>();
Renderer[] AllRenderers = FindObjectsOfType<Renderer>();
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<MeshFilter>().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<MeshFilter>().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<T>(ref ComputeBuffer buffer, List<T> 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<Editor>();
// for (int i = 0; i < ed.Length; i++)
// {
// if (ed[i].GetType() == t)
// {
// ed[i].Repaint();
// return;
// }
// }
//}
}