Java 使用三次bezier样条曲线渲染平滑地形?

Java 使用三次bezier样条曲线渲染平滑地形?,java,algorithm,graphics,bezier,spline,Java,Algorithm,Graphics,Bezier,Spline,我正在尝试用贝塞尔补丁制作一幅山地景观 我这样做是为了确保C1的连续性,但它就是不起作用 作为函数的输入,虽然它是在计算之前生成的,但它是一个由nxn网格高度组成的高度图。 然后在我的方法中,我想为每个网格生成16个立方体贝塞尔面片控制桥 但是,我不能这样做,因为这会导致奇怪的结果,我的代码: new PatchIterator(width).forEachRemaining(p -> { int w = p.first, d = p.second;

我正在尝试用贝塞尔补丁制作一幅山地景观

我这样做是为了确保C1的连续性,但它就是不起作用

作为函数的输入,虽然它是在计算之前生成的,但它是一个由nxn网格高度组成的高度图。
然后在我的方法中,我想为每个网格生成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);
            }
        }
    }
}
新的PatchIterator(宽度)。ForEachLeving(p->{
int w=p.first,d=p.second;
补丁=新补丁();
布尔值lastWidthPatchExists=(w-1>=0&&patchs[w-1][d]!=null);
布尔值lastDepthPatchExists=(d-1>=0&&patches[w][d-1]!=null);
如果(!lastWidthPatchExists&!lastDepthPatchExists){
createFirstPatch(高度映射[w][d]);
}
else if(lastWidthPatchExists&!lastDepthPatchExists){
createWidthConstrainedPatch(高度映射[w][d],面片[w-1][d]);
}
如果(!lastWidthPatchExists&&lastDepthPatchExists)存在,则为else{
createDepthConstrainedPatch(高度映射[w][d],面片[w][d-1]);
}
否则{
createWithAndDepthConstrainedPatch(高度映射[w][d]、面片[w-1][d]、面片[w][d-1]);
}
亚高大斑块(水螅目,w,d);
贴片[w][d]=贴片;
});
私有类补丁{
最终浮点[][]patchData=新浮点[4][4];
/**
*创建第一个面片,该面片不受任何其他约束。
* 
*@param height修补程序所需的高度
*/
void createFirstPatch(最终浮动高度){
循环((x,z)->{
patchData[x][z]=高度;
});
}
/**
*创建受上一个面片宽度约束的面片,这保证了Bezier曲面的C1连续性。
* 
*@param height修补程序所需的高度
*@param lastWidthPatch宽度方向上的最后一个面片
*/
void createWidthConstrainedPatch(最终浮动高度,最终面片lastWidthPatch){
循环((x,z)->{
浮动y;
如果(x==0){
//第一个点应与上一个贴片的最后一个点相同(C0连续性)
y=lastWidthPatch.patchData[3][z];
}
else如果(x==1){
//三个点(前一个补片最后两个和当前补片前两个)应在一条线上(C1连续性)
float-previousDifference=lastWidthPatch.patchData[3][z]-lastWidthPatch.patchData[2][z];
y=lastWidthPatch.patchData[3][z]+上一个差值;
}
否则{
//这些点不受任何约束
y=高度;
}
patchData[x][z]=y;
});
}
/**
*创建受上一个面片深度约束的面片,这保证了Bezier曲面的C1连续性。
* 
*@param height修补程序所需的高度
*@param lastDepthPatch深度方向上的最后一个补丁
*/
void createDepthConstrainedPatch(最终浮动高度,最终面片lastDepthPatch){
浮动[]Y值=新浮动[4];
数组。填充(y值,0f);
循环((x,z)->{
浮动y;
如果(z==0){
//第一个点应与上一个贴片的最后一个点相同(C0连续性)
y=lastDepthPatch.patchData[x][3];
}
else如果(z==1){
//三个点(前一个补片最后两个和当前补片前两个)应在一条线上(C1连续性)
float-previousDifference=lastDepthPatch.patchData[x][3]-lastDepthPatch.patchData[x][2];
y=lastDepthPatch.patchData[x][3]+上一个差值;
}
否则{
//这些点不受任何约束
y=高度;
}
patchData[x][z]=y;
});
}
/**
*创建受上一个面片深度约束的面片,这保证了Bezier曲面的C1连续性。
* 
*@param height修补程序所需的高度
*@param lastWidthPatch宽度方向上的最后一个面片
*@param lastDepthPatch深度方向上的最后一个补丁
*/
void createWithAndDepthConstrainedPatch(最终浮动高度、最终面片lastWidthPatch、最终面片lastDepthPatch){
循环((x,z)->{
浮动y;
如果(x==0){
//第一个点应与上一个宽度面片的最后一个点相同(C0连续性)
y=lastWidthPatch.patchData[3][z];
}
else如果(z==0){
//第一个点应与上一个深度面片的最后一个点相同(C0连续性)
y=lastDepthPatch.patchData[x][3];
}
else如果(x==1){
//我认为x,z==1应该是一个单独的例子
//三个点(前一个宽度补片最后两个和当前宽度补片前两个)应在一条线上(C1连续性)
float-previousDifference=lastWidthPatch.patchData[3][z]-lastWidthPatch.patchData[2][z];
y=lastWidthPatch.patchData[3][z]+上一个差值;
}
else如果(z==1){
//三个点(前一个深度片最后两个和当前深度片前两个)应在一条线上(C1连续性)
float-previousDifference=lastDepthPatch.patchData[x][3]-lastDepthPatch.patchData[x][2];
y=lastDepthPatch.patchData[x][3]+上一个差值;
}
否则{
//这些点不受任何约束