Kapitel 9.3
Dot3-Bumpmapping (Normalmapping)
Bumpmaps Textur, deren Pixel unterschiedlich auf einfallendes Licht reagieren Betrachter hat den Eindruck, dass ein Objekt detailreicher sei Aber: die Geometrie des Objekts ändert sich nicht (z. B. an der
Silhouette des Objekts zu erkennen)
Es gibt zwei Arten von Bumpmaps: Heightmaps und Normalmaps
Heightmaps Graustufen-Texturen, in denen
Tiefenwerte codiert sind, welche dann zur Berechnung der Schattierung herangezogen werden
Normalmaps In der Textur werden die Normalen gespeichert 3 Farbkanäle: RGB Jeder Kanal steht für eine Dimension der Normale Bsp: liegt eine Textur in der XY-Ebene, so repräsentiert
der Rot-Anteil die X-Richtung
der Grün-Anteil die Y-Richtung
der Blau-Anteil die Z-Richtung der Normale
Tangentenraum Aber: Farb-Achsen-Zuordnung ist relativ Bei Bewegung des Objekts müssen sich die Normalen der Textur auch mit
bewegen Für jedes Polygon des Objekts muss also ein eigenes Koordinatensystem erstellt
werden Die Normale der Fläche des Polygons entspricht dann der logischen Z-Richtung
der Normalmap (Blau-Anteil) So ein Koordinatensystem nennt man Tangentenraum und die Achsen
bezeichnet man als:
Tangente (rot), Binormale (grün), und Normale (blau)U
V Textur
Die Tangente ist parallel zur U-Achse der Textur und die Binormale ist parallel zur V-Achse
Die EffektdateiBenötigte Parameter
// Matrizen float4x4 MatWVP : WorldViewProjection; float4x4 MatViewInv : ViewInverse; float4x4 MatWorld : World;
//Direktionale Lichtquelle float3 LightDir : Direction; float4 LightColor : Diffuse; float4 LightAmbient : Ambient;
//Materialien float4 MtrlDiffColor : Diffuse; float4 MtrlSpecColor : Specular; float MtrlSpecPower : SpecularPower;
Texture TexDiffuse : Diffuse; //Diffusemap Texture TexNormal : Diffuse; //Normalmap
Textursampler
sampler SamplerDiffuse = sampler_state { texture = <TexDiffuse>; AddressU = WRAP; AddressV = WRAP; AddressW = WRAP; MIPFILTER = LINEAR; MINFILTER = LINEAR; MAGFILTER = LINEAR;};
sampler SamplerNormal = sampler_state { texture = <TexNormal>; AddressU = WRAP; AddressV = WRAP; AddressW = WRAP; MIPFILTER = LINEAR; MINFILTER = LINEAR; MAGFILTER = LINEAR;};
Sampler für die Diffusemap
Sampler für die Normalmap
Vertexshader Ein- und Ausgabestrukturen
struct VertexInput { float3 Position : POSITION; float3 Normal : NORMAL; float2 Tex0 : TEXCOORD0; float3 Tangent : TANGENT; float3 Binormal : BINORMAL;};
struct VertexOutput { float4 HPosition : POSITION; float2 TexCoord : TEXCOORD0; float3 HalfVector : TEXCOORD2; float3 Normal : TEXCOORD3; float3 Tangent : TEXCOORD4; float3 Binormal : TEXCOORD5;};
Eingangsstruktur für den Vertexshader
Ausgabestruktur
Vertexshader
VertexOutput VS_Main( VertexInput IN ) { VertexOutput OUT; float3 PosWorld; float3 CamPosWorld; float3 CamDirToPos; float3 HalfVector; CamPosWorld = MatViewInv[3].xyz; PosWorld = mul( float4(IN.Position.xyz , 1.0) , MatWorld ); CamDirToPos = normalize( PosWorld - CamPosWorld ); HalfVector = -(LightDir+CamDirToPos);
OUT.Normal = normalize( mul( IN.Normal, (float3x3) MatWorld ) ); OUT.Tangent = normalize( mul( IN.Tangent, (float3x3) MatWorld ) ); OUT.Binormal = normalize( mul( IN.Binormal, (float3x3) MatWorld ) );
OUT.HPosition = mul( float4(IN.Position.xyz , 1.0) , MatWVP); OUT.TexCoord = IN.Tex0; OUT.HalfVector = HalfVector; return OUT;}
Transformieren der Tangente, Normale und Binormale in das Weltkoordinatensystem (alle Achsen des Tangentenraums werden mit der Weltmatrix multipliziert)
Pixelshaderfloat4 PS_Main( VertexOutput IN ) : COLOR{ float3 Normal; float4 Diffuse; float4 Specular; float3 HalfVec; float3 Light; float Intensity; float3x3 MatTex;
HalfVec = normalize(IN.HalfVector); //Normalisierung des Halbvektors MatTex = float3x3( IN.Tangent, -IN.Binormal, IN.Normal );
//Tangente, (negierte)Binormale und Normale in Matrix einsetzten Normal = 2.0f * tex2D(SamplerNormal, IN.TexCoord).rgb - 1.0f;
//Normale der Normalmap anhand der Farbwerte bestimmen Normal = normalize( mul( MatTex, Normal ) );
//Normale wird mit Matrix multipliziert//Berechnung des Farbwerts: Intensity = saturate( dot(Normal, -LightDir) ); Diffuse = Intensity * MtrlDiffColor * LightColor *
tex2D(SamplerDiffuse, IN.TexCoord); Specular = pow( max(0,dot( HalfVec, Normal)), MtrlSpecPower ) *
MtrlSpecColor * LightColor; return (Diffuse + Specular + LightAmbient);}
Ein Farbwert muss zwischen 0 und 1 liegen. Die Werte Normalen der Normalmap müssen auf den Bereich zwischen -1 und 1 skaliert werden.
Bsp:
voller Rot-Anteil -> 1 -> Normale erhält Wert 1 in Richtung der Tangente
Kein rot -> 0 -> Normale erhält Wert -1 in Richtung der Tangente
Die Dot3-Bumpmapping-Klasseclass CBumpEffect{protected:
ID3DXEffect* m_Effect;SBumpParameter m_Parameter;
public:static BOOL SetupMesh( ID3DXMesh** mesh, LPDIRECT3DDEVICE9 device );BOOL Create( LPDIRECT3DDEVICE9 Device );void Destroy();void Setup( SBumpParameter& Para );DWORD Begin( LPDIRECT3DDEVICE9 Device );void BeginPass(DWORD pass);void EndPass();void End();
};
SBumpParameter-Struktur
struct SBumpParameter{
//Welche Werte wurden geändert?DWORD Changed;
//LichtD3DXVECTOR4 LightColor;D3DXVECTOR4 LightAmbient;D3DXVECTOR3 LightDir;
//MaterialD3DXVECTOR4MtrlDiffColor;D3DXVECTOR4MtrlSpecColor;FLOAT MtrlSpecPower;LPDIRECT3DTEXTURE9 TexDiffuse;LPDIRECT3DTEXTURE9 TexNormal;
};
Alle Parameter, die wir an den Effekt übergeben wollen in einer Struktur festgehalten.
Changed signalisiert welche Werte geändert wurden, damit nur diese Werte neu übergeben werden müssen.
Vertexdeklaration Dient, genau wie das Flexible Vertex-Format (FVF), zur „Beschreibung“ der
Vertices Array der Struktur D3DVERTEXELEMENT9 Jeder Array-Eintrag beschreibt ein Attribut des Vertex (z.B. Position, Normale,
Tangente…)
D3DVERTEXELEMENT9-Struktur:
Typedef struct _D3DVERTEXELEMENT9 {WORD Stream;WORD Offset;BYTE Type;BYTE Method;BYTE Usage; //semantische Bedeutung des Eintrags (z.B. Tangente)BYTE UsageIndex;
}
Die Funktion SetupMesh
BOOL CBumpEffect::SetupMesh( ID3DXMesh** mesh, LPDIRECT3DDEVICE9 device ){
D3DVERTEXELEMENT9decl[MAX_FVF_DECL_SIZE]; //VertexdeklarationD3DVERTEXELEMENT9endDecl = D3DDECL_END(); //setzt „Endeintrag“ im ArrayDWORD FVF = (*mesh)->GetFVF();ID3DXMesh* clone = NULL;BumpVertex* vertex = NULL;DWORD numVertices = (*mesh)->GetNumVertices();
if( FVF!=(D3DFVF_TEX1|D3DFVF_NORMAL|D3DFVF_XYZ|D3DFVF_TEXCOORDSIZE2(1)) )return false; /*Prüfen, ob der Mesh die Position des V., die Normale
des V. und ein Texturkoordinatenpaar gespeichert hat. Diese Werte brauchen wir zur Berechnung des Tangentenraums*/
(*mesh)->GetDeclaration( decl );//Aktuelle //Vertexdeklaration wird übergeben
decl[3].Stream = 0;decl[3].Offset = 32;decl[3].Type = D3DDECLTYPE_FLOAT3;decl[3].Method = D3DDECLMETHOD_DEFAULT;decl[3].Usage = D3DDECLUSAGE_TANGENT;decl[3].UsageIndex = 0;
Pro Vertex sind schon 3 Einträge im Array gespeichert:
Eintrag 0: Position
Eintrag 1: Normale
Eintrag 2: Texturkoordinaten-Paar
Hier wird die Tangente hinzugefügt (Eintrag 3)
decl[4].Stream = 0;decl[4].Offset = 44;decl[4].Type = D3DDECLTYPE_FLOAT3;decl[4].Method = D3DDECLMETHOD_DEFAULT;decl[4].Usage = D3DDECLUSAGE_BINORMAL;decl[4].UsageIndex = 0;
decl[5] = endDecl; //Endeintrag der Vertexdeklaration (Eintrag 5)
(*mesh)->CloneMesh((*mesh)->GetOptions(), decl, device, &clone ))//Mesh wird geklont und enthält nun die neuen Deklarationen
SAFE_RELEASE( (*mesh) ); //alten Mesh löschen*mesh = clone; //Klon einsetzten
D3DXComputeTangent( clone, 0, 0, 0, false, NULL)) //Berechnet Tangentenraum
return true;}
Binormale wird hinzugefügt (Eintrag 4)
Weitere Funktionen… Create:
BOOL CBumpEffect::Create( LPDIRECT3DDEVICE9 Device ){
ID3DXBuffer* ErrorBuffer;
if( FAILED( D3DXCreateEffectFromFile( Device, "Effects/BumpMapping_FXComposer.fx", NULL, NULL, 0/*D3DXSHADER_DEBUG|D3DXSHADER_FORCE_PS_SOFTWARE_NOOPT|D3DXSHADER_FORCE_VS_SOFTWARE_NOOPT*/,
NULL, &m_Effect, &ErrorBuffer ) ) ){
char *Errors = (char*) ErrorBuffer->GetBufferPointer();fprintf( stderr, "%s", Errors );return false;
}
return true;}
Destroy:void CBumpEffect::Destroy(){
SAFE_RELEASE( m_Effect );} Setup:void CBumpEffect::Setup( SBumpParameter& Para ){
if( Para.Changed & LIGHTCOLOR )m_Parameter.LightColor = Para.LightColor;
if( Para.Changed & LIGHTAMBIENT )m_Parameter.LightAmbient = Para.LightAmbient;
if( Para.Changed & LIGHTDIR )m_Parameter.LightDir = Para.LightDir;
if( Para.Changed & MTRLDIFFCOLOR )m_Parameter.MtrlDiffColor = Para.MtrlDiffColor;
if( Para.Changed & MTRLSPECCOLOR )m_Parameter.MtrlSpecColor = Para.MtrlSpecColor;
if( Para.Changed & MTRLSPECPOWER )m_Parameter.MtrlSpecPower = Para.MtrlSpecPower;
if( Para.Changed & TEXDIFFUSE )m_Parameter.TexDiffuse = Para.TexDiffuse;
if( Para.Changed & TEXNORMAL )m_Parameter.TexNormal = Para.TexNormal;
m_Parameter.Changed |= Para.Changed;}
Begin:
DWORD CBumpEffect::Begin( LPDIRECT3DDEVICE9 Device ){
UINT passes = 0;
// Matrizen setzenD3DXMATRIX MatWorld, MatView, MatProj;D3DXMATRIX MatWVP, MatViewInv, MatWorldInv;
Device->GetTransform( D3DTS_WORLD, &MatWorld );Device->GetTransform( D3DTS_VIEW, &MatView );Device->GetTransform( D3DTS_PROJECTION, &MatProj );
m_Effect->SetMatrix( "MatWorld", &MatWorld );
MatWVP = (MatWorld*MatView)*MatProj;m_Effect->SetMatrix( "MatWVP", &MatWVP );
D3DXMatrixInverse( &MatViewInv, NULL, &MatView );m_Effect->SetMatrix( "MatViewInv", &MatViewInv );
m_Effect->SetTechnique( "BumpMapping" );m_Effect->Begin( &passes, 0 );
return passes;}
BeginPass:
void CBumpEffect::BeginPass(DWORD pass){
// Parameter setzenif( m_Parameter.Changed & LIGHTCOLOR )
m_Effect->SetValue( "LightColor", m_Parameter.LightColor, sizeof( D3DXVECTOR4 ) ) ;if( m_Parameter.Changed & LIGHTAMBIENT )
m_Effect->SetValue( "LightAmbient", m_Parameter.LightAmbient, sizeof( D3DXVECTOR4 ) );if( m_Parameter.Changed & LIGHTDIR )
m_Effect->SetValue( "LightDir", m_Parameter.LightDir, sizeof( D3DXVECTOR3 ) );if( m_Parameter.Changed & MTRLDIFFCOLOR )
m_Effect->SetValue( "MtrlDiffColor", m_Parameter.MtrlDiffColor, sizeof( D3DXVECTOR4 ) );if( m_Parameter.Changed & MTRLSPECCOLOR )
m_Effect->SetValue( "MtrlSpecColor", m_Parameter.MtrlSpecColor, sizeof( D3DXVECTOR4 ) );if( m_Parameter.Changed & MTRLSPECPOWER )
m_Effect->SetFloat( "MtrlSpecPower", m_Parameter.MtrlSpecPower );if( m_Parameter.Changed & TEXDIFFUSE )
m_Effect->SetTexture( "TexDiffuse", m_Parameter.TexDiffuse );if( m_Parameter.Changed & TEXNORMAL )
m_Effect->SetTexture( "TexNormal", m_Parameter.TexNormal );
m_Effect->BeginPass( pass );}
EndPass:void CBumpEffect::EndPass(){
m_Effect->EndPass();m_Parameter.Changed = 0;
}
End:void CBumpEffect::End(){
m_Effect->End();}
Top Related