OpenGL學習筆記6:高階紋理知識
矩形紋理
紋理目標為GL_TEXTURE_RECTANGLE。
首選,矩形紋理不能進行Mip貼圖;
然後,矩形紋理不是標準化的(實際上是對畫素定址)
最後,紋理座標不能重複,並且不支援紋理壓縮
載入矩形紋理
bool LoadTGATextureRect(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
GLbyte *pBits;
int nWidth, nHeight, nComponents;
GLenum eFormat;
// 讀入紋理位
pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
if(pBits == NULL)
return false;
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, wrapMode);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, wrapMode);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, minFilter);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, magFilter);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1 );
glTexImage2D(GL_TEXTURE_RECTANGLE, 0, nComponents, nWidth, nHeight, 0,
eFormat, GL_UNSIGNED_BYTE, pBits);
free(pBits);
return true;
}
使用矩形紋理
載入矩形紋理:
glBindTexture(GL_TEXTURE_RECTANGLE, uiTextures[3]);
LoadTGATextureRect("OpenGL-Logo.tga", GL_NEAREST, GL_NEAREST, GL_CLAMP_TO_EDGE) ;
對矩形紋理投影矩陣的設定:
M3DMatrix44f mScreenSpace;
m3dMakeOrthographicMatrix(mScreenSpace, 0.0f, 800.0f, 0.0f, 600.0f, -1.0f, 1.0f);
設定矩形紋理從0.0到標誌寬度或者高度範圍內的紋理座標:
int x = 500; int y = 155; int width = 300; int height = 155; logoBatch.Begin(GL_TRIANGLE_FAN, 4, 1);
// Upper left hand corner
logoBatch.MultiTexCoord2f(0, 0.0f, height);
logoBatch.Vertex3f(x, y, 0.0);
// Lower left hand corner
logoBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
logoBatch.Vertex3f(x, y - height, 0.0f);
// Lower right hand corner
logoBatch.MultiTexCoord2f(0, width, 0.0f);
logoBatch.Vertex3f(x + width, y - height, 0.0f);
// Upper righ hand corner
logoBatch.MultiTexCoord2f(0, width, height);
logoBatch.Vertex3f(x + width, y, 0.0f);
logoBatch.End();
矩形紋理的貼圖著色器需要將取樣器從sampler2D改變成samplerRect型別:
// Rectangle Texture (replace) Shader
// Fragment Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 140
out vec4 vFragColor;
uniform sampler2DRect rectangleImage;
smooth in vec2 vVaryingTexCoord;
void main(void)
{
vFragColor = texture(rectangleImage, vVaryingTexCoord);
}
立方體貼圖
立方體貼圖是作為一個單獨的由組成立方體6個面的6個正方形的2D影象組成的紋理物件看待。
載入立方體貼圖
立方體貼圖新增了以下6個值,這些值可以傳遞到glTexImage2D:
GL_TEXTURE_CUBE_MAP_POSITIVE_X,GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
GL_TEXTURE_CUBE_MAP_POSITIVE_Y,GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
GL_TEXTURE_CUBE_MAP_POSITIVE_Z,GL_TEXTURE_CUBE_MAP_NEGATIVE_Z.
例如,要載入x軸正方向的貼圖:
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X,0,GL_RGBA,iWidth,iHeight,0,GL_RGBA,GL_UNSIGNED_BYTE,pImage);
建立天空盒
例如,我們建立一個在每個方向到原點距離都為20個單位長度的立方體:
gltMakeCube(cubeBatch,20.0f);
然後使用如下著色器對從立方體的中心指向每個角的向量標準化:
立方體貼圖頂點著色器
// Skybox Shader
// Vertex Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130
// Incoming per vertex... just the position
in vec4 vVertex;
uniform mat4 mvpMatrix; // Transformation matrix
// Texture Coordinate to fragment program
varying vec3 vVaryingTexCoord;
void main(void)
{
// Pass on the texture coordinates
vVaryingTexCoord = normalize(vVertex.xyz);
// Don't forget to transform the geometry!
gl_Position = mvpMatrix * vVertex;
}
立方體貼圖片段著色器
// Skybox Shader
// Fragment Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130
out vec4 vFragColor;
uniform samplerCube cubeMap;
varying vec3 vVaryingTexCoord;
void main(void)
{
vFragColor = texture(cubeMap, vVaryingTexCoord);
}
最後消除立方體邊緣的縫隙:
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
建立反射
首先必須使用表面法線和指向頂點的向量在著色器中建立一個視覺座標系中的反射向量。另外,為了獲得一個真實的反射,還要考慮照相機的方向。從GLFrame類中提取照相機的旋轉矩陣並進行轉置。然後將其作為統一值,與另一個變換矩陣(用來與前述的反射向量進行旋轉,這個反射實際上就是立方體貼圖紋理座標)一起提供給著色器。
// Reflection Shader
// Vertex Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130
// Incoming per vertex... position and normal
in vec4 vVertex;
in vec3 vNormal;
uniform mat4 mvpMatrix;
uniform mat4 mvMatrix;
uniform mat3 normalMatrix;
uniform mat4 mInverseCamera;
// Texture coordinate to fragment program
smooth out vec3 vVaryingTexCoord;
void main(void)
{
// Normal in Eye Space
vec3 vEyeNormal = normalMatrix * vNormal;
// Vertex position in Eye Space
vec4 vVert4 = mvMatrix * vVertex;
vec3 vEyeVertex = normalize(vVert4.xyz / vVert4.w);
// Get reflected vector
vec4 vCoords = vec4(reflect(vEyeVertex, vEyeNormal), 1.0);
// Rotate by flipped camera
vCoords = mInverseCamera * vCoords;
vVaryingTexCoord.xyz = normalize(vCoords.xyz);
// Don't forget to transform the geometry!
gl_Position = mvpMatrix * vVertex;
}
多重紋理
片段著色器中的單個統一值是我們將要繫結到的紋理單元的索引,我們可以對實現進行查詢,來檢視支援的紋理單元數量:
Glint iUnits;
glGetIntegerv(GL_MAX_TEXTURE_UNITS,&iUnits);
我們可以通過呼叫以紋理單元識別符號為變數的glActiveTexture來改變當前紋理單元:
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D,textureID);
多重紋理座標
在呼叫以nTextureUnits為引數的Begin函式時,我們最多可以指定4組紋理座標:
void GLBatch::Begin(GLenum primitive,GLuint nVerts,GLuint nTextureUnits=0);
CopyTexCoordData2f會一次複製整個一組紋理座標:
void GLBatch::CopyTexCoordData2f(M3DVector2f *vTexCoords,GLuint uiTextureLayer);
而下面的方法則會每次提供紋理一個頂點的介面,速度較慢:
void GLBatch::MultiTexCoord2f(GLuint texture,GLclampf s,GLclampf t);
void GLBatch::MultiTexCoord2fv(GLuint texture,M3DVector2f vTexCoord);
點精靈(點塊紋理)
使用點精靈,我們可以通過繪製一個3D點將一個2D紋理影象顯示在螢幕的任意位置上。
店精靈允許我們通過傳送單個3D頂點,渲染一個完美對其的紋理2D多邊形,他所需要的頻寬只有為四邊形傳送4個頂點所需頻寬的四分之一,並且不需要客戶端的矩陣邏輯來保持3D四邊形與照相機的對其。
使用點
在客戶端,我們需要做的指示簡單地繫結一個2D紋理;
// SpaceFlight Shader
// Fragment Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130
out vec4 vFragColor;
in vec4 vStarColor;
uniform sampler2D starImage;
void main(void)
{
vFragColor = texture(starImage, gl_PointCoord) * vStarColor;
}
點大小
有兩種方式可以設定點大小。第一種方式是glPointSize函式:
void glPointSize(GLfloat size);
我們也可以在頂點著色器中用程式設定點大小。首先啟用點大小模式:
glEnable(GL_PROGRAM_POINT_SIZE);
然後,在我們的低昂點程式中,可以設定一個內建變數gl_PointSize,這個變數確定了點的最終光柵化大小。這通常用來根據點的距離來確定它的大小。
點引數
通過glPointParameter函式,我們可以對店精靈的幾個特性進行微調。將GL_POINT_SPRITE_COORD_ORIGIN引數設定為GL_LOWER_LEFT,可以將紋理座標系的原點放置在點的左下角,點精靈的預設方向為GL_UPPER_LEFT
glPointParameteri(GL_POINT_SPRITE_COORD_ORGIN,GL_LOWER_LEFT);
異形點
我們可以在片段著色器中使用discard關鍵字來丟棄位域我們想要的點形狀範圍之外的片段,從而創建出非正方形的點。
vec2 p=gl_PointCoord*2.0-vec2(1.0);
if(dot(p,p)>1.0)
discard;
點的旋轉
要讓點旋轉,我們只需在片段著色器中建立一個2D旋轉矩陣,並用它乘以gl_PointCoord使它圍繞z軸進行旋轉。旋轉的角度可以從頂點著色器或者集合著色器中作為一個插值變數傳遞到片段著色器。
紋理陣列
在紋理陣列中,我們可以將整個陣列的紋理圖縣繫結到一個紋理物件上,然後阻礙著色器中對它們進行檢索,這樣就大大增加了著色器可用的文禮書局的數量。
載入2D紋理陣列
紋理陣列添加了兩個新的紋理物件作為大多數文理管理函式的有效引數,他們是GL_TEXTURE_1D_ARRAY和GL_TEXTURE_2D_ARRAY。
GLuint moonTexture;
……
glGenTextures(1, &moonTexture);
glBindTexture(GL_TEXTURE_2D_ARRAY, moonTexture);
紋理引數、環繞模式和過濾器的情況也是如此:
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
對於2D紋理陣列來說,我們使用管理TexImageD函式來載入紋理資料:
void glTexImage3D(GLenum target,Glint level,Glint internalformat,GLsizei width,GLsizei height,GLsizei depth,Glint border,GLenum format,GLenum type,void *data);
然後,我們使用glTexSubImageXD函式族來更新紋理。例如要保留29個64X64的RGBA影象,程式碼應如下所示:
glTexImage3D(GL_TEXTURE_2D_ARRAY,0,GL_RGBA,64,64,29,0,GL_BGRA,GL_UNSIGNED_BYTE,NULL);
紋理陣列索引
在進行渲染前,下面的程式碼會在頂點著色器中設定恰當的統一值:
float fTime = timer.GetElapsedSeconds();
fTime = fmod(fTime, 28.0f);
glUniform1f(locTimeStamp, fTime);
moonBatch.Draw();
在頂點著色器中,我們將包含經過實踐的統一值放在vec3的p變數中在隨後的片段著色器中使用:
// MoonShader
// Vertex Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130
in vec4 vVertex;
in vec4 vTexCoords;
uniform mat4 mvpMatrix;
uniform float fTime;
smooth out vec3 vMoonCoords;
void main(void)
{
vMoonCoords.st = vTexCoords.st;
vMoonCoords.p = fTime;
gl_Position = mvpMatrix * vVertex;
}
訪問紋理陣列
// MoonShader
// Fragment Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130
out vec4 vFragColor;
uniform sampler2DArray moonImage;
smooth in vec3 vMoonCoords;
void main(void)
{
vFragColor = texture2DArray(moonImage, vMoonCoords);
}
紋理代理
下面方法可以獲得一個一維或二維紋理貼圖(或者也可以用特定紋理型別如GL_MAX_3D_TEXTURE_SIZE和GL_MAX_CUBE_MAP_TEXTURE_SIZE)最大寬度或者最大高度的下限:
Glint maxSize;
glGetIntegerv(GL_MAX_TEXTURE_SIZE,&maxSize);
要弄清是否支援某種特定的紋理大小和格式,我們可以使用一個紋理代理。紋理代理不佔用記憶體空間也無法應用到幾何圖形上,僅僅用來嘗試。
例如,為了查明是否能夠整整載入一個2048X4096的BGRA紋理,我們可以建立類似下面這樣的代理:
glTexImage2D(GL_PROXY_TEXTURE_2D,0,GL_RGBA,2048,4096,0,GL_BGRA,GL_UNSIGNED_BYTE,NULL);
然後檢視是否支援相應的高度4096:
void glGetTexLevelParameter(GL_PROXY_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);