Supporting OBJ and MTL Materials
Description
For my final project, I am increasing support for the information included in OBJ files and their associated MTL files. With better support for OBJ files we can make much more interesting scenes than our simple shapes and colors can provide. Specifically, the aim of this project is to support barycentric-based interpolated normals and texture coordinates, and texture-based normal mapping.
Implementation
Barycentric Coordinates- When retrieving the color or normal at a location, we can convert that location into barycentric coordinates. We can then take the weighted average of each vertex to compute per-pixel texutre coordinates and normals. In this way we can go from the triangulated normals to smooth normals as shown by the reflective and refractive knot renders below.
Shapes- Supporting OBJ files with multiple shapes is important for more complicated OBJ files. It also has the benefit of making smaller bounding boxes which can speed up rendering time. My renderer can support per-face materials and per-shape textures. Most OBJ files I have come across only use per-shape materials and textures.
Normal maps- Normal maps are handled similarly to textures. To determine the orientation of the map in world space, I followed the guide on learnopengl linked below. Using the existing mapping of texture space to world space created by texture coordinates for each vertex, we generate a tangent and bitengent vector for each triangle. These vectors are then put in a matrix with the normal and multiplied by the texture normal to get our world space normal for that ray.
Materials- I focused on mapping materials from MTL files to the materials we had already implemented. This led to a few issues, namely that many OBJs I came across had add material properties for the material they were representing. For example, some materials that were supposed to be completely opaque had an index of refraction not equal to 1. Other materials meant to be glass had a 'dissolve' (or see-through) property, but had an index of refraction of 1. I did my best to accomodate as much as I could. Particularly, I modified my ray-tracing algorithm for materials that had both specular and diffuse properties. For these materials I generate 2 rays on every contact with that material and weight their results according to their properties defined in the MTL. This allows for mostly diffuse black materials to still gain some color from the surrounding environment with their specular component.
Conclusion
The following are some renders with these new capabilities demonstrated. I found that it is quite difficult to find readily available OBJ files with well-defined materials and normal maps. As a result, I show off some of these different properties separately to demonstrate the effects they produce. Particularly, normal mapped OBJ files were very rare. I also ran into timing issues. For example, the renders below of the house and apartment have several different materials, but with how noisy the images are it is often difficult to differentiate the different materials. I rendered this top scene below (medieval house) at a resolution of 640 x 480 with 100 rays per pixel, and that took over 2 hourse which doesn't leave much room to increase the quality. I found much better images are generated by objects with solid colors and interesting normals than are generated with a combination of normal maps and textures where those details become lost in noise. I can also imagine that denoising techniques may struggle with textured/detailed scenery. Overall this was an interesting project and I learned a lot but I don't think CPU-based textured OBJs will generate pretty images in a timely manner any time soon. That being said, someone with skills in blender who can make custom objects may have better luck
This is the rendering of the medieval house that took 2 hours. There are 37 shapes and around 12,000 triangles. It is rendered at 640 x 480 (as are all the images below but one) with 100 rays per pixel and 100 recursion depth.
A different house with similar results. This one has the same material properties throughout.
A semi-reflective cube with normal maps. This is a good display of the power of a normal map on a flat surface as you can see how much of the right wall and floor's color is displayed on the edge normals.
Refractive version of the cube above
OBJ of the knot from above with a brick texture and corresponding normal map. The lighting of the mortar between the bricks is apparent, especially on the top where the 'bottom' of the brick appears darker.
Below is a door OBJ followed by reflective/refractive examples of OBJs with smoothed normals
This is one of the most interesting renders. A semi-refractive obj where the color comes from both the material and the refracted ray. There is also a normal map applied. The texture is another brick texture which is why it appears red.
Sources
- https://learnopengl.com/Advanced-Lighting/Normal-Mapping
- http://paulbourke.net/dataformats
- OBJs found in renders
- Glasses: https://clara.io/view/a4ae2568-989c-46ea-bee0-bb294660ab25
- Apartment: https://clara.io/view/254dbaa1-78fc-45f1-9de4-b84de429ab0f
- Medieval house: https://clara.io/view/b733ea40-f35f-4115-a15b-f5a44275f02e
- Normal cube: https://clara.io/view/cd9fe284-ad25-4c07-9724-d4045ea37cd6
- Knot: https://clara.io/view/ae6cf149-d6a7-4088-b21f-8c6d94d22b85