0

我最近一直在从事一个项目,该项目创建程序生成的地形,该地形使用 3d 单纯形噪声以及行进立方体算法。我目前在我的 cpu 上运行它,渲染一个 200x200x200 的地形大约需要 10-20 秒,如果我想让世界变得无限,这不是最佳的。有什么方法可以提高地形渲染的速度,或者这是我能达到的最大速度。(我已经尝试过使用计算着色器,但 GLSL 的限制不允许我追求这个想法。)

地形生成代码

package Terrain;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import Entities.Entity;
import Maths.Vector3f;
import Models.RawModel;
import Render.Loader;


public class Terrain {
    private int width = 200, height = 2, interval = 8;
    
    private float x,y,z = 2f;
    private int x1,y1,z1;
    
    private Loader loader;
    
    private List<Entity> cubes = new ArrayList<Entity>();
    

    private RawModel model;

    private double perlin2DScale = 0.01, perlin3DScale = 0.01;//, maskScale = 0.00;
    
    private double perlin3Dratio = 0.8;// ratio of 3d noise to 2d;
    
    private double amp = 1; //height of mountains
    
    List<Vector3f> verticeArray = new ArrayList<Vector3f>();
    float[][][] terrainMap = new float[width+1][height+1][width+1];
    float[] SimplexMap3D = new float[(width+1)*(height+1)*(width+1)];
    float[] SimplexMap2D = new float[(width+1)*(width+1)];

    private float surfaceLevel = 1f;
        
    int seed;
    
    //SimplexComputeBuffer compute = new SimplexComputeBuffer();
    public Terrain(float x, float z, Loader loader){
        this.loader = loader; 
        this.x1 = (int) (x*width);
        this.z1 = (int) (z*width);
        this.x = x*width*interval;
        this.z = z*width*interval;
        Random rand = new Random();
        seed = rand.nextInt(100000);
        
        loadMarchingTerrain();
    }
    public void changeAmp(double i){
        x+=i;
        System.out.println("amp ="+amp);
        verticeArray.clear();
        loadMarchingTerrain();
    }
    public void changeSurface(double i){
        surfaceLevel+=i;
        System.out.println("surface ="+surfaceLevel);
        verticeArray.clear();
        loadMarchingTerrain();
    }
    public void loadMarchingTerrain(){
        for(int x = x1; x<x1+width+1; x++){
            for(int y = y1; y<y1+height+1; y++){
                for(int z = z1; z<z1+width+1; z++){ 
                    double noise3d = this.sumOctaves(4,x,y,z,0.5,perlin3DScale,0,1); // creates 3d terrain like caves and overhangs
                    double noise2d = this.sumOctaves(4,x,z,0.5,perlin2DScale,0,1); // creates 2d terrain like mountains and hills (gives only height)
                    //double mask =  this.sumOctaves(1,x,z,0.5,maskScale,0,1); // creates a 2d mask to vary heights of regions
                    float curHeight = (float)height*(float)(noise2d*(1-perlin3Dratio)+noise3d*perlin3Dratio); // mixing them together with correct ratio of 3d and 2d data
                    terrainMap[x-x1][y-y1][z-z1] = (float)-y+curHeight;
                }
            }
        }
        for(int x = 0; x<width; x++){
            for(int y = 0; y<height; y++){
                for(int z = 0; z<width; z++){
                    marchCube(new Vector3f(x,y,z)); 
                }
            }
        }
        float[] vertices = new float[verticeArray.size()*3];
        int[] indice = new int[verticeArray.size()];
        int vertexCount = 0;
        for(Vector3f v : verticeArray){
            vertices[vertexCount++] =v.x*interval;
            vertices[vertexCount++] =v.y*interval;
            vertices[vertexCount++] =v.z*interval;
        }
        for(int i = 0; i<indice.length; i++){
            indice[i] = i;
        }
        model = loader.loadToVao(vertices, null, indice);
    }
    
    public int configIndex(float[] cube){
        int configIndex = 0;
        for(int i = 0; i<8; i++){
            if(cube[i] > surfaceLevel){
                configIndex |= 1 << i;
            }
        }       
        return configIndex;
    }
    
    public float sampleTerrain(Vector3f point){
        return terrainMap[(int) point.x][(int) point.y][(int) point.z];
    }
    
    public Vector3f smoothPoint(Vector3f vert1, Vector3f vert2, int indice, float[] cube){
        float sampleVert1 = cube[MarchingCubeTable.edges[indice][0]];
        float sampleVert2 = cube[MarchingCubeTable.edges[indice][1]];
        
        float difference = sampleVert1-sampleVert2;
        
        if(difference == 0){
            difference = surfaceLevel;
        }else{
            difference = (surfaceLevel-sampleVert1)/difference;
        }
        Vector3f a2 = vert1.subtract(vert2).scale(difference);
        
        Vector3f vertPos = vert1.add(a2);

        return vertPos;
    }
    public void marchCube(Vector3f position){
        //create cube
        float[] cube = new float[8];
        for(int i = 0; i<8; i++){       
            Vector3f corner = position.add(MarchingCubeTable.cornerTable[i]);       
            cube[i] = terrainMap[(int) corner.x][(int) corner.y][(int) corner.z];       
        }
        //search through index
        int currentConfigIndex = configIndex(cube);     
        if(currentConfigIndex == 0 || currentConfigIndex == 255){
            return;
        }       
        //search through points
        int edgeIndex = 0;
        for(int j = 0; j<5; j++){
            for(int i = 0; i<3; i++){
                int indice = MarchingCubeTable.getIndex(currentConfigIndex)[edgeIndex];         
                if(indice == -1){
                    return;
                }
                Vector3f vert2 = position.add(MarchingCubeTable.cornerTable[MarchingCubeTable.edges[indice][0]]);
                Vector3f vert1 = position.add(MarchingCubeTable.cornerTable[MarchingCubeTable.edges[indice][1]]);           
                Vector3f vertPos = this.smoothPoint(vert1, vert2, indice, cube);                
                verticeArray.add(vertPos);
                edgeIndex++;
            }
        }
    }
    
    /*
     * Simplex Noise functions
     */
    public double sumOctaves(int iterations, double x, double y, double z, double persistance, double scale, double low, double high){
        double maxamp = 0;
        double amp = this.amp;
        double frequency = scale;
        double noise = 0;
        
        for(int i = 0; i<iterations; i++){
            noise += SimplexNoise.noise((x)*frequency, (y)*frequency, (z)*frequency)*amp;
            maxamp += amp;
            amp *= persistance;
            frequency *= 2;
        }
        
        noise /= maxamp;
        
        noise = noise * (high - low) / 2 + (high + low) / 2;
        return noise;
    }
    
    public double sumOctaves(int iterations, int x, int y, double persistance, double scale, double low, double high){
        double maxamp = 0;
        double amp = this.amp;
        double frequency = scale;
        double noise = 0;
        
        for(int i = 0; i<iterations; i++){
            noise += SimplexNoise.noise((x)*frequency, (y)*frequency)*amp;
            maxamp += amp;
            amp *= persistance;
            frequency *= 2;
        }
        
        noise /= maxamp;
        
        noise = noise * (high - low) / 2 + (high + low) / 2;
        return noise;
    }
    
    public List<Entity> getCubes(){
        return cubes;
    }
    public float getX() {
        return x;
    }

    public float getZ() {
        return z;
    }
    
    public float getY() {
        return y;
    }
    public RawModel getRawModel(){
        return model;
    }
}
4

0 回答 0