代碼吸貓 | 用 OpenGL 圖像渲染的養(yǎng)貓計(jì)劃
在掘金上看到最近的新活動(dòng) "代碼吸貓",技術(shù)類文章只要和貓有關(guān)就行。
對(duì)于沒有養(yǎng)貓的程序員,這不是為難人嘛。
不過沒關(guān)系,用 OpenGL 圖像渲染給自己造一只貓吧?。?!
模型構(gòu)造
首先需要構(gòu)造出貓的模型,有能力的話可以直接在三維軟件里面造一個(gè)。
或者像我一樣直接下載免費(fèi)的貓模型,然后把它導(dǎo)入 Blender 3D 軟件中。

在 Blender 中可以預(yù)覽貓模型,或者對(duì)它做一下調(diào)整,最后在把這個(gè)模型導(dǎo)出。
模型加載
導(dǎo)出的 ?obj 文件里面就記錄了模型的頂點(diǎn)信息,接下來就要用 OpenGL 將它繪制出來。
這里要用到 assimp 開源庫(kù),它支持多種模型文件的解析操作,通過它將模型解析成一個(gè)個(gè) Mesh 。
Mesh 的定義如下:
class?Mesh?{
public:
??/*??Mesh?Data??*/
??vector?vertices;
??vector<unsigned?int>?indices;
??vector?textures;
??unsigned?int?VAO;
??//?省略部分代碼
}
Mesh 相當(dāng)于繪制模型上的一個(gè)個(gè)網(wǎng)格或者說面片,它包含了該網(wǎng)格的頂點(diǎn)、紋理信息和繪制索引。
而模型 Model 就是由這一系列網(wǎng)格 Mesh 組成的。

如上圖所示,貓模型是由一個(gè)個(gè)小矩陣組成的,小矩陣就可以理解成 mesh 網(wǎng)格了。
Model 的定義如下:
class?Model
{
public:
??/*??Model?Data?*/
??vector?textures_loaded;????
??vector?meshes;
??//?省略部分代碼??
}??
在實(shí)際繪制的時(shí)候,也是由一個(gè)一個(gè) Mesh 最終繪制成的。
????//?draws?the?model,?and?thus?all?its?meshes
?void?Draw(Shader?shader)
?{
???for(unsigned?int?i?=?0;?i??????meshes[i].Draw(shader);
?}
從圖中也可以看到,貓模型的網(wǎng)格數(shù)量是很多的,導(dǎo)致加載的時(shí)候會(huì)很很慢了,加載方法如下:
??void?loadModel(string?const?&path)
??{
????//?使用?assimp?庫(kù)進(jìn)行加載
????const?aiScene*?scene?=?importer.ReadFile(path,?aiProcess_Triangulate?|?aiProcess_FlipUVs?|?aiProcess_CalcTangentSpace);
????//?檢查是否有錯(cuò)
????if(!scene?||?scene->mFlags?&?AI_SCENE_FLAGS_INCOMPLETE?||?!scene->mRootNode)?//?if?is?Not?Zero
????{
??????cout?<"ERROR::ASSIMP::?"?<endl;
??????return;
????}
????//?獲取模型所在文件夾
????directory?=?path.substr(0,?path.find_last_of('/'));
????//?從根節(jié)點(diǎn)一個(gè)一個(gè)節(jié)點(diǎn)開始處理
????processNode(scene->mRootNode,?scene);
??}
使用 assimp 處理后會(huì)得到一個(gè)根節(jié)點(diǎn),然后順著根節(jié)點(diǎn)一個(gè)一個(gè)往下處理就好了。
??void?processNode(aiNode?*node,?const?aiScene?*scene)
??{
????for(unsigned?int?i?=?0;?i?mNumMeshes;?i++)
????{
??????aiMesh*?mesh?=?scene->mMeshes[node->mMeshes[i]];
??????//?處理得到的?aiMesh?并組裝成定義好的?Mesh?數(shù)據(jù)結(jié)構(gòu)?
??????meshes.push_back(processMesh(mesh,?scene));
????}
????//?處理子節(jié)點(diǎn)
????for(unsigned?int?i?=?0;?i?mNumChildren;?i++)
????{
??????processNode(node->mChildren[i],?scene);
????}
??}
可以看到處理過程大量的 for 循環(huán)操作,所以后續(xù)才會(huì)針對(duì)模型文件的優(yōu)化,加快其加載速度。
模型渲染
得到了最終的 Model 之后,就可以對(duì)它做渲染顯示了。
????//?model?矩陣調(diào)整模型顯示位置和方向
????glm::mat4?model?=?glm::mat4(1.0f);
????model?=?glm::translate(model,?glm::vec3(tranx_x,?tranx_y,?tranx_z));
????model?=?glm::rotate(model,glm::radians(90.0f),glm::vec3(0.0,0.0,1.0));
????model?=?glm::scale(model,?glm::vec3(0.5f,?0.5f,?0.5f));????
????shader.setMatrix4fv("model",?glm::value_ptr(model));
????ourModel.Draw(shader);
由于模型自身就帶了一個(gè)位置和方向,顯示的時(shí)候不一定是我們想要的觀察方位,所以還是要調(diào)整一個(gè)模型矩陣。
最后渲染就可以看到 貓模型 效果啦。



小結(jié)
為了便于觀察,可以處理一下鍵盤或者鼠標(biāo)事件,修改模型矩陣的值,從不同角度擼貓。
目前的貓模型還只是靜態(tài)的,調(diào)整的話也只能用鍵盤調(diào)整,而且還只是改了 移動(dòng)、縮放、旋轉(zhuǎn)這些屬性,貓本身是沒有動(dòng)的。
想要貓自身能動(dòng)的話,還需要模型里面有對(duì)應(yīng)的骨骼動(dòng)畫才可以了,等后面有了這樣的模型,再繼續(xù)迭代 。
OpenGL 相關(guān)的文章歡迎閱讀:

技術(shù)交流,歡迎加我微信:ezglumes ,拉你入技術(shù)交流群。
私信領(lǐng)取相關(guān)資料
推薦閱讀:
音視頻開發(fā)工作經(jīng)驗(yàn)分享 || 視頻版
開通專輯 | 細(xì)數(shù)那些年寫過的技術(shù)文章專輯
NDK 學(xué)習(xí)進(jìn)階免費(fèi)視頻來了
推薦幾個(gè)堪稱教科書級(jí)別的 Android 音視頻入門項(xiàng)目
覺得不錯(cuò),點(diǎn)個(gè)在看唄~

