210 likes | 345 Views
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)
E N D
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 Die Tangente ist parallel zur U-Achse der Textur und die Binormale ist parallel zur V-Achse Textur V
Die Effektdatei Benö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)
Pixelshader float4 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-Klasse class 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 ); DWORDBegin( LPDIRECT3DDEVICE9 Device ); void BeginPass(DWORD pass); void EndPass(); void End(); };
SBumpParameter-Struktur struct SBumpParameter { //Welche Werte wurden geändert? DWORD Changed; //Licht D3DXVECTOR4 LightColor; D3DXVECTOR4 LightAmbient; D3DXVECTOR3 LightDir; //Material D3DXVECTOR4 MtrlDiffColor; D3DXVECTOR4 MtrlSpecColor; 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 ) { D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE];//Vertexdeklaration D3DVERTEXELEMENT9 endDecl= D3DDECL_END();//setzt „Endeintrag“ im Array DWORD 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 setzen D3DXMATRIX 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 setzen if( 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(); }