C/C++ & Visual C++

Kurz DirectX (35.)

┌vodem  |  DatovΘ struktury |  Kurz DirectX  |  Downloads  |  Otßzky a odpov∞di

V tΘto lekci se jeÜt∞ vrßtφme k normßlov²m vektor∙m terΘnu. Do te∩ jsme toti₧ tyto vektory poΦφtali velice zjednoduÜen∞ a dß se °φct i nep°esn∞. Dnes si ukß₧eme zp∙sob, jak vektory urΦit p°esn∞ji. V dalÜφ Φßsti lekce tyto vektory zobrazφme nad terΘnem, abychom vid∞li, ₧e jsou skuteΦn∞ sprßvn∞.

35.1. NormßlovΘ vektory (2.)

Do dneÜnφ lekce jsme normßlov² vektor ka₧dΘho vertexu poΦφtali pouze ze dvou sm∞rov²ch vektor∙, kterΘ byly urΦeny polohou danΘho vertexu a jeho sousedy. Tento zp∙sob lze pou₧φt v p°φpad∞, ₧e terΘn nenφ p°φliÜ Φlenit² (tedy nejsou zde nßhlΘ zm∞ny v²Üky vertexu). V tomto p°φpad∞ toti₧ je vypoΦten² normßlov² vektor znaΦn∞ nep°esn². Meznφm p°φpadem je ostrß hrana. Normßla bude kolmß bu∩ k jednΘ nebo k druhΘ rovin∞ (podle toho, kde le₧φ uva₧ovanΘ vertexy), co₧ je samoz°ejm∞ neodpovφdß skuteΦnosti.

Sprßvn∞ °eÜenφ spoΦφvß v tom, ₧e vezmeme v ·vahu vÜechny dφlΦφ normßlovΘ vektory rovin p°ilehl²ch k danΘm vertexu a z t∞chto vektor∙ pak urΦφme pr∙m∞rn² normßlov² vektor.

Tedy pro vyznaΦen² vertex se jeho normßla spoΦφtß jako pr∙m∞r vektor∙ n0 a₧ n5 (d°φv jsme vlastn∞ vzali vektor n1 a ten jsme prohlßsili za normßlu n).

Jak budeme postupovat p°i v²poΦtu? Nebude to nic moc slo₧itΘho. Nejprve vybereme sousedy vertexu, pro kter² chceme normßlu vypoΦφtat. Musφme brßt v ·vahu to, ₧e ne ka₧d² vertex mß vÜech 6 soused∙. Nap°φklad vrcholy na kraji terΘnu budou mφt o dva sousedy mΘn∞, vrcholy na "rohu" dokonce o t°i sousedy mΘn∞.

Postupn∞ tedy otestujeme existenci soused∙ v1 a₧ v6 a rovnou spoΦφtßme sm∞rov² vektor z vektoru v0 v²razem vx - v0 kde vx je v1 a₧ v6. P°itom jeÜt∞ ulo₧φme informaci, ₧e dan² soused skuteΦn∞ existuje a ₧e ho pozd∞ji musφme brßt v ·vahu. K≤d tΘto Φßsti bude vypadat nßsledovn∞:

// Vektor, pro ktery normalu pocitame - stredovy vektor
v0 = m_arTerrain[x][y].vecPos;
// Nyni vybereme ty spravne body okolo stredoveho vektoru
// a rovnou urcime smerove vektory k temto bodum

if(x < m_iTerrainTilesX)
{
  s1 = m_arTerrain[x+1][y].vecPos - v0;
  vecMask |= 0x01;
}

if(y < m_iTerrainTilesY)
{
  s2 = m_arTerrain[x][y+1].vecPos - v0;
  vecMask |= 0x02;
}
if(x > 0 && y < m_iTerrainTilesY)
{
  s3 = m_arTerrain[x-1][y+1].vecPos - v0;
  vecMask |= 0x04;
}
if(x > 0)
{
  s4 = m_arTerrain[x-1][y].vecPos - v0;
  vecMask |= 0x08;
}
if(y > 0)
{
  s5 = m_arTerrain[x][y-1].vecPos - v0;
  vecMask |= 0x10;
}
if(y > 0 && x < m_iTerrainTilesX)
{
  s6 = m_arTerrain[x+1][y-1].vecPos - v0;
  vecMask |= 0x20;
}

Prom∞nnß vecMask je typu unsigned char nebo BYTE. Ve vektorech s1 a₧ s6 jsou ulo₧eny sm∞rovΘ vektory p°φsluÜn²ch vertex∙ v0 - v6.

V dalÜφm kroku vybere sprßvnΘ sousedy a vypoΦteme dφlΦφ normßly. V prom∞nnΘ vecMask je nastaven bit pokud existuje sm∞rov² vektor, tak₧e dva sousednφ bity p°edstavujφ dva sousednφ sm∞rovΘ vektory, ze kter²ch urΦφme normßlu.

// Spocitame normaly pro sousedni dvojice
// s1 a s2 normala

if(vecMask & 0x03)
{
  D3DXVec3Cross(&n, &s1, &s2);
  D3DXVec3Normalize(&n, &n);
  // Vysledny vektor n0
  n0 += n;
  // Pocet vektoru pro prumer
  iNormCount++;
}
// s2 a s3 normala
if(vecMask & 0x06)
{
  D3DXVec3Cross(&n, &s2, &s3);
  D3DXVec3Normalize(&n, &n);
  // Vysledny vektor n0
  n0 += n;
  // Pocet vektoru pro prumer
  iNormCount++;
}
// s3 a s4 normala
if(vecMask & 0x0C)
{
  D3DXVec3Cross(&n, &s3, &s4);
  D3DXVec3Normalize(&n, &n);
  // Vysledny vektor n0
  n0 += n;
  // Pocet vektoru pro prumer
  iNormCount++;
}
// s4 a s5 normala
if(vecMask & 0x18)
{
  D3DXVec3Cross(&n, &s4, &s5);
  D3DXVec3Normalize(&n, &n);
  // Vysledny vektor n0
  n0 += n;
  // Pocet vektoru pro prumer
  iNormCount++;
}
// s5 a s6 normala
if(vecMask & 0x30)
{
  D3DXVec3Cross(&n, &s5, &s6);
  D3DXVec3Normalize(&n, &n);
  // Vysledny vektor n0
  n0 += n;
  // Pocet vektoru pro prumer
  iNormCount++;
}
// s6 a s1 normala
if(vecMask & 0x41)
{
  D3DXVec3Cross(&n, &s6, &s1);
  D3DXVec3Normalize(&n, &n);
  // Vysledny vektor n0
  n0 += n;
  // Pocet vektoru pro prumer
  iNormCount++;
}

Pro ka₧dΘ dva sousednφ sm∞rovΘ vektory spoΦφtßme vektorov² souΦin a v²sledn² vektor normalizujeme na jednotkovou velikost. V prom∞nnΘ n0 je suma vÜech dφlΦφch normßl. Tento vektor v zßp∞tφ pod∞lφme hodnotou iNormCount a zφskßme koneΦn² normßlov² vektor jednoho vertexu!

// ulozime vysledny vektor - udelame prumer vsech dilcich normal
m_arTerrain[x][y].vecNormal = n0 / float(iNormCount);

Jako t°eÜniΦku na dortu vytvo°φme koneΦn² vektor jako pr∙m∞r vÜech dφlΦφch vektor∙ p°ilehl²ch rovin. Tφmto algoritmem spoΦφtßme normßly o hodn∞ p°esn∞ji ne₧ p∙vodnφ verzφ. Na druhou stranu v²poΦet trvß podstatn∞ dΘle, pro ka₧d² vertex toti₧ volßme funkci D3DXVec3Cross() hned 6x!!!

35.2. Zobrazenφ normßlov²ch vektor∙

V druhΘ Φßsti lekce se budeme zab²vat tφm, jak normßly zobrazit u ka₧dΘho vertexu. Normßlu zobrazφme jako Φßru z p°φsluÜnΘho vertexu sm∞rem vzh∙ru. V²sledek pak bude vypadat jako kdyby na terΘnu byla dokonale kolmß trßva. Pro ka₧dou normßlu budeme pot°ebovat dva body (vertexy). V lokaci mßme 128x128 polφΦek tak₧e dohromady to bude 16384 normßl s 32768 vrcholy. Vytvo°φme tedy pro ka₧dou lokaci dalÜφ vertex buffer. Index buffer nebude v tomto p°φpad∞ pot°eba, proto₧e jednotlivΘ Φßry spolu nebudou mφt nic spoleΦnΘho, tudφ₧ je jakßkoliv indexace zbyteΦnß.

struct Location
{
  // Jednoznacne ID lokace (pouze kvuli vypisum)
  WORD m_wID;
  // Index buffer ktery ma indexy v ramci jedny lokace
  IIndexBuffer *pIB;
  // pocet viditelnych listu
  int m_iVisQuads;
  int m_iVisTiles;
  // Minimalni a maximalni pouzity index
  WORD m_iMinIndex;
  WORD m_iMaxIndex;
  // Docasny pointer na otevreny index buffer
  WORD *pIndices;
  // Okrajove body
  BoundingPoint arBounds[4];
  // vertex buffer pro normaly
  IVertexBuffer *pNormalsVB;
};

Tento buffer vytvo°φme v metod∞ CreateQuadTree(). Ka₧dß lokace mß vlastnφ buffer na normßly a velikost jsme odvodili o pßr °ßdk∙ v²Üe:

// Create VB for terrain and copy all locations
if(S_OK == CreateDisplayObject(DISIID_IVertexBuffer, (void**)&m_arLoc[l].pNormalsVB))
{
   dwRet = m_arLoc[l].pNormalsVB->Create(2*LOC_V_SIZE*LOC_V_SIZE*sizeof(VERTEX),
                                       D3DUSAGE_WRITEONLY,VERTEXFORMAT,D3DPOOL_DEFAULT);
   if(dwRet != S_OK)
   {
      XException exp("Terrain is not initialized. Cannot create terrain vertex buffer.", dwRet);
      THROW(exp);
   }
}

Buffer m∙₧eme vytvo°it a naplnit ve stejnΘ smyΦce jako vertex buffer pro samotnΘ vertexy.

m_pTerrainVB->GetBuffer()->Lock(l*LOC_V_SIZE*LOC_V_SIZE*sizeof(VERTEX),
                                 LOC_V_SIZE*LOC_V_SIZE*sizeof(VERTEX), (BYTE**)&pVertices, 0);

m_arLoc[l].pNormalsVB->GetBuffer()->Lock(0,0, (BYTE**) &NormalsVertices, 0);
for(y = m_arLoc[l].arBounds[0].y; y <= m_arLoc[l].arBounds[3].y; y++)
{
   for(x = m_arLoc[l].arBounds[0].x; x <= m_arLoc[l].arBounds[3].x; x++)
   {

      NormalsVertices[n + 0] = m_arTerrain[x][y];
      NormalsVertices[n + 0].dwDiffuse = D3DCOLOR_ARGB(0, 255, 255, 255);
      NormalsVertices[n + 1] = m_arTerrain[x][y];
      NormalsVertices[n + 1].dwDiffuse = D3DCOLOR_ARGB(0, 255, 255, 0);
      NormalsVertices[n + 1].vecPos += m_arTerrain[x][y].vecNormal;

      pVertices[i] = m_arTerrain[x][y];
      if(m_dwFlags & TF_WRITEBUFFERS)
      {
         fprintf(f, "%d: %f, %f, %f\n", i+l*LOC_V_SIZE*LOC_V_SIZE, pVertices[i].vecPos.x,
         pVertices[i].vecPos.y, pVertices[i].vecPos.z);
      }
      i++;

      n += 2;
   }
}

m_arLoc[l].pNormalsVB->GetBuffer()->Unlock();
m_pTerrainVB->GetBuffer()->Unlock()
;

Mod°e je vyznaΦen nov² k≤d ve smyΦce. Nejprve uzamkneme p°φsluÜn² buffer lokace, potΘ ve dvou vno°en²ch smyΦkßch nastavφme dva body pro ka₧dou normßlu. Prvnφ z t∞chto bod∙ je samotn² vertex, kterΘmu normßla pat°φ. Druh² bod je posunut² ve sm∞ru normßly (normßlov² vektor mßme ji₧ spoΦφtan²). Navφc jeÜt∞ ka₧dΘmu bodu p°i°adφme jinou barvu, aby byl vektor p∞kn∞ vid∞t na zelenΘm terΘnu. Na zßv∞r buffer samoz°ejm∞ odemkneme.

Na ·pln² zßv∞r lekce jeÜt∞ ukß₧i, jak normßly vykreslit:

if(m_dwFlags & TF_SHOWNORMALS)
{
   pDis->GetDevice()->SetVertexShader(VERTEXFORMAT);
   pDis->GetDevice()->SetTexture(0, NULL);
   pDis->GetDevice()->SetRenderState(D3DRS_LIGHTING, FALSE);
   for(l = m_iLocCount-1; l >= 0; l--)
   {
      if(m_arLoc[l].m_iVisTiles > 0)
      {
         pDis->GetDevice()->SetStreamSource(0, m_arLoc[l].pNormalsVB->GetBuffer(), sizeof(VERTEX));
         pDis->GetDevice()->DrawPrimitive(D3DPT_LINELIST, 0, LOC_V_SIZE*LOC_V_SIZE);
      }
   }
   pDis->GetDevice()->SetRenderState(D3DRS_LIGHTING, TRUE);
}

Viditelnost normßl je zßvislß na p°φznaku TF_SHOWNORMALS. Nastavφme standardnφ parametry jako vertex shader, texturu na NULL, vypneme sv∞tlo (normßly normßl nevedeme) a pak vykreslφme vektory pro ka₧dou viditelnou lokaci. Vykreslovßnφ normßl nenφ p°φliÜ efektivnφ, ale v tomto p°φpad∞ nßm samoz°ejm∞ nejde o v²kon.

35.3. Zßv∞r

Na zßv∞r tu mßme obrßzek terΘnu s vykreslen²mi normßlov²mi vektory:

P°φklad si samoz°ejm∞ m∙₧ete stßhnout v sekci Downloads. Vykreslovßnφ normßlov²ch vektor∙ lze zapnout a vypnout klßvesou U.

T∞Üφm se p°φÜt∞ nashledanou.

 

Ond°ej BuriÜin a Ji°φ Formßnek