How do I make shiny bumpmaps?

Started by
4 comments, last by Dookie 16 years, 7 months ago
Hello! I wrote some hackneyed code that displays bumpmaps in my 2D game (flat sprites now look 3D'ish). But the bumpmaps are pretty flat looking, in that there's no shiny on the bumps... Everything looks like it's modeled in dust. I'd like to add some shine to the bumps, to hopefully make my bumpmaps look like they're glossy or metallic. I'm using this to bumpmap a quad:
// Setup stream source...
g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) );
g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );

// Set the texture factor to the rgb calculated above...
g_pd3dDevice->SetRenderState(D3DRS_TEXTUREFACTOR, dwFactor);

// Modulate the normalmap texture with the light vector (stored
// above in the texture factor)...
g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_DOTPRODUCT3 );
g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_TFACTOR );

// Set the texture to be the normal map
g_pd3dDevice->SetTexture( 0, g_NormMap );

// Overlay primary texture...
g_pd3dDevice->SetTexture( 1, g_Texture );
g_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_MODULATE );

// And finally tell D3D to draw our bumpmapped quad!
g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLEFAN, 0, 2 );
"dwFactor" is a manually-calculated light vector, "g_NormMap" is my normalmap texture, and "g_Texture" is the image texture. I calculate how dwFactor is going to affect g_NormMap, then draw g_NormMap. I then draw g_Texture on top of that. The result is a bumpmapped texture! Now for my question... How do I add specular highlights to the above code? Or is that even what I want to do? I can't find any tutorials on this anywhere, since most people now use pixel shaders to do the same thing... I want my game to work on older machines, so I don't want to use pixel shaders in my game engine. Can anybody help me to get my bumps to look like they were Turtle-waxed, instead of like they were painted in flat housepaint? Thanks in advance for the help!
"The crows seemed to be calling his name, thought Caw"
Advertisement
You can try to simulate specular. Lets ignore for now that specular would use a different vector than diffuse for the light. Other than the slightly different vector, the calculate is the same as diffuse, but then raised to a power, and then added to the diffuse. It's something which you can't do well in the fixed pipe, especially on older cards. We can get something better than flat paint though. Older cards support only 2 stages, so we'll limit ourselves to that. We can use a power of 2, 4, or 8 with the fixed pipe if we're tricky, and even get less than white highlights on the ^2 and ^4 case.

In all these we're going to use 2 passes where the second pass is your previous setup but with blending:

alphablendenable = true
srcblend = one
destblend = one
ie: spec pass + diffusebump pass

For x^2 specular:
Stage 0: dot like you're doing.
stage 1: modulate current, current (pow 2)
alphablendenable = false

for x^4 specular
do x^2 specular with a blend op.
alphablendenable = true
srcblend = srccolor
destblend = zero
(we can optimize this to not use blending, see below)

for x^8 specular
Do x^4 specular with the second pass destblend set to destcolor.

The x^8 case make me think of an optimization to the x^4 case. Don't alphablend the first pass, and use destblend = destcolor for the second pass. This has the same effect but uses alphablending on only one pass, which is better.

In all these case we're assuming a specular of white, when a softer grey often looks more realistic. For x^2 and x^4 you have unused blending hardware on the first pass. You could set this to enabled, srcblend=srcalpha, destblend=zero, and set the alpha in stage 1 to select from tfactor. Not true colored specular, but all the shades of grey, which is often all you'll use. Keep in mind any reduction from white is going to be squared again the next pass in the x^4 case.
I'll play around with this tomorrow... I tried tonight but my mind's too tired to comprehend and put to code everything you've described, so I couldn't get anything to work.

Thanks for the info though, I'll see if I can't get it to work tomorrow after I've had a few cups of coffee. [smile]
"The crows seemed to be calling his name, thought Caw"
Doggone it, I just can't get it right... All I can accomplish is a washed-out overly bright texture, I can't get a highlight at all (no white 'shine', just overly bright colored pixels). Also, the specular effect isn't very defined, it simply looks like the same bumpmap blended over the previously drawn bumpmapped image. Am I doing it right?
// Previously drawn bumpmapped quad...g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLEFAN, 0, 2 );// Draw specular highlight (don't know how to do 'power of two', etc.// just simply blending normalmap over bumpmapped texture again)g_pd3dDevice->SetTexture( 0, g_NormMap );g_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );g_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLEFAN, 0, 2 );g_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ZERO );

In this example, I'm simply re-drawing the bumpmap image over the bumpmapped quad while blending with ONE/ONE. It's the closest I can get to a specular effect, but it's not even close... All this does is create overly bright pixels on the prevously drawn bumpmapped image, there's no white or grey highlights at all. Also, I'm not sure how to calculate the power-of-x that you described, so this specular effect is pretty drab and undefined.

By the way, here's how I calculate my lightsource...
// Calculate lightsource...vLight.x = (litePos[0] / 150.0f - 1);vLight.y = (litePos[1] / 150.0f - 1);	vLight.z = 1.0f;D3DXVec3Normalize( &vLight, &vLight );DWORD r = (DWORD)(127.0f * vLight.x + 128.0f);DWORD g = (DWORD)(127.0f * vLight.y + 128.0f);DWORD b = (DWORD)(127.0f * vLight.z + 128.0f);dwFactor = D3DCOLOR_XRGB(r, g, b);

The demo window size is 300, so that's where I get the '150.0f' in the above calculation. Can you show me how to incorporate your 'power of x' idea into the above code?

Thanks again for the help and explanations. I hope you can help me get specular highlights to work, because that will look REALLY cool in my game!
"The crows seemed to be calling his name, thought Caw"
Just as a quick demo I made an FX file doing (fixed pipe pixel work), with 4 techniques (diffuse, pow2, pow4, and pow8).

Low-powered specular produces a nearly identical result to diffuse, so pow2 might not be your best choice. You may want to move your light vector so it's not pointing nearly vertical, or use a seperate lower vector for the specular than for diffuse (I show when to use each of the two vectors in the FX file).

Play with the FX file on a plane in FX composer or something first to get an idea of how it looks.

float4x4 worldViewProj : WorldViewProjection;texture diffuseTexture;texture bumpTexture;sampler DiffuseSampler = sampler_state {    texture = <diffuseTexture>;    AddressU  = CLAMP;            AddressV  = CLAMP;    AddressW  = CLAMP;    MIPFILTER = LINEAR;    MINFILTER = LINEAR;    MAGFILTER = LINEAR;};sampler BumpSampler = sampler_state {    texture = <bumpTexture>;    AddressU  = CLAMP;            AddressV  = CLAMP;    AddressW  = CLAMP;    MIPFILTER = LINEAR;    MINFILTER = LINEAR;    MAGFILTER = LINEAR;};float4 lightColor : Diffuse<    string UIName = "Diffuse Light Color";    string Object = "DirectionalLight";> = {1.0f, 1.0f, 1.0f, 1.0f};//------------------------------------struct vertexInput {    float4 position				: POSITION;    float2 texCoordDiffuse		: TEXCOORD0;};struct vertexOutput {    float4 hPosition		: POSITION;    float2 texCoordBump		: TEXCOORD0;    float2 texCoordDiffuse	: TEXCOORD1;};//------------------------------------vertexOutput VS_TransformAndTexture(vertexInput IN) {    vertexOutput OUT;    OUT.hPosition = mul( IN.position, worldViewProj);    OUT.texCoordBump = IN.texCoordDiffuse;    OUT.texCoordDiffuse = IN.texCoordDiffuse;    return OUT;}#define lightVec 0xFF3030C0#define lightVecSpec 0xFF3030C0//-----------------------------------technique DiffOnly{    pass p1    {				VertexShader = compile vs_1_1 VS_TransformAndTexture();		TextureFactor = lightVec;		Sampler[0] = <BumpSampler>;		ColorOp[0] = DotProduct3; ColorArg1[0] = Texture; ColorArg2[0] = TFactor;		AlphaOp[0] = SelectArg1; AlphaArg1[0] = TFactor;		Sampler[1] = <DiffuseSampler>;		ColorOp[1] = Modulate; ColorArg1[1] = Texture; ColorArg2[1] = Current;		AlphaOp[1] = SelectArg1; AlphaArg1[1] = TFactor;				ColorOp[2] = Disable; AlphaOp[2] = Disable;		AlphaBlendEnable = false;	}}technique SpecPow2{    pass p0    {				VertexShader = compile vs_1_1 VS_TransformAndTexture();		TextureFactor = lightVecSpec;		Sampler[0] = <BumpSampler>;		ColorOp[0] = DotProduct3; ColorArg1[0] = Texture; ColorArg2[0] = TFactor;		AlphaOp[0] = SelectArg1; AlphaArg1[0] = TFactor;		ColorOp[1] = Modulate; ColorArg1[1] = Current; ColorArg2[1] = Current;		AlphaOp[1] = SelectArg1; AlphaArg1[1] = TFactor;				ColorOp[2] = Disable; AlphaOp[2] = Disable;		AlphaBlendEnable = false;	}    pass p1    {				VertexShader = compile vs_1_1 VS_TransformAndTexture();		TextureFactor = lightVec;		Sampler[0] = <BumpSampler>;		ColorOp[0] = DotProduct3; ColorArg1[0] = Texture; ColorArg2[0] = TFactor;		AlphaOp[0] = SelectArg1; AlphaArg1[0] = TFactor;		Sampler[1] = <DiffuseSampler>;		ColorOp[1] = Modulate; ColorArg1[1] = Texture; ColorArg2[1] = Current;		AlphaOp[1] = SelectArg1; AlphaArg1[1] = TFactor;				ColorOp[2] = Disable; AlphaOp[2] = Disable;		AlphaBlendEnable = true;		SrcBlend = ONE;		DestBlend = ONE;	}}technique SpecPow4{    pass p0    {				VertexShader = compile vs_1_1 VS_TransformAndTexture();		TextureFactor = lightVecSpec;		Sampler[0] = <BumpSampler>;		ColorOp[0] = DotProduct3; ColorArg1[0] = Texture; ColorArg2[0] = TFactor;		AlphaOp[0] = SelectArg1; AlphaArg1[0] = TFactor;		ColorOp[1] = Modulate; ColorArg1[1] = Current; ColorArg2[1] = Current;		AlphaOp[1] = SelectArg1; AlphaArg1[1] = TFactor;				ColorOp[2] = Disable; AlphaOp[2] = Disable;		AlphaBlendEnable = false;	}    pass p1    {				VertexShader = compile vs_1_1 VS_TransformAndTexture();		TextureFactor = lightVec;		Sampler[0] = <BumpSampler>;		ColorOp[0] = DotProduct3; ColorArg1[0] = Texture; ColorArg2[0] = TFactor;		AlphaOp[0] = SelectArg1; AlphaArg1[0] = TFactor;		Sampler[1] = <DiffuseSampler>;		ColorOp[1] = Modulate; ColorArg1[1] = Texture; ColorArg2[1] = Current;		AlphaOp[1] = SelectArg1; AlphaArg1[1] = TFactor;				ColorOp[2] = Disable; AlphaOp[2] = Disable;		AlphaBlendEnable = true;		SrcBlend = ONE;		DestBlend = DESTCOLOR;	}}technique SpecPow8{    pass p0    {				VertexShader = compile vs_1_1 VS_TransformAndTexture();		TextureFactor = lightVecSpec;		Sampler[0] = <BumpSampler>;		ColorOp[0] = DotProduct3; ColorArg1[0] = Texture; ColorArg2[0] = TFactor;		AlphaOp[0] = SelectArg1; AlphaArg1[0] = TFactor;		ColorOp[1] = Modulate; ColorArg1[1] = Current; ColorArg2[1] = Current;		AlphaOp[1] = SelectArg1; AlphaArg1[1] = TFactor;				ColorOp[2] = Disable; AlphaOp[2] = Disable;		AlphaBlendEnable = true;		SrcBlend = SRCCOLOR;		DestBlend = ZERO;	}    pass p1    {				VertexShader = compile vs_1_1 VS_TransformAndTexture();		TextureFactor = lightVec;		Sampler[0] = <BumpSampler>;		ColorOp[0] = DotProduct3; ColorArg1[0] = Texture; ColorArg2[0] = TFactor;		AlphaOp[0] = SelectArg1; AlphaArg1[0] = TFactor;		Sampler[1] = <DiffuseSampler>;		ColorOp[1] = Modulate; ColorArg1[1] = Texture; ColorArg2[1] = Current;		AlphaOp[1] = SelectArg1; AlphaArg1[1] = TFactor;				ColorOp[2] = Disable; AlphaOp[2] = Disable;		AlphaBlendEnable = true;		SrcBlend = ONE;		DestBlend = DESTCOLOR;	}}
OHHHH, I see. I was setting up my texture blending incorrectly... That's pretty cool Namethatnobodyelsetook, thanks for the info! I was trying to blend in a new texture (a grey 'specular' texture), and that didn't really work at all. Re-using the normalmap texture works great! I'll play around with this until I fully understand what's going on and then implement the best option for my game.

Thanks again!
"The crows seemed to be calling his name, thought Caw"

This topic is closed to new replies.

Advertisement