Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

DarkBASIC Professional Discussion / Tutorial: A simple shadow shader

Author
Message
Fallout
21
Years of Service
User Offline
Joined: 1st Sep 2002
Location: Basingstoke, England
Posted: 6th Jul 2012 21:00 Edited at: 6th Jul 2012 23:05
Ok, so a few people have been asking me how I did the shadow shading in Carnage. I promised to write a kind of tutorial, so here it is. This isn't a particularly attractive shader and there are many better examples on the forum. The purpose of this is just to help you understand how it all works.

Firstly, thanks to Evolved for all his work which is where I leant all the basics from. But having said that, his shaders are really complicated because they're packed full of cool normal mapping and the like. When I finally tore them apart, I was surprised to see just how simple the actual shadow shading bit is. It really doesn't take many lines at all.

So without further ado, here's a brief explanation of how to write a very basic shadow shader.

BASIC STEPS
The basic steps in getting a shadow onto any scene are these ...

1. Make some objects and set them to use your shadow shading FX/DBS file
2. Make an additional camera which renders the world from the light's perspective to a 'depth texture'. So imagine you are the light bulb and you're looking at the scene. Save that view to an image, with a kind of depth based colour effect.
3. Texture all your shaded objects with this depth texture at stage 1. Stage 0 has their normal texture, but stage 1 has this depth texture applied, so they know how the light views the world.
4. Render the normal camera, taking into account how the light views the world to figure out where the shadows are.

DBP Source Code



So that's the basic concept. If you open up the attached project, you should be able to run through the source code and see how it's done in DBP. I'll highlight the main bits here, with an explanation.

Lines 27-29. Any object that is going to cast a shadow or have a shadow cast upon it needs to have the shader applied.

Line 33. Make a camera which will render from the perspective of the light and make the depth texture.
- Set the range so that the maximum distance is the furthest away object we'll see. This ensure we get the maximum contrast in our depth texture.
- Set the FOV to whatever you want. The more you capture the more of your area will be shadows, but at the expense of shadow resolution.
- Colour the backdrop to red so that it blends correctly with the depth data generated.

Line 36-39: Make our depth texture image. Camera 1 will render to this image so will never render to screen.

Line 42-44: Set the texture stage 1 to our newly created depth texture. Now all our shaded objects will always have the latest depth texture from the lights perspective applied to them for analysis.

In the main loop ...

STEP 1
Perform all your logic which positions your objects first. One this is complete ...

STEP 2
Where ever you want the imaginary shadow shading light source to be, positing camera 1 here. Then look at the scene that you want to be in shadow. Once this camera is in position and looking the right way, we can calculate a projection matrix for the shadow light. This basically describes how the light views our objects.

STEP 3
The next step is to render what the light sees in a red shaded pattern. We do this by setting the effect technique of the shader to the "GetDepth" technique. We give the shader the light matrix we just calculated and then tell DBP to render the light camera.

The result of all this is an image which is a view from the light source, shaded in red with BLACK pixels being really close to the light, and RED pixels being really far away from the light.



STEP 4
Now we want to render our final image ... the image the player sees. We set the shader effect back to the normal render and we sync camera 0.

Hey presto, our scene is rendered!




The DBS Shader File



I won't go into much detail about all the complexities of shaders. I've tried to put loads of comments in there to help clarify it, but essentially what happens is this ...

Depth Render
Firstly our DBP program asks for a depth render for the light (STEP 3 above), by setting the GetDepth technique and then rendering the light camera. When that happens, the GPU runs our shader and first calls this function:
OUT_DepthRender VS_Depth(IN_DepthRender IN)

This is the vertex shader. This takes the vertex data for our objects and translates it into something that looks right from the light's perspective. It saves this info and how far this vertex is away from the light. Next the GPU calls ...

float4 PS_Depth(OUT_DepthRender IN) : COLOR

This is the pixel shader. This takes the vertex data calculated before and renders all the pixels which get saved to our depth image. It creates a smooth red representation of what the light sees, using how far the objects are away from the light source to determine how dark the red colour is.

Remember, this red depth image from the lights perspective is attached to all our objects already in texture stage 1. This pixel shader is essentially just updating that texture for them.


Normal Render
Later, when DBP sets the effect technique back to "NormalRender" and updates the camera (STEP 4 above), the GPU runs this function in the shader ...

OUT_NormalRender VS_Normal(IN_NormalRender IN)
This is the vertex shader for the normal technique. Again, it takes our objects vertex data and translates it into useful data. It figures out how the normal camera views the model and how the light views the model, and saves this out for the pixel shader to use. It also passes on the UV coordinates.

Next the GPU runs ...
float4 PS_Normal(OUT_NormalRender IN) : COLOR
This is the final stage which creates the image we see on screen. You'd expect it to be really complex, but it's not!
- Firstly the distance the pixel is from the from the light is smoothed out into a nicer value.
- Then the key tex2Dproj function is run. This looks at the depth texture stored in stage 1 for the object and asks "if the light is looking at this point in the world, what can it see? Gimme that pixel!"

So right now we have a pixel on the depth texture which describes what the light can see. Perhaps this is an obstruction? Perhaps not? Either way, it's red shade describes how far this object is from the light. AND we also have a distance for how far the pixel we want to draw is away from the light.

With that info we can say that if the object the light can see is closer to it than the pixel is, then the pixel must be obscured from the light. If this is the case, draw it in shadow.

The last line (125) does just that, sampling a colour from the object's base texture, and either drawing it in it's normal colour, or drawing is slightly darker.


Conclusion
So that's about it! There are quite a few steps involved, but the actual code is really quite simple. It's just understanding all the steps involved in drawing shadow pixels and putting them all together. One caveat is there is no smoothing or anything going on here, so the shadows look pretty ugly, but I wanted to keep this all simple.

Hopefully this has been some help to people trying to get their head around shadow shading. I'll try and answer any questions people have.

Attachments

Login to view attachments
Fallout
21
Years of Service
User Offline
Joined: 1st Sep 2002
Location: Basingstoke, England
Posted: 6th Jul 2012 21:01 Edited at: 6th Jul 2012 21:06
Reserved for tutorial image.

Attachments

Login to view attachments
Fallout
21
Years of Service
User Offline
Joined: 1st Sep 2002
Location: Basingstoke, England
Posted: 6th Jul 2012 21:01 Edited at: 6th Jul 2012 21:03
Reserved for another image.

Attachments

Login to view attachments
Matty H
15
Years of Service
User Offline
Joined: 7th Oct 2008
Location: England
Posted: 7th Jul 2012 15:55
Great, thanks Fallout.

I am going to work through this today, I'll post the GDK source code when I am done.

Fallout
21
Years of Service
User Offline
Joined: 1st Sep 2002
Location: Basingstoke, England
Posted: 7th Jul 2012 16:34
Good plan. I thought I'd do it in DBP so it reaches the widest audience, but GDK source would be a great addition.

Matty H
15
Years of Service
User Offline
Joined: 7th Oct 2008
Location: England
Posted: 7th Jul 2012 16:47
GDK source:



Works great, now I'm gonna read through it and see how it works.

I commented out a couple of lines, when you set the camera to an image it creates the image for you, you created a bitmap as well which does not seem to be necessary?

Fallout
21
Years of Service
User Offline
Joined: 1st Sep 2002
Location: Basingstoke, England
Posted: 7th Jul 2012 17:01
Yeah, you're right on both counts. I put that together quite quickly though. Must admit, I didn't know the cam command make the image for you. Good to know as that makes more sense.

Pincho Paxton
21
Years of Service
User Offline
Joined: 8th Dec 2002
Location:
Posted: 7th Jul 2012 17:19
I just get a black screen with the DBPro version.

Fallout
21
Years of Service
User Offline
Joined: 1st Sep 2002
Location: Basingstoke, England
Posted: 7th Jul 2012 18:04
That's strange. I've tested it with an old version of DBP, then just upgraded to the newest and it works fine too. I also tried making it so the DBS file wasn't found, that pops up an error box. So the code is fine and works with the most recent DBP.

Only possible problems I can think of are the SET DISPLAY MODE command isn't working on your monitor. Have you got some crazy widescreen that can't support old skool 1024x768? Or have you perhaps got a really old GFX card that doesn't support pixel shader 2.0?

Is the screen actually black, or is it blank blue (backdrop colour)?

Dar13
15
Years of Service
User Offline
Joined: 12th May 2008
Location: Microsoft VisualStudio 2010 Professional
Posted: 7th Jul 2012 18:55
@Fallout
Have you put this in the Codebase yet? Because I think this would be a great thing for the community to have stick around, even after this thread dies off eventually(as all threads do).

Matty H
15
Years of Service
User Offline
Joined: 7th Oct 2008
Location: England
Posted: 7th Jul 2012 19:01
@Pinch Paxton

It might be that your computer is really slow creating the textures, my laptop takes forever and it's just a black screen for ages Try loading some textures instead.

Pincho Paxton
21
Years of Service
User Offline
Joined: 8th Dec 2002
Location:
Posted: 7th Jul 2012 20:35
Quote: "
It might be that your computer is really slow creating the textures, my laptop takes forever and it's just a black screen for ages Try loading some textures instead."


That fixed it!

Olby
20
Years of Service
User Offline
Joined: 21st Aug 2003
Location:
Posted: 7th Jul 2012 20:48 Edited at: 7th Jul 2012 20:48
Quote: "It might be that your computer is really slow creating the textures, my laptop takes forever and it's just a black screen for ages"


That's odd, how old is your laptop? It creates textures instantly on mine.


Intel Core2Duo 2.2GHZ, 2GB, GeForce 8600M GT 1280MB, Windows Vista Ultimate SP2, PureBasic 4.61 + DarkGDK 2.0
Fallout
21
Years of Service
User Offline
Joined: 1st Sep 2002
Location: Basingstoke, England
Posted: 7th Jul 2012 20:55 Edited at: 7th Jul 2012 20:56
Woah! I didn't see that being the problem! That DOT command really is slow as hell.

@Dar13 - Good shout. I will do that.

Pincho Paxton
21
Years of Service
User Offline
Joined: 8th Dec 2002
Location:
Posted: 7th Jul 2012 21:10
If somebody wants the version without a dot command here it is...

Attachments

Login to view attachments
Matty H
15
Years of Service
User Offline
Joined: 7th Oct 2008
Location: England
Posted: 7th Jul 2012 22:23
Quote: "That's odd, how old is your laptop? It creates textures instantly on mine."


It's a couple of years old.

I noticed certain rare issues with speed on this laptop and people on the forum seem to have similar issues from time to time. A large x file can take up to 90 seconds to load, but converted to dbo it takes a few seconds.

My Desktop computer was not faster than my laptop in other respects but never had this issue.

This is why you should avoid certain commands and save all your media as dbo, if you plan on distributing your game that is

Fallout
21
Years of Service
User Offline
Joined: 1st Sep 2002
Location: Basingstoke, England
Posted: 7th Jul 2012 22:58
Well I'll definitely strip out the DOT before I stick this in the codebase. I knew it was slow, but I would've expected the delay to be seconds, making it unusable for realtime ... not WEEKS.

Btw, the main thing missing from this example is the smoothing. In order to smooth the shadow, you just need to run the tex2DProj command for multiple locations and generate an averaged out darkness/lightness value. The more samples the better, but I also think it's one of the more GPU intensive commands in the shader.

Olby
20
Years of Service
User Offline
Joined: 21st Aug 2003
Location:
Posted: 8th Jul 2012 15:50
Quote: "A large x file can take up to 90 seconds to load, but converted to dbo it takes a few seconds."


I recall Lee once said that ALL models are converted to DBO internally. So that would be just a conversion performance issue rather than your laptop specs affecting the application.


Intel Core2Duo 2.2GHZ, 2GB, GeForce 8600M GT 1280MB, Windows Vista Ultimate SP2, PureBasic 4.61 + DarkGDK 2.0
Matty H
15
Years of Service
User Offline
Joined: 7th Oct 2008
Location: England
Posted: 8th Jul 2012 23:08
Quote: "I recall Lee once said that ALL models are converted to DBO internally. So that would be just a conversion performance issue rather than your laptop specs affecting the application."


I understand that.

My guess is that when an x file is loaded, all vertices for all animation frames are looped through to calculate extra stuff that dbo holds, perhaps object bounds etc.

Loading a dbo means all that stuff is pre-calculated so no extra overhead.

I am highlighting the fact that people don't realise how slow this process can be. My desktop computer did not seem to slow down at all even though the specs were about the same as my laptop. So I never thought there was much difference between x and dbo until I bought my laptop

Andrew_Neale
14
Years of Service
User Offline
Joined: 3rd Nov 2009
Location: The Normandy SR-2
Posted: 12th Jul 2012 21:39
Thanks very much for putting this up. It seems like a pretty nice implementation and the demo, once set to run with an unlocked frame rate and 1920 x 1080 screen resolution, runs at an absurd 2400 frames per-second which is just epic! Will definitely have to see how this looks in my project as it should boost my frame rate quite a bit compared to my current shadow shader.

Quote: "Firstly, thanks to Evolved for all his work which is where I leant all the basics from."

Exactly the same way I've learned and I'm sure there are many more people here who can say the same!


Previously TEH_CODERER.
Matty H
15
Years of Service
User Offline
Joined: 7th Oct 2008
Location: England
Posted: 19th Jul 2012 00:23
I am trying to add this shadow shader to another shader I use for my terrain.

I have started by just adding the depth render part but it's not working. After hours of messing I decided to simplify the depth part to just output green, still no joy.

The shader compiles because my objects work great using the existing technique but they disappear when I change technique to depth.




It's long but most of it works and the technique that does not work (depth) is really small.

Since I am new to this I may be using multiple techniques wrong. Or since my shader is fx while Fallouts original is dbs perhaps the syntax is wrong somewhere?

Thanks to anyone who has time to take a quick look.

Green Gandalf
VIP Member
19
Years of Service
User Offline
Joined: 3rd Jan 2005
Playing: Malevolence:Sword of Ahkranox, Skyrim, Civ6.
Posted: 19th Jul 2012 01:21
Your Depth vertex shader should use the World View Projection matrix from the point of view of the depth camera which should be in the same position as the light - at the moment it seems to use the World matrix which will give strange results if anything. I haven't checked the rest.

Dbs and fx files use the same HLSL syntax.
Fallout
21
Years of Service
User Offline
Joined: 1st Sep 2002
Location: Basingstoke, England
Posted: 19th Jul 2012 10:27
Woah! Chill out Fallout. I almost had a go at Green Gandalf. I missed Matty's post, and just saw GG's, and thought he was telling me my shader didn't work and I was doing it wrong, having just looked at a single line of code.

@Matty - Yep, GG is right.

Quote: "OUT_DepthRender OUT;
float4 WorldVertexPosition = mul(IN.VertexPosition,w);"


Up to here, you've taken your vertex data for your model and translated it using it's translations. So you've got it's rotation and position in 3D space, and translated the vertex data in the model into position. This isn't enough to display it though. The camera still needs this data translated into it's view and projection (where it's looking, and it's FOV/depth etc).

So you need ...
Quote: "float4 LightProjectionVertexPosition = mul(WorldVertexPosition,LightProjMatrix);"


Now the vertex data is translated correctly for the light camera.

Just outputting green in the pixel shader isn't enough, because if the object never actually makes it on screen (because it's vertex data isn't correctly translated for the camera), there will be no geometry on screen to draw green.

If the code wasn't working for you, my best guess would be the 'LightProjMatrix' you pass in wasn't being calculated properly, or wasn't being passed in every loop. Check the source code part of the example to see how the ViewProjection matrix is calculated from the camera and then later past to the shader.

Green Gandalf
VIP Member
19
Years of Service
User Offline
Joined: 3rd Jan 2005
Playing: Malevolence:Sword of Ahkranox, Skyrim, Civ6.
Posted: 19th Jul 2012 13:24
Quote: "So you need ...

Quote: "float4 LightProjectionVertexPosition = mul(WorldVertexPosition,LightProjMatrix);""


Why do you need that? Just use the world view projection matrix for the light camera. You'll need the light camera projection matrix when you use the depth map but not when you create it. Perhaps you are rendering the depth map a different way from the way I've done it? I haven't checked out your demo yet. Perhaps it's time I did?
Matty H
15
Years of Service
User Offline
Joined: 7th Oct 2008
Location: England
Posted: 19th Jul 2012 14:07
Thanks for checking this out

The shader was fine, I put my altered shader into Fallouts original project and it worked. So it was something in the code, turned out that loading my skybox shader broke the depth part of fallouts shader

I can't think how this is possible, loading the skybox shader earlier in my code fixed it. If any one knows how this can happen then let me know, so I'm more clued up if something similar happens again

Thanks again, just need to get the shadow part working now.

Fallout
21
Years of Service
User Offline
Joined: 1st Sep 2002
Location: Basingstoke, England
Posted: 19th Jul 2012 14:56 Edited at: 19th Jul 2012 14:56
Could it be the skybox itself? Could the skybox be blocking the light camera so all it's rendering is the skybox? If so, setting the camera mask bits for the skybox will be your friend.

@Green - To be honest, I'm not sure. I've been using that basic principle since I ripped apart Evolved's shaders and used them as a basis. Now I understand how it works, it does seem using the DBP supplied WVP for the light would do the job fine.

My only guess is when I quickly put that together from my Carnage shader, I was ripping out GPU animation code and pixel lighting. It's possible I was using the World coordinate for something else, so was calculating that first, and then applying the lightVP later. Either way, there would be no saving.

I can't remember how Evolved's shader worked exactly, but I think it did the same thing (hence why I did that out of habit). I'm trying to find an efficiency reason (i.e. not duplicated matrix multiplication) but I can't find any. So probably makes sense to, as you said, just multiply by the WVP, rather than world, then VP, which would be slower.

Green Gandalf
VIP Member
19
Years of Service
User Offline
Joined: 3rd Jan 2005
Playing: Malevolence:Sword of Ahkranox, Skyrim, Civ6.
Posted: 19th Jul 2012 16:39
I vaguely recall someone saying that in earlier upgrades DBPro didn't always update some of the rendering matrices correctly. Perhaps Evolved decided to play safe? I haven't encountered the problem myself though.
Chris Ritchie
18
Years of Service
User Offline
Joined: 7th Jan 2006
Location: Norn Iron
Posted: 19th Jul 2012 22:37
Hi, was playing around with this and noticed my shadows disappear when I move the light object farther away. Is there some distance float I can change somewhere to increase it?.

Thanks.


Lead programmer ULIDIA
Fallout
21
Years of Service
User Offline
Joined: 1st Sep 2002
Location: Basingstoke, England
Posted: 19th Jul 2012 22:45
Hi Chris,

Try this line in the DBP source file:
Quote: "make camera 1: set camera range 1,1,20: set camera fov 1,130: color backdrop 1,rgb(255,0,0)"


I should've put those on separate lines to keep it clear. You'll probably need to adjust the 'set camera range' command if you move the light source further away, otherwise everything will move out of range of the light camera.

Chris Ritchie
18
Years of Service
User Offline
Joined: 7th Jan 2006
Location: Norn Iron
Posted: 20th Jul 2012 02:00
Yeah already tried that, didn't seem to make much difference. I assume there is nothing in the shader itself that would determine how far the shadows render?.

Thanks.


Lead programmer ULIDIA
Fallout
21
Years of Service
User Offline
Joined: 1st Sep 2002
Location: Basingstoke, England
Posted: 20th Jul 2012 09:42
The shader shouldn't have anything in it that will affect how far it operates. If you press Return there's a line of code in there that will paste the depth map to screen. If you can't see anything in the depth map, then you know your problem is that the light camera isn't seeing anything in your scene. Probably best to start there. If that's the case, you'll need to double check the camera range and ensure it's actually pointed correctly as all your objects.

Matty H
15
Years of Service
User Offline
Joined: 7th Oct 2008
Location: England
Posted: 20th Jul 2012 11:24
Quote: "Could it be the skybox itself? Could the skybox be blocking the light camera so all it's rendering is the skybox? If so, setting the camera mask bits for the skybox will be your friend."


I think I may have a buffer overrun in my code somewhere, the shadows stop working when I use dbMakeVector4( ) after the shadow shader is loaded, but not before.

Strange behaviour, I will need to go over my project with a fine tooth comb

Matty H
15
Years of Service
User Offline
Joined: 7th Oct 2008
Location: England
Posted: 20th Jul 2012 12:32
Vector4

It seems there could be a problem with 'dbVector4', if I use a new id number each time, or just use the same id number without deleting it then my problems disappear. Something to be aware of perhaps.

To be sure it's a bug I would need to replicate it in a simple manor which I may do if I find the time.

Repeating shadows

I have noticed the shadow repeats itself if you are projecting onto a large object such as a terrain. Is there anyway to turn off this behaviour? I was thinking perhaps a texture flag or something.

If not, do you think just applying the darkness to vertices near the camera could be a solution, if I put a distance check in the shader? This would mean an extra calculation per vertex which I would like to avoid if possible. Or is there another solution?

Thanks.

Fallout
21
Years of Service
User Offline
Joined: 1st Sep 2002
Location: Basingstoke, England
Posted: 20th Jul 2012 13:05
Interesting problem there Matty. It could be that DB passes the memory location to the shader once only, perhaps? Seems odd, but since I reuse my vectors everywhere, I've never noticed it before.

The shadow repeating is an interesting problem. It basically happens when the scene your player can see includes geometry the light camera can't see. Then when the location is mapped to the depth texture, it's mapped to a place outside of the texture and you get either repeating or dark lines, depending on whether you have clamping or wrapping switched on.

The solutions are (which can be combined):

- Always point the light camera at a location that encompasses all of your geometry. So in carnage, I calculate where the center of the normal camera is pointing at in the level, then make the light camera point there.

- Expand the FOV of the light camera so it takes in more scenery, at the expense of depth map precision.

- Add a spot light texture stage. This is a process of adding a texture to your objects which is white in the middle and black at the borders. Set this texture sampler up with CLAMPING. Then, in the tex2dProj part of the pixel shader, perform the same tex2dproj lookup against this texture. The result is a white colour sampled when the object is in the middle of the light camera, all the way to black at the edges, and also black when outside the camera views. You can then multiply this into the darkness calculation, and you'll have a nice spot light effect, with everything outside the range of the range camera black.

- Use a cube map shadow shader, so everywhere in the world is sampled. This is beyond the scope of this tute. Time for Green Gandalf to chime in because he's made a few of those.

Btw, you will need to add more code to that shader anyway to improve it's quality, so I wouldnt be too concerned about adding more instructions. It's going to be needed at some point.

Matty H
15
Years of Service
User Offline
Joined: 7th Oct 2008
Location: England
Posted: 20th Jul 2012 13:32
Ha ha, I solved it by clamping the depth texture

Thanks for information, you helped me realise the cause of the problem here:

Quote: "- Always point the light camera at a location that encompasses all of your geometry. So in carnage, I calculate where the center of the normal camera is pointing at in the level, then make the light camera point there."


And the solution here:

Quote: " This is a process of adding a texture to your objects which is white in the middle and black at the borders. Set this texture sampler up with CLAMPING."


So it was a simple fix, thanks again.

Chris Ritchie
18
Years of Service
User Offline
Joined: 7th Jan 2006
Location: Norn Iron
Posted: 20th Jul 2012 18:58
Thanks for the help fallout, still no luck. I'm starting to think it may be a graphics card thing.


Lead programmer ULIDIA
Matty H
15
Years of Service
User Offline
Joined: 7th Oct 2008
Location: England
Posted: 22nd Jul 2012 00:43
@Chris Ritchie

There is a distance limit in the shader here:



You should make this a tweakable value.

Chris Ritchie
18
Years of Service
User Offline
Joined: 7th Jan 2006
Location: Norn Iron
Posted: 22nd Jul 2012 22:47
That's done the trick thanks Matty


Lead programmer ULIDIA
Fallout
21
Years of Service
User Offline
Joined: 1st Sep 2002
Location: Basingstoke, England
Posted: 22nd Jul 2012 22:58
Balls. I missed that while copying and pasting that from my Carnage shader. Good spot Matty!

Login to post a reply

Server time is: 2024-04-16 07:53:37
Your offset time is: 2024-04-16 07:53:37