Search Unity

Billboarding Vertex Shader

Discussion in 'Shaders' started by DP007, Mar 25, 2009.

  1. DP007

    DP007

    Joined:
    Jan 23, 2009
    Posts:
    24
    Hi,

    I've got problems with doing viewpoint oriented billboarding of a plane in a vertex shader. This is my code for now:

    v2f vert (appdata_base v)
    {
    v2f o;

    // direction we are viewing the billboard from
    float3 fViewDirection = normalize(_CamPos.xyz - v.vertex.xyz);

    // calculate right and up vector
    float3 fRightVector = normalize(cross(fViewDirection, _CamUp.xyz));
    float3 fUpVector = normalize(cross(fViewDirection, fRightVector));

    // position of this billboard vertex
    float3 fPosition = v.vertex.xyz;

    // move position based on texture coordinates
    fPosition += fRightVector * (v.texcoord.x - 0.5f);
    fPosition += fUpVector * v.texcoord.y;

    PositionFog( float4(fPosition,1), o.pos, o.fog );

    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

    return o;
    }


    But somehow it doesn't work well. Where is the mistake? Did anyone else perform billboarding via the vertex shader?

    I also looked at the wavinggrass shader. But it didn't seem clear to me, whats done in there with the strange offset value comparison.

    Thanks for helping...
     
  2. metervara

    metervara

    Joined:
    Jun 15, 2006
    Posts:
    203
    Hi,

    looks like it's based on this:

    http://www.gamedev.net/community/forums/topic.asp?topic_id=452635

    and assuming that one works there's one small difference in your implementation, where you do:
    Code (csharp):
    1. PositionFog( float4(fPosition,1), o.pos, o.fog );
    which is only the first line in what they do I think from looking in UnityCG.cginc:
    Code (csharp):
    1. inline void PositionFog( in float4 v, out float4 pos, out float fog )
    2. {
    3.     pos = mul( glstate.matrix.mvp, v );
    4.     fog = pos.z;
    5. }
    Implementation on gamedev.net has another mul():
    Code (csharp):
    1. // apply the camera transform
    2. float4 fViewPosition = mul(fModelView, float4(fPosition, 1.0f));
    3. OUT.fPosition = mul(fProjection, fViewPosition);
    I'm not sure what the fModelView fProjection match is, maybe glstate.matrix.mvp and glstate.matrix.projection ?

    Anyway, hope it helps you finding the error.

    /Patrik
     
  3. DP007

    DP007

    Joined:
    Jan 23, 2009
    Posts:
    24
    Thanks for reply...
    I don't think the last multiply is the error. I multiply the vertex with the whole mvp matrix, which is similar to firt multiply the vertex with modelview and then with the projection, just done in one instruction instead of two.
     
  4. metervara

    metervara

    Joined:
    Jun 15, 2006
    Posts:
    203
    Ah. I must admit that those built-in state variables are a bit of a mystery to me...

    I managed to work out a sollution from this XNA example. Vertex shader looks like this:
    Code (csharp):
    1. v2f vert (appdata_base v)
    2. {
    3.     v2f o;
    4.    
    5.     float3 center = v.vertex;//mul(_Object2World, v.vertex);
    6.     float3 eyeVector = ObjSpaceViewDir( v.vertex );
    7.    
    8.     float3 upVector = _CamUp;//float3(0,1,0);
    9.     float3 sideVector = normalize(cross(eyeVector,upVector));  
    10.        
    11.     float3 finalposition = center;
    12.     finalposition += (v.texcoord.x-0.5f)*sideVector;
    13.     finalposition += (v.texcoord.y-0.5f)*upVector;
    14.        
    15.     float4 pos = float4(finalposition,1);
    16.    
    17.     o.pos = mul( glstate.matrix.mvp, pos );
    18.    
    19.     return o;  
    20. }
    _CamUp is set in a script:
    Code (csharp):
    1. Shader.SetGlobalVector("_CamUp", Camera.main.transform.TransformDirection(Vector3.up));
    The mesh needs to be created in a specific manner -> For each particle there's 4 vertices and 2 triangles. The 4 vertices all share the same position, the center of the billboard. The vertices have different UV's, each vertex uses a different corner of the UV map. The uv's are used in the vertex shader to place the vertices.

    I'll see about adding per particle rotation and other goodies next.

    /Patrik
     
  5. DP007

    DP007

    Joined:
    Jan 23, 2009
    Posts:
    24
    Hi,
    this one looks good, I'll try this...
    I didn't thought to position all of the vertices on one position but it seems clear to me now ;)

    Thanks...
     
  6. metervara

    metervara

    Joined:
    Jun 15, 2006
    Posts:
    203
    Here's my start at a particle system with rotating particles.

    /Patrik
     

    Attached Files:

  7. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    340
    Hi,

    I'm very interested by this system, do you have any idea to get a proper Z sorting of particles ?


    EDIT : Ok, I've seen the disabled depth sorting function in your code ;)

    EDIT2 : it looks like your rotating particles are wrong in some way... the texture is rotating inside the fixed sized billboard... so they look streched while rotating.
     
    Last edited: Jun 7, 2011
  8. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    340
    Hi,

    I've got a new question here : is it possible to know the vertex index in the vertex array from a shader ? if yes, how ?
     
  9. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    You would have to supply extra vertex data to tell different indices apart in a shader, and it would have to be floating point values.

    If you're still trying to make rotating particles, you should know that the built-in system supports rotation since Unity 3.
     
  10. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    340
    Hi,

    I'm not trying to make that kind of rotating particles... in fact I would like to get them all oriented in the same way, so I only have four vectors to compute, and then add them to each particle position to get the right quad. That's why I need to know the vertex index.
    So how could I provide this extra information to the vertices ?
     
    Last edited: Jul 2, 2011
  11. rageingnonsense

    rageingnonsense

    Joined:
    Dec 3, 2014
    Posts:
    99

    This is a pretty old post, but this works WONDERFULLY (using it for billboarded trees) and I wanted to expand on a few things.

    You do not need any special mesh for this other than the default unity quad (or equivalent). You just need to replace this:
    Code (CSharp):
    1. float3 center = v.vertex;//mul(_Object2World, v.vertex);
    with this:
    Code (CSharp):
    1. float3 center = float3(0,0,0);
    So long as you have a quad with "standard" uvs, and the mesh's origin is at the center; this will suffice. I think in theory the origin does not even need to be in the center so long as you adjust the 0.5f being subtracted from the correct UVs. For a quad that has its origin at the bottom-center:

    Code (CSharp):
    1.     finalposition += (v.texcoord.x-0.5f)*sideVector;
    2.     finalposition += (v.texcoord.y-0.0f)*upVector;
    It gets even easier if your camera never rolls. If you never have a rolling camera; you can replace _CamUp with float3(0,1,0).


    Anyways, S***'s dope; solved my issue with perspective distortion
     
  12. rageingnonsense

    rageingnonsense

    Joined:
    Dec 3, 2014
    Posts:
    99
    I've improved it a bit further. I noticed that the illusion breaks apart when you are looking at the billboards from above.

    Using Vector3(0,1,0) for camera up works perfect when you are nearly parallel with the ground, but falls apart when looking down.

    Conversely, using true camera up vector works well from above, but at ground level perspective distortion takes effect at the edges of the view frustum.

    Here is a solution to that that blends the best of both worlds. It is not perfect, but it does mitigate the issue with perspective distortion a great deal (although it is still present, just subdued). this version also does not require a helper script to set camera's up vector:

    Code (CSharp):
    1. vertexOutput o;
    2.  
    3.                     float3 center = float3(0,0,0);//input.vertex;//mul(_Object2World, v.vertex);
    4.                     float3 eyeVector = ObjSpaceViewDir( input.vertex );                      
    5.  
    6.                     float3 upVectorCamera = normalize(UNITY_MATRIX_IT_MV[1].xyz);//float3(0,1,0);
    7.                     float upDifference = dot(upVectorCamera, float3(0,1,0));
    8.                     float3 upVector = lerp(upVectorCamera, float3(0,1,0), upDifference);
    9.  
    10.                     float3 sideVector = normalize(cross(eyeVector,upVector));                    
    11.      
    12.                     float3 finalposition = center;
    13.                     finalposition += (input.tex.x-0.5f) * sideVector;
    14.                     finalposition += (input.tex.y-0.5f) * upVector;
    15.      
    16.                     float4 pos = float4(finalposition,1);
    17.  
    18.                     o.pos = mul(UNITY_MATRIX_MVP, pos );
    19.                     o.tex = input.tex;
    20.                     return o;  
     
  13. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    340
    Hi, thanks for this tip :)
    I have two questions :

    1) Is "UNITY_MATRIX_IT_MV[1].xyz" the camera up vector in world space or in object space ?
    2) How to get the camera right vector in the same way ?
     
  14. rageingnonsense

    rageingnonsense

    Joined:
    Dec 3, 2014
    Posts:
    99
    1.) I think it is in... you know I am actually unsure. I found it hidden away in an answers.unity3d comment.
    2.) I am also unsure! haha. You might want to try indexes 2 or 3 (or maybe 0 as well)
     
  15. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    340
    hehehe, ok thanks for your help ! ;)

    EDIT : ok that's not what I'm looking for :/

    For now, I'm trying to get the world forward,up and right direction vectors for the camera...
    I tried many combinations with UNITY_MATRIX_IT_MV, UNITY_MATRIX_MVP, and such, but didn't found any good solution ...
     
    Last edited: Feb 13, 2016
  16. rageingnonsense

    rageingnonsense

    Joined:
    Dec 3, 2014
    Posts:
    99
    I think all you need to do is acquire the local ones and multiply it by the _Object2World matrix no?

    Then again, I am not so sure there is a difference between the local and world vectors for directions since they are both just normalized vectors anyways.
     
  17. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    340
    Normalized or not, local and world vectors are very different and can't be used for the same purpose !

    To be able to multiply the local ones by the _Object2World matrix, I first need to know which ones to use exactly :p
     
  18. gferrand_UofM

    gferrand_UofM

    Joined:
    Jan 19, 2016
    Posts:
    8
    Note: when you manually set the initial vertex position to be at the origin:
    Code (csharp):
    1. float3 center = float3(0,0,0);
    you also need to set it in the computation of the eye ray:
    Code (csharp):
    1. float3 eyeVector = ObjSpaceViewDir(center);
    otherwise each vertex uses a different direction (all the more so since you get closer) and you'll get distortions.