Rendering a Texture on Faces of a Cube
See our page on applying a texture to a single rectangle before studying this example. Find the image Bricks0.png used in this demonstration in the folder Smart Mobile Projects\Contest Demos\Afternoon Walk\bin.
In this example we change the code of the static cube on the previous page so that it now displays Bricks0.png on the front, back, left and right faces. In each case the image is as viewed from the outside of the cube. In order to specify the texture coordinates for each triangle in the correct order, we first drew a diagram of the cube and labelled the vertices (knowing the three faces that contain each vertex). The y coordinate of an image (unlike that of an openGL y coordinate) becomes more positive down the screen. For the first triangle, we need to supply the bottom left, bottom right and top right coordinates of our image. (The GPU will interpolate to calculate the colour of each pixel in the triangle). The other triangle of the front face (vertices 0, 2 then 3 of the cube) needs the bottom left, top right and then top left coordinates of the image. If you supplied a coordinate of 0.5 instead of 1.0, you would use only half the image in that direction. In this way you can change the dimensions of the bricks in this example. You can also load a composite image and use only the portion you require for each rendered triangle.

Screenshot of Four Walls
This code compiles with Versions 2.2 and 3.0 of Smart Mobile Studio to give an effective HTML file on a server giving the display 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 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, GLS.Base, W3C.Canvas2DContext; type TForm1 = class(TW3form) protected FFilepath: string = 'res/Bricks0.png'; FCanvas: TW3GraphicContext; gl: JWebGLRenderingContext; rc: TGLRenderingContext; FCubeBuffer, FTexCoordBuffer: TGLArrayBuffer; FFragmentShader: TGLFragmentShader; FVertexShader: TGLVertexShader; FShaderProgram: TGLShaderProgram; FVertexPosAttrib, FTexCoordAttrib: integer; FImage: TW3Image; data: JImageData; texture: JWebGLTexture; procedure InitializeObject; override; procedure Resize; override; procedure SetupScene; procedure Render; procedure ProcessImage(Sender : TObject); end; implementation procedure TForm1.InitializeObject; begin inherited; FImage := TW3Image.Create(nil); FImage.LoadFromURL(FFilepath); FImage.OnLoad := ProcessImage; FCanvas := TW3GraphicContext.Create(Self.Handle); gl := JWebGLRenderingContext(FCanvas.Handle.getContext('experimental-webgl')); rc := TGLRenderingContext.Create; rc.gl := gl; end; procedure TForm1.ProcessImage(Sender : TObject); begin if FImage.Handle and FImage.Ready then data := JImageData(FImage.Handle) else ShowMessage('Error: image unready'); SetupScene; Render; end; procedure TForm1.Resize; begin inherited; FCanvas.Handle.width := Min(Width, 500); FCanvas.Handle.height := Min(Height, 500); 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 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; varying vec2 v_texCoord; uniform mat4 uModelViewMatrix; uniform mat4 uProjectionMatrix; 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; void main(void) { gl_FragColor = texture2D(u_image, v_texCoord); }"); // create shader program and link shaders FShaderProgram := TGLShaderProgram.Create(rc); FShaderProgram.Link(FVertexShader, FFragmentShader); FVertexPosAttrib := FShaderProgram.AttribLocation("aVertexPosition"); FTexCoordAttrib := FShaderProgram.AttribLocation("a_texCoord"); 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 : Float); begin ModelViewStack.Push(ModelViewMatrix); //save the matrix ModelViewMatrix := ModelViewMatrix.Translate([x, y, z]); ModelViewMatrix := ModelViewMatrix.RotateX(0.5); ModelViewMatrix := ModelViewMatrix.RotateY(0.9); ModelViewMatrix := ModelViewMatrix.RotateZ(0.2 ); FShaderProgram.SetUniform('uModelViewMatrix', ModelViewMatrix); FCubeBuffer.VertexAttribPointer(FVertexPosAttrib, 3, False, 0, 0); FTexCoordBuffer.VertexAttribPointer(FTexCoordAttrib, 2, False, 0, 0); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, texture); gl.drawArrays(gl.TRIANGLES, 0, 24); ModelViewMatrix := ModelViewStack.Pop; //restore the matrix 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); FShaderProgram.Use; //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); end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end.