Last active
May 28, 2023 10:49
-
-
Save knowuh/48136d7a17387e7cf6c3 to your computer and use it in GitHub Desktop.
Blender script to turn an image data block into 3D cubes...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') |
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
Thanks everyone for the tips.
I have a more performant version now.
bpy.ops
cubes are created in data mode by usingbpy.data
calls. This is faster for a few reasons including that there is no undo/redo history.attribute
input node in your shader.There are two more approaches I will explore and document here.