Sign in to follow this  
Meltac

Math for computing relative sun direction

Recommended Posts

Hi everyone!

I'm trying to implement sun shafts in my pixel shader. So I need to compute the sun direction relative to the screen / view direction.

I got the following globals:

[CODE]
L_sun_dir_w // absolute (world coords) sun direction
eye_direction // direction of the camera
eye_position // position of the camera
[/CODE]

The latter two I'm not sure whether they are absolute or not. However with the right math it should be easy to find out.

What I'm missing now is the math / formula for getting the view-relative sun direction out of those values. Should be a no-brainer for the experienced guys of you, but for me it's quite hard to figure out.

Who can help me with that math?

Share this post


Link to post
Share on other sites
[CODE]
// Transform sun position to screen space
SunPos = mul( SunPos, ViewProj );


float4x4 TexAdj = { 0.5f, 0.0f, 0.0f, 0.0f,
0.0f, -0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f };

SunPos = mul( SunPos, TexAdj );


// Calculate vector from pixel to light source in screen space.
float2 deltaTex = (TexPos - SunPos);
[/CODE] Edited by SIIYA

Share this post


Link to post
Share on other sites
Ok, that code works, except two things that need to be tweaked:

1. On a certain view angle the calculated sun direction abruptly breaks. Seems like some abs() or similar is required at the -180/+180 degrees border of the player view. Any hint what exactly needs to be altered?

2. I figured out that the sun direction provided by the engine is due to some bug about 40 degrees too high positioned in the sky, meaning I need to recalculate the sun direction to come from a sun standing 40 degrees lower than what my L_sun_dir_w value says. How do I transpose/rotate that vector by 40 degrees? Edited by Meltac

Share this post


Link to post
Share on other sites
sun position / direction is set by the game engine I'm using, I'm just trying to fix the game shaders without having access to the engine's functions themselves.

So I have to live with the fact that the engine sends a wrong value for sun position / direction to the shader.

The question how I correct that wrong value within the shader by some math / formula. I just need something like

[CODE]
float3 new_sun_direction = rotate_vector_by_Y_axis(old_sun_direction, 40 /* degrees */ );
[/CODE]

Can you help me with that formula?

Share this post


Link to post
Share on other sites
Sure, I'm so free to quote some other guy who has made some screenshots:

Notice how the telephone pole is blocking the sun where I am standing, which logically means I should be standing in its shadow. But notice where the pole's shadow is. Way in front of me! Nowhere near where I am:

[url="http://i19.photobucket.com/albums/b158/mariner767/sunoutofsync.jpg"]http://i19.photobuck...unoutofsync.jpg[/url]

Here's the sun behind a tree, blocking the sunlight from my view, so I should be in its shadow, but again, the shadow is way in front of me:

[url="http://i19.photobucket.com/albums/b158/mariner767/sunoutofsync2.jpg"]http://i19.photobuck...noutofsync2.jpg[/url]

And a graphic to show what's happening:

[url="http://i19.photobucket.com/albums/b158/mariner767/shadows2.jpg"]http://i19.photobuck...67/shadows2.jpg[/url]

EDIT:
I know from other source that the reason for this lies in the way the engine calculates the sun's coordinates which are passed to the shader. The geometrical horizonal taken for sun light direction calculation is about 40 degrees below the game world's horizon - meaning I'll need a way to correct this shader-wise. A simple vector shift or rotation with these 40 degrees should be accurate enough to make shadows look acceptable. Edited by Meltac

Share this post


Link to post
Share on other sites
I am not surprised as its stalker engine :)....I cant tell you for sure but you could try something like this:

float l = length(sunpos);
sunpos = normalize(sunpos);
sunpos.x *= cos(Angle);
sunpos.z *= sin(Angle);

sunpos *= l;

(do this before the above code)

I am not sure which component(s) should be modified or whether to use sin or cos. Try all combinations :)

Share this post


Link to post
Share on other sites
isnt this function what are you looking for? This rotates (x,y,z) about the axis (u,v,w) by the angle ?.

f(x,y,z,u,v,w,?) =

[img]http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation13x.png[/img]

Share this post


Link to post
Share on other sites
Ok, so far transformation and rotation worked more or less. Now I'm having an issue with this transformation:

[quote name='SIIYA' timestamp='1339064964' post='4947002']
[CODE]
// Transform sun position to screen space
SunPos = mul( SunPos, ViewProj );


float4x4 TexAdj = { 0.5f, 0.0f, 0.0f, 0.0f,
0.0f, -0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f };

SunPos = mul( SunPos, TexAdj );


// Calculate vector from pixel to light source in screen space.
float2 deltaTex = (TexPos - SunPos);
[/CODE]
[/quote]

This works well as long as the sun is near the center of the screen, but the more peripheral it gets (i.e. the nearer to the screen's border it is located) the less accurate the transformation seems to be: The computed sun position in screen space is "too much centered" in comparison to the real visual sun location on the screen.

It seems to me like an issue of linear versus radial transformation, so that the above formula does not seem to take into account that the field of view of the camera is not linear but radial, meaning that when turning the camera for example to the left so that the visible sun moves to the right within the screen (if it was in its center), this "linear" movement needs to take the camera rotation (=radial movement) into account - what seems not to be the case with the above formula.

My problem is that I don't understand the respective math well enough to figure out how to fix this. Can you help me here? What formula needs to be applied to correct this?

Share this post


Link to post
Share on other sites
Wouldn't you want to divide-by-w after transforming by your ViewProj, assuming that it contains a perspective projection?

EDIT: nevermind, I realized that the code is from someone else and it's probably not the code that you're using. Edited by MJP

Share this post


Link to post
Share on other sites
[quote name='MJP' timestamp='1342562435' post='4960206']
EDIT: nevermind, I realized that the code is from someone else and it's probably not the code that you're using.
[/quote]

I'm basically using SIIYA's code:

[quote name='SIIYA' timestamp='1339064964' post='4947002']
[CODE]
// Transform sun position to screen space
SunPos = mul( SunPos, ViewProj );


float4x4 TexAdj = { 0.5f, 0.0f, 0.0f, 0.0f,
0.0f, -0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f };

SunPos = mul( SunPos, TexAdj );


// Calculate vector from pixel to light source in screen space.
float2 deltaTex = (TexPos - SunPos);
[/CODE]
[/quote]

However I've also tried a few other transformations and projection matrices, as well as building my own. The overall result is always the same: The calculated sun position is more or less correct as long as near the screen center, but when moving further to the left or right, it becomes worse. The calculated position then is [b]too much left or right[/b], but when the sun moves towards the left or right border of the screen, the calcuation is [b]not enough left or right[/b].

So there must by some math function responsible for this behavior. If I'd know that function I could correct the calculation. I've tried polynomial functions with pow(...) but that seemed not to be the right one. Might it be some sinus or cosinus function that I'm needing in this case? Or any other correction algorithm?

Share this post


Link to post
Share on other sites
I'm still struggling... I guess the projection of the sun coords from 3D to 2D (world to screen space) is not yet done correctly.

As said, I'm using SIIYA's approach:

[quote name='SIIYA' timestamp='1339064964' post='4947002']
[CODE]
// Transform sun position to screen space
SunPos = mul( SunPos, ViewProj );


float4x4 TexAdj = { 0.5f, 0.0f, 0.0f, 0.0f,
0.0f, -0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f };

SunPos = mul( SunPos, TexAdj );


// Calculate vector from pixel to light source in screen space.
float2 deltaTex = (TexPos - SunPos);
[/CODE]
[/quote]

Might the TexAdj matrix be wrong in my case? Were do these values come from? Also, I don't see any relation to the FOV, isn't that important here?

BTW; a divide-by-w is most propably not possible here (if even required) because all position values I'm getting from the engine are either only float3, or have w = 0.

Share this post


Link to post
Share on other sites
As MJP correctly pointed out, the homogenous clip space coordinates need to be divided by w. So the correct code would look like this:

[CODE]
// Transform sun position to screen space
SunPos = mul( SunPos, ViewProj );

SunPos /= SunPos.w;

float4x4 TexAdj = { 0.5f, 0.0f, 0.0f, 0.0f,
0.0f, -0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f };

SunPos = mul( SunPos, TexAdj );


// Calculate vector from pixel to light source in screen space.
float2 deltaTex = (TexPos - SunPos);
[/CODE]

If your world space SunPos isn't (x, y, z, 1) you need to set w to 1 before the projection.

When I did this, I simply drew the sun on the screen to check if it aligns with the already drawn sun:

[code]float3 color = distance(TexPos, SunPos) < 0.005 ? float3(1, 0, 0) : tex2D(sampler0, input.texCoord).rgb;[/code] Edited by CryZe

Share this post


Link to post
Share on other sites
[quote name='CryZe' timestamp='1352366881' post='4998802']
As MJP correctly pointed out, the homogenous clip space coordinates need to be divided by w. So the correct code would look like this:

If your world space SunPos isn't (x, y, z, 1) you need to set w to 1 before the projection.[/quote]

Thanks, but the SunPos that I'm getting from the engine is a float3, so I don't have a w here! Also, float4(SunPos, 1) obviously doesn't change a thing.

[quote name='CryZe' timestamp='1352366881' post='4998802']
When I did this, I simply drew the sun on the screen to check if it aligns with the already drawn sun:

[code]float3 color = distance(TexPos, SunPos) < 0.005 ? float3(1, 0, 0) : tex2D(sampler0, input.texCoord).rgb;[/code]
[/quote]

I don't want to [i]draw[/i] the sun, but render sun shafts coming from the correct direction where the sun is rendered by the engine is in screen space.

Share this post


Link to post
Share on other sites
[quote name='Meltac' timestamp='1352388561' post='4998893']
Thanks, but the SunPos that I'm getting from the engine is a float3, so I don't have a w here! Also, float4(SunPos, 1) obviously doesn't change a thing.
[/quote]
It does, because the ViewProj matrix needs homogeneous coordinates for it to work correctly. The resulting vector will have a w-component that is essential for the projection to work. Here's the code:

[CODE]
float4 homogeneousSunPos = float4(SunPos, 1);

// Transform sun position to screen space
homogeneousSunPos = mul( homogeneousSunPos, ViewProj );

homogeneousSunPos /= homogeneousSunPos.w;

float4x4 TexAdj = { 0.5f, 0.0f, 0.0f, 0.0f,
0.0f, -0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f };

homogeneousSunPos = mul( homogeneousSunPos, TexAdj );

// Calculate vector from pixel to light source in screen space.
float2 deltaTex = (TexPos - homogeneousSunPos);
[/CODE]

[quote name='Meltac' timestamp='1352388561' post='4998893']
I don't want to draw the sun, but render sun shafts coming from the correct direction where the sun is rendered by the engine is in screen space.
[/quote]
It's just easier to tell whether the projected sun position works... Simply for debugging purposes... Edited by CryZe

Share this post


Link to post
Share on other sites
[quote name='CryZe' timestamp='1352392750' post='4998921']
It does, because the ViewProj matrix needs homogeneous coordinates for it to work correctly. The resulting vector will have a w-component that is essential for the projection to work.
[/quote]

Thanks. You're right, it does - it places my sun always in the exact center of the screen, regardless of the camera/view direction!

So that code still doesn't seem right. Also, in the version that more-or-less works (besides the inaccurate sun positioning), I didn't do the
[source lang="cpp"]
// Transform sun position to screen space
SunPos = mul( SunPos, ViewProj );
[/source]
at the beginning of the code. I remember that I had assumed that the sun position that I'm getting is already transformed. However I never was sure about that.

I may be misunderstanding the whole thing, so that I'm not able at all to figure out what the problem might be. Could you please explain your code a bit? E.g. why do I have to transform into screen space first, and then apply another transformation? What is that TexAdj matrix, where do those values come from, and why do it need it?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this