OpenGL 4.0 GLSL 实现 投影纹理映射(Projective Texture Mapping)
//
// main.cpp
// oneOfbag01
//
// Created by 瑛峰 on 14/12/22.
// Copyright (c) 2014年 angran. All rights reserved.
//
#include
/**
#define MAX_NUM 5
#define MAX_WEIGHT 10
using namespace std;
//动态规划求解
int zero_one_pack(int total_weight, int w[], int v[], int flag[], int n) {
int c[MAX_NUM+1][MAX_WEIGHT+1] = {0}; //c[i][j]表示前i个物体放入容量为j的背包获得的最大价值
//
状态转移方程:c[i][j] = max{c[i-1][j], c[i-1][j-w[i]]+v[i]}
//状态转移方程的解释:第i件物品要么放,要么不放
// 如果第i件物品不放的话,就相当于求前i-1件物体放入容量为j的背包获得的最大价值
// 如果第i件物品放进去的话,就相当于求前i-1件物体放入容量为j-w[i]的背包获得的最大价值
for (int i = 1; i <= n; i++) {
std::cout <<"\n\n";
for (int j = 1; j <= total_weight; j++) {
if (w[i] > j) {
// 说明第i件物品大于背包的重量,放不进去
c[i][j] = c[i-1][j];
} else {
//说明第i件物品的重量小于背包的重量,所以可以选择第i件物品放还是不放
if (c[i-1][j] > v[i]+c[i-1][j-w[i]]) {
c[i][j] = c[i-1][j];
}
else {
c[i][j] = v[i] + c[i-1][j-w[i]];
}
}
std::cout << "c["<
<<"]["<
<<"]的值" << c[i][j] <<"\n";
}
}
//下面求解哪个物品应该放进背包
int i = n, j = total_weight;
while (c[i][j] != 0) {
if (c[i-1][j-w[i]]+v[i] == c[i][j]) {
// 如果第i个物体在背包,那么显然去掉这个物品之后,前面i-1个物体在重量为j-w[i]的背包下价值是最大的
flag[i] = 1;
j -= w[i]; //表示既然i在背包里,就要去掉i占用的容量。
//--i; 移到外面去
}--i;
}
return c[n][total_weight];//最后一个获得的时总价值
}
int main() {
int total_weight = 10;
int w[4] = {0, 3, 4, 5};
int v[4] = {0, 4, 5, 6};
int flag[4]; //flag[i][j]表示在容量为j的时候是否将第i件物品放入背包
int total_value = zero_one_pack(total_weight, w, v, flag, 3);
cout << "需要放入的物品如下" << endl;
for (int i = 1; i <= 3; i++) {
std::cout << "flag["<
<<"]的值" << flag[i] <<"\n";
if (flag[i] == 1)
cout << "\t" << i << "\t重量为" << w[i] << ", 价值为" << v[i] << endl;
}
cout << "总的价值为: " << total_value << endl;
return 0;
}
*/
using namespace std;
#define MAX_WEIGHT 10
int Search(int n,int total_weight,int w[],int v[],int flag[])
{
int c[MAX_WEIGHT+1] = {0};
for (int i = 1; i <= n; i++) {
std::cout <<"\n\n";
for (int j = total_weight; j >= 1; j--) {
if (w[i] > j) {
c[j] = c[j]; //表示第i次与第i-1次相等,这里因为c[j]本来就保存这上一次的值,所以这里不需变化
} else {
//说明第i件物品的重量小于背包的重量,所以可以选择第i件物品放还是不放
if (c[j] > v[i]+c[j-w[i]]) {
c[j] = c[j];
}
else {
c[j] = v[i] + c[j-w[i]];
}
}
std::cout << "c["<
<<"]的值" << c[j] <<"\n"; } } //下面求解哪个物品应该放进背包 // int i = n, j = total_weight; // while (c[j] != 0) { // if (c[j-w[i]]+v[i] == c[j]) { // // 如果第i个物体在背包,那么显然去掉这个物品之后,前面i-1个物体在重量为j-w[i]的背包下价值是最大的 // flag[i] = 1; // j -= w[i]; //表示既然i在背包里,就要去掉i占用的容量。 // //--i; 移到外面去 // }--i; // } return c[total_weight]; } int main(int argc, const char * argv[]) { // insert code here... std::cout << "Hello, World!\n"; int total_weight = 10; int n = 3; int w[4] = {0,3,4,5}; int v[4] = {0,4,5,6}; int flag[4]; // for (int i=0; i<=10; i++) { // c[i] = 0; // } int total_value = Search(n,total_weight,w,v,flag); // 空间优化版本最后是求解不出来最优解序列的, // 但是能求出最优解,也就是最大价值 // cout << "需要放入的物品如下" << endl; // for (int i = 1; i <= 3; i++) { // std::cout << "flag["<
<<"]的值" << flag[i] <<"\n"; // if (flag[i] == 1) // cout << "\t" << i << "\t重量为" << w[i] << ", 价值为" << v[i] << endl; // } cout << "总的价值为: " << total_value << endl; return 0; }
投影纹理映射 (projective texture mapping):就是把纹理投射到场景的物体上,就像一个投影机把幻灯片投影到其他物体上一样。
如下图:用左边的纹理图像投影到一个茶壶上
投影纹理的实现方法:
其实最重要的一点就是确定纹理坐标,纹理坐标的确定依赖于物体表面点的相对位置和投影机的位置。
在OpenGL中我们可以定义一个camera,我们定义一个中心在投影机位置的坐标空间,
viewMatrix(V)把世界坐标系的点 转换 到投影机的坐标系中,然后定义一个投影矩阵(P)。这样P*V就把点转化到投影空间,
但规格化的投影空间是[-1,1],而纹理坐标是[0,1],因此我们需要把 这个视景体转化到[0,1]中,
我们可以先把其缩小1/2,然后再平移1/2,这样就转化到了[0,1]上。如图:
注意:由于坐标是齐次坐标系(homogeneous),在我们用其访问纹理坐标前需要除以w。
应用程序设置代码片段
- vec3 projPos = vec3(5.0f,5.0f,5.0f);
- vec3 projAt = vec3(-2.0f,-4.0f,0.0f);
- vec3 projUp = vec3(0.0f,1.0f,0.0f);
- mat4 projView = glm::lookAt(projPos, projAt, projUp);print(projView);
- mat4 projProj = glm::perspective(30.0f, 1.0f, 0.2f, 1000.0f);print(projProj);
- mat4 projScaleTrans = glm::translate(vec3(0.5f)) * glm::scale(vec3(0.5f));
- prog.setUniform("ProjectorMatrix", projScaleTrans * projProj * projView);
- glActiveTexture(GL_TEXTURE0);
- TGAIO::loadTex("../media/texture/flower.tga");
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
顶点shader
片元shader
- #version 430
- in vec3 EyeNormal;
- in vec4 EyePosition;
- in vec4 ProjTexCoord;
- layout(binding=0) uniform sampler2D ProjectorTex;
- struct MaterialInfo {
- vec3 Kd;
- vec3 Ks;
- vec3 Ka;
- float Shininess;
- };
- uniform MaterialInfo Material;
- struct LightInfo {
- vec3 Intensity;
- vec4 Position;
- };
- uniform LightInfo Light;
- layout( location = 0 ) out vec4 FragColor;
- vec3 phongModel( vec3 pos, vec3 norm ) {
- vec3 s = normalize(vec3(Light.Position) - pos);
- vec3 v = normalize(-pos.xyz);
- vec3 r = reflect( -s, norm );
- vec3 ambient = Light.Intensity * Material.Ka;
- float sDotN = max( dot(s,norm), 0.0 );
- vec3 diffuse = Light.Intensity * Material.Kd * sDotN;
- vec3 spec = vec3(0.0);
- if( sDotN > 0.0 )
- spec = Light.Intensity * Material.Ks *
- pow( max( dot(r,v), 0.0 ), Material.Shininess );
- return ambient + diffuse + spec;
- }
- void main() {
- vec3 color = phongModel(vec3(EyePosition), EyeNormal);
- vec4 projTexColor = vec4(0.0);
- if( ProjTexCoord.z > 0.0 )
- projTexColor = textureProj( ProjectorTex, ProjTexCoord );
- FragColor = vec4(color,1.0) + projTexColor * 0.5;
- }
在片元着色器中,if( ProjTexCoord.z > 0.0 ) 意思是如果ProjTexCoord.z 为负,则说明在投影机的后面,我们不需要查找对应的纹理了。
textureProj 函数是用来访问纹理的(传入的纹理坐标是投影坐标系下的坐标),在前面我们提到,在平移缩放投影矩阵后,我们需要除以其坐标中的w项,而textureProj 会为我们做这些。
在本例子中有一个很大的缺点,投影纹理(projected texture) 将会投射到场景中的任何物体上,即便有物体遮挡。例如,我们可以让上图的地板向下移动一些。结果如图:
总结:
投影纹理映射真正的流程是 “ 根据投影机(视点相机)的位置、投影角度,物体的坐标,求出每个顶点所对应的纹理坐标,
然后依据纹理坐标去查询纹理值 ” ,
也就是说,不是将纹理投影到墙上,而是把墙投影到纹理上。
投影纹理坐标的求得,也与纹理本身没有关系,而是由投影机的位置、角度,以及 3D 模型的顶点坐标所决定
projtexcoord=偏移矩阵 * 光源投影矩阵 * 光源观察矩阵 * 建模矩阵 【我的理解:偏移矩阵就是 CVV坐标是在[-1,1],而纹理坐标是[0,1] 】