Rotating Triangle

We now rotate the triangle with graded shading. As of May 2014 there is no 3D equivalent of the Canvas Game Project in SMS, so movement is implemented with a callback to the Render procedure in a visual components (form-based) project.

We use a projection matrix and a ModelView matrix to calculate the display at regular intervals. The fractional part of the real number returned by the now function is the fraction of the day, so 0.5 is midday. Multiplying by the constant 24 * 3600 converts this to seconds.

We removed the square from the original HelloWebGL demo and centred the triangle by giving the camera x and y coordinates of 0. We set the z coordinate to be negative so that the triangle is in front of the camera. The first argument passed to the Matrix4.CreatePerspective routine, the camera field of view, is 40. We advise you to try to understand as much as possible of the triangle code before moving on to tetrahedra and cubes. As usual, experimenting with the code is useful to aid your learning. Here are some suggestions for experimenting with the code.

  • Change the value of the first Matrix4.CreatePerspective argument slightly from 40 to see the a smaller angle close in on the triangle making it appear bigger.
  • Change the colours of the three vertices from red, green and blue.
  • Change the shader code so that the triangle is uniformly coloured.
  • Change gl.TRIANGLES to one of the other possibilities enumerated in Unit w3c.WebGL (POINTS, LINES, LINE_LOOP, LINE_STRIP, TRIANGLES, TRIANGLE_STRIP or TRIANGLE_FAN).

The code for the rotating triangle follows the demo. If these demonstrations do not run in your current browser, please try another such as Chrome, Firefox, Safari or Opera. If you see no display at school, the security system might have blocked it. You can try instead this direct link to the program running on its own page.

Rotating Triangle

Code of Triangle3D

unit Form1;

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, GLS.Vectors;

type
  TForm1 = class(TW3form)
  protected
    FCanvas : TW3GraphicContext;
    gl : JWebGLRenderingContext;
    rc : TGLRenderingContext;
    FTriangleBuffer, FColorBuffer : TGLArrayBuffer;
    FFragmentShader: TGLFragmentShader;
    FVertexShader: TGLVertexShader;
    FShaderProgram: TGLShaderProgram;
    FVertexPosAttrib, FVertexColorAttrib : Integer;
    procedure InitializeObject; override;
    procedure SetupScene;
    procedure Render;
  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
  gl.enable(gl.DEPTH_TEST);           // Enable depth testing
  gl.depthFunc(gl.LEQUAL);            // Near things obscure far things

  FColorBuffer := TGLArrayBuffer.Create(rc);
  FColorBuffer.SetData([
     1.0, 0.0, 0.0, //red
     0.0, 1.0, 0.0, //green
     0.0, 0.0, 1.0  //blue
    ], abuStatic);

  FTriangleBuffer := TGLArrayBuffer.Create(rc);
  FTriangleBuffer.SetData([
     0.0,  1.0,  0.0,
    -1.0, -1.0,  0.0,
     1.0, -1.0,  0.0
    ], abuStatic);

  // create vertex shader
  FVertexShader := TGLVertexShader.Create(rc);
  FVertexShader.Compile(#"
    attribute vec3 aVertexPosition;
    attribute vec3 aVertexColor;

    uniform mat4 uModelViewMatrix;
    uniform mat4 uProjectionMatrix;

    varying vec4 vColor;

    void main(void) {
       gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aVertexPosition, 1.0);
       vColor = vec4(aVertexColor, 1.0);
    }");

  // create fragment shader
  FFragmentShader := TGLFragmentShader.Create(rc);
  FFragmentShader.Compile(#"
    precision mediump float;
    varying vec4 vColor;
    void main(void) {
       gl_FragColor = vColor;
    }");

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

  FVertexPosAttrib := FShaderProgram.AttribLocation("aVertexPosition");
  FVertexColorAttrib := FShaderProgram.AttribLocation("aVertexColor");
  asm
    window.requestAnimFrame = (function(){
      return  window.requestAnimationFrame       ||
              window.webkitRequestAnimationFrame ||
              window.mozRequestAnimationFrame    ||
              window.oRequestAnimationFrame      ||
              window.msRequestAnimationFrame     ||
              function( callback ){
                window.setTimeout(callback, 1000 / 60);
              };
    })();
  end;
end;

procedure TForm1.Render;
const
  cSpeed = 24 * 3600;
var
  ProjectionMatrix, ModelViewMatrix : Matrix4;
  ModelViewStack : array of Matrix4;
begin
  gl.ViewportSet(0, 0, FCanvas.Handle.width, FCanvas.Handle.height);
  gl.clear(gl.COLOR_BUFFER_BIT);  // clears background

  FShaderProgram.Use;
  ProjectionMatrix := Matrix4.CreatePerspective(40, FCanvas.width / FCanvas.height, 0.1, 100);
  ModelViewMatrix := Matrix4.Identity;
  FShaderProgram.SetUniform('uProjectionMatrix', ProjectionMatrix);
  gl.enableVertexAttribArray(FVertexPosAttrib);
  gl.enableVertexAttribArray(FVertexColorAttrib);

  // move centre of triangle directly in front of camera
  ModelViewMatrix := ModelViewMatrix.Translate([0, 0, -4]);

  ModelViewMatrix := ModelViewMatrix.RotateY(Frac(Now) * cSpeed);
  FShaderProgram.SetUniform('uModelViewMatrix', ModelViewMatrix);

  FTriangleBuffer.VertexAttribPointer(FVertexPosAttrib, 3, False, 0, 0);
  FColorBuffer.VertexAttribPointer(FVertexColorAttrib, 3, False, 0, 0);
  gl.drawArrays(gl.TRIANGLES, 0, 3);

  var renderCallback := @Render;
  asm
    window.requestAnimFrame(@renderCallback);
  end;
end;
 
initialization
  Forms.RegisterForm({$I %FILE%}, TForm1);

end. 
   
Programming - a skill for life!

Using WebGL for advanced graphics in Smart Mobile Studio