下面介紹的是如何從一個檔案加載一個紋理并把它貼到幾何體上面
在這兒通過頂點定義每個三角形。盡管這會添加更多的代碼用來建立盒子和頂點結構體,但是它在紋理貼圖時會更容易。
在D3D中,使用一個2D(U,V)坐标系來将紋理貼到對象上面。U軸水準地運作到圖像,V軸是垂直運作的,U範圍是0~1(0是圖像的起始處,1是圖像的結尾處),是以圖像的水準長度的一半就是0.5,即使真實圖像長度為256個像素也是如此。
那麼假如将u和v的值改為超過1會發生什麼呢?比如改為2?結果是會重複改紋理,如下所示:
全局聲明
第一個接口是一個對象,該對象用來存儲從檔案加載的紋理。第二個接口是用來存儲采樣狀态資訊的,這在後面解釋。
ID3D11ShaderResourceView* CubesTexture;
ID3D11SamplerState* CubesTexSamplerState;
頂點結構/輸入布局
來看一下頂點結構,用紋理的坐标成員替換了顔色成員。2D紋理的坐标隻需要U和V值,然而,對天空貼圖來說還會用到3D紋理,它會需要一個額外的w值。在這兒也修改了輸入布局來包含紋理坐标的兩個浮點元素,它替換了之前的顔色元素。
struct Vertex //Overloaded Vertex Structure
{
Vertex(){}
Vertex(float x, float y, float z,
float u, float v)
: pos(x,y,z), texCoord(u, v){}
XMFLOAT3 pos;
XMFLOAT2 texCoord;
};
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 },
};
頂點結構/緩沖,索引清單
如果沒有為每個三角形頂點分離出紋理坐标,那麼隻有頂部和底部才會被映射出來,這就是為什麼要在立方體内為每個三角形添加頂點,因為這樣就可以設定它們的每個紋理坐标。可以嘗試在上一章的8個頂點上設定紋理左邊,但是就如剛才所說的,在六個邊上紋理将不會正确的被映射。
由于添加了更多的頂點,是以也需要更新頂點緩沖到24個而不是之前的8個。
Vertex v[] =
{
// Front Face
Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f),
Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 0.0f),
Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 0.0f),
Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f),
// Back Face
Vertex(-1.0f, -1.0f, 1.0f, 1.0f, 1.0f),
Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 1.0f),
Vertex( 1.0f, 1.0f, 1.0f, 0.0f, 0.0f),
Vertex(-1.0f, 1.0f, 1.0f, 1.0f, 0.0f),
// Top Face
Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f),
Vertex(-1.0f, 1.0f, 1.0f, 0.0f, 0.0f),
Vertex( 1.0f, 1.0f, 1.0f, 1.0f, 0.0f),
Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 1.0f),
// Bottom Face
Vertex(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f),
Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 1.0f),
Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 0.0f),
Vertex(-1.0f, -1.0f, 1.0f, 1.0f, 0.0f),
// Left Face
Vertex(-1.0f, -1.0f, 1.0f, 0.0f, 1.0f),
Vertex(-1.0f, 1.0f, 1.0f, 0.0f, 0.0f),
Vertex(-1.0f, 1.0f, -1.0f, 1.0f, 0.0f),
Vertex(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f),
// Right Face
Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 1.0f),
Vertex( 1.0f, 1.0f, -1.0f, 0.0f, 0.0f),
Vertex( 1.0f, 1.0f, 1.0f, 1.0f, 0.0f),
Vertex( 1.0f, -1.0f, 1.0f, 1.0f, 1.0f),
};
DWORD indices[] = {
// Front Face
0, 1, 2,
0, 2, 3,
// Back Face
4, 5, 6,
4, 6, 7,
// Top Face
8, 9, 10,
8, 10, 11,
// Bottom Face
12, 13, 14,
12, 14, 15,
// Left Face
16, 17, 18,
16, 18, 19,
// Right Face
20, 21, 22,
20, 22, 23
};
D3D11_BUFFER_DESC indexBufferDesc;
ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) );
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = sizeof(DWORD) * 12 * 3;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA iinitData;
iinitData.pSysMem = indices;
d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &squareIndexBuffer);
d3d11DevCon->IASetIndexBuffer( squareIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
D3D11_BUFFER_DESC vertexBufferDesc;
ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) );
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof( Vertex ) * 24;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = 0;
vertexBufferDesc.MiscFlags = 0;
從檔案中加載紋理:
D3D11CreateShaderResourceViewFromFile()
現在到了初始化場景函數的底部了,在這兒使用函數D3D11CreateShaderResourceViewFromFile()從一個檔案内加載紋理:
HRESULT D3DX11CreateShaderResourceViewFromFile(
__in ID3D11Device *pDevice,
__in LPCTSTR pSrcFile,
__in D3DX11_IMAGE_LOAD_INFO *pLoadInfo,
__in ID3DX11ThreadPump *pPump,
__out ID3D11ShaderResourceView **ppShaderResourceView,
__out HRESULT *pHResult
);
pDevice
一個指向D3D裝置的指針
pSrcFile
檔案名(若與exe不在同一個目錄,則這裡還可以是位置)
pLoadInfo
一個指向結構體D3DX11_IMAGE_LOAD_INFO的指針,它用來定義紋理如何被加載。設為NULL
pPump
一個指向接口ID3DX11ThreadPump的指針,隻有當想要多線程時才會使用,并且會使程式繼續保持運作,即使此時檔案正在被加載。若設為NULL,則會讓該函數隻有在完成時才會傳回。
ppShaderResourceView
這時一個指向着色器資源視圖(ID3D11ShaderResourceView)的指針,它将存儲該紋理的資料
pHResult
這時一個傳回指針,它會存儲該函數的結果。
hr = D3DX11CreateShaderResourceViewFromFile( d3d11Device, L"braynzar.jpg",
NULL, NULL, &CubesTexture, NULL );
聲明采樣狀态
D3D11_SAMPLER_DESC
這兒聲明采樣狀态,或者着色器如何渲染紋理。建立D3D11_SAMPLER_DESC對象:
typedef struct D3D11_SAMPLER_DESC {
D3D11_FILTER Filter;
D3D11_TEXTURE_ADDRESS_MODE AddressU;
D3D11_TEXTURE_ADDRESS_MODE AddressV;
D3D11_TEXTURE_ADDRESS_MODE AddressW;
FLOAT MipLODBias;
UINT MaxAnisotropy;
D3D11_COMPARISON_FUNC ComparisonFunc;
FLOAT BorderColor[4];
FLOAT MinLOD;
FLOAT MaxLOD;
} D3D11_SAMPLER_DESC;
Filter
一個D3D11_FILTER的枚舉類型,說明了要用到的過濾方法。
AddressU
一個D3D11_TEXTURE_ADDRESS_MODE的枚舉類型,說明了當u值大于1或小于0時要做什麼
AddressV
一個D3D11_TEXTURE_ADDRESS_MODE的枚舉類型,說明了當V值大于1或小于0時要做什麼
AddressW
一個D3D11_TEXTURE_ADDRESS_MODE的枚舉類型,說明了當w值大于或小于0時要做什麼
MipLODBias
已經計算好的貼圖級别的偏移量。比如,若d3d計算一個貼圖應該在貼圖級别3被采樣并且MipLODBias是2,那麼紋理将會在貼圖級别5被采樣。
MaxAnisotropy
若将Filter設為D3D11_FILTER_ANISOTROPIC或D3D11_FILTER_COMPARISON_ANISOTROPIC,則會使用clamping值,有效值位于1~16之間。
ComparisonFunc
一個枚舉類型的D3D11_COMPARISON_FUNC。這會将該紋理的采樣貼圖資料和另外個貼圖采樣資料進行對比。
BorderColor[4]
若AddressU,V或W任意一個指定為D3D11_TEXTURE_ADDRESS_BORDER,若u,v或w比1大或比0小,那麼這就表示位于紋理和三角形邊緣的顔色空間。
MinLOD
這是要用到的最低的紋理貼圖級别,其中0是最詳細和最大的一個。
MaxLOD
這是用到的最大的紋理貼圖級别,其中0是最詳細和最大的。若要使用是以的貼圖,那麼要指定一個非常大的數字,比如FLT_MAX。
若任意一個成員沒有填充,則預設值會被用到:
Filter MIN_MAG_MIP_LINEAR
AddressU Clamp
AddressV Clamp
AddressW Clamp
MinLOD -3.402823466e+38F (-FLT_MAX)
MaxLOD 3.402823466e+38F (FLT_MAX)
MipMapLODBias 0.0f
MaxAnisotropy 16
ComparisonFunc Never
BorderColor float4(0.0f,0.0f,0.0f,0.0f)
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;
頂點結構/輸入布局
(ID3D11Device::CreateSamplerState())
在描述完采樣之後,需要建立它。可使用函數CreateSamplerState()來做這些事,這裡第一個參數是描述的采樣狀态,第二個參數是将采樣狀态放入的接口。
hr = d3d11Device->CreateSamplerState( &sampDesc, &CubesTexSamplerState );
發送采樣狀态和紋理到着色器
ID3D11DeviceContext::PSSetShaderResources()
ID3D11DeviceContext::PSSetSamplers()
若想要,隻需要讓每個要使用該采樣狀态和紋理的對象組将采樣狀态和紋理發送到像素着色器一次,但是大多數情況下,對象不止一個紋理,是以每個對象就不能值設定一次了。由于是PS用到這些資訊,可以調用ID3D11DeviceContext::PSSetShaderResources()和ID3D11DeviceContext::PSSetSamplers()來完成。
ID3D11DeviceContext::PSSetShaderResources()的第一個參數是槽号,會将着色器發送進去。第二個參數是紋理矩陣的元素數量。因為隻發送一個紋理,是以這裡設為0,但實際上是可以發送一個紋理數組到着色器的。第三個參數是ID3D11ShaderResourceView的數組。
除了不是發送一個ID3D11ShaderResourceView的數組,而是發送一個ID3D11SamplerState的數組之外,PSSetSamplers也是類似的,這裡隻有一個采樣狀态,是以隻需要發送一個就好:
void DrawScene()
{
//Clear our backbuffer
float bgColor[4] = {(0.0f, 0.0f, 0.0f, 0.0f)};
d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor);
//Refresh the Depth/Stencil view
d3d11DevCon->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0);
//Set the WVP matrix and send it to the constant buffer in effect file
WVP = cube1World * camView * camProjection;
cbPerObj.WVP = XMMatrixTranspose(WVP);
d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 );
d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer );
///**************new**************
d3d11DevCon->PSSetShaderResources( 0, 1, &CubesTexture );
d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState );
///**************new**************
//Draw the first cube
d3d11DevCon->DrawIndexed( 36, 0, 0 );
WVP = cube2World * camView * camProjection;
cbPerObj.WVP = XMMatrixTranspose(WVP);
d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 );
d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer );
///**************new**************
d3d11DevCon->PSSetShaderResources( 0, 1, &CubesTexture );
d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState );
///**************new**************
//Draw the second cube
d3d11DevCon->DrawIndexed( 36, 0, 0 );
//Present the backbuffer to the screen
SwapChain->Present(0, 0);
}
代碼執行個體如下:
#include "stdafx.h"
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dx11.lib")
#pragma comment(lib, "d3dx10.lib")
#include <windows.h>
#include "Resource.h"
#include <d3d11.h>
#include <d3dx11.h>
#include <d3dx10.h>
#include <xnamath.h>
//全局描述符
IDXGISwapChain* SwapChain;
ID3D11Device* d3d11Device;
ID3D11DeviceContext* d3d11DevCon;
ID3D11RenderTargetView* renderTargetView;
//索引緩沖
ID3D11Buffer* squareIndexBuffer;
ID3D11Buffer* squareVertBuffer;
//深度值-20170927
ID3D11DepthStencilView* depthStencilView;
ID3D11Texture2D* depthStencilBuffer;
//着色器
ID3D11Buffer* triangleVertBuffer;
ID3D11VertexShader* VS;
ID3D11PixelShader* PS;
ID3D10Blob* VS_Buffer;
ID3D10Blob* PS_Buffer;
ID3D11InputLayout* vertLayout;
///
ID3D11Buffer* cbPerObjectBuffer;
///
ID3D11ShaderResourceView* CubesTexture;
ID3D11SamplerState* CubesTexSamplerState;
// 設定線框
//ID3D11RasterizerState* WireFrame;
#if 0
float red = 0.0f;
float green = 0.0f;
float blue = 0.0f;
int colormodr = 1;
int colormodg = 1;
int colormodb = 1;
#endif
/
LPCTSTR WndClassName = "firstwindow";
HWND hwnd = NULL;
HRESULT hr;
const int Width = 800; //設定寬
const int Height = 800; // 設定高
///四個空間以及相機屬性
XMMATRIX WVP;
//立方體
XMMATRIX cube1World;
XMMATRIX cube2World;
//
XMMATRIX World;
XMMATRIX camView;
XMMATRIX camProjection;
XMVECTOR camPosition;
XMVECTOR camTarget;
XMVECTOR camUp;
//相機
XMMATRIX Rotation;
XMMATRIX Scale;
XMMATRIX Translation;
float rot = 0.01f;
//函數聲明
bool InitializeDirect3d11App(HINSTANCE hInstance);
//void ReleaseObjects();
void CleanUp();
bool InitScene();
void UpdateScene();
void DrawScene();
// 初始化視窗
bool InitializeWindow(HINSTANCE hInstance,
int ShowWnd,
int width, int height,
bool windowed);
//初始化消息循環函數
int messageloop();
//初始化視窗回調過程。Windows API是事件驅動型的程式設計模型。在該函數中捕獲Windows消息,比如一個按鍵按下(也叫事件)以及程式操作流程。
LRESULT CALLBACK WndProc(HWND hWnd,
UINT msg,
WPARAM wParam,
LPARAM lParam);
//建立效果常量緩沖的結構體
struct cbPerObject
{
XMMATRIX WVP;
};
cbPerObject cbPerObj;
//頂點結構體以及頂點布局(輸入布局)
#if 1
struct Vertex
{
Vertex(){}
Vertex(float x, float y, float z,
float u, float v)
:pos(x, y, z), texCoord(u, v){}
XMFLOAT3 pos;
XMFLOAT2 texCoord;
};
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 },
};
UINT numElements = ARRAYSIZE(layout);
#else
struct Vertex
{
Vertex(){}
Vertex(float x, float y, float z,
float cr, float cg, float cb, float ca)
:pos(x, y, z), color(cr, cg, cb, ca){}
XMFLOAT3 pos;
XMFLOAT4 color;
};
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);
#endif
//主函數,傳入應用程式句柄hInstance,前一個應用程式句柄hPrevInstance,傳給函數處理的指令行lpCmdLine以及視窗顯示方式的nShowCmd
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd)
{
//建立并注冊視窗
if (!InitializeWindow(hInstance, nShowCmd, Width, Height, true))
{
MessageBox(0, "Window Initilization - Failed", "Error", MB_OK);
return 0;
}
/new
if (!InitializeDirect3d11App(hInstance)) // 初始化D3D
{
MessageBox(0, "Direct3D Initialization - Failed", "Error", MB_OK);
return 0;
}
if (!InitScene())
{
MessageBox(0, "Scene Initialization - Failed", "Error", MB_OK);
return 0;
}
messageloop();
CleanUp();
//ReleaseObjects();
return 0;
}
// windowed 若為true則為視窗模式顯示,若為false則為全屏模式顯示
bool InitializeWindow(HINSTANCE hInstance,
int ShowWnd,
int width, int height,
bool windowed)
{
/*typedef struct _WNDCLASS{
UINT cbSize;
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HANDLE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
}WNDCLASS;
*/
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX); //window類的大小
/********windows類風格
*CS_CLASSDC 一個使用該類建立的在所有視窗間共享的裝置上下文
*CS_DBLCLKS 在視窗上使能輕按兩下功能
*CS_HREDRAW 若視窗的寬度有改變或者視窗水準地移動,視窗将會重新整理
*CS_NOCLOSE 視窗菜單上禁止關閉選項
*CS_OWNDC 為每個視窗建立自己的裝置上下文。正好與CS_CLASSDC相反
*CS_PARENTDC 這會設定建立的子視窗的剪裁四邊形到父視窗,這允許子視窗能夠在父視窗上繪畫
*CS_VERDRAW 若在視窗的高度或視窗在垂直方向有移動視窗會重繪
**/
wc.style = CS_HREDRAW | CS_VREDRAW;
//lpfnWndProc是一個指向處理視窗消息函數的指針,設定視窗處理函數的函數名WndProc
wc.lpfnWndProc = WndProc;
//cbClsExtra是WNDCLASSEX之後額外申請的位元組數
wc.cbClsExtra = NULL;
//cbWndExtra指定視窗執行個體之後所申請的位元組數
wc.cbWndExtra = NULL;
//目前視窗應用程式的句柄,通過給函數GetModuleHandle()函數第一個參數傳入NULL可擷取目前視窗應用程式。
wc.hInstance = hInstance;
//hIcon用來指定視窗标題欄左上角的圖示。以下是一些标準圖示:
/*
*IDI_APPLICATION 預設應用程式圖示
*IDI_HAND 手形狀的圖示
*IDI_EXCLAMATION 感歎号圖示
*IDI_INFORMATION 星号圖示
*IDI_QUESTION 問号圖示
*IDI_WINLOGO 若使用的是XP則是預設應用程式圖示,否則是視窗logo
*/
wc.hIcon = LoadIcon(NULL, (LPCTSTR)IDI_SMALL);
/*定義光标圖示
*IDC_APPSTARTING 标準箭頭以及小型沙漏光标
*IDC_ARROW 标準箭頭光标
*IDC_CROSS 十字線光标
*IDC_HAND 手型光标
*IDC_NO 斜線圈光标
*IDC_WAIT 沙漏光标
*/
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
//hbrBackground是一個刷子的句柄,可使得背景黑色。
wc.hbrBackground = (HBRUSH)(COLOR_BTNSHADOW + 2);
//附加到視窗的菜單名字,不需要的話設定為NULL
wc.lpszMenuName = NULL;
//對類進行命名
wc.lpszClassName = WndClassName;
//指定工作列的圖示,使用上面的IDI_圖示
wc.hIconSm = LoadIcon(NULL, (LPCTSTR)IDI_MYICON);
//注冊類。若失敗則會獲得一個錯誤,若成功,則繼續建立視窗
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, "Error registering class", "Error", MB_OK | MB_ICONERROR);
return 1;
}
//建立視窗
hwnd = CreateWindowEx(NULL, WndClassName, "Rotating Cube", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width,
height, NULL, NULL, hInstance, NULL);
if (!hwnd)
{
MessageBox(NULL, "Error registering class", "Error", MB_OK | MB_ICONERROR);
return 1;
}
//BOOL ShowWindow(HWND hWnd, int nCmdShow);
//BOOL UpdateWindow(HWND hWnd);
ShowWindow(hwnd, ShowWnd);
UpdateWindow(hwnd);// 發送WM_PAINT消息到視窗過程,若視窗客戶區沒有任何東西要顯示,則不發送消息。傳回true,繼續運作到mainloop中去。
return true;
}
bool InitializeDirect3d11App(HINSTANCE hInstance)
{
//聲明緩沖
DXGI_MODE_DESC bufferDesc;
ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC));
bufferDesc.Width = Width;
bufferDesc.Height = Height;
bufferDesc.RefreshRate.Numerator = 60;
bufferDesc.RefreshRate.Denominator = 1;
bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
//聲明交換鍊
DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
swapChainDesc.BufferDesc = bufferDesc;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 1;
swapChainDesc.OutputWindow = hwnd;
swapChainDesc.Windowed = TRUE;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
//建立交換鍊
D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, NULL, NULL,
D3D11_SDK_VERSION, &swapChainDesc, &SwapChain, &d3d11Device, NULL, &d3d11DevCon);
//建立後緩沖
ID3D11Texture2D* BackBuffer;
SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&BackBuffer);
//建立渲染目标
d3d11Device->CreateRenderTargetView(BackBuffer, NULL, &renderTargetView);
BackBuffer->Release();
//建立深度模闆緩沖
D3D11_TEXTURE2D_DESC depthStencilDesc;
depthStencilDesc.Width = Width;
depthStencilDesc.Height = Height;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.ArraySize = 1;
depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthStencilDesc.SampleDesc.Count = 1;
depthStencilDesc.SampleDesc.Quality = 0;
depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; //綁定到OM
depthStencilDesc.CPUAccessFlags = 0;
depthStencilDesc.MiscFlags = 0;
//建立深度模闆視圖
d3d11Device->CreateTexture2D(&depthStencilDesc, NULL, &depthStencilBuffer);
d3d11Device->CreateDepthStencilView(depthStencilBuffer, NULL, &depthStencilView);
//設定渲染目标
d3d11DevCon->OMSetRenderTargets(1, &renderTargetView, depthStencilView);
return true;
}
void CleanUp()
{
SwapChain->Release();
d3d11Device->Release();
d3d11DevCon->Release();
renderTargetView->Release();
squareVertBuffer->Release();
squareIndexBuffer->Release();
//triangleVertBuffer->Release();
VS->Release();
PS->Release();
VS_Buffer->Release();
PS_Buffer->Release();
vertLayout->Release();
depthStencilView->Release();
depthStencilBuffer->Release();
//
cbPerObjectBuffer->Release();
//釋放線框
//WireFrame->Release();
}
void ReleaseObjects()
{
//釋放建立的COM對象
SwapChain->Release();
d3d11Device->Release();
d3d11DevCon->Release();
}
bool InitScene()
{
//編譯着色器
hr = D3DX11CompileFromFile("Effects.fx", 0, 0, "VS", "vs_4_0", 0, 0, 0, &VS_Buffer, 0, 0);
hr = D3DX11CompileFromFile("Effects.fx", 0, 0, "PS", "ps_4_0", 0, 0, 0, &PS_Buffer, 0, 0);
//建立着色器對象
hr = d3d11Device->CreateVertexShader(VS_Buffer->GetBufferPointer(), VS_Buffer->GetBufferSize(), NULL, &VS);
hr = d3d11Device->CreatePixelShader(PS_Buffer->GetBufferPointer(), PS_Buffer->GetBufferSize(), NULL, &PS);
//設定頂點和像素着色器
d3d11DevCon->VSSetShader(VS, 0, 0);
d3d11DevCon->PSSetShader(PS, 0, 0);
//建立頂點緩沖
Vertex v[] = {
#if 1
// Front Face
Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f),
Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 0.0f),
Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 0.0f),
Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f),
// Back Face
Vertex(-1.0f, -1.0f, 1.0f, 1.0f, 1.0f),
Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 1.0f),
Vertex( 1.0f, 1.0f, 1.0f, 0.0f, 0.0f),
Vertex(-1.0f, 1.0f, 1.0f, 1.0f, 0.0f),
// Top Face
Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f),
Vertex(-1.0f, 1.0f, 1.0f, 0.0f, 0.0f),
Vertex(1.0f, 1.0f, 1.0f, 1.0f, 0.0f),
Vertex(1.0f, 1.0f, -1.0f, 1.0f, 1.0f),
// Bottom Face
Vertex(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f),
Vertex(1.0f, -1.0f, -1.0f, 0.0f, 1.0f),
Vertex(1.0f, -1.0f, 1.0f, 0.0f, 0.0f),
Vertex(-1.0f, -1.0f, 1.0f, 1.0f, 0.0f),
// Left Face
Vertex(-1.0f, -1.0f, 1.0f, 0.0f, 1.0f),
Vertex(-1.0f, 1.0f, 1.0f, 0.0f, 0.0f),
Vertex(-1.0f, 1.0f, -1.0f, 1.0f, 0.0f),
Vertex(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f),
// Right Face
Vertex(1.0f, -1.0f, -1.0f, 0.0f, 1.0f),
Vertex(1.0f, 1.0f, -1.0f, 0.0f, 0.0f),
Vertex(1.0f, 1.0f, 1.0f, 1.0f, 0.0f),
Vertex(1.0f, -1.0f, 1.0f, 1.0f, 1.0f),
#else
Vertex(-1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f),
Vertex(-1.0f, +1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f),
Vertex(+1.0f, +1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f),
Vertex(+1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f),
Vertex(-1.0f, -1.0f, +1.0f, 0.0f, 1.0f, 1.0f, 1.0f),
Vertex(-1.0f, +1.0f, +1.0f, 1.0f, 1.0f, 1.0f, 1.0f),
Vertex(+1.0f, +1.0f, +1.0f, 1.0f, 0.0f, 1.0f, 1.0f),
Vertex(+1.0f, -1.0f, +1.0f, 1.0f, 0.0f, 0.0f, 1.0f),
#endif
};
DWORD indices[] = {
// Front Face
0, 1, 2,
0, 2, 3,
// Back Face
4, 5, 6,
4, 6, 7,
// Top Face
8, 9, 10,
8, 10, 11,
// Bottom Face
12, 13, 14,
12, 14, 15,
// Left Face
16, 17, 18,
16, 18, 19,
// Right Face
20, 21, 22,
20, 22, 23
};
D3D11_BUFFER_DESC indexBufferDesc;
ZeroMemory(&indexBufferDesc, sizeof(indexBufferDesc));
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = sizeof(DWORD) * 12 * 3;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA iinitData;
iinitData.pSysMem = indices;
d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &squareIndexBuffer);
d3d11DevCon->IASetIndexBuffer(squareIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
D3D11_BUFFER_DESC vertexBufferDesc;
ZeroMemory(&vertexBufferDesc, sizeof(vertexBufferDesc));
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof(Vertex) * 24;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = 0;
vertexBufferDesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA vertexBufferData;
ZeroMemory(&vertexBufferData, sizeof(vertexBufferData));
vertexBufferData.pSysMem = v;
hr = d3d11Device->CreateBuffer(&vertexBufferDesc, &vertexBufferData, &squareVertBuffer);
//設定頂點緩沖
UINT stride = sizeof(Vertex);
UINT offset = 0;
d3d11DevCon->IASetVertexBuffers(0, 1, &squareVertBuffer, &stride, &offset);
//建立輸入布局
d3d11Device->CreateInputLayout(layout, numElements, VS_Buffer->GetBufferPointer(),
VS_Buffer->GetBufferSize(), &vertLayout);
//設定輸入布局
d3d11DevCon->IASetInputLayout(vertLayout);
//設定圖元拓撲
d3d11DevCon->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
//建立視口
D3D11_VIEWPORT viewport;
ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = Width;
viewport.Height = Height;
viewport.MinDepth = 0;
viewport.MaxDepth = 1;
//設定視口
d3d11DevCon->RSSetViewports(1, &viewport);
//建立緩沖用來發送到效果檔案的cbuffer
D3D11_BUFFER_DESC cbbd;
ZeroMemory(&cbbd, sizeof(D3D11_BUFFER_DESC));
cbbd.Usage = D3D11_USAGE_DEFAULT;
cbbd.ByteWidth = sizeof(cbPerObject);
cbbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
cbbd.CPUAccessFlags = 0;
cbbd.MiscFlags = 0;
hr = d3d11Device->CreateBuffer(&cbbd, NULL, &cbPerObjectBuffer);
//相機資訊
//相機資訊
camPosition = XMVectorSet(0.0f, 3.0f, -8.0f, 0.0f);
//camPosition = XMVectorSet(0.0f, 0.0f, -0.5f, 0.0f);
camTarget = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
camUp = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
//設定視圖矩陣
camView = XMMatrixLookAtLH(camPosition, camTarget, camUp);
//設定投影矩陣
camProjection = XMMatrixPerspectiveFovLH(0.4f*3.14f, (float)Width / Height, 1.0f, 1000.0f);
#if 1
hr = D3DX11CreateShaderResourceViewFromFile(d3d11Device, "Eye.jpg", NULL, NULL, &CubesTexture, NULL);
//配置采樣狀态
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 = d3d11Device->CreateSamplerState(&sampDesc, &CubesTexSamplerState);
#else
//設定線框
D3D11_RASTERIZER_DESC wfdesc;
ZeroMemory(&wfdesc, sizeof(D3D11_RASTERIZER_DESC));
wfdesc.FillMode = D3D11_FILL_WIREFRAME;
wfdesc.CullMode = D3D11_CULL_NONE;
hr = d3d11Device->CreateRasterizerState(&wfdesc, &WireFrame);
d3d11DevCon->RSSetState(WireFrame);
#endif
return true;
}
void UpdateScene()
{
// 更新場景顔色
//讓立方體旋轉起來
rot += 0.00005f;
if (rot > 6.26f)
rot = 0.0f;
//複位cube1World
cube1World = XMMatrixIdentity();
//定義cube1的世界空間矩陣
XMVECTOR rotaxis = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
Rotation = XMMatrixRotationAxis(rotaxis, rot);
Translation = XMMatrixTranslation(0.0f, 0.0f, 4.0f);
//用轉換設定cube1的世界空間
cube1World = Translation* Rotation;
//複位cube2World
cube2World = XMMatrixIdentity();
//定義cube2的世界空間矩陣
Rotation = XMMatrixRotationAxis(rotaxis, -rot);
Scale = XMMatrixScaling(1.3f, 1.3f, 1.3f);
//設定cube2的世界空間矩陣
cube2World = Rotation * Scale;
#if 0
red += colormodr * 0.00005f;
green += colormodg * 0.00002f;
blue += colormodb * 0.00001f;
if (red >= 1.0f || red <= 0.0f)
colormodr *= -1;
if (green >= 1.0f || green <= 0.0f)
colormodg *= -1;
if (blue >= 1.0f || blue <= 0.0f)
colormodb *= -1;
#endif
}
void DrawScene()
{
//将更新的顔色填充後緩沖
// D3DXCOLOR bgColor(red, green, blue, 1.0f);
float bgColor[4] = {(0.0f, 0.0f, 0.0f, 0.0f)};
d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor);
//重新整理深度模闆視圖
d3d11DevCon->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
//設定WVP矩陣并将它送到效果檔案中的常量緩沖中
WVP = cube1World * camView * camProjection;
cbPerObj.WVP = XMMatrixTranspose(WVP);
d3d11DevCon->UpdateSubresource(cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0);
d3d11DevCon->VSSetConstantBuffers(0, 1, &cbPerObjectBuffer);
///
d3d11DevCon->PSSetShaderResources(0, 1, &CubesTexture);
d3d11DevCon->PSSetSamplers(0, 1, &CubesTexSamplerState);
//繪制第一個立方體
d3d11DevCon->DrawIndexed(36, 0, 0);
//設定世界/視圖/投影矩陣,随後發送到效果檔案的常量緩沖中
//World = XMMatrixIdentity();
WVP = cube2World * camView * camProjection;
cbPerObj.WVP = XMMatrixTranspose(WVP);
d3d11DevCon->UpdateSubresource(cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0);
d3d11DevCon->VSSetConstantBuffers(0, 1, &cbPerObjectBuffer);
///
d3d11DevCon->PSSetShaderResources(0, 1, &CubesTexture);
d3d11DevCon->PSSetSamplers(0, 1, &CubesTexSamplerState);
//繪制第二個立方體
d3d11DevCon->DrawIndexed(36, 0, 0);
//繪制三角形
//d3d11DevCon->DrawIndexed(6, 0, 0);
//畫三角形
//d3d11DevCon->Draw(3, 0);
//将後緩沖呈現到螢幕
SwapChain->Present(0, 0);
}
int messageloop(){
MSG msg;
ZeroMemory(&msg, sizeof(MSG));//清除結構體被設為NULL。
while (true){
//使用PeekMessage()檢查是否有消息傳進來
/*LPMSG lpMsg 消息結構體的指針
*HWND hWnd 發送消息的視窗句柄。若設為NULL,那麼它會從目前程式中接收來自任何一個視窗的消息
*UINT wMsgFilterMin 指定消息範圍内第一個要檢查的消息的值。若wMsgFilterMin和wMsgFilterMax都設為0,那麼PeekMessage将會檢查素有的消息
*UINT wMsgFilterMax 指定消息範圍内最後一個要檢測的消息的值
*UINT wRemoveMsg 指定消息的處理方式。若設定為PM_REMOVE,則在讀取之後會被删除
*/
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
break;
}
//若消息為視窗消息,則解析并分發它。TranslateMessage()将會讓視窗做一些解析,類似鍵盤的虛拟鍵值轉換到字元形式。
//而DispatchMessage()則發送消息到視窗過程WndProc。
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else //若沒有視窗消息,則運作遊戲
{
// run game code
UpdateScene();
DrawScene();
}
}
return msg.wParam;
}
//視窗消息處理函數
//HWND hwnd 擷取消息的視窗句柄
//UINT msg 消息的内容
/*
*WM_ACTIVE 當視窗激活時發送的消息
*WM_CLOSE 當視窗關閉時發送的消息
*WM_CREATE 當視窗建立時發送的消息
*WM_DESTROY 當視窗銷毀時發送的消息
*/
//wParam和lParam時消息的額外資訊。使用wParam來檢測鍵盤輸入消息
LRESULT CALLBACK WndProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam
)
{
// 這是事件檢測消息的地方,若escape鍵被按下,會顯示一個消息框,詢問是否真的退出。若點選yes,則程式關閉。若不點選,則消息框關閉。若消息包含WM_DESTROY
// 則意味着視窗正在被銷毀,傳回0并且程式關閉
switch (msg)
{
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
{
if (MessageBox(0, "Are you sure you want to exit?",
"Really?", MB_YESNO | MB_ICONASTERISK) == IDYES)
{
DestroyWindow(hwnd);
}
return 0;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
break;
}
//調用預設視窗過程函數
return DefWindowProc(hwnd,
msg,
wParam,
lParam);
}
效果檔案:
cbuffer cbPerObject
{
float4x4 WVP;
};
Texture2D ObjTexture;
SamplerState ObjSamplerState;
struct VS_OUTPUT
{
float4 Pos : SV_POSITION;
float2 TexCoord : TEXCOORD;
};
VS_OUTPUT VS(float4 inPos : POSITION, float2 inTexCoord : TEXCOORD)
{
VS_OUTPUT output;
output.Pos = mul(inPos, WVP);
output.TexCoord = inTexCoord;
return output;
}
float4 PS(VS_OUTPUT input) : SV_TARGET
{
return ObjTexture.Sample( ObjSamplerState, input.TexCoord );
}
将eye.jpg與源代碼放在同一個目錄即可:
效果:
參考網址