[updated] Unity 3: started to port advanced terrain shaders

After having received my downloadlink to the Unity 3 beta I have tried to port my hacked terrain shaders to the latest built (3.00b5).
Here you can find the results.

VertextLit


As in my shader package for unity 2.x I abuse the VertexLit Shader to render bushes and all kind of foliage that is higher than grass.

Features:

  • It supports casting shadows and should support receiving shadows – but somehow it doesn’t. Applying the shader to a normal game object it works perfectly. Doing so with foliage painted within the built in terrain engine it fails to cast shadows… so I think it’s got nothing to do with the shader itself.
  • It allows individual bending of each model brought into the terrain engine, but this bending has nothing to do with the terrain wind settings nor the new wind zones.
  • It works with a modified lighting term in order to brighten surfaces pointing away from the light.
// hacked lighting term
c.rgb = s.Albedo * (light.rgb * 0.7 + 0.5);

There are much smarter solutions on lighting leaves, but as my capabilities on programming are more than limited I am not able to implement those. Maybe someone else. Some ideas can be found here:
http://http.developer.nvidia.com/GPUGems3/gpugems3_ch04.html
http://http.developer.nvidia.com/GPUGems3/gpugems3_ch16.html
Unfortunately it is not possible to assign more than one texture to any detail mesh within the terrain engine. But lighting depending on the angle between light vector and view vector should be possible.

Usage:

  • Just create a new shader, give it any name, and copy the sourcecode. You might have to restart Unity to make it work.
  • This time it uses 2 sided geometry in order to avoid having to flip normal for this caused some artifacts in lighting around edges. So just double your geometry, flip the normals and combine both meshes in your 3d app.
  • You might have to adjust (reduce!) the phong smoothing angle in order to avoid wrong lighting. Do this in your 3d app or in the Unity importer settings.

Bugs:
-Only works in deferred lighting. Why?

TreeLeaves


TreeLeaves in Unity 3 look pretty weird as they do not use any precalculated lighting any more (although the shader still contains vars for ambient and direct occlussion…): I think the contrast between lit and unlit surfaces is much to high, in addition on the side facing away from the light there are some pretty bright leaves whereas on the lit side you can find some rather dark leaves.

Features:

  • It uses the same hacked lighting as the VertexLit shader which gives you a more realistic lighting on the leaves I think.
  • In opposite to the VertexLit shader it uses 1 sided geometry and flips the normals if needed. Although this leads to some artifacts and sudden popping in the lighting i stuck with this, as 2 sided geometry didn’t produce a better result: As I wanted the tree bark to still be rendered very smoothly I could not lower the phong smoothing angle which is assigned to the whole model. Maybe there is a way to do this within the shader, but I don’t know.
  • It supports casting shadows but does not receive any – like the original shader.
  • Original coloring, scaling and the new bending introduced by Unity 3.

Usage:

  • Just create a new shader, give it any name, and copy the sourcecode. Then assign the shader to your tree prefabs.
    You might have to restart Unity to make it work.
  • As it still works with 1 sided geometry you can use all the given tree models.

TreeBark


Features:

  • Bumpmapping. Finally.
  • Original coloring, scaling and the new bending introduced by Unity 3.

Usage:

  • Just create a new shader, give it any name, and copy the sourcecode. Then assign the shader to your tree prefabs.
    You might have to restart Unity to make it work.
  • Make sure that your trees are NOT in a folder containing the name “Ambient-Occlusion” – otherwise you won’t have much bumpmapping. This costed me hours on investigating my models to find out…

Splatmaps


Bumpmapping! Well, I have tried hard to get this thing to work – but without much success. Only some pretty wired bumpmapping and only darkening no brighting. I guess it has got something to do with beast lightmapping… Anyway. So it will be up to someones else to make this work with unity 3.

One more thing


Billboards are not affected by fog. I hope Unity fill fix this until the final release.

Conclusion


Although lighting and shadows in Unity 3 now affects grass and although I can have bending trees in different sizes and coloring plus bending detail meshes I still like my results in Unity 2.6 better due to the following facts:

  • Detail meshes casting shadows on the terrain.
  • Pseudo bumpmapping on the terrain.
  • It should not be mentioned but: correct billboards.

Just have a look at:
http://forum.unity3d.com/viewtopic.php?t=45290&postdays=0&postorder=asc&start=0

lars

VertexLitShader

Shader "Hidden/TerrainEngine/Details/Vertexlit" {
Properties {
	_Color ("Main Color", Color) = (1,1,1,1)
	_MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
	_Cutoff ("Alpha cutoff", Range(0,1)) = 0.3
	
	
}

SubShader {
	Tags {"IgnoreProjector"="True" "RenderType"="TreeTransparentCutout"}
	LOD 200
	
	// in case you use 1sided geometry you would have to enable this:
	//Cull Off
	
CGPROGRAM
#pragma exclude_renderers gles
#pragma surface surf MyLambert vertex:vert addshadow alphatest:_Cutoff

// forward rendering doesn't work... ????

inline half4 LightingMyLambert (SurfaceOutput s, half3 lightDir, half atten)
{
	#ifndef USING_DIRECTIONAL_LIGHT
	lightDir = normalize(lightDir);
	#endif
	//half diff = max (0, dot (s.Normal, lightDir));
	half diff = saturate(dot(s.Normal, lightDir));
	
	// hacked lighting term
	diff = diff * 0.7 + 0.5;
	//
	
	half4 c;
	c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten * 2);	
	c.a = s.Alpha;
	return c;
}


inline half4 LightingMyLambert_PrePass (SurfaceOutput s, half4 light)
{
	half4 c;
	// hacked lighting term
	c.rgb = s.Albedo * (light.rgb * 0.7 + 0.5);
	c.a = s.Alpha;
	return c;
}

sampler2D _MainTex;
float4 _Color;

struct Input {
	float2 uv_MainTex;
	float4 color : COLOR;
	
};

// Calculate a 4 fast sine-cosine pairs 
// val:    the 4 input values - each must be in the range (0 to 1) 
// s:      The sine of each of the 4 values 
// c:      The cosine of each of the 4 values

void FastSinCos (float4 val, out float4 s, out float4 c) { 
   val = (val * 1.02 - .5) * 6.28318530;	// scale to range: -pi to pi  make it cyclic
   // powers for taylor series 
   float4 r5 = val * val;					// wavevec ^ 2 
   float4 r1 = r5 * val;					// wavevec ^ 3 
   float4 r6 = r1 * val;					// wavevec ^ 4; 
   float4 r2 = r6 * val;					// wavevec ^ 5; 
   float4 r7 = r2 * val;					// wavevec ^ 6; 
   float4 r3 = r7 * val;					// wavevec ^ 7; 
   float4 r8 = r3 * val;					// wavevec ^ 8; 

   //Vectors for taylor's series expansion of sin and cos 
   float4 sin7 = {1, -0.16161616, 0.0083333, -0.00019841}; 
   float4 cos8  = {-0.5, 0.041666666, -0.0013888889, 0.000024801587}; 
   // sin 
   s = r1 * sin7.y + val + r2 * sin7.z + r3 * sin7.w; 
   // cos 
   c = 1 + r5 * cos8.x + r6 * cos8.y + r7 * cos8.z + r8 * cos8.w; 
} 


void vert (inout appdata_full v, out Input o) {
	
	
	// in case you use 1sided geometry you would have to enable this:
	/*
	//Viewdir in object space 
	float3 objSpaceCameraPos = mul(_World2Object, float4(_WorldSpaceCameraPos.xyz, 1)).xyz * unity_Scale.w;
	float3 ovd = v.vertex.xyz - objSpaceCameraPos;
	float dp = dot(ovd, v.normal);    
	//If viewdir is same direction-ish as normal, flip it. 
	v.normal = v.normal * -sign(dp);
	*/
	
	
////////// start bending
	
	// red value of color (in "add/edit detail dialog") is factor of bending
	float factor = v.color.r;
		
	const float _WindSpeed  = 0.5;		
	const float _WaveScale = 2;
	
	const float4 _waveXSize = float4(0.012, 0.02, 0.06, 0.024) * 4;
	const float4 _waveZSize = float4 (0.006, .02, 0.02, 0.05) * 4;
	const float4 waveSpeed = float4 (0.3, .5, .4, 1.2) * 15;


	// this is the amount of bending
	float4 _waveXmove = float4(0.012, 0.02, -0.06, 0.048) * 10 * factor;
	float4 _waveZmove = float4(0.006, .02, -0.02, 0.1) * 10 * factor;
    
	float4 waves;
	waves = v.vertex.x * _waveXSize;
	waves += v.vertex.z * _waveZSize;

	waves += _Time.x * waveSpeed *_WindSpeed;

	float4 s, c;
	waves = frac (waves);
	FastSinCos (waves, s,c);

	float waveAmount = v.texcoord.y;
	s *= waveAmount;

	// Faster winds move the grass more than slow winds 
	s *= normalize (waveSpeed);

	s = s * s;
	float fade = dot (s, 1.3);
	s = s * s;
	float3 waveMove = float3 (0,0,0);
	waveMove.x = dot (s, _waveXmove);
	waveMove.z = dot (s, _waveZmove);
	v.vertex.xz -= mul ((float3x3)_World2Object, waveMove).xz;
	
	
////////// end bending
}

void surf (Input IN, inout SurfaceOutput o) {
	half4 c = tex2D(_MainTex, IN.uv_MainTex);
	o.Albedo = c.rgb;
	o.Alpha = c.a;
	
}
ENDCG
}
Fallback "Transparent/Cutout/VertexLit"
}

TreeLeaves Shader

Shader "Nature/mySoft Occlusion Leaves" {
	Properties {
		_Color ("Main Color", Color) = (1,1,1,1)
		_MainTex ("Main Texture", 2D) = "white" {  }
		_Cutoff ("Base Alpha cutoff", Range (.5,.9)) = .5
		_BaseLight ("BaseLight", range (0, 1)) = 0.35
		_AO ("Amb. Occlusion", range (0, 10)) = 2.4
		_Occlusion ("Dir Occlusion", range (0, 20)) = 7.5
		_Scale ("Scale", Vector) = (1,1,1,1)
		_SquashAmount ("Squash", Float) = 1
	}


SubShader {
		Tags {
			"Queue" = "Transparent-99"
			"IgnoreProjector"="True"
			"RenderType" = "TreeTransparentCutout"
		}
		
		// switch on for 1sided leaves
		Cull Off
		
		CGPROGRAM
		#pragma surface surf TreeleavesLambert vertex:treevertex addshadow alphatest:_Cutoff
		#include "TerrainEngine.cginc"
		
		half4 LightingTreeleavesLambert (SurfaceOutput s, half3 lightDir, half atten) {
          lightDir = normalize(lightDir);
          half NdotL = saturate(dot(s.Normal, lightDir));
          half diff = NdotL * 0.8 + 0.3;
          half4 c;
          c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten * 2);
          c.a = s.Alpha;
          return c;
      	}
      	      	
		
		//@TODO: soft vegetation
		
		void treevertex (inout appdata_full v) {
			
			//Viewdir in object space 
			float3 objSpaceCameraPos = mul(_World2Object, float4(_WorldSpaceCameraPos.xyz, 1)).xyz * unity_Scale.w;
			float3 ovd = v.vertex.xyz - objSpaceCameraPos;
			float dp = dot(ovd, v.normal);    
			
			
			TerrainAnimateTree(v.vertex, v.color.w);
			
			//If viewdir is same direction-ish as normal, flip it. 
			v.normal = v.normal * -sign(dp);
		}
		
		sampler2D _MainTex;
		float4 _Color;
		
		struct Input {
			float2 uv_MainTex;
			//
			float3 worldRefl;
		};
		
		void surf (Input IN, inout SurfaceOutput o) {
			half4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
			o.Albedo = c.rgb;
			o.Alpha = c.a;
		}
		ENDCG
	}
	
	SubShader {
		Tags {
			"Queue" = "Transparent-99"
			"IgnoreProjector"="True"
			"RenderType" = "TransparentCutout"
		}
		Cull Off
		ColorMask RGB
		Pass {
			Tags { "LightMode" = "Vertex" }
			AlphaTest GEqual [_Cutoff]
			Lighting On
			Material {
				Diffuse [_Color]
				Ambient [_Color]
			}
			SetTexture [_MainTex] { combine primary * texture DOUBLE, texture }
		}		
	}

	Dependency "BillboardShader" = "Hidden/TerrainEngine/Soft Occlusion Leaves rendertex"
	Fallback Off
}


Tree Bark Shader

Shader "Nature/my Soft Occlusion Bark" {
	Properties {
		_Color ("Main Color", Color) = (1,1,1,0)
		_MainTex ("Main Texture", 2D) = "white" {  }
		
		//
		_BumpMap ("Normalmap", 2D) = "bump" {}

		_BaseLight ("BaseLight", range (0, 1)) = 0.35
		_AO ("Amb. Occlusion", range (0, 10)) = 2.4
		_Scale ("Scale", Vector) = (1,1,1,1)
		_SquashAmount ("Squash", Float) = 1
	}
	SubShader {
		Tags {
			"RenderType"="Opaque"
		}
		CGPROGRAM
		#pragma surface surf Lambert vertex:treevertex addshadow
		#include "TerrainEngine.cginc"
		
		
		
		void treevertex (inout appdata_full v) {
			TerrainAnimateTree(v.vertex, v.color.w);
		}
		
		sampler2D _MainTex;
		
		sampler2D _BumpMap;

		float4 _Color;
		
		struct Input {
			float2 uv_MainTex;
			float2 uv_BumpMap;
		};
		
		void surf (Input IN, inout SurfaceOutput o) {
			half4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
			o.Albedo = c.rgb;
			o.Alpha = c.a;
			
			o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
		}
		ENDCG
	}
	
	SubShader {
		Tags {
			"RenderType"="Opaque"
		}

		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma exclude_renderers gles
			#include "SH_Vertex.cginc"
			ENDCG
						
			SetTexture [_MainTex] { combine primary * texture DOUBLE, constant }
		}
	}
	SubShader {
		Tags {
			"RenderType" = "Opaque"
		}
		Pass {
			Tags { "LightMode" = "Vertex" }
			Lighting On
			Material {
				Diffuse [_Color]
				Ambient [_Color]
			}
			SetTexture [_MainTex] { combine primary * texture DOUBLE, constant }
		}		
	}
	
	
	
	Dependency "BillboardShader" = "Hidden/TerrainEngine/Soft Occlusion Bark rendertex"
	Fallback Off
}

Thanks for that work. I’m especially looking forward to whether you get bumpmapped terrain working again.

The tree generator in Unity 3 allows some interesting things with regards to bark and leaves. It may provide better lighting as well, but I’ve yet to find leaf textures that support all the required channels, especially translucency.

hi tom, your are right: using the new tree editor and its new shaders produces much better results. but i am wondering how many draw calls a single mesh tree (placed within the terrain engine) will cause using those shaders. i think it is 6 (without real time shadows) and 12 with soft shadows turned on compared to 4 / 9 draw calls using the hacked shaders… which makes quite a difference in case you have something like a dense forest…

Not really, since most of them will be billboarded. You rarely need more than 20-30 actual mesh trees to simulate even the densest forest.

Did some testing. Here’s a forest with tree creator trees. Leaves are full-featured, i.e. normal map, gloss and translucency. Quality settings are on fantastic.

There was some visible slowdown when moving around, but I think polycount will be more of a problem than draw calls with the tree creator trees. :slight_smile:

might be right, but billboarding doesn’t work correctly right now in unity 3: billboards do not receive any fog, no matter if they are “traditional” imported or generated using the tree creator.
by the way: do your trees bend in the wind? when editing a tree within the editor everything looks nice. even when i just add a tree prefab to the scene it bends. but it doesn’t when i add it using the terrain engine…

Didn’t they fix the fog in b6?

fog is fixed – thanks! but there is still no bending…
by the way: tree generetad with the new tree creator tool look gorgeous: very nice shaders! although they are pretty expensive to render: 12 draw calls on each mesh tree…

i just did a small example scene comparing unity 3’s new tree shaders and the hacked ones.
although the screenshoots give you the impression that the new shaders are faster they are not… but produce very nice results.

here you will find the player:
http://wp1089796.vwp0260.webpack.hosteurope.de/unity/treetest_mac_intel.zip
http://wp1089796.vwp0260.webpack.hosteurope.de/unity/treetest_win.zip

next to the trees the player shows bending detail meshes using the hacked vertexlit shader, ssao and edge smoothing to bring back some kind of “soft vegetation”. it’s done using unity 3 beta v6.

lars

To my eye at least, the bark on your shaders look better, but the leaves look better in the stock U3 image. Don’t you think?

the original leave shaders look defenitely better: you have bumpmapping, translucency, self shadowing and ambient occlusion. bark shaders are more or less the same.
but whatever i do i can’t make trees created with the tree creator bend in the wind. it works while i am editing the tree, but whenever i place them on the terrain i get no bending at all. has anybody ever had bending trees using the terrain engine?

Hi, can you share the bump mapping you trying to do with unity3.0 terrain, I did like to modify that see if I can get it to work.

I am getting a couple of errors trying to compile your bark shader with Unity 3.0… not sure if this is related to my graphics card or not…

Shader error in ‘Nature/my Soft Occlusion Bark’: Program ‘vert’, syntax error, unexpected $end at token “” at line 54

Shader error in ‘Nature/my Soft Occlusion Bark’: Program ‘vert’, type name expected at token “” at line 54

Shader error in ‘Nature/my Soft Occlusion Bark’: Shader program had errors at line 56

Shader error in ‘Nature/my Soft Occlusion Bark’: Program ‘vert’, Can’t open include file “SH_Vertex.cginc” at line 59

Any updates on this, ever? I’m interested solely in the terrain texture shaders, i.e. bump-mapping on terrain splats.

bump mapping was done by chris morris:
http://forum.unity3d.com/threads/63282-Bump-maps-for-built-in-terrain?highlight=bump+terrain

Similar problem.

Well, I just can’t seem to get the bark of my terrain placed trees bump mapped. What the hell am I doing doing?