JLI Spieleprogrammierung Foren-Übersicht JLI Spieleprogrammierung

 
 FAQFAQ   SuchenSuchen   MitgliederlisteMitgliederliste   BenutzergruppenBenutzergruppen 
 medals.php?sid=317edafd07a69955de55d993ffd9a042Medaillen   RegistrierenRegistrieren   ProfilProfil   Einloggen, um private Nachrichten zu lesenEinloggen, um private Nachrichten zu lesen   LoginLogin 

Shadersystem

 
Neues Thema eröffnen   Neue Antwort erstellen    JLI Spieleprogrammierung Foren-Übersicht -> DirectX, OpenGL
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen  
Autor Nachricht
Fallen
JLI MVP
JLI MVP


Alter: 40
Anmeldedatum: 08.03.2003
Beiträge: 2860
Wohnort: Münster
Medaillen: 1 (mehr...)

BeitragVerfasst am: 01.01.2007, 01:51    Titel: Shadersystem Antworten mit Zitat

Hallöchen, ich habe gestern ein kleines Shadersystem gebastelt mit dem man Shader laden (aus Dateien), erstellen (aus Text) und benutzen kann.

Da ich aber vielbeschäftigt bin würde ich gerne von euch einige Verbesserungsvorschläge oder andere Kommentare erfahren.

Der DL Link ist hier zu finden

Einbinden sollte eigentlich recht einfach sein, einfach alle Dateien des Archives irgendwo hinkopieren (Verzeichnissstruktur sollte dabei natürlich beibehalten werden), dann die Dateien dem projekt hinzufügen und die erforderliche Lib dem Projekt hinzufügen (d3dx9.lib).

Die Hauptinclude Datei ist "Shas.h" und damit sollte auch schon alles nötige von der Shaderseite vorhanden sein.

Im Code sieht das ganze dann ungefähr so aus wenn man es benutzen möchte:

CPP:
// initialisieren
IShaderSystem* myShaderSystem = new ShaderSystem(myD3D9Device);
...
// deinitialisieren
delete myShaderSystem;


Damit erstellt man das Shadersystem, bisher hoffentlich recht einfach zu verstehen Smile

Shader lädt man dann recht einfach wie folgt:

CPP:
myShader = myShaderSystem->LoadShader("UniqueShaderName", "C:/Projekte/TollesProjekt/Simple.fx");


Diese shader lassen sich dann echt einfach auch verwenden um seine Objekte damit auszuschmücken, da sich wohl jedes RenderSystem unterscheidet habe stelle ich das hier mal recht vereinfacht dar, es ist jedem selbst überlassen wie er das ganze nun verwendet:

CPP:
unsigned int Passes = myShader->get_PassCount(); // Anzahl Passes sammeln
myShader->Begin(); // Benutzung des Shaders starten
for(int CurrentPass = 0; CurrentPass<Passes; ++CurrentPass)
{
   RenderPassReturnValue::Enumeration ReturnValue = RenderPassReturnValue::RenderAndCallAgain;
   do
   {
      ReturnValue = myShader->BeginPass(CurrentPass); // einen Pass aktivieren
      if (ReturnValue != RenderPassReturnValue::RenderAndCallNot)
         Mesh->Render(); // Modell rendern
   } while(ReturnValue == RenderPassReturnValue::RenderAndCallAgain);
   mySimpleShader->EndPass(); // den Pass wieder deaktivieren
}
myShader->End(); // Benutzung des Shaders wieder beenden


Shader haben leider die Angewohnheit diverse informationen aus der Anwendung zu benötigen, wie zum Beispiel die WorldViewProjection Matrix. Das Shadersystem ist dabei im aktuellen Zustand noch nicht in der Lage diese informationen zu liefern, doch dazu gibt es das ISemanticHandler interface, welches wenn man es in eine hübsche Klasse verpackt und an das ShaderSystem übergibt dieses Problem beseitigt.

Das ganze geschieht einfach so, als erstes erstellen wir uns die hübsche Klasse:

CPP:
class WorldViewProjectionSemanticHandler
   : public ISemanticHandler
{
public:
   WorldViewProjectionSemanticHandler()
      : Name("WorldViewProjection")
   {}

   virtual ~WorldViewProjectionSemanticHandler()
   {}

   virtual void OnHandle(IShader* inShader, IShaderParameter* inParameter)
   {
      D3DXMATRIXA16 matViewProject;
      D3DXMATRIXA16 matWorldViewProject;

      ::D3DXMatrixMultiply(
         &matViewProject,
         &viewMat,
         &projMat );
      ::D3DXMatrixMultiply(
         &matWorldViewProject,
         &worldMat,
         &matViewProject );
      inParameter->set_Matrix(matWorldViewProject);
   }
   virtual void OnShaderSystemRefresh(IShader* inShader)
   {}

   virtual const std::string& get_Name() const
   {
      return Name;
   }
private:
   std::string Name;
};


Folgende Dinge sind hierbei zu beachten:
get_Name() gibt den Semantic Namen zurück welchen der Handler benutzt um die entsprechend zu verändernde Shadervariable zu finden, im Shadercode müsste demnach folgende Zeile vorhanden sein:

Zitat:

float4x4 matWVP : WorldViewProjection;


Auch zu beachten ist, das bei meiner Beispielklasse die Matritzen für die Projektions, World und View Sachen als viewMat, projMat und worldMat bekannt sind, wie ihr das macht ist wie immer eure eigene Sache Smile

Der SemanticHandler wird dabei bei jeder Benutzung des Shaders angewendet, vorrausgesetzt man hat ihn vor der Erstellung des Shaders dem ShaderSystem zugewiesen, dies geschieht so:

CPP:
myShaderSystem->AddSemanticHandler(new WorldViewProjectionSemanticHandler());


Alle Handler welche direkt in das ShaderSystem eingetragen sind werden automatisch an alle neu erstellten Shader gegeben, möchte man allerdings das ein bestimmter Handler auch nur für bestimmte Shader zur verfügung steht so kann man das ganze direkt an den Shader geben:

CPP:
myShader->AddSemanticHandler(new WorldViewProjectionSemanticHandler());


Sollte der Handler keinerlei Wirkung haben (entsprechende Semantics nicht vorhanden) wird der Handler vom Shader automatisch entfernt.

Diese Semantics sind für alle gängigen Shadervariablentypen verfügbar:
Float, Int, Bool, Float2, Float3, Float4, Float4Array, Float3Array, Float2Array, FloatArray, IntArray, String, Bool, Texture1D, Texture2D, Texture3D, TextureCube, Matrix.

Die gleichen Typen sind für Shadervariablen allgemein benutzbar, man kann also auch auf Variablen im Shader zugreifen welche nicht per Semantics markiert wurden, das geht recht einfach über:

CPP:
IShaderParameter* myParameter = myShader->get_ParameterByName("ambientLight");


Sollte die Variable ambientLight vorhanden sein so wird der entsprechende IShaderParameter erzeugt (man muss sich keine Sorgen um deren Entsorgung machen, hach was fürn Wortspiel). Möchte man die Variable als bestimmten Typ haben so kann man diese Anweisung etwas spezifizieren:

CPP:
IShaderParameter* myParameter = myShader->get_ParameterByName("ambientLight", ShaderParameterType::Float4);


Sollte also die Variable vorhanden UND als float4 vorliegen bekommt man die entsprechende IShaderParameter Variable zurück.

Aktuell werden diese Variablen nicht ausgelesen sondern mit einem Standard Wert belegt (meist NULL), danach kann mit get und set Methoden der Wert gesetzt und wieder ausgelesen werden, die Shader werden diese Werte dann korrekt verarbeiten.

IShaderParameter und IShader haben die Möglichkeit euch darüber zu informieren ob sie einwandfrei funktionieren können, das geschieht recht einfach über die Methode isValid() welches true für alles ok zurück gibt und false für gar nichts ist ok. IShader kann noch passend dazu einen fehlerbeschreibungstext zurück geben per get_ShaderErrors() welcher unter anderem auch die Kompilierungsfehler des Shaders angibt, sollte es zu solchen gekommen sein.

Im Falle eines DeviceResets sollte dem ShaderSystem auch mitgeteilt werden das sonderbare Umstände eingetreten sind, das ShaderSystem wird dann alle ShaderParameter sowie Shader auffordern sich neu zu erstellen. Dies geht über:

CPP:
myShaderSystem->Refresh(myD3D9Device);


Auch kann diese Methode benutzt werden um geänderte Shader neu zu laden, IShader besitzt ebenfalls eine Öffentliche Methode um sich aufzufrischen, ShaderParameter welche mit diesem IShader in Verbindung standen werden anschliessend ebenfalls aktualisiert (die Verbindung zum Effekt wird aufrecht erhalten).

So das war eine etwas ausführlichere beschreibung zum ganzen System, aber ich hoffe es gefällt euch und ihr sagt mir was euch nicht gefällt oder was verbessert/erweitert werden könnte.

Die DX Tutorial Samples eignen sich zum experimentieren für all jene welche kein anderes framework zum testen haben Smile
_________________
"I have a Core2Quad at 3.2GHz, 4GB of RAM at 1066 and an Nvidia 8800 GTS 512 on Vista64 and this game runs like ass whereas everything else I own runs like melted butter over a smokin' hot 18 year old catholic schoolgirl's arse."


Zuletzt bearbeitet von Fallen am 02.01.2007, 19:56, insgesamt 2-mal bearbeitet
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden Website dieses Benutzers besuchen
Fallen
JLI MVP
JLI MVP


Alter: 40
Anmeldedatum: 08.03.2003
Beiträge: 2860
Wohnort: Münster
Medaillen: 1 (mehr...)

BeitragVerfasst am: 02.01.2007, 19:39    Titel: Antworten mit Zitat

Bump und eine neuere Version ist oben, mit einigen kleineren Verbesserungen (conbst std::string& return values zB) und mit einem neuen Callbacksystem welches man benutzen kann wenn man auf einen bestimmten pass wartet der gerendert werden soll.

Das ist zB nützlich wenn man bestimmte werte Übergeben möchte die erst bei einem bestimmten pass gebraucht werden, zb bei einem LichtPass:

CPP:
class DiffuseLightCallbackHandler
   : public ICallbackHandler
{
public:
   DiffuseLightCallbackHandler()
      : Name("DiffuseLightCallback")
      , LightNumber(0)
   {}

   virtual ~DiffuseLightCallbackHandler() {}

   virtual RenderPassReturnValue::Enumeration OnHandle(IShader* inShader, D3DXHANDLE Handle)
   {
      if (LightNumber>=LightList.size())
         RenderPassReturnValue::RenderAndCallNot;

      IShaderParameter* Parameter;
      Parameter = inShader->get_ParameterByName("DiffuseColor", ShaderParameter::get_Float4());
      if (Parameter)
         Parameter->set_Float4(LightList[LightNumber]->DiffuseColor);
      Parameter = inShader->get_ParameterByName("LightPosition", ShaderParameter::get_Float4());
      if (Parameter)
         Parameter->set_Float4(LightList[LightNumber]->Position);

      ++LightNumber;
      if (LightNumber>LightList.size())
      {
         LightNumber = 0;
         RenderPassReturnValue::RenderAndCallOnce;
      }

      return RenderPassReturnValue::RenderAndCallAgain;
   }

   virtual void OnShaderSystemRefresh(IShader* inShader) {}

   virtual const std::string& get_Name() const
   {
      return Name;
   }

private:
   std::string Name;
   int LightNumber;
};

...

myShaderSystem->AddCallbackHandler(new DiffuseLightCallbackHandler());


Dieser Callback wird immer dann aufgerufen wenn ein Pass eine bool Annotation namens DiffuseLightCallback mit dem Wert true beinhaltet:

Zitat:

pass Pass1
<
bool DiffuseLightCallback = true;
>

{
VertexShader = compile vs_1_1 ItMayBeLightVS();
PixelShader = compile ps_2_0 ItMayBeLightPS();
...
}


Auch neu hinzugekommen sind durch das Callbacksystem BeginPass return values:

RenderAndCallAgain bedeutet der Pass möchte ein erneutes mal gerendert werden.
RenderAndCallOnce bedeutet der Pass möchte ein mal gerendert werden.
RenderAndCallNot bedeutet der Pass möchte gar nicht gerendert werden.

Die Benutzung des ganzen kann man im Startpost nachlesen welches ich dementsprechend an den Änderungen angepasst habe (oder noch werde).
_________________
"I have a Core2Quad at 3.2GHz, 4GB of RAM at 1066 and an Nvidia 8800 GTS 512 on Vista64 and this game runs like ass whereas everything else I own runs like melted butter over a smokin' hot 18 year old catholic schoolgirl's arse."
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden Website dieses Benutzers besuchen
Beiträge der letzten Zeit anzeigen:   
Neues Thema eröffnen   Neue Antwort erstellen    JLI Spieleprogrammierung Foren-Übersicht -> DirectX, OpenGL Alle Zeiten sind GMT
Seite 1 von 1

 
Gehe zu:  
Du kannst keine Beiträge in dieses Forum schreiben.
Du kannst auf Beiträge in diesem Forum nicht antworten.
Du kannst deine Beiträge in diesem Forum nicht bearbeiten.
Du kannst deine Beiträge in diesem Forum nicht löschen.
Du kannst an Umfragen in diesem Forum nicht mitmachen.


Powered by phpBB © 2001, 2005 phpBB Group
Deutsche Übersetzung von phpBB.de

Impressum