public PackedUniformVolume ReduceOnce(PackedUniformVolume srcPackedUniformVolume) { var srcPackedVolumeElementCount = srcPackedUniformVolume.Voxels.Length; var srcPackedVolume = new ComputeBuffer(srcPackedVolumeElementCount, sizeof(uint)); srcPackedVolume.SetData(srcPackedUniformVolume.Voxels); _reduceOneComputeShader.SetBuffer(_reduceOneKernelId, "src_packed_volume", srcPackedVolume); var srcPackedVolumeDimensions = srcPackedUniformVolume.GetVolumeBitDimensions(); _reduceOneComputeShader.SetInts("src_packed_volume_bit_dimensions", srcPackedVolumeDimensions.x, srcPackedVolumeDimensions.y, srcPackedVolumeDimensions.z); var dstPackedVolumeElementCount = math.max(1, srcPackedVolumeElementCount / 2); var dstPackedVolume = new ComputeBuffer(dstPackedVolumeElementCount, sizeof(uint)); _reduceOneComputeShader.SetBuffer(_reduceOneKernelId, "dst_packed_volume", dstPackedVolume); var dstPackedVolumeDimensions = srcPackedVolumeDimensions / 2; _reduceOneComputeShader.SetInts("dst_packed_volume_bit_dimensions", dstPackedVolumeDimensions.x, dstPackedVolumeDimensions.y, dstPackedVolumeDimensions.z); var dispatchVolumeDimensions = math.max(new int3(1), dstPackedVolumeDimensions / 8); _reduceOneComputeShader.Dispatch( _reduceOneKernelId, dispatchVolumeDimensions.x, dispatchVolumeDimensions.y, dispatchVolumeDimensions.z ); var dstPackedVolumeData = new uint[srcPackedVolumeElementCount / 2]; dstPackedVolume.GetData(dstPackedVolumeData); srcPackedVolume.Release(); dstPackedVolume.Release(); return(new PackedUniformVolume( srcPackedUniformVolume.VoxelWorldScaleInMeters * 2, srcPackedUniformVolume.Depth - 1) { Voxels = dstPackedVolumeData }); }
private void CreateVoxelData() { PackedUniformVolume = new PackedUniformVolume(PackedUniformVolume.VoxelWorldScaleInMeters, PackedUniformVolume.Depth); var voxelHalfScale = PackedUniformVolume.GetVolumeWorldScale() / PackedUniformVolume.GetSideBitCount() / 2.0f; var index = 0; var volumeDimensions = PackedUniformVolume.GetVolumeBitDimensions(); for (var y = 0; y < volumeDimensions.y; y++) { for (var z = 0; z < volumeDimensions.z; z++) { for (var x = 0; x < volumeDimensions.x; x++) { var position = new float3(x, y, z); var worldPosition = (float3)transform.position + position * PackedUniformVolume.VoxelWorldScaleInMeters; if (Physics.OverlapBox(worldPosition, voxelHalfScale).Length > 0) { var packedIndex = index / 32; var bitIndex = index % 32; PackedUniformVolume.Voxels[packedIndex] |= 1u << bitIndex; } index++; } } } Debug.Log($"Volume dimensions: {PackedUniformVolume.GetVolumeBitDimensions()}"); }
public static Mesh CreateDebugMesh(PackedUniformVolume packedUniformVolume) { int[] GetQuadIndicesArray(int i0, int i1, int i2, int i3) { return(new[] { i0, i1, i2, i2, i3, i0, }); } var vertices = new List <Vector3>(); var indices = new List <int>(); var voxelHalfScale = packedUniformVolume.GetVolumeWorldScale() / packedUniformVolume.GetSideBitCount() / 2.0f; var index = 0; var volumeDimensions = packedUniformVolume.GetVolumeBitDimensions(); for (var y = 0; y < volumeDimensions.y; y++) { for (var z = 0; z < volumeDimensions.z; z++) { for (var x = 0; x < volumeDimensions.x; x++) { var position = new float3(x, y, z); var worldPosition = position * packedUniformVolume.VoxelWorldScaleInMeters; var packedIndex = index / 32; var bitIndex = index % 32; var isOccupied = (packedUniformVolume.Voxels[packedIndex] & (1u << bitIndex)) > 0; if (isOccupied) { vertices.AddRange(new Vector3[] { worldPosition + new float3(-voxelHalfScale.x, -voxelHalfScale.y, -voxelHalfScale.z), worldPosition + new float3(-voxelHalfScale.x, -voxelHalfScale.y, +voxelHalfScale.z), worldPosition + new float3(+voxelHalfScale.x, -voxelHalfScale.y, +voxelHalfScale.z), worldPosition + new float3(+voxelHalfScale.x, -voxelHalfScale.y, -voxelHalfScale.z), worldPosition + new float3(-voxelHalfScale.x, +voxelHalfScale.y, -voxelHalfScale.z), worldPosition + new float3(-voxelHalfScale.x, +voxelHalfScale.y, +voxelHalfScale.z), worldPosition + new float3(+voxelHalfScale.x, +voxelHalfScale.y, +voxelHalfScale.z), worldPosition + new float3(+voxelHalfScale.x, +voxelHalfScale.y, -voxelHalfScale.z), }); var vertexCount = vertices.Count; indices.AddRange(GetQuadIndicesArray(vertexCount - 4, vertexCount - 3, vertexCount - 2, vertexCount - 1)); indices.AddRange(GetQuadIndicesArray(vertexCount - 5, vertexCount - 6, vertexCount - 7, vertexCount - 8)); indices.AddRange(GetQuadIndicesArray(vertexCount - 5, vertexCount - 1, vertexCount - 2, vertexCount - 6)); indices.AddRange(GetQuadIndicesArray(vertexCount - 8, vertexCount - 7, vertexCount - 3, vertexCount - 4)); indices.AddRange(GetQuadIndicesArray(vertexCount - 8, vertexCount - 4, vertexCount - 1, vertexCount - 5)); indices.AddRange(GetQuadIndicesArray(vertexCount - 7, vertexCount - 6, vertexCount - 2, vertexCount - 3)); } index++; } } } var mesh = new Mesh { indexFormat = UnityEngine.Rendering.IndexFormat.UInt32, vertices = vertices.ToArray(), triangles = indices.ToArray() }; mesh.RecalculateNormals(); return(mesh); }
public List <PackedUniformVolume> Reduce(PackedUniformVolume packedUniformVolume) { // Calculate the size of the array containing all the voxel data for all layers var bitCount = 0; for (var i = 0; i <= packedUniformVolume.Depth; i++) { bitCount += PackedUniformVolume.GetVolumeBitCount(i); } var dataCount = (int)math.ceil(bitCount / 32.0); // Setup an buffer that will contain all the reduction layers and fill it with the finest layer values // To start the reduction process var packedVolumes = new ComputeBuffer(dataCount, sizeof(uint)); packedVolumes.SetData(packedUniformVolume.Voxels); _reduceComputeShader.SetBuffer(_reduceKernelId, "packed_volumes", packedVolumes); // Setup an buffer that will contain all the hashed reduction layers var hashedVolumes = new ComputeBuffer(bitCount, sizeof(uint)); _reduceComputeShader.SetBuffer(_reduceKernelId, "hashed_volumes", hashedVolumes); // Bit offsets to use when reading from src layer or writing to the dst layer var dstPackedVolumeStartOffsetBitIndex = 0; var dstHashedVolumeStartOffsetIndex = 0; for (var i = packedUniformVolume.Depth; i > 0; i--) { // Set the src packed volume to the dst packed volume for next iteration var srcPackedVolumeStartOffsetBitIndex = dstPackedVolumeStartOffsetBitIndex; var srcHashedVolumeStartOffsetIndex = dstHashedVolumeStartOffsetIndex; // Assign the dimensions of the source volume var srcPackedVolumeBitDimensions = PackedUniformVolume.GetVolumeBitDimensions(i); _reduceComputeShader.SetInts("src_packed_volume_bit_dimensions", srcPackedVolumeBitDimensions.x, srcPackedVolumeBitDimensions.y, srcPackedVolumeBitDimensions.z); // Assign the offset to use when reading bits from the src layer _reduceComputeShader.SetInt("src_packed_volume_start_offset_bit_index", srcPackedVolumeStartOffsetBitIndex); // Assign dimensions of the destination volume var dstPackedVolumeBitDimensions = srcPackedVolumeBitDimensions / 2; _reduceComputeShader.SetInts("dst_packed_volume_bit_dimensions", dstPackedVolumeBitDimensions.x, dstPackedVolumeBitDimensions.y, dstPackedVolumeBitDimensions.z); // Increment destination offset so we write to the area reserved for the next layer dstPackedVolumeStartOffsetBitIndex += PackedUniformVolume.GetVolumeBitCount(i); _reduceComputeShader.SetInt("dst_packed_volume_start_offset_bit_index", dstPackedVolumeStartOffsetBitIndex); _reduceComputeShader.SetInts("src_hashed_volume_dimensions", srcPackedVolumeBitDimensions.x, srcPackedVolumeBitDimensions.y, srcPackedVolumeBitDimensions.z); _reduceComputeShader.SetInt("src_hashed_volume_start_offset_index", srcHashedVolumeStartOffsetIndex); _reduceComputeShader.SetInts("dst_hashed_volume_dimensions", dstPackedVolumeBitDimensions.x, dstPackedVolumeBitDimensions.y, dstPackedVolumeBitDimensions.z); // Increment destination offset so we write to the area reserved for the next layer dstHashedVolumeStartOffsetIndex += PackedUniformVolume.GetVolumeBitCount(i); _reduceComputeShader.SetInt("dst_hashed_volume_start_offset_index", dstHashedVolumeStartOffsetIndex); // Dispatch to the GPU. /8 is used for better utilization of the GPU var dispatchVolumeDimensions = math.max(new int3(1), dstPackedVolumeBitDimensions / 8); _reduceComputeShader.Dispatch( _reduceKernelId, dispatchVolumeDimensions.x, dispatchVolumeDimensions.y, dispatchVolumeDimensions.z ); } //Collect all the reduction data from the gpu var reducedPackedVolumes = new uint[dataCount]; packedVolumes.GetData(reducedPackedVolumes); var reducedHashedVolumes = new uint[bitCount]; hashedVolumes.GetData(reducedHashedVolumes); var packedVolumeList = new List <PackedUniformVolume>(); var voxelWorldScaleInMeters = packedUniformVolume.VoxelWorldScaleInMeters; var bitOffset = 0; for (var i = packedUniformVolume.Depth; i > 0; i--) { bitCount = PackedUniformVolume.GetVolumeBitCount(i); var intCount = (int)math.ceil(bitCount / 32.0); var intOffset = (int)math.ceil(bitOffset / 32.0); packedVolumeList.Add(new PackedUniformVolume(voxelWorldScaleInMeters, i) { Voxels = reducedPackedVolumes.Skip(intOffset).Take(intCount).ToArray(), Hashes = reducedHashedVolumes.Skip(bitOffset).Take(bitCount).ToArray() }); bitOffset += bitCount; voxelWorldScaleInMeters *= 2; } packedVolumes.Dispose(); DEBUG = packedVolumeList; return(packedVolumeList); }
private void Update() { if (RecreateVoxels) { RecreateVoxels = false; var startTime = Time.realtimeSinceStartup; CreateVoxelData(); var endTime = Time.realtimeSinceStartup; Debug.Log($"Create voxel data time: {endTime - startTime}s"); } if (CreateDebugMesh) { CreateDebugMesh = false; var mesh = VoxelizationVisualizer.CreateDebugMesh(PackedUniformVolume); var visualizerGameObject = new GameObject("Debug mesh"); visualizerGameObject.AddComponent <MeshFilter>().mesh = mesh; visualizerGameObject.AddComponent <MeshRenderer>().sharedMaterial = VisualizerMaterial; } if (ReduceOneCpu) { ReduceOneCpu = false; SrcPackedVolume = PackedUniformVolume.Voxels; SrcPackedVolumeBitDimensions = (uint3)PackedUniformVolume.GetVolumeBitDimensions(); DstPackedVolume = new uint[SrcPackedVolume.Length / 2]; DstPackedVolumeBitDimensions = SrcPackedVolumeBitDimensions / 2; Reduce(SrcPackedVolume, SrcPackedVolumeBitDimensions, DstPackedVolume, DstPackedVolumeBitDimensions); var srcPackedUniformVolume = new PackedUniformVolume(0.1f, 5) { Voxels = SrcPackedVolume }; var srcMesh = VoxelizationVisualizer.CreateDebugMesh(srcPackedUniformVolume); var srcGameObject = new GameObject("Reduce one CPU source debug mesh"); srcGameObject.AddComponent <MeshFilter>().mesh = srcMesh; srcGameObject.AddComponent <MeshRenderer>().sharedMaterial = VisualizerMaterial; var dstPackedUniformVolume = new PackedUniformVolume(0.2f, 4) { Voxels = DstPackedVolume }; var dstMesh = VoxelizationVisualizer.CreateDebugMesh(dstPackedUniformVolume); var dstGameObject = new GameObject("Reduce one CPU destination debug mesh"); dstGameObject.AddComponent <MeshFilter>().mesh = dstMesh; dstGameObject.AddComponent <MeshRenderer>().sharedMaterial = VisualizerMaterial; } if (ReduceOneGpu) { ReduceOneGpu = false; var startTime = Time.realtimeSinceStartup; var dstPackedUniformVolume = _svdagManager.ReduceOnce(PackedUniformVolume); var endTime = Time.realtimeSinceStartup; Debug.Log($"Reduce one GPU time: {endTime-startTime}s"); var dstMesh = VoxelizationVisualizer.CreateDebugMesh(dstPackedUniformVolume); var dstGameObject = new GameObject("Reduce one GPU debug mesh"); dstGameObject.AddComponent <MeshFilter>().mesh = dstMesh; dstGameObject.AddComponent <MeshRenderer>().sharedMaterial = VisualizerMaterial; } if (ReduceGpu) { ReduceGpu = false; var startTime = Time.realtimeSinceStartup; var reducedPackedUniformVolumes = _svdagManager.Reduce(PackedUniformVolume); var endTime = Time.realtimeSinceStartup; Debug.Log($"Reduce GPU time: {endTime - startTime}s"); foreach (var reducedPackedUniformVolume in reducedPackedUniformVolumes) { var dstMesh = VoxelizationVisualizer.CreateDebugMesh(reducedPackedUniformVolume); var dstGameObject = new GameObject("Reduced GPU debug mesh"); dstGameObject.AddComponent <MeshFilter>().mesh = dstMesh; dstGameObject.AddComponent <MeshRenderer>().sharedMaterial = VisualizerMaterial; } } }