Billboard Shader Issue (HLSL/DX9)

Started by
17 comments, last by DJTN 12 years, 8 months ago
I’ve manually created a mesh with multiple quads (billboards) for trees. I want the vertex shader to transform their positions to face the camera per quad so I don’t have to update the vertex buffer every frame. I can do it with the entire model like this:



(HLSL)

//face cam
float4x4 worldViewMatrix = mul(matWorld, matView);
float3 positionVS = input.Position + float3(worldViewMatrix._41, worldViewMatrix._42, worldViewMatrix._43);
output.Position = mul(float4(positionVS, 1.0f), matProjection);



But, I need to do it per quad/vertex - I’m having some issues with the concept.

I’ve read several different posts but the solutions offered aren’t complete or explained. My current vertex format is position - normal - textured. Do I need to pass in a “center” position or can the vertex shader calculate the position with my existing data?

The world matrix in the code above (matWorld) is transformed on the CPU with scale, rotation then position.

To convolute matters, I’d like to still be able to scale and move the entire mesh as a whole and still have each quad face the camera. I would think this would be as simple as creating the world matrix in the vertex shader per vertex prior to executing my existing code but is that the proper way to handle my issue?



EDIT: Solution here: http://www.gamedev.net/topic/154890-billboarding-with-vertex-shaders/
Advertisement
Anyone?
Sounds like this requires a different rotation per quad. Which means you would have to pass one 3x3 transformation matrix per quad to the vertex shader. I'm not too familiar with the limit of how much data you can pass, but I think you should be fine updating the vertex buffer every frame (why don't you want to update it every frame?)

EDIT: does the tree look too bad if you don't rotate branches/leaves to face the camera?

Sounds like this requires a different rotation per quad. Which means you would have to pass one 3x3 transformation matrix per quad to the vertex shader. I'm not too familiar with the limit of how much data you can pass, but I think you should be fine updating the vertex buffer every frame (why don't you want to update it every frame?)

EDIT: does the tree look too bad if you don't rotate branches/leaves to face the camera?


It's a quad with a tree image on it, several of them. If I have to lock the vertex buffer and update it per frame it kind of negates what I'm trying to accomplish. I want to draw all the quads in one call (batch). I'm wanting to move the process of the CPU to the GPU. You would think it would be rather simple.
Drawing a static tree in one go is simple... making each branch/leaf face the camera is not that simple... if you want each one to face the camera from their own position (which you already have in the vertex attributes), then it will require a different transformation matrix for each quad, and you'll have to send those to the vertex shader (as uniforms?). You could also try to use a single transformation for all of them, taking into account a "center" point (i think this is what you were talking about). You could send that matrix as a uniform, just like your other transformation matrices. However, using a single position may not look exactly right, depending on how far away the leaves/branches are from the "center".

EDIT: if you will do one TM per quad, then you'll need a way to index into the "array of TM's". You could add an integer vertex attribute, but that means more data per vertex. I still think you should try to update the vertex buffer every frame... maybe it's not as slow as you expect it to be.
What is more expensive: updating the vertex buffer every frame or drawing each quad in a loop?
[font=arial, verdana, tahoma, sans-serif][size=2]You know what... forget the "send a bunch of 3x3 matrices" method. You don't need to do that... all you need to compute a "camera facing" billboard matrix is the camera position plus the particle position.. so you could compute the matrix in the vertex shader by sending the camera position as a uniform. I'm not sure if doing it per vertex will skew the quad though... and I don't know if this will be faster than updating the vertex buffer every frame... it's something worth testing.[/font]

[font="arial, verdana, tahoma, sans-serif"]You know what... forget the "send a bunch of 3x3 matrices" method. You don't need to do that... all you need to compute a "camera facing" billboard matrix is the camera position plus the particle position.. so you could compute the matrix in the vertex shader by sending the camera position as a uniform. I'm not sure if doing it per vertex will skew the quad though... and I don't know if this will be faster than updating the vertex buffer every frame... it's something worth testing.[/font]



I'm confused and my code is a mess. I'm currently trying the "update vertex buffer" concept and I'm getting trees flying all around. I was assuming that I'd update the verticie's position in a loop by transforming their cordinates with a orientation matrix I create from the inverted LookAtLH matrix using the cameras position, target and up vector but this is not working and I have a feeling it is because of the order.
Could you explain the "Camera Facing Billboard Matrix" concept a little more?
[font="arial, verdana, tahoma, sans-serif"]

Could you explain the "Camera Facing Billboard Matrix" concept a little more?



The following code can be used to create a billboard matrix. The result is the transformation matrix you use to multiply each vertex of the quad. In my case I used it for particles. Notice how I'm setting the values at the end... this works for a right-handed system. Just to clarify, you'll need to compute this matrix per quad, then use the same matrix to transform every vertex in the quad.[/font]


CreateBillboardMatrix(const Vector3& particlePos, const Vector3& cameraPos, D3DXMATRIXA16& out)
{


// compute billboard basis
Vector3 look = particlePos;
look = look - cameraPos;
look.Normalize();

const Vector3 CAMERA_UP_VECTOR(0, 1, 0);
Vector3 camUp = CAMERA_UP_VECTOR;
camUp.Normalize();

Vector3 right = camUp.CrossProduct(look);
right.Normalize();

Vector3 up = look.CrossProduct(right);
up.Normalize();

// set matrix values
D3DXMatrixIdentity(&out);

out._11 = right.x;
out._12 = right.y;
out._13 = right.z;

out._21 = up.x;
out._22 = up.y;
out._23 = up.z;

out._31 = look.x;
out._32 = look.y;
out._33 = look.z;

out._41 = particlePos.x;
out._42 = particlePos.y;
out._43 = particlePos.z;
}



EDIT: This type of billboard function will make an object face the camera in all directions (spherical billboard)... not sure if that's what you want.
Thanks gsamour. I've got to run, I'll have to get back to this tomorrow.

This topic is closed to new replies.

Advertisement