代码在立方体案例基础上,使用了第二个纹理进行混合反射球体颜色。
主要代码:
// Set textures to their texture units
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tarnishTexture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTexture);
当前纹理单元切到第二个纹理单元,然后设定tarnishTexture纹理到第二个2D纹理单元上,然后切到第一个纹理单元,设定cubeTexture纹理到第一个立方体纹理单元上。
在构建球体批次时会有一组二维纹理坐标属性,在顶点着色器会输出到片段着色器中,经过插值在片段着色器得到插值后的2D纹理坐标,可用它进行采样第二个纹理贴图即tarnishTexture。
在片段着色器采样得到颜色后乘上立方体贴图采样出的颜色即可输出。下面,只给出球体的着色器和GLSL代码,天空盒着色器还是与立方体贴图案例的保持一样。
// Reflection Shader
// Vertex Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130
// Incoming per vertex... position and normal
in vec4 vVertex;
in vec3 vNormal;
in vec2 vTexCoords;
uniform mat4 mvpMatrix;
uniform mat4 mvMatrix;
uniform mat3 normalMatrix;
uniform mat4 mInverseCamera;
// Texture coordinate to fragment program
smooth out vec3 vVaryingTexCoord;
smooth out vec2 vTarnishCoords;
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);
vTarnishCoords = vTexCoords.st;
// Don't forget to transform the geometry!
gl_Position = mvpMatrix * vVertex;
}
// Reflection Shader
// Fragment Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#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);
}
// MultiTexture.cpp
// OpenGL SuperBible
// Demonstrates applying a cube map to an object (sphere)
// simultaneously with a "tarnish" texture.
// Program by Richard S. Wright Jr.
#pragma comment(lib, "gltools.lib")
#include <GLTools.h> // OpenGL toolkit
#include <GLMatrixStack.h>
#include <GLFrame.h>
#include <GLFrustum.h>
#include <GLGeometryTransform.h>
#include <Stopwatch.h>
#include <math.h>
#include <stdlib.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
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;
// Six sides of a cube map
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 };
//
// This function does any needed initialization on the rendering
// context.
void SetupRC()
{
GLbyte *pBytes;
GLint iWidth, iHeight, iComponents;
GLenum eFormat;
int i;
// Cull backs of polygons
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glEnable(GL_DEPTH_TEST);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Load the tarnish texture
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);
// Load the cube map
glGenTextures(1, &cubeTexture);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTexture);
// Set up texture maps
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);
// Load Cube Map images
for (i = 0; i < 6; i++)
{
// Load this texture map
pBytes = gltReadTGABits(szCubeFaces[i], &iWidth, &iHeight, &iComponents, &eFormat);
glTexImage2D(cube[i], 0, iComponents, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBytes);
free(pBytes);
}
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
viewFrame.MoveForward(-4.0f);
gltMakeSphere(sphereBatch, 1.0f, 52, 26);
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");
// Set textures to their texture units
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tarnishTexture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTexture);
}
void ShutdownRC(void)
{
glDeleteTextures(1, &cubeTexture);
}
// Called to draw scene
void RenderScene(void)
{
// Clear the window
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();
// Draw the sphere
modelViewMatrix.MultMatrix(mCamera);
glUseProgram(reflectionShader);
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(mCameraRotOnly);
glUseProgram(skyBoxShader);
glUniformMatrix4fv(locMVPSkyBox, 1, GL_FALSE, transformPipeline.GetModelViewProjectionMatrix());
cubeBatch.Draw();
modelViewMatrix.PopMatrix();
// Do the buffer Swap
glutSwapBuffers();
}
// Respond to arrow keys by moving the camera frame of reference
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);
// Refresh the Window
glutPostRedisplay();
}
void ChangeSize(int w, int h)
{
// Prevent a divide by zero
if (h == 0)
h = 1;
// Set Viewport to window dimensions
glViewport(0, 0, w, h);
viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 1000.0f);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(800, 600);
glutCreateWindow("OpenGL MultiTexture");
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
glutSpecialFunc(SpecialKeys);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}