PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : OpenGL Multipass


Kant
2003-02-19, 05:36:36
Hallo,

Ich habe die letzten Wochen an einer netten kleinen OpenGL-Grafik Engine gebastelt. Das ganze ist jetzt auch soweit Feature-komplett (DOT3 Normal-mapping, specular-mapping, Stencilshadows (Carmacks Reverse, finite, closed), 3ds-importer). Nun, da die Funktionen alle soweit komplett und funktional sind, habe ich angefangen alternative Render-Pfade zu bauen. Aber dabei bin ich in ein paar Probleme gelaufen.

Blending :
Bei dem bisherigen Pfad (NV2x) generiere ich die gesamte Beleuchtung in einem Pass. Wenn multiple Lichter vorhanden sind, rendere ich für jedes Licht einen Pass, und addiere die Farbwerte im FrameBuffer. Auch multiple Lichter die Schatten werfen, sind kein Problem, diese werden dann mit 3 Passes gerendert. (2xstencil+1xlight).
Bei der Umsetzung auf den NV1x Pfad musste ich die Beleuchtung aber auf 3 eigene Passes zerlegen. Die ersten beiden Passes (Decal-Textur, Diffuse-Bump) moduliere ich im Frame-Buffer zusammen, und das specular wird im 3. Pass geaddet. Funktioniert auch soweit ganz gut... bei einem Licht :(
Wenn ich aber jetzt mehrere Lichtquellen habe, funktioniert das so nicht mehr, weil der Framebuffer mit dem "alten" Pixel bereits belegt ist, und ich ihn nicht mehr zum modulieren verwenden kann. Ich bräuchte also noch einen weiteren Puffer, in dem ich das Ergebniss des 1. Passes zwischenspeichern kann.. Nur welcher ? Oder habe ich einen generellen Denk-Fehler ?

Specular :
Parallel habe ich auch angefangen, einen Opengl-core-pfad zu erstellen. Ich vermutete eigentlich das ich mit den neuen dot3 bzw cube-map Erweiterungen in OGL 1.3/1.4 alle Mittel zur Hand hätte, die benötigt würden. Allerdings funktioniert mit den Standard-GL-Funktionen zZ nur Diffuse-Bump-Mapping, da ich noch keine Möglichkeit gefunden habe, einen specularen Exponent umzusetzen (bei den NV Pfaden multipliziere ich das einfach ein bisschen in den Register-Combinern mit sich selbst. ^16 auf NV2x ^8 auf NV1x). Aber wie setze ich das auf GL-Core um ? Zudem taucht hier natürlich auf wieder das Blending Prob auf...

Wenn jemand nen Tipp zu den Problemem hätte, wäre das klasse.

zeckensack
2003-02-19, 12:38:04
Kannst du dein 'diffuse bump' nicht auf einen Graustufenwert reduzieren, und im Alpha-Kanal (Destination Alpha) parken?

edit:
////////////////////////
//Pass1 - 'diffuse bump'
glColorMask(false,false,false,true); //allow only alpha updates
glDisable(GL_BLEND); //replace the alpha channel's old contents

//set combiners to produce diffuse bumped light intensity in the alpha channel
<...>
render_stuff();

///////////////////////
//Pass2 - base textures
glColorMask(true,true,true,true);
glBlendFunc(GL_DST_ALPHA,GL_ONE); //modulate with intensity and add to previous frame buffer contents
glEnable(GL_BLEND);

//setup combiners for decal texturing
<...>
render_stuff();

//////////////////
//Pass3 - specular
glBlendFunc(GL_ONE,GL_ONE); //accumulate
//setup combiners for specular
<...>
render_stuff();

Kant
2003-02-19, 12:58:22
Leider nein.
Im DST_Alpha leigt nach dem ersten(decal) Pass die Specular-Intensität.
Im 2.Pass (Diffuse-Bump) wird das dann nochmals verändert (self-shadowing).

Ich habe so die Befürchtung, das ich in einer Sackgasse stehe..
Werde wohl den ganzen Pfad noch mal neu strukturieren müssen... oder für GF1/2 das Bump-mapping schlicht streichen, und einfaches per-pixel-licht machen.

Xmas
2003-02-19, 13:38:11
Wieso bringst du eigentlich die Decal-Textur im ersten Pass rein?

Könntest du den geplanten Ablauf mal als mathematische Gleichung formulieren? So lässt sich vielleicht einfacher was optimieren.

Kant
2003-02-19, 17:16:36
Im Moment habe ich :
Pass 1 => fuellt RGBA Framebuffer (decal+specularmap)

glEnable(GL_BLEND);
glBlendFunc(GL_DST_COLOR,GL_ZERO);
glDepthFunc(GL_EQUAL);

Pass 2 => Dotproduct zwischen Normalmap und Cube-Map(Lichtvektor TS). Test auf self-shadowing. Wenn self-shadow, dann ist diffuse=ambient, und alpha im FB wird "aktualisiert". Wenn kein selfshadow dann normales ambient/diffuse shading.

glBlendFunc(GL_DST_ALPHA,GL_ONE);

Pass 3 => Dotproduct zwischen Normalmap und Cube-Map(HalfAngle TS). In den RCs durch wildes multiplizieren auf ^8 powern. Durch die Blendfunc greift self-shadowing + specularmap in DST_Alpha.

Selbst wenn ich Pass 1 und 2 vertausche, habe ich immer noch das Problem, das ich 2 Informationen transportieren muss.
Zum einen das self-shadowing/specularmap , zum anderen den "Farb"-Wert an sich.

Wie ich es auch drehe und wende...
Wenn ich auf self-shadow und specularmap verzichte, müßte es so gehen, wie es Zeckensack in seinem Post beschrieben hat. Aber es schmerzt mich wirklich, die specularmap ist ne feine Sache. Das self-shadowing ist eigentlich auch ne gute Sache, nicht alle meine Lichtquellen werfen schatten... und es sieht doof aus, auf der Rückseite eines Objectes speculare Highlights zu haben.

*seufz* Ich dachte nachdem ich mir meinen Weg durch die Tangent-Spaces erkämpft habe, und mit bösartigen GL-Matrizen kämpfen musste, wäre das schlimmste überstanden.

Aber noch gebe ich nicht auf ;)

btw, läuft eigentlich Doom3 mit vollem Effekt-Umfang auf GF1/GF2 ??

Xmas
2003-02-19, 17:47:53
Um ganz sicher zu gehen: du machst alle drei Passes pro Licht?

Kant
2003-02-19, 18:07:27
Originally posted by Xmas
Um ganz sicher zu gehen: du machst alle drei Passes pro Licht?
Yep, zumindest im NV1x Pfad.. auf NV2x ist es nur ein Pass. Je nach Licht noch 2 passes stencil (und dann die Licht-passes mit stencil-test)
Oder sollte es eine Möglichkeit geben das zu reduzieren , bzw Lichter zusammenzufassen ?
Ist mein erstes per-pixel-light Project, insofern will ich schwere Denk-Fehler meinerseits nicht ausschließen :)

Demirug
2003-02-19, 18:15:19
Das erinnert mich daran das ich Chips mit nur 2 Texturen pro Pass nicht mag.

Wenn ich mich richtig erinnere funktioniert das ganze wie folgt:

1. Pass : Alpha = Normalmap Dot Cube-Map(Lichtvektor TS)
Color = 0
Source*1 + Dest*1

2. Pass: Color = Decal
Alpha = 0
Source*1 + Dest*SourceAlpha

3. Pass Color = 1,1,1
Alpha = 0
Source*Dest + Dest*0

4. Pass: Alpha = Normalmap Dot Cube-Map(HalfAngle TS)
Color = 0;
Source*1 + Dest*1;

5. Pass: Color = specularmap
Alpha = 0
Source*1 + Dest*SourceAlpha

6. Pass Color = 1,1,1
Alpha = 0
Source*Dest + Dest*0

Doom III kann mit der GF1/2 angeblich alle Effekte aber nicht mit dem Default OpenGL Pfad.

Kant
2003-02-19, 19:00:25
6 Passes .....Puh..
Aber mit specular-map ;)

Hmm, ich könnte also das erste Licht mit cleanem FB mit 3 Passes rendern... und dann für die folgenden mit "dirty" FB den 6 Pass-Ansatz nehmen, um die alten RGB Infos nicht zu zerstören... dann müßte ich , wenn ich das richtig verstehe nur den 6.Pass ganz an den Anfang ziehen, um cleanes Alpha zu bekommen...
Für korrektes self-shadowing müsste ich dann noch einen Pass zwischen 3 und 4 einschieben, der den self-shadowing term in alpha ablegt dann in 4 mit DST_Alpha statt 1....

Aber erstmal vielen Dank, ich hatte schon nicht mehr geglaubt, das es überhaupt machbar ist.
Das gibt doch wieder Raum für Experimente .. :)

Und...
Originally posted by Demirug
Das erinnert mich daran das ich Chips mit nur 2 Texturen pro Pass nicht mag.

dem schließe ich mich voll an.

Xmas
2003-02-19, 19:17:25
Den Self-Shadowing Teil habe ich nicht so ganz verstanden. Was machst du da genau an Operationen?

Kant
2003-02-19, 20:05:44
Im Prinzip geht es darum, das eine Fläche, die dem Licht abgewandt ist, durch das Bump-mappen durchaus eine Normal bekommen kann, die dem Licht zugewandt ist. Das aüßert sich dann in einem Highlight, (oder auch diffusem Licht) an einer Stelle wo keines sein dürfte.

Da der Lichtvektor aber im TS angeben ist, liegt in dessen z-komponente praktisch die Richtung "die von der Oberfläche weg geht" (scheiss beschreibung). Also, wenn dieser Wert < 0 , ist es im Schatten.
Dann multipliziere ich diesen Wert noch mit 8, und er wird auf [0-1] geclampt. Dadurch wird der Übergang von Lich zu kein Licht weicher, es "poppt" nicht so plötzlich auf.

Also multipliziere ich am Ende vom Diffusen Pass das Ergebniss noch diesem Wert, und speichere ihn im Alpha, das der spec-pass ihn mitbenutzen kann.

Im Detail : Mit Textur 1 = CubeMap (Lichtvektor TS)
// Combiner 0 Alpha spare0a = 4*(1*Lz + 1*Lz)
glCombinerInputNV(GL_COMBINER0_NV,GL_ALPHA,GL_VARIABLE_A_NV,GL_ZERO,GL_UNSIGNED_ INVERT_NV,GL_ALPHA);
glCombinerInputNV(GL_COMBINER0_NV,GL_ALPHA,GL_VARIABLE_B_NV,GL_TEXTURE1,GL_EXPAN D_NORMAL_NV,GL_BLUE);
glCombinerInputNV(GL_COMBINER0_NV,GL_ALPHA,GL_VARIABLE_C_NV,GL_ZERO,GL_UNSIGNED_ INVERT_NV,GL_ALPHA);
glCombinerInputNV(GL_COMBINER0_NV,GL_ALPHA,GL_VARIABLE_D_NV,GL_TEXTURE1,GL_EXPAN D_NORMAL_NV,GL_BLUE);
glCombinerOutputNV(GL_COMBINER0_NV,GL_ALPHA,GL_DISCARD_NV,GL_DISCARD_NV,GL_SPARE 0_NV,GL_SCALE_BY_FOUR_NV,GL_NONE,GL_FALSE,GL_FALSE,GL_FALSE);


Hmm, klingt alles irgendwie sehr konfus...

Aber ich glaube es müßte stimmen ;)

Xmas
2003-02-19, 21:09:03
Herrje, das kann ja keiner lesen...
Wenn ich das richtig sehe ergibt sich daraus folgende Gleichung:

Farbe = Σ(i=1..k)[ Decal * (Normalmap · Cubemap(Li)) + (Specular * Li.z * 8) * (Normalmap · Cubemap(HAi))^8 ]

Erst mal fällt mir da auf dass du keine Lichtfarbe hast. Aber das ließe sich schnell einfügen:
Σ[ Decal * Colori * (Normalmap · Cubemap(Li)) + Colori * (Specular * Li.z * 8) * (Normalmap · Cubemap(HAi))^8 ]

Zerlegen kann man das auch in:
Decal * Σ[ Colori * (Normalmap · Cubemap(Li)) ] + Σ[ Colori * (Specular + Li.z * 8) * (Normalmap · Cubemap(HAi))^8 ]

Dann sollte doch diese Renderreihenfolge ausreichen:

glBlendFunc(GL_ONE, GL_ONE);
for( i = 1; i < NumLights; i++ )
{
// Rendern mit
// RGB = Lichtfarbe * (Normalmap · Cubemap(Li))
// A = 1
}
glBlendFunc(GL_DST_COLOR, GL_ZERO);
// Rendern mit
// RGB = Decal
// A = Specular * L1.z * 8

glBlendFunc(GL_DST_ALPHA, GL_ONE);
// Rendern mit
// RGB = Lichtfarbe * (Normalmap · Cubemap(HA1))
// A = 0

for( i = 2; i < NumLights; i++ )
{
glBlendFunc(GL_ONE, GL_ONE);
// Rendern mit
// RGB = 0
// A = Specular * Li.z * 8

glBlendFunc(GL_DST_ALPHA, GL_ONE);
// Rendern mit
// RGB = Lichtfarbe * (Normalmap · Cubemap(HAi))
// A = 0
}

Demirug
2003-02-19, 21:15:09
Xmas, wenn man Stencilschatten benutzt muss man leider ein Licht immer komplett am Stück durchrendern.

Xmas
2003-02-19, 21:19:36
:bonk:
Das wurde mir auch gerade klar.

Ach, wenn man doch nur den Stencil-Buffer bitweise ansteuern könnte...

Demirug
2003-02-19, 21:22:06
Originally posted by Xmas
:bonk:
Das wurde mir auch gerade klar.

Ach, wenn man doch nur den Stencil-Buffer bitweise ansteuern könnte...

Kann man ja. Aber dann ist bei einem 8 Bit Buffer bei 8 Lichtern schluss und eine Kyro schaft nur 4. Das würde eine wiederliche sortierei geben.

Edit: Jetzt bin ich mir aber nicht mehr so ganz sicher ob das auch bei OpenGL geht???

Xmas
2003-02-19, 23:12:52
Dann lassen wir mal die Lichtfarbe wieder weg und packen Specular in die Normalmap statt in die Decalmap. Dann lässt sich das ganze so machen:

for( i = 1; i <= NumLights; i++ )
{
// Stencil Pass hier

glBlendFunc( GL_ONE, GL_ONE );

// Texture0 = Normalmap (RGB) + Specular (A)
// Texture1 = CubeMap
//
//Combiner 0:
// spare0.a = texture0.a * Li.z * 8
// spare0.rgb = texture0.rgb · texure1.rgb
//Combiner 1:
// spare0.rgb = spare0.rgb * spare0.rgb
//Final Combiner:
// output.rgb = spare0.rgb * spare0.rgb * spare0.a
// output.a = 0

//glBlendFunc( GL_ONE, GL_ONE );

// Texture0 = Normalmap (RGB) + Specular (A)
// Texture1 = CubeMap
//
//Combiner 0:
// spare0.rgb = texture0.rgb · texure1.rgb
//Final Combiner:
// output.rgb = 0
// output.a = spare0.b
}
glBlendFunc( GL_DST_ALPHA, GL_ONE );

// Texture0 = Decal
//
//Final Combiner:
// output.rgb = texture0.rgb
// output.a = 0


Wie bekommst du eigentlich ^8 mit 2 General Combiners + Final Combiner hin? In dem Code oben geht nur ^4

Edit: Ups, noch ein Fehler drin. Es geht sogar nur ^2.

Xmas
2003-02-20, 02:58:54
Originally posted by Demirug
Kann man ja. Aber dann ist bei einem 8 Bit Buffer bei 8 Lichtern schluss und eine Kyro schaft nur 4. Das würde eine wiederliche sortierei geben.

Edit: Jetzt bin ich mir aber nicht mehr so ganz sicher ob das auch bei OpenGL geht???
Jap, es geht. Ich vergaß, dass es ja auch eine Stencil-Write-Mask gibt.
Allerdings ist das ja doch nicht ausreichend für 8 Lichter, bzw. nur unter bestimmten Voraussetzungen.

Kant
2003-02-20, 07:55:45
Morgen,

Also da sind einige gute Ansätze drin, ich bin bisher davor zurückgeschreckt, Lichter zusammenzufassen, weil ich dachte die Resultate wären dann falsch. Das mit den Stencil-Shadow stimmt zwar auch, aber ich kann ja erst die "non-shadow-lights" zusammenfassen, und danach die shadow-lights Stück für Stück angehen.
Übrigens habe ich LichtDiffuseCol und LichtAmbientCol schon drin gehabt , als Color-Constanten im Diffuse-Bump-Pass. (und self-shadowing betrifft auch diffuse-bump)

Den Wert in den Register-Combinern auf ^8 zu pushen geht zweistufig. (gelobt sei die EF-Einheit)
Im RC1 erst mal spare0*spare0, das wieder in spare0. Dann im FinalCombiner mit :

A=E_Times_F (effektiv ^4)
B=E_Times_F (effektiv ^4)
C=0
D=0
E=spare0 (effektiv ^2)
F=spare0 (effektiv ^2)

festverdrahtet => AB+(1-A)C+D


Werde dann mal anfangen, den NV1x Pass aufzubohren :)

Demirug
2003-02-20, 07:59:03
Originally posted by Xmas

Jap, es geht. Ich vergaß, dass es ja auch eine Stencil-Write-Mask gibt.
Allerdings ist das ja doch nicht ausreichend für 8 Lichter, bzw. nur unter bestimmten Voraussetzungen.

Pro Licht reicht ein Bit. Allerdings nur wenn der Chip beim Increment und Decrement kein clamp macht sondern umläuft.

Xmas
2003-02-20, 12:11:59
Originally posted by Demirug
Pro Licht reicht ein Bit. Allerdings nur wenn der Chip beim Increment und Decrement kein clamp macht sondern umläuft.
Bei Carmacks Reverse reicht ein Bit nicht, weil man tatsächlich die Anzahl der Depth-fail Vorder- und Rückseiten zählen muss. Wenn ein Objekt genau in der Schnittmenge von zwei Schattenvolumen liegt, ist der Endwert +2, weil nur zwei Rückseiten den Depth-Test nicht passieren. Hättest du nur 1 Bit wäre das Objekt plötzlich im Licht.

Xmas
2003-02-20, 12:28:30
Originally posted by Kant
Morgen,

Also da sind einige gute Ansätze drin, ich bin bisher davor zurückgeschreckt, Lichter zusammenzufassen, weil ich dachte die Resultate wären dann falsch. Das mit den Stencil-Shadow stimmt zwar auch, aber ich kann ja erst die "non-shadow-lights" zusammenfassen, und danach die shadow-lights Stück für Stück angehen.
Übrigens habe ich LichtDiffuseCol und LichtAmbientCol schon drin gehabt , als Color-Constanten im Diffuse-Bump-Pass. (und self-shadowing betrifft auch diffuse-bump)

Den Wert in den Register-Combinern auf ^8 zu pushen geht zweistufig. (gelobt sei die EF-Einheit)
Im RC1 erst mal spare0*spare0, das wieder in spare0. Dann im FinalCombiner mit :

A=E_Times_F (effektiv ^4)
B=E_Times_F (effektiv ^4)
C=0
D=0
E=spare0 (effektiv ^2)
F=spare0 (effektiv ^2)

festverdrahtet => AB+(1-A)C+D


Werde dann mal anfangen, den NV1x Pass aufzubohren :)
Gut, ich habe mein Beispiel oben so angepasst dass es ^4 reflektiert. ^8 ist da nicht möglich, weil das Self-Shadowing noch reinmultipliziert werden muss.

Ich finde mein Beispiel ist ein sehr guter Tradeoff für NV10/15.
Erster Pass Ambient (in Alpha!) + Z
Pro Licht 2 Passes + Stencil
Letzter Pass Decal
Besser wird es kaum noch gehen. Dafür opferst du ein bisschen Qualität beim Specular(^4 statt ^8) und die farbigen Lichter.

Da mein Beispiel Diffuse in Alpha und Specular in RGB akkumuliert, könntest du nur für Specular eine Farbe verwenden, Diffuse und Ambient müssten monochrom sein. Andersrum ist es natürlich sinnvoller, aber wenn du Diffuse in RGB und Specular in Alpha akkumulieren willst, brauchst du einen weiteren Pass am Ende, der ein weißes Polygon mit Dst_Alpha multipliziert.

aths
2003-02-22, 03:22:06
Ich möchte mich da jetzt nicht in eure Fach-Diskussion reindrängeln, von der ich nix versteh. Aber sollte ambient light bzw. diffuse light nicht auf jeden Fall farbig sein können, während specular highlights, die ja "nur" Glanz bringen, durchaus monochrom sein könnten?

Demirug
2003-02-22, 08:46:22
Originally posted by aths
Ich möchte mich da jetzt nicht in eure Fach-Diskussion reindrängeln, von der ich nix versteh. Aber sollte ambient light bzw. diffuse light nicht auf jeden Fall farbig sein können, während specular highlights, die ja "nur" Glanz bringen, durchaus monochrom sein könnten?

Ja aths das ist so richtig. aber wenn es um die Performances geht muss man halt manchmal kompromisse mache.

Xmas
2003-02-22, 10:10:49
Originally posted by aths
Ich möchte mich da jetzt nicht in eure Fach-Diskussion reindrängeln, von der ich nix versteh. Aber sollte ambient light bzw. diffuse light nicht auf jeden Fall farbig sein können, während specular highlights, die ja "nur" Glanz bringen, durchaus monochrom sein könnten?
Deswegen ja der letzte Absatz in meinem Posting. Leider kostet eben das "Umkehren" von Diffuse und Specular einen weiteren Pass. Und so ganz korrekt sind monochrome Highlights auch nicht.

Kant
2003-02-22, 15:11:56
Nach einigem rumprobieren mit den verschiedenen Möglichkeiten werde ich wohl 2 verschiedene NV1x Pfade machen. Einen Performance-Pfad, und einen der die Berechnung des NV2x Pfades weitgehend annähert.

Nach den ersten Tests finde ich es allerdings erstaunlich, das es trotz der vielen Passes noch CPU-Limitiert ist.. bei den NV1x Pfaden geht nach Angaben des Profilers mehr als 50% CPU-Leistung für die "Licht-Vorberechnungen", also das ermitteln von LightVector, und HalfAngle drauf. (Und natürlich das Umrechnen in TS). Das erledige ich bei NV2x größtenteils mit Vertex-Programmen.

Achill
2003-02-22, 16:28:34
ich hätte auch eine kleine frage, gibt es denn irgendwo im netzt ein paar seiten (möglichst auf deutsch, geht so schneller) die mir die grundlagen schnell und effektiv erklären, damit ich eure diskution besser verstehen kann?

aths
2003-02-22, 22:34:16
Originally posted by Demirug
Ja aths das ist so richtig. aber wenn es um die Performances geht muss man halt manchmal kompromisse mache. ... und dann mal eben auf farbiges Licht verzichten??