投影紋理映射(Projective Texture Mapping)

轉載:http://blog.csdn.net/xiajun07061225/article/details/7672635

 

一、紋理投影映射簡介

投影紋理映射用於映射一個紋理到物體上,好比將幻燈片投影到牆上一樣。

投影紋理映射經常在一些陰影算法以及體繪製(Volume Rendering)算法中用到。嚴格的說,只要涉及到“紋理實時和空間頂點對應”,通常都要用到投影紋理映射技術。

下面是一個紋理投影映射的實例效果圖:


圖一 紋理投影映射效果圖


二、紋理投影映射優點

1、將紋理與空間頂點實時對應,不需要預先在建模軟件中生成紋理座標。

2、使用投影映射時,可以有效避免紋理扭曲現象。

下面圖顯示了投影紋理映射和普通紋理貼圖的效果對比:


圖二 投影空間插值和真實空間插值對比


三、原理以及實現步驟

爲了把紋理投影到一個表面上,我們所要做的是根據表面點的位置和投影源來確定紋理座標。我們可以把投影源攝像成一個攝像機,位於場景的某處。就像OpenGL中定義一個攝像機一樣,我們這樣的一個座標系統(不放稱之爲投影座標系統):中心點位於投影源所在的位置,視圖矩陣V將座標轉化到投影座標系統,透視投影矩陣P將視景體轉換爲一個大小爲2的立方體,其中心點位於投影座標系統的原點。將這兩個矩陣疊加在一起,再加上縮放和平移的變換矩陣(用於把剛纔得到的大小爲2的立方體變爲大小爲1,而且中心點位於(0.5,0.5,0.5))。我們就得到了下面的變換矩陣:


圖三 投影紋理映射座標變換矩陣

需要注意的是:這樣得到的座標是沒有被歸一化的,在訪問紋理之前需要將各分量除以其w分量。

上述的矩陣P、V均可以通過攝像機的位置、攝影角度求得。


投影紋理映射通常包含了兩個內容:紋理座標被分配到頂點上的方式以及圖元光柵化階段它們的計算方式。我們經常把紋理映射看成將紋理圖像應用到圖元上–事實確實是這樣,但是其中包含着很多數學計算。

光柵化的一些細節

當進行投影紋理映射時,我們使用齊次(homogeneous)紋理座標,或者叫位於投影空間的座標。當進行非投影紋理映射時,我們使用real紋理座標,或者叫位於real space的座標。對於2D紋理投影映射,3分量的齊次座標(s,t,q)被在圖元以及每個片斷上插值,插值後的齊次座標被投影到real 2D紋理座標(s/q,t/q),然後訪問紋理。而對於非投影的2D紋理映射,2分量的real座標(s,t)被在圖元上插值,然後直接用於訪問紋理圖像。

這樣也就導致了前面圖二中的插值效果上的差別。


分配齊次紋理座標

上述關於光柵化的討論基於的一個假設是每個頂點已經被分配了齊次紋理座標。作爲應用程序編寫者,這是我們所要做的工作。下面討論如何在OpenGL中實現它。

設想紋理是從一個投影儀投影到一個場景上的。這個投影儀和攝像機有很多共同屬性:它有視圖變換(將world space 座標變換到projector space或者叫eye space),有投影變換(將projector space視體映射到剪切座標)。最後,我們實施縮放和偏移操作來進行範圍映射。對於攝像機,x和y是依據當前的視口設置進行映射,z是依據當前的深度範圍進行映射。對於投影紋理映射,而範圍映射一般是把每個座標映射到區間[0,1]內。

下圖對比了兩種變換:應用到頂點座標用於計算window space位置座標的變換和計算投影紋理座標的變換。


圖四 應用到world space頂點座標上的用於計算window space座標的攝像機變換和應用到world space頂點座標上用於計算投影紋理座標的投影變換很相似


分配紋理座標很重要的一方面是利用OpenGL的紋理座標生成的功能。它是根據頂點的其他屬性來產生紋理座標。比如GL_OBJECT_LINEAR和GL_EYE_LINEAR,在object space和eyes pace內的頂點座標分別被用來產生紋理座標。另外一些方式使用了其他補貼的屬性。GL_SPEREMAP和GL_REFLECTION_MAP使用了eye space頂點座標和法線。GL_NORMAL_MAP則是根據法線向量產生紋理座標。

OpenGL對於每個座標方向可以使用不同的產生紋理座標的方法。比如你可以在S方向採用GL_SPHERE_MAP,在T上採用GL_REFLECTION_MAP,在R上採用GL_OBJECT_LINEAR,Q上採用GL_EYE_LINEAR。不過這種特性並不是很有用,我們通常採用相同的方式。


下面以一個實際的例子來說明如何實現投影紋理映射(OpenGL+GLSL),其效果及開頭所給出的圖一。圖中繪製了一個茶壺,然後從一個方向將一幅紋理圖像投影到茶壺上面。

頂點着色器projtex.vs:

#version 400

  1. layout (location = 0) in vec3 VertexPosition;  
  2. layout (location = 1) in vec3 VertexNormal;  
  3.   
  4. out vec3 EyeNormal;       // Normal in eye coordinates   
  5. out vec4 EyePosition;     // Position in eye coordinates   
  6. out vec4 ProjTexCoord;  
  7.   
  8. uniform mat4 ProjectorMatrix;//用於計算投影紋理映射的矩陣   
  9.   
  10. uniform vec3 WorldCameraPosition;  
  11. uniform mat4 ModelViewMatrix;//模型視圖矩陣   
  12. uniform mat4 ModelMatrix;//模型變換矩陣   
  13. uniform mat3 NormalMatrix;//法線變換矩陣   
  14. uniform mat4 ProjectionMatrix;//投影變換矩陣   
  15. uniform mat4 MVP;//模型視圖變換矩陣   
  16.   
  17. void main()  
  18. {  
  19.     vec4 pos4 = vec4(VertexPosition,1.0);  
  20.   
  21.     EyeNormal = normalize(NormalMatrix * VertexNormal);  
  22.     EyePosition = ModelViewMatrix * pos4;  
  23.     ProjTexCoord = ProjectorMatrix * (ModelMatrix * pos4);  
  24.     gl_Position = MVP * pos4;  
  25. }  
  1. layout (location = 0) in vec3 VertexPosition;  
  2. layout (location = 1) in vec3 VertexNormal;  
  3.   
  4. out vec3 EyeNormal;       // Normal in eye coordinates  
  5. out vec4 EyePosition;     // Position in eye coordinates  
  6. out vec4 ProjTexCoord;  
  7.   
  8. uniform mat4 ProjectorMatrix;//用於計算投影紋理映射的矩陣  
  9.   
  10. uniform vec3 WorldCameraPosition;  
  11. uniform mat4 ModelViewMatrix;//模型視圖矩陣  
  12. uniform mat4 ModelMatrix;//模型變換矩陣  
  13. uniform mat3 NormalMatrix;//法線變換矩陣  
  14. uniform mat4 ProjectionMatrix;//投影變換矩陣  
  15. uniform mat4 MVP;//模型視圖變換矩陣  
  16.   
  17. void main()  
  18. {  
  19.     vec4 pos4 = vec4(VertexPosition,1.0);  
  20.   
  21.     EyeNormal = normalize(NormalMatrix * VertexNormal);  
  22.     EyePosition = ModelViewMatrix * pos4;  
  23.     ProjTexCoord = ProjectorMatrix * (ModelMatrix * pos4);  
  24.     gl_Position = MVP * pos4;  
  25. }  
layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;

out vec3 EyeNormal;       // Normal in eye coordinates
out vec4 EyePosition;     // Position in eye coordinates
out vec4 ProjTexCoord;

uniform mat4 ProjectorMatrix;//用於計算投影紋理映射的矩陣

uniform vec3 WorldCameraPosition;
uniform mat4 ModelViewMatrix;//模型視圖矩陣
uniform mat4 ModelMatrix;//模型變換矩陣
uniform mat3 NormalMatrix;//法線變換矩陣
uniform mat4 ProjectionMatrix;//投影變換矩陣
uniform mat4 MVP;//模型視圖變換矩陣

void main()
{
    vec4 pos4 = vec4(VertexPosition,1.0);

    EyeNormal = normalize(NormalMatrix * VertexNormal);
    EyePosition = ModelViewMatrix * pos4;
    ProjTexCoord = ProjectorMatrix * (ModelMatrix * pos4);
    gl_Position = MVP * pos4;
}

上面的代碼前兩行是將法線和頂點座標變換到eye space。

然後是計算投影紋理座標:先將位於object space的座標變換到world space,然後通過左乘紋理投影矩陣變換計算出紋理投影座標。

最後計算內置的gl_Position即剪切平面座標。

片斷着色器projtex.fs(下面只列出一部分):

  1. #version 400   
  2.   
  3. in vec3 EyeNormal;       // Normal in eye coordinates   
  4. in vec4 EyePosition;     // Position in eye coordinates   
  5. in vec4 ProjTexCoord;  
  6.   
  7. uniform sampler2D ProjectorTex;  
  8.   
  9. layout( location = 0 ) out vec4 FragColor;  
  10.   
  11. void main() {  
  12.     vec3 color = phongModel(vec3(EyePosition), EyeNormal);  
  13.   
  14.     vec4 projTexColor = vec4(0.0);  
  15.     if( ProjTexCoord.z > 0.0 )  
  16.         //textureProj:紋理座標各分量會除以最後一個分量   
  17.         projTexColor = textureProj( ProjectorTex, ProjTexCoord );  
  18.   
  19.     FragColor = vec4(color,1.0) + projTexColor * 0.5;  
  20. }  
  1. #version 400  
  2.   
  3. in vec3 EyeNormal;       // Normal in eye coordinates  
  4. in vec4 EyePosition;     // Position in eye coordinates  
  5. in vec4 ProjTexCoord;  
  6.   
  7. uniform sampler2D ProjectorTex;  
  8.   
  9. layout( location = 0 ) out vec4 FragColor;  
  10.   
  11. void main() {  
  12.     vec3 color = phongModel(vec3(EyePosition), EyeNormal);  
  13.   
  14.     vec4 projTexColor = vec4(0.0);  
  15.     if( ProjTexCoord.z > 0.0 )  
  16.         //textureProj:紋理座標各分量會除以最後一個分量  
  17.         projTexColor = textureProj( ProjectorTex, ProjTexCoord );  
  18.   
  19.     FragColor = vec4(color,1.0) + projTexColor * 0.5;  
  20. }  
#version 400

in vec3 EyeNormal;       // Normal in eye coordinates
in vec4 EyePosition;     // Position in eye coordinates
in vec4 ProjTexCoord;

uniform sampler2D ProjectorTex;

layout( location = 0 ) out vec4 FragColor;

void main() {
    vec3 color = phongModel(vec3(EyePosition), EyeNormal);

    vec4 projTexColor = vec4(0.0);
    if( ProjTexCoord.z > 0.0 )
        //textureProj:紋理座標各分量會除以最後一個分量
        projTexColor = textureProj( ProjectorTex, ProjTexCoord );

    FragColor = vec4(color,1.0) + projTexColor * 0.5;
}
函數phongModel是計算phong光照模型的。

注意其中的textureProj函數,它專門用於投影紋理訪問的。它的紋理座標的各分量會除以最後一個分量,然後才訪問紋理。

在這個例子中,我們假設投影儀位於(2.0,5.0,5.0),朝向是(-2.0,-4.0,0.0),方向(0.0,1.0,0.0)是向上的。我們採用了一個外部庫GLM庫來根據投影儀的這些信息計算出各個變換矩陣,代碼如下:

  1. vec3 projPos = vec3(2.0f,5.0f,5.0f);  
  2. vec3 projAt = vec3(-2.0f,-4.0f,0.0f);  
  3. vec3 projUp = vec3(0.0f,1.0f,0.0f);  
  4. mat4 projView = glm::lookAt(projPos, projAt, projUp);  
  5. mat4 projProj = glm::perspective(30.0f, 1.0f, 0.2f, 1000.0f);  
  6. mat4 projScaleTrans = glm::translate(vec3(0.5f)) * glm::scale(vec3(0.5f));  
  7. prog.setUniform(“ProjectorMatrix”, projScaleTrans * projProj * projView);  

發佈了23 篇原創文章 · 獲贊 10 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章