DirectX Geometryshader: Punktgröße
-
Hallo liebe Freunde der Shaderprogrammierung. Ich möchte gerne 3D-Punkte zeichnen, wo man die Punktgröße einstellen kann. Meine momentane Idee: Ich erzeuge im Geometry-Shader einfach ein paar zusätzliche Pixel und male sie neben den Ursprungspixel. Hier mein bisheriger Entwurf:
[maxvertexcount(100)] //So viele Vertexe kommen maximal aus dem Geometry-Shader herraus
void GShaderSimpleDrawPoint( point VS_OUT_Simple input[1], inout PointStream<PS_IN_Simple> OutputStream )
{
PS_IN_Simple output = (PS_IN_Simple)0;for( int x=-PointWidth; x <= PointWidth; x++ )
for( int y=-PointWidth; y <= PointWidth; y++ )
{
output.pos = mul(float4(input[0].pos.xyz, 1.0), WorldViewProj) + float4(x,y,0,0) / 10;
OutputStream.Append( output );
OutputStream.RestartStrip(); //Wird nach jeder erzeugten Linie/Punkt/Dreieck aufgerufen
}
}Das Problem an der Lösung: Ich dachte, wenn ich ein Vektor mit der ModelviewMatrix der Linie, der Modelviewmatrix der Kamera, und der Projektionsmatrix multipliziere, dass ich dann ein Clipspacevektor erhalte. D.h., wenn ich nun bei x und y Plus/Minus 1 rechne, dann verschiebt sich dieser Punkt auf den Bildschirm um ein Pixel. Das komische ist,der Springt bei mir dann gleich 10 Pixel oder so zur Seite. Deswegen habe ich bei
output.pos = mul(float4(input[0].pos.xyz, 1.0), WorldViewProj) + float4(x,y,0,0) / 10;
Durch 10 geschrieben. Warum muss ich durch 10 teilen? Was ist an meinen Entwurf falsch? Wie muss ich es machen, damit
mul(float4(input[0].pos.xyz, 1.0), WorldViewProj) + float4(1,0,0,0);
Den Pixel nur um eins nach rechts verschiebt?
-
Ok ich sehe schon die Frage ist nicht einfach. Hier eine weitere Frage, um überhaupt ein Verständniss zur Umrechnung 3D-Punktkoordinaten in 2D-Fensterkoordinaten zu bekommen.
Die Formel, um bei OpenGL von Objektkoordinaten in Fensterkoordinaten zu transformieren sieht so aus:
/Objektkoordinaten->[MV-Matrix des Objektes]->Weltkoordinaten->[MV-Matrix der Kamera]->Eye-Koordinaten->[Projektionsmatrix]->ClipKoordinaten->[PerspektiveDevision] //->normalized devise Koordinates->[Viewporttransformation]->WindowKoordinates private Vektor TransformVektorToWindowKoordinates(Vektor obj, out bool pointIsInScreen) { Vektor window = new Vektor(0, 0, 0); float[] ve = new float[] { obj.x, obj.y, obj.z, 1 }; Matrix.VMatMult(modelviewMatrix, ve); // umrechnen von Objektkoordinaten in Eye-Koodinaten float f = ve[2]; Matrix.VMatMult(projectionMatrix, ve); // umrechnen von Eye-Koodinaten in Clip Koordinaten ve[0] /= ve[3]; // umrechnen von Clip Koordinaten in Normalisierte Clip Koordinaten ve[1] /= ve[3]; ve[2] /= ve[3]; float wideHalf = buffer.Length / 2; //umrechnen in Fensterkoordinaten float highHalf = buffer[0].Length / 2; window.x = wideHalf * (ve[0] + 1); window.y = buffer[0].Length - highHalf * (ve[1] + 1); window.z = (ve[2] + 1) / 2; int d = 100; // Um so viel darf punkt außerhalb von Bildschirm liegen pointIsInScreen = window.x >= 0 - d && window.y >= 0 - d && window.x <= buffer.Length + d && window.y <= buffer[0].Length + d && f < 0; return window; }
Nach der Berechnung des Vektors von Objekt in Clippkordinaten erfolgt noch die Perspektivdivision und die Viewporttransformation. Wer macht diese beiden letzten Schritte bei DirectX? Ich selbst schreibe im Vertexshader ja immer nur das:
float4 newPos = mul(float4(input[0].pos.xyz, 1.0), WorldViewProj);
und speichere das Ergebniss in einer SV_POSITION-Variable. Was passiert dann mit dieser Variable?
-
Ok ich weiß nun, wie man ein dicken Pixel in DirectX malt. Man muss die Normalisierung der Clippkoordinaten einfach selber durchführen. Dann entspricht ein Pixel 2 / Fensterbreite oder 2 / Fensterhöhe.
[maxvertexcount(100)] //So viele Vertexe kommen maximal aus dem Geometry-Shader herraus
void GShaderSimpleDrawPoint( point VS_OUT_Simple input[1], inout PointStream<PS_IN_Simple> OutputStream )
{
PS_IN_Simple output = (PS_IN_Simple)0;float4 newPos = mul(float4(input[0].pos.xyz, 1.0), WorldViewProj);
newPos /= newPos.w;
float ex = (2.0f / WindowWidth);
float ey = (2.0f / WindowHeight);for( int x=-PointWidth / 2; x <= PointWidth / 2; x++ )
for( int y=-PointWidth / 2; y <= PointWidth / 2; y++ )
{
output.pos = newPos + float4(x * ex,y * ey,0,0) ;
OutputStream.Append( output );
OutputStream.RestartStrip(); //Wird nach jeder erzeugten Linie/Punkt/Dreieck aufgerufen
}
}
-
Wieso nicht ein großes Quadrat malen, anstatt viele kleine?
-
Selbst um ein Quadrat zu malen muss man wissen, wie groß ein Pixel in Clippspacekoordinaten ist. Die Quadratidee baut also auf den Wissen von mehrere Pixel nebeneinander auf.
Ich werde das Quadrat für Linien und Punkte aber wirklich so nehmen, da das vielleicht Effektiver ist.
-
Moment, du willst Linien malen, indem du für jeden Pixel per Geometry Shader ein Quadrat erzeugst? Wieso!? Die Grafikkarte kann doch selbst Linien malen? Viel ineffizienter als das geht's wohl kaum...
-
Auch dicke Linien? Wäre es sinnvoller 10 Linien nebeneinander zu zeichnen oder ein Quadrat zu nehmen, um eine dicke Linie zu bekommen?
-
Es wäre wesentlich sinnvoller, ein einzelnes Quadrat zu malen, als hunderte winzige Quadrate...