下面以一个实际的例子来说明如何实现投影纹理映射(OpenGL+GLSL),其效果及开头所给出的图一。图中绘制了一个茶壶,然后从一个方向将一幅纹理图像投影到茶壶上面。
顶点着色器projtex.vs:
#version 400
layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;
out vec3 EyeNormal; // Normal in eye coordinates
out vec4 EyePosition; // Position in eye coordinates
out vec4 ProjTexCoord;
uniform mat4 ProjectorMatrix;//用于计算投影纹理映射的矩阵
uniform vec3 WorldCameraPosition;
uniform mat4 ModelViewMatrix;//模型视图矩阵
uniform mat4 ModelMatrix;//模型变换矩阵
uniform mat3 NormalMatrix;//法线变换矩阵
uniform mat4 ProjectionMatrix;//投影变换矩阵
uniform mat4 MVP;//模型视图变换矩阵
void main()
{
vec4 pos4 = vec4(VertexPosition,1.0);
EyeNormal = normalize(NormalMatrix * VertexNormal);
EyePosition = ModelViewMatrix * pos4;
ProjTexCoord = ProjectorMatrix * (ModelMatrix * pos4);
gl_Position = MVP * pos4;
}
layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;
out vec3 EyeNormal; // Normal in eye coordinates
out vec4 EyePosition; // Position in eye coordinates
out vec4 ProjTexCoord;
uniform mat4 ProjectorMatrix;//用于计算投影纹理映射的矩阵
uniform vec3 WorldCameraPosition;
uniform mat4 ModelViewMatrix;//模型视图矩阵
uniform mat4 ModelMatrix;//模型变换矩阵
uniform mat3 NormalMatrix;//法线变换矩阵
uniform mat4 ProjectionMatrix;//投影变换矩阵
uniform mat4 MVP;//模型视图变换矩阵
void main()
{
vec4 pos4 = vec4(VertexPosition,1.0);
EyeNormal = normalize(NormalMatrix * VertexNormal);
EyePosition = ModelViewMatrix * pos4;
ProjTexCoord = ProjectorMatrix * (ModelMatrix * pos4);
gl_Position = MVP * pos4;
}
上面的代码前两行是将法线和顶点坐标变换到eye space。
然后是计算投影纹理坐标:先将位于object space的坐标变换到world space,然后通过左乘纹理投影矩阵变换计算出纹理投影坐标。
最后计算内置的gl_Position即剪切平面坐标。
片断着色器projtex.fs(下面只列出一部分):
#version 400
in vec3 EyeNormal; // Normal in eye coordinates
in vec4 EyePosition; // Position in eye coordinates
in vec4 ProjTexCoord;
uniform sampler2D ProjectorTex;
layout( location = 0 ) out vec4 FragColor;
void main() {
vec3 color = phongModel(vec3(EyePosition), EyeNormal);
vec4 projTexColor = vec4(0.0);
if( ProjTexCoord.z > 0.0 )
//textureProj:纹理坐标各分量会除以最后一个分量
projTexColor = textureProj( ProjectorTex, ProjTexCoord );
FragColor = vec4(color,1.0) + projTexColor * 0.5;
}
#version 400
in vec3 EyeNormal; // Normal in eye coordinates
in vec4 EyePosition; // Position in eye coordinates
in vec4 ProjTexCoord;
uniform sampler2D ProjectorTex;
layout( location = 0 ) out vec4 FragColor;
void main() {
vec3 color = phongModel(vec3(EyePosition), EyeNormal);
vec4 projTexColor = vec4(0.0);
if( ProjTexCoord.z > 0.0 )
//textureProj:纹理坐标各分量会除以最后一个分量
projTexColor = textureProj( ProjectorTex, ProjTexCoord );
FragColor = vec4(color,1.0) + projTexColor * 0.5;
}
函数phongModel是计算phong光照模型的。
注意其中的textureProj函数,它专门用于投影纹理访问的。它的纹理坐标的各分量会除以最后一个分量,然后才访问纹理。
在这个例子中,我们假设投影仪位于(2.0,5.0,5.0),朝向是(-2.0,-4.0,0.0),方向(0.0,1.0,0.0)是向上的。我们采用了一个外部库GLM库来根据投影仪的这些信息计算出各个变换矩阵,代码如下: