标签:3d 3d引擎 shader bump mapping
作者:i_dovelemon
日期:2014 / 9 / 7
来源:CSDN博客
主题:Bump Mapping, Tangent Space, Normal Map, Height Map
我们知道,在真实世界中,任何物体的表面都不是非常光滑的,这种粗糙的表面效果,在光源的照射下会有明暗的感觉。那么,如何在3D图形中模拟这种效果了?是通过建模的方法,为模型的表面建造出粗糙的表面?这种方法很难做出真实的粗糙感出来,毕竟表面的粗糙程度是很细微的效果,很难用建模的方式建模出来。所以,只能用其他的方法来代替了。在3D图形学中,有一种称为Bump Mapping的技术,这个技术可以实现模型的表面粗糙感,而不需要额外的为模型增加顶点数据。
想要营造这种明暗效果,很显然,我们需要通过光照计算来模拟。要进行光照计算,自然需要光源和法线。光源,我们可以很简单的使用颜色值,光源位置来定义就可以了。而对于法线,因为我们不是增加顶点,改变模型的方式,所以,仅仅有顶点的法线,然后经过插值很难做出粗糙的感觉。我们需要的是,在这个模型渲染到最后的表面的时候,每一个像素的法线都需要不同,都需要根据不同的明度和暗度来营造。
而每一个像素的法线,如何计算了?想象一下,我们可以为模型的表面贴上一个凹凸不平的贴图,但是这种贴图在光源的效果下,并不会有粗糙材质在光源照耀下的明暗感觉。为了能够让这个贴图有那种凹凸明暗的感觉,我们就需要为每一个像素弄出一个法线出来。这样,随着光源不断的移动,我们对每一个像素进行光照计算,这种,每一个像素的明暗效果就可以出现了。所以剩下的问题就是,如何设计贴图每一个像素的法线,从而来营造出我们想要的凹凸粗糙感了?
在3D图形学中,有一种图,称为Height Map(高度图)的东西。如下所示为一张高度图:
从上面的图中可以看出,高低不平,黑色的地方表示很低,白色的地方表示很高。这种高度图,实际上是一种灰度图,也就是说,它的颜色值中RGB的各个分量实际上都是一样的。
好了,有了这种凹凸的效果,接下来,我们的任务就是根据这张高度图来构建每一个像素法线。由于我们不能在Shader中实时的构建,那样效率太差,所以我们需要预先根据这种图来构建所有纹理像素的法线,由于法线是向量,所以也可以使用三个分量来表示,所以,我们完全可以将法线保存在RGB值中,只不过要将法线各个分量的值缩放到0-255范围之类就可以了。
下面的函数用于将BMP格式的高度图计算为法线,并且存在在另外一个相同尺寸BMP位图中,这张图就称为Normal Map:
<span style="font-family:Microsoft YaHei;">/** * Suppose the texture is 32 Bit format heightmap and needs to be * converted to be a normal map instead. */ HRESULT ZFXD3DSkinManager::convertToNormalmap(ZFXTEXTURE *pTexture) { HRESULT hr= ZFX_OK; D3DLOCKED_RECT d3dRect; D3DSURFACE_DESC desc; // get a dummy pointer LPDIRECT3DTEXTURE9 pTex = ((LPDIRECT3DTEXTURE9)pTexture->pData); pTex->GetLevelDesc(0, &desc); if (FAILED(pTex->LockRect(0, &d3dRect, NULL, 0))) { log("error: cannot lock texture to copy pixels \"%s\"", pTexture->chName); return ZFX_BUFFERLOCK; } // get pointer to pixel data DWORD* pPixel = (DWORD*)d3dRect.pBits; // build normals at each pixel for (DWORD j=0; j<desc.Height; j++) { for (DWORD i=0; i<desc.Width; i++) { DWORD color00 = pPixel[0]; DWORD color10 = pPixel[1]; DWORD color01 = pPixel[d3dRect.Pitch/sizeof(DWORD)]; float fHeight00 = (float)((color00&0x00ff0000)>>16)/255.0f; float fHeight10 = (float)((color10&0x00ff0000)>>16)/255.0f; float fHeight01 = (float)((color01&0x00ff0000)>>16)/255.0f; ZFXVector vcPoint00( i+0.0f, j+0.0f, fHeight00 ); ZFXVector vcPoint10( i+1.0f, j+0.0f, fHeight10 ); ZFXVector vcPoint01( i+0.0f, j+1.0f, fHeight01 ); ZFXVector vc10 = vcPoint10 - vcPoint00; ZFXVector vc01 = vcPoint01 - vcPoint00; ZFXVector vcNormal; vcNormal.cross(vc10, vc01); vcNormal.normalize(); // store normal as RGBA in normalmap *pPixel++ = VectortoRGBA( &vcNormal, fHeight00 ); } } pTex->UnlockRect(0); LPDIRECT3DSURFACE9 pSurface=NULL; pTex->GetSurfaceLevel(0, &pSurface); D3DXSaveSurfaceToFile("normal.bmp", D3DXIFF_BMP, pSurface, NULL, NULL); return ZFX_OK; } // ConvertToNormalmap</span>
下面是VectortoRGBA的函数:
<span style="font-family:Microsoft YaHei;">// encode vector data as RGBA color value for normal map DWORD VectortoRGBA(ZFXVector *vc, float fHeight) { DWORD r = (DWORD)( 127.0f * vc->x + 128.0f ); DWORD g = (DWORD)( 127.0f * vc->y + 128.0f ); DWORD b = (DWORD)( 127.0f * vc->z + 128.0f ); DWORD a = (DWORD)( 255.0f * fHeight ); return( (a<<24L) + (r<<16L) + (g<<8L) + (b<<0L) ); } // VectortoRGBA</span>
这篇文章,暂时简单的讲述了如何根据高度图来构建法线图。在接下来的文章中,将会向大家讲述如何根据这个法线图来进行光照计算,从而达到凹凸明暗的效果。并且在接下来将会讲述在3D图形学中很重要的一个概念:Tangent Space。
标签:3d 3d引擎 shader bump mapping
原文地址:http://blog.csdn.net/i_dovelemon/article/details/39118319