 We've made it to section 4 advanced open gel and looking first now at example 1.2 depth testing view What we see here in this scene is that for every pixel what we're displaying is its depth value a grayscale value in the range of zero to one Where we get zero black for pixels that are on the near clipping plane One full white for pixels on the far clipping plane and everything else is proportional in between So we're effectively visualizing the Z values of our pixels as you can see as I move away Things get wider until they get cut off by the far clipping plane and as I get closer they will get darker To achieve this effect in the fragment shader. There are multiple solutions here. I have three Each of them computes a depth value here depth one depth two and depth three And then we take that depth value Which is a value between zero and one and we set the frag color to have the rg and be all equal to that depth value So we get a grayscale value The first solution is I think both the easiest and the simplest We are simply from the fragment shader getting the view space coordinate of the pixel Get that z component and because in open gel the camera looks down the negative z axis in the distance Z values get more negative. So we want to flip its sign and Define where that value lies in the range of near to far We subtract out near and divide it by far minus near and now we have a value between zero and one that reflects the depth of that pixel In solution two, it's a bit more complicated But now we don't need view position instead We're using z of screen space which we get in the special variable gel frag cord and so with this We simply work backwards. We first compute the ndc equivalent of our screen space z by multiplying by two and subtracting out one We then go back from ndc to clip space by dividing by the w of screen space frag cord w because if you recall in clip space We set the w value to be the negative z of view space But then in the conversion to ndc The w value becomes one over that w and that then is copied verbatim to screen space So the z value of clip space gets divided by the w of clip space to get us to ndc But now in screen space we have one over w so to go backwards instead of multiplying by w We actually divide it because it's actually one over the w of clip space And so that gets us the z of clip space and now to find the depth value It's simply a question of where does the clip lie in the range of negative near to positive far So we add in the near and then divide near plus far Getting us a value that should be between zero and one So note the fundamental issue here The z we have in screen space is not linear to the z's of view space because when we go from clip space to ndc The z's are all divided by the w In solution two we're fixing that by undoing that division effectively In solution one we just don't have this problem because we're dealing with z before the perspective division Solution three is the solution offered on learnopenjail.com. It's the original author's solution And like solution two it also starts by finding the z of ndc seen here But strangely it doesn't involve any division by the w but it does still get us a z value that is linear to view space This here gets us a value in the range of near to far and then we simply divide by far to get our depth value I honestly can't explain exactly why this works But if we do a spot check for values of z ndc here if we assume our near value is one and our far value is ten Well for this actually should be negative one for negative one the smallest possible ndc value You get one which is equivalent to our near clipping plane value Which is what we should expect for positive one we get ten which is equal to our far plane But then for values in between notice that the mapping is not linear negative point five is one point three zero gets us One point nine zero point five gets us three point one So you can see that it is effectively undoing the nonlinear transformation from clip space to ndc I looked around and couldn't find a good explanation for why it works this way So I'd be interested if anyone can point to a proper proof for this Anyway, it can be shown to work properly if here we set the output to what we get from depth three and then I rebuild Run it again, and we get what looks indistinguishable to me What we see here in example two is a use of the stencil buffer The stencil buffer is a per pixel buffer like the depth buffer that is usually on most platforms eight bits in size And so it stores integer values from zero to two fifty five And what we can do with the stencil buffer is enable a stencil test whereby for each pixel We compare a value with what's already in the stencil buffer for that pixel and if the test passes only then Do we draw into the frame buffer? Otherwise we discard the pixel And so what's happening here is we've drawn first the floor here as normal But then when we draw the textured boxes the gray boxes you see For those pixels we wrote the value one into the stencil buffer and then we draw the boxes again But we scale them up and use a different shader this one that just draws a solid color But the second time we draw the boxes We apply a stencil test that only passes for pixels where the stencil buffer is not equal to one And so effectively the second time we draw the boxes It's not overriding the textured boxes the first time we drew and so you get this outline effect So now to work with the stencil buffer First you may want to query how large the stencil buffer is on your platform It's almost always going to be eight bits But if you want to be sure you can use this code to query that and so here we're printing out the value It involves querying a parameter of the frame buffer We haven't talked about frame buffers that will do that quite soon But anyway, this is how we get the number of stencil bits The function stencil up here as in stencil operation Specifies what happens to the stencil buffer value in the case of the stencil test and death test either passing or failing And the first argument here is specifying the action in the event of the stencil test failing The second argument specifies what we do in the event the stencil test passes But the depth test fails and the third argument specifies what to do in the event of both the stencil and death tests passing In which case then also the fragment shader will run So for our purposes, we're going to only want to write to the stencil buffer in the event of both tests passing So that's why this third argument is replaced. But otherwise we just want to keep the current value We don't want to modify it. Now the value that replaces what's in the buffer that is specified by another call We'll see later Down in the render loop, we're going to want to make sure to clear the stencil buffer every frame So we add in this flag in the call to GL clear and the function clear stencil Specifies what value we clear the stencil buffer to in this case. We're clearing to zero, which I believe is the default So now when we draw first, we're drawing the floor and when we do so we want to disable any stencil testing Because we want the drawing to be unconditional and we don't want to write into the stencil buffer But then when we draw the cubes we want to enable stencil testing Not because we want the drawing to be conditional But because we want to write into the stencil buffer To specify the value that's written and what the comparison test is we call stencil funk The first argument specifies the comparison test in this case always meaning that the test passes unconditionally The other options here are never equal not equal less than greater than less than or equal and greater than equal But in this case, we just want the test to always pass And the second argument here is the so-called ref value This is what's used in the comparisons if we had a comparison function But it's also the value written into the buffer when our action is jail replace as we set in the call to stencil op at the top The third value here is a bit mask argument And in the comparisons between the ref value and the current value in the stencil buffer Both of them get ended with this bit mask Before the comparison is performed Because our comparison function is always this is effectively irrelevant here, but in other cases there are scenarios where this could be useful Anyway, having enabled stencil testing and set up the stencil function When we now draw the cubes for each pixel of the cubes the stencil buffer value is going to get set to one And so when we draw the cubes again after scaling them up and using a different shader to just draw a solid color Now we want a stencil function that only passes for stencil values other than one So our comparison is not equal and a rough value is still one And incidentally for pixels where this passes where the outline is drawn The stencil buffer value is going to get set to one, but that doesn't really matter here because immediately next frame We're going to clear the stencil buffer. So that's not a problem We could though if we wanted to disable writing to the stencil buffer by calling a function named stencil mask But it's not really necessary here Anyway, note that because we want the outline drawn on top of everything else on top of the floor We're disabling the depth test After drawing the outline, we just need to make sure to re-enable it down here So this is just one example of what we can do with the stencil buffer You can use it in various ways to mask out drawing of certain pixels And achieve various different effects such as this outline