一,看本节教程前应该掌握:
(1)D3D11基础教程二之D3D11初始化
(2)了解3D渲染流水线的知识,如世界变换,相机变换,透视投影变换,视口变换,光栅化,线性插值,gouraud着色等,最好具备一定的图形学基础,我推荐一本书<3D游戏编程大师技巧>,这本书完整的讲解了3D渲染流水线的方方面面。
二,本节教程的程序结构:
三,VertexShader(顶点着色器)和PixelShader(像素着色器)的作用:
在讲解VertexShader(顶点着色器)和PixelShader(像素着色器)的作用之前,我先放出一张D3D11简略版的3D渲染流水线图:
(1)VertexShader(顶点着色器):顶点着色器顾名思义,也就是对顶点进行操作,对顶点进行变换的着色器,看上面图红色圈住的部分就是顶点变换的过程了,而我们的VertexShader(顶点着色器),一般是对顶点进行上面红色圈的 世界变换(WorldTransform), 相机变换(也称视角变换,ViewTransform), 透视投影变换(PerspectiveProjection),(得注意的是"视口变换"不属于VertexShader,而是与"背面剔除'和"顶点属性线性插值"共属于光栅化阶段")剩下的几种变换经常是显卡帮我们自动进行的。
(2) PixelShader(像素着色器):像素着色器顾名思义,也就是对像素进行的操作的着色器,也就是上面图里的蓝色圈住的部分。像素着色器主要是对像素(像素的位置,颜色,向量等)操作,最终输出像素的颜色。
(3)VertexShader(顶点着色器)和PixelShader(像素着色器)之间的关系:这里用一个三角形作为例子,一个三角形由三个顶点ABC组成,三角形ABC位于局部空间,三个顶点ABC分别在VertexShader进行世界变换,相机变换等等各种变换,最终变换到屏幕空间,然后对屏幕空间的三角形ABC进行光栅化,得到三角形里面的像素,然后用PixelShader对像素进行操作,最终输出颜色到屏幕上。如下面图所示:
看图中的三角形ABC以及三角形ABC里面的像素,图中我用红色点表示像素,一个红色点就是一个像素。
(其实上面也不对,这里为了初学者教学的简便,我们就不引入其它种类的Shader了,以及深度缓存一大堆乱七八糟的)
三,程序的代码:
首先能GraphicsClass包含四个类,分别为D3DClass,ModelClass,CameraClass,ColorShaderClass
(1) GrapgicsClass
GraphicsClass.h
#pragma once
#ifndef _GRAPHICS_CLASS_H
#define _GRAPHICS_CLASS_H
#include"D3DClass.h"
#include"CameraClass.h"
#include"ColorShaderClass.h"
#include"ModelClass.h"
//全局变量
const bool FULL_SCREEN = false;
const bool VSYNC_ENABLE = true; //是尽可能快渲染还是限制帧渲染
const float SCREEN_FAR = 1000.0f; //视截体远裁面
const float SCREEN_NEAR = 0.1f; //视截体近裁面
class GraphicsClass
{
private:
//D3D类
D3DClass* mD3D;
//相机类,用于控制场景的相机
CameraClass* mCamera;
//用于控制VertexShader PixelShader,InputLayout
ColorShaderClass* mColorShader;
//用于控制VertexBuffer和IndexBuffer
ModelClass* mModel;
private:
bool Render();
public:
GraphicsClass();
GraphicsClass(const GraphicsClass&);
~GraphicsClass();
public:
bool Initialize(int ScreenWidth, int ScreenHeight, HWND hwnd);
void Shutdown();
bool Frame();
};
#endif // !_GRAPHICS_CLASS_H
(2) ColorShaderClass
ColorShaderClass.h (用于加载VertexShader和PixelShader)
#pragma once
#ifndef _COLOR_SHADER_CLASS_H
#define _COLOR_SHADER_CLASS_H
#define HR2(X) {if(FAILED(X)) { MessageBox(0,L"Create Failed",0,0); return false;}}
#include<d3d11.h>
#include<xnamath.h>
#include<D3DX11.h> //含编译Shader程序的函数
#include<d3dcompiler.h>
#include<fstream>
using namespace std;
class ColorShaderClass
{
private:
//常量缓存结构体
struct CBMatrix
{
XMMATRIX mWorldMatrix;
XMMATRIX mViewMatrix;
XMMATRIX mProjMatrix;
};
private:
ID3D11VertexShader* md3dVertexShader;
ID3D11PixelShader* md3dPixelShader;
ID3D11InputLayout* md3dInputLayout; //这与VertexShader相关联,因此要放在ColorShaderClass里,而不是D3DClass
ID3D11Buffer* mCBMatrixBuffer; //(常量)缓存,顶点索引也是用这个类型
private:
bool InitializeShader(ID3D11Device*, HWND, WCHAR*, WCHAR*); //用于创建InputLayout,VertexShader,PixelShader,常量缓存
bool ShutdownShader();
void OutputShaderErrorMessage(ID3D10Blob*, HWND, WCHAR*);
bool SetShaderParameter(ID3D11DeviceContext*, CXMMATRIX, CXMMATRIX, CXMMATRIX);
void RenderShader(ID3D11DeviceContext*, int);
public:
ColorShaderClass();
ColorShaderClass(const ColorShaderClass&);
~ColorShaderClass();
public:
bool Initialize(ID3D11Device*, HWND);
void Shutdown();
bool Render(ID3D11DeviceContext*, int, CXMMATRIX, CXMMATRIX, CXMMATRIX);
};
#endif
ColorShaderClass.CPP
#include"ColorShaderClass.h"
ColorShaderClass::ColorShaderClass()
{
md3dVertexShader=NULL;
md3dPixelShader=NULL;
md3dInputLayout=NULL;
mCBMatrixBuffer=NULL;
}
ColorShaderClass::ColorShaderClass(const ColorShaderClass&)
{
}
ColorShaderClass::~ColorShaderClass()
{
}
bool ColorShaderClass::Initialize(ID3D11Device* d3dDevice, HWND hwnd)
{
bool result;
result = InitializeShader(d3dDevice, hwnd, L"MyShader.fx", L"MyShader.fx");
if (!result)
return false;
return true;
}
void ColorShaderClass::Shutdown()
{
ShutdownShader();
}
bool ColorShaderClass::Render(ID3D11DeviceContext* d3dDeviceContext, int indexCount, CXMMATRIX worldMatrix, CXMMATRIX viewMatrix, CXMMATRIX ProjMatrix)
{
bool result;
//设置用来渲染的Shader属性
result = SetShaderParameter(d3dDeviceContext, worldMatrix, viewMatrix, ProjMatrix);
if (!result)
return false;
//渲染Shader
RenderShader(d3dDeviceContext, indexCount);
return true;
}
bool ColorShaderClass::InitializeShader(ID3D11Device* d3dDevice, HWND hwnd, WCHAR* VSFileName, WCHAR* PSFileName)
{
HRESULT result;
ID3D10Blob* errorMessage;
ID3D10Blob* VertexShaderBuffer;
ID3D10Blob* PixelShaderBuffer;
//第一,初始化参数
errorMessage = NULL;
VertexShaderBuffer=NULL;
PixelShaderBuffer=NULL;
//第二,编译VertexShader代码,并创建VertexShader
result = D3DX11CompileFromFile(VSFileName, NULL, NULL, "VS", "vs_5_0", D3DCOMPILE_ENABLE_STRICTNESS, 0, NULL, &VertexShaderBuffer, &errorMessage, NULL);
if (FAILED(result))
{
//存在错误信息
if (errorMessage)
{
OutputShaderErrorMessage(errorMessage, hwnd, VSFileName);
}
//不存在错误信息,也就是没有找到Shader文件
else
{
MessageBox(hwnd, L"can not find VS file", L"error", MB_OK);
}
}
HR2(d3dDevice->CreateVertexShader(VertexShaderBuffer->GetBufferPointer(),VertexShaderBuffer->GetBufferSize(),NULL,&md3dVertexShader));
//第三,编译PixelShader,并创建PixelShader
result = D3DX11CompileFromFile(PSFileName, NULL, NULL, "PS", "ps_5_0", D3DCOMPILE_ENABLE_STRICTNESS, 0, NULL, &PixelShaderBuffer, &errorMessage, NULL);
if (FAILED(result))
{
//存在错误信息
if (errorMessage)
{
OutputShaderErrorMessage(errorMessage, hwnd, PSFileName);
}
//不存在错误信息,也就是没有找到Shader文件
else
{
MessageBox(hwnd, L"can not find PS file", L"error", MB_OK);
}
}
HR2(d3dDevice->CreatePixelShader(PixelShaderBuffer->GetBufferPointer(), PixelShaderBuffer->GetBufferSize(), NULL, &md3dPixelShader));
//第四,填充输入布局形容结构体,创建输入布局
D3D11_INPUT_ELEMENT_DESC VertexInputLayout[] =
{
{ "POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0 }, // 96位即12个字节
{ "COLOR",0,DXGI_FORMAT_R32G32B32A32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA,0 },
};
unsigned int numElements = sizeof(VertexInputLayout) / sizeof(VertexInputLayout[0]); //布局数量
HR2(d3dDevice->CreateInputLayout(VertexInputLayout, numElements, VertexShaderBuffer->GetBufferPointer(), VertexShaderBuffer->GetBufferSize(), &md3dInputLayout));
//第五,释放VertexShaderBuffer和PixelShaderBuffer
VertexShaderBuffer->Release();
VertexShaderBuffer = NULL;
PixelShaderBuffer->Release();
PixelShaderBuffer = NULL;
//第六,设置(常量)缓存形容结构体,并创建常量缓存
D3D11_BUFFER_DESC matrixBufferDesc;
ZeroMemory(&matrixBufferDesc, sizeof(matrixBufferDesc));
matrixBufferDesc.Usage = D3D11_USAGE_DEFAULT;
matrixBufferDesc.ByteWidth = sizeof(CBMatrix); //结构体大小,必须为16字节倍数
matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
matrixBufferDesc.CPUAccessFlags = 0;
HR2(d3dDevice->CreateBuffer(&matrixBufferDesc, NULL, &mCBMatrixBuffer));
return true;
}
bool ColorShaderClass::ShutdownShader()
{
HR2(mCBMatrixBuffer);
HR2(md3dInputLayout);
HR2(md3dPixelShader);
HR2(md3dVertexShader);
return true;
}
void ColorShaderClass::OutputShaderErrorMessage(ID3D10Blob* errorMessage, HWND hwnd, WCHAR* shaderFilename)
{
char* compileErrors;
unsigned long bufferSize, i;
ofstream fout;
// 获取指向错误信息文本的指针
compileErrors = (char*)(errorMessage->GetBufferPointer());
// 获取错误信息文本的长度
bufferSize = errorMessage->GetBufferSize();
// 创建一个txt,用于写入错误信息
fout.open("shader-error.txt");
//想txt文件写入错误信息
for (i = 0; i<bufferSize; i++)
{
fout << compileErrors[i];
}
// 关闭文件
fout.close();
// Release the error message.
errorMessage->Release();
errorMessage = 0;
//弹出提醒的小窗口
MessageBox(hwnd, L"Error compiling shader. Check shader-error.txt for message.", shaderFilename, MB_OK);
}
bool ColorShaderClass::SetShaderParameter(ID3D11DeviceContext* d3dDeviceContext, CXMMATRIX worldMatrix, CXMMATRIX viewMatrix, CXMMATRIX ProjMatrix)
{
//D3D11_MAPPED_SUBRESOURCE mappedResource;
//CBMatrix* cbPtr;
unsigned int bufferNum;
//将矩阵转置,在传入常量缓存前进行转置,因为GPU对矩阵数据会自动进行一次转置
CBMatrix cb;
XMMATRIX worldMa = XMMatrixTranspose(worldMatrix);
XMMATRIX viewMa = XMMatrixTranspose(viewMatrix);
XMMATRIX ProjMa = XMMatrixTranspose(ProjMatrix);
cb.mWorldMatrix = worldMa;
cb.mViewMatrix = viewMa;
cb.mProjMatrix = ProjMa;
d3dDeviceContext->UpdateSubresource(mCBMatrixBuffer, 0, NULL, &cb, 0, 0);
/**/
//锁定常量缓存,这时候常量缓存和子资源关联在一起
//HR2(d3dDeviceContext->Map(mCBMatrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource));
//获取指向常量缓存数据的指针
//cbPtr = (CBMatrix*)mappedResource.pData;
//赋予常量缓存数据
//cbPtr->mProjMatrix = worldMa;
//cbPtr->mViewMatrix = viewMa;
//cbPtr->mProjMatrix = ProjMa;
//解锁常量缓存
//d3dDeviceContext->Unmap(mCBMatrixBuffer, 0);
//设置在顶点缓存中常量缓存的位置,注册号
bufferNum = 0;
//设置在VertexShader的常量缓存的值(带着更新的值)
d3dDeviceContext->VSSetConstantBuffers(bufferNum, 1, &mCBMatrixBuffer);
return true;
}
void ColorShaderClass::RenderShader(ID3D11DeviceContext* deviceContext, int indexCount)
{
//设置顶点输入布局
deviceContext->IASetInputLayout(md3dInputLayout);
//设置VertexShader和PixelShader
deviceContext->VSSetShader(md3dVertexShader, NULL, 0);
deviceContext->PSSetShader(md3dPixelShader, NULL, 0);
//渲染三角形
deviceContext->DrawIndexed(indexCount, 0, 0);
}
(3) ModelClass
ModelClass.h(用于加载顶点缓存和索引缓存)
#pragma once
#ifndef _MODEL_CLASS_H
#define _MODEL_CLASS_H
#include<d3d11.h>
#include<xnamath.h>
#define HR1(X) {if(FAILED(X)) { MessageBox(0,L"Create Failed",0,0); return false;}}
#define ReleaseCOM1(x) { if (x) { x->Release(); x = 0; } }
class ModelClass
{
private:
struct Vertex
{
XMFLOAT3 pos;
XMFLOAT4 color;
};
private:
ID3D11Buffer* md3dVertexBuffer; //顶点缓存
ID3D11Buffer* md3dIndexBuffer; //索引缓存
int mVertexCount;
int mIndexCount;
private:
bool InitializeBuffer(ID3D11Device* d3dDevice);
void ShutdownBuffer();
void RenderBuffers(ID3D11DeviceContext* d3dDeviceContext);
public:
ModelClass();
ModelClass(const ModelClass&);
~ModelClass();
public:
//Initialize是创建元素,Render是设置元素,Shutdown是Release
bool Initialize(ID3D11Device* d3dDevice);
void Shutdown();
void Render(ID3D11DeviceContext* d3dDeviceContext);
int GetIndexCount() { return mIndexCount; }
};
#endif
ModelClass.CPP
#include"ModelClass.h"
ModelClass::ModelClass()
{
md3dVertexBuffer=NULL; //顶点缓存
md3dIndexBuffer=NULL; //索引缓存
mVertexCount = 0;
mIndexCount = 0;
}
ModelClass::ModelClass(const ModelClass& other)
{
}
ModelClass::~ModelClass()
{
}
bool ModelClass::Initialize(ID3D11Device* d3dDevice)
{
bool result;
result = InitializeBuffer(d3dDevice);
if (!result)
return false;
return true;
}
void ModelClass::Shutdown()
{
ShutdownBuffer();
}
void ModelClass::Render(ID3D11DeviceContext* d3dDeviceContext)
{
//设置渲染管线的顶点缓存和索引缓存(IA阶段)
RenderBuffers(d3dDeviceContext);
}
bool ModelClass::InitializeBuffer(ID3D11Device* d3dDevice)
{
Vertex* vertexs=NULL;
WORD*indices=NULL; //一个字为两个字节
mVertexCount = 4;
mIndexCount = 6;
//创建顶点数组
vertexs = new Vertex[mVertexCount];
if (!vertexs)
return false;
//创建索引数组
indices = new WORD[mIndexCount];
if (!indices)
return false;
//赋予顶点数组数据和索引数组数据
vertexs[0].pos = XMFLOAT3(-1.0f, -1.0f, 0.0f);
vertexs[0].color = XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f);
vertexs[1].pos = XMFLOAT3(1.0f, 1.0f, 0.0f);
vertexs[1].color = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);
vertexs[2].pos = XMFLOAT3(1.0f, -1.0f, 0.0f);
vertexs[2].color = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);
vertexs[3].pos = XMFLOAT3(-1.0f, 1.0f, 0.0f);
vertexs[3].color = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);
//赋予索引数组数据
//注意用左手定则判定是不是背面
indices[0] = 0;
indices[1] = 3;
indices[2] = 2;
indices[3] = 1;
indices[4] = 2;
indices[5] = 3;
//第一,填充(顶点)缓存形容结构体和子资源数据结构体,并创建顶点缓存
D3D11_BUFFER_DESC vertexBufferDesc;
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof(Vertex) * mVertexCount;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = 0;
vertexBufferDesc.MiscFlags = 0;
vertexBufferDesc.StructureByteStride = 0;
D3D11_SUBRESOURCE_DATA vertexData;
vertexData.pSysMem = vertexs;
vertexData.SysMemPitch = 0;
vertexData.SysMemSlicePitch = 0;
HR1(d3dDevice->CreateBuffer(&vertexBufferDesc, &vertexData, &md3dVertexBuffer));
//第二,填充(索引)缓存形容结构体和子资源数据结构体,并创建索引缓存
D3D11_BUFFER_DESC indexBufferDesc;
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = sizeof(WORD) * mIndexCount;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
indexBufferDesc.StructureByteStride = 0;
D3D11_SUBRESOURCE_DATA indexData;
indexData.pSysMem = indices;
indexData.SysMemPitch = 0;
indexData.SysMemSlicePitch = 0;
HR1(d3dDevice->CreateBuffer(&indexBufferDesc, &indexData, &md3dIndexBuffer));
//释放顶点数组和索引数组(这时数据已经载入缓存,不需要这些数组了)
delete[]vertexs;
vertexs = NULL;
delete[]indices;
indices = NULL;
return true;
}
void ModelClass::ShutdownBuffer()
{
//释放顶点缓存和索引缓存
ReleaseCOM1(md3dIndexBuffer);
ReleaseCOM1(md3dVertexBuffer);
}
void ModelClass::RenderBuffers(ID3D11DeviceContext* d3dDeviceContext)
{
//设置顶点缓存
UINT stride = sizeof(Vertex); //每个顶点元素的跨度大小,或者说每个顶点元素的大小
UINT offset = 0;
d3dDeviceContext->IASetVertexBuffers(0, 1, &md3dVertexBuffer, &stride, &offset);
//设置索引缓存
d3dDeviceContext->IASetIndexBuffer(md3dIndexBuffer, DXGI_FORMAT_R16_UINT, 0); //Word为两个字节
//设置拓扑方式
d3dDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}
四,本节教程的程序得注意的地方:
(1)在一个.h文件里包含XNAMATH.h一定得先包含一个windows.h。
(2)XMMATRIX作为函数参数时应该用CXMMATRIX。
(3)指定顶点的索引值时得注意绕线方向,用左手定则,得到三角形法线方向跟相机LookAt方向为钝角的被正面,反之为背面.
(4)我的VertexShader和PixelShader都在从MyShader.fx文件中编译的,其实.HLSL文件.VS文件.PS文件.FX文件都能编译成Shader
(5)在.FX文件中常量缓存CBMatrix注意用register()注册,注册为b0,则在ColorShaderClass::SetShaderParameter()的d3dDeviceContext->VSSetConstantBuffers(bufferNum, 1, &mCBMatrixBuffer)的bufferNum为0,若注册为b1,则bufferNum为1,以此类推.
(6)在VertexShader阶段和PixelShade阶段,常量缓存的值一直未曾改变,不会进行光栅化,因此世界空间的平行光,聚光灯,点光源都是采用常量缓存。
(7)在对应与常量缓存的结构体一定是16个字节的倍数,XMMATRIX为64个字节,float为4个字节,实在要是怕忘记16字节对齐规则,可在结构体前加个前缀,__declspec(align(16)) ,比如
__declspec(align(16))
struct CbNeverChange //常量缓存结构严格16字节的倍数
{
DirectionLight DireLight;
PointLight PoLight;
SpotLight SpLight;
Material SkullMaterial;
XMFLOAT3 EyePostion;
};
五,本节教程的程序运行的结果:
六,程序源代码链接如下
点击打开链接
---------------------
作者:小毛狗
来源:优快云
原文:https://blog.youkuaiyun.com/qq_29523119/article/details/52716762
版权声明:本文为博主原创文章,转载请附上博文链接!