我目前正在开发一个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
答案 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版的核心中,它比你需要的更灵活。