系列推薦文章:
OpenGL/OpenGL ES入門:圖形API以及專業名詞解析
OpenGL/OpenGL ES入門:渲染流程以及固定存儲着色器
OpenGL/OpenGL ES入門:圖像渲染實現以及渲染問題
OpenGL/OpenGL ES入門:基礎變換 - 初識向量/矩陣
OpenGL/OpenGL ES入門:紋理初探 - 經常使用API解析
OpenGL/OpenGL ES入門: 紋理應用 - 紋理座標及案例解析(金字塔)
OpenGL/OpenGL ES入門: 頂點着色器與片元着色器(OpenGL過渡OpenGL ES)
OpenGL/OpenGL ES入門: GLKit以及API簡介
OpenGL/OpenGL ES入門: GLKit使用以及案例算法
在上一篇文章OpenGL/OpenGL ES 紋理初探 - 經常使用API解析中,咱們講述了紋理相關經常使用的API。加載紋理只是在幾何圖形上應用紋理的第一步。最低限度咱們必須同時提供紋理座標,並設置紋理座標環繞模式和紋理過濾。最後,咱們能夠選擇對紋理進行Mip
貼圖,以提升紋理貼圖性能和/或視覺質量。緩存
整體上說,經過爲每一個頂點指定一個紋理座標而直接在幾何圖形上進行紋理貼圖的。紋理座標要麼是指定爲着色器的一個屬性,要麼經過算法計算出來。bash
紋理貼圖中的紋理單元是做爲一個更加抽象(常常是浮點值)的紋理座標,而不是做爲內存位置(在像素圖中則是這樣)進行尋址的。典型狀況下,紋理座標是0.0~1.0之間的浮點值指定的。函數
紋理座標命名爲s、t、r和q(與頂點座標x、y、z和w相似)
,支持從一維到三維的紋理座標,而且能夠選擇一種對座標進行縮放的方法。post
q
座標對於幾何圖形座標w
。這是一個縮放因子,做用於其餘紋理座標。也就是說,實際上所使用的紋理座標是s/q、t/q和r/q
。在默認狀況下,q
設置爲1.0。性能
着重瞭解2D紋理座標,在x
和y
軸上,範圍爲0~1之間。使用紋理座標獲取紋理顏色叫作採用(Sampling
)。 紋理座標起始於(0,0),也就是紋理圖片的左下角,終止於(1,1)。即紋理圖片的右上角測試
經過下圖,體會一下紋理座標的映射:ui
下面將經過金字塔案例講解一下紋理的使用,效果圖以下:spa
void SetupRC()
{
// 設置背景色
glClearColor(0.7, 0.7, 0.7, 1.0);
// 初始化shaderManager
shaderManager.InitializeStockShaders();
// 開啓深度測試
glEnabel(GL_DEPTH_TEST);
// 分配紋理對象
/*
參數1: 紋理對象個數
參數2: 紋理對象指針
*/
glGenTextures(1, &textureID);
// 綁定紋理
/*
參數1: 紋理狀態2D
參數2: 紋理對象
*/
glBindTexture(GL_TEXTURE_2D, textureID);
// 將TGA文件加載爲2D紋理
/*
參數1: 紋理文件名稱
參數2和參數3: 須要的縮小和放大過濾器
參數4: 紋理座標環繞模式
*/
LoadTGATexture("stone.tga", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR, GL_CLAMP_TO_EDGE);
// 創造金字塔pyramidBatch
MakePyramid(pyramidBatch);
// 相機frame MoveForward(平移)
/*
參數1:Z,深度(屏幕到圖形的Z軸距離)
*/
cameraFrame.MoveForward(-10);
}
複製代碼
// 將TGA文件加載爲2D紋理。
bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
GLbyte *pBits;
int nWidth, nHeight, nComponents;
GLenum eFormat;
//一、讀紋理位,讀取像素
/*
參數1:紋理文件名稱
參數2:文件寬度地址
參數3:文件高度地址
參數4:文件組件地址
參數5:文件格式地址
返回值:pBits,指向圖像數據的指針
*/
pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
if(pBits == NULL)
return false;
//二、設置紋理參數
/*
參數1:紋理維度
參數2:爲S/T座標設置模式
參數3:wrapMode,環繞模式
*/
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
/*
參數1:紋理維度
參數2:線性過濾
參數3:wrapMode,過濾模式
*/
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
//3.載入紋理
/*
參數1:紋理維度
參數2:mip貼圖層次
參數3:紋理單元存儲的顏色成分(從讀取像素圖是得到)
參數4:加載紋理寬
參數5:加載紋理高
參數6:加載紋理的深度
參數7:像素數據的數據類型(GL_UNSIGNED_BYTE,每一個顏色份量都是一個8位無符號整數)
參數8:指向紋理圖像數據的指針
*/
glTexImage2D(GL_TEXTURE_2D, 0, nComponents, nWidth, nHeight, 0,
eFormat, GL_UNSIGNED_BYTE, pBits);
//使用完畢釋放pBits
free(pBits);
//4.加載Mip,紋理生成全部的Mip層
//參數:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
glGenerateMipmap(GL_TEXTURE_2D);
return true;
}
複製代碼
-------- 前情導入 ---------
一、 設置法線
void Normal3f(GLfloat x, GLfloat y, GLfloat z);
Normal3f: 添加一個表面發現(法線座標與Vertex頂點座標中的Y軸一致)
表面法線是有方向的向量,表明表面或頂點面對的方向(相反的方向)。在多數的光照模式下必須使用
二、 設置紋理座標
void MultiTexCoord2f(GLuint texture, GLclampf s, GLclampf t);
參數1:texture,紋理層次,對於使用存儲着色器來進行渲染,設置爲0
參數2:s: 對應頂點座標中的x座標
參數3:t: 對應頂點座標中的y
(s,t,r,q對應頂點座標的x,y,z,w)
pyramidBatch.MultiTexCoord2f(0,s,t);
三、 設置頂點座標
void Vertex3f(GLfloat x, GLfloat y, GLfloat z);
void Vertex3fv(M3DVector3f vVertex);
向三角形批次類添加頂點數據(x,y,z);
pyramidBatch.Vertex3f(-1.0f, -1.0f, -1.0f);
四、獲取從三點找到一個座標(三點肯定一個面)
void m3dFindNormal(result, point1, point2, point3);
參數1:結果
參數2~4:3個頂點數據
複製代碼
金字塔座標解析:3d
在座標系中繪製一個金字塔,座標原點位於金字塔中心
頂點座標
塔頂座標(0.0, 1.0, 0.0)
vBackLeft(-1.0, -1.0, -1.0)
vBackRight(1.0. -1.0, -1.0)
vFrontRight(1.0, -1.0, 1.0)
vFrontLeft(-1.0, -1.0, 1.0)
看下面圖片描述了金字塔底部兩個三角形的紋理座標
紋理座標
三角形A紋理座標
vBackLeft(0.0, 0.0, 0.0)
vBackRight(0.0, 1.0, 0.0)
vFrontRight(0.0, 1.0, 1.0)三角形B紋理座標
vFrontLeft(0.0, 0.0, 1.0)
vBackLeft(0.0, 0.0, 0.0)
vFrontRight(0.0, 1.0, 1.0)
//繪製金字塔
void MakePyramid(GLBatch& pyramidBatch)
{
// 經過pyramidBatch組建三角形批次
/*
參數1:類型
參數2:頂點數
參數3:這個批次中將會應用1個紋理
注意:若是不寫這個參數,默認爲0,表示應用1個紋理
*/
pyramidBatch.Begin(GL_TRIANGLES, 18, 1);
//塔頂
M3DVector3f vApex = { 0.0f, 1.0f, 0.0f };
M3DVector3f vFrontLeft = { -1.0f, -1.0f, 1.0f };
M3DVector3f vFrontRight = { 1.0f, -1.0f, 1.0f };
M3DVector3f vBackLeft = { -1.0f, -1.0f, -1.0f };
M3DVector3f vBackRight = { 1.0f, -1.0f, -1.0f };
M3DVector3f n;
//金字塔底部
//底部的四邊形 = 三角形A + 三角形B
//三角形A = (vBackLeft, vBackRight, vFrontRight)
//1.找到三角形A 法線
m3dFindNormal(n, vBackLeft, vBackRight, vFrontRight);
//vBackLeft
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft);
//vBackRight
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackRight);
//vFrontRight
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
pyramidBatch.Vertex3fv(vFrontRight);
//三角形B =(vFrontLeft,vBackLeft,vFrontRight)
//1.找到三角形B 法線
m3dFindNormal(n, vFrontLeft, vBackLeft, vFrontRight);
//vFrontLeft
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
pyramidBatch.Vertex3fv(vFrontLeft);
//vBackLeft
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft);
//vFrontRight
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
pyramidBatch.Vertex3fv(vFrontRight);
// 金字塔前面
//三角形:(Apex,vFrontLeft,vFrontRight)
m3dFindNormal(n, vApex, vFrontLeft, vFrontRight);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontLeft);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontRight);
//金字塔左邊
//三角形:(vApex, vBackLeft, vFrontLeft)
m3dFindNormal(n, vApex, vBackLeft, vFrontLeft);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontLeft);
//金字塔右邊
//三角形:(vApex, vFrontRight, vBackRight)
m3dFindNormal(n, vApex, vFrontRight, vBackRight);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontRight);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackRight);
//金字塔後邊
//三角形:(vApex, vBackRight, vBackLeft)
m3dFindNormal(n, vApex, vBackRight, vBackLeft);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackRight);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft);
//結束批次設置
pyramidBatch.End();
}
複製代碼
void RenderScene(void)
{
//1.顏色值&光源位置
static GLfloat vLightPos [] = { 1.0f, 1.0f, 0.0f };
static GLfloat vWhite [] = { 1.0f, 1.0f, 1.0f, 1.0f };
//2.清理緩存區
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//3.當前模型視頻壓棧
modelViewMatrix.PushMatrix();
//添加照相機矩陣
M3DMatrix44f mCamera;
//從camraFrame中獲取一個4*4的矩陣
cameraFrame.GetCameraMatrix(mCamera);
//矩陣乘以矩陣堆棧頂部矩陣,相乘結果存儲到堆棧的頂部 將照相機矩陣 與 當前模型矩陣相乘 壓入棧頂
modelViewMatrix.MultMatrix(mCamera);
//建立mObjectFrame矩陣
M3DMatrix44f mObjectFrame;
//從objectFrame中獲取矩陣,objectFrame保存的是特殊鍵位的變換矩陣
objectFrame.GetMatrix(mObjectFrame);
//矩陣乘以矩陣堆棧頂部矩陣,相乘結果存儲到堆棧的頂部 將世界變換矩陣 與 當前模型矩陣相乘 壓入棧頂
modelViewMatrix.MultMatrix(mObjectFrame);
//4.綁定紋理,由於咱們的項目中只有一個紋理。若是有多個紋理。綁定紋理很重要
glBindTexture(GL_TEXTURE_2D, textureID);
/*5.點光源着色器
參數1:GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF(着色器標籤)
參數2:模型視圖矩陣
參數3:投影矩陣
參數4:視點座標系中的光源位置
參數5:基本漫反射顏色
參數6:圖形顏色(用紋理就不須要設置顏色。設置爲0)
*/
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightPos, vWhite, 0);
//pyramidBatch 繪製
pyramidBatch.Draw();
//模型視圖出棧,恢復矩陣(push一次就要pop一次)
modelViewMatrix.PopMatrix();
//6.交換緩存區
glutSwapBuffers();
}
複製代碼
void ShutdownRC(void)
{
// 清理…例如刪除紋理對象
glDeleteTextures(1, &textureID);
}
複製代碼
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP)
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_DOWN)
objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_LEFT)
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
if(key == GLUT_KEY_RIGHT)
objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
glutPostRedisplay();
}
複製代碼
void ChangeSize(int w, int h)
{
//1.設置視口
glViewport(0, 0, w, h);
//2.建立投影矩陣
viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);
//viewFrustum.GetProjectionMatrix() 獲取viewFrustum投影矩陣
//並將其加載到投影矩陣堆棧上
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//3.設置變換管道以使用兩個矩陣堆棧(變換矩陣modelViewMatrix ,投影矩陣projectionMatrix)
//初始化GLGeometryTransform 的實例transformPipeline.經過將它的內部指針設置爲模型視圖矩陣堆棧 和 投影矩陣堆棧實例,來完成初始化
//固然這個操做也能夠在SetupRC 函數中完成,可是在窗口大小改變時或者窗口建立時設置它們並無壞處。並且這樣能夠一次性完成矩陣和管線的設置。
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
複製代碼
本篇主要描述了關於紋理方面的代碼,後面的幾個函數RenderScene、SpecialKeys、ChangeSize等
在前幾篇文章中都詳細介紹過,因此這裏就一筆帶過。
下面整理了一張流程圖,僅供你們參考: