Witam.
Na shaderach znam się słabo, czuję się w tym temacie jeszcze dość niepewnie.
Chciałem odpalić sampla dołączonego do DirectX 9 SDK o nazwie ParallaxOcclusionMapping (tutaj też o nim piszą:
http://msdn.microsoft.com/en-us/library/ee418710%28VS.85%29.aspx) na moim Code::Blocks. Oczywiście podłączyłem wcześniej biblioteki DirectX i już kompilowałem tutaj i uruchamiałem różne inne shadery i aplikacje.
Problem pojawił się natomiast w tym, że w samplu od MS znalazła się większa liczba innych bibliotek i powiedzmy że nadmiarowego kodu.
Uznałem więc że prościej będzie użyć szkieletu aplikacji która już mi w C::B zadziałała (po bardzo drobnych zmianach) a też używała sobie shadera (mowa tutaj o
http://www.dhpoware.com/demos/d3d9NormalMapping.html).
Oczywiście wprowadziłem drobne zmiany w kodzie, załadowałem siatkę zamiast generować sześcian, wczytałem tekstury, zdaje mi się, że dobrze podałem parametry dla shadera (wzorując się na samplu z DirectX SDK) - najprędzej mogłem rympsnąć się przy podawaniu macierzy, bądź wartości takich jak g_fHeightScale.
Po paru próbach wreszcie odpaliłem program bez błędów i bez komunikatów w stylu informacji o niemożliwości załadowania shadera, ale moim oczom (no może nie odrazu, musiałem trochę poobracać się w scenie - tak dla uściślenia) ukazał się nie obiekcik z nałożonymi normalką i teksturą, a czarny i groźny obiekt bez tekstur i bez normal map:

Jako że jestem raczej początkujący, nie bardzo wiem co tutaj skopałem.
Program się kompiluje, pewnie coś źle poustawiałem z shaderem.
Myślę, że gdyby to był problem z macierzami, nie pokazałby mi się w ogóle model a jego pozycja itd. są dobre.
Może z jakiś względów światło go nie oświetla, albo shaderowi brakuje / dostał jakieś błędne/niekompletne dane?
Być może ktoś z większym doświadczeniem coś tutaj wyłapie, sam próbowałem zmieniać przy macierzach, ale wtedy całkowicie gubię model.
Podmieniałem też siatkę z .x na sześcian wklepany 'z palca' - ten sam upiornie czarny efekt.
Upewniłem się też że ścieżki do tekstur i obiektu są dobre.
Być może ktoś wie co to za błąd po samych objawach (załączonym screenie) i kod mu nie potrzebny by stwierdzić gdzie znajduje się problem, ale załączam prawie cały kod (poprzez ... skróciłem miejsca raczej nieistotne, ale mogę je wkleić jakby były jednak potrzebne):
http://pastebin.com/f14359ebeJak i miejsca na które chyba lepiej zwrócić większą uwagę (podejrzewam że gdzieś tutaj może siedzieć licho):
1. Mocniej pozmieniana funkcja renderująca i ustawiająca parametry shadera:
void RenderTUT()
{
static D3DXMATRIX world, view, proj;
static D3DXMATRIX xRot, yRot, translation;
static D3DXMATRIX worldViewProjectionMatrix;
static D3DXMATRIX worldInverseTransposeMatrix;
// Calculate the perspective projection matrix.
D3DXMatrixPerspectiveFovLH(&proj, CAMERA_FOVY,
static_cast<float>(g_windowWidth) / static_cast<float>(g_windowHeight),
CAMERA_ZNEAR, CAMERA_ZFAR);
// Calculate the view matrix.
D3DXMatrixLookAtLH(&view, &g_cameraPos, &D3DXVECTOR3(0.0f, 0.0f, 0.0f),
&D3DXVECTOR3(0.0f, 1.0f, 0.0f));
// Calculate world matrix to transform the cube.
D3DXMatrixRotationX(&xRot, D3DXToRadian(g_pitch));
D3DXMatrixRotationY(&yRot, D3DXToRadian(g_heading));
D3DXMatrixMultiply(&world, &yRot, &xRot);
D3DXMatrixTranslation(&translation, g_cubePos.x, g_cubePos.y, g_cubePos.z);
D3DXMatrixMultiply(&world, &world, &translation);
// Calculate combined world-view-projection matrix.
worldViewProjectionMatrix = world * view * proj;
// Calculate the transpose of the inverse of the world matrix.
D3DXMatrixInverse(&worldInverseTransposeMatrix, 0, &world);
D3DXMatrixTranspose(&worldInverseTransposeMatrix, &worldInverseTransposeMatrix);
/*
float lightDir[4];
lightDir[0]=g_light.dir[0];
lightDir[1]=g_light.dir[1];
lightDir[2]=g_light.dir[2];
lightDir[3]=0.0f;*/
D3DXVECTOR3 lightDir;
lightDir.x=g_light.dir[0];
lightDir.y=g_light.dir[1];
lightDir.z=g_light.dir[2];
lightDir[3]=0.0f;
D3DXCOLOR vLightDiffuse;
float g_fLightScale = 1.0f;
vLightDiffuse = g_fLightScale * D3DXCOLOR( 1, 1, 1, 1 );
g_pEffect->SetValue( "g_LightDir", &lightDir, sizeof( D3DXVECTOR3 ) );
g_pEffect->SetValue( "g_LightDiffuse", &vLightDiffuse, sizeof( D3DXVECTOR4 ) );
// Set the matrices for the shader.
//g_pEffect->SetMatrix("worldMatrix", &world);
g_pEffect->SetMatrix("g_mWorldViewProjection", &worldViewProjectionMatrix);
g_pEffect->SetMatrix( "g_mWorld", &world );
g_pEffect->SetMatrix( "g_mView", &view );
D3DXVECTOR4 vEye;
/*D3DXVECTOR3 vTemp = ( *g_Camera.GetEyePt() );
vEye.x = vTemp.x;
vEye.y = vTemp.y;
vEye.z = vTemp.z;
vEye.w = 1.0;*/
vEye.x = g_cameraPos.x;
vEye.y = g_cameraPos.y;
vEye.z = g_cameraPos.z;
vEye.w = 1.0;
g_pEffect->SetVector( "g_vEye", &vEye );
//strzal ;-)
float g_fHeightScale = 100.0f;
g_pEffect->SetValue( "g_fHeightMapScale", &g_fHeightScale, sizeof( float ) );
g_pEffect->SetTechnique( "RenderSceneWithPOM" );
/*
UINT iPass, cPasses;
g_pEffect->Begin( &cPasses, 0 );
for( iPass = 0; iPass < cPasses; iPass++ )
{
g_pEffect->BeginPass( iPass );
g_pMesh->DrawSubset( 0 );
g_pEffect->EndPass();
}
g_pEffect->End();
}
*/
g_pDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(100, 149, 237), // CornflowerBlue
1.0f, 0);
if (FAILED(g_pDevice->BeginScene()))
return;
UINT totalPasses;
if (SUCCEEDED(g_pEffect->Begin(&totalPasses, 0)))
{
for( UINT iPass = 0; iPass < totalPasses; iPass++ )
{
g_pEffect->BeginPass( iPass );
g_pMesh->DrawSubset( 0 );
g_pEffect->EndPass();
}
g_pEffect->End();
}
g_pDevice->EndScene();
g_pDevice->Present(0, 0, 0, 0);
}
2. Funkcja ładująca shader:
bool LoadShader(const char *pszFilename, LPD3DXEFFECT &pEffect)
{
ID3DXBuffer *pCompilationErrors = 0;
DWORD dwShaderFlags = D3DXFX_NOT_CLONEABLE | D3DXSHADER_NO_PRESHADER;
// Both vertex and pixel shaders can be debugged. To enable shader
// debugging add the following flag to the dwShaderFlags variable:
// dwShaderFlags |= D3DXSHADER_DEBUG;
//
// Vertex shaders can be debugged with either the REF device or a device
// created for software vertex processing (i.e., the IDirect3DDevice9
// object must be created with the D3DCREATE_SOFTWARE_VERTEXPROCESSING
// behavior). Pixel shaders can be debugged only using the REF device.
//
// To enable vertex shader debugging add the following flag to the
// dwShaderFlags variable:
// dwShaderFlags |= D3DXSHADER_FORCE_VS_SOFTWARE_NOOPT;
//
// To enable pixel shader debugging add the following flag to the
// dwShaderFlags variable:
// dwShaderFlags |= D3DXSHADER_FORCE_PS_SOFTWARE_NOOPT;
HRESULT hr = D3DXCreateEffectFromFile(g_pDevice, pszFilename, 0, 0,
dwShaderFlags, 0, &pEffect, &pCompilationErrors);
if (FAILED(hr))
{
if (pCompilationErrors)
{
std::string compilationErrors(static_cast<const char *>(
pCompilationErrors->GetBufferPointer()));
pCompilationErrors->Release();
throw std::runtime_error(compilationErrors);
}
}
if (pCompilationErrors)
pCompilationErrors->Release();
//CHANGES
D3DXCreateTextureFromFile( g_pDevice, "C:/Users/X/Desktop/Content/Textures/color_map.jpg", &g_pBaseTexture );
D3DXCreateTextureFromFile( g_pDevice, "C:/Users/X/Desktop/Content/Textures/normal_map.jpg", &g_pBaseNormal );
g_pEffect->SetTexture( "g_baseTexture", g_pBaseTexture );
g_pEffect->SetTexture( "g_nmhTexture", g_pBaseNormal );
return pEffect != 0;
}
3. Prawie nie zmieniona funkcja ładowania siatki z sampla DirectX:
HRESULT LoadMesh( IDirect3DDevice9* pd3dDevice, ID3DXMesh** ppMesh )
{
ID3DXMesh* pMesh = NULL;
HRESULT hr;
//====================================================================//
// Load the mesh with D3DX and get back a ID3DXMesh*. For this //
// sample we'll ignore the X file's embedded materials since we know //
// exactly the model we're loading. See the mesh samples such as //
// "OptimizedMesh" for a more generic mesh loading example. //
//====================================================================//
HRESULT hResult = D3DXLoadMeshFromX( EARTH_MESH, D3DXMESH_SYSTEMMEM, g_pDevice, NULL,
NULL, NULL, NULL, &pMesh );
// Create a new vertex declaration to hold all the required data
const D3DVERTEXELEMENT9 vertexDecl[] =
{
{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
{ 0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
{ 0, 20, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 },
{ 0, 32, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0 },
{ 0, 44, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 0 },
D3DDECL_END()
};
LPD3DXMESH pTempMesh = NULL;
// Clone mesh to match the specified declaration:
if( FAILED( pMesh->CloneMesh( pMesh->GetOptions(), vertexDecl, pd3dDevice, &pTempMesh ) ) )
{
SAFE_RELEASE( pTempMesh );
return E_FAIL;
}
//====================================================================//
// Check if the old declaration contains normals, tangents, binormals //
//====================================================================//
bool bHadNormal = false;
bool bHadTangent = false;
bool bHadBinormal = false;
D3DVERTEXELEMENT9 vertexOldDecl[ MAX_FVF_DECL_SIZE ];
if( pMesh && SUCCEEDED( pMesh->GetDeclaration( vertexOldDecl ) ) )
{
// Go through the declaration and look for the right channels, hoping for a match:
for( UINT iChannelIndex = 0; iChannelIndex < D3DXGetDeclLength( vertexOldDecl ); iChannelIndex++ )
{
if( vertexOldDecl[iChannelIndex].Usage == D3DDECLUSAGE_NORMAL )
{
bHadNormal = true;
}
if( vertexOldDecl[iChannelIndex].Usage == D3DDECLUSAGE_TANGENT )
{
bHadTangent = true;
}
if( vertexOldDecl[iChannelIndex].Usage == D3DDECLUSAGE_BINORMAL )
{
bHadBinormal = true;
}
}
}
if( pTempMesh == NULL && ( bHadNormal == false || bHadTangent == false || bHadBinormal == false ) )
{
// We failed to clone the mesh and we need the tangent space for our effect:
return E_FAIL;
}
//==============================================================//
// Generate normals / tangents / binormals if they were missing //
//==============================================================//
SAFE_RELEASE( pMesh );
pMesh = pTempMesh;
if( !bHadNormal )
{
// Compute normals in case the meshes have them
D3DXComputeNormals( pMesh, NULL );
}
DWORD* rgdwAdjacency = NULL;
rgdwAdjacency = new DWORD[ pMesh->GetNumFaces() * 3 ];
if( rgdwAdjacency == NULL )
{
return E_OUTOFMEMORY;
}
pMesh->GenerateAdjacency( 1e-6f, rgdwAdjacency );
// Optimize the mesh for this graphics card's vertex cache
// so when rendering the mesh's triangle list the vertices will
// cache hit more often so it won't have to re-execute the vertex shader
// on those vertices so it will improve perf.
pMesh->OptimizeInplace( D3DXMESHOPT_VERTEXCACHE, rgdwAdjacency, NULL, NULL, NULL );
if( !bHadTangent || !bHadBinormal )
{
ID3DXMesh* pNewMesh;
// Compute tangents, which are required for normal mapping
if( FAILED( D3DXComputeTangentFrameEx( pMesh, D3DDECLUSAGE_TEXCOORD, 0, D3DDECLUSAGE_TANGENT, 0,
D3DDECLUSAGE_BINORMAL, 0,
D3DDECLUSAGE_NORMAL, 0, 0, rgdwAdjacency, -1.01f,
-0.01f, -1.01f, &pNewMesh, NULL ) ) )
{
return E_FAIL;
}
SAFE_RELEASE( pMesh );
pMesh = pNewMesh;
}
// SAFE_DELETE_ARRAY( rgdwAdjacency );
*ppMesh = pMesh;
return S_OK;
}
Oczywiście LoadShader i LoadMesh odpalam jednorazowo przed pierwszym wywołaniem funkcji renderującej.
A i jeszcze shader (zupełnie nie zmieniany shader z sampla DirectX, no ale dołączę by wszystko było pod ręką):
http://pastebin.com/f5b64f934Gdyby ktoś miał pomysł co szwankuje, albo gdybym czegoś ważnego nie dopisał itd., będę wdzięczny.