DirectX/10.0/Direct3D/字型引擎
在任何應用程式中,將文字寫入螢幕都是一個非常重要的功能。在 DirectX 11 中渲染文字需要您首先了解如何渲染 2D 影像。由於我們在之前的教程中涵蓋了這個主題,因此本教程將在此基礎上進行構建。
您將需要的第一個東西是您自己的字型影像。我用我想要的字元構建了一個非常簡單的字型,並將其放在一個 1024x16 的 DDS 紋理上
如您所見,它包含了所需的基本字元,並且它們都位於單個紋理檔案中。我們現在可以構建一個簡單的字型引擎,它使用紋理中的索引來根據需要將單個字元繪製到螢幕上。我們在 DirectX 11 中是如何做到這一點的呢?我們是透過構建由兩個三角形組成的正方形,然後將紋理中的單個字元渲染到該正方形上。因此,如果我們有一個句子,我們會找出我們需要的字元,為每個字元建立一個正方形,然後將字元渲染到這些正方形上。完成之後,我們將所有正方形渲染到螢幕上,形成該句子。這與我們在之前的教程中用於將 2D 影像渲染到螢幕上的方法相同。
現在,為了索引紋理,我們需要一個文字檔案,該檔案提供紋理中每個字元的位置和大小。此文字檔案將允許字型引擎快速從紋理中獲取它需要的畫素,以形成可以渲染的字元。以下是此字型紋理的索引檔案。
該檔案的格式為:[字元的 ASCII 值] [字元] [左紋理 U 座標] [右紋理 U 座標] [字元的畫素寬度]
32 0.0 0.0 0
33 ! 0.0 0.000976563 1
34 " 0.00195313 0.00488281 3
35 # 0.00585938 0.0136719 8
36 $ 0.0146484 0.0195313 5
37 % 0.0205078 0.0302734 10
38 & 0.03125 0.0390625 8
39 ' 0.0400391 0.0410156 1
40 ( 0.0419922 0.0449219 3
41 ) 0.0458984 0.0488281 3
42 * 0.0498047 0.0546875 5
43 + 0.0556641 0.0625 7
44 , 0.0634766 0.0644531 1
45 - 0.0654297 0.0683594 3
46 . 0.0693359 0.0703125 1
47 / 0.0712891 0.0751953 4
48 0 0.0761719 0.0820313 6
49 1 0.0830078 0.0859375 3
50 2 0.0869141 0.0927734 6
51 3 0.09375 0.0996094 6
52 4 0.100586 0.106445 6
53 5 0.107422 0.113281 6
54 6 0.114258 0.120117 6
55 7 0.121094 0.126953 6
56 8 0.12793 0.133789 6
57 9 0.134766 0.140625 6
58 : 0.141602 0.142578 1
59 ; 0.143555 0.144531 1
60 0.160156 0.166016 6
63 ? 0.166992 0.171875 5
64 @ 0.172852 0.18457 12
65 A 0.185547 0.194336 9
66 B 0.195313 0.202148 7
67 C 0.203125 0.209961 7
68 D 0.210938 0.217773 7
69 E 0.21875 0.225586 7
70 F 0.226563 0.232422 6
71 G 0.233398 0.241211 8
72 H 0.242188 0.249023 7
73 I 0.25 0.250977 1
74 J 0.251953 0.256836 5
75 K 0.257813 0.265625 8
76 L 0.266602 0.272461 6
77 M 0.273438 0.282227 9
78 N 0.283203 0.290039 7
79 O 0.291016 0.298828 8
80 P 0.299805 0.306641 7
81 Q 0.307617 0.31543 8
82 R 0.316406 0.323242 7
83 S 0.324219 0.331055 7
84 T 0.332031 0.338867 7
85 U 0.339844 0.34668 7
86 V 0.347656 0.356445 9
87 W 0.357422 0.370117 13
88 X 0.371094 0.37793 7
89 Y 0.378906 0.385742 7
90 Z 0.386719 0.393555 7
91 [ 0.394531 0.396484 2
92 \ 0.397461 0.401367 4
93 ] 0.402344 0.404297 2
94 ^ 0.405273 0.410156 5
95 _ 0.411133 0.417969 7
96 ` 0.418945 0.420898 2
97 a 0.421875 0.426758 5
98 b 0.427734 0.432617 5
99 c 0.433594 0.438477 5
100 d 0.439453 0.444336 5
101 e 0.445313 0.450195 5
102 f 0.451172 0.455078 4
103 g 0.456055 0.460938 5
104 h 0.461914 0.466797 5
105 i 0.467773 0.46875 1
106 j 0.469727 0.472656 3
107 k 0.473633 0.478516 5
108 l 0.479492 0.480469 1
109 m 0.481445 0.490234 9
110 n 0.491211 0.496094 5
111 o 0.49707 0.501953 5
112 p 0.50293 0.507813 5
113 q 0.508789 0.513672 5
114 r 0.514648 0.517578 3
115 s 0.518555 0.523438 5
116 t 0.524414 0.527344 3
117 u 0.52832 0.533203 5
118 v 0.53418 0.539063 5
119 w 0.540039 0.548828 9
120 x 0.549805 0.554688 5
121 y 0.555664 0.560547 5
122 z 0.561523 0.566406 5
123 { 0.567383 0.570313 3
124 | 0.571289 0.572266 1
125 } 0.573242 0.576172 3
126 ~ 0.577148 0.583984 7
有了索引檔案和紋理檔案,我們就有了構建字型引擎所需的一切。如果您需要構建自己的索引檔案,請確保每個字元之間只用空格分隔,並且您可以自己編寫點陣圖解析器,根據沒有空白的位置建立 TU 和 TV 座標。
請記住,不同的使用者將在不同的解析度下執行您的應用程式。一個大小的字型無法在所有解析度下都清晰可讀。因此,您可能需要製作 3-4 種不同的字型大小,並在特定解析度下使用特定大小的字型來解決此問題。
由於我們希望將字型功能封裝在它自己的類集中,因此我們將向我們的框架新增一些新類。更新後的框架如下所示
在本教程中,我們有三個新類,分別稱為 TextClass、FontClass 和 FontShader 類。FontShaderClass 是用於渲染字型的著色器,類似於 TextureShaderClass 在之前教程中用於渲染點陣圖影像的方式。FontClass 儲存字型資料並構造渲染字串所需的頂點緩衝區。TextClass 包含每個需要渲染到螢幕的文字字串集的頂點和索引緩衝區,它使用 FontClass 建立字串的頂點緩衝區,然後使用 FontShaderClass 渲染這些緩衝區。
我們將首先檢視新的 FontClass。此類將處理字型的紋理、來自文字檔案的字型資料以及用於使用字型資料構建頂點緩衝區的函式。儲存單個句子字型資料的頂點緩衝區將位於 TextClass 中,而不是此類中。
////////////////////////////////////////////////////////////////////////////////
// Filename: fontclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _FONTCLASS_H_
#define _FONTCLASS_H_
//////////////
// INCLUDES //
//////////////
#include <d3d11.h>
#include <d3dx10math.h>
#include <fstream>
using namespace std;
///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "textureclass.h"
////////////////////////////////////////////////////////////////////////////////
// Class name: FontClass
////////////////////////////////////////////////////////////////////////////////
class FontClass
{
private:
FontType 結構用於儲存從字型索引檔案中讀取的索引資料。left 和 right 是 TU 紋理座標。size 是字元的畫素寬度。
struct FontType
{
float left, right;
int size;
};
VertexType 結構用於實際的頂點資料,這些資料用於構建渲染文字字元的正方形。單個字元將需要兩個三角形才能構成一個正方形。這些三角形將只有位置和紋理資料。
struct VertexType
{
D3DXVECTOR3 position;
D3DXVECTOR2 texture;
};
public:
FontClass();
FontClass(const FontClass&);
~FontClass();
bool Initialize(ID3D11Device*, char*, WCHAR*);
void Shutdown();
ID3D11ShaderResourceView* GetTexture();
BuildVertexArray 將處理構建和返回一個三角形頂點陣列,該陣列將渲染作為輸入傳遞給此函式的字元句子。此函式將由新的 TextClass 呼叫,以構建它需要渲染的所有句子的頂點陣列。
void BuildVertexArray(void*, char*, float, float); private: bool LoadFontData(char*); void ReleaseFontData(); bool LoadTexture(ID3D11Device*, WCHAR*); void ReleaseTexture(); private: FontType* m_Font; TextureClass* m_Texture; }; #endif
/////////////////////////////////////////////////////////////////////////////// // Filename: fontclass.cpp /////////////////////////////////////////////////////////////////////////////// #include "fontclass.h"
類建構函式將 FontClass 的所有私有成員變數初始化為 null。
FontClass::FontClass()
{
m_Font = 0;
m_Texture = 0;
}
FontClass::FontClass(const FontClass& other)
{
}
FontClass::~FontClass()
{
}
Initialize 將載入字型資料和字型紋理。
bool FontClass::Initialize(ID3D11Device* device, char* fontFilename, WCHAR* textureFilename)
{
bool result;
// Load in the text file containing the font data.
result = LoadFontData(fontFilename);
if(!result)
{
return false;
}
// Load the texture that has the font characters on it.
result = LoadTexture(device, textureFilename);
if(!result)
{
return false;
}
return true;
}
Shutdown 將釋放字型資料和字型紋理。
void FontClass::Shutdown()
{
// Release the font texture.
ReleaseTexture();
// Release the font data.
ReleaseFontData();
return;
}
LoadFontData 函式是我們在其中載入包含紋理索引資訊的 fontdata.txt 檔案的地方。
bool FontClass::LoadFontData(char* filename)
{
ifstream fin;
int i;
char temp;
首先,我們建立一個 FontType 結構陣列。陣列的大小設定為 95,因為紋理中有 95 個字元,因此 fontdata.txt 檔案中有 95 個索引。
// Create the font spacing buffer.
m_Font = new FontType[95];
if(!m_Font)
{
return false;
}
現在,我們開啟檔案並將每一行讀取到陣列 m_Font 中。我們只需要讀取紋理 TU 左右座標以及字元的畫素大小。
// Read in the font size and spacing between chars.
fin.open(filename);
if(fin.fail())
{
return false;
}
// Read in the 95 used ascii characters for text.
for(i=0; i> m_Font[i].left;
fin >> m_Font[i].right;
fin >> m_Font[i].size;
}
// Close the file.
fin.close();
return true;
}
ReleaseFontData 函式釋放儲存紋理索引資料的陣列。
void FontClass::ReleaseFontData()
{
// Release the font data array.
if(m_Font)
{
delete [] m_Font;
m_Font = 0;
}
return;
}
LoadTexture 函式將 font.dds 檔案讀取到紋理著色器資源中。這將是我們從中獲取字元並將其寫入它們自己的正方形多邊形以進行渲染的紋理。
bool FontClass::LoadTexture(ID3D11Device* device, WCHAR* filename)
{
bool result;
// Create the texture object.
m_Texture = new TextureClass;
if(!m_Texture)
{
return false;
}
// Initialize the texture object.
result = m_Texture->Initialize(device, filename);
if(!result)
{
return false;
}
return true;
}
ReleaseTexture 函式釋放用於字型的紋理。
void FontClass::ReleaseTexture()
{
// Release the texture object.
if(m_Texture)
{
m_Texture->Shutdown();
delete m_Texture;
m_Texture = 0;
}
return;
}
GetTexture 返回字型紋理介面,以便可以渲染字型圖形。
ID3D11ShaderResourceView* FontClass::GetTexture()
{
return m_Texture->GetTexture();
}
BuildVertexArray 將由 TextClass 呼叫,以使用它作為輸入傳送的文字句子構建頂點緩衝區。這樣,TextClass 中每個需要繪製的句子都有自己的頂點緩衝區,這些緩衝區在建立後可以輕鬆渲染。輸入的 vertices 是指向將在構建後返回給 TextClass 的頂點陣列的指標。輸入的 sentence 是將用於建立頂點陣列的文字句子。輸入變數 drawX 和 drawY 是在螢幕上繪製句子的座標。
void FontClass::BuildVertexArray(void* vertices, char* sentence, float drawX, float drawY)
{
VertexType* vertexPtr;
int numLetters, index, i, letter;
// Coerce the input vertices into a VertexType structure.
vertexPtr = (VertexType*)vertices;
// Get the number of letters in the sentence.
numLetters = (int)strlen(sentence);
// Initialize the index to the vertex array.
index = 0;
以下迴圈現在將構建頂點和索引陣列。它從句子中獲取每個字元併為其建立兩個三角形。然後,它使用 m_Font 陣列(包含 TU 紋理座標和畫素大小)將紋理中的字元對映到這兩個三角形上。建立該字元的多邊形後,它會更新在螢幕上繪製下一個字元的 X 座標。
// Draw each letter onto a quad.
for(i=0; i<numLetters; i++)
{
letter = ((int)sentence[i]) - 32;
// If the letter is a space then just move over three pixels.
if(letter == 0)
{
drawX = drawX + 3.0f;
}
else
{
// First triangle in quad.
vertexPtr[index].position = D3DXVECTOR3(drawX, drawY, 0.0f); // Top left.
vertexPtr[index].texture = D3DXVECTOR2(m_Font[letter].left, 0.0f);
index++;
vertexPtr[index].position = D3DXVECTOR3((drawX + m_Font[letter].size), (drawY - 16), 0.0f); // Bottom right.
vertexPtr[index].texture = D3DXVECTOR2(m_Font[letter].right, 1.0f);
index++;
vertexPtr[index].position = D3DXVECTOR3(drawX, (drawY - 16), 0.0f); // Bottom left.
vertexPtr[index].texture = D3DXVECTOR2(m_Font[letter].left, 1.0f);
index++;
// Second triangle in quad.
vertexPtr[index].position = D3DXVECTOR3(drawX, drawY, 0.0f); // Top left.
vertexPtr[index].texture = D3DXVECTOR2(m_Font[letter].left, 0.0f);
index++;
vertexPtr[index].position = D3DXVECTOR3(drawX + m_Font[letter].size, drawY, 0.0f); // Top right.
vertexPtr[index].texture = D3DXVECTOR2(m_Font[letter].right, 0.0f);
index++;
vertexPtr[index].position = D3DXVECTOR3((drawX + m_Font[letter].size), (drawY - 16), 0.0f); // Bottom right.
vertexPtr[index].texture = D3DXVECTOR2(m_Font[letter].right, 1.0f);
index++;
// Update the x location for drawing by the size of the letter and one pixel.
drawX = drawX + m_Font[letter].size + 1.0f;
}
}
return;
}
字型頂點著色器只是之前教程中用於渲染 2D 影像的紋理頂點著色器的修改版本。唯一的更改是頂點著色器名稱。
////////////////////////////////////////////////////////////////////////////////
// Filename: font.vs
////////////////////////////////////////////////////////////////////////////////
/////////////
// GLOBALS //
/////////////
cbuffer PerFrameBuffer
{
matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;
};
//////////////
// TYPEDEFS //
//////////////
struct VertexInputType
{
float4 position : POSITION;
float2 tex : TEXCOORD0;
};
struct PixelInputType
{
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
};
////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType FontVertexShader(VertexInputType input)
{
PixelInputType output;
// Change the position vector to be 4 units for proper matrix calculations.
input.position.w = 1.0f;
// Calculate the position of the vertex against the world, view, and projection matrices.
output.position = mul(input.position, worldMatrix);
output.position = mul(output.position, viewMatrix);
output.position = mul(output.position, projectionMatrix);
// Store the texture coordinates for the pixel shader.
output.tex = input.tex;
return output;
}
//////////////////////////////////////////////////////////////////////////////// // Filename: font.ps //////////////////////////////////////////////////////////////////////////////// ///////////// // GLOBALS // ///////////// Texture2D shaderTexture; SamplerState SampleType;
我們有一個新的常量緩衝區,其中包含 pixelColor 值。我們使用它來控制將用於繪製字型文字的畫素的顏色。
cbuffer PixelBuffer
{
float4 pixelColor;
};
//////////////
// TYPEDEFS //
//////////////
struct PixelInputType
{
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
};
FontPixelShader 首先對字型紋理進行取樣以獲取畫素。如果它取樣的是黑色畫素,則它只是背景三角形的一部分,而不是文字畫素。在這種情況下,我們將該畫素的 alpha 設定為零,因此當計算混合時,它將確定該畫素應該是透明的。如果輸入畫素的顏色不是黑色,則它是文字畫素。在這種情況下,我們將它乘以 pixelColor 以獲得我們想要的顏色的畫素,然後將其繪製到螢幕上。
////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 FontPixelShader(PixelInputType input) : SV_TARGET
{
float4 color;
// Sample the texture pixel at this location.
color = shaderTexture.Sample(SampleType, input.tex);
// If the color is black on the texture then treat this pixel as transparent.
if(color.r == 0.0f)
{
color.a = 0.0f;
}
// If the color is other than black on the texture then this is a pixel in the font so draw it using the font pixel color.
else
{
color.a = 1.0f;
color = color * pixelColor;
}
return color;
}
FontShaderClass 只是之前教程中用於渲染字型的 TextureShaderClass,只是重新命名並進行了一些程式碼更改。
////////////////////////////////////////////////////////////////////////////////
// Filename: fontshaderclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _FONTSHADERCLASS_H_
#define _FONTSHADERCLASS_H_
//////////////
// INCLUDES //
//////////////
#include <d3d11.h>
#include <d3dx10math.h>
#include <d3dx11async.h>
#include <fstream>
using namespace std;
////////////////////////////////////////////////////////////////////////////////
// Class name: FontShaderClass
////////////////////////////////////////////////////////////////////////////////
class FontShaderClass
{
private:
struct ConstantBufferType
{
D3DXMATRIX world;
D3DXMATRIX view;
D3DXMATRIX projection;
};
我們有一個新的結構型別,與畫素著色器中的 PixleBuffer 相匹配。它只包含將用於渲染文字字型的畫素顏色。
struct PixelBufferType
{
D3DXVECTOR4 pixelColor;
};
public:
FontShaderClass();
FontShaderClass(const FontShaderClass&);
~FontShaderClass();
bool Initialize(ID3D11Device*, HWND);
void Shutdown();
bool Render(ID3D11DeviceContext*, int, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, ID3D11ShaderResourceView*, D3DXVECTOR4);
private:
bool InitializeShader(ID3D11Device*, HWND, WCHAR*, WCHAR*);
void ShutdownShader();
void OutputShaderErrorMessage(ID3D10Blob*, HWND, WCHAR*);
bool SetShaderParameters(ID3D11DeviceContext*, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, ID3D11ShaderResourceView*, D3DXVECTOR4);
void RenderShader(ID3D11DeviceContext*, int);
private:
ID3D11VertexShader* m_vertexShader;
ID3D11PixelShader* m_pixelShader;
ID3D11InputLayout* m_layout;
ID3D11Buffer* m_constantBuffer;
ID3D11SamplerState* m_sampleState;
FontShaderClass 有一個常量緩衝區,用於儲存將用於渲染文字字型的顏色的畫素顏色。
ID3D11Buffer* m_pixelBuffer; }; #endif
////////////////////////////////////////////////////////////////////////////////
// Filename: fontshaderclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "fontshaderclass.h"
FontShaderClass::FontShaderClass()
{
m_vertexShader = 0;
m_pixelShader = 0;
m_layout = 0;
m_constantBuffer = 0;
m_sampleState = 0;
我們在類建構函式中將畫素顏色常量緩衝區初始化為 null。
m_pixelBuffer = 0;
}
FontShaderClass::FontShaderClass(const FontShaderClass& other)
{
}
FontShaderClass::~FontShaderClass()
{
}
bool FontShaderClass::Initialize(ID3D11Device* device, HWND hwnd)
{
bool result;
Initialize 載入新的字型頂點著色器和畫素著色器 HLSL 檔案。
// Initialize the vertex and pixel shaders.
result = InitializeShader(device, hwnd, L"../Engine/font.vs", L"../Engine/font.ps");
if(!result)
{
return false;
}
return true;
}
Shutdown 呼叫 ShutdownShader,釋放與字型著色器相關的指標和資料。
void FontShaderClass::Shutdown()
{
// Shutdown the vertex and pixel shaders as well as the related objects.
ShutdownShader();
return;
}
Render 將設定著色器引數,然後使用字型著色器繪製緩衝區。請注意,它與 TextureShaderClass 相同,只是它接受了新的 pixelColor 引數。
bool FontShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix,
D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture, D3DXVECTOR4 pixelColor)
{
bool result;
// Set the shader parameters that it will use for rendering.
result = SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix, texture, pixelColor);
if(!result)
{
return false;
}
// Now render the prepared buffers with the shader.
RenderShader(deviceContext, indexCount);
return true;
}
InitializeShader 函式載入新的 HLSL 字型頂點和畫素著色器,以及與著色器互動的指標。
bool FontShaderClass::InitializeShader(ID3D11Device* device, HWND hwnd, WCHAR* vsFilename, WCHAR* psFilename)
{
HRESULT result;
ID3D10Blob* errorMessage;
ID3D10Blob* vertexShaderBuffer;
ID3D10Blob* pixelShaderBuffer;
D3D11_INPUT_ELEMENT_DESC polygonLayout[2];
unsigned int numElements;
D3D11_BUFFER_DESC constantBufferDesc;
D3D11_SAMPLER_DESC samplerDesc;
D3D11_BUFFER_DESC pixelBufferDesc;
// Initialize the pointers this function will use to null.
errorMessage = 0;
vertexShaderBuffer = 0;
pixelShaderBuffer = 0;
頂點著色器的名稱已更改為 FontVertexShader。
// Compile the vertex shader code.
result = D3DX11CompileFromFile(vsFilename, NULL, NULL, "FontVertexShader", "vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL,
&vertexShaderBuffer, &errorMessage, NULL);
if(FAILED(result))
{
// If the shader failed to compile it should have writen something to the error message.
if(errorMessage)
{
OutputShaderErrorMessage(errorMessage, hwnd, vsFilename);
}
// If there was nothing in the error message then it simply could not find the shader file itself.
else
{
MessageBox(hwnd, vsFilename, L"Missing Shader File", MB_OK);
}
return false;
}
畫素著色器的名稱已更改為 FontPixelShader。
// Compile the pixel shader code.
result = D3DX11CompileFromFile(psFilename, NULL, NULL, "FontPixelShader", "ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL,
&pixelShaderBuffer, &errorMessage, NULL);
if(FAILED(result))
{
// If the shader failed to compile it should have writen something to the error message.
if(errorMessage)
{
OutputShaderErrorMessage(errorMessage, hwnd, psFilename);
}
// If there was nothing in the error message then it simply could not find the file itself.
else
{
MessageBox(hwnd, psFilename, L"Missing Shader File", MB_OK);
}
return false;
}
// Create the vertex shader from the buffer.
result = device->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL,
&m_vertexShader);
if(FAILED(result))
{
return false;
}
// Create the vertex shader from the buffer.
result = device->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL,
&m_pixelShader);
if(FAILED(result))
{
return false;
}
// Create the vertex input layout description.
// This setup needs to match the VertexType structure in the ModelClass and in the shader.
polygonLayout[0].SemanticName = "POSITION";
polygonLayout[0].SemanticIndex = 0;
polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
polygonLayout[0].InputSlot = 0;
polygonLayout[0].AlignedByteOffset = 0;
polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
polygonLayout[0].InstanceDataStepRate = 0;
polygonLayout[1].SemanticName = "TEXCOORD";
polygonLayout[1].SemanticIndex = 0;
polygonLayout[1].Format = DXGI_FORMAT_R32G32_FLOAT;
polygonLayout[1].InputSlot = 0;
polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
polygonLayout[1].InstanceDataStepRate = 0;
// Get a count of the elements in the layout.
numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);
// Create the vertex input layout.
result = device->CreateInputLayout(polygonLayout, numElements, vertexShaderBuffer->GetBufferPointer(),
vertexShaderBuffer->GetBufferSize(), &m_layout);
if(FAILED(result))
{
return false;
}
// Release the vertex shader buffer and pixel shader buffer since they are no longer needed.
vertexShaderBuffer->Release();
vertexShaderBuffer = 0;
pixelShaderBuffer->Release();
pixelShaderBuffer = 0;
// Setup the description of the dynamic constant buffer that is in the vertex shader.
constantBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
constantBufferDesc.ByteWidth = sizeof(ConstantBufferType);
constantBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
constantBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
constantBufferDesc.MiscFlags = 0;
constantBufferDesc.StructureByteStride = 0;
// Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class.
result = device->CreateBuffer(&constantBufferDesc, NULL, &m_constantBuffer);
if(FAILED(result))
{
return false;
}
// Create a texture sampler state description.
samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
samplerDesc.MipLODBias = 0.0f;
samplerDesc.MaxAnisotropy = 1;
samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
samplerDesc.BorderColor[0] = 0;
samplerDesc.BorderColor[1] = 0;
samplerDesc.BorderColor[2] = 0;
samplerDesc.BorderColor[3] = 0;
samplerDesc.MinLOD = 0;
samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
// Create the texture sampler state.
result = device->CreateSamplerState(&samplerDesc, &m_sampleState);
if(FAILED(result))
{
return false;
}
在這裡,我們設定了新的畫素顏色常量緩衝區,該緩衝區將允許此類在畫素著色器中設定畫素顏色。
// Setup the description of the dynamic pixel constant buffer that is in the pixel shader.
pixelBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
pixelBufferDesc.ByteWidth = sizeof(PixelBufferType);
pixelBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
pixelBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
pixelBufferDesc.MiscFlags = 0;
pixelBufferDesc.StructureByteStride = 0;
// Create the pixel constant buffer pointer so we can access the pixel shader constant buffer from within this class.
result = device->CreateBuffer(&pixelBufferDesc, NULL, &m_pixelBuffer);
if(FAILED(result))
{
return false;
}
return true;
}
ShutdownShader 函式釋放所有與著色器相關的資料。
void FontShaderClass::ShutdownShader()
{
新的畫素顏色常量緩衝區在此處釋放。
// Release the pixel constant buffer.
if(m_pixelBuffer)
{
m_pixelBuffer->Release();
m_pixelBuffer = 0;
}
// Release the sampler state.
if(m_sampleState)
{
m_sampleState->Release();
m_sampleState = 0;
}
// Release the constant buffer.
if(m_constantBuffer)
{
m_constantBuffer->Release();
m_constantBuffer = 0;
}
// Release the layout.
if(m_layout)
{
m_layout->Release();
m_layout = 0;
}
// Release the pixel shader.
if(m_pixelShader)
{
m_pixelShader->Release();
m_pixelShader = 0;
}
// Release the vertex shader.
if(m_vertexShader)
{
m_vertexShader->Release();
m_vertexShader = 0;
}
return;
}
OutputShaderErrorMessage 將任何著色器編譯錯誤寫入文字檔案,可以在編譯失敗的情況下進行檢視。
void FontShaderClass::OutputShaderErrorMessage(ID3D10Blob* errorMessage, HWND hwnd, WCHAR* shaderFilename)
{
char* compileErrors;
unsigned long bufferSize, i;
ofstream fout;
// Get a pointer to the error message text buffer.
compileErrors = (char*)(errorMessage->GetBufferPointer());
// Get the length of the message.
bufferSize = errorMessage->GetBufferSize();
// Open a file to write the error message to.
fout.open("shader-error.txt");
// Write out the error message.
for(i=0; i<bufferSize; i++)
{
fout Release();
errorMessage = 0;
// Pop a message up on the screen to notify the user to check the text file for compile errors.
MessageBox(hwnd, L"Error compiling shader. Check shader-error.txt for message.", shaderFilename, MB_OK);
return;
}
SetShaderParameters 函式在渲染之前設定所有著色器變數。
bool FontShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix,
D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture, D3DXVECTOR4 pixelColor)
{
HRESULT result;
D3D11_MAPPED_SUBRESOURCE mappedResource;
ConstantBufferType* dataPtr;
unsigned int bufferNumber;
PixelBufferType* dataPtr2;
// Lock the constant buffer so it can be written to.
result = deviceContext->Map(m_constantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if(FAILED(result))
{
return false;
}
// Get a pointer to the data in the constant buffer.
dataPtr = (ConstantBufferType*)mappedResource.pData;
// Transpose the matrices to prepare them for the shader.
D3DXMatrixTranspose(&worldMatrix, &worldMatrix);
D3DXMatrixTranspose(&viewMatrix, &viewMatrix);
D3DXMatrixTranspose(&projectionMatrix, &projectionMatrix);
// Copy the matrices into the constant buffer.
dataPtr->world = worldMatrix;
dataPtr->view = viewMatrix;
dataPtr->projection = projectionMatrix;
// Unlock the constant buffer.
deviceContext->Unmap(m_constantBuffer, 0);
// Set the position of the constant buffer in the vertex shader.
bufferNumber = 0;
// Now set the constant buffer in the vertex shader with the updated values.
deviceContext->VSSetConstantBuffers(bufferNumber, 1, &m_constantBuffer);
// Set shader texture resource in the pixel shader.
deviceContext->PSSetShaderResources(0, 1, &texture);
在這裡,我們在渲染之前設定畫素顏色。我們鎖定畫素常量緩衝區,然後在其中設定畫素顏色,然後再次解鎖。我們在畫素著色器中設定常量緩衝區位置,它就可以使用。
// Lock the pixel constant buffer so it can be written to.
result = deviceContext->Map(m_pixelBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if(FAILED(result))
{
return false;
}
// Get a pointer to the data in the pixel constant buffer.
dataPtr2 = (PixelBufferType*)mappedResource.pData;
// Copy the pixel color into the pixel constant buffer.
dataPtr2->pixelColor = pixelColor;
// Unlock the pixel constant buffer.
deviceContext->Unmap(m_pixelBuffer, 0);
// Set the position of the pixel constant buffer in the pixel shader.
bufferNumber = 0;
// Now set the pixel constant buffer in the pixel shader with the updated value.
deviceContext->PSSetConstantBuffers(bufferNumber, 1, &m_pixelBuffer);
return true;
}
RenderShader 使用字型著色器繪製準備好的字型頂點/索引緩衝區。
void FontShaderClass::RenderShader(ID3D11DeviceContext* deviceContext, int indexCount)
{
// Set the vertex input layout.
deviceContext->IASetInputLayout(m_layout);
// Set the vertex and pixel shaders that will be used to render the triangles.
deviceContext->VSSetShader(m_vertexShader, NULL, 0);
deviceContext->PSSetShader(m_pixelShader, NULL, 0);
// Set the sampler state in the pixel shader.
deviceContext->PSSetSamplers(0, 1, &m_sampleState);
// Render the triangles.
deviceContext->DrawIndexed(indexCount, 0, 0);
return;
}
TextClass 處理應用程式所需的所有 2D 文字繪製。它將 2D 文字渲染到螢幕上,並使用 FontClass 和 FontShaderClass 來幫助它完成此操作。
////////////////////////////////////////////////////////////////////////////////
// Filename: textclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _TEXTCLASS_H_
#define _TEXTCLASS_H_
///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "fontclass.h"
#include "fontshaderclass.h"
////////////////////////////////////////////////////////////////////////////////
// Class name: TextClass
////////////////////////////////////////////////////////////////////////////////
class TextClass
{
private:
SentenceType 是一個結構,它儲存每個文字句子的渲染資訊。
struct SentenceType
{
ID3D11Buffer *vertexBuffer, *indexBuffer;
int vertexCount, indexCount, maxLength;
float red, green, blue;
};
VertexType 必須與 FontClass 中的 VertexType 相匹配。
struct VertexType
{
D3DXVECTOR3 position;
D3DXVECTOR2 texture;
};
public:
TextClass();
TextClass(const TextClass&);
~TextClass();
bool Initialize(ID3D11Device*, ID3D11DeviceContext*, HWND, int, int, D3DXMATRIX);
void Shutdown();
bool Render(ID3D11DeviceContext*, D3DXMATRIX, D3DXMATRIX);
private:
bool InitializeSentence(SentenceType**, int, ID3D11Device*);
bool UpdateSentence(SentenceType*, char*, int, int, float, float, float, ID3D11DeviceContext*);
void ReleaseSentence(SentenceType**);
bool RenderSentence(ID3D11DeviceContext*, SentenceType*, D3DXMATRIX, D3DXMATRIX);
private:
FontClass* m_Font;
FontShaderClass* m_FontShader;
int m_screenWidth, m_screenHeight;
D3DXMATRIX m_baseViewMatrix;
在本教程中,我們將使用兩個句子。
SentenceType* m_sentence1; SentenceType* m_sentence2; }; #endif
/////////////////////////////////////////////////////////////////////////////// // Filename: textclass.cpp /////////////////////////////////////////////////////////////////////////////// #include "textclass.h"
類建構函式將私有成員變數初始化為 null。
TextClass::TextClass()
{
m_Font = 0;
m_FontShader = 0;
m_sentence1 = 0;
m_sentence2 = 0;
}
TextClass::TextClass(const TextClass& other)
{
}
TextClass::~TextClass()
{
}
bool TextClass::Initialize(ID3D11Device* device, ID3D11DeviceContext* deviceContext, HWND hwnd, int screenWidth, int screenHeight,
D3DXMATRIX baseViewMatrix)
{
bool result;
儲存螢幕大小和基礎檢視矩陣,這些將用於渲染 2D 文字。
// Store the screen width and height. m_screenWidth = screenWidth; m_screenHeight = screenHeight; // Store the base view matrix. m_baseViewMatrix = baseViewMatrix;
建立並初始化字型物件。
// Create the font object.
m_Font = new FontClass;
if(!m_Font)
{
return false;
}
// Initialize the font object.
result = m_Font->Initialize(device, "../Engine/data/fontdata.txt", L"../Engine/data/font.dds");
if(!result)
{
MessageBox(hwnd, L"Could not initialize the font object.", L"Error", MB_OK);
return false;
}
建立並初始化字型著色器物件。
// Create the font shader object.
m_FontShader = new FontShaderClass;
if(!m_FontShader)
{
return false;
}
// Initialize the font shader object.
result = m_FontShader->Initialize(device, hwnd);
if(!result)
{
MessageBox(hwnd, L"Could not initialize the font shader object.", L"Error", MB_OK);
return false;
}
建立並初始化將用於本教程的兩個字串。一個字串在 100, 100 處以白色顯示 "Hello",另一個字串在 100, 200 處以黃色顯示 "Goodbye"。可以呼叫 UpdateSentence 函式來隨時更改字串的內容、位置和顏色。
// Initialize the first sentence.
result = InitializeSentence(&m_sentence1, 16, device);
if(!result)
{
return false;
}
// Now update the sentence vertex buffer with the new string information.
result = UpdateSentence(m_sentence1, "Hello", 100, 100, 1.0f, 1.0f, 1.0f, deviceContext);
if(!result)
{
return false;
}
// Initialize the first sentence.
result = InitializeSentence(&m_sentence2, 16, device);
if(!result)
{
return false;
}
// Now update the sentence vertex buffer with the new string information.
result = UpdateSentence(m_sentence2, "Goodbye", 100, 200, 1.0f, 1.0f, 0.0f, deviceContext);
if(!result)
{
return false;
}
return true;
}
Shutdown 函式將釋放兩個句子、字型物件和字型著色器物件。
void TextClass::Shutdown()
{
// Release the first sentence.
ReleaseSentence(&m_sentence1);
// Release the second sentence.
ReleaseSentence(&m_sentence2);
// Release the font shader object.
if(m_FontShader)
{
m_FontShader->Shutdown();
delete m_FontShader;
m_FontShader = 0;
}
// Release the font object.
if(m_Font)
{
m_Font->Shutdown();
delete m_Font;
m_Font = 0;
}
return;
}
Render 將繪製兩個句子到螢幕上。
bool TextClass::Render(ID3D11DeviceContext* deviceContext, D3DXMATRIX worldMatrix, D3DXMATRIX orthoMatrix)
{
bool result;
// Draw the first sentence.
result = RenderSentence(deviceContext, m_sentence1, worldMatrix, orthoMatrix);
if(!result)
{
return false;
}
// Draw the second sentence.
result = RenderSentence(deviceContext, m_sentence2, worldMatrix, orthoMatrix);
if(!result)
{
return false;
}
return true;
}
InitializeSentence 函式建立一個 SentenceType,其中包含一個空的頂點緩衝區,該緩衝區將用於儲存和渲染句子。maxLength 輸入引數確定頂點緩衝區的大小。所有句子都具有與之關聯的頂點和索引緩衝區,這些緩衝區在此函式中首先進行初始化。
bool TextClass::InitializeSentence(SentenceType** sentence, int maxLength, ID3D11Device* device)
{
VertexType* vertices;
unsigned long* indices;
D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc;
D3D11_SUBRESOURCE_DATA vertexData, indexData;
HRESULT result;
int i;
// Create a new sentence object.
*sentence = new SentenceType;
if(!*sentence)
{
return false;
}
// Initialize the sentence buffers to null.
(*sentence)->vertexBuffer = 0;
(*sentence)->indexBuffer = 0;
// Set the maximum length of the sentence.
(*sentence)->maxLength = maxLength;
// Set the number of vertices in the vertex array.
(*sentence)->vertexCount = 6 * maxLength;
// Set the number of indexes in the index array.
(*sentence)->indexCount = (*sentence)->vertexCount;
// Create the vertex array.
vertices = new VertexType[(*sentence)->vertexCount];
if(!vertices)
{
return false;
}
// Create the index array.
indices = new unsigned long[(*sentence)->indexCount];
if(!indices)
{
return false;
}
// Initialize vertex array to zeros at first.
memset(vertices, 0, (sizeof(VertexType) * (*sentence)->vertexCount));
// Initialize the index array.
for(i=0; iindexCount; i++)
{
indices[i] = i;
}
在為句子建立頂點緩衝區描述期間,我們將 Usage 型別設定為動態,因為我們可能想要隨時更改句子的內容。
// Set up the description of the dynamic vertex buffer.
vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
vertexBufferDesc.ByteWidth = sizeof(VertexType) * (*sentence)->vertexCount;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
vertexBufferDesc.MiscFlags = 0;
vertexBufferDesc.StructureByteStride = 0;
// Give the subresource structure a pointer to the vertex data.
vertexData.pSysMem = vertices;
vertexData.SysMemPitch = 0;
vertexData.SysMemSlicePitch = 0;
// Create the vertex buffer.
result = device->CreateBuffer(&vertexBufferDesc, &vertexData, &(*sentence)->vertexBuffer);
if(FAILED(result))
{
return false;
}
索引緩衝區被設定為一個正常的靜態緩衝區,因為其內容永遠不需要更改。
// Set up the description of the static index buffer.
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = sizeof(unsigned long) * (*sentence)->indexCount;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
indexBufferDesc.StructureByteStride = 0;
// Give the subresource structure a pointer to the index data.
indexData.pSysMem = indices;
indexData.SysMemPitch = 0;
indexData.SysMemSlicePitch = 0;
// Create the index buffer.
result = device->CreateBuffer(&indexBufferDesc, &indexData, &(*sentence)->indexBuffer);
if(FAILED(result))
{
return false;
}
// Release the vertex array as it is no longer needed.
delete [] vertices;
vertices = 0;
// Release the index array as it is no longer needed.
delete [] indices;
indices = 0;
return true;
}
UpdateSentence 更改輸入句子的頂點緩衝區的內容。它使用 Map 和 Unmap 函式以及 memcpy 來更新頂點緩衝區的內容。
bool TextClass::UpdateSentence(SentenceType* sentence, char* text, int positionX, int positionY, float red, float green, float blue,
ID3D11DeviceContext* deviceContext)
{
int numLetters;
VertexType* vertices;
float drawX, drawY;
HRESULT result;
D3D11_MAPPED_SUBRESOURCE mappedResource;
VertexType* verticesPtr;
設定句子的顏色和大小。
// Store the color of the sentence.
sentence->red = red;
sentence->green = green;
sentence->blue = blue;
// Get the number of letters in the sentence.
numLetters = (int)strlen(text);
// Check for possible buffer overflow.
if(numLetters > sentence->maxLength)
{
return false;
}
// Create the vertex array.
vertices = new VertexType[sentence->vertexCount];
if(!vertices)
{
return false;
}
// Initialize vertex array to zeros at first.
memset(vertices, 0, (sizeof(VertexType) * sentence->vertexCount));
計算在螢幕上繪製句子的起始位置。
// Calculate the X and Y pixel position on the screen to start drawing to. drawX = (float)(((m_screenWidth / 2) * -1) + positionX); drawY = (float)((m_screenHeight / 2) - positionY);
使用 FontClass 和句子資訊構建頂點陣列。
// Use the font class to build the vertex array from the sentence text and sentence draw location. m_Font->BuildVertexArray((void*)vertices, text, drawX, drawY);
將頂點陣列資訊複製到句子頂點緩衝區中。
// Lock the vertex buffer so it can be written to.
result = deviceContext->Map(sentence->vertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if(FAILED(result))
{
return false;
}
// Get a pointer to the data in the vertex buffer.
verticesPtr = (VertexType*)mappedResource.pData;
// Copy the data into the vertex buffer.
memcpy(verticesPtr, (void*)vertices, (sizeof(VertexType) * sentence->vertexCount));
// Unlock the vertex buffer.
deviceContext->Unmap(sentence->vertexBuffer, 0);
// Release the vertex array as it is no longer needed.
delete [] vertices;
vertices = 0;
return true;
}
ReleaseSentence 用於釋放句子頂點和索引緩衝區以及句子本身。
void TextClass::ReleaseSentence(SentenceType** sentence)
{
if(*sentence)
{
// Release the sentence vertex buffer.
if((*sentence)->vertexBuffer)
{
(*sentence)->vertexBuffer->Release();
(*sentence)->vertexBuffer = 0;
}
// Release the sentence index buffer.
if((*sentence)->indexBuffer)
{
(*sentence)->indexBuffer->Release();
(*sentence)->indexBuffer = 0;
}
// Release the sentence.
delete *sentence;
*sentence = 0;
}
return;
}
RenderSentence 函式將句子頂點和索引緩衝區放在輸入彙編器上,然後呼叫 FontShaderClass 物件來繪製作為此函式輸入給出的句子。請注意,我們使用 m_baseViewMatrix 而不是當前檢視矩陣。這使我們能夠在每一幀中將文字繪製到螢幕上的相同位置,而不管當前檢視可能在哪裡。同樣,我們使用 orthoMatrix 而不是常規投影矩陣,因為這應該使用 2D 座標進行繪製。
bool TextClass::RenderSentence(ID3D11DeviceContext* deviceContext, SentenceType* sentence, D3DXMATRIX worldMatrix,
D3DXMATRIX orthoMatrix)
{
unsigned int stride, offset;
D3DXVECTOR4 pixelColor;
bool result;
// Set vertex buffer stride and offset.
stride = sizeof(VertexType);
offset = 0;
// Set the vertex buffer to active in the input assembler so it can be rendered.
deviceContext->IASetVertexBuffers(0, 1, &sentence->vertexBuffer, &stride, &offset);
// Set the index buffer to active in the input assembler so it can be rendered.
deviceContext->IASetIndexBuffer(sentence->indexBuffer, DXGI_FORMAT_R32_UINT, 0);
// Set the type of primitive that should be rendered from this vertex buffer, in this case triangles.
deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// Create a pixel color vector with the input sentence color.
pixelColor = D3DXVECTOR4(sentence->red, sentence->green, sentence->blue, 1.0f);
// Render the text using the font shader.
result = m_FontShader->Render(deviceContext, sentence->indexCount, worldMatrix, m_baseViewMatrix, orthoMatrix, m_Font->GetTexture(),
pixelColor);
if(!result)
{
false;
}
return true;
}
在本教程中,我們還修改了 D3DClass 以合併混合狀態。混合允許字型與背景中的 3D 物件混合。如果我們不開啟混合,我們會看到文字後面的黑色三角形。但混合開啟後,只有文字的畫素會顯示在螢幕上,其餘的三角形是完全透明的。這裡我不會詳細介紹混合,但本教程需要一個簡單的混合才能正常工作。
////////////////////////////////////////////////////////////////////////////////
// Filename: d3dclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _D3DCLASS_H_
#define _D3DCLASS_H_
/////////////
// LINKING //
/////////////
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dx11.lib")
#pragma comment(lib, "d3dx10.lib")
//////////////
// INCLUDES //
//////////////
#include <dxgi.h>
#include <d3dcommon.h>
#include <d3d11.h>
#include <d3dx10math.h>
////////////////////////////////////////////////////////////////////////////////
// Class name: D3DClass
////////////////////////////////////////////////////////////////////////////////
class D3DClass
{
public:
D3DClass();
D3DClass(const D3DClass&);
~D3DClass();
bool Initialize(int, int, bool, HWND, bool, float, float);
void Shutdown();
void BeginScene(float, float, float, float);
void EndScene();
ID3D11Device* GetDevice();
ID3D11DeviceContext* GetDeviceContext();
void GetProjectionMatrix(D3DXMATRIX&);
void GetWorldMatrix(D3DXMATRIX&);
void GetOrthoMatrix(D3DXMATRIX&);
void TurnZBufferOn();
void TurnZBufferOff();
我們有兩個用於開啟和關閉 alpha 混合的新函式。
void TurnOnAlphaBlending(); void TurnOffAlphaBlending(); private: bool m_vsync_enabled; IDXGISwapChain* m_swapChain; ID3D11Device* m_device; ID3D11DeviceContext* m_deviceContext; ID3D11RenderTargetView* m_renderTargetView; ID3D11Texture2D* m_depthStencilBuffer; ID3D11DepthStencilState* m_depthStencilState; ID3D11DepthStencilView* m_depthStencilView; ID3D11RasterizerState* m_rasterState; D3DXMATRIX m_projectionMatrix; D3DXMATRIX m_worldMatrix; D3DXMATRIX m_orthoMatrix; ID3D11DepthStencilState* m_depthDisabledStencilState;
我們有兩個新的混合狀態。m_alphaEnableBlendingState 用於開啟 alpha 混合,而 m_alphaDisableBlendingState 用於關閉 alpha 混合。
ID3D11BlendState* m_alphaEnableBlendingState; ID3D11BlendState* m_alphaDisableBlendingState; }; #endif
由於前面的教程,我們將只介紹此類中已更改的函式。
////////////////////////////////////////////////////////////////////////////////
// Filename: d3dclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "d3dclass.h"
D3DClass::D3DClass()
{
m_swapChain = 0;
m_device = 0;
m_deviceContext = 0;
m_renderTargetView = 0;
m_depthStencilBuffer = 0;
m_depthStencilState = 0;
m_depthStencilView = 0;
m_rasterState = 0;
m_depthDisabledStencilState = 0;
將兩個新的混合狀態設定為 null。
m_alphaEnableBlendingState = 0;
m_alphaDisableBlendingState = 0;
}
bool D3DClass::Initialize(int screenWidth, int screenHeight, bool vsync, HWND hwnd, bool fullscreen,
float screenDepth, float screenNear)
{
HRESULT result;
IDXGIFactory* factory;
IDXGIAdapter* adapter;
IDXGIOutput* adapterOutput;
unsigned int numModes, i, numerator, denominator;
DXGI_MODE_DESC* displayModeList;
DXGI_SWAP_CHAIN_DESC swapChainDesc;
D3D_FEATURE_LEVEL featureLevel;
ID3D11Texture2D* backBufferPtr;
D3D11_TEXTURE2D_DESC depthBufferDesc;
D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
D3D11_RASTERIZER_DESC rasterDesc;
D3D11_VIEWPORT viewport;
float fieldOfView, screenAspect;
D3D11_DEPTH_STENCIL_DESC depthDisabledStencilDesc;
我們有一個新的描述變數,用於設定兩個新的混合狀態。
D3D11_BLEND_DESC blendStateDescription;
// Store the vsync setting.
m_vsync_enabled = vsync;
// Create a DirectX graphics interface factory.
result = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&factory);
if(FAILED(result))
{
return false;
}
// Use the factory to create an adapter for the primary graphics interface (video card).
result = factory->EnumAdapters(0, &adapter);
if(FAILED(result))
{
return false;
}
// Enumerate the primary adapter output (monitor).
result = adapter->EnumOutputs(0, &adapterOutput);
if(FAILED(result))
{
return false;
}
// Get the number of modes that fit the DXGI_FORMAT_R8G8B8A8_UNORM display format for the adapter output (monitor).
result = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, NULL);
if(FAILED(result))
{
return false;
}
// Create a list to hold all the possible display modes for this monitor/video card combination.
displayModeList = new DXGI_MODE_DESC[numModes];
if(!displayModeList)
{
return false;
}
// Now fill the display mode list structures.
result = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, displayModeList);
if(FAILED(result))
{
return false;
}
// Now go through all the display modes and find the one that matches the screen width and height.
// When a match is found store the numerator and denominator of the refresh rate for that monitor.
for(i=0; i<numModes; i++)
{
if(displayModeList[i].Width == (unsigned int)screenWidth)
{
if(displayModeList[i].Height == (unsigned int)screenHeight)
{
numerator = displayModeList[i].RefreshRate.Numerator;
denominator = displayModeList[i].RefreshRate.Denominator;
}
}
}
// Release the display mode list.
delete [] displayModeList;
displayModeList = 0;
// Release the adapter output.
adapterOutput->Release();
adapterOutput = 0;
// Release the adapter.
adapter->Release();
adapter = 0;
// Release the factory.
factory->Release();
factory = 0;
// Initialize the swap chain description.
ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
// Set to a single back buffer.
swapChainDesc.BufferCount = 1;
// Set the width and height of the back buffer.
swapChainDesc.BufferDesc.Width = screenWidth;
swapChainDesc.BufferDesc.Height = screenHeight;
// Set regular 32-bit surface for the back buffer.
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
// Set the refresh rate of the back buffer.
if(m_vsync_enabled)
{
swapChainDesc.BufferDesc.RefreshRate.Numerator = numerator;
swapChainDesc.BufferDesc.RefreshRate.Denominator = denominator;
}
else
{
swapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
}
// Set the usage of the back buffer.
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
// Set the handle for the window to render to.
swapChainDesc.OutputWindow = hwnd;
// Turn multisampling off.
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
// Set to full screen or windowed mode.
if(fullscreen)
{
swapChainDesc.Windowed = false;
}
else
{
swapChainDesc.Windowed = true;
}
// Set the scan line ordering and scaling to unspecified.
swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
// Discard the back buffer contents after presenting.
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
// Don't set the advanced flags.
swapChainDesc.Flags = 0;
// Set the feature level to DirectX 11.
featureLevel = D3D_FEATURE_LEVEL_11_0;
// Create the swap chain, Direct3D device, and Direct3D device context.
result = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, &featureLevel, 1,
D3D11_SDK_VERSION, &swapChainDesc, &m_swapChain, &m_device, NULL, &m_deviceContext);
if(FAILED(result))
{
return false;
}
// Get the pointer to the back buffer.
result = m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBufferPtr);
if(FAILED(result))
{
return false;
}
// Create the render target view with the back buffer pointer.
result = m_device->CreateRenderTargetView(backBufferPtr, NULL, &m_renderTargetView);
if(FAILED(result))
{
return false;
}
// Release pointer to the back buffer as we no longer need it.
backBufferPtr->Release();
backBufferPtr = 0;
// Initialize the description of the depth buffer.
ZeroMemory(&depthBufferDesc, sizeof(depthBufferDesc));
// Set up the description of the depth buffer.
depthBufferDesc.Width = screenWidth;
depthBufferDesc.Height = screenHeight;
depthBufferDesc.MipLevels = 1;
depthBufferDesc.ArraySize = 1;
depthBufferDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthBufferDesc.SampleDesc.Count = 1;
depthBufferDesc.SampleDesc.Quality = 0;
depthBufferDesc.Usage = D3D11_USAGE_DEFAULT;
depthBufferDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
depthBufferDesc.CPUAccessFlags = 0;
depthBufferDesc.MiscFlags = 0;
// Create the texture for the depth buffer using the filled out description.
result = m_device->CreateTexture2D(&depthBufferDesc, NULL, &m_depthStencilBuffer);
if(FAILED(result))
{
return false;
}
// Initialize the description of the stencil state.
ZeroMemory(&depthStencilDesc, sizeof(depthStencilDesc));
// Set up the description of the stencil state.
depthStencilDesc.DepthEnable = true;
depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
depthStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;
depthStencilDesc.StencilEnable = true;
depthStencilDesc.StencilReadMask = 0xFF;
depthStencilDesc.StencilWriteMask = 0xFF;
// Stencil operations if pixel is front-facing.
depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
// Stencil operations if pixel is back-facing.
depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
// Create the depth stencil state.
result = m_device->CreateDepthStencilState(&depthStencilDesc, &m_depthStencilState);
if(FAILED(result))
{
return false;
}
// Set the depth stencil state.
m_deviceContext->OMSetDepthStencilState(m_depthStencilState, 1);
// Initialize the depth stencil view.
ZeroMemory(&depthStencilViewDesc, sizeof(depthStencilViewDesc));
// Set up the depth stencil view description.
depthStencilViewDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
depthStencilViewDesc.Texture2D.MipSlice = 0;
// Create the depth stencil view.
result = m_device->CreateDepthStencilView(m_depthStencilBuffer, &depthStencilViewDesc, &m_depthStencilView);
if(FAILED(result))
{
return false;
}
// Bind the render target view and depth stencil buffer to the output render pipeline.
m_deviceContext->OMSetRenderTargets(1, &m_renderTargetView, m_depthStencilView);
// Setup the raster description which will determine how and what polygons will be drawn.
rasterDesc.AntialiasedLineEnable = false;
rasterDesc.CullMode = D3D11_CULL_BACK;
rasterDesc.DepthBias = 0;
rasterDesc.DepthBiasClamp = 0.0f;
rasterDesc.DepthClipEnable = true;
rasterDesc.FillMode = D3D11_FILL_SOLID;
rasterDesc.FrontCounterClockwise = false;
rasterDesc.MultisampleEnable = false;
rasterDesc.ScissorEnable = false;
rasterDesc.SlopeScaledDepthBias = 0.0f;
// Create the rasterizer state from the description we just filled out.
result = m_device->CreateRasterizerState(&rasterDesc, &m_rasterState);
if(FAILED(result))
{
return false;
}
// Now set the rasterizer state.
m_deviceContext->RSSetState(m_rasterState);
// Setup the viewport for rendering.
viewport.Width = (float)screenWidth;
viewport.Height = (float)screenHeight;
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
viewport.TopLeftX = 0.0f;
viewport.TopLeftY = 0.0f;
// Create the viewport.
m_deviceContext->RSSetViewports(1, &viewport);
// Setup the projection matrix.
fieldOfView = (float)D3DX_PI / 4.0f;
screenAspect = (float)screenWidth / (float)screenHeight;
// Create the projection matrix for 3D rendering.
D3DXMatrixPerspectiveFovLH(&m_projectionMatrix, fieldOfView, screenAspect, screenNear, screenDepth);
// Initialize the world matrix to the identity matrix.
D3DXMatrixIdentity(&m_worldMatrix);
// Create an orthographic projection matrix for 2D rendering.
D3DXMatrixOrthoLH(&m_orthoMatrix, (float)screenWidth, (float)screenHeight, screenNear, screenDepth);
// Clear the second depth stencil state before setting the parameters.
ZeroMemory(&depthDisabledStencilDesc, sizeof(depthDisabledStencilDesc));
// Now create a second depth stencil state which turns off the Z buffer for 2D rendering. The only difference is
// that DepthEnable is set to false, all other parameters are the same as the other depth stencil state.
depthDisabledStencilDesc.DepthEnable = false;
depthDisabledStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
depthDisabledStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;
depthDisabledStencilDesc.StencilEnable = true;
depthDisabledStencilDesc.StencilReadMask = 0xFF;
depthDisabledStencilDesc.StencilWriteMask = 0xFF;
depthDisabledStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthDisabledStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
depthDisabledStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthDisabledStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
depthDisabledStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthDisabledStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
depthDisabledStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthDisabledStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
// Create the state using the device.
result = m_device->CreateDepthStencilState(&depthDisabledStencilDesc, &m_depthDisabledStencilState);
if(FAILED(result))
{
return false;
}
首先初始化混合狀態描述。
// Clear the blend state description. ZeroMemory(&blendStateDescription, sizeof(D3D11_BLEND_DESC));
要建立一個啟用 alpha 的混合狀態描述,請將 BlendEnable 更改為 TRUE,並將 DestBlend 更改為 D3D11_BLEND_INV_SRC_ALPHA。其他設定設定為其預設值,可以在 Windows DirectX 圖形文件中查詢。
// Create an alpha enabled blend state description. blendStateDescription.RenderTarget[0].BlendEnable = TRUE; blendStateDescription.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; blendStateDescription.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; blendStateDescription.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; blendStateDescription.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; blendStateDescription.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; blendStateDescription.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; blendStateDescription.RenderTarget[0].RenderTargetWriteMask = 0x0f;
然後,我們使用剛剛設定的描述建立一個啟用 alpha 的混合狀態。
// Create the blend state using the description.
result = m_device->CreateBlendState(&blendStateDescription, &m_alphaEnableBlendingState);
if(FAILED(result))
{
return false;
}
現在,要建立一個停用 alpha 的狀態,我們將剛剛更改的描述更改為將 BlendEnable 設定為 FALSE。其餘設定可以保持原樣。
// Modify the description to create an alpha disabled blend state description. blendStateDescription.RenderTarget[0].BlendEnable = FALSE;
然後,我們使用修改後的混合狀態描述建立一個停用 alpha 的混合狀態。現在,我們有兩種混合狀態,可以在它們之間切換以開啟和關閉 alpha 混合。
// Create the blend state using the description.
result = m_device->CreateBlendState(&blendStateDescription, &m_alphaDisableBlendingState);
if(FAILED(result))
{
return false;
}
return true;
}
void D3DClass::Shutdown()
{
// Before shutting down set to windowed mode or when you release the swap chain it will throw an exception.
if(m_swapChain)
{
m_swapChain->SetFullscreenState(false, NULL);
}
釋放兩個新的混合狀態。
if(m_alphaEnableBlendingState)
{
m_alphaEnableBlendingState->Release();
m_alphaEnableBlendingState = 0;
}
if(m_alphaDisableBlendingState)
{
m_alphaDisableBlendingState->Release();
m_alphaDisableBlendingState = 0;
}
if(m_rasterState)
{
m_rasterState->Release();
m_rasterState = 0;
}
if(m_depthStencilView)
{
m_depthStencilView->Release();
m_depthStencilView = 0;
}
if(m_depthDisabledStencilState)
{
m_depthDisabledStencilState->Release();
m_depthDisabledStencilState = 0;
}
if(m_depthStencilState)
{
m_depthStencilState->Release();
m_depthStencilState = 0;
}
if(m_depthStencilBuffer)
{
m_depthStencilBuffer->Release();
m_depthStencilBuffer = 0;
}
if(m_renderTargetView)
{
m_renderTargetView->Release();
m_renderTargetView = 0;
}
if(m_deviceContext)
{
m_deviceContext->Release();
m_deviceContext = 0;
}
if(m_device)
{
m_device->Release();
m_device = 0;
}
if(m_swapChain)
{
m_swapChain->Release();
m_swapChain = 0;
}
return;
}
第一個新函式 TurnOnAlphaBlending 允許我們使用我們的 m_alphaEnableBlendingState 混合狀態與 OMSetBlendState 函式一起開啟 alpha 混合。
void D3DClass::TurnOnAlphaBlending()
{
float blendFactor[4];
// Setup the blend factor.
blendFactor[0] = 0.0f;
blendFactor[1] = 0.0f;
blendFactor[2] = 0.0f;
blendFactor[3] = 0.0f;
// Turn on the alpha blending.
m_deviceContext->OMSetBlendState(m_alphaEnableBlendingState, blendFactor, 0xffffffff);
return;
}
第二個新函式 TurnOffAlphaBlending 允許我們使用我們的 m_alphaDisableBlendingState 混合狀態與 OMSetBlendState 函式一起關閉 alpha 混合。
void D3DClass::TurnOffAlphaBlending()
{
float blendFactor[4];
// Setup the blend factor.
blendFactor[0] = 0.0f;
blendFactor[1] = 0.0f;
blendFactor[2] = 0.0f;
blendFactor[3] = 0.0f;
// Turn off the alpha blending.
m_deviceContext->OMSetBlendState(m_alphaDisableBlendingState, blendFactor, 0xffffffff);
return;
}
//////////////////////////////////////////////////////////////////////////////// // Filename: graphicsclass.h //////////////////////////////////////////////////////////////////////////////// #ifndef _GRAPHICSCLASS_H_ #define _GRAPHICSCLASS_H_ ///////////// // GLOBALS // ///////////// const bool FULL_SCREEN = true; const bool VSYNC_ENABLED = true; const float SCREEN_DEPTH = 1000.0f; const float SCREEN_NEAR = 0.1f; /////////////////////// // MY CLASS INCLUDES // /////////////////////// #include "d3dclass.h" #include "cameraclass.h"
現在,我們包含了新的 TextClass 標頭檔案。
#include "textclass.h"
////////////////////////////////////////////////////////////////////////////////
// Class name: GraphicsClass
////////////////////////////////////////////////////////////////////////////////
class GraphicsClass
{
public:
GraphicsClass();
GraphicsClass(const GraphicsClass&);
~GraphicsClass();
bool Initialize(int, int, HWND);
void Shutdown();
void Frame();
bool Render();
private:
D3DClass* m_D3D;
CameraClass* m_Camera;
有一個用於 TextClass 物件的新私有變數。
TextClass* m_Text; }; #endif
我們將只檢視自上一教程以來已更改的函式。
////////////////////////////////////////////////////////////////////////////////
// Filename: graphicsclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "graphicsclass.h"
GraphicsClass::GraphicsClass()
{
m_D3D = 0;
m_Camera = 0;
在類建構函式中,將新的 TextClass 物件初始化為 null。
m_Text = 0;
}
bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
{
bool result;
D3DXMATRIX baseViewMatrix;
// Create the Direct3D object.
m_D3D = new D3DClass;
if(!m_D3D)
{
return false;
}
// Initialize the Direct3D object.
result = m_D3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR);
if(!result)
{
MessageBox(hwnd, L"Could not initialize Direct3D", L"Error", MB_OK);
return false;
}
// Create the camera object.
m_Camera = new CameraClass;
if(!m_Camera)
{
return false;
}
我們從相機物件建立一個新的檢視矩陣,供 TextClass 使用。它將始終使用此檢視矩陣,以便文字始終繪製在螢幕上的相同位置。
// Initialize a base view matrix with the camera for 2D user interface rendering. m_Camera->SetPosition(0.0f, 0.0f, -1.0f); m_Camera->Render(); m_Camera->GetViewMatrix(baseViewMatrix);
在這裡,我們建立並初始化新的 TextClass 物件。
// Create the text object.
m_Text = new TextClass;
if(!m_Text)
{
return false;
}
// Initialize the text object.
result = m_Text->Initialize(m_D3D->GetDevice(), m_D3D->GetDeviceContext(), hwnd, screenWidth, screenHeight, baseViewMatrix);
if(!result)
{
MessageBox(hwnd, L"Could not initialize the text object.", L"Error", MB_OK);
return false;
}
return true;
}
void GraphicsClass::Shutdown()
{
在這裡,我們釋放 TextClass 物件。
// Release the text object.
if(m_Text)
{
m_Text->Shutdown();
delete m_Text;
m_Text = 0;
}
// Release the camera object.
if(m_Camera)
{
delete m_Camera;
m_Camera = 0;
}
// Release the D3D object.
if(m_D3D)
{
m_D3D->Shutdown();
delete m_D3D;
m_D3D = 0;
}
return;
}
bool GraphicsClass::Render()
{
D3DXMATRIX worldMatrix, viewMatrix, projectionMatrix, orthoMatrix;
bool result;
// Clear the buffers to begin the scene.
m_D3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);
// Generate the view matrix based on the camera's position.
m_Camera->Render();
// Get the view, projection, and world matrices from the camera and D3D objects.
m_Camera->GetViewMatrix(viewMatrix);
m_D3D->GetWorldMatrix(worldMatrix);
m_D3D->GetProjectionMatrix(projectionMatrix);
m_D3D->GetOrthoMatrix(orthoMatrix);
// Turn off the Z buffer to begin all 2D rendering.
m_D3D->TurnZBufferOff();
在這裡,我們開啟 alpha 混合,以便文字可以與背景混合。
// Turn on the alpha blending before rendering the text. m_D3D->TurnOnAlphaBlending();
我們在這裡呼叫文字物件以將其所有句子渲染到螢幕上。就像 2D 影像一樣,我們在繪製之前停用 Z 緩衝區,並在所有 2D 繪製完成後再次啟用它。
// Render the text strings.
result = m_Text->Render(m_D3D->GetDeviceContext(), worldMatrix, orthoMatrix);
if(!result)
{
return false;
}
在這裡,我們關閉 alpha 混合,以便繪製的任何其他內容都不會與後面的物件進行 alpha 混合。
// Turn off alpha blending after rendering the text. m_D3D->TurnOffAlphaBlending(); // Turn the Z buffer back on now that all 2D rendering has completed. m_D3D->TurnZBufferOn(); // Present the rendered scene to the screen. m_D3D->EndScene(); return true; }
現在,我們能夠將彩色文字渲染到螢幕上的任何位置。
1. 重新編譯程式碼,並確保您在螢幕上看到一個白色的 "Hello" 寫在 100x100 處,以及一個黃色的 "Goodbye" 在其下方。
2. 更改句子的畫素顏色、位置和內容。
3. 建立第三個句子結構並使其也進行渲染。
4. 在 GraphicsClass::Render 函式中註釋掉混合呼叫,並將 m_D3D->BeginScene(0.0f, 0.0f, 1.0f, 1.0f); 設定在 GraphicsClass::Render 函式中。這將顯示為什麼需要混合。
5. 將混合呼叫添加回 GraphicsClass::Render 函式中,並保持 m_D3D->BeginScene(0.0f, 0.0f, 1.0f, 1.0f); 在 GraphicsClass::Render 函式中,以檢視區別。