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. C++中static的全部作用

    要理解static,就必须要先理解另一个与之相对的关键字,很多人可能都还不知道有这个关键字,那就是auto,其实我们通常声明的不用static修饰的变量,都是auto的,因为它是默认的,就象short ...

  2. eclipse sdk 无法更新

    最近祖国越来越强了,强得android开发工具都没法更新了,但是祖国再怎么强也阻挡不了我开发的脚步.下面给大家分享个更新android sdk 的方法.方法原理就是利用国内镜像源. 工具/原料 电脑一 ...

  3. samsung-smart app 开发

    http://www.samsungdforum.com/ http://seller.samsungapps.com/login/signIn.as?returnURL=%2fmain%2fsell ...

  4. Unity bundle的制作和使用

    原地址:http://unity3d.9ria.com/?p=2863 Unity有个很好的功能,大致是很多专注于PC的engine没有提供的(因为没有必要),就是能加载主包外的资源,这个主包外的资源 ...

  5. Sqli-labs less 26a

    Less-26a 这关与26的区别在于,sql语句添加了一个括号,同时在sql语句执行抛出错误后并不在前台页面输出.所有我们排除报错注入,这里依旧是利用union注入. sql语句为SELECT * ...

  6. A Product Array Puzzle

    Given an array arr[] of n integers, construct a Product Array prod[] (of same size) such that prod[i ...

  7. POJ 2101

    #include <iostream> #include <algorithm> #include <cmath> using namespace std; int ...

  8. Android中如何查看内存(上)

    文章参照自:http://stackoverflow.com/questions/2298208/how-to-discover-memory-usage-of-my-application-in-a ...

  9. JS中的内部类

     js内部类 javascript中本身有提供一些可以直接使用的类,这种类就是内部类.主要有:Object.Array.Math.Boolean.String.Number.Date.RegExp. ...

  10. CSS深入理解之line-height

    以下文字整理自慕课网——张鑫旭的<CSS深入理解之line-height>. line-height,又称行高,指的是两行文字基线之间的距离,又可以称为这行文字所占的高度. 定义三问: 什 ...