DirectX9使用Effect和Vertexshader、PixelShader的区别

时间:2021-03-14 17:05:53

    最近刚看完UnityShader的一些介绍和例子,发现理论上的东西都是含糊过去的,就还是老老实实去看dx。之前有学过dx,dx10,11的初始化太繁琐,代码是dx9的好几倍,手上也有本《DirectX3D HLSL高级实例精讲》里面用的就是dx9,所以就选择用dx9来写。

    最近刚看到不用effect来写,分成vertexshader.vs和pixeslshader.ps来写,就跟dx11的差不多,就没仔细看,直接开始写,然后遇到bug,调试了很久。刚开始还以为是用的版本太高的问题,用的是vs2017和Microsoft DirectX SDK (June 2010)。想了想effect都能用,那就应该不是这个问题。而且这本《DirectX3D HLSL高级实例精讲》书上的源码完全不跟实例相关,坑啊。只好一步步断点调试,发现有一个空指针。(抽个空会把《DirectX3D HLSL高级实例精讲》的源码上传下,不要期待有多大用处,用的是DXUT框架,vs2017+win10sdk不能编译成lib,就算编译好了,还会各种报错,也懒得弄个xp系统的,直接用之前学习的那套框架,简单粗暴点)

    先来effect源码:文件名是ColorShader.fx,如果用的是vs2017的话,记得在HLSL compiler设置为effect,shader model 2

float time;
float4x4 worldMatrix;
float4x4 viewMatrix;
float4x4 projectionMatrix;
texture modelTexture;

sampler ModelTextureSampler = sampler_state
{
	Texture = <modelTexture>;
	MipFilter = LINEAR;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
};

struct VertexInputType
{
	float4 vertex : POSITION;
	float2 texcoord : TEXCOORD0;
};

struct PixelInputType
{
	float4 pos : POSITION;
	float2 texcoord : TEXCOORD0;
};

PixelInputType ColorVertexShader(VertexInputType input)
{
	PixelInputType output;

	float4x4 worldViewMatrix = mul(worldMatrix, viewMatrix);
	float4x4 worldViewProjectionMatrix = mul(worldViewMatrix, projectionMatrix);
	output.pos = mul(input.vertex, worldViewProjectionMatrix);
	output.texcoord = input.texcoord;

	return output;
}

float4 ColorPixelShader(PixelInputType input) : COLOR
{
	float4 color = tex2D(ModelTextureSampler, input.texcoord);
	return color;
}

technique ColorTechnique
{
	pass pass0
	{
		CullMode = None;
		VertexShader = compile vs_2_0 ColorVertexShader();
		PixelShader = compile ps_2_0 ColorPixelShader();
	}
}

    接着是VertexShader,文件名为ColorVertexShader.vs

float4x4 worldMatrix;
float4x4 viewMatrix;
float4x4 projectionMatrix;
sampler modelTexture;


struct VertexInputType
{
	float4 vertex : POSITION;
	float2 texcoord : TEXCOORD0;
};

struct PixelInputType
{
	float4 pos : POSITION;
	float2 texcoord : TEXCOORD0;
};

PixelInputType ColorVertexShader(VertexInputType input)
{
	PixelInputType output;
	float4x4 worldViewMatrix = mul(worldMatrix, viewMatrix);
	float4x4 worldViewProjectionMatrix = mul(worldViewMatrix, projectionMatrix);
	output.pos = mul(input.vertex, worldViewProjectionMatrix);
	output.texcoord = input.texcoord;

	return output;
}

    PixelShader,文件名为ColorPixelShader.ps

float time;
sampler modelTexture;

struct PixelInputType
{
	float4 pos : POSITION;
	float2 texcoord : TEXCOORD0;
};

float4 ColorPixelShader(PixelInputType input) : COLOR
{
	float4 color = tex2D(modelTexture, input.texcoord) * cos(time);
	return color;
}    

    Shader代码其实很简单,就不多说了,先来说我遇到的问题:

bool ColorShaderClass::SetShaderParameters(IDirect3DDevice9* device, float time, IDirect3DTexture9* texture, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
	HRESULT result;
	D3DXHANDLE textureHandle;
	D3DXCONSTANT_DESC textureDesc;
	UINT count;
	
	result = device->SetVertexShader(m_vertexShader);
	if (FAILED(result))
		return false;

	result = device->SetPixelShader(m_pixelShader);
	if (FAILED(result))
		return false;

	result = m_vertexShaderTable->SetMatrix(device, "worldMatrix", &worldMatrix);
	if (FAILED(result))
		return false;

	result = m_vertexShaderTable->SetMatrix(device, "viewMatrix", &viewMatrix);
	if (FAILED(result))
		return false;

	result = m_vertexShaderTable->SetMatrix(device, "projectionMatrix", &projectionMatrix);
	if (FAILED(result))
		return false;

	// The variable of shader must be used, otherwise ID3DXConstantTable can't find it
	result = m_pixelShaderTable->SetFloat(device, "time", time);
	if (FAILED(result))
		return false;

	textureHandle = m_pixelShaderTable->GetConstantByName(nullptr, "modelTexture");
	if (!textureHandle)
		return false;

	// The first way to set texture in shader
	/*result = m_pixelShaderTable->GetConstantDesc(textureHandle, &textureDesc, &count);
	if (FAILED(result))
		return false;

	result = device->SetTexture(textureDesc.RegisterIndex, texture);
	if (FAILED(result))
		return false;*/

	// The second way to set texture in shader
	result = device->SetTexture(m_pixelShaderTable->GetSamplerIndex(textureHandle), texture);
	if (FAILED(result))
		return false;

	return true;
}

    我遇到的问题就是代码第一个英文注释的地方,(我写的,英文烂,语法错误什么的就不管了,如果要装逼,我可以用日文写),其实这个问题是在pixelshader中遇到的,问题就是不能设置pixelshader的变量。而且hlsl调试只能靠猜,搞得我还以为pixelshader是不能设置变量的。后来通过dx的ID3DXConstantTable中的GetConstantByName方法调试得到,在pixelshader中获取time的D3DXHANDLE为空指针,接着我在vertexshader中添加一个新的矩阵来测试,发现了问题所在,就是在VertexShader和PixelShder中不使用的变量,如果用代码去获得和设置都会报错,因为得到的都是空指针,你对空指针操作肯定出问题,估计是dx过滤了不使用的变量。但是最主要的一点是在Effect中没有这个问题,看我effect源码中的time就没有用到,但没有任何问题。

    使用effect的源码:

    ModelClass:

#include "ModelClass.h"

ModelClass::ModelClass()
{
	m_mesh = nullptr;
	m_colorShader = nullptr;
	m_texture = nullptr;
}

ModelClass::ModelClass(const ModelClass& other)
{
}

ModelClass::~ModelClass()
{
}

bool ModelClass::Initialize(IDirect3DDevice9* device, TCHAR* modelFilePath, TCHAR* textureFilePath)
{
	bool result;

	result = InitializeMesh(device, modelFilePath);
	if (!result)
		return false;

	result = InitializeTexture(device, textureFilePath);
	if (!result)
		return false;

	m_colorShader = new ColorShaderClass();
	if (!m_colorShader)
		return false;

	result = m_colorShader->Initialize(device);
	if (!result)
		return false;

	return true;
}

void ModelClass::Shutdown()
{
	if (m_mesh)
	{
		m_mesh->Release();
		m_mesh = nullptr;
	}

	if (m_colorShader)
	{
		m_colorShader->Shutdown();
		delete m_colorShader;
		m_colorShader = nullptr;
	}

	if (m_texture)
	{
		m_texture->Shutdown();
		delete m_texture;
		m_texture = nullptr;
	}
}

void ModelClass::Render(IDirect3DDevice9* device, float time, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
	bool result;
	UINT passMaxNum;

	result = m_colorShader->Render(device, time, m_texture->GetTexture(), worldMatrix, viewMatrix, projectionMatrix);
	if (!result)
		return;

	m_colorShader->GetEffect()->Begin(&passMaxNum, 0);
	for (UINT pass = 0; pass < passMaxNum; ++pass)
	{
		m_colorShader->GetEffect()->BeginPass(pass);
		m_mesh->DrawSubset(0);
		m_colorShader->GetEffect()->EndPass();
	}
	m_colorShader->GetEffect()->End();
}

bool ModelClass::InitializeMesh(IDirect3DDevice9* device, TCHAR* modelFilePath)
{
	HRESULT result;

	result = ::D3DXLoadMeshFromX(
		modelFilePath,
		D3DXMESH_MANAGED,
		device,
		nullptr, 
		nullptr,
		nullptr, 
		nullptr,
		&m_mesh);
	if (FAILED(result))
		return false;

	return true;
}

bool ModelClass::InitializeTexture(IDirect3DDevice9* device, TCHAR* textureFiltPath)
{
	bool result;

	m_texture = new TextureClass();
	if (!m_texture)
		return false;

	result = m_texture->Initialize(device, textureFiltPath);
	if (!result)
		return false;

	return true;
}

    ColorShaderClass:

#include "ColorShaderClass.h"

ColorShaderClass::ColorShaderClass()
{
	m_effect = nullptr;
}

ColorShaderClass::ColorShaderClass(const ColorShaderClass& other)
{
}

ColorShaderClass::~ColorShaderClass()
{
}

bool ColorShaderClass::Initialize(IDirect3DDevice9* device)
{
	bool result;

	result = InitializeShader(device, TEXT("ColorShader.fx"));
	if (!result)
		return false;

	return true;
}

void ColorShaderClass::Shutdown()
{
	if (m_effect)
	{
		m_effect->Release();
		m_effect = nullptr;
	}
}

bool ColorShaderClass::Render(IDirect3DDevice9* device, float time, IDirect3DTexture9* texture, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
	bool result;

	result = SetShaderParameters(time, texture, worldMatrix, viewMatrix, projectionMatrix);
	if (!result)
		return false;

	return true;
}

ID3DXEffect* ColorShaderClass::GetEffect()
{
	return m_effect;
}

bool ColorShaderClass::InitializeShader(IDirect3DDevice9* device, TCHAR* shaderPath)
{
	HRESULT result;
	ID3DXBuffer* errorMessage;

	errorMessage = nullptr;

	result = ::D3DXCreateEffectFromFile(device, shaderPath, nullptr, nullptr, 0, nullptr, &m_effect, &errorMessage);
	if (FAILED(result))
	{
		if (errorMessage)
			OutputShaderErrorMessage(errorMessage, shaderPath);
		else
			::MessageBox(nullptr, shaderPath, TEXT("Missing Shader File"), MB_OK | MB_ICONERROR);

		return false;
	}

	return true;
}

void ColorShaderClass::OutputShaderErrorMessage(ID3DXBuffer* errorMessage, TCHAR* shaderPath)
{
	char* compileErrors;
	ULONG bufferSize;
	ofstream fout;

	compileErrors = (char*)(errorMessage->GetBufferPointer());

	bufferSize = errorMessage->GetBufferSize();

	fout.open("shader-error.txt");

	for (ULONG i = 0; i < bufferSize; ++i)
		fout << compileErrors[i];

	fout.close();

	errorMessage->Release();
	errorMessage = nullptr;

	::MessageBox(nullptr, TEXT("Error compiling shader. Check shader-error.txt for message!"), shaderPath, MB_OK | MB_ICONERROR);
}

bool ColorShaderClass::SetShaderParameters(float time, IDirect3DTexture9* texture, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
	HRESULT result;

	result = m_effect->SetFloat("time", time);
	if (FAILED(result))
		return false;

	result = m_effect->SetTexture("modelTexture", texture);
	if (FAILED(result))
		return false;

	result = m_effect->SetMatrix("worldMatrix", &worldMatrix);
	if (FAILED(result))
		return false;

	result = m_effect->SetMatrix("viewMatrix", &viewMatrix);
	if (FAILED(result))
		return false;

	result = m_effect->SetMatrix("projectionMatrix", &projectionMatrix);
	if (FAILED(result))
		return false;

	result = m_effect->SetTechnique("ColorTechnique");
	if (FAILED(result))
		return false;

	return true;
}

    接着是VertexShader和PixelShader的源码,MoedlClass只有渲染地方不一样其它都是一样的

    ModelClass:

#include "ModelClass.h"

ModelClass::ModelClass() 
{
	m_mesh = nullptr;
	m_colorShader = nullptr;
	m_texture = nullptr;
}

ModelClass::ModelClass(const ModelClass& other)
{
}

ModelClass::~ModelClass()
{
}

bool ModelClass::Initialize(IDirect3DDevice9* device, TCHAR* modelFilePath, TCHAR* textureFilePath)
{
	bool result;

	result = InitializeMesh(device, modelFilePath);
	if (!result)
		return false;

	result = InitializeTexture(device, textureFilePath);
	if (!result)
		return false;

	m_colorShader = new ColorShaderClass();
	if (!m_colorShader)
		return false;

	result = m_colorShader->Initialize(device);
	if (!result)
		return false;

	return true;
}

void ModelClass::Shutdown()
{
	if (m_mesh)
	{
		m_mesh->Release();
		m_mesh = nullptr;
	}

	if (m_colorShader)
	{
		m_colorShader->Shutdown();
		delete m_colorShader;
		m_colorShader = nullptr;
	}

	if (m_texture)
	{
		m_texture->Shutdown();
		delete m_texture;
		m_texture = nullptr;
	}
}

void ModelClass::Render(IDirect3DDevice9* device, float time, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
	bool result;

	result = m_colorShader->Render(device, time, m_texture->GetTexture(), worldMatrix, viewMatrix, projectionMatrix);
	if (!result)
		return;

	m_mesh->DrawSubset(0);
}

bool ModelClass::InitializeMesh(IDirect3DDevice9* device, TCHAR* modelFilePath)
{
	HRESULT result;

	result = ::D3DXLoadMeshFromX(
		modelFilePath,
		D3DXMESH_MANAGED,
		device,
		nullptr,
		nullptr,
		nullptr,
		nullptr,
		&m_mesh);
	if (FAILED(result))
		return false;

	return true;
}

bool ModelClass::InitializeTexture(IDirect3DDevice9* device, TCHAR* textureFilePath)
{
	bool result;

	m_texture = new TextureClass();
	if (!m_texture)
		return false;

	result = m_texture->Initialize(device, textureFilePath);
	if (!result)
		return false;

	return true;
}

    ColorShaderClass:

#include "ColorShaderClass.h"

ColorShaderClass::ColorShaderClass()
{
	m_vertexShader = nullptr;
	m_vertexShaderTable = nullptr;
	m_pixelShader = nullptr;
	m_pixelShaderTable = nullptr;
}

ColorShaderClass::ColorShaderClass(const ColorShaderClass& other)
{
}

ColorShaderClass::~ColorShaderClass()
{
}

bool ColorShaderClass::Initialize(IDirect3DDevice9* device)
{
	TCHAR* vertexShaderPath; 
	TCHAR* pixelShaderPath;
	DWORD flag;
	HRESULT result;
	ID3DXBuffer* tempBuffer;
	ID3DXBuffer* errorMessage;

	vertexShaderPath = TEXT("ColorVertexShader.vs");
	pixelShaderPath = TEXT("ColorPixelShader.ps");
	flag = D3DXSHADER_DEBUG | D3DXSHADER_SKIPOPTIMIZATION;
	errorMessage = nullptr;

	result = ::D3DXCompileShaderFromFile(vertexShaderPath, nullptr, nullptr, "ColorVertexShader", "vs_2_0", flag, &tempBuffer, &errorMessage, &m_vertexShaderTable);
	if (FAILED(result))
	{
		if (errorMessage != nullptr)
			OutputShaderErrorMessage(errorMessage, vertexShaderPath);
		else
			::MessageBox(nullptr, vertexShaderPath, TEXT("Missing Shader File"), MB_OK | MB_ICONERROR);

		return false;
	}
	
	result = device->CreateVertexShader((DWORD*)tempBuffer->GetBufferPointer(), &m_vertexShader);
	if (FAILED(result))
		return false;

	result = ::D3DXCompileShaderFromFile(pixelShaderPath, nullptr, nullptr, "ColorPixelShader", "ps_2_0", flag, &tempBuffer, &errorMessage, &m_pixelShaderTable);
	if (FAILED(result))
	{
		if (errorMessage != nullptr)
			OutputShaderErrorMessage(errorMessage, pixelShaderPath);
		else
			::MessageBox(nullptr, pixelShaderPath, TEXT("Missing Shader File"), MB_OK | MB_ICONERROR);

		return false;
	}

	result = device->CreatePixelShader((DWORD*)tempBuffer->GetBufferPointer(), &m_pixelShader);
	if (FAILED(result))
		return false;

	return true;
}

void ColorShaderClass::Shutdown()
{
	if (m_vertexShader)
	{
		m_vertexShader->Release();
		m_vertexShader = nullptr;
	}

	if (m_vertexShaderTable)
	{
		m_vertexShaderTable->Release();
		m_vertexShaderTable = nullptr;
	}

	if (m_pixelShader)
	{
		m_pixelShader->Release();
		m_pixelShader = nullptr;
	}

	if (m_pixelShaderTable)
	{
		m_pixelShaderTable->Release();
		m_pixelShaderTable = nullptr;
	}
}

bool ColorShaderClass::Render(IDirect3DDevice9* device, float time, IDirect3DTexture9* texture, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
	bool result;

	result = SetShaderParameters(device, time, texture, worldMatrix, viewMatrix, projectionMatrix);
	if (!result)
		return false;

	return true;
}

void ColorShaderClass::OutputShaderErrorMessage(ID3DXBuffer* errorMessage, TCHAR* shaderPath)
{
	char* compileErrors;
	ULONG bufferSize;
	ofstream fout;

	compileErrors = (char*)(errorMessage->GetBufferPointer());

	bufferSize = errorMessage->GetBufferSize();

	fout.open("shader-error.txt");

	for (ULONG i = 0; i < bufferSize; ++i)
		fout << compileErrors[i];

	fout.close();

	errorMessage->Release();
	errorMessage = nullptr;

	::MessageBox(nullptr, TEXT("Error compiling shader. Check shader-error.txt for message!"), shaderPath, MB_OK | MB_ICONERROR);
}

bool ColorShaderClass::SetShaderParameters(IDirect3DDevice9* device, float time, IDirect3DTexture9* texture, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
	HRESULT result;
	D3DXHANDLE textureHandle;
	D3DXCONSTANT_DESC textureDesc;
	UINT count;
	
	result = device->SetVertexShader(m_vertexShader);
	if (FAILED(result))
		return false;

	result = device->SetPixelShader(m_pixelShader);
	if (FAILED(result))
		return false;

	result = m_vertexShaderTable->SetMatrix(device, "worldMatrix", &worldMatrix);
	if (FAILED(result))
		return false;

	result = m_vertexShaderTable->SetMatrix(device, "viewMatrix", &viewMatrix);
	if (FAILED(result))
		return false;

	result = m_vertexShaderTable->SetMatrix(device, "projectionMatrix", &projectionMatrix);
	if (FAILED(result))
		return false;

	// The variable of shader must be used, otherwise ID3DXConstantTable can't find it
	result = m_pixelShaderTable->SetFloat(device, "time", time);
	if (FAILED(result))
		return false;

	textureHandle = m_pixelShaderTable->GetConstantByName(nullptr, "modelTexture");
	if (!textureHandle)
		return false;

	// The first way to set texture in shader
	/*result = m_pixelShaderTable->GetConstantDesc(textureHandle, &textureDesc, &count);
	if (FAILED(result))
		return false;

	result = device->SetTexture(textureDesc.RegisterIndex, texture);
	if (FAILED(result))
		return false;*/

	// The second way to set texture in shader
	result = device->SetTexture(m_pixelShaderTable->GetSamplerIndex(textureHandle), texture);
	if (FAILED(result))
		return false;

	return true;
}