Parallax mapping by marching

I had this idea thanks to the existance of raymarching. Currently figuring out self shadowing. Last half hour was messing with defines and such to get it to work on PS2.0, which it does now although I had to strip out the _Color and _SpecularColor in that version (althouh the properties are still defined the multiplications simply aren’t done).

Also seeing if maybe I need to make a PS3.0 version with loops instead of this hideous stack of if-checks. I just didn’t want to struggle with compiling so this was easier right now.

Shader "Custom/ParallaxMarching" 
{
	Properties 
	{
		_MainTex ("Base (RGBA)", 2D) = "white" {}
		_Color ("Color (RGBA)", Color) = (1,1,1,1)
		_NormalMap ("Tangent normals", 2D) = "bump" {}
		_HeightMap ("Height map (R)", 2D) = "white" {}
		_Intensity ("Intensity", Float) = 0.001
		
		_SpecularPower ("Specular power", Float) = 100
		_SpecularFresnel ("Specular fresnel falloff", Float) = 4
		_SpecularTex ("Specular texture (RGB)", 2D) = "white" {}
		_SpecularColor ("Specular color (RGB)", Color) = (1,1,1,1)
	}
	
	CGINCLUDE
	//only 4 steps in shader program 2.0
	//10 steps is max & prettiest, 2 steps is min
	#define PARALLAX_STEPS 4
	#define INTENSITYSCALE 5/PARALLAX_STEPS
	//#define SPECULAR_FRESNEL
	#define OPTIMIZE_PS20
	
	uniform sampler2D _MainTex;
	uniform half4 _Color;
	uniform sampler2D _NormalMap;
	uniform sampler2D _HeightMap;
	uniform half _Intensity;
	uniform half _SpecularPower;
	uniform half _SpecularFresnel;
	uniform sampler2D _SpecularTex;
	uniform half4 _SpecularColor;
	
	#include "BaseFunctions.cginc"
	
	half4 frag_parallax(v2f i) : COLOR
	{
		//get some normalized vectors
		half3 worldBiTangent = cross(i.worldTangent, i.worldNormal);
		half3 cameraDirection = normalize(i.worldPosition - _WorldSpaceCameraPos);
		
		//determine what the tangent space step is from this view angle
		half2 uvstep = half2( dot( cameraDirection, i.worldTangent ),
		  dot( cameraDirection, worldBiTangent ) ) * _Intensity;
		uvstep *= INTENSITYSCALE;
		
		//iteratively sample until a point is hit
		half2 uv = i.uv;
		
		#if PARALLAX_STEPS > 1
		half mapDepth0 = 1-tex2D(_HeightMap, uv).r;
		half mapDepth1 = 1-tex2D(_HeightMap, uv + uvstep).r;
		#endif
		#if PARALLAX_STEPS > 2
		half mapDepth2 = 1-tex2D(_HeightMap, uv + uvstep*2).r;
		#endif
		#if PARALLAX_STEPS > 3
		half mapDepth3 = 1-tex2D(_HeightMap, uv + uvstep*3).r;
		#endif
		#if PARALLAX_STEPS > 4
		half mapDepth4 = 1-tex2D(_HeightMap, uv + uvstep*4).r; 
		#endif
		#if PARALLAX_STEPS > 5
		half mapDepth5 = 1-tex2D(_HeightMap, uv + uvstep*5).r;
		#endif
		#if PARALLAX_STEPS > 6
		half mapDepth6 = 1-tex2D(_HeightMap, uv + uvstep*6).r;
		#endif
		#if PARALLAX_STEPS > 7
		half mapDepth7 = 1-tex2D(_HeightMap, uv + uvstep*7).r;
		#endif
		#if PARALLAX_STEPS > 8
		half mapDepth8 = 1-tex2D(_HeightMap, uv + uvstep*8).r;
		#endif
		#if PARALLAX_STEPS > 9
		half mapDepth9 = 1-tex2D(_HeightMap, uv + uvstep*9).r;
		#endif
		
		#if defined(STEPS_10)
		half depthStep = 0.1;
		#else
		half depthStep = 0.2;
		#endif
		
		#if PARALLAX_STEPS > 1
		if( mapDepth0 > 0 && mapDepth1 > depthStep )
		{
			uv = uv + uvstep;
			#if PARALLAX_STEPS > 2
			if( mapDepth2 > depthStep*2 )
			{
				uv = uv + uvstep*2;
				#if PARALLAX_STEPS > 3
				if( mapDepth3 > depthStep*3 )
				{
					uv = uv + uvstep*3; 
					#if PARALLAX_STEPS > 4
					if( mapDepth4 > depthStep*4 )
					{
						uv = uv + uvstep*4;
						#if PARALLAX_STEPS > 5
						if( mapDepth5 > depthStep*5 )
						{
							uv = uv + uvstep*5;
							#if PARALLAX_STEPS > 6
							if( mapDepth6 > depthStep*6 )
							{
								uv = uv + uvstep*6;
								#if PARALLAX_STEPS > 7
								if( mapDepth7 > depthStep*7 )
								{
									uv = uv + uvstep*7;
									#if PARALLAX_STEPS > 8
									if( mapDepth8 > depthStep*8 )
									{
										uv = uv + uvstep*8;
										
										#if PARALLAX_STEPS > 9
										if( mapDepth9 > depthStep*9 )
										{
											uv = uv + uvstep*9;
										}
										#endif
									}
									#endif
								}
								#endif
							}
							#endif
						}
						#endif
					} 
					#endif
				}
				#endif
			}
			#endif
		}
		#endif
		
		//apply normal mapping
		half3 N = half3(tex2D(_NormalMap, i.uv).ra*2.0-1.0, 1.0);
		N = normalize( mul( N, float3x3(i.worldTangent, worldBiTangent, i.worldNormal) ) );
		
		//implement some lighting
		half3 L = _WorldSpaceLightPos0.xyz;
		half atten = 1.0;
		#ifndef OPTIMIZE_PS20
		if( _WorldSpaceLightPos0.w == 1 )
		{
			L -= i.worldPosition;
		#endif
			#ifdef OPTIMIZE_PS20
		 	//multiplying the worldPosition by 0 costs less instructions
			L -= i.worldPosition * _WorldSpaceLightPos0.w;
			#endif
			//it does mean that for directional lights these calculations are all useless and slow down the shader
			half invLightDistance = 1.0 / length(L);
			L *= invLightDistance;
			atten *= invLightDistance;
		#ifndef OPTIMIZE_PS20
		}
		#endif
		half NdotL = max(0, dot(L, N));
		
		half3 R = reflect(cameraDirection, N);
		half RdotL = max(0, dot(R, L))*atten;
		RdotL = pow(RdotL, _SpecularPower);
		
		#ifdef SPECULAR_FRESNEL
		half FR = dot(cameraDirection, N);
		if( _SpecularFresnel < 0 )
			RdotL *= pow(1-FR, _SpecularFresnel);
		else
			RdotL *= pow(FR, _SpecularFresnel);
		#endif
		
		half4 outColor = NdotL * _LightColor0 * tex2D(_MainTex, uv)
#ifndef OPTIMIZE_PS20
		* _Color.xyz
#endif
;
		outColor.xyz += RdotL * _LightColor0.xyz * tex2D(_SpecularTex, uv).xyz
#ifndef OPTIMIZE_PS20
		* _SpecularColor.xyz
#endif
;
		return outColor;
	}
	ENDCG
	
	SubShader 
	{
		Tags { "RenderType"="Opaque" }
		
		Pass
		{ 
			Tags{ "LightMode" = "ForwardBase" }
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag_parallax
			#pragma target 2.0
			#pragma only_renderers d3d9 
			#pragma fragmentoption ARB_precision_hint_fastest 
			//required for lights to update
			#pragma multi_compile_fwdbase_fullshadows
			ENDCG
		}
	} 
	FallBack "Diffuse"
}

Leave a Reply

Your email address will not be published. Required fields are marked *