java - 如何更改vbo的特定部分?

标签 java opengl lwjgl vbo vao

我最近创建了 2D 高度图网格,它为我的世界生成 3D 地形网格,能够在运行时通过鼠标单击事件添加山丘/凹凸。我的问题是,每次我添加顶点的高度时,我都会更新整个地形的法线和位置 vbos(非常低效)。有什么办法 改变vbo的特定部分? 我听说 glBufferSubData 就是这样,但是我怎样才能只改变 Y 值呢? (vbo 是 x,y,z,x,y,z...) 并获取更改后的顶点以便 glBufferSubData

地形类别:

public class Terrain {
    public static final int SIZE =  500;
    //VAO, vertexCount, VBOS
    private RawModel model;
    //textures for the terrain
    private terrainTexturePack texturePack;
    Loader loader;
    private static int VERTEX_COUNT =128;


    float[] Vertices;
    float[] Normals;
    float[] TextureCoords;
    int[] Indices;
    private float[][] heights;

    public Terrain(Loader loader, terrainTexturePack texturePack) {
        this.texturePack = texturePack;
        this.loader = loader;
        this.model = generateTerrain(loader);
    }


    public RawModel getModel() {
        return model;
    }

    public terrainTexturePack getTexturePack() {
        return texturePack;
    }

    //player collision detection witn the terrain
    public Vector3f getXYZOfTerrain(float worldX, float worldZ) {
        float gridSquareSize = SIZE / ((float) heights.length - 1);
        int gridX = (int) Math.floor(worldX / gridSquareSize);
        int gridZ = (int) Math.floor(worldZ / gridSquareSize);
        if(gridX >= heights.length - 1 || gridZ >= heights.length - 1 || gridX < 0 || gridZ < 0) {
            return null;
        }

        float xCoord = (worldX % gridSquareSize)/gridSquareSize;
        float zCoord = (worldZ % gridSquareSize)/gridSquareSize;
        float yCoord;

        if (xCoord <= (1-zCoord)) {
            yCoord = Maths.barryCentric(new Vector3f(0, heights[gridX][gridZ], 0), new Vector3f(1,
                            heights[gridX + 1][gridZ], 0), new Vector3f(0,
                            heights[gridX][gridZ + 1], 1), new Vector2f(xCoord, zCoord));
        } else {
            yCoord = Maths.barryCentric(new Vector3f(1, heights[gridX + 1][gridZ], 0), new Vector3f(1,
                            heights[gridX + 1][gridZ + 1], 1), new Vector3f(0,
                            heights[gridX][gridZ + 1], 1), new Vector2f(xCoord, zCoord));
        }
        return new Vector3f(gridX, yCoord, gridZ);
    }

    //GENERATE THE TERRAIN
    private RawModel generateTerrain(Loader loader) {
        int pointer = 0;
        int count = VERTEX_COUNT * VERTEX_COUNT;
        heights = new float[VERTEX_COUNT][VERTEX_COUNT];
        float[] vertices = new float[count * 3];
        float[] normals = new float[count * 3];
        float[] textureCoords = new float[count * 2];
        int[] indices = new int[6 * (VERTEX_COUNT - 1) * (VERTEX_COUNT * 1)];
        int vertexPointer = 0;

        for (int i = 0; i < VERTEX_COUNT; i++) {
            for (int j = 0; j < VERTEX_COUNT; j++) {
                vertices[vertexPointer * 3] = (float) j / ((float) VERTEX_COUNT - 1) * SIZE;
               float height = 0f;
               vertices[vertexPointer * 3 + 1] = height;
                heights[j][i] = height;
                vertices[vertexPointer * 3 + 2] = (float) i / ((float) VERTEX_COUNT - 1) * SIZE;
                Vector3f normal =new Vector3f(0, 1, 0);// calculateNormal(j, i, noise);
                normals[vertexPointer * 3] = normal.x;
                normals[vertexPointer * 3 + 1] = normal.y;
                normals[vertexPointer * 3 + 2] = normal.z;
                textureCoords[vertexPointer * 2] = (float) j / ((float) VERTEX_COUNT - 1);
                textureCoords[vertexPointer * 2 + 1] = (float) i / ((float) VERTEX_COUNT - 1);
                vertexPointer++;
                if(i < VERTEX_COUNT - 1 && j < VERTEX_COUNT - 1){
                    int topLeft = (i * VERTEX_COUNT) + j;
                    int topRight = topLeft + 1;
                    int bottomLeft = ((i + 1) * VERTEX_COUNT) + j;
                    int bottomRight = bottomLeft + 1;
                    indices[pointer++] = topLeft;
                    indices[pointer++] = bottomLeft;
                    indices[pointer++] = topRight;
                    indices[pointer++] = topRight;
                    indices[pointer++] = bottomLeft;
                    indices[pointer++] = bottomRight;
                }
            }
        }
        Vertices = vertices;
        TextureCoords = textureCoords;
        Normals = normals;
        Indices = indices;

        return loader.loadToVAO(vertices, textureCoords, normals, indices);
    }

    //Calculate normal  
    private Vector3f calculateNormal(int x, int z) {
        float heightL = Vertices[(((    (z)   *VERTEX_COUNT)+  (x-1)    )*3)+1];
        float heightR = Vertices[(((    (z)   *VERTEX_COUNT)+  (x+1)    )*3)+1];
        float heightD = Vertices[(((    (z-1)   *VERTEX_COUNT)+  (x)    )*3)+1];
        float heightU = Vertices[(((    (z+1)   *VERTEX_COUNT)+  (x)    )*3)+1];
        Vector3f normal = new Vector3f(heightL - heightR, 2f, heightD - heightU);
        normal.normalise();
        return normal;
    }
    //create mountain where the mouse clicked
    //Vertices[(((y*VERTEX_COUNT)+x)*3)+1] = one Vertex in 2d grid
    public void createHill(int x0, int y0){
       float h = 0.06f;
       int xs=VERTEX_COUNT; 
       int ys=VERTEX_COUNT;
       float maxHeight =Vertices[(((y0*xs)+x0)*3)+1]+h;
       float r = (9*maxHeight)/30;

       //Loop the vertices
       for(int y=(int) (y0-r);y<=y0+r;y++)
        for(int x=(int) (x0-r);x<=x0+r;x++){
            double circule = Math.sqrt((x-x0)*(x-x0)+(y0-y)*(y0-y));
            if (circule <= r)            
                if ((x>=1)&&(x<xs-1))    
                    if ((y>=1)&&(y<ys-1)){
                        Vertices[(((y*xs)+x)*3)+1]  = Maths.hillsHeight(x0, x, y0, y,(maxHeight), r);
                        Vector3f normal = calculateNormal(x,y);
                        Normals[((((y*xs)+x))) * 3] = normal.x;
                        Normals[((((y*xs)+x))) * 3 + 1] = normal.y;
                        Normals[((((y*xs)+x))) * 3 + 2] = normal.z;

                    }
        }

        //change the whole VBO's not effective
        //Note: i know that i dont need to update textures and indices 
        this.model=loader.loadToVAO(Vertices, TextureCoords, Normals, Indices);

      }   

}

原始模型类(vbo 和 vao 持有者):

//Store the VAOS and VBOS
public class RawModel {

    private int vaoID;
    private int vertexCount;
    private int positionVbo;
    private int normalVbo;
    private int textureVbo;

        public RawModel(int vaoID, int vertexCount, int positionVbo, int normalVbo, int textureVbo) {        

        this.vaoID = vaoID;
        this.vertexCount = vertexCount;
        this.positionVbo = positionVbo;
        this.normalVbo = normalVbo;
        this.textureVbo = textureVbo;
    }

    public RawModel(int vaoID, int vertexCount) {
        this.vaoID = vaoID;
        this.vertexCount = vertexCount;
    }

    public int getVaoID() {
        return vaoID;
    }

    public int getVertexCount() {
        return vertexCount;
    }

    public int getPositionVbo() {
        return positionVbo;
    }

    public int getTextureVbo() {
        return textureVbo;
    }


    public int getNormalVbo() {
        return normalVbo;
      }

}

加载器类:

public class Loader {
    //For clean up
    private List<Integer> vaos = new ArrayList<Integer>();
    private List<Integer> vbos = new ArrayList<Integer>();
    private List<Integer> textures = new ArrayList<Integer>();

    //Load mesh into VAO
    public RawModel loadToVAO(float[] positions,float[] textureCoords,float[] normals,int[] indices){
        int vaoID = createVAO();
        bindIndicesBuffer(indices);
        int positionvbo = storeDataInAttributeList(0,3,positions);
        int textureVbo = storeDataInAttributeList(1,2,textureCoords);
        int normalsnvbo = storeDataInAttributeList(2,3,normals);
        unbindVAO();
        return new RawModel(vaoID,indices.length, positionvbo, textureVbo, normalsnvbo);
    }

    //Load texture
    public int loadTexture(String fileName) {
        Texture texture = null;
        try {
            texture = TextureLoader.getTexture("PNG",
                    new FileInputStream("res/textures/" + fileName + ".png"));
              GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D);
                GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
                        GL11.GL_LINEAR_MIPMAP_LINEAR);
                GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, -2);
              if(GLContext.getCapabilities().GL_EXT_texture_filter_anisotropic){
                 float amount = Math.min(4f, 
                         GL11.glGetFloat(EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT));
                 GL11.glTexParameterf(GL11.GL_TEXTURE_2D, 
                         EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, amount);    
              }
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Tried to load texture " + fileName + ".png , didn't work");
            System.exit(-1);
        }
        textures.add(texture.getTextureID());
        return texture.getTextureID();
    }

    //Clean up
    public void cleanUp(){
        for(int vao:vaos){
            GL30.glDeleteVertexArrays(vao);
        }
        for(int vbo:vbos){
            GL15.glDeleteBuffers(vbo);
        }
        for(int texture:textures){
            GL11.glDeleteTextures(texture);
        }
    }

    //Creates vao
    private int createVAO(){
        int vaoID = GL30.glGenVertexArrays();
        vaos.add(vaoID);
        GL30.glBindVertexArray(vaoID);
        return vaoID;
    }
    //Store data in vbo
    private int storeDataInAttributeList(int attributeNumber, int coordinateSize,float[] data){
        int vboID = GL15.glGenBuffers();
        vbos.add(vboID);
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboID);
        FloatBuffer buffer = storeDataInFloatBuffer(data);
        GL15.glBufferData(GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
        GL20.glVertexAttribPointer(attributeNumber,coordinateSize,GL11.GL_FLOAT,false,0,0);
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);


        return vboID;
    }

    private void unbindVAO(){
        GL30.glBindVertexArray(0);
    }


    //Bind indices buffer
    private void bindIndicesBuffer(int[] indices){
        int vboID = GL15.glGenBuffers();
        vbos.add(vboID);
        GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboID);
        IntBuffer buffer = storeDataInIntBuffer(indices);
        GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
    }

    //Store in int buffer
    private IntBuffer storeDataInIntBuffer(int[] data){
        IntBuffer buffer = BufferUtils.createIntBuffer(data.length);
        buffer.put(data);
        buffer.flip();
        return buffer;
    }

    //Store in float buffer
    private FloatBuffer storeDataInFloatBuffer(float[] data){
        FloatBuffer buffer = BufferUtils.createFloatBuffer(data.length);
        buffer.put(data);
        buffer.flip();
        return buffer;
    }

    //Load skyBox textures
    public int loadCubeMap(String[] textureFiles){
        int texID = GL11.glGenTextures();
        GL13.glActiveTexture(GL13.GL_TEXTURE0);
        GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, texID);
        for(int i = 0; i < textureFiles.length; i++){
            TextureData data = decodeTextureFile("res/textures/"+ textureFiles[i] + ".png");
            GL11.glTexImage2D(GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, GL11.GL_RGBA, data.getWidth(), data.getHeight(), 0, 
                    GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, data.getBuffer());
        }

        GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
        GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);



        textures.add(texID);
        GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
        GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
        return texID;
    }
    private TextureData decodeTextureFile(String fileName) {
        int width = 0;
        int height = 0;
        ByteBuffer buffer = null;
        try {
            FileInputStream in = new FileInputStream(fileName);
            PNGDecoder decoder = new PNGDecoder(in);
            width = decoder.getWidth();
            height = decoder.getHeight();
            buffer = ByteBuffer.allocateDirect(4 * width * height);
            decoder.decode(buffer, width * 4, Format.RGBA);
            buffer.flip();
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Tried to load texture " + fileName + ", didn't work");
            System.exit(-1);
        }
        return new TextureData(buffer, width, height);
    }

      //Load textures for GUI
      public RawModel loadToVAO(float[] positions, int dimensions) {
            int vaoID = createVAO();
            this.storeDataInAttributeList(0, dimensions, positions);
            unbindVAO();
            return new RawModel(vaoID, positions.length / dimensions);
      }

}

感谢 Reto Koradi 解决

public void changeVbo(int position, float[] data, int VboId){
    GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, VboId);

    FloatBuffer ArrayData = storeDataInFloatBuffer(data);
    GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER,position * 4, ArrayData);

    GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

}

最佳答案

最简单且可能最有效的方法是将高度(y 值)存储在单独的 VBO 中,并将它们指定为单独的顶点属性。

然后,在顶点着色器代码中,您可以简单地从单独的属性重新组合位置。您现在的着色器代码中可能有类似这样的内容:

in vec3 pos;

这将更改为:

in vec3 posXZ;
in float posY;
...
vec3 pos = vec3(posXZ.x, posY, posXZ.y);

对经常更改的数据使用单独的 VBO 还允许您相应地指定分配标志。对于经常变化的数据,您可以使用GL_DYNAMIC_DRAW,对于其余数据,可以使用GL_STATIC_DRAW

另一种选择是使用glMapBuffer()。这为您提供了指向缓冲区内容的 CPU 指针,允许您仅修改实际想要更改的数据。但是,您必须小心,不要在 CPU 和 GPU 之间引入不需要的同步。 glMapBuffer() 调用可能会阻塞,直到 GPU 使用缓冲区的先前内容完成所有渲染调用。一种常见的技术是在一组缓冲区中使用数据的多个副本,并循环遍历它们,以最大限度地减少同步。但如果数据量很大,显然会导致内存占用急剧增加。

在您的用例中,我怀疑您还必须更新法线,因为它们取决于高度值。

关于java - 如何更改vbo的特定部分?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38283904/

相关文章:

c++ - 在 OpenGL 中用极坐标绘制正方形

python - OpenGL utf-8 字符串渲染

java - 没有 gl_LightSource[0] 的照明

java相当于unsigned long long不就是BigInteger吗?

java - PHP:将值添加到 HTTP header 中的现有键中

适用于 Google 钱包的 Java Eclipse Google App Engine 教程

c++ - openGL 渲染在 WS_OVERLAPPEDWINDOW 中工作,但在 WS_POPUP 中不工作

java - 当程序意外退出时 VRAM 会发生什么

java - 使用 LWJGL 和 VBO 的简单纹理不起作用

java - 未找到请求的操作的编解码器 : [duration <-> java. time.Duration]