DX11 HLSL help, please.

Started by
3 comments, last by Hawkblood 10 years ago

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).

Advertisement

Have you checked whether D3D outputs any errors or warnings when you create the device with the debug flag?

If the debug output is clean, graphics debuggers are a very useful for making sure that the right buffers are bound and that their contents are correct. The new Visual Studio contains a built-in graphics debugger, but Crytek's RenderDoc is awesome too (and free).

current project: Roa

It's a very long story, but I figured it out. Here is what I decided on:

vs:


/////////////
// GLOBALS //
/////////////
cbuffer MatrixBuffer
{
    matrix worldMatrix;
    matrix viewMatrix;
    matrix projectionMatrix;
};


//////////////
// TYPEDEFS //
//////////////
struct VertexInputType
{
    float4 position : POSITION;
    float2 tex : TEXCOORD0;
    float3 normal : NORMAL;
    float3 tangent : TANGENT;
    float3 binormal : BINORMAL;
};

struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
    float3 normal : NORMAL;
    float3 tangent : TANGENT;
    float3 binormal : BINORMAL;
	float3 worldPos : TEXCOORD1;
	float3 facenormal :	NORMAL1;
};


////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType BumpMapVertexShader(VertexInputType input)
{
    PixelInputType output;
    

    // Change the position vector to be 4 units for proper matrix calculations.
    input.position.w = 1.0f;

    // Calculate the position of the vertex against the world, view, and projection matrices.
    output.position = mul(input.position, worldMatrix);
	output.worldPos=output.position;
    output.position = mul(output.position, viewMatrix);
    output.position = mul(output.position, projectionMatrix);
    
    // Store the texture coordinates for the pixel shader.
    output.tex = input.tex;
    
    // Calculate the normal vector against the world matrix only and then normalize the final value.
    output.normal = mul(input.normal, (float3x3)worldMatrix);
    output.normal = normalize(output.normal);


    // Calculate the tangent vector against the world matrix only and then normalize the final value.
    output.tangent = mul(input.tangent, (float3x3)worldMatrix);
    output.tangent = normalize(output.tangent);

    // Calculate the binormal vector against the world matrix only and then normalize the final value.
    output.binormal = mul(input.binormal, (float3x3)worldMatrix);
    output.binormal = normalize(output.binormal);

    float3x3 tbnMatrix = float3x3(input.tangent.x, input.tangent.y, input.tangent.z,
	                              input.binormal.x, input.binormal.y, input.binormal.z,
	                              input.normal.x, input.normal.y, input.normal.z);
	output.facenormal=mul(input.normal,tbnMatrix);
    return output;
}


ps:


/////////////
// GLOBALS //
/////////////


Texture2D shaderTextures[2];
SamplerState SampleType;


cbuffer LightBuffer
{
	float4 Ambient;
	float4 NumberOfLights;
	float4 PLPos[8];
	float4 PLColor[8];
	float4 PLRange[8];
	float4 DLightColor;
	float4 DLightDir;
	float4 SLPos[8];
	float4 SLDir[8];
	float4 SLColor[8];
	float4 SLData[8];//icone==.x, ocone==.y, range==.z
};


//////////////
// TYPEDEFS //
//////////////
struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
    float3 normal : NORMAL;
    float3 tangent : TANGENT;
    float3 binormal : BINORMAL;
	float3 worldPos : TEXCOORD1;
	float3 facenormal :	NORMAL1;
};

////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 BumpMapPixelShader(PixelInputType input) : SV_TARGET
{
    float4 textureColor;
    float4 bumpMap;
    float3 bumpNormal;
    float3 lightDir;
    float4 color=float4(0,0,0,0);
	float3 facenormal;

    // Sample the texture pixel at this location.
    textureColor = shaderTextures[0].Sample(SampleType, input.tex);
	
    // Sample the pixel in the bump map.
    bumpMap = shaderTextures[1].Sample(SampleType, input.tex);

    // Expand the range of the normal value from (0, +1) to (-1, +1).
    bumpMap = (bumpMap * 2.0f) - 1.0f;

    // Calculate the normal from the data in the bump map.
    bumpNormal = (bumpMap.x * input.tangent) + (bumpMap.y * input.binormal) + (bumpMap.z * input.normal);
    // Normalize the resulting bump normal.
    bumpNormal = normalize(bumpNormal);


    float3x3 tbnMatrix = float3x3(input.tangent.x, input.tangent.y, input.tangent.z,
	                              input.binormal.x, input.binormal.y, input.binormal.z,
	                              input.normal.x, input.normal.y, input.normal.z);

//************** do the point light
	float3 h,l;
	float nDotH,nDotL,power;
	{[branch]
		if (NumberOfLights.x>0){//directional

			l=-DLightDir.xyz;
			nDotL = saturate(dot(bumpNormal, l));
			color += DLightColor*nDotL;


			l = mul(l,tbnMatrix);
			h = normalize(l + input.facenormal);
        
			nDotH = saturate(dot(bumpNormal, h));
			power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, 256.0f);
        
			color += DLightColor*power;

		}
	}
	{[loop]
		for (int i=0;i<NumberOfLights.y;i++){

			l=PLPos[i].xyz-input.worldPos;//use this for distance calc
			{[branch]
				if (length(l)<PLRange[i].x*1.25f){
					lightDir=mul(l/PLRange[i].x,tbnMatrix);
					l=normalize(l);
					float atten=saturate(1.0f-dot(lightDir,lightDir));//*saturate(dot(-l,input.facenormal));

					h = normalize(l+input.facenormal);
					nDotL = saturate(dot(bumpNormal, l));
					nDotH = saturate(dot(bumpNormal, h));
					power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, 256.0f);
					color += (PLColor[i]*(nDotL+power)) * atten;
				}
			}
		}
	}
	{[loop]
		for (int i=0;i<NumberOfLights.z;i++){//spot lights
			l = (SLPos[i].xyz - input.worldPos);
			{[branch]
				if (length(l)<SLData[i].z*1.25f){
					l /= SLData[i].z;
					float atten = saturate(1.0f - dot(l, l));
					l = normalize(l);
					float2 cosAngles = cos(float2(SLData[i].y, SLData[i].x) * 0.5f);
					float3 dl=SLDir[i];//SLDir[i] should ALWAYS be normalized on input
					float spotDot = dot(-l, dl);
					float spotEffect = smoothstep(cosAngles[0], cosAngles[1], spotDot);
    
					atten *= spotEffect;
					h = normalize(l + input.worldPos);
        
					nDotL = saturate(dot(bumpNormal, l));
					nDotH = saturate(dot(bumpNormal, h));
					power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, 256.0f);
        
					color += (SLColor[i]*(nDotL+power)) * atten;
				}
			}
		}
	}
    // Combine the final bump light color and ambient with the texture color.
    color = saturate(color+Ambient) * textureColor;


	
    return color;
}


It works with maximum 1 directional light, 8 point lights, and 8 spot lights. I don't believe ANY scene would use all 17 possible, but I wanted to keep it simple and consistent.

The solution above doesn't seem to give me any specular highlights. I've tinkered with the calculations but haven't been able to get any positive results.

I finally figured out the specular problem. Here is the shader:

vs:


/////////////
// GLOBALS //
/////////////
cbuffer MatrixBuffer
{
    matrix worldMatrix;
    matrix viewMatrix;
    matrix projectionMatrix;
};


//////////////
// TYPEDEFS //
//////////////
struct VertexInputType
{
    float4 position : POSITION;
    float2 tex : TEXCOORD0;
    float3 normal : NORMAL;
    float3 tangent : TANGENT;
    float3 binormal : BINORMAL;
};

struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
    float3 normal : NORMAL;
    float3 tangent : TANGENT;
    float3 binormal : BINORMAL;
	float3 worldPos : TEXCOORD1;
	float3 cdir : TEXCOORD2;
};


////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType BumpMapVertexShader(VertexInputType input)
{
    PixelInputType output;
    

    // Change the position vector to be 4 units for proper matrix calculations.
    input.position.w = 1.0f;

    // Calculate the position of the vertex against the world, view, and projection matrices.
    output.position = mul(input.position, worldMatrix);
	output.worldPos=output.position;
    output.position = mul(output.position, viewMatrix);
    output.position = mul(output.position, projectionMatrix);
    
    // Store the texture coordinates for the pixel shader.
    output.tex = input.tex;
    
    // Calculate the normal vector against the world matrix only and then normalize the final value.
    output.normal = mul(input.normal, (float3x3)worldMatrix);
    output.normal = normalize(output.normal);


    // Calculate the tangent vector against the world matrix only and then normalize the final value.
    output.tangent = mul(input.tangent, (float3x3)worldMatrix);
    output.tangent = normalize(output.tangent);

    // Calculate the binormal vector against the world matrix only and then normalize the final value.
    output.binormal = mul(input.binormal, (float3x3)worldMatrix);
    output.binormal = normalize(output.binormal);

	output.cdir=normalize(-output.worldPos);

    return output;
}


ps:


/////////////
// GLOBALS //
/////////////


Texture2D shaderTextures[2];
SamplerState SampleType;


cbuffer LightBuffer
{
	float4 Ambient;
	float4 NumberOfLights;
	float4 PLPos[8];
	float4 PLColor[8];
	float4 PLRange[8];
	float4 DLightColor;
	float4 DLightDir;
	float4 SLPos[8];
	float4 SLDir[8];
	float4 SLColor[8];
	float4 SLData[8];//icone==.x, ocone==.y, range==.z
};


//////////////
// TYPEDEFS //
//////////////
struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
    float3 normal : NORMAL;
    float3 tangent : TANGENT;
    float3 binormal : BINORMAL;
	float3 worldPos : TEXCOORD1;
	float3 cdir : TEXCOORD2;
};

////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 BumpMapPixelShader(PixelInputType input) : SV_TARGET
{
    float4 textureColor;
    float4 bumpMap;
    float3 bumpNormal;
    float3 lightDir;
    float4 color=float4(0,0,0,0);
	float3 viewDirection;

    // Sample the texture pixel at this location.
    textureColor = shaderTextures[0].Sample(SampleType, input.tex);
	
    // Sample the pixel in the bump map.
    bumpMap = shaderTextures[1].Sample(SampleType, input.tex);

    // Expand the range of the normal value from (0, +1) to (-1, +1).
    bumpMap = (bumpMap * 2.0f) - 1.0f;

    // Calculate the normal from the data in the bump map.
    bumpNormal = (bumpMap.x * input.tangent) + (bumpMap.y * input.binormal) + (bumpMap.z * input.normal);
    // Normalize the resulting bump normal.
    bumpNormal = normalize(bumpNormal);


	float3 h,l;
	float nDotH,nDotL,power;
	{[branch]
		if (NumberOfLights.x>0){//directional

			l=-DLightDir.xyz;
			nDotL = saturate(dot(bumpNormal, l));
			h = normalize(l + input.cdir);
        
			nDotH = saturate(dot(bumpNormal, h));
			power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, 256.0f);
        
			color += DLightColor*(nDotL+power);

		}
	}
	{[loop]
		for (int i=0;i<NumberOfLights.y;i++){//point lights

			l=PLPos[i].xyz-input.worldPos;//use this for distance calc
			{[branch]
				if (length(l)<PLRange[i].x*1.25f){
					lightDir=l/PLRange[i].x;
					l=normalize(l);
					float atten=saturate(1.0f-dot(lightDir,lightDir));

					h = normalize(l+input.cdir);
					nDotL = saturate(dot(bumpNormal, l));
					nDotH = saturate(dot(bumpNormal, h));
					power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, 16.0f);
					color += (PLColor[i]*(nDotL+power)) * atten;
				}
			}
		}
	}
	{[loop]
		for (int i=0;i<NumberOfLights.z;i++){//spot lights
			l = (SLPos[i].xyz - input.worldPos);
			{[branch]
				if (length(l)<SLData[i].z*1.25f){
					l /= SLData[i].z;
					float atten = saturate(1.0f - dot(l, l));
					l = normalize(l);
					float2 cosAngles = cos(float2(SLData[i].y, SLData[i].x) * 0.5f);
					float3 dl=SLDir[i];//SLDir[i] should ALWAYS be normalized on input
					float spotDot = dot(-l, dl);
					float spotEffect = smoothstep(cosAngles[0], cosAngles[1], spotDot);
    
					atten *= spotEffect;

					h = normalize(l + input.cdir);
        
					nDotL = saturate(dot(bumpNormal, l));

					nDotH = saturate(dot(bumpNormal, h));
					power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, 256.0f);
        
					color += (SLColor[i]*(nDotL+power)) * atten;
				}
			}
		}
	}
    // Combine the final bump light color and ambient with the texture color.
    color = saturate(color+Ambient) * textureColor;

	
    return color;
}


The specular power is hard coded but it should be done as an input variable...... That shouldn't be a problem.

This topic is closed to new replies.

Advertisement