[video member=cmuratori stream_platform=twitch stream_username=handmade_hero project=code title="Adding an Ambient Occlusion Pass" vod_platform=youtube id=-BNki3w_HEE annotator=Miblo] [0:01][Recap and set the stage for the day] [1:29][Run the game to see the lighting diamonds] [5:31][handmade_render_group.cpp: Make LightingTest() correctly sum the Dest->FrontEmitC] [5:55][Run the game to see the correctly summed colours] [6:08][handmade_render_group.cpp: Make LightingTest() account for the DistanceFalloff] [7:16][Run the game with DistanceFalloff and summation, and consider our way forward] [8:17][handmade_render_group.cpp: Make LightingTest() set DiffuseInner to 1.0f] [8:35][Run the game to see the flickering] [9:02][Determine to introduce the ability to add light sources] [12:27][handmade_platform.h: Add Emission to the textured_vertex struct] [14:11][Run the game to see that nothing bad happens] [14:30][handmade_render_group.cpp: Make PushQuad() and LightingTest() set the emission] [16:40][Run the game to see everything unlit] [16:47][handmade_render_group.cpp: Introduce PushLight()] [19:23][handmade_world_mode.cpp: Make UpdateAndRenderWorld() call PushLight()] [20:59][Run the game and see a cube light that is not emitting any light] [21:28][handmade_render_group.cpp: Make PushQuad() take Emission] [22:27][Run the game to see our emitting light, with less flickering, but that the light itself is not lit up] [24:55][handmade_render_group.cpp: Add a BreakHere in LightingTest() for the light] [25:58][Step in to LightingTest() and inspect the values for the light] [27:59][handmade_render_group.cpp: Make LightingTest() correctly set the FrontEmit] [28:35][Run the game to see our lit light] [29:18][handmade_render_group.cpp: Make LightingTest() iterate three times] [29:29][Run the game to see our much brighter scene, now with flutter] [30:03][handmade_render_group.cpp: Revert LightingTest() to perform one iteration] [30:10][Run the game and consider building a wall] [30:45][handmade_world_mode.cpp: Make AddStandardRoom() add a wall] [33:35][Run the game to see our wall] [35:56][handmade_render_group.cpp: Enable LightingTest() to compute the colours differently in the debug and game view] [39:24][Run the game to see the beautiful game view] [41:19][handmade_render_group.cpp: Consider gathering the light from the cubes' corners rather than their faces] [42:53][handmade_config.h: Add Global_Renderer_Lighting_ShowReflectors and Global_Renderer_Lighting_IterationCount] [45:08][win32_handmade.cpp: Try to add keys in Win32ProcessPendingMessages() to control the reflectors and lighting iteration count] [46:47][handmade_opengl.cpp: Prevent OpenGLRenderCommands() from calling ComputeLightTransport()] [47:12][win32_handmade.cpp: Increase the resolution in WinMain()] [47:34][Run the game to see that it's totally usable for lighting] [47:45][handmade.cpp: Add lighting controls to the debug system] [48:43][Run the game and try out those lighting controls] [49:53][Determine to compute the lighting hierarchically] [52:12][Consult the multipass shadowing algorithm[ref site="NVIDIA Developer" page="Chapter 14. Dynamic Ambient Occlusion and Indirect Lighting" url="https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter14.html"]] [55:07][Run the game and consider the problem of shadowing] [57:10][Blackboard: Comparing reflectors, being an O(n³) problem] [58:51][Read 14.3 Indirect Lighting and Area Lights[ref site="NVIDIA Developer" page="Chapter 14. Dynamic Ambient Occlusion and Indirect Lighting" url="https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter14.html"]] [1:00:26][Run the game and consider an iterative solution across multiple frames] [1:01:56][handmade_render_group.cpp: Start to enable LightingTest() to compute some shadowing] [1:03:57][Consult the disk-to-disk occlusion algorithm[ref site="NVIDIA Developer" page="Chapter 14. Dynamic Ambient Occlusion and Indirect Lighting" url="https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter14.html"]] [1:05:32][handmade_render_group.cpp: Enable LightingTest() to compute the disk-to-disk occlusion[ref site="NVIDIA Developer" page="Chapter 14. Dynamic Ambient Occlusion and Indirect Lighting" url="https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter14.html"]] [1:08:24][Blackboard: Shadow Approx.[ref site="NVIDIA Developer" page="Chapter 14. Dynamic Ambient Occlusion and Indirect Lighting" url="https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter14.html"]] [1:19:58][A mini rant on papers giving partial solutions] [1:22:06][Consider what their saturate function is meant to be doing[ref site="NVIDIA Developer" page="Chapter 14. Dynamic Ambient Occlusion and Indirect Lighting" url="https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter14.html"]] [1:26:31][Blackboard: Understanding their shadow approximation equation[ref site="NVIDIA Developer" page="Chapter 14. Dynamic Ambient Occlusion and Indirect Lighting" url="https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter14.html"]] [1:29:25][handmade_render_group.cpp: Enable LightingTest() to compute this shadow approximation equation[ref site="NVIDIA Developer" page="Chapter 14. Dynamic Ambient Occlusion and Indirect Lighting" url="https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter14.html"]] [1:32:08][handmade_intrinsics.h: Introduce ReciprocalSquareRoot()] [1:32:47][handmade_render_group.cpp: Continue to implement the shadow equation in LightingTest()[ref site="NVIDIA Developer" page="Chapter 14. Dynamic Ambient Occlusion and Indirect Lighting" url="https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter14.html"]] [1:41:28][Blackboard: Occlusion from in front and behind] [1:41:46][handmade_render_group.cpp: Break out the shadow equation values in LightingTest()] [1:45:22][Correcting for occlusion by overlapping objects[ref site="NVIDIA Developer" page="Chapter 14. Dynamic Ambient Occlusion and Indirect Lighting" url="https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter14.html"]] [1:46:43][handmade_render_group.cpp: Enable LightingTest() to iteratively occlude] [1:50:07][Run the game to see the shadowing looking kind of reasonable] [1:51:21][handmade_render_group.cpp: Make LightingTest() perform one shadowing iteration, and run the game to see that the wall's front face is not all black] [1:52:20][handmade_render_group.cpp: Move the Dest->Visibility setting one scope out in LightingTest()] [1:52:39][Run the game to see that the wall's front face is still black here] [1:53:51][handmade_render_group.cpp: Read carefully though the shadowing code in LightingTest()] [1:56:01][handmade_render_group.cpp: Add assertions in LightingTest() and compile in debug mode] [1:56:43][Run the game and fail to hit those assertions] [1:58:44][handmade_render_group.cpp: Assert in LightingTest() that the Visibility >= Dest->Visibility on the first iteration] [1:59:33][Run the game, hit that assertion and consider why] [2:00:37][handmade_render_group.cpp: Add a Shadow in LightingTest() to accumulate based on the Visibility] [2:04:42][Run the game to see our ambient occlusion solution] [2:05:20][Q&A] [2:06:43][@vkar2][square root (A/pi + r²) = square root (r² * ( A/(pi * r²) + 1) = r * square root (A/(pi * r²) + 1)] [2:07:31][Blackboard: Pulling the r out of square root (A/π + r²)] [2:10:36][@alexkelbo][Thank you very much for 400 days of programming. Had I remembered I would've sent you a golden crown to wear today. You're awesome!] [2:10:43][@mtsmox][What do extra iterations do for ambient occlusion? I understand it for bouncing light, but shadows?] [2:10:59][Blackboard: Calculating ambient occlusion over multiple iterations] [2:16:12][@mtsmox][Should the accumulator for the light passes be reset to zero at the start of the next pass?] [2:16:40][handmade_render_group.cpp: Make LightingTest() reset the accumulator] [2:17:50][Run the game to see the current lighting] [2:18:14][handmade_render_group.cpp: Make LightingTest() perform ambient occlusion] [2:18:55][Run the game to see how that looks with ambient occlusion] [2:20:50][handmade_config.h: Increase the Global_Renderer_Lighting_IterationCount and run the game to see how that looks] [2:22:22][@thesizik][It seems like the ground colors are off. Looks like it goes light green > gray > dark green] [2:24:24][handmade_render_group.cpp: Make LightingTest() cast the Element->Visibility calculation to a V4 in the setting of FrontEmit] [2:25:25][Run the game to see how that looks] [2:26:40][Wind it down] [/video]