easyshader
easyshader is a tool for rendering 3D scenes, exporting .ply files for 3D printing and creating animations, powered by Signed Distance Fields (SDFs) and written in Python/Taichi.
It was created to enable drawing 3D shapes using a very concise syntax, and is packed with 3D primitives, transformations and smooth operators.
Basic usage
# If you're running this from Google Colab or you simply don't have easyshader installed, you can install it by running: #%pip install git+https://github.com/marceloprates/easyshader from easyshader import *
Use the "color" parameter to paint your object
easyshader primitives
You can choose from the following primitives:
- Box
- BoxFrame
- Callable
- Cone
- Cyllinder
- Icosahedron
- Iterable
- Line
- Number
- Octahedron
- Shape
- Sphere
- Torus
for obj in [Sphere(1), Cyllinder(1,1), Cone(1,2), Torus(1,.2), Box(1), BoxFrame(1,.1), Icosahedron(1), Octahedron(1)]: display(obj.paint('#0B4F6C'))
Exporting to .ply for usage in Blender or 3D printing
Export your creations to polygon meshes for 3d printing or rendering on external apps (e.g. Blender)
#Icosahedron(1).to_mesh(simplify = 20, save_path='icosahedron.obj')Color your creations using functions defined over x,y,z and a color palette:
palette = ['#B80C09','#0B4F6C','#01BAEF','#FBFBFF','#040F16'] x = Box(.8,'palette(6*(x+y+z))',palette = palette) x = x.isometric() x
Binary operations
Union:
BoxFrame(1,.1,'#0B4F6C') + Sphere(.5,'#B80C09')
Difference:
Box(1,'#0B4F6C') - Sphere(1.2)
Intersection:
Icosahedron(1,'#0B4F6C') & Sphere(1.1)
Examples
A coffee cup!
x = Sphere(1, 'palette(6*(x+y+z))', palette = palette) x = x.twist(4) x &= Cyllinder(.5,.5) x -= Cyllinder(.4,.5) + 'dy .1' x += (Torus(.3,.05) & Shape('-x')) + 'dx .5' x = x.isometric() x += 'rx -pi/3' x
Create videos!
Use the 't' (time) variable to control the animation
x = BoxFrame(1,.1,'palette(6*t + 6*(x+y+z))',palette = palette) x += '.1*sin(t)' x += 'ry t' x += 'rx t' x.animate(frames = 60, framerate = 15, iterations = 1000)
Animating..: 100%|██████████| 59/59 [08:59<00:00, 9.14s/it]
Transformations
Translation
Scale
Rotation
Advanced transformations
You can use x,y,z (and the time parameter, t) as variables in transformations such as translation, rotation, scale
BoxFrame(1,.1,'#f44') + 'dx .2*y'
Other operations
Twist along an axis
Box(1,'#f44').twist(2,'y')
Create an "onion" shape
# Create an onion from a box x = Box(1,'#f44').onion() # Cut a hole in the onion x &= Shape('z') x
Smooth operators
Smooth union
sphere = (Sphere(.5,'#f44') + 'dx -.5') box = (Box(.5,'#4ff') + 'dx +.5') # Normal union display(sphere + box) # Smooth union display(sphere <<su(.5)>> box)
Smooth difference
sphere = Sphere(1.1) box = Box(1,'#f44') # Normal difference display(box - sphere) # Smooth difference display(box <<sd(.5)>> sphere)
Smooth intersection
sphere = Sphere(1) box = Box(.9,'#f44') # Normal intersection display(box & sphere) # Smooth intersection display(box <<si(.5)>> sphere)
Use custom taichi functions!
@ti.func def mandelbulb_fn(p,max_it,k): z, dr, r = p, 1., 0. for i in range(max_it): r, steps = z.norm(), i if r > 4.0: break; # convert to polar coordinates theta = ti.acos(z.z/r) phi = ti.atan2(z.y,z.x) dr = r**2 * 3 * dr + 1.0 # scale and rotate the point zr = r**3 theta = theta*3 phi = phi*3 # convert back to cartesian coordinates z = zr*ti.Vector([ ti.sin(theta)*ti.cos(phi), ti.sin(phi)*ti.sin(theta), ti.cos(theta) ]) z+=p out = ti.log(r)*r/dr if k==1: out = r return out # Create mandelbulb shape mandelbulb = Shape( # Call 'mandelbulb_fn' to compute the SDF sdf = '.2*mandelbulb_fn(p,10,0)', #color = 'palette(200*mandelbulb_fn(1.1*p))', color = 'palette(12*mandelbulb_fn(p,10,1))', palette = ['#0C0F0A', '#FBFF12', '#FF206E', '#41EAD4', '#FFFFFF'], # Pass 'mandelbulb_fn' as a keyword argument mandelbulb_fn = mandelbulb_fn, ) mandelbulb = mandelbulb.isometric() # Only run the lines below if you have a very powerful GPU! #mandelbulb.render( # resolution = (2480,3508), # fov = .25, # max_raymarch_steps = 200, # iterations = 1000, # verbose = True #)
Rendering scene...: 100%|██████████| 1000/1000 [20:02<00:00, 1.20s/it]































