zoukankan      html  css  js  c++  java
  • Directx11学习笔记【十三】 实现一个简单地形

    本文由zhangbaochong原创,转载请注明出处http://www.cnblogs.com/zhangbaochong/p/5510294.html

    上一个教程我们实现了渲染一个会旋转的立方体,这次我们来实现一个简单地形。

    先来看看最终实现效果吧(蓝色是背景色,地形的不同高度分别渲染了不同颜色)

    实现原理其实很简单,我们现在xz平面定义一个二维网格,然后y值可以根据一定的函数得到,比如正余弦函数组成等等,便可以得到一个看似不错的地形

    或者水面效果。

    1.创建二维网格

    首先我们在GeometryGenerator中定义了一个只有XMFLOAT3 Position一个变量的结构Vertex用来描述顶点信息,MeshData中保存了整个grid所有

    顶点的顶点信息和索引信息。CreateGrid函数负责创建grid。

     1 #pragma once
     2 
     3 #include<vector>
     4 #include "Dx11DemoBase.h"
     5 
     6 class GeometryGenerator
     7 {
     8 public:
     9     struct  Vertex 
    10     {
    11         Vertex(){}
    12         Vertex(const XMFLOAT3& p)
    13             : Position(p){}
    14         Vertex(float px, float py, float pz): Position(px, py, pz){}
    15         XMFLOAT3 Position;
    16     };
    17 
    18     struct MeshData
    19     {
    20         std::vector<Vertex> vertices;
    21         std::vector<UINT> indices;
    22     };
    23 
    24     void CreateGrid(float width, float depth, UINT m, UINT n, MeshData& meshData);
    25 };

    顶点索引的计算关键是推导出一个用于求构成第i行,第j列的顶点处右下方两个三角形的顶点索引的通用公式。

    对顶点缓存中的任意一点A,如果该点位于地形中的第i行、第j列的话,那么该点在顶点缓存中所对应的位置应该就是i*m+j(m为每行的顶点数)。如果A点在索引缓存中的位置为k的话,那么A点为起始点构成的三角形ABC中,B、C顶点在顶点缓存中的位置就为(i+1)x m+j和i x m+(j+1)。且B点索引值为k+1,C点索引值为k+2.这样。这样,公式就可以推导为如下:

    三角形ABC=【i*每行顶点数+j,i*每行顶点数+(j+1),(i+1)*行顶点数+j】

    三角形CBD=【(i+1)*每行顶点数+j,i*每行顶点数+(j+1),(i+1)*行顶点数+(j+1)】

     1 #include "GeometryGenerator.h"
     2 
     3 
     4 void GeometryGenerator::CreateGrid(float width, float depth, UINT m, UINT n, MeshData& meshData)
     5 {
     6     UINT vertexCount = m*n;
     7     UINT faceCount = (m - 1)*(n - 1) * 2;
     8 
     9     // Create the vertices.
    10 
    11     float halfWidth = 0.5f*width;
    12     float halfDepth = 0.5f*depth;
    13 
    14     float dx = width / (n - 1);
    15     float dz = depth / (m - 1);
    16 
    17     meshData.vertices.resize(vertexCount);
    18     for (UINT i = 0; i < m; ++i)
    19     {
    20         float z = halfDepth - i*dz;
    21         for (UINT j = 0; j < n; ++j)
    22         {
    23             float x = -halfWidth + j*dx;
    24             meshData.vertices[i*n + j].Position = XMFLOAT3(x, 0.0f, z);
    25         }
    26     }
    27 
    28     // Create the indices.
    29 
    30     meshData.indices.resize(faceCount * 3); // 3 indices per face
    31 
    32     UINT k = 0;
    33     for (UINT i = 0; i < m - 1; ++i)
    34     {
    35         for (UINT j = 0; j < n - 1; ++j)
    36         {
    37             meshData.indices[k] = i*n + j;
    38             meshData.indices[k + 1] = i*n + j + 1;
    39             meshData.indices[k + 2] = (i + 1)*n + j;
    40 
    41             meshData.indices[k + 3] = (i + 1)*n + j;
    42             meshData.indices[k + 4] = i*n + j + 1;
    43             meshData.indices[k + 5] = (i + 1)*n + j + 1;
    44 
    45             k += 6; // next quad
    46         }
    47     }
    48 }

    2.鼠标拖动控制视角

    为了方便观察最后创建出的地形,我们采用鼠标拖动旋转的方式,通过鼠标拖动改变相应的视图矩阵,因此我们Dx11DemoBase中添加了三个函数

    //鼠标事件
    virtual void OnMouseDown(WPARAM btnState, int x, int y){}
    virtual void OnMouseUp(WPARAM btnState, int x, int y){}
    virtual void OnMouseMove(WPARAM btnState, int x, int y){}

    main函数消息循环中添加

     1 case WM_LBUTTONDOWN:
     2     case WM_MBUTTONDOWN:
     3     case WM_RBUTTONDOWN:
     4         demo->OnMouseDown(wParam, GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));
     5         return 0;
     6     case WM_LBUTTONUP:
     7     case WM_MBUTTONUP:
     8     case WM_RBUTTONUP:
     9         demo->OnMouseUp(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
    10         return 0;
    11     case WM_MOUSEMOVE:
    12         demo->OnMouseMove(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));

    具体函数的实现在下面给出

    3.顶点缓冲和索引缓冲的创建

     1     GeometryGenerator::MeshData grid;
     2     GeometryGenerator geoGen;
     3     geoGen.CreateGrid(160.0f, 160.0f, 50, 50, grid);
     4     m_gridIndexCount = grid.indices.size();
     5 
     6     std::vector<Vertex> vertices(grid.vertices.size(),Vertex(XMFLOAT3(0,0,0),XMFLOAT4(0,0,0,0)));
     7     for (UINT i = 0; i < grid.vertices.size(); ++i)
     8     {
     9         XMFLOAT3 p = grid.vertices[i].Position;
    10         p.y = GetHeight(p.x, p.z);
    11 
    12         vertices[i].pos = p;
    13         
    14         //渲染顶点根据高度给出不同颜色
    15         if (p.y < -10.0f)
    16         {
    17             //sandy beach color
    18             vertices[i].color = XMFLOAT4(1.0f, 0.96f, 0.62f, 1.0f);
    19         }
    20         else if (p.y < 5.0f)
    21         {
    22             //light yellow-green color
    23             vertices[i].color = XMFLOAT4(0.48f, 0.77f, 0.46f, 1.0f);
    24         }
    25         else if (p.y < 12.0f)
    26         {
    27             //dark yellow-green color
    28             vertices[i].color = XMFLOAT4(0.1f, 0.48f, 0.19f, 1.0f);
    29         }
    30         else if (p.y < 20.f)
    31         {
    32             //dark brown color
    33             vertices[i].color = XMFLOAT4(0.45f, 0.39f, 0.34f, 1.0f);
    34         }
    35         else
    36         {
    37             //white snow color
    38             vertices[i].color = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
    39         }
    40     }
    41 
    42     D3D11_BUFFER_DESC vertexDesc;
    43     ZeroMemory(&vertexDesc, sizeof(vertexDesc));
    44     vertexDesc.Usage = D3D11_USAGE_IMMUTABLE;
    45     vertexDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    46     vertexDesc.ByteWidth = sizeof(Vertex)* grid.vertices.size();
    47     D3D11_SUBRESOURCE_DATA resourceData;
    48     ZeroMemory(&resourceData, sizeof(resourceData));
    49     resourceData.pSysMem = &vertices[0];
    50     result = m_pd3dDevice->CreateBuffer(&vertexDesc, &resourceData, &m_pVertexBuffer);
    51     if (FAILED(result))
    52     {
    53         return false;
    54     }
    55 
    56 
    57 
    58     D3D11_BUFFER_DESC indexDesc;
    59     ZeroMemory(&indexDesc, sizeof(indexDesc));
    60     indexDesc.Usage = D3D11_USAGE_IMMUTABLE;
    61     indexDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    62     indexDesc.ByteWidth = sizeof(UINT)* m_gridIndexCount;
    63 
    64     D3D11_SUBRESOURCE_DATA indexData;
    65     ZeroMemory(&indexData, sizeof(indexData));
    66     indexData.pSysMem = &grid.indices[0];
    67     result = m_pd3dDevice->CreateBuffer(&indexDesc, &indexData, &m_pIndexBuffer);
    68     if (FAILED(result))
    69     {
    70         return false;
    71     }

    加载shader代码与之前相同,故不再给出

    GetHeight函数根据xz的值得到y的值

    1 float HillsDemo::GetHeight(float x, float z) const
    2 {
    3     return 0.3f*(z*sinf(0.1f*x) + x*cosf(0.1f*z));
    4 }

    下面给出鼠标控制的具体做法:

    定义4了个变量

    1     float m_theta;
    2     float m_phi;
    3     float m_radius;
    4     POINT m_lastMousePos;

    其中m_lastMousePos意思明确很容易理解,那么其他三个分别代表什么意思呢?看下面的图就明白了

    m_radius为半径,m_phi和m_theta为两个角度。

    初始化半径和角度

    1 HillsDemo::HillsDemo()//其他变量初始化省略了
    2 : m_theta(1.5f*XM_PI), m_phi(0.1f*XM_PI), m_radius(200.0f){}

    在Update函数中,给world,view,proj矩阵赋值

     1 void HillsDemo::Update(float dt)
     2 {
     3     float x = m_radius*sinf(m_phi)*cosf(m_theta);
     4     float z = m_radius*sinf(m_phi)*sinf(m_theta);
     5     float y = m_radius*cosf(m_phi);
     6 
     7     XMVECTOR pos = XMVectorSet(x, y, z, 1.0f);
     8     XMVECTOR target = XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f);
     9     XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
    10 
    11     XMMATRIX V = XMMatrixLookAtLH(pos, target, up);
    12     XMStoreFloat4x4(&m_view, V);
    13     XMMATRIX T = XMMatrixPerspectiveFovLH(XM_PIDIV4, m_width / static_cast<float>(m_height),
    14         1.0f, 1000.0f);
    15     XMStoreFloat4x4(&m_proj, T);
    16     
    17 }

    拖动鼠标时适当改变半径和角度的值,便可以间接改变view矩阵,从而改变视角

     1 void HillsDemo::OnMouseDown(WPARAM btnState, int x, int y)
     2 {
     3     m_lastMousePos.x = x;
     4     m_lastMousePos.y = y;
     5     SetCapture(m_hWnd);
     6 }
     7 
     8 void HillsDemo::OnMouseUp(WPARAM btnState, int x, int y)
     9 {
    10     ReleaseCapture();
    11 }
    12 
    13 //限定数值范围
    14 template<typename T>
    15 static T Clamp(const T& x, const T& low, const T& high)
    16 {
    17     return x < low ? low : (x > high ? high : x);
    18 }
    19 
    20 void HillsDemo::OnMouseMove(WPARAM btnState, int x, int y)
    21 {
    22     if ((btnState & MK_LBUTTON) != 0)
    23     {
    24         // Make each pixel correspond to a quarter of a degree.
    25         float dx = XMConvertToRadians(0.25f*static_cast<float>(x - m_lastMousePos.x));
    26         float dy = XMConvertToRadians(0.25f*static_cast<float>(y - m_lastMousePos.y));
    27 
    28         // Update angles based on input to orbit camera around box.
    29         m_theta += dx;
    30         m_phi += dy;
    31 
    32         // Restrict the angle mPhi.
    33         m_phi = Clamp(m_phi, 0.1f, XM_PI - 0.1f);
    34     }
    35     else if ((btnState & MK_RBUTTON) != 0)
    36     {
    37         // Make each pixel correspond to 0.2 unit in the scene.
    38         float dx = 0.2f*static_cast<float>(x - m_lastMousePos.x);
    39         float dy = 0.2f*static_cast<float>(y - m_lastMousePos.y);
    40 
    41         // Update the camera radius based on input.
    42         m_radius += dx - dy;
    43 
    44         // Restrict the radius.
    45         m_radius = Clamp(m_radius, 50.0f, 500.0f);
    46     }
    47 
    48     m_lastMousePos.x = x;
    49     m_lastMousePos.y = y;
    50 }

    4.渲染Render()函数

    因为同之前教程中代码并没有什么改变,所以不再给出了。

    这样运行程序就能得到之前给出图片所示的效果了,是不是很简单呢?

  • 相关阅读:
    OC-数组类
    OC-字符串函数
    C——位操作
    C——字符串练习
    C语言——指针习题
    指针数组和数组指针
    数组指针和指针数组的区别
    cocos2d-iphone中兼容iphone/ipad的问题
    一个Universal程序还是iPhone、iPad俩个版本 ?
    objective-c获取自1970年以来的毫秒数
  • 原文地址:https://www.cnblogs.com/zhangbaochong/p/5510294.html
Copyright © 2011-2022 走看看