How to make a Sun (physical)

Recommended Posts

lipsryme    1522
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 on other sites
dave    2187
What's wrong with a white sphere? Or am i missing the point?

Share on other sites
lipsryme    1522
Umm.....white sphere ?
I just want to know how to make a sun on the horizon (skydome) that I can move according to the current daytime.

Share on other sites
dave    2187
Well the sky dome i just a hemisphere that moves with the camera, draw a sphere just inside it that also moves with the camera.

Share on other sites
lipsryme    1522
so just a sphere ?
I'd have to deactivate depth as I did with the skydome then, right ?

Share on other sites
InvalidPointer    1842
Quote:
 Original post by lipsrymeso 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 on other sites
scope    108
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 on other sites
lipsryme    1522
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 on other sites
scope    108
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.

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

Share on other sites
lipsryme    1522
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 on other sites
scope    108
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 on other sites
lipsryme    1522
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 on other sites
scope    108
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 on other sites
lipsryme    1522
Alright thanks for the explanation.

Now I don't suppose you know how to add a dx9 lens flare effect to it ? :p Anyone ?

Share on other sites
scope    108
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 on other sites
lipsryme    1522
Hmmm not sure....sounds pretty complicated.

Anyway you can check my blog for updates: http://lipsryme.wordpress.com

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 on other sites
lipsryme    1522
Is there some way to color the sun while not coloring the skydome texture at the same time?
e.g. for sunrise?

Share on other sites
scope    108
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 on other sites
lipsryme    1522
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.

scope    108

Share on other sites
lipsryme    1522
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 on other sites
scope    108
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 :)