Diffuse shader fail

This question follows from http://forum.unity3d.com/threads/135310-Shader-works-in-unity-window-(OSX)-but-fails-on-iPhone

The shader in that post works fine on OS X (ie renders correctly in the unity preview window) but fails on an actual iPhone device.

As has been pointed out by steego, this #pragma needs to be taken out:

//#pragma exclude_renderers gles

( looks like it only got but there automatically in the first place because I didn’t have an explicit fragment shader; I have since explicitly written out a trivial fragment shader, and commented out the #pragma )

but then this line fails:

float LdotN = saturate(  dot ( glstate.light[0].position, N )  );

… with the error: ‘glstate: undeclared identifier’

If I understand correctly from dreamora, ‘glstate’ is simply not available on GL ES.

So how can I access this data?

Martin links me to http://unity3d.com/support/documentation/Components/SL-BuiltinValues.html which I have already visited…

That page seems to be saying that I should recode the above line to:

float LdotN = saturate(  dot ( _ObjectSpaceLightPos[0].position, N )  );

…but this now gives an error of ‘_ObjectSpaceLightPos: undeclared identifier’

Next I dig into:

#include "UnityCG.cginc"

…and discover that the text ‘_ObjectSpaceLightPos’ doesn’t appear anywhere.

Does this mean that the page referenced above is outdated?

Reading through the file, I notice that there is a

float4 unity_LightColor[4];
float4 unity_LightPosition[4];

on line 150.

FINALLY, I get something working with:

	// DIFFUSE
	float3 N = mul(UNITY_MATRIX_IT_MV, float4(i.normal, 1));
	float LdotN = saturate(  dot ( unity_LightPosition[0], N )  );
	
	o.color += LdotN * _Color * unity_LightColor[0];

	// original was:	
	//   float LdotN = saturate(  dot (glstate.light[0].position, N)  ); //_ObjectSpaceLightPos[0].position fails
	//   o.color += LdotN * _Color * glstate.light[0].diffuse;

O joy it even runs on the iPhone!

919469--34459--$Screen Shot 2012-05-11 at 08.08.52.png

Does this mean that this unity help page referenced above needs to be updated?

But wait! That is not right. It is failing to calculate the diffuse colour correctly. It looks as though LdotN must always be coming out at the same value.

Now I am against stuck. What is going wrong here?

This is taken straight out of Unitys own Mobile shaders.

It should put you on the right track.

First the Vertex shader part

// Vert Lighting
			half3 worldN = mul((float3x3)_Object2World, SCALED_NORMAL);
			o.vLight = ShadeSH9 (float4(worldN,1.0)); // Ambient light contribution
			o.vLight += LightingLambertVS (worldN, _WorldSpaceLightPos0.xyz); // the actual light

and the lighting Function

// Lighting Function
		inline half3 LightingLambertVS (half3 normal, half3 lightDir)
		{
			half diff = max (0, dot (normal, lightDir));
			return _LightColor0.rgb * (diff * 2);
		}

Kind regards,
Brn

Sorry that the page that I linked to didn’t help. I have only used trivial Cg shaders so far; thus, I don’t really know what works and what doesn’t work.

Just out of curiosity: why don’t you forget about that outdated shader and start learning how to use Surface Shaders as suggested by dreamora?

brn: thanks for answering! Which particular mobile shader was this taken out of? I can’t find it…

I’m just trying this out now, and there is a problem: it isn’t recognising _LightColor0.

I do a test like this:

	half3 foo = _LightColor0.rgb;

and that produces an ’ undeclared identifier ’ error.

I’m going to assume I can replace this by unity_LightColor[0]

So this is the code I ended up with (I had to also swizzle .rgb to avoid type mismatches):

// Lighting Function
inline half3 LightingLambertVS (half3 normal, half3 lightDir)
{
    half diff = max ( 0, dot( normal, lightDir ) );
    //return _LightColor0.rgb * (diff * 2);
    return unity_LightColor[0].rgb * (diff * 2);
}

:
:
	// Vert Lighting	
	half3 worldN = mul((float3x3)_Object2World, i.normal);
	o.color.rgb = ShadeSH9 (float4(worldN,1.0)); // Ambient light contribution
	o.color.rgb += LightingLambertVS (worldN, _WorldSpaceLightPos0.xyz); // the actual light

this produces a completely black screen.

:expressionless:

here is my complete shader code as it stands:

Shader "Custom/AmbientDiffuseVertex" 
{
	Properties 
	{
		_Color ("Main Color:", Color) = (1,1,1,1)
		_Glow ("Glow:", float) = 0.0
	}
	SubShader 
	{
		Pass
		{
			Lighting On
			
CGPROGRAM

#pragma vertex vert 
#pragma fragment frag

#include "UnityCG.cginc"

float4 _Color;
float _Glow;

struct v2f
{
	float4 position : POSITION;
	float4 color : COLOR; 
};


// Lighting Function
inline half3 LightingLambertVS (half3 normal, half3 lightDir)
{
    half diff = max ( 0, dot( normal, lightDir ) );
    //return _LightColor0.rgb * (diff * 2);
    return unity_LightColor[0].rgb * (diff * 2);
}

  
        
v2f vert ( appdata_base i)
{	
	v2f o;

	o.position = mul(UNITY_MATRIX_MVP, i.vertex);
		
	// Vert Lighting

	half3 worldN = mul((float3x3)_Object2World, i.normal);
	o.color.rgb = ShadeSH9 (float4(worldN,1.0)); // Ambient light contribution
	o.color.rgb += LightingLambertVS (worldN, _WorldSpaceLightPos0.xyz); // the actual light	
			
	return o;
}

float4 frag( v2f i ) : COLOR
{
	//float4 tex
	return i.color;
}
ENDCG

		}
	} 
}

I should probably be multiplying the diffuse contribution by _Color. But if it is rendering black as it is, there is some problem!

How to get this shader working?

Dreamora’s answer did motivate me to look for some documentation on surface shaders, which led me here: Unity - Manual: Writing Surface Shaders

And that page kind of makes me despair… looking through it I’m pretty sure it assumes a certain knowledge. So I figured if this is some sort of new abstraction mechanism, I should probably figure out how to do all of this at the low-level, and then this page would make sense.

If anyone can link me to a more suitable resource (read: idiot’s guide) I would be grateful.

I am really aghast how far I have fallen down the rabbit hole on what seems to be a completely trivial ’ hello world ’ style application.

OK, that makes sense. Well, one of the best options to learn Cg is Nvidia’s Cg tutorial: http://http.developer.nvidia.com/CgTutorial/cg_tutorial_chapter01.html
Ippokratis has ported the first few examples of Cg shaders to Unity: http://unifycommunity.com/wiki/index.php?title=Cg_Tutorial_to_Unity

I’m not aware of other resources that teach you how to program Cg shaders in Unity without assuming that you already know how to write Cg code. Actually, I would be very interested in such resources.

you just need to include some of the unity cginc files so they become available for your use.

like this

			Pass {
		Name "FORWARD"
		Tags { "RenderType"="Opaque" "LightMode"="ForwardBase"}
		
		CGPROGRAM
		
		#pragma vertex vert_surf
		#pragma fragment frag_surf
		#pragma fragmentoption ARB_precision_hint_fastest
		#pragma multi_compile_fwdbase

		#include "HLSLSupport.cginc"
		#include "UnityCG.cginc"
		#include "Lighting.cginc"
		#include "AutoLight.cginc"

brn, thanks for that – it works perfectly.

If you fancy cutting all of the quoted garbage from your post it would improve this thread as a resource

PPS Here is the final working shader for the record:

Shader "Custom/piGlowShader" 
{
	Properties 
	{
		_Color 		("_Color:"		, Color) = (1,1,1,1)
		
		_Specular 	("_Specular:"	, Color) = (1,1,1,1)
		_Shininess 	("_Shininess:"	, float) = 0.5

		_Glow 		("_Glow:"		, float) = 0.0		
	}
	
	
	SubShader 
	{
		Pass
		{
        	Name "FORWARD"
        	Tags { "RenderType"="Opaque" "LightMode"="ForwardBase"}

			Lighting On

// - - - - - - - 

CGPROGRAM

#pragma vertex vertex_shader 
#pragma fragment fragment_shader

#pragma fragmentoption ARB_precision_hint_fastest
#pragma multi_compile_fwdbase


#include "HLSLSupport.cginc"
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"  


// important to pass the properties here
float4 _Color; 
float4 _Specular; 
float  _Shininess;
float  _Glow;


struct v2f
{
	float4 position : POSITION;
	float4 color : COLOR; 
};


// Lighting Function
inline half3 LightingLambertVS (half3 normal, half3 lightDir)
{
    half diff = max ( 0, dot( normal, lightDir ) );
    return _LightColor0.rgb * (diff * 2);
}

        
v2f vertex_shader ( appdata_base i)
{	
	v2f o;

	o.position = mul(UNITY_MATRIX_MVP, i.vertex);
		

	// - - - AMBIENT - - - 
	
	o.color = UNITY_LIGHTMODEL_AMBIENT * _Color;
	
	
	// - - - DIFFUSE - - - 
	
	float3 worldN = mul((float3x3)_Object2World, i.normal);

	float LdotN = saturate(  dot ( _WorldSpaceLightPos0, worldN )  );
	
	o.color += LdotN * _Color * _LightColor0;

	
	// - - - SPECULAR - - - 
	float3 L = normalize( ObjSpaceLightDir( i.vertex ) );
	float3 V = normalize( ObjSpaceViewDir( i.vertex ) );
	float3 H = normalize( L + V );
	
	float NdotH = saturate(  dot (i.normal, H)  );
	
	float4 spec = _Specular * _LightColor0 * pow( NdotH, _Shininess * 128 );
	
	o.color += spec;	
	
	// - - - GLOW - - - 
	
	float4 glow = _Glow * LdotN * _LightColor0;
	o.color += glow;
	
	// - - - - - 
	
	return o;
}

float4 fragment_shader( v2f i ) : COLOR
{
	return i.color;
}
ENDCG

// - - - - - - - 

		}
	} 
}

Out of curiosity: is the line “Lighting On” needed? Does it work with more than one pixel light?