Directx11基础教程三之VertexShader,PixelShader,buffer



一,看本节教程前应该掌握:

     (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;
	};


五,本节教程的程序运行的结果:




六,程序源代码链接如下

点击打开链接


  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值