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版。

你可能感兴趣的:(OpenGL)