Lock rotation to Y axes only?

I have this modified tree billboard shader where I’ve fixed the alpha so it’s cutout, and I’m trying to lock the billboard rotation to only the Y axes, currently when I look up or down with the first person controller the billboarded trees follow really wierdly. I know this has to be possible because when I roll the camera the trees do not roll with it.

I’ve tried modifying the position code but It either throws up a load of errors or the trees disappear. I’m at a loss and would much appreciate a fix. I have a feeling that I may need to go bounty hunting somewhere else, if I do then where do I need to go?

Shader "Hidden/TerrainEngine/BillboardTree" {
	Properties {
		_MainTex ("Base (RGB) Alpha (A)", 2D) = "white" {}
		_Cutoff ("Cutoff", float) = 0.5
	}
	
	SubShader {
		Tags { "Queue" = "Transparent-100" "IgnoreProjector"="True" }
		
		Pass {
			ColorMask rgb
			ZWrite Off Cull Off
			
			CGPROGRAM
			#pragma vertex vert
			#include "UnityCG.cginc"
			#include "TerrainEngine.cginc"
			#pragma fragment frag

			struct v2f {
				float4 pos : POSITION;
				fixed4 color : COLOR0;
				float2 uv : TEXCOORD0;
			};

			v2f vert (appdata_tree_billboard v) {
				v2f o;
				TerrainBillboardTree(v.vertex, v.texcoord1.xy, v.texcoord.y);	
				o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
				o.uv.x = v.texcoord.x;
				o.uv.y = v.texcoord.y > 0;
				o.color = v.color;
				return o;
			}

			sampler2D _MainTex;
			fixed _Cutoff;
			fixed4 frag(v2f input) : COLOR
			{
				fixed4 col = tex2D( _MainTex, input.uv);
				col.rgb *= input.color.rgb;
				clip(col.a - _Cutoff);
				return col;
			}
			ENDCG			
		}
	}

	SubShader {
		Tags { "Queue" = "Transparent-100" "IgnoreProjector"="True" }
		
		Pass {

			CGPROGRAM
			#pragma vertex vert
			#pragma exclude_renderers shaderonly
			#include "UnityCG.cginc"
			#include "TerrainEngine.cginc"

			struct v2f {
				float4 pos : POSITION;
				fixed4 color : COLOR0;
				float2 uv : TEXCOORD0;
			};

			v2f vert (appdata_tree_billboard v) {
				v2f o;
				TerrainBillboardTree(v.vertex, v.texcoord1.xy, v.texcoord.y);	
				o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
				o.uv.x = v.texcoord.x;
				o.uv.y = v.texcoord.y > 0;
				o.color = v.color;
				return o;
			}
			ENDCG			

			ColorMask rgb
			ZWrite Off Cull Off
			
			AlphaTest Greater 0
			SetTexture [_MainTex] { combine texture * primary, texture }
		}
	}
	
	Fallback Off
}

Is this not possible in Unity, I’m pretty sure it’s something that goes in after v2f o; but everything I’ve tried throws errors

This is the code in TerrainEngine.cginc if there’s anything I need to change in there instead.

void TerrainBillboardTree( inout float4 pos, float2 offset, float offsetz )
{
	float3 treePos = pos.xyz - _TreeBillboardCameraPos.xyz;
	float treeDistanceSqr = dot(treePos, treePos);
	if( treeDistanceSqr > _TreeBillboardDistances.x )
		offset.xy = offsetz = 0.0;
		
	// positioning of billboard vertices horizontally
	pos.xyz += _TreeBillboardCameraRight.xyz * offset.x;
	
	// tree billboards can have non-uniform scale,
	// so when looking from above (or bellow) we must use
	// billboard width as billboard height
	
	// 1) non-compensating
	//pos.xyz += _TreeBillboardCameraUp.xyz * offset.y;
	
	// 2) correct compensating (?) 
	//float alpha = _TreeBillboardCameraPos.w;
	//float a = offset.y;
	//float b = offsetz;
		 //2a) using elipse-radius formula
		//float r = abs(a * b) / sqrt(sqr(a * sin(alpha)) + sqr(b * cos(alpha))) * sign(b);
		//float r = abs(a) * b / sqrt(sqr(a * sin(alpha)) + sqr(b * cos(alpha)));
		 //2b) sin-cos lerp
		//float r = b * sin(alpha) + a * cos(alpha);	
//	pos.xyz += _TreeBillboardCameraUp.xyz * r;
	
	// 3) incorrect compensating (using lerp)
	// _TreeBillboardCameraPos.w contains ImposterRenderTexture::GetBillboardAngleFactor()
	//float billboardAngleFactor = _TreeBillboardCameraPos.w;
	//float r = lerp(offset.y, offsetz, billboardAngleFactor);	
	//pos.xyz += _TreeBillboardCameraUp.xyz * r;
	
	// so now we take solution #3 and complicate it even further...
	// 
	// case 49851: Flying trees
	// The problem was that tree billboard was fixed on it's center, which means
	// the root of the tree is not fixed and can float around. This can be quite visible
	// on slopes (checkout the case on fogbugz for screenshots).
	//
	// We're fixing this by fixing billboards to the root of the tree. 
	// Note that root of the tree is not necessary the bottom of the tree - 
	// there might be significant part of the tree bellow terrain.
	// This fixation mode doesn't work when looking from above/below, because
	// billboard is so close to the ground, so we offset it by certain distance
	// when viewing angle is bigger than certain treshold (40 deg at the moment)
	
	// _TreeBillboardCameraPos.w contains ImposterRenderTexture::billboardAngleFactor
	float billboardAngleFactor = _TreeBillboardCameraPos.w;
	// The following line performs two things:
	// 1) peform non-uniform scale, see "3) incorrect compensating (using lerp)" above
	// 2) blend between vertical and horizontal billboard mode
	float radius = lerp(offset.y, 0, billboardAngleFactor);
			
	// positioning of billboard vertices veritally
	pos.xyz += _TreeBillboardCameraUp.xyz * radius;
			
	// _TreeBillboardCameraUp.w contains ImposterRenderTexture::billboardOffsetFactor
	float billboardOffsetFactor = _TreeBillboardCameraUp.w;
	// Offsetting billboad from the ground, so it doesn't get clipped by ztest.
	// In theory we should use billboardCenterOffsetY instead of offset.x,
	// but we can't because offset.y is not the same for all 4 vertices, so 
	// we use offset.x which is the same for all 4 vertices (except sign). 
	// And it doesn't matter a lot how much we offset, we just need to offset 
	// it by some distance
	pos.xyz += _TreeBillboardCameraFront.xyz * abs(offset.x) * billboardOffsetFactor;
}

Thanks for pointing out the TerrainEngine.cginc :slight_smile:
No have not t solved it so far, but i interested in improving the tree billboard / transitions from 3d to 2d , so i have maybe a chance now.

Edit:
My best bet is that this line is changing the vertical rotation

TerrainEngine.cginc

    // positioning of billboard vertices veritally
    pos.xyz += _TreeBillboardCameraUp.xyz * radius;

maybe to set the radius to 0 or disable the line at all could do the trick.

Did anyone find a solution to this problem?

I can’t seem to find TerrainEngine.cginc, how would I go about editing it?

Thanks

I second this! The billboards are very annoying this way… Not sure what the developer of this thought when he implemented this.

I think it’s crazy that this issue still hasn’t been addressed by Unity devs. I’ve searched high and low for an actual fix to the built in billboarding, and this seems to be the only thread that’s looking in the right direction. Quite often this question ends with someone pointing to Advanced Foliage Shader, which is fine, but why should Unity’s built in tree billboard shader stay broken for years?

Anyway I’m completely new to Unity and shader code, but I managed to stop the billboards from tilting by modifying the line in TerrainEngine.cginc that fffMalzbier mentioned:

pos.xyz += float4(0.0, _TreeBillboardCameraUp.y, 0.0, 0.0) * radius;

This stops the x and z tilting which makes these billboards a lot more usable!
I haven’t been able to work out how to stop the height from scaling with the camera angle though.
So if someone more knowledgeable can help to give this thread a more final solution that would be great.

With this change the Terrain setting “Fade Length” has to be 0 otherwise the mesh trees that fade into billboards still tilt. I don’t think there’s any need for fading if the billboards don’t move with the camera though.

And it seems the above can be simplified to

pos.y += _TreeBillboardCameraUp.y * radius;