如何计算投影纹理坐标

Project Texture是Shadow Mapping中的重要一环。所以要先搞定Project Texture才行。:)

Project Texture是一种很常见的特效了,在OGL中Project Texture的通常做法是:设置好纹理矩阵,然后让OGL自动生成纹理坐标。
这样做虽然也能得到很好的效果,但是本着知其然,知其所以然的精神,还是让我们自己来计算一下纹理坐标吧。

Project Texture的原理我就不介绍了,不明白的话可以参阅心蓝兄翻译的一篇文章:http://xreal.51.net/Game/ProjectTexture.htm

为了使过程更明了,我们将不采用OGL函数,而是自己通过矩阵运算来得到我们需要的纹理坐标。计算中所需要的各种矩阵用如下的变量表示:

None.gif CMatrix4x4 matrixModel;              //  模型的模型视图变换矩阵
None.gif
CMatrix4x4 matrixLight;              //  投影仪的模型视图变换矩阵
None.gif
CMatrix4x4 matrixProjModel;          //  模型的投影变换矩阵
None.gif
CMatrix4x4 matrixProjTexture;        //  纹理的投影变换矩阵
None.gif
CMatrix4x4 matrixBiasTexture;        //  纹理偏移矩阵矩阵(使纹理坐标变换到[0,1]内)
None.gif
CMatrix4x4 matrixTexture;            //  纹理矩阵


因为矩阵变换的累加性,所以实际上只需要用两个矩阵不断的累加操作就可以了。这里拆分开是为了使过程更加明了。

第一步:初始化。
我们假设在渲染过程中摄像机和投影仪的投影方式不发生变化,所以在初始化函数中我们将一次性的计算好摄像机和投影仪的投影矩阵。

ExpandedBlockStart.gif ContractedBlock.gif Init() dot.gif {
InBlock.gif    
// dot.gif
InBlock.gif    
InBlock.gif    
// 将纹理矩阵、投影矩阵与模型视图矩阵置为单位矩阵,
InBlock.gif    
// 因为以下变换将全部由自定义矩阵运算实现。
InBlock.gif
    glMatrixMode(GL_TEXTURE);
InBlock.gif    glLoadIdentity();
InBlock.gif    glMatrixMode(GL_PROJECTION);
InBlock.gif    glLoadIdentity();
InBlock.gif    glMatrixMode(GL_MODELVIEW);
InBlock.gif    glLoadIdentity();
InBlock.gif    
InBlock.gif    
// 初始化为单位矩阵
InBlock.gif
    matrixProjModel.LoadIdentity();
InBlock.gif    matrixProjTexture.LoadIdentity();
InBlock.gif    matrixBiasTexture.LoadIdentity();
InBlock.gif    
InBlock.gif    
// 设置投影矩阵
InBlock.gif
    float fBase = 0.05f;
InBlock.gif    
float fZoom = (float)SCREEN_HEIGHT / (float)SCREEN_WIDTH;
InBlock.gif    matrixProjModel.Frustum(
-fBase, fBase, fZoom * -fBase, fZoom * fBase, 0.1f20.0f);
InBlock.gif    
InBlock.gif    
// 设置纹理投影矩阵
InBlock.gif
    matrixProjTexture.Frustum(-0.02f0.02f-0.02f0.02f0.1f2.0f);
InBlock.gif
InBlock.gif    
// 设置纹理偏移矩阵
InBlock.gif
    matrixBiasTexture.Translate(0.5f0.5f0.0f);
InBlock.gif    matrixBiasTexture.Scale(
0.5f0.5f1.0f);
InBlock.gif    
InBlock.gif    
// dot.gif
ExpandedBlockEnd.gif
}


这里所进行的操作和

None.gif glMatrixMode(GL_PROJECTION);
None.gifglLoadIdentity();
None.gifglFrustum( dot.gif );


是一样的。

在设置投影矩阵之前我们将OGL内置的纹理矩阵、投影矩阵与模型视图矩阵置为单位矩阵,因为这些变换将由我们自己全盘实现。另一种做法是自己编写Vertex Shader,并且在Shader中不进行任何变换。:)
接着我们设置了模型的投影矩阵,纹理的投影矩阵与纹理的偏移矩阵。纹理偏移矩阵的作用我们将在后面说明。



第二步,渲染。

None.gif
None.gif
ExpandedBlockStart.gifContractedBlock.gifDisplay()
dot.gif {
InBlock.gif    
// dot.gif
InBlock.gif    
InBlock.gif    
// 我们不希望由矩阵累加变换,
InBlock.gif    
// 累加操作将由一些存储输入信息的变量来实现。
InBlock.gif
    matrixModel.LoadIdentity();
InBlock.gif    matrixLight.LoadIdentity();
InBlock.gif    matrixTexture.LoadIdentity();
InBlock.gif    
ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//* 模型的模型视图变换 */
InBlock.gif    
// 先将模型(或摄像机)移动到一个位置
InBlock.gif
    matrixModel.Translate(0.0f0.0f-2.0f);
InBlock.gif    matrixModel.Rotate(
30.0f1.0f0.0f0.0f);
InBlock.gif    
// 然后根据键盘操作来做平移、旋转变换
InBlock.gif    
// inputView[0~2]中分别存储的是绕X、Y、Z轴进行旋转的角度
InBlock.gif    
// inputView[3]中存储的是向Z轴方向进行平移的距离
InBlock.gif
    matrixModel.Translate(0.0f0.0f, inputView[3]);
InBlock.gif    matrixModel.Rotate(inputView[
0], 1.0f0.0f0.0f);
InBlock.gif    matrixModel.Rotate(inputView[
1], 0.0f1.0f0.0f);
InBlock.gif    matrixModel.Rotate(inputView[
2], 0.0f0.0f1.0f);
InBlock.gif    
ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//* 投影点的模型视图变换 */
InBlock.gif    
// 在模型变换的基础上进行投影仪变换
InBlock.gif
    matrixLight = matrixModel;
InBlock.gif    
// 和模型变换一样,inputLight中存储的是旋转与平移的信息
InBlock.gif
    matrixLight.Translate(0.0f0.0f2.0f);
InBlock.gif    matrixLight.Translate(
0.0f0.0f, inputLight[3]);
InBlock.gif    matrixLight.Rotate(inputLight[
0], 1.0f0.0f0.0f);
InBlock.gif    matrixLight.Rotate(inputLight[
1], 0.0f1.0f0.0f);
InBlock.gif    matrixLight.Rotate(inputLight[
2], 0.0f0.0f1.0f);
InBlock.gif    
ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//* 获得纹理矩阵 */
InBlock.gif    
// 投影仪矩阵求逆
InBlock.gif
    matrixTexture = matrixLight.GetInverse();
InBlock.gif    
// 左乘纹理投影矩阵
InBlock.gif
    matrixTexture = matrixProjTexture * matrixTexture;
InBlock.gif    
// 左乘偏移矩阵
InBlock.gif
    matrixTexture = matrixBiasTexture* matrixTexture;
InBlock.gif    
ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//* 画模型 */
InBlock.gif    glEnable(GL_TEXTURE_2D);
InBlock.gif    glBegin(GL_QUADS);
InBlock.gif    glColor3f(
0.80.80.8);
InBlock.gif    
// CubeModel[24][4]中存储的是一个Cube模型的24个顶点坐标
ExpandedSubBlockStart.gifContractedSubBlock.gif
    for (int i = 0; i < 24; i ++)dot.gif{
InBlock.gif        
// vertex_tmp中存储的是模型经过模型视图变换但是未经投影变换的坐标
InBlock.gif        
// vertex中存储的是模型经过模型视图与投影变换后的坐标
InBlock.gif        
// texture中存储的是我们计算出来的纹理坐标
InBlock.gif
        GLfloat vertex_tmp[4], vertex[4], texture[4];
InBlock.gif        
// 模型的模型视图矩阵乘以模型坐标,将模型坐标进行模型视图变换
InBlock.gif
        mult_matrix_vector(matrixModel, CubeModel[i], vertex_tmp);
InBlock.gif        
// 模型的投影矩阵乘以模型经过模型视图变换后的坐标,将模型坐标进行投影变换
InBlock.gif
        mult_matrix_vector(matrixProjModel, vertex_tmp, vertex);
InBlock.gif
InBlock.gif        
// 模型经过模型视图变换后的顶点坐标乘以纹理矩阵,获得纹理坐标
InBlock.gif
        mult_matrix_vector(matrixTexture, vertex_tmp, texture);
InBlock.gif        
InBlock.gif        
// 设置模型的纹理坐标与顶点坐标
InBlock.gif
        glTexCoord2f(texture[0], texture[1]);
InBlock.gif        glVertex3f(vertex[
0], vertex[1], vertex[2]);
ExpandedSubBlockEnd.gif    }

InBlock.gif    glEnd();
InBlock.gif    glDisable(GL_TEXTURE_2D);
InBlock.gif    
InBlock.gif    
// dot.gif
ExpandedBlockEnd.gif
}


这是Project Texture的核心步骤。
我们先像通常一样分别获得模型(或摄像机)与投影仪的模型视图变换矩阵,然后对模型坐标应用该矩阵,获得经过模型变换后的坐标,最后进行投影变换。这里需要注意的一点是:我们直接进行了顶点的模型视图变换与投影变换,而通常这个变换是在Vertex Shader中进行的,所以在Vertex Shader中将不再需要任何变换,只需要写上

ExpandedBlockStart.gif ContractedBlock.gif void  main( void ) dot.gif {
InBlock.gif    gl_Position 
= gl_Vertex;
ExpandedBlockEnd.gif}


就可以了。


接下来进行的是我们最为关心的纹理坐标计算。在这里我累加了矩阵操作,我们把它拆分开看看:
1 经过模型视图变换后的顶点坐标左乘投影仪的逆矩阵。这里的几何意义可以这样理解:根据顶点坐标获得纹理坐标的本质是将顶点坐标投影到NDC平面上,此时投影点的平面坐标即为纹理坐标。而Frustum操作是朝向投影仪坐标系中Z轴负方向进行的,所以需要先将模型坐标变换到投影仪坐标系中。这时用经过模型视图变换后的顶点坐标左乘投影仪模型视图变换矩阵的逆矩阵即可。
2 如上文中所说,将模型坐标变换到投影仪坐标系中后,就可以进行投影变换了。此时只需直接左乘纹理的投影矩阵。
3 到这里我们已经得到了纹理坐标,不过该坐标却是在[-1, 1]范围内的,我们需要将其变换到[0, 1]范围内,可以先乘以1/2然后再给每个分量加上1/2。对此我们将结果左乘纹理偏移矩阵做最后的调整。
 
最后是对模型的顶点坐标应用模型视图变换矩阵、投影变换矩阵与投影纹理矩阵。代码里注释的很详细,这里就不多说了。

OK,到此为止所有的工作都做完了,最后让我们来看看结果吧。:)

Project%20Texture%2001.jpg

 

最后的一点疑惑

实际上最后得到的结果和使用自动纹理坐标生成得到的结果还是有一些差异的,对比下图:

Project%20Texture%20atcg.JPG
自动生成的纹理坐标

Project%20Texture%20error1.JPG
通过上述方法计算出的纹理坐标

可以清楚的看到使用上述方法得到的投影有所扭曲,而使用自动纹理坐标生成得到的结果则是正常的。

因为我采用OGL纹理坐标自动生成时传入的GL_TEXTURE矩阵也是上面计算出的matrixTexture,所以应该不是matrixTexture计算错误。那么难道是我在最终生成纹理坐标的时候少做了某些操作?

Project%20Texture%20error2.JPG

这个问题一直没有想明白,希望知道答案的高人能够指点一下。这里谢谢先了。:)

转载于:https://www.cnblogs.com/Pointer/archive/2004/12/20/79323.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值