creator's profileCreator_Chen's dream isl...BlogListsNetwork Tools Help

creator

Occupation
Location
Interests

Windows Media Player

Creator_Chen's dream island

March 24

获取地形上某一点的高度

  • 基本思想

在初始化地形时计算并保存地形上每个三角形所在平面方程的系数和常数项。此时,给定地形上某个点的x坐标和z坐标,就可以把它们带入该点所在三角形的平面方程来得到该点的高度值了。

  • 三角形所在平面方程的系数和常数项计算过程

首先,我们要有一个数据结构来存储每个平面方程的系数和常数项,如下所示:

struct  COEFFICIENT

{

float  a1, b1, c1, d1; 

float  a2, b2, c2, d2;

};

COEFFICIENT  Coefficient[Height - 1][Width - 1];

D3DXVECTOR3  TerrainVertices[Height * Width];

COEFFICIENT结构体用来存放组成一个地形网格的两个三角形的平面信息。其中,a1, b1, c1, d1为第一个三角形所在平面方程的系数和常数项,a2, b2, c2, d2为第二个三角形所在平面方程的系数和常数项。假设地形宽和高方向上的顶点数分别为WidthHeight,则需要(Height - 1)*(Width - 1)COEFFICIENT结构体来存储所有三角形的平面信息。此外,还需要一个3维向量数组来存放每个地形顶点的坐标,在计算平面方程系数和常数项时会用到这些顶点。就像把顶点信息写入顶点缓冲区一样,按照从左到右、从上到下的顺序把顶点坐标写入TerrainVertices[Height * Width]数组,这个工作可以在创建顶点缓冲区时同步完成。然后就可以在计算顶点索引时顺便计算各个平面的系数和常数项了:) 

计算平面系数和常数项的代码如下:

//平面系数和常数项的计算与顶点索引的计算同步完成

//假设pIndexBuffer为索引缓冲区的首地址,j,i为当前地形网格的行列号

for( int  j = 0; j < ( Height - 1 ); j++ )

{

for( int  i = 0; i < ( Height - 1 ); i++ )

{

//当前网格左上角顶点索引

DWORD  dwLeftTop = j * Width + i;

//将组成当前网格的两个三角形的顶点索引写入索引缓冲区

//左上角、右上角、左下角组成第一个三角形

//右上角、右下角、左下角组成第二个三角形

( * pIndexBuffer ) = dwLeftTop;

pIndexBuffer++;

( * pIndexBuffer ) = dwLeftTop + 1;

pIndexBuffer++;

( * pIndexBuffer ) = dwLeftTop + Width;

pIndexBuffer++;

( * pIndexBuffer ) = dwLeftTop + 1;

pIndexBuffer++;

( * pIndexBuffer ) = dwLeftTop + Width + 1;

pIndexBuffer++;

( * pIndexBuffer ) = dwLeftTop + Width;

pIndexBuffer++;

//设当前网格左上角,右上角,右下角,左下角分别为v0, v1, v2, v3

//计算组成当前网格的两个三角形的法线向量n1, n2

D3DXVECTOR3  v0, v1, v2, v3, n1, n2;

v0 = TerrainVertices[dwLeftTop];

v1 = TerrainVertices[dwLeftTop + 1];

v2 = TerrainVertices[dwLeftTop + Width + 1];

v3 = TerrainVertices[dwLeftTop + Width];

D3DXVec3Cross( &n1, &D3DXVECTOR3( v1.x - v0.x, v1.y - v0.y, v1.z - v0.z ), &D3DXVECTOR3( v3.x - v0.x, v3.y - v0.y, v3.z - v0. z ) );

D3DXVec3Cross( &n2, &D3DXVECTOR3( v2.x - v1.x, v2.y - v1.y, v2.z - v1.z ), &D3DXVECTOR3( v3.x - v1.x, v3.y - v1.y, v3.z - v1.z ) );

//计算组成当前网格的两个三角形所在平面方程的系数和常数项

Coefficient[j][i].a1 = n1.x;

Coefficient[j][i].b1 = n1.y;

Coefficient[j][i].c1 = n1.z;

Coefficient[j][i].d1 = - ( n1.x * v0.x + n1.y * v0.y + n1.z * v0.z );

Coefficient[j][i].a2 = n2.x;

Coefficient[j][i].b2 = n2.y;

Coefficient[j][i].c2 = n2.z;

Coefficient[j][i].d2 = - ( n2.x * v2.x + n2.y * v2.y + n2.z * v2.z ); 

}//end  for  i

}//end  for  j

  • 根据地形上某一点的x坐标和z坐标来计算该点的地形高度

首先,我们要计算出当前点所在的地形网格的行列号。由于地形网格的行列号是从0开始从左到右,从上到下递增的,所以要先把坐标( x, z )由图(1)所示的坐标系变换到图(2)所示的坐标系

 

变换公式的推导:

设图(1)所示的坐标系中有一点p( x, z ),图(2)所示的坐标系中有一点p0( x0, z0 )与之对应。设地形网格的边长为Span,则x0, x, z0, z的变化范围分别为:

x0 : 0  ~  ( Width - 1 ) * Span

x : - Width / 2 * Span  ~  - Width / 2 * Span + ( Width - 1 ) * Span

z0 : 0  ~  ( Height - 1 ) * Span

z : Height / 2 * Span  ~  Height / 2 * Span - ( Height - 1 ) * Span

稍微对比一下就可以很容易的得出坐标( x, z )与坐标( x0, z0 )之间的关系:

x0 = x + Width / 2 * Span;

z0 = - z + Height / 2 * Span;

设当前点所在地形网格的行列号为j, i,则:

j = ( int )( z0 / Span );

i = ( int )( x0 / Span );

有了地形网格的行列号,我们就可以用它来查询该地形网格所包含的两个三角形的平面信息了,这两个三角形的平面信息就存放在Coefficient[j][i]中。但是现在有一个小问题,现在有两个三角形,当前点位于哪个三角形中呢?可以用下面的方法来判断,如图(3)所示

算出当前点px坐标和它所在的地形网格左上角v0x坐标的差值dx,以及当前点pz坐标和v0z坐标的差值dz。如下:

dx = x0 - i * Span;

dz = z0 - j * Span;

dz < Span - dx,则点p位于三角形v0v1v3中,否则,点p位于三角形v1v2v3中。一旦确定点p位于哪个三角形中,就可以将点p的坐标( x, z )带入其所在三角形的平面方程来得到它的高度了。代码如下:

float  Height;

if( dz < ( Span - dx ) )

Height = - ( Coefficient[j][i].a1 * x + Coefficient[j][i].c1 * z + Coefficient[j][i].d1 ) / Coefficient[j][i].b1;

else

Height = - ( Coefficient[j][i].a2 * x + Coefficient[j][i].c2 * z + Coefficient[j][i].d2 ) / Coefficient[j][i].b2;

March 22

HLSL实现基本光照模型

  • 基本光照公式

物体上某一点受到光照后的颜色由下列公式来计算:

FinalColor = Emissive + Ambient + diffuse + Specular

光照的最终颜色是发射光、环境光、漫反射光和镜面反射光综合作用的结果。其中:

Emissive = Ie;

Ambient = Ia * Ka;

Diffuse   = Id * Ka * ( L . N );

Specular = Is * Ks * ( R . V ) ^ Ns;

以上等式中Ix为各种光线的强度,是一个RGB颜色值。Kx是一个取值区间为[0,1]的系数。L为从被照射点指向光源的单位向量。N为被照射点法线向量的单位向量。R为光线向量的反射向量。V从被照射点指向视点的单位向量。Ns是一个取值区间为[0,100]的镜面反射指数,可以控制镜面反射所产生的光斑大小。

  • HLSL代码
    • VertexShader

float4x4  matWorldViewProjection;

float4x4  matWorld;

float3    fvLightPosition;

float3    fvViewPoint;

struct  VS_INPUT

{

float4  Position : POSITION0;

float3  Normal : NORMAL0;

};

struct  VS_OUTPUT

{

float4  Position : POSITION0;

float3  Normal : TEXCOORD0;

float3  Light :    TEXCOORD1;

float3  View :    TEXCOORD2;

};

VS_OUTPUT  vs_main(  VS_INPUT  Input  )

{

VS_OUTPUT Output;

Output.Position    =  mul(  Input.Position, matWorldViewProjection  );

Output.Normal     =  mul(  Input.Normal, matWorld  ); 

float4  fvPosition  =  mul(  Input.Position, matWorld  );

Output.Light        =  mul(  fvLightPosition, matWorld  ) - fvPosition;

Output.View        =  mul(  fvViewPoint, matWorld  ) - fvPosition;

return Output;

}

    • PixelShader

float  fKa;

float  fKd;

float  fKs;

float  fNs;

float4  fvIa;

float4  fvId;

float4  fvIs;

struct  PS_INPUT

{

float3  Normal : TEXCOORD0;

float3  Light :    TEXCOORD1;

float3  View :    TEXCOORD2;

};

float4  ps_main(  PS_INPUT  Input  ) : COLOR0

{

float3  fvNormal      =  normalize(  Input.Normal  );

float3  fvLight         =  normalize(  Input.Light  );

float3  fvView         =  normalize(  Input.View  );

float3  fvReflection  =  normalize(  2.0f * dot(  fvLight, fvNormal  ) * fvNormal - fvLight  );

float4  fvAmbientColor  =  fvIa * fvKa;

float4  fvDiffuseColor    =  fvId * fvKd * max(  dot(  fvLight, fvNormal  ), 0.0f  );

float4  fvSpecularColor  =  fvIs * fvKs * pow(  max(  dot(  fvReflection, fvView  ), 0.0f  ), fNs  );

return  fvAmbientColor + fvDiffuseColor + fvSpecularColor;

}

  • Demo截图

 

 
No list items have been added yet.