OpenGL (LWJGL 3) 剔除不在视锥体中的地形 vertices/triangles

OpenGL (LWJGL 3) culling terrain vertices/triangles that are not in the view frustum

我目前正在尝试在我的 3D 游戏中实施视锥体剔除,并且它可以有效地处理实体,因为它们有一个边界框 (AABB) 并且更容易根据视锥体检查一个框。话虽如此,我将如何剔除地形? (它在物理上不能有 AABB 或球体)

平截头体class(我使用内置的 JOML):

import org.joml.FrustumIntersection;
import org.joml.Matrix4f;

import engine.Terrians.Terrain;
import engine.maths.Matrices;
import engine.maths.Vector3f;
import engine.objects.Camera;
import engine.physics.AABB;

public class FrustumG {

    private final Matrix4f projectionViewMatrix;

    private FrustumIntersection frustumInt;

    public FrustumG() {
        projectionViewMatrix = new Matrix4f().identity();
        frustumInt = new FrustumIntersection();
    }
    
    public void update(Matrix4f projectionMatrix, Matrix4f viewMatrix) {
        projectionViewMatrix.set(projectionMatrix);
        projectionViewMatrix.mul(viewMatrix);
        
        frustumInt.set(projectionViewMatrix);
    }
    
    public boolean intersectsAABB(AABB aabb) {
        return frustumInt.testAab(aabb.getWorldMinX(), aabb.getWorldMinY(), aabb.getWorldMinZ(), 
                                  aabb.getWorldMaxX(), aabb.getWorldMaxY(), aabb.getWorldMaxZ());
    }
    
    public boolean intersectsPoint(Vector3f point) {
        return frustumInt.testPoint(point.getX(), point.getY(), point.getZ());
    }
    
}

My Mesh Class 存储地形的顶点信息。我不想每一帧都编辑和更新顶点 VBO,因为我用谷歌搜索它会影响游戏的性能(而且我还必须编辑索引列表,并在每一帧循环遍历这两个列表)。看到有些网站说用GL_DYNAMIC_DRAW代替GL_STATIC_DRAW,没看懂

这是网格 Class:

import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.system.MemoryUtil;

public class Mesh {
    private float[] verticesData;
    private int[] indicesData;
    private float[] textureData;
    private float[] normalsData;
    private int vao, pbo, ibo, cbo, tbo, nbo;
    private Texture texture;
    
    public Mesh(float[] verticesArray, int[] indices, float[] normalsArray, float[] texturesArray) {
        this.verticesData = verticesArray;
        this.indicesData = indices;
        this.textureData = texturesArray;
        this.normalsData = normalsArray;
    }
    
    public Mesh(float[] positions, int dimensions) {
        this.verticesData = positions;
        vao = GL30.glGenVertexArrays();
        GL30.glBindVertexArray(vao);
         
        FloatBuffer positionBuffer = MemoryUtil.memAllocFloat(positions.length);
        positionBuffer.put(positions).flip();
        
        pbo = storeData(positionBuffer, 0, dimensions);
    }
    
    public void createTextures(String filepath) {
        Texture texture = new Texture(filepath);
        texture.create();
        this.texture = texture;
    }
    
    public int loadCubeMap(String[] textureFiles, String textureMainSystemPath) {
        int texID = GL11.glGenTextures();
        GL30.glActiveTexture(GL30.GL_TEXTURE0);
        GL30.glBindTexture(GL30.GL_TEXTURE_CUBE_MAP, texID);
        
        for(int i = 0; i < textureFiles.length; i++) {
            Texture.createSkybox(textureMainSystemPath + textureFiles[i] + ".png", i);
        }
        
        GL30.glTexParameteri(GL30.GL_TEXTURE_CUBE_MAP, GL30.GL_TEXTURE_MAG_FILTER, GL30.GL_LINEAR);
        GL30.glTexParameteri(GL30.GL_TEXTURE_CUBE_MAP, GL30.GL_TEXTURE_MIN_FILTER, GL30.GL_LINEAR);
        
        return texID;
    }
    
    public void createMeshes() {    
        vao = GL30.glGenVertexArrays();
        GL30.glBindVertexArray(vao);
        
        FloatBuffer positionBuffer = MemoryUtil.memAllocFloat(verticesData.length);
        positionBuffer.put(verticesData).flip();
        
        pbo = storeData(positionBuffer, 0, 3);
        
        /**FloatBuffer colorBuffer = MemoryUtil.memAllocFloat(vertices.length * 3);
        float[] colorData = new float[vertices.length * 3];
        for (int i = 0; i < vertices.length; i++) {
            colorData[i * 3] = vertices[i].getColor().getX();
            colorData[i * 3 + 1] = vertices[i].getColor().getY();
            colorData[i * 3 + 2] = vertices[i].getColor().getZ();
        }
        colorBuffer.put(colorData).flip();
        
        cbo = storeData(colorBuffer, 1, 3);**/
        
        FloatBuffer textureBuffer = MemoryUtil.memAllocFloat(verticesData.length);
        textureBuffer.put(textureData).flip();
        tbo = storeData(textureBuffer, 1, 2);
        
        FloatBuffer normalBuffer = MemoryUtil.memAllocFloat(verticesData.length);
        normalBuffer.put(normalsData).flip();
        nbo = storeData(normalBuffer, 2, 3);
        
        IntBuffer indicesBuffer = MemoryUtil.memAllocInt(indicesData.length);
        indicesBuffer.put(indicesData).flip();
        
        ibo = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ibo);
        GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL15.GL_STATIC_DRAW);
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
    }
    
    private int storeData(FloatBuffer buffer, int index, int size) {
        int bufferID = GL15.glGenBuffers();
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferID);
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
        GL20.glVertexAttribPointer(index, size, GL11.GL_FLOAT, false, 0, 0);
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
        return bufferID;
    }
    
    public void destroyBuffers() {
        GL15.glDeleteBuffers(pbo);
        GL15.glDeleteBuffers(cbo);
        GL15.glDeleteBuffers(ibo);
        GL15.glDeleteBuffers(tbo);
        GL15.glDeleteBuffers(nbo);
        
        GL30.glDeleteVertexArrays(vao);
    }

    public float[] getVertices() {
        return verticesData;
    }
    
    public float[] getPositions2D() {
        return verticesData;
    }

    public int[] getIndices() {
        return indicesData;
    }

    public int getVAO() {
        return vao;
    }

    public int getPBO() {
        return pbo;
    }
    
    public int getCBO() {
        return cbo;
    }

    public int getIBO() {
        return ibo;
    }
    
    public int getTBO() {
        return tbo;
    }
    
    public int getNBO() {
        return nbo;
    }

    public Texture getTexture() {
        return texture;
    }
}

是否有更有效的方法来剔除地形的顶点/三角形?

确定应剔除地形的哪一部分的一种方法是使用四叉树(对于高度图)或八叉树(对于体素图)。基本上,您将地形分成小块,然后相应地进一步划分。然后,您可以测试这些块是否在您的视锥体中,并在必要时剔除它们。已经对这项技术进行了非常详细的讨论:

I saw some websites saying to use GL_DYNAMIC_DRAW instead of GL_STATIC_DRAW, but I did not understand it.

这些是 OpenGL 关于如何访问数据的使用提示,因此实现能够对如何 store/use 应用某些优化。

usage is a hint to the GL implementation as to how a buffer object's data store will be accessed. This enables the GL implementation to make more intelligent decisions that may significantly impact buffer object performance. (https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBufferData.xhtml)

请注意,这些只是指示,没有限制:

It does not, however, constrain the actual usage of the data store.

因为您可能会不断更新您的 VBO 和 IBO(参见剔除)并且只想绘制它们 GL_DYNAMIC_DRAW 是一个不错的选择:

The data store contents will be modified repeatedly (because of culling) and used many times. The data store contents are modified by the application and used as the source for GL drawing and image specification commands.

as I have googled that it can affect the performance of the game

嗯,剔除地形会消耗一些性能,但最终可能会提高性能,因为可以丢弃许多顶点(三角形)。这种性能增益可能会随着地形的扩大而增加。