Search Unity

GLSL Animated sprite shader / billboard questions

Discussion in 'Shaders' started by MrScary, Oct 22, 2011.

  1. MrScary

    MrScary

    Joined:
    Dec 8, 2007
    Posts:
    94
    I have the shader below which works great.. It will basically animate the uv's on a filmstrip texture without having to modify renderer.material.mainTextureOffset in script.

    I have two questions:

    1) Can anyone think of a way to randomize the frame offset using the model's position ( or something ) so that multiple instances aren't all animating in unison?

    2) Additionally, I'd also like to do the billboarding rotations in the shader to avoid having script do that. I want an axis aligned rotation around the up/+y axis on the sprites ( not camera view plane aligned ). The geometry rendered with this shader is just a quad. I've reviewed the wiki page at http://en.wikibooks.org/wiki/GLSL_Programming/Unity/Billboards but it's not really helping. Anyone have some hints on how to get this working?

    Thanks in advance.

    Code (csharp):
    1.  
    2. Shader "FX/Anim Sprite" {
    3.        Properties {
    4.           _MainTex ( "Filmstrip (RGBA)", 2D ) = "white" {}
    5.           _Properties( "Properties ( # Frames, 1 / # Frames, Frame Offset )", Vector) = ( 16.0, 0.0625, 0, 0 )
    6.        }
    7.      
    8.     SubShader {
    9.         Tags { "Queue"="Transparent" "RenderType"="Transparent" }
    10.         LOD 100
    11.         Cull Off ZWrite Off Fog { Mode Off }  
    12.         Blend SrcAlpha One
    13.        
    14.         Pass {  
    15.             GLSLPROGRAM
    16.             varying lowp vec2 uv;
    17.             uniform lowp vec4 _Properties;
    18.             uniform vec4 _Time;
    19.          
    20.             #ifdef VERTEX
    21.             void main() {
    22.                 // position
    23.                 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    24.                  // calc animated uv
    25.                 uv = gl_MultiTexCoord0.xy;
    26.                  float timeVal = fract( _Time.y + uv.x ) * 2.0;
    27.                  uv.x = ( ( uv.x * _Properties.y ) + ( _Properties.z * _Properties.y ) ) + ( floor( timeVal * _Properties.x ) * _Properties.y );
    28.             }
    29.             #endif
    30.          
    31.             #ifdef FRAGMENT
    32.             uniform lowp sampler2D _MainTex;
    33.             void main() {
    34.                 gl_FragColor = texture2D( _MainTex, uv );
    35.             }
    36.             #endif  
    37.             ENDGLSL
    38.         }
    39.     }
    40. }
    41.  
     
  2. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I may be able to help, but I'm not familiar with what you mean by "filmstrip texture", or "axis aligned rotation around the up/+y axis on the sprites". A package with test assets would be handy.
     
  3. MrScary

    MrScary

    Joined:
    Dec 8, 2007
    Posts:
    94
    Well by filmstrip texture, I just mean an animated strip of images. I've attached one here that should work with that shader.

    $test_anim.png

    As for axis aligned rotation, I only mean rotating around the Y axis.. so if the camera is above the sprite, it's not going to angle up at the camera.. basically like a tree billboard or a flame. You want those to orient towards the camera but you don't want them pitching or rolling.. only yaw. Script code to rotate the object in this fashion is as follows:

    Code (csharp):
    1.  
    2.             Vector3 cameraPos = Camera.main.transform.position;
    3.             cameraPos.y = thisTransform.position.y;
    4.             thisTransform.LookAt( cameraPos );
    5.  
    I can try to work up a little test package. Thanks for taking a look.. as usual :)
     
  4. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I don't think you should be using a texture like that, because it won't compress. Do you disagree?

    I think this vert shader will give you the positional behavior you want, though (as long as your quads face their positive Z axis). Good?
    Code (csharp):
    1. uniform mediump mat4 _World2Object;
    2. uniform mediump vec3 _WorldSpaceCameraPos;
    3. void main() {
    4.     mediump vec2 zVector_XZ = normalize(_WorldSpaceCameraPos.xz + _World2Object[3].xz);
    5.     mediump vec2 positionXZ = mat2(vec2(zVector_XZ[1], -zVector_XZ.x), zVector_XZ) * gl_Vertex.xz;
    6.     gl_Position = gl_ModelViewProjectionMatrix * vec4(positionXZ.x, gl_Vertex.y, positionXZ[1], 1.);
    7. }
     
  5. MrScary

    MrScary

    Joined:
    Dec 8, 2007
    Posts:
    94
    okay.. here's the sample package...

    View attachment $AnimSpriteTest.unitypackage

    the auto sprites on the left don't have the auto rotate script on them. The ones on the right do. I'd like to figure out a way to get the rotation working in the shader so I could do away with the script. I'd also like to find a way to offset each animated sprite's start frame so they don't all animate in unison ( though maintain batching ).

    Thanks
     
  6. MrScary

    MrScary

    Joined:
    Dec 8, 2007
    Posts:
    94
    Oh .. must have missed your response while I was building the test package. I'll give it a try.

    We use 16 bit uncompressed textures for those textures. ( though I didn't bother changing the import settings in the test package ). They're pretty small. If we have enough of them, they may end up on an atlas, but as you probably know, pvrtc compressed alpha doesn't look so good.

    Update: looks like there's a bad transform in that vertex program somewhere. all the objects render offset and rotated from their origins. I do think they rotate towards the camera though. When i'm not so tired, I'll sit down and go through the math :)

    thanks again
     
    Last edited: Oct 24, 2011
  7. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Ug; I didn't take batching into consideration, which causes that problem. I can account for it, but I think it's only possible for uniformly-scaled meshes. What I did, was copy the vertex positions into UV2. I don't think you can find the center of the quad (or any other useful pivot) for non-uniformly scaled meshes, in the shader. If this doesn't work for you, I don't see a way around scripting the rotations.

    Like I said before, this shader is designed for quads that face their positive Z axis. Your meshes are not aligned that way. I'm attaching a Blender file with appropriate alignment and UV2s, if that's helpful.

    Code (csharp):
    1. uniform mediump mat4 _Object2World, _World2Object;
    2. uniform mediump vec4 unity_Scale;
    3. uniform mediump vec3 _WorldSpaceCameraPos;
    4. void main() {
    5.     uv = gl_MultiTexCoord0.xy;
    6.    
    7.     mediump float position_NotBatched_X = gl_MultiTexCoord1.x / unity_Scale.w;
    8.    
    9.     // _Object2World does not scale batched meshes; they are already scaled.
    10.     mediump vec2 centerOfQuad_XZ = gl_Vertex.xz * _Object2World[0].x - vec2(position_NotBatched_X, 0.);
    11.    
    12.     // _World2Object does no translation for batched meshes.
    13.     mediump vec2 zVector_XZ = normalize(
    14.         _WorldSpaceCameraPos.xz + _World2Object[3].xz - centerOfQuad_XZ);
    15.                
    16.     // Removes scale for unbatched meshes,
    17.     // as scale is built into gl_ModelViewProjectionMatrix for them.
    18.     // position_NotBatched_X is not changed for batched meshes.
    19.     position_NotBatched_X /= _Object2World[0].x;
    20.    
    21.     mediump vec4 position_Object = gl_Vertex;
    22.     position_Object.x += zVector_XZ[1] * position_NotBatched_X - position_NotBatched_X;
    23.     position_Object.z -= zVector_XZ.x * position_NotBatched_X;
    24.     gl_Position = gl_ModelViewProjectionMatrix * position_Object;          
    25. }
    I don't understand why you're using alpha at all, for an additive shader. Here's a comparison with your shader on a black background, for RGBA 16, RGB 16 with a second Alpha 8 texture, and RGBA 32. I don't think you're making a good choice with RGBA 16; I'd like to see what RGB PVRTC 4bpp w/Alpha 8 looks like, but the PNG you supplied wasn't conducive to testing, as PNGs don't open properly in Photoshop (they have transparency instead of an alpha channel.) However, unless you need this texture for alpha blending, elsewhere, I'd fill the background with black and try PVRTC 4bpp or RGB 16; RGB 16 is well-suited to this texture, considering how green it is. I'd like you to rationalize your choice before making a time investment to proceed further with the animation. I haven't looked at that part yet, and it will have to be approached differently if we need to scroll in two dimensions. That's fine; you should see what looks best, and go from there.
     

    Attached Files:

    Last edited: Oct 25, 2011
  8. Airship

    Airship

    Joined:
    Sep 10, 2011
    Posts:
    260
    I am trying to make a cg billboard shader and also need the billboard to just rotate along the y axis. I won't have issues with dynamic batching because my shader will be using 2 passes. Does Anyone know what the cg equivalent for doing that would be? If the glsl code Jessy posted does indeed rotate only around y axis could someone translate it to cg? Thanks for any help!
    Code (csharp):
    1. ///cg code from http://en.wikibooks.org/wiki/Cg_Programming/Unity/Billboards
    2. //but it should only rotate around y axis
    3.  output.pos = mul(UNITY_MATRIX_P,
    4.             mul(UNITY_MATRIX_MV, float4(0.0, 0.0, 0.0, 1.0))
    5.             + float4(input.vertex.x, input.vertex.y, 0.0, 0.0));
    Code (csharp):
    1. //Jessy's glsl code
    2. uniform mediump mat4 _World2Object;
    3. uniform mediump vec3 _WorldSpaceCameraPos;
    4. void main() {
    5.    mediump vec2 zVector_XZ = normalize(_WorldSpaceCameraPos.xz + _World2Object[3].xz);
    6.    mediump vec2 positionXZ = mat2(vec2(zVector_XZ[1], -zVector_XZ.x), zVector_XZ) * gl_Vertex.xz;
    7.     gl_Position = gl_ModelViewProjectionMatrix * vec4(positionXZ.x, gl_Vertex.y, positionXZ[1], 1.);
    8. }
     
  9. Airship

    Airship

    Joined:
    Sep 10, 2011
    Posts:
    260
    OK after some trial and error I think I replicated the gl code in cg. However, now the shader is no longer billboarding at all on any axis and just behaving like a normal shader with no billboard effect.

    Code (csharp):
    1. Shader "Cg  shader for billboards" {
    2.    Properties {
    3.       _MainTex ("Texture Image", 2D) = "white" {}
    4.    }
    5.    SubShader {
    6.       Pass {  
    7.          CGPROGRAM
    8.  
    9.          #pragma vertex vert  
    10.          #pragma fragment frag
    11.          
    12.          uniform sampler2D _MainTex;        
    13.  
    14.          struct vertexInput {
    15.             float4 vertex : POSITION;
    16.          };
    17.          struct vertexOutput {
    18.             float4 pos : SV_POSITION;
    19.             float4 tex : TEXCOORD0;
    20.          };
    21.  
    22.          vertexOutput vert(vertexInput input)
    23.          {
    24.             vertexOutput output;
    25.                        
    26. float2 zVector_XZ = normalize(_WorldSpaceCameraPos.xz + _World2Object[3].xz);
    27. float2 positionXZ = mul(float2x2(float2(zVector_XZ[1], -zVector_XZ.x), zVector_XZ),input.vertex.xz);
    28. output.pos =  mul(UNITY_MATRIX_MVP, float4(positionXZ.x, input.vertex.y, positionXZ[1], 1.));
    29.  
    30.    
    31.            
    32.             /* 
    33.             output.pos = mul(UNITY_MATRIX_P,
    34.              mul(UNITY_MATRIX_MV, float4(0.0, 0.0, 0.0, 1.0))
    35.              + float4(input.vertex.x, input.vertex.y, 0.0, 0.0));
    36.             */ 
    37.             output.tex = float4(input.vertex.x + 0.5,
    38.                input.vertex.y + 0.5, 0.0, 0.0);
    39.        
    40.             return output;
    41.          }
    42.  
    43.          float4 frag(vertexOutput input) : COLOR
    44.          {
    45.             return tex2D(_MainTex, float2(input.tex));  
    46.          }
    47.                
    48.  
    49.          ENDCG
    50.    
    51.       }
    52.      
    53.    }
    54. }
    55.  
    56.  
     
    Last edited: Mar 13, 2013
  10. Airship

    Airship

    Joined:
    Sep 10, 2011
    Posts:
    260
    Had another go at this after reading some posts on gamedev.net. Unfortunately I still do not have this working properly or aligned to the y axis but, I will post what I have in case someone can see the problem.

    Code (csharp):
    1.  
    2. float3 look = normalize(_WorldSpaceCameraPos - input.vertex.xyz); look.y = 0;
    3. float3 up = float3(0.0f, 1.0f, 0.0f);
    4. float3 right = normalize(cross(up, look));
    5. float4x4 rotationMatrix = float4x4( float4(right,0),float4(up,0),float4(look,0), float4(input.vertex.xyz,1));
    6. output.pos= mul(rotationMatrix, input.vertex);
     
  11. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    You can't just copy and paste from GLSL to Cg; a matrix index in Cg accesses a row, not a column.
     
  12. Airship

    Airship

    Joined:
    Sep 10, 2011
    Posts:
    260
    Thank you for your reply. so if I am not mistaken
    Code (csharp):
    1. float2 zVector_XZ = normalize(_WorldSpaceCameraPos.xz + _World2Object[3].xz);
    should really be :
    Code (csharp):
    1. float2 zVector_XZ = normalize(_WorldSpaceCameraPos.xz + float2(_World2Object[1].z,_World2Object[3].z ));
    2.  
    The result still appears to be the same.
    Code (csharp):
    1.  
    2. float2 zVector_XZ = normalize(_WorldSpaceCameraPos.xz + float2(_World2Object[1].z,_World2Object[3].z ));
    3. float2 positionXZ = mul(float2x2(float2(zVector_XZ[1], -zVector_XZ.x), zVector_XZ),input.vertex.xz);
    4. output.pos =  mul(UNITY_MATRIX_MVP, float4(positionXZ.x, input.vertex.y, positionXZ[1], 1.0));
    5.  
     
  13. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Nope, I don't know how you came to that conclusion*, and you're not constructing the matrix properly in the following line either.

    * Maybe I've got it. If you thought that matrices were 1-indexed, that would be right. They're not. And they're swizzled with .xyzw
     
    Last edited: Mar 16, 2013
  14. Airship

    Airship

    Joined:
    Sep 10, 2011
    Posts:
    260
  15. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Code (csharp):
    1. float2 billboardDirection_local_xz = normalize(
    2.     _WorldSpaceCameraPos.xz + float2(_World2Object[0].w, _World2Object[2].w)
    3. );
    4. float2x2 billboardRotation = float2x2(
    5.     billboardDirection_local_xz[1], billboardDirection_local_xz.x,
    6.     -billboardDirection_local_xz.x, billboardDirection_local_xz[1]
    7. );
    8. output.position.xz = mul(billboardRotation, input.position.xz);
    9. output.position.yw = input.position.yw;
    10. output.position = mul(UNITY_MATRIX_MVP, output.position);
     
    Last edited: Mar 18, 2013
    astracat111 and image28 like this.
  16. Airship

    Airship

    Joined:
    Sep 10, 2011
    Posts:
    260
    Thank you for taking the time to post the code. Works great and I will study it.
     
  17. hexart

    hexart

    Joined:
    Feb 22, 2013
    Posts:
    4
    Could either of you guys post the full shader doc? I'm still confused.
     
  18. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I don't see how that would help you. Everything else is basic.
     
  19. hexart

    hexart

    Joined:
    Feb 22, 2013
    Posts:
    4
    'couse I tried to follow this instruction, but I failed.;)

    I don't even know where to paste those 10 lines of codes~
     
  20. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
  21. kit

    kit

    Joined:
    Jun 16, 2011
    Posts:
    87
    Hi! I see that this is an old thread, but i really cannot understand why my shader is not turns plane to the camera. Here part of my code

    Code (csharp):
    1.  
    2. float3 look = normalize(ObjSpaceViewDir(v.vertex).xyz);
    3. float3 up = float3(0.0f, 1.0f, 0.0f);
    4. float3 right = normalize(cross(look, up));
    5. float4x4 rotationMatrix =
    6. float4x4(float4(right.x, up.x, -look.x, 0), float4(right.y, up.y, -look.y, 0), float4(right.z, up.z, -look.z, 0),  float4(v.vertex.xyz, 1));
    7.  
    8. float4 finalposition = mul(rotationMatrix, v.vertex);              
    9. float4 pos = float4(finalposition.xyz, 1);
    10. o.vertex = mul(UNITY_MATRIX_MVP, pos);
    11.  
    Please tell me, where is the mistake in this code?

    Thanks in advance.
     
  22. image28

    image28

    Joined:
    Jul 17, 2013
    Posts:
    457
    Thanks a lot Jessy, that little snippet allowed me to get billboards working with My LOD Fade in/out shader....
     
  23. image28

    image28

    Joined:
    Jul 17, 2013
    Posts:
    457
    Was having trouble with the C# billboards from the unity wiki sometimes failing ( they worked properly on the 4.6 beta, but when I downgraded they started malfunctioning on random trees )
     
  24. ds44

    ds44

    Joined:
    Mar 4, 2015
    Posts:
    6
    This is what I use for y axis aligned billboards (you will have to use it on rect that has vertices in xz plane, and properly oriented face normal - otherwise it will be culled/invisible). It works with object rotation, so it can be used for bullets and such... and you will probably have to disable dynamic batching.

    Code (CSharp):
    1. Shader "Transparent/AxisY Aligned Billboard CG"
    2. {
    3.    Properties {
    4.       _MainTex ("Texture Image", 2D) = "white" {}
    5.    }
    6.    SubShader {
    7.         Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
    8.         Lighting Off ZWrite Off Ztest LEqual Fog { Mode Off }
    9.         Cull Back
    10.         //Blend SrcAlpha One //additive blend
    11.         //Blend SrcAlpha OneMinusSrcAlpha
    12.         LOD 200
    13.  
    14.       Pass {
    15.          CGPROGRAM
    16.          #pragma vertex vert
    17.          #pragma fragment frag
    18.      #pragma fragmentoption ARB_precision_hint_fastest
    19.          uniform sampler2D _MainTex;      
    20.          struct vertexInput {
    21.             float4 vertex : POSITION;
    22.             float2 tex : TEXCOORD0;
    23.          };
    24.          struct vertexOutput {
    25.             float4 pos : SV_POSITION;
    26.             float2 tex : TEXCOORD0;
    27.          };
    28.          vertexOutput vert(vertexInput i)
    29.          {
    30.             vertexOutput o;
    31.             float3 objSpaceCamPos = mul(_World2Object, float4(_WorldSpaceCameraPos.xyz, 1)).xyz;
    32.             float3 offsetDir = normalize(cross(float3(0.0f, 1.0f, 0.0f), objSpaceCamPos));
    33.  
    34.             o.pos.xz = i.vertex.x * offsetDir.xz;
    35.             o.pos.yw = i.vertex.yw;
    36.  
    37.             o.pos = mul(UNITY_MATRIX_MVP, o.pos);
    38.             o.tex = i.tex;
    39.             return o;
    40.          }
    41.          float4 frag(vertexOutput input) : COLOR
    42.          {
    43.             return tex2D(_MainTex, float2(input.tex.xy));
    44.          }
    45.          ENDCG
    46.       }
    47.    }
    48. }
     
    Last edited: Mar 22, 2015
    mgear likes this.
  25. Metalhawk93

    Metalhawk93

    Joined:
    Jul 13, 2016
    Posts:
    45
    Hello, I understand that this thread is very old by now, but I was wondering I could have some assistance with the kind of billboarding I'm trying to do. You see I want to make a shader for my player character like the one probably used for Mario kart 64's player sprites, in which depend of the angle in camera view, a different unit of the sprite sheet is shown.