はじめに
おい!
数週間前、JS チャレンジの小さなデモを行いました。このデモは、手続き的に生成されたheightmapに基づいて風景を表示していました。それを 3D サーフェスとして表示するために、ランダム ポイントの補間された高さを評価し (モンテカルロ レンダリング)、それらを投影しました。
その時、私は自分の方法にいくつかの不具合があることをすでに認識していましたが、助けを求めて挑戦が終わるのを待っていました. 私はあなたを頼りにしています。:)
問題
したがって、次のスクリーンショットで主なエラーを確認できます。
スクリーンショット - 補間エラー? http://code.aldream.net/img/interpolation-error.jpg
中央に見えるように、いくつかのポイントは半島の上に浮かんでいるように見え、密度の低いレリーフのように形成されています。問題はグローバルに見えますが、色の違いにより、背後にある海では特に明白です。
現在の方法
サーフェス補間
サーフェスの各ポイントの高さを評価するために、三角形分割 + 重心座標による線形補間を使用しています。
- A = (X,Y)、B = (X+1, Y)、C = (X, Y+1)、D = (X+1) で、私の点(x, y)がである正方形 ABCD を見つけます, Y+1)、XとYはx, yの切り捨てられた値です。(各ポイントは私のハイトマップにマッピングされています)
- 条件を使用して、どの三角形 - ABD または ACD - 私の点がであるかを推定します: isInABD = dx > dy with dx, dy x, yの小数部。
- 線形補間を使用してポイントの高さを評価します。
- ABD の場合、高さ = h(B) + [h(A) - h(B)] * (1-dx) + [h(D) - h(B)] * dy
- ACD の場合、高さ = h(C) + [h(A) - h(C)] * (1-dy) + [h(D) - h(C)] * dx、h(X) の高さ地図。
表示中
ポイントを表示するには、(x、y、高さ)をワールド座標に変換し、頂点を投影します (ヨー角とピッチ角を使用した単純な透視投影を使用)。取得したピクセルを描画するかどうかを確認するために、更新し続けるzBufferを使用します。
試み
私の印象では、いくつかのポイントで、補間された高さが間違っています。したがって、三角形分割+線形補間の実装で、いくつかのエラーまたはカバーされていない境界のケースを検索しようとしました。しかし、もしあれば、私はそれらを見つけることができません。
私は他のデモでプロジェクションを使用しているので、ここで問題が発生するとは思いません。zBuffering に関しては、それがどのように関連しているのかわかりません...
ここで運が尽きてきました...どんなヒントでも大歓迎です!
ご清聴ありがとうございました。よい一日を!
別館
JsFiddle-デモ
これは、微調整したい人のために、わずかに単純化されたデモ全体の jsFiddle http://jsfiddle.net/PWqDL/です...
JsFiddle - 補間の小さなテスト
この質問を書き留めていたとき、内挿の結果をよく見てみようという考えが浮かびました。いくつかの色相値を含む 2x2 マトリックスを使用する簡単なテストを実装し、キャンバスに表示する前に中間色を補間しました。
これが jsFiddle です: http://jsfiddle.net/y2K7n/
残念ながら、結果は、私が行っている「三角」補間の種類で予想される動作と一致しているように見えるので、間違いなくアイデアが不足しています。
コードサンプル
そして、これはレンダリング方法を説明する私の JS コードの最もおそらく間違っている部分を簡略化したものです (ただし、ここでは言語はあまり重要ではないと思います)。サイズ(SIZE x SIZE) :
for (k = 0; k < nbMonteCarloPointsByFrame; k++) {
// Random float indices:
var i = Math.random() * (dim-1),
j = Math.random() * (dim-1),
// Integer part (troncated):
iTronc = i|0,
jTronc = j|0,
indTronc = iTronc*dim + jTronc,
// Decimal part:
iDec = i%1,
jDec = j%1,
// Now we want to intrapolate the value of the float point from the surrounding points of our map. So we want to find in which triangle is our point to evaluate the weighted average of the 3 corresponding points.
// We already know that our point is in the square defined by the map points (iTronc, jTronc), (iTronc+1, jTronc), (iTronc, jTronc+1), (iTronc+1, jTronc+1).
// If we split this square into two rectangle using the diagonale [(iTronc, jTronc), (iTronc+1, jTronc+1)], we can deduce in which triangle is our point with the following condition:
whichTriangle = iDec < jDec, // ie "are we above or under the line j = jTronc + distanceBetweenLandscapePoints - (i-iTronc)"
indThirdPointOfTriangle = indTronc +dim*whichTriangle +1-whichTriangle, // Top-right point of the square or bottm left, depending on which triangle we are in.
// Intrapolating the point's height:
deltaHeight1 = (displayHeightMap[indTronc] - displayHeightMap[indThirdPointOfTriangle]),
deltaHeight2 = (displayHeightMap[indTronc+dim+1] - displayHeightMap[indThirdPointOfTriangle]),
height = displayHeightMap[indThirdPointOfTriangle] + deltaHeight1 * (1-(whichTriangle? jDec:iDec)) + deltaHeight2 * (!whichTriangle? jDec:iDec),
posX = i*distanceBetweenLandscapePoints - SIZE/2,
posY = j*distanceBetweenLandscapePoints - SIZE/2,
posZ = height - WATER_LVL;
// 3D Projection:
var temp1 = cosYaw*(posY - camPosY) - sinYaw*(posX - camPosX),
temp2 = posZ - camPosZ,
dX = (sinYaw*(posY - camPosY) + cosYaw*(posX - camPosX)),
dY = sinPitch*temp2 + cosPitch*temp1,
dZ = cosPitch*temp2 - sinPitch*temp1,
pixelY = dY / dZ * minDim + canvasHeight,
pixelX = dX / dZ * minDim + canvasWidth,
canvasInd = pixelY * canvasWidth*2 + pixelX;
if (!zBuffer[canvasInd] || (dZ < zBuffer[canvasInd])) { // We check if what we want to draw will be visible or behind another element. If it will be visible (for now), we draw it and update the zBuffer:
zBuffer[canvasInd] = dZ;
// Color:
a.fillStyle = a.strokeStyle = EvaluateColor(displayHeightMap, indTronc); // Personal tweaking.
a.fillRect(pixelX, pixelY, 1, 1);
}
}