Your First 3-D Scene¶
Everything so far has been flat 2-D shapes, but Algan’s scene is genuinely three-dimensional and its renderer is a full-featured ray tracer: 3-D objects are lit, they occlude each other correctly, and they can reflect and refract, if their material allows so.
Nothing new is required to use 3-D. 3-D Mobs spawn, move and rotate exactly like 2-D ones; the differences are that they respond to light and that you now have a camera worth moving.
Depth¶
The third axis is OUT (towards the viewer) and IN (away from it). The
default camera sits at OUT * 7 looking at the ORIGIN, so moving something
IN pushes it away and makes it look smaller:
Example: ThreeDDepth ¶
from algan import *
with Off():
cubes = Group([Cube(size=0.8, color=BLUE).move(IN * 1.6 * i + RIGHT * 0.9 * i)
for i in range(4)]).spawn()
with Seq(runtime=3):
cubes.rotate(360, UP, about=ORIGIN)
Scene.wait()
Scene.save_video()
The four cubes are identical; the further ones look smaller because the default camera is a perspective camera.
Moving the Camera¶
get_camera() returns the active Camera,
which is also a Mob. That means everything you’ve learned so far also applies to the camera.
So, we can animate a camera movement as follows:
Example: ThreeDCamera ¶
from algan import *
ball = Sphere(radius=1, color=BLUE).spawn()
camera = Scene.get_camera()
with Sync(runtime=3, easing=easings.identity):
camera.rotate(180, UP, about=ORIGIN)
ball.move(UP * 0.8)
Scene.wait()
Scene.save_video()
To aim the camera somewhere specific, look_at()
turns it to face a point, and center_on() frames a given Mob.
See also
Cameras – field of view, clipping planes, near-orthographic projection, and how to follow a moving subject.
Lighting¶
By default, every scene starts with one white PointLight above and to the right of
the camera. That is what gives 3-D shapes their shading, and it is why a Sphere
reads as a ball rather than a flat disc.
Lights are Mobs, so you animate them like anything else.
get_light_sources() returns a list of all mobs in the scene
which act as light sources.
Example: ThreeDLightOrbit ¶
from algan import *
ball = Sphere(radius=1.2, color=BLUE).spawn()
light = Scene.get_light_sources()[0]
with Seq(runtime=4, easing=easings.identity):
light.orbit(360, OUT, about=ORIGIN)
Scene.wait()
Scene.save_video()
Casting Shadows¶
Ray-traced shadows are off by default because they make rendering noticeably slower. You can turn them on with one setting:
Example: ThreeDShadows ¶
from algan import *
SETTINGS.raytracing.set(shadows=True)
with Off():
Scene.clear_lights()
DirectionalLight(location=UP * 8 + RIGHT * 4 + OUT * 4, target=ORIGIN,
color=WHITE, intensity=3).spawn()
AmbientLight(color=WHITE, intensity=0.3).spawn()
sphere = Sphere(radius=0.8, color=BLUE).move(UP * 0.4).spawn()
Cube(size=4, color=GREY).move(DOWN * 3).spawn()
sphere.move(RIGHT)
Scene.wait()
Scene.save_video()
Two things worth noticing:
clear_lights()drops the default light first, so the lighting is entirely yours.The shadow is hard-edged, because the light has no size. Giving the sun an angular size softens the edge.
See also
Lighting and Shadows – every light type, soft shadows, environment maps, and how to build a three-point rig.
Curved Surfaces¶
Surface allows you to define a manifold surface of any shape,
by providing a function which maps two intrinsic coordinates u and v
(each in [0, 1]) to a coordinate in 3-D space. The function you provide must
handle batched tensors.
Example: ThreeDSurface ¶
from algan import *
import torch
def saddle(uv):
x = uv[..., :1] * 4 - 2
y = uv[..., 1:] * 4 - 2
return torch.cat((x, y, (x ** 2 - y ** 2) * 0.4), -1)
checker = get_checkerboard((BLUE, BLUE_E))
surface = Surface(saddle, color_texture=checker).spawn()
with Seq(runtime=3):
surface.rotate(60, RIGHT)
surface.rotate(360, OUT)
Scene.wait()
Scene.save_video()
See also
Images and Textures – painting an image or a per-texel material property across a surface, and wrapping a map onto a globe.
Materials¶
By default 3-D shapes get a simple diffuse shading. For metal, plastic, glass and so on, apply a material before spawning:
Example: ThreeDMaterials ¶
from algan import *
metal = Sphere(radius=0.8, color=RED).move(LEFT * 1.8).set_material(
MeshStandardMaterial(metalness=1.0, roughness=0.2))
plastic = Sphere(radius=0.8, color=BLUE).move(RIGHT * 1.8).set_material(
MeshStandardMaterial(metalness=0.0, roughness=0.6))
with Sync():
metal.spawn()
plastic.spawn()
with Seq(runtime=3):
metal.roughness = 0.9
Scene.wait()
Scene.save_video()
Once a mob has used
set_material(),
that material’s properties become animatable attributes of the mob, so
metal.roughness = 0.9 animates like any other change.
Important
set_material(),
set_shader() and
set_fragment_shader()
must all be called before
spawn().
See also
Shaders and Materials – the full material catalogue and custom shaders.
Reflections and Glass – what
metalness,roughnessandtransmissiondo to rays.
More 3-D¶
3-D rendering is too broad a topic to cover in one tutorial, so if you want to learn more, these advanced tutorials pick up where this one stops:
Cameras – field of view, clipping, orthographic projection, camera animation.
Lighting and Shadows – every light type, soft shadows, environment maps.
Shaders and Materials – the full material catalogue, custom shaders.
Reflections and Glass – mirrors and refraction.
Importing 3-D Models – importing
.glband.fbxmodel assets.Renderer Limitations – what the renderer does not do, and where its approximations show.