/// <summary> /// Computes normal vectors based on weight of each voxel. /// </summary> /// <param name="voxels">Array of voxels.</param> private void ComputeNormal(Voxel[, ,] voxels) { int width = voxels.GetLength(0); int height = voxels.GetLength(1); int depth = voxels.GetLength(2); Parallel.For(0, width, (x) => { for (int y = 0; y < height; y++) { for (int z = 0; z < depth; z++) { int xi = x == width - 1 ? x : x + 1; int xd = x == 0 ? x : x - 1; int yi = y == height - 1 ? y : y + 1; int yd = y == 0 ? y : y - 1; int zi = z == depth - 1 ? z : z + 1; int zd = z == 0 ? z : z - 1; Vector3 normal = new Vector3() { X = voxels[xi, y, z].Weight - voxels[xd, y, z].Weight, Y = voxels[x, yi, z].Weight - voxels[x, yd, z].Weight, Z = voxels[x, y, zi].Weight - voxels[x, y, zd].Weight }; normal = -normal; normal.Normalize(); voxels[x, y, z].Normal = normal; } } }); }
/// <summary> /// Computes triangle positions and normal vectors based on array of voxels. /// </summary> /// <param name="voxels">Array of voxels.</param> /// <param name="levelOfDetail">Lower value makes more triangles.</param> /// <returns>List of triangles.</returns> private List<VoxelMeshVertex> ComputeTriangles(Voxel[, ,] voxels, int levelOfDetail) { List<VoxelMeshVertex> triangles = new List<VoxelMeshVertex>(); for (int x = 0; x < voxels.GetLength(0) - levelOfDetail; x += levelOfDetail) { for (int y = 0; y < voxels.GetLength(1) - levelOfDetail; y += levelOfDetail) { for (int z = 0; z < voxels.GetLength(2) - levelOfDetail; z += levelOfDetail) { Voxel[] cubeVoxels = new Voxel[] { voxels[x, y, z], voxels[x, y, z + levelOfDetail], voxels[x + levelOfDetail, y, z + levelOfDetail], voxels[x + levelOfDetail, y, z], voxels[x, y + levelOfDetail, z], voxels[x, y + levelOfDetail, z + levelOfDetail], voxels[x + levelOfDetail, y + levelOfDetail, z + levelOfDetail], voxels[x + levelOfDetail, y + levelOfDetail, z] }; triangles.AddRange(VoxelCube.ComputeTriangles(cubeVoxels)); } } } return triangles; }
/// <summary> /// Computes ambient color for each voxel. /// </summary> /// <param name="voxels">Array of voxels.</param> private void ComputeAmbient(Voxel[, ,] voxels) { int width = voxels.GetLength(0); int height = voxels.GetLength(1); int depth = voxels.GetLength(2); float stepLength = (width * container.Settings.AmbientRayWidth / 100.0f) / container.Settings.AmbientSamplesCount; Parallel.For(0, width, (x) => { for (int y = 0; y < height; y++) { for (int z = 0; z < depth; z++) { float ambient = 0; Vector3 position = voxels[x, y, z].Position; for (int i = 0; i < poissonDisc.Length; i++) { float sample = 0; for (int j = 0; j < container.Settings.AmbientSamplesCount; j++) { // Ray starting point is situated in a small distance from center to avoid rendering artifacts. int stepNumber = j + 2; int cx = (int)Helper.Clamp(position.X + stepNumber * stepLength * poissonDisc[i].X, 0, width - 1); int cy = (int)Helper.Clamp(position.Y + stepNumber * stepLength * poissonDisc[i].Y, 0, height - 1); int cz = (int)Helper.Clamp(position.Z + stepNumber * stepLength * poissonDisc[i].Z, 0, depth - 1); sample += voxels[cx, cy, cz].Weight > 0 ? 0 : 1; } ambient += sample / container.Settings.AmbientSamplesCount; } voxels[x, y, z].Ambient = ambient / poissonDisc.Length; } } }); }