The 3D features of diagrams
are changing even more rapidly than
the rest of the library. The examples in this tutorial may not work
in the next release! This tutorial is provided for those interested
in trying out cutting-edge features; we welcome feedback and
contributions.
Diagrams can be used to model three dimensional scenes, as well. There is currently one 3D Backend, diagrams-povray, which uses POV-Ray to raytrace the scene to PNG. (Note that diagrams-povray is currently unreleased; you can obtain and build it from github.) Many functions used for 2D diagrams have polymorphic types and can also be used in 3D. Some are specific to three dimensions—many of these are introduced below.
Every 3D scene rendered by diagrams-povray
needs 3 elements: an
object, a light, and a camera. (Other backends may work differently.)
Without a light, the scene will appear completely black. The
primitive sphere
is converted to a Diagram
by skin
, similar to
how strokePath
works in 2D.
> import Diagrams.Backend.POVRay
> import Diagrams.Prelude
>
> cam = mm50Camera # translate (r3 (0,0,10))
>
> xy :: Direction V3 Double
> xy = direction . r3 $ (-1, -1, -0.5)
>
> light = parallelLight xy white
>
> color :: Colour Double -> Diagram POVRay -> Diagram POVRay
> color c = diffuse 0.5 . ambient 0.1 . sc c
>
> example :: Diagram POVRay
> example = centerX $ cat unitX
> [skin sphere # color cyan, skin sphere # color green, skin sphere # color blue]
>
> main :: IO ()
> main = putStrLn $ renderDia POVRay POVRayOptions $ mconcat [example, cam, light]
We introduce a camera, a light which casts parallel rays,
and some spheres (of radius 1). We move the camera away from the
origin, so that the spheres are in its field of view. The translate
function is polymorphic, and takes a vector V3 Double
because cameras are
defined in V3 Double
. Now we render our scene. At the command line:
cabal exec runhaskell 3dtutorial.hs > 3dtutorial.pov
povray -W640 -H480 3dtutorial.pov
After these commands you should have a file 3dtutorial.png:
Write more about cameras and lights.
Besides spheres, Diagrams provides a primitive cube
(rectangular
prisms), cone
, and cylinder
. Cones and cylinders are special cases
of the more general frustum
, which can have arbitrary radii at the two
ends. (A cone has radius 0 at one end, and a cylinder has the same
radius at each end.)
ThreeD.Transform
defines many Transformation
s specific to
three dimensions. For example, we could have defined the camera above
as cam = mm50Camera # translateZ 10
. The example below uses
boxes, nonuniform scales and rotations to make a slightly more interesting
scene.
> import Data.Colour.Palette.ColorSet
> import Diagrams.Backend.POVRay
> import Diagrams.Prelude
>
> cam = mm50Camera # translateZ 20
>
> xy :: Direction V3 Double
> xy = direction . r3 $ (-1, -1, -0.5)
>
> light = parallelLight xy white
>
> s = skin cube # scaleY 1.6 # translateX 3 # transform (aboutX (0.25 @@ turn)) # diffuse 1.5 # ambient 0.1
>
> color :: Double -> Diagram POVRay -> Diagram POVRay
> color theta = sc $ rybColor (floor $ theta * 24)
>
> example :: Diagram POVRay
> example = mconcat
> [transform (aboutZ (t @@ turn)) (s # color t) | t <- [0,1/8..7/8]]
>
> main :: IO ()
> main = putStrLn $ renderDia POVRay POVRayOptions $ mconcat [example, cam, light]
These primitive objects can also be combined using the CSG operations
union
, intersection
and difference
. These work on the various
primitives, before skin
wraps them in a Diagram
. The image below
subtracts a sphere from a cone (with its apex towards the camera) to
create a crescent.
> import Diagrams.Backend.POVRay
> import Diagrams.Prelude
>
> cam = mm50Camera # translateZ 10
>
> xy :: Direction V3 Double
> xy = direction . r3 $ (-1, -1, -0.5)
>
> light = parallelLight xy white
>
> color :: Colour Double -> Diagram POVRay -> Diagram POVRay
> color c = diffuse 0.5 . ambient 0.1 . sc c
>
> example :: Diagram POVRay
> example = centerX . color blue . skin $ difference sphere (sphere # translate (V3 0.5 0 0.5))
>
> main :: IO ()
> main = putStrLn $ renderDia POVRay POVRayOptions $ mconcat [example, cam, light]