介绍
嘿!
几周前,我为一个 JS 挑战做了一个小演示。该演示显示基于程序生成的高度图的景观。为了将其显示为 3D 表面,我正在评估随机点的插值高度(蒙特卡罗渲染),然后投影它们。
那时,我已经意识到自己的方法有一些小问题,但我正在等待挑战结束寻求帮助。我指望你。:)
问题
所以我得到的主要错误可以在下面的截图中看到:
屏幕截图 - 插值错误?http://code.aldream.net/img/interpolation-error.jpg
正如您在中心看到的那样,一些点似乎漂浮在半岛上方,形成了一个密度较低的浮雕。由于颜色差异,后面的海尤其明显,尽管问题似乎是全球性的。
当前方法
曲面插值
为了评估表面每个点的高度,我使用重心坐标的三角测量+线性插值,即:
- 我发现我的点(x, y)在哪个正方形 ABCD中,其中A = (X,Y), B = (X+1, Y), C = (X, Y+1) 和 D = (X+1 , 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, height)转换为世界坐标,投影顶点(使用带有偏航角和俯仰角的简单透视投影)。我使用一个我不断更新的zBuffer来检查我是否绘制了获得的像素。
尝试
我的印象是,对于某些点,我得到了错误的插值高度。因此,我尝试在三角测量+线性插值的实现中搜索一些错误或一些未覆盖的边界情况。但如果有的话,我看不到它们。
我在其他演示中使用投影,所以我认为问题不是来自这里。至于zBuffering,我看不出它是如何相关的......
我在这里运气不好...欢迎任何提示!
感谢您的关注,祝您有美好的一天!
附件
JsFiddle - 演示
这是整个稍微简化的演示的 jsFiddle http://jsfiddle.net/PWqDL/,适合那些想要调整的人......
JsFiddle - 插值的小测试
当我写下这个问题时,我有了一个更好地了解插值结果的想法。我实现了一个简单的测试,其中我使用了一个包含一些色调值的 2x2 矩阵,并在将它们显示在画布中之前插入了中间颜色。
这是 jsFiddle:http: //jsfiddle.net/y2K7n/
唉,结果似乎与我正在做的那种“三角形”插值的预期行为相匹配,所以我肯定没有想法了。
代码示例
这是我的 JS 代码中描述我的渲染方法的最有可能错误的简化部分(但我认为语言在这里并不重要),给定一个正方形高度图“ displayHeightMap ”,其大小为(dim x dim)用于风景大小(大小 x 大小):
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);
}
}