POV-Ray SDL project

From HaskellWiki
Revision as of 15:17, 6 February 2021 by Gwern (talk | contribs) (Reverted edits by Tomjaguarpaw (talk) to last revision by MathematicalOrchid)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

I have an idea for an interesting project, and I'm wondering if I can get some useful input here...

Introduction

The Persistence of Vision Ray Tracer ('POV-Ray') is a freeware 3D rendering program. The source code and several pre-built executables can be downloaded from the official website at http://www.povray.org/. (Not to mention very extensive documentation.)

Unlike most 3D applications, POV-Ray does not come with any sort of modeller. Instead, the image to be rendered is described by a human-readable (and typically hand-written) text file written in the POV-Ray Scene Description Language ('SDL'). A trivial SDL file might look like this:

camera
{
  location <0, 0, -5>
  look_at <0, 0, 0>
}
light_source {<-2, +3, -5>, colour rgb 1}
object
{
  sphere {<0, 0, 0>, 2}
  texture
  {
    pigment {colour rgb <1, 0, 0>}
    finish
    {
      ambient 0
      diffuse 0.7
      specular 0.5
    }
  }
}

As POV-Ray has developed, the SDL has developed with it. However, as of the last few releases, the SDL has become very much more than a scene description language. It is in fact effectively a scene construction language now, since a few releases back it become a Turing-complete programming language.

This has heralded a new era in POV-Ray. Before it was 'just' a very powerful ray tracer. Now people routinely construct elaborate libraries of code for constructing scenes; fractal generators and physics simulations seem to be especially popular, and a number of people use POV-Ray for mathematical graphing.

Problem description

However, all is not well in paradise. You see, while the SDL's programming capabilities are both simple and fairly flexible, what they are not is expressive or efficient.

The SDL provides no way to define new datatypes. The only data you can handle is floating-point reals, character strings, various sizes of vectors (of floating-point reals) and arbitrary-size multi-dimensional arrays of any of the above.

Also, the SDL is not a very compute-efficient programming language. POV-Ray is really designed to be a high-performance renderer, and not a high-performance interpreter. It is not uncommon for some scenes to take longer to parse than to render. The 'programming' constructs in the SDL essentially work by injecting tokens into the scene parser. In particular, calling code in another source file causes that file to be repeatedly opened and parsed.

The idea

For many years now the authors of POV-Ray have been looking at implementing more powerful programming constructs in the SDL. Endless flamewars have raged over the subject of whether the SDL should become object-oriented.

What nobody wants is for the SDL to end up looking like this:

new Camera(new Vector3(0, 0, -5), new Vector3(0, 0, 0));
new LightSource(new Vector3(-2, +3, -5), Colour.White);
Finish f = new Finish(0, 0.7, 0.5);
Texture t = new Texture(Colour.Red, f);
Object o1 = new Object(new Sphere(new Vector3(0, 0, 0), 2), t);

I have personally written a ray tracer in Java, and the above is exactly the kind of thing you end up writing to construct test scenes to render. Urgh! Never again!!

On the other hand, Haskell is a very simple, elegant and powerful programming language. To make matters even more tantalising, the existing SDL is already almost valid Haskell source code! All you have to do is write a big tedious thing like this:

module POVRay where

data Shape =
  Plane {normal :: Vector3, distance :: Real} |
  Sphere {center :: Vector3, radius :: Real} |
  ...

data Finish = Finish {ambient :: Colour, diffuse :: Colour, ... }

data Texture = Texture {pigment :: Colour, finish :: Finish, ... }

Once the above is imported, most valid SDL files suddenly become valid Haskell!

The problems

Well... almost. There are one or two little glitches.

First of all, every object conceptually has a 'shape' property. This could be a plane, sphere, cylinder, etc. Do we make 'shape' a type or a class?

If we make it a class, then we instantly have problems. A sphere and a cylinder both have a 'radius'. Unless you're going to start writing ugly and tedious constructions like 'cylRadius' and 'sphRadius', you're going to hit a name-clash problem. On top of that, there are situations where you might very well want to process a list of shapes. If 'shape' is a class, all the elements of the list must be the exact same shape type.

Alternatively, we can make 'shape' a type. Then we avoid the name clash problem, and 'radius' will work as expected. Also, we can now construct a list of shapes containing all different kinds of shape. The problem now is the reverse: we might want to demand that a list contains only spheres, for example. There is now no way to force this though the type system.

On balance, shape = type seems to work best.

There's still a slight problem though. Some shapes have a 'radius'. However, spotlights also have a 'radius'. Some shapes have a 'normal'. But so too does the 'gradient' texture type. There isn't an obvious way to fix this. (Aside from making a class 'HasRadius' with a single method 'radius'. And likewise for every other clashing name...)

Also, in SDL you add things to the scene by just writing them out. To make the SDL into valid Haskell you would have to assign them to some kind of top-level identifier or some such. A minor issue...

Next, Haskell's named-field syntax very closely matches POV-Ray's SDL (though not exactly), but there are still problems. Declaring a field named abc neatly defines a new query function abc :: TypeX -> TypeY. However, it does not define any update function. The only way to update is by the curiose syntactical construct my_x {abc = foo}. Last time I checked, that's not a function, and you can't pass it around the way the abc function can be passed.

I did experiment with a construction such as

type Property a b = (a -> b, b -> a -> b)

get = fst
set = snd

radius = (get_radius, set_radius) :: Property

With this done, get radius fetches the radius and set radius sets it. It proved to be a little unwieldy though.

Conclusion

POV-Ray's existing SDL provides a very straight-forward, intuitive and readable way to describe scenes. However, as a programming language, the SDL is weak and inexpressive. Haskell is an extremely powerful language, and I would dearly love to be able to write all my POV-Ray scenes in Haskell. The fact that Haskell's named-field syntax almost directly matches the existing SDL syntax only makes the thing all the more appealing.

However, there are a number of problems to overcome. It looks like it would work on the surface, but the more you dig into it the more you find that Haskell doesn't *quite* fit what is wanted 'as-is'. Perhaps one could write an interpreter in Haskell that would match exactly - but that would seem to eliminate a lot of benefits of Haskell. (In particular, you just lost all the libraries. And you can't compile this thing any more - unless you're planing to implement a compiler too. It will never go as fast as GHC!)

If anybody has any useful thoughts or suggestions, I'd love to hear them!

MathematicalOrchid 20:51, 9 February 2007 (UTC)

Suggestions

ChrisKuklewicz writes: You are very close to what you want. Making SDL valid Haskell is not quite going to happen, but you can solve many of your objections above with DrIFT. It can created getters and setters and updaters. You can probably easily extend DrIFT to handle any custom classes you write for this project.

Since the SDL is fairly fixed, some of your issues could be made more pleasant by the use of Template Haskell to create tedious top level declarations.

My suggestion is to use as many types as possible to statically enforce requirements of POV-Ray's SDL language.

Very Random and misguided suggestion: If the SDL syntax can be modeled like s-expressions or XML then some techniques from HSXML might work.

  • Ah, I see. So find or make some kind of tool that takes normal SDL and processes it slightly to make it valid Haskell. (And inserts any tedious bits needed to keep the type checker happy.) That could work... MathematicalOrchid 09:51, 10 February 2007 (UTC)
  • (There have also been endless suggestions at making the SDL an XML application. Clearly it could be done, but... it would be much harder for humans to read!)