My journey on the 3D viewer

 

    3D graphics has been occupying a big part of my interest ever since I learned to play video (which was before I learned how to speak, surprisingly.) I always feel motivated to learn more about the sophisticated techniques to render wonderful 3D images. Actually learning to write OpenGL (a real-time graphics library) code was long overdue. That was the main drive for me to sit down and learn to create a 3D model viewer from scratch. The 3D viewer is now a part of the Unpacking Manuel’s project.

    Prior to creating the 3D viewer, I spent a lot of time looking at many 3D shading techniques. I had also worked with shaders in Unity game engine so I was more or less familiar with the concept of shading. In fact I set out one week of my time to create a shader pack to further understand what went on under the hood.

   Even though I was able to write shader code in Unity, I thirst for advanced 3D graphics was not quenched. Unity still handled most of the heavy lifting like Physically Based Rendering (PBR) or the bloom effect you can see in the video. My second attempt at digging into advanced 3D rendering lead me to the first step of creating the 3D model viewer. The process began with me playing around with some simple tutorials that taught me the what it takes to at least render something to an OpenGL canvas. Once I was able to simply render an unshaded cube out to the screen, thanks to my pass experience with shader code, I knew exactly what to do next. I started writing an OBJ file processor. OBJ files are the simplest type of 3D model files that stores all the information for a 3D model inside of a text-based file. Parsing an OBJ is a piece of cake. But there are things that OBJ files do not encapsulate and the processor has to infer by itself. OBJ files do not store the surface tangent vector used to apply normal maps to the material. I needed to take some time off and research on how to implement the tangent vector generator. After some vector algebra, I was back on track to progress on the viewer. I then was able to apply the textures and normal maps to the model but I didn’t have any ambient lighting. The background of the viewer was just black. That’s when I looked into implementing environment maps and how to use it to light the scene. I have yet to fully implement the environment map that adheres to modern graphics standard but I have a very clear plan for it in mind. After (half-way) implementing the environment map, the viewer was more or less functional and could be used to view some 3D models with satisfactory visual quality.

(Left-click and drag to rotate. Right-click and drag to pan.) 

  But most of the lighting calculations were all my own approximations and parameter tweaking. That was when I started digging into learning PBR shading. I spent another couple of weeks looking at articles and papers on PBR and what was used as the industry standard. That was when I came across this article. It lays out all the equations for Physically Based Rendering used in the Unreal game engine. I rewrote my shaders based off of that article and put in some of my own parameters. The result is a much cleaner and more modern looking 3D Viewer.

(Left-click and drag to rotate. Right-click and drag to pan.) 

    At this point I was very satisfied with the result. But there was a tiny problem, because I wanted to make the viewer look professional, I naively implemented the bloom effect into it as well. The problem with the bloom effect is that it requires multiple iterations of blurring and that is detrimental to the performance of the viewer. For a period of time, the viewer was almost unusable on low end computers. I thought long and hard about it and all of a sudden, a brilliant idea came to my mind. Because I was going to blur the image anyways, I didn’t have to apply it at full resolution. All I needed to do was downscale the canvas down and apply the blur filter at the lower resolution and linearly scale the image back up. I quickly applied that idea to the viewer and its performance increased significantly. The viewer ran smoothly even on my old $350 laptop. Also the effect looked a lot better and more prominent because the linear scaling added more blur to the bloom.

(Bloom effect at full resolution. The blur filter is not as prominent and performance was very bad.)

(Bloom effect at low resolution. The blur filter is amplified by the lower resolution and linear scaling. Performance is as good as not having the bloom effect at all. I was also able to apply more blur iterations thanks to the performance boost.)

   All in all, I still have a long way to go with the 3D viewer such as applying environment maps properly and not just query lower MIPs to get the blurry effect on rough surface. I also plan on implementing light shadows, which I also have a solid plan to do. I’m still very happy and proud of what I have so far.