標籤:des style blog http color 使用
一、前言
在學習OpenGL的過程中,發現很多函數都是全域的。前面幾章中都是在一個視窗DC中畫圖,那麼要在多個視窗畫圖,需要怎麼處理呢?網上方法有多種,這裡採用其中一種,利用wglMakeCurrent函數來切換不同視窗,以達到多視窗同時喧染的目的。
二、準備
每個視窗與OpenGL綁定時,都通過以下幾個過程進行:
1.擷取視窗控制代碼Handle/HWND(在TWinControl繼承下來的類中,都可以通過TWinControl.Handle獲得,MFC的視窗可以通過CWnd::GetSafeHwnd()獲得)
2.通過控制代碼擷取裝置情境HDC(通過API GetDC()擷取)
3.通過裝置情境HDC擷取OpenGL的HGLRC(通過OpenGL的wglCreateContext()獲得)
當然,在程式銷毀時記得釋放相應的HGLRC與視窗DC。通過以上的對應關係,可以把這些資訊儲存在數組中。以四個視窗為例,我建了這樣幾個數組:
FDC: array[0..3] of HDC; FHRC: array[0..3] of HGLRC; FHwnd: array[0..3] of THandle;
三、初始化
本例以多個TPanel為例代替多個視窗,以上三個數組進行初始化。建立了一個inidc()的方法,在視窗的OnCreate時調用,目的是初始化陣列變數的值
procedure TForm1.FormCreate(Sender: TObject);begin Fbmpindex := 0; // 對多個視窗進行初始化 inidc(0, Pnl1.Handle); inidc(1, Pnl2.Handle); inidc(2, Pnl3.Handle); inidc(3, Pnl4.Handle);end;
下面的inidc的代碼
procedure TForm1.inidc(i: Integer; hform: THandle);var pfd:TPIXELFORMATDESCRIPTOR; pixelFormat: Integer;begin With pfd do begin nSize := sizeof(TPIXELFORMATDESCRIPTOR); // size nVersion := 1; // version dwFlags := PFD_SUPPORT_OPENGL or PFD_DRAW_TO_WINDOW or PFD_DOUBLEBUFFER; // support double-buffering iPixelType := PFD_TYPE_RGBA; // color type cColorBits := 24; // preferred color depth cRedBits := 0; cRedShift := 0; // color bits (ignored) cGreenBits := 0; cGreenShift := 0; cBlueBits := 0; cBlueShift := 0; cAlphaBits := 0; cAlphaShift := 0; // no alpha buffer cAccumBits := 0; cAccumRedBits := 0; // no accumulation buffer, cAccumGreenBits := 0; // accum bits (ignored) cAccumBlueBits := 0; cAccumAlphaBits := 0; cDepthBits := 16; // depth buffer cStencilBits := 0; // no stencil buffer cAuxBuffers := 0; // no auxiliary buffers iLayerType := PFD_MAIN_PLANE; // main layer bReserved := 0; dwLayerMask := 0; dwVisibleMask := 0; dwDamageMask := 0; end; FDC[i] := GetDC(hform); FHwnd[i] := hform; pixelFormat := ChoosePixelFormat(FDC[i], @pfd); if pixelFormat = 0 then Exit; if not SetPixelFormat(FDC[i], pixelFormat, @pfd) then Exit; FHRC[i] := wglCreateContext(FDC[i]); wglMakeCurrent(FDC[i], FHRC[i]); // 設定背景色為 黑色 參數為 RGBA glClearColor(0, 0, 0, 0);end;
四、繪製
這裡修改一下前面幾章中的Draw函數,用於支援多視窗的繪製,代碼如下:
procedure TForm1.Draw(i: Integer);var Bmp: TBitmap; texture: GLuint; l, t, w, h: Integer; rc: TRect;begin // 重新設定顯示地區 wglMakeCurrent(FDC[i], FHRC[i]); // 重新計算並設定顯示地區大小 GetWindowRect(fhwnd[i], rc); w := rc.Right - rc.Left; h := rc.Bottom - rc.Top; glMatrixMode(GL_PROJECTION); glLoadIdentity; glViewPort(0, 0, w, h); gluOrtho2D(0, w, h, 0); glMatrixMode(GL_MODELVIEW); glLoadIdentity; // 只是為了顯示多個圖片,不是必須的 inc(Fbmpindex); Bmp := TBitmap.Create; Bmp.LoadFromFile(ExtractFilePath(ParamStr(0)) + IntToStr(Fbmpindex mod 3) + ‘.bmp‘); // 建立紋理地區 glGenTextures(1, @texture); // 綁定紋理地區 glBindTexture(GL_TEXTURE_2D, texture); // 使用位元影像建立映像紋理 glTexImage2D( GL_TEXTURE_2D, // 紋理是一個2D紋理 GL_TEXTURE_2D 0, // 映像的詳細程度 預設 0 3, // 資料的成分數。因為映像是由紅,綠,藍三種組成 預設3 Bmp.Width, // 紋理的寬度 Bmp.Height, // 紋理的高度 0, // 邊框的值 預設 0 GL_BGR_EXT, // 資料格式 bmp使用 bgr GL_UNSIGNED_BYTE, // 組成映像的資料是無符號位元組類型的 Bmp.ScanLine[Bmp.Height - 1] // DIB資料指標 ); // 下面兩行是讓opengl在放大原始的紋理大(GL_TEXTURE_MAG_FILTER)或縮小原始紋理(GL_TEXTURE_MIN_FILTER)時OpenGL採用的濾波方式。 // GL_LINEAR 使用線性濾波,可以把圖片處理處平滑,但需要更多的記憶體與CPU glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // 線形濾波 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 線形濾波 // 以下是繪圖,利用一個四邊形,繪製圖片 // 啟用紋理映射 if glIsEnabled(GL_TEXTURE_2D) = 0 then glEnable(GL_TEXTURE_2D); // 清空緩衝區 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); l := 10; t := 10; w := 100 + (i * 50); // 放大為200*200的圖片 // 選擇紋理 如果情境中使用多個紋理,不能在glBegin() 和 glEnd() 之間綁定紋理 glBindTexture(GL_TEXTURE_2D, texture); glBegin(GL_QUADS); // glTexCoord2f 的第一個參數是X座標。 // 0.0是紋理的左側。 0.5是紋理的中點, 1.0是紋理的右側。 // glTexCoord2f 的第二個參數是Y座標。 // 0.0是紋理的底部。 0.5是紋理的中點, 1.0是紋理的頂部。 glTexCoord2f(0, 1); glVertex2f(l, t); glTexCoord2f(1, 1); glVertex2f(l + w, t); glTexCoord2f(1, 0); glVertex2f(l + w, t + w); glTexCoord2f(0, 0); glVertex2f(l, t + w); glEnd(); Bmp.Free; SwapBuffers(FDC[i]);end;
上面的畫圖片的代碼,如果覺得看起來複雜,可以用以下簡單點的代碼:
procedure TForm1.DrawSimple(i: Integer);var l, t, w, h: Integer; rc: TRect;begin // 重新設定顯示地區 wglMakeCurrent(FDC[i], FHRC[i]); // 重新計算並設定顯示地區大小 GetWindowRect(fhwnd[i], rc); w := rc.Right - rc.Left; h := rc.Bottom - rc.Top; glMatrixMode(GL_PROJECTION); glLoadIdentity; glViewPort(0, 0, w, h); gluOrtho2D(0, w, h, 0); glMatrixMode(GL_MODELVIEW); glLoadIdentity; // 清空緩衝區 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // 設定四邊形的顏色 glColor3f(1, 0.5, 0); // 繪製第一個多邊形 l := 10; t := 10; w := 64 + (i - 2) * 50; glBegin(GL_QUADS); glVertex2f(l, t); glVertex2f(l + w, t); glVertex2f(l + w, t + w); glVertex2f(l, t + w); glEnd(); SwapBuffers(FDC[i]);end;
為了方便示範,這裡用一個定時器TTimer代替以前在視窗的OnPaint畫圖,定時器下的代碼如下:
procedure TForm1.tmr1Timer(Sender: TObject);begin Draw(0); Draw(1); DrawSimple(2); DrawSimple(3);end;
五、釋放
最後記得釋放資源,Delphi中基本上申請的資源都要回收,new->delete/dispose、get->release、create->destroy,簡單的說,建立(create)的要銷毀(destroy),借(get)的要還(release)。
procedure TForm1.FormDestroy(Sender: TObject);var i: Integer;begin for i := 0 to 4 - 1 do begin wglMakeCurrent(FDC[i], FHRC[i]); wglDeleteContext(FHRC[i]); ReleaseDC(fhwnd[i], FDC[i]); end;end;
效果如下:
源碼下載:OpenGL_06.zip
2014-07-17 by lin