http://blog.csdn.net/xukunn1226/article/details/775644

Projective Texture是比较常见的一种技术,实现起来代码也就区区的不过百行,了解其原理及技术细节是我们的重点,知其然,知其所以然。
       粗略的说就是想象场景中有台投影仪(Projector),texture就是我们投影的内容,把纹理放在近裁剪面(near clip plane)上,沿着投影仪的方向把纹理投影到场景中。Xheartblue兄翻译了一篇文章,很好的给投影纹理的原理进行的阐述[1],有兴趣阅读原文的可以访问这里[2],这本书可以是好东东啊!!
在这里有几个概念不能混淆了:Camera——人眼坐标;Projector——投影仪坐标。在纹理坐标自动生成过程中,关键的就是如何把人眼坐标系中的vertex转换到Projector Space,进而转换到Projector clip plane,最后规范化为纹理坐标[0,1]。在我的实现中是把Projector定义为一个Camera(注意:不同于显示场景的Camera),所以Projector有Camera的各项属性,我们可以通过gluPerspective,gluLookAt对其进行设置。如何为Projective texture自动生成纹理坐标是重点,这个过程和物体顶点变换为窗口坐标是类似的。在NV Developer Document[3]中有篇文档说的很详细,截图如下:
 
 

对上图可以这么理解,Camera用于物体顶点坐标到规范化设备的生成,Projector用于物体顶点纹理坐标的生成。而在不同模式下纹理坐标的生成方式是不同。

根据glTexGen的不同参数GL_OBJECT_LINEAR,GL_EYE_LINEAR来确定纹理生成的函数。在Projective texture mapping一文中给出的纹理坐标生成公式是:

注意:此处Vo是基于物体坐标系的,无论物体在人眼坐标系中如何变换,其物体坐标是不变的,根据公式其纹理坐标也是不变的。所以在GL_OBJECT_LINEAR模式下看到的纹理是紧贴在物体表面的。而Ve是基于人眼坐标系的,在Projector设置好位置后是基于人眼坐标不变的。

我们来看Object Linear模式下纹理坐标是如何生成的:

此处的M(Model Matrix)是模型变换矩阵,不同于OPENGL的MODELVIEW MATRIX(这在模型变换的基础上还进行了视图变换)。顶点坐标左乘M后变换到World Space,为什么要变换到World Space呢??这是因为Camera和Projector都是通过gluLookAt而定义在世界坐标中,这就像座桥梁,唯有通过它才能使得人眼视图体中的顶点转换到Projector定义的视图体内,才能进一步求出相应的纹理坐标。Vp是projector的view matrix(由gluLookAt定义),累加左乘得到projector space中的坐标。Pp是projector的projection matrix(由gluPerspective定义),累加左乘得到projector clip space中的坐标。最后累加偏移矩阵,使纹理坐标的s、t、r映射到[0,1]内。

本文关注的是Eye Linear模式下纹理坐标的生成,有了以上对Object Linear的理解就好办了,公式如下:

Eye Linear模式是把人眼坐标下的顶点左乘OPENGL的MODELVIEW逆矩阵转换到world space中。Eye Linear和Object Linear的最后一项略有不同,Ve-1是Camera视图矩阵的逆矩阵,目的是把人眼坐标下的顶点转换到世界坐标系中(还记得为什么一定要转换到世界坐标中吗?桥梁的作用,前面已经讲过了^_^)。总之,无论何种模式下使用何种方法都需要把物体顶点转换到世界坐标系中,这样才能通过累加Vp(Projector的view matrix)、Pp(Projector的projection matrix)、偏移矩阵得到纹理坐标。

Pointer在其BLOG中对上述问题也有详细的描述[4],有很好的启发作用。值得拜读!

纹理坐标的自动生成大致就如此了,看点代码或许能更好的理解吧!

//----------------------------------------ProjTexture.h------------------------------------

/********************************************************

Usage Instruction:

//in init()

glGenTextures(1, &id);

glBindTexture(GL_TEXTURE_2D, id);

glTexImage2D(GL_TEXTURE_2D, ......, texImage);

ProjectiveTexture lightmap;

lightmap.SetupTexture(id);

lightmap.SetupMatrix(Camera* lightCam);

//in the render pipe loop...

lightmap.SetupMatrix(lightCam);

lightmap.BeginRender();

draw scene...

lightmap.EndRender();

********************************************************/

#ifndef    _PROJTEXTURE_H_

#define    _PROJTEXTURE_H_

#include "stdafx.h"

#include <gl/gl.h>

#include <gl/glu.h>

#include <gl/glext.h>

#include "Camera.h"

class ProjectiveTexture

{

private:

GLuint    textureID;

float        matrix[16];

public:

ProjectiveTexture() {}

virtual ~ProjectiveTexture() { glDeleteTextures(1, &textureID); }

//绑定纹理,设置纹理单元过滤操作、环境应用等参数

void SetupTexture(GLuint id)

{

textureID = id;

glBindTexture(GL_TEXTURE_2D, id);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);

//            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

}

//lightCam是我们定义的一个Camera类,此处代表Projector

void SetupMatrix(Camera* lightCam)

{

glMatrixMode(GL_TEXTURE);

glPushMatrix();

static float biasMatrix[16] = { 0.5, 0.0, 0.0, 0.0,

0.0, 0.5, 0.0, 0.0,

0.0, 0.0, 0.5, 0.0,

0.5, 0.5, 0.5, 1.0 };

static double modelviewMatrix[16];

static double projMatrix[16];

//获得Projector的模型视图矩阵,用于把world space的顶点转换到projector space

lightCam->GetModelViewMatrix(modelviewMatrix);

//获得Projector的投影矩阵,用于把projector space的顶点转换到projector clip space

lightCam->GetProjectionMatrix(projMatrix);

glLoadMatrixf(biasMatrix);

glMultMatrixd(projMatrix);

glMultMatrixd(modelviewMatrix);

glGetFloatv(GL_TEXTURE_MATRIX, matrix);          //获得纹理矩阵

glPopMatrix();

}

void BeginRender()

{

static float planeS[4] = { 1.0f, 0.0f, 0.0f, 0.0f };

static float planeT[4] = { 0.0f, 1.0f, 0.0f, 0.0f };

static float planeR[4] = { 0.0f, 0.0f, 1.0f, 0.0f };

static float planeQ[4] = { 0.0f, 0.0f, 0.0f, 1.0f };

glBindTexture(GL_TEXTURE_2D,textureID);

glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR);

glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR);

glTexGeni(GL_R,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR);

glTexGeni(GL_Q,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR);

glTexGenfv(GL_S,GL_EYE_PLANE,planeS);

glTexGenfv(GL_T,GL_EYE_PLANE,planeT);

glTexGenfv(GL_R,GL_EYE_PLANE,planeR);

glTexGenfv(GL_Q,GL_EYE_PLANE,planeQ);

glEnable(GL_TEXTURE_GEN_S);

glEnable(GL_TEXTURE_GEN_T);

glEnable(GL_TEXTURE_GEN_R);

glEnable(GL_TEXTURE_GEN_Q);

glMatrixMode(GL_TEXTURE);

glLoadMatrixf(matrix);                               // load our texture matrix

//渲染管线就像流水线,顶点是我们的操作对象,何时把相关的操作传入渲染管线,

//何时把不必要的操作卸下是我们该考虑的。物体顶点坐标应该是在模型视图矩阵

//(GL_MODELVIEW)转换到世界坐标,然后进入纹理矩阵模式下求出纹理坐标

glMatrixMode(GL_MODELVIEW);

}

void EndRender()

{

glDisable(GL_TEXTURE_GEN_S);

glDisable(GL_TEXTURE_GEN_T);

glDisable(GL_TEXTURE_GEN_R);

glDisable(GL_TEXTURE_GEN_Q);

}

};

#endif

void ProjectiveTextureViewer::Init()

{

glEnable(GL_CULL_FACE);

glGenTextures(1, &texdecal);

glBindTexture(GL_TEXTURE_2D, texdecal);

read_ppm("Data//decal_image.ppm");

glGenTextures(1, &texspotlight);

glBindTexture(GL_TEXTURE_2D, texspotlight);

read_ppm("Data//spotlight_image.ppm");

……

……

……

pLightMap = new Camera();         //create Projector

lightmap.SetupTexture(texspotlight);     //ProjectiveTexture lightmap

lightmap.SetupMatrix(pLightMap);

}

void ProjectiveTextureViewer::Draw()

{

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glMatrixMode(GL_MODELVIEW);

glEnable(GL_TEXTURE_2D);

//画一个圆球,代表Projector

glPushMatrix();

glMultMatrixd(pLightMap->frame()->matrix());

glColor3f(1.0, 1.0, 0.0);

gluSphere(q, 0.02, 12, 12);

glPopMatrix();

//因为Projector是可以控制的,所以需要实时更新纹理矩阵

lightmap.SetupMatrix(pLightMap);

lightmap.BeginRender();

glMatrixMode(GL_MODELVIEW);

glPushMatrix();

glMultMatrixd(pRoom->matrix());

DrawRoom2();

glPopMatrix();

glPushMatrix();

glMultMatrixd(pObject->matrix());

DrawObject2();

glPopMatrix();

lightmap.EndRender();

}

现在来看看效果图吧:)

黄色小球为Projector(可控),ProjectiveTexture用的是一张笑脸纹理。

上面三副图从不同角度给出了投影纹理的效果图,效果还是可以的。这里没有考虑遮挡的问题,导致墙上的一些纹理本应被立方体阻挡的也渲染出来了,或许获取一个基于Projector的depth map可以解决,这就该是Shadow Mapping了,有待解决^_^,希望高人可以指点一下了!!

又一问题,从这两张图中可以看出Reverse Projection的问题,当Projector出现在两堵墙的同侧,墙上的纹理方向一致,如果Projector出现在两堵墙的中间,则一个沿着projector的视线方向,另一个则为相反方向,通过定义一个裁剪平面能否解决这个问题呢?思考一下……

只是简单实现了一个投影纹理,发现问题还是瞒多的,fighting……

see also:

[1]、http://dev.gameres.com/Program/Visual/3D/ProjectTexture.htm

[2]、http://www.bluevoid.com/opengl/sig00/advanced00/notes/node81.html

[3]、http://www.nvidia.com/object/Projective_Texture_Mapping.html

[4]、http://pointer.cnblogs.com/archive/2004/12/30/84367.html

Projective Texture的原理与实现的更多相关文章

  1. Projective Texture的原理与实现 【转】

              Projective Texture是比较常见的一种技术,实现起来代码也就区区的不过百行,了解其原理及技术细节是我们的重点,知其然,知其所以然.        粗略的说就是想象场景 ...

  2. 投影纹理映射(Projective Texture Mapping)

    摘抄“GPU Programming And Cg Language Primer 1rd Edition” 中文名“GPU编程与CG语言之阳春白雪下里巴人”  投影纹理映射( Projective ...

  3. 投影纹理映射(Projective Texture Mapping) 【转】

    摘抄“GPU Programming And Cg Language Primer 1rd Edition” 中文名“GPU编程与CG语言之阳春白雪下里巴人”  投影纹理映射( Projective ...

  4. OpenGL 4.0 GLSL 实现 投影纹理映射(Projective Texture Mapping) (转)

    http://blog.csdn.net/zhuyingqingfen/article/details/19331721   分类: GLSL  投影纹理映射 (projective texture ...

  5. Projective Texture Mapping - 投影纹理

    昨天导师让写一个投影纹理,将一个相机渲染的图片的一部分投影到另外一个相机里面,目的是无缝的拼接. 投影纹理就和shadow map一样,都是将片元转换到另外一个相机/光源坐标系下,投影后找到对应的纹素 ...

  6. 3DShader之投影贴图(Projective Texturing)

    相信大家都应该玩过CS或者CF吧,游戏里面有个喷图功能,就是按一个T键就能在墙上或者地板上喷出自己预先设定的图案. 而刚好这就是我们这个Shader所需实现的内容.由于没有潜伏者的贴图,我只有从这个图 ...

  7. OpenGL阴影,Shadow Mapping(附源程序)

    实验平台:Win7,VS2010 先上结果截图(文章最后下载程序,解压后直接运行BIN文件夹下的EXE程序): 本文描述图形学的两个最常用的阴影技术之一,Shadow Mapping方法(另一种是Sh ...

  8. shader 4 杂 一些和函数名词、数据结构

    Normal:  法线 Normao mapping: 法线贴图 Lighting mapping: 光照贴图 Bump mapping:     凹凸贴图:模拟粗糙外表面的技术. FX-Water ...

  9. (转)GEM -次表面散射的实时近似

    次表面散射(Subsurface Scattering),简称SSS,或3S,是光射入非金属材质后在内部发生散射, 最后射出物体并进入视野中产生的现象, 即光从表面进入物体经过内部散射,然后又通过物体 ...

随机推荐

  1. yii无限极分类

    /** * 获取菜单Tree * * @return multitype: */ public function getMenuAllList() { $resArr = $this->getT ...

  2. win7 IIS7 PHP环境配置

    PHP5.2.17 官方下载: http://windows.php.net/downloads/releases/php-5.2.17-Win32-VC6-x86.zip PHP5.3.5 官方下载 ...

  3. sourcemap的使用

    minify.bat @echo off if ""%1""=="""" goto end :loop if not e ...

  4. 简单的说说jsonp

    jsonp和json有什么区别?json是一种文件格式,而jsonp是一种技术方法. jsonp会被人认为是一种新的跨域技术,其实本质上和利用带有src属性的标签进行js跨域本质没什么区别. 区别就是 ...

  5. B树、B-树、B+树、B*树---转载

    B树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): 2.所有结点存储一个关键字: 3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树: 如: B ...

  6. The first day of HTML

    这是韩顺平老师的<轻松搞定网页设计(html.css.js)>,讲的还凑合,仅作入门.决定还是做好笔记,记录学习的过程,这是HTML的第一天. HTML(HyperText Mark-up ...

  7. vim使用详解

    1 插入类命令 i               // 在当前字符前插入 I               // 在当前行首插入 a               // 在当前字符后写入 A         ...

  8. MongoDB (三) MongoDB 安装

    MongoDB安装在Windows上 在 Windows上,首先要安装 MongoDB下载最新发布的MongoDB: http://www.mongodb.org/downloads 确保得到正确的版 ...

  9. 模拟+思维 HDOJ 5319 Painter

    题目传送门 /* 题意:刷墙,斜45度刷红色或蓝色,相交的成绿色,每次刷的是连续的一段,知道最终结果,问最少刷几次 模拟+思维:模拟能做,网上有更巧妙地做法,只要前一个不是一样的必然要刷一次,保证是最 ...

  10. ruby 学习 -- string --1

    # define french_string = "il \xc3\xa9tait une fois" long_string = <<EOF Here is a lo ...