web-dev-qa-db-ja.com

高さマップから法線マップを生成しますか?

私は、ビデオゲーム用のランダム化されたフラクタルを使用して、汚れのパッチを手続き的に生成することに取り組んでいます。中点変位アルゴリズムを使用して高さマップを既に生成し、テクスチャに保存しました。私はそれを法線のテクスチャに変える方法についていくつかのアイデアを持っていますが、いくつかのフィードバックをいただければ幸いです。

私の高さテクスチャは現在257 x 257のグレースケール画像です(高さの値は可視性のためにスケーリングされています):

enter image description here

私の考えでは、画像の各ピクセルは256 x 256グリッドの格子座標を表します(したがって、257 x 257の高さがある理由)。これは、座標(i、j)の法線が(i、j)、(i、j + 1)、(i + 1、j)、および(i + 1、j + 1の高さによって決定されることを意味します)(それぞれ、A、B、C、およびDを呼び出します)。

したがって、A、B、C、およびDの3D座標が与えられた場合、次のことには意味があります。

  1. 4つをABCとBCDの2つの三角形に分割します
  2. 外積を介してこれらの2つの面の法線を計算する
  3. 2つの三角形に分割:ACDとABD
  4. これらの2つの面の法線を計算します
  5. 4つの法線の平均

...または私が見逃しているはるかに簡単な方法はありますか?

34
Dawson

水面レンダリングシェーダーのGLSLコードの例:

#version 130
uniform sampler2D unit_wave
noperspective in vec2 tex_coord;
const vec2 size = vec2(2.0,0.0);
const ivec3 off = ivec3(-1,0,1);

    vec4 wave = texture(unit_wave, tex_coord);
    float s11 = wave.x;
    float s01 = textureOffset(unit_wave, tex_coord, off.xy).x;
    float s21 = textureOffset(unit_wave, tex_coord, off.zy).x;
    float s10 = textureOffset(unit_wave, tex_coord, off.yx).x;
    float s12 = textureOffset(unit_wave, tex_coord, off.yz).x;
    vec3 va = normalize(vec3(size.xy,s21-s01));
    vec3 vb = normalize(vec3(size.yx,s12-s10));
    vec4 bump = vec4( cross(va,vb), s11 );

結果は、バンプベクトルです:xyz = normal、a = height

42
kvark

私の考えでは、画像の各ピクセルは256 x 256グリッドの格子座標を表します(したがって、257 x 257の高さがある理由)。これは、座標(i、j)の法線が(i、j)、(i、j + 1)、(i + 1、j)、および(i + 1、j + 1の高さによって決定されることを意味します)(それぞれ、A、B、C、およびDを呼び出します)。

いいえ。画像の各ピクセルはグリッドの頂点を表しているため、直感的には、対称から、その法線は隣接するピクセルの高さ(i-1、j)、(i + 1、j)、(i、j- 1)、(i、j + 1)。

関数fが与えられた場合:ℝ2 →ℝで表面を記述するin3、(x、y)での法線単位は

v =(−∂f /∂x、−∂f /∂y、1)and n = v/| v |。

2つのサンプルによる∂f/∂xの最適な近似は、以下によってアーカイブされることが証明できます。

∂f/∂x(x、y)=(f(x +ε、y)− f(x−ε、y))/(2ε)

より良い近似を得るには、少なくとも4つのポイントを使用する必要があります。したがって、3番目のポイント(つまり(x、y))を追加しても結果は改善されません。

Hightmapは、通常のグリッド上の関数fのサンプリングです。 ε= 1を取得すると、次のようになります。

2v =(f(x−1、y)− f(x + 1、y)、f(x、y−1)− f(x、y + 1)、2)

16
ybungalobill

一般的な方法は、 Sobel フィルターを各方向の加重/平滑化された微分に使用することです。

各テクセルの周囲の高さの3x3領域をサンプリングすることから始めます(ここでは、[4]は、私たちが法線を求めるピクセルです)。

[6][7][8]
[3][4][5]
[0][1][2]

次に、

//float s[9] contains above samples
vec3 n;
n.x = scale * -(s[2]-s[0]+2*(s[5]-s[3])+s[8]-s[6]);
n.y = scale * -(s[6]-s[0]+2*(s[7]-s[1])+s[8]-s[2]);
n.z = 1.0;
n = normalize(n);

scaleは、サイズに応じてハイトマップの実世界の深さに一致するように調整できます。

12
jozxyqk

各ピクセルを面ではなく頂点と考える場合、単純な三角形メッシュを生成できます。

+--+--+
|\ |\ |
| \| \|
+--+--+
|\ |\ |
| \| \|
+--+--+

各頂点には、マップ内のピクセルのxおよびyに対応するxおよびy座標があります。 z座標は、その場所のマップの値に基づいています。三角形は、グリッド内の位置によって明示的または暗黙的に生成できます。

必要なのは、各頂点の法線です

vertex法線は、その点で交わる各三角形のsurface法線の面積加重平均を取ることで計算できますポイント。

頂点v0v1v2の三角形がある場合、(三角形の2つの辺にある2つのベクトルの)ベクトル外積を使用して計算できます法線方向のベクトルで、三角形の面積に比例してスケーリングされます。

Vector3 contribution = Cross(v1 - v0, v2 - v1);

Edgeにない各頂点は、6つの三角形で共有されます。これらの三角形をループしてcontributionsを合計し、ベクトルの合計を正規化できます。

注:法線がすべて同じ方向を指していることを確認するために、一貫した方法で外積を計算する必要があります。常に同じ順序(時計回りまたは反時計回り)で両側を選んでください。それらのいくつかを混ぜると、それらの貢献は反対方向を指します。

エッジ上の頂点の場合、短いループと多くの特殊なケースになります。おそらく、偽の頂点のグリッドの周りに境界線を作成してから、内部の頂点の法線を計算し、偽の境界線を破棄する方が簡単でしょう。

for each interior vertex V {
  Vector3 sum(0.0, 0.0, 0.0);
  for each of the six triangles T that share V {
    const Vector3 side1 = T.v1 - T.v0;
    const Vector3 side2 = T.v2 - T.v1;
    const Vector3 contribution = Cross(side1, side2);
    sum += contribution;
  }
  sum.Normalize();
  V.normal = sum;
}

三角形の特定のポイント(頂点の1つ以外)で法線が必要な場合、3つの頂点の法線をポイントの重心座標で重み付けすることで補間できます。これが、グラフィックラスタライザがシェーディングの法線を処理する方法です。これにより、三角形のメッシュを、隣接する平らな三角形の束ではなく、滑らかな曲面のように見せることができます。

ヒント:最初のテストでは、完全に平らなグリッドを使用し、計算された法線がすべて真上を向いていることを確認します。

9
Adrian McCarthy