Since this summer, I start learning Vulkan API and preparing for the coming game project (GAM550). In three months I made a simple wrap of Vulkan and explored some interesting graphics effects like Tile Based Lighting, Screen Space Reflection, effective shadow mapping. The wrapped API worked well during these explorations so I decide to continue use it for this class.
The framework only wrapped some basic functions of Vulkan to make it easier to use (like creating buffers, images, render passes, framebuffers, pipelines, recording command buffers, etc.) so basically I have to write everything related with rendering from scratch. For this class, I will write most rendering logics in one main file so the process of building pipeline would be clear.
At first, I tried to make a simple deferred shading pipeline with one single parallel light which is hard-coded in shaders:
Offline Pass: Rendering the scene from the perspective of camera into one color texture, one view-space normal texture, one view-space position texture and one depth-stencil texture.
Deferred Pass: retrieve position, normal and color of each pixel using the textures from Offline Pass with a fill-screen rect and doing simple lambert and blinn-phong shading with a parallel light (a vector) to get final color.
View Space Normal(normalized and scaled by 0.5 then add 0.5)
View Space Position(Scaled by 0.01 then add 0.5)
Scene Albedo
Final Result
Later I tried to rebuild the view space position by multiply the clip space position(build with texture uv and depth value) with the inverse projection matrix of camera and it seems to work well.
Reconstructed view space position
Offline rendered view space position
After implemented basic deferred shading, I followed the instructions of handout paper and implemented multi local lights rendering. Firstly, to check the correctness of drawing method, I draw the light balls with a single color.
A light ball with red color, blend mode is additive, depth test off, culling back face
During this time I encountered two problems. The first one is when I tried to set depth test as read-only test(same as drawing transparent objects) to reduce overdraw of lights behind objects, the light disappears when the camera goes into its range. Then I tried to turn off face culling but then the light seems to be over bright because of drawing twice(both front and back face). Finally I gave up and turned depth test off. The second problem is when I calculate texture coordinates of light in vertex shader, they are not interpolated correctly and become distorted in final result, so then I put perspective divide of texture coordinate into fragment shader and it works fine now.
Background become distorted when camera come closer
Works fine when doing perspective divide in fragment shader
Finally, by using instanced draw, I can draw as many as 10000 small point lights (radius varies from 10 to 30) in scene with decent frame rate on my GTX 2070 lap-top.
8000 point lights
10000 point lights
10000 point lights with attenuation of light calculated by distance from light source
After implemented multi local light shading, I start working on global parallel light shadowing. Following the instructions form CS541, I rendered a depth map of scene from the perspective of light with orthogonal projection matrix onto a 1024 x 1024 depth texture in a shadow render pass
Then in the deferred render pass, I re-project view space position of pixels back to world space using inversed view matrix of camera, then project it into light space and doing comparison with sampled depth. If sampled depth(plus a small epsilon) is smaller than the pixel depth, then set attenuation as 0, otherwise 1.
Rendering result
Seems like the resolution of depth texture is not big enough, so I set it to 4096 x 4096.
4096 x 4096
The edge still looks hackly, so I added a PCF filtering by sampling nearby pixels of shadow depth texture
20 x 20 sampling
For the deferred shading, I also used stencil test to avoid overdraw. To do that, I used a depth-stencil texture instead of using pure depth texture. In the offline pass, I rendered the scene with stencil write setting and in the deferred passes, I rendered the fill-screen rect and point light spheres with stencil compare setting. Since the depth-stencil texture should be used as both attachment and sampler in same pipeline, I set the layout of the texture as depth-stencil read only.
roof position rendered with stencil test
roof position rendered without stencil test