原文:Directx11教程(56) 建立一个skydome

      本章建立一个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[i] = pos[i];

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

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

    //投影顶点到球面上,然后缩放顶点到球心的距离
    for(int i = 0; i < vertices.size(); ++i)
        {
        D3DXVec3Normalize(&vertices[i], &vertices[i]);
        vertices[i] *= 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[i].compare(filename) )
            return m_TextureRVs[i];

     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

Directx11教程(56) 建立一个skydome的更多相关文章

  1. Directx11教程(10) 画一个简易坐标轴

    原文:Directx11教程(10) 画一个简易坐标轴       本篇教程中,我们将在三维场景中,画一个简易的坐标轴,分别用红.绿.蓝三种颜色表示x,y,z轴的正向坐标轴. 为此,我们要先建立一个A ...

  2. Directx11教程(5) 画一个简单的三角形(1)

    原文:Directx11教程(5) 画一个简单的三角形(1)       在本篇教程中,我们将通过D3D11画一个简单的三角形.在D3D11中,GPU的渲染主要通过shader来操作(当然还有一些操作 ...

  3. Directx11教程(55) 建立球形和锥形物体

    原文:Directx11教程(55) 建立球形和锥形物体 本教程中,我们新建2个model class,SphereModelClass以及CylinderModelClass,分别用来表示球形和锥形 ...

  4. Directx11教程(19) 画一个简单的地形

    原文:Directx11教程(19) 画一个简单的地形       通常我们在xz平面定义一个二维的网格,然后y的值根据一定的函数计算得到,比如正弦.余弦函数的组合等等,可以得到一个看似不错的地形或者 ...

  5. Directx11教程(11) 增加一个debug宏

    原文:Directx11教程(11) 增加一个debug宏       现在我们在common.h中增加一个debug的宏,在每个d3d11函数后调用,如果d3d函数出错,它能够给出程序中错误的代码行 ...

  6. Directx11教程(9) 增加一个TimerClass类

    原文:Directx11教程(9) 增加一个TimerClass类      在上篇教程代码的基础上,我们增加一个TimerClass类,这个类的功能很简单,就是可以计算相邻2帧的时间差.利用这个时间 ...

  7. Directx11教程(7) 画一个颜色立方体

    原文:Directx11教程(7) 画一个颜色立方体       前面教程我们通过D3D11画了一个三角形,本章我们将画一个颜色立方体,它的立体感更强.主要的变动是ModelClass类,在Model ...

  8. Directx11教程(6) 画一个简单的三角形(2)

    原文:Directx11教程(6) 画一个简单的三角形(2)      在上篇教程中,我们实现了在D3D11中画一个简单的三角形,但是,当我们改变窗口大小时候,三角形形状却随着窗口高宽比例改变而改变, ...

  9. 使用 MVC 5 的 EF6 Code First 入门 系列:建立一个EF数据模型

    这是微软官方SignalR 2.0教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第一篇:建立一个E ...

随机推荐

  1. pycharm每次新建项目都要重新安装一些第三方库的解决办法(转载防删)

    目前有三个解决办法,也是亲测有用的: 第一个方法:因为之前有通过pycharm的project interpreter里的+号添加过一些库,但添加的库只是指定的项目用的,如果想要用,就必须用之前的项目 ...

  2. python 中的split()函数和os.path.split()函数

    Python中有split()和os.path.split()两个函数: split():拆分字符串.通过指定分隔符对字符串进行切片,并返回分割后的字符串列表. os.path.split():将文件 ...

  3. TZ_16_Vue的v-model和v-on

    1.v-model是双向绑定,视图(View)和模型(Model)之间会互相影响. 既然是双向绑定,一定是在视图中可以修改数据,这样就限定了视图的元素类型.目前v-model的可使用元素有: inpu ...

  4. 微信小程序滚动到某个位置添加class效果。

    <scroll-view scroll-y="true" style="height:100vh;" bindscrolltoupper="up ...

  5. JS---案例:tab切换效果

    案例:tab切换效果 获取所有的li标签 第一件事:把这个a所在的所以兄弟元素的类样式全部移除 (removeAttributes) 第二件事:当前点击的a父级元素li (点击这个a所在的所在元素li ...

  6. springmvc java程序文件保存地址的路径问题

    会保存为这种斜杠 不论之前填写的是什么样

  7. 使用Docker 安装Elasticsearch、Elasticsearch-head、IK分词器 和使用

    原文:使用Docker 安装Elasticsearch.Elasticsearch-head.IK分词器 和使用 Elasticsearch的安装 一.elasticsearch的安装 1.镜像拉取 ...

  8. Lab2 新增的细节

    entry.S 新增加了这个入口函数 bootloader 加载完成后 将执行 kern_entry 而非lab1 中的kern_init defs.h 使用了 ({})宏定义的方式,并且执行了一行定 ...

  9. 洛谷P2178 [NOI2015]品酒大会

    题目描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战 两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainb ...

  10. NOIP模拟 6.30

    Problem 1 护花(flower.cpp/c/pas) [题目描述] 约翰留下他的N(N<=100000)只奶牛上山采木.他离开的时候,她们像往常一样悠闲地在草场里吃草.可是,当他回来的时 ...