AS8
Texture Surfaces. (No partner).
Submission Effects
I decided to make a shape of a Chinese Knot, specifically a "good luck" knot. Download the b-spline and textures, or just the b-spline.
All Effects
Gouraud Interpolation
Phong Interpolation
Gouraud Interoplation and Texture Mapping
Phong Interpolation and Texture Mapping
Phong Interpolation, Texture Mapping and Bump Mapping
(EC) Phong, Texture, Bump, and Displacement
(EC) Phong, Texture, Bump, Displacement, and Shadows
(EC) Phong, Texture, Bump, Displacement, Shadows, and Percentage Closer Filtering
(EC) Phong, Texture, Bump, Displacement, Shadows, Percentage Closer Filtering, and Ambient Occlusion
(EC) Phong, Texture, Bump, Displacement, Shadows, Percentage Closer Filtering, Ambient Occlusion, and Ground
Example Lighting Animation
Default Shape
Because my shape does not exemplify all of my effects, I rendered the default scene with my new renderer. You can clearly see the soft penumbra, and darker occluded spots.
Extra Credit
Displacement Mapping (GLSL)
I implemented displacement mapping using the vertex shader. This turned out to be simpler than modifying the geometry before sending it to the shaders, and will achieve a faster effect. However, this method of displacement is dependent on how finely sampled the scene is.
Shadows (GLSL)
I implemented shadow mapping in a two-pass render phase. First, the scene is rendered from the light's point of view. This render is saved to a texture (depth buffer/shadow map), storing distance in Z as color. The second pass renders the scene from the actual camera's point of view, this time using the depth buffer in the fragment shader. The depth buffer is mapped to the current view, and the portions of the shape that the light can "see" are rendered in light. The other parts have their color values simply scaled by 1/2 to simulate a shadow. Shadow mapping proved to be difficult, especially keeping transformations consistent in both the depth buffer and camera view.
The second picture above shows the depth buffer of my scene. Notice that it is of a different perspective (the light's perspective) and obeys my displacement map.
Screen Space Ambient Occlusion (GLSL)
Using the depth buffer from my shadow implementation, I made a try at SSAO. I made a hack-y approximation of an ambient occlusion effect in the vertex shader by 16 random points in the depth buffer around the pixel to be rendered. The 16 random points are achieved using a vector offset within the 3d unit sphere (I chose a 3d unit sphere, but only utilize the 2d coordinates. The third coordinate is to ensure a maximum distance of 1 of its 2d space without making the distance exactly 1). These 16 vectors are generated using a python script (randomspherical.py). The vectors are hardcoded to provide a consistent frame-to-frame picture in a single compilation of the fragment shader. The offset vectors are used in the depth buffer to determine if the given pixel should be occluded. An occluded pixel will be one with a nearby depth buffer pixel that is closer to the light. This effect achieves shadow similarly to my shadow map, by simply multiplying color.
The effect may not be very noticeable because the variables have not been fine tuned. However, you may verify the (approximated) occlusion effect using the second image, which displays only the occlusion layer.
Percentage Closer Filtering (GLSL)
To achieve softer shadows, I used a Percentage Closer Filtering (PCF) technique. This involves using a 8x8 kernel of pixels to sample around the actual depth buffer location. The final shadow result is the averaged total of the kernel's depth buffer values (of being in shadow or not).
Environment Mapping (GLSL)
I implemented environment mapping. This is achieved by loading a texture as a samplerCube (GLSL) or GL_TEXTURE_CUBE_MAP (OpenGL). After appropriately calcuating a reflectance vector in the fragment shader, the color of the reflection can be directly mapped to using sampleCube() (GLSL) with the 3D reflectance vector. Differing metalness factors can be easily achieved using the mix() (GLSL) function, which linearly interpolates between two colors.
The picture shown above has a metalness factor of 0.5.
Skybox (OpenGL)
I implemented a skybox by creating a large cube containing the object. It is not rendered in the first phase of shadow generation, so it does not cast a shadow. A custom texture can be used as the second argument to sweep.
Background (OpenGL)
I thought that my skybox was a bit too dizzy, so I implemented a static ground. It uses the same texture as my skybox.
Face Culling (OpenGL)
I used face culling primarily to prevent self-shadowing. I culled the front faces to provide an accurate shadow map for the back faces from the light. Extending this, culling back faces when rendering from the camera provides a speedup without sacrificing any visible differences. The given framework code actually renders b-splines backwards (front faces being back faces), and this issue is solved by reversing the order of the control points of the cross-section (courtesy of James Andrews), and reversing the computed normals.
On-Screen Console (OpenGL)
I used the on-screen console I wrote in AS1 to output toggles of various effects. This initially provided some issues with textures and coloring not previously encountered.
Movable Camera and Lights
Using various keys, the user can move the camera and lights in the scene.
B-Spline Editor
I utilized one of my AS7 EC, my track editor, to create the Chinese Knot b-spline. With some bugfixes related to adding points, it proved invaluable to designing such a shape. Without this tool, I would have not been able to (reasonably) make such a shape.
Et cetera
Gouraud vs. Phong Interpolation
As the b-spline is sampled at more and more points, the Gouraud interpolation of brightness approaches the Phong interpolation of normals in terms of appearance. I rendered the default shape/texture at a low sample to exemplify the difference between Gouraud and Phong shading.
Gouraud Interpolation
Phong Interpolation
Notice the specular top highlight on the Gouraud picture to be of an odd shape. This is due to the specular highlight being on the vertex and the brightness being linearly interpolated across its polygonal faces. The other specular highlights on the Phong picture are nonexistant on the Gourad picture because the specular highlight does not fall on any edge, and therefore cannot be interpolated.
Comparison of Shadow Effects
Above is a picture with side-by-side comparisons of my shadow effects. Note that I did not yet tune the ambient occlusion parameters, so there exist artifacts (by having a maximum distance that is too far), and a small sample size (16 vectors, compared to hundreds) that results in more discrete occlusion factors.
Performance
Because most of the (difficult) computation is done on the GPU, the CPU usage is fairly low. I get a fairly consistent FPS (~30), and I believe my FPS is soft capped because turning off effects and even the shader does not increase my (steady state) FPS. (There are transient effects when turning on/off effects that will yield a temporarily higher FPS, but the FPS will scale back to 30 after some time.)
Issues
Moire effect/Self Shadowing Patterns
Because our shapes are so smooth, it is difficult removing the self-shadowing/Moire pattern effect (as compared to angled surfaces). I alleviated most of the effect by slightly displacing the depth buffer's z coordinate, allowing for (hopefully very insignificant) parts to be "assumed" in shadow. However, this solution breaks down at very slightly curved shapes, as can be seen in the above screenshot (the screenshot has a smaller z displacement to display the effect). This also affects ambient occlusion.
SSAO approximation
My SSAO effect is approximated by 16 random vectors around the current pixel to be rendered. This is a very large approximation, as I would ideally compare with all pixels in a given range of the given pixel to be rendered. However, this is unreasonable and would cause rendering times to increase to an unreasonable point (on most hardware). The 16 vectors are used for a quicker render time, but approximates the effect. The approximation is obvious on certain shapes and certain viewing angles. I also did not have time to fine tune the numbers in the algorithm (vector scale, minimum/maximum distance) so there are artifacts present, most notably on objects that may be a bit too far to be considered for occlusion. In retrospect, I should have used a pixel kernel (similar to one I used for PCF) to achieve a better effect.
Face Culling
Using face culling helps create the effect of shadows without self-shading. However, usage of face culling makes the depth buffer not a true depth buffer that would present the object's depth, not just its back faces. My SSAO implementation assumes a true depth buffer, and the effects of face culling can be seen when viewing the ambient layer, where it occludes only on back face, and appears to be able to see through objects.
PCF Performance
Using PCF gives nicer, soft shadows. However, the performance hit is massive. Using a 8x8 pixel kernel increases texture lookups per pixel by a factor of 64, which is a large performance hit.
Skybox Appearance
I wanted shadows to appear on my skybox. However, I could not turn off all my other effects. As a result, the skybox is unnecessarily bumped and displaced.