API Tips

From Sunflow Wiki

Jump to: navigation, search

Contents

Lights

Light Samples

The only way to get a valid light sample is inside a light loop. If you are using java 5+, it looks like this:

state.initLightSamples();
for (LightSample ls : state) {
 // do stuff with ls
}

If you are going with Janino which doesn't yet have support for the extended for-loop syntax of Java 5 you have to do it manually:

state.initLightSamples();
for (Iterator it = state.iterator(); it.hasNext();) {
  LightSample ls = (LightSample) it.next();
   // do stuff with ls
}

Keep in mind that for the Iterator type you need to use "import java.util.Iterator;".

Normals

math.OrthoNormalBasis.makeFromW

The orthonormal basis defines the local tangent frame at the hit point. This is needed for bump mapping or anisotropic reflections. If you're unsure how to fill it out, just make a dummy one from the surface normal like

OrthoNormalBasis.makeFromW(state.getNormal())

state.getNormal() and Modifiers

If you are doing bump mapping via modifiers, ShadingState's getNormal() will contain the bumped normal when the shader starts running. There is no easy way to get back the original un-bumped smooth normal. Note that you can always do the bump mapping inside your shader if you prefer by copying the code from the bump modifier into your shader. Then you will be able to store the original normal before changing it. The reason for having the modifier system was so that you could switch shaders without affecting the bump.

Rays

state.getRay() and .getMax()

state.getRay() is the ray that generated the shading state you are currently shading. In other words, for the first hits (seen by the camera) state.getRay().getMax() would be the distance from the eye to the current point.

If you shoot your own reflection ray (or any other kind) you could look at reflectedRay.getMax() to get an idea of how far the first intersect surface is (after you've traced the ray of course). If nothing was hit, you will get infinity back.

Wouldn't the refracted ray have to be traced before absorption can be correctly calculated?

For the glass shader, the refracted ray is applied when leaving the glass object, which means the state.getRay() is the ray fired from the last refraction (or total internal reflection).

Refraction Rays and Specular

At the moment you will get specular highlights when you shoot refraction rays. A possible workaround would be to use a custom shader and manually intercept the rays that have refraction depth > 1 and skip their specular contribution.

Shading

Color.X versus Color.x

When changing a color X (for example, Color.WHITE.mul(...)), this is actually changing the value of the constant "WHITE". You should instead use a method that returns an object (for example, Color.white().mul(..)).

Chris notes: This could be argued to be a poor design of the Color class. Perhaps the methods should always return new objects - but I believe that would have a small speed penalty. This is one of the drawbacks of Java actually.

static final (in the case of Color.X) just means you can't point the reference to X (for example WHITE) to a different object. But it guarantees nothing about the contents of the object itself. Since the mul() method actually modifies objects in place (to avoid creating new ones) - this leads to the observed erratic behavior. Sadly Java doesn't have the concept of "const" as Sun felt it would complicate the language too much. The way you are supposed to design this is to create objects that cannot be mutated. This would mean creating a Color class with final members (they can only be assigned once). Then, each member method (mul, add, etc ...) would need to create a new object each time.

But this might have a small performance penalty unfortunately. It would be an interesting experiment to try it out under the latest JVMs. Similar caveats apply to the Point3, Vector3 classes as well.

Loading A Texture

The specific method that deals with this is TextureCache.getTexture(filename, isLinear). So you would define the texture like so:

Texture myTexture = TextureCache.getTexture("C:\\myPath\\myImage.jpg", true);

This will let you have several shaders share the same texture in memory if they all point to the same file. You should call this code during your update() method and then use the resulting texture object in getRadiance().

UVs

There are two types of UV calculations: one for the intersection code, and the other for the shading preparation code. The intersection calculation gives you the u/v for free. Note that there is still a difference between the hitpoint (u,v) pair and the texture coordinates. One is for barycentric coords, the other is for actual texture lookups. Only the latter is important for shaders.

Geometry

API Methods

The api has two methods that relate to geometry:

geometry(..) instance(...)

The first one just lets you declare the kind of geometry you want: triangle mesh, sphere, hair, particles, etc. The second actually puts that geometry in the scene, attaches a shader to it, etc. If you never call the second method, you won't get anything in your scene.

Two-Sided Geometry

Geometry is always two-sided at the moment, there isn't an option for the opposite. If you want such an ability, you'd have to manually do that in a shader.

Thread Safe Code

The simple rule is: don't use global variables unless they are read-only.

Example

Here is code run in a non-thread safe way. Notice that rColor is defined globally in the unsafe code. This will lead to poor results (in this case, heavy speckling).

Unsafe:

Color rColor;
Texture texr;

public boolean update(ParameterList pl, SunflowAPI api) { 
   texr = TextureCache.getTexture("C:\\myPath\\myImage.jpg", true); 
   return true;
} 

public Color getRadiance(ShadingState state) { 
   rColor = texr.getPixel(state.getUV().x, state.getUV().y);
}

In the safe code I've defined rColor locally in the getRadiance method.

Thread Safe:

Texture texr; 

public boolean update(ParameterList pl, SunflowAPI api) { 
   texr = TextureCache.getTexture("C:\\myPath\\myImage.jpg", true); 
   return true; 
   } 

public Color getRadiance(ShadingState state) { 
   Color rColor = texr.getPixel(state.getUV().x, state.getUV().y);
}

Changes In 0.07.3

In 0.07.3 and up:

  • "parse" has been replaced by "include"
  • "getCurrentFrame()" has been replaced by "currentFrame()"