Realidade Aumentada no Android com Artoolkit
Por João Nunes para o Pplware
Workshop realizado no âmbito do Mestrado em Computação Móvel do Instituto Politécnico da Guarda na Unidade Curricular de Seminário
Artoolkit é uma biblioteca open source em C, que permite o desenvolvimento de interfaces baseadas na realidade aumentada. Usa técnicas de visão computacional para calcular a posição no espaço real da câmara e sua orientação em relação às marcas, permitindo ao programador sobrepor objetos virtuais na localização das marcas. A biblioteca disponibiliza código fonte tornando possível o transporte para diversas plataformas. A API OpenGL é usada para calcular as coordenadas virtuais da câmara e desenhar as imagens virtuais.
Artoolkit no Android
Android Augmented Reality (AndAR) é uma estrutura que permite utilizar a biblioteca do artoolkit na plataforma Android. AndAR oferece uma API em Java e também é orientada a objetos. A figura abaixo mostra o diagrama de classes da aplicação para esta demonstração que usa AndAR.
Para esta demonstração vamos criar três classes, CustomActivity, CustomObject e CustomRenderer. A classe CustomActivity vai herdar a classe AndARActivity que já tem tudo relacionado com a realidade aumentada, tais como abrir a câmara e detetar as marcas. A classe CustomObject herda a ARObject, é onde o objeto neste caso um cubo vai ser desenhado e precisa também do nome da marca onde o objeto vai ser desenhado. As marcas ficam localizadas na pasta assests no projeto do Eclipse. A classe CustomRenderer é a responsável por tudo o que seja OpenGL herdando a classe OpenGLRenderer.
Requisitos
Para este workshop é necessário o Eclipse, a instalação do SDK, do plugin do Android e adicionar os seguintes ficheiros à pasta libs. Fazer download aqui
Criação de um novo projeto no eclipse
File -> New -> Other -> Android Aplication Project
Após escolher o nome do projeto dar next até chegar à janela acima, mudar o nome da Activity para CustomActivity e finish.
Criação das restantes classes
Criação das classes com o nome CustomObject e CustomRenderer.
Nota: Repetir o processo para a classe CustomObject.
Adicionar a biblioteca AndAR ao projeto.
Arrastar os ficheiros para a pasta libs do projeto.
Exemplo Prático
Android Manifest
Copiar o seguinte código para o android manifiest, ter em atenção ao nome do package e da classe principal.
Classe CustomObject Na classe CustomObject fazer extends ARObject Adicionar os dois métodos. Criamos as seguintes variáveis globais:
private SimpleBox box = new SimpleBox(); private FloatBuffer matAmbiente; private FloatBuffer matFlash; private FloatBuffer matFlashBrilho; private FloatBuffer matDifusao; |
Box é o cubo que vai ser desenhada, as restantes variáveis servem para dar cor ao cubo.
No construtor criado automaticamente pelo eclipse acrescentamos outro parâmetro, um array de floats da cor do cubo, isto permite desenhar os três cubos de cor diferente.
public CustomObject(String name, String patternName, double markerWidth,double[] markerCenter, float[] cor) |
No array não vem o brilho então precisamos de declarar no construtor um outro array de floats para o brilho.
float matBrilho[] = {50.0f}; |
Agora necessitamos de atribuir os arrays às variáveis globais declaradas anteriormente.
matAmbiente = GraphicsUtil.makeFloatBuffer(cor); matFlash = GraphicsUtil.makeFloatBuffer(cor); matDifusao = GraphicsUtil.makeFloatBuffer(cor); matFlashBrilho = GraphicsUtil.makeFloatBuffer(matBrilho); |
Agora o nosso construtor da classe CustomObject está pronto.
Necessitamos agora do método draw para desenhar o cubo. Para facilitar a criação do método, botão direito do rato na área do código, Source, Override/Implement Methods e selecionamos o método draw.
No método draw se fizermo:s
box.draw(gl); |
O cubo já é desenhado mas sem cores, então necessitamos de adicionar as variáveis da cor à variável gl que foi criada automaticamente quando a criação do método.
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR,matFlash); gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SHININESS, matFlashBrilho); gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, matDifusao); gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, matAmbiente); |
O método init que tem de estar na classe obrigatoriamente fica vazio. A classe CustomObject está finalizada.
Classe CustomRenderer
- Fazer implements OpenGLRenderer.
- Adicionar os métodos
- Declarar as seguintes variáveis globais para as definições da luz, são valores recomendados pelo autor do AndAR.
private float[] ambientlight1 = {.3f, .3f, .3f, 1f}; private float[] diffuselight1 = {.7f, .7f, .7f, 1f}; private float[] specularlight1 = {0.6f, 0.6f, 0.6f, 1f}; private float[] lightposition1 = {20.0f,-40.0f,100.0f,1f}; private FloatBuffer lightPositionBuffer1 = GraphicsUtil.makeFloatBuffer(lightposition1); private FloatBuffer specularLightBuffer1 = GraphicsUtil.makeFloatBuffer(specularlight1); private FloatBuffer diffuseLightBuffer1 = GraphicsUtil.makeFloatBuffer(diffuselight1); private FloatBuffer ambientLightBuffer1 = GraphicsUtil.makeFloatBuffer(ambientlight1); |
- O método draw fica vazio.
- O método setupEnv é chamado antes de cada desenho do objecto. É usado para inicializar dados e as definições da luz do OpenGL.
gl.glEnable(GL10.GL_LIGHTING); gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_AMBIENT, ambientLightBuffer1); gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_DIFFUSE, diffuseLightBuffer1); gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_SPECULAR, specularLightBuffer1); gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_POSITION, lightPositionBuffer1); gl.glEnable(GL10.GL_LIGHT1); gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); gl.glDisable(GL10.GL_TEXTURE_2D); initGL(gl); |
- O método initGL é chamado uma vez quando a Surface do OpenGL é criada.
gl.glDisable(GL10.GL_COLOR_MATERIAL); gl.glEnable(GL10.GL_CULL_FACE); gl.glShadeModel(GL10.GL_SMOOTH); gl.glDisable(GL10.GL_COLOR_MATERIAL); gl.glEnable(GL10.GL_LIGHTING); gl.glEnable(GL10.GL_CULL_FACE); gl.glEnable(GL10.GL_DEPTH_TEST); gl.glEnable(GL10.GL_NORMALIZE); |
Classe CustomActivity
- Alterar para extends AndARActivity.
- Adicionar o método.
- Declaramos as seguintes variáveis globais
CustomObject someObject; ARToolkit artoolkit; |
No método onCreate vamos então criar os cubos chamando a classe CustomObject e também a CustomRenderer.
CustomRenderer renderer = new CustomRenderer(); super.setNonARRenderer(renderer); |
Vamos criar agora os três cubos:
Para criar os cubos precisamos de passar vários parâmetros. O nome do objeto, o nome da marca onde vai aparecer, comprimento e centro da marca, e a cor que pretendemos para o objeto. É necessário também registar o objeto no artoolkit.
artoolkit = super.getArtoolkit(); someObject = new CustomObject("Cubo1", "patt.hiro", 80.0, new double[]{0,0}, new float[]{0f, 1.0f, 0f, 0.0f});//Verde artoolkit.registerARObject(someObject); someObject = new CustomObject ("Cubo2", "android.patt", 80.0, new double[]{0,0}, new float[] {0f, 1.0f, 2.0f, 0.0f});//Azul artoolkit.registerARObject(someObject); someObject = new CustomObject ("Cubo3", "barcode.patt", 80.0, new double[]{0,0}, new float[] {1.0f, 0.0f, 0.0f, 0f});//Vermelho artoolkit.registerARObject(someObject); |
Colocamos dentro de um try catch. Para finalizar chamamos o método da AndARActivity startPreview()
Neste momento os cubos já são desenhados nas marcas. Para finalizar este tutorial vamos acrescentar código para poder tirar screenshot dos objetos.
Vamos criar um menu para tirar a screenshot para isso vamos novamente a Source, Override/Implement Methods e adicionar o método onCreateOptionsMenu
menu.add(0, 0, 0, "Tirar Foto").setIcon(R.drawable.screenshoticon); return true; |
Arrastar a imagem que está no ficheiro rar da biblioteca para a pasta res->drawlable-lpdi
Voltar Source, Override/Implement Methods e adicionar o método onOptionsItemsSelected
switch (item.getItemId()) { case 0: new TakeAsyncScreenshot().execute(); return true; } return false; |
Agora precisamos de criar a classe TakeAsyncScreenshot que vai extends AsyncTask e os três parâmetros são Void.
Adicionamos o método.
Declaramos uma variável global string inicializada a null. Na função doInBackground chamamos o método do takescreenshot que já faz parte da AndARActivity retornando um Bitmap. Escolhemos o caminho para onde a screenshot vai ser guardada e convertemos para .png.
Para terminar adicionamos outro método dentro da classe novamente Source, Override/Implement Methods e adicionar o método onPostExecute.
Aqui mandamos uma mensagem ao utilizador caso a screenshot foi guardada ou não com sucesso.
Aspeto final da classe:
class TakeAsyncScreenshot extends AsyncTask<Void, Void, Void>{ private String errorMsg = null; @Override protected Void doInBackground(Void... params) { // TODO Auto-generated method stub Bitmap bm = takeScreenshot(); FileOutputStream fos; try { fos = new FileOutputStream("/sdcard/AndARScreenshot"+new Date().getTime()+".png"); bm.compress(CompressFormat.PNG, 100, fos); fos.flush(); fos.close(); } catch (FileNotFoundException e) { errorMsg = e.getMessage(); e.printStackTrace(); } catch (IOException e) { errorMsg = e.getMessage(); e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void result) { // TODO Auto-generated method stub if(errorMsg == null){ Toast.makeText(CustomActivity.this, "Foto Guardada com Sucesso!", Toast.LENGTH_SHORT).show(); }else{ Toast.makeText(CustomActivity.this,"Erro" + errorMsg, Toast.LENGTH_SHORT).show(); } } } |
Agora é possível guardamos a screenshot dos objetos que é guardada na raiz do smartphone.
Imprimir as seguintes marcas.
O resultado deve ser semelhante à seguinte imagem:
Conclusão
Existem dois projetos desenvolvidos pelo AndAR até o momento, AndARMovelViewer que permite fazer load do nosso próprio objeto e AndARPong, onde o jogo pong está implementado na realidade aumenta. É possível também fazer as nossas próprias marcas, no site do projeto ensina como as fazer.
Referências
- Autor da biblioteca AndAR – Tobias Domhan
- Site do projeto: AndAR
- Download de todo o projecto aqui
Este artigo tem mais de um ano
Não deixa de ser engraçado um artigo sobre android em que na imagem aparece um iphone.
Foi a única imagem decente que consegui arranjar sobre o Artoolkit. Mas é apenas uma representação
Aqui aparece-me um Samsung..
Aqui parece-me que já alteraram a imagem..
Boa tarde!
Onde é que crio os 3 cubos?
Em que classe? Método?
Eu faço AndARActivity.startPreview()(supostamente?) e nao me aparece nada!
Ficam essas dúvidas.
Mas eu acho que seria mais amigo se disponibilizasse o projecto para os utilizadores verem a limpo.
O artigo é muito interessante. Obrigado pela partilha 😀
Viva Nuno,
Vou solicitar ao autor o respectivo código.
Pedro Pinto
Muito obrigado!
Abraço
Olá Nuno,
Isso fica no método onCreate assim como a criação dos cubos.
venham mais artigos como estes 🙂
Boas,
Alguém sabe dizer-me onde posso encontrar informação sobre como carregar modelos do Autodesk 3DS MAX, Blender ou outro motor de modelação 3D?
Obrigado,
O renderizador mostra qualquer imagem ou só esses 3 cubos ?
Estou gerando novas marcas para aumentar a quantidade de objetos reconhecidos e tive um problema.
Gerando o arquivo de reconhecimento (patt) com 16 segmentos (para melhorar a precisão da realidade virtual), o app não é capaz de reconhecer a marca. Só reconhece com 8 segmentos.
Saberia me indicar qual trecho do código faz a leitura do patt? e se é possível alterar para 16 segmentos ou mais?
Como vc faz com videos filmados em chroma ?