Portal AR al revés de Stranger Things: 10 pasos (con imágenes)
Portal AR al revés de Stranger Things: 10 pasos (con imágenes)

Video: Portal AR al revés de Stranger Things: 10 pasos (con imágenes)

Video: Portal AR al revés de Stranger Things: 10 pasos (con imágenes)
Video: Antes de Once estaba la Chilindrina en Stranger Things | Netflix 2025, Enero
Anonim
Portal de RA al revés de Stranger Things
Portal de RA al revés de Stranger Things
Portal de RA al revés de Stranger Things
Portal de RA al revés de Stranger Things

Este Instructable pasará por la creación de una aplicación móvil de realidad aumentada para iPhone con un portal que conduce al revés de Stranger Things. Puedes entrar al portal, caminar y volver a salir. Todo lo que hay dentro del portal solo se puede ver a través del portal hasta que entras. Una vez dentro, todo se renderizará en todas partes, hasta que regrese al mundo real. Usaremos el motor de videojuegos Unity 3D con el complemento Apple ARKit. Todo el software que utilizaremos se puede descargar y utilizar de forma gratuita. No necesita ser un experto para seguir adelante, ¡seguiremos todos los pasos!

Paso 1: Inicie un nuevo proyecto de Unity

Inicie un nuevo proyecto de Unity
Inicie un nuevo proyecto de Unity

En primer lugar, descargue Unity3D y asegúrese de instalar los archivos de compilación para la plataforma IOS. También deberá descargar Xcode y registrarse para obtener una cuenta de desarrollador de Apple gratuita. Su iPhone también tendrá que ejecutar IOS 11 o superior. A partir de hoy, 5 de febrero de 2018, IOS 11.3 está disponible, pero xCode 9.2 aún no tiene archivos de soporte para él. Entonces, si está ejecutando la última versión de IOS, asegúrese de descargar la última versión beta de Xcode de Apple. Developer.com.

Una vez que tenga todos los programas necesarios, abra Unity y comience un nuevo proyecto, llámelo como quiera. Vamos a necesitar el complemento Apple ARKit para que podamos usar la cámara de nuestro teléfono para detectar el suelo y colocar objetos en el suelo. Importémoslo ahora yendo a la pestaña Asset Store y busquemos "ARKit". Deberá crear una cuenta gratuita de Unity si aún no tiene una, luego haga clic en importar para obtener el complemento.

Navegue a la carpeta de ejemplos en la carpeta ARKit y busque "UnityARKitScene". Haz doble clic en eso para abrirlo. Usaremos esta escena como punto de partida y partiremos de aquí. Esta escena por defecto te permitirá detectar el suelo y cuando toques la pantalla, se colocará un cubo en esa posición.

Primero vamos a arreglar nuestra configuración de construcción para que no nos olvidemos de hacerlo más tarde. Haga clic en archivo, cree la configuración y elimine todas las escenas de esa lista. Haga clic en agregar escenas abiertas para agregar la actual. Lo último que necesitamos configurar aquí es que en la configuración del reproductor baje al identificador del paquete y el formato de esta cadena es com. YourCompanyName. YourAppName, así que en mi caso hago algo como com. MatthewHallberg. PortalTest.

Paso 2: configura la escena

Prepara la escena
Prepara la escena

Primero eche un vistazo a la izquierda y busque el objeto del juego llamado "GeneratePlanes". Con eso resaltado, mire hacia la derecha ahora y haga clic en la casilla de verificación para deshabilitarlo. De esta manera no tenemos los feos cuadrados azules que se generan cuando ARKit detecta un plano de tierra. A continuación, elimine el objeto del juego "RandomCube" porque no queremos verlo en nuestra escena.

Ahora tenemos que crear primero la puerta de nuestro portal. Elimine el cubo que es hijo de "HitCubeParent". Haz clic derecho y elige crear un objeto de juego vacío. Cambie el nombre de "Portal". Ahora haga clic derecho en ese objeto y cree un cubo, esto lo convertirá en un hijo del portal. Cambie el nombre de "PostLeft" y esta será la publicación izquierda de nuestro portal. Escale para que la x sea 1, la y sea 28 y la z sea uno. Haz lo mismo con la publicación correcta. Ahora cree la publicación superior y escale la y a 14. Gírela hacia los lados y muévala de manera que conecte las otras publicaciones. Haga todo el portal a escala 1,3 x 1,4 x 1.

Vaya a Google y escriba textura de madera o corteza. Descarga una de esas imágenes y arrástrala a tu carpeta de activos en Unity. Ahora arrastre esa imagen a todas las publicaciones de su portal.

Haga clic en el objeto "Portal" nuevamente y haga clic en agregar componente a la derecha. Agregue el script "UnityARHitTestExample". Hay un espacio vacío allí para "Hit Transform", arrastre el objeto "HitCubeParent" a ese espacio.

Paso 3: Hagamos algunas partículas

Hagamos algunas partículas
Hagamos algunas partículas

Ahora vamos a utilizar el sistema Unity Particle para crear un efecto de humo y partículas flotantes para el interior de nuestro portal. Vaya a Activos en la barra de menú superior, activos estándar e importe sistemas de partículas.

Crea dos objetos de juego vacíos dentro de tu portal y llama a uno "SmokeParticles" y al otro "FloatingParticles".

Agregue un componente del sistema de partículas a las partículas de humo.

Este componente tiene un montón de opciones, pero solo necesitamos cambiar un par.

Cambie el color de inicio a algo azul oscuro con aproximadamente un 50% de transparencia. Haga que la tasa de emisión sea 100. En la forma interior, haga el radio.01. En la parte inferior del renderizador, cambie el tamaño mínimo a.8 y el tamaño máximo a 5. En el componente de material, elija el material de humo de la lista, pero lo cambiaremos más adelante.

Agregue un sistema de partículas al objeto del juego de partículas flotantes ahora y establezca la emisión en 500. Establezca la vida útil inicial en 2, el radio en 10, el tamaño mínimo de partículas en.01 y el tamaño máximo de partículas en.015. Establezca el material en partículas predeterminadas por ahora.

Finalmente, tome ambos objetos del juego y gírelos 90 grados en la x y levántelos en el aire para que se emitan hacia la entrada del portal.

Paso 4: Disminuir la velocidad de las partículas

Disminuir la velocidad de las partículas
Disminuir la velocidad de las partículas

Como queremos que estas partículas cubran un área grande pero también se muevan lentamente, necesitamos crear nuestra propia función de muestra. Así que haga clic derecho en la carpeta de activos y cree un nuevo script C # y llámelo "ParticleSample". Copia y pega este código:

usando System. Collections;

usando System. Collections. Generic; usando UnityEngine; ParticleSample de clase pública: MonoBehaviour {ParticleSystem privado ps; // Use esto para la inicialización void Start () {ps = GetComponent (); StartCoroutine (SampleParticleRoutine ()); } IEnumerator SampleParticleRoutine () {var main = ps.main; main.simulationSpeed = 1000f; ps. Play (); rendimiento return new WaitForSeconds (.1f); main.simulationSpeed =.05f; }}

Ahora arrastre este script a cada uno de los objetos del juego de su sistema de partículas.

Paso 5: ¡Creando el Portal

¡Creando el Portal!
¡Creando el Portal!

Ahora tenemos que crear el portal, así que haga clic con el botón derecho en el objeto del juego del portal y cree un quad. Escale el quad para que cubra todo el portal, esto se convertirá en nuestra ventana de portal. Lo primero que debemos agregar es el sombreador del portal, esto solo renderizará objetos con otro sombreador específico en ellos. Haga clic derecho en la carpeta de activos y cree un nuevo sombreador apagado. Elimina todo lo que hay allí y pega este código:

Sombreador "Portal / portalWindow"

{SubShader {Zwrite off Colormask 0 cull off Stencil {Ref 1 Pass replace} Pass {}}}

Haga clic con el botón derecho en la jerarquía y cree un nuevo material, llámelo PortalWindowMat, en el menú desplegable de este material busque la sección del portal y elija la ventana del portal. Arrastre este material a su portal quad.

Paso 6: Sombreadores de partículas

Sombreadores de partículas
Sombreadores de partículas

Haga clic derecho en la carpeta de activos nuevamente y cree un nuevo sombreador. Necesitamos hacer los sombreadores para las partículas que entran en el portal. Reemplaza todo el código con esto:

Shader "Portal / Particles" {

Propiedades {_TintColor ("Tint Color", Color) = (0.5, 0.5, 0.5, 0.5) _MainTex ("Particle Texture", 2D) = "white" {} _InvFade ("Soft Particles Factor", Range (0.01, 3.0)) = 1.0 _Stencil ("stencil", int) = 6} Categoría {Etiquetas {"Queue" = "Transparente" "IgnoreProjector" = "True" "RenderType" = "Transparente" "PreviewType" = "Plane"} Mezcla SrcAlpha OneMinusSrcAlpha ColorMask RGB Cull Off Lighting Off ZWrite Off SubShader {Stencil {Ref 1 Comp [_Stencil]} Pass {CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 2.0 #pragma multi_compile_particles #pragma multi_compile_fog #include "Unity" sampx.cg; _ Fixed4 _TintColor; struct appdata_t {float4 vertex: POSITION; color fijo4: COLOR; float2 texcoord: TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID}; struct v2f {vértice float4: SV_POSITION; color fijo4: COLOR; float2 texcoord: TEXCOORD0; UNITY_FOG_COORDS (1) #ifdef SOFTPARTICLES_ON float4 projPos: TEXCOORD2; #endif UNITY_VERTEX_OUTPUT_STEREO}; float4 _MainTex_ST; v2f vert (appdata_t v) {v2f o; UNITY_SETUP_INSTANCE_ID (v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO (o); o.vertex = UnityObjectToClipPos (v.vertex); #ifdef SOFTPARTICLES_ON o.projPos = ComputeScreenPos (o.vertex); COMPUTE_EYEDEPTH (o.projPos.z); #endif o.color = v.color * _TintColor; o.texcoord = TRANSFORM_TEX (v.texcoord, _MainTex); UNITY_TRANSFER_FOG (o, o.vertex); volver o; } UNITY_DECLARE_DEPTH_TEXTURE (_CameraDepthTexture); float _InvFade; Fixed4 frag (v2f i): SV_Target {#ifdef SOFTPARTICLES_ON float sceneZ = LinearEyeDepth (SAMPLE_DEPTH_TEXTURE_PROJ (_CameraDepthTexture, UNITY_PROJ_COORD (i.projPos))); flotador partZ = i.projPos.z; desvanecimiento flotante = saturar (_InvFade * (sceneZ-partZ)); i.color.a * = desvanecerse; #endif fixed4 col = 2.0f * i.color * tex2D (_MainTex, i.texcoord); UNITY_APPLY_FOG (i.fogCoord, col); return col; } ENDCG}}}}

Cree dos materiales nuevos, uno llamado portalSmoke y otro llamado portalParticles.

Para cada uno elija este sombreador, del desplegable, en portales, partículas. Para las partículas de humo, elija una textura de humo y para las partículas elija la textura de partículas. Cambie el color del humo a un azul más oscuro con aproximadamente un 50% de transparencia. Vaya al componente de renderizado de cada sistema de partículas en su portal y elija sus respectivos materiales que acabamos de crear.

Paso 7: crea el Skybox

Crea el Skybox
Crea el Skybox

Ahora, para crear realmente el tipo de look al revés, tenemos que teñir todo de azul oscuro. Para esto usaremos un skybox transparente, así que crea un nuevo sombreador y pega este código:

Shader "Portal / portalSkybox" {

Propiedades {_Tint ("Tint Color", Color) = (.5,.5,.5,.5) [Gamma] _Exposure ("Exposure", Range (0, 8)) = 1.0 _Rotation ("Rotation", Range (0, 360)) = 0 [NoScaleOffset] _Tex ("Cubemap (HDR)", Cube) = "gray" {} _Stencil ("StencilNum", int) = 6} SubShader {Tags {"Queue" = "Background" "RenderType" = "Background" "PreviewType" = "Skybox"} Cull Off ZWrite Off Blend SrcAlpha OneMinusSrcAlpha Stencil {Ref 1 Comp [_Stencil]} Pass {CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 2.0 #include "UnityCG.cginc "samplerCUBE _Tex; half4 _Tex_HDR; half4 _Tint; mitad _Exposición; float _Rotation; float3 RotateAroundYInDegrees (vértice flotante3, grados flotantes) {alfa flotante = grados * UNITY_PI / 180.0; flotar sina, cosa; sincos (alfa, sina, cosa); float2x2 m = float2x2 (cosa, -sina, sina, cosa); return float3 (mul (m, vertex.xz), vertex.y).xzy; } struct appdata_t {float4 vertex: POSITION; UNITY_VERTEX_INPUT_INSTANCE_ID}; struct v2f {vértice float4: SV_POSITION; float3 texcoord: TEXCOORD0; UNITY_VERTEX_OUTPUT_STEREO}; v2f vert (appdata_t v) {v2f o; UNITY_SETUP_INSTANCE_ID (v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO (o); float3 rotated = RotateAroundYInDegrees (v.vertex, _Rotation); o.vertex = UnityObjectToClipPos (girado); o.texcoord = v.vertex.xyz; volver o; } Fixed4 frag (v2f i): SV_Target {half4 tex = texCUBE (_Tex, i.texcoord); half3 c = DecodeHDR (tex, _Tex_HDR); c = c * _Tint.rgb * unity_ColorSpaceDouble.rgb; c * = _Exposición; return half4 (c,.5); } ENDCG}} Retroceso desactivado}

Ahora cree un nuevo material skybox, llámelo "PortalSkybox" y elija este sombreador portalSkybox del menú del portal. Vaya a Ventana, Iluminación, en la parte superior y elija este skybox que acabamos de crear. Vaya a la cámara principal y coloque banderas claras en el palco. Mientras estamos aquí, agreguemos algunos componentes en nuestra cámara para que podamos detectar colisiones. Agregue un componente de cuerpo rígido a la cámara y desmarque Usar gravedad. Agregue un colisionador de cajas y verifique su disparador. Haga que los colisionadores de cajas tengan un tamaño de.5 x 1 x 4. Establezca el plano de recorte de la cámara en.01.

Paso 8: lógica del portal

Lógica del portal
Lógica del portal

Lo último que debemos hacer es crear la lógica que controla nuestro portal. Cree un nuevo script C # y llámelo PortalController.

usando System. Collections;

usando System. Collections. Generic; usando UnityEngine; espacio de nombres UnityEngine. XR.iOS {clase pública PortalController: MonoBehaviour {material público materiales; public MeshRenderer meshRenderer; public UnityARVideo UnityARVideo; private bool isInside = false; private bool isOutside = true; // Use esto para la inicialización void Start () {OutsidePortal (); } void OnTriggerStay (Collider col) {Vector3 playerPos = Camera.main.transform.position + Camera.main.transform.forward * (Camera.main.nearClipPlane * 4); if (transform. InverseTransformPoint (playerPos).z <= 0) {if (isOutside) {isOutside = false; isInside = true; InsidePortal (); }} else {if (isInside) {isInside = false; isOutside = true; OutsidePortal (); }}} void OutsidePortal () {StartCoroutine (DelayChangeMat (3)); } void InsidePortal () {StartCoroutine (DelayChangeMat (6)); } IEnumerator DelayChangeMat (int stencilNum) {UnityARVideo.shouldRender = false; rendimiento return new WaitForEndOfFrame (); meshRenderer.enabled = false; foreach (Alfombrilla de material en materiales) {mat. SetInt ("_Stencil", stencilNum); } rendimiento return new WaitForEndOfFrame (); meshRenderer.enabled = true; UnityARVideo.shouldRender = true; }}}

Arrastre este nuevo script a la ventana de su portal. Esto nos permitirá entrar y salir del portal siempre que el colisionador de nuestra cámara choque con la ventana del portal. Ahora en la función que cambia todos los materiales le decimos al plugin ARkit que no renderice el marco, así que ve a la cámara principal y abre el script UnityARVideo. Cree un bool público shouldRender en la parte superior y configúrelo como verdadero. Abajo, en la función OnPreRender (), envuelva todo en una declaración if donde todo lo que hay dentro solo se ejecutará si shouldRender es verdadero. Todo el guión debería verse así:

usando el sistema;

utilizando System. Runtime. InteropServices; usando UnityEngine; usando UnityEngine. Rendering; espacio de nombres UnityEngine. XR.iOS {public class UnityARVideo: MonoBehaviour {public Material m_ClearMaterial; [HideInInspector] public bool shouldRender = true; privado CommandBuffer m_VideoCommandBuffer; Texture2D privado _videoTextureY; Texture2D privado _videoTextureCbCr; privado Matrix4x4 _displayTransform; private bool bCommandBufferInitialized; public void Start () {UnityARSessionNativeInterface. ARFrameUpdatedEvent + = UpdateFrame; bCommandBufferInitialized = falso; } UpdateFrame vacío (cámara UnityARCamera) {_displayTransform = new Matrix4x4 (); _displayTransform. SetColumn (0, cam.displayTransform.column0); _displayTransform. SetColumn (1, cam.displayTransform.column1); _displayTransform. SetColumn (2, cam.displayTransform.column2); _displayTransform. SetColumn (3, cam.displayTransform.column3); } void InitializeCommandBuffer () {m_VideoCommandBuffer = new CommandBuffer (); m_VideoCommandBuffer. Blit (nulo, BuiltinRenderTextureType. CurrentActive, m_ClearMaterial); GetComponent (). AddCommandBuffer (CameraEvent. BeforeForwardOpaque, m_VideoCommandBuffer); bCommandBufferInitialized = verdadero; } void OnDestroy () {GetComponent (). RemoveCommandBuffer (CameraEvent. BeforeForwardOpaque, m_VideoCommandBuffer); UnityARSessionNativeInterface. ARFrameUpdatedEvent - = UpdateFrame; bCommandBufferInitialized = falso; } #if! UNITY_EDITOR public void OnPreRender () {if (shouldRender) {ARTextureHandles identificadores = UnityARSessionNativeInterface. GetARSessionNativeInterface (). GetARVideoTextureHandles (); if (handle.textureY == System. IntPtr. Zero || handle.textureCbCr == System. IntPtr. Zero) {return; } if (! bCommandBufferInitialized) {InitializeCommandBuffer (); } Resolución currentResolution = Screen.currentResolution; // Textura Y if (_videoTextureY == null) {_videoTextureY = Texture2D. CreateExternalTexture (currentResolution.width, currentResolution.height, TextureFormat. R8, false, false, (System. IntPtr) handle.textureY); _videoTextureY.filterMode = FilterMode. Bilinear; _videoTextureY.wrapMode = TextureWrapMode. Repeat; m_ClearMaterial. SetTexture ("_ texturaY", _videoTextureY); } // Textura CbCr if (_videoTextureCbCr == null) {_videoTextureCbCr = Texture2D. CreateExternalTexture (currentResolution.width, currentResolution.height, TextureFormat. RG16, false, false, (System. IntPtr) handle.textureCbCr); _videoTextureCbCr.filterMode = FilterMode. Bilinear; _videoTextureCbCr.wrapMode = TextureWrapMode. Repeat; m_ClearMaterial. SetTexture ("_ textureCbCr", _videoTextureCbCr); } _videoTextureY. UpdateExternalTexture (maneja.texturaY); _videoTextureCbCr. UpdateExternalTexture (maneja.textureCbCr); m_ClearMaterial. SetMatrix ("_ DisplayTransform", _displayTransform); }} #else public void SetYTexure (Texture2D YTex) {_videoTextureY = YTex; } public void SetUVTexure (Texture2D UVTex) {_videoTextureCbCr = UVTex; } public void OnPreRender () {if (! bCommandBufferInitialized) {InitializeCommandBuffer (); } m_ClearMaterial. SetTexture ("_ texturaY", _videoTextureY); m_ClearMaterial. SetTexture ("_ textureCbCr", _videoTextureCbCr); m_ClearMaterial. SetMatrix ("_ DisplayTransform", _displayTransform); } #terminara si } }

Paso 9: ¡Casi terminado

¡Casi termino!
¡Casi termino!

Finalmente cuando hacemos clic en la pantalla y colocamos el portal queremos que esté siempre frente a nosotros. Para hacer esto, vaya al script "UnityARHitTestExample" en el portal. Reemplaza todo el interior con esto:

usando el sistema;

usando System. Collections. Generic; espacio de nombres UnityEngine. XR.iOS {public class UnityARHitTestExample: MonoBehaviour {public Transform m_HitTransform; public float maxRayDistance = 30.0f; public LayerMask collisionLayer = 1 <0) {foreach (var hitResult en hitResults) {Debug. Log ("Got hit!"); m_HitTransform.position = UnityARMatrixOps. GetPosition (hitResult.worldTransform); m_HitTransform.rotation = UnityARMatrixOps. GetRotation (hitResult.worldTransform); Debug. Log (string. Format ("x: {0: 0. ######} y: {1: 0. ######} z: {2: 0. ###### } ", m_HitTransform.position.x, m_HitTransform.position.y, m_HitTransform.position.z)); Vector3 currAngle = transform.eulerAngles; transform. LookAt (Camera.main.transform); transform.eulerAngles = new Vector3 (currAngle.x, transform.eulerAngles.y, currAngle.z); devuelve verdadero; } } falso retorno; } // La actualización se llama una vez por cuadro void Update () {#if UNITY_EDITOR // solo usaremos este script en el lado del editor, aunque no hay nada que impida que funcione en el dispositivo si (Input. GetMouseButtonDown (0)) {Rayo = Camera.main. ScreenPointToRay (Input.mousePosition); RaycastHit hit; // intentaremos golpear uno de los objetos del juego de colisionador de aviones que fueron generados por el complemento // efectivamente similar a llamar a HitTest con ARHitTestResultType. ARHitTestResultTypeExistingPlaneUsingExtent if (Physics. Raycast (ray, out hit, maxRayDistance, collisionLayer)) {// vamos a obtener la posición del punto de contacto m_HitTransform.position = hit.point; Debug. Log (string. Format ("x: {0: 0. ######} y: {1: 0. ######} z: {2: 0. ###### } ", m_HitTransform.position.x, m_HitTransform.position.y, m_HitTransform.position.z)); // y la rotación de la transformada del colisionador de planos m_HitTransform.rotation = hit.transform.rotation; }} #else if (Input.touchCount> 0 && m_HitTransform! = null) {var touch = Input. GetTouch (0); if (touch.phase == TouchPhase. Began || touch.phase == TouchPhase. Moved) {var screenPosition = Camera.main. ScreenToViewportPoint (touch.position); Punto ARPoint = nuevo ARPoint {x = screenPosition.x, y = screenPosition.y}; // priorizar tipos reults ARHitTestResultType = {resultTypes ARHitTestResultType. ARHitTestResultTypeExistingPlaneUsingExtent, // si desea utilizar usan planos infinitos esto: //ARHitTestResultType. ARHitTestResultTypeExistingPlane, ARHitTestResultType. ARHitTestResultTypeHorizontalPlane, ARHitTestResultType. ARHitTestResultTypeFeaturePoint}; foreach (ARHitTestResultType resultType en resultTypes) {if (HitTestWithResultType (punto, resultType)) {return; } } } } #terminara si } } }

Paso 10: ¡Pon la aplicación en tu teléfono

¡Pon la aplicación en tu teléfono!
¡Pon la aplicación en tu teléfono!

Finalmente hemos terminado. Vaya a archivo, configuración de compilación y haga clic en compilar. Abra Xcode y elija la carpeta que se creó a partir de la compilación. ¡Elija su equipo de desarrollo y coloque la aplicación en su teléfono! Es posible que desee cambiar los colores de las partículas y el skybox para satisfacer sus necesidades. ¡Déjame saber en los comentarios si tienes alguna pregunta y gracias por mirar!