.[ ČeskéHry.cz ].
Jak správně zobrazit 2D bitmapu pomocí 3D rozhraní?

 
odeslat nové téma   Odpovědět na téma    Obsah fóra České-Hry.cz -> 3D API / 3D Enginy
Zobrazit předchozí téma :: Zobrazit následující téma  
Autor Zpráva
Al



Založen: 23. 10. 2007
Příspěvky: 196

PříspěvekZaslal: 9. březen 2012, 19:27:04    Předmět: Jak správně zobrazit 2D bitmapu pomocí 3D rozhraní? Odpovědět s citátem

Před lety jsem dělal emulátor Sam Coupé/ZX Spectrum. Je to už asi 15 let stará záležitost a dodnes jsem nevydal žádnou verzi pro Windows, protože když jsem ěnkdy před 10 lety dělal konverzi do Visual Studia, celé se to nějak rozsypalo a nechtělo to chodit korektně. Teď jsem "slavnostně" po 9 letech hledání opravil jednu klíčovou chybu v jádru emulátoru a jede mi to už stabilně. A řeším teď kreslení. Jak to kreslit?

Emulátor "vyrábí" bitmapovou obrazovku v rozlišení 640x240, kterou potřebuju roztáhnout do AR 4:3 na obrazovku. (Umím to vygenerovat v 8, 16 i 32bitové hloubce.) Vytvořil jsem k tomu dva "drivery" - jeden používá DirectDraw 6 a pěkně kreslí, jenže na Windows Vista/7 to blokuje Aero, čili při zpanutí mého progrmau Windows přestane kreslit průhledná okna. To považuju za nevyhovující.

Druhý "driver" mám v Direct3D 9. Udělám tam dva trojúhelníky a jednu texturu a obraz z prgoramu dávám do té textury a kreslím. Má to zásadní mouchu, která se mi nedaří opravit: Nepasuje mi to úplně přesně pixel na pixel. Moje otázka je tedy jasná: Kde je ta zatracená chyba a jak ho přimět, aby mi přenášel 2D bitmapu pomocí Direct3D nebo OpenGL úplně přesně pixel na pixel. (Ano, jsem z toho na prášky a klidně bych tam dal OpenGL, kdyby fungovalo.) Předpokládám, že pro lidi, co dělají dnes a denně v Direct3D, to bude věc na minutku, takže přikládám i zdroják. (Já jsem naposledy něco reálně dělal v Direct3D před 7 lety, kdy jsem sestavil tento kód, který nefunguje, a pak před tím v roce 2001, kdy jsem ještě dělal v Illusion Softworks, ale to už jsem dávno zapomněl.)

1. krok - vytvořím device:

kód:
  D3DPRESENT_PARAMETERS d3dpp;
  ZeroMemory(&d3dpp, sizeof(d3dpp));
  d3dpp.BackBufferWidth=640;  //0=vezmi velikost client area okna
  d3dpp.BackBufferHeight=240; //0=vezmi velikost client area okna
  d3dpp.BackBufferFormat=D3DFMT_UNKNOWN; //unknown=kompatibilni s desktopem
  d3dpp.Windowed=TRUE;
  d3dpp.SwapEffect=D3DSWAPEFFECT_DISCARD;
  d3dpp.hDeviceWindow=hwnd;
  d3dpp.PresentationInterval=D3DPRESENT_INTERVAL_IMMEDIATE;

  if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3ddev))) return E_FAIL;


2. krok - vytvořím vertex buffer:

kód:
  const int w = 640;
  const int h = 240;
  vertices[0].pos = D3DXVECTOR3(0,0,0); // top left
  vertices[1].pos = D3DXVECTOR3(0,h,0); // bottom left
  vertices[2].pos = D3DXVECTOR3(w,0,0); // top right
  vertices[3].pos = D3DXVECTOR3(w,h,0); // bottom right

  const float maxx = 639.0f/640;
  const float maxy = 239.0f/240;
  vertices[0].tu = 0.0f; vertices[0].tv = 0.0f; // low left
  vertices[1].tu = 0.0f; vertices[1].tv = maxy; // high left
  vertices[2].tu = maxx; vertices[2].tv = 0.0f; // low right
  vertices[3].tu = maxx; vertices[3].tv = maxy; // high right

  for(int i=0;i<4;i++) vertices[i].rhw=1;

  if(FAILED(d3ddev->CreateVertexBuffer(sizeof(vertices), D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, 0))) return E_FAIL;

  void *pVertices;
  if(FAILED(g_pVB->Lock(0,0,&pVertices,0))) return E_FAIL;
  memcpy(pVertices,vertices,sizeof(vertices));
  g_pVB->Unlock();


3. krok - nastavím ještě nějaké flagy a vytvořím texturu:

kód:
  tryx(d3ddev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE));
  tryx(d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE));

  tryx(d3ddev->CreateTexture(640, 240, 1, D3DUSAGE_DYNAMIC, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &tex, 0));
  tex->GetSurfaceLevel(0,&texsurf);


No a pak se to kreslí 50x za sekundu takhle:

kód:
  if(SUCCEEDED(tex->LockRect(0,&locked,0,D3DLOCK_DISCARD|D3DLOCK_NOSYSLOCK))) {
    BYTE *wpos = (BYTE*)locked.pBits;
    for(int y=0; y<240; y++,wpos+=locked.Pitch) memcpy(wpos, osvideo.linebuf[y], 640*4);
    try(tex->UnlockRect(0));
  }

  if(SUCCEEDED(d3ddev->BeginScene())) {
    try(d3ddev->SetTexture(0,tex));
    d3ddev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
    d3ddev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);

    d3ddev->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
    d3ddev->SetFVF(D3DFVF_CUSTOMVERTEX);
    d3ddev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); //nakresli obdelnik
   
    d3ddev->EndScene();
  }

  d3ddev->Present(0,0,0,0);
Návrat nahoru
Zobrazit informace o autorovi Odeslat soukromou zprávu
igor



Založen: 28. 07. 2007
Příspěvky: 196

PříspěvekZaslal: 9. březen 2012, 19:41:56    Předmět: Odpovědět s citátem

Já nemám s D3D9 zkušenosti, ale nemohlo by pomoct tohle? http://msdn.microsoft.com/en-us/library/windows/desktop/bb219690 V D3D10, D3D11 už to pasuje přesně, OpenGL nevím.
Návrat nahoru
Zobrazit informace o autorovi Odeslat soukromou zprávu
Al



Založen: 23. 10. 2007
Příspěvky: 196

PříspěvekZaslal: 9. březen 2012, 21:47:35    Předmět: Odpovědět s citátem

Jo, pomohlo mi to. Tady to odečtení 0.5f od každé souřadnice, jak píšou na tom webu Microsoftu, mi tam chybělo. A ještě dvě další drobnosti. Souřadnice textury musejí být přesně [0-1]. A až jsem to opravil, ukázalo se, že byla taky nepřesně nastavená velikost okna. Smile

Mám to v DX 9, souvislost s DX10 a 11 jsem nepochopil. V DX 8 jsem to nezkoušel dělat, mám dojem, že tam nějak vůbec není ta podpora custom vertexů.
Návrat nahoru
Zobrazit informace o autorovi Odeslat soukromou zprávu
igor



Založen: 28. 07. 2007
Příspěvky: 196

PříspěvekZaslal: 9. březen 2012, 22:08:50    Předmět: Odpovědět s citátem

V D3D10 to změnili a ten shift tam nutný není. http://msdn.microsoft.com/en-us/library/windows/desktop/bb205073 Tuším, že v OpenGL to je taky jako v té desítce, ale ruku do ohně za to teď nedám.
Návrat nahoru
Zobrazit informace o autorovi Odeslat soukromou zprávu
VladR



Založen: 30. 07. 2007
Příspěvky: 1322
Bydliště: Greater New York City Area

PříspěvekZaslal: 9. březen 2012, 22:29:12    Předmět: Odpovědět s citátem

Al napsal:
Mám to v DX 9, souvislost s DX10 a 11 jsem nepochopil. V DX 8 jsem to nezkoušel dělat, mám dojem, že tam nějak vůbec není ta podpora custom vertexů.
To mas velmi zly dojem.

Pod DX8.1 som sa Custom Vertexov nadeklaroval stovky, takze rozhodne tam podpora je - dokonca je na vyssej urovni ako v XNA, kde v zaujme zjednodusenia API uz nie su mozne kombinacie, ktore predtym hardwaru hodne pomohli (vykonovo).

Half-Texel Offset pod OpenGL , aspon v dobe, ked som pod OpenGL robil, nebol. Velmi jasne si spominam, ako som pred 10+ rokmi konvertoval aplikaciu do DX a cumel na to prvy raz jak puk. Ked som kodil lightmapper z Radiosity, tak mi ten half-texel narobil hodne vela problemov, lebo ta hrana, aj ked mala len pol texelu totalne rozbila scenu...

Ci to nezaviedli pod poslednymi verziami OGL, vskutku netusim - to nech sa zveri niekto znaly posledneho OGL.
Návrat nahoru
Zobrazit informace o autorovi Odeslat soukromou zprávu
nou



Založen: 28. 07. 2007
Příspěvky: 1050

PříspěvekZaslal: 9. březen 2012, 23:03:17    Předmět: Odpovědět s citátem

stred pixelu textury je na surdaniciach 0.5/L+N/L L-rozmer textury N-cele cisla
a taktiez stred pixela obrazovky je taktiez na tych istych suradniciach ak pouzijeme ortho projekciu. teda vykreslenie obdlznika od [0,0] po [1,1] nam namapuje 1:1 texturu na obrazovku ak ma ta textura rovnake rozlisenia ako framebuffer. v konecnom dosledku sa tieto polkove offsety vyrusia.
_________________
Najjednoduchšie chyby sa najtažšie hľadajú.
Návrat nahoru
Zobrazit informace o autorovi Odeslat soukromou zprávu
Al



Založen: 23. 10. 2007
Příspěvky: 196

PříspěvekZaslal: 10. březen 2012, 18:07:36    Předmět: Odpovědět s citátem

To, co psal nou, jsem nepochopil. (Je to takovej typickej příklad textu, kterýmu porozumí jen ten, co tomu už rozumí, ale ne ten, co se na to tady ptá. Wink)

Teď mi to teda už kreslí přesně pixel-to-pixel 1:1. Krása. Akorát bych chtěl ještě bilineární mapování, když cílové okno je větší než zdrojová bitmapa. A to mi nefunguje, při zvětšení na FULL HD monitor se pixely opakují, nikde nevidím žádné zprůměrované body. Co musím kde ještě nastavit, aby mi to při zvětšování dělalo bilineárně? (O případu zmenšování ani nemluvě, tam by se taky nějaké průměrování hodilo, ale to asi nebude tak snadné.)

Mám tam tohle a myslel jsem, že to k bilineárnímu mapování textury bude stačit:

kód:
d3ddev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
d3ddev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
d3ddev->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

Mám kartu 7600GT, tak by to snad měla umět. Původně mi to právě rozmazávalo úplně, proto jsem tady pokládal ten původní dotaz. A teď to zas nerozmazává vůbec, takže nechápu.
Návrat nahoru
Zobrazit informace o autorovi Odeslat soukromou zprávu
Crypton



Založen: 14. 05. 2009
Příspěvky: 306
Bydliště: The Void

PříspěvekZaslal: 10. březen 2012, 19:54:11    Předmět: Odpovědět s citátem

Ještě musíš nastavit D3DSAMP_ADDRESSU a D3DSAMP_ADDRESSV u té textury.

Více informací zde: http://www.toymaker.info/Games/html/sampler_states.html

Wink
_________________
Návrat nahoru
Zobrazit informace o autorovi Odeslat soukromou zprávu Zobrazit autorovi WWW stránky
Al



Založen: 23. 10. 2007
Příspěvky: 196

PříspěvekZaslal: 10. březen 2012, 22:30:07    Předmět: Odpovědět s citátem

Podle té dokumentace se D3DSAMP_ADDRESSU a D3DSAMP_ADDRESSV vůbec netýká bilineárního mapování, ale opakování textury. Já samozřejmě opakování nepotřebuju. Potřebuju aby mi mezi bílým a černým pixelem byl dopočítán šedý. Zatím se mi dopočítává mezilehlý bod mezi černým a bílým ta, že je buď černý, nebo bílý, co je zrovna blíž, ale nikdy není šedý.
Návrat nahoru
Zobrazit informace o autorovi Odeslat soukromou zprávu
Zobrazit příspěvky z předchozích:   
odeslat nové téma   Odpovědět na téma    Obsah fóra České-Hry.cz -> 3D API / 3D Enginy Časy uváděny v GMT + 1 hodina
Strana 1 z 1

 
Přejdi na:  
Nemůžete odesílat nové téma do tohoto fóra
Nemůžete odpovídat na témata v tomto fóru
Nemůžete upravovat své příspěvky v tomto fóru
Nemůžete mazat své příspěvky v tomto fóru
Nemůžete hlasovat v tomto fóru


Powered by phpBB © 2001, 2005 phpBB Group


Vzhled udelal powermac
Styl "vykraden" z phpBB stylu MonkiDream - upraveno by rezna