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……

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

  1. Projective Texture的原理与实现

    http://blog.csdn.net/xukunn1226/article/details/775644 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. luogu3381 【模板】最小费用最大流

    每次选代价最小的流增广 #include <iostream> #include <cstring> #include <cstdio> #include < ...

  2. stl vector 类

    目录 [-]说明构造方法例子vector 类中定义了4中种构造函数: · 默认构造函数,构造一个初始长度为0的空向量,如:vector<int> v1; · 带有单个整形参数的构造函数,此 ...

  3. 2.新手必须掌握的Linux命令

    第2章 新手必须掌握的Linux命令 章节简述: 本章首先介绍系统内核和Shell终端的关系与作用,然后介绍Bash解释器的4大优势并学习Linux命令的执行方法.经验丰富的运维人员可以通过合理地组合 ...

  4. day04_07 while循环01

    while循环结构: #while 条件: print("any") print("any") 死循环案例 num = 1 while num<=10 : ...

  5. 【NOIP2017】 列队

    线段树博客先开个点随笔.... 这意味着啥呢? 今天绝对要把这道题写出来并且更掉这篇blog!!!! ~ upd:懂了哈哈哈哈哈哈哈 先贴代码 回家+讲解 ---------------------- ...

  6. 聊聊、Java 网络编程

    Socket 编程大家都不陌生,Java 学习中必学的部分,也是 Java网络编程核心内容之一.Java 网络编程又包括 TCP.UDP,URL 等模块.TCP 对应 Socket模块,UDP 对应  ...

  7. [持续集成学习篇]【1】[jenkins安装与配置]

    Guided Tour This guided tour will use the "standalone" Jenkins distribution which requires ...

  8. VirtualBox Host-only Adapter,Failed to create the host-only adapter 转

    不用重装VirtualBox,安装虚拟网卡 今天使用VirtualBox的host-only模式,因为之前把网络连接卸载,这次出现的各种错误. Failed to create the host-on ...

  9. Welcome-to-Swift-04集合类型(Collection Types)

    Swift提供了两种集合类型来存放多个值——数组(Array)和字典(Dictionary).数组把相同类型的值存放在一个有序链表里.字典把相同类型的值存放在一个无序集合里,这些值可以通过唯一标识符( ...

  10. ESXi 给虚拟机添加网络串口

    之前的有点儿小问题,我再更新下: Notice: 要看配没配对,能不能通,得先把虚拟机开开,在关机状态下,这种telnet方式一直是连不通的. 1. 先将ESXi的SSH开启(不知道不开行不行): 2 ...