Such a well explained and informative video. Thank you for the time spent to make it. Shaders are a complex topic , but you are doing great at explaining.
There is an elegant way to express a backward iterating loop which can be used for the u and v. Use "for (int i = loop_count; i--;) { ... }, This has the exact same effect as in the example shown around 11:45 in the video. Once you are used to it, you'll always immediately recognise it as a backward counting loop from loop_count - 1 through 0.
Hello. In the fragment shader we don't need to use uniform variable gShadowMapOffsetTextureSize because this information can be retrieved with textureSize(gShadowMapOffsetTexture, 0).yz. Also in the for loop where we populate our filters with offsets there's no need for double loop for window_pos on x and y axis because we don't use it anywhere, we could only do: for (int window_pos = 0; window_pos < window_siz * window_siz; ++window_pos) which makes it a little bit more readable. Also I might be wrong but in fragment shader it seems fishy that when we look at the vector we use for for texelFetch, then we basically get vec3(i, OffsetCoord.yz), because y and z axis of our sampler3D object corresponds to our window height and width, so OffsetCoord.y should be used for z axis and OffsetCoord.z for y axis so in short vec3(i, OffsetCoord.zy). I know it doesn't change anything because in this implementation offset windows are squares, but just wanted to point out. Overall this video helped me a lot to implement this myself and the trick with rotating the data structure of offsets with comparison to what was used in nvidia article to make for loops a lot easier to read is amazing. Thank you for the video it helped me a bunch.
You're welcome! Thanks for the feedback, you may be right on the YZ vs ZY issue but it's been so long since this video was published so I need to spend some time to look into this...
Great stuff! I have an odd question, would you consider doing a tutorial explaining the theory and implementation of LTC (Area lights?) There's an article about it in learnOpenGL but I found it quite hard to understand since it's very math heavy.
this shadow maps technique is very cool and sustainable, but I wanted something more geometric, like shadow volume, could you make a tutorial video about it?
I've added this topic to my todo list. In the meantime you may want to check my web based tutorial on stencil shadow volumes: ogldev.org/www/tutorial40/tutorial40.html
Great tutorial! Link to source (in description) seems to be broken. It's not a big problem, I just found it manually, but I thought that pointing it out to you would be useful :)
Thank you very much! Random Sampling was interesting for me. I was able to smooth shadow with only 4 samples, each taking it's own 4 samples for a bilinear interpolation. This is done using sampler2DShadow. Which require setting it on over the texture settings. The problem is, it would have a blocky edges like a pixel. and there are no depth interpolation. I wonder if random sampling fixed both issues?
I am interested in how effective the check for if the fragment is completely in or out the shadow is in your soft-shadow implementation. I read in a couple sources, that graphics cards are horrible at branching and therefore just run all if-branches at the same time and then pick the result from the correct one. Wouldn't that mean, that the first for loop in your fragment shader to check the sum to be all 0 or 1 is just twice the work necessary?
According to the original article by NVIDIA (see link in the video description) there is true dynamic branching since the Geforce series 6. The GPU processes thousands of pixels in parallel as long as they run the same piece of code so if some pixels take path A while others take path B they cannot be processed together so this hurts parallelism but the assumption is that in a local neighborhood all pixels will take the same path so the effect on parallelism will be negligible. Please see the article for more details.
You can make the radius of the circle dependent on the distance from the object. In the case of shadow mapping we have the depth of the pixel and the depth in the shadow map. Quickest way is to make the radius a function of the delta between them. Or extrapolate the original depth and do something similar.
Great video like always. I have a problem with the second approach though. The more I increase the radius the more I get a border around the shadow darker than the rest of the shadow and the sharp points of the mesh appear duplicated. Do you know why I get this? Once more thanks for the video!
@@OGLDEV Thanks for the reply! Here is what I've tried so far : window 8, filter 4, radius 7 & window 16, filter 8, radius 15 as you suggested. On the second (radius 15) the problem is more obvious. I only get the normal shadow with radius = 1. I could make a screenshot and post it somewhere if that helps
Your tutorials are top-notch. It's a shame I only just found your channel.
Better late than never - glad to have you on board :-)
Such a well explained and informative video. Thank you for the time spent to make it. Shaders are a complex topic , but you are doing great at explaining.
Glad it was helpful!
Thanks for the very detailed explanation about the soft shadows. Will be really useful. Hope to see more great tutorials like this.
You're welcome :-)
If you liked how that works, you'll also like how corner detectors in computer vision work. Some use that same ring approach. Happy coding everyone :)
There is an elegant way to express a backward iterating loop which can be used for the u and v. Use "for (int i = loop_count; i--;) { ... }, This has the exact same effect as in the example shown around 11:45 in the video. Once you are used to it, you'll always immediately recognise it as a backward counting loop from loop_count - 1 through 0.
Thanks, cool tip!
I have not yet entered the realm of shadowmapping. Though, the first method looks pretty simple. As always, nicely explained 👌
Thanks!
Hello. In the fragment shader we don't need to use uniform variable gShadowMapOffsetTextureSize because this information can be retrieved with textureSize(gShadowMapOffsetTexture, 0).yz. Also in the for loop where we populate our filters with offsets there's no need for double loop for window_pos on x and y axis because we don't use it anywhere, we could only do: for (int window_pos = 0; window_pos < window_siz * window_siz; ++window_pos) which makes it a little bit more readable. Also I might be wrong but in fragment shader it seems fishy that when we look at the vector we use for for texelFetch, then we basically get vec3(i, OffsetCoord.yz), because y and z axis of our sampler3D object corresponds to our window height and width, so OffsetCoord.y should be used for z axis and OffsetCoord.z for y axis so in short vec3(i, OffsetCoord.zy). I know it doesn't change anything because in this implementation offset windows are squares, but just wanted to point out.
Overall this video helped me a lot to implement this myself and the trick with rotating the data structure of offsets with comparison to what was used in nvidia article to make for loops a lot easier to read is amazing. Thank you for the video it helped me a bunch.
You're welcome! Thanks for the feedback, you may be right on the YZ vs ZY issue but it's been so long since this video was published so I need to spend some time to look into this...
Thank you so much for making these! Not enough OGL resources! Do you have PBR planned for the future?
You're welcome :-). PBR is in the todo list.
Great stuff! I have an odd question, would you consider doing a tutorial explaining the theory and implementation of LTC (Area lights?) There's an article about it in learnOpenGL but I found it quite hard to understand since it's very math heavy.
I will add this to my TODO list but it will take some time to get there because there is already a long roadmap.
@@OGLDEV I'd be eager to watch :)
this shadow maps technique is very cool and sustainable, but I wanted something more geometric, like shadow volume, could you make a tutorial video about it?
I've added this topic to my todo list. In the meantime you may want to check my web based tutorial on stencil shadow volumes: ogldev.org/www/tutorial40/tutorial40.html
Great tutorial! Link to source (in description) seems to be broken. It's not a big problem, I just found it manually, but I thought that pointing it out to you would be useful :)
Thanks for the feedback! I fixed it.
Hi! Great tutorial! Are you planning to make a tutorial on cascaded shadow mapping? It would be also a useful tutorial 🙂
It's on the road map...
In the meantime you can try the CSM tutorial on my website: ogldev.org/www/tutorial49/tutorial49.html
@@OGLDEV Great, thanks again! 🙂
Thank you very much!
Random Sampling was interesting for me.
I was able to smooth shadow with only 4 samples, each taking it's own 4 samples for a bilinear interpolation.
This is done using sampler2DShadow. Which require setting it on over the texture settings.
The problem is, it would have a blocky edges like a pixel. and there are no depth interpolation.
I wonder if random sampling fixed both issues?
I think that it does but you will need to try it out and tune the parameters.
wonderful!
Many thanks!
Can you do more advance rendering series?
Yes, every once in a while there will be a tutorial like that.
I am interested in how effective the check for if the fragment is completely in or out the shadow is in your soft-shadow implementation.
I read in a couple sources, that graphics cards are horrible at branching and therefore just run all if-branches at the same time and then pick the result from the correct one.
Wouldn't that mean, that the first for loop in your fragment shader to check the sum to be all 0 or 1 is just twice the work necessary?
According to the original article by NVIDIA (see link in the video description) there is true dynamic branching since the Geforce series 6. The GPU processes thousands of pixels in parallel as long as they run the same piece of code so if some pixels take path A while others take path B they cannot be processed together so this hurts parallelism but the assumption is that in a local neighborhood all pixels will take the same path so the effect on parallelism will be negligible. Please see the article for more details.
@@OGLDEV Thank you!
Shadows in real life are sharper close to the shadowing object, and get blurrier and blurrier away from the object. Would that be hard to implement?
You can make the radius of the circle dependent on the distance from the object. In the case of shadow mapping we have the depth of the pixel and the depth in the shadow map. Quickest way is to make the radius a function of the delta between them. Or extrapolate the original depth and do something similar.
Great video like always. I have a problem with the second approach though. The more I increase the radius the more I get a border around the shadow darker than the rest of the shadow and the sharp points of the mesh appear duplicated. Do you know why I get this? Once more thanks for the video!
What values did you use for the windows size, filter size and radius?
@@OGLDEV Thanks for the reply! Here is what I've tried so far : window 8, filter 4, radius 7 & window 16, filter 8, radius 15 as you suggested. On the second (radius 15) the problem is more obvious. I only get the normal shadow with radius = 1. I could make a screenshot and post it somewhere if that helps
I can't reproduce the problem with these settings. Can you please send me a screenshot at ogldev1@gmail.com? Thanks.
@@OGLDEV Thanks a lot for taking the time to look at the problem. I've sent you 2 screenshots and my fragment shader code