Pokemon Go nem chegou oficialmente ao Brasil e já é uma febre (e eu já até escrevi um livro sobre o assunto). As ações da Nintendo, detentora da marca Pokemon, já mais que dobraram desde seu lançamento, com a grande aposta do mercado que esse jogo mobile, primeira aposta bem sucedida da Nintendo neste segmento, irá explodir em rios de dinheiro. Como um fã de Pokemon desde os 10 anos de idade (faz tempo…) arrisco dizer que eles estão certos.
Como desenvolvedor de apps e games móveis, muitos alunos têm me perguntado como fazer um jogo que nem o Pokemon Go. Ou que tipo de tecnologia alienígena eles usaram para inserir Pokemons em nosso mundo real através do app. E eu respondo: não há nada de místico, nem nada de inédito, chama-se Realidade Aumentada, e é disso que se trata este post, traduzido de um material em Inglês.
Se é seu primeiro app em Android, sugiro começar com este livro aqui, bem curto e objetivo. Agora se nem mesmo conhece a linguagem Java, bom, comece por esse aqui.
Mas que diabos é “realidade aumentada”? Ela tornou-se um termo um tanto popular há alguns anos graças ao Google Glass, mas a ideia é mais velha do que o primeiro celular com Android. Você se lembra do filme do Exterminador do Futuro? Arnold tinha uma visão que mapeava a área ao redor e mostrava informações adicionais sobre os objetos ou pessoas.
E acho que esta é a melhor maneira de mostrar a você o que realmente é realidade aumentada. A definição de AR vem do guru Ronald Azuma em 1997, quando ele estabeleceu os três elementos que definem a realidade aumentada:
1. Ela conecta os mundos real e virtual
2. Ela é interativa em tempo real
3. ela permite movimentos em 3D
É muito importante que você não confunda realidade aumentada com realidade virtual porque estas tecnologias não são as mesmas. A ideia crucial é sobrepor informações digitalizadas sobre imagens do mundo real (offline). Soa legal, não é? Isto pode parecer ficção científica…mas existe. Claro que existem diversos dispositivos mais sofisticados do que câmeras de celular para trabalhar com realidade aumentada, com diferentes níveis de precisão e qualidade. Mas a parte mais legal é que boa parte das coisas realmente úteis que podemos fazer com realidade aumentada (inclusive jogos divertidíssimos como Pokemon Go) já estão ao nosso alcance e podem fazer parte do nosso cotidiano.
Existem dispositivos à venda na Internet (em sites como o dx.com) que, quando instalados no seu carro, transformam o seu pára-brisa em um head-up display (também conhecido como HUD pelos gamers) que mostra informações de navegação, alertas de segurança, etc.
Quem não ouviu falar do Google Glass? É um famoso wearable (computador vestível) feito pelo Google. Você também pode pensar nele como um hads-up display pessoal. Mas o que é mais legal no Glass é que ele possui um SDK para Android que nos permite criar nossas próprias aplicações de realidade aumentada. e alguns excelentes apps foram criados com ele. Por exemplo, se você precisa de motivação para correr todo dia, você pode usar o app RaceYourself e escolher o modo “Zombie Chase” (perseguição zumbi). Toda vez que você olhar para trás (usando um Google Glass, é claro), você verá o quão perto você está de ser devorado por uma hora de zumbis que estão lhe perseguindo!
Mas vamos nos focar em tecnologias que estão ao nosso alcance (afinal somos brazucas e Glass por aqui nem pensar) e vamos falar de alguns apps mais acessíveis. Smartphones estão sempre conosco, e podem fornecer importantes informações em tempo real. As experiências mais interessantes com certeza são as que conectam o que você vê ou ouve com dados processados digitalmente. Por exemplo, o Google recentemente implementou uma funcionalidade de realidade aumentada no seu app mobile de tradução. Ele automaticamente traduz texto via a câmera do smartphone e o mais impressionante é que funciona em real-time (não precisa tirar uma foto)!
Dado o que mostrei acima sobre realidade aumentada, você deve estar pensando que é muito difícil, que requer um hardware moderno ou poderosos algoritmos de processamento combinados. Mas será que precisa ser tão sofisticado? Vamos dar uma olhada no Yelp. É um app que fornece informações sobre restaurantes próximos (reviews, etc). O que realmente importa para este exemplo é o recurso chamado Monocle (monóculo), que sobrepõe informações sobre os lugares de interesse na visão da sua câmera.
Agora pense o quão difícil deve ser implementar um app tipo o Monocle do Yelp. O ponto mais importante aqui aqui é detectar os pontos de interesse e apresentá-los na tela. Algoritmos de reconhecimento de imagem podem ser a escolha certa, mas vamos ser sinceros, um smartphone ainda não tem um poder de processamento para tanto (ainda). Então vamos buscar outras alternativas, como geodésia, ou o estudo das formas e dimensões dos corpos, muito aplicado à engenharia.
Então mãos à obra! No tutorial à seguir vou mostrar como fazer um app muito simples que identifica nosso ponto de interesse usando a câmera. Para referência, veja este vídeo:
Um pouco de teoria
(Geodesy theory is based on the book: A. Jagielski, Geodezja I, GEODPIS , 2005.)
Vamos ver o que Wikipedia nos diz sobre ângulo azimutal, um conceito que precisamos entender:
“O azimute é o ângulo formado entre uma direção referencial (Norte) e uma linha a partir do observador até um ponto de interesse projetado no mesmo plano como a direção ortogonal referencial ao zênite”
Então basicamente o que tentaremos fazer é reconhecer o ponto de destino comparando o azimute calculado a partir das propriedades básicas de um triângulo de ângulo reto e o azimute verdadeiro do dispositivo que está apontado.
Lista do que precisaremos:
- pegar a localização GPS do dispositivo
- pegar a localização GPS do ponto de destino
- calcur o azimute teórico baseado nos dados GPS
- pegar o azimute real do dispositivo
- comparar ambos azimutes baseado na precisão e chamar um evento
Agora a questão é como calcular o azimute. Isto não deve ser muito difícil uma vez que vamos ignorar a curvatura da Terra e tratá-la como uma superfície plana:
Como pode ver, temos um simples triângulo reto e podemos calcular o ângulo ᵠ entre os pontos A e B a partir da simples equação:
A tabela abaixo representa as relações entre o ângulo em graus e o azimute A(AB):
Hora da Implementação
Neste tutorial, não vamos descrever como obter a localização e orientação do azimute do dispositivo porque isto é bem documentado na Internet, com vário tutoriais. Principalmente para referência, leia Sensors Overview (especially TYPEORIENTATION and TYPEROTATION_VECTOR) e Location Strategies.
Uma vez que você tenha preparado os dados dos sensores, é hora de implementar a CameraViewActivity. A primeira e mais importante coisa é implementar o SurfaceHolder.Callback para carregar uma imagem da camera em nosso layout. SurfaceHolder.Callback implementa os três métodos responsáveis para isso: surfaceChanged(), surfaceCreated() e surfaceDestroyed().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
public class CameraViewActivity extends Activity implements SurfaceHolder.Callback,OnLocationChangedListener,OnAzimuthChangedListener{ private Camera mCamera; private SurfaceHolder mSurfaceHolder; private boolean isCameraviewOn = false; private AugmentedPOI mPoi; private double mAzimuthReal = 0; private double mAzimuthTeoretical = 0; private static double AZIMUTH_ACCURACY = 5; private double mMyLatitude = 0; private double mMyLongitude = 0; private MyCurrentAzimuth myCurrentAzimuth; private MyCurrentLocation myCurrentLocation; TextView descriptionTextView; ImageView pointerIcon; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_camera_view); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); setupListeners(); setupLayout(); setAugmentedRealityPoint(); } /**Aqui você pode configurar seus ponto de interesse. Em nosso exemplo vamos fixar em somente um mas você pode estender o algoritmo para funcionar com um banco, por exemplo **/ private void setAugmentedRealityPoint() { poi = new AugmentedPOI( "RedeHost Internet", "Hospedagem de sites e cloud computing", -29.9354453, -51.0150942 ); } public double calculateTeoreticalAzimuth() { .... } private List<Double> calculateAzimuthAccuracy(double azimuth) { .... } private boolean isBetween(double minAngle, double maxAngle, double azimuth) { .... } private void updateDescription() { descriptionTextView.setText(mPoi.getPoiName() + " azimuthTeoretical " + mAzimuthTeoretical + " azimuthReal " + mAzimuthReal + " latitude " + mMyLatitude + " longitude " + mMyLongitude); } @Override public void getCurrentLocation(Location location) { .... } @Override public void onAzimuthChanged(float azimuthChangedFrom, float azimuthChangedTo) { .... } private void setupListeners() { myCurrentLocation = new MyCurrentLocation(this); myCurrentLocation.buildGoogleApiClient(this); myCurrentLocation.start(); myCurrentAzimuth = new MyCurrentAzimuth(this, this); myCurrentAzimuth.start(); } private void setupLayout() { descriptionTextView = (TextView) findViewById(R.id.cameraTextView); getWindow().setFormat(PixelFormat.UNKNOWN); SurfaceView surfaceView = (SurfaceView) findViewById(R.id.cameraview); surfaceHolder = surfaceView.getHolder(); surfaceHolder.addCallback(this); surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { if (isCameraviewOn) { mCamera.stopPreview(); isCameraviewOn = false; } if (mCamera != null) { try { mCamera.setPreviewDisplay(surfaceHolder); mCamera.startPreview(); isCameraviewOn = true; } catch (IOException e) { e.printStackTrace(); } } } @Override public void surfaceCreated(SurfaceHolder holder) { mCamera = Camera.open(); mCamera.setDisplayOrientation(90); } @Override public void surfaceDestroyed(SurfaceHolder holder) { mCamera.stopPreview(); mCamera.release(); mCamera = null; isCameraviewOn = false; } } |
O próximo passo é criar um arquivo de layout XML, chamado activity_camera_view.xml. Isto é bem simples:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <SurfaceView android:id="@+id/cameraview" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <ImageView android:id="@+id/icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:src="@drawable/ic_launcher" android:visibility="invisible" /> <TextView android:id="@+id/cameraTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:src="@drawable/ic_launcher" android:background="#FFFFFF" android:text="Large Text" /> </RelativeLayout> |
Uma vez que temos a apresentação pronta, precisamos conectá-la com os sensores que nos forneceerão os dados. Neste caso, apenas significa implementar e inicializar os listeners apropriados de OnLocationChangedListener e OnAzimuthChangedListener.
Quando tivermos obtido a localização atual poderemos calcular o azimute teórico baseado na teoria apresentada antes.
1 2 3 4 5 6 7 8 9 10 |
@Override public void getCurrentLocation(Location location) { mMyLatitude = location.getLatitude(); mMyLongitude = location.getLongitude(); mAzimuthTeoretical = calculateTeoreticalAzimuth(); Toast.makeText(this, "latitude: " + location.getLatitude() + " longitude: " + location.getLongitude(), Toast.LENGTH_SHORT).show(); updateDescription(); } |
Quando a orientação do telefone muda, precisamos calcular a precisão, comparar ambos ângulos e se o ângulo atual está dentro da precisão nós podemos mostrar uma seta na tela. É claro que esta abordagem é bem simples, mas ilustra a ideia.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@Override public void onAzimuthChanged(float azimuthFrom, float azimuthTo) { mAzimuthReal = azimuthChangedTo; mAzimuthTeoretical = calculateTeoreticalAzimuth(); pointerIcon = (ImageView) findViewById(R.id.icon); double minAngle = calculateAzimuthAccuracy(mAzimuthTeoretical).get(0); double maxAngle = calculateAzimuthAccuracy(mAzimuthTeoretical).get(1); if (isBetween(minAngle, maxAngle, mAzimuthReal)) { pointerIcon.setVisibility(View.VISIBLE); } else { pointerIcon.setVisibility(View.INVISIBLE); } updateDescription(); } |
Um método que implementa o cálculo do azimute entre pontos deve se parecer com o seguinte:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public double calculateTeoreticalAzimuth() { double dX = mPoi.getPoiLatitude() - mMyLatitude; double dY = mPoi.getPoiLongitude() - mMyLongitude; double phiAngle; double tanPhi; double azimuth = 0; tanPhi = Math.abs(dY / dX); phiAngle = Math.atan(tanPhi); phiAngle = Math.toDegrees(phiAngle); if (dX > 0 && dY > 0) { // I quater return azimuth = phiAngle; } else if (dX < 0 && dY > 0) { // II return azimuth = 180 - phiAngle; } else if (dX < 0 && dY < 0) { // III return azimuth = 180 + phiAngle; } else if (dX > 0 && dY < 0) { // IV return azimuth = 360 - phiAngle; } return phiAngle; } |
Para comparar o azimute calculado e o teórico usaremos os métodos isBetween() e calculateAzimuthAccuracy():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
private List<Double> calculateAzimuthAccuracy(double azimuth) { double minAngle = azimuth - AZIMUTH_ACCURACY; double maxAngle = azimuth + AZIMUTH_ACCURACY; List<Double> minMax = new ArrayList<Double>(); if (minAngle < 0) minAngle += 360; if (maxAngle >= 360) maxAngle -= 360; minMax.clear(); minMax.add(minAngle); minMax.add(maxAngle); return minMax; } private boolean isBetween(double minAngle, double maxAngle, double azimuth) { if (minAngle > maxAngle) { if (isBetween(0, maxAngle, A) && isBetween(minAngle, 360, azimuth)) return true; } else { if (azimuth > minAngle && azimuth < maxAngle) return true; } return false; } |
Coisas a Considerar
É claro, este tutorial é bem básico. Meu objetivo foi mostrar a você que não é muito difícil fazer um simples app com realidade aumentada. Algumas coisas a se considerar para continuar esta implementação são:
A precisão dos sensores – infelizmente não é perfeita, principalmente devido ao campo magnético emitido pelo próprio celular.
Provavelmente é necessário implementar um filtro para estabilizar os indicadores na tela. Um exemplo muito bom é fornecido aqui: http://phrogz.net/js/framerate-independent-low-pass-filter.html. A fórmula é escrita em JavaScript, mas é agnóstica de linguagem.
Você provavelmente precisará implementar um filtro de distância para mostrar somente pontos mais próximos. Para conseguir isso, simplesmente calcule a distância entre os pontos:
E isto é tudo que você precisa saber para começar sua jornada em realidade aumentada. Eu lhe desafio a usar sua criatividade e imaginação para criar apps realmente inovadores com este recurso. O potencial é enorme, e nosso trabalho como desenvolvedores é de fazê-lo acontecer! Boa sorte!
Este artigo é uma adaptação livre do original https://www.netguru.co/blog/augmented-reality-mobile-android
Você pode encontrar o projeto Android inteiro no GitHub do autor original.
Olá, tudo bem?
O que você achou deste conteúdo? Conte nos comentários.
Oi Luiz muito interessante esse assunto, também comecei a fazer protótipos de realidade aumentada utilizando o unity e vuforia, gostaria de saber se tem dicas de como aplicar essas funcionalidades no dia a dia.
Abraço
Kiyoshi
Ideias de uso existem aos montes. Desde apps para imobiliárias (já imaginou mirar um imóvel com a câmera e receber as informações dele na tela do aparelho?) até jogos (já pensou miniaturas de RPG ganharem vida no tablet para simular os combates que estão acontecendo no mapa hexagonal). O céu é o limite quando o assunto é AR, isso sem falar de VR, que é outro universo à parte.
Obrigado Luiz, vou seguir essas dicas para poder aplicar em algo futuramente.
Abraço
Quanto sairia um projeto desse ?
Pokemon Go custou milhões, mas possivelmente apps ou jogos menores consigam ser feitos com menos. Difícil precisar o quanto menos, mas imagino algo na casa dos 6 dígitos.