Crea Proyecto de Portal dimensional en UNITY con REALIDAD AUMENTADA y ARCore más SECTR.

Realizar Portales dimensionales en Realidad Aumentada ya es una realidad!. En ésta oportunidad te presentamos una muestra del potencial y alcance que puede implementar el motor de realidad aumentada ARCore en Unity.

Asset Implementados:

https://assetstore.unity.com/packages…

https://assetstore.unity.com/packages…

https://assetstore.unity.com/packages…

Link de referencia:

https://unity3d.com/es

https://developers.google.com/ar/

https://developers.google.com/ar/deve…l

Clase ARController:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GoogleARCore;

#if UNITY_EDITOR
//using input = GoogleARCore.InstantPreviewInput;
#endif
public class ARController : MonoBehaviour
{
    //Listamos la lista de planos que ARCore detecta en el frame actual
    private List m_newDetectedPlanes = new List();
    public GameObject GridPrefab;
    public GameObject Portal;
    public GameObject ARCamera;


    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        Screen.sleepTimeout = SleepTimeout.NeverSleep;
        //Verifica ARCore session status
        if(Session.Status != SessionStatus.Tracking)
        {
            return;
        }

        //La siguiente funcion llenará m_newDetectedPlanes con los planos que ARCore detecte en el frame actual
        Session.GetTrackables(m_newDetectedPlanes, TrackableQueryFilter.New);

        //Instanciamos un Grid por cada DetectedPlane en m_newDetectedPlanes
        for(int i=0; i < m_newDetectedPlanes.Count; i++)
        {
            GameObject grid = Instantiate(GridPrefab, Vector3.zero, Quaternion.identity, transform);
            //Ésta función seteará la posición del Grid y modificará los vertices del mesh adjunto
            grid.GetComponent().Initialize(m_newDetectedPlanes[i]);
        }

        //Verifica si el usuario ha tocado la pantalla
        Touch touch;
        if(Input.touchCount < 1 || (touch = Input.GetTouch(0)).phase != TouchPhase.Began)
        {
            return;
        }

        //Verificamos is el usuario tocó algun plano detectado
        TrackableHit hit;
        if(Frame.Raycast(touch.position.x,touch.position.y, TrackableHitFlags.PlaneWithinPolygon, out hit))
        {
            //Ahora ubicamos el portal arriba de los planos detectados cuando los tocamos

            //Habilitamos el portal
            Portal.SetActive(true);

            //Creamos una nueva ancla
            Anchor anchor = hit.Trackable.CreateAnchor(hit.Pose);

            //Seteamos la posición del portal a la misma de la posición de toque
            Portal.transform.position = hit.Pose.position;
            Portal.transform.rotation = hit.Pose.rotation;

            //Ubicamos el portal enfrente de la camara
            Vector3 cameraPosition = ARCamera.transform.position;

            //El portal sólo rotará en el eje Y
            cameraPosition.y = hit.Pose.position.y;

            //Rotamos el portal enfrente de la camara
            Portal.transform.LookAt(cameraPosition, Portal.transform.up);

            //ARCore se mantendrá analizando el entorno del mundo real y actualizando las anclas mientras necesitemos añadir nuestro portal a las anclas
            Portal.transform.parent = anchor.transform;
        }
    }
}

Clase GridVisualizer:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GoogleARCore;
public class GridVisualizer : MonoBehaviour
{
    private static int s_PlaneCount = 0;
    private readonly Color[] k_PlaneColors = new Color[]
    {
        new Color(1.0f, 1.0f, 1.0f),
        new Color(0.956f, 0.262f, 0.211f),
        new Color(0.913f, 0.117f, 0.388f),
        new Color(0.611f, 0.152f, 0.654f),
        new Color(0.403f, 0.227f, 0.717f),
        new Color(0.247f, 0.317f, 0.709f),
        new Color(0.129f, 0.588f, 0.952f),
        new Color(0.011f, 0.662f, 0.956f),
        new Color(0f, 0.737f, 0.831f),
        new Color(0f, 0.588f, 0.533f),
        new Color(0.298f, 0.686f, 0.313f),
        new Color(0.545f, 0.764f, 0.290f),
        new Color(0.803f, 0.862f, 0.223f),
        new Color(1.0f, 0.921f, 0.231f),
        new Color(1.0f, 0.756f, 0.027f)
    };

    private DetectedPlane m_DetectedPlane;

    // Keep previous frame's mesh polygon to avoid mesh update every frame.
    private List m_PreviousFrameMeshVertices = new List();
    private List m_MeshVertices = new List();
    private Vector3 m_PlaneCenter = new Vector3();

    private List m_MeshColors = new List();

    private List m_MeshIndices = new List();

    private Mesh m_Mesh;

    private MeshRenderer m_MeshRenderer;

    ///
    /// The Unity Awake() method.
    /// 
    public void Awake()
    {
        m_Mesh = GetComponent().mesh;
        m_MeshRenderer = GetComponent();
    }

    ///
    /// The Unity Update() method.
    /// 
    public void Update()
    {
        if (m_DetectedPlane == null)
        {
            return;
        }
        else if (m_DetectedPlane.SubsumedBy != null)
        {
            Destroy(gameObject);
            return;
        }
        else if (m_DetectedPlane.TrackingState != TrackingState.Tracking)
        {
                m_MeshRenderer.enabled = false;
                return;
        }

        m_MeshRenderer.enabled = true;

        _UpdateMeshIfNeeded();
    }

    ///
    /// Initializes the DetectedPlaneVisualizer with a DetectedPlane.
    /// 
    ///The plane to vizualize.
    public void Initialize(DetectedPlane plane)
    {
        m_DetectedPlane = plane;
        m_MeshRenderer.material.SetColor("_GridColor", k_PlaneColors[s_PlaneCount++ % k_PlaneColors.Length]);
        m_MeshRenderer.material.SetFloat("_UvRotation", Random.Range(0.0f, 360.0f));

        Update();
    }

    ///
    /// Update mesh with a list of Vector3 and plane's center position.
    /// 
    private void _UpdateMeshIfNeeded()
    {
        m_DetectedPlane.GetBoundaryPolygon(m_MeshVertices);

        if (_AreVerticesListsEqual(m_PreviousFrameMeshVertices, m_MeshVertices))
        {
            return;
        }

        m_PreviousFrameMeshVertices.Clear();
        m_PreviousFrameMeshVertices.AddRange(m_MeshVertices);

        m_PlaneCenter = m_DetectedPlane.CenterPose.position;

        Vector3 planeNormal = m_DetectedPlane.CenterPose.rotation * Vector3.up;

        m_MeshRenderer.material.SetVector("_PlaneNormal", planeNormal);

        int planePolygonCount = m_MeshVertices.Count;

        // The following code converts a polygon to a mesh with two polygons, inner
        // polygon renders with 100% opacity and fade out to outter polygon with opacity 0%, as shown below.
        // The indices shown in the diagram are used in comments below.
        // _______________     0_______________1
        // |             |      |4___________5|
        // |             |      | |         | |
        // |             | =>   | |         | |
        // |             |      | |         | |
        // |             |      |7-----------6|
        // ---------------     3---------------2
        m_MeshColors.Clear();

        // Fill transparent color to vertices 0 to 3.
        for (int i = 0; i < planePolygonCount; ++i)
        {
            m_MeshColors.Add(Color.clear);
        }

        // Feather distance 0.2 meters.
        const float featherLength = 0.2f;

        // Feather scale over the distance between plane center and vertices.
        const float featherScale = 0.2f;

        // Add vertex 4 to 7.
        for (int i = 0; i < planePolygonCount; ++i)
        {
            Vector3 v = m_MeshVertices[i];

            // Vector from plane center to current point
            Vector3 d = v - m_PlaneCenter;

            float scale = 1.0f - Mathf.Min(featherLength / d.magnitude, featherScale);
            m_MeshVertices.Add((scale * d) + m_PlaneCenter);

            m_MeshColors.Add(Color.white);
        }

        m_MeshIndices.Clear();
        int firstOuterVertex = 0;
        int firstInnerVertex = planePolygonCount;

        // Generate triangle (4, 5, 6) and (4, 6, 7).
        for (int i = 0; i < planePolygonCount - 2; ++i)
        {
            m_MeshIndices.Add(firstInnerVertex);
            m_MeshIndices.Add(firstInnerVertex + i + 1);
            m_MeshIndices.Add(firstInnerVertex + i + 2);
        }

        // Generate triangle (0, 1, 4), (4, 1, 5), (5, 1, 2), (5, 2, 6), (6, 2, 3), (6, 3, 7)
        // (7, 3, 0), (7, 0, 4)
        for (int i = 0; i < planePolygonCount; ++i)
        {
            int outerVertex1 = firstOuterVertex + i;
            int outerVertex2 = firstOuterVertex + ((i + 1) % planePolygonCount);
            int innerVertex1 = firstInnerVertex + i;
            int innerVertex2 = firstInnerVertex + ((i + 1) % planePolygonCount);

            m_MeshIndices.Add(outerVertex1);
            m_MeshIndices.Add(outerVertex2);
            m_MeshIndices.Add(innerVertex1);

            m_MeshIndices.Add(innerVertex1);
            m_MeshIndices.Add(outerVertex2);
            m_MeshIndices.Add(innerVertex2);
        }

        m_Mesh.Clear();
        m_Mesh.SetVertices(m_MeshVertices);
        m_Mesh.SetTriangles(m_MeshIndices, 0);
        m_Mesh.SetColors(m_MeshColors);
    }

    private bool _AreVerticesListsEqual(List firstList, List secondList)
    {
        if (firstList.Count != secondList.Count)
        {
            return false;
        }

        for (int i = 0; i < firstList.Count; i++)
        {
            if (firstList[i] != secondList[i])
            {
                return false;
            }
        }

        return true;
    }
}

Clase PortalManager:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

public class PortalManager : MonoBehaviour
{
    public GameObject MainCamera;
    public GameObject Sponza;
    public GameObject Scenario;
    public GameObject Zombie;
    public GameObject SPIDER;


    private Material[] SponzaMaterials;
    private Material[] ScenarioMaterials;
    private Material[] ZombieMaterials;
    private Material[] SpiderMaterials;

    private Vector3 camPositionInPortalSpace;

    // Start is called before the first frame update
    void Start()
    {
        SponzaMaterials = Sponza.GetComponent().sharedMaterials;
        ScenarioMaterials = Scenario.GetComponent().sharedMaterials;
        ZombieMaterials = Zombie.GetComponent().sharedMaterials;
        SpiderMaterials = SPIDER.GetComponent().sharedMaterials;

    }

    // Update is called once per frame
    void OnTriggerStay(Collider collider)
    {
        Debug.Log("camPositionInPortalSpace: "+camPositionInPortalSpace.z);
        Debug.Log("MainCamera.transform.position: "+MainCamera.transform.position);

        if(MainCamera.transform.position.z < 0.5f)
        {
            //Deshabilita la plantilla de prueba
            for(int i=0; i < SponzaMaterials.Length; ++i)
            {
                SponzaMaterials[i].SetInt("_StencilComp", 0);
                Debug.Log("else camera position: "+MainCamera.transform.position.z);
            }

            for(int i=0; i < ScenarioMaterials.Length; ++i)
            {
                ScenarioMaterials[i].SetInt("_StencilComp", 0);
                Debug.Log("else camera position: "+MainCamera.transform.position.z);
            }
            for(int i=0; i < ZombieMaterials.Length; ++i)
            {
                ZombieMaterials[i].SetInt("_StencilComp", 0);
                Debug.Log("else camera position: "+MainCamera.transform.position.z);
            }
            for(int i=0; i < SpiderMaterials.Length; ++i)
            {
                SpiderMaterials[i].SetInt("_StencilComp", 0);
                Debug.Log("else camera position: "+MainCamera.transform.position.z);
            }
            
        }
        else
        {
            //Habilita la plantilla de prueba
            for(int i=0; i < SponzaMaterials.Length; ++i)
            {
                SponzaMaterials[i].SetInt("_StencilComp", 3);
            }

            for(int i=0; i < ScenarioMaterials.Length; ++i)
            {
                ScenarioMaterials[i].SetInt("_StencilComp", 3);
                Debug.Log("else camera position: "+MainCamera.transform.position.z);
            }
            for(int i=0; i < ZombieMaterials.Length; ++i)
            {
                ZombieMaterials[i].SetInt("_StencilComp", 3);
                Debug.Log("else camera position: "+MainCamera.transform.position.z);
            }
            for(int i=0; i < SpiderMaterials.Length; ++i)
            {
                SpiderMaterials[i].SetInt("_StencilComp", 3);
                Debug.Log("else camera position: "+MainCamera.transform.position.z);
            }
        } 
    }

}

SponzaShader:

// (c) 2018 Guidev
// This code is licensed under MIT license (see LICENSE.txt for details)

Shader "Unlit/SponzaShader"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		[Enum(CompareFunction)] _StencilComp("Stencil Comp", int) = 3
		[Enum(CompareFunction)] _StencilComp("Stencil Comp", int) = 0
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		Pass
		{
			Stencil{
				Ref 1
				Comp [_StencilComp]
				//Comp Always
			}
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float4 normal: NORMAL;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float3 normal: TEXCOORD1;
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.normal = normalize(mul(v.normal, unity_WorldToObject));
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				float3 normal = normalize(i.normal);
				float3 lightDir = normalize(float3(0.0, 10.0, 10.0));
				float l = max(dot(lightDir, normal), 0.0);
				float4 texCol = tex2D(_MainTex, i.uv);
				fixed4 col = texCol * l + texCol * 0.4f;
				return col;
			}
			ENDCG
		}
	}
}

Portal Shader:

// (c) 2018 Guidev
// This code is licensed under MIT license (see LICENSE.txt for details)

Shader "Unlit/Portal"
{
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float4 normal: NORMAL;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float3 normal: TEXCOORD1;
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.normal = normalize(mul(v.normal, unity_WorldToObject));
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				float3 normal = normalize(i.normal);
				float3 lightDir = normalize(float3(0.0, 10.0, 10.0));
				float l = max(dot(lightDir, normal), 0.0);
				return fixed4(l, l, l, 1.0f) + fixed4(0.3, 0.3, 0.3, 1.0);
			}
			ENDCG
		}
	}
}

PortalPlane Shader:

// (c) 2018 Guidev
// This code is licensed under MIT license (see LICENSE.txt for details)

Shader "Unlit/PortalPlane"
{
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100
		Zwrite Off
		ColorMask 0

		Pass
		{
			Stencil{
				Ref 1
				Comp always
				Pass replace
			}

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
			};

			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				return fixed4(0.0, 0.0, 0.0, 0.0);
			}
			ENDCG
		}
	}
}

Suscribete a nuestro canal de Youtube

Siguenos en nuestro canal de Youtube Síguenos en nuestro canal de Youtube dedicado a Tecnología, MarketPlace de Proyectos Tecnológicos, Cursos Online y Tutoriales de Desarrollo de Videojuegos. Ofrecemos consultoría en Desarrollo de Software, Marketing Online, Servicios de TI, Hosting Web, dominios web entre otros.

Siguenos en Patreon

Si quieres contribuir con cualquier aporte o donación hacia nuestros proyectos y el canal puedes hacerlo a través de nuestra cuenta en Patreon.

¿Quieres publicar tus propios proyectos?. ¡Pues que esperas!
Foto de perfil de Facebook

Written by 

Tecnología, MarketPlace, Videojuegos, Marketing Online, Web Hosting y más..

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *