我正在尝试用Direct3D制作一个相当基本的2D引擎。
我创建了一个LoadImage()函数,它将图像中所有相当静态的行为存储在一个对象中。 (着色器,顶点缓冲器,采样器等)
我打算在常量缓冲区中用矩阵定位顶点。
但是,我还想要一个DrawImage()函数,它有一个参数来告诉应该绘制(剪裁)纹理的哪个部分,所以我必须更新纹理坐标。 由于顶点缓冲区已经预先定义,我想知道是否有办法通过将被发送到顶点着色器的常量缓冲区来更新纹理坐标?
如果您对下面的代码有任何疑问,我希望我的问题很清楚。
bool GameManager::GMLoadImage(Image* pImage, const char* pkcFilePath, ImageDesc* pImDesc)
{
pImage = new Image();
ID3D11ShaderResourceView* pColorMap = (pImage)->GetpColorMap();
/// CREATE SHADER RESOURCE VIEW (from file) ///
HRESULT result = D3DX11CreateShaderResourceViewFromFileA(m_pDevice,
pkcFilePath,
0,
0,
&pColorMap,
0);
if (FAILED(result)) {
MessageBoxA(NULL,"Error loading ShaderResourceView from file","Error",MB_OK);
return false;
}
/// RECEIVE TEXTURE DESC ///
ID3D11Resource* pColorTex;
pColorMap->GetResource(&pColorTex);
((ID3D11Texture2D*)pColorTex)->GetDesc(&((pImage)->GetColorTexDesc()));
pColorTex->Release();
/// CREATE VERTEX BUFFER ///
D3D11_TEXTURE2D_DESC colorTexDesc = pImage->GetColorTexDesc();
float halfWidth = static_cast<float>(colorTexDesc.Width)/2.0f;
float halfHeight = static_cast<float>(colorTexDesc.Height)/2.0f;
Vertex.PosTex vertices[]=
{
{XMFLOAT3( halfWidth, halfHeight, 1.0f ), XMFLOAT2( 1.0f, 0.0f )},
{XMFLOAT3( halfWidth, -halfHeight, 1.0f ), XMFLOAT2( 1.0f, 1.0f )},
{XMFLOAT3( -halfWidth, -halfHeight, 1.0f ), XMFLOAT2( 0.0f, 1.0f )},
{XMFLOAT3( -halfWidth, -halfHeight, 1.0f ), XMFLOAT2( 0.0f, 1.0f )},
{XMFLOAT3( -halfWidth, halfHeight, 1.0f ), XMFLOAT2( 0.0f, 0.0f )},
{XMFLOAT3( halfWidth, halfHeight, 1.0f ), XMFLOAT2( 1.0f, 0.0f )}
};
D3D11_BUFFER_DESC vertexDesc;
ZeroMemory(&vertexDesc,sizeof(vertexDesc));
vertexDesc.Usage = D3D11_USAGE_DEFAULT;
vertexDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexDesc.ByteWidth = sizeof(Vertex.PosTex)*6;
D3D11_SUBRESOURCE_DATA resourceData;
resourceData.pSysMem = vertices;
ID3D11Buffer* pVBuffer = pImage->GetpVertexBuffer();
result = m_pDevice->CreateBuffer(&vertexDesc,&resourceData,&pVBuffer);
if (FAILED(result))
{
MessageBoxA(NULL,"Error Creating VBuffer","Error",MB_OK);
return false;
}
/// SET POINTER TO IMAGEDESC
ImageDesc* pThisImDesc = pImage->GetpImageDesc();
pThisImDesc = pImDesc;
return true;
}
bool GameManager::GMDrawImage(Image* pImage, const CLIPRECT& rkClip)
{
ImageDesc* thisImDesc = pImage->GetpImageDesc();
if ( (thisImDesc != m_pImDesc) ) {
m_pImDesc = thisImDesc;
m_pContext->IASetInputLayout(m_pImDesc->pInputLayout);
m_pContext->IASetPrimitiveTopology(m_pImDesc->Topology);
m_pContext->VSSetShader(m_pImDesc->pSolidColorVS,0,0);
m_pContext->PSSetShader(m_pImDesc->pSolidColorPS,0,0);
m_pContext->PSSetSamplers(0,1,&m_pImDesc->pSampler);
m_pContext->OMSetBlendState(m_pImDesc->pBlendState,NULL,0xFFFFFFFF);
}
UINT stride = m_pImDesc->VertexSize;
UINT offset = 0;
ID3D11Buffer* pVBuffer = pImage->GetpVertexBuffer();
ID3D11ShaderResourceView* pColorMap = pImage->GetpColorMap();
m_pContext->IASetVertexBuffers(0,1,&pVBuffer,&stride,&offset);
m_pContext->PSSetShaderResources(0,1,&pColorMap);
//set constant buffers?
m_pContext->Draw(6,0);
}
答案 0 :(得分:4)
是的,只要您的纹理坐标在顶点缓冲区中硬编码为0.0到1.0,就可以使用纹理变换矩阵。它是一个3x3矩阵,可以转换您的2D纹理坐标。
例如,如果要使用纹理的右下象限(假设左上角是原点),则可以使用以下矩阵:
0.5 0.0 0.0
0.0 0.5 0.0
0.5 0.5 1.0
然后,在顶点着色器中,将纹理坐标乘以该矩阵,如下所示:
float3 coord = float3(In.texCoord, 1.0);
coord *= textureTransform;
Out.texCoord = coord.xy / coord.z;
In.texCoord和Out.texCoord分别是float2输入和输出纹理坐标。
如果您只进行仿射变换(平移,缩放,旋转和倾斜),Z除法是可选的,所以如果不需要,可以随意删除它。
概括矩阵:
Sx 0.0 0.0
0.0 Sy 0.0
Tx Ty 1.0
其中Txy是剪辑区域的位置,Sxy是剪辑区域的大小,在纹理坐标中。