帮酷LOGO
0 0 评论
  • 显示原文与译文双语对照的内容
文章标签:DIA  Directx  DIR  template  pix  对话框  SURF  TEMP  


介紹

這是我關於DirectX編程系列文章的第一部分: 標題為"我們從哪裡來"。 我將簡要介紹一下有些技術在時間里可以能用到的一些圖紙。 of的工作台。這將為我們更好地理解DirectX和現代圖形硬體下的奇妙事物。 本教程包含六個部分,有兩個主要示例,介紹如何使用本教程提供的代碼。

  • DirectX教程部件 I: DirectX對話框模板
  • DirectX教程第二部分:在彈球遊戲中使用 CDirectXDialog
  • DirectX教程第三部分:基本 3D
  • DirectX教程第IV部分:在 Vectorballs scroller中使用 Vector
  • DirectX教程零件 V: Lambert和多邊形填充
  • DirectX教程第VI部分:完成 direct 3d方法。

在本教程最後,你應該有關於 3D 數學。多邊形填充的基本知識,以及使用DirectX的方式。 但是首先,讓我們開始使用 Glimpse 在DirectX功能上進行遠征以快速訪問 Surface 內存。 這將允許我們模擬VGA像繪圖到視頻內存( 記住 0 xA000 )。 我們的Lambert和多邊形填充器只能使用這個功能在屏幕上操作像素。

為了給出這個像素程序的快速性,我決定計算一個基於函數的數學圖形(。查看上面的屏幕截圖)。f(x,y) = ( x2+y2 ) 2. 正如你所看到的,這個函數與兩個軸對稱,因這裡只能計算一個四分之一,並將值翻轉。 但是這樣做不會阻止我們從任何季度繪製所有的像素。 如果在平均解析度上最大化對話框,這意味著你必須繪製 780000個像素。 在我目前的硬體配置( 奔騰 IV,3 GHz。) 中,只需要 72毫秒。 順便說一下,我在第一個電腦( 這是一個 Amiga,只有 7.14兆赫) 上可以能已經做了 15年了。 對於 320 x256=81920pixels的默認解析度,列印整個圖像大約為 5分鐘( 我已經在MC6800x0程序集中完成了所有的編程)。 對於硬體愛好者,讓我們計算速度的增長: 我認為這個值是驚人的,不是?

編譯要求

你必須下載並安裝Microsoft以編譯本教程提供的示例項目。 如果編譯器找不到合適的DirectX頭,請查看你的項目設置。 你必須指定公共包含的位置。 我的特殊情況下,我在 C:DXSDK. 中安裝了 DirectX SDK,因此項目設置的附加包含路徑是c: dxsdksamplesc++commoninclude。

直接 Surface 訪問

IDirectDrawSurface7 介面為直接操作它的像素提供了一個獲取 DirectX Surface 內存的方法。 方法被稱為 Lock,你將必須注意,當繪圖到達 Surface 內存時,將釋放鎖。 適當的釋放方法命名為 Unlock。 雖然應用程序鎖定了 Surface 內存,但沒有它的他的DirectX單元可以訪問它,甚至圖形硬體本身。 因此,你不應該在實際的DirectX應用程序中使用這裡方法,因為整體的性能。 但在我們的情況下這並不是很重要。 最後,我們只對基於像素的直接繪圖常式的簡單實現感興趣。

HRESULT Lock(LPRECT lpDestRect, 
 LPDDSURFACEDESC2 lpDDSurfaceDesc, 
 DWORD dwFlags, HANDLE hEvent );
HRESULT UnLock(LPRECT lpRect);

當你獲得對 Surface 內存的訪問時,DirectX運行時將在名為 DDSURFACEDESC2的結構中返回關於該 Surface的信息。 DirectX Surface的內存布局可以根據系統設置的顯示而變化。 因此,我們必須仔細檢查這個結構來了解哪個位元組對應于屏幕上的哪個像素。 可以將 Surface 內存分為可見區域和非可視區域。 可見區域的寬度和高度由 DDSURFACEDESC2 結構的屬性 dwWidthdwHeight 確定。 我不太確定為什麼有一個不可以見的部分,但是我想它與水平滾動有關。 通過更改 Surface的起始地址,可以操縱每個掃描行使用內存的哪個部分。 效果是,非可以見區域中的像素變為可以見,看起來像滾動曲面。

現在,要改變 Surface 上的像素,我們必須將屏幕坐標映射到 Surface 內存中的位置。 這個映射可以通過線性數學函數來描述:

memory_position = start_address + x * bytes_per_pixel + y * bytes_per_line

每個像素都在內存中使用一些位,根據系統設置的顯示。 為了確定像素( 變數 bytes_per_pixel ) 實際佔用的位元組數,我們可以查看 DDPIXELFORMAT 結構的dwRGBBitCount 屬性。 變數 bytes_per_line 對應于 DDSURFACEDESC2 結構的lPitch 屬性。 但是計算特定像素的內存位置是不夠的,為它設置正確的顏色。 為此,我們還必須檢查哪些位屬於哪個顏色通道。 在 32位模式中,實現非常簡單。 在這種情況下,可以直接將RGB顏色放入 Surface 內存中,因為每個像素都是寬度為。 但是在其他情況下,映射稍微複雜一點。 例如在 16位模式中,每個像素只佔用兩個位元組的Surface 內存。 這意味著,每個顏色通道的表示( 只有 5位精確) 都少於 8位。 因此,我們必須屏蔽並將我們的RGB值轉換為對應的像素格式。 我們需要完成這個任務的唯一信息是每個顏色通道的位掩碼和位位置。 相應的屬性是 DDPIXELFORMAT 結構的dwRBitMaskdwGBitMaskdwBBitMask

下表為 16位模式提供了這些屬性的值。 如前所述,這種模式的遮罩是 5位寬,其中藍色通道佔據最頂端的位,紅色通道佔據最頂端的通道。

顏色通道位掩碼AttributeValue的值
dwRBitMask01111100 00000000
dwGBitMask00000011 11100000
dwBBitMask00000000 00011111

為了確定位掩碼中的位數,我們不斷地減少位掩碼,並應用一個邏輯和操作。 以下 CDirectXDialog 類的操作執行這裡計算。

int CDirectXDialog::getNumberOfBits(DWORD mask)
{
 int nob = 0;
 while (mask)
 {
 mask = mask & (mask - 1);
 nob++;
 }
 return nob;
}

最後,可以通過不斷應用邏輯和操作來識別位掩碼的起始位,從右向左移動變位。 為了完整性,這裡是確定這個起始位位置的操作。

int CDirectXDialog::getBitMaskPosition(DWORD mask)
{
 int pos = 0;
 while (!(mask & 1 <<pos)) pos++;
 return pos;
}

操作像素

DirectX Surface 訪問完全由類 CDirectXDialog 包裝。 要訪問 Surface 內存時,必須調用該類的BackbufferLock()。 完成繪圖后,你必須調用 BackbufferUnlock()。 調用這些操作對很重要,我決定讓它們成為 private。 因此,你不能直接訪問這些操作。 你需要使用輔助對象,該對象負責鎖定和解鎖 Surface。 這裡worker對象類是 CDirectXDialog的友元類,它被稱為 CDirectXLockGuard

BackbufferLock() 中處理的第一個問題是 DirectX Surface的鎖。 然後,檢查 Surface,確定每個彩色通道的位掩碼和位位置,當然還有位置值。 可以通過成員函數指針訪問 CDirectXDialog的實際 setPixel 操作。 在 BackbufferLock() 中,這個函數指針是根據當前設置模式初始化的。 在 DirectX Surface 中設置像素的具體函數稱為 CDirectXDialog::setPixelOPTIMIZEDCDirectXDialog::setPixelSECURE。 優化的版本只將RGB值複製到相應的內存位置。 安全版本還掩蓋和移動RGB顏色值到正確的像素格式,因這裡比優化的一個更慢。

inlinevoid CDirectXDialog::setPixelOPTIMIZED(int x, int y, DWORD color)
{
 *(unsignedint*)(backbuffervideodata + 
 x*x_pitch + y*y_pitch) = color;
}inlinevoid CDirectXDialog::setPixelSECURE(int x, int y, DWORD color)
{
 int offset = x*x_pitch + y*y_pitch;
 DWORD Pixel = *(LPDWORD)((DWORD)backbuffervideodata + offset);
 Pixel = (Pixel & ~ sDesc.ddpfPixelFormat.dwRBitMask) | 
 ((RGB_GETRED(color)>> (8 - rbits)) <<rpos);
 Pixel = (Pixel & ~ sDesc.ddpfPixelFormat.dwGBitMask) | 
 ((RGB_GETGREEN(color)>> (8 - gbits)) <<gpos);
 Pixel = (Pixel & ~ sDesc.ddpfPixelFormat.dwBBitMask) | 
 ((RGB_GETBLUE(color)>> (8 - bbits)) <<bpos);
 *(unsignedint*)(backbuffervideodata + offset) = Pixel;
}

CDirectXDialog類

CDirectXDialog 是一個抽象類。 因此,你必須將它的子類化並覆蓋純虛函數 displayFrame()。 這裡操作在每次重新繪製對話框時都由框架調用。 這裡示例的圓形圖像在 CDlgBackgroundArtDecoDlg 類的displayFrame() 操作中繪製。 其他重要的和可以寫的操作是。

  • initDirectDraw()

    在初始化對話框時由框架調用。 你可以創建和初始化其他的DirectX資源,如其他。

  • restoreSurfaces()

    當發生異常且DirectX圖形 Surface 丟失時,由框架調用。 你必須在這裡創建並初始化你的DirectX資源。

  • freeDirectXResources()

    當對話框被破壞時,由框架調用。 你可以在這裡釋放你創建的資源。

繪製圖像

顯示的圖像在 CDlgBackgroundArtDecoDlg 類的displayFrame() 操作中計算並繪製。 我想檢查 C++ 和直接彙編器實現之間的速度優勢。 因此,你可以選擇要激活哪個。 如果預處理器變數 IS_IT_WORTH_IT 已經定義,則圖形將由x86彙編程序常式完成。 否則,它由 C++ 實現完成。 另外,我使用成員函數指針來調用assember代碼 fragment 中的setPixel 成員函數。 請參閱我的另一篇文章,如果你想了解更多關於這個主題( 如何從內聯彙編代碼段調用調用 C++ 成員操作。)的信息。

void CDlgBackgroundArtDecoDlg::displayFrame()
{
 CRect rect; GetClientRect(rect);
 int width = rect.Width()/2;
 int height = rect.Height()/2;
 DWORD starttime,stoptime;
 starttime = GetTickCount();
 {
 g_pDisplay.Clear();
 CDirectXLockGuard lock(this);#if defined(IS_IT_WORTH_IT) setPixelPTR _setPixel = setPixel;
 int x, y, c, z = zoom;
 _asm
 {
 mov edx, width
loop1: mov ebx, height
loop2: mov eax, edx; //color = (x*x+y*y) imul eax, eax;
 mov ecx, ebx;
 imul ecx, ecx;
 add eax, ecx;
 imul eax, eax; //color = color*color; mov ecx, z; //zoom sar eax, cl;
 and eax, 0xFF;
 mov cl, al;
 and cl, 0x80; //if (color> = 128) color = color - 127; jz weiter;
 xor eax, 0x7F;
weiter: shl eax, 8+1;
 //Backup mov x, edx;
 mov y, ebx;
 mov c, eax;
 push eax; //color  mov eax, ebx; //yadd eax, height;
 push eax; 
 mov eax, edx; //xadd eax, width;
 push eax; 
 mov ecx, this; //this-call of member function pointer call _setPixel;
 mov eax, c; //color  push eax;
 mov eax, y; //yadd eax, height;
 push eax; 
 mov eax, width; //x sub eax, x;
 push eax; 
 mov ecx, this; //this-call of member function pointer call _setPixel;
 mov eax, c; //color  push eax;
 mov eax, height;//y sub eax, y;
 push eax; 
 mov eax, width; //x sub eax, x;
 push eax; 
 mov ecx, this; //this-call of member function pointer call _setPixel;
 mov eax, c; //color  push eax;
 mov eax, height;//y sub eax, y;
 push eax; 
 mov eax, x; //xadd eax, width;
 push eax; 
 mov ecx, this; //this-call of member function pointer call _setPixel;
 //Reload mov edx, x;
 mov ebx, y; 
 sub ebx, 1 jge loop2
 sub edx, 1 jge loop1 
 }#else for (long x = 0; x < width; x++)
 for (long y = 0; y < height; y++)
 {
 long g = x*x + y*y;
 g = g * g; 
 g = g>> zoom; 
 g = g & 0xFf;
 if (g & 0x80) // if (g> 0x7f) g = 0x7f - g; g = 0x7f ^ g;
 g = g <<1; 
 (this->*setPixel)(width + x, height + y,RGBA_MAKE(g,0,0,0));
 (this->*setPixel)(width - x, height + y,RGBA_MAKE(0,g,0,0));
 (this->*setPixel)(width + x, height - y,RGBA_MAKE(0,0,g,0));
 (this->*setPixel)(width - x, height - y,RGBA_MAKE(g,g,g,0));
 } #endif }
 stoptime = GetTickCount();
 char buffer[128];
 sprintf(buffer, "time: %4dms", stoptime - starttime);
 g_pTextSurface->DrawText(NULL, buffer, 0, 0, RGB(0,0,0), RGB(255,255,0));
 g_pDisplay.Blt(20, 20, g_pTextSurface, NULL);
 CDialog::OnPaint();
}

還有什麼要說的?

一些人可能會說,這篇文章有點混淆,因為我想解釋一下比DirectX更老的技術。 我向你保證,這是我們需要了解的關於DirectX的全部。 我使用DirectX的唯一原因是,它是快速的,而我展示的像素繪圖常式對應于 DOS VGA模式。 我可以使用微軟的GDI實現像素繪製程序,坦率地說,我有一個基於GDI的版本。 但是用它來創建快速多邊形填充器太慢了。 因此,我認為這是一個很好。 希望你能發現這些文章有幫助和有趣。



文章标签:PAR  fast  TEMP  DIR  template  DIA  对话框  Directx  

Copyright © 2011 HelpLib All rights reserved.    知识分享协议 京ICP备05059198号-3  |  如果智培  |  酷兔英语