Effect架構簡述
之前我們已經提到過HLSL檔案。并且我還編寫程式編譯它,加載它,使用它。但是那樣使用未免有點複雜。需要繪制複雜圖形的時候,我們可以直接使用Effect架構來加載.fx檔案.這裡提到的.fx檔案和我們之前提到的.HLSL檔案差不多,文法上基本相同。我們把它編譯之後是與一個ID3DX11Effect接口關聯在一起,被關聯的接口就成為了它的經紀人,把外界的資料傳遞給它,比如設定各種常量矩陣。我們通過ID3DX11EffectTechnique來調用其中的着色器。着色器的設定在.fx檔案中完成
定義與外部互動的變量
在Effect檔案與外部互動的變量通常定義在cbuffer中,或者也可以像HLSL檔案那樣直接定義在全局。如下所示:
cbuffer MatrixBuffer
{
matrix World; //世界坐标變換矩陣
matrix View; //觀察坐标變換矩陣
matrix Projection; //投影坐标變換矩陣
float4 EyePosition;//視點距離
};
Texture2D Texture; //紋理變量
外部程式通過設定這些變量,供着色器在渲染的時候使用.
Technique
每個Effect檔案至少包含一個Technique,每個Technique至少包含一個通道.定義方式如下:
//
//定義Techinique
/
technique11 TexTech
{
pass P0
{
SetVertexShader(CompileShader(vs_5_0,VSMain()));
SetGeometryShader(NULL);
SetPixelShader(CompileShader(ps_5_0,PSMain()));
}
}
頂點着色器和像素着色器就很好編寫了.編寫好直接使用即可.
Effect架構的使用
在Direct3D11中,Effect架構由兩個接口管理着,一個就是經紀人,一個就是執行者.它們分别是
ID3DX11Effect *g_pEffect = NULL;
ID3DX11EffectTechnique *g_pEffectTechnique = NULL;
ID3DX11Effect就是充當着經紀人的角色,溝通着Effect程式内部與外部程式.而ID3DX11EffectTechnique則管理着通道.負責對頂點進行渲染.
編譯Effect檔案
編譯Effect檔案與之前編譯.HLSL檔案相同,都是通過D3DX11CompileFromFile來完成.不過這裡要注意的是,之前使用的着色器模型要改成fx_5_0.
之前提到有一個flag2,在MSDN上說明了,建議設為0比較好。那就用0吧。
下面給出一個調用執行個體:
hr = D3DX11CompileFromFile(L"SimpleShader.fx", NULL, NULL, NULL,
"fx_5_0", D3DCOMPILE_ENABLE_STRICTNESS, ,
NULL, &pTechBlob, NULL, NULL);
if (FAILED(hr)) {
::MessageBox(NULL, L"fx 載入失敗", L"Error", MB_OK);
return hr;
}
建立Effect
Effect檔案編譯成功之後,它的位元組碼就儲存在記憶體當中。我們要從記憶體建立Effect接口對象,也就是聘用一個經紀人來管理它.
通過調用D3DX11CreateEffectFromMemory來完成.該函數的原型如下
HRESULT D3DX11CreateEffectFromMemory(
void *pData,
SIZE_T DataLength,
UINT FXFlags,
ID3D11Device *pDevice,
ID3DX11Effect **ppEffect
);
- void *pData:一個指針,指向Effect資料所在記憶體段的首位址
- SIZE_T DataLength:資料段長度
- UINT FXFlags:目前暫時沒有flags,設定為0即可
- ID3D11Device *pDevice:很明顯,Direct3D裝置接口
- ID3DX11Effect **ppEffect:Effect接口,就是我們之前說的經紀人.
擷取ID3DX11EffectTechnique接口
ID3DX11EffectTechnique接口是通過ID3DX11Effect的GetTechniqueByName或者GetTechniqueByIndex方法得到的,這兩個方法的使用都很簡單,隻需要傳入相應的參數就行.
GetTechniqueByName指的就是根據名字尋找Technique
GetTechniqueByIndex就是根據索引号來尋找Technique
建立輸入布局
建立Effect的輸入布局,必須提前擷取通道的描述資訊,以便在建立輸入布局的時候要用到.
前面說過,ID3DX11EffectTechnique就是負責管理通道的接口,可以通過調用它的GetPassByIndex或者GetPassByName來擷取類型為ID3DX11EffectPass的通道接口對象.通過該接口的GetDesc就可以擷取到通道的相關參數資訊.
GetDesc隻有一個D3DX11_PASS_DESC指針的參數,它的原型如下:
HRESULT GetDesc(
_Out D3DX11_PASS_DESC *pDesc
);
而提到D3DX11_PASS_DESC就順便把該結構體的内部資訊展示出來:
struct D3DX11_PASS_DESC
{
LPCSTR Name; // Name of this pass (nullptr if not anonymous)
uint32_t Annotations; // Number of annotations on this pass
uint8_t *pIAInputSignature; // Signature from VS or GS (if there is no VS)
// or nullptr if neither exists
size_t IAInputSignatureSize; // Singature size in bytes
uint32_t StencilRef; // Specified in SetDepthStencilState()
uint32_t SampleMask; // Specified in SetBlendState()
FLOAT BlendFactor[]; // Specified in SetBlendState()
};
其中的注釋已經相當清楚,就不再做過多的解釋了.
下面是一個調用示例:
g_technique = g_effect->GetTechniqueByName("TexTech");
D3DX11_PASS_DESC PassDesc;
g_technique->GetPassByIndex()->GetDesc(&PassDesc);
當調用了GetDesc得到通道的參數資訊之後,可以通過D3D11_INPUT_ELEMENT_DESC結構體數組來描述輸入的元素特征。接着通過裝置接口調用CreateInputLayout即可獲得輸入布局.
下面建立的示例代碼:
hr = g_pd3dDevice->CreateInputLayout(
layout, //上面定義的D3D11_INPUT_ELEMENT_DESC數組
numElements, //D3D11_INPUT_ELEMENT_DESC數組的元素個數
PassDesc.pIAInputSignature,//Effect Pass描述的輸入辨別
PassDesc.IAInputSignatureSize,//Effect Pass描述的輸入辨別的大小
&g_vertexLayout);//傳回生成的輸入布局對象
輸入布局建立成功基本上就完成了初始化的工作。在後續的渲染操作中需要使用到輸入布局的時候,隻需要通過裝置上下文接口來設定一下.
Effect的使用
遊戲中每個頂點都不會是一成不變的。我們在Effect檔案中通常會定義一些變量,用于和外部程式溝通。我們可以從外部程式設定這些變量的值.
明星都不會直接與你打交道的,都是經紀人在打點這一切。是以Effect程式也是相當地高貴,我們隻能通過ID3DX11Effect接口對象來設定變量的值.
我們可以通過ID3DX11Effect接口對象的GetVariableByName或者GetVariableByIndex或者GetVariableBySemantic方法來擷取變量接口:ID3DX11EffectVariable.
通過ID3DX11EffectVariable接口可以将得到的變量解讀為某一種類型的接口.如若要解釋為矩陣,則調用它的AsMatrix方法即可.然後通過SetMatrix方法來将值設定到變量中.
設定完成之後,就需要對頂點進行着色。這是ID3DX11EffectPass接口對裝置上下文接口的一次調用,很明顯,在裝置上下文接口中應該已經設定好了頂點緩存,或者頂點緩存+索引緩存.接着通過ID3DX11EffectPass接口的Apply方法即可把頂點作為着色器的輸入參數,輸出結果頂點.
這一過程的示例代碼如下:
//設定世界坐标系
g_effect->GetVariableByName("World")->AsMatrix()->SetMatrix((float*)&world);
//設定觀察坐标系
g_effect->GetVariableByName("View")->AsMatrix()->SetMatrix((float*)&view);
//設定投影坐标系
g_effect->GetVariableByName("Projection")->AsMatrix()->SetMatrix((float*)&projection);
//定義一個D3DX11_TECHNIQUE_DESC對象來描述technique
//D3DX11_TECHNIQUE_DESC techDesc;
//g_technique->GetDesc(&techDesc);//擷取techinique的描述
//擷取通道Pass,把它設定到執行上下文中
g_technique->GetPassByIndex()->Apply(, g_pImmediateContext);
//繪制立方體,注意,這裡調用的是DrawIndexed
g_pImmediateContext->DrawIndexed(, , );
g_pSwapChain->Present(, );
旋轉立方體代碼
Cube.fx
cbuffer ConstantBuffer
{
matrix WORLD;
matrix VIEW;
matrix PROJECTION;
};
struct VS_INPUT
{
float4 Pos:SV_POSITION;
float4 Color:COLOR;
};
VS_INPUT VS(float4 Pos:POSITION, float4 Color : COLOR)
{
VS_INPUT input = (VS_INPUT);
input.Pos = mul(Pos, WORLD);
input.Pos = mul(input.Pos, VIEW);
input.Pos = mul(input.Pos, PROJECTION);
input.Color = Color;
return input;
}
float4 PS(VS_INPUT input):SV_TARGET
{
return input.Color;
}
technique11 CubeTex
{
pass P0 {
SetVertexShader(CompileShader(vs_5_0, VS()));
SetGeometryShader(NULL);
SetPixelShader(CompileShader(ps_5_0, PS()));
}
};
Cube.h
#pragma once
#include"dxUtil.h"
struct Vertex
{
XMFLOAT3 Position;
XMFLOAT4 Color;
};
class Cube
{
private:
ID3DX11Effect *m_pEffect;
ID3DX11EffectTechnique *m_pTechnique;
ID3D11InputLayout *m_pInputLayout; //輸入布局
ID3D11Buffer *m_pVertexBuffer;//頂點緩存
UINT m_uStrid;
UINT m_uOffset;
ID3D11Buffer *m_pIndexBuffer;//索引緩存
XMFLOAT4X4 m_world; //物體的位置
public:
Cube()
:m_pEffect(NULL),m_pTechnique(NULL),m_pInputLayout(NULL),m_pVertexBuffer(NULL),m_pIndexBuffer(NULL)
{
XMStoreFloat4x4(&m_world, XMMatrixIdentity());
}
HRESULT Init(DirectSystem* d3dSys) {
HRESULT hr;
/*建立Effect*/
ID3DBlob *effectFileBlob = NULL;
hr=D3DX11CompileFromFile(L"Cube.fx", NULL, NULL, NULL, "fx_5_0", NULL, , NULL, &effectFileBlob, NULL, NULL);
if (FAILED(hr))
{
return hr;
}
hr = D3DX11CreateEffectFromMemory(
effectFileBlob->GetBufferPointer(),
effectFileBlob->GetBufferSize(),
, d3dSys->D3DDevice(),
&m_pEffect,
NULL
);
m_pTechnique = m_pEffect->GetTechniqueByName("CubeTex");
/*建立輸入布局*/
D3D11_INPUT_ELEMENT_DESC layout[] = {
{
"POSITION",,DXGI_FORMAT_R32G32B32_FLOAT,,,D3D11_INPUT_PER_VERTEX_DATA,
},
{
"COLOR",,DXGI_FORMAT_R32G32B32A32_FLOAT,,,D3D11_INPUT_PER_VERTEX_DATA,
}
};
UINT numLayoutSize = ARRAYSIZE(layout);
D3DX11_PASS_DESC passDesc;
m_pTechnique->GetPassByIndex()->GetDesc(&passDesc);
d3dSys->D3DDevice()->CreateInputLayout(layout, numLayoutSize, passDesc.pIAInputSignature, passDesc.IAInputSignatureSize, &m_pInputLayout);
/*建立頂點緩存*/
Vertex vertices[]=
{
{ XMFLOAT3(-,,-),XMFLOAT4(,,,) },
{ XMFLOAT3(,,-),XMFLOAT4(,,,) },
{ XMFLOAT3(,,),XMFLOAT4(,,,) },
{ XMFLOAT3(-,,),XMFLOAT4(,,,) },
{ XMFLOAT3(-,-,-),XMFLOAT4(,,,) },
{ XMFLOAT3(,-,-),XMFLOAT4(,,,) },
{ XMFLOAT3(,-,),XMFLOAT4(,,,) },
{ XMFLOAT3(-,-,),XMFLOAT4(,,,) }
};//頂點集合
D3D11_BUFFER_DESC vertexDesc;
::ZeroMemory(&vertexDesc, sizeof(D3D11_BUFFER_DESC));
vertexDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexDesc.ByteWidth = sizeof(Vertex) * ;
vertexDesc.CPUAccessFlags = ;
vertexDesc.MiscFlags = ;
vertexDesc.StructureByteStride = sizeof(Vertex);
vertexDesc.Usage = D3D11_USAGE_DEFAULT;
D3D11_SUBRESOURCE_DATA vertexData;
::ZeroMemory(&vertexData,sizeof(D3D11_SUBRESOURCE_DATA));
vertexData.pSysMem = vertices;
d3dSys->D3DDevice()->CreateBuffer(&vertexDesc, &vertexData, &m_pVertexBuffer);
m_uStrid = sizeof(Vertex);
m_uOffset = ;
/*建立索引緩存*/
WORD indexs[] = {
,,,
,,,
,,,
,,,
,,,
,,,
,,,
,,,
,,,
,,,
,,,
,,
};
UINT numElements = ARRAYSIZE(indexs);
D3D11_BUFFER_DESC indexDesc;
indexDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexDesc.ByteWidth = sizeof(WORD)*numElements;
indexDesc.CPUAccessFlags = ;
indexDesc.MiscFlags = ;
indexDesc.StructureByteStride = sizeof(WORD);
indexDesc.Usage = D3D11_USAGE_DEFAULT;
D3D11_SUBRESOURCE_DATA indexData;
indexData.pSysMem = indexs;
d3dSys->D3DDevice()->CreateBuffer(&indexDesc, &indexData, &m_pIndexBuffer);
}
VOID Update(float deltaTime)
{
static float angle = ;
angle += deltaTime;
if (angle >= )
angle = ;
//XMMATRIX world = XMLoadFloat4x4(&m_world);
XMMATRIX mov = XMMatrixTranslation(, , );
XMVECTOR axis = XMVectorSet(, , , );
XMMATRIX rotAxis = XMMatrixRotationAxis(axis, angle);
XMMATRIX rot = XMMatrixRotationY(angle);
XMMATRIX world = rotAxis*mov*rot;
XMStoreFloat4x4(&m_world, world);
}
HRESULT Draw(DirectSystem* d3dSys)
{
d3dSys->D3DDeviceContext()->IASetVertexBuffers(, , &m_pVertexBuffer,&m_uStrid,&m_uOffset);
d3dSys->D3DDeviceContext()->IASetIndexBuffer(m_pIndexBuffer, DXGI_FORMAT_R16_UINT, );
d3dSys->D3DDeviceContext()->IASetInputLayout(m_pInputLayout);
d3dSys->D3DDeviceContext()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
XMMATRIX world;
world=XMLoadFloat4x4(&m_world);
XMMATRIX view;
XMMATRIX projection;
view = d3dSys->GetViewMatrix();
projection = d3dSys->GetProjectionMatrix();
m_pEffect->GetVariableByName("WORLD")->AsMatrix()->SetMatrix((float*)&world);
m_pEffect->GetVariableByName("VIEW")->AsMatrix()->SetMatrix((float*)&view);
m_pEffect->GetVariableByName("PROJECTION")->AsMatrix()->SetMatrix((float*)&projection);
m_pTechnique->GetPassByIndex()->Apply(, d3dSys->D3DDeviceContext());
d3dSys->D3DDeviceContext()->DrawIndexed(, , );
return S_OK;
}
~Cube() {
SAFE_RELEASE(m_pTechnique);
SAFE_RELEASE(m_pEffect);
SAFE_RELEASE(m_pVertexBuffer);
SAFE_RELEASE(m_pIndexBuffer);
}
};
Main.cpp核心部分
MSG msg = { };
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg,NULL, , , PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
static float lastTime = f;
float currentTime = (float)timeGetTime();
float deltaTime = (currentTime - lastTime)*0;
g_pCube->Update(deltaTime);
g_pDirect->Direct3D_Clear();
g_pCube->Draw(g_pDirect);
g_pDirect->Direct3D_Present();
lastTime = currentTime;
}
}
Copyright© by 寒江雪
Date:2016.12.30