I added config file options to place random spheres in 3DWorld. The user specifies the number, size range, spatial distribution, and probabilities for various sphere materials in a text file. These can be added to any scene. In this case, I used a simple white cube for a ground plane. The spheres are placed randomly within a circular region on the ground. I used between 100 and 200 spheres for these tests because that gave reasonable runtime numbers.
3DWorld implements reflective materials using one cube map per object (sphere in this case), as shown in this blog post from last year. The basic description of how cube map reflections work in 3DWorld is shown here. All six cube faces of each object are rendered to textures during update frames. Several update frames are needed to get the nested reflections between multiple nearby reflective objects. For example, two nearby metal spheres will reflect each other recursively. Each update frame takes around 250ms to compute almost 100 cube maps, or around 500 cube face texture images.
Moving individual non-emissive spheres takes 50-100ms per frame to update the needed cube map faces. Moving the sun requires the full 250ms update due to the global affect of sun lighting in the cube maps of all the spheres. Frames where nothing moves but the camera take only around 14ms for these scenes, or about 70 FPS.
Indirect lighting is computed in about two minutes for 14 light sources in 3DWorld. I used the same method as in other scenes, as described in this post. The lighting data can be saved and reused at a later time as long as none of the emissive light spheres move. The lighting grid is stored in a 3D texture on the GPU and used to lookup indirect lighting data in the fragment shader.
For reference, here is the Three Eyed Games image:
GPU path traced image on Three Eyed Games blog that took several minutes to generate. |
Here is as close as I was able to get using 3DWorld:
Realtime 3DWorld rendering of similar scene using precomputed indirect lighting, shadow maps, and cube map reflections. |
I'm not trying to exactly duplicate the reference image, I just want to make something in a similar style. The goal is to compare the two different rendering approaches.
There are a variety of differences. The most obvious one is that the 3DWorld version is darker than the reference image. This is because there are fewer emissive light source spheres. 3DWorld is currently limited to around a dozen dynamic point lights with cube map shadow maps because it has a fixed number of shadow map slots (64). I could probably increase the limit, though it may hurt performance.
The 3DWorld image has hard shadows, whereas the reference image has soft shadows. That's the difference between using shadow maps vs. ray tracing. There's not much I can do about this one. Soft shadows from many point light sources are difficult to do in realtime.
Another difference is the mixture of materials. The reference image contains a combination of emissive, metal, dielectric, and diffuse spheres. The reflective spheres have variable roughness. 3DWorld has the same types of materials, but seems to use a higher percentage of shiny metals. There are some rough metals and some dielectrics, but the lower lighting levels makes them difficult to see. I've shown rough materials previously. I could tune the random number generation parameters to make them more in agreement. I kind of like all of the reflective spheres in my image though.
The two images have different types of noise. The reference image has high frequency pixel noise that can only really be seen when viewing the image fullscreen. 3DWorld has lower frequency noise due to the sampling of the precomputed indirect lighting grid, currently 192x192x16 texels in size. I spent a while adjusting various parameters in the code and config file to try and minimize noise, and it definitely improved things. I could further reduce the noise with a higher resolution grid. However, this would take more precomputation time, more disk space to write, more GPU memory, and more work in the fragment shader. This leads to increased render time/lower frame rate.
This screenshot shows the same scene during the day under bright sunlight.
Same 3DWorld scene as above, but this time during the day. The bright sun produces sharp shadows on the ground plane. |
Here is another view of the 3DWorld scene at night. Now that I look at it, this seems closer to the reference image than my first screenshot.
Another view of night time 3DWorld scene. |
Here is a similar scene with 100 spheres in day time. The bright sun makes it easier to see the different materials. This time I used transparent spheres and refractive glass spheres in addition to the other types. The glass spheres with higher index of refraction produce inverted refraction images. I removed the tiny spheres that were difficult to see.
Day time rendering of a similar spheres scene with strong sun lighting. This one has transparent/glass objects as well. |
I also found a scene that had two large light emitters near a small glass sphere. This produces caustic lighting where the glass focused the light sources to two bright white spots of light on the ground plane.
Metal, glass, ceramic, and emissive spheres during the day. A caustic refraction can be seen near the center. |
Here is the same scene and view, but at night. The indirect sphere lighting is easier to see.
Metal, glass, ceramic, and emissive spheres at night. The two white lights are focused through the glass sphere in the center to produce caustic lighting patterns on the ground. |
Here is the same scene, but a different view. We're now looking at the other side of the glass sphere.
Same scene as above, but viewed from the front. The noise in the lighting is due to grid sampling artifacts. |
That's it for now. I'll consider making a more complex and interesting scene in the future. I would also like to reduce the indirect lighting noise, though I don't know how to do it efficiently.