/// <summary> /// Adds a voxel to the octree, possibly subdividing everything that is necessary to reach the target level /// </summary> /// <param name="_wsVoxelPosition">World space voxel position</param> /// <param name="_voxel"></param> /// <param name="_voxelLevel"></param> public void AddVoxel(ref float3 _wsVoxelPosition, Voxel _voxel, int _voxelLevel) { if (m_level < _voxelLevel) { // Recursively add voxel until we reach the leaf level OctreeNode childNode = GetOrCreateChildNode(_wsVoxelPosition.x >= m_wsCornerMin.x + m_halfSize ? 1 : 0, _wsVoxelPosition.y >= m_wsCornerMin.y + m_halfSize ? 1 : 0, _wsVoxelPosition.z >= m_wsCornerMin.z + m_halfSize ? 1 : 0); childNode.AddVoxel(ref _wsVoxelPosition, _voxel, _voxelLevel); } else { // Accumulate m_brick.albedo += _voxel.albedo; m_brick.opacity += 1.0f; // Assume 100% opaque m_brick.normal += _voxel.normal; m_brick.variance += 0.0f; // Assume perfect directionality m_brick.accumulationCounter++; } }
public OctreeBuilder(Device _device, float3 _wsCornerMin, float _volumeSize, int _subdivisionsLevelsCount) { int subdivisionsCount = 1 << _subdivisionsLevelsCount; float voxelSize = _volumeSize / subdivisionsCount; #if LOAD_OCTREE // Load the octree OctreeNode root = null; using (System.IO.FileStream S = new System.IO.FileInfo("CornellBox_" + subdivisionsCount + ".octree").OpenRead()) using (System.IO.BinaryReader R = new System.IO.BinaryReader(S)) root = new OctreeNode(R); #else ////////////////////////////////////////////////////////////////////////// // 1] Generate a list of non-empty voxels // #if !LOAD_VOXELS // Start collecting all non-empty voxels float3 dV = voxelSize * float3.One; float3 wsVoxelMin = _wsCornerMin + 0.5f * dV; // Start voxel center float voxelDistance = (float)Math.Sqrt(dV.x * dV.x + dV.y * dV.y + dV.z * dV.z); List <Voxel> voxels = new List <Voxel>(10000000); // 10 million voxels DateTime buildStartTime = DateTime.Now; float3 voxelCenter = new float3(); voxelCenter.z = wsVoxelMin.z; for (uint Z = 0; Z < subdivisionsCount; Z++, voxelCenter.z += dV.z) { voxelCenter.y = wsVoxelMin.y; for (uint Y = 0; Y < subdivisionsCount; Y++, voxelCenter.y += dV.y) { voxelCenter.x = wsVoxelMin.x; for (uint X = 0; X < subdivisionsCount; X++, voxelCenter.x += dV.x) { float2 sceneDistance = Map(voxelCenter); if (sceneDistance.x > voxelDistance) { continue; // Scene is too far away } float3 sceneNormal = Normal(voxelCenter); float3 sceneAlbedo = Albedo(voxelCenter, sceneDistance.y); voxels.Add(new Voxel() { X = X, Y = Y, Z = Z, albedo = sceneAlbedo, normal = sceneNormal }); } } } DateTime buildEndTime = DateTime.Now; System.Diagnostics.Debug.WriteLine("Octree build time = " + (buildEndTime - buildStartTime).TotalSeconds + " seconds"); // Write to disk as it takes hell of a time to generate! using (System.IO.FileStream S = new System.IO.FileInfo("CornellBox_" + subdivisionsCount + ".voxels").Create()) using (System.IO.BinaryWriter W = new System.IO.BinaryWriter(S)) { W.Write(_wsCornerMin.x); W.Write(_wsCornerMin.y); W.Write(_wsCornerMin.z); W.Write(_volumeSize); W.Write(_subdivisionsLevelsCount); W.Write(voxels.Count); foreach (Voxel V in voxels) { W.Write(V.X); W.Write(V.Y); W.Write(V.Z); W.Write(V.albedo.x); W.Write(V.albedo.y); W.Write(V.albedo.z); W.Write(V.normal.x); W.Write(V.normal.y); W.Write(V.normal.z); } } #else // Read from disk as it takes hell of a time to generate! List <Voxel> voxels = null; using (System.IO.FileStream S = new System.IO.FileInfo("CornellBox_" + subdivisionsCount + ".voxels").OpenRead()) using (System.IO.BinaryReader R = new System.IO.BinaryReader(S)) { _wsCornerMin.x = R.ReadSingle(); _wsCornerMin.y = R.ReadSingle(); _wsCornerMin.z = R.ReadSingle(); _volumeSize = R.ReadSingle(); _subdivisionsLevelsCount = R.ReadInt32(); int voxelsCount = (int)R.ReadUInt32(); voxels = new List <Voxel>(voxelsCount); Voxel V = new Voxel(); for (int voxelIndex = 0; voxelIndex < voxelsCount; voxelIndex++) { V.X = R.ReadUInt32(); V.Y = R.ReadUInt32(); V.Z = R.ReadUInt32(); V.albedo.x = R.ReadSingle(); V.albedo.y = R.ReadSingle(); V.albedo.z = R.ReadSingle(); V.normal.x = R.ReadSingle(); V.normal.y = R.ReadSingle(); V.normal.z = R.ReadSingle(); voxels.Add(V); } } #endif ////////////////////////////////////////////////////////////////////////// // 2] Encode these voxels into an octree OctreeNode root = new OctreeNode(_wsCornerMin, _volumeSize, 0); float3 wsVoxelPosition = float3.Zero; // 2.1) Add each voxel individually foreach (Voxel V in voxels) { wsVoxelPosition.x = _wsCornerMin.x + (0.5f + V.X) * voxelSize; wsVoxelPosition.y = _wsCornerMin.y + (0.5f + V.Y) * voxelSize; wsVoxelPosition.z = _wsCornerMin.z + (0.5f + V.Z) * voxelSize; root.AddVoxel(ref wsVoxelPosition, V, _subdivisionsLevelsCount); } // // 2.2) Normalize existing voxels // root.FinalizeLeafVoxels(); // 2.3) Build mips at all levels root.BuildAllMips(); // Save the resulting octree using (System.IO.FileStream S = new System.IO.FileInfo("CornellBox_" + subdivisionsCount + ".octree").Create()) using (System.IO.BinaryWriter W = new System.IO.BinaryWriter(S)) root.SaveRoot(W); #endif }