2010-01-14 17 views
6

Ich mache einen Software-Rasterer, und ich habe in ein bisschen ein Haken geraten: Ich kann nicht scheinen, um Perspektive-korrekte Textur Mapping zu arbeiten.Perspektive korrekte Textur Mapping; Z-Abstand Berechnung könnte falsch sein

Mein Algorithmus ist zuerst die Koordinaten zu sortieren, um durch y zu plotten. Dies gibt einen höchsten, niedrigsten und Mittelpunkt zurück. Ich gehe dann über die Abtastlinien des Deltas mit:

// ordering by y is put here 

order[0] = &a_Triangle.p[v_order[0]]; 
order[1] = &a_Triangle.p[v_order[1]]; 
order[2] = &a_Triangle.p[v_order[2]]; 

float height1, height2, height3; 

height1 = (float)((int)(order[2]->y + 1) - (int)(order[0]->y)); 
height2 = (float)((int)(order[1]->y + 1) - (int)(order[0]->y)); 
height3 = (float)((int)(order[2]->y + 1) - (int)(order[1]->y)); 

// x 

float x_start, x_end; 
float x[3]; 
float x_delta[3]; 

x_delta[0] = (order[2]->x - order[0]->x)/height1; 
x_delta[1] = (order[1]->x - order[0]->x)/height2; 
x_delta[2] = (order[2]->x - order[1]->x)/height3; 

x[0] = order[0]->x; 
x[1] = order[0]->x; 
x[2] = order[1]->x; 

Und dann machen wir order[0]->y-order[2]->y, die x_start und x_end durch eine Delta erhöht. Beim Rendern des oberen Teils sind die Delta-Werte x_delta[0] und x_delta[1]. Beim Rendern des unteren Teils sind die Delta-Werte x_delta[0] und x_delta[2]. Dann interpolieren wir linear zwischen x_start und x_end auf unserer Scanline. UV-Koordinaten werden auf die gleiche Weise interpoliert, geordnet nach y, beginnend am Anfang und Ende, auf die bei jedem Schritt Deltas angewendet werden.

Dies funktioniert gut, außer wenn ich versuche, Perspektive korrekte UV-Mapping zu tun. Der grundlegende Algorithmus besteht darin, für jeden Knoten UV/z und 1/z zu nehmen und zwischen ihnen zu interpolieren. Für jedes Pixel wird die UV-Koordinate UV_current * z_current. Dies ist jedoch das Ergebnis:

alt text

Der inversed Teil der Ihnen sagt, wo das Delta des gespiegelt werden. Wie Sie sehen können, scheinen die beiden Dreiecke zu verschiedenen Punkten im Horizont zu gehen.

Hier ist, was ich verwenden, um die Z an einem Punkt im Raum zu berechnen:

float GetZToPoint(Vec3 a_Point) 
{ 
    Vec3 projected = m_Rotation * (a_Point - m_Position); 

    // #define FOV_ANGLE 60.f 
    // static const float FOCAL_LENGTH = 1/tanf(_RadToDeg(FOV_ANGLE)/2); 
    // static const float DEPTH = HALFHEIGHT * FOCAL_LENGTH; 
    float zcamera = DEPTH/projected.z; 

    return zcamera; 
} 

Bin ich richtig, ist es ein z-Puffer Problem?

Antwort

4

ZBuffer hat nichts damit zu tun.

Der ZBuffer ist nur nützlich, wenn sich Dreiecke überlappen und Sie sicherstellen möchten, dass sie korrekt gezeichnet sind (z. B. richtig in Z geordnet). Der ZBuffer wird für jedes Pixel des Dreiecks bestimmen, ob ein zuvor plaziertes Pixel näher an der Kamera ist, und wenn dies der Fall ist, nicht das Pixel Ihres Dreiecks zeichnen.

Da Sie zwei Dreiecke zeichnen, die sich nicht überlappen, kann dies nicht das Problem sein.

Ich habe einmal einen Software-Rasterizer in Fixpunkt (für ein Mobiltelefon) gemacht, aber ich habe die Quellen nicht auf meinem Laptop. Also lass mich heute Nacht nachsehen, wie ich es gemacht habe. Im Wesentlichen ist das, was du hast, nicht schlecht! Eine Sache wie diese könnte durch einen sehr kleinen Fehler verursacht werden

Allgemeine Tipps in der Fehlersuche ist es, ein paar Test Dreiecke (Neigung links, Neigung rechts, 90 Grad Winkel, etc etc) und Schritt durch sie mit dem Debugger und sehen, wie Ihre Logik mit den Fällen befasst.

EDIT:

peudocode meiner Rasterizer (nur U, V und Z berücksichtigt ...Wenn Sie auch Gouraud machen wollen, müssen Sie auch alles für R G und B tun, was Sie für U und V und Z tun:

Die Idee ist, dass ein Dreieck in 2 Teile zerlegt werden kann. Der obere Teil und der untere Teil. Der obere Teil ist von y [0] bis y [1] und der untere Teil ist von y [1] bis y [2]. Für beide Mengen müssen Sie die Schrittvariablen berechnen, mit denen Sie interpolieren. Das folgende Beispiel zeigt Ihnen, wie Sie den oberen Teil machen. Bei Bedarf kann ich auch den unteren Teil liefern.

Bitte beachten Sie, dass ich bereits das benötigte Interpolations-Offsets für den unteren Teil des unter 'Pseudo-Code' Fragmente

  • erste Ordnung der Koordinaten (x, y, z, u, v) in der Reihenfolge berechnen so dass Koord [0] .y < Koord [1] .y < Koord [2] .y
  • Als nächstes prüfen, ob 2 Koordinatensätze identisch sind (nur x und y überprüfen). Wenn so nicht zeichnen
  • Ausnahme: Hat das Dreieck eine flache Oberseite? wenn ja, ist die erste Steigung unendlich
  • exception2: hat das Dreieck einen flachen Boden (ja Dreiecke können auch diese haben; ^)) dann wird auch die letzte Steigung unendlich sein
  • berechnen 2 Steigungen (linke Seite und rechte Seite)
    leftDeltaX = (x [1] - x [0])/(y [1] -y [0]) und rightDeltaX = (x [2] - x [0])/(y [2] -y [0])
  • der zweite Teil des Dreiecks berechnet wird abhängig von: wenn die linke Seite des Dreiecks nun wirklich auf dem linksseitigen ist (oder Bedürfnisse Swapping)

Codefragment:

if (leftDeltaX < rightDeltaX) 
{ 
     leftDeltaX2 = (x[2]-x[1])/(y[2]-y[1]) 
     rightDeltaX2 = rightDeltaX 
     leftDeltaU = (u[1]-u[0])/(y[1]-y[0]) //for texture mapping 
     leftDeltaU2 = (u[2]-u[1])/(y[2]-y[1]) 
     leftDeltaV = (v[1]-v[0])/(y[1]-y[0]) //for texture mapping 
     leftDeltaV2 = (v[2]-v[1])/(y[2]-y[1]) 
     leftDeltaZ = (z[1]-z[0])/(y[1]-y[0]) //for texture mapping 
     leftDeltaZ2 = (z[2]-z[1])/(y[2]-y[1]) 
} 
else 
{ 
     swap(leftDeltaX, rightDeltaX); 
     leftDeltaX2 = leftDeltaX; 
     rightDeltaX2 = (x[2]-x[1])/(y[2]-y[1]) 
     leftDeltaU = (u[2]-u[0])/(y[2]-y[0]) //for texture mapping 
     leftDeltaU2 = leftDeltaU 
     leftDeltaV = (v[2]-v[0])/(y[2]-y[0]) //for texture mapping 
     leftDeltaV2 = leftDeltaV 
     leftDeltaZ = (z[2]-z[0])/(y[2]-y[0]) //for texture mapping 
     leftDeltaZ2 = leftDeltaZ 
    } 
  • eingestellt den currentLeftX und currentRightX sowohl auf x [0]
  • Satz currentLeftU auf leftDeltaU, currentLeftV auf leftDeltaV und currentLeftZ auf leftDeltaZ
  • calc beginnen und Endpunkt für die ersten Y-Bereich: startY = ceil (y [0]); endY = ceil (y [1])
  • prestep x, u, v und z für den Bruchteil von y für Subpixelgenauigkeit (ich denke, das wird auch für Floats benötigt) Für meine Fixpoint - Algorithmen wurde dies benötigt, um die Linien und Texturen geben die Illusion, sich in viel feineren Schritten zu bewegen, als die Auflösung des Displays.
  • berechnen wo x sollte bei y [1] sein: halfwayX = (x [2] -x [0]) * (y [ 1] -y [0])/(y [2] -y [0]) + x [0] und das gleiche für U und V und z: halbautyU = (u [2] -u [0]) * (y [1] -y [0])/(y [2] -y [0]) + u [0]
  • und mit dem halfwayX berechnen Sie den Stepper für die U und V und z: if (halfwayX - x [1] == 0) {SteigungU = 0, SteigungV = 0, SteigungZ = 0} else {SteigungU = (halbherzigU - U [1])/(halbherzigX - x [1])}// (und dasselbe für v und z)
  • schneiden Sie für das Y oben (also berechnen, wo wir beginnen zu zeichnen, falls die Spitze des Dreiecks außerhalb des Bildschirms (oder aus dem Clipping-Rechteck))
  • ist für y = startY; y < endY; y ++) {
    • ist Y hinter dem Bildschirm? hör auf zu rendern!
    • calc startX und endX für die erste horizontale Zeile leftCurX = ceil (startx); leftCurY = ceil (endy);
    • die Leitungsklammer nach links horizontalen Rand des Bildschirms zu ziehen (oder Clipping-Bereich)
    • einen Zeiger auf den Zielpuffer vorbereitet (es durch Array-Indizes tut jedes Mal zu langsam) unsigned int buf = destbuf + (y Tonhöhe) + startX; (Unsigned int, falls Sie tun 24bit oder 32 Bit-Rendering) auch hier Ihre ZBuffer Zeiger vorbereiten (wenn Sie diese verwenden)
    • für (x = startX; x < ENDX; x ++) {
      • jetzt Perspektive Textur-Mapping (ohne Verwendung eines bilineair Interpolation Sie folgendes tun):

Codefragment:

  float tv = startV/startZ 
     float tu = startU/startZ; 
     tv %= texturePitch; //make sure the texture coordinates stay on the texture if they are too wide/high 
     tu %= texturePitch; //I'm assuming square textures here. With fixed point you could have used &= 
     unsigned int *textPtr = textureBuf+tu + (tv*texturePitch); //in case of fixedpoints one could have shifted the tv. Now we have to multiply everytime. 
     int destColTm = *(textPtr); //this is the color (if we only use texture mapping) we'll be needing for the pixel 
  • Blindleitung
    • Blindleitung
      • Blindleitung
      • optional: Zeigt die ZBuffer wenn die zuvor aufgetragen Pixels an dieser Koordinate höher oder niedriger ist dieses Modell dann.
      • das Pixel pixelieren
      • startZ + = slopeZ; startU + = SteigungU; StartV + = SteigungV; // Aktualisieren aller Interpolatoren
    • } x Ende loop
    • leftCurX + = leftDeltaX; rightCurX + = rightDeltaX; leftCurU + = rightDeltaU; linksCurV + = rechtsDeltaV; leftCurZ + = rightDeltaZ; // Update Y Interpolatoren
  • } Ende y loop

    // dies ist das Ende des ersten Teils. Wir haben jetzt das halbe Dreieck gezeichnet. von oben bis zur mittleren Y-Koordinate. // wir jetzt im Grunde tun genau die gleiche Sache, aber jetzt für die untere Hälfte des Dreiecks (der andere Satz von Interpolatoren verwendet)

traurig über den ‚Dummy-Linien‘ .. sie waren nötig, das bekommen Markdown-Codes synchron. (dauerte eine Weile, bis alles wie geplant aussah)

lassen Sie mich wissen, wenn dies Ihnen hilft, das Problem zu lösen, mit dem Sie konfrontiert sind!

+0

andere Spitze für das Debugging, berechnet, jeden Punkt die Textur altmodischen, nicht-inkrementellen U und V unter Verwendung von für jeden Bildpunkt-Koordinaten und durch Ihren Algorithmus (unter Berücksichtigung einiger Slop wegen Rundungsfehlern), berechnet auf die inkrementellen Werte vergleichen. –

+1

Wow ... das ist eine Antwort und eine Hälfte! : D – Goz

+0

goz: hoffen jetzt nur nützlich es ist, da er wahrscheinlich durch das Bild zu urteilen die meisten dieser Code bereits vorhanden ist. Ich denke, dass er wahrscheinlich einen der Interpolationswerte falsch berechnet hat. Aber hoffentlich, wenn er neben mir seinen Algorithmus legt kann er herausfinden, welches es ist; ^) – Toad

0

Ich weiß nicht, dass ich mit Ihrer Frage helfen kann, aber eines der besten Bücher über Software-Rendering, die ich zu der Zeit gelesen hatte, ist online Graphics Programming Black Book von Michael Abrash.

0

Wenn Sie 1/z interpolieren, müssen Sie UV/z mit z, nicht 1/z multiplizieren.Angenommen, Sie haben dies:

UV = UV_current * z_current

und z_current1/z interpoliert, sollten Sie es ändern:

UV = UV_current/z_current

Und dann könnten Sie z_current so etwas wie one_over_z_current umbenennen möchten.

+0

Es ist wahrscheinlich eine Geschwindigkeit von bis Trick: Er muss von z teilen. Aber da Multiplikation schneller ist als Division, multipliziert er mit 1/z. – Toad

+0

Sie können eine Teilung mit perspektivisch korrekter Texturzuordnung nicht vermeiden. Sie können es über einen Spam von Pixeln approximieren, aber Sie können es nicht beseitigen. Angenommen, Sie möchten etwas, das korrekt aussieht. – MSN

Verwandte Themen