Vielen Dank für die Antworten!
Schrompf hat geschrieben:Du schreibst aktuell wahrscheinlich 2x3 Vertizes pro Sprite in einen VertexBuffer und renderst den dann. Das ist für Dein Spiel wahrscheinlich auch völlig ausreichend. Andere Szenarien wollen dann aber irgendwann mal rotieren, skalieren, verfärben, ein- und ausblenden und wasweißichnoch. Dann lohnt es sich, die finalen Vertizes erst auf der Grafikkarte auszurechnen, und nur ein Platzhalter-Set an Vertizes hochzuladen, in denen alle Parameter des Sprites drinstehen. Dann hast Du in den Shadern sowieso alle Daten da, und kannst daraus auch die Skalierung im VertexShader ermitteln.
Ich benutze aktuell folgendes:
Code: Alles auswählen
const UInt32 D3DFVF_CUSTOM_2D = Direct3D9::D3DFVF_XYZRHW | Direct3D9::D3DFVF_DIFFUSE | Direct3D9::D3DFVF_TEX1;
struct StructCustomVertex2D
{
Float32 x; // Bildschirm-X
Float32 y; // Bildschirm-Y
Float32 z; // immer 0
Float32 rhw; // immer 1
UInt32 color; // Farbe und Transparenz
Float32 u; // Texture-X (0 bis 1)
Float32 v; // Texture-Y (0 bis 1)
};
Rotation und Skalierung berechne ich selber.
Schrompf hat geschrieben:Ich löse das aktuell mittels Instancing. Der primäre VertexBuffer und IndexBuffer enthält genau zwei Dreiecke mit (0,0), (1,0), (0,1) und (1,1). Kannst Du auch aus der VertexID im VertexShader live berechnen. Pro Instanz gebe ich dann einen Satz Parameter mit:
Instancing? Sowas:
http://www.rastertek.com/dx11tut37.html
Da wird ein "shader-constant buffer" erzeugt (ID3D11Device::CreateBuffer).
Unter D3D9 müsste ich dazu dann SetVertexShaderConstantF usw. nutzen.
Schrompf hat geschrieben:Das ist natürlich nur ein Vorschlag. Du schleifst da durch, was auch immer Du an Parametern brauchst, und verwurstest die erst im VertexShader zu tatsächlichen Bildschirmpositionen. Und zu beliebigen Parametern, die Du Dir zum PixelShader durchreichst. Lass Dich nicht von den Oldschool-Bezeichnern wie NORMAL oder COLOR0 oder sowas beeindrucken. Das sind nur Namen für Menschen. Du kannst einfach alles als TEXTURE0 bis TEXTURE7 bezeichnen. Nur ein einziges POSITION0 muss Dein VertexShader haben, weil das einer der letzten noch nicht frei programmierbaren Teile der GPU braucht, nämlich der Rasterizer. Alles andere sind beliebig nutzbare Vektoren, und Du solltest sie so auch nutzen.
Ich könnte also z.B. folgendes machen:
Code: Alles auswählen
const UInt32 D3DFVF_CUSTOM_2D = Direct3D9::D3DFVF_XYZ | Direct3D9::D3DFVF_DIFFUSE | Direct3D9::D3DFVF_TEX1 | Direct3D9::D3DFVF_TEX2 | Direct3D9::D3DFVF_TEX3;
struct StructCustomVertex2D
{
Float32 x;
Float32 y;
Float32 z;
UInt32 color;
Float32 u;
Float32 v;
Float32 u2;
Float32 v2;
Float32 u3;
Float32 v3;
};
u2, v2, u3 und v3 kann ich dann mit was auch immer vollklatschen... unendliche Möglichkeiten...
Schrompf hat geschrieben:Wenn Du Instancing nicht haben willst, kannst Du auch die Sprite-Daten auch in jeden Vertex kopieren. Du hast dann halt pro Sprite vier dicke Vertizes (0, 0, <Instanzdaten>), (1, 0, <Instanzdaten), ... - aber egal, funktioniert auch, verbraucht nur minimal mehr Speicher. Später kannst Du dann die VertexPosition aus der VertexID berechnen, die Dreiecke erst im GeometryShader erzeugen oder wasweißich. Da kann man kreativ werden. Ist aber alles erst später wichtig, zuerst muss man mal artefakt-frei rotieren und skalieren können.
Das "Instancing" macht man doch nur, um bestimmte Daten nicht jedem Vertex mitgeben zu müssen? Ich arbeite gerade ohne IndexBuffer, muss also pro Sprite 6 Vertices an D3D9 übergeben (mit IndexBuffer wären es nur 4). Ich übergebe also Informationen wie z.B. Rotation, Texturgesamtgröße, usw. entweder pro Sprite einmalig mit "Instancing" oder 6 mal in den Vertexdaten. Der Speicherverbrauch sollte egal sein, am Ende wird wohl die Performance entscheiden, was von beiden Varianten sinnvoller ist.
Krishty hat geschrieben:Off-Topic: Kompilierte Shader sind auch schneller zu entwickeln. Speicher sie mit der Endung .hlsl und füge sie deinem Visual C++-Projekt hinzu. Dann werden sie beim Kompilieren des Projekts von Visual Studio mitkompiliert und liegen als fertige .dxbc-Dateien vor. Falls es dir zu aufwändig ist, die Dateien zu laden, kannst du in den Projekteinstellungen Header mit dem fertigen Bytecode als Array erzeugen lassen, die du im Projekt #includeieren und direkt an CreateVertexShader() & Co. übergeben kannst. Mit D3DX musst du überhaupt nichts zu tun haben (deshalb ist auch die Dokumentation so schwach).
Vielen Dank für den Hinweis!
Zudomon hat geschrieben:Und zwar ist bei den Texturkoordinaten noch zu beachten, dass du nicht einfach mit der Texturbreite multiplizierst. Wenn du z.B. eine 1024 Textur hast, dann sind die adressierbaren Pixel von 0 - 1023. Benutzt du einen Texturatlas und unterteilst deine 1024 Textur in 8 Blöcke pro Zeile von je 128 Pixel, dann muss die Texturkoordinate mit 127 multipliziert werden. Also das ganze muss natürlich auch wieder zurück skaliert werden, also so:
Texturkoordinate = Texturkoordinate * (Grafikbreite - 1) / Grafikbreite
Das verstehe ich nicht ganz, vor allem die letzte Formel.
Bei mir liegen mehrere Sprites mit unterschiedlicher Größe auf einer Textur:
- 0206.png (10.59 KiB) 11727 mal betrachtet
Die Texturkoordinaten (0 bis 1) ermittel ich einfach durch das Teilen der Spritekoordinaten durch die Texturbreite -und Höhe.
Beispiel:
Code: Alles auswählen
Texture.SizeX = 256;
Texture.SizeY = 128;
Sprite.X1 = 123;
Sprite.Y1 = 33;
Sprite.X2 = 146;
Sprite.Y2 = 74;
TexCoord.X1 = 123 / 256 = 0.4805;
TexCoord.Y1 = 33 / 128 = 0.2578;
TexCoord.X2 = 146 / 256 = 0.5703;
TexCoord.Y2 = 74 / 128 = 0.5781;
Zudomon hat geschrieben:Soweit ich weiß fällt übrigens diese 0.5 Texelverschiebung bei Pointsampling weg. Also da muss man auch genau drauf achten, ob man Point Filtert oder Linear. Bei mir verwende ich Pointsampling, um auch nur die kleinsten Rundungsfehler nicht als lineare Interpolation der Daten zu verschandeln.
Mmmmh... Ich benutze auch den Pointfilter (D3DTEXF_POINT), wenn ich aber bei den Ziel/Screenkoordinaten nicht 0.5f abziehe, sieht das bei mir so aus:
Zudomon hat geschrieben:MipMaps sind dann nochmal eine Stufe härter. Denn dieses multiplizieren mit der ((Grafikbreite - 1) / Grafikbreite) der Texturkoordinaten pflanzt sich natürlich auf den Mipmapstufen fort. Da muss man dann schauen, ob es zu sichtbaren Fehlern kommt.
Mip Mapping schon ausprobiert, da zerhaut es alles, sobald er nicht mehr die "erste/0" Textur nimmt. Das werde ich angehen, wenn ich die Shader (Vertex/Pixel) verstanden und umgesetzt habe.
So, vernünftige Infos zu HLSL sind wohl wirklich selten im Netz. Die Doku finde ich grausam schlecht.
Die Koordinaten, die ich im Vertex Shader zurückgeben muss, erscheinen auf den ersten Blick auch recht seltsam.
Die Bildschirm-Mitte scheint 0.0, 0.0 zu sein,
oben links ist -1.0, 1.0
und unten rechts ist 1.0, -1.0 ...
Es hat euch etwas gedauert, bis ich auf die Idee gekommen bin, die TEXCOORD0 im Vertex Shader durchzureichen, damit ich was von den Texturen sehe. Bis jetzt ist mir noch nichts begegnet, was sich so mühsam recherchieren lässt, wie Shader... narf?!
Ich habe jetzt erstmal zum Testen und zum verstehen einen Vertex Shader gebastelt, mit dem ich kein Direct3D9::D3DFVF_XYZRHW mehr brauche:
Code: Alles auswählen
struct VS_INPUT
{
float4 Position : POSITION;
float4 Color : COLOR;
float4 TexCoord0 : TEXCOORD0;
};
struct VS_OUTPUT
{
float4 Position : POSITION;
float4 Color : COLOR;
float4 TexCoord0 : TEXCOORD0;
};
VS_OUTPUT VertexShader(VS_INPUT Parameter)
{
VS_OUTPUT Result;
Result.Color = Parameter.Color;
Result.Position[0] = Parameter.Position[0] / 640;
Result.Position[1] = (720 - Parameter.Position[1]) / 360;
Result.Position[2] = 0;
Result.Position[3] = 1;
Result.Position[0] -= 1;
Result.Position[1] -= 1;
Result.TexCoord0 = Parameter.TexCoord0;
return Result;
}
... und ja, sobald die Auflösung nicht 1280x720 entspricht, funktioniert der nicht mehr. Hier muss ich irgendwie die Größe vom Rendertarget verfügbar machen.
Und noch ein Meisterwerk der Shaderkunst! ;-)
Code: Alles auswählen
struct PS_INPUT
{
float4 TexCoord0 : TEXCOORD0;
float4 Color : COLOR;
};
struct PS_OUTPUT
{
float4 Color : COLOR;
};
sampler2D samp;
PS_OUTPUT PixelShader(PS_INPUT Parameter)
{
PS_OUTPUT Result;
Result.Color = tex2D(samp, Parameter.TexCoord0) * Parameter.Color;
return Result;
}
Ich benutze zum Zeichnen/Rendern von Flächen (FillRect) und Rahmen (FrameRect) IDirect3DDevice9::DrawPrimitive. Aktuell wird dazu einfach nur die Textur entfernt, SetTexture(Null). Mein Pixel Shader macht da jetzt Unsinn und versucht immer eine Textur zu samplen, wo dann anscheinend, wenn keine Textur gesetzt ist, immer 0 bei rauskommt. Die beste Lösung ist vermutlich, für diese Fälle einen gesonderten, bzw. gar keinen Pixel Shader zu nutzen. Gibt es aber eine Möglichkeit im Pixel Shader festzustellen, ob überhaupt eine Textur gesetzt ist?