2011-06-28 24 views
8

OpenGLジオメトリシェーダで地形を生成していて、照明の法線を計算する際に問題があります。私は幾何シェーダに実装されたパーリンノイズ関数を使って各フレームを動的に生成しています。このため、ノイズ関数(テクスチャなどはありません)に基づいて、頂点ごとに法線を計算する効率的な方法が必要です。私は顔の法線を得るために2面の積を取ることができますが、ジオメトリを使って動的に生成されるので、頂点の法線の顔の法線を元に戻すことはできません。 y平面上の地形の高さを生成するノイズ関数を使用するだけで、頂点法線を即座に得ることができます(したがって、高さは1と-1の間です)。 ...私はノイズ関数に各頂点の4倍をサンプリングする必要があると考えていますが、私は次のようなものを試してみました、それが動作しませんでしたパーリンノイズのように動き回るパーリンノイズの頂点ごとの法線?

vec3 xP1 = vertex + vec3(1.0, 0.0, 0.0); 
vec3 xN1 = vertex + vec3(-1.0, 0.0, 0.0); 
vec3 zP1 = vertex + vec3(0.0, 0.0, 1.0); 
vec3 zN1 = vertex + vec3(0.0, 0.0, -1.0); 

float sx = snoise(xP1) - snoise(xN1); 
float sz = snoise(zP1) - snoise(zN1); 

vec3 n = vec3(-sx, 1.0, sz); 
normalize(n); 

return n; 

上記実際に発生した照明!頂点ごとの法線を正しく得るためのアドバイスはありますか?

答えて

5

実際にポジションをどのように生成したのか正確には言いませんでした。そこで、高さマップで高さ値を生成するためにPerlinノイズを使用していると仮定します。したがって、hieghtmapの任意の位置X、Yに対して、2Dノイズ関数を使用してZ値を生成します。

それでは、次のように自分の位置が計算されていると仮定しましょう:

vec3 CalcPosition(in vec2 loc) { 
    float height = MyNoiseFunc2D(loc); 
    return vec3(loc, height); 
} 

これは、3次元位置を生成します。しかし、何でスペースのこの位置ですか?それが問題です。

ほとんどのノイズ関数は、ある種の浮動小数点範囲ではlocが2つの値になると予測しています。あなたのノイズ関数がどれほど優れているかによって、値を渡すことができる範囲が決まります。モデル空間の2D位置がノイズ関数の範囲内にあることが保証されていない場合は、その範囲に変換し、計算を行い、それからにモデル空間に変換してください。

これで、3Dの位置になりました。 XとYの値の変換は単純です(ノイズ関数の空間への変換の逆ですが)。ここでは、高さにある程度のスケールを適用する必要があります。ノイズ関数は[0、1]の範囲の数値を返します。したがって、この範囲をXとYの値と同じモデル空間に合わせる必要があります。これは、通常、最大の高さを選択し、位置を適切に調整することによって行われます。

vec3 CalcPosition(in vec2 modelLoc, const in mat3 modelToNoise, const in mat4 noiseToModel) 
{ 
    vec2 loc = modelToNoise * vec3(modelLoc, 1.0); 
    float height = MyNoiseFunc2D(loc); 
    vec4 modelPos = noiseToModel * vec4(loc, height, 1.0); 
    return modelPos.xyz; 
} 

二つの行列は、ノイズ関数の空間に変換した後、バック変換:したがって、私たちの改訂版のcalc位置は次のようになります。実際のコードでは、ユースケースに応じて複雑な構造を使用することはできませんが、完全なアフィン変換は簡単に記述できます。

これで、あなたが覚えておく必要があるのは、次のとおりです。スペースがどれだけあるか分からない限り意味がありません。あなたの普通の位置、何も問題ありません。に。

この関数は、モデル空間内の位置を返します。法線はモデルスペースで計算する必要があります。これを行うには、頂点の現在の位置と、現在の位置からわずかにオフセットした2つの位置の3つの位置が必要です。 のポジションはでなければなりません。あなたは一つにこれらの二つの機能をマージすることができ、明らかに

void CalcDeltas(in vec2 modelLoc, const in mat3 modelToNoise, const in mat4 noiseToModel, out vec3 modelXOffset, out vec3 modelYOffset) 
{ 
    vec2 loc = modelToNoise * vec3(modelLoc, 1.0); 
    vec2 xOffsetLoc = loc + vec2(delta, 0.0); 
    vec2 yOffsetLoc = loc + vec2(0.0, delta); 
    float xOffsetHeight = MyNoiseFunc2D(xOffsetLoc); 
    float yOffsetHeight = MyNoiseFunc2D(yOffsetLoc); 
    modelXOffset = (noiseToModel * vec4(xOffsetLoc, xOffsetHeight, 1.0)).xyz; 
    modelYOffset = (noiseToModel * vec4(yOffsetLoc, yOffsetHeight, 1.0)).xyz; 
} 

したがって、我々は、以下の機能を持っている必要があります。

deltaの値は、ノイズテクスチャの入力スペースの小さなオフセットです。このオフセットのサイズはノイズ関数に依存します。実際の現在の位置から返された高さと大幅に異なる高さを返すのに十分な大きさである必要があります。しかし、それはで十分である必要があります。ノイズ分布のランダムな部分から引っ張らないようにしてください。

あなたのノイズ機能を知る必要があります。今、あなたは3ポジションを持っていることを

(現在の位置は、xがオフセット、とyオフセット)モデル空間で、あなたがモデル空間で通常の頂点を計算することができます:ここから

vec3 modelXGrad = modelXOffset - modelPosition; 
vec3 modelYGrad = modelYOffset - modelPosition; 

vec3 modelNormal = normalize(cross(modelXGrad, modelYGrad)); 

、普通のことをする。しかし、決してあなたのさまざまなベクトルのスペースを追跡することを忘れないでください。

ああ、もう1つ:頂点シェーダで行う必要があります。ジオメトリシェーダでこれを行う理由はありません。計算のどれも他の頂点に影響しないからです。あなたにGPUの並列処理をさせてください。

+0

私はこのようなアプローチを考え、試して失敗しました。あなたの答えを読んだ後、私はおそらく正しい場所にすべてがあるわけではなく、エラーを引き起こしています。頂点、ジオメトリ、およびフラグメントシェーダーを使って作業しているので、私が何か空間を保持しているかどうかをさらに細心の注意を払わなければなりません。明示的にこのメソッドを使用し、何が起こるかを見てみましょう。 – Nitrex88

+0

私はこのアプローチで作業しました。しかし、それを動作させるには、4つのオフセットベクトルを作成し、オフセットの勾配(modelPositionのオフセットの勾配ではありません)を使用する必要がありました。そして、適切な方向に照明を得るためには、modelXGradとmodelYGradのクロス製品を取る順番を入れ替えなければなりませんでした。座標空間をとてもうまく説明してくれてありがとう。それが助けになった。また、ジオメトリシェーダでジオメトリシェーダの地形をテッセレーションするので、ジオメトリシェーダでこれを実行していたことを言いたいと思います – Nitrex88

8

法線は接線に垂直なベクトルです(傾きとも呼ばれます)。関数の傾きはその派生関数です。 n次元の部分偏微分したがって、中心点PおよびP±(δx、0)およびP±(0、δy)の周りのノイズをサンプリングします.δx、δyはできるだけ小さく選択されますが、数値安定性は十分に大きくなります。これにより、各方向の接線が得られます。次に、あなたはそれらのクロス積を取って、結果を正規化し、Pで法線を得ました。

+1

+1:これは適切なアプローチですが、シェーダでどの程度実用的であるかわかりません。 –

+0

この回答は、他の長い回答(基本的には)と同じことを言っています。いずれにせよ、私は明日試して、それがうまくいくかどうかを見ていきます。 – Nitrex88

+2

@ Nitrex88:@Nicol Bolasの答えは、私と同じことをあなたに伝えていますが、より詳細で洗練された形です。もちろん、あなたはあなたがどの空間にいるのかを把握しておく必要があります。しかし、関数の観点から見れば、偏微分、つまりグラジエントになります。ノイズのサンプリング座標を「揺らす」と、それに応じてノイズ値が変化します。次に、入力ウィグル空間ベクトルを出力ウィグル空間に関連付けることだけです。 – datenwolf

関連する問題