Pages

getteammates.com

Friday, 27 March 2015

OpenGL ES 3.0 Programming in Android


Android introduced OpenGL ES 3.0 support from API level 18, Android 4.3 (JELLY_BEAN_MR2).

OpenGL ES 3.0 is a super set of OpenGL ES 2.0.

We will be creating an Android application which draw a simple rectangle using OpenGL ES 3.0.


Updating AndroidManifest.xml file

We will make sure that the application is installed on device which supports OpenGL ES 3.0 using uses-feature tag.

<uses-feature android:glEsVersion="0x00030000" android:required="true" />

And also the minimum SDK and target SDK will set to 18.

<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="18" />

Main Activity implementation


Now lets create the Activity implementation. We will create a GLSurfaceView implementation and set it 
as the content view.

Our Activity class implementation is shown below,

    1 import android.app.Activity;
    2 import android.os.Bundle;
    3 
    4 public class MainActivity extends Activity {
    5     private GLES3View mView;
    6 
    7     @Override
    8     protected void onCreate(Bundle savedInstanceState) {
    9         super.onCreate(savedInstanceState);
   10         mView = new GLES3View(getApplication());
   11         setContentView(mView);
   12     }
   13 
   14     @Override
   15     protected void onPause() {
   16         super.onPause();
   17         mView.onPause();
   18     }
   19 
   20     @Override
   21     protected void onResume() {
   22         super.onResume();
   23         mView.onResume();
   24     }   
   25 }

View implementation

So far so good. Now lets go to the actual OpenGL ES 3.0 rendering.

What we will be doing is drawing a simple rectangle. Lets look at the vertex shader,
    1 #version 300 es
    2 layout(location = 0) in vec4 aPosition;
    3 void main()
    4 {
    5     gl_Position = aPosition;
    6 }

First line tells that we are going to use GLSL version 3.0. Next we declare our attribute variable. layout(location = 0) specifies the attribute index. See that we are not required to have glBindAttribLocation​ any more.
Rest of the statement is same as OpenGL ES 2.0 shader.

Now lets look at the fragment shader,

    1 #version 300 es
    2 precision mediump float;
    3 out vec4 fragColor;
    4 void main()
    5 {
    6     fragColor = vec4(1.0f, 1.0f, 0.0f, 1.0f);
    7 }

fragColor is out put variable. The value written to this variable is what will be written out into the color buffer. Here we will have an yellow rectangle.

Rest of the source code is almost similar to that of OpenGL ES 2.0 rendering, please note that we will be using android.opengl.GLES30 instead of android.opengl.GLES20.

Complete source code is given below,

    1 import java.nio.ByteBuffer;
    2 import java.nio.ByteOrder;
    3 import java.nio.FloatBuffer;
    4 
    5 import javax.microedition.khronos.egl.EGLConfig;
    6 import javax.microedition.khronos.opengles.GL10;
    7 
    8 import android.content.Context;
    9 import android.opengl.GLES30;
   10 import android.opengl.GLSurfaceView;
   11 import android.util.Log;
   12 
   13 public class GLES3View extends GLSurfaceView implements GLSurfaceView.Renderer {
   14 
   15     private static final String TAG = "GLES_3_HELLOWORLD";
   16     
   17     private static final String VertexShader = 
   18             "#version 300 es                               \n" +
   19             "layout(location = 0) in vec4 aPosition;       \n" +
   20             "void main()                                   \n" +
   21             "{                                             \n" +
   22             "    gl_Position = aPosition;                  \n" +
   23             "}                                             \n" +
   24             "                                              \n";
   25 
   26     private static final String FragmentShader = 
   27             "#version 300 es                               \n" +
   28             "precision mediump float;                      \n" +
   29             "out vec4 fragColor;                           \n" +
   30             "void main()                                   \n" +
   31             "{                                             \n" +
   32             "    fragColor = vec4(1.0f, 1.0f, 0.0f, 1.0f); \n" +
   33             "}                                             \n" +
   34             "";
   35 
   36     final int mPosLoc = 0;
   37 
   38     int mProgram;
   39 
   40     FloatBuffer mVerticesBuffer;
   41 
   42     public GLES3View(Context context) {
   43         super(context);
   44         
   45         // setup EGL configurations
   46         setEGLConfigChooser(8, 8, 8, 8, 16, 0);
   47         setEGLContextClientVersion(2);
   48         
   49         setRenderer(this);
   50     }
   51 
   52     private void init() {
   53         GLES30.glClearColor(0.5f, 0.5f, 0.5f, 1f);
   54         
   55         mProgram = createProgram(VertexShader, FragmentShader);
   56         
   57         // vertices
   58         float vertices[] = {
   59                 -0.75f, -0.75f,
   60                 0.75f, -0.75f,
   61                 0.75f, 0.75f,
   62                 -0.75f, 0.75f                
   63         };
   64         
   65         // create the float buffer
   66         ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
   67         vbb.order(ByteOrder.nativeOrder()); 
   68         mVerticesBuffer = vbb.asFloatBuffer();
   69         mVerticesBuffer.put(vertices);
   70         mVerticesBuffer.position(0);
   71     }
   72 
   73     private void draw() {
   74         GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
   75         
   76         GLES30.glUseProgram(mProgram);
   77         
   78         GLES30.glVertexAttribPointer(mPosLoc, 2, GLES30.GL_FLOAT,
   79                                              false, 0, mVerticesBuffer);
   80         GLES30.glEnableVertexAttribArray(mPosLoc);
   81         
   82         GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 0, 4);
   83     }
   84 
   85     private int loadShader(int shaderType, String source) {
   86         Log.d(TAG, source);
   87         
   88         int shader = GLES30.glCreateShader(shaderType);
   89         if (shader != 0) {
   90             // compile the shader
   91             GLES30.glShaderSource(shader, source);
   92             GLES30.glCompileShader(shader);
   93             
   94             int[] compiled = new int[1];
   95             GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0);
   96             if (compiled[0] == 0) {
   97                 Log.e(TAG, GLES30.glGetShaderInfoLog(shader));
   98                 GLES30.glDeleteShader(shader);
   99                 shader = 0;
  100             }
  101         }
  102         
  103         return shader;
  104     }
  105 
  106     private int createProgram(String vertexSource, String fragmentSource) {
  107         int vertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertexSource);
  108         if (vertexShader == 0) {
  109             return 0;
  110         }
  111 
  112         int pixelShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentSource);
  113         if (pixelShader == 0) {
  114             return 0;
  115         }
  116 
  117         int program = GLES30.glCreateProgram();
  118         if (program != 0) {
  119             GLES30.glAttachShader(program, vertexShader);
  120             GLES30.glAttachShader(program, pixelShader);
  121             GLES30.glLinkProgram(program);
  122             int []linkStatus = {0};
  123             GLES30.glGetProgramiv(program, GLES30.GL_LINK_STATUS, linkStatus, 0);
  124             if (linkStatus[0] != 1) {
  125                 Log.e(TAG, GLES30.glGetProgramInfoLog(program));
  126                 GLES30.glDeleteProgram(program);
  127                 program = 0;
  128             }
  129         }
  130         return program;
  131     }
  132     
  133     public void onDrawFrame(GL10 gl) {
  134         draw();
  135     }
  136 
  137     public void onSurfaceChanged(GL10 gl, int width, int height) {
  138         GLES30.glViewport(0, 0, width, height);
  139     }
  140 
  141     public void onSurfaceCreated(GL10 gl, EGLConfig config) {
  142         init();
  143     }
  144 }

Complete source code can be found at https://github.com/trsquarelab/glexamples/tree/master/android/gles_3_helloworld

Wednesday, 25 March 2015

Texture atlas implementation in OpenGL ES 2.0 using shaders: texture coordinate to texture region coordinate transformation

Let me show a method of implementing texture atlas. Texture region is a sub area of the actual texture.
It is used for performance benefit, in that texture need not be switched.

With texture region also as a user we will be specifying the same texture coordinate(from (0, 0) to (1, 1)),
but the shader implementation internally will convert or transform it to the texture coordinate to match the region's.

How we do that is by simply transforming the texture coordinate in the fragment shader. We will create a 3x3 matrix
corresponding to the texture region and multiply it with the texture coordinates.

Texture Region class

This class creates the 3x3 matrix which transforms the original texture coordinates to texture region coordinates.

It is provided with the original texture size and a sub rectangle within the texture.

Source code for the complete class,

    1 #ifndef TEXTUREREGION_H
    2 #define TEXTUREREGION_H
    3 
    4 #include <algorithm>
    5 
    6 class TextureRegion {
    7 private:
    8     int mTextureWidth;
    9     int mTextureHeight;
   10     int mX;
   11     int mY;
   12     int mWidth;
   13     int mHeight;
   14     float mMatrix[3*3];
   15     
   16 public:
   17     TextureRegion()
   18     {}
   19     TextureRegion(int textureWidth, int textureHeight, int x, int y, int width, int height) {
   20         mTextureWidth = textureWidth;
   21         mTextureHeight = textureHeight;
   22         
   23         if (x < 0) {
   24             x = 0;
   25         }
   26         if (x < 0) {
   27             x = 0;
   28         }
   29         
   30         if (width > textureWidth) {
   31             width = textureWidth;
   32         }
   33         
   34         if (height > textureHeight) {
   35             height = textureHeight;
   36         }
   37 
   38         mX = x;
   39         mY = y;
   40         mWidth = width;
   41         mHeight = height;
   42 
   43         createMatrix();            
   44     }
   45     const float * getMatrix() {
   46         return mMatrix;
   47     }
   48 private:
   49     void createMatrix() {
   50         // translation factor
   51         float tx = (float)(mX - 0) / (float)mTextureWidth; 
   52         float ty = (float)(mY - 0) / (float)mTextureHeight;
   53         
   54         // scaling factor
   55         float sx = (float)mWidth / (float)mTextureWidth; 
   56         float sy = (float)mHeight / (float)mTextureHeight;
   57 
   58         // 3x3 matrix
   59         float mat[] = {
   60                 sx, 0,   0,
   61                 0,   sy, 0,
   62                 tx, ty, 1.0f
   63         };
   64         std::copy(mat, mat+9, mMatrix);
   65     }
   66 };
   67 
   68 #endif
   69 

Vertex shader


There is no additional logic in vertex shader from that of drawing a simple texture.

    1 attribute vec4 aPosition;
    2 attribute vec2 aTexCoord;
    3 varying vec2 vTexCoord;
    4 void main() {
    5     vTexCoord = aTexCoord;
    6     gl_Position = aPosition;
    7 }
    8 

Fragment shader


It is in the fragment shader that we use the texture matrix which transforms the original texture coordinates to the required coordinates.

    1 #ifdef GL_ES
    2 precision mediump float;
    3 #endif
    4 varying vec2 vTexCoord;
    5 uniform mat3 uTextureMatrix;
    6 uniform vec4 uColor;
    7 uniform sampler2D uTexture;
    8 void main() {
    9     vec3 texCoord = uTextureMatrix * vec3(vTexCoord.x, vTexCoord.y, 1.0f);
   10     gl_FragColor = uColor * texture2D(uTexture, texCoord.xy);
   11 }


As you are seeing we have declared the uniform variable uTextureMatrix for transforming the texture coordinate.
As you can see in line 9 instead directly passing the texture coordinate to texture2D function we are transforming it
using the matrix uTextureMatrix to the required coordinates.
We are doing it by multiplying the matrix uTextureMatrix with the texture coordinate, last value is 1.0 because
we are using homogeneous coordinate system in two dimension.

Complete source code is pasted below for your reference. You need glut to compile it.
    1 #include <GL/glut.h>
    2 #include <iostream>
    3 #include <string>
    4 
    5 #include "textureregion.h"
    6 
    7 bool init();
    8 void display();
    9 void reshape(int w, int h);
   10 int loadShader(int shaderType, std::string pSource);
   11 int createProgram(std::string vertexSource, std::string fragmentSource);
   12 
   13 static const std::string VertexShader =
   14     "attribute vec4 aPosition;\n"
   15     "attribute vec2 aTexCoord;\n"
   16     "varying vec2 vTexCoord;\n"
   17     "void main() {\n"
   18     "    vTexCoord = aTexCoord;\n"
   19     "    gl_Position = aPosition;\n"
   20     "}\n";
   21 
   22 static const std::string FragmentShader =
   23     "#ifdef GL_ES\n"
   24     "precision mediump float;\n"
   25     "#endif\n"
   26     "varying vec2 vTexCoord;\n"
   27     "uniform mat3 uTextureMatrix;\n"
   28     "uniform vec4 uColor;\n"
   29     "uniform sampler2D uTexture;\n"
   30     "void main() {\n"
   31     "    vec3 texCoord = uTextureMatrix * vec3(vTexCoord.x, vTexCoord.y, 1.0f);\n"
   32     "    gl_FragColor = uColor * texture2D(uTexture, texCoord.xy);\n"
   33     "}\n";
   34 
   35 int mProgram;
   36 
   37 GLuint mTextureId;
   38 
   39 int mPosLoc;
   40 int mTexCoordLoc;
   41 int mTexMatrixLoc;
   42 int mSampleLoc;
   43 int mColorLoc;
   44 
   45 float mVerticesBuffer[8];
   46 float mTexcoordBuffer[8];
   47 
   48 TextureRegion mTextureRegion;
   49 
   50 int loadShader(int shaderType, std::string pSource)
   51 {
   52     int shader = glCreateShader(shaderType);
   53     if (shader != 0) {
   54         const char *vsSource  = pSource.c_str();
   55         glShaderSource(shader, 1, &vsSource, 0);
   56         glCompileShader(shader);
   57         // not interested in checking status
   58     }
   59 
   60     return shader;
   61 }
   62 
   63 int createProgram(std::string vertexSource, std::string fragmentSource)
   64 {
   65     int vertexShader = loadShader(GL_VERTEX_SHADER, vertexSource);
   66     if (vertexShader == 0) {
   67         return 0;
   68     }
   69 
   70     int pixelShader = loadShader(GL_FRAGMENT_SHADER, fragmentSource);
   71     if (pixelShader == 0) {
   72         return 0;
   73     }
   74 
   75     int program = glCreateProgram();
   76     if (program != 0) {
   77         glAttachShader(program, vertexShader);
   78         glAttachShader(program, pixelShader);
   79         glLinkProgram(program);
   80         // not interested in checking status
   81     }
   82     return program;
   83 }
   84 
   85 bool init()
   86 {
   87     glClearColor(0.0f, 1.0f, 1.0f, 1.0f);
   88 
   89     mProgram = createProgram(VertexShader, FragmentShader);
   90 
   91     // retrieve locations
   92     glUseProgram(mProgram);
   93 
   94     mPosLoc = glGetAttribLocation(mProgram, "aPosition");
   95     mTexCoordLoc = glGetAttribLocation(mProgram, "aTexCoord");
   96     mSampleLoc = glGetUniformLocation(mProgram, "uTexture");
   97     mColorLoc = glGetUniformLocation(mProgram, "uColor");
   98     mTexMatrixLoc = glGetUniformLocation(mProgram, "uTextureMatrix");
   99 
  100     GLubyte texdata[] = {
  101         0xFF, 0x00, 0x00, 0xFF,
  102         0x00, 0xFF, 0x00, 0xFF,
  103         0x00, 0x00, 0xFF, 0xFF,
  104         0xFF, 0xFF, 0x00, 0xFF
  105     };
  106 
  107     glGenTextures(1, &mTextureId);
  108 
  109     glBindTexture(GL_TEXTURE_2D, mTextureId);
  110     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  111     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  112 
  113     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  114     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  115 
  116     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, texdata);
  117 
  118     // first color
  119     mTextureRegion = TextureRegion(2, 2, 1, 1, 1, 1);
  120 
  121     glEnable(GL_TEXTURE_2D);
  122 
  123     // create vertices buffer
  124     float vertices[] = {
  125         -0.75f, -0.75f,
  126         0.75f, -0.75f,
  127         0.75f, 0.75f,
  128         -0.75f, 0.75f
  129     };
  130 
  131     std::copy(vertices, vertices+8, mVerticesBuffer);
  132 
  133     // create texcoord buffer
  134     float texcoords[] = {
  135         0, 1,
  136         1, 1,
  137         1, 0,
  138         0, 0
  139     };
  140 
  141     std::copy(texcoords, texcoords+8, mTexcoordBuffer);
  142 
  143     glEnable(GL_BLEND);
  144 
  145     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  146 
  147     return true;
  148 }
  149 
  150 void display()
  151 {
  152     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  153 
  154     glUseProgram(mProgram);
  155 
  156     glActiveTexture(GL_TEXTURE0);
  157     glBindTexture(GL_TEXTURE_2D, mTextureId);
  158     glUniform1i(mSampleLoc, 0);
  159 
  160     glUniform4f(mColorLoc, 1.0f, 1.0f, 1.0f, 1.0f);
  161 
  162     glVertexAttribPointer(mPosLoc, 2, GL_FLOAT, false, 0, mVerticesBuffer);
  163     glVertexAttribPointer(mTexCoordLoc, 2, GL_FLOAT, false, 0, mTexcoordBuffer);
  164 
  165     glUniformMatrix3fv(mTexMatrixLoc, 1, false, mTextureRegion.getMatrix());
  166 
  167     glEnableVertexAttribArray(mPosLoc);
  168     glEnableVertexAttribArray(mTexCoordLoc);
  169 
  170     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
  171 
  172     glutSwapBuffers();
  173 }
  174 
  175 void reshape(int w, int h)
  176 {
  177     glViewport(0, 0, w, h);
  178 }
  179 
  180 int main(int argc, char **argv)
  181 {
  182     glutInit(&argc, argv);
  183     glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
  184     glutInitWindowPosition(100, 100);
  185     glutInitWindowSize(320, 420);
  186 
  187     glutCreateWindow("Texture region example");
  188 
  189     glutDisplayFunc(display);
  190     glutReshapeFunc(reshape);
  191 
  192     if (init()) {
  193         glutMainLoop();
  194     } else {
  195         std::cerr << "Initialization failed" << std::endl;
  196         return 1;
  197     }
  198 
  199     return 0;
  200 }
  201 
  202  


Source code is also available at  https://github.com/chikkutechie/glexamples

 

Compiling the sources

The source code can be compiled using the below command. main.cpp contals the above pasted code.
 
    g++ main.cpp -o textreg -DGL_GLEXT_PROTOTYPES -lGL -lglut 
 

Friday, 13 March 2015

New UI implementation using c# scripts in Unity 3D

Introduction

New UI is introduced from Unity 4.6 on wards. New UI controls will be added in the similar way as other components in Unity.
Basic step is to create a GameObject and then add the required UI elements and other components to it. If you want to have an hierarchy of UI controls then add child GameObject and continue the process mentioned above.

Here we will create a simple dialog as show below just using scripts.



Name spaces for the new UI controls

    1 using UnityEngine.UI;
    2 using UnityEngine.EventSystems;
    3 using UnityEngine.Events;

MonoBehaviour implementation

We will have a game object added in to our scene and add a script to the object. From this script we will create UI controls.

    1 public class UICreator : MonoBehaviour {
    2 
    3     // Layer 5 is used for UI
    4     private const int LayerUI = 5;
    5 
    6     void Start () {
    7         CreateUI();
    8     }
    9 
   10     void Update () {
   11     }
   12 
   13     private void CreateUI() {
   14         // we will have the code to create UI here
   15     }
   16 }
   17 

Canvas Object

All the UI controls in the NEW UI should be a child of Canvas class. Lets start by creating Canvas class.
We will create a GameObject for canvas first and then set the layer to UI and also make it child of current GameObject.
Actual Canvas UI class and other required component will be added to this GameObject.

    1 GameObject canvasObject = new GameObject("Canvas");
    2 canvasObject.layer = LayerUI;
    3 canvasObject.transform.SetParent(parent);
   

Now we will add components RectTransform, Canvas, CanvasScaler and GraphicRaycaster.

    1 RectTransform canvasTrans = canvasObject.AddComponent<RectTransform>();
    2 
    3 Canvas canvas = canvasObject.AddComponent<Canvas>();
    4 canvas.renderMode = RenderMode.ScreenSpaceOverlay;
    5 canvas.pixelPerfect = true;
    6 
    7 CanvasScaler canvasScal = canvasObject.AddComponent<CanvasScaler>();
    8 canvasScal.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
    9 canvasScal.referenceResolution = new Vector2(800, 600);
   10 
   11 canvasObject.AddComponent<GraphicRaycaster>();
   12 

Creating event system

Next we will create EventSystem so that our controls will responds to events.
    1
    2 GameObject esObject = new GameObject("EventSystem");
    3 esObject.transform.SetParent(canvasObject.transform);
     

Just like the Canvas creation we will now add components to the event system GameObject

    1 EventSystem esClass = esObject.AddComponent<EventSystem>();
    2 esClass.sendNavigationEvents = true;
    3 esClass.pixelDragThreshold = 5;
    4 
    5 StandaloneInputModule stdInput = esObject.AddComponent<StandaloneInputModule>();
    6 stdInput.horizontalAxis = "Horizontal";
    7 stdInput.verticalAxis = "Vertical";
    8 
    9 esObject.AddComponent<TouchInputModule>();

Panel to hold UI controls

We will use a Panel to hold our UI controls, so lets create the Panel now.

    1 GameObject panelObject = new GameObject("Panel");
    2 panelObject.transform.SetParent(canvasObject.transform);
    3 panelObject.layer = LayerUI;
    


Now lets add the RectTransform component, RectTransform will be stretched which takes 80% of the Canvas size.

    1 RectTransform trans = panelObject.AddComponent<RectTransform>();
    2 trans.anchorMin = new Vector2(0, 0);
    3 trans.anchorMax = new Vector2(1, 1);
    4 trans.anchoredPosition3D = new Vector3(0, 0, 0);
    5 trans.anchoredPosition = new Vector2(0, 0);
    6 trans.offsetMin = new Vector2(0, 0);
    7 trans.offsetMax = new Vector2(0, 0);
    8 trans.localPosition = new Vector3(0, 0, 0);
    9 trans.sizeDelta = new Vector2(0, 0);
   10 trans.localScale = new Vector3(0.8f, 0.8f, 1.0f);
   11 
   12 panelObject.AddComponent<CanvasRenderer>();
   

Now we will add a background image to the panel. We will use a simple png file as the background. png file should be present at Assets\Resources.

    1 Image image = panelObject.AddComponent<Image>();
    2 Texture2D tex = Resources.Load<Texture2D>("panel_bkg");
    3 image.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));

Creating Text UI control

Now lets go and create Text control.

    1 GameObject textObject = new GameObject("Text");
    2 textObject.transform.SetParent(panelObject.transform);
    3 textObject.layer = LayerUI;
  

Now we will add RectTransform which sets the size and position, CanvasRenderer and the actual Text component. Text will be positioned at (x, y)

    1 RectTransform trans = textObject.AddComponent<RectTransform>();
    2 trans.sizeDelta.Set(w, h);
    3 trans.anchoredPosition3D = new Vector3(0, 0, 0);
    4 trans.anchoredPosition = new Vector2(x, y);
    5 trans.localScale = new Vector3(1.0f, 1.0f, 1.0f);
    6 trans.localPosition.Set(0, 0, 0);
    7 
    8 textObject.AddComponent<CanvasRenderer>();
    9 
   10 Text text = textObject.AddComponent<Text>();
   11 text.supportRichText = true;
   12 text.text = message;
   13 text.fontSize = fontSize;
   14 text.font = Resources.GetBuiltinResource(typeof(Font), "Arial.ttf") as Font;
   15 text.alignment = TextAnchor.MiddleCenter;
   16 text.horizontalOverflow = HorizontalWrapMode.Overflow;
   17 text.color = new Color(0, 0, 1);

Creating buttons

Creating button has two part, first creation of the button itself and the second part is creating a text object for displaying the text.
    1 GameObject buttonObject = new GameObject("Button");
    2 buttonObject.transform.SetParent(panelObject.transform);
    3 buttonObject.layer = LayerUI;


Now lets create the RectTransform and position our button accordingly.

    1 RectTransform trans = buttonObject.AddComponent<RectTransform>();
    2 SetSize(trans, new Vector2(w, h));
    3 trans.anchoredPosition3D = new Vector3(0, 0, 0);
    4 trans.anchoredPosition = new Vector2(x, y);
    5 trans.localScale = new Vector3(1.0f, 1.0f, 1.0f);
    6 trans.localPosition.Set(0, 0, 0);
    7 
    8 buttonObject.AddComponent<CanvasRenderer>();

Setting background image for button will be similar to that of panel's. The png file button_bkg.png should be at Assets\Resources as before.

    1 Image image = buttonObject.AddComponent<Image>();
    2 Texture2D tex = Resources.Load<Texture2D>("button_bkg");
    3 image.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));

Creating the actual button, eventListner is of type UnityAction.

    1 Button button = buttonObject.AddComponent<Button>();
    2 button.interactable = true;
    3 button.onClick.AddListener(eventListner);

Next we will have to create a text object with the message and add it to the buttonObject. We can use the same code which creates the text object for this. So I am not a showing that here.

Ok now we have created NEW UI using just scripts.

Complete source code is pasted below. Please note that the code is no where near release quality.

using UnityEngine;
using System.Collections;
using System.IO;

using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.Events;

public class UICreator : MonoBehaviour {

    private const int LayerUI = 5;

    void Start () {
        CreateUI();
    }
    
    void Update () {
    }


    private void CreateUI() {
        
        GameObject canvas = CreateCanvas(this.transform);

        CreateEventSystem(canvas.transform);

        GameObject panel = CreatePanel(canvas.transform);

        CreateText(panel.transform, 0, 100, 160, 50, "Message", 32);
        CreateText(panel.transform, 0, 0, 160, 50, "Are you sure, you want to exit?", 24);

        CreateButton(panel.transform, -100, -100, 160, 50, "Yes", delegate {OnExit();});
        CreateButton(panel.transform, 100, -100, 160, 50, "No", delegate {OnCancel();});
    }

    private GameObject CreateCanvas(Transform parent) {
        // create the canvas
        GameObject canvasObject = new GameObject("Canvas");
        canvasObject.layer = LayerUI;

        RectTransform canvasTrans = canvasObject.AddComponent<RectTransform>();
        
        Canvas canvas = canvasObject.AddComponent<Canvas>();
        canvas.renderMode = RenderMode.ScreenSpaceOverlay;
        canvas.pixelPerfect = true;

        CanvasScaler canvasScal = canvasObject.AddComponent<CanvasScaler>();
        canvasScal.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
        canvasScal.referenceResolution = new Vector2(800, 600);
        
        GraphicRaycaster canvasRayc = canvasObject.AddComponent<GraphicRaycaster>();

        canvasObject.transform.SetParent(parent);
        
        return canvasObject;
    }

    private GameObject CreateEventSystem(Transform parent) {
        GameObject esObject = new GameObject("EventSystem");

        EventSystem esClass = esObject.AddComponent<EventSystem>();
        esClass.sendNavigationEvents = true;
        esClass.pixelDragThreshold = 5;

        StandaloneInputModule stdInput = esObject.AddComponent<StandaloneInputModule>();
        stdInput.horizontalAxis = "Horizontal";
        stdInput.verticalAxis = "Vertical";

        TouchInputModule touchInput = esObject.AddComponent<TouchInputModule>();

        esObject.transform.SetParent(parent);

        return esObject;
    }

    private GameObject CreatePanel(Transform parent) {
        GameObject panelObject = new GameObject("Panel");
        panelObject.transform.SetParent(parent);

        panelObject.layer = LayerUI;

        RectTransform trans = panelObject.AddComponent<RectTransform>();
        trans.anchorMin = new Vector2(0, 0);
        trans.anchorMax = new Vector2(1, 1);
        trans.anchoredPosition3D = new Vector3(0, 0, 0);
        trans.anchoredPosition = new Vector2(0, 0);
        trans.offsetMin = new Vector2(0, 0);
        trans.offsetMax = new Vector2(0, 0);
        trans.localPosition = new Vector3(0, 0, 0);
        trans.sizeDelta = new Vector2(0, 0);
        trans.localScale = new Vector3(0.8f, 0.8f, 1.0f);

        CanvasRenderer renderer = panelObject.AddComponent<CanvasRenderer>();
        
        Image image = panelObject.AddComponent<Image>();
        Texture2D tex = Resources.Load<Texture2D>("panel_bkg");
        image.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height),
                                                  new Vector2(0.5f, 0.5f));

        return panelObject;
    }
    
    private GameObject CreateText(Transform parent, float x, float y,
                                      float w, float h, string message, int fontSize) {
        GameObject textObject = new GameObject("Text");
        textObject.transform.SetParent(parent);

        textObject.layer = LayerUI;

        RectTransform trans = textObject.AddComponent<RectTransform>();
        trans.sizeDelta.Set(w, h);
        trans.anchoredPosition3D = new Vector3(0, 0, 0);
        trans.anchoredPosition = new Vector2(x, y);
        trans.localScale = new Vector3(1.0f, 1.0f, 1.0f);
        trans.localPosition.Set(0, 0, 0);

        CanvasRenderer renderer = textObject.AddComponent<CanvasRenderer>();
        
        Text text = textObject.AddComponent<Text>();
        text.supportRichText = true;
        text.text = message;
        text.fontSize = fontSize;
        text.font = Resources.GetBuiltinResource(typeof(Font), "Arial.ttf") as Font;
        text.alignment = TextAnchor.MiddleCenter;
        text.horizontalOverflow = HorizontalWrapMode.Overflow;
        text.color = new Color(0, 0, 1);

        return textObject;
    }
    
    private GameObject CreateButton(Transform parent, float x, float y,
                                        float w, float h, string message,
                                        UnityAction eventListner) {
        GameObject buttonObject = new GameObject("Button");
        buttonObject.transform.SetParent(parent);

        buttonObject.layer = LayerUI;

        RectTransform trans = buttonObject.AddComponent<RectTransform>();
        SetSize(trans, new Vector2(w, h));
        trans.anchoredPosition3D = new Vector3(0, 0, 0);
        trans.anchoredPosition = new Vector2(x, y);
        trans.localScale = new Vector3(1.0f, 1.0f, 1.0f);
        trans.localPosition.Set(0, 0, 0);

        CanvasRenderer renderer = buttonObject.AddComponent<CanvasRenderer>();

        Image image = buttonObject.AddComponent<Image>();

        Texture2D tex = Resources.Load<Texture2D>("button_bkg");
        image.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height),
                                                  new Vector2(0.5f, 0.5f));

        Button button = buttonObject.AddComponent<Button>();
        button.interactable = true;
        button.onClick.AddListener(eventListner);

        GameObject textObject = CreateText(buttonObject.transform, 0, 0, 0, 0,
                                                   message, 24);

        return buttonObject;
    }

    private static void SetSize(RectTransform trans, Vector2 size) {
        Vector2 currSize = trans.rect.size;
        Vector2 sizeDiff = size - currSize;
        trans.offsetMin = trans.offsetMin -
                                  new Vector2(sizeDiff.x * trans.pivot.x,
                                      sizeDiff.y * trans.pivot.y);
        trans.offsetMax = trans.offsetMax + 
                                  new Vector2(sizeDiff.x * (1.0f - trans.pivot.x),
                                      sizeDiff.y * (1.0f - trans.pivot.y));
    }

    private void OnExit()
    {
        Application.Quit();
    }

    private void OnCancel()
    {
        GameObject.Destroy(this);
    }
}
 

UI Using prefab

Another, may be better, option is to use prefab. Create the UI using the unity editor and create prefab.


Using prefab we can launch the UI as shown below, Here DialogCanvas is the prefab and Yes and No are name of the GameObjects which has Button component.
    1 GameObject termsAndConditionCanvas = (GameObject)Instantiate(Resources.Load("DialogCanvas"));
    2 termsAndConditionCanvas.transform.SetParent(this.transform);
    3 GameObject decline = (GameObject)GameObject.Find("Yes");
    4 Button declineButton = decline.GetComponent<Button>();
    5 declineButton.onClick.AddListener(delegate{OnYesClicked();});
    6 GameObject accept = (GameObject)GameObject.Find("No");
    7 Button acceptButton = (Button)accept.GetComponent<Button>();
    8 acceptButton.onClick.AddListener(delegate{OnNoClicked();});

OnYesClicked and OnNoClicked is declared as,
    1 void OnYesClicked()
    2 {}
    3 void OnNoClicked()
    4 {}