上一節的筆記本身寫的十分糟糕,那個程序也寫的十分糟糕。。。。。。。。。若是真的有人看的話,說聲抱歉。函數
這一節主要是記錄一個旋轉的正方形的製做過程,先說好:如下全部內容請配合上傳了的代碼食用。。。。。。。。。。若是真的有人看的話。佈局
首先,先大概介紹一下繪製一個圖形的基本流程:visual-studio
一.建立基本的D3D對象:學習
1.使用D3D11CreateDeviceAndSwapChain建立D3D設備對象與交換鏈。this
2.使用CreateRenderTargetView建立後一個繪製緩衝區。spa
3.若是須要,建立模板與景深緩衝區。3d
4.使用RSSetViewports設置視窗。code
二.編譯,連接到effect:orm
1.組建,加載頂點着色器。對象
2.初始化着色器的輸入佈局。
3.組建,加載像素着色器。
三.建立繪製圖形:
1.建立,定義頂點座標,建立相應的緩衝區,用已定義的頂點初始化,再將建立好的緩衝區綁到設備輸入槽上去。
2.建立頂點索引,建立相應的緩衝區,用以建立的索引初始化。
3.設置好投影空間的位置,中心座標,z軸座標(下面再進行詳細介紹)。
4.HLSL相關。
關於如何建立D3D的一些基本對象就再也不敘述了。直接從第二個開始:
首先咱們須要一個.fx的文件,關於文件如何組成的以後再說明,咱們只要知道如今這個文件給予了程序2個接口:頂點着色器與像素着色器。
關於頂點,咱們的圖像在程序中都是由一個個三角形組成的,參見書中的各類圖形,假設這裏咱們須要繪製一個方形,那麼咱們至少須要2個三角形,4個頂點去描述這個方形。
頂點着色器經過咱們輸入的頂點與它相應的屬性去描述各個三角形經過各類座標變換,最後組成了咱們的圖像。
像素着色器,圖形的顏色是由一個個像素點組成的,像素着色器告訴GPU哪些像素須要着色,須要什麼顏色。
(以上是我如今的理解,可能會有許多錯誤)
代碼以前,先對書中內容進行一些討論:因爲D3DX系列的函數在我如今的環境下沒法使用,因此書中的代碼是沒法正常經過編譯的,主要是書中使用了D3DX11CompileFromFile去編譯fx文件,這對個人學習產生了極大的困擾。
關於這個問題的解決方案,看一下stackoverflow的這個回答。
http://stackoverflow.com/questions/30579016/d3d11-d3dx11createeffectfrommemory-returns-e-noiterface
以及
http://stackoverflow.com/questions/12549472/using-directx-effect11-with-visual-studio-2012
總之,雖然我下載了D3DX的開源代碼,而且編譯成功了,可是仍是沒有成功搭建出使用D3DX11CompileFromFile以及之類的函數的環境,因此選擇使用其餘的函數。
個人代碼
HRESULT DrawFunction::CompileTheShader(void) { HRESULT hr = S_OK;//ret ID3DBlob* pVSBlob = nullptr; ID3DBlob* pErrorBlob = nullptr; //Compiler vertex shader //組建加載頂點着色器 hr = D3DCompileFromFile(L"Squance.fx", nullptr, nullptr, "VS", "vs_5_0", D3DCOMPILE_ENABLE_STRICTNESS, 0, &pVSBlob, &pErrorBlob); if (FAILED(hr)) { #ifdef _DEBUG printf("Can not find FX File .\nPlease run this executable from the directory that contains the FX file\n Error HRESULT:%ld\n\n", hr); return hr; #else MessageBox( nullptr, L"The FX file cannot be compiled. Please run this executable from the directory that contains the FX file.", L"Error", MB_OK ); return hr; #endif } //Create VERTEX shader hr = dev->CreateVertexShader(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), nullptr, &pVertexShader); if (FAILED(hr)) { pVSBlob->Release(); return hr; } // Define the input layout //定義輸入佈局 D3D11_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; UINT numElements = ARRAYSIZE(layout); // Create the input layout hr = dev->CreateInputLayout(layout, numElements, pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &pVertexLayout); if (FAILED(hr)) return hr; // Set the input layout devContext->IASetInputLayout(pVertexLayout); // Compile the pixel shader //組建加載像素着色器 ID3DBlob* pPSBlob = nullptr; hr = D3DCompileFromFile(L"Squance.fx", nullptr, nullptr, "PS", "ps_5_0", D3DCOMPILE_ENABLE_STRICTNESS, 0, &pPSBlob, &pErrorBlob); if (FAILED(hr)) { #ifdef _DEBUG printf("Can not find FX File .\nPlease run this executable from the directory that contains the FX file\n Error HRESULT:%ld\n\n", hr); return hr; #else MessageBox(nullptr, L"The FX file cannot be compiled. Please run this executable from the directory that contains the FX file.", L"Error", MB_OK); return hr; #endif } // Create the pixel shader hr = dev->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), nullptr, &pPixelShader); if (FAILED(hr)) return hr; //release resource pVSBlob->Release(); pPSBlob->Release(); return hr; }
關於輸入佈局:
對於頂點着色器,咱們須要知道輸入的頂點包含了哪些結構與元素,這裏我就只包含了一個頂點的座標與顏色(頂點的顏色。。。。好奇怪。。。)
建立繪製的圖像:
上面提到了三角形與圖像的關係,仍是用方形爲例,一個方形須要4個頂點,因此就定義出來
這裏我定義了2個方形,由於在繪圖的時候會出現背面消影的問題,若是我須要一個旋轉的,二面的方形,就須要把它的正反面都定義出來。
struct SimpleVertex { DirectX::XMFLOAT3 Pos; DirectX::XMFLOAT4 Color; }; SimpleVertex vertices[] = { //正面 { DirectX::XMFLOAT3(+1.0f, +1.0f, +0.0f), DirectX::XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },//紅 { DirectX::XMFLOAT3(+1.0f, -1.0f, +0.0f), DirectX::XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f) },//黃 { DirectX::XMFLOAT3(-1.0f, -1.0f, +0.0f), DirectX::XMFLOAT4(0.0f, 0.5f, 1.0f, 1.0f) },//藍 { DirectX::XMFLOAT3(-1.0f, +1.0f, +0.0f), DirectX::XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },//綠 //反面 { DirectX::XMFLOAT3(+1.0f, +1.0f, -0.1f), DirectX::XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) }, { DirectX::XMFLOAT3(+1.0f, -1.0f, -0.1f), DirectX::XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f) }, { DirectX::XMFLOAT3(-1.0f, -1.0f, -0.1f), DirectX::XMFLOAT4(0.0f, 0.5f, 1.0f, 1.0f) }, { DirectX::XMFLOAT3(-1.0f, +1.0f, -0.1f), DirectX::XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) } };
定義頂點索引:
頂點索引的原理在書中已經說明了,目的就是爲了避免對頂點進行復刻,一個頂點只須要定義一次,減小內存與處理器的負擔。
WORD indices[] = { 0, 2, 1, 0, 3, 2, 4, 5, 6, 4, 6, 7 };
這是一個以上面的8個頂點爲例的索引,能夠看見建立了4個三角形。
而後須要注意一點:背面消隱,具體看書中5.10.2的部分,簡而言之,一個三角形只有一面是「有顏色」去顯示的,而三角形的正反面是由你定義三角形時的頂點順序有關的
投影的視角,座標:
與其叫投影,我更喜歡將他比喻成相機或眼睛。
想象一下,咱們繪製好的圖形須要一個攝像機去把它放到咱們的屏幕上,須要如下幾個參數:
1.攝像機的位置,相對於世界空間,攝像機的擺放在哪一個座標。
2.攝像機的朝向,也就是鏡頭的方向,鏡頭向哪裏拍攝。
3.垂直方向,在上面2項決定好的狀況下,還須要一個固定鏡頭垂直的方向,來防止鏡頭隨意的旋轉,來告訴鏡頭Z軸。
DirectX::XMVECTOR Eye = DirectX::XMVectorSet(0.0f, 0.0f, 4.0f, 0.0f);; //眼睛座標 DirectX::XMVECTOR Point = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); //看向的位置 DirectX::XMVECTOR Eye_Up = DirectX::XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);//定義眼睛"向上"的方向 View_View = DirectX::XMMatrixLookAtLH(Eye, Point, Eye_Up); View_Projection = DirectX::XMMatrixPerspectiveFovLH(DirectX::XM_PIDIV2, 800 / (FLOAT)600, 0.01f, 100.0f);
上面的代碼中,我將鏡頭設置在(0,0,4)的位置,看向原點(0,0,0),鏡頭的垂直方向是(0,1,0)就是Y軸。
HLSL語言:
使用時,須要設置FX文件的屬性爲不參與生成,不然會報一個沒有main入口的錯誤。
着色器的編寫須要HLSL這種語言,好在他與C++極其類似。據說之前的着色器語言是用匯編寫的。。。這真是太糟糕了。
對於語法什麼的也沒啥好講的,主要是1個功能:
常量緩衝
C++代碼中有:
//結構定義 struct ConstantBuffer //用於描述視角座標 { DirectX::XMMATRIX mWorld; DirectX::XMMATRIX mView; DirectX::XMMATRIX mProjection; }; //建立 bd.Usage = D3D11_USAGE_DEFAULT; bd.ByteWidth = sizeof(ConstantBuffer); bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; bd.CPUAccessFlags = 0; hr = dev->CreateBuffer(&bd, nullptr, &pConstantBuffer); if (FAILED(hr)) { #ifdef _DEBUG printf("Can not create the constant buffer\n Error HRESULT:%ld\n\n", hr); #endif return hr; }
fx文件中有:
cbuffer ConstantBuffer : register(b0) { matrix World; matrix View; matrix Projection; } VS_OUTPUT VS(float4 Pos : POSITION, float4 Color : COLOR) { VS_OUTPUT output = (VS_OUTPUT)0; output.Pos = mul(Pos, World); output.Pos = mul(output.Pos, View); output.Pos = mul(output.Pos, Projection); output.Color = Color; return output; }
能夠看做着色器與程序共享了一塊內存,運行時,着色器不能改變常量緩衝,咱們經過C++代碼改變常量緩衝的內容,加以該變着色器的顯示。
最後,也是最重要的,數學變換原理:
1.局部座標,世界座標相互轉換:
上面的的內容說了,咱們在世界座標的某點有一個攝像機,然而,對於程序而言,繪圖就是繪製一個個像素點,座標分析的步驟須要咱們去完成:
在世界座標內有一個座標點,那麼在咱們一相機爲原點的座標系內它的座標是什麼呢?
部分代碼:
View_World = DirectX::XMMatrixIdentity();//XMMatrixIdentity構建單位矩陣 // Initialize the view matrix DirectX::XMVECTOR Eye = DirectX::XMVectorSet(0.0f, 0.0f, 4.0f, 0.0f);; //眼睛座標 DirectX::XMVECTOR Point = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); //看向的位置 DirectX::XMVECTOR Eye_Up = DirectX::XMVectorSet(0.0f, 1.0f, 100.0f, 0.0f);//定義眼睛"向上"的方向 View_View = DirectX::XMMatrixLookAtLH(Eye, Point, Eye_Up); //XMMatrixLookAtLH函數返回的是世界->視圖變換矩陣
代碼中定義了一個單位矩陣做爲原世界座標,在(0,0,4)這點定義了相機,而後看一個API:XMMatrixLookAtLH,它的做用是產生一個變換矩陣,用於將世界座標轉換爲局部座標,也就是一相機爲原點的座標。描述一下它的實現原理:
將固定好的相機方向單位化,計算出從局部座標轉換成世界座標的矩陣,而後求這個矩陣的逆矩陣,就是從世界座標轉換爲局部座標的轉換矩陣。
咱們能夠直接在書中3.4.5節找到直接求逆的公式,完整的用代碼表示就是:
XMMATRIX XM_CALLCONV XMMatrixLookAtLH ( FXMVECTOR Eye FXMVECTOR At FXMVECTOR Up ) zaxis = normal(At - Eye) xaxis = normal(cross(Up, zaxis)) yaxis = cross(zaxis, xaxis)
對應矩陣:
xaxis.x yaxis.x zaxis.x 0 xaxis.y yaxis.y zaxis.y 0 xaxis.z yaxis.z zaxis.z 0 -dot(xaxis, eye) -dot(yaxis, eye) -dot(zaxis, eye) 1
這裏normal指求單位向量,cross指向量叉乘,dot指向量點乘
2.投影空間
如今,咱們有了一個本身的相機座標,與相對於相機座標的經過世界——》局部矩陣轉換後的點。
那麼,咱們須要一個投影方式,將他push到咱們的屏幕上。
投影空間能夠看做一個4棱錐的投射。在書中,咱們須要如下4個變量表示這個錐形:
1.垂直視角域
2.縱橫比
3.近剪裁面
4.遠剪裁面
示例:咱們如今以本身的眼睛爲例,眼睛看向的方向爲z軸,頭的向上的方向爲y軸,肩膀的方向爲x軸。
很明顯,眼睛向上的視角是有限的,你固然不可能看到額頭上有什麼,這個視角,就是垂直視角域。那麼,水平視角域怎麼處理呢?它使用了一個縱橫比,就是你水平能夠看見的長度的極限除以你垂直能夠看見長度 的極限。至於遠,近裁剪面,定義了你的視野最遠與最近能夠看見的東西。
代碼:
View_Projection = DirectX::XMMatrixPerspectiveFovLH(DirectX::XM_PIDIV2, 800 / (FLOAT)600, 0.01f, 100.0f);
這裏,我定義了垂直視角爲90度,這裏指的是總視角0到180度的,橫縱比爲800/600,最近顯示0.01,最遠顯示100。
好,看一下我定義的邊長爲2的正方形:
{ DirectX::XMFLOAT3(+1.0f, +1.0f, +0.0f), DirectX::XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },//紅 { DirectX::XMFLOAT3(+1.0f, -1.0f, +0.0f), DirectX::XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f) },//黃 { DirectX::XMFLOAT3(-1.0f, -1.0f, +0.0f), DirectX::XMFLOAT4(0.0f, 0.5f, 1.0f, 1.0f) },//藍 { DirectX::XMFLOAT3(-1.0f, +1.0f, +0.0f), DirectX::XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },//綠
以及攝像機的位置:
DirectX::XMVECTOR Eye = DirectX::XMVectorSet(0.0f, 0.0f, 4.0f, 0.0f);; //眼睛座標 DirectX::XMVECTOR Point = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); //看向的位置 DirectX::XMVECTOR Eye_Up = DirectX::XMVectorSet(0.0f, 1.0f, 100.0f, 0.0f);//定義眼睛"向上"的方向
攝像機在(0,0,4)看向(0,0,0)
那麼能夠計算出這個正方形的高度佔顯示總高度的大小,爲4分之1,就是說,咱們的若是程序窗口的高度爲600像素(假設D3D佔滿整個屏幕),那麼顯示的時候這個正方形邊長爲150像素。
其它:
我仍是沒搞懂HLSL中mul的詳細做用,以及投影詳細的數學推導,何時看懂了再寫。