Skip to content

Instantly share code, notes, and snippets.

@knowuh
Last active May 28, 2023 10:49
Show Gist options
  • Save knowuh/48136d7a17387e7cf6c3 to your computer and use it in GitHub Desktop.
Save knowuh/48136d7a17387e7cf6c3 to your computer and use it in GitHub Desktop.
Blender script to turn an image data block into 3D cubes...
import bpy
import colorsys
"""
cubify-image.py - Turns each pixel of an image into a scaled cube.
Noah Paessel | @knowuh - updated on 2022-02-13 (test w Blender 3.1b)
MIT license http://opensource.org/licenses/MIT
WARNING: This script will generate a thousands objects (one per image pixel)
I recommend only using it with image with less than 40,000 pixels (200x200).
"""
# We use MATERIAL named 'obj_color'. If it doesn't exist, create it.
# NB: This material is used for all cubes. This material will use an
# attribute node to read the 'custom_color' attribute from the cube.
material_name = 'obj_color'
mats = bpy.data.materials
material = mats.get(material_name, mats.new(material_name))
# We put all our objects in a single COLLECTION
collection = bpy.data.collections.new("pixels")
bpy.context.scene.collection.children.link(collection)
# We make prototype cube for our make_cube function. We do this so we can
# reuse the mesh, and avoid creating a new one each time.
bpy.ops.mesh.primitive_cube_add()
default_cube = bpy.context.object
default_cube.data.materials.append(material)
mesh = default_cube.data
def make_cube(location, color):
"""
Create a single cube at the given location with the given color.
location: the location of the cube
color: the color of the cube
"""
x, y, unused_z = location
# break out the components of the color
r, g, b = color[0:3]
h, s, v = colorsys.rgb_to_hsv(r, g, b)
# The height of our cube, based on a component of HSV or RGB
size = 16 * s
location = [x, y, size]
scale = [0.9, 0.9, size]
cube = bpy.data.objects.new(name='pixel', object_data=mesh)
cube.scale = scale
cube.location = location
# Assign a 'custom_color' attribute value to this object.
# Used by the material for the color of the cube.
cube['custom_color'] = color
collection.objects.link(cube)
def cubify(image_name):
"""
Invokes the make_cube() function for each pixel in the image.
The image must already exist in a data block named 'image_name'.
"""
myImage = bpy.data.images[image_name]
color_chans = myImage.channels
width, height = myImage.size
pixels = myImage.pixels
for y in range(0, width):
for x in range(0, height):
block_number = (y * width) + x
color = pixels[block_number *
color_chans:block_number * color_chans + color_chans]
if len(color) < color_chans:
break
if len(pixels) < block_number:
break
make_cube([x * 2, y * 2, 0], color)
# Track our progress update on for each row in terminal output:
print("y: %(y)04d / %(height)04d" % {"y": y, "height": height})
# To test the make_cube() function:
# make_cube([0,0,0], [1.0,0.2,0.3, 1.0])
# To voxelize an image:
# cubify('test.png')
@knowuh
Copy link
Author

knowuh commented Mar 21, 2022

FWIW, this turned into a Bmesh add-on. There is a new Repo, PRs welcome:
https://github.com/knowuh/blender-image-voxel

Video demo: https://youtu.be/kH-hKRg6rPo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment