Innehållsförteckning:
Video: AR -portalen upp och ner från främmande saker: 10 steg (med bilder)
2025 Författare: John Day | [email protected]. Senast ändrad: 2025-01-13 06:58
Denna Instructable kommer att gå igenom att skapa en augmented reality -mobilapp för iPhone med en portal som leder upp och ner från Stranger Things. Du kan gå in i portalen, gå runt och komma tillbaka. Allt inuti portalen kan bara ses genom portalen tills du går in. Väl inne kommer allt att återges överallt tills du går tillbaka till den verkliga världen. Vi kommer att använda Unity 3D -videospelmotorn med Apple ARKit -plugin. All programvara som vi använder kan laddas ner och användas gratis. Du behöver inte vara expert för att följa med, vi kommer att gå igenom varje steg!
Steg 1: Starta ett nytt enhetsprojekt
Ladda ner Unity3D och se till att installera byggfilerna för IOS -plattformen. Du kommer också att behöva ladda ner Xcode och registrera dig för ett kostnadsfritt Apple -utvecklarkonto. Din iPhone måste också ha IOS 11 eller senare. Från och med den 5 februari 2018 är IOS 11.3 ute men xCode 9.2 har ännu inte supportfiler för det. Så om du kör den allra senaste IOS -versionen, se till att ladda ner den senaste Xcode -betaversionen från Apple. Developer.com.
När du har alla nödvändiga program, öppna Unity och starta ett nytt projekt, kalla det vad du vill. Vi kommer att behöva Apple ARKit -plugin så att vi kan använda vår telefonkamera för att upptäcka marken och placera föremål på golvet. Låt oss importera det nu genom att gå till fliken Tillgångsbutik och söka "ARKit". Du måste skapa ett gratis Unity -konto om du inte redan har ett, klicka sedan på import för att få plugin.
Navigera till mappen exempel i ARKit -mappen och hitta "UnityARKitScene". Dubbelklicka på den för att öppna den. Vi kommer att använda denna scen som utgångspunkt och bygga ut härifrån. Denna scen låter dig som standard identifiera marken och när du trycker på skärmen kommer en kub att placeras i den positionen.
Låt oss först ta bort våra bygginställningar så att vi inte glömmer att göra det senare. Klicka på filen, bygg inställningar och ta bort alla scener från listan. Klicka på lägg till öppna scener för att lägga till vår nuvarande. Det sista vi behöver konfigurera här är att i spelarinställningarna gå ner till bunt -identifieraren och formatet för den här strängen är com. YourCompanyName. YourAppName, så i mitt fall gör jag något som com. MatthewHallberg. PortalTest.
Steg 2: Ställ in scenen
Ta först en titt till vänster och hitta spelobjektet som heter "GeneratePlanes". Med det markerat, titta bort till höger nu och markera kryssrutan för att inaktivera det. På så sätt har vi inte de fula blå rutorna som genereras när ARKit detekterar ett markplan. Ta sedan bort spelobjektet "RandomCube" eftersom vi inte vill se det i vår scen.
Nu måste vi först skapa vår portaldörr. Ta bort kuben som är ett barn till "HitCubeParent". Högerklicka och välj skapa tomt spelobjekt. Byt namn på "Portal". Högerklicka nu på det objektet och skapa en kub, detta kommer att göra det till ett barn av portalen. Byt namn på det "PostLeft" och detta kommer att vara den vänstra posten på vår portal. Skala det så att x är 1 y är 28 och z är ett. Gör samma sak för rätt inlägg. Skapa nu den övre stolpen och skala y till 14. Vänd detta åt sidan och flytta det så att det ansluter de andra inläggen. Gör hela portalskalan 1,3 x 1,4 x 1.
Gå till google och skriv in trä eller barkstruktur. Ladda ner en av dessa bilder och dra den till mappen tillgångar i Unity. Dra nu bilden till alla dina portalinlägg.
Klicka på "Portal" -objektet igen och klicka på lägg till komponent till höger. Lägg till "UnityARHitTestExample" -skriptet till det. Det finns en tom plats där för "Hit Transform", dra objektet "HitCubeParent" till den platsen.
Steg 3: Låt oss göra några partiklar
Nu ska vi använda Unity Particle -systemet för att göra en rök- och flytande partikeleffekt för inuti vår portal. Gå till Tillgångar längst upp i menyraden, standardtillgångar och importpartikelsystem.
Skapa två tomma spelobjekt inuti din portal och kalla det ena "SmokeParticles" och det andra "FloatingParticles".
Lägg till en partikelsystemkomponent till rökpartiklarna.
Denna komponent har en massa alternativ men vi behöver bara byta ett par.
Ändra startfärgen till något mörkblått med cirka 50% transparens. Gör utsläppshastigheten 100. Invändig form, gör radien.01. I renderingsdelen längst ner ändras min storlek till.8 och maxstorlek till 5. På materialkomponenten väljer du bara rökmaterialet från listan, men vi kommer att ändra detta senare.
Lägg till ett partikelsystem till spelobjektet för flytande partiklar nu och ställ in utsläppet till 500. Ställ in starttiden på 2, radie till 10, min partikelstorlek till.01 och max partikelstorlek till.015. Ställ materialet till standardpartikel för tillfället.
Ta slutligen båda spelobjekten och vrid dem med 90 grader på x och höj dem upp i luften så att de släpper ner på portporten.
Steg 4: Sakta ner partiklarna
Eftersom vi vill att dessa partiklar ska täcka ett stort område men också gå långsamt måste vi skapa vår egen provfunktion. Så högerklicka i tillgångsmappen och skapa ett nytt C# -skript och kalla det "ParticleSample". Kopiera och klistra in den här koden:
använder System. Collections;
använder System. Collections. Generic; använder UnityEngine; offentlig klass ParticleSample: MonoBehaviour {private ParticleSystem ps; // Använd detta för att initiera void Start () {ps = GetComponent (); StartCoroutine (SampleParticleRoutine ()); } IEnumerator SampleParticleRoutine () {var main = ps.main; main.simulationSpeed = 1000f; ps. Play (); avkastning avkastning nya WaitForSeconds (.1f); main.simulationSpeed =.05f; }}
Dra nu detta skript till vart och ett av dina partikelsystemspelobjekt.
Steg 5: Skapa portalen
Nu måste vi skapa portalen så högerklicka på portalspelobjektet och skapa en fyrkant. Skala fyrhjulingen så att den täcker hela portalen, detta kommer att bli vårt portalfönster. Det första vi behöver lägga till är portalskuggaren, detta kommer bara att göra objekt med en annan specifik skuggare på. Högerklicka i tillgångsmappen och skapa en ny oupplyst skuggare. Ta bort allt där och klistra in den här koden:
Shader "Portal/portalWindow"
{SubShader {Zwrite off Colormask 0 cull off Stencil {Ref 1 Pass replace} Pass {}}}
Högerklicka i hierarkin och skapa ett nytt material, kalla det PortalWindowMat, i rullgardinsmenyn för detta material hittar du portalsektionen och väljer portalfönster. Dra detta material till din portal quad.
Steg 6: Particle Shaders
Högerklicka i tillgångsmappen igen och skapa en ny skuggare. Vi måste göra shaders för partiklarna som går in i portalen. Ersätt hela koden med detta:
Shader "Portal/Particles" {
Egenskaper {_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} Kategori {Taggar {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "PreviewType" = "Plane"} Blanda 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 "UnityCG.cginc"; fixed4 _TintColor; struct appdata_t {float4 vertex: POSITION; fixed4 färg: FÄRG; float2 texcoord: TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID}; struct v2f {float4 vertex: SV_POSITION; fixed4 färg: FÄRG; float2 texcoord: TEXCOORD0; UNITY_FOG_COORDS (1) #ifdef SOFTPARTICLES_ON float4 projektPos: 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); återvända 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))); float partZ = i.projPos.z; float fade = saturate (_InvFade * (sceneZ-partZ)); i.color.a *= blekna; #endif fixed4 col = 2.0f * i.color * tex2D (_MainTex, i.texcoord); UNITY_APPLY_FOG (i.fogCoord, kol); retur col; } ENDCG}}}}}
Skapa två nya material, ett som kallas portalSmoke och ett som kallas portalParticles.
För var och en väljer denna shader, från rullgardinsmenyn, i portaler, partiklar. För rökpartiklarna väljer du en rökstruktur och för partiklarna väljer du partikelstrukturen. Ändra färgen på röken till en mörkare blå med cirka 50% transparens. Gå till rendererkomponenten i varje partikelsystem i din portal och välj deras respektive material som vi just skapat.
Steg 7: Skapa Skybox
Nu för att verkligen skapa den upp och ner typen av utseende måste vi färga allt mörkblått. För detta kommer vi att använda en transparent skybox så gör en ny shader och klistra in den här koden:
Shader "Portal/portalSkybox" {
Egenskaper {_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) = "grå" {} _Stencil ("StencilNum", int) = 6} SubShader {Tags {"Queue" = "Background" "RenderType" = "Bakgrund" "PreviewType" = "Skybox"} Avlägsna ZWrite Off Blend SrcAlpha OneMinusSrcAlpha Stencil {Ref 1 Comp [_Stencil]} Godkänn {CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 2.0 #include "UnityCG.cginc "samplerCUBE _Tex; half4 _Tex_HDR; half4 _Tint; hälften _Exponering; float _Rotation; float3 RotateAroundYInDegrees (float3 vertex, float degrees) {float alpha = degrees * UNITY_PI / 180.0; flyta 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 {float4 vertex: 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 (roterad); o.texcoord = v.vertex.xyz; återvända 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 *= _Exponering; returhalva4 (c,.5); } ENDCG}} Fallback Off}
Skapa nu ett nytt skybox -material, kalla det "PortalSkybox" och välj denna portalSkybox -skuggare från portalmenyn. Gå till Fönster, Belysning, överst och välj den här skyboxen vi just skapade. Gå till huvudkameran och ställ in tydliga flaggor till skybox. Medan vi är här kan vi lägga till några komponenter på vår kamera så att vi kan upptäcka kollisioner. Lägg till en styv kroppskomponent i kameran och avmarkera använd tyngdkraften. Lägg till en boxkollider och kontrollera om utlösaren är aktiverad. Gör rutan kolliderare storlek. 5 x 1 x 4. Ställ in klippplanet på kameran på.01.
Steg 8: Portal Logic
Det sista vi behöver göra är att skapa logiken som styr vår portal. Skapa ett nytt C# -skript och kalla det PortalController.
använder System. Collections;
använder System. Collections. Generic; använder UnityEngine; namnutrymme UnityEngine. XR.iOS {public class PortalController: MonoBehaviour {public Material materials; offentlig MeshRenderer meshRenderer; offentlig UnityARVideo UnityARVideo; privat bool isInside = false; privat bool isOutside = true; // Använd detta för att initiera 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 (); }} annat {if (isInside) {isInside = false; isOutside = true; OutsidePortal (); }}} void OutsidePortal () {StartCoroutine (DelayChangeMat (3)); } ogiltig InsidePortal () {StartCoroutine (DelayChangeMat (6)); } IEnumerator DelayChangeMat (int stencilNum) {UnityARVideo.shouldRender = false; avkastning avkastning ny WaitForEndOfFrame (); meshRenderer.enabled = false; foreach (Materialmatta i material) {mat. SetInt ("_Stencil", stencilNum); } ge avkastning ny WaitForEndOfFrame (); meshRenderer.enabled = true; UnityARVideo.shouldRender = true; }}}
Dra det här nya skriptet till ditt portalfönster. Detta kommer att överföra oss in och ut ur portalen när kollideraren på vår kamera kolliderar med portalfönstret. Nu i funktionen som ändrar allt material säger vi till ARkit -plugin att inte återge ramen, så gå till huvudkameran och öppna UnityARVideo -skriptet. Skapa en offentlig bool shouldRender högst upp och ställ den lika med true. Ner i OnPreRender () -funktionen linda allt i ett if -uttalande där allt inuti bara körs om shouldRender är sant. Hela manuset ska se ut så här:
använder System;
använder System. Runtime. InteropServices; använder UnityEngine; använder UnityEngine. Rendering; namnutrymme UnityEngine. XR.iOS {public class UnityARVideo: MonoBehaviour {public Material m_ClearMaterial; [HideInInspector] public bool shouldRender = true; privat CommandBuffer m_VideoCommandBuffer; privat Texture2D _videoTextureY; privat Texture2D _videoTextureCbCr; privat Matrix4x4 _displayTransform; privat bool bCommandBufferInitialized; public void Start () {UnityARSessionNativeInterface. ARFrameUpdatedEvent += UpdateFrame; bCommandBufferInitialized = false; } void UpdateFrame (UnityARCamera cam) {_displayTransform = ny 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 = ny CommandBuffer (); m_VideoCommandBuffer. Blit (null, BuiltinRenderTextureType. CurrentActive, m_ClearMaterial); GetComponent (). AddCommandBuffer (CameraEvent. BeforeForwardOpaque, m_VideoCommandBuffer); bCommandBufferInitialized = true; } ogiltig OnDestroy () {GetComponent (). RemoveCommandBuffer (CameraEvent. BeforeForwardOpaque, m_VideoCommandBuffer); UnityARSessionNativeInterface. ARFrameUpdatedEvent -= UpdateFrame; bCommandBufferInitialized = false; } #if! UNITY_EDITOR public void OnPreRender () {if (shouldRender) {ARTextureHandles handles = UnityARSessionNativeInterface. GetARSessionNativeInterface (). GetARVideoTextureHandles (); if (handles.textureY == System. IntPtr. Zero || handles.textureCbCr == System. IntPtr. Zero) {return; } if (! bCommandBufferInitialized) {InitializeCommandBuffer (); } Resolution currentResolution = Screen.currentResolution; // Texture Y if (_videoTextureY == null) {_videoTextureY = Texture2D. CreateExternalTexture (currentResolution.width, currentResolution.height, TextureFormat. R8, false, false, (System. IntPtr) handles.textureY); _videoTextureY.filterMode = FilterMode. Bilinear; _videoTextureY.wrapMode = TextureWrapMode. Repeat; m_ClearMaterial. SetTexture ("_ textureY", _videoTextureY); } // Texture CbCr if (_videoTextureCbCr == null) {_videoTextureCbCr = Texture2D. CreateExternalTexture (currentResolution.width, currentResolution.height, TextureFormat. RG16, false, false, (System. IntPtr) handles.textureCbC); _videoTextureCbCr.filterMode = FilterMode. Bilinear; _videoTextureCbCr.wrapMode = TextureWrapMode. Repeat; m_ClearMaterial. SetTexture ("_ textureCbCr", _videoTextureCbCr); } _videoTextureY. UpdateExternalTexture (handles.textureY); _videoTextureCbCr. UpdateExternalTexture (handles.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 ("_ textureY", _videoTextureY); m_ClearMaterial. SetTexture ("_ textureCbCr", _videoTextureCbCr); m_ClearMaterial. SetMatrix ("_ DisplayTransform", _displayTransform); } #endif}}
Steg 9: Nästan klart
Slutligen när vi klickar på skärmen och placerar portalen vill vi att den alltid ska möta oss. För att göra detta, gå till skriptet "UnityARHitTestExample" på portalen. Byt ut allt inuti med detta:
använder System;
använder System. Collections. Generic; namnutrymme UnityEngine. XR.iOS {public class UnityARHitTestExample: MonoBehaviour {public Transform m_HitTransform; public float maxRayDistance = 30.0f; public LayerMask collisionLayer = 1 <0) {foreach (var hitResult in 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. ######}}: {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 = ny Vector3 (currAngle.x, transform.eulerAngles.y, currAngle.z); återvända sant; }} returnera false; } // Uppdatering kallas en gång per ram void Uppdatering () {#if UNITY_EDITOR // vi kommer bara att använda detta skript på redaktörssidan, även om det inte finns något som skulle hindra det från att fungera på enheten om (Input. GetMouseButtonDown (0)) {Ray ray = Camera.main. ScreenPointToRay (Input.mousePosition); RaycastHit träffade; // vi ska försöka träffa ett av flygkollider -spelobjekten som genererades av tillägget // effektivt liknar att ringa HitTest med ARHitTestResultType. ARHitTestResultTypeExistingPlaneUsingExtent if (Physics. Raycast (ray, out hit, maxRayDistance, collisionLayer)) {// vi kommer att få positionen från kontaktpunkten m_HitTransform.position = hit.point; Debug. Log (string. Format ("x: {0: 0. ######}}: {1: 0. ######}} z: {2: 0. ###### } ", m_HitTransform.position.x, m_HitTransform.position.y, m_HitTransform.position.z)); // och rotationen från transformationen av plankollidern 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); ARPoint -punkt = ny ARPoint {x = screenPosition.x, y = screenPosition.y}; // Prioritera reults typer ARHitTestResultType resultTypes = {ARHitTestResultType. ARHitTestResultTypeExistingPlaneUsingExtent, // om du vill använda oändliga plan använder denna: //ARHitTestResultType. ARHitTestResultTypeExistingPlane, ARHitTestResultType. ARHitTestResultTypeHorizontalPlane, ARHitTestResultType. ARHitTestResultTypeFeaturePoint}; foreach (ARHitTestResultType resultType in resultTypes) {if (HitTestWithResultType (point, resultType)) {return; }}}} #endif}}}
Steg 10: Lägg appen på din telefon
Äntligen är vi klara. Gå till filen, bygg inställningar och klicka på bygg. Öppna Xcode och välj mappen som skapades från byggnaden. Välj ditt utvecklingsteam och lägg appen på din telefon! Du kanske vill ändra färgerna på partiklarna och skyboxen för att passa dina behov. Låt mig veta i kommentarerna om du har några frågor och tack för att du tittade!