Jump to content
  • Advertisement
Sign in to follow this  
cephalo

DX11 Questions about billboards

This topic is 1417 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm at the point in my project where I have to implement some basic spherical billboards. I have dabbled with billboards in the past and I've never been able to get controllable results, always having things flip backwards or upside down or really just always the opposite of what I was trying to do.

 

I have been googling the problem and it seems like there have been many ways to do it over the years, and I'm wondering how it should be done these days with DX11. Most of the examples I have found use the geometry shader to generate a quad, but whenever I have used geometry shaders, I get massive framerate hits just by having a pass-through geometry shader in my pipline. It seems like it could also be done with the tessellation pipeline instead.

 

Can anyone point me toward some modern hlsl shader code that implements a basic spherical billboard? My grasp of the theory behind it is a bit shaky, so I'm hoping some actual working code will show me what I'm missing.

 

I would also like to implement a particle system in the future, but I need to get billboards down first.

Share this post


Link to post
Share on other sites
Advertisement

Can't show the code at this moment, but one of the reasons for geometry shader to exist is particle / quad expansion. 

 

Cheers!

Share this post


Link to post
Share on other sites

A low-tech way is to rotate the model coordinates in your vertex shader before applying the world matrix.

 

Easiest way is to multiply them with the transpose of your view matrix with the translations set to 0:

 

[source]
float4 RotateTowardsEye(float4 inModelPos){

 

 float4x4 Q;
 for(int i=0; i<4; i++){
  for(int j=0; j<4; j++){
   Q[j]=View[j];
  }
 }
 Q[0][3]=0;
 Q[1][3]=0;
 Q[2][3]=0;

 

 return mul(inModelPos,Q);
}

[/source]

 

This way you can use 3D models as particles/billboards as well, not just simple shapes like quads.

Share this post


Link to post
Share on other sites

You can expand and align the quads using a vertex shader and instancing without having to resort to using the geometry shader stage.

 

Here is the vertex shader part of a DX11 example of rendering particles using billboards and vertex shader instancing taken from Chapter 8 of my book Direct3D Rendering Cookbook. This doesn't need the geometry shader stage at all and the full example uses append/consume buffers within a compute shader to perform the particle simulation.

 

By using instancing to determine the index into the particle buffer there is no need to provide any input vertex buffer(s) into the vertex shader stage.

// Represents a particle
struct Particle {
    float3 Position;
    float Radius;
    float3 OldPosition;
    float Energy;
};

// Access to the particle buffer
StructuredBuffer<Particle> particles : register(t0);

// Some common structures and constant buffers (e.g. PS_Input, projections and so on)
#include "Common.hlsl"

// Represents the vertex positions for our triangle strips
static const float4 vertexUVPos[4] =
{
    { 0.0, 1.0, -1.0, -1.0 },
    { 0.0, 0.0, -1.0, +1.0 },
    { 1.0, 1.0, +1.0, -1.0 },
    { 1.0, 0.0, +1.0, +1.0 },
};

float4 ComputePosition(in float3 pos, in float size, in float2 vPos)
{
    // Create billboard (quad always facing the camera)
    float3 toEye = normalize(CameraPosition.xyz - pos);
    float3 up    = float3(0.0f, 1.0f, 0.0f);
    float3 right = cross(toEye, up);
    up           = cross(toEye, right);
    pos += (right * size * vPos.x) + (up * size * vPos.y);
    return mul(float4(pos, 1), WorldViewProjection);
}

PS_Input VSMainInstance(in uint vertexID : SV_VertexID, in uint instanceID : SV_InstanceID)
{
    PS_Input result = (PS_Input)0;

    // Load particle using vertex instance Id
    Particle p = particles[instanceID];
    // Vertices in triangle strip
    // 0-1
    //  /
    // 2-3
    // Load vertex pos using the vertexID for the vertex in the strip (i.e. 0, 1, 2 or 3)
    result.UV = vertexUVPos[vertexID].xy;
    result.Position = ComputePosition(p.Position, p.Radius, vertexUVPos[vertexID].zw);
    result.Energy = p.Energy;
    return result;
}

To use this shader you set the input assembler primitive topology to a "triangle strip" and then use DrawInstancedIndirect - assuming that a compute shader determines the number of particles, otherwise DrawInstanced.

Edited by spazzarama

Share this post


Link to post
Share on other sites

Ok, I have a couple of questions regarding spazzarama's example. Let me see if I get this straight.

 

The vertex buffer is merely the numbers 0, 1, 2 ,3. That's the vertex buffer. spazzarama says its not needed, but then how do we know what vertex we are on?

 

The instance buffer is a list of indexes into the structured particle buffer, is this also not needed?

 

Yeah, I'm gonna have to buy the book.

Edited by cephalo

Share this post


Link to post
Share on other sites
That's the magic of those system value semantics. If e.g. you call DrawInstanced(4, n, 0, 0) that vertex shader will get these values:
 
vertexID| instanceID
--------+-----------
     0  |   0
     1  |   0
     2  |   0
     3  |   0
     0  |   1
     1  |   1
     2  |   1
     3  |   1
     0  |   2
     1  |   2
     2  |   2
     3  |   2
    ... |  ...
     0  |  n-1
     1  |  n-1
     2  |  n-1
     3  |  n-1
If you can generate geometry from these values alone (procedurally and/or with a lookup), you don't need to bind any buffer to the input assembler stage. Neither do you need an input layout.

In spazzarama's example he binds a shader resource view to the vertex shader stage.

(And as I hear: On hardware/driver level the input assembler is actually such a "shader")

Share this post


Link to post
Share on other sites

Not billboard related, but I think you might be interested.

The canonical example for a buffer-less setup is the full-screen triangle (e.g. for post-processing shaders)... Hmmm, pity, that nice altdevblog entry seems to have gone. Here a alternative:
Full screen quad without vertex buffer?

What I only learned recently is to bind only a index buffer, and use any of the indexed draw calls. Read here (it also shows the above triangle thingy):
Vertex Shader Tricks.
Peculiar: This can actually improve over instanced draw calls (low vertex count seems to be bad for instancing).

Since I'm quite fascinated by procedural geometry: Here I wrote a shader producing a cube (could be improved with the above index buffer trick).

Back to billboards: In you OP you mention tesselation shader. Sure possible, but unless you have a special need (i.e. actual tesselation) for a simple quad this is overkill - and less efficient for sure.

 

Share this post


Link to post
Share on other sites

You know, its funny but it almost seems like with these indices coming in, using traditional vertex and instance buffers is obsolete. With this you can access neighbor information with some index math.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!