Problems With my Parallax Occlusion Mapping

I wanna try to create a POM shader for Unity, buthave some problems:

Shader "POM" {
	Properties {
		_Color ("Main Color", Color) = (1,1,1,1)
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_BumpMap ("Bumpmap (RGB)", 2D) = "bump" {}
		_HeightScale("Height Scale",Range (0,1)) = 0.05
		_MaxSamples("Max Samples",Range (0,128)) = 50
		_MinSamples("Min Samples",Range (0,128)) = 4

	}
	SubShader {
		Pass{
			Tags {"LightMode" = "PixelOrNone"}
            Blend AppSrcAdd AppDstAdd
			Color [_PPLAmbient]
			CGPROGRAM
			// profiles arbfp1
			#pragma fragmentoption ARB_fragment_program
				
				#pragma target 3.0
				#pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"
				#include "AutoLight.cginc" 
				sampler2D _MainTex;				
				sampler2D _BumpMap;
				float _HeightScale;
				int _MinSamples;
				int _MaxSamples;
				struct v2f{
					V2F_POS_FOG;
                    float2  uv;
					float3  tangentSpaceLightDir;
					float3	viewDirT;	
					float2	ParallaxOffset;
				};
               v2f vert (appdata_tan v)
               {
                    v2f o;
                    PositionFog( v.vertex, o.pos, o.fog );
                    o.uv = TRANSFORM_UV(0);
                   
                    float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) );
					float3x3 rotation = float3x3( v.tangent.xyz, binormal, v.normal );
					
                    o.viewDirT = mul(normalize(ObjSpaceViewDir(v.vertex)),rotation);
					float3 viewDir = o.viewDirT;
					o.tangentSpaceLightDir = mul(rotation, viewDir);
					
					float2 vParallaxDirection = normalize(  o.viewDirT.xy );
					float fLength = length( o.viewDirT );
					float fParallaxLength = sqrt( fLength * fLength - o.viewDirT.z * o.viewDirT ) / o.viewDirT.z; 
                    o.ParallaxOffset = vParallaxDirection * fParallaxLength;
					o.ParallaxOffset *= _HeightScale;
					return o;
               }
			   half4 frag (v2f i) : COLOR
			   {
					appdata_tan v;

	half4 texcol = tex2D( _MainTex, i.uv );
	
	// get normal from the normal map
	half3 normal = tex2D(_BumpMap, i.uv).rgb * 2.0 - 1.0;
					
					float2 dx = ddx(i.uv);
					float2 dy = ddy(i.uv);
					int nNumSteps = (int) lerp( _MaxSamples, _MinSamples, dot( normalize(i.viewDirT), normalize(v.normal )) );
					
					float fCurrHeight = 0.0;
					   float fStepSize   = 1.0 / (float) nNumSteps;
					   float fPrevHeight = 1.0;
					   float fNextHeight = 0.0;

					   int    nStepIndex = 0;
					   bool   bCondition = true;

					   float2 vTexOffsetPerStep = fStepSize * i.ParallaxOffset;
					   float2 vTexCurrentOffset = i.uv;
					   float  fCurrentBound     = 1.0;
					   float  fParallaxAmount   = 0.0;

					   float2 pt1 = 0;
					   float2 pt2 = 0;

					   float2 texOffset2 = 0;
					   
					   while (nStepIndex < nNumSteps ) 
   {
      vTexCurrentOffset -= vTexOffsetPerStep;

      // Sample height map which in this case is stored in the alpha channel of the normal map:
      fCurrHeight = tex2D( _BumpMap, vTexCurrentOffset, dx, dy ).a;

      fCurrentBound -= fStepSize;

      if ( fCurrHeight > fCurrentBound ) 
      {     
         pt1 = float2( fCurrentBound, fCurrHeight );
         pt2 = float2( fCurrentBound + fStepSize, fPrevHeight );

         texOffset2 = vTexCurrentOffset - vTexOffsetPerStep;

         nStepIndex = nNumSteps + 1;
      }
      else
      {
         nStepIndex++;
         fPrevHeight = fCurrHeight;
      }
   }
      float fDelta2 = pt2.x - pt2.y;
   float fDelta1 = pt1.x - pt1.y;
   fParallaxAmount = (pt1.x * fDelta2 - pt2.x * fDelta1 ) / ( fDelta2 - fDelta1 );
   
   float2 vParallaxOffset = i.ParallaxOffset * (1 - fParallaxAmount );
   
   // The computed texture offset for the displaced point on the pseudo-extruded surface:
   float2 texSample = i.uv - vParallaxOffset;
   
   float2 vLightRayTS = normalize(i.tangentSpaceLightDir) * _HeightScale;
   
   half4 c = texcol; 
   return c;
			   }
			ENDCG
			
		}
		UsePass "Bumped Specular/PPL"
	} 
	FallBack "Parallax Specular", 1
}

and there are 3 errors that appear:

  1. Cg in program ‘frag’: error C3004: function “ddx” not supported in this profile at line 67

  2. Cg in program ‘frag’: error C3004: function “ddy” not supported in this profile at line 68

  3. Cg in program ‘frag’: error C3004: function “tex2D” not supported in this profile at line 94.

anybody can help me???

Unity defaults to Vertex Shader 1.1 and Pixel Shader 2.0 as its Cg compilation target. To target VS and PS 3.0, add #pragma target 3.0 to the top of your Cg block.

You can read more about compilation directives here: Unity - Manual: HLSL in Unity

I setted the #pragma target 3.0, but the problem still in the ddx and de ddy function and the tex2D ddx and ddy implementation). in CG, there´s a ddy/ddx function for it and the Tex2D supports ddx and ddy (CG haven´t Tex2DGrad):

Tex2D(sampler, i.uv,dx/the float2 ddx function returned variable/,dy/the float2 ddy function returned variable/);

after this, the while statement is not supported in shaderlab… (preety strange, huh??)

It might be having trouble determining the length of the while loop at compile time. I’m not familiar with the limits around loops.

I just worked on a shader which calculates stretching in UV space and there for I also need the ddx/ddy functions.

But i get the same error:

Cg in program ‘frag’: error C3004: function “ddx” not supported in this profile at line 36

Cg in program ‘frag’: error C3004: function “ddy” not supported in this profile at line 37

If I compile the shader for d3d9 only, I get no error message.

Hummm, this means that the CG version of shaderlab is OUT OF DATE. But, if exists a ddx/ddy function, maybe exists a ddx/ddy algorithm.

The derivative functions in Cg are not something you could write yourself, let alone efficiently. They use the parallelism of the graphics card to evaluate the argument and get the slope using a linear interpolation of neighbouring fragments.

I’m not sure why Unity doesn’t support the functions in OpenGL. Maybe Aras will drop in and explain.

Unity compiles Cg into ARB vertex/fragment problems. ARB programs do not support derivative instructions. Cg used in Unity is not out of date; ARB and OpenGL in general is out of date :slight_smile:

If you don’t need OpenGL, tell ShaderLab to compile the shader into D3D9 only (#pragma only_renderers d3d9, see docs).

There is a GPU Gems article on how to “emulate” derivatives using texture lookup.

Is there really no OpenGL equivalent? What are dFdx and dFdy that people tend to mention when they talk about ddx and ddy?

Thats great, I had this problem a few days ago. I will have a look at it… but I guess it’s less efficient, right!?

thanks a lot Aras, I think for an instant that Shaderlab had some problems or something else…

Just a quick note, for anyone who uses the code posted in this thread:

Line 52 should be:

float fParallaxLength = sqrt( fLength * fLength - o.viewDirT.z * o.viewDirT.z ) / o.viewDirT.z;

instead of

float fParallaxLength = sqrt( fLength * fLength - o.viewDirT.z * o.viewDirT.z ) / o.viewDirT.z;

Besides you have to change the fragment shader to a surface shader, but this shouldn’t be a problem.

kind regards,
Kevin