PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [OpenGL] Normalen beim Import berechnen?


Ganon
2005-11-19, 13:38:53
Hi.

Ich versuche gerade einen Import von WaveFront .obj Dateien.

Jetzt will ich z.B. einen Würfel importieren :

(Ausgabe von Cinema4D als Beispiel)

# WaveFront *.obj file (generated by Cinema4D)

g W_rfel
v -99.75705 -101.707428 99.994446
v -99.75705 98.292572 99.994446
v 100.24295 -101.707428 99.994446
v 100.24295 98.292572 99.994446
v 100.24295 -101.707428 -100.005554
v 100.24295 98.292572 -100.005554
v -99.75705 -101.707428 -100.005554
v -99.75705 98.292572 -100.005554

vt 0 1 0
vt 1 0 0
vt 0 0 0
vt 0 0 0
vt 1 1 0
vt 0 1 0
vt 1 1 0
vt 0 0 0
vt 1 0 0
vt 1 0 0
vt 0 1 0
vt 1 1 0
vt 1 0 0
vt 0 0 0
vt 1 1 0
vt 0 1 0
vt 0 0 0
vt 1 0 0
vt 0 1 0
vt 1 1 0

f 1/3 2/6 4/12 3/9
f 3/8 4/11 6/15 5/13
f 5/14 6/16 8/20 7/18
f 7/17 8/19 2/5 1/2
f 2/4 8/19 6/15 4/10
f 7/17 1/1 3/7 5/13

# g = Gruppe
# v = Vectoren
# vt = Texturkoord
# f = Bauplan


Jetzt fehlen mir aber sämtliche Normalen, was zur folge hat das das Objekt von Standard-OpenGL-Licht nicht/fehlerhaft beleuchtet wird.

Kann man diese Normalen (gesetzt über glNormal) irgendwie berechnen, oder so ähnlich? Weil Cinema4D scheint das ja auch irgendwie hinzubekommen. Die Spezifikation von .obj sieht Normalen im Dateiformat vor, aber C4D schreibt diese nicht...

Danke. :)

Demirug
2005-11-19, 14:09:05
Hast du dir schon mal den MeshMender von nVidia angeschaut?

Ganon
2005-11-19, 15:59:31
Hmm. Sieht irgendwie nach Direct3D aus, oder was meinst du?

Demirug
2005-11-19, 16:02:47
Das Beispiel ist für D3D aber die MeshMender Funktion als solches kann man für jede 3D-API nutzen.

Ganon
2005-11-19, 20:28:01
Hmm. Schon mal danke. :)

Aber kann mir vielleicht jemand einen groben Ablauf sagen, wie man so etwas theoretisch angeht? Weil ich guck bei dem MeshMender-Code wie ein Schwein ins Uhrwerk.

Danke. :)

KhanRKerensky
2005-11-19, 22:30:25
Die Normalen kannst du eigentlich recht einfach über das Kreuzprodukt/Vektorprodukt berechnen.
Die Vertizes hast du ja und die Reihenfolge der Vertizes im Dreieck sind ja wohl auch bekannt. Damit kannst du dann das Kreuzprodukt ausrechnen und den Vektor den du rausbekommst musst nurnoch normalisieren.

Vieleicht auch mal das durchlesen:
http://www.resourcecode.de/view.php?id=760

ScottManDeath
2005-11-20, 00:18:08
Ich hatte den NVMeshMender mal genutzt, um den Tangentspace für Bumpmapping zu erstellen. der hatte allerdings irgendwie ein paar bugs, so dass ich das dann anders gemacht haben. AFAIK wurde der NVMeshMender aber noch übererabeitet vor einiger Zeit.

AFAIK ist es so, wenn Du keine Normalen spezifizierst (was ich tue), werden sie automatisch generiert.

Hab noch auskommentierten code gefunden:


NVMeshMender mesh_mender;
NVMeshMender::VAVector input;
NVMeshMender::VertexAttribute attribute;
for(size_t i=0; i < m_positions.size(); ++i)
{
attribute.floatVector_.push_back(m_positions[i][0]);
attribute.floatVector_.push_back(m_positions[i][1]);
attribute.floatVector_.push_back(m_positions[i][2]);
}
attribute.Name_ = "position";
input.push_back(attribute);
attribute.floatVector_.clear();
for(size_t i=0; i < m_normals.size(); ++i)
{
attribute.floatVector_.push_back(m_normals[i][0]);
attribute.floatVector_.push_back(m_normals[i][1]);
attribute.floatVector_.push_back(m_normals[i][2]);
}
attribute.Name_ = "normal";
input.push_back(attribute);
attribute.floatVector_.clear();
for(size_t i=0; i < m_texcoords.size(); ++i)
{
attribute.floatVector_.push_back(m_texcoords[i][0]);
attribute.floatVector_.push_back(m_texcoords[i][1]);
attribute.floatVector_.push_back(0.0f);
}
attribute.Name_ = "tex_coord";
input.push_back(attribute);
attribute.floatVector_.clear();
for(size_t cur_v =0; cur_v < m_indices.size(); ++cur_v)
{
attribute.intVector_.push_back(m_indices[cur_v]);
}
attribute.Name_="indices";
input.push_back(attribute);
attribute.intVector_.clear();
NVMeshMender::VAVector output;
attribute.Name_ = "position";
output.push_back(attribute);
attribute.Name_ = "tex0";
output.push_back(attribute);
attribute.Name_ = "tex_coord";
output.push_back(attribute);
attribute.Name_ = "normal";
output.push_back(attribute);
attribute.Name_ = "tangent";
output.push_back(attribute);
attribute.Name_ = "binormal";
output.push_back(attribute);
attribute.Name_ = "indices";
output.push_back(attribute);

if (!mesh_mender.Munge(input,output,GLH_PI / 2.1f,0, NVMeshMender::FixTangents,NVMeshMender::FixCylindricalTexGen,NVMeshMender::DontW eightNormalsByFaceSize))
{
printf("mesh mender failed\n");
string es;
while ( (es=mesh_mender.GetLastError())!= "")
printf("mesh mender error: %s\n",es.c_str());
getchar();
exit (0xff);
}

clear_triangles();
clear_vertices();
vector<vec3f>& tmp_positions = m_positions;
vector<vec3f>& tmp_tangents = m_tangents;
vector<vec3f>& tmp_binormals = m_binormals;
vector<vec3f>& tmp_normals = m_normals;
vector<vec2f>& tmp_texcoords = m_texcoords;

vector<GLushort>&tmp_indices = m_indices;

for ( size_t j=0; j < output.size(); ++j)
{
// printf("output <%s>\n",output[j].Name_.c_str() );
if(output[j].Name_ == "position")
{
for (size_t k = 0; k < output[j].floatVector_.size()/ 3 ; ++k)
{
tmp_positions.push_back(vec3f( output[j].floatVector_[3 * k + 0],
output[j].floatVector_[3 * k + 1],
output[j].floatVector_[3 * k + 2]) );
}
}
if(output[j].Name_ == "normal")
{
for (size_t k = 0; k < output[j].floatVector_.size()/ 3 ; ++k)
{
tmp_normals.push_back(vec3f( output[j].floatVector_[3 * k + 0],
output[j].floatVector_[3 * k + 1],
output[j].floatVector_[3 * k + 2]) );
}
}
if(output[j].Name_ == "tangent")
{
for (size_t k = 0; k < output[j].floatVector_.size()/ 3 ; ++k)
{
tmp_tangents.push_back(vec3f( output[j].floatVector_[3 * k + 0],
output[j].floatVector_[3 * k + 1],
output[j].floatVector_[3 * k + 2]) );
if ( tmp_tangents.back().length() < 1.0f)
{
//printf("found error tangent length %f\n",tmp_tangents.back().length());
// tmp_tangents.back().set_value(1.0f,0.0f,0.0f);
}
}
}
if(output[j].Name_ == "binormal")
{
for (size_t k = 0; k < output[j].floatVector_.size()/ 3 ; ++k)
{
tmp_binormals.push_back(vec3f( output[j].floatVector_[3 * k + 0],
output[j].floatVector_[3 * k + 1],
output[j].floatVector_[3 * k + 2]) );
if ( tmp_binormals.back().length() < 1.0f)
{
//printf("found error binormal length %f\n",tmp_binormals.back().length());
// tmp_binormals.back().set_value(0.0f,0.0f,-1.0f);
}
}
}
if(output[j].Name_ == "tex_coord")
{
for (size_t k = 0; k < output[j].floatVector_.size()/ 3 ; ++k)
{
tmp_texcoords.push_back(vec2f( output[j].floatVector_[3 * k + 0],
output[j].floatVector_[3 * k + 1]) );
}
}
if(output[j].Name_ == "indices")
{
for (size_t k = 0; k < output[j].intVector_.size() ; k++)
{
assert (output[j].intVector_[k] < 0xffff);
tmp_indices.push_back(output[j].intVector_[k]);
}
}
} // for outputs
//printf("indices = %u, pos = %u, tan = %u, bin = %u, nor = %u, tex = %u\n",
// tmp_indices.size(),
// tmp_positions.size(),
// tmp_tangents.size(),
// tmp_binormals.size(),
// tmp_normals.size(),
// tmp_texcoords.size());
m_vertex_count = m_positions.size();
m_triangle_count = m_indices.size()/3;

Neomi
2005-11-20, 01:02:55
Es gibt je nach Art der Quelldaten viele Wege, Normalen zu berechnen. Mal muß man sie nicht berechnen, da sie spezifiziert sind, mal hat man pro Kante die Info, ob sie hart oder weich ist. Das geht solange gut, wie höchstens 2 Polygone eine Kante benutzen. Darüber sind Smoothing Groups die eleganteste Lösung. Dabei hat man meistens einen 32 Bit großen Integer (vorzeichenlos als Bitfeld) pro Fläche, jedes Bit steht für eine Gruppe. Für einen gemeinsam genutzten Eckpunkt bekommen dann alle Flächen die gleiche Normale, die über Gruppen miteinander verbunden sind (z.B. 1x Gruppe 1, 1x Gruppe 1 und 2, 1x Gruppe 2). Flächen ohne gleiche Gruppen (z.B. 1x Gruppe 3, 1x Gruppe 4) bekommen jeweils eigene Normalen.

In deinem Fall liegen solche Infos nicht vor, also muß man eine Automatik ranlassen. Meistens werden zwischen allen Flächen weiche Kanten generiert, deren Flächennormalen weniger als einen gegebenen Wert (z.B. 30°) voneinander abweichen. Automatiken funktionieren leider nur im Regelfall, der "Normalfall" besteht aber aus Mischungen von Ausnahmefällen.

Soweit zur groben Logik der Normalengenerierung. Die Mathematik dahinter ist auch simpel. Geht es dir mehr um die Mathematik oder um die Logik?

Ganon
2005-11-20, 09:06:00
Danke erst mal. :)

Ich muss erst mal die gegebenen Informationen verarbeiten. :)

@Neomi
Mir ging es bei meiner Frage um die Logik dahinter.