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. 前端PS切图技巧

    先选择“编辑”-“首选项” 打开,找到“参考线”    设置一下每格网格 100像素 5个细块 确定后 ctrl+‘ 出现网格.(通过网格对齐切图比用参考线切图更好). 如果使用PS cc的软件的话, ...

  2. [Oracle] Lob介绍

    [Oracle] Lob介绍   像Oracle这种关系型数据库,比较擅长处理结构化的数据,那么对于非结构化的数据,Oracle是怎么处理和存储的呢?Lob (Large Object)是Oracle ...

  3. python+selenium面试题

    selenium中如何判断元素是否存在? selenium中没有提供原生的方法判断元素是否存在,一般我们可以通过定位元素+异常捕获的方式判断. # 判断元素是否存在 try: dr.find_elem ...

  4. perl第三章 列表和数组

    访问数组中的元素    $fred[0]   $fred[1] $number=2.75; print $fred[$number-1]  结果就是print $fred[1] 特殊的数组索引1.对索 ...

  5. Leetcode 457.环形数组循环

    环形数组循环 给定一组含有正整数和负整数的数组.如果某个索引中的 n 是正数的,则向前移动 n 个索引.相反,如果是负数(-n),则向后移动 n 个索引. 假设数组首尾相接.判断数组中是否有环.环中至 ...

  6. PHP-7.1 源代码学习:字节码在 Zend 虚拟机中的解释执行 之 概述

    本文简要介绍 zend 虚拟机解释执行字节码的基本逻辑以及相关的数据结构,关于 PHP 源代码的下载,编译,调试可以参考之前的系列文章 execute_ex 我们来看看执行一个简单的脚本 test.p ...

  7. CodeM美团点评编程大赛初赛A轮

    因为语文太差弃赛,第一个追及问题看不懂我就弃赛了.打进复赛确实挺难的,补一下题,锻炼下就行了. 身体训练 时间限制:1秒 空间限制:32768K 美团外卖的配送员用变速跑的方式进行身体训练.他们训练的 ...

  8. C语言总结(1)

    1scanf( )和printf( )属于系统的函数,分别表示输入和输出. 2.所有C语言的程序只有一个main( )函数,从这里开始运行. 3.程序先执行main( ),调用scanf( ),最后输 ...

  9. 九度oj 题目1362:左旋转字符串(Move!Move!!Move!!!)

    题目描述: 汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果.对于一个给定的字符序列S,请你把其循环左移K位后的序列输出.例如,字符序列S=”ab ...

  10. HDU——4162Shape Number(字符串的最小表示)

    Shape Number Time Limit: 24000/12000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...