[1]With SmartMS v1.0.1 [2] out you can now use WebGL!
Download HelloWebGL.zip [3] (51 kB), it contains the first demo (in .opp form and pre-compiled), as well as the initial WebGLScene units which you’ll have to copy to your “Libraries” folder (the WebGL import units should have been delivered in v1.0.1).
- GLS.Vectors: contains vector manipulations types as well as a collection of helpers to operate on them.
- GLS.Base: contains Pascal classes to simplify basic OpenGL tasks revolving around buffers and shaders.
Quick tour of the demo (or how to setup and use WebGL)
In TForm1.InitializeObject, the WebGL graphic & rendering contexts are created, this will be simplified and wrapped more neatly by a component in the future, currently it just piggybacks a TW3GraphicContext, but it stays rather simple:
canvas := TW3GraphicContext.Create(Self.Handle); gl := JWebGLRenderingContext( canvas.Handle.getContext('experimental-webgl') ); rc := TGLRenderingContext.Create; rc.GL := gl;
The TGLRenderingContext is a container class for the JWebGLRenderingContext that the other helper refer, it’s currently quite bare-bones.
In SetupScene, the OpenGL scene is initialized, it begins with classic OpenGL initialization code, which the above mentioned future component should take care off one day:
gl.clearColor(0.0, 0.0, 0.25, 1.0); // Set clear color to black, 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
Note that since gl is exposed by JWebGLRenderingContext as an external class, the function names are case-insensitive as usual in Pascal, you can follow the JavaScript case, but you don’t have to.
Then comes the raw geometry buffer, that uses a TGLArrayBuffer helper, f.i. for the triangle you’ve got
triangleBuffer := TGLArrayBuffer.Create(rc); triangleBuffer.SetData([ 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ], abuStatic);
abuStatic is to indicate a static buffer (can be abuStream or abuDynamic as well).
Next: Setting up the Shaders [4]
Previous: Quick tour of the demo. [5]
Setting up the Shaders
This is followed by the shader setup, which uses GLScene helpers too, f.i. the fragment shader is created and compiled with
fragmentShader := TGLFragmentShader.Create(rc); if not fragmentShader.Compile(#" precision mediump float; varying vec4 vColor; void main(void) { gl_FragColor = vColor; }") then raise Exception.Create(fragmentShader.InfoLog);
The vertex shader is created & compiled similarly, then both are linked into a shader program:
shaderProgram := TGLShaderProgram.Create(rc); if not shaderProgram.Link(vertexShader, fragmentShader) then raise Exception.Create(shaderProgram.InfoLog);
Note that the shader program automatically builds a cache of uniforms and locations, and you can also use the AttribInfo[] and UniformInfo[] properties to enumerate attributes and uniforms.
Finally the Render loop is where you can see the vector & matrix helpers at work.
Since OpenGL ES 2.0 doesn’t include matrix stacks, you have to do them on your side, but this is easily achieved with a dynamic array’s Push() & Pop() pseudo-methods.
const cSpeed = 24*3600; var projMat, mvMat : Matrix4; mvStack : array of Matrix4; begin gl.ViewportSet(0, 0, canvas.Handle.width, canvas.Handle.height); gl.clear(gl.COLOR_BUFFER_BIT); shaderProgram.Use; projMat := Matrix4.CreatePerspective(45, canvas.width/canvas.height, 0.1, 100); mvMat := Matrix4.Identity; shaderProgram.SetUniform('uPMatrix', projMat); gl.enableVertexAttribArray(vertexPosAttrib); mvMat := mvMat.Translate([-1.5, 0, -7]); mvStack.Push(mvMat); mvMat := mvMat.RotateY(Frac(Now)*cSpeed); shaderProgram.SetUniform('uMVMatrix', mvMat); triangleBuffer.VertexAttribPointer(vertexPosAttrib, 3, false, 0, 0); gl.drawArrays(gl.TRIANGLES, 0, 3); mvMat := mvStack.Pop; mvMat := mvMat.Translate([3.0, 0, 0]).RotateX(Frac(Now)*cSpeed); shaderProgram.SetUniform('uMVMatrix', mvMat); squareBuffer.VertexAttribPointer(vertexPosAttrib, 3, false, 0, 0); gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
The last bit I didn’t mention is the requestAnimationFrame [6] shim, which is used to keep things rotating/animating, and which should at some point become part of the VJL or RTL.
So that’s about it for the walk-through!