介绍
球面纹理映射就是将一个平面纹理映射到球面上。见下图。实现球面纹理映射有两种方法,一种是使用顶点的法向量来生成纹理坐标,另一个是使用顶点的位置向量来生成纹理坐标。
使用顶点的法向量生成纹理坐标
分析
问题的本质是根据球面上每个点的法向量坐标生成对应的纹理坐标,请看下图,下图中外部的方框表示二维纹理坐标,其范围是(u,v)min = (0,0), (u,v)max = (1,1),中间的圆形表示球面法向量坐标,其x,y分量的范围是(x,y)min = (-1,-1), (x,y)max = (1,1)。所以问题的本质变成了两组坐标的映射,也即将区间(x,y)min - (x,y)max映射到区间(u,v)min - (u,v)max。这里我们使用反正弦函数y = acrsin(x)来实现。先看一下它的函数图象。
由这个图象知,它的定义域x = (-pi / 2, pi / 2),值域是y = (-1,1)。我们稍作变型,得到下面两个公式。正好完成了由(-1, 1)到(0, 1)的映射。这里tu表示纹理的x坐标,tv表示纹理的y坐标,Nx表示顶点法向量的x轴分量,Ny表示顶点法向量的y轴分量。
tu = arcsin(Nx) / PI + 0.5 tv = arcsin(Ny) / PI + 0.5
代码
具体实现分以下几个步骤- 用D3DXCreateSphere生成球体
- 克隆一份该球体的Mesh
- 对新的Mesh添加纹理坐标
- 绘制新的Mesh
struct VERTEX { D3DXVECTOR3 pos ; // Vertex position D3DXVECTOR3 norm ; // Vertex normal float tu ; // Texture coordinate u float tv ; // Texture coordinate v } ; #define FVF_VERTEX D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1 LPD3DXMESH GenerateSphereMesh(LPDIRECT3DDEVICE9 pDev, float fRadius, UINT slices, UINT stacks) { // Create D3D sphere LPD3DXMESH mesh ; if(FAILED(D3DXCreateSphere(pDev, fRadius, slices, stacks, &mesh, NULL))) { return NULL ; } // Get a copy of the sphere mesh LPD3DXMESH texMesh ; if (FAILED(mesh->CloneMeshFVF(D3DXMESH_SYSTEMMEM, FVF_VERTEX, pDev, &texMesh))) { return NULL ; } // Release original mesh mesh->Release() ; // add texture coordinates VERTEX* pVerts ; if (SUCCEEDED(texMesh->LockVertexBuffer(0, (void **)&pVerts))) { // Get vertex count int numVerts = texMesh->GetNumVertices() ; for (int i = 0; i < numVerts; ++i) { // Calculates texture coordinates pVerts->tu = asinf(pVerts->norm.x) / D3DX_PI + 0.5f ; pVerts->tv = asinf(pVerts->norm.y) / D3DX_PI + 0.5f ; ++pVerts ; } // Unlock the vertex buffer texMesh->UnlockVertexBuffer() ; } return texMesh ; }
在render函数中调用如下代码
if( SUCCEEDED( g_pd3dDevice->BeginScene() ) ) { // Set texture g_pd3dDevice->SetTexture(0, g_pTexture) ; g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE ); // Draw mesh g_pMesh->DrawSubset(0) ; g_pd3dDevice->EndScene(); }
另一种更快的方法是使用下面的公式生成纹理坐标,这里省去了三角函数的计算,用除2代替除pi,速度上快了一些。但是这个方法生成的坐标并不是线性的。
tu = Nx/2 + 0.5 tv = Ny/2 + 0.5
最新评论