OpenGL:使用一个VAO(LWJGL)

时间:2016-02-10 18:43:49

标签: java opengl lwjgl vbo vao

我目前正在开发一个LWJGL程序,该程序显示带有纹理和材质的.obj模型(渲染材料尚未添加,但对此问题并不重要)。加载文件和存储theire数据是没有问题的 - 最后我有一个WaveFrontObject.class(如下所示),其中包含有关我的模型的所有必需信息,包括它的单个部分/组的列表。使用glVertex3f(),glTextureCoord2f()和glNormal3f()渲染它们也很有效。但是由于一些模型变得非常大(8MB或更多),我想使用VAO和VBO使我的渲染算法更有效。

我的问题是,我想渲染一个,全部或一组自定义的modelparts / groupobjects并对它们应用自定义翻译/旋转和缩放。 为了保持清洁和简单,我想每个实体只使用一个VAO。所以我不知何故需要添加/删除我的顶点的一些部分-VPO& indicesVBO或至少以某种方式决定我的模型的哪些部分应该实际呈现。 这样做的最佳方式是什么?

这就是我到目前为止 - 抱歉,如果有一些不必要的代码部分,我尝试了很多东西,但它没有工作,所以我决定来这里问:< / p>

WaveFrontObject.class(主要OBJLoader&amp; OBJModel.class):

    package objmodels;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.util.vector.Vector2f;
import org.lwjgl.util.vector.Vector3f;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;

import Toolbox.FileUtils;
import program.RLMEditor;
import shaders.StaticShader;
import textures.ModelTexture;

public class WaveFrontObject {

    private String fileName;

    public List<Vector3f> vertices = new ArrayList<Vector3f>();
    public List<Vector3f> normals = new ArrayList<Vector3f>();
    public List<Vector2f> textureCoordinates = new ArrayList<Vector2f>();
    public List<GroupObject> groupObjects = new ArrayList<GroupObject>();

    private GroupObject currentGroupObject = null;

    public int textureID;
    public boolean loadedTexture = false;

    public File texture;

    public int numLines;

    public RLMEditor program;

    public WaveFrontObject(File objFile,RLMEditor parent, boolean transferProgress) {
        this.textureID = ModelTexture.getStandardModel().getTextureID();
        this.fileName = objFile.getPath();
        this.program = parent;
        if (isValidOBJFile(objFile)) {
            loadOBJModel(objFile, transferProgress);
        }
    }

    public WaveFrontObject(File objFile) {
        this.textureID = ModelTexture.getStandardModel().getTextureID();
        this.fileName = objFile.getPath();
        if (isValidOBJFile(objFile)) {
            loadOBJModel(objFile, false);
        }
    }

    private void loadOBJModel(File objFile, boolean transferProgress) {
        if (transferProgress)
            try {
                //program.loadingProgress = new FrameProgressBar(numLines);
                //program.loadingProgress.setVisible(true);
                numLines = FileUtils.countLines(objFile.getPath());
            } catch (IOException e1) {
                e1.printStackTrace();
            }

        try {
            FileReader fileReader;
            fileReader = new FileReader(objFile);
            BufferedReader reader = new BufferedReader(fileReader);

            String line = null;
            int lineCount = 0;
            while ((line = reader.readLine()) != null) {
                lineCount++;
                if (transferProgress) {
                       // program.loadingProgress.addStep(1);
                }
                if (line.startsWith("#") || line.isEmpty()) {
                } else if (line.startsWith("v ")) {
                    System.out.println(line);
                    Vector3f vertex = parseVertex(line.split(" "));
                    vertices.add(vertex);
                } else if (line.startsWith("vn ")) {
                    Vector3f normal = parseNormal(line.split(" "));
                    normals.add(normal);
                } else if (line.startsWith("vt ")) {
                    Vector2f texture = parseTexture(line.split(" "));
                    textureCoordinates.add(texture);
                } else if (line.startsWith("f ")) {
                    if (currentGroupObject == null) {
                        currentGroupObject = new GroupObject("default");
                    }

                    Face face = parseFace(line, lineCount);

                    if (face != null) {
                        currentGroupObject.faces.add(face);
                    }
                } else if (line.startsWith("o ") || line.startsWith("g ")) {
                    GroupObject group = parseGroupObject(line, lineCount);

                    if (group != null) {
                        if (currentGroupObject != null) {
                            groupObjects.add(currentGroupObject);
                        }
                    }

                    currentGroupObject = group;
                    currentGroupObject.validate();
                    if(canAddGroupObject(currentGroupObject)){
                        groupObjects.add(currentGroupObject);
                    }else
                    System.out.println("The GroupObject '"+currentGroupObject.name+"' was already loaded!");
                }

            }
            //Parsing textureCoords to shader

            reader.close();
        } catch (FileNotFoundException e) {
            System.err.println("[FILENAME ERROR] The following File could not be found: " + fileName + "!");
            e.printStackTrace();
        } catch (IOException e) {
            System.err.println("[IO ERROR] FileReader for file: " + fileName + " could not be created!");
            e.printStackTrace();
        }
    }

    private boolean canAddGroupObject(GroupObject currentGroupObject) {
        for(GroupObject groupObject:groupObjects){
            if(groupObject.name.equalsIgnoreCase(currentGroupObject.name)){
                return false;
            }
        }
        return true;
    }

    public void renderAll(StaticShader shader) {
        if (currentGroupObject != null) {
            //GL11.glBegin(currentGroupObject.drawingMode);
        } else {
            //GL11.glBegin(GL11.GL_TRIANGLES);
        }
        GL13.glActiveTexture(GL13.GL_TEXTURE0);
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
        renderAllObjects(shader);
        //GL11.glEnd();
    }

    private void renderAllObjects(StaticShader shader) {
        for (GroupObject object : groupObjects) {
            object.render(shader);
        }
    }

    public void renderOnly(String... groupNames) {
        for (GroupObject groupObject : groupObjects) {
            for (String groupName : groupNames) {
                if (groupName.equalsIgnoreCase(groupObject.name)) {
                    renderObject(groupObject, program.window.shader);
                    //groupObject.render(program.window.shader);
                }
            }
        }
    }

    private void renderObject(GroupObject groupObject, StaticShader shader) {
             // Bind to the VAO that has all the information about the vertices
            GL30.glBindVertexArray(groupObject.vaoID);
            GL20.glEnableVertexAttribArray(0);

            // Bind to the index VBO that has all the information about the order of the vertices
            GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, groupObject.vboiID);

            // Draw the vertices
            GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, groupObject.getNumVertices());

            // Put everything back to default (deselect)
            GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
            GL20.glDisableVertexAttribArray(0);
            GL30.glBindVertexArray(0);
    }

    public void renderPart(String partName) {
        for (GroupObject groupObject : groupObjects) {
            if (partName.equalsIgnoreCase(groupObject.name)) {
                System.out.println("Rendering " + groupObject.name);
                groupObject.render(program.window.shader);
            }
        }
    }

    private GroupObject parseGroupObject(String line, int lineCount) {
        GroupObject group = null;

        if (isValidGroupObjectLine(line)) {
            String trimmedLine = line.substring(line.indexOf(" ") + 1);

            if (trimmedLine.length() > 0) {
                group = new GroupObject(trimmedLine);
            }
        } else {
            System.err.println("[OBJ FORMAT ERROR]");
            System.out.print("Parser was unable to read line number " + lineCount + "!");
        }

        return group;
    }

    private boolean isValidGroupObjectLine(String line) {
        return line.split(" ").length == 2;
    }

    private Face parseFace(String line, int lineCount) {
        Face face = null;
        String[] faceVertices = line.split(" ");
        String[] subTokens = null;
        if (faceVertices.length == 4) {
            currentGroupObject.drawingMode = GL11.GL_TRIANGLES;
            face = new Face();
            face.vertices = new Vector3f[faceVertices.length - 1];
            face.vertexNormals = new Vector3f[faceVertices.length - 1];
            face.textureCoords = new Vector2f[faceVertices.length - 1];

            for (int i = 1; i < faceVertices.length; i++) {
                subTokens = faceVertices[i].split("/");

                int currentVertexPointer = Integer.parseInt(subTokens[0]);
                face.indices.add(currentVertexPointer);

                face.vertices[i - 1] = vertices.get(Integer.parseInt(subTokens[0]) - 1);
                face.textureCoords[i - 1] = textureCoordinates.get(Integer.parseInt(subTokens[1]) - 1);
                face.vertexNormals[i - 1] = normals.get(Integer.parseInt(subTokens[2]) - 1);
            }

            face.faceNormal = face.calculateFaceNormal();

        }

        if (faceVertices.length == 5) {
            currentGroupObject.drawingMode = GL11.GL_QUADS;
            System.err.println("[OBJ FORMAT ERROR]");
            System.out.print("The .obj parser can currently only load models with triangulated faces!");
        }

        return face;
    }

    private Vector2f parseTexture(String[] textureData) {
        Vector2f texture = new Vector2f(Float.parseFloat(textureData[1]), Float.parseFloat(textureData[2]));
        return texture;
    }

    private Vector3f parseNormal(String[] normalData) {
        Vector3f normal = new Vector3f(Float.parseFloat(normalData[1]), Float.parseFloat(normalData[2]),
                Float.parseFloat(normalData[3]));
        return normal;
    }

    private Vector3f parseVertex(String[] vertexData) {
        Vector3f vertex = new Vector3f(Float.parseFloat(vertexData[1]), Float.parseFloat(vertexData[2]),
                Float.parseFloat(vertexData[3]));
        return vertex;
    }

    private boolean isValidVertex(String[] vertexData) {
        return vertexData.length == 4;
    }

    private boolean isValidNormal(String[] normalData) {
        return normalData.length == 4;
    }

    private boolean isValidTexture(String[] textureData) {
        return textureData.length == 4;
    }

    private boolean isValidOBJFile(File objfile) {

        return objfile.getName().endsWith(".obj") || objfile.getName().endsWith(".OBJ");
    }

    public File getFileLocation() {
        return new File(fileName);
    }

    public void bindTexture(File textureFile) {
        Texture texture = null;
        try {
            texture = TextureLoader.getTexture("png", new FileInputStream(textureFile));
            this.textureID = texture.getTextureID();
            this.texture = textureFile;
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public float[] getVertexArray(){
        float[] verticesArray = new float[vertices.size()*3];
        for(int i = 0; i<vertices.size(); i++){
            verticesArray[i*3]=vertices.get(i).x;
            verticesArray[i*3+1]=vertices.get(i).y;
            verticesArray[i*3+2]=vertices.get(i).z;

        }

        return verticesArray;
    }

    public float[] getTextureArray(){
        float[] texturesArray = new float[textureCoordinates.size()*2];
        for(int i = 0; i<textureCoordinates.size(); i++){
            texturesArray[i*2]=textureCoordinates.get(i).x;
            texturesArray[i*2+1]=textureCoordinates.get(i).y;

        }
        return texturesArray;
    }

    public void cleanUp(){
        for(GroupObject groupObject:groupObjects){
            groupObject.cleanUp();
        }
    }

}

GroupObject.class - 包含来自单个modelpart的所有面,它的名称和glDrawingMode(目前这只能是GL11.GL_TRIANGLES):

    package objmodels;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.util.vector.Vector3f;

import shaders.StaticShader;

    public class GroupObject {
        public int vaoID;
        public int vboID;
        public int vboiID;

        private boolean isValid; //Used to set the vertex-&indicesArray only once 
        public String name;
        public List<Face> faces = new ArrayList<Face>();
        public int drawingMode;
        private FloatBuffer verticesBuffer;
        private IntBuffer indicesBuffer;
        private int numIndices;

        public GroupObject(String name) {
            this.name = name;
        }

        public void prepareRendering(){
            vaoID = GL30.glGenVertexArrays();
            GL30.glBindVertexArray(vaoID);

            // Create a new VBO for the indices and select it (bind)
            vboID = GL15.glGenBuffers();
            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboID);
            GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verticesBuffer, GL15.GL_STATIC_DRAW);
            // Put the VBO in the attributes list at index 0
            GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0);
            // Deselect (bind to 0) the VBO
            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

            // Deselect (bind to 0) the VAO
            GL30.glBindVertexArray(0);

            vboiID = GL15.glGenBuffers();
            GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiID);
            GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL15.GL_STATIC_DRAW);
            // Deselect (bind to 0) the VBO
            GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
        }

        public void render(StaticShader shader) {
             // Bind to the VAO that has all the information about the vertices
            GL30.glBindVertexArray(vaoID);
            GL20.glEnableVertexAttribArray(0);

            // Bind to the index VBO that has all the information about the order of the vertices
            GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiID);

            // Draw the vertices
            GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, getNumVertices());

            // Put everything back to default (deselect)
            GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
            GL20.glDisableVertexAttribArray(0);
            GL30.glBindVertexArray(0);
        }

        public void cleanUp(){
            // Disable the VBO index from the VAO attributes list
            GL20.glDisableVertexAttribArray(0);

            // Delete the vertex VBO
            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
            GL15.glDeleteBuffers(vboID);

            // Delete the index VBO
            GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
            GL15.glDeleteBuffers(vboiID);

            // Delete the VAO
            GL30.glBindVertexArray(0);
            GL30.glDeleteVertexArrays(vaoID);
        }

        public void renderFaces(StaticShader shader) {
            if (faces.size() > 0) {
                for (Face face : faces) {
                    face.addFaceForRender(shader);
                }
            }
        }

        public void validate() {
            this.isValid=true;
            this.verticesBuffer = getVertexBuffer();
            this.indicesBuffer = getIndicesBuffer();
        }

        public FloatBuffer getVertexBuffer() {
            float[] verticesArray = new float[getNumVertices()];
            for (int facePointer = 0; facePointer < faces.size(); facePointer++) {
                Face face = faces.get(facePointer);
                for (Vector3f faceVertex : face.vertices) {
                    verticesArray[facePointer] = faceVertex.x;
                    verticesArray[facePointer + 1] = faceVertex.y;
                    verticesArray[facePointer + 2] = faceVertex.z;
                }
            }

            FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(verticesArray.length);
            verticesBuffer.put(verticesArray);
            verticesBuffer.flip();

            return verticesBuffer;
        }

        public IntBuffer getIndicesBuffer() {
            List<Integer> indices = new ArrayList<Integer>();
            for (int facePointer = 0; facePointer < faces.size(); facePointer++) {
                Face face = faces.get(facePointer);
                int[] indicesArray = face.getIndicesArray();
                for (int i = 0; i < indicesArray.length; i++) {
                    indices.add(indicesArray[i]);
                    numIndices++;
                }
            }

            int[] indicesArray = new int[indices.size()];
            for (int index = 0; index < indices.size(); index++) {
                indicesArray[index] = indices.get(index);
            }

            IntBuffer indicesBuffer = BufferUtils.createIntBuffer(indicesArray.length);
            indicesBuffer.put(indicesArray);
            indicesBuffer.flip();

            return indicesBuffer;
        }

        public int getNumVertices() {
            int number = 0;
            for (Face face : faces) {
                number += face.getNumVertices();
            }
            return number;
        }

    }

Face.class - 控制有关单个Face(Vertices,Normals&amp; Texturecoords)的信息:

    package objmodels;

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

import org.lwjgl.opengl.GL11;
import org.lwjgl.util.vector.Vector2f;
import org.lwjgl.util.vector.Vector3f;

import shaders.StaticShader;

public class Face {

    public Vector3f[] vertices;
    public Vector3f[] vertexNormals;
    public Vector2f[] textureCoords;

    public Vector3f faceNormal;
    public List<Integer> indices = new ArrayList<Integer>();

    public Face() {
        this.vertices = new Vector3f[3];
        this.vertexNormals = new Vector3f[3];
        this.textureCoords = new Vector2f[3];
    }

    public Face(Vector3f[] vertices, Vector3f[] vertexNormals, Vector2f[] textureCoords) {
        this.vertices = vertices;
        this.vertexNormals = vertexNormals;
        this.textureCoords = textureCoords;
        this.faceNormal = calculateFaceNormal();
    }

    public void setVertex(int index, Vector3f vertex, Vector3f normal, Vector2f textureCoords) {
        this.vertices[index] = vertex;
        this.vertexNormals[index] = normal;
        this.textureCoords[index] = textureCoords;
    }

    public Vector3f calculateFaceNormal() {
        Vector3f v1 = new Vector3f(vertices[1].x - vertices[0].x, vertices[1].y - vertices[0].y,
                vertices[1].z - vertices[0].z);
        Vector3f v2 = new Vector3f(vertices[2].x - vertices[0].x, vertices[2].y - vertices[0].y,
                vertices[2].z - vertices[0].z);
        Vector3f normalVector = null;

        normalVector = (Vector3f) Vector3f.cross(v1, v2, normalVector).normalise();

        return new Vector3f((float) normalVector.x, (float) normalVector.y, (float) normalVector.z);
    }

    public void addFaceForRender(StaticShader shader) {
        Vector3f normal = calculateFaceNormal();
        for (int i = 0; i < vertices.length; i++) {
            {
                // int textureCoordsLocation =
                // GL20.glGetAttribLocation(shader.programID, "textureCoords");
                Vector3f vertex = vertices[i];
                Vector2f textureCoord = textureCoords[i];
                // GL20.glVertexAttrib2f(textureCoordsLocation, textureCoord.x,
                // textureCoord.y);
                GL11.glTexCoord2f(textureCoord.x, textureCoord.y);
                GL11.glVertex3d(vertex.x, vertex.y, vertex.z);
                GL11.glNormal3d(normal.x, normal.y, normal.z);
            }
        }
    }

    public int getNumVertices() {
        return vertices.length;
    }

    public int[] getIndicesArray() {
        int[] indicesArray = new int[indices.size()];
        for (int i = 0; i < indices.size(); i++) {
            indicesArray[i]=indices.get(i);
        }
        return indicesArray;
    }

}

感谢您的建议 - ItsAMysterious

1 个答案:

答案 0 :(得分:2)

不幸的是,你发布的代码太多了(~570行,哇!)所以我无法全部阅读。但是你正在解决一个非常普遍的问题,我可以给你一般的解决方案。

问题是,&#34;我想使用一个共享的VAO绘制不同的模型。&#34;这实际上比听起来容易得多。您可以简单地将所有模型连接到相同的VBO中,然后在调用glDrawElements()glDrawArrays()时,指定要使用的数组部分(使用glDrawElementsBaseVertex()而不是glDrawElements() glDrawArrays(GL_TRIANGLES, 0, 1000); // model A glDrawArrays(GL_TRIANGLES, 1000, 500); // model B glDrawArrays(GL_TRIANGLES, 1500, 750); // model C ,如有必要)。

例如,假设我们有模型A,B和C.假设模型A有1000个顶点,模型B有500个,模型C有750个。

首先,将所有顶点数据连接成一个VBO,并将一个VAO用于所有内容。现在你可以打电话:

glDrawElements()

如果您使用glDrawElements(),则有两种选择。

  • 您可以将范围传递给glDrawElementsBaseVertex(),并将索引数组指向合并的VAO,或

  • 您可以将范围传递给glBindVertexBuffer(),并使索引数组相对于每个模型中的第一个顶点。

无论哪种方式,减少状态变化次数的常用技术都是常见的。另一种方法是使用// Our class variables private int attempts = 0, limit = 3; // A method to call our recursive method for the first time. void BeginProcessing() { ProcessData(); } // Our recursive method containing our read logic. void ProcessData() { try { /* Insert read logic here. */ } catch(Exception) { attempts++; if (attempts<=limit) { ProcessData(); } else { System.out.println("Attempted to process " + limit + " times."); } } } ,每次都在VBO中指定不同的偏移量,但这只是在4.3版的核心中,它比你需要的更灵活。