Autor Wątek: D3D11 - błędne działający depth / z test  (Przeczytany 1012 razy)

Offline 0x46415243494...

  • Użytkownik

# Listopad 08, 2012, 16:11:51
Witam
Problem, tak jak w tytule - polega na tym, że test z buffera nie działa poprawnie. Załączam: www.pgd.boo.pl/Projects.rar, żeby samemu zobaczyć (wymagane dx11 + vs/ps_4_0).

Wszystko ustawiłem, jak w dokumentacji przykazano, ale nie jest dobrze. Jeden mesh obraca się dookoła drugiego, mesh środkowy jest renderowany jako pierwszy, obracający się jako drugi i jak widać ten środkowy/pierwszy jest zawsze przed drugim, czyli objaw jest taki, że raz zapisany piksel do zbuffera już nie jest później modyfikowany (w przypadku kilku drawów), nawet gdy facey są przed - cóż funkcja testująca nie działa jak trzeba. Już pomijam fakt, że na poziomie jednego mesha prymitywy są źle przysłaniane, co dobrze widać, gdy tygrys jest zwrócony ogonem, bo ogon chowa się w tułowiu.


Zaraz dam garść kodu, jeszcze moje pytanie odnośnie z-buffera. Otóż definiując macierz projekcji określam znear i zfar, które przecież potem definiują ten zakres przestrzeni, która będzie wyznaczała wartości w zbufferze (znear jako 0.0f, zfar jako 1.0f), ale... ale na poziomie d3d11 ta przestrzeń nie jest definiowana, bo przecież macierze istnieją jedynie na poziomie shadera, równie dobrze mogło by ich nie być... a więc d3d11 nie jest w stanie policzyć wartości do zbuffera, w takim wypadku domyśliłem się, że wartość ta musi być liczona właśnie w momencie projekcji punktu 3d do 2d w shaderze. To tej pory wiedziałem, że projekcja przekształca koordynat float3(x,y,z) do float2(x,y) w d3d11 mapowane do (-1,1) (w d3d9 były to koordynaty rozdzielczości, tak?). Ale przecież poza float2(x,y) w przekształconym wierzchołku musi być informacja o głębi, gdyż tylko i wyłącznie tu może być policzona. W każdym tutorialu na wyjście pozycji vertex shadera definiuje się float4, więc domyślam się, że float4-z zawiera głębokość, czego wcześniej nie byłem świadomy, która trafia do z-testu, a potem przekazywana jest do pixel shadera, jako, że zwyczajnie pozycję przepisuję. Dobrze rozumuję, czy tkwię w błędzie?
We wszystkich źródłach, które do tej pory czytałem (no może nie było tego za dużo :>) zawsze było jedynie powiedziane, że w wyniku transformacji przez macierz projekcji, punkt jest rzutowany z 3d do 2d, a nigdy nie wspomina się o zapisie głębokości w tym miejscu, ale w sumie człowiek do pewnych rzeczy musi dojść sam...

Aha i czego używacie do debugowania shaderów, PIX jest nie sprawny.

No i potrzebny kod:
// crete depth-buffer
  const DXGI_FORMAT depth_buffer_format = DXGI_FORMAT_D32_FLOAT;
  D3D11_TEXTURE2D_DESC tx_desc;
  tx_desc.Width = width;
  tx_desc.Height = height;
  tx_desc.MipLevels = 1;
  tx_desc.ArraySize = 1;
  tx_desc.Format = depth_buffer_format;
  tx_desc.SampleDesc.Count = 1;
  tx_desc.SampleDesc.Quality = 0;
  tx_desc.Usage = D3D11_USAGE_DEFAULT;
  tx_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
  tx_desc.CPUAccessFlags = 0;
  tx_desc.MiscFlags = 0;
  hr = d3d11.dev->CreateTexture2D(&tx_desc, NULL, &buffer);
  if(FAILED(hr))
  {
    releaseD3D11Interfaces();
    return ILU_APIERROR;
  }
  D3D11_DEPTH_STENCIL_VIEW_DESC db_desc;
  db_desc.Format = depth_buffer_format;
  db_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
  db_desc.Flags = 0;
  db_desc.Texture2D.MipSlice = 0;
  hr = d3d11.dev->CreateDepthStencilView(buffer, &db_desc, &d3d11.depth_buffer);
  buffer->Release();
  if(FAILED(hr))
  {
    releaseD3D11Interfaces();
    return ILU_APIERROR;
  }
 
 
  // create depth stencil state
  D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
  depthStencilDesc.DepthEnable = TRUE;
depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
depthStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;

  depthStencilDesc.StencilEnable = TRUE;
depthStencilDesc.StencilReadMask = 0;
depthStencilDesc.StencilWriteMask = 0;

  // front facing
depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
 
  // back facing
depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

  hr = d3d11.dev->CreateDepthStencilState(&depthStencilDesc, &d3d11.depth_state);
if(FAILED(hr))
{
releaseD3D11Interfaces();
    return ILU_APIERROR;
}

 

  // crete rasterizer state
  D3D11_RASTERIZER_DESC rasterDesc;
  rasterDesc.FillMode = D3D11_FILL_SOLID;
rasterDesc.CullMode = D3D11_CULL_BACK;
  rasterDesc.FrontCounterClockwise = FALSE;
rasterDesc.DepthBias = 0;
rasterDesc.DepthBiasClamp = 0.0f;
  rasterDesc.SlopeScaledDepthBias = 0.0f;
rasterDesc.DepthClipEnable = TRUE;
  rasterDesc.ScissorEnable = FALSE;
  rasterDesc.MultisampleEnable = FALSE;
rasterDesc.AntialiasedLineEnable = FALSE;
hr= d3d11.dev->CreateRasterizerState(&rasterDesc, &d3d11.rasterizer);
if(FAILED(hr))
{
releaseD3D11Interfaces();
    return ILU_APIERROR;
}

//...

// set used buffers and states
  d3d11.dev_con->OMSetRenderTargets(1, &d3d11.render_buffer, d3d11.depth_buffer);
  d3d11.dev_con->OMSetDepthStencilState(d3d11.depth_state, 1);
  d3d11.dev_con->RSSetState(d3d11.rasterizer);
  d3d11.dev_con->PSSetSamplers(0, 1, &d3d11.sampler);

// i czyszczenie

ILU_RESULT ILU_CALL Render::frameBegin(bool clear_depth, bool clear_target)
{
  // clear render target buffer
  if(clear_target) d3d11.dev_con->ClearRenderTargetView(d3d11.render_buffer, (float*)&d3d11.background_color);

  // clear depth buffer
  if(clear_depth) d3d11.dev_con->ClearDepthStencilView(d3d11.depth_buffer, D3D11_CLEAR_DEPTH, 1.0f, 0);


// aha no i transformacje
bool onFrameRender()
{
  // setup matrix
 
  DWORD time = timeGetTime();
  // calc angle position in deg based on time
  float rotateX = (time / 10) % 360;
  float rotateY = (time / 25) % 360;
  float rotateZ = (time / 50) % 360;
  // convert deg to rad
  rotateX *= (XM_PI / 180.0f);
  rotateY *= (XM_PI / 180.0f);
  rotateZ *= (XM_PI / 180.0f);

  const float fov = 70.0f * (XM_PI / 180.0f);
 
  XMFLOAT4
    eyePos(0.0f, 0.0f, 4.0f, 0.0f),
    viewPos(0.0f, 0.0f, 0.0f, 0.0f),
    upDir(0.0f, 1.0f, 0.0f, 0.0f);

  struct MatrixBufferData {
    XMFLOAT4X4 worldviewproj;
    XMFLOAT4X4 world;
    XMFLOAT4   eyePos;
    XMFLOAT3X3 normal;
  };
  MatrixBufferData* bufData = (MatrixBufferData*)matrixBuffer->updateData();
 
  // calc world transformation
  XMMATRIX _world = XMMatrixRotationY(rotateY);
  XMStoreFloat4x4(&bufData->world, _world);
 
  // calc view & proj transform
  XMMATRIX _view = XMMatrixLookAtLH(XMLoadFloat4(&eyePos), XMLoadFloat4(&viewPos), XMLoadFloat4(&upDir));
  XMMATRIX _proj = XMMatrixPerspectiveFovLH(fov, 4.0f / 3.0f, 0.5f, 25.0f);
  _proj = XMMatrixMultiply(XMMatrixMultiply(_world, _view), _proj); // world * view * proj
  XMStoreFloat4x4(&bufData->worldviewproj, _proj); // store result in GPU matrix buffer
 
  // set eye pos
  bufData->eyePos = eyePos;

  // calc normal vectors transformation
  XMVECTOR det;
  _world = XMMatrixInverse(&det, _world);
  _world = XMMatrixTranspose(_world);
  XMStoreFloat3x3(&bufData->normal, _world);


  if(ILU_FAILED(render->frameBegin())) return false;


  render->render(mesh, material, texture, vdesc, vsh, psh, (const DataBuffer**)&matrixBuffer, 1, NULL, 0);

  // update matrices again
  bufData = (MatrixBufferData*)matrixBuffer->updateData();
  _world = XMMatrixTranslation(2.0f, 0.0f, 0.0f);
  _world = XMMatrixMultiply(_world, XMMatrixRotationY(rotateZ));
  XMStoreFloat4x4(&bufData->world, _world);
  _proj = XMMatrixPerspectiveFovLH(fov, 4.0f / 3.0f, 0.001f, 25.0f);
  _proj = XMMatrixMultiply(XMMatrixMultiply(_world, _view), _proj); // world * view * proj
  XMStoreFloat4x4(&bufData->worldviewproj, _proj); // store result in GPU matrix buffer
 

  render->render(mesh, material, texture, vdesc, vsh, psh, (const DataBuffer**)&matrixBuffer, 1, NULL, 0);


  if(ILU_FAILED(render->frameEnd())) return false;

  if(ILU_FAILED(render->presentFrame())) return false;

  return true; // render next frame
}

Jakieś pomysły?

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Krzysiek K.

  • Moderator
    • DevKK.net

# Listopad 08, 2012, 16:39:30
Przede wszystkim bym zrezygnował z formatu D32 na rzecz D24_X8 - czyli stary i lubiany format z DX9. A po drugie, właczony stencil test (co to właściwie ma tu robić?) przy formacie D32 (który stencila przecież nie ma) jest co najmniej podejrzane.

Offline 0x46415243494...

  • Użytkownik

# Listopad 08, 2012, 17:03:38
Tak... kombinowałem z tymi ustawieniami...
Próbowałem, różnych formatów, początkowo miałem jakiś inny, z  DXGI_FORMAT_D24_UNORM_S8_UINT, także próbowałem, format nie wpływa na problem, co najwyżej createBuffer kończy się FAILem.
Ustawień D3D11_DEPTH_STENCIL_DESC także próbowałem przeróżnych. StencilEnable także nie wpływa w żaden sposób na problem.

Jak np dam funkcję D3D11_COMPARISON_ALWAYS, wtedy odwrotnie ten obracający się dookoła jest zawsze przed, co prawidłowo wynika z działania tej funkcji, bo ten obracający się dookoła rysowany jest jako drugi.

Problem leży w funkcji testującej. Wydaje mi się że głębokość jest mapowana odwrotnie, czyli to co powinno być przykryte jest pokazane, gdyby nie było usuwało prymitywów tyłem, pewnie widziałbym zawsze te prymitywy przykryte. W mnie wartość 0 określa znear, a najwyższa zfar, bufor czyszczę 1.0f, czyli odległością najdalszą, aczkolwiek funkcja testujący widzi to chyba odwrotnie.

EDIT:
zgadza się jak dałem D3D11_FILL_NULL, widzę wtedy te "wewnętrzne" prymitywy. zamiast te które je przykryły, a całość jest o tyle dziwna, że ta reguła obowiązuje tylko w przypadku pojedynczego drawIndexed, gdy gdy rysuję drugiego tygrysa (drugi drawIndexed) ten zawsze jest z tyłu, obojętnie czy z w rzeczywistości powinien być przed czy za

EDIT:
nadal nie uzyskałem odpowiedzi na to w którym miejscu obliczana jest wartość z-depth, czy jest tak jak ja mówiłem, czyli podczas mnożenia przez macierz projekcji? bo to chyba tutaj jest problem, wartość z-depth jest błędna


EDIT: jest 23:59 przysiadłem jeszcze raz otworzyłem projekt i znalazłem...
void ILU_CALL Render::setViewportArea(float x, float y, float width, float height)
{
  D3D11_VIEWPORT viewport;
  viewport.TopLeftX = x;
  viewport.TopLeftY = y;
  viewport.Width = width;
  viewport.Height = height;
  viewport.MinDepth = 0.0f;
  viewport.MaxDepth = 0.0f; // <- !!!! <... wiązanka niecenzuralnych słów ...>

  d3d11.dev_con->RSSetViewports(1, &viewport);
}
i pół dnia w plecy
« Ostatnia zmiana: Listopad 09, 2012, 01:05:49 wysłana przez 0x464152434941525A5E5E »