Clastic - Semantic Scholar

Clastic
Texture Generator Package in Clean
Version 01.10
User’s Tutorial
Jerzy Karczmarczuk
University of Caen, France
[email protected]
October 2001
Contents
1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5
5
6
8
8
9
2 Interfacing
2.1 Your first Clastic textures . . . . . . . . . . .
2.2 Primitive interface elements . . . . . . . . .
2.3 More elaborate interface . . . . . . . . . . .
2.3.1 What can we see on other examples?
2.3.2 What does this interface? . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
10
10
11
12
12
14
3 General Utilities
3.1 List utilities . . .
3.2 Numerics . . . .
3.3 Function algebra
3.4 Vectors . . . . .
3.5 Viewports . . . .
3.6 Colours . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
16
16
16
17
18
19
19
Basic textures
4.1 Primitives and standard combinators . . . . . .
4.1.1 DSP-style primitives . . . . . . . . . .
4.1.2 Basic transformations and combinations
4.2 More elaborate combinations . . . . . . . . . .
4.2.1 Weaving . . . . . . . . . . . . . . . .
4.3 A few words on optimization . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
20
20
20
21
23
24
25
Random Textures
5.1 Basic noise generators . . . . . . . . .
5.2 Noise transformations and combinations
5.3 Fractal noise and turbulence . . . . . .
5.4 Random placement of objects . . . . . .
4
5
6
Introduction
1.1 What is it? . . . . . . . . . .
1.2 The structure of the package
1.3 Additional Information . . .
1.3.1 Shortcomings . . . .
1.3.2 The name . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
27
27
29
29
30
Deformations and Mappings
6.1 Some simple truths from the Group Theory
6.2 Deformation is a local transformation . . .
6.3 How to transform deformers . . . . . . . .
6.4 General warping . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
33
33
33
35
36
.
.
.
.
2
CONTENTS
3
7
Textures in 3 Dimensions
7.1 What are they for and how to show them . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.2 Visualization of 3D textures in Clastic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.3 Light and bumps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
38
38
40
41
8
Tesselations
8.1 Introduction . . . . . . . . . . . . .
8.1.1 Small optimization . . . . .
8.2 Plane symmetries . . . . . . . . . .
8.2.1 Important entities . . . . . .
8.2.2 Wallpaper groups . . . . . .
8.3 Tile generators in Clastic. Unit cells
8.4 Symmetry P3 . . . . . . . . . . . .
8.4.1 A simple case . . . . . . . .
8.4.2 A tribute to Escher . . . . .
8.5 Symmetry P3ml: a kaleidoscope . .
8.6 Parquets PGG . . . . . . . . . . . .
8.7 Conclusions . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
44
44
45
45
46
47
49
51
51
52
53
54
55
Differential Structures
9.1 Who needs derivatives . . . . . .
9.2 What is Automatic Differentiation
9.3 Generalizations and simplifications
9.3.1 A simple example . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
56
56
56
58
59
9
.
.
.
.
A System requirements
B Further work and wishes
B.1 Optimization . . . . . . . . .
B.2 Redaction . . . . . . . . . . .
B.3 Interfacing . . . . . . . . . . .
B.4 Base layer . . . . . . . . . . .
B.5 Bitmap handling . . . . . . . .
B.6 Random stuff . . . . . . . . .
B.7 Mappings and deformations .
B.8 Non-local texturing procedures
B.9 Tesselation package . . . . . .
B.10 Differential stuff . . . . . . . .
B.11 General utilities . . . . . . . .
61
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
62
62
62
62
63
63
63
64
64
64
65
65
C List of Useful Data Structures and Procedures
C.1 Introduction . . . . . . . . . . . . . . . . .
C.2 General utilities . . . . . . . . . . . . . . .
C.3 Function algebra . . . . . . . . . . . . . .
C.4 Colours . . . . . . . . . . . . . . . . . . .
C.5 Vectors . . . . . . . . . . . . . . . . . . .
C.6 Basic generators and transformers . . . . .
C.7 Mappings and deformations . . . . . . . .
C.8 Random stuff . . . . . . . . . . . . . . . .
C.9 Tesselation utilities . . . . . . . . . . . . .
C.10 More math . . . . . . . . . . . . . . . . . .
C.11 More examples . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
66
66
66
67
68
68
69
70
70
71
71
71
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
CONTENTS
C.12 Viewing routines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
71
Chapter 1
Introduction
1.1 What is it?
Clastic is a collection of functional tools written in Clean [7]. It may be used to synthesize procedural 2dimensional images [1], and in a lesser extent, to process some imported bitmap pictures as well. By procedural
images we mean images resulting of a visualization of mathematical algorithms: showing triples of real values
as colours, depicting the domains of mathematical relations, e.g. showing the interior or the boundary of
a curve, etc. By processing we mean geometric deformations, algebraic manipulations in the colour space,
perturbing with noise, adding some 3D effects, etc. In its spirit, Clastic is similar to the system PAN of Conal
Elliott [10], and we acknowledge with pleasure that some parts of it inspired us, the reader will find in Clastic
similar functional clichés as in PAN, although the differences between Haskell and Clean type systems are
sometimes important. But our system was born and evolved independently, as a part of a complex and general
pedagogical project: the usage of functional tools for image synthesis. The idea of procedural textures is
covered very well by the literature, see already cited book [1], but also [2, 3, 13, 14] and many others.
So, let’s be sincere. We wrote this package mainly for ourselves, without special “public” ambitions,
with plenty of personal idiosyncrasies, and far (yet) from any stability. We will be more than grateful for
any criticism. In particular: we believe that the package might be useful for some people interested in image
synthesis, not necessarily experts in the Clean language. But in its current version, if you don’t know Clean
you will have problems with Clastic. This is a package rather for the developers and experimentors, than for
the final user. This description should be readable by non-specialists in image-synthesis, but who have some
basic notions of this domain, and who have some reasonable mathematical, mainly geometrical culture.
Clastic was meant as a library of shader modules for a ray tracer, written also in Clean (this was a student
project at the University of Caen, Normandy, France). The tracer works, and will be described elsewhere, Clastic is to be used separately. It defines the appropriate data structures, and more than three hundred procedures,
but in its current form it is not a collection of software components for texturing, ready to plug into a fully
fledged, ambitious rendering engine. It is just a collection of generators and transformers.
Clastic is not a drawing program, but a texturing library. This means that its basic activity as of a texture
generator is the following: an external interfacing function (rendering engine, testing loop, etc.) provides a
Point in intrinsic texture space; this may require first some mapping from some other coordinate systems.
A concrete generator, a function of type Point → Colour yields the colour for this point. Points are usually
real, Cartesian, and the scaling and truncation when rendering the image in the pixel space is ensured by
the interface, and by the parametrization of the Viewport — a virtual window which handles the conversion
between the texture space and the pixels.
Clastic was written and tested using Clean for Windows (NT, 2000), and the testing interface uses its
Object IO subsystem. We modified one minor thing in the standard package, in the original, the Bitmap
data structure is abstract (opaque). We export it from osbitmap.dcl. The details are given in the Appendix
(A). The modified file osbitmap.dcl has been included in the folder Extras. Put it in your Clean I/O
5
1.2 The structure of the package
6
Windows directory.
However, Clastic in principle may be used under other operating systems, the package is sufficiently modular. You may program and generate a texture, and export a bitmap using standard file output. Currently only
BMP format is supported, but this will be generalized as soon as possible. The actual generators, deformers,
combinators etc. are completely platform-independent functional objects. The visual interface is also modular,
first a device-independent bitmap (Windows DiB) is constructed in a scanning loop over all the rendered pixels.
The result is a byte array, the image of a .bmp file, which may be output by a standard IO routine. Only when
the user wants to visualize directly on the screen the rendered image, the Windows specific IO is used. This is
described further on.
Our main paradigms and objectives were:
1. To exploit the polymorphism and the type class-based overloading in order to define in a generic and
very compact manner procedures which operate upon textures implemented as functional entities of a
specific mathematical flavour. You don’t see what do we mean? You will learn from the examples and
their style. . .
2. To play with higher order constructs in order to facilitate such operations as generic blending, nesting,
combined deformations, tiling, etc. Some other projects are in gestation: generic weaving, and some
textures based on differential geometry.
3. To keep as far as possible a certain independence of concrete Point domain (not only Reals, but also
elements of algebraically treatable differential manifolds, see sect. (9)). To give the utmost importance
to the geometric invariance properties to objects which manipulate vectors (e.g., transformers). This is
important from the pedagogical point of view.
4. To have some more creative liberty than provided by better known interactive texture generators, such
as Corel Texture, or SynTex [9] (a very nice, modular package of Synthetic Realms, Calgary, Canada,
who offer an instructive evaluation version). Those packages usually offer a predefined set of elementary
textures and limited ways to combine them, but on the other hand they are distributed with many inspiring
examples.
5. To make everything reasonably clear and easy to use. But the package is not designed to be the ultimate
tool for generating textures. This is a pedagogic, experimental work. If you need just some textures,
you might waste quite a time to harness Clastic, and it is better to use one of very abundant texturing
applications on the market, or to use the RenderMan shaders [4, 5, 6]. If you want to learn how to
compose and how to transform textures in a (as far as possible) geometrically invariant, generic way —
Clastic may be for you, although the current version is very imperfect.
6. To rewrite in a compact and universal way several “standard” RenderMan shaders in order to learn what
classes of primitives and of manipulators are actually used in practice, and should be optimized. This
work is starting only now.
7. Finally, we wanted to learn how to construct reasonable, non-trivial GUIs in Clean on a concrete example.
8. And, really, we wanted to write this tutorial describing how to make procedural textures, presenting classical techniques, some less known tricks, covering the geometrical issues such as symmetries, generation
of random noise, etc. This task is far from the completion, and we have to underline that This text is not
a reference manual. Rather, it is a tutorial based on our manuscript lecture notes.
1.2 The structure of the package
This is the first, draft version of the package, a study rather than a finished product. Its identification “01.10”
reflects its creation period: year.month. So, its structure reflects its historical evolution, several functions belong
1.2 The structure of the package
7
to inappropriate modules, the name conventions are — to say it mildly — very inhomogeneous, some code is
repeated, and the interface is still awful. The package is autonomous in the sense that it uses only StdEnv,
StdLib and Object IO system packages, it contains thus a voluminous collection of small utilities. In fact,
is uses also the Notice object from the Gui Utilities which are part of the I/O examples, not of the main
library (Notices are also defined in Peter Achten’s I/O Tutorial; we will probably incorporate a version we need
directly into Clastic). We put it in the folder Extras.
Everything is exported. The package is composed of following modules, all of them having names which
begin with CT. . . :
A. CTexture. This is a dummy module which imports all other. Only this one has to be declared in the import clause, unless you want to check the examples which are gathered in an independent set of modules.
B. CTGeneral. Plenty of numeric constants (such as DPi or Sqrt2 and other simple numeric routines,
basic universal classes (including general vector-like functionalities), and some list utilities.
C. CTUtils. Other mixed routines, for example list sorting. Also, some utilities to plot 1-dim functions in
order to test them. It might (should) evolve one day into a reasonable scientific plotting/visualization
package, which we all badly need. We won’t discuss it further.
D. CTFunalg. The overloading of arithmetic to functional objects, some very primitive functionals such as
generic, domain-independent transformations) translation, scaling), the reduction of argument to a given
interval (for replicated patterns), some masking etc. Some of these functions should belong perhaps to
CTBase.
E. CTVector. Standard 2D and 3D vector routines. Instantiation of overloaded arithmetic, scalar and vector
products, rotation, a primitive decomposition of a vector in a non-cartesian system, conversion to polar
coordinates, and that (basic only) stuff that goes together with any 2D geometric-oriented computations.
F. CTBase. This is the foundation of the package, the main “primitive” textures. Transformations of
textures (translations, rotations, etc.), polygons, interpolators, bands/stripes, disks, checkerboards, etc.
G. CTColour. Definition of Real colours — records with real RGB in [0 – 1]. The package uses only
that; the conversion to standard 3-byte RGB entities is done while rendering. (This might change a bit
when we complete the sub-package with caching of intermediate replicated patterns; they will probably
be stored as bitmaps). We define here some conversion functions such as hsvtorgb, and procedures
which handle colour maps and gradients.
H. CTRandom. This module defines a collection of random texture generators, as the name suggests. . .
We define a purely functional noise function (Perlin type) in a rather generic way, permitting its usage in
1-, 2-, and 3-dimensional case. Scalar and vector noise is constructed. Fractal sums and turbulence are
defined as well. This module needs more work. . .
I. CTMapping. Some typical deformations and 3D mappings (mainly for testing volumic textures). Some
general ways showing how to transform deformers (or deform transformers if you are anarchist) are
implemented here.
J. CTView. This is our essential abstract interface, the definition of the rendering viewport with conversion
from real coordinates to integer (pixel) measures. But also some 3D objects are defined here, the abstract
camera, and a “ViewScreen”: a standard focal camera, and its associated viewport. Additionally in this
module you will find all procedures which handle bitmaps independently of the specific Windows IO
procedures (so, bitmaps are {#Char} arrays which may be read and written to files, no relation to
display contexts nor handles, although the format used is particular: it is the Windows DiB).
K. CTWindow. This is the Windows-dependent part of the interfacing. Opening windows, some Dialogs,
displaying something on Pictures, etc. Plenty of technical stuff.
1.3 Additional Information
8
L. CTMath. A set of mathematical utilities less general than mentioned above. Currently the package
contains the arithmetic overloading for rational numbers, complex numbers and the Automatic Differentiation stuff which will be discussed in details later.
M. CTTile. This is not the generation of “seamless” textures out of finite chunks, useful as Web page
backgrounds (abused by nasty people who don’t care about their “clients” eyes. . . ), but some universal
methods of replicating patterns, not necessarily rectangular, with non-trivial “wallpaper” symmetries.
The package is far from being finished, but it has some interesting genericity worth seeing. Requires
some, perhaps not so rudimentary knowledge of geometry.
Of course, if you wish generate seamless background patterns, Clastic has plenty of tools, only you will
have to program everything.
This paper describes the package from the user perspective, beginning with a simple user’s guide permitting to
produce a few simple textures, and then passing to the gory details.
All package modules are gathered in a directory Clastic which should be declared in the set of paths
searched during the compilation. It contains a folder with examples, and this documentation.
1.3 Additional Information
Clastic is distributed as thankware. You shouldn’t try to convince yourself that you didn’t read this, that you
don’t understand what does it mean, or that you have forgotten it immediately.
More concretely: the only usage restrictions you should care about, come from the legal status of the Clean
language and its official libraries. Of course, there is also a non-negligible danger of violating some patents
generously accorded by the US Patent Office I was not aware of, such as the basic vector algebra, construction
of random number generators, illicit usage of the word “warping”, etc.
This was supposed to be a very bad joke, but after having seen a recent patent granted for an antenna which
emits signals with the speed greater than the speed of light, I am not sure of anything. The US movie and music
stars will soon patent their genetic code, although they are not authors of their DNA (with possible exception
of Michael Jackson).
And now seriously: you are allowed, and even encouraged to modify these files, and to share them, but not
under identical names: the name Clastic, and the names of its modules. Please, mark explicitly changed
modules, and choose different names, and then do whatever you wish. Incompatible versions of any multimodule program is a disease which can and should be avoided. . . If you think that your modifications may
be useful for other people, send them to me, I hope that Clastic lives longer than 1 year, and that its possible
updates have some sense. Anyway I use it for teaching image synthesis, for generating texture examples.
Just to be safe, I declare hereby that I am not responsible for any damage of your computer, your washing
mashine, your pancreas or your marriage etc. if you decide to use Clastic for any purpose.
1.3.1
Shortcomings
There are always some things badly done, and some planned (dreamt) improvements are listed in the Appendix
(B). But some other shortcomings are difficult to eliminate.
• The package is slow. It is simply not true that Clean speed is comparable with a highly tuned “C” code
in such a context. The arrays (accessing, but mainly generation) are slower. Run-time checks elsewhere
seem to slow down the speed as well. And, of course, the dynamic memory management has its price.
The usage of high-order functions generates some call indirections which cannot be neglected. Classbased overloading is heavily used, and this is also expensive.
• According to the I/O system authors, bitmaps which are output to screen are not garbage-collected,
which makes it difficult to make in one run many tests; the memory may get eventually clogged, and the
program will fail. Perhaps the bitmaps (within the Windows layer) may be somehow reused, but we have
1.3 Additional Information
9
no idea how. For the moment we didn’t have any drastic problems with this. We simply cannot generate
too many textures simultaneously.
Once more, if you “just” need textures, use other rendering software. Now even POVRay [15] is evolving fast
in the direction permitting to plug-in some dynamic shaders.
1.3.2
The name
(This is just to prevent some people from searching dictionaries. . . ) According to The Concise Oxford dictionary, clastic is a geological term, meaning: “composed of broken pieces of older rocks; conglomerate”. We
hope the reader sees the analogy. If not, there is always an ex post, sham development of this word as Clean
Appliance for Synthesis of Textures (Ingenuous, and Clumsy).
Chapter 2
Interfacing
2.1 Your first Clastic textures
First of all, you would use our simple interfaces, but they are not provided as texture design facilities (this
shall come perhaps later, if somebody is interested), only as testing/display tools. The design begins with the
programming, the design is programming.
Go to the Examples folder, and launch the module example0.icl. Don’t forget that Clastic should
be on the compiler search path, and to replace the modified osbitmap.dcl file. It shouldn’t harm any other
program!
In order to program a texture you should define the mainfun function, which is of type Point → Colour.
The simplest possible texture may be given as
mainfun = const RGreen
// Just a green rectangle...
which is rather dull. . . The file example0 contains two related examples, which generate the results shown
on Fig. (2.1).
Fig. 2.1: Your first textures: xfun1 and xfun2
The functions which generate these images are defined as not too simple, but elementary periodic functions
producing real values:
xfun1 (V2 x y) = sin(x+sin(y+sin x)) - sin(y+sin(x+sin y))
xfun2 (V2 x y) = floor(sin(x+sin(y+sin x))) - floor(sin(y+sin(x+sin y)))
10
2.2 Primitive interface elements
11
These real values are converted into colours through:
mainfun = interpol RGold RBlue Linear o sscaled 0.1 xfun1
where sscaled s is a function which expands/shrinks the space textured by the function T (~x). The call to
sscaled s T yields T (1/s~x). The operator o is the functional composition.
We note the following:
• In order to change the window size, or the viewport parameters, you have to recompile the example
module. This is not very friendly, although quite easy.
• The interpolation function interpol Col0 Col1 Method realArg attributes Col0 to 0, and
Col1 to 1. If the argument realArg gets outside this limit, you may get strange “negative” results, as
here. It can be useful for you as a pretentious artist, or as a motivation to learn the clamping procedures.
• Periodic functions, especially multi-periodic entities can produce results very difficult to guess.
You may now generate some of your own textures, for example a real (0 or 1) checkerboard, rotated by 0.2 radians: xfun = rotated 0.2 checkers interpolating subsequently
pany colours you wish, or concentric
rings given by xfun = symteeth o norm, where norm computes x2 + y 2 and symteeth generates
an infinite sawtooth pattern with symmetric slopes. (Choose your scaling factor as you wish, say 0.2). This is
just to show you the basic usage of the texturing functions, the main issue in this section is the interfacing.
2.2 Primitive interface elements
In the module example0 the standard “main” function of Clean Start, called here Texture is defined as
texturwin "Title string" tecport mainfun
• The function texturwin declared in the Clastic module CTWindow creates and opens a window
attached to the viewport tecport, which contains the geometric attributes of the texture space, and
the related conversion functions. The window is opened by a standard startIO SDI Void ...
routine.
• This function constructs the “look” function for the window by launching makeDib tecport mainfun, where makeDib is a function declared in the module CTView. It is a loop which uses the viewport
geometry, and constructs a standard, uncompressed BMP image — a {#Char} array with the appropriate header, and all the pixels. This can be done independently of the windowing procedures, and the
result may be dumped onto a file. The module CTView contains the function writeBmp which takes
the DiB, the file name and a file-system environment, and writes the bitmap.
• The Device-Independent Bitmap image is fed into the drawDib function declared in CTWindow, since
it uses the Picture structure. The result is a !*Picture -> *Picture function which is the
WindowLook main work horse. Since the DiB is constructed separately, off-screen, the window regeneration is simple and fast, the system redraws the bitmap. On the other hand, you don’t see the progress.
In a new version we shall include a visual gauge to show something, but updating Pictures pixel by pixel
is extremely inefficient.
• The viewport is made through the call
tecport =: mkvport wsize hsize xmin ymin xmax ymax
which constructs a Viewport object. This is just a data structure, a record containing the size in
the virtual and in the pixel space, some precalculated scale factors and conversion functions, that’s all.
In some unspecified future it may be augmented with some masks, additional buffers facilitating the
manipulation of transparency, perhaps some global colour correction functions, etc. For the moment this
was not needed.
2.3 More elaborate interface
12
2.3 More elaborate interface
We see thus that we need at least the possibility to re-run the program with different viewport parameters
without recompilation and re-linking. Launch (but read first!) the program example1.icl in the folder
Examples. This will open a MDI interface, permitting to open many windows with different sizes, save
bitmaps, and also load bitmaps for visualization.
Please read this text together with the code. We show here some figures, but the definitions are often
omitted.
There are a few different functions predefined: mfun1 . . . , you may add a few more, or modify the existing
definitions. The interface supports longer lists. Those functions should define legal textures, exactly as in the
previous example, but you can close a window, open a new one with different geometry, and do whatever the
interface menus permit you.
Open from the menu “File” a “New viewport” dialog, the Standard one (3D Viewports and associated
textures will be discussed later. In this set of examples this will simply refuse to work). Choose the dimensions
of the window, and the size of the virtual viewport. If you check the Square option, you will get a square
window and viewport, only the right column is read. Choose your function.
The first function is a simple checkerboard. Generate it using default parameters, then check the anti-alias
option in the menu “Control”, and generate the same texture again. The difference in quality is visible, but the
rendering is 5 times slower. (See the function aalias in the module CTView; in a more reasonable package
it should be parameterized in a more flexible way, now it is a simple regular super-sampling with 5 points.) For
typical tests keep this option desactivated.
Make some textures, save them on the disk, and verify them by loading the bitmaps back from the disk.
For the moment this “Control” option recognizes only BMP files, and does not permit any manipulation of the
loaded images, but you can use it as a primitive previewer of BMP files on the disk.
Attention! As you see, the list of textures within the function textureset is not just a list of functions,
it contains mnemonic titles, and the texture generators are tagged by T2, they are objects belonging to a
special type Texture, whose instances may be also 3D textures tagged with T3.
2.3.1
What can we see on other examples?
1. The second texture is a mathematical relation r < 1.2 + cos(9φ), where (r, φ) are the polar coordinates
of a point. No Boolean condition is explicitly used, but the function step (the θ function of Heaviside)
useful to delimit the interior of closed implicit curves. Most geometric patterns will be constructed using
such primitives.
Let’s underline the fact that implicit representation of graphical entities is not only natural for some
specific style of modelling [12], but is natural for the texturing as well.
2. The next one is the contour of this flower. This texture is an example of usage of the Automatic Differentiation algorithm; contour computes the gradient of a function, and shifts the region boundary
(the curve) by a specific amount in the direction of this gradient, which is of course orthogonal to the
curve. A simple subtraction produces the zone near the original curve. The rendering is slow. In general,
we implemented the AD stuff for fun and experiments (and its elegance), but in the texturing context
probably the brutal numeric approach may be sufficient (if well parameterized).
3. Next three examples illustrate some random noise generators.
• The basic 2D noise function grnoise2 (gradient Perlin noise [13, 14], described later) is quite
dull, shown on Fig. (2.3) at the left (this does not belong to the examples in the code). However, a
judicious usage of a colour map produces the example on the right. This may be further modified
using e.g. displacement maps discussed later, here we just took the standard grnoise2 which
produces real values about 0, rescaled it by a large factor (10), took the fractional part of the
result with the asymmetric sawtooth function gfrac, and interpolated the colours. This is a fairly
general, commonly used technique. See the result on Fig. (2.3).
2.3 More elaborate interface
13
Fig. 2.2: Interior and contour of a curve
Fig. 2.3: Basic noise
• The function turbulence2 n κ ω0 constructs the fractal “positive” noise with the formula
n κ
X
i
1
η(2 ω0 )
t(p) =
2
i=1
(2.1)
where η is the basic noise function. You can produce many interesting patterns with this and similar
compound noise functions. here we passed it through a simple non-linear transformation in order
to generate some simple clouds, as shown on Fig. (2.4).
4. The last example shows the usage of external bitmaps (e.g. photos) as textures. An image is read, and
converted into a texture function. In order to do that you must define some fake viewport for it, and
define the default background colour outside the bitmap in the virtual space. This is very imperfect, we
should have used some kind of masking, alpha (transparency) channel, etc. Stay tuned.
Once the conversion done, the texture generated from a bitmap can be used as any other. In the current
version only the bi-linear interpolation of pixels is operational, which means that you should not try to
distort the bitmap too much, otherwise the quality loss will be big. You can do something anyway, e.g.
use the lense distorter (discussed later), as on Fig. (2.5) at the right. (Please don’t show this to my
2.3 More elaborate interface
14
Fig. 2.4: Turbulent textures
daughter, otherwise my life will be in danger. . . ) In future we must implement the bicubic interpolation,
and — more important — some filtering routines.
Fig. 2.5: Images as textures
2.3.2
What does this interface?
The Start function is txdialog textureset, where textureset generates a list of pairs: (mnemonic
name, tagged texture function). A texture function is specified as previously. The generator textureset
takes World as its argument, which permits to open some image files in order to construct the appropriate
textures. The function txdialog, defined in CTWindow starts a MDI interface, and passes to the local stuff
the texture set, and some Identifiers. It constructs also a local process state which contains a private list of open
windows, or rather a list of descriptors which contain the windows Ids, and references to the stored bitmaps,
which permits in such a way to save them on disk.
The texture creation dialog imports the default viewport parameters from Defaultpars defined in CTViev.
It should be coded in a more clever way, e.g. keeping the last user specifications in some other part of the local
state. The rest is a fairly standard GUI stuff, but the code is so untidy and spaghettious that we recommend
vividly to read it and to understand it, in such a way your soul will get some bonus in the Purgatory.
2.3 More elaborate interface
15
We pass now to the description of some accessories and to the general mathematical framework of the package.
Chapter 3
General Utilities
Not all procedures defined in CTGeneral and other auxiliary modules have direct relation to texturing. Some
of them are there because we needed them for testing or for experiments. E. g., we borrowed from Haskell a
generic Show class in order to convert all kind of stuff into Strings, and to output a combined String in a more
efficient way than just by concatenation (although in a lazy language this might not be a big economy). For
types – members of the class Show we define show, more or less equivalent to toString, and showS x s
which converts x to string, and concatenates the result with the string s. This avoids the concatenation when
combining many partial results.
We have also some generic manipulators of composite data structures: generalized mapping and “zipping”
facilitating the application of component-wise functions to vectors, or to construct some folds.
class fmap t :: (a->b) (t a) -> (t b)
class fzip t :: (a a->a) (t a) (t a) -> (t a)
However, the bulk of the module CTGeneral is devoted to some numeric goodies. More specific math can
be found in the module CTMath, which contains among other things the arithmetic of rational and of complex
numbers. This will not be discussed here, but we want to underline: we tried to define most of numeric stuff
in a way independent on actual numeric domain. This means that in several procedures the Real constants
are hidden within fromReal x. The genericity of the package is improved, but it costs some speed.
3.1 List utilities
Lists are of limited usage in the package. We needed such generators as cycle (standard in Haskell) in
order to construct “infinite” colour maps, and such functions as map2 mapping functions over a list of lists (a
rectangular grid), useful for some (not ready yet) discrete operations on textures, and for the generation of 3D
parametric surfaces, which belong to another package, in construction. The module CTUtils contains the
quicksort and binary tree sorting procedures useful here as well, sometimes better than the merge-sort routine
included in the standard StdOrdList module.
The arithmetic: +, −, ∗, / is overloaded, defined for lists element-wise. This is useful for the manipulation
of n-dim vectors which exploit lists.
This collection will grow, but we don’t want to put here irrelevant procedures which too often pollute
libraries of list-processing utilities.
3.2 Numerics
The “numerics” is not so numeric. . . An important place is given to an abstract algebraic layer. We define
the operator (Constructor class) (*>) which multiplies a scalar by a vector (belonging to a space spanned on
scalars of this type). Also the division of a vector by a scalar (>/) is defined as a class.
16
3.3 Function algebra
17
Such polymorphic entities are defined:
class norm2 t :: (t a) -> a | *,+ a
norm :: (a !b) -> !b | * , + , sqrt b & norm2 a
norm x = sqrt (norm2 x)
normalize :: (a !b) -> a b | >/, norm2 a & *,+,/, sqrt b
normalize v = v>/norm v
as well, as generic scalar product and projections:
class (<.>)
infix 9 t
:: (t a) (t a) -> a | *,+ a
axproj :: (a !b) (a !b) -> a b | *,+ b & *>,<.> a
axproj n v = (v<.>n) *> n
perp :: (a !b) (a !b) -> a b | *>,<.> a & * , + , - b
perp n v = v - (v<.>n) *> n
which should work for any, 2D or 3D vectors, based on any scalar domain. This avoids a substantial code
replication and increases the readability.
Common numeric constants as 0, 1, 2, 1/2 and 3 are made polymorphic under such names (what a surprise. . . ) as zero and one — present already in the standard clean libraries, to which we add half, two,
etc. In such a way a “clamping function”, the constrained linear interpolator:
clerp v0 v1 t | t<zero = v0
| t>one = v1
= (one-t)*>v0 + t*>v1
// or v0 + t*>(v1-v0)
becomes polymorphic. There is a small problem with such a style: the basic domain (Reals), although obviously a Vector Space, cannot be used as such here, the operator (*>) won’t compile. The solution might be
given by multiparametric classes with functional dependencies, but we have to wait a little bit for that.
The “abstract” numeric layer contains other goodies: a generic linear iterator which applies n times an
operator to a value, a generic dichotomic iterator for associative operators permitting to construct the power
function xn with logarithic complexity in n, etc. The function atan2 useful for computations in polar coordinate systems is overloaded as well.
There are some integer utilities such as the Extended Euler algorithms, and also some (usually) real functions, e.g. the Hermite interpolator h(x) = 3x2 − 2x3 , defined of course in an overloaded style.
3.3 Function algebra
The arithmetic operations are overloaded in the domain of functions. The package contains a full plethora of
such definitions:
instance + (a -> b) | + b
where
(+) f g = \x -> f x + g x
instance one (a->b) | one b
where
one = \_ -> one
instance fromReal (a->b) | fromReal b
where
fromReal c =
-> fromReal c
3.4 Vectors
18
etc., and a number of functionals which modify their function argument by acting on the argument of the latter,
e.g.,
translated a f x = f (x-a)
scaled c f = \x -> f (x/c)
// Just to vary the style a bit
All this is quite simple, the only interesting feature here is that due to the overloading of arithmetic operations
these functionals: translated or scaled may act not only on scalars, but on any vectors. Admittedly,
overloading the division (coordinate-wise) for vectors is a little bizarre, but harmless.
The user code may be quite compact, and written in a combinatorial style. For example, in order to replicate
f (x) from the interval [0–1] to the entire x space, you may write f o gfrac, where gfrac computes the
fractional part of a numeric object (more general than a Real). Such composition is predefined in the package
as train f; there are also other replicators.
Not everything from the world of numbers can be easily overloaded to functional objects. The relations
(<) etc. according to their official signatures yield Booleans, and they cannot return functions. But computing
the minimum or maximum of two functions as functions is fairly useful, we define thus
minf f g = \x -> min (f x) (g x)
maxf f g = \x -> max (f x) (g x)
The names have been changed, because the standard definitions: min x y :== if (x<y) x y are not
applicable. We will need our functions to define some masking functionals, for example posit f = max
zero o f cuts out the negative values returned by a function.
3.4 Vectors
We shall use principally 2- and 3-dim vectors, structures of types
:: Vec3 a = V3 !a !a !a
:: Vec2 a = V2 !a !a
and a reasonable set of operations acting upon: arithmetic, scalar and cross products, projections, rotation,
decomposition into coordinates in a non-cartesian system, conversion between cartesian and polar coordinates,
etc. We have some useful constants, such as X2axis etc., and linear equation solvers, with matrices represented by tuples of vectors.
The domain of vector elements is usually the real axis, but — we underline it again — the generality is
such that almost all library functions work with vectors containing differential scalars, objects permitting to
compute very easily all the gradients, defining such functions as contour, constructing shaded 3D reliefs,
etc.
Most textures are defined on vectors, and it is recommended to use the invariant notation, avoiding the
separation of arguments into coordinates. The code is much proper and less bug-prone. For example, the
rotation of a vector ~u about a (normalized) axis ~n in 3D does not need matrices, but is based on the following
observation, which yields the Rodrigues formula:
• In 2D the rotation is given by
0 cos(α) − sin(α)
x
x
x
→
=
y
y0
sin(α) cos(α)
y
(3.1)
or: x0 = cos(α)x − sin(α)y; y 0 = sin(α)x + cos(α)y.
• In 3D we begin by separating the vector ~u in a component parallel to ~n: ~u|| = (~u ·~n)~n, and the remaining
part, perpendicular to the axis: ~u⊥ = ~u − ~u|| .
3.5 Viewports
19
• The parallel component remains invariant under the rotation, and the perpendicular one remains in its
plane orthogonal to the axis; the problem became 2-dimensional. But we don’t have (rather: we don’t
want to use) a concrete coordinate system, so we use the vector itself as the “x” component, and find
another one by ~u3 = ~n ∧ ~u.
• The result is: ~u|| + cos(α)~u⊥ + sin(α)~u3 .
3.5 Viewports
As mentioned already, the textures are constructed in a “virtual” space, mapped into pixels at the last possible
moment. This virtual space is based on Reals, without any genericity. We define the structure
:: Viewport
= {width::!Int,height::!Int,
xmin::!Real,xmax::!Real,ymin::!Real,ymax::!Real,
xscale::!Real,yscale::!Real, vpix::(!RVec2->Point2),
...
}
See the details in CTView. The texturing area is defined by xmin–xmax, ymin–ymax. These, and the rendering window width and height are only input attributes. The scales: w * (x-xmin)/(xmax-xmin)
etc., and the remaining parameters — various conversion functions, are precomputed during the creation of the
viewport.
We introduce this structure here, because in concrete texturing applications the user may need something
more: the position of a camera in 3D, some default lights, some specific (e.g. hyperbolic) mappings, some
masks or colour transfer functions, etc. It is recommended to gather all these information in one data structure. On the other hand it should be fairly independent of the rendering process, of system-dependent window
attributes etc. The 3D layer will be discussed later.
3.6 Colours
The standard Clean I/O system operates upon colours (datatype Colour) defined as 3-byte RGB records. We
use real colour components, and we predefine several constants, such as RBlue, Red, RGrey, RDarkGreen, etc. Colours are generic records:
:: RealCol a = tr::a,tg::a,tb::a
which makes it easier to compute gradients in colour space, but for the moment this genericity is not exploited,
we use Reals.
Colours constitue a vector space: they can be added, multiplied by a scalar, interpolated, etc. There are
some functions permitting the conversion between RGB and HSV, and some other utilities, such as saturation
routine, etc.
Chapter 4
Basic textures
4.1 Primitives and standard combinators
The basic principle of shader construction is the same as everywhere else in software construction: build
reusable blocks, combine them together, avoid explicit multi-branch decisions, put related entities together,
etc. In the discussed context typical imperative constructions are far from ideal, and if you read the sources of
some RenderMan shaders, you will see that it is not easy to exploit the work done by others. But you will learn
also some basic combination techniques which we have adopted as well. A word of warning: the methodology
presented below economises human, not computer time. If one day you will need to generate very efficiently
many textures, even in real time for an interactive animation, you will have to optimize everything, but you will
know already what should be optimized.
We have seen already that the separation between the interior and exterior of a closed curve given implicitly
as the solution of f (p) = 0, where positive values are assigned to the interior, p
is provided by the θ (step)
function of Heaviside, e.g. step o (one - norm) generates the unit disk: x2 + y 2 < 1. Of course, a
consciencious user will write norm2 (x2 + y 2 ) rather than norm, the square root here is useless. Plenty of
other functions which belong to the standard panoply of DSP (Digital Signal Processing) packages are defined
in the package.
4.1.1
DSP-style primitives
Since step etc. are often used shifted, the package provides a parameterized version of it, xstep a: θ(x−a).
We have the parameterized smoothstep a b which yields 0 for the argument x smaller than a, 1 for
x > b, and uses the Hermite interpolator (3t2 − 2t3 ) for t = (x − a)/(b − a) between 0 and 1.
The function pulse filters (attributes 1 to it; zero outside) the interval [0–1], and xpulse a b — a more
general domain. From smoothstep we may construct smoothpulse by subtraction, and sometimes it is
useful to use a continuous, piece-wise linear filteredpulse block:
filteredpulse c0 c1 dx x
= max zero ((min (x+dx) c1 - max (x-dx) c0)/(two*dx))
(This last function is used in some RenderMan shaders.) In general, discontinuous functions may be very
cumbersome when computing gradients, and for the visual comfort one may introduce also some smoothing
into piece-wise differentiable functions, defining for example:
filteredabs dx x
= if (dx<x || x< ~dx) x (half*(dxh+x*x/dx))
Periodic functions may be constructed — as already mentioned — by taking the fractional part of the argument.
If we need something more symmetric, we might use symtrain defined and plotted below. Of course nothing
prevents you from using trigonometric functions.
20
4.1 Primitives and standard combinators
21
Here we see some plots of primitive blocks, and periodicity generators:
ramp = max zero o min one
sawtooth = pulse*id
filteredpulse
smoothpulse
trainpulse
symteeth = symtrain abs
There is no need to supply the package with hundreds of such functions, they can be constructed very easily.
They are 1-dimensional functions, and in order to use them as textures we might use predefined lifters such as
xstripe f = \(V2 x _) -> f x
and its partner ystripe, which generate horizontal or vertical bands. In order to get something more complicated, the primitives may be combined, or used in a sufficiently sophisticated way. For example, the following
definitions generates (differently scaled) checkerboards:
yetanotherchecker (V2 x y) = step (sin x * cos y)
morecheckers (V2 x y) = abs (trainpulse x - trainpulse y)
and the only requirement demanded from the user is a bit of geometric imagination, as always. . .
4.1.2
Basic transformations and combinations
We mentioned already the translated transformer which shifts the argument of a function: translated
a f x = f (x-a). In general, one rule must be respected in these transformations: they act contravariantly on the texture argument. If you want to rotate left a texture, apply its generator to the argument rotated
right. The scaling by a scalar c divides the texture argument by it.
This means that for general transformations (especially local deformations) the situation is difficult, we
have to find the inverse transformation applied to the argument. More formally, if ζ(~
p) represents the texture
rendering, with p~ being the current point, and if we wish to apply a transformation (e.g., a rotation) R, the
resulting texture is obtained through
(Rζ) (~
p) = ζ R−1 p~
(4.1)
The generic layer of the package contains thus only the simple transformations: translations, scaling, rotations
(with some special rotations by 45, 60 or 90 degrees defined separately for efficiency), and transpositions. We
have e.g. a generic disk centered anywhere and with any radius: disk cntr r2 = step o (fR r2 translated cntr norm2), where fR is just an abbreviation for often used fromReal.
There exists also the primitive tiling functional reduced which takes fractional parts of x and of y, and
then applies the texture generator. Often more complicated manipulations are needed, but we shall come to
them later.
There aren’t any specific “texture combinators”, all functional compositions can be used. The simplest
2-dimensional “Constructive Solid Geometry”, i.e., Boolean combinations of objects: union, intersection, etc.
depends on the representation of objects. If the step function is used to assign every object its characteristic
function, the product a · b gives the intersection of two objects f and g at the point p~, where a = f (~
p), and
4.1 Primitives and standard combinators
22
b = g(~
p). The union results from a + b − a · b, and the complement is simply defined as complement tx =
one-tx.
We may use also decisional masks. The package contains such functions:
cmask c u v = if (c<>zero) u v
fmask h f g = \x -> cmask (h x) (f x) (g x)
Sometimes it is more natural to operate with objects defined implicitly by f (~
p) ≥ 0. Then the union is given by
max f (~
p) g(~
p) (it suffices that one value is positive to be inside the object), the intersection uses min (only if
both are positive, the outcome is positive), and the complement is the negation. This is a well known technique
used in the implementation of 3D CSG objects. Because of this freedom of representation, the appropriate
combinators are not included in the package; they can be defined by 1-liners adapted to users’ needs.
As an example we shall construct a regular n-star. The technique presented below is conceptually extremely
easy, but the rendering is rather slow, don’t use this model in a serious work. The Fig. (4.1) shows first a wedge
Fig. 4.1: A star is born
constructed as the union of two half-planes. Everything is defined here:
lspace (V2 _ y) = (step o ~) y
// Lower half-plane
intersect = foldl (*) one
union = foldl (\x y -> x+y-x*y) zero
wedge alpha = translated (V2 0.0 0.5)
(union [rotated alpha lspace,rotated (~alpha) lspace])
which gives the result at the left of Fig. (4.1). At the right we see the result of iterating the intersection between
rotated wedges:
star n alpha
# wd = wedge alpha
# beta = Dpi/fromInt n
= intersect (map (\k -> rotated (fromInt k * beta) wd) [0 .. (n-1)])
A bit faster solution would be to apply the texture generators first, and then compose the values at a given
point. A much faster solution, which we leave to the reader (for the moment), consists in finding the angle in
the polar representation of the current point; for each angle there exists one straight line (with two possible
slopes wrt. the central ray passing by the current point) which delimits the star. We shall return to this strategy
in the section devoted to tesselations.
4.2 More elaborate combinations
23
We end this section showing how to generate the oriental pattern shown on Fig. (4.2), which can be considered as an appetizer
for more complicated symmetry considerations. The following
function
c2symmetry trf ax tx p
=cmask (step(ax<.>p)) (tx p) (trf(tx(~p)))
is an central symmetry transformer written in invariant fashion. If
the current point (vector) and the axis point in more or less similar
direction (angle between them is less than 90◦ ), then the texture
is applied, otherwise this texture is transformed, and applied to
the inverted point. In our case the additional transformation is the
complement. Here is the definition of the texture:
Fig. 4.2: Yin-Yang pattern
yinyang tx1 tx2 bg
# yin = posit (unitdisk - disk (0.5*>Y2axis) 0.25)
# yang = c2symmetry complement X2axis yin
= fmask unitdisk
(fmask yang (const tx1) (const tx2))
(const bg)
and, as we see, it is used in: yinyang RBlue RRed RWhite.
Since this is a tutorial, and the main problem in the generation of procedural images is the human imagination and experience, we pass to a few less obvious examples.
4.2 More elaborate combinations
p
A simple radial gradient function, e.g. 0.8 − 0.9 (x + 3.2)2 + (y − 2.8)2 , coded as
g = fR 0.8 - fR 0.09 * translated (V2 (-3.2) 2.8) norm
may be used to simulate a matte sphere, as we see on the left side of Fig. (4.3), where it is masked by a disk of
radius 5.
Fig. 4.3: Halftoning
But the same function may be used differently, it modulate a highly periodic function (dense checkerboardlike pattern) in the following way:
4.2 More elaborate combinations
24
hftone p=:(V2 x y) = sin(omega*x)-cos(omega*y)+2.0 - 4.0*g p
where omega is sufficiently big (say, 30). This function yields positive results when the peaks of the periodic
patterns are greater than the appropriately scaled “density” g, otherwise it is negative, and can be masked out.
The function:
gmod = fmask disk5
(cmix RBlack RWhite (one - step o hftone))
(const RWhite)
generates the halfone pattern at the right of Fig. (4.3). The possible variations are very numerous, you may
use different grid functions (e.g. hexagonal), use three slightly shifted grids for three colours, and use a bitmap
image as the density source in order to produce a coarse, halftoned “newspaper style” texture (and make some
ugly T-shirts out of it), etc.
Oh, you don’t know how to make hexagonal pattern, such as
this on the Fig. (4.4)? This is not too difficult, you need only to
produce a checkerboard-style, periodic pattern in a non-cartesian
coordinate system. The pattern shown has been obtained by the
function
tsfun (V2 x y) =
symteeth (2.0*x) +
symteeth (x+Sqrt3*y) +
symteeth (x-Sqrt3*y) - 1.5
where the additive constant, here 1.5 specifies (through thresholding) the size of the dots, it should be replaced by the density
to be modulated, and the function step should be applied, as
previously.
We acknowledge having borrowed the tricks above from a
Fig. 4.4: Hexagonal masking pattern
nice piece of software: GrafEq of Pedagoguery Software [11],
a package very helpful for the visualization of mathematical relations.
More complicated examples of non-trivial symmetries and pattern replication we shall see in the section
(8), we show now how to make simple “weaved” motifs.
4.2.1
Weaving
As elsewhere, we want to develop some universal, generic tools rather than just produce some concrete patterns.
The structure we discuss is considered to be discrete rather than continuous, but its rendering is a standard real
texture.
The model is a simple “loom” with a “canvas” containing vertical threads, stripes of any colour (or more
complex texture structure), and a set of horizontal threads which are going to be interlaced with the threads of
the canvas. We might complicate it, construct some fabrics with hexagonal symmetry, use specific topology for
ring armours or baskets, etc., but there is no place for such details in this tutorial.
Our virtual loom has to consider the following details:
1. The colour assignments for the threads. We shall use two lists, horizontal and vertical, rendered “infinite” in both directions by appropriate periodic indexing functions (rather than using laziness in order to
construct cycles).
2. The topology of the fabric, or the interlacing order. This will be represented by a function Γnm which
depends on vertical and horizontal indices, and takes the decision which thread: horizontal or vertical
should be visible (masking the other one). In our case a two-valued function suffices, our Γ (called loom
in the weaving procedure) returns a Boolean.
4.3 A few words on optimization
25
Fig. 4.5: Weaving
3. The geometry in real texture space: the width of threads and of the empty space between them. Fig. (4.5)
at the left shows a simple checkerboard interlacing with two vertical and three horizontal threads. Each
thread together with his empty part has width equal to 1.
4. The decorations; the background texture, the texturing of the threads, and special effects near the crossing
edges, such as on Fig. (4.5) at the right.
This last effect needs the introduction of some specific masks which
change the colour of the neighbourhood of a thread, but without touching
the background. We use the mask shown on Fig. 4.6. If the width of the
parabolic section is zero, the mask is switched-off.
Clastic defines a generic interlacing generator:
modinterlace (row_mod,row_edge,col_mod) n m
= row (m + n pmod col_mod)
where
row m = m pmod row_mod < row_edge
Fig. 4.6: Darkening mask
which called with parameters (2,1,2) generates the simplest even-odd interlacing pattern, and with (4,2,4) —
a Scottish Tartan pattern. As we have already mentioned, thread sets are represented by lists of textures,
e.g., [CYellow,CRed,CBlue] where CYellow = const RYellow is a texture function returning a
constant colour.
The rest is the user imagination. On Fig. 4.7 we show two weaving patterns in details, and a Tartan
based on some simplified Stewart of Galloway pattern. These examples are included in the file examples/example2.icl, and the weaving procedures are gathered inside the module CTExamples.
4.3 A few words on optimization
• A shader may execute sometimes very complicated operations, and the fundamental issue is to avoid to
compute for each pixel quantities which do not depend on the current point. The RenderMan shaders
contain specific type qualifiers: uniform which means that the declared quantity remains the same
for all pixels (or rather: texture space coordinates), and a reasonable shader compiler should permit its
pre-computation.
We don’t need a special compiler tricks, since a decent functional language permits easily the construction of closures. So, we may define
4.3 A few words on optimization
26
Fig. 4.7: A loom at work
textgen cpar1 cpar2 ... = fx
where
... constant computations
...
fx point = ...
or something similar. With some experience one applies such style almost automatically.
• Another place where serious economies remain hidden is to avoid computations which are bound to fail.
This is an issue very well known in 3D modelling. If an object: a 2D texture item, or a 3D surface is
small wrt. to the modelling/texturing space, but fairly complicated, i.e. its implicit procedural description
is costly, it is useful to enclose this object in a bounding box or bounding sphere (disk).
If the current point is found to be outside the bounding volume, the actual generator is not launched at
all. We will use that e.g. to optimize a regular star pattern.
• Finally, for dense replications of complicated patterns it might be interesting to introduce some cache
or memoizing mechanism. We are working on it. We will have also to convert several functions into
macros.
A lazy functional language is full of traps for a beginner. A potential user should know how to enforce strictness,
how to avoid redundant pattern matching, etc. No place to discuss all that here.
Chapter 5
Random Textures
5.1 Basic noise generators
Most practically used textures are irregular. Wood, stone, water surface, human or animal skin, etc., all this
requires some degree of randomness, often superposed on regular patterns: brick walls, “realistic” tissues,
dusty windows, etc. We shall need several different, often quite complex random patterns, and differently used:
introducing colour variations, deforming the contours (displacement maps), placing randomly some objects,
etc. The basic layer is rather simple; most random noise functions used in typical shaders are based on the s.c.
Perlin’s noise, a pseudo-random function with narrow spectrum (bumps and valleys of similar size). How is it
obtained?
The basic algorithm which made the fame of Ken Perlin consists in generating a sufficiently long list of
random numbers, and storing them in an array. The array corresponds to the texture space: 2-dimensional for
2-dim. textures, 3-dimensional for volumic noise, etc. The set of indices of a given element corresponds to a
point with integer coordinates. In order to obtain a value for any point with real coordinates, an interpolation
of nearest neighbours’ values is used. If just real values are interpolated using some smoothing function, e.g.
the Hermite polynomial, the noise pattern reflects too much the shape of the lattice; there are visible “square”
artefacts. Perlin attached to each integer vertex a random vector, and interpolated scalar products of it with the
vector describing the point in its elementary lattice cell (square, cube. . . ). The result is presented on the Fig.
(5.1), at the left. The width of the textured zone is 12 units.
Fig. 5.1: Perlin’s noise and its “relief” visualisation
At the right we see the same noise, but viewed as a bump-map shading. The noise is treated as a height27
5.1 Basic noise generators
28
field. Using our AD package we computed the normal to the surface, and added some light. The details are
described in the section (9). We see that perhaps the basic noise is not ideal, the “edges” between peaks are
long and omnipresent, while we should expect many isolated peaks as well. But nobody (and only nobody) is
perfect. . . If you look well you will see also some small artefacts at the edges of cells, coming from the fact
that in these regions the interpolated data switch.
We decided not to use precomputed random numbers stored in arrays, but to use a pure function which
behaves very irregularly, without visible correlation between values returned for neighbouring arguments (integer, of course). The behaviour of that kind, where the result of a process does not depend “reasonably” on
its input, but is very unstable, and exhibits everywhere irregular fluctuations, is known in physics as ergodicity.
The trajectories of non-linear dynamic systems are usually ergodic, which makes it very difficult or impossible
to predict its future behaviour, but permits to use statistical reasoning. In fact, the thermodynamics is a nice,
deterministic theory because the underlying microscopic dynamics is crazily ergodic.
We tested a few functions, finally we discovered that the function chosen by Hugo Elias in his noise tutorial
[14]
ergodic n
# n = (n<<13) bitxor n
= toReal((n*(n*n*15731+789221)+1376312589) bitand 2147483647)
/ 2147483648.0
is very reasonably crazy. The results are in the interval [0–1], but Clastic contains a few other similar functions,
differently parametrized, and returning values between -1 and 1, etc. For those readers who doubt that this
function is really ugly, we have the Fig. (5.2), which displays the values for n from 0 to 200.
Fig. 5.2:
Now we have to interpolate such a noise for real coordinates, as shown on Fig. (5.3) for the 2-dimensional
case.
Using the basic ergodic noise we attach a random vector ~ri to
each integer vertex Pi of a unit lattice cell. Four scalar products
(Q − Pi ) · ~ri are computed, and the result is its weighted sum.
The weight functions are not Hermite cubics (3x2 − 2x3 ) as in
the original Perlin algorithm, but more smooth quintics q(x) =
10x3 − 15x4 + 6x5 , having the properties: q(0) = 0, q(1) = 1,
q 0 (0) = q 0 (1) = q 00 (0) = q 00 (1) = 0. The vanishing of second
derivatives is necessary in order to have smooth gradients of the
noise function at the edges of unit cells.
In principle this is all. In practice we have a few more noise
functions, e.g., 2- and 3D vector noise which permits to generate
random displacements of texture elements. The implementation
Fig. 5.3: Constructing Perlin Noise
is similar to the scalar case.
5.2 Noise transformations and combinations
29
5.2 Noise transformations and combinations
You can do whatever you wish (including differentiation) with the noise functions. Even a dull, featureless,
basic noise within a small region (dimensions of order 1) can give interesting patterns, if a sufficiently sensitive
colour map is used. This is our function, which uses the basic noise function noise2 (a scaled and shifted
version of grnoise2 which produces positive values only)
wnoise p
# r = 12.0 * noise2 p
= interpol RDarkBrown RLightBrown Hermite (gfrac r)
Fig. 5.4: Some random textures
On Fig. 5.4 we see the result of this transformation, followed by an even simpler usage of the random generator,
which needs only ergodic noise on integer coordinates. The “Truchet” pattern is a random mosaic obtained by
choosing one random number within each unit cell, and using it to choose a simple geometric pattern, or its
transpose. They are defined in CTExamples, and used in examples/examples2.icl.
Much more interesting forms are generated if we sum the contributions of noise functions with varying
frequencies, which generates a “fractal noise”, or “turbulence” discussed in the next section, but even without
this summing, we can choose a big scaling factor in order to have unit cells comparable to pixels, and construct
in such a way a 2-dimensional white noise, which can be used e.g. to dither any density function, for example
a texture produced by an image, as we see at the right of Fig. 5.4. Full details of this “complicated” procedure
the reader will find as the 8-th item in this set of examples. The dithering procedure is just:
dither factor tx p
# r = grnoise2 (factor*>p) + 0.5
| tx p > r = RWhite
= RBlack
and it is easy to see what is needed to augment the number of spread colours, or to change the dithering
resolution.
5.3 Fractal noise and turbulence
This is an extremely important category of noise functions, used to simulate all kinds of “natural” phenomena,
which are known to be multi-scale, possessing wide spectra of spatial frequencies.
The “fractal sum” noise is defined by
n iκ
X
1
F (~
p) =
η ai ω0 p~
(5.1)
2
i=0
5.4 Random placement of objects
30
and the “turbulence” replaces η(. . .) by |η(. . .)|, where η is the standard noise function, and a is the “lacunarity”
factor, usually about 2 (slightly different, in order to avoid some periodic artefacts enhanced by the fact that the
basic generator η is seldom ideal. The number of octaves n varies usually between 4 and 10, and ω0 is chosen
according to the texture scaling wished by the user.
Practically, our generator in the module CTRandom includes in the summing loop an offset of p~ which
varies between octaves, in order to avoid spurious correlations. Some researchers propose other “randomization” techniques such as rotations, etc., but we are not trying to build the best noise generators in the world. We
must admit that in the current version the generators are awfully slow, it takes several seconds to generate even
a 250x250 turbulent pattern, and when we want to compute its gradients using the Automatic Differentiation
routines, the time is multiplied by 4.
Fig. 5.5: Fractal noise and turbulence
On Fig. 5.5 we see an example of a fractal noise, called sometimes “plasma clouds”, although the nickname
“plasma” (which has nothing to do with the meaning given to this word by physicists) is usually linked with
the recursive subdivision algorithm. For texturing that algorithm is less appropriate than the spectral technique
used here, because of its non-locality.
At the right we see a turbulent pattern, with the same number of octaves (6), and the fractality exponent κ
equal to 0.8. Some filamentary structures are clearly visible. More filaments can be obtained by enhancing the
contrast, or, more generally, by the usage of an appropriate colour map. Turbulence can be used to generate
lightnings or other electric discharges, and to hundreds of other textures, as advocated by Perlin.
Some interesting effects can be obtained by applying non-linear transformation to turbulent noise, we have seen
already how to make hmmm. . . almost realistic clouds. We will see later some more random, wide spectrum
noise examples used as deformation generators. Fig. 5.6 shows the same noise functions as previously, but
passed through the gradient machinery in order to visualize them as 3D profiles.
5.4 Random placement of objects
For a “local” generation protocol, the random placement of an object bigger than a point is not natural. But
random scattering of some graphic objects on the texturing zone is a useful technique, so at least one simple
method should be discussed here.
Clastic implements a method called “bombing”, which is a variant of jittering: the object is placed in its
“regular” position, modified by a random displacement vector.
Concretely, for the current point p~ we retrieve integer indices of its unit cell, which are fed into the random
vector generator resulting in a pair of displacements (dx, dy). Then, the texture generator with the object
belonging to a unit cell (or, as we often do, to a [-1 – 1] square) is applied to a reduced and shifted point.
5.4 Random placement of objects
31
Fig. 5.6: Fractal noise and turbulence shown as reliefs
Fig. 5.7: Random placement of extended objects
There are severe problems with this simple-minded method, as we see on Fig. 5.7 at the left. If the object
crosses its cell boundary it might get truncated, since the region beyond the boundary belongs to another texture
instance.
Because of the locality of texturing machinery, there are no ideal solutions of this difficulty, the simplest one consists in verifying for each cell
also its 8 neighbours, and Clastic implements this technique. But some
easy optimisation is also possible. The central cell is divided into quadrants, and each quadrant verifies only its 3 neighbours, as shown on Fig.
5.8.
The 3 possible contributions are combined, so in order to avoid more
difficulties
• the graphic objects should be small,
• the displacement should also be rather small, and
Fig. 5.8: Neighbours for jittering
• they are binary inside/outside entities, and simple union may be used to combine them.
The result of an improved method is shown on Fig. 5.7 at the right. More complex combinations are also
5.4 Random placement of objects
32
possible, although the corresponding combinators would be much more involved than just binary unions. An
additional random generator assigned to each cell may desactivate the rendering altogether, or change the object
colour, or make it undergo any parameterized transformation.
Most examples discussed in the last section are coded in the file Examples/example2.icl, and use
auxiliary functions in the module CTExamples.
Chapter 6
Deformations and Mappings
6.1 Some simple truths from the Group Theory
We know already that one possible manipulation of a texture is to act on its definition domain, and that this
transformation acts contravariantly, which is a source of difficulty: it is not possible in general to invert a given
transformation, especially if it is local, i.e., if it depends on the current point.
Anyway, it seems appropriate to say a few more words on the equation (4.1), which we repeat here:
(Rζ) (~
p) = ζ R−1 p~ .
(6.1)
where ζ is a texture. We have abused the language a bit. R means here a transformation which acts on points,
and on textures, which is frustrating for mathematically-oriented readers, and even more for somebody who
wants to implement it. The type checker will be really unhappy. So, let’s put a bit of order on that, and change
the notation.
We know that (typical) geometric transformations of space can be composed and inverted, i.e., they form a
group. All people who effectively use the Group Theory (e.g., physicists) have to think not only about such a
group from the abstract point of view, but on its representation, which acts on other objects than just points
(here: textures or deformers, i.e. some functional objects).
Say, we have the transformations R acting on points, and implying the transformations of a set of objects ζ
somehow related to these points. For a given transformation R we define its representation — an operator TR
which acts on ζs. The fundamental property of a representation is that it constitutes a group homomorphic to
−1
the original: R1 R2 generates TR1 R2 = TR1 TR2 , and — if applicable — R−1 → TR−1 = (TR ) .
Now, textures are functions from points to reals, or to colours which are geometric scalars, their codomain
does not feel directly a geometric transformation of points. It is easy to prove that if ζ 0 = TR ζ is a function
which acts as we have suggested: ζ 0 (p) = ζ R−1 p , then it is a correct definition of a representation:
−1 −1
−1
(TR1 R2 ζ) (p) = (TR1 TR2 ζ) (p) = (TR2 ζ) R−1
p .
(6.2)
1 p = ζ R2 R1 p = ζ (R1 R2 )
This is the only non-trivial representation in the domain of scalar functions.
But sometimes we need more complicated representations. In particular, we may need to transform transformers, for example applying the same warp to different regions of space (thus: translate a warp), and deformers are different kind of objects than textures, their codomain contain vectors (points), not scalars, and their
transformation rules will be more difficult. The details are described in section (6.3).
6.2 Deformation is a local transformation
Even if it is difficult to invert practically an arbitrary operation, many deformations are quite simple to imagine, we simply must think directly in terms of the inverse transformation. Clastic uses a very simple functional: transf trfun txtr = \p-> txtr (trfun p), where trfun is a transformer function of
33
6.2 Deformation is a local transformation
34
type Point → Point. (A combinatorial maniac will write: transf trfun txtr = txtr o trfun, or
even transf = flip (o), where flip is a well known combinator which changes the order of arguments
of a binary function: flip f x y = f y x.)
Fig. 6.1: Simple deformations in polar coordinates
Fig. 6.1 shows a simple-minded transformation which consists in translating the polar angle by a value which is
dependent on r. Clastic library has two such transformers, one: whirl (shown on the left picture) very similar
to the notorious torsion deformation included in such packages as Photoshop or Paintshop, and vortex, a
more “physical” one, where the 2-dim “matter” rotates around the central point (and is eventually aspired by
it), obeying the law of area (“matter”) conservation, which fixes the relation between the torsion angle and the
current radius. The vortex deformer is defined as follows:
vortex rmin2 dr2 dph p=:(V2 x y)
# r2=x*x+y*y
| r2<rmin2 = p
# r=sqrt r2
# cf=x/r
# sf=y/r
# df = dph/r
# cs = cos df
# ss = sin df
= sqrt(r2+dr2) *> V2 (cf*cs-sf*ss) (sf*cs+cf*ss)
A very typical, a bit more concrete than a general transformation is the displacement: the applied operator
produces a result which is added to the original argument of the texture. Clastic defines a generic functional:
displace dspl tx p = tx (p+dspl p)
If you wish:
displace dspl tx = tx o (id+dspl)
where id is the identity combinator, is a more compact formulation of it. A transformer trf used in:
transf trf texture is converted into a displacer by adding id to it. Nice to have the arithmetic overloaded to functions. . .
Random displacements, especially turbulent ones are particularly useful to generate irregular wood grain,
distorted marble veins, etc. We will use our fractal sum function in order to generate displacements, so the
basic noise generator should produce vectors (the fractal sum functional is generic, and may be reused). The
examples of this section may be found in the file examples/examples3.icl.
On Fig. 6.2 we see the following examples.
6.3 How to transform deformers
35
Fig. 6.2: Some random displacement effects
1. a brownish, wood-like simple noise pattern, which has been already shown on Fig. 5.4, but which has
additionnally been perturbed by a turbulent (i.e. multi-frequence) displacement.
2. A simple vertical band which begins to desintegrate because of its perturbations.
3. A perturbed high-frequency, non-uniformly scaled noise. This may simulate hair or other irregular fibrelike structures.
Some more interesting effects may be obtained if the deformer action is very inhomogeneous and localized,
even more than the torsion transformation.
Another characteristic deformer is a spherical “lense” effect
which we have already seen, applied to a texture retrieved from a
bitmap, on Fig. 2.5. Fig. 6.3 explains it better then the code, but
if somebody wants to see the details, everything is in the module
CTMapping. The lense deformer generates the displacement,
and it should be used under displace rather than transf.
Another example of the lense deformer is the pattern visualised on the cover page. Both lense and torsion deformers — as
defined — are centered at zero.
So, there is one important point which will be discussed now:
Fig. 6.3: Lense effect explained
how to apply shifted, rotated, etc. deformers to textures. This is
not the same as translating etc. the textures themselves.
6.3 How to transform deformers
The necessity to compose transformations is self-evident. Clastic library contains a few utilities for common
tasks, e.g. the rotation of a texture about a given point which includes a rotation and two translations: direct
and inverse. It may be defined as
rotatedAbout p0 angle // of a texture
= translated p0 o rotated angle o translated (~p0)
which is a pure transformer, defined independently of its argument. We will now generalize such constructions.
Suppose we want to apply a translated lense to a texture, with ~u being the translation vector, but without
moving the texture. Analyzing the sense of (6.2) we see that we can proceed as follows:
• Translate the texture by ~u, i.e., apply the inverse transformation to the texture argument. This will shift
the location p~ = ~u to zero. In general, transform the argument: p~ → R−1 p~.
6.4 General warping
36
• Apply the original deformer G (“anchored” at zero).
• Translate the resulting texture back, by −~u, or, in general, apply R−1 to the resulting texture (or R to its
argument.
In fact, we don’t need to distinguish conceptually the “main” deformer, say, G, and the auxiliary transformation
R. The final effect is the composition of three transformations: RGR−1 , and the appropriate representation in
the domain of textures is immediate, we apply ζ RG−1 R−1 p~ .
For arbitrary R the problem is difficult, since we have to operate with both, direct and inverse transformations, and current version of Clastic does not provide generic solvers adapted to it. But typical global
transformations such as translations or rotations are easy to invert, and the package defines such simplistic
functionals, as
trfshift u dform p = u + dform (p-u)
with dform being the deformer function. It is straightforward to see that this definition is equivalent to
trfshift u dform = ((+) u) o dform o ((+) (~u))
exactly as expected. This is not the end of the story, but almost. Iin order to apply the modified deformer to a
texture, we define
trftranslate p0 = transf o (trfshift p0)
or, if you are a more normal person:
trftranslate p0 deformer = transf (trfshift p0 deformer)
Anologously, we have
trfturn ang tfun = rot2cs ca sa o (tfun o rot2cs ca (~sa))
where
ca=cos ang
sa=sin ang
for rotated deformers, applicable — of course — to anisotropic transformations e.g., for already shifted deformers. Fig. 6.4 shows a shifted local deformer, already well know lense, and also an iterated, 5-fold shifted
and rotated whirl, which produces a celtic-style multi-spiral. As mentioned, the code of these (and further)
examples is included in the package distribution.
6.4 General warping
We address here the following question: how to “push” locally a texture in a given direction. Suppose we want
to displace the zero of the coordinate system by a vector ~v . The neighbouring points should follow it, but the
amount of translation decreases with the distance, and points far away are not affected. We should get a result
shown on Fig. 6.5 at the left.
We might imagine applying the following (direct!) transformation of points:
p~ → p~ + exp −k|~
p|2 ~v
(6.3)
which satisfies our general requirements. The problem is that we need the inverse transformation. . . . Moreover,
√
if the central displacement ~v is too big with respect to the range of the effectively deformed zone, about 1/ k,
the transformation is not reversible at all, the texture would “fold”, as a membrane pulled too far with respect
to its elasticity, and some information would be lost.
We will thus consider that the displacement vector is small. More consequent warping can be obtained by
iterating small steps. The displacement of an arbitrary point, not necessarily the origin of the coordinate system
has been already discussed.
6.4 General warping
37
Fig. 6.4: Transformed deformers
Fig. 6.5: Warping, simple and compound
Clastic in its current version implements a warper based on Eq. (6.3), but it is relatively easy to change it.
Relatively, since the procedure warp p inverts the above formula using Newton iteration, and the Jacobian is
constructed manually. In the next version it should be semi-automatic, with the aid of the AD package.
The module CTExamples contains warp, but also warpfold — a composite warper which applies
warp along a trajectory (list of points). The result is shown at the right of Fig. 6.5.
Chapter 7
Textures in 3 Dimensions
7.1 What are they for and how to show them
Although we see only the surface of objects — if they are opaque, the appearance of this surface may reflect
the 3D structure of the enclosed volume. Objects made of stone, wood, or any other structured material should
be visualized as such, and Clastic provides some tools to make 3D textures also.
Tools are simple, since Clastic is not a 3D modelling package! But we have a reasonable collection of 3D
vector algebra tools, and a definition of a camera: an object parameterized by its position, the “look-at” vector
(points along this direction will be mapped to the center of the rendering rectangle), its “focal length”, the “up”
vector which defines which direction is vertical on the screen, and some auxiliary, derived attributes. (Actually,
we have a small ray tracer.)
Fig. 7.1: 3D checkerboard, 3 different visualizations
The camera etc. will be described in the next section, here we simply point out the fact that we can visualize
a 3D texture by cutting it with any plane, for example, proj2 tx3 is a normal, 2D texture reconstructed from
a 3D density tx3 by putting z to zero:
proj2 tx3 (V2 x y) = tx3 (V3 x y zero)
For any other (visualization) plane the projection is just a little more complicated. Suppose that the pair (~n, c)
defines an implicit plane in the space, the set of points ~x which fulfil the equation ~n · ~x = c, where ~n is the
(normalized) normal to the plane, pointing from the plane to the “camera” (arbitrarily, or infinitely distant in
this case, since the default projection is parallel, orthogonal to the surface), and c — its distance from the origin.
We consider that this plane defines our virtual viewport, and that we “look at it” along its normal. This
means that the viewport coordinates p~ define vectors perpendicular to ~n. The center of the viewport (x, y = 0)
38
7.1 What are they for and how to show them
39
~ = ~n. We underline once more that the
corresponds in the 3D space to the vector c~n, and that the axis Z
projection being orthogonal, the position of the “camera” along ~n has no meaning.
This is not enough to establish the “anti-projection” which generalizes the definition proj2 above, since
~ Y
~ perpendicular to ~n. We must specify which vector is the
we still have the freedom of choice of the axes X,
~ axis. The procedure goes as follows:
“up” one, and it will be our Y
~ = ~n.
• Declare Z
~ non-collinear with ~n. Construct the Y
~ axis by computing U
~ ⊥ with respect to Z,
~
• Choose any vector U
and normalizing it.
~ =Y
~ ∧ Z.
~
• Complete the triad by X
The definitions of a parallel and a perpendicular projection of a vector ~v wrt. a normalized axis ~n are just:
~v||
~v⊥
= (~n · ~v ) ~n ,
= ~v − ~v|| .
(7.1)
(7.2)
~ + yY
~ + cZ.
~ This will be
This permits to lift p~ = (x, y) from the viewport to the 3D space. We get: ~x = xX
coded as
proj_plane n c up0 tx3 = t2
where
upar = (up0<.>n)*>n
yax = normalize (up0-upar)
xax = yax /\ n
t2 (V2 x y) = tx3 (x*>xax + y*>yax + c*>n)
Of course, instead of choosing an arbitrary normal vector we can also rotate the texture itself. The appropriate
transformer is defined similarly to the 2D case, it suffices to know how to rotate a 3D vector. The decomposition
in (7.2 will help us to derive the Rodrigues formula, without the necessity to construct matrices, and produce
other unreadable pieces of code.
Obviously, if a vector ~v is decomposed into parallel and perpendicular components wrt. the rotation axis
~n , only the perpendicular part is transformed, and the problem becomes 2-dimensional. The only “difficulty”
is that in using an invariant notation we do not have the “X” nor “Y ” axes given explicitly. The idea is to use
the vector itself (its perpendicular projection) as one of axes, and construct the other one by taking the vector
product of the first one and the rotation axis. The code follows.
rot3 ang = rot3_cs co si
where
co=cos ang
si=sin ang
rot3_cs co si n v
# vpar = (v<.>n) *> n
// Invariant part
# vperp = v-vpar
// Perpendicular; Axis 1
# v3 = n / vperp
// Axis 2
= vpar + co*>vperp + si*>v3
In such a way, from a “3D-checkerboard” (cubes of alternating colours) we obtain the Fig. 7.1, at the right.
We could easily provided a few other planar projections, e.g., from a point camera, but we will choose now
the projection surfaces more complicated than just a plane, as texture testing tools. The rendering of 3D objects
is very far from the aims of this package.
7.2 Visualization of 3D textures in Clastic
40
7.2 Visualization of 3D textures in Clastic
Clastic implements a simple ray tracer. Simple, means simple: no recursive ray projection (no reflections nor
refractions), no optimisation. It is there just to permit the visualization of 3D textures on some predefined single
objects. In the current versions we have a sphere, a cube and a cylinder.
As already mentioned, the module CTView defines a camera object with enough properties in order to deduce the parameters of the associated viewport. The camera together with its viewport, an attached background
texture, and an (optional) light source constitute a Viewscreen object, which is constructed by our interface
(or manually, if you wish so). It handles the focal (“perspective”) projection from 3D onto the viewport. The
tracer procedure takes a 3D texture, the viewscreen, and a mapping which describes the single object, and
constructs a 2D standard texture.
The most important element here is the mapping: a cube, a sphere or a cylinder, as mentioned above. Such
an object is centered at zero. Here is the definition of the spherical mapping spherepoint viewscreen
point2 -> (intersection,normal,viewline):
spherepoint view = smap where
// Straight line generator
ray = view.ray
pos = view.cam.pos
ps2 = norm2 pos
// distance sq. camera --- origin
smap p
# v = ray p
// concrete, this ray
# sc = pos<.>v
# dl = sc*sc+1.0-ps2
| dl<zero = (False,zero,zero,zero)
# rpos = (sc + sqrt dl)*>v
# xx = pos - rpos
= (True,xx,xx,rpos)
The reader may have a look on CTMapping in order to find the mappings corresponding to other objects (and
perhaps construct his own).
A 3D texture is a function of type: Point Point Point → Colour. The second argument is the normal at a
given point, and the third: the vision line, the vector pointing from the current point to the camera. This might
facilitate the implementation of the specular light reflections, bump-mapping, and related effects.
Fig. 7.2: More 3D textures
On Fig. 7.2 we see a marble sphere (coded in Examples/example4.icl), and two wood-like objects
whose code is not given since it is based on our old, well known turbulent displacement, only in 3 dimensions.
The normal is not used.
7.3 Light and bumps
41
7.3 Light and bumps
Since the interface of Clastic is just a visualizer, there was no point in implementing exquisite lighting effects.
However, in order to see better the geometry of a 3D object the light is very useful. Clastic implements
thus a simple-minded lighting, composed of the ambient and of diffuse terms, and additionally attenuates the
transition between lit and unlit zones (sharp shadows would look silly). The diffuse term is just an attenuation
~ ·N
~ C, where N
~ is the normal to the surface, L
~ — the direction of the light source, and C — the
factor: L
colour of the source. This factor combined with a constant “ambient” term multiplies the intrinsic colour of the
texture.
Adding some specularity, e.g. the Phong or Blinn algorithm is trivial, and will be probably added to the
next version. The 3D texture function, and some interfacing protocols are already prepared for that. But, again,
don’t expect any wonders, if you need something serious, it must be ensured by a different layer of procedural
texturing: volumic shaders, light shaders, etc. Perhaps some next version of the package will contain such
elements, but then a fully-fledged rendering engine will be necessary to test all that.
So, the next and last example of this section is the construction of a bump-mapped surface illuminated in a
more complex way. The default light available through the interface dialog should be desactivated.
Making “3D effects” which add interesting visual aspects to flat (or spherically dull) surfaces, ia a whole
branch of science, art, and dirty cheating. The file Examples/example4.icl shows how to apply the
Blinn “bump-mapping” technique to simple surfaces.
Fig. 7.3: Bump-mapping
Fig. 7.3 shows our “marble” sphere distorted visually by a 3D noise function, and a reconstruction of a
planet surface from a colour map and a height field. The bump-mapping is a pure illumination effect, the
geometry of the surface is not modified.
The bump-mapping algorithm goes as follows. Fig. 7.4 shows what happens if the original, smooth surface
(blue line) S0 is deformed, by, say, adding a small height displacement field to it, prodicing the surface S.
Fig. 7.4: Distortion of the normal
The mathematical details depend on the way the surface is represented: parametrically or implicitly. Standardway of dealing with bump mapping is parametric. A surface is a two-argument vector function, giving the
7.3 Light and bumps
42
~ v). Then, the vectors:
coordinates of a point in terms of a local 2D coordinate system: ~x = S(u,
~
~u = ∂ S ;
S
∂u
~
~v = ∂ S
S
∂v
(7.3)
~ =
form the axes of this local system, they are tangent to the surface. Then, obviously the normal is given by N
~ Suppose now
~v . Usually it should be normalized; after the normalization of this vector we name it: N.
~u ∧ S
S
~ v) =
that the deformed surface is obtained by a normal displacement of the original by a small quantity: S(u,
~ 0 h(u, v), where h is a scalar function, small wrt. typical scales of graphic structures within the
~0 (u, v) + N
S
scene. If h is really small as compared to its derivatives, it is easy to see that we can approximate:
~
~0
∂S
∂S
∂h ~
=
+
N0 ,
∂u
∂u
∂u
and
~ =N
~ 0 + ∂h
N
∂u
~
~ 0 ∧ ∂ S0
N
∂v
!
∂h
−
∂v
(7.4)
~
~ 0 ∧ ∂ S0
N
∂u
!
.
(7.5)
Higher terms in h are neglected. If we can compute the derivatives, analytically or numerically, we have the
normal. We normalize it, and we use some light sources in order to find the resulting shading. The normalization necessary for every sampled point is quite costly, and there are some acceleration methods available (the
computations need not be exact, we have already cheated a lot. . . ). We shall not treat these problems here.
Since our 3D objects are very simple: sphere, etc., we could have used this algorithm. The angular
parametrization is straightforward. But we wanted to implement the technique identical to what we have done
with 2D, textural objects, i.e., to use implicit techniques.
If the surface is given in the implicit representation, i.e., as an equation S(~x) = 0, then the (unnormalized)
~ is just the gradient of S. In order to deform it — not necessarily for the bump mapping, but
normal vector N
in general — the technique used is our well known contravariant transformation of the coordinates:
~0 .
S(~x) = S0 ~x − h(~x)N
(7.6)
The modified normal is given then by
or
~0
~ =N
~ 0 1 − ∇h
~
~0 · N
N
N
,
(7.7)
~ 0 − ∇h
~ = |N
~ 0| N
~
N
.
(7.8)
We need thus just to compute the gradient of the height field, but
differently than in the parametric case. Of course it should be
normalized. This is what we have exploited in our examples.
The gradient may be calculated through finite differences, analytically, or using AD. The module example4.icl uses finite
differences (the gradient is defined in the module CTVector)
just to see whether they do not introduce too much noise.
There is nothing in the package which is specific to spheres
or other simple surfaces, and even if the bitmap which represents
the bump height field is adapted to spherical mapping, we can use
it in different context.
Fig. 7.5 shows the true shape of our Moon, which is badly
known because of atmospheric distortion and other religious prejudice.
Fig. 7.5: The Astronomic Reality
The last two textures in example4.icl are again 2D objects. We used the inverse spherical mapping in order to reconstruct a 2D “height field” from a 3D texture
7.3 Light and bumps
43
(here: a horrible fractal (turbulent) spherical relief, with even more horrible colours) retrieved from the 3D
surface which should be bumped. It should be rendered in a rectangle x : [0, 2π], y = [0, π]. See the distortion
near the polar region, characteristic for the Mercator or other similar projections.
Fig. 7.6 shows the result. This is a common technique, useful for some synthesis tasks. If the manipulation
of a 3D texture is costly, and we know in which contexts it will be used, it is often possible to construct an
appropriate map, and to use it instead of the original. Reflection maps, environment maps etc. are examples of
this strategy.
Fig. 7.6: A Fractal Planet and its maps
Chapter 8
Tesselations
8.1 Introduction
Tilings are omnipresent. Mosaics, rugs, wallpaper, bricks, decorative sidewalks (such as the notorious wiggly
pattern on the Copacabana beach), etc. So, this is an important part of the texturing technology. Clastic
implements some useful tools, but the work is very far from being complete, and it will continue for some time.
An obvious feature of this domain is that something interesting is obtained by experimentation, and the number
of “regular” techniques is small. Anyway, in order to progress, one has to know a lot about plane symmetries
and tesselation in general, and this is not always easy [16, 17, 18, 21]. . .
If you have a “drawing” program, i.e., a set of routines which place some graphic objecs where you need them,
making replicated pattern with translational, rotational or mirror symmetries is essentially straightforward, you
write an appropriate iterative loop, provided you know how to transform objects.
For a texturing application the life is a bit harder. If the textured zone contains several identical objects,
e.g. it is a star polygon, such as that we constructed in the section (4.1.2) using the CSG method, for every
pixel of the image all the contributions from individual patterns have to be checked, which may be extremely
expensive. In general case nothing can be done, but if the objects placed are replicas of the same entity which
may be transformed according to some symmetry considerations, the situation changes.
The serious way of dealing with that resides in reducing the
argument (point) of the texture generator, finding the “original”,
and from the relation between the actual point and its reduction,
deduce the transformation to be applied.
In such a way, the correct way to render a star should follow
the idea shown on Fig. 8.1. If the current point is located in the
sector between the lower green ray and the red dotted line, the
texture applied is the dashed half-plane. If the point is above the
dotted ray, apply the mirror inversion of the half-plane wrt. the
dotted ray, otherwise, if the point is outside the sector between
green lines, rotate it back, together with the texture definition.
This is the entire code of a regular star (1 inside, 0 outside).
The code is quite long because of the parametrization; this functions computes the star for any n, and for any two “radii”, as
shown on the Fig. 8.1. Moreover, it is as invariant as possible,
Fig. 8.1: Another star making
without the separation of point coordinates. The function angle
(V2 x y) returns atan2 x y.
regstar n r0 r1 = rstar
where
44
8.2 Plane symmetries
45
alpha = Dpi/fromInt n
// Elementary sector
alph2 = alpha/2.0
hax = rot2 alph2 X2axis
// Dotted half-sector axis
p0 = r0*>hax
nrm = rot90 (normalize (p0 - r1*>X2axis))
hplane p = step (nrm<.>(p-p0))
// Half-plane. Below is reflected
reflected v = hplane ((2.0*v<.>hax)*>hax - v)
rstar p
# a = angle p
# turn = floor(a/alpha) * alpha
# ared = a - turn
// Angle reduced to the 1st sector
| ared <= alph2 = rotated turn hplane p
// No reflection needed
= rotated turn reflected p
// But here: yes
This definition belongs to the standard Clastic panoply. The reader will find it in the module CTBase.
8.1.1
Small optimization
p
If you want to render many such stars, you may accelerate the painting using bounding disks. If r = x2 + y 2
is greater than r1 , the point is outside the star, and there is nothing more to compute.
Here we can also use the interior bounding disk: if r is smaller than r0 , then the point is inside the star.
This technique can be applied to more complicated figures where the gain would be much more important.
Readers acquainted with graphic modelling should see immediately the possibility of applying a hierarchical
decomposition of the figure in quadtrees. As compared with the standard quadtree technique, where the leaves
are blocks of desired size (depending on the precision, i.e. the qudtree depth), in a functional language we may
attach to the branches — at any level — the closures which compute the details, or return a static value very
fast.
This strategy may pay itself if the figure is really complicated, because the quadtree construction and
traversal have also their costs. . .
8.2 Plane symmetries
In the construction of arbitrary geometric patterns, finite and infinite, we have to provide for all possible symmetry reductions. In particular, we can make stars with arbitrary number of vertices. Replacing in regstar
the straight lines by something more complicated, and constant values attributed to the interior/exterior zones
by some composite textures, we may generate very nice decorative patterns of any complexity.
We shall see that for infinite textures possessing also translational symmetry (forming a kind of 2D “crystal”
lattice), the freedom to choose rotational symmetry is severely limited. We learn it on the elementary geometry
courses: only triangles, squares and hexagons fill the plane. It would be preposterous to try to cover all the
theory of plane symmetries in this tutorial, so we shall sketch here a bare minimum useful for the construction
of interesting textures.
Conventionally, we classify symmetric planar patterns into:
1. Rosettes, possessing rotational symmetry of any order, and — possibly — some reflections. A regular
n-star is a rosette. If it is defined with homogeneous texturing of the inside, it has n axes of reflective
symmetry if n is odd, and 2n such axes, if n is even. A rosette may have only one center of rotation,
because two distinct centers imply the existence of translations.
2. Friezes, 1-dimensional bands with translational symmetry, and eventually some reflections wrt. the axis
of the band, or perpendicular.
3. Wallpapers, having as their symmetries the so called wallpaper groups, containing at least 2D translations, eventually rotations, reflections, and glide reflections which will be defined below.
8.2 Plane symmetries
46
Here we will treat the last category. There are 17 distinct wallpaper groups, but, as we shall see, they don’t say
too much about the actual replicated shapes.
Fig. 8.2 shows the difference between axial reflections and glide reflections. These last transformations are
composed of reflections wrt. an axis, and a translation parallel to this axis. We remind that there is no need to
speak about focal reflection (wrt. the center of the coordinate system) in two dimensions, since this operation
is identical to a rotation by 180 degrees.
Fig. 8.2: Reflection (axial), and glide-reflection
Glide reflections have nothing obvious in them, and their usage needs some imagination. But they are
useful for parquets, or similar patterns, see e.g., the central picture on Fig. 4.7. This is the Tartan weaving
pattern: neighbouring diagonals with horizontal and vertical segments are in glide-reflection relation (apart
from colours, of course).
8.2.1
Important entities
In order to understand the rest of this section the reader must become acquainted with the following.
1. Since we are considering translationally invariant textures (i.e. we shall not touch Penrose patterns, etc.),
the infinite plane may be covered by discrete translations (and only translations) of a finite (and rather
small with respect to the texturing area) region, which we call the Unit Cell (UC). Sometimes it is called
“lattice unit”. The choice of UC is never unique, and we will see that much freedom remains after having
chosen it.
2. If the symmetry group under consideration possesses also other operations, i.e. discrete rotations, reflections, and glide-reflections, it is possible to cover the plane using all these symmetry operations.
The smallest (or: one among smallest) regions able to generate the whole plane by applying all possible
translations, rotations, etc., is called the Fundamental Region (FR). Usually it is a fragment of the UC.
3. Neither UC nor FR do specify the replicated shape! This pattern, whose form and texture details depend
on the user, may occupy neighbouring fragments of different UC and FR. We will call it the Motif (M).
It cannot be arbitrary, of course, by cutting it and reassembling its parts (eventually repeated) we must
be able to reconstruct the UC and FR.
8.2 Plane symmetries
47
So, the geometry of UC and FR doesn’t say anything about the internal
structure of the replicated pattern. Sometimes the same formal symmetry
attributes provide just some topology constraints, and from the same group
we might produce a hexagonal lattice and rectangular brick wall, as we shall
show later. Also, the “skeletons”, or the boundaries of the replicated regions
may possess symmetries higher than the actual textures. E.g., a square has
a fourfold rotation center and 4 reflection axes, but if you put something
asymmetric inside, all this symmetry is lost.
The Fig. 8.3 shows a Motif which fills the plane with just translations,
Fig. 8.3: Translational block
as shown on Fig. 8.4, one of the most elegant semi-regular tilings. If we
eliminate the colours, the pattern will acquire several rotational and reflective invariance properties. If we shear
it, these symmetries will disappear once more, but the translational symmetry will remain.
Fig. 8.4: A semiregular P1 pattern
8.2.2
Wallpaper groups
We enumerate them all before passing to concrete realisations, where all the details and illustrations will be
introduced incrementally. We shall include also some simple theorems needed to construct the coordinate
reducing routines. There are two popular naming schemes, the International Union of Cristallography notation,
and a more recent and mathematically-oriented Orbifold notation, proposed by J.H. Conway (based on ideas
of William Thurston). We will use the IUC notation. it consists of four symbols, sometimes simplified. It
identifies
• The Cell. Usually the UC is chosen so as to have centers of the highest order rotational symmetry at its
vertices. We call it a primitive cell; the group will contain the letter P. Sometimes, however, it is more
useful to deal with a centered cell (C), in which there are reflection axes perpendicular to the cell sides.
• The highest order of rotation: an integer number n.
• A symbol denoting an axis normal to the x-axis (conventional): M (mirror) indicates a reflection axis, G
indicates a glide-reflection axis (but no ordinary reflection symmetry), L indicates no symmetry axis.
8.2 Plane symmetries
48
• A symbol which denotes a symmetry axis at angle α to x-axis, with α dependent on n, the highest order
of rotation: α = 180◦ for n = 1 or 2, 45◦ for n = 4,60◦ for n = 3 or 6. Symbol M, G, or L are
interpreted as above. No symbols in the third and fourth position indicate that the group contains no
reflections or glide-reflections, and the notation may be shortened.
So here is the catalogue of wallpaper groups. Please read [16, 17, 18] for the details and examples.
1. P1. Translations only, two arbitrary axes. The FR and UC are identical, and as you may imagine, this is
a parallelogram. This is the most simple tiling possible, but we shall see that if the translation axes are
not perpendicular, the texturing is less trivial than we have seen while constructing a checkerboard.
2. P2 (P2LL). The presence of the 180◦ rotation (half-turn) symmetry implies that the texture is invariant
wrt. the central reflection: p~ → −~
p. Of course, here and later: the existence of a rotation center, and
the translational symmetry imply that there are infinitely many rotation centers forming a lattice
compatible with the translational grid. It is not difficult to see that the center of the UC must be a rotation
center, since the half-turn must leave the UC invariant. But the UC remains an arbitrary parallelogram,
only the FR is now reduced to a half of it (below or above one of its diagonals).
3. PM (P1ML). We have a mirror now. Knowing that an arbitrary parallelogram transforms to something
non-isomorphic to it under an axial reflection, we see that the UC is a rectangle, and the translation axes
must be perpendicular. Of course, as previously, it is easy to deduce that we will have an infinite number
of reflection lines. The Fundamental Region is a half of the UC, say, the lower half of the rectangle, if
the reflection axis is horizontal.
4. PG (P1GL). Similar to the above, only the mirror translates also the pattern horizontally.
5. PMM (P2MM). This is a very classical tesselation, with horizontal and vertical reflection axes. Of
course, having two different axial reflections implies the existence of a half-turn symmetry. The UC is
rectangular, rotation centers are at its corners, mid-edges, and in the center, and the FR is a quarter of the
UC.
6. PMG (P2MG). Two horizontal reflection lines within the UC, accompanied by a vertical glide-reflection
line. UC rectangular, FR is a lower quarter of it.
7. PGG (P2GG). Two glide-reflections, implying a rotational 180◦ symmetry as well. This is an interesting
symmetry, able to generate parquets, or specific weaving parquets, as mentioned above (see the Fig. 4.7,
the picture at the center).
8. CM (C1ML). Here the UC is a rhombus, having a reflection axis along its diagonal, and two parallel
glide-reflection lines. The FR is a half of UC.
9. CMM (C2MM). An augmented version of the above. Horizontal and vertical reflections, and also glide
reflections. So plenty of half-turn symmetry centers as well. UC is a rhombus, FR is a quarter of it.
10. P4. That’s it, almost nothing more to say. Or, perhaps not. The UC must be a square, and the 4-rotation
centers are at the corners, and there is a central one too. This implies that there are also 4 2-rotation
centers, at the middle of the 4 edges of the UC. The FR is a quarter of the UC. There are no reflection
symmetries, this group may generate “swastika” patterns.
11. P4M (P4MM). P4 plus a mirror implies much more. There are 4 mirror planes, vertical, horizontal
and diagonal (of course both). There are also four glide-reflections, also diagonal, but shifted, half-way
between the center and a corner of the UC. The FR is one-eight of the UC. It generates more or less
“classical” cross-style patterns.
12. P4G (P4GM). The UC is a square with five 4-fold rotation centers, four half-turns at the mid-edges, four
half-diagonal reflection axes, and 6 glide-reflections. Not very intuitive, but you have seen it already.
This is the right pattern on the Fig. 2.1. The FR is one eight of the UC, but positioned differently than in
the case above. Of course, the Motif may be much more sophisticated.
8.3 Tile generators in Clastic. Unit cells
49
13. P3. The basis for higher symmetries, kaleidoscopes, and other magic patterns. If you think that UC is a
tringle, it isn’t. It is a parallelogram (a rhombus in fact) with axes of identical length, forming an angle of
60◦ (and 120◦ , of course). There are six 3-rotation centers within it, two inside, and four at the corners.
The details are below, see Fig. 8.10. The FR is a smaller rhombus, occupying one third of the UC, and
whose long diagonal is the short diagonal of the UC.
14. P3ML. Ah, this one is very interesting. In fact this may be called the kaleidoscopic group. The UC is
the same rhombus as in the P3 case, but additionally there are 5 reflection axes (and some implied glide
reflections as well). One of these reflection axes is the long diagonal of the rhombus, and the FR is a
half of the P3 FR, above the long diagonal. (This is an equilateral triangle in case you still have some
doubts). The 3-fold rotations are the crossing points of the 3 reflection axes.
Actually, the mirrors suffice to generate all the rest. Three such axes form a triangle composed of mirrors,
the most classical kaleidoscope. If you have never seen it, you wasted your childhood. . .
15. P3LM. This one is not very intuitive. There are 3-rotations with centers at the crossing of 3 mirrors, and
also twice as many 3-rotation centers in the middle of triangles formed by reflection axes. The FR is a
triangle with angles 30◦ , 30◦ , and 120◦ . This is one-third of an equilateral triangle which is a half of the
UC rhombus.
16. P6. As in many cases above, the existence of a 6-rotation center implies also some others. The UC is a
60 –120◦ rhombus, and the 6-centers are at its corners. But the UC contains also two 3-rotation centers,
and four half-turns. The FR is one-sixth of the UC.
17. P6M (P6MM). This is an extremely symmetrical “stained glass” pattern generator with the same rotation
centers as above, but possessing 9 reflection axes, and a large number of glide-reflections. A verbal
description doesn’t say much. The FR is 1/12 of the UC rhombus.
8.3 Tile generators in Clastic. Unit cells
As we have already said, we will not discuss limited symmetries with just 1D translations, or only rotations.
So, all textures will lie upon a geometry possessing two translation axes. In the module CTTile we define
a generic UnitCell data structure: a record containing two axes axS and axT, and plenty auxiliary values,
such as norms of the axes vectors (they need not to be identically normalized, although in principle we could
work with normalized axes, and rescale everything later. But this would be extremely cumbersome). This
record contains also the precalculated scalar and vector products of the axes, and several conversion functions,
needed to pass between normal Cartesian coordinates and the local coordinate space. The constructor of a UC,
mkunitcell needs only the two axes.
Fig. 8.5 shows a general Unit Cell, and a way to calculate the internal, normalized coordinates s and t,
which belong to the interval [0 – 1]. In general case, any vector p~ (not necessarily within the UC) can be
~ + tT~ . Since the axes are not orthogonal, construction of s and t is
expressed through the UC axes: p~ = sS
more involved that just taking a scalar product of the point and one of the axes. But we can compute the skew
product which in 2 dimensions yields a scalar: ~a ∧ ~b = ax by − ay bx . Here we have
~ ∧ T~ ,
p~ ∧ T~ = sS
(8.1)
etc. We have thus the non-cartesian (contravariant) decomposition given by:
~ ∧ T~ ;
s = p~ ∧ T~ /S
~ ∧ p~/S
~ ∧ T~ ,
t=S
(8.2)
and in order to reduce any point to the UC, we apply the function frac to the resulting coordinates. The
reduced point is reconstructed through (8.1), and the tiling problem of any P1 texture is solved.
Well, almost. . . We have already mentioned that the Motif doesn’t need to cover a UC. Fig. 8.6 shows what
may happen. Now, the possible ways of cutting the UC into pieces and their reconstruction is a work for an
8.3 Tile generators in Clastic. Unit cells
50
Fig. 8.5: A unit cell; two sets of intrinsic coordinates
Fig. 8.6: Motif displaced wrt. the Unit Cell
artist, not for a program, although there are programs which can almost automatically “Escherize” a not very
complicated form. In this case some rotational and reflective symmetry usually must be exploited. We shall
show here just some transformations easy to produce regularly. Fig. 8.7 shows an asymmetric pattern which
will be sometimes used to visualise the symmetry of the tiling.
Clastic in its current version offers a very simple tool: a generator of hexagonal patterns (more or less) centered at the origin, otherwise arbitrary. As an
extra bonus, the reducing procedure discovers whether the current point is
near the boundaries of the motif region (the black lines forming a “H” inside
the UC on Fig. 8.6), and considers it to be the “mortar” zone, filled with
a different texture (which is P1-reduced, but not displaced as the remaining parts of the UC). The module CTTile contains the procedure hextex
which takes a cell, the width of the mortar (if negative, its only effect is a
slight slowing down of the rendering), and two points sufficient to specify
completely the “H” pattern within the UC. It takes two more parameters, the
main texture, and the mortar filling. This is a horribly long procedure, about
30 lines. . . , but then, the construction of beehives, or of brick walls, is done
Fig. 8.7: Asymmetric motif
in just a few lines, less than 10. We suggest to compare this with typical
RenderMan shaders. The file examples/example5.icl shows some
simple textures, visualized on Fig. 8.8.
This is more or less everything we can say about the translational symmetry. The situation becomes technically more complex if we engage rotations and reflections, but conceptually there is nothing new, just additional
properties permitting to reduce the current point in a more agressive way.
8.4 Symmetry P3
51
Fig. 8.8: Some tesselations with hexagonal topology
8.4
Symmetry P3
Clastic does not implement (for the moment) the full 17 wallpaper groups. First, it would be boring, the results
are not always so pleasant.
Actually, truly interesting tilings are either semi-regular, or
hierarchic, with different symmetries at different scales. All this
will be treated much later.
Then, implementing wallpaper groups quickly becomes tedious, and doesn’t add anything to the applicative side of the texturing process. Finally, we have to leave something to our students and/or other readers. But something is there for your inspiration. In this section we treat the group with three-fold rotation
centers, and no reflections.
If we don’t play with a differently positioned motif, nor other
additional transformations which may add some juice to our artistic creations (for example: colour changing between rotated instances of the motif), me may get a result shown on Fig. 8.9. The
concrete code is shared between the module CTTile, and the file
examples/example5.icl.
Fig. 8.9: A simple P3 pattern
8.4.1
A simple case
Fig. 8.10 shows the UC of a P3 group. We may divide it into two equilateral triangles, each of them containing
three rotated instances of a half-FR, A or B .
Again we see that the choice of the FR is not unique. At the right of Fig. 8.10 we see two different possible
Fundamental Regions. There is nothing universal permitting to choose one of them for the tesselation.
The reducer procedure is not more complicated than the technique already known from the motif reconstruction, only the rotation is used instead of translations. Perhaps, from time to time it is useful to see the full
code of such a reducing procedure, here it is:
p3reduce tx = ttx
where
stvec =: Tricell.st_vec
rp0 =: stvec (one>/3.0)
rp1 =: stvec (2.0*>one>/3.0)
ttx p
# q = Tricell.red_st p
// A constant, standard rhombic Ucell
// Centers of two triangles
// Point, reduced to the Unit Cell; (s,t)
8.4 Symmetry P3
52
Fig. 8.10: The P3 symmetry, Unit Cell and Fundamental Regions
#
#
#
|
|
|
|
=
8.4.2
(V2 s t)=q
rq = stvec q
// Reconstruction in (x,y) space
(V2 u v) = Tricell.st_uv q
// Covariant coords are here also useful
u<=0.5 && s<t = rotatedAbout rp0 PiD tx rq
// PiD = 120 deg.
v<=0.5 && s>=t = rotatedAbout rp0 PiA tx rq // PiA = 240 deg.
u>1.0 && s>=t = rotatedAbout rp1 PiD tx rq
v>1.0 && s<t = rotatedAbout rp1 PiA tx rq
tx rq
A tribute to Escher
We might want to produce a tiling with the motif which overflows the FR, and moreover, it is would be nice
to change something (e.g. colour) while passing from one instance of the motif, to another one, rotated. Let’s
produce the notorious reptile pattern on the Fig. 8.11.
Fig. 8.11: Escher lizards bred by Clastic
We see that the shape (the outline) of one of those beasts — although composed of straight segments — is
not very easy to represent as a mathematical form (we tried, and we decided not to repeat this experience. . . ).
8.5 Symmetry P3ml: a kaleidoscope
53
The position of one lizard within the UC is presented on the Fig. 8.12, and if the reader is imaginative enough,
he will see that the corners of the lower triangle of the FR are three rotation centers permitting to fill the space
around the original.
Fig. 8.12: A lizard in its geometric habitat
The lower, empty part of the grey rectangle is not completely useless. If the side
√ of
√ the UC is equal to
one, the rectangle has well-defined “virtual” dimensions: x ∈ [O, 3/2]; y ∈ [−1/2 3, 3/2]. We know the
position of the rotation center in the texture space, and we shall use a bitmap image of the lizard as a replicated
pattern, of course after having transformed the image into a texture generator. The same image, or a simplified
version of it will serve as a mask permitting to find out whether the current point finds itself inside or outside
the motif. So, don’t expect that any texturing package does such manipulations automatically! You must know
everything about the geometry of your motif, choose the background mask, etc.
There is one remaining problem, which should be resolved also case by case. The motif overflows the
UC, not only the FR. The reptile generator verifies thus also the UC beneath, and at the right of the main cell
(without touching the rotation symmetries; in other cases one might has to look up other neighbours as well).
The generator is used in the file examples/example5.icl. It is defined in the module CTExamples,
and it should be fairly easy to parameterize it to other Escherisms of this kind. Because of the imperfection of
current bitmap handling, the size of the rendering window should be at least 400 – 500 pixels, otherwise the
results are ugly (but switching on the antialias helps a lot). Anyway, those lizards are ugly, the geometric shape
which we sketched by hand using Escher model is not precise.
8.5 Symmetry P3ml: a kaleidoscope
A kaleidoscope is a set of three mirrors forming an equilateral triangle. With two (or more) reflections there
is plenty of rotation centers as well, and in principle we might not use the rotations, only the reflections. This
would make the reduction process slightly longer.
In fact, even the translations are redundant. The kaleidoscope generates an infinite plane only by reflections,
and with some tuning just two reflections suffice to reduce any point on the plane to its original triangle, which
is its Fundamental Region. But in the current version we reduce first evrything to the Unit Cell by translations.
We have shown on Fig. 8.5 the two useful sets of non-Cartesian coordinates, the standard contravariant
(s, t), and covariant (u, v). Both will be used here. Looking at the left drawing on Fig. 8.10 we may localize
8.6 Parquets PGG
54
the FR in the present case, it is the upper half of theyellow rhombus. The mirrors are given thus by the equations:
s = t;
u = 1/2 ;
v = 12 .
(8.3)
There is another pair of (secondary) mirrors: u = 1, and v = 1/2, and there is almost nothing more to say.
When the generator finds out that exactly two reflections are needed, this is the rotation case. In realistic
kaleidoscopes the motif is enclosed entirely by the FR, although the ambiguity between the original reality and
its mirrored instances is an immortal artistic and fantastic cliché.
On Fig. 8.13 we see two kaleidoscopes, one with our simple asymmetric motif, and other representing a
reptilian folk dance.
Fig. 8.13: Two kaleidoscopes
8.6 Parquets PGG
Time to introduce some glide-reflections. On Fig. 8.14 we see a classical parquet pattern. This result was produced by a specific reduction procedure, which doesn’t use the standard Unit Cell data structure. It is possibly
not the most efficient algorithm, since it iterates the short movement along the diagonal, but it economises some
computations needed to localize the point within the UC. A regular procedure which reduces the current point
to the UC, then using the glide reflection (and the half-turn, which we will avoid) with the translation equal to
the half of the UC width, seems a bit awkward here because of the particular placement of the motif.
Anyway, Fig. 8.15 shows the structure of this symmetry. The red
rectangular contour delimits the UC, and the grey zone is the FR.
The dashed blue line is one of glide-reflectors within this group.
The width a and the height b of tiles may be arbitrary, although
for practical reasons very often on is a multiple of the other (this is
the case of “parquet” generated by our weaving algorithms; you
can exploit then the horizontal and vertical translations for the
coordinate reduction).
√
√
The dimensions of the UC rectangle are: 2a, and 2b, of
course. Its area is the sum of the areas of the horizontal and vertical tile. The center of the UC is a half-turn center, which is
easy, although non-trivial to see. However, if the tiles are filled
with a wood-like, not very regular texture, this symmetry is lost.
So, attention! Although the title of this section is “PGG”, the
generated parquet texture will have only the symmetry PG.
Fig. 8.14: A PGG/PG parquet
8.7 Conclusions
55
Fig. 8.15: A PGG parquet pattern
Here is the full code, with (a, b) being the tile dimensions, and tx — any texture you wish.
parquet a b tx = tx o redcell
where
a2 =: 2.0*a; b2 =: 2.0*b
// Some useful constants
apb=: a+b;
amb=: a-b
p0 =: V2 a (0.5*b)
u =: (Y2axis-X2axis)>/Sqrt2
// Up-left diagonal
tr =: (b/Sqrt2)*>u
// Glide amount
glidrefl p = refl p0 u p + tr
// refl is defined in CTVector
redcell (V2 x y)
# su=(y+x) mod a2
# di=(y-x) mod b2
// Just the standard UC reduction
= redmotif (0.5*(su-di)) (0.5*(su+di))
redmotif x y
// Look up the figure to understand this
| x<0.0 && y<b = glidrefl (V2 (x+apb) (y+amb))
| (x>=0.0 && x<a && y<b) = V2 x y
| x>=a = glidrefl (V2 x y)
= redmotif (x+b) (y-b)
// Might repeat this, usually less than 4 times
8.7 Conclusions
Needless to say, Clastic is not and will never be a tesselation package. We decided to built in some procedures
of this kind, since it is not easy to find such texture generators with some degree of genericity, and free to
manipulate. It is easy to find hundreds of tesselation packages, tutorials, descriptions of wallpaper groups, etc.,
but if we need such a texture as a shader, permitting its arbitrary composition and mapping, the life becomes
harder. Especially, it is difficult to construct interactively complex, multi-level hierarchic patterns. Feel free to
make your own experiments, and send us your results!
This work is far from being terminated. We need to code more groups, more reduction tools, and, last
but not least, construct the hyperbolic tesselation generators as well. But everything depends on the needs of
potential users.
Chapter 9
Differential Structures
9.1 Who needs derivatives
A cynical person might say that if you don’t know, then you don’t need them, and you might well skip this
section. But in computer graphics, as everywhere else, sometimes needs are conditioned by the availability of
tools, and often by the knowledge of their existence.
Suppose that the function t(~
p) is a texture, a scalar function of a point. If the co-domain is the space of
reals, it may be considered as a height field. In the section 7.3 (see the eqs. (7.5) and (7.8)) we show how to
compute the normal to it, and to compute the light effects, permitting to use a real texture as a bump map.
Another example, truly planar this time, is included in the file Examples/example1.icl, and discussed in the section 2.3.1 (see Fig. 2.2). If the implicit equation f (~
p) ≥ 0 defines the interior of a closed
~ (~
~ to the contour at the point p~. Then, if we normalize N
~ , the function
curve, the vector ∇f
p
)
is
the
normal
N
~ becomes zero along the contour shifted by with respect to the original. The function
fˆ(~
p) = f p~ − N
contour implemented in Clastic does exactly this.
More complex deformations, e.g. depending on the local curvature, need higher derivatives. In simulation/animation we need often to construct differential equations from some variational principle, or to compute
forces (accelerations) from a given trajectory. The vorticity fields (tornadoes, galaxies) may be constructed by
taking curls of specific vector potentials.
Derivatives/gradients are used in equation solvers which use the Newton algorithm. We needed them for
general warping.
If you don’t know how to operate with gradients etc., your creative freedom is restricted. In principle
the easiest way to compute them in a framework similar to ours (having all generators as functions) is to use
finite difference approximation. Clastic implements simplistic gradients in 2 and 3 dimensions using finite
differences, but it has been done just to test our AD package. Sometimes there is no choice, e.g., if the texture
is given as a static bitmap. But there exist some more elegant approaches as well.
9.2 What is Automatic Differentiation
As we know, the differentiation of an elementary function, say f (x) = 2 sin x2 + x /(exp(−x) + 1) is
a mechanical procedure, which usually does need any specific analytical, topological knowledge, and may be
performed by a high-school pupil, or by a not particularly intelligent symbolic manipulation program, which has
access to the syntaxic structure of such a definition. But symbolic differentiation is quite costly, and if we need
just numerical derivatives at a given point, we can compute them easily, efficiently, and precisely, by performing
this “symbolic” (in fact: semi-numeric, i.e. working on numbers and some data structures containing numbers
(vectors, polynomials, etc., but no symbolic names) transformation directly during the numerical evaluation of
expressions in a program. This is AD: Automatic Differentiation, for which there exist several implementation
56
9.2 What is Automatic Differentiation
57
techniques, this is an established technology. We shall exploit the possibility to overload arithmetic operations
in Clean to some specific compound data which represent numerical expressions together with their derivatives.
For simplicity we treat scalar, one independent variable first, and discuss the generalizations (and one
important simplification) afterwards. Imagine that we define the following data, structurally equivalent to a
simple, homogeneous list:
:: Dif a = Cst a | Dif a (Dif a)
where the type a typically is Real (but we keep it generic, it may be Complex, etc.) We define also the class of
polymorphic differentiation functions
class dif a :: a -> a
instance dif Real
where
dif _ = zero
etc., which transform expressions into expressions, following the rules of differential calculus. A word of
warning! The function dif does not take a functional object as its argument, but just a “generalized
numeric” expression, and produces another object of that kind. If this is a (here: real) number, its
derivative vanishes identically. In order to produce something non-trivial we must generalize numbers
to non-trivial instances of a differential algebra. The datatype Dif a defines such an instance, the derivative
is just the second element of the tuple. More concretely:
instance dif (Dif a) | zero a
where
dif (Cst _) = zero
dif (Dif _ p) = p
We see thus that in order to convert a standard numerical program into an augmented, differentiating procedure,
all numeric constants c whose derivatives are identically zero, should be lifted to Cst c. This cannot be done
automatically in Clean (in Haskell: yes); the user should code: fromReal 2.0 * sin x + fromInt
1. . . , etc. The instances of fromInt, fromReal are appropriately overloaded in the module CTMath.
Such typical constants as zero, one, two, half. . . are overloaded as well.
A variable x e.g., the argument of a differentiated function, has its derivative equal to 1, so the number x
should be lifted to Dif x (Cst one).
In several AD packages the data structure used is simpler: the “main” value, and the first derivative. But in
this case the function dif — as defined above — cannot be applied, since the derivative of a tuple is a number,
the typing is not respected. Using lists we can define a closed algebra, an algebraically complete domain, but
which requires a lazy language for its implementation, since for most cases the chain of derivatives is infinite
(it truncates only for polynomials) [19]. We can define thus
instance + (Dif a) | + a where
(+) (Cst x) (Cst y) = Cst (x+y)
(+) (Cst x) (Dif y p) = Dif (x+y) p
(+) (Dif x p) (Cst y) = Dif (x+y) p
(+) (Dif x p) (Dif y q) = Dif (x+y) (p+q)
instance ~ (Dif a) | ~ a where
~ (Cst x) = Cst (~x)
~ (Dif x p) = Dif (~x) (~p)
// Now omit ALL Cst instances; trivial
9.3 Generalizations and simplifications
58
instance * (Dif a) | +,* a where
(*) (Cst x) (Dif y p) = Dif (x*y) (x*>p)
(*) (Dif y p) (Cst x) = Dif (x*y) (x*>p)
(*) e=:(Dif x p) f=:(Dif y q) = Dif (x*y) (p*f+q*e)
instance / (Dif a) | one,+,*,/,-,~ a where
(/) (Dif x p) f=:(Cst y) = Dif (x/y) (p/f)
(/) (Cst x) (Dif y p) = let ip = Dif (one/y) (~p*ip*ip)
in x*>ip
(/) e=:(Dif x p) f=:(Dif y q) = Dif (x/y) (p/f - e*q/(f*f))
instance exp (Dif a) | exp,*,+ a where
exp (Dif x p) = let r = Dif (exp x) (p*r)
in r
instance ln (Dif a) | ln,one,+,*,/,-,~ a where
ln e=:(Dif x p) = Dif (ln x) (p/e)
instance sqrt (Dif a) | sqrt,one,+,*,/,-,~,fromReal a
where
sqrt e=:(Dif x p) = let r = Dif (sqrt x) (fromReal 0.5*p/r)
in r
etc. The module CTMath defines the trigonometric functions, atan, and some others. In case of need it is
straightforward to define the Bessel, or other special functions defined recurrently, even those functions which
uses their own derivatives in the recurrence equations! We see that we usually don’t even need the function
dif – we apply the normal arithmetic and we get “for free” all the derivatives hidden in deeper elements of the
Dif structure. Of course, nothing is really free, but Clean is a lazy language, and those higher derivatives are
not created physically, unless you need them. Still, they occupy the memory as unevaluated thunks.
9.3 Generalizations and simplifications
We will need both.
• We need gradients, i.e. derivatives in multi-dimensional space.
• In almost all texturing applications having the possibility to compute arbitrarily high derivatives is useless. We need over all just gradients. If we need — very seldom — second derivatives, we can always
use the double embedding: Dif (Dif Real). The general lazy package is much less efficient in this
case, it clogs the memory.
Clastic defines thus the datatype: :: Dv2 a = Dv2 !a !(Vec2 a) of expressions containing a scalar
value, and its 2-dimensional gradient. (In the current version there is no 3-Dim. version of it simply because
we didn’t need it; it can be added in 10 minutes.) This variant of AD has been used to define the contour
function. The gradient is a normal, numeric vector, it is not possible to differentiate it twice if the type a is
specified as Real.
If the arithmetic is lifted correctly, the scalar products and all the vector algebra follow. If the functions to
be differentiated are defined generically, i.e., if all numeric constants are lifted by fromReal, the only thing
which remains is to apply a function not to p=:(V2 x y), but to mkdvec2 p, where this lifter is defined as
mkdvec2 (V2 x y) = V2 (Dv2 x (V2 one zero)) (Dv2 y (V2 zero one))
Then we can retrieve the numerical gradient just by taking the second field of the Dv2 structure.
9.3 Generalizations and simplifications
9.3.1
59
A simple example
Fig. 9.1 at the left shows an ordinary, scalar noise. Nothing really fascinating, moreover just by looking at
it it is difficult to say whether it has or has not some ugly artefacts coming from the fact that it is glued from
interpolated pieces.
Fig. 9.1: Noise function and the absolute value of its gradient
At the right we see the absolute value of the gradient of this noise, with pure cyan attributed to zero. We
might suspect that the critical lines, where the gradient vanishes are perhaps too long, and that some discrete
grid features remain, although we worked hard to prevent discontinuities of the noise field. These examples
and those below may be found in Examples/example6.icl.
Fig. 9.2: Gradient and curl of a noise, visualized through LIC
The left picture on Fig. 9.2 shows the same gradient, but visualized with the aid of the most classical (and
awfully slow) Linear Integral Convolution algorithm, with constant kernel. The LIC code is in the module
CTMath, don’t worry, it is just 10 lines. . . The contrast of these pictures had to be enhanced a little bit. We
see that our noise is not perfect, as everybody would suspect anyway. So, just for your visual pleasure we have
reused the same noise treated not as a scalar, but as the only non-vanishing axial (“z”) component of a vector
potential (ask some physicist what is it), and we visualized the curl of it. It shows some nice vorticity pattern.
This example in not “serious”, since we had used rather primitive methods to compute the LIC contributions,
9.3 Generalizations and simplifications
60
but it is here just to show that the methods advocated here might be useful for the visualization of vectorial
scientific data, starting with some potential functions.
Appendix A
System requirements
The package works (has been tested) under Clean 1.3, Windows Nt/2000 version. Memory: 64M or more. One
minor modification of the standard IO package has been made. The file osbitmap.dcl which exports the
OSBitmap structure as an opaque, abstract entity, now unveils its contents, copied from the .icl module:
:: OSBitmap
= {originalSize
reSize
bitmapContents
bitmapHandle
}
::
::
::
::
!(!Int,!Int)
!(!Int,!Int)
!{#Char}
!Int
//
//
//
//
The size of the bitmap
We never used this.
This we badly need! (J.K.)
The handle...
We are aware that in a sense we are violating the integrity of the standard library, but we have no remorses.
Bitmap handling in the actual version of the IO subsystem is hopelessly poor, and we are convinced that in
order to develop it, either some system-dependent details must be accessible, or more powerful interfacing
functions implemented.
The modified osbitmap.dcl is included in the Extras folder. Put it into the Object IO/OS Windows
folder. It shouldn’t harm anything unless the I/O library authors decide one day to modify the Bitmap structure;
then it should be straightforward to correct everything.
The Windows interface uses the Notice sub-package, defined in Object IO examples, gui utilities,
and also in the tutorial examples of Peter Achten. We put a copy of it in the folder Extras.
61
Appendix B
Further work and wishes
Oh, plenty of things to do, if the package is to be really used for concrete work. Of course, a more complete
set of examples is needed. In particular we would like to code in Clean a dozen or so of typical RenderMan
examples we used for our teaching.
But some mathematical, structural and interfacing utilities are needed as well. The list below should be
shorter in the next versions, provided they will ever appear. . . Some of the ideas below are for a straightforward
implementation, and others are dreams and explosions of wishful thinking, put here in order not to forget them.
B.1
Optimization
The package and this tutorial must be completed and corrected. In particular what is missing is a serious
work on optimization. The actual version is rather slow. No caching of repetitive structures, almost no
macros/inlining, and heavy load on the garbage collector, since Clastic routines produce a good deal of intermediate datastructures (mainly vectors). Next version might keep vectors at the user layer, but the primitives
will most probably streamline the acces to compound data, passing x and y separately. Chains of functions
may pass them as multiple values using CPS (provided we find that such style in Clean is implemented in a
sufficiently efficient way. . . ).
We should analyze better the strictness declarations, and the usage of unique objects. (But — frankly —
we have no idea whether the presence or absence of attributes within the declarations change anything.) Some
nice higher-order constructs within the basic layer should be simplified.
One day we have to think seriously about rewriting Clastic in Haskell. Apart from the utilitary viewpoint,
this would be a really excellent exercice in type system changing and monadisation. Any takers?
B.2
Redaction
Ugh, rewrite all that! Add serious index. Gather and add all useful references, on texturing in general, random
noise, tesselations, etc. The actual references are very incomplete.
Perhaps one day we will collect also all our exercises and include them here as well.
B.3
Interfacing
1. A progress gauge is badly needed. The actual interface may be called a lot of names, but never “concurrent”. . . Actually, Clean programs under Windows are rather unfriendly, blocking the screen refresh,
etc.
62
B.4 Base layer
63
2. We need more parameterization, the possibility to change some texture attributes without recompilation,
and this is a prioritary upgrade. It would be nice, and not too difficult to define some data structures
representing standard blocks, to write a parser for simple arithmetic and colour expressions, and permit
to assemble the textures dynamically under the interface control. (This is a student project awaiting a
candidate. . . Any volunteers?)
3. Clastic is not, and never will be an image-processing program, nor a fully-fledged visualizer, but some
utilities such as post-filtering, colour shifts (e.g. gamma correction), and zooming would be quite useful.
4. A parametrizable way of generating several bitmap files from one generator, i.e., creating animations
(perhaps MNGs, or just a set of images to be treated off-line by a MPEG encoder) seems interesting, and
not too difficult.
B.4
Base layer
1. We have to review seriously the manipulation of the alpha channel, transparency masks, etc. Add primitive combinators quite popular in image processing packages, such as mixing semi-transparent objects,
trasferring only the hue, or only the luminance, etc. We didn’t do it, because we use just simple arithmetic
operations, but for a real user some shortcuts may be necessary.
2. We didn’t touch the problem of anti-aliasing seriously, when spatial frequencies of texture patterns are of
the order of pixel size. Then some low-pass filtering should be used. Actually shrinking a bitmap texture
is simply awful, but problems of that kind arise also in procedural objects.
3. Several function, especially the tesselators, still contain many cascading conditionals. Some more elaborate periodic functions are needed.
B.5
Bitmap handling
1. Loading and saving JPEG and PNG files. This would be enough, no need really to output/input anything
else. Full PNG might be rather difficult, must use public libraries. (This is another student project waiting
for a victim).
2. Some standard bitmap operations such as low-pass filtering (for down-resizing). Some other filters much
more efficient directly on the pixel space, without passing first to the functional layer (waste of time for
the interpolation). Colour separation/management primitives.
3. Use bitmaps to cache replicated patters, useful for complex tesselations.
4. Some suggested, but not implemented manipulations (such as the classical algorithms for the halftoning
of colour images) should be there.
B.6
Random stuff
1. Most probably we shall construct some faster noise functions, based on the original Perlin’s idea: tabulate
first a set of random numbers assigned to the lattice (integer coordinates). Retrieving them should be
faster than our ergodic functions.
2. Implement the simplectic noise suggested by Perlin (and never seen working).
B.7 Mappings and deformations
64
3. Some non-linear transformations producing random distributions with different spectral properties will
be needed one day. Poisson noise in 2 and 3D; Gaussian bumps, etc. In RenderMan shaders there is
plenty of them, but badly documented (if at all), and thrown-in ad hoc, for specific purposes.
4. Implement multi-frequence noise by spectral synthesis (using some base functions, e.g., trigonometric,
with random phases and coefficients). This should produce more regular functions than those generated
by piece-wise interpolations.
5. Generalize the “bombing” algorithm, permitting to render fuzzy objects, which may interact (another
way of making smoke, etc.; may be difficult.)
B.7
Mappings and deformations
In general, more examples are needed.
1. Work on hyperbolic projections, fish-eye camera, etc.
2. Add a torus to the 3D panoply.
B.8
Non-local texturing procedures
This is a highly speculative part of our dreams, but perhaps useful. A texture viewed as just a local function
from points to colours often does not correspond to a physical process which generates a realistic pattern.
1. Implement a differential equation solver and code some reaction-diffusion patterns. Greg Turk patterns
are really very nice [22].
2. Optimize the LIC (Linear Integral Convolution) algorithm, in order to visualize vector fields. Implement
the deformed spot noise for the same purpose, it should be much faster [24].
3. Learn how to make textures using Voronoi cells, and how to implement them efficiently within our
framework.
B.9
Tesselation package
1. Complete the wallpaper package, implement all the groups. Implement also the “rosette” and “frieze”
subgroups (rotations and reflections only; 1-dim translations only).
2. The reduction procedure should transmit also the information about the “cell number”, the integral part
of the coordinates, and also identify the reduced FR instance; this will enable the changing of colour or
other attributes from one cell to another. We did it with the reptiles, but this was a solution ad hoc.
This is really important: a parquet where all wooden tiles are identical is bad. So, the random texture
generator for one tile migh use the cell number in order to change something (add an offset, vary the
colour a little bit, etc.)
3. Work a bit more on generic weaving algorithms, there is plenty of nice solutions to discover; octogonal
and hex lattices of threads, etc., found in Islamic or Celtic geometric decorations.
B.10 Differential stuff
B.10
65
Differential stuff
1. Complete the 3D differentiation procedures.
2. Use AD that to construct blending functions for contours; to re-parametrize deformations making them
area-preserving (a non-trivial problem).
3. Generalize the bump-mapping, include specular reflections.
B.11
General utilities
1. Code finally the 2-dim Fast Fourier stuff, and some image convolution/filtering procedures.
2. Something which is quite far from Clastic as it is now, but a project which started already (as a pedagogical assignment): implement the pyramidal (Heeger et al.; perhaps some other, based on wavelets?)
algoritm [23, 25] for the analysis, and re-synthesis of “natural” textures.
All other suggestions are welcome. Thank you for your interest.
Appendix C
List of Useful Data Structures and
Procedures
C.1
Introduction
Even if the current version of the package is a study, and much work is still needed, Clastic contains more than
300 functions (and class instances), not counting some composite data types, and numerous numeric and colour
constants. The list below replaces a decent documentation, and may be useful, but it is not very well structured,
nor complete. Please, look the .dcl files. In fact, most definitions are short, and the best way to learn them is
just to look at them. So, the list below should guide you rather than supply the full truth. This text is a tutorial
rather than a formalistic manual. You should learn more from the text and from the examples.
C.2
General utilities
Many functions here are really trivial, but they are used in examples, so we acknowledge their existence.
• There are some abbreviations we needed, e.g. fR for fromReal.
• A large collection of numeric constants. Pi= π, Dpi= 2π,√PiH= π/2, PiT=
√ 3π/2, Pi3=
√ π/3, Pi4=
π/4,
Pi6=
π/6,
PiD=
2π/3,
PiA=
4π/3,
Sqrt2=
2,
Sqrt3=
3,
Sqrt5=
5, SqrtH=
p
1/2, and degrees= π/180. More constants, overloading commonly used numbers: zero, one,
two, three,four, half, onethird, onequart.
• The definition of a generic Functor (fmap) class, and a generic constructor class fzip permitting to
combine different structures element-wise.
• The class Show as in Haskell, and its instances for typical data: numbers, lists, tuples, Booleans, Maybes,
vectors, etc. Contains the function show essentially equivalent to toString, and ShowS, which takes
two arguments, the conversion of the first is concatenated with the second (this avoids the concatenation
in longer print chains). Not really used in Clastic, but useful for testing.
• Auxiliary numeric functions: sqr, cube (rather silly. . . ), and signum which returns overloaded
zero, one, or one. Also: dsgn returning ±1 depending on the argument parity (even is 1). Such
real functions as floor, nearest, and frac returning what they should: nearest lower integer, nearest integer, and fractional part, and gfloor and gfrac — polymorphic, applicable to, and returning
generalized numbers, e.g. differential entities.
• Plenty of related functions splitting number in their integer and fractional parts: entfr, entfrac,
isplit, rsplit, positive modulo pmod (standard mod is equal to rem, which seems to be a bug in
66
C.3 Function algebra
67
Clean). Functions which compute the maximum, minimum or both for 3 arguments or lists of arguments:
min3, max3, minmax3, minmax.
• Other real functions, such as atan2 y x — the arctangent which yields the angle (in [0 – 2π]) of
a point in polar system. The 0 – 1 interpolator hermite x which returns 3x2 − 2x3 , a 5-th order
interpolator inpol5: 10x3 − 15x4 + 6x5 . The Catmull-Rom spline spline a−1 a0 a1 a2 x returning
a cubic in x which goes from a0 to a1 when x in [0 – 1]. A masking function cmask c u v returning
u if c is different from zero, v otherwise. An angle normalizator computing this angle mod 2π.
• Some list utilities, such as cycle repeating infinitely a list and cyclst — a function generating a
cyclic indexing function for a finite list; more economic than cycle, used in weaving procedures.
Element-wise sum, difference, product and quotient of two lists. Forward-difference generator: (∆u)l =
ul+1 − ul .
Also: map2 for lists of lists, and an interval generator interv a b n, splitting the interval [a – b]
into n fragments.
• Some generic vector procedures, applicable to 2- and 3-dimensional vectors, but also to colours, in fact to
any structures which are vector-like. Aritmetic constructor classes: <+>, <->, which are there because
we don’t have genuine multi-parametric classes, a generic scalar product constructor class <.>, the
overloaded: norm and norm2 (norm squared), and the function normalize. Projections — parallel
and perpendicular — of vectors on normalized axes: axproj and perp (very simple, actually they
should be always inlined).
• Some linear and non-linear object interpolators: lint for numbers and vectors, cmix for functions,
etc. A universal parameterised interpolator interpol, permitting to choose the interpolation method
among Floor, Nearest, Linear, Hermite, Spline etc.
• Some auxiliary functions useful for treating BMP images ({#Char} tables): splitting an integer into
bytes, its reconstruction.
C.3
Function algebra
• Arithmetic is lifted to functional domain, concretely such (and related) instances are defined:
instance + (a -> b) | + b
where
(+) f g = \x -> f x + g x
...
instance / (a -> b) | / b
where
(/) f g = \x -> f x / g x
instance ~ (a -> b) | ~ b
where
~ f = \x -> ~(f x)
instance zero (a->b) | zero b
where
zero = \_ -> zero
instance one (a->b) | one b
where
one = \_ -> one
instance two (a->b) | two b
C.4 Colours
68
where
two = \_ -> two
...
instance abs (a->b) | abs b
where
abs f = \x -> abs (f x)
instance fromReal (a->b) | fromReal b
where
fromReal c = \_ -> fromReal c
etc., etc. There was no point in lifting unary operations, such as exp, etc. use just the composition.
• Functions minf, maxf which lift min, max to functional domain (the original could not be overloaded, minf returns a function which yields a Boolean, the type is incompatible). The function posit
f which truncates to zero if f x is negative.
• General, domain independent transformers of functions, such as translated a f which returns
\x -> f (x-a), the function scaled etc. All this may be used for scalars or any vectors. More specific (vectorial) transformations are in a different module. here we have generic transformers transf
and displace, discussed in the text.
• Replicators such as train f which applies gfrac to its argument. Clamping function: clamp which
limits the range to [a – b], and ramp to: [0 – 1]. The filtering function fmask which lifts cmask to
functions.
C.4
Colours
• Colours are records :: RealCol a = {tr::a,tg::a,tb::a} with a usually real, between 0
and 1, but we keep this generic, in order to be able to compute gradients in colour space using AD (no
example implemented yet). Colours form a vector space with all overloading you may need: zero,
adding, multiplying by scalars (instance of *>, equality, etc. Functions convertCol and iconvCol
convert between real colours and standard 3-byte RGBs.
• There is plenty of colour constants: RWhite, RRed, RBlue, RDarkGreen, etc., and constant
colour functions (textures): CRed = const RRed etc. The function saturmul saturates multiplicatively a colour, dividing all components by the greates one (unless it is black, of course); lighten
shifts up a colour, adding enough white to make one (or more) components equal to 1.
• There is a simple luminance calculator: L = 0.222R+0.707G+0.071B, and the converters rgbtohsv
and hsvtorgb between real RGB colours and the Hue-Saturation-Value space.
C.5
Vectors
• The basic definitions:
:: Vec3 a = V3 !a !a !a
:: RVec3 :== Vec3 Real
:: Vec2 a = V2 !a !a
:: RVec2 :== Vec2 !Real
C.6 Basic generators and transformers
69
for 2- and 3-dimensional vectors.
• Reasonably complete vector algebra. Addition, scalar product <.>, vector product in 2: skew, and 3
dimensions: /\; the skew product in two dimensions yields a scalar. Multiplication of a vector by its
base element (usually a number): *>. Division by a scalar: >/. Instances of norm and norm2.
• Constants (axes): X3axis, Y3axis, Z3axis, X2axis,Y2axis. Of course X3axis = V3 1
0 0, etc.
• Functions tospher and fromspher, transforming between Cartesian (x, y, z), and spherical (r, ϑ, ϕ)
coordinates. Rotations in 3D: rot3 cs co si n v, rotating v about the (normalized) axis n, by an
angle whose sine and cosine are given; rot3 parametrized by the angle. Function findrot n v
which finds the axis and the angle (its cosine and sine) of a minimal rotation which transforms vinto a
vector parallel to n.
• Some specific manipulations in 2D. Function skew computes the skew product. decomp2 u v p
decomposes the vector p~ into contravariant coordinates (u, v) along the axes ~u and ~v : p~ = u~u + v~v .
Became obsolete after the introduction of general tiling tools.
• Rotations in 2D: rot2cs, rot2. Mirroring: refl p0 u p reflects the point p~ in the line ~x =
p~0 + t~u.
• A solver of 2 linear equations using vector notation. Nothing original. . . Also, two procedures: fgrad3
eps f p and fgrad2 eps f p which compute gradients of f at a given point, using “cheap” finite
differences with the step equal to . This was coded originally to test the AD package, but we left that,
since it is faster than the automatic differentiation, and works quite well.
• A function fbumpshade which should be defined elsewhere, which computes the lighting of a height
field by finding its normal, and comparing with the “light” direction. Diffuse reflection only for the
moment.
C.6
Basic generators and transformers
This is the “main stuff”, but as you will see, the actual functions are really simple.
• Typical transformers of textures (not of points): rotated angle, also rotated cs co si parameterized by the cosine and the sine, useful if angle doesn’t change. Some related functions: rotated90, rotated45, rotated60, and transposed. Some others, which reduce x and/or
y using the fractional part of the arguments: reduced, symreduced, redscaled which combines the reduction and scaling, etc. Combined transformers such as repliq s = scaled s o
symreduced. Also rotatedabout p0 angle rotating a texture around an arbitrary point.
• Some transformers in 3D: rotated3, rotated3 cs parameterized by the angle and the axis of
rotation.
• Several primitive generators discussed already: step, pulse, parameterized xstep (shifted step),
xpulse. Replicators: trainpulse, symtrain, and their instances: symteeth = symtrain
abs, etc. Others: filteredabs, filteredpulse, smoothstep, etc. The function hypulse sharp from one side, smooth from the other, useful for weaving.
• unitdisk, and disk parameterized. Band generators: xstripe, ystripe from 1-coordinate
functions. Two checkerboards: checkers, chboard. Regular polygon generator, and a regular
star regstar. Also: triangle parametrized by the three corners. We defined also chboard3 with
checkerboard cubic pattern in 3D.
C.7 Mappings and deformations
C.7
70
Mappings and deformations
This module will grow, for the moment it contains just some simple examples. The generic higher-order
transformers transf, and displace are coded in CTFunalg.
• Higher-order transformers: transf which applies a vector transformer to a texture; its variant displace shifts a texture by a given amount. The funtion trfshift v tfun p = v + tfun (pv) which generates shifted transformations of a texture, and a translated deformer trftranslate
p0 which uses the former. Also: trfturn, a functional which rotates deformers.
• Some specific deformation examples: whirl, vortex, lense.
• 3D mappings: functions which take a ViewScreen, object containing the camera and the ViewPoint, and
construct intersections in 3D of a ray, and a surface; the result returned is a boolean (True if intersection
exists), the intersection point, the normal, and the relative vision vector, which may be useful to compute
specular lighting. Three such mapping are defined now: spherepoint, cubepoint, and cylinderpoint. OK, there is another one: proj plane, but different and much simpler, it finds simply
the intersection of a ray and a plane.
• The function bumped which “bumps” one texture with another one; parametrized by the light position
and the normal (computed elsewhere).
C.8
Random stuff
• Some simple classical (congruential) random number generators. rand0, rand1 which act on an
integer seed and produce a tuple: (aReal,newSeed), with aReal in [0 – 1]. Two of them with different
parameters in case you want to be sure not to generate spurious correlations.
Four more: rand0 s, rand1 s with result symmetric between -1 and 1, and rand0 h, rand1 h
returning numbers in [−0.5 – 0.5]. Yes, anybody can define them, and the sense of putting in a library
several similar instances is dubious, but we needed them, and the philosophy to leave in the library all
functions used was applied. . .
Also, a simple Gaussian generator (two exemplars) randn0, randn1 which exploits the Central
Limit Theorem: adds 12 instances of rand0 h (or rand1 h), producing a random value in [−6 –
6], with mean zero, and variance 1. Almost a Gaussian, as everybody knows.
• Our main engine: ergodic, a pure function Int -> Real with “crazy” behaviour, its value for any
argument n is so uncorrelated with n, that it can be used as a random number between 0 and 1. Also:
ergodics, with result between -1 and 1.
• Functions ergxy, ergxyz generating random values from pairs or triples of integer numbers. ergvecxy
ix iy a b generating a random vector, and returning its scalar product with (a, b).
• Main real noise functions: grnoise2, gennoise2 (the last one is more general) symmetric around
zero, noise2 with positive values, grnoise3, vecnoise2, vecnoise3 generating random vectors, etc.
• Functionals fractsum2, fractsum3 summing contribution with various frequencies. Also, more
general frsum, very generic, which subsumes the former; heavily overloaded.
• Function fractnoise2 summing scalar 2-dim noise. turbulence2, turbulence3 — summing the abs. values of multifrequence (scalar!) noise in 2- and 3 dimensions. In order to sum vector
noise, use frsum. If the co-domain of the noise is not scalar, it is difficult to distinguish turbulence and
“fractal noise”.
C.9 Tesselation utilities
C.9
71
Tesselation utilities
This is a very incomplete, evolving package.
• We define two more normalized vector constants T2axis and U2axis forming angles 60◦ and 120◦
degrees with X2axis, useful for triangular and hexagonal lattices.
• A generic composite data structure UnitCell (created by mkunitcell) contains the coordinate conversion and reduction/reconstruction procedures, the norms of axes, the (fixed) angles between them, the
definition of covariant coordinates, etc.
• The function hextex generates an arbitrary hexagonal P1 lattice, and it paramerized by the unit cell,
and by two point inside the cell which define the vertices of the hexagon (shared by 4 neighbouring UC).
This function is used by regular hex, and brickwall in order to produce textures shown in the
text.
• The function p3reduce reduces the point to the FR of the P3 group, it doesnt move the Motif.
C.10
More math
• Actually we have here only the Automatic Differentiation package, Complex and Rational numbers
(overloaded arithmetic; some textures based on relation between complex numbers are very nice), two
applications of the AD subsystem: contour and bumpshade, and a short function lic: generator of
a texture which visualizes a vector field, using the Linear Integral Convolution.
C.11
More examples
The module CTExamples contains some functions which belong elsewhere, and they are here only because
we want finally to release the package and this tutorial.
• The veawing package, in particular the function weave, the setts for the tartan patterns etc. are here.
• Some random “goodies”: truchet, dithering and “bombing” generators as well.
• Functions warp, warpfold use a simple equation solver in order to propagate a local warp along any
trajectory.
• There are some 3D examples: projecting an image on a spherical surface, construction of a planet from
its surfacic and height (bump) maps, etc.
• Finally, some tesselation examples: Escher reptiles, a kaleidoscope generator, and a PG/PGG parquet.
C.12
Viewing routines
Here we describe all the stuff which deals with the “virtual” display, i.e., the manipulation of the intrinsic
texture space and its conversion to pixels, and also the Windows iterfacing routines.
• Definition of the basic types: Texture a which may be a tagged function from a 2D point into
RColours, or a 3D texture, needing the point, the normal, and the line of vision.
• The definition of Light. For the moment only an isotropic point source is used. It is created using
mklight.
C.12 Viewing routines
72
• The Viewport. Contains the parameters of the texturing zone, and defines the conversion procedures
from virtual to pixel space (and vice-versa). The function mkviewport creates one. Another function:
bmapport creates a fake viewport attached to a bitmap. The user specifies xmin and xmax. The
bitmap reader makes available the width and the height, and all this is used to construct the rest, so that
the aspect ratio is preserved.
• Bitmap handling procedures, readDib file and readBmp filename world, which read bitmap
files and construct texture generators. The function writeDib saves a texture stored as a DiB array.
makeDib creates this array, uses setPixel to index it. The function makedib is the principal work
horse, which iterates over all pixels of the texture zone. The function getPixel retrieves a pixel
from a bitmap array, and bmapToTx performs the actual conversion between a DiB, and our “standard”
functional texture. There is also makebmaptx paramterized by the file name, etc. Makes it possible to
tile directly the bitmap in one or two dimensions.
• The definition of Camera, with its position, look at, and up vectors, and its focal parameter. Also,
the definition of the ViewScreen structure which contains a camera, a list of lights, and a generic ray
specification. It is created by mkview, and mkvscreen.
• A few additional functions such as addlights, diffuselight, lightview — which facilitate the construction of various lighting conditions, and solidtx — the generic renderer of 3D-textures.
• We pass to windowing procedures, which actually work under Windows only. There are actually two
simple interfaces, one primitive SDI, and another MDI, more elaborate. We cannot describe the details
here. The SDI interface demands firs the construction of a viwport which precise the size of the texture;
the function mkvport is used. Then the function texturwin takes the viewport as it first parameter,
and constructs a window. It launches mkportwin which attaches the viewport to the window, and
passes to it the “renderer”, the WindowLook function which iterates over all the pixels of the texture,
using makedib.
• The MDI interface applies to the World the function Render = txdialog textureset, where
textureset returns the world paired with a list of pairs: (title,Texture). The function txdialog
starts the wholme business. allocates some ids, initializes the list of windows, the menus, etc. All is
essentially straightforward and easy to manipulate, provided you decide first to learn some intricacies of
the Clean IO system. . . .
Bibliography
[1] David S. Ebert, F. Kenton Musgrave, Darwyn Peachey, Ken Perlin, Steven Worley, Texturing and Modeling. A Procedural Approach, AP Professional, (1998).
[2] Bryan et Lisa Gibson-Winge, Texture Synthesis, www.threedgraphics.com/texsynth .
[3] John C. Hart, Procedural Texturing, Web course, available from the site
graphics.eecs.wsu.edu/cpts548/procedural/sld0001.htm .
[4] Steve Upstill, The RenderMan Companion: A Programmer’s Guide to Realistic Computer Graphics,
Addison-Wesley, (1990).
[5] Larry Gritz, Blue Moon Rendering Tools, Exluna Inc., www.exluna.com/bmrt/ .
[6] Web site www.pixar.com .
[7] Rinus Plasmaijer, Marko van Eekelen, Concurrent Clean Language Report, Version 1.3, HILT B.V. and
University of Nijmegen, (1998). See also www.cs.kun.nl/~clean .
[8] Spencer Kimball, Peter Mattis, GIMP, www.gimp.org .
[9] Sean Gibb, Peter Graumann, SynTex,
www.SyntheticRealms.com .
Synthetic
Realms,
Calgary,
Canada.
Web
site
[10] Conal Elliott, Functional Images, research.microsoft.com/~conal/Pan with references,
plenty of additional documentation and examples.
[11] Pedagoguery Software Inc., GrafEq, www.peda.com/grafeq .
[12] Jules Bloomenthal (ed.), Introduction to Implicit Surfaces, Morgan Kaufmann, (1997).
[13] Ken Perlin, www.noisemachine.com , see also mrl.nyu.edu/perlin .
[14] Hugo Elias, tutorial, freespace.virgin.net/hugo.elias .
[15] POV-Team, Persistence of Vision Ray-Tracer,
www.povray.org .
Version 3.1 User’s Documentation (1999),
[16] Doris Schattschneider, The Plane Symmetry Groups: Their recognition and notation, American Math.
Monthly. 85, pp. 439–450, (1978).
[17] Xah Lee, The Discontinuous Groups of Rotation and Translation in the Plane, Web pages
www.best.com/~xah/. This site contains a comprehensive overview of literature.
[18] David E. Joyce, Wallpaper Groups (Plane
aleph0.clarku.edu/~djoyce/home.html .
73
Symmetry
Groups),
tutorial.
Web
site
BIBLIOGRAPHY
74
[19] J. Karczmarczuk, Functional Differentiation of Computer Programs, Journal of Higher Order and Symbolic Computing 14, (2001), pp. 35–57.
[20] George Wolberg, Digital Image Warping, IEEE Comp. Soc. Press, (1990).
[21] A.V. Shubnikov, V.A. Koptsik, Symmetry in Science and Art, Plenum Press, (1974).
[22] Greg Turk, Generating Textures on Arbitrary Surfaces Using Reaction-Diffusion.
[23] Li-Yi Wei, Marc Levoy, Fast Texture Synthesis using Tree-structured Vector Quantization, see
http://graphics.stanford.edu/projects/texture/.
[24] Willem C. de Leeuw, Jarke J. van Wijk, Enhanced Spot Noise for Vector Field Visualization.
[25] Robert Brumley, Wavelets for Texture Analysis.