javascript - 三 Angular 测量 + 线性插值 + 3D 投影的毛刺

标签 javascript 3d geometry interpolation projection

简介


嘿!

几周前,我为 JS 挑战做了一个小演示。该演示显示了基于程序生成的高度图的景观。为了将其显示为 3D 表面,我评估了随机点的插值高度(蒙特卡洛渲染),然后对其进行投影。

当时,我已经意识到自己的方法存在一些问题,但我正在等待挑战结束来寻求帮助。我指望你。 :)

问题


所以我得到的主要错误可以在下面的屏幕截图中看到:

Screenshot - Interpolation Error? http://code.aldream.net/img/interpolation-error.jpg

正如您在中心看到的那样,有些点似乎漂浮在半岛上方,形成密度较小的浮雕。尽管这个问题看起来是全局性的,但由于颜色差异,在大海后面尤其明显。

当前方法


表面插值

为了评估表面每个点的高度,我使用三 Angular 测量+带有重心坐标的线性插值,即:

  1. 我找到我的点(x, y)在哪个方格ABCD,其中A = (X,Y), B = (X+1 ,Y),C = (X, Y+1) 和 D = (X+1, Y+1)XY 被 chop x, y 的值。 (每个点都映射到我的高度图)
  2. 我估计我的点位于哪个三 Angular 形 - ABD 或 ACD,使用条件:isInABD = dx > dydx, dy> x, y 的小数部分。
  3. 我使用线性插值法评估点的高度:
    • 如果在 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 ) 距 map 的高度。

显示

要显示该点,我只需将(x,y,高度)转换为世界坐标,投影顶点(使用带有偏航 Angular 和俯仰 Angular 的简单透视投影 )。我使用一个不断更新的zBuffer来检查我是否绘制了获得的像素。

尝试


我的印象是,对于某些点,我得到了错误的插值高度。因此,我尝试在三 Angular 测量+线性插值的实现中寻找一些错误或一些未覆盖的边界情况。但即使有,我也找不到它们。

我在其他演示中使用了投影,所以我认为问题不是来自这里。至于zBuffering,我看不出它有什么关系......

我的运气已经不够了...非常欢迎任何提示!

感谢您的关注,祝您有美好的一天!



附件


JsFiddle - 演示

这是一个 jsFiddle http://jsfiddle.net/PWqDL/整个稍微简化的演示,对于那些想要调整的人......

JsFiddle - 插值的小测试

当我写下这个问题时,我想到了更好地查看插值结果的想法。我实现了一个简单的测试,其中使用包含一些色调值的 2x2 矩阵,并在 Canvas 中显示中间颜色之前对它们进行插值。

这是 jsFiddle:http://jsfiddle.net/y2K7n/

,结果似乎与我正在做的“三 Angular 形”插值的预期行为相符,所以我肯定没有想法了。

代码示例

这是我的 JS 代码中最有可能出错的简化部分,描述了我的渲染方法(但我认为语言在这里并不重要),给定一个方形高度图“displayHeightMap”尺寸为 (dim x dim) 的景观尺寸为 (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);
        }
    }

最佳答案

明白了。正如预期的那样,这是一个愚蠢的错误:我每帧都重新初始化我的 zBuffer...

通常这是您应该做的,但在我的情况下,每个(即调用我的Painting()函数)都会向同一帧添加详细信息(即从恒定的给定 Angular 绘制静态场景)。

如果我在每次调用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.
}

有趣的事实

如果在大海后面的浮雕中,故障显得更加明显,这不仅仅是因为颜色差异,而是因为景观的丘陵部分比平坦区域(如大海)需要更多的点来渲染,鉴于其拉伸(stretch)表面

我对点的简单化蒙特卡罗采样没有考虑到这一特性,这意味着在每次调用Painting()时,海洋在统计上获得了更多的密度比土地

由于每帧都会重新初始化 zBuffer,因此在图片中本应被山脉覆盖的区域中,大海“赢得了战斗”(解释了那里的“幽灵般的山脉”效果)。

更正 JsFiddle

感兴趣的更正版本:http://jsfiddle.net/W997s/1/

关于javascript - 三 Angular 测量 + 线性插值 + 3D 投影的毛刺,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16441376/

相关文章:

javascript - 将对象插入数组会导致 TypeError

math - 将 3d 面绘制为 2d

algorithm - 如何计算某物是否在某人的视野中

3d - 计算3D空间中单个三角形的法线

c++ - 三角形 - 二维正方形相交测试

javascript - textarea 控件 - 自定义行为 enter/ctrl+enter

javascript - 在页面刷新时以正确的数字启动字符计数器

JavaScript <%= MyProperty %>

javascript - 在拖动和滚动时旋转 javascript 立方体

opengl - 在opengl中围绕兴趣点旋转相机