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);

轉載請註明出處:http://blog.csdn.net/ylbs110/article/details/51926110
說明
由於超級寶典版本太老了,決定本週開始改學習編程指南第8版。

發佈了41 篇原創文章 · 獲贊 22 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章