Ambient Lighting
The lighting is based on Learning WebGL Lesson 7 and omits the code for directional lighting; ambient lighting is much easier to implement than the other forms of lighting described in Lesson 7. We will provide demonstrations of directional lighting and point lighting if and when we can make them work perfectly in Smart Mobile Studio.
Here we reuse code for displaying the cube with bricks on four walls from Rendering a Texture on the Faces of a Cube. The same rgb components are added to each pixel to implement the ambient lighting. The bricks are red by default, but you can enter your own RGB values in the edit boxes. RGB values of 1.0, 0.5 and 0.2 colour the bricks orange. 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.
Code of Ambient Lighting Demonstration
This code compiles with Versions 2.2 and 3.0 of Smart Mobile Studio to give an effective HTML file on a server as shown above. Some browsers do not show the images when opening the HTML file directly from a local folder on a PC.
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, W3C.Canvas2DContext; type TForm1 = class(TW3form) procedure edtBlueChanged(Sender: TObject); procedure edtGreenChanged(Sender: TObject); procedure edtRedChanged(Sender: TObject); private {$I 'Form1:intf'} protected FCanvas: TW3GraphicContext; gl: JWebGLRenderingContext; rc: TGLRenderingContext; FCubeBuffer, FTexCoordBuffer, FNormalBuffer: TGLArrayBuffer; FFragmentShader: TGLFragmentShader; FVertexShader: TGLVertexShader; FShaderProgram: TGLShaderProgram; FVertexPosAttrib, FTexCoordAttrib: Integer; FImage: TW3Image; data: JImageData; texture: JWebGLTexture; adjustedLD : Vector3; ambientColorLocation: JWebGLUniformLocation; yAngle: real = 0.2; dirRed : real = 1.0; procedure InitializeObject; override; procedure Resize; override; procedure SetupScene; procedure Render; procedure ProcessImage(Sender : TObject); procedure Rotate(sender : TObject); procedure UpdateAmbient; end; implementation procedure TForm1.edtRedChanged(Sender: TObject); begin UpdateAmbient; end; procedure TForm1.edtGreenChanged(Sender: TObject); begin UpdateAmbient; end; procedure TForm1.edtBlueChanged(Sender: TObject); begin UpdateAmbient; end; procedure TForm1.InitializeObject; begin inherited; {$I 'Form1:impl'} FImage := TW3Image.Create(nil); FImage.LoadFromURL('res/Bricks0.png'); FCanvas := TW3GraphicContext.Create(Self.Handle); gl := JWebGLRenderingContext(FCanvas.Handle.getContext('experimental-webgl')); rc := TGLRenderingContext.Create; rc.GL := gl; FImage.OnLoad := ProcessImage; OnClick := Rotate; asm window.requestAnimFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function( callback ){ window.setTimeout(callback, 1000 / 500); }; })(); end; end; procedure TForm1.ProcessImage(Sender : TObject); begin if FImage.Handle and FImage.Ready then begin data := JImageData(FImage.Handle); SetupScene; Render; end else ShowMessage('Error: image unready'); end; procedure TForm1.Resize; begin inherited; FCanvas.Handle.width := Min(Width, 500); FCanvas.Handle.height := Min(Height, 500); end; procedure TForm1.UpdateAmbient; begin gl.uniform3fv(ambientColorLocation, [strToFloat(edtRed.text), strToFloat(edtGreen.text), strToFloat(edtBlue.text)]); end; procedure TForm1.Rotate(sender : TObject); begin UpdateAmbient; yAngle += 0.1; end; procedure TForm1.SetupScene; begin gl.clearColor(0.5, 0.5, 0.9, 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 FTexCoordBuffer := TGLArrayBuffer.Create(rc); FTexCoordBuffer.SetData([ 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0 ], abuStatic); FCubeBuffer := TGLArrayBuffer.Create(rc); FCubeBuffer.SetData([ //Vertices of cube //front face: 0, 1, 2, 0, 2, 3 -1.0, -1.0, 1.0, //0 1.0, -1.0, 1.0, //1 1.0, 1.0, 1.0, //2 -1.0, -1.0, 1.0, //0 1.0, 1.0, 1.0, //2 -1.0, 1.0, 1.0, //3 //back face: 4, 5, 6, 4, 6, 7 -1.0, -1.0, -1.0, //4 -1.0, 1.0, -1.0, //5 1.0, 1.0, -1.0, //6 -1.0, -1.0, -1.0, //4 1.0, 1.0, -1.0, //6 1.0, -1.0, -1.0, //7 //right face: 16, 17, 18, 16, 18, 19 1.0, -1.0, -1.0, //16 same as 7 1.0, 1.0, -1.0, //17 same as 6 1.0, 1.0, 1.0, //18 same as 2 1.0, -1.0, -1.0, //16 same as 7 1.0, 1.0, 1.0, //18 same as 2 1.0, -1.0, 1.0, //19 same as 1 //left face: 20, 21, 22, 20, 22, 23 -1.0, -1.0, -1.0, //20 same as 4 -1.0, -1.0, 1.0, //21 same as 0 -1.0, 1.0, 1.0, //22 same as 3 -1.0, -1.0, -1.0, //20 same as 4 -1.0, 1.0, 1.0, //22 same as 3 -1.0, 1.0, -1.0 //23 same as 5 ], abuStatic); // create vertex shader FVertexShader := TGLVertexShader.Create(rc); if not FVertexShader.Compile(#" attribute vec3 aVertexPosition; attribute vec2 a_texCoord; uniform mat4 uModelViewMatrix; uniform mat4 uProjectionMatrix; varying vec2 v_texCoord; void main(void) { gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aVertexPosition, 1.0); v_texCoord = a_texCoord; }") then raise Exception.Create(FVertexShader.InfoLog); // create fragment shader FFragmentShader := TGLFragmentShader.Create(rc); FFragmentShader.Compile(#" precision mediump float; uniform sampler2D u_image; varying vec2 v_texCoord; uniform vec3 uAmbientColor; void main(void) { vec4 textureColor = texture2D(u_image, v_texCoord); gl_FragColor = vec4(textureColor.rgb * uAmbientColor, textureColor.a); }"); // create shader program and link shaders FShaderProgram := TGLShaderProgram.Create(rc); FShaderProgram.Link(FVertexShader, FFragmentShader); FShaderProgram.Use; FVertexPosAttrib := FShaderProgram.AttribLocation("aVertexPosition"); FTexCoordAttrib := FShaderProgram.AttribLocation("a_texCoord"); ambientColorLocation := FShaderProgram.UniformLocation("uAmbientColor"); updateAmbient; texture := gl.createTexture; gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, texture); // Set the parameters so we can render any size image. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); // Upload the image into the texture. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, data); end; procedure TForm1.Render; var ProjectionMatrix, ModelViewMatrix : Matrix4; ModelViewStack : array of Matrix4; procedure RenderCube(x, y, z, yAngle : Float); begin ModelViewStack.Push(ModelViewMatrix); //save the matrix ModelViewMatrix := ModelViewMatrix.Translate([x, y, z]); ModelViewMatrix := ModelViewMatrix.RotateX(0.5); ModelViewMatrix := ModelViewMatrix.RotateY(yAngle); ModelViewMatrix := ModelViewMatrix.RotateZ(0.2 ); FShaderProgram.SetUniform('uModelViewMatrix', ModelViewMatrix); FCubeBuffer.VertexAttribPointer(FVertexPosAttrib, 3, False, 0, 0); FTexCoordBuffer.VertexAttribPointer(FTexCoordAttrib, 2, False, 0, 0); gl.drawArrays(gl.TRIANGLES, 0, 24); ModelViewMatrix := ModelViewStack.Pop; //restore the matrix var renderCallback := @Render; asm window.requestAnimFrame(@renderCallback); end; end; begin // set viewport to bounds of canvas gl.ViewportSet(0, 0, FCanvas.Handle.width, FCanvas.Handle.height); // clear background gl.clear(gl.COLOR_BUFFER_BIT); //Camera field of view of 45 shows the cube just fitting into the canvas ProjectionMatrix := Matrix4.CreatePerspective(40, FCanvas.width / FCanvas.height, 0.1, 100); ModelViewMatrix := Matrix4.Identity; FShaderProgram.SetUniform('uProjectionMatrix', ProjectionMatrix); gl.enableVertexAttribArray(FVertexPosAttrib); gl.enableVertexAttribArray(FTexCoordAttrib); RenderCube(0, 0, -5, yAngle); end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end.
XML View of Form of Ambient Lighting Demonstration
<SMART> <Form version="2" subversion="1"> <Created>2015-06-05T17:24:41.363</Created> <Modified>2015-06-05T17:24:41.367</Modified> <object type="TW3Form"> <Caption>W3Form</Caption> <Name>Form1</Name> <object type="TW3EditBox"> <Value></Value> <Text>1.0</Text> <Range></Range> <Width>32</Width> <Top>24</Top> <Left>8</Left> <Height>32</Height> <Name>edtRed</Name> <OnChanged>edtRedChanged</OnChanged> </object> <object type="TW3EditBox"> <Value></Value> <Text>0.5</Text> <Range></Range> <Width>32</Width> <Top>24</Top> <Left>40</Left> <Height>32</Height> <Name>edtGreen</Name> <OnChanged>edtGreenChanged</OnChanged> </object> <object type="TW3EditBox"> <Value></Value> <Text>0.5</Text> <Range></Range> <Width>32</Width> <Top>24</Top> <Left>72</Left> <Height>32</Height> <Name>edtBlue</Name> <OnChanged>edtBlueChanged</OnChanged> </object> <object type="TW3Label"> <Caption>R</Caption> <Width>32</Width> <Left>8</Left> <Height>24</Height> <Color>16711680</Color> <Name>W3Label1</Name> </object> <object type="TW3Label"> <Caption>G</Caption> <Width>32</Width> <Left>40</Left> <Height>24</Height> <Color>32768</Color> <Name>W3Label2</Name> </object> <object type="TW3Label"> <Caption>B</Caption> <Width>32</Width> <Left>72</Left> <Height>24</Height> <Color>255</Color> <Name>W3Label3</Name> </object> </object> </Form> </SMART>