我最近一直在从事一个项目,该项目使用3d单工噪声以及Marching cubes算法来创建程序生成的地形。我目前正在我的CPU上运行它,大约需要10到20秒来渲染200x200x200的地形,如果我想使世界无限,那不是最佳选择。有什么方法可以提高地形渲染的速度,或者这是我可以达到的最大速度。 (我已经尝试过使用计算着色器,但是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;
}
}