Interstellar

2019-03-03T15:17:54+00:00

The first project I worked on at Sticky Studios was the web and mobile game: Interstellar.

For this game I was responsible for the looks and customization of the different planets you can create in your solar system. For this I created the shader(s) and the tools for both the artists and the players. With these tools the artists could define specific ranges exposed to the player to create a wide range of unique planets from within the game.

Almost all planets share the same shader.

The game is playable at the following platforms:

Metrico

2019-03-03T15:17:27+00:00

As part of my internship at Digital Dreams, I have been working on the game: Metrico.

Metrico is a puzzle game with platforming elements that makes use of so called “input morphing”. Input morphing means that all kinds of input, ranging from the player character walking in the game to physical input from the player, will have an impact on the game world. This impact comes in two variants:

  • Environment altering that prevents the player from progressing in a linear sense so the player has to take his or her actions in consideration. This is the puzzle element of the game where the player needs to solve the puzzles by carefully considering its input.
  • Environment altering from a pure aesthetical viewpoint where the actions of the player have no direct impact on the gameplay.

The game has been released for the Playstation Vita exclusively.

During my internship I got full responsibility over the graphics programming side of the game. I was in charge of creating all the shaders that define the look of the game as well as all the visual effects that happen in the game. Next to graphics programming and visual effects, I also helped with game play programming where needed and did my share of optimizing where needed.

 

Super Puzzle Platformer

2019-03-03T15:17:04+00:00

This is a game made in a group of 7 people (2 programmers, 4 artists and 1 designer) in roughly 14 days in Unity3D as a student project. The aim of this student project is to deliver a fully working game within this given time period.

The tasks I had in this project:

  • Creating a particle manager
  • Creating an audio manager
  • In game GUI handling
  • Menu
  • Rim shader
  • Animation handles (for use in Mecanim)
  • Shark behaviour
  • Waves of blocks
  • Camera behaviour
  • Dynamic weather simulation
  • General configuration manager
  • Player feedback system
  • Tutorial

It’s based on the original Super Puzzle Platformer game, recently released on steam.

It is a platformer game in which the player tries to stay alive as long as possible, to get the highest score by destroying blocks and collecting coins.

Special Effects

2019-03-03T15:16:08+00:00

The aim for this project was to create a collection of special effects commonly found in games. The special effects varied to model shaders, post processing effects and small systems, like a basic particle system and a lens flare system. It was mandatory that all the post processing effect could work separately and combined from each other.

The effects and their implementations were made in an already existing framework as an exercise to work within existing frameworks and build needed functionality into it without breaking the existing code base.

 

Code Snippits

struct input
{
  float4 position		: POSITION;
  float2 uv			: TEXCOORD0;

  float4 copyposition : TEXCOORD1;
  float3 normsunpos : TEXCOORD2;
  float4 sunpos : TEXCOORD3;
  float4 moonpos : TEXCOORD5;
  float4 projpos : TEXCOORD4;
};

struct output 
{
  float4 color : COLOR;
};

output pixelfunction( input IN	, uniform sampler2D gradient1: TEXUNIT0
                , uniform sampler2D gradient2: TEXUNIT1
                , uniform sampler2D suntexture: TEXUNIT2
                , uniform sampler2D sungradient: TEXUNIT3
                , uniform sampler2D moontexture: TEXUNIT4
                , uniform sampler2D moonmasktexture: TEXUNIT5
                , uniform sampler2D moonhalotexture: TEXUNIT6
                , uniform sampler2D starfieldtexture: TEXUNIT7
                , uniform sampler2D cloudfieldtexture: TEXUNIT8
                , uniform float moonsunangle
                , uniform float atmosthickness
                , uniform float skyscroll
                , uniform float lightstrength
                , uniform float clouds
                , uniform float stars
                 )
{
  output OUT;

  float aspect = 1080.0f/1920.0f;	
  float2 suninscreen = IN.sunpos.xy / IN.sunpos.w;
  float2 pixinscreen = IN.projpos.xy / IN.projpos.w;
  float2 sunuv = ((suninscreen - pixinscreen) * 4) + float2(0.5,0.45 / aspect);
  sunuv.y *= aspect;

  float2 pixelheight = float2(IN.copyposition.y,0);
  float gradientsclr = dot(IN.copyposition.xyz,IN.normsunpos.xyz);
  float3 suncolor = tex2D(suntexture,sunuv) * (tex2D(sungradient,pixelheight * atmosthickness) * gradientsclr);

  float2 mooninscreen = IN.moonpos.xy / IN.moonpos.w;
  float2 moonuv = ((mooninscreen - pixinscreen) * 4) + float2(0.5,0.5 / aspect);
  moonuv.y *= aspect;

  float3 moonmaskcolor = clamp(tex2D(moonmasktexture,moonuv) - moonsunangle,0,1);
  float3 moonhalocolor = tex2D(moonhalotexture,moonuv) * clamp(1-IN.normsunpos.y,0,1);
  float3 mooncolor = tex2D(moontexture,moonuv) * moonmaskcolor * tex2D(sungradient,pixelheight);

  float2 skyuv = float2(IN.copyposition.y * atmosthickness,0);
  float3 skycolor = clamp(lerp(tex2D( gradient2, skyuv ),tex2D( gradient1, skyuv ),abs(IN.normsunpos.y * atmosthickness)) * clamp(IN.normsunpos.y * atmosthickness,0.025,1),0,1);

 	float redcorrectscalar = clamp(1.0f-dot((IN.copyposition.xyz),IN.normsunpos.xyz),0,1);
 	float3 redcorrectcolor = ((float3( 1, 0.59, 0.16 ) * 0.90f  * redcorrectscalar)) * (1-clamp(IN.normsunpos.y * atmosthickness,0.0,1)) * clamp(IN.normsunpos.y * atmosthickness,0.0,1);

 	float3 starcolor = tex2D(starfieldtexture,IN.uv + float2(skyscroll * 0.1,0) ) * clamp(abs(IN.normsunpos.y),0,1) * redcorrectscalar;
  if(IN.normsunpos.y > 0)starcolor = float3(0,0,0);
  if(stars == 0)starcolor = float3(0,0,0);

  float4 cloudcolor = tex2D(cloudfieldtexture,IN.uv + float2(skyscroll * 0.2,-skyscroll * 0.1));
  cloudcolor.rgb +=  (tex2D(sungradient,pixelheight * atmosthickness))* (1-abs(IN.normsunpos.y));
  cloudcolor.rgb *= clamp(IN.normsunpos.y,0.025,1);
  cloudcolor.a *=  clouds;

  if(IN.sunpos.z < 0)	suncolor = float3(0,0,0);
  if(IN.moonpos.z < 0) mooncolor = float3(0,0,0);

  float3 correctedcolor = (skycolor - clamp(redcorrectcolor,0,1));
  correctedcolor = clamp(correctedcolor,0,1);
  OUT.color.rgb = lerp((correctedcolor) + suncolor + mooncolor + starcolor,cloudcolor.rgb,cloudcolor.a);
  OUT.color.rgb *= lightstrength;

  return OUT;
}

 

struct input
{
  float4 position		: POSITION;		// (projected) position in screen space
  float4 pixelpos		: TEXCOORD5;		// (projected) position in screen space
  float4 posworld		: TEXCOORD6;
  float4 color		: COLOR;
  float4 pos			: TEXCOORD0;	// position in world space (not projected)
  float3 normal		: TEXCOORD1;
  float2 uv			: TEXCOORD2;
  float3 tangent		: TEXCOORD3;
  float3 binormal		: TEXCOORD4;
  float depth			: TEXCOORD7;
};

#define NEARPLANE 0.01
#define FARPLANE 1000.0

float ZfromDepth( float depth, float zNear, float zFar )
{
  // source: http://olivers.posterous.com/linear-depth-in-glsl-for-real
  // OpenGL only!
  return 2*zFar*zNear / (zFar + zNear - (zFar - zNear)*(2*depth -1));
}

struct output 
{
  float4 color : COLOR;
};
float3 expand(float3 v)
{
  return (v - 0.5) * 2;
}

float texwidth = 1.0f/512 * 10;
output pixelfunction( input IN, 
          uniform float4x4 worldCam, 
          uniform float4x4 ModelCamp, 
          uniform float4 lightPos,
          uniform float3 lightColor, 
          uniform float3 eyePosW, 
          uniform float uvoffset,
          uniform sampler2D normalmap: TEXUNIT0,
          uniform sampler2D reflectmap: TEXUNIT1,
          uniform sampler2D refractmap: TEXUNIT2,
          uniform sampler2D depthmap: TEXUNIT3
           )
{
  output OUT;

  float3 P = IN.pos.xyz;

    float3x3 tangentMatrix = float3x3( normalize( IN.tangent ), 
                                       normalize( IN.binormal ),
                                       normalize( IN.normal.xyz ) );

    float2 screenPosition = IN.pixelpos.xy / IN.pixelpos.w; 
    screenPosition = (screenPosition + 1.0)/2; 
    screenPosition.y = screenPosition.y; 

    float3 normalTex = expand(tex2D(normalmap, IN.uv).xyz);
    normalTex += expand(tex2D(normalmap, (IN.uv + float2(texwidth,0)) + uvoffset * 3.1f).xyz);
    normalTex += expand(tex2D(normalmap, (-IN.uv + float2(-texwidth,0)) + uvoffset * 5.2f).xyz);
    normalTex += expand(tex2D(normalmap, (IN.uv + float2(0,texwidth)) + uvoffset * 1.3f).xyz);
    normalTex += expand(tex2D(normalmap, (-IN.uv + float2(0,-texwidth)) + uvoffset * 4.8f).xyz);
    float3 N = normalize( (normalTex) );
    float3 refractTex = tex2D(refractmap, (screenPosition) - (N.xy * 0.0125f / IN.posworld.z)).xyz;
    float3 reflectTex = tex2D(reflectmap, (screenPosition) - (N.xy * 0.025f / IN.posworld.z)).xyz;

    float waterZdepth = IN.depth;
    float refractZdepth = ZfromDepth(tex2D(depthmap, (screenPosition)).xyz,NEARPLANE,FARPLANE);
    float distance = clamp(1/((refractZdepth - waterZdepth)),0,1);

    N = normalize(mul( N, tangentMatrix )); 	
    float3 L = normalize( lightPos - IN.pos.xyz );
    float3 V = normalize( eyePosW - IN.pos.xyz );
    float3 H = normalize ( L + V );

    float fresnel = dot(V,N);

    float specular = 0;
    float diffuse = saturate(dot(N, L));
    float remappedLambert = diffuse * 0.3 + 0.7;
    if ( diffuse > 0 )
    {
        specular = pow(saturate(dot(N, H)), 100);
    }     

    float4 diffuseColor = float4(0.4,1,1,1);
    float4 specularColor = float4(1,1,1,1) * specular;
    float3 deepColor = float3(0.2784,0.3961,1);

    float3 turqoise = float3(64.0f  / 255.0f, 224.0f  / 255.0f, 208.0f  / 255.0f);
    float3 reflectcolor = clamp(reflectTex * diffuseColor,0,1);
    float3 refractcolor = lerp(deepColor,refractTex * turqoise,distance);

  OUT.color.xyz = lerp(reflectcolor,refractcolor,clamp(fresnel,0,1)) + (specularColor * lightColor);
  return OUT;
}

 

struct input
{
  float4 position		: POSITION;
  float2 uv			: TEXCOORD0;
};

struct output 
{
  float4 color : COLOR;
};
#define NEARPLANE 0.01
#define FARPLANE 1000.0

#define NUM_DOF_SAMPLES 64

float ZfromDepth( float depth, float zNear, float zFar )
{
  // source: http://olivers.posterous.com/linear-depth-in-glsl-for-real
  // OpenGL only!
  return 2*zFar*zNear / (zFar + zNear - (zFar - zNear)*(2*depth -1));
}

float CalculateBlurriness(float curpixelz, float minrange, float maxrange)
{
  return (clamp( (curpixelz  - minrange) / (maxrange - minrange), 0, 1 ) * 2) - 1;
}

output pixelfunction( input IN, uniform sampler2D colortexture: TEXUNIT0, 
                uniform sampler2D depthtexture: TEXUNIT1,
                uniform float2 samples[NUM_DOF_SAMPLES],
                uniform float minrange,
                uniform float maxrange,
                uniform float blursize
                )
{
  output OUT;

  float curpixeldepth = tex2D(depthtexture, IN.uv);
  float curpixelz = abs(ZfromDepth(curpixeldepth,NEARPLANE, FARPLANE));

  float blurriness = CalculateBlurriness(curpixelz,minrange,maxrange);

  float totalweight = 1.0f;
  float3 finalColor = tex2D(colortexture, IN.uv).rgb; 

  int numsamples = NUM_DOF_SAMPLES;

  for(int i = 0; i < numsamples; i++)
  {
    float2 sampleuv = IN.uv + samples[i] * abs(blurriness) * blursize;

    float4 samplecolor = tex2D(colortexture, sampleuv);
    float sampledepth = tex2D(depthtexture, sampleuv);

    float samplez = abs(ZfromDepth(sampledepth, NEARPLANE,FARPLANE));
    float sampleblurriness = CalculateBlurriness(samplez,minrange,maxrange);

    float sampleweight;
    sampleweight = (sampledepth < curpixelz) ? abs(sampleblurriness) : 1.0;

    totalweight += sampleweight;	
    finalColor += samplecolor * sampleweight;
  }

  OUT.color.rgb = (finalColor.rgb / totalweight);	
  return OUT;
}