I've been trying to get a multiple light normal map shader to work in DX11. This shader (as a .fx file) I have used in a DX9 program already and it works fine. Here are the files:
the .vs:
cbuffer MatrixBuffer : register(cb0)
{
matrix worldMatrix;
matrix worldInverseTransposeMatrix;
matrix worldViewProjectionMatrix;
};
struct VS_INPUT
{
float3 position : POSITION;
float2 texCoord : TEXCOORD0;
float3 normal : NORMAL;
float4 tangent : TANGENT;
};
struct VS_OUTPUT
{
float4 position : POSITION;
float3 CamWorld : POSITION1;
float3 worldPos : TEXCOORD0;
float2 texCoord : TEXCOORD1;
float3 normal : TEXCOORD2;
float3 tangent : TEXCOORD3;
float3 bitangent : TEXCOORD4;
};
VS_OUTPUT VS_AllLighting(VS_INPUT IN)
{
VS_OUTPUT OUT;
OUT.position = mul(float4(IN.position, 1.0f), worldViewProjectionMatrix);
OUT.worldPos = mul(float4(IN.position, 1.0f), worldMatrix).xyz;
OUT.texCoord = IN.texCoord;
OUT.normal = mul(IN.normal, (float3x3)worldInverseTransposeMatrix);
OUT.normal =normalize(OUT.normal);
OUT.tangent = mul(IN.tangent.xyz, (float3x3)worldInverseTransposeMatrix);
OUT.tangent = normalize(OUT.tangent);
OUT.bitangent = cross(OUT.normal, OUT.tangent) * IN.tangent.w;
OUT.bitangent = normalize(OUT.bitangent);
float3x3 tbnMatrix = float3x3(OUT.tangent.x, OUT.bitangent.x, OUT.normal.x,
OUT.tangent.y, OUT.bitangent.y, OUT.normal.y,
OUT.tangent.z, OUT.bitangent.z, OUT.normal.z);
OUT.CamWorld = normalize(mul( - OUT.worldPos, tbnMatrix));
return OUT;
}
and the .ps:
struct DirectionalLight
{
float4 dir;
float4 diffuse;
};
struct SpotLight
{
float4 dir;
float4 pos; // world space position
float4 diffuse;
float4 specular;
float4 SpotInfo;//x==radius, y==ICone, z==OCone
};
struct PointLight
{
float4 pos; // world space position
float4 diffuse;
float4 radius;
};
struct Material
{
float4 ambient;
float4 diffuse;
float4 emissive;
float4 specular;
float4 shininess;
};
cbuffer LightsInfoBuffer : register(cb1)
{
float4 NumberOfLights;
/*
DirectionalLight DLight;//no more than 1
PointLight PLight[8];
SpotLight SLight[8];
float4 globalAmbient;
Material material;
float4 cameraPos;
*/
};
Texture2D Texture[2];
SamplerState SampleType;
struct VS_OUTPUT
{
float4 position : POSITION;
float3 CamWorld : POSITION1;
float3 worldPos : TEXCOORD0;
float2 texCoord : TEXCOORD1;
float3 normal : TEXCOORD2;
float3 tangent : TEXCOORD3;
float3 bitangent : TEXCOORD4;
};
float4 PS_AllLighting(VS_OUTPUT IN) : SV_TARGET
{
/*
float3x3 tbnMatrix = float3x3(IN.tangent.x, IN.bitangent.x, IN.normal.x,
IN.tangent.y, IN.bitangent.y, IN.normal.y,
IN.tangent.z, IN.bitangent.z, IN.normal.z);
float3 l = float3(0.0f, 0.0f, 0.0f);
float3 h = float3(0.0f, 0.0f, 0.0f);
float atten = 0.0f;
float nDotL = 0.0f;
float nDotH = 0.0f;
float power = 0.0f;
float4 color = float4(0.0f, 0.0f, 0.0f, 0.0f);
float4 ShinnyMap = Texture[1].Sample(SampleType, IN.texCoord);
float3 n= ShinnyMap.rgb *2.0f-1.0f;//this should already be normalized
{[loop]
for (int i=0;i<NumberOfLights.x;i++){
l = mul(-DLight.dir.xyz, tbnMatrix);
l = normalize(l);
h = normalize(l + IN.CamWorld);
nDotL = saturate(dot(n, l));
nDotH = saturate(dot(n, h));
power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, material.shininess.x);
color += ((material.diffuse * DLight.diffuse )+
(power*ShinnyMap.w ))* nDotL;
}
}
{[loop]
for (int i=0;i<NumberOfLights.z;i++){
{[branch]
if (distance(SLight[i].pos.xyz , IN.worldPos)<SLight[i].SpotInfo.x*1.25f){
l = mul((SLight[i].pos.xyz - IN.worldPos) / SLight[i].SpotInfo.x, tbnMatrix);
atten = saturate(1.0f - dot(l, l));
l = normalize(l);
float2 cosAngles = cos(float2(SLight[i].SpotInfo.z, SLight[i].SpotInfo.y) * 0.5f);
float3 dl=mul(SLight[i].dir.xyz,tbnMatrix);//lights[i].dir should ALWAYS be normalized on input
float spotDot = dot(-l, dl);
float spotEffect = smoothstep(cosAngles[0], cosAngles[1], spotDot);
atten *= spotEffect;
h = normalize(l + IN.CamWorld);
nDotL = saturate(dot(n, l));
nDotH = saturate(dot(n, h));
power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, material.shininess.x);
color += ((material.diffuse * SLight[i].diffuse* nDotL )+
(power *ShinnyMap.w)) * atten;
}
}
}
}
{[loop]
for (int i=0;i<NumberOfLights.y;i++){
{[branch]
if (distance(PLight[i].pos.xyz , IN.worldPos)<PLight[i].radius.x*1.25f){
l = mul((PLight[i].pos.xyz - IN.worldPos) / PLight[i].radius.x, tbnMatrix);
atten = saturate(1.0f - dot(l, l));
l = normalize(l);
h = normalize(l + IN.CamWorld);
nDotL = saturate(dot(n, l));
nDotH = saturate(dot(n, h));
power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, material.shininess.x);
color += ((material.diffuse * PLight[i].diffuse* nDotL )+
(power *ShinnyMap.w)) * atten;
}
}
}
}
color += material.ambient * globalAmbient +material.emissive;
// return color * Texture[0].Sample(SampleType, IN.texCoord);
*/
return float4(1,1,1,1);
}
I rem'd out EVERYTHING in the .ps except the return color (totally white) for testing. The problem is that I can't even get the object to appear white (or even appear at all). I have the object with the appropriate vertex structure:
struct NMVertex
{
NMVertex(){}
D3DXVECTOR3 pos;
D3DXVECTOR2 texCoord;
D3DXVECTOR3 normal;
D3DXVECTOR4 tangent;
};
and I render like this:
--set up the object:
void EFFECTMESH::Render(void){
unsigned int stride;
unsigned int offset;
// Set vertex buffer stride and offset.
stride = sizeof(NMVertex);
offset = 0;
// Set the vertex buffer to active in the input assembler so it can be rendered.
GE->devcon->IASetVertexBuffers(0, 1, &VB, &stride, &offset);
// Set the index buffer to active in the input assembler so it can be rendered.
GE->devcon->IASetIndexBuffer(IB, DXGI_FORMAT_R32_UINT, 0);
// Set the type of primitive that should be rendered from this vertex buffer, in this case triangles.
GE->devcon->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}
and I call the shader: (the NormalMapShaderClass::Render(....) function)
NormalMapShaderClass::NormalMapShaderClass()
{
m_vertexShader = 0;
m_pixelShader = 0;
m_layout = 0;
m_matrixBuffer = 0;
m_LightsInfoBuffer=0;
m_sampleState = 0;
}
NormalMapShaderClass::NormalMapShaderClass(const NormalMapShaderClass& other)
{
}
NormalMapShaderClass::~NormalMapShaderClass()
{
}
void NormalMapShaderClass::Shutdown()
{
// Shutdown the vertex and pixel shaders as well as the related objects.
ShutdownShader();
return;
}
bool NormalMapShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix,
D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView** textureArray)
{
bool result;
// Set the shader parameters that it will use for rendering.
result = SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix, textureArray);
if(!result)
{
return false;
}
// Now render the prepared buffers with the shader.
RenderShader(deviceContext, indexCount);
return true;
}
bool NormalMapShaderClass::InitializeShader(ID3D11Device* device, HWND hwnd, WCHAR* vsFilename, WCHAR* psFilename)
{
HRESULT result;
ID3D10Blob* errorMessage;
ID3D10Blob* vertexShaderBuffer;
ID3D10Blob* pixelShaderBuffer;
D3D11_INPUT_ELEMENT_DESC polygonLayout[4];
unsigned int numElements;
D3D11_BUFFER_DESC matrixBufferDesc,lightsBufferDesc;
D3D11_SAMPLER_DESC samplerDesc;
// Initialize the pointers this function will use to null.
errorMessage = 0;
vertexShaderBuffer = 0;
pixelShaderBuffer = 0;
// Compile the vertex shader code.
result = D3DX11CompileFromFileW(vsFilename, NULL, NULL, "VS_AllLighting", "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, (LPCSTR)vsFilename, "Missing Shader File", MB_OK);
}
return false;
}
// Compile the pixel shader code.
result = D3DX11CompileFromFileW(psFilename, NULL, NULL, "PS_AllLighting", "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, (LPCSTR)psFilename, "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 pixel 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 stucture 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;
polygonLayout[2].SemanticName = "NORMAL";
polygonLayout[2].SemanticIndex = 0;
polygonLayout[2].Format = DXGI_FORMAT_R32G32B32_FLOAT;
polygonLayout[2].InputSlot = 0;
polygonLayout[2].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
polygonLayout[2].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
polygonLayout[2].InstanceDataStepRate = 0;
polygonLayout[3].SemanticName = "TANGENT";
polygonLayout[3].SemanticIndex = 0;
polygonLayout[3].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
polygonLayout[3].InputSlot = 0;
polygonLayout[3].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
polygonLayout[3].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
polygonLayout[3].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 matrix constant buffer that is in the vertex shader.
matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
matrixBufferDesc.ByteWidth = sizeof(MatrixBufferType);
matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
matrixBufferDesc.MiscFlags = 0;
matrixBufferDesc.StructureByteStride = 0;
// Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class.
result = device->CreateBuffer(&matrixBufferDesc, NULL, &m_matrixBuffer);
if(FAILED(result))
{
return false;
}
lightsBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
lightsBufferDesc.ByteWidth = sizeof(LightsInfoBufferType);
lightsBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
lightsBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
lightsBufferDesc.MiscFlags = 0;
lightsBufferDesc.StructureByteStride = 0;
// Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class.
result = device->CreateBuffer(&lightsBufferDesc, NULL, &m_LightsInfoBuffer);
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;
}
return true;
}
void NormalMapShaderClass::ShutdownShader()
{
// Release the sampler state.
if(m_sampleState)
{
m_sampleState->Release();
m_sampleState = 0;
}
// Release the matrix constant buffer.
if(m_matrixBuffer)
{
m_matrixBuffer->Release();
m_matrixBuffer = 0;
}
if(m_LightsInfoBuffer)
{
m_LightsInfoBuffer->Release();
m_LightsInfoBuffer = 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;
}
void NormalMapShaderClass::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 << compileErrors[i];
}
// Close the file.
fout.close();
// Release the error message.
errorMessage->Release();
errorMessage = 0;
// Pop a message up on the screen to notify the user to check the text file for compile errors.
MessageBox(hwnd, "Error compiling shader. Check shader-error.txt for message.", (LPCSTR)shaderFilename, MB_OK);
return;
}
bool NormalMapShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, D3DXMATRIX worldMatrix, D3DXMATRIX WorldInverseTransposeMatrix,
D3DXMATRIX WorldViewProjectionMatrix, ID3D11ShaderResourceView** textureArray)
{
HRESULT result;
D3D11_MAPPED_SUBRESOURCE mappedResource;
MatrixBufferType* dataPtr;
unsigned int bufferNumber;
/*
// Transpose the matrices to prepare them for the shader.
D3DXMatrixTranspose(&worldMatrix, &worldMatrix);
D3DXMatrixTranspose(&WorldInverseTransposeMatrix, &WorldInverseTransposeMatrix);
D3DXMatrixTranspose(&WorldViewProjectionMatrix, &WorldViewProjectionMatrix);
*/
// Lock the constant buffer so it can be written to.
result = deviceContext->Map(m_matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if(FAILED(result))
{
return false;
}
// Get a pointer to the data in the constant buffer.
dataPtr = (MatrixBufferType*)mappedResource.pData;
// Copy the matrices into the constant buffer.
dataPtr->worldMatrix = worldMatrix;
dataPtr->worldInverseTransposeMatrix = WorldInverseTransposeMatrix;
dataPtr->worldViewProjectionMatrix = WorldViewProjectionMatrix;
// Unlock the constant buffer.
deviceContext->Unmap(m_matrixBuffer, 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_matrixBuffer);
//*********************************************************************************************************
// Lock the constant buffer so it can be written to.
result = deviceContext->Map(m_LightsInfoBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if(FAILED(result))
{
return false;
}
// Get a pointer to the data in the constant buffer.
LightsInfoBufferType *dataPtr2 = (LightsInfoBufferType*)mappedResource.pData;
dataPtr2->NumberOfLights=D3DXVECTOR4(1,0,0,0);
/*
// Copy the matrices into the constant buffer.
dataPtr2->material.ambient=D3DXVECTOR4(1,1,1,1);
dataPtr2->material.diffuse=D3DXVECTOR4(1,1,1,1);
dataPtr2->material.emissive=D3DXVECTOR4(1,1,1,1);
dataPtr2->material.specular=D3DXVECTOR4(1,1,1,1);
dataPtr2->material.shininess=D3DXVECTOR4(64,0,0,0);
dataPtr2->cameraPos=D3DXVECTOR4(0,0,0,0);
dataPtr2->globalAmbient=D3DXVECTOR4(1,1,1,1);
dataPtr2->DLight.diffuse=D3DXVECTOR4(1,1,1,1);
dataPtr2->DLight.dir=D3DXVECTOR4(1,0,0,0);
*/
// Unlock the constant buffer.
deviceContext->Unmap(m_LightsInfoBuffer, 0);
// Set the position of the constant buffer in the vertex shader.
bufferNumber = 1;
// Now set the constant buffer in the vertex shader with the updated values.
deviceContext->PSSetConstantBuffers(bufferNumber, 1, &m_LightsInfoBuffer);
// Set shader texture resource in the pixel shader.
deviceContext->PSSetShaderResources(0, 2, textureArray);
return true;
}
void NormalMapShaderClass::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 this triangle.
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 triangle.
deviceContext->DrawIndexed(indexCount, 0, 0);
return;
}
It uses the following matrices:
world
WIT(inverse, transpose of the world)
and WVP(world*view*projection)
as I have done in DX9 (which works perfectly).
EDIT: I totally forgot to give you my request. I would like some help figuring out why this isn't working. I know the mesh works because it works with other shaders (when I have the vertex data correctly assembled).