Displaying a Rectangle
Our first three demonstrations are based on WebGL Fundamentals by Gregg Tavares. The output of our Smart Mobile Studio code is similar to that of the original JavaScript. The first example renders a green rectangle comprising two triangles. Since we wrote this introduction and demonstration, a visual TW3WebGL control has become available, with different code to support its use. See the next section of this page for updated notes and code for use with Version 2.1 of Smart Mobile Studio and the last section for advice with Version 3.0.
gl := JWebGLRenderingContext(FCanvas.Handle.getContext('experimental-webgl'));(or more simply gl := JWebGLRenderingContext(FCanvas.Handle.getContext('webgl')); now that WebGL has become established). Once you have a reference to the context you have access to many inbuilt routines in the w3c.WebGL unit.
The coordinates for the two triangles are given in "clipspace" with values in the range -1 to +1. See the following page for code to convert coordinates in pixels to clipspace.
attribute vec2 a_position;According to the useful WebGL Quick Reference Card, the type vec2 denotes a 2-component floating point vector. Conventionally, the prefix "a_" represents attribute. Attributes have a one-to-one relationship with vertices.
The output gl_Position has homogeneous coordinates (with x, y, z, and w) components. In this simple case no matrix is applied and we simply need to add a z component of 0 and a w component of 1 to the x and y values that we input.
FVertexPosAttrib := FShaderProgram.AttribLocation("a_position");then supply values from the buffer with
FRectBuffer.VertexAttribPointer(FVertexPosAttrib, 2, False, 0, 0);The value of the second argument is 2 because the numbers are supplied in pairs (the x and y coordinates). In later examples with 3D shapes its value will be 3.
The trivial fragment shader simply outputs the same value of gl_FragColor corresponding to green for each pixel. You should not find it difficult to output a different colour.
unit Form1; // Draws a green rectangle made up of two triangles // Based on http://www.html5rocks.com/en/tutorials/webgl/webgl_fundamentals/ interface uses w3system, w3graphics, w3forms, w3application, w3c.webgl, GLS.Base; type TForm1 = class(TW3form) protected FCanvas : TW3GraphicContext; gl : JWebGLRenderingContext; rc : TGLRenderingContext; FRectBuffer : TGLArrayBuffer; FFragmentShader: TGLFragmentShader; FVertexShader: TGLVertexShader; FShaderProgram: TGLShaderProgram; FVertexPosAttrib : Integer; procedure InitializeObject; override; procedure Resize; 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; setupScene; Render; end; procedure TForm1.Resize; begin inherited; FCanvas.Handle.width := Min(Width, 300); FCanvas.Handle.height := Min(Height, 300); 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 //Put x and y coords of vertices in buffer FRectBuffer := TGLArrayBuffer.Create(rc); FRectBuffer.SetData([ -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0 ], abuStatic); // create vertex shader FVertexShader := TGLVertexShader.Create(rc); FVertexShader.Compile(#" attribute vec2 a_position; void main() { gl_Position = vec4(a_position, 0, 1); }"); // create fragment shader FFragmentShader := TGLFragmentShader.Create(rc); FFragmentShader.Compile(#" void main() { gl_FragColor = vec4(0, 1, 0, 1); // green }"); // create shader program and link shaders FShaderProgram := TGLShaderProgram.Create(rc); FShaderProgram.Link(FVertexShader, FFragmentShader); FVertexPosAttrib := FShaderProgram.AttribLocation("a_position"); 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); FRectBuffer.VertexAttribPointer(FVertexPosAttrib, 2, False, 0, 0); gl.drawArrays(gl.TRIANGLES, 0, 6); end; end.
Using a TW3WebGL Component in Version 2.1 of Smart Mobile Studio
This demonstration is based on both WebGL Fundamentals by Gregg Tavares and on the featured demo Lesson 1. (Lesson 1 in the Demos\Featured Demos\API\WebGL folder did not work for us without the callback code).
Once you have written the code FWebGL := WebGLCanvas.Scene.Handle;, you have access to many inbuilt routines. Type FWebGL. to see the possibilities in code insight.
The coordinates for the two triangles are given in "clipspace" with values in the range -1 to +1. See the following page for code to convert coordinates in pixels to clipspace.
attribute vec2 a_position;According to the useful WebGL Quick Reference Card, the type vec2 denotes a 2-component floating point vector. Conventionally, the prefix "a" represents attribute. Attributes have a one-to-one relationship with vertices.
The output gl_Position has homogeneous coordinates (with x, y, z, and w) components. In this simple case no matrix is applied and we simply need to add a z component of 0 and a w component of 1 to the x and y values that we input.
FVertexPosAttrib := FWebGL.getAttribLocation(FShaderProgram, "a_position");then supply values from the buffer with
FWebGL.vertexAttribPointer(FVertexPosAttrib, 2, FWebGL.FLOAT, False, 0, 0);The value of the second argument is 2 because the numbers are supplied in pairs (the x and y coordinates). In later examples with 3D shapes its value will be 3.
The trivial fragment shader simply outputs the same value of gl_FragColor corresponding to green for each pixel. You should not find it difficult to output a different colour.
See the comment at the start of this code for instructions on how to use it in a new project.
unit Form1; { Start a new form-based project, select the Graphics tab and add a TW3WebGL component then name the component WebGLCanvas. Paste this code into the source view of the form. Change Line 121 to FWebGL.drawArrays(FWebGL.TRIANGLES, 0, 3); to see one of the two green triangles.} interface uses W3C.TypedArray, SmartCL.System, SmartCL.Graphics, SmartCL.Controls, SmartCL.Components, SmartCL.Forms, SmartCL.Fonts, SmartCL.Borders, SmartCL.Application, SmartCL.Controls.WebGL, Khronos.WebGl, GLS.Vectors; type TForm1 = class(TW3Form) private {$I 'Form1:intf'} protected FWebGL: JWebGLRenderingContext; FBuffer: JWebGLBuffer; FFragmentShader, FVertexShader: JWebGLShader; FShaderProgram: JWebGLProgram; FVertexPosAttrib: GLint; procedure InitializeObject; override; procedure InitShaders; procedure InitBuffers; procedure DrawScene; end; implementation procedure TForm1.InitializeObject; begin inherited; {$I 'Form1:impl'} FWebGL := WebGLCanvas.Scene.Handle; InitShaders; InitBuffers; FWebGL.clearColor(0.0, 0.0, 1.0, 1.0); 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.InitBuffers; var Vertices: array of Float; begin FBuffer := FWebGL.createBuffer; FWebGL.bindBuffer(FWebGL.ARRAY_BUFFER, FBuffer); Vertices := [ -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0 ]; FWebGL.bufferData(FWebGL.ARRAY_BUFFER, JFloat32Array.Create(Vertices), FWebGL.STATIC_DRAW); end; procedure TForm1.InitShaders; begin // create vertex shader FVertexShader := FWebGL.createShader(FWebGL.VERTEX_SHADER); FWebGL.shaderSource(FVertexShader, #" attribute vec2 a_position; void main(void) { gl_Position = vec4(a_position, 0.0, 1.0); }"); // compile vertex shader FWebGL.compileShader(FVertexShader); // create fragment shader FFragmentShader := FWebGL.createShader(FWebGL.FRAGMENT_SHADER); FWebGL.shaderSource(FFragmentShader, #" void main(void) { gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); }"); // 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); FWebGL.useProgram(FShaderProgram); FVertexPosAttrib := FWebGL.getAttribLocation(FShaderProgram, "a_position"); FWebGL.enableVertexAttribArray(FVertexPosAttrib); end; procedure TForm1.DrawScene; begin // Set viewport to bounds of WebGL canvas FWebGL.ViewportSet(0, 0, WebGLCanvas.Width, WebGLCanvas.Height); // clear background FWebGL.clear(FWebGL.COLOR_BUFFER_BIT); FWebGL.vertexAttribPointer(FVertexPosAttrib, 2, FWebGL.FLOAT, False, 0, 0); FWebGL.drawArrays(FWebGL.TRIANGLES, 0, 6); var renderCallback := @DrawScene; asm window.requestAnimFrame(@renderCallback); end; end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end.
Using a TW3WebGL Component in Version 3.0 of Smart Mobile Studio
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;
Vertices := [ -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0 ];and draws the rectangle with the instruction
FWebGL.drawArrays(FWebGL.TRIANGLE_STRIP, 0, 4);