Przekazywanie tablic wierzchołków do OpenGL

0

Funkcja wczytująca .obj i przekazywanie tablic:

void loadobj(char* filename, GLfloat* vertices, GLfloat* normals, GLfloat* textures, GLuint* faces)
{
	int vc=0, fc=0, nc=0, tc=0, v=0, n=0, t=0, f=0;  // Indeksy tablic
	char line[64];
	FILE *file;
	file = fopen(filename, "r");			// Open file
	setvbuf(file, NULL, _IOFBF, 1024*256);	// Assume 256 KB buffer
	while(!feof(file))
	{
		fgets(line, 64, file);          // linijka z pliku
		if(strstr(line, "v ") == line)
		{
			++vc;
		}
		else if(strstr(line, "vn") == line)
		{
			++nc;
		}
		else if(strstr(line, "vt") == line)
		{
			++tc;
		}
		else if(strstr(line, "f ") == line)
		{
			++fc;
		}
	}
	fseek(file, 0, SEEK_SET);
	vertices = (GLfloat*)calloc(3*vc, sizeof(GLfloat));
	normals = (GLfloat*)calloc(3*nc, sizeof(GLfloat));
	textures = (GLfloat*)calloc(3*tc, sizeof(GLfloat));
	faces = (GLuint*)calloc(9*fc, sizeof(GLuint));
	while(!feof(file))
	{
		fgets(line, 64, file);
		if(strstr(line, "v ") == line)    // wierzchołki
		{
			sscanf(line+2, "%f %f %f", &vertices[v], &vertices[v+1], &vertices[v+2]);
			vertices[v] /= 10000;
			vertices[v+1] /= 10000;
			vertices[v+2] /= 10000;
			v+=3;
		}
		else if(strstr(line, "vn") == line)   // normalne
		{
			sscanf(line+2, "%f %f %f", &normals[n], &normals[n+1], &normals[n+2]);
			n+=3;
		}
		else if(strstr(line, "vt") == line)   // współrzędne tekstur
		{
			sscanf(line+2, "%f %f %f", &textures[t], &textures[t+1], &textures[t+2]);
			t+=3;
		}
		else if(strstr(line, "f ") == line)   // figury (numery wierzchołków, normalnych, tekstur)
		{
			sscanf(line+2, "%u/%u/%u %u/%u/%u %u/%u/%u", &faces[f], &faces[f+3], &faces[f+6], &faces[f+1], &faces[f+4], &faces[f+7], &faces[f+2], &faces[f+5], &faces[f+8]);
			f+=9;   // format: v1/n1/t1 v2/n2/t2 v3/n3/t3
		}
	}
	fclose(file);
}

// To nasze tablice
GLfloat *V, *N, *T;
GLuint *F;

// Tutaj wczytujemy plik do tablic
void init()
{
	loadobj("plik.obj", V, N, T, F);
}

// A tu wyświetlamy
void render(void)
{
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glVertexPointer(3, GL_FLOAT, 1, V);
	glNormalPointer(3, GL_FLOAT, N);
	glTexCoordPointer(3, GL_FLOAT, 1, T);
	glDrawElements(GL_TRIANGLES, sizeof(F)/sizeof(int)/9, GL_UNSIGNED_INT, F);
	glDisableClientState(GL_VERTEX_ARRAY);  // to już nie wykonuje się
	glDisableClientState(GL_NORMAL_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

Niestety, nie ma żadnego śladu po obiekcie. Nawet ustawienie kolorów nic nie daje. Czy da się to w ogóle zrobić tak, jak powyżej? Jaka jest najlepszy i najbardziej wydajny sposób, żeby wyświetlić obiekt składający się z wielu wierzchołków?

0
  1. nie glVertexPointer(3, GL_FLOAT, 1, V); tylko glVertexPointer(3, GL_FLOAT, 0, V);

  2. Jesteś pewny że tekstury masz 3D a nie 2D? Jeżeli używasz 3D to glTexCoordPointer(3, GL_FLOAT,** 0,** T); jeżeli jednak 2D to wczytując olej 3cią współrzędną i załaduj i ustaw glTexCoordPointer(2, GL_FLOAT,** 0,** T);

  3. Co do trójkątów, to większy problem F powinno mięć tylko 3 indeksy na trójkąt powiedzmy że i1, i2, i3 i te trzy indeksy powinny być takie same dla tekstury, wierzchołków i normalnych, czyli karta graficzna będzie próbować pobrać T[i1], T[i2], T[i3], V[i1], V[i2], V[i3], N[i1], N[i2], N[i3] i z tego narysować trójkąt, obiekt obj ma niestety każdy indeks osobno dla kazdego z tych odwołań co daje 9.
    Przepakuj je do odpowiedniego formatu i zmień glDrawElements(GL_TRIANGLES, sizeof(F)/sizeof(int)/9, GL_UNSIGNED_INT, F); na glDrawElements(GL_TRIANGLES, sizeof(F)/(sizeof(int)*3), GL_UNSIGNED_INT, F);

0

Mam 3 możliwości:

A. Skonwertować OBJ do C w Blenderze. Wtedy wierzchołków jest znacznie więcej, bo są to wszystkie potrzebne kombinacje współrzędnych, normalnych i położenia tekstur. Zaleta: można użyć tablic wierzchołków lub VBO bez specjalnej obróbki. Przykład: 8*8797 wierzchołków = 70376 float. Indeksy: 3 * 3062 powierzchnie = 9186 uint. Razem: 311 KB. Tylko trójkąty. Niestety, brak danych o kolorach i materiałach.

struct vertex_struct {
	float x,y,z;
	float nx,ny,nz;
	float u,v;
};

B. Normalizować OBJ po wczytaniu do powyższego formatu.

C. Wczytywać wierzchołki, normalne i współrzędne tekstur do osobnych tablic z pliku OBJ, ale wtedy pozostaje wyłącznie tryb bezpośredni glVertex3*(). Przykład: 31533 + 31567 + 260 = 9420 danych float. Indeksy: 83062 = 24496 uint. Razem: 132 KB. Prawie 2.5 razy mniej. Można ustawić tylko trójkąty, można też zarówno trójkąty i wielokąty. Przy okazji generuje się plik MTL.

Zakładamy float = int = 4 B. Koniec obliczeń pamięciowych. Danych i tak jest mało, zatem transfer między RAM a GPU powinien przebiegać szybko. Wczytałem obiekt sposobem A, ale za pomocą funkcji glNormal3f(), glVertex3f(). Wszystko chodzi, tylko obiekt wygląda zupełnie inaczej, np. cały czerwony albo z kontrastującymi sąsiednimi trójkątami (np. jeden jasno czerwony, drugi ciemno czerwony). Tak - kolejny problem to światła i materiały! One są zapisane w pliku MTL. Eksporter może wygenerować nawet kilka osobnych materiałów, a są włączane (usemtl) w pliku OBJ.

Temat świateł i materiałów jest rozległy. W przykładowych plikach MTL światła ambient i diffuse są identyczne. Po odpowiednim zabiegu można użyć glColor3f() do zmiany obu parametrów obiektu. Specular jest już inny (ale w obu plikach 0.35 0.35 0.35). Czy dla każdego obiektu zmieniać światło, czy ono dotyczy całej sceny? Czy wystarczy ustawić identyczne światła dla całej sceny, a w ścianach zmieniać tylko kolory i ewentualnie materiały? Jak dobrze skonfigurować oświetlenie?

1 użytkowników online, w tym zalogowanych: 0, gości: 1