使用三次贝塞尔曲线渲染平滑地形?

时间:2014-02-08 21:07:51

标签: java algorithm graphics bezier spline

我正在努力制作一个有更好的补丁的山地景观。

我有它确保C1连续性,但它没有成功。

image describing the error

我对该函数的输入虽然是在计算之前生成的,但它是由n x n网格的高度组成的高度图。
然后在我的方法中,我想为每个网格生成16个用于立方贝塞尔补丁的控制字。

但是我无法这样做,因为这导致了奇怪的结果,我的代码:

    new PatchIterator(width).forEachRemaining(p -> {
        int w = p.first, d = p.second;
        Patch patch = new Patch();
        boolean lastWidthPatchExists = (w - 1 >= 0 && patches[w - 1][d] != null);
        boolean lastDepthPatchExists = (d - 1 >= 0 && patches[w][d - 1] != null);
        if (!lastWidthPatchExists && !lastDepthPatchExists) {
            patch.createFirstPatch(heightMapping[w][d]);
        }
        else if (lastWidthPatchExists && !lastDepthPatchExists) {
            patch.createWidthConstrainedPatch(heightMapping[w][d], patches[w - 1][d]);
        }
        else if (!lastWidthPatchExists && lastDepthPatchExists) {
            patch.createDepthConstrainedPatch(heightMapping[w][d], patches[w][d - 1]);
        }
        else {
            patch.createWithAndDepthConstrainedPatch(heightMapping[w][d], patches[w - 1][d], patches[w][d - 1]);
        }
        patch.submitAll(terrainData, w, d);
        patches[w][d] = patch;
    });

private class Patch {
    final float[][] patchData = new float[4][4];

    /**
     * Creates the first patch, which is not constrained to anything else.
     * 
     * @param height The desired height for the patch
     */
    void createFirstPatch(final float height) {
        loop((x, z) -> {
            patchData[x][z] = height;
        });
    }

    /**
     * Creates a patch that is constrained by the previous patch in width, this guarantees C1-continuity of the Bezier Surface.
     * 
     * @param height The desired height for the patch
     * @param lastWidthPatch The last patch in the width direction
     */
    void createWidthConstrainedPatch(final float height, final Patch lastWidthPatch) {
        loop((x, z) -> {
            float y;
            if (x == 0) {
                //First point should be the same as the last point of the previous patch (C0-continuity)
                y = lastWidthPatch.patchData[3][z];
            }
            else if (x == 1) {
                //The three points (previous patch last two and current patch first two) should be on a line (C1-continuity)
                float previousDifference = lastWidthPatch.patchData[3][z] - lastWidthPatch.patchData[2][z];
                y = lastWidthPatch.patchData[3][z] + previousDifference;
            }
            else {
                //No constraints apply to these points
                y = height;
            }
            patchData[x][z] = y;
        });
    }

    /**
     * Creates a patch that is constrained by the previous patch in depth, this guarantees C1-continuity of the Bezier Surface.
     * 
     * @param height The desired height for the patch
     * @param lastDepthPatch The last patch in the depth direction
     */
    void createDepthConstrainedPatch(final float height, final Patch lastDepthPatch) {
        float[] yValues = new float[4];
        Arrays.fill(yValues, 0f);
        loop((x, z) -> {
            float y;
            if (z == 0) {
                //First point should be the same as the last point of the previous patch (C0-continuity)
                y = lastDepthPatch.patchData[x][3];
            }
            else if (z == 1) {
                //The three points (previous patch last two and current patch first two) should be on a line (C1-continuity)
                float previousDifference = lastDepthPatch.patchData[x][3] - lastDepthPatch.patchData[x][2];
                y = lastDepthPatch.patchData[x][3] + previousDifference;
            }
            else {
                //No constraints apply to these points
                y = height;
            }
            patchData[x][z] = y;
        });
    }

    /**
     * Creates a patch that is constrained by the previous patch in depth, this guarantees C1-continuity of the Bezier Surface.
     * 
     * @param height The desired height for the patch
     * @param lastWidthPatch The last patch in the width direction
     * @param lastDepthPatch The last patch in the depth direction
     */
    void createWithAndDepthConstrainedPatch(final float height, final Patch lastWidthPatch, final Patch lastDepthPatch) {
        loop((x, z) -> {
            float y;
            if (x == 0) {
                //First point should be the same as the last point of the previous width patch (C0-continuity)
                y = lastWidthPatch.patchData[3][z];
            }
            else if (z == 0) {
                //First point should be the same as the last point of the previous depth patch (C0-continuity)
                y = lastDepthPatch.patchData[x][3];
            }
            else if (x == 1) {
                //TODO I think that x, z == 1 should be a seperate case
                //The three points (previous width patch last two and current width patch first two) should be on a line (C1-continuity)
                float previousDifference = lastWidthPatch.patchData[3][z] - lastWidthPatch.patchData[2][z];
                y = lastWidthPatch.patchData[3][z] + previousDifference;
            }
            else if (z == 1) {
                //The three points (previous depth patch last two and current depth patch first two) should be on a line (C1-continuity)
                float previousDifference = lastDepthPatch.patchData[x][3] - lastDepthPatch.patchData[x][2];
                y = lastDepthPatch.patchData[x][3] + previousDifference;
            }
            else {
                //No constraints apply to these points
                y = height;
            }
            patchData[x][z] = y;
        });
    }

    void submitAll(final FloatBuffer data, final int width, final int depth) {
        loop((x, z) -> {
            int datax = x + (width * 3);
            int dataz = z + (depth * 3);
            data.put(datax).put(patchData[x][z]).put(dataz);
        });
    }

    void loop(final BiConsumer<Integer, Integer> consumer) {
        for (int x = 0; x < 4; x++) {
            for (int z = 0; z < 4; z++) {
                consumer.accept(x, z);
            }
        }
    }
}

这些是最相关的,任何人都可以通过提示来帮助我吗? 另外,有没有专门设计用于处理此问题的算法?

0 个答案:

没有答案