2 registered members (AndrewAMD, alibaba),
1,184
guests, and 3
spiders. |
Key:
Admin,
Global Mod,
Mod
|
|
|
Atmospheric Scattering + Sun Shader
#434195
12/16/13 15:29
12/16/13 15:29
|
Joined: Aug 2002
Posts: 3,258 Mainz
oliver2s
OP
Expert
|
OP
Expert
Joined: Aug 2002
Posts: 3,258
Mainz
|
This code/shader simulates atmospheric scattering and renders the sun into the sky. Because it's a bit tricky to set up everything right, I made a test level where you can directly see the results in realtime (and play with sun angles) Download testlevel and source files: http://www.stonehill-games.de/downloads/atmospheric_scattering_A8.rarIf you want to get real sun positions, use this script: http://www.opserver.de/ubb7/ubbthreads.php?ubb=showflat&Number=434181Shader code is taken from Sean O'Neil's GPU Gems 2: http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html EDIT: for those who want to set up the code by themselfs. Here you go: 1. create s centered sphere "sky_sphere.mdl" in MED with radius = 1 quant with inverted normals. 2. create the "atmospheric_scattering.fx" file:
#define PI 3.14159f
#define m_hdrExposure 1.25f
// Difference between inner and ounter radius. Must be 2.5%
#define m_outerScaleFactor 1.025f
// Wave length of sun light
#define m_waveLength float3(0.65f, 0.57f, 0.475f)
// Sun brightness constant
#define m_ESun 20.0f
// Rayleigh scattering constant
#define m_kr 0.0035f
// Mie scattering constant
#define m_km 0.0010f
// The Mie phase asymmetry factor, must be between 0.999 to -0.999
#define m_g -0.99f
// The scale depth (i.e. the altitude at which the atmosphere's average density is found)
#define m_scaleDepth 0.25f
#define radius vecSkill1.x
float4x4 matWorldViewProj;
float4x4 matWorld;
float4 vecViewPos;
float4 vecSunDir;
float4 vecSkill1;
float3 v3InvWavelength;
float fOuterRadius; // The outer (planetary) radius
float fOuterRadius2; // fOuterRadius^2
float fInnerRadius; // The inner (planetary) radius
float fInnerRadius2; // fInnerRadius^2
float fKrESun; // Kr * ESun
float fKmESun; // Km * ESun
float fKr4PI; // Kr * 4 * PI
float fKm4PI; // Km * 4 * PI
float fScale; // 1 / (fOuterRadius - fInnerRadius)
float fScaleOverScaleDepth; // fScale / fScaleDepth
float fHdrExposure; // HDR exposure
struct v2f
{
float4 pos : POSITION;
float2 uv : TEXCOORD0;
float3 t0 : TEXCOORD1;
float3 c0 : COLOR0;
float3 c1 : COLOR1;
};
float scale(float fCos)
{
float x = 1.0 - fCos;
return 0.25 * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
}
v2f VS (
float4 inPos : POSITION,
float2 inTexCoord : TEXCOORD0
)
{
v2f Out;
Out.pos = mul(inPos,matWorldViewProj);
v3InvWavelength = float3(1.0f / pow(m_waveLength.x, 4.0f), 1.0f / pow(m_waveLength.y, 4.0f), 1.0f / pow(m_waveLength.z, 4.0f));
fOuterRadius = radius * m_outerScaleFactor;
fOuterRadius2 = fOuterRadius*fOuterRadius;
fInnerRadius = radius;
fInnerRadius2 = fInnerRadius*fInnerRadius;
fKrESun = m_kr*m_ESun;
fKmESun = m_km*m_ESun;
fKr4PI = m_kr*4.0f*PI;
fKm4PI = m_km*4.0f*PI;
fScale = (1.f / (fOuterRadius - fInnerRadius));
fScaleOverScaleDepth = fScale/m_scaleDepth;
float3 v3CameraPos = float3(0.f,radius,0.f); // The camera's current position
float fCameraHeight = radius; // The camera's current height
//float fCameraHeight2 = fCameraHeight*fCameraHeight; // fCameraHeight^2
// Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere)
float3 v3Pos = mul(matWorld, inPos).xyz;
float3 v3Ray = v3Pos - v3CameraPos;
float fFar = length(v3Ray);
v3Ray /= fFar;
// Calculate the ray's starting position, then calculate its scattering offset
float3 v3Start = v3CameraPos;
float fHeight = length(v3Start);
float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fCameraHeight));
float fStartAngle = dot(v3Ray, v3Start) / fHeight;
float fStartOffset = fDepth *scale(fStartAngle);
float fSamples = 2.0;
// Initialize the scattering loop variables
float fSampleLength = fFar / fSamples;
float fScaledLength = fSampleLength * fScale;
float3 v3SampleRay = v3Ray * fSampleLength;
float3 v3SamplePoint = v3Start + v3SampleRay * 0.5f;
// Now loop through the sample rays
float3 v3FrontColor = float3(0.0, 0.0, 0.0);
for(int i=0; i<int(fSamples); i++)
{
float fHeight = length(v3SamplePoint);
float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
float fLightAngle = dot(vecSunDir.xyz*(-1.f), v3SamplePoint) / fHeight;
float fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight;
float fScatter = (fStartOffset + fDepth*(scale(fLightAngle) - scale(fCameraAngle)));
float3 v3Attenuate = exp(-fScatter * (v3InvWavelength * fKr4PI + fKm4PI));
v3FrontColor += v3Attenuate * fDepth * fScaledLength;
v3SamplePoint += v3SampleRay;
}
Out.uv = inTexCoord.xy;
// Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader
Out.c0 = v3FrontColor * v3InvWavelength * fKrESun;
Out.c1 = v3FrontColor * fKmESun;
Out.t0 = v3CameraPos - v3Pos;
return Out;
}
// Calculates the Mie phase function
float getMiePhase(float fCos, float fCos2, float g, float g2)
{
return 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos2) / pow(1.0 + g2 - 2.0*g*fCos, 1.5);
}
// Calculates the Rayleigh phase function
float getRayleighPhase(float fCos2)
{
return 0.75 + 0.75*fCos2;
}
float4 PS(v2f IN) : COLOR
{
float fCos = dot(vecSunDir.xyz*(-1.f), IN.t0) / length(IN.t0);
float fCos2 = pow(fCos,2);
float3 col = getRayleighPhase(fCos2) * IN.c0 + getMiePhase(fCos, fCos2, m_g, m_g*m_g) * IN.c1;
//Adjust color from HDR
fHdrExposure = m_hdrExposure;
col = 1.0 - exp(col * -fHdrExposure);
return float4(col,1.0f);
}
technique tMain
{
pass
{
VertexShader = compile vs_3_0 VS();
PixelShader = compile ps_3_0 PS();
}
}
3. create the "atmospheric_scattering.c" script with following code:
#include <acknex.h>
#include <default.c>
var mtl_atmospheric_scattaring_radius=50000;
//atmospheric scattering material
MATERIAL* mtl_atmospheric_scattering =
{
effect="atmospheric_scattering.fx";
}
void init_atmospheric_scattering(ENTITY* sky_)
{
//scale sky sphere to defined radius multiplied by factor 1.025 (difference of inner and outer atmosphere radius
vec_scale(sky_.scale_x,mtl_atmospheric_scattaring_radius);
vec_scale(sky_.scale_x,1.025);
//pass sky sphere radius to material
mtl_atmospheric_scattering.skill1=floatv(mtl_atmospheric_scattaring_radius);
}
void atmospheric_scattering_startup()
{
//wait until level is loaded
while(!level_ent){wait(1);}
//create sky sphere
ENTITY* sky_=ent_create("sky_sphere.mdl",nullvector,NULL);
//set flags
set(sky_,PASSABLE|UNTOUCHABLE|NOFOG);
//assign material
sky_.material=mtl_atmospheric_scattering;
//update scale
init_atmospheric_scattering(sky_);
while(1)
{
//sky sphere needs to have always same position as camera minus sky sphere radius
vec_set(sky_.x,camera.x);
sky_.z-=mtl_atmospheric_scattaring_radius;
wait(1);
}
}
void main()
{
video_screen=2;
video_mode=8;
//load level
level_load(NULL);
}
Last edited by oliver2s; 12/16/13 15:36.
|
|
|
Re: Atmospheric Scattering + Sun Shader
[Re: sivan]
#434198
12/16/13 15:44
12/16/13 15:44
|
Joined: Aug 2002
Posts: 3,258 Mainz
oliver2s
OP
Expert
|
OP
Expert
Joined: Aug 2002
Posts: 3,258
Mainz
|
is it okay only with a sphere sky model? What do mean by "okay"? It's only working with a sphere sky model. But not defined as Sky Entity but as regular Entity (see code).
|
|
|
Re: Atmospheric Scattering + Sun Shader
[Re: oliver2s]
#434231
12/17/13 08:10
12/17/13 08:10
|
Joined: Mar 2011
Posts: 3,150 Budapest
sivan
Expert
|
Expert
Joined: Mar 2011
Posts: 3,150
Budapest
|
I meant that thanks there are several bad things in 3dgs sky system I rreported earlier, but no correction can be expected: - SCENE models are clipped - image based sky cube covers world at around sqrt(2)*clip_far distance - sky domes have no material assigned anyway it is another great contribution, I'm sure it can be integrated with other skies hiding model edges by fog, dof or other effects.
|
|
|
Re: Atmospheric Scattering + Sun Shader
[Re: oliver2s]
#434442
12/18/13 15:42
12/18/13 15:42
|
Joined: May 2005
Posts: 2,713 Lübeck
Slin
Expert
|
Expert
Joined: May 2005
Posts: 2,713
Lübeck
|
Nice that you went through with it When I read the article after seeing your post I decided to delay better sky rendering for now The results seem pretty good, but the code to get there is by far not as straight forward as I would like it to be. Also I would like to do this on a per fragment basis instead of doing most things per vertex using a sky sphere with an even polygon distribution for clean results. From just a quick look at your code, I do recommend you to use a define for fSamples. I know that without at least the glsl compiler on my mac won´t unroll the loop and probably even take additional time for the cast to int. Especially real (as in not unrolled, dynamic) loops, have a serious performance impact. Also about sky rendering in general: To make the sky appear behind everything you usually render the sky before everything else without depth writing and only applying the cameras rotation and projection matrix and if needed the skies rotation matrix. As a result it will appear behind everything else, won´t be clipped and won´t clip anything and just replace the clear color as it should. As far as I know gamestudio does this by default, but since you messed with it more than I did you probably know better how you can or can´t use this shader with gamestudio sky system or replace it with your own custom sky rendering.
|
|
|
|