最近刚看完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;
}