開始我的GL離屏渲染綁定[轉]


地址: http://wiki.woodpecker.org.cn/moin/lilin/swig-glBmpContext

呵呵,有了第一次的經驗,我們就要開始我們的GL離屏渲染的綁定了。

關 於OpenGL的離屏渲染,前面已經有一些涉及了。再說一下吧,OpenGL有兩種渲染方式:一種是通過操作系統打開窗口進行渲染,然后可以直接在屏幕上 顯示,這種渲染方式叫做屏幕渲染。一種通過在內存中一塊位圖區域內渲染,這種渲染方式在沒有通過SwapBuffer方式前不可以在屏幕上顯示,所以這種 方法叫離屏渲染。一般來說,OpenGL通過屏幕顯示方式展示它的魅力,屏幕渲染方式是大多數情況下的首選。而且很多窗口系統都有實現OpenGL的屏幕 渲染方式。比如glut,wxwidgets,QT,gtk。但是有些時候我們不需要屏幕顯示。只是想把它保存成一個圖像文件就好。而且我們就是不想打開 一個窗口。這樣就需要用離屏渲染的方法。在內存中畫,最后保存成圖像。

可 惜的是OpenGL沒有統一的離屏渲染操作API。GL把這些事情全部交給系統。這樣就導致各個系統的離屏渲染都有各自的 API,Windows,X,Apple,SGI,OS2都有自己的離屏RC上下文構建方法,每套API都不同。在缺少了榜樣的力量后,各個系統就紛紛開 始諸侯割據了,就造成天下大亂的局勢。這樣確實不太好。不過現在亂了就讓它亂吧,誰叫我們是“小程序員”?天下大勢就這樣,你要怎么着吧-_-! 沒辦法。實在是沒辦法~~~如今的世界太瘋狂…… 如今的世界變化快……

我還是靜下心來看看這么在各個系統上實現離屏渲染吧。OS2大概八輩子用不到了吧,Apple是高高在上的貴族們的東西。咱們老百姓……還是算了吧。老老實實研究一下Windows和X吧。於是先開始研究WGL。

WGL要建立離屏渲染,可以參看官方解釋,不過太多,太亂了,紅寶書中的解釋比較淺顯。這里也有兩句解釋(不過這里主要是SIG的解釋,X的解釋也比較詳細)。最令人激動的是這里有win32上的完整例子。

簡單得說吧,要進行離屏渲染,win32下需要做下面的幾個步驟:

  1. 創建一個內存 DC
  2. 創建一個位圖
  3. 把位圖選入DC
  4. 設置DC的像元格式
  5. 通過DC創建OpenGL的渲染上下文RC
  6. 開始渲染.

好了,可用的渲染過程如下:

 

#include "stdafx.h"
#include <windows.h> #include <iostream> #include <commctrl.h> #include <gl/gl.h> #include <gl/glu.h> #include <string>  using namespace std;  BOOL SaveBmp(HBITMAP hBitmap, string FileName) {  HDC hDC;  //當前分辨率下每象素所占字節數  int iBits;  //位圖中每象素所占字節數  WORD wBitCount;  //定義調色板大小, 位圖中像素字節大小 ,位圖文件大小 , 寫入文件字節數  DWORD dwPaletteSize=0, dwBmBitsSize=0, dwDIBSize=0, dwWritten=0;  //位圖屬性結構  BITMAP Bitmap;  //位圖文件頭結構  BITMAPFILEHEADER bmfHdr;  //位圖信息頭結構  BITMAPINFOHEADER bi;  //指向位圖信息頭結構  LPBITMAPINFOHEADER lpbi;  //定義文件,分配內存句柄,調色板句柄  HANDLE fh, hDib, hPal,hOldPal=NULL;   //計算位圖文件每個像素所占字節數  hDC = CreateDC("DISPLAY", NULL, NULL, NULL);  iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);  DeleteDC(hDC);  if (iBits <= 1) wBitCount = 1;  else if (iBits <= 4) wBitCount = 4;  else if (iBits <= 8) wBitCount = 8;  else wBitCount = 24;   GetObject(hBitmap, sizeof(Bitmap), (LPSTR)&Bitmap);  bi.biSize = sizeof(BITMAPINFOHEADER);  bi.biWidth = Bitmap.bmWidth;  bi.biHeight = Bitmap.bmHeight;  bi.biPlanes = 1;  bi.biBitCount = wBitCount;  bi.biCompression = BI_RGB;  bi.biSizeImage = 0;  bi.biXPelsPerMeter = 0;  bi.biYPelsPerMeter = 0;  bi.biClrImportant = 0;  bi.biClrUsed = 0;   dwBmBitsSize = ((Bitmap.bmWidth * wBitCount + 31) / 32) * 4 * Bitmap.bmHeight;   //為位圖內容分配內存  hDib = GlobalAlloc(GHND,dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER));  lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);  *lpbi = bi;   // 處理調色板  hPal = GetStockObject(DEFAULT_PALETTE);  if (hPal)  {  hDC = ::GetDC(NULL);  hOldPal = ::SelectPalette(hDC, (HPALETTE)hPal, FALSE);  RealizePalette(hDC);  }   // 獲取該調色板下新的像素值  GetDIBits(hDC, hBitmap, 0, (UINT) Bitmap.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER)  +dwPaletteSize, (BITMAPINFO *)lpbi, DIB_RGB_COLORS);   //恢復調色板  if (hOldPal)  {  ::SelectPalette(hDC, (HPALETTE)hOldPal, TRUE);  RealizePalette(hDC);  ::ReleaseDC(NULL, hDC);  }   //創建位圖文件  fh = CreateFile(FileName.c_str(), GENERIC_WRITE,0, NULL, CREATE_ALWAYS,  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);    if (fh == INVALID_HANDLE_VALUE) return FALSE;   // 設置位圖文件頭  bmfHdr.bfType = 0x4D42; // "BM"  dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize;  bmfHdr.bfSize = dwDIBSize;  bmfHdr.bfReserved1 = 0;  bmfHdr.bfReserved2 = 0;  bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize;  // 寫入位圖文件頭  WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);  // 寫入位圖文件其余內容  WriteFile(fh, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL);  //清除  GlobalUnlock(hDib);  GlobalFree(hDib);  CloseHandle(fh);   return TRUE; }  void mGLRender() {  glClearColor(0.9f,0.9f,0.3f,1.0f);  glClear(GL_COLOR_BUFFER_BIT);  glMatrixMode(GL_PROJECTION);  gluPerspective(30.0, 1.0, 1.0, 10.0);  glMatrixMode(GL_MODELVIEW);  gluLookAt(0, 0, -5, 0, 0, 0, 0, 1, 0);  glBegin(GL_TRIANGLES);  glColor3d(1, 0, 0);  glVertex3d(0, 1, 0);  glColor3d(0, 1, 0);  glVertex3d(-1, -1, 0);  glColor3d(0, 0, 1);  glVertex3d(1, -1, 0);  glEnd();  glFlush(); // remember to flush GL output! }  int _tmain(int argc, _TCHAR* argv[]) {  const int WIDTH = 500;  const int HEIGHT = 500;   // Create a memory DC compatible with the screen  HDC hdc = CreateCompatibleDC(0);  if (hdc == 0) cout<<"Could not create memory device context";   // Create a bitmap compatible with the DC  // must use CreateDIBSection(), and this means all pixel ops must be synchronised  // using calls to GdiFlush() (see CreateDIBSection() docs)  BITMAPINFO bmi = {  { sizeof(BITMAPINFOHEADER), WIDTH, HEIGHT, 1, 32, BI_RGB, 0, 0, 0, 0, 0 },  { 0 }  };  DWORD *pbits; // pointer to bitmap bits  HBITMAP hbm = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void **) &pbits,  0, 0);  if (hbm == 0) cout<<"Could not create bitmap";   //HDC hdcScreen = GetDC(0);  //HBITMAP hbm = CreateCompatibleBitmap(hdcScreen,WIDTH,HEIGHT);   // Select the bitmap into the DC  HGDIOBJ r = SelectObject(hdc, hbm);  if (r == 0) cout<<"Could not select bitmap into DC";   // Choose the pixel format  PIXELFORMATDESCRIPTOR pfd = {  sizeof (PIXELFORMATDESCRIPTOR), // struct size  1, // Version number  PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL, // use OpenGL drawing to BM  PFD_TYPE_RGBA, // RGBA pixel values  32, // color bits  0, 0, 0, // RGB bits shift sizes...  0, 0, 0, // Don't care about them  0, 0, // No alpha buffer info  0, 0, 0, 0, 0, // No accumulation buffer  32, // depth buffer bits  0, // No stencil buffer  0, // No auxiliary buffers  PFD_MAIN_PLANE, // Layer type  0, // Reserved (must be 0)  0, // No layer mask  0, // No visible mask  0 // No damage mask  };  int pfid = ChoosePixelFormat(hdc, &pfd);  if (pfid == 0) cout<<"Pixel format selection failed";   // Set the pixel format  // - must be done *after* the bitmap is selected into DC  BOOL b = SetPixelFormat(hdc, pfid, &pfd);  if (!b) cout<<"Pixel format set failed";   // Create the OpenGL resource context (RC) and make it current to the thread  HGLRC hglrc = wglCreateContext(hdc);  if (hglrc == 0) cout<<"OpenGL resource context creation failed";  wglMakeCurrent(hdc, hglrc);   // Draw using GL - remember to sync with GdiFlush()  GdiFlush();  mGLRender();   SaveBmp(hbm,"output.bmp");  /*  Examining the bitmap bits (pbits) at this point with a debugger will reveal  that the colored triangle has been drawn.  */   // Clean up  wglDeleteContext(hglrc); // Delete RC SelectObject(hdc, r); // Remove bitmap from DC DeleteObject(hbm); // Delete bitmap DeleteDC(hdc); // Delete DC return 0; }

好了,編譯成功,運行,確實是可以啊!看看步驟是什么樣的:

CreateCompatibleDC

創建dc

CreateDIBSection

創建圖像

SelectObject

圖像選入DC

SetPixelFormat

設置像元格式

wglCreateContext

創建RC

wglMakeCurrent

選擇RC

mGLRender

開始渲染

SaveBmp

保存圖像(這段是我從網上隨便摘下來的)

...

清理

好的,既然C++可以,那么Python……

等等,Python好像不行!

單 單是OpenGL的世界亂了,也就算了,偏偏Python也來湊熱鬧。PyWin32里我死活找不到CreateDIBSection。好 吧,PyWin32找不到,那么我還有PIL。里面有個ImageWin.Dib,我試過,不行。總是在SetPixelFormat中出現問題。后來我 把 CreateDIBSection的部分整個注釋掉改成類似:

 

HDC hdcScreen = GetDC(0);
HBITMAP hbm = CreateCompatibleBitmap(hdcScreen,WIDTH,HEIGHT);

的 代碼。當然這是C++的改動,python改動也類似。因為這兩個函數PyWin32里有,現在通過了。並且運行到了wglCreateContext的 步驟。等等,提示空間不夠?什么空間不夠?我在C++中都運行好好的。對比兩個語言的兩段代碼,完全一樣的步驟,居然一個可以一個就是不行!發個郵件給 pyopengl的郵件列表吧,幾天沒回應……真的暈了。

大概可能是我不懂怎么玩PyWin32或者PyOpenGL,或者PIL的Dib類我用得不對,但是我在泡了三天的google后,我放棄了。與其在這個問題上拖延時間,不如另辟蹊徑。(如果你成功得在Python下離屏渲染了,一定要告訴我哦!)

既然C++可以,為什么不用C++來做?然后用Swig來綁定?不就是創建一個環境嗎?我在C++中創建好,然后在Python中渲染,然后在C++中關閉環境。反正環境在哪里不是一樣創建!

 

來吧

現在我的思路就定下來,用C++寫兩個函數,用來創建離屏RC環境和關閉環境。名字就叫StartBmpContext和EndBmpContext。

創建一個工程。叫glBmpContext。然后做一些什么取消stdafx,清空等善前工作。然后寫入內容。

 

#include <windows.h>
#include <iostream> #include <commctrl.h> #include <gl/gl.h> #include <gl/glu.h> using namespace std;  static HDC hdc; static HBITMAP hbm; static HGDIOBJ r; static HGLRC hglrc; static DWORD *pbits;// pointer to bitmap bits  static int WIDTH = 120; static int HEIGHT = 90;  __declspec(dllexport) void StartBmpContext(int width,int height) {  WIDTH = width;  HEIGHT = height;   // Create a memory DC compatible with the screen  hdc = CreateCompatibleDC(0);  if (hdc == 0) cout<<"Could not create memory device context";   // Create a bitmap compatible with the DC  // must use CreateDIBSection(), and this means all pixel ops must be synchronised  // using calls to GdiFlush() (see CreateDIBSection() docs)  BITMAPINFO bmi = {  { sizeof(BITMAPINFOHEADER), WIDTH, HEIGHT, 1, 32, BI_RGB, 0, 0, 0, 0, 0 },  { 0 }  };   hbm = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void **) &pbits,  0, 0);  /*HBITMAP hbm = CreateCompatibleBitmap(hdc,WIDTH,HEIGHT);*/  if (hbm == 0) cout<<"Could not create bitmap";   // Select the bitmap into the DC  r = SelectObject(hdc, hbm);  if (r == 0) cout<<"Could not select bitmap into DC";   // Choose the pixel format  PIXELFORMATDESCRIPTOR pfd = {  sizeof (PIXELFORMATDESCRIPTOR), // struct size  1, // Version number  PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL, // use OpenGL drawing to BM  PFD_TYPE_RGBA, // RGBA pixel values  32, // color bits  0, 0, 0, // RGB bits shift sizes...  0, 0, 0, // Don't care about them  0, 0, // No alpha buffer info  0, 0, 0, 0, 0, // No accumulation buffer  32, // depth buffer bits  0, // No stencil buffer  0, // No auxiliary buffers  PFD_MAIN_PLANE, // Layer type  0, // Reserved (must be 0)  0, // No layer mask  0, // No visible mask  0 // No damage mask  };  int pfid = ChoosePixelFormat(hdc, &pfd);  cout<<pfid<<endl;  if (pfid == 0) cout<<"Pixel format selection failed";   // Set the pixel format  // - must be done *after* the bitmap is selected into DC  BOOL b = SetPixelFormat(hdc, pfid, &pfd);  if (!b) cout<<"Pixel format set failed";   // Create the OpenGL resource context (RC) and make it current to the thread  hglrc = wglCreateContext(hdc);  if (hglrc == 0) cout<<"OpenGL resource context creation failed";  wglMakeCurrent(hdc, hglrc);  }  int SaveBmp(HBITMAP hBitmap, char* FileName) {  HDC hDC;  //當前分辨率下每象素所占字節數  int iBits;  //位圖中每象素所占字節數  WORD wBitCount;  //定義調色板大小, 位圖中像素字節大小 ,位圖文件大小 , 寫入文件字節數  DWORD dwPaletteSize=0, dwBmBitsSize=0, dwDIBSize=0, dwWritten=0;  //位圖屬性結構  BITMAP Bitmap;  //位圖文件頭結構  BITMAPFILEHEADER bmfHdr;  //位圖信息頭結構  BITMAPINFOHEADER bi;  //指向位圖信息頭結構  LPBITMAPINFOHEADER lpbi;  //定義文件,分配內存句柄,調色板句柄  HANDLE fh, hDib, hPal,hOldPal=NULL;   //計算位圖文件每個像素所占字節數  hDC = CreateDC("DISPLAY", NULL, NULL, NULL);  iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);  DeleteDC(hDC);  if (iBits <= 1) wBitCount = 1;  else if (iBits <= 4) wBitCount = 4;  else if (iBits <= 8) wBitCount = 8;  else wBitCount = 24;   GetObject(hBitmap, sizeof(Bitmap), (LPSTR)&Bitmap);  bi.biSize = sizeof(BITMAPINFOHEADER);  bi.biWidth = Bitmap.bmWidth;  bi.biHeight = Bitmap.bmHeight;  bi.biPlanes = 1;  bi.biBitCount = wBitCount;  bi.biCompression = BI_RGB;  bi.biSizeImage = 0;  bi.biXPelsPerMeter = 0;  bi.biYPelsPerMeter = 0;  bi.biClrImportant = 0;  bi.biClrUsed = 0;   dwBmBitsSize = ((Bitmap.bmWidth * wBitCount + 31) / 32) * 4 * Bitmap.bmHeight;   //為位圖內容分配內存  hDib = GlobalAlloc(GHND,dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER));  lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);  *lpbi = bi;   // 處理調色板  hPal = GetStockObject(DEFAULT_PALETTE);  if (hPal)  {  hDC = ::GetDC(NULL);  hOldPal = ::SelectPalette(hDC, (HPALETTE)hPal, FALSE);  RealizePalette(hDC);  }   // 獲取該調色板下新的像素值  GetDIBits(hDC, hBitmap, 0, (UINT) Bitmap.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER)  +dwPaletteSize, (BITMAPINFO *)lpbi, DIB_RGB_COLORS);   //恢復調色板  if (hOldPal)  {  ::SelectPalette(hDC, (HPALETTE)hOldPal, TRUE);  RealizePalette(hDC);  ::ReleaseDC(NULL, hDC);  }   //創建位圖文件  fh = CreateFile(FileName, GENERIC_WRITE,0, NULL, CREATE_ALWAYS,  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);    if (fh == INVALID_HANDLE_VALUE) return 1;   // 設置位圖文件頭  bmfHdr.bfType = 0x4D42; // "BM"  dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize;  bmfHdr.bfSize = dwDIBSize;  bmfHdr.bfReserved1 = 0;  bmfHdr.bfReserved2 = 0;  bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize;  // 寫入位圖文件頭  WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);  // 寫入位圖文件其余內容  WriteFile(fh, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL);  //清除  GlobalUnlock(hDib);  GlobalFree(hDib);  CloseHandle(fh);   return 0;  }  __declspec(dllexport) int SaveBmp(char* FileName) {  return SaveBmp(hbm,FileName); }  __declspec(dllexport) int GetWidth() {  return WIDTH; } __declspec(dllexport) int GetHeight() {  return HEIGHT; }  __declspec(dllexport) void GetMemBmpData(char **s,int *slen) {  *s = (char*)pbits; *slen = WIDTH*HEIGHT*4; } __declspec(dllexport) void EndBmpContext() { // Clean up wglDeleteContext(hglrc); // Delete RC SelectObject(hdc, r); // Remove bitmap from DC DeleteObject(hbm); // Delete bitmap DeleteDC(hdc); // Delete DC }

其實這里做得事情也就是這樣,把前面那段C++代碼拆開,把開始渲染前和渲染結束后兩個部分單獨拆出來,放到Start和End兩個函數里。為了能在最后做清理工作,把一些句柄做成全程靜態變量。提到開頭而已。

等一下,多了很多函數。

是 的。這里多了SaveBmp,這個是為了測試數據的正確性。用vc的方法保存bmp圖像。但是我並不想在vc中保存圖像。太麻煩了。我們有PIL啊!保存 只要一句的PIL啊~~~~~所以我需要有個函數讀取bmp圖像的信息。所以我添加了個GetMemBmpData函數。用於獲取圖像數據的二進制表示。 當然,渲染圖像大小不可以定死,所以我暴露了獲取圖像大小的函數,並在初始化環境的時候用兩個參數定義寬高。

好了,編譯,鏈接,成功。(需要說明的是,這里的GetMemBmpData的參數很奇怪,這是因為要返回二進制時Swig的特殊要求決定的)

我們現在有了C++的庫了。

好,開始定義glBmpContext.i,這是重點!

 

%module glBmpContext
%include "cstring.i"  %cstring_output_allocate_size(char **s, int *slen, free(*$1));   %{ #include <windows.h> #include <iostream> #include <commctrl.h> #include <gl/gl.h> #include <gl/glu.h> using namespace std;  void StartBmpContext(int w,int h); int SaveBmp( char* FileName ); void GetMemBmpData(char **s,int *slen); void EndBmpContext(); int GetWidth(); int GetHeight(); %}   void StartBmpContext(int w,int h); int SaveBmp( char* FileName ); void GetMemBmpData(char **s,int *slen); void EndBmpContext(); int GetWidth(); int GetHeight();

首 先,我們定義模塊名稱,然后引入一個叫cstring的swig預定義模塊,以及定義一種返回值形式。引入這個模塊是因為我們需要在 GetMemBmpData中返回圖像格式的二進制形式給Python,然后通過PIL.Image的fromstring函數轉化成圖像並可以用 save保存。

Python 中不單單是int,double,這樣的簡單類型。一些如數組,指針,字典,等等就比較麻煩了。Swig定義了很多預定義的模塊來處理這些東西。通 過%include 來定義這些數據格式和操作。這才是從C++到Python的惡夢。也是swig最核心的東西。這些東西是很多的,需要我們慢慢去掌握。

先 掌握兩個。一個是字符串。在Python中字符串是一個很強大的東西,但在swig定義中卻看起來不是那么強大。因為它被定義成c的字符串形式。一個 char* !不錯,是char*。看SaveBmp的參數,是一個char*。這就是Python中的字符串!在Python中調用就像這樣:

SaveBmp("f:/image/img.bmp")

好了,再看一個,返回一個二進制數據對象!這個比較復雜,可以看這個,這個解釋十分詳細。還有好幾種類型。我們用的是最后那個。因為C++/C不比Python,可以返回一個列表,它只能返回一個東西。所以在Python綁定定義中要用參數來代替返回。

還有更多的東西可以看這里

函數定義像這樣:

 

void foo(char **s, int *sz) {
 *s = (char *) malloc(64);  *sz = 64;  // Write some binary data  ... }

在swig定義.i 文件中就要這樣寫:

 

%cstring_output_allocate_size(char **s, int *slen, free(*$1));
... void foo(char **s, int *slen);

在Python中就要這樣調:

 

>>> foo()
'\xa9Y:\xf6\xd7\xe1\x87\xdbH;y\x97\x7f\xd3\x99\x14V\xec\x06\xea\xa2\x88' >>>

呵呵,很奇妙吧!

我也是第一次看到這種做法!

其他應該都看得懂了。

好了,現在我們定義setup.py:

 

from distutils.core import setup,Extension
include_dirs = [] libraries = ['glBmpContextD','opengl32','glu32'] library_dirs = ['./glBmpContext/'] extra_link_args = []  glBmpContext_module = Extension('_glBmpContext',  sources = ['glBmpContext_wrap.cpp'],  include_dirs = include_dirs,  libraries = libraries,  library_dirs = library_dirs,  extra_link_args = extra_link_args,  ) setup(name='glBmpContext wrapper',  version='1.0',  py_modules=["glBmpContext"],  ext_modules=[glBmpContext_module],  )

這個和前一個例子很像。特別注意的是Libraries,這里放了opengl32 和glu32 是為了能鏈接通過。

好了,寫個腳本來運行swig和編譯。

 

@echo off
swig -c++ -python -modern -new_repr -I. -o glBmpContext_wrap.cpp glBmpContext.i python.exe setup.py build copy .\build\lib.win32-2.4\*.* .\bin\ pause

好了,運行編譯通過后就可以了。這個腳本還把生成的pyd拷貝到bin目錄下。

好了,在bin目錄下建立一個測試腳本

 

import _glBmpContext
from OpenGL.GL import * from OpenGL.GLU import * from Numeric import * import Image w = 500 h = 400 _glBmpContext.StartBmpContext(w,h)  glMatrixMode(GL_PROJECTION); gluPerspective(30.0, w*1.0/h, 1.0, 10.0); glMatrixMode(GL_MODELVIEW); gluLookAt(0, 0, -5, 0, 0, 0, 0, 1, 0); glBegin(GL_TRIANGLES); glColor3d(1, 0, 0); glVertex3d(0, 1, 0); glColor3d(0, 1, 0); glVertex3d(-1, -1, 0); glColor3d(0, 0, 1); glVertex3d(1, -1, 0); glEnd(); glFlush();  data = _glBmpContext.GetMemBmpData() #print len(data),type(data) w = _glBmpContext.GetWidth() h = _glBmpContext.GetHeight()  arr = fromstring(data,Int8) arr.shape = (-1,4) arr = arr[:,0:3] print arr.shape  img = Image.fromstring("RGB",(w,h),arr.tostring()).transpose(Image.FLIP_TOP_BOTTOM) \  .save("../tt.png","png")  _glBmpContext.SaveBmp("hehe.bmp")  _glBmpContext.EndBmpContext()

運行,嗯,一切盡在掌握中。我的目的實現了!可以在StartBmpContext后盡情渲染,然后GetMemBmpData獲得數據,然后用Image操作數據保存成各種圖片。最后EndBmpContext關閉環境。

這里需要注意的是內存中的HBITMAP存儲的圖像是倒着的。要在Image中執行transpose(Image.FLIP_TOP_BOTTOM)把它轉回來。

當然這樣做很不安全。可能被惡意地重復創建環境,並且一旦出錯,環境就沒法釋放。可以把_glBmpContext的東西包裝成類,銷毀的時候自動釋放。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM