glLoadIdentity() führt zu einem schwarzen Bild beim Versuch stencil shadows zu rendern
-
Ich habe mal probiert stencil shadows zu zeichnen und auch schon die Funktionen für die Berechnung der Kanten etc fertig.
Aber ich habe einen merkwürdigen Fehler in der unten angefügten Funktion. Das erste glLoadIdentity(); führt dazu, dass mein Bild einfach schwarz wird.
Ich habe auch mal testweise code aus einer stencil-shadow demo in mein Programm eingefügt um auszuschließen, dass es an irgendetwas andereres liegen könnte.Fehler werden keine ausgegeben, weis jemand wie man am besten herausfindet was das Problem ist.
glPushMatrix(); glLoadIdentity(); // schwarzes Bild glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, 1, 1, 0, 0, 1); glDisable(GL_DEPTH_TEST); glColor4f(0.0f, 0.0f, 0.0f, 0.5f); glBegin(GL_QUADS); glVertex2i(0, 0); glVertex2i(0, 1); glVertex2i(1, 1); glVertex2i(1, 0); glEnd(); glEnable(GL_DEPTH_TEST); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix();
void SceneGraph::RenderGraph() { /* * Check for errors while execution */ DisplayErrorMessage(); glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // lösche Screen und Depth Buffer glLoadIdentity(); // Resette die Modelview Matrix glColor3f(1.f, 1.f, 1.f); m_pCamera->setPerspective(); //.. //Objekte zeichnen //Schattenvolumen zeichnen mit Stencil test //volumen mit oberen schatten ersetzen SDL_GL_SwapBuffers(); }
-
Das erste glLoadIdentity(); führt dazu, dass mein Bild einfach schwarz wird.
Nunja, Du malst ein schwarzes Polygon ueber den ganzen Bildschirm...
wie man am besten herausfindet was das Problem ist
Blending vergessen?
-
Habe vergessen Stencil Buffer in SDL zu initialisieren ): Jetzt scheint es endlich zu gehen, aber ich habe merkwürdige Darstellungsfehler. Wobei die Silhouette und das Volumen aber theoretisch richtig berechnet wurde. Ich habe es mit einem geschlossenen Körper getestet (Würfel).
Ich bin schonmal froh das der Teil funktioniert. Ich habe Eine Funktion geschrieben, die beliebeige Vertex-Arrays in Triangles zerlegt, diese prüft, ob sie bestrahlt werden und daraus die Kanten der Silhouette bildet.
Meine Würfelsilhouette bei 90° Einfallswinkel hat bei so einem Test auch genau 4 Seitenkanten und das Volumen passt eigentlich auch, wenn ich es rendere.
Wenn ich aber mit Stenciltest rendere, dann habe ich kurioserweise immer nur von einem bestimmnten Blickwinkel einen korrekten Schatten. Ansonsten entweder keinen Schatten, teile davon und unter anderem auch das komplette Volumen?!
Kennst du dich oder irgendjemand mit Schattenvolumen aus? Weil ich dann evtl. mal fragen wollte woran das liegen könnte, wenn ich den source poste.
-
Kennst du dich oder irgendjemand mit Schattenvolumen aus?
Ja.
Anhand Deiner Beschreibung gibt's aber viele moegliche Fehlerquellen.
-
Ich habe mal meinen Code ein wenig aufgeräumt. Ich habe eine kleine Engine die im wesentlichen in 3 Klassen gegliedert ist. Ein Graph, ein GraphScene und ein SceneItem.
Das Item hat ne eigene Zeichenfunktion und erbt von ShadowObject, welches die Funktion zum zeichnen des Schattens hat.
Darum beschränke ich mich auf das Posten dieser Klasse:Als erstes mein Triangle: Ich zerlege das Vertexarray in Dreiecke, das senkt vermutlich die Renderzeit, weil ich nicht jeden Vertex einzeln prüfen muss und erlaubt die Silhouette schneller zu berechnen.
class Triangle { public: /*Punkte die ein Dreieck beschreiben * INTS ==> INDICES im VArray*/ std::vector<int> m_vPoints; /*Alle möglichen Nachbardreiecke eines Dreiecks * INTS ==> INDICES im Array */ std::vector<int> m_iTriangleNeighbors; /*mittlere Normale der Dreiecksfläche*/ Vec3f m_vNormal; /*ist das Dreieck in der Silhouette*/ bool m_bIsInSilhouette; };
meine hat folgende wichtige Funktionen (sind durch Comments kurz umschrieben):
/* Als erstes wird aus dem Vertexarray eine Liste aus Dreiecken gebildet */ void CreateTriangles(); /* Zum schnelleren berechnen der Silhouette wird * danach eine Durchschnittsnormale berechnet */ void CalcNormalOfTriangles(); /* Man muss wissen welche Nachbardreiecke jedes Dreieck besitzt, * um die Kante der Silhouette zu berechnen */ void CalcEachNeighborTriangles(); /* Berechnet welche Dreiecke in der Silhouette sind */ void CalcTrianglesInSilhouette(const Vec3f &lightPos); /* Berechnet den Umriss der Silhouette */ void CalcBorder(const Vec3f &lightPos);
Da meine Funktionen vermutlich funktionieren poste ich nur mal die, die aus der Silhouette (also den beschienenen Dreiecken) den Umriss errechnet und die Zeichenfunktionen:
Die Funktion funkioniert im Moment wohl nur mit geschlossenen Objekten, für Flächen, die ein Ende haben und damit weniger als 3 Nachbardreiecke muss ich mal irgendwann erweitern. Die Funktion an sich läuft die errechneten Dreiecke durch und prüft ob es in der Silhouette ist, wenn ja, dann wird ein Nachbardreieck gesucht, welches nicht bestrahlt wird und dann kann die Kante gesucht werden. Das macht die Funktion GetEdge(), diese gibt dann einfach die zwei Punkte der Kante zurück!!!
Die Kante wird dann mit einem Vektor ausgehend vom Licht verlängert und einfach in ein Array gelegt um es dann in der nächsten Funktion zu zeichnen.void ShadowObject::CalcBorder(const Vec3f &lightPos) { m_vShadowVol.clear(); Triangle curTriangle, curNeighbor; std::vector<Vec3f> curBorder; Vec3f curVec; for(unsigned int i = 0; i < m_vTriangles.size(); i++) { if(m_vTriangles[i].m_bIsInSilhouette == true) { curTriangle = m_vTriangles[i]; //std::cout<<"Dreieck "<<i<<" ist in Silhouette"<<std::endl; for(int j = 0; j < m_vTriangles[i].m_iTriangleNeighbors.size(); j++) { curNeighbor = m_vTriangles[curTriangle.m_iTriangleNeighbors[j] ]; //std::cout <<"Dreieck "<<curTriangle.m_iTriangleNeighbors[j] // <<" ; Wert: "<<curNeighbor.m_bIsInSilhouette<<std::endl; if(curNeighbor.m_bIsInSilhouette == false) { curBorder = GetEdge(curTriangle, curNeighbor); //std::cout<<"Punkte in Kante: "<<curBorder.size()<<std::endl; //if(curBorder.size() < 2) //return; curVec = curBorder[0] - lightPos; curVec.Normalize(); curVec *= 1000; m_vShadowVol.push_back(curBorder[0]); m_vShadowVol.push_back(curBorder[0]+curVec); curVec = curBorder[1] - lightPos; curVec.Normalize(); curVec *= 1000; m_vShadowVol.push_back(curBorder[1]); m_vShadowVol.push_back(curBorder[1]+curVec); } } } } //std::cout<<"SILHOUETTE "<<m_vShadowVol.size()<<std::endl; }
Diese Funktion soll dann zeichnen, was vorher errechnet wurde. Das scheint auch zu Klappen, denn ich kriege so ein Bild, wenn ich mit GL_LINES rendere:
http://img186.imageshack.us/img186/3320/bildschirmfoto1za1.th.jpgvoid ShadowObject::DrawShadowVolume(const Vec3f &lightPos) { CalcTrianglesInSilhouette(lightPos); CalcBorder(lightPos); /* * Draw SIDES */ for(unsigned int i = 0; i < m_vShadowVol.size()/4; i++) { glBegin(GL_TRIANGLE_STRIP); glVertex3fv(m_vShadowVol[i*4+0]); glVertex3fv(m_vShadowVol[i*4+1]); glVertex3fv(m_vShadowVol[i*4+2]); glVertex3fv(m_vShadowVol[i*4+3]); glEnd(); } /* * Draw Bottom Cap */ /* glBegin(GL_POLYGON); for(unsigned int i = 1; i < m_vShadowVol.size(); i+=2) glVertex3fv(m_vShadowVol[i]); glEnd(); */ /* * Draw Front Cap */ /* glBegin(GL_POLYGON); for(unsigned int i = 0; i < m_vShadowVol.size(); i+=2) glVertex3fv(m_vShadowVol[i]); glEnd(); */ }
So und dann habe ich versucht das Ganze als Schatten zu zeichnen.
// schattenvolumen für übergebene lichtquellen zeichnen void ShadowObject::CastShadows(std::vector<Vec3f> lightSources) { glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glDepthMask(GL_FALSE); glEnable(GL_CULL_FACE); glEnable(GL_STENCIL_TEST); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(0.0f, 100.0f); glCullFace(GL_FRONT); glStencilFunc(GL_ALWAYS, 0x0, 0xff); glStencilOp(GL_KEEP, GL_INCR, GL_KEEP); for(unsigned int i = 0; i < lightSources.size(); i++) DrawShadowVolume(lightSources.at(i)); glCullFace(GL_BACK); glStencilFunc(GL_ALWAYS, 0x0, 0xff); glStencilOp(GL_KEEP, GL_DECR, GL_KEEP); for(unsigned int i = 0; i < lightSources.size(); i++) DrawShadowVolume(lightSources.at(i)); glDisable(GL_POLYGON_OFFSET_FILL); glDisable(GL_CULL_FACE); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_TRUE); glStencilFunc(GL_NOTEQUAL, 0x0, 0xff); glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); /* * Draw Shadow */ glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, 1, 1, 0, 0, 1); glDisable(GL_DEPTH_TEST); glColor4f(0.0f, 0.0f, 0.0f, 0.5f); glBegin(GL_QUADS); glVertex2i(0, 0); glVertex2i(0, 1); glVertex2i(1, 1); glVertex2i(1, 0); glEnd(); glEnable(GL_DEPTH_TEST); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glDisable(GL_STENCIL_TEST); }
Danach sah das Ergebnis nicht mehr so bombig aus. Bis auf das zweite Bild gibts oft Darstellungsfehler, ich habe hier zum Test einfach mal ein Quad als Untergrund genommen und eine Lichtquelle herumrotieren lassen.
http://img258.imageshack.us/img258/8892/bildschirmfoto2yw9.th.jpg
http://img228.imageshack.us/img228/8689/bildschirmfoto3df9.th.jpghttp://img228.imageshack.us/img228/251/bildschirmfoto4dg1.th.jpg
edit: habe mal meinen src als 7zip anghangen, die kleine scene steht in der datei test_SceneGraphItem.h, vielleicht kann mir ja jemand helfen.
http://www.file-upload.net/download-1348723/test.7z.html
-
habe mal meinen src als 7zip anghangen
Es wird wahrscheinlich kaum jemand Lust haben, sich erst alle Abhaengigkeiten (sdl, glfw, freeimage/plus) zusammen zu suchen/uebersetzen.
Wenn ich Deinen Code richtig deute, pruefst Du zunaechst fuer jedes Dreieck ob es der Lichtquelle zugewandt ist. Fuer jedes zugewandte Dreieck ueberpruefst Du dann alle Nachbardreiecke (fuer gewoehnlich 3) ob eins davon nicht zugewandt ist und konstruierst dann an der gemeinsamen Kante (die Du mit etlichem Aufwand ermittelst) ein Quad der Silhouette.
Da Du offenbar die ZFail-Methode implementierst, musst Du das Schattenvolumen korrekt schliessen, dh lichtabgewandte Polygone ebenfalls projezieren und, je nach DepthFunc, auch zugewandte zeichnen.
Davon abgesehen ist Deine Datenstruktur zu kompliziert. Man verwendet hier normalerweise vereinfachte winged edges. An jeder Kante treffen sich genau zwei Dreiecke. Diese Liste von Dreieckspaaren generierst Du nur ein mal fuer das Mesh wobei jedes Paar eindeutig ist. Wenn bei einem Paar ein Dreieck der Lichtquelle zugewand ist und das andere nicht, ist die gemeinsame Kante Teil der Silhoutte.
An dieser Kante konstruierst Du ein Quad entlang der Lichtstrahlen (genauso wie bisher).
Wenn Du die Reihenfolge der Dreieckspaare so arrangieren kannst, dass das zweite Dreieck des aktuellen Paares das erste Dreieck des Nachfolgepaares ist (aehnlich Strips), sparst Du Dir nochmals die Haelfte des Aufwandes.
Zu beachten waere noch, dass die Normale eines Dreiecks nicht identisch mit dem Mittelwert der Vertexnormalen ist (siehe ShadowObject::CalcNormalOfTriangles).
Du renderst die Volumenhuellen aller Lichtquellen in den selben Stencilbuffer. Wie willst Du unterscheiden, welche Lichtquelle den jeweiligen Pixel nun beleuchtet?
Warum verschiebst Du die Volumenhuelle um 100 Einheiten?
-
habe das problem mit dem renderfehler gefunden, das glCullFace(GL_FRONT); funktioniert auch nicht, weil ich die reihenfolge der kanten nicht beachte.
ich muss es wohl auch so machen wie du es schilderst und die kantenreihenfolge beachten.danke schonmal, bin gespannt ob gehen wird.