bramz' diary : loop subdivision surfaces

December 12th, 2006 by bramz

I’ve implemented subdivision surfaces for the TriangleMesh using the Loop’s scheme. Triangles are split in four, and the vertices are weighted using masks to produce a smoother surface. This process is repeated a few times, indicated by the subdivision level.

The implementation also supports the concept of creases. If an edge of the mesh has a crease level different than zero, the subdivision must leave the edge sharp for a number of iterations equal to the crease level. For example, if an edge has crease level three, and a subdivision of five levels is applied, then the algorithm must leave the edge sharp for the first three iterations, and may only smooth it for the last two. This will give the edge a sharper appearance than the rest of the mesh. If the crease level is equal or greater than the subdivision level (for example both level five), then the edge will stay ultra sharp, since the algorithm will never be allowed to smooth it.

In the following image, the same cube is shown for different subdivision and crease levels. There are no vertex normals used, to clearly show the different faces of each resulting triangle mesh. So no sneaky normal interpolation to give the mesh a smooth appearance!

The same cube with different settings of subdivision and crease level

At three o’clock, there’s a perfect cube with subdivison level zero. Going in counterclockwise order, the subdivision is each time increased by one level. At nine o’clock, subdivision level six is reached, resulting in a very smooth object. Remember there are no vertex normals here!

Continuing in counterclockwise order, the subdivision level is now kept at constant of six, but the crease level of the cube’s edges is each time increased by one. The circle would be closed again at three o’clock with both the subdivision and creasing at level six, but this is virtually the same as the original cube (both at level zero), only with a much finer triangle mesh.

You can try all this for yourself using the example loop_subdivision.py in the examples/scenery subdirectory.

PS: you will have to update your Lass installation to use this, since the actual subdivision code is part of Lass.

bramz' diary : FZR in Uffizi (and thin dielectrics)

December 2nd, 2006 by bramz

I’ve reproduced the older FZR render using the Ashikhmin & Shirley BSDF and the Uffizi Gallery light probe. For the glass of the windows and the head lights, I’ve implemented a new material called ThinDielectric that acts as a thin (but not very) pane of glass. Given an inner refraction index and transparency factor for the medium, it simulates multiple reflections and transmissions due to the bouncing of the light inside the pane. Maybe I will write it more properly some day, but here’s how it goes in a nutshell:

  • R = r \left(1 + \frac{\left(1 \minus r\right)^2 t^2}{1 – r^2 t^2}\right)
  • T = \frac{\left(1 – r\right)^2 t^2}{1 – r^2 t^2}

with:

  • R and T the resulting reflectance and transmittance \left(R + T < 1\right)
  • r is your usual fresnel reflectance
  • t = \tau ^ {\frac 1 {\cos \theta_t} is the transmittance of the medium using Beer’s law where \tau is the transparency of the medium and \cos \theta_t is the cosine of the angle of the refracted rays inside the material

FZR with PHD-Motorsports skin in Uffizi gallery

Model by Live for Speed, light probe by Paul Debevec, custom skin by PHD-Motorsports.

update: this render is also featured in the #flipcode gallery.

update: Nicholas “Ono-Sendai” Chapman of the Indigo Renderer has pointed out that Radiance is using a very similar BSDF for their glass material. This is not so surprising, since it’s a very simple extension of the Fresnel equations. The only difference is the distinction between TE and TM waves, which is more correct than what I’m currently doing.

update: Leonhard Gruenschloss noticed a few typos in my formulas (see comments). I’ve corrected them now Thanks Leonhard! (20 feb. 2007)

bramz' diary : caustics in the cornell box

November 15th, 2006 by bramz

It’s been more than a month since last update, so it’s time for a status report. I’ve been further playing with photon mapping and some nice shaders. The main additions to the photon mapper itself are the (ir)radiance caching when using the final gather step, and the caustics map.

The former was – I believe – originally suggested by Per Christensen in Faster Photon Map Global Illumination. The idea comes from the observation that when using final gathering, a lot of time is consumed by the radiance estimates where final gather rays hit. When you cache these estimates at selected positions, You can gain a factor of 5 to 10 in speed, while the error you get by using this approximation will hardly be noticeable due to the nature of the final gathering.

For the caustics photon map, I’ve used the technique mentioned by Keller and Wald in Efficient Importance Sampling Techniques for the Photon Map. All photons that have gone through a specular event before hitting a diffuse surface, are stored in the caustics map. All other photons are stored in the global photon map after being selected with a probability of 1/Q, with Q the caustics quality factor. The Q factor is an easy way to set the resolution of the caustics map. If you set it higher, it will contain more photons and deliver crispier caustics. There’s still some issues with the caustics map though. I need to add some filtering, and use the convex hull as an area estimate to get crispier caustics and prevent leaking.

To experiment with the caustics, I’ve added a dielectric Fresnel shader with proper reflection and refraction (see my article Reflections and Refractions in Ray Tracing), which is of course used for the yellow sphere. I’ve deployed a simple medium material system (volumetric shaders), so that I could attenuate the light going through the sphere using Beer’s law. The yellow colour is entirely due to this Beer medium, as the Fresnel shader is … well … colourless (greyscale).

For the blue sphere in the back, I’ve implemented the famous Ashikhmin & Shirley BRDF (An Anisotropic Phong BRDF Model and An Anisotropic Phong Light Reflection Model). This of course has nothing to do with caustics, but it’s a very cool shader nevertheless. Notice the slightly glossy reflection and the Fresnel behaviour at grazing angles. I do have problems for keeping the photons at constant powers though, the specular lobe bumps power up, but I’m not really sure if it can be avoided.

Caustics in the Cornell box

Where’s the road going from here? One thing that I certainly want to add to the photon mapper is importance driven photon shooting, so that we can go crazy with sky lights and windows … The other is some priority stack for medium materials, to solve the water-in-a-glass problem (more about that later). And while we’re at it, we should add more in- and outscattering to the medium materials so we can have fog =) I’m also trying to put one of the PHD-Motorsports cars in the Cornell box, but I seem to have some problems to smooth the mesh properly.

PS: we have reached revision 42 in the subversion repository. w00t! ;)

bramz' diary : photon mapping with the cornell box

October 10th, 2006 by bramz

Summer is over (already 20 days since September 21), and as promised, we’re back with some more LiAR stuff =)

I’ve been busy with a few things, the main one being the photon mapper. It’s not completed yet. Currently we only have a global photon map and a gather step with a full radiance estimate. On the agenda are precomputed radiance estimates, importance driven photon shooting, caustic photon maps, …

The photon mapper does not use the traditional diffuse/specular/absorption Russian roulette of Jensen, but rather a full BSDF approach as described by Pharr. This required a complete refactoring of the shaders, which was the other big thing I was working on.

As you could read in an earlier post, I was not so happy with my material implementation. The shaders didn’t match well with things like photon mapping, so I completely ditched the shader approach for a BSDF one. I was a bit worried about having to evaluate expensive textures for each BSDF call, so I had to find a way to buffer them. The solution I’ve choosen is to compute many values in one call by some kind of iterator approach.

I guess that’s it for today. But of course, there’s still some render to show. OK, it’s only a dull cornell box, but it allows me to check my results more easily because I know what to expect. Overall, it looks good, but you might notice some problems at the edges of the wall. Apparently, the final gather doesn’t work so well over there.

traditional cornell box rendered with photon mapping

news : summer break …

June 20th, 2006 by bramz

Hi all,

It’s been a while since the last update. We can assure you that LiAR isn’t dead, but unfortunately we have been through some busy months, and some more are coming up, which puts everything on a hold. So there probably won’t be many updates in the next couple of months either.

See you after the break =)

bramz' diary : image based lighting

April 26th, 2006 by bramz

My first render using an environment map as skylight … Using the technique described in Infinite Area Light Source with Importance Sampling by Pharr and Humphreys, it’s implemented in a breeze … (thanks Thomas, for pointing out that article =)

three differnent spheres illuminated by the grace cathedral probe (light probe by Paul Debevec)

(Light probe: Grace Cathedral by Paul Debevec)

bramz' diary : Fixing transformations and local geometry

April 20th, 2006 by bramz

Today, I noticed there was something seriously wrong with the way local differential geometry was treated by transformation objects.

Basically, rendering goes like this: find intersection, find geometry of intersection, get shader at intersection, shade using the geometry. However, currently, if the object being intersected is a transformed one (by scenery::Transformation), all local geometry (3D point, normal, …) was transformed to global space, the top level of the transformations. So, all shading was done in global space. Is that bad? Yes. Imagine a sphere that uses a 3D texture like CheckerVolume, and this sphere moves from the left to the right. Because the value of the texture is looked up in global space, this means that the sphere will move while the checkerboard pattern is stuck to its global position. This is not what is wanted. The pattern should be fixed to the object!

OK, seems easy to solve that: don’t transform local geometry! Instead, when shading, transform all global information (eye rays, light rays, …) to local space. Well, not exactly … There’s a bit of a problem to this approach: suppose you create a complex object using CSG and some transformations, then every part of that object will have its own local space. If you apply one 3D texture to the complex object, you’ll notice that you won’t get a continuous texture: it will jump from one local space to another.

So, we need a combination of both. Basically, from the observation above, we want to do the shading in the coordinate space of the (compound) object the shader was attached to (let’s call it shader space). The untransformed sphere in the first case, the complete CSG object in the latter. When retrieving local geometry, transform it up (towards global space) until you get to the object with the shader attached. From there, keep track of a single transformation from shader to global space.

Using this approach, we get the following result. One sphere with radius 1 is instanced three times: once without transformation, and two times scaled up by a factor 2.

  • The middle sphere is the untransformed sphere and a shader attached with a standard CheckerVolume texture. For this one, local space = global space = shader space.
  • The left sphere is a second instance of the middle sphere, but scaled up by a factor of two. The shader space is still the local space of the untransformed sphere. As result the texture is scaled up together with the object.
  • The right sphere is a third instance of the same sphere, however this time, the shader is attached to the transformed sphere. This means that the shader space = global space. As result, the pattern has the same size of the untransformed sphere.

Transformations and shader space

script: examples/scenery/transformation.py

bramz' diary : shaders or BSDFs?

April 18th, 2006 by bramz

I’m in a bit of a design dilemma right now … Currently, materials are implemented as shaders. This is similar to the RenderMan Interface. Each material is a piece of code that gets executed when the ray tracer hits the surface, and yields the outgoing radiance (the colour to be rendered). That’s great, we have maximum flexibility, and we keep open the possibility to be somewhat, more-or-less compatible to the RenderMan Interface.

Add photon mapping to the mix (or another GI thingy like path tracing, radiosity, … though radiosity might still do with the shaders above?). Suddenly, those shaders aren’t so cute anymore. Since photon mapping basically shoots a photon at a surface, and wants to know in what direction that photon will be scattered. This is very BRDF oriented. We can’t really do that with shaders, or can we?

So here’s the dilemma … abandon shaders, or come up with a solution that fits both? What solution?

bramz' diary : cornell box (direct lighting only)

April 17th, 2006 by bramz

Today, I’ve being translating the ever-famous Cornell box to a python module. Of course, I also had to make some render, so here it is. Since I still only have direct lighting (ok ok, I really need to fix that =), the result is not so terrific. But there’s at least soft shadows ;)

Cornell box (direct lighting only)

The scripts are available in the examples/cornell_box directory.

bramz' diary : area lights

April 16th, 2006 by bramz

Today, I’ve been playing a bit with area lights … They are implemented as a scene object scenery.LightArea that takes another scene object as light surface. I’m not entirely happy with it, but here’s some tests …

The first one is the good ol’ red sphere with a spherical area light. The entire scene is also illuminated by a blueish sky. 9 samples per pixel, 16 light samples for the sphere and 36 for the sky.

area light ...

Happy Buddha under a bright white sky … 9 samples per pixel, 64 light samples

happy buddha under a white sky

The sky surface is a bit troublesome because it LiAR isn’t exactly designed to cope with points at infinity. Currently, it’s just a giant normal sphere, but it should be examined how we can improve that.

We also need to be able to texture map the area lights so that we can use a nice HDR image as sky =)