Viewpoint oriented Billboarding

Started by
4 comments, last by iedoc 12 years, 1 month ago
Hello,

I'm trying to learn DirectX 11 at the moment and wanted to implement viewpoint oriented billboarding with the geometry shader.
I'm sending one vertex to the geometry shader and let it build the quadrangle which is oriented towards the camera position. But it doesn't work as supposed to be with my implementation. I'm using a free look camera and everytime i rotate my camera the billboard effect isn't seen anymore. Example: I'm facing the sprite and take some steps to the left. The sprite is facing the camera as it's supposed to be, but when I rotate the camera to the left, the sprite isn't changing it's direction and I can see the flat quadrangle.

Here is my code for the geometry Shader:
[maxvertexcount(4)]
void GS_Main(point PS_INPUT p[1], inout TriangleStream<PS_INPUT> triStream){
PS_INPUT p1 = (PS_INPUT)0;
float3 yUnitVector = {0.0f, 1.0f, 0.0f};
float3 normal = p[0].pos - camPosition;
float3 rightAxis = cross(yUnitVector, normal);
float3 upAxis = cross(normal, rightAxis);
rightAxis = normalize(rightAxis);
upAxis = normalize(upAxis);

float4 rightVector = {rightAxis.x, rightAxis.y, rightAxis.z, 0.0};
float4 upVector = {upAxis.x, upAxis.y, upAxis.z, 0.0};
p[0].pos = mul(p[0].pos, viewMatrix);

p1.pos = p[0].pos+rightVector*(0.1)+upVector*(0.1);
p1.tex0.x = 1.0f; p1.tex0.y = 0.0f;
p1.pos = mul(p1.pos, projMatrix);
triStream.Append(p1);

p1.pos = p[0].pos+rightVector*(0.1)+upVector*(-0.1);
p1.tex0.x = 1.0f; p1.tex0.y = 1.0f;
p1.pos = mul(p1.pos, projMatrix);
triStream.Append(p1);

p1.pos = p[0].pos+rightVector*(-0.1)+upVector*(0.1);
p1.tex0.x = 0.0f; p1.tex0.y = 0.0f;
p1.pos = mul(p1.pos, projMatrix);
triStream.Append(p1);

p1.pos = p[0].pos+rightVector*(-0.1)+upVector*(-0.1);
p1.tex0.x = 0.0f; p1.tex0.y = 1.0f;
p1.pos = mul(p1.pos, projMatrix);
triStream.Append(p1);
}


Maybe something is wrong with my first person camera implementation, so i post its code too, just in case:
void CameraClass::renderFreeLookCamera(){
XMVECTOR up, forward, right, position;
XMMATRIX rotationMatrix;

up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
forward = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f);
position = XMLoadFloat3(&_position);

_pitch += _amountPitch;
_yaw += _amountYaw;
_roll += _amountRoll;

rotationMatrix = XMMatrixRotationRollPitchYaw(_pitch, _yaw, _roll);

forward = XMVector3Transform(forward, rotationMatrix);
forward = XMVector3Normalize(forward);

up = XMVector3Transform(up, rotationMatrix);
up = XMVector3Normalize(up);

right = XMVector3Cross(up, forward);
right = XMVector3Normalize(right);

position += forward*_amountZ;
position += right*_amountX;
position += up*_amountY;

_amountZ = _amountY = _amountX = 0;
_amountPitch = _amountYaw = _amountRoll = 0;

XMMATRIX viewMatrix = XMMatrixLookToLH(position, forward, up);

XMStoreFloat3(&_position, position);
XMStoreFloat3(&_lookTo, forward);
XMStoreFloat3(&_up, up);
XMStoreFloat4x4(&_viewMatrix, viewMatrix);

return;
}


I think I figured out what the problem is. I'm not considering the direction in which my camera is looking when creating the quadrangle. It is always facing towards the camera position, but the position doesn't change when i rotate the camera. I tried different things to solve the problem but nothing worked out yet.
Advertisement
Hi!

In your geometry shader, the normal is still in world space. You should transform it to view space before you do the cross products.

There are two ways to approach the billboard problem. You can either let all billboards face exactly to the viewer, so the billboard plane is perpendicular to the eye vector or you align all billboards to the near plane.
The first approach (the one you used) sounds better at first, but it doesn’t work well if you have big billboards close to one another. The billboard geometries could intersect, which makes depth sorting for alpha blending impossible. The second approach always guarantees that billboards do not intersect. Of course at the cost that they are not facing exactly the viewer, but usually that’s not noticeable.

Here is some code for the second approach, in case you consider it:
[maxvertexcount(4)]
void GS_Main(point PS_INPUT p[1], inout TriangleStream<PS_INPUT> triStream)
{
PS_INPUT output;

float4 viewPos = mul(float4(p[0].pos, 1), viewMatrix);
viewPos /= viewPos.w;
float3 v = viewPos.xyz + float3(-1, -1, 0) * Radius;
output.pos = mul(float4(v,1), projMatrix);
output.tex0 = float2(0,0);
triStream.Append(output);

v = viewPos.xyz + float3(-1, 1, 0) * Radius;
output.pos = mul(float4(v,1), projMatrix);
output.tex0 = float2(0,1);
triStream.Append(output);

v = viewPos.xyz + float3(1, -1, 0) * Radius;
output.pos = mul(float4(v,1), projMatrix);
output.tex0 = float2(1,0);
triStream.Append(output);

v = viewPos.xyz + float3(1, 1, 0) * Radius;
output.pos = mul(float4(v,1), projMatrix);
output.tex0 = float2(1,1);
triStream.Append(output);

triStream.RestartStrip();
}


Hope that helps. smile.png
Wow, thank you very much! This stupid mistake gave me hours of desperation! biggrin.png It works fine now and looks quite well with sprites, which are not intersecting.

But someday I want to create a particle system out of this, so I think your approach is the better choice. I implemented it and your code works as well. Thanks a lot! :)

Just of curiosity: The constructor float4(float3, float) doesn't work. I get a compile error when I try to compile the shader. Did you use HLSL or GLSL in your code?

Wow, thank you very much! This stupid mistake gave me hours of desperation! biggrin.png It works fine now and looks quite well with sprites, which are not intersecting.

Alright, glad to help. smile.png


Just of curiosity: The constructor float4(float3, float) doesn't work. I get a compile error when I try to compile the shader. Did you use HLSL or GLSL in your code?

I used HLSL (in GLSL the types are named vec3, vec4 and so on).
Did you get the error here?
float4 viewPos = mul(float4(p[0].pos, 1), viewMatrix);
Then I guess, p[0].pos is a float4.
Try this instead to explicitly use only the first three components:
float4 viewPos = mul(float4(p[0].pos.xyz, 1), viewMatrix);
Well, or simply just the following. smile.png
float4 viewPos = mul(p[0].pos, viewMatrix);
I added the 1 at the end to make it a float4 because I passed in the position as float3. If you pass it in as float4 you can use it right away.

The other location where I used the float4(float3, float) constructor is here:
float4(v,1)
But this should be fine, since v is declared a float3.

Cheers!
Yeah you're right p[0].pos is a float4. Now everything works fine. Thanks again! :)
just want to mention something about the alpha blending problem when the geometry is intersecting. That's not actually much of a problem, as there are two ways to fix it. The simpler is to add a "clip()" in the pixel shader, for example:

clip(diffuse.a - 0.25)

So that pixels with an alpha value of less than .25 is clipped, so you will be able to see the geometry behind it.

The other way is to turn on AlphaToCoverageEnable, by setting it to true for blending. look at the link below for a great explanation of what alpha to coverage does

http://gamedev.stackexchange.com/questions/22507/what-is-the-alphatocoverage-blend-state-useful-for

This topic is closed to new replies.

Advertisement