文章出處

CSharpGL(43)環境映射(Environment Mapping)-天空盒(Skybox)反射(Reflection)和折射(Refraction)

開始

如圖所示,本文圍繞GLSL里的samplerCube記錄天空盒(Skybox)、反射(reflection)、折射(refraction)的實現。

下載

CSharpGL已在GitHub開源,歡迎對OpenGL有興趣的同學加入(https://github.com/bitzhuwei/CSharpGL

天空盒(Skybox)

samplerCube

想在三維場景里渲染出天空和大地,可以利用一個巨大的立方體,把6個連接的圖片分別貼在立方體的6個面上。

 

這樣的立方體就是所謂的天空盒(Skybox)。

OpenGL提供了一個GL_TEXTURE_CUBE_MAP類型的紋理,其本身就是一個立方體,自帶6個紋理面。它對應的GLSL里的類型就是samplerCube。SamplerCube正適合做天空盒。

 

SkyboxNode

渲染天空盒,實際上就是渲染一個巨大的立方體,并且用samplerCube為其上色。

其vertex shader如下:

 1 #version 330 core
 2 
 3 layout(location = 0) in vec3 inPosition;// 頂點位置
 4 
 5 uniform mat4 mvpMatrix;
 6 
 7 out vec3 texCoord;// 立方體紋理在此頂點處的坐標值
 8 
 9 void main()
10 {
11     vec4 position = mvpMatrix * vec4(inPosition, 1.0); 
12     gl_Position = position.xyww;// 保證天空盒的深度始終為最深
13     texCoord = inPosition;// 立方體紋理的特殊情況
14 }

注意,這里的gl_Position = position.xyww;,是為了保證天空盒的深度始終為最深。這樣就不會遮擋住場景里的其他物體。再注意,texCoord = inPosition;這句,就要求我們的天空盒模型必須是中心在坐標原點的立方體,這樣才能保證inPosition在數值上等于此頂點的紋理坐標值

其fragment shader如下:

 1 #version 330 core
 2 
 3 uniform samplerCube skybox;
 4 
 5 in vec3 texCoord;
 6 
 7 out vec4 color;
 8 
 9 void main()
10 {
11     color = texture(skybox, texCoord);
12 }

極其簡單,就是從skybox紋理中取出對應位置的顏色,寫入Framebuffer。

如果把鏡頭拉遠,你會看到所謂的天空盒是這樣的:一個剔除了正面的立方體 

 

反射(Reflection)

利用samplerCube,可以實現一個反射效果——根據反射原理,把天空盒的紋理貼到模型上,看上去的感覺是,模型像鏡子一樣反射了周圍的東西。此即為環境映射的一種。

 

GLSL自帶了反射函數reflect(,);

實現反射的vertex shader如下:

 1 #version 330 core
 2 
 3 layout (location = 0) in vec3 inPosition;
 4 layout (location = 1) in vec3 inNormal;
 5 
 6 uniform mat4 projection;
 7 uniform mat4 view;
 8 uniform mat4 model;
 9 
10 out vec3 passNormal;
11 out vec3 passPosition;
12 
13 void main()
14 {
15     gl_Position = projection * view * model * vec4(inPosition, 1.0);
16 
17     passNormal = mat3(transpose(inverse(model))) * inNormal;
18     passPosition = vec3(model * vec4(inPosition, 1.0));
19 }

此vertex shader做了3件事:1.給gl_Position賦值。2.傳遞world space里的法線passNormal。3.傳遞world space里的位置passPosition。

下面根據反射原理為模型上色(fragment shader):

 1 #version 330 core
 2 
 3 uniform vec3 cameraPos;
 4 uniform samplerCube skybox;
 5 
 6 in vec3 passNormal;
 7 in vec3 passPosition;
 8 
 9 out vec4 FragColor;
10 
11 void main()
12 {             
13     vec3 I = normalize(passPosition - cameraPos);
14     vec3 R = reflect(I, normalize(passNormal));
15     FragColor = vec4(texture(skybox, R).rgb, 1.0);
16 }

這里利用reflect函數找到反射方向(即紋理坐標),從而找到目標顏色。

 

折射(Refraction)

 

折射與反射類似,也是一種環境映射方式。其vertex shader與反射相同,fragment shader也只有一點點不同:利用GLSL內置的refract()函數找到折射方向。

 1 #version 330 core
 2 
 3 uniform vec3 cameraPos;
 4 uniform samplerCube skybox;
 5 
 6 in vec3 passNormal;
 7 in vec3 passPosition;
 8 
 9 out vec4 FragColor;
10 
11 void main()
12 {             
13     float ratio = 1.00 / 1.52;
14     vec3 I = normalize(passPosition - cameraPos);
15     vec3 R = refract(I, normalize(passNormal), ratio);
16     FragColor = vec4(texture(skybox, R).rgb, 1);
17 }

注意,這里有個ratio是指兩種透明物體的折射率。1.52是玻璃對空氣的折射率。再注意,這里我們只計算了一個面的折射,然而本文的模型有光線的進入和穿出兩次折射。不過一般這樣也沒關系,最終效果還是不錯的。

總結

沒什么可總結的。

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()