Sign in to follow this  
lipsryme

How to make a Sun (physical)

Recommended Posts

Hi, right now I'm doing an outside scene with daytime change but I've got a problem with the sun. Well of course the Sun would be the directional lighting but I want to have a sun on the horizon that I can translate according to the daytime. I've been trying to figure out a way to do that but I can't think of anything that'll work. Does any here have an idea how I can do that ? It doesn't have to be the best looking way.

Share this post


Link to post
Share on other sites
Quote:
Original post by lipsryme
so just a sphere ?
I'd have to deactivate depth as I did with the skydome then, right ?

Disable writes, but enable tests. Quick rule of thumb for this-- if you want it to be *hidden by* things, such as terrain, enable testing. If you want it to *hide* things, then enable writes.

Share this post


Link to post
Share on other sites
or you could compute the sun in the pixel shader for example like this


float3 norm = normalize(input.vertex);
float3 Sun = normalize(SunPos);
float SunDot = dot(Sun, norm);

vector sun = 0.5f * pow( max(0, SunDot), 300.0f );



code in action:

Share this post


Link to post
Share on other sites
Could you explain the last two lines?

Also what's with the normal calculation?
Do I draw a single vertex with Pos, Color and Normal ?
And where would the normal of that vertex point to ?

[Edited by - lipsryme on May 1, 2010 2:33:38 PM]

Share this post


Link to post
Share on other sites
well i'm not the best at explaining these thing but i'll give it a try:



You have a normal from the current pixel and one for the sun position. the smaller the angle between those, the bigger the dot product. the pow() is used to control the attenution.

Quote:

Also what's with the normal calculation?
Do I draw a single vertex with Pos, Color and Normal ?
[


No, you just have to pass the untransformed vertex position to the pixel shader to compute the normal because it is assumed that all faces of the skydome point to the center of the skydome.

vertex shader:


output.Vertex = input.Position;
output.Position = mul(float4(input.Position.xyz , 1.0f) , MatWVP);


Share this post


Link to post
Share on other sites
Hmm I don't quite get it.

So...the input.vertex is the untransformed Vertex Position of the Skydome ?

And the variable "norm" would then be the position vector of the Skydome pixel we are looking at ?

Share this post


Link to post
Share on other sites
Well, your skydome should be created with the center at xyz(0,0,0) in model space.

That way your vertex normal would be normalize(vertex.xyz - center.xyz) which equals normalize(vertex.xyz) because center is (0,0,0).

As is said, i'm not the best at explaining these things ;-) maybe someone else can pitch in.

Or just try plugging that code into your sky shader and see what happens :-)

Share this post


Link to post
Share on other sites
Wow it really works ;)
But I've got a little problem.
My sun is always at the top of the skydome no matter what the sunPos is.
Why is that ?

edit: ah I got it.
Don't know why but I have to use the X value to make it go up and down not the Y.


Well thanks a lot for your help and for taking your time :)

edit2: Just a one thing I'm not sure how to move the sun right now.
Do you know what value I'd have to change to make it move in a certain direction ?

[Edited by - lipsryme on May 1, 2010 4:38:03 PM]

Share this post


Link to post
Share on other sites
The sunpos could be a xyz vector in the range of [-1, 1].

Quote:

Don't know why but I have to use the X value to make it go up and down not the Y.


Not sure about that, i don't have to do it in my implementation.

Here's what i use to make the sun move according to the current day time, where time_elapsed is the current day time in the range of [0.0, 1.0] i think (it's been a while)

Warning, unoptimized code:


struct CeSun
{
D3DXVECTOR4 pos;
D3DXVECTOR3 vDirection, vRotAxis, vOrg;
D3DXMATRIX matRotAxis, matRotZ;
};

void initSun() // once
{
sun.pos = D3DXVECTOR4(0.0f, -1.0f, 0.0f, 0.0f);
sun.vOrg = D3DXVECTOR3(sun.pos);
D3DXVec3Cross(&sun.vRotAxis, &sun.vOrg, &D3DXVECTOR3(0, 1.0f, 0));
D3DXVec3Normalize(&sun.vRotAxis, &sun.vRotAxis);
D3DXMatrixRotationAxis(&sun.matRotAxis, &sun.vRotAxis, 0);
}

void onUpdate() //each cycle
{
D3DXMatrixRotationX(&sun.matRotZ, -D3DXToRadian(360.0f * time_elapsed));
D3DXVec3TransformCoord(&sun.pos, &sun.vOrg, &(sun.matRotAxis * sun.matRotZ));
D3DXVec3Normalize(&sun.pos);
}


Share this post


Link to post
Share on other sites
I in fact don't know, but i vaguely remember a technique being described in this article in chapter 29.8 An Application: Lens Flares. Keep us updated and feel free to post screen shots with results :-)

Share this post


Link to post
Share on other sites
You may also use rayleigh scattering:
http://en.wikipedia.org/wiki/Rayleigh_scattering
And this is how to do it with OpenGL but it's the same with DX:
http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html

Share this post


Link to post
Share on other sites
depending on the method you settled for, it should be possible.

if you use the pixel shader method, it should be sufficient to multiply the sun with a color value and add that to your final color like this


sun *= somecolor;
output.Color = sky + sun;



is that what you meant?

in my implementation, most color comes from computation and my sky textures are almost greyscale.

here i found this old thread that i learned a lot of these things from Sky Rendering.

Share this post


Link to post
Share on other sites
Not sure that works for me.
The thing is I've been using a texture, 3 actually, and color values for each time of day.
Which I then "lerp" between with the help of a time variable passed to the effect file.

Problem now with the sun is that the pixel shader color output from the sun would be the same as the one from the skydome.

But I want to change the color of the sun and not of the sky texture at the same time.

Share this post


Link to post
Share on other sites
There you go...





struct SkydomeVSOutput
{
float4 Position : POSITION;
float2 TexCoord : TEXCOORD0;
float3 UPosition : TEXCOORD1;
};



struct SkydomePSOutput
{
float4 Color : COLOR0;
};






SkydomeVSOutput SkydomeVS(float4 inPos : POSITION, float2 inTexCoord : TEXCOORD0)
{
SkydomeVSOutput output = (SkydomeVSOutput)0;

float4x4 ViewProjection = mul(xView, xProj);
float4x4 WorldViewProjection = mul(xWorld, ViewProjection);
output.Position = mul(inPos, WorldViewProjection);
inTexCoord.x /= 1.6;
output.TexCoord = inTexCoord;
output.UPosition = inPos;


return output;
}




SkydomePSOutput SkydomePS(float2 inTexCoord : TEXCOORD0, float3 inUPos : TEXCOORD1)
{
SkydomePSOutput output = (SkydomePSOutput)0;

output.Color = ambientColor * ambientIntensity;


float3 finalSunPosition = float3(0.0f, 0.0f, 0.0f);

float3 SunPosNoon = float3(1.0f, 1.0f, 0.0f);
float3 SunPosSundown = float3(0.0f, 0.0f, 1.0f);
float3 SunPosNight = float3(0.0f, 0.0f, 0.0f);
float3 SunPosSunrise = float3(0.0f, 0.0f, -1.0f);

if(xDaytimeNoon)
{
finalSunPosition += lerp(SunPosNoon, SunPosSundown, xTime);
}

if(xDaytimeSundown)
{
//finalSunPosition = SunPosNight;
finalSunPosition += lerp(SunPosSundown, SunPosNight, xTime);
}

if(xDaytimeNight)
{
finalSunPosition += lerp(SunPosNight, SunPosSunrise, xTime);
}

if(xDaytimeSunrise)
{
finalSunPosition += lerp(SunPosSunrise, SunPosNoon, xTime);
}

// Calculating Sun
float3 normal = normalize(inUPos);
float3 sun = normalize(finalSunPosition);
float SunDot = dot(sun, normal);
vector sunVec = 0.5f * pow( max(0, SunDot), 300.0f );





float4 noon = float4(tex2D(SkydomeNoonTextureSampler, inTexCoord) + float4(0.1, 0.1, 0.1, 0));
float4 sundown = float4(tex2D(SkydomeSundownTextureSampler, inTexCoord) + float4(0.6, 0.15, 0.15, 0));
float4 night = float4(tex2D(SkydomeNightTextureSampler, inTexCoord) + float4(0, 0, 0.3, 0) + float4(-0.2, -0.2, -0.2, 0));



if(xDaytimeNoon)
{
output.Color += lerp(noon, sundown, xTime);
output.Color += sunVec;
}

if(xDaytimeSundown)
{
output.Color += lerp(sundown, night, xTime);
output.Color += sunVec;
}

if(xDaytimeNight)
{
output.Color += lerp(night, sundown, xTime);
output.Color += sunVec;
}

if(xDaytimeSunrise)
{
output.Color += lerp(sundown, noon, xTime);
output.Color += sunVec;
}



return output;
}









technique Skydome
{
pass Pass0
{
VertexShader = compile vs_2_0 SkydomeVS();
PixelShader = compile ps_2_0 SkydomePS();

// Render States
ZWriteEnable = false;
FillMode = Solid;
}
}

Share this post


Link to post
Share on other sites
hmm this has a lot of conditions in it, i don't know if thats good in a pixel shader performance wise.

i think it's better to compute the sun position on the cpu (for example by rotating it around an axis as i showed you) and use a simple variable that represents the current time of day in the range of 0 (midnight) to 1 (noon) to blend those textures.

for example like this:


float4 night = float4(tex2D(SkydomeNightTextureSampler, inTexCoord));
night *= 1.0f - saturate(time_of_day * 1.5);







this would make your night texture fade out and disappear at around 1/3 of the day. no lerps needed.


But back to your question, if you multiply "sunVec" with some color value, you don't get the desired results?


if(xDaytimeNoon) output.Color += lerp(noon, sundown, xTime);
if(xDaytimeSundown) output.Color += lerp(sundown, night, xTime);
if(xDaytimeNight) output.Color += lerp(night, sundown, xTime);
if(xDaytimeSunrise) output.Color += lerp(sundown, noon, xTime);

output.Color += sunVec * float4(1.0, 0, 0, 1.0f); //somecolor





(you can actually render the sun at night too because it's on the other side of the "earth" just like in real life :-))

Oh, btw you can define "sunVec" just as a float3 or float4. it said "vector" because i pasted that from some old sloppy code :)

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