OpenGL使用多重纹理也同样非常简单,比如我们想在上一节反射的小球上再加一层纹理,实现下面的效果

Android OpenGL 多纹理 opengl多重纹理_编程


我们给小球加上去的纹理贴图名字叫做"tarnish.tga",如下图

Android OpenGL 多纹理 opengl多重纹理_编程_02


还记得上一节反射中,我们给小球设置颜色值时,只是将对应的天空盒的纹理坐标赋予小球的颜色值,如下

vFragColor = texture(cubeMap, vVaryingTexCoord);



这一次我们只需要把这个颜色值再乘上"晦暗"的纹理坐标的颜色值即可,如下

//采用天空盒的纹理坐标颜色值
vFragColor = texture(cubeMap, vVaryingTexCoord.stp);
//与晦暗纹理贴图的颜色值相乘,作为光栅化时的颜色值
vFragColor *= texture(tarnishMap, vTarnishCoords);


假如我们想要同时使用两个纹理,我们首先需要创建了个纹理对象 

glGenTextures(1, &texture0);

glGenTextures(1, &texture1);


然后我们使用下面的代码即可将两个纹理对象绑定在一起

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture0);

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D,texture1);

下面来看看本节的代码实现,这次我们将贴出所以代码,注释很详细


首先是天空盒的顶点着色器SkyBox.vp

//需要OpenGL 最低版本为1.3
#version 130
//顶点
in vec4 vVertex;
uniform mat4   mvpMatrix;  
// 需要传入到片段着色器中的纹理坐标
varying vec3 vVaryingTexCoord;

void main(void) 
    {
		//得到顶点法线
		vVaryingTexCoord = normalize(vVertex.xyz);
		//将顶点变换到照相机坐标系下
		gl_Position = mvpMatrix * vVertex;
    }



下面是天空盒的片段着色器SkyBox.fp

// 需要OpenGL 最低版本为1.3
#version 130
out vec4 vFragColor;
uniform samplerCube  cubeMap;
//纹理坐标
varying vec3 vVaryingTexCoord;

void main(void)
    { 
		vFragColor = texture(cubeMap, vVaryingTexCoord);
    }



下面是小球的反射的顶点着色器Reflection.vp

// 需要OpenGL 最低版本为1.3
#version 130

// 需要从外界输入的值
//顶点
in vec4 vVertex;
//法线
in vec3 vNormal;
//纹理坐标
in vec2 vTexCoords;
//需要从外界设置的uniform值
uniform mat4   mvpMatrix;
uniform mat4   mvMatrix;
uniform mat3   normalMatrix;
uniform mat4   mInverseCamera;

//需要传入到片段着色器中的天空盒的纹理坐标
smooth out vec3 vVaryingTexCoord;
//需要传入到片段着色器中的晦暗的纹理坐标
smooth out vec2 vTarnishCoords;

void main(void) 
    {
		// 照相机空间中的顶点法线
		vec3 vEyeNormal = normalMatrix * vNormal;
		
		// 照相机空间中的顶点位置
		vec4 vVert4 = mvMatrix * vVertex;
		vec3 vEyeVertex = normalize(vVert4.xyz / vVert4.w);
		
		// 得到顶点的反射向量
		vec4 vCoords = vec4(reflect(vEyeVertex, vEyeNormal), 1.0);
	   
		// 通过反转的照相机进行旋转
		vCoords = mInverseCamera * vCoords;
		vVaryingTexCoord.xyz = normalize(vCoords.xyz);
		//对"晦暗"的纹理坐标进行赋值
		vTarnishCoords = vTexCoords.st;

		// 将顶点变换到照相机坐标系下
		gl_Position = mvpMatrix * vVertex;
    }



下面是小球的反射的片段着色器Reflection.fp

// 需要OpenGL 最低版本为1.3
#version 130

out vec4 vFragColor;
//天空盒的纹理对象索引
uniform samplerCube cubeMap;
//"晦暗"的纹理对象索引
uniform sampler2D   tarnishMap;

smooth in vec3 vVaryingTexCoord;
smooth in vec2 vTarnishCoords;

void main(void)
    { 
		//采用天空盒的纹理坐标颜色值
		vFragColor = texture(cubeMap, vVaryingTexCoord.stp);
		//与晦暗纹理贴图的颜色值相乘,作为光栅化时的颜色值
		vFragColor *= texture(tarnishMap, vTarnishCoords);
    }




下面是工程代码部分

首先是需要包含的头文件和全局变量部分

#include <GLTools.h>	
#include <GLMatrixStack.h>
#include <GLFrame.h>
#include <GLFrustum.h>
#include <GLGeometryTransform.h>
#include <Stopwatch.h>

#include <math.h>
#include <stdlib.h>

#define FREEGLUT_STATIC
#include <GL/glut.h>

GLFrame             viewFrame;
GLFrustum           viewFrustum;
GLTriangleBatch     sphereBatch;
GLBatch             cubeBatch;
GLMatrixStack       modelViewMatrix;
GLMatrixStack       projectionMatrix;
GLGeometryTransform transformPipeline;
GLuint              cubeTexture;
GLuint				tarnishTexture;
GLint               reflectionShader;
GLint               skyBoxShader;

GLint               locMVPReflect, locMVReflect, locNormalReflect, locInvertedCamera;
GLint				locCubeMap, locTarnishMap;
GLint				locMVPSkyBox;

// 天空盒六个面的纹理图片
const char *szCubeFaces[6] = { "pos_x.tga", "neg_x.tga", "pos_y.tga", "neg_y.tga", "pos_z.tga", "neg_z.tga" };
//天空盒六个方向
GLenum  cube[6] = {  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 };






然后是主函数main


int main(int argc, char* argv[])
{
	//设置工程路径
	gltSetWorkingDirectory(argv[0]);
	//初始化GLUT
	glutInit(&argc, argv);
	//设置需要使用的窗口模式
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
	//窗口的大小
	glutInitWindowSize(800, 600);
	//窗口的名字
	glutCreateWindow("ToomShader");
	//窗口大小改变时的回调函数
	glutReshapeFunc(ChangeSize);
	//渲染时的回调函数
	glutDisplayFunc(RenderScene);
	//键盘响应的回调函数
	glutSpecialFunc(SpecialKeys);
	//初始化glew
	GLenum err = glewInit();
	if (GLEW_OK != err) {
		fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
		return 1;
	}
	//初始化函数
	SetupRC();
	//消息循环
	glutMainLoop();
	//程序退出时的清理工作
	ShutdownRC();
	return 0;
}



下面是窗口大小改变时的回调函数ChangeSize

void ChangeSize(int w, int h)
{
	// Prevent a divide by zero
	if (h == 0)
		h = 1;

	//设置视口大小
	glViewport(0, 0, w, h);
	//设置投影变换矩阵
	viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 100.0f);
	//设置模型变换矩阵
	projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
	transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}



下面是响应键盘消息的函数SpecialKeys

void SpecialKeys(int key, int x, int y)
    {
		if(key == GLUT_KEY_UP)
			viewFrame.MoveForward(0.1f);

		if(key == GLUT_KEY_DOWN)
			viewFrame.MoveForward(-0.1f);

		if(key == GLUT_KEY_LEFT)
			viewFrame.RotateLocalY(0.1);
      
		if(key == GLUT_KEY_RIGHT)
			viewFrame.RotateLocalY(-0.1);

		glutPostRedisplay();
    }



下面是初始化函数SetupRC

void SetupRC()
{
	GLbyte *pBytes;
	GLint iWidth, iHeight, iComponents;
	GLenum eFormat;
	int i;

	// 禁用多边形背面上的光照、阴影和颜色计算及操作
	glCullFace(GL_BACK);
	//GL_CCW 表示窗口坐标上投影多边形的顶点顺序为逆时针方向的表面为正面
	glFrontFace(GL_CCW);
	//开启深度测试
	glEnable(GL_DEPTH_TEST);
	//设置数据内存对齐方式
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	//创建纹理对象
	glGenTextures(1, &cubeTexture);
	//绑定纹理对象
	glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTexture);

	//设置纹理过滤模式    
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	//设置纹理渲染模式
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

	// 加载"晦暗"的纹理贴图tarnish.tga
	glGenTextures(1, &tarnishTexture);
	glBindTexture(GL_TEXTURE_2D, tarnishTexture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	pBytes = gltReadTGABits("tarnish.tga", &iWidth, &iHeight, &iComponents, &eFormat);
	glTexImage2D(GL_TEXTURE_2D, 0, iComponents, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBytes);
	free(pBytes);
	glGenerateMipmap(GL_TEXTURE_2D);

	

	//加载纹理图片
	for (i = 0; i < 6; i++)
	{
		pBytes = gltReadTGABits(szCubeFaces[i], &iWidth, &iHeight, &iComponents, &eFormat);
		glTexImage2D(cube[i], 0, iComponents, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBytes);
		free(pBytes);
	}
	//开启mip贴图模式
	glGenerateMipmap(GL_TEXTURE_CUBE_MAP);

	viewFrame.MoveForward(-4.0f);
	//初始化小球模型数据
	gltMakeSphere(sphereBatch, 1.0f, 52, 26);
	//初始化立方体模型的数据,这个立方体就是用来贴6张天空的纹理图片的,从而形成天空盒
	gltMakeCube(cubeBatch, 20.0f);

	reflectionShader = gltLoadShaderPairWithAttributes("Reflection.vp", "Reflection.fp", 3,
		GLT_ATTRIBUTE_VERTEX, "vVertex",
		GLT_ATTRIBUTE_NORMAL, "vNormal",
		GLT_ATTRIBUTE_TEXTURE0, "vTexCoords");

	locMVPReflect = glGetUniformLocation(reflectionShader, "mvpMatrix");
	locMVReflect = glGetUniformLocation(reflectionShader, "mvMatrix");
	locNormalReflect = glGetUniformLocation(reflectionShader, "normalMatrix");
	locInvertedCamera = glGetUniformLocation(reflectionShader, "mInverseCamera");
	locCubeMap = glGetUniformLocation(reflectionShader, "cubeMap");
	locTarnishMap = glGetUniformLocation(reflectionShader, "tarnishMap");

	//加载自己写的天空盒的着色器                                        
	skyBoxShader = gltLoadShaderPairWithAttributes("SkyBox.vp", "SkyBox.fp", 2,
		GLT_ATTRIBUTE_VERTEX, "vVertex",
		GLT_ATTRIBUTE_NORMAL, "vNormal");

	locMVPSkyBox = glGetUniformLocation(skyBoxShader, "mvpMatrix");

	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, tarnishTexture);
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTexture);

}



下面是渲染函数RenderScene

void RenderScene(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	M3DMatrix44f mCamera;
	M3DMatrix44f mCameraRotOnly;
	M3DMatrix44f mInverseCamera;

	viewFrame.GetCameraMatrix(mCamera, false);
	viewFrame.GetCameraMatrix(mCameraRotOnly, true);
	m3dInvertMatrix44(mInverseCamera, mCameraRotOnly);

	modelViewMatrix.PushMatrix();
	modelViewMatrix.MultMatrix(mCamera);
	//使用自己写的着色器
	glUseProgram(reflectionShader);
	//设置着色器uniform值
	glUniformMatrix4fv(locMVPReflect, 1, GL_FALSE, transformPipeline.GetModelViewProjectionMatrix());
	glUniformMatrix4fv(locMVReflect, 1, GL_FALSE, transformPipeline.GetModelViewMatrix());
	glUniformMatrix3fv(locNormalReflect, 1, GL_FALSE, transformPipeline.GetNormalMatrix());
	glUniformMatrix4fv(locInvertedCamera, 1, GL_FALSE, mInverseCamera);
	//设置片段着色器中的纹理对象的索引
	glUniform1i(locCubeMap, 0);
	glUniform1i(locTarnishMap, 1);

	glEnable(GL_CULL_FACE);
	sphereBatch.Draw();
	glDisable(GL_CULL_FACE);
	modelViewMatrix.PopMatrix();

	modelViewMatrix.PushMatrix();
	modelViewMatrix.MultMatrix(mCamera);
	//使用自己写的着色器
	glUseProgram(skyBoxShader);
	//设置着色器uniform值
	glUniformMatrix4fv(locMVPSkyBox, 1, GL_FALSE, transformPipeline.GetModelViewProjectionMatrix());
	//绘制模型
	cubeBatch.Draw();
	modelViewMatrix.PopMatrix();
	glutSwapBuffers();
}



下面是程序退出时的清理函数ShutdownRC

void ShutdownRC(void)
    {
		//销毁纹理对象
		glDeleteTextures(1, &cubeTexture);
		glDeleteTextures(1, &tarnishTexture);
    }