Search Unity

Billboard shader using vertex offsets in color or uv2 data

Discussion in 'Shaders' started by jc_lvngstn, Jul 26, 2013.

  1. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    I'm working on a poor man's terrain replacement project. It creates a mesh from a heightmap, has lod handling, all that.
    Here's some screenshots, if you look at them on another tab and zoom in you can see that the distant trees are very dense, and very distant. I haven't been able to get this kind of distance and density using Unity's billboard trees.

    http://forum.unity3d.com/threads/188495-Procedural-Terrain-with-Dense-Forests/page3

    The way I handle distant trees is, they are just tall grass basically. It's two bisecting planes. 8 Vertices, 4 polygons. There is no "billboard" rotation to always face the camera. I create patches of trees, and combine them into one mesh. The performance is great, in my opinion. Obviously, there are some "gotchas", but it works well for my needs. The upside is, I don't have to manage each individual tree polygon, just the mesh patches. I suspect Unity draws and calculates its billboard trees on an individual basis.

    Ok, here is what I need to know.

    I want to just use billboard trees, locked to the Y axis.
    But the challenge is...each tree quad would be part of a mesh of other tree quad polygons.
    The trick as I see it is, each vertex needs to store the vertex offset from the pivot, AND the location of the pivot itself. This would allow you to rotate each vertex around the pivot to face the camera. Why not have each vertex coordinate the actual pivot, and the UV2 data be the offset data?

    So, four vertices. A tree polygon, 5 meters wide, 10 meters tall. Pivot is in the center at 0.0 (local to the polygon vertices, NOT the mesh vertices).
    This quad is to be positioned at 12.0, 10, 3.5 in world coordinates.

    Vert 1 (bottom left, on the ground) - UV2 would be new vector2(-2.5f, 0)
    Vert 2 (top left) - UV2 would be new vector2(-2.5.0f, 10.0f)
    Vert 3 (top right) - UV2 would be vector2(2.5f, 10.0f)
    Vert 4 (bottom right) - UV2 would be vector2 (2.5f, 0.0f)
    Each vertex coordiante would actually be the quad pivot coordinate..12.0, 10, 3.5.


    Again, I want this to be locked to the Y axis. So, it would only be adjusting the vertex X and Z positions, to avoid the "tilting" or bowing effect when you are nearby and tilt the camera. I could probably do this in C# or something pretty easily, but I don't know how to do it in a vertex shader.


    So, in summary...with the UV2 coordinates for a quad holding offsets of each vertex from the quad pivot, would it be possible for a vertex shader to use this to rotate those to face the camera. Even if the polygons are part of a larger mesh. This way, even a giant mesh of polygons could be used for distant billboards. Maybe this is how the particle system works, I don't know.

    Really hoping for some advice or direction on this.

    Thanks,
    JC
     
  2. Neyl

    Neyl

    Joined:
    Feb 3, 2011
    Posts:
    18
    Hope this helps
    Code (csharp):
    1. Shader "Crankshaft/Vegetation/Billboard Tree Tinted" {
    2. Properties {
    3.     _Color ("Tint Color", Color) = (1,1,1,1)
    4.     _MainTex ("Texture", 2D) = "white" { }
    5.     _Speed ("Wave Speed", Float) = 0.12
    6. }
    7.  
    8.  
    9. SubShader {
    10.     Tags { "Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="Opaque"}
    11.     //Cull Back Lighting Off ZWrite Off Fog { Mode Off }
    12.     //Blend One One
    13.     Pass {
    14.         AlphaTest Greater 0.25
    15.  
    16. CGPROGRAM
    17. #pragma vertex vert
    18. #pragma fragment frag
    19.  
    20. #include "UnityCG.cginc"
    21. //#include "../CrankshaftShared.cginc"
    22.  
    23. float4 _Color;
    24. sampler2D _MainTex;
    25. float _Speed;
    26.  
    27. struct appdata {
    28.     float4 vertex : POSITION;
    29.     float4 texcoord : TEXCOORD0;//texture - uv1
    30.     float4 texcoord1 : TEXCOORD1;//billboard offset - uv2
    31.     float4 color : COLOR;
    32. };
    33.  
    34. struct v2f {
    35.     float4  pos : SV_POSITION;
    36.     float2  uv : TEXCOORD0;
    37.     float4 color : COLOR;
    38. };
    39.  
    40. v2f vert (appdata v)
    41. {
    42.     v2f o;
    43.     //vertical billboarding
    44.     float3 eyeVector = ObjSpaceViewDir( v.vertex );
    45.    
    46.     float3 upVector = float3(0,1,0);
    47.     float3 sideVector = normalize(cross(eyeVector,upVector));
    48.  
    49.     float3 finalposition = v.vertex;
    50.     finalposition += (v.texcoord1.x) * sideVector;
    51.     finalposition += (v.texcoord1.y) * upVector;
    52.                
    53.         //waves
    54.    
    55.     float4 pos = float4(finalposition, 1);
    56.                
    57.     o.pos = mul( UNITY_MATRIX_MVP, pos );
    58.  
    59.     o.uv = v.texcoord.xy;
    60.     o.color = v.color;
    61.     o.color.a = 1;//fragment shader need one, we use alpha in vertex shader to waving
    62.  
    63.     return o;
    64. }
    65.  
    66. half4 frag (v2f i) : COLOR
    67. {
    68.     return tex2D(_MainTex, i.uv) *  _Color;
    69. }
    70. ENDCG
    71.  
    72.     }
    73. }
    74.  
    75.  
    76. Fallback "VertexLit"
    77. }
     
  3. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    Neyl, thank you very much! I was hoping for some direction on whether or not it was possible, but you appear to have given a complete solution. I'll test this as soon as I can, hopefully this evening or the next.
     
  4. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    First, the billboard shader works great!
    The first screenshot below is from how I was doing it before. Soft edge one from the wiki:

    http://wiki.unity3d.com/index.php/Transparent_Diffuse_Two_Pass_Lit_Shadows

    I was using two planes bisecting, so more planes. No billboarding. 2.4M triangles, 236 draw calls, 4.1m vertices. 35 fps (still not bad, seeing as how there are SO many trees).

    The one Neyl posted, using billboards:
    991k triangles (less than half), 155 draw calls (the first shader was two pass, for softer trees), 1.4M vertices. 95 FPS.
    So...in terms of memory use, I would say it is probably a huge thing.

    Here are screenshot comparisons. Open in another tab for more detail.

    Old:
    $DenseRegularShader.jpg

    New:
    $DenseBillboardShader.jpg



    Ok, so...sadly, quality suffers a little because it's not using a softer edge shader, and the normals of the vertices do not seem to affect lighting.

    Neyl, I hate to impose further, so I'll just ask in general...would anyone be willing to add to this shader, to provide better shading and soft edges? I'm working on packaging this up for the community, it features both terran, grass and distant tree generation. It would be shame to lose the normal lighting and soft edges, I feel they are very attractive. They don't need shadows, just normals that I can set to match the terrain normal, thus giving them a much better shading.

    I'll also dig around and see if I can find a simpler shader than the two pass one, that I can merge with Nyel's.
    Thanks Neyl, I'm very excited that the billboard shader is not only possible but saves a ton on memory and performance.
     
    Last edited: Jul 30, 2013
  5. Vanamerax

    Vanamerax

    Joined:
    Jan 12, 2012
    Posts:
    938
    Now I'm not an expert with shaders (actually, I just started with learning the syntax etc.) But here is the following that I do know to get a basic normal-based vertex lighting (anyone, please correct me if I'm wrong at anything):

    In the appdata structure, Define a variable as follows:

    Code (csharp):
    1.  
    2. float3 normal : NORMAL;
    3.  
    This will make the shader aware of the normal of the faces

    In the vertex shader, this normal has to be multiplied with some matrix (dont know which one though, there are a lot of them) and then stored v2f struct (I think as a float3)

    Then in the fragment shader, you apply a Lambert lighting function: "the normal of the point dot the light direction * the attenuation * 2."

    A tutorial of it can be found here (scroll to about halfway) : http://unitygems.com/noobs-guide-shaders-5-bumped-diffuse-shader/

    I don't understand all of it though, so these are just some pointers to the things I know. I don't know where the attenuation is coming from and what it really represents. You also have to think about "how many" or what lights you want to support. For the purpose of far-distance billboards, I guess only a directional light would suffice in most cases.
     
  6. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    Thanks, wasstraat65. I would expect only the main directional light to be taken into account.
    And, I think I could get by without the soft edges, as long as we could take the normal lighting into account.
     
  7. kaz2057

    kaz2057

    Joined:
    Jan 18, 2011
    Posts:
    326
    Very optimized shader.
    Cool. Need just to complete with speed var . now there is no wind ;)
    However there are other crankshaft shader or pack to share or sell in asset store?

    Thanks
     
  8. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    This is what I changed it to, still nor lighting based on normals :(

    Code (csharp):
    1. Shader "Crankshaft/Vegetation/Billboard Tree Tinted"
    2. {
    3.     Properties
    4.     {
    5.         _Color ("Tint Color", Color) = (1,1,1,1)
    6.         _MainTex ("Texture", 2D) = "white" { }
    7.         _Speed ("Wave Speed", Float) = 0.12
    8.     }
    9.  
    10.     SubShader
    11.     {
    12.         Tags { "Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="Opaque"}
    13.         Cull Back Lighting On// ZWrite On Fog// { Mode Off }
    14.         //Blend One One
    15.         Pass
    16.         {
    17.             AlphaTest Greater 0.25
    18.            
    19.             CGPROGRAM
    20.  
    21.             #pragma vertex vert
    22.             #pragma fragment frag            
    23.  
    24.             #include "UnityCG.cginc"
    25.             //#include "../CrankshaftShared.cginc"           
    26.  
    27.             float4 _Color;
    28.             sampler2D _MainTex;
    29.             float _Speed;            
    30.  
    31.             struct appdata
    32.             {
    33.                 float4 vertex : POSITION;
    34.                 float4 texcoord : TEXCOORD0;//texture - uv1
    35.                 float4 texcoord1 : TEXCOORD1;//billboard offset - uv2
    36.                 float3 normal: NORMAL;
    37.                 float4 color : COLOR;
    38.             };
    39.  
    40.             struct v2f
    41.             {
    42.                 float4  pos : SV_POSITION;
    43.                 float2  uv : TEXCOORD0;
    44.                 float3 color : COLOR;
    45.             };
    46.  
    47.             v2f vert (appdata v)
    48.             {
    49.                 v2f o;
    50.                 //vertical billboarding
    51.                 float3 eyeVector = ObjSpaceViewDir( v.vertex );
    52.                 float3 upVector = float3(0,1,0);
    53.                 float3 sideVector = normalize(cross(eyeVector,upVector));
    54.                 float3 finalposition = v.vertex;
    55.                 finalposition += (v.texcoord1.x) * sideVector;
    56.                 finalposition += (v.texcoord1.y) * upVector;                           
    57.                 float4 pos = float4(finalposition, 1);
    58.                 o.pos = mul( UNITY_MATRIX_MVP, pos );
    59.                 o.uv = v.texcoord.xy;
    60.                 o.color = ShadeVertexLights(v.vertex, v.normal);
    61.                 //o.color.a = 1;//fragment shader need one, we use alpha in vertex shader to waving
    62.                 return o;
    63.             }
    64.            
    65.             half4 frag (v2f i) : COLOR
    66.             {
    67.                 return tex2D(_MainTex, i.uv) *  _Color;
    68.             }
    69.            
    70.             ENDCG
    71.         }
    72.  
    73.     }
    74.  
    75.     Fallback "VertexLit"
    76.  
    77. }
    I added passing in the normal in the vertex shader, and using ShadeVertexLights. Also, I guess ShadeVertexLights returns a Vector3, so I had to lose the alpha channel.
     
    Last edited: Jul 31, 2013
  9. dirtybassett

    dirtybassett

    Joined:
    Oct 3, 2012
    Posts:
    59
    I am interested in using this shader but not sure how to set up the uv's. Any chance of some help?
     
  10. pailhead

    pailhead

    Joined:
    Oct 8, 2012
    Posts:
    68
    I'm unfamiliar with this, but i've done some similar stuff where i had to recalculate the normal based on the deformation.

    I can't quite make out the texcoord stuff here without trying it, but it seems like you are rotating your billboard, but keeping normals the same.



    Code (csharp):
    1.  
    2. //this should be your new normal
    3. float3 newNormal = normalize(cross(sideVector,upVector));
    4.  

    I cant make out the newposition stuff in the shader, i'll have to sketch it, But theoretically, you don't need to use uv2, you can do all your transformations in the shader. The same way this shader has a new position, and still knows what v.vertex are. You can go like

    myUV2 = uv;

    and then do whatever you want, like, i would offset it by -0.5 so i get the 0 in the middle, and vertices having +/- 0.5,

    myUV2.x -= 0.5;

    will center the 'pivot' of sorts,

    btw


    Code (csharp):
    1.  
    2. // this can become...
    3. float3 finalposition = v.vertex;
    4. //....
    5. float4 pos = float4(finalposition, 1);
    6.  
    7. //...this
    8. float4 finalPos = v.vertex;
    9.  
    10. finalPos.xyz += .... //stuff in here was float3
    11.  
    12.  
    13. //this way you store one less variable, finalPos.w just stays 1, from v.vertex.w because you call it as float 4 (x,y,z,w) above
    14.  
    15.  
    16.  
    17.  

    I can give it some more thought, since im not sure what this particular shader does yet.
     
    Last edited: Aug 10, 2013
  11. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
  12. dirtybassett

    dirtybassett

    Joined:
    Oct 3, 2012
    Posts:
    59
    Got the bill-boarding to work at last but not getting any lighting, also how would you go about rotation on all axis not just Y?
     
  13. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    I bought a book on Unity shaders this week, it looks interesting. Hopefully I can fix the lighting issue.
    In the terrain project, I believe it uses the terrain normal for the tree polys, so that the shading matches. This should improve the visual quality a lot. I just need to get back home and try it out (been out of town this past week).
    As far as rotation on other axis...can't say. You mean to have them always face the camera, with no axis alignment?
     
  14. dirtybassett

    dirtybassett

    Joined:
    Oct 3, 2012
    Posts:
    59
    Yeah I could do with always facing the camera from any angle, I think it's possible but I have no idea how.
     
  15. Lulucifer

    Lulucifer

    Joined:
    Jul 8, 2012
    Posts:
    358
  16. dirtybassett

    dirtybassett

    Joined:
    Oct 3, 2012
    Posts:
    59
    Had a look at your link but seems to have trouble with multiple billboards. The shader here works with combined meshes also.
     
  17. Deleted User

    Deleted User

    Guest

    I'm trying to combine this thread shader with the one Lulucifer linked, but for now I'm failing miserably. Only managed to center the pivot thats it.
     
  18. dirtybassett

    dirtybassett

    Joined:
    Oct 3, 2012
    Posts:
    59
    Well I have had some success in getting lighting to work. Not sure what I was doing as I'm not good with code, basically I found a vertex lighting cg shader and pasted in the uv offset transforms. Also had to recalculate the normals.
    Still cant figure out always face camera?.


    Code (csharp):
    1. Shader "Custom/Billboard Tree Tinted" {
    2.       Properties {
    3.         _MainTex ("Texture 1", 2D) = "white" {}
    4.         _Ambient("Ambient Color",Color) = (0.3,0.3,0.3,1)
    5.       }
    6.       SubShader {
    7.       Tags { "RenderType"="Opaque"}
    8.       Pass {
    9.            Blend SrcAlpha OneMinusSrcAlpha  
    10.            Tags { "LightMode" = "Vertex" }//otherwise no light related values will be filled
    11.            CGPROGRAM
    12.            #pragma vertex vert
    13.            #pragma fragment frag
    14.            #include "UnityCG.cginc"
    15.    
    16.            sampler2D _MainTex;
    17.            fixed4 _Ambient;
    18.            half4 _MainTex_ST;
    19.            float4 _Color;
    20.            struct appdata
    21.                 {
    22.                     float4 vertex : POSITION;
    23.                     float4 texcoord : TEXCOORD0;//texture - uv1
    24.                     float4 texcoord1 : TEXCOORD1;//billboard offset - uv2
    25.                     float3 normal: NORMAL;
    26.                     float4 color : COLOR;
    27.                 };
    28.    
    29.            struct v2f{
    30.                fixed4 position: POSITION;
    31.                half2 uv_MainTex:TEXCOORD0;
    32.                fixed4 color: COLOR;
    33.            };
    34.    
    35.            v2f vert (appdata_full v) {
    36.                v2f o;
    37.                
    38.                
    39.         float3 eyeVector = ObjSpaceViewDir(v.vertex);
    40.                     float3 upVector =float3(0,1,0);
    41.                     float3 sideVector = normalize(cross(eyeVector,upVector));
    42.                     float3 finalposition = v.vertex;
    43.                     finalposition += (v.texcoord1.x) * sideVector;
    44.                     finalposition += (v.texcoord1.y) * upVector;                          
    45.                     float4 pos = float4(finalposition, 1);
    46.                o.position = mul(UNITY_MATRIX_MVP, pos );
    47.                v.normal = float4(finalposition, 1);
    48.                
    49.                o.uv_MainTex = TRANSFORM_TEX(v.texcoord.xy , _MainTex);
    50.    
    51.                // per vertex light calc
    52.                fixed3 lightDirection;
    53.                fixed attenuation;
    54.                // add diffuse
    55.                if(unity_LightPosition[0].w == 0.0)//directional light
    56.                {
    57.                   attenuation = 2;
    58.                   lightDirection = normalize(mul(unity_LightPosition[0],UNITY_MATRIX_IT_MV).xyz);
    59.                }
    60.                else// point or spot light
    61.                {
    62.                   lightDirection = normalize(mul(unity_LightPosition[0],UNITY_MATRIX_IT_MV).xyz - v.vertex.xyz);
    63.                   attenuation = 1.0/(length(mul(unity_LightPosition[0],UNITY_MATRIX_IT_MV).xyz - v.vertex.xyz)) * 0.5;
    64.                }
    65.                fixed3 normalDirction = normalize(v.normal);
    66.                fixed3 diffuseLight =  unity_LightColor[0].xyz * max(dot(normalDirction,lightDirection),0);
    67.                // combine the lights (diffuse + ambient)
    68.                
    69.                o.color.xyz = diffuseLight * attenuation + _Ambient.xyz;
    70.                o.color.a = 1;
    71.                return o;
    72.            }
    73.    
    74.            fixed4 frag(v2f i):COLOR{
    75.               fixed4 c = tex2D(_MainTex, i.uv_MainTex);  
    76.               return  c * i.color;
    77.            }
    78.            ENDCG
    79.       }
    80.       }
    81. Fallback "Mobile/Diffuse"
    82. }
    83.  
     
    Last edited: Aug 24, 2013
  19. AR_Rizvi

    AR_Rizvi

    Joined:
    Aug 12, 2013
    Posts:
    40
    This shader is not working as a billboard shader and it also dsnt have that alpha chanel in it which the shader before has why?
     
  20. dirtybassett

    dirtybassett

    Joined:
    Oct 3, 2012
    Posts:
    59
    Edited shader so alpha should work now. Have you set up your UVs correctly?
     
  21. AR_Rizvi

    AR_Rizvi

    Joined:
    Aug 12, 2013
    Posts:
    40
    yup alpha is working nw but billboard still missng
     
  22. AR_Rizvi

    AR_Rizvi

    Joined:
    Aug 12, 2013
    Posts:
    40