Joshua Boggs

josh@joshuaboggs.com

LinkedIn

Resume

I'm an experienced Lead Programmer and Project Lead at Firemint - and I thrive on creating things that people enjoy.

I'm passionate about rapid prototyping, holistic game design, and refining gameplay till it's just right. I also have a knack for tweaking shaders till they're pixel perfect.

Outside of game development, I enjoy the aroma of a fresh espresso, flamenco guitar, weight training, sampling good food and wine, and adding one more stamp to my passport.



Work

SPY mouse

#1 App Store hit.
I was Lead Programmer and Project Lead throughout development, and was closely involved in the design of the game.

Little Snake

This charming little fella was created in XNA and is sporting some sweet chip tunes, and juicy visuals to make him totally irresistible.

Real Racing 2

#1 App Store hit.
I was involved in programming client and server side web services.

Pillager

Won the "Award for Best Graphics" at GCAP in 2009. Unique oil painting style I created using shaders for post-processing.

Flight Control Rocket

Spiritual Successor to Flight Control.
My work on Flight Control Rocket involved gameplay and server side programming.

// Soft Particle Shader 
// Created by: Joshua Boggs
// Alpha's out fragments based on the depth of the scene
// =============================================================

//---------------------------------------------------------------------------
// Constants
//---------------------------------------------------------------------------
sampler2D baseMap : register(s0);
sampler2D depthMap : register(s1);

static const float g_farPlane = 50000.0f;
float4x4	World;
float4x4	View;
float4x4	Proj;

static const float g_fDepthThreshold = 100.0f;

static const float2 g_fPixelStep = { 1.0f / 1024.0f, 1.0f / 768.0f };
//---------------------------------------------------------------------------
struct VS_OUTPUT
{
	float4 Pos    		: SV_POSITION;
	float4 Colour		: COLOR;
	float2 TexCoord		: TEXCOORD0;
	float Depth			: TEXCOORD1;
	float3 ProjPos		: TEXCOORD2;
};
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Vertex Shader
//---------------------------------------------------------------------------
VS_OUTPUT VSMain(float4 inPos : POSITION, float4 inColour : COLOR0, float2 inTex0 : TEXCOORD0)
{
	VS_OUTPUT Out;
	
	// Concatenate our world and view matrix into one
	float4x4 matWorldView = mul(World, View);
	
	// Transform our position into view space
	Out.Pos = mul(inPos, matWorldView);	
	
	// Set our depth into into a single float, as COLOR.a has low precision
	Out.Depth = Out.Pos.z;	
	
	// Now project our position into screen space
	Out.Pos = mul(Out.Pos, Proj);	
	
	Out.TexCoord = inTex0;
	Out.Colour = inColour;	
	Out.ProjPos.xyz = Out.Pos.xyw;// / Out.Pos.w);
		
	return Out;
}

//---------------------------------------------------------------------------
// Pixel Shader
//---------------------------------------------------------------------------
float4 PSMain(VS_OUTPUT In) : SV_TARGET
{
	float4 outColour = tex2D(baseMap, In.TexCoord) * In.Colour;
	
	// Project position into tex co-ords
	float2 projTexCoords = In.ProjPos.xy / In.ProjPos.z;
	projTexCoords = (projTexCoords * 0.5f) + 0.5f;
	projTexCoords.y = 1.0f - projTexCoords.y;
	projTexCoords += (0.5f * g_fPixelStep);
	
	// Scale our depth relative to the max distance so that it is between 0 and 1
	float particleDepth = In.Depth / g_farPlane;
	float sceneDepth = tex2D(depthMap, projTexCoords).a;
	
	// find the absolute difference between the particle and scene depth
	outColour.a = saturate((sceneDepth - particleDepth) * g_fDepthThreshold) * outColour.a;
	return outColour;
}
// =============================================================
// The oil painting shader.
// Created by: Joshua Boggs
// ------------------------------------------------------------
// Symmetric Nearest Neighbour - An Edge Preserving Smoothing Filter:
// Samples a range of symmetric pixels around the current pixel to 
// blur pixels whilst preserving edges. Giving it an oil painting look.
// =============================================================

//----------------------------------------------------
// Global Constants
//----------------------------------------------------
static const uint g_uiSampleRange = 4;

// offset by 4 to provide a bit more variance in our samples. 
// this is simply what I found to work best for a large brush look.
static const float2 g_vSampleKernel[8] = 
{ 
	-(1.0f / 1024.0f) * 4.0f, 	(1.0f / 768.0f) * 4.0f,
	(1.0f / 1024.0f) * 4.0f,   -(1.0f / 768.0f) * 4.0f,
	0.0f, 						(1.0f / 768.0f) * 4.0f,
	0.0f, 					   -(1.0f / 768.0f) * 4.0f,
	(1.0f / 1024.0f) * 4.0f, 	(1.0f / 768.0f) * 4.0f,
	-(1.0f / 1024.0f) * 4.0f,  -(1.0f / 768.0f) * 4.0f,
	-(1.0f / 1024.0f) * 4.0f,	0.0f,
	(1.0f / 1024.0f) * 4.0f, 	0.0f 
};

//----------------------------------------------------
// Texture Samplers
//----------------------------------------------------
sampler2D ScreenTexture : register(s0);		// register for screen quad

//----------------------------------------------------
// Main Pixel Shader Program
//----------------------------------------------------
float4 PSMain(float2 inTex0 : TEXCOORD0 ) : SV_TARGET
{
	inTex0 = float2( (1.0f / 1024.0f) * 0.5f,(1.0f / 768.0f) * 0.5f ) + inTex0;
	
	// Apply awesomeness.. NOW!
	float3 vAccumColour = 0.0f;
	float4 vFinalColour = 0.0f;
	float fVariance[2];
	float3 vSymmertricColours[2];
	
	// Calculate our Squared Variance
	vFinalColour = tex2D(ScreenTexture, inTex0);
	float fOriginalGray = dot(vFinalColour, 0.333f);
	
	for(int index = 0; index < g_uiSampleRange*2; index+=2)
	{
		// Grab our Symmetric Samples
		vSymmertricColours[0] = tex2D( ScreenTexture, inTex0 + g_vSampleKernel[index] );
		vSymmertricColours[1] = tex2D( ScreenTexture, inTex0 + g_vSampleKernel[index+1]);
		
		// Variance^2 = SUM[n]( (sample[n].r - mean.r)^2 + (sample[n].g - mean.g)^2 + (sample[n].b - mean.b)^2 ) * 1 / N
		fVariance[0] = dot(vSymmertricColours[0]-vFinalColour, vSymmertricColours[0]-vFinalColour);
		fVariance[1] = dot(vSymmertricColours[1]-vFinalColour, vSymmertricColours[1]-vFinalColour);
		
		if( fVariance[0] < fVariance[1] )
		{
			vAccumColour += vSymmertricColours[0];
		}
		else
		{
			vAccumColour += vSymmertricColours[1];
		}
	}
	
	vAccumColour /= g_uiSampleRange;
	
	// Pack our mean into RGB
	vFinalColour.rgb = saturate(vAccumColour);
	vFinalColour.a = 1.0f;
	return vFinalColour;
}
;----------------------------------------------------------------------------
; Hardware Skinning Shader
; Created By: Joshua Boggs
;----------------------------------------------------------------------------
vs_1_1

;----------------------------------------------------------------------------
;
; Constants specified by the app
;       c0-3 = ViewProj
;		c4-7 = InvProj
;       c8   = constants - 1.0f, 0.0f, 1.0f / farClipPlane, 1020.01f
;       c10+ = bone matrices (with bone-to-skin-to-world transform)
;
; Vertex components (as specified in the vertex DECL)
;       v0  = pVertex[i].position
;       v1  = pVertex[i].blendweights
;       v2  = pVertex[i].blendindices
;       v3  = pVertex[i].normal
;
;----------------------------------------------------------------------------

dcl_position        v0
dcl_blendweight     v1
dcl_blendindices    v2
dcl_normal          v3

;----------------------------------------------------------------------------
; Determine the last blending weight
;----------------------------------------------------------------------------
mov     r0.xyz, v1.xyz              ; r0.xyz = w0, w1, w2
dp3     r0.w, v1.xyz, c8.xxxx       ; r0.w = w0 + w1 + w2
add     r0.w, -r0.w, c8.x           ; r0.w = 1 - (w0 + w1 + w2) = w3

;----------------------------------------------------------------------------
; Decode the index - it is swizzled due to the usage of D3DCOLOR for 
;       packing the indices...
;----------------------------------------------------------------------------
mul     r1, v2.zyxw, c8.wwww        ; r1 = indices w/ offset

;----------------------------------------------------------------------------
; Transform the position and normal for each bone
; v0 = position
; v3 = normal
;----------------------------------------------------------------------------
; Get the index of the bone matrix [0]
mov     a0.x, r1.x
; Transform position and normal
m4x3    r5, v0, c[a0.x + 10]
m3x3    r6, v3, c[a0.x + 10]
; Blend them
mul     r5.xyz, r5.xyz, r0.xxx
mul     r6.xyz, r6.xyz, r0.xxx

; Get the index of the bone matrix [1]
mov     a0.x, r1.y
; Transform position and normal
m4x3    r2, v0, c[a0.x + 10]
m3x3    r3, v3, c[a0.x + 10]
; Blend them
mad     r5.xyz, r2.xyz, r0.yyy, r5.xyz
mad     r6.xyz, r3.xyz, r0.yyy, r6.xyz

; Get the index of the bone matrix [2]
mov     a0.x, r1.z
; Transform position and normal
m4x3    r2, v0, c[a0.x + 10]
m3x3    r3, v3, c[a0.x + 10]
; Blend them
mad     r5.xyz, r2.xyz, r0.zzz, r5.xyz
mad     r6.xyz, r3.xyz, r0.zzz, r6.xyz

; Get the index of the bone matrix [3]
mov     a0.x, r1.w
; Transform position and normal
m4x3    r2, v0, c[a0.x + 10]
m3x3    r3, v3, c[a0.x + 10]
; Blend them
mad     r5.xyz, r2, r0.wwww, r5
mad     r6.xyz, r3, r0.wwww, r6

;----------------------------------------------------------------------------
; Transform the resulting position into world space
;----------------------------------------------------------------------------
mov     r5.w, c8.x		; r5 = world space position
m4x4	r10, r5, c0		; r10 = clip-space position

;----------------------------------------------------------------------------
; Transform back into world-view space, and calculate depth
;----------------------------------------------------------------------------
mov		oPos, r10		; outputPos = clip-space position
m4x4	r9, r10, c4		; r9 = world-view position
mul 	r10, r9, c8.z	; r10 = depth

;----------------------------------------------------------------------------
; Normalize the normal
;----------------------------------------------------------------------------
dp3     r11.x, r6.xyz, r6.xyz       ; r11.x = length of normal
rsq     r11.xyz, r11.x              ; r11.xyz = 1/sqrt(length of normal)
mul     r6.xyz, r6.xyz, r11.xyz     ; r6 = normalized normal

;----------------------------------------------------------------------------
; Output Normals and Depth as Colour
;----------------------------------------------------------------------------
mov		oD0.xyz, r6.xyz		; Normals
mov		oD0.w, r10.z		; Depth