HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaModerate

Ray Tracer main loop

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
loopraytracermain

Problem

For the past week I have been studying and improving a ray tracer that I ported from JavaScript. The bare finished port was only exactly what was written in JavaScript and rendered at around 20fps at 100x100 resolution with a scene of just one plane and two spheres, over the week I have implemented very simple frustum culling (although it doesn't help as nothing is occluded currently), I reduced the number of new objects created per ray by making the Color and Vector class more instance-based and multi-threaded the whole render job which has improved it to around 85fps.

While improving the code, debugging and benchmarking it i found that simply five math functions can impact the whole render in the end by 20ms! So as I do not understand the ins-and-outs of all the mathematical functions, I would appreciate any improvements that could be made highly.

```
private static Color traceRay(Scene scene, Raycast job, Ray ray, int depth) {
if (depth >= maxRays) return job.sumColor;
RaycastReport intersections = intersectScene(scene, ray);
if (intersections == null) // Stop tracing if no hit
return Color.voidColor;
if (depth == 0) job.objdistance += intersections.distance;
job.hits = depth;
Color hitcolor = shade(job, intersections, scene, depth);
job.sumColor.add(hitcolor);
return hitcolor;
}

private static float testRay(Scene scene, Ray ray) {
RaycastReport intersection = intersectScene(scene, ray);
if (intersection == null)
return -1;
return intersection.distance;
}

private static RaycastReport intersectScene(Scene scene, Ray ray) {
float closest = Float.POSITIVE_INFINITY;
RaycastReport closestobject = null;
for (RenderObject object : scene.objects) {
RaycastReport intersection = object.intersect(ray);
if (intersection != null && intersection.distance 0) ? Color.scale(illum, light.color) : Color.defaultcolor;
float specular = Vector.dot(livec, Vector.normal(reflectdirection)

Solution

Let your code breathe

Your code is very hard to read - lines are long and crowded together, containing multiple method nesting (direction.subtract(Vector.multiply(2, Vector.multiply(Vector.dot(normal,...).

Reading code is like reading a book. If you don't give the reader some breathing room (a period, coma, paragraph) - your reader will get lost, and might abandon the book altogether.

You should consider adding a few blank lines to make the structure of your methods more apparent, for example:

private static Color traceRay(Scene scene, Raycast job, Ray ray, int depth) {
    if (depth >= maxRays) return job.sumColor;

    RaycastReport intersections = intersectScene(scene, ray);
    if (intersections == null) // Stop tracing if no hit
        return Color.voidColor;

    if (depth == 0) job.objdistance += intersections.distance;

    job.hits = depth;
    Color hitcolor = shade(job, intersections, scene, depth);
    job.sumColor.add(hitcolor);
    return hitcolor;
}


You should also consider extracting some of the hard computational lines to helper methods, and name them so that the reader will better understand your code:

private static Vector travel(Vector startPosition, Vector direction, float distance) {
    Vector travelPath = Vector.multiply(distance, direction)
    return Vector.add(travelPath, startPosition);
}


Variable Naming

Also hindering readability is your choice to use an alllower variable naming convention. naturalColor is much more readable than naturalcolor, all the more so isInShadow vs. isinshadow.

You also tend to use lazy shortcuts like ldis, livec, lcolor, illum, etc. which might seem meaningful to you, but to an outside reader the names might be less helpful.

Something must be missing...

for (Object object : scene.objects) {
    RaycastReport intersection = object.intersect(ray);


How does your object intersect with ray? As far as I know, there is no such method on Object... It is quite hard to calculate complexity of code, if we can't know how this intersection happens, especially since it happens within a polynomial iteration (lights*objects) in testRay, and within a recursion in traceRay.

Caching?

If the complexity of intersect is a constant, your complexity would be \$O(maxRays \cdot objects \cdot lights)\$ (I'm counting your Vector operations' complexity as constant), this might not be ideal.

I've got lost in all the recursion and iteration loop you have there, but might there be a way to cache some of the calculations? Would it help you to "remember" distances between your scene (which does not change during your calculations), and your rays, and reuse them as you go along? This might lower your effective complexity, and make your code run a lot faster.

Code Snippets

private static Color traceRay(Scene scene, Raycast job, Ray ray, int depth) {
    if (depth >= maxRays) return job.sumColor;

    RaycastReport intersections = intersectScene(scene, ray);
    if (intersections == null) // Stop tracing if no hit
        return Color.voidColor;

    if (depth == 0) job.objdistance += intersections.distance;

    job.hits = depth;
    Color hitcolor = shade(job, intersections, scene, depth);
    job.sumColor.add(hitcolor);
    return hitcolor;
}
private static Vector travel(Vector startPosition, Vector direction, float distance) {
    Vector travelPath = Vector.multiply(distance, direction)
    return Vector.add(travelPath, startPosition);
}
for (Object object : scene.objects) {
    RaycastReport intersection = object.intersect(ray);

Context

StackExchange Code Review Q#45573, answer score: 10

Revisions (0)

No revisions yet.