Lesson 18: SDK文档:Tutorial 7 Texture Mapping and Constant Buffers分析

中文翻译文档地址:
http://blog.sina.com.cn/s/blog_6e1283650102vfi9.html


学习心得:

学完本篇,D3D11 SDK文档附带的教程就全部学习完了,想要真正掌握的话,还是需要不断地对代码进行修改,有什么好的想法就在原代码基础上添加、修改,这样你也能在这个过程中弄清其作用及含义。


纹理映射

纹理映射是指2D纹理到3D几何模型的投影。我们可以认为它是一种包装。为了完成这项任务,我们不得不确定2D图像跟表面上顶点的关系。
这个立方体比较适合进行纹理坐标的设置。对于复杂的模型,手动地设置纹理坐标是比较困难的。那么,3D建模包一般会算出模型的每个顶点所对应的纹理坐标。既然我们的例子是立方体,那就比较容易地就可以设置所需要的纹理坐标。纹理坐标被定义在顶点,然后为表面上的每个像素进行插值运算。

创建渲染器的纹理资源和采样状态

纹理是来自文件的一张2D图像,并被用来创建一个shader-resource view,以至于它能从渲染器中读取。
我们在设备初始化函数中通过调用D3DX11CreateShaderResourceViewFromFile加载纹理图片

// Load the Texture
    hr = D3DX11CreateShaderResourceViewFromFile( g_pd3dDevice, L"seafloor.dds", NULL, NULL, &g_pTextureRV, NULL );
    if( FAILED( hr ) )
        return hr;

我们也要创建一个采样状体来控制渲染器如何对纹理进行过滤,筛选,寻址。
在这个教程中,我们启动采样状态并进行线性过滤和重叠寻址。为了创建采样状态,我们使用ID3D11Device::CreateSamplerState()函数。

// Create the sample state
D3D11_SAMPLER_DESC sampDesc;
ZeroMemory( &sampDesc, sizeof(sampDesc) );
sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
sampDesc.MinLOD = 0;
sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
hr = g_pd3dDevice->CreateSamplerState( &sampDesc, &g_pSamplerLinear );
if( FAILED( hr ) )
    return hr;

定义坐标

在我们把图片映射到立方体之前,我们必须首先立方体每个顶点的纹理坐标。既然纹理能够以任意大小使用,纹理坐标系统将被控制在[0,1]。左上角的纹理坐标是(0,0),右下角的纹理坐标为(1,1)。
在这个例子中,我们使用整个纹理铺满立方体的每个面。这个定义鉴定清晰,没有任何混乱。然而,也完全有可能延展纹理来铺设立方体的六个面,虽然使用定义点更困难,纹理会出现伸展和扭曲。

第一,我们更新这个结构体来定义我们的包含纹理坐标的顶点。

struct SimpleVertex{
    XMFLOAT3 Pos;
    XMFLOAT2 Tex;
};

接下来,我们修改渲染器的input layout。

// 修改渲染器的input layout
D3D11_INPUT_ELEMENT_DESC layout[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};

既然我们的input layout改变了,对应的顶点渲染器也必须修改

struct VS_INPUT{
    float4 Pos : POSITION;
    float2 Tex : TEXCOORD;
};

最后,我们把纹理坐标添加到顶点中。
注意,第二个输入参数D3DXVECTOR2包含纹理坐标。立方体的每个顶点将对应一个纹理坐标。这个纹理映射的创建是把(0,0)(0,1)(1,0)或者(1,1)作为纹理坐标。

// Create vertex buffer
SimpleVertex vertices[] = {
    { XMFLOAT3( -1.0f, 1.0f, -1.0f ), XMFLOAT2( 0.0f, 0.0f ) },
    { XMFLOAT3( 1.0f, 1.0f, -1.0f ), XMFLOAT2( 1.0f, 0.0f ) },
    { XMFLOAT3( 1.0f, 1.0f, 1.0f ), XMFLOAT2( 1.0f, 1.0f ) },
    { XMFLOAT3( -1.0f, 1.0f, 1.0f ), XMFLOAT2( 0.0f, 1.0f ) },

    { XMFLOAT3( -1.0f, -1.0f, -1.0f ), XMFLOAT2( 0.0f, 0.0f ) },
    { XMFLOAT3( 1.0f, -1.0f, -1.0f ), XMFLOAT2( 1.0f, 0.0f ) },
    { XMFLOAT3( 1.0f, -1.0f, 1.0f ), XMFLOAT2( 1.0f, 1.0f ) },
    { XMFLOAT3( -1.0f, -1.0f, 1.0f ), XMFLOAT2( 0.0f, 1.0f ) },

    { XMFLOAT3( -1.0f, -1.0f, 1.0f ), XMFLOAT2( 0.0f, 0.0f ) },
    { XMFLOAT3( -1.0f, -1.0f, -1.0f ), XMFLOAT2( 1.0f, 0.0f ) },
    { XMFLOAT3( -1.0f, 1.0f, -1.0f ), XMFLOAT2( 1.0f, 1.0f ) },
    { XMFLOAT3( -1.0f, 1.0f, 1.0f ), XMFLOAT2( 0.0f, 1.0f ) },

    { XMFLOAT3( 1.0f, -1.0f, 1.0f ), XMFLOAT2( 0.0f, 0.0f ) },
    { XMFLOAT3( 1.0f, -1.0f, -1.0f ), XMFLOAT2( 1.0f, 0.0f ) },
    { XMFLOAT3( 1.0f, 1.0f, -1.0f ), XMFLOAT2( 1.0f, 1.0f ) },
    { XMFLOAT3( 1.0f, 1.0f, 1.0f ), XMFLOAT2( 0.0f, 1.0f ) },

    { XMFLOAT3( -1.0f, -1.0f, -1.0f ), XMFLOAT2( 0.0f, 0.0f ) },
    { XMFLOAT3( 1.0f, -1.0f, -1.0f ), XMFLOAT2( 1.0f, 0.0f ) },
    { XMFLOAT3( 1.0f, 1.0f, -1.0f ), XMFLOAT2( 1.0f, 1.0f ) },
    { XMFLOAT3( -1.0f, 1.0f, -1.0f ), XMFLOAT2( 0.0f, 1.0f ) },

    { XMFLOAT3( -1.0f, -1.0f, 1.0f ), XMFLOAT2( 0.0f, 0.0f ) },
    { XMFLOAT3( 1.0f, -1.0f, 1.0f ), XMFLOAT2( 1.0f, 0.0f ) },
    { XMFLOAT3( 1.0f, 1.0f, 1.0f ), XMFLOAT2( 1.0f, 1.0f ) },
    { XMFLOAT3( -1.0f, 1.0f, 1.0f ), XMFLOAT2( 0.0f, 1.0f ) },
      };

当我们进行纹理采样的时候,我们需要受几何模型的材质的调节。

绑定纹理作为渲染器资源

在渲染函数中:

// 绑定纹理作为渲染器资源
// 一个纹理和采样状态都是对象,与静态缓存对象类似。
// 在它们能够被渲染器使用之前,它们需要用ID3D11DeviceContext::PSSetSamplers()函数
// 和ID3D11DeviceContext::PSSetShaderResources()函数进行设置。
g_pImmediateContext->PSSetShaderResources( 0, 1, &g_pTextureRV );
g_pImmediateContext->PSSetSamplers( 0, 1, &g_pSamplerLinear );

现在,渲染器可以使用纹理了。

使用纹理

为了将纹理映射到几何模型上,我们将在像素渲染器中调用纹理采样函数。
Sample()函数将执行2D纹理的采样功能,并且能返回采样颜色。像素渲染器调用这个函数并且把它乘以模型的颜色(模型的材质),然后输出最后的颜色。

float4 PS( PS_INPUT input) : SV_Target
{
    // 1.当我们将g_TextureRV绑定,txDiffuse是用来保存我们纹理的对象。
    // 2.samLinear在下面描述,是一种纹理的采样模式。
    // 3.当我们确定纹理资源的时候,input.Tex是纹理坐标。
    return txDiffuse.Sample( samLinear, input.Tex ) * vMeshColor;
}

还有一件我们要记得去做的就是从顶点渲染器中传出纹理坐标。
如果我们不这么做,当我们进入像素渲染器时将得不到这些数据。这里,我们只是复制输入的纹理坐标作为输出的纹理坐标,并让硬件来处理其他工作。

// Vertex Shader
PS_INPUT VS( VS_INPUT input ){
    PS_INPUT output = (PS_INPUT)0;
    output.Pos = mul( input.Pos, World );
    output.Pos = mul( output.Pos, View );
    output.Pos = mul( output.Pos, Projection );
    output.Tex = input.Tex;

    return output;
}

静态缓存

在Direct3D中,一个应用程序使用静态缓存来设置渲染器变量。
静态缓存将使用类C的语法声明。静态缓存能够减少带宽来更新渲染器,它是通过将静态缓存放在一起,并能同时处理,而不是分开调用。
在前面的教程中,我们使用单一的静态缓存来保存我们需要的渲染器变量。但是最好,最高效的使用方法是用静态缓存来组织渲染器变量,因为他们经常被改动。这样就可以让应用程序以最小的宽带来更新渲染器变量。

// 一旦被设置就无法改变的变量
struct CBNeverChanges
{
    XMMATRIX mView;
};

// 只有在窗口大小被改变时才改变的变量
struct CBChangeOnResize
{
    XMMATRIX mProjection;
};

// 每帧值都在改变的变量
struct CBChangesEveryFrame
{
    XMMATRIX mWorld;
    XMFLOAT4 vMeshColor;
};

为了跟静态缓存一起工作,你需要为每个创建一个ID3D11Buffer对象。然后调用ID3D11DeviceContext::UpdateSubresource()来更新每个静态缓存,而不影响其他静态缓存。

//
// Update variables that change once per frame
//
CBChangesEveryFrame cb;
cb.mWorld = XMMatrixTranspose( g_World );
cb.vMeshColor = g_vMeshColor;
g_pImmediateContext->UpdateSubresource( g_pCBChangesEveryFrame, 0, NULL, &cb, 0, 0 );
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值