リアルなゲームを実現するための大気散乱シミュレーション その2

前回の記事

大気散乱シミュレーションの続きです。

あれからしばらくソースを解析したり、改良したりしました。

うまくソースコードを張れないので、そのうち直します。
オリジナルと異なる部分は、ピクセルシェーダーでレンダリングしている所と
カメラに追従する天球から、大気圏への接触判定を改良してある部分です。
また、地平線以下のカラーを変更する場合は下のコードを入れます。
ただし、Ray.y < 0 の時です。

[code lang=”cpp”]
float3 _outer = cross( -v3CameraPos.xyz, v3Ray.xyz );
if ( dot( _outer, _outer ) &lt; fInnerRadius * fInnerRadius ) ){
// 地平線以下のカラーを変える
}
[/code]

[code lang=”cpp”]
ConstantBuffer skycb1
{
float fInnerRadius;
float atomosphere_length;
float fRayleighScatteringConstant;
float fMieScatteringConstant;
float fSunBrightness;
float fRayleighScaleDepth;
float fMiePhaseAsymmetryFactor;
float atomosphereRate;
float sampleNum;

float4 vWaveLength;
float4 exposure;
float4 scatterScale;
}

float Scale(float fCos)
{
float x = 1.0f – fCos;
return fRayleighScaleDepth * exp(-0.00287f + x*(0.459f + x*(3.83f + x*(-6.80f + x*5.25f))));
}

float GetRayPhase( float fCos2 )
{
return 0.75f * (1.0f + fCos2);
}

float GetMiePhase( float fCos, float fCos2, float g, float g2 )
{
return 1.5f * ((1.0f – g2) / (2.0f + g2)) * (1.0f + fCos2) / pow(1.0f + g2 – 2.0f*g*fCos, 1.5f);
}

PS_OUTPUT main( VS_OUTPUT In ) : S_TARGET_OUTPUT
{
PS_OUTPUT Out;

const float fKr = fRayleighScatteringConstant; // Rayleigh scattering constant
const float fKr4PI = fKr * 4.0f *3.141592f;
const float fKm = fMieScatteringConstant; // Mie scattering constant
const float fKm4PI = fKm * 4.0f *3.141592f;
const float fOuterRadius = fInnerRadius * ( 1.f + atomosphereRate );
const float fAtmosLenR = 158918.807855f;
const float fAtmosLenV = fOuterRadius – fInnerRadius;
const float fScale = 1.f / fAtmosLenV;

const float fKrESun = fKr *fSunBrightness;
const float fKmESun = fKm *fSunBrightness;

const float fScaleDepth = fRayleighScaleDepth;
const float fScaleOverScaleDepth = fScale / fRayleighScaleDepth;

float3 v3LightPos = -dirLightVec.xyz;
float3 v3CameraPos = float3( 0.f, 0.f, 0.f );
float3 i_Position = float3( 0.f, 0.f, 0.f );
float fCameraHeight = 0.f;

i_Position = In.coord7.xyz – viewPos.xyz;
float3 i_Position_vec = normalize( i_Position.xyz );

v3CameraPos.y = (fAtmosLenV * viewPos.y) / fAtmosLenR + fInnerRadius;

float posv = dot( v3CameraPos.xyz, i_Position_vec );
float posLen2 = dot( v3CameraPos.xyz, v3CameraPos.xyz );
float ofs = fOuterRadius * fOuterRadius – posLen2 + posv * posv;
float fDet = max( 0.0f, ofs );
float fNear = sqrt( fDet ) – posv;

i_Position.xyz = v3CameraPos.xyz + i_Position_vec * fNear;

fCameraHeight = v3CameraPos.y;

float3 v3InvWavelength = 1 / pow( vWaveLength.xyz, 4 );

float3 v3Pos = i_Position.xyz;

// 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 v3Ray = v3Pos – v3CameraPos;
float fFar = length(v3Ray);
v3Ray /= fFar;

// Calculate the ray’s starting position, then calculate its scattering offset
float3 v3Start = v3CameraPos;
float fStartHeight = length(v3Start);
float fStartAngle = dot(v3Ray, v3Start) / fStartHeight;
float fStartDepth = exp(fScaleOverScaleDepth * (fInnerRadius – fCameraHeight));
float fStartOffset = fStartDepth * ExpScale(fStartAngle);

// Initialize the scattering loop variables
float fSampleLength = fFar / sampleNum;
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&lt; sampleNum; i++ )
{
float fHeight = length(v3SamplePoint);
float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius – fHeight));
float fLightAngle = dot(v3LightPos, v3SamplePoint) / fHeight;
float fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight;
float fScatter = (fStartOffset + fDepth*(ExpScale(fLightAngle) – ExpScale(fCameraAngle)));
float3 v3Attenuate = exp(-fScatter * (v3InvWavelength * fKr4PI + fKm4PI));
v3FrontColor += v3Attenuate * (fDepth * fScaledLength);
v3SamplePoint += v3SampleRay;
}

// Finally, scale the Mie and Rayleigh colours and set up the varying variables for the pixel shader
float3 vRayColor = v3FrontColor * (v3InvWavelength * fKrESun);
float3 vMieColor = v3FrontColor * fKmESun;

float3 vCameraToVertexDir = v3CameraPos – v3Pos;

const float g = fMiePhaseAsymmetryFactor;
const float g2 = fMiePhaseAsymmetryFactor *fMiePhaseAsymmetryFactor;

float fCos = dot(v3LightPos, vCameraToVertexDir) / length(vCameraToVertexDir);
float fCos2 = fCos * fCos;
float fRayPhase = GetRayPhase( fCos2 );
float fMiePhase = GetMiePhase( fCos, fCos2, g, g2 );

Out.color_dst = float4( fRayPhase * vRayColor + fMiePhase * vMieColor, 1.0f );

// トーンを掛けるにしてもここでカラーを落とさないと大きくなりすぎる
Out.color_dst = float4( scatterScale.rgb * ( float3( 1.0f, 1.0f, 1.0f ) – exp( -exposure.rgb * Out.color_dst.xyz ) ), 1.0f ); // to ldr

return Out;
}
[/code]

スポンサーリンク
スポンサーリンク

シェアする

  • このエントリーをはてなブックマークに追加

フォローする

スポンサーリンク