六狼论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

新浪微博账号登陆

只需一步,快速开始

搜索
查看: 271|回复: 0

Directx11教程(56) 建立一个skydome

[复制链接]

升级  91.33%

154

主题

154

主题

154

主题

举人

Rank: 3Rank: 3

积分
474
 楼主| 发表于 2012-12-12 00:26:41 | 显示全部楼层 |阅读模式
Directx11教程(56) 建立一个skydome

<div class="postbody"><div id="cnblogs_post_body">      本章建立一个skydome(天空穹),主要学习如何使用cube mapping。
       cube map就是把六张纹理当作一个cube的六个面,而cube的中心,则是坐标轴,而六个面则是垂直于坐标轴某个轴,如下图所示,在cube mapping中,我们不在使用二维纹理坐标,而是用(u,v,w)三维纹理坐标,用这个坐标产生一个查询向量,这个向量和cube 纹理的交点,即为该顶点对应的纹理texel。
  
      
         可以通过微软的Directx Texture tool制作cube map纹理,前提是要准备好6张无缝过度的图片,这可能需要专业的工具。如下图,就是用6张图片生成的cube map纹理图,每章图片对应cube的一个面。
  
       我们建立的skydome是一个球形体,并不是skybox,但使用cube map的方法是一样的。
       首先是建立一个SkyDomeModelClass类,这个类中的顶点结构比较简单,只有一个参数:position,至于skydome贴图用的纹理坐标,我们则是在vs中生成。
  struct VertexType      
    {      
    D3DXVECTOR3 position;

      };
      SkyDome模型是通过SkyDomeModelClass中的函数BuildGeoSphere得到,它通过细分一个20面体,得到一个近似的球形模型,具体的细分算法是把一个三角形细分成四个三角形(取每条边的中点),如下图所示:
  
  
  生成SkyDome模型的代码如下:
  //通过一个20面体细分,近似得到一个球体        
void SkyDomeModelClass::BuildGeoSphere(    int numSubdivisions,    float radius,    VertexList& vertices, IndexList& indices)      
    {      
    // 最小的细分数量.        
    numSubdivisions = min(numSubdivisions, 5);

      const float X = 0.525731f;      
    const float Z = 0.850651f;

      D3DXVECTOR3 pos[12] =      
        {      
        D3DXVECTOR3(-X, 0.0f, Z),  D3DXVECTOR3(X, 0.0f, Z), 
        D3DXVECTOR3(-X, 0.0f, -Z), D3DXVECTOR3(X, 0.0f, -Z),   
        D3DXVECTOR3(0.0f, Z, X),   D3DXVECTOR3(0.0f, Z, -X),      
        D3DXVECTOR3(0.0f, -Z, X),  D3DXVECTOR3(0.0f, -Z, -X),   
        D3DXVECTOR3(Z, X, 0.0f),   D3DXVECTOR3(-Z, X, 0.0f),      
        D3DXVECTOR3(Z, -X, 0.0f),  D3DXVECTOR3(-Z, -X, 0.0f)      
        };

      DWORD k[60] =      
        {      
        1,4,0,  4,9,0,  4,5,9,  8,5,4,  1,8,4,   
        1,10,8, 10,3,8, 8,3,5,  3,2,5,  3,7,2,   
        3,10,7, 10,6,7, 6,11,7, 6,0,11, 6,1,0,      
        10,1,6, 11,0,9, 2,11,9, 5,2,9,  11,2,7      
        };

      vertices.resize(12);      
    indices.resize(60);

      for(int i = 0; i < 12; ++i)      
        vertices = pos;

      for(int i = 0; i < 60; ++i)      
        indices = k;

      for(int i = 0; i < numSubdivisions; ++i)      
        Subdivide(vertices, indices);

      //投影顶点到球面上,然后缩放顶点到球心的距离        
    for(int i = 0; i < vertices.size(); ++i)      
        {      
        D3DXVec3Normalize(&vertices, &vertices);      
        vertices *= radius;      
        }      
    }      
//细分输入三角形,为四个面积相等的三角形        
void SkyDomeModelClass::Subdivide(VertexList& vertices, IndexList& indices)      
    {      
    VertexList vin = vertices;      
    IndexList  iin = indices;

      vertices.resize(0);      
    indices.resize(0);

  
      int numTris = (int)iin.size()/3;      
    for(int i = 0; i < numTris; ++i)      
        {      
        D3DXVECTOR3 v0 = vin[ iin[i*3+0] ];      
        D3DXVECTOR3 v1 = vin[ iin[i*3+1] ];      
        D3DXVECTOR3 v2 = vin[ iin[i*3+2] ];

          D3DXVECTOR3 m0 = 0.5f*(v0 + v1);      
        D3DXVECTOR3 m1 = 0.5f*(v1 + v2);      
        D3DXVECTOR3 m2 = 0.5f*(v0 + v2);

          vertices.push_back(v0); // 0      
        vertices.push_back(v1); // 1      
        vertices.push_back(v2); // 2      
        vertices.push_back(m0); // 3      
        vertices.push_back(m1); // 4      
        vertices.push_back(m2); // 5

          //索引出四个三角形        
        indices.push_back(i*6+0);      

          indices.push_back(i*6+4);      
        }      
    }

       另外我们新建一个SkyDomeShaderClass, 用来渲染SkyDome,它调用的vs,ps shader文件为cubetex.vs, cubetex.ps
       在cubeTex.vs中,我们要注意两点:
  1、设置skydome顶点的世界坐标系z=w,这样,skydome总会在远裁剪平面上。
  2、用顶点local坐标做为cubemap的纹理坐标。
  // set z = w so that z/w = 1 (i.e., skydome always on far plane).        
//设置z=w         
output.position =  output.position.xyww;

  //用local坐标做为cubemap查询向量.      
output.tex = input.position;      

       cubeTex.ps中,我们通过cube sample函数得到cube纹理:
  TextureCube gCubeMap;
  …
  float4 CubePixelShader(PixelInputType input) : SV_TARGET      
{

       return gCubeMap.Sample(SampleType, input.tex);      
}

       在GraphicsClass类中渲染SkyDome时,我们要关掉cull,再就是建立一个skydome专用depthstencil状态,在该状态中深度比较函数是: D3D11_COMPARISON_LESS_EQUAL
  //skyome 顶点和索引数据放入缓冲区,准备渲染        
m_SkydomeModel->Render(m_D3D->GetDeviceContext());

  m_D3D->EnableCubeDepthStencil();      
m_D3D->ChangeNoCullMode(true);

  result = m_SkydomeShader->Render(m_D3D->GetDeviceContext(), m_SkydomeModel->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix,      
    m_TexManager->createCubeTex(m_D3D->GetDevice(),string("grassenvmap1024.dds")));

  if(!result)      
    {      
    return false;      
    }      
m_D3D->ChangeNoCullMode(false);      
m_D3D->EnableDefaultDepthStencil();      

       另外,在textureManagerClass类中,我们增加了函数createCubeTex,专门用来读入cube texture
  ID3D11ShaderResourceView* TexManagerClass::createCubeTex(ID3D11Device* device,string filename)      
    {

     // 如果纹理资源已经存在,则返回,否则创建        
    for(int i = 0; i < m_TextureRVs.size(); ++i)      
        if(! m_TextureNames.compare(filename) )      
            return m_TextureRVs;

       HRESULT result;      
     D3DX11_IMAGE_LOAD_INFO loadInfo;      
     loadInfo.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;

       ID3D11Texture2D* tex = 0;      
     result = D3DX11CreateTextureFromFile(device, stringToLPCWSTR(filename), &loadInfo, 0, (ID3D11Resource**)&tex, 0) ;      

       D3D11_TEXTURE2D_DESC texDesc;      
    tex->GetDesc(&texDesc);

      D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;      
    viewDesc.Format = texDesc.Format;      
    viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;      
    viewDesc.TextureCube.MipLevels = texDesc.MipLevels;      
    viewDesc.TextureCube.MostDetailedMip = 0;

      ID3D11ShaderResourceView* rv = 0;      
    result = device->CreateShaderResourceView(tex, &viewDesc, &rv);      
    if(FAILED(result))      
        {      
        HR(result);      
        return false;      
        }

      m_TextureNames.push_back(filename);      
    m_TextureRVs.push_back(rv);

      return rv;      
    }

  程序执行后界面如下,你可以旋转摄像机,看看能不能超过skydome的包围:     

  完整的代码请参考:
  工程文件myTutorialD3D11_51
  代码下载:
  http://files.cnblogs.com/mikewolf2002/d3d1150-58.zip
  http://files.cnblogs.com/mikewolf2002/pictures.zip
您需要登录后才可以回帖 登录 | 立即注册 新浪微博账号登陆

本版积分规则

快速回复 返回顶部 返回列表