I was trying to do some normal mapping on billboards the other day but ran into some problems. Other people have commented on this restriction too, see http://www.ogre3d.org/forums/viewtopic.php?f=3&t=75789 & http://www.ogre3d.org/forums/viewtopic.php?f=2&t=69823 & http://ogre3d.org/forums/viewtopic.php?f=5&t=63350
So I came up with a solution that works for me and thought I'd post it for the team. This solution is a little different from some of the solutions offered above and does handle billboard rotation. One of the big problems with billboards when doing lighting on them is that the light direction is not correct if you rotate the billboard. This is because when you rotate a billboard you're not actually updating the rotation component of world matrix... Think of billboards as being a few vertexes in a single manual object instead of being individual entities. Billboards receive the world matrix orientation from the scenenode that the BBS is attached to.
In my use case I wanted to create impact craters on the ground after an explosion occurs, pretty standard fare. I used a single Ogre::BillboardSet (BBS) for all of the billboards and each billboard can have its own rotation around the Y axis.
The method:
1. Create a BBS that always has billboards facing up in the +Y direction (flat on the ground facing up).
2. Each time a billboard is created give it a random rotation and set the billboard colour's red-channel to represent this rotation.
3. In the billboard's vertex shader rotate the light direction vector around the Y axis by the rotation denoted by the vertex colour red-channel.
4. As the vertexes don't have tangents, use a hard-coded matrix to rotate this adjusted light direction vector, this becomes the tangent-space light direction.
Here's the BBS setup.
Code: Select all
//death splatter billboardset is used to display the residue of creeps when they die
blastBBS = m_pSceneMgr->createBillboardSet("Blast", 100);
blastBBS->setMaterialName("DeathSplatter/Blast");
blastBBS->setBillboardType(Ogre::BBT_PERPENDICULAR_COMMON);
blastBBS->setCommonUpVector(Ogre::Vector3(0,0,1));
blastBBS->setCommonDirection(Ogre::Vector3(0,1,0));
blastBBS->setCastShadows(false);
blastBBS->setDefaultDimensions(3,3);
blastBBS->setBillboardRotationType(Ogre::BBR_VERTEX);
m_pSceneMgr->getRootSceneNode()->attachObject(blastBBS);
Code: Select all
//place death billboard on ground
Ogre::Billboard *blast = blastBBS->createBillboard(location);
Ogre::Real rnd = Ogre::Math::UnitRandom(); // a random in range 0.0f -> 1.0f
blast->setRotation(Ogre::Radian(rnd * 2 * 3.14f)); //set the rotation as a radian value
blast->mColour.r = rnd; //set the vertex red-channel to represent this rotation
Code: Select all
void vp_blast(
float4 position : POSITION,
float2 uv : TEXCOORD0,
float4 colour : COLOR0,
float3 normal : NORMAL,
out float4 oPosition : POSITION,
out float2 oUV : TEXCOORD0,
out float3 oTSLightDirection : TEXCOORD1,
out float3 oTSHalfAngle : TEXCOORD2,
uniform float4x4 worldViewProjMatrix,
uniform float3 eyePosition,
uniform float4 lightDirection)
{
oPosition = mul(worldViewProjMatrix, position);
oUV.xy = uv;
float3 eyeDirection = normalize(eyePosition - position.xyz);
//Extract the true radian value for the billboard
float billboardRotation = colour.r * 2 * 3.14;
float3x3 modelRotation;
//RHS Y rotation -this works perfect for just getting the true lightdir
modelRotation[0] = float3(cos(billboardRotation), 0, -sin(billboardRotation));
modelRotation[1] = float3(0,1,0);
modelRotation[2] = float3(sin(billboardRotation), 0, cos(billboardRotation));
float3 adjustedLightDir = mul(modelRotation, lightDirection.xyz);
float3 adjustedViewDir = mul(modelRotation, eyeDirection.xyz);
float3x3 TSRotation;
TSRotation[0] = float3(1,0,0);
TSRotation[1] = float3(0,0,1);
TSRotation[2] = float3(0,1,0);
oTSLightDirection = mul(TSRotation, adjustedLightDir);
oTSHalfAngle = mul(TSRotation, adjustedViewDir);
}
Here's a screenshot showing just the NdotH value of the billboard using a simple concrete-like bumpy normalmap.
And here's what I'm using in my game. I use another texture as a alpha mask and then only output the NdotL vector, turned out quite well.
Some caveats
* If your billboards are oriented in another direction then you'll need to change the rotation matrices to reflect this.
* More complex orientations can be achieved by passing in rotations around different axes using the remaining vertex colour channels.
OK, hope this helps someone