Rectangle with Coordinates in Pixels
In this example the vertex coordinates are stored in the buffer as pixels and the vertex shader program converts them to clipspace. For the conversion, the x and y coordinates of each pixel must be divided by the width and the height of the canvas, respectively. The vector u_resolution stores the width and height. It is a uniform (with prefix u_) rather than an attribute because it remains unchanged for each vertex. We obtain its location with the line
resolutionLocation := FShaderProgram.UniformLocation("u_resolution");and input its value with the statement
gl.uniform2f(resolutionLocation, FCanvas.Handle.width, FCanvas.Handle.height);.Comments in the code of the vertex shader are prefixed with // (like one-line comments in Pascal and C). The variable vectors zeroToOne, zeroToTwo and clipSpace are declared in-line.
The example on the next page uses coordinates in pixels and renders rectangles that are randomly sized and coloured.
See below a demo that draws a green rectangle using Version 3.0 of Smart Mobile Studio.
unit Form1; // Draws a green rectangle 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 : JWebGLUniformLocation; 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 // Put x and y pixel coords of vertices in buffer FRectBuffer := TGLArrayBuffer.Create(rc); FRectBuffer.SetData([ 10, 20, 80, 20, 10, 30, 10, 30, 80, 20, 80, 30 ], abuStatic); // 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(#" 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"); resolutionLocation := FShaderProgram.UniformLocation("u_resolution"); 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); gl.drawArrays(gl.TRIANGLES, 0, 6); end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end.
Using a TW3WebGL Component to Display a Rectangle with Coordinates in Pixels 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 : JWebGLUniformLocation; 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); // 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.InitBuffers; var Vertices: array of Float; begin // Put x and y pixel coords of vertices in buffer FRectBuffer := FWebGL.createBuffer; FWebGL.bindBuffer(FWebGL.ARRAY_BUFFER, FRectBuffer); Vertices := [ 50, 100, 400, 100, 50, 150, 400, 150 ]; 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; 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, #" void main() { gl_FragColor = vec4(0, 1, 0, 1); }"); // 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 and u_resolution. FVertexPosAttrib := FWebGL.getAttribLocation(FShaderProgram, "a_position"); resolutionLocation := FWebGL.getUniformLocation(FShaderProgram, "u_resolution"); end; procedure TForm1.DrawScene; begin WebGLCanvas.Width := 450; WebGLCanvas.Height := 250; // 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); FWebGL.drawArrays(FWebGL.TRIANGLE_STRIP, 0, 4); var renderCallback := @DrawScene; asm window.requestAnimFrame(@renderCallback); end; end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end.