Vertex/Fragment Operations in 3.0?

I’m a little confused with the new surface shader method. Namely, I’m afraid it makes complex effects that require computations at per-vertex/fragment levels impossible (or maybe I missed something).

I imported my old shaders (with vertex and fragment programs) and they compile, but all come out black.

So my question is - what directives/new syntax do I need to continue writing vertex and fragment programs the “old fashioned” way? I understand the pipeline has changed for new features (like deferred rendering) so I was just wondering how to properly write compliant shaders.

Thanks,
Chris

Unity 2.6 uses a base pass for ambient and emissive, and then additional passes for each pixel light. Unity 3 does ambient, emissive, spherical harmonics, light maps and a directional light in the base pass. Additional passes are used for important pixel lights.

Additionally, a lot of little things have changed. I think the best way to learn to write a complete “traditional” shader for Unity 3 is to take a look at examples. As the surface shader reference says:

You can also use this Oren-Nayar shader I ported a few months ago:

Shader "Oren-Nayar/Diffuse" {
	Properties {
		_Color ("Main Color", Color) = (1,1,1,1)
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_Roughness ("Roughness", range(0,1)) = 0.5
	}
	
	CGINCLUDE
		
		struct SurfaceOutput {
			half3 Albedo;
			half3 Normal;
			half3 Emission;
			half Specular;
			half Gloss;
			half Alpha;
		};
		
		float4 _LightColor0;
		float4 _SpecColor;
		
		float _Roughness;
		
		inline half4 LightingOrenNayar (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {
			
			#ifndef USING_DIRECTIONAL_LIGHT
			lightDir = normalize(lightDir);
			#endif
			
			half3 n = s.Normal;
			half3 l = lightDir;
			half3 v = normalize(viewDir);
			
			half gamma = dot(v - n*dot(v, n), l - n*dot(l, n));
			
			half rough_sq = _Roughness * _Roughness;
			
			half a = 1 - 0.5 * (rough_sq / (rough_sq + 0.57));
			
			half b = 0.45 * (rough_sq / (rough_sq + 0.09));
			
			half alpha = max(acos(dot(v, n)), acos(dot(l, n)));
			half beta  = min(acos(dot(v, n)), acos(dot(l, n)));
			
			half C = sin(alpha)*tan(beta);
			
			half3 final = (a + b*max(0, gamma)*C);
			
			return half4(2*atten*_LightColor0.rgb*s.Albedo*max(0, dot(n,l))*final, 1);
		}
	ENDCG
	
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		Fog { Color [_AddFog] }
		Pass {
			Name "FORWARD"
			Tags { "LightMode" = "ForwardBase" }
			
			CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#pragma multi_compile_fwdbase
				#pragma target 3.0
				#include "HLSLSupport.cginc"
				#include "UnityCG.cginc"
				#include "AutoLight.cginc"
				
				sampler2D _MainTex;
				float4 _Color;
				
				struct v2f {
					V2F_POS_FOG;
					float2 packuv0 : TEXCOORD0;
					#ifdef LIGHTMAP_OFF
						float3 normal : TEXCOORD1;
					#endif
					#ifndef LIGHTMAP_OFF
						float2 lightmapUV : TEXCOORD2;
					#else
						float3 lightDir : TEXCOORD2;
						float3 shlight : TEXCOORD3;
						float3 viewDir : TEXCOORD4;
					#endif
					LIGHTING_COORDS(5,6)
				};
				
				float4 _MainTex_ST;
				
				#ifndef LIGHTMAP_OFF
					float4 unity_LightmapST;
				#endif
				v2f vert (appdata_full v) {
					v2f o;
					PositionFog( v.vertex, o.pos, o.fog );
					o.packuv0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
					#ifndef LIGHTMAP_OFF
						o.lightmapUV.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
					#endif
					#ifdef LIGHTMAP_OFF
						o.normal = v.normal;
						o.lightDir = ObjSpaceLightDir( v.vertex );
						o.viewDir = ObjSpaceViewDir( v.vertex );
					#endif
					#ifdef LIGHTMAP_OFF
						float3 shlight = ShadeSH9 (float4(v.normal,1.0));
						o.shlight = shlight;
					#endif
					TRANSFER_VERTEX_TO_FRAGMENT(o);
					return o;
				}
				#ifndef LIGHTMAP_OFF
					sampler2D unity_Lightmap;
					sampler2D unity_LightmapInd;
				#endif
				half4 frag (v2f IN) : COLOR {
					float2 uv_MainTex = IN.packuv0.xy;
					SurfaceOutput o;
					o.Alpha = 0.0;
					half4 c = tex2D(_MainTex, uv_MainTex) * _Color;
					o.Albedo = c.rgb;
					o.Alpha = c.a;
					#ifdef LIGHTMAP_OFF
						o.Normal = IN.normal;
					#endif
					half atten = LIGHT_ATTENUATION(IN);
					#ifdef LIGHTMAP_OFF
						c = LightingOrenNayar (o, IN.lightDir, IN.viewDir, atten);
						c.rgb += o.Albedo * IN.shlight;
					#else
						half3 lmFull = tex2D (unity_Lightmap, IN.lightmapUV.xy).rgb * 2.0;
						#ifdef SHADOWS_SCREEN
							c.rgb = o.Albedo * min(lmFull, atten*2);
						#else
							c.rgb = o.Albedo * lmFull;
						#endif
						c.a = 0;
					#endif
					return c;
				}
			
			ENDCG
		}
		
		Pass {
			Name "FORWARD"
			Blend One One
			Tags { "LightMode" = "ForwardAdd" }
			CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#pragma multi_compile_fwdadd
				#pragma target 3.0
				#include "HLSLSupport.cginc"
				#include "UnityCG.cginc"
				#include "AutoLight.cginc"
				
				sampler2D _MainTex;
				float4 _Color;
				
				struct v2f {
					V2F_POS_FOG;
					float2 packuv0 : TEXCOORD0;
					float3 normal : TEXCOORD1;
					float3 lightDir : TEXCOORD2;
					float3 viewDir : TEXCOORD3;
					LIGHTING_COORDS(4,5)
				};
				
				float4 _MainTex_ST;
				
				v2f vert (appdata_full v) {
					v2f o;
					PositionFog( v.vertex, o.pos, o.fog );
					o.normal = v.normal;
					o.packuv0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
					o.lightDir = ObjSpaceLightDir( v.vertex );
					o.viewDir = ObjSpaceViewDir( v.vertex );
					TRANSFER_VERTEX_TO_FRAGMENT(o);
					return o;
				}
				
				half4 frag (v2f IN) : COLOR {
					float2 uv_MainTex = IN.packuv0.xy;
					SurfaceOutput o;
					o.Alpha = 0.0;
					half4 c = tex2D(_MainTex, uv_MainTex) * _Color;
					o.Albedo = c.rgb;
					o.Alpha = c.a;
					o.Normal = IN.normal;
					c = LightingOrenNayar (o, IN.lightDir, IN.viewDir, LIGHT_ATTENUATION(IN));
					return c;
				}
				
			ENDCG
		}
	}
	
	Fallback "Diffuse"
}

And here is the same lighting model but with a bump map as well:

Shader "Oren-Nayar/Diffuse Bumped" {
	Properties {
		_Color ("Main Color", Color) = (1,1,1,1)
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_BumpMap ("Normalmap", 2D) = "bump" {}
		_Roughness ("Roughness", range(0,1)) = 0.5
	}
	
	CGINCLUDE
		
		struct SurfaceOutput {
			half3 Albedo;
			half3 Normal;
			half3 Emission;
			half Specular;
			half Gloss;
			half Alpha;
		};
		
		float4 _LightColor0;
		float4 _SpecColor;
		
		float _Roughness;
		
		inline half4 LightingOrenNayar (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {
			
			#ifndef USING_DIRECTIONAL_LIGHT
			lightDir = normalize(lightDir);
			#endif
			
			half3 n = s.Normal;
			half3 l = lightDir;
			half3 v = normalize(viewDir);
			
			half gamma = dot(v - n*dot(v, n), l - n*dot(l, n));
			
			half rough_sq = _Roughness * _Roughness;
			
			half a = 1 - 0.5 * (rough_sq / (rough_sq + 0.57));
			
			half b = 0.45 * (rough_sq / (rough_sq + 0.09));
			
			half alpha = max(acos(dot(v, n)), acos(dot(l, n)));
			half beta  = min(acos(dot(v, n)), acos(dot(l, n)));
			
			half C = sin(alpha)*tan(beta);
			
			half3 final = (a + b*max(0, gamma)*C);
			
			return half4(2*atten*_LightColor0.rgb*s.Albedo*max(0, dot(n,l))*final, 1);
		}
	ENDCG
	
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		Fog { Color [_AddFog] }
		Pass {
			Name "FORWARD"
			Tags { "LightMode" = "ForwardBase" }
			
			CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#pragma multi_compile_fwdbase
				#pragma target 3.0
				#include "HLSLSupport.cginc"
				#include "UnityCG.cginc"
				#include "AutoLight.cginc"
				
				sampler2D _MainTex;
				sampler2D _BumpMap;
				float4 _Color;
				
				struct v2f {
					V2F_POS_FOG;
					float4 packuv0 : TEXCOORD0;
					#ifndef LIGHTMAP_OFF
						float2 lightmapUV : TEXCOORD1;
					#else
						float3 lightDir : TEXCOORD1;
						float3 shlight : TEXCOORD2;
						float3 viewDir : TEXCOORD3;
					#endif
					LIGHTING_COORDS(4,5)
				};
				
				float4 _MainTex_ST;
				float4 _BumpMap_ST;
				
				#ifndef LIGHTMAP_OFF
					float4 unity_LightmapST;
				#endif
				v2f vert (appdata_full v) {
					v2f o;
					PositionFog( v.vertex, o.pos, o.fog );
					o.packuv0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
					o.packuv0.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);
					#ifndef LIGHTMAP_OFF
						o.lightmapUV.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
					#endif
					TANGENT_SPACE_ROTATION;
					#ifdef LIGHTMAP_OFF
						o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
						o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex));
					#endif
					#ifdef LIGHTMAP_OFF
						float3 shlight = ShadeSH9 (float4(v.normal,1.0));
						o.shlight = shlight;
					#endif
					TRANSFER_VERTEX_TO_FRAGMENT(o);
					return o;
				}
				#ifndef LIGHTMAP_OFF
					sampler2D unity_Lightmap;
				#endif
				half4 frag (v2f IN) : COLOR {
					float2 uv_MainTex = IN.packuv0.xy;
					float2 uv_BumpMap = IN.packuv0.zw;
					SurfaceOutput o;
					half4 c = tex2D(_MainTex, uv_MainTex) * _Color;
					o.Albedo = c.rgb;
					o.Alpha = c.a;
					o.Normal = UnpackNormal(tex2D(_BumpMap, uv_BumpMap));
					half atten = LIGHT_ATTENUATION(IN);
					#ifdef LIGHTMAP_OFF
						c = LightingOrenNayar (o, IN.lightDir, IN.viewDir, atten);
						c.rgb += o.Albedo * IN.shlight;
					#else
						half3 lmFull = tex2D (unity_Lightmap, IN.lightmapUV.xy).rgb * 2.0;
						#ifdef SHADOWS_SCREEN
							c.rgb = o.Albedo * min(lmFull, atten*2);
						#else
							c.rgb = o.Albedo * lmFull;
						#endif
						c.a = 0;
					#endif
					return c;
				}
			
			ENDCG
		}
		
		Pass {
			Name "FORWARD"
			Blend One One
			Tags { "LightMode" = "ForwardAdd" }
			CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#pragma multi_compile_fwdadd
				#pragma target 3.0
				#include "HLSLSupport.cginc"
				#include "UnityCG.cginc"
				#include "AutoLight.cginc"
				
				sampler2D _MainTex;
				sampler2D _BumpMap;
				float4 _Color;
				
				struct v2f {
					V2F_POS_FOG;
					float4 packuv0 : TEXCOORD0;
					float3 lightDir : TEXCOORD1;
					float3 viewDir : TEXCOORD2;
					LIGHTING_COORDS(3,4)
				};
				
				float4 _MainTex_ST;
				float4 _BumpMap_ST;
				
				v2f vert (appdata_full v) {
					v2f o;
					PositionFog( v.vertex, o.pos, o.fog );
					o.packuv0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
					o.packuv0.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);
					TANGENT_SPACE_ROTATION;
					o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
					o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex));
					TRANSFER_VERTEX_TO_FRAGMENT(o);
					return o;
				}
				
				half4 frag (v2f IN) : COLOR {
					float2 uv_MainTex = IN.packuv0.xy;
					float2 uv_BumpMap = IN.packuv0.zw;
					SurfaceOutput o;
					half4 c = tex2D(_MainTex, uv_MainTex) * _Color;
					o.Albedo = c.rgb;
					o.Alpha = c.a;
					o.Normal = UnpackNormal(tex2D(_BumpMap, uv_BumpMap));
					c = LightingOrenNayar (o, IN.lightDir, IN.viewDir, LIGHT_ATTENUATION(IN));
					return c;
				}
				
			ENDCG
		}
	}
	
	Fallback "Diffuse"
}

Thanks I really appreciate it!

  • Chris

Daniel, thanks for posting this. I loaded your Oren Nayar bump map into 3 beta5 but noticed that shadows do not work with it. Is this intentional?

Also, where did you see the quote you posted above about #pragma debug? I can’t find reference to that any other place.

*EDIT ok I see it works with shadows when forward rendering is enabled, because of the forward declarations in the shader. Interesting.

That’s in the middle of the “Writing Surface Shaders” page in the reference manual, near the end of “Surface Shader compile directives”.

Strange then why it didn’t show up with my search for:
site:http://unity3d.com/support/documentation “#pragma debug”

Thanks

*EDIT closest thing I can find is this page:

with no mention of “#pragma debug”, or “debug”. Closest thing I can find is debugging normals, binormals and uvs. Oh well, not really important I guess, just wondering if there was another resource you got that from that wasn’t common knowledge.

because unity 3 documentations are shared with the beta only, the documentation on the page is Unity 2.6.1 only

Unity 3 docs are naturally not online. You have to look at your local docs.

–Eric

So there is already documentation for v3???

Well hot damn sure enough “reference manual” from Unity 3 beta menu brings up new reference material, this is news to me!!!

"Additionally, you can write #pragma debug inside CGPROGRAM block, and then surface compiler will spit out a lot of comments of the generated code. You can view that using Open Compiled Shader in shader inspector. "

Thanks everyone!

Sure, the beta comes with new updated documentation for its functionality.
not all might be up to date but if you find such stuff, report it :slight_smile:

Daniel, thanks for posting the shader. I am trying to incorporate my homography calculations into the vertex program, and this helped me to accomplish that with the diffuse shader.

However, I am now trying to add transparency as well. I looked at the built-in Alpha Diffuse shader code and these threads:

  1. http://forum.unity3d.com/threads/38743-transparency-with-bump-and-specular-mapping?highlight=adding+transparency+diffuse
  2. unlit transparent shader: How to add alpha composition combined with the texture alpha - Questions & Answers - Unity Discussions
  3. http://forum.unity3d.com/threads/85299-Self-Illumin-Diffuse-shader-add-Alpha-property
  4. Unity - Manual: ShaderLab: legacy alpha testing

Based on these, I have made the following changes:

  1. To Properties:
_MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
  1. To the SubShader:
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
ZWrite Off
Alphatest Greater 0
  1. To the Forward Base Pass, I added
Blend SrcAlpha OneMinusSrcAlpha

and in the Forward Add Pass I changed

Blend One One to Blend SrcAlpha OneMinusSrcAlpha

I am a complete noob at shader programming and can’t figure out what else to add/change. I would be thankful if you could guide me or point me in the right direction.

Thanks,
Chirag

That all looks correct, except you’re not changing how the fragment alpha value is set. You can do this in the lighting function at line 49/50, where you will want to put the otherwise unused s.Alpha into your return value.

Thanks Daniel!
I fixed it by changing line 139 to c.a = o.Alpha. For the sake of learning, does it achieve anything different than returning the s.Alpha value?

Also, if I need to make this a self-illumin diffuse shader, what should i read up on? Most of what I find uses a SetTexture[_MainTex] method, but I can’t figure out what to do with it…
Thanks

It probably won’t make any difference in the compiled shader. Just write what makes the most sense to you.

You should probably write a surface shader with a custom lighting function. It’s far easier than modifying this old shader.

Hey,

The problem with writing a surface shader is that I need to perform vertex operations within the shader, ie I need to multiply the MVP matrix with the Homography matrix that I’ve calculated elsewhere to calculate the position of each vertex. From what I saw it is harder to do that in a surface shader.

The way I’m doing it now is:

v2f o;
o.pos = mul (UNITY_MATRIX_MVP,  v.vertex);
o.pos = o.pos / o.pos.w;
o.pos = mul (matrixH, o.pos);
o.pos.x = o.pos.x / o.pos.w;
o.pos.y = o.pos.y / o.pos.w;
o.pos.w = o.pos.w / o.pos.w;

Is there a way to do this in the surface shader?

If it’s not compatible with the custom vertex function (the vertex directive for the surface #pragma), you might have luck using #pragma debug and modifying the resulting code in the compiled shader.

Thanks! I’ll explore that option…

Tested that diffuse model, but seems that lightmaps are not correctly used.

Original:


Thanks!