openGL CG 系列教程5 – Environment Mapping ( 环境贴图 )
2014-12-25 21:49
926 查看
openGL CG 系列教程5 – Environment Mapping ( 环境贴图 )
分类: openGL CG系列教程2010-05-08 13:38 3676人阅读 评论(2) 收藏 举报floatshaderbytefilter扩展工作
*原创教程,转载请注明出处*
openGL CG 系列教程5 – Environment Mapping (环境贴图)
利用物体材质specular属性来模拟高反光的物体是不够的。高反光的物体通常可以在表面反射出周围的物体,这样的效果需要通过环境贴图来实现。这篇教程将介绍如何利用Cg进行环境贴图。环境反射的原理很简单,一个光滑的物体表面可以根据我们观察的不同角度反射出不同位置的环境。即物体表面一点反射的颜色和该点的法线,观察视线和反射视线有关系。
Fig1 观察视线和反射
Fig1中显示了它们的关系,其中I为观察视线,N为物体上一点p的法线,R为I的反射视线。P点的颜色取决于反射视线R,只要R达到周围环境一点p1,那么p1的颜色就会显示到p点上。向量R可以根据公式
R = 2(I•N)•N-I
简单计算,或者使用Cg的内置函数reflect(),该函数接受参数向量I和法线N,返回I关于N的反射向量。
float3 R = reflect(I,N); |
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); } |
GL_TEXTURE_CUBE_MAP_POSITIVE_X . . . GL_TEXTURE_CUBE_MAP_NEGATIVE_Z |
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); |
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 }, }; |
glTexCoord3fv(vertex[i]); glVertex3fv(vertex[i]); |
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的反射向量 } |
shader。 而在fragment shader中,我们要用Cg函数texCUBE(cubemap,R),该函数接受两个参数,一个是刚才我们创建的环境贴图纹理数据,一个是反射向量。该函数会自动根据传入的反射向量,然后计算该反射向量和这个环境贴图的交点,并且插值计算出该点对应的像素颜色并返回。整个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; } |
完全反光的茶壶 | 带纹理反光的茶壶 |
要让环境反射的颜色和纹理颜色混合,只需要向alpha混合一样即可。如果反射的颜色为Cr,纹理颜色为Ct,那么混合后的颜色C
C = (1 - factor)•Ct + factor•Cr
这里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为混合系数 } |
整个环境贴图还是比较简单,也比较容易实现。但是这中环境贴图也有很多不利的地方,比如模拟环境的贴图一般是静态图片,所以环境不能发生变化。还有如果同时渲染多个物体,每个物体不能其他的物体反射到表面,想要达到这种效果要使用ray tracing(光线跟踪)或
render to texture(渲染到纹理)技术。
相关文章推荐
- openGL CG 系列教程5 – Environment Mapping ( 环境贴图 )
- openGL CG 系列教程
- openGL CG 系列教程2 - Vertex Lighting
- openGL CG 系列教程1 - Hello CG
- openGL CG 系列教程06 – Normal Mapping (法线贴图)
- 【转】OpenGL CG系列教程2,vertex lighting
- openGL CG 系列教程4 - Lighting + Texture
- 【转】OpenGL CG系列教程1 - Hello CG
- OPENGL CG 系列教程1-HelloVCG
- openGL CG5 – Environment Mapping (环境贴图)
- OpenGL CG 系列教程1 - Hello CG
- openGL CG 系列教程3-Pixel Lighting
- openGL CG 系列教程07 – Toon Shader
- citrix环境部署系列教程 (一) 前期环境规划
- 2D游戏引擎Allegro 系列教程(一) 配置allegro开发环境
- NeHe的opengl教程delphi版(6)----纹理映射(贴图)
- OpenGL开发教程之--Windows下环境配置
- Cocos2d-HTML5系列教程[1] 配置开发环境
- SCCM 2007系列教程之四在工作组环境内实现SCCM客户端
- citrix环境部署系列教程 (三)Citrix Licence Server安装