呵呵,有了第一次的經驗,咱們就要開始咱們的GL離屏渲染的綁定了。 html
關 於OpenGL的離屏渲染,前面已經有一些涉及了。再說一下吧,OpenGL有兩種渲染方式:一種是經過操做系統打開窗口進行渲染,而後能夠直接在屏幕上 顯示,這種渲染方式叫作屏幕渲染。一種經過在內存中一塊位圖區域內渲染,這種渲染方式在沒有經過SwapBuffer方式前不能夠在屏幕上顯示,因此這種 方法叫離屏渲染。通常來講,OpenGL經過屏幕顯示方式展現它的魅力,屏幕渲染方式是大多數狀況下的首選。並且不少窗口系統都有實現OpenGL的屏幕 渲染方式。好比glut,wxwidgets,QT,gtk。可是有些時候咱們不須要屏幕顯示。只是想把它保存成一個圖像文件就好。並且咱們就是不想打開 一個窗口。這樣就須要用離屏渲染的方法。在內存中畫,最後保存成圖像。 python
可 惜的是OpenGL沒有統一的離屏渲染操做API。GL把這些事情所有交給系統。這樣就致使各個系統的離屏渲染都有各自的 API,Windows,X,Apple,SGI,OS2都有本身的離屏RC上下文構建方法,每套API都不一樣。在缺乏了榜樣的力量後,各個系統就紛紛開 始諸侯割據了,就形成天下大亂的局勢。這樣確實不太好。不過如今亂了就讓它亂吧,誰叫咱們是「小程序員」?天下大勢就這樣,你要怎麼着吧-_-! 沒辦法。實在是沒辦法~~~現在的世界太瘋狂…… 現在的世界變化快…… ios
我仍是靜下心來看看這麼在各個系統上實現離屏渲染吧。OS2大概八輩子用不到了吧,Apple是高高在上的貴族們的東西。我們老百姓……仍是算了吧。老老實實研究一下Windows和X吧。因而先開始研究WGL。 c++
WGL要創建離屏渲染,能夠參看官方解釋,不過太多,太亂了,紅寶書中的解釋比較淺顯。這裏也有兩句解釋(不過這裏主要是SIG的解釋,X的解釋也比較詳細)。最使人激動的是這裏有win32上的完整例子。 程序員
簡單得說吧,要進行離屏渲染,win32下須要作下面的幾個步驟: 小程序
好了,可用的渲染過程以下: windows
#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安全 |
建立dcapp |
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的東西包裝成類,銷燬的時候自動釋放。