喂!
几周前,我为JS挑战做了一个小型演示。此演示显示基于程序生成的高度图的格局。为了将其显示为3D表面,我正在评估随机点的插值高度(蒙特卡罗渲染)然后投影它们。
当时,我已经意识到我的方法中存在一些故障,但我正在等待挑战结束以寻求帮助。我指望你。 :)
所以我得到的主要错误可以在以下屏幕截图中看到:
Screenshot - Interpolation Error? http://code.aldream.net/img/interpolation-error.jpg
正如你在中心所看到的,有些点似乎漂浮在半岛之上,形成了一个不那么密集的浮雕。特别明显的是,由于色差,背后的海洋,即使问题看似全球化。
为了评估表面每个点的高度,我使用带有重心坐标的三角测量+线性插值,即:
要显示该点,我只需将(x,y,height)转换为世界坐标,投影顶点(使用简单的透视投影与偏航和俯仰角度)。我使用 zBuffer 我会不断更新以检查我是否绘制了获得的像素。
我的印象是,对于某些点,我得到了错误的插值高度。因此,在我的三角测量+线性插值的实现中,我试图搜索一些错误或一些未覆盖的边界情况。但如果有,我无法发现它们。
我在其他演示中使用投影,所以我不认为问题来自这里。至于 zBuffering ,我看不出它是如何相关的......
我运气不好......任何提示都是最受欢迎的!
感谢您的关注,祝您度过愉快的一天!
这是一个简短的简化演示的jsFiddle http://jsfiddle.net/PWqDL/,适合想要调整的人......
当我写下这个问题时,我有一个想法,可以更好地了解插值的结果。我实现了一个简单的测试,其中我使用了一个包含一些色调值的2x2矩阵,并且我在插入画布之前插入中间色。
这是jsFiddle:http://jsfiddle.net/y2K7n/
唉,结果似乎与我正在做的那种“三角形”插值的预期行为相匹配,所以我肯定没有想法。
这是我的JS代码中描述我的渲染方法的简化的最可能有缺陷的部分(但我认为这里的语言并不重要),给定方形高度图“ displayHeightMap ”尺寸为(尺寸x尺寸)的尺寸为(暗淡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);
}
}
答案 0 :(得分:2)
知道了。这是一个像预期的那样愚蠢的错误:我每帧重新初始化我的zBuffer ......
通常这是你应该做的,但在我的情况下,每个框架(即我的 Painting()函数的调用)将细节添加到同一个框架(即从常量给定的角度绘制静态场景)。
如果我在每次调用 Painting()时重置zBuffer,我将丢失在之前调用期间绘制的点的深度信息。因此,相应的像素被视为空白,并且会针对任何投影点重新绘制,而不考虑其深度。
注意:如果没有重新启动,zBuffer会变得非常大。我之前应该做的另一个修复是将投影点的像素位置(以及zBuffer的索引)转换为整数值:
pixelY = dY / dZ * minDim + canvasHeight +.5|0,
pixelX = dX / dZ * minDim + canvasWidth +.5|0,
canvasInd = pixelY * canvasWidth*2 + pixelX;
if (dZ > 0 && (!zBuffer[canvasInd] || (dZ < zBuffer[canvasInd]))) {
// We draw the point and update the zBuffer.
}
如果后面的海面上的毛刺显得更加明显,那不仅是因为颜色差异,而且因为景观的丘陵部分需要比平坦区域(如大海)更多的点,给出拉伸表面。
我简单的蒙特卡罗点数采样不考虑这个特征,这意味着每次调用绘画(),海洋在统计上获得更多密度比土地。
由于zBuffer每一帧的重新初始化,因此大海在山脉应该覆盖它的图片区域中“赢得战斗”(解释那里的“幽灵山”效应)。
感兴趣的修正版:http://jsfiddle.net/W997s/1/