朝陽科技大學 資訊工程系 專題成果報告 3D 水墨動畫研製 指導教授 : 劉啟東專題組員 : 梁詠傑 (9727055) 許倫維 (9727052) 鄭景文 (9727217) 葉坤育 (9727205) 陳楷壬 (9727202) 中華民國一零一年一月
3D 水墨動畫研製 劉啟東梁詠傑 許倫維 鄭景文 葉坤育 陳楷壬 朝陽科技大學資訊工程系 cfyu@mail.cyut.edu.tw 摘要 水墨文化在中國的文化有相當長久的歷史, 利用墨汁的濃淡以及毛筆的運用來描繪出對比極為強烈的圖, 使其產生不同的渲染效果 我們利用 opengl(open Graphics Library) 寫一個繪圖程式, 利用程式的計算來使畫面產生模擬水墨的黑白效果, 並將儲存的檔案放置於 blender 的遊戲引擎 (blender game engine) 內來及時改變物件的紋理效果, 以達到我們實作動態貼圖 (Dynamic mapping) 的功能 遊戲引擎內再利用 python script 來控制物件的移動以及切換視角 接著使用 Blender 設計一段動畫, 從一開始的草稿到最後的分鏡圖, 紋理的部分改使用 Gimp 來繪製水墨效果, 藉此來觀察兩種不同方式的水墨效果 Abstract Ink culture in the Chinese culture has very long history. The use of ink and brush the use of shading to depict the contrast is very strong figure, to produce differentrenderings.we use open GL (Open Graphics Library) to write a drawing program, calculated using the program to make the picture black and white ink simulation results produced and save the file placed in the blender game engine (blender game engine) to make changes in an objectwithin the texture effect, we have implemented to achieve the dynamic map (Dynamic mapping) functionre-use within the game engine python script to control the movement of objects and switch perspective. Then design an animation using Blender, from the beginning to the final draft of the sub-mirror map, change the texture of some of the effect using Gimp to draw the ink, thereby to observe the effect of ink in two different ways. - 4 -
專題簡介首先使用 OpenGL 所製作的繪圖程式將圖片的水墨效果呈現出來, 再進行動態貼圖及時改變魚的紋理貼圖效果, 並將此功能應用在動畫上, 以便觀察水墨貼圖的效果 遊戲引擎內有三項主要功能 :1. 控制魚的游動方向 2. 切換多種不同的視角來觀察 3. 即時動態貼圖 分為兩大結構 : 1. 動畫部分 : 首先利用 Blender 設計動畫, 從基本的草稿 故事內容再到分鏡圖, 建構完整的動畫場景, 將物件改為 UV 模式輸出成 tga 檔, 再使用 Gimp 繪製物件紋理, 再利用 Blender 的 UV 貼圖把物件的水墨效果呈現出來, 製作成 3-5 分鐘左右的動畫影片 目的在於觀察使用 Gimp 時, 水墨效果是否符合預期 2. 遊戲引擎 : 從 Blender 設計好的場景, 加入 Python Script 來控制物件 ( 魚 ) 的移動 視角的切換 即時切換魚的紋理貼圖 ( 動態貼圖 ) 接著用 OpenGL 寫一個模擬水墨渲染的程式, 將繪製好的多張圖片載入到 Game engine 內, 以便達到動態貼圖功能, 並觀察水墨效果 - 5 -
1. 系統架構流程圖 設定主題 : 3D 水墨動畫研製 設計動畫草稿 分為兩 種主題 利用建立好的場景 來製作動態貼圖 建立分鏡圖 使用 python 控制 物件並切換視角 按照分鏡圖設 計場景 使用 OpenGL 製作 水墨模擬繪圖程式 使用 Gimp 來繪製紋理效果 繪製物件 紋理效果 將紋理貼至 物件上 載入至動態貼 圖內 否 觀察水 墨效果 觀察水 墨效果 否 是 輸出為影片檔 是 輸出為 EXE 檔 完成 圖 1.1-6 -
2. 系統功能方法 2.1 使用 OpenGL 實作模擬水墨程式 OpenGL 水墨模擬流程圖 設定目標 : OpenGL 水墨模擬 程式 建立程式框架 載入貼圖的 UVmap 檔 成功 失敗 利用程式計算 模擬水墨效果 兩種方法模 擬水墨效果 使用論文的演算 法模擬水墨效果 繪製圖片 繪製圖片 觀察水 墨效果 否 否 觀察水 墨效果 是 重新儲存繪 製完成的圖 是 圖 2.1.1 完成 - 7 -
此小節將說明 OpenGL 實做過程 首先我們在做水墨渲染時, 原先採用的方法是參考論文 [1] 所提供的演算法, 使用 OpenGL[2] 以及 NVIDIA Cg[3] 語言寫成, 模擬水的流動以及在紙張上的變化, 藉由水流變化來達到模擬水墨的效果 後來因為此演算法難度太高, 因此將模擬水墨部分修正成, 利用程式的計算來改變筆觸在紙張上的變化 步驟 1. 環境建置步驟 2. 載入 UVmap 檔步驟 3. 模擬水墨效果步驟 4. 重新儲存繪製完成的圖片 引入標頭檔 配置程式基本框架 繪圖區 控制區 主程式區 [4] 首先載入 BMP 檔, 並將圖檔設定為 RGBA,Alpha 值設為 0.5, 讓圖片有半透明遮罩的效果 1. 先替陣列配置記憶體, 設定 [RGBA] 分量值為 [0.0.0.1] 圖案會畫在配置的記憶體內, 並不影響載入的圖檔 2. 將滑鼠指向此記憶體, 並在移動時重複增加 RGB 三個分量 將畫圖用的記憶體內的圖片資料回存成 BMP 檔 表 2.1.1 圖 2.1.2: 使用 OpenGL 模擬繪製效果 - 8 -
2.1.1 部分程式碼說明 : float * texture01() float * ptr = (float *)malloc(m_texheight * m_texwidth * 4 * sizeof(float)); GLuint i,j; float * reg = ptr; for( i = 0; i < m_texheight; i++) for( j = 0; j < m_texwidth; j++) *reg++ = 0.0f; *reg++ = 0.0f; *reg++ = 0.0f; *reg++ = 1.0f; return ptr; 配置空白記憶體大小為高 * 寬 *4, 圖片型別設為 RGBA 因此有 4 個分量這部分為配置一塊空白記憶體, 目的在於寫入畫圖時的資料 圖 2.1.3: 模擬效果 - 9 -
void MotionMouse(int x, int y) int a=4; y= m_texheight - y; int x0 = x - a; int y0 = y + a; int x1 = x + a; int y1 = y - a; if( x0 < 0) x0 = 0; if( y0 >= m_texheight) y0 = m_texheight-1; if( x1 >= m_texwidth) x1 = m_texwidth-1; if( y1 < 0) y1 = 0; for( y = y1; y <= y0; ++y) for(x = x0 ; x <= x1; ++x) one_texbuffer[y*m_texwidth*4+x*4] += 0.05; one_texbuffer[y*m_texwidth*4+x*4+1] += 0.05; one_texbuffer[y*m_texwidth*4+x*4+2] += 0.05; glutpostredisplay(); 這段是在做筆觸的疊加, 因為滑鼠座標只有一個點那樣太小了看不清楚, 所以從滑鼠的位置為中心在一個 4x4 的範圍做顏色變深 (RGB 各 + 0.1) 的處理, 滑鼠移動慢的話就會在同一個位置反覆的增加那就會變得比較清楚 - 10 -
2.2 使用 blender 製作動畫 Blender 動畫流程圖 設定目標 : Blender 水墨動畫 設計動畫 故事架構 繪畫故事版 動畫分鏡圖 建立 3D 模組 設計模組動作 否 觀察動 畫結果 是 利用 Gimp 製作水墨紋理 否 貼上水墨紋 理觀察效果 是 依照分鏡圖運鏡拍攝 圖 2.2.1 輸出影片 - 11 -
首先我們討論故事版, 挑選主角場景, 因為在水墨畫中最常看到竹子 魚 荷葉 蓮花這幾種東西, 所以我們選擇這幾種東西將它製作成水墨動畫 在模型完成後, 最重要的步驟是將主角接上骨架, 將魚接上骨架的過程中, 要照著魚的身型放入數根的骨架如圖 [2.2.2], 這樣才不會造成製作魚的擺動時, 身體產生奇怪的扭曲變形 場景及主角擺設好後, 開始製作魚的擺動如圖 [2.2.3], 製作過程中參考了許多網路影片魚的游動, 觀察許多胸鰭 腹鰭 尾鰭及頭的擺動後, 最後製作出優游的金魚擺動 所有模組的動作都已設定好, 接下來就是將模組加上水墨紋理, 使模組產生水墨的效果, 首先利用 Blender[5] 輸出模組的 UV face 如圖 [2.2.4], 使用 Gimp 繪圖軟體, 讓模組的 UV face 成底圖方便描繪, 利用 Gimp 淡出的功能使貼圖更具水墨的效果, 繪製好水墨紋理如圖 [2.2.5], 將紋理貼至模組, 這部份算是最難的部分 繪製好的水墨紋理與貼上模組後感覺, 具有相大的差別, 所以必須一直繪畫紋理貼上找出最符合水墨的紋理, 完成水墨紋理後如圖 [2.2.6], 接下來根據先前討論好的故事版, 將背景與竹子 荷葉 等等, 模組放置於故事版設定的位置, 並讓主角跟著故事版的流程移動, 接著在將故事版細化成分鏡圖, 來控制 blender 的鏡頭如圖 [2.2.7] 的移動讓拍攝的畫面有重點, 鏡頭的拍攝主要分為整個場景 魚 蓮花 竹子 的特寫, 還有整個主角魚擺動的方式 圖 2.2.2: 放置魚的骨架 - 12 -
圖 2.2.3 : 製作骨架的擺動並記錄下座標 圖 2.2.4:UV face 圖 2.2.5: 水墨紋理 圖 2.2.6: 貼圖水墨效果 圖 2.2.7: 鏡頭畫面 - 13 -
2.3 使用 Blender game engine 實作基本控制 Game Engine 架構流程圖設定主題 : Blender Game Engine 將動畫組設計好 的場景加入 PythonScript 將魚本身的 擺動做出來 加入視角 切換功能 加入動態 貼圖功能 設計魚在場景 內的移動控制 否 執行後觀察 控制效果 是 輸出為 EXE 檔 圖 2.3.1 完成 - 14 -
首先說明 blender 是個甚麼樣的軟體, 它是一套三維繪圖及渲染軟體 它具有跨平台的特性, 支持 FreeBSD,IRIX,GNU/Linux,Microsoft Windows,Mac OS X,Solaris Blender 的安裝後所佔空間很少以及可以運行於不同的平台 雖然它經常不連說明文檔或範例發佈, 但其擁有極豐富的功能 他的特色以及功能有 : 1. 支持不同的幾何圖元, 包括多邊形網紋, 快速表層塑模, 曲線及向量字元 2. 多用途的內部洵染及整合 YafRay 這個開源的射線追蹤套件 3. 動畫工具, 包括了反向動作組件, 可設定骨幹, 結構變形, 關鍵影格, 時間線, 非線性動畫, 系統規定參數, 頂點量重及柔化動量組件, 包括網孔碰撞偵察和一個具有偵察碰撞的粒子系統 4. 使用 Python[6] 語言來創作及製作遊戲及工作自動化腳本 5. 基本的非線性影像編輯及製作功能 6. Game Blender, 一個子計劃, 用以製作實時的電腦遊戲 功能 : 1. 在 Blender 中, 物體與數據是分離的, 這使其可以快速塑模 2. 所有場景, 物件, 材料, 材質, 聲音, 圖片, 後期製作特效均可整合至最後生成的.blend 動畫檔裡 3. 可透過.blend 檔案來自訂使用者介面 視角切換功能 : 動態貼圖 : 魚的控制 : 先在 Blender 內架設多台攝影機, 然後用 Python 程式分別切換各個攝影機 須要先載入兩個 ( 或多個 ) 紋理貼圖, 之後再用 Python 寫出切換貼圖的功能 mat = VideoTexture.materialID(obj, 'IMgfish.bmp') 此函式是指向你所需要載入的圖片先在 logic 的地方設定方向鍵功能, 接著再指向你所寫的 python 讓他會照你所寫的方式移動 表 2.3.1-15 -
2.3.1 動態貼圖程式碼說明 (Python Script): import GameLogic as logic // 載入邏輯函數 import GameKeys as events // 載入鍵盤控制 import VideoTexture // 載入影像紋理 co = logic.getcurrentcontroller() obj = co.owner a=0 sensor = co.sensors["keyboard"] keylist = sensor.events for key in keylist: if key[1] == GameLogic.KX_INPUT_JUST_ACTIVATED: if key[0] == events.rkey: // 按 R 鍵切換為 A 圖 a=1 if key[0] == events.tkey: // 按 T 鍵切換為 B 圖 a=0 if not 'texture' in obj: mat=videotexture.materialid(obj,"ma"+'texture') // 載入指定圖檔 obj['texture'] = VideoTexture.Texture(obj, mat) obj['file'] = 'gfish.bmp' obj['changed'] = 0 else: if obj['changed']: path = logic.expandpath('//'+obj['file']) obj.texture.source = VideoTexture.ImageFFmpeg(path) obj['changed'] = 0 obj.texture.refresh(true) if a == 1: obj['file'] = 'ink.bmp' // 載入 A 圖,ink.bmp obj['changed'] = 1 elif a == 0: obj['file']='gfish.bmp' // 載入 B 圖,gfish.bmp obj['changed'] = 1 利用此程式碼達到動態貼圖功能 (Dynamic maps), 如圖 [2.3.2] 和圖 [2.3.3] 藉由 Game engine 的 Python Script 來完成, 目的在於可以即時繪製各種不同的紋理, 在馬上貼圖到魚的物件上面, 以此來觀察各種不同紋理所呈現的效果 - 16 -
圖 2.3.2: 按 R 切換成此圖片 圖 2.3.3: 按 T 切換成此圖片 - 17 -
2.3.2 魚的控制 : 首先分別設定所需要控制的按鍵, 在連接到邏輯控制部分, 這裡我們只需要用到基本的 AND 就好, 最後連接到你設定好的動作 W: 向前 A: 向後 S: 向左 D: 向右 Q E: 分別為向上及向下移動 圖 2.3.4: 控制設定圖 鍵盤設定邏輯控制執行動作 圖 2.3.5: 簡單說明控制設定 執行動作的部分須要先設定骨架, 首先設定好各部位骨架名稱, 再慢慢調整所需要的動作, 讓邏輯控制連接過來時可以依照設定好的動作移動 圖 2.3.6: 骨架動作編輯 - 18 -
3. 結果與討論 關於我們這次做的專題, 許多地方與一開始的設定目標有落差, 在我們實做以後才發現, 按照既定目標下去做的話, 有許多要克服的困難點 例如 OpenGL 水墨渲染的部分, 原先是需要使用論文所提供的演算法 [1] 來做水墨的效果, 但是他是先計算水在紙張上的流動變化, 再利用許多方程式將之修改為演算法, 我們在演算法的實做上遇到許多難題 接著動畫部分, 物件模組都是網路尋找居多, 只是在轉變成讓 Blender 使用時結構都會跑掉, 需要自己再重新修改架構 再來我們對鏡頭運鏡, 動畫場景等基本技巧都不夠熟練, 在檢視成品時發現有許多地方都有些粗糙 而且在做 UV 貼圖時, 預定是用 OpenGL 來繪圖, 由於效果太差於是決定改用 Gimp 來繪製圖片 最後 Game Engine 的部分並無太大問題, 過程中遇到的難點是在製作魚的擺動, 在控制魚左轉時魚頭要往另一邊方向跟著擺動, 這部分需要把原本製作的架構重新修改過, 魚的骨幹需要做編號及命名, 讓他在擺動過程時, 每個節點的地方都會有小幅度的變化, 可是這個部分的效果最終還是沒有完成 4. 結論 這次的專題所致做出來的成果跟我們原先預定的目標有落差, 水墨這個主題我們並沒有把它完整呈現出來, 主要的核心部分在 OpenGL, 一開始是要呈現他的擬真性, 可是演算法的難度比想像中的高, 因此只好改變目標將重點放在水墨動畫的呈現上, 但是我們在之後還是會繼續修改, 希望能達成我們原先所期望的效果出來 在實做過程中所遇到的難題我們也有一一請教劉啟東老師, 老師也不吝嗇的告訴我們許多問題點, 也幫我們尋找了許多相關資料, 雖然成果並不是令他很滿意, 但是過程中我們也學到了許多東西, 學習如何把一個問題拆開來變成許多小問題, 接著大家分工將他完成在合併 - 19 -
OpenGL 程式碼附件 : //#include <GL/glew.h> // 用 glew 開啟 OpenGL 延伸模式參考 http://glew.sourceforge.net/basic.html #include <GL/glut.h> #include <math.h> #include <malloc.h> #include <stdlib.h> #include <string.h> #include <windows.h> #include <wingdi.h> #include <stdio.h> char BMP_Header[54]; #define BMP_Header_Length 54 void WindowSize(int, int ); void Keyboard(unsigned char, int, int ); void MotionMouse(int,int ); void Display(void); void grab(void); void SetMaterial(void); void texture(void); // 負責視窗及繪圖內容的比例 // 獲取鍵盤輸入 // 獲取滑鼠按下期間的訊息 // 設定材質屬性 // 處理材質貼圖的相關指令 unsigned char *LoadBitmapFile(char *, BITMAPINFO *); // 用來讀取 BMP 圖檔 unsigned char *hbmp; /////////////// // 宣告全域變數 GLuint m_texid[2] = 0,1; GLuint m_texwidth = 256; GLuint m_texheight = 256; float * one_texbuffer ; unsigned char * two_texbuffer ; unsigned char *bottomup_pixel; unsigned char *image; bool DisplayUV = true; // 材質編號 // 設定材質的寬度 // 設定材質的高度 // 材質陣列的指標 // 材質陣列的指標 // 得到圖案, 能直接讓 OpenGL 使用的資料 - 20 -
void QUADS(void) glbegin(gl_quads); gltexcoord2f(0,0); glvertex2f(0,0); gltexcoord2f(1,0); glvertex2f(256,0); gltexcoord2f(1,1); glvertex2f(256,256); gltexcoord2f(0,1); glvertex2f(0,256); glend(); // 繪製畫面 void Display(void) glclearcolor(1.0, 0.0, 0.0, 1.0); // 背景顏色 glclear(gl_color_buffer_bit GL_DEPTH_BUFFER_BIT); glviewport(0,0,m_texwidth,m_texheight); glloadidentity(); glenable(gl_texture_2d); glbindtexture(gl_texture_2d, m_texid[0]); glteximage2d(gl_texture_2d, 0, 4, m_texwidth, m_texheight, 0, GL_RGBA, GL_FLOAT, one_texbuffer); QUADS(); if (DisplayUV) glbindtexture(gl_texture_2d, m_texid[1]); glteximage2d(gl_texture_2d, 0, 0, m_texwidth, m_texheight,0, GL_RGBA, GL_UNSIGNED_BYTE, two_texbuffer); QUADS(); glutswapbuffers(); - 21 -
// 處理螢幕尺寸變更 void ChangeSize(GLsizei w, GLsizei h) glmatrixmode(gl_projection); glloadidentity(); gluortho2d(0,256,0,256); // 參數代表 ( 左下角 x 座標, 右上角 x 座標, 左下角 y 座標, 右上角 y 座標 ) glmatrixmode(gl_modelview); glloadidentity(); float * texture01() // 替陣列配置記憶體大小是相當於 float m_texbuffer[m_height][m_width][4]; float * ptr = (float *)malloc(m_texheight * m_texwidth * 4 * sizeof(float)); // 歸零 GLuint i,j; float * reg = ptr; for( i = 0; i < m_texheight; i++) for( j = 0; j < m_texwidth; j++) *reg++ = 0.0f; *reg++ = 0.0f; *reg++ = 0.0f; *reg++ = 1.0f; return ptr; - 22 -
void SetupRC(void) one_texbuffer = texture01(); glenable(gl_texture_2d); // 宣告一個材質 glgentextures(2, m_texid); glbindtexture( GL_TEXTURE_2D, m_texid[0]); gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_S, GL_CLAMP); gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_T, GL_CLAMP); gltexparameteri(gl_texture_2d, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gltexparameteri(gl_texture_2d, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glteximage2d(gl_texture_2d, 0, 4, m_texwidth, m_texheight, 0, GL_RGBA, GL_FLOAT, one_texbuffer); void MotionMouse(int x, int y) int a=4; y= m_texheight - y; int x0 = x - a; int y0 = y + a; int x1 = x + a; int y1 = y - a; if( x0 < 0) x0 = 0; if( y0 >= m_texheight) y0 = m_texheight-1; if( x1 >= m_texwidth) x1 = m_texwidth-1; if( y1 < 0) y1 = 0; - 23 -
for( y = y1; y <= y0; ++y) for(x = x0 ; x <= x1; ++x) one_texbuffer[y*m_texwidth*4+x*4] += 0.1; one_texbuffer[y*m_texwidth*4+x*4+1] += 0.1; one_texbuffer[y*m_texwidth*4+x*4+2] += 0.1; glutpostredisplay(); void SetMaterial() float material_ambient[] = 0.2, 0.2, 0.2, 1.0; float material_diffuse[] = 0.3, 0.3, 0.3, 1.0; float material_specular[] = 0.2, 0.2, 0.2, 1.0; glmaterialfv( GL_FRONT, GL_AMBIENT, material_ambient); glmaterialfv( GL_FRONT, GL_DIFFUSE, material_diffuse); glmaterialfv( GL_FRONT, GL_SPECULAR, material_specular); void texture(void) BITMAPINFO bmpinfo; // 用來存放 HEADER 資訊 image = LoadBitmapFile("gfish.bmp", &bmpinfo); m_texwidth = bmpinfo.bmiheader.biwidth; m_texheight = bmpinfo.bmiheader.biheight; glbindtexture( GL_TEXTURE_2D, m_texid[1]); glteximage2d(gl_texture_2d,0,4,m_texwidth,m_texheight,0,gl_rgba,gl _UNSIGNED_BYTE,two_TexBuffer); gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_S, GL_CLAMP); gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_T, GL_CLAMP); gltexparameteri(gl_texture_2d, GL_TEXTURE_MAG_FILTER, - 24 -
GL_NEAREST); gltexparameteri(gl_texture_2d, GL_TEXTURE_MIN_FILTER, GL_NEAREST); unsigned char *LoadBitmapFile(char *filename, BITMAPINFO *bitmapinfo) FILE *fp; BITMAPFILEHEADER bitmapfileheader; // Bitmap file header unsigned char *bitmapimage; // Bitmap image data unsigned int linfosize; // Size of information unsigned int lbitsize; // Size of bitmap unsigned char *ptr; int pixel; int p=0; fp = fopen(filename, "rb"); fread(&bitmapfileheader, sizeof(bitmapfileheader), 1, fp); 取 bitmap header // 讀 linfosize = bitmapfileheader.bfoffbits - sizeof(bitmapfileheader); //Info 的 size fread(bitmapinfo, linfosize, 1, fp); pixel = (bitmapinfo->bmiheader.biwidth)*(bitmapinfo->bmiheader.biheight); lbitsize = pixel*3; bitmapimage = new BYTE[lBitSize]; fread(bitmapimage, 1, lbitsize, fp); // 讀取影像檔 fclose(fp); // 此時傳回 bitmapimage 的話, 顏色會是 BGR 順序, 下面迴圈會改順序為 RGB two_texbuffer = (unsigned char *)malloc(pixel * 4 * sizeof(char)); ptr = two_texbuffer; - 25 -
for( int i=0 ; i<pixel ; i++, p+=3 ) *ptr++ = bitmapimage[p+2]; *ptr++ = bitmapimage[p+1]; *ptr++ = bitmapimage[p]; *ptr++ = 128; delete[] bitmapimage; return two_texbuffer; void Init() glenable(gl_blend); glblendfunc(gl_src_alpha, GL_ONE_MINUS_SRC_ALPHA); void MyKey(unsigned char key, int x, int y) if (key == ' ') // 按空白鍵來開關 load 進來的 BMP 檔 DisplayUV =!DisplayUV; glutpostredisplay(); if(key=='a') grab(); exit(0); // 按 a 把畫面上的圖案儲存成 BMP 檔 - 26 -
void grab(void) FILE* poriginfile = fopen("gfish.bmp", "rb"); // 設定並打開兩個 bmp 檔 FILE* pgrabfile = fopen("grab.bmp", "wb"); FILE* pdummyfile; FILE* pwritingfile; GLubyte* ppixeldata; GLubyte BMP_Header[BMP_Header_Length]; GLint i, j; GLint PixelDataLength; i = m_texwidth * 3; // 計算 PixelData while( i%4!= 0 ) ++i; PixelDataLength = i * m_texheight; // 配置記憶體 ppixeldata = (GLubyte*)malloc(PixelDataLength); if( ppixeldata == 0 ) exit(0); pdummyfile = fopen("gfish.bmp", "rb"); if( pdummyfile == 0 ) exit(0); pwritingfile = fopen("grab.bmp", "wb"); if( pwritingfile == 0 ) exit(0); // 讀取 PixelData glpixelstorei(gl_unpack_alignment, 4); glreadpixels(0, 0, m_texwidth, m_texheight, GL_BGR_EXT, GL_UNSIGNED_BYTE, ppixeldata); - 27 -
// 把 dummy.bmp 的標頭檔複製到新檔案 fread(bmp_header, sizeof(bmp_header), 1, pdummyfile); fwrite(bmp_header, sizeof(bmp_header), 1, pwritingfile); fseek(pwritingfile, 0x0012, SEEK_SET); i = m_texwidth; j = m_texheight; fwrite(&i, sizeof(i), 1, pwritingfile); fwrite(&j, sizeof(j), 1, pwritingfile); // 寫入 PixelData fseek(pwritingfile, 0, SEEK_END); fwrite(ppixeldata, PixelDataLength, 1, pwritingfile); fclose(pdummyfile); fclose(pwritingfile); free(ppixeldata); int main(int argc, char* argv[]) glutinitdisplaymode(glut_double GLUT_RGBA GLUT_DEPTH); glutcreatewindow(" 水墨筆觸 "); glutdisplayfunc(display); // 設定處理繪製的函數 glutreshapefunc(changesize); // 設定處理視窗尺寸變更的函數 glutmotionfunc(motionmouse); glutkeyboardfunc(mykey); SetMaterial(); glutreshapewindow(m_texwidth, m_texheight); // 要求將視窗尺寸切換到 256 x 256 Init(); SetupRC(); texture(); glutmainloop(); return 0; - 28 -
參考文獻 [1] CHU SIU HANG Making Digital Painting Organic, August 2007, Hong Kong [2] OpenGL 3.3 Reference Pages http://www.opengl.org/sdk/docs/man3/ [3] The Cg tutorial http://http.developer.nvidia.com/cgtutorial/cg_tutorial_chapter01.html [4] OpenGL 超級寶典 ( 簡 ) 人民郵電出版社譯 [5] Blender http://www.blender.org/ [6] Python http://www.python.org/ [7] 陳鐘誠的網站 http://ccckmit.wikidot.com/3d:main [8] 阿杰老師 http://www.tiec.tp.edu.tw/lt/blog/325 [9] 中文教學網站 http://www.fhkkta.com/category-3d.html [10] Iwantpass 中文教學網站 http://iwantpass.wikispaces.com/ - 29 -