Direct3D轮回:游戏场景之陆地

继天空效果之后,这一节简单阐述一点地形生成的基本原理和方法~ 如果哪里说的不对,还望园子的前辈们多多拍砖 ^ ^

首先,我们准备两张图:

                 

                    grass.dds                                  heightdata.raw                        

(注:dds为directx支持的特定格式;raw可由photoshop自动生成,在无文件头标准下,其内部仅包含各点颜色值,不再含有其他冗余数据。)

左边这张草地的图我们作基本的地表纹理只用;右边这张怪模怪样的灰度图,其实就是日常大家口中的“高度图”了,我们以其特定点的颜色数据为依据来形成相应顶点的高度,以产生地形的高低错落感~

以下,我们来编写这个基本的地形类——CBaseTerrain:

/*-------------------------------------

代码清单:BaseTerrain.h
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/

#include 
"D3DInit.h"

#pragma once

class CBaseTerrain
{
public:
    CBaseTerrain(
void);
    
~CBaseTerrain(void);
public:
    
bool  Create(                               // 构建地形
         int iSizeX,                            // 原始地形图宽
         int iSizeY,                            // 原始地形图高
         int iRate,                             // 放大倍率
         char* szHeightRaw,                     // 原始地形图
         char* szTexture                        // 地形纹理
         );
    
void  Draw();                               // 绘制地形
    void  Release();                            // 资源释放
    float GetExactHeightAt(float xCoord, float zCoord);                                          // 获得地形高度
private:
    
bool  LoadHightData(int iSizeX, int iSizeY, char* szHeightRaw);                              // 加载地形图
    float GetHeightData(int X, int Y){return (float)m_pHeightData[ m_SizeX * Y + X ];}           // 获得地形图数据
    void  CreateTerrainVertices();                                                               // 产生顶点缓冲
    void  CreateTerrainIndices();                                                                // 产生索引缓冲
    void  GenerateNormals(VertexPosNorColorTex* vertices, int* indices);                         // 计算全部法线数据
private:
    VertexPosNorColorTex
*   m_pVertices;                                                         // 顶点缓冲区
    int*                    m_pIndices;                                                          // 索引缓冲区
    IDirect3DTexture9*      m_pTexture;                                                          // 地形纹理指针
    unsigned char*          m_pHeightData;                                                       // 地形数据缓冲
    int                     m_SizeX;                                                             // 地形原始长
    int                     m_SizeY;                                                             // 地形原始宽
    int                     m_Rate;                                                              // 地形放大倍率
};
BaseTerrain.cpp
/*-------------------------------------

代码清单:BaseTerrain.cpp
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/

#include 
"StdAfx.h"
#include 
"BaseTerrain.h"
#include 
"Ken3DGame.h"
#include 
"D3DCamera.h"
#include 
<fstream>

extern IDirect3DDevice9 *g_pD3DDevice;
extern CD3DCamera       *g_pD3DCamera;

CBaseTerrain::CBaseTerrain(
void) : m_pVertices(NULL),
                                   m_pIndices(NULL),
                                   m_pTexture(NULL),
                                   m_pHeightData(NULL),
                                   m_SizeX(
0),
                                   m_SizeY(
0),
                                   m_Rate(
1)
{
}

CBaseTerrain::
~CBaseTerrain(void)
{
}

void CBaseTerrain::Release()
{
    delete[] m_pHeightData;
    ReleaseCOM(m_pTexture);
    delete[] m_pVertices;
    delete[] m_pIndices;
}

bool CBaseTerrain::Create(int iSizeX, int iSizeY, int iRate, char* szHeightRaw, char* szTexture)
{
    m_Rate 
= iRate;
    
// 加载高度数据
    if(!LoadHightData(iSizeX,iSizeY,szHeightRaw))
        
return false;
    
// 产生顶点缓冲
    CreateTerrainVertices();
    
// 产生索引缓冲
    CreateTerrainIndices();
    
// 计算发现数据
    GenerateNormals(m_pVertices,m_pIndices);
    
// 产生地形纹理
    HRESULT hr = D3DXCreateTextureFromFile(g_pD3DDevice,szTexture,&m_pTexture);
    
if(FAILED(hr))
        
return false;
    
return true;
}

void CBaseTerrain::Draw()
{
    
// 地形绘制
    g_pD3DDevice->SetTexture(0,m_pTexture);
    g_pD3DDevice
->SetFVF(VertexPosNorColorTex::FVF);
    g_pD3DDevice
->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, m_SizeX * m_SizeY, (m_SizeX - 1* (m_SizeY - 1* 2&m_pIndices[0],
        D3DFMT_INDEX32, 
&m_pVertices[0], sizeof(VertexPosNorColorTex));
}

bool CBaseTerrain::LoadHightData(int iSizeX, int iSizeY, char* szHeightRaw)
{
    m_SizeX 
= iSizeX;
    m_SizeY 
= iSizeY;
    m_pHeightData 
= new unsigned char[iSizeX * iSizeY];
    memset(m_pHeightData, 
0, iSizeX * iSizeY);
    
// 从.raw地形图读取高度数据
    std::ifstream inFile(szHeightRaw, std::ios_base::binary);
    
if(!inFile)
    {
        delete[] m_pHeightData;
        
return false;
    }
    inFile.read((
char*)&m_pHeightData[0], iSizeX * iSizeY);
    inFile.close();
    
return true;
}

void CBaseTerrain::CreateTerrainVertices()
{
    
// 产生顶点缓冲
    
// 高度图中有多少数据便产生多少顶点,或者你也可以仅仅使用其中的一部分数据,这个很好理解 :)
    m_pVertices = new VertexPosNorColorTex[m_SizeX * m_SizeY];
    
int i = 0;
    
for (int z = 0; z < m_SizeY; z++)
    {
        
for (int x = 0; x < m_SizeX; x++)
        {
            
float y = GetHeightData(x,z);
            m_pVertices[i
++= VertexPosNorColorTex(
                x 
* m_Rate, y, z * m_Rate,                // 注意x、z坐标m_Rate倍率的使用,相应的y值即为由地形图获得的高度数据
                000
                D3DXCOLOR_WHITE, 
                (
float)x / 30.0f, (float)z / 30.0f);
        }
    }
}

void CBaseTerrain::CreateTerrainIndices()
{
    
// 产生全部的索引数据
    /*
      *---*---*
      | / | / |
      *---*---*  
      | / | / |
      *---*---*

      索引缓冲包含的元素个数 = 三角形个数 x 3
      N 行顶点形成 N-1 行“三角形【对】”,每行三角形对个数为列数 M -1,则总的索引缓冲数 = ( N - 1 ) * ( M - 1 )   *   2   *   3
                                                                                          ---------   ---------      ---     ---
                                                                                          行数 - 1     列数 - 1    三角形对  每个三角形三个顶点
    
*/
    m_pIndices 
= new int[(m_SizeX-1)*(m_SizeY-1)*6];
    
int i = 0;
    
for (int z=0; z<m_SizeY-1; z++)
    {
        
for (int x=0; x<m_SizeX-1; x++)
        {
            
// 分别为每【对】三角形的顶点索引赋值,注意一下不要被“背面剔除”就可以了 ^ ^
            m_pIndices[i++= z * m_SizeX + x;
            m_pIndices[i
++= (z + 1* m_SizeX + x;
            m_pIndices[i
++= z * m_SizeX + x + 1;

            m_pIndices[i
++= (z + 1* m_SizeX + x;
            m_pIndices[i
++= (z + 1* m_SizeX + x + 1;
            m_pIndices[i
++= z * m_SizeX + x + 1;
        }
    }
}

void CBaseTerrain::GenerateNormals(VertexPosNorColorTex* pVertices, int* pIndices)
{
    
// 遍历每个三角形
    for (int i = 0; i < (m_SizeX-1)*(m_SizeY-1)*2; i++
    {
        
// 获得第一条向量
        D3DXVECTOR3 firstVec = D3DXVECTOR3(
            pVertices[pIndices[i
*3 + 1]]._x - pVertices[pIndices[i*3]]._x,
            pVertices[pIndices[i
*3 + 1]]._y - pVertices[pIndices[i*3]]._y,
            pVertices[pIndices[i
*3 + 1]]._z - pVertices[pIndices[i*3]]._z
            );
        
// 获得第二条向量
        D3DXVECTOR3 secondVec = D3DXVECTOR3(
            pVertices[pIndices[i
*3 + 2]]._x - pVertices[pIndices[i*3]]._x,
            pVertices[pIndices[i
*3 + 2]]._y - pVertices[pIndices[i*3]]._y,
            pVertices[pIndices[i
*3 + 2]]._z - pVertices[pIndices[i*3]]._z
            );
        
// 两向量叉乘的该三角形片面的发现数据
        D3DXVECTOR3    normal;
        D3DXVec3Cross(
&normal,&firstVec,&secondVec);
        D3DXVec3Normalize(
&normal,&normal);

        
// 所得法线数据单位化并累计到组成此片面的三个顶点的法线数据中
        D3DXVECTOR3 nfirstVec = D3DXVECTOR3(
            pVertices[pIndices[i 
* 3]]._nx += normal.x,
            pVertices[pIndices[i 
* 3]]._ny += normal.y,
            pVertices[pIndices[i 
* 3]]._nz += normal.z
            );
        D3DXVECTOR3 nsecondVec 
= D3DXVECTOR3(
            pVertices[pIndices[i 
* 3 + 1]]._nx += normal.x,
            pVertices[pIndices[i 
* 3 + 1]]._ny += normal.y,
            pVertices[pIndices[i 
* 3 + 1]]._nz += normal.z
            );
        D3DXVECTOR3 nthirdVec 
= D3DXVECTOR3(
            pVertices[pIndices[i 
* 3 + 2]]._nx += normal.x,
            pVertices[pIndices[i 
* 3 + 2]]._ny += normal.y,
            pVertices[pIndices[i 
* 3 + 2]]._nz += normal.z
            );
    } 

    
// 遍历全部顶点
    for (int i=0;i<m_SizeX * m_SizeY;i++)
    {
        
// 单位化全部顶点的法线数据
        D3DXVECTOR3 nVec;
        D3DXVec3Normalize(
&nVec,
            
&D3DXVECTOR3(pVertices[i]._nx, pVertices[i]._ny, pVertices[i]._nz)
            );
        pVertices[i]._nx 
= nVec.x;
        pVertices[i]._ny 
= nVec.y;
        pVertices[i]._nz 
= nVec.z;
    }
    
// 以上,基本原理即基于所有三角形片面计算全部顶点法线数据而后求平均 :)


float CBaseTerrain::GetExactHeightAt(float xCoord, float zCoord) 
{
    
// 还原倍率
    xCoord /= m_Rate;
    zCoord 
/= m_Rate;

    
// 非法判定
    bool invalid = xCoord < 0
    invalid 
|= zCoord < 0
    invalid 
|= xCoord > m_SizeX - 1
    invalid 
|= zCoord > m_SizeY - 1
    
if (invalid) 
        
return 10;  // 默认高度10

    
// 获得该点所在三角形【对】四点对应高度
    int xLower = (int)xCoord; 
    
int xHigher = xLower + 1
    
float xRelative = (xCoord - xLower) / ((float)xHigher - (float)xLower); 
    
int zLower = (int)zCoord; 
    
int zHigher = zLower + 1
    
float zRelative = (zCoord - zLower) / ((float)zHigher - (float)zLower); 
    
float heightLxLz = GetHeightData(xLower, zLower); 
    
float heightLxHz = GetHeightData(xLower, zHigher); 
    
float heightHxLz = GetHeightData(xHigher, zLower); 
    
float heightHxHz = GetHeightData(xHigher, zHigher); 

    
// 判断该点位于三角形对中的哪个,并根据该三角形三点高度进行双线性插值计算最终高度
    bool pointAboveLowerTriangle = (xRelative + zRelative < 1); 
    
float finalHeight; 
    
if (pointAboveLowerTriangle) 
    { 
        finalHeight 
= heightLxLz; 
        finalHeight 
+= zRelative * (heightLxHz - heightLxLz); 
        finalHeight 
+= xRelative * (heightHxLz - heightLxLz); 
    }
    
else 
    {
        finalHeight 
= heightHxHz; 
        finalHeight 
+= (1.0f - zRelative) * (heightHxLz - heightHxHz); 
        finalHeight 
+= (1.0f - xRelative) * (heightLxHz - heightHxHz); 
    }
    
return finalHeight; 

代码看起来挺长,不过其中并没有什么高深的机制 ^ ^

LoadHightData()用于加载高度图数据。

函数中使用的是最基本的C++读取文件二进制数据的方法,意在将raw存储的高度数据导入到内存的unsigned char型缓冲区中,已供生成顶点高度(也就是y坐标)之用~

CreateTerrainVertices()用于产生全部的顶点数据。

很明显,raw中有多少像素,我们这里便产生多少顶点即可。我在代码中提到的放大倍率,其实就是顶点与顶点间的间隔而已~

CreateTerrainIndices()用于产生全部的索引数据。

我在代码中画的那个简陋的图图不知道大家看懂没有,呵呵~ 简单而言就是索引数据永远是三角形个数的3倍,而三角形对数永远是 (行数-1)*(列数-1)。各索引值取得皆有公式,龙书中有详细的阐述,大家看不懂的话可以留言~

GenerateNormals()用于为所有顶点生成法线数据。

要实现地形的光照效果,顶点法线是必不可少的数据成分。单个顶点的法线其实就是其所在三角形面的法线,如果一个顶点为多个三角形共有,则累加之后单位化即可(相当于求平均)~

GetExactHeightAt()用于求取某点的高度。

高度图中仅存有各顶点的高度数据,而地形中并不是每个点都位于顶点的位置。如果某点位于顶点与顶点之间,则我们需要采取双线性差值的方法求得改点合适的高度,其中的原理跟高中学过的相似三角形类似,挺简单~

有关双线性差值的相关理论,可以参看老师为大家翻译的这篇文章:http://shiba.hpe.sh.cn/jiaoyanzu/WULI/showArticle.aspx?articleId=473&classId=4

之后是主体代码:

D3DGame.cpp
/*-------------------------------------

代码清单:D3DGame.cpp
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/

#include 
"StdAfx.h"
#include 
"D3DGame.h"
#include 
"D3DCamera.h"
#include 
"D3DEffect.h"
#include 
"CoordCross.h"
#include 
"SimpleXMesh.h"
#include 
"Texture2D.h"
#include 
"D3DSprite.h"
#include 
"Skybox.h"
#include 
"SpriteBatch.h"
#include 
"BaseTerrain.h"
#include 
<stdio.h>

//---通用全局变量

HINSTANCE  g_hInst;
HWND       g_hWnd;
D3DXMATRIX g_matProjection;

//---D3D全局变量

IDirect3D9       
*g_pD3D           = NULL;
IDirect3DDevice9 
*g_pD3DDevice     = NULL;
CMouseInput      
*g_pMouseInput    = NULL;
CKeyboardInput   
*g_pKeyboardInput = NULL;
CD3DCamera       
*g_pD3DCamera     = NULL;
CCoordCross      
*g_pCoordCross    = NULL;
CSimpleXMesh     
*g_pSimpleXMesh   = NULL;
CD3DEffect       
*g_pD3DEffect     = NULL;
CD3DSprite       
*g_pD3DSprite     = NULL;
CTexture2D       
*g_pTexture2D     = NULL;
CSpriteBatch     
*g_SpriteBatch    = NULL;
CTexture2D       
*g_pTexture2D2    = NULL;
CD3DEffect       
*g_pD3DEffect2    = NULL;
CSkybox          
*g_pSkybox        = NULL;
CBaseTerrain     
*g_pBaseTerrain   = NULL;


//---HLSL全局变量句柄

D3DXHANDLE   g_CurrentTechHandle 
= NULL;
D3DXHANDLE   g_matWorldViewProj  
= NULL;  
D3DXHANDLE   g_matWorld          
= NULL;
D3DXHANDLE   g_vecEye            
= NULL;
D3DXHANDLE   g_vecLightDir       
= NULL;
D3DXHANDLE   g_vDiffuseColor     
= NULL;
D3DXHANDLE   g_vSpecularColor    
= NULL;
D3DXHANDLE   g_vAmbient          
= NULL;

D3DXHANDLE   g_CurrentTechHandle2 
= NULL;
D3DXHANDLE   g_Scale              
= NULL;

// HLSL特效参数设置
void GetParameters();
void SetParameters();


void Initialize(HINSTANCE hInst, HWND hWnd)
{
    g_hInst 
= hInst;
    g_hWnd  
= hWnd;
    InitD3D(
&g_pD3D, &g_pD3DDevice, g_matProjection, hWnd);
    g_pMouseInput 
= new CMouseInput;
    g_pMouseInput
->Initialize(hInst,hWnd);
    g_pKeyboardInput 
= new CKeyboardInput;
    g_pKeyboardInput
->Initialize(hInst,hWnd);
    g_pD3DCamera 
= new CD3DCamera;
}

void LoadContent()
{
    g_pCoordCross 
= new CCoordCross;

    g_pD3DCamera
->SetCameraPos(D3DXVECTOR3(0.0f,0.0f,0.0f));

    g_pSimpleXMesh 
= new CSimpleXMesh;
    g_pSimpleXMesh
->LoadXMesh("teapot.X");

    g_pD3DEffect 
= new CD3DEffect;
    g_pD3DEffect2 
= new CD3DEffect;
    g_pD3DEffect
->LoadEffect("Light.fx");
    g_pD3DEffect2
->LoadEffect("Thunder.fx");

    GetParameters();

    g_pD3DSprite 
= new CD3DSprite(g_pD3DDevice);
    g_SpriteBatch 
= new CSpriteBatch(g_pD3DDevice);

    g_pTexture2D 
= new CTexture2D;
    g_pTexture2D
->LoadTexture("img.jpg");
    g_pTexture2D2 
= new CTexture2D;
    g_pTexture2D2
->LoadTexture("img2.jpg");

    g_pSkybox 
= new CSkybox;
    g_pSkybox
->Create("Skybox_0.JPG","Skybox_1.JPG","Skybox_2.JPG"
        ,
"Skybox_3.JPG","Skybox_4.JPG","Skybox_5.JPG");

    g_pBaseTerrain 
= new CBaseTerrain;
    g_pBaseTerrain
->Create(128,128,10,"HeightData_128x128.raw","Grass.dds");
}

void Update()
{
    g_pMouseInput
->GetState();
    g_pKeyboardInput
->GetState();
    
// 更新摄影机高度
    D3DXVECTOR3 CameraPos = g_pD3DCamera->GetCameraPos();
    
float roleHeight = 25.0f;
    
float Ty = g_pBaseTerrain->GetExactHeightAt(CameraPos.x,CameraPos.z) + roleHeight;
    g_pD3DCamera
->SetCameraPos(D3DXVECTOR3(
        CameraPos.x,
        Ty,
        CameraPos.z));
    g_pD3DCamera
->Update();
    
// 更新g_pD3DEffect中的视点位置
    D3DXVECTOR4 vecEye = D3DXVECTOR4(CameraPos.x,Ty,CameraPos.z,0.0f);
    g_pD3DEffect 
-> GetEffect() -> SetVector(g_vecEye,&vecEye);
}

void Draw()
{
    
// 参数设定
    SetParameters();
    g_pD3DDevice
->SetTransform(D3DTS_VIEW,&g_pD3DCamera->GetViewMatrix());

    g_pD3DDevice
->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA(100,149,237,255), 1.0f0);
    
if(SUCCEEDED(g_pD3DDevice->BeginScene())) 
    {
        
// 天空处在无限远处,因此必须最先绘制天空盒
        g_pSkybox->Draw();

        UINT numPasses;
        
// 开启特效
        g_pD3DEffect->BeginEffect(numPasses);
        
for(UINT i=0;i<numPasses;i++)
        {
            
// 开启路径
            g_pD3DEffect->GetEffect()->BeginPass(i);
            
// 绘制地形
            g_pBaseTerrain->Draw();
            
// 路径结束
            g_pD3DEffect->GetEffect()->EndPass();
        }
        
// 特效结束
        g_pD3DEffect->EndEffect();
    
        g_pD3DDevice
->EndScene();
    }
    g_pD3DDevice
->Present(NULL, NULL, NULL, NULL);
}

void UnloadContent()
{
    ReleaseCOM(g_pBaseTerrain);
    ReleaseCOM(g_pSkybox);
    ReleaseCOM(g_pTexture2D2);
    ReleaseCOM(g_pTexture2D);
    ReleaseCOM(g_SpriteBatch);
    ReleaseCOM(g_pD3DSprite);
    ReleaseCOM(g_pD3DEffect2);
    ReleaseCOM(g_pD3DEffect);
    ReleaseCOM(g_pSimpleXMesh);
    ReleaseCOM(g_pCoordCross);
}

void Dispose()
{
    ReleaseCOM(g_pD3DCamera);
    ReleaseCOM(g_pKeyboardInput);
    ReleaseCOM(g_pMouseInput);
    ReleaseCOM(g_pD3DDevice);
    ReleaseCOM(g_pD3D);
}

void GetParameters()
{
    
// 获得HLSL中各个全局变量句柄
    g_CurrentTechHandle = g_pD3DEffect -> GetEffect() -> GetTechniqueByName("SpecularLight");
    g_matWorldViewProj  
= g_pD3DEffect -> GetEffect() -> GetParameterByName(0"matWorldViewProj");
    g_matWorld          
= g_pD3DEffect -> GetEffect() -> GetParameterByName(0"matWorld");
    g_vecEye            
= g_pD3DEffect -> GetEffect() -> GetParameterByName(0"vecEye");
    g_vecLightDir       
= g_pD3DEffect -> GetEffect() -> GetParameterByName(0"vecLightDir");
    g_vDiffuseColor     
= g_pD3DEffect -> GetEffect() -> GetParameterByName(0"vDiffuseColor");
    g_vSpecularColor    
= g_pD3DEffect -> GetEffect() -> GetParameterByName(0"vSpecularColor");  
    g_vAmbient          = g_pD3DEffect -> GetEffect() -> GetParameterByName(0"vAmbient");

    g_CurrentTechHandle2 
= g_pD3DEffect2 -> GetEffect() -> GetTechniqueByName("Technique1");
    g_Scale              
= g_pD3DEffect2 -> GetEffect() -> GetParameterByName(0"Scale");
}

void SetParameters()
{
    
// 设定当前技术
    g_pD3DEffect -> GetEffect() -> SetTechnique(g_CurrentTechHandle);
    
// 设定HLSL中的各个参数
    D3DXMATRIX worldMatrix;
    D3DXMatrixTranslation(
&worldMatrix,0.0f,0.0f,0.0f);
    g_pD3DEffect 
-> GetEffect() -> SetMatrix(g_matWorldViewProj,&(worldMatrix*g_pD3DCamera->GetViewMatrix()*g_matProjection));
    g_pD3DEffect 
-> GetEffect() -> SetMatrix(g_matWorld,&worldMatrix);

    D3DXVECTOR4 vLightDirection 
= D3DXVECTOR4(-5.0f-1.0f2.0f1.0f);
    g_pD3DEffect 
-> GetEffect() -> SetVector(g_vecLightDir,&vLightDirection);
    D3DXVECTOR4 vColorDiffuse 
= D3DXVECTOR4(0.0f0.0f0.0f1.0f);
    D3DXVECTOR4 vColorSpecular 
= D3DXVECTOR4(1.0f1.0f1.0f1.0f);
    D3DXVECTOR4 vColorAmbient 
= D3DXVECTOR4(0.0f0.0f0.0f1.0f);
    g_pD3DEffect 
-> GetEffect() -> SetVector(g_vDiffuseColor,&vColorDiffuse);
    // g_pD3DEffect 
-> GetEffect() -> SetVector(g_vSpecularColor,&vColorSpecular); //暂时不加入镜面高光 ^ ^
    g_pD3DEffect 
-> GetEffect() -> SetVector(g_vAmbient,&vColorAmbient);

    g_pD3DEffect2 
-> GetEffect() -> SetTechnique(g_CurrentTechHandle2);
    g_pD3DEffect2 
-> GetEffect() -> SetFloat(g_Scale,0.8f);
}

我们使用light.fx为新生成的地形添加光照效果。这里我在其原有基础上作了改动。

首先添加一个纹理采样器:

sampler2D TextureSampler = sampler_state
{
 magfilter=LINEAR;
 minfilter=LINEAR;
 mipfilter=LINEAR;
};

然后让像素着色器最终的输出结果累计地形纹理原有的颜色:

return vAmbient + vDiffuseColor * Diff + vSpecularColor * Specular + tex2D(TextureSampler, vtop.uv);   //---最后的颜色 = 环境光 + 漫射光 + 镜面高光 + 本来颜色(纹理)

我们来看看效果:

          

山地跟沟壑~ 都是高度图的功劳,呵呵~

主体代码中我注释掉了镜面光的颜色赋值,因为按理说草地是不会反射阳光的。我们不妨换一张冷峻的岩石地表,然后再打开镜面光,试试看效果如何~

怎么样,很耀眼吧?呵呵 ^ ^

到此,以上阐述了地形生成方法中的诸多基本原理,但如果要用于生成大型的地形或者无边界地形的话,仅仅靠这样简单的方法是不堪重负的。不过大家无需担心,地形生成的基本原理大致都是相同的,只不过复杂的大地形在此基础上加入了一些特殊的算法和机制,以动态数据的方法减轻了CPU和显卡的负担。如果后续有机会深度研究这些,我会尽我所能,为大家一一道来~

谢谢~ ^ ^

原文地址:https://www.cnblogs.com/kenkao/p/2114767.html