Our Blog

26.08.10 @ 15:15

AR3D - not R2D2 - augmented reality in 3D.

SUBMITTED BY royi

The end result (picture is changeable as easy as replacing the drawable)

Hello all,
This blog feed is dedicated to augment reality (or "AR").

I was asked to create an Android application that will display a virtual "marker" over "reality" upon the discovery of a "pattern", in short i was asked to create a pattern oriented augmented reality mobile application (not that short, is it?).

Since i'm willing to bet that half of you reread the last line 2 or 3 times and are still wondering what am i blabbing about, this is the time for some glossary:
A mobile pattern oriented AR app workflow is as follows:
1. Reality - what is currently captured in the camera lens and is displayed on the device's screen.
2. Marker - a virtual entity that is projected on to "reality".
3. Pattern - the trigger for the marker to be displayed.

O.k. - after some basics lets start addressing the code.
We start off with an open-source project called "AndAR" which has done an incredible job at making a pattern oriented AR client for android, so what you need to do to obtain the code is to open the terminal and type:
svn checkout http://andar.googlecode.com/svn/trunk/ andar-read-only
In this feed i am referring to the AndAR project that is 1 of the 3 sample projects you get via the SVN,
for further documentation on the project and it's open source assets go to the AndAR project page at:
http://code.google.com/p/andar/
So, what are we getting?
- Pattern recognition.
- Basic pattern customization.
- A single "hard-wired" cube marker in fluorescent-green.
It's a little basic and crude but the implementation is light, easy to read and quite brilliant.

Now for some real programming, in order to present a customized image based marker you need to find the SimpleBox.java file in AndAR/src/edu/dhbw/andar/pub, the draw function initially looks like this:

        public final void draw(GL10 gl)
{  
       gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
      gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
      gl.glVertexPointer(3, GL10.GL_FLOAT, 0, box);
      gl.glNormalPointer(GL10.GL_FLOAT,0, normals);
      gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
     gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4, 4);
     gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 8, 4);
     gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 12, 4);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 16, 4);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 20, 4);
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
     gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
}

as you can see, it's pretty basic and straight forward, the function works in phases:
1. It enables vertices and normal arrays in the client.
2. It sets a pointer for each of these arrays (which are declared in the SimpleBox constructor).
3. It draws the arrays by vertex and since it's 3 dimensional every vertex is declared as 3 float values for the x,y and z axis so a quick calculation will tell you that a cube will need 92(!) float values.
4. It disables vertices and normal arrays in the client.

and after the modification it looks like this: (additions are marked by //)

        public final void draw(GL10 gl)
{  
       /
/Bitmap bitmap = CustomActivity.b;  //1- an explanation is appended 
            //gl.glEnable(GL10.GL_CULL_FACE); // Enable face culling.  
        /
/gl.glCullFace(GL10.GL_BACK);    // What faces to remove with the face culling.
     gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
      gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
      //gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
       /
/gl.glEnable(GL10.GL_TEXTURE_2D);
       //loadTextureFromBitmap(gl, bitmap); //2 - function is appended
      gl.glVertexPointer(3, GL10.GL_FLOAT, 0, box);
      /
/gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, tb); //3 - values are appended
       gl.glNormalPointer(GL10.GL_FLOAT,0, normals);
      gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4, 4);
     gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 8, 4);
     gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 12, 4);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 16, 4);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 20, 4);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
     gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
     gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
     //gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
      /
/gl.glDisable(GL10.GL_TEXTURE_2D);
  }

** 1 - In CustomActivity.java i added:

public static Bitmap b;
b = BitmapFactory.decodeResource(this.getResources(), R.drawable.bgtex); //where bgtex is a PNG file //located in res/drawable

** 2 - In SimpleBox i added this function:

public int loadTextureFromBitmap(GL10 gl, Bitmap bitmap)
{
      int[] textures = new int[1];
       gl.glGenTextures(1, textures, 0);
      gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
     gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,      GL10.GL_NEAREST);
      gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST);
       ByteBuffer imageBuffer = ByteBuffer.allocateDirect(bitmap.getHeight() * bitmap.getWidth() * 4);
        imageBuffer.order(ByteOrder.nativeOrder());
        byte buffer[] = new byte[4];
       for(int i = 0; i < bitmap.getHeight(); i++)
     {
          for(int j = 0; j < bitmap.getWidth(); j++)
          {
              int color = bitmap.getPixel(j, i);
             buffer[0] = (byte)Color.red(color);
                buffer[1] = (byte)Color.green(color);
              buffer[2] = (byte)Color.blue(color);
               buffer[3] = (byte)Color.alpha(color);
              imageBuffer.put(buffer);
           }
      }
      imageBuffer.position(0);
       gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGBA, bitmap.getWidth(),       bitmap.getHeight(), 0, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, imageBuffer);
        return textures[0];
    }

**3 - in the SimpleBox constructor i added:
      float[] tc = new float[]
                             {
               1.0f, 1.0f,
                0.0f, 1.0f,
               1.0f, 0.0f,
               0.0f, 0.0f,
              
               1.0f, 1.0f,
                0.0f, 1.0f,
               1.0f, 0.0f,
               0.0f, 0.0f,
              
               1.0f, 1.0f,
                0.0f, 1.0f,
               1.0f, 0.0f,
               0.0f, 0.0f,
              
               1.0f, 1.0f,
                0.0f, 1.0f,
               1.0f, 0.0f,
               0.0f, 0.0f,
              
               1.0f, 1.0f,
                0.0f, 1.0f,
               1.0f, 0.0f,
               0.0f, 0.0f,
              
               1.0f, 1.0f,
                0.0f, 1.0f,
               1.0f, 0.0f,
               0.0f, 0.0f,
                              };
      tb = makeFloatBuffer(tc);

I'd like to end this feed with an acknowledgment, if it wasn't for the work of open source projects as "AndAR"
(http://code.google.com/p/andar/) it is very plausible that this blog feed wouldn't be possible, so, I thank you dearly.
That's it, you can see the result in the appended screen shot.

0 Comments so far

Post a Comment

The content of this field is kept private and will not be shown publicly.
  • Use one of the forms name.module, name.theme, name.translation, name.installprofile or name.project, in order to link to http://drupal.org/project/name. Note that a link will be generated even if a project does not exist.
  • You can use Markdown syntax to format and style the text. Also see Markdown Extra for tables, footnotes, and more.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
  • Allowed HTML tags: <b> <a> <p> <br> <em> <strong> <cite> <blockquote> <table> <tr> <td> <th> <tbody> <ul> <ol> <li> <dl> <dt> <dd><img> <div> <h2> <h3> <h4> <code>
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.
  • Image links with 'rel="lightbox"' in the <a> tag will appear in a Lightbox when clicked on.

More information about formatting options


Or