Random Rectangles

This example extends the example on the previous page, supplying the coordinates in pixels as before and now rendering randomly sized and coloured rectangles. Again, we base it on the JavaScript code in WebGL Fundamentals by Gregg Tavares.

We obtain the address of the uniform vec4 u_color with the statement
colourLocation := FShaderProgram.UniformLocation("u_color");
and supply a random value of the colour for each rectangle with the line
gl.uniform4f(colourLocation, random(), random(), random(), 1);

See below a demo that draws random rectangles using Version 3.0 of Smart Mobile Studio.

unit Form1;
// Draws randomly sized and coloured rectangles made up of two triangles with coords in pixels
interface

uses
  System.Types, W3C.TypedArray, SmartCL.System, SmartCL.Graphics,
  SmartCL.Controls, SmartCL.Components, SmartCL.Forms, SmartCL.Fonts,
  SmartCL.Borders, SmartCL.Application, Khronos.WebGl, GLS.Base;

type
  TForm1 = class(TW3form)
  protected
    FCanvas: TW3GraphicContext;
    gl: JWebGLRenderingContext;
    rc: TGLRenderingContext;
    FRectBuffer: TGLArrayBuffer;
    FFragmentShader: TGLFragmentShader;
    FVertexShader: TGLVertexShader;
    FShaderProgram: TGLShaderProgram;
    FVertexPosAttrib : Integer;
    resolutionLocation, colourLocation : JWebGLUniformLocation;
    procedure InitializeObject; override;
    procedure SetupScene;
    procedure Render;
    procedure setRectangle(x, y, width, height: real);
  end;

implementation

procedure TForm1.InitializeObject;
begin
  inherited;
  FCanvas := TW3GraphicContext.Create(Self.Handle);
  gl := JWebGLRenderingContext(FCanvas.Handle.getContext('experimental-webgl'));
  rc := TGLRenderingContext.Create;
  rc.GL := gl;
  gl.canvas.width := 300;
  gl.canvas.height := 300;
  setupScene;
  Render;
end;

procedure TForm1.SetupScene;
begin
  gl.clearColor(0.0, 0.0, 0.25, 1.0); // Set clear color to blue, fully opaque
  gl.clearDepth(1.0);                 // Clear everything

  FRectBuffer := TGLArrayBuffer.Create(rc);

  // create vertex shader
  FVertexShader := TGLVertexShader.Create(rc);
  FVertexShader.Compile(#"
    attribute vec2 a_position;

    uniform vec2 u_resolution;

    void main() {
      // convert the rectangle from pixels to 0.0 to 1.0
      vec2 zeroToOne = a_position / u_resolution;

      // convert from 0->1 to 0->2
      vec2 zeroToTwo = zeroToOne * 2.0;

      // convert from 0->2 to -1->+1 (clipspace)
      vec2 clipSpace = zeroToTwo - 1.0;

      gl_Position = vec4(clipSpace, 0, 1);
    }"
  );

  // create fragment shader
  FFragmentShader := TGLFragmentShader.Create(rc);
  FFragmentShader.Compile(#"
    precision mediump float;

    uniform vec4 u_color;

    void main() {
      gl_FragColor = u_color;
    }"
  );

  // create shader program and link shaders
  FShaderProgram := TGLShaderProgram.Create(rc);
  FShaderProgram.Link(FVertexShader, FFragmentShader);

  FVertexPosAttrib := FShaderProgram.AttribLocation("a_position");
  resolutionLocation := FShaderProgram.UniformLocation("u_resolution");
  colourLocation := FShaderProgram.UniformLocation("u_color");
end;

procedure TForm1.Render;
begin
  gl.ViewportSet(0, 0, FCanvas.Handle.width, FCanvas.Handle.height);
  gl.clear(gl.COLOR_BUFFER_BIT);  // clears background
  FShaderProgram.Use;
  gl.enableVertexAttribArray(FVertexPosAttrib);
  gl.uniform2f(resolutionLocation, FCanvas.Handle.width, FCanvas.Handle.height);
  FRectBuffer.VertexAttribPointer(FVertexPosAttrib, 2, False, 0, 0);
  // draw 50 random rectangles in random colors
  for var i := 0 to 49 do
    begin
      // Setup a random rectangle
      setRectangle(randomInt(300), randomInt(300), randomInt(300), randomInt(300));
      // Set a random color.
      gl.uniform4f(colourLocation, random(), random(), random(), 1);
      gl.drawArrays(gl.TRIANGLES, 0, 6);
    end;
end;

procedure TForm1.setRectangle(x, y, width, height: real);
var
  x1, x2, y1, y2 : real;
begin
  x1 := x;
  x2 := x + width;
  y1 := y;
  y2 := y + height;
  FRectBuffer.SetData([
     x1, y1,
     x2, y1,
     x1, y2,
     x1, y2,
     x2, y1,
     x2, y2
    ], abuStatic);
end;

initialization
  Forms.RegisterForm({$I %FILE%}, TForm1);
end.    

Using a TW3WebGL Component in Version 3.0 of Smart Mobile Studio

Start a new form-based project in Version 3.0 of Smart Mobile Studio, select the Graphics tab and add a TW3WebGL component then name the component WebGLCanvas. Paste this code into the source view of the form.

unit Form1;

interface

uses
  System.Types, System.Types.Convert, System.Objects, System.Time,
  System.IOUtils, System.Device.Storage,
  SmartCL.System, SmartCL.Time, SmartCL.Graphics, SmartCL.Components,
  SmartCL.FileUtils, SmartCL.Device.Storage, SmartCL.Forms, SmartCL.Fonts,
  SmartCL.Theme, SmartCL.Borders, SmartCL.Application, SmartCL.Controls.WebGL,
  Khronos.WebGl, GLS.Vectors, W3C.TypedArray;

type
  TForm1 = class(TW3Form)
  private
    {$I 'Form1:intf'}
  protected
    FWebGL: JWebGLRenderingContext;
    FRectBuffer: JWebGLBuffer;
    FFragmentShader, FVertexShader: JWebGLShader;
    FShaderProgram: JWebGLProgram;
    FVertexPosAttrib: GLint;
    resolutionLocation, colorLocation : JWebGLUniformLocation;
    FIteration: integer := 0;

    procedure InitializeObject; override;
    procedure InitShaders;
    procedure DrawScene;
    procedure setRectangle(x, y, width, height: real);
  end;

implementation

procedure TForm1.InitializeObject;
begin
  inherited;
  {$I 'Form1:impl'}
  FWebGL := WebGLCanvas.Scene.Handle;

  InitShaders;
  FRectBuffer := FWebGL.createBuffer;
  FWebGL.bindBuffer(FWebGL.ARRAY_BUFFER, FRectBuffer);

  FWebGL.clearColor(0.0, 0.0, 1.0, 1.0);  // blue
  FWebGL.enable(FWebGL.DEPTH_TEST);
  asm
    window.requestAnimFrame = (function(){
      return  window.requestAnimationFrame       ||
              window.webkitRequestAnimationFrame ||
              window.mozRequestAnimationFrame    ||
              window.oRequestAnimationFrame      ||
              window.msRequestAnimationFrame     ||
              function( callback ){
                window.setTimeout(callback, 1000 / 60);
              };
    })();
  end;
  DrawScene;
end;



procedure TForm1.InitShaders;
begin
  // create vertex shader
  FVertexShader := FWebGL.createShader(FWebGL.VERTEX_SHADER);
  FWebGL.shaderSource(FVertexShader, #"
    attribute vec2 a_position;
    uniform vec2 u_resolution;

    void main() {
    // convert the rectangle from pixels to 0.0 to 1.0
    vec2 zeroToOne = a_position / u_resolution;

    // convert from 0->1 to 0->2
    vec2 zeroToTwo = zeroToOne * 2.0;

    // convert from 0->2 to -1->+1 (clipspace)
    vec2 clipSpace = zeroToTwo - 1.0;

    gl_Position = vec4(clipSpace, 0, 1);
          }");

  // compile vertex shader
  FWebGL.compileShader(FVertexShader);
  // create fragment shader
  FFragmentShader := FWebGL.createShader(FWebGL.FRAGMENT_SHADER);
  FWebGL.shaderSource(FFragmentShader, #"
    precision mediump float;

    uniform vec4 u_color;

    void main() {
      gl_FragColor = u_color;
    }");

  // Compile fragment shader.
  FWebGL.compileShader(FFragmentShader);

  // Create shader program and attach shaders, then link shader program.
  FShaderProgram := FWebGL.createProgram;
  FWebGL.attachShader(FShaderProgram, FVertexShader);
  FWebGL.attachShader(FShaderProgram, FFragmentShader);
  FWebGL.linkProgram(FShaderProgram);
  // Get locations of a_position, u_resolution and u_color.
  FVertexPosAttrib := FWebGL.getAttribLocation(FShaderProgram, "a_position");
  resolutionLocation := FWebGL.getUniformLocation(FShaderProgram, "u_resolution");
  colorLocation := FWebGL.getUniformLocation(FShaderProgram, "u_color");
end;

procedure TForm1.DrawScene;
begin
  if FIteration < 2 then
    begin
      WebGLCanvas.Width := 300;
      WebGLCanvas.Height := 300;
      // Set viewport to bounds of WebGL canvas.
      FWebGL.ViewportSet(0, 0, WebGLCanvas.Width, WebGLCanvas.Height);
      // Clear background to blue.
      FWebGL.clear(FWebGL.COLOR_BUFFER_BIT);
      FWebGL.useProgram(FShaderProgram);
      FWebGL.enableVertexAttribArray(FVertexPosAttrib);
      FWebGL.uniform2f(resolutionLocation, WebGLCanvas.Width, WebGLCanvas.Height);
      FWebGL.vertexAttribPointer(FVertexPosAttrib, 2, FWebGL.FLOAT, False, 0, 0);

      // Draw 50 random rectangles in random colors.
      for var i := 0 to 49 do
        begin
          // Setup a random rectangle.
          setRectangle(randomInt(300), randomInt(300), randomInt(300), randomInt(300));
          // Set a random color.
          FWebGL.uniform4f(colorLocation, random(), random(), random(), 1);
          FWebGL.drawArrays(FWebGL.TRIANGLE_STRIP, 0, 4);
        end;
      var renderCallback := @DrawScene;
      asm
        window.requestAnimFrame(@renderCallback);
      end;
      inc(FIteration);
  end;
end;

procedure TForm1.setRectangle(x, y, width, height: real);
var
  x1, x2, y1, y2 : real;
  Vertices: array of Float;
begin
  x1 := x;
  x2 := x + width;
  y1 := y;
  y2 := y + height;

  Vertices:= [
     x1, y1,
     x2, y1,
     x1, y2,
     x2, y2
    ];
  FWebGL.bufferData(FWebGL.ARRAY_BUFFER, JFloat32Array.Create(Vertices), FWebGL.STATIC_DRAW);

end;

initialization
  Forms.RegisterForm({$I %FILE%}, TForm1);
end.
    
Programming - a skill for life!

Using WebGL for Advanced Graphics in Smart Mobile Studio