レイトレーシングにおけるBRDFおよび球座標


9

標準のフォン/ブリンフォン照明モデルを使用するレイトレーサーを開発しました。物理ベースのレンダリングをサポートするように変更しているので、さまざまなBRDFモデルを実装しています。現在、私はOren-NayarとTorrance-Sparrowモデルに集中しています。これらはそれぞれ、入射wiおよび出射wo光の方向を表すために使用される球面座標に基づいています。

私の質問は次のとおりです。wiとwoをデカルト座標から球座標に変換する正しい方法はどれですか。

私はここで報告された標準の公式を適用していますhttps://en.wikipedia.org/wiki/Spherical_coordinate_system#Coordinate_system_conversions 私のベクトルが原点の尾にないので、私が正しいことをしているのかわかりませんデカルト座標系ですが、光線とオブジェクトの交点を中心とします。

ここに私の現在の実装を見つけることができます:

誰かがwiとwoのベクトルをデカルト座標から球座標に変換する正しい方法の説明を手伝ってくれる?

更新

ここにコードの関連部分をコピーします。

球面座標計算

float Vector3D::sphericalTheta() const {

    float sphericalTheta = acosf(Utils::clamp(y, -1.f, 1.f));

    return sphericalTheta;
}

float Vector3D::sphericalPhi() const {

    float phi = atan2f(z, x);

    return (phi < 0.f) ? phi + 2.f * M_PI : phi;
}

オーレンナヤル

OrenNayar::OrenNayar(Spectrum<constant::spectrumSamples> reflectanceSpectrum, float degree) : reflectanceSpectrum{reflectanceSpectrum} {

    float sigma = Utils::degreeToRadian(degree);
    float sigmaPowerTwo = sigma * sigma;

    A = 1.0f - (sigmaPowerTwo / 2.0f * (sigmaPowerTwo + 0.33f));
    B = 0.45f * sigmaPowerTwo / (sigmaPowerTwo + 0.09f);
};

Spectrum<constant::spectrumSamples> OrenNayar::f(const Vector3D& wi, const Vector3D& wo, const Intersection* intersection) const {

    float thetaI = wi.sphericalTheta();
    float phiI = wi.sphericalPhi();

    float thetaO = wo.sphericalTheta();
    float phiO = wo.sphericalPhi();

    float alpha = std::fmaxf(thetaI, thetaO);
    float beta = std::fminf(thetaI, thetaO);

    Spectrum<constant::spectrumSamples> orenNayar = reflectanceSpectrum * constant::inversePi * (A + B * std::fmaxf(0, cosf(phiI - phiO) * sinf(alpha) * tanf(beta)));

    return orenNayar;
}

トーランス-スズメ

float TorranceSparrow::G(const Vector3D& wi, const Vector3D& wo, const Vector3D& wh, const Intersection* intersection) const {

    Vector3D normal = intersection->normal;
    normal.normalize();

    float normalDotWh = fabsf(normal.dot(wh));
    float normalDotWo = fabsf(normal.dot(wo));
    float normalDotWi = fabsf(normal.dot(wi));
    float woDotWh = fabsf(wo.dot(wh));

    float G = fminf(1.0f, std::fminf((2.0f * normalDotWh * normalDotWo)/woDotWh, (2.0f * normalDotWh * normalDotWi)/woDotWh));

    return G;
}

float TorranceSparrow::D(const Vector3D& wh, const Intersection* intersection) const {

    Vector3D normal = intersection->normal;
    normal.normalize();

    float cosThetaH = fabsf(wh.dot(normal));

    float Dd = (exponent + 2) * constant::inverseTwoPi * powf(cosThetaH, exponent);

    return Dd;
}

Spectrum<constant::spectrumSamples> TorranceSparrow::f(const Vector3D& wi, const Vector3D& wo, const Intersection* intersection) const {

    Vector3D normal = intersection->normal;
    normal.normalize();

    float thetaI = wi.sphericalTheta();
    float thetaO = wo.sphericalTheta();

    float cosThetaO = fabsf(cosf(thetaO));
    float cosThetaI = fabsf(cosf(thetaI));

    if(cosThetaI == 0 || cosThetaO == 0) {

        return reflectanceSpectrum * 0.0f;
    }

    Vector3D wh = (wi + wo);
    wh.normalize();

    float cosThetaH = wi.dot(wh);

    float F = Fresnel::dieletricFresnel(cosThetaH, refractiveIndex);
    float g = G(wi, wo, wh, intersection);
    float d = D(wh, intersection);

    printf("f %f g %f d %f \n", F, g, d);
    printf("result %f \n", ((d * g * F) / (4.0f * cosThetaI * cosThetaO)));

    Spectrum<constant::spectrumSamples> torranceSparrow = reflectanceSpectrum * ((d * g * F) / (4.0f * cosThetaI * cosThetaO));

    return torranceSparrow;
}

アップデート2

調べたところ、このOren-Nayar BRDFの実装が見つかりました。

上記の実装では、wiとwoのシータは、arccos(wo.dotProduct(Normal))とarccos(wi.dotProduct(Normal))を実行するだけで取得されます。交点の法線を球面座標系の天頂方向として使用して計算できるので、これは私には理にかなっているようです。gamma = cos(phi_wi-phi_wo)の計算は、「接線空間」と呼ばれるものに対して、wiとwoのある種の投影を行います。この実装ですべてが正しいと仮定すると、式を使用できます| View-Normal x(View.dotProduct(Normal))| と|ライト-標準x(Light.dotProduct(Normal))| (arctan( "something")を使用する代わりに)ファイ座標を取得するには?


誰でも手伝ってくれる?
Fabrizio Duroni 2015年

リポジトリ全体ではなく、正確なコードスニペットを表示できますか?
concept3d

これは、常にレイトレーシングに関する最も神秘的な質問の1つであるようです:D
Fabrizio Duroni


@ concept3dを完了しました。あなたはここでそれを見つけることができますcomputergraphics.stackexchange.com/questions/1799/...
ファブリツィオDuroni

回答:


2

それは実際に良いです 、BRDFを実装するために球座標(またはその点では任意の角度)を使用するのではなく、デカルト座標系でまっすぐに機能し、ベクトル間の角度の余弦を使用する方が良いです。これは、より堅牢で効率的です。

Oren-Nayarの場合、角度を使用する必要があると思うかもしれませんが(角度の最小/最大のため)、単純にデカルト空間でBRDFを実装できます:https : //fgiesen.wordpress.com/2010/10/21 / finish-your-derivations-please

Torrance-SparrowまたはCook-TorranceのマイクロファセットBRDFの場合、球面座標を使用する必要もありません。これらのBRDFでは、角度はD / F / G項およびBRDF分母で三角関数(通常はコサイン)関数に渡されるため、球座標を介さずに内積直線または三角関数のアイデンティティを使用できます。


1

法線Nと別のベクトルを指定して、座標系を指定できます。wiを選択します。したがって、接平面に投影されたときにwiと同じ方向を持つベクトルは、方位角が0になります。

まず、接平面にwiを投影します(wiは既に正規化されていると仮定)。

wit = normalize(wi - N * dot(wi, N))

今、私たちはwoで同じことができます:

wot = normalize(wo - N * dot(wo, N))

ここで、ウィットとウォットは、Nに直交する平面上にあり、交点に接しています。

ここで、2つの間の角度を計算できます。

azimuth = arcos ( dot(wit, wot) )

これは、接平面に投影したときの、ウィットに対するウォットの方位角です。


0

交点と起点がわかっている場合は、単に一方から他方を差し引くだけの質問ではないので、あたかも原点からのように結果が得られますか?

結果が信じられず、長い道のりを進みたい場合は、回転変換を取得して、LookAtマトリックスを介してあるポイントから別のポイントに移動し、それを分解して回転コンポーネントを取得することもできます。必要に応じて、そこから四元数を取得することもできます。

結果は同じです。証明は少し長いですが、複雑ではなく、読者に任されています。


こんにちは@Panda Pajama回答ありがとうございます。回答を理解できません。明確にしようとします。交差点と視点があれば、wiとwoを計算できます。次に、法線を天頂方向として使用して計算できますが、天頂に直交する平面上の方位角を見つけるために必要な他の軸を見つけることができません。上記の抜粋では、世界座標系で与えられたwiとwoの球面座標の変換式を単に適用しましたが、これがシータとファイを計算する正しい方法ではないと思います。
Fabrizio Duroni
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.