OpenGL蓝宝书第七章:立体天空和纹理折射、双纹理(上)

本文深入探讨OpenGL中的3D纹理映射和顶点处理,特别是立体天空和双纹理技术。讲解了立方图纹理的选择、球体纹理的折射原理,以及在OpenGL中的实现方法,包括球面折射的GL_SPHERE_MAP函数和GLSL中的反射计算。通过实例代码skyBox.vp和Reflection.vp展示了具体操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

难点在于如何理解3D纹理和顶点的映射。


遗留问题,怎么我看到的原纹理是倒置的。


P293

在红宝书中提到:

为了确定某个特定的片段所使用的纹理(以及纹理单元),当前纹理(s,t,r)首先根据s/t/r中最大绝对值(主轴)以及它的符号(方向),在6个纹理中选择其一。剩余的两个坐标除以坐标的最大值,得到新坐标(s',t'),以查找立方图纹理中那个被选择的纹理对应的纹理单元。


立方图纹理的原点在其几何中心,即正方体的几何中心。


球体纹理:

在球体上折射环境纹理,需要取一条从观察点到这个点的光线,然后把这条光线从表面反射出来,这条带有环境纹理颜色的放射光线的方向完全决定了这个点需要绘制的颜色。

而需要编码的顶点或片段区域是一个圆形区域,它与纹理图像的顶、底、左、右边缘相切,圆形区域之外的纹理值不会对结果产生影响,因为她们不会在环境纹理中使用。

创建环境纹理图像后,需要在球体表面找到一个与被渲染物理的点具有相同正切面的点,并且把球体上这个点的颜色绘制成那个物体对应点的颜色。


GL_SPHERE_MAP 是球体纹理映射的的函数标示常量。其原理如下:

1)u是从原点指向顶点的单位向量(在视觉坐标中,即惯性坐标系)

2)n'是当前法线向量,在变换为视觉坐标系之后

3)r是放射向量(Rx,Ry,Rz)T,它是通过u-2n‘(n’T)u这个公式计算,即vp脚本中的reflect函数

4)中间值m = 2*sqrt(Rx^2+Ry^2+(Rz+1)^2)

5) s = Rx/m +1/2, t =  Ry/m +1/2


通过上述的解释可以确定一件事,球面折射的实现需要在glsl中做顶点和折射处理即可,不需要绑定纹理


//skyBox.vp

#version 330

in vec4 vVertex;

uniform mat4 mvpMatrix;  // Transformation matrix

smooth out vec3 vVaryingTexCoord;

void main(void)
{
	vVaryingTexCoord = normalize(vVertex.xyz);
	gl_Position = mvpMatrix * vVertex;
}

//skyBox.fp

#version 330

uniform samplerCube cubeMap;

in vec3 vVaryingTexCoord;

out vec4 vFragColor;

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



//Reflection.vp

#version 330

in vec4 vVertex;
in vec3 vNormal;

uniform mat4 mvpMatrix;
uniform mat4 mvMatrix;
uniform mat3 normalMatrix;
uniform mat4 mInverseCamera;

smooth out vec3 vVaryingTexCoord;

void main(void)
{
//在惯性系的坐标
	vec3 vEyeNormal = normalMatrix * vNormal;
	
	vec4 vPosition4 = mvMatrix * vVertex;
	vec3 vEyeVertex = normalize(vPosition4.xyz/vPosition4.w);
	
	vec4 cCoords = vec4(reflect(vEyeVertex,vEyeNormal),1.0f);//不清楚reflect的原理,导致代码思想断片,靠猜?开玩笑,还是开源比较好
	cCoords = mInverseCamera * cCoords;//全面反射场景???
	
	vVaryingTexCoord.xyz = normalize(cCoords.xyz); //获得是什么坐标?
	
	gl_Position = mvpMatrix * vVertex;
}

//Reflection.fp

#version 330

smooth in vec3 vVaryingTexCoord;

uniform samplerCube cubeMap;

out vec4 vFragColor;

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



//cpp

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

#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

GLFrame viewFrame;
GLFrustum viewFrustum;
GLMatrixStack projctionMatrix;
GLMatrixStack mvpMatrix;
GLMatrixStack modelViewMatrix;
GLGeometryTransform transformPipeLine;

GLTriangleBatch sphereBatch;
GLBatch cubeBatch;

GLuint cubeTexture;
GLuint reflectionShader;
GLuint skyBoxShader;

GLint locMVPReflect;
GLint locMVReflect;
GLint locNormalReflect;
GLint locInvertedCamera;

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
};

void ChangeSize(int w, int h)
{
	if (h <= 0)
	{
		h = 1;
	}

	glViewport(0, 0, w, h);
	viewFrustum.SetPerspective(80.0f, float(w) / float(h), 1.0f, 120.0f);
	projctionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
	transformPipeLine.SetMatrixStacks(modelViewMatrix, projctionMatrix);
}


void SetupRC(void)
{
	GLbyte * pBytes;
	int nWidth, nHeight, nComponent;
	GLenum eFormat;

	glCullFace(GL_BACK);
	glEnable(GL_DEPTH_TEST);
	
	glGenTextures(1, &cubeTexture);
	glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTexture);

	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_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);

	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	for (size_t i = 0; i < 6; ++i)
	{
		pBytes = gltReadTGABits(szCubeFaces[i], &nWidth, &nHeight, &nComponent, &eFormat);
		// border 为0,是指边框的宽度
		//eFormat 不需要 和internalFormat取值必须相同,都是指定纹理中的颜色组件
		glTexImage2D(cube[i], 0, nComponent, nWidth, nHeight, 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", 2, GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_NORMAL, "vNormal");
	//uniform值绑定
	locMVPReflect = glGetUniformLocation(reflectionShader, "mvpMatrix");
	locMVReflect = glGetUniformLocation(reflectionShader, "mvMatrix");
	locNormalReflect = glGetUniformLocation(reflectionShader, "normalMatrix");
	locInvertedCamera = glGetUniformLocation(reflectionShader, "mInverseCamera");

	skyBoxShader = gltLoadShaderPairWithAttributes("skyBox.vp", "skyBox.fp", 1, GLT_ATTRIBUTE_VERTEX, "vVertex");
	//uniform值绑定

	locMVPSkyBox = glGetUniformLocation(skyBoxShader, "mvpMatrix");

}

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);//向后移动 4.0f,惯性坐标系的向后则是z变小,模型变小
		glUseProgram(reflectionShader);

		glUniformMatrix4fv(locMVReflect, 1, GL_FALSE, transformPipeLine.GetModelViewMatrix());
		glUniformMatrix4fv(locMVPReflect, 1, GL_FALSE, transformPipeLine.GetModelViewProjectionMatrix());
		glUniformMatrix3fv(locNormalReflect, 1, GL_FALSE, transformPipeLine.GetNormalMatrix());
		glUniformMatrix4fv(locInvertedCamera, 1, GL_FALSE, mInverseCamera);

		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();

	glutSwapBuffers();
}

void SpecialKeys(int key, int x, int y)
{
	float linear = 1.0f;
	float angular = float(m3dDegToRad(5.0f));

	if (key == GLUT_KEY_UP)
		viewFrame.MoveForward(linear);

	if (key == GLUT_KEY_DOWN)
		viewFrame.MoveForward(-linear);

	if (key == GLUT_KEY_LEFT)
		viewFrame.RotateWorld(angular, 0.0f, 1.0f, 0.0f);

	if (key == GLUT_KEY_RIGHT)
		viewFrame.RotateWorld(-angular, 0.0f, 1.0f, 0.0f);

	glutPostRedisplay();
}

void ShutdownRC(void)
{
	glDeleteTextures(1, &cubeTexture);
	glDeleteProgram(reflectionShader);
	glDeleteProgram(skyBoxShader);
}

int main(int argc, char* argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(800, 600);
	glutCreateWindow("OpenGL Cube Maps");
	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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值