Voxelate
What is a Voxel ? 🧊
In computer graphics, a
voxel, also known as a 3D pixel, is a 3D cube located on a three-dimensional grid used to create 3D models.
Similar to a pixel in a 2D image, a voxel can contain a specific location inside a
3D grid
and has a color value assigned to it.
All individual voxels are the same size, however voxels can then be combined in the same grid at different positions with different colors to create what is known as a voxel model.
What is a voxel ?
From the left, Original shape · Voxelated shape
In addition, a voxel is also used as the smallest unit composing an architectural space. This is because
voxels have no deadspace
and can be used for all architectural purposes and reasonable.
Therefore It's being used in many architectural offices. The process of architecture below shows how to apply
voxelization to the building.
This article covers automation of voxelization and how change to the voxelated shape when given specific shape. And let's put some information in the voxel.
mvrdv's RED7
3D grid & conditions map
First we should create
grids
and to create a grid, need a bounding box of given shape. The angle of bounding box sets sun faced like the
picture 4
above.
Through the segments X and Y of the bounding box facing the sun, we can get a 3D voxel grid of the desired size that fully fills the bounding box.
Then we check if the voxel is inside the given shape,
filter only the voxel that is inside the shape.
This grid of voxels can be represented as a
List,
one of Python's data structures. And the data structure List can express
multi-dimensional
data.
Let's take a look at a simple example below.
The given simple conditions are as follows.
â‘ voxel size: 2.0 â‘¡ given shape: sphere that radius: 20.0. As mentioned earlier, in the last picture below you can see the voxel data structure represented as a List in Python.
Data of
0
means empty voxels and data of
1
means filled voxels.
3D grid & conditions map
From the left, Given sphere · Voxelated sphere · Conditions map(9th voxels grid)
Determining the voxel's condition
Now, we can determine each voxel condition through the data we have.
Examples include
interior voxels, exterior voxels, corner voxels, and roof voxels
etc.
def __get_voxels_condition_list(self):
inside_grid_centroid_integer = [int(b) for b in self.inside_grid_centroid]
voxel_3d_list = self.__get_reshaped_list(inside_grid_centroid_integer, *self.cols)
roof_3d_list = self.__get_default_3d_list()
exterior_3d_list = self.__get_default_3d_list()
exterior_both_3d_list = self.__get_default_3d_list()
exterior_corner_3d_list = self.__get_default_3d_list()
exterior_corner_u_3d_list = self.__get_default_3d_list()
exterior_corner_o_3d_list = self.__get_default_3d_list()
for zi, z_list in enumerate(voxel_3d_list):
prev_zi = (zi - 1) % len(voxel_3d_list)
next_zi = (zi + 1) % len(voxel_3d_list)
for yi, y_list in enumerate(z_list):
prev_yi = (yi - 1) % len(z_list)
next_yi = (yi + 1) % len(z_list)
for xi, x in enumerate(y_list):
prev_x = bool(y_list[(xi - 1) % len(y_list)])
next_x = bool(y_list[(xi + 1) % len(y_list)])
back_x = bool(z_list[prev_yi][xi])
frnt_x = bool(z_list[next_yi][xi])
exterior_count = [prev_x, next_x, back_x, frnt_x].count(False)
is_roof = bool(x) and not bool(voxel_3d_list[next_zi][yi][xi])
is_exterior_both = bool(x) and (not prev_x and not next_x) or (not back_x and not frnt_x)
is_exterior = bool(x) and any([not prev_x, not next_x, not back_x, not frnt_x]) and not is_exterior_both
is_exterior_corner = bool(x) and exterior_count == 2 and not is_exterior_both
is_exterior_corner_u = bool(x) and exterior_count == 3
is_exterior_corner_o = bool(x) and exterior_count == 4
if is_roof:
roof_3d_list[zi][yi][xi] = 1
if is_exterior:
exterior_3d_list[zi][yi][xi] = 1
if is_exterior_both:
exterior_both_3d_list[zi][yi][xi] = 1
if is_exterior_corner:
exterior_corner_3d_list[zi][yi][xi] = 1
if is_exterior_corner_o:
exterior_corner_o_3d_list[zi][yi][xi] = 1
if is_exterior_corner_u:
exterior_corner_u_3d_list[zi][yi][xi] = 1
interior_3d_list = self.__get_default_3d_list()
for zi, (z_list, e_list) in enumerate(zip(voxel_3d_list, exterior_3d_list)):
for yi, (z, e) in enumerate(zip(z_list, e_list)):
for xi, (zx, ex) in enumerate(zip(z, e)):
interior_3d_list[zi][yi][xi] = zx - ex
grid_3d_list = self.__get_reshaped_list(self.grid, *self.cols)
return (
voxel_3d_list,
grid_3d_list,
roof_3d_list,
exterior_3d_list,
exterior_both_3d_list,
exterior_corner_3d_list,
exterior_corner_o_3d_list,
exterior_corner_u_3d_list,
interior_3d_list
)
To determine the ROOF voxel, simply
check the condition of the upper voxel.
Likewise, to determine the CORNER voxel, simply check previous voxel, next voxel, front voxel, back voxel.
The diagram below shows how red voxels are estimated to be CORNER voxels. Please refer to the link above for detailed code related to determining the voxel condition.
Determining the voxel's condition
From the left, Target voxel · Check surround voxel conditions
Voxel unit matching by condition
Several predefined VoxelUnits can be matched through the condition of each voxel determined based on the conditions map.
You can design and apply a unit suitable for the desired voxel size, and if you wish, you can
resize the voxel size and apply it.
And for voxel matching, it also supports
rotation function by calculating the angle of the voxel.
VoxelUnits
From the left, Exterior · Exterior both · Exterior corner · Exterior corner O · Exterior corner U
class VoxelUnits:
def __init__(self, exterior_unit, exterior_both_unit, exterior_corner_unit, exterior_corner_o_unit, exterior_corner_u_unit):
self.unit_bounding_box = gh.BoundingBox(exterior_unit, WORLD_XY).box
self.exterior_unit_centroid = gh.Volume(self.unit_bounding_box).centroid
self.voxel_face_edges = gh.DeconstructBrep(self.unit_bounding_box.ToBrep().Faces[0].ToBrep()).edges
self.scale_factor = self.voxel_size / self.voxel_face_edges[0].GetLength()
self.rotated_exterior_unit = self.__get_scaled_unit(exterior_unit)
self.rotated_exterior_both_unit = self.__get_scaled_unit(exterior_both_unit)
self.rotated_exterior_corner_unit = self.__get_scaled_unit(exterior_corner_unit)
self.rotated_exterior_corner_u_unit = self.__get_scaled_unit(exterior_corner_u_unit)
self.rotated_exterior_corner_o_unit = self.__get_scaled_unit(exterior_corner_o_unit)
def __get_joined_mesh(self, unit):
return gh.MeshJoin(rg.Mesh.CreateFromBrep(unit, rg.MeshingParameters(0)))
def __get_rotated_unit(self, unit):
return gh.Rotate(
self.__get_joined_mesh(unit),
self.converted_sun_facing_angle,
self.exterior_unit_centroid
).geometry
def __get_scaled_unit(self, unit):
return gh.Scale(
self.__get_rotated_unit(unit),
self.exterior_unit_centroid,
self.scale_factor
).geometry if self.is_needed_resize else self.__get_rotated_unit(unit)
Finally, I'll wrap up the article by showing some examples of each of the above VoxelUnits applied. See the
link for details.
RED7
From the left, Given shape · Matched VoxeUnits · Visualized each voxel's condition · Shades of voxelated shape
79&PARK
From the left, Given shape · Matched VoxeUnits · Visualized each voxel's condition · Shades of voxelated shape
KING TORONTO
From the left, Given shape · Matched VoxeUnits · Visualized each voxel's condition · Shades of voxelated shape
VANCOUVER HOUSE
From the left, Given shape · Matched VoxeUnits · Visualized each voxel's condition · Shades of voxelated shape