Global game jam 2014 – Unity webcam part 2

Last post, I went over how to get data from the webcam and display it on a texture.

Now I’ll go over how I used some color utilities I found online to shift the color from the webcam around the color wheel. In order to move a color around the color wheel, I needed to convert the color from RGB (Red, green, blue) to HSB (Hue, saturation, brightness). I can’t remember where I found this, but below is a C# version of HSBColor that I had originally found in javascript:

public struct HSBColor
{
	public float h;
	public float s;
	public float b;
	public float a;
	
	public HSBColor(float h, float s, float b, float a)
	{
		this.h = h;
		this.s = s;
		this.b = b;
		this.a = a;
	}
	
	public HSBColor(float h, float s, float b)
	{
		this.h = h;
		this.s = s;
		this.b = b;
		this.a = 1f;
	}
	
	public HSBColor(Color col)
	{
		HSBColor temp = FromColor(col);
		h = temp.h;
		s = temp.s;
		b = temp.b;
		a = temp.a;
	}
	
	public static HSBColor FromColor(Color color)
	{
		HSBColor ret = new HSBColor(0f, 0f, 0f, color.a);
		
		float r = color.r;
		float g = color.g;
		float b = color.b;
		
		float max = Mathf.Max(r, Mathf.Max(g, b));
		
		if (max <= 0)
		{
			return ret;
		}
		
		float min = Mathf.Min(r, Mathf.Min(g, b));
		float dif = max - min;
		
		if (max > min)
		{
			if (g == max)
			{
				ret.h = (b - r) / dif * 60f + 120f;
			}
			else if (b == max)
			{
				ret.h = (r - g) / dif * 60f + 240f;
			}
			else if (b > g)
			{
				ret.h = (g - b) / dif * 60f + 360f;
			}
			else
			{
				ret.h = (g - b) / dif * 60f;
			}
			if (ret.h < 0)
			{
				ret.h = ret.h + 360f;
			}
		}
		else
		{
			ret.h = 0;
		}
		
		ret.h *= 1f / 360f;
		ret.s = (dif / max) * 1f;
		ret.b = max;
		
		return ret;
	}
	
	public static Color ToColor(HSBColor hsbColor)
	{
		float r = hsbColor.b;
		float g = hsbColor.b;
		float b = hsbColor.b;
		if (hsbColor.s != 0)
		{
			float max = hsbColor.b;
			float dif = hsbColor.b * hsbColor.s;
			float min = hsbColor.b - dif;
			
			float h = hsbColor.h * 360f;
			
			if (h < 60f)
			{
				r = max;
				g = h * dif / 60f + min;
				b = min;
			}
			else if (h < 120f)
			{
				r = -(h - 120f) * dif / 60f + min;
				g = max;
				b = min;
			}
			else if (h < 180f)
			{
				r = min;
				g = max;
				b = (h - 120f) * dif / 60f + min;
			}
			else if (h < 240f)
			{
				r = min;
				g = -(h - 240f) * dif / 60f + min;
				b = max;
			}
			else if (h < 300f)
			{
				r = (h - 240f) * dif / 60f + min;
				g = min;
				b = max;
			}
			else if (h <= 360f)
			{
				r = max;
				g = min;
				b = -(h - 360f) * dif / 60 + min;
			}
			else
			{
				r = 0;
				g = 0;
				b = 0;
			}
		}
		
		return new Color(Mathf.Clamp01(r),Mathf.Clamp01(g),Mathf.Clamp01(b),hsbColor.a);
	}
	
	public Color ToColor()
	{
		return ToColor(this);
	}
	
	public override string ToString()
	{
		return "H:" + h + " S:" + s + " B:" + b;
	}
	
	public static HSBColor Lerp(HSBColor a, HSBColor b, float t)
	{
		float h,s;
		
		//check special case black (color.b==0): interpolate neither hue nor saturation!
		//check special case grey (color.s==0): don't interpolate hue!
		if(a.b==0){
			h=b.h;
			s=b.s;
		}else if(b.b==0){
			h=a.h;
			s=a.s;
		}else{
			if(a.s==0){
				h=b.h;
			}else if(b.s==0){
				h=a.h;
			}else{
				// works around bug with LerpAngle
				float angle = Mathf.LerpAngle(a.h * 360f, b.h * 360f, t);
				while (angle < 0f)
					angle += 360f;
				while (angle > 360f)
					angle -= 360f;
				h=angle/360f;
			}
			s=Mathf.Lerp(a.s,b.s,t);
		}
		return new HSBColor(h, s, Mathf.Lerp(a.b, b.b, t), Mathf.Lerp(a.a, b.a, t));
	}
	

}

The other half of this is a set of color utilities that I found. It’s just a set of static methods which allow you to shift a color around the color wheel by a certain percentage:

public class ColorUtils {

	public static Color GetComplimentaryColor(Color rgb) {
		return GetShiftedColor (rgb, .5f);
	}

	public static Color GetShiftedColor(Color rgb, float shift){
		var hsbColor = new HSBColor (rgb);
		
		var shiftedHue = HueShift (hsbColor.h, shift);
		hsbColor.h = shiftedHue;

		return hsbColor.ToColor ();
	}

	public static Color SetValues(Color color, float? saturation, float? brightness){
		var c = color;
		if (saturation.HasValue) {
			c = SetSaturation(c, saturation.Value);
		}
		if (brightness.HasValue) {
			c = SetBrightness(c, brightness.Value);
		}
		return c;
	}

	public static Color SetSaturation(Color color, float saturation){

		var hsb = new HSBColor (color);
		hsb.s = saturation;
		return hsb.ToColor ();
	}

	public static Color SetBrightness(Color color, float brightness){
		
		var hsb = new HSBColor (color);
		hsb.b = brightness;
		return hsb.ToColor ();
	}


	static float HueShift(float h, float shiftAmount) 
	{ 
		h+=shiftAmount; 
		while (h>=360.0f) h-=360.0f; 
		while (h<0.0f) h+=360.0f; 
		return h; 
	}

}

The last part is a custom playmaker FSM action. The webcam color is set to a playmaker variable earlier in the FSM, but this script basically applies the webcam color, as well as any complimentary colors to any object in the scene which has a particular tag. The object must have particular materials attached to it called “Color1”, “Color2”, and “Color3”.

[ActionCategory("Custom")]
	public class SetColorsAction : FsmStateAction {

		[RequiredField]
		[UIHint(UIHint.Variable)]
		public FsmColor colorVariable;

		private GameObject[] _objectsToSet;

		public FsmFloat saturation;
		public FsmFloat brightness;

		[Tooltip("Repeat every frame.")]
		public bool everyFrame;


		public override void Reset()
		{
			_objectsToSet = GameObject.FindGameObjectsWithTag ("DynamicMaterials");
			base.Reset();
			colorVariable = new FsmColor{UseVariable=true};
			saturation = .3f;
			brightness = .5f;
		}
		
		public override void OnEnter()
		{
			DoSetColors ();
			if (!everyFrame) {
				Debug.Log ("test");
				
				Finish();
			}
		}

		public override void OnUpdate(){
			DoSetColors ();
		}

		public void DoSetColors(){
			foreach(var go in _objectsToSet){
				if(go.renderer.isVisible){
					foreach (var m in go.renderer.materials) {
						
						if(m.name.Contains("Color1")){
							var originalColor = m.GetColor("_Color");
							var c = ColorUtils.SetValues(colorVariable.Value, saturation.Value, brightness.Value);
							
							
						}
						
						if(m.name.Contains("Color2")){
							var c = ColorUtils.GetShiftedColor(colorVariable.Value, .33f);
							c = ColorUtils.SetValues(c, saturation.Value, brightness.Value);
							m.SetColor("_Color", c);
							
						}
						
						if(m.name.Contains("Color3")){
							var c = ColorUtils.GetShiftedColor(colorVariable.Value, .66f);
							c = ColorUtils.SetValues(c, saturation.Value, brightness.Value);
							m.SetColor("_Colour", c);
							
						}
					}
				}

			}

		}
	}

The Game Jam was a great experience, and while I’m not sure using a webcam to paint objects is really viable for a game, I ended up learning a lot in the process. Let me know in the comments if you have any questions, or any good ideas for how this sort of thing could be used in a game!

Share and Enjoy:
  • Reddit
  • del.icio.us
  • Google Bookmarks
  • Twitter
  • StumbleUpon
  • Facebook
  • Yahoo! Buzz
  • Digg

Leave a Reply

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