Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

Environment Mapping.

Ähnliche Präsentationen


Präsentation zum Thema: "Environment Mapping."—  Präsentation transkript:

1 Environment Mapping

2 Environment mapping ist ein Materialeffekt, bei dem das Material eines Objekts die Umgebung reflektiert Folgende Eigenschaften braucht man zur Berechnung: - Reflexionseigenschaften des Materials auf diffuses Licht - Reflexionseigenschaften des Materials auf spekulares Licht - den Grad der spekularen Reflexion - die Umgebungstextur - die Intensität der Spiegelung Zur Implementation dieser Eigenschaften brauchen wir das Phong-Reflexionsmodell, auf dem die D3DMATERIAL9-Materialstruktur basiert

3 In DirectX setzt sich ein Material aus 4 Komponenten zusammen:
- ambiente Komponente - diffuse Komponente - spekulare Komponente - emissive Komponente (Selbstleucht-Eigenschaft) Aus den ersten 3 setzt sich das reflektierte Licht zusammen -> ARGB-Farbwert Die Umgebungsfarbe berechnet sich dabei folgendermaßen: AmbientColor = AmbientColorMaterial * AmbientColorLichtquelle (kanalweise Multiplikation)

4 Die diffuse Komponente hängt davon ab, wie das Objekt zur Lichtquelle positioniert ist,
daher müssen wir Richtung des Lichts und die Normale der Objektoberfläche einbeziehen: DiffuseColor = DiffuseColorMaterial * DiffuseLichtquelle * max(0, L N ) Ist der Winkel zwischen dem negativen, normierten Richtungsvektor des Lichts und der Flächennormale = 0 => Skalarprodukt ergibt 1 90° => Skalarprodukt ergibt 0 >90° => Skalarprodukt negativ => Flächen, die dem Licht zugewandt sind, werden erhellt, abgewandte Flächen verdunkelt

5 Die spekulare Reflexion simuliert den Glanzpunkt
Dies ist eine direkte Spiegelung der Lichtquelle

6 → → SpecularColor = SpecularColorMaterial * SpecularColorLichtquelle *
max (0, H N ) n n ist die Größe und Intensität des Glanzpunktes Dieses Beleuchtungsmodell ist das Standard-Modell von DirectX für die Fixed-Function-Pipeline Nun setzen wir dies in eine FX-Datei um:

7 // Matrizen float4x4 MatWVP; // = World View Projection float4x4 MatViewInv; // = Inverse der View-Matrix -> zur Berechnung der Position des Betrachters float4x4 MatWorld; // zur Transformation der Normalen eines Modells in Weltkoordinatensystem // Directionale Lichtquelle float3 LightDir; // muss normiert sein! float4 LightColor; // verwendet für diffuse & spekulare Komponente float4 LightAmbient; // wirkt sich auf alle Objekte der Szene aus // Materialien float4 MtrlDiffColor; float4 MtrlSpecColor; float MtrlSpecPower;

8 Ein- und Ausgabestruktur der Vertices
// Eingang Vertexshader struct VertexInput { float3 Position : POSITION; float3 Normal : NORMAL; }; // Ausgang Vertexshader struct VertexOutput { float4 HPosition : POSITION; float4 Diffuse : COLOR0; float4 Specular : COLOR1;

9 VertexOutput VS_Main( VertexInput IN )
{ VertexOutput OUT; float3 NormalWorld; float3 PosWorld; float3 CamPosWorld; float3 CamDirToPos; float3 HalfVector; NormalWorld = normalize( mul( IN.Normal, (float3x3) MatWorld ) ); CamPosWorld = MatViewInv[3].xyz; PosWorld = mul( float4(IN.Position.xyz , 1.0) , MatWorld ); CamDirToPos = normalize( PosWorld - CamPosWorld ); HalfVector = -normalize( LightDir+CamDirToPos ); OUT.HPosition = mul( float4(IN.Position.xyz , 1.0) , MatWVP); OUT.Diffuse = max( 0, dot( NormalWorld, -LightDir ) ) * MtrlDiffColor * LightColor; OUT.Specular = pow( max( 0, dot( HalfVector, NormalWorld) ), MtrlSpecPower ) * MtrlSpecColor * LightColor; return OUT;

10 Der Pixelshader ist sehr simpel gehalten, da die Berechnungen der Farben im Vertexshader vorgenommen wurden. Dies dient der Leistungssteigerung. float4 PS_Main( VertexOutput IN ) : COLOR { return IN.Diffuse + IN.Specular + LightAmbient; } technique EnvMapping pass p0 VertexShader = compile vs_2_0 VS_Main(); PixelShader = compile ps_2_0 PS_Main(); ColorArg1[0] = Diffuse; AlphaArg1[0] = Diffuse; ColorOp[0] = SelectArg1; AlphaOp[1] = SelectArg1;

11 Hiermit haben wir eine Beleuchtungsroutine geschaffen, die das Licht in der Szene reflektiert.
Um jedoch die gesamte Umgebung zu reflektieren, sind noch einige weitere Schritte notwendig. Grundsätzlich könnte ein Modell der Abbildung so aussehen:

12 Um aber die gesamte Umgebung auf eine Textur zu mappen, bedarf es eines Tricks, dem sogenannten Cubemapping Hierbei wird die Umgebung auf die Innenseiten eines Würfels abgebildet, in dessen Mitte sich unser Objekt befindet. Wichtig ist hierbei natürlich, dass der eigentliche Würfel für den User unsichtbar ist und nur die Reflexionen des Würfels auf das Objekt zu sehen sind

13 Hierzu bietet DirectX ein Interface namens
IDirect3DCubeTexture9 Mithilfe des DirectX Texture Tool (liegt dem SDK bei) können wir aus 6 einzelnen Bildern eine Cubemap-Textur erzeugen Mit D3DXCreateTextureFromFile wird das Cubemap-Objekt geladen Unsere FX-Datei müssen wir mit folgenden Parametern erweitern: Texture TexDiffuse; // für den diffusen Kanal Texture TexEnvironment; // die Cubemap-Textur float MtrlReflectionFactor; // Faktor, der die Stärke der Reflexion des Objekts angibt Die Textursampler für die beiden Texturen sehen so aus:

14 sampler SamplerDiffuse = sampler_state
{ texture = <TexDiffuse>; AddressU = WRAP; AddressV = WRAP; AddressW = WRAP; MIPFILTER = LINEAR; MINFILTER = LINEAR; MAGFILTER = LINEAR; }; sampler SamplerEnvironment = sampler_state texture = <TexEnvironment>;

15 Der VertexInput des Vertexshaders muss um ein Texturkoordinatenpaar ergänzt werden
float2 Tex0 : TEXCOORD0; Die Texturkoordinaten der Cubemap werden im Pixelshader berechnet. Dazu müssen die Reflexionsvektoren der einzelnen Vertices übergeben werden (als dreidimensionale Texturkoordinaten, da HLSL keine Semantik für Reflexionskoordinaten hat) Der VertexOutput wird ergänzt: float2 TexCoordDiff : TEXCOORD0; float3 TexCoordCube : TEXCOORD1;

16 VertexOutput VS_Main( VertexInput IN )
{ OUT.TexCoordDiff = IN.Tex0; OUT.TexCoordCube = reflect( CamDirToPos, NormalWorld ); return out; } Die Texturkoordinaten für die diffuse Textur werden einfach weitergereicht, der Reflexionsvektor wird mithilfe der Funktion reflect aus dem Vektor vom Betrachter auf den Vertex und der Vertexnormale errechnet

17 float4 PS_Main( VertexOutput IN ) : COLOR {
Der Pixelshader wird ergänzt: float4 PS_Main( VertexOutput IN ) : COLOR { float4 Environment = texCUBE( SamplerEnvironment, IN.TexCoordCube ); // sampelt Farbe aus der Cubemap mithilfe des Reflektionsvektors float4 Diffuse = IN.Diffuse * tex2D( SamplerDiffuse, IN.TexCoordDiff ); float4 Specular = IN.Specular; Diffuse = lerp( Diffuse, Environment, MtrlReflectionFactor ); // hier wird geregelt, wie stark das Objekt spiegelt. Beim Maximum entspricht die Pixelfarbe der Environmentfarbe, ansonsten der diffusen Farbe. lerp ist eine lineare Interpolation, die beide Farben mithilfe des Materialfaktors mischt return (Diffuse + Specular + LightAmbient); }

18 Nun die umgebende C++-Klasse
#pragma once #include <d3dx9.h> class CEnvEffect { protected: ID3DXEffect* m_Effect; SEnvParameter m_Parameter; public: BOOL Create( LPDIRECT3DDEVICE9 Device ); void Destroy(); void Setup( SEnvParameter& Para ); DWORD Begin( LPDIRECT3DDEVICE9 Device ); void BeginPass(DWORD pass); void EndPass(); void End(); };

19 struct SEnvParameter { //Welche Werte wurden geändert? DWORD Changed; //Licht, x-Wert entspricht Rot, y-Wert Grün, z-Wert blau, w dem Alpha-Kanal D3DXVECTOR4 LightColor; D3DXVECTOR4 LightAmbient; D3DXVECTOR3 LightDir; //Material D3DXVECTOR4 MtrlDiffColor; D3DXVECTOR4 MtrlSpecColor; FLOAT MtrlSpecPower; FLOAT MtrlReflectionFactor; LPDIRECT3DTEXTURE9 TexDiffuse; LPDIRECT3DCUBETEXTURE9 TexEnvironment; }; Lediglich die Matrizen fehlen hier, diese werden direkt aus dem 3D-Device geholt

20 Changed teilt uns mit, welche Variablen geändert wurden, damit wir die Parameterübergabe möglichst effizient halten: enum EEnvParameter { LIGHTCOLOR = 1, LIGHTAMBIENT = 2, LIGHTDIR = 4, MTRLDIFFCOLOR = 8, MTRLSPECCOLOR = 16, MTRLSPECPOWER = 32, MTRLREFLECTIONFACTOR = 64, TEXDIFFUSE = 128, TEXENVIRONMENT = 256 }; Per bitweises Oder können die einzelnen Parameter verknüpft werden (Bsp.: Changed TEXDIFFUSE | TEXENVIRONMENT ; )

21 Nun zu den einzelnen Funktionen
BOOL CEnvEffect::Create( LPDIRECT3DDEVICE9 Device ) { ID3DXBuffer* ErrorBuffer; if( FAILED( D3DXCreateEffectFromFile( Device, "Effects/cube_environmentmapping.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; void CEnvEffect::Destroy() SAFE_RELEASE( m_Effect );

22 void CEnvEffect::Setup( SEnvParameter& 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 & MTRLREFLECTIONFACTOR ) m_Parameter.MtrlReflectionFactor = Para.MtrlReflectionFactor; if( Para.Changed & TEXDIFFUSE ) m_Parameter.TexDiffuse = Para.TexDiffuse; if( Para.Changed & TEXENVIRONMENT ) m_Parameter.TexEnvironment = Para.TexEnvironment; m_Parameter.Changed |= Para.Changed; }

23 DWORD CEnvEffect::Begin( LPDIRECT3DDEVICE9 Device )
{ UINT passes = 0; // Matrizen setzen D3DXMATRIX MatWorld, MatView, MatProj; D3DXMATRIX MatWVP, MatViewInv; // Matrizen aus dem 3D-Device holen Device->GetTransform( D3DTS_WORLD, &MatWorld ); Device->GetTransform( D3DTS_VIEW, &MatView ); Device->GetTransform( D3DTS_PROJECTION, &MatProj ); // Weitergeben der Matrizen an die Effektinstanz 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( "EnvMapping" ); m_Effect->Begin( &passes, 0 ); return passes; }

24 void CEnvEffect::BeginPass(DWORD pass) // Übergibt restliche Parameter an Effektinstanz
{ // 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 & MTRLREFLECTIONFACTOR ) m_Effect->SetFloat( "MtrlReflectionFactor", m_Parameter.MtrlReflectionFactor ); if( m_Parameter.Changed & TEXDIFFUSE ) m_Effect->SetTexture( "TexDiffuse", m_Parameter.TexDiffuse ); if( m_Parameter.Changed & TEXENVIRONMENT ) m_Effect->SetTexture( "TexEnvironment", m_Parameter.TexEnvironment ); m_Effect->BeginPass( pass ); }

25 void CEnvEffect::EndPass()
{ m_Effect->EndPass(); m_Parameter.Changed = 0; } void CEnvEffect::End() m_Effect->End(); Damit wären wir fertig. Allerdings werden bei dieser Methode andere Objekte, die sich in der Szene befinden, nicht reflektiert, da wir nur die statische Cubemap benutzen. Wir müssen also einen Weg finden, auch bewegliche Objekte mit einzubeziehen.

26 Hierzu postieren wir die Kamera in die Mitte unseres Objekts und setzen den Öffnungswinkel der Kamera auf 90°. Dann rendern wir die Szene und drehen die Kamera nach oben, unten, links, rechts und hinten. Das ergibt 6 Seiten einer Cubemap. Wir nutzen die Seiten der Cubemap als Rendertarget und lassen uns jeweils das passende Surface geben, um es als Backbuffer im 3D-Device zu verwenden. Danach müssen wir den (vorher gespeicherten) regulären Backbuffer wieder an das Device übergeben. Die Funktion ID3DXRenderToEnvMap nimmt uns diese beiden Schritte ab. LPDIRECT3DSURFACE9 BackBuffer; D3DSURFACE_DESC BBDesc; 3DDEVICE->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &BackBuffer); BackBuffer->GetDesc (& BBDesc ); BackBuffer->Release();

27 D3DXCreateRenderToEnvMap( 3DDevice, 256, 1, BBDesc
D3DXCreateRenderToEnvMap( 3DDevice, 256, 1, BBDesc.Format, TRUE, D3DFMT_D16, &m_RenderToEnvMap ) ) ) Die Parameter: 1. 3D-Device 2. Größe der gewünschten Textur (hier: 256 Pixel) 3. Anzahl der Mipmaps 4. Pixelformat (hier: das vom ursprünglichen Backbuffer) 5. Flag, das angibt, ob wir Stencil- und Tiefen-Buffer verwenden möchten 6. Format des Tiefen-Stencil-Buffers (hier: 16-Bit-Z-Buffer) 7. das ID3DXRenderToEnvMap-Objekt, das erstellt werden soll D3DXCreateCubeTexture( 3DDevice, 256, 1, D3DUSAGE_RENDERTARGET, BBDesc.Format, D3DPOOL_DEFAULT, &m_CubeTex ) ) ) 2. Größe der einzelnen Seite der Cubemap 4. Verwendungszweck der Textur (-> Rendertarget) 5. Pixelformat der Textur (siehe Backbuffer) 6. Ressourcen-Pool, in dem die Textur abgelegt werden soll 7. Doppelpointer auf das zu erstellende IDirect3DCubeTexture9-Objekt

28 HRESULT RenderIntoCube()
{ HRESULT hr; D3DXMATRIX matProj; // Erstellen einer Matrix, die einen Öffnungswinkel von 90° und ein Seitenverhältnis von 1 hat D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI*0.5f, 1.0f, 0.5f, f); hr = m_RenderToEnvMap->BeginCube( m_CubeTex ); if (FAILED(hr)) return hr; for( UINT i = 0; i < 6; i++ ) // Über Face können wir die einzelnen Seiten der Cubemap definieren: m_RenderToEnvMap->Face( (D3DCUBEMAP_FACES) i, 0 ); D3DXMATRIX matView; // Erstellt eine View-Matrix für alle Seiten der Cubemap: matView = D3DUtil_GetCubeMapViewMatrix((D3DCUBEMAP_FACES) i); // Szene wird gerendert: RenderScene( matView, matProj); } m_RenderToEnvMap->End( 0 ); return S_OK;

29 Damit sind wir (endlich) fertig!
Vielen Dank für die Aufmerksamkeit!


Herunterladen ppt "Environment Mapping."

Ähnliche Präsentationen


Google-Anzeigen