Fortsätt från del 1 där vi greppade med Papervision3D, del 2 tar scenen lite längre. I den här avslutande delen lär du dig hur du får dina roterande kuber att springa ut på betraktaren med 3D-glasögon.
Jag vet att det står där uppe att denna handledning handlar om Papervision3D i Flash, men det handlar faktiskt om att lura din hjärna. Vi ska göra din hjärna tänk att det ser på ett verkligt 3D-objekt, när det faktiskt bara ser en 2D-bild.
Om du är bekant med Papervision3D kan du följa det här genom att använda något projekt som har en scen, en kamera och ett visningsport. Om inte, kolla in del 1 i denna handledning först, eftersom jag kommer att fortsätta från var vi slutade. Här är den typ av sak vi ska arbeta för:
Peka fingret i luften och håll det upp framför näsan.
Titta på det med bara ditt högra öga öppet, då med bara vänster. Se hur det verkar hoppa från vänster till höger, medan vad som helst i bakgrunden knappast verkar flytta alls?
Din hjärna märker dessa skillnader mellan vad dina ögon ser och använder dem för att generera en full 3D-simulering av världen runt omkring dig. I ditt huvud. Omedelbart. Ganska imponerande!
Om vi kommer att lura din hjärna behöver vi göra varenda ögon se en annan bild. Så att börja, låt oss lägga till ett annat "öga" (en annan kamera) till scenen. Detta kommer att användas för att skapa bilden för ditt högra öga, medan den befintliga kamerans bild kommer att matas till ditt vänstra öga.
Skapa en ny offentlig variabel som heter camera2...
public var kamera2: Camera3D;
... och initialisera den i huvudfunktionen ():
kamera = ny Camera3D ();
Vi behöver en annan visningsport för att ta kamerans utmatning, så lägg till följande:
public var viewport2: Viewport3D;
viewport2 = nytt Viewport3D (); viewport2.autoScaleToStage = true; // detta kommer att göra visningsporten så stor som scenen addChild (viewport2);
(Jag rusar igenom det här, eftersom vi täckte det hela förra gången.)
Kom ihåg, viewport2 kommer att vara tomt tills vi gör något åt det. Vi behöver inte skapa en ny renderare för detta. använd bara den befintliga två gånger. Så, i Main (), ta den här koden:
renderer = ny BasicRenderEngine (); renderer.renderScene (scen, kamera, visningsport);
... och lägg till en ny rad för att göra den andra kameran till den andra visningen, så här:
renderer = ny BasicRenderEngine (); renderer.renderScene (scen, kamera, visningsport); renderer.renderScene (scen, kamera2, viewport2);
Gör detsamma i din onEnterFrame () funktion också:
renderer.renderScene (scen, kamera, visningsport); renderer.renderScene (scen, kamera2, viewport2);
Om du testar det här nu, så ser det ut som det gjorde förut:
Men det är ingen överraskning: för en sak är de två kamerorna på exakt samma plats!
Som standard placeras kameror på (x = 0, y = 0, z = -1000) - du kan kontrollera genom att göra spår (kamera.x, kamera.y, kamera.z).
Så sedd ovanifrån ser scenen ut så här:
Sett från sidan är det så här:
För att efterlikna våra egna ögon borde vi placera vår andra kamera lite till höger om vårt första:
Låt oss försöka flytta det 50 pixlar till höger och se vad som händer. Sätt det här i Main (), någonstans efter att du skapat den andra kameran:
camera2.x = camera.x + 50;
Testa det nu och se vad du får:
Det ser konstigt ut, men det fungerar tydligt! Området runt kuberna är genomskinligt i varje visningsport, så det första kan ses bakom den andra.
Det kommer att vara bra att titta på vad varje kamera ser individuellt, så låt oss se analoga ögon ytterligare genom att de blinkar när vi klickar. Först lägger du till en händelselyttare i varje visningsport, för att upptäcka musklickarna:
viewport.addEventListener (MouseEvent.CLICK, onClickViewport); viewport2.addEventListener (MouseEvent.CLICK, onClickViewport2);
Sätt det i slutet av huvudfunktionen (). Glöm inte att importera MouseEvent:
importera flash.events.MouseEvent;
Lägg till dessa nya funktioner i din kod, utanför huvudfunktionen () men i huvudklassen:
offentlig funktion onClickViewport (mouseEvt: MouseEvent): void allmän funktion påClickViewport2 (mouseEvt: MouseEvent): void
När vi klickar på det första visningsporten behöver vi den bli osynlig medan den andra blir synlig (och vice versa för när vi klickar på det andra). Så ändra dina händelsehanterare så här:
allmän funktion påClickViewport (mouseEvt: MouseEvent): void viewport.visible = false; viewport2.visible = true; allmän funktion påClickViewport2 (mouseEvt: MouseEvent): void viewport.visible = true; viewport2.visible = false;
Testa:
Observera att du måste klicka på en av kuberna; De genomskinliga områdena avfyras inte av en CLICK MouseEvent.
Du kommer behöva ett par 3D "anaglyph" glasögon; det slag där varje lins är en annan färg. Paret jag använder har en gul lins i vänstra ögat och en blå lins i höger öga (kallat ColorCode3D-systemet), men andra färgkombinationer är också populära, som röd och cyan.
Du kan hitta dessa glasögon på eBay för några dollar: sök bara efter 3d glasögon
Alternativt kan du till och med göra din egen! André skrev en kommentar på den första delen av denna handledning som förklarar att han använder Tic-Tac-lådor med olika färger för att göra hans. Geni!
Trots att mina exempel kommer att göras för gula och blåa glasögon, försöker jag förklara principerna så att du kan få ditt arbete att fungera med alla färger du har. Det är lite knepigt, så vänligen skriv en kommentar nedan om det blir förvirrande!
Varje pixel på din dator är som en grupp med tre lampor: en röd, en grön och en blå.
Genom att justera ljusstyrkan hos varje "lampa" kan vi ändra färg på den pixeln:
Linserna i 3D-glasögon stoppar ljuset från några av dessa glödlampor från att komma igenom. Till exempel blockerar den blå linsen i mina glasögon det röda och gröna ljuset från att nå mitt öga.
Så om jag tittar på den här bilden genom min blå lins, kan jag inte läsa den:
Inget ljus når mitt öga från det svarta området eftersom alla tre lampor är avstängda. Och inget ljus når mitt öga från det gröna området eftersom bara den gröna glödlampan är påslagen och den blå linsen filtrerar ut det. Så allt jag ser är en svart rektangel.
Nu, okej, om du försöker detta själv kan du upptäcka att detta inte är helt sant. Linsen är förmodligen inte 100% renblå, så det låter lite grönt och rött ljus genom. Eller kanske din bildskärms färger är kalibrerad annorlunda än min, så den gröna texten har en liten mängd rött och blått i det. För att vara ärlig är det samma för mig - men det spelar ingen roll så länge texten är hård att läsa.
Vårt mål är att göra det svårt för ditt högra öga att se bilden från vänster kamera, och likaledes för ditt vänstra öga och rätt kamera.
Den blå linsen är över mitt högra öga, så i teorin om jag tar bort allt blå från den vänstra kamerans bild kommer jag inte att kunna se den med mitt högra öga.
I det här steget, låt oss göra det vänstra visningsbilden bara avge rött och grönt ljus; inget blått ljus alls. Vi kan göra detta med hjälp av a Colortransform. Dessa är lätta att ställa in; först importera klassen:
importera flash.geom.ColorTransform;
Skapa sedan en ny offentlig variabel för att hålla en förekomst av klassen:
public var leftViewportColorTransform: ColorTransform;
Skapa nu den här nya instansen i Main ():
leftViewportColorTransform = ny ColorTransform ();
Slutligen berätta för Flash att använda denna ColorTransform till vänster kamerans visningsport:
viewport.transform.colorTransform = leftViewportColorTransform;
(Den sista raden måste vara efter att du har skapat visningsporten och färgtransformat. Det är förmodligen enklast att bara säga det rätt i slutet av Main ().)
SWF kommer inte att se annorlunda än, eftersom ColorTransform inte ändrar bilden som helhet som standard.
Vi kan välja vilken fraktion av varje lampa som vi vill släppa igenom genom att ändra blueMultiplier, greenMultiplier och redMultiplier egenskaper hos vår ColorTransform.
Ställer in någon av dessa till 1 berättar för Flash att lämna den ensam, medan den ställs in 0 berättar att Flash stängs av helt. Och, naturligtvis, 0,5 kommer att göra det produktionen hälften av det normala beloppet, 2,0 kommer att göra det dubbla det, och så vidare.
Så att dränera allt det blå ur vänstra visningsporten kan vi göra det här:
leftViewportColorTransform.blueMultiplier = 0;
Du måste ange det före linjen som gäller transformationen till visningsporten, så här:
leftViewportColorTransform.blueMultiplier = 0; viewport.transform.colorTransform = leftViewportColorTransform;
Om dina glasögon har en röd eller grön lins över höger öga, bör du ställa in redMultiplier eller greenMultiplier till 0, snarare än den blåa. För andra färger måste du använda en blandning av rött, grönt och blått - mer på det i steg 15.
Testa SWF:
Det är svårt att ta fram "a" av logotypen genom den blå linsen, men det är inte svårt att bestämma kubens form mot den vita bakgrunden!
Om du ändrar bakgrunden till din SWF ser du problemet. Eftersom det "vita" området runt kuberna inte är faktiskt vitt, men ganska genomskinligt och låter den vita bakgrunden av SWF visa sig, påverkas den inte av ColorTransform.
För att fixa detta måste vi ge visningsporten en solid vit bakgrund som kuberna kommer att bli gjorda ovanpå. Det är lätt nog bara gå tillbaka till var du skapade visningsporten:
viewport = nytt Viewport3D (); viewport.autoScaleToStage = true; // detta kommer att göra visningsporten så stor som scenen
... och lägg till ett par rader för att rita en vit rektangel som är lika stor som scenen:
viewport = nytt Viewport3D (); viewport.autoScaleToStage = true; // detta kommer att göra visningsporten så stor som scenen viewport.graphics.beginFill (0xffffff); // 0xffffff är vit viewport.graphics.drawRect (0, 0, stage.stageWidth, stage.stageHeight);
Testa det igen:
Bilden ska vara riktigt dim, tråkig och svår att se genom rätt lins, men lika lätt som den vanliga bilden att se genom vänster lins.
Vi behöver göra samma sak för rätt visningsport nu. Så, skapa din nya ColorTransform:
public var rightViewportColorTransform: ColorTransform;
rightViewportColorTransform = nya ColorTransform ();
Använd nu den här nya omvandlingen till rätt visningsport:
viewport2.transform.colorTransform = rightViewportColorTransform;
Eftersom min vänstra lins är gul måste jag tömma allt gult ur denna visningsport.
(Om din vänstra lins är röd, så är detta steg enkelt - sätt bara in redMultiplier av din nya ColorTransform till 0.)
Men hur blir vi gula ur rött, grönt och blått?
Om vi använde färger kunde vi inte; gul är en primär färg i det fallet. Men när man arbetar med ljus är sakerna olika. Titta på det här:
Bild från Wikimedia Commons. Tack, Mike Horvath och jacobolus!
Som du kan se, mixar rött och grönt tillsammans för att ge gult (vilket förklarar varför bakgrunden på den vänstra visningen blev gul när vi tog bort allt blå). För att ta bort allt gult från vår bild måste vi ta bort alla röda och alla gröna!
Vi kan göra så här:
rightViewportColorTransform = nya ColorTransform (); rightViewportColorTransform.redMultiplier = 0; rightViewportColorTransform.greenMultiplier = 0; viewport2.transform.colorTransform = rightViewportColorTransform;
Åh, rätt, vi måste också fylla i bakgrunden till detta visningsport.
Gör detta på samma sätt som tidigare:
viewport2 = nytt Viewport3D (); viewport2.autoScaleToStage = true; // detta kommer att göra visningsporten så stor som scenen viewport2.graphics.beginFill (0xffffff); // 0xffffff är vit viewport2.graphics.drawRect (0, 0, stage.stageWidth, stage.stageHeight);
Testa det:
Den gula linsen på mina glasögon låter faktiskt en hel del blått ljus genom, men det är fortfarande mycket mörkare när man tittar genom linsen än när man tittar genom den blå linsen, och det är det som betyder något.
Så vi har våra två bilder uppställda och färgomvandlade. Problem är att vi för närvarande bara kan se en av dem i taget.
Det mest uppenbara sättet att låta oss se båda på en gång är att minska alfabetet (genomskinligheten) på utsikten på framsidan, som så:
viewport2.alpha = 0.5;
Testa detta:
Ah. OK, inte en väckande framgång. Alpha är helt klart inte det rätta verktyget för jobbet. vad har vi fått?
Flash ger oss valet av flera olika blandningslägen, metoder för att blanda två visningsobjekt tillsammans. Det finns en fullständig lista över dem här, men jag kommer bara att fokusera på den som heter Lägg till.
De Lägg till blandningsläge går igenom varje pixel av de två bilderna och lägger samman ljusstyrkan hos var och en av de tre lamporna.
Så om en viss pixel i den första bilden har den gröna glödlampan helt på och de andra två helt av, och samma pixel i den andra bilden har den blå glödlampan helt på och de andra två helt av, då när de blandas ihop den gröna och Blå glödlampor kommer att vara helt på och den röda glödlampan kommer att vara helt avstängd.
Om de första och andra bilderna båda har den röda glödlampan halvvägs och de andra två helt av, så kommer den blandade bilden att ha den röda glödlampan helt på, medan de andra två stannar.
Få bilden? Bra, låt oss använda det. Ta bort raden som vi just skrev som justerade alfabetet och ersätt det med följande:
viewport2.blendMode = "add";
(Vi behöver inte tillämpa blandningsläge för båda visningsportarna, bara den högst uppe på visningslistan.)
Testa det:
Det är mer som det!
Om du tittar på det genom ett öga åt gången bör du märka att varje öga ser ett annat visningsport mer framträdande. Du kommer förmodligen också att kunna se ett svagt spöke i den andra utsikten, men det är inte ett problem. Din hjärna kan ignorera det.
Bilden ser emellertid inte riktigt ut än 3D. Låt oss sortera ut det.
Även om dina ögon ser olika bilder, räcker det inte med att lura din hjärna. Din hjärna är ganska smart, du ser; vissa skulle till och med säga att det är det smartaste organet i din kropp. Det vet att det borde använda andra ledtrådar för att se om ett objekt är 3D. Perspektiv, till exempel: hur litet ett objekt ska se utifrån hur långt det är.
Det största problemet här är att våra två kameror är åtskilda mycket längre ifrån varandra än våra egna ögon är. Det är inte förvånande, eftersom jag plockade numret 50 slumpmässigt.
Det faktiska separationsavståndet vi ska använda kommer att bero på ett antal saker, som hur långt du är från skärmen och hur stor skärmen är, så jag kan inte bara ge dig ett magiskt nummer att använda. Vi måste räkna ut det manuellt.
Vi kan göra detta genom att försöka massor av olika siffror en efter en, men det här är tråkigt och tar evigt. Jag har en bättre idé.
Lägg till den här raden i din onEnterFrame () -funktion:
camera2.x = camera.x + (mouseX * 0,1);
Ändra nu dina funktioner påClickViewport och onClickViewport2 till följande:
allmän funktion påClickViewport (mouseEvt: MouseEvent): void trace (camera2.x - camera.x); allmän funktion påClickViewport2 (mouseEvt: MouseEvent): void trace (camera2.x - camera.x);
Kör SWF och flytta musen åt vänster och höger. Det ser ut så här:
Sätt på dina glasögon, stäng av lamporna och fortsätt flytta musen tills du hittar den "söta platsen" där bilden verkar bli solid. Klicka på musen vid denna tidpunkt, och det kommer att spåra ett nummer. Notera det!
När du väl har hittat den optimala separationen, radera raden som vi just lagt till funktionen onEnterFrame ():
camera2.x = camera.x + (mouseX * 0,1);
Hitta nu linjen i Main () där du ställer avståndet mellan kamerorna:
camera2.x = camera.x + 50;
... och ersätt 50 med vilket nummer du noterade ner. Jag fann att 18 var ett bra nummer att använda för min inställning.
Testa slutligen SWF:
Ta-da!
Bra gjort - du har skapat en 3D-scen i Flash och använde lite sinne för att överföra den till en 3D-scen i ditt huvud;)
Om du har förstått allt du gick igenom, borde du kunna använda samma teknik för att göra 3D-anaglyphbilder med andra 3D-motorer.
Jag nämnde i första delen att du kanske vill försöka göra det möjligt för användaren att flytta kameran med musen. Nu har du lagt till anaglyph-effekten, och kamerorna fram och bak kommer att se väldigt imponerande ut.
Tack för att du läste denna handledning, jag hoppas att du har funnit det användbart. Som alltid, om du har några frågor, fråga dem i kommentarerna nedan.