Jump to content

  • Log In with Google      Sign In   
  • Create Account

Awesome job so far everyone! Please give us your feedback on how our article efforts are going. We still need more finished articles for our May contest theme: Remake the Classics

#ActualBummel

Posted 17 March 2012 - 08:26 AM

Take a look at this shader. It is designed to work with 3DGS:

bool AUTORELOAD;

float4x4 matWorldViewProj;
float4x4 matWorld;
float4x4 matWorldInv;
float4x4 matWorldView;
float4x4 matView;
float4x4 matViewInv;

float4 vecSunDir;		
float4 vecViewPos;		
float4 vecTime;		

float4 vecSkill41;

texture entSkin1;
//texture entSkin2;

texture mtlSkin1;
texture mtlSkin2;
texture mtlSkin3;
texture mtlSkin4;

sampler AmbOccMapSampler = sampler_state
{
	Texture   = <entSkin1>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;  
	AddressU  = clamp;
	AddressV  = clamp;
};

sampler ColorMapSampler = sampler_state
{
	Texture   = <mtlSkin1>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;  
	AddressU  = wrap;
	AddressV  = wrap;
};

sampler BumpMapSampler = sampler_state
{
	Texture   = <mtlSkin2>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;  
	AddressU  = wrap;
	AddressV  = wrap;
};

sampler DepthMapSampler = sampler_state
{
	Texture   = <mtlSkin3>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;  
	AddressU  = wrap;
	AddressV  = wrap;
};

sampler DetailMapSampler = sampler_state
{
	Texture   = <mtlSkin4>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;  
	AddressU  = wrap;
	AddressV  = wrap;
};

void DiffuseVS
(		
in float4 vPos	   	   : POSITION,
in float2 iTex			: TEXCOORD0,
in float3 normal   	   : NORMAL,

out float3 outnormal	 : TEXCOORD0,
out float3 oPos			: TEXCOORD1,
out float3 View			: TEXCOORD2,
out float3 OS_View		 : TEXCOORD5,
out float2 oTex			: TEXCOORD3,

out float4 Pos   		  : POSITION
)
{
	normal=mul(normal,matWorld);
	vPos=mul(vPos,matWorld);
	
	oPos=vPos;
	oTex=iTex;
	
	outnormal=normal;
	
	Pos = mul(vPos,matWorldViewProj);
	
	float3 wPos=mul(vPos,matWorld);
	
	View=normalize(vecViewPos-wPos);
	
	OS_View=normalize(mul(vecViewPos,matWorldInv)-vPos);	
}

///////////////////

float3 sample_ColorProj(sampler2D ColorSampler, float3 BlendF,
float2 Tex1, float2 Tex2, float2 Tex3)
{
	float3 Col1=tex2D(ColorSampler,Tex1.xy);
	float3 Col2=tex2D(ColorSampler,Tex2.xy);
	float3 Col3=tex2D(ColorSampler,Tex3.xy);

	return Col1*BlendF.x + Col2*BlendF.y + Col3*BlendF.z;
}

float3 sample_DepthProj(sampler2D DepthSampler, float3 BlendF,
float2 Tex1, float2 Tex2, float2 Tex3)
{
	float3 D;
	D.x=tex2Dlod(DepthSampler,float4(Tex1.xy,0,0)).x;
	D.y=tex2Dlod(DepthSampler,float4(Tex2.xy,0,0)).x;
	D.z=tex2Dlod(DepthSampler,float4(Tex3.xy,0,0)).x;

	return dot(D,BlendF);
}

float3 sample_NormalProj(sampler2D NormalSampler, float3 BlendF,
float2 Tex1, float2 Tex2, float2 Tex3, float3x3 matTangent13, float3x3 matTangent2)
{
	float3 NM1=tex2D(NormalSampler,Tex1.xy)*2.f-1.f;
	float3 NM2=tex2D(NormalSampler,Tex2.xy)*2.f-1.f;
	float3 NM3=tex2D(NormalSampler,Tex3.xy)*2.f-1.f;
	
	NM1=mul(NM1,matTangent13);
	NM2=mul(NM2,matTangent2);
	NM3=mul(NM3,matTangent13);
	
	return normalize(NM1*BlendF.x + NM2*BlendF.y + NM3*BlendF.z);
}

float3 sample_NormalProj_simple(sampler2D NormalSampler, float3 BlendF,
float2 Tex1, float2 Tex2, float2 Tex3, float3 Normal, float3 ds, float BumpWeight)
{
	float3 NM1=tex2D(NormalSampler,Tex1.xy)*2.f-1.f;
	float3 NM2=tex2D(NormalSampler,Tex2.xy)*2.f-1.f;
	float3 NM3=tex2D(NormalSampler,Tex3.xy)*2.f-1.f;
	
	NM1=float3(0,-NM1.y,NM1.x*ds.x);
	NM2=float3(NM2.x,0,-NM2.y*ds.y);
	NM3=float3(-NM3.x*ds.z,-NM3.y,0);
	
	return normalize(Normal+(NM1*BlendF.x + NM2*BlendF.y + NM3*BlendF.z)*BumpWeight);
}

float IntersectionTest(
in float3 BlendF,
in float2 Tex1, in float2 Off1,
in float2 Tex2, in float2 Off2,
in float2 Tex3, in float2 Off3,
in float Bias)
{
	const int linear_search_steps=15;

	float depth_step=1.0/linear_search_steps;

	float size=depth_step; // current size of search window

	float depth=-Bias; // current depth position
	float lastHeight=0;
	float height=0;
	
	// search from front to back for first point inside the object
	for(int i=0;i<linear_search_steps-1;i++)
	{
		height=sample_DepthProj(DepthMapSampler, BlendF, Tex1+Off1*depth, Tex2+Off2*depth, Tex3+Off3*depth);
		
		if (depth < height-Bias)
		{
			depth+=size;
			lastHeight = height;
		}
		
	}
	
	//	#define USE_BINARY_SEARCH
	#ifdef USE_BINARY_SEARCH
		const int binary_search_steps=7;
		
		//search around first point (depth) for closest match
		for ( int i=0; i<binary_search_steps;i++)
		{		
			size*=0.5f;		
			height=sample_DepthProj(DepthMapSampler, BlendF, Tex1+Off1*depth, Tex2+Off2*depth, Tex3+Off3*depth);
			
			if (depth<height-Bias)
			depth += (2*size);
			
			depth -= size;			
		}
		#else
		depth+=Bias;
		depth = ( depth - size * ( 1 - ( (depth-size-lastHeight) / (height-size-lastHeight) ) ) );
		depth-=Bias;
	#endif

	return depth;

}

float4 DiffusePS
(
in float3 normal	  : TEXCOORD0,
in float3 Pos		  : TEXCOORD1,
in float3 View	  : TEXCOORD2,
in float3 OS_View  : TEXCOORD5,

in float2 Tex	  : TEXCOORD3
) : COLOR
{
	float4 c;
	normal=normalize(normal);
	View=normalize(View);
	OS_View=normalize(OS_View);
	
	float s=80;
	Pos.xyz/=s;
	
	///////////////////////////////////////////////////////
	//===================================================//
	float3 ds=sign(normal);
	
	float2 Tex1=Pos.zy*float2(ds.x,-1.f);
	float2 Tex2=Pos.xz*float2(1.f,-ds.y);
	float2 Tex3=Pos.xy*float2(-ds.z,-1.f);
	
	#define USE_REALTANGENTBASE //usually not required, but if you project on a sphere you can see the difference clearly
	
	#ifdef USE_REALTANGENTBASE
		float3x3 matTangent13;
		matTangent13[0]=cross(normal,float3(0.f,1.f,0.f));
		matTangent13[1]=cross(normal,matTangent13[0]);
		matTangent13[2]=normal;
		
		float3x3 matTangent2;
		matTangent2[0]=cross(normal,float3(0.f,0.f,1.f));
		matTangent2[1]=cross(normal,matTangent2[0]);
		matTangent2[2]=normal;
		
		matTangent2[0]*=ds.y;
		matTangent2[1]*=ds.y;
	#endif
	//===================================================//
	///////////////////////////////////////////////////////
	
	float3 BlendF=pow(abs(normal), 60.f);	
	BlendF/=dot(BlendF.xyz,1.f);

	///////////////////////////////////////////////////////
	//===================================================//
	
	const float off_scale=0.08f;
	
	float3 ProjView=cross(cross(OS_View,normal),normal)*off_scale;
	
	float2 offset_vec1=ProjView.zy*float2(ds.x,-1.f);
	float2 offset_vec2=ProjView.xz*float2(1,-ds.y);
	float2 offset_vec3=ProjView.xy*float2(-ds.z,-1.f);
	
	//-----//dont use the following to create the offset_vecs, it´s slower and less consistent:
	//	offset_vec1 =-mul(matTangent13,OS_View).xy*off_scale;
	//	offset_vec2 =-mul(matTangent2 ,OS_View).xy*off_scale;
	//	offset_vec3=offset_vec1;
	//-----//
	
	float offset_length = IntersectionTest(BlendF,
	Tex1.xy, offset_vec1,
	Tex2.xy, offset_vec2,
	Tex3.xy, offset_vec3,1.f);
	
	Tex1.xy+=offset_vec1*offset_length;
	Tex2.xy+=offset_vec2*offset_length;
	Tex3.xy+=offset_vec3*offset_length;
	
	//===================================================//
	///////////////////////////////////////////////////////
	
	float3 Albedo=sample_ColorProj(ColorMapSampler,BlendF,Tex1,Tex2,Tex3);
	
	#ifdef USE_REALTANGENTBASE
		float3 Bump=sample_NormalProj(BumpMapSampler,BlendF,Tex1,Tex2,Tex3,matTangent13,matTangent2);
		#else
		float3 Bump=sample_NormalProj_simple(BumpMapSampler,BlendF,Tex1,Tex2,Tex3,normal,ds,1.f);
	#endif
	
	Bump=mul(Bump,matWorld);
	normal=mul(normal,matWorld);
	
	///////////////////////////////////////////////////////add some interesting lighting for visualisation:
	//===================================================//
	
	float AmbOcc=tex2D(AmbOccMapSampler,Tex).r;
	
	float SunLight_Bump=dot(vecSunDir,Bump);
	float SunLight_Smooth=dot(vecSunDir,normal);
	
	float SunLight_Bump_BackBleed=SunLight_Bump*0.5f+0.5f;
	float SunLight_Bump_BackBleed2=SunLight_Bump*0.2f+0.8f;
	float SunLight_Smooth_BackBleed=SunLight_Smooth*0.5f+0.5f;
	
	SunLight_Bump=saturate(SunLight_Bump);
	SunLight_Smooth=saturate(SunLight_Smooth);
	
	float SunLight_Amount=min(1.f, SunLight_Bump+SunLight_Smooth);
	
	float SunLight_Back=SunLight_Bump_BackBleed*SunLight_Smooth_BackBleed;
	SunLight_Back=lerp(SunLight_Bump_BackBleed2*0.05f,1.f,SunLight_Back);
	
	c.rgb=Albedo*AmbOcc*lerp(SunLight_Back,SunLight_Bump,SunLight_Amount);

	c.a=1;
	
	//===================================================//
	///////////////////////////////////////////////////////
	
	//	c.rg=offset_vec1*BlendF.x+offset_vec2*BlendF.y+offset_vec3*BlendF.z;
	//	c.rg=normalize(c.rg)*0.5+0.5;
	//	c.b=0;
	
	//	c.rgb=Bump;
	
	return c;
}

technique DiffuseTechnique
{
	pass P0
	{
		VertexShader = compile vs_3_0 DiffuseVS();
		PixelShader  = compile ps_3_0 DiffusePS();
	}
}

As you can see the shader also shows how parallax-occlusion mapping can work together with triplanar-projection.
If anything is unclear, just ask.

#1Bummel

Posted 17 March 2012 - 08:25 AM

Take a look at this shader. It is designed to work with 3DGS:

bool AUTORELOAD;

float4x4 matWorldViewProj;
float4x4 matWorld;
float4x4 matWorldInv;
float4x4 matWorldView;
float4x4 matView;
float4x4 matViewInv;

float4 vecSunDir;		
float4 vecViewPos;		
float4 vecTime;		

float4 vecSkill41;

texture entSkin1;
//texture entSkin2;

texture mtlSkin1;
texture mtlSkin2;
texture mtlSkin3;
texture mtlSkin4;

sampler AmbOccMapSampler = sampler_state
{
	Texture   = <entSkin1>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;  
	AddressU  = clamp;
	AddressV  = clamp;
};

sampler ColorMapSampler = sampler_state
{
	Texture   = <mtlSkin1>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;  
	AddressU  = wrap;
	AddressV  = wrap;
};

sampler BumpMapSampler = sampler_state
{
	Texture   = <mtlSkin2>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;  
	AddressU  = wrap;
	AddressV  = wrap;
};

sampler DepthMapSampler = sampler_state
{
	Texture   = <mtlSkin3>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;  
	AddressU  = wrap;
	AddressV  = wrap;
};

sampler DetailMapSampler = sampler_state
{
	Texture   = <mtlSkin4>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;  
	AddressU  = wrap;
	AddressV  = wrap;
};

void DiffuseVS
(		
in float4 vPos	   	   : POSITION,
in float2 iTex			: TEXCOORD0,
in float3 normal   	   : NORMAL,

out float3 outnormal	 : TEXCOORD0,
out float3 oPos			: TEXCOORD1,
out float3 View			: TEXCOORD2,
out float3 OS_View		 : TEXCOORD5,
out float2 oTex			: TEXCOORD3,

out float4 Pos   		  : POSITION
)
{
	normal=mul(normal,matWorld);
	vPos=mul(vPos,matWorld);
	
	oPos=vPos;
	oTex=iTex;
	
	outnormal=normal;
	
	Pos = mul(vPos,matWorldViewProj);
	
	float3 wPos=mul(vPos,matWorld);
	
	View=normalize(vecViewPos-wPos);
	
	OS_View=normalize(mul(vecViewPos,matWorldInv)-vPos);	
}

///////////////////

float3 sample_ColorProj(sampler2D ColorSampler, float3 BlendF,
float2 Tex1, float2 Tex2, float2 Tex3)
{
	float3 Col1=tex2D(ColorSampler,Tex1.xy);
	float3 Col2=tex2D(ColorSampler,Tex2.xy);
	float3 Col3=tex2D(ColorSampler,Tex3.xy);

	return Col1*BlendF.x + Col2*BlendF.y + Col3*BlendF.z;
}

float3 sample_DepthProj(sampler2D DepthSampler, float3 BlendF,
float2 Tex1, float2 Tex2, float2 Tex3)
{
	float3 D;
	D.x=tex2Dlod(DepthSampler,float4(Tex1.xy,0,0)).x;
	D.y=tex2Dlod(DepthSampler,float4(Tex2.xy,0,0)).x;
	D.z=tex2Dlod(DepthSampler,float4(Tex3.xy,0,0)).x;

	return dot(D,BlendF);
}

float3 sample_NormalProj(sampler2D NormalSampler, float3 BlendF,
float2 Tex1, float2 Tex2, float2 Tex3, float3x3 matTangent13, float3x3 matTangent2)
{
	float3 NM1=tex2D(NormalSampler,Tex1.xy)*2.f-1.f;
	float3 NM2=tex2D(NormalSampler,Tex2.xy)*2.f-1.f;
	float3 NM3=tex2D(NormalSampler,Tex3.xy)*2.f-1.f;
	
	NM1=mul(NM1,matTangent13);
	NM2=mul(NM2,matTangent2);
	NM3=mul(NM3,matTangent13);
	
	return normalize(NM1*BlendF.x + NM2*BlendF.y + NM3*BlendF.z);
}

float3 sample_NormalProj_simple(sampler2D NormalSampler, float3 BlendF,
float2 Tex1, float2 Tex2, float2 Tex3, float3 Normal, float3 ds, float BumpWeight)
{
	float3 NM1=tex2D(NormalSampler,Tex1.xy)*2.f-1.f;
	float3 NM2=tex2D(NormalSampler,Tex2.xy)*2.f-1.f;
	float3 NM3=tex2D(NormalSampler,Tex3.xy)*2.f-1.f;
	
	NM1=float3(0,-NM1.y,NM1.x*ds.x);
	NM2=float3(NM2.x,0,-NM2.y*ds.y);
	NM3=float3(-NM3.x*ds.z,-NM3.y,0);
	
	return normalize(Normal+(NM1*BlendF.x + NM2*BlendF.y + NM3*BlendF.z)*BumpWeight);
}

float IntersectionTest(
in float3 BlendF,
in float2 Tex1, in float2 Off1,
in float2 Tex2, in float2 Off2,
in float2 Tex3, in float2 Off3,
in float Bias)
{
	const int linear_search_steps=15;

	float depth_step=1.0/linear_search_steps;

	float size=depth_step; // current size of search window

	float depth=-Bias; // current depth position
	float lastHeight=0;
	float height=0;
	
	// search from front to back for first point inside the object
	for(int i=0;i<linear_search_steps-1;i++)
	{
		height=sample_DepthProj(DepthMapSampler, BlendF, Tex1+Off1*depth, Tex2+Off2*depth, Tex3+Off3*depth);
		
		if (depth < height-Bias)
		{
			depth+=size;
			lastHeight = height;
		}
		
	}
	
	//	#define USE_BINARY_SEARCH
	#ifdef USE_BINARY_SEARCH
		const int binary_search_steps=7;
		
		//search around first point (depth) for closest match
		for ( int i=0; i<binary_search_steps;i++)
		{		
			size*=0.5f;		
			height=sample_DepthProj(DepthMapSampler, BlendF, Tex1+Off1*depth, Tex2+Off2*depth, Tex3+Off3*depth);
			
			if (depth<height-Bias)
			depth += (2*size);
			
			depth -= size;			
		}
		#else
		depth+=Bias;
		depth = ( depth - size * ( 1 - ( (depth-size-lastHeight) / (height-size-lastHeight) ) ) );
		depth-=Bias;
	#endif

	return depth;

}

float4 DiffusePS
(
in float3 normal	  : TEXCOORD0,
in float3 Pos		  : TEXCOORD1,
in float3 View	  : TEXCOORD2,
in float3 OS_View  : TEXCOORD5,

in float2 Tex	  : TEXCOORD3
) : COLOR
{
	float4 c;
	normal=normalize(normal);
	View=normalize(View);
	OS_View=normalize(OS_View);
	
	float s=80;
	Pos.xyz/=s;
	
	///////////////////////////////////////////////////////
	//===================================================//
	float3 ds=sign(normal);
	
	float2 Tex1=Pos.zy*float2(ds.x,-1.f);
	float2 Tex2=Pos.xz*float2(1.f,-ds.y);
	float2 Tex3=Pos.xy*float2(-ds.z,-1.f);
	
	#define USE_REALTANGENTBASE //usually not required, but if you project on a sphere you can the the difference clearly
	
	#ifdef USE_REALTANGENTBASE
		float3x3 matTangent13;
		matTangent13[0]=cross(normal,float3(0.f,1.f,0.f));
		matTangent13[1]=cross(normal,matTangent13[0]);
		matTangent13[2]=normal;
		
		float3x3 matTangent2;
		matTangent2[0]=cross(normal,float3(0.f,0.f,1.f));
		matTangent2[1]=cross(normal,matTangent2[0]);
		matTangent2[2]=normal;
		
		matTangent2[0]*=ds.y;
		matTangent2[1]*=ds.y;
	#endif
	//===================================================//
	///////////////////////////////////////////////////////
	
	float3 BlendF=pow(abs(normal), 60.f);	
	BlendF/=dot(BlendF.xyz,1.f);

	///////////////////////////////////////////////////////
	//===================================================//
	
	const float off_scale=0.08f;
	
	float3 ProjView=cross(cross(OS_View,normal),normal)*off_scale;
	
	float2 offset_vec1=ProjView.zy*float2(ds.x,-1.f);
	float2 offset_vec2=ProjView.xz*float2(1,-ds.y);
	float2 offset_vec3=ProjView.xy*float2(-ds.z,-1.f);
	
	//-----//dont use the following to create the offset_vecs, it´s slower and less consistent:
	//	offset_vec1 =-mul(matTangent13,OS_View).xy*off_scale;
	//	offset_vec2 =-mul(matTangent2 ,OS_View).xy*off_scale;
	//	offset_vec3=offset_vec1;
	//-----//
	
	float offset_length = IntersectionTest(BlendF,
	Tex1.xy, offset_vec1,
	Tex2.xy, offset_vec2,
	Tex3.xy, offset_vec3,1.f);
	
	Tex1.xy+=offset_vec1*offset_length;
	Tex2.xy+=offset_vec2*offset_length;
	Tex3.xy+=offset_vec3*offset_length;
	
	//===================================================//
	///////////////////////////////////////////////////////
	
	float3 Albedo=sample_ColorProj(ColorMapSampler,BlendF,Tex1,Tex2,Tex3);
	
	#ifdef USE_REALTANGENTBASE
		float3 Bump=sample_NormalProj(BumpMapSampler,BlendF,Tex1,Tex2,Tex3,matTangent13,matTangent2);
		#else
		float3 Bump=sample_NormalProj_simple(BumpMapSampler,BlendF,Tex1,Tex2,Tex3,normal,ds,1.f);
	#endif
	
	Bump=mul(Bump,matWorld);
	normal=mul(normal,matWorld);
	
	///////////////////////////////////////////////////////add some interesting lighting for visualisation:
	//===================================================//
	
	float AmbOcc=tex2D(AmbOccMapSampler,Tex).r;
	
	float SunLight_Bump=dot(vecSunDir,Bump);
	float SunLight_Smooth=dot(vecSunDir,normal);
	
	float SunLight_Bump_BackBleed=SunLight_Bump*0.5f+0.5f;
	float SunLight_Bump_BackBleed2=SunLight_Bump*0.2f+0.8f;
	float SunLight_Smooth_BackBleed=SunLight_Smooth*0.5f+0.5f;
	
	SunLight_Bump=saturate(SunLight_Bump);
	SunLight_Smooth=saturate(SunLight_Smooth);
	
	float SunLight_Amount=min(1.f, SunLight_Bump+SunLight_Smooth);
	
	float SunLight_Back=SunLight_Bump_BackBleed*SunLight_Smooth_BackBleed;
	SunLight_Back=lerp(SunLight_Bump_BackBleed2*0.05f,1.f,SunLight_Back);
	
	c.rgb=Albedo*AmbOcc*lerp(SunLight_Back,SunLight_Bump,SunLight_Amount);

	c.a=1;
	
	//===================================================//
	///////////////////////////////////////////////////////
	
	//	c.rg=offset_vec1*BlendF.x+offset_vec2*BlendF.y+offset_vec3*BlendF.z;
	//	c.rg=normalize(c.rg)*0.5+0.5;
	//	c.b=0;
	
	//	c.rgb=Bump;
	
	return c;
}

technique DiffuseTechnique
{
	pass P0
	{
		VertexShader = compile vs_3_0 DiffuseVS();
		PixelShader  = compile ps_3_0 DiffusePS();
	}
}

As you can see the shader also shows how parallax-occlusion mapping can work together with triplanar-projection.
If anything is unclear, just ask.

PARTNERS