3
\$\begingroup\$

I'm taking my first venture in to writing a shader and what I really need to do is create a billboarding effect. A thorough Google search turns up a number of threads on the topic and they always end with

A) Figured it out. Thread closed.

B) Anyone?

C) "long language/system-specific answer without explanation"

Most commonly, people are directed to Riemers which is fine, except his code isn't compatible with XNA 4.0 (what I'm using) and he doesn't explain the shader itself.

Can anyone explain/direct-me-to a language/system agnostic answer of how quad billboarding works?

EDIT: For others so they don't need to dig through the comments: NtscCobalt pointed out that I was attempting to use an orthographic camera when applying the shader. This essentially voided some of the effects of the shader. After changing to a perspective-based cam, the shader worked as it should have.

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

Here is a simple solution that doesn't require any changes in code to change an object from 3d to billboard. It is probably a hack and might not work in every situation.

Set the constant 'WorldView' to the WorldMatrix*ViewMatrix and 'Projection' to your Projection Matrix. No additional code required. This shader doesn't include lighting but you could use the same lighting code from per-pixel lighting pretty easily. This correctly works with textures by the way.

float4x4 WorldView  : WorldView;
float4x4 Projection : Projection;

texture texture0 : DIFFUSE <
    string ResourceName = "default_color.dds";
    string UIName =  "Diffuse Texture";
    string ResourceType = "2D";
>;

sampler2D colorMap = sampler_state
{
    Texture = <texture0>;
    MagFilter = Linear;
    MinFilter = Anisotropic;
    MipFilter = Linear;
    MaxAnisotropy = 16;
};

struct VertexShaderInput
{
    float4 Position : POSITION0;
    float2 TextureCoordinate : TEXCOORD0;
};

struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float2 TextureCoordinate : TEXCOORD0;
};

VertexShaderOutput VertexShaderFunction(VertexShaderInput IN)
{
    VertexShaderOutput OUT;
    OUT.Position = mul(IN.Position + float4(WorldView[3].xyz, 0), Projection);
    OUT.TextureCoordinate = IN.TextureCoordinate;

    return OUT;
}

float4 PixelShaderFunction(VertexShaderOutput IN) : COLOR0
{
    return tex2D(colorMap, IN.TextureCoordinate);
}

technique BasicShader
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 VertexShaderFunction();
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}

Edit: To clarify the way this works is by multiplying the vertices position by the worldview matrix position but ignoring either's rotation and scale. That result is then multiplied by the projection matrix. In short: any object that faces the camera in model space that then uses this shader will face the camera.

\$\endgroup\$
9
  • \$\begingroup\$ While I appreciate the effort (that did not work for me. I may need to re-visit my camera code) that doesn't answer the question. I was asking for the fundamentals of quad billboarding because I want to understand it, not just copy someone elses code and call it good. \$\endgroup\$
    – Mike Cluck
    Commented Oct 28, 2011 at 21:25
  • \$\begingroup\$ @Mike C, Sorry I guess I assumed that the shader spoke for itself. The basic idea here is instead of doing a bunch of math to make the object face the camera we simply ignore the object's orientation and the world matrix's rotation. It sounds strange but works very well for an object that uses a single matrix to determine its location (all relative coordinates stored in vertex data). This billboard uses the shader above without any additional modification/transform. i30.photobucket.com/albums/c308/thentsc/billboard1.png \$\endgroup\$
    – NtscCobalt
    Commented Oct 29, 2011 at 19:24
  • \$\begingroup\$ @MikeC, Here is what happens when I take the cogs in the above image and transform them into a billboard (disabled the brick billboard for visibility). i30.photobucket.com/albums/c308/thentsc/billboard2-1.png I got as close in as possible to show how this works with objects that have actual 3d geometry. Again since it doesn't actually rotate the object you can see the object's sides (look at the grass covered cog). The location of the cogs relative to eachother is all wrong because they normally use a relative matrix which this billboarding method doesn't support. \$\endgroup\$
    – NtscCobalt
    Commented Oct 29, 2011 at 19:27
  • \$\begingroup\$ That makes sense and seems to work based on the pictures you provided. But for some reason my quads still aren't facing the camera, and in fact are being rendered as pure black squares. Here's a proof-of-concept if you'd care to look pastebin.com/SNqpStF6 \$\endgroup\$
    – Mike Cluck
    Commented Oct 29, 2011 at 20:14
  • \$\begingroup\$ @Mike C, It looks like you are doing everything right except for setting the texture. You need a call that either looks like Effect.Parameters["texture0"].SetValue(Texture); or GFX.SetTexture(0, Texture);. Which one depends on how they decided to wrap effects in Managed Direct3D. I'm sure you can find the function call on MSDN. I have to do the same thing in my code depending on if you are using an effect shader (combined vertex and pixel) or if you are using vertex shader + pixel shader. code.google.com/p/cobaltlibrary/source/browse/trunk/Drivers/… \$\endgroup\$
    – NtscCobalt
    Commented Oct 29, 2011 at 20:27
1
\$\begingroup\$

In the Riemers tutorial, CylBillboardVS is moving each vertex so that the quad faces the camera. It calculates the vectors for how the quad should be facing (upVector points up and sideVector points to the right, perpendicular to the camera direction):

float3 upVector = xAllowedRotDir;
float3 sideVector = cross(eyeVector,upVector);

Then it moves the vertex according to where it is in the texture:

float3 finalPosition = center;
finalPosition += (inTexCoord.x-0.5f)*sideVector;
finalPosition += (1.5f-inTexCoord.y*1.5f)*upVector;

So, if the vertex has UV-coordinates at the top-left of the texture, it'll move to the left (perpendicular to the camera direction) and up along the upVector.

\$\endgroup\$
3
  • \$\begingroup\$ That makes a certain amount of sense but I guess I'm fundamentally missing something. Whenever I apply that method using that exact shader code then it only blurs my quads, doesn't rotate them. \$\endgroup\$
    – Mike Cluck
    Commented Oct 26, 2011 at 13:17
  • \$\begingroup\$ @MikeC without seeing your code we can't say anything about that. COuld you post a small proof of concept? \$\endgroup\$
    – Roy T.
    Commented Oct 26, 2011 at 16:42
  • \$\begingroup\$ @RoyT. Sure, I can't believe I forgot to in the first place pastebin.com/eTKVaG2S I made one minor alteration to the shader. The textures are coming from a spritesheet so I just adjusted the way the shader determines which vertex it's moving. \$\endgroup\$
    – Mike Cluck
    Commented Oct 27, 2011 at 18:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .