April 07, 2003
GRT is the beginnings of a lisp raytracer [via CLiki].
GRT started as a C project, but “The productivity boost of using Lisp over C is amazing! This way GRT will proceed much faster -- no more core dumps.”
Posted by jjwiseman at April 07, 2003 10:27 PM
A lisp raytracer is actually a pretty good idea. A quick look at GRT, however, suggests that there are some design issues that will make progress to an 'interesting' renderer a bit difficult. By this I mean that design constraints are being introduced that will make things like global illumination and path tracing more difficult.
It will definitely take a fair while before GRT is interesting / usable as a raytracer, but what are these design constraints you mention?
If I've unwittingly introduced something wierd into the design, I'd like to know ASAP!
-- Nikodemus, the GRT guy
I haven't spent a lot of time looking at your code, so my impressions may be
wrong; please correct me if I have missed something.
As you know, a basic raytracer is very simple to write. With a fair bit of
additional work, you can get some quite interesting rendering out basically the
same framework. However, you will hit diminishing returns since simple ray
tracing makes some fundamentally poor physical assumptions. You can think of it
perhaps as a 0th order approximation to the physics of light transport.
For a 1st order approximation, you really want to look at 'global illumination'
techniques: radiosity, path-tracing, photon-maps, etc. So, assuming you are
interested in bringing GRT up to some somewhat modern capabilities, I think you
should have these goals in mind when designing your renderer.
What are the implications? Like classic rt, you will still be dominated by
intersection calculations. This implies you need:
1) algorithmic accellerators. I don't think you have anything here yet, but you
can retrofit. Perhaps more than one spatial subdivision scheme (for example for
shadow rays and light)
2) probably seperate intersection methods, does-intersect, first-intersect,
all-intersect, each with minimial possible state calculated and returned.
2b) a way to get more state from minimal intersection info (e.g., pass time and
ray geometry to an object and get a normal back)
3) fast primitive vector operations. I think you are implementing these as
structs rather than arrays. Depending on the compiler you should be ok here.
4) think about what space intersection calcs are done in.
So with a suitable framework in place, you can be fast enough to use global
methods. What might you want then?
a) consider `real' energy transport, not approximation.
b) surface interactions with many possible sources (e.g. photon maps + light
sources + ...)
c) don't work in RGB colour, or at least not exclusively. This implies the
light being transported should be completely decoupled from the rest of the
d) think about gamut/tone mapping issues
So what does this all imply for a project like GRT? Some things can be easily
ignored until later, as they will bolt on. Others may affect your pipeline
deeply, so you really want them to be there at the begining.
Potential problems I see:
- intersection calculations need seperate cases, you need some idea of how
geometry will work with accellerators, how much modeling you want to be able
to do (i.e. what space you calculate things in).
- intersection calcs need to be lightweight, does your current model couple to
design of BRDF (surface/light) calcs?
- geometry calculations not really seperate from transport. Rays are about geometry.
will your geometry calcs work backward without changing the code? (you will
probably want to send rays from lights at some point).
- reliance on RGB colour.
- there seems to be a lot of geometry related state in functions like
trace-ray. In effect you are special-casing things like
total-internal-reflection, but this should be decoupled from tracing.
Similarly, you are implicitly enforcing a model of light-surface interaction
(eg a BRDF) inside the trace-ray function! This should be abstracted
completely away from geometry.
- camera is correctly decoupled from geometry calcs, so you can replace it
with a more capable camera (not just walking a grid) later easily, this is
the sort of modularity you want in the rest too
- have you thought about hierarchical geometry? instances?
The efficiency issues are the same for plain rt that you want to run fast,
either for RT, or just complex scenes, etc.
hrrm this is getting a bit long...
Thanks for your comments,
You make good points. GRT is going to be classical raytracer for quite a while yet, but the performance conserns are starting to be addressed fairly soon (spatial hierarchies via octrees for 0.2, plus maybe first-intersection acceleration via grid-projected bounding boxes).
Once basic raytracing functionality is in place I intend to start moving into a shader based architecture, where scene-objects are largely in control of how they are rendered: this includes choosing the illumination model per object and gathering data from the environment (automatic level-of-detail control).
But those things are still in non-foreseeable future, and the current goal is doing the basic raytracer well. Trivial raytracer is dead simple, but even a good classical raytracer is non-trivial by my standards -- there is a lot to implement, esp. in various primitives and accelerators.
Once GRT gets there, it may be that I have painted myself into a corner, but at least there is a decent raytracer for all my troubles. I do hope to avoid the corner, however. ,)