OpenGL学习脚印: 纹理映射基础篇
写在前面
纹理映射是个复杂话题。本节旨在初步认识纹理映射概念,基本原理。同时进行了3个简单纹理映射的实验,熟悉在OpenGL中使用。参考资料列在文章末尾。
1.纹理映射基本概念
纹理映射使用一个图案或者纹理来确定渲染流水线中片元处理阶段片元的颜色。
简单来讲,纹理就是矩形的数据数组,例如颜色数据、亮度数据、颜色和alpha数据。纹理数组中的单个值常常称为纹理单元,也叫纹素(texel),这里让它区别于像素,主要是为了强调它的应用方式。
OpenGL支持1D、2D、3D以及立方体纹理,现在主要考虑2D纹理。
纹理映射就是要实现,如何把纹素映射到几何对象的每个点。一个2D的纹理有一个宽度和高度,通过宽度和高度相乘即可得到有多少个纹素。
那么如何来指定顶点的纹素呢?通过坐标来指定,但是这个坐标不应该是具体纹理的中坐标,而应该是抽象的纹理坐标空间中的坐标;否则通过指定具体纹理的坐标,当更换纹理,例如改变纹理的宽度和高度时,这些坐标值可能变得无意义,而不得不更新所有顶点的坐标值,因此需要使用抽象的纹理坐标空间的坐标。纹理坐标一般都规范化到[0,1]范围内。例如一个纹理宽度为320,高度为200,而纹理坐标(0.5,0.1)则表示纹素的位置在: (320*0.5,200*0.1)=(160,20)。通常使用UV坐标系来表示纹理坐标系:
这里注意,OpenGL中V轴从下往上是正方向,U轴从左往右是正方向。在具体使用时,这与应用中纹理Y方向有关。如果纹理从上到下,则需要将纹理的Y方向翻转来满足这个图形所示的纹理坐标。
与纹理映射有关的一个特性是,当模型进行变换时,纹理坐标仍然会跟着模型的顶点,他们并不进行变换(当然也有其他方法可以改变纹理坐标),就好像粘着顶点一样。例如下图所示的三角形,如果在其中应用一个小的纹理:
当对三角形进行变换时,纹理坐标保持不变,这样当模型进行旋转、拉伸和放缩时,纹理也会跟着变化,如下图所示:
与纹理有关的另一个特性是纹理采样。当把纹理坐标映射到纹素数组时,正好得到对应纹素的中心位置的情况很少出现。解决这一问题的一种方法是,从纹素数组中取这样一个纹素,该纹素的位置,最佳逼近通过光栅化模块计算输出的纹理坐标。这样一方法成为点采样(Point sampling),也叫做nearest filtering。例如坐标(152.34,745.14)的纹素,就使用(152,745)来代替。用点采样容易产生走样误差。
另外一种方法是线性滤波方法(linear filtering)。例如,如果计算出一个纹理坐标位于(152.34,745.14),那么这个值对应的最近的4个纹理坐标为: ( (152,745), (153,745), (152,744) , (153,744) )。那么我们可以利用这4个纹理坐标的对应的颜色值进行线性插值,例如计算这一组纹素的加权平均值,并把该值作为纹理坐标映射到纹素数组时的纹素值。这种方法计算量要比点菜用大,效果一般比点采样好。
OpenGL支持多种滤波类型,可以通过设置来进行选择。
在进行纹理映射时,还需要考虑纹素与屏幕像素之间的对应关系。单个的纹素通常并不与屏幕像素对应,当纹素比单个像素大时,屏幕上多个像素对应于单个像素,称之为放大(manification);当纹素比单个像素小,屏幕上单个像素对应多个纹素,则称之为缩小(minification)。关系如下图所示(来自[2]):
2.纹理映射OpenGL实现
OpenGL中纹理映射的步骤如下:
- 创建纹理对象,并为他指定一个纹理
- 确定纹理如何应用到每个像素上
- 启用纹理贴图功能
- 绘制场景,提供纹理坐标和几何图形坐标
这里参考的是红宝书的步骤,实际上采用着色器方式的话流程稍微有所不同,主要是纹理坐标和几何图形坐标,可能在绘制场景之前在着色器中已经提供了。
实现纹理映射主要关系到4个概念:纹理对象(the texture object), 纹理单元(the texture unit), 采样器对象(the sampler object )采样器变量(sampler uniform in the shader).他们的关系如下图所示:
纹理对象包含了纹理数据,它可以是1D、2D、3D的,底层数据可以是RGB、RGBA格式的。
纹理对象并不直接绑定到着色器,而是绑定到一个纹理单元,纹理单元的索引将会传递给着色器。要绑定到一个纹理单元,先要将其激活,可以使用glActiveTexture函数,例如glActiveTexture(GL_TEXTURE0)将激活单元0。可以使用多个纹理单元,每个纹理单元可以绑定到相同或者不同的纹理对象。有一点值得注意,只要纹理对象的类型不同,一个纹理单元可以绑定多个纹理对象。例如你可以分别将两个纹理对象绑定到同一个纹理单元的1D和
2D不同的目标上。
你可以通过采样器变量来使用多个纹理,这个uniform变量有'sampler1D', 'sampler2D', 'sampler3D', 'samplerCube'等不同形式。在片元着色器中,采样函数需要通过采样器变量来访问多个纹理单元。
采样器对象与纹理对象不相同。纹理对象中包含了纹理数据,以及配置采样操作的参数,这些参数是采样状态的一部分。然而,你也可以创建一个采样对象,用采样状态参数配置它,并把它绑定到纹理单元中。这样,采样器对象会覆盖纹理对象中定义的采样状态。目前我们并不使用这一对象。
3.纹理映射实验
这里通过3个实验来认识OpenGL中纹理映射的实现。实验中使用的着色器是自己封装的类Shader,可以从《OpenGL学习脚印: 顶点数据传送和着色器处理1》一文中着色器的生成部分获取。当然这里的重点并不是理解如何构造着色器,使用其他替代库也可以。另外程序依赖于freeglut和glew库,加载图片时使用的是SOIL库。
3.1 在程序中生成纹理
纹理不一定都得从图片加载,也可以是程序中生成的纹理。参看[3]实现的程序中生成纹理,并映射到一个正方形的例子。
程序运行效果如下:
参考代码如下:
square.cpp
- //用程序生成纹理 映射正方形
- #include <string>
- #include <vector>
- #include <GL/glew.h>
- #include <GL/freeglut.h>
- #include <math.h>
- #include "shader.h"
- using namespace std;
-
- //依赖库
- #pragma comment(lib, "opengl32.lib")
- #pragma comment(lib, "glew32.lib")
- #pragma comment(lib, "freeglut.lib")
-
-
-
- void userInit();
- void reshape(int w,int h);
- void display( void );
- void keyboardAction( unsigned char key, int x, int y );
-
-
- GLuint vboId;//vertex buffer object句柄
- GLuint vaoId;//vertext array object句柄
- GLuint programId;//shader program 句柄
- GLuint textureId;//texture 句柄
- GLuint samplerId;//sampler变量句柄
- const int verticesNum = 6;
- //2个三角形的顶点及纹理坐标
- const float verticesData[] = {
- -0.5,-0.5,0.0,
- 0.5,-0.5,0.0,
- 0.5,0.5,0.0,
-
- 0.5,0.5,0.0,
- -0.5,0.5,0.0,
- -0.5,-0.5,0.0,
-
- 0.0,0.0,0.0,
- 1.0,0.0,0.0,
- 1.0,1.0,0.0,
-
- 1.0,1.0,0.0,
- 0.0,1.0,0.0,
- 0.0,0.0,0.0
- };
- int main( int argc, char **argv )
- {
- glutInit(&argc, argv);
- glutInitDisplayMode( GLUT_RGBA|GLUT_DOUBLE);
- glutInitWindowPosition(100,100);
- glutInitWindowSize( 512, 512 );
- glutCreateWindow( "Simple Texture Demo" );
-
- glewInit();
- userInit();
- glutReshapeFunc(reshape);
- glutDisplayFunc( display );
- glutKeyboardFunc( keyboardAction );
- glutMainLoop();
- return 0;
- }
- void InitializeProgram()
- {
- //从文件创建着色器
- std::vector<GLuint> idVector;
- idVector.push_back(Shader::createShader(GL_VERTEX_SHADER,"vertex.glsl"));
- idVector.push_back(Shader::createShader(GL_FRAGMENT_SHADER,"fragment.glsl"));
- programId = Shader::createProgram(idVector);
- //获取offset uniform变量句柄
- samplerId = glGetUniformLocation(programId,"gSampler");
- }
- //初始化VBO
- void InitializeVBO()
- {
- //创建vertex buffer object对象
- glGenBuffers(1,&vboId);
- glBindBuffer(GL_ARRAY_BUFFER,vboId);
- glBufferData(GL_ARRAY_BUFFER,sizeof(verticesData),verticesData,GL_STATIC_DRAW);
- glBindBuffer(GL_ARRAY_BUFFER,0);
- }
- //初始化VAO
- void InitializeVAO()
- {
- //创建vertex array object对象
- glGenVertexArrays(1,&vaoId);
- glBindVertexArray(vaoId);
-
- glBindBuffer(GL_ARRAY_BUFFER,vboId);
- //启用顶点着色器顶点坐标属性索引
- glEnableVertexAttribArray(0);
- glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,0);
- //启用顶点着色器纹理坐标属性索引
- glEnableVertexAttribArray(1);
- size_t textCoordOffset = 3*sizeof(float) * verticesNum;
- glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)textCoordOffset);
-
- glBindBuffer(GL_ARRAY_BUFFER,0);
- glBindVertexArray(0);
- }
- void InitializeTexture()
- {
- //创建纹理对象
- glGenTextures(1,&textureId);
- glBindTexture(GL_TEXTURE_2D,textureId);
-
- //设置采样参数
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
-
- //定义纹理
- float pixels[] = {
- 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
- 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f
- };
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_FLOAT, pixels);
- }
- //自定义初始化函数
- void userInit()
- {
- glClearColor( 0.0f, 0.2f, 0.1f, 0.0f );
- glEnable(GL_TEXTURE_2D);
- InitializeProgram();
- InitializeVBO();
- InitializeVAO();
- InitializeTexture();
- }
- //绘制回调函数
- void display( void )
- {
- glClear(GL_COLOR_BUFFER_BIT);
- glUseProgram(programId);
- glBindVertexArray(vaoId);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE0,textureId);
- glUniform1i(samplerId, 0);
- //绘制三角形
- glDrawArrays(GL_TRIANGLES, 0, verticesNum);
- glUseProgram(0);
- glBindVertexArray(0);
- glutSwapBuffers();
- }
- //调整窗口大小回调函数
- void reshape(int w,int h)
- {
- glViewport(0,0,(GLsizei)w,(GLsizei)h);
- }
- //键盘按键回调函数
- void keyboardAction( unsigned char key, int x, int y )
- {
- switch( key )
- {
- case 033: // Escape key
- exit( EXIT_SUCCESS );
- break;
- }
- }
vertex.glsl
- #version 330
-
- layout(location = 0) in vec3 position;
- layout(location = 1) in vec3 textCoord;
-
- out vec3 textCoord0;
-
- void main()
- {
- gl_Position = vec4(position,1.0);
- textCoord0 = textCoord;
- }
fragment.glsl
- #version 330
-
- in vec3 textCoord0;
- uniform sampler2D gSampler;
-
- out vec4 fragColor;
-
- void main()
- {
- fragColor = texture2D(gSampler,textCoord0.st);
- }
3.2 从图片文件读取纹理
这个例子参考自[1] 。从图片中读取纹理,映射到四面体上。这里使用索引绘图方式实现,加载图形的库使用SOIL。
程序运行效果如下:
着色器代码同上,其余参考实现代码如下:
bricks.cpp
- //利用SOIL加载图片纹理 实现纹理映射
- #include <string>
- #include <vector>
- #include <GL/glew.h>
- #include <GL/freeglut.h>
- #include <math.h>
- #include "shader.h"
- #include "SOIL.h"
- #define ARRAY_COUNT( array ) (sizeof( array ) / (sizeof( array[0] ) * (sizeof( array ) != sizeof(void*) || sizeof( array[0] ) <= sizeof(void*))))
- using namespace std;
-
- //依赖库
- #pragma comment(lib, "opengl32.lib")
- #pragma comment(lib, "glew32.lib")
- #pragma comment(lib, "freeglut.lib")
- #pragma comment(lib, "SOIL.lib")
-
-
- void userInit();
- void reshape(int w,int h);
- void display( void );
- void keyboardAction( unsigned char key, int x, int y );
-
-
- GLuint vboId;//vertex buffer object句柄
- GLuint vaoId;//vertext array object句柄
- GLuint indexboId;//index buffer object句柄
- GLuint programId;//shader program 句柄
- GLuint textureId;//texture 句柄
- GLuint samplerId;//sampler变量句柄
- const int verticesNum = 4;
- //三角形的顶点及纹理坐标
- const float verticesData[] = {
- -0.8f, 0.0f, 0.0f,
- 0.0f, -0.5f, 0.5,
- 0.8f, 0.0f, 0.0f,
- 0.0f, 0.8f, 0.0f,
-
- 0.0f, 0.0f,0.0f,
- 0.5f, 0.0f,0.0f,
- 1.0f, 0.0f,0.0f,
- 0.5f, 1.0f,0.0f
- };
- const GLshort indices[] = {
- 0,1,3,
- 1,2,3,
- 0,2,1,
- 0,3,2
- };
- int main( int argc, char **argv )
- {
- glutInit(&argc, argv);
- glutInitDisplayMode( GLUT_RGBA|GLUT_DOUBLE);
- glutInitWindowPosition(100,100);
- glutInitWindowSize( 512, 512 );
- glutCreateWindow( "Simple Texture Demo" );
-
- glewInit();
- userInit();
- glutReshapeFunc(reshape);
- glutDisplayFunc( display );
- glutKeyboardFunc( keyboardAction );
- glutMainLoop();
- return 0;
- }
- void InitializeProgram()
- {
- //从文件创建着色器
- std::vector<GLuint> idVector;
- idVector.push_back(Shader::createShader(GL_VERTEX_SHADER,"vertex.glsl"));
- idVector.push_back(Shader::createShader(GL_FRAGMENT_SHADER,"fragment.glsl"));
- programId = Shader::createProgram(idVector);
- //获取offset uniform变量句柄
- samplerId = glGetUniformLocation(programId,"gSampler");
- }
- //初始化VBO
- void initializeVBO()
- {
- glGenBuffers(1,&vboId);
- glBindBuffer(GL_ARRAY_BUFFER,vboId);
- glBufferData(GL_ARRAY_BUFFER,sizeof(verticesData),verticesData,GL_STATIC_DRAW);
- glBindBuffer(GL_ARRAY_BUFFER,0);
-
- glGenBuffers(1,&indexboId);
- //绑定到GL_ELEMENT_ARRAY_BUFFER才能支持索引绘图
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,indexboId);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
-
-
- }
- //初始化VAO
- void initializeVAO()
- {
- //创建vertex array object对象
- glGenVertexArrays(1,&vaoId);
- glBindVertexArray(vaoId);
- glBindBuffer(GL_ARRAY_BUFFER,vboId);
- //启用顶点着色器顶点坐标属性索引
- glEnableVertexAttribArray(0);
- glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,0);
- //启用顶点着色器纹理坐标属性索引
- glEnableVertexAttribArray(1);
- size_t textCoordOffset = 3*sizeof(float) * verticesNum;
- glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)textCoordOffset);
-
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,indexboId);
- }
- void InitializeTexture()
- {
- //创建纹理对象
- glGenTextures(1,&textureId);
- glBindTexture(GL_TEXTURE_2D,textureId);
-
- //设置采样参数
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
-
- //利用SOIL加载图片纹理
- int width,height;
- unsigned char* image = SOIL_load_image("bricks.png",&width,&height,0,SOIL_LOAD_RGBA);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
- SOIL_free_image_data(image);
- }
- //自定义初始化函数
- void userInit()
- {
- glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
- InitializeProgram();
- initializeVBO();
- initializeVAO();
- InitializeTexture();
- glEnable(GL_TEXTURE_2D);
- glEnable(GL_CULL_FACE);
- glFrontFace(GL_CCW);
- glCullFace(GL_BACK);
- }
- //绘制回调函数
- void display( void )
- {
- glClear(GL_COLOR_BUFFER_BIT);
- glUseProgram(programId);
- glBindVertexArray(vaoId);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE0,textureId);
- glUniform1i(samplerId, 0);
- //使用索引绘制四面体
- glDrawElements(GL_TRIANGLES,ARRAY_COUNT(indices),GL_UNSIGNED_SHORT,0);
- glUseProgram(0);
- glBindVertexArray(0);
- glutSwapBuffers();
- }
- //调整窗口大小回调函数
- void reshape(int w,int h)
- {
- glViewport(0,0,(GLsizei)w,(GLsizei)h);
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- gluPerspective(60.0,(GLfloat)w/(GLfloat)h,1.0,10.0);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- gluLookAt(0.0,0.0,10.0,0.0,0.5,0.0,0.0,1.0,0.0);
- }
- //键盘按键回调函数
- void keyboardAction( unsigned char key, int x, int y )
- {
- switch( key )
- {
- case 033: // Escape key
- exit( EXIT_SUCCESS );
- break;
- }
- }
3.3 使用多个纹理
使用多个纹理对象,并对两个纹理进行线性插值。参考[3]实现的效果如下:
参考代码如下:
textureUnits.cpp
- //绑定多个texture unit
- #include <string>
- #include <vector>
- #include <GL/glew.h>
- #include <GL/freeglut.h>
- #include <math.h>
- #include "shader.h"
- #include "SOIL.h"
- #define ARRAY_COUNT( array ) (sizeof( array ) / (sizeof( array[0] ) * (sizeof( array ) != sizeof(void*) || sizeof( array[0] ) <= sizeof(void*))))
- using namespace std;
-
- //依赖库
- #pragma comment(lib, "opengl32.lib")
- #pragma comment(lib, "glew32.lib")
- #pragma comment(lib, "freeglut.lib")
- #pragma comment(lib, "SOIL.lib")
-
-
- void userInit();
- void reshape(int w,int h);
- void display( void );
- void keyboardAction( unsigned char key, int x, int y );
-
-
- GLuint vboId;//vertex buffer object句柄
- GLuint vaoId;//vertext array object句柄
- GLuint indexboId;//index buffer object句柄
- GLuint programId;//shader program 句柄
- GLuint textureIds[2];//texture 句柄
- GLuint samplerIds[2];//sampler变量句柄
- const int verticesNum = 4;
- //三角形的顶点及纹理坐标
- const float verticesData[] = {
- -0.5f,-0.5f,0.0f, //bottom left
- 0.5f,-0.5f,0.0f, //bottom right
- 0.5f,0.5,0.0f, //top right
- -0.5f,0.5,0.0f, //top left
-
- 0.0f,0.0f,0.0f,
- 1.0f,0.0f,0.0f,
- 1.0f,1.0f,0.0f,
- 0.0f,1.0f,0.0f,
- };
- const GLshort indices[] = {
- 0,1,2,
- 2,3,0
- };
- int main( int argc, char **argv )
- {
- glutInit(&argc, argv);
- glutInitDisplayMode( GLUT_RGBA|GLUT_DOUBLE);
- glutInitWindowPosition(100,100);
- glutInitWindowSize( 512, 512 );
- glutCreateWindow( "Simple Texture Demo" );
-
- glewInit();
- userInit();
- glutReshapeFunc(reshape);
- glutDisplayFunc( display );
- glutKeyboardFunc( keyboardAction );
- glutMainLoop();
- return 0;
- }
- void InitializeProgram()
- {
- //从文件创建着色器
- std::vector<GLuint> idVector;
- idVector.push_back(Shader::createShader(GL_VERTEX_SHADER,"vertex.glsl"));
- idVector.push_back(Shader::createShader(GL_FRAGMENT_SHADER,"fragment.glsl"));
- programId = Shader::createProgram(idVector);
- //获取offset uniform变量句柄
- samplerIds[0]= glGetUniformLocation(programId,"catSampler");
- samplerIds[1]= glGetUniformLocation(programId,"dogSampler");
- }
- //初始化VBO
- void initializeVBO()
- {
- glGenBuffers(1,&vboId);
- glBindBuffer(GL_ARRAY_BUFFER,vboId);
- glBufferData(GL_ARRAY_BUFFER,sizeof(verticesData),verticesData,GL_STATIC_DRAW);
- glBindBuffer(GL_ARRAY_BUFFER,0);
-
- glGenBuffers(1,&indexboId);
- //绑定到GL_ELEMENT_ARRAY_BUFFER才能支持索引绘图
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,indexboId);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
-
-
- }
- //初始化VAO
- void initializeVAO()
- {
- //创建vertex array object对象
- glGenVertexArrays(1,&vaoId);
- glBindVertexArray(vaoId);
- glBindBuffer(GL_ARRAY_BUFFER,vboId);
- //启用顶点着色器顶点坐标属性索引
- glEnableVertexAttribArray(0);
- glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,0);
- //启用顶点着色器纹理坐标属性索引
- glEnableVertexAttribArray(1);
- size_t textCoordOffset = 3*sizeof(float) * verticesNum;
- glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)textCoordOffset);
-
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,indexboId);
- }
- //初始化多个纹理对象
- void InitializeTexture()
- {
- int width,height;
- unsigned char* image;
-
- glGenTextures(2,textureIds);
-
- //创建纹理对象1
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D,textureIds[0]);
- image = SOIL_load_image("cat.png",&width,&height,0,SOIL_LOAD_RGBA);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
- SOIL_free_image_data(image);
- //设置采样参数
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- //创建纹理对象2
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D,textureIds[1]);
- image = SOIL_load_image("dog.png",&width,&height,0,SOIL_LOAD_RGBA);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
- SOIL_free_image_data(image);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- }
- //自定义初始化函数
- void userInit()
- {
- glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
- InitializeProgram();
- initializeVBO();
- initializeVAO();
- InitializeTexture();
- glEnable(GL_TEXTURE_2D);
- glEnable(GL_CULL_FACE);
- glFrontFace(GL_CCW);
- glCullFace(GL_BACK);
- }
- //绘制回调函数
- void display( void )
- {
- glClear(GL_COLOR_BUFFER_BIT);
- glUseProgram(programId);
- glBindVertexArray(vaoId);
-
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE0,textureIds[0]);
- glUniform1i(samplerIds[0], 0);
-
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE0,textureIds[1]);
- glUniform1i(samplerIds[1], 1);
-
- //使用索引绘制四面体
- glDrawElements(GL_TRIANGLES,ARRAY_COUNT(indices),GL_UNSIGNED_SHORT,0);
- glUseProgram(0);
- glBindVertexArray(0);
- glutSwapBuffers();
- }
- //调整窗口大小回调函数
- void reshape(int w,int h)
- {
- glViewport(0,0,(GLsizei)w,(GLsizei)h);
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- gluPerspective(60.0,(GLfloat)w/(GLfloat)h,1.0,10.0);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- gluLookAt(0.0,0.0,10.0,0.0,0.5,0.0,0.0,1.0,0.0);
- }
- //键盘按键回调函数
- void keyboardAction( unsigned char key, int x, int y )
- {
- switch( key )
- {
- case 033: // Escape key
- exit( EXIT_SUCCESS );
- break;
- }
- }
vertex.glsl
- #version 330
-
- layout(location = 0) in vec3 position;
- layout(location = 1) in vec3 tc;
-
- out vec3 textCoord;
-
- void main()
- {
- gl_Position = vec4(position,1.0);
- textCoord = vec3(tc.s,1.0-tc.t,0.0);
- }
注意这里使用1.0-tc.t的作用是实现图片纹理的Y翻转。
fragment.glsl
- #version 330
-
- in vec3 textCoord;
- uniform sampler2D catSampler;
- uniform sampler2D dogSampler;
- out vec4 fragColor;
-
- void main()
- {
- fragColor = mix(texture2D(catSampler,textCoord.st),texture2D(dogSampler,textCoord.st),0.6);
- }
使用mix函数对两个纹理进行线性插值。
参考资料:
[1]
Basic Texture Mapping
[2] Texture Parameter and Filtering
[3] Textures objects and parameters
|