宇宙人工物からの大気散乱空


20

私は宇宙からの惑星の大気散乱を実装する過程にいます。http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.htmlからSean O'Neilのシェーダーを出発点として使用しています。

ここにあるGroundFromSpaceシェーダーではなく、SkyFromSpaceシェーダーを除いて、fCameraAngleに関連する問題はほとんどあります。http://www.gamedev.net/topic/621187-sean-oneils-atmospheric-scattering/

fCameraAngle = 1内側のループで使用していない場合、スペースシェーダーから空の奇妙なアーティファクトを取得します。これらのアーティファクトの原因は何ですか?fCameraAngleが1に制限されると、アーティファクトは消えます。O'Neilのサンドボックス(http://sponeil.net/downloads.htm)にある色相も欠けているようです。

カメラ位置X = 0、Y = 0、Z = 500。左側のGroundFromSpace、右側のSkyFromSpace。 ここに画像の説明を入力してください

カメラ位置X = 500、Y = 500、Z = 500。左側のGroundFromSpace、右側のSkyFromSpace。 ここに画像の説明を入力してください

カメラの角度は、ソースに応じて非常に異なる方法で処理されるようであることがわかりました。

元のシェーダーでは、SkyFromSpaceShaderのカメラアングルは次のように計算されます。

float fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight;

一方、スペースシェーダーの地面では、カメラの角度は次のように計算されます。

float fCameraAngle = dot(-v3Ray, v3Pos) / length(v3Pos);

しかし、さまざまなソースがオンラインで、レイを否定しています。どうしてこれなの?

問題を示し、画像の生成に使用したC#Windows.Formsプロジェクトは次のとおりです。https : //github.com/ollipekka/AtmosphericScatteringTest/

更新: O'NeilのサイトにあるScatterCPUプロジェクトから、カメラがシェーディングされているポイントの上にあるときにカメラレイが無効になり、ポイントからカメラへの散乱が計算されることがわかりました。

光線の方向を変更すると、実際にアーティファクトは削除されますが、次に示すように他の問題が発生します。

カメラ角度のレイを否定する

さらに、ScatterCPUプロジェクトでは、O'Neilは光の光学的深さが0未満の状況から保護します。

float fLightDepth = Scale(fLightAngle, fScaleDepth);

if (fLightDepth < float.Epsilon)
{
    continue;
}

コメントで指摘されているように、これらの新しいアーティファクトとともに、これは依然として疑問を残しています。カメラが500、500、500に配置されている画像の何が問題になっていますか?ハローが惑星の完全に間違った部分に焦点を合わせているように感じます。昼から夜へと変化する場所ではなく、太陽が惑星に当たるはずの場所に光が近づくと予想されます。

githubプロジェクトは、この更新の変更を反映するように更新されました。


1
私はあなたのコードを

プロジェクトへのすべての外部参照を削除しました。プロジェクトにはXNAは必要ありません。これは単純なWindows.Formsプロジェクトであり、実行するのに特別なものは必要ありません。したがって、古いVisual Studioバージョンに変換するのはかなり簡単です。
-ollipekka

最初の画像の球の中心に向かうピクセルアーティファクトについて話していますか?それらは最終的なイメージに実際に影響を与えるべきではありません。SkyFromSpaceシェーダーは裏返しの球体に適用されることになっているため、惑星を越えて広がる大気の一部のみが表示され、アーティファクトのある中心は惑星の後ろに隠されます。ただし、地面のシェーディングと空のシェーディングの両方が、500,500,500 ........でカメラを監視しています

回答:


1

エンジンを移行しているので、現在動作するコードはありませんが、これらは動作するパラメーター設定です:

// Inited in code
float innerRadius = sphere.Radius;
float outerRadius = innerRadius*1.025f;
float scale = 1.0f/(outerRadius - innerRadius);
float scaleDepth = outerRadius - innerRadius;
float scaleOverScaleDepth = scale/scaleDepth;

Vector4 invWavelength = new Vector4(
    (float) (1.0/Math.Pow(wavelength.X, 4.0)),
    (float) (1.0/Math.Pow(wavelength.Y, 4.0)),
    (float) (1.0/Math.Pow(wavelength.Z, 4.0)),
    1);

float ESun = 15.0f;
float kr = 0.0025f;
float km = 0.0015f;
float g = -0.95f;
float g2 = g * g;
float krESun = kr * ESun;
float kmESun = km * ESun;
float epkr4Pi = epkr4Pi = (float)(kr * 4 * Math.PI)
float epkm4Pi = epkr4Pi = (float)(kr * 4 * Math.PI)

これがシェーダーでした:

struct AtmosphereVSOut
{
    float4 Position : POSITION;
    float3 t0 : TEXCOORD0;
    float3 c0 : TEXCOORD1; // The Rayleigh color
    float3 c1 : TEXCOORD2; // The Mie color
    float4 LightDirection : TEXCOORD3;
};

// The scale equation calculated by Vernier's Graphical Analysis
float expScale (float fCos)
{
    //float x = 1.0 - fCos;
    float x = 1 - fCos;
    return scaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));

}
// 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 + (1.0 + fCos2);
}

// Returns the near intersection point of a line and a sphere
float getNearIntersection(float3 vPos, float3 vRay, float fDistance2, float fRadius2)
{
    float B = 2.0 * dot(vPos, vRay);
    float C = fDistance2 - fRadius2;
    float fDet = max(0.0, B*B - 4.0 * C);
    return 0.5 * (-B - sqrt(fDet));
}

AtmosphereVSOut
AtmosphereFromSpaceVS(float4 vPos : POSITION )
{
    // Multiply the camera position vector in world space by the 
    // World Inverse matrix so that it gets transformed to
    // object space coordinates
    float4 vEyePosInv = mul(vEyePos, mWorldInverse);

    // Compute a ray from the vertex to the camera position
    float3 vRay = vPos - vEyePosInv.xyz;

    // Transform the Light Position to object space and use
    // the result to get a ray from the position of the light
    // to the vertex. This is our light direction vector
    // which has to be normalized.
    float4 vLightDir = mul(vLightPosition,mWorldInverse) - vPos;
    vLightDir.xyz = normalize(vLightDir.xyz);
    vLightDir.w = 1.0;

    // From the vRay vector we can calculate the 
    // "far" intersection with the sphere
    float fFar = length (vRay);
    vRay /= fFar;

    // But we have to check if this point is obscured by the planet
    float B = 2.0 * dot(vEyePosInv, vRay);
    float C = cameraHeight2 - (innerRadius*innerRadius);
    float fDet = (B*B - 4.0 * C);

    if (fDet >= 0)
    {
        // compute the intersection if so
        fFar = 0.5 * (-B - sqrt(fDet));
    }

    // Compute the near intersection with the outer sphere
    float fNear = getNearIntersection (vEyePosInv, vRay, cameraHeight2, outerRadius2);

    // This is the start position from which to compute how
    // the light is scattered
    float3 vStart = vEyePosInv + vRay * fNear;
    fFar -= fNear;

    float fStartAngle = dot (vRay, vStart) / outerRadius;
    float fStartDepth = exp (scaleOverScaleDepth * (innerRadius - cameraHeight));
    float fStartOffset = fStartDepth * expScale (fStartAngle);
    float fSampleLength = fFar / samples;
    float fScaledLength = fSampleLength * scale;
    float3 vSampleRay = vRay * fSampleLength;
    float3 vSamplePoint = vStart + vSampleRay * 0.5f;

    // Now we have to compute each point in the path of the
    // ray for which scattering occurs. The higher the number
    // of samples the more accurate the result.
    float3 cFrontColor = float3 (0,0,0);
    for (int i = 0; i < samples; i++)
    {
        float fHeight = length (vSamplePoint);
        float fDepth = exp (scaleOverScaleDepth * (innerRadius - fHeight));
        float fLightAngle = dot (vLightDir, vSamplePoint) / fHeight;
        float fCameraAngle = dot(-vRay, vSamplePoint) / fHeight;
        float fScatter = (fStartOffset + fDepth * (expScale (fLightAngle) - expScale (fCameraAngle)));

        float3 cAttenuate = exp (-fScatter * (vInvWavelength.xyz * kr4PI + km4PI));

        cFrontColor += cAttenuate * (fDepth * fScaledLength);
        vSamplePoint += vSampleRay;
    }

    // Compute output values
    AtmosphereVSOut Out;

    // Compute a ray from the camera position to the vertex
    Out.t0 = vEyePos.xyz - vPos.xyz;

    // Compute the position in clip space
    Out.Position = mul(vPos, mWorldViewProj);

    // Compute final Rayleigh and Mie colors
    Out.c0.xyz = cFrontColor * (vInvWavelength.xyz * krESun);
    Out.c1.xyz = cFrontColor * kmESun;

    // Pass the light direction vector along to the pixel shader
    Out.LightDirection = vLightDir;

    return Out;
}

PSOut
AtmosphereFromSpacePS(AtmosphereVSOut In)
{
    PSOut Out;

    float cos = saturate(dot (In.LightDirection, In.t0) / length (In.t0));
    float cos2 = cos*cos;

    float fMiePhase = getMiePhase(cos,cos2,g,g2);
    float fRayleighPhase = getRayleighPhase(cos2);

    float exposure = 2.0;
    Out.color.rgb = 1.0 - exp(-exposure * (fRayleighPhase * In.c0 + fMiePhase * In.c1));
    Out.color.a = Out.color.b;

    return Out;
    }

それでも機能するかどうか教えてください。他のヘルプが必要な場合は、コードを掘り下げてみます。レンダリングには2つの球体を使用したと思います。1つは表面用、もう1つは大気用です。


0

いくつかの思考の軌跡:フロートの精度を確認してください。スペーススケールでは、ほとんどの場合、float32では十分ではありません。スキャッタリングシェーダの下にある球体のようなプリミティブレンダリングがある場合は、dpethバッファを確認します。

これらのアーティファクトはレイトレーシングでも見られます。これらは通常、フロート精度の問題から生じるプライマリサーフェスのジッターと交差するセカンダリレイです。

編集:1000(すべての整数は、24ビットの仮数のおかげで、float32表現で1600万まで完全に表現可能)、float32の次の数値は1000.00006103であるため、この範囲で精度はかなり良好です。

ただし、メーター範囲を使用する場合、惑星を表示するには、この距離は100,000,000の値を意味し、次は100000008:100,000kmで8メートルの精度です。

たとえば、衛星の周りを移動しようとするとカメラがジャンプし、世界のゼロが惑星の中心にある場合、衛星自体のレンダリングがすべて壊れます。それが星系の中心であるならば、それはさらに悪いです。

フラビアンブレビオン(ヤサニヤ)と地球のゲームインフィニティクエストをご覧ください。彼はgamedevの興味深いdevジャーナルと彼のフォーラムを持ち、アブソリュートを使用してスターシステムの距離を管理することがいかに不可能かを説明しています。

彼はまた、そのような種類の範囲での深度バッファの問題について言及しており、対数zスケールを導入する最初ではないにしても最初の1つです。 http://www.gamedev.net/blog/73/entry-2006307-tip-of-the-day-logarithmic-zbuffer-artifacts-fix/ さらに詳しい情報はこちら:http : //outerra.blogspot.jp/ 2012/11 / maximizing-depth-buffer-range-and.html

ソフトウェアテストベッド:良いアイデアです。シェーダーをオーサリングする優れた方法であるため、ステップごとにデバッグを行うことができます。値を1行ずつ確認するだけで、何かおかしい場合は調査できます。シェーダーでカメラアングルが使用される部分を投稿したコードには表示されなかったので、この部分については少し困惑しています。


フロート精度の意味を詳しく説明していただけますか?この例で使用されている縮尺は-1000〜1000です。この例は、現時点では純粋なソフトウェア実装であり、シェーダーの結果が画像にレンダリングされ、c#System.Drawing APIを使用して表示されます。この例ではプリミティブを使用しません。
ollipekka
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.