openGL CG 系列教程5 – Environment Mapping ( 环境贴图 )

原创 2010年05月08日 13:38:00

*原创教程,转载请注明出处*

openGL CG 系列教程5 – Environment Mapping (环境贴图)

 

利用物体材质specular属性来模拟高反光的物体是不够的。高反光的物体通常可以在表面反射出周围的物体,这样的效果需要通过环境贴图来实现。这篇教程将介绍如何利用Cg进行环境贴图。环境反射的原理很简单,一个光滑的物体表面可以根据我们观察的不同角度反射出不同位置的环境。即物体表面一点反射的颜色和该点的法线,观察视线和反射视线有关系。

 

 

 

Fig1 观察视线和反射

 

Fig1中显示了它们的关系,其中I为观察视线,N为物体上一点p的法线,RI的反射视线。P点的颜色取决于反射视线R,只要R达到周围环境一点p1,那么p1的颜色就会显示到p点上。向量R可以根据公式

 

R = 2(IN)N-I

 

简单计算,或者使用Cg的内置函数reflect(),该函数接受参数向量I和法线N,返回I关于N的反射向量。

 

float3 R = reflect(I,N);

 

有了反射向量R,现在只要创建好环境贴图后,再根据R在环境贴图中查询出对应的颜色即可。环境贴图就是一个每个面都带有贴图的立方体,六个面的贴图刚好可以并接在一起,看起来就像周围环境一样。

 

 

Fig2 环境贴图

 

Fig2展示了一个环境贴图的六个面,在创建环境贴图的时候,只需要将这六个面贴到立方体对应面上即可。要创建环境贴图,可以简单的渲染六个正方形,然后分别贴上不同的贴图。下面是使用环境贴图渲染茶壶的例子。

 

 

Fig3 茶壶的环境贴图

 

OpenGL的扩展glew中提供了对环境贴图的支持,要开启对环境贴图的支持,使用下面代码即可。

 

glEnable(GL_TEXTURE_CUBE_MAP);

 

当然要在使用环境贴图之间,要创建一个环境贴图。下面给出创建环境贴图的代码。

 

void LoadCubeMapFromBMP()

{

      AUX_RGBImageRec *x_pos_map = auxDIBImageLoad("img/pos_x.bmp");

      AUX_RGBImageRec *x_neg_map = auxDIBImageLoad("img/neg_x.bmp");

      AUX_RGBImageRec *y_pos_map = auxDIBImageLoad("img/pos_y.bmp");

      AUX_RGBImageRec *y_neg_map = auxDIBImageLoad("img/neg_y.bmp");

      AUX_RGBImageRec *z_pos_map = auxDIBImageLoad("img/pos_z.bmp");

      AUX_RGBImageRec *z_neg_map = auxDIBImageLoad("img/neg_z.bmp");

 

      glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, 3, x_pos_map->sizeX, x_pos_map->sizeY,

                    0, GL_RGB, GL_UNSIGNED_BYTE, x_pos_map->data);

 

      glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, 3, x_neg_map->sizeX, x_neg_map->sizeY,

           0, GL_RGB, GL_UNSIGNED_BYTE, x_neg_map->data);

 

      glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, 3, y_pos_map->sizeX, y_pos_map->sizeY,

           0, GL_RGB, GL_UNSIGNED_BYTE,  y_pos_map->data);

 

      glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, 3, y_neg_map->sizeX, y_neg_map->sizeY,

           0, GL_RGB, GL_UNSIGNED_BYTE, y_neg_map->data);

 

      glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, 3, z_pos_map->sizeX, z_pos_map->sizeY,

           0, GL_RGB, GL_UNSIGNED_BYTE, z_pos_map->data);

 

      glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, 3, z_neg_map->sizeX, z_neg_map->sizeY,

           0, GL_RGB, GL_UNSIGNED_BYTE, z_neg_map->data);

 

      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

}

 

从代码中可以看到,首先使用glaux扩展读取六个作为环境贴图的纹理数据。使用glTexImage2D创建纹理的时候使用了

 

 GL_TEXTURE_CUBE_MAP_POSITIVE_X

 .

.

.

 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z

 

它们分别表示环境贴图的不同方向的纹理,然后glTexParameteri设置贴图参数的时候使用GL_TEXTURE_CUBE_MAP表示对环境贴图设置参数。创建好环境贴图后,就可以直接在openGL中渲染。

 

glEnable(GL_TEXTURE_CUBE_MAP);

glBindTexture(GL_TEXTURE_CUBE_MAP, ENVIRONMENT_MAP);

glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

     

glBegin(GL_QUADS);

for (int i=0; i<4*6; i++)

{

      glTexCoord3fv(vertex[i]);

      glVertex3fv(vertex[i]);

}

glEnd();

 

glDisable(GL_TEXTURE_CUBE_MAP);

 

上面的代码就可以渲染整个环境贴图了,vertex表示立方体的顶点,它的定义如下。

 

static const GLfloat vertex[4*6][3] = {

      /* Positive X face. */

      { 1, -1, -1 },  { 1, 1, -1 },  { 1, 1, 1 },  { 1, -1, 1 },

      /* Negative X face. */

      { -1, -1, -1 },  { -1, 1, -1 },  { -1, 1, 1 },  { -1, -1, 1 },

      /* Positive Y face. */

      { -1, 1, -1 },  { 1, 1, -1 },  { 1, 1, 1 },  { -1, 1, 1 },

      /* Negative Y face. */

      { -1, -1, -1 },  { 1, -1, -1 },  { 1, -1, 1 },  { -1, -1, 1 },

      /* Positive Z face. */

      { -1, -1, 1 },  { 1, -1, 1 },  { 1, 1, 1 },  { -1, 1, 1 },

      /* Negative Z face. */

      { -1, -1, -1 },  { 1, -1, -1 },  { 1, 1, -1 },  { -1, 1, -1 },

};

 

立方体的六个面这里用了24个顶点来表示,这样可以方便的对每个顶点设置纹理坐标。就像下面的代码:

 

glTexCoord3fv(vertex[i]);

glVertex3fv(vertex[i]);

 

到现在为止,已经做好了渲染环境贴图的准备工作了,下面就是完成shader的内容。利用Cg来进行环境贴图很方便,也很简单。Cg中提供了内置函数来为我们做这些工作,先来看看vertex shader的内容。

 

vs05.cg

void vs_main(   float4 position : POSITION,     // 顶点坐标

                  float2 texCoord : TEXCOORD0,   // 纹理坐标

                  float3 normal   : NORMAL,      // 顶点法线

                  

                  out float4 oPosition : POSITION, 

                  out float2 oTexCoord : TEXCOORD0,

                  out float3 R         : TEXCOORD1,  //反射向量R,保存在纹理坐标中

                  

                  uniform float3   eyePositionW,

                  uniform float4x4 MVP)

{

      oPosition = mul(MVP, position);

      oTexCoord = texCoord;

     

      float3 N = normalize(normal);

      float3 I = position-eyePositionW;  //计算观察向量

 

      R = reflect(I,N);  //利用Cg内置函数计算I关于N的反射向量

}

 

可以看到vertex shader的内容很简单,主要就是计算反射向量R,并将它传到fragment shader 而在fragment shader中,我们要用Cg函数texCUBE(cubemapR),该函数接受两个参数,一个是刚才我们创建的环境贴图纹理数据,一个是反射向量。该函数会自动根据传入的反射向量,然后计算该反射向量和这个环境贴图的交点,并且插值计算出该点对应的像素颜色并返回。整个fragment shader代码如下。

 

05fs.cg

void fs_main(   float2 texCoord : TEXCOORD0,

                  float3 R        : TEXCOORD1,

                  

                  out float4 color :COLOR,

                  

                  uniform float reflectivity,

                  uniform samplerCUBE cubemap)

{

      float4 reflectedColor = texCUBE(cubemap, R);

      color = reflectedColor;

}

 

fragment shader代码很简洁,就是利用函数texCUBE在环境贴图中查询向量R对应的像素颜色。这样就可得到Fig3中的效果。为了真实的模拟反光的物体,有的时候可以对反光的物体再加上纹理,因为在生活中完全反光的物体几乎是没有的,加上其他纹理可以是反光效果真实一些。

 

 

完全反光的茶壶

 

 

带纹理反光的茶壶

Fig4 环境贴图对比

 

要让环境反射的颜色和纹理颜色混合,只需要向alpha混合一样即可。如果反射的颜色为Cr,纹理颜色为Ct,那么混合后的颜色C

 

C = (1 - factor)Ct + factorCr

 

这里factor就是混合系数了。现在只与需要对fragment进行很少的修改就可以实现这个效果。

 

05fs.cg

void fs_main(     float2 texCoord : TEXCOORD0,

                  float3 R        : TEXCOORD1,

                  

                  out float4 color :COLOR,

                  

                  uniform float reflectivity,

                  uniform sampler2D decalMAP, // 只需增加传入一个纹理

                  uniform samplerCUBE cubemap)

{

      float4 reflectedColor = texCUBE(cubemap, R);  //计算反射颜色

      float4 decalColor = tex2D(decalMAP, texCoord); //计算纹理颜色

 

      color = lerp(decalColor, reflectedColor, reflectivity); //混合反射颜色和纹理颜色,reflectivity为混合系数

}

 

这里使用了Cg另一个内置函数lerp,该函数根据传入的混合系数,进行线性插值计算。一般混合系数在01之间。

 

整个环境贴图还是比较简单,也比较容易实现。但是这中环境贴图也有很多不利的地方,比如模拟环境的贴图一般是静态图片,所以环境不能发生变化。还有如果同时渲染多个物体,每个物体不能其他的物体反射到表面,想要达到这种效果要使用ray tracing(光线跟踪)或 render to texture(渲染到纹理)技术。

 

 

 

 

*原创教程,转载请注明出处*

OpenGL学习脚印: 环境纹理映射(environment mapping)

写在前面 上一节初步学习了使用cubeMap创建天空包围盒,本节继续深入Cubemap这个主题,学习环境纹理贴图。本节示例程序均可以从我的github下载。 本节内容整理自: 1....
  • ziyuanxiazai123
  • ziyuanxiazai123
  • 2016年09月16日 21:41
  • 3176

立方体环境贴图(Cube Mapping)之OpenGL原理

立方体纹理是一种特殊的纹理技术,它用6幅二维纹理图像构成一个以原点为中心的纹理立方体。对于每个片段,纹理坐标(s, t, r)被当作方向向量看待,每个纹理单元都表示从原点所看到的纹理立方体上的图像。 ...
  • luofeixiongsix
  • luofeixiongsix
  • 2016年03月19日 21:56
  • 3852

OpenGL天空贴图以及反射纹理映射即镜面反射

编程中一个好的天空贴图会给玩家带来舒适的感觉,还有那对周围物体的反射光映射到球、等物体上或许是一个特别愉悦的事件,然而这在opengl里实现又不太难,请看下面的代码: #include #incl...
  • Liuchuang_MFC
  • Liuchuang_MFC
  • 2015年10月31日 21:15
  • 2136

立方体环境贴图(Cube Mapping)之OpenGL原理

立方体纹理是一种特殊的纹理技术,它用6幅二维纹理图像构成一个以原点为中心的纹理立方体。对于每个片段,纹理坐标(s, t, r)被当作方向向量看待,每个纹理单元都表示从原点所看到的纹理立方体上的图像。 ...
  • luofeixiongsix
  • luofeixiongsix
  • 2016年03月19日 21:56
  • 3852

OpenGL学习脚印: 环境纹理映射(environment mapping)

写在前面 上一节初步学习了使用cubeMap创建天空包围盒,本节继续深入Cubemap这个主题,学习环境纹理贴图。本节示例程序均可以从我的github下载。 本节内容整理自: 1....
  • ziyuanxiazai123
  • ziyuanxiazai123
  • 2016年09月16日 21:41
  • 3176

Html5斜45度地图+3D模型ARPG系列教程(1)-- 搭建开发环境

前言:本来做这个项目,本来是为了趁html5市场火热参与一下(几个月以前写的了),后面由于各种原因没有用上,但是前前后后还是花了一个多月的时间放在硬盘里感觉有点浪费了,所以出个教程帮一下想做Arpg游...
  • leeveel
  • leeveel
  • 2017年05月16日 18:05
  • 613

Hadoop平台搭建使用系列教程(5)- 网络以及初始统一环境配置

雪影工作室版权所有,转载请注明【http://blog.csdn.net/lina791211】  Tips:以下操作完全可以在远程终端(SSH Secure ShellClient)中完成。 ...
  • lina791211
  • lina791211
  • 2013年06月05日 21:46
  • 1172

实时环境映射贴图(Real-time Evironmnet Mapping)

如果你是个赛车游戏的爱好者,你一定玩过风靡全世界的赛车游戏《极品飞车:地下狂飚2》 (Need For Speed : UnderGround2)。在这款游戏中,玩家扮演了一个靠赛车为生的地下飚车手,...
  • rabbit729
  • rabbit729
  • 2011年08月24日 14:51
  • 1855

ASP.NET MVC3 系列教程 – 新的Layout布局系统

I:回忆MVC2当中MasterPage那些事 Code Master head Master1... Master2... Master3... ...
  • xingxing513234072
  • xingxing513234072
  • 2013年06月26日 15:52
  • 2250

Unity3D系列教程–使用免费工具在Unity3D中开发2D游戏 第三节

声明:   本博客文章原创均为个人原创 , 翻译类文章均为个人业余时间翻译,版权所有。转载请注明出处:http://www.wjfxgame.com,另外本人的CSDN博客:http://blog.c...
  • ml3947
  • ml3947
  • 2013年06月02日 16:32
  • 4426
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:openGL CG 系列教程5 – Environment Mapping ( 环境贴图 )
举报原因:
原因补充:

(最多只允许输入30个字)