Making a game, adding Instagram-like Filters

So I’m working on a new game that’s a cross between Toca Mini and a vlog creator (editor..?), so I thought it would be fun to add some Instagram-like filters.

Emoji Balloon Photo by Bernard Hermant on Unsplash

For this I am using Cinema Suite Inc’s Color Grading LUTs Library, which you can get in the Unity Asset Store for free, and some old-fashioned Legacy Color Correction Lookup.

This post was supposed to be a tutorial, but sadly, Legacy Image Effects are deprecated from the Asset Store, so… yuh (but if you happen to still have it from old-fashioned Standard Assets, read on).

First, I like to put all my Manager type code in its own GameObject and then attach the script to it. So, I have CameraMgr.

Button Panel is for hiding UI stuff; Camera Image Fitter and Camera Plane is for showing your device Camera (more on that in a bit); and Color Correction Lookup is for changing filters (that script is from Legacy Image Effects) and it’s attached to your MainCamera.

Camera Plane is a Raw Image with an Aspect Ratio Fitter Script
Main Camera has a Color Correction Script (from Legacy Image Effects)
And Filter Buttons are a bunch of buttons with its own Filter (Lookup Texture or LUT) and it calls ClickFilterBtn in CameraMgr

Code Snippets Link: https://bitbucket.org/snippets/studiosremade/EbojrA

CameraMgr.cs (Yes, some of the code are from UnityAnswers)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityStandardAssets.ImageEffects;

public class CameraMgr : MonoBehaviour
{
    public GameObject m_buttonPanel;

    private bool m_isFrontFacing = false;

    private WebCamDevice m_currCamera;
    private WebCamDevice m_backCamera;
    private WebCamDevice m_frontCamera;
    
    private WebCamTexture m_cameraTexture;

    // Source: https://answers.unity.com/questions/773464/webcamtexture-correct-resolution-and-ratio.html
    public AspectRatioFitter m_cameraImageFitter;

    // Image rotation
    Vector3 m_camRotationVector = new Vector3(0f, 0f, 0f);

    // Image uvRect
    private Rect m_cameraDefaultRect = new Rect(0f, 0f, 1f, 1f);
    private Rect m_cameraFixedRect = new Rect(0f, 1f, 1f, -1f);

    // Image Parent's scale
    private Vector3 m_cameraDefaultScale = new Vector3(1f, 1f, 1f);
    private Vector3 m_cameraFixedScale = new Vector3(-1f, 1f, 1f);

    public RawImage m_cameraPlane;

    private string m_currFileName;

    public ColorCorrectionLookup m_colorCorrectionLookup;

    private void Start()
    {
        m_isFrontFacing = false;

        StartCoroutine(SetUpCamera());
    }

    // Sources:
    // https://docs.unity3d.com/ScriptReference/WebCamTexture-ctor.html
    // https://docs.unity3d.com/ScriptReference/WebCamTexture-videoRotationAngle.html
    // https://docs.unity3d.com/ScriptReference/WebCamTexture-videoVerticallyMirrored.html
    private IEnumerator SetUpCamera()
    {
        yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);

        if (!Application.HasUserAuthorization(UserAuthorization.WebCam)) yield break;

        WebCamDevice[] devices = WebCamTexture.devices;
        for (int i = 0; i < devices.Length; i++)
        {
            Debug.Log("Camera Names " + devices[i].name);

            if (devices[i].isFrontFacing)
            {   m_frontCamera = devices[i];
            }

            else
            {   m_backCamera = devices[i];
            }
        }

        m_isFrontFacing = false;

        StartCoroutine(StartCamera());
    }

    private IEnumerator StartCamera()
    {   
        // camera is flipped when testing with front facing camera in editor
        if (Application.isEditor || m_isFrontFacing)
        {   m_currCamera = m_frontCamera;
        }

        else
        {   m_currCamera = m_backCamera;
        }

        m_cameraTexture = new WebCamTexture(m_currCamera.name, 1280, 720, 30);

        // Sources: 
        // https://answers.unity.com/questions/1421387/webcamtexture-zoomed-in-on-ipad-pro.html
        // https://answers.unity.com/questions/773464/webcamtexture-correct-resolution-and-ratio.html

        m_cameraTexture.Play();

        // Display webcam
        yield return new WaitUntil(() => m_cameraTexture.width != 16 && m_cameraTexture.height != 16); // Workaround for weird bug on macOS

        m_cameraPlane.texture = m_cameraTexture;

        // Set camera filter modes for a smoother looking image
        m_cameraPlane.texture.filterMode = FilterMode.Trilinear;

        // Set AspectRatioFitter's ratio
        m_cameraImageFitter.aspectRatio = (float)m_cameraTexture.width / m_cameraTexture.height;
        m_cameraImageFitter.aspectMode = AspectRatioFitter.AspectMode.WidthControlsHeight;

        // If you want height to control width:
        /**
        m_camImageFitter.aspectRatio = (float) m_cameraTexture.height / m_cameraTexture.width;
        m_camImageFitter.aspectMode = AspectRatioFitter.AspectMode.HeightControlsWidth;
        **/

        // Rotate image to show correct orientation 
        m_camRotationVector.z = -m_cameraTexture.videoRotationAngle;
        m_cameraPlane.rectTransform.localEulerAngles = m_camRotationVector;

        // Unflip if vertically flipped
        m_cameraPlane.uvRect = m_cameraTexture.videoVerticallyMirrored ? m_cameraFixedRect : m_cameraDefaultRect;

        // Mirror front-facing camera's image horizontally to look more natural
        if (Application.isEditor)
        {   m_cameraPlane.transform.localScale = m_cameraFixedScale;
        }   else
        {   
            m_cameraPlane.transform.localScale = m_currCamera.isFrontFacing ? m_cameraFixedScale : m_cameraDefaultScale;
        }
    }

    public void ClickFlipCameraBtn()
    {
        StopCamera();

        m_isFrontFacing = !m_isFrontFacing;
        StartCoroutine(StartCamera());
    }

    public void StopCamera()
    {   m_cameraTexture.Stop();
    }

    public void ClickCameraBtn()
    {
        HideUI();

        m_currFileName = "Photo" + PlayerPrefs.GetInt("PhotoIdx", 0) + ".png";
        ScreenCapture.CaptureScreenshot(m_currFileName);

        PlayerPrefs.SetInt("PhotoIdx", PlayerPrefs.GetInt("PhotoIdx", 0) + 1);

        Invoke("ShowUI", 0.2f);
    }

    public void ClickFilterBtn(FilterBtn filterBtn)
    {
        if (m_colorCorrectionLookup != null)
        {
            if (filterBtn.m_filter != null)
            {
                m_colorCorrectionLookup.enabled = true;
                m_colorCorrectionLookup.Convert(filterBtn.m_filter, "");
            }
            else
            {
                m_colorCorrectionLookup.enabled = false;
            }
        }
    }

    private void HideUI()
    {   m_buttonPanel.SetActive(false);
    }

    private void ShowUI()
    {   m_buttonPanel.SetActive(true);
    }
}

FilterBtn.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class FilterBtn : Button
{
    public Texture2D m_filter;
    public Text m_text;
}

FilterBtnEditor.cs

using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

[CustomEditor(typeof(FilterBtn))]
public class FilterBtnEditor : UnityEditor.UI.ButtonEditor 
{
    public override void OnInspectorGUI()
    {
        FilterBtn targetFilterBtn = (FilterBtn) target;

        targetFilterBtn.m_filter = EditorGUILayout.ObjectField("Filter", ((FilterBtn) target).m_filter, typeof(Texture2D), true) as Texture2D;
        targetFilterBtn.m_text = EditorGUILayout.ObjectField("Text", ((FilterBtn)target).m_text, typeof(Text), true) as Text;


        EditorUtility.SetDirty(target);

        base.OnInspectorGUI();
    }
}

And that’s it for now.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s