1. 程式人生 > >OpenGL學習筆記-2015.4.1——紋理載入與控制

OpenGL學習筆記-2015.4.1——紋理載入與控制

在紋理貼圖技術問世之前,虛擬3維世界只能通過幾何模型去模擬現實,可想而知,現實世界的錯綜複雜,計算量之大並不能滿足實時顯示的需求。紋理對映帶來了革命性的變換,簡單的使得虛擬的3維世界得以無限接近於現實。紋理對映技術最早是由Catmull 在1947年率先提出的,Catmull首先找到了以(u,v)表示的雙變數實數空間(紋理空間)和以引數(s,t)表示的三維曲面之間的對應關係(對映關係)。     紋理由紋素組成,通常包括顏色值。但是在實際應用中,很多時候把紋理看作可以再著色器中查詢的表(一個儲存資料的地方),你可以用於任何目的,不僅僅侷限於紋理貼圖。     基本紋理型別: OpenGL支援多型別的維數和佈局可變的紋理物件,每個紋理物件表示組成完整紋理的一組影象,可以是1維,2維,3維陣列。紋理可以使用用於2維和2維陣列紋理的多重取樣的紋理型別來表示多重取樣顏色。多重取樣是一個用於抗鋸齒的術語,其中每個紋素被賦予多個獨立的顏色,然後在渲染過程中合併這些顏色來生成最終的顏色。         紋理通過紋理單元繫結到OpenGL中,紋理單元命名為GL_TEXTURE0到GL_TEXTUREi(i是OpenGL實現所支援的最大紋理數目減一)的繫結點。只要一個紋理繫結到環境中,則在著色器中可以使用取樣器來訪問,而取樣器變數用匹配紋理的維數來宣告,具體看下錶:
目標GL_TEXTURE* 取樣器型別 維度
1D sampler1D 1D
1D_ARRAY sampler1DArray 1D陣列
2D sampler2D 2D
2D_ARRAY sampler2DArray 2D陣列
2D_MULTISAMPLE sampler2DMS 2D多重取樣
2D_MULTISAMPLE_ARRAY sampler2DMSArray 2D多重取樣陣列
3D sampler3D 3D
CUBE samplerCube 立方體紋理對映
ARRAY samplerCubeArray 立方體對映陣列
RECTANGLE samplerRect 2D矩陣
BUFFER samplerBuffer 1D快取
ps:很多型別的紋理對映現在自己並沒用用到對於一些概念還是比較模糊,在此mark~~什麼時候瞭解了回來改改 一些特殊的紋理物件:1、矩形紋理物件(GL_TEXTURE_RECTANGLE),2維紋理的特例,表示一個簡單的矩陣,沒用mipmap(層次紋理)並且不能表示為紋理陣列?!一些紋理模式(texture wrapping)不支援矩陣紋理。2、緩衝紋理(GL_TEXTURE_BUFFER),表示紋素的1維陣列。同樣沒有mipmap,不能聚合陣列。紋理緩衝是任意著色器階段的資料訪問(頂點資料,顏色。。。)而不需要把資料拷貝到紋理影象中。     紋理對映四步曲:
 一、建立紋理物件     1、void glGenTextures( GLsizei n, GLuint* textures ):返回n個用於紋理物件的當前沒有使用的名稱。     2、void glBindTexture( GLenum target, GLuint texture ):改api完成三項工作:a、當第一次繫結texture並且texture不為0時,建立一個新的紋理物件並關聯該名稱,並激活。b、當繫結是先前已經建立的紋理物件,則直接啟用該物件。c、當texture為0時,OpenGL將刪除與啟用紋理單元(GL_TEXTURE0...GL_TEXTUREi)特定目標關聯的所有繫結。target引數可以為一下之一:GL_TEXTURE_1D, GL_TEXTURE_2D。。。。。見上表。     3、void glActiveTexture( GLenum texture ):選擇當前紋理函式改變的紋理單元,GL_TEXTURE0--GL_TEXTUREi.預設的應該是GL_TEXTURE0.     4、void glIIsTexture( GLuint texture ):判斷一個整形是否為紋理物件名稱。     5、void  glDeleteTextures( GLsizei n, const GLuint* textures ):刪除紋理物件,這將刪除所有與紋理關聯的底層儲存引用,並且在最後不被需要時釋放。只要紋理物件唄glDeleteTextures刪除,那麼他的名稱將再次變為沒有使用,可以被glGenTextures()返回。 二、設定載入資料。     1、OpenGL有兩個系列的API去對紋理物件設定儲存和資料:glTexStorage*()系列和glTexImage*()         glTexStorage*():建立的是固定儲存,使用這些函式分配之後,不能再重新定義儲存,這被認為是OpenGL的最好實現,OpenGL假設紋理物件的格式和大小在他生命週期內不會改變,因此可以停止跟蹤紋理物件的某些方面,ps:這只是儲存屬性不可變!紋理的內容可以使用glTexSubImage*()函式來修改。         glTexImage*():與glTexStorage*()相對,允許重新定義紋理物件(例如,縮放或者改變格式)         並且,兩個系列的都有單取樣和多重取樣的過載函式,以下是函式的詳細宣告:         void glTexStorage1D( GLenum target, GLsizei levels, GLenum internalFormat, GLsize width )         void glTexStorage2D( GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height )         void glTexStorage3D( GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth )         void glTexStorage2DMultisample( GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations )         void glTexStorage3DMultisample( GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLbollean fixedsamplelocations )         PS:target是GL_TEXTURE_1D, GL_TEXTURE_2D。。。。。也可以見上表,當然裡不同的函式有不同的target引數,glTexStorage1D的target必須是GL_TEXTURE_1D。glTexStorage2D()可以是GL_TEXTURE_1D_ARRAY或GL_TEXTURE_2D。glTexStorage3()可以為GL_TEXTURE_2D_ARRAY或GL_TEXTURE_3D。glTexStorage2DMultisample()則必須是GL_TEXTURE_2D_MULTISAMPLE。glTexStorage3DMultisample()為GL_TEXTURE_3D_MULTISAMPLE。                 levels表示mipmap的層數。interFormat表示記憶體格式,太多了,例如GL_RGBA8....就不一一列舉。width,height, depth分別為紋理的寬,高和深度。samples設定了紋理取樣數目。fixedsamplelocations 如果為GL_TRUE, OpenGL為紋理中每個紋理的相同樣本使用相同的子紋素位置,否則OpenGL為每個紋素給定的樣本選擇空間變化的位置(這話讀起來這麼繞。。)。         void glTexImage1D( GLenum target , GLint level, GLint interalFormat, GLsizei width, GLsizei border, GLenum format, GLenum type, const void* data )         void glTexImage2D( GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLborder, GLenum format, GLenum type, const void* data )             void glTexImage3D( GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height,  GLsizei depth, GLborder, GLenum format, GLenum type, const void* data )             void glTexImage2DMultisample( GLenum target, GLenum samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations )         void glTexImage3DMultisample( GLenum target, GLenum samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations )         PS:border表示邊框寬度必須是0,format 與type定義了 data中的資料格式和儲存格式。                 特別注意:對於data引數有兩種解釋,a:如果將一個緩衝繫結到了GL_PIXEL_UNPACK_BUFFER繫結點,那麼他表示偏移地址,b.如果沒有快取繫結到GL_PIXEL_UNPACK_BUFFER那麼表示紋理資料內容,如果為NULl空指標則將是未定義的。         API : void glTexSubImage*()系列函式顯示的設定紋理資料:             void glTexSubImage1D( GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void* data )             void glTexSubImage2D( GLenum target, GLint level, GLint xoffset, GLsizei width,  GLsizei height, GLenum format, GLenum type, const void* data )             void glTexSubImage1D( GLenum target, GLint level, GLint xoffset, GLsizei width, ,  GLsizei height, ,  GLsizei depth,GLenum format, GLenum type, const void* data )             引數的意義已經夠明瞭了。函式最終的結果是替換紋理資料的一個區域。        紋理資料的資料來源可以是程式生成,可以是幀快取中拷貝,可以衝影象檔案中載入。         glCopyTexImage*()提供了在幀快取中拷貝資料的途徑。而從影象檔案中載入則顯得比較複雜,因為影象檔案格式多種多樣,需要對應每種格式的檔案編寫對應的解析程式碼。幸福的是這些工作大部分前人都幫我們做了,如freeImage一個開源的影象載入庫。而本書自身編寫了vglLoadImage()的封裝函式載入dds格式的紋理。  三、紋理的使用         1、取樣器物件         在非固定流水線中,OpengGL通過關聯一個取樣器變數和一個紋理單元,並且使用GLSL的內建函式從紋理影象中提取紋素,這樣在著色中可以任意是用紋理資料。            void glGenSamplers( GLsizei count, GLuint* samplers ):返回count個未使用的sampler名稱。0是保留至,永遠不會被返回。            void glBindSampler( GLuint uint, GLuint sampler ): unit 是基於0的無符號整數,允許OpenGL實現支援任意數目的取樣器單元而不用OpenGL保留標誌。 可以理解為任意的無符號整數,0,1,2.......並不表示紋理單元。那問題來了,該如何關聯起來讓著色器通過sampler查詢紋理資料呢?1、需要在著色器中定義內建取樣器型別:samplerBuffer,sample2D.... 修飾的整形變數。texture unit 的值就對應於這個變數的值,這個值需要在應用程式c++程式碼中通過glUniformi()賦值,著色器中根據這個值具體型別採用不同型別的內建函式查詢紋理資料,並在取樣過程中應用比較模式,縮放過濾模式之類的,而對於同一紋理資料來源,使用不同設定的sampler可以達到不同的取樣結果,這也許是非預設sampler的好處之一。             GLboolean gllsSampler( GLenum id ):判斷id是否是一個取樣器名稱。返回GL_TRUE or GL_FALSE.         2、設定取樣器引數          每個取樣器物件擁有控制如何從紋理中採集紋素的一些引數,使用glSamplerParameter*()函式進行設定。          void glSamplerParameter{fi}( GLuint sampler, GLenum paname, Type param )          void glSamplerParameter{fi}v( GLuint sampler, GLenum paname, const Type* param  ):          void glSamplerParameterl{I,ui}v( GLuint sampler, GLenum pname, const Type* param )           給sampler名稱的取樣器物件設定,pname引數,值為 param。三個版本對應於不同資料型別的param。對於pname主要包括,紋理縮放過濾方式,對於當紋理座標不在0.0--1.0範圍內是的處理方式。其中對應關係如下:
pname param
GL_TEXTURE_MAG_FILTER GL_NEAREST、GL_LINEAR
GL_TEXTURE_MIN_FILTER GL_NEAREST、GL_LINEAR、GL_NEAREST_MIPMAP_NEAREST、GL_NEAREST_MIPMAP_LINEAR、GL_LINEAR_MIPMAP_NEAREST、GL_LINEAR_MIPMAP_LINEAR
GL_TEXTURE_WRAP_S、GL_TEXTURE_WRAP_T GL_REPEAT、GL_CLAMP_TO_EDGE、GL_MIRRORED_REPEAT
具體意義課查閱openGL文件:https://www.opengl.org/sdk/docs/man4/  ctrl+f函式名稱即可。有詳細的解釋。         當沒有采樣器物件繫結到對應的取樣器單元時,每個用來從紋理中讀取資料的紋理物件都有預設的取樣器物件。為了修改這些引數,可以使用glTexParameter*()函式。         void glTexParameter{fi}( GLenum target, GLenum pname, Type param )         void glTexParameter{fi}v( GLenum target, GLenum pname, Type, param )         void glTexParameterl{fi}v( GLenum target, GLenum pname, Type param )         target引數表示繫結到當前啟用紋理單元的紋理物件,pname ,與 param大致與glSamplerParameter*()相同,不過glTexParameter*()有更多不同的引數,控制不同方面的內容,如當還有對於外部的資料來源,並不能保證其紋理資料的儲存順序均為RGBA分分量順序。所以OpenGL提供了在讀取時紋理資料的儲存順序設定。man手冊萬能:https://www.opengl.org/sdk/docs/man4/          最後你是刪除sampler物件的介面:glDeleteSamplers( GLsizei count, const GLuint* samplers )         GLSL在著色器階段可以使用內建函式來讀取紋理資訊。texture()的幾個過載版本。         Gvec4 texture( gsampler1D tex, float P[, float bias] )         Gvec4 texture( gsampler2D tex, float P[, float bias] );         Gvec4 texture( gsampler3D tex, float P[, float bias] );         Gvec4 texture( gsamplerCube tex, float P[, float bias] );         Gvec4 texture( gsampler1DArray tex, float P[, float bias] );         Gvec4 texture( gsampler2DArray tex, float P[, float bias] );         Gvec4 texture( gsampler2DRect tex, float P[, float bias] );         Gvec4 texture( gsamplerCubeArray tex, float P[, float bias] );         tex表示紋理取樣器型別,前面加g相當於一個佔位符,如gsampler2D可以表示sampler2D, isampler2D,或則usampler2D.Gvec4也是可以表示vec4 ivec4 uvec4.。p表示紋理的紋理座標,bias表示如果啟動了mipmapping,用來偏移確定mipmap的細節層次計算。       4、幾個簡單的例子,程式碼中對於使用到的一些介面引數會有詳細的解釋。             先來看一下LoadTexture的內容。
    
GLuint vglLoadTexture(const char* filename,
                      GLuint texture,
                      vglImageData* image)
{
    vglImageData local_image;
    int level;

    if (image == 0)
        image = &local_image;

    vglLoadImage(filename, image);

    if (texture == 0)
    {
        glGenTextures(1, &texture);
    }

    glBindTexture(image->target, texture);

    GLubyte * ptr = (GLubyte *)image->mip[0].data;

    switch (image->target)
    {
        case GL_TEXTURE_1D:
            glTexStorage1D(image->target,
                           image->mipLevels,
                           image->internalFormat,
                           image->mip[0].width); //根據從dds中讀取到的紋理資料,分配紋理記憶體,設定紋理格式。
            for (level = 0; level < image->mipLevels; ++level)
            {
                glTexSubImage1D(GL_TEXTURE_1D,
                                level,
                                0,
                                image->mip[level].width,
                                image->format, image->type,
                                image->mip[level].data);//設定資料,minmap
            }
            break;
        case GL_TEXTURE_1D_ARRAY:
            glTexStorage2D(image->target,
                           image->mipLevels,
                           image->internalFormat,
                           image->mip[0].width,
                           image->slices);
            for (level = 0; level < image->mipLevels; ++level)
            {
                glTexSubImage2D(GL_TEXTURE_1D,
                                level,
                                0, 0,
                                image->mip[level].width, image->slices,
                                image->format, image->type,
                                image->mip[level].data);
            }
            break;
        case GL_TEXTURE_2D:
            glTexStorage2D(image->target,
                           image->mipLevels,
                           image->internalFormat,
                           image->mip[0].width,
                           image->mip[0].height);
            for (level = 0; level < image->mipLevels; ++level)
            {
                glTexSubImage2D(GL_TEXTURE_2D,
                                level,
                                0, 0,
                                image->mip[level].width, image->mip[level].height,
                                image->format, image->type,
                                image->mip[level].data);
            }
            break;
        case GL_TEXTURE_CUBE_MAP:
            for (level = 0; level < image->mipLevels; ++level)
            {
                ptr = (GLubyte *)image->mip[level].data;
                for (int face = 0; face < 6; face++)
                {
                    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
                                 level,
                                 image->internalFormat,
                                 image->mip[level].width, image->mip[level].height,
                                 0,
                                 image->format, image->type,
                                 ptr + image->sliceStride * face);
                }
            }
            break;
        case GL_TEXTURE_2D_ARRAY:
            glTexStorage3D(image->target,
                           image->mipLevels,
                           image->internalFormat,
                           image->mip[0].width,
                           image->mip[0].height,
                           image->slices);
            for (level = 0; level < image->mipLevels; ++level)
            {
                glTexSubImage3D(GL_TEXTURE_2D_ARRAY,
                                level,
                                0, 0, 0,
                                image->mip[level].width, image->mip[level].height, image->slices,
                                image->format, image->type,
                                image->mip[level].data);
            }
            break;
        case GL_TEXTURE_CUBE_MAP_ARRAY:
            glTexStorage3D(image->target,
                           image->mipLevels,
                           image->internalFormat,
                           image->mip[0].width,
                           image->mip[0].height,
                           image->slices);
            break;
        case GL_TEXTURE_3D:
            glTexStorage3D(image->target,
                           image->mipLevels,
                           image->internalFormat,
                           image->mip[0].width,
                           image->mip[0].height,
                           image->mip[0].depth);
            for (level = 0; level < image->mipLevels; ++level)
            {
                glTexSubImage3D(GL_TEXTURE_3D,
                                level,
                                0, 0, 0,
                                image->mip[level].width, image->mip[level].height, image->mip[level].depth,
                                image->format, image->type,
                                image->mip[level].data);
            }
            break;
        default:
            break;
    }

    glTexParameteriv(image->target, GL_TEXTURE_SWIZZLE_RGBA, reinterpret_cast<const GLint *>(image->swizzle));

    if (image == &local_image)
    {
        vglUnloadImage(image);
    }

    return texture;
}
a、簡單紋理載入
#include "vermilion.h"
#include "loadTexture.h"
#include "vbm.h"
#include "vmath.h"
#include "vutils.h"

namespace loadTexture
{
	float aspect = 800.0f/600.0f;
	GLuint base_prog;
	GLuint vao;

	GLuint quad_vbo;

	GLuint tex;
};

using namespace loadTexture;

void loadTextureInit()
{
	base_prog = glCreateProgram();

	static const char quad_shader_vs[] =
		"#version 330 core\n"
		"\n"
		"layout (location = 0) in vec2 in_position;\n"
		"layout (location = 1) in vec2 in_tex_coord;\n"
		"\n"
		"out vec2 tex_coord;\n"
		"\n"
		"void main(void)\n"
		"{\n"
		"    gl_Position = vec4(in_position, 0.5, 1.0);\n"
		"    tex_coord = in_tex_coord;\n"//把紋理座標傳遞給下一階段。 
		"}\n"
		;

	static const char quad_shader_fs[] =
		"#version 330 core\n"
		"\n"
		"in vec2 tex_coord;\n"
		"\n"
		"layout (location = 0) out vec4 color;\n"
		"\n"
		"uniform sampler2D tex;\n"
		"\n"
		"void main(void)\n"
		"{\n"
		"    color = texture(tex, tex_coord);\n"//texture 根據紋理座標採集紋素。
		"}\n"
		;
		
	vglAttachShaderSource( base_prog, GL_VERTEX_SHADER, quad_shader_vs );
	vglAttachShaderSource( base_prog, GL_FRAGMENT_SHADER, quad_shader_fs );

	glGenBuffers( 1, &quad_vbo );
	glBindBuffer( GL_ARRAY_BUFFER, quad_vbo );

	static const GLfloat quad_data[] =
	{
		0.5f, -0.5f,
		-1.0f, -1.0f,
		-1.0f, 1.0f,
		1.0f, 1.0f,

		0.0f, 0.0f,
		1.0f, 0.0f,
		1.0f, 1.0f,
		0.0f, 1.0f
	};

	glBufferData( GL_ARRAY_BUFFER, sizeof(quad_data), quad_data, GL_STATIC_DRAW );

	glGenVertexArrays( 1, &vao );
	glBindVertexArray( vao );

	glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) ); //設定頂點著色器的輸出,頂點座標
	glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET( 8 * sizeof(float) ) );//頂點資料,紋理座標

	glEnableVertexAttribArray(0);
	glEnableVertexAttribArray(1);

	glLinkProgram( base_prog );

	//char buf[1024];
	//glGetProgramInfoLog( base_prog, 1024, NULL, buf );

	vglImageData image;

	tex = vglLoadTexture("../8edlib/media/test.dds", 0, &image ); //內部實現了紋理載入操作glGenTexture(),glBindTexture(),glStorage*(),glTexSubImage*()

	glTexParameteri( image.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); //紋理縮小過濾模式
	vglUnloadImage(&image);//紋理資訊已經儲存在紋理物件當中,釋放記憶體
}

void loadTextureDisplay()
{
	float t = float(GetTickCount() & 0x3FFF) / float(0x3FFF);
	static const vmath::vec3 X(1.0f, 0.0f, 0.0f);
	static const vmath::vec3 Y(0.0f, 1.0f, 0.0f);
	static const vmath::vec3 Z(0.0f, 0.0f, 1.0f);

	glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
	glClearDepth( 1.0f );
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	glDisable( GL_CULL_FACE );//禁用剔除操作
	glUseProgram( base_prog );

	glBindVertexArray( vao );
	glDrawArrays( GL_TRIANGLE_FAN, 0, 4 );//扇形三角形繪製

}

void loadTextureUpdate(float delta)
{

}
執行結果:
b、紋理座標越界控制
#include "commonHead.h"
#include "vbm.h"
#include "vutils.h"

namespace textureWrap
{
	float aspect = 800.0f/600.0f;
    GLuint base_prog;
    GLuint vao;

    GLuint quad_vbo;

    GLuint tex;
};

using namespace textureWrap;

void textureWrapInit()
{
	base_prog = glCreateProgram();

	static const char quad_shader_vs[] =
		"#version 330 core\n"
		"\n"
		"layout (location = 0) in vec2 in_position;\n"
		"layout (location = 1) in vec2 in_tex_coord;\n"
		"\n"
		"out vec2 tex_coord;\n"
		"\n"
		"void main(void)\n"
		"{\n"
		"    gl_Position = vec4(in_position, 0.5, 1.0);\n"
		"    tex_coord = in_tex_coord;\n"
		"}\n"
		;

	static const char quad_shader_fs[] =
		"#version 330 core\n"
		"\n"
		"in vec2 tex_coord;\n"
		"\n"
		"layout (location = 0) out vec4 color;\n"
		"\n"
		"uniform sampler2D tex;\n"
		"\n"
		"void main(void)\n"
		"{\n"
		"    color = texture(tex, tex_coord);\n"
		"}\n"
		;

	vglAttachShaderSource( base_prog, GL_VERTEX_SHADER, quad_shader_vs );
	vglAttachShaderSource( base_prog, GL_FRAGMENT_SHADER, quad_shader_fs );

	glGenBuffers( 1, &quad_vbo );
	glBindBuffer( GL_ARRAY_BUFFER, quad_vbo );

	static const GLfloat quad_data[] = 
	{
		-0.75f, -0.75f,
		0.75f, -0.75f,
		0.75f, 0.75f,
		-0.75f, 0.75f,

		0.0f, 0.0f,
		4.0f, 0.0f, 
		4.0f, 4.0f,
		0.0f, 4.0f
	};

	glBufferData( GL_ARRAY_BUFFER, sizeof(quad_data), quad_data, GL_STATIC_DRAW );

	glGenVertexArrays( 1, &vao );
	glBindVertexArray( vao );

	glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );
	glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET( 8 * sizeof(float) ) );

	glEnableVertexAttribArray(0);
	glEnableVertexAttribArray(1);

	glLinkProgram( base_prog );

	glGenTextures( 1, &tex );
	glBindTexture( GL_TEXTURE_2D, tex );
	glTexStorage2D( GL_TEXTURE_2D, 4, GL_RGBA8, 8, 8 ); //分配記憶體,不可改變

	static const unsigned char texture_data[] =
	{
		0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
		0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
		0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
		0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
		0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
		0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
		0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
		0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF
	};

	glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 8, 8, GL_RED, GL_UNSIGNED_BYTE, texture_data );//資料初始化

	static const GLint swizzles[] = { GL_RED, GL_RED, GL_RED, GL_ONE };//只使用紋理的紅色通道
	glTexParameteriv( GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzles );

	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );//紋理過濾函式,縮小
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );//紋理過濾函式,放大

	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );//超出-1.0~1.0f的紋理座標處理方式設定s方向
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );//t方向

	glGenerateMipmap( GL_TEXTURE_2D );//產生層級紋理。

}

void textureWrapUpdate(float)
{

}

void textureWrapDisplay()
{
	unsigned int ticks = GetTickCount();
	float t = float(ticks & 0x3FFF) / float(0x3FFF);
	static const vmath::vec3 X(1.0f, 0.0f, 0.0f);
	static const vmath::vec3 Y(0.0f, 1.0f, 0.0f);
	static const vmath::vec3 Z(0.0f, 0.0f, 1.0f);

	glClearColor( 0.3f, 0.3f, 0.3f, 1.0f );
	glClearDepth( 1.0f );
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	glDisable( GL_CULL_FACE );
	glUseProgram( base_prog );

	static const GLenum wrap_modes[] = { GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER, GL_REPEAT, GL_MIRRORED_REPEAT };
	//GL_CLAMP_TO_EDGE : 當紋理座標超出0.0—1.0時,使用紋理邊緣的紋素返回。
	//GL_CLAMP_TO_BORDER : 讀取紋理範圍之外的卓彪將生成用來形成最總的紋理常數邊界顏色。可以通過glTexParameterfv()以GL_TEXTURE_BORDER_COLOR為引數設定邊界顏色值。
	//GL_REPEAT : 包裹紋理,無限重複,只有紋理座標的小數部分用來生成紋理座標,整數部分被拋棄。
	//GL_MIRRORED_REPEAT : 是一個特殊的模式,紋理可以以映象模式重複。整數部分是偶數的紋理座標只考慮小數部分,整數部分是奇數的紋理座標是1.0減去其小數部分來形成最終座標。
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_modes[3] );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_modes[3] );

	glBindVertexArray( vao );
	glDrawArrays( GL_TRIANGLE_FAN, 0, 4 );

}
執行結果: GL_CLAMP_TO_EDGE模式 GL_CLAMP_TO_BORDER模式 GL_REPEAT模式 GL_MIRROED_REPEAT模式。 c、使用多重紋理。
#include "multiTexture\multiTexture.h"
#include "vbm.h"
#include "vutils.h"
#include  "vermilion.h"

namespace multiTexture
{
	float aspect = 800.0f/600.0f;
	GLuint base_prog;
	GLuint vao;

	GLuint quad_vbo;

	GLuint tex1;
	GLuint tex2;

	GLint  time_loc;
};

using namespace multiTexture;

void multiTextureInit()
{
	base_prog = glCreateProgram();

	static const char quad_shader_vs[] =
		"#version 330 core\n"
		"\n"
		"layout (location = 0) in vec2 in_position;\n"
		"layout (location = 1) in vec2 in_tex_coord;\n"
		"\n"
		"out vec2 tex_coord0;\n"
		"out vec2 tex_coord1;\n"
		"\n"
		"uniform float time;\n"
		"\n"
		"void main(void)\n"
		"{\n"
		"    mat2 m = mat2( vec2(cos(time), sin(time)),\n" //書中原始碼這裡定義的是const mat2是會產生連線時錯誤,因為m是根據時間而改變的,不可以為const
		"                         vec2(-sin(time), cos(time)) );\n"
		"    gl_Position = vec4(in_position, 0.5, 1.0);\n"
		"    tex_coord0 = in_tex_coord * m;\n"
		"    tex_coord1 = in_tex_coord * transpose(m);\n"//構造的是一個旋轉矩陣,
		"}\n"
		;

	static const char quad_shader_fs[] =
		"#version 330 core\n"
		"\n"
		"in vec2 tex_coord0;\n"
		"in vec2 tex_coord1;\n"
		"\n"
		"layout (location = 0) out vec4 color;\n"
		"\n"
		"uniform sampler2D tex1;\n" //sampler1
		"uniform sampler2D tex2;\n" //sampler2
		"\n"
		"void main(void)\n"
		"{\n"
		"    color = texture(tex1, tex_coord0) + texture(tex2, tex_coord1);\n" //對兩個紋理取樣進行混合,add
		"}\n"
		;
	vglAttachShaderSource( base_prog, GL_VERTEX_SHADER, quad_shader_vs );
	vglAttachShaderSource(base_prog, GL_FRAGMENT_SHADER, quad_shader_fs);

	glGenBuffers( 1, &quad_vbo );
	glBindBuffer( GL_ARRAY_BUFFER, quad_vbo );

	static const GLfloat quad_data[] =
	{
		1.0f, -1.0f,
		-1.0f, -1.0f,
		-1.0f, 1.0f,
		1.0f, 1.0f,

		0.0f, 0.0f,
		1.0f, 0.0f,
		1.0f, 1.0f,
		0.0f, 1.0f
	};

	glBufferData( GL_ARRAY_BUFFER, sizeof(quad_data), quad_data, GL_STATIC_DRAW );

	glGenVertexArrays( 1, &vao );
	glBindVertexArray( vao );

	glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );
	glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET( sizeof(float) * 8 ) );

	glEnableVertexAttribArray(0);
	glEnableVertexAttribArray(1);
	
	glLinkProgram( base_prog );
	char buf[1024];
	glGetProgramInfoLog(base_prog, 1024, NULL, buf);
	glUseProgram(base_prog);
	time_loc = glGetUniformLocation( base_prog, "time" );
	glUniform1i(glGetUniformLocation(base_prog, "tex1"), 0);
	glUniform1i( glGetUniformLocation(base_prog, "tex2"), 1);
	
	vglImageData image;

	tex1 = vglLoadTexture("../8edlib/media/test.dds", 0, &image );
	
	glBindTexture( image.target, tex1 );
	glTexParameteri( image.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
	
	vglUnloadImage( &image );

	tex2 = vglLoadTexture("../8edlib/media/test3.dds", 0, &image);
	vglUnloadImage( &image );


}

void multiTextureDisplay()
{
	float t = float(GetTickCount() & 0x3FFF) / float(0x3FFF);
	static const vmath::vec3 X(1.0f, 0.0f, 0.0f);
	static const vmath::vec3 Y(0.0f, 1.0f, 0.0f);
	static const vmath::vec3 Z(0.0f, 0.0f, 1.0f);

	glClearColor( 0.0f, 1.0f, 0.0f, 1.0f );
	glClearDepth( 1.0f );
	
	glDisable( GL_CULL_FACE );
	glUseProgram( base_prog );
	glUniform1f( time_loc, t );
	
	glBindVertexArray( vao );
	glActiveTexture( GL_TEXTURE0 ); //啟用紋理單元0
	glBindTexture( GL_TEXTURE_2D, tex1 );
	glActiveTexture( GL_TEXTURE1 ); //啟用紋理單元1
	glBindTexture(GL_TEXTURE_2D, tex2 );

	glDrawArrays( GL_TRIANGLE_FAN, 0, 4 );
	GLenum errcode = glGetError();
	const char* buf1 = (const char*)glewGetErrorString(errcode);
}

void multiTextureUpdate(float delta)
{

}
執行結果 The End~~ 希望堅持和努力會迎來好的結果。