シェーダーでベクトルを別のベクトルで回転する


8

地形の各ポイントの法線を含む地形サーフェスがあります。

地形に適用する2番目の詳細法線マップがあります。

  • これらの法線は3スペースです。

  • 両方の法線のY値は 常に正です。

  • 両方の法線のX、Z値は、正/負/ゼロにすることができます。

  • 2番目のベクトル(オレンジ)を回転させる1番目の法線ベクトル(青)は、ほぼ水平にすることができます。

計算が簡単/高速になれば、おおよそのソリューションで大丈夫です。 青い表面の法線、オレンジの法線マップの法線、および緑の望ましい結果の法線の画像。

上の画像では、青い表面法線(1番目の法線マップから)、オレンジ色の法線マップ法線(2番目の法線マップから)、および目的の緑の法線が表示されています。

オレンジ色のベクトルの回転量は、青の法線ベクトルがXZ平面(DirectX座標系のようにYが上にある)と形成する角度とほぼ(または可能であれば正確に)等しくなければなりません。

次に、2番目のシナリオを示します。

青い表面の法線はほぼ水平なので、2番目の法線マップはほぼ垂直な表面に適用されているため、オレンジの法線マップベクトルはさらに回転しています。

上の画像では、青い表面法線がほぼ水平なので、2番目の法線マップがほぼ垂直な表面に適用されているため、オレンジ色の法線マップベクトルはさらに回転しています。

ローテーションはHLSLシェーダーで実装されています。

2番目の青い法線の方向に基づいて1番目のオレンジの法線を回転させる方法を教えてください。

編集:たぶん私は正接だけでなく正接と正接が必要ですか?

これが私が正常になる方法です:

float4 ComputeNormals(VS_OUTPUT input) : COLOR
{
    float2 uv = input.TexCoord;

    // top left, left, bottom left, top, bottom, top right, right, bottom right
    float tl = abs(tex2D(HeightSampler, uv + TexelSize * float2(-1, -1)).x);
    float  l = abs(tex2D(HeightSampler, uv + TexelSize * float2(-1,  0)).x);
    float bl = abs(tex2D(HeightSampler, uv + TexelSize * float2(-1,  1)).x);
    float  t = abs(tex2D(HeightSampler, uv + TexelSize * float2( 0, -1)).x);
    float  b = abs(tex2D(HeightSampler, uv + TexelSize * float2( 0,  1)).x);
    float tr = abs(tex2D(HeightSampler, uv + TexelSize * float2( 1, -1)).x);
    float  r = abs(tex2D(HeightSampler, uv + TexelSize * float2( 1,  0)).x);
    float br = abs(tex2D(HeightSampler, uv + TexelSize * float2( 1,  1)).x);

    // Compute dx using Sobel filter.
    //           -1  0  1 
    //           -2  0  2
    //           -1  0  1
    float dX = tr + 2*r + br - tl - 2*l - bl;

    // Compute dy using Sobel filter.
    //           -1 -2 -1 
    //            0  0  0
    //            1  2  1
    float dY = bl + 2*b + br - tl - 2*t - tr;

    // Compute cross-product and renormalize
    float3 N = normalize(float3(-dX, NormalStrength, -dY));    

    // Map [-1.0 , 1.0] to [0.0 , 1.0];
    return float4(N * 0.5f + 0.5f, 1.0f);
}

それでは、どうすれば接線ベクトルと正接ベクトルを取得できますか?

接線ベクトルを見つけるために、法線とZ軸の単位ベクトルの外積をとるだけで十分ですか?(normal.Yは常に正であり、Yは上で、Zは画面を指しています)。

そして、その接線ベクトルを取り、それを法線と交差させて、接線を取得しますか?

次に、法線、接線、バイタンジェントを組み合わせて回転行列を形成し、オレンジ色の法線マップベクトルを回転させますか?

それがうまくいくとしても、これはピクセルシェーダーの多くの計算のようです。これは機能しますか?より効率的な方法はありますか?

編集:

この画像は、私が何をしようとしているのかを理解するのに役立ちます。 通常マッピング。

通常のマッピングが何か知っているなら、これは簡単だと思います。

さまざまな法線が含まれている法線マップを取得して、サーフェスに適用しようとしています。サーフェスには独自の法線があります。法線マップには、サーフェスよりもはるかに多くの法線が含まれるため、法線が1つしかないサーフェスの単一の部分でいくつかの法線マップの法線がサンプリングされます。


あなたの質問は意味がありません。あなたは、ベクトルを回転させることができないことで(あなたが回転することができ、ベクターの周りベクトル、実際にあなたがしなければならない 3空間における回転軸を指定します)。ベクトルが平面となす角度を見つけることができます。ただし、表面法線の場合は90度と定義されます。さらに、前述したように回転軸を指定しない限り、ベクトルを90度回転しても意味がありません。
Andrew Russell、

表面の法線は、表面から90度で、XZ平面から他の角度です(Direct Xのように、Yが上で、Zが画面の方向です)。法線がXZ平面となす角度でベクトルを回転させます。
Olhovsky

私がやろうとしていることは、地形面に法線マップを適用することです。正接ベクトルと正接ベクトルを見つけて、サーフェス法線と一緒に使用して行列を形成し、オレンジ色の法線マップ法線を掛けて、緑の結果法線を取得するのが1つの方法だと思います。正接と正接を見つける方法、またはもっと簡単な方法があるかどうかわかりません。質問を更新して、地形から通常の方法を取得する方法を示しました。
Olhovsky

質問に別の画像を追加して、自分がやろうとしていることをより明確にしました。それほど凝ったものはなく、通常のマッピングだけです。
Olhovsky

あなたの最新の図は作るずっとより多くの意味を。
Andrew Russell、

回答:


4

タイトルと最初の2枚の写真は混乱しますが、私はあなたの質問を理解すると思います。

問題は、xz平面(オレンジ色のベクトル)にマップされる法線マップがあることです。これで、青い法線で表される実際の平面は必ずしもxz平面にあるとは限らないため、オレンジのベクトルを回転させて実際の平面にマッピングするには、xz平面から実際の平面への回転を見つける必要があります。

では、どうすればよいでしょうか。基本的に、xz平面を青い法線で表される平面に回転させる回転を見つける必要があります。

  1. 青い法線とxz平面{0、1、0}からの法線の外積をとって回転軸を見つけ、オレンジ色のベクトルをこの軸の周りで回転させる必要があります。

    axis = N_xz cross N_blue
  2. 両方の法線の内積の逆余弦を取ることにより、回転角度を求めます

    angle = acos(N_xz dot N_blue)
  3. 今、回転行列を作成し、それらの値から、そのマトリックスと通常のオレンジを変換します。


あなたが説明することはうまくいくかもしれませんが、私はシェーダーでそれをすべてフレームごとに920000回(つまり、ピクセルごとに1回)行うことにうんざりしています。通常のcross(normal、float3(1,0,0))とcross(normal、float3(0,0,1))から行列を作成できますか?動作していないようですが、私は今それを試しています。
Olhovsky、2011

質問に画像を追加して、自分がやろうとしていることをより明確にしました。それほど凝ったものはなく、通常のマッピングだけです。
Olhovsky

頂点シェーダーでプレーンごとに1回そのマトリックスを計算してピクセルシェーダーに渡すか、プレーンごとにCPUで計算してシェーダーにそのマトリックスを渡すことができます。しかし、これは最適化しています。まず実行して、それを行う方法を理解してから、最適化します;)
Maik Semder '22

さて、今それを試してみてください。
Olhovsky

これは機能します、+ 1。ありがとう。しかし、それは少し遅いです。青の法線を格納する代わりに、この回転行列のクォータニオンを格納する方法を理解し、そのクォータニオンから法線を構築しようとしています。私がどうやってそれをするのか知っていますか?
Olhovsky

2

あなたは自分の問題を考えすぎていると思います。

標準の法線マッピングに戻るには、通常、3つのベクトルがあります。サーフェス法線、その法線に垂直な接線ベクトル、および両方に垂直な余接ベクトルです。これらは一緒に、法線マップが方向を表す接線空間を形成します。このセットアップでは、法線マップの各コンポーネントの読み取り値とそれぞれの接線空間軸を乗算し、結果を合計するだけです。

これで、地形があり、それに適用したい法線マップがあります。デルタの高さを取得し、表面の法線を生成する方法はすでにわかっています。ここで、タンジェントとコタンジェントを探しています。

接線とは何ですか?さて、上記の説明から、それは実際にはテクスチャマッピングのUV軸の1つによって記述された3D方向です。結局のところ、それが使用されるのですよね?では、この方向性をどうやって見つけますか?まあ、任意のメッシュを考えると、それは少しトリッキーです。しかし、通常のグリッドテレインが与えられれば、それは自明です。通常のUVを持つ通常のグリッドがある場合、接線と余接方向は、頂点を離れた2つの軸に沿ったエッジにすぎません。

だから、あなたはあなたの周りのポイントの高さが何であるかを知っています。これらの点が平面上でどれだけ離れているかを渡すことができます。現在のポイントからそれらを差し引き、正規化すると、インスタントタンジェント空間になります。


+1はこれが役立つ投稿であるためです。私を接線空間に変換する基底行列を作成できることを知っています(これは、私が提案していることだと思います)。私はそれを形成する方法がわかりません。あなたの答えは、開始方法をほのめかしていますが、実際にそれを行う方法を示していません。たとえば、「これらの点が平面上でどれだけ離れているかを渡すことができます。現在の点からそれらを差し引いて、正規化すると、すぐに接線空間になります。」本当に曖昧です。現在、私の法線マップは接線空間にありません。ここは午前4時なので、明日また訪れます。
Olhovsky

0

接線もある場合は、この関数を使用して、後のバンプされた法線を生成できます。それ以外の場合は、今後も自由に盗用してください!

//------------------------------------------------------------------------------
// Transforms a normal map sample to world space.
//------------------------------------------------------------------------------


  float3 NormalSampleToWorldSpace(float3 normalMapSample, float3 unitNormalW, float3 tangentW)
    {
        // Uncompress each component from [0,1] to [-1,1].
        float3 normalT = 2.0f * normalMapSample - 1.0f;

        // Build orthonormal basis.
        float3 N = unitNormalW;
        float3 T = normalize(tangentW - dot(tangentW, N) * N);
        float3 B = cross(N, T);

        float3x3 TBN = float3x3(T, B, N);

        // Transform from tangent space to world space.
        float3 bumpedNormalW = mul(normalT, TBN);

        return bumpedNormalW;
    }

-1

LERP関数を使用できます。これにより、2つのベクトル間が線形補間されます。あなたに素敵な「平均的な」正常を与えます。

http://msdn.microsoft.com/en-us/library/bb509618(VS.85).aspx

(もちろん、これは実際には回転していませんが、オレンジと青のベクトルが与えられると、これは緑のベクトルを取得します)


ありがとう。ただし、青いベクトルはXZ平面から約30度上げることができます。その場合、レルピングは機能しません(何かが欠けている場合を除きます)。ところで、あなたの写真を認識しました。XNAのA *検索の例を書いてくださいました。
Olhovsky

ねえはい、それは私です:)。あなたの質問については、レルピングはまだ機能すると思いますが、ベクトルを再正規化する必要があるかもしれません。それが機能しない場合、小さなテストケースを提供できますか(HLSLにある必要はありません。XNAのベクターにもLerpがあります)。期待される結果と返される結果の2つのベクターを使用して、詳細を説明できる場合があります。 。
ロイT.

lerpingが機能しない理由を示すために、2つ目の画像を追加しました。オレンジの法線マップ法線は、サーフェスに適用されるように回転する必要があります。たとえば、オレンジの法線マップの法線と等しいサーフェス法線があるが、両方が回転している場合、どの程度の量でレルピングしても同じベクトルが得られ、回転はまったく行われません。
Olhovsky
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.