Ich habe mich mal dazu entschlossen ein kleines Tutorial über Fragmentshader zu schreiben. Das Fragmentshader ansich ja recht leicht zu verstehen sind, werde ich noch einen kleinen Manager hinzufügen.
Einleitung
Also, als Erstes mal, was ist ein Fragmentshader überhaupt. Es steckt schon im wort drin -> Fragment. Es handelt sich hier nicht mehr um einen kompletten Vertex/Pixelshader, sondern um kleine Bruckstücke, Fragmente eben.
Diese Fragmente werden dann im Hauptprogramm zu einem richtigen und vollständigen Shader verlinkt.
Dies hat den Vorteil, dass man nicht für alles komplette Shader schreiben muss. Moderne Spiele haben heute weit über 1000 verschiedene Pixelshader. Vertexshader häufen sich ebenfalls. Wäre jetzt doch dumm, wenn man immer komplette Shader scheiben müsste. Hier kommen dann die Fragmente ins Spiel.
Der Manager ist nur ein Beispiel, wie man es machen kann. Den Code kann man mit Sicherheit auch noch verbessern, aber zum Vorführen reicht er vollkommen aus.
Ich habe zwar schon viel über D3DX gehört. Es soll langsam sein, nicht zuverlässig arbeiten etc... . Ich halte das für schwachsinn. Es arbeitet schnell und auch gut. Deswegen werde ich hier auch D3DX verwenden.
Außerdem verwende ich noch die TriBase. Sie ist einfach strukturiert, sodass man es schnell auf jede andere Engine umschreiben kann.
FSM -- FragmentShaderManager
Der Name der Klasse lautet "FSM". Dazu habe ich mich entschlossen, sie als Singleton anzulegen. Ist natürlich jedem selbst überlassen.
Wie man einen Singleton erstellt sollte eigentlich jedem bekannt sein. Falls das nicht der Fall sein sollte, schaut euch Anhang "A" an.
Alle Variablen die ich in dem Manager nutze sind als "private" deklariert.
Nun zur Klasse.
Ich habe als erstes drei "Sicherheitsvariablen" eingebaut:
|
C-/C++-Quelltext
|
1
2
3
|
bool _isInit;
bool _isVertexShader;
bool _isPixelShader;
|
Die Variable "_isInit" wird auf true gesetzt, wenn man den Manager initialisiert. Bei jeder wichtigen Methode wird überprüft ob der Manager initialisiert ist, wenn nicht, passiert da gar nichts.
Die anderen beiden Variablen geben an, ob ein Vertex/Pixelshader gesetzt ist. Die werden später noch mal auftauchen.
Als nächtes kommen die Linker sachen:
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
|
ID3DXFragmentLinker* _fragmentLinker;
LPD3DXCONSTANTTABLE _constantTable;
LPD3DXBUFFER _vertexFragments;
LPD3DXBUFFER _pixelFragments;
IDirect3DVertexShader9* _vertexshader;
IDirect3DPixelShader9* _pixelshader;
D3DXHANDLE _vertexFragmentHandles[10];
D3DXHANDLE _pixelFragmentHandles[10];
|
Die Variable "_fragmentLinker" ist der eigentliche Linker, der später die einzelnen Fragmente linkt.
Die Variable "_constantTable" beinhaltet die ganzen globalen Variablen, die es gibt.
Die beiden Buffer "_vertexFragments" und "_pixelFragments" beinhalten die einzelnen Fragmente, die aus der Datei gelesen wurden.
Die beiden nächsten sind zu einem der Vertexshader und zum anderen der Pixelshader.
Die beiden letzten sind die Handles auf die einzelnen Fragmente.
Als letztes komme ein paar Hilfsvariablen:
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
|
unsigned int _numberofSetVertexshader;
VertexFragments _vertexFragmentList;
unsigned int _numberofSetPixelshader;
PixelFragments _pixelFragmentList;
unsigned int _arrayPosition;
tbDirect3D* _d3d;
std::basic_string<wchar_t> _vertexFragmentFile;
std::basic_string<wchar_t> _pixelFragmentFile;
|
Die Variablen sollten sich von alleine klären, anderenfalls tauchen sie später nochmals auf.
Das waren die Variablen des Managers. Jetzt kommen noch die einzelnen Methoden der Klasse:
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
|
tbResult InitManager(tbDirect3D* _direct3D, std::basic_string<wchar_t> _vertexFragmentFile, std::basic_string<wchar_t> _pixelFragmentFile);
tbResult ExitManager();
tbResult SetVertexShader(std::basic_string<char> _shaderVersion, DWORD _flags);
tbResult SetPixelShader(std::basic_string<char> _shaderVersion, DWORD _flags);
LPD3DXCONSTANTTABLE GetConstantTable()const;
tbResult Start();
tbResult Stop();
tbResult SetInteger(std::basic_string<char> _name, int _integer);
tbResult SetFloat(std::basic_string<char> _name, float _float);
tbResult SetVector(std::basic_string<char> _name, D3DXVECTOR4* _vector);
tbResult SetMatrix(std::basic_string<char> _name, D3DXMATRIX* _matrix);
|
Kommen wir jetzt zu den einzelnen Funktionen. Die werde ich nicht großartig erleutern. Man sollte das auch so erkennen was ich da mache.
InitManager
Die Init-Methode erwartet 3 Parameter. Der erste ist ein gültiger Zeiger auf eine D3D-Instance. Der 2 und 3 sind Pfadabgaben zu den einzelnen Files, die die Fragmente beinhalten. Dazu später.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
tbResult FSM::InitManager(tbDirect3D* _direct3D, std::basic_string<wchar_t> _vertexFragmentFile, std::basic_string<wchar_t> _pixelFragmentFile)
{
TB_INFO("FragmentShaderManager wird initialisiert...");
// Check parameters
if(_vertexFragmentFile.empty() == true || _pixelFragmentFile.empty() == true || _direct3D == NULL)
return TB_ERROR;
// Copy parameter
_d3d = _direct3D;
_vertexFragmentFile = _vertexFragmentFile;
_pixelFragmentFile = _pixelFragmentFile;
// Create fragment linker
if(D3DXCreateFragmentLinker(_direct3D->GetDevice(), 0, &_fragmentLinker) != D3D_OK)
{
TB_ERROR("Fragmentlinker konnte nicht erstellt werden", TB_ERROR);
return TB_ERROR;
}
// Gather all vertexfragments
if(D3DXGatherFragmentsFromFile(_vertexFragmentFile.c_str(), NULL, NULL, NULL, &_vertexFragments, NULL) != D3D_OK)
{
TB_ERROR("Vertexfragmente konnten nicht gesammelt werden", TB_ERROR);
return TB_ERROR;
}
// Gather all pixelfragments
if(D3DXGatherFragmentsFromFile(_pixelFragmentFile.c_str(), NULL, NULL, NULL, &_pixelFragments, NULL) != D3D_OK)
{
TB_ERROR("Pixelfragmente konnten nicht gesammelt werden", TB_ERROR);
return TB_ERROR;
}
// Add vertexfragments to buffer
if(_fragmentLinker->AddFragments((DWORD*)_vertexFragments->GetBufferPointer()) != D3D_OK)
{
TB_ERROR("Vertexfragmente konnten nicht zum Buffer hinzugefügt werden", TB_ERROR);
return TB_ERROR;
}
// Add pixelfragments to buffer
if(_fragmentLinker->AddFragments((DWORD*)_pixelFragments->GetBufferPointer()) != D3D_OK)
{
TB_ERROR("Pixelfragmente konnten nicht zum Buffer hinzugefügt werden", TB_ERROR);
return TB_ERROR;
}
_isInit = true;
TB_INFO("FragmentShaderManager wurde initialisiert");
return TB_OK;
}
|
ExitManager
Die Exit-Methode hat die Aufgabe alles wieder zu beenden.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
tbResult FSM::ExitManager()
{
TB_INFO("FragmentShaderManager wird beendet...");
// Release shader, vertex/pixel
if(_vertexshader) _vertexshader->Release();
if(_pixelshader) _pixelshader->Release();
// Release fragmentbuffers, vertex/pixel
if(_vertexFragments) _vertexFragments->Release();
if(_pixelFragments) _pixelFragments->Release();
// Release table
if(_constantTable) _constantTable->Release();
// Release linker
if(_fragmentLinker) _fragmentLinker->Release();
_isInit = false;
TB_SAFE_DELETE(_manager);
TB_INFO("FragmentShaderManager wurde beendet");
return TB_OK;
}
|
SetVertexShader
Die Methode ist dafür verantwortlich alles zu linken, und anschließend den Vertexshader daraus zu basteln. Sie erwartet 2 Parameter. Der erste ist die Version des Shaders, vs_1_1 ... vs_4_1. Wer das neue SDK haben sollte kann nur noch den Bereich, vs_2_0 ... vs_4_1, verwenden.
Der zweite Parameter sind die Flags. Siehe für mehr Infos Anhang "B".
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
tbResult FSM::SetVertexShader(std::basic_string<char> _shaderVersion, DWORD _flags)
{
// Variables
ID3DXBuffer* code;
DWORD* shaderCode = NULL;
// Check parameters
if(_shaderVersion.empty() == true)
return TB_ERROR;
_numberofSetVertexshader = NULL;
_arrayPosition = NULL;
// Check flags
if((_flags & VSF_NONE) == PSF_NONE)
return TB_OK;
if((_flags & VSF_PROJECTION) == VSF_PROJECTION)
{
_numberofSetVertexshader++;
_vertexFragmentList.projection = true;
}
if((_flags & VSF_DIFFUSEAMBIENT) == VSF_DIFFUSEAMBIENT)
{
_numberofSetVertexshader++;
_vertexFragmentList.diffuseAmbient = true;
}
// Get fragments
if(_vertexFragmentList.projection == true)
{
_vertexFragmentHandles[_arrayPosition] = _fragmentLinker->GetFragmentHandleByName("Fragment_Vertex_Projection");
_arrayPosition++;
}
if(_vertexFragmentList.diffuseAmbient == true)
{
_vertexFragmentHandles[_arrayPosition] = _fragmentLinker->GetFragmentHandleByName("Fragment_Vertex_DiffuseAmbient");
_arrayPosition++;
}
if(_vertexshader) _vertexshader->Release();
// Link...
if(_fragmentLinker->LinkShader(_shaderVersion.c_str(), 0, _vertexFragmentHandles, _numberofSetVertexshader, &code, NULL) != D3D_OK)
{
TB_ERROR("Vertexshader konnten nicht gelinkt werden", TB_ERROR);
return TB_ERROR;
}
shaderCode = (DWORD*)code->GetBufferPointer();
code->Release();
if(_d3d->GetDevice()->CreateVertexShader(shaderCode, &_vertexshader) != D3D_OK)
{
TB_ERROR("Vertexshader konnte nicht erstellt werden", TB_ERROR);
return TB_ERROR;
}
if(_constantTable) _constantTable->Release();
if(D3DXGetShaderConstantTable(shaderCode, &_constantTable) != D3D_OK)
{
TB_ERROR("Konstantenliste konnte nicht erstellt werden", TB_ERROR);
return TB_ERROR;
}
_isVertexShader = true;
return TB_OK;
}
|
SetPixelShader
Diese Funktion macht alles genaus wie der Vorgänger nur eben für die Pixelshader.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
tbResult FSM::SetPixelShader(std::basic_string<char> _shaderVersion, DWORD _flags)
{
// Variables
ID3DXBuffer* code;
DWORD* shaderCode = NULL;
// Check parameters
if(_shaderVersion.empty() == true)
return TB_ERROR;
_numberofSetPixelshader = NULL;
_arrayPosition = NULL;
// Check flags
if((_flags & PSF_NONE) == PSF_NONE)
return TB_OK;
if((_flags & PSF_TEXTURESURFACE) == PSF_TEXTURESURFACE)
{
_numberofSetPixelshader++;
_pixelFragmentList.texturingSurface = true;
}
// Get fragments
if(_pixelFragmentList.texturingSurface == true)
{
_pixelFragmentHandles[_arrayPosition] = _fragmentLinker->GetFragmentHandleByName("Fragment_Pixel_TexturingSurface");
_arrayPosition++;
}
if(_pixelshader) _pixelshader->Release();
// Link...
if(_fragmentLinker->LinkShader(_shaderVersion.c_str(), 0, _pixelFragmentHandles, _numberofSetPixelshader, &code, NULL) != D3D_OK)
{
TB_ERROR("Pixelshader konnten nicht gelinkt werden", TB_ERROR);
return TB_ERROR;
}
shaderCode = (DWORD*)code->GetBufferPointer();
code->Release();
if(_d3d->GetDevice()->CreatePixelShader(shaderCode, &_pixelshader) != D3D_OK)
{
TB_ERROR("Pixelshader konnte nicht erstellt werden", TB_ERROR);
return TB_ERROR;
}
_isPixelShader = true;
return TB_OK;
}
|
Jetzt fehlern eigentlich nur noch die Funktionen für die Übergabe an Werten. Das kann man auf verschiedener Weise erledigen. Einmal direkt über die Variable "_constantTable". Allerdings, falls der ungültig werden sollte, schmiert das Programm mit einem Runtime-Error ab -> unschöne sache. Da habe ich mir einfach ein paar Methoden selbst erstellt, die vorher prüfen, ob die Variable gültig ist.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
//.................................................
// FSM -> GetConstantTable()
//.................................................
LPD3DXCONSTANTTABLE FSM::GetConstantTable()const
{
if(_constantTable)
return _constantTable;
return NULL;
}
//.................................................
// FSM -> SetInteger()
//.................................................
tbResult FSM::SetInteger(std::basic_string<char> _name, int _integer)
{
if(_constantTable)
_constantTable->SetInt(_d3d->GetDevice(), _name.c_str(), _integer);
return TB_OK;
}
//.................................................
// FSM -> SetFloat()
//.................................................
tbResult FSM::SetFloat(std::basic_string<char> _name, float _float)
{
if(_constantTable)
_constantTable->SetFloat(_d3d->GetDevice(), _name.c_str(), _float);
return TB_OK;
}
//.................................................
// FSM -> SetVector()
//.................................................
tbResult FSM::SetVector(std::basic_string<char> _name, D3DXVECTOR4* _vector)
{
if(_constantTable)
_constantTable->SetVector(_d3d->GetDevice(), _name.c_str(), _vector);
return TB_OK;
}
//.................................................
// FSM -> SetMatrix()
//.................................................
tbResult FSM::SetMatrix(std::basic_string<char> _name, D3DXMATRIX* _matrix)
{
if(_constantTable)
_constantTable->SetMatrix(_d3d->GetDevice(), _name.c_str(), _matrix);
return TB_OK;
}
|
Beinahe hätte ich das vergessen. Wie startet man den Shader nun? Das übernehmen die beiden nächsten Funktionen. Dort finden wir auch wieder unsere 2 Sicherheitsvariablen wieder.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
//.................................................
// FSM -> Start()
//.................................................
tbResult FSM::Start()
{
// Set pixel/vertex shader
if(_isPixelShader)
{
if(_d3d->GetDevice()->SetPixelShader(_pixelshader) != D3D_OK)
{
TB_ERROR("Pixelshader konnte nicht gesetzt werden", TB_ERROR);
return TB_ERROR;
}
}
if(_isVertexShader)
{
if(_d3d->GetDevice()->SetVertexShader(_vertexshader) != D3D_OK)
{
TB_ERROR("Vertexshader konnte nicht gesetzt werden", TB_ERROR);
return TB_ERROR;
}
}
return TB_OK;
}
//.................................................
// FSM -> Stop()
//.................................................
tbResult FSM::Stop()
{
// Set pixel/vertex shader
_d3d->GetDevice()->SetPixelShader(NULL);
_isPixelShader = false;
_d3d->GetDevice()->SetVertexShader(NULL);
_isVertexShader = false;
return TB_OK;
}
|
Damit wäre die Klasse vollständig und einsatzbereit.
Fragmentshader-Files
Ich habe mir meine eigene Struktur für die Shader überlegt. Sie sieht ein wenig kompliziert aus, ist es aber eigentlich nicht.
Es gibt bei mir 5 unterschiedliche Datein. Die Namen sollten den Inhalt erklären:
1. Include_Sampler.fx
2. Include_Variables.fx
3. Include_Structs.fx
4. PixelShaderFragments.fx
5. PixelShaderFragments.fx
Ich werde nur auf die beiden letzten ein wenig intensiver eingehen.
Include_Sampler.fx
Das File beinhaltet die ganzen Sampler, die es gibt. Bei mir sind es ein paar mehr, aber hier brauchen wir erstmal nur einen.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//------------------------------------------------
// VARIABLES
//------------------------------------------------
texture texture_Surface;
//------------------------------------------------
// SAMPLER - SURFACE
//------------------------------------------------
sampler SamplerSurfaceLinear = sampler_state
{
texture = (texture_Surface);
MIPFILTER = LINEAR;
MINFILTER = LINEAR;
MAGFILTER = LINEAR;
};
|
Die Texturevariablen lagern hier und nicht in dem eigentlichen Inlcudefile für die Variablen, aufgrund des sonst kommenden Fehlers der Mehrfachinkludierung.
Include_Variables.fx
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#pragma once
//------------------------------------------------
// VARIABLES
//------------------------------------------------
shared float4x4 matrix_4x4_World;
shared float4x4 matrix_4x4_Rotation;
shared float4x4 matrix_4x4_View;
shared float4x4 matrix_4x4_Projection;
shared float4x4 matrix_4x4_ViewProjection;
shared float4x4 matrix_4x4_WorldViewProjection;
shared float4 vector_4_MaterialDiffuse;
shared float4 vector_4_MaterialAmbient;
shared float4 vector_4_LightDiffuse;
shared float4 vector_4_LightAmbient;
shared float3 vector_3_LightPosition;
shared float3 vector_3_DirToLight;
|
Include_Structs.fx
Hier lagern die ganzen Input-und Outputstrukturen für die Shader.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
#pragma once
//------------------------------------------------
// STRUCTS - PROJECTION
//------------------------------------------------
// Input
struct input_Projection
{
float4 ObjectPosition:POSITION;
float3 ObjectNormal:NORMAL;
float2 TextureSurface:TEXCOORD0;
};
// Output
struct output_Projection
{
float4 ProjectionPosition:POSITION;
float3 WorldNormal:r_WorldNormal;
float2 TextureSurface:TEXCOORD0;
};
//------------------------------------------------
// STRUCTS - DIFFUSAMBIENTECOLOR
//------------------------------------------------
// Input
struct input_ColorDiffuseAmbient
{
float3 WorldNormal:r_WorldNormal;
};
// Output
struct output_ColorDiffuseAmbient
{
float4 ColorDiffuseAmbient:COLOR0;
};
//------------------------------------------------
// STRUCTS - TEXTURINGSURFACE
//------------------------------------------------
// Input
struct input_TexturingSurface
{
float4 Color:COLOR0;
float2 TextureSurface:TEXCOORD0;
};
// Output
struct output_TexturingSurface
{
float4 Color:COLOR0;
};
|
PixelShaderFragments.fx
Kommen wir nun endlich zu den Hauptfiles, beginnend mit dem für die Pixelshaderfragmente.
Zuerst inkludieren wir alle drei Include-Files.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
|
#pragma once
//------------------------------------------------
// INCLUDES
//------------------------------------------------
#include "Include_Variables.fx"
#include "Include_Structs.fx"
#include "Include_Sampler.fx"
|
Jetzt folgt das erste Fragment. Es sieht einer Funktion sehr ähnlich. Sie erwartet 2 Parameter. Der erste ist die Inputstruktur, der zweite die Outputstruktur. Die Parameter füllt die Grafikkarte selbstständig.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
|
//------------------------------------------------
// TexturingSurface
//------------------------------------------------
void TexturingSurface(in input_TexturingSurface IN, out output_TexturingSurface OUT)
{
OUT.Color = IN.Color * tex2D(SamplerSurfaceAnisotropic, IN.TextureSurface);
};
|
Die Funktion addiert die Farbe des momentanen Texels mit der momentane Pixelfarbe des Models.
Jetzt kommt die eigentliche Fragmentdefinition.
|
C-/C++-Quelltext
|
1
2
3
4
|
//------------------------------------------------
// Fragments
//------------------------------------------------
pixelfragment Fragment_Pixel_TexturingSurface = compile_fragment ps_2_0 TexturingSurface();
|
Die Variable "pixelfragment" gibt an, dass es sich um ein Fragment für den Pixelshader handelt. Gefolgt von den Namen, des Fragments.
Als nächstes sagen wir ihm, das er unsere Funktion mit einem ps20 compilieren soll. Ansich recht simpel.
Damit hätten wir diese Datei auch besprochen.
VertexShaderFragments.fx
Diese Datei unterscheidet sich nicht groß zwischen der anderen. Hier inkludieren wir ebenfalls die Include-Files. Danach kommen 2 Funktionen. Die erste erledigt grundlegende Sachen, wie Projektion....
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
|
//------------------------------------------------
// Projection
//------------------------------------------------
void Projection(in input_Projection IN, out output_Projection OUT)
{
OUT.ProjectionPosition = mul(IN.ObjectPosition, matrix_4x4_WorldViewProjection);
OUT.WorldNormal = mul(IN.ObjectNormal, (float3x3)matrix_4x4_World);
OUT.TextureSurface = IN.TextureSurface;
};
|
Die zweite berechnet anschließend die DifusseAmbiente-Beleuchtung.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
|
//------------------------------------------------
// CalculateDiffuseAmbientColor
//------------------------------------------------
void CalculateDiffuseAmbientColor(in input_ColorDiffuseAmbient IN, out output_ColorDiffuseAmbient OUT)
{
OUT.ColorDiffuseAmbient = vector_4_LightDiffuse * vector_4_MaterialDiffuse * saturate(dot(IN.WorldNormal, normalize(vector_3_DirToLight)));
OUT.ColorDiffuseAmbient += vector_4_LightAmbient * vector_4_MaterialAmbient;
};
|
Zu guter Letzt die beiden Fragmente.
|
C-/C++-Quelltext
|
1
2
3
4
5
|
//------------------------------------------------
// Fragments
//------------------------------------------------
vertexfragment Fragment_Vertex_Projection = compile_fragment vs_2_0 Projection();
vertexfragment Fragment_Vertex_DiffuseAmbient = compile_fragment vs_2_0 CalculateDiffuseAmbientColor();
|
Schlusswort
Ich hoffe mal ich konnte ich die Fragmentshader ein wenig näher bringen. Sicher, dieses Beispiel ist das leichteste was man hätte nehmen können. Aber so kommen auch Anfänger mit zu recht.
Das Geschriebene ist natürlich sehr lang. Falls einer da nicht mehr mit kommen sollte, kann sich den Coder im Anhang runterladen.
Ein paar Fragen kläre ich jetzt noch im Anhang.
Anhang A -> Singelton
http://www.oop-trainer.de/Themen/Singleton.html
Anhang B -> Flags
Die beiden Funktion SetVertexShader uns SetPixelShader erwarten Flags. Ich habe die folgenden genommen.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
|
#define VSF_NONE 0x00
#define VSF_PROJECTION 0x01
#define VSF_DIFFUSEAMBIENT 0x02
#define PSF_NONE 0x00
#define PSF_TEXTURESURFACE 0x01
|
Anhang C -> Allgemeines
Die beiden Variablen des Managers, "_vertexFragmentList", "_pixelFragmentList" sind beides Strukturen, die Infos halten.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
struct VertexFragments
{
// Variables
bool projection;
bool diffuseAmbient;
// Constructor
VertexFragments()
{
projection = false;
diffuseAmbient = false;
}
// Destructor
virtual ~VertexFragments()
{}
};
struct PixelFragments
{
// Variables
bool texturingSurface;
// Constructor
PixelFragments()
{
texturingSurface = false;
}
// Destructor
virtual ~PixelFragments()
{}
};
|
In den dem Strukturen-File der Shader taucht 2x sowas auf.
|
C-/C++-Quelltext
|
1
|
float3 WorldNormal:r_WorldNormal;
|
Das simbolisiert, dass die Variable ein und die selbe ist. Sie steckt zwar un unterschiedlichen Strukturen drin, aber wenn der Linker sie zusammenfummelt, macht er praktisch eine raus. das "r_" ist dabei Pflicht!
Anhang D -> Download
http://home.arcor.de/BlackSnake-Studios_…gmentShader.rar