Functional Approach to Texture Generation

 


 

Introduction

This is the summary of the talk given by Jerzy Karczmarczuk at PADL02 in January 2002 in Portland, OR. The talk presented Clastic, a system for generation of procedural textures.

It is common to operate on matrices and pixelmaps by reading and writing the (i,j)-th element or pixel. There is, however, a dual approach: rather than manipulate pixels we manipulate functions that manipulate pixels. The result is surprisingly concise and lucid, let alone coordinate- and storage-independent.

The pure functional library of texture generators has been used in teaching, of functional programming and of computer graphics. The Clastic web site and the tutorial show many intricate and exquisite textures, described declaratively, combinatorially -- often by an order of magnitude shorter than in the traditional, imperative, pixel-shoving approach. Furthermore, the sequences of combinators can be subjected to automated deforestation based on fusion laws.

The coordinate-free way of describing textured objects reminds one of the coordinate-free formalism of Geometric Algebra and of the General Covariance principle of Relativity (or, the General Coordinate Invariance principle).

Clastic uses programming version of Clean for Windows OS. The examples below are paraphrased in Haskell.

Version

The current version is 1.0, Jan 2002

References

This summary was posted, with a comment, as Re: Not quite enough abstraction on the SRFI-25 mailing list on Mon, 28 Jan 2002 21:10:10 -0800 (PST). That SRFI-25 thread includes Brad Lucier's message urging more abstraction in programming with vectors and matrices.

<http://users.info.unicaen.fr/~karczma/Work/Clastic_distr/clastic.html>
The Clastic Web site (what used to be. Please use Archive.org) It refers to the tutorial with many beautiful pictures.

 

Active and Passive raster graphics algorithms

Raster graphics algorithms can be partitioned into ``active'' and ``passive''. Active algorithms take a pixelmap and actively modify it: they traverse the pixelmap or a subset of it -- often in complex ways -- and examine and set pixel values. Bresenham line drawing and contour filling algorithms belong to that class. Passive algorithms on the other hand do not by themselves draw anything. They are expressed as a function f(x,y). A rendering engine passes the function coordinates of a point and expects in return the color value at that point. The values of x and y of two consecutive invocations of f(x,y) are generally unpredictable. The function f(x,y) does not have access to a pixelmap and cannot examine pixel values. Texture mapping algorithms belong to the passive category. Such algorithms -- shaders -- are used in animation and ray tracing pipelines.

Clastic is an exploratory tool for passive raster graphics. The tool can generate regular (geometric) or random textures described as relationships between 2D points and their colors. In Clastic, a texture is a function R²→R³: a function from points to RGB color vectors. The relationship is described purely declaratively.

 

Example of a texture description

In Clastic, a texture function can be specified in a low-level way, for example
    circle (x,y) = if x*x + y*y <= 1.0 then tx (x,y) else rgb_white (x,y)
which creates a circle filled with the texture tx(x,y) on a white foreground. However, if we define a texture combinator
    fmask h f g = \x -> if (h x == 0.0) then g x else f x
and an overloaded function
    step x | x < zero = zero
           | x > zero = one
           | otherwise = half
then we can write the circle as
    circle = fmask (\p -> step (1.0 - norm2 p)) tx rgb_white
or even
    circle = fmask (step . (sone .- norm2)) tx rgb_white
where
    sone p = 1.0
    norm2 (x,y) = x*x+y*y
and .- is subtraction lifted to (Float,Float)->Float functions.

What is remarkable about the last circle definition is that point coordinates do not appear at all. The shader circle is defined as a pure combination of primitive textures. This coordinate-free style of programming is strongly encouraged by Clastic. In Clastic, we transform things rather than coordinates. The coordinates should be hidden inside higher-order operators such as translate, rotate, scale; inside primitive textures such as rgb_white; inside `soft objects' Point->Float such as sone; and inside blending combinators such as fmask and over.

As the paper and the Clastic tutorial show, combining a few primitives yields surprisingly complex textures such as impressive geometric patterns, tesselations and wallpaper including Escher reptiles, and woven patterns.

 

Randomness

Clastic uses purely functional, stateless random number generators (advocated by Ward). The generators are pure Integer->Float functions with the property that function values do not visibly correlate even for neighboring arguments. The ``random noise'' generators let Clastic produce dithering, fractal patterns (e.g., clouds), turbulence, and more complex marble-like textures and bump-maps.

 

Deformers

Finally, Clastic can apply transformers to other transformers: deformers. Examples include warping, lenses and random displacement to generate irregular wooden grain. The problem of inverting a transformation is generally rather complex. Still Clastic can deal with it. As the paper stresses, warping and deformation can be used abstractly, can be used generically and can be composed. This may make the coding by an order of magnitude shorter and easier than in the imperative approach.