[video output=day400 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][:speech]
[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][:speech]
[/video]
