Sign in to follow this  
WarrenHarding

Help using Rocket Commander parallax mapping shader in DirectX

Recommended Posts

Hello, Recently I've gotten into shaders and have tried following the Rocket Commander tutorials on it (specifically six, seven, and eight). I use C++, so the source code doesn't help me, but I've been focusing on using FX Composer, and the tutorial goes into great detail about it. My only experience with shaders is from DirectXTutorials.com, using its simple shader and its code which was essentially loading it with D3DXCreateEffectFromFile, setting the world, view, and projection matrices for it, and drawing a teapot mesh after calling ID3DXEffect::Begin and ID3DXEffect::BeginPass. It worked perfectly, but clearly the parallax mapping shader is much more complicated. I used D3DXCreateEffectFromFile to load the effect file, and set the variables he seems to set in his videos. I intended to use the shader on a simple square mesh, which looked fine in FX Composer. Originally nothing was rendered, but after some tinkering I got it to draw a pure white square, which is a far cry from how it should look. I honestly don't know what the problem is, but I'm likely making some beginner's mistake considering my inexperience with it. The parallax mapping shader code:
// Project: Rocket Commander, File: ParallaxMapping.fx
// Creation date: 01.11.2005 04:56
// Last modified: 01.11.2005 18:13
// Author: Benjamin Nitschke (abi@exdream.com) (c) 2005
// Note: To test this use FX Composer from NVIDIA!

// Uncomment the mode you need (only change is lightDir and tangent matrix calculation).
//#define FOR_ABI_ENGINE
//#define FOR_3DS_MAX
#define FOR_FX_COMPOSER

string description = "Parallax normal mapping shaders for a directional light";

// Shader techniques in this file, all shaders work with vs/ps 1.1, shaders not
// working with 1.1 have names with 20 at the end:
// Specular           : Full vertex ambient+diffuse+specular lighting
// Specular20         : Same for ps20, only required for 3DS max to show shader!

// Default variables, supported by the engine (may not be used here)
// If you don't need any global variable, just comment it out, this way
// the game engine does not have to set it!
//obs: float4x4 WorldViewProj         : WorldViewProjection;
float4x4 viewProj         : ViewProjection;
float4x4 World            : World;
float4x4 viewInverse      : ViewInverse;

float3 lightDir : Direction
<
	string UIName = "Light Direction";
	string Object = "DirectionalLight";
	string Space = "World";
> = {-0.65f, 0.65f, -0.39f}; // Normalized by app. FxComposer still uses inverted stuff :(

// The ambient, diffuse and specular colors are pre-multiplied with the light color!
float4 ambientColor : Ambient
<
	string UIName = "Ambient Color";
	string Space = "material";
> = {0.1f, 0.1f, 0.1f, 1.0f};
//> = {0.25f, 0.25f, 0.25f, 1.0f};

float4 diffuseColor : Diffuse
<
	string UIName = "Diffuse Color";
	string Space = "material";
> = {1.0f, 1.0f, 1.0f, 1.0f};

float4 specularColor : Specular
<
	string UIName = "Specular Color";
	string Space = "material";
> = {1.0f, 1.0f, 1.0f, 1.0f};

float shininess : SpecularPower
<
	string UIName = "Specular Power";
	string UIWidget = "slider";
	float UIMin = 1.0;
	float UIMax = 128.0;
	float UIStep = 1.0;
> = 16.0;

float parallaxAmount
<
	string UIName = "Parallax amount";
	string UIWidget = "slider";
	float UIMin = 0.0;
	float UIMax = 1.0;
	float UIStep = 0.0001;
> = 0.033f;

// Texture and samplers
texture diffuseTexture : Diffuse
<
	string UIName = "Diffuse Texture";
	string ResourceName = "C:\Users\James B\Documents\Visual Studio 2008\Projects\GUI\Marble.dds";
>;
sampler diffuseTextureSampler = sampler_state
{
	Texture = <diffuseTexture>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;
};

texture normalTexture : Diffuse
<
	string UIName = "Normal Texture";
	string ResourceName = "C:\Users\James B\Documents\Visual Studio 2008\Projects\GUI\MarbleNormal.dds";
>;
sampler normalTextureSampler = sampler_state
{
	Texture = <normalTexture>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;
};

texture heightTexture : Diffuse
<
	string UIName = "Height Texture";
	string ResourceName = "C:\Users\James B\Documents\Visual Studio 2008\Projects\GUI\MarbleHeight.dds";
>;
sampler heightTextureSampler = sampler_state
{
	Texture = <heightTexture>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;
};

texture NormalizeCubeTexture : Environment
<
	string UIName = "Normalize Cube Map Texture";
	string ResourceType = "CUBE";
//	string ResourceName = "R:\\Shaders\\NormalizeCubeMap.dds";
>;

samplerCUBE NormalizeCubeTextureSampler = sampler_state
{
	Texture = <NormalizeCubeTexture>;
	// Clamp isn't good for negative values we need to normalize!
	AddressU  = Wrap;//Clamp;
	AddressV  = Wrap;//Clamp;
	AddressW  = Wrap;//Clamp;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = None;
};

//----------------------------------------------------

// Vertex input structure (used for ALL techniques here!)
struct VertexInput
{
	float3 pos      : POSITION;
	float2 texCoord : TEXCOORD0;
	float3 normal   : NORMAL;
	float3 tangent	: TANGENT;
};

//----------------------------------------------------
/*obs
// Common functions
float4 TransformPosition(float3 pos)//float4 pos)
{
	//this eats up 1 more instruction:
	return mul(float4(pos.xyz, 1), WorldViewProj);
	//needs float4:
	//return mul(pos, WorldViewProj);
} // TransformPosition(.)
*
float3 GetWorldPos(float3 pos)
{
	return mul(float4(pos, 1), World).xyz;
} // GetWorldPos(.)
*/
float3 GetCameraPos()
{
	return viewInverse[3].xyz;
} // GetCameraPos()

float3 CalcNormalVector(float3 nor)
{
	return normalize(mul(nor, (float3x3)World));//WorldInverseTranspose));
} // CalcNormalVector(.)

// Get light direction, inverted for FX Composer, else it will just return lightDir!
float3 GetLightDir()
{
//#ifdef FOR_FX_COMPOSER
//	// In FX Composer the light direction is inverted.
//	return -lightDir;
//#else
	// For our engine and 3DSMax
	return lightDir;
//#endif
} // GetLightDir()

float3x3 ComputeTangentMatrix(float3 tangent, float3 normal)
{
	// Compute the 3x3 tranform from tangent space to object space
	float3x3 WorldToTangentSpace;
#ifdef FOR_ABI_ENGINE
	// For Abi engine ^^ we have to invert tangent because
	// we don't use ATI style normal map.
	WorldToTangentSpace[0] = mul(cross(tangent, normal), World);
	WorldToTangentSpace[1] = mul(tangent, World);
	WorldToTangentSpace[2] = mul(normal, World);
	return WorldToTangentSpace;
#endif
#ifdef FOR_3DS_MAX
	// Using 3DS Max format here (binormal, tangent, normal), i guess its left handed.
	// Also used in our engine and compatible with most other tools too :)
	// Will look wrong in FX Composer (rotated degrees wrong)
	WorldToTangentSpace[0] = mul(cross(normal, tangent), World);
	WorldToTangentSpace[1] = mul(tangent, World);
	WorldToTangentSpace[2] = mul(normal, World);
	return WorldToTangentSpace;
#endif
#ifdef FOR_FX_COMPOSER
	// For FX Composer use this format:
	WorldToTangentSpace[0] = mul(tangent, World);
	WorldToTangentSpace[1] = mul(cross(tangent, normal), World);
	WorldToTangentSpace[2] = mul(normal, World);
	return WorldToTangentSpace;
#endif
} // ComputeTangentMatrix(..)

//----------------------------------------------------

// Helpers for constant instancing
//we need rotation too, just update WorldMatrix, not as fast, but much easier:
//float4 objPos = {0, 0, 0, 1}; // xyz is pos, w is size
//just use diffuseColor now: float4 objColor = {1, 1, 1, 1}; // color with alpha

// vertex shader output structure (optimized for ps_1_1)
struct VertexOutput_Specular
{
	float4 pos          : POSITION;
	float2 diffTexCoord : TEXCOORD0;
	float2 normTexCoord : TEXCOORD1;
	float3 viewVec      : TEXCOORD2;
	float3 lightVec     : TEXCOORD3;
	float3 lightVecDiv3 : COLOR0;
};

// Vertex shader function
VertexOutput_Specular VS_Specular(VertexInput In)
{
	VertexOutput_Specular Out = (VertexOutput_Specular) 0;
	
	float4 WorldVertPos = mul(float4(In.pos.xyz, 1), World);
	Out.pos = mul(WorldVertPos, viewProj);
	//obs: Out.pos = TransformPosition(In.pos);// * objPos.w + objPos.xyz);
	
	// Duplicate texture coordinates for diffuse and normal maps
	Out.diffTexCoord = In.texCoord;
	Out.normTexCoord = In.texCoord;

	// Compute the 3x3 tranform from tangent space to object space
	float3x3 WorldToTangentSpace =
		ComputeTangentMatrix(In.tangent, In.normal);

	float3 WorldEyePos = GetCameraPos();
	//already defined: float3 WorldVertPos = GetWorldPos(In.pos);

	// Transform light vector and pass it as a color (clamped from 0 to 1)
	// For ps_2_0 we don't need to clamp form 0 to 1
	float3 lightVec = normalize(mul(WorldToTangentSpace, GetLightDir()));
	Out.lightVec = 0.5 + 0.5 * lightVec;
	Out.lightVecDiv3 = 0.5 + 0.5 * lightVec / 3;
	Out.viewVec = //0.5 + 0.5 *
		mul(WorldToTangentSpace, WorldEyePos - WorldVertPos);

	//obs: Out.color = objColor;
	
	// And pass everything to the pixel shader
	return Out;
} // VS_Specular(.)

// Techniques
technique Specular
{
	pass P0
	{
		VertexShader = compile vs_1_1 VS_Specular();
		sampler[0] = (diffuseTextureSampler);
		sampler[1] = (normalTextureSampler);
		sampler[2] = (NormalizeCubeTextureSampler);
		PixelShaderConstant1[0] = <ambientColor>;
		PixelShaderConstant1[2] = <diffuseColor>;
		PixelShaderConstant1[3] = <specularColor>;
		PixelShader = asm
		{
			// Optimized for ps_1_1, uses all possible 8 instructions.
			ps_1_1
			// Helper to calculate fake specular power.
			def c1, 0, 0, 0, -0.25
			//def c2, 0, 0, 0, 4
			def c4, 1, 0, 0, 1
			// Sample diffuse and normal map
			tex t0
			tex t1
			// Normalize view vector (t2)
			tex t2
			// Light vector (t3)
			texcoord t3
			// v0 is lightVecDiv3!
			// Convert agb to xyz (costs 1 instuction)
			lrp r1.xyz, c4, t1.w, t1
			// Now work with r1 instead of t1
			dp3_sat r0.xyz, r1_bx2, t3_bx2
			mad r1.xyz, r1_bx2, r0, -v0_bx2
			dp3_sat r1, r1, t2_bx2
			// Increase pow(spec) effect
			//obs: mul r0.rgb, r0, v1
			mul_x2_sat r1.w, r1.w, r1.w
			//we have to skip 1 mul because we lost 1 instruction because of agb
			//mul_x2_sat r1.w, r1.w, r1.w
			mad r0.rgb, r0, c2, c0
			// Combine 2 instructions because we need 1 more to set alpha!
			+add_sat r1.w, r1.w, c1.w
			mul r0.rgb, t0, r0
			+mul_x2_sat r1.w, r1.w, r1.w
			mad r0.rgb, r1.w, c3, r0
			// Set alpha from texture to result color!
			// Also multiplied by diffuseColor.a.
			+mul r0.a, t0.a, c2.a
		};
	} // pass P0
} // Specular

//----------------------------------------

// vertex shader output structure
struct VertexOutput_Specular20
{
	float4 pos          : POSITION;
	float2 texCoord     : TEXCOORD0;
	float3 lightVec     : TEXCOORD1;
	float3 viewVec      : TEXCOORD2;
	float  backFaceFactor : TEXCOORD3;
};

// Vertex shader function
VertexOutput_Specular20 VS_Specular20(VertexInput In)
{
	VertexOutput_Specular20 Out = (VertexOutput_Specular20) 0;
	
	float4 WorldVertPos = mul(float4(In.pos.xyz, 1), World);
	Out.pos = mul(WorldVertPos, viewProj);
	//obs: Out.pos = TransformPosition(In.pos * objPos.w + objPos.xyz);
	
	// Copy texture coordinates for diffuse and normal maps
	Out.texCoord = In.texCoord;

	// Compute the 3x3 tranform from tangent space to object space
	float3x3 WorldToTangentSpace =
		ComputeTangentMatrix(In.tangent, In.normal);

	float3 WorldEyePos = GetCameraPos();
	//already defined: float3 WorldVertPos = GetWorldPos(In.pos);

	// Transform light vector and pass it as a color (clamped from 0 to 1)
	// For ps_2_0 we don't need to clamp form 0 to 1
	Out.lightVec = normalize(mul(WorldToTangentSpace, GetLightDir()));
	Out.viewVec = mul(WorldToTangentSpace, WorldEyePos - WorldVertPos);

	//obs: Out.color = objColor;
	// Little helper to darken back face areas, looks more realistic on asteroids
	Out.backFaceFactor = 0.25f + 0.75f *
		saturate(dot(CalcNormalVector(In.normal), lightDir)+0.5f);

	// And pass everything to the pixel shader
	return Out;
} // VS_Specular20(.)

//--------------------------------------

// Pixel shader function
float4 PS_Specular20(VertexOutput_Specular20 In) : COLOR
{
	// Get height from normal map alpha channel!
	float2 height = tex2D(heightTextureSampler, In.texCoord);
	
	// Calculate parallax offset
	float3 viewVector = normalize(In.viewVec);
	float2 offsetTexCoord = In.texCoord +
		// Push stuff more in than pulling it out, this minimized the disortion effect.
		(height*parallaxAmount - parallaxAmount*0.6f)*viewVector;
		//(height-1)*parallaxAmount*viewVector;

	// Grab texture data
	float4 diffuseTexture = tex2D(diffuseTextureSampler, offsetTexCoord);
	float3 normalVector = (2.0 * tex2D(normalTextureSampler, offsetTexCoord).agb) - 1.0;
	// Normalize normal to fix blocky errors
	normalVector = normalize(normalVector);

	// Additionally normalize the vectors
	float3 lightVector = //In.lightVec;//not needed:
		normalize(In.lightVec);
	// Compute the angle to the light
	float bump =
		dot(normalVector, lightVector);
		//saturate(dot(normalVector, lightVector));
	// Specular factor
	float3 reflect = normalize(2 * bump * normalVector - lightVector);
	float spec = pow(saturate(dot(reflect, viewVector)), shininess);

	// Darken down bump factor on back faces
	bump = bump * In.backFaceFactor;

	float3 ambDiffColor = ambientColor + bump * diffuseColor;
	//obs: diffuseTexture = diffuseTexture * In.color;
	float4 ret;
	ret.rgb = diffuseTexture * ambDiffColor +
		// Also multiply by height, lower stuff should be more occluded
		// and not have so much shininess
		(height.x + 0.5f) * bump * spec * specularColor * diffuseTexture.a;
	// Apply color
	ret.a = diffuseTexture.a * diffuseColor.a;
	return ret;
} // PS_Specular20(.)

// Techniques
technique Specular20
{
	pass P0
	{
		VertexShader = compile vs_1_1 VS_Specular20();
		PixelShader  = compile ps_3_0 PS_Specular20();
	} // pass P0
} // Specular20



And my code:
		HRESULT DXresult; 
		LPD3DXEFFECT Shader;
		DXresult = D3DXCreateEffectFromFile(d3ddev, "ParallaxMapping2.fx", 0, 0, D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY, 0, &Shader, NULL);
		DXresult = Shader->SetTechnique("Specular20");
		DXresult = Shader->SetMatrix("World", &Transform);
		DXresult = Shader->SetMatrix("viewInverse", &View);
		DXresult = Shader->SetMatrix("viewProj", &Projection);
		DXresult = Shader->SetTexture("diffuseTexture", Texture);
		DXresult = Shader->SetTexture("normalTexture", Bump);
		DXresult = Shader->SetTexture("heightTexture", Height);
		DXresult = Shader->SetVector("lightDir", &D3DXVECTOR4(-0.65f, 0.65f, -0.39f, 0.0f));
		DXresult = Shader->SetVector("ambientColor", &D3DXVECTOR4(0.1f, 0.1f, 0.1f, 1.0f));
		DXresult = Shader->SetVector("diffuseColor", &D3DXVECTOR4(1.0f, 1.0f, 1.0f, 1.0f));
		DXresult = Shader->SetVector("specularColor", &D3DXVECTOR4(1.0f, 1.0f, 1.0f, 1.0f));
		DXresult = Shader->SetFloat("shininess", 16.0f);
		DXresult = Shader->SetFloat("parallaxAmount", 0.033f);
		DXresult = Shader->Begin(NULL, NULL);
		DXresult = Shader->BeginPass(0);
		DXresult = d3ddev->SetTexture();
                DXresult = Mesh->DrawSubset(0); 
		DXresult = Shader->EndPass();
		DXresult = Shader->End();




I know it's a rather dumb thing to constantly be loading the effect file like that as it would kill the frame rate (which it does), but I'm trying to eliminate every variable and make it as simple as possible, and it also makes no difference if loaded only once. Every function returns S_OK, and all the textures are loaded correctly. Any help at all is greatly appreciated. [Edited by - WarrenHarding on October 12, 2008 11:39:57 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this