Wavefront .obj - richtig rendern



  • Hallo zusammen,

    ich bin jetzt soweit, dass ich Modelle im Wavefront .obj Format einlesen und rendern kann. Nachdem ich nun auch meinen Texturloader fertiggestellt habe, musste ich feststellen, dass meine Texturen nicht richtig gerendert werden.
    Was mir aufgefallen ist, dass im .obj Format, eine Unterschiedliche Anzahl an Verices, Normalen und Texturkoordinaten vorhanden ist. Außerdem ist mir bis jetzt noch keine OpenGL-Funktion bekannt, die die Faces der Normals und Texturekoordinaten abarbeitet.

    Nun meine Frage: Wie kann ich die .obj-Modell korrekt mit der dazugehörigen Textur rendern?

    Ich habe mal irgendwo gelesen, dass die Faces alle in ein Array "interweaved?" gepackt werden sollen, und dann alles per glDrawElements korrekt gerendert wird.

    Anbei mein Code, vll. ist dort noch ein Fehler enthalten.

    #include "Device.h"
    #include "Light.h"
    #include "TextureLoader.h"
    #include "ModelLoader.h"
    #include "Model.h"
    
    void init();
    void display();
    void reshape(int, int);
    
    Device *device;
    Light *l1;
    TextureLoader tl;
    ModelLoader ml;
    Model *m;
    
    float angle = 0.0;
    
    int main(int argc, char **argv)
    {
    	int winSize[2] = {800, 600};
    	int winPos[2] = {1920/2-800/2, 1080/2-600/2};
    
    	device = new Device(argc, argv, winSize, winPos, "Test", GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH, display, reshape);
    	init();	
    	device->setIdleFunc(display);
    	device->run();
    }
    
    void init()
    {
    	glEnable(GL_DEPTH_TEST);
    	glEnable(GL_TEXTURE_2D);
    	glEnable(GL_LIGHTING);
    	glClearColor(0.0, 0.0, 0.0, 0.0);
    	glShadeModel(GL_SMOOTH);
    
    	l1 = new Light(GL_LIGHT0, 15, 0, 0);
    	l1->setVisible(true);
    
    	m = ml.loadOBJModel("sphere.obj");
    	m->setTexture(tl.loadBMPTexture("Unbenannt.bmp"));
    }
    
    void display()
    {
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    	glColor3f(1.0, 1.0, 1.0);
    
    	glRotatef(0.01f, 0, 1, 0);
    	m->render();
    
    	glPushMatrix();
    	glTranslatef(10, 10, 0);
    	glutSolidSphere(4, 16, 8);
    	glPopMatrix();
    
    	glPushMatrix();
    	glRotatef(angle, 0, 1, 0);
    	GLfloat pos[] = {10, 0, 0, 0};
    	l1->glLight(GL_POSITION, pos); 
    	angle += 0.1f;
    	glPopMatrix();
    
    	glutSwapBuffers();
    }
    
    void reshape(int w, int h)
    {
    	glViewport(0, 0, (GLsizei) w, (GLsizei) h);
    	glMatrixMode(GL_PROJECTION);
    	glLoadIdentity();
    	gluPerspective(65.0, (GLdouble) w / (GLdouble) h, 1.0, 100.0);
    	glMatrixMode(GL_MODELVIEW);
    	glLoadIdentity();
    	gluLookAt(0.0, 0.0, 40.0, 
    	0.0, 0.0, 0.0,
    	0.0, 1.0, 0.0);
    }
    
    void Model::render(void)
    {
    	glPushMatrix();
    	{
    		glEnableClientState(GL_VERTEX_ARRAY);
    		glEnableClientState(GL_NORMAL_ARRAY);
    		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    
    		glBindTexture(GL_TEXTURE_2D, texture);
    
    		glTexCoordPointer(3, GL_FLOAT, 0, vt);
    		glNormalPointer(GL_FLOAT, 0, vn);
    		glVertexPointer(3, GL_FLOAT, 0, v);
    
    		glDrawElements(GL_TRIANGLES, fCount, GL_UNSIGNED_INT, fv);
    
    		glDisableClientState(GL_VERTEX_ARRAY);
    		glDisableClientState(GL_NORMAL_ARRAY);
    		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    	}
    	glPopMatrix();
    }
    
    GLuint TextureLoader::loadBMPTexture(const string file)
    {
    	Tools tools;
    	GLuint texture;
    	GLuint bfOffBits;
    	GLuint width;
    	GLuint height;
    	GLuint size;
    	GLubyte *data;
    
    	ifstream ifs;
    	ifs.open(file.c_str(), ios_base::in);
    
    	if(ifs.good())
    		cout << "Textur: " << file << " wurde geoeffnet." << endl;
    	else
    	{
    		cout << "ERROR >> Textur: " << file << " konnte nicht geoeffnet werden." << endl;
    		return 0;
    	}
    
    	ifs.seekg(10, ios_base::beg);
    	ifs.read(reinterpret_cast<char*>(&bfOffBits), 4);
    	cout << "OffBits: " << bfOffBits << endl;
    
    	ifs.seekg(18, ios_base::beg);
    	ifs.read(reinterpret_cast<char*>(&width), 4);
    	cout << "Width: " << width << endl;
    
    	ifs.seekg(22, ios_base::beg);
    	ifs.read(reinterpret_cast<char*>(&height), 4);
    	cout << "Height: " << height << endl;
    
    	size = width * height * 3;
    	data = new GLubyte[size];
    	char *cdata = new char[size];
    
    	ifs.seekg(bfOffBits, ios_base::beg);
    	ifs.read(reinterpret_cast<char*>(data), size);
    
    	glGenTextures(1, &texture);
    	glBindTexture(GL_TEXTURE_2D, texture);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT, data);	
    
    	delete []data;
    
    	return texture;
    }
    
    # 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
    # File Created: 19.08.2012 14:21:25
    
    mtllib sphere.mtl
    
    #
    # object Sphere001
    #
    
    v  0.0000 10.0000 -0.0000
    v  -0.0000 7.0711 -7.0711
    v  -5.0000 7.0711 -5.0000
    v  -7.0711 7.0711 0.0000
    v  -5.0000 7.0711 5.0000
    v  0.0000 7.0711 7.0711
    v  5.0000 7.0711 5.0000
    v  7.0711 7.0711 0.0000
    v  5.0000 7.0711 -5.0000
    v  -0.0000 -0.0000 -10.0000
    v  -7.0711 -0.0000 -7.0711
    v  -10.0000 -0.0000 0.0000
    v  -7.0711 -0.0000 7.0711
    v  0.0000 -0.0000 10.0000
    v  7.0711 -0.0000 7.0711
    v  10.0000 -0.0000 0.0000
    v  7.0711 -0.0000 -7.0711
    v  -0.0000 -7.0711 -7.0711
    v  -5.0000 -7.0711 -5.0000
    v  -7.0711 -7.0711 0.0000
    v  -5.0000 -7.0711 5.0000
    v  0.0000 -7.0711 7.0711
    v  5.0000 -7.0711 5.0000
    v  7.0711 -7.0711 0.0000
    v  5.0000 -7.0711 -5.0000
    v  0.0000 -10.0000 -0.0000
    # 26 vertices
    
    vn -0.0000 1.0000 -0.0000
    vn 0.0000 0.6630 -0.7486
    vn -0.5294 0.6630 -0.5294
    vn -0.7486 0.6630 0.0000
    vn -0.5294 0.6630 0.5294
    vn 0.0000 0.6630 0.7486
    vn 0.5294 0.6630 0.5294
    vn 0.7486 0.6630 0.0000
    vn 0.5294 0.6630 -0.5294
    vn 0.0000 -0.0000 -1.0000
    vn -0.7071 -0.0000 -0.7071
    vn -1.0000 -0.0000 0.0000
    vn -0.7071 -0.0000 0.7071
    vn -0.0000 -0.0000 1.0000
    vn 0.7071 0.0000 0.7071
    vn 1.0000 -0.0000 0.0000
    vn 0.7071 -0.0000 -0.7071
    vn 0.0000 -0.6630 -0.7486
    vn -0.5294 -0.6630 -0.5294
    vn -0.7486 -0.6630 0.0000
    vn -0.5294 -0.6630 0.5294
    vn 0.0000 -0.6630 0.7486
    vn 0.5294 -0.6630 0.5294
    vn 0.7486 -0.6630 0.0000
    vn 0.5294 -0.6630 -0.5294
    vn 0.0000 -1.0000 -0.0000
    # 26 vertex normals
    
    vt 0.0000 1.0000 0.0000
    vt 0.0000 0.7500 0.0000
    vt 0.1250 0.7500 0.0000
    vt 0.1250 1.0000 0.0000
    vt 0.2500 0.7500 0.0000
    vt 0.2500 1.0000 0.0000
    vt 0.3750 0.7500 0.0000
    vt 0.3750 1.0000 0.0000
    vt 0.5000 0.7500 0.0000
    vt 0.5000 1.0000 0.0000
    vt 0.6250 0.7500 0.0000
    vt 0.6250 1.0000 0.0000
    vt 0.7500 0.7500 0.0000
    vt 0.7500 1.0000 0.0000
    vt 0.8750 0.7500 0.0000
    vt 0.8750 1.0000 0.0000
    vt 1.0000 0.7500 0.0000
    vt 0.0000 0.5000 0.0000
    vt 0.1250 0.5000 0.0000
    vt 0.2500 0.5000 0.0000
    vt 0.3750 0.5000 0.0000
    vt 0.5000 0.5000 0.0000
    vt 0.6250 0.5000 0.0000
    vt 0.7500 0.5000 0.0000
    vt 0.8750 0.5000 0.0000
    vt 1.0000 0.5000 0.0000
    vt 0.0000 0.2500 0.0000
    vt 0.1250 0.2500 0.0000
    vt 0.2500 0.2500 0.0000
    vt 0.3750 0.2500 0.0000
    vt 0.5000 0.2500 0.0000
    vt 0.6250 0.2500 0.0000
    vt 0.7500 0.2500 0.0000
    vt 0.8750 0.2500 0.0000
    vt 1.0000 0.2500 0.0000
    vt 0.0000 0.0000 0.0000
    vt 0.1250 0.0000 0.0000
    vt 0.2500 0.0000 0.0000
    vt 0.3750 0.0000 0.0000
    vt 0.5000 0.0000 0.0000
    vt 0.6250 0.0000 0.0000
    vt 0.7500 0.0000 0.0000
    vt 0.8750 0.0000 0.0000
    # 43 texture coords
    
    g Sphere001
    usemtl Material__25
    f 1/1/1 2/2/2 3/3/3 
    f 1/4/1 3/3/3 4/5/4 
    f 1/6/1 4/5/4 5/7/5 
    f 1/8/1 5/7/5 6/9/6 
    f 1/10/1 6/9/6 7/11/7 
    f 1/12/1 7/11/7 8/13/8 
    f 1/14/1 8/13/8 9/15/9 
    f 1/16/1 9/15/9 2/17/2 
    f 2/2/2 10/18/10 11/19/11 
    f 2/2/2 11/19/11 3/3/3 
    f 3/3/3 11/19/11 12/20/12 
    f 3/3/3 12/20/12 4/5/4 
    f 4/5/4 12/20/12 13/21/13 
    f 4/5/4 13/21/13 5/7/5 
    f 5/7/5 13/21/13 14/22/14 
    f 5/7/5 14/22/14 6/9/6 
    f 6/9/6 14/22/14 15/23/15 
    f 6/9/6 15/23/15 7/11/7 
    f 7/11/7 15/23/15 16/24/16 
    f 7/11/7 16/24/16 8/13/8 
    f 8/13/8 16/24/16 17/25/17 
    f 8/13/8 17/25/17 9/15/9 
    f 9/15/9 17/25/17 10/26/10 
    f 9/15/9 10/26/10 2/17/2 
    f 10/18/10 18/27/18 19/28/19 
    f 10/18/10 19/28/19 11/19/11 
    f 11/19/11 19/28/19 20/29/20 
    f 11/19/11 20/29/20 12/20/12 
    f 12/20/12 20/29/20 21/30/21 
    f 12/20/12 21/30/21 13/21/13 
    f 13/21/13 21/30/21 22/31/22 
    f 13/21/13 22/31/22 14/22/14 
    f 14/22/14 22/31/22 23/32/23 
    f 14/22/14 23/32/23 15/23/15 
    f 15/23/15 23/32/23 24/33/24 
    f 15/23/15 24/33/24 16/24/16 
    f 16/24/16 24/33/24 25/34/25 
    f 16/24/16 25/34/25 17/25/17 
    f 17/25/17 25/34/25 18/35/18 
    f 17/25/17 18/35/18 10/26/10 
    f 26/36/26 19/28/19 18/27/18 
    f 26/37/26 20/29/20 19/28/19 
    f 26/38/26 21/30/21 20/29/20 
    f 26/39/26 22/31/22 21/30/21 
    f 26/40/26 23/32/23 22/31/22 
    f 26/41/26 24/33/24 23/32/23 
    f 26/42/26 25/34/25 24/33/24 
    f 26/43/26 18/35/18 25/34/25 
    # 48 faces
    


  • Hast du einfach alle Vertices in eine VertexBufferObjekt geladen und alle Texturkoordinaten in eine VertexBufferObjekt geladen und die Indizes aus den Faces geladen?
    Ich hoffe, ich habe das richtig interpretiert (der Code dafür ist leider nicht dabei)

    Das funktioniert, wenn man nur die Vertices verwendet. Allerdings stimmen die Indizes nur für die Vertices, aber die Texturkoordinaten benötigen andere Indizes.

    Meine Methode ist momentan, Dreieck für Dreieck die Vertices, Texturkoordinaten und Normalen in VBOs zu laden, dann kann ich diese mit glDrawArrays rendern.

    Wenn du glDrawElements verwenden willst, musst du bedenken, dass für ein Element Vertex, Texturkoordinate und Normale zusammenpassen müssen, denn glDrawElements verwendet einen Index für alle drei Parameter.

    Sollte ich das falsch interpretiert haben entschuldige ich mich für deine fürs Lesen verschwendete Zeit 😉



  • Ich habe jeweils für die Vertices, Texturkoordinaten und Normalen ein Array und dann noch jeweils für die drei Indices ein Array. Ich hab mir schon gedacht, dass für die Texturkoordinaten die richten Indices benötigt werden. Aber wie kann ich die denn rendern??

    Hier nochmal der komplette Code meiner Klassen.

    #include "Model.h"
    
    Model::Model(GLfloat *v, GLfloat *vn, GLfloat *vt, GLuint *fv, GLuint *fvt, GLuint *fvn, int vCount, int vnCount, int vtCount, int fCount)
    {
    	this->vCount = vCount;
    	this->vnCount = vnCount;
    	this->vtCount = vtCount;
    	this->fCount = fCount;
    	this->v = new GLfloat[this->vCount];
    	this->vn = new GLfloat[this->vnCount];
    	this->vt = new GLfloat[this->vtCount];
    	this->fv = new GLuint[this->fCount];
    	this->fvn = new GLuint[this->fCount];
    	this->fvt = new GLuint[this->fCount];
    
    	for(int i = 0;i < this->vCount;i++)
    		this->v[i] = v[i];
    
    	for(int i = 0;i < this->vnCount;i++)
    		this->vn[i] = vn[i];
    
    	for(int i = 0;i < this->vtCount;i++)
    		this->vt[i] = vt[i];
    
    	for(int i = 0;i < this->fCount;i++)
    	{
    		this->fv[i] = fv[i];
    		this->fvt[i] = fvt[i];
    		this->fvn[i] = fvn[i];
    	}
    }
    
    Model::~Model(void)
    {
    	delete []position;
    	delete []v;
    	delete []vn;
    	delete []vt;
    	delete []fv;
    	delete []fvt;
    	delete []fvn;
    }
    
    void Model::render(void)
    {
    	glPushMatrix();
    	{
    		glEnableClientState(GL_VERTEX_ARRAY);
    		glEnableClientState(GL_NORMAL_ARRAY);
    		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    
    		glBindTexture(GL_TEXTURE_2D, texture);
    
    		glVertexPointer(3, GL_FLOAT, 0, v);
    		glNormalPointer(GL_FLOAT, 0, vn);
    		glTexCoordPointer(3, GL_FLOAT, 0, vt);
    		glDrawElements(GL_TRIANGLES, fCount, GL_UNSIGNED_INT, fv);
    
    		glDisableClientState(GL_VERTEX_ARRAY);
    		glDisableClientState(GL_NORMAL_ARRAY);
    		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    	}
    	glPopMatrix();
    }
    
    void Model::setTexture(GLuint tex)
    {
    	texture = tex;
    }
    


  • Für deine Idee mit mehreren Indizes gibt es in OpenGL keine Funktion.

    Das heißt für dich, entweder du sortierst alle Vertexattribute so, dass sie zusammenpassen, damit ein Index passt, oder du legst sie gleich Dreieck für Dreieck in den Array und renderst mit glDrawArrays.
    Die erste Möglichkeit ist besser, die zweite einfacher zu realisieren.



  • Hey Senfti,

    danke für deine Antwort 🙂 hatte schon die Hoffnung verloren 😛

    Das heißt für dich, entweder du sortierst alle Vertexattribute so, dass sie zusammenpassen, damit ein Index passt...

    Wie sieht das Ganze dann aus? Und benötige ich dann nur die Indizes der Vertices?

    Vertex1, Normale1, Texturkoordinate1, Index1,
    Vertex2, Normale2, Texturkoordinate2, Index2, ...
    

    So in etwa?? 🙂

    ...oder du legst sie gleich Dreieck für Dreieck in den Array und renderst mit glDrawArrays.

    Wie würde das aussehen?

    Danke für deine Hilfe 🙂



  • Am Beispiel der ersten 2 Dreiecke:

    f 1/1/1 2/2/2 3/3/3
    f 1/4/1 3/3/3 4/5/4

    Für glDrawElements:
    Es wird der Index für jedes Vertexattribut verwendet, also auch für Texturkoordinaten und Normalen.
    Im 1. Dreieck ist dein Code in Ordnung, da die Indizes 1,2 und 3 für alle Vertexattribute gelten.
    Im 2. Dreieck beginnen die Probleme, da für den Vertex 1/4/1 nicht die 4. sondern auch wieder die 1. Texturkoordinate verwendet wird (eben weil für Vertex, Texturkoordinate und Normale der Vertexindizes verwendet wird.
    Somit müsstest du den 1. Vertex noch einmal an 4. Stelle im Array haben, damit Vertexindex und Texturkoordinatenindex zusammenpassen.
    Du siehst, das wird ein aufwendiger Code, um das richtig hinzubekommen.

    also müsste der Array so sein, wenn du die Vertexindizes für glDrawElements verwendest:

    v 0.0000 10.0000 -0.0000
    v -0.0000 7.0711 -7.0711
    v -5.0000 7.0711 -5.0000
    v 0.0000 10.0000 -0.0000 //wieder erster Vertex

    vt 0.0000 1.0000 0.0000
    vt 0.0000 0.7500 0.0000
    vt 0.1250 0.7500 0.0000
    vt 0.1250 1.0000 0.0000 //passende Texturkoordinaten

    Einfacher, aber mit etwas schlechterer Performance, Dreieck für Dreieck mit glDrawArrays:
    Dazu brauchst du keine Indizes mehr, dafür müssen die Vertizes, Texturkoordinaten und Normalen im Array richtig geordnet sein:

    für die ersten Dreiecke sieht das dann so im Array aus:

    v[0] = Vertex 1, v[1] = Vertex 2, v[2] = Vertex 3, //1. Dreieck
    v[3] = Vertex 1, v[4] = Vertex 3, v[5] = Vertex 4, //2. Dreieck
    ...

    vt[0] = TexCoord 1, vt[1] = TexCoord 2, vt[2] = TexCoord 3, //1. Dreieck
    vt[3] = TexCoord 4, vt[4] = TexCoord 3, vt[5] = TexCoord 5, //2. Dreieck
    ...



  • Danke für deine Hilfe 🙂 funktioniert zu 100% 🙂 😘


Anmelden zum Antworten