martes, 12 de abril de 2011

OpenGLES2.0 y Monotouch I (Poligon)

Actualmente me encuentro trabajando con OpenGLES2.0 en el proyecto MonoGame y una de las cosas que más trabajo me ha costado encontrar
es un ejemplo de monotouch usando openGL-ES2.0. Por ello he decido escribir uno para muchos como yo que estuvierán perdidos.

Bien pues si teneís instalado Monotouch en vuestro MacOSX (actualmente monotouch sólo funciona sobre MacOSX) y usáis Monodevelop podréis ver una plantilla de Monotouch y OpenGL.

Creamos un proyecto de este tipo y veremos que tenemos dos ficheros (Main.cs y EAGLView.cs) además del componente ventana (fichero xib).

Main.cs: Es la clase que contiene el método main y que llama a UIApplication.Main que es el método principal al trabajar en IOS.

EAGLView.cs: Es la clase de ejemplo que nos crea Monotouch usando OpenGL-ES1.1 con fixed pipeline. Que basicamente significa que no podemos modificar el pipeline de dibujado y por lo tanto no podemos usar shader. Los shader nos permiten tener un control más pontente del pipeline de dibujado y por lo tanto mayor flexibilidad. Otra de las mejoras al usar OpenGL-ES2.0 es que en vez de limitarnos a texturas de 1024x1024 en la versión ES1.1 en la segunda versión ya puedes usar texturas de 2048x2048.

Bueno en resumen por estos motivos y muchos más es muy interesante migrarnos a OpenGL-ES2.0 pero con la salvedad de que en la actualidad aún todos los terminales no soportan ES2.0, así que si el dispositivo no detecta esto deberemos volver a la versión 1.1

Para usar OpenGLES2.0 sobre este ejemplo sólo vamos a tocar la clase EAGLView.cs por lo que es la única que os muestro:



EAGLView.cs


#define OPENGLES2


using System;
using OpenTK.Platform.iPhoneOS;
using MonoTouch.CoreAnimation;
using OpenTK;

#if OPENGLES2
using OpenTK.Graphics.ES20;
#else
using OpenTK.Graphics.ES11;
#endif
using MonoTouch.Foundation;
using MonoTouch.ObjCRuntime;
using MonoTouch.OpenGLES;
using System.Text;
using System.Drawing;
using OpenTK.Platform;

namespace OpenGLES
{
public partial class EAGLView : iPhoneOSGameView
{
int viewportWidth, viewportHeight;
int program;
float [] vertices = new float [] {0.0f, 0.5f, 0.0f,
  -0.5f, -0.5f, 0.0f,
  0.5f, -0.5f, 0.0f
     };

[Export("layerClass")]
static Class LayerClass ()
{
return iPhoneOSGameView.GetLayerClass ();
}

[Export("initWithCoder:")]
public EAGLView (NSCoder coder) : base(coder)
{
LayerRetainsBacking = false;
LayerColorFormat = EAGLColorFormat.RGBA8;
}

protected override void CreateFrameBuffer ()
{
#if OPENGLES2
ContextRenderingApi = EAGLRenderingAPI.OpenGLES2;
base.CreateFrameBuffer();
Initialize();
#else
ContextRenderingApi = EAGLRenderingAPI.OpenGLES1;
base.CreateFrameBuffer();
#endif
}

                                

#if OPENGLES2
// protected override void OnLoad(EventArgs e)
// {
// Initialize();
// }

private bool Initialize()
{
viewportHeight = Size.Height;
viewportWidth = Size.Width;

// Vertex and fragment shaders
string vertexShaderSrc =  @"attribute vec4 aPosition;  
    void main()                  
    {                        
       gl_Position = aPosition;
    }";                          

string fragmentShaderSrc = @"precision mediump float;
varying vec4 vcolor;
            void main()                                
            {                                        
              gl_FragColor = vec4(1.0,0.0,0.0,1.0);
            }";

int vertexShader = LoadShader (All.VertexShader, vertexShaderSrc );
int fragmentShader = LoadShader (All.FragmentShader, fragmentShaderSrc );
program = GL.CreateProgram();
if (program == 0)
throw new InvalidOperationException ("Unable to create program");

GL.AttachShader (program, vertexShader);
GL.AttachShader (program, fragmentShader);

//Set position
GL.BindAttribLocation (program, 0, "aPosition");

GL.LinkProgram (program);

int linked = 0;
GL.GetProgram (program, All.LinkStatus, ref linked);
if (linked == 0) {
// link failed
int length = 0;
GL.GetProgram (program, All.InfoLogLength, ref length);
if (length > 0) {
var log = new StringBuilder (length);
GL.GetProgramInfoLog (program, length, ref length, log);
Console.WriteLine ("GL2", "Couldn't link program: " + log.ToString ());
return false;
}

GL.DeleteProgram (program);
throw new InvalidOperationException ("Unable to link program");
}

return true;
}

private int LoadShader ( All type, string source )
{
   int shader = GL.CreateShader(type);

   if ( shader == 0 )
   throw new InvalidOperationException("Unable to create shader");     

   // Load the shader source
   int length = 0;
GL.ShaderSource(shader, 1, new string[] {source}, (int[])null);
  
   // Compile the shader
   GL.CompileShader( shader );

   int compiled = 0;
GL.GetShader (shader, All.CompileStatus, ref compiled);
if (compiled == 0) {
length = 0;
GL.GetShader (shader, All.InfoLogLength, ref length);
if (length > 0) {
var log = new StringBuilder (length);
GL.GetShaderInfoLog (shader, length, ref length, log);
Console.WriteLine("GL2", "Couldn't compile shader: " + log.ToString ());
}

GL.DeleteShader (shader);
throw new InvalidOperationException ("Unable to compile shader of type : " + type.ToString ());
}

return shader;

}
#endif

protected override void ConfigureLayer (CAEAGLLayer eaglLayer)
{
eaglLayer.Opaque = true;
}


#if OPENGLES2
protected override void OnRenderFrame (FrameEventArgs e)
{
base.OnRenderFrame (e);

MakeCurrent();

GL.ClearColor (0.7f, 0.7f, 0.7f, 1);
GL.Clear ((int)All.ColorBufferBit);

GL.Viewport (0, 0, viewportWidth, viewportHeight);
GL.UseProgram (program);

GL.EnableVertexAttribArray (0);

GL.VertexAttribPointer (0, 3, All.Float, false, 0, vertices);

GL.DrawArrays (All.Triangles, 0, 3);

SwapBuffers ();
}

#else
protected override void OnRenderFrame (FrameEventArgs e)
{
base.OnRenderFrame(e);

float[] squareVertices = { -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f };
byte[] squareColors = { 255, 255, 0, 255, 0, 255, 255, 255, 0, 0,
0, 0, 255, 0, 255, 255 };

MakeCurrent ();
GL.Viewport (0, 0, Size.Width, Size.Height);

GL.MatrixMode (All.Projection);
GL.LoadIdentity ();
GL.Ortho (-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
GL.MatrixMode (All.Modelview);
GL.Rotate (3.0f, 0.0f, 0.0f, 1.0f);

GL.ClearColor (0.5f, 0.5f, 0.5f, 1.0f);
GL.Clear ((uint)All.ColorBufferBit);

GL.VertexPointer (2, All.Float, 0, squareVertices);
GL.EnableClientState (All.VertexArray);
GL.ColorPointer (4, All.UnsignedByte, 0, squareColors);
GL.EnableClientState (All.ColorArray);

GL.DrawArrays (All.TriangleStrip, 0, 4);

SwapBuffers ();
}
#endif
}
}




Nota. Para terminar si comentas la primera linea es decir "#define OPENGLES2" se renderizará usando ES1.1 y si no
usará ES2.0 con shaders.