Tangent-Vektor ausrechnen



  • Ich hoffe mir kann bei diesem schwierigen Problem auch so gut geholfen werden wie neulich als ich eine Frage zum Interpolieren von Normalen hatte.
    Oder sollte ich lieber ins Matheforum posten?
    Ich lese aus einer Wavefrontdatei folgende Informaionen:
    Vertexkoordinaten
    Texturkoordinaten
    Normalen

    Nun möchte ich möglichst fehlerfrei für Bumpmapping Tangent (und Bitangent, aber das ist dann ja relativ einfach) bestimmten.
    Auf folgenen Link bin ich gestoßen:
    http://www.opengl.org/discussion_boards/ubb/Forum3/HTML/011349.html
    Hier zeigt Eric Lengyel den seiner Meinung nach einzig richtigen Algorithmus, der im Netz zu finden ist.

    Leider ist mein Programm anders aufgebaut als seins. Ich habe einfach zur Laufzeit std::vector's (list wäre vllt. besser, ich weiß) von oben genannten Daten + Tangent + Bitangent, und zwar ohne Indices oder sowas, einfach ne Liste , die so wie sie ist als Dreiecksliste gesehen wird.
    Er benutzt aber Indices und kann/muss damit auch lustige Sachen machen, er verwendet 2 Schleifen um die Daten zu bearbeiten.

    Ich habe den Code jetzt abgewandelt und herausgekommen ist folgendes

    // ...
    // wird immer wenn ein "f" im .obj file gefunden wurde, ausgeführt
    	else if (s == "f") 
    		{
    			size_t indices[3][3];
    			//memset(&indices[0][0], 0, 9);
    			for (int i = 0; i < 3; ++i) 
    				for (int j = 0; j < 3; ++j) 
    					indices[ i ][j] = 0;
    			for (int i = 0; i < 3; ++i) 
    			{
    
    				f >> s;
    				size_t index1 = s.find_first_of('/');
    				if (index1 != string::npos)
    				{
    					string sub = s.substr(0, index1);
    					indices[ i ][0] = stringToSizet(sub);
    					size_t index2 = s.find_first_of('/', index1 + 1);
    					sub = s.substr(index1 + 1, index2 - index1 - 1);
    					if (sub != "") 
    						indices[ i ][1] = stringToSizet(sub);
    					sub = s.substr(index2 + 1);	
    					if (sub != "") 
    						indices[ i ][2] = stringToSizet(sub);
    				}
    				else
    				{
    					indices[ i ][0] = stringToSizet(s);
    				}
    			}
    
    // hier wirds langsam interessant
    vector3 & v1 = (*coords)[indices[0][0] - 1];
    			vector3 & v2 = (*coords)[indices[1][0] - 1];
    			vector3 & v3 = (*coords)[indices[2][0] - 1];
    
    			vector2 & w1 = (indices[0][1] > 0) ? (*texcoords)[indices[0][1] - 1] : vector2(0.0f, 0.0f);
    			vector2 & w2 = (indices[1][1] > 0) ? (*texcoords)[indices[1][1] - 1] : vector2(0.0f, 0.0f);
    			vector2 & w3 = (indices[2][1] > 0) ? (*texcoords)[indices[2][1] - 1] : vector2(0.0f, 0.0f);
    
    			vector3 n = calcNormal((*coords)[indices[0][0] - 1], (*coords)[indices[1][0] - 1], (*coords)[indices[2][0] - 1]);
    			vector3 & n1 = (indices[0][2] > 0) ? (*normals)[indices[0][2] - 1] : n;
    			vector3 & n2 = (indices[1][2] > 0) ? (*normals)[indices[1][2] - 1] : n;
    			vector3 & n3 = (indices[2][2] > 0) ? (*normals)[indices[2][2] - 1] : n;
    
    			float x1 = v2.x - v1.x;
    			float x2 = v3.x - v1.x;
    			float y1 = v2.y - v1.y;
    			float y2 = v3.y - v1.y;
    			float z1 = v2.z - v1.z;
    			float z2 = v3.z - v1.z;
    
    			float s1 = w2.x - w1.x;
    			float s2 = w3.x - w1.x;
    			float t1 = w2.y - w1.y;
    			float t2 = w3.y - w1.y;
    
    			float r = 1.0f / (s1 * t2 - s2 * t1);
    			vector3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
    			vector3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
    
    			vector3 tangent_vec1 = (sdir - DotProduct(n1, sdir) * n1).normalize();
    			vector3 bitangent_vec1 = CrossProduct(n1, sdir);
    			bitangent_vec1 = DotProduct(bitangent_vec1, tdir) < 0 ? -bitangent_vec1 : bitangent_vec1;
    			vector3 tangent_vec2 = (sdir - DotProduct(n2, sdir) * n2).normalize();
    			vector3 bitangent_vec2 = CrossProduct(n2, sdir);
    			bitangent_vec2 = DotProduct(bitangent_vec2, tdir) < 0 ? -bitangent_vec2 : bitangent_vec2;
    			vector3 tangent_vec3 = (sdir - DotProduct(n3, sdir) * n3).normalize();
    			vector3 bitangent_vec3 = CrossProduct(n3, sdir);
    			bitangent_vec3 = DotProduct(bitangent_vec3, tdir) < 0 ? -bitangent_vec3 : bitangent_vec3;
    
    			normal.push_back(n1); 
    			vertex.push_back(v1);
    			texcoord.push_back(w1);
    			tangent.push_back(tangent_vec1);
    			bitangent.push_back(bitangent_vec1);
    
    			normal.push_back(n2);
    			vertex.push_back(v2);
    			texcoord.push_back(w2);
    			tangent.push_back(tangent_vec2);
    			bitangent.push_back(bitangent_vec2);
    
    			normal.push_back(n3); 
    			vertex.push_back(v3);
    			texcoord.push_back(w3);
    			tangent.push_back(tangent_vec3);
    			bitangent.push_back(bitangent_vec3);
    		}
    	}
    

    Ich dachte nämlich, dass ich sowas nicht brauche :

    tan1[i1] += sdir;
    tan1[i2] += sdir;
    tan1[i3] += sdir;
    
    tan2[i1] += tdir;
    tan2[i2] += tdir;
    tan2[i3] += tdir;
    

    weil ja ohnehin bei mir jedes Dreieck 3 eigene Verices hat, und auch für jedes Dreieck 3 eigene Tangenten / Bitangenten da sind.

    Resultat: Ich hab sowas ähnliches wie Flat Shading nur etwas besser; sehr seltsam. Was kann ich am Besten tun, um wieder ordentliches Phong Shading zu bekommen?

    Edit: Argh, wieso muss i in eckigen Klammern ausgerechnet auch noch schräg schreiben heißen?



  • Naja, ich vermute ich muss
    a) nicht jedem Vertex eine eigene Tangente geben sondern die Tangenten müssen "geshared" werden wie die Normalen auch
    b) ich sollte a) befolgen, anstatt zu versuchen mit den geshareten Normalen unterschiedliche Tangenten pro Vertex eines Dreiecks zu errechnen, und zum Berechnen immer die eine wirkliche Normale des Dreiecks benutzen

    dazu muss ich irgendwie rausfinden, welche Vertices die selbe Position haben und damit den selben Tangentenvektor

    Toll, denn das ist nur schwer bei meinem System verwirklichbar (weil ich nicht mit Indices arbeite, wie gesagt, und wegen Rundungsfehlern)

    Hoffe ich irre mich, und jemand sagt mir was ich sonst ändern könnte dass es klappt, am besten mit angepasstem Code gratis 🤡
    😉
    ...
    😞



  • Habs geschafft (in 10 Minuten oder so, lol). Noch nicht optimal aber geht erstmal, relativ gut zumindest

    for (size_t i = 0; i < vertex.size(); ++i) 
    	{
    		for (size_t j = i + 1; j < vertex.size(); ++j)
    		{
    			if (NearlyEquals(vertex[ i], vertex[j], 0.001))
    			{
    				tangent[ i] = Normalized(tangent[ i] + tangent[j]);
    				tangent[j] = tangent[ i];
    				bitangent[ i] = Normalized(bitangent[ i] + bitangent[j]);
    				bitangent[j] = bitangent[ i];
    			}
    		}
    	}
    

    einfach hinten dran



  • An Deiner Stelle würde ich sowieso einen Indexbuffer benutzen. Ich weiß zwar nicht genau was Du da darstellen willst, aber wahrscheinlich könntest Du einen Haufen Vertizen einsparen (die Du jetzt mehrfach an der selben Position hast).
    Das würde den Bus stark entlasten.
    Übrigens reicht es, wenn Du nur 2 der 3 nötigen Vektoren speicherst. Den dritten kann man im Shader mittels Kreuzprodukt bekommen, das spart auch nochmal speicher.



  • Du hast recht, aber ich habe erstmal Bequemlichkeit und Geschwindigkeit vorgehen lassen.
    Das mit den 3 Vektoren bleibt erstmal sicher, weil so viel Speicher ist das nich, sagen wir ich hab 100,000 Dreiecke und 50,000 Vertices, also 50,000 * 3 * 8 byte * 2/3 < 1 MB; Aber 2/3 * 50000 Kreuzprodukte pro Frame gespart.
    Naja, Ansichtssache.


Anmelden zum Antworten